Files
examination/docs/architecture.md
T
wonder 273b88219a feat: 前端优化 - 明暗模式、题型Tab分隔、移除统计
- 支持明暗主题切换,用户选择持久化到 localStorage
- 题型通过 Tab 按钮分隔展示,支持过滤
- 移除正确/错误统计和进度条
- 题型颜色通过 CSS 变量集中管理(--type-*)
- JS 抽取 QUESTION_TYPES 常量,便于扩展
- 更新架构文档反映上述变更

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-02 18:40:50 +08:00

369 lines
13 KiB
Markdown
Raw 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.
# 架构文档
## 1. 系统总览
```
┌─────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ ┌───────────────────────────────────────────────┐ │
│ │ index.html (单文件 SPA) │ │
│ │ ┌─────────┐ ┌──────────┐ ┌─────────────┐ │ │
│ │ │ 导航栏 │ │ 做题区域 │ │ 导入模态框 │ │ │
│ │ └────┬────┘ └─────┬────┘ └──────┬──────┘ │ │
│ └───────┼─────────────┼──────────────┼──────────┘ │
└──────────┼─────────────┼──────────────┼─────────────┘
│ fetch │ fetch │ JSON.parse
▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ topics/ (数据层) │
│ ├── index.json ← 主题索引 │
│ └── {topic-slug}/ │
│ ├── meta.json ← 主题元信息(稳定) │
│ ├── fill_blank.json ← 填空题集 │
│ └── single_choice.json ← 单选题集 │
└─────────────────────────────────────────────────────┘
▲
│ git push / pull
▼
┌─────────────────────────────────────────────────────┐
│ Agent / LLM (生成层) │
│ ├── Claude Code Skill (generate / validate) │
│ ├── LLM + 提示词模板 (schema/prompt-template.md) │
│ └── JSON Schema 校验 (schema/question.schema.json) │
└─────────────────────────────────────────────────────┘
```
## 2. 目录结构
```
examination/
├── index.html # 前端入口(单文件 SPA)
├── topics/ # 题目数据
│ ├── index.json # 主题索引
│ └── {topic-slug}/ # 主题目录
│ ├── meta.json # 主题元信息
│ ├── fill_blank.json # 填空题
│ ├── single_choice.json # 单选题
│ ├── multiple_choice.json # 多选题(可选)
│ ├── true_false.json # 判断题(可选)
│ ├── short_answer.json # 简答题(可选)
│ ├── code_reading.json # 代码阅读题(可选)
│ └── scenario.json # 场景分析题(可选)
├── schema/ # Schema 与模板
│ ├── question.schema.json # 题目 JSON Schema(Draft-07)
│ ├── prompt-template.md # LLM 生成提示词模板
│ └── templates/ # 各题型示例
│ ├── single_choice.json
│ ├── multiple_choice.json
│ ├── true_false.json
│ ├── fill_blank.json
│ ├── short_answer.json
│ ├── code_reading.json
│ └── scenario.json
├── docs/ # 文档
│ ├── requirements.md # 需求文档
│ └── architecture.md # 架构文档(本文件)
├── .gitignore # 排除 .ref/
└── README.md
```
## 3. 数据模型
### 3.1 主题索引 — `topics/index.json`
```json
{
"version": "1.0.0",
"updated": "2026-09-02",
"topics": [
{
"slug": "gc-jvm",
"name": "GC / JVM / 三色标记",
"description": "...",
"path": "topics/gc-jvm",
"stats": {
"total": 20,
"by_type": { "fill_blank": 10, "single_choice": 10 }
}
}
]
}
```
前端加载此文件渲染主题列表,`stats` 字段用于展示题目数量。
### 3.2 主题元信息 — `topics/{slug}/meta.json`
```json
{
"slug": "gc-jvm",
"name": "GC / JVM / 三色标记",
"description": "JVM 堆内存模型、GC 算法、三色标记法、垃圾收集器",
"tags": ["jvm", "gc", "heap"],
"difficulty_range": [1, 5],
"schema_version": "1.0.0",
"question_files": ["fill_blank", "single_choice"],
"stats": {
"total": 20,
"by_type": { "fill_blank": 10, "single_choice": 10 }
}
}
```
- `question_files` 列出该主题包含的题型文件名(不含 `.json` 后缀)
- 前端根据此列表动态加载对应的题目文件
- `stats` 在每次生成/追加题目后由 Agent 更新
### 3.3 题目文件 — `topics/{slug}/{type}.json`
```json
{
"topic": "gc-jvm",
"type": "fill_blank",
"schema_version": "1.0.0",
"generated": "2026-09-02T00:00:00Z",
"questions": [ /* 题目数组 */ ]
}
```
每种题型一个文件,支持多 Agent 并行写入不同题型文件而无冲突。
### 3.4 题目对象
#### 公共字段(所有题型必有)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `id` | string | ✅ | 格式 `{type_short}-{seq}`,如 `fb-001` |
| `type` | enum | ✅ | 题型标识 |
| `difficulty` | integer | ✅ | 1-5 |
| `tags` | string[] | ✅ | 细粒度知识点标签 |
| `question` | string | ✅ | 题干 |
| `explanation` | string | ✅ | 解析 |
| `source` | string | ❌ | 来源 |
| `related` | string[] | ❌ | 关联题目 id |
#### 填空题 (`fill_blank`)
```json
{
"answer": ["老", "Old"],
"answer_rule": "any"
}
```
- `answer`: string[],可接受的答案列表
- `answer_rule`: `"any"`(任一匹配) | `"all"`(全部匹配) | `"ordered"`(按序匹配)
#### 单选题 (`single_choice`)
```json
{
"options": { "A": "选项A", "B": "选项B", "C": "选项C", "D": "选项D" },
"answer": "B"
}
```
#### 多选题 (`multiple_choice`)
```json
{
"options": { "A": "...", "B": "...", "C": "...", "D": "..." },
"answer": ["A", "C"]
}
```
#### 判断题 (`true_false`)
```json
{ "answer": true }
```
#### 简答题 (`short_answer`)
```json
{
"answer": "参考答案文本",
"keywords": ["关键词1", "关键词2"],
"scoring_rubric": "评分标准说明"
}
```
#### 代码阅读题 (`code_reading`)
```json
{
"code": "int x = 1;\nprintf(\"%d\", x);",
"language": "c",
"sub_questions": [
{ "index": 1, "type": "single_choice", "question": "...", "options": {...}, "answer": "A", "explanation": "..." }
]
}
```
#### 场景分析题 (`scenario`)
```json
{
"context": "一个电商系统在高峰期出现超卖问题...",
"code": null,
"sub_questions": [
{ "index": 1, "type": "short_answer", "question": "...", "answer": "...", "keywords": [...], "explanation": "..." }
]
}
```
### 3.5 ID 编码规则
```
{type_short}-{sequence}
fb - 001
```
| type_short | 题型 |
|------------|------|
| `sc` | single_choice |
| `mc` | multiple_choice |
| `tf` | true_false |
| `fb` | fill_blank |
| `sa` | short_answer |
| `cr` | code_reading |
| `sn` | scenario |
## 4. 前端架构
### 4.1 页面结构
```
┌──────────────────────────────────────────┐
│ Header: 🎯 CS 知识应试强化 [🌙/☀️] │
├────────────┬─────────────────────────────┤
│ Sidebar │ Main Content │
│ │ │
│ 📚 主题 │ ┌─ Tabs ─────────────────┐ │
│ · gc-jvm │ │ 全部 | 填空 | 选择 |.. │ │
│ · ai-so │ └────────────────────────┘ │
│ · ai-mm │ ┌─ Toolbar ──────────────┐ │
│ · ai-he │ │ 重置 全部显示 提交全部 │ │
│ · ai-at │ └────────────────────────┘ │
│ │ ┌─ Question Card ────────┐ │
│ ──────── │ │ #1 ★★★ │ │
│ 📥 导入 │ │ 题干 [____] │ │
│ │ │ [检查] [显示答案] │ │
│ │ │ 💡 解析... │ │
│ │ └────────────────────────┘ │
└────────────┴─────────────────────────────┘
```
### 4.2 数据流
```
页面加载
│
▼
fetch topics/index.json ──→ 渲染侧边栏主题列表
│
│ 用户点击主题
▼
fetch topics/{slug}/meta.json ──→ 获取 question_files 列表
│
▼
fetch topics/{slug}/{type}.json (并行) ──→ 合并 questions 数组
│
▼
renderQuiz() ──→ 渲染 Tab 栏 + 按当前 Tab 过滤题目卡片
│
│ 用户交互(选择/输入/检查/显示答案/切换 Tab)
▼
answers / activeTab 状态更新 ──→ re-render
```
### 4.3 状态管理
```javascript
// 题型配置(常量)
const QUESTION_TYPES = {
fill_blank: { label: '填空', color: '--type-fill_blank' },
single_choice: { label: '选择', color: '--type-single_choice' },
// ...
};
// 应用状态
let topics = []; // 从 index.json 加载的主题列表
let currentTopic = null; // 当前选中的主题 slug
let currentQuestions = []; // 当前主题的所有题目(合并自多个题型文件)
let answers = {}; // qId → { value, correct, revealed }
let customQuestions = []; // 通过导入功能临时添加的题目
let activeTab = 'all'; // 当前 Tab:'all' 或题型名称
```
### 4.4 题型配置与渲染
题型颜色通过 CSS 变量定义在 `:root` 中,新增题型只需:
1. 在 `QUESTION_TYPES` 添加配置
2. 在 `:root` 添加 `--type-{name}` 变量
3. CSS 自动生效(通过 `.q-card.type-{name}` 规则)
| 题型 | 渲染方式 | 判定逻辑 | 边框颜色变量 |
|------|----------|----------|--------------|
| fill_blank | 题干中的 `______` 替换为 `<input>` | `matchFillAnswer()` 根据 `answer_rule` 匹配 | `--type-fill_blank` |
| single_choice | 选项列表,radio 样式 | `value === answer` | `--type-single_choice` |
| multiple_choice | 选项列表,checkbox 样式 | 数组比较 | `--type-multiple_choice` |
| true_false | 两个大按钮 | `value === answer` | `--type-true_false` |
| short_answer | `<textarea>` | 关键词匹配(辅助) | `--type-short_answer` |
| code_reading | 代码块 + 子题列表 | 按子题类型分别判定 | `--type-code_reading` |
| scenario | 场景描述 + 子题列表 | 按子题类型分别判定 | `--type-scenario` |
## 5. 生成工作流
### 5.1 非 Agent 环境(手动 LLM 对话)
1. 打开 `schema/prompt-template.md`,复制提示词模板
2. 替换 `{topic_name}`、`{question_type}`、`{count}` 等变量
3. 将提示词发送给 LLM(ChatGPT / Claude 等)
4. 获取 JSON 输出后,粘贴到对应题型文件中
5. 更新 `meta.json` 的 `stats` 字段
6. 刷新前端页面
### 5.2 Agent 环境(Claude Code Skill)
```
/examination generate --topic gc-jvm --type fill_blank --count 10
/examination validate --file topics/gc-jvm/fill_blank.json
/examination batch-generate --topic gc-jvm --types fill_blank,single_choice
```
Skill 内部流程:
1. 读取 `meta.json` 获取主题上下文
2. 调用 LLM 生成题目(使用 schema 约束)
3. 校验输出是否符合 `question.schema.json`
4. 写入对应题型文件
5. 更新 `meta.json` 和 `index.json` 的 `stats`
### 5.3 多 Agent 并行生成
```
Agent 1 ──→ topics/gc-jvm/fill_blank.json
Agent 2 ──→ topics/gc-jvm/single_choice.json
Agent 3 ──→ topics/gc-jvm/multiple_choice.json
```
由于每种题型写入独立文件,多个 Agent 可并行生成同一主题的不同题型而无写冲突。
## 6. 扩展指南
### 新增主题
1. 创建 `topics/{new-slug}/` 目录
2. 创建 `meta.json`
3. 按题型创建题目 JSON 文件
4. 更新 `topics/index.json`
### 新增题型
1. 在 `schema/question.schema.json` 的 `definitions.question.type.enum` 中添加新类型
2. 添加对应的 `allOf` 条件分支定义字段约束
3. 在前端 `typeLabel()` 中添加映射
4. 在前端 `renderQuestionCard()` 中添加渲染逻辑
5. 在 `schema/templates/` 中添加示例模板