企业版自动化

Odoo 企业版 Marketing Automation 为什么不是“定时群发”而已:participants、traces 与触发图边界讲透

很多人把 Odoo 企业版 Marketing Automation 想成邮件自动化器。真正看 marketing_automation 源码会发现,官方做的是一张“参与者 + 轨迹”执行图:campaign 决定目标模型与唯一键,participant 代表被纳入流程的记录,trace 代表某个活动在某个参与者上的待执行节点,活动修改后还要重新 sync 才会重排计划。

企业 其他
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 7 阅读

很多人第一次接触 Odoo 企业版 Marketing Automation,脑子里对应的往往是 Mailchimp 式理解:

  • 拉一个人群;
  • 定时发几封邮件;
  • 根据打开/点击继续发下一封。

这个理解有一定表象,但没说到它的架构重点。

真正看 marketing_campaign.pymarketing_activity.pymarketing_participant.py 后,你会发现 Odoo 做的不是“邮件序列器”,而是一套 参与者执行图

  • campaign 定义目标对象和总过滤条件;
  • activity 定义节点、延迟和触发边;
  • participant 代表某条业务记录进入这张图;
  • trace 代表“这个参与者在这个节点上的待执行/已执行轨迹”。

所以 Marketing Automation 的本质不是“定时发消息”,而是“给一批记录跑一张带触发边和时间延迟的执行图”。


一、campaign 的核心不是名字,而是“目标模型 + 去重语义”

marketing.campaign 上最关键的不是标题,而是:

  • model_id
  • domain
  • unique_field_id
  • marketing_activity_ids
  • participant_ids

这说明 campaign 不是邮件容器,而是:

  1. 面向哪个业务模型跑;
  2. 用什么 domain 选人;
  3. 需要按哪个字段去重;
  4. 跑哪些节点。

为什么 unique_field_id 很重要

源码注释说得很直白:

  • 它用于避免基于某个字段处理重复记录;
  • 比如 email 相同的联系人,只想进一次流程。

这说明 Odoo 在 campaign 层就考虑了:

自动化流程的对象不是“数据库行”,而是“业务身份”。

否则同邮箱多个记录会被反复轰炸。


二、为什么 activity 不是列表项,而是图节点

marketing.activity 不只是一个“第 1 封邮件 / 第 2 封邮件”的顺序项。

它同时有:

  • parent_id
  • child_ids
  • trigger_type
  • interval_number / interval_type
  • validity_duration
  • activity_domain

这已经非常像图结构了。

节点如何连接?

  • begin:流程起点;
  • activity:接在另一个活动后;
  • mail_open / mail_not_open / mail_click / mail_reply ...:按邮件行为触发。

这说明营销自动化不是线性序列,而是:

  • 一条记录进入后,可能沿不同分支继续;
  • 某些节点只在父节点满足特定行为后生成子轨迹。

activity 的本质是带触发条件的节点,不是普通待发任务。


三、为什么 participant 是核心,而不是 mailing trace

marketing.participant 是整个模块很容易被忽视、但其实最重要的模型之一。

它绑定的是:

  • campaign_id
  • model_name
  • res_id
  • trace_ids
  • state

这意味着 Odoo 不是让每次邮件直接盯着业务对象跑,而是先造一个 participant 代理层。

这个代理层解决了什么?

  1. 把一个业务记录和某个 campaign 的关系固定下来
  2. 允许同一业务记录进入不同 campaign 而互不冲突
  3. 允许在 participant 层维护运行中 / 已完成 / 已移除状态
  4. 让后续 traces 全挂在这个 participant 身上。

所以 participant 不是冗余表,而是 流程实例


四、为什么 begin 活动不是 campaign 启动时一次性群发,而是 participant 创建时生成 trace

marketing.participant.create() 里做了一件很关键的事:

  • 为所有 trigger_type='begin' 的 activity 生成 trace;
  • schedule_date = 当前时间 + 活动延迟;
  • 并触发 cron 在相应时间点执行。

这说明 campaign 本身不是“发出去”的主体。

真正被安排进时间线的是:

  • 某个 participant
  • 在某个 activity
  • 的一条 trace

因此系统实际调度的最小单位是 trace,不是 activity,也不是 campaign。

