PaymentCenter/app/utils/util.go

511 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package utils
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/config"
"context"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/bytedance/sonic"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
"github.com/pkg/errors"
"github.com/qit-team/snow-core/redis"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
mrand "math/rand"
"net"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strings"
"time"
"unicode"
)
const (
CODE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
codeLen = 20
)
func GetHostIp() string {
conn, err := net.Dial("udp", "8.8.8.8:53")
if err != nil {
fmt.Println("get current host ip err: ", err)
return ""
}
addr := conn.LocalAddr().(*net.UDPAddr)
ip := strings.Split(addr.String(), ":")[0]
return ip
}
func Log(c *gin.Context, name string, msg ...interface{}) {
_, file, line, _ := runtime.Caller(1)
var datetime = time.Now().Format("2006-01-02 15:04:05")
fmt.Println(name, msg, file, line, datetime)
}
func GetRealKey(key string) string {
return config.GetConf().ServiceName + ":" + key
}
// MD5加密
func SToMd5(data string) string {
r := md5.Sum([]byte(data))
return hex.EncodeToString(r[:])
}
/**
* 编码 整数 为 base62 字符串
*/
func Encode(number int64) string {
if number == 0 {
return "0"
}
result := make([]byte, 0)
for number > 0 {
round := number / codeLen
remain := number % codeLen
result = append(result, CODE62[remain])
number = round
}
return string(result)
}
// 生成用户touken
func GeneratorToken(playerName string, playerId int) string {
//去生成一个token返回给客户端
m5 := SToMd5(playerName + time.Now().String())
var pid = int64(playerId)
bsCode := Encode(pid)
tk := m5 + bsCode
//将token放入redis
_, err := redis.GetRedis(redis.SingletonMain).SetEX(context.Background(), GetRealKey(common.TOKEN_PRE+tk), playerId, time.Duration(3600)*time.Second).Result()
if err != nil {
Log(nil, "token", err)
}
return tk
}
// ExistFile 检查给定的文件是否存在
func ExistFile(dirPath string) (exist bool, err error) {
// 使用filepath.Abs获取绝对路径确保我们处理的是实际存在的路径
absPath, err := filepath.Abs(dirPath)
if err != nil {
return exist, fmt.Errorf("failed to get absolute path: %v", err)
}
// 使用os.Stat检查路径是否存在
_, err = os.Stat(absPath)
if errors.Is(err, os.ErrNotExist) {
return exist, nil
}
return true, err
}
// StrToTimeShanghai 字符串转时间
func StrToTimeShanghai(strTime string) (t time.Time, err error) {
location, _ := time.LoadLocation("Asia/Shanghai")
return time.ParseInLocation(time.DateTime, strTime, location)
}
func GenIncrementId(tableName string) (int, error) {
var id, err = redis.GetRedis().Incr(context.Background(), GetRealKey(tableName)).Result()
return int(id), err
}
// CheckOrCreateYmdDirectory 创建ymd目录
func CheckOrCreateYmdDirectory(dirPath string) (SavePath string, err error) {
// 使用filepath.Abs获取绝对路径确保我们处理的是实际存在的路径
now := time.Now()
year := now.Year()
month := now.Month()
day := now.Day()
savePath := filepath.Join("uploads", dirPath, fmt.Sprintf("%d", year), fmt.Sprintf("%d", month), fmt.Sprintf("%d", day))
absPath, err := filepath.Abs(savePath)
if err != nil {
return SavePath, fmt.Errorf("failed to get absolute path: %v", err)
}
// 使用os.Stat检查路径是否存在
info, err := os.Stat(absPath)
if err != nil {
if !os.IsNotExist(err) {
return SavePath, fmt.Errorf("error checking directory: %v", err)
}
// 目录不存在,尝试创建
err = os.MkdirAll(absPath, 0755) // 0755是默认权限可以按需调整
if err != nil {
return SavePath, fmt.Errorf("failed to create directory: %v", err)
}
} else if !info.IsDir() {
return SavePath, fmt.Errorf("%s exists but it's not a directory", absPath)
}
SavePath = absPath
return SavePath, nil
}
// CheckOrCreateDirectory 检查给定的目录是否存在,如果不存在则创建它
func CheckOrCreateDirectory(dirPath string) error {
// 使用filepath.Abs获取绝对路径确保我们处理的是实际存在的路径
absPath, err := filepath.Abs(dirPath)
if err != nil {
return fmt.Errorf("failed to get absolute path: %v", err)
}
// 使用os.Stat检查路径是否存在
info, err := os.Stat(absPath)
if err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("error checking directory: %v", err)
}
// 目录不存在,尝试创建
err = os.MkdirAll(absPath, 0755) // 0755是默认权限可以按需调整
if err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
} else if !info.IsDir() {
return fmt.Errorf("%s exists but it's not a directory", absPath)
}
return nil
}
func ToSnakeCase(s string) string {
ch, en := splitChineseEnglish(s)
fmt.Println(ch, en)
re := regexp.MustCompile("([a-z0-9])([A-Z])")
snake := re.ReplaceAllString(en, "${1}_${2}")
return strings.ToLower(snake) + ch
}
func splitChineseEnglish(input string) (chinese string, english string) {
var index = findChineseStartIndex(input)
return input[index:], input[0:index]
}
func findChineseStartIndex(input string) int {
runes := []rune(input)
for i, r := range runes {
if unicode.Is(unicode.Han, r) {
return i
}
}
return -1 // 如果没有找到中文字符,返回-1
}
func DownloadFileFromOss(url, savePath string) error {
_, bucket, err := AliOssClient()
if err != nil {
return err
}
err = bucket.GetObjectToFileWithURL(url, savePath)
return err
}
func EntityCopy(dst, src interface{}) {
dstValue := reflect.ValueOf(dst).Elem()
srcValue := reflect.ValueOf(src).Elem()
for i := 0; i < srcValue.NumField(); i++ {
srcField := srcValue.Field(i)
srcName := srcValue.Type().Field(i).Name
dstFieldByName := dstValue.FieldByName(srcName)
if dstFieldByName.IsValid() {
switch dstFieldByName.Kind() {
case reflect.Ptr:
switch srcField.Kind() {
case reflect.Ptr:
if srcField.IsNil() {
dstFieldByName.Set(reflect.New(dstFieldByName.Type().Elem()))
} else {
dstFieldByName.Set(srcField)
}
default:
dstFieldByName.Set(srcField.Addr())
}
default:
switch srcField.Kind() {
case reflect.Ptr:
if srcField.IsNil() {
dstFieldByName.Set(reflect.Zero(dstFieldByName.Type()))
} else {
dstFieldByName.Set(srcField.Elem())
}
default:
if srcField.Type().Name() == "Time" {
if (srcField.Interface().(time.Time).Unix()) < 1 {
dstFieldByName.Set(reflect.ValueOf(""))
} else {
dstFieldByName.Set(reflect.ValueOf(srcField.Interface().(time.Time).Format("2006-01-02 15:04:05")))
}
} else {
dstFieldByName.Set(srcField)
}
}
}
}
}
}
// AliOssClient 返回Oss客户链接
func AliOssClient() (client *oss.Client, Bucket *oss.Bucket, err error) {
/*
oss 的相关配置信息
*/
bucketName := config.GetConf().AliOss.BucKet
endpoint := config.GetConf().AliOss.EndPoint
accessKeyId := config.GetConf().AliOss.AccessKey
accessKeySecret := config.GetConf().AliOss.AccessKeySecret
//domain := config.GetConf().AliOss.Domain
//Dir := config.GetConf().AliOss.Dir
//创建OSSClient实例
client, err = oss.New(endpoint, accessKeyId, accessKeySecret)
if err != nil {
return nil, nil, err
}
// 获取存储空间
Bucket, err = client.Bucket(bucketName)
if err != nil {
return nil, nil, err
}
return client, Bucket, nil
}
func GetSHA256HashCode(message []byte) string {
//方法一:
//创建一个基于SHA256算法的hash.Hash接口的对象
hash := sha256.New()
//输入数据
hash.Write(message)
//计算哈希值
bytes := hash.Sum(nil)
//将字符串编码为16进制格式,返回字符串
hashCode := hex.EncodeToString(bytes)
//返回哈希值
return hashCode
//方法二:
//bytes2:=sha256.Sum256(message)//计算哈希值返回一个长度为32的数组
//hashcode2:=hex.EncodeToString(bytes2[:])//将数组转换成切片转换成16进制返回字符串
//return hashcode2
}
func GetSHA512HashCode(message []byte) string {
hash := sha512.New()
hash.Write(message)
bytes := hash.Sum(nil)
hashCode := hex.EncodeToString(bytes)
return hashCode
}
// SM2Encode sm2公钥加密
func SM2Encode(pubKey string, plaintext string, mode int) (string, error) {
pubMen, err := x509.ReadPublicKeyFromHex(pubKey)
if err != nil {
return "", err
}
msg := []byte(plaintext)
ciphertxt, err := sm2.Encrypt(pubMen, msg, rand.Reader, mode)
if err != nil {
return "", err
}
return hex.EncodeToString(ciphertxt), nil
}
// SM2Decode sm2私钥解密
func SM2Decode(privKey string, data string, mode int) (string, error) {
priv, err := x509.ReadPrivateKeyFromHex(privKey)
if err != nil {
return "", err
}
ciphertext, err := hex.DecodeString(data)
if err != nil {
return "", err
}
plaintext, err := sm2.Decrypt(priv, []byte(ciphertext), mode)
if err != nil {
return "", err
}
return string(plaintext), nil
}
func GenerateOrderNumber() string {
// 生成当前日期部分例如20231008
datePart := time.Now().Format("20060102150405")
// 生成随机数部分4位随机数
mrand.Seed(time.Now().UnixNano())
randomPart := fmt.Sprintf("%04d", mrand.Intn(10000))
// 添加固定前缀
prefix := "SN"
// 最终的订单号由前缀、日期和随机数部分组成
orderNumber := fmt.Sprintf("%s%s%s", prefix, datePart, randomPart)
return orderNumber
}
func IsNil(x interface{}) bool {
if x == nil {
return true
}
rv := reflect.ValueOf(x)
return rv.Kind() == reflect.Ptr && rv.IsNil()
}
type User struct {
Id int
Phone string
}
func GeneratorJwtToken(user User) string {
// 定义一个用于签名的密钥
secretKey := []byte(config.GetConf().Jwt.SecretKey)
// 创建一个MapClaims对象用于存放自定义的声明信息
claims := jwt.MapClaims{
"id": user.Id,
"phone": user.Phone,
"exp": time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间为24小时后
}
// 使用HS256算法创建一个Token对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 使用密钥对Token进行签名生成JWT字符串
tokenString, err := token.SignedString(secretKey)
if err != nil {
fmt.Println("Failed to create token:", err)
return ""
}
return tokenString
}
type Claims struct {
Id int
Phone string
jwt.StandardClaims
}
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
Claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, Claims, func(token *jwt.Token) (i interface{}, err error) {
return []byte(config.GetConf().Jwt.SecretKey), nil
})
return token, Claims, err
}
func PopLast[T any](slice *[]T) (T, bool) {
if len(*slice) == 0 {
var zero T
return zero, false
}
index := len(*slice) - 1
lastElement := (*slice)[index]
*slice = (*slice)[:index] // 移除最后一个元素
return lastElement, true
}
func SonicApiDataToStruct(data interface{}, structInterFace interface{}) (dataStruct interface{}, err error) {
bytes, err := sonic.Marshal(data)
if err != nil {
return nil, err
}
err = sonic.Unmarshal(bytes, &structInterFace)
return structInterFace, err
}
func ContainsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}
// 判断切片是否包含指定字符串
func SliceInStr(s string, slice []string) bool {
for _, v := range slice {
if s == v {
return true
}
}
return false
}
// 判断支付方式支付宝或者微信
func PayType(payChannel int) int {
switch payChannel {
case common.PAY_CHANNEL_WECHAT_H5, common.PAY_CHANNEL_WECHAT_JSAPI, common.PAY_CHANNEL_WECHAT_NATIVE, common.PAY_CHANNEL_WECHAT_APP, common.PAY_CHANNEL_WECHAT_MINI:
return common.PAY_CHANNLE_TYPE_WECHAT
case common.PAY_CHANNEL_ALIPAY_JSAPI, common.PAY_CHANNEL_ALIPAY_WEB, common.PAY_CHANNEL_ALIPAY_MINI, common.PAY_CHANNEL_ALIPAY_PC:
return common.PAY_CHANNLE_TYPE_ZFB
default:
return 0
}
}
// redis控制访问频率
func KeyExistsOrSet(key string, expire int) bool {
redisClient := redis.GetRedis()
// 假设Exists方法返回的是int64类型表示键是否存在0为不存在1为存在
if exists, err := redisClient.Exists(context.Background(), key).Result(); err == nil && exists == 1 {
return true
} else if err != nil {
// 处理错误情况,可以根据实际情况记录日志或返回错误
Log(nil, "redis_Exists异常", err)
return false
}
// 如果键不存在则设置键并返回false
if err := redisClient.SetEX(context.Background(), key, "", time.Duration(expire)*time.Second).Err(); err != nil {
// 处理设置键时的错误情况
Log(nil, "redis_SetEX异常", err)
return false
}
return false
}
// AcquireLock 尝试获取分布式锁,localValue用于检测是否是同一个客户端持有的锁锁
func AcquireLock(key, lockValue string, expired time.Duration) (bool, error) {
ctx := context.Background()
rd := redis.GetRedis()
result := rd.SetNX(ctx, key, lockValue, expired)
return result.Val(), result.Err()
}
// 释放锁
func ReleaseLock(key string, lockValue string) (bool, error) {
rd := redis.GetRedis()
result := rd.Eval(context.Background(), "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", []string{key}, lockValue)
return result.Val() == int64(1), result.Err()
}
// 判断切片是否包含指定
func Contains[T comparable](s T, slice []T) bool {
for _, v := range slice {
if s == v {
return true
}
}
return false
}