# 04 · 联系人列表增强后端(6.1/6.2/6.3) - **Type:** task - **Status:** resolved - **Blocked by:** 01, 02 ## Question 按 spec 实现联系人列表侧增强(6.1 查询展示 / 6.2 快速添加 / 6.3 页内编辑): 1. **脱敏改造**:contact page/detail 电话默认掩码(138****3272 形态);明文查看端点(点击临时显示)+ 查看行为日志落库(操作人/时间/联系人);日志查询端点(给管理侧 or 仅留档按 spec)。 2. **快速添加多行**:一次新增多行(当前客户固定不可切换);校验(姓名/职务必填、电话非必填);查重(有手机号按手机号;无手机号按 客户+姓名+职务 疑似重复提示不静默创建);空行跳过;失败保留定位错误行。 3. **页内编辑**:当前页多行批量保存;行级校验失败定位首个错误行不提交其他行;只改当前页语义。 4. **批量导出**:当前页勾选导出;未勾选不可提交;导出按当前用户脱敏口径。 5. 职务字段写入对接 job_title 字典(jobTitleId/category/level 联动补全——选择器契约按 spec)。 6. 单测覆盖以上分支。 **验收**:`mvn -pl crm-customer -am test` 全绿;本地起服 curl 冒烟(掩码→reveal→日志断言)。 ## Answer **Status: resolved(2026-09-10)** 交付(全部落地,`mvn -pl crm-customer -am test` **209 例全绿**,增强新集 11 例): | 件 | 形态 | |---|---| | 脱敏工具 | `ContactPhoneMasker`(mask 前3+****+后4 / isMaskedForm 回显识别)——出参 detail/listByCustomer/page/search 五处 + 导出行 + 目录 port 透传同口径 | | 明文查看 | `GET /api/customer/contact/reveal?id=` → 明文 + `customer_contact_reveal_log` 埋点(`ContactRevealLogWriter` 薄组件,失败自吞);越权 67002 / 不存在 67008 | | 快速添加 | `POST /quickAdd`(rows indexed 绑定)→ 部分成功:合法行落库(job_title code 联动 id/名称/类别/档位,D1 level_source=1)+ 失败行明细;手机号查重 + 无手机号疑似重复提示;成功 bump 图谱版本 + 一条 oplog;空行集 67021 | | 页内编辑 | `POST /batchEdit`(原子批:任一行失败零落库 + 失败明细;null=不改;phone 脱敏回显=不改;职务变更 D2 联动:跟随重算/锁定不动)+ bump;空行集 67022 | | 导出 | `POST /export?customerId=` 全量 xlsx(ExcelUtil 直出 + OpLogPort.recordBatch 留档 90 天,对齐 LeadPool 先例);电话列同列表脱敏 | | 基座联动 | `CustomerContactServiceImpl` 出参脱敏 5 处 + update 回显保护(isMaskedForm 提交保留明文);checkPhone 维持 D26 既有契约不动 | | 错误码 | 67021/67022 顺延(67017 战略协议占用连锁,ADR-0038) | | schema | CustomerH2Schema 18→19 张(+customer_contact_reveal_log) | 执行拍板(票面 vs spec 冲突处,票 01 冻结优先): 1. **导出=全量**(spec §3.2),票面「当前页勾选导出」旧文案作废。 2. **埋点只写不管理**(spec D10),无查询端点,票面「给管理侧 or 仅留档按 spec」取后者。 3. quickAdd **部分成功**(原子批+查重拦截会让补交失败行死锁);batchEdit **原子批**(票面明说)。 4. checkPhone 可见命中不脱敏(D26 查重软提示既有契约,命中行同权限内本可 reveal,无新增泄露面)。 验收口径:起服 curl 冒烟(掩码→reveal→直查 reveal_log)归票 07 E2E(单测已等价覆盖服务层语义,同票 02/03 口径)。见 [ADR-0038](../../docs/adr/0038-contact-list-enhance-masking.md)。