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
|
|
|
}
|