yjp 1 жил өмнө
parent
commit
3bd5c1a111

+ 3 - 3
binding/dto.go

@@ -48,16 +48,16 @@ func ToConcreteDTO[T DTO](object DTO) T {
 func getDTOFieldValue(dto DTO, fieldName string) (*reflect.Value, error) {
 	dtoType := reflect.TypeOf(dto)
 
-	if dtoType.Kind() != reflect.Ptr {
+	if dtoType.Kind() != reflect.Pointer {
 		return nil, fserr.New("dto必须是结构指针类型")
 	}
 
-	if dtoType.Kind() == reflect.Ptr && dtoType.Elem().Kind() != reflect.Struct {
+	if dtoType.Kind() == reflect.Pointer && dtoType.Elem().Kind() != reflect.Struct {
 		return nil, fserr.New("dto必须是结构指针类型")
 	}
 
 	dtoValue := reflect.ValueOf(dto)
-	if dtoType.Kind() == reflect.Ptr && !dtoValue.IsValid() {
+	if dtoType.Kind() == reflect.Pointer && !dtoValue.IsValid() {
 		return nil, fserr.New("dto为nil")
 	}
 

+ 3 - 3
domain/object.go

@@ -64,16 +64,16 @@ func ToConcreteObject[T Object](object Object) T {
 func getObjectFieldValue(object Object, fieldName string) (*reflect.Value, error) {
 	objectType := reflect.TypeOf(object)
 
-	if objectType.Kind() != reflect.Ptr {
+	if objectType.Kind() != reflect.Pointer {
 		return nil, fserr.New("object必须是结构指针类型")
 	}
 
-	if objectType.Kind() == reflect.Ptr && objectType.Elem().Kind() != reflect.Struct {
+	if objectType.Kind() == reflect.Pointer && objectType.Elem().Kind() != reflect.Struct {
 		return nil, fserr.New("object必须是结构指针类型")
 	}
 
 	objectValue := reflect.ValueOf(object)
-	if objectType.Kind() == reflect.Ptr && !objectValue.IsValid() {
+	if objectType.Kind() == reflect.Pointer && !objectValue.IsValid() {
 		return nil, fserr.New("object为nil")
 	}
 

+ 1 - 1
examples/assign_tag/main.go

@@ -22,7 +22,7 @@ type DomainID struct {
 }
 
 type ClassDomain struct {
-	DomainID
+	*DomainID
 	Name        string
 	CreatedTime *time.Time
 }

+ 5 - 1
go.mod

@@ -3,10 +3,11 @@ module git.sxidc.com/go-framework/baize
 go 1.22.3
 
 require (
-	git.sxidc.com/go-tools/utils v1.5.8
+	git.sxidc.com/go-tools/utils v1.5.9
 	git.sxidc.com/service-supports/fserr v0.3.5
 	git.sxidc.com/service-supports/websocket v1.3.1
 	github.com/gin-gonic/gin v1.10.0
+	github.com/iancoleman/strcase v0.3.0
 	github.com/vrecan/death v3.0.1+incompatible
 	go.uber.org/zap v1.27.0
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
@@ -29,6 +30,7 @@ require (
 	github.com/gorilla/websocket v1.5.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/kr/pretty v0.3.0 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -36,6 +38,7 @@ require (
 	github.com/olahol/melody v1.2.1 // indirect
 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
 	github.com/puzpuzpuz/xsync v1.5.2 // indirect
+	github.com/rogpeppe/go-internal v1.12.0 // indirect
 	github.com/satori/go.uuid v1.2.0 // indirect
 	github.com/smartystreets/goconvey v1.8.1 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
@@ -47,4 +50,5 @@ require (
 	golang.org/x/sys v0.20.0 // indirect
 	golang.org/x/text v0.15.0 // indirect
 	google.golang.org/protobuf v1.34.1 // indirect
+	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 )

+ 20 - 3
go.sum

@@ -1,5 +1,5 @@
-git.sxidc.com/go-tools/utils v1.5.8 h1:hjKQ8kcUy2XJ2alkwmJvRE+QqsrCAf8biBIPFkVmjnc=
-git.sxidc.com/go-tools/utils v1.5.8/go.mod h1:fkobAXFpOMTvkZ82TQXWcpsayePcyk/MS5TN6GTlRDg=
+git.sxidc.com/go-tools/utils v1.5.9 h1:8xo0ChP79KoM+wlOl09vaV+pTAtVv4uy7YInKI3V5b0=
+git.sxidc.com/go-tools/utils v1.5.9/go.mod h1:fkobAXFpOMTvkZ82TQXWcpsayePcyk/MS5TN6GTlRDg=
 git.sxidc.com/service-supports/fserr v0.3.5 h1:1SDC60r3FIDd2iRq/oHRLK4OMa1gf67h9B7kierKTUE=
 git.sxidc.com/service-supports/fserr v0.3.5/go.mod h1:8U+W/ulZIGVPFojV6cE18shkGXqvaICuzaxIJpOcBqI=
 git.sxidc.com/service-supports/fslog v0.5.9 h1:q2XIK2o/fk/qmByy4x5kKLC+k7kolT5LrXHcWRSffXQ=
@@ -16,6 +16,7 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/
 github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -42,6 +43,8 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d
 github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
+github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@@ -50,6 +53,14 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
 github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -67,6 +78,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/puzpuzpuz/xsync v1.5.2 h1:yRAP4wqSOZG+/4pxJ08fPTwrfL0IzE/LKQ/cw509qGY=
 github.com/puzpuzpuz/xsync v1.5.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
@@ -112,8 +126,11 @@ golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
 golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
 google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
 gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 161 - 0
infrastructure/database/sql_tpl/condition.go

@@ -0,0 +1,161 @@
+package sql_tpl
+
+type Conditions struct {
+	Conditions []string
+	err        error
+}
+
+func NewConditions() *Conditions {
+	return &Conditions{
+		Conditions: make([]string, 0),
+	}
+}
+
+func (conditions *Conditions) AddCondition(condition string) *Conditions {
+	conditions.Conditions = append(conditions.Conditions, condition)
+	return conditions
+}
+
+func (conditions *Conditions) Equal(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" = "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) Like(columnName string, value string, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" LIKE "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) In(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseSliceValues(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" IN "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) NotIn(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseSliceValues(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" NOT IN "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) Not(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" != "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) LessThan(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" < "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) LessThanAndEqual(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" <= "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) GreaterThan(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" > "+parsedValue)
+
+	return conditions
+}
+
+func (conditions *Conditions) GreaterThanAndEqual(columnName string, value any, opts ...AfterParsedStrValueOption) *Conditions {
+	if conditions.err != nil {
+		return conditions
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		conditions.err = err
+		return conditions
+	}
+
+	conditions.Conditions = append(conditions.Conditions, columnName+" >= "+parsedValue)
+
+	return conditions
+}

+ 304 - 0
infrastructure/database/sql_tpl/sql_tpl.go

@@ -0,0 +1,304 @@
+package sql_tpl
+
+import "errors"
+
+const InsertTpl = `
+INSERT INTO
+    {{ .table_name }} ({{ .columns | join "," }})
+VALUES
+{{- $valuesClauses := list }}
+{{- range .values_list }}
+{{- $valuesClause := printf "(%s)" ( . | join "," ) }}
+{{- $valuesClauses = append $valuesClauses $valuesClause }}
+{{- end }}
+    {{ $valuesClauses | join "," }}
+`
+
+type InsertExecuteParams struct {
+	TableName string
+	*TableRow
+}
+
+func (params InsertExecuteParams) Map() (map[string]any, error) {
+	if params.TableRow == nil {
+		return nil, nil
+	}
+
+	if params.TableRow.err != nil {
+		return nil, params.TableRow.err
+	}
+
+	columns := make([]string, 0)
+	values := make([]any, 0)
+
+	for _, cv := range params.TableRow.columnValues {
+		columns = append(columns, cv.column)
+		values = append(values, cv.value)
+	}
+
+	return map[string]any{
+		"table_name":  params.TableName,
+		"columns":     columns,
+		"values_list": []any{values},
+	}, nil
+}
+
+type InsertBatchExecuteParams struct {
+	TableName     string
+	TableRowBatch []TableRow
+}
+
+func (params InsertBatchExecuteParams) Map() (map[string]any, error) {
+	if params.TableRowBatch == nil || len(params.TableRowBatch) == 0 {
+		return nil, nil
+	}
+
+	columns := make([]string, 0)
+	for _, cv := range params.TableRowBatch[0].columnValues {
+		columns = append(columns, cv.column)
+	}
+
+	valuesList := make([]any, 0)
+
+	for _, tableRow := range params.TableRowBatch {
+		if tableRow.err != nil {
+			return nil, tableRow.err
+		}
+
+		if len(columns) != len(tableRow.columnValues) {
+			return nil, errors.New("列数不匹配,保证每个TableRow的Add数量一致")
+		}
+
+		columnAndValueMap := make(map[string]any, 0)
+
+		for _, cv := range tableRow.columnValues {
+			columnAndValueMap[cv.column] = cv.value
+		}
+
+		values := make([]any, len(columnAndValueMap))
+		for _, column := range columns {
+			values = append(values, columnAndValueMap[column])
+		}
+
+		valuesList = append(valuesList, values)
+	}
+
+	return map[string]any{
+		"table_name":  params.TableName,
+		"columns":     columns,
+		"values_list": valuesList,
+	}, nil
+}
+
+const DeleteTpl = `
+DELETE FROM
+    {{ .table_name }}
+WHERE
+    {{ range .conditions }} {{ . }} AND {{ end }} 1 = 1
+`
+
+type DeleteExecuteParams struct {
+	TableName string
+	*Conditions
+}
+
+func (params DeleteExecuteParams) Map() (map[string]any, error) {
+	if params.Conditions == nil {
+		return nil, errors.New("没有传递删除条件")
+	}
+
+	if params.Conditions.err != nil {
+		return nil, params.Conditions.err
+	}
+
+	return map[string]any{
+		"table_name": params.TableName,
+		"conditions": params.Conditions.Conditions,
+	}, nil
+}
+
+const UpdateTpl = `
+UPDATE
+    {{ .table_name }}
+SET
+    {{ .set_list | join "," }}
+WHERE
+    {{ range .conditions }} {{ . }} AND {{ end }} 1 = 1
+`
+
+type UpdateExecuteParams struct {
+	TableName string
+	*TableRow
+	*Conditions
+}
+
+func (params UpdateExecuteParams) Map() (map[string]any, error) {
+	if params.TableRow == nil {
+		return nil, nil
+	}
+
+	if params.TableRow.err != nil {
+		return nil, params.TableRow.err
+	}
+
+	setList := make([]string, 0)
+	for _, cv := range params.TableRow.columnValues {
+		setList = append(setList, cv.column+" = "+cv.value)
+	}
+
+	conditions := make([]string, 0)
+	if params.Conditions != nil {
+		if params.Conditions.err != nil {
+			return nil, params.Conditions.err
+		}
+
+		conditions = params.Conditions.Conditions
+	}
+
+	return map[string]any{
+		"table_name": params.TableName,
+		"set_list":   setList,
+		"conditions": conditions,
+	}, nil
+}
+
+const QueryTpl = `
+SELECT
+    {{ if .select_columns }}{{ .select_columns | join "," }}{{ else }}*{{ end }} 
+FROM
+    {{ .table_name }}
+WHERE
+    {{ range .conditions }} {{ . }} AND {{ end }} 1 = 1 
+{{ if .limit }}LIMIT {{ .limit }}{{ end }}
+{{ if .offset }}OFFSET {{ .offset }}{{ end }}
+`
+
+type QueryExecuteParams struct {
+	TableName     string
+	SelectColumns []string
+	*Conditions
+	PageNo   int
+	PageSize int
+}
+
+func (params QueryExecuteParams) Map() (map[string]any, error) {
+	var limit int
+	var offset int
+
+	if params.PageNo != 0 && params.PageSize != 0 {
+		limit = params.PageSize
+		offset = (params.PageNo - 1) * params.PageSize
+	}
+
+	conditions := make([]string, 0)
+	if params.Conditions != nil {
+		if params.Conditions.err != nil {
+			return nil, params.Conditions.err
+		}
+
+		conditions = params.Conditions.Conditions
+	}
+
+	return map[string]any{
+		"table_name":     params.TableName,
+		"select_columns": params.SelectColumns,
+		"conditions":     conditions,
+		"limit":          limit,
+		"offset":         offset,
+	}, nil
+}
+
+type QueryOneExecuteParams struct {
+	TableName     string
+	SelectColumns []string
+	*Conditions
+}
+
+func (params QueryOneExecuteParams) Map() (map[string]any, error) {
+	conditions := make([]string, 0)
+	if params.Conditions != nil {
+		if params.Conditions.err != nil {
+			return nil, params.Conditions.err
+		}
+
+		conditions = params.Conditions.Conditions
+	}
+
+	return map[string]any{
+		"table_name":     params.TableName,
+		"select_columns": params.SelectColumns,
+		"conditions":     conditions,
+	}, nil
+}
+
+const CountTpl = `
+SELECT
+    COUNT(*)
+FROM
+    {{ .table_name }}
+WHERE
+    {{ range .conditions }} {{ . }} AND {{ end }} 1 = 1
+`
+
+type CountExecuteParams struct {
+	TableName string
+	*Conditions
+}
+
+func (params CountExecuteParams) Map() (map[string]any, error) {
+	conditions := make([]string, 0)
+	if params.Conditions != nil {
+		if params.Conditions.err != nil {
+			return nil, params.Conditions.err
+		}
+
+		conditions = params.Conditions.Conditions
+	}
+
+	return map[string]any{
+		"table_name": params.TableName,
+		"conditions": conditions,
+	}, nil
+}
+
+type CheckExistExecuteParams struct {
+	TableName string
+	*Conditions
+}
+
+func (params CheckExistExecuteParams) Map() (map[string]any, error) {
+	conditions := make([]string, 0)
+	if params.Conditions != nil {
+		if params.Conditions.err != nil {
+			return nil, params.Conditions.err
+		}
+
+		conditions = params.Conditions.Conditions
+	}
+
+	return map[string]any{
+		"table_name": params.TableName,
+		"conditions": conditions,
+	}, nil
+}
+
+type CheckHasOnlyOneExecuteParams struct {
+	TableName string
+	*Conditions
+}
+
+func (params CheckHasOnlyOneExecuteParams) Map() (map[string]any, error) {
+	conditions := make([]string, 0)
+	if params.Conditions != nil {
+		if params.Conditions.err != nil {
+			return nil, params.Conditions.err
+		}
+
+		conditions = params.Conditions.Conditions
+	}
+
+	return map[string]any{
+		"table_name": params.TableName,
+		"conditions": conditions,
+	}, nil
+}

+ 36 - 0
infrastructure/database/sql_tpl/table_row.go

@@ -0,0 +1,36 @@
+package sql_tpl
+
+type TableRow struct {
+	columnValues []columnValue
+	err          error
+}
+
+type columnValue struct {
+	column string
+	value  string
+}
+
+func NewTableRow() *TableRow {
+	return &TableRow{
+		columnValues: make([]columnValue, 0),
+	}
+}
+
+func (tableRow *TableRow) Add(column string, value any, opts ...AfterParsedStrValueOption) *TableRow {
+	if tableRow.err != nil {
+		return tableRow
+	}
+
+	parsedValue, err := parseValue(value, opts...)
+	if err != nil {
+		tableRow.err = err
+		return tableRow
+	}
+
+	tableRow.columnValues = append(tableRow.columnValues, columnValue{
+		column: column,
+		value:  parsedValue,
+	})
+
+	return tableRow
+}

+ 190 - 0
infrastructure/database/sql_tpl/value.go

@@ -0,0 +1,190 @@
+package sql_tpl
+
+import (
+	"errors"
+	"git.sxidc.com/go-tools/utils/encoding"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const (
+	timeWriteFormat = time.DateTime + ".000000 +08:00"
+)
+
+type AfterParsedStrValueOption func(strValue string) (string, error)
+
+func WithAESKey(aesKey string) AfterParsedStrValueOption {
+	return func(strValue string) (string, error) {
+		if strutils.IsStringEmpty(strValue) {
+			return "''", nil
+		}
+
+		encrypted, err := encoding.AESEncrypt(strValue, aesKey)
+		if err != nil {
+			return "", err
+		}
+
+		return "'" + encrypted + "'", nil
+	}
+}
+
+func parseValue(value any, opts ...AfterParsedStrValueOption) (string, error) {
+	valueValue := reflect.ValueOf(value)
+
+	if !valueValue.IsValid() {
+		return "", errors.New("无效值")
+	}
+
+	if valueValue.Kind() == reflect.Pointer && valueValue.IsNil() {
+		return "", errors.New("空值")
+	}
+
+	if valueValue.Kind() == reflect.Pointer {
+		valueValue = valueValue.Elem()
+	}
+
+	var parsedValue string
+
+	switch v := valueValue.Interface().(type) {
+	case string:
+		parsedValue = v
+
+		if opts == nil || len(opts) == 0 {
+			return "'" + parsedValue + "'", nil
+		}
+	case bool:
+		parsedValue = strconv.FormatBool(v)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case time.Time:
+		parsedValue = v.Format(timeWriteFormat)
+
+		if opts == nil || len(opts) == 0 {
+			return "'" + parsedValue + "'", nil
+		}
+	case int:
+		parsedValue = strconv.Itoa(v)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case int8:
+		parsedValue = strconv.FormatInt(int64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case int16:
+		parsedValue = strconv.FormatInt(int64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case int32:
+		parsedValue = strconv.FormatInt(int64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case int64:
+		parsedValue = strconv.FormatInt(v, 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case uint:
+		parsedValue = strconv.FormatUint(uint64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case uint8:
+		parsedValue = strconv.FormatUint(uint64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case uint16:
+		parsedValue = strconv.FormatUint(uint64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case uint32:
+		parsedValue = strconv.FormatUint(uint64(v), 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	case uint64:
+		parsedValue = strconv.FormatUint(v, 10)
+
+		if opts == nil || len(opts) == 0 {
+			return parsedValue, nil
+		}
+	default:
+		return "", errors.New("不支持的类型")
+	}
+
+	for _, opt := range opts {
+		innerParsedValue, err := opt(parsedValue)
+		if err != nil {
+			return "", err
+		}
+
+		parsedValue = innerParsedValue
+	}
+
+	return parsedValue, nil
+}
+
+func parseSliceValues(values any, opts ...AfterParsedStrValueOption) (string, error) {
+	sliceValue := reflect.ValueOf(values)
+
+	if !sliceValue.IsValid() {
+		return "", errors.New("无效值")
+	}
+
+	if sliceValue.Kind() == reflect.Pointer && sliceValue.IsNil() {
+		return "()", nil
+	}
+
+	if sliceValue.Kind() == reflect.Pointer {
+		sliceValue = sliceValue.Elem()
+	}
+
+	if sliceValue.Kind() != reflect.Slice {
+		return "", errors.New("传递的不是slice")
+	}
+
+	parsedValues := make([]string, 0)
+	for i := 0; i < sliceValue.Len(); i++ {
+		value := sliceValue.Index(i).Interface()
+		parsedValue, err := parseValue(value)
+		if err != nil {
+			return "", err
+		}
+
+		for _, opt := range opts {
+			innerParsedValue, err := opt(parsedValue)
+			if err != nil {
+				return "", err
+			}
+
+			parsedValue = innerParsedValue
+		}
+
+		parsedValues = append(parsedValues, parsedValue)
+	}
+
+	if len(parsedValues) == 0 {
+		return "()", nil
+	}
+
+	return "(" + strings.Join(parsedValues, ",") + ")", nil
+}

+ 23 - 15
tag/assign_struct.go

@@ -30,6 +30,18 @@ func AssignTo(from any, to any) error {
 	fromElemValue := reflectutils.PointerValueElem(fromValue)
 	retElemValue := reflectutils.PointerValueElem(retValue)
 
+	for i := 0; i < retElemValue.NumField(); i++ {
+		retField := retElemValue.Field(i)
+		if !retField.IsValid() {
+			return fserr.New("被赋值的结构存在无效字段")
+		}
+
+		// 初始化空值指针
+		if retField.Kind() == reflect.Ptr && retField.IsNil() {
+			retField.Set(reflect.New(retField.Type().Elem()))
+		}
+	}
+
 	err := assignTo(fromElemValue, &retElemValue)
 	if err != nil {
 		return err
@@ -48,17 +60,14 @@ func assignTo(fromElemValue reflect.Value, retElemValue *reflect.Value) error {
 			continue
 		}
 
-		tagStr := fromField.Tag.Get(assignTagKey)
+		fromFieldElemValue := reflectutils.PointerValueElem(fromFieldValue)
 
-		// 结构上没有添加Tag, 先尝试直接按照字段赋值结构,如果失败,进一步进入内部尝试
-		if strutils.IsStringEmpty(tagStr) &&
-			!reflectutils.IsValueTime(fromFieldValue) &&
-			!reflectutils.IsValueTimePointer(fromFieldValue) {
-			if !fromFieldValue.IsValid() || fromFieldValue.IsZero() {
-				continue
-			}
+		tagStr := fromField.Tag.Get(assignTagKey)
 
-			err := assignTo(reflectutils.PointerValueElem(fromFieldValue), retElemValue)
+		// 结构类型的字段上没有添加Tag, 先尝试直接按照字段赋值
+		if strutils.IsStringEmpty(tagStr) && fromFieldElemValue.Kind() == reflect.Struct &&
+			!reflectutils.IsValueTime(fromFieldElemValue) {
+			err := assignTo(fromFieldElemValue, retElemValue)
 			if err != nil {
 				return err
 			}
@@ -81,7 +90,7 @@ func assignTo(fromElemValue reflect.Value, retElemValue *reflect.Value) error {
 		}
 
 		retFieldElemValue := retFieldValue
-		if retFieldValue.Kind() == reflect.Ptr {
+		if retFieldValue.Kind() == reflect.Pointer {
 			if !retFieldValue.IsValid() {
 				continue
 			}
@@ -98,7 +107,6 @@ func assignTo(fromElemValue reflect.Value, retElemValue *reflect.Value) error {
 			retFieldElemValue = retFieldValue.Elem()
 		}
 
-		fromFieldElemValue := reflectutils.PointerValueElem(fromFieldValue)
 		err = assignField(fromFieldElemValue, retFieldElemValue, tag)
 		if err != nil {
 			return err
@@ -118,7 +126,7 @@ func assignField(fromFieldElemValue reflect.Value, retFieldElemValue reflect.Val
 		// time.Time类型的结构,接收字段是string类型,使用FormatTime的格式转换
 		if reflectutils.IsValueTime(fromFieldElemValue) && retKind == reflect.String {
 			fromString := fromFieldElemValue.Interface().(time.Time).Format(tag.FormatTime)
-			fromAny = trimFromString(fromString, tag)
+			fromAny = assignTrimFromString(fromString, tag)
 			break
 		}
 
@@ -132,7 +140,7 @@ func assignField(fromFieldElemValue reflect.Value, retFieldElemValue reflect.Val
 	case reflect.Slice:
 		if reflectutils.IsSliceValueOf(fromFieldElemValue, reflect.String) && retKind == reflect.String {
 			fromString := strings.Join(fromFieldElemValue.Interface().([]string), tag.JoinWith)
-			fromAny = trimFromString(fromString, tag)
+			fromAny = assignTrimFromString(fromString, tag)
 			break
 		}
 
@@ -155,7 +163,7 @@ func assignField(fromFieldElemValue reflect.Value, retFieldElemValue reflect.Val
 			break
 		}
 
-		fromAny = trimFromString(fromString, tag)
+		fromAny = assignTrimFromString(fromString, tag)
 	default:
 		fromAny = fromFieldElemValue.Interface()
 	}
@@ -177,7 +185,7 @@ func assignField(fromFieldElemValue reflect.Value, retFieldElemValue reflect.Val
 	}
 }
 
-func trimFromString(fromString string, tag *assignTag) string {
+func assignTrimFromString(fromString string, tag *assignTag) string {
 	if strutils.IsStringNotEmpty(tag.Trim) {
 		return strings.Trim(fromString, tag.Trim)
 	} else {

+ 188 - 0
tag/sql_mapping.go

@@ -0,0 +1,188 @@
+package tag
+
+import (
+	"errors"
+	"git.sxidc.com/go-tools/utils/reflectutils"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"git.sxidc.com/service-supports/fserr"
+	"github.com/iancoleman/strcase"
+	"reflect"
+	"strings"
+)
+
+type OnParsedFieldTagFunc func(entityFieldElemValue reflect.Value, tag *SqlMappingTag) error
+
+func BuildExecuteParams(e any, onParsedFieldTagFunc OnParsedFieldTagFunc) error {
+	if e == nil {
+		return nil
+	}
+
+	entityValue := reflect.ValueOf(e)
+
+	// 类型校验
+	if !reflectutils.IsValueStructOrStructPointer(entityValue) {
+		return fserr.New("参数不是结构或结构指针")
+	}
+
+	err := parseEntitySqlMappingTag(reflectutils.PointerValueElem(entityValue), onParsedFieldTagFunc)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func parseEntitySqlMappingTag(entityElemValue reflect.Value, onParsedFieldTagFunc OnParsedFieldTagFunc) error {
+	for i := 0; i < entityElemValue.NumField(); i++ {
+		entityField := entityElemValue.Type().Field(i)
+		entityFieldValue := entityElemValue.Field(i)
+
+		// 无效零值,不进行映射
+		if !entityFieldValue.IsValid() || entityFieldValue.IsZero() {
+			continue
+		}
+
+		entityFieldElemValue := reflectutils.PointerValueElem(entityFieldValue)
+
+		tagStr := entityField.Tag.Get(sqlMappingTagKey)
+
+		tag, err := parseSqlMappingTag(entityField, tagStr)
+		if err != nil {
+			return err
+		}
+
+		if tag == nil {
+			continue
+		}
+
+		// 结构类型的字段,解析结构内部
+		if entityFieldElemValue.Kind() == reflect.Struct &&
+			!reflectutils.IsValueTime(entityFieldElemValue) {
+			err := parseEntitySqlMappingTag(entityFieldElemValue, onParsedFieldTagFunc)
+			if err != nil {
+				return err
+			}
+		}
+
+		err = onParsedFieldTagFunc(entityFieldElemValue, tag)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+const (
+	sqlMappingDefaultKeyColumnName     = "id"
+	sqlMappingDefaultJoinWith          = "::"
+	sqlMappingTagPartSeparator         = ";"
+	sqlMappingTagPartKeyValueSeparator = ":"
+)
+
+const (
+	sqlMappingTagKey      = "sqlmapping"
+	sqlMappingIgnore      = "-"
+	sqlMappingColumn      = "column"
+	sqlMappingKey         = "key"
+	sqlMappingNotUpdate   = "notUpdate"
+	sqlMappingUpdateClear = "updateClear"
+	sqlMappingAes         = "aes"
+	sqlMappingJoinWith    = "joinWith"
+	sqlMappingTrim        = "trim"
+	sqlMappingTrimPrefix  = "trimPrefix"
+	sqlMappingTrimSuffix  = "trimSuffix"
+)
+
+type SqlMappingTag struct {
+	Name           string
+	IsKey          bool
+	CanUpdate      bool
+	CanUpdateClear bool
+	AESKey         string
+	JoinWith       string
+	Trim           string
+	TrimPrefix     string
+	TrimSuffix     string
+}
+
+func parseSqlMappingTag(field reflect.StructField, tagStr string) (*SqlMappingTag, error) {
+	if tagStr == sqlMappingIgnore {
+		return nil, nil
+	}
+
+	sqlColumn := &SqlMappingTag{
+		Name:           strcase.ToSnake(field.Name),
+		IsKey:          false,
+		CanUpdate:      true,
+		CanUpdateClear: false,
+		AESKey:         "",
+		JoinWith:       sqlMappingDefaultJoinWith,
+		Trim:           "",
+		TrimPrefix:     "",
+		TrimSuffix:     "",
+	}
+
+	if sqlColumn.Name == sqlMappingDefaultKeyColumnName {
+		sqlColumn.IsKey = true
+		sqlColumn.CanUpdate = false
+	}
+
+	if strutils.IsStringEmpty(tagStr) {
+		return sqlColumn, nil
+	}
+
+	sqlMappingParts := strings.Split(tagStr, sqlMappingTagPartSeparator)
+	if sqlMappingParts != nil || len(sqlMappingParts) != 0 {
+		for _, sqlMappingPart := range sqlMappingParts {
+			sqlMappingPartKeyValue := strings.SplitN(strings.TrimSpace(sqlMappingPart), sqlMappingTagPartKeyValueSeparator, 2)
+			if sqlMappingPartKeyValue != nil && len(sqlMappingPartKeyValue) == 2 && strutils.IsStringNotEmpty(sqlMappingPartKeyValue[1]) {
+				// 可以支持' ' ',获取到'字符
+				sqlMappingPartKeyValue[1] = strings.TrimSpace(strings.Trim(sqlMappingPartKeyValue[1], "'"))
+			}
+
+			switch sqlMappingPartKeyValue[0] {
+			case sqlMappingColumn:
+				if strutils.IsStringEmpty(sqlMappingPartKeyValue[1]) {
+					return nil, errors.New("column没有赋值列名")
+				}
+
+				sqlColumn.Name = sqlMappingPartKeyValue[1]
+			case sqlMappingKey:
+				sqlColumn.IsKey = true
+				sqlColumn.CanUpdate = false
+			case sqlMappingNotUpdate:
+				sqlColumn.CanUpdate = false
+				sqlColumn.CanUpdateClear = false
+			case sqlMappingUpdateClear:
+				if !sqlColumn.CanUpdate {
+					sqlColumn.CanUpdateClear = false
+				} else {
+					sqlColumn.CanUpdateClear = true
+				}
+			case sqlMappingAes:
+				if len(sqlMappingPartKeyValue[1]) != 32 {
+					return nil, errors.New("AES密钥长度应该为32个字节")
+				}
+
+				sqlColumn.AESKey = sqlMappingPartKeyValue[1]
+			case sqlMappingJoinWith:
+				if strutils.IsStringEmpty(sqlMappingPartKeyValue[1]) {
+					return nil, errors.New(sqlMappingJoinWith + "没有赋值分隔符")
+				}
+
+				sqlColumn.JoinWith = sqlMappingPartKeyValue[1]
+			case sqlMappingTrim:
+				sqlColumn.Trim = sqlMappingPartKeyValue[1]
+			case sqlMappingTrimPrefix:
+				sqlColumn.TrimPrefix = sqlMappingPartKeyValue[1]
+			case sqlMappingTrimSuffix:
+				sqlColumn.TrimSuffix = sqlMappingPartKeyValue[1]
+			default:
+				continue
+			}
+		}
+	}
+
+	return sqlColumn, nil
+}