初始化项目

This commit is contained in:
皮蛋13361098506
2025-01-06 16:01:02 +08:00
commit 1b77f62820
575 changed files with 69193 additions and 0 deletions

8
trunk/framework/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
trunk/framework/.idea/Framework.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,5 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" />
</settings>
</component>

6
trunk/framework/.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
trunk/framework/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Framework.iml" filepath="$PROJECT_DIR$/.idea/Framework.iml" />
</modules>
</component>
</project>

6
trunk/framework/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

17
trunk/framework/README Normal file
View File

@@ -0,0 +1,17 @@
v1.0版本,支持以下功能:
1、项目中的各种基础功能
2、其中的managecenterMgr兼容旧版本的ManageCenter2019-12-01之前
3、新增日志记录功能LogMgr 2020-03-09
4、新增监控功能MonitorNewMgr 2020-03-09
5、新增短链功能ShortUrlMgr 2020-03-09
v2.0版本,支持以下功能:
1、新的ManageCenter版本(2019-12-01之后)
v2.0.0.1
LogMgr里面Log日志消息先进行base64编码之后再发送到mq因为原消息有特殊符号
直接发送消息会导致消息返送之后在腾讯mq收到消息之后数据会丢失导致验签失败
v2.0.1.1
新增屏蔽字处理forbidWordsMgr
新增gameServerMgr

View File

@@ -0,0 +1,9 @@
Stack trace:
Frame Function Args
000FFFFA328 0018006021E (00180252DED, 001802340A6, 000FFFFA328, 000FFFF9220)
000FFFFA328 00180048859 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFA328 00180048892 (00180252EA9, 000FFFFA1D8, 000FFFFA328, 00000000000)
000FFFFA328 001800AF0D8 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFA328 001800AF25D (000FFFFA340, 00000000000, 00000000000, 00000000000)
000FFFFA5B0 001800B0673 (000FFFFA340, 00000000000, 00000000000, 00000000000)
End of stack trace

View File

@@ -0,0 +1,34 @@
package configMgr
import (
"goutil/configUtil"
)
// 配置管理对象
type ConfigManager struct {
// 初始化方法列表
initFuncList []func(*configUtil.XmlConfig) error
}
// 注册初始化方法
func (this *ConfigManager) RegisterInitFunc(initFunc func(*configUtil.XmlConfig) error) {
this.initFuncList = append(this.initFuncList, initFunc)
}
// 初始化
func (this *ConfigManager) Init(configObj *configUtil.XmlConfig) error {
for _, initFunc := range this.initFuncList {
if err := initFunc(configObj); err != nil {
return err
}
}
return nil
}
// 创建配置管理对象
func NewConfigManager() *ConfigManager {
return &ConfigManager{
initFuncList: make([]func(*configUtil.XmlConfig) error, 0, 8),
}
}

View File

@@ -0,0 +1,13 @@
package contextcheckMgr
type ResultModel struct {
Code int `json:"code"`
Msg string `json:"msg"`
Result ResultDetail `json:"result"`
}
type ResultDetail struct {
TaskId string `json:"taskId"`
Action int `json:"action"`
CensorType int `json:"censorType"`
}

View File

@@ -0,0 +1,126 @@
package contextcheckMgr
import (
"encoding/json"
"fmt"
"math/rand"
"sort"
"strconv"
"time"
"goutil/logUtil"
"goutil/securityUtil"
"goutil/webUtil"
)
var (
//接口密钥Id
msecretId string = "c98276c73c122eaa65163fe431cbf7d4"
//接口密钥key
msecretKey string = "3d2b39f824fa2f992494e17be500e303"
//业务ID
mbusinessId string = "9559ce74b5c5d24f6b124799a53c7431"
//接口请求地址
mapiurl string = "http://as.dun.163.com/v3/text/check"
//版本号
mversion string = "v3.1"
)
//参数设置
func SetPara(secretId, secretKey, businessId, apiurl, version string) {
msecretId = secretId
msecretKey = secretKey
mbusinessId = businessId
mapiurl = apiurl
mversion = version
}
//content:用户发表内容
//account:玩家账号(用户唯一标识)
//nickname:角色名称
//extStr1:角色区服名称
//extStr2:UserId
//ip:用户IP地址建议抄送辅助机审策略精准调优
//extLon1:区服ID
//返回值 code: 0通过 1嫌疑2不通过
//文本内容检测
func TextCheck(content, account, nickname, extStr1, extStr2, ip string, extLon1 int64) (code int, err error) {
//构造请求参数
postDataDict := make(map[string]string)
postDataDict["secretId"] = msecretId
postDataDict["businessId"] = mbusinessId
postDataDict["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
postDataDict["nonce"] = strconv.FormatInt(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(10000000000), 10)
rawString := fmt.Sprintf("%v%v%v", account, postDataDict["timestamp"], content)
postDataDict["dataId"] = securityUtil.Md5String(rawString, false)
postDataDict["content"] = content
postDataDict["version"] = mversion
postDataDict["account"] = account
postDataDict["nickname"] = nickname
postDataDict["extLon1"] = strconv.FormatInt(extLon1, 10)
// postDataDict["extLon2"] = extLon2
postDataDict["extStr1"] = extStr1
postDataDict["extStr2"] = extStr2
postDataDict["ip"] = ip
postDataDict["signature"] = getSignature(postDataDict)
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
//请求接口
statusCode, result, err := webUtil.PostMapData(mapiurl, postDataDict, header, transport)
//定义错误信息
var logMessage string
//post请求错误
if err != nil {
logMessage = fmt.Sprintf("TextCheck:,错误信息为:%s", err.Error())
logUtil.ErrorLog(logMessage)
return
}
if statusCode != 200 {
logMessage = fmt.Sprintf("TextCheck:%d is wrong", statusCode)
logUtil.ErrorLog(logMessage)
err = fmt.Errorf("TextCheck:%d is wrong", statusCode)
return
}
//反序列化结果
var checkResponseObj *ResultModel
err = json.Unmarshal(result, &checkResponseObj)
if err != nil {
logMessage = fmt.Sprintf("json.Unmarshal(checkResponseObj),err%s", err.Error())
logUtil.ErrorLog(logMessage)
return
}
//判断接口是否调用成功
if checkResponseObj.Code != 200 {
logMessage = fmt.Sprintf("TextCheck接口调用失败,ResultStatus %d", checkResponseObj.Code)
err = fmt.Errorf("TextCheck接口调用失败code%d", checkResponseObj.Code)
return
}
//返回检测结果
code = checkResponseObj.Result.Action
return
}
//生成签名字符串
func getSignature(postDataDict map[string]string) string {
var paramStr string
keys := make([]string, 0, len(postDataDict))
for k := range postDataDict {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
paramStr += key + postDataDict[key]
}
paramStr += msecretKey
return securityUtil.Md5String(paramStr, false)
}

View File

@@ -0,0 +1,49 @@
package contextcheckMgr
import (
"fmt"
"testing"
)
func TestCheck(t *testing.T) {
//content, account, nickname, extLon1, extLon2, extStr1, extStr2, ip
var content = "文本检查测试"
var account = "000e6a6e-7cfc-4da7-81a2-ef3a5d24c586"
var nickname = "不仅仅是喜欢"
var extLon1 int64 = 10001
//var extLon2 = "游戏内聊天"
var extStr1 = "10001铁血丹心"
var extStr2 = "5DF67DD225640567D4D0B6FE273262B5"
var ip = "113.116.66.45"
//设置完全正确的参数
code, err := TextCheck(content, account, nickname, extStr1, extStr2, ip, extLon1)
if err != nil {
t.Errorf("There should be no error, but now there is:%v", err)
return
}
fmt.Printf("检测完成,状态码为:%d\n", code)
content = "修炼发轮功"
code, err = TextCheck(content, account, nickname, extStr1, extStr2, ip, extLon1)
if err != nil {
t.Errorf("There should be no error, but now there is:%v", err)
return
}
fmt.Printf("检测完成,状态码为:%d\n", code)
content = ""
code, err = TextCheck(content, account, nickname, extStr1, extStr2, ip, extLon1)
if err != nil {
t.Errorf("There should be no error, but now there is:%v", err)
return
}
fmt.Printf("检测完成,状态码为:%d\n", code)
}

View File

@@ -0,0 +1,13 @@
package mysqlSync
/*
提供数据同步到mysql的方法。基本逻辑如下
1、对外接收数据以追加的方式保存到大文件中。数据的格式为header(4bytes)+content。
2、启动独立的goroutine来从大文件中读取数据并保存到数据库中。
3、使用syncInfo.txt文件保存当前已经处理的文件的路径以及下一次将要读取的文件的Offset。为了降低向syncInfo.txt文件中写入失败
导致需要从头开始同步数据,所以采用了在指定数目的范围内以追加形式来写入数据的方式;只有达到了指定数量才会将整个文件清空。
对于错误的处理方式,分为以下两种:
1、文件错误由于文件系统是本系统的核心所以如果出现文件的读写出错则需要终止整个进程所以需要抛出panic。
2、数据库错误当数据库不可访问时为了不影响整个外部进程的运行故而不抛出panic而只是通过monitorNewMgr.Report的方式来报告故障。
*/

View File

@@ -0,0 +1,100 @@
package mysqlSync
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"goutil/fileUtil"
"goutil/logUtil"
)
var (
// 记录错误sql命令的文件名
con_Error_FileName = "errorFile.txt"
)
// 定义处理错误命令的文件对象
type errorFile struct {
// 错误文件
file *os.File
// 文件路径
filePath string
// 同步数据对象的唯一标识,用于进行重复判断
identifier string
}
// 保存命令到错误文件
// command: sql命令
func (this *errorFile) SaveCommand(command string) {
this.open()
defer this.close()
// 覆盖写入
this.file.Seek(0, 0)
// 写入命令
_, err := this.file.WriteString(command)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "errorFile.SaveCommand")
err = fmt.Errorf("%s-Write %s to file failed:%s", prefix, command, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
// 清理残留数据
this.file.Truncate(int64(len(command)))
}
// 读取文件中命令
func (this *errorFile) ReadCommand() string {
this.open()
defer this.close()
this.file.Seek(0, 0)
content, err := ioutil.ReadAll(this.file)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "errorFile.ReadCommand")
err = fmt.Errorf("%s-Read command failed:%s", prefix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
return string(content)
}
// 打开文件
func (this *errorFile) open() {
// 打开errorFile文件, 如果没有就创建
var err error
this.file, err = os.OpenFile(this.filePath, os.O_CREATE|os.O_RDWR, os.ModePerm|os.ModeTemporary)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "errorFile.newErrorFile.os.OpenFile")
err = fmt.Errorf("%s-Open File failed:%s", prefix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
}
// 关闭文件
func (this *errorFile) close() {
this.file.Close()
}
// 删除文件
func (this *errorFile) Delete() {
fileUtil.DeleteFile(this.filePath)
}
// 构造错误文件对象
// _dirPath:文件路径
// _identifier:唯一标识
func newErrorFile(_dirPath string, _identifier string) *errorFile {
_filePath := filepath.Join(_dirPath, con_Error_FileName)
return &errorFile{
filePath: _filePath,
identifier: _identifier,
}
}

View File

@@ -0,0 +1,88 @@
package logSqlSync
import (
"database/sql"
"fmt"
"time"
"goutil/logUtil"
)
// 错误信息记录表是否已经初始化
var ifSyncErrorInfoTableInited bool = false
// 同步的错误信息处理对象
type syncErrorInfo struct {
// 数据库连接对象
db *sql.DB
}
// 初始化表信息
func (this *syncErrorInfo) init() error {
// 初始化表结构
if ifSyncErrorInfoTableInited == false {
err := this.initTable(this.db)
if err == nil {
ifSyncErrorInfoTableInited = true
}
return err
}
return nil
}
// 把同步信息更新到数据库
// data:待更新的数据
// 返回值:
// error:错误信息
func (this *syncErrorInfo) AddErrorSql(tran *sql.Tx, data string, errMsg string) error {
updateSql := "INSERT INTO `sync_error_info` (`SqlString`,`ExecuteTime`,`RetryCount`,`ErrMessage`) VALUES(?,?,?,?);"
var err error
if tran != nil {
_, err = tran.Exec(updateSql, data, time.Now(), 0, errMsg)
} else {
_, err = this.db.Exec(updateSql, data, time.Now(), 0, errMsg)
}
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncErrorInfo.AddErrorSql Error:%s", err.Error()))
}
return err
}
// 初始化同步信息表结构
// db:数据库连接对象
func (this *syncErrorInfo) initTable(db *sql.DB) error {
// 创建同步信息表
createTableSql := `CREATE TABLE IF NOT EXISTS sync_error_info (
Id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增Id',
SqlString varchar(1024) NOT NULL COMMENT '执行的sql',
ExecuteTime datetime NOT NULL COMMENT '最近一次执行时间',
RetryCount int NOT NULL COMMENT '重试次数',
ErrMessage text NULL COMMENT '执行错误的信息',
PRIMARY KEY (Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='未执行成功的sql数据';`
if _, err := db.Exec(createTableSql); err != nil {
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncErrorInfo.initTable Error:%s", err.Error()))
return err
}
return nil
}
// 创建同步信息对象
// _db:数据库连接对象
// 返回值:
// 同步信息对象
func newSyncErrorInfoObject(_db *sql.DB) (result *syncErrorInfo, err error) {
result = &syncErrorInfo{
db: _db,
}
err = result.init()
return result, err
}

View File

@@ -0,0 +1,204 @@
package logSqlSync
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"time"
"Framework/dataSyncMgr/mysqlSync/sqlSync"
"goutil/logUtil"
)
// 同步对象定义
type SyncObject struct {
// 服务器组Id
serverGroupId int32
// 同步数据的存储路径
dirPath string
// 同步数据对象的唯一标识,用于进行重复判断
identifier string
// 数据库对象
dbObj *sql.DB
// 同步信息对象
syncingInfoObj *syncingInfo
// 错误处理对象
errorHandleObj *syncErrorInfo
// 同步对象
syncObj *sqlSync.SyncObject
}
// 初始化
// baseObj:基础同步对象
func (this *SyncObject) Init(baseObj *sqlSync.SyncObject) {
this.syncObj = baseObj
// 初始化同步信息对象
syncingInfoObj, err := newSyncingInfoObject(this.serverGroupId, this.dbObj)
if err != nil {
panic(err)
}
//// 初始化错误处理对象
errorHandleObj, err := newSyncErrorInfoObject(this.dbObj)
if err != nil {
panic(err)
}
this.syncingInfoObj = syncingInfoObj
this.errorHandleObj = errorHandleObj
// 初始化当前处理的文件
fileList := sqlSync.GetDataFileList(this.dirPath)
filePath, _ := this.syncingInfoObj.GetSyncingInfo()
if len(filePath) < 0 && len(fileList) > 0 {
this.syncingInfoObj.Update(fileList[0], 0, nil)
}
}
// 获取正在同步的信息
// filePath:文件路径
// offset:文件偏移量
func (this *SyncObject) GetSyncingInfo() (filePath string, offset int64) {
return this.syncingInfoObj.GetSyncingInfo()
}
// 更新
// filePath:文件路径
// offset:文件偏移量
// tran:事务对象
// 返回值:
// error:错误对象
func (this *SyncObject) Update(filePath string, offset int64, tx *sql.Tx) error {
return this.syncingInfoObj.Update(filePath, offset, tx)
}
// 同步一条sql语句
// command:待执行的命令
// filePath:保存路径
// offset:文件偏移量
// 返回值:
// error:错误信息
func (this *SyncObject) SyncOneSql(command string, filePath string, offset int64) {
var err error
for {
err = sqlSync.ExecuteByTran(this.dbObj, func(tran *sql.Tx) (isCommit bool, err error) {
// 保存sql到数据库
err = this.syncToMysql(command, tran)
if err != nil {
return
}
// 保存进度信息到数据库
err = this.syncingInfoObj.Update(filePath, offset, tran)
if err != nil {
return
}
isCommit = true
return
})
// 如果是连接出错,则仍然循环执行
if err != nil {
if sqlSync.CheckIfConnectionError(err.Error()) {
time.Sleep(5 * time.Second)
continue
}
}
// 如果不是数据库连接出错,则算是执行完成
break
}
// 如果存在错误,则循环尝试执行
if err != nil {
this.recordSqlError(command, filePath, offset, err.Error())
}
return
}
// 同步数据到mysql中
// command:sql语句
// tx:事务处理对象
// 返回值:
// error:错误信息
func (this *SyncObject) syncToMysql(command string, tx *sql.Tx) error {
_, err := tx.Exec(command)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/logSqlSync/syncObject.syncToMysql error:%s", err.Error()))
return err
}
return nil
}
// 错误处理
// cmd:待执行的命令
// filePath:保存路径
// offset:文件偏移量
// errMsg:错误信息
func (this *SyncObject) recordSqlError(command string, filePath string, offset int64, errMsg string) {
errMsg = sqlSync.GetSimpleErrorMessage(errMsg)
for {
err := sqlSync.ExecuteByTran(this.dbObj, func(tran *sql.Tx) (isCommit bool, err error) {
// 保存sql到数据库
err = this.errorHandleObj.AddErrorSql(tran, command, errMsg)
if err != nil {
return
}
// 保存进度信息到数据库
err = this.syncingInfoObj.Update(filePath, offset, tran)
if err != nil {
return
}
isCommit = true
return
})
if err == nil {
return
}
time.Sleep(5 * time.Second)
}
}
// 创新新的mysql同步对象
// dirPath:存放数据的目录
// identifier:当前数据的唯一标识(可以使用数据库表名)
// dbObj:数据库对象
// syncingInfoObj:同步信息记录对象
// errorHandleObj:错误处理对象
// 返回值:
// mysql同步对象
func NewSyncObject(serverGroupId int32, dirPath, identifier string, dbObj *sql.DB) *SyncObject {
dirPath = filepath.Join(dirPath, identifier)
// 创建更新目录
err := os.MkdirAll(dirPath, os.ModePerm|os.ModeTemporary)
if err != nil {
err = fmt.Errorf("%s-%s-make dir failed:%s", identifier, "SyncObject.newSyncObject.os.MkdirAll", err)
logUtil.ErrorLog(err.Error())
panic(err)
}
// 构造同步信息对象
result := &SyncObject{
serverGroupId: serverGroupId,
dirPath: dirPath,
identifier: identifier,
dbObj: dbObj,
}
return result
}

View File

@@ -0,0 +1,186 @@
package logSqlSync
import (
"database/sql"
"fmt"
"time"
"goutil/logUtil"
)
// 同步信息表是否已经被初始化
var ifSyncingTableInited bool = false
// 同步信息项,保存已经处理过的文件的信息
type syncingModel struct {
// 服务器组Id
ServerGroupId int32
// 待处理文件的绝对路径
FilePath string
// 待处理文件的偏移量
FileOffset int64
// 更新时间
UpdateTime time.Time
}
// 同步信息对象
type syncingInfo struct {
// 服务器组Id
ServerGroupId int32
// 同步信息项
item *syncingModel
// 数据库连接对象
db *sql.DB
}
// 获取同步信息
// filePath:正在同步的文件
// fileOffset:同步到的位置
func (this *syncingInfo) GetSyncingInfo() (filePath string, fileOffset int64) {
return this.item.FilePath, this.item.FileOffset
}
// 更新正在同步的位置和文件信息
// filePath:文件路径
// offset:当前同步到的位置
// tran:事务对象可以为nil
// 返回值:
// error:处理的错误信息
func (this *syncingInfo) Update(filePath string, offset int64, tran *sql.Tx) error {
this.item.FilePath = filePath
this.item.FileOffset = offset
this.item.UpdateTime = time.Now()
// 更新到数据库
return this.update(this.item, tran)
}
// 初始化同步信息
// 返回值:
// error:错误信息
func (this *syncingInfo) init() error {
// 数据表初始化
if ifSyncingTableInited == false {
if err := this.initSyncingInfoTable(this.db); err == nil {
ifSyncingTableInited = true
} else {
return err
}
}
// 获取此表的同步信息
data, exist, err := this.get()
if err != nil {
return err
}
// 2. 如果同步信息不存在,则初始化一条到此表
if exist == false {
data = &syncingModel{
ServerGroupId: this.ServerGroupId,
FilePath: "",
FileOffset: 0,
UpdateTime: time.Now(),
}
}
this.item = data
return nil
}
// 初始化同步信息表结构
// db:数据库连接对象
func (this *syncingInfo) initSyncingInfoTable(db *sql.DB) error {
// 创建同步信息表
createTableSql := `CREATE TABLE IF NOT EXISTS syncing_info (
ServerGroupId int NOT NULL COMMENT '服务器组Id',
FilePath varchar(500) NOT NULL COMMENT '正在同步的文件路径',
FileOffset bigint(20) NOT NULL COMMENT '偏移量',
UpdateTime datetime NOT NULL COMMENT '最后一次更新时间',
PRIMARY KEY (ServerGroupId)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='正在同步的文件信息';`
if _, err := db.Exec(createTableSql); err != nil {
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.initSyncingInfoTable Error:%s", err.Error()))
return err
}
return nil
}
// 从数据库获取数据
// 返回值:
// data:获取到的数据
// exist:是否存在此数据
// err:错误信息
func (this *syncingInfo) get() (data *syncingModel, exist bool, err error) {
//// 从数据库查询
querySql := fmt.Sprintf("SELECT FilePath,FileOffset,UpdateTime FROM syncing_info WHERE ServerGroupId ='%v'", this.ServerGroupId)
var rows *sql.Rows
rows, err = this.db.Query(querySql)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.get.Query ServerGroupId:%v error:%s", this.ServerGroupId, err.Error()))
return
}
defer rows.Close()
if rows.Next() == false {
exist = false
return
}
exist = true
// 读取数据
data = &syncingModel{
ServerGroupId: this.ServerGroupId,
}
err = rows.Scan(&data.FilePath, &data.FileOffset, &data.UpdateTime)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.get.Query ServerGroupId:%v error:%s", this.ServerGroupId, err.Error()))
return
}
return
}
// 把同步信息更新到数据库
// data:待更新的数据
// tran:事务处理对象
// 返回值:
// error:错误信息
func (this *syncingInfo) update(data *syncingModel, tran *sql.Tx) error {
updateSql := "REPLACE INTO `syncing_info` SET `ServerGroupId` = ?, `FilePath` = ?,`FileOffset` = ?, `UpdateTime` = ?;"
var err error
if tran != nil {
_, err = tran.Exec(updateSql, data.ServerGroupId, data.FilePath, data.FileOffset, data.UpdateTime)
} else {
_, err = this.db.Exec(updateSql, data.ServerGroupId, data.FilePath, data.FileOffset, data.UpdateTime)
}
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.update ServerGroupId:%v error:%s", this.ServerGroupId, err.Error()))
}
return err
}
// 创建同步信息对象
// _dirPath:目录的路径
// _identifier:当前数据的唯一标识(可以使用数据库表名)
// _db:数据库连接对象
// 返回值:
// 同步信息对象
func newSyncingInfoObject(serverGroupId int32, _db *sql.DB) (result *syncingInfo, err error) {
result = &syncingInfo{
ServerGroupId: serverGroupId,
db: _db,
}
err = result.init()
return result, err
}

View File

@@ -0,0 +1,62 @@
package main
import (
"fmt"
"github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"Framework/dataSyncMgr/mysqlSync"
"goutil/logUtil"
)
var _ = mysql.DeregisterLocalFile
var (
connectionString = "root:moqikaka3309@tcp(10.1.0.10:3309)/develop_liujun?charset=utf8&parseTime=true&loc=Local&timeout=60s"
maxOpenConns = 10
maxIdleConns = 10
syncFileSize = 1024 * 1024
)
var (
// 数据库对象
dbObj *gorm.DB
// 同步管理对象
syncMgr *mysqlSync.SyncMgr
)
func init() {
// 初始化数据库连接
dbObj = initMysql()
// 构造同步管理对象
syncMgr = mysqlSync.NewLogSyncMgr(1, "Sync", syncFileSize, dbObj.DB())
}
// 初始化Mysql
func initMysql() *gorm.DB {
dbObj, err := gorm.Open("mysql", connectionString)
if err != nil {
panic(fmt.Errorf("初始化数据库:%s失败错误信息为%s", connectionString, err))
}
logUtil.DebugLog(fmt.Sprintf("连接mysql:%s成功", connectionString))
if maxOpenConns > 0 && maxIdleConns > 0 {
dbObj.DB().SetMaxOpenConns(maxOpenConns)
dbObj.DB().SetMaxIdleConns(maxIdleConns)
}
return dbObj
}
// 注册同步对象
func registerSyncObj(identifier string) {
syncMgr.RegisterSyncObj(identifier)
}
// 保存sql数据
func save(identifier string, command string) {
syncMgr.Save(identifier, command)
}

View File

@@ -0,0 +1,82 @@
package main
import (
"fmt"
"sync"
"time"
"goutil/stringUtil"
)
var (
wg sync.WaitGroup
)
func init() {
wg.Add(1)
}
func main() {
playerMgr := newPlayerMgr()
// insert
go func() {
for {
id := stringUtil.GetNewGUID()
name := fmt.Sprintf("Hero_%s", id)
obj := newPlayer(id, name)
playerMgr.insert(obj)
insert(obj)
time.Sleep(10 * time.Millisecond)
}
}()
/*
// update
go func() {
for {
obj := playerMgr.randomSelect()
if obj == nil {
continue
}
suffix := mathUtil.GetRandInt(1000)
newName := fmt.Sprintf("Hero_%d", suffix)
obj.resetName(newName)
update(obj)
time.Sleep(10 * time.Millisecond)
}
}()
// delete
go func() {
for {
obj := playerMgr.randomSelect()
if obj == nil {
continue
}
playerMgr.delete(obj)
clear(obj)
time.Sleep(10 * time.Millisecond)
}
}()
// errorFile
go func() {
for {
time.Sleep(1 * time.Hour)
id := stringUtil.GetNewGUID()
name := fmt.Sprintf("Hero_%s%s", id, id)
obj := newPlayer(id, name)
playerMgr.insert(obj)
print("errorFile")
insert(obj)
}
}()
*/
wg.Wait()
}

View File

@@ -0,0 +1,64 @@
package main
import (
"sync"
)
type player struct {
// 玩家id
Id string `gorm:"column:Id;primary_key"`
// 玩家名称
Name string `gorm:"column:Name"`
}
func (this *player) resetName(name string) {
this.Name = name
}
func (this *player) tableName() string {
return "player"
}
func newPlayer(id, name string) *player {
return &player{
Id: id,
Name: name,
}
}
type playerMgr struct {
playerMap map[string]*player
mutex sync.Mutex
}
func (this *playerMgr) insert(obj *player) {
this.mutex.Lock()
defer this.mutex.Unlock()
this.playerMap[obj.Id] = obj
}
func (this *playerMgr) delete(obj *player) {
this.mutex.Lock()
defer this.mutex.Unlock()
delete(this.playerMap, obj.Id)
}
func (this *playerMgr) randomSelect() *player {
this.mutex.Lock()
defer this.mutex.Unlock()
for _, obj := range this.playerMap {
return obj
}
return nil
}
func newPlayerMgr() *playerMgr {
return &playerMgr{
playerMap: make(map[string]*player),
}
}

View File

@@ -0,0 +1,29 @@
//package test
package main
import (
"fmt"
)
var (
con_player_tableName = "player"
)
func init() {
registerSyncObj(con_player_tableName)
}
func insert(obj *player) {
command := fmt.Sprintf("INSERT INTO `%s` (`Id`,`Name`) VALUES ('%v','%v') ", con_player_tableName, obj.Id, obj.Name)
save(con_player_tableName, command)
}
func update(obj *player) {
command := fmt.Sprintf("UPDATE `%s` SET `Name` = '%v' WHERE `Id` = '%v';", con_player_tableName, obj.Name, obj.Id)
save(con_player_tableName, command)
}
func clear(obj *player) {
command := fmt.Sprintf("DELETE FROM %s where Id = '%v';", con_player_tableName, obj.Id)
save(con_player_tableName, command)
}

View File

