379 lines
11 KiB
Go
379 lines
11 KiB
Go
package mtpsapi
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.rosy.net.cn/baseapi/platformapi"
|
|
"git.rosy.net.cn/baseapi/utils"
|
|
)
|
|
|
|
const (
|
|
mtpsAPIURL = "https://peisongopen.meituan.com/api"
|
|
signKey = "sign"
|
|
)
|
|
|
|
const (
|
|
OrderStatusWaitingForSchedule = 0
|
|
OrderStatusAccepted = 20
|
|
OrderStatusPickedUp = 30
|
|
OrderStatusDeliverred = 50
|
|
OrderStatusCanceled = 99
|
|
)
|
|
|
|
const (
|
|
OrderExceptCodePhonePoweroff = 10001
|
|
OrderExceptCodePhoneSuspended = 10002
|
|
OrderExceptCodePhoneNobody = 10003
|
|
OrderExceptCodePhoneEmpty = 10004
|
|
OrderExceptCodePhoneWrong = 10005
|
|
OrderExceptCodePhoneOther = 10006
|
|
OrderExceptCodeAddressWrong = 10101
|
|
OrderExceptCodeAddressOutOfService = 10201
|
|
OrderExceptCodeUserReject = 10202
|
|
OrderExceptCodeUserRequestDelay = 10203
|
|
OrderExceptCodeStoreClosed = 10401
|
|
)
|
|
|
|
var (
|
|
OrderStatusName = map[int]string{
|
|
OrderExceptCodePhonePoweroff: "电话关机",
|
|
OrderExceptCodePhoneSuspended: "电话停机",
|
|
OrderExceptCodePhoneNobody: "无人接听",
|
|
OrderExceptCodePhoneEmpty: "电话空号",
|
|
OrderExceptCodePhoneWrong: "电话错误",
|
|
OrderExceptCodePhoneOther: "电话其它原因",
|
|
OrderExceptCodeAddressWrong: "地址错误",
|
|
OrderExceptCodeAddressOutOfService: "地址超区",
|
|
OrderExceptCodeUserReject: "用户拒收",
|
|
OrderExceptCodeUserRequestDelay: "要求延迟配送",
|
|
OrderExceptCodeStoreClosed: "门店关店",
|
|
}
|
|
)
|
|
|
|
const (
|
|
DeliveryServiceCodeSuperRapid = 4002 // 飞速达
|
|
DeliveryServiceCodeRapid = 4011 // 快速达
|
|
DeliveryServiceCodeIntime = 4012 // 及时达
|
|
DeliveryServiceCodeTogether = 4013 // 集中送
|
|
)
|
|
|
|
const (
|
|
PickupTypeClientSendToStation = 1
|
|
PickupTypeMtPick = 2
|
|
)
|
|
|
|
const (
|
|
OrderTypeASAP = 0
|
|
OrderTypeBook = 1
|
|
)
|
|
|
|
const (
|
|
CoordinateTypeMars = 0 // 火星坐标(高德,腾讯地图均采用火星坐标),此为默认项
|
|
CoordinateTypeBaidu = 1 // 百度坐标
|
|
)
|
|
|
|
// 错误码
|
|
const (
|
|
ResponseCodeSuccess = 0
|
|
)
|
|
|
|
// 取消原因
|
|
const (
|
|
CancelReasonClientActive = 101
|
|
CancelReasonClientChangeTimeOrAddress = 102
|
|
CancelReasonGoodsRelated = 103
|
|
CancelReasonMerchantOther = 199
|
|
|
|
CancelReasonMtpsAttitude = 201
|
|
CancelReasonRidderSendNotIntime = 202
|
|
CancelReasonRideerGetGoodNotIntime = 203
|
|
CancelReasonRideerMtpsOther = 299
|
|
|
|
CancelReasonRideerOther = 399
|
|
)
|
|
|
|
const (
|
|
ShopStatusAuditRejected = 10 // 审核驳回
|
|
ShopStatusAuditPassed = 20 // 审核通过
|
|
ShopStatusCreateSuccess = 30 // 创建成功
|
|
ShopStatusOnline = 40 // 上线可发单
|
|
)
|
|
|
|
const (
|
|
ShopCategoryMarket = 120 // 生活超市
|
|
ShopCategoryMarketConvenience = 120001 // 便利店
|
|
|
|
ShopCategoryFruit = 150 // 生鲜果蔬
|
|
ShopCategoryFruitFruit = 150001 // 果蔬
|
|
)
|
|
|
|
type OrderInfoCommon struct {
|
|
DeliveryID int64
|
|
MtPeisongID string
|
|
OrderID string
|
|
CourierName string
|
|
CourierPhone string
|
|
}
|
|
|
|
type OrderInfo struct {
|
|
OrderInfoCommon
|
|
Status int
|
|
OperateTime int
|
|
CancelReasonID int
|
|
CancelReason string
|
|
}
|
|
|
|
type OrderResponse struct {
|
|
MtPeisongID string `json:"mt_peisong_id"`
|
|
DeliveryID int64 `json:"delivery_id"`
|
|
OrderID string `json:"order_id"`
|
|
}
|
|
|
|
type ResponseResult struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Data map[string]interface{} `json:"data"`
|
|
}
|
|
|
|
type CreateOrderByShopParam struct {
|
|
DeliveryID int64 `json:"delivery_id"`
|
|
OrderID string `json:"order_id"`
|
|
ShopID string `json:"shop_id"`
|
|
DeliveryServiceCode int `json:"delivery_service_code"`
|
|
ReceiverName string `json:"receiver_name"`
|
|
ReceiverAddress string `json:"receiver_address"`
|
|
ReceiverPhone string `json:"receiver_phone"`
|
|
ReceiverLng int `json:"receiver_lng"`
|
|
ReceiverLat int `json:"receiver_lat"`
|
|
CoordinateType int `json:"coordinate_type"`
|
|
GoodsValue float64 `json:"goods_value"`
|
|
GoodsWeight float64 `json:"goods_weight"`
|
|
// ExpectedDeliveryTime int64 `json:"expected_delivery_time"`
|
|
OrderType int `json:"order_type"`
|
|
}
|
|
|
|
type GoodsItem struct {
|
|
GoodCount int `json:"goodCount"`
|
|
GoodName string `json:"goodName"`
|
|
GoodPrice float64 `json:"goodPrice"`
|
|
GoodUnit string `json:"goodUnit"`
|
|
}
|
|
|
|
type GoodsDetail struct {
|
|
Goods []*GoodsItem `json:"goods"`
|
|
}
|
|
|
|
type BusinessHour struct {
|
|
BeginTime string `json:"beginTime"`
|
|
EndTime string `json:"endTime"`
|
|
}
|
|
|
|
type ShopInfo struct {
|
|
ShopID string `json:"shop_id,omitempty"`
|
|
ShopName string `json:"shop_name,omitempty"`
|
|
Category int `json:"category,omitempty"`
|
|
SecondCategory int `json:"second_category,omitempty"`
|
|
ContactName string `json:"contact_name,omitempty"`
|
|
ContactPhone string `json:"contact_phone,omitempty"`
|
|
ContactEmail string `json:"contact_email,omitempty"`
|
|
ShopAddress string `json:"shop_address,omitempty"`
|
|
ShopAddressDetail string `json:"shop_address_detail,omitempty"`
|
|
ShopLng int `json:"shop_lng,omitempty"`
|
|
ShopLat int `json:"shop_lat,omitempty"`
|
|
CoordinateType int `json:"coordinate_type"`
|
|
DeliveryServiceCodes string `json:"delivery_service_codes,omitempty"`
|
|
BusinessHours string `json:"business_hours,omitempty"`
|
|
|
|
// 以下为查询专有的
|
|
City int `json:"city,omitempty"`
|
|
DeliveryHours string `json:"delivery_hours,omitempty"`
|
|
Prebook int `json:"prebook,omitempty"`
|
|
PrebookOutOfBizTime int `json:"prebook_out_of_biz_time,omitempty"`
|
|
PrebookPeriod string `json:"prebook_period,omitempty"`
|
|
}
|
|
|
|
type API struct {
|
|
appKey string
|
|
secret string
|
|
client *http.Client
|
|
config *platformapi.APIConfig
|
|
}
|
|
|
|
func New(appKey, secret string, config ...*platformapi.APIConfig) *API {
|
|
curConfig := platformapi.DefAPIConfig
|
|
if len(config) > 0 {
|
|
curConfig = *config[0]
|
|
}
|
|
return &API{
|
|
appKey: appKey,
|
|
secret: secret,
|
|
client: &http.Client{Timeout: curConfig.ClientTimeout},
|
|
config: &curConfig,
|
|
}
|
|
}
|
|
|
|
func (a *API) signParams(params url.Values) string {
|
|
keys := make([]string, 0)
|
|
for k := range params {
|
|
if k != signKey {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
finalStr := a.secret
|
|
for _, key := range keys {
|
|
valStr := strings.Join(params[key], "")
|
|
if valStr != "" {
|
|
finalStr += key + valStr
|
|
}
|
|
}
|
|
|
|
// baseapi.SugarLogger.Debug(finalStr)
|
|
return fmt.Sprintf("%x", sha1.Sum([]byte(finalStr)))
|
|
}
|
|
|
|
func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *ResponseResult, err error) {
|
|
if params == nil {
|
|
panic("params is nil!")
|
|
}
|
|
|
|
params2 := utils.Map2URLValues(params)
|
|
params2.Set("appkey", a.appKey)
|
|
params2.Set("timestamp", utils.Int64ToStr(utils.GetCurTimestamp()))
|
|
params2.Set("version", "1.0")
|
|
params2.Set(signKey, a.signParams(params2))
|
|
// baseapi.SugarLogger.Debug(params2.Encode())
|
|
|
|
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
|
func() *http.Request {
|
|
request, _ := http.NewRequest(http.MethodPost, mtpsAPIURL+"/"+action, strings.NewReader(params2.Encode()))
|
|
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")
|
|
}
|
|
code := int(utils.MustInterface2Int64(jsonResult1["code"]))
|
|
retVal = &ResponseResult{
|
|
Code: code,
|
|
}
|
|
if code == ResponseCodeSuccess {
|
|
if innerData, ok := jsonResult1["data"]; ok {
|
|
retVal.Data, _ = innerData.(map[string]interface{})
|
|
}
|
|
return platformapi.ErrLevelSuccess, nil
|
|
}
|
|
retVal.Message = jsonResult1["message"].(string)
|
|
newErr := utils.NewErrorIntCode(retVal.Message, code)
|
|
return platformapi.ErrLevelCodeIsNotOK, newErr
|
|
})
|
|
return retVal, err
|
|
}
|
|
|
|
func (a *API) result2OrderResponse(result *ResponseResult) (order *OrderResponse) {
|
|
order = new(OrderResponse)
|
|
order.MtPeisongID = result.Data["mt_peisong_id"].(string)
|
|
order.DeliveryID = utils.MustInterface2Int64(result.Data["delivery_id"])
|
|
order.OrderID = result.Data["order_id"].(string)
|
|
return order
|
|
}
|
|
|
|
func (a *API) CreateOrderByShop(basicParams *CreateOrderByShopParam, addParams map[string]interface{}) (order *OrderResponse, err error) {
|
|
params := utils.Struct2MapByJson(basicParams)
|
|
params["goods_value"] = strconv.FormatFloat(basicParams.GoodsValue, 'f', 2, 64)
|
|
params["goods_weight"] = strconv.FormatFloat(basicParams.GoodsWeight, 'f', 2, 64)
|
|
allParams := utils.MergeMaps(params, addParams)
|
|
|
|
if params["order_type"] != utils.Int2Str(OrderTypeBook) {
|
|
delete(params, "expected_delivery_time")
|
|
}
|
|
if result, err := a.AccessAPI("order/createByShop", allParams); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return a.result2OrderResponse(result), nil
|
|
}
|
|
}
|
|
|
|
func (a *API) QueryOrderStatus(deliveryId int64, mtPeiSongId string) (retVal map[string]interface{}, err error) {
|
|
params := map[string]interface{}{
|
|
"delivery_id": deliveryId,
|
|
"mt_peisong_id": mtPeiSongId,
|
|
}
|
|
if result, err := a.AccessAPI("order/status/query", params); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return result.Data, nil
|
|
}
|
|
}
|
|
|
|
func (a *API) CancelOrder(deliveryId int64, mtPeiSongId string, cancelReasonId int, cancelReason string) (result *OrderResponse, err error) {
|
|
params := map[string]interface{}{
|
|
"delivery_id": deliveryId,
|
|
"mt_peisong_id": mtPeiSongId,
|
|
"cancel_reason_id": cancelReasonId,
|
|
"cancel_reason": cancelReason,
|
|
}
|
|
if result, err := a.AccessAPI("order/delete", params); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return a.result2OrderResponse(result), nil
|
|
}
|
|
}
|
|
|
|
func (a *API) ShopCreate(shopInfo *ShopInfo) (status int, err error) {
|
|
params := utils.Struct2MapByJson(shopInfo)
|
|
result, err := a.AccessAPI("shop/create", params)
|
|
if err == nil {
|
|
status = int(utils.Interface2Int64WithDefault(result.Data["status"], 0))
|
|
}
|
|
return status, err
|
|
}
|
|
|
|
func (a *API) ShopQuery(shopID string) (shopInfo *ShopInfo, err error) {
|
|
result, err := a.AccessAPI("shop/query", map[string]interface{}{
|
|
"shop_id": shopID,
|
|
})
|
|
if err == nil {
|
|
err = utils.Map2StructByJson(result.Data, &shopInfo, false)
|
|
}
|
|
return shopInfo, err
|
|
}
|
|
|
|
func (a *API) simulateOrderBehavior(action string, deliveryId int64, mtPeiSongId string) (err error) {
|
|
params := map[string]interface{}{
|
|
"delivery_id": deliveryId,
|
|
"mt_peisong_id": mtPeiSongId,
|
|
}
|
|
_, err = a.AccessAPI("test/order/"+action, params)
|
|
return err
|
|
}
|
|
|
|
func (a *API) SimulateArrange(deliveryId int64, mtPeiSongId string) (err error) {
|
|
return a.simulateOrderBehavior("arrange", deliveryId, mtPeiSongId)
|
|
}
|
|
|
|
func (a *API) SimulatePickup(deliveryId int64, mtPeiSongId string) (err error) {
|
|
return a.simulateOrderBehavior("pickup", deliveryId, mtPeiSongId)
|
|
}
|
|
|
|
func (a *API) SimulateDeliver(deliveryId int64, mtPeiSongId string) (err error) {
|
|
return a.simulateOrderBehavior("deliver", deliveryId, mtPeiSongId)
|
|
}
|
|
|
|
func (a *API) SimulateRearrange(deliveryId int64, mtPeiSongId string) (err error) {
|
|
return a.simulateOrderBehavior("rearrange", deliveryId, mtPeiSongId)
|
|
}
|
|
|
|
func (a *API) SimulateReportException(deliveryId int64, mtPeiSongId string) (err error) {
|
|
return a.simulateOrderBehavior("reportException", deliveryId, mtPeiSongId)
|
|
}
|