背景

教学数据中心的热力图原本是 AI 批改作业时自动生成 的:AI 按闭包知识点列表返回 knowledgePoints(掌握度),后端存入 homework_knowledge 表,前端只读渲染。老师无法干预热力图内容。

本次重构目标:把热力图从 AI 自动生成的静态展示,变成老师驱动的动态管理系统——老师创建知识点,AI 批改时按老师定义的知识点对错误进行分类,热力图变成错误聚合 + 交互式筛选入口。


数据流改造对比

1
2
旧:AI 批改 → knowledgePoints(封闭列表) → homework_knowledge → 热力图
新:老师创建知识点 → AI 批改(传入知识点列表) → errors[].knowledgePoint → submission_errors → 热力图 → 点击弹窗看具体错误

核心改动

1. 数据库

操作 表名 说明
新建 teacher_knowledge 老师自定义知识点(class_id, name, color, sort_order)
新建 submission_errors 错误分类明细(error_text, knowledge_point, severity)
删除 homework_knowledge 旧表废弃

2. SKILL.md 改造

  • errors[]suggestions[] 增加 knowledgePoint 字段
  • 教师定义知识点优先:提供了就用,未提供/空则全部归「其他」
  • 删除了旧的闭包知识点列表

3. Entity / Mapper

  • 新增 TeacherKnowledgeTeacherKnowledgeMapper
  • 新增 SubmissionErrorSubmissionErrorMapper
  • 删除 HomeworkKnowledgeHomeworkKnowledgeMapper
  • DashboardServiceImpl.KP_CANONICAL(知识点评分名称归一化 Map)删除

4. Controller / Service

  • GET /api/dashboard/knowledge-mastery:从 teacher_knowledge + submission_errors 聚合,老师定义优先,自动补「其他」
  • GET /api/dashboard/frequent-errors:增加 knowledgePoint 筛选参数,去掉缓存(避免新 key 没被清理的问题)
  • POST /api/dashboard/teacher-knowledge/add:单条添加
  • DELETE /api/dashboard/teacher-knowledge/{id}:单条删除
  • POST /api/dashboard/teacher-knowledge/batch:批量覆盖(编辑模式)
  • saveTeacherKnowledge / addTeacherKnowledge 末尾:自动建「其他」+ 关键词重归类 + AI 异步重归类

5. GradingStreamConsumer

  • 停写 homework_knowledge
  • 注入 TeacherKnowledgeMapper + SubmissionErrorMapper
  • buildPrompt:传入该班级老师定义的知识点列表作为 AI 上下文
  • saveSubmissionErrors:解析 AI 返回的 errors[].knowledgePoint + suggestions[].knowledgePoint,逐条写入 submission_errors

6. 前端(PageFour.vue)

  • 热力图:展示错误数,不再展示掌握度百分比
  • 添加知识点弹窗:输入名称 + 选颜色 → 调 POST add 直接入库
  • 编辑模式:每格可改名称/颜色/删除,「其他」删除按钮禁用
  • 保存按钮:批量覆盖 + 触发重归类轮询
  • 点击热力图格子:弹窗展示该知识点的错误列表

遇到的问题与解决

问题 1:@Cacheable 新 key 未被清理

现象getFrequentErrors(classId, knowledgePoint) 加了 knowledgePoint 参数后,缓存 key 从 errors:1 变成 errors:1:其他,但 GradingStreamConsumer 里只清理了 errors:1,导致新 key 缓存了空结果永远不更新。

解决:去掉 getFrequentErrors@Cacheable 注解。该接口入参组合太多,不缓存更简单。

问题 2:knowledge_point = NULL 查不出数据

现象SubmissionErrorMapper.selectByClassIdAndKnowledgePoint(classId, null, 50) 生成的 SQL 是 WHERE knowledge_point = NULL,在 SQL 里永远为假。

解决:改用 MyBatis 动态 SQL <if test='knowledgePoint != null'>,null 时不拼接该条件。

问题 3:保存时「其他」被重复插入

现象saveKnowledgeknowledgeMastery 整个数组传给后端批量保存,混入了后端动态生成的「其他」项,触发 uk_class_knowledge 唯一约束冲突。

解决:改为单条操作(add/delete 各调独立接口),编辑模式批量保存时从数组里分离「其他」不再 filter,因为「其他」已由后端 ensureOtherExists() 统一管理。

问题 4:「其他」重复出现在热力图

现象getKnowledgeMastery()teacher_knowledge 已有「其他」的情况下,又追加了一次合成版「其他」。

解决:增加 hasOtherInTk 判断,teacher_knowledge 里有就不补合成版。

问题 5:数据库文件编码损坏

现象:用 PowerShell Set-Content 做字符串替换后,Java 文件的 import 语句被破坏,中文注释乱码。

解决:通过 write 工具重写整个文件,避免 PowerShell 编码副作用。


重归类机制

1
2
3
4
老师保存知识点
├── ensureOtherExists() —— 确保 teacher_knowledge 有「其他」
├── reclassifyUnclassified() —— 关键词匹配合并(60+ C语言关键词,毫秒级)
└── reclassifyWithAI() —— @Async 异步,OpenClaw 调用,语义理解

前端反馈:保存后 toast 提示,轮询「其他」错误数变化,减少时通知用户。


废弃删除清单

项目 处理
homework_knowledge DROP TABLE
HomeworkKnowledge.java 删除文件
HomeworkKnowledgeMapper.java 删除文件
KP_CANONICAL 静态块 删除
GradingStreamConsumer 写入旧表代码 删除
HomeworkResultService 知识点方法 删除,改用 SubmissionErrorMapper
SubmissionService 未使用方法 清理

API 变更汇总

方法 路径 说明
GET /api/dashboard/knowledge-mastery 改造:从 teacher_knowledge + submission_errors 聚合
GET /api/dashboard/frequent-errors 增加 ?knowledgePoint= 可选参数
GET /api/dashboard/teacher-knowledge 新增:查询老师自定义知识点
POST /api/dashboard/teacher-knowledge/add 新增:单条添加
DELETE /api/dashboard/teacher-knowledge/{id} 新增:单条删除
POST /api/dashboard/teacher-knowledge/batch 新增:批量覆盖