Files
X-Financial/docs/superpowers/plans/2026-04-24-ai-reimbursement-mvp.md
WIN-JHFT4D3SIVT\caoxiaozhu 7141e1d11a feat: refactor monolithic App.vue into modular Vue component architecture
- Extract 711-line App.vue into 15+ focused files across 5 directories
- Add data layer (icons, metrics, policies, auditTrail, requests)
- Add composables (useNavigation, useRequests, useChat, useToast)
- Add layout components (SidebarRail, TopBar, FilterBar)
- Add shared components (PanelHead, InfoRow, ToastNotification)
- Add business component (RequestTable) and 5 view components
- Extract global CSS to assets/styles/global.css
- Add start.sh with WSL/Windows cross-platform support
- Add .gitignore for node_modules, dist, and IDE dirs
2026-04-28 17:20:52 +08:00

46 KiB
Raw Blame History

AI 报销预审中台 MVP 实施计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在 8 周内完成 AI 报销预审中台的 MVP跑通「上传材料 → OCR 识别 → 草稿生成 → 规则预审 → 补件交互 → 用户确认 → 模拟同步」完整闭环,优先支持差旅报销场景。

Architecture: 采用模块化单体架构,前端 Vue3 + Ant Design Vue后端 Python FastAPI数据库 PostgreSQL + Redis文件存储 MinIOOCR 先用百度/腾讯云 API 封装。Agent 编排用自研轻量状态机 + LangGraph可选规则引擎自研 JSON Rule Engine。6 层架构:用户入口 → AI 操作层 → Agent 层 → 影子账本 → Policy & Evidence → System Adapter。

Tech Stack:

  • 前端Vue 3 + TypeScript + Ant Design Vue + Vite + Pinia
  • 后端Python 3.11+ / FastAPI + SQLAlchemy + Alembic + Pydantic v2
  • 数据库PostgreSQL 15 + Redis 7
  • 文件存储MinIOS3 兼容)
  • OCR百度云 OCR API增值税发票、火车票、机票行程单
  • 规则引擎:自研 JSON Rule Engine
  • Agent自研 Orchestrator 状态机 + 大模型 APIOpenAI / 国内模型)
  • 部署Docker Compose

团队分工建议3-5 人)

角色 人数 职责
后端工程师 A 1 核心后端任务管理、影子账本、Agent 编排、规则引擎
后端工程师 B 1 OCR 集成、文件服务、适配器层、审计日志
前端工程师 1-2 所有页面与组件(可拆分为两人并行)
全栈/Agent 工程师 1 Agent Prompt 设计、大模型集成、规则配置

总体里程碑

里程碑 核心交付
W1 项目基建 + 数据模型 项目骨架、数据库 schema、开发环境
W2-W3 后端核心服务 任务/票据/OCR/规则引擎 API
W3-W4 Agent 编排 + 影子账本 Orchestrator 状态机、5 个 Agent
W4-W5 前端核心页面 入口/上传/草稿/预审/补件/确认
W5-W6 前后端联调 + 规则配置 完整流程跑通
W7-W8 集成测试 + 打磨 + 部署 E2E 测试、修复、演示 Demo

Phase 1: 项目基建W1

Task 1.1: 后端项目骨架搭建

Files:

  • Create: backend/app/__init__.py

  • Create: backend/app/main.py

  • Create: backend/app/core/config.py

  • Create: backend/app/core/database.py

  • Create: backend/app/core/dependencies.py

  • Create: backend/app/api/__init__.py

  • Create: backend/app/api/v1/__init__.py

  • Create: backend/app/api/v1/router.py

  • Create: backend/app/models/__init__.py

  • Create: backend/app/schemas/__init__.py

  • Create: backend/app/services/__init__.py

  • Create: backend/requirements.txt

  • Create: backend/pyproject.toml

  • Create: backend/Dockerfile

  • Create: backend/alembic.ini

  • Create: backend/alembic/env.py

  • Test: backend/tests/__init__.py

  • Test: backend/tests/conftest.py

  • Test: backend/tests/test_health.py

  • Step 1: 初始化后端项目结构

创建 FastAPI 项目骨架,使用以下目录结构:

backend/
├── app/
│   ├── __init__.py
│   ├── main.py                  # FastAPI 应用入口
│   ├── core/
│   │   ├── config.py            # SettingsPydantic BaseSettings
│   │   ├── database.py          # SQLAlchemy async engine + session
│   │   └── dependencies.py      # 通用依赖注入db session, 当前用户等)
│   ├── api/
│   │   └── v1/
│   │       ├── __init__.py
│   │       ├── router.py        # v1 路由聚合
│   │       ├── tasks.py         # 报销任务 API
│   │       ├── documents.py     # 票据附件 API
│   │       ├── precheck.py      # 预审结果 API
│   │       └── supplements.py   # 补件 API
│   ├── models/                  # SQLAlchemy ORM models
│   │   ├── __init__.py
│   │   ├── task.py
│   │   ├── reimbursement.py
│   │   ├── document.py
│   │   ├── rule.py
│   │   └── audit.py
│   ├── schemas/                 # Pydantic schemas
│   │   ├── __init__.py
│   │   ├── task.py
│   │   ├── reimbursement.py
│   │   ├── document.py
│   │   └── rule.py
│   ├── services/                # 业务逻辑层
│   │   ├── __init__.py
│   │   ├── task_service.py
│   │   ├── document_service.py
│   │   ├── ocr_service.py
│   │   ├── rule_engine.py
│   │   └── sync_service.py
│   └── agents/                  # Agent 编排层
│       ├── __init__.py
│       ├── orchestrator.py
│       ├── intake_agent.py
│       ├── parse_agent.py
│       ├── rule_check_agent.py
│       ├── explain_agent.py
│       └── sync_agent.py
├── alembic/
│   ├── env.py
│   └── versions/
├── alembic.ini
├── requirements.txt
├── pyproject.toml
├── Dockerfile
└── tests/
    ├── __init__.py
    ├── conftest.py
    └── test_health.py
  • Step 2: 编写核心配置文件

