from django.http import JsonResponse from adrf.views import APIView from rest_framework.permissions import IsAuthenticated from rest_framework.parsers import JSONParser from asgiref.sync import sync_to_async from ..models import Tool, ToolFavorite class ToolFavoriteToggleView(APIView): permission_classes = [IsAuthenticated] parser_classes = [JSONParser] async def post(self, request, tool_id): # 事务段(get_or_create + delete + count)整体放入同步函数,sync_to_async 执行保证原子性 def _toggle_in_txn(): from django.db import transaction with transaction.atomic(): try: tool = Tool.objects.get(pk=tool_id, is_enabled=True) except Tool.DoesNotExist: return None favorite, created = ToolFavorite.objects.get_or_create( user=request.user, tool=tool ) if not created: favorite.delete() is_favorite = False message = '已取消收藏' else: is_favorite = True message = '已添加收藏' favorite_count = ToolFavorite.objects.filter(tool=tool).count() return { 'tool_id': tool.id, 'is_favorite': is_favorite, 'favorite_count': favorite_count, 'message': message, } result = await sync_to_async(_toggle_in_txn)() if result is None: return JsonResponse({'success': False, 'error': '工具不存在'}, status=404) return JsonResponse({ 'success': True, 'data': { 'id': result['tool_id'], 'is_favorite': result['is_favorite'], 'favorite_count': result['favorite_count'] }, 'message': result['message'] }) class ToolFavoriteListView(APIView): permission_classes = [IsAuthenticated] async def get(self, request): favorites = [fav async for fav in ToolFavorite.objects.select_related( 'tool', 'tool__category' ).filter( user=request.user, tool__is_enabled=True ).order_by('-created_at')] data = [] for fav in favorites: favorite_count = await ToolFavorite.objects.filter(tool=fav.tool).acount() data.append({ 'id': fav.tool.id, 'name': fav.tool.name, 'description': fav.tool.description, 'icon': fav.tool.icon, 'url_path': fav.tool.url_path, 'color': fav.tool.color, 'category': fav.tool.category.id if fav.tool.category else None, 'category_name': fav.tool.category.name if fav.tool.category else '', 'is_favorite': True, 'favorite_count': favorite_count, 'created_at': fav.created_at.isoformat() }) return JsonResponse({ 'success': True, 'data': { 'count': len(data), 'results': data } }) class ToolFavoriteStatusView(APIView): permission_classes = [IsAuthenticated] async def get(self, request, tool_id): is_favorite = await ToolFavorite.objects.filter( user=request.user, tool_id=tool_id ).aexists() favorite_count = await ToolFavorite.objects.filter(tool_id=tool_id).acount() return JsonResponse({ 'success': True, 'data': { 'tool_id': tool_id, 'is_favorite': is_favorite, 'favorite_count': favorite_count } })