package util import ( "bufio" "container/list" "fmt" "io" "math" "math/rand" "net/http" "os" "reflect" "regexp" "sort" "strconv" "strings" "time" "github.com/liangdas/mqant/log" "github.com/mitchellh/mapstructure" ) // FormatFloat 取小数点后n位小数,四舍五入 func FormatFloat(num float64, decimal int) string { if math.Trunc(num) == num || decimal == 0 { return fmt.Sprintf("%.f", math.Trunc(num)) } format := "%." + strconv.Itoa(decimal) + "f" return fmt.Sprintf(format, num) } // RoundFloat 取小数点后n位非零小数 func RoundFloat(num float64, decimal int) string { // 默认乘1 d := float64(1) if decimal > 0 { // 10的N次方 d = math.Pow10(decimal) } // math.trunc作用就是返回浮点数的整数部分 // 再除回去,小数点后无效的0也就不存在了 return strconv.FormatFloat(math.Trunc(num*d)/d, 'f', -1, 64) } // Decimal 保留x位小数,返回float64 func Decimal(value float64, deci int) float64 { value, _ = strconv.ParseFloat(fmt.Sprintf("%."+strconv.Itoa(deci)+"f", value), 64) return value } func GetInt(data interface{}) int { switch data := data.(type) { case int: return data case int64: return int(data) case float32: return int(data) case float64: return int(data) case string: ret, _ := strconv.Atoi(data) return ret default: return 0 } } func GetInt64(data interface{}) int64 { switch data := data.(type) { case int: return int64(data) case int64: return data case float32: return int64(data) case float64: return int64(data) case string: ret, _ := strconv.ParseInt(data, 10, 64) return ret default: return 0 } } // CheckFileIsExist 判断文件是否存在 func CheckFileIsExist(filename string) bool { var exist = true if _, err := os.Stat(filename); os.IsNotExist(err) { exist = false } return exist } func StructToMap(obj interface{}, tag string) map[string]interface{} { t := reflect.TypeOf(obj) v := reflect.ValueOf(obj) if v.Kind() == reflect.Ptr { t = t.Elem() v = v.Elem() } var result = make(map[string]interface{}) for i := 0; i < t.NumField(); i++ { tagName := t.Field(i).Tag.Get(tag) if tagName != "" && tagName != "-" { result[tagName] = v.Field(i).Interface() } } return result } func StructToMapJson(obj interface{}) map[string]interface{} { t := reflect.TypeOf(obj) v := reflect.ValueOf(obj) if t.Kind() == reflect.Ptr { t = t.Elem() v = v.Elem() } var result = make(map[string]interface{}) for i := 0; i < t.NumField(); i++ { if v.Field(i).Kind() == reflect.Ptr && v.Field(i).IsNil() { continue } tagName := t.Field(i).Tag.Get("json") if tagName != "" && tagName != "-" && !v.Field(i).IsZero() { result[tagName] = v.Field(i).Interface() } } return result } // StructToMapJsonAll 非空字段也包括 func StructToMapJsonAll(obj interface{}) map[string]interface{} { t := reflect.TypeOf(obj) v := reflect.ValueOf(obj) if t.Kind() == reflect.Ptr { t = t.Elem() v = v.Elem() } var result = make(map[string]interface{}) for i := 0; i < t.NumField(); i++ { if v.Field(i).Kind() == reflect.Ptr && v.Field(i).IsNil() { continue } tagName := t.Field(i).Tag.Get("json") if tagName != "" && tagName != "-" { result[tagName] = v.Field(i).Interface() } } return result } func Map2Struct(obj map[string]interface{}, result interface{}) error { return mapstructure.Decode(obj, result) } // Go 用协程处理f func Go(f func()) { go func() { defer Recover() f() }() } // RandomLetters 随机26位不重复的字符串 func RandomLetters() string { a := rand.Perm(26) s := []byte{} for _, v := range a { s = append(s, byte(97+v)) } return string(s) } var ( characters52 = [52]byte{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', } ) func GenerateRandomString(size int) string { raw := make([]byte, size) for i := 0; i < size; i++ { raw[i] = characters52[rand.Intn(52)] } return string(raw) } // StrFirstToUpper 首字母大写 func StrFirstToUpper(str string) string { if len(str) < 1 { return "" } strArry := []byte(str) if strArry[0] >= 97 && strArry[0] <= 122 { strArry[0] -= 32 } return string(strArry) } // DecodeRedisMap 解析map到目标t中,目前只实现了部分方法,如果需要解析更复杂结构需实现 func DecodeRedisMap(m map[string]string, t interface{}) (err error) { val := reflect.ValueOf(t).Elem() for i := 0; i < val.NumField(); i++ { f := val.Type().Field(i) fi := val.Field(i) name := f.Name if s, ok := m[name]; ok { switch f.Type.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: ret, err := strconv.ParseInt(s, 10, 64) if err != nil { return err } fi.SetInt(ret) case reflect.Float32, reflect.Float64: ret, err := strconv.ParseFloat(s, 64) if err != nil { return err } fi.SetFloat(ret) case reflect.String: fi.SetString(s) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: ret, err := strconv.ParseUint(s, 10, 64) if err != nil { return err } fi.SetUint(ret) case reflect.Bool: ret, err := strconv.ParseBool(s) if err != nil { return err } fi.SetBool(ret) default: fmt.Println(t) continue } } } return nil } /* 判断文件或文件夹是否存在 如果返回的错误为nil,说明文件或文件夹存在 如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在 如果返回的错误为其它类型,则不确定是否在存在 */ func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } // CheckPath 判断目录是否存在,不存在自动创建 func CheckPath(path string) error { b, err := PathExists(path) if err != nil { return err } if b { return fmt.Errorf("path %s 存在\n", path) } err = os.Mkdir(path, os.ModePerm) if err != nil { return err } return nil } // SavePic 下载并保存图片 func SavePic(imgPath, imgUrl, fileName string) error { res, err := http.Get(imgUrl) if err != nil { log.Error("err:%v", err) return err } defer res.Body.Close() // 获得get请求响应的reader对象 reader := bufio.NewReaderSize(res.Body, 32*1024) path := imgPath + string(os.PathSeparator) + fileName + ".png" if ok, _ := PathExists(path); ok { if err := os.Remove(path); err != nil { log.Error("err:%v", err) return err } } file, err := os.Create(path) if err != nil { log.Error("err:%v", err) return err } defer file.Close() // 获得文件的writer对象 writer := bufio.NewWriter(file) if _, err := io.Copy(writer, reader); err != nil { log.Error("err:%v", err) return err } return nil } // GetSimpleRandomString 获取纯英文大写的随机n位字符串 func GetSimpleRandomString(n int) string { ret := []byte{} for i := 0; i < n; i++ { tmp := rand.Intn(26) ret = append(ret, byte(tmp+65)) } return string(ret) } var tokenMap = map[string]string{ "1": "G", "2": "J", "3": "M", "4": "S", "5": "Q", "6": "B", "7": "E", "8": "F", "9": "I", "0": "Z", } // RandomToken 生成token func RandomToken(uid int) string { ran := GetSimpleRandomString(10) str := strconv.Itoa(uid) for _, v := range str { ran += tokenMap[string(v)] } return ran } // NewOrderID 新生成一个订单id func NewOrderID(uid int) string { orderID := "" str := strconv.Itoa(uid) for _, v := range str { orderID += tokenMap[string(v)] } return orderID + strconv.FormatInt(time.Now().Unix(), 10) } // SliceInt2Int32 数组转换 func SliceInt2Int32(c []int) []uint32 { ret := []uint32{} for _, v := range c { ret = append(ret, uint32(v)) } return ret } // SliceInt322Int 数组转换 func SliceInt322Int(c []uint32) []int { ret := []int{} for _, v := range c { ret = append(ret, int(v)) } return ret } // SliceInt2Int64 数组转换 func SliceInt2Int64(c []int) []int64 { ret := []int64{} for _, v := range c { ret = append(ret, int64(v)) } return ret } // RemoveSlice 根据b中的元素,从a中剔除,相当于两个slice做差 func RemoveSlice(a, b []int) []int { ret := []int{} dm := map[int]int{} for _, v := range b { dm[v]++ } for _, v := range a { if num, ok := dm[v]; ok { num-- if num == 0 { delete(dm, v) } else { dm[v] = num } continue } else { ret = append(ret, v) } } return ret } // RemoveOne 从a中剔除b func RemoveOne(a []int, b int) []int { ret := []int{} count := 0 for _, v := range a { if v == b && count == 0 { count++ continue } ret = append(ret, v) } return ret } // SliceContain a中是否包含b func SliceContain(a []int, b int) bool { for _, v := range a { if v == b { return true } } return false } // SliceContainInt64 a中是否包含b func SliceContainInt64(a []int64, b int64) bool { for _, v := range a { if v == b { return true } } return false } func TrimHiddenCharacter(originStr string) string { srcRunes := []rune(originStr) dstRunes := make([]rune, 0, len(srcRunes)) for _, c := range srcRunes { if c >= 0 && c <= 31 { continue } if c == 127 { continue } dstRunes = append(dstRunes, c) } return string(dstRunes) } func IsEqualSlice(a, b []int) bool { dm := map[int]int{} for _, v := range a { dm[v]++ } for _, v := range b { dm[v]-- } for _, v := range dm { if v != 0 { return false } } return true } // GetRandSection 获取区间随机值 func GetRandSection(a, b int) int { if a >= b { return a } return a + rand.Intn(b-a) } // CopySameStruct 将b中的非空字段复制到相同的结构体a中 func CopySameStruct(a, b interface{}) bool { typa := reflect.TypeOf(a) typb := reflect.TypeOf(b) if typa != typb { return false } if typa.Kind() != reflect.Ptr { return false } vala := reflect.ValueOf(a).Elem() valb := reflect.ValueOf(b).Elem() // typaE := typa.Elem() // typbE := typb.Elem() for i := 0; i < vala.NumField(); i++ { if valb.Field(i).IsZero() { continue } vala.Field(i).Set(valb.Field(i)) } return true } // 字符串去重 func RemoveDuplication(arr []string) []string { set := make(map[string]struct{}, len(arr)) j := 0 for _, v := range arr { _, ok := set[v] if ok { continue } set[v] = struct{}{} arr[j] = v j++ } return arr[:j] } // Abs 取绝对值 func Abs(a int64) int64 { if a < 0 { a = -a } return a } func CleanList(l *list.List) { e := l.Front() if e == nil { return } for { next := e.Next() l.Remove(e) if next == nil { break } e = next } } func CheckPhone(phone string) string { ok, _ := regexp.MatchString("[1-9]{2}[2-9][0-9]{7,8}", phone) if !ok { phone = "" for i := 0; i < 2; i++ { phone += fmt.Sprintf("%d", rand.Intn(9)+1) } phone += fmt.Sprintf("%d", rand.Intn(8)+2) tmp := rand.Intn(2) count := 7 if tmp == 0 { count = 8 } for i := 0; i < count; i++ { phone += fmt.Sprintf("%d", rand.Intn(10)) } } return phone } func CheckCPF(cpf string) string { if IsCPF(cpf) { return cpf } num := [11]int{} for i := 0; i < 9; i++ { num[i] = rand.Intn(9) + 1 } s1 := 0 for i := 0; i < 9; i++ { s1 += (10 - i) * num[i] } m1 := s1 % 11 if m1 < 2 { num[9] = 0 } else { num[9] = 11 - m1 } s2 := 0 for i := 0; i < 10; i++ { s2 += num[i] * (11 - i) } m2 := s2 % 11 if m2 < 2 { num[10] = 0 } else { num[10] = 11 - m2 } s := "" for _, v := range num { s += fmt.Sprintf("%d", v) } return s } func IsCPF(cpf string) bool { cpf = strings.ReplaceAll(cpf, ".", "") cpf = strings.ReplaceAll(cpf, "-", "") num := StringToInt(cpf) if len(num) != 11 { return false } s1 := 0 for i := 0; i < len(num)-2; i++ { s1 += (10 - i) * num[i] } m1 := s1 % 11 y := num[9] if m1 < 2 && y != 0 { return false } if m1 >= 2 && y != (11-m1) { return false } z := num[10] s2 := 0 for i := 0; i < len(num)-1; i++ { s2 += num[i] * (11 - i) } m2 := s2 % 11 if m2 < 2 && z != 0 { return false } if m2 >= 2 && z != 11-m2 { return false } return true } func IndexTryCallback(f func() error, cb func()) { Go(func() { for i := 0; i <= 6; i++ { if i > 0 { next := time.Duration(i*i) * time.Minute time.Sleep(next) } err := f() if err == nil { break } log.Error("err:%v next:%v", err, i) if i == 6 { cb() } } }) } func IndexTry(f func() error) { Go(func() { for i := 0; i <= 6; i++ { if i > 0 { next := time.Duration(i*i) * time.Minute time.Sleep(next) } err := f() if err == nil { break } log.Error("err:%v next:%v", err, i) } }) } func IndexTryS(f func() error) { Go(func() { for i := 0; i <= 6; i++ { if i > 0 { next := time.Duration(i*i) * time.Second time.Sleep(next) } err := f() if err == nil { break } log.Error("err:%v next:%v", err, i) } }) } // 获取下一个整点5分钟时间戳 func GetNext5MinUnix() int64 { now := time.Now() zero := GetZeroTime(now) h, m, _ := now.Clock() m -= m % 5 last := zero.Add(time.Duration(h) * time.Hour).Add(time.Duration(m) * time.Minute) return last.Add(5 * time.Minute).Unix() } // stringsToInt 数字字符串转数字数组 func StringToInt(s string) []int { b := []byte(s) ret := []int{} for _, v := range b { if v < 48 || v > 57 { return nil } ret = append(ret, int(v)-48) } return ret } func ZhToUnicode(raw string) (string, error) { str, err := strconv.Unquote(strings.Replace(strconv.Quote(raw), `\\u`, `\u`, -1)) if err != nil { return "", err } return str, nil } // IsSingle 判断数字是不是单数 func IsSingle(n int) bool { return n/2*2 != n } // GetIntFromString 从字符串s中提取出所有的int func GetIntFromString(s string) int { ret := []rune{} for _, v := range s { if v < 48 || v > 57 { continue } ret = append(ret, v) } tmp := string(ret) number, err := strconv.Atoi(tmp) if err != nil { log.Error("err:%v", err) return 0 } return number } func FormatUserName(name string) (fn string, ln string) { if len(name) == 0 { return } name1 := strings.Split(strings.TrimSpace(name), " ") name2 := []string{} for _, v := range name1 { if len(v) == 0 { continue } name2 = append(name2, v) } if len(name2) > 1 { for i, v := range name2 { if i == len(name2)-1 { break } fn += v } ln = name2[len(name2)-1] } else if len(name2) == 1 { fn = name2[0] } return } // FormatNumberBrazil 格式化数字为巴西格式(例如:1234.56 -> "1.234,56"),小数点后全为零时去除小数部分 func FormatNumberBrazil(number float64) string { // 首先,使用fmt.Sprintf格式化数字,这里使用"%.2f"保证即使是整数也会带有".00" usFormatted := fmt.Sprintf("%.2f", number) // 如果小数部分为.00,则去除它 // if strings.HasSuffix(usFormatted, ".00") { // usFormatted = usFormatted[:len(usFormatted)-3] // } usFormatted = strings.TrimSuffix(usFormatted, ".00") // 将点替换为逗号,作为小数点 step1 := strings.ReplaceAll(usFormatted, ".", ",") // 分割字符串为整数部分和可能的小数部分 parts := strings.Split(step1, ",") // 替换整数部分中的千位分隔符 integerPart := parts[0] var sb strings.Builder for i, r := range integerPart { if i > 0 && (len(integerPart)-i)%3 == 0 { sb.WriteRune('.') } sb.WriteRune(r) } // 如果有小数部分,且不是"00",将其添加回去 if len(parts) > 1 && parts[1] != "00" { sb.WriteRune(',') sb.WriteString(parts[1]) } return sb.String() } func CountDecimals(value float64) int { // 将浮点数转换为字符串 text := fmt.Sprintf("%v", value) // 查找小数点位置 pointIndex := strings.Index(text, ".") if pointIndex == -1 { // 没有找到小数点,说明没有小数部分 return 0 } // 去除小数点后面尾随的0 trimmed := strings.TrimRight(text[pointIndex+1:], "0") // 返回小数部分的长度 return len(trimmed) } // 根据权重,从数组中随机一个元素 // name 权重字段名(该字段值必须为整型) func RandomOneEleFromSlice(slice interface{}, name string) int { ref := reflect.ValueOf(slice) var total int64 for i := 0; i < ref.Len(); i++ { tmp := ref.Index(i) if tmp.Kind() == reflect.Pointer { tmp = tmp.Elem() } total += tmp.FieldByName(name).Int() } ran := rand.Int63n(total) var rans int64 for i := 0; i < ref.Len(); i++ { tmp := ref.Index(i) if tmp.Kind() == reflect.Pointer { tmp = tmp.Elem() } rans += tmp.FieldByName(name).Int() if ran < rans { return i } } return 0 } func RandBetween64(min, max int64) int64 { return rand.Int63n(max-min+1) + min } func RandBetween(min, max int) int { return rand.Intn(max-min+1) + min } // HashMarshal 用于将结构体序列化为Redis哈希 func HashMarshal(v interface{}) (map[string]interface{}, error) { result := make(map[string]interface{}) vType := reflect.TypeOf(v) vValue := reflect.ValueOf(v) if vType.Kind() != reflect.Struct { return nil, fmt.Errorf("HashMarshal: expected a struct type, got %v", vType.Kind()) } for i := 0; i < vType.NumField(); i++ { field := vType.Field(i) fieldValue := vValue.Field(i) redisTag := field.Tag.Get("redis") if redisTag == "" || redisTag == "-" { continue } var value interface{} switch fieldValue.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: value = fieldValue.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: value = fieldValue.Uint() case reflect.Float32, reflect.Float64: value = fieldValue.Float() case reflect.String: value = fieldValue.String() case reflect.Bool: value = fieldValue.Bool() case reflect.Struct: if t, ok := fieldValue.Interface().(time.Time); ok { value = t.Format(time.RFC3339) } else { return nil, fmt.Errorf("HashMarshal: unsupported struct type %v for field %s", fieldValue.Kind(), field.Name) } default: return nil, fmt.Errorf("HashMarshal: unsupported type %v for field %s", fieldValue.Kind(), field.Name) } result[redisTag] = value } return result, nil } // HashUnmarshal 用于从Redis哈希反序列化为结构体 func HashUnmarshal(data map[string]string, v interface{}) error { vType := reflect.TypeOf(v).Elem() vValue := reflect.ValueOf(v).Elem() if vType.Kind() != reflect.Struct { return fmt.Errorf("HashUnmarshal: expected a struct type, got %v", vType.Kind()) } for i := 0; i < vType.NumField(); i++ { field := vType.Field(i) fieldValue := vValue.Field(i) redisTag := field.Tag.Get("redis") if redisTag == "" || redisTag == "-" { continue } strValue, ok := data[redisTag] if !ok { continue } switch fieldValue.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: intValue, err := strconv.ParseInt(strValue, 10, 64) if err != nil { return err } fieldValue.SetInt(intValue) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: uintValue, err := strconv.ParseUint(strValue, 10, 64) if err != nil { return err } fieldValue.SetUint(uintValue) case reflect.Float32, reflect.Float64: floatValue, err := strconv.ParseFloat(strValue, 64) if err != nil { return err } fieldValue.SetFloat(floatValue) case reflect.String: fieldValue.SetString(strValue) case reflect.Bool: boolValue, err := strconv.ParseBool(strValue) if err != nil { return err } fieldValue.SetBool(boolValue) case reflect.Struct: if fieldValue.Type() == reflect.TypeOf(time.Time{}) { t, err := time.Parse(time.RFC3339, strValue) if err != nil { return err } fieldValue.Set(reflect.ValueOf(t)) } else { return fmt.Errorf("HashUnmarshal: unsupported struct type %v for field %s", fieldValue.Kind(), field.Name) } default: return fmt.Errorf("HashUnmarshal: unsupported type %v for field %s", fieldValue.Kind(), field.Name) } } return nil } func Int64SliceToStringSlice(int64Slice []int64) []string { stringSlice := make([]string, len(int64Slice)) for i, num := range int64Slice { stringSlice[i] = strconv.FormatInt(num, 10) } return stringSlice } func StringToInt64Slice(s, sep string) ([]int64, error) { // 使用分隔符拆分字符串 parts := strings.Split(s, sep) // 创建一个空的 []int64 切片用于存储转换后的数字 int64Slice := make([]int64, len(parts)) // 遍历拆分后的字符串部分并转换为 int64 for i, part := range parts { num, err := strconv.ParseInt(part, 10, 64) if err != nil { return nil, err // 如果转换失败,返回错误 } int64Slice[i] = num } return int64Slice, nil } // 通过总数Count分成一个递增数列 func GenerateSequence(totalSum, minValue int64) ([]int64, error) { length := 6 // 初步生成递增数列 [minValue, minValue+1, ..., minValue+5] sequence := make([]int64, length) for i := 0; i < length; i++ { sequence[i] = minValue + int64(i) } // 计算初步数列的总和 sumInitial := (2*minValue + int64(length-1)) * int64(length) / 2 // 如果初步数列的总和已经大于 totalSum,则无法生成 if sumInitial > totalSum { return nil, fmt.Errorf("cannot generate a sequence with the given totalSum and minValue") } // 计算剩余需要分配的值 remaining := totalSum - sumInitial // 设置随机种子 rand.Seed(time.Now().UnixNano()) // 平均分配剩余的值,同时减少极端情况的出现 for remaining > 0 { // 每次迭代从头到尾随机增量分配,减少单个元素过度增长 for i := 0; i < length && remaining > 0; i++ { // 确保递增,计算可能的最大增量 maxIncrement := remaining / int64(length-i) if i > 0 && sequence[i] <= sequence[i-1] { maxIncrement = sequence[i-1] - sequence[i] + 1 } // 随机选择增量 if maxIncrement > 0 { randIncrement := rand.Int63n(maxIncrement + 1) sequence[i] += randIncrement remaining -= randIncrement } } } sort.Slice(sequence, func(i, j int) bool { return sequence[i] < sequence[j] }) return sequence, nil }