backend/app/core/config.py 使用 Pydantic BaseSettings

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    # App
    APP_NAME: str = "AI Reimbursement Agent"
    APP_VERSION: str = "0.1.0"
    DEBUG: bool = True

    # Database
    DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/x_financial"

    # Redis
    REDIS_URL: str = "redis://localhost:6379/0"

    # MinIO / S3
    MINIO_ENDPOINT: str = "localhost:9000"
    MINIO_ACCESS_KEY: str = "minioadmin"
    MINIO_SECRET_KEY: str = "minioadmin"
    MINIO_BUCKET: str = "reimbursement"

    # OCR
    OCR_PROVIDER: str = "baidu"  # baidu | tencent | mock
    BAIDU_OCR_API_KEY: str = ""
    BAIDU_OCR_SECRET_KEY: str = ""

    # LLM
    LLM_PROVIDER: str = "openai"
    LLM_API_KEY: str = ""
    LLM_MODEL: str = "gpt-4o-mini"
    LLM_BASE_URL: str = ""

    class Config:
        env_file = ".env"

settings = Settings()
  • Step 3: 编写数据库连接和 FastAPI 入口

backend/app/core/database.py:

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase
from app.core.config import settings

engine = create_async_engine(settings.DATABASE_URL, echo=settings.DEBUG)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

class Base(DeclarativeBase):
    pass

async def get_db() -> AsyncSession:
    async with async_session() as session:
        yield session

backend/app/main.py:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.core.config import settings
from app.api.v1.router import api_router

app = FastAPI(title=settings.APP_NAME, version=settings.APP_VERSION)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(api_router, prefix="/api/v1")

@app.get("/health")
async def health():
    return {"status": "ok", "version": settings.APP_VERSION}
  • Step 4: 编写 requirements.txt
fastapi==0.115.0
uvicorn[standard]==0.30.0
sqlalchemy[asyncio]==2.0.35
asyncpg==0.30.0
alembic==1.13.0
pydantic==2.9.0
pydantic-settings==2.5.0
python-multipart==0.0.9
httpx==0.27.0
redis==5.1.0
minio==7.2.9
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
pytest==8.3.0
pytest-asyncio==0.24.0
httpx  # for TestClient
  • Step 5: 编写健康检查测试

backend/tests/conftest.py:

import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app

@pytest.fixture
async def client():
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as ac:
        yield ac

backend/tests/test_health.py:

import pytest

@pytest.mark.asyncio
async def test_health(client):
    response = await client.get("/health")
    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "ok"
  • Step 6: 运行测试确认骨架可用

Run: cd backend && pip install -r requirements.txt && pytest tests/test_health.py -v Expected: PASS

  • Step 7: Commit
git add backend/
git commit -m "feat: 初始化后端项目骨架FastAPI + SQLAlchemy + Alembic"

Task 1.2: 数据库 Schema + 迁移

Files:

  • Create: backend/app/models/base.py

  • Create: backend/app/models/task.py

  • Create: backend/app/models/reimbursement.py

  • Create: backend/app/models/document.py

  • Create: backend/app/models/rule.py

  • Create: backend/app/models/audit.py

  • Create: backend/app/models/enums.py

  • Modify: backend/app/models/__init__.py

  • Test: backend/tests/test_models.py

  • Step 1: 定义枚举类型

backend/app/models/enums.py:

import enum

class TaskStatus(str, enum.Enum):
    CREATED = "created"
    MATERIAL_COLLECTING = "material_collecting"
    PARSING = "parsing"
    DRAFT_GENERATED = "draft_generated"
    PRECHECKING = "prechecking"
    NEED_SUPPLEMENT = "need_supplement"
    PENDING_USER_CONFIRM = "pending_user_confirm"
    SUBMITTING = "submitting"
    SYNCED = "synced"
    SYNC_FAILED = "sync_failed"
    CLOSED = "closed"

class ExpenseType(str, enum.Enum):
    TRAVEL_TRANSPORT = "travel_transport"
    TRAVEL_HOTEL = "travel_hotel"
    TRAVEL_MEAL = "travel_meal"
    LOCAL_TRANSPORT = "local_transport"
    BUSINESS_MEAL = "business_meal"
    OFFICE_SUPPLY = "office_supply"
    COMMUNICATION = "communication"
    OTHER = "other"

class DocumentType(str, enum.Enum):
    VAT_INVOICE = "vat_invoice"
    TRAIN_TICKET = "train_ticket"
    FLIGHT_ITINERARY = "flight_itinerary"
    TAXI_RECEIPT = "taxi_receipt"
    HOTEL_BILL = "hotel_bill"
    PAYMENT_SCREENSHOT = "payment_screenshot"
    TRAVEL_ORDER = "travel_order"
    OTHER_ATTACHMENT = "other_attachment"

class RiskLevel(str, enum.Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    BLOCKED = "blocked"

class RuleAction(str, enum.Enum):
    PASS = "pass"
    WARN = "warn"
    REQUIRE_EXPLANATION = "require_explanation"
    REQUIRE_ATTACHMENT = "require_attachment"
    REQUIRE_APPROVAL = "require_approval"
    BLOCK = "block"

class SyncStatus(str, enum.Enum):
    SUCCESS = "success"
    FAILED = "failed"
    RETRYING = "retrying"
    PENDING = "pending"
  • Step 2: 定义所有 ORM 模型

backend/app/models/base.py:

import uuid
from datetime import datetime
from sqlalchemy import String, DateTime, func
from sqlalchemy.orm import Mapped, mapped_column

def generate_id():
    return str(uuid.uuid4())

class TimestampMixin:
    created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now())

class IDMixin:
    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=generate_id)

backend/app/models/task.py — 包含 ReimbursementTask,字段按文档 5.2.1 节:

from sqlalchemy import String, Text, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import IDMixin, TimestampMixin, Base
from app.models.enums import TaskStatus

