first commit
This commit is contained in:
328
tool/helper.go
Normal file
328
tool/helper.go
Normal file
@ -0,0 +1,328 @@
|
||||
package tool
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// GetOrderNumber 获取订单号
|
||||
func GetOrderNumber() string {
|
||||
parse, _ := time.Parse(time.DateTime, "2021-04-27 00:00:00")
|
||||
hours := time.Now().Sub(parse).Hours()
|
||||
day := math.Floor(hours / 24)
|
||||
prefix := fmt.Sprintf("%05d", int64(day))
|
||||
format := time.Now().Format("") + "%0" + strconv.Itoa(10) + "d"
|
||||
n := math.Pow10(10)
|
||||
return prefix + fmt.Sprintf(format, rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(int64(n)))
|
||||
}
|
||||
|
||||
// ByteFmt 格式化显示文件大小
|
||||
func ByteFmt(size int64) string {
|
||||
var unitArr = []string{"B", "KB", "MB", "GB", "TB", "EB"}
|
||||
if size == 0 {
|
||||
return "unknown"
|
||||
}
|
||||
fs := float64(size)
|
||||
p := int(math.Log(fs) / math.Log(1024))
|
||||
val := fs / math.Pow(1024, float64(p))
|
||||
_, frac := math.Modf(val)
|
||||
if frac > 0 {
|
||||
return fmt.Sprintf("%.1f%s", math.Floor(val*10)/10, unitArr[p])
|
||||
} else {
|
||||
return fmt.Sprintf("%d%s", int(val), unitArr[p])
|
||||
}
|
||||
}
|
||||
|
||||
// UniqueArray 切片唯一值
|
||||
func UniqueArray[T comparable](array []T) []T {
|
||||
result := make([]T, 0, len(array))
|
||||
temp := map[T]struct{}{}
|
||||
for _, item := range array {
|
||||
if _, ok := temp[item]; !ok {
|
||||
temp[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// InArray 是否在切片
|
||||
func InArray[T comparable](item T, array []T) bool {
|
||||
for _, s := range array {
|
||||
if item == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ChunkArray 分割切片
|
||||
func ChunkArray[T comparable](array []T, size int) (ss [][]T) {
|
||||
if size <= 0 || len(array) <= size {
|
||||
return [][]T{array}
|
||||
}
|
||||
mod := len(array) % size
|
||||
k := len(array) / size
|
||||
var end int
|
||||
if mod == 0 {
|
||||
end = k
|
||||
} else {
|
||||
end = k + 1
|
||||
}
|
||||
for i := 0; i < end; i++ {
|
||||
if i != k {
|
||||
ss = append(ss, array[i*size:(i+1)*size])
|
||||
} else {
|
||||
ss = append(ss, array[i*size:])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsChinese 判断字符串是否包含中文
|
||||
func IsChinese(str string) bool {
|
||||
var count int
|
||||
for _, v := range str {
|
||||
if unicode.Is(unicode.Han, v) {
|
||||
count++
|
||||
break
|
||||
}
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// RandInt 随机数
|
||||
func RandInt(max int) int {
|
||||
if max <= 0 {
|
||||
return 0
|
||||
}
|
||||
return rand.Intn(max)
|
||||
}
|
||||
|
||||
// FindIPv4 字符串中查找IP4
|
||||
func FindIPv4(input string) string {
|
||||
partIp := "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
|
||||
must := partIp + "\\." + partIp + "\\." + partIp + "\\." + partIp
|
||||
matchMe := regexp.MustCompile(must)
|
||||
return matchMe.FindString(input)
|
||||
}
|
||||
|
||||
// RandString 随机字符串
|
||||
func RandString(n int) string {
|
||||
var letterRunes = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[RandInt(len(letterRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// IP4ToInt ip4地址转Int类型
|
||||
func IP4ToInt(ip net.IP) int {
|
||||
ipSplit := strings.Split(ip.To4().String(), ".")
|
||||
var ipInt = 0
|
||||
var pos uint = 24
|
||||
for _, ipSeg := range ipSplit {
|
||||
tempInt, _ := strconv.Atoi(ipSeg)
|
||||
tempInt = tempInt << pos
|
||||
ipInt = ipInt | tempInt
|
||||
pos -= 8
|
||||
}
|
||||
return ipInt
|
||||
}
|
||||
|
||||
// IP4IntToString 反序列化Int为IP4地址
|
||||
func IP4IntToString(ipInt int) string {
|
||||
ipSplit := make([]string, 4)
|
||||
var length = len(ipSplit)
|
||||
buffer := bytes.NewBufferString("")
|
||||
for i := 0; i < length; i++ {
|
||||
tempInt := ipInt & 0xFF
|
||||
ipSplit[length-i-1] = strconv.Itoa(tempInt)
|
||||
ipInt = ipInt >> 8
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
buffer.WriteString(ipSplit[i])
|
||||
if i < length-1 {
|
||||
buffer.WriteString(".")
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CompareVersion 比较`x.y.z`类型版本号大小
|
||||
func CompareVersion(verCurrent, verUpdate string) int {
|
||||
versionA := strings.Split(verCurrent, ".")
|
||||
versionB := strings.Split(verUpdate, ".")
|
||||
|
||||
for i := len(versionA); i < 4; i++ {
|
||||
versionA = append(versionA, "0")
|
||||
}
|
||||
for i := len(versionB); i < 4; i++ {
|
||||
versionB = append(versionB, "0")
|
||||
}
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
version1, _ := strconv.Atoi(versionA[i])
|
||||
version2, _ := strconv.Atoi(versionB[i])
|
||||
if version1 == version2 {
|
||||
continue
|
||||
} else if version1 > version2 {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetLocalIP 获取内网本机ipv4地址
|
||||
func GetLocalIP() (string, error) {
|
||||
var address []net.Addr
|
||||
eth0Interface, err := net.InterfaceByName("eth0")
|
||||
if err == nil {
|
||||
address, err = eth0Interface.Addrs()
|
||||
} else {
|
||||
address, err = net.InterfaceAddrs()
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, addr := range address {
|
||||
if it, ok := addr.(*net.IPNet); ok && !it.IP.IsLoopback() {
|
||||
if it.IP.To4() != nil {
|
||||
return it.IP.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// GetInternetIP 获取外网本机ipv4地址
|
||||
func GetInternetIP() (string, error) {
|
||||
resp, err := http.Get("https://api.ipify.org")
|
||||
if err != nil {
|
||||
resp, err = http.Get("https://icanhazip.com")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
s, readErr := io.ReadAll(resp.Body)
|
||||
if readErr != nil {
|
||||
return "", readErr
|
||||
}
|
||||
return string(s), nil
|
||||
}
|
||||
|
||||
// IsExists 检测指定路径文件或者文件夹是否存在
|
||||
func IsExists(path string) bool {
|
||||
_, err := os.Stat(path) //os.Stat获取文件信息
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckWebsiteAddr 检查域名是否正确
|
||||
func CheckWebsiteAddr(addr string) error {
|
||||
if 0 == len(addr) {
|
||||
return errors.New("url is empty")
|
||||
}
|
||||
|
||||
parse, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if "http" != parse.Scheme && "https" != parse.Scheme {
|
||||
return errors.New("url Scheme illegal. err:" + parse.Scheme)
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$`)
|
||||
u := strings.Split(parse.Host, ":")
|
||||
if 2 == len(u) && 0 != len(u[1]) {
|
||||
result := re.FindAllStringSubmatch(u[0], -1)
|
||||
if result == nil {
|
||||
return errors.New("url illegal. err:" + u[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
result := re.FindAllStringSubmatch(parse.Host, -1)
|
||||
if result == nil {
|
||||
return errors.New("url illegal. err:" + parse.Host)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadKeyValConfig 获取本地`a=c\n`类型文件的键值对
|
||||
func LoadKeyValConfig(text string) map[string]string {
|
||||
config := make(map[string]string)
|
||||
r := bufio.NewReader(bytes.NewBufferString(text))
|
||||
for {
|
||||
b, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
s := strings.TrimSpace(string(b))
|
||||
index := strings.Index(s, "=")
|
||||
if index < 0 || strings.HasPrefix(s, "#") {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(s[:index])
|
||||
if len(key) == 0 {
|
||||
continue
|
||||
}
|
||||
value := strings.TrimSpace(s[index+1:])
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
config[key] = value
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// PortIsUse 判断端口是否占用
|
||||
func PortIsUse(port int) bool {
|
||||
_, tcpError := net.DialTimeout("tcp", fmt.Sprintf(":%d", port), time.Millisecond*50)
|
||||
udpAddr, _ := net.ResolveUDPAddr("udp4", fmt.Sprintf(":%d", port))
|
||||
udpConn, udpError := net.ListenUDP("udp", udpAddr)
|
||||
if udpConn != nil {
|
||||
defer func() {
|
||||
_ = udpConn.Close()
|
||||
}()
|
||||
}
|
||||
return tcpError == nil || udpError != nil
|
||||
}
|
||||
|
||||
// ReplaceInvalidFilenameChars 用下划线替换不合法文件名的字符
|
||||
func ReplaceInvalidFilenameChars(filename string) string {
|
||||
pattern := regexp.MustCompile(`[\\/:*?"<>|{}'()!@#$%^&\[\];,+=~·!¥…()—{}、:“”‘’;,《》。?]`)
|
||||
filename = pattern.ReplaceAllString(filename, "_")
|
||||
return strings.ReplaceAll(filename, " ", "")
|
||||
}
|
133
tool/http.go
Normal file
133
tool/http.go
Normal file
@ -0,0 +1,133 @@
|
||||
package tool
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/aes"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/hmac"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/httpclient"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"github.com/tidwall/gjson"
|
||||
netUrl "net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HttpPostByAes(url, productID, key, vi string, params netUrl.Values) (body []byte, err error) {
|
||||
var signatureDatetime = time_parse.CSTLayoutString()
|
||||
unix, _ := time_parse.CSTLayoutStringToUnix(signatureDatetime)
|
||||
params.Set("t", strconv.FormatInt(unix, 10))
|
||||
var sign string
|
||||
sign, err = aes.New(key, vi).EncryptCBC(params.Encode(), false)
|
||||
if err != nil {
|
||||
return body, err
|
||||
}
|
||||
body, err = httpclient.PostForm(
|
||||
url,
|
||||
params,
|
||||
httpclient.WithTTL(time.Second*30),
|
||||
httpclient.WithHeader("Signature", sign),
|
||||
httpclient.WithHeader("Signature-Datetime", signatureDatetime),
|
||||
httpclient.WithHeader("Product-ID", productID),
|
||||
httpclient.WithOnFailedRetry(1, time.Millisecond*200,
|
||||
func(body []byte) (shouldRetry bool) {
|
||||
return len(body) == 0
|
||||
},
|
||||
),
|
||||
)
|
||||
return body, err
|
||||
}
|
||||
|
||||
func HttpPostByAesWithTimeout(url, productID, key, vi string, params netUrl.Values, timeout time.Duration) (body []byte, err error) {
|
||||
var signatureDatetime = time_parse.CSTLayoutString()
|
||||
unix, _ := time_parse.CSTLayoutStringToUnix(signatureDatetime)
|
||||
params.Set("t", strconv.FormatInt(unix, 10))
|
||||
var sign string
|
||||
sign, err = aes.New(key, vi).EncryptCBC(params.Encode(), false)
|
||||
if err != nil {
|
||||
return body, err
|
||||
}
|
||||
body, err = httpclient.PostForm(
|
||||
url,
|
||||
params,
|
||||
httpclient.WithTTL(timeout),
|
||||
httpclient.WithHeader("Signature", sign),
|
||||
httpclient.WithHeader("Signature-Datetime", signatureDatetime),
|
||||
httpclient.WithHeader("Product-ID", productID),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1,
|
||||
func(body []byte) (shouldRetry bool) {
|
||||
return len(body) == 0
|
||||
},
|
||||
),
|
||||
)
|
||||
return body, err
|
||||
}
|
||||
|
||||
func DingTalkAlertMarkdown(accessToken, secret, title, text string) bool {
|
||||
params := netUrl.Values{}
|
||||
timestamp := strconv.FormatInt(time_parse.GetMilliTimestamp(), 10)
|
||||
signData := timestamp + "\n" + secret
|
||||
sign := netUrl.QueryEscape(hmac.New(secret).Sha256ToBase64String(signData))
|
||||
params.Set("access_token", accessToken)
|
||||
params.Set("sign", sign)
|
||||
params.Set("timestamp", timestamp)
|
||||
var content struct {
|
||||
MsgType string `json:"msgtype"`
|
||||
Markdown struct {
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
} `json:"markdown"`
|
||||
}
|
||||
content.MsgType = "markdown"
|
||||
content.Markdown.Title = title
|
||||
content.Markdown.Text = text
|
||||
body, _ := json.Marshal(content)
|
||||
response, err := httpclient.PostJSON(
|
||||
"https://oapi.dingtalk.com/robot/send?"+params.Encode(),
|
||||
body,
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithHeader("Content-Type", "application/json;charset=utf-8"),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1,
|
||||
func(body []byte) (shouldRetry bool) {
|
||||
return len(body) == 0
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return gjson.Get(string(response), "errcode").Int() == 0
|
||||
}
|
||||
|
||||
func DingTalkAlertText(accessToken, secret string, text string) bool {
|
||||
params := netUrl.Values{}
|
||||
timestamp := strconv.FormatInt(time_parse.GetMilliTimestamp(), 10)
|
||||
signData := timestamp + "\n" + secret
|
||||
sign := netUrl.QueryEscape(hmac.New(secret).Sha256ToBase64String(signData))
|
||||
params.Set("access_token", accessToken)
|
||||
params.Set("sign", sign)
|
||||
params.Set("timestamp", timestamp)
|
||||
var content struct {
|
||||
MsgType string `json:"msgtype"`
|
||||
Text struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"text"`
|
||||
}
|
||||
content.MsgType = "text"
|
||||
content.Text.Content = text
|
||||
body, _ := json.Marshal(content)
|
||||
response, err := httpclient.PostJSON(
|
||||
"https://oapi.dingtalk.com/robot/send?"+params.Encode(),
|
||||
body,
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithHeader("Content-Type", "application/json;charset=utf-8"),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1,
|
||||
func(body []byte) (shouldRetry bool) {
|
||||
return len(body) == 0
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return gjson.Get(string(response), "errcode").Int() == 0
|
||||
}
|
Reference in New Issue
Block a user