feat(成就): 下架三个拿不到的成就、理顺稀有度和图标;接口多给本班几人有和「本班」卡片

- 0032:下架六边形战士(判题机只剩 3 种语言)、坚持三天/一周(按天算基本拿不到);
  不撞南墙改白银、屡败屡战改黄金;百发百中换 💯、屡败屡战换 💪,不再和别的撞图标;
  代码行数只算做对的、空行不算,并按新口径重算所有人;已经拿到的一个不收回
- 奖杯收藏家只数上架的成就(判题结算、补发重算、0032 存量一起改)
- GET /achievements:每个成就带全站几人有、本班几人有;看自己时带「本班」(最近拿到、成就最多前五、自己第几);
  隐藏成就同一串拿到一档就整串亮出来;连续几周给「现在连着几周」而不是历史最长
- 删掉只有旧成就页在用的 /achievements/summary

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-13 00:26:37 +08:00
1 parent 689d40b116
commit 85108df9d8
9 files changed
+4220 -213

No files matched your search

@@ -0,0 +1,60 @@
-- 成就清单的五处毛病(我的成就重设计,2026-10-13)。只改配置和进度,已经拿到的一个不收回。
--
-- 1. 下架拿不到的:六边形战士要用过 5 种语言,判题机只剩 C / C++ / Python;
-- 坚持三天 / 一周按天算,学生跟着课表来,基本拿不到(按周的「周周向上」那一串已经顶上了)。
UPDATE "achievement" SET "visible" = false
WHERE ("metric" = 'languages_used' AND "threshold" = 5) OR "metric" = 'max_ac_streak_days';
--> statement-breakpoint
UPDATE "achievement" SET "description" = 'C、C++、Python 都用过'
WHERE "metric" = 'languages_used' AND "threshold" = 3;
--> statement-breakpoint
-- 2. 稀有度倒挂:错 10 次才做对是黄金、错 20 次反而是白银
UPDATE "achievement" SET "rarity" = 'silver' WHERE "metric" = 'max_wa_before_ac' AND "threshold" = 10;
--> statement-breakpoint
-- 3. 图标撞了:屡败屡战和持之以恒都是火,百发百中和一发入魂是同一个靶子(noto:direct-hit 是 bullseye 的别名)
UPDATE "achievement" SET "rarity" = 'gold', "icon" = 'noto:flexed-biceps'
WHERE "metric" = 'max_wa_before_ac' AND "threshold" = 20;
--> statement-breakpoint
UPDATE "achievement" SET "icon" = 'noto:hundred-points'
WHERE "metric" = 'first_try_ac_count' AND "threshold" = 100;
--> statement-breakpoint
-- 4. 代码行数只算做对的、空行不算:原来交什么都算,贴一大段就是「长篇巨制」
UPDATE "achievement" SET "description" = '做对的代码有 ' || "threshold" || ' 行(空行不算)'
WHERE "metric" = 'max_code_lines';
--> statement-breakpoint
-- 按新口径重算所有人的 max_code_lines(判题结算里的口径见 services/achievements.ts):
-- 不含比赛,做对 = ACCEPTED(0) / AST_CHECK_FAILED(10)
UPDATE "user_stat" AS target
SET "metrics" = jsonb_set(target."metrics", '{max_code_lines}', to_jsonb(coalesce(fresh.n, 0)))
FROM (
SELECT st."id", m.n
FROM "user_stat" st
LEFT JOIN (
SELECT s."user_id", max((
SELECT count(*) FROM regexp_split_to_table(s."code", E'\n') AS line
WHERE btrim(line, E' \t\r') <> ''
))::int AS n
FROM "submission" s
WHERE s."contest_id" IS NULL AND s."result" IN (0, 10)
GROUP BY s."user_id"
) m ON m."user_id" = st."user_id"
) fresh
WHERE target."id" = fresh."id"
AND (target."metrics" -> 'max_code_lines') IS DISTINCT FROM to_jsonb(coalesce(fresh.n, 0));
--> statement-breakpoint
-- 5. 奖杯收藏家数的「已拿到几个」只数上架的(判题结算和 refreshUnlockedCount 同步改了)
UPDATE "user_stat" AS target
SET "metrics" = jsonb_set(target."metrics", '{achievement_unlocked_count}', to_jsonb(fresh.value))
FROM (
SELECT s."id", coalesce(c.value, 0) AS value
FROM "user_stat" s
LEFT JOIN (
SELECT ua."user_id", count(*)::int AS value
FROM "user_achievement" ua
JOIN "achievement" a ON a."id" = ua."achievement_id"
WHERE a."rarity" <> 'platinum' AND a."visible"
GROUP BY ua."user_id"
) c ON c."user_id" = s."user_id"
) fresh
WHERE target."id" = fresh."id"
AND (target."metrics" -> 'achievement_unlocked_count') IS DISTINCT FROM to_jsonb(fresh.value);
File diff suppressed because it is too large. Load diff
+7
View File
@@ -225,6 +225,13 @@
"when": 1791766110876,
"tag": "0031_tutorial_project_and_problems",
"breakpoints": true
},
{
"idx": 32,
"version": "7",
"when": 1791821976519,
"tag": "0032_achievement_cleanup",
"breakpoints": true
}
]
}