73 lines
3.0 KiB
Python
73 lines
3.0 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")
|
|
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
|