---
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
```

**响应:**

```json
{ "session_id": "a1b2c3d4e5f6" }
```

---

### 2. 上传文件（PDF/图片）

```
POST /api/upload/<session_id>
Content-Type: multipart/form-data
```

| 字段 | 类型 | 说明 |
|------|------|------|
| file | File | PDF 发票或支付截图 |

**响应（成功）:**

```json
{ "ok": true, "filename": "1. 电容一批.pdf" }
```

**响应（失败）:**

```json
{ "error": "未选择文件" }
```

HTTP `400`

---

### 3. 列出会话文件

```
GET /api/files/<session_id>
```

**响应:**

```json
{
  "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` 中的值 |

**处理内容:**

1. 从会话目录 PDF 提取发票信息 → `invoice_summary.csv`
2. 对支付截图多模态 LLM 识别，回填刷卡字段
3. 根据发票类型自动分类：差旅发票（高铁票/酒店住宿）不生成出库单；普通发票从模板复制并自动填写

配置会写入 `src/web/uploads/<session_id>/config.json`。

**响应（立即）:**

```json
{ "status": "started" }
```

处理在后台线程执行，进度与结果通过 `GET /api/logs/<session_id>`（SSE）获取。

**SSE 完成时 `result` 示例（成功）:**

```json
{
  "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
}
```

**纯差旅发票（跳过出库单生成）:**

```json
{
  "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 等仍可能成功）:**

```json
{
  "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: 正在提取发票...
```

**结束消息:**

```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/<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` | 自动填写的出库单 |

**错误:**

```json
{ "error": "文件不存在" }
```

HTTP `404`。`filename` 仅允许会话目录内的文件名（防止路径穿越）。

---

### 8. 获取发票数据

```
GET /api/data/<session_id>
```

**响应:**

```json
{
  "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
```

**请求体:**

```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/<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` 等配置一致）。

**纯差旅发票跳过出库单:**

```json
{
  "ok": true,
  "doc_ok": null,
  "doc_skipped": true
}
```

**出库单生成失败:**

```json
{
  "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 识别
- 根据发票类型选择填报模式：纯差旅发票走差旅报销流程，含普通发票走普通报销流程

**响应（立即）:**

```json
{ "status": "started" }
```

**SSE 完成示例:**

```json
{
  "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 读取失败等内部错误 |

统一错误体:

```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)。