# 报销后端移植适配细则 > 实施 P0–P4 时加载。与 [00-migration-plan.md](./00-migration-plan.md) 配套。 --- ## 1. 文件复制对照表 | bx-backend 源 | shanxia2.0 目标 | 改造程度 | |---------------|----------------|---------| | `schemas/common.py` | `schemas/bx/common.py` | 原样 | | `schemas/expense_item.py` 等 | `schemas/bx/*.py` | id 改 str | | `services/*.py` | `services/bx/*.py` | tenant + uuid + user | | `routers/*.py` | `api/bx/*.py` | Depends + prefix + import | | `models/*.py` | `models/bx_models.py` | 按 DDL 重写 | | `utils/codegen.py` | `utils/bx_codegen.py` 或 `utils/codegen.py` | 租户隔离可选 | | `core/deps.py` | **不复制** | 用 `core/bx_auth.py` | | `services/minio.py` | **不复制** | 门户 storage 适配 | --- ## 2. Router 改造检查清单 复制每个 router 后逐项核对: - [ ] `APIRouter(prefix="/api/xxx")` → `prefix="/xxx"`(去掉 `/api`) - [ ] `from app.database import get_db` → `from app.core.database import get_db` - [ ] `Depends(get_current_user)` → `Depends(get_bx_current_user)` - [ ] `User` 类型 → `BxUserContext` - [ ] `require_roles("ADMIN")` → `Depends(get_bx_current_user)` 或 `require_menu_code("bx:...")`(P5 后) - [ ] 路径参数 `{advance_id}: int` → `str` - [ ] `response_model=ApiResponse[...]` 保持不变 - [ ] **禁止** `return success_response(...)` --- ## 3. Service 改造检查清单 - [ ] 所有 `get_by_id(model, db, id: int)` → `id: str` - [ ] 查询追加 `Model.tenant_no == tenant_no` - [ ] 创建:`generate_uuid()`、`tenant_no`、`create_by`、`create_dept_id` - [ ] `applicant_id` / `approver_id` 使用 `sys_user.id`(str) - [ ] 冗余姓名字段:从 `SysUser.nick_name` 写入 `applicant_name` 等 - [ ] 单号生成:`generate_apply_code` 加租户维度(同日重置逻辑保留) - [ ] 软删除:`del_flag=1`,查询默认 `del_flag=0` --- ## 4. Schema 改造检查清单 - [ ] `AdvanceApplyOut.id: int` → `str` - [ ] `expense_item_id: Optional[int]` → `Optional[str]` - [ ] 保留 `model_config`:`populate_by_name=True`, `alias_generator=to_camel` - [ ] 保留 `DatetimeToStr` / `apply_time` 等字段 alias - [ ] Create/Update body 与 bx 原字段一致 --- ## 5. bx_auth 参考实现 ```python # backend/app/core/bx_auth.py(实施 P0 时落地) from dataclasses import dataclass from fastapi import Depends from app.core.menu_guard import require_admin_or_super_admin @dataclass class BxUserContext: id: str name: str dept_id: str def get_bx_current_user( user: dict = Depends(require_admin_or_super_admin), ) -> BxUserContext: return BxUserContext( id=user.get("user_id") or "", name=user.get("nick_name") or user.get("username") or "", dept_id=user.get("dept_id") or "", ) ``` Service 中需要 tenant 时: ```python from app.core.tenant_context import get_tenant_no tenant_no = get_tenant_no(db, {"user_id": user.id, "role": ...}) ``` --- ## 6. main.py 注册 ```python from app.api.bx import bx_router app.include_router(bx_router, prefix=settings.API_V1_PREFIX) ``` `api/bx/__init__.py`: ```python from fastapi import APIRouter from app.api.bx import expense_item, workflow, advance, reimburse, payment, attachment bx_router = APIRouter() bx_router.include_router(expense_item.router) bx_router.include_router(workflow.router) # ... ``` `app/api/__init__.py` 增加 `bx_router` 导出(可选,或直接 main 引用)。 --- ## 7. 加密排除(config.py) 在 `API_ENCRYPT_EXCLUDE_PATHS` 追加(逗号分隔): ``` /api/expense-items, /api/workflows, /api/advances, /api/reimburses, /api/payments, /api/attachments ``` --- ## 8. 字段一致性(可选) 后端 CRUD 落地后,若 WriteBody 字段较多,可扩展 `scripts/check_field_consistency.py` 支持 `bx_*` 表,规则同 CRM。 --- ## 9. 测试建议 | 阶段 | 方式 | |------|------| | P1 | Swagger `/docs` 手动测 expense-items / workflows | | P2 | 备用金完整流程 + 附件 upload | | P3–P4 | 对照 bx 原环境相同操作序列 | | 协议 | 断言响应 `code===0`、`data.rows` 存在 | 原 bx 环境(8011)可与门户(8000)并行,便于 diff 响应结构。