from rest_framework import viewsets, permissions, status, filters, serializers from rest_framework.decorators import action, api_view, permission_classes from rest_framework.response import Response from django.db.models import Q from shop.utils import get_current_wechat_user from .models import Competition, CompetitionEnrollment, Project, ProjectFile, Score, Comment, ScoreDimension, HomePageConfig, CarouselItem from .serializers import ( CompetitionSerializer, CompetitionEnrollmentSerializer, ProjectSerializer, ProjectFileSerializer, ScoreSerializer, CommentSerializer, ScoreDimensionSerializer, HomePageConfigSerializer, CarouselItemSerializer ) from rest_framework.pagination import PageNumberPagination @api_view(['GET']) @permission_classes([permissions.AllowAny]) def get_homepage_config(request): """获取首页配置""" try: config = HomePageConfig.objects.filter(is_active=True).first() if not config: config = HomePageConfig.objects.create() serializer = HomePageConfigSerializer(config, context={'request': request}) return Response(serializer.data) except Exception as e: return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class CarouselItemViewSet(viewsets.ModelViewSet): """轮播图项目管理""" queryset = CarouselItem.objects.all() serializer_class = CarouselItemSerializer permission_classes = [permissions.AllowAny] filter_backends = [filters.SearchFilter] search_fields = ['title'] def get_queryset(self): queryset = CarouselItem.objects.all() carousel_type = self.request.query_params.get('carousel_type') if carousel_type: queryset = queryset.filter(carousel_type=carousel_type) return queryset class StandardResultsSetPagination(PageNumberPagination): page_size = 10 page_size_query_param = 'page_size' max_page_size = 100 class CompetitionViewSet(viewsets.ReadOnlyModelViewSet): """ 比赛视图集 """ queryset = Competition.objects.filter(is_active=True).order_by('created_at') serializer_class = CompetitionSerializer permission_classes = [permissions.AllowAny] pagination_class = StandardResultsSetPagination filter_backends = [filters.SearchFilter] search_fields = ['title', 'description'] def get_serializer_context(self): context = super().get_serializer_context() context['request'] = self.request return context def get_queryset(self): """ 获取比赛查询集,支持根据查询参数进行动态过滤 """ queryset = super().get_queryset() # 状态过滤 status_param = self.request.query_params.get('status') if status_param and status_param != 'all': queryset = queryset.filter(status=status_param) return queryset @action(detail=True, methods=['post'], permission_classes=[permissions.AllowAny]) def enroll(self, request, pk=None): """ 报名参加比赛 """ competition = self.get_object() user = get_current_wechat_user(request) if not user: return Response({"detail": "未登录"}, status=status.HTTP_401_UNAUTHORIZED) role = request.data.get('role', 'contestant') # 检查是否已报名 if CompetitionEnrollment.objects.filter(competition=competition, user=user).exists(): return Response({"detail": "您已报名该比赛"}, status=status.HTTP_400_BAD_REQUEST) enrollment = CompetitionEnrollment.objects.create( competition=competition, user=user, role=role, status='pending' # 默认待审核 ) return Response(CompetitionEnrollmentSerializer(enrollment).data, status=status.HTTP_201_CREATED) @action(detail=True, methods=['get']) def my_enrollment(self, request, pk=None): """ 获取我的报名信息 """ competition = self.get_object() user = get_current_wechat_user(request) if not user: return Response({"detail": "未登录"}, status=status.HTTP_401_UNAUTHORIZED) try: enrollment = CompetitionEnrollment.objects.get(competition=competition, user=user) return Response(CompetitionEnrollmentSerializer(enrollment).data) except CompetitionEnrollment.DoesNotExist: return Response({"detail": "未报名"}, status=status.HTTP_404_NOT_FOUND) @action(detail=False, methods=['get']) def my_enrollments(self, request): """ 获取我的所有报名信息 """ user = get_current_wechat_user(request) if not user: return Response([]) enrollments = CompetitionEnrollment.objects.filter(user=user) return Response(CompetitionEnrollmentSerializer(enrollments, many=True).data) class ProjectViewSet(viewsets.ModelViewSet): """ 参赛项目视图集 """ serializer_class = ProjectSerializer permission_classes = [permissions.AllowAny] pagination_class = StandardResultsSetPagination def get_queryset(self): queryset = Project.objects.all() competition_id = self.request.query_params.get('competition') if competition_id: queryset = queryset.filter(competition_id=competition_id) contestant_id = self.request.query_params.get('contestant') if contestant_id: queryset = queryset.filter(contestant_id=contestant_id) user = get_current_wechat_user(self.request) # 1. 基础条件:公开可见且已提交的项目 q = Q(competition__project_visibility='public', status='submitted') if user: # 2. 用户自己的项目(始终可见,包括草稿) q |= Q(contestant__user=user) # 3. 基于角色的可见性 # 获取用户已通过审核的报名信息 enrollments = CompetitionEnrollment.objects.filter(user=user, status='approved') # 获取各角色的比赛ID集合 judge_comp_ids = set(enrollments.filter(role='judge').values_list('competition_id', flat=True)) guest_comp_ids = set(enrollments.filter(role='guest').values_list('competition_id', flat=True)) contestant_comp_ids = set(enrollments.filter(role='contestant').values_list('competition_id', flat=True)) # 'judge' 可见性:仅评委可见 if judge_comp_ids: q |= Q(competition__project_visibility='judge', competition__in=judge_comp_ids, status='submitted') # 'guest' 可见性:嘉宾及评委可见 guest_access_ids = judge_comp_ids | guest_comp_ids if guest_access_ids: q |= Q(competition__project_visibility='guest', competition__in=guest_access_ids, status='submitted') # 'contestant' 可见性:选手及以上可见(包括评委、嘉宾) contestant_access_ids = judge_comp_ids | guest_comp_ids | contestant_comp_ids if contestant_access_ids: q |= Q(competition__project_visibility='contestant', competition__in=contestant_access_ids, status='submitted') queryset = queryset.filter(q) return queryset.order_by('-final_score', '-created_at') def perform_create(self, serializer): user = get_current_wechat_user(self.request) if not user: raise serializers.ValidationError("请先登录") competition = serializer.validated_data['competition'] # 检查是否有参赛资格 try: enrollment = CompetitionEnrollment.objects.get( competition=competition, user=user, role='contestant', status='approved' ) except CompetitionEnrollment.DoesNotExist: raise serializers.ValidationError("您没有参赛资格或审核未通过") # 检查是否已提交过项目 if Project.objects.filter(competition=competition, contestant=enrollment).exists(): raise serializers.ValidationError("您已提交过该比赛的项目,请勿重复提交") serializer.save(contestant=enrollment) @action(detail=True, methods=['post']) def submit(self, request, pk=None): """ 提交项目(从草稿转为已提交) """ project = self.get_object() user = get_current_wechat_user(request) if project.contestant.user != user: return Response({"detail": "无权操作"}, status=status.HTTP_403_FORBIDDEN) project.status = 'submitted' project.save() return Response({"status": "submitted"}) class ProjectFileViewSet(viewsets.ModelViewSet): """ 项目附件管理 """ serializer_class = ProjectFileSerializer permission_classes = [permissions.AllowAny] def get_queryset(self): return ProjectFile.objects.all() def perform_create(self, serializer): # 简单权限控制:只有项目拥有者可以上传 project = serializer.validated_data['project'] user = get_current_wechat_user(self.request) if not user or project.contestant.user != user: raise serializers.ValidationError("无权上传文件") serializer.save() class ScoreViewSet(viewsets.ModelViewSet): """ 评分管理 """ serializer_class = ScoreSerializer permission_classes = [permissions.AllowAny] def get_queryset(self): project_id = self.request.query_params.get('project') if project_id: return Score.objects.filter(project_id=project_id) return Score.objects.all() def perform_create(self, serializer): user = get_current_wechat_user(self.request) if not user: raise serializers.ValidationError("请先登录") project = serializer.validated_data['project'] # 检查是否是评委 try: enrollment = CompetitionEnrollment.objects.get( competition=project.competition, user=user, role='judge', status='approved' ) except CompetitionEnrollment.DoesNotExist: raise serializers.ValidationError("您不是该比赛的评委") # 检查是否重复打分 dimension = serializer.validated_data['dimension'] if Score.objects.filter(project=project, judge=enrollment, dimension=dimension).exists(): raise serializers.ValidationError("您已对该维度打分") serializer.save(judge=enrollment) class CommentViewSet(viewsets.ModelViewSet): """ 评语管理 """ serializer_class = CommentSerializer permission_classes = [permissions.AllowAny] def get_queryset(self): project_id = self.request.query_params.get('project') if project_id: return Comment.objects.filter(project_id=project_id) return Comment.objects.all() def perform_create(self, serializer): user = get_current_wechat_user(self.request) if not user: raise serializers.ValidationError("请先登录") project = serializer.validated_data['project'] # 检查是否是评委 try: enrollment = CompetitionEnrollment.objects.get( competition=project.competition, user=user, role='judge', status='approved' ) except CompetitionEnrollment.DoesNotExist: raise serializers.ValidationError("您不是该比赛的评委") serializer.save(judge=enrollment)