admin_backend/util/util.go
2025-10-27 16:48:47 +08:00

423 lines
8.8 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 util
import (
"backend/msg"
"bytes"
"crypto/aes"
"crypto/cipher"
crand "crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"os"
"reflect"
"slices"
"strconv"
"strings"
"text/template"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/net/websocket"
"google.golang.org/protobuf/proto"
)
var MergeData = make(map[string]interface{})
var MergeEmitData = make(map[string]interface{})
func init() {
data, err := os.ReadFile("conf/MergeData.json")
if err != nil {
log.Printf("failed to read conf/MergeData.json: %v", err)
} else {
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
log.Printf("failed to unmarshal MergeData.json: %v", err)
} else {
MergeData = m
}
}
data, err = os.ReadFile("conf/MergeDataEmit.json")
if err != nil {
log.Printf("failed to read conf/MergeEmitData.json: %v", err)
} else {
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
log.Printf("failed to unmarshal MergeEmitData.json: %v", err)
} else {
MergeEmitData = m
}
}
}
// 获取结构体名称
func GetStructName(v interface{}) string {
t := reflect.TypeOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() == reflect.Struct {
return t.Name()
}
return ""
}
func PackMsg(m proto.Message) []byte {
buf, _ := proto.Marshal(m)
Func := GetStructName(m)
req := &msg.AdminReq{
Func: Func,
Info: buf,
}
buf, _ = proto.Marshal(req)
return append([]byte{0, 2}, buf...)
}
func UnpackMsg(buf []byte, n int) ([]byte, error) {
res := &msg.AdminRes{}
err := proto.Unmarshal(buf[2:n], res)
if err != nil {
return nil, err
}
return res.Info, nil
}
func ParseUid(uid int) (int, int) {
AppId := uid / 100000000
ServerId := uid % 100000000 / 100000
return AppId, ServerId
}
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 InArray(Id int, s []int) bool {
for _, v := range s {
if v == Id {
return true
}
}
return false
}
func Now() int64 {
return time.Now().Unix()
}
func Year() int {
return time.Now().Year()
}
func SendAdminMsg(ws *websocket.Conn, req proto.Message) (map[string]interface{}, error) {
reqBuf := PackMsg(req)
_, err := ws.Write(reqBuf)
if err != nil {
return nil, fmt.Errorf("failed to write to websocket: %v", err)
}
readbuf := make([]byte, 4096)
n, err := ws.Read(readbuf)
if err != nil {
return nil, fmt.Errorf("failed to read from websocket: %v", err)
}
resBuf, _ := UnpackMsg(readbuf, n)
r := make(map[string]interface{})
json.Unmarshal(resBuf, &r)
return r, nil
}
func FloatDecimals(f float64, n int) float64 {
format := fmt.Sprintf("%%.%df", n)
r, _ := strconv.ParseFloat(fmt.Sprintf(format, f), 64)
return r
}
// 将两个参数转换成浮点数如果无法转换则为0实现两个浮点数的除法除数为0的话返回0
func FloatDiv(a, b interface{}, Decimal int) float64 {
af := toFloat64(a)
bf := toFloat64(b)
if bf == 0 {
return 0
}
result := af / bf
format := fmt.Sprintf("%%.%df", Decimal)
r, _ := strconv.ParseFloat(fmt.Sprintf(format, result), 64)
return r
}
func Decimal(f float64, n int) float64 {
format := fmt.Sprintf("%%.%df", n)
r, _ := strconv.ParseFloat(fmt.Sprintf(format, f), 64)
return r
}
// 辅助函数将interface{}转换为float64
func toFloat64(v interface{}) float64 {
switch val := v.(type) {
case int:
return float64(val)
case int32:
return float64(val)
case int64:
return float64(val)
case float32:
return float64(val)
case float64:
return val
case string:
r, err := strconv.ParseFloat(val, 64)
if err != nil {
return 0
}
return r
default:
return 0
}
}
const (
SECRET_KEY = ")VQbB(vpy=U(wcp)"
)
// 加密字符串
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 ParseTmpl(file string, data interface{}) string {
t, err := template.ParseFiles(file)
if err != nil {
log.Fatalf("failed to parse template file: %v", err)
}
var buf bytes.Buffer
err = t.Execute(&buf, data)
if err != nil {
log.Fatalf("failed to execute template: %v", err)
}
s := buf.String()
return s
}
// 将字符串转换成一行的字符串格式,并再进行一次转义输出
func ToTmplStr(input string) string {
// 转义换行符
escapedStr := strings.ReplaceAll(input, "\r", "")
escapedStr = strings.ReplaceAll(escapedStr, "\n", "\\n")
escapedStr = strings.ReplaceAll(escapedStr, "\"", "\\\"")
// 转义输出
// return template.HTMLEscapeString(escapedStr)
return escapedStr
}
func Ternary(condition bool, trueVal, falseVal interface{}) interface{} {
if condition {
return trueVal
}
return falseVal
}
func TimestampToDate(timestamp int64, Tz string) string {
loc, err := time.LoadLocation(Tz)
if err != nil {
log.Printf("failed to load location %s: %v", Tz, err)
loc = time.UTC // 默认使用UTC
}
t := time.Unix(timestamp, 0).In(loc)
return t.Format("2006-01-02")
}
func TimestampToDateTime(timestamp int64, Tz string) string {
loc, err := time.LoadLocation(Tz)
if err != nil {
log.Printf("failed to load location %s: %v", Tz, err)
loc = time.UTC // 默认使用UTC
}
t := time.Unix(timestamp, 0).In(loc)
return t.Format("2006-01-02 15:04:05")
}
func GenerateToken() string {
// 生成一个安全的随机token适合网页登录
b := make([]byte, 32)
if _, err := crand.Read(b); err != nil {
return ""
}
return base64.URLEncoding.EncodeToString(b)
}
func GeneratedCode(Phone string) (string, error) {
Code := Rand6DigitNumber()
err := SmsCode(Phone, Code)
if err != nil {
return "", fmt.Errorf("failed to send SMS code: %v", err)
}
return Code, err
}
func Rand6DigitNumber() string {
n := rand.Intn(1000000)
return fmt.Sprintf("%06d", n)
}
func GetToken(c *gin.Context) string {
// 从请求头中获取Token
token := c.GetHeader("Authorization")
if len(token) > 7 && token[:7] == "Bearer " {
token = token[7:] // 去掉"Bearer "前缀
}
return token
}
func GetRole(code int) string {
switch code {
case 0:
return "super"
case 1:
return "admin"
case 2:
return "user"
case 99:
return "wb_transfer"
default:
return "guest"
}
}
func ToJson(v interface{}) string {
data, err := json.Marshal(v)
if err != nil {
log.Printf("failed to marshal to JSON: %v", err)
return ""
}
return string(data)
}
func AddAdminLog(c *gin.Context, action string, params interface{}) {
admin := c.GetString("admin")
ip := c.ClientIP()
db := MPool.GetGameDB()
_, err := db.Exec("INSERT INTO admin_log (admin, action, params, ip, createTime) VALUES (?, ?, ?, ?, ?)",
admin, action, ToJson(params), ip, Now())
if err != nil {
fmt.Printf("failed to insert admin log: %v", err)
return
}
}
func ParseParam(s string) map[string]interface{} {
result := make(map[string]interface{})
if strings.TrimSpace(s) == "" {
return result
}
if err := json.Unmarshal([]byte(s), &result); err != nil {
return map[string]interface{}{}
}
return result
}
func CheckContainChess(ChessList []string, Emit []string) bool {
if len(Emit) == 0 {
return true
}
for _, c := range ChessList {
d, ok := MergeData[c]
if !ok {
continue
}
if d == nil {
continue
}
m, ok := d.(map[string]interface{})
if !ok {
continue
}
Color := m["Color"].(string)
Serise := getSeriseByColor(Color)
if InArrayStr(Serise, Emit) {
return true
}
}
return false
}
func InArrayStr(s string, arr []string) bool {
return slices.Contains(arr, s)
}
func getSeriseByColor(Color string) string {
for k, v := range MergeEmitData {
d := v.(map[string]interface{})
colors, ok := d["Order_Type"].(string)
if !ok {
continue
}
colorList := strings.Split(colors, ",")
if InArrayStr(Color, colorList) {
return k
}
}
return Color
}