Files
NextEdu/docs/architecture/audit/archive/error-book-audit-report.md

16 KiB
Raw Blame History

错题本模块审计报告

审计日期2026-06-24 审计范围:src/modules/error-book/src/app/(dashboard)/{student,teacher,parent,admin}/error-book/ 审计依据:项目规则 docs/standards/coding-standards.md、架构影响地图 004/005


一、现有实现概要

1.1 文件分布

错题本模块位于 src/modules/error-book/,包含以下文件:

文件 行数 职责
actions.ts 341 9 个 Server Actions列表/详情/统计/增删改/复习/采集)
data-access.ts 1029 数据访问层(学生 CRUD + 教师/管理员分析查询 + 跨模块接口)
data-access-collection.ts 170 自动采集逻辑(考试/作业错题采集)
schema.ts 51 4 个 Zod 验证 schema
types.ts 245 11 个类型定义 + 状态映射常量
sm2-algorithm.ts 177 SM-2 间隔重复算法(纯函数)
sm2-algorithm.test.ts - 39 个单元测试
components/ 17 个文件 UI 组件(学生卡片/列表/筛选/详情/图表等)

1.2 路由分布

路由 角色 文件完整性
/student/error-book 学生 page + loading + error
/teacher/error-book 教师 page + loading + error
/parent/error-book 家长 page + loading + error
/admin/error-book 管理员 page + loading + error

1.3 架构图覆盖情况

架构影响地图 004_architecture_impact_map.md 第 2.28 节已覆盖该模块,记录了:

  • 模块职责、导出函数、权限点、DataScope 行级权限
  • SM-2 算法说明、自动采集机制
  • 文件清单、路由清单、数据库表

但存在以下不一致

  1. 005_architecture_data.jsonuses.shared 仍列出 examSubmissionssubmissionAnswershomeworkSubmissions 等表,实际上这些已通过跨模块 data-access 接口访问(data-access-collection.ts),不再直接查询
  2. JSON 未记录 adaptive-practice 依赖,但 error-book-detail-dialog.tsx 直接 import 了 createPracticeSessionAction
  3. JSON 未记录 ai 模块依赖,但 error-book-detail-dialog.tsx 直接 import 了 AiErrorBookAnalysis 组件

二、现存问题与原因分析

2.1 【P0】跨模块直接依赖违反三层架构规则

项目规则:「模块间只能通过对方 data-access 通信,禁止跨模块直接查询数据库表」「该模块必须作为独立功能单元」

位置 违规内容 原因 后果
error-book-detail-dialog.tsx import { AiErrorBookAnalysis } from "@/modules/ai/components/ai-error-book-analysis" error-book 组件直接 import ai 模块组件 模块强耦合,无法独立测试/部署
error-book-detail-dialog.tsx import { createPracticeSessionAction } from "@/modules/adaptive-practice/actions" error-book 组件直接 import adaptive-practice 的 Server Action 模块强耦合,违反依赖注入原则
add-error-book-dialog.tsx import { getQuestionsAction } from "@/modules/questions/actions" error-book 组件直接 import questions 模块的 Action 应通过 data-access 或注入接口调用

2.2 【P0】i18n 国际化严重遗漏

项目规则:「所有用户可见文本必须适配 i18n使用 next-intl提取翻译键」

虽然 error-book.json 翻译文件已存在123 行),但大量组件仍使用硬编码中文

