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

6.0 KiB
Raw Blame History

海外仓费用统一接入阶段 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 方案为准。