Files

258 lines
6.8 KiB
Go
Raw Permalink Normal View History

2024-04-12 15:05:49 +08:00
package structx
import (
2025-09-20 20:28:59 +08:00
"encoding/json"
2024-04-12 15:05:49 +08:00
"fmt"
"reflect"
2024-10-28 21:37:58 +08:00
2025-09-20 20:28:59 +08:00
"github.com/spf13/cast"
2024-04-12 15:05:49 +08:00
)
2025-09-21 12:06:32 +08:00
// 全局函数(保持向后兼容)
func AttactToStructAny(structxx any, updateMap map[string]any) (map[string]ChangeInfo, error) {
return NewStructProcessor().AttactToStructAny(structxx, updateMap)
2025-09-21 00:40:12 +08:00
}
2025-09-21 12:06:32 +08:00
func AttactToStruct(structxx any, updateMap map[string]string) (map[string]ChangeInfo, error) {
return NewStructProcessor().AttactToStruct(structxx, updateMap)
2025-09-21 00:40:12 +08:00
}
// StructProcessor 结构体处理器
type StructProcessor struct {
2025-10-31 23:28:14 +08:00
options *Options
2025-09-21 00:40:12 +08:00
fieldMapper FieldMapper
valueSetter ValueSetter
2025-09-20 20:28:59 +08:00
}
2025-09-21 00:40:12 +08:00
// NewStructProcessor 创建新的结构体处理器
2025-10-31 23:28:14 +08:00
func NewStructProcessor(ops ...Option) *StructProcessor {
options := defaultOptions()
for _, op := range ops {
op(options)
}
2025-09-21 00:40:12 +08:00
return &StructProcessor{
2025-10-31 23:28:14 +08:00
options: options,
2025-09-21 00:40:12 +08:00
fieldMapper: &defaultFieldMapper{},
valueSetter: &defaultValueSetter{},
}
}
2025-09-21 00:30:53 +08:00
2025-09-21 00:40:12 +08:00
// AttactToStructAny 将 map[string]interface{} 转换为字符串映射并调用 AttactToStruct
2025-09-21 12:06:32 +08:00
func (sp *StructProcessor) AttactToStructAny(structxx any, updateMap map[string]any) (map[string]ChangeInfo, error) {
2025-09-20 20:28:59 +08:00
stringMap := make(map[string]string, len(updateMap))
2024-10-28 21:37:58 +08:00
for k, v := range updateMap {
2025-09-20 20:28:59 +08:00
str, err := cast.ToStringE(v)
2024-10-28 21:37:58 +08:00
if err != nil {
2025-09-20 20:28:59 +08:00
return nil, fmt.Errorf("转换键 %s 的值失败: %w", k, err)
2024-10-28 21:37:58 +08:00
}
2025-09-20 20:28:59 +08:00
stringMap[k] = str
2024-10-28 21:37:58 +08:00
}
2025-09-21 00:40:12 +08:00
return sp.AttactToStruct(structxx, stringMap)
2024-10-28 21:37:58 +08:00
}
2025-09-21 00:40:12 +08:00
// AttactToStruct 将映射数据赋值到结构体中
2025-09-21 12:06:32 +08:00
func (sp *StructProcessor) AttactToStruct(structxx any, updateMap map[string]string) (map[string]ChangeInfo, error) {
2024-04-12 15:05:49 +08:00
v := reflect.ValueOf(structxx)
if v.Kind() != reflect.Ptr || v.IsNil() {
2025-09-20 20:28:59 +08:00
return nil, fmt.Errorf("structxx 需要是非空指针")
2024-04-12 15:05:49 +08:00
}
changeMap := make(map[string]ChangeInfo)
v = v.Elem()
2025-09-20 20:28:59 +08:00
t := v.Type()
2025-10-31 23:28:14 +08:00
2025-09-21 00:40:12 +08:00
fieldMap := sp.fieldMapper.GetFieldMap(t)
2025-09-20 20:28:59 +08:00
for mapKey, mapValue := range updateMap {
fieldInfo, exists := fieldMap[mapKey]
if !exists {
2025-10-31 23:28:14 +08:00
if sp.options.allowUnknownFields {
continue
} else {
return nil, fmt.Errorf("字段 %s 不存在", mapKey)
}
2025-09-20 20:28:59 +08:00
}
2025-09-21 00:40:12 +08:00
field, err := sp.getFieldByPath(v, fieldInfo.Index)
if err != nil {
return nil, fmt.Errorf("获取字段 %s 失败: %w", mapKey, err)
2025-09-20 20:28:59 +08:00
}
2025-09-21 00:40:12 +08:00
if !field.IsValid() {
continue
2025-09-21 00:30:53 +08:00
}
2025-09-21 00:40:12 +08:00
// 处理指针字段
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()
2025-09-20 20:28:59 +08:00
}
2024-04-12 15:05:49 +08:00
2025-09-21 00:40:12 +08:00
// 处理切片和数组
2025-09-20 21:17:17 +08:00
if fieldInfo.IsSlice || fieldInfo.IsArray {
2025-09-21 00:40:12 +08:00
if err := sp.processSliceOrArrayField(field, mapValue, mapKey); err != nil {
2025-09-20 21:17:17 +08:00
return nil, fmt.Errorf("处理切片/数组字段 %s 失败: %w", mapKey, err)
}
continue
}
// 处理嵌套结构体
2025-09-21 00:40:12 +08:00
if field.Kind() == reflect.Struct && !isBasicStructType(field.Type()) {
nestedChanges, err := sp.processNestedStruct(field, mapValue, mapKey)
2025-09-20 20:28:59 +08:00
if err != nil {
return nil, fmt.Errorf("处理嵌套结构体字段 %s 失败: %w", mapKey, err)
}
for nestedKey, change := range nestedChanges {
changeMap[nestedKey] = change
}
continue
}
2025-09-21 00:40:12 +08:00
if !field.CanSet() {
2025-09-20 20:28:59 +08:00
continue
}
2025-09-21 00:40:12 +08:00
oldValueStr, _ := cast.ToStringE(field.Interface())
newValue, err := sp.valueSetter.SetFieldValue(field, mapValue)
2025-09-20 20:28:59 +08:00
if err != nil {
return nil, fmt.Errorf("设置字段 %s 的值失败: %w", mapKey, err)
}
2024-04-12 15:05:49 +08:00
2025-09-21 00:40:12 +08:00
newValueStr, _ := cast.ToStringE(newValue)
2025-09-20 20:28:59 +08:00
changeMap[mapKey] = ChangeInfo{
Old: oldValueStr,
New: newValueStr,
Val: newValue,
}
}
return changeMap, nil
}
2025-09-21 00:40:12 +08:00
// 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:])
}
2025-09-21 00:30:53 +08:00
2025-09-21 00:40:12 +08:00
return field, nil
}
// processSliceOrArrayField 处理切片和数组字段
func (sp *StructProcessor) processSliceOrArrayField(field reflect.Value, value string, fieldKey string) error {
2025-09-20 21:17:17 +08:00
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() {
2025-09-21 00:40:12 +08:00
return fmt.Errorf("字段 %s 的数组长度不匹配", fieldKey)
2025-09-20 21:17:17 +08:00
}
newContainer = reflect.New(fieldType).Elem()
} else {
return fmt.Errorf("字段 %s 不是切片或数组类型", fieldKey)
}
for i, item := range jsonData {
2025-09-21 00:40:12 +08:00
if err := sp.valueSetter.SetSliceElementValue(newContainer.Index(i), item, elemType); err != nil {
return fmt.Errorf("设置切片元素失败: %w", err)
2025-09-20 21:17:17 +08:00
}
}
field.Set(newContainer)
return nil
}
2025-09-21 00:40:12 +08:00
// 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()))
2025-09-20 21:17:17 +08:00
}
2025-09-21 00:40:12 +08:00
structValue = field.Elem()
} else {
structValue = field
2025-09-20 21:17:17 +08:00
}
2025-09-21 00:40:12 +08:00
if !structValue.IsValid() || structValue.Kind() != reflect.Struct {
return nil, fmt.Errorf("无效的结构体字段")
2025-09-20 23:13:52 +08:00
}
2025-09-21 00:40:12 +08:00
// 尝试解析为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
2025-09-20 21:17:17 +08:00
}
2025-09-21 00:40:12 +08:00
nestedChanges, err := sp.AttactToStruct(structValue.Addr().Interface(), stringMap)
2025-09-21 00:30:53 +08:00
if err != nil {
2025-09-21 00:40:12 +08:00
return nil, err
2025-09-20 21:17:17 +08:00
}
2025-09-21 00:40:12 +08:00
for key, change := range nestedChanges {
changeMap[parentKey+"."+key] = change
2025-09-20 21:17:17 +08:00
}
2025-09-21 00:40:12 +08:00
return changeMap, nil
2025-09-20 20:28:59 +08:00
}
2025-09-21 00:40:12 +08:00
// 尝试直接设置值
if hasUnmarshalJSON(structValue.Type()) || isBasicStructType(structValue.Type()) {
oldValueStr, _ := cast.ToStringE(field.Interface())
2025-09-21 12:06:32 +08:00
2025-09-21 00:40:12 +08:00
if err := setBasicStructValue(structValue, value); err != nil {
return nil, fmt.Errorf("设置基本结构体值失败: %w", err)
2025-09-20 20:28:59 +08:00
}
2025-09-21 00:40:12 +08:00
newValueStr, _ := cast.ToStringE(field.Interface())
changeMap[parentKey] = ChangeInfo{
Old: oldValueStr,
New: newValueStr,
Val: structValue.Interface(),
}
return changeMap, nil
2025-09-20 20:28:59 +08:00
}
2025-09-21 00:40:12 +08:00
return nil, fmt.Errorf("嵌套结构体值必须是有效的JSON格式")
2025-09-20 20:28:59 +08:00
}