Project Atlas 知识图谱 / 发票管理系统 ← 返回图谱
🧾

发票管理系统 invoice-management

发票上传、识别、核验与管理一体化系统。完整流水线:上传 → 按文件 MD5 去重 → 识别 → 自动核验 → 存储 → 列表/详情展示。Vue3 前端 + FastAPI 后端,另提供 RabbitMQ 异步核验对接。

Vue 3 + Vite FastAPI motor / MongoDB MinIO RabbitMQ :8000 / :5173
本页导航
架构 处理流水线 REST 接口 MQ 对接 外部依赖 部署 关联

# 一、架构

┌─────────────┐      HTTP/JSON       ┌──────────────────┐
│  Vue 3 SPA  │ ───────────────────> │  FastAPI 后端     │
│ (Vite + TS) │ <─────────────────── │                  │
└─────────────┘                      │  ┌────────────┐  │   PARSE_MODE 二选一:
                                     │  │ 识别服务    │──┼──> single   : Parse API(:7500)
                                     │  │ 核验服务    │  │  └> two_stage: OCR API → 通义千问 LLM
                                     │  │ 编排服务    │──┼──> 核验 API (chromePa)
                                     │  └────────────┘  │
                                     └───┬──────────┬───┘
                                         │          │
                                  ┌──────▼───┐  ┌───▼──────┐
                                  │ MongoDB  │  │  MinIO   │
                                  │ 元数据/  │  │ 原文件/  │
                                  │ 核验记录 │  │ 缩略图   │
                                  └──────────┘  └──────────┘
后端
FastAPI(异步)+ motor(MongoDB)+ MinIO SDK + httpx + Pillow + PyMuPDF(PDF 渲染)+ aio-pika(MQ)
前端
Vue 3 + Vite + TypeScript SPA
存储
MongoDB 三集合 invoices / verifications / verification_index;MinIO 存原始文件 + 缩略图
MongoDB 用库 invoice、MinIO 用桶 invoice,与舆情系统的 deep-research 库物理同集群但逻辑独立(同一 Mongo/MinIO 实例,不同库/桶)。

# 二、处理流水线

上传文件(图片/PDF)
   │ PDF 渲染首页用于识别
   ▼
按文件 MD5 去重 ── 命中 → 直接返回已有记录
   │ 未命中
   ▼
存 MinIO(原文件 + 缩略图) → 落库 stage=parsing
   ▼
识别(按 PARSE_MODE 二选一,约 1~2 分钟)
   ├─ single(当前)   : Parse API 一次返回结构化发票
   └─ two_stage(回退): OCR API 取原文 → 通义千问 LLM 提结构化
   │ parse_success
   ▼
自动核验(调核验 API)
   │
   ├─ verified=true  → 完成
   └─ verified=false → 记录失败原因(验证码失败/次数超限/发票无效等)
上传接口立即返回 stage=parsing,识别 + 核验在后台异步进行。识别较慢(LLM 约 1~2 分钟),同步接口客户端超时建议 ≥180s。

# 三、REST 接口

方法路径说明
POST/api/invoices/upload上传单个发票(图片/PDF),立即返回 stage=parsing,后台识别+核验
GET/api/invoices?page=1&size=20发票列表(分页,卡片字段)
GET/api/invoices/{id}发票详情(识别信息 + 核验记录列表)
POST/api/invoices/{id}/verify手动重新核验(追加一条核验记录)
POST/api/invoices/{id}/reparse重新识别(失败重试,成功后自动核验)
GET/api/verifications按 fpdm/fphm/kprq/kjje 查询核验记录
GET/api/files/{object}后端代理返回 MinIO 文件/缩略图
POST/api/verify-invoice对外开放:上传文件→识别→核验,返回核验原始 JSON(无需鉴权)
POST/api/verify-invoice-url对外开放:按 URL 下载文件走完整核验,返回结果(附带 url)

对外接口响应(/api/verify-invoice)

{
  "success": true,        // 识别且核验均通过
  "parse_success": true,  // 识别是否成功
  "verified": true,       // 核验是否通过
  "invoice_id": "6a21...",
  "verify_result": { ... },  // 核验接口完整原始 JSON(失败为 null)
  "error": ""             // 失败原因
}
健康检查 GET /api/health。接口前缀统一 /api,后端会自动挂载 frontend/dist/ 提供 SPA。

# 四、RabbitMQ 异步核验对接

除 REST 外,系统提供 MQ 方式做批量/异步核验。两个队列:

队列名方向说明
invoice.verify.queue调用方 → 本系统投递待核验消息 { "url": "..." }
invoice.verify.result.queue本系统 → 调用方投递核验结果

结果消息关键字段

url
原样返回请求的 url,用于对应
success
最终是否成功(识别成功且核验通过才 true)
verified / parse_success
核验是否通过 / 识别是否成功
invoice_category
发票类型(如"电子发票(增值税专用发票)")
verify_result
核验接口原始返回
error
失败原因

失败分类与重试

