From c02d5b5201e4b633ad7cbb737575a6f64a83e759 Mon Sep 17 00:00:00 2001 From: yuxh Date: Thu, 3 Jul 2025 18:06:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20=E4=BC=98=E5=8C=96=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20msdk=20=E6=9C=8D=E5=8A=A1=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复了当 signer为 nil 时不执行签名逻辑的问题 - 优化了签名处理流程,提高了代码的可读性和性能 - 新增了对 msdk 服务的支持,包括用户归因和设备信息获取功能 - 添加了 md5 加密工具函数和相关测试 --- sdk/auth/rpc_signature_composer.go | 50 +++---- sdk/client.go | 17 ++- sdk/responses/response.go | 2 +- sdk/utils/utils.go | 5 + sdk/utils/utils_test.go | 4 + services/msdk/client.go | 61 ++++++++ services/msdk/client_test.go | 141 +++++++++++++++++++ services/msdk/user.go | 218 +++++++++++++++++++++++++++++ 8 files changed, 467 insertions(+), 31 deletions(-) create mode 100644 services/msdk/client.go create mode 100644 services/msdk/client_test.go create mode 100644 services/msdk/user.go diff --git a/sdk/auth/rpc_signature_composer.go b/sdk/auth/rpc_signature_composer.go index 049f80f..95dba53 100644 --- a/sdk/auth/rpc_signature_composer.go +++ b/sdk/auth/rpc_signature_composer.go @@ -18,38 +18,40 @@ func signRpcRequest(request requests.AcsRequest, signer Signer) (err error) { if err != nil { return } - if _, isContainsSign := request.GetQueryParams()["sign"]; isContainsSign { - delete(request.GetQueryParams(), "sign") + if signer != nil { + if _, isContainsSign := request.GetQueryParams()["sign"]; isContainsSign { + delete(request.GetQueryParams(), "sign") + } + stringToSign := buildRpcStringToSign(request) + request.SetStringToSign(stringToSign) + signature := signer.Sign(stringToSign, "&") + request.GetQueryParams()["sign"] = signature + debug("GrSdk sign: %s", signature) + debug("GrSdk sign string: %s", stringToSign) + debug("GrSdk sign: \r\n") } - - stringToSign := buildRpcStringToSign(request) - request.SetStringToSign(stringToSign) - signature := signer.Sign(stringToSign, "&") - request.GetQueryParams()["sign"] = signature - debug("GrSdk sign: %s", signature) - debug("GrSdk sign string: %s", stringToSign) - debug("GrSdk sign: \r\n") return } func completeRpcSignParams(request requests.AcsRequest, signer Signer) (err error) { + if signer != nil { + var accessKeyFrom string + if accessKeyFrom, err = signer.GetAccessKeyFrom(); err != nil { + return + } - var accessKeyFrom string - if accessKeyFrom, err = signer.GetAccessKeyFrom(); err != nil { - return + queryParams := request.GetQueryParams() + queryParams["access_time"] = fmt.Sprintf("%d", time.Now().Unix()) + queryParams["access_key"], err = signer.GetAccessKeyId() + queryParams["access_from"] = accessKeyFrom + request.GetHeaders()["Gr-Sdk-From"] = accessKeyFrom + + if err != nil { + return + } } - - queryParams := request.GetQueryParams() - queryParams["access_time"] = fmt.Sprintf("%d", time.Now().Unix()) - queryParams["access_key"], err = signer.GetAccessKeyId() - queryParams["access_from"] = accessKeyFrom - - if err != nil { - return - } - request.GetHeaders()["Content-type"] = requests.Form - request.GetHeaders()["Gr-Sdk-From"] = accessKeyFrom + formString := utils.GetUrlFormedMap(request.GetFormParams()) request.SetContent(bytes.NewBufferString(formString).Bytes()) return diff --git a/sdk/client.go b/sdk/client.go index 7b59027..03fc2c7 100644 --- a/sdk/client.go +++ b/sdk/client.go @@ -91,11 +91,15 @@ func (client *Client) Init() (err error) { func (client *Client) InitWithAccessKey(accessKeyId, accessKeySecret, accessKeyFrom string) (err error) { config := client.InitWithConfig() - credential := &credentials.BaseCredential{ - AccessKeyId: accessKeyId, - AccessKeySecret: accessKeySecret, - AccessKeyFrom: accessKeyFrom, + var credential auth.Credential + if accessKeyId != "" { + credential = &credentials.BaseCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + AccessKeyFrom: accessKeyFrom, + } } + return client.InitWithOptions(config, credential) } @@ -133,8 +137,9 @@ func (client *Client) InitWithOptions(config *Config, credential auth.Credential if config.Timeout > 0 { client.httpClient.Timeout = config.Timeout } - - client.signer, err = auth.NewSignerWithCredential(credential, client.ProcessCommonRequestWithSigner) + if credential != nil { + client.signer, err = auth.NewSignerWithCredential(credential, client.ProcessCommonRequestWithSigner) + } return } diff --git a/sdk/responses/response.go b/sdk/responses/response.go index b45f2f5..af5d033 100644 --- a/sdk/responses/response.go +++ b/sdk/responses/response.go @@ -89,7 +89,7 @@ func Unmarshal(response AcsResponse, httpResponse *http.Response, format string) return } - if _, isCommonResponse := response.(CommonResponse); isCommonResponse { + if _, isCommonResponse := response.(*CommonResponse); isCommonResponse { return } diff --git a/sdk/utils/utils.go b/sdk/utils/utils.go index c655962..fff6a19 100644 --- a/sdk/utils/utils.go +++ b/sdk/utils/utils.go @@ -109,3 +109,8 @@ func InitStructWithDefaultTag(bean interface{}) { } } } + +func Md5(data string) string { + s := md5.Sum([]byte(data)) + return hex.EncodeToString(s[:]) +} diff --git a/sdk/utils/utils_test.go b/sdk/utils/utils_test.go index 96fcf22..db7838e 100644 --- a/sdk/utils/utils_test.go +++ b/sdk/utils/utils_test.go @@ -23,3 +23,7 @@ func TestInitStructWithDefaultTag(t *testing.T) { InitStructWithDefaultTag(testcase) fmt.Printf("%+v", testcase) } + +func TestMd5(t *testing.T) { + t.Log(Md5("123456")) +} diff --git a/services/msdk/client.go b/services/msdk/client.go new file mode 100644 index 0000000..08cfbfd --- /dev/null +++ b/services/msdk/client.go @@ -0,0 +1,61 @@ +package msdk + +import ( + "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk" + "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/requests" + "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/responses" +) + +const VERSION = "2024-06-25" + +var HOST = requests.Host{ + Default: "msdk.api.gaore.com", +} + +type Client struct { + sdk.Client +} + +func NewClient() (client *Client, err error) { + client = &Client{} + err = client.Init() + return +} + +// GetIdfa 获取设备归因信息 +func (c *Client) GetIdfa(req *GetIdfaReq) (resp *GetIdfaResp, err error) { + resp = &GetIdfaResp{ + BaseResponse: &responses.BaseResponse{}, + } + if req.Imei == "" && req.Idfa != "" { + req.Imei = req.Idfa + } + err = c.DoAction(req, resp) + return +} + +// GetUserAttr 获取用户归因信息 +func (c *Client) GetUserAttr(req *GetUserAttrReq) (resp *GetUserAttrResp, err error) { + resp = &GetUserAttrResp{ + BaseResponse: &responses.BaseResponse{}, + } + err = c.DoAction(req, resp) + return +} + +// GetImei 用户注册归因 +func (c *Client) GetImei(req *GetImeiReq) (resp *GetImeiResp, err error) { + resp = &GetImeiResp{ + BaseResponse: &responses.BaseResponse{}, + } + err = c.DoAction(req, resp) + return +} + +func (c *Client) SetImei(req *SetImeiReq) (resp *SetImeiResp, err error) { + resp = &SetImeiResp{ + BaseResponse: &responses.BaseResponse{}, + } + err = c.DoAction(req, resp) + return +} diff --git a/services/msdk/client_test.go b/services/msdk/client_test.go new file mode 100644 index 0000000..37e6cfc --- /dev/null +++ b/services/msdk/client_test.go @@ -0,0 +1,141 @@ +package msdk + +import ( + "testing" +) + +func TestClient_GetUserAttr(t *testing.T) { + client, err := NewClient() + if err != nil { + t.Error(err) + return + } + req := CreateGetUserAttrReq("ql83649336", "xxhbbxxl") + resp, err := client.GetUserAttr(req) + if err != nil { + t.Error(err) + return + } + t.Log(resp) +} + +func TestGetIdfa(t *testing.T) { + client, err := NewClient() + if err != nil { + t.Error(err) + return + } + req := CreateGetIdfaReq() + req.ChannelId = 1 + req.GameId = 3706 + req.Ip = "112.23.230.210" + req.Imei = "00000000-0000-0000-0000-000000000000" + req.LongId = "daff65f07c7cf84862f4217773e3d613" + req.SdkVersion = "1.7.2" + req.Os = "ios" + req.GameOs = 2 + req.GameSubOs = 0 + req.Ua = "Mozilla/5.0 (iPad; CPU OS 15_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" + req.GameSign = "hlhj" + req.PkgAgentId = "100" + req.PkgSiteId = "1001" + req.Network = "Wifi" + req.Idfv = "53F2A5F7-B1D4-4716-9775-07727C29BC7A" + req.ScreenResolution = "2360*1640" + req.System = "15.6.1" + req.ProcessorModel = "iPad13,16" + req.BaseBand = "" + req.Model = "iPad13,16" + req.Battery = "74" + req.Oaid = "" + req.AdInfo = "" + req.WxPlatform = "" + req.AdDevice = "{\"bootTimeInSec\":\"1745364499\",\"countryCode\":\"CN\",\"language\":\"zh-Hans-CN\",\"deviceName\":\"1b9018182a49e16ba85bb095f224867c\",\"systemVersion\":\"15.6.1\",\"machine\":\"iPad13,16\",\"carrierInfo\":\"unknown\",\"memory\":\"8000356352\",\"disk\":\"255983177728\",\"sysFileTime\":\"1663537105.729985\",\"model\":\"J407AP\",\"timeZone\":\"28800\",\"mntId\":\"A058368B97B0D073829608AAC13FFA64D9BEFD0FE3E14EDB106F2BABD6DF94B1C2BFC7509CBB683EE5B22D91A19FF67A@/dev/disk0s1s1\",\"deviceInitTime\":\"1663537056.906820124\"}" + resp, err := client.GetIdfa(req) + if err != nil { + t.Error(err) + return + } + t.Log(resp) +} + +func TestGetImei(t *testing.T) { + client, err := NewClient() + if err != nil { + t.Error(err) + return + } + req := CreateGetImeiReq() + req.Uid = 218047048 + req.UserName = "ct939725671" + req.GameId = 8050 + req.GameSign = "mrwld" + req.RegTime = 1750939725 + req.Imei = "of5wO5sKWep0OFPt9rWQf6xNJVPg" + req.ChannelId = 1 + req.AgentId = 100 + req.SiteId = 1001 + req.Ip = "1864204063" + req.Idfa = "of5wO5sKWep0OFPt9rWQf6xNJVPg" + req.Logined = 1 + req.MatchType = 1 + req.GameAwemeId = "" + req.ComeBackUser = 0 + req.FanCode = "" + req.Network = "" + req.Idfv = "" + req.ScreenResolution = "" + req.System = "" + req.Electric = "" + req.ProcessorModel = "" + req.BaseBand = "" + req.Model = "" + req.Battery = "" + req.Oaid = "" + req.AdInfo = "" + req.AdDevice = "" + req.Ua = "WebSdk GaoreGame/1.3.1" + req.WxPlatform = "" + resp, err := client.GetImei(req) + if err != nil { + t.Error(err) + } + t.Log(resp) +} + +func TestSetImei(t *testing.T) { + client, err := NewClient() + if err != nil { + t.Error(err) + return + } + req := CreateSetImeiReq() + // 基础字段赋值 + req.UserName = "15179405888" + req.GameId = 6062 + req.GameSign = "hlhj" + req.ChannelId = 1 // mtype + req.MatchType = 2 + req.Imei = "96d9acdd57535c92-null" + req.Idfa = "96d9acdd57535c92-null" + req.Network = "4G" + req.Idfv = "" + req.ScreenResolution = "2132x1080" + req.System = "11" + req.ProcessorModel = "" + req.BaseBand = "" + req.Model = "PCDM10" + req.Battery = "45" + req.Oaid = "B9258E43A5084B43B72D94580C830898343a97328d6fd210b9e23859b1d5e83d_gaore_" + req.AdInfo = "" + req.AdDevice = "" + req.Ua = "Mozilla/5.0 (Linux; Android 11; PCDM10 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36; SHGR GaoreGame/2.3.5" + req.WxPlatform = "" + + resp, err := client.SetImei(req) + if err != nil { + t.Error(err) + return + } + t.Log(resp) +} diff --git a/services/msdk/user.go b/services/msdk/user.go new file mode 100644 index 0000000..60df8fe --- /dev/null +++ b/services/msdk/user.go @@ -0,0 +1,218 @@ +package msdk + +import ( + "fmt" + "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/requests" + "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/responses" + "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/utils" + "time" +) + +const msdkKey = "msdk@gaore.com#!!" + +type GetIdfaReq struct { + *requests.RpcRequest + ChannelId int `position:"Query" field:"mtype"` + GameId int `position:"Query" field:"game_id"` + GameSign string `position:"Query" field:"game_sign"` + Ip string `position:"Query" field:"ip"` + Imei string `position:"Query" field:"imei"` + Idfa string `position:"Query" field:"idfa"` + Idfv string `position:"Query" field:"idfv"` + LongId string `position:"Query" field:"long_id"` + SdkVersion string `position:"Query" field:"version"` + Os string `position:"Query" field:"os"` + GameOs int `position:"Query" field:"game_os"` + GameSubOs int `position:"Query" field:"game_sub_os"` + UserName string `position:"Query" field:"user_name"` + Ua string `position:"Query" field:"ua"` + LiveCode string `position:"Query" field:"live_code"` + AdDevice string `position:"Query" field:"ad_device"` + PkgAgentId string `position:"Query" field:"pkg_agent_id"` + PkgSiteId string `position:"Query" field:"pkg_site_id"` + Network string `position:"Query" field:"network"` + ScreenResolution string `position:"Query" field:"screen_resolution"` + System string `position:"Query" field:"system"` + Electric string `position:"Query" field:"electric"` + ProcessorModel string `position:"Query" field:"processor_model"` + BaseBand string `position:"Query" field:"baseband"` + Model string `position:"Query" field:"model"` + Battery string `position:"Query" field:"battery"` + Oaid string `position:"Query" field:"oaid"` + AdInfo string `position:"Query" field:"adinfo"` + WxPlatform string `position:"Query" field:"wx_platform"` +} + +type GetIdfaResp struct { + *responses.BaseResponse + GameId int `json:"game_id"` + AgentId int `json:"agent_id"` + SiteId int `json:"site_id"` + GameAwemeId string `json:"game_aweme_id"` + LongId string `json:"long_id"` + DeviceId string `json:"device_id"` + Exists bool `json:"exists"` + FromAd bool `json:"from_ad"` + MatchType int `json:"match_type"` + ClickId string `json:"click_id,omitempty"` // 非必要字段,使用 omitempty 忽略空值 + MatchTrace string `json:"match_trace,omitempty"` // 非必要字段 + RegTime int64 `json:"reg_time"` +} + +func CreateGetIdfaReq() *GetIdfaReq { + req := &GetIdfaReq{ + RpcRequest: &requests.RpcRequest{}, + } + req.InitWithApiInfo(HOST, VERSION, "/getIdfa.php") + req.Method = requests.GET + return req +} + +type GetUserAttrReq struct { + *requests.RpcRequest + UserName string `position:"Query" field:"user_name"` + GameSign string `position:"Query" field:"game_sign"` + Ts int64 `position:"Query" field:"ts"` + Sign string `position:"Query" field:"sign"` +} + +type GetUserAttrResp struct { + *responses.BaseResponse + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + Uid int `json:"uid"` + UserName string `json:"user_name"` + RegTime int `json:"reg_time"` // 假设注册时间是时间戳 + GameID int `json:"game_id"` + RegIP string `json:"reg_ip"` + AgentId int `json:"agent_id"` + SiteId int `json:"site_id"` + Imei string `json:"imei"` + Oaid string `json:"oaid"` + LongId string `json:"long_id"` + PromotionId string `json:"promotion_id"` + Mid3 string `json:"mid3"` + } `json:"data"` +} + +func CreateGetUserAttrReq(userName, gameSign string) *GetUserAttrReq { + req := &GetUserAttrReq{ + RpcRequest: &requests.RpcRequest{}, + } + req.UserName = userName + req.GameSign = gameSign + req.Ts = time.Now().Unix() + req.Sign = utils.Md5(fmt.Sprintf("%d%s", req.Ts, msdkKey)) + req.InitWithApiInfo(HOST, VERSION, "/getUserAttr.php") + req.Method = requests.GET + return req +} + +type GetImeiReq struct { + *requests.RpcRequest + Uid int `position:"Query" field:"uid"` + UserName string `position:"Query" field:"user_name"` + GameId int `position:"Query" field:"game_id"` + GameSign string `position:"Query" field:"game_sign"` + RegTime int64 `position:"Query" field:"reg_time"` + Imei string `position:"Query" field:"imei"` + ChannelId int `position:"Query" field:"mtype"` + AgentId int `position:"Query" field:"agent_id"` + SiteId int `position:"Query" field:"site_id"` + Ip string `position:"Query" field:"ip"` + UserIp string `position:"Query" field:"user_ip"` + Idfa string `position:"Query" field:"idfa"` + Logined int `position:"Query" field:"logined"` + MatchType int `position:"Query" field:"match_type"` + GameAwemeId string `position:"Query" field:"game_aweme_id"` + ComeBackUser int `position:"Query" field:"come_back_user"` //回流用户标识 1=>回流用户 + LpReg int `position:"Query" field:"lp_reg"` // 落地页注册用户标识 + FanCode string `position:"Query" field:"fan_code"` // 粉丝码 + Network string `position:"Query" field:"network"` + Idfv string `position:"Query" field:"idfv"` + ScreenResolution string `position:"Query" field:"screen_resolution"` + System string `position:"Query" field:"system"` + Electric string `position:"Query" field:"electric"` + ProcessorModel string `position:"Query" field:"processor_model"` + BaseBand string `position:"Query" field:"baseband"` + Model string `position:"Query" field:"model"` + Battery string `position:"Query" field:"battery"` + Oaid string `position:"Query" field:"oaid"` + AdInfo string `position:"Query" field:"adinfo"` + AdDevice string `position:"Query" field:"ad_device"` + Ua string `position:"Query" field:"ua"` + WxPlatform string `position:"Query" field:"wx_platform"` +} + +type GetImeiResp struct { + *responses.BaseResponse + Uid string `json:"uid"` + UserName string `json:"user_name"` + Openid string `json:"openid"` + ChannelId string `json:"mtype"` + Logined int `json:"logined"` + GameId string `json:"game_id"` + GameSign string `json:"game_sign"` + MatchType int `json:"match_type"` + RegTime int64 `json:"reg_time"` // 原始时间戳字符串 + Imei string `json:"imei"` + Oaid string `json:"oaid"` + Idfa string `json:"idfa"` + Ip int64 `json:"ip"` + UserIp string `json:"user_ip"` + Ua string `json:"ua"` + Media string `json:"media"` + AgentId int `json:"agent_id"` + SiteId int `json:"site_id"` + AdInfo string `json:"adinfo"` + GameAwemeId string `json:"game_aweme_id"` +} + +func CreateGetImeiReq() *GetImeiReq { + req := &GetImeiReq{ + RpcRequest: &requests.RpcRequest{}, + } + req.InitWithApiInfo(HOST, VERSION, "/getImei.php") + req.Method = requests.GET + return req +} + +type SetImeiReq struct { + *requests.RpcRequest + UserName string `position:"Query" field:"user_name"` + GameId int `position:"Query" field:"game_id"` + Imei string `position:"Query" field:"imei"` + Idfa string `position:"Query" field:"idfa"` + GameSign string `position:"Query" field:"game_sign"` + ChannelId int `position:"Query" field:"mtype"` + MatchType int `position:"Query" field:"match_type"` + Network string `position:"Query" field:"network"` + Idfv string `position:"Query" field:"idfv"` + ScreenResolution string `position:"Query" field:"screen_resolution"` + System string `position:"Query" field:"system"` // 可能为系统版本号字符串 + ProcessorModel string `position:"Query" field:"processor_model"` + BaseBand string `position:"Query" field:"baseband"` + Model string `position:"Query" field:"model"` + Battery string `position:"Query" field:"battery"` + Oaid string `position:"Query" field:"oaid"` + AdInfo string `position:"Query" field:"adinfo"` + AdDevice string `position:"Query" field:"ad_device"` + Ua string `position:"Query" field:"ua"` + WxPlatform string `position:"Query" field:"wx_platform"` +} + +type SetImeiResp struct { + *responses.BaseResponse + Code int `json:"code"` + Msg string `json:"msg"` +} + +func CreateSetImeiReq() *SetImeiReq { + req := &SetImeiReq{ + RpcRequest: &requests.RpcRequest{}, + } + req.InitWithApiInfo(HOST, VERSION, "/setImei.php") + req.Method = requests.GET + return req +}