先说结论
Odoo Survey 真正的核心不是“问卷模板”,而是 survey.user_input 这张答题执行单。
从 /home/ubuntu/odoo-temp/addons/survey/models/survey_user_input.py 与 addons/survey/models/survey_survey.py 来看,官方把下面这些能力都收拢到一条主链里:
- 答题会话的开始、进行中与完成状态
- 评分总分、百分比与是否通过
- 尝试次数限制与第几次尝试判断
- 总时长限制与 Live Session 单题限时
- 认证成功后的邮件与徽章发放
所以它真正回答的问题不是“用户填完没有”,而是:
这次作答到底是一次普通问卷、一次考试、一次认证,还是一次直播互动,而系统又如何用同一套对象把它们区分开。
第一层:为什么作答记录才是 Survey 的业务中心
很多人研究 Survey 时会把注意力都放在 survey.survey:
- 有哪些题
- 分页怎么排
- 随机题怎么抽
这些当然重要,但真正承载业务结果的是 survey.user_input。
这张表上直接挂着:
survey_idstatestart_datetimeend_datetimeaccess_tokenpartner_id / email / nicknamescoring_totalscoring_percentagescoring_successattempts_count / attempts_numberis_session_answer
这说明在 Odoo 眼里,一张问卷不是“模板被提交了一次”,而是“有人完成了一次带规则的作答过程”。
这个视角特别关键,因为只看模板时,很多复杂问题都无法回答:
- 这个人是第几次考
- 这次有没有超时
- 分数是多少
- 是否有资格拿证书
- 这是不是 Live Session 的临时答题
这些都只能在 user input 层处理。
第二层:评分不是表面功能,而是整个认证链的入口
_compute_scoring_values() 和 _compute_scoring_success() 很值得看。
源码的逻辑不是简单“对错题数 / 总题数”,而是:
- 先根据预定义题目集合,算出本次可得的总分上限
- 不同题型采用不同的总分算法 - 单选取正分答案里的最大值 - 多选累加正分答案 - 其他 scored question 用题目自身分值
- 再把作答行
user_input_line_ids.answer_score汇总成实际得分 - 最后转换成百分比,并与
scoring_success_min比较
这说明 Odoo 的评分机制并不只是“给个结果看看”,而是为了让不同题型在同一套认证规则里可比较。
为什么这很重要?
因为后面的证书发放、是否通过、平均分统计,全部依赖这套标准化后的百分比逻辑。
如果实施时只盯前端显示,忽略这里的计分边界,很容易出现:
- 配了多选题却误判总分上限
- 认证门槛设了 80%,结果业务理解和系统算法不一致
- 导出的答题明细看着合理,但通过率异常
第三层:证书为什么不是“通过后发封邮件”那么简单
很多人以为 certification 只是一个 UI 开关。 其实从源码看,它有一整套约束与后置动作。
在 survey.survey 上:
certification只能用于有评分机制的问卷- 有
_certification_check约束:没有 scoring 就不能做 certification - 还能配置
certification_mail_template_id - 还能决定是否
certification_give_badge
而在 survey.user_input._mark_done() 里,完成作答后会:
- 把状态写成
done - 如果是 certification 且答题成功 - 发送认证邮件模板 - 需要时收集 badge,触发 gamification challenge 更新
这意味着“发证书”在 Odoo 里不是前端页面弹一个成功提示,而是后台的一条自动化分发链。
它至少涉及三件事:
- 成绩是否达标
- 这次是否测试条目
test_entry - 是否需要发正式邮件 / 徽章
所以证书业务真正怕的不是“模板没设计好”,而是:
你没有把评分、完成状态和自动化后置动作当成一个整体配置。
第四层:限次逻辑为什么比想象中更严谨
_compute_attempts_info() 非常值得研究。
它不是简单在单条记录上加一个“第几次”的字段,而是基于 SQL 聚合,对同一问卷下的全部已完成尝试进行比较。
匹配口径同时考虑:
survey_idstate = done- 不是
test_entry invite_tokenpartner_id或email
这几个条件很有现实感。
为什么要看 invite_token
因为某些邀请型问卷并不是“任何登录用户都算同一个对象”,而是一次邀请池里的尝试应该被视作同一组。
为什么同时看 partner 和 email
因为问卷入口可能既来自实名联系人,也可能来自匿名或邮件直达场景。
为什么只统计 done
因为真正应该消耗尝试次数的,是完成作答,而不是中途打开又退出的会话。
这比很多第三方问卷“打开一次算一次”的做法更合理,也更适合考试或认证业务。
第五层:时间限制在普通问卷和 Live Session 里根本不是一回事
源码里有两套时间边界:
普通作答:survey_time_limit_reached
当问卷设置了 is_time_limited,系统会用:
start_datetimesurvey.time_limit
来判断总时长是否超限。
Live Session:question_time_limit_reached
如果 is_session_answer 为真,就改用:
survey.session_question_start_timesurvey.session_question_id.time_limit
来判断单题时间是否超限。
这说明 Odoo 并没有把 Live Session 仅仅当成“界面更热闹的问卷模式”,而是把它当成另一种答题语义。
普通考试关注的是整份卷子的时间预算。 Live Session 关注的是当前问题窗口。
再结合 session_code、session_show_leaderboard、session_speed_rating 这些字段,你会发现 Live Session 更接近:
- 课堂抢答
- 培训互动
- 会议现场答题
而不是传统考试。
所以实施时最常见的误区,就是拿考试思维去配置现场互动,或者拿直播互动思维去配置正式认证。
第六层:为什么预定义题目集合也要落到 user input 上
create() 里会自动把 survey._prepare_user_input_predefined_questions() 填进 predefined_question_ids。
这件事很容易被忽略,但它其实非常关键。
原因是:
- 问卷题目可能后来被改动
- 条件题可能动态出现或失效
- 随机抽题模式下,不同人看到的题目本来就不一样
如果不把“这次作答实际对应的题集合”固化到 user input,后续评分、打印答卷、审计复盘都会失真。
这就是为什么 Odoo 会在完成后再把 inactive conditional questions 从 predefined_question_ids 里剔除——它在尽力保留一次作答的真实快照。
最容易误解的四个点
误区一:Survey 就是表单
不对。只要你启用了评分、限次、证书或 Live Session,它就是一套轻量考试引擎。
误区二:Certification 是邮件模板功能
也不对。它建立在评分、通过门槛、完成状态和徽章链路之上。
误区三:Attempts 只是简单计数
源码实际上用了比较严谨的身份归并逻辑,避免匿名 / 邀请 / 联系人三种入口互相冲突。
误区四:Live Session 只是 Survey 的一个皮肤
不是。它改变了答题时间边界、排行榜逻辑和互动方式。
实战上怎么把 Survey 配得更稳
建议按这套顺序理解与落地:
- 先决定业务类型:调查、考试、认证、还是现场互动
- 再决定是否需要评分,以及评分方式
- 如果是认证,再配置通过门槛、邮件和徽章
- 如果要限次,提前确认身份口径以 partner 还是 email 为主
- 如果是 Live Session,把单题限时和排行榜当成核心,而不是附加项
- 任何会影响结果的随机题、条件题,都要关注 user input 快照而不是只看 survey 模板
最后一句话
Odoo Survey 最容易被低估的地方,就是它表面像问卷,底层却已经长成了一套统一管理作答过程的规则引擎。
所以看懂它的最好方式不是“会不会出题”,而是:
它如何把一次作答变成一条可评分、可限时、可认证、可追溯的业务记录。
DISCUSSION
评论区