> ⚠️ **已归档文档**
> 本文档记录的是 2025-12-31 作业模块的实现设计。
> 当前实现已演进，最新架构与组件清单详见 [004 架构影响地图](../architecture/004_architecture_impact_map.md) 的 homework 模块章节。
> 保留用于历史参考，不再维护。

---

# 作业模块实现设计文档（Homework Module）

**日期**: 2025-12-31
**模块**: Homework (`src/modules/homework`)

---

## 1. 概述

作业模块提供“由试卷派发作业”的完整生命周期：

- 教师从已存在的 Exam 派发 Homework Assignment（冻结当时的结构与题目引用）
- 指定作业目标学生（Targets）
- 学生开始一次作答（Submission），保存答案（Answers），并最终提交
- 教师在提交列表中查看并批改（按题给分/反馈，汇总总分）

核心目标是：在不破坏 Exam 本体数据的前提下，为作业提供可追溯、可批改、可统计的独立域模型。

**说明（合并调整）**：教师端“阅卷/批改”统一通过 Homework submissions 完成，`/teacher/exams/grading*` 相关路由已重定向到 `/teacher/homework/submissions`。

---

## 2. 数据架构

### 2.1 核心实体

- `homework_assignments`: 作业实例（从 exam 派生）
- `homework_assignment_questions`: 作业与题目关系（score/order）
- `homework_assignment_targets`: 作业目标学生列表
- `homework_submissions`: 学生作业尝试（attempt_no/status/时间/是否迟交）
- `homework_answers`: 每题答案（answer_content/score/feedback）

