--- last_reviewed: 2026-06-09 --- # 财务报销自动化 — API 文档 > 基础地址: `http://localhost:5000` > 启动: `uv run python src/web/app.py` ## 总览 | # | 方法 | 路径 | 说明 | |---|------|------|------| | 1 | GET | `/` | PC 端主页 | | 2 | POST | `/api/session` | 创建会话 | | 3 | POST | `/api/upload/` | 上传文件(PDF/图片) | | 4 | GET | `/api/files/` | 列出会话目录中的文件 | | 5 | POST | `/api/process/` | 启动处理(提取+LLM识别+出库单) | | 6 | GET | `/api/logs/` | SSE 日志流 | | 7 | GET | `/api/download//` | 下载生成的文件 | | 8 | GET | `/api/data/` | 获取发票数据(JSON) | | 9 | POST | `/api/save/` | 保存编辑后的发票数据 | | 10 | POST | `/api/submit-financial/` | 提交到财务系统 | | 11 | GET | `/mobile/` | 移动端上传页面 | | 12 | POST | `/api/mobile-upload/` | 移动端上传图片 | --- ## 会话与目录 - 调用 `POST /api/session` 获得 `session_id` - 该会话下所有文件存放在 `src/web/uploads//` - 典型产物:`invoice_summary.csv`、`易耗品、出库单.doc`、`config.json`、`session.log`、`result.json` --- ## 接口详情 ### 1. 创建会话 ``` POST /api/session ``` **响应:** ```json { "session_id": "a1b2c3d4e5f6" } ``` --- ### 2. 上传文件(PDF/图片) ``` POST /api/upload/ Content-Type: multipart/form-data ``` | 字段 | 类型 | 说明 | |------|------|------| | file | File | PDF 发票或支付截图 | **响应(成功):** ```json { "ok": true, "filename": "1. 电容一批.pdf" } ``` **响应(失败):** ```json { "error": "未选择文件" } ``` HTTP `400` --- ### 3. 列出会话文件 ``` GET /api/files/ ``` **响应:** ```json { "pdfs": ["1. 电容一批.pdf"], "images": ["payment_01.jpg"] } ``` `images` 包含扩展名:`.png`、`.jpg`、`.jpeg`、`.bmp`、`.webp`。 --- ### 5. 启动管道处理 ``` POST /api/process/ Content-Type: application/json ``` **请求体:** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | username | string | 否 | 财务系统工号 | | password | string | 否 | 登录密码 | | default_name | string | 否 | 默认报销人姓名 | | default_card_no | string | 否 | 默认公务卡号 | | default_person_id | string | 否 | 默认人员编号 | | consumable_storage | string | 否 | 出库单存放地点;未填则用 `config.json` 中的值 | **处理内容:** 1. 从会话目录 PDF 提取发票信息 → `invoice_summary.csv` 2. 对支付截图多模态 LLM 识别,回填刷卡字段 3. 根据发票类型自动分类:差旅发票(高铁票/酒店住宿)不生成出库单;普通发票从模板复制并自动填写 配置会写入 `src/web/uploads//config.json`。 **响应(立即):** ```json { "status": "started" } ``` 处理在后台线程执行,进度与结果通过 `GET /api/logs/`(SSE)获取。 **SSE 完成时 `result` 示例(成功):** ```json { "ok": true, "elapsed": "45.2s", "invoice_count": 4, "csv_url": "/api/download//invoice_summary.csv", "travel_count": 2, "general_count": 2, "doc_url": "/api/download//%E6%98%93%E8%80%97%E5%93%81%E3%80%81%E5%87%BA%E5%BA%93%E5%8D%95.doc", "doc_ok": true } ``` **纯差旅发票(跳过出库单生成):** ```json { "ok": true, "invoice_count": 3, "csv_url": "/api/download//invoice_summary.csv", "travel_count": 3, "general_count": 0, "doc_ok": null, "doc_skipped": true, "doc_message": "差旅发票无需生成易耗品出库单" } ``` **出库单生成失败时(CSV 等仍可能成功):** ```json { "ok": true, "invoice_count": 4, "csv_url": "/api/download//invoice_summary.csv", "travel_count": 2, "general_count": 2, "doc_ok": false, "doc_error": "服务器未安装 pywin32,无法生成 Word 出库单" } ``` **字段说明:** | 字段 | 类型 | 说明 | |------|------|------| | `travel_count` | int | 差旅发票数量(高铁票/酒店住宿) | | `general_count` | int | 普通发票数量 | | `doc_ok` | bool/null | `true`=成功,`false`=失败,`null`=已跳过(纯差旅发票) | | `doc_skipped` | bool | 是否因纯差旅发票而跳过出库单生成 | | `doc_message` | string | 跳过时的提示信息 | --- ### 6. SSE 日志流 ``` GET /api/logs/ Accept: text/event-stream ``` **日志行格式:** ``` data: 2026-05-26 12:00:01 [INFO ] extractor: 正在提取发票... ``` **结束消息:** ```json { "type": "done", "result": { } } ``` `result` 结构取决于触发来源: | 来源 | 典型字段 | |------|----------| | `/api/process` | `ok`, `elapsed`, `invoice_count`, `csv_url`, `travel_count`, `general_count`, `doc_url`, `doc_ok`, `doc_skipped`, `doc_error` | | `/api/submit-financial` | `ok`, `submit_ok`, `submit_error` | > SSE 超时时间为 10 分钟(600 秒)。 --- ### 7. 下载文件 ``` GET /api/download// ``` **响应:** 文件二进制流,带 `Content-Disposition: attachment` 与 UTF-8 文件名。 | 扩展名 | Content-Type | |--------|----------------| | `.csv` | `text/csv; charset=utf-8` | | `.doc` | `application/msword` | | 其它 | `application/octet-stream` | **常见文件名:** | 文件名 | 说明 | |--------|------| | `invoice_summary.csv` | 发票汇总(含 LLM 识别结果) | | `易耗品、出库单.doc` | 自动填写的出库单 | **错误:** ```json { "error": "文件不存在" } ``` HTTP `404`。`filename` 仅允许会话目录内的文件名(防止路径穿越)。 --- ### 8. 获取发票数据 ``` GET /api/data/ ``` **响应:** ```json { "csv_filename": "invoice_summary.csv", "fields": [ "序号", "发票号码", "开票日期", "项目名称", "规格型号", "价税合计", "销售方名称", "人员姓名", "刷卡日期", "公务卡号", "刷卡金额", "备注", "工号" ], "data": [ { "__row": 0, "序号": "1", "发票号码": "26442000005432755951", "价税合计": "2900.00" } ] } ``` - `fields`:列顺序 - `data[].__row`:内部行索引(保存时不需要提交,服务端按数组顺序写回) **错误:** `404` 未找到 CSV;`500` 读取失败。 --- ### 9. 保存编辑后的发票数据 ``` POST /api/save/ Content-Type: application/json ``` **请求体:** ```json { "csv_filename": "invoice_summary.csv", "data": [ { "序号": "1", "发票号码": "26442000005432755951", "开票日期": "2026/05/18", "项目名称": "...", "规格型号": "...", "价税合计": "2900.00", "销售方名称": "...", "人员姓名": "", "刷卡日期": "2026/04/28", "公务卡号": "", "刷卡金额": "2850.00", "备注": "", "工号": "202407021" } ] } ``` **响应(成功):** ```json { "ok": true, "doc_ok": true, "doc_url": "/api/download//%E6%98%93%E8%80%97%E5%93%81%E3%80%81%E5%87%BA%E5%BA%93%E5%8D%95.doc" } ``` 保存后会根据最新 CSV **重新生成** 出库单 Word(与会话 `config.json` 中的 `consumable_storage` 等配置一致)。 **纯差旅发票跳过出库单:** ```json { "ok": true, "doc_ok": null, "doc_skipped": true } ``` **出库单生成失败:** ```json { "ok": true, "doc_ok": false, "doc_error": "出库单模板不存在,请将模板放在项目根目录" } ``` --- ### 10. 提交到财务系统 ``` POST /api/submit-financial/ ``` **前置条件:** - 会话目录存在 `config.json`(由 `/api/process` 写入),否则返回 `400` - 存在可用的发票 CSV(通常为 `invoice_summary.csv`) **说明:** - 前端一般在提交前调用 `/api/save` 保存表格修改 - 本接口**不会**自动执行发票提取或 LLM 识别 - 根据发票类型选择填报模式:纯差旅发票走差旅报销流程,含普通发票走普通报销流程 **响应(立即):** ```json { "status": "started" } ``` **SSE 完成示例:** ```json { "type": "done", "result": { "ok": true, "submit_ok": true } } ``` 失败时 `submit_ok: false`,`submit_error` 为错误描述。 --- ### 11. 移动端上传页面 ``` GET /mobile/ ``` 返回移动端 HTML 页面。扫码上传的图片与 PC 端共用同一会话目录;PC 通过轮询 `GET /api/files/` 同步文件列表。 --- ### 12. 移动端上传图片 ``` POST /api/mobile-upload/ Content-Type: multipart/form-data ``` | 字段 | 类型 | 说明 | |------|------|------| | file | File | 图片文件 | 逻辑与 `POST /api/upload/` 相同。 --- ## 错误码汇总 | HTTP | 场景 | |------|------| | 400 | 参数缺失、未找到配置等 | | 404 | `session_id` 不存在、文件不存在 | | 500 | CSV 读取失败等内部错误 | 统一错误体: ```json { "error": "错误描述" } ``` --- ## 发票类型分类 系统自动将发票分为两类,影响出库单生成和后续报销流程: | 类型 | 判断依据 | 出库单 | 报销流程 | |------|----------|--------|----------| | 差旅发票 | 高铁票、酒店住宿等 | 不生成 | 差旅报销 | | 普通发票 | 其他(办公用品、耗材等) | 自动生成 | 普通报销 | `/api/process` 和 `/api/save` 的响应中 `travel_count` / `general_count` 即为分类统计。 --- ## 端到端流程 ```mermaid sequenceDiagram participant PC as PC 端 participant Server as Server participant Mobile as 移动端 participant Word as Word COM PC->>Server: POST /api/session Server-->>PC: session_id PC->>Server: POST /api/upload/{sid} PC->>Server: POST /api/process/{sid} Note over Server: PDF 提取 + LLM 识别 + 写 config.json alt 含普通发票 Server->>Word: 从模板复制并填写出库单 else 纯差旅发票 Note over Server: 跳过出库单生成 end Server-->>PC: SSE done (csv_url, doc_url, ...) PC->>Server: GET /api/data/{sid} Server-->>PC: fields + data PC->>Server: POST /api/save/{sid} Note over Server: 更新 CSV,重新生成出库单 Server-->>PC: doc_url PC->>Server: GET /api/download/{sid}/易耗品、出库单.doc PC->>Server: POST /api/submit-financial/{sid} Note over Server: Playwright 浏览器填报 Server-->>PC: SSE done (submit_ok) Mobile->>Server: POST /api/mobile-upload/{sid} PC->>Server: GET /api/files/{sid} (轮询) ``` --- ## 相关 CLI 不经过 Web、在本地直接填写出库单: ```bash uv run python -m src.doc.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc" ``` 详见 [README.md](./README.md)。