package jdeclpapi import ( "crypto/md5" "fmt" "net/http" "sort" "strings" "time" "git.rosy.net.cn/baseapi" "git.rosy.net.cn/baseapi/platformapi" "git.rosy.net.cn/baseapi/utils" ) const ( prodURL = "https://api.jd.com/routerjson" prodURL2 = "https://oauth.jd.com/oauth" sigKey = "sign" IsvSource = "ISV0020000000068" DepartmentNo = "EBU4418046609452" ShopNo = "ESP0020000066348" WarehouseNo = "110014006" CustomerCode = "028K595510" OrderMark = "00000000000000000000000000000000000000000000000000" // CustomerCode2 = "028K588716" state = "1212" orderType10 = "10" //订单号类型(10:代表ECLP订单号, 20:代表商家订单号) orderType20 = "20" SalePlatformSource = "6" SoStatusCode10010 = "10010" //订单初始化 SoStatusCode10022 = "10022" //暂停 SoStatusCode10014 = "10014" //已下发库房 SoStatusCode10015 = "10015" //任务已分配 SoStatusCode10016 = "10016" //拣货下架 SoStatusCode10019 = "10019" //交接发货 SoStatusCode10018 = "10018" //货品已打包 SoStatusCode10017 = "10017" //复核 SoStatusCode10054 = "10054" //分拣中心发货 SoStatusCode10033 = "10033" //站点验收 SoStatusCode10034 = "10034" //妥投 ) type API struct { accessToken string appKey string appSecret string client *http.Client config *platformapi.APIConfig } type QuerySpSourceResult struct { Website string `json:"website"` //销售平台网址 SpSourceName string `json:"spSourceName"` //销售平台名称 SpSourceNo string `json:"spSourceNo"` //销售平台编号(必填,长度:20) Reserve5 string `json:"reserve5"` Reserve4 string `json:"reserve4"` Reserve3 string `json:"reserve3"` Reserve2 string `json:"reserve2"` Reserve1 string `json:"reserve1"` //预留字段1 } type AddOrderParam struct { IsvUUID string `json:"isvUUID"` //isv出库单号(商家出库单号),作为isv出库的唯一性校验码:长度不能超125 IsvSource string `json:"isvSource"` //ISV来源编号 ShopNo string `json:"shopNo"` //店铺编号(B2C订单必填,B2B(soType=2)订单非必填) DepartmentNo string `json:"departmentNo"` //事业部编号,且与pin匹配 WarehouseNo string `json:"warehouseNo"` //库房编号,事业部开启寻源拆分服务可不填;否则必填; SalesPlatformOrderNo string `json:"salesPlatformOrderNo"` //销售平台订单号,如果销售平台来源为京东平台,则该字段不能为空,长度不超过200 SalePlatformSource string `json:"salePlatformSource"` //销售平台来源 ConsigneeName string `json:"consigneeName"` //收货人姓名,长度不能超20 ConsigneeMobile string `json:"consigneeMobile"` //收货人手机(收货人电话、手机至少有一个不为空),长度不能超30 ConsigneeAddress string `json:"consigneeAddress"` //收货人地址 ,长度不能超100 OrderMark string `json:"orderMark"` //订单标记位,首位为1代表货到付款 GoodsNo string `json:"goodsNo"` //ECLP商品编号,与商家商品编号二选一必填(两者都指定,以goodsNo为准) Price string `json:"price"` //商品金额,会在订单面单上线上商品单价 Quantity string `json:"quantity"` //商品的出库数量,必须>0 // IsvGoodsNo string `json:"isvGoodsNo"` //商家商品编码 } type AddOrderResult struct { EclpSoNo string `json:"eclpSoNo"` //订单的出库单号 } type CancelOrderResult struct { Code string `json:"code"` Msg string `json:"msg"` } type QueryOrderStatusResult struct { OrderStatusList []struct { SoStatusCode string `json:"soStatusCode"` OperateTime string `json:"operateTime"` SoStatusName string `json:"soStatusName"` OperateUser string `json:"operateUser"` } `json:"orderStatusList"` IsvUUID string `json:"isvUUID"` EclpSoNo string `json:"eclpSoNo"` } type GetTrackMessagePlusByOrderResult struct { ResultData []struct { OpeTitle string `json:"opeTitle"` OpeTime string `json:"opeTime"` OpeRemark string `json:"opeRemark"` OpeName string `json:"opeName"` WaybillCode string `json:"waybillCode"` } `json:"resultData"` ResultCode string `json:"resultCode"` ResultMessage string `json:"resultMessage"` } type SearchShopStockParam struct { RequestId string `json:"requestId"` DeptNo string `json:"deptNo"` ShopNo string `json:"shopNo"` GoodsNo string `json:"goodsNo"` PageSize int `json:"pageSize"` PageNumber int `json:"pageNumber"` } type SearchShopStockResult struct { PageCount string `json:"pageCount"` ResponseCode string `json:"responseCode"` RequestID string `json:"requestId"` ErrMsg string `json:"errMsg"` Data []struct { DeptNo string `json:"deptNo"` StockNum string `json:"stockNum"` GoodsNo string `json:"goodsNo"` WarehouseNo string `json:"warehouseNo"` ShopNo string `json:"shopNo"` } `json:"data"` PageSize string `json:"pageSize"` PageNumber string `json:"pageNumber"` } type QueryStockResult struct { RecordCount int `json:"recordCount"` IsvSku string `json:"isvSku"` DeptName string `json:"deptName"` SellerGoodsSign string `json:"sellerGoodsSign"` GoodsNo string `json:"goodsNo"` UsableNum int `json:"usableNum"` DeptNo string `json:"deptNo"` WarehouseName string `json:"warehouseName"` TotalNum int `json:"totalNum"` StockType string `json:"stockType"` StockStatus string `json:"stockStatus"` GoodsLevel string `json:"goodsLevel"` WarehouseNo string `json:"warehouseNo"` GoodsName string `json:"goodsName"` } func New(accessToken, appKey, appSecret string, config ...*platformapi.APIConfig) *API { curConfig := platformapi.DefAPIConfig if len(config) > 0 { curConfig = *config[0] } return &API{ accessToken: accessToken, appKey: appKey, appSecret: appSecret, client: &http.Client{Timeout: curConfig.ClientTimeout}, config: &curConfig, } } func (a *API) signParam(params map[string]interface{}) (sig string) { var valueList []string for k, v := range params { if k != sigKey { if str := fmt.Sprint(v); str != "" { valueList = append(valueList, fmt.Sprintf("%s%s", k, str)) } } } sort.Sort(sort.StringSlice(valueList)) valueList = append(valueList, fmt.Sprintf("%s", a.appSecret)) var valueList2 = make([]string, len(valueList)+1) at := copy(valueList2, valueList[:0]) at += copy(valueList2[at:], []string{a.appSecret}) copy(valueList2[at:], valueList[0:]) sig = strings.Join(valueList2, "") binSig := md5.Sum([]byte(sig)) sig = fmt.Sprintf("%X", binSig) return sig } func (a *API) AccessAPI(action string, url string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) { params := make(map[string]interface{}) var fullURL string if url == prodURL2 { params = utils.MergeMaps(params, bizParams) fullURL = utils.GenerateGetURL(url, action, nil) } else { params["access_token"] = a.accessToken params["app_key"] = a.appKey params["timestamp"] = utils.Time2Str(time.Now()) params["method"] = action params["v"] = "2.0" params = utils.MergeMaps(params, bizParams) signStr := a.signParam(params) params["sign"] = signStr fullURL = utils.GenerateGetURL(url, "", nil) } err = platformapi.AccessPlatformAPIWithRetry(a.client, func() *http.Request { 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") 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") } if err == nil { if jsonResult1["error_response"] != nil { errLevel = platformapi.ErrLevelGeneralFail err = utils.NewErrorCode(jsonResult1["error_response"].(map[string]interface{})["zh_desc"].(string), jsonResult1["error_response"].(map[string]interface{})["code"].(string)) baseapi.SugarLogger.Debugf("jdeclp AccessAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true)) } retVal = jsonResult1 } return errLevel, err }) return retVal, err } //获取销售平台信息 //https://open.jd.com/home/home#/doc/api?apiCateId=138&apiId=947&apiName=jingdong.eclp.master.querySpSource func (a *API) QuerySpSource() (querySpSourceResult []*QuerySpSourceResult, err error) { result, err := a.AccessAPI("jingdong.eclp.master.querySpSource", prodURL, nil) if err == nil { utils.Map2StructByJson(result["jingdong_eclp_master_querySpSource_responce"].(map[string]interface{})["queryspsource_result"], &querySpSourceResult, false) } return querySpSourceResult, err } //销售出库单下发 //https://open.jd.com/home/home#/doc/api?apiCateId=138&apiId=928&apiName=jingdong.eclp.order.addOrder func (a *API) AddOrder(addOrderParam *AddOrderParam) (eclpSoNo string, err error) { result, err := a.AccessAPI("jingdong.eclp.order.addOrder", prodURL, utils.Struct2FlatMap(addOrderParam)) if err == nil { return result["jingdong_eclp_order_addOrder_responce"].(map[string]interface{})["eclpSoNo"].(string), err } return "", err } //销售出库单取消 //https://open.jd.com/home/home#/doc/api?apiCateId=138&apiId=957&apiName=jingdong.eclp.order.cancelOrder func (a *API) CancelOrder(eclpSoNo string) (cancelOrderResult *CancelOrderResult, err error) { result, err := a.AccessAPI("jingdong.eclp.order.cancelOrder", prodURL, map[string]interface{}{ "eclpSoNo": eclpSoNo, }) if err == nil { utils.Map2StructByJson(result["jingdong_eclp_order_cancelOrder_responce"].(map[string]interface{})["cancelorder_result"], &cancelOrderResult, false) } return cancelOrderResult, err } //销售出库单状态查询 //https://jos.jd.com/api/detail.htm?apiName=jingdong.eclp.order.queryOrderStatus&id=929 func (a *API) QueryOrderStatus(eclpSoNo string) (queryOrderStatus *QueryOrderStatusResult, err error) { result, err := a.AccessAPI("jingdong.eclp.order.queryOrderStatus", prodURL, map[string]interface{}{ "eclpSoNo": eclpSoNo, }) if err == nil { utils.Map2StructByJson(result["jingdong_eclp_order_queryOrderStatus_responce"].(map[string]interface{})["queryorderstatus_result"], &queryOrderStatus, false) } return queryOrderStatus, err } //查询物流跟踪消息 //https://open.jd.com/home/home#/doc/api?apiCateId=138&apiId=2378&apiName=jingdong.eclp.order.getTrackMessagePlusByOrder func (a *API) GetTrackMessagePlusByOrder(bizCode string) (getTrackMessagePlusByOrderResult *GetTrackMessagePlusByOrderResult, err error) { result, err := a.AccessAPI("jingdong.eclp.order.getTrackMessagePlusByOrder", prodURL, map[string]interface{}{ "bizCode": bizCode, "customerCode": CustomerCode, "type": orderType20, }) if err == nil { utils.Map2StructByJson(result["jingdong_eclp_order_getTrackMessagePlusByOrder_responce"].(map[string]interface{})["getTrackMessagePlusByOrder_result"], &getTrackMessagePlusByOrderResult, false) } return getTrackMessagePlusByOrderResult, err } //查询仓库商品库存 //https://open.jd.com/home/home#/doc/api?apiCateId=138&apiId=3396&apiName=jingdong.eclp.stock.searchShopStock func (a *API) SearchShopStock(goodsNo string) (searchShopStockResult *SearchShopStockResult, err error) { params := map[string]interface{}{ "requestId": utils.GetUUID(), "deptNo": DepartmentNo, "shopNo": ShopNo, "pageSize": 100, "pageNumber": 1, } if goodsNo != "" { params["goodsNo"] = goodsNo } result, err := a.AccessAPI("jingdong.eclp.stock.searchShopStock", prodURL, params) if err == nil { utils.Map2StructByJson(result["jingdong_eclp_stock_searchShopStock_responce"].(map[string]interface{})["shopStockSearchResponse"], &searchShopStockResult, false) } return searchShopStockResult, err } //查询仓库商品库存 //https://open.jd.com/home/home#/doc/api?apiCateId=138&apiId=3396&apiName=jingdong.eclp.stock.searchShopStock func (a *API) QueryStock(goodsNo string) (queryStockResult []*QueryStockResult, err error) { params := map[string]interface{}{ "deptNo": DepartmentNo, "warehouseNo": WarehouseNo, "returnZeroStock": 2, //表示返回库存为0的数据 } if goodsNo != "" { params["goodsNo"] = goodsNo } result, err := a.AccessAPI("jingdong.eclp.stock.queryStock", prodURL, params) if err == nil { utils.Map2StructByJson(result["jingdong_eclp_stock_queryStock_responce"].(map[string]interface{})["querystock_result"], &queryStockResult, false) } return queryStockResult, err }