数据库变更记录见：[schema-changelog.md](file:///e:/Desktop/CICD/docs/db/schema-changelog.md#L34-L77)

### 2.2 设计要点：冻结 Exam → Homework Assignment

- `homework_assignments.source_exam_id` 保存来源 Exam
- `homework_assignments.structure` 在 publish 时复制 `exams.structure`（冻结当时的呈现结构）
- 题目关联使用 `homework_assignment_questions`（仍引用 `questions` 表，作业侧记录分值与顺序）

---

## 3. 路由与页面

### 3.1 教师端

- `/teacher/homework/assignments`: 作业列表  
  实现：[assignments/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/assignments/page.tsx)
- `/teacher/homework/assignments/create`: 从 Exam 派发作业  
  实现：[create/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/assignments/create/page.tsx)
- `/teacher/homework/assignments/[id]`: 作业详情  
  实现：[[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/assignments/%5Bid%5D/page.tsx)
- `/teacher/homework/assignments/[id]/submissions`: 作业提交列表（按作业筛选）  
  实现：[[id]/submissions/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/assignments/%5Bid%5D/submissions/page.tsx)
- `/teacher/homework/submissions`: 全部提交列表  
  实现：[submissions/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/submissions/page.tsx)
- `/teacher/homework/submissions/[submissionId]`: 批改页  
  实现：[[submissionId]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/submissions/%5BsubmissionId%5D/page.tsx)

关联重定向：

- `/teacher/exams/grading` → `/teacher/homework/submissions`  
  实现：[grading/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/exams/grading/page.tsx)
- `/teacher/exams/grading/[submissionId]` → `/teacher/homework/submissions`  
  实现：[grading/[submissionId]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/exams/grading/%5BsubmissionId%5D/page.tsx)

### 3.2 学生端

- `/student/learning/assignments`: 作业列表  
  实现：[page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/assignments/page.tsx)
- `/student/learning/assignments/[assignmentId]`: 作答页（开始/保存/提交）  
  实现：[[assignmentId]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/assignments/%5BassignmentId%5D/page.tsx)

---

## 4. 数据访问层（Data Access）

数据访问位于：[data-access.ts](file:///e:/Desktop/CICD/src/modules/homework/data-access.ts)

### 4.1 教师侧查询

- `getHomeworkAssignments`：作业列表（可按 creatorId/ids）
- `getHomeworkAssignmentById`：作业详情（含目标人数、提交数统计）
- `getHomeworkSubmissions`：提交列表（可按 assignmentId/classId/creatorId）
- `getHomeworkSubmissionDetails`：提交详情（题目内容 + 学生答案 + 分值/顺序）

### 4.2 学生侧查询

- `getStudentHomeworkAssignments(studentId)`：只返回“已派发给该学生、已发布、且到达 availableAt”的作业
- `getStudentHomeworkTakeData(assignmentId, studentId)`：进入作答页所需数据（assignment + 当前/最近 submission + 题目列表 + 已保存答案）

### 4.3 开发模式用户选择（Demo）

为了在未接入真实 Auth 的情况下可演示学生端页面，提供：

- `getDemoStudentUser()`：优先选取最早创建的 student；若无 student，则退化到任意用户

---

## 5. Server Actions

实现位于：[actions.ts](file:///e:/Desktop/CICD/src/modules/homework/actions.ts)

### 5.1 教师侧

- `createHomeworkAssignmentAction`：从 exam 创建 assignment；可写入 targets；可选择 publish（默认 true）
- `gradeHomeworkSubmissionAction`：按题写入 score/feedback，并汇总写入 submission.score 与 status=graded

### 5.2 学生侧

- `startHomeworkSubmissionAction`：创建一次 submission（attemptNo + startedAt），并校验：
  - assignment 已发布
  - student 在 targets 中
  - availableAt 已到
  - 未超过 maxAttempts
- `saveHomeworkAnswerAction`：保存/更新某题答案（upsert 到 homework_answers）
- `submitHomeworkAction`：提交作业（校验 dueAt/lateDueAt/allowLate，写入 submittedAt/isLate/status=submitted）

---

## 6. UI 组件

### 6.1 教师批改视图

- [HomeworkGradingView](file:///e:/Desktop/CICD/src/modules/homework/components/homework-grading-view.tsx)
  - 左侧：学生答案只读展示
  - 右侧：按题录入分数与反馈，并提交批改

### 6.2 学生作答视图

- [HomeworkTakeView](file:///e:/Desktop/CICD/src/modules/homework/components/homework-take-view.tsx)
  - Start：开始一次作答
  - Save：按题保存
  - Submit：提交（提交前会先保存当前题目答案）
  - 题型支持：`text` / `judgment` / `single_choice` / `multiple_choice`

题目 content 约定与题库一致：`{ text, options?: [{ id, text, isCorrect? }] }`（作答页仅消费 `id/text`）。

---

## 7. 类型定义

类型位于：[types.ts](file:///e:/Desktop/CICD/src/modules/homework/types.ts)

- 教师侧：`HomeworkAssignmentListItem` / `HomeworkSubmissionDetails` 等
- 学生侧：`StudentHomeworkAssignmentListItem` / `StudentHomeworkTakeData` 等

---

## 8. 校验

- `npm run typecheck`: 通过
- `npm run lint`: 0 errors（仓库其他位置存在 warnings，与本模块新增功能无直接关联）

---

## 9. 部署与环境变量（CI/CD）

### 9.1 本地开发

- 本地开发使用项目根目录的 `.env` 提供 `DATABASE_URL`
- `.env` 仅用于本机开发，不应写入真实生产库凭据

### 9.2 CI 构建与部署（Gitea）

工作流位于：[ci.yml](file:///e:/Desktop/CICD/.gitea/workflows/ci.yml)

- 构建阶段（`npm run build`）不依赖数据库连接：作业相关页面在构建时不会静态预渲染执行查库
- 部署阶段通过 `docker run -e DATABASE_URL=...` 在运行时注入数据库连接串
- 需要在 Gitea 仓库 Secrets 配置 `DATABASE_URL`（生产环境 MySQL 连接串）
- CI 中关闭 Next.js telemetry：设置 `NEXT_TELEMETRY_DISABLED=1`

### 9.3 Next.js 渲染策略（避免 build 阶段查库）

作业模块相关页面在渲染时会进行数据库查询，因此显式标记为动态渲染以避免构建期预渲染触发数据库连接：

- 教师端作业列表：[assignments/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/assignments/page.tsx)

---

## 10. 实现更新（2026-01-05）

### 10.1 教师端作业详情页组件化（按 Vertical Slice 拆分）

将 `/teacher/homework/assignments/[id]` 页面调整为“只负责组装”，把可复用展示逻辑下沉到模块内组件：

- 页面组装：[page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/homework/assignments/%5Bid%5D/page.tsx)
- 题目错误概览卡片（overview）：[homework-assignment-question-error-overview-card.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-question-error-overview-card.tsx)
- 题目错误明细卡片（details）：[homework-assignment-question-error-details-card.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-question-error-details-card.tsx)
- 试卷预览/错题工作台容器卡片：[homework-assignment-exam-content-card.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-exam-content-card.tsx)

### 10.2 题目点击联动：试卷预览 ↔ 错题详情

在“试卷预览”中点击题目后，右侧联动展示该题的统计与错答列表（按学生逐条展示，不做合并）：

- 工作台（选择题目、拼装左右面板）：[homework-assignment-exam-error-explorer.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-exam-error-explorer.tsx)
- 试卷预览面板（可选中题目）：[homework-assignment-exam-preview-pane.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-exam-preview-pane.tsx)
- 错题详情面板（错误人数/错误率/错答列表）：[homework-assignment-question-error-detail-panel.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-question-error-detail-panel.tsx)

### 10.3 统计数据增强：返回逐学生错答

为满足“错答列表逐条展示学生姓名 + 答案”的需求，作业统计查询返回每题的错答明细（包含学生信息）：

- 数据访问：[getHomeworkAssignmentAnalytics](file:///e:/Desktop/CICD/src/modules/homework/data-access.ts)
- 类型定义：[types.ts](file:///e:/Desktop/CICD/src/modules/homework/types.ts)

### 10.4 加载优化：Client Wrapper 动态分包

由于 `next/dynamic({ ssr: false })` 不能在 Server Component 内使用，工作台动态加载通过 Client wrapper 进行隔离：

- Client wrapper：[homework-assignment-exam-error-explorer-lazy.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-exam-error-explorer-lazy.tsx)
- 入口卡片（Server Component，渲染 wrapper）：[homework-assignment-exam-content-card.tsx](file:///e:/Desktop/CICD/src/modules/homework/components/homework-assignment-exam-content-card.tsx)

### 10.5 校验

- `npm run lint`: 通过
- `npm run typecheck`: 通过
- `npm run build`: 通过

---

## 11. 学生成绩图表与排名（2026-01-06）

### 11.1 目标

在学生主页（Dashboard）展示：

- 最近已批改作业的成绩趋势（百分比折线）
- 最近若干次已批改作业明细（标题、得分、时间）
- 班级排名（基于班级内作业总体得分百分比）

### 11.2 数据访问与计算口径

数据由 Homework 模块统一提供聚合查询，避免页面层拼 SQL：

- 新增查询：[getStudentDashboardGrades](file:///e:/Desktop/CICD/src/modules/homework/data-access.ts)
  - `trend`：取该学生所有 `graded` 提交中“每个 assignment 最新一次”的集合，按时间升序取最近 10 个
  - `recent`：对 `trend` 再按时间降序取最近 5 条，用于表格展示
  - `maxScore`：通过 `homework_assignment_questions` 汇总每个 assignment 的总分（SUM(score)）
  - `percentage`：`score / maxScore * 100`
  - `ranking`：
    - 班级选择：取该学生最早创建的一条 active enrollment 作为当前班级
    - 班级作业集合：班级内所有学生的 targets 合并得到 assignment 集合
    - 计分口径：班级内“每个学生 × 每个 assignment”取最新一次 graded 提交，累加得分与满分，得到总体百分比
    - 排名：按总体百分比降序排序（百分比相同按 studentId 作为稳定排序因子）

### 11.3 类型定义

为 Dashboard 聚合数据提供显式类型：

- [types.ts](file:///e:/Desktop/CICD/src/modules/homework/types.ts)
  - `StudentHomeworkScoreAnalytics`
  - `StudentRanking`
  - `StudentDashboardGradeProps`

### 11.4 页面与组件接入

- 学生主页页面负责“取数 + 计算基础计数 + 传参”：
  - [student/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/dashboard/page.tsx)
  - 取数：`getStudentDashboardGrades(student.id)`
  - 传入：`<StudentDashboard grades={grades} />`
- 展示组件负责渲染卡片：
  - [student-view.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/student-view.tsx)
  - 趋势图：使用内联 `svg polyline` 渲染折线，避免引入额外图表依赖

### 11.5 校验

- `npm run lint`: 通过
- `npm run typecheck`: 通过
- `npm run build`: 通过

---

## 12. UI/UX 优化更新（2026-01-12）

### 12.1 教师端作业列表 (`/teacher/homework/assignments`)

- **表格重构**: 从简单的卡片列表升级为功能丰富的数据表格（Table）。
- **信息增强**: 合并展示标题/时间，使用 Badge 区分状态，清晰展示截止日期（含 Late 标记），可视化提交进度。
- **操作便捷**: 每行增加操作菜单（Actions），支持快速跳转详情或提交列表。

### 12.2 作业详情页 (`/teacher/homework/assignments/[id]`)

- **布局重构**:
  - **Sticky Header**: 头部信息栏（标题、状态、面包屑）随滚动吸顶，但后续优化为随页面滚动（移除 Sticky）以节省空间。
  - **关键指标**: 将截止日期、目标数、提交数、已批改数整合到头部下方，使用图标增强可读性。
  - **双栏布局**: 主体内容分为“Performance Analytics”（分析）和“Assignment Content”（内容）两部分。
- **图表升级**:
  - 重构 `HomeworkAssignmentQuestionErrorOverviewCard`，废弃 SVG，改用 **Recharts** 实现柱状图（BarChart）。
  - 增强交互：支持 Tooltip 悬停查看具体题目错误率和人数。
- **详情面板优化**:
  - 移除了冗余的 `HomeworkAssignmentQuestionErrorDetailsCard`。
  - 深度优化 `HomeworkAssignmentQuestionErrorDetailPanel`:
    - 增加饼图展示单题错误率。
    - 错误答案列表卡片化，清晰展示每个错误答案的内容及选择人数。
    - 整合预览面板与详情面板，提供更连贯的“左侧选题-右侧分析”体验。

---

## 13. Bug 修复与完善 (2026-01-13)

### 13.1 批改视图 (Grading View)
- **Type Safety Fix**: 修复了 `HomeworkGradingView` 组件中的 TypeScript 类型错误。
    - 问题：`isCorrect` 字段可能为 `boolean | null`，直接用于 JSX 渲染导致 "Type 'unknown' is not assignable to type 'ReactNode'" 错误。
    - 修复：增加显式布尔值检查 `opt.isCorrect === true`，确保 React 条件渲染接收到合法的 boolean 值。

---

## 14. 实现更新（2026-03-03）

### 14.1 学生作业状态修复
- 提交作业与学生列表查询改为使用真实登录用户，避免提交后仍显示未开始。
- 学生列表优先展示最近一次已提交/已评分记录，提升状态准确性。
- 主要修改：
  - [actions.ts](file:///e:/Desktop/CICD/src/modules/homework/actions.ts)
  - [data-access.ts](file:///e:/Desktop/CICD/src/modules/homework/data-access.ts)

### 14.2 校验
- `npm run lint`：通过
- `npm run typecheck`：通过

