Files
baseapi/platformapi/jdeclpapi/jdeclpapi.go
2020-03-18 09:39:54 +08:00

314 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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订单必填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 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
}