feat(课堂看板): 看板接口给没做对的格子带上今天最后一次提交,按错法归堆、点开讲错误要用

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-13 10:02:50 +08:00
1 parent 754a9d7caf
commit 0a4f528c44
3 files changed
+62 -1

No files matched your search

+46 -1
View File
@@ -9,6 +9,7 @@ import {
type ClassActivityProblem,
type LastVisit,
type LessonLanguage,
type StatisticInfo,
FLOWCHART_PASS_GRADES,
} from "@oj2/contract"
import { and, asc, desc, eq, gte, inArray, isNotNull, isNull, lt, sql } from "drizzle-orm"
@@ -558,7 +559,7 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
// 「来了几次」代码和流程图合起来数:那天只画了流程图的也算来过,和 recentDays 同一个口径
const recentActivity = activityRows({ since: windowStart, users: userIds })
const localDay = sql<string>`to_char(${localTime(recentActivity.createTime)}, 'YYYY-MM-DD')`
const [cells, lastSubmits, lastDrawn, attendance, drawn] = await Promise.all([
const [cells, lastSubmits, lastDrawn, attendance, drawn, latests] = await Promise.all([
ids.length && userIds.length
? db
.select({
@@ -650,7 +651,42 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
)
.groupBy(schema.flowchartSubmission.userId, schema.flowchartSubmission.problemId)
: [],
/**
* 每人每题今天最后一次代码提交:没做对的格子靠它点开讲错误、按错法归堆。
* 范围和 cells 一样(非比赛、布置的语言),只多一个「今天」
*/
ids.length && userIds.length
? db
.selectDistinctOn([schema.submission.userId, schema.submission.problemId], {
userId: schema.submission.userId,
problemId: schema.submission.problemId,
id: schema.submission.id,
result: schema.submission.result,
runtimeError: sql<
StatisticInfo["runtime_error"] | null
>`${schema.submission.statisticInfo}->'runtime_error'`,
sampleCheck: sql<
StatisticInfo["sample_check"] | null
>`${schema.submission.statisticInfo}->'sample_check'`,
})
.from(schema.submission)
.where(
and(
isNull(schema.submission.contestId),
inArray(schema.submission.userId, userIds),
inArray(schema.submission.problemId, ids),
gte(schema.submission.createTime, start),
lessonLanguageFilter(lesson.language),
),
)
.orderBy(
schema.submission.userId,
schema.submission.problemId,
desc(schema.submission.createTime),
)
: [],
])
const latestByKey = new Map(latests.map((row) => [`${row.userId}:${row.problemId}`, row]))
const cellByKey = new Map(cells.map((row) => [`${row.userId}:${row.problemId}`, row]))
const drawnByKey = new Map(drawn.map((row) => [`${row.userId}:${row.problemId}`, row]))
const lastByUser = new Map(lastSubmits.map((row) => [row.userId, row.lastAt]))
@@ -689,12 +725,21 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
const cell = cellByKey.get(key)
const flow = drawnByKey.get(key)
const acceptedAt = earliest(cell?.firstAcceptedAt, flow?.firstPassedAt)
const latest = acceptedAt ? undefined : latestByKey.get(key)
return {
status: !cell && !flow ? "none" : acceptedAt ? "accepted" : "tried",
attempts: (cell?.attempts ?? 0) + (flow?.attempts ?? 0),
codeAttempts: cell?.attempts ?? 0,
flowchartAttempts: flow?.attempts ?? 0,
acceptedAt,
latest: latest
? {
submissionId: latest.id,
result: latest.result,
runtimeError: latest.runtimeError ?? undefined,
sampleCheck: latest.sampleCheck ?? undefined,
}
: null,
}
}),
lastSubmitAt: lastByUser.get(student.userId) ?? null,
+16
View File
@@ -2,6 +2,7 @@ import { z } from "zod"
import { judgeStatusSchema } from "./judge-status"
import { lessonLanguageSchema as lessonLanguageValueSchema } from "./language"
import { statisticInfoSchema } from "./submission"
/**
* 「班里在做」:同班同一天有好几个人做的题,基本就是老师在课上点名的那几道。
@@ -105,6 +106,21 @@ export const classBoardCellSchema = z.object({
flowchartAttempts: z.number().int(),
/** 第一次通过的时刻;今天之前通过的,前端显示成「之前」 */
acceptedAt: z.string().nullable(),
/**
* 还没做对时,今天最后一次**代码**提交:点格子打开它讲错误,题卡片下面按错法归堆。
* 做对了、今天没交代码(只画了流程图)的为 null。
*
* 只带归类要用的那几样(runtime_error / sample_check 和提交里存的同一份),
* 不带代码和原始报错 —— 一班四五十人,真要看代码的时候按 submissionId 再拉
*/
latest: z
.object({
submissionId: z.string(),
result: judgeStatusSchema,
runtimeError: statisticInfoSchema.shape.runtime_error,
sampleCheck: statisticInfoSchema.shape.sample_check,
})
.nullable(),
})
export const classBoardStudentSchema = z.object({