feat(挑战): 做对之后可以加一条写法上的限制再做一遍,判题时记下谁做到了

借小丑牌的赌注档:同一道题加一条限制(「整首歌只用 1 次 print」)再做一遍,
给课上做得快的学生找事做(做完一节课的题,中位要空等 13 分钟)。
判定沿用 AST 规则,挑战配在 problem.challenges,做到的记在 challenge_success;
做对的提交顺手判一遍,挑战没做到也照样算做对。没过的规则带上是第几行。
0039 灌进第一批 56 道常用题的 73 条挑战,api 起来后拿历史上做对的代码补一遍谁已经做到了。
出题页能配挑战、从做对的代码里找候选;课堂看板的格子带上挑战状态。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-26 08:52:50 +08:00
1 parent cc1361e596
commit 0faf78ad8f
17 files changed
+9057 -37

No files matched your search

+35
View File
@@ -7,6 +7,12 @@ import { config } from "../config"
import { db, schema } from "../db"
import { publishAchievementNotification } from "../events"
import { updateAchievementsForSubmission } from "../services/achievements"
import {
challengesFor,
judgeChallenges,
recordChallengeSuccesses,
type ChallengeVerdict,
} from "../services/challenge"
import { recordSolvedAndNotify } from "../services/problemset"
import { checkAst, type AstRule } from "./ast"
import { publishSubmissionUpdate } from "./events"
@@ -696,6 +702,19 @@ export async function judgeSubmission(job: JudgeJobData) {
}
}
}
// 挑战:做对了就拿这道题的每条挑战各判一遍(比赛不判,同上)。结果全存下来,
// 前端在挑战中时按 id 取它正在挑战的那条;做到的下面落库
if (result === JudgeStatus.ACCEPTED && row.submission.contestId === null) {
const challenges = challengesFor(row.problem.challenges, row.submission.language)
if (challenges.length > 0) {
statisticInfo.challenges = await judgeChallenges(
row.submission.code,
row.submission.language,
challenges,
)
}
}
}
const counted = countedBefore(job.rejudgedFrom)
@@ -722,6 +741,22 @@ export async function judgeSubmission(job: JudgeJobData) {
await recordSolvedAndNotify(row.submission.userId, row.problem.id, row.submission.createTime)
}
if (Array.isArray(statisticInfo.challenges)) {
await recordChallengeSuccesses(
(statisticInfo.challenges as ChallengeVerdict[])
.filter((verdict) => verdict.passed)
.map((verdict) => ({
userId: row.submission.userId,
problemId: row.problem.id,
challengeId: verdict.id,
submissionId: row.submission.id,
createdAt: row.submission.createTime,
})),
).catch((error: unknown) =>
console.error(`[judge] 挑战记录失败 ${row.submission.id}:`, error),
)
}
try {
const unlocked = await updateAchievementsForSubmission(row.submission.id, counted)
await publishAchievementNotification(