Files
baiyue-firstleg/docs/Order-Firstleg在线依赖解环设计.md
T
2026-08-25 19:07:34 +08:00

7.1 KiB
Raw Permalink Blame History

Order↔Firstleg 在线依赖解环设计

结论

当前不能在不改变已确认产品成功时点的前提下直接实施解环,业务代码保持不变。在线图仍包含 Order→Firstleg 与 Firstleg→Order,所以非平凡强连通分量仍是 Firstleg↔Order。

建议的最小退出方向是 退出全部 Firstleg→Order REQUEST 边。该方向只有 OrderCargoClient 和 OrderFulfillmentSplitClient 两个客户端;Order 继续拥有订单、货物、父子订单和 拆分台账,Firstleg 继续拥有入库、测量、分柜装柜、PDA 拣货和库位结算。完成后只保留 Order→Firstleg 在线查询/门禁边以及双方后台边,在线 SCC 才会实际消失。

当前可执行调用

方向/客户端 Controller→Service→Client→被调用入口 时点与调用者期望 失败传播 幂等性
Order→Firstleg FirstlegPricingClient MerchantFirstLegOrderController、PlatformFirstLegOrderController 的创建/编辑/提交 → OrderApplicationService → OrderPricingChannelGuard → /internal/firstleg/v1/pricing/channels/validate 保存或提交前实时确认渠道有效;提交还必须取得入仓码前缀 业务错误原码上传;连接、空响应或不完整数据映射依赖不可用/冲突,订单不提交 纯读取 SAFE_READ
Order→Firstleg FirstlegUnifiedOrderClient PlatformUnifiedOrderController、MerchantUnifiedOrderController 列表/详情 → UnifiedOrderQueryService → Firstleg unified query/summaries/detail 一个响应内组合 Order 主数据与 Firstleg 实时履约、测量、装柜和时间线 远端异常、失败码、空数据均失败关闭,不返回伪造履约摘要 纯读取 SAFE_READ
Order→Firstleg FirstlegInboundClient MerchantFirstLegOrderController.page → MerchantInboundOrderListService → references/prepareBatch;已提交订单编辑提交后 → OrderApplicationService.prepareDraftInbound → prepare;审核 Outbox → OrderInboundOutboxPublisher → create 列表需要入库引用;草稿/待审编辑尝试同步预入库;审核成功已与 Firstleg 建单解耦 references 失败关闭;prepare/prepareBatch 当前捕获异常并允许列表缺少入库引用,是现有失败传播缺口;审核 Outbox 持久化重试 tenantId+orderId 业务键;Firstleg 对订单唯一且入仓码一致时幂等
Firstleg→Order OrderCargoClient 箱号 Controller/Order 审核后台建单 → InboundCartonLabelService;测量 Controller/OpenAPI → InboundMeasurementService/InboundMeasurementOpenApiService;智能装柜 Controller → SmartLoadingService → Order cargo/snapshot endpoints 首次箱号、测量明细、申报回填和装柜清单均要求当前订单货物事实 服务不可用、空数据、归属不一致均阻断当前操作;没有默认货物或空列表成功 纯读取 SAFE_READ;同时存在 REQUEST 和审核 Outbox 回调产生的 BACKGROUND 上下文
Firstleg→Order OrderFulfillmentSplitClient PlatformLoadingPdaController.complete → LoadingPickService.complete @Transactional → PdaPickSettlementService.settle → /internal/order/v1/fulfillment-splits → OrderFulfillmentSplitService.split @Transactional PDA 完成请求必须先取得 Order 真实余量子单 ID/单号,再在同一 Firstleg 事务写分片、测量、库位、审计并推进装柜完成 Order 不可用或响应数量/柜次/父子关系不一致时整个 Firstleg 完成事务回滚;不得返回完成成功 Firstleg 使用 idempotencyKey=pda-task-{taskId}、operationId=PDA-SPLIT-{taskId}-{inboundId};Order 保存请求哈希并以唯一键重放

事实所有权

  • Order:订单交易状态、订单货物及允许编辑的字段、父子订单 ID/单号、履约拆分台账和订单版本。
  • Firstleg:入库单、测量事实、分柜/装柜计划、PDA 拣货数量、库位结算、装柜证据和 LOADING→LOADED 门禁。
  • 允许复制的仅是带来源版本和哈希的 Order 快照;Firstleg 不得成为订单货物或父子订单的第二写入方。
  • 统一订单查询是组合读模型,不把 Firstleg 履约状态写回 Order 交易状态。

推荐退出方案

1. 先退出货物在线读取

复用 Order 已有 Outbox 和 Firstleg 已有订单入库接收入口,将完整货物快照、订单版本和规范化哈希随草稿准备、待审编辑和审核事件可靠投递;Firstleg 以 tenantId+orderId+orderVersion 单调 Inbox/快照落库。箱号、测量和智能装柜只读本地快照,缺失、版本冲突或哈希冲突均失败关闭。

不能只在审核时复制一次:当前契约允许 LOADED 前按权限和状态修改业务资料。必须先冻结货物在哪个节点停止修改,或要求每次允许的 Order 编辑都可靠发布版本事件,并明确 Firstleg 在投影落后期间阻断哪些操作。

2. 再将履约拆分改为可靠后台命令

Firstleg 在本地持久化稳定 operation/idempotency key、数量行、柜次和请求哈希;后台 Relay 调用现有 Order 幂等拆分接口并保存完整响应。Order 现有拆分台账和唯一键可承担 Inbox/业务幂等,不需要新服务或 MQ。

Firstleg 只有在后台结果为 READY 且父子关系、数量和柜次全部校验通过后,才能消费余量子订单 ID并执行本地结算。失败进入可诊断重试/DEAD,禁止默认成功。远端成功、本地未完成时只允许向前恢复,不得自动删除 Order 子订单。

这一方案会引入技术态 PENDING/READY/FAILED,但不能擅自增加产品主状态。若在 PDA 首次点击完成时只创建任务并返回待处理,成功时点发生变化;若提前创建 Order 子单,则子单可能在装柜硬门禁完成前可见,同样改变现有语义。

必须由产品冻结的决策

  1. 货物冻结点:Order 货物在审核通过、首次生成箱号、确认测量还是确认预装后不可再改;若仍允许修改,哪些 Firstleg 操作必须等待最新 orderVersion 投影。
  2. PDA 完成成功时点:必须继续在一次请求中同时完成 Order 子单与 Firstleg 结算,还是允许首次请求返回“处理中”,后台完成后才成为 LOADED。同时确认后台已创建 Order 子单但 Firstleg 最终失败时采用持续向前修复,不提供在线冲正。

上述任一决策未冻结前,不实施业务解环。

最小实施与验证边界(决策冻结后)

  • Order:扩充版本化货物快照 Outbox;保留 OrderFulfillmentSplitService 事实所有权和现有幂等唯一键。
  • Firstleg:货物快照 Inbox/表、拆分命令 Outbox/Relay/诊断查询;替换两个在线 Client 的业务调用点。
  • SQL:仅更新当前模块基线与统一增量,不引入 Flyway/Liquibase,不执行 SQL。
  • 合同:证明生产请求路径不再引用两个客户端、Firstleg→Order 只剩 BACKGROUND,REQUEST SCC 中不再出现两者同一分量。
  • 测试:Order/Firstleg 全量直接测试、重复/异哈希事件、租约恢复、远端成功本地失败重放、DEAD 人工重试、PDA 未 READY 失败关闭,以及同步依赖合同。