from django.db.models import Count, Q from django.shortcuts import get_object_or_404 from drf_spectacular.utils import extend_schema from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.throttling import ScopedRateThrottle from rest_framework.views import APIView from .models import BugReport, Ticket, TicketMessage from .serializers import ( BugReportCreateRequestSerializer, BugReportCreateResponseSerializer, TicketCreateRequestSerializer, TicketCreateResponseSerializer, TicketDetailResponseSerializer, TicketListResponseSerializer, TicketMessageCreateRequestSerializer, TicketMessageCreateResponseSerializer, ) class BugReportCreateView(APIView): """POST /api/support/bug-reports — ثبت گزارش باگ""" permission_classes = [IsAuthenticated] # بدونِ idempotency/throttle، یک کاربر می‌تونست تعدادِ نامحدودی BugReport # بسازه. throttle_scope = 'support_create' throttle_classes = [ScopedRateThrottle] @extend_schema( request=BugReportCreateRequestSerializer, responses=BugReportCreateResponseSerializer, tags=['Support (support)'], ) def post(self, request): serializer = BugReportCreateRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) v = serializer.validated_data bug = BugReport.objects.create( user_id=request.user.id, title=v['title'], description=v['description'], screenshot_url=v.get('screenshotUrl', ''), ) data = {'bugReportId': str(bug.pk), 'status': bug.status} return Response(BugReportCreateResponseSerializer(data).data) class TicketCreateView(APIView): """ GET /api/support/tickets — تیکت‌های همین کاربر (۲۰ تای آخر) POST /api/support/tickets — ایجاد تیکت GET تا ۲۰۲۶-۰۹-۲۹ نبود و اپ شناسه‌ی تیکت‌ها را فقط در حافظه‌ی همان مرورگر نگه می‌داشت؛ با عوض شدنِ دستگاه، تیکت و پاسخِ پشتیبانی از دید پنهان می‌شد. """ LIST_LIMIT = 20 permission_classes = [IsAuthenticated] # مثلِ BugReportCreateView: هیچ idempotency ای نیست، پس این endpoint هم # همون scope=support_create رو به‌اشتراک می‌ذاره تا محدودیتِ ساختِ # رکوردهای پشتیبانی، مستقل از این‌که کدوم نوع رکورده، رعایت بشه. throttle_scope = 'support_create' throttle_classes = [ScopedRateThrottle] def get_throttles(self): # GET سهمِ خواندن را می‌خورد، نه سقفِ ۳۰ در ساعتِ ساختِ رکورد. if self.request.method == 'GET': self.throttle_scope = 'read' return super().get_throttles() @extend_schema( operation_id='support_tickets_list', responses=TicketListResponseSerializer, tags=['Support (support)'], ) def get(self, request): tickets = ( Ticket.objects.filter(user_id=request.user.id) .annotate(support_replies=Count('messages', filter=Q(messages__sender='support'))) .order_by('-created_at')[: self.LIST_LIMIT] ) data = {'tickets': [ { 'ticketId': str(t.pk), 'subject': t.subject, 'category': t.category, 'status': t.status, 'createdAt': t.created_at, 'supportReplies': t.support_replies, } for t in tickets ]} return Response(TicketListResponseSerializer(data).data) @extend_schema( request=TicketCreateRequestSerializer, responses=TicketCreateResponseSerializer, tags=['Support (support)'], ) def post(self, request): serializer = TicketCreateRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) v = serializer.validated_data ticket = Ticket.objects.create( user_id=request.user.id, subject=v['subject'], category=v['category'], priority=v.get('priority', 'normal'), ) data = {'ticketId': str(ticket.pk), 'status': ticket.status} return Response(TicketCreateResponseSerializer(data).data) class TicketDetailView(APIView): """ GET /api/support/tickets/{id} — جزئیات و پیام‌های تیکت فقط تیکت متعلق به همون کاربر برمی‌گرده (فیلتر با user_id، دقیقاً همون الگوی محدودسازی به مالک که در progress/views.py برای UserStats و در ai_tutor/views.py برای Conversation استفاده شده). """ permission_classes = [IsAuthenticated] throttle_scope = 'read' throttle_classes = [ScopedRateThrottle] @extend_schema(responses=TicketDetailResponseSerializer, tags=['Support (support)']) def get(self, request, ticket_id): ticket = get_object_or_404(Ticket, pk=ticket_id, user_id=request.user.id) messages = [ {'id': str(m.pk), 'sender': m.sender, 'text': m.text} for m in ticket.messages.all() ] data = { 'ticketId': str(ticket.pk), 'subject': ticket.subject, 'status': ticket.status, 'messages': messages, } return Response(TicketDetailResponseSerializer(data).data) class TicketMessageCreateView(APIView): """ POST /api/support/tickets/{id}/messages — ارسال پیام در تیکت این Endpoint سمت کاربره، پس sender همیشه 'user' ثبت می‌شه. پاسخ پشتیبانی (sender='support') از پنل ادمین ثبت می‌شه و طبق سند معماری از همون‌جا رویداد TicketReplied برای Notifications ساطع می‌شه (پیاده‌سازی در support/admin.py، نه اینجا). """ permission_classes = [IsAuthenticated] # مثلِ دو Endpoint بالا: بدونِ throttle، یک کاربر می‌تونست تعدادِ # نامحدودی پیام در یک تیکت بسازه. throttle_scope = 'support_create' throttle_classes = [ScopedRateThrottle] @extend_schema( request=TicketMessageCreateRequestSerializer, responses=TicketMessageCreateResponseSerializer, tags=['Support (support)'], ) def post(self, request, ticket_id): ticket = get_object_or_404(Ticket, pk=ticket_id, user_id=request.user.id) serializer = TicketMessageCreateRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) v = serializer.validated_data message = TicketMessage.objects.create(ticket=ticket, sender='user', text=v['text']) data = {'messageId': str(message.pk), 'success': True} return Response(TicketMessageCreateResponseSerializer(data).data)