344 lines
13 KiB
Go
344 lines
13 KiB
Go
package mtps
|
||
|
||
import (
|
||
"crypto/sha1"
|
||
"errors"
|
||
"fmt"
|
||
"net/http"
|
||
"net/url"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.rosy.net.cn/baseapi/platformapi/mtpsapi"
|
||
"git.rosy.net.cn/baseapi/utils"
|
||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||
"git.rosy.net.cn/jx-callback/business/model"
|
||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||
"git.rosy.net.cn/jx-callback/business/partner"
|
||
"git.rosy.net.cn/jx-callback/business/partner/delivery"
|
||
"git.rosy.net.cn/jx-callback/globals"
|
||
"git.rosy.net.cn/jx-callback/globals/api"
|
||
beego "github.com/astaxie/beego/adapter"
|
||
"github.com/astaxie/beego/adapter/orm"
|
||
)
|
||
|
||
const (
|
||
// maxOrderPrice = 6399 // 单位为分,达达最大价格,超过这个价格配送费会增加
|
||
maxOrderWeight = 5000 // 5公斤
|
||
)
|
||
|
||
var (
|
||
ErrCanNotFindMTPSStore = errors.New("不能找到美团配送站点配置")
|
||
ErrStoreNoPriceInfo = errors.New("找不到门店的美团配送价格信息")
|
||
)
|
||
|
||
var (
|
||
curDeliveryHandler *DeliveryHandler
|
||
)
|
||
|
||
type DeliveryHandler struct {
|
||
}
|
||
|
||
func init() {
|
||
if api.MtpsAPI != nil {
|
||
curDeliveryHandler = new(DeliveryHandler)
|
||
partner.RegisterDeliveryPlatform(curDeliveryHandler, true)
|
||
}
|
||
}
|
||
|
||
func (c *DeliveryHandler) GetVendorID() int {
|
||
return model.VendorIDMTPS
|
||
}
|
||
|
||
func OnWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *mtpsapi.CallbackResponse) {
|
||
return curDeliveryHandler.OnWaybillMsg(msg)
|
||
}
|
||
|
||
func OnWaybillExcept(msg *mtpsapi.CallbackOrderExceptionMsg) (retVal *mtpsapi.CallbackResponse) {
|
||
return curDeliveryHandler.OnWaybillExcept(msg)
|
||
}
|
||
|
||
func (c *DeliveryHandler) OnWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *mtpsapi.CallbackResponse) {
|
||
jxutils.CallMsgHandler(func() {
|
||
retVal = c.onWaybillMsg(msg)
|
||
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDMTPS))
|
||
return retVal
|
||
}
|
||
|
||
func (c *DeliveryHandler) OnWaybillExcept(msg *mtpsapi.CallbackOrderExceptionMsg) (retVal *mtpsapi.CallbackResponse) {
|
||
jxutils.CallMsgHandler(func() {
|
||
order := &model.Waybill{
|
||
VendorWaybillID: msg.MtPeisongID,
|
||
VendorWaybillID2: utils.Int64ToStr(msg.DeliveryID),
|
||
WaybillVendorID: model.VendorIDMTPS,
|
||
CourierName: msg.CourierName,
|
||
CourierMobile: msg.CourierPhone,
|
||
Status: model.WaybillStatusUnknown, // todo 这里要再确定一下是否只要收到订单异常消息就只简单当成一个消息
|
||
VendorStatus: utils.Int2Str(msg.ExceptionCode),
|
||
StatusTime: utils.Timestamp2Time(msg.Timestamp),
|
||
}
|
||
order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
|
||
retVal = mtpsapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), "mtps OnWaybillExcept")
|
||
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDDada))
|
||
return retVal
|
||
}
|
||
|
||
func (c *DeliveryHandler) onWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *mtpsapi.CallbackResponse) {
|
||
order := c.callbackMsg2Waybill(msg)
|
||
switch msg.Status {
|
||
case mtpsapi.OrderStatusWaitingForSchedule:
|
||
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order)
|
||
order.Status = model.WaybillStatusNew
|
||
case mtpsapi.OrderStatusAccepted:
|
||
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order) // 美团外卖可能会丢失新运单事件,这里补一下
|
||
order.Status = model.WaybillStatusAccepted
|
||
order.Remark = order.CourierName + "," + order.CourierMobile
|
||
case mtpsapi.OrderStatusPickedUp:
|
||
order.Status = model.WaybillStatusDelivering
|
||
case mtpsapi.OrderStatusDeliverred:
|
||
order.Status = model.WaybillStatusDelivered
|
||
case mtpsapi.OrderStatusCanceled:
|
||
order.Status = model.WaybillStatusCanceled
|
||
default:
|
||
globals.SugarLogger.Warnf("onWaybillMsg unknown msg:%v", msg)
|
||
return mtpsapi.SuccessResponse
|
||
}
|
||
order2, _ := partner.CurOrderManager.LoadOrder(order.VendorOrderID, order.OrderVendorID)
|
||
// order2, _ := dao.GetSimpleOrder(dao.GetDB(), order.VendorOrderID)
|
||
//查不到订单可能就是果园的订单
|
||
if order2 == nil {
|
||
c.pushToGy(msg)
|
||
return mtpsapi.SuccessResponse
|
||
}
|
||
return mtpsapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), order.VendorStatus)
|
||
}
|
||
|
||
func (c *DeliveryHandler) pushToGy(msg *mtpsapi.CallbackOrderMsg) {
|
||
cl := http.Client{}
|
||
params := make(map[string]interface{})
|
||
params["mt_peisong_id"] = msg.MtPeisongID
|
||
params["courier_name"] = msg.CourierName
|
||
params["delivery_id"] = msg.DeliveryID
|
||
params["appkey"] = msg.AppKey
|
||
params["order_id"] = msg.OrderID
|
||
params["courier_phone"] = msg.CourierPhone
|
||
params["status"] = msg.Status
|
||
params["timestamp"] = msg.Timestamp
|
||
params["cancel_reason_id"] = msg.CancelReasonId
|
||
params["cancel_reason"] = msg.CancelReason
|
||
urls := utils.Map2URLValues(params)
|
||
sign := signParams(urls)
|
||
params["sign"] = sign
|
||
globals.SugarLogger.Debugf("pushToGy", utils.Format4Output(msg, false))
|
||
request, err := http.NewRequest(http.MethodPost, "http://callback-jxgy.jxc4.com/mtps/status", strings.NewReader(utils.Map2URLValues(params).Encode()))
|
||
if err != nil {
|
||
return
|
||
}
|
||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||
cl.Do(request)
|
||
}
|
||
|
||
func signParams(params url.Values) string {
|
||
keys := make([]string, 0)
|
||
for k := range params {
|
||
if k != "sign" {
|
||
keys = append(keys, k)
|
||
}
|
||
}
|
||
|
||
sort.Strings(keys)
|
||
finalStr := "b1M}9?:sTbsB[OF2gNORnN(|(iy9rB8(`7]|[wGLnbmt`evfM>E:A90DjHAW:UPE"
|
||
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 (c *DeliveryHandler) callbackMsg2Waybill(msg *mtpsapi.CallbackOrderMsg) (retVal *model.Waybill) {
|
||
retVal = &model.Waybill{
|
||
VendorWaybillID: msg.MtPeisongID,
|
||
VendorWaybillID2: utils.Int64ToStr(msg.DeliveryID),
|
||
WaybillVendorID: model.VendorIDMTPS,
|
||
CourierName: msg.CourierName,
|
||
CourierMobile: msg.CourierPhone,
|
||
VendorStatus: utils.Int2Str(msg.Status),
|
||
StatusTime: utils.Timestamp2Time(msg.Timestamp),
|
||
Remark: msg.CancelReason,
|
||
}
|
||
retVal.VendorOrderID, retVal.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
|
||
return retVal
|
||
}
|
||
|
||
func (c *DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) {
|
||
db := dao.GetDB()
|
||
deliveryFeeInfo = &partner.WaybillFeeInfo{}
|
||
deliveryFeeInfo.RefDeliveryFee, deliveryFeeInfo.RefAddFee, err = delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
|
||
if err == nil {
|
||
if _, err = c.getMTPSShopID(order, db); err == nil {
|
||
deliveryFeeInfo.DeliveryFee = deliveryFeeInfo.RefDeliveryFee
|
||
}
|
||
}
|
||
return deliveryFeeInfo, err
|
||
}
|
||
|
||
// IDeliveryPlatformHandler
|
||
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, maxDeliveryFee int64) (bill *model.Waybill, err error) {
|
||
db := dao.GetDB()
|
||
if vendorOrgCode, err := dao.GetVendorOrgCode(db, model.VendorIDMTPS, "", model.VendorOrgTypeDelivery); err == nil {
|
||
if len(vendorOrgCode) > 0 {
|
||
if vendorOrgCode[0].IsOpen == model.YES {
|
||
return nil, fmt.Errorf("此平台配送已被系统关闭,暂不发配送 [%v]", vendorOrgCode[0].Comment)
|
||
}
|
||
}
|
||
} else {
|
||
return nil, err
|
||
}
|
||
deliveryFee, _, err := delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
|
||
if err == nil {
|
||
if err = delivery.CallCreateWaybillPolicy(deliveryFee, maxDeliveryFee, order, model.VendorIDMTPS); err != nil {
|
||
return nil, err
|
||
}
|
||
// 忽略坐标转换错误,即使是转换出错,也只能当成转换成功来处理,底层会有错误日志输出
|
||
lngFloat, latFloat, _ := jxutils.IntCoordinate2MarsStandard(order.ConsigneeLng, order.ConsigneeLat, order.CoordinateType)
|
||
billParams := &mtpsapi.CreateOrderByShopParam{
|
||
OrderID: jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID),
|
||
DeliveryServiceCode: mtpsapi.DeliveryServiceCodeRapid,
|
||
ReceiverName: utils.FilterMb4(order.ConsigneeName),
|
||
ReceiverAddress: utils.FilterMb4(order.ConsigneeAddress),
|
||
ReceiverPhone: order.ConsigneeMobile,
|
||
CoordinateType: model.CoordinateTypeMars,
|
||
ReceiverLng: jxutils.StandardCoordinate2Int(lngFloat),
|
||
ReceiverLat: jxutils.StandardCoordinate2Int(latFloat),
|
||
GoodsValue: jxutils.IntPrice2Standard(order.ActualPayPrice), // todo 超价处理
|
||
GoodsWeight: float64(jxutils.IntWeight2Float(limitOrderWeight(order.Weight))),
|
||
// ExpectedDeliveryTime: order.ExpectedDeliveredTime.Unix(),
|
||
OrderType: mtpsapi.OrderTypeASAP,
|
||
}
|
||
if billParams.DeliveryID, err = c.getDeliveryID(order, db); err == nil {
|
||
if billParams.ShopID, err = c.getMTPSShopID(order, db); err == nil {
|
||
globals.SugarLogger.Debug(billParams.ShopID)
|
||
goods := &mtpsapi.GoodsDetail{
|
||
Goods: []*mtpsapi.GoodsItem{},
|
||
}
|
||
goodItemMap := map[string]*mtpsapi.GoodsItem{}
|
||
for _, sku := range order.Skus {
|
||
goodItem := &mtpsapi.GoodsItem{
|
||
GoodCount: sku.Count,
|
||
GoodPrice: jxutils.IntPrice2Standard(sku.SalePrice),
|
||
}
|
||
goodItem.GoodName, goodItem.GoodUnit = jxutils.GetNameAndUnitFromSkuName(sku.SkuName)
|
||
// 好像SKU名不能重复,否则会报错,尝试处理一下
|
||
if item, ok := goodItemMap[goodItem.GoodName]; !ok {
|
||
goods.Goods = append(goods.Goods, goodItem)
|
||
goodItemMap[goodItem.GoodName] = goodItem
|
||
} else {
|
||
item.GoodCount += goodItem.GoodCount
|
||
}
|
||
}
|
||
// addParams := map[string]interface{}{
|
||
// "note": utils.FilterMb4(order.BuyerComment),
|
||
// "goods_detail": string(utils.MustMarshal(goods)),
|
||
// "goods_pickup_info": fmt.Sprintf("%s第%d号单", model.VendorChineseNames[order.VendorID], order.OrderSeq),
|
||
// "poi_seq": fmt.Sprintf("#%d", order.OrderSeq),
|
||
// }
|
||
// storeTel := ""
|
||
// storeID := jxutils.GetSaleStoreIDFromOrder(order)
|
||
// storeDeatail, _ := dao.GetStoreDetail(db, storeID, order.VendorID)
|
||
// if storeDeatail.Tel2 != "" {
|
||
// storeTel = ",门店电话:" + storeDeatail.Tel2
|
||
// }
|
||
billParams.Note = utils.FilterMb4("客户电话:" + order.ConsigneeMobile + "," + order.BuyerComment + ",取货失败或配送遇到问题请联系18048531223,禁止未配送直接完成定单!")
|
||
billParams.GoodsDetail = string(utils.MustMarshal(goods))
|
||
billParams.GoodsPickupInfo = fmt.Sprintf("%s第%d号单", model.VendorChineseNames[order.VendorID], order.OrderSeq)
|
||
billParams.PoiSeq = fmt.Sprintf("#%d", order.OrderSeq)
|
||
if globals.EnableStoreWrite {
|
||
result, err2 := api.MtpsAPI.CreateOrderByShop2(billParams)
|
||
if err = err2; err == nil {
|
||
bill = &model.Waybill{
|
||
VendorOrderID: order.VendorOrderID,
|
||
OrderVendorID: order.VendorID,
|
||
VendorWaybillID: result.MtPeisongID,
|
||
VendorWaybillID2: utils.Int64ToStr(result.DeliveryID),
|
||
WaybillVendorID: model.VendorIDMTPS,
|
||
DesiredFee: deliveryFee,
|
||
}
|
||
delivery.OnWaybillCreated(bill)
|
||
} else {
|
||
globals.SugarLogger.Debugf("CreateWaybill failed, orderID:%s, billParams:%v, error:%v", order.VendorOrderID, billParams, err)
|
||
}
|
||
} else {
|
||
err = fmt.Errorf("测试环境不能真正创建运单")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return bill, err
|
||
}
|
||
|
||
func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
|
||
// switch cancelReasonID {
|
||
// case partner.CancelWaybillReasonNotAcceptIntime:
|
||
// cancelReasonID = mtpsapi.CancelReasonRideerMtpsOther
|
||
// case partner.CancelWaybillReasonSwitch2SelfFailed:
|
||
// cancelReasonID = mtpsapi.CancelReasonMerchantOther
|
||
// default:
|
||
// cancelReasonID = mtpsapi.CancelReasonRideerOther
|
||
// }
|
||
cancelReasonID = mtpsapi.CancelReasonMerchantOther
|
||
cancelReason = ""
|
||
_, err = api.MtpsAPI.CancelOrder(utils.Str2Int64(bill.VendorWaybillID2), bill.VendorWaybillID, cancelReasonID, cancelReason)
|
||
return nil
|
||
}
|
||
|
||
func (c *DeliveryHandler) getDeliveryID(order *model.GoodsOrder, db *dao.DaoDB) (retVal int64, err error) {
|
||
// jxorder表当前已经有50多万条记录了,加100万避免冲突
|
||
// 508505
|
||
return order.ID + 1000000, nil
|
||
}
|
||
|
||
func (c *DeliveryHandler) getMTPSShopID(order *model.GoodsOrder, db *dao.DaoDB) (retVal string, err error) {
|
||
saleStoreID := jxutils.GetSaleStoreIDFromOrder(order)
|
||
storeCourierList, err2 := dao.GetOpenedStoreCouriersByStoreID(db, saleStoreID, model.VendorIDMTPS)
|
||
if err = err2; err != nil && err != orm.ErrNoRows {
|
||
return "", err
|
||
}
|
||
if len(storeCourierList) == 0 {
|
||
return "", partner.ErrStoreHaveNoCourier
|
||
}
|
||
retVal = storeCourierList[0].VendorStoreID
|
||
if beego.BConfig.RunMode == "dev" {
|
||
retVal = "test_0001"
|
||
}
|
||
return retVal, nil
|
||
}
|
||
|
||
func limitOrderWeight(weight int) int {
|
||
if weight > maxOrderWeight {
|
||
return maxOrderWeight
|
||
}
|
||
return weight
|
||
}
|
||
|
||
func (c *DeliveryHandler) ComplaintRider(bill *model.Waybill, resonID int, resonContent string) (err error) {
|
||
if globals.EnableStoreWrite {
|
||
err = api.MtpsAPI.EvaluateRider(utils.Str2Int64(bill.VendorWaybillID2), bill.VendorWaybillID, 1, resonContent)
|
||
}
|
||
return err
|
||
}
|
||
|
||
func (c *DeliveryHandler) GetRidderPosition(ctx *jxcontext.Context, vendorOrgCode, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (lng, lat float64, err error) {
|
||
intLng, intLat, err := api.MtpsAPI.RiderLocation(utils.Str2Int64(vendorWaybillID2), vendorWaybillID)
|
||
if err == nil {
|
||
lng = jxutils.IntCoordinate2Standard(intLng)
|
||
lat = jxutils.IntCoordinate2Standard(intLat)
|
||
}
|
||
return lng, lat, err
|
||
}
|