@@ -0,0 +1,73 @@
package sqlSync
import (
"encoding/binary"
"errors"
"os"
"goutil/fileUtil"
"goutil/intAndBytesUtil"
)
const (
// 头部字节长度
con_Header_Length = 4
)
var (
// 字节的大小端顺序
byteOrder = binary.LittleEndian
)
// 按照指定方式读取文本内容
// fileObj:大文件对象
// data:待写入的数据
// 返回值:
// error:写入是否存在异常
func Write(fileObj *fileUtil.BigFile, data string) error {
// 获得数据内容的长度
dataLength := len(data)
// 将长度转化为字节数组
header := intAndBytesUtil.Int32ToBytes(int32(dataLength), byteOrder)
// 将头部与内容组合在一起
message := append(header, data...)
// 写入数据
return fileObj.WriteMessage(message)
}
// 从文件读取一条数据
// fileObj:文件对象
// 返回值:
// result:读取到的字符串
// err:错误信息
func Read(fileObj *os.File) (result string, readLen int64, err error) {
// 1. 读取头部内容
header := make([]byte, 4)
var n int
n, err = fileObj.Read(header)
if err != nil {
return
}
if n < con_Header_Length {
err = errors.New("can not read 4 byte for read len")
readLen = int64(n)
return
}
dataLength := intAndBytesUtil.BytesToInt32(header, byteOrder)
// 2. 读取指定长度的内容
data := make([]byte, dataLength)
n, err = fileObj.Read(data)
if err != nil {
return
}
readLen = int64(len(header) + int(dataLength))
result = string(data)
return
}

View File

@@ -0,0 +1,139 @@
package sqlSync
import (
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"goutil/fileUtil"
"goutil/logUtil"
)
const (
// 第一个文件名
con_Default_FileName = "00000000"
// 文件名后缀
con_FileName_Suffix = "data"
)
// 同步数据对象(用于往文件中写入sql语句)
type SqlFile struct {
// 存放同步数据的文件夹路径
dirPath string
// 同步数据对象的唯一标识,用于进行重复判断
identifier string
// 保存数据的大文件对象
bigFileObj *fileUtil.BigFile
// 数据同步对象
mutex sync.Mutex
}
// 将数据写入同步数据对象
// data:待写入的数据
func (this *SqlFile) Write(data string) {
this.mutex.Lock()
defer this.mutex.Unlock()
// 写入数据
err := Write(this.bigFileObj, data)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "SqlFile.write.bigFileObj.WriteMessage")
err = fmt.Errorf("%s-Write message to big file object failed:%s", prefix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
}
// 获取大文件对象的文件绝对路径
func (this *SqlFile) FileFullName() string {
return filepath.Join(this.dirPath, this.bigFileObj.FileName())
}
// 当前读写的文件名
func (this *SqlFile) FileName() string {
return this.bigFileObj.FileName()
}
// 创建同步数据对象
// _dirPath:目录的路径
// _identifier:当前数据的唯一标识(可以使用数据库表名)
// _maxFileSize:每个大文件的最大写入值单位Byte
// 返回值:
// 同步数据对象
func NewSqlFile(dirPath, identifier, fileName string, maxFileSize int) *SqlFile {
result := &SqlFile{
dirPath: dirPath,
identifier: identifier,
}
// 初始化大文件对象
if fileName == "" {
fileName = con_Default_FileName
}
bigFileObj, err := fileUtil.NewBigFileWithNewFileNameFunc2(dirPath, "", fileName, maxFileSize, NewFileName)
if err != nil {
prefix := fmt.Sprintf("%s-%s", result.identifier, "SqlFile.newSqlFile.fileUtil.NewBigFileWithNewFileNameFunc")
err = fmt.Errorf("%s-Create big file object failed:%s", prefix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
result.bigFileObj = bigFileObj
return result
}
// 根据当前文件名生成下一个sql文件名
// prefix:文件名前缀
// path:当前文件的路径
// 返回值:
// string:下一个文件的完整路径
func NewFileName(prefix, path string) string {
fullName := filepath.Base(path)
curFileName := strings.Split(fullName, ".")[0]
curFileId, err := strconv.Atoi(curFileName)
if err != nil {
err = fmt.Errorf("%s-Convert newFileName:%s to int failed:%s", prefix, curFileName, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
newFileId := curFileId + 1
newFileName := fmt.Sprintf("%08d", newFileId)
// 加上文件后缀
newFileName = fmt.Sprintf("%s.%s", newFileName, con_FileName_Suffix)
return newFileName
}
// 获取文件夹下所有的sql文件
// dirPath:指定要获取的文件夹路径
// 返回值:
// []string:sql文件列表
func GetDataFileList(dirPath string) []string {
// 获取当前目录中所有的数据文件列表
fileList, err := fileUtil.GetFileList2(dirPath, "", con_FileName_Suffix)
if err != nil {
if os.IsNotExist(err) {
} else {
err = fmt.Errorf("%s/*.%s-Get file list failed:%s", dirPath, con_FileName_Suffix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
}
// 如果文件数量大于1则进行排序以便于后续处理
if len(fileList) > 1 {
sort.Strings(fileList)
}
return fileList
}

View File

@@ -0,0 +1,271 @@
package sqlSync
import (
"database/sql"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"goutil/debugUtil"
"goutil/fileUtil"
"goutil/logUtil"
)
// 同步对象定义
type SyncObject struct {
// 同步数据的存储路径
dirPath string
// 同步数据对象的唯一标识,用于进行重复判断
identifier string
// 数据库对象
dbObj *sql.DB
// 处理数据写入的文件
sqlFileObj *SqlFile
// 同步处理对象
syncHandleObj SyncHandler
}
// 进行同步对象初始化
// maxFileSize:每个大文件的最大写入值单位Byte
func (this *SyncObject) Init(maxFileSize int) {
// 启动时同步所有数据(然后才能从数据库中查询数据,以免数据丢失)
this.syncHandleObj.Init(this)
// 构造同步数据对象
fileName, _ := this.syncHandleObj.GetSyncingInfo()
this.sqlFileObj = NewSqlFile(this.dirPath, this.identifier, fileName, maxFileSize)
// 当前没有正在同步的文件,则指向当前正在写的文件
if len(fileName) <= 0 {
this.syncHandleObj.Update(this.sqlFileObj.FileFullName(), 0, nil)
}
// 启动一个新goroutine来负责同步数据
go func() {
/* 此处不使用goroutineMgr.Monitor/ReleaseMonitor因为此处不能捕获panic需要让外部进程终止执行
因为此模块的文件读写为核心逻辑,一旦出现问题必须停止进程,否则会造成脏数据
*/
this.Sync()
}()
}
// 保存数据到本地文件
// command:待保存的指令
func (this *SyncObject) Save(command string) {
this.sqlFileObj.Write(command)
}
// 循环同步多个文件
func (this *SyncObject) Sync() {
// 开始循环同步
for {
// 同步当前文件
this.syncOneFile()
// 当前文件同步完成,记录同步日志
nowFilePath, _ := this.syncHandleObj.GetSyncingInfo()
// 删除已同步完成的文件
WaitForOk(func() bool {
fileExist, err := fileUtil.IsFileExists(nowFilePath)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncObject IsFileExists error:%s", err.Error()))
return false
}
if fileExist == false {
return true
}
err = fileUtil.DeleteFile(nowFilePath)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncObject delete file error:%s", err.Error()))
return false
}
return true
}, 10*time.Second)
// 当前文件同步完成,获取下个文件
nextFileName := NewFileName("", nowFilePath)
filePath := filepath.Join(this.dirPath, nextFileName)
exist, err := fileUtil.IsFileExists(filePath)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncObject IsFileExists error:%s", err.Error()))
panic(err)
}
// 如果文件不存在,退出
if !exist {
// fmt.Println("协程退出了")
return
}
// 更新同步的位置信息 此处忽略错误是因为,哪怕是出错了,也不会影响整体逻辑
this.syncHandleObj.Update(filePath, 0, nil)
}
}
// 同步单个文件
func (this *SyncObject) syncOneFile() {
// 获取信息同步项对象
filePath, offset := this.syncHandleObj.GetSyncingInfo()
// 打开待读取的文件
f, exist := this.openFile(filePath)
if exist == false {
// logUtil.WarnLog(fmt.Sprintf("待同步的文件不存在,跳过此文件:%s", filePath),"")
return
}
defer f.Close()
for {
// 移动到需要读取的位置
if _, err := f.Seek(offset, io.SeekStart); err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.Seek")
err = fmt.Errorf("%s-Seek offset for header failed:%s", prefix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
command, readLen, err := Read(f)
if err != nil {
// 如果读取到文件末尾,判断是否等待
if err == io.EOF {
if this.sqlFileObj != nil && strings.Contains(filePath, this.sqlFileObj.FileName()) {
time.Sleep(20 * time.Millisecond)
continue
}
// 如果该文件是空文件,同步更新信息
return
}
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncOneFile.f.Read")
err = fmt.Errorf("%s-Read header failed:%s", prefix, err)
logUtil.ErrorLog(err.Error())
panic(err)
}
// 3. 同步到mysql中,并更新同步位置
this.syncHandleObj.SyncOneSql(command, filePath, offset+readLen)
// 4. 更新内存中的同步位置
offset += readLen
}
}
// 打开待读取的文件
// filePath:待打开的文件
// 返回值:
// *os.File:文件句柄,
func (this *SyncObject) openFile(filePath string) (f *os.File, exist bool) {
var err error
for {
exist, err = fileUtil.IsFileExists(filePath)
if err != nil {
err = fmt.Errorf("check file error,filePath:%v error:%v", filePath, err.Error())
logUtil.ErrorLog(err.Error())
time.Sleep(time.Second * 5)
continue
}
if exist == false {
// 如果文件不存在,则跳过此文件
logUtil.WarnLog(fmt.Sprintf("file no exist, skip file:%v", filePath))
exist = false
return
}
exist = true
// 打开当前处理文件
f, err = os.OpenFile(filePath, os.O_RDONLY, os.ModePerm|os.ModeTemporary)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncOneFile.os.OpenFile")
err = fmt.Errorf("%s-Open file:%s failed:%s", prefix, filePath, err)
logUtil.ErrorLog(err.Error())
time.Sleep(time.Second * 5)
continue
}
return
}
}
// 同步数据到mysql中
// command:sql语句
// tx:事务处理对象
// 返回值:
// error:错误信息
func (this *SyncObject) syncToMysql(command string, tx *sql.Tx) error {
_, err := tx.Exec(command)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncToMysql")
err = fmt.Errorf("%s-%s Update to mysql failed:%s", prefix, command, err)
logUtil.ErrorLog(err.Error())
debugUtil.Printf("fatal Error:%v", err.Error())
return err
}
return nil
}
// 创新新的mysql同步对象
// dirPath:存放数据的目录
// identifier:当前数据的唯一标识(可以使用数据库表名)
// dbObj:数据库对象
// _syncHandleObj:同步处理对象
// 返回值:
// mysql同步对象
func NewSyncObject(dirPath, identifier string, dbObj *sql.DB, _syncHandleObj SyncHandler) *SyncObject {
dirPath = filepath.Join(dirPath, identifier)
// 创建更新目录
err := os.MkdirAll(dirPath, os.ModePerm|os.ModeTemporary)
if err != nil {
err = fmt.Errorf("%s-%s-make dir failed:%s", identifier, "SyncObject.newSyncObject.os.MkdirAll", err)
logUtil.ErrorLog(err.Error())
panic(err)
}
// 构造同步信息对象
result := &SyncObject{
dirPath: dirPath,
identifier: identifier,
dbObj: dbObj,
syncHandleObj: _syncHandleObj,
}
return result
}
// 同步处理接口
type SyncHandler interface {
// 初始化
Init(baseObj *SyncObject)
// 获取正在同步的信息
// filePath:文件路径
// offset:文件偏移量
GetSyncingInfo() (filePath string, offset int64)
// 更新
// filePath:文件路径
// offset:文件偏移量
// tran:事务对象
// 返回值:
// error:错误对象
Update(filePath string, offset int64, tran *sql.Tx) error
// 同步一条sql
// command:指令数据
// filePath:文件路径
// offset:文件偏移量
SyncOneSql(command string, filePath string, offset int64)
}

View File

@@ -0,0 +1,99 @@
package sqlSync
import (
"database/sql"
"fmt"
"strings"
"time"
"goutil/logUtil"
)
// 以事务的方式执行
// db:数据库对象
// funcObj:对应的具体处理函数
// 返回值:
// error:处理是否存在错误
func ExecuteByTran(db *sql.DB, funcObj func(tran *sql.Tx) (isCommit bool, err error)) error {
tran, err := db.Begin()
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("start transaction error:%v", err.Error()))
return err
}
// 事务处理
isCommit := false
defer func() {
if isCommit {
err = tran.Commit()
} else {
err = tran.Rollback()
}
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("transaction end error:%v", err.Error()))
}
}()
isCommit, err = funcObj(tran)
return err
}
// 循环执行知道返回成功为止
// funcObj:待执行的函数
// interval:执行间隔时间
func WaitForOk(funcObj func() bool, interval time.Duration) {
for {
if funcObj() == false {
time.Sleep(interval)
}
break
}
}
// 检查是否是连接错误
// errMsg:错误信息
// 返回值:
// bool:true连接错误 false:其他异常
func CheckIfConnectionError(errMsg string) bool {
//// 连接被关闭
ifConnectionClose := strings.Contains(errMsg, "A connection attempt failed because the connected party did not properly respond")
if ifConnectionClose {
return true
}
// 使用过程中连接断开
ifConnectionClose = strings.Contains(errMsg, "No connection could be made")
if ifConnectionClose {
return true
}
// 事务处理过程中连接断开的提示
ifConnectionClose = strings.Contains(errMsg, "bad connection")
if ifConnectionClose {
return true
}
// socket压根儿连不上的处理
ifConnectionClose = strings.Contains(errMsg, "A socket operation was attempted to an unreachable network")
if ifConnectionClose {
return true
}
// 用户无法访问
return strings.Contains(errMsg, "Access denied for user")
}
// 获取比较简洁的错误信息
// errMsg:错误信息
// 返回值:
// string:比较简洁的错误信息
func GetSimpleErrorMessage(errMsg string) string {
if strings.Contains(errMsg, "Error 1064: You have an error in your SQL syntax") {
return "SqlError"
}
return errMsg
}

View File

@@ -0,0 +1,119 @@
package mysqlSync
import (
"database/sql"
"fmt"
"sync"
"Framework/dataSyncMgr/mysqlSync/logSqlSync"
"Framework/dataSyncMgr/mysqlSync/sqlSync"
"goutil/debugUtil"
"goutil/logUtil"
)
// 数据同步管理
type SyncMgr struct {
// 服务器组Id
serverGroupId int32
// 同步数据的存储路径
dirPath string
// 大文件对象size
maxFileSize int
// 数据库对象
dbObj *sql.DB
// 同步对象集合
syncObjMap map[string]*sqlSync.SyncObject
// 同步对象锁
mutex sync.RWMutex
// 新建实例对象的函数
newInstanceFunc func(mgr *SyncMgr, identifier string) *sqlSync.SyncObject
}
// 注册同步对象
// identifier:当前数据的唯一标识(可以使用数据库表名)
func (this *SyncMgr) RegisterSyncObj(identifier string) {
this.mutex.Lock()
defer this.mutex.Unlock()
// 判断是否设置了相同的唯一标识,以免弄混淆
if _, exists := this.syncObjMap[identifier]; exists {
prefix := fmt.Sprintf("%s-%s", identifier, "SyncMgr.RegisterSyncObj")
err := fmt.Errorf("%s has already existed, please change another identifier", prefix)
logUtil.ErrorLog(err.Error())
panic(err)
}
syncObj := this.newInstanceFunc(this, identifier)
syncObj.Init(this.maxFileSize)
this.syncObjMap[identifier] = syncObj
if debugUtil.IsDebug() {
fmt.Printf("%s同步对象成功注册进SyncMgr, 当前有%d个同步对象\n", identifier, len(this.syncObjMap))
}
}
// 保存数据
// identifier:当前数据的唯一标识(可以使用数据库表名)
// command:sql命令
func (this *SyncMgr) Save(identifier string, command string) {
this.mutex.RLock()
defer this.mutex.RUnlock()
syncObj, exists := this.syncObjMap[identifier]
if !exists {
err := fmt.Errorf("syncObj:%s does not existed, please register first", identifier)
logUtil.ErrorLog(err.Error())
panic(err)
}
syncObj.Save(command)
}
// 构造同步管理对象
// serverGroupId:服务器组Id
// dirPath: 文件目录
// maxFileSize: 大文件对象大小
// survivalTime: 同步数据存活时间 (单位hour)
// dbObj: 数据库对象
func NewSyncMgr(serverGroupId int32, dirPath string, maxFileSize int, survivalTime int, dbObj *sql.DB) *SyncMgr {
result := &SyncMgr{
serverGroupId: serverGroupId,
dirPath: dirPath,
maxFileSize: maxFileSize,
dbObj: dbObj,
syncObjMap: make(map[string]*sqlSync.SyncObject),
newInstanceFunc: func(mgr *SyncMgr, identifier string) *sqlSync.SyncObject {
handler := newSyncObject(mgr.dirPath, identifier, mgr.dbObj)
return sqlSync.NewSyncObject(mgr.dirPath, identifier, mgr.dbObj, handler)
},
}
return result
}
// 新建日志同步管理对象
// serverGroupId:服务器组Id
// dirPath: 文件目录
// maxFileSize: 大文件对象大小
// dbObj: 数据库对象
func NewLogSyncMgr(serverGroupId int32, dirPath string, maxFileSize int, dbObj *sql.DB) *SyncMgr {
result := &SyncMgr{
serverGroupId: serverGroupId,
dirPath: dirPath,
maxFileSize: maxFileSize,
dbObj: dbObj,
syncObjMap: make(map[string]*sqlSync.SyncObject),
newInstanceFunc: func(mgr *SyncMgr, identifier string) *sqlSync.SyncObject {
handler := logSqlSync.NewSyncObject(mgr.serverGroupId, mgr.dirPath, identifier, mgr.dbObj)
return sqlSync.NewSyncObject(mgr.dirPath, identifier, mgr.dbObj, handler)
},
}
return result
}

View File

@@ -0,0 +1,210 @@
package mysqlSync
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"time"
"Framework/dataSyncMgr/mysqlSync/sqlSync"
"goutil/debugUtil"
"goutil/logUtil"
)
// 同步对象定义
type SyncObject struct {
// 同步数据的存储路径
dirPath string
// 同步数据对象的唯一标识,用于进行重复判断
identifier string
// 数据库对象
dbObj *sql.DB
// 同步信息对象
syncingInfoObj *syncingInfo
// 错误处理对象
errorHandleObj *errorFile
// 同步对象
syncObj *sqlSync.SyncObject
}
// 进行同步对象初始化
// maxFileSize:每个大文件的最大写入值单位Byte
func (this *SyncObject) Init(baseObj *sqlSync.SyncObject) {
this.syncObj = baseObj
// 创建同步信息记录对象
syncingInfoObj, err := newSyncingInfoObject(this.identifier, this.dbObj)
if err != nil {
panic(err)
}
this.syncingInfoObj = syncingInfoObj
// 启动时同步所有数据(然后才能从数据库中查询数据,以免数据丢失)
this.syncOldData()
}
// 同步完成之前未同步完的数据
func (this *SyncObject) syncOldData() {
// 获取文件列表(有序的列表)
fileList := sqlSync.GetDataFileList(this.dirPath)
filePath, _ := this.syncingInfoObj.GetSyncingInfo()
// 判断是否有文件
if len(fileList) == 0 {
return
}
// 判断当前文件是否为空,如果为空则将第一个文件赋给它
if filePath == "" {
this.syncingInfoObj.Update(fileList[0], 0, nil)
}
// 开始同步数据
this.syncObj.Sync()
return
}
// 获取正在同步的信息
// filePath:文件路径
// offset:文件偏移量
func (this *SyncObject) GetSyncingInfo() (filePath string, offset int64) {
return this.syncingInfoObj.GetSyncingInfo()
}
// 更新
// filePath:文件路径
// offset:文件偏移量
// tran:事务对象
// 返回值:
// error:错误对象
func (this *SyncObject) Update(filePath string, offset int64, tran *sql.Tx) error {
return this.syncingInfoObj.Update(filePath, offset, tran)
}
// 同步一条sql语句
// command:待执行的命令
// filePath:保存路径
// offset:文件偏移量
// 返回值:
// error:错误信息
func (this *SyncObject) SyncOneSql(command string, filePath string, offset int64) {
err := this.syncOneSqlDetail(command, filePath, offset)
if err == nil {
return
}
// 发送监控报警
this.handleError(command, filePath, offset, err)
return
}
// 同步一条sql语句的具体逻辑
// command:待执行的命令
// filePath:保存路径
// offset:文件偏移量
// 返回值:
// error:错误信息
func (this *SyncObject) syncOneSqlDetail(command string, filePath string, offset int64) error {
return sqlSync.ExecuteByTran(this.dbObj, func(tx *sql.Tx) (isCommit bool, err error) {
// 保存sql到数据库
err = this.syncToMysql(command, tx)
if err != nil {
return false, err
}
// 保存进度信息到数据库
err = this.syncingInfoObj.Update(filePath, offset, tx)
if err != nil {
return false, err
}
return true, nil
})
}
// 同步数据到mysql中
// command:待执行的命令
// tx:事务对象
// 返回值:
// error:错误信息
func (this *SyncObject) syncToMysql(command string, tx *sql.Tx) error {
_, err := tx.Exec(command)
if err != nil {
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncToMysql")
err = fmt.Errorf("%s-%s Update to mysql failed:%s", prefix, command, err)
logUtil.ErrorLog(err.Error())
debugUtil.Printf("fatal Error:%v", err.Error())
return err
}
return nil
}
// 进行错误处理
// command:存在异常的数据
// filePath:文件路径
// offset:文件偏移量
// err:错误信息
func (this *SyncObject) handleError(command string, filePath string, offset int64, err error) {
defer this.errorHandleObj.Delete()
// 保存当前sql命令
this.errorHandleObj.SaveCommand(command)
// 循环处理当前命令,直到没有错误
beginTime := time.Now().Unix()
for {
// 每隔5分钟发送警报
if time.Now().Unix()-beginTime > 5*60 {
beginTime = time.Now().Unix()
}
// 每次循环休眠20秒
time.Sleep(5 * time.Second)
command = this.errorHandleObj.ReadCommand()
err = this.syncOneSqlDetail(command, filePath, offset)
if err != nil {
continue
}
break
}
}
// 创新新的mysql同步对象
// dirPath:存放数据的目录
// identifier:当前数据的唯一标识(可以使用数据库表名)
// dbObj:数据库对象
// syncingInfoObj:同步信息记录对象
// errorHandleObj:错误处理对象
// 返回值:
// mysql同步对象
func newSyncObject(dirPath, identifier string, dbObj *sql.DB) *SyncObject {
dirPath = filepath.Join(dirPath, identifier)
// 创建更新目录
err := os.MkdirAll(dirPath, os.ModePerm|os.ModeTemporary)
if err != nil {
err = fmt.Errorf("%s-%s-make dir failed:%s", identifier, "SyncObject.newSyncObject.os.MkdirAll", err)
logUtil.ErrorLog(err.Error())
panic(err)
}
// 构造同步信息对象
result := &SyncObject{
dirPath: dirPath,
identifier: identifier,
dbObj: dbObj,
errorHandleObj: newErrorFile(dirPath, identifier),
}
return result
}

View File

@@ -0,0 +1,62 @@
package main
import (
"fmt"
"github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"Framework/dataSyncMgr/mysqlSync"
"goutil/logUtil"
)
var _ = mysql.DeregisterLocalFile
var (
connectionString = "root:moqikaka3309@tcp(10.1.0.10:3309)/develop_liujun?charset=utf8&parseTime=true&loc=Local&timeout=60s"
maxOpenConns = 10
maxIdleConns = 10
syncFileSize = 1024 * 1024
)
var (
// 数据库对象
dbObj *gorm.DB
// 同步管理对象
syncMgr *mysqlSync.SyncMgr
)
func init() {
// 初始化数据库连接
dbObj = initMysql()
// 构造同步管理对象
syncMgr = mysqlSync.NewSyncMgr(1, "Sync", syncFileSize, 1, dbObj.DB())
}
// 初始化Mysql
func initMysql() *gorm.DB {
dbObj, err := gorm.Open("mysql", connectionString)
if err != nil {
panic(fmt.Errorf("初始化数据库:%s失败错误信息为%s", connectionString, err))
}
logUtil.DebugLog(fmt.Sprintf("连接mysql:%s成功", connectionString))
if maxOpenConns > 0 && maxIdleConns > 0 {
dbObj.DB().SetMaxOpenConns(maxOpenConns)
dbObj.DB().SetMaxIdleConns(maxIdleConns)
}
return dbObj
}
// 注册同步对象
func registerSyncObj(identifier string) {
syncMgr.RegisterSyncObj(identifier)
}
// 保存sql数据
func save(identifier string, command string) {
syncMgr.Save(identifier, command)
}

View File

@@ -0,0 +1,82 @@
package main
import (
"fmt"
"sync"
"time"
"goutil/mathUtil"
"goutil/stringUtil"
)
var (
wg sync.WaitGroup
)
func init() {
wg.Add(1)
}
func main() {
playerMgr := newPlayerMgr()
// insert
go func() {
for {
id := stringUtil.GetNewGUID()
name := fmt.Sprintf("Hero_%s", id)
obj := newPlayer(id, name)
playerMgr.insert(obj)
insert(obj)
time.Sleep(10 * time.Millisecond)
}
}()
// update
go func() {
for {
obj := playerMgr.randomSelect()
if obj == nil {
continue
}
suffix := mathUtil.GetRand().GetRandInt(1000)
newName := fmt.Sprintf("Hero_%d", suffix)
obj.resetName(newName)
update(obj)
time.Sleep(10 * time.Millisecond)
}
}()
// delete
go func() {
for {
obj := playerMgr.randomSelect()
if obj == nil {
continue
}
playerMgr.delete(obj)
clear(obj)
time.Sleep(10 * time.Millisecond)
}
}()
// errorFile
go func() {
for {
time.Sleep(1 * time.Hour)
id := stringUtil.GetNewGUID()
name := fmt.Sprintf("Hero_%s%s", id, id)
obj := newPlayer(id, name)
playerMgr.insert(obj)
print("errorFile")
insert(obj)
}
}()
wg.Wait()
}

View File

@@ -0,0 +1,64 @@
package main
import (
"sync"
)
type player struct {
// 玩家id
Id string `gorm:"column:Id;primary_key"`
// 玩家名称
Name string `gorm:"column:Name"`
}
func (this *player) resetName(name string) {
this.Name = name
}
func (this *player) tableName() string {
return "player"
}
func newPlayer(id, name string) *player {
return &player{
Id: id,
Name: name,
}
}
type playerMgr struct {
playerMap map[string]*player
mutex sync.Mutex
}
func (this *playerMgr) insert(obj *player) {
this.mutex.Lock()
defer this.mutex.Unlock()
this.playerMap[obj.Id] = obj
}
func (this *playerMgr) delete(obj *player) {
this.mutex.Lock()
defer this.mutex.Unlock()
delete(this.playerMap, obj.Id)
}
func (this *playerMgr) randomSelect() *player {
this.mutex.Lock()
defer this.mutex.Unlock()
for _, obj := range this.playerMap {
return obj
}
return nil
}
func newPlayerMgr() *playerMgr {
return &playerMgr{
playerMap: make(map[string]*player),
}
}

View File

@@ -0,0 +1,29 @@
//package test
package main
import (
"fmt"
)
var (
con_player_tableName = "player"
)
func init() {
registerSyncObj(con_player_tableName)
}
func insert(obj *player) {
command := fmt.Sprintf("INSERT INTO `%s` (`Id`,`Name`) VALUES ('%v','%v') ", con_player_tableName, obj.Id, obj.Name)
save(con_player_tableName, command)
}
func update(obj *player) {
command := fmt.Sprintf("UPDATE `%s` SET `Name` = '%v' WHERE `Id` = '%v';", con_player_tableName, obj.Name, obj.Id)
save(con_player_tableName, command)
}
func clear(obj *player) {
command := fmt.Sprintf("DELETE FROM %s where Id = '%v';", con_player_tableName, obj.Id)
save(con_player_tableName, command)
}

View File

