diff --git a/docs/superpowers/specs/2026-07-30-teacher-student-demo-mode-design.md b/docs/superpowers/specs/2026-07-30-teacher-student-demo-mode-design.md new file mode 100644 index 0000000..f523b00 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-teacher-student-demo-mode-design.md @@ -0,0 +1,93 @@ +# 教师学生端讲解模式设计 + +## 目标 + +教师可以选择自己创建的教学班,在新窗口以学生端界面讲解任务流程。演示会话读取所选教学班的任务、任务分配和页面配置,但不会创建虚拟学生账号、不会写入教学班成员关系,也不会持久化答案、进度、成绩或其他学生业务数据。 + +## 不采用 `_ys` 账号的原因 + +学生端当前通过 `teaching_class_student` 的有效成员记录确定教学班。为 `张辉_ys` 一类账号提供内容,必须把账号加入教学班;而现有模型只会取该学生最近的一条有效教学班归属。切换讲解班级会导致成员关系迁移,并污染学生名单、学习记录、成绩和账号管理。 + +因此,演示模式使用受控的虚拟会话,不创建 `userinfo` 学生记录,也不新增 `teaching_class_student` 记录。 + +## 会话与单点进入 + +### 教师端 + +教师端新增“学生端讲解”入口。点击后展示本人创建的教学班列表;未选择教学班不能进入。 + +教师选择班级后,前端调用教师专用接口创建一次性演示票据。后端必须验证: + +1. 当前用户为教师; +2. 教学班类型为 `TEACHING`; +3. `school_class.created_by` 等于当前教师用户 ID。 + +前端打开独立学生端窗口,并将一次性票据通过同源 `postMessage` 发送给新窗口,不把 JWT 放入 URL、浏览器历史或 Referer。 + +### 后端 + +新增演示会话接口: + +| 接口 | 用途 | +| --- | --- | +| `POST /api/teacher/student-demo-sessions` | 教师为本人教学班创建短时、单次使用的演示票据。 | +| `POST /api/student-demo-sessions/exchange` | 新窗口使用票据换取演示学生 token。 | + +票据存储字段包括票据值哈希、来源教师 ID、教学班 ID、学校 ID、过期时间、已使用时间和创建时间。票据有效期为 5 分钟,成功兑换后立即失效。演示 token 有效期为 30 分钟。 + +兑换后的 JWT 维持学生端 `roleId=4`,并增加如下声明: + +- `demoMode=true` +- `demoTeachingClassId` +- `sourceTeacherId` + +`JwtUser` 与 `TokenProvider` 解析这些声明,供后续服务统一判断演示上下文。 + +## 教学班解析规则 + +新增统一的学生上下文解析器: + +1. 普通学生:保留现有 `teaching_class_student` 有效成员关系查询。 +2. 演示学生:直接使用 JWT 的 `demoTeachingClassId`;不查询、不创建成员关系。 + +任务详情、任务列表、任务分配、成绩参考、进度展示等学生端读取服务均使用该解析器,确保教师看到的是所选教学班的真实教学内容和配置。 + +演示 token 不允许通过传入其他班级 ID 改变上下文;任何请求中的教学班 ID 必须与 `demoTeachingClassId` 一致,否则拒绝。 + +## 演示数据隔离 + +演示会话不能持久化任何学生业务数据。 + +| 类型 | 演示模式行为 | +| --- | --- | +| 读取任务、任务分配、班级配置 | 使用 `demoTeachingClassId` 正常读取。 | +| 查询答案与进度 | 返回当前窗口的临时数据;首次进入为未开始、空答案。 | +| 保存、提交、重置或删除答案 | 前端写入 `sessionStorage` 并展示成功,不调用持久化接口。 | +| 成绩、排名、进度写入 | 禁止调用或返回演示提示,不写数据库。 | +| AI 助学、AI 评测及外部资源调用 | 在演示模式禁用,避免产生外部调用成本或写入结果。 | + +关闭演示窗口或演示 token 过期后,临时数据自动丢弃。教师端、学生端和后端均不保留演示答案内容。 + +## 前端交互 + +1. 教师端按钮只对教师可见,班级选择框只展示当前教师创建的教学班。 +2. 新窗口进入专用学生端演示路由,等待父窗口发送票据并换取 token;若票据缺失、过期或已使用,展示“演示链接已失效,请返回教师端重新打开”。 +3. 演示学生端顶部展示醒目条:`演示模式:{教学班名称}`,并提供“退出演示”按钮。 +4. 学生端菜单与普通学生一致;被禁用的 AI、成绩提交等操作提供明确的“演示模式不保存数据”提示。 +5. 演示窗口使用独立的 session storage 键保存 token 和临时答案,不覆盖教师窗口或普通学生窗口的登录状态。 + +## 安全与边界 + +- 创建票据和兑换票据均校验有效期、单次使用状态和来源学校。 +- 教师只能创建本人教学班的演示会话,不能进入其他教师的班级。 +- 演示 token 不拥有教师管理权限;仅拥有学生端读取权限和演示上下文。 +- 所有持久化写接口在后端额外识别 `demoMode` 并拒绝写入,不能仅依赖前端禁用。 +- 不创建 `_ys` 用户、密码、学生档案、班级成员或成绩记录。 + +## 验收标准 + +1. 教师可在新窗口进入自己创建的教学班对应的学生端,并看到该班任务内容。 +2. 教师无法为非本人创建的教学班创建演示会话。 +3. 普通学生的登录、任务读取、答案保存和班级归属逻辑保持不变。 +4. 演示过程中填写答案、切换步骤、提交或退出后,数据库的答案、进度、成绩、排名和成员数据均无新增或变更。 +5. 票据不能重复兑换,过期票据无法兑换;演示 token 过期后必须重新从教师端进入。