Files
baseapi/platformapi/jdapi/jdapi.go
2019-07-24 16:18:11 +08:00

390 lines
12 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"
"fmt"
"net/http"
"sort"
"strings"
"sync"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
paramJson = "jd_param_json"
)
const (
// ResponseCodeSuccess 操作成功
ResponseCodeSuccess = "0"
// ResponseCodeAccessFailed 操作失败请重试如还不能正常提供服务可以提交工单https://opengd.jd.com详细说明一下开放平台跟进回复。
ResponseCodeAccessFailed = "-1"
// ResponseCodeFailedCanAutoRetry 操作失败系统会自动重试如还不能正常提供服务可以提交工单https://opengd.jd.com详细说明一下开放平台跟进回复。
ResponseCodeFailedCanAutoRetry = "-10000"
ResponseCodeFailedAccessDB = "10000"
// ResponseCodeMissingMandatoryParam 请求参数中缺失必填项信息请检查如不清楚请访问到家开放平台https://opendj.jd.comAPI分组有接口详细说明。
ResponseCodeMissingMandatoryParam = "10005"
ResponseCodeInvalidToken = "10013"
ResponseCodeInvalidSign = "10014"
ResponseCodeAbnormalParam = "10015"
ResponseCodeMethodNotExist = "10018"
ResponseCodeExceedLimit = "10032"
ResponseCodeTimeout = "100022"
ResponseCodeTomcateFailed = "100023"
ResponseCodeLoadUnexpected = "100024"
)
const (
ResponseInnerCodeOrderAlreadyPickedUp = "10132"
ResponseInnerCodeOrderCanceled = "10138"
ResponseInnerCodeOrderLocked = "10139"
ResponseInnerCodePartialFailed = "190005"
)
const (
// prodURL = "https://openo2o.jd.com/djapi"
prodURL = "https://openapi.jddj.com/djapi"
signKey = "sign"
appSecretKey = "app_secret"
AllPage = 0
DefaultPageSize = 50
)
type API struct {
platformapi.APICookie
token string
appKey string
appSecret string
client *http.Client
config *platformapi.APIConfig
locker sync.RWMutex
storeCookie string
}
var (
exceedLimitCodes = map[string]int{
ResponseCodeExceedLimit: 1,
}
canRetryCodes = map[string]int{
ResponseCodeFailedCanAutoRetry: 1,
ResponseCodeAccessFailed: 1,
ResponseCodeFailedAccessDB: 1,
ResponseCodeTimeout: 1,
ResponseCodeTomcateFailed: 1,
ResponseCodeLoadUnexpected: 1,
}
// innerCodeKeys = []string{"code", "retCode", "errorCode"}
// innerCodeOKCodes = map[string]int{
// "None": 1,
// "0": 1,
// "1": 1,
// "200": 1,
// "190005": 1, // 部分失败
// }
// noPageInnerDataKeys = []string{"result", "data"}
havePageInner2DataKeys = []string{"result", "resultList"}
havePageTotalCountKeys = []string{"totalCount", "count"}
)
var (
nullResultParser = genNoPageResultParser("code", "msg", "", "0")
watchKeys = []string{
KeyOutStationNo,
KeyStationNo,
"StoreNo",
"outSkuId",
}
)
type PageResultParser func(map[string]interface{}, int) ([]interface{}, int, error)
func (a *API) signParams(jdParams map[string]interface{}) string {
var keys []string
for k := range jdParams {
if k != signKey {
keys = append(keys, k)
}
}
sort.Strings(keys)
allStr := a.appSecret
for _, k := range keys {
allStr += k + fmt.Sprint(jdParams[k])
}
allStr = allStr + a.appSecret
return fmt.Sprintf("%X", md5.Sum([]byte(allStr)))
}
func New(token, appKey, appSecret string, config ...*platformapi.APIConfig) *API {
curConfig := platformapi.DefAPIConfig
if len(config) > 0 {
curConfig = *config[0]
}
return &API{
token: token,
appKey: appKey,
appSecret: appSecret,
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
}
}
func (a *API) AccessAPI(apiStr string, jdParams map[string]interface{}) (retVal map[string]interface{}, err error) {
params := make(map[string]interface{})
params["v"] = "1.0"
params["format"] = "json"
params["app_key"] = a.appKey
params["token"] = a.token
if jdParams == nil {
jdParams = make(map[string]interface{}, 0)
}
jdParamBytes, err := json.Marshal(jdParams)
if err != nil {
baseapi.SugarLogger.Errorf("Error when marshal %v, error:%v", jdParams, err)
return nil, err
}
jdParamStr := string(jdParamBytes)
userGet := true
if true { //len(jdParamStr) > 12 {
userGet = false
}
params["jd_param_json"] = jdParamStr
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
params["timestamp"] = utils.GetCurTimeStr()
sign := a.signParams(params)
params[signKey] = sign
var request *http.Request
if userGet {
fullURL := utils.GenerateGetURL(prodURL, apiStr, params)
// baseapi.SugarLogger.Debug(fullURL)
request, _ = http.NewRequest(http.MethodGet, fullURL, nil)
} else {
fullURL := prodURL + "/" + apiStr
// baseapi.SugarLogger.Debug(utils.Map2URLValues(params).Encode())
request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(params).Encode()))
request.Header.Set("charset", "UTF-8")
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
// 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")
}
code := jsonResult1["code"].(string)
if code == ResponseCodeSuccess {
retVal = jsonResult1
return platformapi.ErrLevelSuccess, nil
}
newErr := utils.NewErrorCode(jsonResult1["msg"].(string), code)
if _, ok := exceedLimitCodes[code]; ok {
return platformapi.ErrLevelExceedLimit, newErr
} else if _, ok := canRetryCodes[code]; ok {
return platformapi.ErrLevelRecoverableErr, newErr
} else {
baseapi.SugarLogger.Debugf("jd AccessAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
return platformapi.ErrLevelCodeIsNotOK, newErr
}
})
return retVal, platformapi.RebuildError(err, jdParams, watchKeys)
}
func genNoPageResultParser(codeKey, msgKey, resultKey, okCode string) func(data map[string]interface{}) (interface{}, error) {
return func(data map[string]interface{}) (innerData interface{}, err error) {
rawInnerCode, ok := data[codeKey]
if !ok {
panic(fmt.Sprintf("genNoPageResultParser codeKey %s can not be found in result:%v", codeKey, data))
}
innerCode := forceInnerCode2Str(rawInnerCode)
if resultKey != "" {
innerData, _ = data[resultKey]
}
if innerCode != okCode {
errMsg := formatErrorMsg(data[msgKey])
if innerCode == ResponseInnerCodePartialFailed {
errMsg += ", " + utils.Format4Output(innerData, true)
}
err = utils.NewErrorCode(errMsg, innerCode, 1)
}
return innerData, err
}
}
func formatErrorMsg(msg interface{}) (strMsg string) {
if msg != nil {
strMsg = utils.Format4Output(msg, true)
}
return strMsg
}
func (a *API) AccessAPINoPage(apiStr string, jdParams map[string]interface{}, keyToRemove, keyToKeep []string, resultParser func(data map[string]interface{}) (interface{}, error)) (interface{}, error) {
if resultParser == nil {
resultParser = genNoPageResultParser("code", "msg", "result", "0")
}
jsonResult, err := a.AccessAPI(apiStr, jdParams)
if err != nil {
return nil, err
}
var data map[string]interface{}
if err := utils.UnmarshalUseNumber([]byte(jsonResult["data"].(string)), &data); err != nil {
return nil, platformapi.ErrResponseDataFormatWrong
}
result, err := resultParser(data)
if err == nil {
return utils.DictKeysMan(result, keyToRemove, keyToKeep), nil
}
err = platformapi.RebuildError(err, jdParams, watchKeys)
baseapi.SugarLogger.Infof("AccessAPINoPage failed, apiStr:%s, jdParams:%s, data:%s, error:%v", apiStr, utils.Format4Output(jdParams, true), utils.Format4Output(jsonResult, true), err)
return result, err
}
func normalHavePageResultParser(data map[string]interface{}, totalCount int) ([]interface{}, int, error) {
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 {
return nil, 0, platformapi.ErrResponseDataFormatWrong
}
}
result = tempResult.(map[string]interface{})
if totalCount == 0 {
for _, totalCountKey := range havePageTotalCountKeys {
if totalCount2, ok := result[totalCountKey]; ok {
totalCountInt64, _ := totalCount2.(json.Number).Int64()
totalCount = int(totalCountInt64)
if totalCount == 0 {
return make([]interface{}, 0), 0, nil
}
break
}
}
if totalCount == 0 {
baseapi.SugarLogger.Errorf("can not find totalCount key, data:%v", result)
return nil, 0, platformapi.ErrResponseDataFormatWrong
}
}
for _, inner2ResultKey := range havePageInner2DataKeys {
if inner2Result, ok := result[inner2ResultKey]; ok {
if inner2Result == nil {
retVal = nil
} else if inner2ResultStr, ok := inner2Result.(string); ok {
err := utils.UnmarshalUseNumber([]byte(inner2ResultStr), &retVal)
if err != nil {
return nil, 0, platformapi.ErrResponseDataFormatWrong
}
} else {
retVal = inner2Result.([]interface{})
}
return retVal, totalCount, nil
}
}
baseapi.SugarLogger.Errorf("can not find result key, data:%v", result)
return nil, 0, platformapi.ErrResponseDataFormatWrong
}
func (a *API) AccessAPIHavePage(apiStr string, jdParams map[string]interface{}, keyToRemove, keyToKeep []string, pageResultParser PageResultParser) ([]interface{}, int, error) {
if pageResultParser == nil {
pageResultParser = normalHavePageResultParser
}
localJdParams := make(map[string]interface{})
if jdParams != nil && len(jdParams) > 0 {
for k, v := range jdParams {
localJdParams[k] = v
}
}
totalCount := 0
pageNo := AllPage
pageSize := DefaultPageSize
if tempPageNo, ok := localJdParams[KeyPageNo]; ok {
pageNo = tempPageNo.(int)
}
if tempPageSize, ok := localJdParams[KeyPageSize]; ok {
pageSize = tempPageSize.(int)
}
curPage := pageNo
if curPage == AllPage {
curPage = 1
}
retVal := make([]interface{}, 0)
for {
localJdParams[KeyPageNo] = curPage
localJdParams[KeyPageSize] = pageSize
jsonResult, err := a.AccessAPI(apiStr, localJdParams)
if err != nil {
return nil, totalCount, err
}
var data map[string]interface{}
if err := utils.UnmarshalUseNumber([]byte(jsonResult["data"].(string)), &data); err != nil {
return nil, totalCount, platformapi.ErrResponseDataFormatWrong
}
innerCode := forceInnerCode2Str(data["code"])
if innerCode != "0" {
err2 := utils.NewErrorCode(getErrMsgFromData(data), innerCode, 1)
baseapi.SugarLogger.Infof("AccessAPIHavePage failed, apiStr:%s, jdParams:%s, data:%s, error:%v", apiStr, utils.Format4Output(jdParams, true), utils.Format4Output(jsonResult, true), err2)
return nil, totalCount, platformapi.RebuildError(err2, jdParams, watchKeys)
}
inResult, totalCount2, err := pageResultParser(data, totalCount)
if err != nil {
return nil, totalCount, platformapi.RebuildError(err, jdParams, watchKeys)
}
totalCount = totalCount2
inResult = utils.DictKeysMan(inResult, keyToRemove, keyToKeep).([]interface{})
retVal = append(retVal, inResult...)
if curPage == pageNo || curPage*pageSize >= totalCount {
break
}
curPage++
}
return retVal, totalCount, nil
}
func forceInnerCode2Str(innerCode interface{}) string {
if innerCode == nil {
return "0"
} else if innerCodeStr, ok := innerCode.(string); ok {
return innerCodeStr
}
return fmt.Sprintf("%v", innerCode)
}
func getErrMsgFromData(data map[string]interface{}) string {
msg := utils.Interface2String(data["msg"])
if msg == "" {
msg = string(utils.MustMarshal(data))
baseapi.SugarLogger.Warnf("getErrMsgFromData data:%v have no field msg!", data)
}
return msg
}