跳到正文

面向开发者

这份设计文档记录架构和设计取舍,使用了较多内部术语。只想了解怎么使用软件,请看使用指南。

tidoc — 设计文档(DESIGN) ​

报账凭证管理与整理工具。Mac / Windows 通用桌面程序。

实现状态与开发进度见 README 状态节。本文档只描述设计意图与架构决策,不重复记录「做完了什么」。

  • 定位:帮个人、团队和社团集中录入发票及相关材料,核对识别结果,并生成可交换的绑定包。材料和提交文档由报账方案定义;整理人员可用打印导出组件生成提交件。
  • 参考仓库(只读,不再改动):invoice2docx。Tidoc 复用其发票解析和金额校验经验,Word 业务输出采用适配包模板。

核心理念 ​

软件替用户整理和校验,用户只填该填的;能改的在软件里改,不能改的锁死;一切交换通过带签名的绑定包,杜绝手工乱改。


1. 名称 ​

  • 产品名:tidoc(Tidy + Doc)
  • 绑定包 / 汇总文件后缀:.tidoc

2. 技术选型(定稿) ​

前端形态:PyWebView 原生窗口 + HTML/CSS/JS 前端。

用户拿到的是一个 tidoc.app(Mac)/ tidoc.exe(Win),双击即进主界面,有独立 Dock / 任务栏图标,不进目录、不弹浏览器、不点脚本。这一条专门解决旧仓库"进目录双击脚本再弹浏览器"的别扭体验。

  • 窗口内核用系统自带 WebView(macOS 的 WKWebView、Windows 的 WebView2),不打包浏览器内核,因此体积小。
  • 界面用 HTML/CSS/JS 自由编写,能做到简洁美观、动画细腻。
  • 后端用 Python,直接复用移植自 engine.py 的解析引擎。
  • 打包用 PyInstaller,产物按"核心 + 可选组件"拆分。

被否决的备选:Electron(自带 Chromium,体积过大,与"体积小"冲突);Tauri(体积最小但需引入 Rust + 维护 Rust↔Python 通信,复杂度高一档);Flet(旧仓库桌面版走过,界面自由度不如直接写 HTML)。


3. 体积控制与依赖分层 ​

核心(必装)只依赖轻量件:

  • pywebview(原生窗口)
  • pypdf(读 PDF 文本,轻)
  • Send2Trash(把已归档的外部文件移到系统废纸篓 / 回收站,可恢复)
  • jsonschema(校验团队适配包及共享输出上下文的 Draft 2020-12 Schema)
  • 标准库 xml、sqlite3、hashlib、hmac、zipfile

重依赖全部下沉到「打印导出组件」(可选安装,按需从 COS 下载):

  • python-docx(读取和检查方案定义的 Word 文档)
  • pypdf(PDF 拼接)
  • Pillow + reportlab(付款截图转 / 拼进 PDF、拼接页编号)
  • docxtpl + Jinja2(受限 Word 模板渲染)

核心和独立打印组件都依赖 jsonschema,打印依赖清单为 requirements-print.txt。唯一的输出上下文 Schema 位于 schemas/team-adapter/1/context.schema.json,由轻量模块 tidoc_print.context_validation 加载,核心与独立组件使用同一校验器和资源。字段投影模块与校验模块不依赖数据库、API 或 WebView。

总览 Excel 由核心直接手写 OXML + zip 生成(见 services/exports.py),不引 openpyxl;PDF 拼接用 pypdf,不引 pikepdf。

不打包通用 OCR SDK: 默认发票识别走 XML 优先、PDF 文本其次。核心只调用系统原生能力做轻量兜底:查验单发票号归属确认、付款截图实付金额提取;阿里云 OCR 作为可选组件实现(见第 10 节),SDK 不进核心包,只在用户安装组件并明确点击时调用。

系统原生 OCR 不增加打包体积:macOS 侧复用 pywebview 已引入的 pyobjc(Quartz / Foundation),并在运行时用 objc.loadBundle 动态加载系统 Vision.framework,无需单独安装 pyobjc-framework-Vision;Windows 侧走系统 PowerShell / WinRT,无额外 Python 依赖。PyInstaller 打包时需为这些隐式导入配置 hidden-imports。


4. 总体架构 ​

tidoc App
├─ 核心(必装)
│   ├─ 本地后端(Python,随 App 进程内启动,不对外暴露端口)
│   │   ├─ 解析引擎(移植自 engine.py:XML/PDF 解析、金额闭合校验)
│   │   ├─ 数据层(SQLite + 附件文件仓库)
│   │   ├─ 绑定包 导出/导入(.tidoc,带 HMAC 签名)
│   │   └─ 更新服务(对接腾讯云 COS)
│   └─ PyWebView 原生窗口 + HTML 前端
│       (录入 / 列表 / 搜索筛选 / 编辑 / 备注 / 报账人)
│
├─ 打印导出组件(可选安装,按需从 COS 下载)
│   ├─ 发票 PDF 拼接、付款截图拼接、查验单拼接
│   ├─ 每页信息标注(可选填哪些字段)
│   └─ 方案声明的多份 Word 模板输出
│
└─ OCR 识别组件(可选安装,按需从 COS 下载)
    ├─ 阿里云增值税发票识别(移植自 invoice2docx,SDK 随组件安装)
    ├─ 用户在设置内自填 AccessKey,仅保存在本机
    └─ 手动触发(批量 / 单条)+ 识别提醒视图建议条,不做后台识别

前后端通信走 PyWebView 的 JS↔Python 桥(window.pywebview.api),无需开本地 HTTP 端口,更安全也更简单。


5. 数据模型 ​

Profile(报账人) ​

报账人表示“这张发票属于谁”,每条发票绑定一个报账人。

  • name 姓名,必填。
  • reviewer 审核人,可空。方案控制相关操作的必填要求,以及普通、高级或隐藏显示方式。
  • is_default 默认报账人

收款资料保存在独立 payees 记录中,与材料归属人分开。本地方案、批次和本次导出选择完整收款对象,按报账人收款时使用明确映射。旧全局收款设置只用于一次迁移和兼容转发,不再逐字段拼接来源。

Entry(报账条目,以「一张发票」为核心) ​

  • id、profile_id(报账人)、created_at、updated_at
  • scheme_id、scheme_revision_id:本地方案和固定修订。默认方案变化只影响新条目。
  • title 抬头和 title_profile_id 配置主体 ID:按条目修订解释。通用方案没有预填学校,BITFSAE 包包含其组织抬头。主体隔离见第 7 节。
  • 识别得到、默认只读的字段:
    • invoice_no 发票号码
    • invoice_date 发票日期
    • seller 销售方 / 来源出货厂商
    • total 价税合计,保存精确十进制字符串。缺失与零分别表示,零和负数均为有效金额。
    • buyer_name 购买方抬头、buyer_tax_id 税号。按条目固定修订的抬头目录校验,不以当前默认方案改写票面事实。
    • items[] 物品明细(名称、单位、数量、单价、金额)
  • 用户可改字段:
    • paid_amount 实付金额(默认按发票总额;付款截图上传时提示确认或修改)
    • actual_item_name 实际物资名称
    • notes 条目备注
  • attachments[] 附件
  • status 状态:draft 草稿 / partial 部分材料 / complete 完整
  • check_status 校验:pass / warning / blocked,及 check_message
  • field_history[] 字段级修改记录(不可擦除,见第 6 节)

