from rest_framework import serializers from django.conf import settings from .models import Competition, CompetitionEnrollment, ScoreDimension, Project, ProjectFile, Score, Comment, HomePageConfig, CarouselItem from shop.serializers import WeChatUserSerializer def _media_url(file_field): """返回 media 文件的相对路径,避免 build_absolute_uri 生成容器内部地址""" if file_field and file_field.name: return settings.MEDIA_URL + file_field.name return None class CarouselItemSerializer(serializers.ModelSerializer): display_image = serializers.SerializerMethodField() class Meta: model = CarouselItem fields = ['id', 'carousel_type', 'image', 'image_url', 'display_image', 'title', 'subtitle', 'status', 'status_color', 'date', 'location', 'order', 'is_active'] def get_display_image(self, obj): return _media_url(obj.image) or obj.image_url class HomePageConfigSerializer(serializers.ModelSerializer): display_banner = serializers.SerializerMethodField() carousel1_items = serializers.SerializerMethodField() carousel2_items = serializers.SerializerMethodField() class Meta: model = HomePageConfig fields = ['id', 'banner_image', 'banner_image_url', 'display_banner', 'main_title', 'carousel1_title', 'carousel2_title', 'organizer', 'undertaker', 'carousel1_items', 'carousel2_items'] def get_display_banner(self, obj): return _media_url(obj.banner_image) or obj.banner_image_url def get_carousel1_items(self, obj): items = CarouselItem.objects.filter(carousel_type='carousel1', is_active=True) return CarouselItemSerializer(items, many=True, context=self.context).data def get_carousel2_items(self, obj): items = CarouselItem.objects.filter(carousel_type='carousel2', is_active=True) return CarouselItemSerializer(items, many=True, context=self.context).data class ScoreDimensionSerializer(serializers.ModelSerializer): class Meta: model = ScoreDimension fields = ['id', 'name', 'description', 'weight', 'max_score', 'order'] class CompetitionSerializer(serializers.ModelSerializer): score_dimensions = ScoreDimensionSerializer(many=True, read_only=True) display_cover_image = serializers.SerializerMethodField() status_display = serializers.CharField(source='get_status_display', read_only=True) class Meta: model = Competition fields = ['id', 'title', 'description', 'rule_description', 'condition_description', 'cover_image', 'cover_image_url', 'display_cover_image', 'start_time', 'end_time', 'status', 'project_visibility', 'status_display', 'is_active', 'score_dimensions', 'created_at'] def get_display_cover_image(self, obj): return _media_url(obj.cover_image) or obj.cover_image_url class CompetitionEnrollmentSerializer(serializers.ModelSerializer): user = WeChatUserSerializer(read_only=True) class Meta: model = CompetitionEnrollment fields = ['id', 'competition', 'user', 'role', 'status', 'created_at'] read_only_fields = ['status'] class ProjectFileSerializer(serializers.ModelSerializer): class Meta: model = ProjectFile fields = ['id', 'project', 'file_type', 'file', 'file_url', 'name', 'created_at'] def validate_file(self, value): if not value: return value limit_mb = 50 if value.size > limit_mb * 1024 * 1024: raise serializers.ValidationError(f"文件大小不能超过 {limit_mb}MB") return value class ProjectSerializer(serializers.ModelSerializer): files = ProjectFileSerializer(many=True, read_only=True) contestant_info = serializers.SerializerMethodField() display_cover_image = serializers.SerializerMethodField() class Meta: model = Project fields = ['id', 'competition', 'contestant', 'title', 'description', 'team_info', 'cover_image', 'cover_image_url', 'display_cover_image', 'status', 'final_score', 'files', 'contestant_info', 'created_at'] read_only_fields = ['final_score', 'contestant'] def get_contestant_info(self, obj): return { "nickname": obj.contestant.user.nickname, "avatar_url": obj.contestant.user.avatar_url } def get_display_cover_image(self, obj): return _media_url(obj.cover_image) or obj.cover_image_url class ScoreSerializer(serializers.ModelSerializer): judge_name = serializers.CharField(source='judge.user.nickname', read_only=True) dimension_name = serializers.CharField(source='dimension.name', read_only=True) class Meta: model = Score fields = ['id', 'project', 'judge', 'dimension', 'score', 'judge_name', 'dimension_name', 'created_at'] read_only_fields = ['judge'] class CommentSerializer(serializers.ModelSerializer): judge_name = serializers.CharField(source='judge.user.nickname', read_only=True) score = serializers.SerializerMethodField() class Meta: model = Comment fields = ['id', 'project', 'judge', 'content', 'judge_name', 'created_at', 'score'] read_only_fields = ['judge'] def get_score(self, obj): scores = Score.objects.filter(project=obj.project, judge=obj.judge) if not scores.exists(): return None total_score = 0 total_weight = 0 for score in scores: total_score += score.score * score.dimension.weight total_weight += score.dimension.weight if total_weight > 0: return round(total_score / total_weight, 1) return None