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→入库→上传→校验)
This commit is contained in:
2026-10-11 18:03:45 +08:00
parent f7fb571bce
commit 9b0910ccb1
160 changed files with 23628 additions and 3 deletions

59
tests/test_basic.py Normal file
View File

@@ -0,0 +1,59 @@
"""基本 sanity 测试 — 不依赖具体 DB 连接,只验模块能 load"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def test_imports():
"""所有模块能 import 不报错"""
from db import models, database
from db.models import Book, BookDocument, DocumentFile
from services import book_service, document_service, file_service, rustfs_service
from services.wordparse import cleaner, post, render, convert
print("✓ 所有模块 import OK")
# Book 是 mongo helper class(非 ORM)
assert hasattr(Book, 'to_mongo')
assert hasattr(Book, 'from_mongo')
print("✓ Book 是 mongo helper class")
# mongo ↔ dict 对称
b = Book(id=1, name='test', zip_path='x', zip_sha256='abc', zip_size=100)
m = b.to_mongo()
assert m['_id'] == 1 and m['name'] == 'test'
b2 = Book.from_mongo(m)
assert b2.id == 1 and b2.name == 'test'
print("✓ Book to_mongo/from_mongo 对称")
# doc_id 派生
doc_id = BookDocument.make_doc_id(1, "docx/chapter1.docx")
assert len(doc_id) == 16
assert doc_id == BookDocument.make_doc_id(1, "docx/chapter1.docx")
print(f"✓ doc_id 幂等:{doc_id}")
# doc_type 推断
from services.document_service import _infer_doc_type
assert _infer_doc_type("课时作业1.docx") == "课时作业"
assert _infer_doc_type("周末提升训练.docx") == "周末提升"
assert _infer_doc_type("阶段质量评估(一).docx") == "阶段质量评估"
assert _infer_doc_type("期末质量评估.docx") == "综合评估"
assert _infer_doc_type("random.docx") == "unknown"
print("✓ doc_type 推断正确")
# cleaner 文本修整
from services.wordparse.cleaner import _fix_text
out = _fix_text('<a w="210mm" h="25.4"/>')
assert '0' in out and 'mm' not in out
print(f"✓ cleaner 修单位:{out}")
# rustfs key 模板
from services.rustfs_service import build_key
key = build_key(123, "5b3a2f1e9c0d4b7a", "html")
assert key == "book/123/docs/5b3a2f1e9c0d4b7a/output.html"
print(f"✓ rustfs key 模板:{key}")
if __name__ == '__main__':
test_imports()
print("\n✅ 全部测试通过")