github.com/zhongdalu/gf@v1.0.0/g/util/gvalid/gvalid_check.go (about)

     1  // Copyright 2017-2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  package gvalid
     8  
     9  import (
    10  	"regexp"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/zhongdalu/gf/g/container/gmap"
    15  	"github.com/zhongdalu/gf/g/encoding/gjson"
    16  	"github.com/zhongdalu/gf/g/net/gipv4"
    17  	"github.com/zhongdalu/gf/g/net/gipv6"
    18  	"github.com/zhongdalu/gf/g/os/gtime"
    19  	"github.com/zhongdalu/gf/g/text/gregex"
    20  	"github.com/zhongdalu/gf/g/util/gconv"
    21  )
    22  
    23  const (
    24  	gSINGLE_RULE_PATTERN = `^([\w-]+):{0,1}(.*)` // 单条规则匹配正则
    25  )
    26  
    27  var (
    28  	// 默认错误消息管理对象(并发安全)
    29  	errorMsgMap = gmap.NewStrStrMap()
    30  
    31  	// 单规则正则对象,这里使用包内部变量存储,不需要多次解析
    32  	ruleRegex, _ = regexp.Compile(gSINGLE_RULE_PATTERN)
    33  
    34  	// 即使参数为空(nil|"")也需要校验的规则,主要是必需规则及关联规则
    35  	mustCheckRulesEvenValueEmpty = map[string]struct{}{
    36  		"required":             struct{}{},
    37  		"required-if":          struct{}{},
    38  		"required-unless":      struct{}{},
    39  		"required-with":        struct{}{},
    40  		"required-with-all":    struct{}{},
    41  		"required-without":     struct{}{},
    42  		"required-without-all": struct{}{},
    43  		"same":                 struct{}{},
    44  		"different":            struct{}{},
    45  		"in":                   struct{}{},
    46  		"not-in":               struct{}{},
    47  		"regex":                struct{}{},
    48  	}
    49  	// 所有支持的校验规则
    50  	allSupportedRules = map[string]struct{}{
    51  		"required":             struct{}{},
    52  		"required-if":          struct{}{},
    53  		"required-unless":      struct{}{},
    54  		"required-with":        struct{}{},
    55  		"required-with-all":    struct{}{},
    56  		"required-without":     struct{}{},
    57  		"required-without-all": struct{}{},
    58  		"date":                 struct{}{},
    59  		"date-format":          struct{}{},
    60  		"email":                struct{}{},
    61  		"phone":                struct{}{},
    62  		"telephone":            struct{}{},
    63  		"passport":             struct{}{},
    64  		"password":             struct{}{},
    65  		"password2":            struct{}{},
    66  		"password3":            struct{}{},
    67  		"postcode":             struct{}{},
    68  		"id-number":            struct{}{},
    69  		"qq":                   struct{}{},
    70  		"ip":                   struct{}{},
    71  		"ipv4":                 struct{}{},
    72  		"ipv6":                 struct{}{},
    73  		"mac":                  struct{}{},
    74  		"url":                  struct{}{},
    75  		"domain":               struct{}{},
    76  		"length":               struct{}{},
    77  		"min-length":           struct{}{},
    78  		"max-length":           struct{}{},
    79  		"between":              struct{}{},
    80  		"min":                  struct{}{},
    81  		"max":                  struct{}{},
    82  		"json":                 struct{}{},
    83  		"integer":              struct{}{},
    84  		"float":                struct{}{},
    85  		"boolean":              struct{}{},
    86  		"same":                 struct{}{},
    87  		"different":            struct{}{},
    88  		"in":                   struct{}{},
    89  		"not-in":               struct{}{},
    90  		"regex":                struct{}{},
    91  	}
    92  	// 布尔Map
    93  	boolMap = map[string]struct{}{
    94  		// true
    95  		"1":    struct{}{},
    96  		"true": struct{}{},
    97  		"on":   struct{}{},
    98  		"yes":  struct{}{},
    99  		// false
   100  		"":      struct{}{},
   101  		"0":     struct{}{},
   102  		"false": struct{}{},
   103  		"off":   struct{}{},
   104  		"no":    struct{}{},
   105  	}
   106  )
   107  
   108  // 检测单条数据的规则:
   109  //
   110  // 1. value为需要校验的数据,可以为任意基本数据类型;
   111  //
   112  // 2. msgs为自定义错误信息,由于同一条数据的校验规则可能存在多条,为方便调用,参数类型支持 string/map/struct/*struct,
   113  // 允许传递多个自定义的错误信息,如果类型为string,那么中间使用"|"符号分隔多个自定义错误;
   114  //
   115  // 3. params参数为联合校验参数,支持任意的map/struct/*struct类型,对于需要联合校验的规则有效,如:required-*、same、different;
   116  func Check(value interface{}, rules string, msgs interface{}, params ...interface{}) *Error {
   117  	// 内部会将参数全部转换为字符串类型进行校验
   118  	val := strings.TrimSpace(gconv.String(value))
   119  	data := make(map[string]string)
   120  	errorMsgs := make(map[string]string)
   121  	if len(params) > 0 {
   122  		for k, v := range gconv.Map(params[0]) {
   123  			data[k] = gconv.String(v)
   124  		}
   125  	}
   126  	// 自定义错误消息处理
   127  	msgArray := make([]string, 0)
   128  	customMsgMap := make(map[string]string)
   129  	switch v := msgs.(type) {
   130  	case string:
   131  		msgArray = strings.Split(v, "|")
   132  	default:
   133  		for k, v := range gconv.Map(msgs) {
   134  			customMsgMap[k] = gconv.String(v)
   135  		}
   136  	}
   137  	ruleItems := strings.Split(strings.TrimSpace(rules), "|")
   138  	// 规则项预处理, 主要解决规则中存在的"|"关键字符号
   139  	for i := 0; ; {
   140  		array := strings.Split(ruleItems[i], ":")
   141  		if _, ok := allSupportedRules[array[0]]; !ok {
   142  			if i > 0 && ruleItems[i-1][:5] == "regex" {
   143  				ruleItems[i-1] += "|" + ruleItems[i]
   144  				ruleItems = append(ruleItems[:i], ruleItems[i+1:]...)
   145  			} else {
   146  				return newErrorStr("parse_error", "invalid rules:"+rules)
   147  			}
   148  		} else {
   149  			i++
   150  		}
   151  		if i == len(ruleItems) {
   152  			break
   153  		}
   154  	}
   155  	for index := 0; index < len(ruleItems); {
   156  		item := ruleItems[index]
   157  		results := ruleRegex.FindStringSubmatch(item)
   158  		ruleKey := strings.TrimSpace(results[1])
   159  		ruleVal := strings.TrimSpace(results[2])
   160  		match := false
   161  		if len(msgArray) > index {
   162  			customMsgMap[ruleKey] = strings.TrimSpace(msgArray[index])
   163  		}
   164  		switch ruleKey {
   165  		// 必须字段
   166  		case "required":
   167  			fallthrough
   168  		case "required-if":
   169  			fallthrough
   170  		case "required-unless":
   171  			fallthrough
   172  		case "required-with":
   173  			fallthrough
   174  		case "required-with-all":
   175  			fallthrough
   176  		case "required-without":
   177  			fallthrough
   178  		case "required-without-all":
   179  			match = checkRequired(val, ruleKey, ruleVal, data)
   180  
   181  		// 长度范围
   182  		case "length":
   183  			fallthrough
   184  		case "min-length":
   185  			fallthrough
   186  		case "max-length":
   187  			if msg := checkLength(val, ruleKey, ruleVal, customMsgMap); msg != "" {
   188  				errorMsgs[ruleKey] = msg
   189  			} else {
   190  				match = true
   191  			}
   192  
   193  		// 大小范围
   194  		case "min":
   195  			fallthrough
   196  		case "max":
   197  			fallthrough
   198  		case "between":
   199  			if msg := checkSize(val, ruleKey, ruleVal, customMsgMap); msg != "" {
   200  				errorMsgs[ruleKey] = msg
   201  			} else {
   202  				match = true
   203  			}
   204  
   205  		// 自定义正则判断
   206  		case "regex":
   207  			// 需要判断是否被|符号截断,如果是,那么需要进行整合
   208  			for i := index + 1; i < len(ruleItems); i++ {
   209  				// 判断下一个规则是否合法,不合法那么和当前正则规则进行整合
   210  				if !gregex.IsMatchString(gSINGLE_RULE_PATTERN, ruleItems[i]) {
   211  					ruleVal += "|" + ruleItems[i]
   212  					index++
   213  				}
   214  			}
   215  			match = gregex.IsMatchString(ruleVal, val)
   216  
   217  		// 日期格式,
   218  		case "date":
   219  			// 使用标准日期格式检查,但是日期之间必须带连接符号
   220  			if _, err := gtime.StrToTime(val); err == nil {
   221  				match = true
   222  				break
   223  			}
   224  			// 检查是否不带日期连接符号的格式
   225  			if _, err := gtime.StrToTime(val, "Ymd"); err == nil {
   226  				match = true
   227  				break
   228  			}
   229  
   230  		// 日期格式,需要给定日期格式
   231  		case "date-format":
   232  			if _, err := gtime.StrToTimeFormat(val, ruleVal); err == nil {
   233  				match = true
   234  			}
   235  
   236  		// 两字段值应相同(非敏感字符判断,非类型判断)
   237  		case "same":
   238  			if v, ok := data[ruleVal]; ok {
   239  				if strings.Compare(val, v) == 0 {
   240  					match = true
   241  				}
   242  			}
   243  
   244  		// 两字段值不应相同(非敏感字符判断,非类型判断)
   245  		case "different":
   246  			match = true
   247  			if v, ok := data[ruleVal]; ok {
   248  				if strings.Compare(val, v) == 0 {
   249  					match = false
   250  				}
   251  			}
   252  
   253  		// 字段值应当在指定范围中
   254  		case "in":
   255  			array := strings.Split(ruleVal, ",")
   256  			for _, v := range array {
   257  				if strings.Compare(val, strings.TrimSpace(v)) == 0 {
   258  					match = true
   259  					break
   260  				}
   261  			}
   262  
   263  		// 字段值不应当在指定范围中
   264  		case "not-in":
   265  			match = true
   266  			array := strings.Split(ruleVal, ",")
   267  			for _, v := range array {
   268  				if strings.Compare(val, strings.TrimSpace(v)) == 0 {
   269  					match = false
   270  					break
   271  				}
   272  			}
   273  
   274  		/*
   275  		 * 验证所给手机号码是否符合手机号的格式.
   276  		 * 移动: 134、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、178(4G)、147(上网卡);
   277  		 * 联通: 130、131、132、155、156、185、186、176(4G)、145(上网卡)、175;
   278  		 * 电信: 133、153、180、181、189 、177(4G);
   279  		 * 卫星通信:  1349
   280  		 * 虚拟运营商: 170、173
   281  		 * 2018新增: 16x, 19x
   282  		 */
   283  		case "phone":
   284  			match = gregex.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`, val)
   285  
   286  		// 国内座机电话号码:"XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
   287  		case "telephone":
   288  			match = gregex.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, val)
   289  
   290  		// 腾讯QQ号,从10000开始
   291  		case "qq":
   292  			match = gregex.IsMatchString(`^[1-9][0-9]{4,}$`, val)
   293  
   294  		// 中国邮政编码
   295  		case "postcode":
   296  			match = gregex.IsMatchString(`^\d{6}$`, val)
   297  
   298  		/*
   299  		   公民身份证号
   300  		   xxxxxx yyyy MM dd 375 0     十八位
   301  		   xxxxxx   yy MM dd  75 0     十五位
   302  
   303  		   地区:[1-9]\d{5}
   304  		   年的前两位:(18|19|([23]\d))      1800-2399
   305  		   年的后两位:\d{2}
   306  		   月份:((0[1-9])|(10|11|12))
   307  		   天数:(([0-2][1-9])|10|20|30|31) 闰年不能禁止29+
   308  
   309  		   三位顺序码:\d{3}
   310  		   两位顺序码:\d{2}
   311  		   校验码:   [0-9Xx]
   312  
   313  		   十八位:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
   314  		   十五位:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$
   315  
   316  		   总:
   317  		   (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
   318  		*/
   319  		case "id-number":
   320  			match = gregex.IsMatchString(`(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)`, val)
   321  
   322  		// 通用帐号规则(字母开头,只能包含字母、数字和下划线,长度在6~18之间)
   323  		case "passport":
   324  			match = gregex.IsMatchString(`^[a-zA-Z]{1}\w{5,17}$`, val)
   325  
   326  		// 通用密码(任意可见字符,长度在6~18之间)
   327  		case "password":
   328  			match = gregex.IsMatchString(`^[\w\S]{6,18}$`, val)
   329  
   330  		// 中等强度密码(在弱密码的基础上,必须包含大小写字母和数字)
   331  		case "password2":
   332  			if gregex.IsMatchString(`^[\w\S]{6,18}$`, val) && gregex.IsMatchString(`[a-z]+`, val) && gregex.IsMatchString(`[A-Z]+`, val) && gregex.IsMatchString(`\d+`, val) {
   333  				match = true
   334  			}
   335  
   336  		// 强等强度密码(在弱密码的基础上,必须包含大小写字母、数字和特殊字符)
   337  		case "password3":
   338  			if gregex.IsMatchString(`^[\w\S]{6,18}$`, val) && gregex.IsMatchString(`[a-z]+`, val) && gregex.IsMatchString(`[A-Z]+`, val) && gregex.IsMatchString(`\d+`, val) && gregex.IsMatchString(`[^a-zA-Z0-9]+`, val) {
   339  				match = true
   340  			}
   341  
   342  		// json
   343  		case "json":
   344  			if _, err := gjson.Decode([]byte(val)); err == nil {
   345  				match = true
   346  			}
   347  
   348  		// 整数
   349  		case "integer":
   350  			if _, err := strconv.Atoi(val); err == nil {
   351  				match = true
   352  			}
   353  
   354  		// 小数
   355  		case "float":
   356  			if _, err := strconv.ParseFloat(val, 10); err == nil {
   357  				match = true
   358  			}
   359  
   360  		// 布尔值(1,true,on,yes:true | 0,false,off,no,"":false)
   361  		case "boolean":
   362  			match = false
   363  			if _, ok := boolMap[strings.ToLower(val)]; ok {
   364  				match = true
   365  			}
   366  
   367  		// 邮件
   368  		case "email":
   369  			match = gregex.IsMatchString(`^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$`, val)
   370  
   371  		// URL
   372  		case "url":
   373  			match = gregex.IsMatchString(`(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`, val)
   374  
   375  		// domain
   376  		case "domain":
   377  			match = gregex.IsMatchString(`^([0-9a-zA-Z][0-9a-zA-Z-]{0,62}\.)+([0-9a-zA-Z][0-9a-zA-Z-]{0,62})\.?$`, val)
   378  
   379  		// IP(IPv4/IPv6)
   380  		case "ip":
   381  			match = gipv4.Validate(val) || gipv6.Validate(val)
   382  
   383  		// IPv4
   384  		case "ipv4":
   385  			match = gipv4.Validate(val)
   386  
   387  		// IPv6
   388  		case "ipv6":
   389  			match = gipv6.Validate(val)
   390  
   391  		// MAC地址
   392  		case "mac":
   393  			match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, val)
   394  
   395  		default:
   396  			errorMsgs[ruleKey] = "Invalid rule name:" + ruleKey
   397  		}
   398  
   399  		// 错误消息整合
   400  		if !match {
   401  			// 不存在则使用默认的错误信息,
   402  			// 如果在校验过程中已经设置了错误信息,那么这里便不作处理
   403  			if _, ok := errorMsgs[ruleKey]; !ok {
   404  				if msg, ok := customMsgMap[ruleKey]; ok {
   405  					errorMsgs[ruleKey] = msg
   406  				} else {
   407  					errorMsgs[ruleKey] = errorMsgMap.Get(ruleKey)
   408  				}
   409  			}
   410  		}
   411  		index++
   412  	}
   413  	if len(errorMsgs) > 0 {
   414  		return newError([]string{rules}, ErrorMap{
   415  			// 单条数值校验没有键名
   416  			"": errorMsgs,
   417  		})
   418  	}
   419  	return nil
   420  }
   421  
   422  // 判断必须字段
   423  func checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool {
   424  	required := false
   425  	switch ruleKey {
   426  	// 必须字段
   427  	case "required":
   428  		required = true
   429  
   430  	// 必须字段(当任意所给定字段值与所给值相等时)
   431  	case "required-if":
   432  		required = false
   433  		array := strings.Split(ruleVal, ",")
   434  		// 必须为偶数,才能是键值对匹配
   435  		if len(array)%2 == 0 {
   436  			for i := 0; i < len(array); {
   437  				tk := array[i]
   438  				tv := array[i+1]
   439  				if v, ok := params[tk]; ok {
   440  					if strings.Compare(tv, v) == 0 {
   441  						required = true
   442  						break
   443  					}
   444  				}
   445  				i += 2
   446  			}
   447  		}
   448  
   449  	// 必须字段(当所给定字段值与所给值都不相等时)
   450  	case "required-unless":
   451  		required = true
   452  		array := strings.Split(ruleVal, ",")
   453  		// 必须为偶数,才能是键值对匹配
   454  		if len(array)%2 == 0 {
   455  			for i := 0; i < len(array); {
   456  				tk := array[i]
   457  				tv := array[i+1]
   458  				if v, ok := params[tk]; ok {
   459  					if strings.Compare(tv, v) == 0 {
   460  						required = false
   461  						break
   462  					}
   463  				}
   464  				i += 2
   465  			}
   466  		}
   467  
   468  	// 必须字段(当所给定任意字段值不为空时)
   469  	case "required-with":
   470  		required = false
   471  		array := strings.Split(ruleVal, ",")
   472  		for i := 0; i < len(array); i++ {
   473  			if params[array[i]] != "" {
   474  				required = true
   475  				break
   476  			}
   477  		}
   478  
   479  	// 必须字段(当所给定所有字段值都不为空时)
   480  	case "required-with-all":
   481  		required = true
   482  		array := strings.Split(ruleVal, ",")
   483  		for i := 0; i < len(array); i++ {
   484  			if params[array[i]] == "" {
   485  				required = false
   486  				break
   487  			}
   488  		}
   489  
   490  	// 必须字段(当所给定任意字段值为空时)
   491  	case "required-without":
   492  		required = false
   493  		array := strings.Split(ruleVal, ",")
   494  		for i := 0; i < len(array); i++ {
   495  			if params[array[i]] == "" {
   496  				required = true
   497  				break
   498  			}
   499  		}
   500  
   501  	// 必须字段(当所给定所有字段值都为空时)
   502  	case "required-without-all":
   503  		required = true
   504  		array := strings.Split(ruleVal, ",")
   505  		for i := 0; i < len(array); i++ {
   506  			if params[array[i]] != "" {
   507  				required = false
   508  				break
   509  			}
   510  		}
   511  	}
   512  	if required {
   513  		return !(value == "")
   514  	} else {
   515  		return true
   516  	}
   517  }
   518  
   519  // 对字段值长度进行检测
   520  func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
   521  	msg := ""
   522  	switch ruleKey {
   523  	// 长度范围
   524  	case "length":
   525  		array := strings.Split(ruleVal, ",")
   526  		min := 0
   527  		max := 0
   528  		if len(array) > 0 {
   529  			if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil {
   530  				min = v
   531  			}
   532  		}
   533  		if len(array) > 1 {
   534  			if v, err := strconv.Atoi(strings.TrimSpace(array[1])); err == nil {
   535  				max = v
   536  			}
   537  		}
   538  		if len(value) < min || len(value) > max {
   539  			if v, ok := customMsgMap[ruleKey]; !ok {
   540  				msg = errorMsgMap.Get(ruleKey)
   541  			} else {
   542  				msg = v
   543  			}
   544  			msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
   545  			msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
   546  			return msg
   547  		}
   548  
   549  	// 最小长度
   550  	case "min-length":
   551  		if min, err := strconv.Atoi(ruleVal); err == nil {
   552  			if len(value) < min {
   553  				if v, ok := customMsgMap[ruleKey]; !ok {
   554  					msg = errorMsgMap.Get(ruleKey)
   555  				} else {
   556  					msg = v
   557  				}
   558  				msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
   559  			}
   560  		} else {
   561  			msg = "校验参数[" + ruleVal + "]应当为整数类型"
   562  		}
   563  
   564  	// 最大长度
   565  	case "max-length":
   566  		if max, err := strconv.Atoi(ruleVal); err == nil {
   567  			if len(value) > max {
   568  				if v, ok := customMsgMap[ruleKey]; !ok {
   569  					msg = errorMsgMap.Get(ruleKey)
   570  				} else {
   571  					msg = v
   572  				}
   573  				msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
   574  			}
   575  		} else {
   576  			msg = "校验参数[" + ruleVal + "]应当为整数类型"
   577  		}
   578  	}
   579  	return msg
   580  }
   581  
   582  // 对字段值大小进行检测
   583  func checkSize(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
   584  	msg := ""
   585  	switch ruleKey {
   586  	// 大小范围
   587  	case "between":
   588  		array := strings.Split(ruleVal, ",")
   589  		min := float64(0)
   590  		max := float64(0)
   591  		if len(array) > 0 {
   592  			if v, err := strconv.ParseFloat(strings.TrimSpace(array[0]), 10); err == nil {
   593  				min = v
   594  			}
   595  		}
   596  		if len(array) > 1 {
   597  			if v, err := strconv.ParseFloat(strings.TrimSpace(array[1]), 10); err == nil {
   598  				max = v
   599  			}
   600  		}
   601  		if v, err := strconv.ParseFloat(value, 10); err == nil {
   602  			if v < min || v > max {
   603  				if v, ok := customMsgMap[ruleKey]; !ok {
   604  					msg = errorMsgMap.Get(ruleKey)
   605  				} else {
   606  					msg = v
   607  				}
   608  				msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
   609  				msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
   610  			}
   611  		} else {
   612  			msg = "输入参数[" + value + "]应当为数字类型"
   613  		}
   614  
   615  	// 最小值
   616  	case "min":
   617  		if min, err := strconv.ParseFloat(ruleVal, 10); err == nil {
   618  			if v, err := strconv.ParseFloat(value, 10); err == nil {
   619  				if v < min {
   620  					if v, ok := customMsgMap[ruleKey]; !ok {
   621  						msg = errorMsgMap.Get(ruleKey)
   622  					} else {
   623  						msg = v
   624  					}
   625  					msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
   626  				}
   627  			} else {
   628  				msg = "输入参数[" + value + "]应当为数字类型"
   629  			}
   630  		} else {
   631  			msg = "校验参数[" + ruleVal + "]应当为数字类型"
   632  		}
   633  
   634  	// 最大值
   635  	case "max":
   636  		if max, err := strconv.ParseFloat(ruleVal, 10); err == nil {
   637  			if v, err := strconv.ParseFloat(value, 10); err == nil {
   638  				if v > max {
   639  					if v, ok := customMsgMap[ruleKey]; !ok {
   640  						msg = errorMsgMap.Get(ruleKey)
   641  					} else {
   642  						msg = v
   643  					}
   644  					msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
   645  				}
   646  			} else {
   647  				msg = "输入参数[" + value + "]应当为数字类型"
   648  			}
   649  		} else {
   650  			msg = "校验参数[" + ruleVal + "]应当为数字类型"
   651  		}
   652  	}
   653  	return msg
   654  }