Files
medical_training/apps/user/auth/send_code.py
T

62 lines
2.4 KiB
Python

import re
from django.core.cache import cache
from django.conf import settings
from rest_framework.decorators import api_view, permission_classes, throttle_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import serializers as drf_serializers
from drf_spectacular.utils import extend_schema, inline_serializer
from config.exceptions import AppError
from apps.user.models import User
from apps.user.throttling import SmsPhoneMinuteThrottle, SmsPhoneDayThrottle, SmsIpThrottle
from apps.user.utils.sms import generate_sms_code, get_sms_service, SmsError
@extend_schema(
summary='U1 发送短信验证码',
request=inline_serializer('SendCodeRequest', fields={
'phone': drf_serializers.CharField(help_text='手机号'),
'scene': drf_serializers.ChoiceField(choices=['register', 'login', 'reset'],
help_text='场景:register/login/reset'),
}),
responses={200: inline_serializer('SendCodeResponse', fields={
'message': drf_serializers.CharField(),
})},
tags=['认证'],
)
@api_view(['POST'])
@permission_classes([AllowAny])
@throttle_classes([SmsPhoneMinuteThrottle, SmsPhoneDayThrottle, SmsIpThrottle])
def send_code(request):
"""U1 发送短信验证码"""
data = request.data
phone = data.get('phone', '')
scene = data.get('scene', '')
if not re.match(r'^1[3-9]\d{9}$', str(phone)):
raise AppError('SMS_INVALID_PHONE', '手机号格式不合法')
if scene not in ('register', 'login', 'reset'):
raise AppError('SMS_INVALID_SCENE', 'scene 参数无效,仅允许 register / login / reset')
user_exists = User.objects.filter(phone=phone).exists()
if scene == 'register' and user_exists:
raise AppError('AUTH_PHONE_REGISTERED', '该手机号已注册')
if scene in ('login', 'reset') and not user_exists:
raise AppError('AUTH_PHONE_NOT_FOUND', '手机号未注册')
code = generate_sms_code()
cache_key = f'sms:{scene}:{phone}'
cache.set(cache_key, code, timeout=settings.SMS_CODE_EXPIRE)
try:
get_sms_service().send_code(phone, scene, code)
except SmsError as e:
cache.delete(cache_key)
err_code = str(e) if str(e) in ('SMS_BIZ_ERROR',) else 'SMS_PROVIDER_ERROR'
raise AppError(err_code, '短信发送失败,请稍后重试', status_code=500)
return Response({'message': '验证码已发送'})