package impl import ( "ai_scheduler/internal/data/model" "ai_scheduler/tmpl/dataTemp" "ai_scheduler/utils" "context" "time" ) // AdviceFollowImpl 统管两张表的数据访问: // - ai_advice_remind_task(定时提醒) // - ai_advice_deal_lead(成交线索) // DataTemp 基础模型仅占位,所有方法显式 Model(&具体结构体{})。 type AdviceFollowImpl struct { dataTemp.DataTemp } func NewAdviceFollowImpl(db *utils.Db) *AdviceFollowImpl { return &AdviceFollowImpl{ DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceRemindTask)), } } // ================= 定时提醒 ai_advice_remind_task ================= // RemindAdd 新增一条定时提醒。 func (a *AdviceFollowImpl) RemindAdd(ctx context.Context, record *model.AiAdviceRemindTask) (int64, error) { if err := a.Db.WithContext(ctx).Create(record).Error; err != nil { return 0, err } return record.Id, nil } // RemindExists 去重:同客户 + 到点时间 + 标题 是否已存在。 func (a *AdviceFollowImpl) RemindExists(ctx context.Context, clientId string, dueAt time.Time, title string) (bool, error) { var count int64 err := a.Db.WithContext(ctx).Model(&model.AiAdviceRemindTask{}). Where("client_id = ? AND due_at = ? AND title = ?", clientId, dueAt, title). Count(&count).Error return count > 0, err } // FindDueRemind 取到点且待执行的提醒(供 cron 每分钟执行)。 func (a *AdviceFollowImpl) FindDueRemind(ctx context.Context, now time.Time) ([]model.AiAdviceRemindTask, error) { var list []model.AiAdviceRemindTask err := a.Db.WithContext(ctx). Where("status = ? AND due_at <= ?", model.FollowStatusPending, now). Order("due_at asc").Limit(200). Find(&list).Error return list, err } // RemindList 定时提醒列表(按到点时间倒序,支持项目/客户/状态过滤 + 分页)。 func (a *AdviceFollowImpl) RemindList(ctx context.Context, projectId int32, clientId, wxid, status string, page, pageSize int) ([]model.AiAdviceRemindTask, error) { query := a.Db.WithContext(ctx).Model(&model.AiAdviceRemindTask{}) if projectId != 0 { query = query.Where("project_id = ?", projectId) } if clientId != "" { query = query.Where("client_id = ?", clientId) } if wxid != "" { query = query.Where("wxid = ?", wxid) } if status != "" { query = query.Where("status = ?", status) } query = query.Order("due_at desc") if pageSize > 0 { if page < 1 { page = 1 } query = query.Offset((page - 1) * pageSize).Limit(pageSize) } else { query = query.Limit(200) } var list []model.AiAdviceRemindTask err := query.Find(&list).Error return list, err } // RemindUpdateStatusIfPending 仅当提醒仍为 pending 时更新状态(幂等),返回受影响行数。 func (a *AdviceFollowImpl) RemindUpdateStatusIfPending(ctx context.Context, id int64, status, result, errMsg string) (int64, error) { now := time.Now() set := map[string]interface{}{"status": status, "done_at": now, "update_at": now} if result != "" { set["result"] = result } if errMsg != "" { set["err_msg"] = errMsg } res := a.Db.WithContext(ctx).Model(&model.AiAdviceRemindTask{}). Where("id = ? AND status = ?", id, model.FollowStatusPending). Updates(set) return res.RowsAffected, res.Error } // ================= 成交线索 ai_advice_deal_lead ================= // LeadAdd 新增一条成交线索。 func (a *AdviceFollowImpl) LeadAdd(ctx context.Context, record *model.AiAdviceDealLead) (int64, error) { if err := a.Db.WithContext(ctx).Create(record).Error; err != nil { return 0, err } return record.Id, nil } // LeadExists 去重:同客户 + 线索类型 + 期望时间 + 标题 是否已存在。 func (a *AdviceFollowImpl) LeadExists(ctx context.Context, clientId, leadType string, expectAt time.Time, title string) (bool, error) { var count int64 err := a.Db.WithContext(ctx).Model(&model.AiAdviceDealLead{}). Where("client_id = ? AND lead_type = ? AND expect_at = ? AND title = ?", clientId, leadType, expectAt, title). Count(&count).Error return count > 0, err } // FindActiveLeadsByWxid 取某客户尚未闭环的成交线索(pending),供对话注入。 func (a *AdviceFollowImpl) FindActiveLeadsByWxid(ctx context.Context, wxid string) ([]model.AiAdviceDealLead, error) { var list []model.AiAdviceDealLead if wxid == "" { return list, nil } err := a.Db.WithContext(ctx). Where("wxid = ? AND status = ?", wxid, model.FollowStatusPending). Order("expect_at asc").Limit(20). Find(&list).Error return list, err } // LeadList 成交线索列表(按期望时间倒序,支持项目/客户/状态/线索类型过滤 + 分页)。 func (a *AdviceFollowImpl) LeadList(ctx context.Context, projectId int32, clientId, wxid, status, leadType string, page, pageSize int) ([]model.AiAdviceDealLead, error) { query := a.Db.WithContext(ctx).Model(&model.AiAdviceDealLead{}) if projectId != 0 { query = query.Where("project_id = ?", projectId) } if clientId != "" { query = query.Where("client_id = ?", clientId) } if wxid != "" { query = query.Where("wxid = ?", wxid) } if status != "" { query = query.Where("status = ?", status) } if leadType != "" { query = query.Where("lead_type = ?", leadType) } query = query.Order("expect_at desc") if pageSize > 0 { if page < 1 { page = 1 } query = query.Offset((page - 1) * pageSize).Limit(pageSize) } else { query = query.Limit(200) } var list []model.AiAdviceDealLead err := query.Find(&list).Error return list, err } // LeadUpdateStatusIfPending 仅当线索仍为 pending 时更新状态/跟进结果(幂等),返回受影响行数。 func (a *AdviceFollowImpl) LeadUpdateStatusIfPending(ctx context.Context, id int64, status, result string) (int64, error) { now := time.Now() set := map[string]interface{}{"status": status, "done_at": now, "update_at": now} if result != "" { set["result"] = result } res := a.Db.WithContext(ctx).Model(&model.AiAdviceDealLead{}). Where("id = ? AND status = ?", id, model.FollowStatusPending). Updates(set) return res.RowsAffected, res.Error }