登录代码提交

This commit is contained in:
tangping 2025-01-15 17:36:12 +08:00
parent 8c40855c4e
commit 22ac6c1fed
39 changed files with 2551 additions and 189 deletions

View File

@ -4,7 +4,16 @@
<option name="autoReloadType" value="ALL" /> <option name="autoReloadType" value="ALL" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="2037df1b-9ccc-4caa-9145-2d6722712612" name="更改" comment="" /> <list default="true" id="2037df1b-9ccc-4caa-9145-2d6722712612" name="更改" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/admincenter/config.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/center/admincenter/config.yaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/common/configsYaml/baseConfig.go" beforeDir="false" afterPath="$PROJECT_DIR$/center/common/configsYaml/baseConfig.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/common/configsYaml/configYaml.go" beforeDir="false" afterPath="$PROJECT_DIR$/center/common/configsYaml/configYaml.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/common/connection/dal.go" beforeDir="false" afterPath="$PROJECT_DIR$/center/common/connection/dal.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/common/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/center/common/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/common/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/center/common/go.sum" afterDir="false" />
<change beforePath="$PROJECT_DIR$/center/paycenter/internal/pay/logic.go" beforeDir="false" afterPath="$PROJECT_DIR$/center/paycenter/internal/pay/logic.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -30,40 +39,47 @@
&quot;associatedIndex&quot;: 3 &quot;associatedIndex&quot;: 3
}</component> }</component>
<component name="ProjectId" id="2qn73uMaWLMYCjjMpyatMYepw4t" /> <component name="ProjectId" id="2qn73uMaWLMYCjjMpyatMYepw4t" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="1" id="Add" />
</component>
<component name="ProjectViewState"> <component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent"><![CDATA[{
&quot;keyToString&quot;: { "keyToString": {
&quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;, "DefaultGoTemplateProperty": "Go File",
&quot;Docker.admincenter.redis: Compose 部署.executor&quot;: &quot;Run&quot;, "Docker.admincenter.redis: Compose 部署.executor": "Run",
&quot;Docker.center/admincenter/Dockerfile builder.executor&quot;: &quot;Run&quot;, "Docker.center/admincenter/Dockerfile builder.executor": "Run",
&quot;Go 构建.go build admincenter.executor&quot;: &quot;Debug&quot;, "Go 构建.go build admincenter.executor": "Run",
&quot;Go 构建.go build logincenter.executor&quot;: &quot;Debug&quot;, "Go 构建.go build logincenter.executor": "Debug",
&quot;Go 构建.go build usercenter.executor&quot;: &quot;Debug&quot;, "Go 构建.go build usercenter.executor": "Debug",
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, "Go 测试.dbcenter 中的 Test001.executor": "Run",
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;, "Go 测试.dbcenter 中的 Test002.executor": "Run",
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;, "RunOnceActivity.ShowReadmeOnStart": "true",
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;, "RunOnceActivity.go.formatter.settings.were.checked": "true",
&quot;git-widget-placeholder&quot;: &quot;master&quot;, "RunOnceActivity.go.migrated.go.modules.settings": "true",
&quot;go.import.settings.migrated&quot;: &quot;true&quot;, "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
&quot;go.sdk.automatically.set&quot;: &quot;true&quot;, "git-widget-placeholder": "master",
&quot;last_opened_file_path&quot;: &quot;D:/workspace/e2023/goProject/trunk&quot;, "go.import.settings.migrated": "true",
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;, "go.sdk.automatically.set": "true",
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, "last_opened_file_path": "D:/workspace/e2023/goProject/trunk/game/common",
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;, "node.js.detected.package.eslint": "true",
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot; "node.js.selected.package.eslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "org.intellij.sdk.editor.settings.AppSettingsConfigurable"
} }
}</component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="D:\workspace\e2023\goProject\trunk\game\common" />
<recent name="D:\workspace\e2023\goProject\trunk\game\common\model" />
<recent name="D:\workspace\e2023\goProject\trunk\center\logincenter\internal\game" /> <recent name="D:\workspace\e2023\goProject\trunk\center\logincenter\internal\game" />
<recent name="D:\workspace\e2023\goProject\trunk\center\logincenter" /> <recent name="D:\workspace\e2023\goProject\trunk\center\logincenter" />
<recent name="D:\workspace\e2023\goProject\trunk\center" /> <recent name="D:\workspace\e2023\goProject\trunk\center" />
</key> </key>
</component> </component>
<component name="RunManager" selected="Go 构建.go build usercenter"> <component name="RunManager" selected="Go 测试.dbcenter 中的 Test002">
<configuration name="go build admincenter" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true"> <configuration name="go build admincenter" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="trunk" /> <module name="trunk" />
<working_directory value="$PROJECT_DIR$/center/admincenter" /> <working_directory value="$PROJECT_DIR$/center/admincenter" />
@ -73,13 +89,26 @@
<filePath value="$PROJECT_DIR$/center/admincenter/main.go" /> <filePath value="$PROJECT_DIR$/center/admincenter/main.go" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="go build usercenter" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true"> <configuration name="dbcenter 中的 Test001" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="trunk" /> <module name="trunk" />
<working_directory value="$PROJECT_DIR$/center/usercenter" /> <working_directory value="$PROJECT_DIR$/center/dbcenter" />
<kind value="PACKAGE" /> <kind value="PACKAGE" />
<package value="logincenter" /> <package value="dbcenter" />
<directory value="$PROJECT_DIR$" /> <directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/center/logincenter/main.go" /> <filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<pattern value="^\QTest001\E$" />
<method v="2" />
</configuration>
<configuration name="dbcenter 中的 Test002" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="trunk" />
<working_directory value="$PROJECT_DIR$/center/dbcenter" />
<kind value="PACKAGE" />
<package value="dbcenter" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<pattern value="^\QTest002\E$" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration default="true" type="docker-deploy" factoryName="docker-compose.yml" temporary="true"> <configuration default="true" type="docker-deploy" factoryName="docker-compose.yml" temporary="true">
@ -119,10 +148,11 @@
</configuration> </configuration>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Go 构建.go build usercenter" /> <item itemvalue="Go 测试.dbcenter 中的 Test002" />
<item itemvalue="Go 测试.dbcenter 中的 Test001" />
<item itemvalue="Go 构建.go build admincenter" /> <item itemvalue="Go 构建.go build admincenter" />
<item itemvalue="Docker.admincenter.redis: Compose 部署" />
<item itemvalue="Docker.center/admincenter/Dockerfile builder" /> <item itemvalue="Docker.center/admincenter/Dockerfile builder" />
<item itemvalue="Docker.admincenter.redis: Compose 部署" />
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>

Binary file not shown.

View File

@ -9,6 +9,12 @@ root:
# Elasticsearch 地址 # Elasticsearch 地址
es_urls: "http://10.252.0.70:18099" es_urls: "http://10.252.0.70:18099"
# RabbitMQ 配置
rabbitmq_config: "amqp://guest:guest@localhost:5672/"
# mq队列名称
mq_queue_name: "admin_center"
# 数据库配置 # 数据库配置
db_config: db_config:
admin_db: admin_db:
@ -29,7 +35,7 @@ root:
max_idle_conns: 0 max_idle_conns: 0
# 数据库连接字符串 # 数据库连接字符串
connection_string: "root:Qq5201530300@tcp(192.168.50.110:3306)/pay?charset=utf8&parseTime=true&loc=Local&timeout=30s&multiStatements=true" connection_string: "root:Qq5201530300@tcp(192.168.50.110:3306)/user?charset=utf8&parseTime=true&loc=Local&timeout=30s&multiStatements=true"
redis_config: redis_config:
# 数据库连接字符串 # 数据库连接字符串

View File

