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 ResponseCodeInvalidParam = 4 // 参数格式错误 ResponseCodeFlowControl = 11 // 接口流控 ResponseCodeShopNotExist = 112 // 门店不存在 ) // 取消原因 const ( CancelReasonClientActive = 101 CancelReasonClientChangeTimeOrAddress = 102 CancelReasonGoodsRelated = 103 CancelReasonMerchantOther = 199 CancelReasonMtpsAttitude = 201 CancelReasonRidderSendNotIntime = 202 CancelReasonRideerGetGoodNotIntime = 203 CancelReasonRideerMtpsOther = 299 CancelReasonRideerOther = 399 ) 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 API struct { platformapi.APICookie appKey string secret string client *http.Client config *platformapi.APIConfig } var ( exceedLimitCodes = map[int]int{ ResponseCodeFlowControl: 1, } ) 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) AccessAPI2(baseURL, action string, params map[string]interface{}) (retVal *ResponseResult, err error) { if params == nil { panic("params is nil!") } params2 := utils.Map2URLValues(params) if baseURL == mtpsAPIURL { params2.Set("appkey", a.appKey) params2.Set("timestamp", utils.Int64ToStr(utils.GetCurTimestamp())) params2.Set("version", "1.0") params2.Set(signKey, a.signParams(params2)) } else if a.GetCookieCount() == 0 { return nil, fmt.Errorf("需要设置Store Cookie才能使用此方法") } // baseapi.SugarLogger.Debug(params2.Encode()) err = platformapi.AccessPlatformAPIWithRetry(a.client, func() *http.Request { request, _ := http.NewRequest(http.MethodPost, baseURL+"/"+action, strings.NewReader(params2.Encode())) request.Header.Set("charset", "UTF-8") request.Header.Set("Content-Type", "application/x-www-form-urlencoded") if baseURL != mtpsAPIURL { a.FillRequestCookies(request) } 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 } msgKey := "message" if baseURL != mtpsAPIURL { msgKey = "msg" } retVal.Message, _ = jsonResult1[msgKey].(string) newErr := utils.NewErrorIntCode(retVal.Message, code) if _, ok := exceedLimitCodes[code]; ok { return platformapi.ErrLevelExceedLimit, newErr } return platformapi.ErrLevelCodeIsNotOK, newErr }) return retVal, err } func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *ResponseResult, err error) { return a.AccessAPI2(mtpsAPIURL, action, params) } 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) 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) } //评价骑手 func (a *API) EvaluateRider(deliveryId int64, mtPeiSongId string, score int, commentContent string) (err error) { params := map[string]interface{}{ "delivery_id": deliveryId, "mt_peisong_id": mtPeiSongId, "score": score, "comment_content": commentContent, } _, err = a.AccessAPI("order/evaluate", params) return err }