// file: gemini-balancer\internal\settings\settings.go package settings import ( "encoding/json" "fmt" "gemini-balancer/internal/models" "gemini-balancer/internal/store" "gemini-balancer/internal/syncer" "reflect" "strconv" "strings" "github.com/sirupsen/logrus" "gorm.io/gorm" "gorm.io/gorm/clause" ) const SettingsUpdateChannel = "system_settings:updated" const DefaultGeminiEndpoint = "https://generativelanguage.googleapis.com/v1beta/models" // SettingsManager [核心修正] syncer现在缓存正确的“蓝图”类型 type SettingsManager struct { db *gorm.DB syncer *syncer.CacheSyncer[*models.SystemSettings] logger *logrus.Entry jsonToFieldType map[string]reflect.Type // 用于将JSON字段映射到Go类型 } func NewSettingsManager(db *gorm.DB, store store.Store, logger *logrus.Logger) (*SettingsManager, error) { sm := &SettingsManager{ db: db, logger: logger.WithField("component", "SettingsManager⚙️"), jsonToFieldType: make(map[string]reflect.Type), } // settingsLoader 的职责:读取“砖块”,组装并返回“蓝图” settingsType := reflect.TypeOf(models.SystemSettings{}) for i := 0; i < settingsType.NumField(); i++ { field := settingsType.Field(i) jsonTag := field.Tag.Get("json") if jsonTag != "" && jsonTag != "-" { sm.jsonToFieldType[jsonTag] = field.Type } } // settingsLoader 的职责:读取“砖块”,智能组装成“蓝图” settingsLoader := func() (*models.SystemSettings, error) { sm.logger.Info("Loading system settings from database...") var dbRecords []models.Setting if err := sm.db.Find(&dbRecords).Error; err != nil { return nil, fmt.Errorf("failed to load system settings from db: %w", err) } settingsMap := make(map[string]string) for _, record := range dbRecords { settingsMap[record.Key] = record.Value } // 从一个包含了所有“出厂设置”的“蓝图”开始 settings := defaultSystemSettings() v := reflect.ValueOf(settings).Elem() t := v.Type() // [智能卸货] for i := 0; i < t.NumField(); i++ { field := t.Field(i) fieldValue := v.Field(i) jsonTag := field.Tag.Get("json") if dbValue, ok := settingsMap[jsonTag]; ok { if err := parseAndSetField(fieldValue, dbValue); err != nil { sm.logger.Warnf("Failed to set config field '%s' from DB value '%s': %v. Using default.", field.Name, dbValue, err) } } } if settings.BaseKeyCheckEndpoint == DefaultGeminiEndpoint || settings.BaseKeyCheckEndpoint == "" { if settings.DefaultUpstreamURL != "" { // 如果全局上游URL已设置,则基于它构建新的检查端点。 originalEndpoint := settings.BaseKeyCheckEndpoint derivedEndpoint := strings.TrimSuffix(settings.DefaultUpstreamURL, "/") + "/models" settings.BaseKeyCheckEndpoint = derivedEndpoint sm.logger.Infof( "BaseKeyCheckEndpoint is dynamically derived from DefaultUpstreamURL. Original: '%s', New: '%s'", originalEndpoint, derivedEndpoint, ) } } else { sm.logger.Infof("BaseKeyCheckEndpoint is using a user-defined override: %s", settings.BaseKeyCheckEndpoint) } sm.logger.Info("System settings loaded and cached.") sm.DisplaySettings(settings) return settings, nil } s, err := syncer.NewCacheSyncer(settingsLoader, store, SettingsUpdateChannel) if err != nil { return nil, fmt.Errorf("failed to create system settings syncer: %w", err) } sm.syncer = s go sm.ensureSettingsInitialized() return sm, nil } // GetSettings [核心修正] 现在它正确地返回我们需要的“蓝图” func (sm *SettingsManager) GetSettings() *models.SystemSettings { return sm.syncer.Get() } // UpdateSettings [核心修正] 它接收更新,并将它们转换为“砖块”存入数据库 func (sm *SettingsManager) UpdateSettings(settingsMap map[string]interface{}) error { var settingsToUpdate []models.Setting for key, value := range settingsMap { fieldType, ok := sm.jsonToFieldType[key] if !ok { sm.logger.Warnf("Received update for unknown setting key '%s', ignoring.", key) continue } var dbValue string // [智能打包] // 如果字段是 slice 或 map,我们就将传入的 interface{} “打包”成 JSON string kind := fieldType.Kind() if kind == reflect.Slice || kind == reflect.Map { jsonBytes, marshalErr := json.Marshal(value) if marshalErr != nil { // [真正的错误处理] 如果打包失败,我们记录日志,并跳过这个“坏掉的集装箱”。 sm.logger.Warnf("Failed to marshal setting '%s' to JSON: %v, skipping update.", key, marshalErr) continue // 跳过,继续处理下一个key } dbValue = string(jsonBytes) } else if kind == reflect.Bool { if b, ok := value.(bool); ok { dbValue = strconv.FormatBool(b) } else { dbValue = "false" } } else { dbValue = fmt.Sprintf("%v", value) } settingsToUpdate = append(settingsToUpdate, models.Setting{ Key: key, Value: dbValue, }) } if len(settingsToUpdate) > 0 { err := sm.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "key"}}, DoUpdates: clause.AssignmentColumns([]string{"value"}), }).Create(&settingsToUpdate).Error if err != nil { return fmt.Errorf("failed to update settings in db: %w", err) } } return sm.syncer.Invalidate() } // ensureSettingsInitialized [核心修正] 确保DB中有所有“砖块”的定义 func (sm *SettingsManager) ensureSettingsInitialized() { defaults := defaultSystemSettings() v := reflect.ValueOf(defaults).Elem() t := v.Type() for i := 0; i < t.NumField(); i++ { field := t.Field(i) fieldValue := v.Field(i) key := field.Tag.Get("json") if key == "" || key == "-" { continue } var existing models.Setting if err := sm.db.Where("key = ?", key).First(&existing).Error; err == gorm.ErrRecordNotFound { var defaultValue string kind := fieldValue.Kind() // [智能初始化] if kind == reflect.Slice || kind == reflect.Map { // 为复杂类型,生成一个“空的”JSON字符串,例如 "[]" 或 "{}" jsonBytes, _ := json.Marshal(fieldValue.Interface()) defaultValue = string(jsonBytes) } else { defaultValue = field.Tag.Get("default") } setting := models.Setting{ Key: key, Value: defaultValue, Name: field.Tag.Get("name"), Description: field.Tag.Get("desc"), Category: field.Tag.Get("category"), DefaultValue: field.Tag.Get("default"), // 元数据中的default,永远来自tag } if err := sm.db.Create(&setting).Error; err != nil { sm.logger.Errorf("Failed to initialize setting '%s': %v", key, err) } } } } // ResetAndSaveSettings [核心新增] 將所有配置重置為其在 'default' 標籤中定義的值。 func (sm *SettingsManager) ResetAndSaveSettings() (*models.SystemSettings, error) { defaults := defaultSystemSettings() v := reflect.ValueOf(defaults).Elem() t := v.Type() var settingsToSave []models.Setting for i := 0; i < t.NumField(); i++ { field := t.Field(i) fieldValue := v.Field(i) key := field.Tag.Get("json") if key == "" || key == "-" { continue } var defaultValue string kind := fieldValue.Kind() // [智能重置] if kind == reflect.Slice || kind == reflect.Map { jsonBytes, _ := json.Marshal(fieldValue.Interface()) defaultValue = string(jsonBytes) } else { defaultValue = field.Tag.Get("default") } setting := models.Setting{ Key: key, Value: defaultValue, Name: field.Tag.Get("name"), Description: field.Tag.Get("desc"), Category: field.Tag.Get("category"), DefaultValue: field.Tag.Get("default"), } settingsToSave = append(settingsToSave, setting) } if len(settingsToSave) > 0 { err := sm.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "key"}}, DoUpdates: clause.AssignmentColumns([]string{"value", "name", "description", "category", "default_value"}), }).Create(&settingsToSave).Error if err != nil { return nil, fmt.Errorf("failed to reset settings in db: %w", err) } } if err := sm.syncer.Invalidate(); err != nil { sm.logger.Errorf("Failed to invalidate settings cache after reset: %v", err) } return defaults, nil }