class ReimbursementTask(IDMixin, TimestampMixin, Base):
    __tablename__ = "reimbursement_task"

    user_id: Mapped[str] = mapped_column(String(36), index=True)
    company_id: Mapped[str] = mapped_column(String(36), index=True)
    task_type: Mapped[str] = mapped_column(String(50), default="travel_expense")
    status: Mapped[TaskStatus] = mapped_column(default=TaskStatus.CREATED)
    user_intent: Mapped[str | None] = mapped_column(Text, nullable=True)
    current_agent: Mapped[str | None] = mapped_column(String(50), nullable=True)

    # relationships
    documents = relationship("ExpenseDocument", back_populates="task", lazy="selectin")
    reimbursement = relationship("ShadowReimbursement", back_populates="task", uselist=False, lazy="selectin")

backend/app/models/reimbursement.py — 包含 ShadowReimbursement, ReimbursementItem, SupplementRequest, SyncRecord,字段按文档 5.2.2 ~ 5.2.4, 5.2.7, 5.2.8 节定义。

backend/app/models/document.pyExpenseDocument,字段按文档 5.2.4 节。

backend/app/models/rule.pyExpenseRule, RuleHit,字段按文档 5.2.5, 5.2.6 节。

backend/app/models/audit.pyAuditLog(通用审计日志表,记录所有关键操作)。

  • Step 3: 更新 models/__init__.py 导出所有模型
from app.models.task import ReimbursementTask
from app.models.reimbursement import ShadowReimbursement, ReimbursementItem, SupplementRequest, SyncRecord
from app.models.document import ExpenseDocument
from app.models.rule import ExpenseRule, RuleHit
from app.models.audit import AuditLog
from app.models.base import Base

__all__ = [
    "ReimbursementTask", "ShadowReimbursement", "ReimbursementItem",
    "ExpenseDocument", "ExpenseRule", "RuleHit",
    "SupplementRequest", "SyncRecord", "AuditLog", "Base",
]
  • Step 4: 生成 Alembic 迁移

Run: cd backend && alembic revision --autogenerate -m "init schema" Run: cd backend && alembic upgrade head

  • Step 5: 编写模型测试

验证所有表能正确创建和插入数据。

  • Step 6: Commit
git add backend/
git commit -m "feat: 完成所有数据模型定义和数据库迁移"

Task 1.3: 前端项目骨架搭建

Files:

  • Create: 使用 Vite 初始化 Vue3 + TypeScript 项目

  • Create: frontend/src/router/index.ts

  • Create: frontend/src/stores/ — Pinia stores

  • Create: frontend/src/api/ — API 调用封装

  • Create: frontend/src/views/ — 页面

  • Create: frontend/src/components/ — 组件

  • Create: frontend/src/layouts/ — 布局

  • Test: frontend/src/App.vue

  • Step 1: 初始化 Vue3 项目

npm create vite@latest frontend -- --template vue-ts
cd frontend
npm install ant-design-vue @ant-design/icons-vue vue-router pinia axios dayjs

目录结构:

frontend/
├── src/
│   ├── api/                  # API 调用
│   │   ├── index.ts          # axios 实例
│   │   ├── task.ts           # 报销任务 API
│   │   ├── document.ts       # 票据 API
│   │   └── precheck.ts       # 预审 API
│   ├── components/           # 通用组件
│   │   ├── FileUpload.vue
│   │   ├── ExpenseTable.vue
│   │   └── RuleHitCard.vue
│   ├── layouts/
│   │   └── MainLayout.vue
│   ├── router/
│   │   └── index.ts
│   ├── stores/
│   │   ├── task.ts
│   │   └── user.ts
│   ├── views/
│   │   ├── HomeView.vue         # 报销入口
│   │   ├── UploadView.vue       # 票据上传
│   │   ├── DraftView.vue        # 报销草稿
│   │   ├── PrecheckView.vue     # 预审结果
│   │   ├── SupplementView.vue   # 补件交互
│   │   ├── ConfirmView.vue      # 提交确认
│   │   └── AuditView.vue        # 审计日志
│   ├── App.vue
│   └── main.ts
├── index.html
├── vite.config.ts
├── tsconfig.json
└── package.json
  • Step 2: 配置路由和布局

frontend/src/router/index.ts:

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', name: 'home', component: () => import('@/views/HomeView.vue') },
  { path: '/task/:taskId/upload', name: 'upload', component: () => import('@/views/UploadView.vue') },
  { path: '/task/:taskId/draft', name: 'draft', component: () => import('@/views/DraftView.vue') },
  { path: '/task/:taskId/precheck', name: 'precheck', component: () => import('@/views/PrecheckView.vue') },
  { path: '/task/:taskId/supplement', name: 'supplement', component: () => import('@/views/SupplementView.vue') },
  { path: '/task/:taskId/confirm', name: 'confirm', component: () => import('@/views/ConfirmView.vue') },
  { path: '/audit', name: 'audit', component: () => import('@/views/AuditView.vue') },
]

export default createRouter({ history: createWebHistory(), routes })
  • Step 3: 配置 API 封装

frontend/src/api/index.ts:

import axios from 'axios'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api/v1',
  timeout: 30000,
})

export default api
  • Step 4: 确认前端能正常启动

Run: cd frontend && npm run dev Expected: 浏览器访问 http://localhost:5173 能看到页面

  • Step 5: Commit
git add frontend/
git commit -m "feat: 初始化前端项目骨架Vue3 + TypeScript + Ant Design Vue"

Task 1.4: Docker Compose 开发环境

Files:

  • Create: docker-compose.yml

  • Create: backend/Dockerfile

  • Create: frontend/Dockerfile

  • Create: .env.example

  • Create: backend/init.sql(初始数据)

  • Step 1: 编写 docker-compose.yml

version: "3.8"
services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: x_financial
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - minio_data:/data

volumes:
  pgdata:
  minio_data:
  • Step 2: 编写 .env.example

按 config.py 中的字段列出所有环境变量,标注必填/选填。

  • Step 3: 验证环境启动