@ -36,6 +36,12 @@ var (
EsUrls string EsUrls string
BaseDay int BaseDay int
// Rabbitmq 配置
Rabbitmq string
// RabbitMQName mq队列名称
RabbitMQName string
) )
// initBaseConfig // initBaseConfig
@ -69,6 +75,11 @@ func initBaseConfig() {
EsUrls = root.EsUrls EsUrls = root.EsUrls
BaseDay = root.BaseDay BaseDay = root.BaseDay
// rabbitmq配置
Rabbitmq = root.RabbitMQAddress
RabbitMQName = root.RabbitMQName
} }
// GetDebug // GetDebug
@ -77,14 +88,14 @@ func initBaseConfig() {
// return: // return:
// @bool: // @bool:
func GetDebug() bool { func GetDebug() bool {
return ConfigYaml.Root.Debug return DEBUG
} }
// GetWebServerAddress 返回Web服务器的地址。 // GetWebServerAddress 返回Web服务器的地址。
// 该函数通过访问ConfigYaml中的配置信息获取并返回Web服务器的地址。 // 该函数通过访问ConfigYaml中的配置信息获取并返回Web服务器的地址。
// 主要用于需要与Web服务器建立连接的场景。 // 主要用于需要与Web服务器建立连接的场景。
func GetWebServerAddress() string { func GetWebServerAddress() string {
return ConfigYaml.Root.WebServerAddress return WebServerAddress
} }
// GetEsUrls 返回配置文件中 Elasticsearch 的 URL 地址。 // GetEsUrls 返回配置文件中 Elasticsearch 的 URL 地址。
@ -93,5 +104,13 @@ func GetWebServerAddress() string {
// 并将其作为字符串返回。这提供了一种简单的方法来获取 Elasticsearch 数据库的连接信息, // 并将其作为字符串返回。这提供了一种简单的方法来获取 Elasticsearch 数据库的连接信息,
// 而无需直接访问配置文件。 // 而无需直接访问配置文件。
func GetEsUrls() string { func GetEsUrls() string {
return ConfigYaml.Root.EsUrls return EsUrls
}
func GetRabbitMQAddress() string {
return Rabbitmq
}
func GetRabbitMQName() string {
return RabbitMQName
} }

View File

@ -43,6 +43,12 @@ type Root struct {
// ES 地址 // ES 地址
EsUrls string `yaml:"es_urls"` EsUrls string `yaml:"es_urls"`
// rabbitMQ 地址
RabbitMQAddress string `yaml:"rabbitmq_address"`
// mq队列名称
RabbitMQName string `yaml:"mq_queue_name"`
// 数据库配置 // 数据库配置
DbConfig DBConfig `yaml:"db_config"` DbConfig DBConfig `yaml:"db_config"`

View File

@ -92,12 +92,12 @@ func initMysql(dbConfig *mysqlUtil.DBConfig) *gorm.DB {
if dbConfig == nil || dbConfig.ConnectionString == "" { if dbConfig == nil || dbConfig.ConnectionString == "" {
return nil return nil
} }
logUtilPlus.WarnLog("开始连接mysql:%s", dbConfig.ConnectionString) logUtilPlus.DebugLog("开始连接mysql:%s", dbConfig.ConnectionString)
dbObj, err := gorm.Open(mysql.Open(dbConfig.ConnectionString), &gorm.Config{}) dbObj, err := gorm.Open(mysql.Open(dbConfig.ConnectionString), &gorm.Config{PrepareStmt: true})
if err != nil { if err != nil {
panic(fmt.Errorf("初始化数据库:%s失败错误信息为%s", dbConfig.ConnectionString, err)) panic(fmt.Errorf("初始化数据库:%s失败错误信息为%s", dbConfig.ConnectionString, err))
} }
logUtilPlus.WarnLog("连接mysql:%s成功", dbConfig.ConnectionString) logUtilPlus.DebugLog("连接mysql:%s成功", dbConfig.ConnectionString)
return dbObj return dbObj
} }
@ -126,7 +126,7 @@ func initRedis(redisConfig *redisUtil.RedisConfig) *goredis.Client {
panic(fmt.Errorf("ping->redis:%s失败,DB:%s错误信息为%s", redisConfig.ConnectionString, redisConfig.Database, err)) panic(fmt.Errorf("ping->redis:%s失败,DB:%s错误信息为%s", redisConfig.ConnectionString, redisConfig.Database, err))
} }
logUtilPlus.WarnLog("ping->redis:%s成功信息为%s", redisConfig.ConnectionString, ping) logUtilPlus.DebugLog("ping->redis:%s成功信息为%s", redisConfig.ConnectionString, ping)
return client return client
} }

View File

@ -12,6 +12,8 @@ replace (
require ( require (
framework v0.0.0-20230425160006-b2d0b0a0b0b0 framework v0.0.0-20230425160006-b2d0b0a0b0b0
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
github.com/streadway/amqp v1.1.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.7 gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12 gorm.io/gorm v1.25.12
goutil v0.0.0-20230425160006-b2d0b0a0b0b0 goutil v0.0.0-20230425160006-b2d0b0a0b0b0
@ -24,6 +26,8 @@ require (
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.15.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/gomodule/redigo v1.8.9 // indirect github.com/gomodule/redigo v1.8.9 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jinzhu/gorm v1.9.12 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect

View File

@ -1,109 +1,47 @@
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/go.mod h1:mdtqvCSg8JOxk8PmpTNGyo6wzd4BMm4QXSfDnTXmgkE=
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/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
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 h1:OoL469zqSNrTLSz5zeVF/I6VOO7fiw2bzSzQe4J557c=
github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4= 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/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
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/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 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 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
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-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.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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= 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/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
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/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/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 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 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 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 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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.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 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@ -111,115 +49,40 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
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/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/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.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.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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 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/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
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/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-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-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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/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-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-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 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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-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-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.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 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.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/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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/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/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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 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/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
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.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
@ -227,5 +90,3 @@ gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkD
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
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,54 @@
package rebbitMQ
import (
configYaml "common/configsYaml"
"github.com/streadway/amqp"
"goutil/logUtil"
)
var RabbitMQConn *amqp.Connection
var RabbitMQChannel *amqp.Channel
// 初始化rabbitMQ
func init() {
rabbitMQAddress := configYaml.GetRabbitMQAddress()
//是否有mq配置
if rabbitMQAddress == "" {
return
}
// 连接到 RabbitMQ 服务器
var err error
RabbitMQConn, err = amqp.Dial(rabbitMQAddress)
if err != nil {
//抛出一个异常
logUtil.FatalLog("Failed to connect to RabbitMQ: %serr:%s", rabbitMQAddress, err.Error())
}
// 打开一个通道
RabbitMQChannel, err = RabbitMQConn.Channel()
if err != nil {
//抛出一个异常
logUtil.FatalLog("Failed to open a channelerr:%s", err.Error())
}
//队列名称
queueName := configYaml.GetRabbitMQName()
// 声明一个队列
_, err = RabbitMQChannel.QueueDeclare(
queueName, // 队列名称
true, // 是否持久化
false, // 是否在使用后删除
false, // 是否排他
false, // 是否阻塞
nil, // 其他参数
)
if err != nil {
logUtil.FatalLog("Failed to declare a queuequeueName:%s,err:%s", queueName, err.Error())
}
}

View File

@ -0,0 +1,25 @@
package rebbitMQ
import (
configYaml "common/configsYaml"
"github.com/streadway/amqp"
"goutil/logUtilPlus"
)
// 发送单挑mq消息
func sendMqData(data string) {
// 发布消息到队列
err := RabbitMQChannel.Publish(
"", // 交换机名称
configYaml.GetRabbitMQName(), // 路由键
false, // 是否强制
false, // 是否立即
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(data),
})
if err != nil {
logUtilPlus.ErrorLog("Failed to publish a messageerr:%s", err.Error())
}
}

View File

@ -0,0 +1,104 @@
package main
import (
"log"
"testing"
amqp "github.com/streadway/amqp"
)
// failOnError 检查错误并记录致命错误
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
// 测试函数
func Test001(t *testing.T) {
// 连接到 RabbitMQ 服务器
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "连接到 RabbitMQ 失败")
defer conn.Close()
// 打开一个通道
ch, err := conn.Channel()
failOnError(err, "打开通道失败")
defer ch.Close()
// 声明一个队列
q, err := ch.QueueDeclare(
"hello", // 队列名称
true, // 是否持久化
false, // 是否在使用后删除
false, // 是否排他
false, // 是否阻塞
nil, // 其他参数
)
failOnError(err, "声明队列失败")
// 注册一个消费者
msgs, err := ch.Consume(
q.Name, // 队列名称
"", // 消费者名称
true, // 是否自动确认
false, // 是否排他
false, // 是否本地
false, // 是否阻塞
nil, // 其他参数
)
failOnError(err, "注册消费者失败")
// 创建一个无缓冲的通道,用于阻塞主 goroutine
forever := make(chan bool)
// 启动一个 goroutine 来处理消息
go func() {
for d := range msgs {
log.Printf("收到消息: %s", d.Body)
}
}()
log.Printf(" [*] 等待消息。按 CTRL+C 退出")
<-forever
}
func Test002(t *testing.T) {
// 连接到 RabbitMQ 服务器
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "连接到 RabbitMQ 失败")
defer conn.Close()
// 打开一个通道
ch, err := conn.Channel()
failOnError(err, "打开通道失败")
defer ch.Close()
// 声明一个队列
q, err := ch.QueueDeclare(
"hello", // 队列名称
true, // 是否持久化
false, // 是否在使用后删除
false, // 是否排他
false, // 是否阻塞
nil, // 其他参数
)
failOnError(err, "声明队列失败")
// 定义要发送的消息内容
body := "Hello World!1111"
// 发布消息到队列
err = ch.Publish(
"", // 交换机名称
q.Name, // 路由键
false, // 是否强制
false, // 是否立即
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})
failOnError(err, "发布消息失败")
log.Printf(" [x] 发送 %s", body)
}

View File

@ -0,0 +1,5 @@
module dbcenter
go 1.22.10
require github.com/streadway/amqp v1.1.0 // indirect

View File

@ -0,0 +1,2 @@
github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=

View File

@ -0,0 +1,5 @@
package main
func main() {
}

View File

