# 路线 03:从换货方案、客户确认和审批追踪到 OMS ## 学习场景 客户为已签收商品申请换货,确认当前方案,主管审核通过,履约工作进程向 OMS 提交操作。沿这个场景观察资格校验、版本检查、事务、消息投递、数量预占和外部结果恢复。 图中的后端路径均相对 `src/zhifu/`。连续箭头表示直接函数调用,标注“框架调用”的箭头表示 FastAPI 分发;间断箭头表示 HTTP、消息或持久数据连接。数据节点使用类名,函数节点使用源码中的函数名。同一调用者的多条连续箭头表示它分别调用相关函数,执行条件写在箭头或正文中。返回[路线目录](README.md)。 ## 图 1:创建方案、客户确认与主管审批 三个操作分别来自独立 HTTP 请求。`command` 建立事务并认证,`execute` 根据 `action` 检查权限、读取幂等收据并选择业务方法。 ```mermaid flowchart TB HTTP["FastAPI
三个独立 POST 请求"] API["modules/after_sales/api.py
create_proposal / confirm / decide
提供各自的 action"] CMD["modules/after_sales/api.py
command
建立业务事务"] AUTH["modules/identity/auth.py
authenticate
确认当前身份"] EXEC["modules/after_sales/service.py
AfterSalesService.execute
权限、幂等与业务分派"] CREATE["modules/after_sales/service.py
AfterSalesService.create
保存 DRAFT 方案及规则依据"] CONFIRM["modules/after_sales/service.py
AfterSalesService.confirm
保存客户确认与 PENDING 审批"] DECIDE["modules/after_sales/service.py
AfterSalesService.decide
校验审批人并记录决定"] VALIDATE["modules/after_sales/service.py
AfterSalesService.validate_proposal
读取方案引用的规则和订单版本"] EVALUATE["modules/after_sales/service.py
AfterSalesService.evaluate → assess
校验时效、签收、范围与数量"] CHANGED["modules/after_sales/service.py
AfterSalesService.changed
增加工单版本"] EMIT["modules/after_sales/service.py
AfterSalesService.emit
登记业务事件"] EVENT[("modules/after_sales/models.py
BusinessEvent
与业务变更共同提交")] RECEIPT[("modules/after_sales/models.py
CommandReceipt
保存请求摘要与响应")] HTTP -->|框架调用| API API --> CMD CMD --> AUTH CMD -->|认证成功后| EXEC EXEC -->|action=create| CREATE EXEC -->|action=confirm| CONFIRM EXEC -->|action=decide| DECIDE CREATE --> EVALUATE CONFIRM --> VALIDATE DECIDE --> VALIDATE VALIDATE --> EVALUATE CONFIRM --> CHANGED DECIDE --> CHANGED CREATE --> EMIT CHANGED --> EMIT EMIT -.->|事务内保存| EVENT EXEC -.->|读取重试收据或保存新响应| RECEIPT ``` `create` 使用当前规则建立 `ProposalVersion`,保存 `rule_version`、`order_version` 和 `citation`。`confirm`、`decide` 使用该方案引用的版本重新检查;订单来源版本变化会要求修订。图中 `evaluate → assess` 表示 `evaluate` 直接调用 `assess`。 `confirm` 只接受订单所属客户,检查方案版本与工单版本后进入 `WAITING_REVIEW`。`decide` 检查当前审批仍为 `PENDING`、客户确认存在,以及审批人和方案提交者不同;通过后进入 `APPROVED` 并产生 `after_sale.approved`。`execute` 的当前权限检查发生在读取并返回旧收据之前。 ### 图 1 源码导航 - [API:command 与事务入口](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/api.py:45)、[create_proposal](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/api.py:123)、[confirm](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/api.py:142)、[decide](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/api.py:173)。 - [execute:身份类型、店铺权限、接管权、幂等收据](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:199)。客服创建或修订方案时,需要当前客户会话的接管权及对应控制版本。 - [create](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:413)、[validate_proposal](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:453)、[confirm](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:470)、[decide](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:504)。 - [evaluate](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:136)、[assess](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:107):五分钟订单快照时效、签收时间、申请期限、商品类型和数量规则。 - [changed](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:448)、[emit](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:141)、[CommandReceipt 与 BusinessEvent](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/models.py:141)。 ## 图 2:审批事件触发履约准备 工作进程读取已经提交的 `BusinessEvent`。发布循环与消费循环独立运行;消息只传递事件标识等信息,`prepare_event` 根据 `event_id` 读取数据库中的业务事件。 ```mermaid flowchart TB EVENT[("modules/after_sales/models.py
BusinessEvent
已提交的审批事件")] PUB["modules/integrations/worker.py
publish_one
领取发布租约"] SEND["modules/integrations/worker.py
Broker.publish_event
等待 RabbitMQ 发布确认"] MQ[("RabbitMQ
zhifu.events / zhifu.fulfillment")] CONSUME["modules/integrations/worker.py
consume_one
取得消息并管理确认"] PRE["modules/integrations/fulfillment.py
prepare_event
读取事件、检查消费收据"] ORDER["modules/integrations/client.py
OmsClient.order
取得最新订单快照"] OMS["oms/api.py
create_app.get_order
返回凭据范围内订单"] PREPARE["modules/integrations/fulfillment.py
prepare
复查审批、规则和订单,预占数量"] OP[("modules/integrations/models.py
ExternalOperation / Reservation
PREPARED 操作与 HELD 数量")] RECEIPT[("modules/integrations/models.py
ConsumerReceipt
已处理的事件标识")] EVENT -.->|数据库查询 PENDING 事件| PUB PUB --> SEND SEND -.->|发布持久消息| MQ MQ -.->|queue.get 取得消息| CONSUME CONSUME --> PRE EVENT -.->|按 event_id 读取| PRE PRE -->|审批事件且工单仍为 APPROVED| ORDER ORDER -.->|"HTTP GET /orders/{order_id};FastAPI 分发"| OMS PRE -->|取得快照后建立事务| PREPARE PREPARE -.->|检查通过后共同保存| OP PRE -.->|与履约准备共同提交| RECEIPT RECEIPT -.->|重复事件直接结束| PRE ``` `prepare` 重新检查租户、店铺、审批员工状态及 `approvals:decide` 权限,同时核对方案版本、确认记录、规则有效性、连接器配置和 OMS 订单归属。业务条件变化时,工单进入 `NEEDS_REVISION`;可用数量扣除当前 `HELD` 数量后不足时,进入 `FAILED`。 准备成功时,`ExternalOperation`、`Reservation` 和工单 `PROCESSING` 状态共同提交。`consume_one` 在处理事务完成后确认消息。发布确认和消费收据分别处理发布端重发与消费端重复执行。 ### 图 2 源码导航 - [worker.main:建立发布、消费和执行循环](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/worker.py:117)。图中省略循环节点,三个循环由同一个工作进程启动并各自推进。 - [publish_one](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/worker.py:78)、[Broker.publish_event](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/worker.py:54)、[consume_one](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/worker.py:104)。 - [prepare_event](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/fulfillment.py:46)、[OmsClient.order](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/client.py:45)、[OMS get_order](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/oms/api.py:156)。外部订单查询在履约准备事务之外完成。 - [prepare](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/fulfillment.py:85)、[ExternalOperation](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/models.py:23)、[Reservation](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/models.py:53)、[ConsumerReceipt](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/models.py:71)。 ## 图 3:OMS 受理、完成与网络结果恢复 `execute_due` 从数据库领取到期操作,每次增加 `lease_token`。首次领取提交操作;后续领取先查询同一 `operation_id`,查询返回 404 才提交操作。仓库使用带有 `can_fulfill` 权限的凭据调用完成接口。 ```mermaid flowchart TB OP[("modules/integrations/models.py
ExternalOperation
PREPARED / UNKNOWN / ACCEPTED")] LOOP["modules/integrations/worker.py
main.executor
持续领取到期操作"] EXEC["modules/integrations/fulfillment.py
execute_due
取得租约并执行请求"] CLIENT["modules/integrations/client.py
OmsClient.process
按领取次数提交或查询"] SUBMIT["oms/api.py
create_app.submit
幂等受理并扣减数量"] QUERY["oms/api.py
create_app.query
查询当前操作结果"] EXCHANGE[("oms/models.py
Exchange
保存外部状态与版本")] WAREHOUSE["仓库请求
具有 can_fulfill 权限"] COMPLETE["oms/api.py
create_app.complete
推进为 COMPLETED"] RECORD["modules/integrations/fulfillment.py
record_result
核对租约与外部版本"] TICKET[("modules/after_sales/models.py
Ticket
保存工单状态")] VIEW["modules/after_sales/api.py
get_ticket
通过 ticket_view 返回当前进度"] LOOP --> EXEC OP -.->|数据库查询到期操作| EXEC EXEC --> CLIENT CLIENT -.->|HTTP PUT;首次或查询返回 404;FastAPI 分发| SUBMIT CLIENT -.->|HTTP GET;后续领取;FastAPI 分发| QUERY SUBMIT -.->|同一事务保存结果与订单数量| EXCHANGE EXCHANGE -.->|读取状态与版本| QUERY WAREHOUSE -.->|"HTTP POST /exchanges/{operation_id}/complete;FastAPI 分发"| COMPLETE COMPLETE -.->|事务提交 COMPLETED 与新版本| EXCHANGE EXEC -->|process 返回结果或 None 后| RECORD RECORD -.->|保存操作状态与下次执行时间| OP RECORD -.->|保存 PROCESSING / RESULT_UNKNOWN / COMPLETED / FAILED| TICKET TICKET -.->|后续 GET 请求读取| VIEW ``` `record_result` 仅接受当前有效租约提交的结果,并要求外部版本递增。`process` 遇到传输错误、HTTP 429 或服务端错误时返回 `None`;尚未明确受理的操作进入 `UNKNOWN`,对应工单进入 `RESULT_UNKNOWN`,数量预占继续保持 `HELD`。已经确认 `ACCEPTED` 的操作遇到未知结果时保留已确认状态,等待后续查询。 OMS 返回 `ACCEPTED` 后预占状态为 `CONSUMED`,工单继续保持 `PROCESSING`;返回 `COMPLETED` 后工单完成;明确返回 `FAILED` 时预占状态为 `RELEASED`。完成接口由外部履约方调用,平台工作进程通过后续查询取得完成结果。 ### 图 3 源码导航 - [main.executor](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/worker.py:142)、[execute_due](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/fulfillment.py:216)、[OmsClient.process](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/client.py:50)。 - [OMS submit](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/oms/api.py:181)、[query](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/oms/api.py:239)、[complete](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/oms/api.py:252)。 - [record_result](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/fulfillment.py:245)、[get_ticket](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/api.py:202)、[ticket_view](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:157)。 ## 关键数据与状态 - `Ticket.version` 控制工单并发修改;`Ticket.proposal_version` 标识当前方案。`ProposalVersion` 保存当时的规则、订单版本和规则依据。 - `Confirmation` 与 `Approval` 都关联具体方案版本。`revise` 增加方案版本,将待处理旧审批改为 `INVALIDATED`,工单回到 `DRAFT`,需要客户重新确认。 - `CommandReceipt` 使用租户、操作者、操作路径和 `Idempotency-Key` 组成作用域;相同标识更换参数返回 `IDEMPOTENCY_CONFLICT`。 - `BusinessEvent.aggregate_version` 用于确认事件仍对应当前工单。`ConsumerReceipt` 以消费者名称和事件标识记录处理结果。 - `ExternalOperation.id` 作为 OMS 的 `operation_id` 保持不变。操作的执行租约和外部结果版本分别控制当前工作进程提交权、外部状态推进。 - 主流程为 `DRAFT → WAITING_REVIEW → APPROVED → PROCESSING → COMPLETED`。审批可以进入 `REJECTED`;取消操作受当前状态和外部操作登记情况约束;履约准备可能进入 `NEEDS_REVISION` 或 `FAILED`。 ## 阅读检查问题 1. 客户确认与主管审批之间,订单来源版本变化时,哪个函数拒绝继续处理?方案修订后,哪些记录需要重新产生? 2. RabbitMQ 重复投递同一审批事件时,哪个事务保证只登记一次履约准备?发布租约与消费收据各自负责什么? 3. OMS 已受理但响应没有到达平台时,为什么数量继续保持预占?后续执行怎样复用 `operation_id`,并拒绝旧工作进程提交的结果? ## 相关测试阅读入口 本路线只阅读测试代码,未执行下列用例。 - [test_confirmation_approval_and_isolation](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_after_sales.py:298):客户确认、员工审批和数据访问范围。 - [test_concurrent_idempotency_and_conflict](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_after_sales.py:333)、[test_revision_invalidates_previous_approval](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_after_sales.py:369):并发重复提交和方案版本变化。 - [test_broker_delivery_and_fulfillment](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_fulfillment.py:138):消息重复投递、OMS 受理、仓库完成与工单查询。 - [test_lost_response_keeps_reservation_and_recovers](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_fulfillment.py:255):通过网络响应延迟观察结果未知与后续恢复。 - [test_revoked_approval_permission_prevents_external_operation](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_fulfillment.py:302):审批权限变化后,履约准备重新检查当前权限。