You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
7.5 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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