fix: 系统配置数据类型增强
This commit is contained in:
@@ -95,19 +95,8 @@ func DefaultSystemSettings() SystemSettings {
|
|||||||
|
|
||||||
fieldValue := v.Field(i)
|
fieldValue := v.Field(i)
|
||||||
if fieldValue.CanSet() {
|
if fieldValue.CanSet() {
|
||||||
switch fieldValue.Kind() {
|
if err := setFieldFromString(fieldValue, defaultTag); err != nil {
|
||||||
case reflect.Int:
|
logrus.Warnf("Failed to set default value for field %s: %v", field.Name, err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +185,7 @@ func (sm *SystemSettingsManager) GetSettings() SystemSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSettings 更新系统配置
|
// UpdateSettings 更新系统配置
|
||||||
func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]string) error {
|
func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]any) error {
|
||||||
if db.DB == nil {
|
if db.DB == nil {
|
||||||
return fmt.Errorf("database not initialized")
|
return fmt.Errorf("database not initialized")
|
||||||
}
|
}
|
||||||
@@ -211,7 +200,7 @@ func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]string) e
|
|||||||
for key, value := range settingsMap {
|
for key, value := range settingsMap {
|
||||||
settingsToUpdate = append(settingsToUpdate, models.SystemSetting{
|
settingsToUpdate = append(settingsToUpdate, models.SystemSetting{
|
||||||
SettingKey: key,
|
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 验证系统配置的有效性
|
// ValidateSettings 验证系统配置的有效性
|
||||||
func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]string) error {
|
func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]any) error {
|
||||||
tempSettings := DefaultSystemSettings()
|
tempSettings := DefaultSystemSettings()
|
||||||
v := reflect.ValueOf(&tempSettings).Elem()
|
v := reflect.ValueOf(&tempSettings).Elem()
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
@@ -295,16 +284,19 @@ func (sm *SystemSettingsManager) ValidateSettings(settingsMap map[string]string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateTag := field.Tag.Get("validate")
|
validateTag := field.Tag.Get("validate")
|
||||||
if validateTag == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch field.Type.Kind() {
|
switch field.Type.Kind() {
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
intVal, err := strconv.Atoi(value)
|
// JSON numbers are decoded as float64
|
||||||
if err != nil {
|
floatVal, ok := value.(float64)
|
||||||
return fmt.Errorf("invalid integer value for %s: %s", key, value)
|
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=") {
|
if strings.HasPrefix(validateTag, "min=") {
|
||||||
minValStr := strings.TrimPrefix(validateTag, "min=")
|
minValStr := strings.TrimPrefix(validateTag, "min=")
|
||||||
minVal, _ := strconv.Atoi(minValStr)
|
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)
|
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:
|
default:
|
||||||
return fmt.Errorf("unsupported type for setting key validation: %s", key)
|
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 {
|
if fieldName, ok := jsonToField[key]; ok {
|
||||||
fieldValue := v.FieldByName(fieldName)
|
fieldValue := v.FieldByName(fieldName)
|
||||||
if fieldValue.IsValid() && fieldValue.CanSet() {
|
if fieldValue.IsValid() && fieldValue.CanSet() {
|
||||||
switch fieldValue.Kind() {
|
if err := setFieldFromString(fieldValue, valStr); err != nil {
|
||||||
case reflect.Int:
|
logrus.Warnf("Failed to set value from map for field %s: %v", fieldName, err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func interfaceToInt(val interface{}) (int, error) {
|
||||||
@@ -382,30 +398,33 @@ func interfaceToInt(val interface{}) (int, error) {
|
|||||||
case int:
|
case int:
|
||||||
return v, nil
|
return v, nil
|
||||||
case float64:
|
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
|
return int(v), nil
|
||||||
case string:
|
case string:
|
||||||
return strconv.Atoi(v)
|
return strconv.Atoi(v)
|
||||||
default:
|
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) {
|
func interfaceToString(val interface{}) (string, bool) {
|
||||||
s, ok := val.(string)
|
s, ok := val.(string)
|
||||||
return s, ok
|
return s, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interfaceToBool is kept for GetEffectiveConfig
|
||||||
func interfaceToBool(val interface{}) (bool, bool) {
|
func interfaceToBool(val interface{}) (bool, bool) {
|
||||||
switch v := val.(type) {
|
switch v := val.(type) {
|
||||||
case bool:
|
case bool:
|
||||||
return v, true
|
return v, true
|
||||||
case string:
|
case string:
|
||||||
lowerV := strings.ToLower(v)
|
b, err := strconv.ParseBool(v)
|
||||||
if lowerV == "true" || lowerV == "1" || lowerV == "on" {
|
if err == nil {
|
||||||
return true, true
|
return b, true
|
||||||
}
|
|
||||||
if lowerV == "false" || lowerV == "0" || lowerV == "off" {
|
|
||||||
return false, true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, false
|
return false, false
|
||||||
|
@@ -38,7 +38,7 @@ func GetSettings(c *gin.Context) {
|
|||||||
// It receives a key-value JSON object and updates system settings.
|
// It receives a key-value JSON object and updates system settings.
|
||||||
// After updating, it triggers a configuration reload.
|
// After updating, it triggers a configuration reload.
|
||||||
func UpdateSettings(c *gin.Context) {
|
func UpdateSettings(c *gin.Context) {
|
||||||
var settingsMap map[string]string
|
var settingsMap map[string]any
|
||||||
if err := c.ShouldBindJSON(&settingsMap); err != nil {
|
if err := c.ShouldBindJSON(&settingsMap); err != nil {
|
||||||
response.BadRequest(c, "Invalid request body")
|
response.BadRequest(c, "Invalid request body")
|
||||||
return
|
return
|
||||||
|
Reference in New Issue
Block a user