pet_home_server/src/server/game_util/GoUtil.go
2026-03-04 11:34:53 +08:00

651 lines
16 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 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"
"server/pkg/github.com/name5566/leaf/log"
"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 Int32(a interface{}) int32 {
if a == nil {
return 0
}
switch v := a.(type) {
case int:
return int32(v)
case int32:
return int32(v)
case int64:
return int32(v)
case float64:
return int32(v)
case string:
r, err := strconv.Atoi(v)
if err != nil {
return 0
}
return int32(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
}
result := make([]byte, buf.Len())
copy(result, buf.Bytes())
bufferPool.Put(buf) // 归还到对象池
return result, 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) {
if uid == 0 {
return
}
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": "firebasepush",
"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")
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
}
log.Debug("notification send uid %d, type %d , res %s", uid, pushid, string(body))
}