238 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Package validate ...
 | 
						|
//
 | 
						|
// Description : validate ...
 | 
						|
//
 | 
						|
// Author : go_developer@163.com<白茶清欢>
 | 
						|
//
 | 
						|
// Date : 2025-03-18 15:10
 | 
						|
package validate
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"git.zhangdeman.cn/zhangdeman/consts"
 | 
						|
	dynamicStructGenerate "git.zhangdeman.cn/zhangdeman/dynamic-struct"
 | 
						|
	filter "git.zhangdeman.cn/zhangdeman/json_filter"
 | 
						|
	"git.zhangdeman.cn/zhangdeman/serialize"
 | 
						|
	"github.com/creasty/defaults"
 | 
						|
	"github.com/go-playground/validator/v10"
 | 
						|
	"github.com/tidwall/gjson"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
var validatorInstance *validator.Validate
 | 
						|
 | 
						|
func init() {
 | 
						|
	validatorInstance = validator.New()
 | 
						|
	validatorInstance.SetTagName(TagValidate)
 | 
						|
}
 | 
						|
 | 
						|
// Run 执行参数验证
 | 
						|
//
 | 
						|
// Author : go_developer@163.com<白茶清欢>
 | 
						|
//
 | 
						|
// Date : 15:12 2025/3/18
 | 
						|
