Files
baseapi/platform/jdapi/jdapi.go
gazebo a3307c1ba6 - fixed some wrong string conversion(should be Int64toStr).
- mtps CreateOrderByShop added.
2018-06-19 13:59:43 +08:00

350 lines
9.1 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 jdapi
import (
"crypto/md5"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"time"
"git.rosy.net.cn/baseapi/platform/common"
"git.rosy.net.cn/baseapi/utils"
"go.uber.org/zap"
)
const (
clientTimeout = time.Second * 10
sleepSecondWhenLimited = 6 * time.Second
maxRetryCountWhenNetworkException = 3
maxRetryCountWhenReachLimited = 10
)
const (
JD_PARAM_JSON = "jd_param_json"
)
const (
// JDErrorCodeSuccess 操作成功
JDErrorCodeSuccess = "0"
// JDerrorCodeAccessFailed 操作失败请重试如还不能正常提供服务可以提交工单https://opengd.jd.com详细说明一下开放平台跟进回复。
JDerrorCodeAccessFailed = "-1"
// JDerrorCodeFailedCanAutoRetry 操作失败系统会自动重试如还不能正常提供服务可以提交工单https://opengd.jd.com详细说明一下开放平台跟进回复。
JDerrorCodeFailedCanAutoRetry = "-10000"
JDerrorCodeFailedAccessDB = "10000"
// JDerrorCodeMissingMandatoryParam 请求参数中缺失必填项信息请检查如不清楚请访问到家开放平台https://opendj.jd.comAPI分组有接口详细说明。
JDerrorCodeMissingMandatoryParam = "10005"
JDerrorCodeInvalidToken = "10013"
JDerrorCodeInvalidSign = "10014"
JDerrorCodeAbnormalParam = "10015"
JDerrorCodeMethodNotExist = "10018"
JDErrorCodeExceedLimit = "10032"
JDErrorCodeTimeout = "100022"
JDErrorCodeTomcateFailed = "100023"
JDErrorCodeLoadUnexpected = "100024"
)
const (
jdAPIURL = "https://openo2o.jd.com/djapi/%s"
AllPage = 0
DefaultPageSize = 50
)
type JDAPI struct {
token string
appKey string
appSecret string
sugarLogger *zap.SugaredLogger
client *http.Client
}
var (
ErrInnerCodeIsNotOk = errors.New("JD result inner code is not ok")
exceedLimitCodes = map[string]int{
JDErrorCodeExceedLimit: 1,
}
// todo replace all magic number
canRetryCodes = map[string]int{
JDerrorCodeFailedCanAutoRetry: 1,
JDerrorCodeAccessFailed: 1,
JDerrorCodeFailedAccessDB: 1,
JDErrorCodeTimeout: 1,
JDErrorCodeTomcateFailed: 1,
JDErrorCodeLoadUnexpected: 1,
}
jdResultInnerCodeKeys = []string{"code", "retCode", "errorCode"}
jdResultInnerCodeOK = map[string]int{
"None": 1,
"0": 1,
"1": 1,
"200": 1,
"190005": 1, // 部分失败
}
jdResultNoPageInnerDataKeys = []string{"result", "data"}
jdResultPageInner2DataKeys = []string{"result", "resultList"}
jdResultPageTotalCountKeys = []string{"totalCount", "count"}
)
type PageResultParser func(map[string]interface{}, int) ([]interface{}, int)
func signParams(jdParams map[string]string) string {
var keys []string
for k := range jdParams {
if k != "app_secret" {
keys = append(keys, k)
}
}
sort.Strings(keys)
allStr := ""
for _, k := range keys {
allStr += k + jdParams[k]
}
allStr = jdParams["app_secret"] + allStr + jdParams["app_secret"]
return fmt.Sprintf("%X", md5.Sum([]byte(allStr)))
}
func genGetURL(baseURL, apiStr string, params map[string]string) string {
fullURL := ""
if params != nil {
for k, v := range params {
if fullURL == "" {
fullURL = "?"
} else {
fullURL += "&"
}
fullURL += k + "=" + url.QueryEscape(v)
}
}
return fmt.Sprintf(baseURL, apiStr) + fullURL
}
func NewJDAPI(token, appKey, appSecret string, sugarLogger *zap.SugaredLogger) *JDAPI {
return &JDAPI{token, appKey, appSecret, sugarLogger, &http.Client{Timeout: clientTimeout}}
}
func (j *JDAPI) AccessJDQuery(apiStr string, jdParams map[string]string) (retVal map[string]interface{}, err error) {
params := make(map[string]string)
params["v"] = "1.0"
params["format"] = "json"
params["app_key"] = j.appKey
params["app_secret"] = j.appSecret
params["token"] = j.token
if jdParams == nil {
jdParams = make(map[string]string, 0)
}
jdParamStr, err := json.Marshal(jdParams)
if err != nil {
j.sugarLogger.Errorf("Error when marshal %v, error:%v", jdParams, err)
return nil, err
}
params["jd_param_json"] = jdParamStr
params["timestamp"] = utils.GetCurTimeStr()
sign := signParams(params)
params["sign"] = sign
url, _ := url.Parse(genGetURL(jdAPIURL, apiStr, params))
apiAccess := &common.AccessPlatformAPIWithRetryParams{
MaxExceedLimitRetryCount: maxRetryCountWhenReachLimited,
MaxRecoverableRetryCount: maxRetryCountWhenNetworkException,
SleepSecondWhenExceedLimit: sleepSecondWhenLimited,
Client: j.client,
Request: &http.Request{
Method: "GET",
URL: url,
},
SugarLogger: j.sugarLogger,
}
err = common.AccessPlatformAPIWithRetry(apiAccess, func(response *http.Response) (errLevel int, err error) {
jsonResult1, err := utils.HttpResponse2Json(response)
if err != nil {
j.sugarLogger.Warnf("HttpResponse2Json return:%v", err)
return 0, err
}
code := jsonResult1["code"].(string)
if code == "0" {
retVal = jsonResult1
return common.PAErrorLevelSuccess, nil
}
j.sugarLogger.Debugf("jd code is:%s", code)
if _, ok := exceedLimitCodes[code]; ok {
return common.PAErrorLevelExceedLimit, nil
} else if _, ok := canRetryCodes[code]; ok {
return common.PAErrorLevelRecoverable, nil
} else {
return common.PAErrorLevelFailed, nil
}
})
return retVal, err
}
func (j *JDAPI) AccessJDQueryNoPage(apiStr string, jdParams map[string]string, keyToRemove, keyToKeep []string) (interface{}, error) {
jsonResult, err := j.AccessJDQuery(apiStr, jdParams)
if err != nil {
return jsonResult, err
}
var data map[string]interface{}
err = utils.UnmarshalUseNumber([]byte(jsonResult["data"].(string)), &data)
if err != nil {
return jsonResult, err
}
innerCode := ""
for _, innerCodeKey := range jdResultInnerCodeKeys {
if innerCode2, ok := data[innerCodeKey]; ok {
innerCode = forceInnerCode2Str(innerCode2)
break
}
}
if _, ok := jdResultInnerCodeOK[innerCode]; ok {
for _, innerDataKey := range jdResultNoPageInnerDataKeys {
if innerData, ok := data[innerDataKey]; ok {
return utils.DictKeysMan(innerData, keyToRemove, keyToKeep), nil
}
}
panic("Can not find inner data")
} else {
return jsonResult, ErrInnerCodeIsNotOk
}
}
func NormalJDQueryHavePageResultParser(data map[string]interface{}, totalCount int) ([]interface{}, int) {
var result map[string]interface{}
var retVal []interface{}
tempResult := data["result"]
if resultStr, ok := tempResult.(string); ok {
if err := utils.UnmarshalUseNumber([]byte(resultStr), &tempResult); err != nil {
panic("Wrong format")
}
}
result = tempResult.(map[string]interface{})
if totalCount == 0 {
for _, totalCountKey := range jdResultPageTotalCountKeys {
if totalCount2, ok := result[totalCountKey]; ok {
totalCountInt64, _ := totalCount2.(json.Number).Int64()
totalCount = int(totalCountInt64)
if totalCount == 0 {
return make([]interface{}, 0), 0
}
break
}
}
if totalCount == 0 {
panic("can not find totalCount key")
}
}
for _, inner2ResultKey := range jdResultPageInner2DataKeys {
if inner2Result, ok := result[inner2ResultKey]; ok {
if inner2ResultStr, ok := inner2Result.(string); ok {
err := utils.UnmarshalUseNumber([]byte(inner2ResultStr), &retVal)
if err != nil {
panic("can not unmarshal inner2result")
}
} else {
retVal = inner2Result.([]interface{})
}
return retVal, totalCount
}
}
panic("wrong format")
}
func (j *JDAPI) AccessJDQueryHavePage(apiStr string, jdParams map[string]string, keyToRemove, keyToKeep []string, pageResultParser PageResultParser) ([]interface{}, error) {
if pageResultParser == nil {
pageResultParser = NormalJDQueryHavePageResultParser
}
localJdParams := make(map[string]string)
if jdParams != nil && len(jdParams) > 0 {
for k, v := range jdParams {
localJdParams[k] = v
}
}
totalCount := 0
pageNo := AllPage
pageSize := DefaultPageSize
if tempPageNo, ok := localJdParams["pageNo"]; ok {
pageNo, _ = strconv.Atoi(tempPageNo)
}
if tempPageSize, ok := localJdParams["pageSize"]; ok {
pageSize, _ = strconv.Atoi(tempPageSize)
}
curPage := pageNo
if curPage == AllPage {
curPage = 1
}
retVal := make([]interface{}, 0)
for {
localJdParams["pageNo"] = strconv.FormatInt(int64(curPage), 10)
localJdParams["pageSize"] = strconv.FormatInt(int64(pageSize), 10)
jsonResult, err := j.AccessJDQuery(apiStr, localJdParams)
if err != nil {
return nil, err
}
var data map[string]interface{}
err = utils.UnmarshalUseNumber([]byte(jsonResult["data"].(string)), &data)
if err != nil {
return nil, err
}
innerCode := forceInnerCode2Str(data["code"])
if innerCode != "0" && innerCode != "200" {
return nil, ErrInnerCodeIsNotOk
}
inResult, totalCount2 := pageResultParser(data, totalCount)
totalCount = totalCount2
inResult = utils.DictKeysMan(inResult, keyToRemove, keyToKeep).([]interface{})
retVal = append(retVal, inResult...)
if curPage == pageNo || curPage*pageSize >= totalCount {
break
}
curPage++
}
return retVal, nil
}
func forceInnerCode2Str(innerCode interface{}) string {
if innerCodeStr, ok := innerCode.(string); ok {
return innerCodeStr
}
return utils.Int64ToStr(utils.MustInterface2Int64(innerCode))
}