Attachment(附件) ​

  • type:invoice_pdf / invoice_xml / payment_screenshot / physical_image / inspection_pdf / other
  • role_id:方案材料角色。自定义角色继续使用 type=other,来源角色另保存定义修订。
  • original_name、stored_path、sha256、added_at
  • 付款截图可多张;附件可写附件备注

条目标签 / 备注 ​

  • tags[] 条目标签,供筛选与交叉标记(如「待催办」「已交」);支持批量打标 / 去标。
  • notes 条目备注,用于记录单张发票的处理信息。
  • 附件备注只描述具体附件,不替代条目备注。

Batch(报账批次,运营组工作单元) ​

运营组把「一次要交的这批材料」自由圈选、命名、留存为一个集合:

  • 可跨报账人、跨抬头装入任意条目;一个条目至多归属于一个批次,改选批次即移动归属,避免重复报账、导出和统计。
  • batch_entries 关联每条在批次内可带批次级催办备注(如「张三缺查验单」),与条目自身的记账备注分离。
  • 批次可归档(已提交后归档),可整批导出 / 打印 / 汇总;批次统计给出条数、合计、每报账人小计与缺件数。批次可填写「批次备注」,用于记录用途、注意事项或交接说明,并在批次栏下方与归档确认中展示。
  • 归档后,该批次的条目退出「在办」、进入「已归档」;未进批次或属于未归档批次的条目留在在办。打开某个具体已归档批次时查看其完整成员;在批次内确认归档后视图随批次转入「已归档」,不跳回全部在办。全库防重复不因归档而放开。
  • 批次只引用条目,不持有材料;删批次不动条目,删条目由外键级联清理关联。
  • 批次新建默认方案、字段、收款选择和输出覆盖按本地方案保存。混合方案批次不会共用同名字段,也不会重绑定已有条目。

主界面工作流 ​

  • 顶栏只放全局动作:导入、设置、批量导入、新建。
  • 工具条第一行放搜索框(宽度随屏宽伸缩、尽量占满)、抬头 / 报账人 / 标签筛选、高级筛选和排序;窄屏只压缩搜索框,不让它独占一行。第二行放批次栏和「全部 / 待补材料」等状态预设:宽度足以完整显示批次栏时,批次栏在左、状态预设靠右,共用一行以压低工具区高度;放不下时退回上下两行,状态预设在上、批次栏在下。批次归属与“未进批次”浏览通过批次栏和条目卡片直接管理。
  • 条目列表默认多栏显示;点击卡片或按空格切换选中,通过“打开详情”按钮或 Enter 打开详情。
  • 抬头用接近纸面的淡色纯色背景区分,卡片左右内边距对称。浅色和深色主题共用语义色;物资名称前另有一个抬头色小圆点,与名称垂直居中并随名称一起换行;圆点不沿用底色和文字色,而是单独一组饱和、色相拉开的颜色(浅色和深色主题各一组),方案里没有的抬头显示灰点,没有抬头的卡片留空位以保持名称对齐;按抬头分组或已筛选到某个抬头时整页同一个抬头,不显示。选中后在抬头底色上叠一层淡淡的主色,边框换成柔和的主色线,左缘内侧多一条短竖线作为形状线索,不加粗外框;键盘焦点仍是外圈光晕,两种状态互不混淆。悬浮或键盘聚焦可读取完整抬头。
  • 条目卡片上的报账人和批次徽章是就地编辑入口;发票、付款截图、实物图、查验按钮左键继续添加或替换,右键在已有材料时调用系统打开。
  • 支持卡片勾选、Shift 范围选择和 Cmd/Ctrl+A 全选当前列表;选中后再显示移动批次、标签、汇总、导出、打印、删除。
  • 选择变化只更新受影响卡片的外观与选择语义,焦点切换只更新前后两张卡片。全选、取消和分组选中保留列表节点与滚动位置;分组标题同步显示整组选中状态。前端索引只持有当前列表节点,重绘时清空,单卡刷新时替换。
  • 取消选择只访问此前已选的卡片,选择栏只写入改变的文字和状态。取消按钮收起后,焦点回到当前卡片且不滚动。键盘导航按实际显示顺序索引卡片;范围选择沿用筛选结果的顺序。列表重绘先在文档片段中构建,再一次替换内容。
  • 设置、报账方案、收款信息、导出记录、更新和打印导出窗口点击后立即占住层级(防止重复打开),但加载窗口在 100 ms 后才显示:快速加载直接出现最终窗口,较慢时显示与最终窗口同宽的骨架加载页,内容到达后在原层级替换,并从加载页当前的高度、透明度和遮罩深浅过渡过去,不再先弹小窗再跳成大窗。窗口打开时遮罩淡入、窗口轻微上移淡入;关闭时留一个不可交互的快照淡出(原窗口立即移除,焦点和下层窗口刷新不受影响);窗口内内容大幅变化(更新检查结果、导出检查结果)时高度平滑过渡。动效只改透明度、位移和窗口高度,时长 130–200 ms,系统开启「减少动态效果」时全部关闭。每次加载持有自己的窗口引用;窗口已关闭时丢弃响应,重新打开互不影响。设置的路径、偏好和本地应用信息通过一次桥调用读取,组件状态另行检查,且不持有后端锁:打印组件的 --capabilities 结果按可执行文件的 SHA-256 缓存到组件目录的 capabilities-cache.json,文件未变时跨启动复用(超时或无法启动的探测不落盘),安装校验的哈希按文件大小和修改时间复用;目录统计在设置显示后运行,仅捕获数据根时持有后端锁,文件遍历不占用数据库锁。
  • 子页面返回设置时只更新对应摘要,不重建表单;保留输入、折叠状态和滚动位置。弹窗采用静态遮罩和即时开关,避免整页背景模糊及缩放重绘;层级没有变化时不重复写入主界面的 inert 状态。自绘下拉批量读取样式后再包装,关闭弹窗时回收脱离文档的下拉记录。
  • 卡片点击和取消即时反馈,连续点击每次切换选择。列表重绘不播放卡片入场动画,悬浮只改变边框和轻量阴影,保持卡片位置。滚动时收起提示并暂停悬浮测量;鼠标停留 160 ms 后显示,已有提示之间直接切换,键盘聚焦即时显示;整张卡片的抬头提示需停留 600 ms,且不参与直接切换,避免扫读列表时提示逐张跟随。卡片使用 content-visibility: auto,长列表只排版视口附近的卡片。列表刷新按请求序号只采用最后一次结果;分组浏览时单卡刷新在分组不变时就地替换并更新组合计。
  • 删除批次默认只删除组织关系并保留条目,条目回到「未进批次」;可通过默认不勾选的危险复选框同时永久删除该批次内条目及附件,界面须明确提示不可恢复。
  • 批次栏先用紧凑的「在办 / 已归档」分段切换工作面,再在同一行展示当前工作面的批次;再次点击当前具体批次会返回所在工作面的汇总视图。批次操作通过悬浮提示和批次菜单发现。单条或批量条目使用统一的目标批次选择器移动归属,也可移到「未进批次」或新建批次。

