You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
530 lines
50 KiB
530 lines
50 KiB
|
3 days ago
|
# 客户 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<T> = {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<CustomerListRowDTO>`,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<CustomerBoardSummaryDTO>`,数组元素两字段):
|
||
|
|
|
||
|
|
| 字段路径 | 类型 | 口径/文案 | 示例 |
|
||
|
|
|---|---|---|---|
|
||
|
|
| [].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<CustomerListRowDTO>`——**与列表 #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<SavedView>`,按 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<Void>) | 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<CustomerSimilarHitDTO>`,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<CompanyCandidate>`,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<CustomerSearchItemDTO>`,行 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<CustomerFollow>`,行 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<Long>` 跟进 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<CustomerOplog>`,行 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<OpportunityItem>`,**契约 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<AgreementDTO>`(页签卡片,时间倒序**非分页**;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<CustomerMemberDTO>`,**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<Long>}`(**重复键** `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 的漂移证据)
|