先说结论
Odoo 的在线学习系统,真正的核心不是课件本身,而是人和课程之间的关系。
从 /home/ubuntu/odoo-temp/addons/website_slides/models/slide_channel.py 与 slide_channel_partner.py 来看,官方把这件事拆成了两层:
slide.channel:课程容器slide.channel.partner:某个成员与这门课之间的关系
这层拆分让系统可以同时处理:
- 公开课与邀请制课程
- 学员加入状态
- 学习完成度
- 下一课推荐
- 结课奖励与邮件通知
所以如果你只把 eLearning 理解成“网站上放几篇文章或几个视频”,会低估它很多。
它真正要解决的是:
一个人是否被允许进课、进来后学到哪、学完后系统怎么识别并触发后续动作。
第一层:为什么课程和成员关系不能混在一张表里
slide.channel 负责课程本身:
- 课程名称和描述
- 可见性
visibility - 报名策略
enroll - 课程内容统计
- 评分、访问量、课程时长
- 先修课程
prerequisite_channel_ids
但这些都还只是“课程定义”。
真正一旦涉及到某个具体学习者,系统关心的问题就变成:
- 他有没有被邀请
- 是刚加入、学习中还是已完成
- 完成了多少内容
- 下一节该看什么
- 是否需要发结课通知
这些显然不该塞回课程主表,于是 Odoo 建了 slide.channel.partner。
这张关系表非常关键,它告诉我们:
在 Odoo 的设计里,学习不是课程字段,而是成员关系状态。
第二层:成员状态为什么不是简单二元值
slide.channel.partner 的 member_status 有四种状态:
invitedjoinedongoingcompleted
这已经说明 Odoo 不把学员关系理解成“在课里 / 不在课里”这么简单。
它至少想表达三层阶段感:
- 还没真正进入课程,只是收到了邀请
- 已经加入,但还没开始或刚开始
- 正在学习
- 已经完成
这对运营很重要,因为不同阶段应该触发不同动作:
- invited:催加入
- joined:引导开课
- ongoing:推动继续学习
- completed:发祝贺、证书或推荐下一门课
如果状态只有一个 enrolled,系统就没法做精细化学习运营。
第三层:完成度为什么要单独计算,而不是手填
_recompute_completion() 是这套学习关系的核心之一。
它会按:
- 当前课程
- 当前成员
- 已发布且 active 的内容
- 对应成员是否完成每个
slide.slide
来统计 completed_slides_count 和 completion 百分比。
这说明 Odoo 对学习进度的理解非常务实:
- 进度不是人工标注
- 也不是纯前端动画效果
- 而是基于成员和内容之间的完成关系实时汇总
更有意思的是,源码里还保留了状态回退逻辑:
- 之前已经 100%
- 后来课程内容增加,或完成记录变化
- 就可能从 completed 回退到 ongoing
这非常符合真实培训场景。
因为课程不是永远静止的,内容更新后,老学员未必还算“完整学完”。
第四层:为什么系统要计算“下一课”
_compute_next_slide_id() 这个方法特别能体现产品思路。
它不是让学习者自己从目录里慢慢翻,而是直接查出:
- 当前课程中
- 已发布、未归档、不是分类节点的内容
- 按 sequence 排序后
- 第一条尚未完成的内容
然后作为 next_slide_id。
这个设计背后的重点不是技术,而是降低学习摩擦。
因为大部分学习平台真正流失的地方,不是“内容不够多”,而是学员每次回来都不知道下一步该点哪里。
Odoo 通过 next_slide_id 其实在做一件很产品化的事情:
尽量让用户少做导航判断,只做继续学习这个动作。
第五层:邀请链接为什么要做哈希
invitation_link 里会拼上:
invite_partner_idinvite_hash
其中哈希来自 _get_invitation_hash()。
这说明 Odoo 的邀请并不是“发一个公开课程链接”那么粗糙,而是希望:
- 链接能对应特定成员
- 邀请关系不能被轻易伪造
- 邀请制课程仍能保持一定访问控制
也就是说,课程邀请并不只是 UX 文案问题,而是访问边界的一部分。
这点在企业培训、内部文档学习、客户赋能课程里很重要。
第六层:为什么完成课程会触发积分和邮件
当 _recompute_completion() 发现成员达到 100% 时,会把记录放进 completed_records,随后调用:
_post_completion_update_hook()_send_completed_mail()
前者会按课程设置给用户加 karma,后者会发结课邮件。
这说明 Odoo 的课程完成不是一个被动统计值,而是工作流触发点。
完成课程后,系统认为应该有后续动作:
- 奖励
- 通知
- 社区活跃度提升
- 后续课程引导
所以学习平台在 Odoo 里不是静态内容仓库,而是一个会驱动行为的运营系统。
最容易误解的三个点
误区一:课程成员就是 Many2many 关系
表面上像,但官方显然不满足于“有成员名单”这种弱关系,所以单独做了带状态、进度和邀请信息的中间模型。
误区二:完成度是前端展示字段
不是。它依赖内容完成记录和后端聚合,直接影响成员状态、结课通知与奖励。
误区三:课程页面能打开,就说明学习链路完整
也不是。真正完整的是:
- 可见性
- 报名策略
- 成员加入
- 学习推进
- 完成触发
- 后续通知
实战上最该关注什么
如果你在做 Odoo 培训站或客户学院,最该检查的是这些边界:
- 课程是公开、登录可见、成员可见,还是仅链接可见
- 报名是开放、邀请制,还是依赖群组自动加入
- 完成度是否只统计有效发布内容
- 内容更新后,老学员状态是否允许重新计算
- 结课邮件、奖励和推荐下一课是否要继续扩展
最后一句
Odoo 在线学习最值得学的,不是它怎么放视频,而是它把“学习”真正建模成了一种成员关系演化:
- 先邀请
- 再加入
- 然后学习
- 最后完成并触发后续动作
理解 slide.channel 和 slide.channel.partner 这组设计之后,你会发现它其实已经很接近一个轻量 LMS 了。
DISCUSSION
评论区