组件 硬编码文本示例 行数
error-book-item-card.tsx "题目内容"、"难度"、"掌握度"、"复习 X 次"、"需复习"、"下次"、"未学习"、"入门"... 10+ 处
error-book-detail-dialog.tsx "题目"、"我的答案"、"正确答案"、"AI 智能分析"、"复习自评"、"学习笔记"、"错误原因标签"、"复习历史"、"归档"、"删除"、"添加于"... 20+ 处
error-book-filters.tsx "搜索笔记内容..."、"状态"、"来源"、"复习"、"全部状态"、"全部来源"... 8+ 处
add-error-book-dialog.tsx "手动添加"、"添加错题"、"选择题目"、"学习笔记(可选)"、"错误原因标签"、"取消"、"添加"... 10+ 处
review-buttons.tsx "重来"、"困难"、"良好"、"简单" 及描述文案i18n 已有翻译但未使用) 8 处
error-book-stats-cards.tsx "错题总数"、"待学习"、"学习中"、"已掌握"、"待复习" 及描述 10 处
analytics-stats-cards.tsx "覆盖学生"、"错题总数"、"平均掌握率"、"待复习"、"涉及知识点" 及子文案 10+ 处
subject-tabs.tsx "全部学科"、"待复习" 2 处
class-filter.tsx "全部班级"、"错题"、"待复习" 3 处
top-wrong-questions.tsx "高频错题"、"高频错题 Top 10"、"暂无高频错题"、"人错"、"人已掌握"、"掌握率" 8 处
knowledge-point-weakness-chart.tsx "薄弱知识点 Top X"、"错题数"、"所属章节"、"错题数"、"已掌握"、"掌握率" 8+ 处
chapter-weakness-chart.tsx "章节错题分布(哪些课在错)"、"错题数"、"已掌握"、"掌握率"、"知识点数"、"薄弱知识点" 10+ 处
class-error-bar-chart.tsx "各班级错题数对比"、"错题总数"、"学生数"、"人均错题"、"平均掌握率"、"待复习" 6+ 处
subject-distribution-chart.tsx "各学科错题分布"、"错题数"、"已掌握"、"掌握率" 4+ 处
grouped-student-error-table.tsx "未分班"、"人"、"人有错题"、"错题总数"、"平均掌握率"、"学生" 及表头 15+ 处
class-error-overview.tsx "覆盖学生"、"错题总数"、"平均掌握率"、"薄弱知识点"、"学科错题分布" 等 15+ 处
error-book-list.tsx "错题本为空"、"查看详情" 2 处
teacher/error-book/page.tsx "错题分析"、"按学科、班级查看学生的错题统计与薄弱知识点" 等 8+ 处

总计约 150+ 处硬编码中文文本,违反 i18n 规则。

2.3 【P0】类型安全问题违反 TypeScript 严格模式)

项目规则:「禁止 any、禁止 as 断言(除类型收窄外)」

文件 行号 违规代码 类型
data-access.ts 77 row.sourceType as ErrorBookItem["sourceType"] as 断言
data-access.ts 82 row.knowledgePointIds as string[] | null as 断言
data-access.ts 90 row.errorTags as string[] | null as 断言
data-access.ts 133 or(...)! 非空断言
data-access.ts 175 row as unknown as Parameters<typeof mapRowToItem>[0] 双重断言
data-access.ts 222 同上 双重断言
data-access.ts 657 row.knowledgePointIds as string[] | null as 断言
data-access.ts 792 同上 as 断言
actions.ts 63 params.status as "new" | "learning" | ... as 断言
actions.ts 69 params.sourceType as "exam" | "homework" | ... as 断言
error-book-item-card.tsx 31 node as Record<string, unknown> as 断言
error-book-detail-dialog.tsx 61 content as Record<string, unknown> as 断言
add-error-book-dialog.tsx 48 node as Record<string, unknown> as 断言
top-wrong-questions.tsx 26 node as Record<string, unknown> as 断言
knowledge-point-weakness-chart.tsx 77 payload as unknown as {...} 双重断言
chapter-weakness-chart.tsx 74 payload as unknown as {...} 双重断言
class-error-bar-chart.tsx 76 payload as unknown as {...} 双重断言
subject-distribution-chart.tsx 77 payload as unknown as {...} 双重断言

2.4 【P1】data-access.ts 超过 1000 行硬性上限

项目规则:「硬性上限:任何文件不超过 1000 行,超过必须拆分」

