网站订阅

Odoo 官网订阅为什么不是“填个邮箱就结束”:名单公开性、重复识别与机器人防护主链路讲透

很多人把 Odoo 官网 Newsletter 订阅理解成一个普通表单,但 website_mass_mailing 实际把订阅状态识别、公开名单约束、重复订阅复用、opt-out 恢复和 reCAPTCHA/Turnstile 防护串成了一套完整流程。

网站
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 官网上的“Subscribe to Newsletter”并不是一个简单的收邮箱输入框。

website_mass_mailing 实际在处理的是一整条订阅治理链路:

  • 判断当前人是不是已经订阅;
  • 区分公开 mailing list 和不可再公开加入的名单;
  • 复用已有联系人而不是盲目重复创建;
  • 遇到历史退订记录时恢复订阅而不是生成脏数据;
  • 在公开网站入口前增加 reCAPTCHA / Turnstile 防护;
  • 把订阅值记到 session,让后续体验更连续。

所以这个模块真正解决的不是“能不能收集邮箱”,而是:

如何在公开网站上,低摩擦但相对可控地完成订阅收口。


一、为什么订阅前先判断“是不是已经订阅”

/website_mass_mailing/is_subscriber 这个接口非常关键。

它会根据:

  • list_id
  • subscription_type
  • 当前用户邮箱或 session 中记住的邮箱

去查 mailing.subscription,并且只把 opt_out = False 的记录算作已订阅。

这意味着 Odoo 在用户点击订阅前,就尽量先回答一个现实问题:

这个人到底是新订阅者,还是已经在名单里的人?

这样做有两个好处:

  1. 前端可以直接切到“已订阅/感谢”状态,而不是让人重复提交;
  2. 运营侧不会因为同一个人反复点按钮而收到一堆噪音动作。

所以在 Odoo 里,“订阅按钮状态”不是纯前端交互,而是基于真实订阅记录的。


二、为什么公开网站也要区分哪些名单可以被加入

controllers/website_form.py 里有一段很有代表性的校验:

如果表单目标是 mailing.contact,系统会把传入的 list_ids 拿出来检查,并拦截 is_public = False 的名单。

换句话说:

  • 不是所有邮件名单都适合暴露在官网上;
  • 即便前端能传入 list id,后端也会再核验;
  • 私有名单不会因为一个公开表单就被随意加入。

这体现了 Odoo 很清晰的边界意识:

官网订阅入口是公开的,但 mailing list 本身不一定应该完全公开。

对于企业来说,这一点很重要。 因为内部客户名单、测试名单、分层运营名单,很多都不应该被官网直接写入。


三、为什么 subscribe 不是单纯 create,而是“查找、复用、恢复”

subscribe_to_newsletter() 的实现非常务实。

它不是见到一个邮箱就直接新建,而是分三步:

第一步:解析输入

如果是 email 订阅,会先 parse_contact_from_email,尽量拆出 name 和 email。

第二步:找现有 subscription

系统会先查:

  • 当前 list
  • 当前 email

对应的 mailing.subscription 是否已经存在。

第三步:按情况处理

  • 没有 subscription: 去找现有 mailing.contact,没有再创建,然后建立 subscription;
  • 已有 subscription 且 opt_out = True: 直接恢复为重新订阅;
  • 已有有效 subscription: 不重复造数据。

这个逻辑背后非常成熟。

它说明 Odoo 把订阅当成“联系人与名单之间的关系”,而不是一条孤立表单记录。

因此它优先考虑的是关系复用和状态恢复,而不是无限新增。


四、为什么 opt-out 恢复比“重新创建一条”更重要

很多系统在处理退订用户重新订阅时,会粗暴再插一条记录,最后导致:

  • 同一邮箱出现多条重复 contact;
  • 名单关系混乱;
  • 历史行为很难解释;
  • 后续退订、统计和清洗都变脏。

Odoo 的做法更干净:

如果已有 subscription 只是 opt_out = True,那就把它恢复。

这个动作看起来简单,实际上非常重要。

因为它承认:

用户状态会变化,但业务对象最好保持连续。

对数据治理来说,这比“重新建一条省事”成熟得多。


五、为什么公开订阅入口必须做机器人防护

/website_mass_mailing/subscribe 在真正处理订阅前,会先调用:

request.env['ir.http']._verify_request_recaptcha_token('website_mass_mailing_subscribe')

前端交互里还兼容了 Google reCAPTCHA 和 Turnstile。

这说明 Odoo 非常清楚:

  • 官网订阅入口天然暴露在公网;
  • 只要能写入联系人或名单,就存在被脚本灌数据的风险;
  • 如果没有机器人防护,邮件营销名单会很快被污染。

而名单一旦被污染,后果不只是“多了点垃圾邮箱”,还会影响:

  • 投递质量;
  • 域名信誉;
  • 退信率和投诉率;
  • 运营分析可信度。

所以验证码这里不是可有可无的安全附件,而是 mailing 数据质量的一道前门。


六、为什么 session 记忆能明显改善订阅体验

源码在成功订阅后会把值写到 session,例如 mass_mailing_email

这让后续页面上的订阅组件可以:

  • 自动知道当前浏览器对应的邮箱是谁;
  • 判断是否已订阅;
  • 避免让同一人重复输入。

这类设计经常被忽略,但它对转化体验非常有用。

因为用户不会觉得“每个区块都在重新问我同一件事”,而是会感到网站记得自己刚完成过什么动作。

对于订阅弹窗、页脚表单、专题页嵌入表单并存的站点,这种一致性尤其重要。


七、为什么官网订阅不是孤立功能,而是 Email Marketing 的入口

从模型关系可以看出,官网表单最终写入的是 mailing.contactmailing.subscription

也就是说,网站层只是入口,真正承接这条数据的,是 Email Marketing 体系。

这会影响你对“订阅成功”的定义。

它不是:

  • 前端 toast 显示成功;
  • 数据库里多了一行邮箱;

而是:

  • 联系人对象干净;
  • 名单关系正确;
  • 私有名单没有被误写;
  • 退订状态被合理恢复;
  • 机器人没有轻易灌水进来。

如果这些没处理好,表面上的“收集成功”其实没有太大价值。


八、实施时最容易踩的坑

1. 把所有名单都设成 public

这样最方便,但长期最危险。公开入口应该只连适合公开收集的名单。

2. 只盯着前端样式,不检查后端名单关系

订阅弹窗再漂亮,如果联系人和 subscription 脏了,后面营销质量会一起受损。

3. 忽略机器人防护配置

reCAPTCHA/Turnstile 不只是“防一点垃圾提交”,而是在保护你的发信资产。

4. 不重视 opt-out 的恢复逻辑

如果团队不了解这一层,可能会误以为“重新订阅怎么没新建联系人”,其实这是更正确的做法。


最后总结

website_mass_mailing 在官网侧真正处理的是:

  • 订阅身份识别;
  • 名单公开边界;
  • 联系人与名单关系复用;
  • 退订后的状态恢复;
  • 公开入口的反机器人保护;
  • 多订阅组件之间的连续体验。

所以它不是一个“邮箱收集小表单”,而是:

Odoo 在官网与 Email Marketing 之间设置的一道低摩擦、但尽量不失控的数据闸门。

理解了这一点,你就会明白为什么它的源码既关心 UX,也关心名单权限,还关心 session 和记忆——因为官网订阅真正难的,从来不是按钮文字写“Subscribe”,而是后面的数据要不要还能放心用。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。