标准答案

  1. 定义状态和允许迁移,更新时同时校验当前状态和版本。
  2. 保存支付方事件 ID、金额和原始状态,重复或过期回调不再次执行发货。
  3. 超时关闭失败时重新查询支付状态,未知结果进入对账而不是直接重复扣款。

题目解析

订单状态机的核心是定义合法状态和迁移,而不是把多个 if 堆在回调处理器里。每次迁移都要原子校验当前状态、版本、事件唯一性以及必要的订单和支付信息。

支付回调与超时关闭可能并发到达,谁先读到状态不重要,最终只有满足条件的一方能成功推进。另一方要查询最新状态或进入对账,不能把失败当作可以再次扣款的信号。

回调重复和乱序是正常外部系统行为。事件 ID、金额和商户信息校验可以防止伪造或重放,发货等副作用还要由唯一事件或状态记录保护。

代码示例

迁移函数应拒绝非法状态:

TypeScript
const transitions: Record<string, string[]> = {
  pending: ["paid", "cancelled", "expired"],
  paid: ["refunded"], cancelled: [], expired: []
}
if (!transitions[current].includes(next)) throw new Error("invalid transition")

常见误区

  • 误区:回调直接覆盖 order.status。改正:通过迁移表、当前状态和版本条件原子更新。
  • 误区:超时任务不校验当前状态。改正:只允许待支付状态进入关闭,已支付或处理中状态要走查询和对账。
  • 误区:状态迁移没有版本或唯一事件保护。改正:保存支付事件 ID、版本和审计记录,拒绝重复和过期回调。

作者信息