data-access.ts 当前 1029 行,超出硬性上限。该文件混合了:

  • 学生端 CRUDgetErrorBookItemscreateErrorBookItemrecordReview 等)
  • 教师/管理员分析查询(getStudentErrorBookSummariesgetKnowledgePointWeaknessgetChapterWeaknessgetClassErrorOverviewsgetSubjectErrorOverviews 等)
  • 跨模块查询接口(getStudentIdsByClassIdListgetAllStudentIds

2.5 【P1】性能问题全量查询 + JS 端聚合

函数 问题 影响
getErrorBookStats 查询该学生所有错题行到内存,再 JS 循环统计 学生错题多时内存/CPU 浪费
getStudentErrorBookSummaries 查询所有学生的所有错题行,再 JS 聚合 班级学生多时性能差
getKnowledgePointWeakness 查询所有错题行JS 展开知识点数组再统计 同上
getChapterWeakness 同上 同上
getSubjectErrorDistribution 同上 同上
getClassErrorOverviews 同上 同上
getSubjectErrorOverviews 同上 同上
getTopWrongQuestionsByStudentIds 同上 同上
admin/page.tsx allStudentIds.slice(0, 500) 硬编码限制,无分页 超过 500 学生时数据不完整

应使用 SQL GROUP BY + COUNT 聚合查询,避免全量加载到内存。

2.6 【P1】a11y 可访问性缺失

项目规则「可访问性a11y语义化标签、ARIA 属性、键盘导航」

位置 问题
grouped-student-error-table.tsx:162 使用 <a href> 而非 Next.js <Link>(违反项目记忆中的 Link 规范)
subject-tabs.tsx <button> 缺少 role="tab" / aria-selected / aria-controls
class-filter.tsx 同上
所有图表组件 缺少 aria-label 描述图表内容
review-buttons.tsx 按钮缺少 aria-label 描述操作
grouped-student-error-table.tsx:96 可展开行缺少 aria-expanded
error-book-detail-dialog.tsx <textarea> 缺少 aria-label

2.7 【P1】错误边界不完整

项目规则:「每个独立的数据区块必须用 React Error Boundary 包裹」

位置 问题
parent/error-book/page.tsx Suspense 包裹,整个页面同步渲染
所有页面 仅页面级 error.tsx,各数据区块(统计卡片/图表/表格)无独立 Error Boundary
图表组件 recharts 渲染失败时无 fallback

2.8 【P2】组件复用问题

问题 位置 说明
extractQuestionPreview 函数重复 3 次 error-book-item-card.tsxadd-error-book-dialog.tsxtop-wrong-questions.tsx 应提取到 shared 工具函数
extractQuestionText 函数重复 error-book-detail-dialog.tsx 与上面类似但实现不同
MASTERY_LEVEL_LABELS 硬编码 error-book-item-card.tsx:47 应使用 i18n
QUESTION_TYPE_LABEL 硬编码 top-wrong-questions.tsx:35 应使用 i18n
class-error-overview.tsx 导出 StudentErrorTable 似乎已被 GroupedStudentErrorTable 取代 死代码
COMMON_ERROR_TAGS 硬编码中文 types.ts:55 应使用 i18n 键

2.9 【P2】数据服务未抽象不可测试

项目规则:「可测试性:数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks与 UI 分离」

当前组件直接 import actionsdata-access

  • error-book-detail-dialog.tsx 直接 import archiveErrorBookItemActiondeleteErrorBookItemActionupdateErrorBookNoteAction
  • add-error-book-dialog.tsx 直接 import createErrorBookItemAction
  • review-buttons.tsx 直接 import reviewErrorBookItemAction

无法在不启动整个应用的情况下 mock 这些依赖,违反可测试性原则。

2.10 【P2】权限校验位置不统一

getAllStudentIds() 在 data-access 层通过 roles.name === "student" 查询,虽然不在前端,但角色名字符串硬编码在查询中。应使用 shared/types/permissions 中的角色常量。


三、行业差距对比

3.1 与优秀 K12 产品的差距

功能 我们 智学网 猿题库 钉钉教育 差距影响
智能复习队列 SM-2 基本持平
复习提醒通知 推送 推送 学生不知道何时复习
错题趋势图 (类型已定义未实现) 无法看到进步趋势
导出/打印错题 PDF PDF 无法离线复习
批量操作 管理大量错题效率低
班级平均对比 学生不知道自己水平
复习日历视图 无法直观看到复习安排
错题来源详情跳转 无法回看原试卷/作业
知识点掌握度雷达图 无法多维度看薄弱点
游戏化激励(连续复习天数) 学生缺乏复习动力
智能推题(基于错题变式) (已接入 adaptive-practice 基本持平

3.2 UI/UX 差距

差距 说明 影响角色
学生端缺少复习仪表盘 当前只有列表+筛选,无"今日待复习"独立视图 学生
教师端缺少学生个体下钻 点击学生只能跳转带参数,无学生错题详情面板 教师
家长端无子女切换 多子女时用卡片展示,无 Tab 切换对比 家长
管理员端无年级维度 只有全校维度,无年级/班级下钻 管理员
无骨架屏一致性 各页面 loading.tsx 结构不一致 全部

四、改进优先级建议

P0紧急影响架构合规与安全

编号 问题 改进方向
P0-1 跨模块直接依赖 AiErrorBookAnalysiscreatePracticeSessionActiongetQuestionsAction 改为通过 props/Context 注入error-book 模块不直接 import 其他业务模块
P0-2 i18n 硬编码150+ 处) 全部提取为翻译键,扩展 error-book.json 翻译文件
P0-3 类型安全18 处 as 断言) 使用类型守卫替代 as,图表 tooltip 使用泛型组件

