62 lines
2.4 KiB
Python
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': '验证码已发送'})
|