10 KiB
客户业务(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: 分配(那是公海/日常分配动作)、转交
超期未跟进提醒:
客户规则族两规则之一(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 资源文件