624 lines
15 KiB
Go
624 lines
15 KiB
Go
package GoUtil
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/aes"
|
||
"crypto/cipher"
|
||
"crypto/md5"
|
||
crand "crypto/rand"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"encoding/gob"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"io"
|
||
"math/rand"
|
||
"net/http"
|
||
"reflect"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
var bufferPool = sync.Pool{
|
||
New: func() interface{} {
|
||
return bytes.NewBuffer(make([]byte, 0, 128*1024))
|
||
},
|
||
}
|
||
|
||
// 实例化一个通过字符串映射函数切片的map
|
||
var eventByName = make(map[string][]*EventObj)
|
||
|
||
type EventObj struct {
|
||
Callback func([]interface{})
|
||
Obj interface{}
|
||
}
|
||
|
||
const (
|
||
SECRET_KEY = ")VQbB(vpy=U(wcp)"
|
||
CONDITION_EQ = "=" // 等于
|
||
CONDITION_GT = ">" // 大于
|
||
CONDITION_LT = "<" // 小于
|
||
CONDITION_GE = ">=" // 大于等于
|
||
CONDITION_LE = "<=" // 小于等于
|
||
|
||
LETTER = "GhCvgqSNTUMVeRfwakiYmcxWKtJQpZrDIBXnPyLsAFdzjHbulE"
|
||
)
|
||
|
||
// 加密字符串
|
||
func Encrypt(plainText string) (string, error) {
|
||
block, err := aes.NewCipher([]byte(SECRET_KEY))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
cipherText := make([]byte, aes.BlockSize+len(plainText))
|
||
iv := cipherText[:aes.BlockSize]
|
||
if _, err := io.ReadFull(crand.Reader, iv); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
stream := cipher.NewCFBEncrypter(block, iv)
|
||
stream.XORKeyStream(cipherText[aes.BlockSize:], []byte(plainText))
|
||
|
||
return base64.URLEncoding.EncodeToString(cipherText), nil
|
||
}
|
||
|
||
// 解密字符串
|
||
func Decrypt(cipherText string) (string, error) {
|
||
cipherTextBytes, err := base64.URLEncoding.DecodeString(cipherText)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
block, err := aes.NewCipher([]byte(SECRET_KEY))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
if len(cipherTextBytes) < aes.BlockSize {
|
||
return "", fmt.Errorf("cipherText too short")
|
||
}
|
||
|
||
iv := cipherTextBytes[:aes.BlockSize]
|
||
cipherTextBytes = cipherTextBytes[aes.BlockSize:]
|
||
|
||
stream := cipher.NewCFBDecrypter(block, iv)
|
||
stream.XORKeyStream(cipherTextBytes, cipherTextBytes)
|
||
|
||
return string(cipherTextBytes), nil
|
||
}
|
||
|
||
func (o *EventObj) isEqual(tar *EventObj) bool {
|
||
if reflect.ValueOf(o.Callback).Pointer() == reflect.ValueOf(tar.Callback).Pointer() && o.Obj == tar.Obj {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
// 注册事件,提供事件名和回调函数
|
||
func RegisterEvent(name string, callback func([]interface{}), Obj interface{}) {
|
||
|
||
eo := new(EventObj)
|
||
eo.Callback = callback
|
||
eo.Obj = Obj
|
||
// 通过名字查找事件列表
|
||
list := eventByName[name]
|
||
|
||
// 在列表切片中添加函数
|
||
list = append(list, eo)
|
||
|
||
// 将修改的事件列表切片保存回去
|
||
eventByName[name] = list
|
||
}
|
||
|
||
// 调用事件
|
||
func CallEvent(name string, param []interface{}) {
|
||
|
||
// 通过名字找到事件列表
|
||
list := eventByName[name]
|
||
|
||
// 遍历这个事件的所有回调
|
||
for _, eo := range list {
|
||
// 传入参数调用回调
|
||
eo.Callback(param)
|
||
}
|
||
}
|
||
|
||
func RemoveEvent(name string, callback func([]interface{}), Obj interface{}) {
|
||
// 通过名字找到事件列表
|
||
list := eventByName[name]
|
||
j := 0
|
||
eo := new(EventObj)
|
||
eo.Callback = callback
|
||
eo.Obj = Obj
|
||
// 遍历这个事件的所有回调
|
||
for _, v := range list {
|
||
if !v.isEqual(eo) {
|
||
list[j] = v
|
||
j++
|
||
}
|
||
}
|
||
eventByName[name] = list[:j]
|
||
}
|
||
|
||
func DeleteEleByValue(list []int, ele int) []int {
|
||
j := 0
|
||
for _, v := range list {
|
||
if v != ele {
|
||
list[j] = v
|
||
j++
|
||
}
|
||
}
|
||
ret := list[:j]
|
||
return ret
|
||
}
|
||
|
||
func BoolToInt32(b bool) int32 {
|
||
if b {
|
||
return 1
|
||
}
|
||
return 0
|
||
}
|
||
|
||
func IfTrue(a bool, b, c interface{}) interface{} {
|
||
if a {
|
||
return b
|
||
}
|
||
return c
|
||
}
|
||
|
||
func Int64(a interface{}) int64 {
|
||
if a == nil {
|
||
return 0
|
||
}
|
||
switch v := a.(type) {
|
||
case int:
|
||
return int64(v)
|
||
case int32:
|
||
return int64(v)
|
||
case int64:
|
||
return v
|
||
case float64:
|
||
return int64(v)
|
||
}
|
||
return 0
|
||
}
|
||
|
||
func Int(a interface{}) int {
|
||
if a == nil {
|
||
return 0
|
||
}
|
||
switch v := a.(type) {
|
||
case int:
|
||
return v
|
||
case int32:
|
||
return int(v)
|
||
case int64:
|
||
return int(v)
|
||
case float64:
|
||
return int(v)
|
||
case string:
|
||
r, err := strconv.Atoi(v)
|
||
if err != nil {
|
||
return 0
|
||
}
|
||
return r
|
||
}
|
||
return 0
|
||
}
|
||
|
||
func String(a interface{}) string {
|
||
if a == nil {
|
||
return ""
|
||
}
|
||
switch v := a.(type) {
|
||
case int:
|
||
return strconv.Itoa(v)
|
||
case int32:
|
||
return strconv.Itoa(int(v))
|
||
case int64:
|
||
return strconv.Itoa(int(v))
|
||
case float64:
|
||
return strconv.FormatFloat(v, 'f', -1, 64)
|
||
case string:
|
||
return v
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func GobMarshal(data interface{}) ([]byte, error) {
|
||
buf := bufferPool.Get().(*bytes.Buffer)
|
||
buf.Reset() // 清空之前的数据
|
||
encode := gob.NewEncoder(buf)
|
||
err := encode.Encode(data)
|
||
if err != nil {
|
||
bufferPool.Put(buf) // 即使出错也要归还
|
||
return nil, err
|
||
}
|
||
bufferPool.Put(buf) // 归还到对象池
|
||
return buf.Bytes(), nil
|
||
}
|
||
|
||
func GobUnmarshal(data []byte, obj interface{}) error {
|
||
decode := gob.NewDecoder(bytes.NewReader(data))
|
||
err := decode.Decode(obj)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func GetServerIdByUid(uid int) int {
|
||
if uid <= 100000 {
|
||
return uid
|
||
}
|
||
if uid <= 100000000 {
|
||
return uid / 100000
|
||
}
|
||
return int((uid % 100000000) / 100000)
|
||
}
|
||
|
||
func CreateOrderSn(uid int) string {
|
||
Now := time.Now()
|
||
return "order_" + strconv.Itoa(uid) + "_" + Now.Format("20060102150405") + RandString(6)
|
||
}
|
||
|
||
// 生成指定长度的随机字符串
|
||
func RandString(n int) string {
|
||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||
b := make([]byte, n)
|
||
for i := range b {
|
||
b[i] = letters[r.Intn(len(letters))]
|
||
}
|
||
return string(b)
|
||
}
|
||
|
||
// 生成索要卡牌唯一id
|
||
func CreateCardId(From, To, CardId int) string {
|
||
return fmt.Sprintf("%d_%d_%d_%d_%s", From, To, CardId, Now(), RandString(3))
|
||
}
|
||
|
||
func SplitInt(str, sep string) []int {
|
||
var ret []int
|
||
for _, v := range strings.Split(str, sep) {
|
||
ret = append(ret, Int(v))
|
||
}
|
||
return ret
|
||
}
|
||
|
||
func Compare(x int, condition string, y int) bool {
|
||
switch condition {
|
||
case CONDITION_EQ:
|
||
return x == y
|
||
case CONDITION_GT:
|
||
return x > y
|
||
case CONDITION_LT:
|
||
return x < y
|
||
case CONDITION_GE:
|
||
return x >= y
|
||
case CONDITION_LE:
|
||
return x <= y
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
func UniKey(seed string) string {
|
||
hash := md5.New()
|
||
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||
io.WriteString(hash, seed+timestamp)
|
||
return fmt.Sprintf("%x", hash.Sum(nil))
|
||
}
|
||
|
||
func GetCatnipLockKey(Uid, GameId int) string {
|
||
return fmt.Sprintf("catnip_lock_%d_%d", Uid, GameId)
|
||
}
|
||
|
||
func Rand6DigitNumber() string {
|
||
n := rand.Intn(1000000)
|
||
return fmt.Sprintf("%06d", n)
|
||
}
|
||
|
||
func Rand8DigitNumber() string {
|
||
n := rand.Intn(100000000)
|
||
return fmt.Sprintf("%08d", n)
|
||
}
|
||
|
||
func UniqueInts(input []int) []int {
|
||
seen := make(map[int]struct{})
|
||
result := make([]int, 0, len(input))
|
||
for _, v := range input {
|
||
if _, ok := seen[v]; !ok {
|
||
seen[v] = struct{}{}
|
||
result = append(result, v)
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
/*
|
||
#codebase: GoUtil
|
||
修改函数UniqueStringFromInt
|
||
|
||
功能说明:
|
||
将整数转换为包含字母和数字的唯一字符串
|
||
|
||
算法步骤:
|
||
1. 将输入整数格式化为5位数字符串(不足前补0)
|
||
2. 分割字符串:前2位、中2位、最后1位
|
||
3. 将各段转为整数,作为字母表索引
|
||
4. 从预定义字母表中选择对应字符
|
||
5. 随机生成2个数字字符
|
||
6. 随机插入数字字符到字母串中
|
||
7. 返回最终唯一标识字符串
|
||
*/
|
||
|
||
func UniqueStringFromInt(n int) string {
|
||
n = n % 100000 // 确保n在0-99999范围内
|
||
str := fmt.Sprintf("%05d", n)
|
||
a1 := str[0:2]
|
||
a2 := str[2:4]
|
||
a3 := str[4:5]
|
||
|
||
s1, _ := strconv.Atoi(a1)
|
||
s2, _ := strconv.Atoi(a2)
|
||
s3, _ := strconv.Atoi(a3)
|
||
|
||
// 修改索引计算逻辑
|
||
var letter1, letter2 string
|
||
if s1 >= len(LETTER) {
|
||
letter1 = string(LETTER[s1%len(LETTER)]) + strconv.Itoa(s1/len(LETTER))
|
||
} else {
|
||
letter1 = string(LETTER[s1])
|
||
}
|
||
|
||
if s2 >= len(LETTER) {
|
||
idx := (s2) % len(LETTER)
|
||
letter2 = string(LETTER[idx]) + strconv.Itoa(s2/len(LETTER))
|
||
} else {
|
||
letter2 = string(LETTER[s2])
|
||
}
|
||
|
||
letter3 := string(LETTER[s3+20])
|
||
letter := fmt.Sprintf("%s%s%s", letter1, letter2, letter3)
|
||
lastLetter := 5 - len(letter)
|
||
indices := rand.Perm(8)[:2]
|
||
chars := []byte{byte('2' + indices[0]), byte('2' + indices[1])}
|
||
if lastLetter > 0 {
|
||
for i := 0; i < lastLetter; i++ {
|
||
insertPos1 := rand.Intn(len(letter) + 1)
|
||
letter = letter[:insertPos1] + string(chars[0]) + letter[insertPos1:]
|
||
}
|
||
}
|
||
pos := rand.Intn(len(LETTER))
|
||
letter += string(LETTER[pos])
|
||
return fmt.Sprintf("%s-%s", letter[:3], letter[3:])
|
||
}
|
||
|
||
// 反解析 UniqueStringFromInt 生成的字符串,返回原始整数(0-99999),失败返回 -1
|
||
func ParseUniqueStringToInt(s string) int {
|
||
arr := strings.Split(s, "-")
|
||
// 去除字符串中大于1的数字
|
||
s1 := arr[2] + arr[3]
|
||
s1 = s1[:len(s1)-1] // 去掉最后一个字符
|
||
s2 := ""
|
||
for _, ch := range s1 {
|
||
if ch < '2' || ch > '9' {
|
||
s2 += string(ch)
|
||
}
|
||
}
|
||
index1 := 0
|
||
s3 := ""
|
||
for i := 0; i < len(s2); i++ {
|
||
index := strings.Index(LETTER, string(s2[i]))
|
||
|
||
if i < len(s2)-1 && s2[i+1] == '1' {
|
||
index += len(LETTER)
|
||
i++
|
||
}
|
||
index1++
|
||
if i == len(s2)-1 {
|
||
index -= 20 // 最后一位是字母,减去20
|
||
s3 += fmt.Sprintf("%d", index)
|
||
} else {
|
||
s3 += fmt.Sprintf("%02d", index)
|
||
}
|
||
|
||
}
|
||
return Int(s3)
|
||
}
|
||
|
||
func GenerateShuffledAlphabet() string {
|
||
// 包含所有大小写字母的字符串
|
||
alphabet := "abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ"
|
||
|
||
// 转换为字节切片以便打乱
|
||
bytes := []byte(alphabet)
|
||
|
||
// 使用当前时间作为随机种子
|
||
rand.Seed(time.Now().UnixNano())
|
||
|
||
// Fisher-Yates 洗牌算法
|
||
for i := len(bytes) - 1; i > 0; i-- {
|
||
j := rand.Intn(i + 1)
|
||
bytes[i], bytes[j] = bytes[j], bytes[i]
|
||
}
|
||
|
||
return string(bytes)
|
||
}
|
||
|
||
// 根据IP获取国家名称
|
||
func GetCountryByIP(ip string) (string, error) {
|
||
return "", nil
|
||
// resp, err := http.Get("https://ipapi.co/" + ip + "/country_name/")
|
||
// if err != nil {
|
||
// return "", err
|
||
// }
|
||
// defer resp.Body.Close()
|
||
// buf := new(bytes.Buffer)
|
||
// buf.ReadFrom(resp.Body)
|
||
// return strings.TrimSpace(buf.String()), nil
|
||
}
|
||
|
||
// 根据国家名称获取ISO 3166-1国家码
|
||
func GetISOCodeByCountry(country string) (string, error) {
|
||
// 简单映射,可以扩展为更完整的映射表
|
||
countryMap := map[string]string{
|
||
"Afghanistan": "004", // 阿富汗
|
||
"Albania": "008", // 阿尔巴尼亚
|
||
"Algeria": "012", // 阿尔及利亚
|
||
"Angola": "024", // 安哥拉
|
||
"Argentina": "032", // 阿根廷
|
||
"Austria": "040", // 奥地利
|
||
"Azerbaijan": "031", // 阿塞拜疆
|
||
"Bahrain": "048", // 巴林
|
||
"Bangladesh": "050", // 孟加拉国
|
||
"Belgium": "056", // 比利时
|
||
"Bolivia": "068", // 玻利维亚
|
||
"Bosnia and Herzegovina": "070", // 波斯尼亚和黑塞哥维那
|
||
"Brazil": "076", // 巴西
|
||
"Bulgaria": "100", // 保加利亚
|
||
"Canada": "124", // 加拿大
|
||
"Chile": "152", // 智利
|
||
"China": "156", // 中国
|
||
"Colombia": "170", // 哥伦比亚
|
||
"Costa Rica": "188", // 哥斯达黎加
|
||
"Croatia": "191", // 克罗地亚
|
||
"Cuba": "192", // 古巴
|
||
"Denmark": "208", // 丹麦
|
||
"Ecuador": "218", // 厄瓜多尔
|
||
"Egypt": "818", // 埃及
|
||
"Ethiopia": "231", // 埃塞俄比亚
|
||
"Finland": "246", // 芬兰
|
||
"France": "250", // 法国
|
||
"Germany": "276", // 德国
|
||
"Ghana": "288", // 加纳
|
||
"Greece": "300", // 希腊
|
||
"Hungary": "348", // 匈牙利
|
||
"India": "356", // 印度
|
||
"Indonesia": "360", // 印度尼西亚
|
||
"Iran": "364", // 伊朗
|
||
"Iraq": "368", // 伊拉克
|
||
"Italy": "380", // 意大利
|
||
"Japan": "392", // 日本
|
||
"Jordan": "400", // 约旦
|
||
"Kenya": "404", // 肯尼亚
|
||
"Kuwait": "414", // 科威特
|
||
"Lebanon": "422", // 黎巴嫩
|
||
"Malaysia": "458", // 马来西亚
|
||
"Mexico": "484", // 墨西哥
|
||
"Morocco": "504", // 摩洛哥
|
||
"Nigeria": "566", // 尼日利亚
|
||
"Norway": "578", // 挪威
|
||
"Pakistan": "586", // 巴基斯坦
|
||
"Peru": "604", // 秘鲁
|
||
"Philippines": "608", // 菲律宾
|
||
"Poland": "616", // 波兰
|
||
"Portugal": "620", // 葡萄牙
|
||
"Qatar": "634", // 卡塔尔
|
||
"Romania": "642", // 罗马尼亚
|
||
"Russia": "643", // 俄罗斯
|
||
"Saudi Arabia": "682", // 沙特阿拉伯
|
||
"South Africa": "710", // 南非
|
||
"South Korea": "410", // 韩国
|
||
"Spain": "724", // 西班牙
|
||
"Sweden": "752", // 瑞典
|
||
"Switzerland": "756", // 瑞士
|
||
"Thailand": "764", // 泰国
|
||
"Turkey": "792", // 土耳其
|
||
"Ukraine": "804", // 乌克兰
|
||
"United Kingdom": "826", // 英国
|
||
"United States": "840", // 美国
|
||
"Vietnam": "704", // 越南
|
||
"Zimbabwe": "716", // 津巴布韦
|
||
}
|
||
if code, ok := countryMap[country]; ok {
|
||
return code, nil
|
||
}
|
||
return "", fmt.Errorf("country code not found for %s", country)
|
||
}
|
||
|
||
// 综合函数:根据IP获取ISO 3166-1国家码
|
||
func GetISOCodeByIP(ip string) (string, error) {
|
||
country, err := GetCountryByIP(ip)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return GetISOCodeByCountry(country)
|
||
}
|
||
|
||
func GetVarKey(Uid int) string {
|
||
return fmt.Sprintf("var_%d", Uid)
|
||
}
|
||
|
||
const (
|
||
PROJECT_ID = "20659"
|
||
ACCESS_ID = "a3d8c1f0e5b72a49"
|
||
NOTIFICATION_SECRET_KEY = "K8pYrR6fXcVzWbAqN3mHsD4gJtL9iUvO1"
|
||
)
|
||
|
||
func NotifyPlayer(uid, pushid int, title, content string) {
|
||
|
||
url := "https://tygapi-new.tuyooglobal.com/api/push/ga/push_message/project_trigger"
|
||
method := "POST"
|
||
executeId := fmt.Sprintf("%s_%s", PROJECT_ID, Rand8DigitNumber()+Rand8DigitNumber())
|
||
payload := strings.NewReader(`[
|
||
{
|
||
"projectId": "` + PROJECT_ID + `",
|
||
"platform": "auto",
|
||
"pushId": "` + PROJECT_ID + `_` + fmt.Sprintf("%08d", pushid) + `",
|
||
"executeId": "` + executeId + `",
|
||
"environment": "production",
|
||
"title": "` + title + `",
|
||
"body": "` + content + `",
|
||
"userId": [
|
||
"` + strconv.Itoa(uid) + `"
|
||
]
|
||
}
|
||
]`)
|
||
|
||
client := &http.Client{}
|
||
req, err := http.NewRequest(method, url, payload)
|
||
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
d, _ := time.ParseDuration("30m")
|
||
timestamp := time.Now().Add(d).Unix()
|
||
|
||
var strBuilder strings.Builder
|
||
strBuilder.WriteString("Access-Id=" + ACCESS_ID + "&Random=a2dS8Iyak03Ma91JC1xR&Secret-Type=forever&Sign-Type=sha256&Timestamp=")
|
||
strBuilder.WriteString(strconv.Itoa(int(timestamp)))
|
||
strBuilder.WriteString(":" + NOTIFICATION_SECRET_KEY)
|
||
|
||
m := sha256.New()
|
||
m.Write([]byte(strBuilder.String()))
|
||
signature := hex.EncodeToString(m.Sum(nil))
|
||
|
||
req.Header.Add("Access-Id", ACCESS_ID)
|
||
req.Header.Add("Secret-Type", "forever")
|
||
req.Header.Add("Sign-Type", "sha256")
|
||
req.Header.Add("Random", "a2dS8Iyak03Ma91JC1xR")
|
||
req.Header.Add("Timestamp", strconv.Itoa(int(timestamp)))
|
||
req.Header.Add("Signature", signature)
|
||
req.Header.Add("Content-Type", "application/json")
|
||
fmt.Print(req)
|
||
res, err := client.Do(req)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
defer res.Body.Close()
|
||
|
||
body, err := io.ReadAll(res.Body)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
fmt.Println(string(body))
|
||
}
|