存储布局(结构化、清晰保存在电脑一处) ​

<数据根目录>/
├─ tidoc.sqlite            # 结构化数据
├─ attachments/
│   └─ <entry_id>/
│       ├─ 发票.pdf
│       ├─ 发票.xml
│       ├─ 付款截图_01.jpg
│       ├─ 付款截图_02.jpg
│       └─ 查验单.pdf
├─ adapters/               # 已安装包、不可变资源和安装日志
├─ export_jobs/            # 导出快照、资源引用和任务结果
├─ exports/                # 生成的绑定包、汇总文件、打印件
├─ components/             # 本机安装的可选组件
└─ updates/                # 待更新文件

数据根目录默认在系统应用数据目录,可在设置里改到用户指定位置。


6. 防篡改与修改追踪(核心顾虑) ​

目的:防止用户绕过软件手工改数据造成混乱甚至欺骗。分两层,强度定为「够用」:

6.1 绑定包 HMAC 签名(检测级,够用) ​

导出的汇总文本 / 绑定包内嵌一份 HMAC 签名清单,密钥内置于软件。导入时校验:签名不符即判定「已被外部修改」,醒目标红,不静默接受。达到「程序能识别、人手工改就会被发现」的效果。这是防篡改检测,非加密;已确认不上非对称签名。

6.2 字段级修改标记,不可擦除 ​

  • 每个可改字段同时存 origin(识别原值)与 current(当前值)。
  • 人工把 current 改为不同于 origin 的值后,该字段永久打上「已人工修改」标记;付款截图 OCR 等自动值单独记录来源,不冒充人工修改。
  • field_history 记录每次改动的时间、旧值、新值、操作人。
  • 标记与历史不可删除,随条目一起导出,接收方能看到哪些被改过、改成什么。

识别精度:旧仓库实测识别较准,用户一般无需改发票号码、总金额等关键字段。关键字段在软件内默认只读;确需修正走「标记为人工修正」的特殊流程并留痕。


7. 抬头与主体隔离 ​

抬头目录属于方案修订,包含名称、税号、简称和语义色。通用方案保留真实购买方,BITFSAE 包提供北理工及教育基金会的公共默认值。修改目录生成新修订,旧条目继续按原修订识别和校验。

  • 解析和校验显式接收条目或本次导入的上下文,多个方案及 API 实例不共享全局抬头配置。
  • Word 和材料 PDF 每个文件只含一个主体。组键同时考虑名称和税号,税号未知时使用稳定标识,避免同名误合并。
  • 跨抬头 Excel 可保留总览,列中注明主体;附件 ZIP 按主体目录隔开。绑定包保留逐条来源,接收时仍在全库查重。
  • 已配置主体的税号缺失、不符或与名称矛盾时提示核对。空抬头目录不限制购买方范围,也不套用其他团队的学校默认值。

8. 核心功能(逐条对应最初构想) ​

8.1 报账人与收款信息 ​

  • 报账人只表示发票归属,字段为姓名和审核人。
  • 普通使用时默认绑定默认报账人;新增到两个报账人时自动开启代填模式,此后新建 / 批量导入时直接选择报账人。用户仍可在设置中手动关闭。凡弹窗内需要选择报账人的位置,都提供就地新建入口,创建后保留当前任务并自动选中新身份。
  • 条目详情可修改报账人并留痕,也可在选择器旁就地新建报账人。
  • 收款对象供需要收款段落的输出使用。输出声明 single、by_claimant 或 none,无收款段落的输出不要求账号。字段定义与选择语义见适配开发手册。

8.2 上传与录入 ​

  • 发票 PDF:上传时提示「推荐同时提供 PDF + XML,识别更准」。
  • 付款截图:支持一张发票多张截图。报账方案设置中的“付款识别方式”默认为本地识别,上传图片时用系统本地 OCR 尝试识别付款金额;批量拖入时,识别金额只对应一个候选条目才自动绑定,没有匹配、识别失败或存在同金额条目时逐份手动选择。单张截图识别金额与发票总额不一致时必须让用户确认保持当前值、采用截图值或手动填写,不能静默覆盖。OCR 自动写入的实付会记录 payment_ocr 来源;用户随后手工改值时切换为人工来源。删除或改类最后一张付款截图后,仅在实付仍由 OCR 控制时恢复为发票总金额,人工金额不覆盖。关闭 OCR 后不读取或套用截图金额,单独拖入截图直接手动选择条目,并且不显示误导性的“未识别到付款金额”。Windows 识别前需把手机长截图缩放到 Windows.Media.Ocr 的尺寸限制内。
  • 本地识别记录识别规则版本与材料摘要。识别规则版本独立于软件版本:普通软件升级保持不变,只有对应的发票或付款截图识别逻辑、结果协议发生变化时才单独更新。任一列表视图选中条目后都可打开「重新识别」,分别勾选发票或付款截图;已经由当前规则处理且原文件未变化的材料自动跳过。付款截图识别采用条目固定修订的设置,切换默认方案不改变旧条目的行为。重新识别只刷新结果与提醒,不覆盖用户已确认的实付金额。截图未识别到金额,或截图合计与发票总额不一致时,合并为条目的非阻断「识别提醒」。
  • “实付默认等于发票金额”由方案设置控制。开启时用有效发票金额初始化实付,缺失金额仍留空。修改默认值不批量改写旧条目,也不覆盖人工填写的差异金额。
  • 发票查验单 PDF:可单独上传,也可从条目卡片 / 详情进入「在线查验」。查验窗口使用比初版更大的常规窗口,但不默认最大化;在线流程分步预填并触发官网对发票号码、日期与价税合计的原生校验,日期通过官网 datepicker 写入并主动收起,避免用户再点空白处确认。官网验证码由用户读取和填写,tidoc 不代填、不绕过验证。
  • 应用不会后台打开官网或自动检查;只有用户明确点击「打开查验官网」才联网。查验窗口不向官网暴露主窗口的 JS API。考虑到部分省级验证码接口使用会被 WebView 拒绝的历史证书,应用会在创建任何原生 WebView 前启用 pywebview 的证书错误放行,并在本次运行期间保持;这样 Windows WebView2 能在初始化时注册证书错误处理器,macOS 的验证码子请求也能使用同一策略。主窗口只加载本地文件,唯一的内嵌远程页面仍是用户主动打开的查验平台;这属于内部兼容措施,不在日常界面暴露。
  • 官网结果页和旧版 PrintArea 目标位于 iframe 中。查验窗口持续发现同源结果 frame,将官网选定的查验明细复制到顶层打印容器;用户点击官网「打印」后进入系统原生打印流程,并由打印样式固定为横向,不再使用 WebView2 PrintToPdfAsync 或 WKWebView createPDF 直接导出。
  • 系统“另存为 PDF”对话框不提供应用可可靠预设的输出路径。会话默认监测下载、桌面和文档目录,用户可在「设置 → 查验单」选择并持久化一个额外目录;目录不能位于 tidoc 数据根内。成品保存到任一监测目录后,界面提示用户等待约 1–2 秒;会话先核对查验单类型和发票号,再由附件仓库复制到发起查验的条目、刷新材料状态并关闭查验窗口。若用户开启“归档后清理原 PDF”,只在内部复制成功后通过 Send2Trash 把外部原文件移到系统废纸篓 / 回收站;清理失败不回滚已归档材料,并明确提示原文件仍在。打印前把顶层文档标题设为“查验单-发票号码”;macOS 还会把同一名称写入 NSPrintOperation 的任务标题,避免系统忽略网页标题。该名称作为保存对话框的建议文件名;不要求用户回到 tidoc 再点击保存。手动上传已有 PDF 保留为明确兜底。
  • macOS 官网「打印」仍兼容子 frame 场景:创建原生打印任务时只为查验窗口设置横向,不修改其他窗口的打印设置。该路径作为官网操作的兼容兜底,不占用 tidoc 弹窗的主操作位。
  • 支持先提交一部分(如只有截图)甚至只是草稿(如只填了实付金额),状态标为 draft/partial,随时补齐。

