You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1049 lines
22 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package Common
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net"
"os"
"os/exec"
"path"
"reflect"
"strconv"
"strings"
"time"
log "github.com/sirupsen/logrus"
// "github.com/faiface/beep/speaker"
// log "github.com/sirupsen/logrus"
)
// 截取字符串 start 起点下标 length 需要截取的长度
func Substr(str string, start int, length int) string {
defer func() {
if r := recover(); r != nil {
log.Error("Substr:", r)
}
}()
rs := []rune(str)
rl := len(rs)
end := 0
if start < 0 {
start = rl - 1 + start
}
end = start + length
if start > end {
start, end = end, start
}
if start < 0 {
start = 0
}
if start > rl {
start = rl
}
if end < 0 {
end = 0
}
if end > rl {
end = rl
}
return string(rs[start:end])
}
// insertSlice 插入
func InsertSlice(index int, newstr []byte, src []byte) (ns []byte) {
defer func() {
if r := recover(); r != nil {
log.Error("InsertSlice:", r)
}
}()
ns = append(ns, src[:index]...) // 切片后加..., 相当于拆包成单个元素
ns = append(ns, newstr...)
ns = append(ns, src[index+len(newstr):]...)
return
}
func InsertToSlice(index int, newstr []byte, src []byte) (ns []byte) {
defer func() {
if r := recover(); r != nil {
log.Error("InsertToSlice:", r)
}
}()
ns = append(ns, src[:index]...) // 切片后加..., 相当于拆包成单个元素
ns = append(ns, newstr...)
ns = append(ns, src[index:]...)
return
}
func ContainCS(list []string, key string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("ContainCS:", r)
}
}()
for _, item := range list {
if item == key {
return true
}
}
return false
}
func Contain(obj interface{}, target interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("Contain:", r)
}
}()
targetValue := reflect.ValueOf(target)
switch reflect.TypeOf(target).Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < targetValue.Len(); i++ {
if targetValue.Index(i).Interface() == obj {
return true
}
}
case reflect.Map:
if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
return true
}
}
return false
}
// RemoveElementToint32 移除数组内的指定元素
func RemoveElementStrings(list []string, value string) []string {
defer func() {
if r := recover(); r != nil {
log.Error("RemoveElementStrings:", r)
}
}()
var result = make([]string, 0)
index := 0
endIndex := len(list) - 1
for i, s := range list {
if s == value {
result = append(result, list[index:i]...)
index = i + 1
} else if i == endIndex {
result = append(result, list[index:endIndex+1]...)
}
}
return result
}
// Strval 获取变量的字符串值
// 浮点型 3.0将会转换成字符串3, "3"
// 非数值或字符类型的变量将会被转换成JSON格式字符串
func Strval(value interface{}) string {
defer func() {
if r := recover(); r != nil {
log.Error("Strval:", r)
}
}()
var key string
if value == nil {
return key
}
switch value.(type) {
case float64:
ft := value.(float64)
key = strconv.FormatFloat(ft, 'f', -1, 64)
case float32:
ft := value.(float32)
key = strconv.FormatFloat(float64(ft), 'f', -1, 64)
case int:
it := value.(int)
key = strconv.Itoa(it)
case uint:
it := value.(uint)
key = strconv.Itoa(int(it))
case int8:
it := value.(int8)
key = strconv.Itoa(int(it))
case uint8:
it := value.(uint8)
key = strconv.Itoa(int(it))
case int16:
it := value.(int16)
key = strconv.Itoa(int(it))
case uint16:
it := value.(uint16)
key = strconv.Itoa(int(it))
case int32:
it := value.(int32)
key = strconv.Itoa(int(it))
case uint32:
it := value.(uint32)
key = strconv.Itoa(int(it))
case int64:
it := value.(int64)
key = strconv.FormatInt(it, 10)
case uint64:
it := value.(uint64)
key = strconv.FormatUint(it, 10)
case string:
key = value.(string)
case []byte:
key = string(value.([]byte))
default:
newValue, _ := json.Marshal(value)
key = string(newValue)
}
return key
}
// MD5加密
func GetAuthKey(token string) (key string) {
defer func() {
if r := recover(); r != nil {
log.Error("GetAuthKey:", r)
}
}()
token = token
md5Ctx := md5.New()
md5Ctx.Write([]byte(token))
data := md5Ctx.Sum(nil)
return hex.EncodeToString(data)
}
/**
*@tips l.ibreoffice 转换指令:
* libreoffice6.2 invisible --convert-to pdf csDoc.doc --outdir /home/[转出目录]
*
* @function 实现文档类型转换为pdf或html
* @param command:libreofficed的命令(具体以版本为准)winsoffice linuxlibreoffice6.2
* fileSrcPath:转换文件的路径
* fileOutDir:转换后文件存储目录
* converterType转换的类型pdf/html
* @return fileOutPath 转换成功生成的文件的路径 error 转换错误
*/
func FuncDocs2Pdf(command string, fileSrcPath string, fileOutDir string, converterType string) (fileOutPath string, error error) {
defer func() {
if r := recover(); r != nil {
log.Error("FuncDocs2Pdf:", r)
}
}()
//校验fileSrcPath
srcFile, erByOpenSrcFile := os.Open(fileSrcPath)
if erByOpenSrcFile != nil && os.IsNotExist(erByOpenSrcFile) {
return "", erByOpenSrcFile
}
//如文件输出目录fileOutDir不存在则自动创建
outFileDir, erByOpenFileOutDir := os.Open(fileOutDir)
if erByOpenFileOutDir != nil && os.IsNotExist(erByOpenFileOutDir) {
erByCreateFileOutDir := os.MkdirAll(fileOutDir, os.ModePerm)
if erByCreateFileOutDir != nil {
fmt.Println("File ouput dir create error.....", erByCreateFileOutDir.Error())
return "", erByCreateFileOutDir
}
}
//关闭流
defer func() {
_ = srcFile.Close()
_ = outFileDir.Close()
}()
//convert
cmd := exec.Command(command, "--invisible", "--language=zh-CN", "--convert-to", converterType,
fileSrcPath, "--outdir", fileOutDir)
byteByStat, errByCmdStart := cmd.Output()
//命令调用转换失败
if errByCmdStart != nil {
return "", errByCmdStart
}
//success
fileOutPath = fileOutDir + "/" + strings.Split(path.Base(fileSrcPath), ".")[0]
if converterType == "html" {
fileOutPath += ".html"
} else {
fileOutPath += ".pdf"
}
fmt.Println("文件转换成功...", string(byteByStat))
return fileOutPath, nil
}
// 中文转拼音函数
func ToPinYin(words string) string {
defer func() {
if r := recover(); r != nil {
log.Error("ToPinYin:", r)
}
}()
//fmt.Println("words",words)
var name string
flag := 0
for _, n := range words {
flag++
//fmt.Println("n:",strings.Replace(string(n)," ","",-1))
if (strings.Compare(strings.Replace(string(n), " ", "", -1), "覃") == 0) && (flag == 1) {
name += firstNamePin(string(n))
flag = 0
} else if (strings.Compare(strings.Replace(string(n), " ", "", -1), "区") == 0) && (flag == 1) {
name += firstNamePin(string(n))
flag = 0
} else if (strings.Compare(strings.Replace(string(n), " ", "", -1), "牟") == 0) && (flag == 1) {
name += firstNamePin(string(n))
flag = 0
} else if (strings.Compare(strings.Replace(string(n), " ", "", -1), "单") == 0) && (flag == 1) {
name += firstNamePin(string(n))
flag = 0
} else {
// str, err := pinyin.New(string(n)).Split("").Mode(pinyin.InitialsInCapitals).Convert()
// if err != nil {
// fmt.Println("转换拼音异常...")
// } else {
// name += str
// }
}
}
return name
}
// 多音字解决方法,尤其是姓氏
func firstNamePin(name string) string {
defer func() {
if r := recover(); r != nil {
log.Error("firstNamePin:", r)
}
}()
var n string
//fmt.Println("name:",name)
n = ""
if strings.Compare(name, "覃") == 0 {
n = "Qin"
} else if strings.Compare(name, "区") == 0 {
n = "Ou"
} else if strings.Compare(name, "牟") == 0 {
n = "Mu"
} else if strings.Compare(name, "单") == 0 {
n = "Shan"
}
return n
}
func DeleteFile(Path string) (total int64, err error) {
defer func() {
if r := recover(); r != nil {
log.Error("DeleteFile:", r)
}
}()
if Path != "" {
err = os.Remove(Path) //删除文件
if err != nil {
//如果删除失败则输出 file remove Error!
fmt.Println("file remove Error!")
//输出错误详细信息
fmt.Printf("%s", err)
return 0, err
}
return 1, nil
}
return 2, nil
}
func PathExists(path string) (bool, error) {
defer func() {
if r := recover(); r != nil {
log.Error("PathExists:", r)
}
}()
_, err := os.Stat(path)
if err != nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, nil
}
/**
* 判断文件是否存在 存在返回 true 不存在返回false
*/
func CheckFileIsExists(filename string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("CheckFileIsExists:", r)
}
}()
var exist = true
if _, err := os.Stat(filename); os.IsNotExist(err) {
exist = false
}
return exist
}
/*
*
判断文件是否存在
*/
func IsDirs(fileAddr string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("IsDirs:", r)
}
}()
s, err := os.Stat(fileAddr)
if err != nil {
log.Println(err)
return false
}
return s.IsDir()
}
/*
极光推送go向Android推送消息
*/
// func JpushGoSend(tags []string, aliasPhone []string, context string, state, jpushState int) error {
// //state区分推送方式 jpushState :区分推送消息的类别;
// //构建要推送的平台: jpushclient.Platform
// var pf jpushclient.Platform
// pf.Add(jpushclient.ANDROID)
// //构建接收听众: jpushclient.Audience
// var ad jpushclient.Audience
// if state == JpushTag {
// //推送 通知公告
// if tags[0] == AdminLevelOne {
// //极光推送 - 所有
// //一级管理员发布,所有人员接收
// ad.All()
// } else {
// //极光推送 - tag
// //二级或者三级管理员发布下属app人员接收
// ad.SetTag(tags)
// }
// } else {
// //极光推送 - 别名 用app用户的登录手机号当作别名
// ad.SetAlias(aliasPhone)
// }
// //构建通知 jpushclient.Notice或者消息 jpushclient.Message
// //Notice
// var notice jpushclient.Notice
// notice.SetAlert("alert_test")
// if jpushState == JpushStateNotice {
// //推送通知公告
// notice.SetAndroidNotice(&jpushclient.AndroidNotice{Title: "通知公告", Alert: context})
// } else {
// //推送新单
// notice.SetAndroidNotice(&jpushclient.AndroidNotice{Title: "新派xx单", Alert: context})
// }
// //构建jpushclient.PayLoad
// payload := jpushclient.NewPushPayLoad()
// payload.SetPlatform(&pf)
// payload.SetAudience(&ad)
// payload.SetNotice(&notice)
// bytes, _ := payload.ToBytes()
// fmt.Printf("%s\r\n", string(bytes))
// //构建PushClient发出推送
// c := jpushclient.NewPushClient(masterSecret, appKey)
// r, err := c.Send(bytes)
// if err != nil {
// fmt.Printf("err:%s", err.Error())
// return err
// } else {
// fmt.Printf("ok:%s", r)
// }
// return nil
// }
func intToChinese(num int) string {
defer func() {
if r := recover(); r != nil {
log.Error("intToChinese:", r)
}
}()
chineseNums := []string{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}
chineseUnits := []string{"", "十", "百", "千"}
chineseBigUnits := []string{"", "万", "亿", "万亿"} // 可根据需求扩展更大的单位
var result strings.Builder
zero := true // 是否需要补零
unit := 0 // 当前的单位
for num > 0 {
n := num % 10
if n > 0 {
if zero {
result.WriteString(chineseNums[0]) // 如果需要补零,则先补零
}
result.WriteString(chineseNums[n])
result.WriteString(chineseUnits[unit])
zero = false
} else {
zero = true
}
unit++
if unit == 4 { // 单位循环
unit = 0
result.WriteString(chineseBigUnits[unit])
}
num /= 10
}
return reverseString(result.String())
}
// 反转字符串
func reverseString(s string) string {
defer func() {
if r := recover(); r != nil {
log.Error("reverseString:", r)
}
}()
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
func MapsAreEqual(map1, map2 map[string]interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("MapsAreEqual:", r)
}
}()
// 检查长度是否相等
if len(map1) != len(map2) {
return false
}
// 遍历 map1 的键值对
for key, value := range map1 {
// 检查 map2 中是否存在相同的键
if value2, ok := map2[key]; ok {
// 递归比较值
if !valuesAreEqual(value, value2) {
return false
}
} else {
// map2 中不存在相同的键
return false
}
}
return true
}
func valuesAreEqual(value1, value2 interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("valuesAreEqual:", r)
}
}()
// 判断类型是否相同
if reflect.TypeOf(value1) != reflect.TypeOf(value2) {
return false
}
// 对不同类型的值进行比较
switch value1 := value1.(type) {
case map[string]interface{}:
return MapsAreEqual(value1, value2.(map[string]interface{}))
case []interface{}:
return slicesAreEqual(value1, value2.([]interface{}))
case []map[string]interface{}:
return slicesmapAreEqual(value1, value2.([]map[string]interface{}))
case int, int32, int64, uint, uint32, uint64, float32, float64, string, bool, time.Time:
// 基本类型直接比较值
return value1 == value2
default:
return structValuesAreEqual(value1, value2)
}
}
func structValuesAreEqual(value1, value2 interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("structValuesAreEqual:", r)
}
}()
// 获取 value1 和 value2 的反射值
v1 := reflect.ValueOf(value1)
v2 := reflect.ValueOf(value2)
// 判断类型是否相同
if v1.Type() != v2.Type() {
return false
}
// 遍历结构体的字段
for i := 0; i < v1.NumField(); i++ {
// 获取字段的反射值和名称
field1 := v1.Field(i)
field2 := v2.Field(i)
name := v1.Type().Field(i).Name
// 递归比较属性值
if !valuesAreEqual(field1.Interface(), field2.Interface()) {
fmt.Printf("%s: %v != %v\n", name, field1.Interface(), field2.Interface())
return false
}
}
return true
}
func slicesmapAreEqual(slice1, slice2 []map[string]interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("slicesmapAreEqual:", r)
}
}()
// 检查长度是否相等
if len(slice1) != len(slice2) {
return false
}
// 递归比较每个元素
for i := 0; i < len(slice1); i++ {
if !valuesAreEqual(slice1[i], slice2[i]) {
return false
}
}
return true
}
func slicesAreEqual(slice1, slice2 []interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("slicesAreEqual:", r)
}
}()
// 检查长度是否相等
if len(slice1) != len(slice2) {
return false
}
// 递归比较每个元素
for i := 0; i < len(slice1); i++ {
if !valuesAreEqual(slice1[i], slice2[i]) {
return false
}
}
return true
}
// IsNullOrEmpty 检查字符串是否为空或为nil
func IsNullOrEmpty(s *string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("IsNullOrEmpty:", r)
}
}()
if s == nil || *s == "" {
return true
}
return false
}
func Contains(slice []interface{}, item interface{}) bool {
defer func() {
if r := recover(); r != nil {
log.Error("Contains:", r)
}
}()
for _, sliceItem := range slice {
if sliceItem == item {
return true
}
}
return false
}
func CreateMutiDir(filePath string) error {
defer func() {
if r := recover(); r != nil {
log.Error("CreateMutiDir:", r)
}
}()
if !isExist(filePath) {
err := os.MkdirAll(filePath, os.ModePerm)
if err != nil {
fmt.Println("创建文件夹失败,error info:", err)
return err
}
return err
}
return nil
}
// 判断所给路径文件/文件夹是否存在(返回true是存在)
func isExist(path string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("isExist:", r)
}
}()
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
func IsExist(path string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("IsExist:", r)
}
}()
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
func StructToMap(obj interface{}) map[string]interface{} {
defer func() {
if r := recover(); r != nil {
log.Error("StructToMap:", r)
}
}()
objValue := reflect.ValueOf(obj)
if objValue.Kind() == reflect.Ptr {
objValue = objValue.Elem()
}
if objValue.Kind() != reflect.Struct {
panic("传入的不是结构体类型")
}
objType := objValue.Type()
result := make(map[string]interface{})
for i := 0; i < objValue.NumField(); i++ {
field := objValue.Field(i)
fieldName := objType.Field(i).Name
result[fieldName] = field.Interface()
}
return result
}
func GetLocalIP() string {
defer func() {
if r := recover(); r != nil {
log.Error("GetLocalIP:", r)
}
}()
var IPAddress string = "127.0.0.1"
netInterfaces, err := net.Interfaces()
if err != nil {
fmt.Println("net.Interfaces failed, err:", err.Error())
return IPAddress
}
for i := 0; i < len(netInterfaces); i++ {
if (netInterfaces[i].Flags & net.FlagUp) != 0 {
addrs, _ := netInterfaces[i].Addrs()
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
IPAddress = ipnet.IP.String()
return IPAddress
}
}
}
}
}
return IPAddress
}
func Md5func(filePath string) string {
defer func() {
if r := recover(); r != nil {
log.Error("Md5func:", r)
}
}()
// 打开文件
file, err := os.Open(filePath)
if err != nil {
fmt.Println("无法打开文件:", err)
return ""
}
defer file.Close()
// 创建一个 MD5 的哈希对象
hash := md5.New()
// 将文件内容拷贝到哈希对象中
if _, err := io.Copy(hash, file); err != nil {
fmt.Println("无法读取文件内容:", err)
return ""
}
// 计算 MD5 哈希值
hashInBytes := hash.Sum(nil)
// 将字节转换为十六进制表示
md5Hash := hex.EncodeToString(hashInBytes)
fmt.Printf("文件 %s 的MD5哈希值为: %s\n", filePath, md5Hash)
return md5Hash
}
func PlayMusic(musicPath string) error {
defer func() {
if r := recover(); r != nil {
log.Error("PlayMusic:", r)
}
}()
// 打开wav文件
f, err := os.Open(musicPath)
if err != nil {
// log.Fatal(err) Fatal报错直接退出程序
log.Println(err)
return err
}
defer f.Close()
// 解码wav文件
// streamer, format, err := wav.Decode(f)
// if err != nil {
// log.Println(err)
// return err
// }
// 初始化音频驱动
// err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
// if err != nil {
// log.Println(err)
// return err
// }
// 异步播放音频
// go func() {
// done := make(chan bool)
// speaker.Play(beep.Seq(streamer, beep.Callback(func() {
// done <- true
// })))
// // 等待音频播放完成
// <-done
// }()
// 播放音频
// done := make(chan bool)
// speaker.Play(beep.Seq(streamer, beep.Callback(func() {
// done <- true
// })))
// fmt.Println("Playing audio...")
// // 等待音频播放完成
// <-done
// 等待一段时间确保音频播放完毕
// time.Sleep(time.Duration(streamer.Len()) / time.Duration(format.SampleRate))
// time.Sleep(5 * time.Second)
return nil
}
// func PlayMusics(musicPath string) error {
// // 打开wav文件
// f, err := os.Open(musicPath)
// if err != nil {
// log.Println(err)
// return err
// }
// defer f.Close()
// // 解码wav文件
// streamer, format, err := wav.Decode(f)
// if err != nil {
// log.Println(err)
// return err
// }
// // 初始化音频驱动
// err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
// if err != nil {
// log.Println(err)
// return err
// }
// // 播放音频
// done := make(chan struct{})
// // 启动一个新的goroutine来播放音频
// go func() {
// defer func() {
// if err := recover(); err != nil {
// log.WithFields(log.Fields{
// "function": "PlayMusics",
// "error": err,
// }).Error("PlayMusics")
// // log.Printf("OpenDevice: %v", err) // 记录错误信息到日志
// fmt.Println("PlayMusics:", err) // 打印错误信息
// }
// }()
// defer close(done)
// // 使用非阻塞方式播放
// speaker.Play(streamer)
// }()
// fmt.Println("Playing audio...")
// // 等待足够长的时间确保音频播放完毕
// time.Sleep(5 * time.Second)
// // 等待音频播放完成
// <-done
// return nil
// }
// FormatTime 将时间字符串格式化为 HH:MM 格式
func FormatTime(timeStr string) string {
defer func() {
if r := recover(); r != nil {
log.Error("FormatTime:", r)
}
}()
// 按照冒号分割小时和分钟
parts := strings.Split(timeStr, ":")
if len(parts) != 2 {
return timeStr // 返回原始字符串如果格式不对
}
// 将小时和分钟转换为整数
hours, err1 := strconv.Atoi(parts[0])
minutes, err2 := strconv.Atoi(parts[1])
if err1 != nil || err2 != nil {
return timeStr // 返回原始字符串如果转换失败
}
// 格式化小时和分钟为两位数字
return fmt.Sprintf("%02d:%02d", hours, minutes)
}
func FormatTimes(now time.Time, format string) string {
// 替换常见的日期时间格式标记
format = strings.ReplaceAll(format, "YYYY", "2006")
format = strings.ReplaceAll(format, "YY", "06")
// 特殊处理带中文的月和日
if strings.Contains(format, "月") {
format = strings.ReplaceAll(format, "MM", "1")
} else {
format = strings.ReplaceAll(format, "MM", "01")
}
if strings.Contains(format, "日") {
format = strings.ReplaceAll(format, "DD", "2")
} else {
format = strings.ReplaceAll(format, "DD", "02")
}
format = strings.ReplaceAll(format, "HH", "15")
format = strings.ReplaceAll(format, "mm", "04")
format = strings.ReplaceAll(format, "ss", "05")
return now.Format(format)
}
func GetAvailableEndpoint() string {
primaryIP := LoadConfig().SeverData.ServerIP
backupIP := LoadConfig().SeverData.BackUpServerIP // 替换为实际备选IP
if IsIPReachable(primaryIP) {
return primaryIP
}
if IsIPReachable(backupIP) {
return backupIP
}
return ""
}
func IsIPReachable(ip string) bool {
conn, err := net.DialTimeout("ip4:icmp", ip, 10*time.Millisecond)
if err != nil {
return false
}
conn.Close()
return true
}