From 6f5aad960735563ae055e5df0c567915546e3b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Mon, 29 Apr 2024 17:29:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=83=A8=E5=88=86=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=AA=8C=E8=AF=81=E8=BD=AC=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/rule.go | 52 ++----- go.mod | 19 ++- go.sum | 38 +++++ rule_instance.go | 84 ++---------- run.go | 350 ++++++++++++++++++++++++++++++++++++++++++++++- run_test.go | 23 ++++ 6 files changed, 444 insertions(+), 122 deletions(-) create mode 100644 run_test.go diff --git a/define/rule.go b/define/rule.go index c7bd260..d7d10af 100644 --- a/define/rule.go +++ b/define/rule.go @@ -15,6 +15,7 @@ package define type FieldRule struct { Path string `json:"path"` // 字段路径 Type string `json:"type"` // 数据类型, 具体枚举值参见 git.zhangdeman.cn/zhangdeman/consts + DisableRewrite bool `json:"disable_rewrite"` // 验证完相关数据类型之后, 不要重新给字段赋值 DefaultValue string `json:"default_value"` // 默认值, 统一以字符串传入, 会转为最终设置的类型 IsRequired bool `json:"is_required"` // 是否必传 AllowEmpty bool `json:"allow_empty"` // 必传时空字符串是否合法 @@ -30,8 +31,8 @@ type FieldRule struct { // // Date : 10:58 2024/4/29 type RequiredCondition struct { - DependOnField string `json:"depend_on_field"` // 依赖数据园中的那一个字段 - DependOnFieldStatus string `json:"depend_on_field_status"` // 依赖数据状态 : NOT_FOUND / IS_NIL / IS_ZERO / IS_EMPTY / IS_FALSE + DependOnField string `json:"depend_on_field"` // 依赖数据园中的那一个字段 + DependOnFieldStatus []string `json:"depend_on_field_status"` // 依赖数据状态 : NOT_FOUND / IS_NIL / IS_ZERO / IS_EMPTY / IS_FALSE } // ValueLimit 取值的限制 @@ -40,11 +41,11 @@ type RequiredCondition struct { // // Date : 11:15 2024/4/29 type ValueLimit struct { - String *StringValueLimit `json:"string"` // 字符串值的限制 - Int *IntValueLimit `json:"int"` // int数据验证 - Float *FloatValueLimit `json:"float"` // 浮点数据 - Slice *SliceValueLimit `json:"slice"` // 数组 - Map *MapValueLimit `json:"map"` // map验证 + EnumList []string `json:"enum_list"` // 枚举值列表, 仅对 int / float / string 生效 + Min *float64 `json:"min"` // 最小值(包含), 对于 int/float, 此值为最小取值, 对于int/map/slice辞职问最小长度 + Max *float64 `json:"max"` // 最大值(不包含), 对于 int/float, 此值为最大取值, 对于int/map/slice辞职问最大长度 + String *StringValueLimit `json:"string"` // 字符串值的限制 + Map *MapValueLimit `json:"map"` // map验证 } // StringValueLimit 字符串类型值的限制 @@ -53,43 +54,8 @@ type ValueLimit struct { // // Date : 11:16 2024/4/29 type StringValueLimit struct { - MinLength int64 `json:"min_length"` // 最小长度(包含) - MaxLength int64 `json:"max_length"` // 最大长度(不包含) AutoTrimSpace bool `json:"auto_trim_space"` // 自动去除前后空格 IncludeSubStrList []string `json:"include_sub_str_list"` // 必须包含指定的子串 - EnumList []string `json:"enum_list"` // 枚举值列表 -} - -// IntValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 11:22 2024/4/29 -type IntValueLimit struct { - Min *int64 `json:"min"` // 最小值(包含) - Max *int64 `json:"max"` // 最大值(不包含) - EnumList []int64 `json:"enum_list"` // 枚举值列表 -} - -// FloatValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 11:22 2024/4/29 -type FloatValueLimit struct { - Min *float64 `json:"min"` // 最小值(包含) - Max *float64 `json:"max"` // 最大值(不包含) - EnumList []float64 `json:"enum_list"` // 枚举值列表 -} - -// SliceValueLimit 数组限制 -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 11:23 2024/4/29 -type SliceValueLimit struct { - MinLength int64 `json:"min_length"` // 最小长度(包含) - MaxLength int64 `json:"max_length"` // 最大长度(不包含) } // MapValueLimit map数据的限制 @@ -98,7 +64,5 @@ type SliceValueLimit struct { // // Date : 11:23 2024/4/29 type MapValueLimit struct { - MinLength int64 `json:"min_length"` // 最小长度(包含) - MaxLength int64 `json:"max_length"` // 最大长度(不包含) IncludeFieldList []string `json:"include_field_list"` // 必须存在的字段列表 } diff --git a/go.mod b/go.mod index a6aa713..69f2923 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,21 @@ module git.zhangdeman.cn/gateway/validator go 1.22.2 -require git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429055735-7d7191726f0c // indirect +require ( + git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429082641-eeef7e967d00 // indirect + git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 // indirect + git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240325080031-1f58204e8687 // indirect + git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253 // indirect + git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240422034417-8c922be06d95 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mozillazg/go-pinyin v0.20.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index b860184..55fd107 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,40 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429055735-7d7191726f0c h1:n+n49onVpIgemvRdX4XnUB5psWh/NZ/qYkapCbJ4AYA= git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429055735-7d7191726f0c/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429080251-9cd57471a8f5 h1:6JsrADIXKWb7ZzaqAFlNpfL5ynu8m1X8Tju8h63mN0Y= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429080251-9cd57471a8f5/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429082641-eeef7e967d00 h1:bOPZXYX9CfnZBPqkQnzgwnuC1uyXBivSY0nmV4HabXg= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240429082641-eeef7e967d00/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= +git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0= +git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U= +git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240325080031-1f58204e8687 h1:uQcGqdzi4UdpZlp4f4FUPeBqoygP58pEKJkmN3ROsE0= +git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240325080031-1f58204e8687/go.mod h1:gf7SW2TXATgux8pfdFedMkXWv2515OtIIM/5c4atkFw= +git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253 h1:GO3oZa5a2sqwAzGcLDJtQzmshSWRmoP7IDS8bwFqvC4= +git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240422034417-8c922be06d95 h1:3lO4ap9p7kEA+4yL5ojG9mAWsz5sY28Nu2tSzAZEehw= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240422034417-8c922be06d95/go.mod h1:Fo4XOiZPua4E4/Qzy3ZYS5zyd15bS/lsb3t6S6PQFGY= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= +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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ= +github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +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= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/rule_instance.go b/rule_instance.go index db9d357..fef3498 100644 --- a/rule_instance.go +++ b/rule_instance.go @@ -25,83 +25,15 @@ func NewDefaultFieldRule(path string, dataType string, isRequired bool, defaultV AllowZero: true, RequiredConditionGroup: make([][]define.RequiredCondition, 0), ValueLimit: &define.ValueLimit{ - String: nil, - Int: nil, - Float: nil, - Slice: nil, - Map: nil, + EnumList: make([]string, 0), + Min: nil, + Max: nil, + String: &define.StringValueLimit{ + AutoTrimSpace: true, + IncludeSubStrList: make([]string, 0), + }, + Map: &define.MapValueLimit{IncludeFieldList: make([]string, 0)}, }, } return r } - -// NewDefaultStringValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 12:22 2024/4/29 -func NewDefaultStringValueLimit(minLength int64, maxLength int64) *define.StringValueLimit { - l := &define.StringValueLimit{ - MinLength: minLength, - MaxLength: maxLength, - AutoTrimSpace: true, - IncludeSubStrList: make([]string, 0), - EnumList: make([]string, 0), - } - return l -} - -// NewDefaultIntValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 12:24 2024/4/29 -func NewDefaultIntValueLimit(min *int64, max *int64) *define.IntValueLimit { - l := &define.IntValueLimit{ - Min: min, - Max: max, - EnumList: make([]int64, 0), - } - return l -} - -// NewDefaultFloatValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 12:27 2024/4/29 -func NewDefaultFloatValueLimit(min *float64, max *float64) *define.FloatValueLimit { - l := &define.FloatValueLimit{ - Min: min, - Max: max, - EnumList: make([]float64, 0), - } - return l -} - -// NewDefaultSliceValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 12:30 2024/4/29 -func NewDefaultSliceValueLimit(minLength int64, maxLength int64) *define.SliceValueLimit { - l := &define.SliceValueLimit{ - MinLength: minLength, - MaxLength: maxLength, - } - return l -} - -// NewDefaultMapValueLimit ... -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 12:30 2024/4/29 -func NewDefaultMapValueLimit(minLength int64, maxLength int64) *define.MapValueLimit { - l := &define.MapValueLimit{ - MinLength: minLength, - MaxLength: maxLength, - IncludeFieldList: make([]string, 0), - } - return l -} diff --git a/run.go b/run.go index 867cae4..1ca3377 100644 --- a/run.go +++ b/run.go @@ -7,6 +7,354 @@ // Date : 2024-04-29 10:51 package validator -func Run() { +import ( + "bytes" + "encoding/json" + "fmt" + "git.zhangdeman.cn/gateway/validator/define" + "git.zhangdeman.cn/zhangdeman/consts" + "git.zhangdeman.cn/zhangdeman/serialize" + "git.zhangdeman.cn/zhangdeman/util" + "git.zhangdeman.cn/zhangdeman/wrapper" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + "strings" +) +// RunForStruct 验证结构体 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 14:18 2024/4/29 +func RunForStruct(sourceData interface{}, ruleList []*define.FieldRule) (map[string]interface{}, error) { + if nil == sourceData { + return map[string]interface{}{}, nil + } + byteData, _ := json.Marshal(sourceData) + var ( + sourceMapData map[string]interface{} + err error + ) + + d := json.NewDecoder(bytes.NewReader(byteData)) + d.UseNumber() + if err = d.Decode(&sourceMapData); nil != err { + return nil, err + } + if err = Run(sourceMapData, ruleList); nil != err { + return nil, err + } + return sourceMapData, nil +} + +// Run 运行参数验证 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 14:13 2024/4/29 +func Run(sourceData map[string]interface{}, ruleList []*define.FieldRule) error { + if nil == sourceData || len(sourceData) == 0 { + return nil + } + byteData, _ := json.Marshal(sourceData) + for _, itemRule := range ruleList { + if len(itemRule.Path) == 0 { + // 未指定验证数据位置 + continue + } + inputFieldVal := gjson.GetBytes(byteData, itemRule.Path) + if formatRule, err := validate(byteData, inputFieldVal, itemRule); nil != err { + return err + } else { + if !itemRule.DisableRewrite { + // 更新数据 + byteData, _ = sjson.SetBytes(byteData, itemRule.Path, formatRule) + } + } + } + sourceData = make(map[string]interface{}) + d := json.NewDecoder(bytes.NewReader(byteData)) + d.UseNumber() + if err := d.Decode(&sourceData); nil != err { + return err + } + return nil +} + +// getDataStatus 获取数据状态 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:59 2024/4/29 +func getDataStatus(val gjson.Result, dataType string) string { + if !val.Exists() { + return consts.DataStatusNotFound + } + switch dataType { + case consts.DataTypeString: + if len(val.String()) == 0 { + return consts.DataStatusIsEmpty + } + case consts.DataTypeFloat: + fallthrough + case consts.DataTypeInt: + if val.Float() == 0 { + return consts.DataStatusIsZero + } + default: + if strings.HasPrefix(dataType, "[]") { + // 数组 + if len(val.Array()) == 0 { + return consts.DataStatusIsEmpty + } + } else if strings.HasPrefix(dataType, "map") { + // 对象 + if len(val.Map()) == 0 { + return consts.DataStatusIsEmpty + } + } + } + return "" +} + +// validate 验证字段 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 14:32 2024/4/29 +func validate(sourceData []byte, val gjson.Result, rule *define.FieldRule) (interface{}, error) { + inputVal := val.Value() + if !val.Exists() { + if rule.IsRequired { + return nil, fmt.Errorf("%v : field is required, but not found", rule.Path) + } + // TODO : 验证有条件必传 + inputVal = rule.DefaultValue + } else { + if inputVal == nil { + if !rule.AllowNil { + return nil, fmt.Errorf("%v : field value is nil, but not allowed", rule.Path) + } + inputVal = rule.DefaultValue + } + } + + return handleData(inputVal, rule) +} + +// handleData 处理数据 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 14:43 2024/4/29 +func handleData(inputVal interface{}, rule *define.FieldRule) (interface{}, error) { + switch rule.Type { + case consts.DataTypeAny: // 任意类型 + return inputVal, nil + case consts.DataTypeFloat: // float数据 + return handleFloat(inputVal, rule) + case consts.DataTypeInt: // int类型 + return handleInt(inputVal, rule) + case consts.DataTypeString: // 字符串处理 + return handleString(inputVal, rule) + case consts.DataTypeMapStrFloat, consts.DataTypeMapStrBool, + consts.DataTypeMapStrInt, consts.DataTypeMapStrUint: + // 一律按照 map[string]float64处理 + return handleMapStringFloat(inputVal, rule) + case consts.DataTypeMapStrAny: // 对象结构 + return handleMapStringAny(inputVal, rule) + case consts.DataTypeMapStrSlice: // map列表 + + case consts.DataTypeMapAnyAny: // 任意类型map + + } + return nil, nil +} + +// handleFloat 处理float数据 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:29 2024/4/29 +func handleFloat(inputVal interface{}, rule *define.FieldRule) (float64, error) { + var ( + err error + formatData float64 + ) + if err = util.ConvertAssign(&formatData, inputVal); nil != err { + return 0, err + } + if !rule.AllowZero { + return 0, fmt.Errorf("%v : field type is float, but zero val is not allowed", rule.Path) + } + if nil == rule.ValueLimit { + return formatData, nil + } + if nil != rule.ValueLimit.Min && formatData < *rule.ValueLimit.Min { + return 0, fmt.Errorf("%v : field type is float, min val is %v, real val is %v", rule.Path, *rule.ValueLimit.Min, formatData) + } + if nil != rule.ValueLimit.Max && formatData > *rule.ValueLimit.Max { + return 0, fmt.Errorf("%v : field type is float, max val is %v, real val is %v", rule.Path, *rule.ValueLimit.Max, formatData) + } + if len(rule.ValueLimit.EnumList) > 0 { + if wrapper.ArrayType(rule.ValueLimit.EnumList).Has(formatData) < 0 { + return 0, fmt.Errorf("%v : field type is float, real val is %v, is not in enum list", rule.Path, formatData) + } + } + return formatData, nil +} + +// handleInt ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:36 2024/4/29 +func handleInt(inputVal interface{}, rule *define.FieldRule) (int64, error) { + var ( + err error + formatData int64 + ) + if err = util.ConvertAssign(&formatData, inputVal); nil != err { + return 0, err + } + if _, err = handleFloat(formatData, rule); nil != err { + return 0, err + } + return formatData, nil +} + +// handleString ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:53 2024/4/29 +func handleString(inputVal interface{}, rule *define.FieldRule) (string, error) { + var ( + err error + formatData string + ) + if err = util.ConvertAssign(&formatData, inputVal); nil != err { + return "", err + } + if nil == rule.ValueLimit { + return formatData, nil + } + if nil != rule.ValueLimit.Min { + if float64(len(formatData)) < *rule.ValueLimit.Min { + return "", fmt.Errorf("%v : data type is string, min length is %v, real length is %v", rule.Path, *rule.ValueLimit.Min, len(formatData)) + } + } + if nil != rule.ValueLimit.Max { + if float64(len(formatData)) >= *rule.ValueLimit.Max { + return "", fmt.Errorf("%v : data type is string, max length is %v, real length is %v", rule.Path, *rule.ValueLimit.Max, len(formatData)) + } + } + if len(rule.ValueLimit.EnumList) > 0 { + if wrapper.ArrayType(rule.ValueLimit.EnumList).Has(formatData) < 0 { + return "", fmt.Errorf("%v : data type is string, not in enum list, real val is %v", rule.Path, formatData) + } + } + return formatData, nil +} + +// handleMapStringFloat ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:38 2024/4/29 +func handleMapStringFloat(inputVal interface{}, rule *define.FieldRule) (map[string]float64, error) { + var ( + err error + res map[string]float64 + ) + + if err = strOrMapConvert(inputVal, &res); nil != err { + return nil, err + } + + dataFieldTable := make(map[string]string) + for k, _ := range res { + dataFieldTable[k] = k + } + + if err = validateMap(dataFieldTable, rule); nil != err { + return nil, err + } + + return res, nil +} + +// handleMapStringAny ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 17:19 2024/4/29 +func handleMapStringAny(inputVal interface{}, rule *define.FieldRule) (map[string]interface{}, error) { + var ( + err error + res map[string]interface{} + ) + + if err = strOrMapConvert(inputVal, &res); nil != err { + return nil, err + } + + dataFieldTable := make(map[string]string) + for k, _ := range res { + dataFieldTable[k] = k + } + + if err = validateMap(dataFieldTable, rule); nil != err { + return nil, err + } + + return res, nil +} + +// strOrMapConvert 字符串或map转map +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 17:26 2024/4/29 +func strOrMapConvert(inputVal interface{}, receiver interface{}) error { + var ( + err error + ) + + if inputValStr, ok := inputVal.(string); ok { + if err = serialize.JSON.UnmarshalWithNumber([]byte(inputValStr), receiver); nil != err { + return err + } + } else { + byteData := serialize.JSON.MarshalForByte(inputVal) + if err = serialize.JSON.UnmarshalWithNumber(byteData, receiver); nil != err { + return err + } + } + return nil +} + +// validateMap 验证map数据 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 17:21 2024/4/29 +func validateMap(dataFieldTable map[string]string, rule *define.FieldRule) error { + if nil == rule.ValueLimit { + return nil + } + if nil != rule.ValueLimit.Min && float64(len(dataFieldTable)) < *rule.ValueLimit.Min { + return fmt.Errorf("%v : data type is map, min item cnt is %v, real item cnt is %v", rule.Path, *rule.ValueLimit.Min, len(dataFieldTable)) + } + if nil != rule.ValueLimit.Max && float64(len(dataFieldTable)) >= *rule.ValueLimit.Max { + return fmt.Errorf("%v : data type is map, max item cnt is %v, real item max is %v", rule.Path, *rule.ValueLimit.Max, len(dataFieldTable)) + } + if nil != rule.ValueLimit.Map { + for _, itemField := range rule.ValueLimit.Map.IncludeFieldList { + if _, exist := dataFieldTable[itemField]; !exist { + return fmt.Errorf("%v : data type is map, %v field is required, but not found", rule.Path, itemField) + } + } + } + return nil } diff --git a/run_test.go b/run_test.go new file mode 100644 index 0000000..b3581e0 --- /dev/null +++ b/run_test.go @@ -0,0 +1,23 @@ +// Package validator ... +// +// Description : validator ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-04-29 14:14 +package validator + +import ( + "encoding/json" + "fmt" + "testing" +) + +func TestRun(t *testing.T) { + sourceData := map[string]interface{}{ + "name": "白茶清欢", + } + _ = Run(sourceData, nil) + byteData, _ := json.Marshal(sourceData) + fmt.Println(string(byteData)) +}