Files
baseapi/platformapi/weimobapi/weimobapi.go
邹宗楠 2ed93fe209 1
2022-10-22 22:45:36 +08:00

255 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package weimobapi
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"sync"
"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"
mediaURL = "https://dopen.weimob.com/media/1_0/ec"
authURL = "https://dopen.weimob.com/fuwu/b"
accessTokenAPI = "oauth2/token"
uploadImgAPI = "goodsImage/uploadImg"
)
const (
// sku
KeySkuID = "skuId"
KeyOuterSkuCode = "outerSkuCode"
KeyImageURL = "imageUrl"
KeySalePrice = "salePrice"
KeyOriginalPrice = "originalPrice"
KeyCostPrice = "costPrice"
KeyEditStockNum = "editStockNum"
KeyAvailableStockNum = "availableStockNum"
KeySkuList = "skuList"
KeyB2cSku = "b2cSku"
KeySkuAttrMap = "skuAttrMap"
KeyField = "field"
KeySort = "sort"
KeyGoodsClassifyId = "goodsClassifyId"
KeySearch = "search"
KeyGoodsStatus = "goodsStatus"
KeyUpdateStartTime = "updateStartTime"
KeyUpdateEndTime = "updateEndTime"
)
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 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 {
if apiStr == uploadImgAPI {
fullURL := utils.GenerateGetURL(mediaURL, apiStr, utils.Params2Map("accesstoken", a.MustGetToken().AccessToken))
var b bytes.Buffer
w := multipart.NewWriter(&b)
imgData := apiParams["file"].([]byte)
imgName, _ := apiParams["name"].(string)
if fw, err := w.CreateFormFile("file", imgName); err != nil {
panic(err.Error())
} else {
fw.Write(imgData)
}
w.Close()
// b.WriteString(utils.Map2URLValues(params).Encode())
request, _ = http.NewRequest(http.MethodPost, fullURL, &b)
request.Header.Set("Content-Type", w.FormDataContentType())
} 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(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
if jsonResult1 == nil {
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
}
// 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"]
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 {
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.(map[string]interface{}))
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.(map[string]interface{}))
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"]),
}
}