标准答案
- 定义明确的状态和允许迁移,例如待支付只能进入已支付、已取消或超时关闭等合法状态。
- 回调按支付方事件 ID 去重,并校验订单、金额、商户和事件版本,不能只看订单号。
- 超时任务与支付回调竞争时由数据库条件更新或状态版本裁决,只有一个动作能成功推进状态。
- 无法确定的支付结果进入查询和对账,不应因为网络超时直接重复扣款或关闭订单。
题目解析
订单状态机的核心是定义合法状态和迁移,而不是把多个 if 堆在回调处理器里。每一次迁移都要原子地校验当前状态、事件唯一性、版本和必要的金额或主体信息。
支付回调与超时关闭可能同时到达,谁先读到状态不重要,最终只有满足条件的一方能成功推进。另一方应查询最新状态,不能把失败当作可以再次扣款的信号。
外部支付结果未知时要进入查询和对账,而不是直接关闭订单或重复发起支付。发货、优惠券和消息也应由唯一事件或状态记录保护,避免回调重放制造副作用。
常见误区
- 误区:先 if 读取状态,再普通 UPDATE。改正:用带当前状态和版本条件的原子更新,并检查影响行数。
- 误区:重复支付回调再次发货。改正:保存支付方事件 ID,只有合法且未处理的迁移才允许产生发货副作用。
- 误区:超时关闭不检查支付最终状态。改正:对未知结果先查询或进入对账,不能用客户端超时推断支付未发生。