Files
book-mcp/db/database.py
Quentin 9b0910ccb1 Initial commit: book-mcp 教辅处理 MCP server + Arco Vue 管理端
后端 (book_mcp_server.py, 335 行, 15 个 MCP 工具):
- create_book / add_document / mark_html_ready / mark_json_ready / mark_failed
- update_book_status / soft_delete_book / retry_failed
- get_book / list_books / get_document / list_documents / get_document_url
- upload_to_rustfs / health
- 走 JSON-RPC + stateless_http + json_response(前端可直接调)
- launchd 守护 / 6 秒拉起(macOS)
- systemd 守护脚本(Linux/codelab)

存储:
- MongoDB (mongo:8.0 容器) - book/book_document/document_file 三表
- RustFS 对象存储(fs.code-lab.cn/scan/)- book/<id>/docs/<doc_id>/{docx,html,json}
- 幂等设计: zip_sha256 唯一 + (book_id+original_path) 唯一

前端 (Vue 3 + TS + Arco Design):
- 列表页 /book/list - 搜索 + 状态过滤 + 进度
- 详情页 /book/:id - 文档折叠 + 文件下载
- MCP 客户端 src/api/mcp.ts (JSON-RPC over fetch)

部署:
- deploy/install.sh (codelab 一键装 backend + systemd)
- deploy/install-frontend.sh (vite dev / 生产构建)
- docs/deploy.md (运维手册)

测试:
- tests/test_basic.py (sanity 7 项)
- tests/test_e2e.py (完整 e2e: docx→HTML→入库→上传→校验)
2026-10-11 18:03:57 +08:00

98 lines
3.2 KiB
Python
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.

"""MongoDB 连接 + collection 管理
3 个 collection:
book — 教辅
book_document — 教辅文档
document_file — 文档文件
设计思路:
- _id 用业务 ID(doc_id 16 字符 sha1 / book_id 自增字符串 / file_id 自增字符串)
- book_id 用 INT 自增(mongo 中用 find_one_and_update + $inc 实现)
- 不强制 schema(mongo 灵活),但用 helper 函数做字段约束
- 索引在 init_db() 时一次性建好
"""
import os
import yaml
from pymongo import MongoClient, ASCENDING, DESCENDING, ReturnDocument
from pymongo.database import Database
_CONFIG_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config.yaml')
_client: MongoClient | None = None
def load_config() -> dict:
with open(_CONFIG_PATH) as f:
return yaml.safe_load(f)
def get_client() -> MongoClient:
global _client
if _client is None:
cfg = load_config()['database']
_client = MongoClient(cfg['url'], serverSelectionTimeoutMS=5000)
return _client
def get_db() -> Database:
cfg = load_config()['database']
return get_client()[cfg['db_name']]
def get_collection(name: str):
return get_db()[name]
# ---- collection 名常量 ----
COL_BOOK = 'book'
COL_DOC = 'book_document'
COL_FILE = 'document_file'
# ---- counters collection(book_id / file_id 自增用)----
COL_COUNTER = 'counters'
# ---- 索引初始化 ----
def init_db():
"""建所有索引(幂等)"""
db = get_db()
# book: zip_sha256 唯一 + (name, version, is_deleted) 唯一
db[COL_BOOK].create_index([('zip_sha256', ASCENDING)], unique=True, name='uk_book_zip_sha')
db[COL_BOOK].create_index([('name', ASCENDING), ('version', ASCENDING), ('is_deleted', ASCENDING)],
unique=True, name='uk_book_name_version')
db[COL_BOOK].create_index([('status', ASCENDING)], name='idx_book_status')
db[COL_BOOK].create_index([('created_at', DESCENDING)], name='idx_book_created')
# book_document: _id 已唯一(doc_id 即 _id);额外索引仅用于查询加速
db[COL_DOC].create_index([('book_id', ASCENDING), ('order_index', ASCENDING)], name='idx_book_order')
db[COL_DOC].create_index([('status', ASCENDING)], name='idx_doc_status')
# document_file: (doc_id, file_type) 唯一 + (bucket, key)
db[COL_FILE].create_index([('doc_id', ASCENDING), ('file_type', ASCENDING)],
unique=True, name='uk_doc_type')
db[COL_FILE].create_index([('object_bucket', ASCENDING), ('object_key', ASCENDING)],
name='idx_bucket_key')
print(f"✓ MongoDB 已就绪(db: {db.name}, collections: {[COL_BOOK, COL_DOC, COL_FILE]})")
print(f" ✓ 8 个索引创建完成")
def health() -> bool:
try:
get_client().admin.command('ping')
return True
except Exception:
return False
def next_id(name: str) -> int:
"""原子自增 ID(mongo 没有 AUTO_INCREMENT,用 counters collection 模拟)"""
db = get_db()
r = db[COL_COUNTER].find_one_and_update(
{'_id': name},
{'$inc': {'seq': 1}},
upsert=True,
return_document=ReturnDocument.AFTER,
)
return int(r['seq'])