京东eclp物流接口

This commit is contained in:
苏尹岚
2020-03-05 16:50:18 +08:00
parent 9e2d38ac5c
commit 81db7b0bb3
2 changed files with 306 additions and 0 deletions

View File

@@ -0,0 +1,266 @@
package jdeclpapi
import (
"crypto/md5"
"fmt"
"net/http"
"sort"
"strings"
"time"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
prodURL = "https://api.jd.com/routerjson"
sigKey = "sign"
IsvSource = "ISV0020000000068"
DepartmentNo = "EBU4418046609452"
ShopNo = "ESP0020000066348"
WarehouseNo = "110014006"
CustomerCode = "028K595510"
OrderMark = "00000"
// CustomerCode2 = "028K588716"
orderType10 = "10" //订单号类型(10代表ECLP订单号, 20代表商家订单号)
orderType20 = "20"
)
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订单必填B2BsoType=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 []int `json:"price"` //商品金额,会在订单面单上线上商品单价
Quantity []int `json:"quantity"` //商品的出库数量,必须>0
}
type AddOrderResult struct {
EclpSoNo string `json:"eclpSoNo"` //订单的出库单号
}
type CancelOrderResult struct {
Code string `json:"code"`
Msg string `json:"msg"`
}
type QueryOrderStatusResult struct {
OrderStatusList []struct {
OrderStatus struct {
SoStatusCode string `json:"soStatusCode"`
OperateTime string `json:"operateTime"`
SoStatusName string `json:"soStatusName"`
OperateUser string `json:"operateUser"`
} `json:"orderStatus"`
} `json:"orderStatusList"`
IsvUUID string `json:"isvUUID"`
EclpSoNo string `json:"eclpSoNo"`
}
type GetTrackMessagePlusByOrderResult struct {
ResultData []struct {
___ struct {
OpeTitle string `json:"opeTitle"`
OpeTime string `json:"opeTime"`
OpeRemark string `json:"opeRemark"`
OpeName string `json:"opeName"`
WaybillCode string `json:"waybillCode"`
} `json:"___"`
} `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 {
___ struct {
DeptNo string `json:"deptNo"`
StockNum string `json:"stockNum"`
GoodsNo string `json:"goodsNo"`
WarehouseNo string `json:"warehouseNo"`
ShopNo string `json:"shopNo"`
} `json:"___"`
} `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, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
params := make(map[string]interface{})
params["access_token"] = a.accessToken
params["app_key"] = a.appKey
params["timestamp"] = utils.Time2Str(time.Now())
params = utils.MergeMaps(params, bizParams)
signStr := a.signParam(params)
params["sign"] = signStr
fullURL := utils.GenerateGetURL(prodURL, "method="+action, 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 {
// returnCode := utils.Interface2String(jsonResult1["trxstatus"])
// if returnCode != TrxStatusSuccess {
// errLevel = platformapi.ErrLevelGeneralFail
// err = utils.NewErrorCode(utils.Interface2String(jsonResult1["errmsg"])+utils.Interface2String(jsonResult1["retmsg"]), returnCode)
// baseapi.SugarLogger.Debugf("tonglianpay 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", nil)
if err == nil {
utils.Map2StructByJson(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) (addOrderResult *AddOrderResult, err error) {
result, err := a.AccessAPI("jingdong.eclp.order.addOrder", utils.Struct2FlatMap(addOrderParam))
if err == nil {
utils.Map2StructByJson(result, &addOrderResult, false)
}
return addOrderResult, 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", map[string]interface{}{
"eclpSoNo": eclpSoNo,
})
if err == nil {
utils.Map2StructByJson(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", map[string]interface{}{
"eclpSoNo": eclpSoNo,
})
if err == nil {
utils.Map2StructByJson(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", map[string]interface{}{
"bizCode": bizCode,
"customerCode": CustomerCode,
"type": orderType20,
})
if err == nil {
utils.Map2StructByJson(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(searchShopStockParam *SearchShopStockParam) (searchShopStockResult *SearchShopStockResult, err error) {
result, err := a.AccessAPI("jingdong.eclp.order.searchShopStock", utils.Struct2FlatMap(searchShopStockParam))
if err == nil {
utils.Map2StructByJson(result, &searchShopStockResult, false)
}
return searchShopStockResult, err
}

View File

@@ -0,0 +1,40 @@
package jdeclpapi
import (
"testing"
"git.rosy.net.cn/baseapi"
"go.uber.org/zap"
)
var (
api *API
sugarLogger *zap.SugaredLogger
)
func init() {
logger, _ := zap.NewDevelopment()
sugarLogger = logger.Sugar()
baseapi.Init(sugarLogger)
api = New("", "", "")
}
func TestQuerySpSource(t *testing.T) {
api.QuerySpSource()
}
func TestAddOrder(t *testing.T) {
api.AddOrder(&AddOrderParam{})
}
func TestCancelOrder(t *testing.T) {
api.CancelOrder("")
}
func TestQueryOrderStatus(t *testing.T) {
api.QueryOrderStatus("")
}
func TestGetTrackMessagePlusByOrder(t *testing.T) {
api.GetTrackMessagePlusByOrder("")
}
func TestSearchShopStock(t *testing.T) {
api.SearchShopStock(&SearchShopStockParam{})
}