Verilog新手入门:硬件思维、时序逻辑与仿真实战
2026/9/18 2:36:37
按照考试执行→客观题答题记录生成→主观题评分→成绩整合→错题集生成→学习分析的核心业务流转顺序(“先有成绩、再有错题、最后学习分析”逻辑),聚焦mock_exam_answer(客观题答题表)、marking_score(主观题评分表)的成绩整合核心,以及wrong_question_collection/item(错题主/明细表)的前置依赖,重构学习分析模块的闭环设计,确保完全匹配真实业务流程。
mock_exam_answer:存储客观题(选择/判断)的作答选项、是否正确、自动得分,无人工干预;marking_score:存储主观题(简答/作文)的批改分数、评分备注,是主观分的唯一来源;wrong_question_collection(错题主表:单学生-单考试的错题集整体信息);wrong_question_item(错题项表:每一道错题的详情,区分客观/主观错题类型);| 区域 | 学习分析模块专属设计(贴合业务顺序) | 全局规范对齐 |
|---|---|---|
| 左侧导航栏 | 学习分析模块位于「错题管理」之后,图标为📊,当前模块高亮;若前序无成绩/错题数据,模块入口置灰并提示“暂无可用分析数据(需先完成考试批改+错题生成)” | 固定宽度、按业务顺序排列,支持折叠/展开 |
| 顶部操作栏 | 保留「筛选重置、报告导出、数据刷新」(全局统一样式);新增「数据溯源」按钮,点击可按业务顺序展示:考试→客观题记录→主观题评分→成绩→错题→分析结果的完整链路 | 显示系统路径、用户头像、全局通知铃铛,按钮样式符合全局规范 |
| 内容区 | 「标签页+卡片+图表」布局强化“数据前置”逻辑: 1. 所有分析标签页顶部增加「数据状态提示」(如“当前分析基于XX次考试的批改数据,共生成XX套错题集”); 2. 无成绩/错题数据时,所有图表/卡片替换为“数据依赖提示”(例:“暂无成绩数据,请先完成主观题批改”); 3. 分析结果卡片增加「关联错题集」入口,点击跳转至对应错题主表/明细表 | 卡片式布局、操作按钮颜色规范,弹窗/抽屉样式与全局统一 |
| 底部状态栏 | 显示分析数据的最终更新时间,以及前序核心数据的更新时间(如“成绩数据更新:2026-01-22 10:00;错题数据更新:2026-01-22 10:10”) | 统一显示页码/总数据量,支持查看操作日志 |
| 场景 | 交互逻辑(贴合“先成绩/错题,后分析”) | 全局规范对齐 |
|---|---|---|
| 数据刷新触发 | 仅当“成绩整合完成”或“错题集生成/更新”后,才推送WebSocket通知,提示“可刷新学习分析数据”;无前置数据时,刷新按钮置灰 | 全局WebSocket实时通知机制 |
| 分析维度穿透 | 点击分析结果(如“某知识点错误率80%”),先跳转至wrong_question_item查看该知识点的错题明细,再可进一步穿透至mock_exam_answer/marking_score查看原题作答/批改记录 | 全局模块间跳转规则,支持携带参数自动筛选 |
| 筛选逻辑限制 | 筛选“考试”维度时,仅展示“已完成批改(有marking_score)+已生成错题集(有wrong_question_collection)”的考试,未完成前序环节的考试不显示 | 下拉框含搜索功能、滚动加载,符合全局筛选交互 |
| 报告导出规则 | 导出的分析报告中,新增“数据依赖说明”章节,明确标注:本报告基于XX考试的mock_exam_answer(客观题)、marking_score(主观题)、wrong_question_item(错题)数据生成 | 全局批量操作/导出功能的样式、反馈规范 |
| 核心表 | 外键关联 | 生成顺序 | 作用 |
|---|---|---|---|
mock_exam_answer(客观题答题表) | exam_id/user_id/question_id | 第1步(考试提交后) | 存储客观题作答记录、自动判分结果,是客观分唯一来源 |
marking_score(主观题评分表) | exam_id/user_id/question_id | 第2步(教师批改后) | 存储主观题批改分数、备注,是主观分唯一来源 |
wrong_question_collection(错题主表) | user_id/exam_id/class_id | 第4步(成绩整合后) | 存储单学生-单考试的错题集基础信息,关联最终成绩 |
wrong_question_item(错题项表) | collection_id/question_id/knowledge_id | 第5步(错题主表后) | 存储单道错题详情,区分客观/主观错题类型 |
// 整合客观/主观分,生成最终成绩(错题生成的前置接口)@PostMapping("/score/integrate")publicResultintegrateFinalScore(@RequestParamLongexamId,@RequestParamLonguserId){// 1. 校验前置数据:必须先有客观题记录booleanhasObjective=mockExamAnswerService.existsByExamIdAndUserId(examId,userId);if(!hasObjective){returnResult.fail("前置数据缺失:暂无该考生的客观题答题记录");}// 2. 校验前置数据:必须先有主观题评分booleanhasSubjective=markingScoreService.existsByExamIdAndUserId(examId,userId);if(!hasSubjective){returnResult.fail("前置数据缺失:该考生的主观题尚未批改");}// 3. 计算客观题总分BigDecimalobjectiveScore=mockExamAnswerService.calculateObjectiveTotalScore(examId,userId);// 4. 计算主观题总分BigDecimalsubjectiveScore=markingScoreService.calculateSubjectiveTotalScore(examId,userId);// 5. 整合最终成绩BigDecimaltotalScore=objectiveScore.add(subjectiveScore);// 6. 保存最终成绩(供错题生成使用)examScoreService.saveOrUpdate(newExamScore().setExamId(examId).setUserId(userId).setObjectiveScore(objectiveScore).setSubjectiveScore(subjectiveScore).setTotalScore(totalScore));returnResult.success(totalScore);}// 基于最终成绩+答题错误记录,生成错题主/明细表@PostMapping("/wrong/gen")publicResultgenerateWrongQuestion(@RequestParamLongexamId,@RequestParamLonguserId){// 1. 校验前置数据:必须先有整合后的最终成绩ExamScoreexamScore=examScoreService.getByExamIdAndUserId(examId,userId);if(examScore==null){returnResult.fail("前置数据缺失:尚未生成该考生的最终成绩");}// 2. 生成错题主表WrongQuestionCollectioncollection=wrongQuestionCollectionService.saveCollection(examId,userId);// 3. 生成客观题错题项(从mock_exam_answer筛选错误记录)List<WrongQuestionItem>objectiveItems=mockExamAnswerService.listWrongItems(examId,userId).stream().map(item->newWrongQuestionItem().setCollectionId(collection.getId()).setQuestionId(item.getQuestionId()).setKnowledgeId(item.getKnowledgeId()).setQuestionType("OBJECTIVE")// 标记客观题.setWrongReason(item.getWrongReason())).collect(Collectors.toList());// 4. 生成主观题错题项(从marking_score筛选低分/错误记录)List<WrongQuestionItem>subjectiveItems=markingScoreService.listWrongItems(examId,userId).stream().map(item->newWrongQuestionItem().setCollectionId(collection.getId()).setQuestionId(item.getQuestionId()).setKnowledgeId(item.getKnowledgeId()).setQuestionType("SUBJECTIVE")// 标记主观题.setWrongReason(item.getWrongReason())).collect(Collectors.toList());// 5. 批量保存错题项wrongQuestionItemService.saveBatch(Stream.concat(objectiveItems.stream(),subjectiveItems.stream()).collect(Collectors.toList()));returnResult.success("错题集生成成功,可进行学习分析");}// 基于错题主/明细表,统计知识点掌握率(学习分析核心接口)@GetMapping("/knowledge/mastery")publicResultgetKnowledgeMastery(@RequestParamLongclassId,@RequestParamLongsubjectId){// 1. 校验前置数据:必须有错题数据booleanhasWrongData=wrongQuestionCollectionService.existsByClassIdAndSubjectId(classId,subjectId);if(!hasWrongData){returnResult.fail("前置数据缺失:该班级暂无错题集,无法进行知识点掌握率分析");}// 2. 按知识点统计错题(区分客观/主观)List<KnowledgeMasteryVO>masteryList=knowledgeService.listBySubject(subjectId).stream().map(knowledge->{LongknowledgeId=knowledge.getId();// 2.1 客观题错题数(来自wrong_question_item)LongobjectiveWrong=wrongQuestionItemService.countByKnowledgeAndType(knowledgeId,classId,"OBJECTIVE");// 2.2 主观题错题数(来自wrong_question_item)LongsubjectiveWrong=wrongQuestionItemService.countByKnowledgeAndType(knowledgeId,classId,"SUBJECTIVE");// 2.3 该知识点总答题数(客观+主观)LongtotalAnswer=mockExamAnswerService.countByKnowledge(knowledgeId,classId)+markingScoreService.countByKnowledge(knowledgeId,classId);// 2.4 计算掌握率BigDecimalmasteryRate=totalAnswer==0?BigDecimal.ZERO:newBigDecimal(totalAnswer-objectiveWrong-subjectiveWrong).divide(newBigDecimal(totalAnswer),2,RoundingMode.HALF_UP);returnnewKnowledgeMasteryVO(knowledge.getKnowledgeName(),masteryRate,objectiveWrong,subjectiveWrong);}).collect(Collectors.toList());returnResult.success(masteryList);}