协同办公

Odoo 语音视频通话为什么不是前端临时状态:RTC session、呼叫历史与失活回收链路讲透

很多人以为 Discuss 里的通话只是浏览器点开 WebRTC 就结束。源码里的 `discuss.channel.rtc.session`、`discuss.call.history` 和失活清理逻辑说明,Odoo 把通话建成了可追踪的频道会话:有成员唯一性、开关麦摄像头状态、通话开始消息、结束时间和垃圾回收。

协同办公
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 里的语音/视频通话,不是“前端浏览器一连上就算了”。

它在后端有正式的 RTC session 模型和 call history 模型,通话开始、成员加入、状态变化、无人后结束、僵尸会话清理,都有明确链路。

所以 Discuss 通话更像一种“实时协作对象”,而不是页面上的临时 UI 特效。


一、为什么每个频道成员只能有一个 RTC session

discuss.channel.rtc.sessionchannel_member_id 做了唯一约束。

这意味着在同一个频道语义下,成员的实时会话身份必须唯一。

它解决的是两个核心问题:

  • 避免同一成员在同一频道被算成多个并发发言者
  • 保证后续的邀请、广播、断开、清理都能定位到明确会话

所以 RTC session 不是“浏览器 tab 数量”,而是频道成员的实时通话占位


二、为什么通话开始时会自动发一条消息

源码里,当某频道第一次出现 RTC session,系统会:

  • 发一条 message_type="notification" 的通话开始消息
  • 创建一条 discuss.call.history
  • 写入开始时间和起始消息 ID

这非常像现实办公:

  • 通话不是凭空发生
  • 它应该在协作线程里留下一个“这里开始过一次通话”的痕迹

因此通话历史不是额外报表,而是频道协作历史的一部分。


三、为什么麦克风/摄像头/共享屏幕状态要落后端

RTC session 上存了:

  • is_screen_sharing_on
  • is_camera_on
  • is_muted
  • is_deaf

很多人会觉得这些都是前端本地状态,为什么还要广播到后端?

答案是:因为协作方需要看见彼此状态,而且这些状态会影响邀请、显示和实时交互。

如果全留在本地:

  • 其他成员看不到一致状态
  • 多端同步会混乱
  • 会话结束后也无法进行统一清理

四、为什么要有失活会话垃圾回收

源码里 _inactive_rtc_session_domain() 把超过约 1 分 15 秒未更新的会话视作失活,_gc_inactive_sessions() 会自动清理。

这背后的场景很现实:

  • 浏览器崩了
  • 网络断了
  • 用户直接关标签页
  • Odoo session 失效

如果没有 GC,频道里会残留“明明人都不在了,还显示通话中”的僵尸状态。


五、为什么最后一个人离开时要做这么多事

unlink() 里,当频道最后一个 RTC session 离开,系统会:

  • 取消邀请
  • 清掉 SFU channel uuid 和 server url
  • 补全 call history 的结束时间
  • 广播会话结束

这说明“挂断电话”不是删一行数据,而是一组协作状态的收口动作


六、最容易误解的地方

误区 1:RTC session 就是前端连接对象

不是,它还是频道协作状态对象。

误区 2:只要浏览器关了,后端自然知道

不一定,所以才需要 autovacuum 清理。

误区 3:通话历史只是统计功能

不是,它和频道消息历史是连着的。


七、排错顺序

当你遇到“频道一直显示有人在通话 / 通话没结束 / 状态不同步”时,建议按这个顺序查:

  1. 当前频道成员是否存在重复或僵尸 RTC session
  2. 最近是否触发过 inactive session GC
  3. 是否已经是最后一个成员离开但 call history 没写 end_dt
  4. SFU 地址和频道 UUID 是否已清空
  5. 最后再怀疑前端麦克风权限或浏览器兼容性

最后一句

Odoo 通话模块真正做的,不只是连音视频,而是把“谁在通话、何时开始、何时结束、现在是什么状态”纳入协作系统本身。

这就是它和纯前端插件最大的区别。

DISCUSSION

评论区

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