From 1b06e944733e9f62d7820f2a2188222b348e7efd Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 23 Oct 2025 16:09:40 +0800 Subject: [PATCH] update --- problemset/models.py | 45 ++++++++++++++++++++++ problemset/signals.py | 81 ++++++++++++++++++++++++++++++++++++++- problemset/urls/admin.py | 7 ++++ problemset/views/admin.py | 44 +++++++++++++++++++++ problemset/views/oj.py | 54 ++++++++++---------------- 5 files changed, 196 insertions(+), 35 deletions(-) diff --git a/problemset/models.py b/problemset/models.py index 00d7770..d148cba 100644 --- a/problemset/models.py +++ b/problemset/models.py @@ -87,6 +87,41 @@ class ProblemSetBadge(models.Model): def __str__(self): return f"{self.problemset.title} - {self.name}" + + def recalculate_user_badges(self): + """重新计算所有用户的徽章资格""" + # 获取所有已加入该题单的用户进度 + user_progresses = ProblemSetProgress.objects.filter(problemset=self.problemset) + + # 删除该徽章的所有现有用户徽章记录 + UserBadge.objects.filter(badge=self).delete() + + # 重新评估每个用户的徽章资格 + for progress in user_progresses: + self._check_user_badge_eligibility(progress) + + def _check_user_badge_eligibility(self, progress): + """检查用户是否符合该徽章的条件""" + + # 检查是否已经拥有该徽章 + if UserBadge.objects.filter(user=progress.user, badge=self).exists(): + return False + + # 根据条件类型检查用户是否符合条件 + if self.condition_type == "all_problems": + if progress.completed_problems_count == progress.total_problems_count: + UserBadge.objects.create(user=progress.user, badge=self) + return True + elif self.condition_type == "problem_count": + if progress.completed_problems_count >= self.condition_value: + UserBadge.objects.create(user=progress.user, badge=self) + return True + elif self.condition_type == "score": + if progress.total_score >= self.condition_value: + UserBadge.objects.create(user=progress.user, badge=self) + return True + + return False class ProblemSetProgress(models.Model): @@ -162,6 +197,14 @@ class ProblemSetProgress(models.Model): self.save() + @classmethod + def sync_all_progress_for_problemset(cls, problemset): + """同步指定题单的所有用户进度""" + progresses = cls.objects.filter(problemset=problemset) + for progress in progresses: + progress.update_progress() + return progresses.count() + class ProblemSetSubmission(models.Model): """题单提交记录模型""" @@ -224,3 +267,5 @@ class UserBadge(models.Model): def __str__(self): return f"{self.user.username} - {self.badge.name}" + + diff --git a/problemset/signals.py b/problemset/signals.py index 6c66feb..6cbfb64 100644 --- a/problemset/signals.py +++ b/problemset/signals.py @@ -1,2 +1,81 @@ # 题单应用信号处理 -# 目前暂时为空,后续可以添加信号处理逻辑 +from django.db.models.signals import post_save, post_delete +from django.dispatch import receiver +from .models import ProblemSetProblem, ProblemSetProgress, ProblemSetBadge, UserBadge +from django.db import transaction +import logging + +logger = logging.getLogger(__name__) + + +@receiver(post_save, sender=ProblemSetProblem) +def sync_progress_on_problem_change(sender, instance, created, **kwargs): + """当题单题目发生变化时,同步所有用户的进度""" + try: + with transaction.atomic(): + # 获取该题单的所有用户进度 + progresses = ProblemSetProgress.objects.filter( + problemset=instance.problemset + ) + + # 批量更新所有用户的进度 + for progress in progresses: + progress.update_progress() + + logger.info(f"已同步题单 {instance.problemset.id} 的所有用户进度") + + except Exception as e: + logger.error(f"同步题单进度时出错: {e}") + + +@receiver(post_delete, sender=ProblemSetProblem) +def sync_progress_on_problem_delete(sender, instance, **kwargs): + """当题单题目被删除时,同步所有用户的进度并清理相关提交记录""" + try: + with transaction.atomic(): + # 清理该题目在题单中的所有提交记录 + from .models import ProblemSetSubmission + ProblemSetSubmission.objects.filter( + problemset=instance.problemset, + problem=instance.problem + ).delete() + + # 获取该题单的所有用户进度 + progresses = ProblemSetProgress.objects.filter( + problemset=instance.problemset + ) + + # 批量更新所有用户的进度 + for progress in progresses: + progress.update_progress() + + logger.info(f"已同步题单 {instance.problemset.id} 的所有用户进度(删除题目后)") + + except Exception as e: + logger.error(f"同步题单进度时出错: {e}") + + +@receiver(post_save, sender=ProblemSetBadge) +def sync_badges_on_badge_change(sender, instance, created, **kwargs): + """当题单奖章发生变化时,重新计算所有用户的奖章资格""" + try: + with transaction.atomic(): + # 重新计算该奖章的所有用户资格 + instance.recalculate_user_badges() + logger.info(f"已重新计算题单 {instance.problemset.id} 的奖章 {instance.id} 的用户资格") + + except Exception as e: + logger.error(f"重新计算奖章资格时出错: {e}") + + +@receiver(post_delete, sender=ProblemSetBadge) +def cleanup_badges_on_badge_delete(sender, instance, **kwargs): + """当题单奖章被删除时,清理相关的用户奖章记录""" + try: + with transaction.atomic(): + # 删除该奖章的所有用户奖章记录 + UserBadge.objects.filter(badge=instance).delete() + logger.info(f"已清理奖章 {instance.id} 的所有用户奖章记录") + + except Exception as e: + logger.error(f"清理用户奖章记录时出错: {e}") diff --git a/problemset/urls/admin.py b/problemset/urls/admin.py index 0d53723..0f60737 100644 --- a/problemset/urls/admin.py +++ b/problemset/urls/admin.py @@ -7,6 +7,7 @@ from problemset.views.admin import ( ProblemSetProblemAdminAPI, ProblemSetProgressAdminAPI, ProblemSetStatusAPI, + ProblemSetSyncAPI, ProblemSetVisibleAPI, ) @@ -50,6 +51,12 @@ urlpatterns = [ ProblemSetProgressAdminAPI.as_view(), name="admin_problemset_progress_detail_api", ), + # 题单同步管理API + path( + "problemset//sync", + ProblemSetSyncAPI.as_view(), + name="admin_problemset_sync_api", + ), # 题单状态管理API path( "problemset/visible", diff --git a/problemset/views/admin.py b/problemset/views/admin.py index 6f8e8d7..74bb3eb 100644 --- a/problemset/views/admin.py +++ b/problemset/views/admin.py @@ -177,6 +177,9 @@ class ProblemSetProblemAdminAPI(APIView): hint=data.get("hint", ""), ) + # 同步所有用户的进度 + ProblemSetProgress.sync_all_progress_for_problemset(problem_set) + return self.success("题目已添加到题单") @super_admin_required @@ -208,6 +211,10 @@ class ProblemSetProblemAdminAPI(APIView): problem_set_problem.hint = data["hint"] problem_set_problem.save() + + # 同步所有用户的进度 + ProblemSetProgress.sync_all_progress_for_problemset(problem_set) + return self.success("题目已更新") @super_admin_required @@ -224,6 +231,10 @@ class ProblemSetProblemAdminAPI(APIView): id=problem_set_problem_id, problemset=problem_set ) problem_set_problem.delete() + + # 同步所有用户的进度 + ProblemSetProgress.sync_all_progress_for_problemset(problem_set) + return self.success("题目已从题单中移除") except ProblemSetProblem.DoesNotExist: return self.error("题目不在该题单中") @@ -277,6 +288,10 @@ class ProblemSetBadgeAdminAPI(APIView): return self.error("奖章不存在") data = request.data + + # 记录是否修改了条件相关的字段 + condition_changed = False + # 更新奖章属性 if "name" in data: badge.name = data["name"] @@ -286,12 +301,23 @@ class ProblemSetBadgeAdminAPI(APIView): badge.icon = data["icon"] if "condition_type" in data: badge.condition_type = data["condition_type"] + condition_changed = True if "condition_value" in data: badge.condition_value = data["condition_value"] + condition_changed = True if "level" in data: badge.level = data["level"] badge.save() + + # 如果修改了条件,重新计算所有用户的徽章资格 + if condition_changed: + try: + badge.recalculate_user_badges() + return self.success("奖章已更新,并重新计算了所有用户的徽章资格") + except Exception as e: + return self.error(f"奖章已更新,但重新计算徽章资格时出错: {str(e)}") + return self.success("奖章已更新") @super_admin_required @@ -348,6 +374,24 @@ class ProblemSetProgressAdminAPI(APIView): return self.error("用户未加入该题单") +class ProblemSetSyncAPI(APIView): + """题单同步管理API""" + + @super_admin_required + def post(self, request, problem_set_id): + """手动同步题单的所有用户进度(管理员)""" + try: + problem_set = ProblemSet.objects.get(id=problem_set_id) + ensure_created_by(problem_set, request.user) + except ProblemSet.DoesNotExist: + return self.error("题单不存在") + + # 同步所有用户的进度 + synced_count = ProblemSetProgress.sync_all_progress_for_problemset(problem_set) + + return self.success(f"已同步 {synced_count} 个用户的进度") + + class ProblemSetVisibleAPI(APIView): """题单可见性管理API""" diff --git a/problemset/views/oj.py b/problemset/views/oj.py index fb72f26..bf6ef5b 100644 --- a/problemset/views/oj.py +++ b/problemset/views/oj.py @@ -1,4 +1,3 @@ -from ssl import HAS_SNI from django.db.models import Q from django.utils import timezone @@ -191,21 +190,27 @@ class ProblemSetProgressAPI(APIView): # 更新进度 progress.update_progress() - submission = Submission.objects.get(id=data["submission_id"]) - problem = Problem.objects.get(id=problem_id) + # 只有当提供了submission_id时才创建ProblemSetSubmission记录 + if "submission_id" in data and data["submission_id"]: + try: + submission = Submission.objects.get(id=data["submission_id"]) + problem = Problem.objects.get(id=problem_id) - has_accepted = ProblemSetSubmission.objects.filter( - problemset=problem_set, - user=request.user, - problem=problem, - ).exists() - if not has_accepted: - ProblemSetSubmission.objects.create( - problemset=problem_set, - user=request.user, - submission=submission, - problem=problem, - ) + has_accepted = ProblemSetSubmission.objects.filter( + problemset=problem_set, + user=request.user, + problem=problem, + ).exists() + if not has_accepted: + ProblemSetSubmission.objects.create( + problemset=problem_set, + user=request.user, + submission=submission, + problem=problem, + ) + except Submission.DoesNotExist: + # 如果提交记录不存在,记录错误但不中断流程 + pass # 检查是否获得奖章 self._check_badges(progress) @@ -215,38 +220,20 @@ class ProblemSetProgressAPI(APIView): def _check_badges(self, progress): """检查是否获得奖章""" badges = ProblemSetBadge.objects.filter(problemset=progress.problemset) - print(f"[BadgeCheck] 检查用户 {progress.user.username} 的徽章,题单 {progress.problemset.title}") - print(f"[BadgeCheck] 已完成题目数: {progress.completed_problems_count}, 总题目数: {progress.total_problems_count}") - print(f"[BadgeCheck] 总分数: {progress.total_score}") - print(f"[BadgeCheck] 找到 {badges.count()} 个徽章") for badge in badges: - print(f"[BadgeCheck] 检查徽章: {badge.name} (条件: {badge.condition_type}, 值: {badge.condition_value})") - - # 检查是否已经获得该奖章 if UserBadge.objects.filter(user=progress.user, badge=badge).exists(): - print(f"[BadgeCheck] 用户已获得徽章: {badge.name}") continue - # 检查是否满足获得条件 if badge.condition_type == "all_problems": if progress.completed_problems_count == progress.total_problems_count: - print(f"[BadgeCheck] 满足条件,创建徽章: {badge.name}") UserBadge.objects.create(user=progress.user, badge=badge) - else: - print(f"[BadgeCheck] 不满足条件: 已完成 {progress.completed_problems_count}/{progress.total_problems_count}") elif badge.condition_type == "problem_count": if progress.completed_problems_count >= badge.condition_value: - print(f"[BadgeCheck] 满足条件,创建徽章: {badge.name}") UserBadge.objects.create(user=progress.user, badge=badge) - else: - print(f"[BadgeCheck] 不满足条件: 已完成 {progress.completed_problems_count} < {badge.condition_value}") elif badge.condition_type == "score": if progress.total_score >= badge.condition_value: - print(f"[BadgeCheck] 满足条件,创建徽章: {badge.name}") UserBadge.objects.create(user=progress.user, badge=badge) - else: - print(f"[BadgeCheck] 不满足条件: 总分数 {progress.total_score} < {badge.condition_value}") class UserProgressAPI(APIView): @@ -271,7 +258,6 @@ class UserBadgeAPI(APIView): return self.success(serializer.data) - class ProblemSetBadgeAPI(APIView): """题单奖章API - 用户端"""