Files
docs/财务管理/海外仓费用统一接入阶段0技术契约.md
T
2026-09-21 20:39:30 +08:00

100 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 海外仓费用统一接入阶段 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 方案为准。