先说结论
群发最怕的不是“没发出去”,而是“该排除的没排除、不该重复的重复了、失败后还不知道怎么补救”。Odoo 的发送引擎核心,其实都在这些看不见的地方。
第一层:opt-out list 为什么优先于发送动作
_get_opt_out_list() 先把目标模型自己的退订逻辑收上来,再交给后续发送流程使用。这说明群发系统并不是“先发,失败了再说”,而是先定义不该发给谁。真正成熟的系统,第一步不是扩大发送量,而是收紧错误触达。
第二层:seen list 在防什么
_get_seen_list() 会去查已经被当前 mailing 或 campaign 触达过的邮箱,从而避免重复发送。尤其在 A/B、分批和重试场景里,这层非常关键。否则你明明是在“补发”,结果用户收到两三封一样的内容。
第三层:剩余收件人为什么还要再算一次
_get_remaining_recipients() 会基于候选收件人,再减去已有 trace 的 res_id。也就是说,Odoo 不信任“理论名单”,而是更相信“真正已经留痕的发送事实”。这是一种很稳的设计:以 trace 为准,而不是只看列表。
第四层:为什么发送要走队列
_process_mass_mailing_queue() 会扫 in_queue / sending 状态的 mailing,再逐个推进。这意味着群发不是一个前台同步动作,而是后台批处理流程。对于大名单或多批次发送,这层队列化是系统稳定性的前提。
第五层:失败重试为什么先删旧失败邮件和 trace
action_retry_failed() 并不是简单再点一次发送,而是先把 exception 状态的 mail.mail 及其 trace 清掉,再重新排队。这样做可以避免旧失败记录和新尝试交织,导致统计和状态混乱。
最容易误解的三个点
- 误区一:群发系统的核心是编辑器。真正难的是名单过滤、去重和恢复。
- 误区二:失败重试就是再发一遍。没有先清失败痕迹,统计很容易乱。
- 误区三:只要名单没重复就不会重复触达。A/B 和多批次下还需要 seen list 兜底。
实战上怎么用更稳
- 查“为什么有人收了两次”时,优先看 seen list 与 trace,而不是先看联系人表。
- 查“为什么没发出去”时,分清是被 opt-out 排除了,还是进入 exception 失败。
- 做大批量邮件时,别绕开队列直接同步发,短期省事长期会翻车。
最后总结
Mass Mailing 能不能稳定,不取决于按钮叫不叫“发送”,而取决于它发前排除了谁、发中怎么推进、发后如何修复。Odoo 把这三步都做得很清楚。
DISCUSSION
评论区