Appearance
14:保护配置、公海、认领、转移与分享
1. 本模块解决的业务问题
客户资源不能被无限期占用。当前 CRM 用“客户-产品跟进”的保护期和公海机制解决资源流转:
text
创建/认领跟进产品
-> 处于保护期
-> 持续跟进记录可刷新截止时间
-> 到期后变为过期或进入公海
-> 其他人可以认领这套规则不是客户主表的规则,而是 客户 + 产品 + 跟进人 维度的规则。
2. 相关代码和表
| 内容 | Service / 类 | 表 |
|---|---|---|
| 普通保护配置 | CustomerProtectConfigurationServiceImpl | customer_protect_configuration |
| 特殊客户配置 | CustomerProtectSpecialConfigServiceImpl | customer_protect_special_config |
| 产品跟进 | CustomerProductFollowServiceImpl | customer_product_follow |
| 公海 | CustomerProductPublicPoolServiceImpl | customer_product_public_pool |
| 认领历史 | CustomerProductClaimLogServiceImpl | customer_product_claim_log |
| 转移历史 | CustomerTransferLogServiceImpl | customer_transfer_log |
| 时间审计 | CustomerFollowDeadlineLogServiceImpl | customer_follow_deadline_log |
| 定时调度 | job/PublicPoolJob | 调用公海 Service |
3. 保护配置的作用
CustomerProtectConfigurationServiceImpl.saveOrUpdateConfiguration 维护规则参数。产品跟进新增时通过:
java
Map<String, Integer> protectConfig = getProtectConfig(customerId);读取规则。源码中会使用的典型参数包括:
text
newFollowDay 新增跟进的保护天数
maxFollowDay 最大保护天数
maxClaim 最大认领次数
cannotClaimDay 多次掉海后的冻结天数
maxCustomer 单人保护中的客户数量上限
maxCustomerProduct 单人对同一客户可跟进未成交产品数上限
contractApprovalProtect 合同审批中是否保护
dealExpireDowngrade 已成交到期后的降级天数特殊配置按客户维度覆盖或补充通用配置。因此规则读取必须集中在 Service,不能散落在 Controller。
4. 到期进入公海的真实算法
入口:
text
PublicPoolJob
-> CustomerProductPublicPoolServiceImpl.expireFollowProductToPublicPool()任务会按租户循环执行,且每次都清理 TenantContextHolder,避免线程复用导致跨租户污染。
expireFollowProductToPublicPool 的主要逻辑:
text
1. 查询截止时间已到的跟进记录
2. 以 customerId + productId 分组
3. 查询相同客户-产品是否仍有“其他人正在跟进且未到期”的记录
4. 若存在:本条只改为 EXPIRED,不进入公海
5. 若不存在:交给 followProductToPublicPool 批量入公海这样同一个客户-产品只要还有一个有效跟进人,就不会因为另一人的记录到期而被整体释放到公海。
5. 入公海时的特殊分支
followProductToPublicPool(...) 在批量入公海前还会判断:
text
合同审批保护:相关跟进产品正在合同审批时,按配置可能跳过掉海
VIP 已成交:不直接掉海;源码会降级为 A.待签约,重置首次跟进时间与截止时间
正常记录:创建公海快照、更新跟进状态/公海标记、写认领日志与变更日志这说明公海不是简单的 followStatus = EXPIRED;它有独立的公海表用于保留待认领资源和历史关系。
6. 认领、转移、分享的区别
6.1 认领 claim
入口:CustomerProductPublicPoolServiceImpl.claim(CustomerProductFollowDto)。
业务前置条件来自产品跟进 Service:
text
不能超过当前用户最大保护客户数
不能超过同一客户的产品数上限
不能超过同产品认领次数
超过认领次数后,尚在冻结天数内不能再次认领认领成功后要同时更新公海记录和跟进记录,并写入认领日志。它是一个事务性状态迁移,不是复制一条客户记录。
6.2 转移 transfer
入口:CustomerProductFollowServiceImpl.transfer(...)。
转移代表跟进责任从 A 人/部门变更为 B 人/部门。必须记录:旧跟进人、目标跟进人、转移时间和对应产品跟进 id,写入 customer_transfer_log。
6.3 分享 share
入口:CustomerProductFollowServiceImpl.share(...)。
分享不是转移。它允许在规则范围内让其他人参与同一客户产品的跟进,原跟进关系的业务含义仍然存在。实现前必须先读源码确认共享后的状态、负责人和保护期如何处理。
7. Demo 对应的最小业务版本
不要直接做多人共享。按下面的状态机实现:
mermaid
stateDiagram-v2
[*] --> FOLLOWING: 创建或认领
FOLLOWING --> EXPIRED: deadline 到期
EXPIRED --> PUBLIC_POOL: 无任何有效跟进人
PUBLIC_POOL --> FOLLOWING: 用户认领Demo 表建议:
text
demo_protect_config
demo_customer_product_follow
demo_public_pool
demo_claim_log最小规则:
text
新建跟进:deadline = now + config.new_follow_days
定时任务:deadline < now 的 FOLLOWING -> PUBLIC_POOL
认领:PUBLIC_POOL -> FOLLOWING,写 claim_log
限制:同一用户最多 20 条 FOLLOWING当这个闭环正确后,再增加“同客户-产品仍有有效跟进则只过期不掉海”的正式项目规则。
8. 必须验证的边界
- A 和 B 同时跟进同一客户产品,A 到期,B 未到期:A 仅过期,不入公海;
- 所有人都到期:客户产品进入公海;
- 已成交 VIP 到期:按配置降级,而不是直接入公海;
- 认领次数超过上限:拒绝认领;
- 冻结期内:拒绝认领;
- 任务处理多个租户:每个租户结束后上下文必须清理。
9. 源码阅读顺序
text
CustomerProtectConfigurationServiceImpl.saveOrUpdateConfiguration
CustomerProductFollowServiceImpl.getProtectConfig
CustomerProductFollowServiceImpl.checkMaxClaim
CustomerProductFollowServiceImpl.transfer
CustomerProductFollowServiceImpl.share
CustomerProductPublicPoolServiceImpl.claim
CustomerProductPublicPoolServiceImpl.expireFollowProductToPublicPool
CustomerProductPublicPoolServiceImpl.followProductToPublicPool
PublicPoolJob