文件夹批量导入 ​

  • 发票 PDF 和 XML 都可用于创建条目。文件夹和多选扫描将可识别号码的独立 XML 单独成组,无法识别的 XML 留在未分组列表中;需要打印票面的输出另查 PDF。
  • XML 只是增强识别准确度:优先按发票号配对;文件名近似匹配只作为兜底,短数字文件名不会参与模糊匹配,避免 26.pdf 误匹配到 2026... 或长票号。
  • PDF 与 XML 按同一发票配对,XML 独立组也在本次导入范围内查重。付款截图、查验单不单独创建条目。
  • 拖拽 / 粘贴混合材料时,发票 PDF / XML 先进入导入预览;确认时同时选择报账人和目标批次,可选已有批次、「未进批次」或随本次条目新建批次。查验单按发票号、付款截图按 OCR 金额绑定到唯一匹配条目。未匹配或匹配不唯一的材料进入「绑定材料」对话框,逐份选择条目。若条目详情处于打开状态,粘贴的材料优先归入当前条目,再继续执行材料类型、发票归属和付款金额校验,并刷新详情状态。
  • 扫描会识别常见查验单 PDF(含税务查验平台 PDF),这类文件只提示跳过,不会创建发票条目。
  • 扫描预览说明将创建多少条、匹配到多少 XML、哪些 XML 先导入后补票面,以及哪些文件无法识别或已经重复。用户勾选要导入的发票组。
  • 创建前按发票号在全库检查重复;发票号未识别时以发票 PDF / XML 文件摘要兜底。命中重复时保留导入预览并在对应分组显示原条目的销售方、报账人和处理建议。单条新建与批量、拖拽、粘贴入口共用同一规则。
  • 每个分组按“全部成功或全部清理”处理:解析、校验或附件复制任一环节失败,都删除该分组已经写入的条目和附件;其他成功分组不回滚。随发票一同拖入的材料只在至少有条目成功创建后匹配,避免失败时意外落入全库其他条目。
  • 批量导入负责把发票文件变成可整理条目。补材料与改字段继续使用条目详情和紧凑绑定对话框。

8.3 识别与校验 ​

发票数据来源优先级 ​

用户一张发票的结构化数据(发票号码、金额、明细等)可以从多个来源获得。按准确度和便捷性排序,优先级如下:

  1. 原生电子发票 XML(最优) —— 用户向商家索要,程序解析零误差。UI 应引导用户尽量提供 XML。
  2. 程序解析发票 PDF —— 上传 PDF 后自动用 pypdf 提取文本并识别字段,兼容左右分栏中的空税号、税号文字层落在标签上方、常规逐行明细、按列拆分的文本流、同品名折扣行、数量 / 单价 / 金额列粘连、空单位/数量及规格末尾字母与中文单位粘连,大多数数电发票可准确解析。
  3. 用户手动填写 / 编辑 —— 识别结果不准或无法识别时,用户直接在界面上编辑修正。编辑体验要足够快捷(inline 编辑、Tab 跳转、明细行增删),使手动填写的成本尽可能低。
  4. 消费外部 Aspose 版面 XML(下下策) —— 程序本身不做 PDF→XML 转换,只解析用户用 Aspose 等外部工具导出的版面 XML(engine/parser.py::parse_aspose_xml)。流程长、收益有限,作为最后手段保留;后续若程序直接解析 PDF 的能力提升,此路径可废弃。

设计原则:不要把精力过度投入边缘 PDF 识别的打磨上。大部分发票应该走路径 1 或 2;识别不准时,让用户能快速手动修正(路径 3)比把识别率从 90% 提到 95% 更实际。

解析流程 ​

导入即自动解析(优先 XML,其次 PDF 文本),提取发票号码、日期、销售方、价税合计、抬头、税号、明细;检查明细识别完整性、抬头一致性,以及已配置抬头对应的购买方税号是否存在且正确。warning 在界面称为「识别提醒」,用于缺明细、明细识别合计与发票总额不一致、抬头未识别、购买方税号缺失 / 错误 / 与抬头矛盾等自动提醒,不阻断材料齐备;blocked 在界面称为「严重问题」,仅用于抬头分区冲突等会造成材料混入的问题。在顶部「识别提醒」快捷视图中,批量操作栏按需显示「重新识别」,用条目已有的原发票 PDF / XML 原子替换识别明细并刷新提醒;用户修改过的实际物资名称、实付和备注保持不变。

附件归属校验 ​

  • 发票 XML 必须能解析为官方电子发票 XML,非发票 XML 不作为发票附件接收。
  • 发票 PDF 不能是查验单 PDF;能解析出发票号时必须与当前条目一致。
  • 查验单 PDF 先读文本 / 元数据中的发票号;读不到时,macOS 使用 Vision、Windows 使用 Windows.Media.Ocr 对查验单发票号区域做系统原生 OCR。识别到发票号后必须与当前条目一致;识别不到但能确认是查验单时允许用户添加。
  • 付款截图金额可用于给出唯一归属建议,但不作为强校验依据;没有唯一候选时必须手动绑定,用户已有手动实付金额时不自动覆盖。

