chore: 删掉两个跑完的一次性订正脚本,recount 留着
Deploy / deploy (push) Has been cancelled

fix-achievement-hours 订正的是时区丢失那两周误发的「夜猫子」「早起的鸟儿」,
2026-09-14 已跑完(修正 148 行 · 撤回 60 条),根因修在代码里之后不会再产生;
backfill-problemsets 补的三笔题单历史欠账也已结清,进度记账现在在判题这一路。
两个子命令、两条 npm 脚本一并去掉,services/problemset.ts 的 badgeHolderDiff
只服务于补发脚本,删完 0 处引用,一起清掉。

recount 留着 —— 反范式计数列会被重判、删提交带偏,是会复发的漂移,
补一条 npm 脚本(原来只有子命令)。

docs/timezone.md 里那节改成「账已平、脚本已删」,但保住那条教训:
unlockAchievements 是纯阈值比较,只删 user_achievement 不修 metrics 的话,
学生下次提交就把同一个成就原样再发一次。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5 committed 2026-09-16 08:03:57 -06:00
1 parent 6fdc3c588e
commit 5eee13fe81
6 files changed
+6 -654

No files matched your search

-20
View File
@@ -193,24 +193,6 @@ export async function resyncProgress(problemsetId: number) {
for (const badge of badges) await recalculateBadge(badge, updated)
}
/** 按奖章算出「现在应该有谁」,只读,供补发脚本先看后写 */
export async function badgeHolderDiff(badge: BadgeRow, known?: (BadgeCheck & { userId: number })[]) {
const [progresses, holders] = await Promise.all([
known ?? db.select().from(schema.problemsetProgress)
.where(eq(schema.problemsetProgress.problemsetId, badge.problemsetId)),
db.select({ userId: schema.userBadge.userId }).from(schema.userBadge)
.where(eq(schema.userBadge.badgeId, badge.id)),
])
const eligible = new Set(progresses.filter((item) => eligibleForBadge(badge, item)).map((item) => item.userId))
const have = new Set(holders.map((item) => item.userId))
return {
missing: [...eligible].filter((id) => !have.has(id)),
extra: [...have].filter((id) => !eligible.has(id)),
eligible: eligible.size,
held: have.size,
}
}
/**
* 判题通过后,把这道题记进该用户所有「已加入且包含这道题」的题单。
*
@@ -222,8 +204,6 @@ export async function badgeHolderDiff(badge: BadgeRow, known?: (BadgeCheck & { u
* 挪到判题这一路之后,记账和判题在同一个事务链里,前端只管显示。
*
* 不按 visible / status 过滤:进度是学生自己的记录,老师把题单藏起来不该让它停止累积。
* 更要紧的是这条规则必须和补账那条(scripts/backfill-problemsets.ts)一致 ——
* 两边口径不一样的话,补账工具会永远「发现」差异。
*/
export async function recordSolvedProblem(
userId: number,