feat(课堂): 老师的课堂看板——布置这节课的题、看全班谁还没开始谁卡住了,布置的题直接出现在学生首页
2025 秋平均每节课有 14 个平时在用的学生一道都没交(三分之一),交了 3 次以上还没过的 平均不到 1 个;而老师原来只能一道题一道题打开统计弹窗看。 - 新表 class_lesson(迁移 0023,仅建表):一个班一天一行,存老师布置的 problem.id 与顺序; day 存东八区日历日文本,created_by CASCADE - GET /classroom/board:这个班今天的题 × 全班学生(通过与否不限今天、今天交了几次、 最后提交时间);不给班级时猜最近两小时提交人数最多的班,不记老师上次选的 - PUT /classroom/lesson:按展示题号布置,对不上的题号点名拒绝,空数组 = 清掉 - /me/class-activity 优先返回老师布置的题(source = teacher),否则照旧推断 - 前端 /classroom:没交过的名单置顶,按 没交过 → 卡住 → 在做 → 做完 排序,15 秒刷新; 投影模式只显示每题完成人数、不出名字;用户菜单给老师加入口 - 学生首页:老师布置的标题写「今天老师布置的题」,没人交时不显示 0/0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e964fdbdd
commit
80106eaa04
12 files changed
+4992
-69
No files matched your search
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE "class_lesson" (
|
||||
"id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "class_lesson_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
|
||||
"class_name" text NOT NULL,
|
||||
"day" text NOT NULL,
|
||||
"problem_ids" jsonb NOT NULL,
|
||||
"created_by" integer NOT NULL,
|
||||
"updated_at" timestamp with time zone NOT NULL,
|
||||
CONSTRAINT "class_lesson_class_day_unique" UNIQUE("class_name","day")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "class_lesson" ADD CONSTRAINT "class_lesson_created_by_fk_user_id" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "class_lesson_created_by_idx" ON "class_lesson" USING btree ("created_by");
|
||||
File diff suppressed because it is too large.
Load diff
@@ -162,6 +162,13 @@
|
||||
"when": 1790512513639,
|
||||
"tag": "0022_add_tag_category",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"version": "7",
|
||||
"when": 1790519287984,
|
||||
"tag": "0023_add_class_lesson",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1449,3 +1449,47 @@ export const exerciseAttempt = pgTable(
|
||||
}).onDelete("cascade"),
|
||||
],
|
||||
)
|
||||
|
||||
/**
|
||||
* 老师给某个班「这节课」布置的题(课堂看板里输入的题号),一个班一天一行。
|
||||
*
|
||||
* 学生首页的「班里在做」优先读它,没有才退回从提交记录推断(routes/classroom.ts)。
|
||||
* 推断在上课头几分钟是空的 —— 还没人交 —— 而这正是没听清题号的学生最需要它的时候。
|
||||
*
|
||||
* - `day` 是**东八区日历日**文本(`calendarDay()` 写的 `YYYY-MM-DD`),不用 date 列:
|
||||
* 「哪一天」的口径只在 time.ts 一处定,不交给驱动和会话时区去解释。
|
||||
* - `problem_ids` 按老师输入的顺序存 problem.id。**不挂外键**(jsonb 挂不了),
|
||||
* 读的时候按 visible 过滤,题目被删、被藏的自然不出现;写的时候校验过都存在。
|
||||
* - `created_by` CASCADE:备课记录不是「学生做过什么」的证据,老师账号没了它也没有意义
|
||||
* (外键分档见 CLAUDE.md「数据库」一节)。
|
||||
*/
|
||||
export const classLesson = pgTable(
|
||||
"class_lesson",
|
||||
{
|
||||
id: integer().primaryKey().generatedByDefaultAsIdentity({
|
||||
name: "class_lesson_id_seq",
|
||||
startWith: 1,
|
||||
increment: 1,
|
||||
minValue: 1,
|
||||
maxValue: 2147483647,
|
||||
cache: 1,
|
||||
}),
|
||||
className: text("class_name").notNull(),
|
||||
day: text().notNull(),
|
||||
problemIds: jsonb("problem_ids").notNull().$type<number[]>(),
|
||||
createdBy: integer("created_by").notNull(),
|
||||
updatedAt: timestamp("updated_at", {
|
||||
withTimezone: true,
|
||||
mode: "string",
|
||||
}).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
unique("class_lesson_class_day_unique").on(table.className, table.day),
|
||||
index("class_lesson_created_by_idx").on(table.createdBy),
|
||||
foreignKey({
|
||||
columns: [table.createdBy],
|
||||
foreignColumns: [user.id],
|
||||
name: "class_lesson_created_by_fk_user_id",
|
||||
}).onDelete("cascade"),
|
||||
],
|
||||
)
|
||||
Reference in new issue
Block a user