8.4 汇总与导出 ​

  • 界面内汇总给用户看:条数、合计金额、抬头分布、前若干条预览,不直接展示开发者式 JSON。
  • 总览 Excel:通用输出包含基础字段,方案输出按声明设置列、行模式、格式和合计。用户文字按文字保存,发票级金额不随明细重复合计。
  • 附件整理包:按材料角色、主体和配置目录命名,保留原文件及清单。通用导出与方案输出复用同一写出器。
  • 绑定包 .tidoc:给 tidoc 之间互相导入继续整理,保留报账人姓名 / 审核人、每条发票的归属、条目、附件、修改历史、阿里云识别结果及签名校验。可从文件选择器导入,也可在列表、卡片或详情处拖拽 / 粘贴单个绑定包;所有入口统一先做完整性检查并进入确认预览,不把绑定包当作普通附件。为避免同时出现多个需要人工确认的预览,一次混入多个绑定包或其他材料时提示分开导入。预览按包内报账人分组勾选;若本机当前报账人与包内身份完全匹配,默认只选该人,否则保持全选。每条在落库前显示「新增 / 补充 / 已有」、将补充的信息以及本机现有批次。默认仍由上方的报账人勾选和统一批次设置决定,不增加日常操作步骤;仅在个别情况下逐条取消勾选,或展开「逐条调整」覆盖某条的目标为已有批次、指定名称的新批次或「未进批次」;对本机已有且内容无变化的条目,只有显式逐条指定时才调整其批次。接收方仍可修改所选身份、追加统一标签。有在办批次时统一设置默认选择已有批次,没有时默认新建并按绑定包文件名预填名称;选择不装入批次需要二次确认。接收方按姓名 + 审核人复用已有报账人,否则创建新报账人;导入后报账人达到两个时自动开启代填模式。条目 / 附件备注和条目标签是否带出由报账方案设置中的两个独立开关控制,默认包含。导入按整个包原子处理,任一条目、身份或附件失败时回滚本次全部写入;发票号或发票文件摘要命中现有条目时,以本机非空值优先,不覆盖成员已经整理的内容,只追加缺失材料、合并备注和普通标签,并补齐空缺基础字段、明细与云识别记录;「已报销 / 未报销」「已支付 / 未支付」属于互斥状态标签,以负责人包内状态替换本机相反状态,避免一条记录同时出现矛盾标签。无新增内容时显示为「已有·无变化」。用户明确确认接收完整性异常的包后,新导入或被补充的条目都标记为严重问题,不以普通条目静默落库。

8.5 条目管理(批量、便捷) ​

  • 整条删除、批量删除;删除数据库记录时同步删除附件目录,数据库删除失败则恢复暂存的附件目录。
  • 修改 / 补充 / 替换附件;文件复制、重命名与数据库记录按可回滚顺序处理,避免失败后留下半份附件或失效路径。可打开文件、在系统文件管理器里定位文件,macOS 下 PDF 直接走系统预览。
  • 修改允许修改的选项(实付金额、实际物资名称、条目备注等);多选条目可批量修改报账人,每条分别留痕。
  • 关键信息(发票号码、总金额、抬头、税号等)软件内禁止随意改;确需修正走「标记为人工修正」流程并留痕。

8.6 绑定压缩包(.tidoc)导出 / 导入 ​

一个 ZIP 包,内含报账人、条目归属、附件、汇总和 HMAC 校验清单。版本 2 增加身份清单;版本 3 增加云识别结果及修正快照;版本 4 增加条目时间及增量补充;版本 5 增加公共规则投影、来源修订、材料角色和允许交换的扩展值与历史。继续读取版本 1~4;旧客户端需更新后接收版本 5。导入的云识别结果不计入接收方本机调用次数。

备注和标签采用方案的带出建议及用户明确选择。扩展值按 include、ask 和 never 处理;禁止分享的原值和历史值一起移除。收款对象、私人方案实际值、密钥及导出快照不进入公共规则投影。交换语义见开发手册。

完整性检查对附件执行流式 HMAC,不把大文件整体读入内存;条目与汇总数据在校验后直接复用已解压内容。本机重复条目的材料、历史、识别记录和批次用批量查询生成预览;用户确认导入时,只要文件大小和修改时间未变,复用该次已验证结果,避免对整包再做一次相同校验。

(条目备注 / 附件备注 / 条目标签的概念区分见 README「概念区分」节,字段定义见第 5 节。)

应用和绑定包图标按平台分开生成:Windows 使用自带透明圆角的多分辨率 ICO,避免系统直接显示无圆角方图;macOS 使用完整方形 ICNS,由系统按当前版本规范裁切。Windows 安装器把 .tidoc 注册为 Tidoc 绑定包,资源管理器显示明确安装的绑定包图标,双击直接启动 Tidoc 并进入该包的导入预览,卸载时自动清除关联;macOS 应用包声明同一扩展名的文档类型和图标。

8.7 分类 / 筛选 / 搜索 ​

按抬头、报账人、日期、销售方、金额区间、状态、条目标签、条目备注、付款截图数量和关键词过滤,也可通过批次栏按「在办」、批次、「未进批次」或「已归档」聚焦;付款截图数量提供「多张(2 张及以上)」快速条件。筛选条件直接通过对应控件的浅蓝激活态表达,高级条件同时点亮具体筛选框和「高级」入口,不在工具条下方重复生成条件标签;下拉控件统一箭头、边框和交互状态,展开后的选项列表同样由应用自绘(原生 select 展开的列表由系统控件绘制,与界面其他浮层不一致):原生控件仍留在页面里承担取值与 change,触发器与浮层由前端组件负责,并保留方向键、Home/End、Esc、首字母跳转等原生键盘行为和 listbox 语义。抬头下拉以设置里配置的抬头为固定项,并动态补入数据库中实际出现的其他抬头,存在未配置抬头时在筛选控件显示轻量提示点。常用视图(全部、待补材料、识别提醒、已修改、齐备)作为筛选条内的快捷条件,不拆成左侧多个“页面”。其中“已修改”仅指实付金额与发票总金额不一致,其他字段的修改仍正常留痕,但不进入该视图;悬浮徽标显示实付以及同时存在的其他字段修改详情。通过卡片勾选、Shift 范围选择(按屏幕上的卡片顺序,分组浏览时也一致)和 Cmd/Ctrl+A 选中条目后,再做批量修改报账人、汇总、导出、打印或删除,重新识别等长任务按条目显示完成进度。

8.8 草稿与部分提交 ​

