241 lines
5.3 KiB
Go
241 lines
5.3 KiB
Go
// package utility
|
|
// @Link https://github.com/xiujiecn/xiu-admin
|
|
// @Copyright Copyright (c) 2025 LiXiujie
|
|
// @Author Lxj <xxx@example.com>
|
|
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
|
package utility
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/crypto/gmd5"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
"github.com/gogf/gf/v2/os/gfile"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/mileusna/useragent"
|
|
"golang.org/x/exp/rand"
|
|
)
|
|
|
|
func Md5(str string) string {
|
|
return gmd5.MustEncryptString(str)
|
|
}
|
|
|
|
// 密码加密
|
|
func PasswordEncrypt(password string, salt string) string {
|
|
return Md5(password + salt)
|
|
}
|
|
|
|
// 解析时间
|
|
func ParseDuration(d string) (time.Duration, error) {
|
|
d = strings.TrimSpace(d)
|
|
dr, err := time.ParseDuration(d)
|
|
if err == nil {
|
|
return dr, nil
|
|
}
|
|
if strings.Contains(d, "d") {
|
|
index := strings.Index(d, "d")
|
|
|
|
hour, _ := strconv.Atoi(d[:index])
|
|
dr = time.Hour * 24 * time.Duration(hour)
|
|
ndr, err := time.ParseDuration(d[index+1:])
|
|
if err != nil {
|
|
return dr, nil
|
|
}
|
|
return dr + ndr, nil
|
|
}
|
|
|
|
dv, err := strconv.ParseInt(d, 10, 64)
|
|
return time.Duration(dv), err
|
|
}
|
|
|
|
// 随机生成5位字符
|
|
func RandomString(n int) string {
|
|
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
b := make([]byte, n)
|
|
for i := range b {
|
|
b[i] = letters[rand.Intn(len(letters))]
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
// 获取客户端IP
|
|
func GetClientIp(ctx context.Context) string {
|
|
if ctx == nil {
|
|
return ""
|
|
}
|
|
if ghttp.RequestFromCtx(ctx) == nil {
|
|
return ""
|
|
}
|
|
return ghttp.RequestFromCtx(ctx).GetClientIp()
|
|
}
|
|
|
|
// 获取user-agent
|
|
func GetUserAgent(ctx context.Context) string {
|
|
return ghttp.RequestFromCtx(ctx).Header.Get("User-Agent")
|
|
}
|
|
|
|
// 获取操作系统
|
|
func GetOs(ctx context.Context) string {
|
|
userAgent := GetUserAgent(ctx)
|
|
ua := useragent.Parse(userAgent)
|
|
return ua.OS
|
|
}
|
|
|
|
// 获取浏览器
|
|
func GetBrowser(ctx context.Context) string {
|
|
userAgent := GetUserAgent(ctx)
|
|
ua := useragent.Parse(userAgent)
|
|
return ua.Name
|
|
}
|
|
|
|
// 获取ip所属城市
|
|
func GetCityByIp(ip string) string {
|
|
parsed := net.ParseIP(strings.Trim(strings.TrimSpace(ip), "[]"))
|
|
if parsed == nil {
|
|
return ""
|
|
}
|
|
if parsed.IsPrivate() || parsed.IsLoopback() || parsed.IsLinkLocalUnicast() || parsed.IsLinkLocalMulticast() || parsed.IsUnspecified() {
|
|
return "内网IP"
|
|
}
|
|
return cityLookup.Get(parsed.String())
|
|
}
|
|
|
|
// 时间戳转 yyyy-MM-dd HH:mm:ss
|
|
func TimeStampToDateTime(timeStamp int64) string {
|
|
tm := gtime.NewFromTimeStamp(timeStamp)
|
|
return tm.Format("Y-m-d H:i:s")
|
|
}
|
|
|
|
// 时间戳转 yyyy-MM-dd
|
|
func TimeStampToDate(timeStamp int64) string {
|
|
tm := gtime.NewFromTimeStamp(timeStamp)
|
|
return tm.Format("Y-m-d")
|
|
}
|
|
|
|
// 服务端ip
|
|
func GetLocalIP() (ip string, err error) {
|
|
addrs, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, addr := range addrs {
|
|
ipAddr, ok := addr.(*net.IPNet)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if ipAddr.IP.IsLoopback() {
|
|
continue
|
|
}
|
|
if !ipAddr.IP.IsGlobalUnicast() {
|
|
continue
|
|
}
|
|
return ipAddr.IP.String(), nil
|
|
}
|
|
return
|
|
}
|
|
func GetPublicIP() (ip string, err error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
return lookupPublicIP(ctx)
|
|
}
|
|
|
|
// 获取两个字符串之间的字符串
|
|
func GetBetweenStr(str, start, end string) (result string) {
|
|
startIndex := strings.Index(str, start)
|
|
if startIndex == -1 {
|
|
return ""
|
|
}
|
|
// 更新startIndex为开始字符串之后的位置
|
|
startIndex += len(start)
|
|
|
|
// 找到结束字符串的位置,从更新后的startIndex开始查找
|
|
endIndex := strings.Index(str[startIndex:], end)
|
|
if endIndex == -1 {
|
|
return ""
|
|
}
|
|
|
|
// 切片操作获取中间的字符串
|
|
result = str[startIndex : startIndex+endIndex]
|
|
return
|
|
}
|
|
|
|
// A和B两个数组,A数组中存在,B数组中不存在
|
|
func ArrayRightDiff(a, b []int64) []int64 {
|
|
diff := make([]int64, 0)
|
|
for _, v := range a {
|
|
if !slices.Contains(b, v) {
|
|
diff = append(diff, v)
|
|
}
|
|
}
|
|
return diff
|
|
}
|
|
|
|
type fileInfo struct {
|
|
name string
|
|
size int64
|
|
}
|
|
|
|
// WalkDir 获取目录下文件的名称和大小
|
|
func WalkDir(dirname string) ([]fileInfo, error) {
|
|
var fileInfos []fileInfo
|
|
err := filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
fileInfos = append(fileInfos, fileInfo{name: path, size: info.Size()})
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return fileInfos, err
|
|
}
|
|
func DirSize(dirname string) string {
|
|
var (
|
|
s int64
|
|
files, _ = WalkDir(dirname)
|
|
)
|
|
for _, n := range files {
|
|
s += n.size
|
|
}
|
|
return FileSize(s)
|
|
}
|
|
func MergeAbs(path string, fileName ...string) string {
|
|
var paths = []string{gfile.RealPath(path)}
|
|
paths = append(paths, fileName...)
|
|
return gfile.Join(paths...)
|
|
}
|
|
|
|
// FileSize 字节的单位转换 保留两位小数
|
|
func FileSize(fileSize int64) string {
|
|
units := []string{"B", "KB", "MB", "GB", "TB", "EB"}
|
|
var size = float64(fileSize)
|
|
var i int
|
|
for i = 0; size > 1024; i++ {
|
|
size /= 1024
|
|
}
|
|
return fmt.Sprintf("%.2f %s", size, units[i])
|
|
}
|
|
|
|
// UniqueSlice 切片去重
|
|
func UniqueSlice[K comparable](languages []K) []K {
|
|
result := make([]K, 0, len(languages))
|
|
temp := map[K]struct{}{}
|
|
for _, item := range languages {
|
|
if _, ok := temp[item]; !ok {
|
|
temp[item] = struct{}{}
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|