package jdapi import ( "crypto/md5" "encoding/json" "fmt" "net/http" "sort" "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.com)API分组有接口详细说明。 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" ) const ( prodURL = "https://openo2o.jd.com/djapi" signKey = "sign" AllPage = 0 DefaultPageSize = 50 ) type API struct { token string appKey string appSecret string client *http.Client config *platformapi.APIConfig } 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"} ) 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 != "app_secret" && k != signKey { keys = append(keys, k) } } sort.Strings(keys) secretStr := fmt.Sprint(jdParams["app_secret"]) allStr := secretStr for _, k := range keys { allStr += k + fmt.Sprint(jdParams[k]) } allStr = allStr + secretStr 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["app_secret"] = a.appSecret params["token"] = a.token if jdParams == nil { jdParams = make(map[string]interface{}, 0) } jdParamStr, err := json.Marshal(jdParams) if err != nil { baseapi.SugarLogger.Errorf("Error when marshal %v, error:%v", jdParams, err) return nil, err } params["jd_param_json"] = string(jdParamStr) params["timestamp"] = utils.GetCurTimeStr() sign := a.signParams(params) params[signKey] = sign err = platformapi.AccessPlatformAPIWithRetry(a.client, func() *http.Request { request, _ := http.NewRequest(http.MethodGet, utils.GenerateGetURL(prodURL, apiStr, params), nil) // request.Close = true //todo 为了性能考虑还是不要关闭 return request }, a.config, func(response *http.Response) (errLevel string, err error) { jsonResult1, err := utils.HTTPResponse2Json(response) if err != nil { return platformapi.ErrLevelGeneralFail, platformapi.ErrResponseDataFormatWrong } 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 { return platformapi.ErrLevelCodeIsNotOK, newErr } }) return retVal, err } func (a *API) AccessAPINoPage(apiStr string, jdParams map[string]interface{}, keyToRemove, keyToKeep []string) (interface{}, error) { 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 } innerCode := "" for _, innerCodeKey := range innerCodeKeys { if innerCode2, ok := data[innerCodeKey]; ok { innerCode = forceInnerCode2Str(innerCode2) break } } if _, ok := innerCodeOKCodes[innerCode]; ok { for _, innerDataKey := range noPageInnerDataKeys { if innerData, ok := data[innerDataKey]; ok { return utils.DictKeysMan(innerData, keyToRemove, keyToKeep), nil } } baseapi.SugarLogger.Errorf("can not find inner data, data:%v", jsonResult) return nil, platformapi.ErrResponseDataFormatWrong } else { return nil, utils.NewErrorCode(string(utils.MustMarshal(data)), innerCode, 1) } } 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 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{}, 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["pageNo"]; ok { pageNo = tempPageNo.(int) } if tempPageSize, ok := localJdParams["pageSize"]; ok { pageSize = tempPageSize.(int) } curPage := pageNo if curPage == AllPage { curPage = 1 } retVal := make([]interface{}, 0) for { localJdParams["pageNo"] = curPage localJdParams["pageSize"] = pageSize jsonResult, err := a.AccessAPI(apiStr, localJdParams) 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 } innerCode := forceInnerCode2Str(data["code"]) if innerCode != "0" && innerCode != "200" { return nil, utils.NewErrorCode(string(utils.MustMarshal(data)), innerCode, 1) } inResult, totalCount2, err := pageResultParser(data, totalCount) if err != nil { return nil, err } 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.Interface2Int64WithDefault(innerCode, 0)) }