// Package validate ... // // Description : validate ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2025-03-18 15:10 package validate import ( "encoding/json" "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) ([]byte, 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) return handleInstance.Run() } type handle struct { sourceData []byte fieldList []StructField dynamicStruct dynamicStructGenerate.IBuilder formatVal string parentFieldTable map[string]bool // 父级字段路径表 } // Run 执行验证 func (h *handle) Run() ([]byte, error) { if len(h.fieldList) == 0 { return []byte("{}"), 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 nil, 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 nil, err } if err := defaults.Set(val); nil != err { // 默认值设置失败 return nil, err } if err := validatorInstance.Struct(val); nil != err { return nil, GetValidateErr(val, err, TagErrMsg) } targetByte, _ := json.Marshal(val) return targetByte, nil } // 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 } 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, " ") }