Files
shanxia2.0/docs/bx-migration/00-migration-plan.md
T
2026-06-07 00:40:01 +08:00

17 KiB
Raw Blame History

报销系统(bx)移植实施方案

编写日期:2026-06-06
状态:方案已定稿,待按阶段实施
源系统:shanxia/bx/bx-backend + shanxia/bx/bx-web
目标系统:shanxia2.0/backend + shanxia2.0/web-admin
DDL 基线:docs/sql/20260606_bx_tables.sql(已在 shanxia2.0 库执行)


1. 迁移目标

将原报销系统业务功能迁入山峡一体化门户(shanxia2.0),实现:

  1. 业务等价:备用金、报销、工作流、支付/冲抵、视图中心等能力与 bx 原系统一致;
  2. 协议兼容:移植 API 保持 bx 原 REST 风格(GET/PUT/DELETE/POST)、入参位置、响应 envelope(code:0、msg、data.rows),不统一为 CRM 的 POST Body 风格;
  3. 体系融合:用户/角色/菜单/认证使用门户 sys_*,业务表使用 bx_* + 门户公共字段;
  4. UI 融合:Web 端挂「报销管理」一级菜单,页面适配 Fresh Mint 规范(第二阶段)。

不在本次范围:bx-mobile 移动端、bx_user/bx_role/bx_menu 等独立权限表、历史业务数据导入(空库启动)。


2. 范围边界

2.1 数据库(10 张业务表,已完成 DDL)

表 用途 DDL 状态
bx_expense_item 费用项目(二级树) ✅
bx_workflow_def 工作流定义 ✅
bx_workflow_node 工作流节点 ✅
bx_advance_apply 备用金申请 ✅
bx_advance_approval 备用金审批记录 ✅
bx_reimburse_apply 报销申请 ✅
bx_reimburse_detail 报销明细 ✅
bx_reimburse_approval 报销审批记录 ✅
bx_reimburse_offset 冲抵关联 ✅
bx_attachment 附件 ✅

不建表:bx_user、bx_role、bx_role_user、bx_menu、bx_menu_role。

2.2 后端 API(约 60 个端点,待移植)

模块 源文件 端点数 优先级
费用项目 routers/expense_item.py 5 P1
工作流 routers/workflow.py 8 P1
备用金 routers/advance.py 14 P2
附件 routers/attachment.py 6 P2
报销 routers/reimburse.py 15 P3
支付/发放 routers/payment.py 12 P4

不移植:auth.py、user.py、role.py、menu.py。

2.3 前端页面(13 页,第二阶段)

分组 页面 源文件
基础配置 费用项目管理、工作流(备用金)、工作流(报销) ExpenseProjectPage、WfAdvancePage、WfReimbursePage
备用金 申请、审批、发放管理 AdvanceApplyPage、AdvanceApprovePage、AdvanceIssuePage
报销 申请、审批、支付管理 ReimburseApplyPage、ReimburseApprovePage、PaymentPage
视图中心 我的申请、待我审批、全部申请单 MinePage、TodoPage、AllPage

不移植:用户/角色/菜单管理、首页 Dashboard、Demo 页。


3. 核心原则(与 CRM 移植的差异)

