一、立项背景和目标
历史纸质图书转电子书业务长期依赖邮件和共享盘,在出版社内部与多家外包转码公司之间流转,存在交接标准不一致、任务进度不透明、元数据缺失严重、异常无法追溯、历史资源检索效率低等问题。本项目立项建设一套统一的历史图书数字化资源管理平台,把出版社原稿上传、转码任务发布、外包公司处理、成果回传、内部校验、验收归档、异常隔离到检索分析的全流程收敛到同一系统内,并以约 100TB 级历史资源为目标规模,从底层保证长期稳定运行与快速查询。
二、软件功能与核心功能模块
系统为模块化单体的企业级 Web 后台,前台使用角色为出版社人员与转码公司人员,核心模块包括:
1. 图书与元数据模块:以规范化书号 canonical_book_no 作为图书唯一标识,维护图书、版本与文件资产;书号、作者等元数据由 EPUB 提取而非人工录入。
2. 任务协作模块:出版社发布转码任务,转码公司处理并回传成果,任务状态经状态机受控流转,禁止直接改状态;转码公司侧只呈现待处理、处理中、已回传、已退回、已作废五态,内部校验与验收状态不外泄。
3. 文件与上传模块:受控本地文件存储,支持大文件分片上传、断点续传与对账补传,下载走浏览器原生与 HTTP Range 分片。
4. 校验验收与异常治理模块:成果 EPUBCheck 校验、验收通过或退回、异常隔离与返工闭环,全流程留痕。
5. 归档与检索模块:对归档成果做结构化解析、切片与检索单元生成,并导出只读知识投影。
6. 权限与审计模块:组织级 RBAC、幂等键与 X-Request-Id、操作审计与状态变更事件。
7. 运行健康看板与统计报表:监控数据库、向量库、各类 worker 与后端服务状态。
三、业务流程与功能路径
典型路径为:出版社上传原稿 → 发布转码任务 → 转码公司下载原稿(首次下载触发任务进入处理中)→ 回传成果包(先上传文件资产、再创建提交批次)→ 系统自动校验 → 出版社验收(通过则归档,不通过则退回返工)→ 归档后重建切片、生成知识投影用于检索分析。异常文件进入隔离区,处理完成后回到任务流;每一次状态变更都会同步写入状态事件与审计日志,保证全流程可追溯。
一、整体架构与设计思路
采用模块化单体架构,前后端分离。前端 React + TypeScript + Ant Design Pro + Vite,TanStack Query 管服务端状态、Zustand 管会话状态;后端 Python + FastAPI + Pydantic 校验 + SQLAlchemy 2.x + Alembic 迁移;数据库 PostgreSQL(预留 pgvector),异步任务与缓存用 Redis + Celery。文件本体不进数据库,进入受控本地存储,库内只存元数据、状态、索引与存储定位字段;大文件处理、校验、OCR、向量化一律走异步 worker。设计上坚持先冻结契约再写代码:实体模型、数据库 schema、API 契约、前后端设计五份文档冻结后才进入实现,状态变更一律走业务动作端点。
二、我负责的模块和结果
我负责系统从 0 到 1 的设计与实现,覆盖需求契约冻结、后端核心服务、前端后台与归档派生能力。量化结果:仓库累计 277 次提交(2026-05-06 至 09-30);后端 190 个 Python 文件、20 个 API 模块、15 个服务包、67 个测试文件,阶段一 pytest 288 项全通过;前端 106 个 TS/TSX 文件、39 个测试文件,覆盖 81 个路由入口;EPUB 结构化解析把 14 本样书解析出 4264 个结构节点、33414 条证据记录、3584 个资产;开发库 14 个知识投影结构检查全部通过,统计 6605 个检索单元、31739 条 Canonical 证据;切片/向量化/静态契约定向测试 106 项通过;完成 FMS 与 WeKnora 生产网关打通,首次投影同步已在生产进入 running。
三、我遇到的难点、坑和解决方案
1. 状态机与幂等:外包交接场景重复提交、重复下载频繁,通过定义幂等键和"先上传资产、再建提交批次"两步契约,并在同一事务里写状态、事件与审计解决。
2. 大文件稳定性:采用分片上传 + upload session 断点续传 + 对账补传;下载改走浏览器原生下载与 HTTP Range 分片,避免整包载入内存。
3. 派生内容可追溯:切片必须能回到原文位置,因此引入确定性 embedding_text 与 evidence ID 回溯源文件,并加 tokenizer 与硬上限门禁,超限切片直接阻断而非静默截断。
4. 安全:成果包 EPUB 预览存在脚本执行风险,最终收敛为服务端加前端双层净化(DOMPurify + Shadow DOM),不开放 allow-scripts。
5. 环境坑:Windows/Docker 下 localhost 端口转发异常导致连不上数据库,改用显式端口映射并固化到配置。