738 lines
15 KiB
Go
738 lines
15 KiB
Go
package global
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
IsIPv4 = "IPv4"
|
|
IsIPv6 = "IPv6"
|
|
NonIP = "NonIp"
|
|
)
|
|
|
|
type em struct{}
|
|
|
|
func GetPkgName() string {
|
|
return reflect.TypeOf(em{}).PkgPath()
|
|
}
|
|
|
|
// interface{} change to map[string]interface{}
|
|
// interface{} data is []interface{}
|
|
func ListToMap(list interface{}, key string) map[string]interface{} {
|
|
res := make(map[string]interface{})
|
|
arr := ToSlice(list)
|
|
for _, row := range arr {
|
|
immutable := reflect.ValueOf(row)
|
|
val := immutable.FieldByName(key).String()
|
|
res[val] = row
|
|
}
|
|
return res
|
|
}
|
|
|
|
// interface{} change to []interface{}
|
|
func ToSlice(arr interface{}) []interface{} {
|
|
ret := make([]interface{}, 0)
|
|
v := reflect.ValueOf(arr)
|
|
if v.Kind() != reflect.Slice {
|
|
ret = append(ret, arr)
|
|
return ret
|
|
}
|
|
l := v.Len()
|
|
for i := 0; i < l; i++ {
|
|
ret = append(ret, v.Index(i).Interface())
|
|
}
|
|
return ret
|
|
}
|
|
|
|
var TodoList []Todo
|
|
|
|
type Todo struct {
|
|
Id int64
|
|
Item string
|
|
}
|
|
|
|
// JSON序列化方式
|
|
func jsonStructToMap(TodoList Todo) (map[string]interface{}, error) {
|
|
// 结构体转json
|
|
strRet, err := json.Marshal(TodoList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// json转map
|
|
var mRet map[string]interface{}
|
|
err1 := json.Unmarshal(strRet, &mRet)
|
|
if err1 != nil {
|
|
return nil, err1
|
|
}
|
|
return mRet, nil
|
|
}
|
|
|
|
func IsContain(item string, items []string) bool {
|
|
for _, e := range items {
|
|
if e == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func IsContainP(item string, items *[]string, size int) bool {
|
|
for i := 0; i < size; i++ {
|
|
if (*items)[i] == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 将字符串 分割成 字符串数组
|
|
// @s:分割符
|
|
func SplitString(str string, s string) []string {
|
|
sa := strings.Split(str, s)
|
|
return sa
|
|
}
|
|
|
|
// 合并字符串数组
|
|
func MergeStringArr(a, b []string) []string {
|
|
var arr []string
|
|
for _, i := range a {
|
|
arr = append(arr, i)
|
|
}
|
|
for _, j := range b {
|
|
arr = append(arr, j)
|
|
}
|
|
return arr
|
|
}
|
|
|
|
// 数组去重
|
|
func UniqueStringArr(m []string) []string {
|
|
d := make([]string, 0)
|
|
tempMap := make(map[string]bool, len(m))
|
|
for _, v := range m { // 以值作为键名
|
|
if tempMap[v] == false {
|
|
tempMap[v] = true
|
|
d = append(d, v)
|
|
}
|
|
}
|
|
return d
|
|
}
|
|
|
|
// 合并整型数组
|
|
func MergeArr(a, b []int) []int {
|
|
var arr []int
|
|
for _, i := range a {
|
|
arr = append(arr, i)
|
|
}
|
|
for _, j := range b {
|
|
arr = append(arr, j)
|
|
}
|
|
return arr
|
|
}
|
|
|
|
// 数组去重
|
|
func UniqueArr(m []int) []int {
|
|
d := make([]int, 0)
|
|
tempMap := make(map[int]bool, len(m))
|
|
for _, v := range m { // 以值作为键名
|
|
if tempMap[v] == false {
|
|
tempMap[v] = true
|
|
d = append(d, v)
|
|
}
|
|
}
|
|
return d
|
|
}
|
|
|
|
// 升序
|
|
func AscArr(e []int) []int {
|
|
sort.Ints(e[:])
|
|
return e
|
|
}
|
|
|
|
// 降序
|
|
func DescArr(e []int) []int {
|
|
sort.Sort(sort.Reverse(sort.IntSlice(e)))
|
|
return e
|
|
}
|
|
|
|
func MatchRmUID(p string, s string) bool {
|
|
match, _ := regexp.MatchString(p, s)
|
|
return match
|
|
}
|
|
|
|
type OrderedMap struct {
|
|
Order []string
|
|
Map map[string]interface{}
|
|
}
|
|
|
|
func (om *OrderedMap) UnmarshalJson(b []byte) error {
|
|
json.Unmarshal(b, &om.Map)
|
|
|
|
index := make(map[string]int)
|
|
for key := range om.Map {
|
|
om.Order = append(om.Order, key)
|
|
esc, _ := json.Marshal(key) //Escape the key
|
|
index[key] = bytes.Index(b, esc)
|
|
}
|
|
|
|
sort.Slice(om.Order, func(i, j int) bool { return index[om.Order[i]] < index[om.Order[j]] })
|
|
return nil
|
|
}
|
|
|
|
func (om OrderedMap) MarshalJson() ([]byte, error) {
|
|
var b []byte
|
|
buf := bytes.NewBuffer(b)
|
|
buf.WriteRune('{')
|
|
l := len(om.Order)
|
|
for i, key := range om.Order {
|
|
km, err := json.Marshal(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buf.Write(km)
|
|
buf.WriteRune(':')
|
|
vm, err := json.Marshal(om.Map[key])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buf.Write(vm)
|
|
if i != l-1 {
|
|
buf.WriteRune(',')
|
|
}
|
|
fmt.Println(buf.String())
|
|
}
|
|
buf.WriteRune('}')
|
|
fmt.Println(buf.String())
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func GetBodyCopy(r *http.Request) (*bytes.Buffer, error) {
|
|
// If r.bodyBuf present, return the copy
|
|
// if r.bodyBuf != nil {
|
|
// return bytes.NewBuffer(r.bodyBuf.Bytes()), nil
|
|
// }
|
|
|
|
// Maybe body is `io.Reader`.
|
|
// Note: Resty user have to watchout for large body size of `io.Reader`
|
|
if r.Body != nil {
|
|
b, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Restore the Body
|
|
// close(r.Body)
|
|
r.Body = io.NopCloser(bytes.NewReader(b))
|
|
|
|
// Return the Body bytes
|
|
return bytes.NewBuffer(b), nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func UnmarshalBody(r *http.Request, v *interface{}, maxLen int64) error {
|
|
body, err := io.ReadAll(io.LimitReader(r.Body, maxLen))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return json.Unmarshal(body, v)
|
|
}
|
|
|
|
func SetNotifyUrl(ip string, port uint16, uri string) string {
|
|
return fmt.Sprintf("http://%s:%d%s", ip, port, uri)
|
|
}
|
|
|
|
func GetIps() (ips []string, err error) {
|
|
interfaceAddr, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
return ips, err
|
|
}
|
|
|
|
for _, address := range interfaceAddr {
|
|
ipNet, isVailIpNet := address.(*net.IPNet)
|
|
// 检查ip地址判断是否回环地址
|
|
if isVailIpNet && !ipNet.IP.IsLoopback() {
|
|
if ipNet.IP.To4() != nil {
|
|
ips = append(ips, ipNet.IP.String())
|
|
}
|
|
}
|
|
}
|
|
return ips, nil
|
|
}
|
|
|
|
func GetCurrentTimeSliceIndexByPeriod(t time.Time, period int) int {
|
|
index := int((t.Hour()*60+t.Minute())/period) - 1
|
|
if index < 0 {
|
|
return int(24*60/period) - 1
|
|
}
|
|
return index
|
|
}
|
|
|
|
var (
|
|
cst *time.Location
|
|
)
|
|
|
|
// RFC3339ToDateTime convert rfc3339 value to china standard time layout
|
|
func RFC3339ToDateTime(value string) (string, error) {
|
|
ts, err := time.Parse(time.RFC3339, value)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return ts.In(cst).Format("2006-01-02 15:04:05"), nil
|
|
}
|
|
|
|
// CreateTimeDir 根据当前时间格式来创建文件夹
|
|
func CreateTimeDir(fmt string, path string) string {
|
|
folderName := time.Now().Format(fmt)
|
|
folderPath := filepath.Join(path, folderName)
|
|
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
|
|
// 必须分成两步:先创建文件夹、再修改权限
|
|
os.Mkdir(folderPath, 0664) //0644也可以os.ModePerm
|
|
os.Chmod(folderPath, 0664)
|
|
}
|
|
return folderPath
|
|
}
|
|
|
|
// CreateDir 根据传入的目录名和路径来创建文件夹
|
|
func CreateDir(folderName string, path string) string {
|
|
folderPath := filepath.Join(path, folderName)
|
|
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
|
|
// 必须分成两步:先创建文件夹、再修改权限
|
|
os.MkdirAll(folderPath, 0664) //0644也可以os.ModePerm
|
|
os.Chmod(folderPath, 0664)
|
|
}
|
|
return folderPath
|
|
}
|
|
|
|
func GetFmtTimeString(srcFmt string, timeString string, dstFmt string) string {
|
|
t, _ := time.ParseInLocation(srcFmt, timeString, time.Local)
|
|
return t.Format(dstFmt)
|
|
}
|
|
|
|
func GetFileMD5Sum(filePath string) (string, error) {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer file.Close()
|
|
md5 := md5.New()
|
|
_, err = io.Copy(md5, file)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
md5str := hex.EncodeToString(md5.Sum(nil))
|
|
|
|
return md5str, nil
|
|
}
|
|
|
|
// PathExists check path is exist or no
|
|
func PathExists(path string) (bool, error) {
|
|
_, err := os.Stat(path)
|
|
if err == nil { //文件或者目录存在
|
|
return true, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
// PathExists check path is exist or no
|
|
func FilePathExists(filePath string) (bool, error) {
|
|
_, err := os.Stat(filePath)
|
|
if err == nil { //文件或者目录存在
|
|
return true, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
func GetDayDuration(d1, d2 string) int64 {
|
|
a, _ := time.Parse("2006-01-02", d1)
|
|
b, _ := time.Parse("2006-01-02", d2)
|
|
d := a.Sub(b)
|
|
|
|
return (int64)(d.Hours() / 24)
|
|
}
|
|
|
|
func GetSecondsSinceDatetime(datetimeStr string) (int64, error) {
|
|
loc1, _ := time.LoadLocation("Local")
|
|
// 解析日期时间字符串为时间对象
|
|
datetime, err := time.ParseInLocation(time.DateTime, datetimeStr, loc1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// 计算时间差
|
|
duration := time.Since(datetime)
|
|
|
|
// 获取时间差的秒数
|
|
seconds := int64(duration.Seconds())
|
|
|
|
return seconds, nil
|
|
}
|
|
|
|
func GetSecondDuration(time1, time2 string) (int64, error) {
|
|
loc1, _ := time.LoadLocation("Local")
|
|
// 解析日期时间字符串为时间对象
|
|
t1, err := time.ParseInLocation(time.DateTime, time1, loc1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
t2, err := time.ParseInLocation(time.DateTime, time2, loc1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// 计算时间差
|
|
duration := t2.Sub(t1)
|
|
|
|
// 获取时间差的秒数
|
|
seconds := int64(duration.Seconds())
|
|
|
|
return seconds, nil
|
|
}
|
|
|
|
// 0: invalid ip
|
|
// 4: IPv4
|
|
// 6: IPv6
|
|
func ParseIP(s string) (net.IP, int) {
|
|
ip := net.ParseIP(s)
|
|
if ip == nil {
|
|
return nil, 0
|
|
}
|
|
for i := 0; i < len(s); i++ {
|
|
switch s[i] {
|
|
case '.':
|
|
return ip, 4
|
|
case ':':
|
|
return ip, 6
|
|
}
|
|
}
|
|
return nil, 0
|
|
}
|
|
|
|
func BytesCombine1(pBytes ...[]byte) []byte {
|
|
return bytes.Join(pBytes, []byte(""))
|
|
}
|
|
|
|
func BytesCombine(pBytes ...[]byte) []byte {
|
|
length := len(pBytes)
|
|
s := make([][]byte, length)
|
|
for index := 0; index < length; index++ {
|
|
s[index] = pBytes[index]
|
|
}
|
|
sep := []byte("")
|
|
return bytes.Join(s, sep)
|
|
}
|
|
|
|
func ParseIPAddr(ip string) string {
|
|
ipAddr := net.ParseIP(ip)
|
|
|
|
if ipAddr != nil {
|
|
if ipAddr.To4() != nil {
|
|
return IsIPv4
|
|
} else {
|
|
return IsIPv6
|
|
}
|
|
}
|
|
|
|
return NonIP
|
|
}
|
|
|
|
func CombineHostUri(ip string, port string) string {
|
|
var hostUri string = ""
|
|
ipType := ParseIPAddr(ip)
|
|
if ipType == IsIPv4 {
|
|
hostUri = fmt.Sprintf("http://%s:%v", ip, port)
|
|
} else {
|
|
hostUri = fmt.Sprintf("http://[%s]:%v", ip, port)
|
|
}
|
|
|
|
return hostUri
|
|
}
|
|
|
|
func StructToMap(obj interface{}) map[string]interface{} {
|
|
objValue := reflect.ValueOf(obj)
|
|
objType := objValue.Type()
|
|
|
|
m := make(map[string]interface{})
|
|
|
|
for i := 0; i < objValue.NumField(); i++ {
|
|
field := objValue.Field(i)
|
|
fieldName := objType.Field(i).Name
|
|
|
|
m[fieldName] = field.Interface()
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
// ToMap 结构体转为Map[string]interface{}
|
|
func ToMap(in interface{}, tagName string) (map[string]interface{}, error) {
|
|
out := make(map[string]interface{})
|
|
|
|
v := reflect.ValueOf(in)
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
if v.Kind() != reflect.Struct { // 非结构体返回错误提示
|
|
return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
|
|
}
|
|
|
|
t := v.Type()
|
|
// 遍历结构体字段
|
|
// 指定tagName值为map中key;字段值为map中value
|
|
for i := 0; i < v.NumField(); i++ {
|
|
fi := t.Field(i)
|
|
if tagValue := fi.Tag.Get(tagName); tagValue != "" {
|
|
out[tagValue] = v.Field(i).Interface()
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func ZipOneFile(srcFile, dstZip string, pathFlag bool) error {
|
|
zipFile, err := os.Create(dstZip)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer zipFile.Close()
|
|
|
|
zipWriter := zip.NewWriter(zipFile)
|
|
defer zipWriter.Close()
|
|
|
|
fileToCompress, err := os.Open(srcFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fileToCompress.Close()
|
|
|
|
var fileInZip io.Writer
|
|
if pathFlag {
|
|
fileInZip, err = zipWriter.Create(srcFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// 获取文件的基本名称
|
|
fileName := filepath.Base(fileToCompress.Name())
|
|
fileInZip, err = zipWriter.Create(fileName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
_, err = io.Copy(fileInZip, fileToCompress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ZipDirectoryFile(srcDir, dstZip string) error {
|
|
// Create a new zip file
|
|
zipfileWriter, err := os.Create(dstZip)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer zipfileWriter.Close()
|
|
|
|
// Create a new zip archive
|
|
zipWriter := zip.NewWriter(zipfileWriter)
|
|
defer zipWriter.Close()
|
|
|
|
// Walk through the directory and add files to the zip archive
|
|
err = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create a new file header for the current file
|
|
header, err := zip.FileInfoHeader(info)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set the name of the file within the zip archive
|
|
header.Name = filepath.Join(filepath.Base(srcDir), path[len(srcDir):])
|
|
|
|
// If the current file is a directory, skip it
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
// Create a new file in the zip archive
|
|
fileWriter, err := zipWriter.CreateHeader(header)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Open the current file
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
// Copy the contents of the current file to the zip archive
|
|
_, err = io.Copy(fileWriter, file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
// 判断软件包是rpm或者deb, 1:rpm, 2:deb, 0:unknown format
|
|
func JudgeRpmOrDebPackage(filePath string) (int, error) {
|
|
var fileType int = 0
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return fileType, err
|
|
}
|
|
defer file.Close()
|
|
|
|
// Read the first 6 bytes of the file
|
|
header := make([]byte, 6)
|
|
_, err = file.Read(header)
|
|
if err != nil {
|
|
return fileType, err
|
|
}
|
|
|
|
// Check the magic numbers to determine the package format
|
|
if string(header) == "!<arch>" {
|
|
fileType = 1
|
|
} else if string(header) == "!<arch\n" || string(header) == "!<arch\r" {
|
|
fileType = 2
|
|
} else {
|
|
fileType = 0
|
|
}
|
|
|
|
return fileType, nil
|
|
}
|
|
|
|
func isRpmPackage(file *os.File) bool {
|
|
// RPM packages start with the magic number "EDABEEDB"
|
|
magic := []byte{0xED, 0xAB, 0xEE, 0xDB}
|
|
buffer := make([]byte, len(magic))
|
|
|
|
_, err := file.Read(buffer)
|
|
if err != nil && err != io.EOF {
|
|
return false
|
|
}
|
|
|
|
return string(buffer) == string(magic)
|
|
}
|
|
|
|
func isDebPackage(file *os.File) bool {
|
|
// DEB packages start with the magic number "!<arch>\n"
|
|
magic := []byte("!<arch>\n")
|
|
buffer := make([]byte, len(magic))
|
|
|
|
_, err := file.Read(buffer)
|
|
if err != nil && err != io.EOF {
|
|
return false
|
|
}
|
|
|
|
return string(buffer) == string(magic)
|
|
}
|
|
|
|
func CheckRpmOrDebPackage(filePath string) (int, error) {
|
|
var fileType int = 0
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return fileType, err
|
|
}
|
|
defer file.Close()
|
|
|
|
isRpm := isRpmPackage(file)
|
|
isDeb := isDebPackage(file)
|
|
|
|
if isRpm {
|
|
fileType = 1
|
|
} else if isDeb {
|
|
fileType = 2
|
|
} else {
|
|
fileType = 0
|
|
}
|
|
|
|
return fileType, nil
|
|
}
|
|
|
|
func IsRpmOrDebPackage(filePath string) int {
|
|
var fileType int = 0
|
|
|
|
if strings.Contains(filePath, ".rpm") {
|
|
fileType = 1
|
|
} else if strings.Contains(filePath, ".deb") {
|
|
fileType = 2
|
|
} else {
|
|
fileType = 0
|
|
}
|
|
|
|
return fileType
|
|
}
|
|
|
|
func RecurseStructToMap(obj any) map[string]any {
|
|
out := make(map[string]any)
|
|
v := reflect.ValueOf(obj)
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
// 递归函数,用于处理嵌套结构体
|
|
var recurse func(reflect.Value) any
|
|
recurse = func(value reflect.Value) any {
|
|
if value.Kind() == reflect.Struct {
|
|
nestedOut := make(map[string]any)
|
|
for i := 0; i < value.NumField(); i++ {
|
|
nestedOut[value.Type().Field(i).Name] = recurse(value.Field(i))
|
|
}
|
|
return nestedOut
|
|
} else if value.Kind() == reflect.Ptr {
|
|
return recurse(value.Elem())
|
|
}
|
|
return value.Interface()
|
|
}
|
|
|
|
t := v.Type()
|
|
for i := 0; i < v.NumField(); i++ {
|
|
f := v.Field(i)
|
|
out[t.Field(i).Name] = recurse(f)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func IsMember(target string, group []string) bool {
|
|
for _, str := range group {
|
|
if target == str {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|