订单拆分日志必须记录original_order_id、split_order_ids、split_items、operator_id、trigger_source等关键字段,并通过Laravel事件机制写入数据库三张表,避免JSON存储与文件并发写丢失。

订单拆分时必须记录哪些关键字段
只记 order_id 和 “已拆分” 字样毫无意义。真正有用的日志要能回溯操作上下文:谁在什么时间、基于什么规则、把原订单拆成了哪几个新订单、各拆出多少商品、库存/金额如何分配。漏掉 operator_id 或 split_rules(比如按商品类型或仓库拆),后续对账或排查异常时根本无法定位。
-
original_order_id:原始订单号,不可为空 -
split_order_ids:JSON 数组,如["SO20240501001", "SO20240501002"],不能只存一个 ID -
split_items:明细数组,每项含item_id、quantity、target_order_id -
operator_id和trigger_source(如"admin_api"或"auto_rule_stockout")必须明确
用 Laravel Eloquent 监听 Order::split() 事件写日志
别在控制器里硬塞 Log::info()。Laravel 的模型事件机制更可靠——只要 Order 模型定义了 split() 方法,就在其内部触发自定义事件,再由监听器统一写库。这样即使以后加了队列异步拆分,日志也不会丢。
示例:在 Order 模型的 split() 方法末尾加
event(new OrderSplitEvent($this, $newOrders, $splitDetails));
监听器中写入日志表(非文件):
立即学习“PHP免费学习笔记(深入)”;
$log = new OrderSplitLog(); $log->original_order_id = $event->originalOrder->id; $log->split_order_ids = json_encode(array_column($event->newOrders, 'id')); $log->split_items = json_encode($event->splitDetails); $log->operator_id = $event->operatorId ?? 0; $log->save();
注意:split_order_ids 和 split_items 必须用 json_encode() 存字符串,不要试图存序列化对象——查起来慢,还容易因类变更反序列化失败。
MySQL 表结构设计要点(避免 varchar(255) 存所有)
用单字段存全部拆分信息看似省事,实际是埋雷。推荐三张表分工:
-
order_split_logs:主日志表,含id、original_order_id、created_at、operator_id等基础字段 -
order_split_relations:中间表,字段为log_id、split_order_id,支持一对多查询 -
order_split_items:明细表,字段为log_id、item_id、quantity、target_order_id
这样查“某次拆分涉及哪些商品”直接 JOIN 即可,不用 JSON_CONTAINS 解析,也不怕字段超长截断。
PHP 原生写日志时防止并发写丢失
如果没上队列或消息中间件,多个请求同时拆同一个大订单,用 file_put_contents($logFile, $msg, FILE_APPEND) 会丢日志——PHP 文件追加不是原子操作。必须加锁:
$fp = fopen($logFile, 'a');
if (flock($fp, LOCK_EX)) {
fwrite($fp, date('Y-m-d H:i:s') . " - " . json_encode($data) . "\n");
flock($fp, LOCK_UN);
}
fclose($fp);但更稳妥的做法是:哪怕不用数据库,也改用 syslog() 或 error_log() 走系统日志服务,由 rsyslog 保证顺序和持久化。直接写文件只适合开发环境快速验证。
拆分日志最常被忽略的是「逆向操作痕迹」——比如合并订单后,原始拆分日志是否标记为失效?这类状态字段不加,时间一长,日志就变成干扰项。