状态保留 draft、partial 和 complete,列表可按状态筛。

状态按条目固定修订的材料、字段和条件规则推导:

  • complete(齐备):发票及当前修订所需材料、字段和规则满足,且无核心阻断项。通用方案仅默认要求发票;BITFSAE 要求由内置包声明。
  • draft(草稿):还没有任何材料。
  • partial(部分材料):介于两者之间。

附件或可改字段变化后自动重算并持久化,供列表筛选与导航分区使用。列表卡片把发票 / 实付 / 付款 / 实物 / 查验状态合并到右侧快捷按钮里;同一条目有多张付款截图时,付款按钮直接显示张数。不再重复显示齐备 / 部分材料徽标;卡片优先显示实际所属的批次名称,没有批次时不占位置。启用实物图要求时,详情按钮缩短为“详情”并与实物按钮同排;未启用且条目没有既有实物图时,卡片入口和详情材料分组都隐藏。

方案设置保存抬头、新建默认值和材料要求,并形成新修订。旧条目只在用户明确预览并应用重绑定后改用新规则。材料齐备与本次可导出分别计算:XML 可满足日常发票要求,需要票面的 PDF 输出另查发票 PDF。


9. 打印导出组件(可选安装,主要给运营组) ​

  • 按需从 COS 下载安装;不装不影响核心录入功能。每个版本装进独立版本目录,安装成功后只保留当前版本,旧版本目录随即清理,避免历史版本累积占磁盘。
  • 发布版核心调用数据目录中安装并校验过的独立组件;从源码开发运行时优先调用当前工作区组件,避免本地修改被历史安装版本遮蔽。
  • 选择范围:跨不同人合并,也可只选某些人某些部分;支持批量与快速勾选。
  • 输出的名称、数量、ID、模板和默认选中项来自方案。一个方案可以声明多份 Word,也可以只输出 PDF;不要求文档叫“报账说明”或“验收单”。
  • 材料 PDF 按声明选择角色、顺序、A4 图片布局和编号。BITFSAE 默认按条目连续排列,付款截图横向 A4 每页两张;其他方案使用自己的配置。
  • 通用材料 PDF 供跨方案和未安装来源团队包的资料使用,按真实主体强制拆分并选择内置及自定义材料。缺票面或不可转换材料在预检中提示,不借用其他团队模板。
  • Word 使用纯数据上下文和受限 docxtpl 模板。历史条目缺明细时,导出投影可按条目固定识别上下文安全重解析原 PDF,不写回数据库。仍无明细时使用标记清楚的汇总行;单位和数量保留空值或采用包声明的缺省值。
  • 预检按本地方案、修订、主体、收款对象和必要批次分组。选中输出有阻断项时整体停止;所有文件成功后才发布任务结果。
  • 历史任务保存当次数据和资源摘要。重新生成读取原快照,附件缺失或变化时说明原因,不以今天的数据冒充原件。
  • 初始任务记录文件写入失败时,数据库任务标为 failed,清理半写文件并使原预览失效;数据库快照和原始材料保留,恢复写入条件后可按原快照重新生成。

10. OCR 识别组件(可选安装) ​

已实现。 核心另有一层轻量的系统原生 OCR 兜底,仅用于查验单 PDF 发票号归属确认和付款截图金额提取,不用于重识别发票明细;通用发票识别由本组件承担。

默认核心的发票识别仍走 XML 优先、PDF 文本其次,不打包阿里云 OCR SDK,保证体积小(见第 3 节)。阿里云识别作为独立可选组件,从 COS 按需下载;识别调用移植自参考仓库 engine.py 的 parse_aliyun_ocr_invoice,并按当前 API 文档修正字段名(purchaserName / purchaserTaxNumber / specification 等)。

定位与依赖下沉

  • 归入可选组件层,与「打印导出组件」并列;组件独立版本、独立更新,安装到 components/ocr/<platform>/,更新对话框单独一行管理。
  • 阿里云 OCR SDK 与拆页所需的 pypdf(见 requirements-ocr.txt)随组件安装,不进核心包。
  • 组件进程接口与打印组件同构:--input/--result JSON 文件 IPC + --self-test 自检(不联网)。核心通过 services/ocr.py 适配层探测(python / external / missing / repair 四态)与调用。

调用方式:用户自填阿里云 Key

  • 用户在「设置 → 阿里云 OCR」填写自己的 AccessKey ID / Secret;不内置密钥、不走统一云端中转。界面建议使用只授权「文字识别 OCR」的 RAM 账号 Key 以限定泄露损失面。
  • Key 存本地 SQLite meta(tidoc.ocr.accessKeyId/Secret),不写入导出、不随绑定包外传;界面只显示掩码,更换时需完整重填 ID 和 Secret。
  • 密钥通过权限 600 的临时 input.json 传给组件子进程,不进命令行参数(避免 ps 泄露)。
  • 未装组件或未填 Key 时,云识别入口引导安装 / 填写,不静默失败。

触发时机:只有用户点击,不自动花钱

  • 批量:选中工具条「云识别」按钮 → 预检确认对话框(发票张数、按 PDF 页数计算的实际调用次数、计费提示、跳过原因)→ 逐张识别并汇报进度。
  • 单条:条目详情「阿里云识别」区块、条目右键菜单。
  • 建议条:识别提醒视图顶部提示「N 条可用云识别补齐」并一键执行——自动建议、手动触发,替代早期的 auto/always 档位设想(付费接口不在用户无感知时调用)。
  • 已有 XML 权威数据的条目默认跳过(可勾选「仅作比对」纳入);无发票 PDF 的条目自动跳过;多选时默认跳过发票文件未变化且已有结果的条目,可取消跳过后重新识别。

结果持久化(防重复计费)

  • 单页发票调用一次;多页 PDF 在组件内临时拆成单页逐页调用,全部成功后按原页序合并明细,再对整张发票做金额闭合。紧随商品、名称为空且金额为负的折扣行并入上一商品;阿里云把完整商品名称连续返回两遍时折叠为一次。任一页失败则整张失败,不保存残缺结果。
  • 每次识别(含失败)都追加写入 ocr_results 表:原始 data JSON、解析快照、发票文件 sha256、实际 API 调用页数、金额闭合结果、待确认差异列表、时间;自动补齐时同时保存字段及明细的补齐前 / 补齐后快照,详情直接展示。
  • 结果与识别时的发票附件 sha256 绑定;发票文件被替换后旧结果标记过期。
  • 阿里云结果随 .tidoc 绑定包迁移,导入后保留“已识别 / 待确认”状态和详情;导入记录与本机实际调用分开计数。设置中展示本机累计调用次数,方便对账;重复识别不覆盖历史。

