package common import ( "crypto/hmac" "crypto/md5" crand "crypto/rand" "crypto/sha1" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/gogf/gf/util/gconv" "github.com/syyongx/php2go" "github.com/tidwall/gjson" "golang_open_platform/pkg/config" "golang_open_platform/pkg/vars" "log" "math" "math/big" "math/rand" "os" "reflect" "regexp" "sort" "strconv" "strings" "time" "unicode" ) /* gjson 判断某值是否存在 map @param json string 分析json字符串 @param arrk string 分析的json键值转成map @param targetk string 目标ID */ func GInArray(json string, arrK string, targetK string) bool { s := gjson.Get(json, arrK).Array() d := make([]string, 0) for _, a := range s { c := a.String() d = append(d, c) } return php2go.InArray(targetK, d) } /* sha1加密(tme目前在用) @param string query 查询字符串 @param string pri_key 加密字符串 @return 返回加密后的字符串 */ func Sha1(query string, pri_key string) string { key := []byte(pri_key) mac := hmac.New(sha1.New, key) mac.Write([]byte(query)) query = base64.StdEncoding.EncodeToString(mac.Sum(nil)) return query } /** @author wangsong 获取请求过来的参数,并转为字符串,作用:为了有时候需要记录请求参数,压入日志 注意: 1.如果请求的是json,接收是需要读取body,而gin大部分函数只能读取一次body,读第二次会读不到 而此函数需要频繁调用,所以需要在调用此函数之前,使用过ShouldBindBodyWith 方法,不然会读取不到。 具体需了解 ShouldBindBodyWith 源码 和 多次使用shouldbindjsond 的问题 2.表单提交不需要读body,则正常使用 */ func GetRequestParam(ctx *gin.Context) string { ct := ctx.Request.Header.Get("Content-Type") switch { case ct == "application/json": var BodyJson string if cb, ok := ctx.Get(gin.BodyBytesKey); ok { if cbb, ok := cb.([]byte); ok { BodyJson = string(cbb) return BodyJson } } return BodyJson default: ctx.Request.ParseMultipartForm(32 << 20) postForm := ctx.Request.Form formData, _ := json.Marshal(postForm) params := string(formData) return params /*ctx.Request.ParseForm() postForm := ctx.Request.PostForm formData, _ := json.Marshal(postForm) params := string(formData) return params*/ } } /** 结构复制,从struct1 复制到 struct2,字段类型不一样,那么就不复制那个字段 @param Starus 说明 1,如果struct1 字段值为零值就不覆盖struct2对应的字段值(默认), 2,struct1字段 覆盖struct2 字段的值 3,struct2 字段为零值,才让struct1 的字段赋值 */ func CopyStruct(struct1 interface{}, struct2 interface{}, Status ...int) { statusType := 1 //statusType 默认参数 for num, opt := range Status { if num > 0 { //只接受一个参数 break } switch num { case 0: if opt == 2 { statusType = 2 } break } } sval := reflect.ValueOf(struct1).Elem() dval := reflect.ValueOf(struct2).Elem() for i := 0; i < sval.NumField(); i++ { value := sval.Field(i) if statusType == 1 { //如果struct1 字段值为零值就不覆盖struct2对应的字段值 if value.IsZero() { continue } } name := sval.Type().Field(i).Name dvalue := dval.FieldByName(name) if statusType == 3 { //struct2字段为零值,才让struct1 的字段赋值,不是零值就跳过此字段的赋值 if dvalue.IsZero() == false { continue } } if(name=="LadderPrice"){ if dvalue.Kind() != sval.Field(i).Kind() { //类型不一样不复制 continue } } if dvalue.Kind() != sval.Field(i).Kind() { //类型不一样不复制 continue } if dvalue.IsValid() == false { //dvalue.IsValid() 与 dvalue.IsZero() 不一样,如果返回fasle是无效的值 continue } dvalue.Set(value) //这里默认共同成员的类型一样,否则这个地方可能导致 panic,需要简单修改一下。 } } /** @author wangsong 合并map,都有值以map1为主,字段类型以map1为主 */ func MergeMap(map1 map[string]interface{}, map2 map[string]interface{}) map[string]interface{} { var newMap map[string]interface{} newMap = map1 //以map1为主,就先将map1给到newMap //先算mp1 for key, _ := range map1 { if !reflect.ValueOf(map1[key]).IsZero() { //如果不为空,就以取map1的值 s := reflect.ValueOf(map1[key]) if s.Kind() == reflect.String { //string newMap[key] = gconv.String(map1[key]) } else { newMap[key] = gconv.Int(map1[key]) //int } delete(map2, key) //删一个重复的map2的key } else { //没有数据,取mp2的值,类型要是map1字段的类型 if _, ok := map2[key]; ok { //map1的key在map2是否存在,不存在就不处理 if !reflect.ValueOf(map2[key]).IsZero() { //map2不能为0值才处理 m1 := reflect.ValueOf(map1[key]) if m1.Kind() == reflect.String { //string newMap[key] = gconv.String(map2[key]) } else { newMap[key] = gconv.Int(map2[key]) //int } } delete(map2, key) //删一个重复的map2的key } } } //处理map2,重复的被删了,剩下的直接添加 for key, value := range map2 { s := reflect.ValueOf(value) if s.Kind() == reflect.String { //string newMap[key] = gconv.String(value) } else { newMap[key] = gconv.Int(value) //int } } return newMap } /* 兼容表单提交,不适用嵌套数组表单提交 */ func ShouldBind(ctx *gin.Context, obj interface{}) error{ ct := ctx.Request.Header.Get("Content-Type") switch { case ct == "application/json": if err := ctx.ShouldBindBodyWith(obj,binding.JSON);err != nil { return err } default: if err := ctx.ShouldBind(obj);err != nil { return err } } return nil } /* 输出header */ func PrintDebugHeader(ctx *gin.Context) { if ctx.Request.FormValue("flag") == "101" { ctx.Header("Content-Type", "text/html; charset=utf-8") } } /* 格式化数据直接输出浏览器 @parm jsonStr 需要json输出的内容 */ func PrintDebugHtml(ctx *gin.Context, jsonStr interface{}) { if ctx.Request.FormValue("flag") != "101" || jsonStr == "" { return } if v, p := jsonStr.(string); p { ctx.String(200, "</br></br>-----------"+v) } else { jsonData, err := json.Marshal(jsonStr) if err != nil { fmt.Println("错误:-----", err) } ctx.String(200, "</br></br>-----------"+string(jsonData)) } } func PrintDebugString(msg string,arg ...interface{}) { if(config.IsOpenDebug()){ logOut := log.New(os.Stdout, "", log.Lshortfile|log.Ldate|log.Ltime) if(len(arg)>0){ logOut.Printf(msg,arg) }else{ logOut.Printf(msg) } } } /** 打印文本到控制台 common.PrintStdout().Printf("新增到redis") */ func PrintStdout() *log.Logger { return log.New(os.Stdout, "", log.Lshortfile|log.Ldate|log.Ltime) } // Md5 md5() func Md5(str string) string { hash := md5.New() hash.Write([]byte(str)) return hex.EncodeToString(hash.Sum(nil)) } //非精确匹配,字符串截取向下80%,5个字符以下不截,5-10个截一个,10-20个向下取80%,20个以上向下取70% func SubKeyWordStr(str string) string { strLen := len(str) strLenFloat := float64(strLen) if strLen < 5 { return str } if strLen >= 5 && strLen <= 10 { str = php2go.Substr(str, 0, strLen-1) } if strLen > 10 && strLen <= 20 { num := php2go.Floor(strLenFloat * 0.2) str = php2go.Substr(str, 0, strLen-int(num)) } if strLen > 20 { num := php2go.Floor(strLenFloat * 0.3) str = php2go.Substr(str, 0, strLen-int(num)) } return str } //转成字符串的方法 func ToString(a interface{}) string { if v, p := a.(int); p { return strconv.Itoa(v) } if v, p := a.(int16); p { return strconv.Itoa(int(v)) } if v, p := a.(int32); p { return strconv.Itoa(int(v)) } if v, p := a.(uint); p { return strconv.Itoa(int(v)) } if v, p := a.(float32); p { return strconv.FormatFloat(float64(v), 'f', -1, 32) } if v, p := a.(float64); p { return strconv.FormatFloat(v, 'f', -1, 32) } return "change to String error" } //替换字符串,不区分大小写 func CaseInsensitiveReplace(subject string, search string, replace string) string { searchRegex := regexp.MustCompile("(?i)" + search) return searchRegex.ReplaceAllString(subject, replace) } /* md5字符串 */ func GetKey(s string) string { return php2go.Md5(strings.ToLower(s)) } /** * 获取联营活动价 */ func lyActivityPrice() { } /*构造SPU和SKU的ID *生成19位纯数组id */ func CreateId(types string) string { var id, db string if types == "sku" { id = "1" db = strconv.Itoa(php2go.Rand(0, 9)) + strconv.Itoa(php2go.Rand(0, 9)) } else { id = "2" db = "0" + strconv.Itoa(php2go.Rand(0, 9)) } s := rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000) return id + strconv.FormatInt(int64(s), 10) + strconv.FormatInt(php2go.Time(), 10) + db } /* * 反爬虫用html标签替换数字,不包括“.” * number 数字串 */ func NumberToHtml(number int) (html string) { numberToClassSlice := vars.NumberToClass if number != 0 { //数字转字符串 numberStr := ToString(number) for i := 0; i < len(numberStr); i++ { var classHtml string numStr := php2go.Substr(numberStr, uint(i), 1) if php2go.IsNumeric(numStr) { num, _ := strconv.Atoi(numStr) index := php2go.Rand(0, 3) class := numberToClassSlice[num][index] otherClass := StrRandom(3) classHtml = `<font class="` + class + otherClass + `"></font>` } html = html + classHtml return } } return } /** * 生成纯小写字母的字符串 * 返回形式如下 : yuiopkdsi rnvewjeil xmiqplmza */ func StrRandom(lenNum int) string { randStr := "sdwpkxmiqplmzacbmeruwulurjlauejrifkfghjklzxcvbnmqwwertyuiopkdsieurnvewjeilweiskvnx" strLen := len(randStr) - 9 var result string for i := 0; i < lenNum; i++ { start := php2go.Rand(0, strLen) str := php2go.Substr(randStr, uint(start), 9) result = result + " " + str } return result } //生成范围区间内的随机数 func Rand(min, max int) int { n, _ := crand.Int(crand.Reader, big.NewInt(int64(max+1))) return int(n.Int64()) + min } func CopyMapString(distmap map[string]string) map[string]string { tmpmap := make(map[string]string, 0) for k, v := range distmap { tmpmap[k] = v } return tmpmap } //反爬虫加密验证 func CheckSignApi(ctx *gin.Context) (resNo int) { params := make(map[string]string) if ctx == nil { return } referer := ctx.Request.Referer() request := make(map[string]string) ctx.MultipartForm() for name, value := range ctx.Request.Form { if value[0] != "" { request[name] = strings.TrimSpace(value[0]) } } if request["hcy_test"] == "1122" { return 0 } if request["no_rule"] == "1122" { return 0 } if request["check_button"] == "2" || strings.Contains(ctx.Request.Referer(), "liexin.com") { return 0 } //如果是内部验证通过,则内部通过 if auth(ctx) { return 0 } //验证必填参数 if referer == "" { return 4 } if ctx.Query("asdfghjkl") != "" { params = request } if ctx.PostForm("asdfghjkl") != "" { params = request } params["Yo4teW_gid"], _ = ctx.Cookie("Yo4teW_gid") if params["asdfghjkl"] == "" || params["Yo4teW_gid"] == "" || params["qwertyuiop"] == "" { return 1 } var temp1 []string //根据参数,按照规则进行签名生成 for k, v := range params { if k == "asdfghjkl" || k == "_" || k == "callback" { continue } if v == "" || v == "undefined" || v == "null" || v == "NULL" { continue } if len(v) <= 0 { continue } temp1 = append(temp1, k+"="+v) } SortSlice(temp1) temp2 := strings.Join(temp1, "") r := regexp.MustCompile(`[^0-9a-zA-Z]`) temp2 = r.ReplaceAllString(temp2, "") temp2 = strings.ToUpper(temp2) r = regexp.MustCompile(`[ABC]`) temp2 = r.ReplaceAllString(temp2, "") temp3 := php2go.Sha1(temp2) //'YO4TEW_GID%3DEFDDE3E06D15F887866E9D96DOM_RNK%3D1GOODS_NME%2FONDITION%3DLM358P%3D1PF%3D1TIME_LIEXIN%3D1545009990450'; //验证签名 if temp3 == params["asdfghjkl"] { return 0 } return 3 } //内部免验证通过 func auth(ctx *gin.Context) bool { if ctx == nil { return false } request := make(map[string]string) ctx.MultipartForm() for name, value := range ctx.Request.Form { if value[0] != "" { request[name] = strings.TrimSpace(value[0]) } } k1 := request["k1"] k1Num, _ := strconv.Atoi(k1) k2 := request["k2"] key := config.Get("AUTH.SUPER_AUTH_KEY").String() if k1 != "" && int64(k1Num) < (time.Now().Unix()-600) { return false } //md5(md5($pwd).$salt); pwd := Md5(Md5(k1) + key) if k2 == pwd { return true } return false } func CreateAnyTypeSlice(slice interface{}) ([]interface{}, bool) { val, ok := isSlice(slice) if !ok { return nil, false } sliceLen := val.Len() out := make([]interface{}, sliceLen) for i := 0; i < sliceLen; i++ { out[i] = val.Index(i).Interface() } return out, true } // 判断是否为slcie数据 func isSlice(arg interface{}) (val reflect.Value, ok bool) { val = reflect.ValueOf(arg) if val.Kind() == reflect.Slice { ok = true } return } //向左补充字符串 func StrPadLeft(input string, padLength int, padString string) string { output := padString for padLength > len(output) { output += output } if len(input) >= padLength { return input } return output[:padLength-len(input)] + input } //移除字符串切片中某個元素 func RemoveSliceString(s []string, i string) []string { b := make([]string, 0) for _, v := range s { if v != i { b = append(b, v) } } return b } //map排序 func MapSort(mapList map[int]int) []int { var ( keys []int newList []int ) newList = make([]int, 0) for _, v := range mapList { keys = append(keys, v) } sort.Ints(keys) for _, k := range keys { newList = append(newList, mapList[k]) } return newList } ////////////类型转换///////////////////// func MyInt(str string) int { res, _ := strconv.ParseInt(str, 10, 64) return int(res) } func MyInt8(str string) int8 { res, _ := strconv.ParseInt(str, 10, 64) return int8(res) } func MyInt16(str string) int16 { res, _ := strconv.ParseInt(str, 10, 64) return int16(res) } func MyInt64(str string) int64 { res, _ := strconv.ParseInt(str, 10, 64) return int64(res) } func MyFloat32(str string) float32 { res, _ := strconv.ParseFloat(str, 64) return float32(res) } func MyFloat64(str string) float64 { res, _ := strconv.ParseFloat(str, 64) return float64(res) } func MyFloat64ToStr(numb float64) string { return strconv.FormatFloat(numb, 'f', 6, 64) } func MyInt64ToStr(numb int64) string { return strconv.FormatInt(numb, 10) } func MyIntToStr(i int) string { return strconv.Itoa(i) } /* 四舍五入取多少位 @param float64 x 目标分析字符串 @param int wei 取小数多少位 */ func MyRound(x float64, wei int) float64 { if wei == 0 { return math.Floor(x + 0.5) } weis := map[int]float64{ 1: 10, 2: 100, 3: 1000, 4: 10000, 5: 100000, 6: 1000000, } weishu := weis[wei] a := math.Floor(x*weishu+0.5) / weishu return a } /* @author wangsong 保留小数点几位(四舍五入) @param float64 val 目标分析字符串 @param int precision 取小数多少位 */ func Round(val float64, precision int) (valueFloat64 float64) { //strconv.ParseFloat(fmt.Sprintf("%.2f", 12.0), 64) //两位 format:="%."+strconv.Itoa(precision)+"f" valueFloat64,err:= strconv.ParseFloat(fmt.Sprintf(format, val), 64) if(err!=nil){ panic("round err"+err.Error()) } return } /** 判断是否包含中文 */ func IsChineseChar(str string) bool { for _, r := range str { if unicode.Is(unicode.Scripts["Han"], r) || (regexp.MustCompile("[\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b]").MatchString(string(r))) { return true } } return false } ////////////类型转换/////////////////////