ai_scheduler/internal/data/impl/advice_follow_impl.go

175 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}