客户反馈同一订单收到两封“已发货”邮件。不要先关闭邮件:两封邮件可能对应两张合法的部分发货,也可能是同一 Shipment 被重复发送,甚至后台真的重复创建了发货单并扣减库存。

先对比邮件里的 Shipment 与 Tracking

记录订单号、Shipment increment_id、发送时间、物流单号和邮件 Message-ID。两个不同 Shipment 需要检查是否为部分发货;同一 Shipment 内容相同,再查发送调用与队列。

SELECT entity_id, increment_id, order_id, created_at, email_sent
FROM sales_shipment
WHERE order_id = 123
ORDER BY entity_id;

谁创建了 Shipment

管理员、ERP/WMS、支付/物流 Webhook 和 Cron 都可能调用 shipment service。外部系统超时重试时必须以物流事件 ID 或订单—发货批次为幂等键。只检查 canShip 仍有并发窗口,两个请求可同时看到可发数量。

同一实体被 send 两次

自定义 observer 在 shipment save 后发信,同时标准流程也勾选 Email Copy,便会重复。异步邮件模式下,生产者重复入队或消费者在发送成功、确认消息前崩溃,也会再次投递。邮件发送本身难以回滚,应记录 shipment_id 与模板动作唯一键。

不要仅依赖 email_sent

两个进程同时读取 email_sent=0,都可能发送后再置 1。需要原子状态转换或可靠 outbox。若 SMTP 接受邮件后客户端超时,Magento 不知道是否成功,盲目重试也可能重复;可结合 Message-ID 与邮件服务日志判断。

修复后模拟外部回调重复、消费者中途终止、部分发货与管理员重发邮件。预期是合法的每张 Shipment 最多自动发送一次,人工重发有明确记录,库存和 shipment 数量始终正确。

邮件副本与收件人配置

后台 Sales Emails 可以配置 Send Shipment Email Copy To,并选择 Bcc 或独立邮件。客户邮箱同时出现在订单地址和 copy 列表时,可能收到两封不同 Message-ID。先核对收件人头与配置,不要把合法 Bcc 流程误判为重复任务。

队列与 Outbox 对账

为发信动作建立唯一业务键,例如 shipment_id + template_type + recipient,记录 queued/sent/failed 与 Message-ID。SMTP 响应成功后再崩溃时,系统可通过该记录判断是否需要人工核对,而不是自动无限重发。

部分发货边界

订单包含多仓或分批发货时,每张 Shipment 可以发送一封;客户觉得重复可能是模板没有列出本次发货商品。邮件标题和内容应包含 Shipment 编号、当次商品和物流号,使合法的多封邮件可区分。

区分客户真的收到两封与后台显示两次

邮件服务商可能对一次提交做重试或转发,Magento 日志也可能因同一个传输异常写两条。以收件箱完整邮件头、Message-ID、服务商事件 ID 和 shipment_id 关联,才能定位重复发生在应用、SMTP 中继还是收件方规则。