package structx import ( "encoding/json" "fmt" "reflect" "strconv" "strings" "sync" "code.yun.ink/pkg/convx" "github.com/spf13/cast" ) type ChangeInfo struct { Old string `json:"old"` New string `json:"new"` Val interface{} `json:"val"` } // FieldMapper 字段映射器接口 type FieldMapper interface { GetFieldMap(t reflect.Type) map[string]FieldInfo } // ValueSetter 值设置器接口 type ValueSetter interface { SetFieldValue(field reflect.Value, value string) (interface{}, error) SetSliceElementValue(elemValue reflect.Value, item interface{}, elemType reflect.Type) error } // converterFunc 类型转换函数 type converterFunc func(reflect.Value, string) (interface{}, error) var ( typeConverters = map[reflect.Kind]converterFunc{ reflect.Bool: convertBool, reflect.Int: convertInt[int], reflect.Int8: convertInt[int8], reflect.Int16: convertInt[int16], reflect.Int32: convertInt[int32], reflect.Int64: convertInt[int64], reflect.Uint: convertUint[uint], reflect.Uint8: convertUint[uint8], reflect.Uint16: convertUint[uint16], reflect.Uint32: convertUint[uint32], reflect.Uint64: convertUint[uint64], reflect.Float32: convertFloat[float32], reflect.Float64: convertFloat[float64], reflect.String: convertString, reflect.Slice: convertSlice, reflect.Array: convertArray, reflect.Map: convertMap, } typeInfoCache = make(map[reflect.Type]map[string]FieldInfo) cacheMutex = &sync.RWMutex{} basicStructTypes = map[string]bool{ "time.Time": true, "github.com/shopspring/decimal.Decimal": true, "sql.NullString": true, "sql.NullInt64": true, "sql.NullBool": true, "sql.NullFloat64": true, } basicKinds = map[reflect.Kind]bool{ reflect.String: true, reflect.Int: true, reflect.Int8: true, reflect.Int16: true, reflect.Int32: true, reflect.Int64: true, reflect.Uint: true, reflect.Uint8: true, reflect.Uint16: true, reflect.Uint32: true, reflect.Uint64: true, reflect.Float32: true, reflect.Float64: true, reflect.Bool: true, } ) // FieldInfo 字段信息 type FieldInfo struct { Index []int Name string IsPtr bool FieldType reflect.Type IsSlice bool IsArray bool } // StructProcessor 结构体处理器 type StructProcessor struct { fieldMapper FieldMapper valueSetter ValueSetter } // NewStructProcessor 创建新的结构体处理器 func NewStructProcessor() *StructProcessor { return &StructProcessor{ fieldMapper: &defaultFieldMapper{}, valueSetter: &defaultValueSetter{}, } } // AttactToStructAny 将 map[string]interface{} 转换为字符串映射并调用 AttactToStruct func (sp *StructProcessor) AttactToStructAny(structxx interface{}, updateMap map[string]interface{}) (map[string]ChangeInfo, error) { stringMap := make(map[string]string, len(updateMap)) for k, v := range updateMap { str, err := cast.ToStringE(v) if err != nil { return nil, fmt.Errorf("转换键 %s 的值失败: %w", k, err) } stringMap[k] = str } return sp.AttactToStruct(structxx, stringMap) } // AttactToStruct 将映射数据赋值到结构体中 func (sp *StructProcessor) AttactToStruct(structxx interface{}, updateMap map[string]string) (map[string]ChangeInfo, error) { v := reflect.ValueOf(structxx) if v.Kind() != reflect.Ptr || v.IsNil() { return nil, fmt.Errorf("structxx 需要是非空指针") } changeMap := make(map[string]ChangeInfo) v = v.Elem() t := v.Type() fieldMap := sp.fieldMapper.GetFieldMap(t) for mapKey, mapValue := range updateMap { fieldInfo, exists := fieldMap[mapKey] if !exists { return nil, fmt.Errorf("字段 %s 不存在", mapKey) } field, err := sp.getFieldByPath(v, fieldInfo.Index) if err != nil { return nil, fmt.Errorf("获取字段 %s 失败: %w", mapKey, err) } if !field.IsValid() { continue } // 处理指针字段 if fieldInfo.IsPtr { if field.Kind() != reflect.Ptr { return nil, fmt.Errorf("字段 %s 应该是指针类型", mapKey) } if field.IsNil() { field.Set(reflect.New(fieldInfo.FieldType.Elem())) } field = field.Elem() } // 处理切片和数组 if fieldInfo.IsSlice || fieldInfo.IsArray { if err := sp.processSliceOrArrayField(field, mapValue, mapKey); err != nil { return nil, fmt.Errorf("处理切片/数组字段 %s 失败: %w", mapKey, err) } continue } // 处理嵌套结构体 if field.Kind() == reflect.Struct && !isBasicStructType(field.Type()) { nestedChanges, err := sp.processNestedStruct(field, mapValue, mapKey) if err != nil { return nil, fmt.Errorf("处理嵌套结构体字段 %s 失败: %w", mapKey, err) } for nestedKey, change := range nestedChanges { changeMap[nestedKey] = change } continue } if !field.CanSet() { continue } oldValueStr, _ := cast.ToStringE(field.Interface()) newValue, err := sp.valueSetter.SetFieldValue(field, mapValue) if err != nil { return nil, fmt.Errorf("设置字段 %s 的值失败: %w", mapKey, err) } newValueStr, _ := cast.ToStringE(newValue) changeMap[mapKey] = ChangeInfo{ Old: oldValueStr, New: newValueStr, Val: newValue, } } return changeMap, nil } // getFieldByPath 通过路径获取字段 func (sp *StructProcessor) getFieldByPath(v reflect.Value, index []int) (reflect.Value, error) { if len(index) == 0 { return v, nil } field := v.Field(index[0]) if !field.IsValid() { return reflect.Value{}, fmt.Errorf("字段索引 %d 无效", index[0]) } // 处理指针 for field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } field = field.Elem() } if len(index) > 1 { return sp.getFieldByPath(field, index[1:]) } return field, nil } // processSliceOrArrayField 处理切片和数组字段 func (sp *StructProcessor) processSliceOrArrayField(field reflect.Value, value string, fieldKey string) error { var jsonData []interface{} if err := json.Unmarshal([]byte(value), &jsonData); err != nil { return fmt.Errorf("字段 %s 的值必须是JSON数组格式: %w", fieldKey, err) } fieldType := field.Type() elemType := fieldType.Elem() var newContainer reflect.Value if fieldType.Kind() == reflect.Slice { newContainer = reflect.MakeSlice(fieldType, len(jsonData), len(jsonData)) } else if fieldType.Kind() == reflect.Array { if len(jsonData) != fieldType.Len() { return fmt.Errorf("字段 %s 的数组长度不匹配", fieldKey) } newContainer = reflect.New(fieldType).Elem() } else { return fmt.Errorf("字段 %s 不是切片或数组类型", fieldKey) } for i, item := range jsonData { if err := sp.valueSetter.SetSliceElementValue(newContainer.Index(i), item, elemType); err != nil { return fmt.Errorf("设置切片元素失败: %w", err) } } field.Set(newContainer) return nil } // processNestedStruct 处理嵌套结构体 func (sp *StructProcessor) processNestedStruct(field reflect.Value, value string, parentKey string) (map[string]ChangeInfo, error) { changeMap := make(map[string]ChangeInfo) var structValue reflect.Value if field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } structValue = field.Elem() } else { structValue = field } if !structValue.IsValid() || structValue.Kind() != reflect.Struct { return nil, fmt.Errorf("无效的结构体字段") } // 尝试解析为JSON对象 var nestedMap map[string]interface{} if err := json.Unmarshal([]byte(value), &nestedMap); err == nil { stringMap := make(map[string]string, len(nestedMap)) for k, v := range nestedMap { str, err := cast.ToStringE(v) if err != nil { return nil, fmt.Errorf("转换嵌套字段 %s 的值失败: %w", k, err) } stringMap[k] = str } nestedChanges, err := sp.AttactToStruct(structValue.Addr().Interface(), stringMap) if err != nil { return nil, err } for key, change := range nestedChanges { changeMap[parentKey+"."+key] = change } return changeMap, nil } // 尝试直接设置值 if hasUnmarshalJSON(structValue.Type()) || isBasicStructType(structValue.Type()) { oldValueStr, _ := cast.ToStringE(field.Interface()) if err := setBasicStructValue(structValue, value); err != nil { return nil, fmt.Errorf("设置基本结构体值失败: %w", err) } newValueStr, _ := cast.ToStringE(field.Interface()) changeMap[parentKey] = ChangeInfo{ Old: oldValueStr, New: newValueStr, Val: structValue.Interface(), } return changeMap, nil } return nil, fmt.Errorf("嵌套结构体值必须是有效的JSON格式") } // defaultFieldMapper 默认字段映射器 type defaultFieldMapper struct{} func (dm *defaultFieldMapper) GetFieldMap(t reflect.Type) map[string]FieldInfo { cacheMutex.RLock() if cached, exists := typeInfoCache[t]; exists { cacheMutex.RUnlock() return cached } cacheMutex.RUnlock() fieldMap := make(map[string]FieldInfo) dm.buildFieldMapRecursive(t, []int{}, fieldMap, "") cacheMutex.Lock() typeInfoCache[t] = fieldMap cacheMutex.Unlock() return fieldMap } func (dm *defaultFieldMapper) buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]FieldInfo, prefix string) { for i := 0; i < t.NumField(); i++ { field := t.Field(i) if !field.IsExported() { continue } currentIndex := append(index, i) jsonTag := getJSONTagName(field) fullKey := jsonTag if prefix != "" { fullKey = prefix + "." + jsonTag } fieldType := field.Type isPtr := fieldType.Kind() == reflect.Ptr actualType := fieldType if isPtr { actualType = fieldType.Elem() } if actualType.Kind() == reflect.Slice || actualType.Kind() == reflect.Array { fieldMap[fullKey] = FieldInfo{ Index: currentIndex, Name: field.Name, IsPtr: isPtr, FieldType: fieldType, IsSlice: actualType.Kind() == reflect.Slice, IsArray: actualType.Kind() == reflect.Array, } continue } if actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) { dm.buildFieldMapRecursive(actualType, currentIndex, fieldMap, fullKey) } fieldMap[fullKey] = FieldInfo{ Index: currentIndex, Name: field.Name, IsPtr: isPtr, FieldType: fieldType, } } } // defaultValueSetter 默认值设置器 type defaultValueSetter struct{} func (ds *defaultValueSetter) SetFieldValue(field reflect.Value, value string) (interface{}, error) { fieldType := field.Type() if hasUnmarshalJSON(fieldType) { if err := setUnmarshalJSONValue(field, value); err != nil { return nil, fmt.Errorf("UnmarshalJSON失败: %w", err) } return field.Interface(), nil } if hasUnmarshalText(fieldType) { if err := setUnmarshalTextValue(field, value); err != nil { return nil, fmt.Errorf("UnmarshalText失败: %w", err) } return field.Interface(), nil } if fieldType.Kind() == reflect.Ptr { return setPointerFieldValue(field, value) } if isTypeAlias(fieldType) { return setTypeAliasValue(field, value) } if isCustomStructType(fieldType) { return setCustomTypeValue(field, value) } converter, exists := typeConverters[fieldType.Kind()] if !exists { return nil, fmt.Errorf("不支持的类型: %s", fieldType.Kind()) } result, err := converter(field, value) if err != nil { return nil, err } field.Set(reflect.ValueOf(result)) return result, nil } func (ds *defaultValueSetter) SetSliceElementValue(elemValue reflect.Value, item interface{}, elemType reflect.Type) error { if elemType.Kind() == reflect.Ptr { if elemValue.IsNil() { elemValue.Set(reflect.New(elemType.Elem())) } return ds.SetSliceElementValue(elemValue.Elem(), item, elemType.Elem()) } if hasUnmarshalJSON(elemType) { return setUnmarshalJSONValue(elemValue, item) } switch elemType.Kind() { case reflect.String: elemValue.SetString(convertToString(item)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: num, err := convertToFloat64(item) if err != nil { return fmt.Errorf("无法转换为整型: %w", err) } elemValue.SetInt(int64(num)) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: num, err := convertToFloat64(item) if err != nil { return fmt.Errorf("无法转换为无符号整型: %w", err) } elemValue.SetUint(uint64(num)) case reflect.Float32, reflect.Float64: num, err := convertToFloat64(item) if err != nil { return fmt.Errorf("无法转换为浮点型: %w", err) } elemValue.SetFloat(num) case reflect.Bool: if b, ok := item.(bool); ok { elemValue.SetBool(b) } else { return fmt.Errorf("无法转换为布尔型") } case reflect.Struct: if isBasicStructType(elemType) { return setBasicStructElement(elemValue, item) } return setStructElement(elemValue, item) default: return fmt.Errorf("不支持的切片元素类型: %s", elemType.Kind()) } return nil } // 工具函数 func getJSONTagName(field reflect.StructField) string { jsonTag := field.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { return field.Name } return strings.Split(jsonTag, ",")[0] } func isBasicStructType(t reflect.Type) bool { if t.Kind() == reflect.Ptr { t = t.Elem() } return basicStructTypes[t.String()] } func hasUnmarshalJSON(t reflect.Type) bool { if t.Kind() == reflect.Ptr { t = t.Elem() } unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() return t.Implements(unmarshalerType) || reflect.PtrTo(t).Implements(unmarshalerType) } func hasUnmarshalText(t reflect.Type) bool { if t.Kind() == reflect.Ptr { t = t.Elem() } textUnmarshalerType := reflect.TypeOf((*interface { UnmarshalText([]byte) error })(nil)).Elem() return t.Implements(textUnmarshalerType) || reflect.PtrTo(t).Implements(textUnmarshalerType) } func setUnmarshalJSONValue(field reflect.Value, value interface{}) error { jsonBytes, err := json.Marshal(value) if err != nil { return err } var fieldAddr reflect.Value if field.CanAddr() { fieldAddr = field.Addr() } else { temp := reflect.New(field.Type()) temp.Elem().Set(field) fieldAddr = temp } if unmarshaler, ok := fieldAddr.Interface().(json.Unmarshaler); ok { return unmarshaler.UnmarshalJSON(jsonBytes) } return fmt.Errorf("类型未实现Unmarshaler") } func setUnmarshalTextValue(field reflect.Value, value string) error { var fieldAddr reflect.Value if field.CanAddr() { fieldAddr = field.Addr() } else { temp := reflect.New(field.Type()) temp.Elem().Set(field) fieldAddr = temp } if unmarshaler, ok := fieldAddr.Interface().(interface { UnmarshalText([]byte) error }); ok { return unmarshaler.UnmarshalText([]byte(value)) } return fmt.Errorf("类型未实现UnmarshalText") } func setBasicStructValue(field reflect.Value, value string) error { if hasUnmarshalText(field.Type()) { return setUnmarshalTextValue(field, value) } return json.Unmarshal([]byte(value), field.Addr().Interface()) } func setPointerFieldValue(field reflect.Value, value string) (interface{}, error) { if field.Kind() != reflect.Ptr { return nil, fmt.Errorf("期望指针类型") } if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } return new(defaultValueSetter).SetFieldValue(field.Elem(), value) } func isTypeAlias(t reflect.Type) bool { if t.Kind() == reflect.Ptr { t = t.Elem() } return t.PkgPath() != "" && basicKinds[t.Kind()] } func setTypeAliasValue(field reflect.Value, value string) (interface{}, error) { baseType := getBaseTypeFromAlias(field.Type()) if baseType == nil { return nil, fmt.Errorf("无法获取基础类型") } converter, exists := typeConverters[baseType.Kind()] if !exists { return nil, fmt.Errorf("不支持的基础类型") } baseValue := reflect.New(baseType).Elem() result, err := converter(baseValue, value) if err != nil { return nil, err } convertedValue, err := convertToTypeAlias(field.Type(), result) if err != nil { return nil, err } field.Set(reflect.ValueOf(convertedValue)) return convertedValue, nil } func isCustomStructType(t reflect.Type) bool { if t.Kind() == reflect.Ptr { t = t.Elem() } return t.PkgPath() != "" && !isBasicStructType(t) && !isTypeAlias(t) && t.Kind() == reflect.Struct } func setCustomTypeValue(field reflect.Value, value string) (interface{}, error) { if hasUnmarshalText(field.Type()) { if err := setUnmarshalTextValue(field, value); err != nil { return nil, err } return field.Interface(), nil } var jsonData interface{} if err := json.Unmarshal([]byte(value), &jsonData); err != nil { return nil, err } jsonBytes, err := json.Marshal(jsonData) if err != nil { return nil, err } if err := json.Unmarshal(jsonBytes, field.Addr().Interface()); err != nil { return nil, err } return field.Interface(), nil } func setBasicStructElement(elemValue reflect.Value, item interface{}) error { return setStructElement(elemValue, item) } func setStructElement(elemValue reflect.Value, item interface{}) error { jsonBytes, err := json.Marshal(item) if err != nil { return err } return json.Unmarshal(jsonBytes, elemValue.Addr().Interface()) } func convertToString(item interface{}) string { if str, ok := item.(string); ok { return str } return fmt.Sprintf("%v", item) } func convertToFloat64(item interface{}) (float64, error) { switch v := item.(type) { case float64: return v, nil case int, int32, int64: return float64(reflect.ValueOf(v).Int()), nil case uint, uint32, uint64: return float64(reflect.ValueOf(v).Uint()), nil case float32: return float64(v), nil default: return 0, fmt.Errorf("无法转换为数字") } } func getBaseTypeFromAlias(aliasType reflect.Type) reflect.Type { if aliasType.Kind() == reflect.Ptr { aliasType = aliasType.Elem() } switch aliasType.Kind() { case reflect.String: return reflect.TypeOf("") case reflect.Int: return reflect.TypeOf(int(0)) case reflect.Int8: return reflect.TypeOf(int8(0)) case reflect.Int16: return reflect.TypeOf(int16(0)) case reflect.Int32: return reflect.TypeOf(int32(0)) case reflect.Int64: return reflect.TypeOf(int64(0)) case reflect.Uint: return reflect.TypeOf(uint(0)) case reflect.Uint8: return reflect.TypeOf(uint8(0)) case reflect.Uint16: return reflect.TypeOf(uint16(0)) case reflect.Uint32: return reflect.TypeOf(uint32(0)) case reflect.Uint64: return reflect.TypeOf(uint64(0)) case reflect.Float32: return reflect.TypeOf(float32(0)) case reflect.Float64: return reflect.TypeOf(float64(0)) case reflect.Bool: return reflect.TypeOf(false) default: return nil } } func convertToTypeAlias(aliasType reflect.Type, value interface{}) (interface{}, error) { if aliasType.Kind() == reflect.Ptr { elemType := aliasType.Elem() newValue := reflect.New(elemType) elemValue := newValue.Elem() converted, err := convertValueToType(value, elemType) if err != nil { return nil, err } elemValue.Set(reflect.ValueOf(converted)) return newValue.Interface(), nil } return convertValueToType(value, aliasType) } func convertValueToType(value interface{}, targetType reflect.Type) (interface{}, error) { valueType := reflect.TypeOf(value) if valueType.AssignableTo(targetType) { return value, nil } if valueType.ConvertibleTo(targetType) { return reflect.ValueOf(value).Convert(targetType).Interface(), nil } return nil, fmt.Errorf("无法转换类型") } // 类型转换函数 func convertBool(field reflect.Value, value string) (interface{}, error) { return convx.ToBool(value) } func convertInt[T int | int8 | int16 | int32 | int64](field reflect.Value, value string) (interface{}, error) { bits := field.Type().Bits() intVal, err := strconv.ParseInt(value, 10, bits) if err != nil { return nil, err } return T(intVal), nil } func convertUint[T uint | uint8 | uint16 | uint32 | uint64](field reflect.Value, value string) (interface{}, error) { bits := field.Type().Bits() uintVal, err := strconv.ParseUint(value, 10, bits) if err != nil { return nil, err } return T(uintVal), nil } func convertFloat[T float32 | float64](field reflect.Value, value string) (interface{}, error) { bits := field.Type().Bits() floatVal, err := strconv.ParseFloat(value, bits) if err != nil { return nil, err } return T(floatVal), nil } func convertString(field reflect.Value, value string) (interface{}, error) { return value, nil } func convertSlice(field reflect.Value, value string) (interface{}, error) { var result []interface{} if err := json.Unmarshal([]byte(value), &result); err != nil { return nil, err } return result, nil } func convertArray(field reflect.Value, value string) (interface{}, error) { var result []interface{} if err := json.Unmarshal([]byte(value), &result); err != nil { return nil, err } return result, nil } func convertMap(field reflect.Value, value string) (interface{}, error) { return nil, fmt.Errorf("map转换未实现") } // 全局函数(保持向后兼容) func AttactToStructAny(structxx interface{}, updateMap map[string]interface{}) (map[string]ChangeInfo, error) { return NewStructProcessor().AttactToStructAny(structxx, updateMap) } func AttactToStruct(structxx interface{}, updateMap map[string]string) (map[string]ChangeInfo, error) { return NewStructProcessor().AttactToStruct(structxx, updateMap) }