场景error 文案自动重试
成功""—
文件下载不了文件不可用否
文件识别失败文件识别错误否
核验没通过(技术性失败)核验失败是
发票无效(税务判定)税务返回的具体原因否
保证必达 + 幂等:每条能解析出 url 的消息最终一定有结果。核验失败 会在工作日 8:00–18:00 每 10 分钟自动重跑(上限默认 6 次),同一 url 可能收到多条结果,以 success=true 或最新一条为准。下游需按 url 关联并做幂等。

# 五、外部依赖

下列为系统外部的第三方服务(图谱中标「外部依赖」角标),地址由 .env 配置:

识别走双模式二选一(PARSE_MODE 开关,入口 parse_service.parse_invoice 分发):single(当前 .env 与代码默认)只调 Parse API 一次返回结构化;two_stage(可回退)才走 OCR API → 通义千问 LLM。所以当前实际生效的只有 Parse API,OCR API 与 LLM 是备用分支,不同时调用。
依赖配置项 / 地址用途当前是否启用
Parse 识别 APIPARSE_API_URL · :7500/api/v1/parse单段式:一次返回结构化发票✅ single 模式(当前)
OCR APIOCR_API_URL · sino-bridge.com:18012/ocr两段式第一步:取原文⏸ 仅 two_stage 回退
LLM(通义千问)LLM_BASE_URL · 阿里云 DashScope · qwen两段式第二步:OCR 后提结构化⏸ 仅 two_stage 回退
核验 APIVERIFY_API_URL · sino-bridge.com/chromePa/invoice_verification发票查验,路径经由采集端 chrome-pa✅ 启用
MinIO 外网入口OSS_PUBLIC_ENDPOINT · sino-bridge.com:82文件对外访问地址✅ 启用
企微告警MONITOR_WEBHOOK_URL · 企业微信机器人服务监控告警(MONITOR_ENABLED 开关)✅ 启用
为何做成开关:两段式 LLM 对某些票会漏字段(如开票日期空、发票号只截到 11 位)导致必填校验失败;单段式实测能补全,故切 single 并保留 two_stage 可回退。两条分支返回结构统一,编排/核验/前端无感知。
共享基础设施(非外部):MongoDB(库 invoice)、MinIO(桶 invoice)、RabbitMQ(核验队列)。
核验 API 的路径带 chromePa,指向 采集后端 暴露的发票查验能力——而采集端在查验税务平台时又依赖验证码识别服务破解彩色验证码。因此"核验失败"里可能包含"验证码识别失败"这类上游原因。

# 六、部署

运行依赖

依赖类别端口 / 标识必需性说明
MongoDB数据库27017 · 库 invoice✅ 必需发票元数据 + 核验记录(集合 invoices/verifications/verification_index)
MinIO对象存储9000(控制台 9001)· 桶 invoice✅ 必需原始文件 + 缩略图
RabbitMQ消息队列5672 · vhost langwell⚙ 默认开(MQ_ENABLED=true)异步核验:消费 invoice.verify.queue,结果发 invoice.verify.result.queue。连外部已有实例,不在本地 compose 内
Parse 识别 API外部服务:7500(PARSE_API_URL)✅ single 模式必需当前默认 single 模式一次返回结构化发票
核验 API外部服务VERIFY_API_URL(/chromePa)✅ 必需发票查验,经采集端 chrome-pa
OCR API + 通义千问外部服务OCR_API_URL / LLM_*⏸ 仅 two_stage回退模式才用,详见外部依赖
本地 docker-compose.yml 只拉起 MongoDB + MinIO 两个容器,不含 RabbitMQ。而后端 MQ_ENABLED 默认 true、会去连外部 RabbitMQ 实例(示例 MQ_HOST=192.168.104.126、vhost langwell)。只跑 REST、不需要 MQ 异步核验时,设 MQ_ENABLED=false 即可跳过消费者启动。

启动步骤

# 1. 启动本地基础设施(仅 MongoDB + MinIO,RabbitMQ 连外部)
docker compose up -d
#   MongoDB :27017    MinIO :9000(控制台 :9001,minioadmin/minioadmin)

# 2. 后端
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp config/config.example.env .env    # 按需改;连外部 Mongo/MinIO/MQ 时填各自地址与凭据
python -m app.main                   # http://localhost:8000,前缀 /api

# 3. 前端
cd frontend && npm install && npm run dev   # http://localhost:5173,/api 代理到 :8000
npm run build                        # 产物 frontend/dist/,后端自动挂载
前置:Python 3.10+、Node.js 18+、Docker(本地起 MongoDB + MinIO)。关键 Python 依赖见 requirements.txt:fastapi / uvicorn / motor(MongoDB)/ minio / httpx / pillow / pymupdf(PDF 渲染)/ aio-pika(RabbitMQ)。测试 pytest tests/ -v 用 mongomock + fake MinIO + mock 外部接口,无需真实外部依赖(含 MQ)即可运行。

# 七、关联

Project Atlas · 返回知识图谱 · 如何维护