@@ -0,0 +1,222 @@
package mysqlSync
import (
"database/sql"
"fmt"
"time"
"goutil/logUtil"
)
var (
// 是否已经初始化了正在同步的表信息
ifSyncingInfoTableInited = false
// 表初始化错误信息
initTableError error = nil
// 表是否已经初始化
isTableInited bool = false
)
// 同步信息项,保存已经处理过的文件的信息
type syncingModel struct {
// 唯一标识
Identifier string
// 待处理文件的绝对路径
FilePath string
// 待处理文件的偏移量
FileOffset int64
// 更新时间
UpdateTime time.Time
}
// 同步信息对象
type syncingInfo struct {
// 同步数据对象的唯一标识,用于进行重复判断
identifier string
// 同步信息项
item *syncingModel
// 数据库连接对象
db *sql.DB
}
// 获取同步信息
// filePath:正在同步的文件
// fileOffset:同步到的位置
func (this *syncingInfo) GetSyncingInfo() (filePath string, fileOffset int64) {
return this.item.FilePath, this.item.FileOffset
}
// 更新正在同步的位置和文件信息
// filePath:文件路径
// offset:当前同步到的位置
// tran:事务对象可以为nil
// 返回值:
// error:处理的错误信息
func (this *syncingInfo) Update(filePath string, offset int64, tran *sql.Tx) error {
this.item.FilePath = filePath
this.item.FileOffset = offset
this.item.UpdateTime = time.Now()
// 更新到数据库
return this.update(this.item, tran)
}
// 初始化同步信息
// 返回值:
// error:错误信息
func (this *syncingInfo) init() error {
if ifSyncingInfoTableInited == false {
err := initSyncingInfoTable(this.db)
if err != nil {
return err
}
ifSyncingInfoTableInited = true
}
// 获取此表的同步信息
data, exist, err := this.get()
if err != nil {
return err
}
// 2. 如果同步信息不存在,则初始化一条到此表
if exist == false {
data = &syncingModel{
Identifier: this.identifier,
FilePath: "",
FileOffset: 0,
UpdateTime: time.Now(),
}
err = this.insert(data)
if err != nil {
return err
}
}
this.item = data
return nil
}
// 从数据库获取数据
// 返回值:
// data:获取到的数据
// exist:是否存在此数据
// err:错误信息
func (this *syncingInfo) get() (data *syncingModel, exist bool, err error) {
//// 从数据库查询
querySql := fmt.Sprintf("SELECT FilePath,FileOffset,UpdateTime FROM syncing_info WHERE Identifier ='%v';", this.identifier)
var rows *sql.Rows
rows, err = this.db.Query(querySql)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo get.query error:%v", err.Error()))
return
}
defer rows.Close()
if rows.Next() == false {
exist = false
return
}
exist = true
// 读取数据
data = &syncingModel{
Identifier: this.identifier,
}
err = rows.Scan(&data.FilePath, &data.FileOffset, &data.UpdateTime)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo get.scan error:%v", err.Error()))
return
}
return
}
// 把同步信息写入到数据库
// data:待插入的数据
// 返回值:
// error:错误信息
func (this *syncingInfo) insert(data *syncingModel) error {
insertSql := "INSERT INTO syncing_info(Identifier,FilePath,FileOffset,UpdateTime) VALUES(?,?,?,?);"
_, err := this.db.Exec(insertSql, data.Identifier, data.FilePath, data.FileOffset, data.UpdateTime)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo insert error:%v", err.Error()))
}
return err
}
// 把同步信息更新到数据库
// data:待更新的数据
// tran:事务对象
// 返回值:
// error:错误信息
func (this *syncingInfo) update(data *syncingModel, tran *sql.Tx) error {
updateSql := "UPDATE syncing_info SET FilePath=?, FileOffset=?, UpdateTime=? WHERE Identifier=?;"
var err error
if tran != nil {
_, err = tran.Exec(updateSql, data.FilePath, data.FileOffset, data.UpdateTime, data.Identifier)
} else {
_, err = this.db.Exec(updateSql, data.FilePath, data.FileOffset, data.UpdateTime, data.Identifier)
}
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo update error:%v", err.Error()))
}
return err
}
// 创建同步信息对象
// _dirPath:目录的路径
// _identifier:当前数据的唯一标识(可以使用数据库表名)
// _db:数据库连接对象
// 返回值:
// 同步信息对象
func newSyncingInfoObject(identifier string, _db *sql.DB) (result *syncingInfo, err error) {
result = &syncingInfo{
identifier: identifier,
db: _db,
}
err = result.init()
return result, err
}
// 初始化同步信息表结构
// db:数据库连接对象
func initSyncingInfoTable(db *sql.DB) error {
if isTableInited {
return initTableError
}
defer func() {
isTableInited = true
}()
// 创建同步信息表
createTableSql := `CREATE TABLE IF NOT EXISTS syncing_info (
Identifier varchar(30) NOT NULL COMMENT '同步唯一标识(数据库表名)',
FilePath varchar(500) NOT NULL COMMENT '正在同步的文件路径',
FileOffset bigint NOT NULL COMMENT '文件偏移量',
UpdateTime datetime NOT NULL COMMENT '最后一次更新时间',
PRIMARY KEY (Identifier)
) COMMENT 'P表同步信息';`
if _, initTableError = db.Exec(createTableSql); initTableError != nil {
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo initSyncingInfoTable error:%v", initTableError.Error()))
return initTableError
}
return nil
}

View File

@@ -0,0 +1,6 @@
package exitMgr
// 程序退出包,提供程序退出时的功能
// 使用方法:
// 1、先调用RegisterExitFunc方法将系统退出时需要调用的方法进行注册。
// 2、在程序退出时调用Exit()方法

View File

@@ -0,0 +1,33 @@
package exitMgr
import (
"fmt"
"goutil/logUtil"
)
var (
exitFuncMap = make(map[string]func())
)
// RegisterExitFunc ...注册Exit方法
// funcName:方法名称
// exitFuncexit方法
func RegisterExitFunc(funcName string, exitFunc func()) {
if _, exists := exitFuncMap[funcName]; exists {
panic(fmt.Sprintf("%s已经存在请重新取名", funcName))
}
exitFuncMap[funcName] = exitFunc
logUtil.InfoLog("RegisterExitFunc funcName:%s当前共有%d个注册", funcName, len(exitFuncMap))
}
// Exit ...退出程序
// 返回值:
// 无
func Exit() {
for funcName, exitFunc := range exitFuncMap {
exitFunc()
logUtil.InfoLog("Call ExitFunc:%s Finish.", funcName)
}
}

View File

@@ -0,0 +1,256 @@
package forbidWordsMgr
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"Framework/goroutineMgr"
. "Framework/managecenterModel"
"goutil/dfaUtil"
"goutil/logUtil"
"goutil/mathUtil"
"goutil/webUtil"
"goutil/zlibUtil"
)
type ForbidWords struct {
//游戏ID
GameId int
//屏蔽字
Words string
}
// 请求屏蔽字库地址
const GetForbidWordURL string = "http://forbidword.7qule.com/Query"
//const GetForbidWordURL string = "http://10.253.0.186:10090/Query"
var (
mHashValue string
mDFAUtil *dfaUtil.DFAUtil
mGameId int
rand *mathUtil.Rand
mGameOnly bool = false
)
// 获取屏蔽字
func refreshForbidWord() error {
//定义参数
requestParamMap := make(map[string]string, 0)
requestParamMap["GameId"] = strconv.Itoa(mGameId)
requestParamMap["HashValue"] = mHashValue
requestParamMap["DataType"] = "0"
requestParamMap["IsResultCompressed"] = "true"
if mGameOnly {
requestParamMap["GameOnly"] = "true"
}
//data, _ := json.Marshal(requestParamMap)
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 1000)
statusCode, returnBytesTemp, err := webUtil.PostMapData(GetForbidWordURL, requestParamMap, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(GetForbidWordURL, requestParamMap, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取屏蔽字出错url:%s,错误信息为:%s", GetForbidWordURL, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取屏蔽字出错url:%s,错误码为:%d", GetForbidWordURL, statusCode))
return err
}
//解压
returnBytes, err := zlibUtil.Decompress(returnBytesTemp)
if err != nil {
logUtil.ErrorLog("返回结果解压失败")
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取屏蔽字反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
msg := fmt.Sprintf("获取屏蔽字出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
} else if returnObj.Code == 0 && len(returnObj.HashValue) == 0 { //表示没有更新
return nil
}
// 解析Data
var tmpForbidWordList []*ForbidWords
if data, ok := returnObj.Data.(string); !ok {
msg := "获取屏蔽字出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpForbidWordList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取屏蔽字反序列化数据出错,错误信息为:%s", err))
return err
}
}
//缓存hasvalue
mHashValue = returnObj.HashValue
//获取屏蔽字数组
var temWordArray []string
for _, item := range tmpForbidWordList {
temWordArray = append(temWordArray, strings.ToUpper(item.Words))
}
//dfa
mDFAUtil = dfaUtil.NewDFAUtil(temWordArray)
return nil
}
// 是否包含屏蔽字
func IsSensitiveWords(input string) (exist bool) {
input = strings.ToUpper(input)
exist = mDFAUtil.IsMatch(input)
return
}
// 取出屏蔽字及开始位置
func SensitiveWords(input string) (words []string, pos []int, exist bool) {
input2 := strings.ToUpper(input)
inputRune := []rune(input)
startIndexList, endIndexList := mDFAUtil.SearchSentence(input2)
if len(startIndexList) > 0 {
exist = true
words = make([]string, 0, len(startIndexList))
pos = make([]int, 0, len(startIndexList))
for i := 0; i < len(startIndexList); i++ {
start := startIndexList[i]
end := endIndexList[i]
words = append(words, string(inputRune[start:end+1]))
pos = append(pos, start)
}
}
return
}
// 取出屏蔽字及开始及结束位置
func SensitiveWordsEndStartPos(input string) (words []string, starts, ends []int, exist bool) {
input2 := strings.ToUpper(input)
inputRune := []rune(input)
startIndexList, endIndexList := mDFAUtil.SearchSentence(input2)
if len(startIndexList) > 0 {
exist = true
words = make([]string, 0, len(startIndexList))
starts = startIndexList
ends = endIndexList
for i := 0; i < len(startIndexList); i++ {
start := startIndexList[i]
end := endIndexList[i]
words = append(words, string(inputRune[start:end+1]))
//pos = append(pos, start)
}
}
return
}
// 根据用户输入的替换词替换敏感词
func ReplaceSendsitiveWords(input, replaceStr string) (newStr string) {
words, _, _, exist := SensitiveWordsEndStartPos(input)
newStr = input
//如果不存在敏感词,则直接返回
if !exist {
return
}
//循环替换
for _, sendsitiveWord := range words {
newStr = strings.Replace(newStr, sendsitiveWord, replaceStr, -1)
}
return
}
// 判断服务器是否存在
func IfServerExists() (exist bool) {
return
}
// 处理屏蔽字
func HandleSendsitiveWords(input string) (newStr string) {
newStr = mDFAUtil.HandleWord(input, rune('*'))
return
}
// 处理敏感字-xjChat
func HandleSendsitiveWordsUseStr(input string, replaceCh string) (newStr string) {
newStr = mDFAUtil.HandleWordUseStr(input, replaceCh)
return
}
// 定时刷新屏蔽字库
func StartRefreshForbidWordListTread(gameId int) {
rand = mathUtil.GetRand()
mGameId = gameId
// 定时刷新数据
go func() {
goroutineName := "forbidWordsMgr.StartRefreshForbidWordListTread"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
for {
func() {
// 防止panic
defer func() {
if r := recover(); r != nil {
logUtil.LogUnknownError(r)
}
}()
// 刷新屏蔽字
refreshForbidWord()
}()
// 每5分钟刷新一次
time.Sleep(time.Duration(rand.GetRandRangeInt64(120, 300)) * time.Second)
}
}()
}
// 定时刷新屏蔽字库(排除公共字库,只刷新游戏内的)
func StartRefreshForbidWordListTreadExcludeComm(gameId int) {
rand = mathUtil.GetRand()
mGameId = gameId
mGameOnly = true
// 定时刷新数据
go func() {
goroutineName := "forbidWordsMgr.StartRefreshForbidWordListTread"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
for {
// 刷新屏蔽字
refreshForbidWord()
// 每5分钟刷新一次
time.Sleep(time.Duration(rand.GetRandRangeInt64(120, 300)) * time.Second)
}
}()
}

View File

@@ -0,0 +1,61 @@
package forbidWordsMgr
import (
"fmt"
"testing"
"time"
)
// type Persion struct {
// name string
// }
// 屏蔽字详细信息
func Test1(t *testing.T) {
//启动获取屏蔽字
refreshForbidWord()
words, pos, exist := SensitiveWords("测试,测试")
if exist {
t.Log(words, pos)
}
t.Log("END")
}
func TestForbidWord(t *testing.T) {
// //启动获取屏蔽字
// refreshForbidWord()
// //判断是否有屏蔽字
// str := "好多花姑凉"
// isExist := IsSensitiveWords(str)
// fmt.Println("是否有敏感字:", isExist)
// //处理屏蔽字
// if isExist {
// newstr := HandleSendsitiveWords(str)
// fmt.Println("newStr:", newstr)
// }
// persionSlice := make([]*Persion, 0, 5)
// //var persionSlice []*Persion
// for i := 0; i < 10; i++ {
// p := &Persion{
// name: "append",
// }
// persionSlice = append(persionSlice, p)
// fmt.Println(&persionSlice)
// }
///fmt.Println(IfServerExists())
StartRefreshForbidWordListTread(27)
time.Sleep(10 * time.Second)
fmt.Println(IsSensitiveWords("g点双享器"))
fmt.Println(IsSensitiveWords("G点双享器"))
time.Sleep(5 * time.Hour)
}

View File

@@ -0,0 +1,80 @@
package gameLogMgr
import (
"fmt"
"github.com/Shopify/sarama"
"goutil/debugUtil"
"goutil/logUtilPlus"
)
var (
producer sarama.AsyncProducer
)
// 启动生产者
// 参数:
// brokerList:Broker地址
// userId:用户名(可默认为空字符串)
// passWard:密码(可默认为空字符串)
// 返回值:
// 无
func Start(brokerList []string, userId string, passWard string) {
/*
设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
对于游戏日志设置为WaitForLocal即可如果是游戏数据则应设置为WaitForAll
设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
*/
var err error
config := sarama.NewConfig()
config.Net.SASL.User = userId
config.Net.SASL.Password = passWard
config.Producer.Return.Successes = false
config.Producer.Return.Errors = true
config.Producer.Retry.Max = 10
config.Producer.RequiredAcks = sarama.WaitForLocal
producer, err = sarama.NewAsyncProducer(brokerList, config)
if err != nil {
panic(fmt.Errorf("Kafka Start failed. Error: %v\n", err))
}
go func() {
for err := range producer.Errors() {
debugUtil.Printf("Send message to kafka failed. Error: %v\n", err.Err)
logUtilPlus.ErrorLog("Send message to kafka failed. Error: %v\n", err.Err)
}
}()
}
func Stop() {
if producer != nil {
err := producer.Close()
if err != nil {
debugUtil.Printf("Stop kafka failed. Error: %v\n", err)
logUtilPlus.ErrorLog("Stop kafka failed. Error: %v\n", err)
}
}
}
// 写入游戏日志
// 参数:
// serverGroupId: 游戏服务器组Id
// key: 标识
// message: 日志
// 返回值: 无
func Write(topic string, serverGroupId int32, message string) {
if producer == nil {
debugUtil.Printf("Send message to kafka failed. producer is nil")
logUtilPlus.ErrorLog("Send message to kafka failed. producer is nil")
return
}
msg := &sarama.ProducerMessage{}
msg.Topic = topic
msg.Key = sarama.StringEncoder(fmt.Sprintf("%d", serverGroupId))
msg.Value = sarama.ByteEncoder(message)
// Send to kafka
producer.Input() <- msg
}

View File

@@ -0,0 +1,67 @@
package gameLogMgr
import (
"bytes"
"fmt"
"testing"
"time"
"goutil/debugUtil"
"goutil/stringUtil"
"goutil/timeUtil"
)
func TestWrite(t *testing.T) {
debugUtil.SetDebug(true)
brokerList := []string{"10.1.0.202:9092", "10.1.0.204:9092", "10.1.0.205:9092"}
Start(brokerList, "", "")
topic := "test2"
serverGroupId := int32(20011)
for i := 0; i < 5; i++ {
Write(topic, serverGroupId, getGameLog(i))
}
time.Sleep(5 * time.Second)
Stop()
}
func BenchmarkWrite(b *testing.B) {
debugUtil.SetDebug(true)
topic := "test2"
serverGroupId := int32(20011)
brokerList := []string{"10.1.0.202:9092", "10.1.0.204:9092", "10.1.0.205:9092"}
Start(brokerList, "", "")
b.ResetTimer()
for i := 0; i < b.N; i++ {
Write(topic, serverGroupId, getGameLog(i))
}
b.StopTimer()
Stop()
}
func getGameLog(int2 int) string {
//kafkaLog组装
var buffer bytes.Buffer
buffer.WriteString("{")
buffer.WriteString(fmt.Sprintf("\"#account_id\":\"%s\"", "123456789123456789"))
buffer.WriteString(",")
buffer.WriteString(fmt.Sprintf("\"#time\":\"%s\"", time.Now().Format("2006-01-02 15:04:05")))
buffer.WriteString(",")
buffer.WriteString(fmt.Sprintf("\"#uuid\":\"%s\"", stringUtil.GetNewGUID()))
buffer.WriteString(",")
buffer.WriteString(fmt.Sprintf("\"#event_id\":\"\""))
buffer.WriteString(",")
buffer.WriteString(fmt.Sprintf("\"#type\":\"track\""))
buffer.WriteString(",")
buffer.WriteString(fmt.Sprintf("\"#event_name\":\"achievement_change_log\""))
buffer.WriteString(",")
buffer.WriteString(fmt.Sprintf("\"properties\":{\"PartnerId\":%d,\"ServerId\":%d,\"Crtime\":\"%s\",\"Crdate\":\"%s\"}", 600021, int2, timeUtil.ToDateTimeString2(time.Now()), timeUtil.ToDateString2(time.Now())))
buffer.WriteString("}")
return buffer.String()
}

View File

@@ -0,0 +1,14 @@
package gameLogMgr
// 游戏日志对象
type GameLog struct {
ServerGroupId int32 // 服务器组Id
LogSql string // 日志Sql
}
func newGameLog(serverGroupId int32, logSql string) *GameLog {
return &GameLog{
ServerGroupId: serverGroupId,
LogSql: logSql,
}
}

View File

@@ -0,0 +1,76 @@
package gameServerMgr
import (
. "Framework/managecenterModel"
)
var (
mAreaList = make([]*Area, 0)
)
// 解析大区信息
func ParseAreaInfo(areaList []*Area) {
mAreaList = areaList
}
// 根据服务器组id获取大区Id
func GetAreaIdByGroupId(groupId int32) (areaId int32) {
areaId = 0
//如果没有大区数据返回0
if mAreaList == nil || len(mAreaList) < 1 {
return
}
for _, area := range mAreaList {
if area.CheckServerIdIsInRange(groupId) {
areaId = area.AreaId
break
}
}
return
}
// 根据服务器组id获取大区对象数据
func GetAreaDBByGroupId(groupId int32) (areaDB *Area, exist bool) {
//如果没有大区数据,返回空
exist = false
if mAreaList == nil || len(mAreaList) < 1 {
return
}
for _, area := range mAreaList {
if area.CheckServerIdIsInRange(groupId) {
areaDB = area
exist = true
break
}
}
return
}
// 根据大区ID获取大区信息
func GetAreaDBbyAreaID(areaId int32) (areaDB *Area, exist bool) {
//如果没有大区数据,返回空
exist = false
if mAreaList == nil || len(mAreaList) < 1 {
return
}
for _, area := range mAreaList {
if area.AreaId == areaId {
areaDB = area
exist = true
break
}
}
return
}
// 获取所有大区信息
func GetAllAreaDB() []*Area {
tempList := mAreaList
return tempList
}

View File

@@ -0,0 +1,33 @@
package gameServerMgr
import (
"encoding/json"
. "Framework/managecenterModel"
)
var (
mChargeConfigMap = make(map[int32][]*ChargeConfig, 0)
)
//解析充值配置信息
func ParseChargeConfigInfo(partnerList []*Partner) {
tmpChargeConfigMap := make(map[int32][]*ChargeConfig, 0)
//循环解析所有合作商里面的充值配置信息
for _, partner := range partnerList {
var chargeConfigList []*ChargeConfig
if err := json.Unmarshal([]byte(partner.ChargeConfig), &chargeConfigList); err == nil {
tmpChargeConfigMap[partner.Id] = chargeConfigList
}
}
mChargeConfigMap = tmpChargeConfigMap
}
// 根据合作商Id获取合作商充值配置对象
func GetChargeConfigList(partnerId int32) (chargeConfigList []*ChargeConfig, exist bool) {
chargeConfigList, exist = mChargeConfigMap[partnerId]
return
}

View File

@@ -0,0 +1,433 @@
package gameServerMgr
import (
"encoding/json"
"fmt"
"math"
"sort"
"strconv"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/securityUtil"
"goutil/webUtil"
)
type ChargeUtil struct{}
//获取Int32型的充值金额(充值金额必须大于1否则可能就会无效)
func (this *ChargeUtil) GetInt32ChargeMoney(money float32) int32 {
return int32(money)
}
//获取排好序的充值配置列表
func (this *ChargeUtil) GetOrderedChargeConfigList(partnerId int32, isMonthCard bool) (list []*ChargeConfig, exist bool) {
var tempList []*ChargeConfig
tempList, exist = GetChargeConfigList(partnerId)
//如果不存在,则返回空集合
if !exist {
return
}
//循环遍历集合,找出是月卡的数据
for _, item := range tempList {
if item.IsMonthCard == isMonthCard {
list = append(list, item)
}
}
return
}
// 生成充值订单号
// url:生成订单号的服务器地址
// productId:产品Id
// partnerId:合作商Id
// serverId:服务器Id
// userId:平台用户Id
// playerId:玩家Id
// mac:mac
// idfa:idfa
// ip:ip
// imei:imei
// extra:extra
// isMonthCard:是否月卡
// 返回值:
// 订单号
// 错误对象
func (this *ChargeUtil) GenerateOrderId(url, productId string, partnerId, serverId int32,
userId, playerId, mac, idfa, ip, imei, extra string,
isMonthCard bool) (orderId string, err error) {
if extra == "" {
extra = "FromGameServer"
}
// 定义请求参数
postDict := make(map[string]string)
postDict["ProductId"] = productId
postDict["PartnerId"] = fmt.Sprintf("%d", partnerId)
postDict["ServerId"] = fmt.Sprintf("%d", serverId)
postDict["UserId"] = userId
postDict["PlayerId"] = playerId
postDict["MAC"] = mac
postDict["IDFA"] = idfa
postDict["IP"] = ip
postDict["IMEI"] = imei
postDict["Extra"] = extra
postDict["IsMonthCard"] = strconv.FormatBool(isMonthCard)
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err1 := webUtil.PostMapData(url, postDict, header, transport)
if err1 != nil {
logUtil.ErrorLog(fmt.Sprintf("生成充值订单号出错url:%s,错误信息为:%s", url, err1))
err = err1
return
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("生成充值订单号出错url:%s,错误码为:%d", url, statusCode))
err = fmt.Errorf("生成充值订单号出错url:%s,错误码为:%d", url, statusCode)
return
}
orderId = string(returnBytes)
if orderId == "" {
err = fmt.Errorf("Order Is Empty")
return
}
return
}
//生成支付订单信息
func (this *ChargeUtil) GeneratePayOrderInfo(url string, partnerId int32, jsonBase64Data string) (returnObject ReturnObject, err error) {
//String requestParam = String.Format(@"PartnerId={0}&JsonBase64Data={1}", partnerId, jsonBase64Data);
postDict := make(map[string]string)
postDict["PartnerId"] = fmt.Sprintf("%d", partnerId)
postDict["JsonBase64Data"] = jsonBase64Data
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
//通过POST方式请求数据
statusCode, returnBytes, err1 := webUtil.PostMapData(url, postDict, header, transport)
if err1 != nil {
logUtil.ErrorLog(fmt.Sprintf("生成支付订单信息错误:%s,错误信息为:%s", url, err1))
err = err1
return
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("生成支付订单信息出错url:%s,错误码为:%d", url, statusCode))
err = fmt.Errorf("生成支付订单信息出错url:%s,错误码为:%d", url, statusCode)
return
}
err = json.Unmarshal(returnBytes, &returnObject)
return
//return JsonUtil.Deserialize<ReturnObject>(WebUtil.PostWebData(generateOrderIdUrl, requestParam, DataCompress.NotCompress));
}
// 获取充值配置列表
// partnerId合作商Id
// vipLv玩家VIP等级
// isMonthCard是否月卡
// filter过滤器
// 返回值:
// 充值配置列表
// 是否存在
// 错误对象
func (this *ChargeUtil) GetChargeConfigList(partnerId int32, vipLv byte, isMonthCard bool,
filter func(*ChargeConfig) bool) (chargeConfigList []*ChargeConfig, exists bool, err error) {
// 获取排好序的充值配置列表
var tmpList []*ChargeConfig
tmpList, exists, err = this.getSortedChargeConfigList(partnerId, isMonthCard)
if err != nil || !exists {
return
}
// 根据vip和filter进行筛选
for _, item := range tmpList {
if filter != nil {
if vipLv >= item.VipLv && filter(item) {
chargeConfigList = append(chargeConfigList, item)
}
} else {
if vipLv >= item.VipLv {
chargeConfigList = append(chargeConfigList, item)
}
}
}
// 判断是否有符合条件的数据
if len(chargeConfigList) == 0 {
exists = false
return
}
return
}
// 获取有序的充值配置列表
// partnerId合作商Id
// isMonthCard是否月卡
// 返回值:
// 充值配置列表
// 是否存在
// 错误对象
func (this *ChargeUtil) getSortedChargeConfigList(partnerId int32, isMonthCard bool) (chargeConfigList []*ChargeConfig, exists bool, err error) {
// 判断合作商是否存在
//var partnerObj *Partner
//partnerObj, exists = managecenterMgr.GetPartner(partnerId)
//if !exists {
//return
//}
// 反序列化充值配置
var tmpChargeConfigList []*ChargeConfig
tmpChargeConfigList, exists = GetChargeConfigList(partnerId)
if !exists {
return
}
//if err = json.Unmarshal([]byte(partnerObj.ChargeConfig), &tmpChargeConfigList); err != nil {
//return
//}
// 根据isMonthCard进行筛选
for _, item := range tmpChargeConfigList {
if item.IsMonthCard == isMonthCard {
chargeConfigList = append(chargeConfigList, item)
}
}
// 判断是否有符合条件的数据
if len(chargeConfigList) == 0 {
exists = false
return
}
// 按默认规则进行排序
sort.Slice(chargeConfigList, func(i, j int) bool {
return chargeConfigList[i].SortByChargePointAsc(chargeConfigList[j])
})
return
}
// 获取充值配置项
// partnerId合作商Id
// vipLv玩家VIP等级
// isMonthCard是否月卡
// money充值金额
// 返回值:
// 充值配置项
// 是否存在
// 错误对象
func (this *ChargeUtil) GetChargeConfigItem(partnerId int32, vipLv byte, isMonthCard bool, money float64) (chargeConfigItem *ChargeConfig, exists bool, err error) {
// 获取排好序的充值配置列表
var tmpList []*ChargeConfig
tmpList, exists, err = this.getSortedChargeConfigList(partnerId, isMonthCard)
if err != nil || !exists {
return
}
// 获取满足充值金额和VIP条件的最后一条数据
for _, item := range tmpList {
if vipLv >= item.VipLv && money >= item.ChargePoint {
chargeConfigItem = item
}
}
// 如果没有符合条件的,则选择第一条配置
if chargeConfigItem == nil {
chargeConfigItem = tmpList[0]
}
return
}
// 计算充值获得的游戏点数
// partnerId合作商Id
// vipLv玩家VIP等级
// isMonthCard是否月卡
// money充值金额
// 返回值:
// 充值获得的游戏点数
// 是否存在
// 错误对象
func (this *ChargeUtil) CalcChargeGamePoint(partnerId int32, vipLv byte, isMonthCard bool, money float64, productId string) (chargeGamePoint int, exists bool, err error) {
// 获取排好序的充值配置列表
chargeGamePoint = 0
tmpList := make([]*ChargeConfig, 0, 8)
tmpList, exists, err = this.getSortedChargeConfigList(partnerId, isMonthCard)
if err != nil || !exists {
return
}
var chargeConfigItem *ChargeConfig
if money > 0 {
// 获取满足充值金额和VIP条件的最后一条数据
for _, item := range tmpList {
if vipLv >= item.VipLv && money >= item.ChargePoint {
chargeConfigItem = item
}
}
// 如果找不到对应的档位,则选择最低金额档位
if chargeConfigItem == nil {
chargeConfigItem = tmpList[0]
}
// 计算充值对应的ProductId以及获得的游戏货币
if money == chargeConfigItem.ChargePoint {
chargeGamePoint = chargeConfigItem.GamePoint
} else {
chargeGamePoint = int(math.Ceil(money * chargeConfigItem.Ratio))
}
return
} else {
for _, item := range tmpList {
if item.ProductId == productId && item.VipLv <= vipLv {
chargeConfigItem = item
break
} else {
continue
}
}
if chargeConfigItem != nil {
chargeGamePoint = chargeConfigItem.GamePoint
return
}
}
return
}
// 计算充值获得的所有游戏点数
// partnerId合作商Id
// vipLv玩家VIP等级
// isMonthCard是否月卡
// money充值金额
// activityMoney活动金额
// isFirstCharge是否首充
// 返回值:
// 充值获得的游戏点数
// 充值赠送获得的游戏内货币数量
// 充值活动获得的游戏内货币数量
// 总的元宝数量
// 是否存在
// 错误对象
func (this *ChargeUtil) CalcChargeAllGamePoint(partnerId int32, vipLv byte, isMonthCard bool,
money, activityMoney float64, productId string, isFirstCharge bool) (
chargeGamePoint int, giveGamePoint int, activityGamePoint int, totalGamePoint int,
exists bool, err error) {
// 获取排好序的充值配置列表
tmpList := make([]*ChargeConfig, 0, 8)
tmpList, exists, err = this.getSortedChargeConfigList(partnerId, isMonthCard)
if err != nil || !exists {
return
}
var chargeConfigItem *ChargeConfig
if money > 0 {
// 获取满足充值金额和VIP条件的最后一条数据
for _, item := range tmpList {
if vipLv >= item.VipLv && money >= item.ChargePoint {
chargeConfigItem = item
}
}
// 如果找不到对应的档位,则选择最低金额档位
if chargeConfigItem == nil {
chargeConfigItem = tmpList[0]
}
// 计算充值对应的ProductId以及获得的游戏货币
if money == chargeConfigItem.ChargePoint {
chargeGamePoint = chargeConfigItem.GamePoint
if isFirstCharge {
giveGamePoint = chargeConfigItem.FirstGiveGamePoint
} else {
giveGamePoint = chargeConfigItem.GiveGamePoint
}
} else {
chargeGamePoint = int(math.Ceil(money * chargeConfigItem.Ratio))
if isFirstCharge {
giveGamePoint = chargeConfigItem.FirstGiveGamePoint
} else {
giveGamePoint = int(math.Ceil(money * chargeConfigItem.Ratio * chargeConfigItem.GiveRatio))
}
}
activityGamePoint = int(math.Ceil(activityMoney * chargeConfigItem.Ratio))
} else {
// 获取满足充值金额和VIP条件的最后一条数据
for _, item := range tmpList {
if vipLv >= item.VipLv && item.ProductId == productId {
chargeConfigItem = item
}
}
if chargeConfigItem != nil {
chargeGamePoint = chargeConfigItem.GamePoint
if isFirstCharge {
giveGamePoint = chargeConfigItem.FirstGiveGamePoint
} else {
giveGamePoint = chargeConfigItem.GiveGamePoint
}
activityGamePoint = int(math.Ceil(activityMoney * chargeConfigItem.Ratio))
}
}
// 计算总和
totalGamePoint = chargeGamePoint + giveGamePoint + activityGamePoint
return
}
// 验证充值签名
// partnerId:合作商Id
// serverId:区服Id
// playerId:玩家Id
// orderId:订单Id
// productId:产品Id
// deviceIdentifier:设备唯一标识
// sign:签名
// 返回值:
// bool:签名验证情况
func (this *ChargeUtil) CheckChargeSign(partnerId int32, serverId int32, playerId string, orderId string, productId string, deviceIdentifier string, sign string) bool {
partnerObj, exist := GetPartnerItem(partnerId)
if exist == false {
return false
}
rawString := fmt.Sprintf("%v%v%v%v%v%v%v", partnerId, serverId, playerId, orderId, productId, deviceIdentifier, partnerObj.LoginKey)
if securityUtil.Md5String(rawString, false) != sign {
return false
}
return true
}
// ------------------类型定义和业务逻辑的分隔符-------------------------
var (
ChargeUtilObj = new(ChargeUtil)
)

