diff --git a/platformapi/weimobapi/goods.go b/platformapi/weimobapi/goods.go new file mode 100644 index 00000000..4921ff7f --- /dev/null +++ b/platformapi/weimobapi/goods.go @@ -0,0 +1,20 @@ +package weimobapi + +import ( + "git.rosy.net.cn/baseapi/utils" +) + +func (a *API) QueryGoodsList(pageNum, pageSize int) (retVal []map[string]interface{}, totalCount int, err error) { + result, err := a.AccessAPI("goods/queryGoodsList", map[string]interface{}{ + "pageNum": pageNum, + "pageSize": pageSize, + }) + if err == nil { + if pageList, ok := result["pageList"].([]interface{}); ok { + retVal = utils.Slice2MapSlice(pageList) + } + totalCount = int(utils.MustInterface2Int64(result["totalCount"])) + return retVal, totalCount, nil + } + return nil, 0, err +} diff --git a/platformapi/weimobapi/goods_test.go b/platformapi/weimobapi/goods_test.go new file mode 100644 index 00000000..6952385e --- /dev/null +++ b/platformapi/weimobapi/goods_test.go @@ -0,0 +1,17 @@ +package weimobapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/utils" +) + +func TestQueryGoodsList(t *testing.T) { + result, totalCount, err := api.QueryGoodsList(1, 20) + if err != nil { + t.Fatal(err) + } + baseapi.SugarLogger.Debug(totalCount) + baseapi.SugarLogger.Debug(utils.Format4Output(result, false)) +} diff --git a/platformapi/weimobapi/weimobapi.go b/platformapi/weimobapi/weimobapi.go new file mode 100644 index 00000000..ae4cfe8c --- /dev/null +++ b/platformapi/weimobapi/weimobapi.go @@ -0,0 +1,209 @@ +package weimobapi + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "sync" + + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/platformapi" + "git.rosy.net.cn/baseapi/utils" +) + +const ( + ResponseCodeSuccess = "0" + ResponseCodeExceedCallFrequency = "8000103" +) + +const ( + prodURL = "https://dopen.weimob.com/api/1_0/ec" + authURL = "https://dopen.weimob.com/fuwu/b" + accessTokenAPI = "oauth2/token" +) + +type TokenInfo struct { + TokenType string `json:"token_type"` + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + RefreshToken string `json:"refresh_token"` + RefreshTokenExpiresIn int `json:"refresh_token_expires_in"` + Scope string `json:"scope"` + PublicAccountID string `json:"public_account_id"` + BusinessID string `json:"business_id"` +} + +type API struct { + token *TokenInfo + appID string + appSecret string + client *http.Client + config *platformapi.APIConfig + + locker sync.RWMutex +} + +var ( + exceedLimitCodes = map[string]int{ + ResponseCodeExceedCallFrequency: 1, + } + + canRetryCodes = map[string]int{} +) + +func New(token *TokenInfo, appID, appSecret string, config ...*platformapi.APIConfig) *API { + curConfig := platformapi.DefAPIConfig + if len(config) > 0 { + curConfig = *config[0] + } + return &API{ + token: token, + appID: appID, + appSecret: appSecret, + client: &http.Client{Timeout: curConfig.ClientTimeout}, + config: &curConfig, + } +} + +func (a *API) SetToken(newToken *TokenInfo) (retVal bool) { + a.locker.Lock() + defer a.locker.Unlock() + a.token = newToken + retVal = true + return retVal +} + +func (a *API) GetToken() *TokenInfo { + a.locker.RLock() + defer a.locker.RUnlock() + return a.token +} + +func (a *API) MustGetToken() *TokenInfo { + token := a.GetToken() + if token == nil { + panic("token is nil") + } + return token +} + +func (a *API) AccessAPI(apiStr string, apiParams map[string]interface{}) (retVal map[string]interface{}, err error) { + err = platformapi.AccessPlatformAPIWithRetry(a.client, + func() *http.Request { + var request *http.Request + if apiStr == accessTokenAPI { + fullURL := utils.GenerateGetURL(authURL, apiStr, utils.MergeMaps(apiParams, map[string]interface{}{ + "client_id": a.appID, + "client_secret": a.appSecret, + })) + request, _ = http.NewRequest(http.MethodPost, fullURL, nil) + } else { + fullURL := utils.GenerateGetURL(prodURL, apiStr, utils.Params2Map("accesstoken", a.MustGetToken().AccessToken)) + var body io.Reader + if apiParams != nil { + apiParamsBytes, err := json.Marshal(apiParams) + if err != nil { + panic(fmt.Sprintf("Error when marshal %v, error:%v", apiParams, err)) + } + body = bytes.NewReader(apiParamsBytes) + } + request, _ = http.NewRequest(http.MethodPost, fullURL, body) + request.Header.Set("Content-Type", "application/json; charset=utf-8") + request.Header.Set("Content-Encoding", "gzip, deflate") + request.Header.Set("User-Agent", "weimob-golang-api") + // request.Close = true //todo 为了性能考虑还是不要关闭 + } + return request + }, + a.config, + func(jsonResult1 map[string]interface{}) (errLevel string, err error) { + // baseapi.SugarLogger.Debug(utils.Format4Output(jsonResult1, false)) + code := ResponseCodeSuccess + errMsg := "" + if apiStr == accessTokenAPI { + // token相关出错到不了这里,因为当取token发生错误时,HTTP CODE不是200 + if errCode, ok := jsonResult1["error"]; ok { + code = getStringCode(errCode) + errMsg = jsonResult1["error_description"].(string) + } + if code == ResponseCodeSuccess { + retVal = jsonResult1 + return platformapi.ErrLevelSuccess, nil + } + } else { + if errMap, ok := jsonResult1["code"].(map[string]interface{}); ok { + code = getStringCode(errMap["errcode"]) + errMsg = errMap["errmsg"].(string) + } + if code == ResponseCodeSuccess { + retVal = jsonResult1["data"].(map[string]interface{}) + return platformapi.ErrLevelSuccess, nil + } + } + newErr := utils.NewErrorCode(errMsg, code) + if _, ok := exceedLimitCodes[code]; ok { + return platformapi.ErrLevelExceedLimit, newErr + } else if _, ok := canRetryCodes[code]; ok { + return platformapi.ErrLevelRecoverableErr, newErr + } else { + baseapi.SugarLogger.Debugf("weimob AccessAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true)) + return platformapi.ErrLevelCodeIsNotOK, newErr + } + }) + return retVal, err +} + +func getStringCode(code interface{}) string { + if codeStr, ok := code.(string); ok { + return codeStr + } + return utils.Int64ToStr(utils.Interface2Int64WithDefault(code, 0)) +} + +// redirectURL只是作为校验,要求转入得到此CODE时给的redirectURL,不包括参数部分 +func (a *API) RefreshTokenByCode(code, redirectURL string) (retVal *TokenInfo, err error) { + result, err := a.AccessAPI(accessTokenAPI, map[string]interface{}{ + "grant_type": "authorization_code", + "code": code, + "redirect_uri": redirectURL, + }) + if err == nil { + retVal = map2TokenInfo(result) + a.SetToken(retVal) + return retVal, nil + } + return nil, err +} + +func (a *API) RefreshTokenByRefreshToken() (retVal *TokenInfo, err error) { + curToken := a.GetToken() + if curToken != nil { + result, err := a.AccessAPI(accessTokenAPI, map[string]interface{}{ + "grant_type": "refresh_token", + "refresh_token": curToken.RefreshToken, + }) + if err == nil { + retVal = map2TokenInfo(result) + a.SetToken(retVal) + return retVal, nil + + } + return nil, err + } + return nil, fmt.Errorf("刷新TOKEN要求已经有TOKEN") +} + +func map2TokenInfo(mapData map[string]interface{}) *TokenInfo { + return &TokenInfo{ + TokenType: utils.Interface2String(mapData["token_type"]), + AccessToken: utils.Interface2String(mapData["access_token"]), + ExpiresIn: int(utils.MustInterface2Int64(mapData["expires_in"])), + RefreshToken: utils.Interface2String(mapData["refresh_token"]), + RefreshTokenExpiresIn: int(utils.MustInterface2Int64(mapData["refresh_token_expires_in"])), + Scope: utils.Interface2String(mapData["scope"]), + PublicAccountID: utils.Interface2String(mapData["public_account_id"]), + BusinessID: utils.Interface2String(mapData["business_id"]), + } +} diff --git a/platformapi/weimobapi/weimobapi_test.go b/platformapi/weimobapi/weimobapi_test.go new file mode 100644 index 00000000..8b667986 --- /dev/null +++ b/platformapi/weimobapi/weimobapi_test.go @@ -0,0 +1,54 @@ +package weimobapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/utils" + "go.uber.org/zap" +) + +var ( + api *API + sugarLogger *zap.SugaredLogger +) + +func init() { + logger, _ := zap.NewDevelopment() + sugarLogger = logger.Sugar() + baseapi.Init(sugarLogger) + + tokenStr := ` + { + "token_type": "bearer", + "access_token": "db2f4a02-2097-4636-8c8a-cc5576f402cf", + "expires_in": 7199, + "refresh_token": "7b961b6b-0dc4-43e1-8c38-a9a10e2e130d", + "refresh_token_expires_in": 604799, + "scope": "default", + "public_account_id": "100000386048", + "business_id": "1224609670" + }` + var token *TokenInfo + if err := utils.UnmarshalUseNumber([]byte(tokenStr), &token); err != nil { + panic(err) + } + // prod + api = New(token, "319F5E7FB6784DFCA3684C9333EB7744", "7267AA7F58261F6965599218F5A1D592") +} + +func TestRefreshTokenByCode(t *testing.T) { + result, err := api.RefreshTokenByCode("6Stxtw", "http://callback.test.jxc4.com/weimob/coded") + if err != nil { + t.Fatal(err) + } + baseapi.SugarLogger.Debug(utils.Format4Output(result, false)) +} + +func TestRefreshTokenByRefreshToken(t *testing.T) { + result, err := api.RefreshTokenByRefreshToken() + if err != nil { + t.Fatal(err) + } + baseapi.SugarLogger.Debug(utils.Format4Output(result, false)) +}