@ -47,8 +47,8 @@ func GetUserByID(orderID int64) (*Order, error) {
return order, nil return order, nil
} }
// AddUserCache 添加用户缓存 // AddOrderCache 添加用户缓存
func AddUserCache(order *Order) { func AddOrderCache(order *Order) {
rwmu.Lock() rwmu.Lock()
defer rwmu.Unlock() defer rwmu.Unlock()
orderMap[order.OrderID] = order orderMap[order.OrderID] = order
@ -65,5 +65,8 @@ func AddOrder(order *Order) (int64, error) {
logUtilPlus.ErrorLog("添加支付订单失败 错误信息:", result.Error.Error()) logUtilPlus.ErrorLog("添加支付订单失败 错误信息:", result.Error.Error())
} }
//添加缓存
AddOrderCache(order)
return order.OrderID, nil return order.OrderID, nil
} }

View File

@ -0,0 +1,109 @@
package clientMgr
import (
"encoding/json"
"sync"
"goutil/debugUtil"
"goutil/logUtil"
)
var (
clientMap = make(map[int32]IClient, 1024)
mutex sync.RWMutex
)
func RegisterClient(clientObj IClient) {
mutex.Lock()
defer mutex.Unlock()
clientMap[clientObj.GetId()] = clientObj
}
func UnregisterClient(clientObj IClient) {
mutex.Lock()
defer mutex.Unlock()
delete(clientMap, clientObj.GetId())
}
func GetClient(id int32) (clientObj IClient, exists bool) {
mutex.RLock()
defer mutex.RUnlock()
clientObj, exists = clientMap[id]
return
}
func getClientCount() int {
mutex.RLock()
defer mutex.RUnlock()
return len(clientMap)
}
func getExpiredClientList() (expiredList []IClient) {
mutex.RLock()
defer mutex.RUnlock()
for _, item := range clientMap {
if item.Expired() {
expiredList = append(expiredList, item)
}
}
return
}
// 绑定连接到玩家
func BindClientAndPlayer(clientObj IClient, playerObj *Player) {
// 发送在另一台设备登陆的信息
sendLoginAnotherDeviceMsg := func(clientObj IClient) {
responseObj := NewServerResponseObject()
responseObj.SetMethodName("Login")
responseObj.SetResultStatus(LoginOnAnotherDevice)
mes, _ := json.Marshal(responseObj)
if debugUtil.IsDebug() {
logUtil.WarnLog("BindClientAndPlayer11BindClientAndPlayer:%v", string(mes))
}
// 先发送消息,然后再断开连接
clientObj.SendMessage(responseObj)
clientObj.SendMessage(NewDisconnectServerResponseObject())
}
// 立即更新活跃时间,避免在将要清除时,玩家又登陆的极端情况
clientObj.Active()
clientObj.ClearSendData()
// 判断是否重复登陆
if playerObj.ClientId > 0 {
if oldClientObj, exist := GetClient(playerObj.ClientId); exist {
// 如果不是同一个客户端,则先给客户端发送在其他设备登陆信息,然后断开连接
if clientObj != oldClientObj {
sendLoginAnotherDeviceMsg(oldClientObj)
}
}
}
// 更新客户端对象的玩家Id、以及玩家对象对应的客户端Id
clientObj.PlayerLogin(playerObj.Id)
playerObj.ClientLogin(clientObj.GetId())
}
// Disconnect 清理断开的连接数据
// / 连接对象
func Disconnect(clientObj IClient) {
if clientObj.GetPlayerId() != 0 {
playerObj, exist, err := getPlayerHandler(clientObj.GetPlayerId(), false)
if err == nil && exist && clientObj.GetId() == playerObj.ClientId {
playerObj.ClientLogout()
}
}
clientObj.Close()
UnregisterClient(clientObj)
}

View File

@ -0,0 +1,41 @@
package clientMgr
import (
"time"
"framework/goroutineMgr"
"goutil/logUtil"
)
func init() {
// 清理过期的客户端
go clearExpiredClient()
}
// 清理过期的客户端
func clearExpiredClient() {
// 处理goroutine数量
goroutineName := "clientMgr.clearExpiredClient"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
for {
// 休眠指定的时间(单位:秒)(放在此处是因为程序刚启动时并没有过期的客户端所以先不用占用资源并且此时LogPath尚未设置如果直接执行后面的代码会出现panic异常)
time.Sleep(5 * time.Minute)
// 获取过期的客户端列表
expiredClientList := getExpiredClientList()
expiredClientCount := len(expiredClientList)
beforeClientCount := getClientCount()
// 客户端断开
if expiredClientCount > 0 {
for _, item := range expiredClientList {
Disconnect(item)
}
}
// 记录日志
logUtil.DebugLog("清理前的客户端数量为:%d本次清理不活跃的客户端数量为%d", beforeClientCount, expiredClientCount)
}
}

View File

@ -0,0 +1,86 @@
package clientMgr
import (
"encoding/json"
. "common/model"
"goutil/debugUtil"
"goutil/logUtil"
)
// HandleRequest 处理客户端请求
// clientObj对应的客户端对象
// request请求内容字节数组(json格式)
// 返回值:无
func HandleRequest(clientObj IClient, request []byte) {
responseObj := NewServerResponseObject()
defer func() {
// 如果是客户端数据错误,则将客户端请求数据记录下来
if responseObj.ResultStatus == ClientDataError {
logUtil.ErrorLog("client:%s\n请求的数据为%s, 返回的结果为客户端数据错误", clientObj, string(request))
}
// 调用发送消息接口
clientObj.SendMessage(responseObj)
// 记录DEBUG日志
if debugUtil.IsDebug() {
result, _ := json.Marshal(responseObj)
logUtil.DebugLog("client:%s\nRequest:%s\nResponse:%s", clientObj, string(request), string(result))
}
}()
// 解析请求字符串
requestObj := new(ServerRequestObject)
if err := json.Unmarshal(request, requestObj); err != nil {
logUtil.ErrorLog("反序列化出错,字符串:%s错误信息为%s", string(request), err)
responseObj.SetResultStatus(ClientDataError)
return
}
// 为参数赋值
responseObj.SetMethodName(requestObj.MethodName)
// 对参数要特殊处理将Client、Player特殊处理
parameters := make([]interface{}, 0)
if requestObj.MethodName == "Login" {
parameters = append(parameters, interface{}(clientObj))
parameters = append(parameters, requestObj.Parameters...)
} else {
// 判断玩家是否已经登陆
if clientObj.GetPlayerId() == 0 {
responseObj.SetResultStatus(NoLogin)
return
}
// 判断是否能找到玩家
var playerObj *Player
var exists bool
var err error
if getPlayerHandler == nil {
panic("getPlayerHandler is nil, please set first")
}
playerObj, exists, err = getPlayerHandler(clientObj.GetPlayerId(), false)
if err != nil {
responseObj.SetResultStatus(DataError)
return
}
if !exists {
responseObj.SetResultStatus(NoLogin)
return
}
parameters = append(parameters, interface{}(clientObj))
parameters = append(parameters, interface{}(playerObj))
parameters = append(parameters, requestObj.Parameters...)
}
// 为参数赋值
requestObj.ModuleName = "Chat"
requestObj.Parameters = parameters
// 调用方法
responseObj = callFunction(requestObj)
}

View File

@ -0,0 +1,33 @@
package clientMgr
import . "common/model"
// IClient 客户端连接接口
type IClient interface {
// GetId 获取客户端对象的唯一标识
GetId() int32
// 获取玩家Id
GetPlayerId() int64
// 玩家登录
PlayerLogin(int64)
// 发送数据
SendMessage(*ServerResponseObject)
// 清空待发送数据
ClearSendData()
// 客户端活跃
Active()
// 获取远程地址IP_Port
GetRemoteAddr() string
// 客户端连接超时
Expired() bool
// 客户端连接对象断开
Close()
}

View File

@ -0,0 +1,25 @@
package clientMgr
import (
"reflect"
)
// 反射的方法和输入、输出参数类型组合类型
type methodAndInOutTypes struct {
// 反射出来的对应方法对象
Method reflect.Value
// 反射出来的方法的输入参数的类型集合
InTypes []reflect.Type
// 反射出来的方法的输出参数的类型集合
OutTypes []reflect.Type
}
func newmethodAndInOutTypes(_method reflect.Value, _inTypes []reflect.Type, _outTypes []reflect.Type) *methodAndInOutTypes {
return &methodAndInOutTypes{
Method: _method,
InTypes: _inTypes,
OutTypes: _outTypes,
}
}

View File

