314 lines
12 KiB
Go
314 lines
12 KiB
Go
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"`
|
||
}
|
||
|
||
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) (searchShopStockResult *SearchShopStockResult, 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"], &searchShopStockResult, false)
|
||
}
|
||
return searchShopStockResult, err
|
||
}
|