// Package utils 不依赖 joybuywork/internal 里面的东西写到这个文件 package utils import ( "bytes" "crypto/md5" "crypto/rand" "joybuywork/internal/consts" "encoding/hex" "encoding/json" "fmt" "math" "math/big" "net" "strconv" "strings" "time" "github.com/golang-module/carbon/v2" "github.com/shopspring/decimal" ) // Ternary 三元运算 func Ternary[T any](cond bool, t, f T) T { if cond { return t } return f } // STernary 如果c==t 就取f 否则 取 c func STernary[T consts.NormalType](c, t, f T) T { if c == t { return f } return c } // PpDbLo page perPage 获取db limit和offset func PpDbLo(page, perPage int32) (offset, limit int) { page = Ternary(page == 0, 1, page) perPage = Ternary(perPage == 0, 20, perPage) limit = int(perPage) offset = int((page - 1) * perPage) return } // DbAllLike 全模糊查询 func DbAllLike(str string) string { return "%" + str + "%" } // BetweenDay 获取区间日期 func BetweenDay(start, end carbon.Carbon, format ...string) (days []string) { for { if start.Gte(end) { return } if len(format) == 0 { days = append(days, start.ToShortDateString()) } else if format[0] == time.DateOnly { days = append(days, start.ToDateString()) } start = start.AddDay() } } // BetweenHour 获取区间小时 func BetweenHour(start, end carbon.Carbon) (hours []int) { for { if start.Gte(end) { return } hours = append(hours, start.Hour()) start = start.AddHour() } } // PercentNoZero 百分额不要0 func PercentNoZero[T consts.NumberType](num, den T) string { if den == 0 { return "0" } return strings.TrimSuffix(strings.TrimRight(fmt.Sprintf("%.2f", Percent(num, den)), "0"), ".") } // Percent 百分额 func Percent[T consts.NumberType](num, den T) float64 { if den == 0 { return 0 } return (float64(num) / float64(den)) * 100 } // StrToInt64 字符串转成int64 func StrToInt64(v string) int64 { id, _ := strconv.ParseInt(v, 10, 64) return id } // StrToInt 字符串转成int func StrToInt(v string) int { id, _ := strconv.Atoi(v) return id } // StrToFloat64 字符串转成 float64 func StrToFloat64(v string) decimal.Decimal { val, _ := decimal.NewFromString(v) return val } // Bs []byte转化成string func Bs(m []byte) string { return bytes.NewBuffer(m).String() } // Md5String md5字符串 func Md5String(v string) string { return Md5([]byte(v)) } // Md5Any 任意md5 func Md5Any(v any) string { b, _ := json.Marshal(v) return Md5(b) } func Md5(v []byte) string { hash := md5.New() hash.Write(v) b := hash.Sum(nil) return hex.EncodeToString(b) } // StrAdd 字符串相加 func StrAdd(nums ...string) string { v := decimal.NewFromFloat(0) for _, num := range nums { d, _ := decimal.NewFromString(num) v = v.Add(d) } return v.String() } // NumberStrCompareGte 第一个数字字符串大于或等于第二个数字字符串 func NumberStrCompareGte(first, second string) bool { return NumberStrCompare(first, second, "gte") } // NumberStrCompare 第一个数字字符串比较第二个数字字符串 func NumberStrCompare(first, second, compare string) bool { f, s := StrToFloat64(first), StrToFloat64(second) switch compare { case ">=", "gte": return f.GreaterThanOrEqual(s) } return false } // UserMark 获取iovd的用户hash标识 func UserMark(ip, os, ver, deviceModel string) string { return Md5String(fmt.Sprintf("%v_%v_%v_%v", ip, os, ver, deviceModel)) } // Join 用字符关联数据 func Join[T consts.NumberType](elems []T, sep string) (str string) { if len(elems) == 0 { return } str = fmt.Sprint(elems[0]) for _, v := range elems[1:] { str += fmt.Sprintf("%s%v", sep, v) } return } // RandNumber 随机数字 func RandNumber(max int64) int64 { n, _ := rand.Int(rand.Reader, big.NewInt(max)) return n.Int64() } func GetIp() string { // 获取所有网络接口的信息 interfaces, _ := net.Interfaces() // 遍历网络接口,查找非回环和非虚拟接口的IP地址 for _, iface := range interfaces { if iface.Flags&net.FlagLoopback == 0 && iface.Flags&net.FlagUp != 0 { addresses, err := iface.Addrs() if err != nil { continue } for _, addr := range addresses { ipNet, ok := addr.(*net.IPNet) if ok && !ipNet.IP.IsLoopback() && ipNet.IP.To4() != nil { return ipNet.IP.String() } } } } return "" } // Groups 数据分组 func Groups[T any](ids []T, nums int) (groups [][]T) { l := len(ids) if l <= nums { groups = append(groups, ids) return } var start int var end = nums for { groups = append(groups, ids[start:end]) start += nums end += nums if start >= l { return } if end > l { end = l } } } // SplitToInt64 字符串变成int64 func SplitToInt64(str string, sep string) []int64 { if str == "" { return nil } arr := strings.Split(str, sep) var ids = make([]int64, len(arr)) for i := 0; i < len(arr); i++ { ids[i] = StrToInt64(arr[i]) } return ids } // NotExistGetFirst 选不中就用第一个 func NotExistGetFirst[T consts.NormalType](selected T, options []T) (show T) { if len(options) == 0 { return } for _, v := range options { if selected == v { return v } } return options[0] } // SupportPlatform 支持的平台 func SupportPlatform(platform string) string { if platform == "" { return "" } platform = strings.ToLower(platform) switch platform { case consts.PlatformIOS, consts.PlatformWeb, consts.PlatformAndroid, consts.PlatformIPad: return platform } return consts.PlatformOther } // PercentCutZero 百分数去掉0 func PercentCutZero(val float64) string { return strconv.FormatFloat(val, 'f', -1, 64) } // Float64PointCutZero 保留位数去0 func Float64PointCutZero(val float64, prec int) string { fl, _ := strconv.ParseFloat(strconv.FormatFloat(val, 'f', prec, 64), 64) return strconv.FormatFloat(fl, 'f', -1, 64) } // NormalizeDecimalStrings 将数字字符串切片归一化为相同小数位数的整数 // 1. 找出所有字符串中的最大小数位数 // 2. 将所有数字乘以 10^最大小数位数,转换为整数 // 返回: (整数切片, 扩展倍数) type decimalInfo struct { integerPart string decimalPart string places int } func NormalizeDecimalStrings(strs []string) ([]int, int) { if len(strs) == 0 { return []int{}, 0 } // 第一步:找出最大小数位数 maxDecimalPlaces := 0 decimalInfos := make([]decimalInfo, len(strs)) for i, s := range strs { s = strings.TrimSpace(s) if s == "" { s = "0" } negative := false if s[0] == '-' { negative = true s = s[1:] } else if s[0] == '+' { s = s[1:] } // 查找小数点 decimalIndex := strings.Index(s, ".") var integerPart, decimalPart string if decimalIndex == -1 { integerPart = s decimalPart = "" decimalInfos[i].places = 0 } else { integerPart = s[:decimalIndex] decimalPart = s[decimalIndex+1:] decimalInfos[i].places = len(decimalPart) } decimalInfos[i].integerPart = integerPart decimalInfos[i].decimalPart = decimalPart if decimalInfos[i].places > maxDecimalPlaces { maxDecimalPlaces = decimalInfos[i].places } if negative { decimalInfos[i].integerPart = "-" + decimalInfos[i].integerPart } } multiplier := int(math.Pow10(maxDecimalPlaces)) result := make([]int, len(strs)) for i, info := range decimalInfos { paddedDecimalPart := info.decimalPart if len(paddedDecimalPart) < maxDecimalPlaces { paddedDecimalPart = paddedDecimalPart + strings.Repeat("0", maxDecimalPlaces-len(paddedDecimalPart)) } num, _ := strconv.Atoi(info.integerPart + paddedDecimalPart) result[i] = num } return result, multiplier }