from rest_framework import status from rest_framework.permissions import IsAuthenticated from rest_framework.parsers import JSONParser from adrf.views import APIView from rest_framework.pagination import PageNumberPagination from rest_framework.response import Response from asgiref.sync import sync_to_async from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi from chunyu_project.common_schemas import success_response, error_response, unauthorized_response, not_found_response from adrf import generics from adrf.generics import aget_object_or_404 from utils.response_codes import ResponseCode, create_standardized_response, create_standardized_error_response from .models import BrowsingHistory from .serializers import BrowsingHistorySerializer, BrowsingHistoryCreateSerializer class HistoryPagination(PageNumberPagination): page_size = 50 page_size_query_param = 'page_size' max_page_size = 200 def get_paginated_response(self, data): return Response({ 'code': 10000, 'message': 'Success', 'data': { 'count': self.page.paginator.count, 'next': self.get_next_link(), 'previous': self.get_previous_link(), 'results': data, } }) class BrowsingHistoryListCreateView(generics.ListCreateAPIView): permission_classes = [IsAuthenticated] parser_classes = [JSONParser] pagination_class = HistoryPagination def get_serializer_class(self): if self.request.method == 'POST': return BrowsingHistoryCreateSerializer return BrowsingHistorySerializer def get_queryset(self): qs = BrowsingHistory.objects.filter(user=self.request.user) # 隐藏实用工具、编程学习和文章资源 qs = qs.exclude(type__in=['utility', 'learn', 'article']) record_type = self.request.query_params.get('type') if record_type: qs = qs.filter(type=record_type) return qs @swagger_auto_schema( tags=['历史'], operation_summary='获取浏览历史列表', operation_description='获取当前用户的浏览历史记录,支持按类型筛选和分页', manual_parameters=[ openapi.Parameter('type', openapi.IN_QUERY, description='记录类型筛选: tool/api', type=openapi.TYPE_STRING), openapi.Parameter('page', openapi.IN_QUERY, description='页码', type=openapi.TYPE_INTEGER), openapi.Parameter('page_size', openapi.IN_QUERY, description='每页数量', type=openapi.TYPE_INTEGER), ], responses={200: success_response, 401: unauthorized_response} ) async def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) # 兜底:DRF 分页器内部同步评估 queryset(count + 切片取值) page = await sync_to_async(self.paginate_queryset)(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(await serializer.adata) results = [x async for x in queryset] serializer = self.get_serializer(results, many=True) return Response({'code': 10000, 'message': 'Success', 'data': await serializer.adata}) @swagger_auto_schema( tags=['历史'], operation_summary='创建浏览记录', operation_description='记录用户的浏览历史,同一链接一小时内重复访问会更新时间', request_body=BrowsingHistoryCreateSerializer, responses={201: success_response, 400: error_response, 401: unauthorized_response} ) async def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) is_valid = serializer.is_valid() if is_valid: # 兜底:serializer.create 内部有同步 ORM(filter/first/save/create) instance = await serializer.asave() return create_standardized_response( data=BrowsingHistorySerializer(instance).data, code=ResponseCode.SUCCESS, message='记录成功', status_code=status.HTTP_201_CREATED ) return create_standardized_error_response( data=serializer.errors, code=ResponseCode.VALIDATION_ERROR, status_code=status.HTTP_400_BAD_REQUEST ) class BrowsingHistoryDeleteView(APIView): permission_classes = [IsAuthenticated] @swagger_auto_schema( tags=['历史'], operation_summary='删除浏览记录', operation_description='删除指定的浏览历史记录', manual_parameters=[ openapi.Parameter('pk', openapi.IN_PATH, description='记录ID', type=openapi.TYPE_INTEGER, required=True), ], responses={204: '删除成功', 401: unauthorized_response, 404: not_found_response} ) async def delete(self, request, pk): record = await aget_object_or_404(BrowsingHistory, pk=pk, user=request.user) await record.adelete() return create_standardized_response( code=ResponseCode.SUCCESS, message='删除成功', status_code=status.HTTP_204_NO_CONTENT ) class BrowsingHistoryClearView(APIView): permission_classes = [IsAuthenticated] @swagger_auto_schema( tags=['历史'], operation_summary='清空浏览记录', operation_description='清空当前用户的所有浏览历史记录', responses={200: success_response, 401: unauthorized_response} ) async def delete(self, request): count = (await BrowsingHistory.objects.filter(user=request.user).adelete())[0] return create_standardized_response( data={'deleted': count}, code=ResponseCode.SUCCESS, message='清空成功' )