diff --git a/g/net/ghttp/http_client_request.go b/g/net/ghttp/http_client_request.go index a1b929b7a..015e1a037 100644 --- a/g/net/ghttp/http_client_request.go +++ b/g/net/ghttp/http_client_request.go @@ -51,14 +51,14 @@ func (r *ClientRequest) GetQueryArray(k string) []string { } // 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -func (r *ClientRequest) GetQueryMap(defaultMap map[string][]string) map[string][]string { - m := make(map[string][]string) +func (r *ClientRequest) GetQueryMap(defaultMap map[string]string) map[string]string { + m := make(map[string]string) for k, v := range defaultMap { v2 := r.GetQueryArray(k) if v2 == nil { m[k] = v } else { - m[k] = v2 + m[k] = v2[0] } } return m @@ -105,11 +105,12 @@ func (r *ClientRequest) GetPostArray(k string) []string { } // 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -func (r *ClientRequest) GetPostMap(defaultMap map[string][]string) map[string][]string { - m := make(map[string][]string) +// 需要注意的是,如果其中一个字段为数组形式,那么只会返回第一个元素,如果需要获取全部的元素,请使用GetPostArray获取特定字段内容 +func (r *ClientRequest) GetPostMap(defaultMap map[string]string) map[string]string { + m := make(map[string]string) for k, v := range defaultMap { if v2, ok := r.PostForm[k]; ok { - m[k] = v2 + m[k] = v2[0] } else { m[k] = v } @@ -146,12 +147,13 @@ func (r *ClientRequest) GetRequestArray(k string) []string { } // 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -func (r *ClientRequest) GetRequestMap(defaultMap map[string][]string) map[string][]string { - m := make(map[string][]string) +// 需要注意的是,如果其中一个字段为数组形式,那么只会返回第一个元素,如果需要获取全部的元素,请使用GetRequestArray获取特定字段内容 +func (r *ClientRequest) GetRequestMap(defaultMap map[string]string) map[string]string { + m := make(map[string]string) for k, v := range defaultMap { v2 := r.GetRequest(k) if v2 != nil { - m[k] = v2 + m[k] = v2[0] } else { m[k] = v } diff --git a/g/os/gtime/time.go b/g/os/gtime/time.go index 2342df977..e3d1d4a3b 100644 --- a/g/os/gtime/time.go +++ b/g/os/gtime/time.go @@ -2,6 +2,7 @@ package gtime import ( "time" + "fmt" ) // 类似与js中的SetTimeout,一段时间后执行回调函数 @@ -55,3 +56,17 @@ func Date() string { func Datetime() string { return time.Now().Format("2006-01-02 15:04:05") } + +// 时间戳转换为指定格式的字符串,format格式形如:2006-01-02 03:04:05 PM +func Format(timestamp int64, format string) string { + return time.Unix(timestamp, 0).Format(format) +} + +// 字符串转换为时间戳,需要给定字符串时间格式,format格式形如:2006-01-02 03:04:05 PM +func StrToTime(timestr string, format string) (int64, error) { + t, err := time.Parse(format, timestr) + if err != nil { + return 0, err + } + return t.Unix(), nil +} diff --git a/g/util/gvalid/gvalid.go b/g/util/gvalid/gvalid.go new file mode 100644 index 000000000..d7404369a --- /dev/null +++ b/g/util/gvalid/gvalid.go @@ -0,0 +1,380 @@ +// 通用数据验证工具 +// 本来打算取名gvalidator的,名字太长了,缩写一下 +package gvalid + +import ( + "strings" + "regexp" + "strconv" + "gitee.com/johng/gf/g/os/gtime" + "gitee.com/johng/gf/g/util/gregx" + "gitee.com/johng/gf/g/encoding/gjson" +) + +// 默认规则校验错误消息(可以通过接口自定义错误消息) +var defaultMessages = map[string]string { + "required" : "字段不能为空", + "required_if" : "字段不能为空", + "required_with" : "字段不能为空", + "required_with_all" : "字段不能为空", + "date" : "日期格式不正确", + "email" : "邮箱地址格式不正确", + "phone" : "手机号码格式不正确", + "telephone" : "电话号码格式不正确", + "passport" : "账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间", + "password" : "密码格式不合法,密码格式为任意6-18位的可见字符", + "password2" : "密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字", + "password3" : "密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符", + "postcode" : "邮政编码不正确", + "id_number" : "身份证号码不正确", + "qq" : "QQ号码格式不正确", + "ip" : "IP地址格式不正确", + "mac" : "MAC地址格式不正确", + "url" : "URL地址格式不正确", + "length" : "字段长度为:min到:max个字符", + "min_length" : "字段最小长度为:min", + "max_length" : "字段最大长度为:max", + "between" : "字段大小为:min到:max", + "min" : "字段最小值为:min", + "max" : "字段最大值为:max", + "json" : "字段应当为JSON格式", + "xml" : "字段应当为XML格式", + "array" : "字段应当为数组", + "integer" : "字段应当为整数", + "float" : "字段应当为浮点数", + "boolean" : "字段应当为布尔值", + "same" : "字段值不合法", + "different" : "字段值不合法", + "in" : "字段值不合法", + "not_in" : "字段值不合法", + "regex" : "字段值不合法", +} + +// 检测一条数据的规则,其中values参数为非必须参数,可以传递所有的校验参数进来,进行多参数对比(部分校验规则需要) +func CheckRule(value, rule string, values...map[string]string) map[string]string { + msgs := make(map[string]string) + params := make(map[string]string) + if len(values) > 0 { + params = values[0] + } + items := strings.Split(strings.TrimSpace(rule), "|") + for _, item := range items { + reg, _ := regexp.Compile(`^(\w+):{0,1}(.*)`) + results := reg.FindStringSubmatch(item) + rulekey := results[1] + ruleval := results[2] + match := false + switch rulekey { + // 必须字段 + case "required": + match = !(value == "") + break + + //// 必须字段(当给定字段值与所给任意值相等时) + //case "required_if": + // array := strings.Split(strings.TrimSpace(ruleval), ",") + // for _, v := range array { + // if strings.Compare(value, strings.TrimSpace(v)) == 0 { + // match = true + // break + // } + // } + // break + // + //// 必须字段(当所给定任意字段值不为空时) + //case "required_with": + // $ruleMatch = false; + // $tmpArray = explode(',', $ruleAttr); + // foreach ($tmpArray as $v) { + //if (!empty(self::$_currentData[$v])) { + //$ruleMatch = true; + //break + //} + //} + // break + // + // // 必须字段(当所给定所有字段值都不为空时) + //case "required_with_all": + // $tmpArray = explode(',', $ruleAttr); + // foreach ($tmpArray as $v) { + //if (empty(self::$_currentData[$v])) { + //$ruleMatch = false; + //break + //} + //} + // break + + // 日期格式,需要给定日期格式 + case "date": + if _, err := gtime.StrToTime(value, ruleval); err == nil { + match = true + } + break + + // // 两字段值应相同(非敏感字符判断,非类型判断) + //case "same": + // $ruleMatch = (isset(self::$_currentData[$ruleAttr]) && $value == self::$_currentData[$ruleAttr]); + // break + // + // // 两字段值不应相同(非敏感字符判断,非类型判断) + //case "different": + // $ruleMatch = (!isset(self::$_currentData[$ruleAttr]) || $value != self::$_currentData[$ruleAttr]); + // break + + // 字段值应当在指定范围中 + case "in": + array := strings.Split(strings.TrimSpace(ruleval), ",") + for _, v := range array { + if strings.Compare(value, strings.TrimSpace(v)) == 0 { + match = true + break + } + } + break + + // 字段值不应当在指定范围中 + case "not_in": + match = true + array := strings.Split(strings.TrimSpace(ruleval), ",") + for _, v := range array { + if strings.Compare(value, strings.TrimSpace(v)) == 0 { + match = false + break + } + } + break + + // 自定义正则判断 + //case "regex": + // $ruleMatch = @preg_match($ruleAttr, $value) ? true : false; + // break + + /* + * 验证所给手机号码是否符合手机号的格式. + * 移动:134、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、178(4G)、147(上网卡); + * 联通:130、131、132、155、156、185、186、176(4G)、145(上网卡)、175; + * 电信:133、153、180、181、189 、177(4G); + * 卫星通信: 1349 + * 虚拟运营商:170、173 + */ + case "phone": + match = gregx.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^17[0,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$`, value) + break + + /* + * 国内座机电话号码:"XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX" + */ + case "telephone": + match = gregx.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, value) + break + + // 腾讯QQ号,从10000开始 + case "qq": + match = gregx.IsMatchString(`^[1-9][0-9]{4,}$`, value) + break + + // 中国邮政编码 + case "postcode": + match = gregx.IsMatchString(`^[1-9]\d{5}$`, value) + break + + /* + 公民身份证号 + xxxxxx yyyy MM dd 375 0 十八位 + xxxxxx yy MM dd 75 0 十五位 + + 地区:[1-9]\d{5} + 年的前两位:(18|19|([23]\d)) 1800-2399 + 年的后两位:\d{2} + 月份:((0[1-9])|(10|11|12)) + 天数:(([0-2][1-9])|10|20|30|31) 闰年不能禁止29+ + + 三位顺序码:\d{3} + 两位顺序码:\d{2} + 校验码: [0-9Xx] + + 十八位:^[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{2}$ + + 总: + (^[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{2}$) + */ + case "id_number": + match = gregx.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{2}$)`, value) + break + + // 通用帐号规则(字母开头,只能包含字母、数字和下划线,长度在6~18之间) + case "passport": + match = gregx.IsMatchString(`^[a-zA-Z]{1}\w{5,17}$`, value) + break + + // 通用密码(任意可见字符,长度在6~18之间) + case "password": + match = gregx.IsMatchString(`^[\w\S]{6,18}$`, value) + break + + // 中等强度密码(在弱密码的基础上,必须包含大小写字母和数字) + case "password2": + if gregx.IsMatchString(`^[\w\S]{6,18}$`, value) && gregx.IsMatchString(`[a-z]+`, value) && gregx.IsMatchString(`[A-Z]+`, value) && gregx.IsMatchString(`\d+`, value) { + match = true + } + break + + // 强等强度密码(在弱密码的基础上,必须包含大小写字母、数字和特殊字符) + case "password3": + if gregx.IsMatchString(`^[\w\S]{6,18}$`, value) && gregx.IsMatchString(`[a-z]+`, value) && gregx.IsMatchString(`[A-Z]+`, value) && gregx.IsMatchString(`\d+`, value) && gregx.IsMatchString(`\S+`, value) { + match = true + } + break + + // 长度范围 + case "length": + array := strings.Split(strings.TrimSpace(ruleval), ",") + min := 0 + max := 0 + if len(array) > 0 { + if v, err := strconv.Atoi(array[0]); err == nil { + min = v + } + } + if len(array) > 1 { + if v, err := strconv.Atoi(array[1]); err == nil { + max = v + } + } + if len(value) >= min && len(value) <= max { + match = true + } + break + + // 最小长度 + case "min_length": + if min, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil { + if len(value) >= min { + match = true + } + } + break + + // 最大长度 + case "max_length": + if max, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil { + if len(value) <= max { + match = true + } + } + break + + // 大小范围 + case "between": + array := strings.Split(strings.TrimSpace(ruleval), ",") + min := 0 + max := 0 + if len(array) > 0 { + if v, err := strconv.Atoi(array[0]); err == nil { + min = v + } + } + if len(array) > 1 { + if v, err := strconv.Atoi(array[1]); err == nil { + max = v + } + } + if v, err := strconv.Atoi(value); err == nil { + if v >= min && v <= max { + match = true + } + } + break + + // 最小值 + case "min": + if min, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil { + if v, err := strconv.Atoi(value); err == nil { + if v >= min { + match = true + } + } + } + break + + // 最大值 + case "max": + if max, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil { + if v, err := strconv.Atoi(value); err == nil { + if v <= max { + match = true + } + } + } + break + + // json + case "json": + if _, err := gjson.Decode([]byte(value)); err == nil { + match = true + } + break + + //// xml + //case "xml": + // $checkResult = @Lib_XmlParser::isXml($value); + // $ruleMatch = ($checkResult !== null && $checkResult !== false); + // break + + // 整数 + case "integer": + if _, err := strconv.Atoi(value); err == nil { + match = true + } + break + + // 小数 + case "float": + if _, err := strconv.ParseFloat(value, 10); err == nil { + match = true + } + break + + // 布尔值(1,true,on,yes:true | 0,false,off,no,"":false) + case "boolean": + if value != "" && value != "0" && value != "false" && value != "off" && value != "no" { + match = true + } + break + + // 邮件 + case "email": + match = gregx.IsMatchString(`^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$`, value) + break + + // URL + case "url": + match = gregx.IsMatchString(`^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$`, value) + break + + // IP + case "ip": + match = gregx.IsMatchString(`^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$`, value) + break + + // MAC地址 + case "mac": + match = gregx.IsMatchString(`^([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}$`, value) + break + + default: + msgs[rulekey] = "Invalid rule name:" + rulekey + break + } + + // 错误消息整合 + if !match { + msgs[rulekey] = defaultMessages[rulekey] + } + } + if len(msgs) > 0 { + return msgs + } + return nil +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index fd44789a5..0608a9655 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,12 +1,11 @@ package main import ( - "gitee.com/johng/gf/g/util/gregx" "fmt" + "gitee.com/johng/gf/g/util/gregx" ) func main() { - s, _ := gregx.Replace(`\w`, []byte("/user/list/page/2"), []byte("-")) - fmt.Println(string(s)) + fmt.Println(gregx.IsMatchString(`^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$`, "joh-n_cn@johng.cn")) } \ No newline at end of file