php订单日志怎么记录拆分订单_php记录订单拆分操作日志方法【方法】

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

订单拆分时必须记录哪些关键字段

只记 order_id 和 “已拆分” 字样毫无意义。真正有用的日志要能回溯操作上下文:谁在什么时间、基于什么规则、把原订单拆成了哪几个新订单、各拆出多少商品、库存/金额如何分配。漏掉 operator_idsplit_rules(比如按商品类型或仓库拆),后续对账或排查异常时根本无法定位。

  • original_order_id:原始订单号,不可为空
  • split_order_ids:JSON 数组,如 ["SO20250501001", "SO20250501002"],不能只存一个 ID
  • split_items:明细数组,每项含 item_idquantitytarget_order_id
  • operator_idtrigger_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_idssplit_items 必须用 json_encode() 存字符串,不要试图存序列化对象——查起来慢,还容易因类变更反序列化失败。

MySQL 表结构设计要点(避免 varchar(255) 存所有)

用单字段存全部拆分信息看似省事,实际是埋雷。推荐三张表分工:

  • order_split_logs:主日志表,含 idoriginal_order_idcreated_atoperator_id 等基础字段
  • order_split_relations:中间表,字段为 log_idsplit_order_id,支持一对多查询
  • order_split_items:明细表,字段为 log_iditem_idquantitytarget_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 保证顺序和持久化。直接写文件只适合开发环境快速验证。

拆分日志最常被忽略的是「逆向操作痕迹」——比如合并订单后,原始拆分日志是否标记为失效?这类状态字段不加,时间一长,日志就变成干扰项。