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.
 
 
 
 
 

9.3 KiB

10 — 客户规则族「超期未跟进提醒」(crm-rule 全局单例)

Type: research Status: resolved Blocked by:

问题

crm-rule 落客户规则族第三个 tab(线索/商机/客户并列),本图**只做「超期未跟进提醒」**一项,全局单例配置(不套 V-CONFIG 版本化,chart D4)。「客户查重设置」不做(chart D6)。

一手证据(蓝湖缓存 txt)

  • a7-3-3-1_客户管理设置_超期未跟进提醒_.txt — 全部 spec 都在这里
  • a7-3-3-2_客户管理设置_客户查重设置_.txt划出不读(查重规则写死在新增流程,票 04)

已知事实(原型明文)

  • 全局单例:非按阶段/按部门分别配,仅后台管理面板可见。
  • 顶部总开关:控制整个超期未跟进提醒功能启用/禁用。
  • 两配置项
    • 首次触发时间:客户超过 N 天未跟进→触发首次提醒(默认 30 天);含独立启用开关。
    • 二次提醒间隔时间:首次后每隔 M 天再提醒(默认 7 天);含独立启用开关。
  • 提醒推送渠道:钉钉推送 + 应用内通知(对称商机票 05 的通知落点)。
  • 交互:保存/取消(取消恢复上次保存的配置)。

待拍板点

  1. 数据模型
    • 推荐 A(单行配置表) customer_reminder_rule(id=1 fixed / master_enabled / first_trigger_enabled / first_trigger_days / second_interval_enabled / second_interval_days / updater / update_time)。
    • 推荐 B(键值配置) 复用某个 platform_config 表按 key 存。
    • 我倾向 A(单行表,与商机 V-CONFIG 表族对称位置摆,只是不做版本,操作更直观)。
  2. 触发口径:「超过 N 天未跟进」=now - last_valid_follow_time ≥ Nlast_valid_follow_time = 客户下最新一条 customer_follow.create_time(或字段编辑时间?→ 推荐仅跟进记录驱动,字段编辑不算——对齐商机票 05 的 last_valid_follow_time 但商机含字段编辑;这里更保守)。
  3. 计时锚点写入customer.last_valid_follow_time 冗余到主表(新写跟进时刷新);对称商机主表 last_valid_follow_time。→ 回填票 02 加此字段。
  4. 调度:定时任务(Quartz / Spring Schedule)每天跑一次;命中的客户写「待发通知」表让通知模块消费(对称商机票 05 的 remind 待发通知)。
  5. 通知去重:一个客户当天只触发一次(首次或二次,取其一)。
  6. 无 owner 的客户(在公海)不参与超期提醒(对谁提醒?→ 无接收人)。
  7. 禁用后:定时任务扫到总开关=禁用 → 跳过整轮;启用后从下一轮开始。
  8. 规则生效时机:chart Q4-补 用户特意强调「规则在客户新增时生效」——我的解读是新增客户时就把 last_valid_follow_time 初始化为 create_time(客户创建后开始计时,30 天没跟进就首次提醒)。待和用户确认这个解读——见 grill 挂号。

Answer

Status: resolved

A. 结论速览

客户「超期未跟进提醒」= crm-rule 全局单例配置 + crm-customer 待发通知 + 调度 Job,三层对称商机「回收提醒」范式,但配置形态是单例(不套 V-CONFIG 版本化,chart D4)。「客户查重设置」不做(chart D6)。

落点 对称对象
配置 crm-rule customer_reminder_rule(单行表,id 固定=1) opportunity_pool_rule(但非版本化)
待发通知 crm-customer customer_pending_notice opportunity_pending_notice
调度 crm-customer CustomerReminderJob(@Scheduled) OpportunityMaintenanceJob

B. 配置表 customer_reminder_rule(crm-rule)

全局单例:单行表,id 固定为 1,种子初始化器 CustomerReminderRuleInitializer 植入默认行(对称 StageTemplateInitializer 兜底范式)。无版本号、无状态机、无 rule_code。

Java 字段 列名 类型 默认 说明
id id bigint 固定 1 单例行主键
masterEnabled master_enabled tinyint 1 总开关:1启用 0禁用(禁用后 Job 整轮跳过)
firstTriggerEnabled first_trigger_enabled tinyint 1 首次触发独立开关
firstTriggerDays first_trigger_days int 30 首次触发天数(>0)
secondIntervalEnabled second_interval_enabled tinyint 1 二次提醒独立开关
secondIntervalDays second_interval_days int 7 二次提醒间隔天数(>0)
updateBy / updateTime update_by / update_time BaseEntity 继承

