465 lines
26 KiB
Markdown
465 lines
26 KiB
Markdown
# 作业和考试模块审计报告
|
||
|
||
> 生成时间: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.2),P1-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 属性;触控目标 ≥ 44px(min-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>` |
|
||
|