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