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层已经设置了TenantId,mongo.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 }