feat(课堂看板): 流程图画到 A / S 在老师的看板上也算做完,只画了图的学生不再显示「没开始」

和学生首页的课堂条同一个口径:格子的状态、做对的时间、尝试次数都把流程图提交算进去;
「最后一次提交」也算上今天画的图。有的题这节课整个班都在画流程图,原来看板上是满屏「没开始」

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-28 18:42:53 +08:00
1 parent 972dac3776
commit b5d2f1589e
1 file changed
+43 -4
+43 -4
View File
@@ -516,8 +516,44 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
.groupBy(schema.submission.userId)
: [],
])
/**
* 流程图作业:画到 A / S 也算这道题做完,和学生首页的课堂条同一个口径(/me/class-activity)。
* 有的题这节课整个班都在画流程图,只数代码提交的话,看板上满屏「没开始」
*/
const drawn =
ids.length && userIds.length
? await db
.select({
userId: schema.flowchartSubmission.userId,
problemId: schema.flowchartSubmission.problemId,
attempts: sql<number>`count(*) filter (where ${gte(schema.flowchartSubmission.createTime, start)})::int`,
firstPassedAt: sql<
string | null
>`min(${schema.flowchartSubmission.createTime}) filter (where ${and(eq(schema.flowchartSubmission.status, 2), inArray(schema.flowchartSubmission.aiGrade, FLOWCHART_PASS_GRADES))})`,
lastAt: sql<
string | null
>`max(${schema.flowchartSubmission.createTime}) filter (where ${gte(schema.flowchartSubmission.createTime, start)})`,
})
.from(schema.flowchartSubmission)
.where(
and(
inArray(schema.flowchartSubmission.userId, userIds),
inArray(schema.flowchartSubmission.problemId, ids),
),
)
.groupBy(schema.flowchartSubmission.userId, schema.flowchartSubmission.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]))
// 今天只画了流程图的学生,「最后一次提交」也得算上画图,不然他在看板上是一直没动手
for (const row of drawn) {
if (!row.lastAt) continue
const known = lastByUser.get(row.userId)
if (!known || Date.parse(row.lastAt) > Date.parse(known)) lastByUser.set(row.userId, row.lastAt)
}
const earliest = (a: string | null | undefined, b: string | null | undefined) =>
!a ? (b ?? null) : !b ? a : Date.parse(a) <= Date.parse(b) ? a : b
const attendedByUser = new Map(attendance.map((row) => [row.userId, row.days]))
return success(c, {
@@ -536,11 +572,14 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
username: student.username,
realName: student.realName ?? null,
cells: ids.map((id) => {
const cell = cellByKey.get(`${student.userId}:${id}`)
const key = `${student.userId}:${id}`
const cell = cellByKey.get(key)
const flow = drawnByKey.get(key)
const acceptedAt = earliest(cell?.firstAcceptedAt, flow?.firstPassedAt)
return {
status: !cell ? "none" : cell.firstAcceptedAt ? "accepted" : "tried",
attempts: cell?.attempts ?? 0,
acceptedAt: cell?.firstAcceptedAt ?? null,
status: !cell && !flow ? "none" : acceptedAt ? "accepted" : "tried",
attempts: (cell?.attempts ?? 0) + (flow?.attempts ?? 0),
acceptedAt,
}
}),
lastSubmitAt: lastByUser.get(student.userId) ?? null,