From 52227d93cc80d201a51b171fc30b17b63a6193ff Mon Sep 17 00:00:00 2001 From: Syd Xu Date: Wed, 24 Apr 2024 14:34:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(oauth):=20=E5=A2=9E=E5=8A=A0oauth2?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E7=9B=B8=E5=85=B3API,=20=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=97=A7=E7=9A=84token=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++ api/oauth/accessToken.go | 17 +++++++ api/oauth/doc.go | 2 + api/oauth/getUserInfo.go | 15 ++++++ api/oauth/refreshToken.go | 17 +++++++ core/client.go | 74 ++++++++++++++--------------- model/account/getUserListByMccid.go | 1 + model/const.go | 7 +++ model/oauth/accessToken.go | 45 ++++++++++++++++++ model/oauth/doc.go | 2 + model/oauth/getUserInfo.go | 50 +++++++++++++++++++ model/oauth/refreshToken.go | 23 +++++++++ model/oauth/url.go | 29 +++++++++++ model/request.go | 23 +++++---- model/response.go | 26 +++++++++- 15 files changed, 284 insertions(+), 51 deletions(-) create mode 100644 api/oauth/accessToken.go create mode 100644 api/oauth/doc.go create mode 100644 api/oauth/getUserInfo.go create mode 100644 api/oauth/refreshToken.go create mode 100644 model/oauth/accessToken.go create mode 100644 model/oauth/doc.go create mode 100644 model/oauth/getUserInfo.go create mode 100644 model/oauth/refreshToken.go create mode 100644 model/oauth/url.go diff --git a/README.md b/README.md index dec192c..fca6f3d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ [![GitHub license](https://img.shields.io/github/license/bububa/baidu-marketing.svg)](https://github.com/bububa/baidu-marketing/blob/master/LICENSE) [![GitHub release](https://img.shields.io/github/release/bububa/baidu-marketing.svg)](https://GitHub.com/bububa/baidu-marketing/releases/) +- OAuth授权 (api/oauth) + - 换取授权令牌接口 [ AccessToken(clt *core.SDKClient, req *oauth.AccessTokenRequest) (*oauth.AccessToken, error) ] + - 更新授权令牌接口 [ RefreshToken(clt *core.SDKClient, req *oauth.RefreshTokenRequest) (*oauth.AccessToken, error) ] + - 查询授权用户信息 [ GetUserInfo(clt *core.SDKClient, req *oauth.GetUserInfoRequest) (*oauth.UserInfo, error) ] - 账户管理 - 财务管理 (api/account/balance) - 查询账户余额成分 [ GetBalanceInfo(clt *core.SDKClient, auth model.RequestHeader, productIds[]uint64) ([]balance.BalanceInfo, error) ] diff --git a/api/oauth/accessToken.go b/api/oauth/accessToken.go new file mode 100644 index 0000000..e027858 --- /dev/null +++ b/api/oauth/accessToken.go @@ -0,0 +1,17 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/core" + "github.com/bububa/baidu-marketing/model/oauth" +) + +// AccessToken 换取授权令牌接口 +func AccessToken(clt *core.SDKClient, req *oauth.AccessTokenRequest) (*oauth.AccessToken, error) { + var resp oauth.AccessToken + req.AppID = clt.AppID() + req.SecretKey = clt.Secret() + if err := clt.OAuth(req, &resp); err != nil { + return nil, err + } + return &resp, nil +} diff --git a/api/oauth/doc.go b/api/oauth/doc.go new file mode 100644 index 0000000..1536392 --- /dev/null +++ b/api/oauth/doc.go @@ -0,0 +1,2 @@ +// Package oauth OAuth授权相关 +package oauth diff --git a/api/oauth/getUserInfo.go b/api/oauth/getUserInfo.go new file mode 100644 index 0000000..d340b3a --- /dev/null +++ b/api/oauth/getUserInfo.go @@ -0,0 +1,15 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/core" + "github.com/bububa/baidu-marketing/model/oauth" +) + +// GetUserInfo 查询授权用户信息 +func GetUserInfo(clt *core.SDKClient, req *oauth.GetUserInfoRequest) (*oauth.UserInfo, error) { + var resp oauth.UserInfo + if err := clt.OAuth(req, &resp); err != nil { + return nil, err + } + return &resp, nil +} diff --git a/api/oauth/refreshToken.go b/api/oauth/refreshToken.go new file mode 100644 index 0000000..7333435 --- /dev/null +++ b/api/oauth/refreshToken.go @@ -0,0 +1,17 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/core" + "github.com/bububa/baidu-marketing/model/oauth" +) + +// RefreshToken 更新授权令牌接口 +func RefreshToken(clt *core.SDKClient, req *oauth.RefreshTokenRequest) (*oauth.AccessToken, error) { + var resp oauth.AccessToken + req.AppID = clt.AppID() + req.SecretKey = clt.Secret() + if err := clt.OAuth(req, &resp); err != nil { + return nil, err + } + return &resp, nil +} diff --git a/core/client.go b/core/client.go index 5e66365..3c70326 100644 --- a/core/client.go +++ b/core/client.go @@ -34,18 +34,14 @@ func defaultHttpClient() *http.Client { // SDKClient object type SDKClient struct { httpClient *http.Client - token string - ocpcToken string - username string - password string + appID string + secret string debug bool } // NewSDKClient init sdk client -func NewSDKClient(token string, ocpcToken string) *SDKClient { +func NewSDKClient(appID string, secret string) *SDKClient { return &SDKClient{ - token: token, - ocpcToken: ocpcToken, httpClient: defaultHttpClient(), } } @@ -54,40 +50,21 @@ func (c *SDKClient) SetHttpClient(httpClient *http.Client) { c.httpClient = httpClient } -// Token get token -func (c SDKClient) Token() string { - return c.token -} - -// OcpcToken get ocpc token -func (c SDKClient) OcpcToken() string { - return c.ocpcToken +// SetDebug set debug mode +func (c *SDKClient) SetDebug(debug bool) { + c.debug = debug } -// SetUser set username password -func (c *SDKClient) SetUser(username string, password string) { - c.username = username - c.password = password +func (c *SDKClient) AppID() string { + return c.appID } -// SetDebug set debug mode -func (c *SDKClient) SetDebug(debug bool) { - c.debug = debug +func (c *SDKClient) Secret() string { + return c.secret } // Do execute api request func (c *SDKClient) Do(req *model.Request, resp interface{}) (*model.ResponseHeader, error) { - if req.Header.Token == "" { - req.Header.Token = c.token - } - if req.Header.AccessToken == "" { - if req.Header.Username == "" { - req.Header.Username = c.username - } - if req.Header.Password == "" { - req.Header.Password = c.password - } - } buf := util.GetBufferPool() defer util.PutBufferPool(buf) if err := json.NewEncoder(buf).Encode(req); err != nil { @@ -107,7 +84,7 @@ func (c *SDKClient) Do(req *model.Request, resp interface{}) (*model.ResponseHea } return &reqResp.Header, reqResp } - if resp != nil { + if resp != nil && reqResp.Body != nil { err = json.Unmarshal(reqResp.Body, resp) if err != nil { return &reqResp.Header, err @@ -117,9 +94,6 @@ func (c *SDKClient) Do(req *model.Request, resp interface{}) (*model.ResponseHea } func (c *SDKClient) Conversion(req model.ConversionRequest, resp interface{}) (*model.ResponseHeader, error) { - if req.OcpcToken() == "" { - req.SetOcpcToken(c.ocpcToken) - } buf := util.GetBufferPool() defer util.PutBufferPool(buf) if err := json.NewEncoder(buf).Encode(req); err != nil { @@ -139,7 +113,7 @@ func (c *SDKClient) Conversion(req model.ConversionRequest, resp interface{}) (* } return &reqResp.Header, reqResp } - if resp != nil { + if resp != nil && reqResp.Body != nil { err = json.Unmarshal(reqResp.Body, resp) if err != nil { return &reqResp.Header, err @@ -164,6 +138,30 @@ func (c *SDKClient) ActionCb(req model.ActionCbRequest) error { return nil } +// OAuth execute oauth api request +func (c *SDKClient) OAuth(req model.RequestBody, resp interface{}) error { + buf := util.GetBufferPool() + defer util.PutBufferPool(buf) + if err := json.NewEncoder(buf).Encode(req); err != nil { + return err + } + var reqResp model.DataResponse + err := c.Post(req.Url(), buf.Bytes(), &reqResp) + if err != nil { + return err + } + if reqResp.IsError() { + return reqResp + } + if resp != nil && reqResp.Data != nil { + err = json.Unmarshal(reqResp.Data, resp) + if err != nil { + return err + } + } + return nil +} + // Post data through api func (c *SDKClient) Post(reqUrl string, bs []byte, resp interface{}) error { debug.PrintPostJSONRequest(reqUrl, bs, c.debug) diff --git a/model/account/getUserListByMccid.go b/model/account/getUserListByMccid.go index 82e8646..c7a7ea0 100644 --- a/model/account/getUserListByMccid.go +++ b/model/account/getUserListByMccid.go @@ -8,6 +8,7 @@ import ( // GetUserListByMccidRequest 账户管家子账号 API Request type GetUserListByMccidRequest struct{} +// Url implement RequestBody interface func (r GetUserListByMccidRequest) Url() string { return util.StringsJoin(model.BASE_URL_FEED, "MccFeedService/getUserListByMccid") } diff --git a/model/const.go b/model/const.go index 392920b..8256a56 100644 --- a/model/const.go +++ b/model/const.go @@ -1,6 +1,8 @@ package model const ( + // BASE_OAUTH_URL oauth api base url + BASE_OAUTH_URL = "https://u.baidu.com/oauth/" // BASE_URL_SMS sms/service api base url BASE_URL_SMS = "https://api.baidu.com/json/sms/service/" // api base url // BASE_URL_FEED feed/v2 api base url @@ -10,3 +12,8 @@ const ( // ACTIONCB_URL ACTIONCB_URL = "https://als.baidu.com/cb/actionCb" ) + +const ( + // PlatformID + PlatformID = "4960345965958561794" +) diff --git a/model/oauth/accessToken.go b/model/oauth/accessToken.go new file mode 100644 index 0000000..122f143 --- /dev/null +++ b/model/oauth/accessToken.go @@ -0,0 +1,45 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/model" + "github.com/bububa/baidu-marketing/util" +) + +// AccessToken 授权令牌 +type AccessToken struct { + // AccessToken 授权令牌 + AccessToken string `json:"accessToken,omitempty"` + // RefreshToken 刷新令牌 + RefreshToken string `json:"refreshToken,omitempty"` + // OpenID 获取授权用户信息标识 + OpenID string `json:"openId,omitempty"` + // ExpiresTime 授权令牌到期时间 + ExpiresTime string `json:"expiresTime,omitempty"` + // RefreshExpiresTime 更新令牌到期时间 + RefreshExpiresTime string `json:"refreshExpiresTime,omitempty"` + // ExpiresIn 授权令牌剩余有效时间 + ExpiresIn int64 `json:"expiresIn,omitempty"` + // RefreshExpiresIn 更新令牌剩余有效时间 + RefreshExpiresIn int64 `json:"refreshExpiresIn,omitempty"` + // UserID 授权账号 ucid + UserID uint64 `json:"userId,omitempty"` +} + +// AccessTokenRequest 换取授权令牌接口 +type AccessTokenRequest struct { + // AppID 应用 appid + AppID string `json:"appId,omitempty"` + // AuthCode 临时授权码(数据来源:通过回调接口获取) + AuthCode string `json:"authCode,omitempty"` + // SecretKey 应用持有的 secretKey + SecretKey string `json:"secretKey,omitempty"` + // GrantType 授权令牌获取模式,仅限:auth_code(授权码模式) + GrantType string `json:"grantType,omitempty"` + // UserID 同意授权的推广账户ID(数据来源:通过回调接口获取) + UserID uint64 `json:"userId,omitempty"` +} + +// Url implement RequestBody interface +func (r AccessTokenRequest) Url() string { + return util.StringsJoin(model.BASE_OAUTH_URL, "accessToken") +} diff --git a/model/oauth/doc.go b/model/oauth/doc.go new file mode 100644 index 0000000..1536392 --- /dev/null +++ b/model/oauth/doc.go @@ -0,0 +1,2 @@ +// Package oauth OAuth授权相关 +package oauth diff --git a/model/oauth/getUserInfo.go b/model/oauth/getUserInfo.go new file mode 100644 index 0000000..8cb6d10 --- /dev/null +++ b/model/oauth/getUserInfo.go @@ -0,0 +1,50 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/model" + "github.com/bububa/baidu-marketing/util" +) + +// GetUserInfoRequest 查询授权用户信息 +type GetUserInfoRequest struct { + // OpenID 授权用户查询标识 + OpenID string `json:"openId,omitempty"` + // AccessToken 已有的授权令牌 + AccessToken string `json:"accessToken,omitempty"` + // UserID 同意授权的推广账户ID + UserID uint64 `json:"userId,omitempty"` + // NeedSubList 是否需要子账号列表,值为true时返回subUserList + NeedSubList bool `json:"needSubList,omitempty"` + // PageSize 分页数量,默认100,最大不超过500 + PageSize int `json:"pageSize,omitempty"` + // LastPageMaxUcId 上一页返回的最大userid,用于子账号列表分页 + // 查询子账号列表时,该字段为必填。第一次获取子账户列表时,该字段需要设置为1 + LastPageMaxUcId uint64 `json:"lastPageMaxUcId,omitempty"` +} + +// Url implement RequestBody interface +func (r GetUserInfoRequest) Url() string { + return util.StringsJoin(model.BASE_OAUTH_URL, "getUserInfo") +} + +// UserInfo 授权用户信息 +type UserInfo struct { + // MasterUid 同意授权用户ucid + MasterUid uint64 `json:"masterUid,omitempty"` + // UserAcctType 授权账户类型 + // 1: 普通账户 + // 2:超管账户(客户中心和账户管家) + UserAcctType int `json:"userAcctType,omitempty"` + // HasNext 是否有下一页子账号列表 + HasNext bool `json:"hasNext,omitempty"` + // SubUserList 同意授权用户关联的子账号列表 + SubUserList []SubUserInfo `json:"subUserList,omitempty"` +} + +// SubUserInfo 同意授权用户关联的子账号 +type SubUserInfo struct { + // UcId 同意授权用户关联的子账号ucid + UcId uint64 `json:"ucId,omitempty"` + // UcName 同意授权用户关联的子账号ucname + UcName string `json:"ucName,omitempty"` +} diff --git a/model/oauth/refreshToken.go b/model/oauth/refreshToken.go new file mode 100644 index 0000000..2225eb2 --- /dev/null +++ b/model/oauth/refreshToken.go @@ -0,0 +1,23 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/model" + "github.com/bububa/baidu-marketing/util" +) + +// RefreshTokenRequest 更新授权令牌接口 +type RefreshTokenRequest struct { + // AppID 应用ID + AppID string `json:"appId,omitempty"` + // RefreshToken 已有的更新令牌 refreshToken + RefreshToken string `json:"refreshToken,omitempty"` + // SecretKey 应用密钥 + SecretKey string `json:"secretKey,omitempty"` + // UserID 同意授权的推广账户ID + UserID uint64 `json:"userId,omitempty"` +} + +// Url implement RequestBody interface +func (r RefreshTokenRequest) Url() string { + return util.StringsJoin(model.BASE_OAUTH_URL, "refreshToken") +} diff --git a/model/oauth/url.go b/model/oauth/url.go new file mode 100644 index 0000000..112de42 --- /dev/null +++ b/model/oauth/url.go @@ -0,0 +1,29 @@ +package oauth + +import ( + "github.com/bububa/baidu-marketing/model" + "github.com/bububa/baidu-marketing/util" +) + +// URLRequest 程序化拼接授权链接 API Request +type URLRequest struct { + // AppID 需要授权的应用 ID + AppID string `json:"appId,omitempty"` + // Scope 应用权限代码,建议从应用管理界面系统生成的授权链接中获取。 + Scope string `json:"scope,omitempty"` + // State 开发者自定义参数,长度限制 512 个字符,特殊字符需要 URLEncode + State string `json:"state,omitempty"` + // Callback 应用回调链接 + Callback string `json:"callback,omitempty"` +} + +// URL 程序化拼接授权链接 +func (r URLRequest) URL() string { + values := util.GetUrlValues() + defer util.PutUrlValues(values) + values.Set("appId", r.AppID) + values.Set("scope", r.Scope) + values.Set("state", r.State) + values.Set("callback", r.Callback) + return util.StringsJoin(model.BASE_OAUTH_URL, "page/index?", values.Encode()) +} diff --git a/model/request.go b/model/request.go index c74d94d..a14463e 100644 --- a/model/request.go +++ b/model/request.go @@ -3,19 +3,9 @@ package model // RequestHeader 请求header对象 type RequestHeader struct { // Username 推广账户名称 - Username string `json:"username,omitempty"` - // Password 推广账户密码 - Password string `json:"password,omitempty"` - // Token 您的token值 - Token string `json:"token"` - // Target 被MCC账户管辖的普通推广账户名称 - Target string `json:"target,omitempty"` + Username string `json:"userName,omitempty"` // AccessToken 百度商业服务市场服务商的access_token。注意属性名是大写的T - AccessToken string `json:"access_token,omitempty"` - - //新的用户token请求方式 - TargetUserNameV2 string `json:"userName,omitempty"` - AccessTokenV2 string `json:"accessToken,omitempty"` + AccessToken string `json:"accessToken,omitempty"` } // RequestBody 请求业务数据 @@ -31,6 +21,15 @@ type Request struct { Body RequestBody `json:"body"` } +// SetUser set username password +func (r *Request) SetUser(username string, accessToken string) { + if r.Header == nil { + r.Header = new(RequestHeader) + } + r.Header.Username = username + r.Header.AccessToken = accessToken +} + // Url 请求API 地址 func (r Request) Url() string { return r.Body.Url() diff --git a/model/response.go b/model/response.go index 938c705..e53b4c0 100644 --- a/model/response.go +++ b/model/response.go @@ -1,6 +1,11 @@ package model -import "encoding/json" +import ( + "encoding/json" + "strconv" + + "github.com/bububa/baidu-marketing/util" +) // ResponseHeader API 返回header对象 type ResponseHeader struct { @@ -48,6 +53,25 @@ func (r Response) Error() string { return string(buf) } +type DataResponse struct { + // Code 请求处理状态,0:处理成功,非0:处理失败 + Code int `json:"code,omitempty"` + // Message 请求处理话术 + Message string `json:"message,omitempty"` + // Data 请求处理结果 + Data json.RawMessage `json:"data,omitempty"` +} + +// IsError 判断结果是否错误 +func (r DataResponse) IsError() bool { + return r.Code != 0 +} + +// Error implement error interface +func (r DataResponse) Error() string { + return util.StringsJoin("code:", strconv.Itoa(r.Code), ", message:", r.Message) +} + // ActionCbResponse 转化追踪回调返回 type ActionCbResponse struct { ErrorCode int `json:"error_code,omitempty"`