Files
customer-server/dao/speechcraft_dao.go
2026-03-14 10:02:49 +08:00

302 lines
8.2 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 dao
import (
"context"
"customer-server/model/dto"
"customer-server/model/entity"
"strings"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/db/mongo"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/grand"
"go.mongodb.org/mongo-driver/v2/bson"
)
var Speechcraft = new(speechcraft)
type speechcraft struct{}
// Insert 插入话术
func (d *speechcraft) Insert(ctx context.Context, data *entity.Speechcraft) (id bson.ObjectID, err error) {
// 统一使用mongo.DB().Insert自动清除缓存
// service层已经设置了TenantIdmongo.DB().Insert不会覆盖已有值
ids, err := mongo.DB().Insert(ctx, []interface{}{data}, entity.SpeechcraftCollection)
if err != nil {
return
}
if len(ids) > 0 {
if oid, ok := ids[0].(bson.ObjectID); ok {
id = oid
data.Id = &oid // 取地址赋值给指针类型
}
}
return
}
// Update 更新话术
func (d *speechcraft) Update(ctx context.Context, req *dto.UpdateSpeechcraftReq) (err error) {
objectId, err := bson.ObjectIDFromHex(req.Id)
if err != nil {
return
}
updateFields := bson.M{}
if !g.IsEmpty(req.Tag) {
updateFields["tag"] = req.Tag
}
if !g.IsEmpty(req.Content) {
updateFields["content"] = req.Content
}
// 状态机字段
if req.Stage != nil {
updateFields["stage"] = *req.Stage
}
if req.Status != nil {
updateFields["status"] = *req.Status
}
if req.Keywords != nil {
updateFields["keywords"] = req.Keywords
}
if req.NextStage != nil {
updateFields["nextStage"] = *req.NextStage
}
if req.Platform != nil {
updateFields["platform"] = *req.Platform
}
if len(updateFields) > 0 {
_, err = mongo.DB().Update(ctx, bson.M{"_id": objectId, "isDeleted": false}, bson.M{"$set": updateFields}, entity.SpeechcraftCollection)
}
return
}
// Delete 软删除话术
func (d *speechcraft) Delete(ctx context.Context, req *dto.DeleteSpeechcraftReq) (err error) {
objectId, err := bson.ObjectIDFromHex(req.Id)
if err != nil {
return
}
filter := bson.M{"_id": objectId, "isDeleted": false}
update := bson.M{"$set": bson.M{"isDeleted": true, "updatedAt": gtime.Now().Time}}
_, err = mongo.DB().Update(ctx, filter, update, entity.SpeechcraftCollection)
if err != nil {
return gerror.Wrap(err, "删除话术失败")
}
return
}
// buildListFilter 构建列表查询的过滤条件
func (d *speechcraft) buildListFilter(req *dto.ListSpeechcraftReq) bson.M {
filter := bson.M{"isDeleted": false}
if !g.IsEmpty(req.Tag) {
filter["tag"] = bson.M{"$regex": req.Tag}
}
if !g.IsEmpty(req.Content) {
filter["content"] = bson.M{"$regex": req.Content}
}
if req.Stage != nil {
filter["stage"] = *req.Stage
}
if !g.IsEmpty(req.Platform) {
filter["platform"] = req.Platform
}
return filter
}
// checkTotalCount 检查总数
func (d *speechcraft) checkTotalCount(ctx context.Context, filter bson.M) (total int64, err error) {
total, err = mongo.DB().Count(ctx, filter, entity.SpeechcraftCollection)
return
}
// List 获取话术列表(排除已删除)
func (d *speechcraft) List(ctx context.Context, req *dto.ListSpeechcraftReq) (list []*entity.Speechcraft, total int64, err error) {
// 构建查询过滤条件
filter := d.buildListFilter(req)
// 检查总数
total, err = d.checkTotalCount(ctx, filter)
if err != nil {
return
}
// 分页参数处理
pageNum := req.PageNum
if pageNum <= 0 {
pageNum = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
// 使用统一的mongo.DB().Find方法支持分页和排序
page := &beans.Page{
PageNum: int64(pageNum),
PageSize: int64(pageSize),
}
orderBy := []beans.OrderBy{
{Field: "createdAt", Order: beans.Desc}, // 按创建时间倒序
}
_, err = mongo.DB().Find(ctx, filter, &list, entity.SpeechcraftCollection, page, orderBy)
return
}
// MatchByStage 根据阶段和用户输入匹配话术
// 匹配逻辑:阶段匹配 + 行为匹配(可选)+ 关键字匹配(可选)
// 从匹配结果中随机选择一条(话术池随机)
func (d *speechcraft) MatchByStage(ctx context.Context, stage int, status, content, platform string) (script *entity.Speechcraft, err error) {
// 查询该阶段的所有话术
filter := bson.M{
"stage": stage,
"isDeleted": false,
}
if !g.IsEmpty(platform) {
filter["platform"] = platform
}
var list []*entity.Speechcraft
// 使用mongo.DB().Find会自动从token或accountName获取tenantId并过滤
// 查询所有匹配的话术(不分页)
page := &beans.Page{
PageNum: 1,
PageSize: 10000, // 话术匹配场景设置足够大的PageSize
}
orderBy := []beans.OrderBy{} // 无需排序,后续会随机选择
if _, err = mongo.DB().Find(ctx, filter, &list, entity.SpeechcraftCollection, page, orderBy); err != nil {
return nil, err
}
// 收集所有匹配的话术
matched := make([]*entity.Speechcraft, 0, len(list))
for _, item := range list {
// 行为匹配(空=任意行为都匹配)
if !g.IsEmpty(item.Status) && item.Status != status {
continue
}
// 关键字匹配(空=任意内容都匹配)
if len(item.Keywords) > 0 && !d.matchKeywords(content, item.Keywords) {
continue
}
// 匹配成功,加入候选池
matched = append(matched, item)
}
// 从候选池随机选择一条
if len(matched) > 0 {
script = matched[grand.Intn(len(matched))]
}
return
}
// matchKeywords 检查内容是否包含任一关键字
func (d *speechcraft) matchKeywords(content string, keywords []string) bool {
for _, kw := range keywords {
if g.IsEmpty(kw) {
continue
}
if strings.Contains(content, kw) {
return true
}
}
return false
}
// FindByTag 根据tag查询话术用于去重检查同一租户下tag唯一
func (d *speechcraft) FindByTag(ctx context.Context, tag string) (speechcraft *entity.Speechcraft, err error) {
filter := bson.M{
"tag": tag,
"isDeleted": false,
}
err = mongo.DB().FindOne(ctx, filter, &speechcraft, entity.SpeechcraftCollection)
if err != nil {
if err.Error() == "mongo: no documents in result" {
return nil, nil
}
return nil, err
}
return
}
// FindByTagAndPlatform 根据tag和platform查询话术用于去重检查
func (d *speechcraft) FindByTagAndPlatform(ctx context.Context, tag, platform string) (speechcraft *entity.Speechcraft, err error) {
filter := bson.M{
"tag": tag,
"platform": platform,
"isDeleted": false,
}
err = mongo.DB().FindOne(ctx, filter, &speechcraft, entity.SpeechcraftCollection)
if err != nil {
if err.Error() == "mongo: no documents in result" {
return nil, nil
}
return nil, err
}
return
}
// GetById 根据ID查询话术
// 使用 MongoDAO不需要token验证
func (d *speechcraft) GetById(ctx context.Context, id string) (speechcraft *entity.Speechcraft, err error) {
objectId, err := bson.ObjectIDFromHex(id)
if err != nil {
return
}
filter := bson.M{"_id": objectId, "isDeleted": false}
var result entity.Speechcraft
err = MongoDAO.FindOne(ctx, filter, &result, entity.SpeechcraftCollection)
if err != nil {
return nil, err
}
// 如果未找到记录result 是零值
if result.Id.IsZero() {
return nil, nil
}
speechcraft = &result
return
}
// UpdateEntity 更新话术实体(用于绑定/解绑/同步等场景)
func (d *speechcraft) UpdateEntity(ctx context.Context, speechcraft *entity.Speechcraft) (err error) {
filter := bson.M{"_id": speechcraft.Id, "isDeleted": false}
// 将实体转换为bson.M
updateDoc := bson.M{}
data, _ := bson.Marshal(speechcraft)
bson.Unmarshal(data, &updateDoc)
_, err = mongo.DB().Update(ctx, filter, bson.M{"$set": updateDoc}, entity.SpeechcraftCollection)
return
}
// FindByStage 查询指定阶段的所有话术
func (d *speechcraft) FindByStage(ctx context.Context, stage int, platform string) (list []*entity.Speechcraft, err error) {
filter := bson.M{
"stage": stage,
"isDeleted": false,
}
if !g.IsEmpty(platform) {
filter["platform"] = platform
}
// 使用统一的mongo.DB().Find方法支持分页和排序
page := &beans.Page{
PageNum: 1,
PageSize: 10000, // 查询所有话术(不分页)
}
orderBy := []beans.OrderBy{
{Field: "priority", Order: beans.Desc}, // 按优先级倒序
}
_, err = mongo.DB().Find(ctx, filter, &list, entity.SpeechcraftCollection, page, orderBy)
return
}