diff --git a/Public/Publish/新建文本文档.txt b/Public/Publish/新建文本文档.txt new file mode 100644 index 0000000..e69de29 diff --git a/Server/Gateway/main.go b/Server/Gateway/main.go index 967a91b..5bb2129 100644 --- a/Server/Gateway/main.go +++ b/Server/Gateway/main.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" "golang.org/x/sync/errgroup" + "gorm.io/gorm/logger" "os" "os/signal" "syscall" @@ -13,13 +14,9 @@ import ( func main() { // 初始化配置与日志 - viper.AutomaticEnv() - logger, _ := zap.NewProduction() - defer logger.Sync() - + initConfig() // 数据库初始化 - db := initDB() - defer db.Close() + initDB() // HTTP 服务配置 server := initServer(viper.GetString("PORT"), logger) @@ -57,3 +54,11 @@ func main() { logger.Fatal("Server exited with error", zap.Error(err)) } } + +func initConfig() { + +} + +func initDB() { + +} diff --git a/Server/common/config/common-config-branch-dev.json b/Server/common/config/common-config-branch-dev.json new file mode 100644 index 0000000..7284c85 --- /dev/null +++ b/Server/common/config/common-config-branch-dev.json @@ -0,0 +1,108 @@ +{ + "logger": { + "level": "debug", + "max_age": 7, + "max_back_up": 128, + "max_size": 128, + "debug": false + }, + "redis": { + "host": "10.0.40.3", + "port": 6379, + "auth": "123456", + "max_idle": 20, + "max_active": 100, + "db": 0, + "tls": false, + "user_name": "default" + }, + "etcd": { + "host": "http://10.0.40.3", + "port": 2379, + "ttl": 10, + "key_discover": "/discover" + }, + "mysql": { + "driver": "mysql", + "host": "10.0.40.3", + "port": "3306", + "user": "root", + "password": "123456" + }, + "mongo": { + "user": "root", + "password": "superhmm", + "ssl_ca_file": "", + "host": "10.0.40.3", + "port": 27017 + }, + "kafka": { + "chunkSize": 0, + "flushInterval": 1, + "brokers": [ + "10.0.40.3:9092" + ] + }, + "jwt": { + "secret": "ZMpy1gIxRevtcIwoW1qA84YAcWj4cjrEhPUebOiB8hUXkiwOfobJxeklH4ZO1VE1", + "expires": 30 + }, + "tencent_video_call": { + "appID": 1600034485, + "secretKey": "9c2e7aab5c318194ee815ee0a5162ea9ab0b0bce981311d5ce08d26c5e9df9e0", + "callBackKey": "Pv3JD4RJXBCo", + "prefix": "branch_dev", + "tencent_asr_cloud_os": { + "bucket": "meeting-test-1306984848", + "region": "ap-guangzhou", + "bucketURL": "https://meeting-test-1306984848.cos.ap-guangzhou.myqcloud.com", + "CIURL": "https://meeting-test-1306984848.ci.ap-guangzhou.myqcloud.com", + "CIWorkflows": { + "asr_workflow": { + "name": "cover_asr", + "workflowId": "wc700505871f849908f56f30533cb54ce" + } + } + } + }, + "tencent_cloud_os": { + "bucketURL": "https://ecosmos-test-1306984848.cos.ap-guangzhou.myqcloud.com", + "serviceURL": "https://cos.ap-guangzhou.myqcloud.com", + "batchURL": "https://AKID27MnCY2eFsWCnKlItThxt1EubMriDewC.cos-control.ap-guangzhou.myqcloud.com", + "CIURL": "https://ecosmos-test-1306984848.ci.ap-guangzhou.myqcloud.com" + }, + "tencent_sdk_common": { + "secretId": "AKID27MnCY2eFsWCnKlItThxt1EubMriDewC", + "secretKey": "IWdfaTTN8Y6g8Q4qdRH3BUsQySCjOKiC" + }, + "back_http": { + "scheme": "http", + "host": "10.0.40.9:7098", + "xkey": "lkAsgl2sShgfnDweJLJSNGLSNG1234sKE58dgd" + }, + "us": { + "public_key": "4eZBdZBQvR7JdD3Q3WMHna67b0BPf7U6O", + "private_key": "FugwvTjts8rJ2eYiLTatH0FK2n3zjhhWP9EsXXbmDhvy", + "bucket_name": "ecosmos-test", + "file_host": "cn-wlcb.ufileos.com", + "bucket_host": "api.ucloud.cn", + "verfiy_upload_md5": true, + "us_host_path": "https" + }, + "ai_server": { + "scheme": "http", + "host": "106.52.14.61:15002" + }, + "elecnest": { + "scheme": "https", + "host": "id.elecnest.cn" + }, + "open_log": true, + "wechat_mini": { + "scheme": "https", + "host": "api.weixin.qq.com", + "envVersion": "trial", + "getWxACodeUnLimitPath": "/wxa/getwxacodeunlimit", + "generateUrlLinkPath": "/wxa/generate_urllink" + } +} \ No newline at end of file diff --git a/Server/common/config/common-config-branch-test.json b/Server/common/config/common-config-branch-test.json new file mode 100644 index 0000000..022c56e --- /dev/null +++ b/Server/common/config/common-config-branch-test.json @@ -0,0 +1,108 @@ +{ + "logger": { + "level": "debug", + "max_age": 7, + "max_back_up": 128, + "max_size": 128, + "debug": false + }, + "redis": { + "host": "10.0.0.12", + "port": 6379, + "auth": "Q3GNWg!TEsmkURuSYe9", + "max_idle": 20, + "max_active": 100, + "db": 0, + "tls": false, + "user_name": "default" + }, + "etcd": { + "host": "http://10.0.0.12", + "port": 2379, + "ttl": 10, + "key_discover": "/discover" + }, + "mysql": { + "driver": "mysql", + "host": "10.0.0.12", + "port": "3306", + "user": "root", + "password": "MwrC5zcZ8HQ7BB=qb4K8" + }, + "mongo": { + "user": "root", + "password": "eExeaWF=dQzJpkMtc9kb", + "ssl_ca_file": "", + "host": "10.0.0.12", + "port": 27017 + }, + "kafka": { + "chunkSize": 0, + "flushInterval": 1, + "brokers": [ + "10.0.0.12:9092" + ] + }, + "jwt": { + "secret": "ZMpy1gIxRevtcIwoW1qA84YAcWj4cjrEhPUebOiB8hUXkiwOfobJxeklH4ZO1VE4", + "expires": 30 + }, + "tencent_video_call": { + "appID": 1600034485, + "secretKey": "9c2e7aab5c318194ee815ee0a5162ea9ab0b0bce981311d5ce08d26c5e9df9e0", + "callBackKey": "Pv3JD4RJXBCo", + "prefix": "branch_test", + "tencent_asr_cloud_os": { + "bucket": "meeting-test-1306984848", + "region": "ap-guangzhou", + "bucketURL": "https://meeting-test-1306984848.cos.ap-guangzhou.myqcloud.com", + "CIURL": "https://meeting-test-1306984848.ci.ap-guangzhou.myqcloud.com", + "CIWorkflows": { + "asr_workflow": { + "name": "cover_asr", + "workflowId": "wc700505871f849908f56f30533cb54ce" + } + } + } + }, + "tencent_cloud_os": { + "bucketURL": "https://ecosmos-test-1306984848.cos.ap-guangzhou.myqcloud.com", + "serviceURL": "https://cos.ap-guangzhou.myqcloud.com", + "batchURL": "https://AKID27MnCY2eFsWCnKlItThxt1EubMriDewC.cos-control.ap-guangzhou.myqcloud.com", + "CIURL": "https://ecosmos-test-1306984848.ci.ap-guangzhou.myqcloud.com" + }, + "tencent_sdk_common": { + "secretId": "AKID27MnCY2eFsWCnKlItThxt1EubMriDewC", + "secretKey": "IWdfaTTN8Y6g8Q4qdRH3BUsQySCjOKiC" + }, + "back_http": { + "scheme": "http", + "host": "10.0.0.12:7098", + "xkey": "lkAsgl2sShgfnDweJLJSNGLSNG1234sKE58dgd" + }, + "us": { + "public_key": "4eZBdZBQvR7JdD3Q3WMHna67b0BPf7U6O", + "private_key": "FugwvTjts8rJ2eYiLTatH0FK2n3zjhhWP9EsXXbmDhvy", + "bucket_name": "ecosmos-test", + "file_host": "cn-wlcb.ufileos.com", + "bucket_host": "api.ucloud.cn", + "verfiy_upload_md5": true, + "us_host_path": "https" + }, + "ai_server": { + "scheme": "http", + "host": "106.52.14.61:15002" + }, + "elecnest": { + "scheme": "https", + "host": "id.elecnest.cn" + }, + "open_log": true, + "wechat_mini": { + "scheme": "https", + "host": "api.weixin.qq.com", + "envVersion": "trial", + "getWxACodeUnLimitPath": "/wxa/getwxacodeunlimit", + "generateUrlLinkPath": "/wxa/generate_urllink" + } +} \ No newline at end of file diff --git a/Server/common/config/common-config-prod.json b/Server/common/config/common-config-prod.json new file mode 100644 index 0000000..27ac7af --- /dev/null +++ b/Server/common/config/common-config-prod.json @@ -0,0 +1,108 @@ +{ + "logger": { + "level": "debug", + "max_age": 7, + "max_back_up": 128, + "max_size": 128, + "debug": false + }, + "redis": { + "host": "10.0.10.9", + "port": 6379, + "auth": "ZzC2gCVuR52!z", + "max_idle": 20, + "max_active": 100, + "db": 1, + "tls": false, + "user_name": "default" + }, + "etcd": { + "host": "http://10.0.0.7", + "port": 2379, + "ttl": 20, + "key_discover": "/discover" + }, + "mysql": { + "driver": "mysql", + "host": "10.0.5.10", + "port": "3306", + "user": "root", + "password": "d3tnt4J2aWRg7fZ52M" + }, + "mongo": { + "user": "mongouser", + "password": "xEti2JhEgI!m", + "ssl_ca_file": "", + "host": "10.0.2.9", + "port": 27017 + }, + "kafka": { + "chunkSize": 0, + "flushInterval": 1, + "brokers": [ + "10.0.12.16:9092" + ] + }, + "jwt": { + "secret": "ZMpy1gIxRevtcIwoW1qA84YAcWj4cjrEhPUebOiB8hUXkiwOfobJxeklH4ZO1VE3", + "expires": 30 + }, + "tencent_video_call": { + "appID": 1600003528, + "secretKey": "8188013dbe9f35305c58a2f8964fbf330f9e1de4a60827966c91a47b9f2ae607", + "callBackKey": "MzPBm3csloMHkXmEcvl", + "prefix": "prod", + "tencent_asr_cloud_os": { + "bucket": "meeting-prod-1306984848", + "region": "ap-guangzhou", + "bucketURL": "https://meeting-prod-1306984848.cos.ap-guangzhou.myqcloud.com", + "CIURL": "https://meeting-prod-1306984848.ci.ap-guangzhou.myqcloud.com", + "CIWorkflows": { + "asr_workflow": { + "name": "prod_cover_asr", + "workflowId": "w2d30647561e341f19342d684c92e483a" + } + } + } + }, + "tencent_cloud_os": { + "bucketURL": "https://ecosmos-prod-1306984848.cos.ap-guangzhou.myqcloud.com", + "serviceURL": "https://cos.ap-guangzhou.myqcloud.com", + "batchURL": "https://AKID27MnCY2eFsWCnKlItThxt1EubMriDewC.cos-control.ap-guangzhou.myqcloud.com", + "CIURL": "https://ecosmos-prod-1306984848.ci.ap-guangzhou.myqcloud.com" + }, + "tencent_sdk_common": { + "secretId": "AKID27MnCY2eFsWCnKlItThxt1EubMriDewC", + "secretKey": "IWdfaTTN8Y6g8Q4qdRH3BUsQySCjOKiC" + }, + "back_http": { + "scheme": "http", + "host": "10.0.0.7:7098", + "xkey": "lkAsgl2sShgfnDweJLJSNGLSNG1234sKE58dgd" + }, + "us": { + "public_key": "4eZBdZBQvR7JdD3Q3WMHna67b0BPf7U6O", + "private_key": "FugwvTjts8rJ2eYiLTatH0FK2n3zjhhWP9EsXXbmDhvy", + "bucket_name": "ecosmos", + "file_host": "cn-wlcb.ufileos.com", + "bucket_host": "api.ucloud.cn", + "verfiy_upload_md5": true, + "us_host_path": "https" + }, + "ai_server": { + "scheme": "http", + "host": "106.52.14.61:15002" + }, + "elecnest": { + "scheme": "https", + "host": "identify.elecnest.com" + }, + "open_log": true, + "wechat_mini": { + "scheme": "https", + "host": "api.weixin.qq.com", + "envVersion": "release", + "getWxACodeUnLimitPath": "/wxa/getwxacodeunlimit", + "generateUrlLinkPath": "/wxa/generate_urllink" + } +} \ No newline at end of file diff --git a/Server/common/config/common-config-trunk-dev.json b/Server/common/config/common-config-trunk-dev.json new file mode 100644 index 0000000..e9f51ac --- /dev/null +++ b/Server/common/config/common-config-trunk-dev.json @@ -0,0 +1,108 @@ +{ + "logger": { + "level": "debug", + "max_age": 7, + "max_back_up": 128, + "max_size": 128, + "debug": false + }, + "redis": { + "host": "10.0.40.9", + "port": 6379, + "auth": "superhmm", + "max_idle": 20, + "max_active": 100, + "db": 0, + "tls": false, + "user_name": "default" + }, + "etcd": { + "host": "http://10.0.40.9", + "port": 2379, + "ttl": 10, + "key_discover": "/discover" + }, + "mysql": { + "driver": "mysql", + "host": "10.0.40.9", + "port": "3306", + "user": "root", + "password": "123456" + }, + "mongo": { + "user": "root", + "password": "superhmm", + "ssl_ca_file": "", + "host": "10.0.40.9", + "port": 27017 + }, + "kafka": { + "chunkSize": 0, + "flushInterval": 1, + "brokers": [ + "10.0.40.9:9092" + ] + }, + "jwt": { + "secret": "ZMpy1gIxRevtcIwoW1qA84YAcWj4cjrEhPUebOiB8hUXkiwOfobJxeklH4ZO1VE1", + "expires": 30 + }, + "tencent_video_call": { + "appID": 1600034485, + "secretKey": "9c2e7aab5c318194ee815ee0a5162ea9ab0b0bce981311d5ce08d26c5e9df9e0", + "callBackKey": "RSHRgskxwUauX6JyTb7m", + "prefix": "dev", + "tencent_asr_cloud_os": { + "bucket": "meeting-dev-1306984848", + "region": "ap-guangzhou", + "bucketURL": "https://meeting-dev-1306984848.cos.ap-guangzhou.myqcloud.com", + "CIURL": "https://meeting-dev-1306984848.ci.ap-guangzhou.myqcloud.com", + "CIWorkflows": { + "asr_workflow": { + "name": "cover_asr", + "workflowId": "w8b3ece33cd2d4b28a373779dc3a93941" + } + } + } + }, + "tencent_cloud_os": { + "bucketURL": "https://ecosmos-dev-1306984848.cos.ap-guangzhou.myqcloud.com", + "serviceURL": "https://cos.ap-guangzhou.myqcloud.com", + "batchURL": "https://AKID27MnCY2eFsWCnKlItThxt1EubMriDewC.cos-control.ap-guangzhou.myqcloud.com", + "CIURL": "https://ecosmos-dev-1306984848.ci.ap-guangzhou.myqcloud.com" + }, + "tencent_sdk_common": { + "secretId": "AKID27MnCY2eFsWCnKlItThxt1EubMriDewC", + "secretKey": "IWdfaTTN8Y6g8Q4qdRH3BUsQySCjOKiC" + }, + "back_http": { + "scheme": "http", + "host": "10.0.40.9:7098", + "xkey": "lkAsgl2sShgfnDweJLJSNGLSNG1234sKE58dgd" + }, + "us": { + "public_key": "4eZBdZBQvR7JdD3Q3WMHna67b0BPf7U6O", + "private_key": "FugwvTjts8rJ2eYiLTatH0FK2n3zjhhWP9EsXXbmDhvy", + "bucket_name": "ecosmos-test", + "file_host": "cn-wlcb.ufileos.com", + "bucket_host": "api.ucloud.cn", + "verfiy_upload_md5": true, + "us_host_path": "https" + }, + "ai_server": { + "scheme": "http", + "host": "106.52.14.61:15002" + }, + "elecnest": { + "scheme": "https", + "host": "id.elecnest.cn" + }, + "openLog": false, + "wechat_mini": { + "scheme": "https", + "host": "api.weixin.qq.com", + "envVersion": "develop", + "getWxACodeUnLimitPath": "/wxa/getwxacodeunlimit", + "generateUrlLinkPath": "/wxa/generate_urllink" + } +} \ No newline at end of file diff --git a/Server/common/config/common-config-trunk-test.json b/Server/common/config/common-config-trunk-test.json new file mode 100644 index 0000000..74c96ce --- /dev/null +++ b/Server/common/config/common-config-trunk-test.json @@ -0,0 +1,108 @@ +{ + "logger": { + "level": "debug", + "max_age": 7, + "max_back_up": 128, + "max_size": 128, + "debug": false + }, + "redis": { + "host": "10.0.0.15", + "port": 6379, + "auth": "Q3GNWg!TEsmkURuSYe9", + "max_idle": 20, + "max_active": 100, + "db": 0, + "tls": false, + "user_name": "default" + }, + "etcd": { + "host": "http://10.0.0.15", + "port": 2379, + "ttl": 10, + "key_discover": "/discover" + }, + "mysql": { + "driver": "mysql", + "host": "10.0.0.15", + "port": "3306", + "user": "root", + "password": "MwrC5zcZ8HQ7BB=qb4K8" + }, + "mongo": { + "user": "root", + "password": "eExeaWF=dQzJpkMtc9kb", + "ssl_ca_file": "", + "host": "134.175.247.234", + "port": 27017 + }, + "kafka": { + "chunkSize": 0, + "flushInterval": 1, + "brokers": [ + "10.0.0.15:9092" + ] + }, + "jwt": { + "secret": "ZMpy1gIxRevtcIwoW1qA84YAcWj4cjrEhPUebOiB8hUXkiwOfobJxeklH4ZO1VE2", + "expires": 30 + }, + "tencent_video_call": { + "appID": 1600034485, + "secretKey": "9c2e7aab5c318194ee815ee0a5162ea9ab0b0bce981311d5ce08d26c5e9df9e0", + "callBackKey": "Pv3JD4RJXBCo", + "prefix": "test", + "tencent_asr_cloud_os": { + "bucket": "meeting-test-1306984848", + "region": "ap-guangzhou", + "bucketURL": "https://meeting-test-1306984848.cos.ap-guangzhou.myqcloud.com", + "CIURL": "https://meeting-test-1306984848.ci.ap-guangzhou.myqcloud.com", + "CIWorkflows": { + "asr_workflow": { + "name": "cover_asr", + "workflowId": "wc700505871f849908f56f30533cb54ce" + } + } + } + }, + "tencent_cloud_os": { + "bucketURL": "https://ecosmos-test-1306984848.cos.ap-guangzhou.myqcloud.com", + "serviceURL": "https://cos.ap-guangzhou.myqcloud.com", + "batchURL": "https://AKID27MnCY2eFsWCnKlItThxt1EubMriDewC.cos-control.ap-guangzhou.myqcloud.com", + "CIURL": "https://ecosmos-test-1306984848.ci.ap-guangzhou.myqcloud.com" + }, + "tencent_sdk_common": { + "secretId": "AKID27MnCY2eFsWCnKlItThxt1EubMriDewC", + "secretKey": "IWdfaTTN8Y6g8Q4qdRH3BUsQySCjOKiC" + }, + "back_http": { + "scheme": "http", + "host": "10.0.0.15:7098", + "xkey": "lkAsgl2sShgfnDweJLJSNGLSNG1234sKE58dgd" + }, + "us": { + "public_key": "4eZBdZBQvR7JdD3Q3WMHna67b0BPf7U6O", + "private_key": "FugwvTjts8rJ2eYiLTatH0FK2n3zjhhWP9EsXXbmDhvy", + "bucket_name": "ecosmos-test", + "file_host": "cn-wlcb.ufileos.com", + "bucket_host": "api.ucloud.cn", + "verfiy_upload_md5": true, + "us_host_path": "https" + }, + "ai_server": { + "scheme": "http", + "host": "106.52.14.61:15002" + }, + "elecnest": { + "scheme": "https", + "host": "id.elecnest.cn" + }, + "open_log": true, + "wechat_mini": { + "scheme": "https", + "host": "api.weixin.qq.com", + "envVersion": "trial", + "getWxACodeUnLimitPath": "/wxa/getwxacodeunlimit", + "generateUrlLinkPath": "/wxa/generate_urllink" + } +} \ No newline at end of file diff --git a/Server/common/config/config.go b/Server/common/config/config.go new file mode 100644 index 0000000..1ec2aca --- /dev/null +++ b/Server/common/config/config.go @@ -0,0 +1,166 @@ +package config + +type ( + LoggerConfig struct { + Level string `json:"level"` + MaxAge int `json:"max_age"` + MaxBackUp int `json:"max_back_up"` + MaxSize int `json:"max_size"` + Debug bool `json:"debug"` + } + + RedisConfig struct { + Host string `json:"host" env:"REDIS_HOST"` + Port int `json:"port" env:"REDIS_PORT"` + Auth string `json:"auth" env:"REDIS_AUTH"` + MaxIdle int `json:"max_idle" env:"REDIS_MAX_IDLE"` + MaxActive int `json:"max_active" env:"REDIS_MAX_ACTIVE"` + Db int `json:"db" env:"REDIS_DB"` + Tls bool `json:"tls"` + UserName string `json:"user_name"` + } + + EtcdConfig struct { + Host string `json:"host"` + Port int `json:"port"` + TTL int64 `json:"ttl"` + } + + JWTConfig struct { + Secret string `json:"secret"` + Expires int `json:"expires"` + } + + TableConfig struct { + Path string `json:"path"` + } + + ConnectionInfo struct { + WebSocket string `json:"web_socket"` + HttpAddr string `json:"http_addr"` + GameSocketHost string `json:"game_socket_host"` + GameSocketPort int `json:"game_socket_port"` + } + + MysqlConfig struct { + Driver string `json:"driver"` + Host string `json:"host"` + Port string `json:"port"` + User string `json:"user"` + Password string `json:"password"` + DbName string `json:"db_name"` + } + + MongoConfig struct { + User string `json:"user"` + Password string `json:"password"` + SSLCaFile string `json:"ssl_ca_file"` + Host string `json:"host"` + Port int `json:"port"` + } + + GamesInfo struct { + ConnectInfo []ConnectionInfo `json:"connect_info"` + } + TencentVideo struct { + AppID int `json:"appID"` + Key string `json:"secretKey"` + CallBackKey string `json:"callBackKey"` + Prefix string `json:"prefix"` + TencentAsrCloudOS TencentAsrCloudOS `json:"tencent_asr_cloud_os"` + } + TencentAsr struct { + AppID string `json:"appID"` + Key string `json:"secretKey"` + CallBackKey string `json:"callBackKey"` + Prefix string `json:"prefix"` + } + TencentAsrCloudOS struct { + Bucket string `json:"bucket"` + + Region string `json:"region"` + + BucketURL string `json:"bucketURL"` + + CIURL string `json:"CIURL"` + + CIWorkflows map[string]TencentCIWorkflows `json:"CIWorkflows"` + } + TencentCIWorkflows struct { + Name string `json:"name"` + + WorkflowId string `json:"workflowId"` + } + + TencentCloudOS struct { + BucketURL string `json:"bucketURL"` + ServiceURL string `json:"serviceURL"` + BatchURL string `json:"batchURL"` + CIURL string `json:"CIURL"` + } + + TencentSdkCommon struct { + SecretId string `json:"secretId"` + SecretKey string `json:"secretKey"` + } + BackHttp struct { + Scheme string `json:"scheme"` + Host string `json:"host"` + Xkey string `json:"xkey"` + } + Us struct { + PublicKey string `json:"public_key"` + PrivateKey string `json:"private_key"` + BucketHost string `json:"bucket_host"` + BucketName string `json:"bucket_name"` + FileHost string `json:"file_host"` + VerifyUploadMD5 bool `json:"verfiy_upload_md5"` + Endpoint string `json:"endpoint"` + UsHostPath string `json:"us_host_path"` + } + + AIServer struct { + Scheme string `json:"scheme"` + Host string `json:"host"` + } + + KafkaConfig struct { + Brokers []string `json:"brokers"` + ChunkSize int `json:"chunkSize"` + FlushInterval int `json:"flushInterval"` + } + + Elecnest struct { + Scheme string `json:"scheme"` + Host string `json:"host"` + } + + WechatMini struct { + Scheme string `json:"scheme"` + Host string `json:"host"` + EnvVersion string `json:"envVersion"` + GetWxACodeUnLimitPath string `json:"getWxACodeUnLimitPath"` + GenerateUrlLinkPath string `json:"generateUrlLinkPath"` + } + + CommonConfig struct { + Logger LoggerConfig `json:"logger"` + Redis RedisConfig `json:"redis"` + Etcd EtcdConfig `json:"etcd"` + Tables TableConfig `json:"tables"` + JWT JWTConfig `json:"jwt"` + MysqlConfig MysqlConfig `json:"mysql"` + MongoConfig MongoConfig `json:"mongo"` + Kafka KafkaConfig `json:"kafka"` + TencentVideo TencentVideo `json:"tencent_video_call"` + TencentCloudOS TencentCloudOS `json:"tencent_cloud_os"` + TencentSdkCommon TencentSdkCommon `json:"tencent_sdk_common"` + GamesInfo GamesInfo `json:"games"` + Back BackHttp `json:"back_http"` + Us Us `json:"us"` + AIServer AIServer `json:"ai_server"` + Elecnest *Elecnest `json:"elecnest"` + OpenLog bool `json:"open_log"` + WechatMini *WechatMini `json:"wechat_mini"` + } +) diff --git a/Server/common/db/etcd/etcd.go b/Server/common/db/etcd/etcd.go new file mode 100644 index 0000000..bae52cf --- /dev/null +++ b/Server/common/db/etcd/etcd.go @@ -0,0 +1,29 @@ +package etcd + +import ( + commonConfig "common/config" + "fmt" + clientv3 "go.etcd.io/etcd/client/v3" +) + +var etcdClient *clientv3.Client + +func Init(cfg *commonConfig.EtcdConfig) error { + client, err := clientv3.New(clientv3.Config{ + Endpoints: []string{fmt.Sprintf("%v:%v", cfg.Host, cfg.Port)}, + DialTimeout: 0, + }) + etcdClient = client + return err +} + +func Close() error { + if etcdClient != nil { + return etcdClient.Close() + } + return nil +} + +func Client() *clientv3.Client { + return etcdClient +} diff --git a/Server/common/db/mongo/mongo.go b/Server/common/db/mongo/mongo.go new file mode 100644 index 0000000..29f7311 --- /dev/null +++ b/Server/common/db/mongo/mongo.go @@ -0,0 +1,77 @@ +package mongo + +import ( + commonConfig "common/config" + "common/log" + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "go.mongodb.org/mongo-driver/event" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "io/ioutil" +) + +var ( + MDB *mongo.Client + DefaultDB *mongo.Database + ChatDB *mongo.Database + ErrNoDocuments = mongo.ErrNoDocuments +) + +func Init(cfg *commonConfig.MongoConfig) error { + dialect := "" + if len(cfg.User) > 0 && len(cfg.Password) > 0 { + dialect = fmt.Sprintf("mongodb://%s:%s@%s:%d/admin?appname=MongoDB", cfg.User, cfg.Password, cfg.Host, cfg.Port) + } else { + dialect = fmt.Sprintf("mongodb://%s:%d/admin?readPreference=primary&appname=MongoDB&ssl=false", cfg.Host, cfg.Port) + } + var clientOptions *options.ClientOptions + if len(cfg.SSLCaFile) > 0 { + data, err := ioutil.ReadFile(cfg.SSLCaFile) + if err != nil { + return err + } + dialect += "&ssl=true" + roots := x509.NewCertPool() + roots.AppendCertsFromPEM(data) + clientOptions = options.Client().ApplyURI(dialect).SetTLSConfig(&tls.Config{ + RootCAs: roots, + //InsecureSkipVerify: true, + }).SetRetryWrites(false) + } else { + dialect += "&ssl=false" + clientOptions = options.Client().ApplyURI(dialect).SetRetryWrites(false) + } + monitor := &event.CommandMonitor{ + Started: func(ctx context.Context, startedEvent *event.CommandStartedEvent) { + log.Infof("mongoDB Command started %v %v", startedEvent.CommandName, startedEvent.Command.String()) + }, + Failed: func(ctx context.Context, failedEvent *event.CommandFailedEvent) { + log.Errorf("mongoDB Command error %v %v", failedEvent.CommandName, failedEvent.Failure) + }, + } + clientOptions.SetMonitor(monitor) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return err + } + + err = client.Ping(context.Background(), nil) + if err != nil { + return err + } + + MDB = client + DefaultDB = MDB.Database("ecosmos") + ChatDB = MDB.Database("ecosmos_chat") + return nil +} + +func Close() error { + if MDB != nil { + return MDB.Disconnect(context.Background()) + } + return nil +} diff --git a/Server/common/db/mysql/mysql.go b/Server/common/db/mysql/mysql.go new file mode 100644 index 0000000..c285379 --- /dev/null +++ b/Server/common/db/mysql/mysql.go @@ -0,0 +1,90 @@ +package mysql + +import ( + commonConfig "common/config" + "common/log" + "common/query" + + "fmt" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "time" +) + +const ( + DBNameEcosmos = "ecosmos" + DBNameEcosmosShop = "ecosmos_shop" +) + +var dbs = make(map[string]*gorm.DB) +var queryS = make(map[string]*query.Query) + +func Init(cfg *commonConfig.MysqlConfig) error { + db, err := initOneDB(cfg, DBNameEcosmosShop) + if err != nil { + return err + } + queryS[DBNameEcosmosShop] = query.Use(db) + + db, err = initOneDB(cfg, DBNameEcosmos) + if err != nil { + return err + } + query.SetDefault(db) + queryS[DBNameEcosmos] = query.Q + return nil +} + +func initOneDB(cfg *commonConfig.MysqlConfig, dbName string) (*gorm.DB, error) { + dialect := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", cfg.User, cfg.Password, cfg.Host, cfg.Port, dbName) + db, err := gorm.Open( + mysql.New( + mysql.Config{ + DSN: dialect, + }, + ), + &gorm.Config{ + TranslateError: true, + }, + ) + if err != nil { + return nil, err + } + sqlDb, err := db.DB() + if err != nil { + return nil, err + } + sqlDb.SetMaxOpenConns(50) + sqlDb.SetMaxIdleConns(5) + sqlDb.SetConnMaxLifetime(time.Second * 10) + if err = sqlDb.Ping(); err != nil { + return nil, err + } + + dbs[dbName] = db + return db.Debug(), nil +} + +func DBQuery(dbName string) *query.Query { + if q, ok := queryS[dbName]; ok { + return q + } + + log.Errorf("mysql db not init: %v", dbName) + return nil +} + +func Close() error { + fmt.Println("close mysql connect") + for _, db := range dbs { + sqlDb, err := db.DB() + if err != nil { + return err + } + err = sqlDb.Close() + if err != nil { + return err + } + } + return nil +} diff --git a/Server/common/db/mysql/sql_code/const.go b/Server/common/db/mysql/sql_code/const.go new file mode 100644 index 0000000..cd14e82 --- /dev/null +++ b/Server/common/db/mysql/sql_code/const.go @@ -0,0 +1,24 @@ +package sql_code + +const ( + Unread_HasRead = 1 + Unread_NotRead = 0 + IsAgree_Agree = 1 + IsAgree_Refuse = -1 + IsAgree_NoDeal = 0 + Online = 1 + Offline = 0 + Mail_UN_READ = 0 + + MAIL_UN_READ = 0 + + MAIL_READ_NOT_GET = 1 + + MAIL_READ_HAS_GET = 2 + + MAIL_HAS_DELETE = 3 + + MAIL_NO_EXTRAL = 0 + + MAIL_HAS_EXTRAL = 1 +) diff --git a/Server/common/db/redis/redisV9.go b/Server/common/db/redis/redisV9.go new file mode 100644 index 0000000..9d11617 --- /dev/null +++ b/Server/common/db/redis/redisV9.go @@ -0,0 +1,57 @@ +package redis + +import ( + commonConfig "common/config" + "context" + "crypto/tls" + "errors" + "fmt" + "github.com/redis/go-redis/v9" + "time" +) + +var ( + redisClient *redis.Client + redisNotInitErr = errors.New("redis is not initialized.") + keyNotExist = errors.New("key does not exist") + Nil = redis.Nil +) + +func Init(cfg *commonConfig.RedisConfig) error { + url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) + if cfg.Tls { + redisClient = redis.NewClient(&redis.Options{ + Addr: url, + Password: cfg.Auth, + DB: cfg.Db, + DialTimeout: 20 * time.Second, + Username: cfg.UserName, + TLSConfig: &tls.Config{}, + }) + } else { + redisClient = redis.NewClient(&redis.Options{ + Addr: url, + Password: cfg.Auth, + DB: cfg.Db, + DialTimeout: 20 * time.Second, + }) + } + + ret := redisClient.Ping(context.Background()) + //p := redisClient.Pipeline() + //l := p.HGetAll(xxx, xxx) + //s := p.Get(xxx, xx) + //p.Exec(xxx) + return ret.Err() +} + +func Close() error { + if redisClient != nil { + return redisClient.Close() + } + return nil +} + +func GetRedisClient() *redis.Client { + return redisClient +} diff --git a/Server/common/db/redis/redis_lock.go b/Server/common/db/redis/redis_lock.go new file mode 100644 index 0000000..af6f67b --- /dev/null +++ b/Server/common/db/redis/redis_lock.go @@ -0,0 +1,37 @@ +package redis + +import ( + "common/log" + "context" + "errors" + "time" +) + +var ReleaseLockExpire = errors.New("release redis lock expire key") + +func Acquire(ctx context.Context, key string, tag string, expiration time.Duration) (bool, error) { + success, err := redisClient.SetNX(ctx, key, tag, expiration).Result() + return success, err +} + +func Release(ctx context.Context, key string, tag string) error { + script := ` + if redis.call("GET", KEYS[1]) == ARGV[1] then + return redis.call("DEL", KEYS[1]) + else + return 0 + end` + + // 执行 Lua 脚本 + result, err := redisClient.Eval(ctx, script, []string{key}, tag).Result() + if err != nil { + return err + } + // 检查返回值 + if result == int64(0) { + log.Errorf("release redis lock expire key :%v", key) + return ReleaseLockExpire + } + + return nil +} diff --git a/Server/common/db/test_db.go b/Server/common/db/test_db.go new file mode 100644 index 0000000..55fd5b7 --- /dev/null +++ b/Server/common/db/test_db.go @@ -0,0 +1,49 @@ +package db + +import ( + "common/config" + "common/db/mongo" + "common/db/mysql" + "common/log" + "common/utils" + "fmt" + "github.com/jinzhu/configor" + "math/rand" + "os" +) + +// 用于单元测试初始化数据库 + +func InitTestDB(env string) { + wd, err := os.Getwd() + if err != nil { + fmt.Println("无法获取工作目录:", err) + return + } + + var commonCfg config.CommonConfig + err = configor.Load(&commonCfg, fmt.Sprintf(wd+"/config/common-config-%v.json", env)) + if err != nil { + panic(err) + } + + log.Init(&commonCfg.Logger) + utils.InitSnowflake(int64(rand.Intn(1000))) + //// Redis + //if err := redis.Init(&commonCfg.Redis); err != nil { + // panic(err) + //} + // MySQL + if err := mysql.Init(&commonCfg.MysqlConfig); err != nil { + panic(err) + } + // Mongo + if err := mongo.Init(&commonCfg.MongoConfig); err != nil { + panic(err) + } + //// Etcd + //if err := etcd.Init(&commonCfg.Etcd); err != nil { + // panic(err) + //} + +} diff --git a/Server/common/discover/common/define.go b/Server/common/discover/common/define.go new file mode 100644 index 0000000..f9b959c --- /dev/null +++ b/Server/common/discover/common/define.go @@ -0,0 +1,25 @@ +package common + +type ListenerType int32 + +const ( + ListenerTypeNewServer = 1 // 服务启动 + ListenerTypeCloseServer = 2 // 服务关闭 +) + +var ( + KeyDiscover = "discover" + KeyDiscoverService = KeyDiscover + "/service" +) + +var ( + KeyDiscoverGateway = KeyDiscoverService + "/gateway" + KeyDiscoverDatabase = KeyDiscoverService + "/database" +) + +// ServiceProvider 服务提供者 +type ServiceProvider struct { + Target string + SID string + Addr string +} diff --git a/Server/common/discover/common/tool.go b/Server/common/discover/common/tool.go new file mode 100644 index 0000000..79f37e9 --- /dev/null +++ b/Server/common/discover/common/tool.go @@ -0,0 +1,28 @@ +package common + +import ( + "common/db/etcd" + "common/log" + "context" + clientv3 "go.etcd.io/etcd/client/v3" +) + +func NewLeaseAndKeepAlive(ttl int64) (clientv3.LeaseID, error) { + lease, err := etcd.Client().Grant(context.Background(), ttl) + if err != nil { + return 0, err + } + chKeepAlive, err := etcd.Client().KeepAlive(context.Background(), lease.ID) + if err != nil { + return 0, err + } + go func() { + for r := range chKeepAlive { + if r == nil { + log.Errorf("lease timeout!") + return + } + } + }() + return lease.ID, nil +} diff --git a/Server/common/discover/grpc_client/conn.go b/Server/common/discover/grpc_client/conn.go new file mode 100644 index 0000000..72cabdb --- /dev/null +++ b/Server/common/discover/grpc_client/conn.go @@ -0,0 +1,41 @@ +package grpc_client + +import ( + "common/log" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" + "time" +) + +type GrpcConnection struct { + sid string + conn *grpc.ClientConn +} + +func NewGrpcConnection(sid, address string) (*GrpcConnection, error) { + p := &GrpcConnection{ + sid: sid, + } + conn, err := grpc.Dial( + address, + grpc.WithInsecure(), + grpc.WithKeepaliveParams( + keepalive.ClientParameters{ + Time: 30 * time.Second, // 保活探测包发送的时间间隔 + Timeout: 10 * time.Second, // 保活探测包的超时时间 + PermitWithoutStream: true, + }, + ), + //grpc.WithStatsHandler(&StatsHandler{}), + ) + if err != nil { + log.Errorf("create grpc err: %v, sid: %v, addr: %v", err, sid, address) + return nil, err + } + p.conn = conn + return p, nil +} + +func (g *GrpcConnection) GetConnection() *grpc.ClientConn { + return g.conn +} diff --git a/Server/common/discover/grpc_client/conn_mgr.go b/Server/common/discover/grpc_client/conn_mgr.go new file mode 100644 index 0000000..6d9a38c --- /dev/null +++ b/Server/common/discover/grpc_client/conn_mgr.go @@ -0,0 +1,62 @@ +package grpc_client + +import ( + "common/log" + "fmt" + "google.golang.org/grpc" + "math/rand" +) + +type GrpcConnectionMgr struct { + poolM map[string]*GrpcConnection + poolS []*GrpcConnection +} + +func NewGrpcConnectionMgr() *GrpcConnectionMgr { + return &GrpcConnectionMgr{ + poolM: make(map[string]*GrpcConnection), + poolS: make([]*GrpcConnection, 0), + } +} + +func (p *GrpcConnectionMgr) Store(sid, addr string) { + pool, err := NewGrpcConnection(sid, addr) + if err != nil { + log.Errorf("create grpc err: %v, sid: %v, addr: %v", err, sid, addr) + return + } + p.poolM[sid] = pool + p.poolS = append(p.poolS, pool) +} + +func (p *GrpcConnectionMgr) Delete(sid string) int { + delete(p.poolM, sid) + for i, pool := range p.poolS { + if pool.sid == sid { + p.poolS = append(p.poolS[:i], p.poolS[i+1:]...) + break + } + } + return len(p.poolS) +} + +func (p *GrpcConnectionMgr) Load(sid ...string) (*grpc.ClientConn, error) { + var pool *GrpcConnection + if len(sid) > 0 && len(sid[0]) > 0 { + pool = p.poolM[sid[0]] + } else { + pool = p.poolS[rand.Intn(len(p.poolS))] + } + if pool == nil { + return nil, fmt.Errorf("cannot find connection") + } + return pool.GetConnection(), nil +} + +func (p *GrpcConnectionMgr) LoadAll() map[string]*grpc.ClientConn { + sidM := make(map[string]*grpc.ClientConn) + for sid, pool := range p.poolM { + sidM[sid] = pool.GetConnection() + } + return sidM +} diff --git a/Server/common/discover/grpc_client/stats.go b/Server/common/discover/grpc_client/stats.go new file mode 100644 index 0000000..54e8254 --- /dev/null +++ b/Server/common/discover/grpc_client/stats.go @@ -0,0 +1,45 @@ +package grpc_client + +import ( + "context" + "fmt" + "google.golang.org/grpc/stats" +) + +// 1. 实现 stats.Handler 接口 +type StatsHandler struct{} + +func (h *StatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { + // 给 RPC 调用打标签(例如记录方法名) + return context.WithValue(ctx, "rpc_method", info.FullMethodName) +} + +func (h *StatsHandler) HandleRPC(ctx context.Context, s stats.RPCStats) { + // 处理 RPC 统计信息 + switch t := s.(type) { + case *stats.Begin: + fmt.Printf("RPC started: %s\n", ctx.Value("rpc_method")) + case *stats.End: + fmt.Printf("RPC finished: %s (duration: %v)\n", + ctx.Value("rpc_method"), t.EndTime.Sub(t.BeginTime)) + case *stats.InPayload: + fmt.Printf("Received %d bytes\n", t.WireLength) + case *stats.OutPayload: + fmt.Printf("Sent %d bytes\n", t.WireLength) + } +} + +func (h *StatsHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context { + // 给连接打标签 + return ctx +} + +func (h *StatsHandler) HandleConn(ctx context.Context, s stats.ConnStats) { + // 处理连接事件 + switch s.(type) { + case *stats.ConnBegin: + fmt.Println("Connection established") + case *stats.ConnEnd: + fmt.Println("Connection closed") + } +} diff --git a/Server/common/discover/listener.go b/Server/common/discover/listener.go new file mode 100644 index 0000000..d702619 --- /dev/null +++ b/Server/common/discover/listener.go @@ -0,0 +1,92 @@ +package discover + +import ( + "common/db/etcd" + "common/discover/common" + "context" + "go.etcd.io/etcd/api/v3/mvccpb" + clientv3 "go.etcd.io/etcd/client/v3" + "strings" + "sync" +) + +var ( + wg = &sync.WaitGroup{} + listenerMU = &sync.RWMutex{} + listener = make(map[common.ListenerType][]func(data any)) + stopFunc context.CancelFunc +) + +// RegisterListener 注册服务变动监听 +func RegisterListener(t common.ListenerType, cb func(data any)) { + listenerMU.Lock() + defer listenerMU.Unlock() + arr := listener[t] + if arr == nil { + arr = make([]func(data any), 0) + } + arr = append(arr, cb) + listener[t] = arr +} + +// 根据类型触发回调 +func onCBByType(t common.ListenerType, data any) { + listenerMU.RLock() + defer listenerMU.RUnlock() + for _, f := range listener[t] { + f(data) + } +} + +func Listen() { + var stopCtx context.Context + stopCtx, stopFunc = context.WithCancel(context.Background()) + wg.Add(1) + go func() { + defer wg.Done() + // 服务 + serviceAll, _ := etcd.Client().Get(stopCtx, common.KeyDiscoverService, clientv3.WithPrefix()) + for _, kv := range serviceAll.Kvs { + onServerChange(clientv3.EventTypePut, string(kv.Key), string(kv.Value)) + } + chService := etcd.Client().Watch(stopCtx, common.KeyDiscoverService, clientv3.WithPrefix(), clientv3.WithRev(serviceAll.Header.Revision+1)) + for { + select { + case msg := <-chService: + for _, event := range msg.Events { + onServerChange(event.Type, string(event.Kv.Key), string(event.Kv.Value)) + } + case <-stopCtx.Done(): + return + } + } + }() +} + +// 服务发生变化 +func onServerChange(t mvccpb.Event_EventType, key, value string) { + split := strings.Split(key, "/") + if len(split) != 5 { + return + } + switch t { + case clientv3.EventTypePut: + onCBByType(common.ListenerTypeNewServer, &common.ServiceProvider{ + Target: common.KeyDiscoverService + "/" + split[2], + SID: split[3], + Addr: value, + }) + case clientv3.EventTypeDelete: + onCBByType(common.ListenerTypeCloseServer, &common.ServiceProvider{ + Target: common.KeyDiscoverService + "/" + split[2], + SID: split[3], + }) + } +} + +func Close() { + if stopFunc != nil { + stopFunc() + wg.Wait() + } +} diff --git a/Server/common/discover/server.go b/Server/common/discover/server.go new file mode 100644 index 0000000..d64e637 --- /dev/null +++ b/Server/common/discover/server.go @@ -0,0 +1,101 @@ +package discover + +import ( + "common/db/etcd" + "common/discover/common" + "common/discover/grpc_client" + "common/log" + "context" + "fmt" + clientv3 "go.etcd.io/etcd/client/v3" + "google.golang.org/grpc" + "sync" +) + +// 大量读少量写的情况下,读写锁比同步Map更高效 +var ( + serverMU = sync.RWMutex{} + conn = make(map[string]*grpc_client.GrpcConnectionMgr) + serverLeaseM = make(map[string]clientv3.LeaseID) +) + +func init() { + RegisterListener(common.ListenerTypeNewServer, onServerStart) + RegisterListener(common.ListenerTypeCloseServer, onServerStop) +} + +// FindServer 根据SID或随机查找服务 +func FindServer(target string, sid ...string) (*grpc.ClientConn, error) { + serverMU.RLock() + defer serverMU.RUnlock() + if v, ok := conn[target]; ok { + return v.Load(sid...) + } + return nil, fmt.Errorf("cannot find server") +} + +func FindServerAll(target string) map[string]*grpc.ClientConn { + serverMU.RLock() + defer serverMU.RUnlock() + if v, ok := conn[target]; ok { + return v.LoadAll() + } + return make(map[string]*grpc.ClientConn) +} + +// RegisterGrpcServer 注册服务提供者 +func RegisterGrpcServer(target, sid, addr string, ttl int64) error { + serverMU.Lock() + defer serverMU.Unlock() + leaseID, err := common.NewLeaseAndKeepAlive(ttl) + if err != nil { + return err + } + _, err = etcd.Client().Put(context.Background(), target+"/"+sid, addr, clientv3.WithLease(leaseID)) + if err != nil { + return err + } + serverLeaseM[sid] = leaseID + return nil +} + +// UnRegisterGrpcServer 解注册服务提供者 +func UnRegisterGrpcServer(sid string) { + serverMU.Lock() + defer serverMU.Unlock() + if leaseID, ok := serverLeaseM[sid]; ok { + _, err := etcd.Client().Revoke(context.Background(), leaseID) + if err != nil { + log.Errorf("server.go UnRegisterGrpcServer err: %v", err) + } + delete(serverLeaseM, sid) + } +} + +// 某个服务启动了 +func onServerStart(data any) { + if provider, ok := data.(*common.ServiceProvider); ok { + serverMU.Lock() + defer serverMU.Unlock() + if v, ok := conn[provider.Target]; ok { + v.Store(provider.SID, provider.Addr) + } else { + mgr := grpc_client.NewGrpcConnectionMgr() + mgr.Store(provider.SID, provider.Addr) + conn[provider.Target] = mgr + } + } +} + +// 某个服务关闭了 +func onServerStop(data any) { + if provider, ok := data.(*common.ServiceProvider); ok { + serverMU.Lock() + defer serverMU.Unlock() + if v, ok := conn[provider.Target]; ok { + if v.Delete(provider.SID) == 0 { + delete(conn, provider.Target) + } + } + } +} diff --git a/Server/common/discover/service/client_gateway.go b/Server/common/discover/service/client_gateway.go new file mode 100644 index 0000000..768bd66 --- /dev/null +++ b/Server/common/discover/service/client_gateway.go @@ -0,0 +1,24 @@ +package service + +import ( + "common/discover" + "common/discover/common" + "common/discover/service/game/game_pb" +) + +func GatewayNewClient(sid ...string) (game_pb.GameClient, error) { + c, err := discover.FindServer(common.KeyDiscoverGateway, sid...) + if err != nil { + return nil, err + } + return game_pb.NewGameClient(c), nil +} + +func GatewayNewBroadcastClient() map[string]game_pb.GameClient { + clientM := make(map[string]game_pb.GameClient) + connM := discover.FindServerAll(common.KeyDiscoverGateway) + for sid, conn := range connM { + clientM[sid] = game_pb.NewGameClient(conn) + } + return clientM +} diff --git a/Server/common/discover/service/service.go b/Server/common/discover/service/service.go new file mode 100644 index 0000000..55db583 --- /dev/null +++ b/Server/common/discover/service/service.go @@ -0,0 +1,94 @@ +package service + +import ( + "common/discover" + "common/discover/common" + "common/log" + "common/utils" + "context" + "fmt" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" + "os" + "strconv" + "sync" + "time" +) + +type IService interface { + Init(addr string) + Close() +} + +type ServiceBase struct { + Target string + SID string + Serve *grpc.Server + EtcdTTL int64 + OnInit func(serve *grpc.Server) + OnClose func() + + wg *sync.WaitGroup +} + +func (s *ServiceBase) Init(addr string) { + s.wg = &sync.WaitGroup{} + s.wg.Add(1) + s.SID = utils.SnowflakeInstance().Generate().String() + go func() { + defer s.wg.Done() + defer s.OnClose() + defer discover.UnRegisterGrpcServer(s.SID) + + pMin, _ := strconv.ParseInt(os.Getenv("P_MIN"), 10, 64) + pMax, _ := strconv.ParseInt(os.Getenv("P_MAX"), 10, 64) + if pMin == 0 || pMax == 0 || pMin > pMax { + log.Errorf(" %v init err: pMin or pMax is 0 or pMin > pMax", s.Target) + return + } + + // 服务注册 + lis, port, err := common.ListenRandomPort(pMin, pMax) + if lis == nil { + log.Errorf(" %v ListenRandomPort err: %v", s.Target, err) + return + } + + s.Serve = grpc.NewServer( + grpc.UnaryInterceptor( + func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + defer func() { + if r := recover(); r != nil { + log.Errorf("server Panic: %v", r) + } + }() + resp, err = handler(ctx, req) + return + }, + ), + grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + MinTime: 20 * time.Second, + PermitWithoutStream: true, + }), + ) + s.OnInit(s.Serve) + + // 服务注册 + if err = discover.RegisterGrpcServer(s.Target, s.SID, fmt.Sprintf("%v:%d", addr, port), s.EtcdTTL); err != nil { + log.Errorf("%v RegisterGrpcServer err: %v", s.Target, err) + return + } + if err = s.Serve.Serve(lis); err != nil { + log.Errorf(" %v Serve err: %v", s.Target, err) + return + } + log.Infof("%v server stop.", s.Target) + }() +} + +func (s *ServiceBase) Close() { + if s.Serve != nil { + s.Serve.Stop() + s.wg.Wait() + } +} diff --git a/Server/common/go.mod b/Server/common/go.mod new file mode 100644 index 0000000..0b9d462 --- /dev/null +++ b/Server/common/go.mod @@ -0,0 +1,52 @@ +module common + +go 1.23.1 + +require ( + github.com/getsentry/sentry-go v0.34.0 + github.com/jinzhu/configor v1.2.2 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/redis/go-redis/v9 v9.10.0 + go.etcd.io/etcd/api/v3 v3.6.1 + go.etcd.io/etcd/client/v3 v3.6.1 + go.mongodb.org/mongo-driver v1.17.4 + go.uber.org/zap v1.27.0 + google.golang.org/grpc v1.71.1 + google.golang.org/protobuf v1.36.5 + gorm.io/driver/mysql v1.6.0 + gorm.io/gorm v1.30.0 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/BurntSushi/toml v1.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/Server/common/go.sum b/Server/common/go.sum new file mode 100644 index 0000000..d7d494a --- /dev/null +++ b/Server/common/go.sum @@ -0,0 +1,178 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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/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/getsentry/sentry-go v0.34.0 h1:1FCHBVp8TfSc8L10zqSwXUZNiOSF+10qw4czjarTiY4= +github.com/getsentry/sentry-go v0.34.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/jinzhu/configor v1.2.2 h1:sLgh6KMzpCmaQB4e+9Fu/29VErtBUqsS2t8C9BNIVsA= +github.com/jinzhu/configor v1.2.2/go.mod h1:iFFSfOBKP3kC2Dku0ZGB3t3aulfQgTGJknodhFavsU8= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= +github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/etcd/api/v3 v3.6.1 h1:yJ9WlDih9HT457QPuHt/TH/XtsdN2tubyxyQHSHPsEo= +go.etcd.io/etcd/api/v3 v3.6.1/go.mod h1:lnfuqoGsXMlZdTJlact3IB56o3bWp1DIlXPIGKRArto= +go.etcd.io/etcd/client/pkg/v3 v3.6.1 h1:CxDVv8ggphmamrXM4Of8aCC8QHzDM4tGcVr9p2BSoGk= +go.etcd.io/etcd/client/pkg/v3 v3.6.1/go.mod h1:aTkCp+6ixcVTZmrJGa7/Mc5nMNs59PEgBbq+HCmWyMc= +go.etcd.io/etcd/client/v3 v3.6.1 h1:KelkcizJGsskUXlsxjVrSmINvMMga0VWwFF0tSPGEP0= +go.etcd.io/etcd/client/v3 v3.6.1/go.mod h1:fCbPUdjWNLfx1A6ATo9syUmFVxqHH9bCnPLBZmnLmMY= +go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= +go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= +gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= +gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= diff --git a/Server/common/log/level.go b/Server/common/log/level.go new file mode 100644 index 0000000..b9c5eea --- /dev/null +++ b/Server/common/log/level.go @@ -0,0 +1,23 @@ +package log + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +var logLevelMap = map[string]zapcore.Level{ + "panic": zap.DPanicLevel, + "fatal": zap.FatalLevel, + "error": zap.ErrorLevel, + "warn": zap.WarnLevel, + "info": zap.InfoLevel, + "debug": zap.DebugLevel, +} + +// GetLogLevel get the logLevel from logLevelName +func GetLogLevel(logLevelName string) zapcore.Level { + if v, ok := logLevelMap[logLevelName]; ok { + return v + } + return zap.DebugLevel +} diff --git a/Server/common/log/log.go b/Server/common/log/log.go new file mode 100644 index 0000000..1587078 --- /dev/null +++ b/Server/common/log/log.go @@ -0,0 +1,66 @@ +package log + +import "go.uber.org/zap" + +var globalLogger *zap.SugaredLogger + +// SetLogger 设置日志记录器 +func SetLogger(logger *zap.SugaredLogger) { + if logger == nil { + return + } + globalLogger = logger +} + +func GetLogger() *zap.SugaredLogger { + return globalLogger +} + +// Debugf 打印调试模板日志 +func Debugf(format string, a ...interface{}) { + if globalLogger != nil { + globalLogger.Debugf(format, a...) + } +} + +// Infof 打印信息模板日志 +func Infof(format string, a ...interface{}) { + if globalLogger != nil { + globalLogger.Infof(format, a...) + } +} + +// Warnf 打印警告模板日志 +func Warnf(format string, a ...interface{}) { + if globalLogger != nil { + globalLogger.Warnf(format, a...) + } +} + +// Errorf 打印错误模板日志 +func Errorf(format string, a ...interface{}) { + if globalLogger != nil { + globalLogger.Errorf(format, a...) + } +} + +// Panicf 打印Panic模板日志 +func Panicf(format string, a ...interface{}) { + if globalLogger != nil { + globalLogger.Panicf(format, a...) + } +} + +// Fatalf 打印致命错误模板日志 +func Fatalf(format string, a ...interface{}) { + if globalLogger != nil { + globalLogger.Fatalf(format, a...) + } +} + +// Close 关闭日志 +func Close() { + if globalLogger != nil { + _ = globalLogger.Sync() + } +} diff --git a/Server/common/log/log_zap.go b/Server/common/log/log_zap.go new file mode 100644 index 0000000..946f627 --- /dev/null +++ b/Server/common/log/log_zap.go @@ -0,0 +1,62 @@ +package log + +import ( + commonConfig "common/config" + "github.com/natefinch/lumberjack" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" +) + +func Init(cfg *commonConfig.LoggerConfig) { + // 格式配置 + jsonConfig := zapcore.EncoderConfig{ + MessageKey: "M", + LevelKey: "L", + TimeKey: "T", + NameKey: "N", + CallerKey: "C", + FunctionKey: zapcore.OmitKey, + StacktraceKey: "S", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: zapcore.TimeEncoderOfLayout("01-02 15:04:05.000"), + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + EncodeName: nil, + ConsoleSeparator: " ", + } + + // 日志输出到控制台和文件 + writeSyncer := []zapcore.WriteSyncer{zapcore.AddSync(os.Stdout)} + if !cfg.Debug { + writeSyncer = append(writeSyncer, zapcore.AddSync(&lumberjack.Logger{ + Filename: "./logs/log.log", // 日志文件位置 + MaxSize: cfg.MaxSize, // 最大文件大小(MB) + MaxBackups: cfg.MaxBackUp, // 保留旧文件的最大个数 + MaxAge: cfg.MaxAge, // 保留旧文件的最大天数 + Compress: false, // 是否压缩/归档旧文件 + LocalTime: true, + })) + } + + var encoder zapcore.Encoder + if cfg.Debug { + encoder = zapcore.NewConsoleEncoder(jsonConfig) + } else { + encoder = zapcore.NewJSONEncoder(jsonConfig) + } + logger := zap.New(zapcore.NewCore( + encoder, + zapcore.NewMultiWriteSyncer(writeSyncer...), + zap.NewAtomicLevelAt(GetLogLevel(cfg.Level)), + )) + if cfg.Debug { + logger = logger.WithOptions( + zap.AddCaller(), + zap.AddCallerSkip(1), + zap.AddStacktrace(zapcore.WarnLevel), + ) + } + SetLogger(logger.Sugar()) +} diff --git a/Server/common/log/sentry.go b/Server/common/log/sentry.go new file mode 100644 index 0000000..5a13f96 --- /dev/null +++ b/Server/common/log/sentry.go @@ -0,0 +1,135 @@ +package log + +import ( + "go.uber.org/zap/zapcore" + "time" +) +import "github.com/getsentry/sentry-go" + +type SentryCoreConfig struct { + Tags map[string]string + AttachStacktrace bool + Level zapcore.Level + FlushTimeout time.Duration + Hub *sentry.Hub + Platform string +} + +type sentryCore struct { + zapcore.LevelEnabler + client sentry.Client + cfg SentryCoreConfig + flushTimeout time.Duration + fields map[string]interface{} +} + +func (c *sentryCore) with(fs []zapcore.Field) *sentryCore { + m := make(map[string]interface{}, len(c.fields)) + for k, v := range c.fields { + m[k] = v + } + + enc := zapcore.NewMapObjectEncoder() + for _, f := range fs { + f.AddTo(enc) + } + + for k, v := range enc.Fields { + m[k] = v + } + + return &sentryCore{ + client: c.client, + cfg: c.cfg, + fields: m, + LevelEnabler: c.LevelEnabler, + } +} + +func (c *sentryCore) With(fs []zapcore.Field) zapcore.Core { + return c.with(fs) +} + +func (c *sentryCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if c.cfg.Level.Enabled(ent.Level) { + return ce.AddCore(ent, c) + } + return ce +} + +func (c *sentryCore) Write(ent zapcore.Entry, fs []zapcore.Field) error { + clone := c.with(fs) + event := sentry.NewEvent() + event.Message = ent.Message + event.Timestamp = ent.Time + event.Level = sentryLevel(ent.Level) + event.Platform = c.cfg.Platform + event.Extra = clone.fields + event.Tags = c.cfg.Tags + + if c.cfg.AttachStacktrace { + trace := sentry.NewStacktrace() + if trace != nil { + event.Exception = []sentry.Exception{ + { + Type: ent.Message, + Value: ent.Caller.TrimmedPath(), + Stacktrace: trace, + }, + } + } + } + + hub := c.cfg.Hub + if hub == nil { + hub = sentry.CurrentHub() + } + _ = c.client.CaptureEvent(event, nil, hub.Scope()) + + if ent.Level > zapcore.ErrorLevel { + c.client.Flush(c.flushTimeout) + } + return nil +} + +func (c *sentryCore) Sync() error { + c.client.Flush(c.flushTimeout) + return nil +} + +func NewSentryCore(cfg SentryCoreConfig, sentryClient *sentry.Client) zapcore.Core { + core := sentryCore{ + client: *sentryClient, + cfg: cfg, + LevelEnabler: cfg.Level, + flushTimeout: 3 * time.Second, + fields: make(map[string]interface{}), + } + + if cfg.FlushTimeout > 0 { + core.flushTimeout = cfg.FlushTimeout + } + + return &core +} + +func sentryLevel(lvl zapcore.Level) sentry.Level { + switch lvl { + case zapcore.DebugLevel: + return sentry.LevelDebug + case zapcore.InfoLevel: + return sentry.LevelInfo + case zapcore.WarnLevel: + return sentry.LevelWarning + case zapcore.ErrorLevel: + return sentry.LevelError + case zapcore.DPanicLevel: + return sentry.LevelFatal + case zapcore.PanicLevel: + return sentry.LevelFatal + case zapcore.FatalLevel: + return sentry.LevelFatal + default: + return sentry.LevelFatal + } +}