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.
 
 
 
 
 

50 KiB

客户 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 无字段级脱敏unifiedCreditCodepresidentClassPhone 等明文回显(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.javadomain/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.javaservice/impl/CustomerWorkspaceServiceImpl.java
  • 请求参数:复用 CustomerWorkspacePageParam(同 #1),groupColumn 必填(stage/star/relation),无需 groupValue;分页/keyword 意义有限(同 WHERE 口径)。示例载荷:workspace=mine&groupColumn=stage&current=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&current=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.javadomain/dto/SavedView.javaSavedViewCondition.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
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.javaViewFormServiceImpl.java
  • GET /api/preference/view-form/get?scopeKey=Stringlist/split/boardnull=未设置,前端默认 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.javaCustomerFocusController.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 客户管理\我的客户\新增客户.brudomain/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 客户管理\客户查重\名称相似提示.bruCustomerSimilarHitDTO.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 客户管理\客户查重\信用代码查重.bruCreditCodeCheckDTO.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 客户管理\我的客户\工商信息查询.bruport/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 客户管理\客户详情\编辑客户.bruCustomerUpdateDTO.java
  • 请求参数:查询参数 id + 表单(与 #21 create 的差异):
    • version(number,必传,乐观锁 CAS,不匹配报 67005「乐观锁冲突」;编辑回显取 GET /detailversion 带回)。
    • :无 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-createCustomerQuickCreateDTO 最小集)

  • 出处:A4 客户管理\我的客户\快速创建客户.bruCustomerQuickCreateDTO.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 客户管理\客户详情\详情公共头部.brudomain/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 客户管理\客户详情\客户详情.brudomain/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 + 新增跟进.brudomain/param/FollowPageParam.javadomain/dto/FollowCreateDTO.javadomain/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 客户管理\客户详情\操作日志\操作日志分页.brudomain/param/OplogPageParam.javadomain/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 客户管理\客户详情\关联商机\关联商机页签.bruport/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.javaMemberAddDTO.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 标注:原型另有职务/加入时间两列——memberRolecreateTime 均不在此 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.pageProjectsCustomerProjectQueryPort.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 的漂移证据)