完成基础版本基于运行时的动态结构体生成 + 参数验证
This commit is contained in:
parent
6b5bfaf666
commit
db7779e488
55
format.go
Normal file
55
format.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Package validate ...
|
||||
//
|
||||
// Description : validate ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-03-19 15:50
|
||||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetValidateErr 格式化验证结果错误信息
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:51 2025/3/19
|
||||
func GetValidateErr(obj any, rawErr error) error {
|
||||
if nil == rawErr {
|
||||
return nil
|
||||
}
|
||||
if nil == obj {
|
||||
return rawErr
|
||||
}
|
||||
var (
|
||||
ok bool
|
||||
validationErrs validator.ValidationErrors
|
||||
errString []string
|
||||
field reflect.StructField
|
||||
)
|
||||
if ok = errors.As(rawErr, &validationErrs); !ok {
|
||||
return rawErr
|
||||
}
|
||||
objType := reflect.TypeOf(obj)
|
||||
if objType.Kind() == reflect.Ptr {
|
||||
objType = objType.Elem()
|
||||
}
|
||||
for _, validationErr := range validationErrs {
|
||||
if field, ok = objType.FieldByName(validationErr.Field()); ok {
|
||||
if e := field.Tag.Get(TagErrMsg); e != "" {
|
||||
errString = append(errString, fmt.Sprintf("%s: %s", field.Tag.Get("json"), e))
|
||||
continue
|
||||
} else {
|
||||
errString = append(errString, fmt.Sprintf("%s: %v", field.Tag.Get("json"), validationErr.Value()))
|
||||
}
|
||||
}
|
||||
errString = append(errString, validationErr.Error())
|
||||
}
|
||||
return errors.New(strings.Join(errString, "\n"))
|
||||
}
|
8
go.mod
8
go.mod
@ -4,14 +4,17 @@ go 1.24.1
|
||||
|
||||
require (
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8
|
||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20250319072714-eab2a7abde63
|
||||
git.zhangdeman.cn/zhangdeman/json_filter v0.0.0-20241205105007-b8c8c9d4338c
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250302133417-c1588abcb436
|
||||
github.com/go-playground/validator/v10 v10.25.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
)
|
||||
|
||||
require (
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd // indirect
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
|
||||
@ -19,7 +22,6 @@ require (
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.25.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mozillazg/go-pinyin v0.20.0 // indirect
|
||||
@ -33,4 +35,4 @@ require (
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace git.zhangdeman.cn/zhangdeman/wrapper => ../wrapper
|
||||
replace git.zhangdeman.cn/zhangdeman/dynamic-struct => ../dynamic-struct
|
||||
|
7
go.sum
7
go.sum
@ -8,6 +8,8 @@ git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd h1:q7G
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd/go.mod h1:+D6uPSljwHywjVY5WSBY4TRVMj26TN5f5cFGEYMldjs=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250302133417-c1588abcb436 h1:SM4zc54W2wmM72+4pMNQ8iS371H6lj4J8rj8KJKf7pw=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250302133417-c1588abcb436/go.mod h1:YJ1FlvFgkfAHkxkt3l5rKKUqEpQkNMbCFDzDmgteEU8=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
|
||||
@ -18,6 +20,8 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
@ -38,6 +42,7 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@ -45,6 +50,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
|
61
validate.go
61
validate.go
@ -12,15 +12,22 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
dynamicStructGenerate "git.zhangdeman.cn/zhangdeman/dynamic-struct"
|
||||
"git.zhangdeman.cn/zhangdeman/json_filter/gjson_hack"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var validatorInstance = validator.New()
|
||||
var validatorInstance *validator.Validate
|
||||
|
||||
func init() {
|
||||
validatorInstance = validator.New()
|
||||
validatorInstance.SetTagName(TagValidate)
|
||||
}
|
||||
|
||||
// Run 执行参数验证
|
||||
//
|
||||
@ -31,7 +38,7 @@ func Run(sourceData []byte, fieldList []StructField) ([]byte, error) {
|
||||
handleInstance := &handle{
|
||||
sourceData: sourceData,
|
||||
fieldList: fieldList,
|
||||
dynamicStruct: wrapper.NewDynamic(),
|
||||
dynamicStruct: dynamicStructGenerate.NewStruct(),
|
||||
}
|
||||
return handleInstance.Run()
|
||||
}
|
||||
@ -39,7 +46,8 @@ func Run(sourceData []byte, fieldList []StructField) ([]byte, error) {
|
||||
type handle struct {
|
||||
sourceData []byte
|
||||
fieldList []StructField
|
||||
dynamicStruct *wrapper.DynamicStruct
|
||||
dynamicStruct dynamicStructGenerate.Builder
|
||||
formatVal string
|
||||
}
|
||||
|
||||
// Run 执行验证
|
||||
@ -67,16 +75,20 @@ func (h *handle) Run() ([]byte, error) {
|
||||
// 没出现异常, 但是value为nil, 视作参数不存在处理
|
||||
continue
|
||||
}
|
||||
// fieldName := wrapper.String(field.JsonTag).SnakeCaseToCamel()
|
||||
h.dynamicStruct.AddAny(field.JsonTag, h.generateTag(field), "dynamic_struct", sourceValue)
|
||||
fieldName := wrapper.String(field.JsonTag).SnakeCaseToCamel()
|
||||
|
||||
fieldTag := h.generateTag(field)
|
||||
h.dynamicStruct.AddField(fieldName, "", sourceValue, fieldTag, false)
|
||||
}
|
||||
val := h.dynamicStruct.ToStructDefaultValue()
|
||||
if err := serialize.JSON.Transition(h.dynamicStruct.MapData(), &val); nil != err {
|
||||
val := h.dynamicStruct.Build().New()
|
||||
if err := serialize.JSON.UnmarshalWithNumber([]byte(h.formatVal), &val); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
if err := validatorInstance.Struct(val); nil != err {
|
||||
return nil, GetValidateErr(val, err)
|
||||
}
|
||||
targetByte, _ := json.Marshal(val)
|
||||
err := validatorInstance.Struct(val)
|
||||
return targetByte, err
|
||||
return targetByte, nil
|
||||
}
|
||||
|
||||
// checkRequired 格式化必传参数
|
||||
@ -123,46 +135,57 @@ func (h *handle) getSourceDataValue(field StructField) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
var (
|
||||
val any
|
||||
err error
|
||||
)
|
||||
defer func() {
|
||||
if nil == err && nil != val {
|
||||
// 更新到格式化之后的数据结构中
|
||||
h.formatVal, err = sjson.Set(h.formatVal, field.TargetPath, val)
|
||||
}
|
||||
}()
|
||||
switch field.Type {
|
||||
case consts.DataTypeInt: // Int类型
|
||||
fallthrough
|
||||
case consts.DataTypeIntPtr: // Uint类型
|
||||
val, err := gjson_hack.Int(sourceValue)
|
||||
val, err = gjson_hack.Int(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeUint:
|
||||
fallthrough
|
||||
case consts.DataTypeUintPtr: // Uint类型
|
||||
val, err := gjson_hack.Uint(sourceValue)
|
||||
val, err = gjson_hack.Uint(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeFloat:
|
||||
fallthrough
|
||||
case consts.DataTypeFloatPtr: // Float类型
|
||||
val, err := gjson_hack.Float64(sourceValue)
|
||||
val, err = gjson_hack.Float64(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeString: // String类型
|
||||
return sourceValue.String(), nil
|
||||
case consts.DataTypeBool: // Bool类型
|
||||
return sourceValue.Bool(), nil
|
||||
case consts.DataTypeSliceFloat: // Float slice
|
||||
val, err := gjson_hack.SliceFloat(sourceValue)
|
||||
val, err = gjson_hack.SliceFloat(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeSliceInt: // Int slice
|
||||
val, err := gjson_hack.SliceInt(sourceValue)
|
||||
val, err = gjson_hack.SliceInt(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeSliceUint: // Uint slice
|
||||
val, err := gjson_hack.SliceUint(sourceValue)
|
||||
val, err = gjson_hack.SliceUint(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeSliceString: // String slice
|
||||
val, err := gjson_hack.SliceString(sourceValue)
|
||||
val, err = gjson_hack.SliceString(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeSliceBool: // Bool slice
|
||||
val, err := gjson_hack.SliceBool(sourceValue)
|
||||
val, err = gjson_hack.SliceBool(sourceValue)
|
||||
return val, err
|
||||
case consts.DataTypeMapStrAny: // Bool slice
|
||||
val, err := gjson_hack.MapStrAny[any](sourceValue)
|
||||
val, err = gjson_hack.MapStrAny[any](sourceValue)
|
||||
return val, err
|
||||
}
|
||||
return sourceValue.Value(), nil
|
||||
val = sourceValue.Value()
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// 生成结构体的tag标签
|
||||
|
@ -17,14 +17,14 @@ import (
|
||||
// TestRun_Simple_Data 无嵌套、无复杂数据类型的处理
|
||||
func TestRun_Simple_Data(t *testing.T) {
|
||||
testMap := map[string]any{
|
||||
"age": 180,
|
||||
"age": 18,
|
||||
"height": 179.5,
|
||||
"name": "baicha",
|
||||
}
|
||||
sourceByteData, _ := json.Marshal(testMap)
|
||||
fieldList := []StructField{
|
||||
{
|
||||
JsonTag: "age",
|
||||
JsonTag: "user_age",
|
||||
Type: consts.DataTypeInt,
|
||||
Required: false,
|
||||
RuleList: []Rule{
|
||||
|
Loading…
x
Reference in New Issue
Block a user