Introduce content/cms/accounts modules with DRF routes, and point default DB settings at the official starintldb instance. Co-authored-by: Cursor <cursoragent@cursor.com>
165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
from django.contrib.auth.models import User
|
||
from django.db import transaction
|
||
from rest_framework import serializers
|
||
|
||
from .models import Member
|
||
from .phone import normalize_phone
|
||
|
||
|
||
class StaffUserSerializer(serializers.ModelSerializer):
|
||
password = serializers.CharField(write_only=True, required=False, allow_blank=True)
|
||
|
||
class Meta:
|
||
model = User
|
||
fields = [
|
||
"id",
|
||
"username",
|
||
"first_name",
|
||
"email",
|
||
"is_superuser",
|
||
"is_active",
|
||
"last_login",
|
||
"date_joined",
|
||
"password",
|
||
]
|
||
read_only_fields = ["last_login", "date_joined"]
|
||
|
||
def create(self, validated_data):
|
||
password = validated_data.pop("password", None) or ""
|
||
if not password:
|
||
raise serializers.ValidationError({"password": "新建时密码必填"})
|
||
user = User(
|
||
username=validated_data["username"],
|
||
first_name=validated_data.get("first_name", ""),
|
||
email=validated_data.get("email", ""),
|
||
is_superuser=validated_data.get("is_superuser", False),
|
||
is_active=validated_data.get("is_active", True),
|
||
is_staff=True,
|
||
)
|
||
user.set_password(password)
|
||
user.save()
|
||
return user
|
||
|
||
def update(self, instance, validated_data):
|
||
password = validated_data.pop("password", None)
|
||
for attr, value in validated_data.items():
|
||
setattr(instance, attr, value)
|
||
instance.is_staff = True
|
||
if password:
|
||
instance.set_password(password)
|
||
instance.save()
|
||
return instance
|
||
|
||
|
||
def _ensure_member_user(member, phone, password=None, name=""):
|
||
"""为会员确保绑定非 staff 的 User;手机号作为 username。"""
|
||
if not phone:
|
||
return member
|
||
user = member.user
|
||
if user is None:
|
||
# 复用同名非 staff 账号,避免唯一约束冲突
|
||
user = User.objects.filter(username=phone, is_staff=False).first()
|
||
if user is None:
|
||
user = User(username=phone, is_staff=False, is_active=True)
|
||
if password:
|
||
user.set_password(password)
|
||
else:
|
||
user.set_unusable_password()
|
||
user.save()
|
||
member.user = user
|
||
else:
|
||
if user.username != phone:
|
||
if User.objects.filter(username=phone).exclude(pk=user.pk).exists():
|
||
raise serializers.ValidationError({"phone": "该手机号已被占用"})
|
||
user.username = phone
|
||
if password:
|
||
user.set_password(password)
|
||
user.is_staff = False
|
||
user.is_active = not member.is_disabled
|
||
user.save()
|
||
if name and not user.first_name:
|
||
user.first_name = name
|
||
user.save(update_fields=["first_name"])
|
||
return member
|
||
|
||
|
||
class MemberSerializer(serializers.ModelSerializer):
|
||
password = serializers.CharField(write_only=True, required=False, allow_blank=True)
|
||
|
||
class Meta:
|
||
model = Member
|
||
fields = [
|
||
"id",
|
||
"name",
|
||
"phone",
|
||
"organize",
|
||
"is_vip",
|
||
"last_login_at",
|
||
"is_disabled",
|
||
"password",
|
||
"created_at",
|
||
"updated_at",
|
||
]
|
||
read_only_fields = ["last_login_at", "created_at", "updated_at"]
|
||
|
||
def validate_phone(self, value):
|
||
# 更新时允许留空(不改手机号);新建在 create 中再强制必填
|
||
if value in (None, "") and self.instance:
|
||
return self.instance.phone
|
||
phone = normalize_phone(value, required=not bool(self.instance))
|
||
if not phone:
|
||
return phone
|
||
qs = Member.objects.filter(phone=phone)
|
||
if self.instance:
|
||
qs = qs.exclude(pk=self.instance.pk)
|
||
if qs.exists():
|
||
raise serializers.ValidationError("该手机号已被注册")
|
||
return phone
|
||
|
||
@transaction.atomic
|
||
def create(self, validated_data):
|
||
password = validated_data.pop("password", None) or ""
|
||
phone = validated_data.get("phone") or ""
|
||
if not phone:
|
||
raise serializers.ValidationError({"phone": "手机号必填"})
|
||
if not password:
|
||
raise serializers.ValidationError({"password": "新建时密码必填"})
|
||
member = Member(**validated_data)
|
||
member.save()
|
||
_ensure_member_user(member, phone, password=password, name=member.name)
|
||
member.save(update_fields=["user", "updated_at"])
|
||
return member
|
||
|
||
@transaction.atomic
|
||
def update(self, instance, validated_data):
|
||
password = validated_data.pop("password", None) or ""
|
||
for attr, value in validated_data.items():
|
||
setattr(instance, attr, value)
|
||
instance.save()
|
||
if instance.phone:
|
||
_ensure_member_user(
|
||
instance,
|
||
instance.phone,
|
||
password=password or None,
|
||
name=instance.name,
|
||
)
|
||
instance.save(update_fields=["user", "updated_at"])
|
||
elif password:
|
||
raise serializers.ValidationError({"phone": "设置密码前需填写手机号"})
|
||
return instance
|
||
|
||
|
||
class MemberPublicSerializer(serializers.ModelSerializer):
|
||
"""前台登录/注册返回的会员信息(不含禁用标记写入)。"""
|
||
|
||
class Meta:
|
||
model = Member
|
||
fields = [
|
||
"id",
|
||
"name",
|
||
"phone",
|
||
"organize",
|
||
"is_vip",
|
||
"last_login_at",
|
||
]
|