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

465 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 作业和考试模块审计报告
> 生成时间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.ts`65 行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.ts``autoMarkExamAction`(第 906-1250 行)包含大量纯转换辅助函数(`buildTiptapDocFromAiResponse``splitByDottedTexts` 等),应提取到 `ai-pipeline/``updateExamFromRichEditorAction`(第 1268-1523 行)直接包含 DB 事务逻辑,应下沉到 data-access
- `exams/data-access.ts`15+ 个导出函数混合了核心 CRUD、跨模块接口、年级仪表盘聚合未按职责拆分
- `homework/data-access.ts`V3-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` 内部直接执行数据库事务:
```typescript
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 行
```typescript
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 转换的 `as`15+ 处)符合规则 ✅。
### 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 行
```typescript
// 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.tsx`176 行)已实现批量自动批改,但缺少批量设置分数(全对/全错/自定义)功能。
**行业实践**:智学网支持列表页勾选多份提交,批量设置分数。
#### 差距 3多选题部分分P1
**当前**`computeIsCorrect` 对多选题采用"全对才得分"策略。
**行业实践**:智学网/猿题库支持"漏选得部分分"。
#### 差距 4优秀作业展示P2
**当前**:无优秀作业展示功能。
**行业实践**:班级小管家支持优秀作业置顶并让全班查看,激励学生。
#### 差距 5作业催交提醒P2
**当前**:无催交功能,教师只能手动通知未提交学生。
**行业实践**:智学网/班级小管家支持一键催交,自动发送通知给未提交学生。
---
## 四、改进优先级建议
### 4.1 P0 优先级(架构合规,必须立即修复)
| 编号 | 改进项 | 实现方案 | 影响范围 |
|------|--------|---------|---------|
| P0-1 | 拆分 `exams/actions.ts`1525→≤800 | 提取 `autoMarkExamAction` 辅助函数到 `ai-pipeline/auto-mark.ts`;提取 DB 事务到 `data-access` | exams 模块 |
| P0-2 | 拆分 `exams/data-access.ts`1036→≤800 | 按职责拆分为 `data-access.ts`(核心 CRUD+ `data-access-cross-module.ts`(跨模块接口) | exams 模块 |
| P0-3 | 拆分 `homework/data-access.ts`1008→≤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.ts`315→≤80 | 拆分为预览状态/后台任务/AI 重写三个 hook | exams/hooks/ |
| P2-2 | 拆分 `exam-rich-form.tsx`542→≤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.ts`65 行,已存在但未记录)
- 更新 `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.ts``homework/lib/question-content-utils` 的违规依赖(待修复)
#### 005_architecture_data.json 需更新
1. **modules.exams** 节点:
- 更新 `actions` 数组,补充 `autoMarkExamAction``createExamFromRichEditorAction``updateExamFromRichEditorAction` 的完整 deps
- 新增 `utils` 子节点,记录 `normalize-structure.ts`
- 更新 `dataAccess` 数组行数和职责描述
2. **modules.homework** 节点:
- 更新 `dataAccess` 行数记录
- 补充 `dataAccessClasses``dataAccessErrorCollection` 子节点
- 更新 `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.tsx`206 行async 服务端组件 + SectionErrorBoundary + Suspense + 骨架屏);`homework/data-access.ts::getExcellentSubmissions`(按得分率过滤、同一学生取最高分、按百分比降序);`homework/actions.ts::getExcellentSubmissionsAction`HOMEWORK_GRADE 权限 + scope 过滤 + 埋点 `homework.excellent_viewed``homework/types.ts` 新增 `ExcellentSubmissionItem`/`ExcellentSubmissionQuery` 类型i18n 键 `examHomework.homework.excellent.*`zh-CN + en 双语) |
| P3-2 | 作业催交提醒 | ✅ 已完成 | 新增 `homework/data-access.ts::getUnsubmittedStudents`(对比 targets 与已提交学生集合返回差集);`homework/actions.ts::remindUnsubmittedAction`HOMEWORK_GRADE 权限 + scope 过滤 + 调用 `notifications/data-access.createNotification` 创建 type=homework/priority=high 站内通知 + Promise.allSettled 容错统计 + 埋点 `homework.remind_unsubmitted``shared/lib/track-event.ts` EventName 新增 `homework.excellent_viewed`/`homework.remind_unsubmitted` |
| P3-3 | 移动端触控优化 | ✅ 已验证 | `homework-take-view.tsx` 导航按钮已使用 `h-11 w-11`44px触控目标符合 a11y 规范 |
| P3-4 | 家长考试详情视图 | ✅ 已完成 | 重写 `parent/components/child-exam-detail.tsx`165 行):所有硬编码英文文案改为 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>` |