客户收到两封订单或发票邮件,不能仅凭收件时间判断 Magento 发了两次。邮件服务商重投、队列重复消费、后台手动发送和扩展监听同一事件,都可能产生相同现象。先从两封邮件的原始头开始。
两封邮件是否拥有相同 Message-ID
下载两封邮件原始内容,比较 Message-ID、Date、Received 链和 SMTP provider id:
- Message-ID 相同:更像邮件网关重投或收件端复制。
- Message-ID 不同、内容时间相近:应用层执行了两次发送。
- 一封标记为管理员副本:检查 Send Order Email Copy To 配置。
邮件原文包含客户信息,保存在受限工单,不要粘贴到公开日志。
按订单时间核对发送标志
SELECT entity_id, increment_id, state, status, email_sent, created_at, updated_at
FROM sales_order
WHERE increment_id='000001234';
SELECT entity_id, increment_id, order_id, email_sent, created_at
FROM sales_invoice
WHERE order_id=(SELECT entity_id FROM sales_order WHERE increment_id='000001234');
email_sent=1 只能说明最终状态,不能证明调用次数。继续检索应用日志、SMTP 日志和第三方邮件平台事件。
画出订单的发送时间线
grep -R '000001234' var/log | tail -n 100
journalctl -u crond --since '2025-03-25 09:50' --until '2025-03-25 10:10'
ps -ef | grep '[c]ron:run'
ps -ef | grep '[q]ueue:consumers:start'
如果两个 Cron 入口同时配置(系统 crontab 与托管平台任务),同一分钟可能并发处理邮件。执行 crontab -l 并检查 systemd timer、容器计划任务和平台计划任务,确保只有一套入口。
寻找自定义重复发送代码
grep -R --line-number --include='*.php' \
-E 'OrderSender|InvoiceSender|send\(' app/code 2>/dev/null
grep -R --line-number --include='events.xml' \
-E 'sales_order_place_after|checkout_onepage_controller_success_action|sales_order_invoice' app/code 2>/dev/null
核心结账流程已经负责订单邮件,自定义 Observer 再调用 OrderSender->send() 就会重复。支付回调也可能被网关重试,处理器必须以 transaction id 或订单状态做幂等判断。
SMTP 超时不代表发送失败
应用把邮件交给服务商后若响应超时,扩展可能重试并产生第二封。检查 provider 的接受事件:如果第一次已经 accepted,重试策略应识别相同业务消息。不要简单关闭重试,否则真实网络失败会丢邮件。
修复后用测试订单验证五个动作:首次下单、重复支付回调、后台 Resend、创建发票、Cron 重启。每个业务动作应产生预期数量的唯一 Message-ID,订单历史只记录一次自动发送;同时确认管理员副本仍按配置工作。

