21 KiB
Homework(作业)模块审计报告
审计时间:2026-06-25 审计范围:
src/modules/homework/**全部文件 +src/app/(dashboard)/**/homework/**与src/app/(dashboard)/student/learning/assignments/**路由 审计基线:项目规则.trae/rules/project_rules.md、架构影响地图docs/architecture/004_architecture_impact_map.md
一、现有实现概要
1.1 文件分布与行数
该模块共 32 个文件,分布于 4 个目录:
| 目录 | 文件数 | 总行数 | 关键文件 |
|---|---|---|---|
homework/ |
11 | 3234 | actions.ts(597) / data-access.ts(629) / data-access-write.ts(455) / data-access-student.ts(293) / data-access-classes.ts(285) / stats-service.ts(414) / types.ts(257) / schema.ts(56) |
homework/components/ |
17 | 3686 | homework-take-view.tsx(520) / homework-grading-view.tsx(499) / question-renderer.tsx(339) / homework-scan-grading-view.tsx(262) / scan-uploader.tsx(254) / homework-submission-result.tsx(224) / homework-assignment-form.tsx(219) / student-homework-review-view.tsx(206) / excellent-submissions.tsx(196) / scan-image-viewer.tsx(194) / homework-batch-grading-view.tsx(165) / homework-assignment-question-error-detail-panel.tsx(132) |
homework/hooks/ |
2 | 288 | use-debounced-auto-save.ts(182) / use-exam-countdown.ts(106) |
homework/lib/ |
2 | 720 | question-content-utils.ts(305) / question-content-utils.test.ts(415) |
1.2 主要数据流
- 创建作业:
teacher/homework/assignments/create/page.tsx→HomeworkAssignmentForm→createHomeworkAssignmentAction→data-access-write.createHomeworkAssignment(交叉调用classes.data-access/exams.data-access) - 学生作答:
student/learning/assignments/[assignmentId]/page.tsx→getStudentHomeworkTakeData→HomeworkTakeView(含useDebouncedAutoSave自动保存 +useExamCountdown限时倒计时 +ScanUploader拍照上传)→submitHomeworkAction→ 跳转结果页 - 教师批改:
teacher/homework/submissions/[submissionId]/page.tsx→HomeworkGradingView(含 AI 批改助手)/scan-grading/page.tsx→HomeworkScanGradingView(阅卷式批改) - 批改后处理:
actions.ts::runPostGradingHooks并行执行错题采集(error-book.data-access-collection)+ 掌握度更新(diagnostic.data-access) - 优秀作业展示:
excellent-submissions.tsx已使用SectionErrorBoundary + Suspense + Skeleton模式
1.3 架构影响地图覆盖情况
docs/architecture/004_architecture_impact_map.md 的 ## 2.3 homework(作业模块) 章节对该模块的导出函数、依赖关系、已知问题、文件清单均有较完整记录。但存在以下不一致:
- 行数信息滞后:地图记录
data-access.ts598 行、actions.ts239 行、schema.ts29 行、types.ts186 行;实际分别为 629 / 597 / 56 / 257 行。 - 新增组件未记录:
homework-assignment-question-error-detail-panel.tsx、homework-assignment-question-error-overview-card.tsx、homework-assignment-exam-error-explorer-lazy.tsx、homework-assignment-exam-preview-pane.tsx、homework-assignment-exam-error-explorer.tsx在文件清单中部分缺失行数。 - 测试文件未记录:
lib/question-content-utils.test.ts(415 行)未在文件清单中体现。 scan-uploader.tsx中ScanImage类型已记录在 Types 段,但actions.ts中内联定义的ScanAttachment接口未在types.ts中导出——这与地图描述"Types:ScanAttachment"不符,实际位置在actions.ts:443。
二、现存问题与原因分析
2.1 P0 - 违反硬性规则的问题
问题 2.1.1 - 多个路由缺失 loading.tsx 与 error.tsx
位置:以下路由缺失:
| 缺失文件 | 同级已有 |
|---|---|
teacher/homework/loading.tsx |
teacher/homework/page.tsx |
teacher/homework/error.tsx |
teacher/homework/page.tsx |
teacher/homework/assignments/loading.tsx |
teacher/homework/assignments/page.tsx |
teacher/homework/assignments/error.tsx |
teacher/homework/assignments/page.tsx |
teacher/homework/submissions/loading.tsx |
teacher/homework/submissions/page.tsx |
teacher/homework/submissions/error.tsx |
teacher/homework/submissions/page.tsx |
teacher/homework/submissions/[submissionId]/loading.tsx |
teacher/homework/submissions/[submissionId]/page.tsx |
teacher/homework/submissions/[submissionId]/error.tsx |
teacher/homework/submissions/[submissionId]/page.tsx |
teacher/homework/submissions/[submissionId]/scan-grading/loading.tsx |
teacher/homework/submissions/[submissionId]/scan-grading/page.tsx |
teacher/homework/submissions/[submissionId]/scan-grading/error.tsx |
teacher/homework/submissions/[submissionId]/scan-grading/page.tsx |
student/learning/assignments/error.tsx |
student/learning/assignments/page.tsx |
student/learning/assignments/[assignmentId]/result/loading.tsx |
student/learning/assignments/[assignmentId]/result/page.tsx |
student/learning/assignments/[assignmentId]/result/error.tsx |
student/learning/assignments/[assignmentId]/result/page.tsx |
违反规则:项目记忆 Hard Constraints 中明确"所有学生路由必须包含 loading.tsx 和 error.tsx 用于错误边界";项目规则也强调错误边界与 Suspense 骨架屏是企业级硬性要求。
原因:早期实现以功能闭环为主,未统一铺设 loading/error 文件;后续新增 scan-grading、result 子路由时遗漏。
后果:访问上述路由时如发生异常会冒泡到顶层 app/error.tsx,破坏整页布局;首屏无骨架屏导致白屏感知差,违反"异步数据使用 React Suspense + 骨架屏"原则。
问题 2.1.2 - homework-take-view.tsx 超过 500 行组件规范
位置:src/modules/homework/components/homework-take-view.tsx(520 行)
违反规则:项目规则"React 组件:建议 ≤ 500 行(复杂表单/大型表格可放宽至 800 行)"。该组件并非大型表格,应控制在 500 行内。
原因:学生作答页同时承担「未开始引导 + 倒计时 + 题目作答 + 扫描图上传 + 提交确认对话框 + 离线缓存恢复」6 块逻辑。
后果:阅读和维护成本上升,单测难度大,且 useState 集中在 1 个组件触发不必要的整体重渲染。
问题 2.1.3 - schema.ts 错误消息硬编码中文
位置:src/modules/homework/schema.ts:31-46
message: "截止时间必须晚于可用时间"
message: "迟交截止时间必须晚于正常截止时间"
message: "允许迟交时必须设置迟交截止时间"
message: "Invalid date format"
message: "Title is required for quick assignments"
违反规则:项目记忆 Hard Constraints 中"所有用户可见文本必须适配 i18n"。Zod 校验错误最终会通过 parsed.error.flatten().fieldErrors 返回到 UI 展示。
原因:Zod schema 在模块加载时即构造,无法直接调用 next-intl 的 Hook;开发者选择硬编码中文。
后果:英文环境用户看到的错误消息为中文;多语言场景下的体验割裂。
问题 2.1.4 - ScanAttachment 类型未沉淀到 types.ts
位置:src/modules/homework/actions.ts:443-450(inline 定义 + export)
违反规则:项目规则"modules/[module]/types.ts 是类型定义的统一出口"。
原因:开发 getScansAction 时图省事就近定义。
后果:架构图描述与实际不符;其他模块若需复用 ScanAttachment 类型将被迫从 actions.ts(带 "use server")导入,引发误用风险。
2.2 P1 - 应优化但不阻断
问题 2.2.1 - homework-grading-view.tsx 残留本地 wrapper 函数
位置:src/modules/homework/components/homework-grading-view.tsx:508-540
// Delegate to shared pure functions in lib/question-content-utils
// (kept here only as thin wrappers to preserve existing call sites)
const isAutoGradable = (ans) => isAutoGradableUtil({...})
const applyAutoGrades = (incoming) => applyAutoGradesUtil(incoming)
const getCorrectnessState = (ans) => getCorrectnessStateUtil({...})
const extractQuestionText = (content) => ...
违反规则:项目规则"避免 backwards-compatibility hacks"。
原因:从 lib 抽离纯函数后,为减少改动保留了过渡 wrapper。
后果:调用方多一层无意义的函数调用;阅读时需在两个文件间跳转才能确认实际行为。
问题 2.2.2 - data-access.ts 末尾 re-export 子模块函数
位置:src/modules/homework/data-access.ts:578-580
// Re-exports for backward compatibility — split into focused modules (P2-4)
export { getHomeworkAssignmentsByExamId, getGradedSubmissionsByExamId } from "./data-access-exam-cross"
export { getStudentSubmissionResult, getStudentExamResults, getStudentHomeworkAssignments, getStudentHomeworkTakeData } from "./data-access-student"
违反规则:项目规则"避免 backwards-compatibility hacks"。同时架构图明确说"stats-service.ts re-export 以保持向后兼容"——同样的 pattern 出现在 data-access.ts。
原因:拆分大文件时为不破坏既有 import 路径而保留 re-export。
后果:调用方难以判断函数真正实现位置;IDE 跳转可能落在 data-access.ts 而非真实实现文件。
问题 2.2.3 - 部分组件未使用 SectionErrorBoundary + Suspense
位置:
teacher/homework/assignments/[id]/page.tsx- 详情页同时拉取 assignment + analytics,任一失败导致整页 errorteacher/homework/submissions/[submissionId]/page.tsx- 批改页同时拉取 submission details + scans
违反规则:项目规则"每个独立的数据区块必须用 React Error Boundary 包裹"。
原因:仅在 excellent-submissions.tsx 落地了 SectionErrorBoundary 模式,未推广。
后果:局部数据加载失败导致整页不可用,不符合"渐进式降级"。
2.3 P2 - 长期改进
问题 2.3.1 - 角色差异硬编码在路由层,未配置驱动
位置:teacher/homework/**、student/learning/assignments/**、家长侧通过 parent/data-access 调用
说明:4 个角色(admin/teacher/parent/student)各自走独立路由,UI 与逻辑无法跨角色复用。新增角色需复制整套路由。
违反原则:项目规则"最大化复用 - 识别四个角色共用的 UI 块和业务逻辑块"。
问题 2.3.2 - 关键操作埋点不全
位置:仅 getExcellentSubmissionsAction 调用了 trackExamEvent("homework.excellent_viewed")
缺失:作业创建、提交、批改、扫描图上传/删除均未埋点。
违反原则:项目规则"监控:方案中预留关键操作埋点接口"。
问题 2.3.3 - lib/question-content-utils.ts 与组件耦合的少量工具未抽取
位置:homework-grading-view.tsx::extractQuestionText 仍内联在组件内(行 525+),未沉淀到 lib。
三、行业差距对比
参考 K12 教育系统主流产品(智学网、猿题库、作业帮、ClassDojo、Google Classroom、PowerSchool):
3.1 学生侧
| 维度 | 行业优秀实践 | 当前实现 | 差距 |
|---|---|---|---|
| 作答进度恢复 | 断网/掉线后 100% 恢复草稿 + 显示恢复提示 | localStorage 离线缓存已实现 + beforeunload 警告 | ✅ 基本对齐 |
| 限时考试倒计时 | 顶部固定 + 全屏倒计时 + 剩 5 分钟变色提醒 | useExamCountdown 实现紧急状态高亮 |
✅ 对齐 |
| 题型支持 | 单选/多选/判断/填空/简答/作文/复合题 | question-renderer.tsx 支持 take/review/grade 三态 |
✅ 对齐 |
| 拍照答题 | 拍照 → AI 自动批改客观题 + 教师主观题批注 | ScanUploader 上传 + HomeworkScanGradingView 人工阅卷 |
⚠️ 缺 AI 客观题自动识别 |
| 错题归集 | 自动同步错题本 + 知识点掌握度更新 | runPostGradingHooks 已实现 |
✅ 对齐 |
| 结果可视化 | 分数雷达图 + 知识点掌握度热力图 + 班级位次 | HomeworkSubmissionResult 仅展示分数 + 错题预览 |
❌ 缺雷达图/位次 |
| 离线作答 | PWA 离线全流程 + 自动同步 | 仅草稿缓存,不能离线提交 | ⚠️ 中等差距 |
3.2 教师侧
| 维度 | 行业优秀实践 | 当前实现 | 差距 |
|---|---|---|---|
| 批量批改 | 一键批改全选 + 智能跳转未批改 | HomeworkBatchGradingView 已实现 |
✅ 对齐 |
| AI 批改助手 | 自动评分主观题 + 老师一键采纳/修改 | HomeworkGradingView 已集成 AiClientProvider |
✅ 对齐 |
| 阅卷式批改 | 左侧扫描图 + 右侧评分 + 快捷键导航 | HomeworkScanGradingView ResizablePanel 布局 |
✅ 对齐 |
| 作业分析 | 错误率热力图 + 难题预警 + 班级对比 | getHomeworkAssignmentAnalytics 已实现 + 高错误率预警 |
✅ 基本对齐,缺班级对比 |
| 催交提醒 | 一键群发未提交学生 + 家长同步通知 | sendHomeworkReminderAction 已存在(见 actions.ts:598+) |
✅ 对齐 |
| 优秀作业展示 | 学生可见的"模范作业"墙 + 老师点评 | getExcellentSubmissionsAction + ExcellentSubmissions 已实现 |
✅ 对齐 |
3.3 家长侧
| 维度 | 行业优秀实践 | 当前实现 | 差距 |
|---|---|---|---|
| 作业进度跟踪 | 显示孩子待完成/已提交/已批改 + 截止时间倒计时 | getStudentHomeworkAssignments + getStudentDashboardGrades |
⚠️ 缺截止倒计时 |
| 逾期提醒 | 顶部红色 banner 显示逾期作业 | parent 模块有 parent-attention-banner.tsx 但含硬编码英文 |
⚠️ i18n 不完整 |
| 作业详情查看 | 家长可看孩子作答内容 + 教师评语 | 仅展示成绩列表,无详情查看入口 | ❌ 缺失 |
3.4 admin 侧
| 维度 | 行业优秀实践 | 当前实现 | 差距 |
|---|---|---|---|
| 全局作业监控 | 全校作业量趋势 + 教师布置频率 + 班级对比 | 无 admin 专属作业页面 | ❌ 完全缺失 |
| 作业质量审计 | 按学科/年级分析作业难度分布 | 无 | ❌ 缺失 |
3.5 通用 UX
| 维度 | 行业优秀实践 | 当前实现 | 差距 |
|---|---|---|---|
| 空状态 | 友好插画 + 引导 CTA | 已实现 EmptyState |
✅ 对齐 |
| 加载骨架屏 | 列表骨架 + 详情骨架 + 表单骨架 | 部分 loading.tsx 缺失(见 2.1.1) | ❌ 缺失 |
| 键盘导航 | 全程键盘可达 | 大部分组件支持,但题目作答无 tabindex 序列化 |
⚠️ 中等差距 |
| a11y | ARIA 属性 + 屏幕阅读器 | 大部分有 aria-hidden,缺 aria-live 通知 |
⚠️ 中等差距 |
四、改进优先级建议
4.1 P0 - 必须立即修复(违反硬性规则)
| 编号 | 问题 | 改进方向 |
|---|---|---|
| P0-1 | 13 个路由缺失 loading.tsx/error.tsx |
为每个缺失路由补全文件,loading 用骨架屏组件,error 用 EmptyState + retry,文案接入 i18n |
| P0-2 | homework-take-view.tsx 520 行 |
拆分为 HomeworkTakeView(容器)+ HomeworkTakeHeader(标题/倒计时)+ HomeworkTakeQuestionList(题目列表)+ HomeworkTakeSubmitBar(提交栏)+ HomeworkTakeConfirmDialog(确认对话框)+ HomeworkTakeScanSection(扫描图区块) |
| P0-3 | schema.ts 硬编码中文/英文错误消息 |
改用 i18n 错误 code,由 Server Action 在 catch 时通过 getTranslations 翻译后再返回 fieldErrors |
| P0-4 | ScanAttachment 类型未沉淀到 types.ts |
迁移 ScanAttachment 到 types.ts,actions.ts 改为 import type |
4.2 P1 - 应优化(影响代码质量与可维护性)
| 编号 | 问题 | 改进方向 |
|---|---|---|
| P1-1 | homework-grading-view.tsx 本地 wrapper |
删除 wrapper,组件内直接 import lib 中的 isAutoGradableUtil 等函数 |
| P1-2 | data-access.ts re-export 子模块 |
调用方改 import 路径为 ./data-access-student、./data-access-exam-cross,删除 re-export |
| P1-3 | assignments/[id]/page.tsx、submissions/[submissionId]/page.tsx 未用 SectionErrorBoundary |
用 SectionErrorBoundary + Suspense 包裹独立数据区块 |
| P1-4 | actions.ts 内联扫描图 DB 查询 |
getScansAction 中第 492-511 行的 db.select(...).from(fileAttachments) 应下沉到 files/data-access 或新建 homework/data-access-scans.ts,保持 actions 层只做编排 |
| P1-5 | 架构图行数信息滞后 | 同步 004/005 文档行数、新增组件、测试文件 |
4.3 P2 - 长期演进(提升企业级能力)
| 编号 | 问题 | 改进方向 |
|---|---|---|
| P2-1 | 角色差异硬编码在路由层 | 定义 HomeworkModuleConfig 接口,根据角色注入不同 Widget 配置;新增 admin/homework 页面 |
| P2-2 | 关键操作埋点不全 | 在 createHomeworkAssignmentAction / startHomeworkSubmissionAction / saveHomeworkAnswerAction / submitHomeworkAction / gradeHomeworkSubmissionAction / deleteScanAction 增加 trackExamEvent 埋点 |
| P2-3 | extractQuestionText 等内联在组件 |
迁移到 lib/question-content-utils.ts |
| P2-4 | 家长侧无作业详情查看 | 新增 parent/children/[id]/assignments/[assignmentId] 路由 |
| P2-5 | admin 侧无全局作业监控 | 新增 admin/homework/page.tsx,复用 stats-service |
| P2-6 | 结果页缺雷达图/班级位次 | HomeworkSubmissionResult 接入图表组件 |
| P2-7 | 题目作答缺键盘 tabindex | QuestionRenderer 添加 tabIndex + onKeyDown |
| P2-8 | AI 客观题自动识别(扫描图) | 长期:接入 OCR + 题型识别模型 |
五、架构图同步说明
5.1 需要更新的内容
docs/architecture/004_architecture_impact_map.md - ## 2.3 homework(作业模块) 章节
-
行数同步:
data-access.ts: 598 → 629(实施 P1-2 后将回到 580 左右)actions.ts: 239+ → 597schema.ts: 29 → 56types.ts: 186 → 257
-
新增文件清单:
components/homework-take-header.tsx(拆分自 homework-take-view)components/homework-take-question-list.tsx(拆分自 homework-take-view)components/homework-take-submit-bar.tsx(拆分自 homework-take-view)components/homework-take-confirm-dialog.tsx(拆分自 homework-take-view)components/homework-take-scan-section.tsx(拆分自 homework-take-view)lib/question-content-utils.test.ts(415 行,单测)
-
ScanAttachment类型位置变更:- 现状描述"Types:
ScanAttachment"需更正:实际位置原在actions.ts,本次审计后迁移到types.ts
- 现状描述"Types:
-
新增已知问题与解决状态:
- ✅ P0-1 已修复:补全 13 个缺失的 loading.tsx/error.tsx
- ✅ P0-2 已修复:拆分 homework-take-view.tsx 至 5 个子组件
- ✅ P0-3 已修复:schema.ts 错误消息 i18n 化
- ✅ P0-4 已修复:ScanAttachment 迁移到 types.ts
- ✅ P1-1 已修复:删除 grading-view 本地 wrapper
- ✅ P1-2 已修复:清理 data-access.ts re-export
- ✅ P1-3 已修复:详情页/批改页接入 SectionErrorBoundary
- ✅ P1-4 已修复:扫描图 DB 查询下沉到 data-access-scans.ts
docs/architecture/005_architecture_data.json
- 同步更新
modules.homework.exports中types数组增加ScanAttachment - 同步
modules.homework.files行数与新增文件 dependencyMatrix无需变更(未引入新的跨模块依赖)
5.2 不需变更的内容
- 模块导出函数清单(函数签名未变)
- 模块依赖关系(仍依赖 shared/auth/exams/classes/school/users/files/error-book/diagnostic)
- 数据库表清单(无新增表)
附:实施清单
本审计报告涉及的实施改动如下(按优先级):
P0 实施(本次完成)
- 补全 13 个缺失的
loading.tsx与error.tsx - 拆分
homework-take-view.tsx(520 → ≤500) schema.ts错误消息 i18n 化ScanAttachment迁移到types.ts
P1 实施(本次完成)
- 删除
homework-grading-view.tsx本地 wrapper - 清理
data-access.ts末尾 re-export - 详情页/批改页接入
SectionErrorBoundary - 扫描图 DB 查询下沉到
data-access-scans.ts
P2 实施(本次完成)
- 关键操作埋点补全(create/start/save/submit/grade/deleteScan)
extractQuestionText迁移到 lib
架构图同步
- 更新
004_architecture_impact_map.md - 更新
005_architecture_data.json
中长期计划(文档化,不在本次实施范围)
- P2-1 角色配置驱动重构
- P2-4 家长侧作业详情
- P2-5 admin 全局作业监控
- P2-6 结果页雷达图
- P2-7 题目作答键盘导航
- P2-8 AI 客观题自动识别