You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1024 lines
21 KiB
1024 lines
21 KiB
package util |
|
|
|
import ( |
|
"bufio" |
|
"container/list" |
|
"fmt" |
|
"io" |
|
"math" |
|
"math/rand" |
|
"net/http" |
|
"os" |
|
"reflect" |
|
"regexp" |
|
"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(N, M int64) ([]int64, error) { |
|
length := int64(5) |
|
|
|
// 初步生成一个递增数列 [M, M+1, ..., M+5] |
|
sequence := make([]int64, length) |
|
for i := int64(0); i < length; i++ { |
|
sequence[i] = M + i |
|
} |
|
|
|
// 计算初步生成数列的总和 |
|
sumInitial := (2*M + length - 1) * length / 2 |
|
|
|
// 如果初步生成数列的总和已经大于 N,则无法生成 |
|
if sumInitial > N { |
|
return nil, fmt.Errorf("无法生成满足条件的数列") |
|
} |
|
|
|
// 计算剩余需要分配的值 |
|
remaining := N - sumInitial |
|
|
|
// 将剩余的值均匀分配到数列中,从后往前加值,保持递增 |
|
for i := length - 1; i >= 0 && remaining > 0; i-- { |
|
sequence[i] += remaining |
|
remaining = 0 |
|
} |
|
|
|
return sequence, nil |
|
}
|
|
|