// package utility // @Link https://github.com/xiujiecn/xiu-admin // @Copyright Copyright (c) 2025 LiXiujie // @Author Lxj // @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 }