先说结论
Odoo CRM 的 lead enrichment,不是“给我一个邮箱,系统就把公司资料全灌进去”。
源码真正表达的是一套很克制的策略:
- 不是所有线索都允许补全
- 不是所有邮箱都值得补全
- 不是所有字段都会被覆盖
- 不是一次失败就无限重试
- 不是一批线索一起跑时就不考虑锁和事务
所以如果你在线上看到这些现象:
- 某些 lead 明明有邮箱,却没有出现 enrich 按钮
- 某些线索被补全了国家、公司名,却没有覆盖你原来手填的电话
- 某批 enrich 跑到一半后没有把所有线索都处理完
- 合并后新主记录看起来“继承了补全状态”
这些都不是零散行为,而是 Odoo 在源码层面有意识地做了保护。
一、按钮能不能点,先看这条线索有没有资格被补全
在 crm_iap_enrich/models/crm_lead.py 里,show_enrich_button 不是单纯看“有没有邮箱”。
_compute_show_enrich_button() 里至少同时判断了这些条件:
- 线索必须是激活状态
active - 必须有
email_from - 邮箱质量不能是
incorrect - 不能已经
iap_enrich_done - 不能已经有
reveal_id - 不能已经
probability == 100
这很关键。
它说明 Odoo 的 enrich,不是一个“想点就点”的信息查询按钮, 而是一个只对仍在推进中的、还没补过的、邮箱看起来可靠的机会对象开放的动作。
翻成人话:
已经赢单、已经补过、邮箱本身无效,或者记录已经不活跃时,系统默认认为再 enrich 一次的性价比不高。
这也是为什么 enrichment 更像 pipeline 中的“资格补充步骤”,而不是万能资料抓取器。
二、自动触发不是即时补全,而是“创建后唤醒 cron”
很多人以为自动 enrich 是在 create() 里直接调用外部服务。
源码没有这么激进。
在 create() override 里,Odoo 做的是:
- 先正常创建 lead
- 看参数
crm.iap.lead.enrich.setting - 如果是
auto,就去触发crm_iap_enrich.ir_cron_lead_enrichment
注意,是 trigger cron,不是同步请求外部 enrich 服务。
这背后的设计很实用:
- 创建 lead 不会因为外部 enrich 网络波动而变慢
- enrich 可以批量处理,不必每条线索单独打服务
- 后续还能复用 cron 的进度提交、锁控制和异常处理能力
所以业务上更稳的理解应该是:
自动 enrich 不是“录入即补全”,而是“录入后进入一个异步补全过程”。
这和很多人脑补的同步调用完全不是一回事。
三、cron 也不是逮到邮箱就补,它先做了一轮资格筛选
_iap_enrich_leads_cron() 里默认会按这些条件找候选线索:
iap_enrich_done = Falseprobability < 100或为空email_from不为空reveal_id = Falsecreate_date还在延迟窗口内active = True
这里有两个很容易被忽略的点。
1. 这是一个“最近创建 lead”的过程
它不是无限回头扫描历史全库。
enrich_hours_delay 默认 24 小时,本质上是在说:
- 系统更关注刚进入 pipeline 的新线索
- 太老的记录默认不在自动 enrich 的主要处理面里
这非常符合实际。
因为 enrichment 的价值最大的时候,通常正是线索刚入池、还没分配、还没推进的时候。
2. reveal_id 是强保护位
一旦已有 reveal_id,系统会认为这条线索已经拿过 enrich 结果,不再重复处理。
所以如果你做了导入、合并、或其他外部同步,看到 reveal 信息已经存在, 但按钮不再出现、cron 也不再碰它,这完全符合源码设计。
四、真正开始 enrich 前,系统先过滤掉“没必要查”的邮箱
iap_enrich() 里最业务化的一段,不是请求外部接口,而是先决定哪些线索值得送去查。
你会看到两层筛选:
第一层:邮箱能不能规范化
如果 email_normalize() 后还是拿不到可靠邮箱,系统直接:
- 把
iap_enrich_done设为 True - 留一条 note,说明没有可用邮箱
这表示:
没法标准化的邮箱,不会进入 enrich 重试地狱,而是明确记为“这个入口已经处理过,但没有结果”。
第二层:是不是通用邮箱提供商
源码会拆邮箱域名,如果命中 _MAIL_PROVIDERS 里的通用邮箱服务商,就直接跳过 enrich。
比如这类域:
- gmail
- outlook
- yahoo
- 其他公共邮箱服务商
为什么?
因为 Odoo enrich 的目标不是“查个人邮箱主人是谁”,而是根据企业域名推公司信息。
如果邮箱是公共服务商地址,查出来的公司画像通常没有业务意义。
所以这里的策略不是“尽量多查”,而是“先把低价值请求砍掉,省信用、少噪音”。
五、字段写入策略非常保守:优先补空,不轻易覆盖人工值
_iap_enrich_from_response() 是最值得实施顾问细读的地方。
Odoo 并不是把外部返回值整包覆盖进 lead。
它的策略更像:
- 如果
partner_name为空,才写 name - 如果
street/city/zip为空,才补地址 - 如果
phone为空,才取返回的第一个电话 - 如果
country_id/state_id为空,才按代码去映射国家和州
这意味着:
enrich 在默认语义上是“补洞”,不是“校正主数据”。
为什么这点重要?
因为很多团队会误以为 enrich 是更权威的数据源, 但 Odoo 的默认设计并不支持这个结论。
在官方实现里:
- 外部 enrich 结果只是参考
- 人工已经录入的值,优先级更高
- 系统宁可保守,也不轻易替你推翻现有信息
这正是线上系统更稳的做法。
六、批量 enrich 不是“all or nothing”,而是按批提交和回滚
iap_enrich() 对批量处理的事务策略,也很像成熟生产代码。
关键点有三个:
1. 支持批大小
默认 batch_size=50。
它不是一次把全部 self 打出去,而是分批处理。
2. cron 模式下会提交进度
from_cron 时会调用 _commit_progress(),
意味着这个任务被设计成可以被 cron 安全地长时间推进,而不是必须一口气跑完。
3. 非 cron 模式下会按批 commit
即使是手动 enrich,多批处理中每批也可能单独 commit; 遇到异常则回滚当前批,不让整批历史全丢。
这背后其实是在平衡两个东西:
- 外部信用消耗已经发生,不能因为后面一条异常就把前面全抹掉
- 但也不能让某个错误把整轮 enrich 卡死
所以它故意不是纯粹的单事务完美主义,而是偏向生产可恢复性。
七、记录锁是显式考虑过的,不是谁先点谁赢
在批处理中,Odoo 会对待处理 lead 使用 try_lock_for_update(limit=batch_size)。
如果一批记录拿不到锁:
- 系统会打日志
- 并把 cron 往后再触发 5 分钟
这其实说明官方默认预期:
- 有可能有人正在编辑 lead
- 也有可能另一轮作业正在处理同一批数据
- enrichment 不应该因为抢锁失败就把记录写坏
所以 enrich 在并发设计上并不天真。
它不是“我拿到 self 就默认能改”,而是明确接受:
CRM 线索在生产环境里就是一个会被销售、自动分配、合并流程同时触碰的对象。
八、为什么有些线索 enrich 失败后,看起来“以后也不再补了”
这里要分情况看。
情况 1:明确不适合 enrich
比如:
- 邮箱无法规范化
- 命中通用邮箱服务商
- 接口返回 not found
这几种情况下,源码通常会把 iap_enrich_done 设为 True。
语义不是“数据补全成功”,而是:
这条线索的 enrich 尝试已经走完,不需要无意义重试。
情况 2:外部异常
如果是 credits 不足、服务异常、JSONRPC 问题等, 系统更偏向:
- 发通知
- 打日志
- 保留后续继续处理其他批的可能
这时是否重跑,要看异常点和批次进度,不是每次都一刀切。
所以实施上一定别把 iap_enrich_done 理解成“百分百补全成功”,
它更准确的意思是:
这个 enrich 生命周期已经处理过。
九、合并时为什么 enrich 状态也会“继承”
crm_iap_enrich 还 override 了 _merge_get_fields_specific()。
它把 iap_enrich_done 的合并策略写成:
- 只要被合并的几条 lead 里有任意一条为 True
- 合并后的主记录就记为 True
这个细节很重要。
因为它代表官方的判断是:
既然这批重复线索里至少已有一个 enrich 过程跑过,就没必要把 merge 结果当成“全新、从未补全过”的 lead 再来一遍。
这和普通字段简单取主记录值不一样, 它显然是在避免 merge 后重复消耗 enrich 资源。
这也是为什么如果你把“待 enrich lead”与“已 enrich lead”合并后, 新主记录未必还会重新出现 enrich 按钮。
十、实战里最容易踩的 5 个坑
1. 把 enrich 当成主数据主来源
默认实现更像补空策略,不是覆盖策略。
2. 用公共邮箱期待拿到企业画像
官方直接跳过这类邮箱,本来就不是 enrich 的理想输入。
3. 误把 iap_enrich_done 当“补全成功”标志
它更接近“流程已处理”。
4. 以为自动 enrich 是同步执行
实际上是创建后唤醒 cron,再批处理。
5. merge 后还期待再次自动 enrich
合并策略已经尽量避免重复消耗。
最后一句
Odoo CRM 的 lead enrichment,最值得学的地方不在“能查到什么资料”, 而在它的默认哲学非常稳:
只补值得补的 lead,只补值得补的字段,只在合适的事务和并发边界里补。
如果你把它理解成一个保守、异步、补空优先的资料增强步骤, 你对 Odoo CRM 这条链路的很多疑惑都会自然消失。
DISCUSSION
评论区