@ -5,12 +5,16 @@ import (
"encoding/json"
"fmt"
"net/url"
"server/internal/dao"
"server/internal/model/do"
"sync"
"time"
"server/internal/consts"
"server/internal/model"
"server/utility/ecode"
"server/utility/encrypt"
"server/utility/rsa"
"time"
"github.com/go-resty/resty/v2"
"github.com/gogf/gf/v2/frame/g"
@ -19,341 +23,349 @@ import (
"github.com/gogf/gf/v2/util/grand"
)
type gamelifeC lient struct {
PlatId string ` json:"platId" `
BrandId string ` json:"brand_id" `
Secret string ` json:"secret" `
Mode string ` json:"mode" `
keyivUrlMap map [ string ] string ` json:"-" ` // 存储获取用户 aes key 和 iv 的 url
boundUrlMap map [ string ] string ` json:"-" ` // 存储用户绑定状态的 url
unBoundUrlMap map [ string ] string ` json:"-" ` // 存储用户解绑状态的 url
getBoundUrl map [ string ] string ` json:"-" ` // 存储用户绑定状态的 url
taskUrlMap map [ string ] string ` json:"-" `
// Config holds gamelife c lient configuration.
type Config struct {
PlatID string
BrandID string
Secret string
Mode string
}
var (
instance * gamelifeClient
)
// gamelifeClient manages interactions with the GameLife system.
type gamelifeClient struct {
config Config
keyIVURLMap map [ string ] string
boundURLMap map [ string ] string
unBoundURLMap map [ string ] string
getBoundURL map [ string ] string
taskURLMap map [ string ] string
}
func newgamelifeClient ( ctx context . Context ) * gamelifeClient {
instance = & gamelifeClien t {
PlatId : g . Config ( ) . MustGet ( ctx , "gamelife.platId" ) . String ( ) ,
BrandId : g . Config ( ) . MustGet ( ctx , "gamelife.brandId" ) . String ( ) ,
Secret : g . Config ( ) . MustGet ( ctx , "gamelife.secret" ) . String ( ) ,
Mode : g . Config ( ) . MustGet ( ctx , "gamelife.mode" ) . String ( ) ,
keyivUrlMap : map [ string ] string {
// URLMaps defines the URL mappings for different environments.
var URLMaps = struc t {
KeyIV map [ string ] string
Bound map [ string ] string
UnBound map [ string ] string
GetBound map [ string ] string
Task map [ string ] string
} {
KeyIV : map [ string ] string {
"test" : "https://api-test.nes.smoba.qq.com/pvpesport.sgamenes.commcgi.commcgi/GetExplatSecret" ,
"prod" : "https://api.nes.smoba.qq.com/pvpesport.sgamenes.commcgi.commcgi/GetExplatSecret" ,
} ,
b oundUrlMap : map [ string ] string {
B ound: map [ string ] string {
"test" : "https://h5-test.cafe.qq.com/pmd-mobile.cafe.bind-account.pc.feature.bind-account/#/index" ,
"prod" : "https://h5.cafe.qq.com/pmd-mobile.cafe.bind-account.pc/#/index" ,
} ,
u nBoundUrlMap : map [ string ] string {
U nBound: map [ string ] string {
"test" : "https://h5-test.cafe.qq.com/pmd-mobile.cafe.bind-account.pc.feature.bind-account/#/bind-manage" ,
"prod" : "https://h5.cafe.qq.com/pmd-mobile.cafe.bind-account.pc/#/bind-manage" ,
} ,
g etBoundUrl : map [ string ] string {
G etBound: map [ string ] string {
"test" : "https://api-test.cafe.qq.com/tipmp.user.authinfo_cgi.authinfo_cgi/GetPlatUserInfo" ,
"prod" : "https://api.cafe.qq.com/tipmp.user.authinfo_cgi.authinfo_cgi/GetPlatUserInfo" ,
} ,
t askUrlMap : map [ string ] string {
T ask: map [ string ] string {
"test" : "https://api-test.cafe.qq.com/netbar.cafe.open_api.open_api/" ,
"prod" : "https://api.cafe.qq.com/netbar.cafe.open_api.open_api/" ,
} ,
}
glog . Infof ( ctx , "初始化 gamelifeClient 成功" )
return instance
}
func init ( ) {
ctx := context . Background ( )
newgamelifeClient ( ctx )
}
var (
instance * gamelifeClient
once sync . Once
)
// GetGamelifeClient returns the singleton gamelifeClient instance, initializing it if necessary.
func GetGamelifeClient ( ctx context . Context ) * gamelifeClient {
if instance == nil {
instance = newg amelifeClient ( ctx )
}
once . Do ( func ( ) {
instance = newG amelifeClient ( ctx )
} )
return instance
}
func ( s * gamelifeClient ) GetUserKeyIV ( ctx context . Context , popenId string ) ( cache model . UserGamelifeCache , err error ) {
// 创建加密原数据
oriData := g . MapStrStr {
" PlatId" : s . PlatId ,
"PopenId" : popenId ,
// newGamelifeClient initializes a new gamelifeClient with configuration and URL mappings.
func newGamelifeClient ( ctx context . Context ) * gamelifeClient {
cfg := Config {
PlatID : g . Config ( ) . MustGet ( ctx , "gamelife.platId" ) . String ( ) ,
BrandID : g . Config ( ) . MustGet ( ctx , "gamelife.brandId" ) . String ( ) ,
Secret : g . Config ( ) . MustGet ( ctx , "gamelife.secret" ) . String ( ) ,
Mode : g . Config ( ) . MustGet ( ctx , "gamelife.mode" ) . String ( ) ,
}
marshal , err := json . Marshal ( oriData )
if err != nil {
err = ecode . Fail . Sub ( "序列化 json 数据出现异常" )
return
client := & gamelifeClient {
config : cfg ,
keyIVURLMap : URLMaps . KeyIV ,
boundURLMap : URLMaps . Bound ,
unBoundURLMap : URLMaps . UnBound ,
getBoundURL : URLMaps . GetBound ,
taskURLMap : URLMaps . Task ,
}
glog . Info ( ctx , "Initialized gamelifeClient successfully" , map [ string ] interface { } {
"plat_id" : cfg . PlatID ,
"mode" : cfg . Mode ,
} )
return client
}
key , err := rsa . GetRsaClient ( ) . EncryptWithRsaPublicKey ( marshal )
if err != nil {
err = ecode . Fail . Sub ( "序列化 json 数据出现异常" )
return
// GetUserKeyIV retrieves or refreshes the AES key, IV, and token for a user, storing them in cache.
func ( s * gamelifeClient ) GetUserKeyIV ( ctx context . Context , popenID string ) ( model . UserGamelifeCache , error ) {
oriData := map [ string ] string {
"PlatId" : s . config . PlatID ,
"PopenId" : popenID ,
}
// base64编码
base64Encode : = encrypt . Base64Encode ( key )
// 向游戏人生发送请求
marshaled , err := json . Marshal ( oriData )
if err ! = nil {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "序列化 json 数据出现异常" )
}
encrypted , err := rsa . GetRsaClient ( ) . EncryptWithRsaPublicKey ( marshaled )
if err != nil {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "序列化 json 数据出现异常" )
}
type httpResult struct {
Secret string ` json:"secret" `
Key string ` json:"key" ` // 该用户的token, 后续的账号绑定、登录态携带都需要此参数。
Key string ` json:"key" `
}
var result httpResult
resp , err := resty . New ( ) . R ( ) . SetBody ( map [ string ] string {
"plat_id" : s . PlatId ,
"key" : base64Encode ,
} ) . SetResult ( & result ) . Post ( s . keyivUrlMap [ s . Mode ] )
resp , err := resty . New ( ) . R ( ) .
SetBody ( map [ string ] string {
"plat_id" : s . config . PlatID ,
"key" : encrypt . Base64Encode ( encrypted ) ,
} ) .
SetResult ( & result ) .
Post ( s . keyIVURLMap [ s . config . Mode ] )
if err != nil || resp . StatusCode ( ) != 200 {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "获取用户信息失败" )
}
decoded , err := encrypt . Base64Decode ( result . Secret )
if err != nil {
err = ecode . Fail . Sub ( "获取 用户信息失败" )
return
}
if resp . StatusCode ( ) != 200 {
err = ecode . Fail . Sub ( "获取用户信息失败" )
return
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "解密 用户信息失败" )
}
decode , err := encrypt . Base64Decode ( result . S ecret )
plain , err := rsa . GetRsaClient ( ) . DecryptWithRsaPrivateKey ( d ecoded )
if err != nil {
err = ecode . Fail . Sub ( "解密用户信息失败" )
return
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "解密用户信息失败" )
}
plain , err := rsa . GetRsaClient ( ) . DecryptWithRsaPrivateKey ( decode )
if err != nil {
err = ecode . Fail . Sub ( "解密用户信息失败" )
return
}
aesResult := struct {
var aesResult struct {
Key string ` json:"key" `
IV string ` json:"iv" `
} { }
if err = json . Unmarshal ( plain , & aesResult ) ; err != nil {
err = ecode . Fail . Sub ( "解密用户信息失败" )
return
}
if err : = json . Unmarshal ( plain , & aesResult ) ; err != nil {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "解密用户信息失败" )
}
gamelifeC ache := model . UserGamelifeCache { Aes : aesResult . Key , IV : aesResult . IV , Token : result . Key }
// 将用户的 aeskey 和 iv 存储到缓存当中,用于后续请求数据加密, 固定时间 2 小时同时不同用户加上一个随机时间
if err = g . Redis ( ) . SetEX ( ctx , fmt . Sprintf ( consts . GameLifeUserKey , popenId ) , gamelifeCache , int64 ( consts . GameLifeUserExpire + grand . Intn ( 1000 ) ) ) ; err != nil {
err = ecode . Fail . Sub ( "设置用户信息失败" )
return
c ache := model . UserGamelifeCache {
Aes : aesResult . Key ,
IV : aesResult . IV ,
Token : result . Key ,
}
return
cacheKey := fmt . Sprintf ( consts . GameLifeUserKey , popenID )
if err := g . Redis ( ) . SetEX ( ctx , cacheKey , cache , int64 ( consts . GameLifeUserExpire + grand . Intn ( 1000 ) ) ) ; err != nil {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "设置用户信息失败" )
}
// GetUrl 为 GameLife 系统生成绑定或解绑定的 URL。
// 它从缓存中获取用户加密密钥(若缓存过期则重新获取),加密用户数据,
// 并根据操作类型(绑定或解绑)构造包含查询参数的 URL。
//
// 参数:
// - ctx: 用于控制请求生命周期和取消的上下文。
// - popenid: 用户的唯一标识符。
// - appname: 应用程序名称。
// - nickname: 用户昵称,仅在解绑操作时需要(绑定操作时忽略)。
// - bindType: 绑定类型( 1 表示 QQ, 其他值表示微信) 。
// - isBound: 布尔值, 指示操作类型( true 表示绑定, false 表示解绑)。
//
// 返回值:
// - string: 构造的绑定或解绑操作的 URL。
// - error: 如果任何步骤( 缓存获取、密钥获取、JSON 序列化、加密或 URL 构造)失败,则返回错误。
//
// 错误:
// - 如果发生以下情况,将返回带有具体错误信息的错误:
// - 无法解析基础 URL。
// - 无法从缓存或通过 GetUserKeyIV 获取用户信息。
// - JSON 序列化或反序列化失败。
// - AES 加密或 Base64 编码失败。
func ( s * gamelifeClient ) GetUrl ( ctx context . Context , popenid , appname , nickname string , bindType int , isBound bool ) ( string , error ) {
// 从缓存中获取用户的 aes 和 iv
rooturl := s . boundUrlMap [ s . Mode ]
return cache , nil
}
// GetUrl generates a URL for binding or unbinding a user in the GameLife system.
// It retrieves or refreshes user encryption keys from cache and constructs a URL
// with query parameters using buildQueryParams.
func ( s * gamelifeClient ) GetUrl ( ctx context . Context , popenID , appName , nickname string , bindType int , isBound bool ) ( string , error ) {
rootURL := s . boundURLMap [ s . config . Mode ]
if ! isBound {
rooturl = s . unBoundUrl Map [ s . Mode ]
rootURL = s . unBoundURL Map [ s . config . Mode]
}
gamelifeCacheKey := fmt . Sprintf ( consts . GameLifeUserKey , popenid )
cacheData , err := g . Redis ( ) . Get ( ctx , gamelifeCacheKey )
cache , err := s . getOrRefreshCache ( ctx , popenID )
if err != nil {
return "" , ecode . Fail . Sub ( "从缓存中获取用户信息失败" )
return "" , err
}
var gamelifeCache model . UserGamelifeCache
if cacheData . IsEmpty ( ) {
// 如果缓存不存在或已过期,重新调用 GetUserKeyIV
data , err := s . GetUserKeyIV ( ctx , popenid )
cache . Params , err = s . buildQueryParams ( ctx , popenID , cache , appName , nickname , bindType , isBound )
if err != nil {
return "" , ecode . Fail . Sub ( "获取用户信息失败" )
}
gamelifeCache = model . UserGamelifeCache { Aes : data . Aes , IV : data . IV , Token : data . Token }
} else {
// 缓存存在,直接解析
if err = json . Unmarshal ( cacheData . Bytes ( ) , & gamelifeCache ) ; err != nil {
return "" , ecode . Fail . Sub ( "解析用户信息失败" )
}
return "" , err
}
// 序列化原始数据
oriData := g . Map {
"PopenId" : popenid ,
cacheKey := fmt . Sprintf ( consts . GameLifeUserKey , popenID )
ttl , err := g . Redis ( ) . TTL ( ctx , cacheKey )
if err != nil {
return "" , ecode . Fail . Sub ( "获取缓存过期时间失败" )
}
if err := g . Redis ( ) . SetEX ( ctx , cacheKey , cache , ttl ) ; err != nil {
return "" , ecode . Fail . Sub ( "更新缓存失败" )
}
return fmt . Sprintf ( "%s?%s" , rootURL , cache . Params ) , nil
}
// GetBound retrieves the binding status of a user from the GameLife system.
// It uses buildQueryParams to construct the encrypted user data for the POST body.
func ( s * gamelifeClient ) GetBound ( ctx context . Context , popenID string ) ( * model . UserBoundResult , error ) {
cache , err := s . getOrRefreshCache ( ctx , popenID )
if err != nil {
return nil , err
}
// Use default values for appName, nickname, bindType, and isBound
platUserStr , err := s . buildQueryParams ( ctx , popenID , cache , s . config . PlatID , "" , 1 , true )
if err != nil {
return nil , err
}
// Parse platUserStr to extract Token and PlatUserInfoStr
queryParams , err := url . ParseQuery ( platUserStr )
if err != nil {
return nil , ecode . Fail . Sub ( "解析查询参数失败" )
}
extplatDataStr := queryParams . Get ( "extplat_data" )
if extplatDataStr == "" {
return nil , ecode . Fail . Sub ( "查询参数中缺少 extplat_data" )
}
var extplatData map [ string ] string
if err := json . Unmarshal ( [ ] byte ( extplatDataStr ) , & extplatData ) ; err != nil {
return nil , ecode . Fail . Sub ( "解析 extplat_data 失败" )
}
postBody := map [ string ] string {
"plat_id" : s . config . PlatID ,
"plat_user_info" : popenID ,
"plat_user_str" : gconv . String ( extplatData ) ,
}
var result model . UserBoundResult
resp , err := resty . New ( ) . R ( ) .
SetBody ( postBody ) .
SetResult ( & result ) .
Post ( s . getBoundURL [ s . config . Mode ] )
if err != nil || resp . StatusCode ( ) != 200 {
return nil , ecode . Fail . Sub ( "向游戏人生获取绑定信息出现异常" )
}
glog . Info ( ctx , "Fetched user binding info" , map [ string ] interface { } {
"popen_id" : popenID ,
"result" : result ,
} )
return & result , nil
}
// buildQueryParams constructs URL query parameters for GameLife requests using cached AES, IV, and token.
// It encrypts user data and encodes it into a URL query string.
func ( s * gamelifeClient ) buildQueryParams ( ctx context . Context , popenID string , cache model . UserGamelifeCache , appName , nickname string , bindType int , isBound bool ) ( string , error ) {
oriData := map [ string ] interface { } {
"PopenId" : popenID ,
"TimeStamp" : time . Now ( ) . Unix ( ) ,
}
marshal , err := json . Marshal ( oriData )
marshaled , err := json . Marshal ( oriData )
if err != nil {
return "" , ecode . Fail . Sub ( "序列化用户信息失败" )
}
// 加密用户信息
aesEncrypt , err := encrypt . AesEncryptCBCPKCS5 ( marshal , [ ] byte ( gamelifeCache . Aes ) , [ ] byte ( gamelifeCache . IV ) )
encrypted , err := encrypt . AesEncryptCBCPKCS5 ( marshaled , [ ] byte ( cache . Aes ) , [ ] byte ( cache . IV ) )
if err != nil {
return "" , ecode . Fail . Sub ( "加密用户信息失败" )
}
platUserInfoStr := encrypt . Base64Encode ( aesE ncrypt)
explatData := g . MapStrStr { "Token" : gamelifeCache . Token , "PlatUserInfoStr" : platUserInfoStr }
queryParams := url . Values { }
queryParams . Add ( "extplat_plat" , s . PlatId )
queryParams . Add ( "app_name" , appname )
platUserInfoStr := encrypt . Base64Encode ( e ncrypted )
if bindType = = 1 {
queryParams . Add ( "bind_type" , consts . GamelifeExtplatBoundTypeQQ )
} else {
queryParams . Add ( "bind_type" , consts . GamelifeExtplatBoundTypeWX )
queryParams : = url . Values {
"extplat_plat" : { s . config . PlatID } ,
"app_name" : { appName } ,
"bind_type" : { consts . GamelifeExtplatBoundTypeQQ } ,
"extplat_type" : { consts . GamelifeExtplatType } ,
"mini_program_band" : { consts . GamelifeMiniProgramBand } ,
"extplat_extra" : { consts . GamelifeExtplatExtraPc } ,
"extplat_data" : { gconv . String ( map [ string ] string { "Token" : cache . Token , "PlatUserInfoStr" : platUserInfoStr } ) } ,
}
if bindType != 1 {
queryParams . Set ( "bind_type" , consts . GamelifeExtplatBoundTypeWX )
}
queryParams . Add ( "extplat_type" , consts . GamelifeExtplatType )
queryParams . Add ( "mini_program_band" , consts . GamelifeMiniProgramBand )
queryParams . Add ( "extplat_extra" , consts . GamelifeExtplatExtraPc )
queryParams . Add ( "extplat_data" , gconv . String ( explatData ) )
// 解绑时加 nickname
if ! isBound {
queryParams . Add ( "nickname" , nickname )
}
// 将请求参数更新到缓存中
gamelifeCache . Params = queryParams . Encode ( )
// 获取原缓存值、过期时间
ttl , err := g . Redis ( ) . TTL ( ctx , gamelifeCacheKey )
if err != nil {
return "" , ecode . Fail . Sub ( "获取缓存过期时间失败" )
}
// 更新数据
if err = g . Redis ( ) . SetEX ( ctx , gamelifeCacheKey , gamelifeCache , ttl ) ; err != nil {
return "" , ecode . Fail . Sub ( "更新缓存失败" )
}
// 拼接最终 URL
url := fmt . Sprintf ( "%s?%s" , rooturl , queryParams . Encode ( ) )
return url , nil
}
// GetBound 获取用户绑定情况
func ( s * gamelifeClient ) GetBound ( ctx context . Context , popenid string ) ( * model . UserBoundResult , error ) {
// 获取基础 URL
rooturl := s . getBoundUrl [ s . Mode ]
// 获取缓存
cacheData , err := g . Redis ( ) . Get ( ctx , fmt . Sprintf ( consts . GameLifeUserKey , popenid ) )
if err != nil {
return nil , ecode . Fail . Sub ( "从缓存中获取用户信息失败" )
}
var gamelifeCache model . UserGamelifeCache
if cacheData . IsEmpty ( ) {
// 如果缓存不存在或已过期,重新调用 GetUserKeyIV
data , err := s . GetUserKeyIV ( ctx , popenid )
if err != nil {
return nil , ecode . Fail . Sub ( "获取用户信息失败" )
}
gamelifeCache = model . UserGamelifeCache { Aes : data . Aes , IV : data . IV , Token : data . Token }
} else {
if err = json . Unmarshal ( cacheData . Bytes ( ) , & gamelifeCache ) ; err != nil {
return nil , ecode . Fail . Sub ( "解析用户信息失败" )
}
}
// 加密原始数据
oriData := g . Map {
"PopenId" : popenid ,
"TimeStamp" : time . Now ( ) . Unix ( ) ,
}
marshal , err := json . Marshal ( oriData )
if err != nil {
return nil , ecode . Fail . Sub ( "序列化用户信息失败" )
}
aesEncrypt , err := encrypt . AesEncryptCBCPKCS5 ( marshal , [ ] byte ( gamelifeCache . Aes ) , [ ] byte ( gamelifeCache . IV ) )
if err != nil {
return nil , ecode . Fail . Sub ( "加密用户信息失败" )
}
platUserInfoStr := encrypt . Base64Encode ( aesEncrypt )
postBody := g . MapStrStr {
"plat_id" : s . PlatId ,
"plat_user_info" : popenid ,
"plat_user_str" : gconv . String ( g . MapStrStr { "Token" : gamelifeCache . Token , "PlatUserInfoStr" : platUserInfoStr } ) ,
}
var result model . UserBoundResult
resp , err := resty . New ( ) . R ( ) . SetBody ( postBody ) . SetResult ( & result ) . Post ( rooturl )
if err != nil {
return nil , ecode . Fail . Sub ( "向游戏人生获取绑定信息出现异常" )
}
if resp . StatusCode ( ) != 200 {
return nil , ecode . Fail . Sub ( "向游戏人生获取绑定信息失败" )
}
glog . Infof ( ctx , "获取用户游戏人生绑定信息: %v" , result )
return & result , nil
return queryParams . Encode ( ) , nil
}
// RequestActivity handles activity requests for the GameLife system based on the service name.
// It uses the cached UserGamelifeCache.Params for authenticated task list requests, reconstructing
// Params if empty and updating the cache.
func ( s * gamelifeClient ) RequestActivity ( ctx context . Context , in * model . QQNetbarActivityIn ) ( interface { } , error ) {
client := resty . New ( )
taskURL := s . taskURLMap [ s . config . Mode ]
switch in . ServiceName {
case consts . GetNonLoginTaskList :
result := model . GameTaskResponse { }
in . TaskParam . Source = s . PlatId
in . TaskParam . BrandId = s . BrandId
in . TaskParam . Source = s . config . PlatID
in . TaskParam . BrandId = s . config . BrandID
var result model . GameTaskResponse
resp , err := client . R ( ) .
SetContext ( ctx ) .
SetBody ( in . TaskParam ) .
SetResult ( & result ) .
Post ( s . taskUrlMap [ s . Mode ] + consts . GetNonLoginTaskList )
if err != nil {
Post ( taskURL + consts . GetNonLoginTaskList )
if err != nil || resp . IsError ( ) {
return nil , ecode . Fail . Sub ( "请求出现异常" )
}
if resp . IsError ( ) {
return nil , ecode . Fail . Sub ( "请求失败" )
}
return & result , nil
case consts . GetTaskList :
result := model . GameTaskResponse { }
in . TaskParam . BrandId = s . BrandId
get , err := g . Redis ( ) . Get ( ctx , fmt . Sprintf ( consts . GameLifeUserKey , in . PopenId ) )
cache , err := s . getOrRefreshCache ( ctx , in . PopenId )
if err != nil {
return nil , ecode . Fail . Sub ( "从缓存中获取用户信息失败" )
return nil , err
}
var gamelifeCache model . User Gamel ifeCache
if err = json . Unmarshal ( get . Bytes ( ) , & gamelifeCache ) ; err ! = nil {
return nil , ecode . Fail . Sub ( "解析用户信息失败" )
cacheKey := fmt . Sprintf ( consts . GameL ifeUserKey , in . PopenId )
if cache . Params = = "" {
// Reconstruct Params with default values
value , err := dao . Games . Ctx ( ctx ) . Where ( do . Games { GameId : in . TaskParam . Gid } ) . Fields ( dao . Games . Columns ( ) . GameCode ) . Value ( )
if err != nil {
return nil , ecode . Fail . Sub ( "获取游戏名称失败" )
}
cache . Params , err = s . buildQueryParams ( ctx , in . PopenId , cache , value . String ( ) , "" , in . BindType , true )
if err != nil {
return nil , err
}
// Update cache with new Params
ttl , err := g . Redis ( ) . TTL ( ctx , cacheKey )
if err != nil {
return nil , ecode . Fail . Sub ( "获取缓存过期时间失败" )
}
if err := g . Redis ( ) . SetEX ( ctx , cacheKey , cache , ttl ) ; err != nil {
return nil , ecode . Fail . Sub ( "更新缓存失败" )
}
}
in . TaskParam . BrandId = s . config . BrandID
var result model . GameTaskResponse
resp , err := client . R ( ) .
SetContext ( ctx ) .
SetBody ( in . TaskParam ) .
SetResult ( & result ) .
Post ( fmt . Sprintf ( "%s%s?%s" , s . taskUrlMap [ s . Mode ] , consts . GetTaskList , gamelifeC ache. Params ) )
if err != nil {
Post ( fmt . Sprintf ( "%s%s?%s" , taskURL , consts . GetTaskList , c ache. Params ) )
if err != nil || resp . IsError ( ) {
return nil , ecode . Fail . Sub ( "请求出现异常" )
}
if resp . IsError ( ) {
return nil , ecode . Fail . Sub ( "请求失败" )
}
return & result , nil
default :
return nil , ecode . Fail . Sub ( "不支持的任务" )
return nil , ecode . Fail . Sub ( fmt . Sprintf ( "不支持的任务: %s" , in . ServiceName ) )
}
}
// getOrRefreshCache retrieves user cache or refreshes it if expired.
func ( s * gamelifeClient ) getOrRefreshCache ( ctx context . Context , popenID string ) ( model . UserGamelifeCache , error ) {
cacheKey := fmt . Sprintf ( consts . GameLifeUserKey , popenID )
cacheData , err := g . Redis ( ) . Get ( ctx , cacheKey )
if err != nil {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "从缓存中获取用户信息失败" )
}
var cache model . UserGamelifeCache
if cacheData . IsEmpty ( ) {
return s . GetUserKeyIV ( ctx , popenID )
}
if err := json . Unmarshal ( cacheData . Bytes ( ) , & cache ) ; err != nil {
return model . UserGamelifeCache { } , ecode . Fail . Sub ( "解析用户信息失败" )
}
return cache , nil
}