原则 说明
API 协议保持 bx 原样 列表用 GET + Query;响应 { code: 0, msg, data };分页 data.rows;禁止改为门户 code:200 / message / data.list
Router 近原样复制 从 bx-backend/app/routers/ 复制到 backend/app/api/bx/,仅改 Depends、import、前缀、租户注入
Service 选择性迁移 services/*.py 业务算法可复用;必须加 tenant_no、UUID 主键、sys_user 用户 ID
不走 CRM crud 分层 bx 模块内部保持 router → service → model,不强制拆 crud/bx/(除非后续维护需要)
最小改动 不重命名 URL、不改 HTTP 方法、不改前端 camelCase 字段名
门户能力注入 认证、租户、DB Session、对象存储用 shanxia2.0 现有能力

4. 源系统与目标系统对照

4.1 技术栈(同源,利于移植)

项 bx-backend shanxia2.0 backend
语言 Python 3 Python 3.9
框架 FastAPI FastAPI
ORM SQLAlchemy SQLAlchemy
校验 Pydantic v2 Pydantic v2

4.2 API 协议对照

维度 bx 原系统 shanxia2.0 CRM(勿用于 bx) bx 移植目标
成功码 0 200 保持 0
消息字段 msg message 保持 msg
时间戳 无 timestamp 不加
分页字段 rows, total, page, pageSize list, total, page, pageSize 保持 rows
列表请求 GET + query POST + body 保持 GET
路径示例 /api/advances/ /api/web/crm/customer/list 保持 /api/advances/

4.3 数据模型对照

维度 bx 原 sx.sql shanxia2.0 已执行 DDL
主键 bigint AUTO_INCREMENT varchar(64) UUID
外键 ID bigint varchar(64)
租户 无 tenant_no DEFAULT 'shanxia2.0'
用户引用 bx_user.id sys_user.id

5. 目标架构

5.1 后端目录结构

backend/app/
├── models/bx_models.py              # 10 张表 ORM(对齐 20260606 DDL)
├── schemas/bx/
│   ├── common.py                    # ApiResponse / PagedData(从 bx 复制)
│   ├── expense_item.py
│   ├── workflow.py
│   ├── advance.py
│   ├── reimburse.py
│   ├── payment.py
│   └── attachment.py                # id: int → str
├── services/bx/                     # 从 bx-backend/services 迁移
│   ├── base.py                      # 可选:通用分页(改 UUID)
│   ├── expense_item.py
│   ├── workflow.py
│   ├── advance.py
│   ├── reimburse.py
│   ├── payment.py
│   └── attachment.py
├── core/
│   └── bx_auth.py                   # 门户 user → BxUserContext
└── api/bx/
    ├── __init__.py                  # 聚合 bx_router
    ├── expense_item.py              # prefix="/expense-items"
    ├── workflow.py                  # prefix="/workflows"
    ├── advance.py                   # prefix="/advances"
    ├── reimburse.py                 # prefix="/reimburses"
    ├── payment.py                   # prefix="/payments"
    └── attachment.py                # prefix="/attachments"

5.2 路由挂载(避免 /api/api/...)

bx 原 router 前缀为 /api/advances;门户 main.py 已挂 prefix="/api"。

规则:bx router 的 APIRouter(prefix=...) 去掉 /api,例如:

# bx 原:prefix="/api/advances"
# 门户:prefix="/advances"
router = APIRouter(prefix="/advances", tags=["备用金管理"])
# main.py
from app.api.bx import bx_router
app.include_router(bx_router, prefix=settings.API_V1_PREFIX)  # → /api/advances/...

5.3 响应包装(独立体系)

bx 模块 禁止 使用 app.core.response.success_response()。

统一使用 schemas/bx/common.py:

class ApiResponse(BaseModel, Generic[T]):
    code: int = 0
    msg: str = "success"
    data: Optional[T] = None

class PagedData(BaseModel, Generic[T]):
    rows: List[T]
    total: int
    page: int
    page_size: int  # JSON 序列化为 pageSize(alias_generator=to_camel)

6. 门户适配层

6.1 认证(core/bx_auth.py)

bx 原:get_current_user → ORM User,user.id: int
门户:require_admin_or_super_admin → dict,user_id: str

@dataclass
class BxUserContext:
    id: str           # sys_user.id
    name: str         # nick_name 冗余写入 applicant_name 等
    dept_id: str      # create_dept_id

def get_bx_current_user(user: dict = Depends(require_admin_or_super_admin)) -> BxUserContext:
    ...

Router 中:Depends(get_current_user) → Depends(get_bx_current_user)。

6.2 租户隔离

所有业务查询/写入通过 get_tenant_no(db, user_dict) 获取 tenant_no,在 Service 层追加:

query.filter(Model.tenant_no == tenant_no, Model.del_flag == 0)

创建记录时:

obj_in["id"] = generate_uuid()
obj_in["tenant_no"] = tenant_no
obj_in["create_by"] = user.id
obj_in["create_dept_id"] = user.dept_id or ""

6.3 API 加密中间件

门户默认对 /api/* 启用 ApiCryptoMiddleware;bx 原系统无加密。

开发阶段:将 bx 业务路径加入 API_ENCRYPT_EXCLUDE_PATHS(backend/app/core/config.py):

/api/expense-items,
/api/workflows,
/api/advances,
/api/reimburses,
/api/payments,
/api/attachments

前端接入 web-admin 后:评估是否改为走门户加密(需 bx 页 axios 适配),或长期保持 exclude。

6.4 附件 / MinIO

不直接拷贝 bx-backend/app/services/minio.py。实现 services/bx/storage.py 适配器,内部调用门户已有 OSS/MinIO 上传能力,对外仍暴露:

  • POST /api/attachments/upload(multipart)
  • GET /api/attachments/download/{id}(StreamingResponse)
  • GET /api/attachments/preview/{id}

7. 分阶段实施计划

总览

P0 基础设施 → P1 基础配置 → P2 备用金+附件 → P3 报销 → P4 支付 → P5 菜单 → P6 前端
     │              │              │              │         │        │         │
   ORM/Auth      expense+wf     advance        reimburse  payment  sys_menu  web-admin

P0:基础设施(后端骨架)

步骤 任务 产出 完成标准
P0-1 创建 models/bx_models.py 10 表 ORM 字段顺序与 DDL 一致
P0-2 创建 schemas/bx/common.py ApiResponse / PagedData 与 bx 响应一致
P0-3 创建 core/bx_auth.py BxUserContext + Depends 能解析门户 JWT 用户
P0-4 创建 api/bx/__init__.py 空 bx_router main.py 可注册
P0-5 配置加密排除路径 config 更新 bx 路径不加密
P0-6 迁移 utils/codegen.py、date_range.py 单号生成等 单元可调用

验收:服务启动无报错;GET /api/health 正常;bx_router 已挂载。


P1:基础配置(费用项目 + 工作流)

步骤 任务 源参考
P1-1 schemas/bx/expense_item.py bx-backend/app/schemas/expense_item.py
P1-2 services/bx/expense_item.py 加 tenant + UUID
P1-3 api/bx/expense_item.py 5 端点原样
P1-4 schemas/bx/workflow.py + service + router 8 端点

API 清单:

方法 路径
GET /api/expense-items/tree
GET/POST/PUT/DELETE /api/expense-items/、/api/expense-items/{item_id}
GET /api/workflows/、/api/workflows/biz/{biz_type}、/api/workflows/{workflow_id}
POST/PUT/DELETE /api/workflows/、/api/workflows/{workflow_id}
PUT /api/workflows/{workflow_id}/nodes

验收:Swagger 可调通 CRUD;费用树、工作流节点保存与原 bx 行为一致。


P2:备用金 + 附件

步骤 任务 说明
P2-1 advance schemas / service / router 含 submit/approve/reject 状态机
P2-2 attachment storage 适配 + router upload/download/preview/bind-biz
P2-3 联调:申请 → 上传附件 → 提交审批 端到端

API 清单(advance,14 端点):

方法 路径
GET /api/advances/、/my、/my/stats、/stats/status-counts
GET /api/advances/pending-approval、/pending-approval/stats、/approved-by-me
GET /api/advances/{advance_id}、/{advance_id}/approvals
POST /api/advances/、/{advance_id}/submit、/approve、/reject
PUT/DELETE /api/advances/{advance_id}

验收:完整备用金审批链路;附件 upload/list/download 可用。


P3:报销

步骤 任务
P3-1 reimburse schemas(含 detail)
P3-2 reimburse service(明细、标题生成)
P3-3 reimburse router(15 端点)

验收:报销申请 CRUD + 明细 + 审批流与 bx 一致。


P4:支付 / 发放 / 冲抵

步骤 任务
P4-1 payment service(最复杂:pay/offset/issue/batch-offset)
P4-2 payment router(含 CSV export StreamingResponse)

验收:报销支付、备用金发放、冲抵关联、offset-records 查询正确。


P5:菜单与权限(门户 sys_menu)

在 sys_menu 新增一级 「报销管理」(parent_id='2',Web 端根),子结构:

报销管理
├── 基础配置
│   ├── 费用项目管理
│   ├── 工作流配置(备用金)
│   └── 工作流配置(报销单)
├── 备用金
│   ├── 备用金申请
│   ├── 备用金审批
│   └── 备用金发放管理
├── 报销
│   ├── 报销申请
│   ├── 报销审批
│   └── 支付管理(发放/冲抵)
└── 视图中心
    ├── 我的申请单
    ├── 待我审批
    └── 全部申请单(总经理)

权限标识(perms)建议前缀 bx:,与 CRM crm: 并列。角色-菜单通过 sys_role_menu 配置(可参考 bx 原 bx-menu-role-init.sql 角色语义:员工/财务/总经理)。

验收:admin 登录可见菜单;无权限用户 403。


P6:前端移植(web-admin)

步骤 任务 参考
P6-1 拷贝 bx-web/src/views/pages/*.vue → web-admin/src/views/bx/ 排除 User/Role/Menu/Home/Demo
P6-2 拷贝/适配 bx-web/src/api/ baseURL 指向门户 /api,协议已是 bx 风格
P6-3 Fresh Mint UI 适配 参考 docs/crm-migration/step-03-frontend-adapt.md
P6-4 认证改为门户登录态 不再调 /api/auth/login
P6-5 用户下拉改用 sys_user API 替代 userApi.dropdown
P6-6 路由与 component 路径对齐 sys_menu hash 路由

UI 规范:docs/web-admin-ui-spec-fresh-mint.md;列表参考 /#/demo/order,树形参考 /#/demo/category。

验收:各页面功能与 bx-web 对照通过;UI 符合 Fresh Mint。


8. 单模块后端实施 SOP(每个模块重复)

  1. 读源:bx-backend/app/routers/{module}.py + services/{module}.py,中文简述业务逻辑
  2. Schema:复制到 schemas/bx/,id 及外键改 str,保留 camelCase alias
  3. Service:复制到 services/bx/,追加 tenant_no 过滤、UUID 生成、用户字段映射
  4. Router:复制到 api/bx/,改 Depends / import / prefix(去 /api)
  5. 注册:在 api/bx/__init__.py include_router
  6. 验证:Swagger 或 Postman 对照 bx 原接口逐项测试
  7. 更新:module-status.md

9. 完整 API 路径索引(移植后保持不变)

详见 01-api-inventory.md。


10. 验证清单

10.1 后端协议

  • 成功响应 code === 0,字段名为 msg(非 message)
  • 分页 data.rows(非 data.list)
  • 列表接口为 GET + query 参数
  • 路径与 bx-web api/index.ts 一致
  • 错误时 HTTP 4xx/5xx + 合理 detail(与 bx 行为接近)

10.2 业务

  • 费用项目二级树 CRUD
  • 工作流定义 + 节点保存(advance / reimburse 各一条)
  • 备用金:草稿 → 提交 → 多级审批 → 驳回/通过 → 发放
  • 报销:申请 + 明细 + 审批 + 支付/冲抵
  • 附件:upload → bind-biz → download/preview
  • 视图:我的 / 待办 / 全部(总经理)

10.3 门户集成

  • 租户隔离:不同 tenant 数据不可见
  • 门户 JWT 可访问 bx 接口
  • 菜单权限控制生效

11. 风险与对策

风险 对策
ID 类型 int→str,前端 bx 页仍用 number Schema 用 str;前端 P6 逐步改类型;JSON 字符串 ID 可兼容
API 加密与 bx GET 冲突 开发期 exclude;联调后再定是否前端加密
门户与 bx 响应双标准并存 bx 模块独立 common.py,禁止混用 success_response
payment 业务复杂 单独 P4,充分对照 bx service 单测/手测
审批人取自 workflow_node.approver_id 配置工作流时选 sys_user,与原 bx 选 bx_user 操作一致

12. 文档索引

文档 用途
00-migration-plan.md 本文:总实施方案
module-status.md 模块进度跟踪
01-api-inventory.md 全量 API 路径与方法
ref-backend-adapt.md 后端适配细则(认证/租户/Service 改造清单)
docs/sql/20260606_bx_tables.sql DDL 权威来源
docs/sql/sx.sql bx 原表业务字段对照
docs/crm-migration/ref-lessons-learned.md 前端/UI 踩坑(P6 参考)
docs/web-admin-ui-spec-fresh-mint.md Web UI 规范(P6)

13. 下一步行动

当前建议从 P0 开始:创建 bx_models.py、schemas/bx/common.py、core/bx_auth.py 并挂载空 bx_router。

每完成一个阶段,更新 module-status.md 并在 PR/提交说明中标注阶段编号(如 bx P1 expense-item)。