Run: docker-compose up -d Run: docker-compose ps Expected: postgres, redis, minio 均为 running

  • Step 4: Commit
git add docker-compose.yml .env.example backend/Dockerfile frontend/Dockerfile
git commit -m "feat: 添加 Docker Compose 开发环境PostgreSQL + Redis + MinIO"

Phase 2: 后端核心服务W2-W3

Task 2.1: 报销任务管理 API

Files:

  • Create: backend/app/schemas/task.py

  • Create: backend/app/services/task_service.py

  • Create: backend/app/api/v1/tasks.py

  • Modify: backend/app/api/v1/router.py

  • Test: backend/tests/test_task_api.py

  • Step 1: 定义 Pydantic schemas

backend/app/schemas/task.py — 包含 TaskCreateRequest, TaskResponse, TaskListResponse 等请求/响应模型,字段按文档第 8 节 API 定义。

  • Step 2: 实现 TaskService 业务逻辑

包含方法:

  • create_task(user_id, company_id, user_intent) → 创建任务,状态设为 CREATED

  • get_task(task_id) → 查询任务详情

  • list_tasks(user_id, status, page, size) → 分页查询

  • update_status(task_id, status, current_agent) → 更新任务状态

  • Step 3: 实现 API 路由

POST /api/v1/reimbursement/tasks — 创建报销任务(对应文档 8.1 GET /api/v1/reimbursement/tasks/{task_id} — 查询任务详情 GET /api/v1/reimbursement/tasks — 列表查询

  • Step 4: 编写测试
@pytest.mark.asyncio
async def test_create_task(client):
    response = await client.post("/api/v1/reimbursement/tasks", json={
        "user_id": "U001",
        "company_id": "C001",
        "user_intent": "我要报这次北京出差的费用",
        "entry_channel": "web"
    })
    assert response.status_code == 201
    data = response.json()
    assert "task_id" in data
    assert data["status"] == "material_collecting"
  • Step 5: Commit
git add backend/
git commit -m "feat: 实现报销任务管理 API创建/查询/列表)"

Task 2.2: 文件上传与票据管理 API

Files:

  • Create: backend/app/schemas/document.py

  • Create: backend/app/services/document_service.py

  • Create: backend/app/services/storage_service.py

  • Create: backend/app/api/v1/documents.py

  • Modify: backend/app/api/v1/router.py

  • Test: backend/tests/test_document_api.py

  • Step 1: 实现 MinIO 存储服务

storage_service.py — 封装 MinIO 操作:

  • upload_file(bucket, file_name, file_data, content_type) → 上传文件

  • get_file_url(bucket, file_name) → 获取文件 URL

  • delete_file(bucket, file_name) → 删除文件

  • Step 2: 实现文档服务

document_service.py:

  • upload_document(task_id, file, document_type) → 保存文件到 MinIO创建 DB 记录

  • get_documents(task_id) → 查询任务下所有票据

  • update_ocr_result(document_id, ocr_result) → 更新 OCR 识别结果

  • Step 3: 实现 API 路由

POST /api/v1/reimbursement/tasks/{task_id}/documents — 上传票据(对应文档 8.2 GET /api/v1/reimbursement/tasks/{task_id}/documents — 查询票据列表

支持 multipart/form-data 文件上传。

  • Step 4: 编写测试

使用 mock MinIO测试文件上传和记录创建。

  • Step 5: Commit
git add backend/
git commit -m "feat: 实现文件上传与票据管理 APIMinIO 存储)"

Task 2.3: OCR 服务集成

Files:

  • Create: backend/app/services/ocr_service.py

  • Create: backend/app/services/ocr_providers/base.py

  • Create: backend/app/services/ocr_providers/baidu.py

  • Create: backend/app/services/ocr_providers/mock.py

  • Test: backend/tests/test_ocr_service.py

  • Step 1: 定义 OCR Provider 抽象接口

ocr_providers/base.py:

from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass
class OCRResult:
    document_type: str          # 识别出的票据类型
    raw_text: str               # 原始文字
    fields: dict                # 结构化字段
    confidence: float           # 整体置信度
    provider: str               # 提供商

class OCRProvider(ABC):
    @abstractmethod
    async def recognize(self, file_url: str, document_type: str | None = None) -> OCRResult:
        ...
  • Step 2: 实现 Mock OCR Provider开发测试用

mock.py — 根据文件名/类型返回预定义的结构化数据,用于开发阶段不依赖真实 OCR。

  • Step 3: 实现百度 OCR Provider

baidu.py — 调用百度云 OCR API支持

  • 增值税发票识别
  • 火车票识别
  • 机票行程单识别
  • 通用票据识别(兜底)

将百度返回结果标准化为 OCRResult

  • Step 4: 实现 OCR Service 门面

ocr_service.py:

  • recognize(file_url, document_type) → 根据 config.OCR_PROVIDER 选择 provider

  • 自动识别票据类型(如果未指定)

  • 返回标准化 OCRResult

  • 更新 document 表的 ocr_statusextracted_json

  • Step 5: 编写测试

使用 Mock Provider 测试完整流程。

  • Step 6: Commit
git add backend/
git commit -m "feat: 实现 OCR 服务(百度云 + Mock Provider"

Task 2.4: 规则引擎

Files:

  • Create: backend/app/services/rule_engine.py

  • Create: backend/app/schemas/rule.py

  • Create: backend/app/api/v1/rules.py

  • Create: backend/app/services/rule_checkers/__init__.py

  • Create: backend/app/services/rule_checkers/required_fields.py

  • Create: backend/app/services/rule_checkers/attachment_check.py

  • Create: backend/app/services/rule_checkers/duplicate_invoice.py

  • Create: backend/app/services/rule_checkers/amount_limit.py

  • Create: backend/app/services/rule_checkers/date_validity.py

  • Create: backend/app/services/rule_checkers/expense_type_match.py

  • Test: backend/tests/test_rule_engine.py

  • Step 1: 定义规则引擎核心接口

rule_engine.pyRuleEngine 类:

class RuleCheckResult:
    rule_code: str
    severity: RiskLevel
    action: RuleAction
    message: str
    suggestion: str
    policy_ref: str
    hit_detail: dict

class RuleEngine:
    def __init__(self, db: AsyncSession):
        self.db = db
        self.checkers: dict[str, RuleChecker] = {}

    async def run_precheck(self, reimbursement_id: str) -> PrecheckResult:
        """执行完整预审"""
        ...

    async def run_single_rule(self, rule_code: str, context: dict) -> RuleCheckResult | None:
        """执行单条规则"""
        ...
  • Step 2: 实现 MVP 阶段的 6 条核心规则检查器

每条规则是一个 RuleChecker 类,接收报销数据上下文,返回 RuleCheckResult | None

  1. RequiredFieldsChecker — 必填字段校验
  2. AttachmentCheckChecker — 附件完整性校验(如住宿费必须上传酒店流水)
  3. DuplicateInvoiceChecker — 重复发票检查invoice_code + invoice_number + amount 去重)
  4. AmountLimitChecker — 金额超标校验(按城市/职级/费用类型检查标准)
  5. DateValidityChecker — 日期合理性校验(费用日期在出差期间内)
  6. ExpenseTypeMatchChecker — 费用类型与票据类型匹配校验
  • Step 3: 实现规则管理 API

  • GET /api/v1/rules — 列出所有规则

  • POST /api/v1/rules — 创建规则

  • PUT /api/v1/rules/{rule_id} — 更新规则

  • PATCH /api/v1/rules/{rule_id}/toggle — 启用/禁用规则

  • Step 4: 种子数据

backend/alembic/seed/rules.sql — 预置文档 7.2 节的 3 条示例规则 + 金额标准表。

  • Step 5: 编写测试

对每条规则编写单元测试,使用 mock 数据验证命中/未命中场景。

  • Step 6: Commit
git add backend/
git commit -m "feat: 实现规则引擎6 条核心规则 + 管理 API + 种子数据)"

Task 2.5: 影子报销账本 CRUD

Files:

  • Create: backend/app/schemas/reimbursement.py

  • Create: backend/app/services/ledger_service.py

  • Create: backend/app/api/v1/ledger.py

  • Modify: backend/app/api/v1/router.py

  • Test: backend/tests/test_ledger_api.py

  • Step 1: 定义 Pydantic schemas

包含:ReimbursementDraftResponse, ReimbursementItemCreate, ReimbursementItemResponse, PrecheckResultResponse

  • Step 2: 实现 LedgerService

核心方法:

  • create_shadow_reimbursement(task_id, data) → 创建影子报销记录

  • get_draft(reimbursement_id) → 获取报销草稿

  • get_draft_by_task(task_id) → 通过任务 ID 获取草稿

  • update_precheck_result(reimbursement_id, result) → 更新预审结果

  • add_item(reimbursement_id, item_data) → 添加报销明细

  • Step 3: 实现 API 路由

GET /api/v1/reimbursement/tasks/{task_id}/draft — 获取报销草稿(对应文档 8.4 GET /api/v1/reimbursement/tasks/{task_id}/precheck-result — 获取预审结果(对应文档 8.5

  • Step 4: 编写测试

  • Step 5: Commit

git add backend/
git commit -m "feat: 实现影子报销账本 CRUD API"

Task 2.6: 补件与提交 API

Files:

  • Create: backend/app/api/v1/supplements.py

  • Create: backend/app/services/supplement_service.py

  • Create: backend/app/services/sync_service.py

  • Modify: backend/app/api/v1/router.py

  • Test: backend/tests/test_supplement_api.py

  • Step 1: 实现补件服务

supplement_service.py:

  • create_supplement_request(reimbursement_id, items) → 创建补件请求

  • respond_supplement(request_id, response_text, document_ids) → 用户补件响应

  • get_supplement_requests(task_id) → 查询补件请求列表

  • Step 2: 实现同步服务MVP 阶段为模拟)

sync_service.py:

  • mock_sync_to_backend(reimbursement_id) → 模拟后端同步,生成假的 backend_bill_id

  • get_sync_status(task_id) → 查询同步状态

  • Step 3: 实现 API 路由

POST /api/v1/reimbursement/tasks/{task_id}/supplements — 用户补件(对应文档 8.6 POST /api/v1/reimbursement/tasks/{task_id}/submit — 用户确认提交(对应文档 8.7 GET /api/v1/reimbursement/tasks/{task_id}/sync-status — 查询同步状态(对应文档 8.8

  • Step 4: 编写测试

  • Step 5: Commit

git add backend/
git commit -m "feat: 实现补件与提交确认 API含模拟同步"

Phase 3: Agent 编排W3-W4

Task 3.1: Agent Orchestrator 状态机

Files:

  • Create: backend/app/agents/orchestrator.py

  • Create: backend/app/agents/state.py

  • Create: backend/app/api/v1/agent.py

  • Modify: backend/app/api/v1/router.py

  • Test: backend/tests/test_orchestrator.py

  • Step 1: 定义 Agent 状态和上下文

agents/state.py:

from dataclasses import dataclass, field
from app.models.enums import TaskStatus

@dataclass
class AgentContext:
    task_id: str
    status: TaskStatus
    user_intent: str | None = None
    current_agent: str | None = None
    ocr_results: list[dict] = field(default_factory=list)
    reimbursement_data: dict | None = None
    precheck_result: dict | None = None
    supplement_requests: list[dict] = field(default_factory=list)
    error_message: str | None = None
    retry_count: int = 0
  • Step 2: 实现 Orchestrator 状态机

agents/orchestrator.py — 核心编排逻辑:

状态转换图(对应文档 4.2 节):

CREATED → MATERIAL_COLLECTING → PARSING → DRAFT_GENERATED → PRECHECKING
→ NEED_SUPPLEMENT → MATERIAL_COLLECTING循环
→ PENDING_USER_CONFIRM → SUBMITTING → SYNCED
→ SYNC_FAILED → SUBMITTING重试

方法:

  • run(task_id, start_from) → 启动编排

  • _transition_to(context, new_status, agent_name) → 状态转换

  • _run_agent(context, agent_name) → 执行单个 Agent

  • _handle_agent_result(context, result) → 处理 Agent 返回结果

  • Step 3: 实现 Agent 启动 API

POST /api/v1/reimbursement/tasks/{task_id}/agent/run — 启动 Agent 处理(对应文档 8.3

  • Step 4: 编写状态机转换测试

覆盖所有正常路径和异常路径(解析失败、预审需补件、同步失败重试等)。

  • Step 5: Commit
git add backend/
git commit -m "feat: 实现 Agent Orchestrator 状态机编排"

Task 3.2: 5 个 Agent 实现

Files:

  • Create: backend/app/agents/base_agent.py

  • Create: backend/app/agents/intake_agent.py

  • Create: backend/app/agents/parse_agent.py

  • Create: backend/app/agents/rule_check_agent.py

  • Create: backend/app/agents/explain_agent.py

  • Create: backend/app/agents/sync_agent.py

  • Test: backend/tests/test_agents.py

  • Step 1: 定义 Agent 基类

agents/base_agent.py:

from abc import ABC, abstractmethod
from app.agents.state import AgentContext

class AgentResult:
    success: bool
    data: dict
    next_action: str | None  # 继续编排 / 等待用户 / 需要补件
    error: str | None

class BaseAgent(ABC):
    name: str

    @abstractmethod
    async def execute(self, context: AgentContext, db: AsyncSession) -> AgentResult:
        ...
  • Step 2: 实现受理 AgentIntakeAgent

职责:理解用户意图,收集上下文。

  • 分析 user_intent 文本,提取报销类型、出差信息

  • 调用 LLM 做 intent classification

  • 返回结构化的任务信息

  • Step 3: 实现单据解析 AgentParseAgent

职责:调用 OCR生成报销草稿。

  • 遍历任务下的所有 document调用 ocr_service

  • 将 OCR 结果汇总为报销明细

  • 创建 ShadowReimbursement + ReimbursementItem

  • 自动识别费用类型

  • Step 4: 实现规则校验 AgentRuleCheckAgent

职责:调用规则引擎完成预审。

  • 从 DB 加载所有 enabled 规则

  • 传入报销数据上下文执行规则引擎

  • 收集所有 RuleHit

  • 计算 overall risk_level

  • 更新预审状态

  • Step 5: 实现解释与补件 AgentExplainAgent

职责:将规则命中结果转化为用户可理解的解释。

  • 遍历 rule_hits使用 LLM 生成自然语言解释

  • 创建 supplement_requests缺件类型的自动创建补件请求

  • 生成修改建议

  • Step 6: 实现同步执行 AgentSyncAgent

职责:生成标准报销单,调用后端同步。

  • 将 ShadowReimbursement 数据映射为标准报销单格式

  • 调用 sync_service.mock_sync_to_backend

  • 记录 SyncRecord

  • 处理同步失败重试

  • Step 7: 编写每个 Agent 的单元测试

使用 mock DB 和 mock OCR/LLM 测试每个 Agent 的输入输出。

  • Step 8: Commit
git add backend/
git commit -m "feat: 实现 5 个 Agent受理/解析/规则校验/解释补件/同步)"

Task 3.3: LLM 集成层

Files:

  • Create: backend/app/services/llm_service.py

  • Create: backend/app/services/llm_prompts/__init__.py

  • Create: backend/app/services/llm_prompts/intent_classification.py

  • Create: backend/app/services/llm_prompts/risk_explanation.py

  • Create: backend/app/services/llm_prompts/expense_type_mapping.py

  • Test: backend/tests/test_llm_service.py

  • Step 1: 实现 LLM Service 封装

llm_service.py:

  • chat(system_prompt, user_message, json_mode) → 调用 LLM API

  • 支持多 providerOpenAI 兼容接口,适配国内模型)

  • 统一错误处理和重试

  • 响应解析JSON mode

  • Step 2: 编写 Prompt 模板

3 个核心 Prompt

  • intent_classification — 分析用户意图,识别报销类型

  • risk_explanation — 将规则命中结果转为自然语言解释

  • expense_type_mapping — 根据 OCR 结果匹配费用类型

  • Step 3: 编写测试(使用 mock LLM 响应)

  • Step 4: Commit

git add backend/
git commit -m "feat: 实现 LLM 集成层(多 Provider + Prompt 模板)"

Task 3.4: 审计日志

Files:

  • Create: backend/app/services/audit_service.py

  • Create: backend/app/api/v1/audit.py

  • Test: backend/tests/test_audit.py

  • Step 1: 实现 AuditService

核心方法:

  • log(action, actor, target_type, target_id, detail) → 记录审计日志
  • query_logs(target_type, target_id, actor, date_range) → 查询日志

需要记录的动作(对应文档 12.1 节文件上传、OCR 识别、Agent 调用、规则命中、用户补件、用户确认、后端同步。

  • Step 2: 在所有关键路径埋点

在 task_service、document_service、ocr_service、rule_engine、orchestrator 的关键操作中调用 audit_service.log()

  • Step 3: 实现审计日志查询 API

GET /api/v1/audit/logs — 查询审计日志(支持按 target_type、target_id、date_range 过滤)

  • Step 4: 编写测试

  • Step 5: Commit

git add backend/
git commit -m "feat: 实现审计日志服务(记录 + 查询 API"

Phase 4: 前端核心页面W4-W5

Task 4.1: 报销入口页 + 上传组件

Files:

  • Create: frontend/src/views/HomeView.vue

  • Create: frontend/src/views/UploadView.vue

  • Create: frontend/src/components/FileUpload.vue

  • Create: frontend/src/stores/task.ts

  • Create: frontend/src/api/task.ts

  • Create: frontend/src/api/document.ts

  • Step 1: 实现 API 调用层

api/task.ts:

import api from './index'

export const createTask = (data: { userId: string; companyId: string; userIntent: string }) =>
  api.post('/reimbursement/tasks', data)

export const getTask = (taskId: string) =>
  api.get(`/reimbursement/tasks/${taskId}`)

export const runAgent = (taskId: string, startFrom = 'intake') =>
  api.post(`/reimbursement/tasks/${taskId}/agent/run`, { start_from: startFrom, mode: 'precheck' })

api/document.ts:

export const uploadDocument = (taskId: string, file: File, documentType: string) => {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('document_type', documentType)
  return api.post(`/reimbursement/tasks/${taskId}/documents`, formData)
}
  • Step 2: 实现报销入口页 HomeView

按文档 9.2 节:

  • 对话输入框(用户输入报销意图)
  • 上传按钮
  • 最近任务列表
  • 常用报销类型快捷按钮("报差旅"、"看发票能不能报"等)
  • 智能引导提示

交互流程:用户输入意图 → 调用 createTask → 跳转到上传页

  • Step 3: 实现文件上传组件 FileUpload

  • 支持拖拽上传

  • 支持多文件

  • 文件类型校验PDF、JPG、PNG

  • 文件大小限制

  • 上传进度条

  • 预览缩略图

  • Step 4: 实现票据上传页 UploadView

  • 引用 FileUpload 组件

  • 选择票据类型下拉框(增值税发票、火车票、机票行程单等)

  • 已上传文件列表

  • "开始识别" 按钮 → 调用 runAgent → 跳转到草稿页

  • Step 5: Commit

git add frontend/
git commit -m "feat: 实现报销入口页和票据上传页"

Task 4.2: 报销草稿页

Files:

  • Create: frontend/src/views/DraftView.vue

  • Create: frontend/src/components/ExpenseTable.vue

  • Create: frontend/src/api/precheck.ts

  • Step 1: 实现报销草稿页 DraftView

按文档 9.3 节展示:

  • 报销人信息(姓名、部门、成本中心、项目)

  • 报销事由

  • 费用明细表格ExpenseTable 组件)

  • 票据附件缩略图列表

  • AI 自动识别结果标注

  • 可编辑字段(金额、事由等可修改)

  • 预审状态指示器

  • "预审" 按钮 → 跳转到预审结果页

  • Step 2: 实现 ExpenseTable 组件

  • Ant Design Table 展示费用明细

  • 列:费用类型、金额、税额、发生日期、城市、商户、风险等级标签

  • 支持行内编辑

  • 风险等级彩色标签(绿/黄/橙/红)

  • Step 3: Commit

git add frontend/
git commit -m "feat: 实现报销草稿页和费用明细表格组件"

Task 4.3: 预审结果页 + 补件交互页

Files:

  • Create: frontend/src/views/PrecheckView.vue

  • Create: frontend/src/views/SupplementView.vue

  • Create: frontend/src/components/RuleHitCard.vue

  • Step 1: 实现预审结果页 PrecheckView

按文档 9.4 节:

  • 总体结论卡片(通过/需补件/有风险)

  • 风险等级指示(彩色徽章)

  • 通过项列表(绿色勾选)

  • 风险项列表RuleHitCard 组件)

  • 缺件项列表(橙色提示)

  • 每条规则命中显示:问题说明、制度依据、修改建议

  • "一键补件" 按钮 → 跳转到补件页

  • "确认提交" 按钮(仅预审通过时可用)

  • Step 2: 实现 RuleHitCard 组件

  • 规则名称和编码

  • 风险等级标签

  • 问题描述

  • 制度依据链接

  • 修改建议

  • 展开详情

  • Step 3: 实现补件交互页 SupplementView

  • 显示待补件清单

  • 每个补件项:类型(补充附件/补充说明/修改字段)、提示文案

  • 上传附件(调用 FileUpload

  • 文本回复输入框

  • 提交补件 → 调用 supplement API → 跳转回预审页

  • Step 4: Commit

git add frontend/
git commit -m "feat: 实现预审结果页和补件交互页"

Task 4.4: 提交确认页 + 审计日志页

Files:

  • Create: frontend/src/views/ConfirmView.vue

  • Create: frontend/src/views/AuditView.vue

  • Create: frontend/src/api/audit.ts

  • Step 1: 实现提交确认页 ConfirmView

  • 最终报销单摘要(不可编辑)

  • 总金额确认

  • 费用明细汇总

  • 附件清单

  • 同步目标系统选择MVP 默认 expense_system

  • "确认提交" 按钮 → 调用 submit API

  • 同步状态轮询展示(提交中 → 已同步/同步失败)

  • Step 2: 实现审计日志页 AuditView

按文档 9.5 节(简化版):

  • 时间线展示所有操作记录

  • 筛选:按任务、操作类型、时间范围

  • 每条日志:时间、操作人、操作类型、详情

  • Step 3: Commit

git add frontend/
git commit -m "feat: 实现提交确认页和审计日志页"

Phase 5: 联调与集成W5-W6

Task 5.1: 前后端联调

Files:

  • Modify: 多个前后端文件(修复联调问题)

  • Step 1: 启动前后端全栈

后端:cd backend && uvicorn app.main:app --reload 前端:cd frontend && npm run dev

  • Step 2: 跑通完整报销流程

按文档 3.1 节的 10 步流程:

  1. 在首页创建报销任务
  2. 上传 2-3 张模拟票据(增值税发票、火车票、酒店流水)
  3. 点击"开始识别" → Agent 启动
  4. 查看草稿页 → 确认自动识别结果
  5. 执行预审 → 查看预审结果
  6. 如有缺件/风险 → 补件
  7. 确认提交 → 查看同步状态
  8. 查看审计日志
  • Step 3: 修复联调过程中发现的问题

API 响应格式不一致、字段缺失、前后端类型不匹配等。

  • Step 4: Commit
git add .
git commit -m "fix: 前后端联调修复"

Task 5.2: 规则配置与种子数据

Files:

  • Create: backend/alembic/seed/expense_policies.sql

  • Create: backend/alembic/seed/expense_rules.sql

  • Create: backend/alembic/seed/city_levels.sql

  • Create: backend/alembic/seed/hotel_limits.sql

  • Step 1: 编写差旅报销制度种子数据

按典型企业差旅报销制度配置:

  • 城市等级(一线/二线/三线)

  • 住宿标准(按城市等级 × 职级)

  • 交通标准(高铁/飞机按职级)

  • 餐补标准

  • 必须上传的附件类型映射

  • Step 2: 编写规则种子数据

至少配置文档 7.2 节的 3 条示例规则 + MVP 需要的 6 条核心规则。

  • Step 3: 编写数据初始化脚本

backend/scripts/seed_data.py — 一键初始化所有种子数据。

  • Step 4: Commit
git add backend/
git commit -m "feat: 添加差旅报销制度和规则种子数据"

Phase 6: 测试与打磨W7-W8

Task 6.1: 后端集成测试

Files:

  • Create: backend/tests/test_integration_flow.py

  • Create: backend/tests/conftest.py(更新,添加测试数据库 fixture

  • Step 1: 编写完整流程集成测试

@pytest.mark.asyncio
async def test_full_reimbursement_flow(db, client):
    # 1. 创建任务
    task = await create_task(client, user_id="U001", intent="报北京出差费用")
    assert task["status"] == "material_collecting"

    # 2. 上传票据
    doc1 = await upload_document(client, task["task_id"], "vat_invoice", "invoice.pdf")
    doc2 = await upload_document(client, task["task_id"], "train_ticket", "train.pdf")

    # 3. 启动 Agent
    result = await run_agent(client, task["task_id"])
    assert result["status"] in ["draft_generated", "prechecking"]

    # 4. 获取草稿
    draft = await get_draft(client, task["task_id"])
    assert len(draft["items"]) > 0

    # 5. 获取预审结果
    precheck = await get_precheck_result(client, task["task_id"])
    assert "risk_level" in precheck

    # 6. 如果需要补件
    if precheck["precheck_status"] == "need_supplement":
        supplements = precheck["rule_hits"]
        for s in supplements:
            if s["action"] == "require_attachment":
                await respond_supplement(client, task["task_id"], s["id"], "已补充")
        # 重新预审
        await run_agent(client, task["task_id"], start_from="precheck")

    # 7. 确认提交
    submit = await submit_task(client, task["task_id"])
    assert submit["status"] == "submitting"

    # 8. 检查同步状态
    sync = await get_sync_status(client, task["task_id"])
    assert sync["sync_status"] == "success"
  • Step 2: 确保所有测试通过

Run: cd backend && pytest tests/ -v --tb=short Expected: All PASS

  • Step 3: Commit
git add backend/
git commit -m "test: 添加完整报销流程集成测试"

Task 6.2: 前端 E2E 测试(可选)

Files:

  • Create: frontend/e2e/reimbursement.spec.ts(如选用 Playwright

  • Step 1: 安装 Playwright

cd frontend && npm install -D @playwright/test
npx playwright install
  • Step 2: 编写核心流程 E2E 测试

模拟用户从创建任务到提交确认的完整操作。

  • Step 3: 确保测试通过

  • Step 4: Commit

git add frontend/
git commit -m "test: 添加前端 E2E 测试"

Task 6.3: Bug 修复与 UI 打磨

  • Step 1: 走查所有页面,修复视觉和交互问题

  • 响应式布局适配

  • Loading 状态

  • 错误提示

  • 空状态

  • 表单校验

  • Step 2: 添加 Demo 数据展示效果

  • Step 3: Commit

git add .
git commit -m "fix: UI 打磨和 Bug 修复"

Task 6.4: 部署与文档

Files:

  • Create: docker-compose.prod.yml

  • Modify: README.md

  • Create: docs/api.md

  • Create: docs/deployment.md

  • Step 1: 编写生产 Docker Compose

包含前后端 + DB + Redis + MinIO + Nginx 反向代理。

  • Step 2: 编写部署文档

环境要求、配置说明、启动步骤、常用运维命令。

  • Step 3: 编写 API 文档

FastAPI 自动生成 Swagger补充说明和示例。

  • Step 4: 更新 README

项目简介、架构图、快速启动、开发指南。

  • Step 5: Commit
git add .
git commit -m "docs: 添加部署文档和 README"

任务总览

Phase 周数 任务数 可并行
Phase 1: 项目基建 W1 4 前端骨架 + 后端骨架 + Docker 可并行
Phase 2: 后端核心 W2-W3 6 任务API + 文件上传 + OCR 可并行
Phase 3: Agent 编排 W3-W4 4 Orchestrator → Agents → LLM → 审计(部分串行)
Phase 4: 前端页面 W4-W5 4 草稿/预审/补件/确认页可并行
Phase 5: 联调集成 W5-W6 2 联调 + 种子数据
Phase 6: 测试打磨 W7-W8 4 测试 + 修复 + 部署
总计 8 周 26 个任务

风险与缓解

风险 影响 缓解措施
OCR 识别准确率不够 草稿数据错误 允许用户手动修改,低置信度高亮提示
LLM 响应慢或幻觉 用户体验差 Prompt 严格约束输出格式,超时 fallback
规则引擎复杂度超预期 延期 MVP 先做 6 条硬编码规则JSON 配置化后续迭代
前后端联调问题多 延期 W5 提前开始联调,边开发边对齐 API
3-5 人不够 交付延期 优先砍规则管理页和审计页W8 补)

验收标准

MVP 完成的标志:

  • 用户能通过 Web 界面创建差旅报销任务
  • 上传 3 种以上票据类型(增值税发票、火车票、酒店流水)
  • OCR 自动识别票据信息并生成报销草稿
  • 规则引擎执行 6 条核心预审规则
  • 预审结果以可视化方式展示(风险等级、命中规则、修改建议)
  • 用户能补件并重新预审
  • 用户确认后模拟同步成功
  • 影子报销账本完整记录业务数据
  • 审计日志记录所有关键操作
  • 完整流程端到端测试通过