|
|
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的命令(具体以版本为准);win:soffice; linux:libreoffice6.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(¬ice)
|
|
|
|
|
|
// 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, "9800") {
|
|
|
return primaryIP
|
|
|
}
|
|
|
if IsIPReachable(backupIP, "9800") {
|
|
|
return backupIP
|
|
|
}
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
func IsIPReachable(ip string, port string) bool {
|
|
|
conn, err := net.DialTimeout("tcp", ip+":"+port, 10*time.Millisecond)
|
|
|
if err != nil {
|
|
|
return false
|
|
|
}
|
|
|
defer conn.Close()
|
|
|
return true
|
|
|
}
|