@@ -0,0 +1,72 @@
|
||||
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")
|
||||
read_only_fields = ("id", "rating")
|
||||
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user