feat: 改版阶段 3:老师按班级 × 任务批改,学生看本班同学作品
老师 - 新的“批改”页(教学默认页):左边班级 + 任务列表和完成进度,中间作品墙 (每人最新一版、未交 / 我未评 / 分数),右侧详情面板:各版本、提示词链、打分、 加入课上展示、加入作品展;J/K 切换学生,1–5 打分,P 课上展示 - “开始讲评”全屏依次播放课上展示的作品;成绩册点分数打开同一个详情面板 - 四色标记和“一键清除全站标记”去掉,flag 只用 present(课上展示);全部提交只给超管 学生 - “同学作品”:只看本班、自己交过这道题才开放,打分前匿名(服务端不返回作者) - 学生的提交列表只有自己的;别人的提交和提示词链只有本班同学交过同一题、或进了作品展才能看 - 随机互评不再返回作者,打完分由 PUT /:id/score 返回 服务端新增 routes/review.ts(/submission/review:options、progress、class、peers), 查询走 slim 覆盖索引 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c6c01eba1
commit
df3f842dac
18 files changed
+1616
-275
No files matched your search
@@ -493,3 +493,74 @@ describe("任务素材", () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("同学作品与批改", () => {
|
||||
test("学生只能看自己的、作品展里的、本班同学在自己也交过的题上的提交", async () => {
|
||||
const me = makeUser("me", "normal", "c1")
|
||||
const mate = makeUser("mate", "normal", "c1")
|
||||
const other = makeUser("other", "normal", "c2")
|
||||
const task = makeTask("T")
|
||||
const mateSub = makeSubmission(mate, task)
|
||||
const otherSub = makeSubmission(other, task)
|
||||
const stu = await client("me")
|
||||
|
||||
expect((await stu("GET", `/api/submission/${uuid(mateSub)}`)).status).toBe(403)
|
||||
expect((await stu("GET", `/api/submission/${uuid(mateSub)}/prompt-chain`)).status).toBe(403)
|
||||
const locked = await stu("GET", `/api/submission/review/peers/${task}`)
|
||||
expect(locked.json).toEqual({ locked: true, items: [] })
|
||||
|
||||
makeSubmission(me, task)
|
||||
expect((await stu("GET", `/api/submission/${uuid(mateSub)}`)).status).toBe(200)
|
||||
// 外班的仍然看不到
|
||||
expect((await stu("GET", `/api/submission/${uuid(otherSub)}`)).status).toBe(403)
|
||||
// 列表里只有自己的
|
||||
const list = await stu("GET", "/api/submission/")
|
||||
expect(list.json.items.map((s: any) => s.username)).toEqual(["me"])
|
||||
expect((await stu("GET", `/api/submission/by-user-task?user_id=${mate}&task_id=${task}`)).status).toBe(403)
|
||||
})
|
||||
|
||||
test("同学作品打分前匿名,打完分才显示作者", async () => {
|
||||
const me = makeUser("me", "normal", "c1")
|
||||
const mate = makeUser("mate", "normal", "c1")
|
||||
makeUser("other", "normal", "c2")
|
||||
const task = makeTask("T")
|
||||
makeSubmission(me, task)
|
||||
const mateSub = makeSubmission(mate, task)
|
||||
const stu = await client("me")
|
||||
|
||||
let peers = await stu("GET", `/api/submission/review/peers/${task}`)
|
||||
expect(peers.json.items).toHaveLength(1)
|
||||
expect(peers.json.items[0]).toMatchObject({ id: uuid(mateSub), username: null, score: null, my_score: 0 })
|
||||
|
||||
const rated = await stu("PUT", `/api/submission/${uuid(mateSub)}/score`, { score: 4 })
|
||||
expect(rated.json.username).toBe("mate")
|
||||
peers = await stu("GET", `/api/submission/review/peers/${task}`)
|
||||
expect(peers.json.items[0]).toMatchObject({ username: "mate", my_score: 4 })
|
||||
|
||||
const random = await (await client("other"))("GET", "/api/submission/random-for-rating")
|
||||
expect(random.json.username).toBeUndefined()
|
||||
})
|
||||
|
||||
test("老师按班级 × 任务看作品墙;课上展示用 flag", async () => {
|
||||
makeUser("t", "admin")
|
||||
const a = makeUser("a", "normal", "c1")
|
||||
makeUser("b", "normal", "c1")
|
||||
const task = makeTask("T")
|
||||
makeSubmission(a, task, 0, "2026-01-01 00:00:00.000000")
|
||||
const latest = makeSubmission(a, task, 0, "2026-01-02 00:00:00.000000")
|
||||
const teacher = await client("t")
|
||||
|
||||
expect((await (await client("a"))("GET", `/api/submission/review/class?classname=c1&task_id=${task}`)).status).toBe(403)
|
||||
const wall = await teacher("GET", `/api/submission/review/class?classname=c1&task_id=${task}`)
|
||||
expect(wall.json.map((s: any) => [s.username, s.submission?.id ?? null, s.submission?.submit_count])).toEqual([
|
||||
["a", uuid(latest), 2],
|
||||
["b", null, undefined],
|
||||
])
|
||||
const progress = await teacher("GET", "/api/submission/review/progress?classname=c1")
|
||||
expect(progress.json).toEqual({ student_count: 2, submitted: { [task]: 1 } })
|
||||
const options = await teacher("GET", "/api/submission/review/options")
|
||||
expect(options.json.classes).toEqual(["c1"])
|
||||
|
||||
expect((await teacher("PUT", `/api/submission/${uuid(latest)}/flag`, { flag: "present" })).json.flag).toBe("present")
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user