package utils import ( "crypto/md5" "crypto/rand" "encoding/hex" "io" "math" "math/big" "os" "reflect" "strings" "golang.org/x/exp/constraints" ) // Concat 字符串拼接 func Concat(strArr ...string) string { var builder strings.Builder for i := 0; i < len(strArr); i++ { builder.WriteString(strArr[i]) } return builder.String() } // PageLimitOffset 根据分页页数和页大小得到Limit&Offset func PageLimitOffset(page int64, pageSize int64) (limit int64, offset int64) { limit = pageSize offset = pageSize * (page - 1) // 分页大小默认值 if limit == 0 { limit = 10 } return } // Ternary 三元运算 func Ternary[T any](ifOp bool, trueV, falseV T) T { if ifOp { return trueV } return falseV } // InSlice 判断指定值是否在切片中 func InSlice[T constraints.Ordered | reflect.Kind](needle T, haystack []T) bool { if len(haystack) == 0 { return false } for i := 0; i < len(haystack); i++ { if haystack[i] == needle { return true } } return false } // RandomExpire redis过期时间添加一个随机值 func RandomExpire(timestamp int64, randSecond int64) int64 { return timestamp + Rand(0, randSecond) } // Rand 生成区间[-m, n]的安全随机数 func Rand(min, max int64) int64 { if min > max { min, max = max, min } if min < 0 { i64Min := int64(math.Abs(float64(min))) result, _ := rand.Int(rand.Reader, big.NewInt(max+i64Min+1)) return result.Int64() - i64Min } else { result, _ := rand.Int(rand.Reader, big.NewInt(max-min+1)) return min + result.Int64() } } // SliceSum 切片求和 func SliceSum[T constraints.Integer | constraints.Float](slice []T) (sum T) { for i := 0; i < len(slice); i++ { sum += slice[i] } return sum } // SliceColumn 结构体切片,数组切片,map切片中提取指定字段 func SliceColumn[T, V any](slice []T, column string) []V { values := make([]V, len(slice)) switch reflect.TypeOf(slice).Elem().Kind() { case reflect.Slice, reflect.Array: for i, v := range slice { values[i] = reflect.ValueOf(v).Index(int(reflect.ValueOf(column).Int())).Interface().(V) } case reflect.Map: for i, v := range slice { values[i] = reflect.ValueOf(v).MapIndex(reflect.ValueOf(column)).Interface().(V) } case reflect.Struct: for i, v := range slice { values[i] = StructColumnByTag[V](v, column, "json") } } return values } // SliceColumns 结构体切片,数组切片,map切片中提取指定多个字段, 以指定符号分割 func SliceColumns[T any](slice []T, sep string, columns ...string) []string { values := make([]string, 0, len(slice)) sCols := make([][]any, 0, len(columns)) for _, column := range columns { sCols = append(sCols, SliceColumn[T, any](slice, column)) } for i := range sCols[0] { elems := make([]any, 0, len(columns)) for j := range columns { elems = append(elems, sCols[j][i]) } values = append(values, Implode(elems, sep)) } return values } func StructColumnByTag[V any](s any, column, tag string) (ret V) { val, typ := Indirect(s) if typ.Kind() != reflect.Struct { panic("expect struct") } for i := 0; i < typ.NumField(); i++ { typField := typ.Field(i) if typField.Anonymous && typField.Type.Kind() == reflect.Struct { return StructColumnByTag[V](val.Field(i).Interface(), column, tag) } tagName := typField.Tag.Get(tag) if tagName == column { return val.Field(i).Interface().(V) } } return ret } // SliceDelIdx 删除切片中指定索引 func SliceDelIdx[T any](slice []T, idx ...int) []T { ret := make([]T, 0, len(slice)) for i := 0; i < len(slice); i++ { if !InSlice(i, idx) { ret = append(ret, slice[i]) } } return ret } // SliceDelVal 删除切片中指定值 func SliceDelVal[T constraints.Ordered](slice []T, val ...T) []T { ret := make([]T, 0, len(slice)) for i := 0; i < len(slice); i++ { if !InSlice(slice[i], val) { ret = append(ret, slice[i]) } } return ret } // IsDir 判断路径是否存在且为文件夹 func IsDir(path string) bool { stat, err := os.Stat(path) if err != nil { return false } return stat.IsDir() } // IsFile 判断路径是否存在且为文件 func IsFile(path string) bool { stat, err := os.Stat(path) if err != nil { return false } return !stat.IsDir() } // FileMD5 计算文件的Md5 func FileMD5(filePath string) (string, error) { file, err := os.Open(filePath) defer func() { _ = file.Close() }() if err != nil { return "", err } hash := md5.New() _, _ = io.Copy(hash, file) return hex.EncodeToString(hash.Sum(nil)), nil } // EqualSlice 切片是否相等 func EqualSlice[T constraints.Ordered](s1, s2 []T) bool { if len(s1) != len(s2) { return false } if (s1 == nil) != (s2 == nil) { return false } for i := 0; i < len(s1); i++ { if s1[i] != s2[i] { return false } } return true } // IsComplexType 判断是否为复杂数据结构 func IsComplexType(typ reflect.Type) bool { kind := typ.Kind() if !InSlice(kind, ComplexType) { return false } // []byte 不作为复杂类型 if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() == reflect.Uint8 { return false } return true } // b=++a func IncrementAfter(inNum *int64, increStepOpt ...int64) int64 { var ( outNum, increStep int64 ) increStep = int64(1) if len(increStepOpt) > 0 { increStep = increStepOpt[0] } *inNum += increStep outNum = *inNum return outNum } // b=a++ func IncrementBefore(inNum *int64, increStepOpt ...int64) int64 { var ( outNum, increStep int64 ) increStep = int64(1) if len(increStepOpt) > 0 { increStep = increStepOpt[0] } outNum = *inNum *inNum += increStep return outNum }