sync from local backup
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework import status
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
|
||||
from user.qr_auth import generate_qr_token, get_qr_status, scan_qr, confirm_qr, cancel_qr
|
||||
from user.models import FUser
|
||||
from user.serializers.user_serializers import UserSerializer
|
||||
from user.services import create_login_record
|
||||
from utils.response_codes import ResponseCode, create_standardized_response, create_standardized_error_response
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from chunyu_project.common_schemas import success_response, error_response
|
||||
|
||||
|
||||
class QRTokenView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['认证'],
|
||||
operation_summary='获取扫码登录二维码',
|
||||
operation_description='生成唯一的 QR Token,返回 token 供前端生成二维码',
|
||||
responses={200: success_response},
|
||||
)
|
||||
def post(self, request):
|
||||
token = generate_qr_token()
|
||||
return create_standardized_response(data={
|
||||
"token": token,
|
||||
"expires_in": 300,
|
||||
})
|
||||
|
||||
|
||||
class QRStatusView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['认证'],
|
||||
operation_summary='查询扫码状态',
|
||||
operation_description='前端轮询此接口获取扫码登录状态',
|
||||
manual_parameters=[
|
||||
openapi.Parameter('token', openapi.IN_PATH, description='QR Token', type=openapi.TYPE_STRING, required=True),
|
||||
],
|
||||
responses={200: success_response},
|
||||
)
|
||||
def get(self, request, token):
|
||||
data = get_qr_status(token)
|
||||
response_data = {
|
||||
"status": data.get("status", "expired"),
|
||||
"username": data.get("scan_username"),
|
||||
}
|
||||
|
||||
if data.get("status") == "confirmed":
|
||||
scan_user_id = data.get("scan_user_id")
|
||||
user = FUser.objects.filter(id=scan_user_id).first()
|
||||
if user:
|
||||
refresh = RefreshToken.for_user(user)
|
||||
response_data["auth"] = {
|
||||
"user": UserSerializer(user).data,
|
||||
"refresh": str(refresh),
|
||||
"access": str(refresh.access_token),
|
||||
"token_type": "bearer",
|
||||
"expires_at_timestamp": refresh.access_token.payload['exp'],
|
||||
}
|
||||
|
||||
return create_standardized_response(data=response_data)
|
||||
|
||||
|
||||
class QRScanView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['认证'],
|
||||
operation_summary='APP扫码上报',
|
||||
operation_description='APP扫描WEB端二维码后,上报token和用户信息',
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['token'],
|
||||
properties={
|
||||
'token': openapi.Schema(type=openapi.TYPE_STRING, description='扫描到的QR Token'),
|
||||
},
|
||||
),
|
||||
responses={200: success_response, 400: error_response, 401: error_response},
|
||||
)
|
||||
def post(self, request):
|
||||
token = request.data.get("token")
|
||||
if not token:
|
||||
return create_standardized_error_response(
|
||||
code=ResponseCode.PARAMETER_ERROR,
|
||||
message="缺少 token",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
user = request.user
|
||||
success = scan_qr(token, user.id, user.username)
|
||||
if not success:
|
||||
return create_standardized_error_response(
|
||||
code=ResponseCode.VALIDATION_ERROR,
|
||||
message="二维码已过期或无效",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
return create_standardized_response(message="扫码成功,等待确认")
|
||||
|
||||
|
||||
class QRConfirmView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['认证'],
|
||||
operation_summary='APP确认登录',
|
||||
operation_description='APP端用户确认登录WEB端',
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['token'],
|
||||
properties={
|
||||
'token': openapi.Schema(type=openapi.TYPE_STRING, description='QR Token'),
|
||||
},
|
||||
),
|
||||
responses={200: success_response, 400: error_response, 401: error_response},
|
||||
)
|
||||
def post(self, request):
|
||||
token = request.data.get("token")
|
||||
if not token:
|
||||
return create_standardized_error_response(
|
||||
code=ResponseCode.PARAMETER_ERROR,
|
||||
message="缺少 token",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
data = confirm_qr(token)
|
||||
if not data:
|
||||
return create_standardized_error_response(
|
||||
code=ResponseCode.VALIDATION_ERROR,
|
||||
message="确认失败,请重新扫码",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
create_login_record(request, request.user, 'success')
|
||||
return create_standardized_response(message="登录确认成功")
|
||||
|
||||
|
||||
class QRCancelView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['认证'],
|
||||
operation_summary='APP取消登录',
|
||||
operation_description='APP端用户取消登录WEB端',
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'token': openapi.Schema(type=openapi.TYPE_STRING, description='QR Token'),
|
||||
},
|
||||
),
|
||||
responses={200: success_response, 401: error_response},
|
||||
)
|
||||
def post(self, request):
|
||||
token = request.data.get("token")
|
||||
if token:
|
||||
cancel_qr(token)
|
||||
return create_standardized_response(message="已取消")
|
||||
Reference in New Issue
Block a user