结果应用:软件结果优先,云端补空与比对

  • 自动补齐:本地为空的比对字段(发票号、日期、销售方、价税合计、购买方抬头、税号;抬头 title 关系分区隔离,绝不参与 OCR 采用)直接填入,记 [阿里云OCR] 留痕。
  • 软件优先:销售方和明细不由阿里云自动覆盖或补齐。销售方全角 / 半角标点视为一致;规格型号不参与差异判断。
  • 待确认:发票号、日期、总额、购买方相关字段的非空冲突,以及名称、单位、数量、金额或行数有任何差异的明细,只在详情「阿里云识别」折叠区展示,由用户逐项或整套采用;条目卡片挂 OCR 徽标直到处理完。销售方冲突标成「保留软件值」,不产生待确认徽标。差异判定只看云端确有值且与软件值不同的单元格:云端为空的单元格不算差异(没有可采用的值),数量按数值比较(2 与 2.00000000 相同);云端整体未返回明细时不进待确认,详情提示已保留软件明细——此时没有可采用的行,进待确认会让徽标无法消除。
  • XML 是税务口径权威数据:XML 来源条目只补空 + 比对。

11. 联网更新(腾讯云 COS) ​

  • COS 放 manifest.json:核心与各组件的最新版本号、下载地址、SHA256、变更说明。
  • 默认在启动完成后检查,并在应用持续运行时按到期时间继续检查(每小时最多联网一次,设置中可关闭)→ 比对本地版本 → 在设置旁出现下载动作 → 用户点击后立即显示 0% 并在后台下载 → 校验哈希与解包 → 动作切换为重启 → 退出旧版本、原位替换并自动打开新版本 → 首次启动显示升级前后版本和本次变化。Windows 核心更新在服务器支持时使用四路分段下载,各分段可从已有进度续传;服务器拒绝分段或并发连接失败时自动复用连续部分并退回单连接续传。界面分别显示下载速度与已下载大小、完整性校验和更新文件准备状态。检查本身不自动下载或安装;安装助手在新界面成功启动前保留旧版本用于失败回退。安装位置不可写或源码运行时退回平台安装包。
  • 核心与「打印导出组件」独立版本、独立更新,避免整包重下;这是控制体积和更新负担的关键。
  • 发布核心版本时不再同步抬高打印组件版本;只有 tidoc_print 代码、打印依赖或核心与组件的 JSON 调用协议变化时才单独增加组件版本。
  • 测试版通道:带预发布后缀的 tag(v0.1.39-beta.1)只写入 manifest-beta.json,不改写正式版清单;用户在设置里打开「接收测试版更新」后,客户端同时读取两份清单、每个组件取较新的版本(按 semver 比较,同版本正式版高于测试版)。关闭后只收正式版,不会降级,已下载未安装的测试版包被丢弃。默认关闭,稳定版用户不读测试版清单。测试版发布不会重新上传正式版清单已在使用的可选组件版本,避免覆盖其校验值对应的文件。
  • 设置中只保留一个「软件与组件」管理入口,在同一行展示核心版本、打印导出组件状态和可用更新提示;两者不再以重复入口指向同一个管理页。

发布流程、COS 路径与客户端更新行为的运维细节见 docs/UPDATE.md,此处只保留设计意图。


12. 人性化与 UI 细节 ​

识别提醒直接显示在条目上;可改和只读字段采用不同样式;人工修改字段带角标;抬头采用方案声明的语义色。拖拽、批量导入和导出等长任务立即显示阶段,完成或失败后切换为结果提示。

折叠标题共用固定尺寸和位置的三角箭头,收起时只保留紧凑标题行,并保留原生键盘交互。收款信息按内容控制窗口宽度,列表操作与默认选择沿用设置行。导出诊断按阻断、提醒和提示分组,同一问题合并显示,保留受影响发票信息;文件预览单列文件名、类型和收款信息,避免混入提醒。打开打印窗口时,默认输出选择采用本地方案的当前设置;实际模板、字段和材料规则仍按条目的固定修订解释。

12.1 外观主题 ​

  • 顶栏提供一个无文字的明暗切换图标:浅色时显示月亮、深色时显示太阳,点击后直接锁定为另一种外观。切换时从触发按钮位置用约 0.34 秒柔和揭示新主题,旧版 WebView 回退为短暂的颜色与表面过渡;系统开启“减少动态效果”时不播放动画。设置中的「外观主题」另提供跟随系统、浅色、深色三种模式,切换后立即生效。
  • 深色主题延续安静、密集的工作台风格:画布与表面用纯中性近黑灰(不带蓝紫偏色),由画布、面板、浮层逐级提亮,去掉整体色雾;强调色使用天蓝(色相约 210°),避免薰衣草蓝带来的偏紫观感;状态色分别保留抬头、齐备、提醒、严重问题和人工修改的业务含义,不采用简单反色。详情里必需但未上传的材料组用琥珀色虚线框提示,重复的「待补」文字提示不再出现。
  • 界面中的「tidoc」字样(顶栏与设置关于)使用自适应 SVG 字标:笔画颜色走 currentColor,浅色为墨色、深色为白色,随应用主题即时切换。
  • 颜色由语义 CSS 变量统一映射,表单、日期控件、下拉选项与自绘的下拉浮层、滚动条、筛选激活态、选择态、拖拽态、设置、更新与 OCR 区域使用同一套主题。固定品牌图形和高对比提示使用独立颜色,避免被主题色误改。
  • localStorage 保存一份启动前可读取的偏好,SQLite 通用偏好保存长期状态;页面在加载样式前解析主题,原生窗口也按已保存模式或系统外观选择初始背景,减少启动时的明暗闪烁。跟随系统模式监听操作系统变化,无需重启。
  • 深色仅影响应用界面。打印媒体强制回到白底黑字;实际材料由独立文件与打印导出组件处理,不修改发票、付款截图或查验单内容。

13. 开发进度 ​

各阶段的实现状态见 README 状态节。本文档只保留设计意图与架构决策,不再重复维护阶段编号与勾选状态。


14. 已定决策 ​

  • 前端:PyWebView 原生窗口 + HTML(双击即开,体积小,界面自由)。
  • 运行方式:同一用户只保留一个主实例;重复启动负责恢复并聚焦已有窗口,携带 .tidoc 路径时转交给已有窗口,在当前弹窗结束后进入导入预览,避免多窗口并发编辑同一 SQLite 数据库。
  • 防篡改:内置 HMAC 检测(够用,不上非对称签名)。
  • 数据存储:SQLite + 附件文件仓库。
  • 不做多人云同步(单机 + 绑定包交换)。
  • 命名:tidoc,后缀 .tidoc。
  • 参考仓库 invoice2docx 只读,engine.py 移植复用。
  • OCR:做成独立可选组件(不进核心包),用户在软件内自填阿里云 Key,仅用户点击时触发(批量 / 单条 + 识别提醒建议条),结果落库并可逐项比对采用(见第 10 节)。

15. 团队适配包 ​