孤立单大写字母检查(AGENTS.md):masterEnabledmaster_enabledfirstTriggerDaysfirst_trigger_dayssecondIntervalDayssecond_interval_days——均为「完整词 + 完整词」,无需 @Column(name=...)

校验规则(服务层,对称商机规则):

  • firstTriggerDays > 0secondIntervalDays > 0
  • 保存 = 全量覆盖(无草稿/发布态,保存即生效)。
  • 只后台管理面板可见(RBAC 功能权限 customer.reminder.rule)。

契约

GET  /api/rule/customer/reminder          → 读当前配置(单行)
PUT  /api/rule/customer/reminder          → 保存配置(全量覆盖,记录操作日志)

C. 触发口径(只前进、确定性提醒链)

计时锚点 = customer.last_valid_follow_time(票 02 已建;新建客户时初始化为 create_time,每次写跟进记录刷新——票 07)。

提醒链(确定性,可从锚点直接推导,无需存「已发首次」状态):

第 0 次(首次)  plan = anchor + first_trigger_days
第 1 次(二次)  plan = anchor + first_trigger_days + 1 × second_interval_days
第 k 次(二次)  plan = anchor + first_trigger_days + k × second_interval_days
  • 幂等键 = (customer_id, notice_type, plan_notify_time),同计划时刻永不重写(对称商机 uk_pending_notice_dedupe)。
  • Job 每轮对每个客户只补下一条未建的到期提醒(不批量堆历史)。
  • 跟进刷新锚点 → 旧锚点的未发提醒置已失效(对称商机回收时 invalidate 语义);新锚点重新从首次开始计时。

参与范围(扫表条件):

  • archive_status = 有效(1)(归档/合并不提醒)
  • owner_user_id IS NOT NULL(公海无接收人不提醒,chart 票 10 待拍板点 6)
  • last_valid_follow_time IS NOT NULL(防御脏数据)
  • master_enabled = 1 且(first_trigger_enabled=1second_interval_enabled=1

D. 待发通知表 customer_pending_notice(crm-customer)

对称 OpportunityPendingNotice,字段集一致:

Java 字段 列名 类型 说明
id id bigint ASSIGN_ID 主键
customerId customer_id bigint 客户 ID
noticeType notice_type varchar(32) OVERDUE_REMIND(本期唯一类型)
targetUserId target_user_id bigint 应通知对象 = 客户销售负责人(快照)
planNotifyTime plan_notify_time datetime 应发提醒时刻(= 提醒链推导时刻)
payloadJson payload_json text 上下文 JSON(见下)
noticeStatus notice_status tinyint 0待发 1已发 2已失效
  • 唯一约束UNIQUE(customer_id, notice_type, plan_notify_time)
  • 索引idx(customer_id, notice_status) 供消费者扫待发;idx(notice_status, plan_notify_time) 供补发调度。
  • payload JSON(对称商机无「剩余天数」伪状态):{ remindSeq(0=首次/k=第k次二次), overdueDays(已无跟进天数), anchor(last_valid_follow_time), firstTriggerDays, secondIntervalDays }

本期只落待发记录,不直接发送;平台通知能力(钉钉 + 应用内通知)spin-out,就绪后由另一消费者扫 notice_status=待发 补发(对称商机票 05 拍板)。


E. 调度 Job CustomerReminderJob(crm-customer)

  • cron${crm.customer.reminder-cron:0 0 4 * * ?}(默认凌晨 4 点,错开线索 2 点 / 商机 3 点)。
  • 流程(对称 OpportunityMaintenanceJob,但无回收分支):
    1. customer_reminder_rule 单行;master_enabled=0 或无配置 → 整轮跳过。
    2. 扫参与范围客户(见 C)。
    3. 对每个客户:算下一条未建的到期提醒plan ≤ now),若有则落 customer_pending_notice(幂等:已存在同 plan 时刻跳过)。
    4. 锚点已被刷新的客户:把旧锚点未发的 OVERDUE_REMIND 置已失效。
  • 不发送:只落待发记录(E 末尾同)。

F. 规则生效时机(chart Q4-补 用户强调,已确认)

用户原话「规则在客户新增的时候生效」——最终解读:新建客户时把 last_valid_follow_time 初始化为 create_time,客户创建后即开始计时,30 天(首次触发默认值)无跟进 → 首次提醒。回填票 02(已落 last_valid_follow_time 字段)+ 票 07(新建跟进时刷新)。


G. 回填 / 下游影响

  • 票 02last_valid_follow_time 已含( 已落)。
  • 票 07:跟进记录写入时刷新 last_valid_follow_time
  • 票 01:CONTEXT.md 增术语「超期未跟进提醒」。
  • crm-rule/CONTEXT.md:增客户规则族段(对称商机规则段)。