// 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" "github.com/tidwall/gjson" "github.com/tidwall/sjson" "strings" ) // RunForStruct 验证结构体 // // Author : go_developer@163.com<白茶清欢> // // Date : 14:18 2024/4/29 func RunForStruct(sourceData any, ruleList []*define.FieldRule, receiver any) error { if nil == sourceData { return nil } byteData, _ := json.Marshal(sourceData) var ( sourceMapData map[string]any err error ) d := json.NewDecoder(bytes.NewReader(byteData)) d.UseNumber() if err = d.Decode(&sourceMapData); nil != err { return err } return Run(sourceMapData, ruleList, receiver) } // Run 运行参数验证 // // Author : go_developer@163.com<白茶清欢> // // Date : 14:13 2024/4/29 func Run(sourceData map[string]any, ruleList []*define.FieldRule, receiver any) error { for _, itemRule := range ruleList { itemRule.DefaultValue = nil } byteData, err := RunWithResult(sourceData, ruleList) if nil != err { return err } if nil != receiver { d := json.NewDecoder(bytes.NewReader(byteData)) d.UseNumber() if err := d.Decode(&receiver); nil != err { return err } } return nil } // RunWithResult 运行并返回结果 // // Author : go_developer@163.com<白茶清欢> // // Date : 15:42 2024/6/12 func RunWithResult(sourceData map[string]any, ruleList []*define.FieldRule) ([]byte, error) { if nil == sourceData { sourceData = make(map[string]any) } byteData, _ := json.Marshal(sourceData) resultByteData, _ := json.Marshal(sourceData) for _, itemRule := range ruleList { if len(itemRule.Path) == 0 { // 未指定验证数据位置 continue } // 通过有条件必传, 填充 is_required checkRuleConditionRequiredRule(byteData, itemRule) inputFieldVal := gjson.GetBytes(byteData, itemRule.Path) if formatValue, err := validate(byteData, inputFieldVal, itemRule); nil != err { return nil, err } else { if !itemRule.DisableRewrite { // 更新数据 resultByteData, _ = sjson.SetBytes(resultByteData, itemRule.Path, formatValue) } } } return resultByteData, nil } // checkRuleConditionRequiredRule 校验有条件必传 // // Author : go_developer@163.com<白茶清欢> // // Date : 21:10 2024/5/16 func checkRuleConditionRequiredRule(sourceData []byte, rule *define.FieldRule) { if rule.IsRequired { // 本身已经必传, 无所谓有条件无条件 return } // 验证有条件必传 if len(rule.RequiredConditionGroup) > 0 { for _, itemRuleGroup := range rule.RequiredConditionGroup { isRequired := true for _, itemFieldRule := range itemRuleGroup { dependFieldStatus := getDataStatus(gjson.GetBytes(sourceData, itemFieldRule.DependOnField), itemFieldRule.DependOnFieldType) isRequired = isRequired && inArray(itemFieldRule.DependOnFieldStatus, dependFieldStatus) } if isRequired { rule.IsRequired = true // 非必传参数, 命中有条件必传 break } } } } // 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 } } else if strings.HasPrefix(dataType, "*") { // 指针类型 if nil == val.Value() { return consts.DataStatusIsNil } } } return "" } // formatInputVal ... // // Author : go_developer@163.com<白茶清欢> // // Date : 18:02 2024/5/2 func formatInputVal(val gjson.Result, rule *define.FieldRule) (any, error) { inputVal := val.Value() if nil == inputVal { if rule.IsRequired { return nil, fmt.Errorf("%v : data is required, but get nil", rule.Path) } if rule.DisableAutoConvert { inputVal = rule.DefaultValue } else { if nil == rule.DefaultValue { return nil, nil } if strVal, ok := rule.DefaultValue.(string); ok { inputVal = strings.TrimSpace(strVal) } else { inputVal = rule.DefaultValue } } } else { if inputValStr, ok := inputVal.(string); ok { if !rule.DisableAutoTrimSpace { // 自动去空格 inputVal = strings.TrimSpace(inputValStr) } else { inputVal = inputValStr } } } return inputVal, nil } // inArray ... // // Author : go_developer@163.com<白茶清欢> // // Date : 21:00 2024/5/16 func inArray(enumList []string, val string) bool { for _, item := range enumList { if strings.ToUpper(val) == strings.ToUpper(item) { return true } } return false } // validate 验证字段 // // Author : go_developer@163.com<白茶清欢> // // Date : 14:32 2024/4/29 func validate(sourceData []byte, val gjson.Result, rule *define.FieldRule) (any, error) { var ( err error inputVal any ) if !val.Exists() || nil == val.Value() { if rule.IsRequired { return nil, fmt.Errorf("%v : field is required, but not found", rule.Path) } if strings.HasSuffix(rule.Type, "_ptr") { // 指针类型数据, 无需验证 return nil, nil } inputVal = rule.DefaultValue } else { if inputVal, err = formatInputVal(val, rule); nil != err { return nil, err } } return handleData(inputVal, rule) } // handleData 处理数据 // // Author : go_developer@163.com<白茶清欢> // // Date : 14:43 2024/4/29 func handleData(inputVal any, rule *define.FieldRule) (any, error) { rule.Type = strings.ToLower(rule.Type) // 处理真实的map和序列化之后的map if strings.HasPrefix(rule.Type, "map") { if strings.HasSuffix(rule.Type, "_marshal") { rule.MapConfig = &define.MapConfig{Mode: consts.DataMapModelMarshal} } else { rule.MapConfig = &define.MapConfig{Mode: consts.DataMapModelReal} } } switch rule.Type { case consts.DataTypeAny: // 任意类型 return inputVal, nil case consts.DataTypeFloat, consts.DataTypeFloatPtr: // float数据 return handleFloat(inputVal, rule) case consts.DataTypeInt, consts.DataTypeIntPtr: // int类型 return handleInt(inputVal, rule) case consts.DataTypeUint, consts.DataTypeUintPtr: return handleUint(inputVal, rule) case consts.DataTypeString, consts.DataTypeStringPtr: // 字符串处理 return handleString(inputVal, rule) case consts.DataTypeBool, consts.DataTypeBoolPtr: return handleBool(inputVal, rule) case consts.DataTypeMapStrFloat, consts.DataTypeMapStrFloatWithMarshal, consts.DataTypeMapStrBool, consts.DataTypeMapStrBoolWithMarshal, consts.DataTypeMapStrInt, consts.DataTypeMapStrIntWithMarshal, consts.DataTypeMapStrUint, consts.DataTypeMapStrUintWithMarshal: // 一律按照 map[string]float64处理 return handleMapStringFloat(inputVal, rule) case consts.DataTypeMapStrAny, consts.DataTypeMapStrAnyWithMarshal: // 对象结构 return handleMapStringAny(inputVal, rule) case consts.DataTypeMapStrStr, consts.DataTypeMapStrStrWithMarshal: // 对象结构 return handleMapStringString(inputVal, rule) case consts.DataTypeMapStrSlice, consts.DataTypeMapStrSliceWithMarshal: // map列表 return handleMapStringSlice(inputVal, rule) case consts.DataTypeMapAnyAny, consts.DataTypeMapAnyAnyWithMarshal: // 任意类型map return handleMapAnyAny(inputVal, rule) case consts.DataTypeSliceInt, consts.DataTypeSliceIntWithChar: // int数组处理 return handleSliceInt(inputVal, rule) case consts.DataTypeSliceUint, consts.DataTypeSliceUintWithChar: // uint数组处理 return handleSliceUint(inputVal, rule) case consts.DataTypeSliceFloat, consts.DataTypeSliceFloatWithChar: // float数组处理 return handleSliceFloat(inputVal, rule) case consts.DataTypeSliceBool, consts.DataTypeSliceBoolWithChar: // bool数组 return handleSliceBool(inputVal, rule) case consts.DataTypeSliceSlice: return handleSliceSlice(inputVal, rule) case consts.DataTypeSliceMapAnyAny: // map 列表 return handleSliceMapAny(inputVal, rule) case consts.DataTypeSliceMapStringAny: return handleSliceMapString(inputVal, rule) } return nil, fmt.Errorf("%v : data type [%v] is not support", rule.Path, rule.Type) }