from django.contrib.auth import authenticate, login, logout from rest_framework import permissions, status from rest_framework.response import Response from rest_framework.views import APIView from .migration_service import apply_visitor_migration from .models import UserSession, VisitorMigration from .serializers import RegisterSerializer, UserSerializer def client_ip(request): forwarded = request.META.get("HTTP_X_FORWARDED_FOR") return forwarded.split(",")[0].strip() if forwarded else request.META.get("REMOTE_ADDR") class RegisterView(APIView): permission_classes = [permissions.AllowAny] def post(self, request): serializer = RegisterSerializer( data=request.data, context={"ip_address": client_ip(request)}, ) serializer.is_valid(raise_exception=True) user = serializer.save() login(request, user) self._record_session(request, user) payload = UserSerializer(user).data payload["visitor_migration_pending"] = hasattr(user, "visitor_migration") return Response(payload, status=status.HTTP_201_CREATED) @staticmethod def _record_session(request, user): if not request.session.session_key: request.session.save() UserSession.objects.update_or_create( session_key=request.session.session_key, defaults={ "user": user, "user_agent": request.headers.get("User-Agent", "")[:300], "ip_address": client_ip(request), }, ) class LoginView(APIView): permission_classes = [permissions.AllowAny] def post(self, request): user = authenticate( request, username=request.data.get("username", ""), password=request.data.get("password", ""), ) if user is None or not user.is_active: return Response( {"error": {"code": "invalid_credentials", "message": "用户名或密码错误"}}, status=status.HTTP_400_BAD_REQUEST, ) login(request, user) RegisterView._record_session(request, user) return Response(UserSerializer(user).data) class LogoutView(APIView): def post(self, request): if request.session.session_key: UserSession.objects.filter(session_key=request.session.session_key).delete() logout(request) return Response(status=status.HTTP_204_NO_CONTENT) class MeView(APIView): def get(self, request): return Response(UserSerializer(request.user).data) def patch(self, request): serializer = UserSerializer(request.user, data=request.data, partial=True) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) class VisitorMigrationView(APIView): def get(self, request): migration = VisitorMigration.objects.filter(user=request.user).first() if migration is None: return Response({"pending": False, "summary": {}}) return Response( { "pending": migration.status == VisitorMigration.Status.PENDING, "status": migration.status, "summary": migration.summary, } ) def post(self, request): migration = VisitorMigration.objects.filter(user=request.user).first() if migration is None: return Response( {"error": {"code": "no_visitor_data", "message": "没有待迁移的游客数据"}}, status=status.HTTP_404_NOT_FOUND, ) migration = apply_visitor_migration(request.user) return Response( { "pending": False, "status": migration.status, "summary": migration.summary, } )