package jdapi import ( "crypto/md5" "encoding/json" "fmt" "net/http" "net/url" "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 ( prodURL = "https://openo2o.jd.com/djapi/%s" signKey = "sign" AllPage = 0 DefaultPageSize = 50 ) type API struct { token string appKey string appSecret string client *http.Client config *platformapi.APIConfig } var ( InnerCodeIsNotOk = "JD result inner code is not ok" exceedLimitCodes = map[string]int{ ResponseCodeExceedLimit: 1, } // todo replace all magic number 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 generateURL(baseURL, apiStr string, params map[string]interface{}) string { fullURL := "" if params != nil { for k, v := range params { if fullURL == "" { fullURL = "?" } else { fullURL += "&" } fullURL += k + "=" + url.QueryEscape(fmt.Sprint(v)) } } return fmt.Sprintf(baseURL, apiStr) + fullURL } 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 url, _ := url.Parse(generateURL(prodURL, apiStr, params)) request := &http.Request{ Method: "GET", URL: url, } err = platformapi.AccessPlatformAPIWithRetry(a.client, 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 } baseapi.SugarLogger.Warnf("response business code is not ok, data:%v, code:%v", jsonResult1, code) 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 { // todo 可以把具体错误消息放进来 return nil, utils.NewErrorCode(InnerCodeIsNotOk, 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" { // todo 可以把具体错误消息放进来 return nil, utils.NewErrorCode(InnerCodeIsNotOk, 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.MustInterface2Int64(innerCode)) }