背景
最近在重构一个老旧的 Shopify 集成模块,核心是一条 600 多行的 PHP 脚本 sync-orders.php。看名字就知道它只做一件事——同步订单。但当你读进去之后会发现,它其实做了三件完全不同的事:
- 从 Shopify API 拉取订单数据
- 解析并存入本地数据库
- 在 WMS 系统中创建出库单、分配库存批次
这种”顺手把后面的事也做了”的模式在遗留系统中很常见。在初期它确实是高效的——一个脚本搞定所有,部署简单,理解直观。但随着需求增加,这种耦合开始暴露问题。
问题的表现
数据不一致
最直接的触发点是:订单同步是定时任务,而 WMS 系统的出库单表可能因为各种原因不可用。如果同步过程中出库单创建失败,数据就陷入了不一致状态:
订单已存入 (shopify_orders) ✅
出库单创建失败 (shipping) ❌
库存批次未分配 (lineitem) ❌
重试整个同步又会导致订单数据重复插入。修复需要手动清理数据再从头跑。
故障半径过大
当仓库同事在操作 WMS 时(比如月底盘点锁表),订单同步定时任务恰好触发,结果整个同步失败——连订单数据都没拉下来。本来订单数据是可以先落地的,出库单可以等锁释放后再处理,但作为一个单体脚本,要么全做要么全不做。
测试困难
一条脚本里混杂了 API 调用和数据库写入,很难单独测试某个环节。想测试”从 Shopify 拉数据”的逻辑,必须先确保 WMS 的表结构和库存数据完整。反过来,想测试”出库单创建”的逻辑,又必须先跑一遍完整的订单同步。
解耦方案
核心思路:从单体脚本变为管道式架构,两个阶段各自独立运行。
第一阶段:订单获取 (sync-orders)
只负责:
- 调用 Shopify GraphQL API 获取订单
- 写入 shopify_orders + shopify_lineitem
- 标记 order_status = pending
不再关心 WMS 出库单、库存分配这些事情。
第二阶段:出库单创建 (create-shipments)
只负责:
- 读取 shopify_orders 中 status = pending 且尚未创建出库单的记录
- 创建 WMS 出库单 + 分配库存批次
- 标记 order_status = shipment_created
不关心 Shopify API、GraphQL、网络延迟这些事。
修改前(单体):
sync-orders.php
├─ Shopify API
├─ shopify_orders 写入
├─ shipping 写入
├─ lineitem 写入
├─ inventry 批次分配
└─ 全部或全不
修改后(管道):
sync-orders.php --orders-only
├─ Shopify API
├─ shopify_orders 写入
└─ shopify_lineitem 写入
create-shipments.php
├─ shipping 写入
├─ lineitem 写入
├─ inventry 批次分配
└─ order_status → shipment_created
兼容性
旧的一步到位模式通过 sync-orders.php(不带 --orders-only)保留,仍可继续使用。新用户可以选择两步模式,也可以切换到一步到位的旧模式。script.php(web 执行端点)中注册了新脚本,可以通过 UI 按钮触发。
一个意外的收获
在解耦过程中,需要仔细检查脚本中每条 SQL 语句的上下文。这一查才发现,这个脚本一直在往 shipping 表写入硬编码的 owner 值。而在系统的出库单列表中,用户按 owner 过滤数据,导致不同公司的用户看不到自己对应的 Shopify 订单。
更深入一看,三条 P0 安全漏洞同时浮现:
| 漏洞 | 发现方式 |
|---|---|
| SSL 证书验证全局关闭 | 审阅 ShopifyClient 时发现 CURLOPT_SSL_VERIFYPEER = false |
| OAuth CSRF state 未校验 | 审阅 callback 时发现 session 存了 token 但从未比较 |
| 开放重定向 | 审阅 auth.php 时发现 return_url 没有任何校验 |
这些漏洞和本次解耦没有直接的因果关系,但它们在同一段代码路径上埋了这么久而未被发现,说明对老代码的”触碰”本身就有安全价值——每次重构都是一次免费的代码审计。
最终效果
解耦后的数据流:
客户在 Shopify 下单
│
▼ sync-orders --orders-only
Shopify 订单数据已落地(不创建出库单)
│
▼ create-shipments(可在不同时间触发)
WMS 出库单 + 库存分配完成
│
▼ 仓库拣货发货
测试验证覆盖:
01-sync-orders.sh ✅ orders=3 lineitems=6 shipments=0
02-create-shipments.sh ✅ shipments=3 source=shopify owner=correct_company
03-single-order.sh ✅ order_status preserved
每个阶段可以独立测试、独立重试、独立监控。
总结
解耦订单同步与出库单创建带来的直接好处:
- 订单获取不再依赖 WMS 表可用性,数据先落地再异步处理
- 出库单创建可以安全重试,不会导致订单重复
- 两段逻辑各自的责任边界清晰,测试成本降低
- 触达老代码的过程中发现了被长期掩盖的安全漏洞
遗留系统的重构很多时候不是引入新功能,而是让已有的功能变得可观测、可控制、可独立失败。这次解耦就是一个小而完整的案例。