Files
jx-callback/business/partner/purchase/mtwm/order.go
gazebo 9f01129374 - 去掉两个没有用的状态OrderStatusFailed,OrderStatusRefund
- 准备将OrderStatusDelivered合并至OrderStatusFinished
2019-04-19 15:02:38 +08:00

382 lines
15 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 mtwm
import (
"errors"
"fmt"
"net/url"
"time"
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
"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/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
FakeMsgTypeOrderReceived = "orderReceived"
FakeMsgTypeOrderDelivering = "orderDelivering"
fakeUserApplyCancel = "fake_user_apply_cancel"
fakeOrderAdjustFinished = "fake_order_adjust_finished"
)
const (
SelfDeliveryCarrierNo = 1 // 美团配送方式0-美团专送1-商家自送
)
const (
pickupOrderDelay = 260 * time.Second
callDeliveryDelay = 10 * time.Minute
callDeliveryDelayGap = 30
)
var (
VendorStatus2StatusMap = map[string]int{
mtwmapi.OrderStatusUserCommitted: model.OrderStatusUnknown,
mtwmapi.OrderStatusNew: model.OrderStatusNew,
mtwmapi.OrderStatusReceived: model.OrderStatusAccepted,
mtwmapi.OrderStatusAccepted: model.OrderStatusFinishedPickup,
mtwmapi.OrderStatusDelivering: model.OrderStatusDelivering,
mtwmapi.OrderStatusDelivered: model.OrderStatusUnknown, // 以mtwmapi.OrderStatusFinished为结束状态这个当成一个中间状态且很少看到这个状态
mtwmapi.OrderStatusFinished: model.OrderStatusFinished,
mtwmapi.OrderStatusCanceled: model.OrderStatusCanceled,
fakeOrderAdjustFinished: model.OrderStatusAdjust,
fakeUserApplyCancel: model.OrderStatusApplyCancel,
}
)
func (p *PurchaseHandler) GetStatusFromVendorStatus(vendorStatus string) int {
if status, ok := VendorStatus2StatusMap[vendorStatus]; ok {
return status
}
return model.OrderStatusUnknown
}
func (p *PurchaseHandler) getOrder(vendorOrderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) {
result, err := api.MtwmAPI.OrderGetOrderDetail(utils.Str2Int64(vendorOrderID), true)
if err == nil {
order = p.Map2Order(result)
}
return order, result, err
}
func (p *PurchaseHandler) GetOrder(vendorOrderID string) (order *model.GoodsOrder, err error) {
order, _, err = p.getOrder(vendorOrderID)
return order, err
}
func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) {
result := orderData
vendorOrderID := utils.Int64ToStr(utils.MustInterface2Int64(result["order_id"]))
// 因为美团外卖不能自动设置商家门店号,且只能通过商家门店号来访问门店,
// 为了在后台设置简单一致把app_poi_code直接当成平台门店号使用(即在后台设置时,平台门店号与商家门店号一样)
// 订单中wm_poi_id实际来平台门店号app_poi_code为商家门店号这样一来这两个就相同了
order = &model.GoodsOrder{
VendorOrderID: vendorOrderID,
// VendorOrderID2: utils.Int64ToStr(utils.MustInterface2Int64(result["wm_order_id_view"])),
VendorID: model.VendorIDMTWM,
VendorStoreID: result["app_poi_code"].(string),
StoreID: 0,
// VendorStoreID: utils.Int64ToStr(utils.MustInterface2Int64(result["wm_poi_id"])),
// StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(result["app_poi_code"]), 0)),
StoreName: result["wm_poi_name"].(string),
ConsigneeName: result["recipient_name"].(string),
ConsigneeMobile: jxutils.FormalizeMobile(result["recipient_phone"].(string)),
ConsigneeAddress: result["recipient_address"].(string),
CoordinateType: model.CoordinateTypeMars,
BuyerComment: utils.TrimBlankChar(utils.Interface2String(result["caution"])),
ExpectedDeliveredTime: getTimeFromTimestamp(utils.Interface2Int64WithDefault(result["delivery_time"], 0)),
PickDeadline: utils.DefaultTimeValue,
VendorStatus: utils.Int64ToStr(utils.MustInterface2Int64(result["status"])),
OrderSeq: int(utils.MustInterface2Int64(result["day_seq"])),
StatusTime: getTimeFromTimestamp(utils.MustInterface2Int64(result["ctime"])),
OriginalData: string(utils.MustMarshal(result)),
ActualPayPrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(result["total"])),
Skus: []*model.OrderSku{},
}
if utils.IsTimeZero(order.PickDeadline) && !utils.IsTimeZero(order.StatusTime) {
order.PickDeadline = order.StatusTime.Add(pickupOrderDelay) // 美团外卖要求在5分钟内拣货不然订单会被取消
}
order.Status = p.GetStatusFromVendorStatus(order.VendorStatus)
if utils.IsTimeZero(order.ExpectedDeliveredTime) {
order.BusinessType = model.BusinessTypeImmediate
} else {
order.BusinessType = model.BusinessTypeDingshida
}
originalLng := utils.MustInterface2Float64(result["longitude"])
originalLat := utils.MustInterface2Float64(result["latitude"])
order.ConsigneeLng = jxutils.StandardCoordinate2Int(originalLng)
order.ConsigneeLat = jxutils.StandardCoordinate2Int(originalLat)
var detail []map[string]interface{}
if err := utils.UnmarshalUseNumber([]byte(result["detail"].(string)), &detail); err != nil {
panic(fmt.Sprintf("mtwm Map2Order vendorID:%s failed with error:%v", vendorOrderID, err))
}
// detail := result["detail"].([]interface{})
for _, product := range detail {
// product := product2.(map[string]interface{})
skuName := product["food_name"].(string)
_, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName)
skuID := utils.Interface2String(product["sku_id"])
sku := &model.OrderSku{
VendorOrderID: order.VendorOrderID,
VendorID: model.VendorIDMTWM,
Count: int(utils.MustInterface2Float64(product["quantity"])),
SkuID: int(utils.Str2Int64WithDefault(skuID, 0)),
VendorSkuID: skuID,
SkuName: skuName,
Weight: jxutils.FormatSkuWeight(specQuality, specUnit), // 订单信息里没有重量,只有名字里尝试找
SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])),
// PromotionType: int(utils.MustInterface2Int64(product["promotionType"])),
}
if sku.Weight == 0 {
sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值
}
// if product["isGift"].(bool) {
// sku.SkuType = 1
// }
order.Skus = append(order.Skus, sku)
order.SkuCount++
order.GoodsCount += sku.Count
order.SalePrice += sku.SalePrice * int64(sku.Count)
order.Weight += sku.Weight * sku.Count
}
// setOrederDetailFee(result, order)
return order
}
func (c *PurchaseHandler) onOrderMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) {
var err error
if msg.Cmd == mtwmapi.MsgTypeNewOrder {
order, orderMap, err2 := c.getOrder(GetOrderIDFromMsg(msg))
if err = err2; err == nil {
err = partner.CurOrderManager.OnOrderNew(order, order.VendorStatus)
if err == nil {
utils.CallFuncAsync(func() {
if msg.Cmd == mtwmapi.MsgTypeNewOrder {
c.OnOrderDetail(orderMap, partner.CreatedPeration)
} else {
c.OnOrderDetail(orderMap, partner.UpdatedPeration)
}
})
}
}
} else {
if status := c.callbackMsg2Status(msg); status != nil {
err = partner.CurOrderManager.OnOrderStatusChanged(status)
if err == nil && msg.Cmd == mtwmapi.MsgTypeOrderFinished {
utils.CallFuncAsync(func() {
orderMap, err := api.MtwmAPI.OrderGetOrderDetail(utils.Str2Int64(GetOrderIDFromMsg(msg)), true)
if err == nil && utils.MustInterface2Int64(orderMap["is_third_shipping"]) == SelfDeliveryCarrierNo {
c.OnOrderDetail(orderMap, partner.UpdatedPeration)
}
})
}
}
}
return mtwmapi.Err2CallbackResponse(err, "")
}
func (c *PurchaseHandler) callbackMsg2Status(msg *mtwmapi.CallbackMsg) (orderStatus *model.OrderStatus) {
orderID := GetOrderIDFromMsg(msg)
vendorStatus := ""
remark := ""
statusTime := utils.Str2Int64(msg.Data.Get("timestamp"))
switch msg.Cmd {
case mtwmapi.MsgTypeUserUrgeOrder, mtwmapi.MsgTypeOrderModified, mtwmapi.MsgTypeOrderFinancial:
vendorStatus = msg.Cmd
case mtwmapi.MsgTypeOrderCanceled:
vendorStatus = mtwmapi.OrderStatusCanceled
remark = msg.Data.Get("reason")
case mtwmapi.MsgTypeNewOrder, FakeMsgTypeOrderReceived, mtwmapi.MsgTypeOrderAccepted, FakeMsgTypeOrderDelivering, mtwmapi.MsgTypeOrderFinished:
vendorStatus = msg.Data.Get("status")
statusTime = utils.Str2Int64(msg.Data.Get("utime"))
case mtwmapi.MsgTypeOrderRefund, mtwmapi.MsgTypeOrderPartialRefund:
notifyType := msg.Data.Get("notify_type")
vendorStatus = msg.Cmd + "-" + notifyType
if !isOrderFinished(utils.Str2Int64(orderID)) {
remark = msg.Data.Get("reason")
if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund {
if notifyType == mtwmapi.NotifyTypePartyApply {
api.MtwmAPI.OrderRefundAgree(utils.Str2Int64(orderID), "bu") // todo 自动同意调整单
} else if notifyType == mtwmapi.NotifyTypeSuccess {
vendorStatus = fakeOrderAdjustFinished
}
} else if msg.Cmd == mtwmapi.MsgTypeOrderRefund {
if notifyType == mtwmapi.NotifyTypeApply {
vendorStatus = fakeUserApplyCancel
}
}
}
default:
globals.SugarLogger.Errorf("mtwm unkonw msg:%s", utils.Format4Output(msg, false))
}
if vendorStatus != "" {
orderStatus = &model.OrderStatus{
VendorOrderID: orderID,
VendorID: model.VendorIDMTWM,
OrderType: model.OrderTypeOrder,
RefVendorOrderID: orderID,
RefVendorID: model.VendorIDMTWM,
VendorStatus: vendorStatus,
Status: c.GetStatusFromVendorStatus(vendorStatus),
StatusTime: getTimeFromTimestamp(statusTime),
Remark: remark,
}
}
return orderStatus
}
func (c *PurchaseHandler) postFakeMsg(vendorOrderID, cmd, vendorStatus string) {
msg := &mtwmapi.CallbackMsg{
Cmd: cmd,
Data: make(url.Values),
}
timeStr := utils.Int64ToStr(time.Now().Unix())
msg.Data.Set(mtwmapi.KeyOrderID, vendorOrderID)
msg.Data.Set("status", vendorStatus)
msg.Data.Set("timestamp", timeStr)
msg.Data.Set("utime", timeStr)
utils.CallFuncAsync(func() {
OnOrderCallbackMsg(msg)
})
}
func (c *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) {
globals.SugarLogger.Debugf("mtwm AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt)
if isAcceptIt {
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderReceived(utils.Str2Int64(order.VendorOrderID))
}
if err == nil {
c.postFakeMsg(order.VendorOrderID, FakeMsgTypeOrderReceived, mtwmapi.OrderStatusReceived)
}
} else {
err = c.CancelOrder(jxcontext.AdminCtx, order, "bu")
}
return err
}
func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) {
globals.SugarLogger.Debugf("mtwm PickupGoods orderID:%s, isSelfDelivery:%t", order.VendorOrderID, isSelfDelivery)
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderConfirm(utils.Str2Int64(order.VendorOrderID))
} else {
c.postFakeMsg(order.VendorOrderID, mtwmapi.MsgTypeOrderAccepted, mtwmapi.OrderStatusAccepted)
}
return err
}
func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("mtwm Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderLogisticsChange2Self(utils.Str2Int64(order.VendorOrderID))
}
return err
}
func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("mtwm Swtich2SelfDelivered orderID:%s", order.VendorOrderID)
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderArrived(utils.Str2Int64(order.VendorOrderID))
}
return err
}
func (c *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("mtwm SelfDeliverDelivering orderID:%s", order.VendorOrderID)
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderDelivering(utils.Str2Int64(order.VendorOrderID))
}
return err
}
func (c *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("mtwm SelfDeliverDelivered orderID:%s", order.VendorOrderID)
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderArrived(utils.Str2Int64(order.VendorOrderID))
}
return err
}
func getTimeFromTimestamp(timeStamp int64) time.Time {
if timeStamp < 1538103149 { // 立即达订单给的是1而不是空01538103149不是特殊值只是一个任意之前的时间这样写可以处理
return utils.DefaultTimeValue
}
return utils.Timestamp2Time(timeStamp)
}
func (c *PurchaseHandler) GetOrderRealMobile(ctx *jxcontext.Context, order *model.GoodsOrder) (mobile string, err error) {
err = errors.New("美团外卖还未实现GetOrderRealMobile")
return mobile, err
}
func (c *PurchaseHandler) GetStatusActionTimeout(order *model.GoodsOrder, statusType, status int) (params *partner.StatusActionParams) {
if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted {
params = &partner.StatusActionParams{ // PickDeadline没有设置时才有效美团外卖要求在5分钟内拣货不然订单会被取消
Timeout: pickupOrderDelay,
}
} else if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusFinishedPickup {
params = &partner.StatusActionParams{ // 立即达订单有效,自配送延时召唤配送
Timeout: callDeliveryDelay,
TimeoutGap: callDeliveryDelayGap,
}
}
return params
}
func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) {
if globals.EnableMtwmStoreWrite {
if isAgree {
err = api.MtwmAPI.OrderRefundAgree(utils.Str2Int64(order.VendorOrderID), reason)
} else {
err = api.MtwmAPI.OrderRefundReject(utils.Str2Int64(order.VendorOrderID), reason)
}
}
return err
}
func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) {
if globals.EnableMtwmStoreWrite {
if err = api.MtwmAPI.OrderCancel(utils.Str2Int64(order.VendorOrderID), reason, mtwmapi.CancelReasonOther); err == nil {
// 调用开放平台接口取消订单,不推送取消订单消息和退款消息。
c.postFakeMsg(order.VendorOrderID, mtwmapi.MsgTypeOrderCanceled, mtwmapi.OrderStatusCanceled)
}
}
return err
}
func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) {
var skuList []*mtwmapi.RefundSku
for _, sku := range removedSkuList {
skuID := utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku))
skuList = append(skuList, &mtwmapi.RefundSku{
AppFoodCode: skuID,
SkuID: skuID,
Count: sku.Count,
})
}
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.OrderApplyPartRefund(utils.Str2Int64(order.VendorOrderID), reason, skuList)
}
return err
}
func isOrderFinished(vendorOrderID int64) bool {
if status, err := api.MtwmAPI.OrderViewStatus(vendorOrderID); err == nil {
strStatus := utils.Int2Str(status)
return strStatus == mtwmapi.OrderStatusDelivered ||
strStatus == mtwmapi.OrderStatusFinished ||
strStatus == mtwmapi.OrderStatusCanceled
}
return false
}