Files
NextEdu/docs/architecture/audit/archive/homework-exams-audit-report.md

26 KiB
Raw Blame History

作业和考试模块审计报告

生成时间2026-06-22 审计范围:src/modules/exams/src/modules/homework/src/app/(dashboard)/teacher/exams/src/app/(dashboard)/teacher/homework/src/app/(dashboard)/student/learning/assignments/、相关共享层 审计维度:三层架构合规性、文件行数、跨模块依赖、权限校验、国际化、类型安全、错误处理、组件复用性


一、现有实现概要

1.1 模块文件分布

作业和考试模块由两个独立但紧密协作的模块组成,共 68 个源文件,总代码量约 14,800 行

考试模块(src/modules/exams/

子目录 文件数 总行数 主要职责
根目录 5 2,810 actions/data-access/types/stats-service/utils
ai-pipeline/ 4 1,117 AI 出题管线(解析/请求/结构/入口)
components/ 22 4,856 考试 UI 组件(表单/组卷/预览/分析/数据表格)
editor/ 13 1,834 Tiptap 富文本编辑器(节点扩展/工具栏/转换)
hooks/ 1 315 AI 预览状态管理
小计 45 10,932

作业模块(src/modules/homework/

子目录 文件数 总行数 主要职责
根目录 7 2,778 actions/data-access(3)/types/schema/stats-service
components/ 16 3,806 作业 UI 组件(作答/批改/阅卷/结果/复习)
hooks/ 2 329 自动保存/倒计时
lib/ 2 777 题目内容解析纯函数 + 单测
小计 27 7,690

App 页面

路由 文件数 说明
teacher/exams/ 10 列表/创建/组卷/分析/编辑/监考/批改
teacher/homework/ 8 列表/创建/详情/提交列表/批改/阅卷
student/learning/assignments/ 3 列表/作答/结果

1.2 数据流概要

考试与作业模块通过 sourceExamId 形成"考试 → 作业 → 提交 → 批改 → 分析"的完整数据链路:

教师创建考试 (exams)
  └─▶ 教师从考试派生作业 (homework, sourceExamId 关联)
       └─▶ 学生开始作答 (homeworkSubmissions)
            └─▶ 学生保存答案 (homeworkAnswers)
                 └─▶ 学生提交 (homeworkSubmissions.status = submitted)
                      └─▶ 教师批改 (homeworkAnswers.score/feedback)
                           └─▶ 考试分析 (exams/stats-service 聚合)
                           └─▶ 错题采集 (error-book 模块)
                           └─▶ 掌握度更新 (diagnostic 模块)

1.3 架构图记录完整性评估

004_architecture_impact_map.md已记录考试流程数据流1.3 节、AI 出题调用链1.4.1、学生提交链路1.4.2P1-1 已修复跨模块直查问题。但未记录 exams/utils/normalize-structure.ts65 行2026-06-22 新增)和 homework/data-access-classes.ts 的完整职责。

005_architecture_data.json:已记录 exams/homework 模块的 actions/dataAccess/tables/dependencyMatrix。但存在不一致:文档记录 homework/data-access.ts 为 598 行,实际已增长至 1008 行;exams/actions.ts 文档未反映已增至 1525 行。


二、现存问题与原因分析

2.1 文件行数严重超标P0

违反规则:项目规则「硬性上限:任何文件不超过 1000 行超过必须拆分」「Server Actions / Data Access 模块:建议 ≤ 800 行」「自定义 Hook建议 ≤ 80 行」「React 组件:建议 ≤ 500 行」

文件 实际行数 限制 超标程度 后果
exams/actions.ts 1525 800硬上限 1000 超硬上限 52% 维护困难、单测不可行、合并冲突频发
exams/data-access.ts 1036 800硬上限 1000 超硬上限 4% 同上
homework/data-access.ts 1008 800硬上限 1000 超硬上限 1% 同上
exams/hooks/use-exam-preview.ts 315 80 超标 294% Hook 职责过多,难以复用和测试
exams/components/exam-rich-form.tsx 542 500 超标 8% 组件臃肿
exams/components/assembly/structure-editor.tsx 771 500放宽 800 在放宽范围内 可接受但接近上限
homework/components/homework-grading-view.tsx 562 500放宽 800 在放宽范围内 可接受
homework/components/homework-take-view.tsx 557 500放宽 800 在放宽范围内 可接受

根因

  • exams/actions.tsautoMarkExamAction(第 906-1250 行)包含大量纯转换辅助函数(buildTiptapDocFromAiResponsesplitByDottedTexts 等),应提取到 ai-pipeline/updateExamFromRichEditorAction(第 1268-1523 行)直接包含 DB 事务逻辑,应下沉到 data-access
  • exams/data-access.ts15+ 个导出函数混合了核心 CRUD、跨模块接口、年级仪表盘聚合未按职责拆分
  • homework/data-access.tsV3-8/V3-9/V3-11 新增的 4 个跨模块查询函数导致文件再次膨胀

2.2 Server Action 直接操作数据库P0

违反规则项目规则「app/ 只能调用 modules/ 的 Server Actions 和 data-access不直接访问 DB」「actions 层移除直接 DB 操作」

位置exams/actions.ts 第 1492-1511 行,updateExamFromRichEditorAction 内部直接执行数据库事务:

await db.transaction(async (tx) => {
  await tx.update(exams).set({...}).where(eq(exams.id, input.examId))
  await tx.delete(examQuestions).where(eq(examQuestions.examId, input.examId))
  if (orderedQuestions.length > 0) {
    await tx.insert(examQuestions).values(...)
  }
})

后果破坏三层架构分层data-access 层无法统一控制 DB 访问,事务逻辑无法被其他 action 复用,单测需要 mock 整个 db 模块。

2.3 跨模块依赖违规P1

违反规则项目规则「modules/ 之间通过对方 data-access 通信,不直接查询对方 DB 表」

位置exams/stats-service.ts 第 12 行

import { getQuestionText } from "@/modules/homework/lib/question-content-utils"

此导入来自 homework/lib/ 而非 homework/data-access,绕过了模块的数据访问层封装。

后果:模块封装性受损,homework/lib/ 的内部实现变更会直接影响 exams 模块。

2.4 RSC 页面权限校验缺失P0

违反规则:项目规则「所有 Server Action 必须调用 requirePermission() 进行权限校验」「所有敏感数据查询必须在 data-access 层结合当前用户权限过滤」

考试模块页面权限缺失

页面 缺失权限点 后果
teacher/exams/all/page.tsx EXAM_READ 任何登录用户可查看考试列表
teacher/exams/create/page.tsx EXAM_CREATE 无创建权限用户可访问创建页
teacher/exams/new/page.tsx EXAM_CREATE 同上
teacher/exams/[id]/analytics/page.tsx EXAM_READ 可查看他人考试分析

作业模块页面 scope 缺失

页面 问题 后果
teacher/homework/assignments/page.tsx 未传 scope 仅按 creatorId 过滤,未应用 dataScope
teacher/homework/submissions/page.tsx 未传 scope 同上
teacher/homework/assignments/[id]/page.tsx 未传 scope 可通过猜测 ID 查看他人作业分析
teacher/homework/assignments/[id]/submissions/page.tsx 未传 scope 可查看他人作业提交列表
teacher/homework/submissions/[submissionId]/page.tsx 无 scope 可查看他人提交详情
teacher/homework/submissions/[submissionId]/scan-grading/page.tsx 无 scope 同上

根因data-access 函数已支持 scope?: DataScope 参数,但页面层未调用 getAuthContext() 获取 dataScope 并传递。

2.5 类型安全问题P1

违反规则:项目规则「禁止 as 断言(除非从 unknown 转换或测试中,需注释原因)」「可选链后禁止跟非空断言 !」

as never 断言(最严重,完全绕过类型检查)— 8 处

文件 行号 代码
exams/actions.ts 1306 editorDocToStructure(editorDoc as never, input.title)
exams/actions.ts 1314 content: q.content as never
exams/actions.ts 1414 editorDocToStructure(editorDoc as never, input.title)
exams/actions.ts 1452 content: q.content as never
exams/actions.ts 1453 q.type as "single_choice" | ...
exams/editor/extensions/group-block.tsx 25, 34 block as never
exams/editor/extensions/section-block.tsx 26, 35 block as never

后果:类型安全完全失效,运行时错误风险高。

非 unknown 的 as 断言 — 12+ 处

文件 行号 断言类型
exams/editor/editor-to-structure.ts 101 as RichQuestionType
exams/editor/editor-to-structure.ts 81, 85 非空断言 match[1]!match[2]!
exams/editor/exam-rich-editor.tsx 155, 171 as EditorJSONContent
exams/editor/exam-rich-editor.tsx 245 as QuestionBlockType
exams/editor/selection-toolbar.tsx 211, 213 as JSONContent[]as JSONContent
exams/components/exam-form.tsx 40 as Resolver<ExamFormValues>
exams/components/exam-rich-form.tsx 148 as EditorJSONContent
exams/components/exam-data-table.tsx 39 as Record<string, string | number | Date>
exams/components/assembly/structure-editor.tsx 459, 480, 481, 648, 649 as string(从 DragEndEvent

正面发现:全模块未发现 any 类型使用 ;从 unknown 转换的 as15+ 处)符合规则

2.6 国际化严重遗漏P1

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

考试模块 i18n 覆盖情况

覆盖状态 文件数 文件列表
完全覆盖 8 exam-form, exam-actions, exam-analytics-dashboard, exam-assembly, structure-editor, all/page, create/page, new/page
完全未覆盖 7 use-exam-preview.ts, exam-rich-editor.tsx, selection-toolbar.tsx, exam-preview-question-editor.tsx, question-sub-questions-editor.tsx, selected-question-list.tsx, exam-paper-preview.tsx
⚠️ 部分覆盖 4 exam-rich-form.tsx, actions.ts, proctoring/page.tsx, exam-form-types.ts

典型硬编码示例

  • hooks/use-exam-preview.ts:全部 toast 消息为硬编码中文(「页面刷新后任务已中断」「未命名试卷」「已加入后台队列」等 10+ 条)
  • editor/selection-toolbar.tsx:工具栏标签全部硬编码(「分卷」「大题」「单选」「填空/简答」「复合」「加点字」「填空」「图片」)
  • components/assembly/selected-question-list.tsx完全硬编码英文「No questions selected」「Create Group」「Create Section」等

作业模块 i18n 覆盖情况

覆盖状态 文件数 文件列表
完全覆盖 9 homework-take-view, homework-assignment-form, homework-grading-view, homework-batch-grading-view, homework-scan-grading-view, homework-submission-result, student-homework-review-view, question-renderer, scan-uploader
完全未覆盖 7 assignment-filters, homework-assignment-exam-content-card, homework-assignment-exam-preview-pane, homework-assignment-question-error-detail-panel, homework-assignment-question-error-overview-card, homework-assignment-exam-error-explorer-lazy, scan-image-viewer
⚠️ App 页面未覆盖 4 submissions/page, assignments/create/page, assignments/[id]/page, submissions/[submissionId]/page

估算总 i18n 覆盖率:约 48%(考试 40%,作业 55%

2.7 组件代码重复P2

违反规则:项目规则「组件必须为纯函数」和 DRY 原则

重复代码 位置 应提取到
QuestionContent + Answer 类型 homework-grading-view.tsx:537-544, homework-scan-grading-view.tsx:23-35 homework/types.ts
formatStudentAnswer 函数 homework-grading-view.tsx:537-544 已存在于 lib/question-content-utils.ts:335-343,应直接导入
isRecord + getOptions 函数 homework-assignment-question-error-detail-panel.tsx:6-21 已存在于 lib/question-content-utils.ts,应直接导入

2.8 共享层缺失组件P2

违反规则:项目规则要求最大化复用

缺失组件 现状 后果
共享 ErrorBoundary 6 个模块各自重复实现lesson-preparation/textbooks/ai/grades/school/settings 代码重复,行为不一致
PermissionGuard 不存在,仅通过 hook + 中间件 无统一的"无权限"UI 状态组件

2.9 导航与图片组件不规范P2

违反规则:项目惯例使用 Next.js <Link><Image>

文件 行号 问题
homework-batch-grading-view.tsx 154-165 使用 <a> 而非 <Link>
scan-uploader.tsx 208-211 使用 <img> 而非 <Image>(有 eslint-disable
scan-image-viewer.tsx 140-145, 192-196 使用 <img> 而非 <Image>(有 eslint-disable

2.10 data-access re-export 代码异味P3

位置homework/data-access.ts 第 1000-1008 行

// Re-export stats functions for backward compatibility
export { getTeacherGradeTrends, ... } from "./stats-service"

注释明确指出"New code should import directly from ./stats-service",但保留 re-export 仅为向后兼容,增加了文件行数。


三、行业差距对比

3.1 对标产品矩阵

结合 K12 教育系统特点对标智学网、班级小管家、猿题库、Google Classroom、Canvas LMS 五款产品:

功能维度 智学网 班级小管家 Google Classroom Canvas LMS 当前实现 差距评估
即时自动批改 提交即出分 需教师批改 需教师批改 可配置 仅批改页计算 P0 差距
批量批改 多选+批量 逐份批改 批量打分 ⚠️ 已实现但 UI 不完整 P1 差距
考试分析 难度/区分度/知识点 基础统计 基础统计 完整分析 ⚠️ 作业有分析,考试已新增 P1 差距
多选题部分分 漏选得部分分 全对才得分 全对才得分 可配置 全对才得分 P1 差距
提交后反馈 即时显示 跳转列表 等待教师 即时显示 已实现 result 页 已达标
错题本 自动归集 可导出 已实现 error-book 已达标
家长视图 考试详情+趋势 作业查看 观察员模式 ⚠️ 仅作业摘要 P2 差距
移动端触控 原生 App 小程序 响应式 响应式 ⚠️ 响应式但触控未优化 P3 差距
优秀作业展示 N/A 置顶+全班可见 P2 差距
作业催交提醒 自动提醒 一键催交 通知 P2 差距

3.2 关键差距分析

差距 1即时自动批改回写P0

当前流程:学生提交 → 跳转列表 → 教师打开批改页 → 客户端计算 → 教师手动提交成绩

行业实践:智学网/猿题库在学生提交瞬间服务端自动批改客观题,学生立即看到分数。

影响:学生提交后看不到即时成绩,体验割裂;若教师不打开批改页,客观题永远不会有分数。

差距 2批量批改 UI 不完整P1

当前homework-batch-grading-view.tsx176 行)已实现批量自动批改,但缺少批量设置分数(全对/全错/自定义)功能。

行业实践:智学网支持列表页勾选多份提交,批量设置分数。

差距 3多选题部分分P1

当前computeIsCorrect 对多选题采用"全对才得分"策略。

行业实践:智学网/猿题库支持"漏选得部分分"。

差距 4优秀作业展示P2

当前:无优秀作业展示功能。

行业实践:班级小管家支持优秀作业置顶并让全班查看,激励学生。

差距 5作业催交提醒P2

当前:无催交功能,教师只能手动通知未提交学生。

行业实践:智学网/班级小管家支持一键催交,自动发送通知给未提交学生。


四、改进优先级建议

4.1 P0 优先级(架构合规,必须立即修复)

编号 改进项 实现方案 影响范围
P0-1 拆分 exams/actions.ts1525→≤800 提取 autoMarkExamAction 辅助函数到 ai-pipeline/auto-mark.ts;提取 DB 事务到 data-access exams 模块
P0-2 拆分 exams/data-access.ts1036→≤800 按职责拆分为 data-access.ts(核心 CRUD+ data-access-cross-module.ts(跨模块接口) exams 模块
P0-3 拆分 homework/data-access.ts1008→≤800 提取跨模块函数到 data-access-exam-cross.ts;学生视角到 data-access-student.ts homework 模块
P0-4 修复 Server Action 直接操作 DB updateExamFromRichEditorAction 的事务逻辑下沉到 data-access exams/actions.ts
P0-5 补全考试页面权限校验 4 个页面添加 requirePermission() teacher/exams/
P0-6 补全作业页面 scope 传递 6 个页面调用 getAuthContext() 并传递 scope teacher/homework/

4.2 P1 优先级(重要体验与安全)

编号 改进项 实现方案 影响范围
P1-1 修复跨模块依赖 exams/stats-service.ts 改为从 homework/data-access 导入 getQuestionText exams/stats-service.ts
P1-2 消除 as never 断言 8 处改用类型守卫或正确类型签名 exams/actions.ts, editor/extensions/
P1-3 考试模块 i18n 补全 7 个完全未覆盖文件 + 4 个部分覆盖文件提取翻译键 exams 模块
P1-4 作业模块 i18n 补全 7 个组件 + 4 个 app 页面提取翻译键 homework 模块
P1-5 提取共享 ErrorBoundary 创建 shared/components/error-boundary.tsx,迁移 6 个模块重复实现 shared + 6 模块
P1-6 代码去重 提取 QuestionContent/Answer 类型到 types.ts删除重复函数 homework 模块

4.3 P2 优先级(增强体验)

编号 改进项 实现方案 影响范围
P2-1 拆分 use-exam-preview.ts315→≤80 拆分为预览状态/后台任务/AI 重写三个 hook exams/hooks/
P2-2 拆分 exam-rich-form.tsx542→≤500 提取 ExamPreview 子组件为独立文件 exams/components/
P2-3 替换 <a><Link> homework-batch-grading-view.tsx homework/components/
P2-4 移除 re-export 代码异味 排查调用方后移除 homework/data-access.ts 尾部 re-export homework/
P2-5 即时自动批改回写 markHomeworkSubmitted 中调用 applyAutoGrades 并回写 DB homework/data-access-write.ts
P2-6 多选题部分分 applyAutoGrades 增加部分分计算策略 homework/lib/

4.4 P3 优先级(细节优化,中长期)

编号 改进项 实现方案 影响范围
P3-1 优秀作业展示 新增 homework/components/excellent-submissions.tsx homework 模块
P3-2 作业催交提醒 新增 homework/actions.remindUnsubmittedAction + 通知 homework + notifications
P3-3 移动端触控优化 题目导航按钮调整为 44px 最小触控目标 homework-take-view.tsx
P3-4 家长考试详情视图 新增 parent/components/child-exam-detail.tsx parent 模块
P3-5 <img> 迁移 <Image> 评估 scan-uploader/scan-image-viewer 迁移可行性 homework/components/

4.5 实施顺序

第一阶段P0 架构合规):
  P0-1 → P0-2 → P0-3 → P0-4 → P0-5 → P0-6
  (先拆分大文件,再修复 DB 直访,最后补权限)

第二阶段P1 安全与体验):
  P1-1 → P1-2 → P1-3 → P1-4 → P1-5 → P1-6
  (先修复跨模块依赖和类型安全,再补 i18n最后提取共享组件

第三阶段P2 增强):
  P2-1 → P2-2 → P2-3 → P2-4 → P2-5 → P2-6
  (先拆分剩余超标文件,再实现即时批改)

第四阶段P3 中长期):
  P3-1 → P3-2 → P3-3 → P3-4 → P3-5
  (优秀作业、催交、移动端、家长视图、图片优化)

五、架构图同步说明

5.1 需要补充的节点

本次审计发现架构图存在以下遗漏和不一致,需同步更新:

004_architecture_impact_map.md 需更新

  1. exams 模块章节

    • 新增 utils/normalize-structure.ts65 行,已存在但未记录)
    • 更新 actions.ts 行数记录1525 行,超硬上限)
    • 更新 data-access.ts 行数记录1036 行,超硬上限)
    • 记录 updateExamFromRichEditorAction 中的 DB 直访问题(待修复)
  2. homework 模块章节

    • 更新 data-access.ts 行数记录1008 行,超硬上限,文档记录为 598 行已过时)
    • 记录 data-access-classes.ts 完整职责(跨模块查询封装)
    • 记录 data-access-error-collection.ts(错题采集接口)
  3. 跨模块依赖

    • 记录 exams/stats-service.tshomework/lib/question-content-utils 的违规依赖(待修复)

