// Package validator ... // // Description : validator ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2024-04-29 10:51 package validator 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 }