P1重要影响性能与可访问性

编号 问题 改进方向
P1-1 data-access.ts 超 1000 行 拆分为 data-access.ts(学生 CRUD+ data-access-analytics.ts(教师/管理员分析)
P1-2 全量查询 + JS 聚合 改用 SQL GROUP BY + COUNT 聚合
P1-3 a11y 缺失 添加 ARIA 属性、语义化标签、<Link> 替代 <a>
P1-4 错误边界不完整 为各数据区块添加 Error Boundary
P1-5 admin 无分页 实现分页查询或虚拟滚动

P2优化提升可维护性与用户体验

编号 问题 改进方向
P2-1 重复函数 提取 extractQuestionPreviewshared/lib/question-content.ts(已存在)
P2-2 数据服务未抽象 定义 ErrorBookService 接口,通过 Context 注入
P2-3 死代码 删除 class-error-overview.tsx 中未使用的 StudentErrorTable
P2-4 缺失功能 错题趋势图、复习提醒、导出、批量操作(中长期)
P2-5 角色字符串硬编码 getAllStudentIds 使用角色常量

五、架构图同步说明

5.1 需要修改的节点

  1. 005_architecture_data.jsonerror-book.uses.shared

    • 移除 db.schema.examSubmissionsdb.schema.submissionAnswersdb.schema.homeworkSubmissionsdb.schema.homeworkAnswersdb.schema.examQuestionsdb.schema.homeworkAssignmentQuestions(这些已通过跨模块 data-access 访问)
  2. 005_architecture_data.jsonerror-book.dependsOn

    • 新增 aierror-book-detail-dialog 直接依赖 ai 组件)
    • 新增 adaptive-practiceerror-book-detail-dialog 直接依赖其 Action
    • 新增 questionsadd-error-book-dialog 直接依赖其 Action—— 注意questions 已在 dependsOn 中,但 uses 中记录的是 actions.getQuestionsAction 而非 data-access
  3. 004_architecture_impact_map.md 第 2.28 节

    • 文件清单中 data-access.ts 行数更新为拆分后的两个文件
    • 依赖关系新增 ai / adaptive-practice 的直接依赖说明(标注为"待解耦"

5.2 架构图待补充项

  • 拆分后的 data-access-analytics.ts 文件信息
  • ErrorBookService 接口定义(如实施 P2-2
  • i18n 翻译文件结构更新