从作业批改系统到学习管理平台
从作业批改系统到学习管理平台:一次完整的全栈开发实践
项目背景
今天完成了一个学习管理平台的数据可视化模块开发。这个项目基于已有的作业批改系统,扩展了教师端的数据分析功能,包括班级学情看板、知识点掌握度热力图、高频错题统计等。
技术栈:
- 后端:Spring Boot + MyBatis-Plus + PostgreSQL
- 前端:Vue3 + 原生 CSS
- AI 网关:OpenClaw(多模型调度)
核心功能实现
1. 数据可视化看板
教师端新增了一个数据看板页面,包含以下模块:
| 模块 | 功能描述 |
|---|---|
| 核心指标卡片 | 学生总数、作业总数、平均正确率、需关注学生 |
| 成绩分布图 | 柱状图展示各分数段人数占比 |
| 知识点热力图 | 12个知识点的掌握度可视化(绿/黄/红) |
| 高频错题 TOP10 | 错误率排序,前3标红高亮 |
| 学生学情列表 | 支持搜索、排序,需关注学生标红 |
| AI教案生成 | 选择教学目标,生成针对性教案 |
2. 知识点掌握度分析
这是本次开发的核心难点。我们需要:
- 修改 AI 批改 Prompt:让 AI 在批改作业时,同时分析涉及的知识点及掌握程度
- 新增知识点表结构:
knowledge_point(字典表)+homework_knowledge(关联表) - 动态热力图:根据实际作业内容,动态展示相关知识点(而不是固定列表)
AI 返回的 JSON 格式示例:
1 | { |
遇到的问题与解决方案
问题1:StackOverflowError - 无限递归
现象:后端抛出 java.lang.StackOverflowError,日志显示 OpenClawServiceImpl.chat() 方法无限调用。
原因:方法重载时,参数类型匹配错误导致自己调用自己。
1 | // 错误代码 |
解决:修正参数类型转换逻辑,确保调用的是真正实现方法。
1 | // 正确代码 |
问题2:JSON 解析失败 - 格式不统一
现象:AI 返回的内容有时包含 markdown 代码块标记(```json),有时又直接返回 JSON,导致解析失败。
原因:Skill 的 Prompt 要求不够严格,AI 有时会添加额外说明文字。
解决:
- 强化 Prompt 要求:”极其重要:必须只返回JSON,不要添加任何其他文字!“
- 后端增加数据清洗逻辑,去除可能的 markdown 标记:
1 | private String cleanRawResponse(String rawResponse) { |
问题3:热力图数据为空
现象:知识点热力图显示为0,数据库查询没有结果。
原因:homework_evaluation 表的 class_id 字段没有正确设置,导致 JOIN 查询失败。
解决:
- 确保
User实体包含classId字段 - 保存批改结果时,从用户信息中获取班级ID:
1 | User user = userMapper.selectById(userId); |
技术亮点
1. 动态知识点分析
传统的学习系统使用预定义的知识点列表,但我们的系统让 AI 根据作业内容动态分析涉及的知识点。这样无论是 Java、C语言还是其他科目,都能自动提取相关知识点。
2. 数据驱动的教案生成
基于学生的薄弱知识点,系统可以自动生成针对性教案。教师只需选择教学目标(巩固基础、突破难点等),AI 就会结合班级数据生成个性化教案。
3. 前后端分离的实时数据
前端使用 Vue3 的 watch 监听班级选择变化,自动加载对应数据。后端提供 RESTful API,支持按班级筛选和排序。
项目结构
1 | 后端 (Spring Boot) |
总结
这次开发让我深刻体会到:
AI 输出的不确定性:即使 Prompt 写得很清楚,AI 仍可能返回各种格式,后端必须做好容错处理。
数据关联的重要性:一个
class_id字段的遗漏,会导致整个查询链条断裂。数据库设计时要考虑完整的关联关系。渐进式开发的价值:先让基础功能跑通(静态数据),再逐步替换成动态数据,最后优化细节。
跨层调试的复杂性:问题可能出现在 Skill、后端、前端任何一个环节,需要分层定位。
项目已开源,欢迎交流讨论。





