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 CheckIPReachable(ip string, port int, timeout time.Duration) bool { conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout) if err != nil { return false } conn.Close() return true } func GetAvailableEndpoint() string { primaryIP := LoadConfig().SeverData.ServerIP backupIP := LoadConfig().SeverData.BackUpServerIP // 替换为实际备选IP port := 9800 timeout := 3 * time.Second if CheckIPReachable(primaryIP, port, timeout) { return primaryIP } if CheckIPReachable(backupIP, port, timeout) { return backupIP } return "" }