614 lines
14 KiB
Go
614 lines
14 KiB
Go
package utils
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility"
|
|
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/tealeg/xlsx"
|
|
|
|
"github.com/gogf/gf/v2/crypto/gmd5"
|
|
"github.com/gogf/gf/v2/encoding/gcharset"
|
|
"github.com/gogf/gf/v2/encoding/gurl"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
)
|
|
|
|
// EncryptPassword 密码加密
|
|
func EncryptPassword(password, salt string) string {
|
|
return gmd5.MustEncryptString(gmd5.MustEncryptString(password) + gmd5.MustEncryptString(salt))
|
|
}
|
|
|
|
// GetDomain 获取当前请求接口域名
|
|
func GetDomain(ctx context.Context) string {
|
|
r := g.RequestFromCtx(ctx)
|
|
pathInfo, err := gurl.ParseURL(r.GetUrl(), -1)
|
|
if err != nil {
|
|
g.Log().Error(ctx, "GetDomain gurl.ParseURL", err)
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("%s://%s:%s/", pathInfo["scheme"], pathInfo["host"], pathInfo["port"])
|
|
}
|
|
|
|
// GetClientIp 获取客户端IP
|
|
func GetClientIp(ctx context.Context) string {
|
|
return g.RequestFromCtx(ctx).GetClientIp()
|
|
}
|
|
|
|
// GetLocalIP 获取服务器内网IP
|
|
func GetLocalIP() (string, error) {
|
|
var localIP string
|
|
interfaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// 遍历所有网卡
|
|
for _, i := range interfaces {
|
|
if i.Flags&net.FlagUp == 0 {
|
|
continue // 网卡未开启
|
|
}
|
|
if i.Flags&net.FlagLoopback != 0 {
|
|
continue // 网卡为loopback地址
|
|
}
|
|
addrs, err := i.Addrs()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// 遍历网卡上的所有地址
|
|
for _, addr := range addrs {
|
|
var ip net.IP
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
ip = v.IP
|
|
case *net.IPAddr:
|
|
ip = v.IP
|
|
}
|
|
if ip == nil || ip.IsLoopback() {
|
|
continue
|
|
}
|
|
ip = ip.To4()
|
|
if ip == nil {
|
|
continue // 不是ipv4地址
|
|
}
|
|
localIP = ip.String()
|
|
return localIP, nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("no local IP address found")
|
|
}
|
|
|
|
// GetPublicIP 获取公网IP
|
|
func GetPublicIP() (ip string, err error) {
|
|
return utility.GetPublicIP()
|
|
}
|
|
|
|
// GetUserAgent 获取user-agent
|
|
func GetUserAgent(ctx context.Context) string {
|
|
return ghttp.RequestFromCtx(ctx).Header.Get("User-Agent")
|
|
}
|
|
|
|
// GetCityByIp 获取ip所属城市
|
|
func GetCityByIp(ip string) string {
|
|
return utility.GetCityByIp(ip)
|
|
}
|
|
|
|
func RemoveRepeatedElementAndEmpty(arr []int) []int {
|
|
newArr := make([]int, 0)
|
|
for _, item := range arr {
|
|
repeat := false
|
|
if len(newArr) > 0 {
|
|
for _, v := range newArr {
|
|
if v == item {
|
|
repeat = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if repeat {
|
|
continue
|
|
}
|
|
newArr = append(newArr, item)
|
|
}
|
|
return newArr
|
|
}
|
|
|
|
// RemoveDuplicationMap 数组去重
|
|
func RemoveDuplicationMap(arr []string) []string {
|
|
set := make(map[string]struct{}, len(arr))
|
|
j := 0
|
|
for _, v := range arr {
|
|
_, ok := set[v]
|
|
if ok {
|
|
continue
|
|
}
|
|
set[v] = struct{}{}
|
|
arr[j] = v
|
|
j++
|
|
}
|
|
return arr[:j]
|
|
}
|
|
|
|
// Decimal 保留两位小数
|
|
func Decimal(value float64) float64 {
|
|
value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", value), 64)
|
|
return value
|
|
}
|
|
|
|
// InArray 判断字符串是否存在数组中
|
|
func InArray(target string, strArray []string) bool {
|
|
sort.Strings(strArray)
|
|
index := sort.SearchStrings(strArray, target)
|
|
if index < len(strArray) && strArray[index] == target {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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])
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// DirSize 获取目录下所有文件大小
|
|
func DirSize(dirname string) string {
|
|
var (
|
|
s int64
|
|
files, _ = WalkDir(dirname)
|
|
)
|
|
for _, n := range files {
|
|
s += n.size
|
|
}
|
|
return FileSize(s)
|
|
}
|
|
|
|
func ConvertToStringSlice(data []interface{}) []string {
|
|
result := make([]string, len(data))
|
|
for i, v := range data {
|
|
str, ok := v.(string)
|
|
if !ok {
|
|
// 如果类型断言失败,可以在此处进行相应的错误处理
|
|
// 这里简单地将该元素转换为空字符串
|
|
str = ""
|
|
}
|
|
result[i] = str
|
|
}
|
|
return result
|
|
}
|
|
|
|
// 删除文件
|
|
func DeleteFile(name string) error {
|
|
//判断文件是否存在
|
|
if _, err := os.Stat(name); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
//打开文件
|
|
file, err := os.Open(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(file *os.File) {
|
|
err := file.Close()
|
|
if err != nil {
|
|
fmt.Println("DeleteFile", err)
|
|
}
|
|
}(file)
|
|
|
|
//删除文件
|
|
err = os.Remove(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReverseReadLines 从文件末尾开始逆序高效地读取行。
|
|
func ReverseReadLines(name string) ([]string, error) {
|
|
// 打开文件。
|
|
file, err := os.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(file *os.File) {
|
|
err := file.Close()
|
|
if err != nil {
|
|
g.Log().Error(context.Background(), "ReverseReadLines", err)
|
|
}
|
|
}(file)
|
|
|
|
// 获取文件大小。
|
|
fs, err := file.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fileSize := fs.Size()
|
|
|
|
// 定义合理的缓冲区大小进行读取。这个值可以根据预期的文件大小和行长度进行调整。
|
|
const bufferSize = 1024 * 20
|
|
|
|
// 创建一个切片用于保存行。
|
|
var lines []string
|
|
|
|
// 从文件末尾开始分块读取。
|
|
for offset := fileSize; offset > 0; offset -= bufferSize {
|
|
// 计算读取的大小。
|
|
size := GetMin(bufferSize, offset)
|
|
buffer := make([]byte, size)
|
|
|
|
// 定位并读取这一块。
|
|
_, err := file.ReadAt(buffer, offset-size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 将缓冲区分割成行,并以逆序添加它们。
|
|
scanner := bufio.NewScanner(bufio.NewReaderSize(file, int(size)))
|
|
var chunkLines []string
|
|
for scanner.Scan() {
|
|
chunkLines = append([]string{scanner.Text()}, chunkLines...)
|
|
}
|
|
// 将这一块的行添加到总行中。
|
|
lines = append(chunkLines, lines...)
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return lines, nil
|
|
}
|
|
|
|
func ReverseRead(name string, lineNum uint) ([]string, error) {
|
|
//打开文件
|
|
file, err := os.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(file *os.File) {
|
|
err := file.Close()
|
|
if err != nil {
|
|
g.Log().Error(context.Background(), "ReverseRead file.Close", err)
|
|
}
|
|
}(file)
|
|
//获取文件大小
|
|
fs, err := file.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fileSize := fs.Size()
|
|
|
|
var offset int64 = -1 //偏移量,初始化为-1,若为0则会读到EOF
|
|
char := make([]byte, 1) //用于读取单个字节
|
|
lineStr := "" //存放一行的数据
|
|
buff := make([]string, 0, 100)
|
|
for (-offset) <= fileSize {
|
|
//通过Seek函数从末尾移动游标然后每次读取一个字节
|
|
_, err := file.Seek(offset, io.SeekEnd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, err = file.Read(char)
|
|
if err != nil {
|
|
return buff, err
|
|
}
|
|
if char[0] == '\n' {
|
|
offset-- //windows跳过'\r'
|
|
lineNum-- //到此读取完一行
|
|
buff = append(buff, lineStr)
|
|
lineStr = ""
|
|
if lineNum == 0 {
|
|
return buff, nil
|
|
}
|
|
} else {
|
|
lineStr = string(char) + lineStr
|
|
}
|
|
offset--
|
|
}
|
|
buff = append(buff, lineStr)
|
|
//使用mahonia解码
|
|
for i := 0; i < len(buff); i++ {
|
|
buff[i], err = gcharset.ToUTF8("UTF-8", buff[i])
|
|
}
|
|
return buff, nil
|
|
}
|
|
|
|
// 文件一块一块的读取
|
|
func ReadBlock(filePath string) {
|
|
start1 := time.Now()
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
g.Log().Error(context.Background(), "ReadBlock Open", err)
|
|
return
|
|
}
|
|
defer func(file *os.File) {
|
|
err := file.Close()
|
|
if err != nil {
|
|
g.Log().Error(context.Background(), "ReadBlock file.Close", err)
|
|
}
|
|
}(file)
|
|
// 设置每次读取字节数
|
|
buffer := make([]byte, 1024*4)
|
|
for {
|
|
n, err := file.Read(buffer)
|
|
// 控制条件,根据实际调整
|
|
if err != nil && err != io.EOF {
|
|
g.Log().Error(context.Background(), "ReadBlock Read", err)
|
|
}
|
|
if n == 0 {
|
|
break
|
|
}
|
|
// 如下代码打印出每次读取的文件块(字节数)
|
|
//fmt.Println(string(buffer[:n]))
|
|
}
|
|
fmt.Println("readBolck spend : ", time.Now().Sub(start1))
|
|
}
|
|
|
|
const (
|
|
kilobyte = 1024
|
|
megabyte = 1024 * kilobyte
|
|
gigabyte = 1024 * megabyte
|
|
terabyte = 1024 * gigabyte
|
|
petabyte = 1024 * terabyte
|
|
)
|
|
|
|
// FormatSize 格式化文件大小。
|
|
func FormatSize(size int64) string {
|
|
switch {
|
|
case size < kilobyte:
|
|
return strconv.Itoa(int(size)) + "B"
|
|
case size < megabyte:
|
|
return fmt.Sprintf("%.2fK", float64(size)/kilobyte)
|
|
case size < gigabyte:
|
|
return fmt.Sprintf("%.2fM", float64(size)/megabyte)
|
|
case size < terabyte:
|
|
return fmt.Sprintf("%.2fG", float64(size)/gigabyte)
|
|
case size < petabyte:
|
|
return fmt.Sprintf("%.2fT", float64(size)/terabyte)
|
|
default:
|
|
return fmt.Sprintf("%.2fP", float64(size)/petabyte)
|
|
}
|
|
}
|
|
|
|
func ValidatePassword(password string, minimumLength int, requireComplexity int, requireDigit int, requireLowercase int, requireUppercase int) (flag bool, err error) {
|
|
//初始化返回结果
|
|
flag = true
|
|
//判断密码长度
|
|
if len(password) < minimumLength {
|
|
err = gerror.New(fmt.Sprintf(g.I18n().T(context.TODO(), "{#utilsValidatePwLen}"), minimumLength))
|
|
flag = false
|
|
return
|
|
}
|
|
//是否有复杂字符
|
|
if requireComplexity == 1 && !hasComplexCharacters(password) {
|
|
err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwChar}"))
|
|
flag = false
|
|
return
|
|
}
|
|
//是否有数字
|
|
if requireDigit == 1 && !hasDigit(password) {
|
|
err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwDigit}"))
|
|
flag = false
|
|
return
|
|
}
|
|
//是否有小写字母
|
|
if requireLowercase == 1 && !hasLowercaseLetter(password) {
|
|
err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwLower}"))
|
|
flag = false
|
|
return
|
|
}
|
|
//是否有大写字母
|
|
if requireUppercase == 1 && !hasUppercaseLetter(password) {
|
|
err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwUpper}"))
|
|
flag = false
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// hasComplexCharacters:检查字符串中是否有复杂字符(特殊字符)
|
|
func hasComplexCharacters(str string) bool {
|
|
specialCharacters := "!@#$%^&*()_+-=[]{}|;:,.<>?~"
|
|
|
|
for _, char := range str {
|
|
if contains(specialCharacters, string(char)) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// hasDigit:检查字符串中是否有数字
|
|
func hasDigit(str string) bool {
|
|
for _, char := range str {
|
|
if isDigit(string(char)) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// hasLowercaseLetter:检查字符串中是否有小写字母
|
|
func hasLowercaseLetter(str string) bool {
|
|
for _, char := range str {
|
|
if isLowercase(string(char)) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// hasUppercaseLetter:检查字符串中是否有大写字母
|
|
func hasUppercaseLetter(str string) bool {
|
|
for _, char := range str {
|
|
if isUppercase(string(char)) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// isDigit:判断字符是否是数字
|
|
func isDigit(c string) bool {
|
|
return c >= "0" && c <= "9"
|
|
}
|
|
|
|
// isLowercase:判断字符是否是小写字母
|
|
func isLowercase(c string) bool {
|
|
return c >= "a" && c <= "z"
|
|
}
|
|
|
|
// isUppercase:判断字符是否是大写字母
|
|
func isUppercase(c string) bool {
|
|
return c >= "A" && c <= "Z"
|
|
}
|
|
|
|
// contains:判断字符串是否包含指定字符
|
|
func contains(str, char string) bool {
|
|
for _, c := range str {
|
|
if string(c) == char {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// GetMin 返回两个整数中的较小值
|
|
func GetMin(a, b int64) int64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// 返回自定义错误码
|
|
func NewGError(code gcode.Code) error {
|
|
return gerror.NewCode(code, code.Message())
|
|
}
|
|
|
|
// 返回自定义错误码
|
|
func NewGErrorS(code gcode.Code, text string) error {
|
|
return gerror.NewCode(code, text)
|
|
}
|
|
|
|
// ToExcel 生成io.ReadSeeker 参数 titleList 为Excel表头,dataList 为数据
|
|
func ToExcel(dataList []interface{}) (content io.ReadSeeker) {
|
|
// 生成一个新的文件
|
|
file := xlsx.NewFile()
|
|
// 添加sheet页
|
|
sheet, _ := file.AddSheet("Sheet1")
|
|
// 插入表头
|
|
titleRow := sheet.AddRow()
|
|
|
|
//获取表头
|
|
objType := reflect.TypeOf(dataList[0])
|
|
elem := objType.Elem()
|
|
var titleList []string
|
|
if elem.Kind() == reflect.Struct {
|
|
for i := 1; i <= elem.NumField(); i++ {
|
|
field := elem.Field(i - 1)
|
|
if field.Name != "PageReq" {
|
|
if field.Tag != "" && field.Tag.Get("description") != "" {
|
|
titleList = append(titleList, g.I18n().T(context.TODO(), field.Tag.Get("description")))
|
|
} else {
|
|
titleList = append(titleList, g.I18n().T(context.TODO(), field.Name))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, v := range titleList {
|
|
cell := titleRow.AddCell()
|
|
cell.Value = v
|
|
//表头字体颜色
|
|
cell.GetStyle().Font.Color = "000000"
|
|
cell.GetStyle().Fill.BgColor = "cfe2f3"
|
|
//居中显示
|
|
cell.GetStyle().Alignment.Horizontal = "center"
|
|
cell.GetStyle().Alignment.Vertical = "center"
|
|
}
|
|
// 插入内容
|
|
for _, v := range dataList {
|
|
row := sheet.AddRow()
|
|
row.WriteStruct(v, -1)
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
_ = file.Write(&buffer)
|
|
content = bytes.NewReader(buffer.Bytes())
|
|
return
|
|
}
|
|
|
|
// 解析时间
|
|
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
|
|
}
|