Recruitment_site/offer_backend/apps/accounts/views.py

214 lines
7.4 KiB
Python
Raw Permalink 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 rest_framework import generics, status
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.conf import settings
from .models import VerificationCode
from .serializers import RegisterSerializer, UserSerializer, AdminUserSerializer, SendCodeSerializer, CodeLoginSerializer, PasswordLoginSerializer, ResetPasswordSerializer, ConfirmResetPasswordSerializer
from .permissions import IsSuperAdmin
User = get_user_model()
class SendCodeView(APIView):
"""发送邮箱验证码(用于登入或密码重置)"""
permission_classes = [AllowAny]
def post(self, request):
serializer = SendCodeSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.validated_data['email']
# 清除旧验证码
VerificationCode.objects.filter(email=email).delete()
# 生成新验证码
code = VerificationCode.generate_code()
vc = VerificationCode.objects.create(email=email, code=code)
# 发送邮件
try:
send_mail(
subject='【集团招聘平台】验证码',
message=f'您的验证码是:{code}\n\n验证码有效期为10分钟请勿泄露给他人。',
from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@offer.com'),
recipient_list=[email],
fail_silently=False,
)
except Exception as e:
vc.delete()
return Response(
{'error': '邮件发送失败,请稍后重试'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
return Response({'message': '验证码已发送到您的邮箱'})
class RequestResetPasswordView(APIView):
"""请求密码重置(发送验证码)"""
permission_classes = [AllowAny]
def post(self, request):
serializer = ResetPasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.validated_data['email']
# 使用 SendCodeView 的逻辑发送验证码
VerificationCode.objects.filter(email=email).delete()
code = VerificationCode.generate_code()
vc = VerificationCode.objects.create(email=email, code=code)
try:
send_mail(
subject='【集团招聘平台】密码重置验证码',
message=f'您的验证码是:{code}\n\n验证码有效期为10分钟请勿泄露给他人。',
from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@offer.com'),
recipient_list=[email],
fail_silently=False,
)
except Exception as e:
vc.delete()
return Response(
{'error': '邮件发送失败,请稍后重试'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
return Response({'message': '验证码已发送到您的邮箱'})
class ConfirmResetPasswordView(APIView):
"""确认密码重置"""
permission_classes = [AllowAny]
def post(self, request):
serializer = ConfirmResetPasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
vc = serializer.validated_data['vc']
new_password = serializer.validated_data['new_password']
# 设置新密码
user.set_password(new_password)
user.save()
# 标记验证码为已使用
vc.mark_as_verified()
return Response({'message': '密码重置成功,请使用新密码登入'})
class CustomTokenObtainPairView(TokenObtainPairView):
"""自定义登入视图,支持三种方式:邮箱验证码、邮箱密码、用户名密码"""
def post(self, request, *args, **kwargs):
# 判断登入方式
has_code = 'code' in request.data
has_email = 'email' in request.data
has_username = 'username' in request.data
has_password = 'password' in request.data
if has_email and has_code:
# 邮箱验证码登入
return self._login_with_code(request)
elif (has_email or has_username) and has_password:
# 邮箱或用户名 + 密码登入
return self._login_with_password(request)
else:
return Response(
{'error': '请提供正确的登入方式'},
status=status.HTTP_400_BAD_REQUEST
)
def _login_with_code(self, request):
"""邮箱验证码登入"""
serializer = CodeLoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
vc = serializer.validated_data['vc']
# 标记验证码为已使用
vc.mark_as_verified()
# 生成 JWT token
from rest_framework_simplejwt.tokens import RefreshToken
refresh = RefreshToken.for_user(user)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
}, status=status.HTTP_200_OK)
def _login_with_password(self, request):
"""邮箱或用户名 + 密码登入"""
serializer = PasswordLoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
# 生成 JWT token
from rest_framework_simplejwt.tokens import RefreshToken
refresh = RefreshToken.for_user(user)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
}, status=status.HTTP_200_OK)
class RegisterView(APIView):
"""密码注册"""
permission_classes = [AllowAny]
def post(self, request):
serializer = RegisterSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
# 创建用户
user = serializer.save()
# 生成 JWT token
from rest_framework_simplejwt.tokens import RefreshToken
refresh = RefreshToken.for_user(user)
return Response({
'user': {
'id': user.id,
'username': user.username,
'email': user.email,
'role': user.role,
},
'refresh': str(refresh),
'access': str(refresh.access_token),
}, status=status.HTTP_201_CREATED)
class MeView(APIView):
permission_classes = [IsAuthenticated]
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 UserManageViewSet(generics.ListCreateAPIView):
"""超管:管理所有用户"""
serializer_class = AdminUserSerializer
permission_classes = [IsSuperAdmin]
queryset = User.objects.all()
class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = AdminUserSerializer
permission_classes = [IsSuperAdmin]
queryset = User.objects.all()