feat(统计): 没填班级的学生也能和班级一样看——班级下拉多一项「没填班级」,一起上的课能切出来、点进去看小方块;零散提交也能点了

- 班级筛选多一个取值 NO_CLASS("none"),统计、方块串、流程图、提交列表都认;只圈普通学生,
  老师管理员的号本来就不填班级,不挡的话试题的提交会算进去
- 切课时没填班级的号合在一起当一个班切(原来 class_name is null 直接排除,7 月 4 日那两节课看不到)
- 没填班级不是真班:不列「还没交」,课上只写几人不写几人之几
- 「另有零散提交」每一项可点,进那个班那一整天

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-30 12:18:06 +08:00
1 parent c2ef55b10b
commit 923f3efeca
10 files changed
+153 -66

No files matched your search

+10 -5
View File
@@ -10,6 +10,7 @@ import {
type FlowchartListItem,
type FlowchartStatistics,
type FlowchartSubmission,
NO_CLASS,
} from "@oj2/contract"
import { and, asc, count, desc, eq, inArray, isNull, sql, type SQL } from "drizzle-orm"
import { Hono } from "hono"
@@ -27,6 +28,7 @@ import { buildWordFrequencies } from "../services/word-frequency"
import { dayStart } from "../time"
import { unknownDisplayIds } from "./submission-statistics"
import {
classCondition,
isAdminRole,
matchedUsers,
asRecord,
@@ -208,7 +210,7 @@ async function flowchartClassFilter(className: string) {
const users = await db
.select({ id: schema.user.id })
.from(schema.user)
.where(eq(schema.user.className, className))
.where(classCondition(className))
return users.length
? inArray(
schema.flowchartSubmission.userId,
@@ -422,10 +424,13 @@ flowchartRoutes.get("/flowcharts/statistics", requireTeacher, async (c) => {
}
const where = and(...filters)
// 花名册:只有指定了用户名才谈得上「班级人数」,不指定时分母无意义。
// 未禁用的普通用户才进分母,教师和管理员不算
const roster = scoped
? matched.filter((row) => !row.isDisabled && row.adminType === "Regular User")
: []
// 未禁用的普通用户才进分母,教师和管理员不算。
// 「没填班级」不是一个真的班(两百来个夏令营、自己注册的号混在一起),「谁没做」列出来全是
// 不相干的人,当没有花名册
const roster =
scoped && c.req.query("className")?.trim() !== NO_CLASS
? matched.filter((row) => !row.isDisabled && row.adminType === "Regular User")
: []
/**
* 五条查询,每条的代价都和窗口里的行数脱钩(词云那条卡了 limit)。