京东eclp物流接口
This commit is contained in:
266
platformapi/jdeclpapi/jdeclpapi.go
Normal file
266
platformapi/jdeclpapi/jdeclpapi.go
Normal 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订单必填,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 []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
|
||||
}
|
||||
40
platformapi/jdeclpapi/jdeclpapi_test.go
Normal file
40
platformapi/jdeclpapi/jdeclpapi_test.go
Normal 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{})
|
||||
}
|
||||
Reference in New Issue
Block a user