package structx import ( "encoding/json" "fmt" "reflect" "github.com/spf13/cast" ) // 全局函数(保持向后兼容) func AttactToStructAny(structxx any, updateMap map[string]any) (map[string]ChangeInfo, error) { return NewStructProcessor().AttactToStructAny(structxx, updateMap) } func AttactToStruct(structxx any, updateMap map[string]string) (map[string]ChangeInfo, error) { return NewStructProcessor().AttactToStruct(structxx, updateMap) } // StructProcessor 结构体处理器 type StructProcessor struct { options *Options fieldMapper FieldMapper valueSetter ValueSetter } // NewStructProcessor 创建新的结构体处理器 func NewStructProcessor(ops ...Option) *StructProcessor { options := defaultOptions() for _, op := range ops { op(options) } return &StructProcessor{ options: options, fieldMapper: &defaultFieldMapper{}, valueSetter: &defaultValueSetter{}, } } // AttactToStructAny 将 map[string]interface{} 转换为字符串映射并调用 AttactToStruct func (sp *StructProcessor) AttactToStructAny(structxx any, updateMap map[string]any) (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 any, 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 { if sp.options.allowUnknownFields { continue } else { 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格式") }