package tag import ( "git.sxidc.com/go-tools/utils/reflectutils" "git.sxidc.com/go-tools/utils/strutils" "git.sxidc.com/service-supports/fserr" "git.sxidc.com/service-supports/fslog" "reflect" "strings" "time" ) func AssignTo[T any](from any) (T, error) { var zero T if from == nil { return zero, nil } fromValue := reflect.ValueOf(from) retType := reflect.TypeOf(zero) retValue := reflect.New(retType).Elem() if retValue.Kind() == reflect.Ptr { retValue.Set(reflect.New(retType.Elem())) } // 类型校验 if fromValue.Kind() != reflect.Ptr && fromValue.Kind() != reflect.Struct { return zero, fserr.New("参数不是结构或结构指针") } if fromValue.Kind() == reflect.Ptr && fromValue.Elem().Kind() != reflect.Struct { return zero, fserr.New("参数不是结构或结构指针") } if retValue.Kind() != reflect.Ptr && retValue.Kind() != reflect.Struct { return zero, fserr.New("返回类型不是结构或结构指针") } if retValue.Kind() == reflect.Ptr && retValue.Elem().Kind() != reflect.Struct { return zero, fserr.New("返回类型不是结构或结构指针") } fromElemValue := fromValue if fromValue.Kind() == reflect.Ptr { fromElemValue = fromValue.Elem() } retElemValue := retValue if retValue.Kind() == reflect.Ptr { retElemValue = retValue.Elem() } err := assignTo(fromElemValue, &retElemValue) if err != nil { return zero, err } return retValue.Interface().(T), nil } func assignTo(fromElemValue reflect.Value, retElemValue *reflect.Value) error { for i := 0; i < fromElemValue.NumField(); i++ { fromField := fromElemValue.Type().Field(i) tagStr := fromField.Tag.Get(assignTagKey) // 结构上没有添加Tag, 先尝试直接按照字段赋值结构,如果失败,进一步进入内部尝试 if strutils.IsStringEmpty(tagStr) && ((fromField.Type.Kind() == reflect.Struct && fromField.Type.String() != "time.Time") || (fromField.Type.Kind() == reflect.Ptr && fromField.Type.Elem().Kind() == reflect.Struct && fromField.Type.Elem().String() != "time.Time")) { fromStructElemValue := fromElemValue.Field(i) if fromField.Type.Kind() == reflect.Ptr { if !fromStructElemValue.IsValid() || fromStructElemValue.IsZero() { continue } fromStructElemValue = fromStructElemValue.Elem() } err := assignTo(fromStructElemValue, retElemValue) if err != nil { return err } } tag, err := parseAssignTag(fromField, tagStr) if err != nil { return err } if tag == nil { continue } fromFieldValue := fromElemValue.Field(i) // 无效零值,不进行赋值 if !fromFieldValue.IsValid() || fromFieldValue.IsZero() { continue } fromFieldElemValue := fromFieldValue if fromFieldValue.Kind() == reflect.Ptr { fromFieldElemValue = fromFieldValue.Elem() } retFieldValue := retElemValue.FieldByName(tag.ToField) // 不存在对应的字段 if !retFieldValue.IsValid() { continue } retFieldElemValue := retFieldValue if retFieldValue.Kind() == reflect.Ptr { if !retFieldValue.IsValid() { continue } if !retFieldValue.CanSet() { continue } // 空值针,初始化 if retFieldValue.IsNil() { retFieldValue.Set(reflect.New(retFieldValue.Type().Elem())) } retFieldElemValue = retFieldValue.Elem() } err = assignField(fromFieldElemValue, retFieldElemValue, tag) if err != nil { return err } } return nil } func assignField(fromFieldElemValue reflect.Value, retFieldElemValue reflect.Value, tag *assignTag) error { fromKind := reflectutils.GroupValueKind(fromFieldElemValue) retKind := reflectutils.GroupValueKind(retFieldElemValue) var fromAny any switch fromKind { case reflect.Struct: if fromFieldElemValue.Type().String() == "time.Time" && retKind == reflect.String { fromString := fromFieldElemValue.Interface().(time.Time).Format(tag.FormatTime) fromAny = trimFromString(fromString, tag) break } if fromFieldElemValue.Type().String() != "time.Time" && retKind == reflect.Struct { return assignTo(fromFieldElemValue, &retFieldElemValue) } fromAny = fromFieldElemValue.Interface() case reflect.Slice: if fromFieldElemValue.Elem().Kind() == reflect.String && retKind == reflect.String { fromString := strings.Join(fromFieldElemValue.Interface().([]string), tag.JoinWith) fromAny = trimFromString(fromString, tag) break } fromAny = fromFieldElemValue.Interface() case reflect.String: fromString := fromFieldElemValue.String() if retKind == reflect.Struct && retFieldElemValue.Type().String() == "time.Time" { retTimeField, err := time.ParseInLocation(tag.ParseTime, fromString, time.Local) if err != nil { return err } fromAny = retTimeField break } if retFieldElemValue.Kind() == reflect.Slice && retFieldElemValue.Elem().Kind() == reflect.String { fromAny = strings.Split(fromString, tag.SplitWith) break } fromAny = trimFromString(fromString, tag) default: fromAny = fromFieldElemValue.Interface() } switch retKind { case reflect.Int64: return reflectutils.AssignInt64Value(fromAny, retFieldElemValue) case reflect.Uint64: return reflectutils.AssignUint64Value(fromAny, retFieldElemValue) case reflect.Float64: return reflectutils.AssignFloat64Value(fromAny, retFieldElemValue) case reflect.Bool: return reflectutils.AssignBoolValue(fromAny, retFieldElemValue) case reflect.String: return reflectutils.AssignStringValue(fromAny, retFieldElemValue) default: retFieldElemValue.Set(reflect.ValueOf(fromAny)) return nil } } func trimFromString(fromString string, tag *assignTag) string { if strutils.IsStringNotEmpty(tag.Trim) { return strings.Trim(fromString, tag.Trim) } else { if strutils.IsStringNotEmpty(tag.TrimPrefix) { return strings.TrimPrefix(fromString, tag.TrimPrefix) } if strutils.IsStringNotEmpty(tag.TrimSuffix) { return strings.TrimSuffix(fromString, tag.TrimSuffix) } } return fromString } const ( assignDefaultStringSliceSeparator = "::" assignTagPartSeparator = ";" assignTagPartKeyValueSeparator = ":" ) const ( assignTagKey = "assign" assignIgnore = "-" assignToField = "toField" assignParseTime = "parseTime" assignFormatTime = "formatTime" assignJoinWith = "joinWith" assignSplitWith = "splitWith" assignTrim = "trim" assignTrimPrefix = "trimPrefix" assignTrimSuffix = "trimSuffix" ) type assignTag struct { ToField string ParseTime string FormatTime string JoinWith string SplitWith string Trim string TrimPrefix string TrimSuffix string } func parseAssignTag(field reflect.StructField, tagStr string) (*assignTag, error) { if tagStr == assignIgnore { return nil, nil } tag := &assignTag{ ToField: field.Name, ParseTime: time.DateTime, FormatTime: time.DateTime, JoinWith: assignDefaultStringSliceSeparator, SplitWith: assignDefaultStringSliceSeparator, Trim: "", TrimPrefix: "", TrimSuffix: "", } if strutils.IsStringEmpty(tagStr) { return tag, nil } assignParts := strings.Split(tagStr, assignTagPartSeparator) if assignParts != nil || len(assignParts) != 0 { for _, assignPart := range assignParts { assignPartKeyValue := strings.SplitN(strings.TrimSpace(assignPart), assignTagPartKeyValueSeparator, 2) if assignPartKeyValue != nil && len(assignPartKeyValue) == 2 && strutils.IsStringNotEmpty(assignPartKeyValue[1]) { // 可以支持' ' ',获取到'字符 assignPartKeyValue[1] = strings.TrimSpace(strings.Trim(assignPartKeyValue[1], "'")) } switch assignPartKeyValue[0] { case assignToField: tag.ToField = assignPartKeyValue[1] case assignParseTime: tag.ParseTime = assignPartKeyValue[1] case assignFormatTime: tag.FormatTime = assignPartKeyValue[1] case assignJoinWith: if strutils.IsStringEmpty(assignPartKeyValue[1]) { return nil, fserr.New(assignJoinWith + "没有赋值分隔符") } tag.JoinWith = assignPartKeyValue[1] case assignSplitWith: if strutils.IsStringEmpty(assignPartKeyValue[1]) { return nil, fserr.New(assignSplitWith + "没有赋值分隔符") } tag.SplitWith = assignPartKeyValue[1] case assignTrim: tag.Trim = assignPartKeyValue[1] case assignTrimPrefix: tag.TrimPrefix = assignPartKeyValue[1] case assignTrimSuffix: tag.TrimSuffix = assignPartKeyValue[1] default: err := fserr.New(assignTagKey + "不支持的tag: " + assignPartKeyValue[0]) fslog.Error(err) continue } } } return tag, nil }