View File

@@ -0,0 +1,410 @@
package gameServerMgr
import (
"Framework/managecenterModel"
"goutil/stringUtil"
"testing"
)
func TestChargeUtil_GetChargeConfigList(t *testing.T) {
/*
渠道充值配置:[{"ProductId":"fzxh_00060","ChargePoint":6,"GamePoint":60,"Ratio":10,"GiveGamePoint":6,"FirstGiveGamePoint":60,"GiveRatio":0.1,"VipLv":0,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00300","ChargePoint":30,"GamePoint":300,"Ratio":10,"GiveGamePoint":30,"FirstGiveGamePoint":300,"GiveRatio":0.1,"VipLv":0,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00500","ChargePoint":50,"GamePoint":550,"Ratio":11,"GiveGamePoint":110,"FirstGiveGamePoint":550,"GiveRatio":0.2,"VipLv":1,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00980","ChargePoint":98,"GamePoint":1470,"Ratio":15,"GiveGamePoint":441,"FirstGiveGamePoint":1470,"GiveRatio":0.3,"VipLv":5,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_restriction_18","ChargePoint":18,"GamePoint":90,"Ratio":5,"GiveGamePoint":0,"FirstGiveGamePoint":0,"GiveRatio":0,"VipLv":3,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":true},{"ProductId":"fzxh_restriction_50","ChargePoint":50,"GamePoint":300,"Ratio":6,"GiveGamePoint":0,"FirstGiveGamePoint":0,"GiveRatio":0,"VipLv":1,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":true}]
*/
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
var partnerId int32 = 1001
//激活器服务器获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
type VipRange struct {
MinVip byte
MaxVip byte
}
var isMonthCard = false
var maxVip byte = 20
vipMap := make(map[VipRange][]float64)
vipMap[VipRange{MinVip: 0, MaxVip: 0}] = []float64{6, 30}
vipMap[VipRange{MinVip: 1, MaxVip: 4}] = []float64{6, 30, 50}
vipMap[VipRange{MinVip: 5, MaxVip: maxVip}] = []float64{6, 30, 50, 98}
isExistsChargeConfig := func(configs []*managecenterModel.ChargeConfig, point float64) bool {
for _, item := range configs {
if item.ChargePoint == point {
return true
}
}
return false
}
//每个VIP等级都获取一遍充值档位
for vipRange, points := range vipMap {
for vip := vipRange.MinVip; vip <= vipRange.MaxVip; vip++ {
showChargeList, exists, error := ChargeUtilObj.GetChargeConfigList(partnerId, vip, isMonthCard, func(config *managecenterModel.ChargeConfig) bool {
if config.IfFirstShow == 1 && config.IfSecondShow == 1 {
return true
}
return false
})
if error != nil {
t.Fatalf("获取充值配置列表出现错误:%v", error)
}
if !exists {
t.Fatalf("PartnerId[%d],Vip[%d],IsMonthCard[%v],未找到充值配置列表", partnerId, vip, isMonthCard)
}
for _, point := range points {
if !isExistsChargeConfig(showChargeList, point) {
t.Fatalf("PartnerId[%d],Vip[%d],IsMonthCard[%v],金额[%f]未找到充值配置", partnerId, vip, isMonthCard, point)
}
}
}
}
}
// 测试生成订单号
func TestChargeUtil_GenerateOrderId(t *testing.T) {
var chargeServerUrl = "https://chargetest-xht.moqikaka.com/API/GenerateOrderId.ashx"
var serverGroupId int32 = 20001
var partnerId int32 = 1001
var productId = "fzxh_00060"
var userId = stringUtil.GetNewGUID()
var playerId = stringUtil.GetNewGUID()
var isMonthCard = true
//生成订单号
orderId, error := ChargeUtilObj.GenerateOrderId(
chargeServerUrl, productId, partnerId, serverGroupId, userId, playerId, "macmacmacmac", "idfa", "ip", "imei", "extra", isMonthCard)
if error != nil {
t.Fatalf("生成订单号[GenerateOrderId]出现错误:%v", error)
}
t.Logf("生成订单号[GenerateOrderId]:%s", orderId)
}
// 测试验证充值回调签名
func TestChargeUtil_CheckChargeSign(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
var partnerId int32 = 1001
var playerId = "04bade21-d7f7-4188-bc7c-8914c5330a23"
var orderId = "1001_20001_1590394423_2"
var productId = "fzxh_00060"
var deviceIdentifier = "deviceIdentifier"
//var loginKey = "a0482eaf-14e8-4a65-950e-864214f62da5"
//激活器服务器获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//充值服务器生成的签名
var targetSign = "02bd33319fc0f520a3e801f725973017"
//验证充值回调签名是否正确
if !ChargeUtilObj.CheckChargeSign(partnerId, serverGroupId, playerId, orderId, productId, deviceIdentifier, targetSign) {
t.Fatalf("验证充值回调签名失败!目标签名:%s", targetSign)
}
}
// 测试获取充值配置
func TestChargeUtil_GetChargeConfigItem(t *testing.T) {
/*
渠道充值配置:[{"ProductId":"fzxh_00060","ChargePoint":6,"GamePoint":60,"Ratio":10,"GiveGamePoint":6,"FirstGiveGamePoint":60,"GiveRatio":0.1,"VipLv":0,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00300","ChargePoint":30,"GamePoint":300,"Ratio":10,"GiveGamePoint":30,"FirstGiveGamePoint":300,"GiveRatio":0.1,"VipLv":0,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00500","ChargePoint":50,"GamePoint":550,"Ratio":11,"GiveGamePoint":110,"FirstGiveGamePoint":550,"GiveRatio":0.2,"VipLv":1,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00980","ChargePoint":98,"GamePoint":1470,"Ratio":15,"GiveGamePoint":441,"FirstGiveGamePoint":1470,"GiveRatio":0.3,"VipLv":5,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_restriction_18","ChargePoint":18,"GamePoint":90,"Ratio":5,"GiveGamePoint":0,"FirstGiveGamePoint":0,"GiveRatio":0,"VipLv":3,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":true},{"ProductId":"fzxh_restriction_50","ChargePoint":50,"GamePoint":300,"Ratio":6,"GiveGamePoint":0,"FirstGiveGamePoint":0,"GiveRatio":0,"VipLv":1,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":true}]
*/
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
var partnerId int32 = 1001
var maxVip byte = 20
//激活器服务器获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
type VipRange struct {
MinVip byte
MaxVip byte
}
//#region 测试获取普通充值配置
isMonthCard := false
vipMap := make(map[VipRange][]float64)
vipMap[VipRange{MinVip: 0, MaxVip: 0}] = []float64{6, 30}
vipMap[VipRange{MinVip: 1, MaxVip: 4}] = []float64{6, 30, 50}
vipMap[VipRange{MinVip: 5, MaxVip: maxVip}] = []float64{6, 30, 50, 98}
//每个VIP等级都获取一遍充值档位
for vipRange, points := range vipMap {
for vip := vipRange.MinVip; vip <= vipRange.MaxVip; vip++ {
for _, point := range points {
//获取充值配置
chargeConfig, exists, error := ChargeUtilObj.GetChargeConfigItem(partnerId, vip, isMonthCard, point)
if error != nil {
t.Fatalf("普通充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]获取充值档位出现错误:%v", vip, point, error)
}
if !exists {
t.Fatalf("普通充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]未找到充值档位配置", vip, point)
}
if chargeConfig == nil {
t.Fatalf("普通充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]找到的充值档位配置为nil", vip, point)
}
if chargeConfig.ChargePoint != point {
t.Fatalf("普通充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]找到的充值档位ChargePoint[%f]错误", vip, point, chargeConfig.ChargePoint)
}
}
}
}
//#endregion
//#region 测试获取月卡充值配置
isMonthCard = true
vipMap = make(map[VipRange][]float64)
vipMap[VipRange{MinVip: 1, MaxVip: 1}] = []float64{50}
vipMap[VipRange{MinVip: 3, MaxVip: maxVip}] = []float64{18, 50}
//每个VIP等级都获取一遍充值档位
for vipRange, points := range vipMap {
for vip := vipRange.MinVip; vip <= vipRange.MaxVip; vip++ {
for _, point := range points {
//获取充值配置
chargeConfig, exists, error := ChargeUtilObj.GetChargeConfigItem(partnerId, vip, isMonthCard, point)
if error != nil {
t.Fatalf("月卡充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]获取充值档位出现错误:%v", vip, point, error)
}
if !exists {
t.Fatalf("月卡充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]未找到充值档位配置", vip, point)
}
if chargeConfig == nil {
t.Fatalf("月卡充值Vip[%d]Point[%f]找到的充值档位配置为nil", vip, point)
}
if chargeConfig.ChargePoint != point {
t.Fatalf("月卡充值,获取充值配置[GetChargeConfigItem]Vip[%d]Point[%f]找到的充值档位ChargePoint[%f]错误", vip, point, chargeConfig.ChargePoint)
}
}
}
}
//#endregion
}
// 测试计算充值获得的游戏货币
func TestChargeUtil_CalcChargeAllGamePoint(t *testing.T) {
/*
渠道充值配置:[{"ProductId":"fzxh_00060","ChargePoint":6,"GamePoint":60,"Ratio":10,"GiveGamePoint":6,"FirstGiveGamePoint":60,"GiveRatio":0.1,"VipLv":0,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00300","ChargePoint":30,"GamePoint":300,"Ratio":10,"GiveGamePoint":30,"FirstGiveGamePoint":300,"GiveRatio":0.1,"VipLv":0,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00500","ChargePoint":50,"GamePoint":550,"Ratio":11,"GiveGamePoint":110,"FirstGiveGamePoint":550,"GiveRatio":0.2,"VipLv":1,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_00980","ChargePoint":98,"GamePoint":1470,"Ratio":15,"GiveGamePoint":441,"FirstGiveGamePoint":1470,"GiveRatio":0.3,"VipLv":5,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":false},{"ProductId":"fzxh_restriction_18","ChargePoint":18,"GamePoint":90,"Ratio":5,"GiveGamePoint":0,"FirstGiveGamePoint":0,"GiveRatio":0,"VipLv":3,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":true},{"ProductId":"fzxh_restriction_50","ChargePoint":50,"GamePoint":300,"Ratio":6,"GiveGamePoint":0,"FirstGiveGamePoint":0,"GiveRatio":0,"VipLv":1,"IfFirstShow":1,"IfSecondShow":1,"IsMonthCard":true}]
*/
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
var partnerId int32 = 1001
//激活器服务器获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//输入订单信息
type OrderItem struct {
Vip byte
Money float64
ActivityMoney float64
ProductId string
IsFirstCharge bool
IsMonthCard bool
}
//计算结果
type GamePointResult struct {
ChargeGamePoint int
GiveGamePoint int
ActivityGamePoint int
TotalGamePoint int
}
testOrderMap := make(map[OrderItem]GamePointResult)
//#region 普通充值
testOrderMap[OrderItem{
Vip: 0,
Money: 6,
ActivityMoney: 1,
ProductId: "fzxh_00060",
IsFirstCharge: true,
IsMonthCard: false,
}] = GamePointResult{
ChargeGamePoint: 60, //GamePoint
GiveGamePoint: 60, //FirstGiveGamePoint
ActivityGamePoint: 10, //ActivityMoney*Ratio = 1*10
TotalGamePoint: 130,
} //首充6元,活动金额1元
testOrderMap[OrderItem{
Vip: 0,
Money: 6,
ActivityMoney: 1,
ProductId: "fzxh_00060",
IsFirstCharge: false,
IsMonthCard: false,
}] = GamePointResult{
ChargeGamePoint: 60, //GamePoint
GiveGamePoint: 6, //GiveGamePoint 6
ActivityGamePoint: 10, //ActivityMoney*Ratio = 1*10
TotalGamePoint: 76,
} //非首充6元,活动金额1元
testOrderMap[OrderItem{
Vip: 0,
Money: 0,
ActivityMoney: 1,
ProductId: "fzxh_00060",
IsFirstCharge: false,
IsMonthCard: false,
}] = GamePointResult{
ChargeGamePoint: 60, //Money*Ratio = 6*10
GiveGamePoint: 6, //GiveGamePoint 6
ActivityGamePoint: 10, //ActivityMoney*Ratio = 1*10
TotalGamePoint: 76,
} //无金额匹配6元,活动金额1元
testOrderMap[OrderItem{
Vip: 0,
Money: 10,
ActivityMoney: 3,
ProductId: "",
IsFirstCharge: false,
IsMonthCard: false,
}] = GamePointResult{
ChargeGamePoint: 100, //Money*Ratio = 10*10
GiveGamePoint: 10, //Money*Ratio*GiveRatio 10 * 10 * 0.1
ActivityGamePoint: 30, //ActivityMoney*Ratio = 3*10
TotalGamePoint: 140,
} //无对应档位配置,模糊匹配10元,活动金额1元,实际匹配到6元档位
testOrderMap[OrderItem{
Vip: 3,
Money: 98,
ActivityMoney: 3,
ProductId: "",
IsFirstCharge: false,
IsMonthCard: false,
}] = GamePointResult{
ChargeGamePoint: 1078, //Money*Ratio = 98*11
GiveGamePoint: 216, //Money*Ratio*GiveRatio 98 * 11 * 0.2 = 215.6
ActivityGamePoint: 33, //ActivityMoney*Ratio = 3*11
TotalGamePoint: 1327,
} //Vip限制,匹配98元,实际匹配到50档位,活动金额3元
//#endregion
for order, result := range testOrderMap {
//计算充值获得的游戏币
chargeGamePoint, giveGamePoint, activityGamePoint, totalGamePoint, exists, err := ChargeUtilObj.CalcChargeAllGamePoint(
partnerId, order.Vip, order.IsMonthCard, order.Money, order.ActivityMoney, order.ProductId, order.IsFirstCharge)
if err != nil {
t.Fatalf("普通充值,计算充值获得游戏币[CalcChargeAllGamePoint],金额[%f] error:%v", order.Money, err)
}
if !exists {
t.Fatalf("普通充值,计算充值获得游戏币[CalcChargeAllGamePoint],金额[%f]未找到充值配置:%v", order.Money, exists)
}
if chargeGamePoint != result.ChargeGamePoint {
t.Fatalf("普通充值,计算充值获得游戏币[CalcChargeAllGamePoint],ChargeGamePoint计算结果不正确,金额[%f],期望[%d],结果[%d]", order.Money, result.ChargeGamePoint, chargeGamePoint)
}
if giveGamePoint != result.GiveGamePoint {
t.Fatalf("普通充值,计算充值获得游戏币[CalcChargeAllGamePoint],GiveGamePoint计算结果不正确,金额[%f],期望[%d],结果[%d]", order.Money, result.GiveGamePoint, giveGamePoint)
}
if activityGamePoint != result.ActivityGamePoint {
t.Fatalf("普通充值,计算充值获得游戏币[CalcChargeAllGamePoint],ActivityGamePoint计算结果不正确,金额[%f],期望[%d],结果[%d]", order.Money, result.ActivityGamePoint, activityGamePoint)
}
if totalGamePoint != result.TotalGamePoint {
t.Fatalf("普通充值,计算充值获得游戏币[CalcChargeAllGamePoint],TotalGamePoint计算结果不正确,金额[%f],期望[%d],结果[%d]", order.Money, result.TotalGamePoint, totalGamePoint)
}
}
//#region 月卡充值
testOrderMap[OrderItem{
Vip: 3,
Money: 18,
ProductId: "fzxh_restriction_18",
IsMonthCard: true,
}] = GamePointResult{
ChargeGamePoint: 90, //GamePoint
} //18元月卡
testOrderMap[OrderItem{
Vip: 0,
Money: 50,
ProductId: "fzxh_restriction_50",
IsMonthCard: true,
}] = GamePointResult{
ChargeGamePoint: 250, //Money * Ratio = 50 * 5
} //VIP限制匹配50元月卡实际匹配到18元月卡
testOrderMap[OrderItem{
Vip: 1,
Money: 0,
ProductId: "fzxh_restriction_50",
IsMonthCard: true,
}] = GamePointResult{
ChargeGamePoint: 300, //GamePoint
} //无金额匹配50元月卡
testOrderMap[OrderItem{
Vip: 0,
Money: 0,
ProductId: "fzxh_restriction_50",
IsMonthCard: true,
}] = GamePointResult{
ChargeGamePoint: 0, //匹配不成功
} //VIP限制无金额匹配50元月卡
for order, result := range testOrderMap {
//计算充值获得的游戏币
chargeGamePoint, exists, err := ChargeUtilObj.CalcChargeGamePoint(
partnerId, order.Vip, order.IsMonthCard, order.Money, order.ProductId)
if err != nil {
t.Fatalf("月卡充值,计算充值获得游戏币[CalcChargeGamePoint],金额[%f] error:%v", order.Money, err)
}
if !exists {
t.Fatalf("月卡充值,计算充值获得游戏币[CalcChargeGamePoint],金额[%f]未找到充值配置:%v", order.Money, exists)
}
if chargeGamePoint != result.ChargeGamePoint {
t.Fatalf("月卡充值,计算充值获得游戏币[CalcChargeGamePoint],ChargeGamePoint计算结果不正确,金额[%f],期望[%d],结果[%d]", order.Money, result.ChargeGamePoint, chargeGamePoint)
}
}
//#endregion
}

View File

@@ -0,0 +1,15 @@
package gameServerMgr
import (
"fmt"
"testing"
)
func TestActive(t *testing.T) {
err := ActiveServer("https://managecenterapitest-xxx.79yougame.com/API/ServerActivate.ashx", 20002)
if err != nil {
fmt.Println("xxx")
}
CheckNewResourceVersion(1001, 20002, 100, "1584085505_769926880ac0ae89a31dcdfef5b94b1e")
}

View File

@@ -0,0 +1,131 @@
package gameServerMgr
import (
"encoding/json"
"fmt"
"strings"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/securityUtil"
"goutil/webUtil"
)
// 登陆助手类
type LoginUtil struct{}
// 验证登陆信息
// partnerId:合作商Id
// userId:合作商用户Id
// loginInfo:登陆信息
// isIntranet:是否是内网true内网false外网
// 返回值:
// 成功与否
// 错误对象
func (this *LoginUtil) CheckLoginInfo(partnerId int32, userId, loginInfo string, isIntranet bool) (success bool, err error) {
// 验证用户合法性
loginItemList := strings.Split(loginInfo, "_")
if len(loginItemList) != 2 {
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s", partnerId, userId, loginInfo)
return
}
//将requestUrl地址进行拆分
requestDomainList := strings.Split(loginItemList[1], ";")
//请求的主域名
requestDomain := ""
if isIntranet || len(requestDomainList) == 1 {
requestDomain = requestDomainList[0]
} else {
requestDomain = requestDomainList[1]
}
//构造请求url
requestUrl := fmt.Sprintf("http://%s/API/CheckDynamicLoginKey.ashx", requestDomain)
// 定义请求参数
postDict := make(map[string]string)
postDict["UserId"] = userId
postDict["LoginKey"] = loginItemList[0]
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err1 := webUtil.PostMapData(requestUrl, postDict, header, transport)
if err1 != nil {
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, err:%s", partnerId, userId, loginInfo, err1)
return
}
if statusCode != 200 {
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, statusCode:%d", partnerId, userId, loginInfo, statusCode)
return
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, err:%s", partnerId, userId, loginInfo, err)
return
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
logUtil.ErrorLog(fmt.Sprintf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, Code:%d, Message:%s", partnerId, userId, loginInfo, returnObj.Code, returnObj.Message))
return
}
success = true
return
}
// 本地验证登陆信息
// partnerId:合作商Id
// userId:合作商用户Id
// loginInfo:登陆信息
// 返回值:
// 成功与否
// 错误对象
func CheckDynamicTokenLocal(partnerId int32, userId, loginInfo string) (success bool, err error) {
//1001直接返回true
if partnerId == 1001 {
return true, nil
}
//非1001渠道验证
if len(loginInfo) == 0 {
success = false
err = fmt.Errorf("Err:%s", "LoginInfo is null!")
return
}
// 验证用户合法性
loginItemList := strings.Split(loginInfo, "_")
if len(loginItemList) != 2 {
success = false
err = fmt.Errorf("CheckLoginInfo Failed. userId:%s, loginInfo:%s", userId, loginInfo)
return
}
//生成key
localSign := securityUtil.Md5String(userId+GetSysConfig().DynamicLoginKey+loginItemList[1], true)
//判断签名是佛正确
if localSign != loginItemList[0] {
success = false
err = fmt.Errorf("CheckLoginInfo Failed. Sign Check Failed! userId:%s,LocalSign:%s,loginInfo:%s", userId, localSign, loginInfo)
return
}
success = true
return
}
// ------------------类型定义和业务逻辑的分隔符-------------------------
var (
LoginUtilObj = new(LoginUtil)
)

View File

@@ -0,0 +1,155 @@
package gameServerMgr
import (
"crypto/tls"
"encoding/json"
"fmt"
"strconv"
. "Framework/managecenterModel"
"goutil/webUtil"
"goutil/zlibUtil"
)
// 区服激活地址后缀
const ActivateServer_URL_SUFFIX string = "/API/ServerActivate.ashx"
var (
mManageCenterServerAPIUrl string
mIsInit bool = true
)
// 解析从ManagecenterServer中获取的服务器相关数据
func ParseInfoFromManageCenterServer(serverGroupId int32, data string) {
var deserializedData map[string]interface{}
err := json.Unmarshal([]byte(data), &deserializedData)
if err != nil {
return
}
//解析服务器组
var serverGroup *ServerGroup
err = json.Unmarshal([]byte(deserializedData["ServerGroupInfo"].(string)), &serverGroup)
if err != nil {
return
}
//解析合作商
var partnerList []*Partner
err = json.Unmarshal([]byte(deserializedData["PartnerList"].(string)), &partnerList)
if err != nil {
return
}
//解析服务器列表
var serverList []*Server
err = json.Unmarshal([]byte(deserializedData["ServerList"].(string)), &serverList)
if err != nil {
return
}
//解析资源包
var resourceList []*ResourceVersion
err = json.Unmarshal([]byte(deserializedData["ResourceVersionList"].(string)), &resourceList)
if err != nil {
return
}
//解析大区
var areaList []*Area
err = json.Unmarshal([]byte(deserializedData["AreaList"].(string)), &areaList)
if err != nil {
return
}
//判断是否需要更新数据如果ServerGroupId不匹配则不解析数据
if serverGroupId != serverGroup.Id {
return
}
//缓存服务器组
ParseServerGroupInfo(serverGroup)
//缓存合作商
ParsePartnerInfo(partnerList)
//缓存合作商对应的充值配置
ParseChargeConfigInfo(partnerList)
//缓存服务器
ParseServerInfo(serverList)
//缓存资源包
ParseResourceVersionInfo(resourceList)
//缓存大区
ParseAreaInfo(areaList)
}
// 激活服务器
func ActiveServer(manageCenterServerAPIUrl string, serverGroupId int32) error {
if len(manageCenterServerAPIUrl) == 0 {
return fmt.Errorf("ManageCenterServerAPI地址不能为空")
}
mManageCenterServerAPIUrl = manageCenterServerAPIUrl
//定义参数
requestParamMap := make(map[string]string, 0)
requestParamMap["ServerGroupID"] = strconv.Itoa(int(serverGroupId))
//构造请求url
url := fmt.Sprintf("%s/%s", mManageCenterServerAPIUrl, ActivateServer_URL_SUFFIX)
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true, //关闭证书校验
}
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, nil)
if err != nil {
return err
}
if statusCode != 200 {
return fmt.Errorf("StatusCode:%d", statusCode)
}
//解压缩
retBytes, err1 := zlibUtil.Decompress(returnBytes)
if err1 != nil {
return err1
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(retBytes, &returnObj); err != nil {
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
return fmt.Errorf("code:%d,Message:%s", returnObj.Code, returnObj.Message)
}
//解析得到的数据
ParseInfoFromManageCenterServer(serverGroupId, returnObj.Data.(string))
//获取白名单
GetWhiteListFromManageCenterServer()
//如果是初始化,则开启白名单刷新线程。避免游戏客户端刷新数据的时候重复开启线程
if mIsInit {
//启动白名单数据刷新线程
StartRefreshWhiteListTread()
//启动刷新MC系统配置
StartRefreshSysConfigTread()
//初始化修改为fasle
mIsInit = false
}
return nil
}