005_architecture_data.json 需更新

  1. modules.exams 节点:

    • 更新 actions 数组,补充 autoMarkExamActioncreateExamFromRichEditorActionupdateExamFromRichEditorAction 的完整 deps
    • 新增 utils 子节点,记录 normalize-structure.ts
    • 更新 dataAccess 数组行数和职责描述
  2. modules.homework 节点:

    • 更新 dataAccess 行数记录
    • 补充 dataAccessClassesdataAccessErrorCollection 子节点
    • 更新 dependencyMatrix,记录 exams → homework/lib 的违规依赖
  3. dbTables 节点:

    • 确认 exams/homeworkAssignments/homeworkSubmissions 等表的 usedBy 已正确记录(当前已正确)

5.2 同步时机

  • P0 修复完成后:同步更新文件行数和拆分后的新文件
  • P1-1 修复完成后:更新跨模块依赖记录
  • P1-5 完成后:记录共享 ErrorBoundary 组件

六、实施记录

以下部分记录审计文档中所有改进项的实施情况。每个改进项完成后更新状态。

6.1 P0 实施记录

编号 改进项 状态 实施说明
P0-1 拆分 exams/actions.ts 待实施
P0-2 拆分 exams/data-access.ts 待实施
P0-3 拆分 homework/data-access.ts 待实施
P0-4 修复 Server Action 直接操作 DB 待实施
P0-5 补全考试页面权限校验 待实施
P0-6 补全作业页面 scope 传递 待实施

