# 章节摸底 + 按知识点顺序学习 方案文档

## 1. 需求概述

### 1.1 核心流程

```
章节摸底(20题) → 按知识点顺序学习 → 知识点达标(90%) → 下一个知识点 → 章节全部达标 → 下一章摸底
```

### 1.2 关键规则

1. **知识点来源**：section 直接绑定的知识点（不扩展子知识点）
2. **90%判断**：跳过没有题目的知识点
3. **智能组卷**：按顺序找第一个未达标知识点，最多2个知识点
4. **题目不够**：不从其他知识点补充，不够就不够
5. **章节流转**：所有知识点达标 → 下一章摸底

---

## 2. assembleType 映射关系

### 2.1 前端传入 → 后端实际处理

| 前端传入 | 后端实际处理 | 说明 |
|---------|-------------|------|
| 0 (旧摸底) | 0 → buildChapterDiagnosticParams | 章节摸底（新逻辑） |
| 9 (原摸底) | 0 → buildChapterDiagnosticParams | 章节摸底（新逻辑） |
| 1 (智能组卷) | 8 → buildChapterIntelligentParams | 按知识点顺序学习（新逻辑） |
| 2/3/4/5 | 保持不变 | 原有逻辑不动 |

### 2.2 paper_type 值

| paper_type | 含义 |
|------------|------|
| 0 | 摸底（包括章节摸底） |
| 1 | 智能组卷 |
| 2 | 知识点组卷 |
| 3 | 教材组卷 |
| 5 | 追练 |

---

## 3. 数据库修改

### 3.1 papers 表增加字段

```sql
ALTER TABLE papers ADD COLUMN diagnostic_chapter_id INT NULL COMMENT '摸底的章节ID';
```

---

## 4. 代码修改清单

### 4.1 新增方法（不影响原有逻辑）

| 文件 | 方法 | 用途 |
|------|------|------|
| ExamTypeStrategy.php | `buildChapterDiagnosticParams` | 章节摸底（新逻辑） |
| ExamTypeStrategy.php | `buildChapterIntelligentParams` | 按知识点顺序学习（新逻辑） |
| DiagnosticChapterService.php | `getChapterKnowledgePointsWithoutExpand` | 获取知识点（不扩展子知识点） |
| DiagnosticChapterService.php | `getFirstUnmasteredKnowledgePointInOrder` | 按顺序找第一个未达标知识点 |
| DiagnosticChapterService.php | `hasChapterDiagnostic` | 判断章节是否已摸底 |
| StudentKnowledgeMastery.php | `allAtLeastSkipNoQuestions` | 判断达标（跳过无题知识点） |

### 4.2 修改方法

| 文件 | 方法 | 修改内容 |
|------|------|---------|
| ExamTypeStrategy.php | `buildParams` | 增加 assembleType 映射逻辑 |

### 4.3 保持不变的方法

- `buildDiagnosticParams`（原摸底）
- `buildIntelligentAssembleParams`（原智能组卷）
- `buildKnowledgePointAssembleParams`（知识点组卷）
- `buildTextbookAssembleParams`（教材组卷）
- `buildMistakeParams`（追练）
- `buildGeneralParams`（通用）

---

## 5. 详细逻辑

### 5.1 章节摸底 (buildChapterDiagnosticParams)

```
1. 获取学生当前教材
2. 找第一个未完成摸底的章节：
   - 遍历章节，检查 papers 表是否有 paper_type=0 且 diagnostic_chapter_id=章节ID 的记录
   - 如果没有，说明这个章节需要摸底
3. 获取该章节所有 section 绑定的知识点（不扩展子知识点）
4. 过滤掉没有题目的知识点
5. 用这些知识点出题（20题）
6. 保存时记录 diagnostic_chapter_id
```

### 5.2 按知识点顺序学习 (buildChapterIntelligentParams)

```
1. 获取当前应该学习的章节（第一个有未达标知识点的章节）
2. 检查该章节是否已摸底
   - 如果没有摸底 → 返回章节摸底参数
3. 获取该章节所有知识点（按 section 顺序）
4. 按顺序遍历，找第一个未达标的知识点（跳过无题知识点）
5. 检查题目数量：
   - 如果不够20题，补充下一个未达标知识点
   - 最多2个知识点
6. 如果所有知识点都达标 → 返回下一章的摸底参数
```

### 5.3 90%达标判断 (allAtLeastSkipNoQuestions)

```
1. 遍历章节的所有知识点
2. 检查每个知识点是否有题目
   - 没有题目 → 跳过
3. 检查掌握度是否 >= 90%
   - 有题但没有掌握度记录 → 视为 0%，未达标
4. 所有有题的知识点都达标 → 返回 true
```

---

## 6. 执行步骤

1. [x] 创建方案文档
2. [x] 创建 migration 增加 diagnostic_chapter_id 字段
   - 文件: `database/migrations/2026_02_01_000001_add_diagnostic_chapter_id_to_papers_table.php`
3. [x] 修改 StudentKnowledgeMastery 增加新方法
   - `allAtLeastSkipNoQuestions`: 判断达标（跳过无题知识点）
   - `getFirstUnmasteredKpCode`: 获取第一个未达标的知识点
4. [x] 修改 DiagnosticChapterService 增加新方法
   - `getChapterKnowledgePointsSimple`: 获取知识点（不扩展子知识点）
   - `hasChapterDiagnostic`: 判断章节是否已摸底
   - `getFirstUndiagnosedChapter`: 获取第一个未摸底的章节
   - `getCurrentLearningChapter`: 获取当前学习章节
   - `getNextChapter`: 获取下一个章节
   - `filterKpCodesWithQuestions`: 过滤有题的知识点
   - `getUnmasteredKpCodesInOrder`: 按顺序获取未达标知识点
5. [x] 修改 ExamTypeStrategy 增加新方法和映射逻辑
   - 修改 `buildParams`: 增加 assembleType 映射
   - 新增 `buildChapterDiagnosticParams`: 章节摸底
   - 新增 `buildChapterIntelligentParams`: 按知识点顺序学习
6. [x] 修改 QuestionBankService 保存 diagnostic_chapter_id
7. [x] 修改 Paper 模型增加 diagnostic_chapter_id 字段
8. [ ] 执行 migration
9. [ ] 测试验证

---

## 7. 风险控制

- 所有原有方法保持不变
- 新增独立方法处理新逻辑
- 通过 assembleType 映射控制流程
- 即使新逻辑有问题，也不影响 assembleType=2/3/4/5 的原有功能
