恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

HarmonyOS 7 ArkTS:通讯录号码归一与重复联系人合并

  • 首页
  • 资讯中心
  • /
  • HarmonyOS 7 ArkTS:通讯录号码归一与重复联系人合并

相关资讯

基于关键词的数据采集、数据分析案例! 2026/10/8 7:16:26
2048无线小游戏 2026/10/8 7:16:25
深入理解HTTP/2帧机制:用hyperframe解析与排障实战 2026/10/8 7:11:25

最新资讯

SCHUNK SVH五指灵巧手:从硬件拆解到实战部署全解析
嵌入式软件动态测试(十二)——API集成测试:Postman、RestAssured与Karate的契约验证与端到端测试
用Snowflake原生能力搞定机器学习生产部署全流程
电动汽车销量数据分析与可视化大屏实战:从数据清洗到ECharts展示
PADS VX2.7 Router约束驱动布线原理与实战避坑指南
C++ USB通信上位机开发实战:从驱动选型到libusb排错

今日推荐

context-mode实战指南:从全量塞入到结构化裁剪与检索增强
大模型对话上下文管理实战:三种模式与Token优化
抖音用户主页视频数据爬虫详解:点赞、收藏、分享字段抓取与 TaoToken 统一 Key 配置

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

HarmonyOS 7 ArkTS:通讯录号码归一与重复联系人合并

