gwy-exam/docs/checks/task-08.md
2026-09-01 16:47:25 +08:00

100 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 任务 08 检查记录:刷题与结果闭环
日期2026-09-01
## 交付内容
### 后端Handler + Schema + Routes
- `server/src/handlers/practice.ts`(重写核心闭环)
- `modules`:返回五大模块与题量(保留)。
- `start`:支持**两种组卷**——
- 普通组卷:按模块 + `duration`5/10/15 分钟)经 `DURATION_TO_COUNT` 换算题量随机抽取;题库不足时返回全部并打乱(实测数量关系仅题 4total=4 而非 5
- **自定义组卷**`body.custom` 指定题目 ID 列表(去重、校验存在、上限 20 题、返回首题模块),`mode='custom'`
- `currentQuestion`:按 `session.recordIndex + 1` 推进取当前题;已答完返回 `{ question: null }`**不返回答案与解析**;新增返回 `answered` / `durationMinutes`
- `answer`:写入作答记录 + 递增 `recordIndex`。**幂等防重复**(同 `sessionId+questionId` 已存在返回 `accepted:false, duplicate:true`,不重复计入);**防乱序**(非当前题返回 409「题目与会话作答顺序不符」返回 `correct` / `correctAnswer` / `analysis` 供单题即时反馈。
- `finish`:按已作答题计分(未作答不计入正确率),返回 `total/answered/correctCount/accuracy/durationSeconds/wrongQuestionIds/questions[]`;标记会话 `finished` 并写 `endedAt`
- `wrongReasons`(新增):最近 30 天错因聚合计数 + 占比,供刷题中心「错因分布」卡片。
- `server/src/schemas/entities.ts``PracticeSessionSchema` 新增 `recordIndex`(默认 -1
- `server/src/schemas/api.ts``PracticeStartBodySchema` 新增可选 `custom``PracticeSessionViewSchema` 新增 `total``PracticeQuestionResponseSchema``question` 改为 `.nullable()` 并新增 `answered/durationMinutes``PracticeAnswerResponseSchema` 新增 `correct/correctAnswer/analysis``PracticeFinishResponseSchema` 新增 `answered`;新增 `PracticeWrongReasonsResponseSchema`
- `server/src/routes.ts`:新增 `GET /api/practice/wrong-reasons`(刷题 tag路由数 35→36。
### 前端View + API
- `client/src/api/index.ts`:新增类型 `PracticeSessionStart/PracticeQuestion/PracticeAnswer/PracticeFinish/PracticeWrongReasons``practiceApi` 新增 `wrongReasons()`
- `client/src/views/practice/PracticeView.vue`(重写,原为 ComingSoon刷题中心含「刷题 / 错题本 / 申论」三 tab按路由高亮
- **刷题 tab对照原型**选择模块5 卡 + 掌握度 + 选中勾、碎片组卷时长5/10/15、开始刷题大按钮含模块+时长文案)、错因分布(最近 30 天横向条)、待复习错题、推荐动作。
- **错题本 tab**:对接 `reviewApi.list({status:'pending'})`,无数据空态;完整间隔复习属任务 09。
- **申论 tab**ComingSoon 占位。
- `client/src/views/practice/AnswerView.vue`(新增):作答 + 单题反馈 + 整卷结果。
- 作答态:顶部返回 + `第X题/共Y题` 进度条 + 计时 + 关闭;模块/子模块/难度标签题干A-D 选项;提交答案。
- 单题结果态:答对/答错横幅(含正确答案 + 「已自动收录到错题本」、解析卡、AI 讲解按钮、标记错因(可多选,本地选择)、下一题 / 查看本卷结果。
- 整卷结果态:答对题数、正确率、用时、错题回顾(你的答案 → 正确答案 + 解析)、返回刷题中心。
- `secondsUsed` 按单题计时(`elapsed - questionStartElapsed`)而非会话累计。
- `client/src/views/practice/CustomView.vue`(新增):自定义组卷——从题库勾选题目(上限 20、已选计数、全选底部开始练习。
- `client/src/components/base/icons.ts`:新增 `plus/sparkles/refresh/book-open` 图标。
- `client/src/components/base/AppButton.vue`:新增 `loading` propspinner
- `client/src/layouts/MobileLayout.vue``/practice` 相关路径隐藏顶栏标题(页面自带 AppPageHeader避免标题重复
- `client/src/router/index.ts`:新增 `/practice/session/:id`(作答)、`/practice/custom`(自定义组卷)。
## 与原型图对照
- 「桌面-刷题中心-刷题态.png」选择模块、碎片组卷时长、开始刷题大按钮、错因分布最近30天、待复习错题、推荐动作 —— 结构一致。
- 「移动-刷题-行测Tab.png」模块卡 + 时长 + 开始刷题按钮 + 底部 TabBar —— 一致(含选中勾选态)。
- 「移动-刷题-作答页.png」进度条 + 计时 + 模块标签 + 题干 + 选项 + 提交答案 —— 一致。
- 「移动-刷题-结果页.png」回答错误横幅正确答案 + 已收录错题本、解析、AI 讲一讲、标记错因(可多选)、下一题 —— 一致。
## 接口实测curl演示数据
| 接口 | 结果 |
|---|---|
| `GET /api/practice/modules` | 5 模块题量 `{言语理解4, 数量关系4, 判断推理4, 资料分析4, 常识判断2}` |
| `POST /api/practice/start`(数量关系 5 分钟) | 返回 sessionId / questionIds / total=4题库不足 5 取全部)/ startedAt / status |
| `POST /api/practice/start`custom 3 题) | 「语用理解 demo-q-001/002/003」total=3mode=custom |
| `POST /api/practice/start`custom 含不存在题) | 409 `CONFLICT`「存在不存在的题目nope-1」 |
| `GET /api/practice/:id/question` | 返回当前题(无答案/解析)+ index/total/answered/durationMinutes |
| `POST /api/practice/:id/answer`(正确) | `{accepted:true, answeredCount:1, correct:true, correctAnswer, analysis}` |
| `POST /api/practice/:id/answer`(重复提交同题) | `{accepted:false, duplicate:true}`,不重复计入 |
| `POST /api/practice/:id/answer`(乱序提交非当前题) | 409 `CONFLICT`:「题目与会话作答顺序不符」 |
| `GET /api/practice/:id/question`(答完全部) | `{question:null, index, total, answered}` |
| `POST /api/practice/:id/finish` | `{total, answered, correctCount, accuracy, durationSeconds, wrongQuestionIds, questions[]}`(部分答错正确率 25%,错题列表正确) |
| `GET /api/practice/wrong-reasons` | `{reasons:[{reason,count,percent}]}` 按错因聚合(演示数据各错因 1 条 14% |
| `GET /api/practice/wrong-reasons`(无数据) | `{reasons:[]}` |
## 浏览器检查agent-browser
| 场景 | 结果 |
|---|---|
| 桌面 1440 刷题中心 | 页头按钮 + 3 tab + 选择模块 + 时长 + 开始大按钮 + 错因分布 + 待复习 + 推荐动作,与原型结构对齐 |
| 桌面 1440 作答页 | 进度条 + 计时 + 模块/子模块/难度标签 + 题干 + 选项 + 提交答案,渲染正常 |
| 桌面 1440 单题结果态(答错) | 正确答案绿勾 / 错误答案红显 / 红色「回答错误」横幅(正确答案 + 已收录错题本) / 解析 / AI 讲一讲 / 标记错因(可多选) / 下一题 —— 对齐原型结果页 |
| 桌面 1440 整卷结果页 | 答对 1/4、正确率 25%、用时、错题回顾(答案对比 + 解析)、返回刷题中心 |
| 桌面 1440 自定义组卷 | 已选计数 / 全选 / 题目列表(标签 + 题干)/ 开始练习,勾选交互正常 |
| 移动 390 刷题中心 | 标题单次显示(修 topbar 重复)、模块 2 列卡片、时长、开始大按钮、底部 TabBar无横向溢出 |
| 移动 390 作答页 | 顶部进度 + 计时、题干、选项、提交答案、底部 TabBar无遮挡对齐原型 |
| 计时修正 | `secondsUsed` 改为单题耗时(修正前误用会话累计时间导致结果页「用时 7 分 24 秒」不合理) |
截图:`deliverables/checks/task-08-practice-desktop.png``task-08-practice-desktop-bottom.png``task-08-answer-desktop.png``task-08-answer-result-desktop.png``task-08-result-desktop.png``task-08-custom-desktop.png``task-08-practice-mobile.png``task-08-answer-mobile.png`
## 完成标准核对开发计划任务08
- ✅ 模块选题5 模块卡片全选)。
- ✅ 5/10/15 分钟组卷(时长→题量)。
- ✅ 自定义组卷。
- ✅ 会话 + 计时(作答页计时,`secondsUsed` 单题计)。
- ✅ 作答 + 提交(单选、校验当前题)。
- ✅ 结果 + 标准解析。
- ✅ 可从刷题中心或首页开始一套题并完成提交;记录写入 JSONpractice-sessions / practice-records
- ✅ 中途刷新恢复(`recordIndex` 进度推进,`currentQuestion` 按索引取题,刷新后 `question:null` 走 finish
- ✅ 提交后不能重复计入同一题(`duplicate:true` 幂等)。
- ✅ 答案、正确率、耗时、完成状态准确。
## 边界说明
- 「答错自动沉淀错题(写 wrong-questions.json」属于**任务 09**。任务 08 的 `answer` 不写错题库,整卷结果与单题结果中的「已自动收录到错题本」为提示文案;错题本 tab 当前仅对接 `reviewApi.list` 展示既有错题,完整间隔复习闭环在任务 09 完成。
- 「标记错因(可多选)」当前为前端本地交互,落库归属任务 09 的错题逻辑。
## 当前结论
任务 08 完成:刷题与结果闭环在前后端打通。普通组卷 + 自定义组卷、会话持久化(`recordIndex` 进度防重复/防乱序)、单题即时反馈(对错/正确答案/解析/AI 入口/标记错因)、整卷结果(正确率/用时/错题回顾全部实测通过新增「错因分布」接口供刷题中心展示修复移动端标题重复与单题耗时统计。类型检查vue-tsc / tsc与生产构建128 模块)全部通过。测试数据已清理,题库/会话/记录恢复演示基线。