Files
CICD/docs/design/006_homework_module_implementation.md
SpecialX 57807def37
Some checks failed
CI / build-and-test (push) Failing after 3m50s
CI / deploy (push) Has been skipped
完整性更新
现在已经实现了大部分基础功能
2026-01-08 11:14:03 +08:00

13 KiB
Raw Permalink Blame History

作业模块实现设计文档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

2.2 设计要点:冻结 Exam → Homework Assignment

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

3. 路由与页面

3.1 教师端

关联重定向:

3.2 学生端

  • /student/learning/assignments: 作业列表
    实现:page.tsx
  • /student/learning/assignments/[assignmentId]: 作答页(开始/保存/提交)
    实现:[assignmentId]/page.tsx

4. 数据访问层Data Access

数据访问位于: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

5.1 教师侧

  • createHomeworkAssignmentAction:从 exam 创建 assignment可写入 targets可选择 publish默认 true
  • gradeHomeworkSubmissionAction:按题写入 score/feedback并汇总写入 submission.score 与 status=graded

5.2 学生侧

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

6. UI 组件

6.1 教师批改视图

  • HomeworkGradingView
    • 左侧:学生答案只读展示
    • 右侧:按题录入分数与反馈,并提交批改

6.2 学生作答视图

  • HomeworkTakeView
    • Start开始一次作答
    • Save按题保存
    • Submit提交提交前会先保存当前题目答案
    • 题型支持:text / judgment / single_choice / multiple_choice

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


7. 类型定义

类型位于: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

  • 构建阶段(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 阶段查库)

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


10. 实现更新2026-01-05

10.1 教师端作业详情页组件化(按 Vertical Slice 拆分)

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

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

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

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

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

10.4 加载优化Client Wrapper 动态分包

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

10.5 校验

  • npm run lint: 通过
  • npm run typecheck: 通过
  • npm run build: 通过

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

11.1 目标

在学生主页Dashboard展示

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

11.2 数据访问与计算口径

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

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

11.3 类型定义

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

  • types.ts
    • StudentHomeworkScoreAnalytics
    • StudentRanking
    • StudentDashboardGradeProps

11.4 页面与组件接入

  • 学生主页页面负责“取数 + 计算基础计数 + 传参”:
  • 展示组件负责渲染卡片:
    • student-view.tsx
    • 趋势图:使用内联 svg polyline 渲染折线,避免引入额外图表依赖

11.5 校验

  • npm run lint: 通过
  • npm run typecheck: 通过
  • npm run build: 通过