题库PLUS 服务端
C/S 架构数学题库管理系统 — 服务端。Python + FastAPI + SQLite,以 docx 原生格式存储题目。
快速部署
git clone ssh://git@xn--48s96u.top:10022/nonvioce/tiku.git /opt/tiku-server
cd /opt/tiku-server/tiku-server
bash install.sh # 安装依赖、构建知识点
nohup ./tiku-server serve --restart > tiku.log 2>&1 & # 后台启动(崩溃/更新自动重启)
部署后打开 http://<服务器IP>:8765,Token 首次启动自动生成(./tiku-server token 查看)。
目录结构
tiku-server/
├── app/
│ ├── config.py # YAML 配置加载(路径、端口、Token)
│ ├── database.py # SQLite 连接、建表、CRUD、FTS5
│ ├── models.py # Pydantic 请求/响应模型
│ ├── main.py # FastAPI 入口 + CLI 入口
│ ├── routers/ # API 路由层(仅参数校验和 HTTP 返回)
│ │ ├── auth.py # Token + HMAC 签名认证
│ │ ├── problems.py # 题目 CRUD、搜索、渲染、标注
│ │ ├── import_.py # 整卷 docx 上传拆分导入
│ │ ├── export_.py # 按章节/试题篮导出 docx
│ │ ├── knowledge.py # 知识点树浏览和搜索
│ │ ├── carts.py # 试题篮 CRUD
│ │ └── admin.py # 备份、索引重建、知识点构建、Git 更新
│ └── services/ # 业务逻辑层
│ ├── knowledge.py # 5层知识点树解析(Markdown → 编码映射)
│ ├── docx_parser.py # 整卷 docx 拆分 + 单题 docx 生成
│ ├── docx_builder.py # 多题 docx 合并导出
│ ├── indexer.py # 增量索引(mtime+checksum 检测变更)
│ ├── renderer.py # pandoc docx→HTML+MathJax 渲染管线
│ ├── backup.py # 本地备份 + WebDAV 远程推送
│ └── search.py # FTS5 全文搜索封装
├── data/ # 运行时数据(不提交 Git)
│ ├── problems/ # 单题 docx(<sha256-8>.docx)
│ ├── bank.db # SQLite 数据库
│ ├── papers/ # 导出试卷
│ ├── backups/ # 自动备份(保留 30 个)
│ └── uploads/ # 上传临时文件
├── src/
│ ├── 知识点树.md # 5层 Markdown 知识树(1811 叶)
│ └── knowledge_map.json # 编码→路径映射(knowledge build 生成)
├── web/ # Flutter Web 构建产物(可选)
├── config.yaml # 配置文件
├── install.sh # 安装脚本
├── tiku-server # CLI 入口
└── requirements.txt # Python 依赖
CLI 命令
| 命令 |
说明 |
./tiku-server serve |
启动 FastAPI 服务 |
./tiku-server serve --restart |
启动服务(崩溃/git-update 后自动重启) |
./tiku-server knowledge build |
解析 src/知识点树.md 生成编码映射 |
./tiku-server rebuild-index |
手动增量扫描 problems/ 目录 |
./tiku-server stats |
终端显示题库统计 |
./tiku-server backup |
创建本地备份快照 |
./tiku-server backup --remote |
备份并推送到 WebDAV |
./tiku-server token |
显示 API Token |
./tiku-server token regenerate |
重新生成 Token |
API 参考
认证
所有需要认证的端点必须携带:
Authorization: Bearer <token>
X-Timestamp: <unix_timestamp>(5 分钟内有效)
X-Signature: HMAC-SHA256(token, METHOD+PATH+timestamp)(hex 编码)
| 方法 |
路径 |
认证 |
说明 |
| GET |
/api/health |
否 |
健康检查 |
| POST |
/api/auth/verify |
否 |
验证 Token |
题库查询
| 方法 |
路径 |
参数 |
说明 |
| GET |
/api/stats |
— |
仪表盘统计(总数、按题型/类型/章节分布) |
| GET |
/api/problems |
page, page_size, chapter, question_type, import_type, annotated |
题目列表(分页+筛选) |
| GET |
/api/problems/{id} |
— |
题目详情(含渲染 HTML) |
| GET |
/api/problems/{id}/render |
include_answer |
题目 HTML 渲染片段(JSON) |
| GET |
/api/problems/{id}/view |
token, include_answer |
题目完整 HTML 页面(IFrame 用,Token 在 URL 中) |
| GET |
/api/problems/next-unannotated |
current_id, direction |
连续标注导航 |
| GET |
/api/search |
q, page, page_size |
全文搜索(FTS5 trigram) |
元数据编辑
| 方法 |
路径 |
说明 |
| PUT |
/api/problems/{id}/metadata |
更新元数据(难度、题型、类型、评语) |
| PUT |
/api/problems/{id}/knowledge |
更新知识点编码 |
导入导出
| 方法 |
路径 |
说明 |
| POST |
/api/import |
上传整卷 docx(multipart: file + import_type) |
| GET |
/api/import/{task_id}/status |
轮询导入进度 |
| POST |
/api/export/chapter |
按章节+类型导出(JSON body: chapter, import_type, title) |
| POST |
/api/export/cart/{id} |
按试题篮导出 |
| GET |
/api/exports/{filename} |
下载导出 docx 文件 |
知识点
| 方法 |
路径 |
说明 |
| GET |
/api/knowledge/tree |
完整知识树(5层嵌套 JSON) |
| GET |
/api/knowledge/search |
搜索知识点(q 参数) |
试题篮
| 方法 |
路径 |
说明 |
| GET |
/api/carts |
篮子列表(含题目数量) |
| POST |
/api/carts |
创建篮子(JSON: name) |
| DELETE |
/api/carts/{id} |
删除篮子 |
| GET |
/api/carts/{id} |
篮子详情(含题目列表) |
| POST |
/api/carts/{id}/items |
添加题目(JSON: problem_id) |
| DELETE |
/api/carts/{id}/items/{pid} |
移除题目 |
管理
| 方法 |
路径 |
说明 |
| GET |
/api/admin/backup-config |
查看 WebDAV 配置 |
| PUT |
/api/admin/backup-config |
设置 WebDAV(JSON: url, username, password) |
| POST |
/api/admin/backup/trigger |
增量备份(bank.db + 变更的 docx) |
| POST |
/api/admin/full-backup |
全量备份(bank.db + 全部 docx) |
| GET |
/api/admin/backup/history |
备份历史 |
| POST |
/api/admin/clear-all |
清空题库(删除所有题目) |
| POST |
/api/admin/rebuild-index |
增量重建索引 |
| POST |
/api/admin/knowledge-build |
重建知识点编码 |
| POST |
/api/admin/git-update |
git pull + 自动重启 |
备份策略
- 增量备份:导入/编辑元数据时自动备份
bank.db + 被修改的 docx
- 全量备份:
POST /api/admin/full-backup,备份全部 docx
- WebDAV:配置后每次本地备份自动推送到远程(坚果云/NextCloud)
- 保留 30 个快照,自动清理旧备份
导入队列
导入使用单线程队列处理,避免并发写入 SQLite 导致数据库损坏。/api/health 返回 queue_depth 可查看队列长度。
单题 docx 格式
[题目正文段落(含 OMML 公式、图片、选项)]
【答案】X ← 分割线
【分析】解题思路... (可选)
【详解】详细步骤... (可选)
【点睛】... (可选)
自定义属性(custom.xml,Word 不可见):
question_type:单选题 / 多选题 / 填空题 / 解答题
difficulty:0~1 浮点数,越大越容易
knowledge_codes:JSON 数组,如 ["01.01.03.02.01"]
import_type:精选题 / 经典题 / 高考题
comment:评语
source:来源文件名
数据库结构
problems (id, file_path, mtime, checksum, chapters, difficulty,
question_type, import_type, knowledge_codes, comment, source, ...)
problems_fts (problem_id, body) — FTS5 trigram 全文索引
knowledge_points (code, path, leaf_name, chapter_code, chapter_name)
carts (id, name, created_at)
cart_items (cart_id, problem_id, added_at) — 联合主键
import_tasks (id, status, total, completed, message, result)
域名转换
中文域名 数学.top 的 punycode 编码为 xn--48s96u.top,在 Git remote 等处使用:
ssh://git@xn--48s96u.top:10022/nonvioce/tiku.git