Browse Source

完成assign tag开发

yjp 1 year ago
parent
commit
8f05da48b3
1 changed files with 191 additions and 137 deletions
  1. 191 137
      tag/assign_struct.go

+ 191 - 137
tag/assign_struct.go

@@ -1,160 +1,240 @@
 package tag
 
 import (
-	"git.sxidc.com/go-framework/baize/infrastructure/logger"
+	"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[F any, T any](from F) T {
+func AssignTo[T any](from any) (T, error) {
 	var zero T
 
-	assign, err := ParseAssignTag(from)
-	if err != nil {
-		logger.GetInstance().Error(err)
-		return zero
+	if from == nil {
+		return zero, nil
 	}
 
-	retType := reflect.TypeOf(zero)
-	retValue := reflect.New(retType).Elem()
-
-	return retValue.Interface()
-}
-
-const (
-	assignDefaultStringSliceSeparator = "::"
-	assignTagPartSeparator            = ";"
-	assignTagPartKeyValueSeparator    = ":"
-)
-
-const (
-	assignTagKey     = "assign"
-	assignIgnore     = "-"
-	assignToField    = "toField"
-	assignParseTime  = "parseTime"
-	assignFormatTime = "formatTime"
-	assignJoinWith   = "joinWith"
-	assignSplitWith  = "splitWith"
-	assignTrim       = "trim"
-	assignTrimLeft   = "trimLeft"
-	assignTrimRight  = "trimRight"
-)
-
-type Assign struct {
-	AssignElement map[string]any
-}
-
-type AssignElement struct {
-	ToField    string
-	ParseTime  string
-	FormatTime string
-	JoinWith   string
-	SplitWith  string
-	Trim       string
-	TrimLeft   string
-	TrimRight  string
-
-	// 原字段的反射结构
-	OriginFieldType  reflect.Type
-	OriginFieldValue reflect.Value
-
-	// 值类型的反射结构
-	FieldTypeElem  reflect.Type
-	FieldValueElem reflect.Value
-}
+	fromValue := reflect.ValueOf(from)
+	retValue := reflect.ValueOf(zero)
 
-func ParseAssignTag(from any) (*Assign, error) {
-	if from == nil {
-		return nil, fserr.New("没有传递结构或结构指针")
+	// 类型校验
+	if fromValue.Kind() != reflect.Ptr && fromValue.Kind() != reflect.Struct {
+		return zero, fserr.New("参数不是结构或结构指针")
 	}
 
-	fromType := reflect.TypeOf(from)
+	if fromValue.Kind() == reflect.Ptr && fromValue.Kind() != reflect.Struct {
+		return zero, fserr.New("参数不是结构或结构指针")
+	}
 
-	if fromType.Kind() != reflect.Ptr && fromType.Kind() != reflect.Struct {
-		return nil, fserr.New("参数不是结构或结构指针")
+	if retValue.Kind() != reflect.Ptr && retValue.Kind() != reflect.Struct {
+		return zero, fserr.New("返回类型不是结构或结构指针")
 	}
 
-	if fromType.Kind() == reflect.Ptr && fromType.Elem().Kind() != reflect.Struct {
-		return nil, fserr.New("参数不是结构或结构指针")
+	if retValue.Kind() == reflect.Ptr && retValue.Elem().Kind() != reflect.Struct {
+		return zero, fserr.New("返回类型不是结构或结构指针")
 	}
 
-	fromValue := reflect.ValueOf(from)
 	fromElemValue := fromValue
-	if fromType.Kind() == reflect.Ptr {
+	if fromValue.Kind() == reflect.Ptr {
 		fromElemValue = fromValue.Elem()
 	}
 
-	assign := new(Assign)
-	assign.AssignElement = make(map[string]any)
+	retElemValue := retValue
+	if retValue.Kind() == reflect.Ptr {
+		retElemValue = retValue.Elem()
+	}
 
 	for i := 0; i < fromElemValue.NumField(); i++ {
-		field := fromType.Field(i)
-		fieldValue := fromElemValue.Field(i)
+		fromField := fromElemValue.Type().Field(i)
+		tag, err := parseAssignTag(fromField)
+		if err != nil {
+			return zero, err
+		}
 
-		// 零值,不用赋值
-		if !fieldValue.IsValid() {
+		if tag == nil {
 			continue
 		}
 
-		element, err := parseSqlMappingElement(field, fieldValue)
-		if err != nil {
-			return nil, err
+		fromFieldValue := fromElemValue.Field(i)
+
+		// 无效零值,不进行赋值
+		if !fromFieldValue.IsValid() || fromFieldValue.IsNil() || fromFieldValue.IsZero() {
+			continue
 		}
 
-		if element == nil {
+		fromFieldElemValue := fromFieldValue
+		if fromFieldValue.Kind() == reflect.Ptr {
+			fromFieldElemValue = fromFieldValue.Elem()
+		}
+
+		retFieldValue := retElemValue.FieldByName(tag.ToField)
+
+		// 不存在对应的字段
+		if !retFieldValue.IsValid() {
 			continue
 		}
 
-		assign.AssignElement[field.Name] = element
+		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 = assignTo(fromFieldElemValue, retFieldElemValue, tag)
+		if err != nil {
+			return zero, err
+		}
 	}
 
-	return assign, nil
+	return retValue.Interface(), nil
 }
 
-func parseSqlMappingElement(field reflect.StructField, fieldValue reflect.Value) (any, error) {
-	assignTag := field.Tag.Get(assignTagKey)
-	if assignTag == assignIgnore {
-		return nil, nil
+func assignTo(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
+		}
+
+		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()
 	}
 
-	fieldValueElemType := field.Type
-	if field.Type.Kind() == reflect.Ptr {
-		fieldValueElemType = field.Type.Elem()
+	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
 	}
+}
 
-	fieldValueElem := fieldValue
-	if fieldValue.Kind() == reflect.Ptr {
-		fieldValueElem = fieldValue.Elem()
+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)
+		}
 	}
 
-	if fieldValueElemType.Kind() == reflect.Struct && fieldValueElemType != reflect.TypeOf(time.Time{}) {
-		return ParseAssignTag(fieldValueElem.Interface())
+	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) (*assignTag, error) {
+	tagStr := field.Tag.Get(assignTagKey)
+
+	if tagStr == assignIgnore {
+		return nil, nil
 	}
 
-	element := &AssignElement{
+	tag := &assignTag{
 		ToField:    field.Name,
 		ParseTime:  time.DateTime,
 		FormatTime: time.DateTime,
 		JoinWith:   assignDefaultStringSliceSeparator,
 		SplitWith:  assignDefaultStringSliceSeparator,
 		Trim:       "",
-		TrimLeft:   "",
-		TrimRight:  "",
-
-		OriginFieldType:  field.Type,
-		OriginFieldValue: fieldValue,
-		FieldTypeElem:    fieldValueElemType,
-		FieldValueElem:   fieldValueElem,
+		TrimPrefix: "",
+		TrimSuffix: "",
 	}
 
-	if strutils.IsStringEmpty(assignTag) {
-		return element, nil
+	if strutils.IsStringEmpty(tagStr) {
+		return tag, nil
 	}
 
-	assignParts := strings.Split(assignTag, assignTagPartSeparator)
+	assignParts := strings.Split(tagStr, assignTagPartSeparator)
 	if assignParts != nil || len(assignParts) != 0 {
 		for _, assignPart := range assignParts {
 			assignPartKeyValue := strings.SplitN(strings.TrimSpace(assignPart), assignTagPartKeyValueSeparator, 2)
@@ -165,62 +245,36 @@ func parseSqlMappingElement(field reflect.StructField, fieldValue reflect.Value)
 
 			switch assignPartKeyValue[0] {
 			case assignToField:
-				element.ToField = assignPartKeyValue[1]
+				tag.ToField = assignPartKeyValue[1]
 			case assignParseTime:
-				if fieldValueElemType.Elem().Kind() != reflect.String {
-					return nil, fserr.New(assignParseTime + "应该添加在string字段上")
-				}
-
-				element.ParseTime = assignPartKeyValue[1]
+				tag.ParseTime = assignPartKeyValue[1]
 			case assignFormatTime:
-				if fieldValueElemType.Elem().Kind() != reflect.Struct && fieldValueElemType != reflect.TypeOf(time.Time{}) {
-					return nil, fserr.New(assignFormatTime + "应该添加在time.Time字段上")
-				}
-
-				element.FormatTime = assignPartKeyValue[1]
+				tag.FormatTime = assignPartKeyValue[1]
 			case assignJoinWith:
 				if strutils.IsStringEmpty(assignPartKeyValue[1]) {
 					return nil, fserr.New(assignJoinWith + "没有赋值分隔符")
 				}
 
-				if fieldValueElemType.Kind() != reflect.Slice || fieldValueElemType.Elem().Kind() != reflect.String {
-					return nil, fserr.New(assignJoinWith + "应该添加在[]string字段上")
-				}
-
-				element.JoinWith = assignPartKeyValue[1]
+				tag.JoinWith = assignPartKeyValue[1]
 			case assignSplitWith:
 				if strutils.IsStringEmpty(assignPartKeyValue[1]) {
 					return nil, fserr.New(assignSplitWith + "没有赋值分隔符")
 				}
 
-				if fieldValueElemType.Elem().Kind() != reflect.String {
-					return nil, fserr.New(assignSplitWith + "应该添加在string字段上")
-				}
-
-				element.SplitWith = assignPartKeyValue[1]
+				tag.SplitWith = assignPartKeyValue[1]
 			case assignTrim:
-				if fieldValueElemType.Elem().Kind() != reflect.String {
-					return nil, fserr.New(assignTrim + "应该添加在string字段上")
-				}
-
-				element.Trim = assignPartKeyValue[1]
-			case assignTrimLeft:
-				if fieldValueElemType.Elem().Kind() != reflect.String {
-					return nil, fserr.New(assignTrimLeft + "应该添加在string字段上")
-				}
-
-				element.TrimLeft = assignPartKeyValue[1]
-			case assignTrimRight:
-				if fieldValueElemType.Elem().Kind() != reflect.String {
-					return nil, fserr.New(assignTrimRight + "应该添加在string字段上")
-				}
-
-				element.TrimRight = assignPartKeyValue[1]
+				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 element, nil
+	return tag, nil
 }