# 02 — 客户主表 CRUD + 查重校验 **What to build:** 客户主数据能新增/编辑/查询/分页,且新增/编辑时内建三层查重校验(名称相似提示、信用代码唯一硬拦、并发兜底)。这是全模块的扇出底座——字段定义、DataScope、索引、查重端口都会被后续票复用。 **Blocked by:** 01 — 客户模块骨架 **Status:** ready-for-agent **Spec 依据(决策细节详见 .scratch/customer-module/issues/02-answer.md 与 04-answer.md)**:票 02 全字段清单 + 票 04 三层查重口径。本票只做主表实体与查重,**不含**联系人(票 03)、视图/归属(票 04)、详情页(票 06)。 - [ ] `customer` 实体全字段(票 02-answer §A1),含 `@DataScope(module="customer", ownerColumn="owner_user_id", deptColumn="owner_dept_id")`;系统字段继承 `BaseEntity`;`deleted` 默认 0 且**一期仅系统在合并时置 1**(§5.3 拍板 A,不开放手动删除/无前端删除入口) - [ ] `@Table` 索引全部建好(票 02 A5):`uk_unified_credit_code`(unified_credit_code, MySQL NULL 不参与唯一约束) / `idx_owner_stage` / `idx_dept_stage` / `idx_name`(customer_name, FULLTEXT ngram token_size=2,供相似查重) / `idx_parent` / `idx_last_follow` - [ ] 显式 `@Column(name=...)` 检查:票 02 §A2 结论为**全表无需**——所有字段均完整词+完整词,无孤立单大写陷阱,按清单复核后落实体 - [ ] CRUD 契约:`POST /api/customer`(新增)/ `PUT /api/customer/{id}`(编辑)/ `GET /api/customer/{id}`(详情带出)/ `GET /api/customer/page`(分页)。编辑走 CAS 乐观锁 - [ ] **三层查重**(票 04 §B1):L1 名称停输防抖(~300ms)相似提示(只提示不阻止,无权命中脱敏为"存在匹配记录");L2 保存时信用代码精确一致→硬拦禁止 + 名称相似→弹窗"仍要创建/返回修改";L3 仍要创建后写库前再查一次(信用代码+名称)仍冲突才写,DB UNIQUE 兜底 - [ ] `CustomerNameSimilarityPort` 出站端口 seam(票 04 §B2):契约稳定、实现可插拔;一期最简实现=MySQL `MATCH(customer_name) AGAINST(?)` 取 topN + 判定"相似";阈值从 crm-rule「客户查重设置」读取(70/80/90%,非写死) - [ ] 信用代码校验(票 04 §B3):格式 `^[0-9A-Z]{18}$`;精确相等→硬拦;命中无权查看的客户只提示"系统已存在该企业",不显 name/编号/负责人 - [ ] 新增时初始化:`customer_stage`=1(潜在)、`archive_status`=1(有效);若直接指派负责人,`last_valid_follow_time`=create_time;若进公海,`last_valid_follow_time`=NULL(票 10 锚点语义) - [ ] 服务层集成测试(H2 MySQL 模式):新增/编辑/查重三层的正常与冲突路径全覆盖