版本更新

This commit is contained in:
hahwu 2026-02-25 10:16:35 +08:00
parent 1a66367ca9
commit 630c5ebd77
22 changed files with 343 additions and 127 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ log/*.log
*.log *.log
runtime/*.db runtime/*.db
config/* config/*
unit/config/*

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 MiB

View File

@ -92,6 +92,7 @@ type ServerInfo struct {
CreateTime int `json:"CreateTime" db:"CreateTime"` CreateTime int `json:"CreateTime" db:"CreateTime"`
OpenServerTime int `json:"OpenServerTime" db:"OpenServerTime"` OpenServerTime int `json:"OpenServerTime" db:"OpenServerTime"`
WsPort int `json:"WsPort" db:"ws_port"` WsPort int `json:"WsPort" db:"ws_port"`
Port int `json:"Port" db:"Port"`
Host string `json:"Host" db:"Host"` Host string `json:"Host" db:"Host"`
StartTime int64 `json:"StartTime"` StartTime int64 `json:"StartTime"`
PlayerNum int `json:"PlayerNum" db:"Online"` PlayerNum int `json:"PlayerNum" db:"Online"`
@ -101,6 +102,7 @@ type ServerInfo struct {
NodeTags string `json:"-" db:"node_tags"` NodeTags string `json:"-" db:"node_tags"`
Tags []string `json:"Tags" db:"-"` Tags []string `json:"Tags" db:"-"`
ECS int `json:"ECS" db:"ecs"` ECS int `json:"ECS" db:"ecs"`
Latency int `json:"Latency" db:"latency"`
} }
type Server struct { type Server struct {

View File

@ -193,6 +193,9 @@ func SendZabbixMsg(data *Type.NotifyData) (_err error) {
// 卡片接收人 // 卡片接收人
Recipients: []*string{}, Recipients: []*string{},
} }
if data.Severity != "Disaster" && data.Severity != "High" {
return nil
}
if data.Severity == "Disaster" || data.Severity == "High" { if data.Severity == "Disaster" || data.Severity == "High" {
imGroupOpenDeliverModel.Recipients = []*string{tea.String("035105216620273488")} imGroupOpenDeliverModel.Recipients = []*string{tea.String("035105216620273488")}
imGroupOpenDeliverModel.AtUserIds = map[string]*string{ imGroupOpenDeliverModel.AtUserIds = map[string]*string{
@ -292,6 +295,9 @@ func SendZabbixRecoveryMsg(data *Type.NotifyRecoveryData) (_err error) {
// 卡片接收人 // 卡片接收人
Recipients: []*string{}, Recipients: []*string{},
} }
if data.Severity != "Disaster" && data.Severity != "High" {
return nil
}
imGroupOpenSpaceModelLastMessageI18n := map[string]*string{ imGroupOpenSpaceModelLastMessageI18n := map[string]*string{
"ZH_CN": tea.String(lastMessage), "ZH_CN": tea.String(lastMessage),
} }
@ -458,6 +464,100 @@ func SendStandardMsg(title, content, color string) (_err error) {
return _err return _err
} }
func SendStandardMsg2(title, content, color string) (_err error) {
client, _err := createCardClient()
if _err != nil {
return _err
}
accessToken, _ := GetToken()
robotCode := "dingrmgtodzxaik76jpc"
userId := ""
openConversationId := "cidivmW+tO/JGyIFM/XHNeQcA=="
templateId := "843a23ff-29d2-4efc-b7f4-2dea2766d7db.schema"
lastMessage := title
searchIcon := ""
searchDesc := ""
createAndDeliverHeaders := &dingtalkcard_1_0.CreateAndDeliverHeaders{}
createAndDeliverHeaders.XAcsDingtalkAccessToken = tea.String(accessToken)
imGroupOpenDeliverModel := &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
RobotCode: tea.String(robotCode),
// 卡片接收人
Recipients: []*string{},
AtUserIds: map[string]*string{
"@ALL": tea.String("@ALL"),
},
}
imGroupOpenSpaceModelLastMessageI18n := map[string]*string{
"ZH_CN": tea.String(lastMessage),
}
imGroupOpenSpaceModelSearchSupport := &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenSpaceModelSearchSupport{
SearchIcon: tea.String(searchIcon),
SearchDesc: tea.String(searchDesc),
}
imGroupOpenSpaceModel := &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
SupportForward: tea.Bool(true),
LastMessageI18n: imGroupOpenSpaceModelLastMessageI18n,
SearchSupport: imGroupOpenSpaceModelSearchSupport,
}
// 此处使用了 MockData 作为测试数据,请结合真实场景设置卡片公有数据
cardDataCardParamMap := map[string]any{
"title": title,
"markdown": content,
"color": color,
"config": map[string]any{
"autoLayout": true,
},
}
cardDataError := error(nil)
_ = cardDataError
cardData := &dingtalkcard_1_0.CreateAndDeliverRequestCardData{
CardParamMap: convertJSONValuesToString(cardDataCardParamMap),
}
createAndDeliverRequest := &dingtalkcard_1_0.CreateAndDeliverRequest{
UserId: tea.String(userId),
CardTemplateId: tea.String(templateId),
// 用于标识卡片的唯一 ID业务需自行建立关联关系用于后续的卡片更新
OutTrackId: tea.String(fmt.Sprintf("standard-out-track-id-%d", time.Now().Unix())),
CallbackType: tea.String("STREAM"),
CardData: cardData,
ImGroupOpenSpaceModel: imGroupOpenSpaceModel,
ImGroupOpenDeliverModel: imGroupOpenDeliverModel,
OpenSpaceId: tea.String(fmt.Sprintf("dtv1.card//im_group.%s", openConversationId)),
UserIdType: tea.Int32(1),
// CardAtUserIds: []*string{tea.String("all")},
}
tryErr := func() (_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
_, _err = client.CreateAndDeliverWithOptions(createAndDeliverRequest, createAndDeliverHeaders, &util.RuntimeOptions{})
if _err != nil {
return _err
}
return nil
}()
if tryErr != nil {
var err = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
err = _t
} else {
err.Message = tea.String(tryErr.Error())
}
if !tea.BoolValue(util.Empty(err.Code)) && !tea.BoolValue(util.Empty(err.Message)) {
// err 中含有 code 和 message 属性,可帮助开发定位问题
}
}
return _err
}
func SendAliveMsg(title, content, color string) (_err error) { func SendAliveMsg(title, content, color string) (_err error) {
client, _err := createCardClient() client, _err := createCardClient()
if _err != nil { if _err != nil {

View File

@ -35,6 +35,7 @@ type SystemConfig struct {
USOperationChatId string `yaml:"us_operation_chat_id"` // US运营群id USOperationChatId string `yaml:"us_operation_chat_id"` // US运营群id
ClientChatId string `yaml:"client_chat_id"` // 客户端群id ClientChatId string `yaml:"client_chat_id"` // 客户端群id
Ssh bool `yaml:"ssh"` // 是否开启SSH连接 Ssh bool `yaml:"ssh"` // 是否开启SSH连接
Env string `yaml:"env"` // 环境变量
} }
type Config struct { type Config struct {
@ -143,6 +144,10 @@ func GetSsh() bool {
return config.System.Ssh return config.System.Ssh
} }
func GetEnv() string {
return config.System.Env
}
func GetTranlaterConfig(account string) *TranlaterUser { func GetTranlaterConfig(account string) *TranlaterUser {
for _, user := range tranlaterConfig.User { for _, user := range tranlaterConfig.User {
if user.Account == account { if user.Account == account {

View File

@ -30,7 +30,7 @@ func AlibabaGameNotify(c *gin.Context) {
return return
} }
content := util.ParseTmpl("./template/alibaba_notify.tmpl", r) content := util.ParseTmpl("./template/alibaba_notify.tmpl", r)
err := alibaba.SendStandardMsg("游戏服务报警通知", content, "red") err := alibaba.SendStandardMsg2("游戏服务报警通知", content, "red")
if err != nil { if err != nil {
log.Printf("failed to send notify message: %v", err) log.Printf("failed to send notify message: %v", err)
} }

View File

@ -241,7 +241,7 @@ func CopyOnline(c *gin.Context) {
case 0: case 0:
success(c, map[string]interface{}{ success(c, map[string]interface{}{
"step": 1, "step": 1,
"label": "关闭QA环境服务", "label": "复制数据库",
"tips": []string{}, "tips": []string{},
"code": 0, "code": 0,
}) })
@ -259,7 +259,7 @@ func CopyOnline(c *gin.Context) {
util.AddAdminLog(c, "文案自动化脚本", request) util.AddAdminLog(c, "文案自动化脚本", request)
} }
func CopyOnlineStep1(c *gin.Context) { func CopyOnlineStep2(c *gin.Context) {
// 关闭QA环境服务 // 关闭QA环境服务
time.Sleep(time.Second) time.Sleep(time.Second)
servers := util.GetAllServersByAppId(2) // AppId=2 QA环境 servers := util.GetAllServersByAppId(2) // AppId=2 QA环境
@ -278,14 +278,14 @@ func CopyOnlineStep1(c *gin.Context) {
} }
} }
success(c, map[string]interface{}{ success(c, map[string]interface{}{
"step": 2, "step": 3,
"label": "复制数据库", "label": "写入数据到QA环境",
"tips": []string{}, "tips": []string{},
"code": 0, "code": 0,
}) })
} }
func CopyOnlineStep2(c *gin.Context) { func CopyOnlineStep1(c *gin.Context) {
// mysqldump 复制数据库 // mysqldump 复制数据库
nodeInfo := util.GetNodeByName("devops") nodeInfo := util.GetNodeByName("devops")
SshClient, err := util.NewSshClient(nodeInfo) SshClient, err := util.NewSshClient(nodeInfo)
@ -303,8 +303,8 @@ func CopyOnlineStep2(c *gin.Context) {
time.Sleep(time.Second) time.Sleep(time.Second)
success(c, map[string]interface{}{ success(c, map[string]interface{}{
"step": 3, "step": 2,
"label": "写入数据到QA环境", "label": "关闭QA环境服务",
"tips": []string{}, "tips": []string{},
"code": 0, "code": 0,
}) })

View File

@ -98,6 +98,18 @@ func UserDetail(c *gin.Context) {
order[k] = info order[k] = info
} }
} }
if user["FriendList"] != nil {
friendList := user["FriendList"].([]interface{})
for k, v := range friendList {
info := v.(map[string]interface{})
face_url := util.GetFaceURL(util.Int(info["Avatar"]))
info["avatarUrl"] = face_url
friendList[k] = info
info["onlineStatus"] = util.Int(info["LoginTime"]) > util.Int(info["LogoutTime"])
}
user["FriendList"] = friendList
}
heat, _ := log.Heat() heat, _ := log.Heat()
if err != nil { if err != nil {
fmt.Print(err) fmt.Print(err)

View File

@ -164,8 +164,10 @@ func main() {
//go util.ScheduleDailyTask() //go util.ScheduleDailyTask()
go server.Server() go server.Server()
go model.InitToken() // 初始化 Token 列表 go model.InitToken() // 初始化 Token 列表
go util.MonitorServerList() // 启动定时任务发送信息 if common.GetEnv() == "prod" { // 生产环境才启动监控
go monitor.UserAliveMonitor(0) // 用户存活监控 go monitor.UserAliveMonitor(0) // 用户存活监控
go util.MonitorServerList() // 启动定时任务发送信息
}
go monitor.ServerInfoMonitor() // 服务器信息监控 go monitor.ServerInfoMonitor() // 服务器信息监控
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {

View File

@ -66,54 +66,12 @@ func (s *Statistics) StatisticsInfo() (interface{}, error) {
if LogDb == nil { if LogDb == nil {
return nil, fmt.Errorf("failed to get mysql database") return nil, fmt.Errorf("failed to get mysql database")
} }
StartTime, _ := util.GetZeroTimestamp(AppConfig.Tz, 0)
EndTime, _ := util.GetZeroTimestamp(AppConfig.Tz, 1)
var Register int var Register int
var TotalRegistger int var TotalRegistger int
var Recharge float64 var Recharge float64
var TotalRecharge float64 var TotalRecharge float64
var RechargeUser int var RechargeUser int
err = LogDb.Get(&Register, "select count(*) as sum from log_login where Event = 'register' and Timestamp >= ? and Timestamp <= ?", StartTime, EndTime)
if err != nil {
return nil, fmt.Errorf("failed to get register count: %v", err)
}
err = LogDb.Get(&TotalRegistger, "select count(*) as sum from log_login where Event = 'register'")
if err != nil {
return nil, fmt.Errorf("failed to get total register count: %v", err)
}
err = LogDb.Get(&Recharge, "select IFNULL(SUM(Price), 0) as sum from log_order where Timestamp >= ? and Timestamp <= ?", StartTime, EndTime)
if err != nil {
return nil, fmt.Errorf("failed to get recharge count: %v", err)
}
err = LogDb.Get(&TotalRecharge, "select IFNULL(SUM(Price), 0) as sum from log_order")
if err != nil {
return nil, fmt.Errorf("failed to get total recharge count: %v", err)
}
// 充值人数统计
err = LogDb.Get(&RechargeUser, "select count(distinct Uid) as sum from log_order where Timestamp >= ? and Timestamp <= ?", StartTime, EndTime)
if err != nil {
return nil, fmt.Errorf("failed to get recharge count: %v", err)
}
var TotalRechargeUser int
// 充值总人数统计
err = LogDb.Get(&TotalRechargeUser, "select count(distinct Uid) as sum from log_order")
if err != nil {
return nil, fmt.Errorf("failed to get total recharge count: %v", err)
}
type RemainCount struct {
Register int `db:"register"`
SecondRemain int `db:"SecondRemain"`
}
var rc RemainCount
err = LogDb.Get(&rc, "select sum(`Register`) as register, sum(`SecondRemain`) as SecondRemain from remain")
if err != nil {
return nil, fmt.Errorf("failed to get recharge count: %v", err)
}
rechargeFloat, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", Recharge), 64) rechargeFloat, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", Recharge), 64)
totalRechargeFloat, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", TotalRecharge), 64) totalRechargeFloat, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", TotalRecharge), 64)
@ -123,8 +81,8 @@ func (s *Statistics) StatisticsInfo() (interface{}, error) {
"recharge": rechargeFloat, "recharge": rechargeFloat,
"totalRecharge": totalRechargeFloat, "totalRecharge": totalRechargeFloat,
"rechargeUser": RechargeUser, "rechargeUser": RechargeUser,
"totalRechargeUser": TotalRechargeUser, "totalRechargeUser": 0,
"remain": util.FloatDecimals(100*float64(rc.SecondRemain)/float64(rc.Register), 2), "remain": 0,
}, nil }, nil
} }

View File

@ -27,7 +27,7 @@ type Mail struct {
ContentPtBr string `json:"content_ptbr" db:"content_ptbr"` ContentPtBr string `json:"content_ptbr" db:"content_ptbr"`
TitleEsLatam string `json:"title_es_latam" db:"title_es_latam"` TitleEsLatam string `json:"title_es_latam" db:"title_es_latam"`
SubTitleEsLatam string `json:"subtitle_es_latam" db:"subTitle_es_latam"` SubTitleEsLatam string `json:"subtitle_es_latam" db:"subtitle_es_latam"`
ContentEsLatam string `json:"content_es_latam" db:"content_es_latam"` ContentEsLatam string `json:"content_es_latam" db:"content_es_latam"`
StartTime int64 `json:"start_time" db:"start_time"` StartTime int64 `json:"start_time" db:"start_time"`
@ -53,6 +53,9 @@ func (m *Mail) MailList() (*Result, error) {
return nil, err return nil, err
} }
Db := util.MPool.GetMysqlDB(AppCfg, m.ServerId) Db := util.MPool.GetMysqlDB(AppCfg, m.ServerId)
if Db == nil {
return nil, fmt.Errorf("failed to get mysql db")
}
defer Db.Close() defer Db.Close()
var mail []*Mail var mail []*Mail
err = Db.Select(&mail, "SELECT `mail_id`, `title`, `content`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`,`send_type`, `to_uids`, `create_time`, `subTitle`, `subTitle_en`, `content_en`, `title_en`, `title_ptbr`,`subTitle_ptbr`, `content_ptbr`, `title_es_latam`, `subtitle_es_latam`, `content_es_latam` FROM system_mail_info") err = Db.Select(&mail, "SELECT `mail_id`, `title`, `content`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`,`send_type`, `to_uids`, `create_time`, `subTitle`, `subTitle_en`, `content_en`, `title_en`, `title_ptbr`,`subTitle_ptbr`, `content_ptbr`, `title_es_latam`, `subtitle_es_latam`, `content_es_latam` FROM system_mail_info")
@ -80,14 +83,17 @@ func (m *Mail) SendMail() error {
return err return err
} }
Db := util.MPool.GetMysqlDB(AppCfg, m.ServerId) Db := util.MPool.GetMysqlDB(AppCfg, m.ServerId)
if Db == nil {
return fmt.Errorf("failed to get mysql db")
}
defer Db.Close() defer Db.Close()
_, err = Db.Exec("INSERT INTO system_mail_info (`title`, `content`, `title_en`, `content_en`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`, `send_type`, `to_uids`, `create_time`, `subTitle`, `subTitle_en`, `title_ptbr`,`content_ptbr`,`subTitle_ptbr`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.Title, m.Content, m.TitleEn, m.ContentEn, m.StartTime, m.EndTime, m.Items, m.RegisterTime, m.MailType, m.SendType, m.ToUids, m.CreateTime, m.SubTitle, m.SubTitleEn, m.TitlePtBr, m.ContentPtBr, m.SubTitlePtBr) _, err = Db.Exec("INSERT INTO system_mail_info (`title`, `content`, `title_en`, `content_en`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`, `send_type`, `to_uids`, `create_time`, `subTitle`, `subTitle_en`, `title_ptbr`,`content_ptbr`,`subTitle_ptbr`, `title_es_latam`, `subTitle_es_latam`, `content_es_latam`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.Title, m.Content, m.TitleEn, m.ContentEn, m.StartTime, m.EndTime, m.Items, m.RegisterTime, m.MailType, m.SendType, m.ToUids, m.CreateTime, m.SubTitle, m.SubTitleEn, m.TitlePtBr, m.ContentPtBr, m.SubTitlePtBr, m.TitleEsLatam, m.SubTitleEsLatam, m.ContentEsLatam)
if err != nil { if err != nil {
return fmt.Errorf("failed to insert mail: %v", err) return fmt.Errorf("failed to insert mail: %v", err)
} }
ServerList := util.GetServerInfo(m.AppId, 0) ServerList := util.GetServerInfo(m.AppId, 0)
for _, server := range ServerList { for _, server := range ServerList {
go func() { go func(serverInfo util.ServerConfig) {
ws, err := util.GetWebsocket(m.AppId, server.ServerId) ws, err := util.GetWebsocket(m.AppId, server.ServerId)
if err != nil { if err != nil {
log.Printf("failed to get websocket: %v", err) log.Printf("failed to get websocket: %v", err)
@ -99,7 +105,8 @@ func (m *Mail) SendMail() error {
if err != nil { if err != nil {
log.Printf("failed to send admin message: %v", err) log.Printf("failed to send admin message: %v", err)
} }
}() log.Printf("sent reload mail message to server %d", server.ServerId)
}(server)
} }
return nil return nil
@ -111,6 +118,9 @@ func (m *Mail) DeleteMail() error {
return err return err
} }
Db := util.MPool.GetMysqlDB(AppCfg, m.ServerId) Db := util.MPool.GetMysqlDB(AppCfg, m.ServerId)
if Db == nil {
return fmt.Errorf("failed to get mysql db")
}
defer Db.Close() defer Db.Close()
_, err = Db.Exec("DELETE FROM system_mail_info WHERE `mail_id` = ?", m.MailId) _, err = Db.Exec("DELETE FROM system_mail_info WHERE `mail_id` = ?", m.MailId)
if err != nil { if err != nil {

View File

@ -48,7 +48,7 @@ func (s *Server) ServerList() ([]*Type.ServerInfo, error) {
Db := util.MPool.GetGameDB() Db := util.MPool.GetGameDB()
var server []*Type.ServerInfo var server []*Type.ServerInfo
defer Db.Close() defer Db.Close()
err := Db.Select(&server, "SELECT `AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime`, `Online`, `cpu`, `free_mem`, `version`, `node_tags` FROM server where AppId = ? order by `ServerId`", s.AppId) err := Db.Select(&server, "SELECT `AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime`, `Online`, `cpu`, `free_mem`, `version`, `node_tags`, `latency` FROM server where AppId = ? order by `ServerId`", s.AppId)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err) return nil, fmt.Errorf("failed to scan rows: %v", err)
} }

View File

@ -6,6 +6,7 @@ import (
"backend/model" "backend/model"
"backend/util" "backend/util"
"fmt" "fmt"
"net"
"time" "time"
) )
@ -66,7 +67,7 @@ func monitorServerInfo() {
Db := util.MPool.GetGameDB() Db := util.MPool.GetGameDB()
var server []*Type.ServerInfo var server []*Type.ServerInfo
defer Db.Close() defer Db.Close()
err := Db.Select(&server, "SELECT `AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime` FROM server ") err := Db.Select(&server, "SELECT `AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime`, `Host`, `Port` FROM server ")
if err != nil { if err != nil {
return return
} }
@ -77,9 +78,21 @@ func monitorServerInfo() {
go func(v *Type.ServerInfo) { go func(v *Type.ServerInfo) {
tmpDb := util.MPool.GetGameDB() tmpDb := util.MPool.GetGameDB()
defer tmpDb.Close() defer tmpDb.Close()
// TCP ping test for server connectivity
address := fmt.Sprintf("%s:%d", v.Host, v.Port) // adjust port as needed
timeout := 3 * time.Second
start := time.Now()
conn, err := net.DialTimeout("tcp", address, timeout)
latency := time.Since(start).Milliseconds()
if err != nil {
// Connection failed, mark server as offline
tmpDb.Exec("update server set Status=0 where AppId=? and ServerId=?", v.AppId, v.ServerId)
return
}
conn.Close()
res, err := model.GetServerInfo(v.AppId, v.ServerId) res, err := model.GetServerInfo(v.AppId, v.ServerId)
if err != nil { if err != nil {
tmpDb.Exec("update server set Status=0 where AppId=? and ServerId=?", v.AppId, v.ServerId) //tmpDb.Exec("update server set Status=0 where AppId=? and ServerId=?", v.AppId, v.ServerId)
return return
} }
serverInfo := res.(map[string]interface{}) serverInfo := res.(map[string]interface{})
@ -119,7 +132,7 @@ func monitorServerInfo() {
// optionally store/update weight // optionally store/update weight
serverInfo["Weight"] = weight serverInfo["Weight"] = weight
// Process serverInfo as needed // Process serverInfo as needed
tmpDb.Exec("update server set Status=1, Online=?,free_mem=?,cpu=?,weight=? where AppId=? and ServerId=?", util.Int(serverInfo["PlayerNum"]), usage_mem, cpu, weight, v.AppId, v.ServerId) tmpDb.Exec("update server set Status=1, Online=?,free_mem=?,cpu=?,weight=?,latency=? where AppId=? and ServerId=?", util.Int(serverInfo["PlayerNum"]), usage_mem, cpu, weight, latency, v.AppId, v.ServerId)
}(v) }(v)
} }
} }

View File

@ -72,7 +72,6 @@ func main() {
// 充值发货 // 充值发货
ChargeApi.POST("test/charge", test.Charge) ChargeApi.POST("test/charge", test.Charge)
ChargeApi.POST("tuyou/charge", tuyou.Charge) ChargeApi.POST("tuyou/charge", tuyou.Charge)
ChargeApi.GET("tuyou/charge", tuyou.Charge)
} }
log.Printf("Ship SDK started on port %d", shipcommon.AppConf.Port) log.Printf("Ship SDK started on port %d", shipcommon.AppConf.Port)
r.Run(fmt.Sprintf(":%d", shipcommon.AppConf.Port)) r.Run(fmt.Sprintf(":%d", shipcommon.AppConf.Port))

View File

@ -37,15 +37,6 @@ func (p *Param) ChangeOrderStatus(Platform string, prodprice string) error {
return err return err
} }
if fmt.Sprintf("%.2f", price) != prodprice { if fmt.Sprintf("%.2f", price) != prodprice {
// str := `
// # 订单金额不匹配报警
// - 项目名称: %s
// - 订单ID: %s
// - 数据库金额: %.2f
// - 回调金额: %s
// - 渠道订单ID: %s
// - 充值ID: %d
// `
return fmt.Errorf("订单金额不匹配,数据库金额:%.2f,回调金额:%s; 订单id:%s, chargeid:%d", price, prodprice, p.OrderId, chargeId) return fmt.Errorf("订单金额不匹配,数据库金额:%.2f,回调金额:%s; 订单id:%s, chargeid:%d", price, prodprice, p.OrderId, chargeId)
} }
if paystatus != 0 { if paystatus != 0 {

17
unit/config_test.go Normal file
View File

@ -0,0 +1,17 @@
package unit
import (
"backend/util"
"fmt"
"testing"
)
func TestEmojiUrl(t *testing.T) {
url := util.GetEmojiURL(1)
fmt.Println("emoji url:", url)
}
func TestFaceUrl(t *testing.T) {
url := util.GetFaceURL(1)
fmt.Println("face url:", url)
}

View File

@ -234,3 +234,41 @@ func TestEs2(t *testing.T) {
fmt.Println("服务器启动成功") fmt.Println("服务器启动成功")
} }
} }
func TestIP(t *testing.T) {
country, city, err := util.GetGeoInfo("58.23.48.244")
if err != nil {
fmt.Println("获取地理位置信息失败:", err)
} else {
fmt.Printf("国家代码: %s, 城市: %s\n", country, city)
}
}
func TestRegister(t *testing.T) {
ctx := context.Background()
client, _ := util.GetEsClient()
var buf bytes.Buffer
query := map[string]interface{}{
"query": map[string]interface{}{
"term": map[string]interface{}{
"game.#event_name": map[string]interface{}{
"value": "register",
},
},
},
"size": 10000,
}
if err := json.NewEncoder(&buf).Encode(query); err != nil {
return
}
res, err := client.Search(
client.Search.WithContext(ctx),
client.Search.WithIndex(".ds-game-node-log*"),
client.Search.WithBody(&buf),
client.Search.WithTrackTotalHits(true),
)
if err != nil {
fmt.Print(err)
}
defer res.Body.Close()
}

View File

@ -66,7 +66,7 @@ func DSlSearch(ctx context.Context, index string, query map[string]interface{},
if err := json.NewEncoder(&buf).Encode(fullQuery); err != nil { if err := json.NewEncoder(&buf).Encode(fullQuery); err != nil {
return nil, fmt.Errorf("编码查询失败: %w", err) return nil, fmt.Errorf("编码查询失败: %w", err)
} }
fmt.Printf("%s", buf.String())
res, err := client.Search( res, err := client.Search(
client.Search.WithContext(ctx), client.Search.WithContext(ctx),
client.Search.WithIndex(index), client.Search.WithIndex(index),
@ -123,12 +123,12 @@ func SearchAssetByUid(app, _uid int, from, size int, start, end int64, itemid in
mustCondition := []map[string]interface{}{ mustCondition := []map[string]interface{}{
{ {
"term": map[string]interface{}{ "term": map[string]interface{}{
"game.#distinct_id": uid, "game.#distinct_id.keyword": uid,
}, },
}, },
{ {
"term": map[string]interface{}{ "term": map[string]interface{}{
"fields.region": region, "fields.environment": region,
}, },
}, },
{ {
@ -218,12 +218,12 @@ func SearchEventByUid(app, _uid int, from, size int, start, end int64, event_nam
mustConditions := []map[string]interface{}{ mustConditions := []map[string]interface{}{
{ {
"term": map[string]interface{}{ "term": map[string]interface{}{
"game.#distinct_id": uid, "game.#distinct_id.keyword": uid,
}, },
}, },
{ {
"term": map[string]interface{}{ "term": map[string]interface{}{
"fields.region": region, "fields.environment": region,
}, },
}, },
{ {
@ -265,7 +265,6 @@ func SearchEventByUid(app, _uid int, from, size int, start, end int64, event_nam
}, },
}, },
} }
result, err := DSlSearch(ctx, "game-user-log*", query, from, size, sort) result, err := DSlSearch(ctx, "game-user-log*", query, from, size, sort)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -328,7 +327,7 @@ func CountDistinctUidLastHour() (int64, int64, error) {
"must": []map[string]interface{}{ "must": []map[string]interface{}{
{ {
"term": map[string]interface{}{ "term": map[string]interface{}{
"fields.region": "us-newyork", "fields.region.keyword": "us-newyork",
}, },
}, },
}, },
@ -367,7 +366,7 @@ func CountDistinctUidLastHour() (int64, int64, error) {
"aggs": map[string]interface{}{ "aggs": map[string]interface{}{
"unique_users": map[string]interface{}{ "unique_users": map[string]interface{}{
"cardinality": map[string]interface{}{ "cardinality": map[string]interface{}{
"field": "game.#distinct_id", "field": "game.#distinct_id.keyword",
}, },
}, },
}, },
@ -384,7 +383,7 @@ func CountDistinctUidLastHour() (int64, int64, error) {
"aggs": map[string]interface{}{ "aggs": map[string]interface{}{
"unique_users": map[string]interface{}{ "unique_users": map[string]interface{}{
"cardinality": map[string]interface{}{ "cardinality": map[string]interface{}{
"field": "game.#distinct_id", "field": "game.#distinct_id.keyword",
}, },
}, },
}, },
@ -396,9 +395,11 @@ func CountDistinctUidLastHour() (int64, int64, error) {
if err := json.NewEncoder(&buf).Encode(query); err != nil { if err := json.NewEncoder(&buf).Encode(query); err != nil {
return 0, 0, fmt.Errorf("编码查询失败: %w", err) return 0, 0, fmt.Errorf("编码查询失败: %w", err)
} }
fmt.Printf("%s", buf.String())
res, err := client.Search( res, err := client.Search(
client.Search.WithContext(ctx), client.Search.WithContext(ctx),
client.Search.WithIndex("game-user-log*"), client.Search.WithIndex("game-user-log*"),
client.Search.WithTrackTotalHits(true),
client.Search.WithBody(&buf), client.Search.WithBody(&buf),
) )
if err != nil { if err != nil {

23
util/geoip.go Normal file
View File

@ -0,0 +1,23 @@
package util
import (
"net"
"github.com/oschwald/geoip2-golang"
)
func GetGeoInfo(ip string) (string, string, error) {
// 这里可以使用第三方库或服务来获取地理位置信息
// 例如,使用 "github.com/oschwald/geoip2-golang" 库读取 MaxMind 的 GeoIP 数据库
// 或者调用第三方 API如 ipinfo.io、ipstack.com 等
db, err := geoip2.Open("./GeoLite2-Country/GeoLite2-City.mmdb")
if err != nil {
return "", "", err
}
defer db.Close()
city, err := db.City(net.ParseIP(ip))
if err != nil {
return "", "", err
}
return city.Country.Names["zh-CN"], city.City.Names["zh-CN"], nil // 返回国家代码或地理位置信息
}

View File

@ -2,7 +2,6 @@ package util
import ( import (
"fmt" "fmt"
"log"
"math/rand" "math/rand"
"sync" "sync"
"time" "time"
@ -35,7 +34,7 @@ func LoginResponse(c *gin.Context, AppId, AreaCode int, Version string) {
} }
func GetUserInfo(AppId, AreaCode int, Uid, Version string) (int, string, int) { func GetUserInfo(AppId, AreaCode int, Uid, Version string) (int, string, int) {
now := time.Now().UnixMilli() // now := time.Now().UnixMilli()
lockUid(Uid) lockUid(Uid)
defer unlockUid(Uid) defer unlockUid(Uid)
// Db := MPool.GetGameDB() // Db := MPool.GetGameDB()
@ -48,14 +47,6 @@ func GetUserInfo(AppId, AreaCode int, Uid, Version string) (int, string, int) {
if len(ServerList) == 0 || Uid == "" || Version == "" { if len(ServerList) == 0 || Uid == "" || Version == "" {
return 0, "", 0 return 0, "", 0
} }
// appConf, _ := GetAppConfig(AppId)
// PlayerDb := MPool.GetMysqlDB(appConf, 1)
// defer PlayerDb.Close()
// var node int
// err := PlayerDb.QueryRow("SELECT node FROM t_player_baseinfo WHERE user_name = ?", Uid).Scan(&node)
// if err != nil {
// fmt.Printf("GetUserInfo query node error: %v\n", err)
// }
node, err := GetUserConnectNode(Uid) node, err := GetUserConnectNode(Uid)
if err != nil { if err != nil {
node = 0 node = 0
@ -126,7 +117,7 @@ func GetUserInfo(AppId, AreaCode int, Uid, Version string) (int, string, int) {
if err == nil { if err == nil {
SaveUserConnectNode(Uid, ServerId) SaveUserConnectNode(Uid, ServerId)
} }
log.Printf("GetUserInfo selected server %d for uid %s, cost time %dms\n", ServerId, Uid, time.Now().UnixMilli()-now) //log.Printf("GetUserInfo selected server %d for uid %s, cost time %dms\n", ServerId, Uid, time.Now().UnixMilli()-now)
return Port, Host, ServerId return Port, Host, ServerId
} }

View File

@ -34,6 +34,7 @@ var FrameData = make(map[string]interface{})
var CardData = make(map[string]interface{}) var CardData = make(map[string]interface{})
var CardCollectData = make(map[string]interface{}) var CardCollectData = make(map[string]interface{})
var NetAssetData = make(map[string]interface{}) var NetAssetData = make(map[string]interface{})
var ItemData = make(map[string]interface{})
func init() { func init() {
data, err := os.ReadFile("config/MergeData.json") data, err := os.ReadFile("config/MergeData.json")
@ -91,6 +92,15 @@ func init() {
NetAssetData = m NetAssetData = m
} }
} }
data, err = os.ReadFile("config/Item.json")
if err == nil {
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
log.Printf("failed to unmarshal ItemData.json: %v", err)
} else {
ItemData = m
}
}
} }
// 获取结构体名称 // 获取结构体名称
@ -605,35 +615,19 @@ func GetLanguageImageURL(key string) string {
// 头像 // 头像
reHead := regexp.MustCompile(`^Data_HeadName_(\d+)$`) reHead := regexp.MustCompile(`^Data_HeadName_(\d+)$`)
if m := reHead.FindStringSubmatch(key); len(m) == 2 { if m := reHead.FindStringSubmatch(key); len(m) == 2 {
if n, err := strconv.Atoi(m[1]); err == nil { id := Int(m[1])
pad := fmt.Sprintf("%03d", n) return GetFaceURL(id)
return fmt.Sprintf("UI/UISprites/Head/DefaultHead/head_pic_circle%s.png", pad)
}
} }
// 头像框 // 头像框
reFrame := regexp.MustCompile(`^Data_HeadFrameName_(\d+)$`) reFrame := regexp.MustCompile(`^Data_HeadFrameName_(\d+)$`)
if m := reFrame.FindStringSubmatch(key); len(m) == 2 { if m := reFrame.FindStringSubmatch(key); len(m) == 2 {
nameKey := m[0] id := Int(m[1])
for _, v := range FrameData { return GetFrameURL(id)
if v == nil {
continue
}
entry, ok := v.(map[string]interface{})
if !ok {
continue
}
if String(entry["NameKey"]) == nameKey {
icon := String(entry["Icon"])
if icon != "" {
return fmt.Sprintf("UI/UISprites/Head/Kuang_new/%s.png", icon)
}
}
}
} }
// Emoji // Emoji
reEmoji := regexp.MustCompile(`^Data_EmojiName_(\d+)$`) reEmoji := regexp.MustCompile(`^Data_EmojiName_(\d+)$`)
if m := reEmoji.FindStringSubmatch(key); len(m) == 2 { if m := reEmoji.FindStringSubmatch(key); len(m) == 2 {
return fmt.Sprintf("UI/UISprites/Head/Emoji/cat_biaoqing_%s.png", m[1]) return GetEmojiURL(Int(m[1]))
} }
// 卡牌 // 卡牌
reCard := regexp.MustCompile(`^UI_MainCardPanel_cardName_`) reCard := regexp.MustCompile(`^UI_MainCardPanel_cardName_`)
@ -661,7 +655,7 @@ func GetLanguageImageURL(key string) string {
if reCardCollect.MatchString(key) { if reCardCollect.MatchString(key) {
parts := strings.Split(key, "_") parts := strings.Split(key, "_")
name := strings.TrimSpace(parts[len(parts)-1]) name := strings.TrimSpace(parts[len(parts)-1])
for k, v := range CardCollectData { for _, v := range CardCollectData {
if v == nil { if v == nil {
continue continue
} }
@ -670,10 +664,7 @@ func GetLanguageImageURL(key string) string {
continue continue
} }
if String(entry["Name"]) == name { if String(entry["Name"]) == name {
icon := "Card/Collect_icon_pethome" + k return String(entry["ResourcesPath"])
if icon != "" {
return fmt.Sprintf("UI/UISprites/%s.png", icon)
}
} }
} }
} }
@ -724,8 +715,72 @@ func GetAppName(AppId int) string {
func GetAppRegion(AppId int) string { func GetAppRegion(AppId int) string {
switch AppId { switch AppId {
case 0: case 0:
return "us-newyork" return "prod"
default: default:
return "cn-shanghai" return "test"
} }
} }
func GetEmojiURL(EmojiId int) string {
return _GetURL(EmojiId, 109)
}
func GetFaceURL(HeadId int) string {
return _GetURL(HeadId, 110)
}
func GetFrameURL(FrameId int) string {
return _GetURL(FrameId, 105)
}
func _GetURL(Id, Type int) string {
for _, v := range ItemData {
info, ok := v.(map[string]interface{})
if !ok {
continue
}
if Int(info["IType"]) == Type {
effect := String(info["Effect"])
effectList := strings.Split(effect, ",")
if Id == Int(effectList[0]) && len(effectList) > 0 {
return info["FullResourcePath"].(string)
}
}
}
return ""
}
func GetCardURL(CardId string) string {
info, ok := CardData[CardId]
if ok && info != nil {
entry, ok := info.(map[string]interface{})
if ok {
return String(entry["ResourcesPath"])
}
}
return ""
}
func GetCardCollectURL(name string) string {
for _, v := range CardCollectData {
info, ok := v.(map[string]interface{})
if !ok {
continue
}
if String(info["Name"]) == name {
return String(info["ResourcesPath"])
}
}
return ""
}
func GetNetAssetURL(Key string) string {
info, ok := NetAssetData[Key]
if ok && info != nil {
entry, ok := info.(map[string]interface{})
if ok {
return String(entry["Picture"])
}
}
return ""
}

View File

@ -16,13 +16,11 @@ func GetWebsocket(AppId, ServerId int) (*websocket.Conn, error) {
if ServerConfig == nil { if ServerConfig == nil {
return nil, fmt.Errorf("server config not found for AppId %d and ServerId %d", AppId, ServerId) return nil, fmt.Errorf("server config not found for AppId %d and ServerId %d", AppId, ServerId)
} }
// if AppId == 0 {
// ServerConfig.Host = "google.bywaystudios.com"
// }
nodeConfig := GetNodeById(ServerConfig.ECS) nodeConfig := GetNodeById(ServerConfig.ECS)
url := fmt.Sprintf("ws://%s:%d/", nodeConfig.Host, ServerConfig.WsPort) url := fmt.Sprintf("ws://%s:%d/", nodeConfig.Host, ServerConfig.WsPort)
// if common.GetEnv() == "dev" {
// url = "ws://127.0.0.1:3567/" // 本地调试使用内网地址
// }
dialer := &websocket.Dialer{ dialer := &websocket.Dialer{
HandshakeTimeout: 5 * time.Second, HandshakeTimeout: 5 * time.Second,
} }