Files
reimburse/README.md
T
2026-09-17 11:12:57 +08:00

109 lines
11 KiB
Markdown
Raw 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.
# 贴票台 · macOS 原生版
SwiftUI 原生报账材料工作台。无需浏览器、Java、Spring Boot、HTTP 服务或用户单独安装 Python。RapidOCR 模型、解释器及 Office 导出依赖随应用内的处理引擎打包,扫描和导出仅通过本地子进程的标准输入/输出交换数据,不监听网络端口。
## 开发环境
- Xcode 26.3macOS 15.7 或更高版本。
- 当前构建为 Apple Siliconarm64);Intel Mac 需要另行构建对应架构的处理引擎。
- 本机 Xcode`/Users/yuxiaoting/Downloads/Xcode.app`。无需修改系统默认开发工具目录。
- 原工程 `../fapiao` 不会被修改,也不是运行依赖。
## 在 Xcode 中运行
打开 `reimburse.xcodeproj`,选择 `reimburse` scheme 和 My Mac,运行即可。构建阶段先核对引擎源码、打包脚本、模板、依赖声明与已打包二进制的指纹;缺失或不一致时自动重新打包,并运行打包引擎的报销单导出回归测试。验证成功才将 `native-engine/dist/receipt-engine-helper.app` 放进应用的 `Contents/Helpers`,并为辅助进程添加沙盒继承权限。没有本地打包依赖时构建会明确失败,不会继续使用旧引擎。
当前开发目录已经准备了本地依赖和引擎。重新下载源码、切换架构或修改 Python 代码后,需先重建引擎:
```sh
python3.12 -m venv .build-tools
.build-tools/bin/python -m pip install -r native-engine/requirements.txt
bash native-engine/build-engine.sh
```
依赖安装只发生在开发打包时,最终用户不需要上述工具。`dist`、依赖环境及构建产物不提交到版本控制;发布流程需先生成引擎,再构建应用。
```sh
DEVELOPER_DIR=/Users/yuxiaoting/Downloads/Xcode.app/Contents/Developer \
xcodebuild -project reimburse.xcodeproj -scheme reimburse \
-configuration Debug -destination 'platform=macOS' \
-derivedDataPath .derived-data build
```
## 使用流程
1. 选择包含 `发票``付款截图``实物照片` 子目录的材料根目录,或从访达将一个材料根文件夹拖入“材料扫描”顶部文件夹卡片。拖入时卡片高亮;不接受单个文件、应用程序或多个文件夹,处理中不接受重复导入。
2. 材料复制到应用自己的本地工作区,原始文件不会修改。再次导入会先确认是否替换当前工作区;取消确认保留当前材料和核对结果。
3. 本地 OCR 提取信息,按旧项目规则自动匹配并分类。扫描显示真实处理进度。
4. 在“人工配对”两侧多选材料,选择分类后确认;识别失败的材料仍可人工配对。
5. “已核对”中可改分类、预览材料、撤销及导出。首列勾选一组或多组材料,或使用表格上方“全选当前分组”,再点击“导出所选 N 组 PPT”或“所选报销单”;切换分组保留勾选,取消当前分组全选不影响其他分组,未勾选时不能导出这两类文件。“全部”页可全选所有已核对组。行程 Excel 仍使用全部已核对材料。自动核对结果不代表已人工复核。
6. 工作区自动持久化到应用沙盒内的 Application Support/ReceiptDesk;清空只删除工作区副本,不删除原始材料。
已核对列表的“匹配”列会显示简要核对项,点击百分比查看每项实际得分、未得分原因及核对建议。证据评分采用真正的 **100 分制**:单张发票与付款截图的权重为金额 40 分、商户 20 分、日期 10 分、单号 15 分、批次金额唯一性 15 分;满分之和为 100,实际得分之和就是百分比,各项未得分之和就是差额。例如仅金额一致且批次唯一时为 55/100 分,即 55%。日期相差不超过 3 天得 10 分,4–7 天得 5 分;商户达到 35% 相似度后按相似度 × 20 分四舍五入。其他组合规则的满分原本就是 100 分,仍保留;多笔付款固定 95 分会明确说明保留分。百分比不是金额匹配比例,也不是成功概率。人工配对不显示自动评分。
旧工作区启动时自动升级为新版评分,无需重扫,不改变配对、分类及导出内容。内部候选筛选、歧义排除沿用原规则及原 `score`,新版百分制证据评分单独存于 `explanation.rawScore``version: 2`),界面不再将内部筛选分数当作百分比。历史依据不足时不显示未经验证的百分比。
## 收款信息
在本地贴票工具栏点击“收款信息”,填写收款人、开户行(建议包含支行)、账号,以及可选的制单人。保存后重启仍可使用,也可在个人报销单弹窗内修改或删除。账号按文本保存,保留前导零和长账号;界面默认隐藏完整账号。
资料独立保存于当前 macOS 用户的本机钥匙串,不写入工作区 JSON,不上传、不云同步;导入新材料和清空工作区不会删除资料。它不随项目管理的登录账号切换,共用同一 macOS 用户时需自行核对收款人。
个人报销单每一页自动填写已保存资料;制单人未填写时使用收款人。没有保存资料时,相应单元格留空,不沿用模板的示例姓名、开户行及账号。PPT 和行程 Excel 不携带这些资料。导出的报销单包含完整收款账号,请妥善保管。删除本机资料不会修改已导出的文件。
## 功能对应
| 原项目 | 原生实现 |
|---|---|
| App.vue、三个页面 | ContentView、ScanPage、PairPage、MatchedPage |
| WorkspaceState / FileItem / MatchPair | Models.swift |
| 前端请求、状态操作 | WorkspaceStore.swift |
| OcrService / worker.py | native-engine/engine.py + 打包 RapidOCR |
| OcrExtractor / WorkspaceService | native-engine/domain.py |
| PptExportService | native-engine/exports.py / export_ppt |
| TravelExcelExportService | native-engine/exports.py / export_travel |
| PersonalExpenseExportService | native-engine/exports.py / export_expense |
保留一对一评分、多发票合计、多付款合计、同程多人归组、歧义判断、七类关键词分类、人工多对多、撤销、预览、金额统计和三类 Office 文件导出。
个人报销单只使用打开弹窗时勾选的材料,按费用类型合并明细。例如勾选 6 组交通,只生成一条“交通”,金额和发票数量累计;未勾选材料不计入。弹窗显示合并后的类型、组数、单据数和金额,可按类型填写用途。金额沿用原报销单口径(每张发票 OCR 金额的绝对值最大值),不改为付款金额;混合票据类型会一并列明。按首次出现的类型顺序输出,每页最多 13 个类型,收款信息与模板公式保持不变。
保存个人报销单前还会核对引擎返回的分类合并版本和明细行数;旧引擎或行数与预览不一致时停止保存,不覆盖用户选定的输出文件。打包引擎通过 `RECEIPT_ENGINE_BINARY` 运行 `test_exports.py``engine_process` 测试。最终应用的引擎带沙盒继承签名,不能直接从普通终端启动;应使用 Debug 应用的 `--verify-local-engine` 自检入口,从真实应用内验证两条汇总并检查生成的 `grouped-expense.xlsx`,而不能仅验证 Python 源码。
### 特意保留的业务口径
- 文件名不参与匹配;PDF 只识别、预览和导出首页。
- 一对一分数达到 90 时可通过歧义检查;其他候选需要至少领先 8 分。
- 一张发票对应多笔付款的唯一合计组合不另设商户/日期门槛。
- 页面发票汇总、OCR 首金额和个人报销单最大金额的不同计算口径沿用旧代码,未擅自合并。
- 订单截图与同金额支付凭证去重沿用旧规则,因此仍需人工检查不同订单同金额的情况。
- “按类型分类”PPT 沿用先日期、后类别排序,并非重新设计分类封面或类别分区。
- PPT 使用竖版 3:4(720×960 pt),纯白页面,不加封面或贴票类型标题。所有类别(包括交通、住宿)均预留实物照片位置,不自动填入实物照片。
- 一张发票配一张付款截图:发票上方整幅,付款截图左下,右下留可编辑、可删除的实物照片提示,不另加照片页。
- 一张发票配多张付款截图:首张发票页下方左右最多放两张付款,剩余付款按每页三列两行、最多六张续页,最后加一张手工照片页。
- 多张发票:先按每页两张上下排列;若剩一张,放在下一页上方,下方容纳前两张付款。剩余付款每页最多六张,最后加一张手工照片页。只有发票或只有付款的组也能完整导出并预留照片页。
- 不同核对组或不同审批申请不合页、不按比例猜测发票付款对应关系;原图等比缩放,不裁切、不重复、不漏图,PDF 仍只用首页。
- 个人报销单沿用原始 XLSX 模板,直接修改工作表 XML,保留样式、打印设置、公式和关联资源;每页 13 条明细,最多六个签字岗位。删除全部岗位时按旧规则回退默认岗位。
### 必须说明的环境差异
RapidOCR 主版本、模型及关键推理依赖与旧环境对齐,但 Windows x64 与 macOS arm64 的底层运行库不同。PDF 首页由本地 PDFium 栅格化,替代原来的 PDFBox。因此不能保证所有文件的 OCR 文本逐字或浮点结果完全一致,仍需用实际报账材料做回归验收。应用不会悄悄换用苹果 Vision 或云端 OCR。
## 验证
```sh
.build-tools/bin/python -m unittest discover -s native-engine/tests -v
.build-tools/bin/python native-engine/tests/smoke_engine.py \
--binary "$PWD/native-engine/dist/receipt-engine-helper.app/Contents/MacOS/receipt-engine-helper" \
--output "$PWD/.validation/packaged"
```
覆盖原铁路多人凭证案例、火车与航班提取、金额歧义、分类、去重、PPT 页面安排、个人报销单分页与公式保留、行程表等。端到端测试使用 `../fapiao/demo-materials` 作为开发测试数据,运行中的应用本身不依赖该目录。
Debug 构建还提供应用沙盒内自检:用 `--verify-local-engine` 参数启动应用,生成独立测试图片,经过真正的 Swift → 本地引擎链路验证 OCR 和三种导出。结果写入沙盒 `Application Support/ReceiptDesk/diagnostics/report.json`,不替换用户工作区;自检后该应用进程自动退出。Release 不包含此入口。
## 发布说明
当前是本机开发运行构建,并非已经完成 Developer ID 签名和苹果公证的公开安装包。正式分发时需用同一 Developer ID 对辅助引擎的所有嵌套 Mach-O 库、辅助程序和主应用按由内到外顺序签名,再进行公证。请同时随发布包保留第三方依赖许可声明。