package structx import ( "encoding/json" "fmt" "reflect" "strconv" "strings" "code.yun.ink/pkg/convx" "github.com/spf13/cast" ) type ChangeInfo struct { Old string `json:"old"` New string `json:"new"` Val interface{} `json:"val"` } // 统一的类型转换函数 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, } // AttactToStructAny 将 map[string]interface{} 类型的值附加到结构体中 func 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 AttactToStruct(structxx, stringMap) } // AttactToStruct 将 map 的数据赋值到结构体中,支持嵌套结构体和指针 func 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 := buildFieldMap(t) fmt.Printf("字段映射2: %+v %+v\n", fieldMap,fieldMap["data"]) for mapKey, mapValue := range updateMap { fieldInfo, exists := fieldMap[mapKey] fmt.Printf("处理字段: %s, 信息: %+v\n", mapKey, fieldInfo) if !exists { return nil, fmt.Errorf("字段 %s 不存在", mapKey) } // 安全获取字段值,处理嵌套指针 field, err := getFieldByIndexSafe(v, fieldInfo.Index) if err != nil { return nil, fmt.Errorf("获取字段 %s 失败: %w", mapKey, err) } if !field.IsValid() { continue } // 处理指针类型 if fieldInfo.IsPtr { if field.IsNil() { newValue := reflect.New(fieldInfo.FieldType.Elem()) field.Set(newValue) } field = field.Elem() } // 处理切片和数组类型 if fieldInfo.IsSlice || fieldInfo.IsArray { err := processSliceOrArrayField(field, mapValue, mapKey) if err != nil { return nil, fmt.Errorf("处理切片/数组字段 %s 失败: %w", mapKey, err) } continue } // 处理嵌套结构体 if field.Kind() == reflect.Struct && !isBasicStructType(field.Type()) { nestedChanges, err := 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, err := cast.ToStringE(field.Interface()) if err != nil { return nil, fmt.Errorf("获取字段 %s 的旧值失败: %w", mapKey, err) } newValue, err := setFieldValue(field, mapValue) if err != nil { return nil, fmt.Errorf("设置字段 %s 的值失败: %w", mapKey, err) } newValueStr, err := cast.ToStringE(newValue) if err != nil { newValueStr = fmt.Sprintf("%v", newValue) } changeMap[mapKey] = ChangeInfo{ Old: oldValueStr, New: newValueStr, Val: newValue, } } return changeMap, nil } // 处理切片和数组字段 func processSliceOrArrayField(field reflect.Value, value string, fieldKey string) error { // 尝试解析JSON数组 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 的数组长度不匹配: 期望 %d, 实际 %d", fieldKey, fieldType.Len(), len(jsonData)) } newContainer = reflect.New(fieldType).Elem() } else { return fmt.Errorf("字段 %s 不是切片或数组类型", fieldKey) } // 填充数据 for i, item := range jsonData { elemValue := newContainer.Index(i) err := setSliceElementValue(elemValue, item, elemType) if err != nil { return fmt.Errorf("设置切片/数组元素失败: %w", err) } } field.Set(newContainer) return nil } // 设置切片/数组元素的值 func 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 setSliceElementValue(elemValue.Elem(), item, elemType.Elem()) } // 根据元素类型进行转换 switch elemType.Kind() { case reflect.String: if str, ok := item.(string); ok { elemValue.SetString(str) } else { elemValue.SetString(fmt.Sprintf("%v", item)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if num, ok := item.(float64); ok { // JSON数字默认是float64 elemValue.SetInt(int64(num)) } else { return fmt.Errorf("无法将 %v 转换为整型", item) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if num, ok := item.(float64); ok { elemValue.SetUint(uint64(num)) } else { return fmt.Errorf("无法将 %v 转换为无符号整型", item) } case reflect.Float32, reflect.Float64: if num, ok := item.(float64); ok { elemValue.SetFloat(num) } else { return fmt.Errorf("无法将 %v 转换为浮点型", item) } case reflect.Bool: if b, ok := item.(bool); ok { elemValue.SetBool(b) } else { return fmt.Errorf("无法将 %v 转换为布尔型", item) } case reflect.Struct: // 处理结构体元素 if isBasicStructType(elemType) { return setBasicStructElement(elemValue, item, elemType) } // 对于自定义结构体,需要JSON unmarshal jsonBytes, err := json.Marshal(item) if err != nil { return err } return json.Unmarshal(jsonBytes, elemValue.Addr().Interface()) default: return fmt.Errorf("不支持的切片元素类型: %s", elemType.Kind()) } return nil } // 设置基本结构体元素(如time.Time) func setBasicStructElement(elemValue reflect.Value, item interface{}, elemType reflect.Type) error { jsonBytes, err := json.Marshal(item) if err != nil { return err } return json.Unmarshal(jsonBytes, elemValue.Addr().Interface()) } // getFieldByIndexSafe 安全地通过索引路径获取字段,处理嵌套指针 func getFieldByIndexSafe(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() { // 创建新的指针实例 elemType := field.Type().Elem() newValue := reflect.New(elemType) field.Set(newValue) } field = field.Elem() } // 递归处理剩余的索引路径 if len(index) > 1 { return getFieldByIndexSafe(field, index[1:]) } return field, nil } // 构建字段映射表,支持嵌套结构体和指针类型 type fieldInfo struct { Index []int Name string IsPtr bool FieldType reflect.Type IsSlice bool // 新增:标识是否为切片或数组 IsArray bool // 新增:标识是否为数组 } func buildFieldMap(t reflect.Type) map[string]fieldInfo { fieldMap := make(map[string]fieldInfo) buildFieldMapRecursive(t, []int{}, fieldMap, "") fmt.Printf("字段映射表: %+v\n", fieldMap) return fieldMap } func buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]fieldInfo, prefix string) { for i := 0; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("处理字段 索引: %+v %+v\n", field, field.Type.Kind()) 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: true, } continue } // 如果是结构体且不是基本类型,递归处理 if actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) { buildFieldMapRecursive(actualType, currentIndex, fieldMap, fullKey) fieldMap[fullKey] = fieldInfo{ Index: currentIndex, Name: field.Name, IsPtr: isPtr, FieldType: fieldType, } continue } fieldMap[fullKey] = fieldInfo{ Index: currentIndex, Name: field.Name, IsPtr: isPtr, FieldType: fieldType, } } } func getJSONTagName(field reflect.StructField) string { jsonTag := field.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { return field.Name } return strings.Split(jsonTag, ",")[0] } // 判断是否为基本结构体类型(如time.Time等) func isBasicStructType(t reflect.Type) bool { // 处理指针类型 if t.Kind() == reflect.Ptr { t = t.Elem() } // 这里可以添加更多需要排除的基本结构体类型 if t.PkgPath() == "time" && t.Name() == "Time" { return true } // 其他常见的基本结构体类型 switch t.String() { case "time.Time", "sql.NullString", "sql.NullInt64", "sql.NullBool", "sql.NullFloat64": return true } return false } // 处理嵌套结构体(支持JSON解析) // processNestedStruct 处理嵌套结构体 func 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() { // 创建新的指针实例 newValue := reflect.New(field.Type().Elem()) field.Set(newValue) } structValue = field.Elem() } else { structValue = field } if !structValue.IsValid() || structValue.Kind() != reflect.Struct { return nil, fmt.Errorf("无效的结构体字段") } // 尝试解析JSON字符串到map var nestedMap map[string]string if err := json.Unmarshal([]byte(value), &nestedMap); err != nil { return nil, fmt.Errorf("嵌套结构体值必须是JSON格式: %w", err) } // 递归处理嵌套结构体 nestedChanges, err := AttactToStruct(structValue.Addr().Interface(), nestedMap) if err != nil { return nil, err } // 为嵌套字段的变更记录添加前缀 for key, change := range nestedChanges { fullKey := parentKey + "." + key changeMap[fullKey] = change } return changeMap, nil } // 检测是否为自定义类型 func isCustomType(t reflect.Type) bool { // 排除基本类型 if t.PkgPath() == "" { return false } // 排除已知的基本结构体类型 if isBasicStructType(t) { return false } // 排除接口类型 if t.Kind() == reflect.Interface { return false } return true } // 设置字段值 // 设置字段值 func setFieldValue(field reflect.Value, value string) (interface{}, error) { kind := field.Kind() // 检测是否为自定义类型(有包路径的类型) if isCustomType(field.Type()) { return setCustomTypeValue(field, value) } // 处理指针类型的基础类型 if kind == reflect.Ptr { return setPointerFieldValue(field, value) } converter, exists := typeConverters[kind] if !exists { return nil, fmt.Errorf("不支持的类型: %s", kind.String()) } result, err := converter(field, value) if err != nil { return nil, err } field.Set(reflect.ValueOf(result)) return result, nil } // 处理指针类型的字段 func setPointerFieldValue(field reflect.Value, value string) (interface{}, error) { if field.IsNil() { // 创建新的指针实例 elemType := field.Type().Elem() newValue := reflect.New(elemType) field.Set(newValue) } // 递归处理指针指向的值 return setFieldValue(field.Elem(), value) } // 类型转换函数 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) { // 实现map转换逻辑 return nil, fmt.Errorf("map转换未实现") } // 处理自定义类型 // 处理自定义类型 func setCustomTypeValue(field reflect.Value, value string) (interface{}, error) { // 检查是否实现了TextUnmarshaler接口 if unmarshaler, ok := field.Addr().Interface().(interface { UnmarshalText([]byte) error }); ok { err := unmarshaler.UnmarshalText([]byte(value)) if err != nil { return nil, err } return field.Interface(), nil } // 对于其他自定义类型,我们需要获取其基础类型并进行转换 baseType := getBaseType(field.Type()) if baseType == nil { return nil, fmt.Errorf("不支持的自定义类型: %s", field.Type().String()) } // 创建基础类型的值并进行转换 baseValue := reflect.New(baseType).Elem() converter, exists := typeConverters[baseType.Kind()] if !exists { return nil, fmt.Errorf("不支持的基础类型: %s", baseType.Kind().String()) } result, err := converter(baseValue, value) if err != nil { return nil, err } // 将基础类型值转换回自定义类型 convertedValue, err := convertToCustomType(field.Type(), result) if err != nil { return nil, err } field.Set(reflect.ValueOf(convertedValue)) return convertedValue, nil } // 获取自定义类型的基础类型 func getBaseType(t reflect.Type) reflect.Type { for t.Kind() == reflect.Ptr { t = t.Elem() } // 如果是自定义类型(有包路径),获取其底层类型 if t.PkgPath() != "" { return t } return t } // 将基础类型值转换为自定义类型 func convertToCustomType(customType reflect.Type, value interface{}) (interface{}, error) { valueType := reflect.TypeOf(value) customBaseType := customType // 处理指针类型的自定义类型 if customType.Kind() == reflect.Ptr { customBaseType = customType.Elem() // 创建新的指针实例 newValue := reflect.New(customBaseType) elemValue := newValue.Elem() // 尝试将值设置到元素 if valueType.AssignableTo(customBaseType) { elemValue.Set(reflect.ValueOf(value)) } else if reflect.ValueOf(value).Type().ConvertibleTo(customBaseType) { converted := reflect.ValueOf(value).Convert(customBaseType) elemValue.Set(converted) } else { return nil, fmt.Errorf("无法将 %v 转换为 %v", valueType, customBaseType) } return newValue.Interface(), nil } // 处理非指针类型的自定义类型 if valueType.AssignableTo(customType) { return value, nil } if reflect.ValueOf(value).Type().ConvertibleTo(customType) { return reflect.ValueOf(value).Convert(customType).Interface(), nil } return nil, fmt.Errorf("无法将 %v 转换为 %v", valueType, customType) }