Files
structx/structx.go
T
2025-09-20 21:17:17 +08:00

657 lines
17 KiB
Go

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