Commit b17c2009 authored by hujiebin's avatar hujiebin

feat:初始化

parents
Pipeline #1752 failed with stages
MODE=local
.idea
/docs/
\ No newline at end of file
swag:
swag init
\ No newline at end of file
# hilo群组中心
# 项目架构
+ .env 项目环境配置,正服/测服会用服务器上的配置覆盖
+ run.sh 守护脚本
+ Makefile 构建文件
+ main.go 入口文件
+ *.ini 配置文件,测服用debug.ini,正服用release.ini
+ 目录分层说明(`着重注意命名规则`,下划线_分割职责)
+ test: 单元测试目录
+ _const: 常量
+ 子目录 enum,放业务定义常量
+ 命名规则 `*_e`,如game_e
+ 子目录 redis_key,放redis的key
+ 命名规则 `*_k`,如game_k
+ cron: 定时任务
+ 统一入口cron.go
+ 子目录命名规则 `*_cron`,如game_cron
+ myerr: 错误码
+ 子目录 bizerr,放业务错误
+ req: 请求参数
+ request.go: 定义一些常用方法
+ 子目录jwt: 鉴权相关
+ 子目录
+ 命名规则 `*_req`,如game_req
+ resp: 返回结构体,通用结构体
+ cv: 客户端需要结构体
+ 子目录
+ 命名规则 `*_cv`,如game_cv
+ route: 路由定义
+ 根目录
+ errorHandler.go 错误处理
+ middleHandle.go 中间件
+ router.go 路由定义
+ util.go 工具包
+ 子目录,业务路由定义
+ 命名规则 `*_r`,如game_r
+ domain: 领域层
+ cache: 缓存层
+ moduleRedis.go 带有model的通用redis方法
+ 子目录
+ 命名规则 `*_c`,如user_c
+ event: 事件层
+ 子目录
+ 命名规则 `*_ev`,如game_ev
+ model: 模型层
+ repo.go 持久化
+ 子目录
+ 命名规则 `*_m`,如game_m/user_m
+ service: 服务层,主要是开启事务和发事件
+ 子目录
+ 命名规则 `*_s`,如game_s
\ No newline at end of file
package res_e
import "git.hilo.cn/hilo-common/resource/mysql"
type MsgIdType = uint
const (
DEFAULT_LANG = "en"
MSG_ID_GAME_CREATE MsgIdType = 1001
MSG_ID_GAME_JOIN MsgIdType = 1002
)
type ResMedalType = mysql.Type
const (
Wealth ResMedalType = 1
Charm ResMedalType = 2
LoveForAll ResMedalType = 3
StartForAll ResMedalType = 4
MoonForAll ResMedalType = 5
MarryMe ResMedalType = 6
RoomRocket ResMedalType = 7
Actity ResMedalType = 8
FruitKing ResMedalType = 9
BoxKing ResMedalType = 10
Helicopter ResMedalType = 11
Roadster ResMedalType = 12
Watermelon ResMedalType = 13
Kiss ResMedalType = 14
Love ResMedalType = 15
Chick ResMedalType = 16
SportsCar ResMedalType = 17
Rocket ResMedalType = 18
Tower ResMedalType = 19
Eagle ResMedalType = 20
Lion ResMedalType = 21
Vacation ResMedalType = 22
RomanticNight ResMedalType = 23
SweetCouple ResMedalType = 24
Castle ResMedalType = 25
WeddingCar ResMedalType = 26
VideoChat ResMedalType = 27
)
type ResMedalScope = mysql.Type
const (
//私有,只能时管理人发放
Private ResMedalScope = 1
//公有,自己获取
Public ResMedalScope = 2
)
package user_e
import "git.hilo.cn/hilo-common/resource/mysql"
type ThirdPartyType = mysql.Type
const (
Phone ThirdPartyType = 1
Google ThirdPartyType = 2
Facebook ThirdPartyType = 3
Apple ThirdPartyType = 4
WeChat ThirdPartyType = 5
)
type UserStatus mysql.Type
const (
//正常
UserStatusNormal UserStatus = 1
//冻结
UserStatusFrozen UserStatus = 2
)
type UserVipType = mysql.Type
const (
// 男
UserMan = 1
// 女
UserWomen = 2
)
type AwardType int
const (
AwardTypeDiamond AwardType = 1 // 钻石
AwardTypeGift AwardType = 2 // 背包礼物
AwardTypeHeadwear AwardType = 3 // 头饰
AwardTypeNoble AwardType = 4 // 贵族
AwardTypeRide AwardType = 5 // 座驾
)
package redis_key
import (
"os"
)
// 替换keyFmt中的${var}变量s
// param keyFmt 如 a_b_${var1}_${var2}
// param arg 是个数组,按照下标替换${var},越界就用var1字样
// return a_b_var1_var2
func ReplaceKey(keyFmt string, arg ...string) string {
n := len(arg)
if n <= 0 {
return keyFmt
}
var i int
return os.Expand(keyFmt, func(s string) (r string) {
if i >= n {
return s
}
r = arg[i]
i++
return
})
}
\ No newline at end of file
package user_k
import (
"fmt"
"git.hilo.cn/hilo-common/resource/mysql"
"hilo-group/_const/redis_key"
)
const (
UserPrefix = "user:"
UserTinyInfoStr = UserPrefix + "tiny:${userId}" // value:json(cv.CvUserTiny)
UserExternalIdToUIdStr = UserPrefix + "externalId:${externalId}" // value:userId
UserCodeToUIdStr = UserPrefix + "code:${externalId}" // value:userId
UserTradeUnionIds = "user:trade:union:ids" // type:string,value json(uids)
)
// 获取用户简要信息缓存key
func GetUserTinyKey(userId mysql.ID) string {
return redis_key.ReplaceKey(UserTinyInfoStr, fmt.Sprintf("%d", userId))
}
// 获取externalId到userId
func GetExternalIdToUidKey(externalId mysql.Str) string {
return redis_key.ReplaceKey(UserExternalIdToUIdStr, externalId)
}
// 获取code到userId
func GetCodeToUidKey(code mysql.Str) string {
return redis_key.ReplaceKey(UserCodeToUIdStr, code)
}
[DATABASE]
MYSQL_HOST=47.244.34.27:3306
MYSQL_USERNAME=root
MYSQL_PASSWORD=yX0jPAhO0I4s2zlA
MYSQL_DB=hilo
[DATABASECODE]
MYSQL_HOST=47.244.34.27:3306
MYSQL_USERNAME=root
MYSQL_PASSWORD=yX0jPAhO0I4s2zlA
MYSQL_DB=hilo_code
[REDIS]
REDIS_HOST=47.244.34.27:6379
REDIS_PASSWORD=8QZ9JD1zLvPR3yHf
[JWT]
SECRET=hilo1632
ISSUER_API=hiloApi
ISSUER_Mgr=hiloMgr
EXPIRE=240h
[GAMEJWT]
SECRET=hilo1632
ISSUER_CLIENT=hiloClient
ISSUER_SERVER=hiloServer
EXPIRE=240h
[APP]
MASTER=true
BIZ_SECRET=biz
WEB_SECRET=webHilo1258
OPERATION_SECRET=operation1258236
SUPERUSER=2701,2831
OFFICIAL_STAFF=2701,2831,3411,2511
OFFICIAL_GROUP=@TGS#3FDW3MPHZ
MINIMAL_VERSION_ANDROID=22001
MINIMAL_VERSION_IOS=22000
MODERATE=TENCENT
[OSS]
OSS_ACCESS_KEY_ID=LTAIxdazV2pCuV3T
OSS_ACCESS_KEY_SECRET=zuAnreAXQ6vlAKnvvmolFLfb1N5w5S
OSS_ROLE_ARN=acs:ram::1509841556585969:role/aliyunosstokengeneratorrole
OSS_END_POINT=http://oss-accelerate.aliyuncs.com
OSS_BUCKET=starvoice
OSS_CDN=https://oss.chathot.me/
OSS_EXPIRED_TIME=3600
OSS_STS_POINT=me-east-1
OSS_STS=sts-faceline-demo
OSS_STS_AES=484194d4d0f968a7
[AWS]
AWS_BUCKET=starchat
AWS_CDN=https://image.whoisamy.shop/
AWS_DIR=hilo/
CONFIDENCE=80
[RONGYUN]
RONG_CLOUD_APP_KEY=pvxdm17jpe9tr
RONG_CLOUD_APP_SECRET=rI4giiKWaBS4
RONG_CLOUD_URL=https://api-sg01.ronghub.com
[TENCENTYUN]
TENCENTYUN_APP_ID=1400548270
TENCENTYUN_KEY=321bd60f73096b059c7350f1cd97d51028850b34fa58c5c0d26bb4a19e783de8
TX_OVERSEA_APP_ID=40000066
TX_OVERSEA_KEY=3ab68ea5bddc8774d90b8c764ae71188914bd5fd06f30b28790c51e44ca7885c
[EMAS]
REGION_ID=cn-hangzhou
ACCESS_KEY_ID=LTAIdQZv5H1kNZp5
ACCESS_KEY_SECRET=UnwY0ClDkqBMLwPx3OJJiLYyk9xYLO
ANDROID_APP_KEY=30250713
ANDROID_APP_SECRET=cae7b9a9d3e54577d2c3b60bf6d23047
IOS_APP_KEY=30790728
IOS_APP_SECRET=4fd69ca084c67d4b5a8d15452f0af26a
APNS=DEV
[AGORA]
APP_ID=fc3e087f701b4f788099e1924c3cc7b0
APP_CERTIFICATE=ff29c100a613433db82324e8411eabc8
CUSTOMER_KEY=6b132c0ff7164560a2bc53fda06ea85a
CUSTOMER_SECRET=eedad2cd16d24834990d5450ace9f1ce
CALLBACK_SECRET=n_ZizS_N8
[CHECKOUT]
AUTHORIZATION=sk_test_9b5e771c-5a3f-4a8d-a4da-31b19bd43d83
URL=https://api.sandbox.checkout.com/hosted-payments
H5=http://test.chathot.me/action/hiloHtml/22_05_30_recharge/topup.html
HILO_SECRET_KEY=sk_test_dfbaa3b6-135d-4376-9996-2089b7d8a086
[MATCH]
MATCH_FREE_TIME=60
MATCH_FREE_TIME_VIP=60
MATCH_ADD_TIME_FREE=90
MATCH_AGORA_TIME=30
MATCH_CYCLE=8
MATCH_USER_EXPIRES=480
MATCH_SUCCESS_WAIT_DURATION=10
MATCH_SUCCESS_SINGLE_WAIT_TIME_IN_SEC=12
MATCH_SUCCESS_DUAL_WAIT_TIME_IN_SEC=15
[ONLINE]
ONLINE_CYCLE=600
ONLINE_USER_EXPIRES=259200
[VIDEO]
VIDEO_DAILY_FREE_NUM=20
VIDEO_FREE_TIME=60
VIDEO_FREE_TIME_VIP=300
VIDEO_ADD_TIME_FREE=60
VIDEO_AGORA_TIME=30
VIDEO_MINUTE_NORMAL=30
VIDEO_MINUTE_UNION=30
[SESSION]
SESSION_DAILY_FREE_NUM=50
GUILD_USER_HELLO_DAY=30
[BEAN]
DIAMOND_BEAN_RATE=90
[H5]
USER_LEVEL=http://test.chathot.me/action/hiloHtml/hiloUserLevel/index.html
GROUP_SUPPORT=http://test.chathot.me/action/activityhtml/21_12_06/page.html
LUCKY_WHEEL=https://h5.whoisamy.shop/action/activityhtml/21_12_30/page.html
WEEKLY_STAR=http://test.chathot.me/action/hiloHtml/lxt_h5/page.html
WEEKLY_CP=https://test.chathot.me/action/hiloHtml/Valentines_22_1_18/page.html
COUNTRY_STAR=https://test.chathot.me/action/hiloHtml/22_08_18_nation_star/page.html
NOBLE_BUY_IOS=https://h5.whoisamy.shop/action/hiloHtml/22_05_26_buy_nobility/page.html
GUILD_DATA_URL=https://test.chathot.me/action/hiloHtml/22_10_18_app_data_coins/index.html
MGR_GUILD_DATA_URL=https://test.chathot.me/action/hiloHtml/22_10_18_app_data_coins/union.html
RANKING_PINK_DIAMOND_URL=https://test.chathot.me/action/activitiesPage/2022_10_17HiloLiveH5/index.html
[GROUPIM]
MSG_SORT_EXPIRE=21600
MSG_SORT_SNAP=300
MSG_PARALLEL_SIZE=10
[GRADE]
CHARM_SPEED_VIP=15
ACTITY_SPEED_VIP=15
WEALTH_SPEED_VIP=15
[LIKE]
I_LIKE_NUM=30
I_LIKE_NUM_VIP=300
I_LIKE_NUM_NOBLE=1000
[APPLEPAY]
PASSWORD=38702750a05c4cb09c9d6ca646835634
[REGISTER]
IMEI_TOTAL=3
IMEI_OAUTH=2
ACCOUNT_IP=100
ACCOUNT_IP_DURATION=21600
[BANNER]
GIFT_BANNER_LEVEL1=500
GIFT_BANNER_LEVEL2=2000
GIFT_BANNER_LEVEL3=5000
[DIAMOND]
DAILY_LOGIN_IMEI_LIMIT=2
DAILY_LOGIN_IP_LIMIT=5
PRIVATE_GIFT_RETURN=1440
[LUCKY_WHEEL]
MINIMAL_PARTICIPANT=2
WAIT_TIMELONG=10
WINNER_DIAMOND_BANNER=10
[GROUP_CUSTOM_THEME]
PIC_LIMIT=5
DAY=10
[GIFT]
WALL_DIAMOND=10
[DAILY]
LOGIN_COMMON=5
LOGIN_VIP=300
[FRUIT_TYCOON]
POOL_RATIO=80
WATERMELON_RATIO=24
[RISK_CONTROL]
USER_QPS_LIMIT=60
[PAYER_MAX]
URL=https://pay-gate-uat.payermax.com/aggregate-pay-gate/api/gateway
KEY=d50d149a883b8bb6
MERCHANT_ID=SP11018326
BIZ_TYPE=CUSTOMIZE
VERSION=2.3
FRONT_CALLBACK_URL=https://www.hiloconn.com
SHOW_RESULT=1
EXPIRE_TIME=1800
LANGUAGE=en
[SUD]
API_LIST=https://sim-asc.sudden.ltd
[URL]
BIZ_HTTP=https://test.apiv1.faceline.live
\ No newline at end of file
package cache
import (
"encoding/json"
"git.hilo.cn/hilo-common/domain"
"math/rand"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
const (
DEFAULT_TTL = time.Hour
)
// 获取默认随机过期ttl
func GetDefaultTTL() time.Duration {
return time.Duration(rand.Intn(5))*time.Second + DEFAULT_TTL
}
// 设置redis json结构体
func SetJSON(model *domain.Model, key string, obj interface{}) (err error) {
defer model.Log.Infof("SetJson key:%v,obj:%v,err:%v", key, obj, err)
value, err := json.Marshal(obj)
if err != nil {
return err
}
return model.Redis.Set(model.Context, key, value, DEFAULT_TTL).Err()
}
// 获取redis中json结构体
// return err: may redisV8.Nil
func GetJSON(model *domain.Model, key string, obj interface{}) (err error) {
var value []byte
defer func() {
model.Log.Infof("GetJson key:%v,value:%s,obj:%v,err:%v", key, value, obj, err)
}()
value, err = model.Redis.Get(model.Context, key).Bytes()
if err != nil {
// may redisV2.Nil
return
}
err = json.Unmarshal(value, &obj)
if err != nil {
return err
}
return nil
}
// 尝试指定时间内获取锁
// acquireTimeout: 尝试获取锁的时间
// expireTimeout: 上锁的时间
// return: true上锁成功 false失败
func TryLock(model *domain.Model, key string, acquireTimeout, expireTimeout time.Duration) bool {
deadline := time.Now().Add(acquireTimeout)
for {
if time.Now().After(deadline) {
return false
}
flag, err := model.Redis.SetNX(model, key, "1", expireTimeout).Result()
if err != nil || !flag {
time.Sleep(time.Millisecond * 10)
} else {
return true
}
}
}
// 解锁
func UnLock(model *domain.Model, key string) {
model.Redis.Del(model, key)
}
package user_c
import (
"git.hilo.cn/hilo-common/domain"
"git.hilo.cn/hilo-common/resource/mysql"
redisV8 "github.com/go-redis/redis/v8"
"github.com/jinzhu/copier"
"hilo-group/_const/redis_key/user_k"
"hilo-group/domain/cache"
"hilo-group/domain/model/user_m"
"hilo-group/myerr"
"hilo-group/myerr/bizerr"
)
// 获取用户简要信息
// param userId 用户id
func GetUserTinyById(model *domain.Model, userId mysql.ID) (*user_m.UserTiny, error) {
userTiny := new(user_m.UserTiny)
key := user_k.GetUserTinyKey(userId)
err := cache.GetJSON(model, key, userTiny)
if err != nil && err != redisV8.Nil {
return nil, err
}
if err == redisV8.Nil {
// cache miss,get from mysql
if user, err := user_m.GetUser(model, userId); err != nil {
return nil, err
} else {
copier.Copy(userTiny, user)
err = cacheUserTiny(model, user)
if err != nil {
return nil, err
}
}
}
return userTiny, nil
}
// 获取用户简要信息By ExternalId
func GetUserByExternalId(model *domain.Model, externalId mysql.Str) (*user_m.UserTiny, error) {
userId, err := ToUserId(model, externalId)
if err != nil {
return nil, err
}
return GetUserTinyById(model, userId)
}
// externalId 到 userId
func ToUserId(model *domain.Model, externalId mysql.Str) (mysql.ID, error) {
if externalId == "" {
return 0, myerr.NewSysError("externalId 不能为空")
}
userId, err := model.Redis.Get(model.Context, user_k.GetExternalIdToUidKey(externalId)).Int64()
if err != nil && err != redisV8.Nil {
return 0, err
}
// cache miss,sync from db
if err == redisV8.Nil {
if user, err := user_m.GetUserByExtId(model, externalId); err != nil {
return 0, bizerr.ExternalIdNoExist
} else {
if err = cacheUserTiny(model, user); err != nil {
return 0, err
}
userId = int64(user.ID)
}
}
return mysql.ID(userId), nil
}
// code 到 userId
func ToUserIdByCode(model *domain.Model, code mysql.Str) (mysql.ID, error) {
if code == "" {
return 0, myerr.NewSysError("code 不能为空")
}
userId, err := model.Redis.Get(model.Context, user_k.GetCodeToUidKey(code)).Int64()
if err != nil && err != redisV8.Nil {
return 0, err
}
// cache miss,sync from db
if err == redisV8.Nil {
if user, err := user_m.GetUserByCode(model, code); err != nil {
return 0, bizerr.CodeNoExist
} else {
if err = cacheUserTiny(model, user); err != nil {
return 0, err
}
userId = int64(user.ID)
}
}
return mysql.ID(userId), nil
}
// 缓存userTiny
// 顺手缓存externalId->userId
// 顺手缓存code->userId
// param user: 已经在上层获取的db User结构
func cacheUserTiny(model *domain.Model, user *user_m.User) error {
userTiny := new(user_m.UserTiny)
if err := copier.Copy(userTiny, user); err != nil {
return err
}
// cache externalId->userId
if err := model.Redis.SetEX(model.Context, user_k.GetExternalIdToUidKey(user.ExternalId), user.ID, cache.GetDefaultTTL()).Err(); err != nil {
return err
}
// cache code->userId
if err := model.Redis.SetEX(model.Context, user_k.GetCodeToUidKey(user.Code), user.ID, cache.GetDefaultTTL()).Err(); err != nil {
return err
}
if err := cache.SetJSON(model, user_k.GetUserTinyKey(user.ID), userTiny); err != nil {
return err
}
return nil
}
package res_m
import (
"git.hilo.cn/hilo-common/domain"
"git.hilo.cn/hilo-common/resource/mysql"
"gorm.io/gorm"
"hilo-group/_const/enum/res_e"
"hilo-group/myerr"
)
type ResCountry struct {
mysql.Entity
*domain.Model `gorm:"-"`
Name mysql.Str
ShortName mysql.Str
Code mysql.Str
Icon mysql.Str
Icon2 mysql.Str
Lang mysql.Str
IsCommon mysql.UserYesNo
IsDefault mysql.UserYesNo
Status mysql.UserYesNo
StandardShortName mysql.Str
AreaCode mysql.Str
AreaCodeName mysql.Str
}
type ResLanguage struct {
mysql.Entity
*domain.Model `gorm:"-"`
Name mysql.Str
Remark mysql.Str
ResCountryId mysql.ID
}
type ResMsgTranslate struct {
mysql.Entity
*domain.Model `gorm:"-"`
Language mysql.Str
Title mysql.Str
Content mysql.Str
IconUrl mysql.Str
MsgType mysql.Type
Type uint32
//跳转类型 0:无跳转 1:网页跳转, 2:跳转到用户 3:跳转到群组
ActionType mysql.Type
ActionUrl mysql.Str
}
// 复制消息并替换图片和URL
func (rmt *ResMsgTranslate) CopyRecord(db *gorm.DB, newType uint64, iconUrl, actionUrl string) error {
return db.Exec("REPLACE INTO res_msg_translate (language,title,content,?,msg_type,?,action_type,?) "+
"SELECT language,title,content,icon_url,msg_type,10001,action_type,action_url FROM res_msg_translate "+
"WHERE msg_type = ? and type = 39", iconUrl, newType, actionUrl, rmt.MsgType, rmt.Type).Error
}
//获取默认的国家
func GetCountryDefault(model *domain.Model) (*ResCountry, error) {
var resCountry ResCountry
if err := model.Db.Where(&ResCountry{
IsDefault: mysql.USER,
}).First(&resCountry).Error; err != nil {
return nil, myerr.WrapErr(err)
}
return &resCountry, nil
}
// 获取所有国家的icon信息
func GetAllCountries(model *domain.Model) (map[string]string, error) {
var countrys []ResCountry
if err := model.Db.Model(&ResCountry{}).Where(&ResCountry{
Status: mysql.USER,
}).Find(&countrys).Error; err != nil {
return nil, myerr.WrapErr(err)
}
result := make(map[string]string, 0)
for _, i := range countrys {
result[i.Name] = i.Icon
}
return result, nil
}
func GetAllCountryByFilter(model *domain.Model, shortNames []string) ([]ResCountry, error) {
var countrys []ResCountry
if err := model.Db.Model(&ResCountry{}).Where(&ResCountry{
Status: mysql.USER,
}).Find(&countrys).Error; err != nil {
return nil, myerr.WrapErr(err)
}
countryMap := map[string]ResCountry{}
//转成map
for i, _ := range countrys {
countryMap[countrys[i].ShortName] = countrys[i]
}
results := make([]ResCountry, 0, len(shortNames))
for i, _ := range shortNames {
if country, flag := countryMap[shortNames[i]]; flag {
results = append(results, country)
}
}
return results, nil
}
//通过standardShortName获取国家
func GetCountryByStandardShortName(model *domain.Model, standardShortName mysql.Str) (*ResCountry, error) {
var resCountry ResCountry
if err := model.Db.Where(&ResCountry{
StandardShortName: standardShortName,
Status: mysql.USER,
}).First(&resCountry).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
} else {
return nil, myerr.WrapErr(err)
}
}
resCountry.Model = model
return &resCountry, nil
}
//通过shortName获取国家
func GetCountryByShortName(model *domain.Model, shortName mysql.Str) (*ResCountry, error) {
var resCountry ResCountry
err := model.Db.Where(&ResCountry{
ShortName: shortName,
}).First(&resCountry).Error
return &resCountry, myerr.WrapErr(err)
}
//通过name获取国家
func GetCountryByName(model *domain.Model, name mysql.Str) (*ResCountry, error) {
var resCountry ResCountry
err := model.Db.WithContext(model.Context).Where(&ResCountry{
Name: name,
}).First(&resCountry).Error
return &resCountry, myerr.WrapErr(err)
}
//通过code获取国家
func GetCountryByCode(model *domain.Model, code mysql.Str) (*ResCountry, error) {
if code == "" {
return nil, nil
}
var resCountry ResCountry
err := model.Db.Where(&ResCountry{
Code: code,
}).First(&resCountry).Error
if err == nil {
return &resCountry, myerr.WrapErr(err)
} else if err == gorm.ErrRecordNotFound {
return nil, nil
} else {
return nil, myerr.WrapErr(err)
}
}
//通过id获取国家
func GetCountryById(model *domain.Model, id mysql.ID) (*ResCountry, error) {
var resCountry ResCountry
err := model.Db.First(&resCountry, id).Error
return &resCountry, myerr.WrapErr(err)
}
//通过语言,找到国家
func GetCountryByLanguage(model *domain.Model, name mysql.Str) (*ResCountry, error) {
if name == "" {
return nil, nil
}
var language ResLanguage
err := model.Db.Where(&ResLanguage{
Name: name,
}).First(&language).Error
if err == nil {
return GetCountryById(model, language.ResCountryId)
} else if err == gorm.ErrRecordNotFound {
return nil, nil
} else {
return nil, myerr.WrapErr(err)
}
}
//通过国家,找到对应的语言
func GetLangeByCountry(db *gorm.DB, country mysql.Str) (string, error) {
var r ResCountry
err := db.Where(&ResCountry{
Name: country,
}).First(&r).Error
if err == nil {
return r.Lang, nil
} else if err == gorm.ErrRecordNotFound {
return res_e.DEFAULT_LANG, nil
} else {
return "", myerr.WrapErr(err)
}
}
package res_m
import (
"git.hilo.cn/hilo-common/resource/mysql"
"gorm.io/gorm"
"hilo-group/_const/enum/res_e"
)
type ResMedal struct {
mysql.Entity
Name string
PicUrl string
SvgaUrl string
Sort mysql.Num
NoPicUrl *string
//类型 (0:代表自身就是一种类型 > 0, 一种相关联的类型)
Type res_e.ResMedalType
Scope res_e.ResMedalScope
//限制 (-1, 没有限制)
Threshold mysql.Num
I mysql.Num
}
// 查勋章中的等级关系
func GetUserMedalLevelMap(db *gorm.DB) (map[uint64]uint8, map[uint8][]uint64, error) {
rows := make([]ResMedal, 0)
if err := db.Model(&ResMedal{}).Order("threshold ASC").Find(&rows).Error; err != nil {
return nil, nil, err
}
medalTypes := make(map[uint64]uint8, 0)
result := make(map[uint8][]uint64, 0)
for _, i := range rows {
medalTypes[i.ID] = i.Type
if i.Type != 0 {
if _, ok := result[i.Type]; !ok {
result[i.Type] = make([]uint64, 0)
}
result[i.Type] = append(result[i.Type], i.ID)
}
}
return medalTypes, result, nil
}
package res_m
import (
"git.hilo.cn/hilo-common/resource/mysql"
"gorm.io/gorm"
"hilo-group/_const/enum/res_e"
"hilo-group/myerr"
)
type ResMultiText struct {
mysql.Entity
MsgId uint
Language mysql.Str
Content mysql.Str
}
func (r *ResMultiText) Get(db *gorm.DB) error {
return db.Where(r).First(r).Error
}
func GetResMultiTextBy(db *gorm.DB, msgId uint, Language mysql.Str) (*ResMultiText, error) {
r := ResMultiText{}
if err := db.Where(&ResMultiText{
MsgId: msgId,
Language: Language,
}).First(&r).Error; err != nil {
if err == gorm.ErrRecordNotFound {
if err := db.Where(&ResMultiText{
MsgId: msgId,
Language: res_e.DEFAULT_LANG,
}).First(&r).Error; err != nil {
return nil, myerr.WrapErr(err)
}
} else {
return nil, myerr.WrapErr(err)
}
}
return &r, nil
}
package user_m
import (
"git.hilo.cn/hilo-common/domain"
"git.hilo.cn/hilo-common/resource/mysql"
"hilo-group/_const/enum/user_e"
"hilo-group/myerr"
"hilo-group/myerr/bizerr"
)
//用户信息
type User struct {
mysql.Entity
*domain.Model `gorm:"-"`
ExternalId mysql.Str
Avatar mysql.Str
DefaultAvatar bool
Nick mysql.Str
Sex mysql.Sex
Birthday mysql.Timestamp
Country mysql.Str
CountryIcon mysql.Str
Language mysql.Str
Description mysql.Str
Code mysql.Str
OriginCode mysql.Str
IsPush mysql.OpenClose
IsShowAge mysql.OpenClose
Status user_e.UserStatus
DeviceType mysql.Str
LogoutTime int64
}
type UserTiny struct {
ID uint64 `json:"id,omitempty"`
ExternalId string `json:"externalId"`
Avatar string `json:"avatar"`
Nick string `json:"nick"`
Sex uint8 `json:"sex"` // 1男2女
Code string `json:"code"`
Description string `json:"description"`
Country string `json:"country"`
CountryIcon string `json:"countryIcon"`
IsPrettyCode bool `json:"isPrettyCode"` // 是否靓号
IsLogout bool `json:"isLogout"` //是否注销 true:已经注销, false:没有注销
//生日,如果是其它人用户信息,年龄则按照是否展示显示,如果是本人,年龄则按照是否存在展示
Birthday *uint64 `json:"birthday"`
}
func (u User) IsPrettyCode() bool {
return u.Code != u.OriginCode
}
//获取用户
func GetUser(model *domain.Model, id mysql.ID) (*User, error) {
var user User
if err := model.Db.WithContext(model.Context).Where(&User{
Entity: mysql.Entity{ID: id},
}).First(&user).Error; err != nil {
return nil, myerr.WrapErr(err)
}
user.Model = model
return &user, nil
}
// 通过code获取用户
func GetUserByCode(model *domain.Model, code string) (*User, error) {
if code == "" {
return nil, bizerr.InvalidParameter
}
var user User
if err := model.Db.Where(&User{Code: code}).First(&user).Error; err != nil {
return nil, myerr.WrapErr(err)
}
user.Model = model
return &user, nil
}
//获取用户信息
func GetUsersByIds(model *domain.Model, userIds []mysql.ID) ([]User, error) {
var users []User
if err := model.Db.Model(User{}).Where("id IN (?)", userIds).Find(&users).Error; err != nil {
return nil, myerr.WrapErr(err)
}
return users, nil
}
func GetUserMapByIds(model *domain.Model, userIds []mysql.ID) (map[mysql.ID]User, error) {
rows, err := GetUsersByIds(model, userIds)
if err != nil {
return nil, err
}
result := make(map[mysql.ID]User, 0)
for _, i := range rows {
result[i.ID] = i
}
return result, nil
}
// 通过externalId获取用户
func GetUserByExtId(model *domain.Model, externalId string) (*User, error) {
var user User
if err := model.Db.Where(&User{ExternalId: externalId}).First(&user).Error; err != nil {
return nil, myerr.WrapErr(err)
}
user.Model = model
return &user, nil
}
module hilo-group
go 1.18
replace git.hilo.cn/hilo-common => ../hilo-common
require (
git.hilo.cn/hilo-common v0.0.0-00010101000000-000000000000 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.6.3 // indirect
github.com/go-openapi/jsonpointer v0.19.3 // indirect
github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-openapi/spec v0.19.4 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/hashicorp/consul/api v1.7.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-hclog v0.12.0 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/golang-lru v0.5.0 // indirect
github.com/hashicorp/serf v0.9.3 // indirect
github.com/jinzhu/copier v0.3.5 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.3.0 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
github.com/lestrrat-go/strftime v1.0.6 // indirect
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/swaggo/gin-swagger v1.2.0 // indirect
github.com/swaggo/swag v1.6.7 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.4.3 // indirect
gorm.io/gorm v1.23.8 // indirect
)
This diff is collapsed.
[DATABASE]
MYSQL_HOST=47.244.34.27:3306
MYSQL_USERNAME=root
MYSQL_PASSWORD=yX0jPAhO0I4s2zlA
MYSQL_DB=hilo
[DATABASECODE]
MYSQL_HOST=47.244.34.27:3306
MYSQL_USERNAME=root
MYSQL_PASSWORD=yX0jPAhO0I4s2zlA
MYSQL_DB=hilo_code
[REDIS]
REDIS_HOST=47.244.34.27:6379
REDIS_PASSWORD=8QZ9JD1zLvPR3yHf
[JWT]
SECRET=hilo1632
ISSUER_API=hiloApi
ISSUER_Mgr=hiloMgr
EXPIRE=240h
[GAMEJWT]
SECRET=hilo1632
ISSUER_CLIENT=hiloClient
ISSUER_SERVER=hiloServer
EXPIRE=240h
[APP]
MASTER=false
BIZ_SECRET=biz
WEB_SECRET=webHilo1258
OPERATION_SECRET=operation1258236
SUPERUSER=2701,2831
OFFICIAL_STAFF=2701,2831
OFFICIAL_GROUP=@TGS#3NC2ATRHS,@TGS#33W3KNLHK
MINIMAL_VERSION_ANDROID=212
MINIMAL_VERSION_IOS=100
MODERATE=AWS
[OSS]
OSS_ACCESS_KEY_ID=LTAIxdazV2pCuV3T
OSS_ACCESS_KEY_SECRET=zuAnreAXQ6vlAKnvvmolFLfb1N5w5S
OSS_ROLE_ARN=acs:ram::1509841556585969:role/aliyunosstokengeneratorrole
OSS_END_POINT=http://oss-accelerate.aliyuncs.com
OSS_BUCKET=starvoice
OSS_CDN=https://oss.chathot.me/
OSS_EXPIRED_TIME=3600
OSS_STS_POINT=me-east-1
OSS_STS=sts-faceline-demo
OSS_STS_AES=484194d4d0f968a7
[AWS]
AWS_BUCKET=starchat
AWS_CDN=https://image.whoisamy.shop/
AWS_DIR=hilo/
CONFIDENCE=80
[RONGYUN]
RONG_CLOUD_APP_KEY=pvxdm17jpe9tr
RONG_CLOUD_APP_SECRET=rI4giiKWaBS4
RONG_CLOUD_URL=https://api-sg01.ronghub.com
[TENCENTYUN]
TENCENTYUN_APP_ID=1400548270
TENCENTYUN_KEY=321bd60f73096b059c7350f1cd97d51028850b34fa58c5c0d26bb4a19e783de8
TX_OVERSEA_APP_ID=40000066
TX_OVERSEA_KEY=3ab68ea5bddc8774d90b8c764ae71188914bd5fd06f30b28790c51e44ca7885c
[EMAS]
REGION_ID=cn-hangzhou
ACCESS_KEY_ID=LTAI4FhNPzxdzD4w6bHirL9Z
ACCESS_KEY_SECRET=OQvUJpXDrjGi3g1F2aHiAIFWIvLdbP
ANDROID_APP_KEY=30250713
ANDROID_APP_SECRET=cae7b9a9d3e54577d2c3b60bf6d23047
IOS_APP_KEY=30240346
IOS_APP_SECRET=57f33ab9ca6a957a8c659f2b0b6d1205
APNS=DEV
[AGORA]
APP_ID=fc3e087f701b4f788099e1924c3cc7b0
APP_CERTIFICATE=ff29c100a613433db82324e8411eabc8
CUSTOMER_KEY=6b132c0ff7164560a2bc53fda06ea85a
CUSTOMER_SECRET=eedad2cd16d24834990d5450ace9f1ce
CALLBACK_SECRET=n_ZizS_N8
[CHECKOUT]
AUTHORIZATION=sk_test_9b5e771c-5a3f-4a8d-a4da-31b19bd43d83
URL=https://api.sandbox.checkout.com/hosted-payments
H5=http://test.chathot.me/action/hiloHtml/22_05_30_recharge/topup.html
HILO_SECRET_KEY=sk_test_dfbaa3b6-135d-4376-9996-2089b7d8a086
[MATCH]
MATCH_FREE_TIME=60
MATCH_FREE_TIME_VIP=300
MATCH_ADD_TIME_FREE=90
MATCH_AGORA_TIME=30
MATCH_CYCLE=8
MATCH_USER_EXPIRES=480
MATCH_SUCCESS_WAIT_DURATION=10
MATCH_SUCCESS_SINGLE_WAIT_TIME_IN_SEC=12
MATCH_SUCCESS_DUAL_WAIT_TIME_IN_SEC=15
[ONLINE]
ONLINE_CYCLE=600
ONLINE_USER_EXPIRES=259200
[VIDEO]
VIDEO_DAILY_FREE_NUM=20
VIDEO_FREE_TIME=60
VIDEO_FREE_TIME_VIP=300
VIDEO_ADD_TIME_FREE=60
VIDEO_AGORA_TIME=30
VIDEO_MINUTE_NORMAL=1000
VIDEO_MINUTE_UNION=2000
[SESSION]
SESSION_DAILY_FREE_NUM=50
[BEAN]
DIAMOND_BEAN_RATE=90
[H5]
USER_LEVEL=http://test.chathot.me/action/activityhtml/hiloUserLevel/index.html
GROUP_SUPPORT=http://test.chathot.me/action/activityhtml/21_12_06/page.html
LUCKY_WHEEL=https://h5.whoisamy.shop/action/activityhtml/21_12_30/page.html
NOBLE_BUY_IOS=https://h5.whoisamy.shop/action/hiloHtml/lxt_h5/page.html
[GROUPIM]
MSG_SORT_EXPIRE=43200
MSG_SORT_SNAP=300
[GRADE]
CHARM_SPEED_VIP=15
ACTITY_SPEED_VIP=15
WEALTH_SPEED_VIP=15
[LIKE]
I_LIKE_NUM=30
I_LIKE_NUM_VIP=100
I_LIKE_NUM_NOBLE=1000
[APPLEPAY]
PASSWORD=38702750a05c4cb09c9d6ca646835634
[REGISTER]
IMEI_TOTAL=3
IMEI_OAUTH=2
ACCOUNT_IP=100
ACCOUNT_IP_DURATION=21600
[BANNER]
GIFT_BANNER_LEVEL1=100
GIFT_BANNER_LEVEL2=2000
GIFT_BANNER_LEVEL3=5000
[DIAMOND]
DAILY_LOGIN_IMEI_LIMIT=200
DAILY_LOGIN_IP_LIMIT=5
PRIVATE_GIFT_RETURN=1440
NEW_USER_INVITE_AWARD=5000
[LUCKY_WHEEL]
MINIMAL_PARTICIPANT=2
WAIT_TIMELONG=10
WINNER_DIAMOND_BANNER=100
[GROUP_CUSTOM_THEME]
PIC_LIMIT=5
DAY=10
[GIFT]
WALL_DIAMOND=10
[DAILY]
LOGIN_COMMON=5
LOGIN_VIP=300
[DAILY]
LOGIN_COMMON=5
LOGIN_VIP=300
[FRUIT_TYCOON]
POOL_RATIO=20
WATERMELON_RATIO=70
[ACTIVITY]
COUNTRY_STAR_POOL_RATIO=20
COUNTRY_STAR_ORDINARY_RATIO=20
[PAYER_MAX]
URL=https://pay-gate-uat.payermax.com/aggregate-pay-gate/api/gateway
KEY=d50d149a883b8bb6
MERCHANT_ID=SP11018326
BIZ_TYPE=CUSTOMIZE
VERSION=2.3
FRONT_CALLBACK_URL=https://www.hiloconn.com
SHOW_RESULT=1
EXPIRE_TIME=1800
LANGUAGE=en
[SUD]
API_LIST=https://sim-asc.sudden.ltd
[URL]
BIZ_HTTP=https://test.apiv1.faceline.live
\ No newline at end of file
package main
import (
"fmt"
"git.hilo.cn/hilo-common/resource/consul"
"hilo-group/route"
)
const (
PORT = 9050
RegisterName = "hiloGroup"
RegisterTag = "群组中心"
)
func main() {
//cron.Init() // 开启定时任务
//event_s.EventInit() // 注册事件(内部事件+mysql拟kafka)
r := route.InitRouter() // 注册路由
consul.RegisterToConsul(PORT, RegisterName, RegisterTag) // 服务注册
r.Run(fmt.Sprintf(":%d", PORT)) // 启动服务
}
package bizerr
import (
"hilo-group/myerr"
)
var (
// 一般性错误
TokenInvalid = myerr.NewBusinessCode(1001, "token invalid", myerr.BusinessData{})
ExternalIdNoExist = myerr.NewBusinessCode(1003, "externalId no exist", myerr.BusinessData{})
CodeNoExist = myerr.NewBusinessCode(1005, "code no exist", myerr.BusinessData{})
ParaMissing = myerr.NewBusinessCode(1006, "parameter missing", myerr.BusinessData{})
InvalidParameter = myerr.NewBusinessCode(1009, "Invalid parameter", myerr.BusinessData{})
IncorrectState = myerr.NewBusinessCode(1013, "Incorrect state", myerr.BusinessData{})
TransactionFailed = myerr.NewBusinessCode(1014, "Transaction failed", myerr.BusinessData{})
ReqTooFrequent = myerr.NewBusinessCode(1018, "Requests are too frequent", myerr.BusinessData{})
// 钻石
DiamondNoEnough = myerr.NewBusinessCode(4000, "Insufficient diamonds", myerr.BusinessData{})
DiamondFrequency = myerr.NewBusinessCode(4001, "Diamond operation frequency too high", myerr.BusinessData{})
DiamondAccountFrozen = myerr.NewBusinessCode(4004, "Diamond Account Frozen", myerr.BusinessData{})
// 游戏
GameInvalidParameter = myerr.NewGameError(10002, "invalid parameter")
GameTokenInvalid = myerr.NewGameError(1004, "user token invalid")
GameTokenExpire = myerr.NewGameError(1005, "user token expire")
// 群组
GroupNotFound = myerr.NewBusinessCode(14001, "Group not found", myerr.BusinessData{}) // 找不到该群
// 游戏服务的错误码,6位,50XXX
GameAddNoPermissions = myerr.NewBusinessCode(50100, "Only room administrators can create users", myerr.BusinessData{}) // 权限不足
GameAddNotOnMic = myerr.NewBusinessCode(50101, "Need on mic", myerr.BusinessData{}) // 需要在麦上才能创建、加入游戏
GameHaveNoEnd = myerr.NewBusinessCode(50102, "Group user have no end", myerr.BusinessData{}) // 房间还有未结束的游戏
GameNotFound = myerr.NewBusinessCode(50103, "Game not found", myerr.BusinessData{}) //
GameStart = myerr.NewBusinessCode(50104, "Gaming", myerr.BusinessData{}) //
GameAlreadyJoin = myerr.NewBusinessCode(50105, "Already Joined", myerr.BusinessData{}) // 已经加入了游戏
GameNotJoin = myerr.NewBusinessCode(50106, "Not Join user", myerr.BusinessData{}) // 还未加入游戏
GameCannotClose = myerr.NewBusinessCode(50107, "Have no power to close user", myerr.BusinessData{}) // 没有权限关闭游戏
GameCloseGaming = myerr.NewBusinessCode(50108, "Can't close a user in progress", myerr.BusinessData{}) // 不能关闭进行中的游戏
GamePlayerNumWrong = myerr.NewBusinessCode(50109, "Game player num wrong", myerr.BusinessData{}) // 玩家数量错误
GameHaveNoMyRoom = myerr.NewBusinessCode(50110, "Have no my room", myerr.BusinessData{}) // 自己没有房间
GameHaveNoEndGame = myerr.NewBusinessCode(50111, "The last user is not over yet, cannot create/join a user", myerr.BusinessData{}) // 已经加入了其他房间的游戏
GameExitWrong = myerr.NewBusinessCode(50112, "Can not exit user", myerr.BusinessData{}) // 离开游戏失败
GameDiamondCannotEdit = myerr.NewBusinessCode(50113, "Game diamond can not edit", myerr.BusinessData{}) //
GameSettleWrong = myerr.NewBusinessCode(50114, "Game settle wrong", myerr.BusinessData{}) // 结算修改错误
GameCloseWrong = myerr.NewBusinessCode(50115, "Game close wrong", myerr.BusinessData{}) // 关闭错误
GameJoinFailed = myerr.NewBusinessCode(50116, "Join failed", myerr.BusinessData{}) // 加入失败
)
package myerr
import (
"fmt"
"git.hilo.cn/hilo-common/mylogrus"
"github.com/pkg/errors"
"strconv"
)
// 成功
type Success struct {
code uint16
message string
}
func (err *Success) Error() string {
return err.message
}
// 正确的标识符
var success = &Success{code: 200, message: "OK"}
func GetSuccessCode() uint16 {
return success.code
}
func GetSuccessMsg() string {
return success.message
}
func GetSuccess() Success {
return *success
}
// 系统错误
type SysError struct {
code uint16
message string
err error
}
var sysError = &SysError{
code: 500,
message: "",
}
func (err *SysError) Error() string {
return err.err.Error()
}
func NewSysError(msg string) *SysError {
return &SysError{
code: sysError.code,
message: msg,
err: errors.New("{code:" + strconv.Itoa(int(sysError.code)) + ",message:" + msg + "}"),
}
}
func NewSysErrorF(format string, args ...interface{}) *SysError {
return NewSysError(fmt.Sprintf(format, args...))
}
func GetSysErrorCode() uint16 {
return sysError.code
}
func (sysError *SysError) GetErr() error {
return sysError.err
}
func (sysError *SysError) GetCode() uint16 {
return sysError.code
}
func (sysError *SysError) GetMsg() string {
return sysError.message
}
// 警告错误
type WaringError struct {
code uint16
message string
err error
}
var waringError = &WaringError{
code: 300,
message: "",
}
func GetWaringCode() uint16 {
return waringError.code
}
func (err *WaringError) Error() string {
return err.err.Error()
}
func NewWaring(msg string) *WaringError {
return &WaringError{
code: waringError.code,
message: msg,
err: errors.New("{code:" + strconv.Itoa(int(waringError.code)) + ",message:" + msg + "}"),
}
}
func NewWaringErrorF(format string, args ...interface{}) *WaringError {
return NewWaring(fmt.Sprintf(format, args...))
}
func (err *WaringError) GetErr() error {
return err.err
}
func (err *WaringError) GetCode() uint16 {
return err.code
}
func (err *WaringError) GetMsg() string {
return err.message
}
// 业务错误
type BusinessError struct {
code uint16
message string
err error
data BusinessData
}
func (err *BusinessError) Error() string {
return err.err.Error()
}
func (err *BusinessError) GetErr() error {
return err.err
}
func (err *BusinessError) GetCode() uint16 {
return err.code
}
func (err *BusinessError) GetMsg() string {
return err.message
}
func (err *BusinessError) GetData() BusinessData {
return err.data
}
var codes = map[uint16]string{}
// 定义必须是明确的。不可以修改,字段等同于翻译中要替换的字符
type BusinessData struct {
//剩余秒
Second int `json:"second"`
//所需数量
Num int `json:"num"`
Code string `json:"code"`
Timestamp int64 `json:"timestamp"`
//官网充值地址
CheckOutUrl string `json:"checkOutUrl"`
}
func NewBusiness(err *BusinessError) *BusinessError {
return &BusinessError{
code: err.code,
message: err.message,
err: err.err,
data: err.data,
}
}
func NewBusinessCode(code uint16, msg string, data BusinessData) *BusinessError {
if _, ok := codes[code]; ok {
mylogrus.MyLog.Error(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code))
return nil
}
codes[code] = msg
return &BusinessError{
code: code,
message: msg,
err: errors.New("{code:" + strconv.Itoa(int(code)) + ",message:" + msg + "}"),
data: data,
}
}
func NewBusinessCodeNoCheck(code uint16, msg string, data BusinessData) *BusinessError {
return &BusinessError{
code: code,
message: msg,
err: errors.New("{code:" + strconv.Itoa(int(code)) + ",message:" + msg + "}"),
data: data,
}
}
// 包装日志,让日志成堆栈状态
func WrapErrWithStr(err error, msg string) error {
if h, ok := err.(*BusinessError); ok {
h.err = errors.Wrap(h.err, msg)
return h
} else if h, ok := err.(*WaringError); ok {
h.err = errors.Wrap(h.err, msg)
return h
} else if h, ok := err.(*SysError); ok {
h.err = errors.Wrap(h.err, msg)
return h
} else {
return errors.Wrap(err, msg)
}
}
func WrapErr(err error) error {
if h, ok := err.(*BusinessError); ok {
h1 := NewBusiness(h)
h1.err = errors.Wrap(h1.err, "")
return h1
} else if h, ok := err.(*WaringError); ok {
h.err = errors.Wrap(h.err, "")
return h
} else if h, ok := err.(*SysError); ok {
h.err = errors.Wrap(h.err, "")
return h
} else {
return errors.Wrap(err, "")
}
}
func WrapGameErr(err error) error {
if err == nil {
return err
}
if h, ok := err.(*GameError); ok {
return h
}
return NewGameError(500, err.Error())
}
// 系统错误
type GameError struct {
code uint16
message string
err error
}
func (err *GameError) Error() string {
return err.err.Error()
}
func (err *GameError) Code() uint16 {
return err.code
}
func NewGameError(code uint16, msg string) *GameError {
return &GameError{
code: code,
message: msg,
err: errors.New("{code:" + strconv.Itoa(int(code)) + ",message:" + msg + "}"),
}
}
[DATABASE]
MYSQL_HOST=ua4papc3hmgqf351pbej-rw4rm.rwlb.dubai.rds.aliyuncs.com
MYSQL_USERNAME=nextvideo
MYSQL_PASSWORD=ihlUwI4nhi9W88MI
MYSQL_DB=hilo
[DATABASECODE]
MYSQL_HOST=ua4papc3hmgqf351pbej-rw4rm.rwlb.dubai.rds.aliyuncs.com
MYSQL_USERNAME=nextvideo
MYSQL_PASSWORD=ihlUwI4nhi9W88MI
MYSQL_DB=hilo_code
[REDIS]
REDIS_HOST=r-eb3btxn8vfdsuwdbuf.redis.dubai.rds.aliyuncs.com:6379
REDIS_PASSWORD=
[JWT]
SECRET=hilo1504
ISSUER_API=hiloApi
ISSUER_Mgr=hiloMgr
EXPIRE=720h
[GAMEJWT]
SECRET=hilo1504
ISSUER_CLIENT=hiloClient
ISSUER_SERVER=hiloServer
EXPIRE=720h
[APP]
MASTER=true
BIZ_SECRET=biz
OPERATION_SECRET=operation1258236
WEB_SECRET=webHilo1258
SUPERUSER=28201,23951,133101,41,2020531
OFFICIAL_STAFF=133101,435731,486461,41
OFFICIAL_GROUP=@TGS#33W3KNLHK,@TGS#3XA5RJ5HH,@TGS#3O6PKBTH6
MINIMAL_VERSION_ANDROID=22600
MINIMAL_VERSION_IOS=22600
ROOM_MODE=AVChatRoom
MODERATE=TENCENT
[OSS]
OSS_ACCESS_KEY_ID=LTAIxdazV2pCuV3T
OSS_ACCESS_KEY_SECRET=zuAnreAXQ6vlAKnvvmolFLfb1N5w5S
OSS_ROLE_ARN=acs:ram::1509841556585969:role/aliyunosstokengeneratorrole
OSS_END_POINT=https://oss-accelerate.aliyuncs.com
OSS_BUCKET=starvoice
OSS_CDN=https://oss.chathot.me/
OSS_EXPIRED_TIME=3600
OSS_STS_POINT=me-east-1
OSS_STS=sts-faceline-demo
OSS_STS_AES=484194d4d0f968a7
[AWS]
AWS_BUCKET=starchat
AWS_CDN=https://image.whoisamy.shop/
AWS_DIR=hilo/
CONFIDENCE=80
[RONGYUN]
RONG_CLOUD_APP_KEY=uwd1c0sxu5t41
RONG_CLOUD_APP_SECRET=vo9djozyBl9bZ
RONG_CLOUD_URL=https://api-sg01.ronghub.com
[TENCENTYUN]
TENCENTYUN_APP_ID=1400487464
TENCENTYUN_KEY=cb4c1f2e3398a88e0e9468b403f671e60d66a564df86f7db925c6ab4f18b66e5
TX_OVERSEA_APP_ID=40000066
TX_OVERSEA_KEY=3ab68ea5bddc8774d90b8c764ae71188914bd5fd06f30b28790c51e44ca7885c
[EMAS]
REGION_ID=cn-hangzhou
ACCESS_KEY_ID=LTAIdQZv5H1kNZp5
ACCESS_KEY_SECRET=UnwY0ClDkqBMLwPx3OJJiLYyk9xYLO
ANDROID_APP_KEY=30774987
ANDROID_APP_SECRET=297a0f231f1286a2de9aab097cc8ff5c
IOS_APP_KEY=30790728
IOS_APP_SECRET=4fd69ca084c67d4b5a8d15452f0af26a
APNS=PRODUCT
[AGORA]
APP_ID=6291d069123642d9929a49c734c50719
APP_CERTIFICATE=d5de40350aa54e60bcdce90c71e9598a
CUSTOMER_KEY=6b132c0ff7164560a2bc53fda06ea85a
CUSTOMER_SECRET=eedad2cd16d24834990d5450ace9f1ce
[CHECKOUT]
AUTHORIZATION=sk_fca6e213-b7df-4bd7-99f4-7c0a9f7c778c
URL=https://api.checkout.com/hosted-payments
H5=https://h5.whoisamy.shop/action/hiloHtml/22_05_30_recharge/topup.html
HILO_SECRET_KEY=sk_26806bf4-e6e3-45e2-a093-c72c5b53eaf5
[MATCH]
MATCH_FREE_TIME=60
MATCH_FREE_TIME_VIP=60
MATCH_ADD_TIME_FREE=90
MATCH_AGORA_TIME=30
MATCH_CYCLE=8
MATCH_USER_EXPIRES=480
MATCH_SUCCESS_WAIT_DURATION=10
MATCH_SUCCESS_SINGLE_WAIT_TIME_IN_SEC=12
MATCH_SUCCESS_DUAL_WAIT_TIME_IN_SEC=15
[ONLINE]
ONLINE_CYCLE=600
ONLINE_USER_EXPIRES=259200
[VIDEO]
VIDEO_DAILY_FREE_NUM=20
VIDEO_FREE_TIME=60
VIDEO_FREE_TIME_VIP=300
VIDEO_ADD_TIME_FREE=60
VIDEO_AGORA_TIME=30
VIDEO_MINUTE_NORMAL=60
VIDEO_MINUTE_UNION=60
[SESSION]
SESSION_DAILY_FREE_NUM=50
GUILD_USER_HELLO_DAY=30
[BEAN]
DIAMOND_BEAN_RATE=90
[H5]
USER_LEVEL=https://h5.whoisamy.shop/action/hiloHtml/hiloUserLevel/index.html
GROUP_SUPPORT=https://h5.whoisamy.shop/action/activityhtml/21_12_06/page.html
LUCKY_WHEEL=https://h5.whoisamy.shop/action/activityhtml/21_12_30/page.html
WEEKLY_STAR=https://h5.whoisamy.shop/action/hiloHtml/lxt_h5/page.html
WEEKLY_CP=https://h5.whoisamy.shop/action/hiloHtml/Valentines_22_1_18/page.html
COUNTRY_STAR=https://h5.whoisamy.shop/action/hiloHtml/22_08_18_nation_star/page.html
#NOBLE_BUY_IOS=https://h5.whoisamy.shop/action/hiloHtml/22_05_26_buy_nobility/page.html
NOBLE_BUY_IOS=https://h5.whoisamy.shop/action/hiloHtml/lxt_h5/page.html
GUILD_DATA_URL=https://h5.whoisamy.shop/action/hiloHtml/22_10_18_app_data_coins/index.html
MGR_GUILD_DATA_URL=https://h5.whoisamy.shop/action/hiloHtml/22_10_18_app_data_coins/union.html
RANKING_PINK_DIAMOND_URL=https://h5.whoisamy.shop/action/activitiesPage/2022_10_17HiloLiveH5/index.html
[GROUPIM]
MSG_SORT_EXPIRE=1209600
MSG_SORT_SNAP=300
MSG_PARALLEL_SIZE=10
[GRADE]
CHARM_SPEED_VIP=15
ACTITY_SPEED_VIP=15
WEALTH_SPEED_VIP=15
[LIKE]
I_LIKE_NUM=500
I_LIKE_NUM_VIP=1000
I_LIKE_NUM_NOBLE=5000
[APPLEPAY]
PASSWORD=38702750a05c4cb09c9d6ca646835634
[REGISTER]
IMEI_TOTAL=5
IMEI_OAUTH=2
ACCOUNT_IP=100
ACCOUNT_IP_DURATION=21600
[BANNER]
GIFT_BANNER_LEVEL1=1000
GIFT_BANNER_LEVEL2=3000
GIFT_BANNER_LEVEL3=5000
[DIAMOND]
DAILY_LOGIN_IMEI_LIMIT=5
DAILY_LOGIN_IP_LIMIT=30
PRIVATE_GIFT_RETURN=1440
NEW_USER_INVITE_AWARD=5000
[LUCKY_WHEEL]
MINIMAL_PARTICIPANT=2
WAIT_TIMELONG=10
WINNER_DIAMOND_BANNER=200
[GROUP_CUSTOM_THEME]
PIC_LIMIT=50
DAY=10
[GIFT]
WALL_DIAMOND=2000
[DAILY]
LOGIN_COMMON=10
LOGIN_VIP=1000
[FRUIT_TYCOON]
BIG_WINNER_THRESDHOLD=30000
BIG_WINNER_LOW=10000
BIG_WINNER_HIGH=20000
POOL_RATIO=5
WATERMELON_RATIO=24
[ACTIVITY]
COUNTRY_STAR_POOL_RATIO=20
COUNTRY_STAR_ORDINARY_RATIO=20
[RISK_CONTROL]
USER_QPS_LIMIT=128
USER_URL_QPS_LIMIT=64
[PAYER_MAX]
URL=https://pay-gate.payermax.com/aggregate-pay-gate/api/gateway
KEY=503a970695756efa
MERCHANT_ID=SP11018326
BIZ_TYPE=CUSTOMIZE
VERSION=2.3
FRONT_CALLBACK_URL=https://www.hiloconn.com
SHOW_RESULT=1
EXPIRE_TIME=1800
LANGUAGE=en
[SUD]
API_LIST=https://asc.sudden.ltd
[URL]
BIZ_HTTP=https://apiv1.faceline.live
\ No newline at end of file
package jwt
import (
"git.hilo.cn/hilo-common/resource/config"
"github.com/dgrijalva/jwt-go"
"hilo-group/myerr"
"time"
)
// 载荷,增加用户别名
type Claims struct {
UserId uint64
ExternalId string
jwt.StandardClaims
}
//生成token
func GenerateToken(userId uint64, externalId string, issuer string) (string, error) {
jwtConfig := config.GetConfigJWT()
duration, err := time.ParseDuration(jwtConfig.EXPIRE)
if err != nil {
return "", myerr.WrapErr(err)
}
expireTime := time.Now().Add(duration)
claims := Claims{
UserId: userId,
ExternalId: externalId,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireTime.Unix(), //过期时间
Issuer: issuer, //签名的发行者
},
}
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := tokenClaims.SignedString(GetJWTSecret())
return token, myerr.WrapErr(err)
}
func GetJWTSecret() []byte {
return []byte(config.GetConfigJWT().SECRET)
}
//解析token
func ParseToken(token string) (*Claims, error) {
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return GetJWTSecret(), nil
})
if err != nil {
return nil, myerr.WrapErr(err)
}
if tokenClaims != nil {
claims, ok := tokenClaims.Claims.(*Claims)
if ok && tokenClaims.Valid {
return claims, nil
}
}
return nil, myerr.WrapErr(err)
}
package req
import (
"git.hilo.cn/hilo-common/domain"
"git.hilo.cn/hilo-common/mycontext"
"git.hilo.cn/hilo-common/resource/mysql"
"github.com/gin-gonic/gin"
"hilo-group/domain/cache/user_c"
"hilo-group/domain/model/res_m"
"hilo-group/myerr/bizerr"
)
func GetUserId(c *gin.Context) (mysql.ID, error) {
if userIdStr, ok := c.Keys[mycontext.USERID]; ok {
userId := userIdStr.(uint64)
return userId, nil
}
return 0, bizerr.ParaMissing
}
// 获取userId和externalId
func GetUserIdAndExtId(c *gin.Context, myContext *mycontext.MyContext) (mysql.ID, string, error) {
if userIdStr, ok := c.Keys[mycontext.USERID]; ok {
userId := userIdStr.(uint64)
externalId, ok := c.Get(mycontext.EXTERNAL_ID)
if ok {
return userId, externalId.(string), nil
} else {
user, err := user_c.GetUserTinyById(domain.CreateModelContext(myContext), userId)
if err != nil {
return 0, "", bizerr.ExternalIdNoExist
}
return userId, user.ExternalId, nil
}
}
return 0, "", bizerr.ParaMissing
}
// 获取userId和ExtId和code
func GetUserIdExtIdCode(c *gin.Context, myContext *mycontext.MyContext) (mysql.ID, string, string, error) {
if userIdStr, ok := c.Keys[mycontext.USERID]; ok {
userId := userIdStr.(uint64)
userCode, ok1 := c.Get(mycontext.CODE)
externalId, ok2 := c.Get(mycontext.EXTERNAL_ID)
if ok1 && ok2 {
return userId, externalId.(string), userCode.(string), nil
} else {
user, err := user_c.GetUserTinyById(domain.CreateModelContext(myContext), userId)
if err != nil {
return 0, "", "", bizerr.ExternalIdNoExist
}
return userId, user.ExternalId, user.Code, nil
}
}
return 0, "", "", bizerr.ParaMissing
}
// 获取userId和LANGUAGE
func GetUserIdLang(c *gin.Context, myContext *mycontext.MyContext) (mysql.ID, string, error) {
if userIdStr, ok := c.Keys[mycontext.USERID]; ok {
userId := userIdStr.(uint64)
lang, ok := c.Get(mycontext.LANGUAGE)
if ok {
return userId, lang.(string), nil
} else {
model := domain.CreateModelContext(myContext)
user, err := user_c.GetUserTinyById(model, userId)
if err != nil {
return 0, "", bizerr.ExternalIdNoExist
}
lang, err := res_m.GetLangeByCountry(model.Db, user.Country)
if err != nil {
return 0, "", err
}
return userId, lang, nil
}
}
return 0, "", bizerr.ParaMissing
}
package resp
import (
"encoding/json"
"git.hilo.cn/hilo-common/mycontext"
"github.com/gin-gonic/gin"
"hilo-group/myerr"
"net/http"
)
type Response struct {
Code uint16 `json:"code"` // 错误码
Message interface{} `json:"message"` // 消息
MessageData interface{} `json:"messageData"` // 消息详情
OperationMessage interface{} `json:"operationMessage"` // 操作消息
Data interface{} `json:"data"` // 数据
}
type GameResponse struct {
RetCode uint16 `json:"ret_code"`
RetMsg string `json:"ret_msg"`
SdkErrorCode uint16 `json:"sdk_error_code"`
Data interface{} `json:"data"`
}
/**
* HTTP输出json信息
* param: *gin.Context
* param: error err
* param: interface{} data
*/
func ResponseOk(c *gin.Context, data interface{}) {
// always return http.StatusOK
response := Response{
Code: myerr.GetSuccessCode(),
Message: myerr.GetSuccessMsg(),
OperationMessage: myerr.GetSuccessMsg(),
Data: data,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func GameResponseOk(c *gin.Context, data interface{}) {
// always return http.StatusOK
response := GameResponse{
RetCode: 0,
RetMsg: myerr.GetSuccessMsg(),
SdkErrorCode: 0,
Data: data,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func GameResponseFail(c *gin.Context, err *myerr.GameError) {
// always return http.StatusOK
response := GameResponse{
RetCode: err.Code(),
RetMsg: err.Error(),
SdkErrorCode: err.Code(),
Data: nil,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func ResponseWaring(c *gin.Context, waringError *myerr.WaringError) {
response := Response{
Code: waringError.GetCode(),
Message: waringError.GetMsg(),
OperationMessage: waringError.GetMsg(),
Data: nil,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func ResponseSysError(c *gin.Context, sysError *myerr.SysError) {
response := Response{
Code: sysError.GetCode(),
Message: sysError.GetMsg(),
OperationMessage: sysError.GetMsg(),
Data: nil,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func ResponseBusiness(c *gin.Context, businessError *myerr.BusinessError) {
response := Response{
Code: businessError.GetCode(),
Message: businessError.GetMsg(),
MessageData: businessError.GetData(),
OperationMessage: businessError.GetMsg(),
Data: nil,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func ResponseErrWithString(c *gin.Context, msg interface{}) {
response := Response{
Code: myerr.GetSysErrorCode(),
Message: msg,
OperationMessage: msg,
Data: nil,
}
printResponseBody(c, &response)
c.JSON(http.StatusOK, response)
}
func printResponseBody(c *gin.Context, response interface{}) {
traceId, _ := c.Get(mycontext.TRACEID)
if _traceId, ok := traceId.(string); ok {
c.Header("X-Trace-ID", _traceId)
}
var userId uint64 = 0
if strUserId, ok := c.Get(mycontext.USERID); ok {
userId = strUserId.(uint64)
}
buf, err := json.Marshal(response)
body := ""
if len(buf) < 1024 {
body = string(buf)
} else {
body = string(buf[0:1024])
}
if err == nil {
mycontext.CreateMyContext(c.Keys).Log.Infof("request rsp url:%s, traceId:%v, userId:%d, body:%s", c.Request.RequestURI, traceId, userId, body)
} else {
mycontext.CreateMyContext(c.Keys).Log.Error("request rsp body Marshal fail traceId:%v, err:%v", traceId, err)
}
}
package route
import (
"git.hilo.cn/hilo-common/mycontext"
"git.hilo.cn/hilo-common/resource/config"
"github.com/gin-gonic/gin"
"hilo-group/myerr"
"hilo-group/req"
"hilo-group/resp"
)
/**
* 主要是解决错误的统一处理
*/
/*
*
错误包装类,统一处理。
*/
type HandlerFunc func(c *gin.Context) (*mycontext.MyContext, error)
// 对错误进行处理,
func wrapper(handler HandlerFunc) func(c *gin.Context) {
return func(c *gin.Context) {
var err error
var myContext *mycontext.MyContext
myContext, err = handler(c)
//防止忘记返回myContext, 单myContext为nil的时候,发出恐慌,导致错误覆盖。
if myContext == nil {
myContext = mycontext.CreateMyContext(nil)
}
c.Set(mycontext.ACTION_RESULt, true)
if err != nil {
c.Set(mycontext.ACTION_RESULt, false)
reqUri := c.Request.RequestURI
method := c.Request.Method
userId, _ := req.GetUserId(c)
switch h := err.(type) {
case *myerr.BusinessError:
myContext.Log.Warnf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.GetErr())
resp.ResponseBusiness(c, h)
case *myerr.WaringError:
myContext.Log.Warningf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.GetErr())
if config.AppIsRelease() {
resp.ResponseOk(c, nil)
//ResponseErrWithStringOperation(c, nil, h.GetMsg())
} else {
resp.ResponseWaring(c, h)
}
case *myerr.SysError:
myContext.Log.Errorf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.GetErr())
resp.ResponseSysError(c, h)
default:
// 注意这里,如果是原生的error, 可能打印不出来,使用errors.Wrap配合%+v可以打印堆栈信息,建议上游使用
myContext.Log.Errorf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, err)
resp.ResponseErrWithString(c, err.Error())
}
}
return
}
}
package route
import (
"bytes"
"git.hilo.cn/hilo-common/mycontext"
"git.hilo.cn/hilo-common/mylogrus"
"git.hilo.cn/hilo-common/resource/config"
"github.com/gin-gonic/gin"
"hilo-group/myerr/bizerr"
"hilo-group/req"
"hilo-group/req/jwt"
"hilo-group/resp"
"io/ioutil"
"runtime/debug"
"strings"
"time"
)
/**
controller层全局异常处理
*/
// 等级最高,为了只为最后有返回值到前端
func ExceptionHandle(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
//打印错误堆栈信息
mylogrus.MyLog.Errorf("ExceptionHandle SYSTEM ACTION PANIC: %v, stack: %v", r, string(debug.Stack()))
resp.ResponseErrWithString(c, r)
//终止后续接口调用,不加的话recover到异常后,还会继续执行接口里后续代码
c.Abort()
}
}()
c.Next()
}
// jwt解密
func JWTApiHandle(c *gin.Context) {
logger := mylogrus.MyLog.WithField("URL", c.Request.URL).WithField("METHOD", c.Request.Method)
token := c.GetHeader("token")
if token == "" {
logger.Warnf("token err is empty! %v", c.Request.Header)
resp.ResponseBusiness(c, bizerr.TokenInvalid)
c.Abort()
return
}
claims, err := jwt.ParseToken(token)
if err != nil {
logger.Warnf("token parsed err:%v", err)
resp.ResponseBusiness(c, bizerr.TokenInvalid)
c.Abort()
return
}
logger = logger.WithField("userId", claims.UserId)
if time.Now().Unix() > claims.ExpiresAt {
logger.Warnf("token expire err, now: %d, expiresAt %d", time.Now().Unix(), claims.ExpiresAt)
resp.ResponseBusiness(c, bizerr.TokenInvalid)
c.Abort()
return
}
if claims.Issuer != config.GetConfigJWT().ISSUER_API {
logger.Warnf("token err issuer:%s, configIssuer %s", claims.Issuer, config.GetConfigJWT().ISSUER_API)
resp.ResponseBusiness(c, bizerr.TokenInvalid)
c.Abort()
return
}
var newToken = token
// token 连续7天没玩,第八天回来后给新token(线上是30天过期)
if claims.ExpiresAt-time.Now().Unix() < 86400*7 {
logger.Infof("token nearly expire err, now:%d,expiresAt:%d", time.Now().Unix(), claims.ExpiresAt)
newToken, err = jwt.GenerateToken(claims.UserId, claims.ExternalId, config.GetConfigJWT().ISSUER_API)
if err != nil {
logger.Warnf("token generation failed, err:%v", err)
resp.ResponseBusiness(c, bizerr.TokenInvalid)
c.Abort()
return
}
}
c.Set(mycontext.USERID, claims.UserId)
c.Set(mycontext.EXTERNAL_ID, claims.ExternalId)
c.Writer.Header().Add("token", newToken)
c.Next()
}
// 日志Handle
func LoggerHandle(c *gin.Context) {
//开始时间
start := time.Now()
clientIp := c.ClientIP()
method := c.Request.Method
traceId := genTraceId(c)
reqUri := c.Request.RequestURI
c.Set(mycontext.TRACEID, traceId)
//
header := c.Request.Header
//类型
devicetype := header.Get("Devicetype")
c.Set(mycontext.DEVICETYPE, devicetype)
appVersion := header.Get("Appversion")
c.Set(mycontext.APP_VERSION, appVersion)
c.Set(mycontext.URL, reqUri)
c.Set(mycontext.METHOD, method)
userId, _ := req.GetUserId(c)
bodyStr := ""
contentType := c.Request.Header.Get("Content-Type")
//文件就不打印
if strings.Index(contentType, "multipart/form-data") == -1 {
data, err := c.GetRawData()
if err != nil {
mylogrus.MyLog.Errorf("handle log err:%v", err)
}
bodyStr = string(data)
//很关键
//把读过的字节流重新放到body
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data))
}
mycontext.CreateMyContext(c.Keys).Log.Infof("request start traceId:%v, clientIp:%v, url:%v, method:%v, userId:%v, body:%v, header:%v", traceId, clientIp, reqUri, method, userId, bodyStr, header)
//加载完 defer recover,继续后续接口调用
c.Next()
end := time.Now()
latency := end.Sub(start)
mycontext.CreateMyContext(c.Keys).Log.Infof("request end fullPath:%v,url:%v, method: %v, traceId:%v, latency:%v userId:%v", c.FullPath(), reqUri, method, traceId, latency, userId)
}
package route
import (
"github.com/gin-gonic/gin"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
_ "hilo-group/docs"
)
func InitRouter() *gin.Engine {
var r = gin.Default()
r.GET("/group-swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
needLogin := r.Group("")
needLogin.Use(ExceptionHandle, LoggerHandle, JWTApiHandle)
v1 := needLogin.Group("/v1")
println(v1)
return r
}
package route
import (
"git.hilo.cn/hilo-common/mycontext"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
"strings"
)
func genTraceId(c *gin.Context) string {
if trace := c.GetHeader(mycontext.TRACEID); len(trace) > 0 {
return trace
}
traceId := strings.Replace(uuid.NewV4().String(), "-", "", -1)
traceId = traceId[:len(traceId)/2]
return traceId
}
#!/usr/bin/bash
if [ $# -lt 1 ]
then
echo "Parameters missing."
echo "Usage: $0 <executable name>"
exit
fi
while :
do
$1
printf "$(date) : $1 return $?, wait for 5 seconds to restart\n"
sleep 5
done
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment