chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,613 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user