Files
Hulumath-Web/backend/accounts/serializers.py
T
Jacky 43e425edbe
CI / test (push) Canceled after 4m2s
v 0.3 Preview: Update admin
2026-08-08 21:47:29 +08:00

82 lines
3.1 KiB
Python

import json
from django.contrib.auth.password_validation import validate_password
from django.db import transaction
from rest_framework import serializers
from .migration_service import migration_summary
from .models import InviteCode, InviteCodeUsage, User, VisitorMigration
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
"id",
"username",
"nickname",
"avatar_url",
"bio",
"track",
"rating",
"is_staff",
)
read_only_fields = ("id", "rating", "is_staff")
class RegisterSerializer(serializers.Serializer):
invite_code = serializers.CharField(max_length=32)
username = serializers.RegexField(r"^[A-Za-z0-9_]{3,30}$")
password = serializers.CharField(write_only=True, min_length=8)
nickname = serializers.CharField(max_length=40)
track = serializers.ChoiceField(choices=User.Track.choices, default=User.Track.STANDARD)
visitor_data = serializers.JSONField(required=False, default=dict, write_only=True)
def validate_username(self, value):
if User.objects.filter(username__iexact=value).exists():
raise serializers.ValidationError("用户名已存在")
return value
def validate_password(self, value):
validate_password(value)
return value
def validate_visitor_data(self, value):
if not isinstance(value, dict):
raise serializers.ValidationError("游客数据必须是对象")
for field in ("story_saves", "favorites", "cards"):
if field in value and not isinstance(value[field], list):
raise serializers.ValidationError(f"{field} 必须是数组")
if "mathbti" in value and not isinstance(value["mathbti"], dict):
raise serializers.ValidationError("mathbti 必须是对象")
if len(json.dumps(value, ensure_ascii=False).encode("utf-8")) > 256 * 1024:
raise serializers.ValidationError("游客数据不能超过 256KB")
return value
@transaction.atomic
def create(self, validated_data):
code_text = validated_data.pop("invite_code")
visitor_data = validated_data.pop("visitor_data", {})
try:
invite = InviteCode.objects.select_for_update().get(code__iexact=code_text)
except InviteCode.DoesNotExist as exc:
raise serializers.ValidationError({"invite_code": "邀请码无效"}) from exc
if not invite.is_available:
raise serializers.ValidationError({"invite_code": "邀请码已过期或已用完"})
user = User.objects.create_user(**validated_data)
InviteCodeUsage.objects.create(
invite_code=invite,
user=user,
ip_address=self.context.get("ip_address"),
)
invite.used_count += 1
invite.save(update_fields=["used_count"])
if visitor_data:
VisitorMigration.objects.create(
user=user,
payload=visitor_data,
summary=migration_summary(visitor_data),
)
return user