发布时间:2026/10/8 7:16:26
HarmonyOS 7 ArkTS:通讯录号码归一与重复联系人合并 通讯录里的号码看起来只是字符串真正导入业务库以后却会变成一组很难收口的数据138 0013 8000、86 13800138000、0086-13800138000可能属于同一个人020-88886666又必须依赖地区才能解释。本文把这次“联系人批量导入”做成一个可以复测的小工具重点不在输入框而在号码解析、地区切换、去重依据和错误行的可追踪性。一、上线前一天重复联系人突然翻了三倍Demo 叫Number Tidy。需求原本很轻用户从 CSV 粘贴一批姓名与电话页面清洗格式确认后写入客户库。测试数据只有中国大陆手机号一切正常接入真实数据后运营发现同一客户被拆成了三条海外号码还有一批被判成“位数错误”。第一次排查时我把注意力放在空格和短横线上写了一个正则把非数字字符全部删掉。这个办法对138-0013-8000有效却把44 20 7946 0958的国家码语义抹掉也无法判断020 7946 0958到底是广州固话还是英国本地号。更麻烦的是号码清洗后如果只保留数字前导00、本地拨号前缀和分机都会混在一起。这不是“多写几个正则”能解决的问题。我最终引入libphonenumber-js但没有让页面直接依赖库返回对象而是在中间加了一层稳定的数据合同原始值用于审计规范值用于保存E.164 用于去重展示值用于 UI错误码用于定位。这样即使以后替换解析库页面和数据库也不用跟着推倒重来。本次固定的运行数据如下批次 ID 为PHONE-1002默认地区CN共导入 12 行最终 8 行有效、2 行合并、2 行需要人工确认。手机图和 DevEco Studio 图都沿用这组数据。二、先把“一个号码”拆成五种含义libphonenumber-js可以根据国家码或默认地区解析号码但工程里最容易犯的错是把解析成功等同于业务可用。解析器能识别结构不代表号码一定在当前产品允许的地区也不代表它适合作为登录账号。Number Tidy 因此把结果分成VALID、REVIEW、INVALID三类。当前问题是页面需要一个稳定的行模型不能把第三方库对象塞进State。库对象包含方法升级版本后行为也可能变化这里先把解析结果投影成纯数据。import{parsePhoneNumberFromString,CountryCode}fromlibphonenumber-jsexporttypeRowStateVALID|REVIEW|INVALIDexportinterfacePhoneRow{rowNo:numbername:stringraw:stringe164:stringdisplay:stringcountry:stringextension:stringstate:RowState reason:string}exportfunctionnormalizePhone(rowNo:number,name:string,raw:string,region:CountryCode):PhoneRow{constsourceraw.trim()constparsedparsePhoneNumberFromString(source,region)if(!parsed){return{rowNo,name,raw,e164:,display:raw,country:,extension:,state:INVALID,reason:PARSE_FAILED}}constvalidparsed.isValid()return{rowNo,name,raw,e164:valid?parsed.number:,display:parsed.formatInternational(),country:parsed.country??,extension:parsed.ext??,state:valid?VALID:REVIEW,reason:valid?OK:NUMBER_NOT_VALID}}这段代码解决的是“第三方对象如何进入应用状态”的问题。输入0086-13800138000时解析结果会变成稳定的8613800138000页面展示国际格式数据库使用 E.164原字符串仍然保留。方法在用户粘贴数据、切换默认地区或点击“重新校验”时执行。需要留意两个边界。第一isPossible()只检查长度和结构isValid()才按号码规划进一步判断正式项目可以把 possible 但 invalid 的号码放入人工确认而不是直接删除。第二分机不能混进 E.164 去重键。当前 Demo 保留extension但客户库是否允许同一主号对应多个分机要由产品规则决定。函数本身不持有资源不需要释放真正的风险是重复调用造成旧批次覆盖新批次所以后面会加批次代次。三、地区切换不是换一个下拉框测试人员把默认地区从CN切到GB后页面上的中国手机号被重新解释几百毫秒内又切回CN偶尔会看到统计数字先显示 6 条有效再跳成 8 条。这不是解析库慢而是我最初把每次重算都丢进异步任务旧任务回来后仍然写 UI。当前问题是一次地区切换会触发整批重算必须阻止过期结果提交。下面用epoch给每轮计算编号并保留原始行作为唯一输入源。interfaceRawContact{name:string;phone:string}exportclassNormalizeSession{privateepoch:number0asyncrebuild(source:RawContact[],region:CountryCode):PromisePhoneRow[]{constminethis.epochconstrowsawaitPromise.resolve(source.map((item,index)normalizePhone(index1,item.name,item.phone,region)))if(mine!this.epoch){thrownewError(STALE_NORMALIZE_RESULT)}returnrows}invalidate():void{this.epoch}}epoch不是为了让计算更快而是让状态变化可解释。用户切到GB时代次从 3 变成 4马上切回CN时变成 5即使第 4 轮最后完成也只能抛出STALE_NORMALIZE_RESULT不能碰页面状态。页面只在第 5 轮提交时把状态从CHECKING改成READY。这个类应跟随页面或 ViewModel 生命周期创建。aboutToDisappear()中调用invalidate()可以阻止页面离开后的回写。它不取消已经开始的纯计算正式项目如果一次导入几万行应把计算放入 TaskPool并在分片之间检查取消标记。还要避免在build()中直接调用rebuild()否则每次状态更新都会再次触发计算形成循环。四、去重键要稳定也要承认业务例外仅靠 E.164 去重仍然不够。两位联系人可能共享公司总机分机不同同一个原始号码也可能在不同地区下解析成不同 E.164。Number Tidy 的规则是country e164 extension形成候选键主号码相同且分机为空时自动合并分机不同则进入人工确认。合并时不静默覆盖姓名而是保存来源行号。当前问题是合并过程既要确定性又要把冲突留给用户看。下面的聚合器按输入顺序保留第一条并把后续来源挂到mergedFrom。exportinterfaceContactCandidateextendsPhoneRow{mergedFrom:number[]}exportfunctionmergeRows(rows:PhoneRow[]):ContactCandidate[]{constbucketnewMapstring,ContactCandidate()constresult:ContactCandidate[][]rows.forEach((row:PhoneRow){if(row.state!VALID){result.push({...row,mergedFrom:[]})return}constkey${row.country}|${row.e164}|${row.extension}constexistedbucket.get(key)if(!existed){constcandidate:ContactCandidate{...row,mergedFrom:[]}bucket.set(key,candidate)result.push(candidate)return}existed.mergedFrom[...existed.mergedFrom,row.rowNo]existed.reasonDUPLICATE_MERGED})returnresult}数据经过这里以后12 行不会简单变成 10 行页面仍能从mergedFrom看见第 4、9 行被合并审计日志也能记录“谁被合并到谁”。Map 只活在函数内部调用结束即可回收如果将它提升为单例缓存必须在新批次开始时清空否则不同导入批次会串数据。正式项目还要补两类例外。一类是家庭共享号码不能因为号码相同就合并身份另一类是企业总机分机字段可能藏在备注中。本 Demo 的自动合并只是候选层最终落库仍要让业务主键和用户确认参与。换句话说号码归一化可以提供稳定证据但不应该越权替代客户身份判断。五、页面只提交“本轮确认过”的结果把解析和合并拆开以后UI 反而简单了。页面只维护原始数据、默认地区、当前批次状态和结果列表保存按钮在CHECKING与SAVING时禁用。为了避免快速点击两次写入客户库我又加了保存令牌。当前问题不是按钮有没有防抖动画而是同一批次只能有一个写事务。下面的页面方法用批次 ID 和本地门闩保证幂等。EntryComponentstruct NumberTidyPage{Stateprivateregion:CountryCodeCNStateprivatephase:stringREADYStateprivaterows:ContactCandidate[][]privatesession:NormalizeSessionnewNormalizeSession()privatesaving:booleanfalseprivatereadonlybatchId:stringPHONE-1002privateasyncsaveBatch():Promisevoid{if(this.saving||this.phase!READY)returnthis.savingtruethis.phaseSAVINGtry{constacceptedthis.rows.filter(itemitem.stateVALID)awaitContactRepository.commitOnce(this.batchId,accepted)this.phaseCOMPLETED}catch(err){this.phaseSAVE_FAILED}finally{this.savingfalse}}aboutToDisappear():void{this.session.invalidate()}}方法只在用户明确点击“保存 8 条”时执行。saving拦截同一页面内的重复点击commitOnce(batchId)则负责跨页面、跨重试幂等两者不能互相替代。保存失败时保留已校验结果用户可以重试不必重新解析 12 行。当前 Demo 用内存仓库演示正式项目应让commitOnce落在关系数据库事务里并给批次 ID 建唯一索引。页面销毁并不意味着后端写事务可以随意取消如果提交已经到达持久层应查询批次状态而不是重新提交一遍。第三方库也应该锁定版本并纳入 ohpm/构建快照避免元数据升级导致同一号码在不同客户端上得到不同判定。六、在 DevEco Studio 里盯住三条日志项目目录按职责拆成pages/NumberTidyPage.ets、model/PhoneRow.ets、service/NormalizeSession.ets、service/ContactRepository.ets和utils/PhoneNormalizer.ets。我没有把解析逻辑塞进页面因为地区重算、批次导入和单行编辑都会复用它。调试时最有价值的三条日志是Batch PHONE-1002 rows12 regionCN、Valid8 merged2 review2、State CHECKING - READY epoch5。它们分别回答输入是不是同一批、输出有没有变化、最终是哪一轮结果获得提交权。截图里中间代码停在normalizePhone()右侧模拟器显示 8 条有效、2 条合并、2 条待确认底部 HiLog 与正文的数据一致。若线上只看到“解析失败”却没有批次、地区和行号日志几乎无法复盘。因此我把敏感原号码排除出日志只记录rowNo、错误码和号码后四位的散列摘要。七、手机端最终结果与边界最终页面没有把所有联系人铺满一屏而是把技术判断放在首屏批次PHONE-1002、默认地区CN、状态READY、有效 8、合并 2、待确认 2。下方列出一条 E.164 规范化结果和一条NUMBER_NOT_VALID让用户知道“待确认”不是网络错误。这次处理后最关键的变化不是重复数从 3 倍降下来而是每条号码都有了可解释的状态。地区切换不会再让旧结果覆盖新结果保存按钮也不会制造重复事务。仍有三条边界需要明确号码有效不代表号码真实存在同号不一定是同一个人第三方元数据升级可能改变边缘号码的判定。工程上要做的是保存原始输入、锁定依赖版本、记录解析地区和规则版本并允许人工修正。这样 Number Tidy 才是一条能回放的数据处理链而不是一个看起来很聪明的正则输入框。八、结语在 HarmonyOS 页面里接入libphonenumber-js并不难难的是决定什么可以自动化、什么必须保留证据。我的最终取舍是解析器负责结构化E.164 负责候选去重epoch 负责异步结果所有权批次 ID 负责持久化幂等人工确认负责业务例外。这套拆分也适用于邮箱、地址、证件号等批量清洗场景。先建立稳定的数据合同再让第三方库提供能力页面就不会被库版本和临时规则牵着走。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号