fix: 系统配置数据类型增强

This commit is contained in:
tbphp
2025-07-03 20:31:33 +08:00
parent 45337d9b80
commit f07fae50ec
2 changed files with 63 additions and 44 deletions

View File

@@ -95,19 +95,8 @@ func DefaultSystemSettings() SystemSettings {
fieldValue := v.Field(i)
if fieldValue.CanSet() {
switch fieldValue.Kind() {
case reflect.Int:
if intVal, err := strconv.ParseInt(defaultTag, 10, 64); err == nil {
fieldValue.SetInt(int64(intVal))
}
case reflect.String:
if strVal, ok := interfaceToString(defaultTag); ok {
fieldValue.SetString(strVal)
}
case reflect.Bool:
if boolVal, ok := interfaceToBool(defaultTag); ok {
fieldValue.SetBool(boolVal)
}
if err := setFieldFromString(fieldValue, defaultTag); err != nil {
logrus.Warnf("Failed to set default value for field %s: %v", field.Name, err)
}
}
}
@@ -196,7 +185,7 @@ func (sm *SystemSettingsManager) GetSettings() SystemSettings {
}
// UpdateSettings 更新系统配置
func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]string) error {
func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]any) error {
if db.DB == nil {
return fmt.Errorf("database not initialized")
}
@@ -211,7 +200,7 @@ func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]string) e
for key, value := range settingsMap {
settingsToUpdate = append(settingsToUpdate, models.SystemSetting{
SettingKey: key,
SettingValue: value,
SettingValue: fmt.Sprintf("%v", value), // Convert interface{} to string
})
}
@@ -275,7 +264,7 @@ func (sm *SystemSettingsManager) GetEffectiveConfig(groupConfig datatypes.JSONMa
}
// ValidateSettings 验证系统配置的有效性
func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]string) error {
func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]any) error {
tempSettings := DefaultSystemSettings()
v := reflect.ValueOf(&tempSettings).Elem()
t := v.Type()
@@ -295,16 +284,19 @@ func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]string)
}
validateTag := field.Tag.Get("validate")
if validateTag == "" {
continue
}
switch field.Type.Kind() {
case reflect.Int:
intVal, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("invalid integer value for %s: %s", key, value)
// JSON numbers are decoded as float64
floatVal, ok := value.(float64)
if !ok {
return fmt.Errorf("invalid type for %s: expected a number, got %T", key, value)
}
intVal := int(floatVal)
if floatVal != float64(intVal) {
return fmt.Errorf("invalid value for %s: must be an integer", key)
}
if strings.HasPrefix(validateTag, "min=") {
minValStr := strings.TrimPrefix(validateTag, "min=")
minVal, _ := strconv.Atoi(minValStr)
@@ -312,6 +304,14 @@ func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]string)
return fmt.Errorf("value for %s (%d) is below minimum value (%d)", key, intVal, minVal)
}
}
case reflect.Bool:
if _, ok := value.(bool); !ok {
return fmt.Errorf("invalid type for %s: expected a boolean, got %T", key, value)
}
case reflect.String:
if _, ok := value.(string); !ok {
return fmt.Errorf("invalid type for %s: expected a string, got %T", key, value)
}
default:
return fmt.Errorf("unsupported type for setting key validation: %s", key)
}
@@ -352,29 +352,45 @@ func (sm *SystemSettingsManager) mapToStruct(m map[string]string, s *SystemSetti
}
}
for key, val := range m {
for key, valStr := range m {
if fieldName, ok := jsonToField[key]; ok {
fieldValue := v.FieldByName(fieldName)
if fieldValue.IsValid() && fieldValue.CanSet() {
switch fieldValue.Kind() {
case reflect.Int:
if intVal, err := interfaceToInt(val); err == nil {
fieldValue.SetInt(int64(intVal))
}
case reflect.String:
if strVal, ok := interfaceToString(val); ok {
fieldValue.SetString(strVal)
}
case reflect.Bool:
if boolVal, ok := interfaceToBool(val); ok {
fieldValue.SetBool(boolVal)
}
if err := setFieldFromString(fieldValue, valStr); err != nil {
logrus.Warnf("Failed to set value from map for field %s: %v", fieldName, err)
}
}
}
}
}
// setFieldFromString sets a struct field's value from a string, based on the field's kind.
func setFieldFromString(fieldValue reflect.Value, value string) error {
if !fieldValue.CanSet() {
return fmt.Errorf("field cannot be set")
}
switch fieldValue.Kind() {
case reflect.Int:
intVal, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return fmt.Errorf("invalid integer value '%s': %w", value, err)
}
fieldValue.SetInt(int64(intVal))
case reflect.Bool:
boolVal, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invalid boolean value '%s': %w", value, err)
}
fieldValue.SetBool(boolVal)
case reflect.String:
fieldValue.SetString(value)
default:
return fmt.Errorf("unsupported field kind: %s", fieldValue.Kind())
}
return nil
}
// 工具函数
func interfaceToInt(val interface{}) (int, error) {
@@ -382,30 +398,33 @@ func interfaceToInt(val interface{}) (int, error) {
case int:
return v, nil
case float64:
// JSON unmarshals numbers into float64
if v != float64(int(v)) {
return 0, fmt.Errorf("value is a float, not an integer: %v", v)
}
return int(v), nil
case string:
return strconv.Atoi(v)
default:
return 0, fmt.Errorf("cannot convert to int: %v", val)
return 0, fmt.Errorf("cannot convert %T to int", v)
}
}
// interfaceToString is kept for GetEffectiveConfig
func interfaceToString(val interface{}) (string, bool) {
s, ok := val.(string)
return s, ok
}
// interfaceToBool is kept for GetEffectiveConfig
func interfaceToBool(val interface{}) (bool, bool) {
switch v := val.(type) {
case bool:
return v, true
case string:
lowerV := strings.ToLower(v)
if lowerV == "true" || lowerV == "1" || lowerV == "on" {
return true, true
}
if lowerV == "false" || lowerV == "0" || lowerV == "off" {
return false, true
b, err := strconv.ParseBool(v)
if err == nil {
return b, true
}
}
return false, false

View File

@@ -38,7 +38,7 @@ func GetSettings(c *gin.Context) {
// It receives a key-value JSON object and updates system settings.
// After updating, it triggers a configuration reload.
func UpdateSettings(c *gin.Context) {
var settingsMap map[string]string
var settingsMap map[string]any
if err := c.ShouldBindJSON(&settingsMap); err != nil {
response.BadRequest(c, "Invalid request body")
return