6.2 P1 实施记录

编号 改进项 状态 实施说明
P1-1 修复跨模块依赖 待实施
P1-2 消除 as never 断言 待实施
P1-3 考试模块 i18n 补全 待实施
P1-4 作业模块 i18n 补全 待实施
P1-5 提取共享 ErrorBoundary 待实施
P1-6 代码去重 待实施

6.3 P2 实施记录

编号 改进项 状态 实施说明
P2-1 拆分 use-exam-preview.ts 待实施
P2-2 拆分 exam-rich-form.tsx 待实施
P2-3 替换 <a><Link> 待实施
P2-4 移除 re-export 代码异味 待实施
P2-5 即时自动批改回写 待实施
P2-6 多选题部分分 待实施

6.4 P3 实施记录(中长期)

编号 改进项 状态 实施说明
P3-1 优秀作业展示 已完成 新增 homework/components/excellent-submissions.tsx206 行async 服务端组件 + SectionErrorBoundary + Suspense + 骨架屏);homework/data-access.ts::getExcellentSubmissions(按得分率过滤、同一学生取最高分、按百分比降序);homework/actions.ts::getExcellentSubmissionsActionHOMEWORK_GRADE 权限 + scope 过滤 + 埋点 homework.excellent_viewedhomework/types.ts 新增 ExcellentSubmissionItem/ExcellentSubmissionQuery 类型i18n 键 examHomework.homework.excellent.*zh-CN + en 双语)
P3-2 作业催交提醒 已完成 新增 homework/data-access.ts::getUnsubmittedStudents(对比 targets 与已提交学生集合返回差集);homework/actions.ts::remindUnsubmittedActionHOMEWORK_GRADE 权限 + scope 过滤 + 调用 notifications/data-access.createNotification 创建 type=homework/priority=high 站内通知 + Promise.allSettled 容错统计 + 埋点 homework.remind_unsubmittedshared/lib/track-event.ts EventName 新增 homework.excellent_viewed/homework.remind_unsubmitted
P3-3 移动端触控优化 已验证 homework-take-view.tsx 导航按钮已使用 h-11 w-1144px触控目标符合 a11y 规范
P3-4 家长考试详情视图 已完成 重写 parent/components/child-exam-detail.tsx165 行):所有硬编码英文文案改为 i18n 翻译键 examHomework.homework.parentExam.*zh-CN + en 双语);新增 ChevronRight 导航图标ul/li 语义化标签 + ARIA 属性;触控目标 ≥ 44pxmin-h-[44px]
P3-5 <img> 迁移 <Image> 已完成 homework/components/scan-uploader.tsx<img><Image fill sizes="(max-width: 768px) 50vw, (max-width: 1200px) 33vw, 25vw">homework/components/scan-image-viewer.tsx:缩略图 <img><Image fill sizes="48px">(主查看器因复杂 CSS transforms 保留 <img>exams/components/exam-preview.tsx:题目图片 <img><Image fill sizes="128px" object-contain>