This commit is contained in:
2026-09-21 20:39:30 +08:00
parent 1fffb3489e
commit b22d1ed0c0
2 changed files with 100 additions and 0 deletions
+1
View File
@@ -63,6 +63,7 @@
详细设计材料:
- [海外仓费用统一接入财务评审方案](海外仓费用统一接入财务评审方案.md)
- [海外仓费用统一接入阶段 0 技术契约](海外仓费用统一接入阶段0技术契约.md)
- [财务业务操作流程图](财务全链路流程图.md)
- [财务业务全流程总览图(PNG)](财务业务全流程总览图.png)
- [财务来源接入现状](财务来源接入现状.md)
@@ -0,0 +1,99 @@
# 海外仓费用统一接入阶段 0 技术契约
> 版本:V0.5-0A | 状态:开发前技术基线 | 来源:海外仓费用统一接入财务评审方案 V0.5
## 1. 目标与边界
阶段 0 只完成“费用事实可追踪、可幂等、可被 Finance 接受或拒绝”的底座,不新增业务收费规则,不部署测试环境,不启动 Java/Nacos/数据库。所有费用仍由业务服务产生事实,Finance 独立落库并负责应收/应付、钱包和结算。
实施顺序固定为:
1. 0A:既有链路和收费身份;
2. 0B:统一来源命令、结果和 Outbox/Inbox;
3. 0C:冻结/结算按收费身份和计划版本;
4. 0D:退货、仓租、按票及库存日事实;
5. 0E:商业调整、拒绝和财务待办。
## 2. 当前代码事实与必须修正点
| 范围 | 当前实现 | V0.5 要求 |
|---|---|---|
| 来源事件 | `BusinessFeeSourceChangedV1` 单向完整快照 | 增加命令/结果闭环,Finance 明确 ACCEPTED/REJECTED |
| 费用版本 | Lastleg 调整、作废先改变当前状态再发事件 | Finance 接受新版本后,旧版本才可 SUPERSEDED/VOIDED |
| Wallet | 以 `businessType + businessNo + amount` 匹配冻结 | 绑定 `chargeIdentity + feeSourceVersion + walletPlanId` |
| Relabel | 部分完成路径发布费用,SKU/客户确认路径漏发 | 所有完成路径统一产出一条收费身份 |
| Destuff | 直接 insert 已审批客户应收,绕过服务和命令 | 进入统一来源命令;供应商应付按阶段配置 |
| Delivery | 转运时直接发布已审批费用 | 票级 AR/AP 分离,人工金额先待审 |
| 来源模型 | 无 `scenarioCode`、收费身份、Finance 同步状态 | 三者均为显式字段,不从业务单号推导 |
## 3. 收费身份与状态
### 3.1 收费身份
`chargeIdentity` 是稳定的业务收费对象键,不等同于数据库 ID。格式由场景定义,至少包含 `scenarioCode`、业务主单号、客户和收费对象序号;同一身份可产生多个不可变 `sourceVersion`。Finance 唯一键为:
`sourceSystem + sourceType + sourceFeeId + sourceVersion`
费用明细必须同时保存 `chargeIdentity`,用于 AR/AP、钱包、账单和追溯关联。
### 3.2 状态轴
- 来源状态:`DRAFT`、`PENDING_REVIEW`、`APPROVED`、`REJECTED`、`SUPERSEDED`、`VOIDED`。
- 发布状态:`NOT_PUBLISHED`、`PUBLISHED`、`ACCEPTED`、`REJECTED`。
- Finance 同步状态:`PENDING`、`ACCEPTED`、`REJECTED`、`RETRYING`。
- 结算状态:`UNSETTLED`、`FROZEN`、`SETTLED`、`RELEASED`。
状态轴不可互相覆盖。费用调整必须生成新版本;同一版本不同 hash 必须拒绝。已进入有效账单、结算或钱包结算的来源不得替换或作废。
## 4. 跨服务命令与结果
### 4.1 `BusinessFeeSourceCommandV2`
事件头:`eventId`、`eventType`、`eventVersion=2`、`eventKey`、`occurredAt`、`sourceSystem`、`traceId`。
业务体:`action`(UPSERT/VOID)、`sourceType`、`sourceFeeId`、`sourceVersion`、`chargeIdentity`、`scenarioCode`、客户/业务/仓库/供应商引用、方向、币种金额、数量单价、期间、付款证据、来源状态、`sourceHash`。
发送方必须发送完整快照,不允许 Finance 回查海外库。`eventKey` 固定为 `sourceSystem:sourceFeeId:sourceVersion:action`。
### 4.2 `BusinessFeeSourceResultV2`
Finance 处理后回传:`commandEventId`、`sourceFeeId`、`sourceVersion`、`result`(ACCEPTED/REJECTED)、`reasonCode`、`reasonMessage`、`financeFeeSourceId`、`processedAt`、`resultHash`。
业务方以 `commandEventId` 幂等接收结果;REJECTED 必须形成可重试或人工待办,不得静默丢弃。结果事件不能改变原始费用快照。
## 5. 钱包与账单关联
钱包命令新增 `chargeIdentity`、`sourceFeeId`、`sourceVersion`、`walletPlanId`、`walletPlanVersion`、`allocationId`。冻结、结算、释放均以 `allocationId` 幂等;禁止仅按业务类型、业务单号和金额猜测关联。
柜级费用先按真实客户/订单形成分摊明细,再分别产生收费身份和钱包分配;退货按票、仓租按期间/仓库、配送按票分别生成身份,不复用订单主单号充当费用键。
## 6. 场景接入约束
- **Relabel**:所有完成入口调用同一应用服务;一次业务收费身份只允许一个当前版本。
- **Destuff**:按实际客户 CBM 分摊;客户应收与供应商应付分开建身份,禁止直接写 APPROVED/PUBLISHED 绕过命令。
- **Delivery**:每票每收费项独立身份;收款方与付款方分离,手工金额进入待审。
- **Return**:按票计费,退货收货完成才结束计费;迟到实收只能生成新版本/调整记录。
- **Warehouse rent**:按仓库、库位/货主、计费期间和计费规则生成期间身份,不能伪造入仓码。
## 7. 阶段 0 开发门禁
开始改代码前必须完成并评审:
1. V2 事件 DTO、事件类型、Gateway 路由、OpenAPI、权限码和跨区 Outbox/Inbox 位置;
2. Lastleg 来源表与 Finance 来源表的字段/索引/状态迁移设计;
3. 钱包计划及分摊表的唯一键与幂等键;
4. Relabel、Destuff、Delivery 的生产入口清单和漏发测试用例;
5. 拒绝、重试、迟到实收、旧版本已结算等异常验收用例。
实现后必须定向运行 DTO/事件/服务测试、`node scripts/gateway-route-contract.test.mjs`,若有 Mapper 变更再运行 `node scripts/mapper-scan-contract.mjs`。本阶段不执行部署和真实 HTTP 验收。
## 8. 代码入口
- 事件模型:`baiyue-firstleg/common-event-contract/`
- 海外来源服务:`baiyue-firstleg/lastleg-service/.../financefee/`
- Finance 消费:`baiyue-firstleg/finance/.../BusinessFeeSourceEventService.java`
- Relabel:`baiyue-firstleg/lastleg-service/.../relabel/`
- Delivery:`baiyue-firstleg/lastleg-service/.../delivery/`
- Destuff:`baiyue-firstleg/lastleg-service/.../inboundplan/`
本文是 V0.5 的技术落地门禁;与 V0.5 方案冲突时,以当前对话确认和 V0.5 方案为准。