Medusa 订单处理全解析:一笔订单从付款到完成,背后到底走了几步?
你在 Medusa 里点下"付款"的那一刻,后端其实已经悄悄排好了一整条流水线:校验、建单、改单、发货、收尾,甚至为退货换货预留了岔路。这篇文章不讲大而空的架构理论,而是跟着一笔真实订单的旅程,把 Medusa 订单处理的每个环节拆开看,顺带告诉你源码都在哪儿、每一步为什么这么设计。读完你会对 Medusa 订单生命周期有一条清晰的主线,而不是散落的 API 名词。
先搞懂 6 种订单状态:Medusa 订单状态机像一张工单流转表
在 Medusa 里,订单本质上是一张"工单",状态就是它挂在工位上的标签。全部状态只有 6 种,定义在 packages/core/types/src/order/common.ts 的 OrderStatus 类型里:
| 状态 | 什么含义 |
|---|---|
pending | 单已建好,还没正式处理,等着后续动作 |
completed | 全程走完,货发了、账平了 |
draft | 草稿单,一般由后台人工发起,还没走支付 |
archived | 已归档,不再被日常流程触碰 |
canceled | 整单取消 |
requires_action | 卡住了,需要人工或下游系统补一手(比如支付方式要用户二次确认) |
把状态机想成工单流转就很直观:大部分单沿着 pending → completed 正常走,canceled 和 archived 是终点侧门,而 requires_action 是"被贴了待办、等人来处理"的特殊工位——这个状态后面会反复出现,值得你重点盯。
第一步:下单瞬间,createOrderWorkflow 替你干了三件事
订单诞生靠的是 createOrderWorkflow 这条 Medusa 订单工作流。它把三件脏活一次干完:
- 验货算钱:核对库存、按当前价格重算、把适用的促销叠加上去,保证落单那一刻的金额就是最终口径;
- 盖章编号:给订单分配全局唯一的订单 ID 和版本号。版本号不是摆设——后续每次变更都会往上加,方便你追踪一笔单被改过几次;
- 贴上
pending标签:新单一律从pending起步,表示"已创建、待处理"。
具体执行者是 OrderService,源码在 packages/modules/order/src/services/order-service.ts。它负责订单的创建、检索、更新这些基础动作,工作流只是把它的原子操作编排成有顺序、可回滚的流水线。这里的设计思路值得记住:业务规则写在工作流里,数据操作收敛在 Service 里,两层各司其职。
第二步:买家改主意了,用 createOrderChangeWorkflow 接住变更
付完款才想起来要换地址?多买一件?Medusa 不让你直接改订单记录,而是走 createOrderChangeWorkflow:先发起一个"变更请求",走自己的审批小闭环,确认后才真正落到订单上。
这套机制有自己的状态集合 OrderChangeStatus,同样是 5 个值:requested(刚发起)、pending(处理中)、confirmed(已确认生效)、declined(被驳回)、canceled(变更本身被取消)。换句话说,"改订单"也是一张迷你工单,有自己的流转和终点。这样做的好处很实际:变更全程留痕、可审计,出了问题能定位到是哪一次请求改的。
第三步:发货时,fulfilled_quantity 和 shipped_quantity 分别在数什么
货要出仓了,登场的是 createOrderFulfillmentWorkflow。它负责库存扣减、包装、发货这一串动作,并在订单行上更新两个容易混淆的字段(定义在 packages/core/types/src/order/common.ts 的 OrderLineItemDTO 里):
fulfilled_quantity:已确认履约的数量,货在流程里被"认领"了;shipped_quantity:真正发出去的数量。
为什么要分两个数?因为"决定给你发"和"快递已揽收"之间有时间差,部分发货时两者还会不相等。理解这两个字段,你就明白了 Medusa 是怎么支撑"一单拆多个包裹发"这种场景的。
第四步:收尾对账,completeOrderWorkflow 把账算平
当所有商品都发完并确认,completeOrderWorkflow 接手最后的收尾:做最终库存更新、落财务相关记录,然后把订单状态拨到 completed。这一步是整条主线的终点——之前每个环节做的准备,都是为了在这里能干净利落地关单。
分支剧情:退货与换货各走各的步骤
主线之外还有两条岔路,它们的步骤名都带"从动作生成"的意思,逻辑一致:先有一个动作描述(要退什么、怎么退),再据此生成正式记录。
- 退货:
createOrderClaimsStep先建索赔(claim),createOrderReturnItemsFromActionsStep再把具体退货行项落下来。退货自身也有一套状态:requested(已申请)、received(货已收回)、partially_received(部分收回)、canceled(取消)。 - 换货:
createOrderExchangesStep建换货单,createOrderExchangeItemsFromActionsStep生成换货行项,流程和退货同构,只是方向反过来——旧货收回、新货发出。
这些步骤都住在 packages/core/core-flows/src/order/ 下,想查细节直接翻这个目录就行。
数据模型三件套:看懂订单结构的钥匙
回头看一眼数据结构,核心就三个接口,全在 packages/core/types/src/order/common.ts:
OrderDTO:订单主体,含 ID、状态、金额汇总和各类关联关系;OrderLineItemDTO:行项目,也就是"买了几件什么",上面提到的fulfilled_quantity就挂在这里;OrderShippingMethodDTO:配送方式,运费和配送渠道的载体。
再加上 OrderService 和那几条工作流,"组件—数据—流程"三层就齐了。平时调试订单问题,从这三个 DTO 的字段定义出发,基本都能找到答案。
上手路径与避坑要点:跑通 Medusa 订单生命周期之后要盯什么
代码读到这里,上手时真正容易踩坑的是运维侧的几件事:
- 盯住
requires_action:这是唯一"等别人"的状态。接入支付、库存、物流任一环节时,都建议对这类订单做告警或定时巡检,别让单悄悄卡在工位上; - 库存数据先行:
pending阶段的校验全靠库存账准。库存数据一漂移,轻则履约延迟,重则超卖,上游数据质量比下游修复重要得多; - 能交给工作流的就别手写:订单确认、发货通知这类重复动作,用 Medusa 订单工作流编排起来,天然带回滚和留痕,比在业务代码里散着写状态判断稳得多;
- 异常路径要提前设计:支付失败、库存不足、部分收货,每一种都对应一个状态分支。上线前把异常路径过一遍,比上线后救火便宜;
- 保持订单数据完整:客户、商品、支付信息齐不齐,直接决定你能不能做复盘和数据分析。落单时字段缺失,后面再补也补不回"当时真实的样子"。
想动手练手,把仓库拉下来跑一遍订单集成测试是最快的路径:
git clone https://gitcode.com/GitHub_Trending/me/medusa
总结一下这次"跟单":一笔订单从 createOrderWorkflow 落地为 pending,经变更、履约、完成逐步流转,退货换货从岔路走独立步骤,最终归档。状态是骨架,工作流是肌肉,OrderService 和数据模型是骨头——三层看懂了,Medusa 的订单系统对你就不再是黑盒。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/gitblog_00019/article/details/154328494





