fix(achievement): distinct() 前必须 order_by() 清掉默认排序

Submission.Meta 有 ordering=("-create_time",),Django 会把排序字段
并入 DISTINCT,导致 _languages 每条提交各成一行——生产环境实测
某用户 659 条而不是 5 种语言。

recompute 侥幸正确只因 .count() 会清排序,两个实现同一个量却不一致。
三处 distinct() 全部显式加 order_by()。

由 check_achievement_deploy 的自检项发现。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 09:50:19 -06:00
parent f95c3a8bae
commit 1504164ae8

View File

@@ -103,7 +103,7 @@ class AcceptedCount(BaseMetric):
metrics["accepted_count"] = metrics.get("accepted_count", 0) + 1
def recompute(self, user):
return _practice_submissions(user.id).filter(result__in=ACCEPTED_RESULTS).values("problem_id").distinct().count()
return _practice_submissions(user.id).filter(result__in=ACCEPTED_RESULTS).order_by().values("problem_id").distinct().count()
@metric("submission_count", "提交总数", "提交次数(不含比赛)")
@@ -181,10 +181,14 @@ class LanguagesUsed(BaseMetric):
metrics["languages_used"] = len(seen)
def recompute(self, user):
return _practice_submissions(user.id).values("language").distinct().count()
return _practice_submissions(user.id).order_by().values("language").distinct().count()
def recompute_state(self, user):
return {"_languages": list(_practice_submissions(user.id).values_list("language", flat=True).distinct())}
# order_by() 不能省Submission.Meta 有默认排序 ("-create_time",)
# Django 会把排序字段并入 DISTINCT于是每条提交各成一行——
# 实测某用户返回 659 条而不是 5 种语言。
# recompute 侥幸正确只是因为 .count() 会清掉排序,不能依赖这一点。
return {"_languages": list(_practice_submissions(user.id).order_by().values_list("language", flat=True).distinct())}
@metric("contest_joined", "参赛场次", "参加过的比赛数量(本指标是比赛维度,不受比赛提交不计入的限制)")
@@ -194,7 +198,7 @@ class ContestJoined(BaseMetric):
return
def recompute(self, user):
return Submission.objects.filter(user_id=user.id, contest_id__isnull=False).values("contest_id").distinct().count()
return Submission.objects.filter(user_id=user.id, contest_id__isnull=False).order_by().values("contest_id").distinct().count()
@metric("badge_count", "题单奖章数", "获得的题单奖章数量")