在日常使用 WhatsApp 进行客户沟通时,联系人重复是一个容易被忽视但影响深远的问题。同一个客户可能以多个号码出现,也可能因为手动导入、扫码添加、群组获取等不同渠道产生重复记录。如果不对这些重复数据进行治理,后续的标签、分组、群发和 CRM 分析都会失真。本文结合 WADesk 多账号场景下的实践经验,分享一套联系人去重与合并的技术方案。
一、联系人重复的来源与影响
1.1 重复数据的常见来源
- 多渠道导入:从电子表格、通讯录、CRM 等不同系统导入同一批号码。
- 账号切换:同一客户被多个销售账号分别添加,形成跨账号重复。
- 国际区号差异:号码可能以
+86、0086或无前缀等多种形式存储。 - 格式不一致:空格、横杠、国家码缺失等造成同一号码文本不同。
1.2 对业务的实际影响
重复联系人会直接导致三个问题:
- 群发触达失真:同一人收到多条重复消息,影响体验并增加账号风险。
- 客户画像分裂:沟通记录分散在不同联系人条目下,无法形成完整视图。
- 统计口径混乱:进粉数、活跃客户数等核心指标被高估。
在 WADesk 的客户管理实践中,联系人去重是构建可靠 CRM 数据底座的前提步骤。
二、联系人标准化处理
去重之前,必须先对号码进行标准化清洗。
2.1 号码格式化
importredefnormalize_phone(raw:str,default_cc:str="86")->str:# 移除所有非数字字符digits=re.sub(r"\D","",raw)# 处理国内常见的 0086 / +86 前缀ifdigits.startswith("00"):digits=digits[2:]ifnotdigits.startswith(default_cc):digits=default_cc+digitsreturndigits samples=["+86 138 0013 8000","0086-138-0013-8000","13800138000",]forsinsamples:print(s,"->",normalize_phone(s))输出:
+86 138 0013 8000 -> 8613800138000 0086-138-0013-8000 -> 8613800138000 13800138000 -> 86138001380002.2 姓名与头像的去噪
除了号码,联系人姓名中也可能包含特殊符号、表情或前后空格。建议统一进行以下处理:
- 去除首尾空白和不可见字符
- 对全角半角符号做统一转换
- 使用统一编码,避免中文、英文、数字混合时的排序异常
三、去重策略设计
3.1 精确匹配
最基础的去重方式是基于标准化后的号码做精确匹配。适用于同一客户使用唯一号码的场景。
fromcollectionsimportdefaultdictdefexact_dedup(contacts):seen={}duplicates=defaultdict(list)forcontactincontacts:key=contact["normalized_phone"]ifkeyinseen:duplicates[key].append(contact["id"])else:seen[key]=contact["id"]returnseen,duplicates3.2 模糊匹配
在部分场景下,同一客户可能存在号码相近但不完全一致的情况,例如最后几位输入错误。可以采用编辑距离或号码后缀匹配做辅助判断。
deflevenshtein(a:str,b:str)->int:iflen(a)<len(b):returnlevenshtein(b,a)iflen(b)==0:returnlen(a)prev=list(range(len(b)+1))fori,cainenumerate(a,1):curr=[i]forj,cbinenumerate(b,1):cost=0ifca==cbelse1curr.append(min(curr[j-1]+1,prev[j]+1,prev[j-1]+cost))prev=currreturnprev[-1]defis_similar(phone_a:str,phone_b:str,max_distance:int=2)->bool:returnlevenshtein(phone_a,phone_b)<=max_distance模糊匹配的结果需要人工复核或设置置信度阈值,避免误合并不同客户。
3.3 跨账号去重
在 WADesk 多账号管理中,联系人数据分散在不同账号下。去重时需要引入账号维度,判断同一个号码是否被多个账号持有,从而识别跨账号重复。
defcross_account_dedup(contacts_by_account):global_index=defaultdict(set)foraccount,contactsincontacts_by_account.items():forcincontacts:global_index[c["normalized_phone"]].add(account)return{phone:accountsforphone,accountsinglobal_index.items()iflen(accounts)>1}跨账号重复不意味着一定要合并,但可以作为客户归属分析和销售协同的参考。
四、联系人合并策略
4.1 字段合并优先级
当确认两个联系人属于同一客户后,需要将其合并为一个主记录。合并规则应事先约定:
| 字段 | 合并策略 |
|---|---|
| 号码 | 保留标准化后的主号码 |
| 姓名 | 取最近更新或最长非空值 |
| 标签 | 取并集 |
| 备注 | 拼接并去重 |
| 归属账号 | 保留最初添加的账号或按业务规则指定 |
| 沟通记录 | 关联到主记录,保留时间线 |
4.2 合并示例代码
defmerge_contacts(primary,secondaries):merged=primary.copy()merged["tags"]=list(set(primary.get("tags",[])))forsecinsecondaries:merged["tags"]=list(set(merged["tags"]+sec.get("tags",[])))iflen(sec.get("name",""))>len(merged.get("name","")):merged["name"]=sec["name"]ifsec.get("note"):merged["note"]=f"{merged.get('note','')}\n{sec['note']}".strip()returnmerged4.3 保留原始映射
合并后应保留旧联系人 ID 到新 ID 的映射关系,便于追溯历史记录和回滚操作。
五、去重流程的工程化
5.1 定时任务触发
建议在低峰期执行全量去重任务,例如每日凌晨。对于新增联系人,可以在导入时进行实时去重。
5.2 冲突报告生成
模糊匹配产生的疑似重复应生成待审核列表,由运营或销售人员确认后再合并,避免自动化误操作。
5.3 操作日志记录
每次合并都需要记录:
- 合并时间
- 合并前后的联系人 ID
- 操作来源(自动 / 人工)
- 合并字段的变化明细
六、实践中的几个注意点
- 国际号码处理:不同国家码格式差异大,清洗逻辑需要按国家码分支处理。
- WhatsApp Business 与普通账号:Business 账号可能包含商业资料字段,合并时需额外保留这些信息。
- 封号后的数据保留:被禁账号的联系人数据在本地仍保留 90 天,去重任务需要考虑这部分数据的可见性。
- 避免过度合并:模糊匹配阈值设置过低会把不同客户合并,影响业务,建议采用保守策略。
七、总结
联系人去重不是一次性清理工作,而是需要持续维护的数据治理机制。通过号码标准化、精确匹配、模糊匹配和跨账号去重四层策略,可以在 WhatsApp 多账号场景下建立更干净的客户数据。在 WADesk 的实际应用中,这套机制帮助团队显著降低了重复客户占比,也让后续的标签分层、群发触达和数据分析更加准确。
数据质量决定运营效率。把联系人去重做扎实,是每一位做 WhatsApp 客户管理的技术人都值得投入的一件事。