package jobs import ( "ai_scheduler/internal/biz" "ai_scheduler/internal/config" "ai_scheduler/internal/entitys" "context" "fmt" "time" "github.com/robfig/cron/v3" ) // AdviceTaskJob 数字销售定时任务 // 1. 客户等级评估:每日 daily_evaluate_hour 点(默认 0 点)评估熟客/意向/沉睡/非客户; // 2. 熟客超时兜底:每 2 分钟扫描「人工超时未回复」的熟客,执行礼貌性回复与简单解答; // 3. 对话完成画像迭代:每 10 分钟扫描闲置结束的对话,分析聊天记录完善客户画像(同时抽取时间约定→跟进线索); // 4. 主动触达:每小时扫描生日/新活动,AI 主动发起对话(受活跃时段与总开关限制); // 5. 跟进线索到点执行:每分钟扫描到期约定,remind_customer 由 AI 提醒客户、offline_arrival 转后台待办。 type AdviceTaskJob struct { cfg *config.Config evaluateBiz *biz.AdviceEvaluateBiz strategyBiz *biz.AdviceStrategyBiz proactiveBiz *biz.AdviceProactiveBiz iterateBiz *biz.AdviceIterateBiz followBiz *biz.AdviceFollowBiz cron *cron.Cron } func NewAdviceTaskJob( cfg *config.Config, evaluateBiz *biz.AdviceEvaluateBiz, strategyBiz *biz.AdviceStrategyBiz, proactiveBiz *biz.AdviceProactiveBiz, iterateBiz *biz.AdviceIterateBiz, followBiz *biz.AdviceFollowBiz, ) *AdviceTaskJob { return &AdviceTaskJob{ cfg: cfg, evaluateBiz: evaluateBiz, strategyBiz: strategyBiz, proactiveBiz: proactiveBiz, iterateBiz: iterateBiz, followBiz: followBiz, // SkipIfStillRunning:上一轮未完成时跳过本轮,避免任务堆积 cron: cron.New(cron.WithChain(cron.SkipIfStillRunning(cron.DefaultLogger))), } } // Start 启动调度(非阻塞;服务退出时调用 Stop) func (j *AdviceTaskJob) Start() { // 1. 客户等级评估:cron 五段式「分 时 日 月 周」,每天 hour 点整执行 hour := j.cfg.Advicer.DailyEvaluateHour if hour < 0 || hour > 23 { hour = 0 } spec := fmt.Sprintf("0 %d * * *", hour) if _, err := j.cron.AddFunc(spec, func() { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) defer cancel() _, _ = j.evaluateBiz.EvaluateAll(ctx, &entitys.AdvicerClientEvaluateReq{}) }); err != nil { // spec 由校验后的 hour 生成,正常不会失败 return } // 2. 熟客超时兜底:每 2 分钟扫描一次 if _, err := j.cron.AddFunc("*/2 * * * *", func() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() _, _ = j.strategyBiz.RegularTimeoutScan(ctx, false) }); err != nil { return } // 3. 对话完成画像迭代:每 10 分钟扫描一次 if _, err := j.cron.AddFunc("*/10 * * * *", func() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute) defer cancel() _, _ = j.iterateBiz.Run(ctx, &entitys.AdvicerIterateRunReq{}) }); err != nil { return } // 4. 主动触达(生日/活动):每小时整点扫描(内部校验活跃时段与开关) if _, err := j.cron.AddFunc("0 * * * *", func() { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) defer cancel() _, _ = j.proactiveBiz.Scan(ctx, &entitys.AdvicerProactiveRunReq{}) }); err != nil { return } // 5. 跟进线索到点执行:每分钟扫描到期约定(AI 提醒客户 / 线下转后台待办) if j.followBiz != nil { if _, err := j.cron.AddFunc("* * * * *", func() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() _, _ = j.followBiz.RunDue(ctx) }); err != nil { return } } j.cron.Start() } // Stop 停止调度 func (j *AdviceTaskJob) Stop() { if j.cron != nil { j.cron.Stop() } }