20 KiB
Map: 客户联系人图谱与联系人页签整节落地(A4-4-2 §6 全量)
wayfinder:map
created: 2026-09-09
Destination
A4-4-2 联系人页签第 6 节需求全量落地并验收:6.4 联系人图谱(职务层级自动布局 + 公司节点 + 编辑器全套 + 持久化)+ 6.1 查询展示(脱敏点击明文 + 查看埋点)+ 6.2 快速添加 + 6.3 页内编辑 + 联系人导入三段式 + 职务/来源字典前置。终点 = 测试环境(ai.itc.vip/crm-api)亲手验收 + 两件套交付(Bruno 增量同步 + e2e-report)。
Notes
范围拍板(chart grill 2026-09-09,四问)
- 目的地 = 全链落地可验收(非 spec 冻结 / 非 POC):图里装执行(task 票串行链,对齐 customer-module / customer-e2e 先例),终点落在测试环境人工验收。
- 随行项全部进图:图谱本体 + 职务二级联动选择器 + 字典前置(job_title 标准职务预置 + contact_source 分组,顺手销 W-01)+ 联系人导入三段式(当初票 06 约定「随图谱 effort」)。
- 列表侧增强也进图(A4-4-2 §6 整节):6.1 查询展示 / 6.2 快速添加 / 6.3 页内编辑。脱敏口径拍板(用户):不搞三档权限——所有人默认脱敏显示,页面点击临时明文,明文查看行为埋点记录(谁/何时/哪个联系人号码)。此拍板替代当初客户 E2E 划出的「三档脱敏」,仅适用于联系人电话,不自动扩展到其他域。
- 验收底盘 = 对齐 customer-e2e 先例全套:API E2E 增量套件 + demo Playwright 冒烟(含图谱编辑器交互)+ Bruno 文档同步 + e2e-report 聚合。
需求权威与底稿
- 原型需求文本全量在案:
.scratch/opportunity-bugfix/lanhu-pages/A4-4-2_客户详情(联系人).md—— u533 = 6.1/6.2/6.3 列表侧需求、u536 = 6.4 联系人图谱 15 条完整需求(生成规则/公司节点/未识别层级/卡片悬停气泡/编辑模式/多父连线/系统边删除持久否决/循环检测/并发冲突/保存重置/验收标准)。判定纪律沿用:原型第一 > spec > 问用户。 - 存量占位:
CustomerContact实体已有job_title_name / job_title_id / job_title_category / job_title_level四列(customer-module D5 留位,注释「图谱用」);customer_module/map.mdD5 = 图谱划出决策原文。 - 导入先例:客户导入三段式(
CustomerImportExecutor:template/preview+confirm/result + Analyzer 预检 + fail 明细 + clip500);联系人导入对齐此形态,复用深度由票 01 定。 - 契约权威:
.scratch/customer-module/API-SUMMARY.md(73 端点);新端点须增量登记并过 ADR-0017 纪律(flat 动词 + 查询参数传 id,禁 @PathVariable/PUT/DELETE/@RequestBody)。 - 前端形态:本仓交付 = 后端 + 单文件 demo(
.scratch/customer-e2e/demo/index.html同款纪律,当前 961 行);真实 Vue 前端另仓另人,demo 即参考实现。
环境事实(2026-09-09 实探)
- 测试环境
https://ai.itc.vip/crm-api:jar 已含 rework+defectfix 最新构建;verify profile debug token 可用(GET /api/auth/debug/token?userId=739564171091247104);数据 mine 15 / pool 3;contact_source 字典 0 项 = W-01(本图销号)。 - 本地起服先例:verify profile +
CRM_MINIO_AK/SK;Maven 增量陷阱(先删 fat jar);E2E 解释器py -X utf8;Playwright.scratch/customer-e2e/.venv10(chromium 已缓存)。 - 新表(边/查看日志/导入表)走
ddl-auto: update自动建(共享库加表不加列,无 DDL 票);注意孤立单大写字母@Column陷阱(AGENTS.md 规则)与 UTF-8 BOM 纪律。 - 跨模块缝合(crm-customer ↔ crm-dict 字典、联系人增强触碰脱敏口径):按惯例 ADR + CONTEXT.md 双文档化(票 03/04 落点)。
默认技能
/prototype— 票 00 已 resolved(原型 = 正式 demo 底座;后续需求微调仍直改 prototype/ 并续记修订)/webapp-testing— 票 00 自检截图 / 票 08 demo Playwright/grilling+/domain-modeling— 票 01 已 resolved(spec.md 冻结);若 spec 需返工重开 grilling 时再用/bruno-sync— 票 09 文档同步(动手前先读 skill 机制 + bruno-sync.config.json)
Decisions so far
-
2026-09-09 改序(用户):先原型后 spec——新票 00 原型 demo(纯前端单文件 HTML + mock 数据,零后端依赖)插到最前,验证对 u533/u536 的需求理解;图谱全量 + 列表侧关键交互都进。验收通过 → 该原型升格为正式 demo 底座(票 06 改为「接真实后端 API」不重写);理解偏差 → 改原型不进代码。票 01 spec 冻结改为「demo 验收后继续」,届时以 demo 为活参照(原型里 mock 实现的规则 = grilling 素材)。
-
2026-09-10 原型第⑥轮验收反馈(用户,票 00):等级换 1–10(10 = 顶层) + 画 10 等级格子区分;等级 10 行在公司上方、1–9 在下方;未设置等级的固定右侧栏;拖动到具体等级格子 = 设置等级(连线到未设等级者改引导拖格子,弹窗定级删除);两点之间只有一条边(无向查重,显式入规则面板);等级锁定:节点只有所有边都取消了才能重新设置等级(含 pending 与公司系统边)。原型已实现并自检 44/44 PASS(0 报错,shots/10-leveldrop.png);衍生待钉点(等级↔字典映射细则、右侧栏持久化语义、定级保存确认)已记入票 01 素材。
-
2026-09-10 原型第⑦轮验收反馈(用户,同日第二轮,票 00):等级锁定规则细化(放宽⑥的全禁口径)——有上级连线时不能调整等级;只有下级连线时可以调整,但不能调整到低于或等于自己最高的下级(公司系统边不计入比较);取消等级(拖回右侧栏)仍须先取消所有连线。原型已实现(tryLevelDrop 重写,edgesOfContact 删除)并自检 49/49 PASS(0 报错);顺带全局纠正⑥落盘笔误「辿」(U+8FBF)→「边」(U+8FB9) ×10。
-
2026-09-10 票 00 resolved(原型验收通过,用户指示「根据原型推进 Map」):7 轮验收反馈全部落地(①定级弹窗 ②连线交互修复 ③值域数值化 ④单父+公司系统边+auto 单父化 ⑤无向连线+一致性清理 ⑥10 等级格子+右侧栏+拖格子定级 ⑦等级调整约束细化),自检 49/49 PASS、零报错;原型升格正式 demo 底座(票 06 接真实 API 不重写)。票 01 解锁为 frontier:已定规则全景 12 条 + 待钉点 5 条灌入票 00 Answer / 票 01 票面;票 01 原「多父 DAG」假设已按修订④ 修正为单父树。见 票 00。
-
2026-09-10 票 02 resolved(字典前置落地):crm-dict 缝合完成——
dict_item.level通用档位列(ADR-0036,档位=图谱等级初始值,业务侧定级永不回写字典)+ job_title 两级树种子 5+9(档位 10/6/3)+ contact_source 4 项(W-01 销号)+ CONTEXT.md「档位」词条。执行拍板:二级兜底 code=other_job(组内 code/名称双重唯一性校验,不能与一级 other 同名同码,spec 已同步);零新端点(复用 tree/enabled-list);起服 HTTP 断言归票 07 E2E(单测已等价覆盖数据源)。crm-dict 64/64 全绿。见 票 02。 -
2026-09-10 票 03 resolved(图谱域后端落地):spec 契约全量实现(ADR-0037)——edge/graph 两张新表(边=无软删快照表,物理删+全量重插)+
level_source列 +graph/detail|save两端点 + 七步校验链 + CAS 版本锁(显式 SQL 非 @Version 插件)。执行拍板四条:错误码顺延 67018/67019/67020(67017 已被战略协议占用);版本行 id PK+唯一索引(BaseEntity 雪花零例外);等级调整约束先于边校验(撤级/调级提示优先于「端点未定级」);首定级豁免 + 撤级 null 走 LambdaUpdateWrapper(MP NOT_NULL 陷阱,同 CustomerOwnershipWriter)。联系人增删联动 bump + 级联清边。crm-customer 198 例全绿(图谱 12 例新集)。见 票 03。 -
2026-09-10 票 04 resolved(联系人列表侧增强落地):reveal/quickAdd/batchEdit/export 四端点 +
customer_contact_reveal_log埋点(只写不管理,D10)+ 全局脱敏口径(ADR-0038:mask 前3+****+后4 出参五处 + isMaskedForm 回显保护 + 导出/port 同口径;checkPhone 维持 D26 不动)。执行拍板:导出全量(spec 冻结优先于票面「当前页勾选」);quickAdd 部分成功 / batchEdit 原子批(补交失败行不死锁);错误码 67021/67022 顺延。基座 update 回显保护(脱敏串提交=不改号)。crm-customer 209 例全绿(增强 11 例新集)。见 票 04。 -
2026-09-10 票 01 resolved(spec 冻结,grilling 12 问全部拍板):产出 spec.md 冻结版——D1–D12 拍板 + T1/T2 技术自定 + 新表 5 张 DDL 草案 + 端点契约 15 个 + 字典种子定稿(job_title 两级树 5+9 / contact_source 4 项,W-01 销号)。最大反转:只有手动边,没有自动边(推翻原型「紧邻更高档」自动生成与删除否决机制;公司边 = 等级 10 派生视图不可删,摘线=降级);等级双值模型(字典=初始值,level_source 区分字典跟随/手工锁定);保存才入库 + 图谱版本乐观锁;明文埋点独立表;联系人导入独立表镜像。demo 修订⑧已预告票 06(接线前先回写原型行为)。四条 fog 全部消解。见 票 01。
-
2026-09-10 票 05 resolved(联系人导入三段式落地):六端点 + Analyzer/Executor(ADR-0039)——
contact_import_task/contact_import_fail独立表镜像客户导入(D11);单 sheet 五列模板(职务/来源=字典 code,值域预检 + 四列联动);SKIP(默认)命中=疑似重复不写入、OVERWRITE 才覆盖;行级部分成功(全失败也 DONE);落库 D1 档位 levelSource=1(UPDATE 职务变化重置手工锁;值全同计 unchanged 不触碰);错误码 67023/67024 顺延(67021/67022 被票 04 占用,第三次连锁)。票面「图谱自动更新引擎」随 spec D6 作废——成功收尾一次 bump 版本即信号。crm-customer 218 例全绿(导入 9 例新集)。见 票 05。 -
2026-09-10 票 06 resolved(demo 接真实 API,原型升格正式 demo):修订⑧先行回写(ADR-0040)——auto 边引擎/否决交互全删(rebuildAutoEdges/autoParentsOf/vetoed tombstone)、公司边派生渲染(edge-company 虚线浅蓝无 × 不可删、摘线=降级、撤级豁免)、层级变化只做 sanitizeManualEdges 清理不生成边(非法边 save 67018 兜底);数据层收敛单一 fetch 层(API_BASE ?api= + apiGet/apiPost/apiDownload + apiEnvelope Result 信封,mock 死代码清零);导入三段式 modal 接五端点(page 为列表分页不在 demo 范围);saveGraph 全量快照 + 67017 重拉重置基准。自检 selfcheck.py 改造:Playwright route 内存后端桩接管 14 端点(含 save CAS/quickAdd 追加/batchEdit 四列联动 bump/导入固定预检,template 钉 GET 405 语义),59 步 0 失败(原 51 步按修订⑧改造 + 新增导入冒烟)。冒烟/对账暴露并修复 3 个原型缺陷:jobFull option value 错绑类别 code(职务变更静默失效)/脱敏回显形态误拦保存(含 * 放行=不改口径)/模板下载 POST→GET 405。CONTEXT.md 无需更新(后端零代码变更)。见 票 06。
-
2026-09-10 票 07 resolved(E2E API 套件全绿):e2e-graph.py(对齐 e2e-incr 模式,G-00~G-06 七流程 + teardown + scan_calls)起服实测 58 checks = 57✅ + 1⚠(D-G1 缺陷记档)+ 0❌,幂等重跑稳定,SCAN 17/17 零缺零余;产物 e2e-graph-checks.json / e2e-graph-specimens.json(17 specimens)。票面随修订⑧适配三条:①图谱读无自动边——断言 D1 字典联动 level {10,6,3,None}+脱敏+bump 信号;②「否决持久化」作废(无自动边即无否决),边 CRUD 按全量终态语义设计(重复边=快照内两条同边、单父违反=快照内一个子两个父,错误码用真实 67018/67019/67020 而非 demo 桩自洽的 67017);③「关系变更日志」适配:graph/save 无专属 oplog(grep 零命中),断言 contact 动作链路 CONTACT_ADD/CONTACT_EDIT「批量编辑联系人【N 条】」+ action 过滤纯度。发现 D-G1 (P2):batchEdit「空串=清空」语义在 Spring 表单绑定下不可达(StringEditor allowEmpty 把空串转 null,Service isBlank→null 分支永不可达,Param 注释与运行时不符)——缺陷验证模式记档不排查,前端清空手机号需 reveal+明文替代。语义分叉记档:导入无手机号=FAIL「手机号必填(导入查重键)」(批量无人在场不可 SUSPECT)vs quickAdd 同条件=疑似重复提示,两口径并存合理。并发冲突用同用户双 session(CAS 按版本不按用户;跨用户实测被 visibleToCurrent 数据可见域拦,团队成员不在口径内)。无后端变更 → 无 ADR、CONTEXT.md 不动。见 票 07。
-
2026-09-10 票 08 resolved(UI Playwright 冒烟全绿):demo_smoke_graph.py(对齐 demo_smoke_r2 模式)——真服务 8080 + 真实夹具(e2c-cgd* 客户 + quickAdd 4 联系人,归属 ADMIN 解耦 BUDDY),票面 6 条按修订⑧适配(公司边无×/否决作废/右侧栏=未设置等级),**40 checks 全 PASS 两轮幂等** + console/page error=0 + 零 PUT/DELETE + 13 张截图 + DB 正门清扫。冒烟路径三件基建经验:①demo 无登录 UI → page.route 反代(urllib 原样转发 + 去 origin/Authorization/host/content-length 头 + 注入 Bearer + 补 CORS:credentials:'include' 下 ACAO 不能是 *,须回显 origin + Allow-Credentials:true);②**Chromium/Playwright 的 post_data_buffer 不含上传文件内容**(572B 头部 vs 4.6KB xlsx,文件只回引用),route.fetch/urllib 转发均丢 file part → 67023「请选择导入文件」——upload 请求分流为 Python 侧重读同一 xlsx 直调真实 API,真实预检结果 fulfill 给页面渲染(confirm/result/failures 无文件请求继续走转发);③demo「保存全部」后保持页内编辑态(listEditing=true,单元格仍 input),reveal/导出断言前须先退出编辑;视口 1720×1080 保证图谱卡片在视口内(否则 bounding_box 坐标落视口外鼠标拖拽无效)。交互覆盖:真实鼠标拖卡位移/.dot-bottom 拖线(tryConnect 自动定向)+× 删边、重置确认/未保存三选(继续编辑+放弃修改)/完成绘制退出(保存修改)三弹窗、悬停气泡、页内编辑撤销+保存(DB 取证)、quickAdd 前端查重→ack→服务端部分成功 2 成 1 败、脱敏明文+reveal_log 埋点 DB 取证、模板/导出下载、导入三段式(预检 FAIL 行→确认轮询→失败明细→好行入库坏行未入)。无后端变更 → 无 ADR、CONTEXT.md 不动。见 票 08。
-
2026-09-10 票 09 resolved(Bruno 同步 + 报告聚合 + 发版移交,destination 达成,Map 收官):①Bruno 增量同步——12 个新 .bru 全部 generated 通道落
D:\code\crm-api-docs\A4 客户管理\客户详情\三新子目录(联系人图谱 2/联系人 4/联系人导入 6;票面预估 11,源码导入组含 /page 实为 12,结构听源码);目录归属按源码类级 @Tag(A4-4-2 客户详情页联系人区)而非票面原述「联系人页签」,与独立菜单「A4 客户管理/联系人」旧端点分属不同目录,页面归属语义正确;示例值=票 07 specimens 实测真值(9 端点 JSON 逐字段真值 + export/template 二进制流文字说明);verify 对账 12/12 PASS;git 未动留用户审查。②报告聚合——e2e-report.md 八章终稿(spec 逐条三档 13✅+1⚠记录性 T2/缺陷 D-G1 P2 台账/双量尺覆盖对账/W-01 销号四节点链/API-SUMMARY 增量登记零漂移:§1 补 67018–67024 + 新 §2.7b 12 端点)。③发版移交——jar 重建 BUILD SUCCESS(首跑 clean 失败 = 本地 8080 JVM 锁 jar,停服释放;-rf :crm-app中段 resume 从本地仓库拉陈旧依赖报错——同-pl单模块坑族,全反应堆重跑 3:05);探针发版前基线已取(contact_source 0 项 = 测试环境旧 jar 铁证;新 jar 启动 ddl-auto 建表 + DictDataInitializer 自动补种,无需手工迁移);服务器三步(scp jar → restart.sh → 探针复验)需 SSH 凭据 + 亲手验收清单随 e2e-report.md §八移交用户。工具坑:Write 不能出 workspace → bruno-out 副本 + python 复制;verify 脚本首版 regex^无 MULTILINE 错报 0/12,逐行 match 后 12/12。见 票 09。
Not yet specified
- (2026-09-10 票 01 spec 冻结后四条全部消解:分档映射→D3 三档制 10/6/3;自动边增量语义→随 D6「只有手动边」作废;导入表复用→D11 独立表镜像;规模上限→T2 记录性预案。后续新 fog 逐票追加于此。)
Out of scope
- 节点展开/折叠、联系人搜索与节点定位 — 原型 6.4 第 6 条明说「本期暂不支持」。
- 跨客户人脉关系 — 原型 6.4 第 1 条明说「不展示跨客户关系」。
- OCR 快捷新增 — customer-module D5 划出,未随行进图(按钮占位照旧)。
- 商机域 A5-1-2-2「生态关系图谱」 — 商机域独立特性,与本图无关。
- 真实 Vue 前端仓实现 — demo 即前端参考实现;Vue 仓由前端团队按 Bruno 文档另做。
- 其他域的脱敏策略 — Q3 拍板仅限联系人电话;三档脱敏原决策不复活。
票与依赖图
00 (task: 原型 demo 验证理解) ─→ 01 (grilling: spec 冻结,以 demo 为活参照) ─┬→ 02 (task: 字典前置) ─┬→ 03 (task: 图谱后端) ─┐
│ ├→ 04 (task: 联系人增强后端) ─┼→ 06 (task: demo 接真实 API) ─┐
│ └→ 05 (task: 联系人导入) ────┤ ├→ 08 (task: UI Playwright) ─┐
│ └→ 07 (task: E2E API 套件) ┘ ├→ 09 (task: Bruno+报告)
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ (07 也是 08/09 前置)
Frontier:无——destination 已达成(2026-09-10 票 09 resolved,Map 收官)。全链 00→09 十票全 resolved。发版移交余项(服务器三步需 SSH 凭据 + 用户亲手验收清单)见 e2e-report.md §八。