Files
lckt-server/service/bot/bot.go

249 lines
7.1 KiB
Go
Raw Permalink 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 bot
import (
"context"
"fmt"
"git.echol.cn/loser/lckt/global"
"git.echol.cn/loser/lckt/model/bot"
botReq "git.echol.cn/loser/lckt/model/bot/request"
"go.uber.org/zap"
"gorm.io/gorm"
"strings"
)
type BotService struct{}
// CreateBot 创建机器人记录
// Author [yourname](https://github.com/yourname)
func (btService *BotService) CreateBot(ctx context.Context, bt *bot.Bot) (err error) {
err = global.GVA_DB.Create(bt).Error
return err
}
// DeleteBot 删除机器人记录
// Author [yourname](https://github.com/yourname)
func (btService *BotService) DeleteBot(ctx context.Context, ID string) (err error) {
err = global.GVA_DB.Delete(&bot.Bot{}, "id = ?", ID).Error
return err
}
// DeleteBotByIds 批量删除机器人记录
// Author [yourname](https://github.com/yourname)
func (btService *BotService) DeleteBotByIds(ctx context.Context, IDs []string) (err error) {
err = global.GVA_DB.Delete(&[]bot.Bot{}, "id in ?", IDs).Error
return err
}
// UpdateBot 更新机器人记录
// Author [yourname](https://github.com/yourname)
func (btService *BotService) UpdateBot(ctx context.Context, bt bot.Bot) (err error) {
err = global.GVA_DB.Model(&bot.Bot{}).Where("id = ?", bt.ID).Updates(&bt).Error
return err
}
// GetBot 根据ID获取机器人记录
// Author [yourname](https://github.com/yourname)
func (btService *BotService) GetBot(ctx context.Context, ID string) (bt bot.Bot, err error) {
err = global.GVA_DB.Where("id = ?", ID).First(&bt).Error
return
}
// GetBotInfoList 分页获取机器人记录
// Author [yourname](https://github.com/yourname)
func (btService *BotService) GetBotInfoList(ctx context.Context, info botReq.BotSearch) (list []bot.Bot, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
// 创建db
db := global.GVA_DB.Model(&bot.Bot{})
var bts []bot.Bot
// 如果有条件搜索 下方会自动创建搜索语句
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
}
err = db.Count(&total).Error
if err != nil {
return
}
if limit != 0 {
db = db.Limit(limit).Offset(offset)
}
err = db.Find(&bts).Error
return bts, total, err
}
// GetBotPublic 模糊搜索公开机器人
func (btService *BotService) GetBotPublic(keyWord botReq.FindKey, userId uint) (bt bot.Bot, err error) {
// 判断用户是否为VIP或者SVIP
var userCount int64
if userId != 0 {
err = global.GVA_DB.Table("app_user").Where("id = ? AND is_vip = 1", userId).Count(&userCount).Error
if err != nil {
global.GVA_LOG.Error("查询用户VIP状态失败", zap.Error(err))
return
}
}
if userCount == 0 {
text := "抱歉该内容仅对VIP用户开放升级VIP后即可查看完整内容。"
// 非VIP直接返回空内容
bt.Content = &text
return
}
userInput := strings.TrimSpace(keyWord.KeyWord)
// 生成用户输入的所有变体
variants := btService.generateKeywordVariants(userInput)
var conditions []string
var args []interface{}
// 对每个变体进行多种匹配策略
for _, variant := range variants {
// 1. 精确匹配(逗号分隔的关键词)
conditions = append(conditions, "FIND_IN_SET(?, keyword)")
args = append(args, variant)
// 2. 包含匹配
conditions = append(conditions, "keyword LIKE ?")
args = append(args, "%"+variant+"%")
// 3. 反向包含匹配(关键词包含用户输入)
conditions = append(conditions, "? LIKE CONCAT('%', keyword, '%')")
args = append(args, variant)
}
// 4. 特殊处理:如果用户输入较短,尝试作为关键词的子串匹配
if len([]rune(userInput)) <= 3 {
conditions = append(conditions, "keyword LIKE ?")
args = append(args, userInput+"%")
conditions = append(conditions, "keyword LIKE ?")
args = append(args, "%"+userInput)
}
whereClause := strings.Join(conditions, " OR ")
err = global.GVA_DB.Where(whereClause, args...).First(&bt).Error
// 如果没有查到进行2字一组分词模糊匹配
if err == gorm.ErrRecordNotFound {
segments := splitByTwoWords(userInput)
if len(segments) > 0 {
var segConds []string
var segArgs []interface{}
for _, seg := range segments {
segConds = append(segConds, "keyword LIKE ?")
segArgs = append(segArgs, "%"+seg+"%")
}
segWhere := strings.Join(segConds, " OR ")
err = global.GVA_DB.Where(segWhere, segArgs...).First(&bt).Error
}
}
go func() {
// 更新查询次数
if err == nil && bt.ID != 0 {
global.GVA_DB.Model(&bot.Bot{}).Where("id = ?", bt.ID).UpdateColumn("search_count", gorm.Expr("search_count + ?", 1))
}
}()
return
}
// splitByTwoWords 将字符串按2字一组分割
func splitByTwoWords(s string) []string {
runes := []rune(s)
var result []string
for i := 0; i < len(runes)-1; i++ {
result = append(result, string(runes[i:i+2]))
}
return result
}
// generateKeywordVariants 生成关键词的所有可能变体
func (btService *BotService) generateKeywordVariants(input string) []string {
variants := make(map[string]bool)
variants[input] = true
// 中文数字到阿拉伯数字的映射
chineseToNum := map[string]string{
"一": "1", "二": "2", "三": "3", "四": "4", "五": "5",
"六": "6", "七": "7", "八": "8", "九": "9", "十": "10",
"零": "0",
}
// 阿拉伯数字到中文数字的映射
numToChinese := map[string]string{
"1": "一", "2": "二", "3": "三", "4": "四", "5": "五",
"6": "六", "7": "七", "8": "八", "9": "九", "10": "十",
"0": "零",
}
// 生成数字转换变体
current := input
// 中文数字 -> 阿拉伯数字
for cn, num := range chineseToNum {
if strings.Contains(current, cn) {
numVariant := strings.ReplaceAll(current, cn, num)
variants[numVariant] = true
}
}
// 阿拉伯数字 -> 中文数字
for num, cn := range numToChinese {
if strings.Contains(current, num) {
cnVariant := strings.ReplaceAll(current, num, cn)
variants[cnVariant] = true
}
}
// 转换为切片返回
result := make([]string, 0, len(variants))
for v := range variants {
if v != "" && len(strings.TrimSpace(v)) > 0 {
result = append(result, v)
}
}
return result
}
func (btService *BotService) BulkBot(p botReq.BulkBot, userName string) (err error) {
// 上传失败的图片
var failFiles []string
for _, a := range p.Files {
content := "<img src=" + a + " alt=\"" + a + "\" data-href=\"\" style=\"width: 100%;height: auto;\"/>"
bots := bot.Bot{
Keyword: getBotKeyWorld(a),
Content: &content,
CreateBy: userName,
}
err = global.GVA_DB.Create(&bots).Error
if err != nil {
global.GVA_LOG.Error("创建机器人失败", zap.Error(err))
// 记录上传失败的文件
failFiles = append(failFiles, a)
continue
}
}
if len(failFiles) > 0 {
global.GVA_LOG.Error("部分文件上传失败", zap.Strings("failedFiles", failFiles))
return fmt.Errorf("部分文件上传失败: %v", failFiles)
}
return nil
}
func getBotKeyWorld(url string) string {
lastSlash := strings.LastIndex(url, "/")
underscore := strings.Index(url[lastSlash+1:], "_")
if lastSlash == -1 || underscore == -1 {
return ""
}
return url[lastSlash+1 : lastSlash+1+underscore]
}