@ -0,0 +1,356 @@
package clientMgr
import (
"fmt"
"reflect"
"strings"
. "common/model"
"goutil/debugUtil"
"goutil/logUtil"
)
const (
// 供客户端访问的模块的后缀
con_ModuleSuffix = "Module"
// 定义用于分隔模块名称和方法名称的分隔符
con_DelimeterOfObjAndMethod = "_"
)
var (
// 定义存放所有方法映射的变量
methodMap = make(map[string]*methodAndInOutTypes)
)
// 获取结构体类型的名称
// structType结构体类型
// 返回值:
// 结构体类型的名称
func getStructName(structType reflect.Type) string {
reflectTypeStr := structType.String()
reflectTypeArr := strings.Split(reflectTypeStr, ".")
return reflectTypeArr[len(reflectTypeArr)-1]
}
// 获取完整的模块名称
// moduleName模块名称
// 返回值:
// 完整的模块名称
func getFullModuleName(moduleName string) string {
return moduleName + con_ModuleSuffix
}
// 获取完整的方法名称
// structName结构体名称
// methodName方法名称
// 返回值:
// 完整的方法名称
func getFullMethodName(structName, methodName string) string {
return structName + con_DelimeterOfObjAndMethod + methodName
}
// 解析方法的输入输出参数
// method方法对应的反射值
// 返回值:
// 输入参数类型集合
// 输出参数类型集合
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
}
// 将需要对客户端提供方法的对象进行注册
// structObject对象
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
outType := outTypes[0]
if _, ok := outType.FieldByName("Code"); !ok {
continue
}
if _, ok := outType.FieldByName("Message"); !ok {
continue
}
if _, ok := outType.FieldByName("Data"); !ok {
continue
}
// 添加到列表中
methodMap[getFullMethodName(structName, methodName)] = newmethodAndInOutTypes(method, inTypes, outTypes)
debugUtil.Println(fmt.Sprintf("%s_%s注册成功,当前共%d个方法", structName, methodName, len(methodMap)))
}
}
// 调用方法
// requestObj请求对象
func callFunction(requestObj *ServerRequestObject) *ServerResponseObject {
responseObj := NewServerResponseObject()
var methodAndInOutTypes *methodAndInOutTypes
var exists bool
// 根据传入的ModuleName和MethodName找到对应的方法对象
key := getFullMethodName(getFullModuleName(requestObj.ModuleName), requestObj.MethodName)
if methodAndInOutTypes, exists = methodMap[key]; !exists {
logUtil.ErrorLog("找不到指定的方法:%v,%s", methodMap, key)
return responseObj.SetResultStatus(NoTargetMethod)
}
// 判断参数数量是否相同
inTypesLength := len(methodAndInOutTypes.InTypes)
paramLength := len(requestObj.Parameters)
if paramLength != inTypesLength {
logUtil.ErrorLog("传入的参数数量不符,本地方法%s的参数数量%d传入的参数数量为%d", key, inTypesLength, paramLength)
return responseObj.SetResultStatus(ParamNotMatch)
}
// 构造参数
in := make([]reflect.Value, inTypesLength)
for i := 0; i < inTypesLength; i++ {
inTypeItem := methodAndInOutTypes.InTypes[i]
paramItem := requestObj.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.Interface:
if param_client, ok := paramItem.(IClient); ok {
in[i] = reflect.ValueOf(param_client)
}
case reflect.Ptr:
if param_player, ok := paramItem.(*Player); ok {
in[i] = reflect.ValueOf(param_player)
}
case reflect.Bool:
if param_bool, ok := paramItem.(bool); ok {
in[i] = reflect.ValueOf(param_bool)
}
case reflect.Int:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(int(param_float64))
}
case reflect.Int8:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(int8(param_float64))
}
case reflect.Int16:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(int16(param_float64))
}
case reflect.Int32:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(int32(param_float64))
}
case reflect.Int64:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(int64(param_float64))
}
case reflect.Uint:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(uint(param_float64))
}
case reflect.Uint8:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(uint8(param_float64))
}
case reflect.Uint16:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(uint16(param_float64))
}
case reflect.Uint32:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(uint32(param_float64))
}
case reflect.Uint64:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(uint64(param_float64))
}
case reflect.Float32:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(float32(param_float64))
}
case reflect.Float64:
if param_float64, ok := paramItem.(float64); ok {
in[i] = reflect.ValueOf(param_float64)
}
case reflect.String:
if param_string, ok := paramItem.(string); ok {
in[i] = reflect.ValueOf(param_string)
}
case reflect.Slice:
// 如果是Slice类型则需要对其中的项再次进行类型判断及类型转换
if param_interface, ok := paramItem.([]interface{}); ok {
switch inTypeItem.String() {
case "[]bool":
params_inner := make([]bool, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_bool, ok := param_interface[i].(bool); ok {
params_inner[i] = param_bool
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]int":
params_inner := make([]int, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = int(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]int8":
params_inner := make([]int8, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = int8(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]int16":
params_inner := make([]int16, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = int16(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]int32":
params_inner := make([]int32, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = int32(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]int64":
params_inner := make([]int64, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = int64(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]uint":
params_inner := make([]uint, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = uint(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
// case "[]uint8": 特殊处理
case "[]uint16":
params_inner := make([]uint16, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = uint16(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]uint32":
params_inner := make([]uint32, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = uint32(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]uint64":
params_inner := make([]uint64, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = uint64(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]float32":
params_inner := make([]float32, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = float32(param_float64)
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]float64":
params_inner := make([]float64, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_float64, ok := param_interface[i].(float64); ok {
params_inner[i] = param_float64
}
}
in[i] = reflect.ValueOf(params_inner)
case "[]string":
params_inner := make([]string, len(param_interface))
for i := 0; i < len(param_interface); i++ {
if param_string, ok := param_interface[i].(string); ok {
params_inner[i] = param_string
}
}
in[i] = reflect.ValueOf(params_inner)
}
} else if inTypeItem.String() == "[]uint8" { // 由于[]uint8在传输过程中会被转化成字符串所以单独处理;
if param_string, ok := paramItem.(string); ok {
param_uint8 := ([]uint8)(param_string)
in[i] = reflect.ValueOf(param_uint8)
}
}
}
}
// 判断是否有无效的参数(传入的参数类型和方法定义的类型不匹配导致没有赋值)
for _, item := range in {
if reflect.Value.IsValid(item) == false {
logUtil.ErrorLog("type:%v,value:%v.方法%s传入的参数%v无效", reflect.TypeOf(item), reflect.ValueOf(item), key, requestObj.Parameters)
return responseObj.SetResultStatus(ParamInValid)
}
}
// 传入参数,调用方法
out := methodAndInOutTypes.Method.Call(in)
// 由于只有一个返回值所以取out[0]
if retResponseObj, ok := (out[0]).Interface().(ServerResponseObject); ok {
responseObj.SetMethodName(requestObj.MethodName)
responseObj.SetResultStatus(retResponseObj.ResultStatus)
responseObj.SetData(retResponseObj.Data)
} else {
logUtil.ErrorLog("(&out[0]).Interface()转换类型失败")
}
return responseObj
}

View File

@ -0,0 +1,101 @@
package config
import (
"encoding/json"
"fmt"
"goutil/configUtil"
"goutil/debugUtil"
)
// 基础配置对象
type BaseConfig struct {
// ChatCenter监听地址
ChatCenterAddress string
// ManageCenter的API的域名地址
ManageCenterDomain string
// 通信协议tcp/websocket
Protocol string
// 内网地址
PrivateIP string
// 公网地址
PublicIP string
// 为ChatServer提价服务的端口
ChatServerPort string
// 为GameServer提供服务的端口
GameServerPort string
// 为GameServer提供服务的Web端口
GameServerWebPort string
// 是否压缩返回给客户端的数据
IfCompressData bool
// GoPs监控程序监听地址
GopsAddr string
}
func (this *BaseConfig) GetPrivateChatServerAddress() string {
return fmt.Sprintf("%s:%s", this.PrivateIP, this.ChatServerPort)
}
func (this *BaseConfig) GetPrivateGameServerAddress() string {
return fmt.Sprintf("%s:%s", this.PrivateIP, this.GameServerPort)
}
func (this *BaseConfig) GetPrivateGameServerWebAddress() string {
return fmt.Sprintf("%s:%s", this.PrivateIP, this.GameServerWebPort)
}
func (this *BaseConfig) GetPrivateGopsAddress() string {
return fmt.Sprintf("%s:%s", this.PrivateIP, this.GopsAddr)
}
func (this *BaseConfig) GetPublicChatServerAddress() string {
return fmt.Sprintf("%s:%s", this.PublicIP, this.ChatServerPort)
}
func (this *BaseConfig) GetPublicGameServerAddress() string {
return fmt.Sprintf("%s:%s", this.PublicIP, this.GameServerPort)
}
func (this *BaseConfig) GetPublicGameServerWebAddress() string {
return fmt.Sprintf("http://%s:%s/API/player/login", this.PublicIP, this.GameServerWebPort)
}
func (this *BaseConfig) String() string {
bytes, _ := json.Marshal(this)
return string(bytes)
}
var (
baseConfig *BaseConfig
)
func initBaseConfig(config *configUtil.XmlConfig) error {
tempConfig := new(BaseConfig)
err := config.Unmarshal("root/BaseConfig", tempConfig)
if err != nil {
return err
}
baseConfig = tempConfig
debugUtil.Printf("baseConfig:%v\n", baseConfig)
if baseConfig.Protocol != "tcp" && baseConfig.Protocol != "websocket" {
panic("Protocol Error, it should be either tcp or websocket")
}
return nil
}
// GetBaseConfig 获取服务器基础配置
func GetBaseConfig() *BaseConfig {
return baseConfig
}

View File

@ -0,0 +1,56 @@
package config
import (
"fmt"
"goutil/configUtil"
"goutil/debugUtil"
"goutil/logUtil"
)
var (
configManager = configMgr.NewConfigManager()
)
func init() {
// 优先加基础配置
configManager.RegisterInitFunc(initBaseConfig)
configManager.RegisterInitFunc(initDBConfig)
configManager.RegisterInitFunc(initMonitorConfig)
}
func init() {
// 设置日志文件的存储目录
logUtil.SetLogPath("LOG")
if err := reload(); err != nil {
panic(fmt.Errorf("初始化配置文件失败,错误信息为:%s", err))
}
// 注册重新加载的方法
reloadMgr.RegisterReloadFunc("config.reload", reload)
}
func reload() error {
// 读取配置文件内容
configObj := configUtil.NewXmlConfig()
err := configObj.LoadFromFile("config.xml")
if err != nil {
return err
}
debug, err := configObj.Bool("root/DEBUG", "")
if err != nil {
return err
}
// 设置debugUtil的状态
debugUtil.SetDebug(debug)
// 调用所有已经注册的配置初始化方法
if err := configManager.Init(configObj); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,29 @@
package config
import (
"goutil/configUtil"
"goutil/debugUtil"
"goutil/mysqlUtil"
)
var (
dbConfig *mysqlUtil.DBConfig
)
func initDBConfig(config *configUtil.XmlConfig) error {
tempConfig := new(mysqlUtil.DBConfig)
err := config.Unmarshal("root/DBConnection", tempConfig)
if err != nil {
return err
}
dbConfig = tempConfig
debugUtil.Printf("dbConfig:%v\n", dbConfig)
return nil
}
// GetDBConfig 获取mysql数据库配置
func GetDBConfig() *mysqlUtil.DBConfig {
return dbConfig
}

View File

@ -0,0 +1,27 @@
package config
import (
"goutil/configUtil"
"goutil/debugUtil"
)
var (
monitorConfig *monitorMgr.MonitorConfig
)
func initMonitorConfig(config *configUtil.XmlConfig) error {
tempConfig := new(monitorMgr.MonitorConfig)
err := config.Unmarshal("root/MonitorConfig", tempConfig)
if err != nil {
return err
}
monitorConfig = tempConfig
debugUtil.Printf("monitorConfig:%v\n", monitorConfig)
return nil
}
// GetMonitorConfig 获监测配置
func GetMonitorConfig() *monitorMgr.MonitorConfig {
return monitorConfig
}

13
trunk/game/common/go.mod Normal file
View File

@ -0,0 +1,13 @@
module common
go 1.22.10
replace (
framework => ../../framework
goutil => ../../goutil
)
require (
framework v0.0.0-20230425160006-b2d0b0a0b0b0
goutil v0.0.0-20230425160006-b2d0b0a0b0b0
)

6
trunk/game/common/go.sum Normal file
View File

@ -0,0 +1,6 @@
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/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=

View File

@ -0,0 +1,63 @@
package model
// 返回结果状态
type ResultStatus struct {
// 状态值(成功是0非成功以负数来表示)
Code int
// 状态信息
Message string
}
func newResultStatus(code int, message string) *ResultStatus {
return &ResultStatus{
Code: code,
Message: message,
}
}
// 定义所有的响应结果的状态枚举值
var (
Success = newResultStatus(0, "成功")
DataError = newResultStatus(-1, "数据错误")
DBError = newResultStatus(-2, "数据库错误")
MethodNotDefined = newResultStatus(-3, "方法未定义")
ParamIsEmpty = newResultStatus(-4, "参数为空")
ParamNotMatch = newResultStatus(-5, "参数不匹配")
ParamTypeError = newResultStatus(-6, "参数类型错误")
OnlySupportPOST = newResultStatus(-7, "只支持POST")
APINotDefined = newResultStatus(-8, "API未定义")
APIParamError = newResultStatus(-9, "API参数错误")
InvalidIP = newResultStatus(-10, "IP无效")
PlayerNotExists = newResultStatus(-11, "玩家不存在")
NoAvailableServer = newResultStatus(-12, "没有可用的服务器")
ClientDataError = newResultStatus(-13, "客户端数据错误")
TokenInvalid = newResultStatus(-14, "令牌无效")
ChannelNotDefined = newResultStatus(-15, "聊天频道未定义")
NoTargetMethod = newResultStatus(-16, "找不到目标方法")
ParamInValid = newResultStatus(-17, "参数无效")
NoLogin = newResultStatus(-18, "尚未登陆")
NotInUnion = newResultStatus(-19, "不在公会中")
NotInShimen = newResultStatus(-20, "不在师门中")
NotFoundTarget = newResultStatus(-21, "未找到目标玩家")
PlayerNotExist = newResultStatus(-22, "玩家不存在")
ServerGroupNotExist = newResultStatus(-23, "服务器组不存在")
NotInTeam = newResultStatus(-24, "不在队伍中")
LoginOnAnotherDevice = newResultStatus(-25, "在另一台设备上登录")
CantSendMessageToSelf = newResultStatus(-26, "不能给自己发消息")
ResourceNotEnough = newResultStatus(-27, "资源不足")
NetworkError = newResultStatus(-28, "网络错误")
ContainForbiddenWord = newResultStatus(-29, "含有屏蔽词语")
SendMessageTooFast = newResultStatus(-30, "发送消息太快")
LvIsNotEnough = newResultStatus(-31, "等级不足,系统未开放")
RepeatTooMuch = newResultStatus(-32, "重复次数太多")
CantCrossServerTalk = newResultStatus(-33, "不能跨服私聊")
InSilent = newResultStatus(-34, "您的账号已被禁言,请联系客服反馈。")
NotInCountry = newResultStatus(-35, "不在国家中")
CantSendMessageToGM = newResultStatus(-36, "当前GM对话已结束如有疑问请联系客服QQ咨询")
DisconnectStatus = newResultStatus(-37, "DisconnectStatus")
LanguageToTextError = newResultStatus(-38, "语音转换失败!")
VoiceCloudNotDefined = newResultStatus(-15, "语音识别云商不存在")
ContainForbiddenWordForQCGreen = newResultStatus(-39, "内容含有屏蔽词语")
ContainForbiddenWordForZSYDun = newResultStatus(-59, "内容中含有屏蔽词语")
)

View File

@ -0,0 +1,29 @@
package model
// 请求对象
type ServerRequestObject struct {
// 请求的唯一标识是需要通过截取请求数据前4位得到并进行手动赋值的暂时未使用
RequestId int64
// 请求的模块名称
ModuleName string
// 请求的方法名称
MethodName string
// 请求的参数数组
Parameters []interface{}
// 客户端发送请求的时间
SendTime int64
ReachTime int64
InQueueTime int64
HandleStartTime int64
HandleEndTime int64
ReturnTime int64
}

View File

@ -0,0 +1,89 @@
package model
import (
"strconv"
"time"
)
// 服务器响应对象
type ServerResponseData struct {
// Id
Id int
// 聊天频道
Channel string
// 聊天消息
Message string
// 语音信息
Voice string
// 语音文字
VoiceText string
// 发送人
FromPlayer string
FromPlayerId string
// 接收人
ToPlayer string `json:"ToPlayer,omitempty"`
ToPlayerId string `json:"ToPlayerId,omitempty"`
// 创建的时间戳
TimeStamp int64
//是否是离线消息
IfOffline bool
//是否已读消息
IfRead bool
}
// 创建新的服务器响应对象
func NewServerResponseData(id int, channel, message, voice, voiceText string, from, to *Player, ifOffline, ifRead int32) *ServerResponseData {
var fromPlayer, toPlayer string
var fromPlayerId, toPlayerId int64
if from != nil {
fromPlayerId = from.Id
fromPlayer = from.String()
}
if to != nil {
toPlayerId = to.Id
toPlayer = to.String()
}
return &ServerResponseData{
Id: id,
Channel: channel,
Message: message,
Voice: voice,
VoiceText: voiceText,
FromPlayer: fromPlayer,
FromPlayerId: strconv.FormatInt(fromPlayerId, 10),
ToPlayer: toPlayer,
ToPlayerId: strconv.FormatInt(toPlayerId, 10),
TimeStamp: time.Now().Unix(),
IfOffline: ifOffline == 1,
IfRead: ifRead == 1,
}
}
// 从其它类型转化为服务器响应对象
func ConvertToServerResponseData(id int, channel, message, voice, voiceText, fromPlayer string, fromPlayerId int64, toPlayer string, toPlayerId int64, timeStamp int64, ifOffline, ifRead bool) *ServerResponseData {
return &ServerResponseData{
Id: id,
Channel: channel,
Message: message,
Voice: voice,
VoiceText: voiceText,
FromPlayer: fromPlayer,
FromPlayerId: strconv.FormatInt(fromPlayerId, 10),
ToPlayer: toPlayer,
ToPlayerId: strconv.FormatInt(toPlayerId, 10),
TimeStamp: timeStamp,
IfOffline: ifOffline,
IfRead: ifRead,
}
}

View File

@ -0,0 +1,80 @@
package model
import (
"compress/zlib"
"encoding/json"
"goutil/zlibUtil"
)
// ChatServer的响应对象
type ServerResponseObject struct {
// 响应结果的状态值
*ResultStatus
// 响应结果的数据
Data interface{} `json:"Data,omitempty"`
// 响应结果对应的请求的方法名称
MethodName string
// 压缩后的字节
DataByte []byte `json:"-"`
}
func (this *ServerResponseObject) SetResultStatus(rs *ResultStatus) *ServerResponseObject {
this.ResultStatus = rs
return this
}
func (this *ServerResponseObject) SetData(data interface{}) *ServerResponseObject {
this.Data = data
return this
}
func (this *ServerResponseObject) SetMethodName(methodName string) *ServerResponseObject {
this.MethodName = methodName
return this
}
func (this *ServerResponseObject) IsDisconnect() bool {
return this.ResultStatus.Code == DisconnectStatus.Code
}
// Compress
//
// @Description: 压缩数据
// parameter:
// receiver this
// ifCompress
// return:
func (this *ServerResponseObject) Compress(ifCompress bool) {
// 序列化发送的数据
contentObj, _ := json.Marshal(this)
// 进行zlib压缩
if ifCompress {
contentObj, _ = zlibUtil.Compress(contentObj, zlib.DefaultCompression)
}
//赋值
this.DataByte = contentObj
}
func NewServerResponseObject() *ServerResponseObject {
return &ServerResponseObject{
ResultStatus: Success,
Data: nil,
MethodName: "",
}
}
func NewDisconnectServerResponseObject() *ServerResponseObject {
return &ServerResponseObject{
ResultStatus: DisconnectStatus,
Data: nil,
MethodName: "",
}
}

View File

@ -0,0 +1,176 @@
package server_http
import (
"encoding/json"
"framework/ipMgr"
"io/ioutil"
"net/http"
. "common/model"
"goutil/debugUtil"
"goutil/logUtil"
"goutil/webUtil"
"goutil/zlibUtil"
)
// 请求上下文对象
type Context struct {
// 请求对象
*http.Request
// 应答写对象
http.ResponseWriter
// 请求数据
requestBytes []byte
// 数据是否已经解析数据
ifDataParsed bool
// Form的数据是否已经解析
ifFormParsed bool
}
// 检查IP是否合法
func (this *Context) checkIP() *ResultStatus {
if debugUtil.IsDebug() == false && ipMgr.IsIpValid(webUtil.GetRequestIP(this.Request)) == false {
return InvalidIP
}
return Success
}
func (this *Context) GetFormValue(key string) (value string, exists bool) {
defer func() {
this.ifFormParsed = true
}()
if !this.ifFormParsed {
this.Request.ParseForm()
}
values := this.Form[key]
if values != nil && len(values) > 0 {
value = values[0]
exists = true
return
}
return
}
// 转换内容
func (this *Context) parseContent() error {
defer func() {
this.Body.Close()
this.ifDataParsed = true
}()
data, err := ioutil.ReadAll(this.Body)
if err != nil {
logUtil.ErrorLog("url:%s,读取数据出错,错误信息为:%s", this.RequestURI, err)
return err
}
this.requestBytes = data
return nil
}
// 获取请求字节数据
// 返回值:
// []byte:请求字节数组
// error:错误信息
func (this *Context) GetRequestBytes(isCompressed bool) (result []byte, exists bool, err error) {
if this.ifDataParsed == false {
this.parseContent()
}
data := this.requestBytes
if data == nil || len(data) <= 0 {
return
} else {
exists = true
}
if isCompressed {
result, err = zlibUtil.Decompress(data)
if err != nil {
logUtil.ErrorLog("解压缩请求数据失败:%s", err)
return
}
} else {
result = data
}
return
}
// 获取请求字符串数据
// 返回值:
// 请求字符串数据
func (this *Context) GetRequestString(isCompressed bool) (result string, exists bool, err error) {
var data []byte
data, exists, err = this.GetRequestBytes(isCompressed)
if err != nil || !exists {
return
}
result = string(data)
exists = true
return
}
// Unmarshal 反序列化
// moduleName:模块名称
// obj:反序列化结果数据
// isCompressed:数据是否已经被压缩
// 返回值:
// 错误对象
func (this *Context) Unmarshal(moduleName string, obj interface{}, isCompressed bool) (exists bool, err error) {
var data []byte
data, exists, err = this.GetRequestBytes(isCompressed)
if err != nil || !exists {
return
}
// 反序列化
if err = json.Unmarshal(data, &obj); err != nil {
logUtil.ErrorLog("Module:%s, 反序列化%s出错错误信息为%s", moduleName, string(data), err)
return
}
debugUtil.Printf("接收到GM登录数据data:%v", string(data))
return
}
// 输出字符串
func (this *Context) WriteString(result string) {
this.ResponseWriter.Write([]byte(result))
}
// 输出json数据
func (this *Context) WriteJson(result interface{}) {
if bytes, err := json.Marshal(result); err == nil {
this.ResponseWriter.Write(bytes)
}
}
// 跳转到其它页面
func (this *Context) RedirectTo(url string) {
this.ResponseWriter.Header().Set("Location", url)
this.ResponseWriter.WriteHeader(301)
}
// 新建API上下文对象
// request:请求对象
// responseWriter:应答写对象
// 返回值:
// *Context:上下文
func newContext(request *http.Request, responseWriter http.ResponseWriter) *Context {
return &Context{
Request: request,
ResponseWriter: responseWriter,
}
}

View File

@ -0,0 +1,37 @@
package server_http
import (
"fmt"
. "common/model"
)
type Handler func(*Context) *ServerResponseObject
var (
// 处理函数集合
handlerMap map[string]Handler
)
func init() {
handlerMap = make(map[string]Handler, 8)
}
// RegisterHandler 详细的注册一个WebAPI处理函数
// pattern:路由地址
// handler:处理函数
// paramInfo:参数列表
func RegisterHandler(pattern string, handler Handler) {
if _, exist := handlerMap[pattern]; exist {
panic(fmt.Errorf("存在重复的webapi注册%s", pattern))
}
// 添加处理对象
handlerMap[pattern] = handler
}
// 获取处理函数
func getHandler(pattern string) (handler Handler, exists bool) {
handler, exists = handlerMap[pattern]
return
}

View File

@ -0,0 +1,71 @@
package server_http
import (
"encoding/json"
"net/http"
. "common/model"
"goutil/debugUtil"
"goutil/logUtil"
"goutil/webUtil"
)
// http服务对象
type httpServer struct{}
// http应答处理
// response:应答对象
// request:请求对象
func (this *httpServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
context := newContext(request, response)
responseObj := NewServerResponseObject()
defer func() {
if data := recover(); data != nil {
logUtil.LogUnknownError(data)
return
}
// 特殊路径进行特别处理
if request.URL.Path == "/" || request.URL.Path == "/favicon.ico" {
return
}
// 获取输入参数的字符串形式
parameter := ""
if len(request.Form) > 0 {
parameter_byte, _ := json.Marshal(request.Form)
parameter = string(parameter_byte)
}
// 记录日志
if debugUtil.IsDebug() || responseObj.ResultStatus != Success {
result, _ := json.Marshal(responseObj)
logUtil.DebugLog("%s-->IP:%s;Request:%v;Response:%s;", request.URL.Path, webUtil.GetRequestIP(request), parameter, string(result))
}
}()
// 特殊路径进行特别处理
if request.URL.Path == "/" || request.URL.Path == "/favicon.ico" {
context.WriteString("ok")
return
}
// 验证IP
if rs := context.checkIP(); rs != Success {
context.WriteJson(responseObj.SetResultStatus(rs))
return
}
var handler Handler
var exists bool
if handler, exists = getHandler(request.URL.Path); !exists {
logUtil.ErrorLog("访问的页面不存在RequestAddr: %s request.URL.Path: %s", request.RemoteAddr, request.URL.Path)
http.Error(response, "访问的页面不存在", 404)
return
}
// 调用处理方法,并返回结果
responseObj = handler(context)
context.WriteJson(responseObj)
}

View File

@ -0,0 +1,44 @@
package server_http
import (
"fmt"
"net/http"
"net/http/pprof"
"sync"
)
// 启动Web服务器
// wg:WaitGroup对象
// address:服务器地址
func Start(wg *sync.WaitGroup, address string) {
defer func() {
wg.Done()
}()
//// 开启服务
//serverInstance := http.Server{
// Addr: address,
// Handler: new(httpServer),
//}
//// 开启监听
//msg := fmt.Sprintf("server_http begins to listen on: %s...", address)
//fmt.Println(msg)
//logUtil.InfoLog(msg)
// 启动Web服务器监听
mux := http.NewServeMux()
mux.Handle("/", &httpServer{})
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
if err := http.ListenAndServe(address, mux); err != nil {
panic(fmt.Sprintf("server_http ListenAndServe Error:%v", err))
}
//if err := serverInstance.ListenAndServe(); err != nil {
// panic(fmt.Sprintf("server_http ListenAndServe Error:%v", err))
//}
}

View File

@ -0,0 +1,347 @@
package server_tcp
import (
"compress/zlib"
"encoding/binary"
"encoding/json"
"fmt"
"net"
"strings"
"sync"
"sync/atomic"
"time"
"common/clientMgr"
"common/config"
. "common/model"
"framework/goroutineMgr"
"goutil/intAndBytesUtil"
"goutil/logUtil"
"goutil/timeUtil"
"goutil/zlibUtil"
)
const (
// 包头的长度
con_HEADER_LENGTH = 4
// 客户端失效的秒数
con_CLIENT_EXPIRE_SECONDS int64 = 300
)
var (
// 全局客户端的id从1开始进行自增
globalClientId int32 = 0
// 字节的大小端顺序
byterOrder = binary.LittleEndian
)
// 定义客户端对象,以实现对客户端连接的封装
type Client struct {
// 唯一标识
id int32
// 客户端连接对象
conn net.Conn
// 接收到的消息内容
receiveData []byte
// 待发送的数据
sendData []*ServerResponseObject
// 连接是否关闭(通过此字段来协调receiveData和sendData方法)
closed bool
// 锁对象用于控制对sendDatap的并发访问receiveData不需要因为是同步访问
mutex sync.Mutex
// 玩家Id
playerId int64
// 上次活跃时间
activeTime int64
}
// 获取唯一标识
func (this *Client) GetId() int32 {
return this.id
}
// 获取玩家Id
// 返回值:
// 玩家Id
func (this *Client) GetPlayerId() int64 {
return this.playerId
}
// 玩家登陆
// playerId玩家Id
// 返回值:无
func (this *Client) PlayerLogin(playerId int64) {
this.playerId = playerId
}
// 获取远程地址IP_Port
func (this *Client) GetRemoteAddr() string {
items := strings.Split(this.conn.RemoteAddr().String(), ":")
return fmt.Sprintf("%s_%s", items[0], items[1])
}
// 获取远程地址IP
func (this *Client) getRemoteShortAddr() string {
items := strings.Split(this.conn.RemoteAddr().String(), ":")
return items[0]
}
// 获取待发送的数据
// 返回值:
// 待发送数据项
// 是否含有有效数据
func (this *Client) getSendData() (responseObj *ServerResponseObject, exists bool) {
this.mutex.Lock()
defer this.mutex.Unlock()
// 如果没有数据则直接返回
if len(this.sendData) == 0 {
return
}
// 取出第一条数据,并为返回值赋值
responseObj = this.sendData[0]
exists = true
// 删除已经取出的数据
this.sendData = this.sendData[1:]
return
}
// 发送数据
// sendDataItemObj:待发送数据项
// 返回值:无
func (this *Client) SendMessage(responseObj *ServerResponseObject) {
this.mutex.Lock()
defer this.mutex.Unlock()
this.sendData = append(this.sendData, responseObj)
}
// 清空待发送数据
func (this *Client) ClearSendData() {
this.mutex.Lock()
defer this.mutex.Unlock()
this.sendData = make([]*ServerResponseObject, 0, 16)
}
// 发送字节数组消息
// responseObj:返回值对象
func (this *Client) sendResponseObject(responseObj *ServerResponseObject) error {
beforeTime := time.Now().Unix()
// 序列化发送的数据
content, _ := json.Marshal(responseObj)
// 进行zlib压缩
if config.GetBaseConfig().IfCompressData {
content, _ = zlibUtil.Compress(content, zlib.DefaultCompression)
}
// 获得数据内容的长度
contentLength := len(content)
// 将长度转化为字节数组
header := intAndBytesUtil.Int32ToBytes(int32(contentLength), byterOrder)
// 将头部与内容组合在一起
message := append(header, content...)
// 发送消息
if err := this.sendMessage(message); err != nil {
return err
}
// 如果发送的时间超过3秒则记录下来
if time.Now().Unix()-beforeTime > 3 {
logUtil.WarnLog("消息Size:%d, UseTime:%d", contentLength, time.Now().Unix()-beforeTime)
}
return nil
}
func (this *Client) sendMessage(message []byte) error {
this.mutex.Lock()
defer this.mutex.Unlock()
if _, err := this.conn.Write(message); err != nil {
return err
}
return nil
}
// 客户端活跃
// 返回值:无
func (this *Client) Active() {
atomic.StoreInt64(&this.activeTime, time.Now().Unix())
}
// 判断客户端是否超时超过300秒不活跃算作超时
// 返回值:是否超时
func (this *Client) Expired() bool {
return time.Now().Unix() > this.activeTime+con_CLIENT_EXPIRE_SECONDS
}
// 客户端连接对象断开
// 返回值:无
func (this *Client) Close() {
this.conn.Close()
this.closed = true
this.playerId = 0
}
// 格式化
func (this *Client) String() string {
return fmt.Sprintf("{Id:%d, RemoteAddr:%s, activeTime:%s, playerId:%s}", this.id, this.GetRemoteAddr(), timeUtil.Format(time.Unix(this.activeTime, 0), "yyyy-MM-dd HH:mm:ss"), this.playerId)
}
// -----------------------------实现IConnection接口方法end-------------------------
// 客户端启动函数
func (this *Client) start() {
go this.handleReceiveData()
go this.handleSendData()
}
// 获取有效的消息
// 返回值:
// 消息内容
// 是否含有有效数据
func (this *Client) getReceiveData() (message []byte, exists bool) {
// 判断是否包含头部信息
if len(this.receiveData) < con_HEADER_LENGTH {
return
}
// 获取头部信息
header := this.receiveData[:con_HEADER_LENGTH]
// 将头部数据转换为内部的长度
contentLength := intAndBytesUtil.BytesToInt32(header, byterOrder)
// 约定len(message) == 0,为心跳请求
if contentLength == 0 {
// 将对应的数据截断,以得到新的内容,并返回心跳内容
this.receiveData = this.receiveData[con_HEADER_LENGTH:]
if err := this.sendMessage([]byte{}); err != nil {
return
}
return
}
// 判断长度是否满足
if len(this.receiveData) < con_HEADER_LENGTH+int(contentLength) {
return
}
// 提取消息内容
message = this.receiveData[con_HEADER_LENGTH : con_HEADER_LENGTH+contentLength]
exists = true
// 将对应的数据截断,以得到新的数据
this.receiveData = this.receiveData[con_HEADER_LENGTH+contentLength:]
return
}
// 处理客户端收到的数据
func (this *Client) handleReceiveData() {
// 处理goroutine数量
goroutineName := "server_tcp.handleReceiveData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
// 无限循环,不断地读取数据,解析数据,处理数据
for {
if this.closed {
break
}
// 先读取数据每次读取1024个字节
readBytes := make([]byte, 2048*2)
// Read方法会阻塞所以不用考虑异步的方式
n, err := this.conn.Read(readBytes)
if err != nil {
break
}
// 将读取到的数据追加到已获得的数据的末尾并更新activeTime
this.receiveData = append(this.receiveData, readBytes[:n]...)
this.Active()
// 处理数据
for {
message, exists := this.getReceiveData()
if !exists {
break
}
clientMgr.HandleRequest(this, message)
}
}
}
// 处理需要客户端发送的数据
func (this *Client) handleSendData() {
// 处理goroutine数量
goroutineName := "server_tcp.handleSendData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
for {
if this.closed {
break
}
// 如果发送出现错误,表示连接已经断开,则退出方法;
if sendDataItemObj, exists := this.getSendData(); exists {
// 判断是否为断开客户端连接的数据
if sendDataItemObj.IsDisconnect() {
break
}
if err := this.sendResponseObject(sendDataItemObj); err != nil {
return
}
} else {
time.Sleep(5 * time.Millisecond)
}
}
}
// 新建客户端对象
// conn连接对象
// 返回值:客户端对象的指针
func newClient(conn net.Conn) *Client {
// 获得自增的id值
getIncrementId := func() int32 {
atomic.AddInt32(&globalClientId, 1)
return globalClientId
}
return &Client{
id: getIncrementId(),
conn: conn,
receiveData: make([]byte, 0, 2048*2),
sendData: make([]*ServerResponseObject, 0, 16),
activeTime: time.Now().Unix(),
playerId: 0,
}
}

View File

@ -0,0 +1,55 @@
package server_tcp
import (
"fmt"
"framework/goroutineMgr"
"net"
"sync"
"goutil/debugUtil"
"common/clientMgr"
"goutil/logUtil"
)
// Start 启动服务器
func Start(wg *sync.WaitGroup, address string) {
defer func() {
wg.Done()
}()
// 处理goroutine数量
goroutineName := "server_tcp.Start"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
msg := fmt.Sprintf("server_tcp begins to listen on:%s...", address)
fmt.Println(msg)
logUtil.InfoLog(msg)
// 监听指定的端口
listener, err := net.Listen("tcp", address)
//debug模式下打印日志
if debugUtil.IsDebug() {
logUtil.DebugLog(fmt.Sprintf("收到客户的连接请求ip:%v", listener.Addr()))
}
if err != nil {
panic(fmt.Sprintf("server_tcp listen Error: %s", err))
}
for {
// 阻塞直至新连接到来
conn, err := listener.Accept()
if err != nil {
logUtil.ErrorLog("server_tcp accept error: %s", err)
continue
}
// 创建客户端对象
clientObj := newClient(conn)
clientObj.start()
clientMgr.RegisterClient(clientObj)
}
}

View File

@ -0,0 +1,302 @@
package server_webSocket
import (
"common/clientMgr"
"common/config"
"common/model"
"encoding/binary"
"fmt"
"framework/goroutineMgr"
"github.com/gorilla/websocket"
"goutil/logUtil"
"goutil/timeUtil"
"strings"
"sync"
"sync/atomic"
"time"
)
const (
// 消息每次读取的上限值
con_MaxMessageSize = 2048 * 2
// 客户端失效的秒数
con_CLIENT_EXPIRE_SECONDS = 20
)
var (
// 全局客户端的id从1开始进行自增
globalClientId int32 = 0
// 字节的大小端顺序
byterOrder = binary.LittleEndian
)
// Client 实现IConnection接口的所有方法
// 定义客户端对象,以实现对客户端连接的封装
type Client struct {
// 唯一标识
id int32
// The websocket connection.
conn *websocket.Conn
// 接收到的消息内容
receiveData []byte
// 待发送的数据
sendData []*model.ServerResponseObject
// 连接是否关闭(通过此字段来协调receiveData和sendData方法)
closed bool
// 锁对象用于控制对sendDatap的并发访问receiveData不需要因为是同步访问
mutex sync.Mutex
// 玩家Id
playerId int64
// 上次活跃时间
activeTime int64
}
// 获取唯一标识
func (this *Client) GetId() int32 {
return this.id
}
// 获取玩家Id
// 返回值:
// 玩家Id
func (this *Client) GetPlayerId() int64 {
return this.playerId
}
// 玩家登陆
// playerId玩家Id
// 返回值:无
func (this *Client) PlayerLogin(playerId int64) {
this.playerId = playerId
}
// 获取远程地址IP_Port
func (this *Client) GetRemoteAddr() string {
items := strings.Split(this.conn.RemoteAddr().String(), ":")
return fmt.Sprintf("%s_%s", items[0], items[1])
}
// 获取远程地址IP
func (this *Client) getRemoteShortAddr() string {
items := strings.Split(this.conn.RemoteAddr().String(), ":")
return items[0]
}
// 获取待发送的数据
// 返回值:
// 待发送数据项
// 是否含有有效数据
func (this *Client) getSendData() (responseObj *model.ServerResponseObject, exists bool) {
this.mutex.Lock()
defer this.mutex.Unlock()
// 如果没有数据则直接返回
if len(this.sendData) == 0 {
return
}
// 取出第一条数据,并为返回值赋值
responseObj = this.sendData[0]
exists = true
// 删除已经取出的数据
this.sendData = this.sendData[1:]
return
}
// 发送数据
// sendDataItemObj:待发送数据项
// 返回值:无
func (this *Client) SendMessage(responseObj *model.ServerResponseObject) {
this.mutex.Lock()
defer this.mutex.Unlock()
this.sendData = append(this.sendData, responseObj)
}
// 清空待发送数据
func (this *Client) ClearSendData() {
this.mutex.Lock()
defer this.mutex.Unlock()
this.sendData = make([]*model.ServerResponseObject, 0, 16)
}
// 向客户端发送消息
// responseObj:返回值对象
func (this *Client) sendResponseObject(responseObj *model.ServerResponseObject) error {
beforeTime := time.Now().Unix()
//// 序列化发送的数据
//content, _ := json.Marshal(responseObj)
//
////debug模式下打印日志
//if debugUtil.IsDebug() {
//
// //记录日志,方便查询
// logUtil.WarnLog("向客户端发送消息%v", string(content))
//}
//
//// 进行zlib压缩
//if config.GetBaseConfig().IfCompressData {
// content, _ = zlibUtil.Compress(content, zlib.DefaultCompression)
//}
//检查是否需要压缩
if len(responseObj.DataByte) <= 0 {
responseObj.Compress(config.GetBaseConfig().IfCompressData)
}
// 发送消息
if err := this.sendMessage(responseObj.DataByte); err != nil {
return err
}
// 如果发送的时间超过3秒则记录下来
if time.Now().Unix()-beforeTime > 3 {
logUtil.WarnLog("消息Size:%d, UseTime:%d", len(responseObj.DataByte), time.Now().Unix()-beforeTime)
}
return nil
}
func (this *Client) sendMessage(message []byte) error {
this.mutex.Lock()
defer this.mutex.Unlock()
if err := this.conn.WriteMessage(websocket.BinaryMessage, message); err != nil {
return err
}
return nil
}
// 客户端活跃
// 返回值:无
func (this *Client) Active() {
atomic.StoreInt64(&this.activeTime, time.Now().Unix())
}
// 判断客户端是否超时超过300秒不活跃算作超时
// 返回值:是否超时
func (this *Client) Expired() bool {
return time.Now().Unix() > this.activeTime+con_CLIENT_EXPIRE_SECONDS
}
// 客户端连接对象断开
// 返回值:无
func (this *Client) Close() {
this.conn.Close()
this.closed = true
this.playerId = 0
}
// 格式化
func (this *Client) String() string {
return fmt.Sprintf("{Id:%d, RemoteAddr:%s, activeTime:%s, playerId:%s}", this.id, this.GetRemoteAddr(), timeUtil.Format(time.Unix(this.activeTime, 0), "yyyy-MM-dd HH:mm:ss"), this.playerId)
}
// -----------------------------实现IConnection接口方法end-------------------------
// 客户端启动函数
func (this *Client) start() {
go this.handleReceiveData()
go this.handleSendData()
}
// 处理从连接收到的数据
func (this *Client) handleReceiveData() {
// 处理goroutine数量
goroutineName := "server_webSocket.handleSendData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
// 无限循环,不断地读取数据,解析数据,处理数据
for {
if this.closed {
break
}
_, message, err := this.conn.ReadMessage()
if err != nil {
break
}
// 更新activeTime
this.Active()
// 约定len(message) == 0,为心跳请求
if len(message) == 0 {
if err := this.sendMessage([]byte{}); err != nil {
return
}
continue
}
clientMgr.HandleRequest(this, message)
}
}
// 处理向客户端发送的数据
func (this *Client) handleSendData() {
// 处理goroutine数量
goroutineName := "server_webSocket.handleSendData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
for {
if this.closed {
break
}
// 如果发送出现错误,表示连接已经断开,则退出方法;
if sendDataItemObj, exists := this.getSendData(); exists {
// 判断是否为断开客户端连接的数据
if sendDataItemObj.IsDisconnect() {
break
}
if err := this.sendResponseObject(sendDataItemObj); err != nil {
return
}
} else {
time.Sleep(5 * time.Millisecond)
}
}
}
// 新建客户端对象
// conn连接对象
// 返回值:客户端对象的指针
func newClient(conn *websocket.Conn) *Client {
conn.SetReadLimit(con_MaxMessageSize)
// 获得自增的id值
getIncrementId := func() int32 {
atomic.AddInt32(&globalClientId, 1)
return globalClientId
}
return &Client{
id: getIncrementId(),
conn: conn,
receiveData: make([]byte, 0, 1024),
sendData: make([]*model.ServerResponseObject, 0, 16),
activeTime: time.Now().Unix(),
playerId: 0,
}
}

View File

@ -0,0 +1,63 @@
package server_webSocket
import (
"fmt"
"net/http"
"sync"
"common/clientMgr"
"github.com/gorilla/websocket"
"goutil/debugUtil"
"goutil/logUtil"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func handleConn(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
//是否debug模式
if debugUtil.IsDebug() {
logUtil.DebugLog(fmt.Sprintf("收到客户的websockte连接,ip%v", conn.RemoteAddr()))
}
if err != nil {
logUtil.ErrorLog("websocket.handleConn获取连接出错err:%v", err)
return
}
// 创建客户端对象
clientObj := newClient(conn)
clientObj.start()
clientMgr.RegisterClient(clientObj)
debugUtil.Printf("收到连接请求:remoteAdd:%s\n", conn.RemoteAddr())
}
// Start 启动服务器
func Start(wg *sync.WaitGroup, address string, isUseTSL bool) {
defer wg.Done()
msg := fmt.Sprintf("server_websocket begins to listen on:%s...", address)
fmt.Println(msg)
logUtil.InfoLog(msg)
http.HandleFunc("/", handleConn)
if isUseTSL {
err := http.ListenAndServeTLS(address, "tlsFile/7qule.com.pem", "tlsFile/7qule.com.key", nil)
if err != nil {
panic(fmt.Sprintf("server_websocket.ListenAndServeTLS, err:%v", err))
}
} else {
err := http.ListenAndServe(address, nil)
if err != nil {
panic(fmt.Sprintf("server_websocket.ListenAndServe, err:%v", err))
}
}
}