aifeilong 13b1ca2b9b Namespace API routes by app and flatten package layout.
Move Django apps out of apps/ and mount them under auth/, common/, content/, and cms/ so endpoint paths map directly to the owning app.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-15 08:42:37 +08:00

165 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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",
]