View File

@@ -0,0 +1,374 @@
package gameServerMgr
import (
"testing"
"time"
"goutil/timeUtil"
"goutil/typeUtil"
)
func TestActiveServer(t *testing.T) {
var managecenterUrl = "https://managecenterapitest-ds3.7qule.com/"
var serverGroupId int32 = 1000
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
}
func TestGetServerGroup(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region测试获取服务器组
serverGroup := GetServerGroup()
if serverGroup == nil {
t.Fatalf("获取服务器组[GetServerGroup]失败serverGroup = nil")
}
if serverGroup.Id != serverGroupId {
t.Fatalf("获取服务器组[GetServerGroup]失败,期望[%d],结果[%d]", serverGroupId, serverGroup.Id)
}
//#endregion
}
func TestGetDbConfig(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region测试获取服务器组
serverGroup := GetServerGroup()
if serverGroup == nil {
t.Fatalf("获取服务器组[GetServerGroup]失败serverGroup = nil")
}
if serverGroup.Id != serverGroupId {
t.Fatalf("获取服务器组[GetServerGroup]失败,期望[%d],结果[%d]", serverGroupId, serverGroup.Id)
}
//#endregion
//#region 测试获取数据库配置
/*
{
"GameDB": "DataSource=10.1.0.4;port=3306;UserId=admin;Password=MOQIkaka$#@!1234;Database=h5xh_player_master;Allow Zero Datetime=true;charset=utf8;pooling=true;MinimumPoolSize=20;maximumpoolsize=200;command timeout=60;",
"LogDB": "DataSource=10.1.0.4;port=3306;UserId=admin;Password=MOQIkaka$#@!1234;Database=h5xh_log_master;Allow Zero Datetime=true;charset=utf8;pooling=true;MinimumPoolSize=20;maximumpoolsize=200;command timeout=60;",
"GameModelDB": "DataSource=10.1.0.4;port=3306;UserId=admin;Password=MOQIkaka$#@!1234;Database=h5xh_model_master;Allow Zero Datetime=true;charset=utf8;pooling=true;MinimumPoolSize=10;maximumpoolsize=10;command timeout=60;"
}
*/
gameDb := "DataSource=10.1.0.4;port=3306;UserId=admin;Password=MOQIkaka$#@!1234;Database=h5xh_player_master;Allow Zero Datetime=true;charset=utf8;pooling=true;MinimumPoolSize=20;maximumpoolsize=200;command timeout=60;"
logDb := "DataSource=10.1.0.4;port=3306;UserId=admin;Password=MOQIkaka$#@!1234;Database=h5xh_log_master;Allow Zero Datetime=true;charset=utf8;pooling=true;MinimumPoolSize=20;maximumpoolsize=200;command timeout=60;"
modelDb := "DataSource=10.1.0.4;port=3306;UserId=admin;Password=MOQIkaka$#@!1234;Database=h5xh_model_master;Allow Zero Datetime=true;charset=utf8;pooling=true;MinimumPoolSize=10;maximumpoolsize=10;command timeout=60;"
dbConfig, err := serverGroup.GetDBConfig()
if err != nil {
t.Fatalf("获取服务器数据库配置[GetDBConfig]失败:%v", error)
}
if dbConfig.GameDB != gameDb {
t.Fatalf("获取服务器数据库配置[GetDBConfig]失败,GameDB,期望[%s],结果[%s]", gameDb, dbConfig.GameDB)
}
if dbConfig.LogDB != logDb {
t.Fatalf("获取服务器数据库配置[GetDBConfig]失败,LogDB,期望[%s],结果[%s]", logDb, dbConfig.LogDB)
}
if dbConfig.GameModelDB != modelDb {
t.Fatalf("获取服务器数据库配置[GetDBConfig]失败,GameModelDB,期望[%s],结果[%s]", modelDb, dbConfig.GameModelDB)
}
//#endregion
}
func TestGetServerItem(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
var serverId = serverGroupId
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region测试获取服务器
serverItem, exists := GetServerItem(partnerId, serverId)
if !exists || serverItem == nil {
t.Fatalf("获取服务器[GetServerItem][%d]失败,PartnerId[%d],服务器不存在!", partnerId, serverId)
}
if serverItem.Id != serverId {
t.Fatalf("获取服务器[GetServerItem]失败,期望[%d],结果[%d]", serverId, serverItem.Id)
}
//#endregion
}
func TestGetServerName(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
var serverId = serverGroupId
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//测试获取服务器名称
var expectServerName = "开发测试服"
serverName := GetServerName(partnerId, serverId)
if serverName != expectServerName {
t.Fatalf("获取服务器名称[GetServerName]失败,期望[%s],结果[%s]", expectServerName, serverName)
}
}
func TestIfServerExists(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
var serverId = serverGroupId
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//测试服务器是否存在
if !IfServerExists(partnerId, serverId) {
t.Fatalf("判断服务器是否存在[IfServerExists]失败,PartnerId[%d],服务器[%d]不存在", partnerId, serverId)
}
}
func TestGetPartnerServerPairString(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//获取合作商、服务器的组合字符串
expectStr := "1001_20001|6666001_20001|"
partnerServerPairStr := GetPartnerServerPairString()
if expectStr != partnerServerPairStr {
t.Fatalf("获取合作商、服务器的组合字符串[GetPartnerServerPairString]失败,期望[%s],结果[%s]", expectStr, partnerServerPairStr)
}
}
func TestGetLoginKey(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region测试获取登录密钥
var expectStr = "a0482eaf-14e8-4a65-950e-864214f62da5"
loginKey, exists := GetLoginKey(partnerId)
if !exists {
t.Fatalf("获取登录密钥[GetLoginKey]失败,未找到渠道[%d]配置", partnerId)
}
if expectStr != loginKey {
t.Fatalf("获取登录密钥[GetLoginKey]失败,期望[%s],结果[%s]", expectStr, loginKey)
}
//#endregion
}
func TestGetServerOpenDate(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//测试获取开服日期 2020/4/21 20:19:56
expectOpenDate := time.Date(2020, 4, 21, 20, 19, 56, 0, time.Local)
openTimeTick := GetServerOpenDate()
openDate, _ := typeUtil.DateTime(openTimeTick)
if openDate != expectOpenDate {
t.Fatalf("获取服务器开服日期[GetServerOpenDate]失败,期望[%s],结果[%s]", timeUtil.ToDateTimeString(expectOpenDate), timeUtil.ToDateTimeString(openDate))
}
}
func TestServerOpenDays(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//测试获取服务器开服天数
now := time.Now()
nowDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
expectOpenDate := time.Date(2020, 4, 21, 0, 0, 0, 0, time.Local)
expectOpenDays := int32(nowDate.Sub(expectOpenDate).Hours()/24) + 1
openDays := ServerOpenDays()
if expectOpenDays != openDays {
t.Fatalf("获取服务器开服天数[ServerOpenDays]失败,期望[%d],结果[%d]", expectOpenDays, openDays)
}
}
func TestCheckMaintainStatus(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//测试服务器维护检查
expectMsg := "维护中"
maintainMessage, isMaintaining := CheckMaintainStatus()
if isMaintaining && expectMsg != maintainMessage {
t.Fatalf("服务器维护检查[CheckMaintainStatus]失败,期望维护消息[%s],结果维护消息[%s]", expectMsg, maintainMessage)
}
}
func TestCheckNewGameVersion(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
var serverId = serverGroupId
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region 测试检查是否有新版本
var gameVersionId int32 = 55 //老版本
var expectGameVersionUrl = "testUrl" //已配置的游戏版本地址IOS用
gameVersionUrl, exist := CheckNewGameVersion(partnerId, serverId, gameVersionId)
if !exist {
t.Fatalf("检查是否有新版本[CheckNewGameVersion]失败,未找到渠道[%d]配置或检查版本[%d]失败", partnerId, gameVersionId)
} else if gameVersionUrl != expectGameVersionUrl {
t.Fatalf("检查是否有新版本[CheckNewGameVersion]失败,期望版本地址[%s],结果版本地址[%s]", expectGameVersionUrl, gameVersionUrl)
}
gameVersionId = 100 //当前版本
_, exist = CheckNewGameVersion(partnerId, serverId, gameVersionId)
if exist {
t.Fatalf("检查是否有新版本[CheckNewGameVersion]失败,渠道[%d],版本[%d]不应有更新", partnerId, gameVersionId)
}
//#endregion
}
func TestCheckNewResourceVersion(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
var serverId = serverGroupId
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region 测试检查新资源版本
partnerId = 6666001
var gameVersionId int32 = 100
resourceVersionName := "1587372891_9eab40313feec913cace0adf0fe05341" //非最新资源版本号
_, exist := CheckNewResourceVersion(partnerId, serverId, gameVersionId, resourceVersionName)
if !exist {
t.Fatalf("检查新资源版本[CheckNewResourceVersion]失败,渠道[%d],服务器[%d],游戏版本Id[%d],资源版本号[%s],应有更新!", partnerId, serverId, gameVersionId, resourceVersionName)
}
gameVersionId = 100
resourceVersionName = "1587374043_cee48a8611276d3e3450782a1585c1a3" //最新资源版本号
_, exist = CheckNewResourceVersion(partnerId, serverId, gameVersionId, resourceVersionName)
if exist {
t.Fatalf("检查新资源版本[CheckNewResourceVersion]失败,渠道[%d],服务器[%d],游戏版本Id[%d],资源版本号[%s],不应有更新!", partnerId, serverId, gameVersionId, resourceVersionName)
}
gameVersionId = 123456 //不存在的游戏版本
resourceVersionName = "1587374043_cee48a8611276d3e3450782a1585c1a3"
_, exist = CheckNewResourceVersion(partnerId, serverId, gameVersionId, resourceVersionName)
if exist {
t.Fatalf("检查新资源版本[CheckNewResourceVersion]失败,渠道[%d],服务器[%d],游戏版本Id[%d],资源版本号[%s],不应有更新!", partnerId, serverId, gameVersionId, resourceVersionName)
}
//#endregion
}
func TestIsInWhiteList(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//测试白名单
partnerId = 1001
var userId = "446bfd6ccccd4229aa295686f9e5855b" //已配置的白名单用户Id
if !IsInWhiteList(partnerId, userId) {
t.Fatalf("检查是否用户在白名单中[IsInWhiteList]失败,渠道[%d],UserId[%s]", partnerId, userId)
}
}
// 测试获取服务器组
func TestGetOtherConfigInfo(t *testing.T) {
var managecenterUrl = "http://managecenterapitest-xxx.79yougame.com/"
var partnerId int32 = 1001
var serverGroupId int32 = 20001
//激活器服务器,获取mc配置
error := ActiveServer(managecenterUrl, serverGroupId)
if error != nil {
t.Fatalf("激活服务器出现错误:%v", error)
}
//#region测试获取其他配置
var configKey = "AppKey"
var expectConfigValue = "a0482eaf-14e8-4a65-950e-864214f62da5" //已配置的配置值
configValue, exist, error := GetOtherConfigInfo(partnerId, configKey)
if error != nil {
t.Fatalf("获取渠道其他配置[GetOtherConfigInfo]失败,渠道[%d],ConfigKey[%s]", partnerId, configKey)
}
if !exist {
t.Fatalf("获取渠道其他配置[GetOtherConfigInfo]失败,渠道[%d],ConfigKey[%s],渠道或配置不存在!", partnerId, configKey)
}
if configValue != expectConfigValue {
t.Fatalf("获取渠道其他配置[GetOtherConfigInfo]失败,期望[%s],结果[%s]", expectConfigValue, configValue)
}
//#endregion
}

View File

@@ -0,0 +1,60 @@
package gameServerMgr
import (
. "Framework/managecenterModel"
)
var (
mPartnerMap = make(map[int32]*Partner, 0)
)
//解析合作商信息
func ParsePartnerInfo(partnerList []*Partner) {
tmpPartnerMap := make(map[int32]*Partner, 0)
//循环解析所有合作商信息
for _, partner := range partnerList {
tmpPartnerMap[partner.Id] = partner
}
mPartnerMap = tmpPartnerMap
}
//根据渠道id获取渠道对象
func GetPartnerItem(partnerId int32) (partner *Partner, exist bool) {
//判断渠道是否存在
partner, exist = mPartnerMap[partnerId]
return
}
//获取渠道的登录加密key
func GetLoginKey(partnerId int32) (loginKey string, exist bool) {
partnerObj, exist := mPartnerMap[partnerId]
if !exist {
return
}
loginKey = partnerObj.LoginKey
return
}
//根据渠道和key获取其他配置
func GetOtherConfigInfo(partnerId int32, configKey string) (configValue string, exist bool, err error) {
partnerObj, exist := GetPartnerItem(partnerId)
if !exist {
return
}
var otherConfigMap map[string]string
otherConfigMap, err = partnerObj.ResolveOtherConfig()
if err != nil {
return
}
configValue, exist = otherConfigMap[configKey]
return
}

View File

@@ -0,0 +1,57 @@
package gameServerMgr
import (
. "Framework/managecenterModel"
)
var (
mResourceVersionLit = make([]*ResourceVersion, 0)
)
// 解析资源版本信息
func ParseResourceVersionInfo(resourceVersionList []*ResourceVersion) {
mResourceVersionLit = resourceVersionList
}
//返回所有的资源包列表
func GetResourceVersionList() (resourceVersionList []*ResourceVersion) {
resourceVersionList = mResourceVersionLit
return
}
//检测资源
func CheckNewResourceVersion(partnerId, serverId, gameVersionId int32, resourceVersionName string) (availableResourceVersionMap map[string]interface{}, exist bool) {
_, exist = GetServerItem(partnerId, serverId)
if !exist {
return
}
//获取服务所在大区Id
areaId := GetAreaIdByGroupId(serverId)
//获取大区的资源列表
var tempResourceVersionList []*ResourceVersion
for _, resourceVerion := range mResourceVersionLit {
if resourceVerion.AreaID == areaId {
tempResourceVersionList = append(tempResourceVersionList, resourceVerion)
}
}
//获取服务器
serverGroup := GetServerGroup()
//获取资源版本列表
availableResourceVersionMap = GetAvailableResource(tempResourceVersionList, partnerId, gameVersionId, resourceVersionName, OfficialOrTest(serverGroup.OfficialOrTest))
//检测是否有新资源
if len(availableResourceVersionMap) != 0 && availableResourceVersionMap["IsNewResource"] == true {
exist = true
return
}
exist = false
return
}

View File

@@ -0,0 +1,166 @@
package gameServerMgr
import (
"fmt"
. "Framework/managecenterModel"
)
var (
mServerMap = make(map[int32][]*Server, 0)
)
//合作商、服务器组合列表
type PartnerServer struct {
//合作商Id
mPartnerId int32
//服务器Id
mServerId int32
}
//解析服务器信息
func ParseServerInfo(serverList []*Server) {
tempServerMap := make(map[int32][]*Server, 0)
for _, server := range serverList {
_, exist := tempServerMap[server.PartnerId]
if !exist {
tempServerMap[server.PartnerId] = make([]*Server, 0)
}
tempServerMap[server.PartnerId] = append(tempServerMap[server.PartnerId], server)
}
mServerMap = tempServerMap
}
//获取服务器对象
func GetServerItem(partnerId, serverId int32) (server *Server, exist bool) {
serverList, existServer := mServerMap[partnerId]
if !existServer {
return
}
for _, serverItem := range serverList {
if serverItem.Id == serverId {
server = serverItem
exist = true
break
}
}
return
}
//判断服务器是否存在
func IfServerExists(partnerId, serverId int32) (exists bool) {
_, exists = GetPartnerItem(partnerId)
if !exists {
return
}
_, exists = GetServerItem(partnerId, serverId)
if !exists {
return
}
return
}
//获取服务器名称
func GetServerName(partnerId, serverId int32) (serverName string) {
_, existPartner := GetPartnerItem(partnerId)
if !existPartner {
return
}
server, existServer := GetServerItem(partnerId, serverId)
if !existServer {
return
}
serverName = server.Name
return
}
//检测是否有游戏版本更新
func CheckNewGameVersion(partnerId, serverId, gameVersionId int32) (gameVersionUrl string, exist bool) {
server, existServer := GetServerItem(partnerId, serverId)
if !existServer {
exist = existServer
return
}
if gameVersionId < server.MinGameVersionId {
partner, existPartner := GetPartnerItem(partnerId)
if !existPartner {
exist = existPartner
return
}
exist = true
gameVersionUrl = partner.GameVersionUrl
}
return
}
//获取合作商、服务器的组合字符串
//字符串格式PartnerId_ServerId|PartnerId_ServerId|
func GetPartnerServerPairString() (partnerServerPair string) {
for _, ps := range GetPartnerServerPairList() {
partnerServerPair += fmt.Sprintf("%d_%d|", ps.mPartnerId, ps.mServerId)
}
return
}
//获取合作商、服务器的组合列表
func GetPartnerServerPairList() (partnerServerList []*PartnerServer) {
for key := range mServerMap {
for _, server := range mServerMap[key] {
if server.GroupId == GetServerGroup().Id {
var ps *PartnerServer = &PartnerServer{server.PartnerId, server.Id}
partnerServerList = append(partnerServerList, ps)
}
}
}
return
}
// // 获取合作商、服务器的组合字符串
// // serverGroupId服务器组Id
// // 返回值
// // 合作商、服务器的组合字符串
// func (this *ManageCenterUtil) GetPartnerServerPairString(serverGroupId int32) string {
// var buf bytes.Buffer
// serverList := managecenterMgr.GetServerList(serverGroupId)
// for _, item := range serverList {
// buf.WriteString(fmt.Sprintf("%d_%d|", item.PartnerId, item.Id))
// }
// return buf.String()
// }
// // 是否是有效的合作商、服务器组合
// // partnerId合作商Id
// // serverId服务器Id
// // parnterServerPairString合作商、服务器的组合字符串,格式为:PartnerId_ServerId|PartnerId_ServerId|
// // 返回值
// // 是否有效
// func (this *ManageCenterUtil) IfValidPartnerServerPair(partnerId, serverId int32, parnterServerPairString string) bool {
// if parnterServerPairString == "" {
// return false
// }
// partnerServerPairStringList := strings.Split(parnterServerPairString, "|")
// if len(partnerServerPairStringList) == 0 {
// return false
// }
// // 获得玩家的合作商、服务器组合字符串
// partnerServerPair := fmt.Sprintf("%d_%d", partnerId, serverId)
// // 遍历寻找
// for _, item := range partnerServerPairStringList {
// if item == partnerServerPair {
// return true
// }
// }
// return false
// }

View File

@@ -0,0 +1,79 @@
package gameServerMgr
import (
"time"
. "Framework/managecenterModel"
"goutil/timeUtil"
"goutil/typeUtil"
)
var (
mServerGroupObj *ServerGroup
)
//解析服务器组信息
func ParseServerGroupInfo(serverGroupObj *ServerGroup) {
mServerGroupObj = serverGroupObj
}
//获取服务器组对象
func GetServerGroup() (serverGroupObj *ServerGroup) {
serverGroupObj = mServerGroupObj
return
}
//检查服务器是否在维护
func CheckMaintainStatus() (maintainMessage string, isMaintaining bool) {
serverGroupObj := GetServerGroup()
nowTick := time.Now().Unix()
if serverGroupObj.GroupState == int32(Con_GroupState_Maintain) || (serverGroupObj.MaintainBeginTimeTick <= nowTick && nowTick <= serverGroupObj.MaintainBeginTimeTick+int64(60*serverGroupObj.MaintainMinutes)) {
maintainMessage = serverGroupObj.MaintainMessage
isMaintaining = true
return
}
return
}
//获取服务器维护开始时间时间戳
func GetMaintainBeginTime() (maintainBeginTimeTick int64) {
serverGroupObj := GetServerGroup()
maintainBeginTimeTick = serverGroupObj.MaintainBeginTimeTick
return
}
//获取服务器维护持续时间 单位分钟
func GetMaintainMinutes() (maintainMinutes int32) {
serverGroupObj := GetServerGroup()
maintainMinutes = serverGroupObj.MaintainMinutes
return
}
//获取服务器开服日期 时间戳
func GetServerOpenDate() (openTimeTick int64) {
serverGroupObj := GetServerGroup()
openTimeTick = serverGroupObj.OpenTimeTick
return
}
//当前服务器已开服天数(开服第几天)
//当前开服天数 计算公式:(当前日期 - 开服日期)的总天数 + 1
func ServerOpenDays() (days int32) {
serverGroupObj := GetServerGroup()
if serverGroupObj.IsOpen() == false {
return 0
}
//(当前日期 - 开服日期)的总天数 + 1
openTimeTick := serverGroupObj.OpenTimeTick
openDate, _ := typeUtil.DateTime(openTimeTick)
days = int32(timeUtil.SubDay(time.Now(), openDate) + 1)
return
}

View File

@@ -0,0 +1,57 @@
package gameServerMgr
import (
"encoding/json"
"fmt"
"strconv"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
//修改区服注册人数
func UpdateRegisterCount(serverGroupId, registerCount int32) (err error, success bool) {
success = false
//定义参数
requestParamMap := make(map[string]string, 0)
requestParamMap["ServerGroupId"] = strconv.Itoa(int(serverGroupId))
requestParamMap["Registrations"] = strconv.Itoa(int(registerCount))
//构造请求url
url := fmt.Sprintf("%s/%s", mManageCenterServerAPIUrl, "/API/RegistrationUpdate.ashx")
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, nil)
if err != nil {
return
}
if statusCode != 200 {
err = fmt.Errorf("StatusCode:%d", statusCode)
return
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("更新区服注册人数出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
err = fmt.Errorf("更新区服注册人数出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(fmt.Sprintf("更新区服注册人数出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message))
return
}
success = true
return
}

View File

@@ -0,0 +1,105 @@
package gameServerMgr
import (
"encoding/json"
"errors"
"fmt"
"time"
"Framework/goroutineMgr"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
const SYSCONF_URL_SUFFIX string = "/API/SysConfig.ashx"
var (
mSysConfig *SysConfig
)
// 获取MC系统配置
func GetSysConfigFromManageCenterServer() error {
//定义参数
requestParamMap := make(map[string]string, 0)
requestParamMap["IsResultCompressed"] = "false"
//构造url
url := fmt.Sprintf("%s/%s", mManageCenterServerAPIUrl, SYSCONF_URL_SUFFIX)
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错反序列化返回值出错错误信息为%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
return nil
} else {
msg := fmt.Sprintf("获取MC系统配置出错返回状态%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
var tmpSysConfig *SysConfig
if data, ok := returnObj.Data.(string); !ok {
msg := "获取MC系统配置出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpSysConfig); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错出错反序列化数据出错错误信息为%s", err))
return err
}
}
// 赋值给最终的sysconfig
mSysConfig = tmpSysConfig
return nil
}
// 定时刷新MC系统配置
func StartRefreshSysConfigTread() {
// 定时刷新数据
go func() {
goroutineName := "gameServerMgr.StartRefreshSysConfigTread"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
for {
// 每30秒刷新一次
time.Sleep(30 * time.Second)
// MC系统配置
GetSysConfigFromManageCenterServer()
}
}()
}
// 获取系统配置
func GetSysConfig() *SysConfig {
return mSysConfig
}

View File

