From 940895f643992293dd3d65ea90bda533bab2af98 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 19 Oct 2026 21:09:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AF=BE=E5=A0=82=E7=9C=8B=E6=9D=BF):=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=BB=99=E5=81=9A=E5=AF=B9=E4=BA=86=E7=9A=84?= =?UTF-8?q?=E6=A0=BC=E5=AD=90=E5=B8=A6=E4=B8=8A=E4=BB=8A=E5=A4=A9=E6=AF=8F?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4=E5=92=8C?= =?UTF-8?q?=E7=AC=AC=E5=87=A0=E6=AC=A1=E5=81=9A=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来今天最后一次提交只给没做对的格子;现在同一条查询取今天全部,做对了的格子 多带 trail(从早到晚的 id / 结果 / 时间)和 acceptedTry(今天第几次做对, 以前就做对了的为 null)。 Co-Authored-By: Claude Opus 5.5 --- apps/api/src/routes/classroom.ts | 33 ++++++++++++++++++++---------- packages/contract/src/classroom.ts | 7 +++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/api/src/routes/classroom.ts b/apps/api/src/routes/classroom.ts index 6bd292f..130af28 100644 --- a/apps/api/src/routes/classroom.ts +++ b/apps/api/src/routes/classroom.ts @@ -21,6 +21,7 @@ import { getLoginWindow } from "../auth/session" import { presenceOf } from "../collab/state" import { db, schema } from "../db" import { failure, parseBody, success } from "../http" +import { isAccepted } from "../judge/status" import { activityRows, classMemberIds, @@ -455,7 +456,7 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => { // 「来了几次」代码和流程图合起来数:那天只画了流程图的也算来过,和 recentDays 同一个口径 const recentActivity = activityRows({ since: windowStart, users: userIds }) const localDay = sql`to_char(${localTime(recentActivity.createTime)}, 'YYYY-MM-DD')` - const [cells, lastSubmits, lastDrawn, attendance, drawn, latests, seen] = await Promise.all([ + const [cells, lastSubmits, lastDrawn, attendance, drawn, todays, seen] = await Promise.all([ ids.length && userIds.length ? db .select({ @@ -548,16 +549,17 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => { .groupBy(schema.flowchartSubmission.userId, schema.flowchartSubmission.problemId) : [], /** - * 每人每题今天最后一次代码提交:没做对的格子靠它点开讲错误、按错法归堆。 - * 范围和 cells 一样(非比赛、布置的语言),只多一个「今天」 + * 每人每题今天交的每一次代码,从早到晚。最后一次给没做对的格子点开讲错误、按错法归堆, + * 整串给做对了的格子看轨迹。范围和 cells 一样(非比赛、布置的语言),只多一个「今天」 */ ids.length && userIds.length ? db - .selectDistinctOn([schema.submission.userId, schema.submission.problemId], { + .select({ userId: schema.submission.userId, problemId: schema.submission.problemId, id: schema.submission.id, result: schema.submission.result, + createTime: schema.submission.createTime, runtimeError: sql< StatisticInfo["runtime_error"] | null >`${schema.submission.statisticInfo}->'runtime_error'`, @@ -575,15 +577,11 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => { lessonLanguageFilter(lesson.language), ), ) - .orderBy( - schema.submission.userId, - schema.submission.problemId, - desc(schema.submission.createTime), - ) + .orderBy(schema.submission.createTime) : [], seenToday(userIds), ]) - const latestByKey = new Map(latests.map((row) => [`${row.userId}:${row.problemId}`, row])) + const todayByKey = Map.groupBy(todays, (row) => `${row.userId}:${row.problemId}`) 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])) @@ -623,7 +621,12 @@ 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) + const today = todayByKey.get(key) ?? [] + const latest = acceptedAt ? undefined : today.at(-1) + const firstTry = + acceptedAt && Date.parse(acceptedAt) >= Date.parse(start) + ? today.findIndex((row) => isAccepted(row.result)) + : -1 return { status: !cell && !flow ? "none" : acceptedAt ? "accepted" : "tried", attempts: (cell?.attempts ?? 0) + (flow?.attempts ?? 0), @@ -638,6 +641,14 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => { sampleCheck: latest.sampleCheck ?? undefined, } : null, + trail: acceptedAt + ? today.map((row) => ({ + id: row.id, + result: row.result, + createTime: row.createTime, + })) + : [], + acceptedTry: firstTry >= 0 ? firstTry + 1 : null, } }), lastSubmitAt: lastByUser.get(student.userId) ?? null, diff --git a/packages/contract/src/classroom.ts b/packages/contract/src/classroom.ts index fe49f52..7d7d2fa 100644 --- a/packages/contract/src/classroom.ts +++ b/packages/contract/src/classroom.ts @@ -121,6 +121,13 @@ export const classBoardCellSchema = z.object({ sampleCheck: statisticInfoSchema.shape.sample_check, }) .nullable(), + /** + * 做对了的格子:今天交的每一次代码,从早到晚(范围和 latest 一样)。点格子看他是怎么一次次 + * 做对的,停在哪一次看哪次的代码。没做对的为空 —— 那边点开是讲错误,靠 latest + */ + trail: z.array(z.object({ id: z.string(), result: judgeStatusSchema, createTime: z.string() })), + /** 今天第几次代码提交做对的,1 = 一次就对;今天之前就做对了、今天没靠代码做对的为 null */ + acceptedTry: z.number().int().nullable(), }) export const classBoardStudentSchema = z.object({