From 8ce3660c87db9487e09bf074f60cf2764a8ff8df Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Tue, 4 Aug 2026 01:08:06 -0600 Subject: [PATCH] =?UTF-8?q?fix(achievement):=20=E5=85=83=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E5=9B=9E=E5=86=99=E6=94=B9=E4=B8=BA=E9=87=8D=E6=96=B0=E5=8F=96?= =?UTF-8?q?=E9=94=81=E9=87=8D=E8=AF=BB=EF=BC=8C=E9=81=BF=E5=85=8D=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- achievement/checker.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/achievement/checker.py b/achievement/checker.py index 6138b92..0ed7fa3 100644 --- a/achievement/checker.py +++ b/achievement/checker.py @@ -84,13 +84,21 @@ def run_for_submission(user, submission): return [] # 第二轮:只重算元指标、只判定依赖元指标的成就,不再有第三轮 - for key in META_METRICS: - value = METRIC_REGISTRY[key].recompute(user) - if value is None: - stat.metrics.pop(key, None) - else: - stat.metrics[key] = value - stat.save(update_fields=["metrics", "update_time"]) + meta_values = {key: METRIC_REGISTRY[key].recompute(user) for key in META_METRICS} + + # 必须重新取锁并重新读一次 stat:上面那个 stat 对象的 metrics 是解锁前的快照, + # 直接 save 会把整份字典写回,覆盖掉并发判题在这期间已提交的增量 + # (同一用户两次提交并发判题时会让提交数/AC 数静默倒退,且无定期重算兜底)。 + # 这里只合并元指标那几个 key。 + with transaction.atomic(): + stat = UserStat.objects.select_for_update().get(user=user) + for key, value in meta_values.items(): + if value is None: + stat.metrics.pop(key, None) + else: + stat.metrics[key] = value + stat.save(update_fields=["metrics", "update_time"]) + second = unlock(user, evaluate(user, stat.metrics, only_metrics=META_METRICS)) return first + second