# 客户 Demo · 核心域字段逐入口对齐表(_fa-core) > 用途:`.scratch/customer-demo-ref/demo/index.html` 的冒烟断言 + 人工验收双用。入口编号对齐 `assets/coverage-matrix.md`(缺口动作≠豁免)。 > 契约权威优先级:**Bruno `.bru` 出参示例 > DTO/Param 源码 > API-SUMMARY(下称 AS,`.scratch/customer-module/API-SUMMARY.md`)**。字段名逐字取自源码/`.bru`;「示例」列取 `.bru` E2E 实测真值或任务书 seed。 > **envelope**:统一 `Result = {code, success, message, data}`(code=0 成功,错误码见 AS §1);本表只写 `data` 内字段。 ## 通用口径(各入口共用,正文不再重复) - **分页请求**(`BaseParam`,crm-base):`current`(默认 1)/ `size`(默认 10,上限 500)/ `keyword` / `orderBy` / `asc`(默认 false=倒序)。 - **分页出参**(`PageResult`):`content[]` / `total` / `size` / `current` / `pages` / `empty`(=total==0)。 - **Long 序列化**:雪花 id 与 Long 计数 JSON 序列化为**字符串**(E2E 实测 `"opportunityCount":"0"`、`"total":"5"`);前端勿做 Number 运算,判空按 `=== null` / `!= null`。 - **时间格式**:LocalDateTime `yyyy-MM-dd HH:mm:ss`;LocalDate `yyyy-MM-dd`;表单提交 LocalDateTime 用 ISO `T` 分隔(空格分隔报 400,见 #40 follow/add)。 - **空值"--"口径**:demo 渲染 `value || '--'`(grid 兜底,旧 demo L1124-1135 同);契约 null 字段显 `--`,不显 0/空串。 - **脱敏总述**:核心域唯一脱敏位=查重两 DTO 的 `masked` 布尔标志——无权命中时 `customerId/customerName/ownerName/customerNo` 等**业务字段不透出(null)+ masked=true**,前端只渲染「存在匹配记录」(check-name)/「系统已存在该企业」(check-credit-code)。客户列表/详情 V1 **无字段级脱敏**:`unifiedCreditCode`、`presidentClassPhone` 等明文回显(E2E 实测 `"13800000001"`;敏感信息三档脱敏定稿划出,AS §0/§5.4)。联系人电话脱敏 + `contact/reveal` 明文端点属 contact 域,不在本表。 - **冒烟 seed**(e2e 种子库):admin `userId=739564171091247104`(罗伟健);客户 `750844477165273088`(e2c-全字段-科技,编号 KH202609030010,负责人=admin);公海客户 `750844487390986240`(e2c-公海-甲,无主);联系人 `750844500171030528`(张关键)。 --- ### #1 workspace 列表分页 — `POST /api/customer/workspace/page`(mine/overview/pool 三 workspace 共用一份契约) - 出处:`A4 客户管理\我的客户\workspace 列表分页.bru`(客户总览/客户公海同名 .bru 各一件)+ `crm-customer/.../domain/param/CustomerWorkspacePageParam.java` + `domain/dto/CustomerListRowDTO.java`(出参 SQL 见 `mapper/CustomerMapper.java` pageWorkspace) - 数据范围:overview=裸表+`@DataScope`(管理员可见全部);mine=负责人**或**协同人(子查询包裹);pool=owner 空且按部门可见范围(子查询包裹)。 **请求参数**(form 表单绑定;含 BaseParam 五参): | 名 | 类型 | 必填 | 示例 | 口径 | |---|---|---|---|---| | workspace | string | 是 | `mine` | `mine`/`overview`/`pool`;未知值 67001「未知 workspace」 | | viewType | string | 否 | `ASSIGNED` | 内置五视图(硬编码不落库),空=基础集;适配矩阵见下 | | savedViewId | string | 否 | | 叠加自定义视图(#7)检索条件于数据集之上,不改 @DataScope 边界;**RECENT 视图不接**;视图不存在静默不叠加;**board/cards 不接** | | archiveStatus | number | 否 | `1` | 1 有效 / 2 已归档;默认 1(D19 归档=删除语义) | | customerStage | number | 否 | `1` | 1 潜在 / 2 重潜 / 3 已成交 | | customerType | string | 否 | `customer_type_01` | customer_type 字典 code | | customerStarLevel | number | 否 | `3` | 1~5 | | relationStarLevel | number | 否 | `3` | 1~5 | | industryCode | string | 否 | `gov` | 行业一级 code | | provinceCode | string | 否 | `440000` | 省国标 code | | groupColumn | string | 否 | `stage` | 仅看板两端点有意义:白名单 `stage`/`star`/`relation`,越界静默忽略 | | groupValue | number | 否 | `1` | 仅 board/cards(与 groupColumn 配对) | | current / size / keyword / orderBy / asc | — | 否 | `1` / `50` / `恒信达` | BaseParam;keyword 按名称/编号等业务列模糊 | **viewType 内置五视图 × workspace 适配矩阵**(越界视图不硬拒,SQL 自然空集;F7 口径:ownerOnly 等布尔未做,单 viewType 参数承载五视图): | viewType | 文案 | mine | overview | pool | |---|---|---|---|---| | ASSIGNED | 我负责的 | ✅ | ✅ | ❌ | | COLLABORATING | 我协同的 | ✅ | ✅ | ❌ | | FOLLOW_UP_DUE | 待跟进(next_follow_time 到期未跟) | ✅ | ❌ | ❌ | | FOCUSED | 我关注的 | ✅ | ✅ | ✅ | | RECENT | 最近访问(customer_view_log 倒序锚点) | ✅ | ✅ | ✅(不接 saved-view) | **出参**(`PageResult`,data 内 24 字段;示例值=E2E 公海行 + mine 行): | 字段路径 | 类型 | 口径/文案 | 脱敏 | 示例 | |---|---|---|---|---| | content[].id | Long→str | 客户 ID | - | `"750844487390986240"` | | content[].customerNo | string | 客户编号 KH+yyyyMMdd+4 位 | - | `"KH202609030020"` | | content[].customerName | string | 客户名称(固定列不可隐藏) | - | `"e2c-公海-甲"` | | content[].customerType | string | 字典 code(前端字典缓存翻译,**后端不回显名**) | - | `"customer_type_01"` | | content[].provinceCode / cityCode / districtCode | string | 国标 code(名称前端字典渲染) | - | `"440000"` / `"440100"` / `"440103"` | | content[].industryCode / industryChildCode | string | 行业一级/二级 code | - | `"gov"` / `"gov_1"` | | content[].customerStage | number | 1 潜在 2 重潜 3 已成交 | - | `1` | | content[].customerStarLevel / relationStarLevel | number | 1~5 | - | `3` / `3` | | content[].strategicAgreementLevel | number | 战略协议等级快照,null=未签 | - | `null` | | content[].vipCustomerLevel | number | 已成交 VIP 客户等级快照,null=无 | - | `null` | | content[].opportunityCount | Long→str | **真算**:SQL 标量子查询 `opportunity_customer join opportunity`(oc.delete_key=0 且 o.deleted=0) | - | `"0"` | | content[].projectCount | Long→str | **恒空**:SQL `SELECT 0 AS project_count`(A5 未接);AS §2.1 写「恒 null 显--」、DTO @Schema 注「恒 0」、E2E 实测 `"0"`——demo 按「恒空显 --」渲染即可,值不具业务含义 | - | `"0"` | | content[].lastValidFollowTime | string | 最近有效跟进时间(超期锚点) | - | `"2026-09-03 22:55:18"` | | content[].ownerUserId | Long→str | 销售负责人 ID;**pool 行恒 null** | - | `null`(pool)/ `"739564171091247104"`(mine) | | content[].ownerUserNameSnapshot | string | 负责人姓名快照 | - | `null` / `"罗伟健"` | | content[].ownerDeptId | Long→str | 销售部门 ID 快照 | - | `"744841292348915712"` | | content[].ownerDeptNameSnapshot | string | 部门名快照 | - | `"广东保伦电子股份有限公司"` | | content[].enterPoolTime | string | 进入公海时间;**pool 固定列可排序**(D29 pool 默认排序=enter_pool_time 倒序;其余 workspace 默认 create_time 倒序) | - | `"2026-09-11 11:45:49"` / `null` | | content[].archiveStatus | number | 1 有效 2 已归档 | - | `1` | | content[].lastViewTime | string | 最近访问时间;**仅 RECENT 视图非 null**(同时是 RECENT 排序锚点) | - | `null` | | content[].focusFlag | number | 是否关注:customer_focus 行存在=1(**按当前登录用户**),0=未关注;三 workspace 通用 | - | `0` | | total / size / current / pages | Long→str | 分页元信息 | - | `"5"` / `"1"` / `"1"` / `"5"` | | empty | boolean | total==0 | - | `false` | - 备注:原型差异列=mine/overview 加负责人+部门四件,pool 加 enterPoolTime 且无负责人列;demo 现 11 列,类型/行业/战略协议等级/VIP 等级/进入公海时间列未渲染(矩阵 #11 字段级挂 07 票)。字典列后端一律回 code 不回名(CustomerListRowDTO 类注释明示)。 --- ### #9 看板分组汇总 — `POST /api/customer/workspace/board/summary` - 出处:`A4 客户管理\我的客户\看板列表\看板分组汇总.bru`(客户总览同名一件;**公海无看板 .bru**)+ `domain/dto/CustomerBoardSummaryDTO.java` + `service/impl/CustomerWorkspaceServiceImpl.java` - 请求参数:复用 `CustomerWorkspacePageParam`(同 #1),**groupColumn 必填**(stage/star/relation),**无需 groupValue**;分页/keyword 意义有限(同 WHERE 口径)。示例载荷:`workspace=mine&groupColumn=stage¤t=1&size=1&keyword=恒信达` - 出参(`List`,数组元素两字段): | 字段路径 | 类型 | 口径/文案 | 示例 | |---|---|---|---| | [].groupValue | number | 分组取值(stage 1~3 / star 1~5) | `1` | | [].cnt | Long→str | 该分组客户数(**仅返回有数据分组**,空列骨架前端补全;本期重潜/已成交恒空=D20 预期) | `"28"` | - 备注(**demo 旧代码字段名漂移**):旧 demo `loadSummary`(L590-592)读 `it.count` / `it.name`——**契约字段是 `cnt` / `groupValue`,旧读法必显 "--"**,改造时按 `groupValue`→文案、`cnt`→数字渲染。E2E 实测:`[{"groupValue":1,"cnt":"28"},{"groupValue":2,"cnt":"6"},{"groupValue":3,"cnt":"3"}]`。 ### #9 看板单列卡片 — `POST /api/customer/workspace/board/cards` - 出处:`A4 客户管理\我的客户\看板列表\看板单列卡片.bru`(总览同名)+ 同上源码 - 请求参数:同 #1 + `groupColumn`(stage/star/relation)+ **`groupValue` 必填**(缺省 67001「看板取卡必须指定分组值」)。示例载荷:`groupColumn=stage&groupValue=2&workspace=mine¤t=1&size=20&keyword=恒信达` - 出参:`PageResult`——**与列表 #1 逐字段同构**(24 字段,E2E 卡片样例:customerStage=2、customerStarLevel=4、ownerUserId=`"739564171091247104"`、total=`"6"`)。 - 备注:仅 mine/overview 可用;**pool 调看板两端口一律 67001「公海无卡片视图,看板仅支持客户总览/我的客户」**(源码 `assertBoardAllowed` 守卫在,AS §2.1 文档口径一致;GR §六.1 记 jar 实测 code=0 漂移待 04 票核实);board/cards 不接 saved-view;summary 与 page/cards 同一 WHERE 口径,数字自洽。 --- ### #7 preference 自定义视图四端点(saved-view) — `GET|POST /api/preference/view/list|save|delete|set-default` - 出处:**A4 无 saved-view `.bru` 文档**(契约源=AS §2.14 + crm-preference 源码 `controller/SavedViewController.java`、`domain/dto/SavedView.java`、`SavedViewCondition.java`);示例形态借 `A3 商机管理\销售机会\自定义视图\*.bru` 四件(字段结构同源,商机域 scopeKey 实测 `opportunity`) - **scopeKey**(客户域,`CustomerConstants` L67-69):`customer.mine` / `customer.overview` / `customer.pool`;userId 取登录态,不接受前端传入。 - 端点逐个: | 端点 | 请求参数 | 出参 data | |---|---|---| | `GET /api/preference/view/list?scopeKey=` | scopeKey | `List`,按 seqNo 排序;出参不回显 scopeKey | | `POST /api/preference/view/save?scopeKey=` | scopeKey(query 表单参)+ **`@RequestBody SavedView` JSON**——全平台唯一 JSON 例外(ADR-0017 范围外) | `String` viewId(新建/覆盖后服务端生成) | | `POST /api/preference/view/delete?scopeKey=&viewId=` | scopeKey、viewId | `null` | | `POST /api/preference/view/set-default?scopeKey=&viewId=` | scopeKey、viewId(单值互斥,设默认自动取消原默认) | `null` | - **SavedView 字段**(JSON body): | 字段 | 类型 | 必填 | 口径 | |---|---|---|---| | viewId | string | 新建可空;编辑/删除/设默认须回传 | 服务端生成 32 位 hex(样例 `"e7b9595f7ea54641b093e9d6bfb096f9"`) | | name | string | 是 | 检索名称(用户填) | | conditions | array | 否 | 本期前端只提交 1 组(契约按列表预留复合扩展) | | conditions[].field | string | - | 业务方字段池稳定 code(如 `industryCode`) | | conditions[].operator | string | - | 业务方自定义:等于/不等于/包含/不包含/为空/不为空(demo 用 eq/like 等) | | conditions[].value | string | - | 为空/不为空操作符时可为 null | | sortField / sortDirection | string | 否 | 排序字段 key / `asc`|`desc` | | isDefault | boolean | 否 | 默认视图图钉,单值互斥 | | seqNo | number | 否 | 列表排序序号 | - 备注:内置五视图(viewType)不走本接口;workspace/page 的 `savedViewId` 消费本接口存的 viewId(#1);视图为用户私有。 ### #8 preference 视图形态两件 — `GET|POST /api/preference/view-form/get|save` - 出处:`A4 客户管理\我的客户\读取视图形态.bru` + `保存视图形态.bru`(总览/公海同名共 6 件)+ `ViewFormController.java`、`ViewFormServiceImpl.java` - `GET /api/preference/view-form/get?scopeKey=` → `String`:`list`/`split`/`board`;**null=未设置,前端默认 list**(E2E:`"data": "list"`) - `POST /api/preference/view-form/save`(form:`scopeKey` + `viewForm`)→ `null`;白名单 {list,split,board},非法 68001(平台段位,不在 67xxx) - 备注:scopeKey 同 #7 三码位(`customer.mine/overview/pool`)。文档口径「pool 仅 list/split,board 被拒 680xx」**实现侧无 pool 特判——pool 保存 board 实回 code=0**(AS §2.14 ⚠ I-06 r2 漂移),前端不得依赖拒绝行为。 --- ### #13-19 归属/关注/重点 动作族(合并表,每端点一行;全 POST,id/ids 走查询参数) - 出处:`crm-customer/.../controller/CustomerOwnershipController.java`、`CustomerFocusController.java`;.bru 逐件:`客户公海\领取客户.bru`、`批量领取.bru`、`分配客户.bru`、`批量分配.bru`、`抛公海.bru`、`批量抛公海.bru`、`归档客户.bru`、`批量归档.bru`、`恢复客户.bru`、`关注客户.bru`、`取消关注客户.bru`、`批量关注客户.bru`、`客户详情\标记重点客户.bru`、`取消重点标记.bru` - 示例值:单条 `id=750844487390986240`;批量 `ids=750844487390986240`(重复键 `ids=..&ids=..`);分配目标 `userId=744842318024015872`。 | 端点 | 参数 | 返回 data | 主要错误码 | 口径 | |---|---|---|---|---| | `POST /api/customer/focus?id=` | id | `null`(Result) | 67002 | 幂等;行存在性=关注态;不写 oplog | | `POST /api/customer/unfocus?id=` | id | `null` | — | 幂等;物理删行,连带清重点标记 | | `POST /api/customer/focus-batch?ids=` | ids[] | **`null`(注意:非 BatchResult**——controller 循环逐个 focus 后恒 success) | 67002 | 逐个幂等关注,对称商机 | | `POST /api/customer/star?id=` | id | `null` | 67002 | 幂等;未关注自动建行 starred=1;不写 oplog | | `POST /api/customer/unstar?id=` | id | `null` | — | 幂等;保留行仅置 starred=0 | | `POST /api/customer/assign?id=&userId=` | id、userId | `null` | 67002/67012 | 目标人员启用在职+非原负责人;不校验进行中商机 | | `POST /api/customer/assign-batch?ids=&userId=` | ids[]、userId | `BatchResult` | 67002/67012 | 目标人员统一校验一次(无效**整批拒绝**);其余逐条独立事务 | | `POST /api/customer/claim?id=` | id | `null` | 67002/67004 | 校验仍在公海;D25 领取即赋 last_valid_follow_time 锚点(为空时赋当下) | | `POST /api/customer/claim-batch?ids=` | ids[] | `BatchResult` | 67002/67004 | 逐条独立事务,部分成功 | | `POST /api/customer/release-pool?id=` | id | `null` | 67002/67007 | 清 owner+协同人(部门锚点保留),软删 team_member;进行中商机 67007 阻断 | | `POST /api/customer/release-pool-batch?ids=` | ids[] | `BatchResult` | 67002/67007 | 逐条独立事务,部分成功 | | `POST /api/customer/archive?id=` | id | `null` | 67002/67004/67006 | 仅销售负责人;归档=删除语义(D19);进行中商机 67006 阻断 | | `POST /api/customer/archive-batch?ids=` | ids[] | `BatchResult` | 67002/67004/67006 | **整批预检:任一失败整批不执行**(预检失败=顶层非 0,E2E 实测顶层 `code:67006`);预检过后逐条独立事务 | | `POST /api/customer/restore?id=` | id | `null` | 67002/67004 | 仅销售负责人(已归档→有效) | - **BatchResult 结构**(`crm-base/.../result/BatchResult.java`):`{total:int, successCount:int, failCount:int, failures:[{id:Long→str, reason:string, message:string}]}`。`reason` 语义枚举(`CustomerBatchFailReason`):`NOT_FOUND / STATUS_NOT_ALLOWED / CONCURRENT_MODIFIED / ARCHIVE_BLOCKED / POOL_BLOCKED / ASSIGN_INVALID / UNKNOWN`。E2E 失败行样例:`{"id":"753612204858671104","reason":"STATUS_NOT_ALLOWED","message":"客户已被领取,不在公海"}`。 - **错误码含义**(AS §1):67004 当前状态不允许(归档/恢复/抛公海守卫);67006 归档被阻断:存在进行中商机;67007 抛公海被阻断:存在进行中商机;67012 分配非法(明细已分配/对象越权/为发起人本人/为当前负责人)。 - 单条动作 CAS 乐观锁 + 写 oplog(TRANSFER/CLAIM/POOL/ARCHIVE/RESTORE 均入 #45 oplog 值域);关注/重点族不写 oplog。 --- ### #21 新建客户 — `POST /api/customer/create`(form 绑定 `CustomerCreateDTO`,禁 @RequestBody) - 出处:`A4 客户管理\我的客户\新增客户.bru` + `domain/dto/CustomerCreateDTO.java`;出参 `CustomerSaveResultDTO.java` + `CustomerSimilarHitDTO.java` - 三层查重内建:L1 停输防抖(check-name,只提示)→ L2 名称相似 needConfirm 弹窗(未落库)→ L3 信用代码硬拦(67003)。`ownerUserId` 空=直接进公海并记 `enter_pool_time`(D25,锚点 NULL);有值=直接指派(快照服务端补齐,不信任前端传部门)。 - 请求参数(DTO 全字段,按区块分组;「必填」以 .bru 参数表+DTO @Schema 为准): | 名 | 类型 | 必填 | 示例 | 值域/口径 | |---|---|---|---|---| | customerName | string | 是 | `e2c-查重-撞码-003045` | 停输防抖触发相似查重 | | customerType | string | 是 | `customer_type_01` | customer_type 字典(6 值,见字典节) | | provinceCode / cityCode | string | 是 | `440000` / `440100` | 国标 code | | districtCode | string | 否 | `440103` | 国标 code | | industryCode | string | 是 | `gov` | 行业一级 code | | industryChildCode | string | 否 | `gov_1` | 行业二级 code | | customerStarLevel / relationStarLevel | number | 是 | `3` | 1~5 | | networkUnits | string | 否 | | 人脉关系单位 JSON 数组(标签名) | | ownerUserId | string(Long) | 否 | `739564171091247104` | 空=进公海;无效/停用 67012 | | companyDecisionMaker | string | 否 | `王总` | 公司决策人 | | isBizNegotiated | number | 是 | `0` | 1 是 / 0 否 | | directorVisitTime | string | 否 | | 总监近期拜访时间 yyyy-MM-dd HH:mm:ss | | isChild | number | 是 | `0` | 1 是 / 0 否(默认 0) | | parentCustomerId | string(Long) | 否 | | isChild=1 时必填,服务端防环校验 | | cooperationSystem | string | 否 | `OA对接` | 合作系统 | | nonCoopReason | string | 否 | | 不合作原因 | | joinedPresidentClass | number | 否 | `1` | 1/0/null(未选) | | presidentClassPerson / presidentClassPhone | string | 条件 | `王总` / `13800000001` | joinedPresidentClass=1 时 Person 必填 | | joinedProductClass | number | 否 | | 1/0/null | | productClassPerson / productClassPhone | string | 条件 | | joinedProductClass=1 时 Person 必填 | | unifiedCreditCode | string | 否 | `91440101E2CHXDA001` | 18 位数字或大写字母;撞码 L3 硬拦 67003 | | legalRepresentative | string | 否 | `王测试` | | | establishedDate | string | 否 | `2018-05-20` | yyyy-MM-dd,不得晚于当前日期 | | registeredCapital / businessScope / staffSize / annualRevenue / businessAddress | string | 否 | `1000万元人民币` 等 | 工商信息区文本 | | bizNameFromLookup | string | 否 | | 企查查确认的工商名称(只存不覆盖客户名称) | | remark | string | 否 | `e2e 实测备注` | ≤500 字 | | confirmSimilar | boolean | 否 | `true` | L2 弹窗后「仍要创建」重发置 true | - 出参(`CustomerSaveResultDTO`,data 内 5 字段):`id`(Long→str,落库成功时)、`needConfirm`(Boolean,true=名称相似需确认**未落库**)、`similarHits[]`(见 #22)、`customerNo`(落库成功时,`"KH202609110010"`)、`customerName`。 - 错误码:67001 入参非法 / 67003 信用代码撞码 / 67012 销售负责人无效 / 67015 查重设置单例缺失。 - E2E 落库成功样例:`{"id":"753584451841163264","needConfirm":null,"similarHits":null,"customerNo":"KH202609110010","customerName":"e2c-cg-A-098932"}`。 ### #22 名称相似查重 — `GET /api/customer/check-name?name=&excludeId=` - 出处:`A4 客户管理\客户查重\名称相似提示.bru` + `CustomerSimilarHitDTO.java`(L1 停输防抖 / L2 弹窗共用载荷;create/quick-create 内建查重同款) - 请求参数:`name`(string,必填,待查客户名称);`excludeId`(string,可选,编辑页传自身排除)。 - 出参(`List`,data 内 6 字段/行): | 字段路径 | 类型 | 口径 | 脱敏 | 示例 | |---|---|---|---|---| | [].customerId | Long→str | 命中客户 ID | **无权命中=null** | `"750844478243209216"` | | [].customerName | string | 命中客户名称 | 无权命中=null | `"e2c-恒信达科技有限公司"` | | [].ownerName | string | 命中客户负责人 | 无权命中=null | `"罗伟健"` | | [].customerStage | number | 1 潜在 2 重潜 3 已成交 | 无权命中=null | `1` | | [].archiveStatus | number | 1 有效 2 已归档 | 无权命中=null | `1` | | [].masked | boolean | 无权命中=true | **true 时以上五字段全 null,前端只渲染「存在匹配记录」** | `false` | - 备注:**masked 脱敏形态(契约=DTO 注释,E2E 无 masked=true 样例)**:不是打星号字符串,而是「字段置 null + masked=true + 固定文案」。匹配方式/阈值走查重设置(AS §2.13:nameMatchMode 1 精确 / 2 模糊,similarityThreshold 0~100 出厂 80;.bru docs 提 ngram 阈值 70/80/90 为旧口径,以查重设置单例为准)。错误码 67015(单例未落地时走出厂默认不报)。 ### #23 信用代码查重 — `GET /api/customer/check-credit-code?creditCode=` - 出处:`A4 客户管理\客户查重\信用代码查重.bru` + `CreditCodeCheckDTO.java` - 请求参数:`creditCode`(string,必填,18 位)。示例 `91440101MA9ABC1234`。 - 出参(`CreditCodeCheckDTO`,data 内 6 字段): | 字段路径 | 类型 | 口径 | 脱敏 | 示例 | |---|---|---|---|---| | exists | boolean | 是否已存在同码客户 | - | `true` | | masked | boolean | 无权命中=true | **true 时只提示「系统已存在该企业」** | `false` | | customerId | Long→str | 命中客户 ID | 有权可见时给 | `"750844478243209216"` | | customerName | string | 命中客户名称 | 有权可见时给 | `"e2c-恒信达科技有限公司"` | | customerNo | string | 命中客户编号 | 有权可见时给 | `"KH202609030011"` | | ownerName | string | 命中客户负责人 | 有权可见时给 | `"罗伟健"` | - 备注:L1 即时校验(失焦/企查查回填后调用);L3 保存时硬拦 67003;系统强制无开关(AS §2.13)。 ### #24 工商联想 — `GET /api/customer/company-lookup?companyName=`(**参数名是 companyName 不是 name**) - 出处:`A4 客户管理\我的客户\工商信息查询.bru` + `port/CompanyLookupPort.java` - 请求参数:`companyName`(string,必填,公司名称关键字)。示例 `恒信达`。 - 出参(`List`,9 字段/候选):`bizName` / `unifiedCreditCode` / `legalRepresentative` / `establishedDate`(LocalDate)/ `registeredCapital` / `businessScope` / `staffSize` / `annualRevenue` / `address`。 - 备注:**一期 stub 恒返回空数组**(E2E:`"data": []`),前端按「未查询到匹配企业」渲染,可手工录入;真实企查查接入后自动激活。前端职责:只回填当前为空的字段,已填字段再选须弹「受影响字段确认」;回填后立即调 check-credit-code;无结果/失败/超时均不阻断手工提交。 ### #25 编辑客户 — `POST /api/customer/edit?id=`(form 绑定 `CustomerUpdateDTO` + CAS) - 出处:`A4 客户管理\客户详情\编辑客户.bru` + `CustomerUpdateDTO.java` - 请求参数:查询参数 `id` + 表单(与 #21 create 的差异): - **多**:`version`(number,**必传**,乐观锁 CAS,不匹配报 **67005**「乐观锁冲突」;编辑回显取 `GET /detail` 的 `version` 带回)。 - **少**:无 `ownerUserId`(**归属字段不可改**——负责人/部门「✅(新增时)」;服务端忽略任何归属入参,归属变更走 assign/transfer)。 - 其余 33 字段与 create 同名同义(customerName…remark + confirmSimilar),必填区相同(isBizNegotiated/isChild 必填)。 - 出参:同 `CustomerSaveResultDTO`(needConfirm 相似确认流同样适用,`confirmSimilar=true` 重发)。 - 错误码:67001 / 67002 / 67003 / 67005。 - 备注(AS §2.3 edit 行注):冒烟实测需一并提交 `version`+`isBizNegotiated`+`isChild`,缺省因 DB NOT NULL 无默认而失败(关联 AS §5 D-05 已修,冒烟口径保守保留)。E2E 反例样例:`{"code":67005,"success":false,"message":"缺少乐观锁版本号","data":null}`。 ### #47 快速创建客户 — `POST /api/customer/quick-create`(`CustomerQuickCreateDTO` 最小集) - 出处:`A4 客户管理\我的客户\快速创建客户.bru` + `CustomerQuickCreateDTO.java` - 请求参数(7+1 字段,DTO `requiredMode=REQUIRED` 全 7 必填): | 名 | 类型 | 必填 | 示例 | |---|---|---|---| | customerName | string | 是 | `e2c-快创-448049` | | customerType | string | 是 | `customer_type_01` | | provinceCode / cityCode | string | 是 | `440000` / `440100` | | industryCode | string | 是 | `gov` | | customerStarLevel / relationStarLevel | number | 是 | `2` | | confirmSimilar | boolean | 否(默认 false) | 命中相似确认后重发置 true(返工票 04 补齐) | - 出参:`CustomerSaveResultDTO`(同 #21)。E2E needConfirm 样例:`{"needConfirm":true,"similarHits":[{"customerId":"753620871771324416","customerName":"e2c-t05qc-相似源甲科技有限公司","ownerName":"罗伟健","customerStage":1,"archiveStatus":1,"masked":false}],"customerNo":null,"customerName":null}`。 - 错误码:67001 / 67003。 - 备注(**两处 .bru 漂移,以源码/AS 为准**):① .bru 参数表把 industryCode/两星级标「否」且**漏 `confirmSimilar`**——DTO 源码有该字段(返工票 04 已补);② .bru docs 文本「QuickCreateDTO 无 confirmSimilar 字段」为陈旧描述。服务端默认:owner=当前用户、is_child=否、isBizNegotiated=否;查重三层与编号生成复用主表 create。 ### #48 商机侧客户搜索 — `POST /api/customer/search`(form 绑定 `CustomerSearchParam`) - 出处:**无 A4 `.bru`**(契约源=源码 `CustomerSearchParam.java` + `CustomerSearchItemDTO.java` + AS §2.4);A3 侧演示形态待 03 票 - 请求参数:BaseParam 五参(keyword=**名称/联系人/电话三维模糊**)+ `customerType`(string,字典 code 精确,可空)+ `archiveStatus`(number,1/2,可空=全部)+ `excludeCustomerIds`(Long 数组,重复键;商机候选池排除已关联客户,可空)。 - 出参(`PageResult`,行 9 字段): | 字段路径 | 类型 | 口径 | 示例 | |---|---|---|---| | content[].id | Long→str | 客户 id | — | | content[].customerNo | string | 客户编号 | — | | content[].customerName | string | 客户名称 | — | | content[].customerType | string | 字典 code | — | | content[].ownerUserId | Long→str | 销售负责人 id | — | | content[].ownerName | string | 负责人姓名(快照) | — | | content[].ownerDeptId | Long→str | 负责人部门 id | — | | content[].customerStage | number | 1 潜在/2 重潜/3 已成交 | — | | content[].archiveStatus | number | 1 有效/2 已归档 | — | - 备注:轻量出参;选中后走 `GET /api/customer/detail?id=` 带出全量;DataScope 注入可见范围。 --- ### #29 详情公共头部 — `GET /api/customer/detail-head?id=` - 出处:`A4 客户管理\客户详情\详情公共头部.bru` + `domain/dto/CustomerDetailHeadDTO.java` - 请求参数:`id`(string,必填,查询参数)。示例 `750844477165273088`。 - **副作用**:upsert `customer_view_log` 刷新「最近访问」锚点(F7 viewTouch 收口;UNIQUE(user_id,customer_id) 去重;高频行为不落 oplog)——冒烟断言 RECENT 视图排序前先打一次详情头。 - 出参(`CustomerDetailHeadDTO`,data 内 24 字段;示例=E2E 客户甲): | 字段路径 | 类型 | 口径/文案 | 示例 | |---|---|---|---| | id | Long→str | 客户 ID | `"750844477165273088"` | | customerNo | string | 客户编号 | `"KH202609030010"` | | customerName | string | 客户名称 | `"e2c-全字段-科技"` | | customerStage | number | 1 潜在 2 重潜 3 已成交(只读条,不可手动改) | `1` | | customerStageName | string | 阶段名回显(CustomerStageEnum) | `"潜在客户"` | | customerStarLevel / relationStarLevel | number | 1~5 | `3` / `3` | | strategicAgreementLevel | number | 快照,null=未签 | `null` | | vipCustomerLevel | number | 已成交 VIP 等级快照,null=无 | `null` | | archiveStatus | number | 1 有效 2 已归档(已归档态 badge 数据源) | `1` | | ownerUserId | Long→str | 销售负责人 ID(公海 null) | `"739564171091247104"` | | ownerUserNameSnapshot | string | 负责人姓名快照 | `"罗伟健"` | | ownerDeptNameSnapshot | string | 部门名快照 | `"广东保伦电子股份有限公司"` | | lastFollowSummary | string | 最近跟进摘要(最新一条跟进内容) | `"e2c-F08 跟进实测:电话沟通年度合作意向"` | | lastFollowTime | string | 最近跟进时间 | `"2026-09-11 11:45:33"` | | nextFollowTime | string | 待跟进提醒(未来最近一条下次跟进时间,null=无待跟进) | `"2026-09-12 22:34:06"` | | opportunityCount | Long→str | 关联商机数(外部反查,失败前端显 "--") | `"0"` | | projectCount | Long→str | DTO @Schema=「恒 null 显 --」;E2E 实测 `"0"`——按恒空渲染 | `"0"` | | wonProjectAmount | BigDecimal | 汇总卡①已中标项目总额——**恒 null 显 "--"**(A5/投标 seam 未接入) | `null` | | planEstimateAmount | BigDecimal | 汇总卡②跟进中方案预估总额——恒 null 显 "--"(方案卡 seam) | `null` | | ongoingOpportunityAmount | BigDecimal | 汇总卡③进行中商机总额——恒 null 显 "--"(可经 port 扩展后切真值) | `null` | | contractAmount | BigDecimal | 汇总卡④合同总额——恒 null 显 "--"(合同模块未建) | `null` | | paidAmount | BigDecimal | 汇总卡④累计回款——恒 null 显 "--"(与合同总额同卡) | `null` | - 备注:**金额 5 字段恒 null 显 "--" 是 grill Q18 契约:前端渲染 4 张汇总卡、显 "--" 不显 0**(矩阵 #29=P3-6 改造点:demo 未渲染卡)。错误码 67002。 ### #30 客户详情(页签1 客户信息 / 编辑页回显同源) — `GET /api/customer/detail?id=` - 出处:`A4 客户管理\客户详情\客户详情.bru` + `domain/dto/CustomerDetailDTO.java`(extends BaseDTO) - 请求参数:`id`(string,必填,查询参数)。错误码 67002。 - 出参(`CustomerDetailDTO`,data 内 48 字段=BaseDTO 3 + 本类 45;示例=E2E 客户甲): | 字段路径 | 类型 | 口径/文案 | 脱敏 | |---|---|---|---| | id | Long→str | 主键 | - | | createTime / updateTime | string | **经 BaseDTO 带出,E2E 实测有值** `"2026-09-03 22:27:53"`(见下方 G4 备注) | - | | customerNo | string | KH+yyyyMMdd+4 位序,只读 | - | | customerStage | number | 1 潜在 2 重潜 3 已成交(只读条) | - | | archiveStatus | number | 1 有效 2 已归档 | - | | version | number | 乐观锁版本号(**编辑回传**,E2E `3`) | - | | customerName | string | 客户名称 | - | | customerType / customerTypeName | string | code + **字典名回显**(`customer_type_01`/`总包`) | - | | provinceCode / cityCode / districtCode | string | 国标 code(名称前端字典渲染,后端不回名) | - | | industryCode / industryName | string | `gov`/`政府机关`(一级字典名回显) | - | | industryChildCode / industryChildName | string | `gov_1`/`人大政协`(二级字典名回显) | - | | customerStarLevel / relationStarLevel | number | 1~5 | - | | networkUnits | string | 人脉关系单位 JSON 数组 | - | | ownerUserId | Long→str | 销售负责人 ID(公海 null) | - | | ownerUserNameSnapshot / ownerDeptId / ownerDeptNameSnapshot | — | 负责人姓名/部门 ID/部门名快照 | - | | enterPoolTime | string | 进入公海时间(公海客户才有值) | - | | lastValidFollowTime | string | 最近有效跟进时间(超期锚点) | - | | strategicAgreementLevel / vipCustomerLevel | number | 快照(null=未签/无) | - | | companyDecisionMaker | string | 公司决策人 | - | | isBizNegotiated | number | 是否商机勾兑 1/0 | - | | directorVisitTime | string | 总监近期拜访时间 | - | | isChild / parentCustomerId / parentCustomerNameSnapshot | — | 子客户标志/父客户 ID/父客户名快照 | - | | cooperationSystem / nonCoopReason | string | 合作系统/不合作原因 | - | | joinedPresidentClass / presidentClassPerson / presidentClassPhone | — | 总裁班三件(**电话明文**,E2E `"13800000001"`) | **无脱敏** | | joinedProductClass / productClassPerson / productClassPhone | — | 产品班三件(电话明文) | **无脱敏** | | unifiedCreditCode | string | 统一社会信用代码(E2E `91440101E2CTEST001` 明文) | **无脱敏(V1 三档脱敏划出)** | | legalRepresentative / establishedDate / registeredCapital / businessScope / staffSize / annualRevenue / businessAddress / bizNameFromLookup | — | 工商信息区(establishedDate=LocalDate) | - | | remark | string | 备注 | - | - 备注(**G4 校准**):矩阵 #30 记档「系统信息四件套 creatorId/createTime/updaterId/updateTime 不在 DTO 恒"–"」——**逐字核对源码后应修正为:`creatorId`/`updaterId` 确实不在 CustomerDetailDTO(无字段);但 `createTime`/`updateTime` 由 BaseDTO 继承带出且 E2E 实测有值**。demo 现渲染四件套恒 "--" 是保守可行口径;若要显示创建/更新时间可直接取值,只有操作人两件必须 "--"。 ### #40 跟进记录分页 — `GET /api/customer/follow/page?id=` + 新增跟进 — `POST /api/customer/follow/add?id=` - 出处:`A4 客户管理\客户详情\跟进记录\跟进记录分页.bru` + `新增跟进.bru` + `domain/param/FollowPageParam.java`、`domain/dto/FollowCreateDTO.java`、`domain/entity/CustomerFollow.java`(出参=实体直出) - **page 请求参数**(GET query):`id`(必填)+ `followWay`(string,follow_way 字典 code 精确,例 `follow_way_01`)+ `startTime`/`endTime`(string,yyyy-MM-dd HH:mm:ss 含端)+ BaseParam 五参。 - **G1 标注**:原型有「操作人」筛选,`FollowPageParam` **无 operatorUserId 参数**——后端无此过滤能力,demo 侧豁免该筛选(P2-G1 记档)。 - **page 出参**(`PageResult`,行 13 字段,实体直出含审计四件套): | 字段路径 | 类型 | 口径 | 示例 | |---|---|---|---| | content[].id / creatorId / createTime / updaterId / updateTime / deleted | — | BaseEntity 审计字段(E2E 实测序列化;`"deleted":false`) | `"750854396027338752"` | | content[].customerId | Long→str | 所属客户 ID | `"750844477165273088"` | | content[].followWay | string | follow_way 字典 code(5 值见字典节;**后端不回名**) | `"follow_way_01"` | | content[].followContent | string | 跟进内容(长文本) | `"e2c-F08 跟进实测:电话沟通年度合作意向"` | | content[].nextFollowTime | string | 下次跟进时间(有值进「待跟进」筛选) | `"2026-09-10 23:07:18"` | | content[].followBy | Long→str | 跟进人用户 ID | `"739564171091247104"` | | content[].followByName | string | 跟进人姓名(服务端快照,前端不传) | `"罗伟健"` | | content[].followDeptName | string | 跟进人部门名 | `"广东保伦电子股份有限公司"` | - **add 请求参数**(form):`followWay`(必填,5 值域)、`followContent`(必填,空→67009)、`nextFollowTime`(选填,**ISO `T` 分隔** `2026-09-10T10:00:00`;空格分隔报 400;有值进待跟进)。 - **add 出参**:`Result` 跟进 id(E2E `"753583108837605376"`)。 - 备注:append-only 提交后不可改删(纠错=新增补充);同客户同内容 30 秒窗口防重复提交;写库刷新 `last_valid_follow_time` 超期锚点(旧未发提醒自动置已失效)。错误码 67002/67009。已知缺陷 D-02(P2):nextFollowTime 早于当前未拦截(.bru docs 记档)。 ### #45 操作日志分页 — `GET /api/customer/oplog/page?id=` - 出处:`A4 客户管理\客户详情\操作日志\操作日志分页.bru` + `domain/param/OplogPageParam.java`、`domain/entity/CustomerOplog.java`(出参=实体直出) - **请求参数**(GET query):`id`(必填)+ `startTime`/`endTime`(yyyy-MM-dd HH:mm:ss 含端)+ `action`(string,操作类型精确)+ BaseParam 五参(keyword 匹配 `detail` 叙事)。 - **action 值域**:权威=`CustomerConstants` 全部 ACTION_* 常量,**18 种**:`CREATE / UPDATE / FOLLOW / ARCHIVE / RESTORE / TRANSFER / CLAIM / POOL / STAGE_CHANGE / MEMBER_ADD / MEMBER_REMOVE / CONTACT_ADD / CONTACT_EDIT / CONTACT_DELETE / AGREEMENT_ADD / AGREEMENT_EDIT / AGREEMENT_DELETE / GRAPH_EDIT`。(漂移标注:AS §3 写「17 种」未含 GRAPH_EDIT,未同步;demo 筛选下拉按 18 种渲染。) - **出参**(`PageResult`,行 12 字段): | 字段路径 | 类型 | 口径 | 示例 | |---|---|---|---| | content[].id / creatorId / createTime / updaterId / updateTime / deleted | — | BaseEntity 审计字段 | `"750854380575522816"` | | content[].customerId | Long→str | 所属客户 ID | `"750844477165273088"` | | content[].action | string | 18 种值域(见上) | `"UPDATE"` | | content[].detail | string | 叙事形态 `把 {字段} 从 {旧} 修改为 {新}`(D13;多字段变更串联一条) | `"把人脉关系单位从[...]修改为空;把公司决策人从王总修改为空;…"` | | content[].operatorId | Long→str | 操作人用户 ID(**系统动作为 null**,如阶段自动流转/导入) | `"739564171091247104"` | | content[].operatorName | string | 操作人姓名(系统动作记「系统」) | `"罗伟健"` | | content[].operatorDeptName | string | 操作人部门名快照 | `"广东保伦电子股份有限公司"` | - 备注:append-only 审计、无限期保留;时间倒序;日期分组由前端渲染。错误码 67002。导出按钮无端点=豁免(P3-4)。 ### #41 关联商机页签 — `GET /api/customer/opportunity/page?id=`(port 反查 crm-opportunity) - 出处:`A4 客户管理\客户详情\关联商机\关联商机页签.bru` + `port/CustomerOpportunityQueryPort.java`(record OpportunityItem,7 字段真名逐字如下) - 请求参数:`id`(必填)+ BaseParam 五参。 - 出参(`PageResult`,**契约 7 字段**;G2:原型 13 列 vs 契约 7 字段,demo 现 5 列): | 字段路径 | 类型 | 口径 | 示例 | |---|---|---|---| | content[].id | Long→str | 商机 ID | `"750844498195513344"` | | content[].oppName | string | 商机名称 | `"e2c-联动-交割"` | | content[].stageName | string | 商机阶段名(已回显名) | `"客户圈定"` | | content[].estimateAmount | BigDecimal | 预计金额 | `null` | | content[].ownerUserId | Long→str | 销售负责人 ID | `"739564171091247104"` | | content[].ownerNameSnapshot | string | 负责人姓名快照(可 null→"--") | `null` | | content[].updateTime | string | 最近更新时间 | `"2026-09-03 22:27:58"` | - 备注:port 失败前端显 "--" 不显 0(issues-07 §A);列清单以 03 票实现实返为准收口 G2。错误码 67002。 ### #43 战略协议五端点 — `CustomerAgreementController /api/customer/agreement/*` - 出处:`A4 客户管理\战略协议\*.bru` 五件 + `domain/dto/AgreementDTO.java`(ADR-0017 Route A:写入参/出参双向同类) - **AgreementDTO 字段**(双向): | 字段 | 类型 | 必填(写) | 口径 | |---|---|---|---| | customerId | Long→str | 新增必填;**编辑传入忽略**(归属不可换客户,以库内为准) | 所属客户 | | agreementLevel | number | 是 | 1~3=一级/二级/三级;越界 67017 | | amount | string | 否 | 签订金额**文本**(数字/区间/含单位,E2E `"500万"`、`"128000"`) | | remark | string | 否 | 备注 | | fileFileId | string | 否 | 附件 fileId(crm-file 引用字符串,单文件可空);**demo 以纯文本框呈现**(附件上传位豁免 D2),下载由前端走 crm-file 通用端点 | | customerName | string | 仅出参 | 所属客户名称(卡片带出) | | id / createTime / updateTime | — | 仅出参 | BaseDTO(E2E detail 实测有值) | | 端点 | 参数 | 出参 data | 错误码 | |---|---|---|---| | `GET /api/customer/agreement/list?customerId=` | customerId | `List`(页签卡片,时间倒序**非分页**;E2E 空列表 `[]`) | 67002 | | `GET /api/customer/agreement/detail?id=` | id | `AgreementDTO` 全量 | 67017/67002 | | `POST /api/customer/agreement/create`(form) | AgreementDTO | `Long` 协议 id | 67001/67017/67002 | | `POST /api/customer/agreement/edit?id=`(form) | id + AgreementDTO | `null` | 67001/67017/67002 | | `POST /api/customer/agreement/delete?id=` | id | `null`(软删) | 67002/67017 | - 备注(**快照联动**):新增/编辑/删除协议同步刷新客户主表 `strategic_agreement_level`(删除=删空后快照回 NULL=未签);oplog 记 `AGREEMENT_ADD/EDIT/DELETE`;67017 语义=等级缺失或越界/协议不存在或无权访问。权限继承所属客户(无独立 ACL,无权 67002/67017)。 ### #44 团队成员三端点 — `GET member/list?id=` + `POST member/add?id=` + `POST member/remove?id=&memberUserId=` - 出处:`A4 客户管理\客户详情\团队成员\*.bru` 三件 + `domain/dto/CustomerMemberDTO.java`、`MemberAddDTO.java` - `GET /api/customer/member/list?id=` 出参(`List`,**data 内仅 4 字段**;负责人 ROLE_OWNER 在前): | 字段路径 | 类型 | 口径 | 示例 | |---|---|---|---| | [].userId | Long→str | 成员用户 ID | `"739564171091247104"` | | [].userName | string | 姓名(负责人=主表快照,协同人=团队成员快照) | `"罗伟健"` | | [].deptName | string | 部门名(负责人=主表快照,协同人=**实时回显**) | `"广东保伦电子股份有限公司"` | | [].role | string | 角色枚举:`OWNER` 负责人 / `COLLABORATOR` 协同人(**字段名是 role,不是 memberRole**) | `"OWNER"` | - **G3 标注**:原型另有职务/加入时间两列——`memberRole`、`createTime` **均不在此 DTO**(不暴露),demo 恒 "--" 渲染=记档口径(P2-G3,字段级挂 07 票)。 - `POST /api/customer/member/add?id=`:form 绑定 `MemberAddDTO{memberUserIds: List}`(**重复键** `memberUserIds=..&memberUserIds=..`,示例 `744842318024015872`)→ `null`。两阶段校验**整批拒绝**:名单空/含销售负责人/重复加入/用户停用→67016;批内幂等去重;姓名快照;oplog `MEMBER_ADD`。E2E 反例:`{"code":67016,"message":"成员名单不能为空"}`。 - `POST /api/customer/member/remove?id=&memberUserId=`:查询参数两件 → `null`。软删保留历史(delete_key 复用键,可重复加入);oplog `MEMBER_REMOVE`;错误码 67002/67016。 ### #42 关联项目页签 — 占位(demo 无端点调用,内容恒 "--") - 矩阵 #42 口径:**demo 侧无入口调用、页签占位显 "--"**(「新增占位页签」动作)。漂移提示:契约面实际已存在 `GET /api/customer/project/page?id=`(`CustomerDetailController.pageProjects`,`CustomerProjectQueryPort.ProjectItem` 9 字段),且 `A4 客户管理\客户详情\关联项目\关联项目页签.bru` 已有 E2E 真值(projectName=e2c-r3d-n3-项目甲、stageName=冲突处理、statusName=进行中、filingStatusName=报备审核中)——AS §5.3「A5 未建恒 --」记档已过时;demo 维持占位,切真值属后续票,本表不展开。 --- ## 字典与枚举(值域 + 文案 + 出处) | 字典/枚举 | 值域与文案 | 出处 | |---|---|---| | customer_stage | 1 潜在客户 / 2 重潜客户 / 3 已成交客户(三态只前进,CAS;**本期两个跃迁均不触发,实际只有「潜在」**,D20 seam) | `domain/enums/CustomerStageEnum.java`;detail-head `customerStageName` 回显「潜在客户」;AS §3 | | customer_star_level | 1~5 整数,无文案字典(星级组件渲染) | DTO @Schema;AS §2.1 | | relation_star_level | 1~5 整数(导入 INSERT 缺省补 0=未评估占位,D-05 修复口径);看板 groupColumn=`relation` 分组 1~5 | AS §5.7/§2.1 | | customer_type | `customer_type_01` 总包 / `_02` 工程商 / `_03` 投资方 / `_04` 设计院 / `_05` 集成商 / `_06` 投标公司 | `crm-dict/.../config/DictDataInitializer.java` L325-332 | | industryCode | 运行时字典接口/字典树核对(industry 分组两级行业树,客户用二级/商机用一级);E2E 样例 `gov`=政府机关、`gov_1`=人大政协 | DictDataInitializer L95;E2E detail 实测 | | follow_way | `follow_way_01` 电话沟通 / `_02` 上门拜访 / `_03` 微信对接 / `_04` 线上会议 / `_05` 展会沟通 | DictDataInitializer L293-299;FollowCreateDTO @Schema | | viewType(内置五视图) | `ASSIGNED` 我负责的 / `COLLABORATING` 我协同的 / `FOLLOW_UP_DUE` 待跟进 / `FOCUSED` 我关注的 / `RECENT` 最近访问;适配矩阵见 #1 | AS §2.1/§3 | | archiveStatus | 1 有效 / 2 已归档(无合并态,D19 归档=删除语义) | AS §3 | | view-form | `list` / `split` / `board`(白名单外 68001;pool 拒 board 文档口径未实现=I-06 漂移) | ViewFormController/Impl;.bru 保存视图形态.bru;AS §2.14 | | oplog action | 18 种(含 `GRAPH_EDIT`),逐值见 #45;AS §3「17 种」为未同步漂移 | `CustomerConstants` L97-114 | | BatchFailReason | `NOT_FOUND / STATUS_NOT_ALLOWED / CONCURRENT_MODIFIED / ARCHIVE_BLOCKED / POOL_BLOCKED / ASSIGN_INVALID / UNKNOWN` | `domain/enums/CustomerBatchFailReason.java` | | member role | `OWNER` / `COLLABORATOR` | CustomerMemberDTO 常量 | | transferReason(交割,本表外引用) | `resign` 离职 / `transfer_post` 岗位调动 / `region_adjust` 区域调整 | AS §3 | | job_title | 运行时两级字典树(demo 动态拉取),无静态值域,不落本表 | AS §2.7(jobTitleId/Name/Category/Level 联动 D1/D2) | --- ## 来源文件清单 **Bruno 仓 `D:\code\crm-api-docs\`**(相对根): - `A4 客户管理\我的客户\workspace 列表分页.bru`、`新增客户.bru`、`快速创建客户.bru`、`编辑客户.bru`(客户详情\)、`工商信息查询.bru` - `A4 客户管理\客户总览\workspace 列表分页.bru`、`看板列表\看板分组汇总.bru`、`看板单列卡片.bru`(我的客户同名) - `A4 客户管理\客户查重\名称相似提示.bru`、`信用代码查重.bru` - `A4 客户管理\客户详情\详情公共头部.bru`、`客户详情.bru`、`标记重点客户.bru`、`取消重点标记.bru`、`跟进记录\跟进记录分页.bru`、`跟进记录\新增跟进.bru`、`操作日志\操作日志分页.bru`、`关联商机\关联商机页签.bru`、`关联项目\关联项目页签.bru`、`团队成员\团队成员页签.bru`、`团队成员\添加团队成员.bru`、`团队成员\移除团队成员.bru` - `A4 客户管理\客户公海\关注客户.bru`、`取消关注客户.bru`、`批量关注客户.bru`、`领取客户.bru`、`批量领取.bru`、`分配客户.bru`、`批量分配.bru`、`抛公海.bru`、`批量抛公海.bru`、`归档客户.bru`、`批量归档.bru`、`恢复客户.bru`、`读取视图形态.bru`、`保存视图形态.bru`(我的客户/客户总览同名) - `A4 客户管理\战略协议\战略协议页签列表.bru`、`战略协议详情.bru`、`新增战略协议.bru`、`编辑战略协议.bru`、`删除战略协议.bru` - `A3 商机管理\销售机会\自定义视图\自定义视图列表.bru`、`保存自定义视图.bru`(saved-view 形态佐证,A4 无同名 .bru) **后端源码 `D:\code\crm-backend-matt\`**(绝对路径根同上): - `crm-customer\src\main\java\com\crm\customer\domain\dto\`:CustomerListRowDTO、CustomerBoardSummaryDTO、CustomerCreateDTO、CustomerUpdateDTO、CustomerQuickCreateDTO、CustomerSaveResultDTO、CustomerSimilarHitDTO、CreditCodeCheckDTO、CustomerSearchItemDTO、CustomerDetailHeadDTO、CustomerDetailDTO、FollowCreateDTO、AgreementDTO、CustomerMemberDTO、MemberAddDTO、CustomerBatchFailItem - `crm-customer\...\domain\param\`:CustomerWorkspacePageParam、CustomerSearchParam、FollowPageParam、OplogPageParam - `crm-customer\...\domain\enums\`:CustomerStageEnum、CustomerBatchFailReason;`constant\CustomerConstants.java` - `crm-customer\...\port\`:CustomerOpportunityQueryPort、CompanyLookupPort;`mapper\CustomerMapper.java`(pageWorkspace SQL:opportunityCount 真算/projectCount 恒 0/focusFlag/lastViewTime);`service\impl\CustomerWorkspaceServiceImpl.java`(board 守卫/排序/viewType);`controller\`:CustomerWorkspaceController、CustomerOwnershipController、CustomerFocusController、CustomerController、CustomerDetailController、CustomerAgreementController - `crm-preference\src\main\java\com\crm\preference\`:controller\SavedViewController、controller\ViewFormController、domain\dto\SavedView、domain\dto\SavedViewCondition - `crm-base\src\main\java\com\crm\base\domain\`:param\BaseParam、dto\BaseDTO、result\PageResult、result\BatchResult、result\Result - `crm-dict\src\main\java\com\crm\dict\config\DictDataInitializer.java`(customer_type/follow_way 种子) **仓内文档**: - `.scratch\customer-module\API-SUMMARY.md`(§0/§1/§2.1-2.6/§2.9/§2.14/§3/§5) - `.scratch\customer-demo-ref\assets\coverage-matrix.md`(入口编号/G1-G6/P3-x 依据) - `.scratch\customer-frontend-handover\mapping-a4-pages.md`(原型列文案) - `.scratch\customer-e2e\demo\index.html`(fork 基线渲染口径:`|| '--'`、maskPhone/reveal、旧 loadSummary 读 count/name 的漂移证据)