# 诗词答题 API 文档 ## 基础信息 | 项目 | 说明 | |------|------| | 基础URL | `/api.php` | | 返回格式 | JSON | | 编码 | UTF-8 | | 请求方式 | **GET / POST 都支持** | ### 请求方式说明 | 接口 | 推荐方式 | 原因 | |------|----------|------| | 获取题目 (question) | **GET** | 读取操作,简单可缓存 | | 下一题 (next) | **GET** | 读取操作 | | 获取新题 (fetch) | **GET** | 读取操作 | | 提交答案 (answer) | **POST** | 提交操作,更规范 | | 获取提示 (hint) | **GET** | 读取操作 | | 题目列表 (list) | **GET** | 读取操作 | | 刷新缓存 (refresh) | **GET** | 管理操作 | | 状态统计 (stats) | **GET** | 读取操作 | ## 通用返回格式 ```json { "code": 0, "msg": "", "data": { ... } } ``` | 字段 | 类型 | 说明 | |------|------|------| | code | int | 状态码,0=成功,其他=错误 | | msg | string | 提示信息 | | data | object | 返回数据 | --- ## 接口列表 ### 1. 获取题目 **请求** ``` GET /api.php?action=question&id=0 ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | action | string | 否 | 默认为 question | | id | int | 否 | 题目ID,默认0 | **返回** ```json { "code": 0, "msg": "", "data": { "id": 0, "total": 10, "question": "欲把西湖比西子,\"________\"", "author": "苏轼", "type": "江南", "grade": "小学", "dynasty": "宋朝", "options": [ {"index": 1, "content": "山色空蒙雨亦奇"}, {"index": 2, "content": "淡妆浓抹总相宜"}, {"index": 3, "content": "门前流水尚能西"}, {"index": 4, "content": "拄杖无时夜叩门"} ] } } ``` --- ### 2. 获取下一题(自动进度) **请求** ``` GET /api.php?action=next ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | action | string | 是 | 固定为 next | **说明** - **无需传 id 参数**,系统自动记住当前进度(使用 Session) - 每次刷新自动跳到下一题 - 如果已经是最后一题,自动回到第 0 题(循环) **使用方式** | 刷新次数 | 返回的 id | |----------|-----------| | 第 1 次 | 1 | | 第 2 次 | 2 | | 第 3 次 | 3 | | ... | ... | | 第 62 次 | 0 (循环) | **返回** ```json { "code": 0, "msg": "", "data": { "id": 1, "total": 62, "question": "人生得意须尽欢,\"__________\"", "author": "李白", "type": "豪放", "grade": "高中", "dynasty": "唐朝", "options": [...], "prev_id": 0 } } ``` --- ### 3. 刷新获取新题(推荐) **请求** ``` GET /api.php?action=fetch ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | action | string | 是 | 固定为 fetch | **说明** - **每次刷新都从百度 API 获取新题目** - 新题目自动写入本地缓存(去重) - 返回本次获取的随机一题 - API 失败时自动降级使用本地缓存 **返回** ```json { "code": 0, "msg": "", "data": { "id": null, "total": 65, "question": "人生得意须尽欢,\"__________\"", "author": "李白", "type": "豪放", "grade": "高中", "dynasty": "唐朝", "options": [...], "from_cache": false, "new_questions": 3 } } ``` **额外返回字段** | 字段 | 类型 | 说明 | |------|------|------| | from_cache | bool | 是否来自本地缓存 | | new_questions | int | 本次新增题目数量 | --- ### 4. 提交答案 **请求方式 1:GET(简单)** ``` GET /api.php?action=answer&id=0&answer=2 ``` **请求方式 2:POST(推荐)** ``` POST /api.php Content-Type: application/x-www-form-urlencoded action=answer&id=0&answer=2 ``` 或使用 JSON: ``` POST /api.php Content-Type: application/json { "action": "answer", "id": 0, "answer": "2" } ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | action | string | 是 | 固定为 answer | | id | int | 是 | 题目ID | | answer | string | 是 | 答案序号(1-4) | **返回** ```json { "code": 0, "msg": "", "data": { "id": 0, "correct": true, "your_answer": "2", "correct_answer": "2", "next_id": 1, "has_next": true } } ``` --- ### 5. 获取提示 **请求** ``` GET /api.php?action=hint&id=0 ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | action | string | 是 | 固定为 hint | | id | int | 是 | 题目ID | **返回** ```json { "code": 0, "msg": "", "data": { "id": 0, "hint": "这是首描写江南的诗,你在小学学过它。", "author": "苏轼", "dynasty": "宋朝" } } ``` --- ### 6. 获取题目列表 **请求** ``` GET /api.php?action=list ``` **返回** ```json { "code": 0, "msg": "", "data": { "total": 10, "list": [ { "id": 0, "question": "欲把西湖比西子,\"________\"...", "author": "苏轼", "dynasty": "宋朝" } ] } } ``` --- ### 7. 刷新缓存 **请求** ``` GET /api.php?action=refresh ``` **说明** - 强制从百度 API 获取最新题目 - 合并到本地缓存(去重) **返回** ```json { "code": 0, "msg": "", "data": { "refreshed": true, "total": 15 } } ``` --- ## 缓存机制 | 配置 | 值 | 说明 | |------|------|------| | 缓存文件 | `data/questions.json` | 本地JSON文件 | | 过期时间 | **永不过期** | 永久保存 | | 去重方式 | 按 `question_content` | 相同题目不重复存储 | | 降级策略 | API失败自动使用本地缓存 | 原始URL失效也能使用 | | API 限频 | 5秒内只请求一次 | 防止并发请求百度 | ### 缓存文件结构 ```json { "updated": "2024-01-01 12:00:00", "count": 10, "questions": [...] } ``` --- ## 性能优化 ### 已实现优化 | 优化项 | 说明 | |--------|------| | 静态变量缓存 | 同一请求内多次读取只加载一次文件 | | API 请求锁 | 5秒内多人请求只发一次 API | | 原子写入 | 先写临时文件再 rename,防止数据损坏 | | 去重优化 | 只在写入时去重,读取时不处理 | | 超时控制 | API 超时 5 秒,连接超时 3 秒 | ### 性能预估 | 场景 | 响应时间 | |------|----------| | 读取本地缓存 | ~2-5ms | | API 请求成功 | ~200-500ms | | API 限频降级 | ~1ms(直接读缓存) | | 100人并发 | 无压力(读缓存为主) | ### 查看状态 ``` GET /api.php?action=stats ``` 返回: ```json { "code": 0, "data": { "total_questions": 149, "cache_file_size": "40.5 KB", "last_updated": "2026-03-29 05:04:04", "memory_usage": "256 KB" } } ``` --- ## 错误码 | code | 说明 | |------|------| | 0 | 成功 | | 400 | 参数错误 | | 404 | 题目不存在 | --- ## 使用示例 ### JavaScript ```javascript // 获取第一题 fetch('/api.php?action=question&id=0') .then(r => r.json()) .then(d => console.log(d.data.question)); // 获取下一题(自动进度,无需传 id) fetch('/api.php?action=next') .then(r => r.json()) .then(d => console.log(d.data.id, d.data.question)); // 提交答案 fetch('/api.php?action=answer&id=0&answer=2') .then(r => r.json()) .then(d => { if(d.data.correct) { console.log('回答正确!'); } }); ``` ### Flutter/Dart ```dart import 'package:dio/dio.dart'; final dio = Dio(); // 获取题目 Future getQuestion(int id) async { final res = await dio.get('/api.php', queryParameters: { 'action': 'question', 'id': id, }); return res.data['data']; } // 获取下一题(自动进度,无需传 id) Future getNextQuestion() async { final res = await dio.get('/api.php', queryParameters: { 'action': 'next', }); return res.data['data']; } // 提交答案(POST 方式) Future checkAnswer(int id, String answer) async { final res = await dio.post('/api.php', data: { 'action': 'answer', 'id': id, 'answer': answer, }); return res.data['data']['correct']; } ``` --- ## App 集成建议 ### 方案 1:App 自己管理进度(推荐) 使用 `?action=question` 接口,App 完全控制进度: ```dart class QuizApi { int _currentId = 0; int _total = 0; Future getQuestion() async { final res = await dio.get('/api.php', queryParameters: { 'action': 'question', 'id': _currentId, }); final data = res.data['data']; _total = data['total'] ?? 0; return data; } Future submitAnswer(String answer) async { final res = await dio.post('/api.php', data: { 'action': 'answer', 'id': _currentId, 'answer': answer, }); return res.data['data']; } void nextQuestion() { _currentId++; if (_currentId >= _total) { _currentId = 0; } } int get currentId => _currentId; } ``` **优点**: - 不依赖 Session,App 完全控制进度 - 可以随时跳转任意题目 - 适合多端同步 --- ### 方案 2:使用自动进度 使用 `?action=next` 接口,API 自动管理: ```dart Future getNextQuestion() async { final res = await dio.get('/api.php', queryParameters: { 'action': 'next', }); return res.data['data']; } ``` **优点**: - 简单,无需管理 ID - 自动循环 **缺点**: - 依赖 Session,需要保持 cookie - 无法自由跳转题目 --- ### 方案 3:不断获取新题 使用 `?action=fetch` 接口,每次都从百度获取新题: ```dart Future getNewQuestion() async { final res = await dio.get('/api.php', queryParameters: { 'action': 'fetch', }); return res.data['data']; } ``` **适用场景**: - 想要不断扩展题库 - 用户每次刷新都可能看到新题