package mtpsapi import ( "crypto/sha1" "fmt" "net/http" "net/url" "sort" "strconv" "strings" "time" "git.rosy.net.cn/baseapi/platform/common" "git.rosy.net.cn/baseapi/utils" "go.uber.org/zap" ) const ( clientTimeout = time.Second * 10 sleepSecondWhenLimited = 6 * time.Second maxRetryCountWhenNetworkException = 3 maxRetryCountWhenReachLimited = 10 ) const ( mtpsAPIURL = "https://peisongopen.meituan.com/api" signKey = "sign" ) const ( OrderStatusWaitingForSchedule = 0 OrderStatusAccepted = 20 OrderStatusPickedUp = 30 OrderStatusDeliverred = 50 OrderStatusCanceled = 99 ) const ( DSCRapid = 4011 DSCIntime = 4012 DSCTogether = 4013 ) const ( PickupTypeClientSendToStation = 1 PickupTypeMtPick = 2 ) const ( OrderTypeASAP = 0 OrderTypeBook = 1 ) const ( CoordinateTypeMars = 0 CoordinateTypeBaidu = 1 ) // 错误码 const ( ResponseCodeSuccess = 0 ) // 取消原因 const ( CancelReasonClientActive = 101 CancelReasonClientChangeTimeOrAddress = 102 CancelReasonGoodRelated = 103 CancelReasonMerchantOther = 199 CancelReasonMtpsAttitude = 201 CancelReasonRidderSendNotIntime = 202 CancelReasonRideerGetGoodNotIntime = 203 CancelReasonRideerMtpsOther = 299 CancelReasonRideerOther = 399 ) type MtpsOrderInfoCommon struct { DeliveryId int64 MtPeisongId string OrderId string CourierName string CourierPhone string } type MtpsOrderInfo struct { MtpsOrderInfoCommon Status int OperateTime int CancelReasonId int CancelReason string } type MtpsOrderResponse struct { MtPeisongId string `json:"mt_peisong_id` DeliveryId int64 `json:"delivery_id` OrderId string `json:"order_id` } type MTPSResult struct { Code int `json:"code"` Message string `json:"message"` Data map[string]interface{} `json:"data"` } type MtpsCreateOrderByShopInfo struct { DeliveryId int64 OrderId string ShopId string DeliveryServiceCode int ReceiverName string ReceiverAddress string ReceiverPhone string ReceiverLng int ReceiverLat int CoordinateType int GoodsValue float64 GoodsWeight float64 ExpectedDeliveryTime int64 OrderType int } type MTPSAPI struct { appKey string secret string sugarLogger *zap.SugaredLogger client *http.Client } func NewMTPSAPI(appKey, secret string, sugarLogger *zap.SugaredLogger) *MTPSAPI { api := &MTPSAPI{ appKey: appKey, secret: secret, sugarLogger: sugarLogger, client: &http.Client{Timeout: clientTimeout}, } return api } func (m *MTPSAPI) signParams(params url.Values) string { keys := make([]string, 0) for k := range params { if k != signKey { keys = append(keys, k) } } sort.Strings(keys) finalStr := m.secret for _, key := range keys { valStr := strings.Join(params[key], "") if valStr != "" { finalStr += key + valStr } } // m.sugarLogger.Debug(finalStr) return fmt.Sprintf("%x", sha1.Sum([]byte(finalStr))) } func (m *MTPSAPI) AccessMTPS(action string, params map[string]interface{}) (retVal *MTPSResult, err error) { if params == nil { panic("params is nil!") } params2 := make(url.Values) for k, v := range params { params2[k] = []string{fmt.Sprint(v)} } params2["appkey"] = []string{m.appKey} params2["timestamp"] = []string{utils.Int64ToStr(utils.GetCurTimestamp())} params2["version"] = []string{"1.0"} params2[signKey] = []string{m.signParams(params2)} // m.sugarLogger.Debug(params2.Encode()) request, _ := http.NewRequest("POST", mtpsAPIURL+"/"+action, strings.NewReader(params2.Encode())) request.Header.Set("Content-Type", "application/x-www-form-urlencoded") apiAccess := &common.AccessPlatformAPIWithRetryParams{ MaxExceedLimitRetryCount: maxRetryCountWhenReachLimited, MaxRecoverableRetryCount: maxRetryCountWhenNetworkException, SleepSecondWhenExceedLimit: sleepSecondWhenLimited, Client: m.client, Request: request, SugarLogger: m.sugarLogger, } err = common.AccessPlatformAPIWithRetry(apiAccess, func(response *http.Response) (result string, err error) { jsonResult1, err := utils.HttpResponse2Json(response) if err != nil { m.sugarLogger.Warnf("HttpResponse2Json return:%v", err) return common.PAErrorLevelGeneralFail, err } code := int(utils.MustInterface2Int64(jsonResult1["code"])) retVal = &MTPSResult{ Code: code, } m.sugarLogger.Debug(jsonResult1) if code == ResponseCodeSuccess { if innerData, ok := jsonResult1["data"]; ok { retVal.Data, _ = innerData.(map[string]interface{}) } return common.PAErrorLevelSuccess, nil } retVal.Message = jsonResult1["message"].(string) return common.PAErrorLevelGeneralFail, utils.NewErrorIntCode(retVal.Message, code) }) return retVal, err } func (m *MTPSAPI) result2OrderResponse(result *MTPSResult) (order *MtpsOrderResponse) { order = new(MtpsOrderResponse) 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 (m *MTPSAPI) CreateOrderByShop(basicParams *MtpsCreateOrderByShopInfo, addParams map[string]interface{}) (order *MtpsOrderResponse, err error) { params := make(map[string]interface{}) params["delivery_id"] = utils.Int64ToStr(basicParams.DeliveryId) params["order_id"] = basicParams.OrderId params["shop_id"] = basicParams.ShopId params["delivery_service_code"] = utils.Int2Str(basicParams.DeliveryServiceCode) params["receiver_name"] = basicParams.ReceiverName params["receiver_address"] = basicParams.ReceiverAddress params["receiver_phone"] = basicParams.ReceiverPhone params["receiver_lng"] = utils.Int2Str(basicParams.ReceiverLng) params["receiver_lat"] = utils.Int2Str(basicParams.ReceiverLat) params["coordinate_type"] = utils.Int2Str(basicParams.CoordinateType) params["goods_value"] = strconv.FormatFloat(basicParams.GoodsValue, 'f', 2, 64) params["goods_weight"] = strconv.FormatFloat(basicParams.GoodsWeight, 'f', 2, 64) params["expected_delivery_time"] = utils.Int64ToStr(basicParams.ExpectedDeliveryTime) params["order_type"] = utils.Int2Str(basicParams.OrderType) if addParams != nil { for k, v := range addParams { params[k] = v } } if params["order_type"] != utils.Int2Str(OrderTypeBook) { delete(params, "expected_delivery_time") } if result, err := m.AccessMTPS("order/createByShop", params); err != nil { m.sugarLogger.Debugf("result:%v", result) return nil, utils.NewErrorIntCode(err.Error(), result.Code) } else { return m.result2OrderResponse(result), nil } } func (m *MTPSAPI) 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 := m.AccessMTPS("order/status/query", params); err != nil { return nil, err } else { return result.Data, nil } } func (m *MTPSAPI) CancelOrder(deliveryId int64, mtPeiSongId string, cancelReasonId int, cancelReason string) (result *MtpsOrderResponse, err error) { params := map[string]interface{}{ "delivery_id": deliveryId, "mt_peisong_id": mtPeiSongId, "cancel_reason_id": cancelReasonId, "cancel_reason": cancelReason, } if result, err := m.AccessMTPS("order/delete", params); err != nil { m.sugarLogger.Debugf("result:%v", result) return nil, err } else { return m.result2OrderResponse(result), nil } } func (m *MTPSAPI) simulateOrderBehavior(action string, deliveryId int64, mtPeiSongId string) (err error) { params := map[string]interface{}{ "delivery_id": deliveryId, "mt_peisong_id": mtPeiSongId, } _, err = m.AccessMTPS("test/order/"+action, params) return err } func (m *MTPSAPI) SimulateArrange(deliveryId int64, mtPeiSongId string) (err error) { return m.simulateOrderBehavior("arrange", deliveryId, mtPeiSongId) } func (m *MTPSAPI) SimulatePickup(deliveryId int64, mtPeiSongId string) (err error) { return m.simulateOrderBehavior("pickup", deliveryId, mtPeiSongId) } func (m *MTPSAPI) SimulateDeliver(deliveryId int64, mtPeiSongId string) (err error) { return m.simulateOrderBehavior("deliver", deliveryId, mtPeiSongId) } func (m *MTPSAPI) SimulateRearrange(deliveryId int64, mtPeiSongId string) (err error) { return m.simulateOrderBehavior("rearrange", deliveryId, mtPeiSongId) } func (m *MTPSAPI) SimulateReportException(deliveryId int64, mtPeiSongId string) (err error) { return m.simulateOrderBehavior("reportException", deliveryId, mtPeiSongId) }