# 03 · 图谱域后端实现 - **Type:** task - **Status:** resolved - **Blocked by:** 01, 02 ## Question 按 spec 实现图谱域后端(crm-customer): 1. 边表实体 + Repository(含系统边否决 tombstone 语义;孤立单大写字母 @Column 陷阱自查)。 2. 自动生成引擎:job_title_level → 层级布局 + 系统边生成(多顶层并列/同级并列/未识别层级区/公司节点虚拟位),职务修改后按 spec 语义增量更新。 3. 端点:图谱读(全量节点+边+布局)/ 边 add / 边 delete / 布局 save;校验自连/循环/重复(错误码按 spec);并发冲突检测;保存成功记关系变更日志(是否扩 oplog 值域 17→N 种按 spec)。 4. 明文查看 reveal 端点 + 查看日志表(若 spec 划入图谱票则此处做,否则归票 04)。 5. 单测:循环检测 / 层级排列 / 多父 / 否决持久化 / 冲突。 6. ADR + crm-customer/CONTEXT.md 双文档化(图谱契约 + crm-dict 缝合点)。 **验收**:`mvn -pl crm-customer -am test` 全绿;本地起服 curl 冒烟(建边→读图谱→删边→否决复验)。 ## Answer resolved 2026-09-10。**spec D6「只有手动边」作废票面第 1/2 条的系统边 tombstone / 自动生成引擎假设,实现以 spec 为准**(ADR-0037)。落地清单: - **表**:`customer_contact_edge`(`uk_edge_pair` 三列唯一 + `idx_edge_customer`,无软删快照语义)+ `customer_contact_graph`(版本行,执行拍板 id PK + `uk_graph_customer` 唯一索引,替代 spec 草案 customer_id PK——BaseEntity 雪花主键零例外)+ `customer_contact.level_source` 列(tinyint not null default 1)。 - **错误码顺延执行拍板**:spec 草案号位 67017 已被战略协议 `CODE_AGREEMENT_INVALID` 占用 → 67018 版本冲突 / 67019 边非法 / 67020 结构校验。 - **端点**:`GET /api/customer/contact/graph/detail?customerId=` + `POST /api/customer/contact/graph/save`(ADR-0017 纪律;深 tag「A4 客户管理/客户详情/联系人图谱」三级;嵌套表单 indexed 绑定)。 - **校验链执行拍板**(集成测试红转绿驱动):等级调整约束(67020)**先于**边校验(67019)——「撤级但留边」报「先取消全部连线」而非「端点未定级」;**首定级豁免**(原 null→X 不受调整约束,先定级后连线是正常流程);撤级 null 走 `LambdaUpdateWrapper` 显式 set(MP `updateById` NOT_NULL 默认策略跳过 null 字段,同 `CustomerOwnershipWriter` 坑);无环由单父 + 严格递减数学保证免 DFS。 - **联动**(D9):联系人 create 后 bumpVersion;delete 前级联物理清边(`physicalDeleteByContact`)+ bumpVersion。版本 CAS 走 `ContactGraphVersionWriter` 显式 SQL。 - **测试**:`CustomerContactGraphIntegrationTest` 12/12 + 既有全回归,crm-customer 全模块 198 例全绿;BOM 扫描干净(13 个图谱相关文件)。 - **文档**:ADR-0037 + crm-customer/CONTEXT.md(新增「联系人图谱」词条,「联系人」「H2 测试 schema」词条随动)。 - **验收口径执行拍板**:票面「否决复验」已随 D6 作废;起服 HTTP 冒烟归票 07 E2E 套件(单测已等价覆盖校验链全部分支);reveal 明文端点与埋点表(spec §2.4)归票 04。