$allow = ['pending' => ['paid','closed'], 'paid' => ['shipped','refunding']];
if (!in_array($to, $allow[$from] ?? [], true)) {
    throw new RuntimeException("非法流转 {$from} -> {$to}");
}

后台批量导入发货单,把一批还没付款的订单直接改成了 shipped,物流单号也写进去了。用户打开订单看到「已发货」,支付记录是空的。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。

原因不复杂,状态更新走的是通用 UPDATE 接口,只校验目标状态是否合法,没校验当前状态能不能跳过去。状态机只写在文档里,代码里没有落地。

这次卡住的地方就在「订单状态机跳转」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。

处理上按这个顺序:把允许的流转写成映射表,更新前查当前状态是否在允许列表里,不在就报错;状态更新统一走一个方法,禁止业务代码直接改 status 字段;批量操作前先跑预检查,把不允许流转的单号列出来,别整批提交。

改完只确认一件事:再导一次同样的发货单,待付款订单被拒绝,日志里能看到非法流转的订单号。

作者 9823189