func Run(sourceData []byte, fieldList []StructField) (*handle, error) {
 | 
						|
	filterRuleList := make([]filter.MapRule, 0)
 | 
						|
	for _, item := range fieldList {
 | 
						|
		filterRuleList = append(filterRuleList, filter.MapRule{
 | 
						|
			SourcePath:   item.SourcePath,
 | 
						|
			TargetPath:   item.TargetPath,
 | 
						|
			Required:     item.Required,
 | 
						|
			DataType:     item.Type,
 | 
						|
			DefaultValue: item.DefaultValue,
 | 
						|
		})
 | 
						|
	}
 | 
						|
	// source_path => target_path数据转换
 | 
						|
	filterInstance := filter.NewFilter(string(sourceData), filterRuleList)
 | 
						|
	if err := filterInstance.Deal(); nil != err {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	// 格式化sourceData
 | 
						|
	handleInstance := &handle{
 | 
						|
		sourceData:       filterInstance.Byte(),
 | 
						|
		fieldList:        fieldList,
 | 
						|
		parentFieldTable: map[string]bool{},
 | 
						|
		formatVal:        filterInstance.String(),
 | 
						|
	}
 | 
						|
	tagTable := map[string]string{}
 | 
						|
	for _, item := range fieldList {
 | 
						|
		tagTable[item.TargetPath] = handleInstance.generateTag(item)
 | 
						|
		// 检测当前字段是否是某一个字段的父级字段
 | 
						|
		for _, field := range fieldList {
 | 
						|
			if field.TargetPath == item.TargetPath {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			// 当前字段以itemTarget开头
 | 
						|
			if strings.HasPrefix(field.TargetPath, item.TargetPath) {
 | 
						|
				// item.TargetPath是父级字段
 | 
						|
				handleInstance.parentFieldTable[item.TargetPath] = true
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	handleInstance.dynamicStruct = dynamicStructGenerate.NewStruct(tagTable)
 | 
						|
 | 
						|
	if err := handleInstance.Run(); nil != err {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return handleInstance, nil
 | 
						|
}
 | 
						|
 | 
						|
type handle struct {
 | 
						|
	sourceData       []byte
 | 
						|
	fieldList        []StructField
 | 
						|
	dynamicStruct    dynamicStructGenerate.IBuilder
 | 
						|
	formatVal        string
 | 
						|
	parentFieldTable map[string]bool // 父级字段路径表
 | 
						|
	res              any             // 处理结果
 | 
						|
}
 | 
						|
 | 
						|
// Run 执行验证
 | 
						|
func (h *handle) Run() error {
 | 
						|
	if len(h.fieldList) == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	for _, field := range h.fieldList {
 | 
						|
		if len(field.Errmsg) == 0 {
 | 
						|
			field.Errmsg = field.JsonTag + " : 参数校验不通过"
 | 
						|
		}
 | 
						|
		required, hasRequired := h.checkRequired(field)
 | 
						|
		field.Required = required
 | 
						|
		if h.parentFieldTable[field.TargetPath] {
 | 
						|
			// 中间层级字段, 无需额外处理, 验一下必传就行
 | 
						|
			if !gjson.GetBytes(h.sourceData, field.SourcePath).Exists() && field.Required {
 | 
						|
				return errors.New(field.TargetPath + " : 数据源路径数据不存在 => " + field.SourcePath)
 | 
						|
			}
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if field.Required && !hasRequired {
 | 
						|
			if nil == field.RuleList {
 | 
						|
				field.RuleList = make([]Rule, 0)
 | 
						|
			}
 | 
						|
			field.RuleList = append(field.RuleList, Rule{
 | 
						|
				Tag:  consts.ValidatorRuleCommonRequired.String(),
 | 
						|
				Args: nil,
 | 
						|
			})
 | 
						|
		}
 | 
						|
		// 支持嵌套结构体
 | 
						|
		fieldTag := h.generateTag(field)
 | 
						|
		// 这里需要设置为对应类型的零值就行, 此处传入值的目的只是为了确认数据类型
 | 
						|
		path := field.TargetPath
 | 
						|
		if path == "" {
 | 
						|
			path = field.JsonTag
 | 
						|
		}
 | 
						|
		h.dynamicStruct.AddField(path, "", consts.GetDataTypeDefaultValue(field.Type), fieldTag, false)
 | 
						|
	}
 | 
						|
	val := h.dynamicStruct.Build().New()
 | 
						|
	if err := serialize.JSON.UnmarshalWithNumber([]byte(h.formatVal), &val); nil != err {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if err := defaults.Set(val); nil != err {
 | 
						|
		// 默认值设置失败
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if err := validatorInstance.Struct(val); nil != err {
 | 
						|
		return GetValidateErr(val, err, TagErrMsg)
 | 
						|
	}
 | 
						|
	h.res = val // 记录结果
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Result 获取结果
 | 
						|
//
 | 
						|
// Author : go_developer@163.com<白茶清欢>
 | 
						|
//
 | 
						|
// Date : 10:50 2025/4/28
 | 
						|
func (h *handle) Result() any {
 | 
						|
	return h.res
 | 
						|
}
 | 
						|
 | 
						|
// Marshal 获取序列化之后的结果
 | 
						|
func (h *handle) Marshal(marshalType string) ([]byte, error) {
 | 
						|
	if marshalType == "" {
 | 
						|
		marshalType = "json" // 默认按照json序列化
 | 
						|
	}
 | 
						|
	return serialize.Wrapper.Marshal(marshalType, h.Result())
 | 
						|
}
 | 
						|
 | 
						|
// Map 直接获取map结果
 | 
						|
func (h *handle) Map(marshalType string) (map[string]any, error) {
 | 
						|
	if marshalType == "" {
 | 
						|
		marshalType = "json" // 默认按照json序列化
 | 
						|
	}
 | 
						|
	var res map[string]any
 | 
						|
	err := h.Transform(marshalType, &res)
 | 
						|
	if nil != err {
 | 
						|
		return res, err
 | 
						|
	}
 | 
						|
	return res, nil
 | 
						|
}
 | 
						|
 | 
						|
// Transform 数据转换
 | 
						|
func (h *handle) Transform(targetType string, receiver any) error {
 | 
						|
	if targetType == "" {
 | 
						|
		targetType = "json" // 默认按照json序列化
 | 
						|
	}
 | 
						|
	byteData, err := serialize.Wrapper.Marshal(targetType, h.Result())
 | 
						|
	if nil != err {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return serialize.Wrapper.Unmarshal(targetType, byteData, receiver)
 | 
						|
}
 | 
						|
 | 
						|
// checkRequired 格式化必传参数
 | 
						|
// 返回值1, 是否必传
 | 
						|
// 返回值2, 校验规则中是否存在必传校验
 | 
						|
func (h *handle) checkRequired(field StructField) (bool, bool) {
 | 
						|
	required := field.Required
 | 
						|
	isHasRequiredRule := false
 | 
						|
	for _, rule := range field.RuleList {
 | 
						|
		if rule.Tag == consts.ValidatorRuleCommonRequired.String() {
 | 
						|
			isHasRequiredRule = true
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if isHasRequiredRule {
 | 
						|
		required = true
 | 
						|
	}
 | 
						|
	if required {
 | 
						|
		if !isHasRequiredRule {
 | 
						|
			// 必传, 但是没有必传校验规则
 | 
						|
			return true, true
 | 
						|
		}
 | 
						|
		return true, false
 | 
						|
	}
 | 
						|
	return false, isHasRequiredRule
 | 
						|
}
 | 
						|
 | 
						|
// 生成结构体的tag标签
 | 
						|
func (h *handle) generateTag(field StructField) string {
 | 
						|
	tagList := []string{
 | 
						|
		fmt.Sprintf(`json:"%s"`, field.JsonTag), // json tag
 | 
						|
		fmt.Sprintf(`xml:"%s"`, field.JsonTag),  // xml tag
 | 
						|
		fmt.Sprintf(`yaml:"%s"`, field.JsonTag), // yaml tag
 | 
						|
		fmt.Sprintf(`toml:"%s"`, field.JsonTag), // toml tag
 | 
						|
		fmt.Sprintf(`ini:"%s"`, field.JsonTag),  // ini tag
 | 
						|
	}
 | 
						|
	if len(field.Errmsg) == 0 {
 | 
						|
		field.Errmsg = field.JsonTag + ": 参数校验不通过"
 | 
						|
	}
 | 
						|
	tagList = append(tagList, fmt.Sprintf(`%s:"%s"`, TagErrMsg, field.Errmsg)) // 错误信息tag
 | 
						|
	validateRuleList := []string{}
 | 
						|
	for _, itemRule := range field.RuleList {
 | 
						|
		if len(itemRule.Args) == 0 {
 | 
						|
			validateRuleList = append(validateRuleList, itemRule.Tag)
 | 
						|
		} else {
 | 
						|
			validateRuleList = append(validateRuleList, fmt.Sprintf("%s=%s", itemRule.Tag, strings.Join(itemRule.Args, " ")))
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// 验证规则tag
 | 
						|
	tagList = append(tagList, fmt.Sprintf(`%s:"%s"`, TagValidate, strings.Join(validateRuleList, ",")))
 | 
						|
	// 默认值
 | 
						|
	if field.DefaultValue == "-" && (field.Type == consts.DataTypeString || field.Type == consts.DataTypeStringPtr) {
 | 
						|
		field.DefaultValue = ""
 | 
						|
	}
 | 
						|
	tagList = append(tagList, fmt.Sprintf(`%s:"%s"`, TagDefaultValue, field.DefaultValue))
 | 
						|
	return strings.Join(tagList, " ")
 | 
						|
}
 |