Files
st-react/server/service/app/regex_script.go
2026-02-28 15:11:12 +08:00

286 lines
8.2 KiB
Go

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
}