# 考试模块实现设计文档 ## 1. 概述 考试模块提供了一个完整的评估管理生命周期,使教师能够创建考试、组卷(支持嵌套分组)、发布评估以及对学生的提交进行评分。 ## 2. 数据架构 ### 2.1 核心实体 - **Exams**: 根实体,包含元数据(标题、时间安排)和结构信息。 - **ExamQuestions**: 关系链接,用于查询题目的使用情况(扁平化表示)。 - **ExamSubmissions**: 学生的考试尝试记录。 - **SubmissionAnswers**: 链接到特定题目的单个答案。 ### 2.2 `structure` 字段 为了支持层级布局(如章节/分组),我们在 `exams` 表中引入了一个 JSON 列 `structure`。这作为考试布局的“单一事实来源”(Source of Truth)。 **JSON Schema:** ```typescript type ExamNode = { id: string; // 节点的唯一 UUID type: 'group' | 'question'; title?: string; // 'group' 类型必填 questionId?: string; // 'question' 类型必填 score?: number; // 在此考试上下文中的分值 children?: ExamNode[]; // 'group' 类型的递归子节点 } ``` ## 3. 组件架构 ### 3.1 组卷(构建器) 位于 `/teacher/exams/[id]/build`。 - **`ExamAssembly` (客户端组件)** - 管理 `structure` 状态树。 - 处理“添加题目”、“添加章节”、“移除”和“重新排序”操作。 - 实时计算总分和进度。 - **`StructureEditor` (客户端组件)** - 基于 `@dnd-kit` 构建。 - 提供嵌套的可排序(Sortable)界面。 - 支持在组内/组间拖拽题目(当前优化为 2 层深度)。 - **`QuestionBankList`** - 可搜索/筛选的可用题目列表。 - “添加”操作将节点追加到结构树中。 ### 3.2 阅卷界面 位于 `/teacher/exams/grading/[submissionId]`。 - **`GradingView` (客户端组件)** - **左侧面板**: 只读视图,显示学生的答案与题目内容。 - **右侧面板**: 评分和反馈的输入字段。 - **状态**: 在提交前管理本地更改。 - **Actions**: `gradeSubmissionAction` 更新 `submissionAnswers` 并将总分聚合到 `examSubmissions`。 ## 4. 关键工作流 ### 4.1 创建与构建考试 1. **创建**: 教师输入基本信息(标题、科目)。数据库创建记录(草稿状态)。 2. **构建**: - 教师打开“构建”页面。 - 服务器从数据库 Hydrate(注水)`initialStructure`。 - 教师从题库拖拽题目到结构树。 - 教师创建章节(分组)。 - **保存**: 同时提交 `questionsJson`(扁平化,用于索引)和 `structureJson`(树状,用于布局)到 `updateExamAction`。 3. **发布**: 状态变更为 `published`。 ### 4.2 阅卷流程 1. **列表**: 教师查看 `submission-data-table`。 2. **评分**: 打开特定提交。 3. **审查**: 遍历题目。 - 系统显示学生答案。 - 教师输入分数(上限为满分)和反馈。 4. **提交**: 服务器更新单个答案记录并重新计算提交总分。 ## 5. 技术决策 ### 5.1 混合存储策略 我们在存储考试题目时采用了 **混合方法**: - **关系型 (`exam_questions`)**: 用于“查找所有使用题目 X 的考试”查询和外键约束。 - **文档型 (`exams.structure`)**: 用于渲染嵌套 UI 和保留任意排序/分组。 *理由*: 这结合了 SQL 的完整性和 NoSQL 在 UI 布局上的灵活性。 ### 5.2 拖拽功能 使用 `@dnd-kit` 代替旧库,因为: - 更好的无障碍支持(键盘支持)。 - 模块化架构(Sensors, Modifiers)。 - 面向未来(现代 React Hooks 模式)。 ### 5.3 Server Actions 所有变更操作(保存草稿、发布、评分)均使用 Next.js Server Actions,以确保类型安全并自动重新验证缓存。