package app import ( "encoding/json" "errors" "regexp" "strings" "git.echol.cn/loser/st/server/global" "git.echol.cn/loser/st/server/model/app" "git.echol.cn/loser/st/server/model/app/request" "git.echol.cn/loser/st/server/model/app/response" "go.uber.org/zap" "gorm.io/datatypes" "gorm.io/gorm" ) type RegexScriptService struct{} // CreateRegexScript 创建正则脚本 func (s *RegexScriptService) CreateRegexScript(userID uint, req *request.CreateRegexScriptRequest) (*response.RegexScriptResponse, error) { trimStringsJSON, _ := json.Marshal(req.TrimStrings) script := &app.RegexScript{ UserID: userID, Name: req.Name, FindRegex: req.FindRegex, ReplaceWith: req.ReplaceWith, TrimStrings: datatypes.JSON(trimStringsJSON), Placement: req.Placement, Disabled: req.Disabled, MarkdownOnly: req.MarkdownOnly, RunOnEdit: req.RunOnEdit, PromptOnly: req.PromptOnly, SubstituteRegex: req.SubstituteRegex, MinDepth: req.MinDepth, MaxDepth: req.MaxDepth, Scope: req.Scope, OwnerCharID: req.OwnerCharID, OwnerPresetID: req.OwnerPresetID, Order: req.Order, } if err := global.GVA_DB.Create(script).Error; err != nil { global.GVA_LOG.Error("创建正则脚本失败", zap.Error(err)) return nil, err } resp := response.ToRegexScriptResponse(script) return &resp, nil } // GetRegexScriptList 获取正则脚本列表 func (s *RegexScriptService) GetRegexScriptList(userID uint, req *request.GetRegexScriptListRequest) ([]response.RegexScriptResponse, int64, error) { var scripts []app.RegexScript var total int64 db := global.GVA_DB.Model(&app.RegexScript{}).Where("user_id = ?", userID) if req.Keyword != "" { db = db.Where("name LIKE ?", "%"+req.Keyword+"%") } if req.Scope != nil { db = db.Where("scope = ?", *req.Scope) } if err := db.Count(&total).Error; err != nil { return nil, 0, err } offset := (req.Page - 1) * req.PageSize if err := db.Order("\"order\" ASC, created_at DESC").Offset(offset).Limit(req.PageSize).Find(&scripts).Error; err != nil { global.GVA_LOG.Error("获取正则脚本列表失败", zap.Error(err)) return nil, 0, err } var list []response.RegexScriptResponse for i := range scripts { list = append(list, response.ToRegexScriptResponse(&scripts[i])) } return list, total, nil } // GetRegexScriptByID 获取正则脚本详情 func (s *RegexScriptService) GetRegexScriptByID(userID uint, id uint) (*response.RegexScriptResponse, error) { var script app.RegexScript if err := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).First(&script).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("正则脚本不存在或无权访问") } return nil, err } resp := response.ToRegexScriptResponse(&script) return &resp, nil } // UpdateRegexScript 更新正则脚本 func (s *RegexScriptService) UpdateRegexScript(userID uint, id uint, req *request.UpdateRegexScriptRequest) error { var script app.RegexScript if err := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).First(&script).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New("正则脚本不存在或无权修改") } return err } updates := make(map[string]interface{}) if req.Name != nil { updates["name"] = *req.Name } if req.FindRegex != nil { updates["find_regex"] = *req.FindRegex } if req.ReplaceWith != nil { updates["replace_with"] = *req.ReplaceWith } if req.TrimStrings != nil { trimStringsJSON, _ := json.Marshal(req.TrimStrings) updates["trim_strings"] = datatypes.JSON(trimStringsJSON) } if req.Placement != nil { updates["placement"] = *req.Placement } if req.Disabled != nil { updates["disabled"] = *req.Disabled } if req.MarkdownOnly != nil { updates["markdown_only"] = *req.MarkdownOnly } if req.RunOnEdit != nil { updates["run_on_edit"] = *req.RunOnEdit } if req.PromptOnly != nil { updates["prompt_only"] = *req.PromptOnly } if req.SubstituteRegex != nil { updates["substitute_regex"] = *req.SubstituteRegex } if req.MinDepth != nil { updates["min_depth"] = req.MinDepth } if req.MaxDepth != nil { updates["max_depth"] = req.MaxDepth } if req.Scope != nil { updates["scope"] = *req.Scope } if req.OwnerCharID != nil { updates["owner_char_id"] = req.OwnerCharID } if req.OwnerPresetID != nil { updates["owner_preset_id"] = req.OwnerPresetID } if req.Order != nil { updates["order"] = *req.Order } return global.GVA_DB.Model(&script).Updates(updates).Error } // DeleteRegexScript 删除正则脚本 func (s *RegexScriptService) DeleteRegexScript(userID uint, id uint) error { result := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).Delete(&app.RegexScript{}) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("正则脚本不存在或无权删除") } return nil } // TestRegexScript 测试正则脚本 func (s *RegexScriptService) TestRegexScript(userID uint, id uint, testString string) (*response.TestRegexScriptResponse, error) { var script app.RegexScript if err := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).First(&script).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("正则脚本不存在或无权访问") } return nil, err } result, err := s.ExecuteScript(&script, testString, "", "") if err != nil { return &response.TestRegexScriptResponse{ Original: testString, Result: testString, Success: false, Error: err.Error(), }, nil } return &response.TestRegexScriptResponse{ Original: testString, Result: result, Success: true, }, nil } // ExecuteScript 执行正则脚本 func (s *RegexScriptService) ExecuteScript(script *app.RegexScript, text string, userName string, charName string) (string, error) { if script.Disabled { return text, nil } result := text // 1. 宏替换 if script.SubstituteRegex { result = s.substituteMacros(result, userName, charName) } // 2. 正则替换 if script.FindRegex != "" { re, err := regexp.Compile(script.FindRegex) if err != nil { global.GVA_LOG.Warn("正则表达式编译失败", zap.String("pattern", script.FindRegex), zap.Error(err)) return text, err } result = re.ReplaceAllString(result, script.ReplaceWith) } // 3. 修剪字符串 if len(script.TrimStrings) > 0 { var trimStrings []string json.Unmarshal(script.TrimStrings, &trimStrings) for _, trimStr := range trimStrings { result = strings.ReplaceAll(result, trimStr, "") } } return result, nil } // substituteMacros 替换宏变量 func (s *RegexScriptService) substituteMacros(text string, userName string, charName string) string { result := text if userName != "" { result = strings.ReplaceAll(result, "{{user}}", userName) result = strings.ReplaceAll(result, "{{User}}", userName) } if charName != "" { result = strings.ReplaceAll(result, "{{char}}", charName) result = strings.ReplaceAll(result, "{{Char}}", charName) } return result } // GetScriptsForPlacement 获取指定阶段的脚本 func (s *RegexScriptService) GetScriptsForPlacement(userID uint, placement int, charID *uint, presetID *uint) ([]app.RegexScript, error) { var scripts []app.RegexScript db := global.GVA_DB.Where("user_id = ? AND placement = ? AND disabled = ?", userID, placement, false) // 作用域过滤:全局(0) 或 角色(1) 或 预设(2) scopeCondition := "scope = 0" // 全局 if charID != nil { scopeCondition += " OR (scope = 1 AND owner_char_id = " + string(rune(*charID)) + ")" } if presetID != nil { scopeCondition += " OR (scope = 2 AND owner_preset_id = " + string(rune(*presetID)) + ")" } db = db.Where(scopeCondition) if err := db.Order("\"order\" ASC").Find(&scripts).Error; err != nil { return nil, err } return scripts, nil } // ExecuteScripts 批量执行脚本 func (s *RegexScriptService) ExecuteScripts(scripts []app.RegexScript, text string, userName string, charName string) string { result := text for _, script := range scripts { executed, err := s.ExecuteScript(&script, result, userName, charName) if err != nil { global.GVA_LOG.Warn("执行正则脚本失败", zap.String("name", script.Name), zap.Error(err)) continue } result = executed } return result }