Files
iotlcm/admin-svr/server/internal/library/utils/utils.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
}