这很像任务图调度器,而不是群发器。


五、为什么改了 activity 后要 require_sync

marketing.activity.write()marketing.campaign.action_update_participants() 这条链很重要。

如果活动配置变了,比如:

  • 延迟改了;
  • 过滤条件改了;
  • 新增了活动节点;

系统不会假装历史 trace 自动天然就一致。

它会:

  • 把活动标成 require_sync=True
  • campaign 上出现 require_sync
  • 需要显式执行 action_update_participants() 去重排 traces。

这说明 Odoo 把活动图和已生成执行轨迹分开看:

  • 图变了,不代表旧轨迹自动安全;
  • 需要一次同步操作,把计划层修改投影到实例层。

sync 的本质是“重新对齐流程定义和已落地执行图”。

这很专业,也很克制。


六、为什么 domain 会沿父节点继承叠加

_compute_inherited_domain() 会把:

  • campaign.domain
  • 当前 activity.activity_domain
  • 所有祖先 activity 的 domain

全部 AND 在一起。

这说明一个子节点能执行,不只是看自己的条件,而是:

  • 它所在整条路径上的限制都得满足。

这有什么好处?

这样你在父节点定义的筛选,会自然向下游分支继承,而不用每个子节点手动重复抄一遍。

也就是说,营销图上的过滤不是局部 patch,而是 路径约束的累积


七、为什么 allowed_parent_ids 要按 trigger 类型限制

并不是所有 activity 都能接在任意父节点后面。

源码会根据 trigger_type 限制 allowed_parent_ids

  • 如果触发条件是邮件打开、点击、回复;
  • 那父节点就必须是 email 类活动;
  • 否则流程图语义不成立。

这说明 Odoo 并不允许用户随意乱连图。

节点图不是随便画出来的 DAG,而是带业务类型约束的触发图。

这保证了流程图的因果关系是真实可执行的。


八、为什么 participant 完成不是看时间,而是看还有没有 scheduled traces

participant.check_completed() 的逻辑很朴素但很关键:

  • 只要这个 participant 已经没有 state='scheduled' 的 traces;
  • 就可以把 participant 标成 completed

这说明参与者完成不是:

  • 跑满了多少天;
  • 执行了多少节点;
  • 或某个最终节点显式打标。

而是:

执行图上已经没有未来待跑的节点。

这是很图论的完成定义,和普通邮件营销工具很不一样。


九、为什么删除业务记录要把 participant 标成 unlinked

如果底层业务对象没了,participant 不会假装还能继续跑。

action_set_unlink() 会:

  • 把 participant 状态置为 unlinked
  • 把未执行 traces 改成 canceled
  • 原因写成 Record deleted

这说明 Odoo 对自动化很谨慎:

  • 不是目标记录没了还继续乱发;
  • 而是显式终止并保留取消痕迹。

十、实战里最容易误解的 4 个点

误区 1:campaign 就是邮件活动集合

不对。

它定义的是目标模型、过滤条件和去重语义。

误区 2:activity 是线性序列步骤

不是。

它们是带触发边的节点图。

误区 3:改了活动配置会自动改好所有实例

不会。

需要 sync 把定义层变更映射到 traces。

误区 4:participant 只是中间表

也不对。

它本质上是“业务记录在某个 campaign 中的一次流程实例”。


总结

marketing_automation 源码串起来后,你会发现 Odoo 企业版做的根本不是“定时群发”。

它真正做的是一张可执行营销图:

  • campaign 定义目标模型、过滤和去重;
  • activity 定义节点、延迟和触发边;
  • participant 表示流程实例;
  • trace 表示实例在节点上的待执行轨迹;
  • sync 在定义层变更后重排执行图;
  • completed / unlinked / canceled 精细收口实例生命周期。

所以 Odoo 企业版 Marketing Automation 的本质,不是“自动发邮件”,而是“围绕业务记录运行一张带时间和行为触发的流程图”。

这才是这部分源码最值得学的地方。


参考源码 - enterprise/marketing_automation/models/marketing_campaign.py - enterprise/marketing_automation/models/marketing_activity.py - enterprise/marketing_automation/models/marketing_participant.py

DISCUSSION

评论区

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