validator/run.go

530 lines
14 KiB
Go
Raw Normal View History

2024-04-29 12:17:18 +08:00
// Package validator ...
//
// Description : validator ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-04-29 10:51
package validator
2024-04-29 17:29:46 +08:00
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"
2024-04-30 22:22:28 +08:00
"reflect"
2024-04-29 17:29:46 +08:00
"strings"
)
2024-04-29 12:17:18 +08:00
2024-04-29 17:29:46 +08:00
// RunForStruct 验证结构体
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:18 2024/4/29
func RunForStruct(sourceData any, ruleList []*define.FieldRule) (map[string]any, error) {
2024-04-29 17:29:46 +08:00
if nil == sourceData {
return map[string]any{}, nil
2024-04-29 17:29:46 +08:00
}
byteData, _ := json.Marshal(sourceData)
var (
sourceMapData map[string]any
2024-04-29 17:29:46 +08:00
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 {
2024-04-29 17:29:46 +08:00
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)
2024-04-29 17:29:46 +08:00
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) {
2024-04-29 17:29:46 +08:00
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) {
2024-04-29 17:29:46 +08:00
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)
2024-04-30 22:22:28 +08:00
case consts.DataTypeUint:
return handleUint(inputVal, rule)
2024-04-29 17:29:46 +08:00
case consts.DataTypeString: // 字符串处理
return handleString(inputVal, rule)
2024-04-30 22:22:28 +08:00
case consts.DataTypeBool:
return handleBool(inputVal, rule)
2024-04-29 17:29:46 +08:00
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)
2024-04-29 17:29:46 +08:00
case consts.DataTypeMapAnyAny: // 任意类型map
return handleMapAnyAny(inputVal, rule)
2024-04-30 22:22:28 +08:00
case consts.DataTypeSliceUint, consts.DataTypeSliceUintWithChar: // uint数组处理
2024-04-29 17:29:46 +08:00
}
return nil, nil
}
// handleFloat 处理float数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:29 2024/4/29
func handleFloat(inputVal any, rule *define.FieldRule) (float64, error) {
2024-04-29 17:29:46 +08:00
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 any, rule *define.FieldRule) (int64, error) {
2024-04-29 17:29:46 +08:00
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
}
2024-04-30 22:22:28 +08:00
// handleUint ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:28 2024/4/30
func handleUint(inputVal any, rule *define.FieldRule) (uint64, error) {
var (
err error
formatData uint64
)
if err = util.ConvertAssign(&formatData, inputVal); nil != err {
return 0, err
}
if _, err = handleFloat(formatData, rule); nil != err {
return 0, err
}
return formatData, nil
}
// handleBool...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:26 2024/4/30
func handleBool(inputVal any, rule *define.FieldRule) (bool, error) {
var (
err error
formatData bool
)
if err = util.ConvertAssign(&formatData, inputVal); nil != err {
return 0, err
}
if _, err = handleFloat(formatData, rule); nil != err {
return false, err
}
return formatData, nil
}
2024-04-29 17:29:46 +08:00
// handleString ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:53 2024/4/29
func handleString(inputVal any, rule *define.FieldRule) (string, error) {
2024-04-29 17:29:46 +08:00
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 any, rule *define.FieldRule) (map[string]float64, error) {
2024-04-29 17:29:46 +08:00
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 any, rule *define.FieldRule) (map[string]any, error) {
var (
err error
res map[string]any
)
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
}
// handleMapStringSlice...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:42 2024/4/29
func handleMapStringSlice(inputVal any, rule *define.FieldRule) (map[string][]any, error) {
2024-04-29 17:29:46 +08:00
var (
err error
res map[string][]any
2024-04-29 17:29:46 +08:00
)
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
}
// handleMapAnyAny map[any]any处理
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:33 2024/4/29
func handleMapAnyAny(inputVal any, rule *define.FieldRule) (map[any]any, error) {
var (
err error
res = map[any]any{}
jsonRes gjson.Result
)
if inputValStr, ok := inputVal.(string); ok {
jsonRes = gjson.Parse(inputValStr)
} else {
jsonRes = gjson.Parse(serialize.JSON.MarshalForString(inputVal))
}
if !jsonRes.IsObject() {
return nil, fmt.Errorf("%v : is not a map", rule.Path)
}
fieldTable := make(map[string]string)
jsonRes.ForEach(func(key, value gjson.Result) bool {
fieldTable[key.String()] = key.String()
res[key.Value()] = value.Value()
return true
})
if err = validateMap(fieldTable, rule); nil != err {
return nil, err
}
return res, nil
}
2024-04-29 17:29:46 +08:00
// strOrMapConvert 字符串或map转map
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:26 2024/4/29
func strOrMapConvert(inputVal any, receiver any) error {
2024-04-29 17:29:46 +08:00
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
2024-04-29 12:17:18 +08:00
}
2024-04-30 22:22:28 +08:00
// handleSlice 数组处理
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:30 2024/4/30
func handleSlice(inputValue interface{}, rule *define.FieldRule) ([]any, error) {
inputValType := reflect.TypeOf(inputValue).Kind()
if inputValType != reflect.Slice && inputValType != reflect.String {
return nil, fmt.Errorf("%v : data type is expect slice or string, but get %v", rule.Path, inputValType.String())
}
if inputValType == reflect.Slice {
inputValue = serialize.JSON.MarshalForString(inputValue)
// 重置配置
if nil == rule.SliceConfig {
rule.SliceConfig = &define.SliceConfig{
Mode: consts.DataSliceModelMarshal,
DisableAutoConvert: false,
SplitChar: "",
}
} else {
rule.SliceConfig.Mode = consts.DataSliceModelMarshal
}
}
if inputStr, ok := inputValue.(string); ok {
return handleSliceString(inputStr, rule)
}
return nil, fmt.Errorf("%v : data type is expect slice or string, but get %v", rule.Path, inputValType.String())
}
// handleSliceString ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:06 2024/4/30
func handleSliceString(inputStr string, rule *define.FieldRule) ([]any, error) {
if nil == rule.SliceConfig {
return nil, fmt.Errorf("%v : data type is slice, but get string", rule.Path)
}
if rule.SliceConfig.Mode == consts.DataSliceModelReal {
return nil, fmt.Errorf("%v : data type expect real slice, but get string", rule.Path)
}
if rule.SliceConfig.Mode == consts.DataSliceModelMarshal { // json序列化之后的
var (
err error
res []any
)
if err = serialize.JSON.UnmarshalWithNumber([]byte(inputStr), &res); nil != err {
return nil, fmt.Errorf("%v : data type expect marshal slice, but can not convert", rule.Path)
}
return res, nil
}
if rule.SliceConfig.Mode == consts.DataSliceModelWithSplitChar { // 指定字符串切割
strArr := strings.Split(inputStr, rule.SliceConfig.SplitChar)
anyArr := make([]any, 0)
for _, item := range strArr {
anyArr = append(anyArr, item)
}
return anyArr, nil
}
return nil, fmt.Errorf("%v : data type is slice, but rule mode [%v] is not supported", rule.Path, rule.SliceConfig.Mode)
}