@@ -0,0 +1,124 @@
package gameServerMgr
import (
"encoding/json"
"errors"
"fmt"
"time"
"Framework/goroutineMgr"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
const URL_SUFFIX string = "/API/UserWhiteList.ashx"
var (
mUserWhiteListMap = make(map[int32]map[string]*WhiteList, 0)
mHashValue string
)
// 获取白名单
func GetWhiteListFromManageCenterServer() error {
//定义参数
requestParamMap := make(map[string]string, 0)
requestParamMap["HashValue"] = mHashValue
//构造url
url := fmt.Sprintf("%s/%s", mManageCenterServerAPIUrl, URL_SUFFIX)
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
return nil
} else {
msg := fmt.Sprintf("获取白名单列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
var tmpWhiteList []*WhiteList
if data, ok := returnObj.Data.(string); !ok {
msg := "获取白名单列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpWhiteList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
tmpWhiteListMap := make(map[int32]map[string]*WhiteList, 64)
for _, item := range tmpWhiteList {
if _, exist := tmpWhiteListMap[item.PartnerId]; !exist {
tmpWhiteListMap[item.PartnerId] = make(map[string]*WhiteList, 8)
}
tmpWhiteListMap[item.PartnerId][item.UserId] = item
}
// 赋值给最终的whiteListMap
mUserWhiteListMap = tmpWhiteListMap
mHashValue = returnObj.HashValue
return nil
}
// 判断用户是否在白名单里面
// partnerId: 合作商ID
// userId: userId
func IsInWhiteList(partnerId int32, userId string) bool {
subWhiteListMap, exist := mUserWhiteListMap[partnerId]
if !exist {
return false
}
_, exist = subWhiteListMap[userId]
return exist
}
// 定时刷新白名单
func StartRefreshWhiteListTread() {
// 定时刷新数据
go func() {
goroutineName := "gameServerMgr.StartRefreshWhiteListTread"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
for {
// 每30秒刷新一次
time.Sleep(30 * time.Second)
// 刷新白名单
GetWhiteListFromManageCenterServer()
}
}()
}

17
trunk/framework/go.mod Normal file
View File

@@ -0,0 +1,17 @@
module Framework
go 1.22.10
replace goutil => ../goutil
require (
github.com/Shopify/sarama v1.29.1
github.com/go-sql-driver/mysql v1.5.0
github.com/gorilla/websocket v1.4.2
github.com/jinzhu/gorm v1.9.12
github.com/rabbitmq/amqp091-go v1.8.1
github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.230
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vms v1.0.230
goutil v0.0.0-00010101000000-000000000000
)

242
trunk/framework/go.sum Normal file
View File

@@ -0,0 +1,242 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.29.1 h1:wBAacXbYVLmWieEA/0X/JagDdCZ8NVFOfS6l6+2u5S0=
github.com/Shopify/sarama v1.29.1/go.mod h1:mdtqvCSg8JOxk8PmpTNGyo6wzd4BMm4QXSfDnTXmgkE=
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4 h1:OoL469zqSNrTLSz5zeVF/I6VOO7fiw2bzSzQe4J557c=
github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8=
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA=
github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 h1:AJNDS0kP60X8wwWFvbLPwDuojxubj9pbfK7pjHw0vKg=
github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.230 h1:4n29EOxPk3fi0epVT+cY7Iwygep7vxn/LCq6RCBAwaM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.230/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vms v1.0.230 h1:jIEph3MVl/7ex+4gK6LtBCpmeGwq+OUSRDmoPshWxpc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vms v1.0.230/go.mod h1:zElyabRGi8DktckzhT3krsYChstFJdSrzDb7pwF2P58=
github.com/xdg/scram v1.0.3/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -0,0 +1,16 @@
package goroutineMgr
/*
goroutine的管理包提供了对goroutine的监控机制
对外提供的方法为:
// 监控指定的goroutine
Monitor(goroutineName string)
// 只添加数量,不监控
MonitorZero(goroutineName string)
// 释放监控
ReleaseMonitor(goroutineName string)
*/

View File

@@ -0,0 +1,93 @@
package goroutineMgr
import (
"fmt"
"runtime"
"sort"
"sync"
"goutil/logUtil"
)
var (
goroutineCountMap = make(map[string]int)
goroutineCountMutex sync.RWMutex
)
// 增加指定名称的goroutine的数量
// goroutineName:goroutine名称
func increaseCount(goroutineName string) {
goroutineCountMutex.Lock()
defer goroutineCountMutex.Unlock()
newCount := 1
if currCount, exist := goroutineCountMap[goroutineName]; exist {
newCount = currCount + 1
}
goroutineCountMap[goroutineName] = newCount
}
// 减少指定名称的goroutine的数量
// goroutineName:goroutine名称
func decreaseCount(goroutineName string) {
goroutineCountMutex.Lock()
defer goroutineCountMutex.Unlock()
newCount := -1
if currCount, exist := goroutineCountMap[goroutineName]; exist {
newCount = currCount - 1
}
if newCount <= 0 {
delete(goroutineCountMap, goroutineName)
} else {
goroutineCountMap[goroutineName] = newCount
}
}
// 获取指定名称的goroutine的数量
// goroutineName:goroutine名称
// 返回值:
// 对应数量
func getGoroutineCount(goroutineName string) int {
goroutineCountMutex.RLock()
defer goroutineCountMutex.RUnlock()
if currCount, exist := goroutineCountMap[goroutineName]; exist {
return currCount
}
return 0
}
// 转化成字符串
func toString() string {
goroutineCountMutex.RLock()
defer goroutineCountMutex.RUnlock()
keys := make([]string, 0, 16)
for key := range goroutineCountMap {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
str := fmt.Sprintf("Goroutine Info:(%s,%d)", "NumGoroutine", runtime.NumGoroutine())
for _, key := range keys {
str += fmt.Sprintf("(%s,%d)", key, goroutineCountMap[key])
}
return str
}
// 记录goroutine数量信息
func logGoroutineCountInfo() {
logUtil.DebugLog(toString())
}
func Test() {
logGoroutineCountInfo()
}

View File

@@ -0,0 +1,75 @@
package goroutineMgr
import (
"fmt"
"runtime"
"sync"
"framework/monitorNewMgr"
"goutil/logUtil"
)
var (
goroutineInfoMap map[string]int = make(map[string]int)
goroutineInfoMutex sync.RWMutex
goroutineWarnCount int = 50
)
func init() {
monitorNewMgr.RegisterMonitorFunc(monitor)
}
// 设置系统协程上报阈值
func SetGoroutineWarnCount(warnCount int) {
goroutineWarnCount = warnCount
}
func registerGoroutineInfo(goroutineName string, count int) {
goroutineInfoMutex.Lock()
defer goroutineInfoMutex.Unlock()
goroutineInfoMap[goroutineName] = count
}
// 监控指定的goroutine
func Monitor(goroutineName string) {
increaseCount(goroutineName)
registerGoroutineInfo(goroutineName, 1)
}
// 只添加数量,不监控
func MonitorZero(goroutineName string) {
increaseCount(goroutineName)
}
// 释放监控
func ReleaseMonitor(goroutineName string) {
if r := recover(); r != nil {
logUtil.LogUnknownError(r)
}
decreaseCount(goroutineName)
}
func monitor() error {
// 判断当前goroutine数量是否达到打印条件数量查过设置的值就打印
if goroutineWarnCount >= runtime.NumGoroutine() {
return nil
}
/*
先记录活跃的goroutine的数量信息
然后再判断数量是否匹配
*/
logGoroutineCountInfo()
goroutineInfoMutex.RLock()
defer goroutineInfoMutex.RUnlock()
for goroutineName, expectedCount := range goroutineInfoMap {
if currCount := getGoroutineCount(goroutineName); currCount != expectedCount {
return fmt.Errorf("%s需要%d个goroutine现在有%d个", goroutineName, expectedCount, currCount)
}
}
return nil
}

View File

@@ -0,0 +1,81 @@
// ************************************
// @package: handleMgr
// @description: 全局操作接口管理API类
// @author:
// @revision history:
// @create date: 2022-02-23 16:24:59
// ************************************
package handleMgr
/*
用于提供全局操作接口管理API类
*/
/*
接口说明
接口1
// RegisterNewModule
// @description: 注册方法
// parameter:
// @moduleName:模块名
// @structObject:模块对象
// @monitorTime:监控日志超时时间,传入0是用默认值100
// return:
示例RegisterNewModule("test", new(testBll), 0)
接口2
// Done
// @description: 执行访问
// parameter:
// @logicalId:逻辑实例Id-同一串行错误类的唯一标识
// @moduleName:模块名
// @funcName:执行方法名称
// @parameter:方法参数
// @isHaveResult:是否处理返回值
// return:
// @data:返还对象
// @code:错误码-0未无错误
// @message:错误说明-空字符串为无错误
示例data,code,mes := Done("test", "testBll", "Add", parameter, false)
注意:
注册的类实现的方法返回值必须是*ResponseObject为返回对象
示例:
type testBll struct {
}
// TestAdd
// @description:测试添加
// parameter:
// @receiver t:
// @x:
// @y:
// return:
// @*ResponseObject:
func (t testBll) TestAdd(x, y int) *ResponseObject {
responseObj := GetInitResponseObj()
data[x] = y
z := x + y
responseObj.SetData(z)
responseObj.SetResultStatus("错误码")
return responseObj
}
func TestNew(t *testing.T) {
// 注册类
RegisterNewModule("test", new(testBll), 10)
// 调用方法
data, code, mes := Done("test", "testBll", "TestAdd", parameter, false)
// 可以重复注册多个类,逻辑实例Id保持一致即可
RegisterNewModule("test", new(test2Bll), 10)
data, code, mes := Done("test", "test2Bll", "TestAdd", parameter, false)
}
*/

View File

@@ -0,0 +1,131 @@
// ************************************
// @package: handleMgr
// @description: 全局操作接口管理
// @author:
// @revision history:
// @create date: 2022-02-21 15:53:59
// ************************************
package handleMgr
import (
"fmt"
"Framework/goroutineMgr"
"goutil/logUtil"
"runtime/debug"
"time"
)
var (
handleData = make(map[string]*HandleObj)
)
// RegisterNewModule
// @description: 注册调用方法
// parameter:
// @logicalId:逻辑实例Id-同一串行错误类的唯一标识
// @structObject:模块对象
// @monitorTime:监控日志超时时间,传入0是用默认值100
// return:
func RegisterNewModule(logicalId string, structObject interface{}, monitorTime int64) {
// 注册模块,开启线程
_, isExists := handleData[logicalId]
if isExists == false {
newHandleObj := NewHandleObj(logicalId)
handleData[logicalId] = newHandleObj
// 设置监控默认值
if monitorTime == 0 {
monitorTime = 100
}
// 开启监控-channel
go func() {
registerName := fmt.Sprintf("handleMgr.RegisterNewModule.logicalId=%s", logicalId)
goroutineMgr.MonitorZero(registerName)
defer goroutineMgr.ReleaseMonitor(registerName)
defer logErrorRecover()
ret := 0
for {
select {
case c := <-newHandleObj.HandleChannel:
start := time.Now().UnixMilli()
responseObject := CallFunction(c)
end := time.Now().UnixMilli()
if c.IsHaveResult == true {
c.ResultChan <- responseObject
}
if end-start > monitorTime {
message := fmt.Sprintf("方法执行时间超长logicalId:%s,moduleName%s,MethodName:%s,消耗时间过程,总耗时:%d,需要检查代码!!!!!", logicalId, c.ModuleName, c.MethodName, end-start)
logUtil.ErrorLog(message)
}
ret++
if ret == 100 {
num := len(newHandleObj.HandleChannel)
if num > 500 {
message := fmt.Sprintf("时间戳:%d,channel监控,内部待执行操作过多logicalId:%s,当前待执行数量:%d,需要检查代码!!!!!", time.Now().UnixMilli(), logicalId, num)
logUtil.ErrorLog(message)
}
}
default:
// 如果找不到数据则休眠5ms
time.Sleep(5 * time.Millisecond)
}
}
}()
}
// 注册对象
RegisterFunction(structObject)
}
// Done
// @description: 执行访问
// parameter:
// @logicalId:逻辑实例Id-同一串行错误类的唯一标识
// @moduleName:模块名
// @funcName:执行方法名称
// @parameter:方法参数
// @isHaveResult:是否处理返回值
// return:
// @data:返还对象
// @code:错误码-0未无错误
// @message:错误说明-空字符串为无错误
func Done(logicalId string, moduleName string, funcName string, parameter []interface{}, isHaveResult bool) (data interface{}, code int, message string) {
responseObj := NewRequestObject(moduleName, funcName, parameter, isHaveResult)
handleObj, isExists := handleData[logicalId]
if isExists == false {
message := fmt.Sprintf("未注册模块moduleName%s", moduleName)
logUtil.ErrorLog(message)
return nil, -1, message
}
handleObj.HandleChannel <- responseObj
if responseObj.IsHaveResult == false {
return nil, 0, ""
}
for {
select {
case responseObject := <-responseObj.ResultChan:
// 处理返回值
return responseObject.Data, 0, responseObject.Message
default:
// 如果找不到数据则休眠5ms
time.Sleep(5 * time.Millisecond)
}
}
}
// logErrorRecover
// @description: 打印日志
// parameter:
// return:
func logErrorRecover() {
if err := recover(); err != nil {
msg := fmt.Sprintf("err msg:%s stack:%s", err, debug.Stack())
logUtil.ErrorLog(msg)
}
}

View File

@@ -0,0 +1,26 @@
// ************************************
// @package: handleMgr
// @description: HandleObj 操作对象
// @author: zhaoxin
// @revision history:
// @create date: 2022-02-21 16:52:44
// ************************************
package handleMgr
// HandleObj 操作对象
type HandleObj struct {
// 模块命名
ModuleName string
// 操作channel
HandleChannel chan *RequestObject
}
// NewHandleObj 初始化
func NewHandleObj(moduleName string) *HandleObj {
return &HandleObj{
ModuleName: moduleName,
HandleChannel: make(chan *RequestObject, 1000),
}
}

View File

@@ -0,0 +1,351 @@
// ************************************
// @package: handleMgr
// @description: 反射类
// @author:
// @revision history:
// @create date: 2022-02-23 16:33:27
// ************************************
package handleMgr
import (
"fmt"
"goutil/logUtil"
"reflect"
"strings"
)
const (
// 定义用于分隔模块名称和方法名称的分隔符
con_DelimeterOfObjAndMethod = "_"
)
var (
// 定义存放所有方法映射的变量
methodMap = make(map[string]*ReflectMethod, 4)
// 函数返回值类型
responseType reflect.Type = reflect.TypeOf(new(ResponseObject))
)
// getStructName
// @description: 获取结构体类型的名称
// parameter:
// @structType:结构体类型
// return:
// @string:结构体类型的名称
func getStructName(structType reflect.Type) string {
reflectTypeStr := structType.String()
reflectTypeArr := strings.Split(reflectTypeStr, ".")
return reflectTypeArr[len(reflectTypeArr)-1]
}
// getFullMethodName
// @description: 获取完整的方法名称
// parameter:
// @structName:结构体名称
// @methodName:方法名称
// return:
// @string:完整的方法名称
func getFullMethodName(structName, methodName string) string {
return structName + con_DelimeterOfObjAndMethod + methodName
}
// resolveMethodInOutParams
// @description: 解析方法的输入输出参数
// parameter:
// @method:方法对应的反射值
// return:
// @inTypes:输入参数类型集合
// @outTypes:输出参数类型集合
func resolveMethodInOutParams(method reflect.Value) (inTypes []reflect.Type, outTypes []reflect.Type) {
methodType := method.Type()
for i := 0; i < methodType.NumIn(); i++ {
inTypes = append(inTypes, methodType.In(i))
}
for i := 0; i < methodType.NumOut(); i++ {
outTypes = append(outTypes, methodType.Out(i))
}
return
}
// RegisterFunction
// @description: 对象注册
// parameter:
// @structObject:对象
// return:
func RegisterFunction(structObject interface{}) {
// 获取structObject对应的反射 Type 和 Value
reflectValue := reflect.ValueOf(structObject)
reflectType := reflect.TypeOf(structObject)
// 提取对象类型名称
structName := getStructName(reflectType)
// 获取structObject中返回值为responseObject的方法
for i := 0; i < reflectType.NumMethod(); i++ {
// 获得方法名称
methodName := reflectType.Method(i).Name
// 获得方法及其输入参数的类型列表
method := reflectValue.MethodByName(methodName)
inTypes, outTypes := resolveMethodInOutParams(method)
// 判断输出参数数量是否正确
if len(outTypes) != 1 {
continue
}
// 判断返回值是否为responseObject
if outTypes[0] != responseType {
continue
}
// 添加到列表中
methodMap[getFullMethodName(structName, methodName)] = NewReflectMethod(method, inTypes, outTypes)
}
}
// CallFunction
// @description: 调用方法
// parameter:
// @requestObject:请求对象
// return:
// @responseObj:返还对象
func CallFunction(requestObject *RequestObject) (responseObj *ResponseObject) {
responseObj = GetInitResponseObj()
var reflectMethod *ReflectMethod
var ok bool
// 根据传入的ModuleName和MethodName找到对应的方法对象
key := getFullMethodName(requestObject.ModuleName, requestObject.MethodName)
if reflectMethod, ok = methodMap[key]; !ok {
message := fmt.Sprintf("找不到指定的方法:%s", key)
logUtil.ErrorLog(message)
responseObj.SetResultStatus(-2, message)
return
}
// 判断参数数量是否相同
inTypesLength := len(reflectMethod.InTypes)
paramLength := len(requestObject.Parameters)
if paramLength != inTypesLength {
message := fmt.Sprintf("传入的参数数量不符,本地方法%s的参数数量%d传入的参数数量为%d", key, inTypesLength, paramLength)
logUtil.ErrorLog(message)
responseObj.SetResultStatus(-3, message)
return
}
// 构造参数
in := make([]reflect.Value, inTypesLength)
for i := 0; i < inTypesLength; i++ {
inTypeItem := reflectMethod.InTypes[i]
paramItem := requestObject.Parameters[i]
// 已支持类型Client,Player(非基本类型)
// 已支持类型Bool,Int,Int8,Int16,Int32,Int64,Uint,Uint8,Uint16,Uint32,Uint64,Float32,Float64,String
// 已支持类型以及上面所列出类型的Slice类型
// 未支持类型Uintptr,Complex64,Complex128,Array,Chan,Func,Interface,Map,Ptr,Struct,UnsafePointer
// 由于byte与int8同义rune与int32同义所以并不需要单独处理
// 枚举参数的类型,并进行类型转换
switch inTypeItem.Kind() {
case reflect.Bool:
if paramBool, ok := paramItem.(bool); ok {
in[i] = reflect.ValueOf(paramBool)
}
case reflect.Int:
if paramFloat64, ok := paramItem.(int); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Int8:
if paramFloat64, ok := paramItem.(int8); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Int16:
if paramFloat64, ok := paramItem.(int16); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Int32:
if paramFloat64, ok := paramItem.(int32); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Int64:
if paramFloat64, ok := paramItem.(int64); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Uint:
if paramFloat64, ok := paramItem.(uint); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Uint8:
if paramFloat64, ok := paramItem.(uint8); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Uint16:
if paramFloat64, ok := paramItem.(uint16); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Uint32:
if paramFloat64, ok := paramItem.(uint32); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Uint64:
if paramFloat64, ok := paramItem.(uint64); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Float32:
if paramFloat64, ok := paramItem.(float32); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.Float64:
if paramFloat64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(paramFloat64)
}
case reflect.String:
if paramString, ok := paramItem.(string); ok {
in[i] = reflect.ValueOf(paramString)
}
case reflect.Slice:
// 如果是Slice类型则需要对其中的项再次进行类型判断及类型转换
if paramInterface, ok := paramItem.([]interface{}); ok {
switch inTypeItem.String() {
case "[]bool":
paramsInner := make([]bool, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramBool, ok := paramInterface[i].(bool); ok {
paramsInner[i] = paramBool
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]int":
paramsInner := make([]int, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(int); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]int8":
paramsInner := make([]int8, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(int8); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]int16":
paramsInner := make([]int16, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(int16); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]int32":
paramsInner := make([]int32, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(int32); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]int64":
paramsInner := make([]int64, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(int64); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]uint":
paramsInner := make([]uint, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(uint); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
// case "[]uint8": 特殊处理
case "[]uint16":
paramsInner := make([]uint16, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(uint16); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]uint32":
paramsInner := make([]uint32, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(uint32); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]uint64":
paramsInner := make([]uint64, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(uint64); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]float32":
paramsInner := make([]float32, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(float32); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]float64":
paramsInner := make([]float64, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramFloat64, ok := paramInterface[i].(float64); ok {
paramsInner[i] = paramFloat64
}
}
in[i] = reflect.ValueOf(paramsInner)
case "[]string":
paramsInner := make([]string, len(paramInterface))
for i := 0; i < len(paramInterface); i++ {
if paramString, ok := paramInterface[i].(string); ok {
paramsInner[i] = paramString
}
}
in[i] = reflect.ValueOf(paramsInner)
}
} else if inTypeItem.String() == "[]uint8" { // 由于[]uint8在传输过程中会被转化成字符串所以单独处理;
if paramString, ok := paramItem.(string); ok {
paramUint8 := ([]uint8)(paramString)
in[i] = reflect.ValueOf(paramUint8)
}
}
}
}
// 判断是否有无效的参数(传入的参数类型和方法定义的类型不匹配导致没有赋值)
for _, item := range in {
if reflect.Value.IsValid(item) == false {
message := fmt.Sprintf("type:%v,value:%v.方法%s传入的参数%v无效", reflect.TypeOf(item), reflect.ValueOf(item), key, requestObject.Parameters)
logUtil.ErrorLog(message)
responseObj.SetResultStatus(-1, message)
return
}
}
out := reflectMethod.Method.Call(in)
// 并输出结果到客户端由于只有一个返回值所以取out[0]
if responseObj, ok = (&out[0]).Interface().(*ResponseObject); !ok {
message := fmt.Sprintf("返回值类型推断为ResponseObject 出错, tyep is :%v", reflect.TypeOf(out[0]))
logUtil.ErrorLog(message)
responseObj.SetResultStatus(-1, message)
return
}
return
}

View File

@@ -0,0 +1,41 @@
// ************************************
// @package: handleMgr
// @description: 反射类-反射的方法和输入、输出参数类型组合类型
// @author:
// @revision history:
// @create date: 2022-02-23 16:33:43
// ************************************
package handleMgr
import (
"reflect"
)
// ReflectMethod 反射的方法和输入、输出参数类型组合类型
type ReflectMethod struct {
// 反射出来的对应方法对象
Method reflect.Value
// 反射出来的方法的输入参数的类型集合
InTypes []reflect.Type
// 反射出来的方法的输出参数的类型集合
OutTypes []reflect.Type
}
// NewReflectMethod
// @description:创建反射的方法和输入、输出参数类型组合类型
// parameter:
// @_method:反射出来的对应方法对象
// @_inTypes:反射出来的方法的输入参数的类型集合
// @_outTypes:反射出来的方法的输出参数的类型集合
// return:
// @*ReflectMethod:
func NewReflectMethod(_method reflect.Value, _inTypes []reflect.Type, _outTypes []reflect.Type) *ReflectMethod {
return &ReflectMethod{
Method: _method,
InTypes: _inTypes,
OutTypes: _outTypes,
}
}

View File

@@ -0,0 +1,46 @@
// ************************************
// @package: handleMgr
// @description: 反射类-请求对象
// @author:
// @revision history:
// @create date: 2022-02-23 16:33:53
// ************************************
package handleMgr
// RequestObject 请求对象
type RequestObject struct {
// 请求的模块名称
ModuleName string
// 请求的方法名称
MethodName string
// 请求的参数数组
Parameters []interface{}
// 是否处理返回值
IsHaveResult bool
// 执行完成,返回chan监控
ResultChan chan *ResponseObject
}
// NewRequestObject
// @description:
// parameter:
// @moduleName:模块名名称
// @methodName:执行方法名称
// @parameters:方法参数
// @isHaveResult:是否处理返回值
// return:
// @*RequestObject:
func NewRequestObject(moduleName string, methodName string, parameters []interface{}, isHaveResult bool) *RequestObject {
return &RequestObject{
ModuleName: moduleName,
MethodName: methodName,
Parameters: parameters,
IsHaveResult: isHaveResult,
ResultChan: make(chan *ResponseObject, 1),
}
}

View File

@@ -0,0 +1,63 @@
// ************************************
// @package: handleMgr
// @description: 反射类-响应对象
// @author:
// @revision history:
// @create date: 2022-02-23 16:34:08
// ************************************
package handleMgr
// ResponseObject 响应对象
type ResponseObject struct {
// 错误码
Code int
// 响应结果的状态值所对应的描述信息
Message string
// 响应结果的数据
Data interface{}
}
// SetResultStatus
// @description: 设置响应结果的状态值
// parameter:
// @receiver r:
// @code:响应结果的错误码
// @message:响应结果的字符串
// return:响应结果对象
// @*ResponseObject:
func (r *ResponseObject) SetResultStatus(code int, message string) *ResponseObject {
r.Code = code
r.Message = message
return r
}
// SetData
// @description: 设置响应结果的状态值
// parameter:
// @receiver r:
// @message:响应结果的字符串
// return:响应结果对象
// @*ResponseObject:
func (r *ResponseObject) SetData(data interface{}) *ResponseObject {
r.Data = data
return r
}
// GetInitResponseObj
// @description: 初始化
// parameter:
// @moduleName:模块名名称
// @methodName:执行方法名称
// @parameters:方法参数
// @isHaveResult:是否处理返回值
// return:
// @*ResponseObject:GetInitResponseObj
func GetInitResponseObj() *ResponseObject {
return &ResponseObject{
Message: "",
Data: nil,
}
}

View File

@@ -0,0 +1,133 @@
package handleMgr
import (
"fmt"
"reflect"
"testing"
"time"
)
// ---------------申请示例实例--------------
var (
data = make(map[int]int, 0)
)
type testBll struct {
}
// TestAdd
// @description:测试添加
// parameter:
// @receiver t:
// @x:
// @y:
// return:
// @*ResponseObject:
func (t testBll) TestAdd(x, y int) *ResponseObject {
responseObj := GetInitResponseObj()
data[x] = y
z := x + y
print(x)
responseObj.SetData(z)
responseObj.SetResultStatus(-11111, "错误码")
return responseObj
}
// See1
// @description:测试添加
// parameter:
// @receiver t:
// @x:
// return:
// @*ResponseObject:
func (t testBll) See1(x int) *ResponseObject {
responseObj := GetInitResponseObj()
fmt.Print(x)
return responseObj
}
// See2
// @description:测试添加
// parameter:
// @receiver t:
// @x:
// return:
// @*ResponseObject:
func (t testBll) See2(x int) *ResponseObject {
responseObj := GetInitResponseObj()
time.Sleep(2000 * time.Millisecond)
fmt.Print(x)
return responseObj
}
func (t testBll) ReMove(x int) *ResponseObject {
responseObj := GetInitResponseObj()
delete(data, x)
return responseObj
}
// ---------------测试方法--------------
func TestNew(t *testing.T) {
var paramItem interface{}
paramItem = 123
paramFloat64, ok := paramItem.(int)
if ok {
x := reflect.ValueOf(int(paramFloat64))
t.Log(x)
}
t.Log(paramFloat64)
t.Log(ok)
//t.Log("A")
//RegisterNewModule("test", new(testBll), 10)
//parameter := []interface{}{"1", 2}
//x, y, mes := Done("test", "testBll", "TestAdd", parameter, false)
//for key, item := range data {
// t.Log(key, item)
//}
//t.Log("B2")
//parameter1 := []interface{}{2, 2}
//x, y, mes = Done("test", "testBll", "TestAdd", parameter1, true)
//for key, item := range data {
// t.Log(key, item)
//}
//t.Log(x)
//t.Log(y)
//t.Log(mes)
//t.Log("B3")
//
//t.Log("C")
//parameter2 := []interface{}{2}
//Done("test", "testBll", "ReMove", parameter2, true)
//for key, item := range data {
// t.Log(key, item)
//}
//t.Log("D")
//
//t.Log(time.Now().UnixMilli())
//parameter3 := []interface{}{1}
//Done("test", "testBll", "See2", parameter3, false)
//t.Log(time.Now().UnixMilli())
//parameter4 := []interface{}{2}
//Done("test", "testBll", "See1", parameter4, true)
//t.Log(time.Now().UnixMilli())
//parameter5 := []interface{}{1}
//Done("test", "testBll", "See1", parameter5, true)
//t.Log(time.Now().UnixMilli())
//
//t.Log("E")
//
//t.Log(time.Now().UnixMilli())
//for i := 0; i < 1000; i++ {
// Done("test", "testBll", "See1", parameter5, true)
//}
//t.Log(time.Now().UnixMilli())
//t.Log("F")
t.Error("G")
}

View File

@@ -0,0 +1,62 @@
package initMgr
import (
"fmt"
"sync"
"goutil/debugUtil"
"goutil/logUtil"
)
// 初始化成功对象
type InitSuccess struct {
name string
registerMap map[string]chan bool
mutex sync.RWMutex
}
// 注册需要被通知的对象
// name:唯一标识
// ch:用于通知的通道
func (this *InitSuccess) Register(name string, ch chan bool) {
this.mutex.Lock()
defer this.mutex.Unlock()
if _, exists := this.registerMap[name]; exists {
panic(fmt.Errorf("InitSuccess.Register-%s-%s已经存在请检查", this.name, name))
}
this.registerMap[name] = ch
logUtil.DebugLog(fmt.Sprintf("InitSuccess.Register-%s当前共有%d个注册", this.name, len(this.registerMap)))
}
// 取消启动成功通知注册
// name:唯一标识
func (this *InitSuccess) Unregister(name string) {
this.mutex.Lock()
defer this.mutex.Unlock()
delete(this.registerMap, name)
}
// 通知所有已注册的对象
func (this *InitSuccess) Notify() {
this.mutex.RLock()
defer this.mutex.RUnlock()
for name, ch := range this.registerMap {
ch <- true
msg := fmt.Sprintf("通知:%s-%s初始化成功", this.name, name)
debugUtil.Println(msg)
logUtil.DebugLog(msg)
}
}
// 创建初始化成功对象
func NewInitSuccess(name string) *InitSuccess {
return &InitSuccess{
name: name,
registerMap: make(map[string]chan bool),
}
}

View File

@@ -0,0 +1,93 @@
/*
提供统一的ip验证的逻辑包括两部分
1、ManageCenter中配置到ServerGroup中的IP系统内部自动处理外部无需关注(暂时不用)
2、各个应用程序中配置的ip通过调用Init或InitString方法进行初始化
*/
package ipMgr
import (
"sync"
"goutil/stringUtil"
)
var (
ipMap = make(map[string]struct{}, 32) // 本地ip集合
ipCheckFuncList = make([]func(string) bool, 0, 16) // ip检查函数列表
mutex sync.RWMutex
)
// 初始化IP列表
func Init(ipList []string) {
mutex.Lock()
defer mutex.Unlock()
// 先清空再初始化
ipMap = make(map[string]struct{}, 32)
for _, item := range ipList {
ipMap[item] = struct{}{}
}
}
// 初始化ip字符串以分隔符分割的分隔符为",", ";", ":", "|", "||"
func InitString(ipStr string) {
mutex.Lock()
defer mutex.Unlock()
// 先清空再初始化
ipMap = make(map[string]struct{}, 32)
for _, item := range stringUtil.Split(ipStr, nil) {
ipMap[item] = struct{}{}
}
}
func AddString(ipStr string) {
mutex.Lock()
defer mutex.Unlock()
// 先清空再初始化
if ipMap == nil {
ipMap = make(map[string]struct{}, 32)
}
for _, item := range stringUtil.Split(ipStr, nil) {
ipMap[item] = struct{}{}
}
}
// RegisterIpCheckFunc 注册Ip检查函数
func RegisterIpCheckFunc(funcName string, funcItem func(string) bool) {
mutex.Lock()
defer mutex.Unlock()
ipCheckFuncList = append(ipCheckFuncList, funcItem)
}
func isLocalValid(ip string) bool {
mutex.RLock()
defer mutex.RUnlock()
_, exist := ipMap[ip]
return exist
}
func isCheckFuncValid(ip string) bool {
mutex.RLock()
defer mutex.RUnlock()
for _, funcItem := range ipCheckFuncList {
if funcItem(ip) {
return true
}
}
return false
}
// 判断传入的Ip是否有效
// ip:ip
// 返回值:
// 是否有效
func IsIpValid(ip string) bool {
return isLocalValid(ip) || isCheckFuncValid(ip)
}

View File

@@ -0,0 +1,44 @@
package ipMgr
import (
"testing"
)
func TestIsIpValid(t *testing.T) {
ip := "10.255.0.7"
if IsIpValid(ip) {
t.Errorf("%s应该无效但是现在却有效", ip)
}
ipList := []string{"10.255.0.7", "10.1.0.21"}
Init(ipList)
if IsIpValid(ip) == false {
t.Errorf("%s应该有效但是现在却无效", ip)
}
ipStr := "10.255.0.7,10.1.0.21;10.255.0.6|10.1.0.30||"
InitString(ipStr)
if IsIpValid(ip) == false {
t.Errorf("%s应该有效但是现在却无效", ip)
}
ip = "192.168.1.1"
RegisterIpCheckFunc("", alwaysFalse)
if IsIpValid(ip) {
t.Errorf("%s应该无效但是现在却有效", ip)
}
RegisterIpCheckFunc("", alwaysTrue)
if IsIpValid(ip) == false {
t.Errorf("%s应该有效但是现在却无效", ip)
}
}
func alwaysTrue(ip string) bool {
return true
}
func alwaysFalse(ip string) bool {
return false
}

View File

@@ -0,0 +1,93 @@
package ipMgr
import (
"encoding/json"
"fmt"
"time"
"goutil/securityUtil"
"goutil/webUtil"
)
var (
IP_SERVICE_URL = "http://ipip.7qule.com/query"
)
// 服务器的响应对象
type QueryResponse struct {
// 响应结果的状态值
ResultStatus string
// 响应结果的数据
Data *IPInfo
}
// IP信息对象
type IPInfo struct {
// 所属大陆块
Continent string
// 国家
Country string
// 省份
Region string
// 城市
City string
// 网络服务提供商
Isp string
}
// 查询ip地址的详细信息
// appId: 为应用分配的唯一标识
// appSecret: 为应用分配的密钥
// ip: 待查询的ip地址
// isDomestic: 是否为国内的IP
// timeout:超时时间(单位:秒)
// 返回值:
// ipInfoObj: IP地址信息对象
// err: 错误对象
func Query(appId, appSecret, ip string, isDomestic bool, timeout int) (ipInfoObj *IPInfo, err error) {
timeStamp := fmt.Sprintf("%d", time.Now().Unix())
rawString := fmt.Sprintf("AppId=%s&IP=%s&Timestamp=%s&AppSecret=%s", appId, ip, timeStamp, appSecret)
sign := securityUtil.Md5String(rawString, true)
postData := make(map[string]string, 5)
postData["AppId"] = appId
postData["IP"] = ip
postData["IsDomestic"] = fmt.Sprintf("%t", isDomestic)
postData["Timestamp"] = timeStamp
postData["Sign"] = sign
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, timeout)
statusCode, result, err := webUtil.PostMapData(IP_SERVICE_URL, postData, header, transport)
//statusCode, result, err := webUtil.PostMapData(IP_SERVICE_URL, postData, header, transport)
if err != nil {
return
}
if statusCode != 200 {
err = fmt.Errorf("StatusCode:%d is wrong.", statusCode)
return
}
var queryResponseObj *QueryResponse
err = json.Unmarshal(result, &queryResponseObj)
if err != nil {
return
}
if queryResponseObj.ResultStatus != "" {
err = fmt.Errorf("Query result:%s", queryResponseObj.ResultStatus)
return
}
ipInfoObj = queryResponseObj.Data
return
}

View File

@@ -0,0 +1,98 @@
package ipMgr
import (
"fmt"
"testing"
)
func TestQuery(t *testing.T) {
IP_SERVICE_URL = "http://ipip.7qule.com/query"
appId := "unittest"
appId_wrong := "wrong"
appSecret := "c5746980-5d52-4ba9-834f-13d0066ad1d0"
appSecret_wrong := "wrong"
ip := "117.139.247.210"
ip_wrong := "wrong"
isDomestic := true
continent := ""
country := "中国"
region := "四川"
city := "成都"
timeout := 3
// Test with wrong AppId
ipInfoObj, err := Query(appId_wrong, appSecret, ip, isDomestic, timeout)
if err == nil {
t.Errorf("There should be an error, but now there is no error")
return
}
// Test with wrong AppSecret
ipInfoObj, err = Query(appId, appSecret_wrong, ip, isDomestic, timeout)
if err == nil {
t.Errorf("There should be an error, but now there is no error")
return
}
// Test with wrong IP
ipInfoObj, err = Query(appId, appSecret, ip_wrong, isDomestic, timeout)
if err == nil {
t.Errorf("There should be an error, but now there is no error")
return
}
// Test with correct information and domestic ip
ipInfoObj, err = Query(appId, appSecret, ip, isDomestic, timeout)
if err != nil {
t.Errorf("There should be no error, but now there is one:%s", err)
return
}
fmt.Printf("ipInfoObj:%v\n", ipInfoObj)
if ipInfoObj.Continent != continent {
t.Errorf("Expected continent %s, but got %s", continent, ipInfoObj.Continent)
}
if ipInfoObj.Country != country {
t.Errorf("Expected country %s, but got %s", country, ipInfoObj.Country)
}
if ipInfoObj.Region != region {
t.Errorf("Expected region %s, but got %s", region, ipInfoObj.Region)
}
if ipInfoObj.City != city {
t.Errorf("Expected city %s, but got %s", city, ipInfoObj.City)
}
// Test with correct information and foreign ip
isDomestic = false
continent = "亚洲"
country = "中国"
region = "四川省"
city = "成都"
ipInfoObj, err = Query(appId, appSecret, ip, isDomestic, timeout)
if err != nil {
t.Errorf("There should be no error, but now there is one:%s", err)
return
}
fmt.Printf("ipInfoObj:%v\n", ipInfoObj)
if ipInfoObj.Continent != continent {
t.Errorf("Expected continent %s, but got %s", continent, ipInfoObj.Continent)
}
if ipInfoObj.Country != country {
t.Errorf("Expected country %s, but got %s", country, ipInfoObj.Country)
}
if ipInfoObj.Region != region {
t.Errorf("Expected region %s, but got %s", region, ipInfoObj.Region)
}
if ipInfoObj.City != city {
t.Errorf("Expected city %s, but got %s", city, ipInfoObj.City)
}
}

View File

@@ -0,0 +1,51 @@
package kafkaMgr
import "strings"
// Kafka配置对象
type KafkaConfig struct {
// Brokers的地址
Brokers string
// 主题
Topics string
// 分区
Partitions string
// 分组Id
GroupId string
// 用户名
UserName string
// 密码
Passward string
// 需要的证书文件
CertFile string
}
func (this *KafkaConfig) GetBrokerList() []string {
if len(this.Brokers) <= 0 {
return make([]string, 0)
}
return strings.Split(this.Brokers, ",")
}
func (this *KafkaConfig) GetTopicList() []string {
if len(this.Topics) <= 0 {
return make([]string, 0)
}
return strings.Split(this.Topics, ",")
}
func (this *KafkaConfig) GetPartitionList() []string {
if len(this.Partitions) <= 0 {
return make([]string, 0)
}
return strings.Split(this.Partitions, ",")
}

View File

@@ -0,0 +1,5 @@
package linuxMgr
// Linux平台的管理器主要提供在Linux平台上才能运行的一些功能
// 如syscall.Dup2
// 使用方式,在游戏中引用此包即可

View File

@@ -0,0 +1,29 @@
// +build !windows
package linuxMgr
import (
"os"
"path/filepath"
"syscall"
"goutil/fileUtil"
)
func init() {
//验证文件夹是否存在
fileAbsoluteDirectory := filepath.Join("Log")
if !fileUtil.IsDirExists(fileAbsoluteDirectory) {
if err := os.MkdirAll(fileAbsoluteDirectory, os.ModePerm|os.ModeTemporary); err != nil {
return
}
}
// 标准输出
stdoutFile, _ := os.OpenFile("Log/Stdout.txt", os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
syscall.Dup2(int(stdoutFile.Fd()), 1)
// 标准错误输出
stderrFile, _ := os.OpenFile("Log/Stderr.txt", os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0644)
syscall.Dup2(int(stderrFile.Fd()), 2)
}

View File

@@ -0,0 +1,174 @@
package managecenterMgr
import (
"encoding/json"
"errors"
"fmt"
"Framework/managecenterModel"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
var (
areaMap = make(map[int32]*Area, 128)
areaHash string
)
// 重新加载大区数据
func reloadArea(isInit bool) error {
//logUtil.DebugLog("开始刷新大区列表")
// 连接服务器,以获取数据
url := getManageCenterUrl("/API/AreaList.ashx")
// 定义请求参数
postDict := make(map[string]string)
postDict["HashValue"] = areaHash
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取大区列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取大区列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取大区列表出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
//如果本地集合为空且数据又没变化时重新初始化一下本地hash值
if len(areaMap) == 0 {
areaHash = ""
}
return nil
} else {
msg := fmt.Sprintf("获取大区列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
tmpAreaList := make([]*Area, 0, 128)
if data, ok := returnObj.Data.(string); !ok {
msg := "获取大区列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpAreaList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取大区列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
//logUtil.DebugLog(fmt.Sprintf("刷新大区信息结束,大区数量:%d", len(tmpAreaList)))
tmpAreaMap := make(map[int32]*Area)
for _, item := range tmpAreaList {
tmpAreaMap[item.AreaId] = item
}
// 赋值给最终的areaMap
areaMap = tmpAreaMap
areaHash = returnObj.HashValue
// 通知变更
mcDataChangeCallBack(managecenterModel.AreaData, isInit)
return nil
}
// 根据区服ID获取所属大区ID
func GetAreaIdByServerId(serverId int32) (areaId int32, exist bool) {
for _, item := range areaMap {
if item.CheckServerIdIsInRange(serverId) {
areaId = item.AreaId
exist = true
return
}
}
return
}
// 根据服务器组id获取大区对象数据
func GetAreaDBByGroupId(groupId int32) (areaDB *Area, exist bool) {
//如果没有大区数据,返回空
exist = false
if areaMap == nil || len(areaMap) < 1 {
return
}
for _, item := range areaMap {
if item.CheckServerIdIsInRange(groupId) {
areaDB = item
exist = true
break
}
}
return
}
// 获取所有大区信息
func GetAllAreaDB() []*Area {
tempList := make([]*Area, 8)
for _, item := range areaMap {
tempList = append(tempList, item)
}
return tempList
}
// 根据大区ID获取大区信息
func GetAreaDBbyAreaID(areaId int32) (areaDB *Area, exist bool) {
//如果没有大区数据,返回空
exist = false
if areaMap == nil || len(areaMap) < 1 {
return
}
if item, ok := areaMap[areaId]; ok {
areaDB = item
exist = true
}
return
}
// 根据服务器ID获取大区页签信息
func GetAreaLabelDBByGroupId(groupId int32) (areaLabelDB *AreaLabel, exist bool) {
areaDB, exist := GetAreaDBByGroupId(groupId)
if !exist {
return nil, false
}
if areaDB.AreaLabelList == nil || len(areaDB.AreaLabelList) < 1 {
return nil, false
}
//校服务器ID是否在大区页签服务器范围内
for _, item := range areaDB.AreaLabelList {
if exist := item.CheckServerIdIsInLabelRange(groupId); exist {
return item, true
}
}
//如果没找到,则返回空
return nil, false
}

View File

@@ -0,0 +1,7 @@
package managecenterMgr
// ManageCenter的管理包提供管理合作商、服务器、服务器组、游戏资源、白名单等功能
// 使用方法:
// 首先调用Start方法这样会进行初始化后续就可以使用提供的其它方法
// 内部会定时去ManageCenter刷新最新的数据

View File

@@ -0,0 +1,137 @@
package managecenterMgr
import (
"fmt"
"strings"
"time"
"Framework/goroutineMgr"
"Framework/initMgr"
"Framework/managecenterModel"
"goutil/logUtil"
)
var (
mcAPIUrl string
mcDataSwitchObj *ManageCenterDataSwitch
initSuccessObj = initMgr.NewInitSuccess("managecenterMgr")
//数据变更通知回调
mDataChangeCallBackFunc func(managecenterModel.MCDataType) error
)
// 注册初始化成功的通道
// name:模块名称
// ch:通道对象
func RegisterInitSuccess(name string, ch chan bool) {
initSuccessObj.Register(name, ch)
}
// 注册MC数据变更通知回调函数
// handler:回调方法
func RegisterDataChangeCallBackFunc(handler func(managecenterModel.MCDataType) error) {
mDataChangeCallBackFunc = handler
}
// Start ...启动ManageCenter管理器
// manageCenterAPIUrl:ManageCenter对外提供的API
// manageCenterDataSwitchObj:ManageCenter数据获取开关对象
func Start(manageCenterAPIUrl string, manageCenterDataSwitchObj *ManageCenterDataSwitch) {
mcAPIUrl = manageCenterAPIUrl
mcDataSwitchObj = manageCenterDataSwitchObj
// 先初始化一次数据
if err := reload(true); err != nil {
panic(err)
}
// 通知初始化成功
initSuccessObj.Notify()
// 定时刷新数据
go func() {
goroutineName := "managecenterMgr.timelyRefresh"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
for {
// 每5秒刷新一次
time.Sleep(5 * time.Second)
// 刷新服务器组
reload(false)
}
}()
}
// 重新加载/初始化
func reload(isInit bool) error {
var err error
// 加载合作商数据
if mcDataSwitchObj.AllDataSwitch || mcDataSwitchObj.PartnerDataSwitch {
if err = reloadPartner(isInit); err != nil && isInit {
return err
}
}
// 加载服务器数据
if mcDataSwitchObj.AllDataSwitch || mcDataSwitchObj.ServerDataSwitch {
if err = reloadServer(isInit); err != nil && isInit {
return err
}
}
// 加载服务器组数据
if mcDataSwitchObj.AllDataSwitch || mcDataSwitchObj.ServerGroupDataSwitch {
if err = reloadServerGroup(isInit); err != nil && isInit {
return err
}
}
// 加载资源数据
if mcDataSwitchObj.AllDataSwitch || mcDataSwitchObj.ResourceVersionDataSwitch {
if err = reloadResourceVersion(isInit); err != nil && isInit {
return err
}
}
// 加载玩家白名单数据
if mcDataSwitchObj.AllDataSwitch || mcDataSwitchObj.WhiteListDataSwitch {
if err = reloadWhiteList(isInit); err != nil && isInit {
return err
}
}
// 加载大区数据
if mcDataSwitchObj.AllDataSwitch || mcDataSwitchObj.AreaDataSwitch {
if err = reloadArea(isInit); err != nil && isInit {
return err
}
}
return nil
}
// 获取可访问的ManageCenter地址
// suffix:Url后缀
// 返回值:
// 可访问的ManageCenter地址
func getManageCenterUrl(suffix string) string {
return fmt.Sprintf("%s/%s", strings.TrimRight(mcAPIUrl, "/"), strings.TrimLeft(suffix, "/"))
}
// 回调
// dataType:数据类型
// isInit:是否初始化
func mcDataChangeCallBack(dataType managecenterModel.MCDataType, isInit bool) {
//如果没注册回调函数,或者是数据初始化,则不回调
if mDataChangeCallBackFunc == nil || isInit {
return
}
//回调
err := mDataChangeCallBackFunc(dataType)
logUtil.ErrorLog(fmt.Sprintf("通知回调出错DataType:%d,错误信息为:%s", dataType, err))
}

View File

@@ -0,0 +1,25 @@
package managecenterMgr
// ManageCenter数据获取开关(每一类数据一个开关)
type ManageCenterDataSwitch struct {
// 获取所有数据开关只要这个开关值为true则不论各类数据的开关是否打开都获取数据
AllDataSwitch bool
// 获取合作商数据开关
PartnerDataSwitch bool
// 获取服务器数据开关
ServerDataSwitch bool
// 获取服务器组数开关
ServerGroupDataSwitch bool
// 获取资源包版本数据开关
ResourceVersionDataSwitch bool
// 获取白名单数据开关
WhiteListDataSwitch bool
// 获取大区数据开关
AreaDataSwitch bool
}

View File

@@ -0,0 +1,132 @@
package managecenterMgr
import (
"fmt"
"testing"
)
func init() {
manageCenterDataSwitchObj := &ManageCenterDataSwitch{
AllDataSwitch: true,
}
Start("https://managecenterapi-sd3.7qule.com/", manageCenterDataSwitchObj)
item, exist := GetAreaLabelDBByGroupId(82)
if exist {
fmt.Printf("%v", item)
}
}
func TestPartner(t *testing.T) {
partnerList := GetPartnerList()
fmt.Printf("PartnerList count:%v\n", len(partnerList))
if len(partnerList) == 0 {
t.Errorf("There is no partner.")
return
}
firstPartner := partnerList[0]
_, exist := GetPartner(firstPartner.Id)
if !exist {
t.Errorf("Partner with Id:%d should exist, but now it doesn't.", firstPartner.Id)
return
}
_, exist = GetPartnerByAlias(firstPartner.Alias)
if !exist {
t.Errorf("Partner with Alias:%s should exist, but now it doesn't.", firstPartner.Alias)
return
}
_, exist, err := GetOtherConfigInfo(firstPartner.Id, "LoginHandler")
if err != nil {
t.Errorf("There should no error for Partner:%d, but now there is one:%v", err, firstPartner.Id)
return
}
if !exist {
t.Errorf("Partner with Id:%d should have an other config named LoginHandler, but now there isn't.", firstPartner.Id)
return
}
}
func TestServer(t *testing.T) {
serverGroupList := GetServerGroupList()
if len(serverGroupList) == 0 {
t.Errorf("There is no ServerGroup.")
return
}
firstServerGroup := serverGroupList[0]
serverList := GetServerListByGroupId(firstServerGroup.Id)
fmt.Printf("There are %d servers of GroupId:%d\n", len(serverList), firstServerGroup.Id)
firstServer := serverList[0]
serverList = GetServerListByPartnerId(firstServer.PartnerId)
fmt.Printf("There are %d servers of PartnerId:%d\n", len(serverList), firstServer.PartnerId)
_, exist := GetServerItem(firstServer.PartnerId, firstServer.Id)
if !exist {
t.Errorf("There is no server with PartnerId:%d, ServerId:%d.", firstServer.PartnerId, firstServer.Id)
return
}
distinctServerIdList := GetDistinctServerIdList()
fmt.Printf("There are %d distinct ServerId\n", len(distinctServerIdList))
}
func TestServerGroup(t *testing.T) {
serverGroupList := GetServerGroupList()
if len(serverGroupList) == 0 {
t.Errorf("There is no ServerGroup.")
return
}
firstServerGroup := serverGroupList[0]
_, exist := GetServerGroupItem(firstServerGroup.Id)
if !exist {
t.Errorf("The ServerGroup with Id:%d should exist, but now it doesn't.", firstServerGroup.Id)
return
}
serverList := GetServerListByGroupId(firstServerGroup.Id)
if len(serverList) == 0 {
t.Errorf("There is no server of ServerGroupId:%d", firstServerGroup.Id)
return
}
firstServer := serverList[0]
_, _, exist = GetServerGroup(firstServer.PartnerId, firstServer.Id)
if !exist {
t.Errorf("The ServerGroup with PartnerId:%d, ServerId:%d should exist, but now it doesn't.", firstServer.PartnerId, firstServer.Id)
return
}
}
func TestResourceVersion(t *testing.T) {
list := GetResourceVersionList()
fmt.Printf("There are %d resource versions.\n", len(list))
}
func TestUserWhiteList(t *testing.T) {
fmt.Printf("There are %d whiteLists.\n", len(whiteListMap))
if len(whiteListMap) == 0 {
return
}
var partnerId int32
var userId string
for _, v := range whiteListMap {
for _, _v := range v {
partnerId = _v.PartnerId
userId = _v.UserId
break
}
break
}
expected := true
got := IsInWhiteList(partnerId, userId)
if got != expected {
t.Errorf("Expected to get %t, but got %t", expected, got)
return
}
}

View File

@@ -0,0 +1,158 @@
package managecenterMgr
import (
"encoding/json"
"errors"
"fmt"
"Framework/managecenterModel"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
var (
partnerMap = make(map[int32]*Partner, 128)
partnerHash string
)
// 重新加载合作商
func reloadPartner(isInit bool) error {
//logUtil.DebugLog("开始刷新合作商列表")
// 连接服务器,以获取数据
url := getManageCenterUrl("/API/PartnerList.ashx")
// 定义请求参数
postDict := make(map[string]string)
postDict["HashValue"] = partnerHash
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取合作商列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取合作商列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取合作商列表出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
//如果本地集合为空且数据又没变化时重新初始化一下本地hash值
if len(partnerMap) == 0 {
partnerHash = ""
}
return nil
} else {
msg := fmt.Sprintf("获取合作商列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
tmpPartnerList := make([]*Partner, 0, 128)
if data, ok := returnObj.Data.(string); !ok {
msg := "获取合作商列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpPartnerList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取合作商列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
//logUtil.DebugLog(fmt.Sprintf("刷新合作商信息结束,合作商数量:%d", len(tmpPartnerList)))
tmpPartnerMap := make(map[int32]*Partner)
for _, item := range tmpPartnerList {
tmpPartnerMap[item.Id] = item
}
// 赋值给最终的partnerMap
partnerMap = tmpPartnerMap
partnerHash = returnObj.HashValue
//通知变更
mcDataChangeCallBack(managecenterModel.PartnerData, isInit)
return nil
}
// 获取合作商对象列表
// 返回值:
// 合作商对象列表
func GetPartnerList() (partnerList []*Partner) {
for _, item := range partnerMap {
partnerList = append(partnerList, item)
}
return
}
// 根据合作商Id获取合作商对象
// id合作商Id
// 返回值:
// 合作商对象
// 是否存在
func GetPartner(id int32) (partnerObj *Partner, exist bool) {
partnerObj, exist = partnerMap[id]
return
}
// 根据合作商别名获取合作商对象
// alias:合作商别名
// 返回值:
// 合作商对象
// 是否存在
func GetPartnerByAlias(alias string) (partnerObj *Partner, exist bool) {
for _, item := range partnerMap {
if item.Alias == alias {
partnerObj = item
exist = true
return
}
}
return
}
// 获取合作商的其它配置信息
// id:合作商Id
// configKey:其它配置Key
// 返回值
// 配置内容
// 是否存在
// 错误对象
func GetOtherConfigInfo(id int32, configKey string) (configValue string, exist bool, err error) {
partnerObj, exist := GetPartner(id)
if !exist {
return
}
var otherConfigMap map[string]string
otherConfigMap, err = partnerObj.ResolveOtherConfig()
if err != nil {
return
}
configValue, exist = otherConfigMap[configKey]
return
}

View File

@@ -0,0 +1,100 @@
package managecenterMgr
import (
"encoding/json"
"errors"
"fmt"
"Framework/managecenterModel"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
var (
resourceVersionList = make([]*ResourceVersion, 128)
resourceVersionHash string
)
// 重新加载资源列表
func reloadResourceVersion(isInit bool) error {
//logUtil.DebugLog("开始刷新资源列表")
// 连接服务器,以获取数据
url := getManageCenterUrl("/API/ResourceVersionList.ashx")
// 定义请求参数
postDict := make(map[string]string)
postDict["HashValue"] = resourceVersionHash
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取资源列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取资源列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取资源列表出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
//如果本地集合为空且数据又没变化时重新初始化一下本地hash值
if len(resourceVersionList) == 0 {
resourceVersionHash = ""
}
return nil
} else {
msg := fmt.Sprintf("获取资源列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
tmpResourceVersionList := make([]*ResourceVersion, 0, 128)
if data, ok := returnObj.Data.(string); !ok {
msg := "获取资源列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpResourceVersionList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取资源列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
//logUtil.DebugLog(fmt.Sprintf("刷新资源信息结束,资源数量:%d", len(tmpResourceVersionList)))
// 赋值给最终的partnerMap
resourceVersionList = tmpResourceVersionList
resourceVersionHash = returnObj.HashValue
//通知变更
mcDataChangeCallBack(managecenterModel.ResourceVersionData, isInit)
return nil
}
// 获取资源包列表
// 返回值:
// 获取资源包列表
func GetResourceVersionList() []*ResourceVersion {
return resourceVersionList
}

View File

@@ -0,0 +1,175 @@
package managecenterMgr
import (
"encoding/json"
"errors"
"fmt"
"Framework/managecenterModel"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
var (
serverMap = make(map[int32]map[int32]*Server, 128)
serverDistinctMap = make(map[int32]*Server, 1024)
serverHash string
)
// 重新加载服务器
func reloadServer(isInit bool) error {
//logUtil.DebugLog("开始刷新服务器列表")
url := getManageCenterUrl("/API/ServerList.ashx")
// 定义请求参数
postDict := make(map[string]string)
postDict["GroupType"] = "Mix"
postDict["HashValue"] = serverHash
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取服务器列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取服务器列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取服务器列表出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
//如果本地集合为空且数据又没变化时重新初始化一下本地hash值
if len(serverMap) == 0 {
serverHash = ""
}
return nil
} else {
msg := fmt.Sprintf("获取服务器列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
tmpServerList := make([]*Server, 0, 1024)
if data, ok := returnObj.Data.(string); !ok {
msg := "获取服务器列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpServerList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取服务器列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
//logUtil.DebugLog(fmt.Sprintf("刷新服务器信息结束,服务器数量:%d", len(tmpServerList)))
tmpServerMap := make(map[int32]map[int32]*Server, 128)
tmpServerDistinctMap := make(map[int32]*Server, 1024)
for _, item := range tmpServerList {
// 构造tmpServerMap数据
if _, ok := tmpServerMap[item.PartnerId]; !ok {
tmpServerMap[item.PartnerId] = make(map[int32]*Server, 1024)
}
tmpServerMap[item.PartnerId][item.Id] = item
// 构造tmpServerDistinctMap数据
tmpServerDistinctMap[item.Id] = item
}
// 赋值给最终的serverMap、serverDistinctMap
serverMap = tmpServerMap
serverDistinctMap = tmpServerDistinctMap
serverHash = returnObj.HashValue
//通知变更
mcDataChangeCallBack(managecenterModel.ServerData, isInit)
return nil
}
// 根据服务器组Id获取对应的服务器列表
// serverGroupId:服务器组Id
// 返回值:
// 服务器列表
func GetServerListByGroupId(serverGroupId int32) (serverList []*Server) {
for _, subServerMap := range serverMap {
for _, item := range subServerMap {
if item.GroupId == serverGroupId {
serverList = append(serverList, item)
}
}
}
return
}
// 根据合作商Id获取对应的服务器列表
// partnerId:合作商Id
// 返回值:
// 服务器列表
func GetServerListByPartnerId(partnerId int32) (serverList []*Server) {
for _, item := range serverMap[partnerId] {
serverList = append(serverList, item)
}
return
}
// 根据合作商对象、服务器Id获取服务器对象
// partnerObj合作商对象
// serverId服务器Id
// 返回值:
// 服务器对象
// 是否存在
func GetServer(partnerObj *Partner, serverId int32) (serverObj *Server, exist bool) {
if subServerMap, exist1 := serverMap[partnerObj.Id]; exist1 {
serverObj, exist = subServerMap[serverId]
}
return
}
// 根据合作商Id、服务器Id获取服务器对象
// partnerId合作商Id
// serverId服务器Id
// 返回值:
// 服务器对象
// 是否存在
func GetServerItem(partnerId, serverId int32) (serverObj *Server, exist bool) {
if subServerMap, exist1 := serverMap[partnerId]; exist1 {
serverObj, exist = subServerMap[serverId]
}
return
}
// 获取不重复的服务器Id列表
// 返回值:
// 不重复的服务器Id列表
func GetDistinctServerIdList() (distinctServerIdList []int32) {
for _, item := range serverDistinctMap {
distinctServerIdList = append(distinctServerIdList, item.Id)
}
return
}

View File

@@ -0,0 +1,247 @@
package managecenterMgr
import (
"encoding/json"
"errors"
"fmt"
"sort"
"Framework/ipMgr"
"Framework/managecenterModel"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
var (
// 服务器组集合
serverGroupMap = make(map[int32]*ServerGroup, 512)
serverGroupHash string
// 服务器组变化方法集合 (完整列表,新增列表,删除列表,更新列表)
serverGroupChangeFuncMap = make(map[string]func([]*ServerGroup, []*ServerGroup, []*ServerGroup, []*ServerGroup))
)
func init() {
ipMgr.RegisterIpCheckFunc("ServerGroupIpCheck", isIpValid)
}
// 重新加载服务器组
func reloadServerGroup(isInit bool) error {
//logUtil.DebugLog("开始刷新服务器组信息")
url := getManageCenterUrl("/API/ServerGroupList.ashx")
// 定义请求参数
postDict := make(map[string]string)
postDict["GroupType"] = "Mix"
postDict["HashValue"] = serverGroupHash
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取服务器组列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取服务器组列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取服务器组列表出错,反序列化返回值出错,错误信息为:%s", err))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
//如果本地集合为空且数据又没变化时重新初始化一下本地hash值
if len(serverGroupMap) == 0 {
serverGroupHash = ""
}
return nil
} else {
msg := fmt.Sprintf("获取服务器组列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
tmpServerGroupList := make([]*ServerGroup, 0, 512)
if data, ok := returnObj.Data.(string); !ok {
msg := "获取服务器组列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpServerGroupList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取服务器组列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
//logUtil.DebugLog(fmt.Sprintf("刷新服务器组信息结束,服务器组数量:%d", len(tmpServerGroupList)))
tmpServerGroupMap := make(map[int32]*ServerGroup, 512)
for _, item := range tmpServerGroupList {
tmpServerGroupMap[item.Id] = item
}
// 处理服务器组是否变化情况
addList, deleteList, updateList := handleServerGroupChange(GetServerGroupMap(), tmpServerGroupMap)
triggerServerGroupChangeFunc(tmpServerGroupList, addList, deleteList, updateList)
// 赋值给最终的ServerGroupMap
serverGroupMap = tmpServerGroupMap
serverGroupHash = returnObj.HashValue
//通知变更
mcDataChangeCallBack(managecenterModel.ServerGroupData, isInit)
return nil
}
// 处理服务器组是否变化情况
// originalServerGroupMap: 当前服务器组集合
// newServerGroupMap: 新的服务器组集合
func handleServerGroupChange(currServerGroupMap, newServerGroupMap map[int32]*ServerGroup) (
addList []*ServerGroup, deleteList []*ServerGroup, updateList []*ServerGroup) {
// 判断是否有新增的数据
for k, v := range newServerGroupMap {
if _, exist := currServerGroupMap[k]; !exist {
addList = append(addList, v)
}
}
// 判断是否有删除的数据
for k, v := range currServerGroupMap {
if _, exist := newServerGroupMap[k]; !exist {
deleteList = append(deleteList, v)
}
}
// 判断是否有更新的数据
for k, currItem := range currServerGroupMap {
if newItem, exist := newServerGroupMap[k]; exist {
if currItem.IsEqual(newItem) == false {
updateList = append(updateList, newItem)
}
}
}
return
}
// 触发服务器组变化的方法
func triggerServerGroupChangeFunc(allList []*ServerGroup, addList []*ServerGroup, deleteList []*ServerGroup, updateList []*ServerGroup) {
// 如果有注册服务器组变化的方法
if len(serverGroupChangeFuncMap) > 0 {
for _, serverGroupChangeFunc := range serverGroupChangeFuncMap {
serverGroupChangeFunc(allList, addList, deleteList, updateList)
}
}
}
// 注册服务器组变化方法
// funcName方法名称
// serverGroupChangeFunc服务器组变化方法
func RegisterServerGroupChangeFunc(funcName string, serverGroupChangeFunc func([]*ServerGroup, []*ServerGroup, []*ServerGroup, []*ServerGroup)) {
if _, exists := serverGroupChangeFuncMap[funcName]; exists {
panic(fmt.Errorf("RegisterServerGroupChange:%s已经存在请检查", funcName))
}
serverGroupChangeFuncMap[funcName] = serverGroupChangeFunc
logUtil.DebugLog(fmt.Sprintf("注册服务器组变化方法 funcName:%s当前共有%d个注册", funcName, len(serverGroupChangeFuncMap)))
}
// 获取服务器组集合
// 返回值:
// 服务器组集合
func GetServerGroupMap() (retServerGroupMap map[int32]*ServerGroup) {
retServerGroupMap = make(map[int32]*ServerGroup, 128)
for k, v := range serverGroupMap {
retServerGroupMap[k] = v
}
return
}
// 获取服务器组列表
// 返回值:
// 服务器组列表
func GetServerGroupList() (serverGroupList []*ServerGroup) {
for _, item := range serverGroupMap {
serverGroupList = append(serverGroupList, item)
}
sort.Slice(serverGroupList, func(i, j int) bool {
return serverGroupList[i].SortByIdAsc(serverGroupList[j])
})
return
}
// 获取服务器组项
// id
// 返回值:
// 服务器组对象
// 是否存在
func GetServerGroupItem(id int32) (serverGroupObj *ServerGroup, exist bool) {
serverGroupObj, exist = serverGroupMap[id]
return
}
// 根据合作商Id、服务器Id获取服务器组对象
// partnerId合作商Id
// serverId服务器Id
// 返回值:
// 服务器组对象
// 服务器对象
// 是否存在
func GetServerGroup(partnerId, serverId int32) (serverGroupObj *ServerGroup, serverObj *Server, exist bool) {
var partnerObj *Partner
// 获取合作商对象
partnerObj, exist = GetPartner(partnerId)
if !exist {
return
}
// 获取服务器对象
serverObj, exist = GetServer(partnerObj, serverId)
if !exist {
return
}
// 获取服务器组对象
serverGroupObj, exist = GetServerGroupItem(serverObj.GroupId)
return
}
// 判断IP是否有效
// ip指定IP地址
// 返回值:
// IP是否有效
func isIpValid(ip string) (exist bool) {
for _, v := range serverGroupMap {
for _, item := range v.GetIPList() {
if ip == item {
exist = true
return
}
}
}
return
}

View File

@@ -0,0 +1,114 @@
package managecenterMgr
import (
"encoding/json"
"errors"
"fmt"
"Framework/managecenterModel"
. "Framework/managecenterModel"
"goutil/logUtil"
"goutil/webUtil"
)
var (
whiteListMap = make(map[int32]map[string]*WhiteList, 64)
whiteListHash string
)
// 重新加载白名单
func reloadWhiteList(isInit bool) error {
//logUtil.DebugLog("开始刷新白名单列表")
url := getManageCenterUrl("/API/UserWhiteList.ashx")
// 定义请求参数
postDict := make(map[string]string)
postDict["HashValue"] = whiteListHash
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 30)
statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, transport)
//statusCode, returnBytes, err := webUtil.PostMapData(url, postDict, header, nil)
if err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错url:%s,错误信息为:%s", url, err))
return err
}
if statusCode != 200 {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错url:%s,错误码为:%d", url, statusCode))
return err
}
// 解析返回值
returnObj := new(ReturnObject)
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
return err
}
// 判断返回状态是否为成功
if returnObj.Code != 0 {
// 数据没有变化,所以没有获取到新的数据,不能算错误。
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
//如果本地集合为空且数据又没变化时重新初始化一下本地hash值
if len(whiteListMap) == 0 {
whiteListHash = ""
}
return nil
} else {
msg := fmt.Sprintf("获取白名单列表出错,返回状态:%d信息为%s", returnObj.Code, returnObj.Message)
logUtil.ErrorLog(msg)
return errors.New(msg)
}
}
// 解析Data
tmpWhiteList := make([]*WhiteList, 0, 128)
if data, ok := returnObj.Data.(string); !ok {
msg := "获取白名单列表出错返回的数据不是string类型"
logUtil.ErrorLog(msg)
return errors.New(msg)
} else {
if err = json.Unmarshal([]byte(data), &tmpWhiteList); err != nil {
logUtil.ErrorLog(fmt.Sprintf("获取白名单列表出错,反序列化数据出错,错误信息为:%s", err))
return err
}
}
//logUtil.DebugLog(fmt.Sprintf("刷新白名单信息结束,白名单数量:%d", len(tmpWhiteList)))
tmpWhiteListMap := make(map[int32]map[string]*WhiteList, 64)
for _, item := range tmpWhiteList {
if _, exist := tmpWhiteListMap[item.PartnerId]; !exist {
tmpWhiteListMap[item.PartnerId] = make(map[string]*WhiteList, 8)
}
tmpWhiteListMap[item.PartnerId][item.UserId] = item
}
// 赋值给最终的whiteListMap
whiteListMap = tmpWhiteListMap
whiteListHash = returnObj.HashValue
//通知变更
mcDataChangeCallBack(managecenterModel.UserWhiteListData, isInit)
return nil
}
// 判断用户是否在白名单里面
// partnerId: 合作商ID
// userId: userId
func IsInWhiteList(partnerId int32, userId string) bool {
subWhiteListMap, exist := whiteListMap[partnerId]
if !exist {
return false
}
_, exist = subWhiteListMap[userId]
return exist
}

View File

@@ -0,0 +1,69 @@
package managecenterModel
import "goutil/stringUtil"
// 大区对象
type Area struct {
// 大区Id
AreaId int32 `json:"AreaId"`
//大区名字
AreaName string `json:"AreaName"`
//客户端显示名字
ClientName string `json:"ClientName"`
//区服范围
ServerRange string `json:"ServerRange"`
//资源包下载地址
ResourceURL string `json:"ResourceURL"`
//推荐服开关0关闭1:打开
RecommendStitch int32 `json:"RecommendStitch"`
//推荐最大注册人数
MaxRegistration int32 `json:"MaxRegistration"`
//限制注册的开关
RegRestrictionSwitch int32 `json:"RegRestrictionSwitch"`
//玩家数量大于该数量,自动开服
AutoOpenServerPlayers int32 `json:"AutoOpenServerPlayers"`
//大区维护状态1正常2维护
AreaMaintainStatus int32 `json:"AreaMaintainStatus"`
//维护消息
AreaMaintainMsg string `json:"AreaMaintainMsg"`
//大区页签集合
AreaLabelList []*AreaLabel `json:"AreaLabelList"`
}
//
//检测服务器是否在大区的区间范围
func (this *Area) CheckServerIdIsInRange(serverId int32) (isVaild bool) {
isVaild = false
for _, serverRangeItem := range stringUtil.Split(this.ServerRange, []string{","}) {
serverRange, _ := stringUtil.SplitToInt32Slice(serverRangeItem, "-")
lower := serverRange[0]
upper := serverRange[1]
//如果范围大小顺序不对,则换位
if lower > upper {
temp := lower
lower = upper
upper = temp
}
//如果服务器在该大区的任意区服区间则返回true
if serverId >= lower && serverId <= upper {
isVaild = true
return
}
}
return
}

View File

@@ -0,0 +1,45 @@
package managecenterModel
import "goutil/stringUtil"
//大区页签对象
type AreaLabel struct {
// 大区Id
AreaId int32 `json:"AreaID"`
//标签ID
LabelID int32 `json:"LabelID"`
//标签名字
LabelName string `json:"LabelName"`
//客户端显示名字
ClientName string `json:"ClientName"`
//标签区间字符串1-100,200-300
LabelServerRange string `json:"LabelServerRange"`
//渠道列表
PartnerIdList []int64 `json:"PartnerIdList"`
}
//检测服务器是否在大区页签的区间范围
func (this *AreaLabel) CheckServerIdIsInLabelRange(serverId int32) (isVaild bool) {
isVaild = false
for _, serverRangeItem := range stringUtil.Split(this.LabelServerRange, []string{","}) {
serverRange, _ := stringUtil.SplitToInt32Slice(serverRangeItem, "-")
lower := serverRange[0]
upper := serverRange[1]
//如果范围大小顺序不对,则换位
if lower > upper {
temp := lower
lower = upper
upper = temp
}
//如果服务器在该大区的任意区服区间则返回true
if serverId >= lower && serverId <= upper {
isVaild = true
return
}
}
return
}

View File

@@ -0,0 +1,44 @@
package managecenterModel
// 充值配置
type ChargeConfig struct {
// 产品Id
ProductId string
// 充值点数(以元为单位;如果是人民币是整数,但如果是美元,或者其它货币可能为小数)
ChargePoint float64
// 游戏内货币点数(元宝/钻石等,必定是整数)
GamePoint int
// 充值金额与游戏内货币的兑换比率
Ratio float64
// 赠送的游戏内货币点数(必定是整数)
GiveGamePoint int
// 赠送的比率
GiveRatio float64
// 首充时赠送的游戏内货币点数
FirstGiveGamePoint int
// 所需的vip等级
VipLv byte
// 首充时是否显示
IfFirstShow byte
// 第二次(及以后)充值时是否显示
IfSecondShow byte
// 是否为月卡
IsMonthCard bool
}
// 按照充值金额进行升序排序
// target:另一个充值配置对象
// 是否是小于
func (this *ChargeConfig) SortByChargePointAsc(target *ChargeConfig) bool {
return this.ChargePoint < target.ChargePoint
}

View File

@@ -0,0 +1,57 @@
package managecenterModel
import (
. "goutil/mysqlUtil"
. "goutil/redisUtil"
)
// 数据库连接字符串配置
type DBConnectionConfig struct {
// 模型数据库内网连接字符串
GameModelDB string
// 游戏数据库内网连接字符串
GameDB string
// 日志数据库内网连接字符串
LogDB string
// Redis连接字符串
RedisConfig string
}
// 获取游戏模型数据库连接
// 返回值:
// 数据库连接配置对象
// 错误对象
func (this *DBConnectionConfig) GetGameModelDBConn() (dbConfig *DBConfig, err error) {
dbConfig, err = NewDBConfig2(this.GameModelDB)
return
}
// 获取游戏数据库连接
// 返回值:
// 数据库连接配置对象
// 错误对象
func (this *DBConnectionConfig) GetGameDBConn() (dbConfig *DBConfig, err error) {
dbConfig, err = NewDBConfig2(this.GameDB)
return
}
// 获取游戏日志数据库连接
// 返回值:
// 数据库连接配置对象
// 错误对象
func (this *DBConnectionConfig) GetLogDBConn() (dbConfig *DBConfig, err error) {
dbConfig, err = NewDBConfig2(this.LogDB)
return
}
// 获取Redis配置
// 返回值:
// redis配置对象
// 错误对象
func (this *DBConnectionConfig) GetRedisConfig() (redisConfig *RedisConfig, err error) {
redisConfig, err = NewRedisConfig(this.RedisConfig)
return
}

View File

@@ -0,0 +1,3 @@
package managecenterModel
// 与ManageCenter共享的模型对象定义

View File

@@ -0,0 +1,10 @@
package managecenterModel
// 游戏版本
type GameVersion struct {
// Id
Id int32 `json:"GameVersionID"`
// 名称
Name string `json:"GameVersionName"`
}

View File

@@ -0,0 +1,15 @@
package managecenterModel
// 服务器状态
type MaintainType int32
const (
// 计划维护
Con_MaintainType_PlanMaintain MaintainType = 1
// 开始维护
Con_MaintainType_BeginMaintain MaintainType = 2
// 结束维护
Con_MaintainType_EndMaintain MaintainType = 3
)

View File

@@ -0,0 +1,26 @@
package managecenterModel
type MCDataType int
const (
// 服务器
ServerData MCDataType = 1
// 服务器组
ServerGroupData MCDataType = 2
// 合作商
PartnerData MCDataType = 3
// 资源
ResourceVersionData MCDataType = 4
// 白名单
UserWhiteListData MCDataType = 5
// 公告
GameNoticeData MCDataType = 6
// 大区
AreaData MCDataType = 7
)

View File

@@ -0,0 +1,11 @@
package managecenterModel
type OfficialOrTest int32
const (
// 正式服
Con_Official OfficialOrTest = 1
// 测试服
Con_Test OfficialOrTest = 2
)

View File

@@ -0,0 +1,55 @@
package managecenterModel
import (
"encoding/json"
)
// 合作商
type Partner struct {
// 合作商Id
Id int32 `json:"PartnerID"`
// 合作商名称
Name string `json:"PartnerName"`
// 合作商别名
Alias string `json:"PartnerAlias"`
// 应用Id
AppId string `json:"AppID"`
// 登陆加密Key
LoginKey string `json:"LoginKey"`
// 充值配置
ChargeConfig string `json:"ChargeConfig"`
// 其它配置
OtherConfigInfo string `json:"OtherConfigInfo"`
// 游戏版本下载Url
GameVersionUrl string `json:"GameVersionUrl"`
// 充值服务器Url
ChargeServerUrl string `json:"ChargeServerUrl"`
// 合作商类型
PartnerType int32 `json:"PartnerType"`
// 权重
Weight int32 `json:"Weight"`
// 资源包所属类型
ResourceType int32 `json:"ResourceType"`
}
// 解析其它配置信息
func (this *Partner) ResolveOtherConfig() (otherConfigMap map[string]string, err error) {
otherConfigMap = make(map[string]string, 8)
if this.OtherConfigInfo == "" {
return
}
err = json.Unmarshal([]byte(this.OtherConfigInfo), &otherConfigMap)
return
}

View File

@@ -0,0 +1,15 @@
package managecenterModel
// 合作商类型
type PartnerType int32
const (
// IOS
Con_IOS PartnerType = 0
// Android
Con_Android PartnerType = 1
// 越狱
Con_JailBreak PartnerType = 2
)

View File

@@ -0,0 +1,210 @@
package managecenterModel
import (
"fmt"
"sort"
"strconv"
"strings"
"time"
"goutil/stringUtil"
"goutil/typeUtil"
)
// 游戏版本
type ResourceVersion struct {
// 资源版本唯一标识
Id int32 `json:"ResourceVersionID"`
// 资源版本名称
Name string `json:"ResourceVersionName"`
// 资源版本的url地址
Url string `json:"ResourceVersionUrl"`
// 资源大小
Size int32 `json:"Size"`
// 资源文件MD5加密的结果
MD5 string `json:"MD5"`
//大区Id
AreaID int32 `json:"AreaID"`
// 资源生效时间
StartTime string `json:"StartTime"`
StartTimeTick int64 `json:"StartTimeTick"`
// 资源失效时间
EndTime string `json:"EndTime"`
EndTimeTick int64 `json:"EndTimeTick"`
// 添加时间
Crdate string `json:"Crdate"`
CrdateTick int64 `json:"CrdateTick"`
// 更新时间
UpdateTime string `json:"UpdateTime"`
UpdateTimeTick int64 `json:"UpdateTimeTick"`
// 是否重启客户端
IfRestart int32 `json:"IfRestart"`
// 是否禁用
IfDelete int32 `json:"IfDelete"`
// 是否审核服下载
IfAuditServiceDownload int32 `json:"IfAuditServiceDownload"`
// 资源所属的合作商ID集合
PartnerIds string `json:"PartnerIDs"`
// 资源所属的游戏版本ID集合
GameVersionIds string `json:"GameVersionIDs"`
}
// 判断资源是否包含指定合作商
// partnerId合作商Id
// 返回值
// 是否包含
func (this *ResourceVersion) ContainsPartner(partnerId int32) bool {
partnerIdList, _ := stringUtil.SplitToInt32Slice(this.PartnerIds, ",")
for _, item := range partnerIdList {
if item == partnerId {
return true
}
}
return false
}
// 判断资源是否包含指定游戏版本
// gameVersionId游戏版本Id
// 返回值
// 是否包含
func (this *ResourceVersion) ContainsGameVersion(gameVersionId int32) bool {
gameVersionIdList, _ := stringUtil.SplitToInt32Slice(this.GameVersionIds, ",")
for _, item := range gameVersionIdList {
if item == gameVersionId {
return true
}
}
return false
}
// 获取有效资源包
func GetAvailableResource(resourceVersionList []*ResourceVersion, partnerId, gameVersionId int32, resourceVersionName string, offTest OfficialOrTest) (availableResourceVersion map[string]interface{}) {
if len(resourceVersionList) == 0 {
return
}
//判断资源是否有效
_, hashCode, isVaild := IsResourceVersionNameValid(resourceVersionName)
if !isVaild {
return
}
//根据合作商Id和游戏版本Id和开始时间来过滤
var targetResourceVersionList []*ResourceVersion
for _, resourceVersion := range resourceVersionList {
startime, err := typeUtil.DateTime(resourceVersion.StartTimeTick)
if resourceVersion.ContainsPartner(partnerId) && resourceVersion.ContainsGameVersion(gameVersionId) && err == nil && startime.Before(time.Now()) {
targetResourceVersionList = append(targetResourceVersionList, resourceVersion)
}
}
if len(targetResourceVersionList) == 0 {
return
}
//组装数据
//按照资源Id进行降序排列
sort.Slice(targetResourceVersionList, func(i, j int) bool {
return targetResourceVersionList[i].SortByIdDesc(targetResourceVersionList[j])
})
//取出资源号最大的资源,如果与传入的资源名称相等,则表示没有新资源
availableResourceVersion = make(map[string]interface{}, 0)
newResource := targetResourceVersionList[0]
availableResourceVersion["ResourceVersionName"] = newResource.Name
availableResourceVersion["Url"] = strings.Replace(newResource.Url, ".zip", "", -1)
if newResource.Name == resourceVersionName {
//是否有新资源如果为fasle也需要返回project.manifest.temp.zip 用于子包验证
availableResourceVersion["IsNewResource"] = false
return
}
//判断资源号中的HashCode是否相等如果相等则表示没有新资源如果传入的timeTick>最新的timeTick说明服务器没有被刷新表示没有新资源
_, newHashCode, newIsVaild := IsResourceVersionNameValid(newResource.Name)
if !newIsVaild {
return
}
if compareRes := strings.Compare(hashCode, newHashCode); compareRes == 0 {
//是否有新资源如果为fasle也需要返回project.manifest.temp.zip 用于子包验证
availableResourceVersion["IsNewResource"] = false
return
}
if offTest == Con_Test && newResource.IfAuditServiceDownload == 0 {
return nil
}
availableResourceVersion["IsNewResource"] = true
return
}
// 按照Id进行升序排序
// target:另一个资源对象
// 是否是小于
func (this *ResourceVersion) SortByIdAsc(target *ResourceVersion) bool {
return this.Id < target.Id
}
// 按照Id进行降序排序
// target:另一个资源对象
// 是否是大于
func (this *ResourceVersion) SortByIdDesc(target *ResourceVersion) bool {
return this.Id > target.Id
}
//判断资源版本是否有效
func IsResourceVersionNameValid(resourceVersionName string) (timeTick int64, hashCode string, isValid bool) {
if len(resourceVersionName) == 0 {
isValid = false
return
}
if index := strings.Index(resourceVersionName, "_"); index == -1 {
resourceVersionName = fmt.Sprintf("0_%s", resourceVersionName)
}
resourceList := stringUtil.Split(resourceVersionName, []string{"_"})
if len(resourceList) != 2 {
isValid = false
return
}
//解析timeTick
timeTick, err := strconv.ParseInt(resourceList[0], 10, 64)
if err != nil {
isValid = false
return
}
hashCode = resourceList[1]
isValid = true
return
}

View File

@@ -0,0 +1,16 @@
package managecenterModel
// 返回结果对象
type ReturnObject struct {
// 返回的状态值0成功非0失败根据实际情况进行定义
Code int32
// 返回的失败描述信息
Message string
// 返回的数据
Data interface{}
// 返回的数据hash值
HashValue string
}

View File

@@ -0,0 +1,22 @@
package managecenterModel
// 服务器
type Server struct {
// 服务器Id
Id int32 `json:"ServerID"`
// 服务器名称
Name string `json:"ServerName"`
// 合作商Id
PartnerId int32 `json:"PartnerID"`
// 服务器组Id
GroupId int32 `json:"GroupID"`
// 对应的游戏版本号
GameVersionId int32 `json:"GameVersionID"`
// 需要的最低游戏版本号
MinGameVersionId int32 `json:"MinGameVersionID"`
}

View File

@@ -0,0 +1,184 @@
package managecenterModel
import (
"encoding/json"
"fmt"
"strings"
"time"
"goutil/stringUtil"
)
// 服务器组
type ServerGroup struct {
// 服务器组Id
Id int32 `json:"GroupID"`
// 服务器组名称
Name string `json:"GroupName"`
// 服务器组Url
Url string `json:"GroupUrl"`
// 聊天服务器Url
ChatServerUrl string `json:"ChatServerUrl"`
// 数据库连接配置
DBConnectionConfig string `json:"DBConnectionConfig"`
// 服务器组状态1正常2维护
GroupState int32 `json:"GroupState"`
// 服务器组热度1正常2新服3推荐
GroupHeat int32 `json:"GroupHeat"`
// 服务器组负载1正常2火爆
GroupLoad int32 `json:"GroupLoad"`
// 服务器开服时间对应的Unix时间戳
OpenTimeTick int64 `json:OpenTimeTick`
// 服务器组Ip(外网IP;内网IP;回调GS内网端口)
Ip string `json:"GroupIp"`
// 正式服或测试服1正式服2测试服
OfficialOrTest int32 `json:"OfficialOrTest"`
// 服务器组类型
Type int32 `json:"GroupType"`
// 服务器组排序
Order int32 `json:"GroupOrder"`
// 服务器组维护开始时间对应的时间戳
MaintainBeginTimeTick int64 `json:MaintainBeginTimeTick`
// 维护持续分钟数
MaintainMinutes int32 `json:"MaintainMinutes"`
// 维护信息
MaintainMessage string `json:"MaintainMessage"`
// 游戏监听地址
GameListenAddr string `json:"GameListenAddr"`
// 回调监听地址
CallbackListenAddr string `json:"CallbackListenAddr"`
// 外网回调地址
ExternalCallbackUrl string `json:"ExternalCallbackUrl"`
// 内网回调地址
InternalCallbackUrl string `json:"InternalCallbackUrl"`
// 是否在主群组(机房)内
IsInMainGroup bool `json:"IsInMainGroup"`
// 监控端口
GopsPort string `json:"GopsPort"`
}
// 排序方法(默认按照Id进行升序排序)
// target:另一个服务器组对象
// 是否是小于
func (this *ServerGroup) SortByIdAsc(target *ServerGroup) bool {
return this.Id < target.Id
}
// 按照开服时间进行升序排序
// target:另一个服务器组对象
// 是否是小于
func (this *ServerGroup) SortByOpenTimeAsc(target *ServerGroup) bool {
return this.OpenTimeTick < target.OpenTimeTick
}
// 获取数据库配置对象
// 返回值:
// 数据库配置对象
// 错误对象
func (this *ServerGroup) GetDBConfig() (*DBConnectionConfig, error) {
var dbConfig *DBConnectionConfig
if err := json.Unmarshal([]byte(this.DBConnectionConfig), &dbConfig); err != nil {
return nil, err
}
return dbConfig, nil
}
// 获取ip列表
// 返回值:
// ip列表
func (this *ServerGroup) GetIPList() []string {
return stringUtil.Split(this.Ip, nil)
}
// 服务器组是否开启
// 返回值:
// 是否开启
func (this *ServerGroup) IsOpen() bool {
return this.OpenTimeTick < time.Now().Unix()
}
// 获取游戏服务器的回调地址
// suffix:地址后缀
// 返回值
// 游戏服务器的回调地址
func (this *ServerGroup) GetGSCallbackUrl(suffix string) string {
// 如果是在主群组(机房)内,则使用内网地址,否则使用外网地址
url := ""
if this.IsInMainGroup {
url = this.InternalCallbackUrl
} else {
url = this.ExternalCallbackUrl
}
if url != "" {
if strings.HasSuffix(url, "/") {
return fmt.Sprintf("%s%s", url, suffix)
} else {
return fmt.Sprintf("%s/%s", url, suffix)
}
}
// 兼容旧的ManageCenter版本
ipList := this.GetIPList()
// 外网IP;内网IP;回调GS内网端口如果数量小于3则直接使用配置的GroupUrl否则使用第3个值
if len(ipList) < 3 {
if strings.HasSuffix(this.Url, "/") {
return fmt.Sprintf("%s%s", this.Url, suffix)
} else {
return fmt.Sprintf("%s/%s", this.Url, suffix)
}
} else {
return fmt.Sprintf("http://%s:%s/%s", ipList[1], ipList[2], suffix)
}
}
// 判断服务器组是否相同
// target:目标服务器组
// 是否相同
func (this *ServerGroup) IsEqual(target *ServerGroup) bool {
return this.Id == target.Id &&
this.Name == target.Name &&
this.Url == target.Url &&
this.ChatServerUrl == target.ChatServerUrl &&
this.DBConnectionConfig == target.DBConnectionConfig &&
this.GroupState == target.GroupState &&
this.GroupHeat == target.GroupHeat &&
this.GroupLoad == target.GroupLoad &&
this.OpenTimeTick == target.OpenTimeTick &&
this.Ip == target.Ip &&
this.OfficialOrTest == target.OfficialOrTest &&
this.Type == target.Type &&
this.Order == target.Order &&
this.MaintainBeginTimeTick == target.MaintainBeginTimeTick &&
this.MaintainMinutes == target.MaintainMinutes &&
this.MaintainMessage == target.MaintainMessage &&
this.GameListenAddr == target.GameListenAddr &&
this.CallbackListenAddr == target.CallbackListenAddr &&
this.ExternalCallbackUrl == target.ExternalCallbackUrl &&
this.InternalCallbackUrl == target.InternalCallbackUrl &&
this.IsInMainGroup == target.IsInMainGroup &&
this.GopsPort == target.GopsPort
}

Some files were not shown because too many files have changed in this diff Show More