from rest_framework import generics, status, permissions from rest_framework.decorators import api_view, permission_classes from rest_framework.response import Response from django.db.models import Count, Q from django.utils import timezone from datetime import datetime, timedelta from django.contrib.auth import get_user_model from .models import DailyReport, ReportComment from .serializers import ( DailyReportListSerializer, DailyReportDetailSerializer, DailyReportCreateUpdateSerializer, ReportCommentSerializer, DailyReportStatsSerializer, UserReportStatsSerializer ) from .permissions import IsOwnerOrStaff, IsOwnerOrStaffReadOnly, IsCommentOwnerOrStaff from .filters import DailyReportFilter User = get_user_model() class DailyReportListCreateView(generics.ListCreateAPIView): """日报列表和创建视图""" permission_classes = [permissions.IsAuthenticated] filterset_class = DailyReportFilter search_fields = ['work_summary', 'next_day_plan', 'user__username', 'user__first_name', 'user__last_name'] ordering_fields = ['report_date', 'created_at', 'updated_at'] ordering = ['-report_date', '-created_at'] def get_queryset(self): """根据用户权限返回不同的查询集""" user = self.request.user if user.is_staff: # 管理员可以查看所有日报 return DailyReport.objects.select_related('user').prefetch_related('comments') else: # 普通用户只能查看自己的日报 return DailyReport.objects.filter(user=user).select_related('user').prefetch_related('comments') def get_serializer_class(self): if self.request.method == 'POST': return DailyReportCreateUpdateSerializer return DailyReportListSerializer def get_filterset_kwargs(self, filterset_class): """传递request给过滤器""" kwargs = super().get_filterset_kwargs(filterset_class) kwargs['request'] = self.request return kwargs class DailyReportDetailView(generics.RetrieveUpdateDestroyAPIView): """日报详情、更新和删除视图""" permission_classes = [permissions.IsAuthenticated, IsOwnerOrStaffReadOnly] def get_queryset(self): """根据用户权限返回不同的查询集""" user = self.request.user if user.is_staff: return DailyReport.objects.select_related('user').prefetch_related('comments__user') else: return DailyReport.objects.filter(user=user).select_related('user').prefetch_related('comments__user') def get_serializer_class(self): if self.request.method in ['PUT', 'PATCH']: return DailyReportCreateUpdateSerializer return DailyReportDetailSerializer class ReportCommentListCreateView(generics.ListCreateAPIView): """日报评论列表和创建视图""" serializer_class = ReportCommentSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): report_id = self.kwargs.get('report_id') return ReportComment.objects.filter( report_id=report_id ).select_related('user', 'report') def perform_create(self, serializer): report_id = self.kwargs.get('report_id') # 验证用户是否有权限查看该日报 try: report = DailyReport.objects.get(id=report_id) if not (report.user == self.request.user or self.request.user.is_staff): raise permissions.PermissionDenied("您没有权限评论此日报") except DailyReport.DoesNotExist: raise generics.NotFound("日报不存在") serializer.save(user=self.request.user, report_id=report_id) class ReportCommentDetailView(generics.RetrieveUpdateDestroyAPIView): """日报评论详情、更新和删除视图""" serializer_class = ReportCommentSerializer permission_classes = [permissions.IsAuthenticated, IsCommentOwnerOrStaff] def get_queryset(self): return ReportComment.objects.select_related('user', 'report') @api_view(['GET']) @permission_classes([permissions.IsAuthenticated]) def report_stats(request): """获取日报统计信息""" user = request.user if user.is_staff: # 管理员查看全局统计 queryset = DailyReport.objects.all() else: # 普通用户查看个人统计 queryset = DailyReport.objects.filter(user=user) now = timezone.now() this_month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) this_week_start = now - timedelta(days=now.weekday()) this_week_start = this_week_start.replace(hour=0, minute=0, second=0, microsecond=0) total_reports = queryset.count() this_month_reports = queryset.filter(report_date__gte=this_month_start.date()).count() this_week_reports = queryset.filter(report_date__gte=this_week_start.date()).count() draft_reports = queryset.filter(is_draft=True).count() # 计算完成率(本月) days_in_month = (now.replace(month=now.month+1, day=1) - timedelta(days=1)).day if now.month < 12 else 31 current_day = now.day expected_reports = current_day if not user.is_staff else User.objects.filter(is_active=True).count() * current_day completion_rate = (this_month_reports / expected_reports * 100) if expected_reports > 0 else 0 stats = { 'total_reports': total_reports, 'this_month_reports': this_month_reports, 'this_week_reports': this_week_reports, 'draft_reports': draft_reports, 'completion_rate': round(completion_rate, 2) } serializer = DailyReportStatsSerializer(stats) return Response(serializer.data) @api_view(['GET']) @permission_classes([permissions.IsAuthenticated]) def user_report_stats(request): """获取用户日报统计信息(仅管理员)""" if not request.user.is_staff: return Response({'error': '权限不足'}, status=status.HTTP_403_FORBIDDEN) users = User.objects.filter(is_active=True).annotate( total_reports=Count('daily_reports'), this_month_reports=Count( 'daily_reports', filter=Q(daily_reports__report_date__gte=timezone.now().replace(day=1).date()) ) ).prefetch_related('daily_reports') stats_list = [] for user in users: last_report = user.daily_reports.first() last_report_date = last_report.report_date if last_report else None # 计算完成率 current_day = timezone.now().day completion_rate = (user.this_month_reports / current_day * 100) if current_day > 0 else 0 stats_list.append({ 'user': user, 'total_reports': user.total_reports, 'this_month_reports': user.this_month_reports, 'last_report_date': last_report_date, 'completion_rate': round(completion_rate, 2) }) # 按完成率排序 stats_list.sort(key=lambda x: x['completion_rate'], reverse=True) serializer = UserReportStatsSerializer(stats_list, many=True) return Response(serializer.data) @api_view(['POST']) @permission_classes([permissions.IsAuthenticated]) def toggle_draft_status(request, pk): """切换日报的草稿状态""" try: report = DailyReport.objects.get(pk=pk) # 检查权限 if report.user != request.user and not request.user.is_staff: return Response({'error': '权限不足'}, status=status.HTTP_403_FORBIDDEN) report.is_draft = not report.is_draft report.save() status_text = '草稿' if report.is_draft else '已发布' return Response({ 'message': f'日报状态已更新为:{status_text}', 'is_draft': report.is_draft }) except DailyReport.DoesNotExist: return Response({'error': '日报不存在'}, status=status.HTTP_404_NOT_FOUND)