feat(exam-homework): add audit report, i18n, error boundaries, and permission hardening

- Add comprehensive audit report for exam and homework module

- Create exam-homework i18n message files (zh-CN + en) and register namespace

- Add permission check to gradeHomeworkSubmissionAction to prevent horizontal privilege escalation

- Add Error Boundary + loading.tsx for 5 key pages (exam build/proctoring, homework assignment/submissions, student assignment)

- Refactor exam-columns to createExamColumns(t) factory for i18n support

- Refactor exam-data-table to manage columns internally via useTranslations

- Replace hardcoded strings with i18n keys in all exam/homework components and pages

- Add getHomeworkSubmissionForGrading data-access for secure grading flow
This commit is contained in:
SpecialX
2026-06-22 16:08:39 +08:00
parent fde711ce46
commit 21c7e65fee
26 changed files with 2059 additions and 463 deletions

View File

@@ -0,0 +1,396 @@
# 考试和作业模块审计报告
> 审计范围:`exams`(考试/试卷/AI 出题)、`homework`(作业/指派/作答/批改)、`proctoring`(监考/防作弊)三个相互耦合的模块,以及它们在 `app/(dashboard)` 下的对应路由页面。
---
## 一、现有实现概要
### 1.1 文件分布
| 层 | 模块 | 关键文件 | 行数 |
|----|------|----------|------|
| app 路由 | teacher/exams | `page.tsx` / `all/page.tsx` / `create/page.tsx` / `[id]/build/page.tsx` / `[id]/proctoring/page.tsx` / `grading/page.tsx`(重定向) / `grading/[submissionId]/page.tsx`(重定向) | - |
| app 路由 | teacher/homework | `assignments/page.tsx` / `assignments/create/page.tsx` / `assignments/[id]/page.tsx` / `assignments/[id]/submissions/page.tsx` | - |
| app 路由 | student/learning/assignments | `page.tsx` / `[assignmentId]/page.tsx` + `loading.tsx` | - |
| modules | exams | `actions.ts`(691) / `ai-pipeline.ts`(857) / `data-access.ts`(473) / `types.ts`(31) / `hooks/use-exam-preview.ts`(295) / `utils/normalize-structure.ts`(57) / `components/*`(18 文件) | - |
| modules | homework | `actions.ts`(239) / `data-access.ts`(598) / `data-access-write.ts`(285) / `data-access-classes.ts`(232) / `stats-service.ts`(425) / `schema.ts`(29) / `types.ts`(186) / `components/*`(11 文件) | - |
| modules | proctoring | `actions.ts`(139) / `data-access.ts`(409) / `types.ts`(136) / `components/*`(3 文件) | - |
### 1.2 主要数据流
1. **考试创建**`teacher/exams/create``createExamAction` / `createAiExamAction``persistExamDraft` / `persistAiGeneratedExamDraft``db.insert(exams)`
2. **组卷**`teacher/exams/[id]/build``getExamById` + `getQuestions``ExamAssembly``updateExamAction`
3. **作业下发**`teacher/homework/assignments/create``createHomeworkAssignmentAction``getExamWithQuestionsForHomework`(跨模块调用 exams data-access`createHomeworkAssignment`(事务写入 assignments + questions + targets
4. **学生作答**`student/learning/assignments/[assignmentId]``getStudentHomeworkTakeData``HomeworkTakeView``startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction`
5. **教师批改**`teacher/homework/assignments/[id]/submissions``getHomeworkSubmissions` → 跳转 `[submissionId]``getHomeworkSubmissionDetails``HomeworkGradingView``gradeHomeworkSubmissionAction`
6. **监考**`teacher/exams/[id]/proctoring``getProctoringDashboardAction``getExamForProctoring` + `getExamProctoringSummary` + `getStudentProctoringStatuses` + `getRecentProctoringEvents`
### 1.3 架构图覆盖情况
`docs/architecture/004_architecture_impact_map.md` 已记录 exams§2.2、homework§2.3、proctoring§2.21)三个模块的导出函数、依赖关系、已知问题和文件清单。架构图信息基本完整,但以下细节未记录:
- `homework/components/homework-assignment-exam-error-explorer.tsx` 等错误分析组件未在文件清单中列出。
- `exams/components/assembly/*` 子目录的 4 个组件未单独记录行数。
- proctoring 的 `exam-mode-config.tsx` 死代码状态已在已知问题中标注,但未记录其与 `ExamForm` 的集成缺失原因。
---
## 二、现存问题与原因分析
### 2.1 国际化缺失(严重)
**问题**:该模块几乎所有用户可见文本均为硬编码,且中英文混杂。
**出现位置**
- `src/modules/exams/components/exam-form.tsx`:硬编码英文 `"Exam draft created"``"Redirecting to exam builder..."``"Missing subject or grade configuration"`
- `src/modules/exams/components/exam-columns.tsx`:硬编码 `"Exam Info"``"Status"``"Stats"``"Difficulty"``"Easy"``"Medium"``"Hard"`
- `src/modules/exams/components/exam-actions.tsx`:硬编码 `"Preview Exam"``"Copy ID"``"Edit"``"Build"``"Publish"``"Archive"``"Delete"``"Are you absolutely sure?"`
- `src/modules/homework/components/homework-take-view.tsx`:硬编码 `"Questions"``"Start Assignment"``"Submit Assignment"``"Save Answer"``"Due Date"``"Attempts"``"Description"``"Progress"``"Confirm Submission"`
- `src/modules/homework/components/homework-grading-view.tsx`:硬编码 `"Grading Summary"``"Total Score"``"Correct"``"Incorrect"``"Partial"``"Submit Grades"``"Previous Student"``"Next Student"`
- `src/modules/homework/components/homework-assignment-form.tsx`:硬编码中文 `"快速作业"``"考试派生作业"``"直接输入标题和描述,无需建题"``"从已有考试派生作业"`
- `src/app/(dashboard)/teacher/homework/assignments/page.tsx`:硬编码中文 `"作业列表"``"管理作业,查看提交率与批改进度。"``"创建作业"``"暂无作业"``"按班级筛选:"``"清除筛选"``"标题"``"状态"``"截止时间"``"提交率"``"平均分"``"逾期"``"来源考试"``"创建时间"`
- `src/app/(dashboard)/teacher/homework/assignments/[id]/submissions/page.tsx`:硬编码英文 `"Submissions"``"Student"``"Status"``"Submitted"``"Score"``"Action"``"Grade"``"Back"``"Open Assignment"`
- `src/app/(dashboard)/student/learning/assignments/page.tsx`:硬编码英文 `"Assignments"``"Your homework and practice assignments."``"No assignments"``"Pending"``"Completed"``"Overdue"``"Due"``"Attempts"``"Score"``"Start"``"Continue"``"View"``"Review"`
- `src/modules/proctoring/components/exam-mode-config.tsx`:硬编码中文 `"考试模式"``"模式"``"考试时长(分钟)"``"题目乱序"``"启用防作弊监控"``"允许迟开始"``"迟到宽限时间(分钟)"`
**问题原因**:模块在 v3 i18n 体系建立前已实现,后续未回填翻译键。
**违反规则**:项目规则"所有用户可见文本必须适配 i18n使用 next-intl提取翻译键"。
**直接后果**
- 切换到英文 locale 后作业列表页仍显示中文考试列表页仍显示英文。多角色admin/teacher/parent/student无法获得一致的语言体验。
- 国际化交付阻塞,无法满足 K12 学校多语言场景。
### 2.2 类型安全问题
**问题**:多处使用 `as any` / `as unknown` 断言,违反 TypeScript 严格规范。
**出现位置**
- `src/modules/exams/components/exam-form.tsx:38``resolver: zodResolver(formSchema) as any`(注释 `eslint-disable`)。
- `src/modules/exams/components/exam-form.tsx:163,168``form.handleSubmit(onSubmit as any)`(两处 `eslint-disable`)。
- `src/modules/exams/components/exam-actions.tsx:60``questionById.set(q.id, q as unknown as Question)`
- `src/modules/exams/components/exam-actions.tsx:63``const hydrate = (nodes: any[]): ExamNode[]``eslint-disable`)。
- `src/modules/homework/components/homework-take-view.tsx:346-347``(prev[q.questionId]?.answer as string[])`
- `src/modules/homework/components/homework-take-view.tsx:468``(answersByQuestionId[q.questionId]?.answer as unknown[])`
- `src/modules/homework/components/homework-grading-view.tsx:199``(ans.questionContent.options as ChoiceOption[])`
- `src/modules/homework/data-access.ts:484``structure: assignment.structure as unknown`
**问题原因**zodResolver 与 react-hook-form 类型不兼容时偷懒用 `as any`;题目内容为 `unknown` 时未做类型守卫直接断言。
**违反规则**:项目规则"禁止 `any`"、"禁止 `as` 断言(除非从 `unknown` 转换或测试中,需注释原因)"。
**直接后果**:类型系统形同虚设,运行时错误无法在编译期捕获;重构时易引入隐性 bug。
### 2.3 权限校验不完整
**问题**`gradeHomeworkSubmissionAction` 未校验教师对该提交记录的访问权限。
**出现位置**`src/modules/homework/actions.ts:249-292`
**问题原因**`gradeHomeworkSubmissionAction` 仅调用 `requirePermission(Permissions.HOMEWORK_GRADE)`,未校验当前教师是否为该作业的创建者、或该学生所在班级的任课教师。任意拥有 `HOMEWORK_GRADE` 权限的教师均可批改任意学生的任意作业。
**违反规则**:项目规则"所有敏感数据查询必须在 data-access 层结合当前用户权限过滤Server Action 二次校验"。
**直接后果**:横向越权风险——教师 A 可批改教师 B 的学生作业,篡改成绩。
### 2.4 错误边界与加载状态缺失
**问题**:考试和作业模块的页面缺少 React Error Boundary 和 Suspense 骨架屏。
**出现位置**
- `src/app/(dashboard)/teacher/exams/[id]/build/page.tsx`:无 `error.tsx`、无 `loading.tsx``getExamById` 失败时整页 500。
- `src/app/(dashboard)/teacher/exams/[id]/proctoring/page.tsx`:无 `error.tsx`、无 `loading.tsx`
- `src/app/(dashboard)/teacher/homework/assignments/[id]/page.tsx`:无 `error.tsx`、无 `loading.tsx`
- `src/app/(dashboard)/teacher/homework/assignments/[id]/submissions/page.tsx`:无 `error.tsx`、无 `loading.tsx`
- `src/app/(dashboard)/teacher/homework/assignments/create/page.tsx`:无 `loading.tsx`
- `src/app/(dashboard)/student/learning/assignments/[assignmentId]/page.tsx`:有 `loading.tsx` 但无 `error.tsx`
-`exams/all``exams/create``loading.tsx`
**问题原因**页面开发时未配套错误边界Suspense 仅在 `exams/all` 使用。
**违反规则**:项目规则"每个独立的数据区块必须用 React Error Boundary 包裹"、"异步数据使用 React Suspense + 骨架屏"、"明确处理空数据、无权限、网络异常等边界状态"。
**直接后果**:数据库连接抖动或单条记录缺失会导致整页崩溃,无法降级展示。
### 2.5 组件复用不足
**问题**:题目渲染逻辑在作答页、批改页、复习页三处重复实现。
**出现位置**
- `src/modules/homework/components/homework-take-view.tsx:248-400`:渲染 `single_choice` / `multiple_choice` / `judgment` / `text` 四种题型。
- `src/modules/homework/components/homework-grading-view.tsx:155-328`:再次渲染同样四种题型(带正确答案高亮)。
- `src/modules/homework/components/student-homework-review-view.tsx`:第三次渲染同样四种题型(带批改反馈)。
- 三处都重复实现 `getQuestionText` / `getOptions` / `isRecord` 等工具函数。
**问题原因**:未抽象 `QuestionRenderer` / `QuestionAnswerInput` / `QuestionResultDisplay` 等复用组件。
**违反规则**:项目规则"最大化复用:识别四个角色共用的 UI 块和业务逻辑块,抽象为泛型组件和 hooks"、"组合优先:所有 UI 通过组件组合实现灵活性"。
**直接后果**:题型扩展(如填空、排序、拖拽)需改三处;样式不一致风险高;单测难以覆盖。
### 2.6 监考模块死代码
**问题**`ExamModeConfig` 组件已实现但未集成到考试创建/编辑表单。
**出现位置**`src/modules/proctoring/components/exam-mode-config.tsx`230 行)从未被 import。
**问题原因**:架构图 §2.21 已标注"❌ P0`exam-mode-config.tsx` 未集成到考试表单(死代码,监考功能无法启用)",但至今未修复。
**违反规则**:项目规则"如果架构图未覆盖该模块的任何部分,必须优先补全架构图再继续"——此处架构图已记录但代码未修复。
**直接后果**:监考功能(防作弊、限时、全屏强制)完全不可用;`proctoring` 模块的 `recordProctoringEventAction` 无前端触发路径。
### 2.7 文件行数超限
**问题**`ai-pipeline.ts` 857 行,超过 800 行建议值。
**出现位置**`src/modules/exams/ai-pipeline.ts`
**问题原因**:混合了 AI 请求构造、响应解析、Zod 校验、题目归一化、结构生成 5 类职责。
**违反规则**:项目规则"Server Actions / Data Access 模块:建议 ≤ 800 行"、"超过建议行数时应考虑拆分"。
**直接后果**维护困难AI 供应商切换需改动整个文件。
### 2.8 可访问性缺陷
**问题**:交互元素缺少 ARIA 属性,颜色作为唯一信息载体。
**出现位置**
- `src/modules/homework/components/homework-grading-view.tsx:156-158`:用 `border-l-emerald-500` / `border-l-red-500` 表示对错,无文本替代。
- `src/modules/exams/components/exam-columns.tsx:110-121`:难度仅用色块表示,`text-[10px]` 标签为英文缩写。
- `src/modules/homework/components/homework-take-view.tsx:471-486`:题目导航按钮 `aria-label` 为英文 `Jump to question ${i+1}`,未 i18n。
- 批改页 `Correct`/`Incorrect` 按钮仅靠颜色区分状态。
**违反规则**:项目规则"可访问性a11y语义化标签、ARIA 属性、键盘导航"。
**直接后果**:色盲教师无法区分对错;屏幕阅读器用户体验差。
### 2.9 性能问题
**问题**`getHomeworkSubmissionDetails` 为获取前后导航 ID 拉取全部提交记录。
**出现位置**`src/modules/homework/data-access.ts:540-548`
```typescript
const allSubmissions = await db.query.homeworkSubmissions.findMany({
where: eq(homeworkSubmissions.assignmentId, submission.assignmentId),
orderBy: [desc(homeworkSubmissions.updatedAt)],
columns: { id: true },
})
const currentIndex = allSubmissions.findIndex((s) => s.id === submissionId)
```
**问题原因**:未用 SQL 窗口函数或 `OFFSET`/`LIMIT` 获取相邻记录。
**违反规则**:项目规则"性能:优先使用 React Server Components 获取初始数据"——此处为 data-access 层低效查询。
**直接后果**:班级 50 人作业批改时,每次打开详情都拉取 50 条记录的 ID。
### 2.10 答案保存无防抖与离线支持
**问题**:学生作答时每题手动点击"Save Answer",无自动保存、无离线缓存。
**出现位置**`src/modules/homework/components/homework-take-view.tsx:139-151`
**问题原因**:未实现自动保存(防抖)和 `localStorage` 离线缓存。
**违反规则**:项目规则"明确处理网络异常等边界状态"。
**直接后果**:网络抖动时学生答案丢失;刷新页面(尽管有 `beforeunload` 警告)仍可能丢失未保存答案。
---
## 三、行业差距对比
### 3.1 与主流 K12 考试系统对比
| 功能 | 行业主流如智学网、猿题库、Google Classroom | 当前实现 | 差距影响 |
|------|------|------|------|
| 限时考试 | 支持设定考试时长,到时自动提交 | `ExamModeConfig` 已实现但未集成 | 教师无法组织课堂限时测验 |
| 题目乱序 | 每位学生题目顺序随机 | `ExamModeConfig` 已实现但未集成 | 防作弊能力缺失 |
| 监考模式 | 切屏检测、强制全屏、AI 行为分析 | `proctoring` 模块后端已实现,前端无入口 | 远程考试无法防作弊 |
| 自动批改 | 选择题/判断题提交后即时出分 | `homework-grading-view``applyAutoGrades` 但仅在打开批改页时计算,不回写 | 学生提交后看不到即时成绩 |
| 批量批改 | 列表页勾选多份提交批量打分 | 仅支持逐份批改 | 50 人班级批改效率低 |
| 评分量规Rubric | 文本题按维度打分 | 仅支持单分数 | 主观题批改粗放 |
| 考试分析 | 题目难度、区分度、知识点掌握度 | `homework/stats-service` 有作业分析,考试无分析 | 考试后无法复盘教学质量 |
| 学生答案草稿 | 自动保存 + 离线缓存 | 手动保存,无离线 | 弱网环境答案易丢 |
| 部分分自动判分 | 多选题漏选得部分分 | 全对才得分 | 评分不够精细 |
| 重考与补考 | 支持重考流程与成绩记录 | `maxAttempts` 已支持但无补考入口 | 补考场景需手动创建新作业 |
### 3.2 多角色体验差距
| 角色 | 行业主流体验 | 当前实现 | 差距 |
|------|------|------|------|
| **教师** | 一站式工作台:创建→发布→监考→批改→分析 | 分散在 `/teacher/exams/*``/teacher/homework/*` 两个独立菜单 | 考试到作业的链路割裂 |
| **学生** | 统一"待办"入口:作业+考试+复习 | 仅 `/student/learning/assignments`,考试作答也走作业流程 | 考试与作业概念混淆 |
| **家长** | 查看孩子考试详情、错题本、趋势 | `parent` 模块仅有作业摘要,无考试详情 | 家长无法了解考试表现 |
| **管理员** | 全校考试统计、年级对比、教师工作量 | 无管理员视角的考试仪表盘 | 管理层无法宏观决策 |
### 3.3 UI/UX 差距
- **空状态**`exams/all` 有空状态,但 `homework/assignments/[id]/submissions` 无空状态(无提交时显示空表格)。
- **加载骨架屏**:仅 `exams/all``exams/create``student/learning/assignments` 有;其余页面白屏加载。
- **错误降级**:全模块无 `error.tsx`,任何数据加载失败均导致整页 500。
- **移动端适配**`homework-take-view``homework-grading-view` 使用 `lg:grid-cols-12`,移动端可正常显示但未优化触控体验(题目导航按钮过小)。
---
## 四、改进优先级建议
### P0紧急影响安全与核心功能
1. **补全 `gradeHomeworkSubmissionAction` 权限校验**:在 data-access 层新增 `getHomeworkSubmissionForGrading(submissionId, teacherId, dataScope)`校验教师对该作业的访问权创建者或班级任课教师。Server Action 二次校验。
2. **i18n 全量回填**:新建 `messages/zh-CN/exam-homework.json``messages/en/exam-homework.json`,提取该模块所有硬编码文本为翻译键;在 `i18n/request.ts` 注册新命名空间;组件改用 `useTranslations('examHomework')`
3. **集成 `ExamModeConfig` 到考试表单**:在 `exam-form.tsx` 中引入 `ExamModeConfig`,将 `examMode` / `durationMinutes` / `shuffleQuestions` / `antiCheatEnabled` 等字段纳入 `ExamFormValues`,持久化到 `exams` 表;`proctoring` 模块读取这些配置启用监考。
### P1重要影响可维护性与体验
4. **添加 Error Boundary 与 loading.tsx**:为 `exams/[id]/build``exams/[id]/proctoring``homework/assignments/[id]``homework/assignments/[id]/submissions``homework/assignments/create``student/learning/assignments/[assignmentId]` 配套 `error.tsx` + `loading.tsx`
5. **抽象题目渲染组件**:新建 `homework/components/question-renderer.tsx`,导出 `QuestionRenderer`(只读展示)、`QuestionAnswerInput`(作答交互)、`QuestionGradingPanel`(批改面板),三处页面改用组合模式复用。
6. **清理类型断言**`exam-form.tsx``as any` 改为正确泛型;`exam-actions.tsx``hydrate` 函数用类型守卫替代 `any[]``homework-take-view.tsx` / `homework-grading-view.tsx``as` 断言改为类型守卫。
7. **拆分 `ai-pipeline.ts`**:按职责拆为 `ai-pipeline/request.ts`(请求构造)、`ai-pipeline/parse.ts`(响应解析+校验)、`ai-pipeline/structure.ts`(结构生成),原文件作为 re-export 入口。
8. **优化 `getHomeworkSubmissionDetails` 相邻记录查询**:用 `LEAD`/`LAG` 窗口函数或两次 `LIMIT 1` 查询替代全量拉取。
### P2增强提升体验与可扩展性
9. **学生答案自动保存 + 离线缓存**`homework-take-view` 增加 `useDebouncedAutoSave` hook答案变更后 3 秒自动保存;同时写入 `localStorage`,断网时队列化重试。
10. **考试分析仪表盘**:新增 `exams/components/exam-analytics-dashboard.tsx`,复用 `homework/stats-service` 模式,展示题目难度、区分度、知识点掌握度。
11. **批量批改 UI**`homework/assignments/[id]/submissions` 增加多选 + 批量打分(全对/全错/自定义分数)。
12. **a11y 修复**:颜色指示器增加文本替代;题目导航按钮 `aria-label` i18n批改页 `Correct`/`Incorrect` 按钮增加 `aria-pressed`
13. **配置驱动的角色渲染**:定义 `ExamHomeworkRoleConfig` 接口,各角色模块仅组合复用单元,新增角色只改配置。
---
## 五、架构图同步说明
本次审计发现架构图需补充以下信息:
### 5.1 需补充的节点
1. **`004_architecture_impact_map.md` §2.2 exams 模块**
- 文件清单补充 `components/assembly/exam-paper-preview.tsx``question-bank-list.tsx``selected-question-list.tsx``structure-editor.tsx` 四个组件的行数与职责。
- 已知问题补充:`exam-mode-config.tsx` 未集成(与 proctoring 模块联动缺失)。
2. **`004_architecture_impact_map.md` §2.3 homework 模块**
- 文件清单补充 `components/homework-assignment-exam-content-card.tsx``homework-assignment-exam-error-explorer.tsx``homework-assignment-exam-error-explorer-lazy.tsx``homework-assignment-exam-preview-pane.tsx``homework-assignment-question-error-detail-panel.tsx``homework-assignment-question-error-overview-card.tsx``student-homework-review-view.tsx` 七个组件的行数与职责。
- 已知问题补充:`gradeHomeworkSubmissionAction` 权限校验不完整P0 安全问题)。
3. **`004_architecture_impact_map.md` §2.21 proctoring 模块**
- 已知问题补充:`ExamModeConfig` 未集成的根因是 `ExamFormValues` 未包含 `examMode` 字段,需扩展表单 schema。
4. **`005_architecture_data.json`**
- `modules.exams.exports` 补充 `ExamModeConfig` 集成状态字段。
- `modules.homework.knownIssues` 新增 `gradeHomeworkPermissionGap` 节点。
- `dependencyMatrix` 补充 `proctoring → exams``examModeConfig` 依赖关系(当前仅记录 data-access 依赖,未记录 UI 集成依赖)。
### 5.2 无需修改的部分
- 三层架构依赖关系记录准确(`app → modules → shared`)。
- 跨模块 data-access 调用关系记录完整exams ↔ homework ↔ proctoring
- 文件行数统计基本准确(`ai-pipeline.ts` 857 行已记录)。
---
## 六、重构方案设计(概要)
### 6.1 完全解耦
定义 `ExamHomeworkServicePort` 接口,抽象数据依赖:
```typescript
// modules/exam-homework/types/service-port.ts
export interface ExamHomeworkServicePort {
getExams(scope: DataScope): Promise<ExamListItem[]>
getExamById(id: string): Promise<ExamDetail | null>
getHomeworkAssignments(scope: DataScope): Promise<HomeworkAssignmentListItem[]>
getStudentHomeworkTakeData(assignmentId: string, studentId: string): Promise<StudentHomeworkTakeData | null>
// ... 其余数据访问方法
}
export interface ExamHomeworkPermissionPort {
canGradeSubmission(teacherId: string, submissionId: string): Promise<boolean>
canViewExam(userId: string, examId: string, scope: DataScope): Promise<boolean>
}
```
通过 `ExamHomeworkServiceProvider`React Context注入实现模块内部组件绝不 import 其他业务模块的 actions。
### 6.2 组合优先
抽象题目渲染组件:
```typescript
// modules/exam-homework/components/question-renderer.tsx
export function QuestionRenderer({
question,
mode,
children,
}: {
question: QuestionData
mode: 'take' | 'grade' | 'review'
children?: React.ReactNode
}) { ... }
export function QuestionAnswerInput({ question, value, onChange, disabled }: TakeProps) { ... }
export function QuestionGradingPanel({ answer, onScoreChange, onFeedbackChange }: GradeProps) { ... }
```
### 6.3 国际化就绪
翻译文件结构示例:
```json
// messages/zh-CN/exam-homework.json
{
"exam": {
"list": { "title": "考试列表", "create": "创建考试", "empty": "暂无考试" },
"form": { "title": "考试标题", "subject": "科目", "grade": "年级", "difficulty": "难度" },
"status": { "draft": "草稿", "published": "已发布", "archived": "已归档" },
"actions": { "preview": "预览", "edit": "编辑", "build": "组卷", "publish": "发布", "duplicate": "复制", "delete": "删除" }
},
"homework": {
"list": { "title": "作业列表", "create": "创建作业", "submissionRate": "提交率", "averageScore": "平均分", "overdue": "逾期" },
"take": { "start": "开始作答", "submit": "提交作业", "saveAnswer": "保存答案", "confirmSubmit": "确认提交", "unansweredWarning": "您有 {{count}} 道题未作答" },
"grade": { "summary": "批改摘要", "totalScore": "总分", "correct": "正确", "incorrect": "错误", "partial": "部分正确", "submitGrades": "提交成绩" }
},
"proctoring": {
"mode": { "homework": "作业模式", "timed": "限时模式", "proctored": "监考模式" },
"config": { "duration": "考试时长(分钟)", "shuffleQuestions": "题目乱序", "antiCheat": "启用防作弊监控" }
}
}
```
### 6.4 错误与边界处理
- 每个页面配套 `error.tsx`React Error Boundary
- 每个页面配套 `loading.tsx`(骨架屏)。
- `ExamHomeworkErrorBoundary` 组件区分 `NetworkError` / `PermissionDenied` / `NotFound` 三种状态。
### 6.5 可测试性
- 纯逻辑函数(`applyAutoGrades` / `computeIsCorrect` / `normalizeStructure`)已与 UI 分离,补充单测。
- 数据获取逻辑通过 `ServicePort` 接口可 mock。
- 新增 `__tests__/exam-homework-service.test.ts` 覆盖权限校验与数据流转。
### 6.6 可扩展性
配置驱动设计:
```typescript
// modules/exam-homework/config/role-config.ts
export const EXAM_HOMEWORK_ROLE_CONFIG: Record<Role, ExamHomeworkRoleConfig> = {
admin: { widgets: ['stats', 'all-exams', 'all-homework'], canGrade: false },
teacher: { widgets: ['my-exams', 'my-homework', 'grading-queue'], canGrade: true },
parent: { widgets: ['child-exam-results', 'child-homework-summary'], canGrade: false },
student: { widgets: ['pending-exams', 'pending-homework', 'results'], canGrade: false },
}
```
### 6.7 企业级补充
- **a11y**:颜色指示器增加 `sr-only` 文本;`aria-pressed` / `aria-label` 全覆盖。
- **性能**RSC 获取初始数据(已实现);客户端组件仅负责交互(已实现);流式渲染(`Suspense` 已部分使用)。
- **安全**data-access 层结合 `dataScope` 过滤已实现Server Action 二次校验P0 待补全)。
- **监控**:预留 `trackExamEvent(eventName, payload)` 接口,关键操作(创建/提交/批改)埋点。