refactor(*): 平台架构 - 拆分后端领域模块并同步插件协议与模型能力
This commit is contained in:
1 parent
916043fb5a
commit
6e5cbc8b05
644 files changed
+62731
-2320
No files matched your search
@@ -0,0 +1,157 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const responseInterceptionSettingKey = "response_interception"
|
||||
|
||||
const (
|
||||
maxResponseInterceptionRules = 100
|
||||
maxResponseInterceptionTextRunes = 200
|
||||
maxResponseInterceptionReplaceRunes = 500
|
||||
)
|
||||
|
||||
type ResponseInterceptionRule struct {
|
||||
Contains string `json:"contains"`
|
||||
Replace string `json:"replace"`
|
||||
}
|
||||
|
||||
type ResponseInterceptionSetting struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Rules []ResponseInterceptionRule `json:"rules"`
|
||||
}
|
||||
|
||||
func defaultResponseInterceptionSetting() ResponseInterceptionSetting {
|
||||
return ResponseInterceptionSetting{Rules: []ResponseInterceptionRule{}}
|
||||
}
|
||||
|
||||
func (s *Service) AdminResponseInterceptionSetting(actor *model.User) (*ResponseInterceptionSetting, error) {
|
||||
if err := s.RequireAdmin(actor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := s.responseInterceptionSetting()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateResponseInterceptionSetting(actor *model.User, value ResponseInterceptionSetting) (*ResponseInterceptionSetting, error) {
|
||||
if err := s.RequireAdmin(actor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value = normalizeResponseInterceptionSetting(value)
|
||||
if err := validateResponseInterceptionSetting(value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
before, err := s.responseInterceptionSetting()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoded, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, err := s.repo.SystemSetting(responseInterceptionSettingKey)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
setting := &model.SystemSetting{Key: responseInterceptionSettingKey, ValueJSON: string(encoded), UpdatedBy: actor.ID}
|
||||
if current != nil {
|
||||
setting.CreatedAt = current.CreatedAt
|
||||
}
|
||||
if err := s.repo.SaveSystemSetting(setting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.appendAdminAudit(actor, "response_interception.update", "system_setting", responseInterceptionSettingKey, "更新模型响应拦截", map[string]any{"before": before, "after": value}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
// InterceptResponseText 只投影用户可见文案,不修改上游响应或请求明细中的原始内容。
|
||||
func (s *Service) InterceptResponseText(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
setting, readErr := s.responseInterceptionSetting()
|
||||
if readErr != nil || !setting.Enabled {
|
||||
return raw
|
||||
}
|
||||
return interceptResponseMessage(setting, raw)
|
||||
}
|
||||
|
||||
// UserFacingErrorMessage 只投影用户可见错误,不修改上游响应或请求明细中的原始内容。
|
||||
func (s *Service) UserFacingErrorMessage(err error) string {
|
||||
return s.InterceptResponseText(taskFailureMessage(err))
|
||||
}
|
||||
|
||||
func interceptResponseMessage(setting ResponseInterceptionSetting, raw string) string {
|
||||
lower := strings.ToLower(raw)
|
||||
for _, rule := range setting.Rules {
|
||||
if strings.Contains(lower, strings.ToLower(rule.Contains)) {
|
||||
return rule.Replace
|
||||
}
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func (s *Service) responseInterceptionSetting() (ResponseInterceptionSetting, error) {
|
||||
setting, err := s.repo.SystemSetting(responseInterceptionSettingKey)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return defaultResponseInterceptionSetting(), nil
|
||||
}
|
||||
if err != nil {
|
||||
return ResponseInterceptionSetting{}, err
|
||||
}
|
||||
value := defaultResponseInterceptionSetting()
|
||||
if strings.TrimSpace(setting.ValueJSON) == "" {
|
||||
return value, nil
|
||||
}
|
||||
if err := json.Unmarshal([]byte(setting.ValueJSON), &value); err != nil {
|
||||
return ResponseInterceptionSetting{}, fmt.Errorf("模型响应拦截配置格式错误: %w", err)
|
||||
}
|
||||
value = normalizeResponseInterceptionSetting(value)
|
||||
if err := validateResponseInterceptionSetting(value); err != nil {
|
||||
return ResponseInterceptionSetting{}, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func normalizeResponseInterceptionSetting(value ResponseInterceptionSetting) ResponseInterceptionSetting {
|
||||
if value.Rules == nil {
|
||||
value.Rules = []ResponseInterceptionRule{}
|
||||
}
|
||||
for index := range value.Rules {
|
||||
value.Rules[index].Contains = strings.TrimSpace(value.Rules[index].Contains)
|
||||
value.Rules[index].Replace = strings.TrimSpace(value.Rules[index].Replace)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func validateResponseInterceptionSetting(value ResponseInterceptionSetting) error {
|
||||
if len(value.Rules) > maxResponseInterceptionRules {
|
||||
return fmt.Errorf("模型响应拦截规则不能超过 %d 条", maxResponseInterceptionRules)
|
||||
}
|
||||
for index, rule := range value.Rules {
|
||||
if rule.Contains == "" {
|
||||
return fmt.Errorf("第 %d 条拦截规则的匹配文案不能为空", index+1)
|
||||
}
|
||||
if len([]rune(rule.Contains)) > maxResponseInterceptionTextRunes {
|
||||
return fmt.Errorf("第 %d 条拦截规则的匹配文案不能超过 %d 个字符", index+1, maxResponseInterceptionTextRunes)
|
||||
}
|
||||
if rule.Replace == "" {
|
||||
return fmt.Errorf("第 %d 条拦截规则的替换文案不能为空", index+1)
|
||||
}
|
||||
if len([]rune(rule.Replace)) > maxResponseInterceptionReplaceRunes {
|
||||
return fmt.Errorf("第 %d 条拦截规则的替换文案不能超过 %d 个字符", index+1, maxResponseInterceptionReplaceRunes)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user