|
|
|
|
# 客户业务(crm-customer)
|
|
|
|
|
|
|
|
|
|
客户的全生命周期业务域:客户实体与三阶段(潜在 / 重潜 / 已成交)、三个 workspace(客户总览 / 我的客户 / 客户公海)、新增 / 编辑 / 导入、联系人子实体、跟进记录与操作日志(`customer_oplog` **无限期保留**,审计日志不设清理/冷热分离,§5.5)、客户交割(离职/调岗批量换主)、战略协议(客户详情页签 6,单表 CRUD + 主表等级快照),以及客户规则族**两族规则**(「超期未跟进提醒」+「客户查重设置」,均 crm-rule 全局单例)。列表三形态(列表/分屏/看板)的形态 / 列 / 自定义视图偏好接入平台 crm-preference(scopeKey `customer.overview/mine/pool` + `contact.list`,业务侧零偏好端点)。依赖方向单向 `crm-opportunity → crm-customer`(商机读客户、建关联);`crm-customer → crm-rule → (crm-auth / crm-dict / crm-file)`;客户读「关联商机」走下游反查上游(同库单体阶段可接受,见 ADR-0029 循环依赖技术债)。访问控制走三机制模型,`@DataScope(module="customer", ownerColumn="owner_user_id", deptColumn="owner_dept_id")`。
|
|
|
|
|
|
|
|
|
|
## Language
|
|
|
|
|
|
|
|
|
|
**客户**:
|
|
|
|
|
`crm-customer` 的核心聚合根。归属由 `owner_user_id`(销售负责人)+ `owner_dept_id`(销售部门快照)表达,owner 空 = 在客户公海;档案正交维度 `archive_status`(有效 / 已归档)独立于归属(D19 去掉「已被合并」,合并 Out-of-scope)。阶段字段 `customer_stage`(潜在 / 重潜 / 已成交)只前进不回退,由外部成功结果驱动。
|
|
|
|
|
_Avoid_: 线索(转化前)、商机(下游)、联系人(子实体)
|
|
|
|
|
|
|
|
|
|
**销售负责人**:
|
|
|
|
|
客户的 `owner_user_id`,当前唯一持有该客户的销售。客户「我的客户」视图的第一层过滤依据(负责人 = 当前用户);抛公海 / 交割 / 分配时变更。协同人可见客户但不视为负责人。
|
|
|
|
|
_Avoid_: 领取人(线索术语)、创建人、持有者
|
|
|
|
|
|
|
|
|
|
**销售部门**:
|
|
|
|
|
客户的 `owner_dept_id`,负责人所在部门的快照,随负责人变更而更新(非全生命周期不变,区别于线索的 `dept_id`)。是数据范围过滤的部门维度。
|
|
|
|
|
_Avoid_: 归属部门、池归属部门、owner_dept
|
|
|
|
|
|
|
|
|
|
**协同人**:
|
|
|
|
|
客户团队中非负责人的成员。协同人可见该客户(进「我的客户」),但不得归档 / 合并。抛公海时清空协同人;交割时不自动恢复原协同人(保留原有其他协同人)。
|
|
|
|
|
_Avoid_: 团队成员(泛指)、共享人
|
|
|
|
|
|
|
|
|
|
**客户阶段**:
|
|
|
|
|
`customer_stage` 单字段(潜在 1 / 重潜 2 / 已成交 3),**只前进不回退、不可手动改**,由外部成功结果驱动:潜在 →(任一方案卡**提交并报备成功**)→ 重潜 →(任一项目首次中标赢单)→ 已成交。**本期两个跃迁均 seam 化不自动触发**(D20 改票 B):报备成功链路本期不可达(不用「已提交」凑合,避免造出错误的重潜状态),A5 项目模块未建;`CustomerStagePort.promote` 契约/只前进 CAS/幂等 全部建好,等对应链路就绪再由其调用。**本期客户阶段实际只有「潜在」**。
|
|
|
|
|
_Avoid_: 客户状态、归属状态、客户类型
|
|
|
|
|
|
|
|
|
|
**档案状态**:
|
|
|
|
|
`archive_status` 单字段(有效 1 / 已归档 2),正交于归属与阶段(D19 去掉「已被合并」态,合并 Out-of-scope,A4-6 不做)。**已归档 = 删除语义(软删)**:默认排除于列表筛选、加 `archive_status` 筛选可查,可恢复(恢复回有效)。仅销售负责人可归档。**§5.3 拍板 A(一期只做归档)**:**不提供任何前端「删除客户」入口**——系统级 `deleted` 软删标志仅在合并时由系统置 1,不开放手动删除、无前端删除按钮;DB 不做物理删除。归档与合并是仅有的两种"移除"用户动作,且归档可恢复、合并本体 Out-of-scope。
|
|
|
|
|
_Avoid_: 删除、禁用、作废
|
|
|
|
|
|
|
|
|
|
**客户目录**:
|
|
|
|
|
客户域发给商机域的窄只读契约 `CustomerCatalogPort`(`com.crm.customer.port`,ADR-0033,对称线索域 `ConvertibleLeadCatalogPort`):三方法 `search`(候选池:名称/联系人姓名/电话模糊 + 类型精确 + 分页)/ `contactsOf`(按客户带联系人,商机字段17级联列)/ `requireLive`(有效客户存在性校验,返回五字段投影作名快照真源)。**归档口径端口钉死**:目录 = `archive_status=有效` 的客户——归档客户不出现在候选池、不可被新关联(存量已关联行不动);排除已关联客户(`excludeCustomerIds`)由消费方传入(商机候选池语义,端口不知 opportunity 表)。目录异常 `CustomerCatalogException` 不跨 seam,消费方翻译为自己的错误码。
|
|
|
|
|
_Avoid_: 宽服务直连(商机侧不得 import `com.crm.customer.service.*` / `domain.*`,跨域只读必须走 port)、客户查询接口(泛称)
|
|
|
|
|
|
|
|
|
|
**客户公海**:
|
|
|
|
|
`owner_user_id` 为空 且 `archive_status=有效` 的客户的**逻辑视图**(无独立池实体,对称商机公海)。进入公海 = 清 owner + 协同人;领取 = 当前用户成为 owner。有进行中商机 / 项目的客户不得抛公海(项目侧留 seam)。
|
|
|
|
|
_Avoid_: 公海池实体(那是线索规则域概念)、池
|
|
|
|
|
|
|
|
|
|
**客户总览**:
|
|
|
|
|
管理视角 workspace,`@DataScope` 覆盖组织范围的未删除、未进入公海客户(含有效 / 已归档两态;默认只列 `archive_status=有效`,已归档走筛选)。管理职务本身不扩大「我的客户」数据范围。**看板 summary/cards 与列表同口径**(同一份列表数据三种渲染,对称商机 D-15;公共 WHERE 在 `CustomerMapper` 常量单一事实源,守恒由集成测试断言)。
|
|
|
|
|
_Avoid_: 全部客户、全局列表
|
|
|
|
|
|
|
|
|
|
**我的客户**:
|
|
|
|
|
销售视角 workspace,当前用户为销售负责人**或协同人** 且 `archive_status=有效` 且非公海的客户。已归档客户 = `archive_status=已归档` 的客户(负责人为当前用户),列表加筛选展示、**不做独立子页/独立 tab**(D19:删除了「已被合并」态、「已归档」用筛选条件承载)。
|
|
|
|
|
_Avoid_: 私海(那是线索术语)
|
|
|
|
|
|
|
|
|
|
**联系人**:
|
|
|
|
|
`customer_contact` 子实体,每条必属一个客户(`customer_id` NOT NULL),无孤立联系人。无独立 ACL,权限继承所属客户。电话查重=可配置软提示(跟随 crm-rule 客户查重设置「联系电话」开关,不建电话唯一索引,D26)、同客户内电话不重复为表单级校验;职务先存文本名 + 可选标准职务 id/类别/层级占位(二级联动图谱留后续)。
|
|
|
|
|
_Avoid_: 线索联系人、决策人(那是客户字段)
|
|
|
|
|
|
|
|
|
|
**人脉关系单位**:
|
|
|
|
|
客户下的关系单位名标签集(如「XX 集团」),是**纯文本标签**,非关联到 CRM 客户实体。
|
|
|
|
|
_Avoid_: 关联客户、子客户(父客户关联是另一字段)
|
|
|
|
|
|
|
|
|
|
**客户交割**:
|
|
|
|
|
销售因员工离职 / 岗位调动 / 区域调整,把本人名下客户批量交给直属销售总监,再由总监分配给具体销售。两段式:发起交接(owner → 总监,整批原子)+ 客户分配(owner → 销售)。同步变更关联商机 / 项目负责人(项目侧留 seam)。只允许离职/调动/调整的销售本人发起。
|
|
|
|
|
_Avoid_: 分配(那是公海/日常分配动作)、转交
|
|
|
|
|
|
|
|
|
|
**归属写入器**:
|
|
|
|
|
`CustomerOwnershipWriter`(`com.crm.customer.service`,ADR-0034,第四轮架构审查候选 #1):customer 表 **owner 列族(owner 四列快照 + `last_valid_follow_time` 锚点 + `enter_pool_time`)的唯一写入口**,具体 `@Component` 零接口(对称 `CustomerOplogWriter` / `CustomerPendingNoticeWriter` 家族)。三动词:`initOwnerOnCreate`(新建 mutate-only,owner 空 = 公海分支:清快照 + enterPoolTime=now + 锚点 NULL)/ `changeOwner`(守卫 67012 + 快照 + **锚点空则赋当下、非空保留** + enterPoolTime wrapper 显式清空 + CAS 落库 67005)/ `releaseToPool`(清 owner 两列 + enterPoolTime=now + 锚点保留;协同人清空与商机守卫留调用方)。**锚点统一规则**(D25):设主时锚点空则赋当下、非空保留——五换主场景(新建指派/领取/分配/交割两段/导入建档)行为不变。编号续号不入本组件(变更原因分离,归 `CustomerNoGenerator`:容错重起 1 + 封顶 9999)。
|
|
|
|
|
_Avoid_: 在服务里手搓 owner 快照/锚点/守卫/CAS 空列(写口径已收编,新换主点必须走 writer)、换主后平式 updateById 清列(MP NOT_NULL 策略跳过 null——assign/交割的 enter_pool_time 契约违约即此坑)
|
|
|
|
|
|
|
|
|
|
**超期未跟进提醒**:
|
|
|
|
|
客户规则族两规则之一(crm-rule 全局单例配置)。客户 `last_valid_follow_time`(新建 = create_time,每次跟进刷新)距今超过首次触发天数(默认 30)触发首次提醒,之后每隔二次提醒间隔(默认 7)重复提醒。只落待发通知 `customer_pending_notice`,不直接发送(平台通知能力 spin-out)。公海 / 已归档客户不参与(「已被合并」态已随 D19 取消)。
|
|
|
|
|
_Avoid_: 超时回收(那是线索/商机公海回收)、逾期
|
|
|
|
|
|
|
|
|
|
**待发通知写入器**:
|
|
|
|
|
`CustomerPendingNoticeWriter`(`com.crm.customer.service`,架构审查候选 #3):`customer_pending_notice` 的**唯一写入口**,薄组件——只把本表形状(biz 列 = customer_id)钉给 crm-base 引擎 `PendingNoticeWriter`(见 crm-base CONTEXT「待发通知写入引擎」)。提醒链推导(k 递增找下一条未占用)与**锚点失效判定**(payload.anchor ≠ 当前锚点挑 staleIds)是域内真差异、留 `CustomerReminderJob`;置失效动作交引擎(只动 PENDING)。payload 内容(remindSeq/anchor/规则快照)域内组装传 Map,序列化兑底归引擎;通知状态常量已收编引擎 `STATUS_*`(域内不再重复定义)。
|
|
|
|
|
_Avoid_: 在 Job 里裸拼 noticeMapper insert/update(写口径已收编,新写入点必须走 writer)、手拼 plan 字符串占位集(planOccupied 接管)、域内重定义通知状态常量
|
|
|
|
|
|
|
|
|
|
**客户查重设置**:
|
|
|
|
|
客户规则族两规则之二(crm-rule 全局单例 `customer_dedup_rule`,返工票 04 从「写死」改为后台可配置,D4-rev2)。总开关 + 名称开关(精确 / 模糊 + 相似度阈值,出厂模糊 80)+ 电话开关;统一社会信用代码为系统强制规则、恒开无配置项。消费边界 = 新增 / 编辑内建查重(`RuleBackedCustomerDedupSettingProvider` 读单例驱动,单例缺失回退出厂值);A4-6 独立查重页 / 合并留作未来消费者。
|
|
|
|
|
_Avoid_: 查重列表(A4-6 划出)、合并
|
|
|
|
|
|
|
|
|
|
**战略协议**:
|
|
|
|
|
客户详情页签 6(返工票 12 上线):`customer_agreement` 单表 CRUD(新增/编辑/删除/详情/页签列表五端点),`agreementLevel` 1~3 必填、归属不可换客户。新增/编辑/删除联动刷新客户主表 `strategic_agreement_level` 快照(删除=清空),oplog 记 `AGREEMENT_ADD/EDIT/DELETE`。
|
|
|
|
|
_Avoid_: 合同(那是商机/项目域概念)、VIP 等级
|
|
|
|
|
|
|
|
|
|
**H2 测试 schema**:
|
|
|
|
|
客户域全部 16 张表(含 opportunity / opportunity_customer / user_saved_view 三张跨域投影表)的 H2 测试 DDL 单一事实源 = `CustomerH2Schema` 常量类(test 源码集);集成测试自举统一走 `CustomerH2Harness.bootstrap(库名, mapper...)`。customer 主表列集由 `CustomerH2SchemaConsistencyTest` 对实体双向对账看守(实体加列漏改 schema 时对账红,不再等半夜 Unknown column)。商机/偏好模块表结构变更须同步 `CustomerH2Schema` 跨域表段落。
|
|
|
|
|
_Avoid_: 测试类内联 DDL(历史 12 份副本之一漂移 22 列长期假绿)、schema.sql 资源文件
|