实时课堂模块安全加固与 AI 教学材料生成——EduMind 暑期冲刺记录
一次针对 EduMind 实时课堂模块的集中攻关,横跨安全、性能、可靠性和 AI 生成能力四个维度,一天内完成 16 项修复与功能新增。
一、安全纵向加固:学生身份防冒用
问题
/api/live/join 是公开接口,学生自报学号就能拿到合法 JWT。WebSocket Handler 又直接信任消息体中的 studentId,导致学生 A 可以任意冒用学生 B 的身份答题、举手、提问。
方案:四层纵深防御
- Join 入口验证 — 在
LiveSessionService.joinSession中增加班级花名册校验:如果教师已导入学生名单,则不在名单内的学号拒绝加入 - WebSocket Handler 覆盖身份 —
InteractionHandler、ReactionHandler、QASessionHandler全部改为从 STOMP Session Attributes(CONNECT 阶段 JWT 验证后存储)读取学生身份,忽略 payload 中的studentId/studentName - REST 端点修复 —
markConfusion从 JWT 提取身份而非请求体 - WebSocket CORS 收紧 —
setAllowedOriginPatterns("*")改为读取app.cors.allowed-origins配置
二、可靠性与性能
scheduleAutoClose 线程池化
InteractionService 中每次限时互动都 new Thread() sleep,改为共享 ScheduledExecutorService,支持手动关闭时取消定时任务,@PreDestroy 优雅退出。
倒计时同步
学生端倒计时从 setInterval(remaining--) 纯前端自减改为 deadlineEpochMs - Date.now() 实时计算。InteractionPushDTO 新增绝对截止时间戳字段,消除网络延迟和页面刷新带来的偏移。
教师断线检测
WebSocketAuthInterceptor 新增教师连接追踪,断开时通过 /topic/session/{sid}/teacher-status 广播 {online: false}。学生端收到后显示 “⚠️ 老师暂时离开” 红色提示。修复了 streamChat 不传 sessionId 的已有 Bug。
学生断线 30s 宽限期
StudentPresenceService 新增宽限期机制:学生 WebSocket 断开后启动 30s 定时器,超时才标记离场。宽限期内重连自动取消定时器,消除切 Wi-Fi 时的在线列表闪烁。
N+1 查询 → 批量查询
getInteractionHistory 和 getStudentProfile 中每次循环调 responseMapper.findByInteractionId() → 改为 findBySessionId 一次批量拉取 + 内存分组。20 个互动的课堂从 21 次查询降到 2 次。同步修复了 LiveNotificationService.sendPersonalizedReview 中的同类问题。
Presence 内存泄漏
LiveSessionService.endSession 新增调用 presenceService.clearSession(sessionId),结课时清理 onlineStudents map + 取消待执行的宽限期任务。
出勤持久化
新增 live_attendance_log 表(V8 迁移),studentJoined 写 JOIN 事件,studentLeft 写 LEAVE 事件。getClassId 使用 ConcurrentHashMap 缓存避免重复查 DB。
三、举手队列
新增 HandRaiseService + HandRaiseHandler,实现 FIFO 举手队列:
- 学生举手入队,再点取消
- 教师按序点名(队首出队)或指定点名
- 教师可移除任意排队学生
- 结课时自动清空队列
- 教师端左侧栏新增队列面板,学生端 ✋ 改为 toggle 蓝色按钮
四、PPT → AI 生成教学材料
核心实现
新增 TeachingMaterialGenerator 服务,调用 OpenClaw → LLM 分析 PPT 内容,同时生成预习作业和课堂试题。
- 预习作业 — prompt 模板
ppt-preview-generation.txt,输出 JSON +===GUIDE===分隔符 + Markdown 导读 - 课堂试题 — prompt 模板
ppt-quiz-generation.txt,输出混合题型 JSON 数组(CHOICE/OPEN/EXERCISE)
关键踩坑
- DeepSeek 超时 — 不是超时,是梯子 DNS 劫持导致
api.deepseek.com解析出内网 IP,OpenClaw 安全层直接拦截Blocked: resolves to private/internal/special-use IP address - 流式返回空 —
streamChat(message, sessionId, status)的请求体 bug:buildRequestBody(message, null, agent, true)—sessionId参数根本没传进去,导致 Agent 行为异常 - JSON 解析失败 — 预习作业的
guideText是长段 Markdown,换行引号导致 JSON 断裂。改为===GUIDE===分隔符方案,JSON 和 Markdown 分家 session_idNOT NULL — 草稿题没有课堂 session,V9 迁移删除外键 + NOT NULL 约束;V11 迁移补充class_id和source_doc_id- INSERT 缺列 —
source_doc_id加到了列定义但漏了 VALUES 子句 correct_key VARCHAR(10)太短 — 简答题答案超长,V10 迁移扩为 TEXT
前端
知识库页面选中 PPT 文件后:
- 文件详情面板显示”🤖 AI 生成教学材料”按钮
- 弹窗内选班级 → 生成 → 审核编辑 → 逐条发布/保存
- 下方展示该文档历史上生成的草稿列表,可点击查看详情或删除
- Axios 请求超时从默认 10s 扩展到 120s
数据库迁移汇总
V8 出勤日志 → V9 草稿允许空 session_id → V10 扩 correct_key 字段 → V11 加 class_id → V12 加 source_doc_id
五、修改文件清单(共计 28 个文件)
| 类别 | 文件 | 改动 |
|---|---|---|
| 安全 | LiveSessionService.java |
join 花名册验证 |
| 安全 | InteractionHandler.java |
从 Session 读身份 |
| 安全 | ReactionHandler.java |
同上 |
| 安全 | QASessionHandler.java |
同上,修复匿名 |
| 安全 | LiveSessionController.java |
confusion 从 JWT 读身份 |
| 安全 | WebSocketConfig.java |
CORS 收紧 |
| 安全 | WebSocketAuthInterceptor.java |
权限 + 教师追踪 + @Lazy |
| 安全 | JwtUtil.java |
新增 getSubjectFromToken |
| 安全 | ClassStudentMapper.java |
新增 existsByClassIdAndStudentId |
| 可靠性 | InteractionService.java |
scheduleAutoClose 线程池 + 倒计时 deadlineEpochMs |
| 可靠性 | StudentPresenceService.java |
30s 宽限期 + clearSession + 出勤日志 |
| 可靠性 | LiveSessionService.java |
endSession 清理 presence + 举手队列 |
| 可靠性 | InteractionResponseMapper.java |
新增 findBySessionId/sessionIds |
| 可靠性 | InteractionMapper.java |
新增 findBySessionIds + 草稿查询 + 插入补列 |
| 可靠性 | LiveNotificationService.java |
删除 N+1 查询 |
| 可靠性 | OpenClawServiceImpl.java |
修复 streamChat sessionId bug |
| 可靠性 | PreviewTaskMapper.java |
插入加 sourceDocId + findBySourceDocId |
| 可靠性 | DTO/InteractionPushDTO.java |
新增 deadlineEpochMs |
| 功能 | HandRaiseService.java |
新增 举手队列 |
| 功能 | HandRaiseHandler.java |
新增 举手 WS 端点 |
| 功能 | TeachingMaterialGenerator.java |
新增 AI 生成教学材料 |
| 功能 | GenerateMaterialsRequest.java |
新增 请求 DTO |
| 功能 | GenerateMaterialsResponse.java |
新增 响应 DTO + 嵌套类型 |
| 功能 | prompts/ppt-preview-generation.txt |
新增 预习生成模板 |
| 功能 | prompts/ppt-quiz-generation.txt |
新增 试题生成模板 |
| 功能 | DocumentController.java |
新增 generate/save/drafts API |
| 前端 | live-session.ts |
状态同步:举手队列 + 教师在线 |
| 前端 | StudentLive.vue |
举手 toggle + 倒计时修复 + 教师离线提示 |
| 前端 | LiveDashboard.vue |
举手队列面板 |
| 前端 | KnowledgeBase.vue |
AI 生成面板 + 草稿列表 + 详情弹窗 |
| 前端 | live.ts |
API 类型扩展 |
| DB | V8~V12 共 5 个迁移 | 见上文汇总 |






