// 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) (map[string]any, error) { if nil == sourceData { return map[string]any{}, 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 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]any, 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]any) 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) (any, 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 any, rule *define.FieldRule) (any, 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.DataTypeUint: return handleUint(inputVal, rule) case consts.DataTypeString: // 字符串处理 return handleString(inputVal, rule) case consts.DataTypeBool: return handleBool(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列表 return handleMapStringSlice(inputVal, rule) case consts.DataTypeMapAnyAny: // 任意类型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) }