|
|
|
|
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
|
|
|
|
|
}
|