diff --git a/problemset/migrations/0007_problemset_end_time.py b/problemset/migrations/0007_problemset_end_time.py new file mode 100644 index 0000000..652fddc --- /dev/null +++ b/problemset/migrations/0007_problemset_end_time.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0 on 2026-03-16 15:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('problemset', '0006_remove_is_displayed_field'), + ] + + operations = [ + migrations.AddField( + model_name='problemset', + name='end_time', + field=models.DateTimeField(blank=True, null=True, verbose_name='截止时间'), + ), + ] diff --git a/problemset/models.py b/problemset/models.py index e419eb8..96f9b55 100644 --- a/problemset/models.py +++ b/problemset/models.py @@ -26,6 +26,8 @@ class ProblemSet(models.Model): status = models.TextField( default="draft", verbose_name="状态" ) # active, archived, draft + # 截止时间(到期后自动解除防作弊隐藏) + end_time = models.DateTimeField(null=True, blank=True, verbose_name="截止时间") class Meta: db_table = "problemset" diff --git a/problemset/serializers.py b/problemset/serializers.py index 3015ddd..86a859a 100644 --- a/problemset/serializers.py +++ b/problemset/serializers.py @@ -92,6 +92,7 @@ class ProblemSetListSerializer(serializers.ModelSerializer): "create_time", "difficulty", "status", + "end_time", "problems_count", "user_progress", "badges", @@ -148,6 +149,7 @@ class CreateProblemSetSerializer(serializers.Serializer): description = serializers.CharField() difficulty = serializers.CharField(default="Easy") status = serializers.CharField(default="active") + end_time = serializers.DateTimeField(required=False) class EditProblemSetSerializer(serializers.Serializer): @@ -159,6 +161,7 @@ class EditProblemSetSerializer(serializers.Serializer): difficulty = serializers.CharField(required=False) status = serializers.CharField(required=False) visible = serializers.BooleanField(required=False) + end_time = serializers.DateTimeField(required=False, allow_null=True) class ProblemSetProblemSerializer(serializers.ModelSerializer): diff --git a/submission/serializers.py b/submission/serializers.py index b09f654..b40ec51 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -1,6 +1,10 @@ +from django.db import models +from django.utils import timezone + from .models import Submission from utils.api import serializers from utils.serializers import LanguageNameChoiceField +from problemset.models import ProblemSetProgress class CreateSubmissionSerializer(serializers.Serializer): @@ -50,4 +54,36 @@ class SubmissionListSerializer(serializers.ModelSerializer): # 没传user或为匿名user if self.user is None or not self.user.is_authenticated: return False - return obj.check_user_permission(self.user) + if not obj.check_user_permission(self.user): + return False + # 题单防作弊:用户加入了包含该题目的 active 题单时,隐藏加入前的提交链接 + # 如果该题目已在题单中做出来了,则恢复显示 + if obj.user_id == self.user.id and self.user.is_regular_user(): + progress = self._get_problemset_progress(obj.problem_id) + if ( + progress + and obj.create_time < progress.join_time + and str(obj.problem_id) not in progress.progress_detail + ): + return False + return True + + def _get_problemset_progress(self, problem_id): + """查询用户是否加入了包含该题目的 active 题单,带缓存避免 N+1""" + if not hasattr(self, "_problemset_progress_cache"): + self._problemset_progress_cache = {} + if problem_id not in self._problemset_progress_cache: + self._problemset_progress_cache[problem_id] = ( + ProblemSetProgress.objects.filter( + user=self.user, + problemset__status="active", + problemset__problemsetproblem__problem_id=problem_id, + ) + .filter( + models.Q(problemset__end_time__isnull=True) + | models.Q(problemset__end_time__gt=timezone.now()) + ) + .only("join_time", "progress_detail") + .first() + ) + return self._problemset_progress_cache[problem_id]