先说结论
很多人把 Discuss 频道通知理解成一个很简单的逻辑:
- 是成员
- 就会收到频道消息
但 addons/mail/models/discuss/discuss_channel.py 和 discuss_channel_member.py 告诉我们的现实要复杂得多。
在 Discuss 里,通知不是“成员统一广播”,而是一张条件矩阵:
- 频道成员是谁
- 频道类型是什么
- 用户是否静音
- 用户是否只想收 mentions
- 用户当前是否是 busy
- 这条消息是不是把某人显式拉进
partner_ids - 最后走 inbox、email 还是 web push
所以更准确的说法是:
Discuss 的协作通知不是“成员名单”,而是“成员状态 × 频道类型 × 消息上下文”的计算结果。
一、频道成员为什么不是 follower
discuss.channel 在 _notify_get_recipients() 里写得很直接:
- channel 不是标准意义上的
mail.thread文档 - 它没有 followers 这一套收件基础
- 它依赖的是
discuss.channel.member
这点特别重要。
因为很多人看到 Odoo 全站都有 Chatter,就会下意识套用“followers 决定收件人”的思路。
但 Discuss 频道不是业务对象页面下的一段消息区, 它本身就是即时协作空间。
因此它的基础单位不是 follower,而是 member。
这意味着 Discuss 更关心的是:
- 你是不是这个空间的成员
- 你对这个空间现在采用什么通知策略
而不是“你是否订阅某条业务记录”。
二、为什么同样是成员,收件策略却不同
discuss.channel.member 上有几个很关键的字段:
custom_notificationsmute_until_dtseen_message_idnew_message_separatorlast_interest_dtunpin_dt
其中最直接影响通知策略的是 custom_notifications。
源码给出的取值有:
allmentionsno_notif
而且帮助文案还明确说:
- 这套定制设置主要应用在频道(channel)上
- 如果没指定,就回退到用户设置里的默认值
也就是说,Discuss 不是“同一频道,同一提醒方式”。
它允许成员按自己的工作方式调节噪音密度。
这很符合真实办公场景:
- 有人要盯全量频道
- 有人只要被点名时再进来
- 有人这段时间临时不想被打断
三、为什么 mentions only 不是前端小开关,而是后端路由条件
在 discuss.channel._notify_get_recipients() 里,频道通知筛成员时写得相当细。
对于 channel_type='channel' 的公共/团队频道,系统会判断:
- 成员自定义是否是
all - 若成员没自定义,看用户设置里
channel_notifications是否是all - 若成员自定义是
mentions,则只有当该成员出现在partner_ids时才会被算进来 - 若成员没自定义且用户默认也不是全量,也会回到“必须在
partner_ids里”这个条件
这说明 “mentions only” 并不是 UI 上写着好看的一行字。
它实际改变了后端的收件人筛选逻辑。
所以你可以把它理解成:
成员仍然在频道里,但默认不进入每条消息的通知收件集;只有被显式点名时,才重新进入。
这和简单静音还不一样。
四、为什么静音和 busy 也会改变通知结果
很多人以为静音只是前端不弹红点。
但 Discuss 在后端选收件人时,直接排除了:
mute_until_dt仍然生效的成员manual_im_status = busy的内部用户
这透露出 Odoo 的一个很务实的设计判断:
- 通知不只是“系统能不能送”
- 还要考虑“此刻是否适合打扰”
也就是说,Discuss 并没有把“成员关系”和“通知资格”画等号。
成员资格只是基础, 真正能不能被推送,还要看当前状态。
这正是即时协作和普通 Chatter 的差异之一。
五、为什么 partner_ids 在频道里仍然很重要
虽然频道没有 followers,
但 partner_ids 仍然在 Discuss 通知中扮演关键角色。
原因很简单:
partner_ids代表这条消息被显式点到的人- mentions only 策略最终就靠它来判断“这次是不是轮到你”
所以在频道场景里,partner_ids 的意义不是“持久订阅”,而更像:
- 本条消息的精确命中名单
这也是为什么 mention 功能和频道通知偏好能自然接到一起。
一个成员平时可以不接收全量频道广播, 但只要本条消息明确点了他,后端仍能把他拉回通知路径。
六、为什么 web push 要和 inbox 分开算
discuss.channel._notify_thread_by_web_push() 里有一段很关键的注释:
- Discuss 频道里,只对
notif == 'web_push'的收件人发 web push - 要排除 inbox recipients
- 因为 inbox 和 web push 在频道场景里可能是互斥的
这段设计很值得品。
它说明 Odoo 没把“通知”抽象成一条单路通道, 而是承认:
- 有的用户想在 Discuss 里看未读
- 有的用户更依赖系统级推送
- 两者不能机械叠加,否则会重复打扰
所以频道通知不是“先确定人,再把所有渠道都打一遍”, 而是:
- 先分类成员
- 再根据分类决定走哪条路
这比很多 IM 集成做法成熟得多。
七、为什么频道类型不同,通知心智也不同
同一个 _notify_get_recipients() 里还能看到 Odoo 对频道类型的不同处理:
chatgroupchannel
尤其 channel 类型最强调成员偏好设置,
因为它更像团队公共空间,天然更容易产生噪音。
而一对一聊天、临时小组聊天的通知逻辑通常更直接。
这背后的产品判断很合理:
- 空间越公共,越要允许成员自己调噪
- 空间越私密、越临时,越接近强提醒
所以 Discuss 的通知模型并不是单一算法,而是跟空间性质耦合的。
八、这套设计对协同办公意味着什么
这意味着 Odoo Discuss 并不把“协作”理解成无差别广播。
它更像在做两件事:
1. 让成员关系足够清楚
你属于哪个频道,决定你有没有资格参与。
2. 让打扰强度足够可调
你怎样被打扰、在什么情况下被拉进来,则由成员设置和消息上下文共同决定。
这种设计比“要么全收、要么退群”更适合企业协作。
因为真实团队里,很多频道本来就应该:
- 低频盯着
- 必要时被点名
- 特殊时段暂时静音
Odoo 把这些现实工作方式直接写进了模型结构里。
九、实战里最容易踩的坑
1. 把 Discuss 频道误当成普通 Chatter 线程
结果设计时只会想 followers,不会想 members。
2. 只测“成员能不能收到”,不测“mentions only 是否只在 partner_ids 命中时触发”
最后以为通知随机。
3. 忽略 mute 与 busy 状态
以为系统漏推,其实是故意不打扰。
4. 同时堆 inbox 和 web push
容易把频道体验做成双重轰炸。
5. 不区分 chat / group / channel 的通知语义
把所有空间做成一套规则,体验通常会很差。
一句话记忆法
Odoo Discuss 的频道通知不是“成员统一收消息”,而是“成员资格 + 频道偏好 + 当前状态 + 本条消息是否点名”的联合计算。
DISCUSSION
评论区