团队通过声明式适配包配置抬头、设置策略、附加字段、材料角色、条件规则和四类输出。公共格式由 schemas/team-adapter/1/ 定义。设置和能力由 tidoc/adapters/registry.py 注册;模板字段及文件名目录来自轻量资源 tidoc_print/context_fields.json,核心与组件读取同一目录。CLI、示例和协议说明见 docs/adapters/。

包加载器检查 JSON Schema、跨文件引用、资源路径、DOCX 结构和能力声明。规则以纯数据表达,不执行适配包代码。方案修订固定在条目上,切换默认方案不会重解释历史条目。个人值、收款账号和历史记录保存在本地数据库,不随公共适配包分发。

应用中的识别和校验显式接收条目修订上下文。engine/validator.py 保留旧独立调用所需的名称及税号常量,但从同一 BITFSAE 内置 scheme.json 读取,不维护学校或税号的字面量副本,也不用于替代应用上下文。

核心发布资源包含 schemas/team-adapter/1/、tidoc/builtin_adapters/ 和共享字段目录。通用包与 BITFSAE 包由同一加载器校验。IPC v2 由核心传入 JSON 上下文和资源表,组件不读取数据库或重新选方案。旧 IPC v1 转换到同一 v2 路径,仅为兼容旧 BITFSAE 请求保留内置模板和组织设置资源。打包位置及自检要求见更新说明。

核心的四类输出在预检中使用共享 context.schema.json 校验完整上下文;打印组件在渲染前再次执行同一检查。源码与外部进程执行相同校验,v1 转换结果也进入此路径。Schema 检查结构、类型和必需字段;资源路径、主体隔离和模板字段权限继续分别检查。缺少 jsonschema 或 context Schema、Schema 损坏时,组件停用 DOCX 和 PDF 渲染能力,自检失败。核心自检也检查该资源。校验诊断只报告 JSON Pointer 和约束,不回显账号、备注等无效值。

共享 Schema 用 $defs 复用条目、行、诊断及齐备结构。export.options 和 completeness 只接受正式声明的属性,未知内部字段不能进入公共上下文。

打印组件 --self-test 通过正式 IPC v2 在临时目录生成 DOCX 和材料 PDF,实际加载并使用渲染依赖。它核对 DOCX 中的中文及金额文本,生成包含发票 PDF、图片和编号的材料 PDF 并检查两页结果,随后清理临时目录。依赖能被定位但不能实际加载时,自检报告失败。自检流程及成品验收边界见更新说明。

批次投影仅提供 ID、名称、文本备注和当前组字段;缺失备注转为空字符串。数据库中的 output_settings、updated_at 等内部属性不进入模板上下文。批次与本次设置先由编排器解析,再投影当前输出的有效选项。

输出选项按核心缺省、包定义、本地方案、批次和本次导出逐层解析,固定值和核心不变量最后检查。同一修订的两个本地副本分别保存字段及收款选择。前端负责提供可达的填写入口,后端负责类型、可见条件和必填校验;界面实现与验收状态见 README。

默认输出遵守“缺少键才继承”的规则,显式 [] 表示全不选。本次空选择也不会恢复默认勾选;默认来源及分组选择的完整语义见输出说明。

导出编排器按实际工作更新资源核对、核心 Excel/ZIP 生成、Word/PDF 生成、产物检查和原子保存阶段。前端每 400 ms 查询 get_export_progress(job_id),接口只读取有界内存状态,不访问数据库、不等待长导出持有的 _api_lock。取消接口同样不等待该锁;核心写出器完成当前有界步骤后检查取消,打印进程沿用已有取消与超时机制。界面按可用的完成数显示进度,不推算 Word/PDF 的虚构百分比。

输出预检和重绑定预览返回时,前端核对表单版本及对话框是否仍打开。用户改选择或关闭窗口后,旧响应不覆盖当前表单,也不启用生成或应用按钮;旧输出预览及时取消。重绑定目标方案的异步加载同样核对请求序号和当前选择,丢弃过期结果。

python -m tidoc.adapter_tools 提供源目录初始化、校验、规则说明、fixture 渲染、样例测试、定义差异和严格打包。打包含 DOCX 的适配包时必须安装打印依赖,并以 fixture 完成真实渲染。自动渲染检查不替代在 Windows、macOS 办公软件中人工检查版式。

16. 文档站 ​

  • 定位:面向使用者的文档站(https://tidoc.bitfsae.com)含简介、开始使用、使用指南、更新与组件和常见问题,另设「开发相关」给开发者。读者大多不了解内部逻辑、代码和内部术语,写作规则见 AGENTS.md 的 Documentation site 一节。
  • 技术:VitePress,源码在 docs/,依赖只写在 docs/package.json,不改变 Python 应用结构;node_modules 和构建产物不提交。
  • 内容归属:使用说明只写在站点页面(docs/intro、start、guide、update、faq),截图放 docs/public/images/,只用虚构数据。设计取舍留在本文档,发布与运维细节留在 docs/UPDATE.md。DESIGN.md、CHANGELOG.md、CONTRIBUTING.md 在仓库根目录,由 docs/dev/ 和 docs/update/ 下的页面嵌入;docs/adapters/ 与 UPDATE.md 留在原位,由站点配置的 rewrites 挂到「开发相关」下;TEAM_ADAPTER_PLAN.md 是实施与验收记录,不进站点。这些文件之间的相对链接在渲染时由 docs/.vitepress/repo-links.ts 改写为站内或 GitHub 地址,源文件不变。
  • 部署:腾讯云 EdgeOne Pages 连接 GitHub 仓库,Root Directory 为 docs,构建配置在 docs/edgeone.json,main 更新后自动构建发布。不使用 GitHub Pages,也不经过官网服务器。项目、域名和证书由维护者在腾讯云配置。
  • 下载入口:「下载与安装」页的下载按钮(组件 docs/.vitepress/theme/components/TidocDownload.vue)指向官网的下载网关 https://www.bitfsae.com/api/downloads/tidoc-core/{windows|macos}。网关读取 img.bitfsae.com/tidoc/manifest.json 后跳转到最新版安装包,所以发新版时文档站不用改。manifest.json 没有开放跨域读取,页面因此不显示版本号和文件大小;需要时由维护者在对象存储上开放跨域,再让组件读取清单。GitHub Releases 和官网首页入口保留为备用。
  • 构建检查:死链接和缺失的截图会使构建失败,修改文档后先运行 cd docs && npm ci && npm run build;.github/workflows/docs.yml 在文档相关文件变化时用同一命令构建一遍,合并前暴露这些问题。
  • 搜索:使用 VitePress 本地搜索。它默认只按空格和标点切词,一整句中文会成为一个词,所以 docs/.vitepress/search-tokenize.ts 把连续汉字切成相邻两字的组合,构建和浏览器两端使用同一个切词函数。