Files
chunyu_project/tool/views/tool_manage_view.py
T
2026-08-05 23:59:15 +08:00

228 lines
8.4 KiB
Python

from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.permissions import AllowAny, IsAuthenticated
from django.db.models import Count, Q
from ..models import ToolCategory, Tool, ToolFavorite
from ..serializers import ToolCategorySerializer, ToolSerializer
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
class ToolCategoryListView(APIView):
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='获取工具分类列表',
operation_description='获取所有工具分类及其关联的工具列表',
tags=['工具'],
responses={200: success_response}
)
def get(self, request):
categories = ToolCategory.objects.prefetch_related('tools').all()
serializer = ToolCategorySerializer(categories, many=True)
return JsonResponse({
'success': True,
'data': serializer.data
})
class ToolListView(APIView):
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='获取工具列表',
operation_description='按分类获取工具列表,支持启用状态过滤',
tags=['工具'],
manual_parameters=[
openapi.Parameter('category_id', openapi.IN_QUERY, description='分类ID', type=openapi.TYPE_INTEGER),
openapi.Parameter('enabled_only', openapi.IN_QUERY, description='是否只显示启用的工具', type=openapi.TYPE_STRING),
openapi.Parameter('ordering', openapi.IN_QUERY, description='排序字段', type=openapi.TYPE_STRING),
],
responses={200: success_response}
)
def get(self, request):
category_id = request.query_params.get('category_id')
enabled_only = request.query_params.get('enabled_only', 'true')
ordering = request.query_params.get('ordering', '')
tools = Tool.objects.select_related('category').annotate(
annotated_favorites_count=Count('tool_favorites')
)
if enabled_only == 'true':
tools = tools.filter(is_enabled=True)
if category_id:
tools = tools.filter(category_id=category_id)
if ordering:
# 支持按 favorites_count 排序
if ordering == '-favorites_count':
tools = tools.order_by('-annotated_favorites_count')
elif ordering == 'favorites_count':
tools = tools.order_by('annotated_favorites_count')
else:
tools = tools.order_by(ordering)
else:
tools = tools.order_by('sort_order', '-created_at')
serializer = ToolSerializer(tools, many=True)
data = serializer.data
# 构建 favorites_count 映射
favorites_count_map = {
t.id: t.annotated_favorites_count for t in tools
}
if request.user.is_authenticated:
favorite_tool_ids = set(
ToolFavorite.objects.filter(user=request.user).values_list('tool_id', flat=True)
)
for item in data:
item['is_favorited'] = item['id'] in favorite_tool_ids
item['favorites_count'] = favorites_count_map.get(item['id'], 0)
else:
for item in data:
item['is_favorited'] = False
item['favorites_count'] = favorites_count_map.get(item['id'], 0)
return JsonResponse({
'success': True,
'data': data
})
class ToolDetailView(APIView):
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='获取工具详情',
operation_description='获取单个工具的详细信息',
tags=['工具'],
manual_parameters=[
openapi.Parameter('pk', openapi.IN_PATH, description='工具ID', type=openapi.TYPE_INTEGER, required=True)
],
responses={200: success_response, 404: not_found_response}
)
def get(self, request, pk):
try:
tool = Tool.objects.select_related('category').get(pk=pk, is_enabled=True)
serializer = ToolSerializer(tool)
data = serializer.data
if request.user.is_authenticated:
data['is_favorited'] = ToolFavorite.objects.filter(
user=request.user, tool=tool
).exists()
else:
data['is_favorited'] = False
data['favorites_count'] = ToolFavorite.objects.filter(tool=tool).count()
return JsonResponse({
'success': True,
'data': data
})
except Tool.DoesNotExist:
return JsonResponse(
{'success': False, 'error': '工具不存在'},
status=status.HTTP_404_NOT_FOUND
)
class ToolFavoriteToggleView(APIView):
permission_classes = [IsAuthenticated]
@swagger_auto_schema(
operation_summary='切换工具收藏状态',
operation_description='添加或取消工具收藏',
tags=['工具'],
manual_parameters=[
openapi.Parameter('pk', openapi.IN_PATH, description='工具ID', type=openapi.TYPE_INTEGER, required=True)
],
responses={200: success_response, 401: unauthorized_response, 404: not_found_response}
)
def post(self, request, pk):
tool = Tool.objects.filter(pk=pk, is_enabled=True).first()
if not tool:
return JsonResponse(
{'success': False, 'error': '工具不存在'},
status=status.HTTP_404_NOT_FOUND
)
fav, created = ToolFavorite.objects.get_or_create(user=request.user, tool=tool)
if not created:
fav.delete()
is_favorited = False
else:
is_favorited = True
favorites_count = ToolFavorite.objects.filter(tool=tool).count()
return JsonResponse({
'success': True,
'data': {
'id': tool.id,
'is_favorited': is_favorited,
'favorites_count': favorites_count,
}
})
class ToolFavoriteListView(APIView):
permission_classes = [IsAuthenticated]
@swagger_auto_schema(
operation_summary='获取我的收藏工具列表',
operation_description='获取当前用户收藏的所有工具',
tags=['工具'],
responses={200: success_response, 401: unauthorized_response}
)
def get(self, request):
favorites = ToolFavorite.objects.select_related('tool', 'tool__category').filter(
user=request.user,
tool__is_enabled=True
).order_by('-created_at')
result = []
for fav in favorites:
tool_data = ToolSerializer(fav.tool).data
tool_data['is_favorited'] = True
tool_data['favorites_count'] = ToolFavorite.objects.filter(tool=fav.tool).count()
result.append(tool_data)
return JsonResponse({
'success': True,
'data': result
})
class ToolUsageIncrementView(APIView):
"""工具使用次数递增接口"""
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='递增工具使用次数',
operation_description='用户点击工具时调用,递增该工具的使用次数(基于会话去重,同一会话5分钟内不重复计数)',
tags=['工具'],
manual_parameters=[
openapi.Parameter('pk', openapi.IN_PATH, description='工具ID', type=openapi.TYPE_INTEGER, required=True)
],
responses={200: success_response, 404: not_found_response}
)
def post(self, request, pk):
try:
tool = Tool.objects.get(pk=pk, is_enabled=True)
# 使用F()表达式避免竞态条件
from django.db.models import F
Tool.objects.filter(pk=pk).update(usage_count=F('usage_count') + 1)
tool.refresh_from_db()
return JsonResponse({
'success': True,
'data': {
'id': tool.id,
'usage_count': tool.usage_count,
}
})
except Tool.DoesNotExist:
return JsonResponse(
{'success': False, 'error': '工具不存在'},
status=status.HTTP_404_NOT_FOUND
)