774 lines
30 KiB
Go
774 lines
30 KiB
Go
package mtwm
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"math"
|
||
"net/url"
|
||
"regexp"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
|
||
"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/jxutils/tasksch"
|
||
"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/globals"
|
||
"git.rosy.net.cn/jx-callback/globals/api"
|
||
)
|
||
|
||
const (
|
||
FakeMsgType = "fakeMsgType"
|
||
|
||
fakeFinishedPickup = "fake_finished_pickup"
|
||
fakeUserApplyCancel = "fake_user_apply_cancel"
|
||
fakeMerchantAgreeApplyCancel = "fake_merchant_agree_apply_cancel"
|
||
fakeRefuseUserApplyCancel = "fake_refuse_user_apply_cancel"
|
||
fakeUserUndoApplyCancel = "fake_user_undo_apply_cancel"
|
||
fakeOrderAdjustFinished = "fake_order_adjust_finished"
|
||
|
||
keyVendorOrgCode = "vendorOrgCode"
|
||
)
|
||
|
||
const (
|
||
SelfDeliveryCarrierNo = 1 // 美团配送方式:0-美团专送,1-商家自送
|
||
)
|
||
|
||
const (
|
||
// pickupOrderDelay = 260 * time.Second
|
||
// pickupOrderDelay = 1 * time.Second
|
||
|
||
// callDeliveryDelay = 10 * time.Minute
|
||
// callDeliveryDelayGap = 30
|
||
)
|
||
|
||
var (
|
||
specPat = regexp.MustCompile(`(\d+)(.+)`)
|
||
)
|
||
|
||
var (
|
||
VendorStatus2StatusMap = map[string]int{
|
||
mtwmapi.OrderStatusUserCommitted: model.OrderStatusUnknown,
|
||
mtwmapi.OrderStatusNew: model.OrderStatusNew,
|
||
// mtwmapi.OrderStatusReceived: model.OrderStatusAccepted,
|
||
// mtwmapi.OrderStatusAccepted: model.OrderStatusFinishedPickup,
|
||
mtwmapi.OrderStatusAccepted: model.OrderStatusAccepted,
|
||
|
||
mtwmapi.OrderStatusDelivering: model.OrderStatusDelivering,
|
||
mtwmapi.OrderStatusDelivered: model.OrderStatusUnknown, // 以mtwmapi.OrderStatusFinished为结束状态,这个当成一个中间状态(且很少看到这个状态)
|
||
mtwmapi.OrderStatusFinished: model.OrderStatusFinished,
|
||
mtwmapi.OrderStatusCanceled: model.OrderStatusCanceled,
|
||
|
||
fakeFinishedPickup: model.OrderStatusFinishedPickup,
|
||
fakeOrderAdjustFinished: model.OrderStatusAdjust,
|
||
fakeRefuseUserApplyCancel: model.OrderStatusVendorRejectCancel,
|
||
fakeUserApplyCancel: model.OrderStatusApplyCancel,
|
||
fakeUserUndoApplyCancel: model.OrderStatusUndoApplyCancel,
|
||
fakeMerchantAgreeApplyCancel: model.OrderStatusCanceled,
|
||
}
|
||
|
||
skuActTypeMap = map[int]int{
|
||
mtwmapi.ExtrasPromotionTypeTeJiaCai: 1,
|
||
mtwmapi.ExtrasPromotionTypeZheKouCai: 1,
|
||
mtwmapi.ExtrasPromotionTypeSecondHalfPrice: 1,
|
||
}
|
||
)
|
||
|
||
func (p *PurchaseHandler) getStatusFromVendorStatus(vendorStatus string) int {
|
||
if status, ok := VendorStatus2StatusMap[vendorStatus]; ok {
|
||
return status
|
||
}
|
||
return model.OrderStatusUnknown
|
||
}
|
||
|
||
func (p *PurchaseHandler) getOrder(vendorOrgCode, vendorOrderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) {
|
||
result, err := api.MtwmAPI.OrderGetOrderDetail(utils.Str2Int64(vendorOrderID), true)
|
||
if err == nil {
|
||
result[keyVendorOrgCode] = vendorOrgCode
|
||
order = p.Map2Order(result)
|
||
}
|
||
return order, result, err
|
||
}
|
||
|
||
func (p *PurchaseHandler) GetOrder(vendorOrgCode, vendorOrderID string) (order *model.GoodsOrder, err error) {
|
||
order, _, err = p.getOrder(vendorOrgCode, vendorOrderID)
|
||
return order, err
|
||
}
|
||
|
||
func (p *PurchaseHandler) GetOrderStatus(vendorOrgCode, vendorOrderID string) (status int, err error) {
|
||
status, err = api.MtwmAPI.OrderViewStatus(utils.Str2Int64(vendorOrderID))
|
||
if err == nil {
|
||
status = p.getStatusFromVendorStatus(utils.Int2Str(status))
|
||
}
|
||
return status, 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"])),
|
||
OrderCreatedAt: getTimeFromTimestamp(utils.MustInterface2Int64(result["ctime"])),
|
||
// OrderFinishedAt: getTimeFromTimestamp(utils.MustInterface2Int64(result["order_completed_time"])),
|
||
OriginalData: string(utils.MustMarshal(result)),
|
||
ActualPayPrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(result["total"])),
|
||
BaseFreightMoney: jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["shipping_fee"], 0)),
|
||
|
||
InvoiceTitle: utils.Interface2String(result["invoice_title"]),
|
||
InvoiceTaxerID: utils.Interface2String(result["taxpayer_id"]),
|
||
InvoiceEmail: jxutils.GetOneEmailFromStr(utils.Interface2String(result["caution"])),
|
||
|
||
VendorOrgCode: utils.Interface2String(result[keyVendorOrgCode]),
|
||
}
|
||
pickType := int(utils.Interface2Int64WithDefault(result["pick_type"], 0))
|
||
if pickType == mtwmapi.OrderPickTypeSelf {
|
||
order.DeliveryType = model.OrderDeliveryTypeSelfTake
|
||
} else {
|
||
logisticsCode := utils.Interface2String(result["logistics_code"])
|
||
if logisticsCode == mtwmapi.PeiSongTypeSelf {
|
||
order.DeliveryType = model.OrderDeliveryTypeStoreSelf
|
||
} else {
|
||
order.DeliveryType = model.OrderDeliveryTypePlatform
|
||
}
|
||
}
|
||
openUID := utils.Interface2Int64WithDefault(result["openUid"], 0)
|
||
if openUID > 0 {
|
||
order.VendorUserID = utils.Int64ToStr(openUID)
|
||
}
|
||
// 不设置最晚拣货时间,以缺省值为准
|
||
// 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))
|
||
}
|
||
|
||
// 添加需要赠送的东西
|
||
if result["extras"] != nil {
|
||
var extraList []*mtwmapi.OrderExtraInfo
|
||
if err := utils.UnmarshalUseNumber([]byte(result["extras"].(string)), &extraList); err != nil {
|
||
panic(fmt.Sprintf("mtwm Map2Order vendorID:%s failed with error:%v", vendorOrderID, err))
|
||
}
|
||
for _, extra := range extraList {
|
||
order.DiscountMoney += jxutils.StandardPrice2Int(extra.ReduceFee)
|
||
if extra.Type == mtwmapi.ExtrasPromotionTypeTaoCanZeng || extra.Type == mtwmapi.ExtrasPromotionTypeManZeng {
|
||
sku := &model.OrderSku{
|
||
VendorOrderID: order.VendorOrderID,
|
||
VendorID: model.VendorIDMTWM,
|
||
Count: 1,
|
||
SkuID: 0,
|
||
VendorSkuID: "",
|
||
SkuName: extra.Remark,
|
||
Weight: 0,
|
||
SalePrice: 0,
|
||
StoreSubName: utils.Int2Str(extra.Type),
|
||
}
|
||
order.Skus = append(order.Skus, sku)
|
||
}
|
||
}
|
||
}
|
||
|
||
if poiReceiveDetailStr := utils.Interface2String(result["poi_receive_detail"]); poiReceiveDetailStr != "" {
|
||
var poiReceiveDetail *mtwmapi.PoiReceiveDetailInfo
|
||
utils.UnmarshalUseNumber([]byte(poiReceiveDetailStr), &poiReceiveDetail)
|
||
if poiReceiveDetail != nil {
|
||
order.TotalShopMoney = poiReceiveDetail.WmPoiReceiveCent
|
||
for _, v := range poiReceiveDetail.ActOrderChargeByMt {
|
||
order.PmSubsidyMoney += v.MoneyCent
|
||
}
|
||
}
|
||
}
|
||
|
||
var skuBenefitDetailMap map[string]*mtwmapi.SkuBenefitDetailInfo
|
||
if skuBenefitDetai := utils.Interface2String(result["sku_benefit_detail"]); skuBenefitDetai != "" {
|
||
skuBenefitDetailMap = make(map[string]*mtwmapi.SkuBenefitDetailInfo)
|
||
var skuBenefitDetailList []*mtwmapi.SkuBenefitDetailInfo
|
||
utils.UnmarshalUseNumber([]byte(skuBenefitDetai), &skuBenefitDetailList)
|
||
for _, v := range skuBenefitDetailList {
|
||
skuBenefitDetailMap[v.SkuID] = v
|
||
}
|
||
}
|
||
ignoreSkuMap := make(map[int]int)
|
||
// detail := result["detail"].([]interface{})
|
||
for _, product := range detail {
|
||
// product := product2.(map[string]interface{})
|
||
skuName := product["food_name"].(string)
|
||
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: getSkuWeight(product),
|
||
VendorPrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])),
|
||
SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])),
|
||
}
|
||
if sku.Weight == 0 {
|
||
sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值
|
||
}
|
||
if skuBenefitDetailMap != nil && skuBenefitDetailMap[sku.VendorSkuID] != nil && ignoreSkuMap[sku.SkuID] == 0 /* && sku.Count == 1 */ {
|
||
for _, v := range skuBenefitDetailMap[sku.VendorSkuID].WmAppOrderActDetails {
|
||
if /*skuActTypeMap[v.Type] == 1 && */ strings.Index(v.Remark, skuName) >= 0 && sku.Count == v.Count {
|
||
ignoreSkuMap[sku.SkuID] = 1
|
||
sku.SalePrice -= jxutils.StandardPrice2Int(v.MtCharge + v.PoiCharge)
|
||
sku.StoreSubName = utils.Int2Str(v.Type)
|
||
}
|
||
}
|
||
}
|
||
|
||
// if product["isGift"].(bool) {
|
||
// sku.SkuType = 1
|
||
// }
|
||
order.Skus = append(order.Skus, sku)
|
||
}
|
||
|
||
jxutils.RefreshOrderSkuRelated(order)
|
||
return order
|
||
}
|
||
|
||
func getRefundSkuDetailList(msg *mtwmapi.CallbackMsg) (skuList []*mtwmapi.RefundSkuDetail, err error) {
|
||
if false {
|
||
skuList = api.MtwmAPI.GetRefundSkuDetailFromMsg(msg)
|
||
} else {
|
||
refundOrderDetailList, err2 := api.MtwmAPI.GetOrderRefundDetail(utils.Str2Int64(GetOrderIDFromMsg(msg)), mtwmapi.RefundTypePart)
|
||
if err = err2; err == nil {
|
||
for _, v := range refundOrderDetailList {
|
||
skuList = append(skuList, v.WmAppRetailForOrderPartRefundList...)
|
||
}
|
||
}
|
||
}
|
||
globals.SugarLogger.Debugf("getRefundSkuDetailList orderID:%s skuList:%s", GetOrderIDFromMsg(msg), utils.Format4Output(skuList, true))
|
||
return skuList, err
|
||
}
|
||
|
||
func getSkuWeight(product map[string]interface{}) (weight int) {
|
||
if weight = int(utils.Interface2Int64WithDefault(product["weight"], 0)); weight == 0 {
|
||
searchResult := specPat.FindStringSubmatch(product["spec"].(string))
|
||
if len(searchResult) == 3 {
|
||
weight = jxutils.FormatSkuWeight(float32(utils.Str2Float64WithDefault(searchResult[1], 0)), utils.TrimBlankChar(searchResult[2]))
|
||
}
|
||
if weight == 0 {
|
||
_, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(product["food_name"].(string))
|
||
weight = jxutils.FormatSkuWeight(specQuality, specUnit)
|
||
}
|
||
}
|
||
return weight
|
||
}
|
||
|
||
func (c *PurchaseHandler) onOrderMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) {
|
||
var err error
|
||
if c.isAfsMsg(msg) {
|
||
response = c.OnAfsOrderMsg(msg)
|
||
} else {
|
||
status := c.callbackMsg2Status(msg)
|
||
if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 {
|
||
return nil
|
||
}
|
||
if msg.Cmd == mtwmapi.MsgTypeNewOrder {
|
||
order, orderMap, err2 := c.getOrder(msg.AppID, GetOrderIDFromMsg(msg))
|
||
if err = err2; err == nil {
|
||
err = partner.CurOrderManager.OnOrderNew(order, c.callbackMsg2Status(msg))
|
||
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 != nil {
|
||
if status.Status == model.OrderStatusAdjust {
|
||
var order *model.GoodsOrder
|
||
if order, err = c.GetOrder(msg.AppID, GetOrderIDFromMsg(msg)); err == nil {
|
||
skuList, err2 := getRefundSkuDetailList(msg)
|
||
if err = err2; err == nil {
|
||
var removedSkuList []*model.OrderSku
|
||
for _, mtwmSku := range skuList {
|
||
order.ActualPayPrice -= jxutils.StandardPrice2Int(mtwmSku.RefundPrice) * int64(mtwmSku.Count)
|
||
removedSkuList = append(removedSkuList, &model.OrderSku{
|
||
SkuID: int(utils.Str2Int64WithDefault(mtwmSku.SkuID, 0)),
|
||
Count: mtwmSku.Count,
|
||
})
|
||
}
|
||
order = jxutils.RemoveSkuFromOrder(order, removedSkuList)
|
||
jxutils.RefreshOrderSkuRelated(order)
|
||
err = partner.CurOrderManager.OnOrderAdjust(order, status)
|
||
}
|
||
}
|
||
} else {
|
||
if status.Status == model.OrderStatusDelivering {
|
||
// 美团订单即使时在配送状态时,如果之前没有调用过拣货完成,也会对门店指标生成影响,这里强制再调用拣货完成,且忽略错误
|
||
utils.CallFuncAsync(func() {
|
||
if globals.EnableMtwmStoreWrite {
|
||
err = api.MtwmAPI.PreparationMealComplete(utils.Str2Int64(status.VendorOrderID))
|
||
}
|
||
})
|
||
}
|
||
err = partner.CurOrderManager.OnOrderStatusChanged(msg.AppID, 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 := msg.Cmd
|
||
remark := ""
|
||
statusTime := utils.Str2Int64(msg.FormData.Get("timestamp"))
|
||
switch msg.Cmd {
|
||
case mtwmapi.MsgTypeUserUrgeOrder, mtwmapi.MsgTypeOrderModified, mtwmapi.MsgTypeOrderFinancial:
|
||
vendorStatus = msg.Cmd
|
||
case mtwmapi.MsgTypeOrderCanceled:
|
||
vendorStatus = mtwmapi.OrderStatusCanceled
|
||
remark = msg.FormData.Get("reason")
|
||
case FakeMsgType, mtwmapi.MsgTypeNewOrder, mtwmapi.MsgTypeOrderAccepted, mtwmapi.MsgTypeOrderFinished:
|
||
vendorStatus = msg.FormData.Get("status")
|
||
statusTime = utils.Str2Int64(msg.FormData.Get("utime"))
|
||
case mtwmapi.MsgTypeOrderRefund, mtwmapi.MsgTypeOrderPartialRefund:
|
||
notifyType := msg.FormData.Get("notify_type")
|
||
vendorStatus = msg.Cmd + "-" + notifyType
|
||
if true { // 已经提前判断了,到这里的都是售中
|
||
remark = msg.FormData.Get("reason")
|
||
if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund {
|
||
if notifyType == mtwmapi.NotifyTypePartyApply {
|
||
if globals.EnableMtwmStoreWrite {
|
||
// api.MtwmAPI.OrderRefundReject(utils.Str2Int64(orderID), "请联系商户,让商户发起订单调整") // todo 京东与饿百都没有售前用户提出订单调整的,自动拒绝调整单
|
||
api.MtwmAPI.OrderRefundAgree(utils.Str2Int64(orderID), "自动确认退款")
|
||
}
|
||
} else if notifyType == mtwmapi.NotifyTypeSuccess {
|
||
vendorStatus = fakeOrderAdjustFinished
|
||
}
|
||
} else if msg.Cmd == mtwmapi.MsgTypeOrderRefund {
|
||
if notifyType == mtwmapi.NotifyTypeApply {
|
||
vendorStatus = fakeUserApplyCancel
|
||
} else if notifyType == mtwmapi.NotifyTypeCancelRefund || notifyType == mtwmapi.NotifyTypeCancelRefundComplaint {
|
||
vendorStatus = fakeUserUndoApplyCancel
|
||
} else if notifyType == mtwmapi.NotifyTypeReject {
|
||
vendorStatus = fakeRefuseUserApplyCancel
|
||
} else if notifyType == mtwmapi.NotifyTypeSuccess {
|
||
vendorStatus = fakeMerchantAgreeApplyCancel // todo 可能导致订单取消消息重复
|
||
}
|
||
}
|
||
}
|
||
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,
|
||
FormData: make(url.Values),
|
||
}
|
||
timeStr := utils.Int64ToStr(time.Now().Unix())
|
||
msg.FormData.Set(mtwmapi.KeyOrderID, vendorOrderID)
|
||
msg.FormData.Set("status", vendorStatus)
|
||
msg.FormData.Set("timestamp", timeStr)
|
||
msg.FormData.Set("utime", timeStr)
|
||
utils.CallFuncAsync(func() {
|
||
c.onOrderMsg(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))
|
||
err = api.MtwmAPI.OrderConfirm(utils.Str2Int64(order.VendorOrderID))
|
||
if err != nil {
|
||
if utils.IsErrMatch(err, utils.Int2Str(mtwmapi.ErrCodeOpFailed), []string{
|
||
"订单已经确认过了",
|
||
}) {
|
||
err = nil
|
||
} else {
|
||
globals.SugarLogger.Warnf("mtwm AcceptOrRefuseOrder orderID:%s failed with err:%v", order.VendorOrderID, err)
|
||
}
|
||
}
|
||
}
|
||
// if err == nil {
|
||
// c.postFakeMsg(order.VendorOrderID, FakeMsgType, mtwmapi.OrderStatusReceived)
|
||
// }
|
||
} else {
|
||
if globals.EnableMtwmStoreWrite {
|
||
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 !isSelfDelivery {
|
||
if globals.EnableMtwmStoreWrite {
|
||
// err = api.MtwmAPI.OrderConfirm(utils.Str2Int64(order.VendorOrderID))
|
||
err = api.MtwmAPI.PreparationMealComplete(utils.Str2Int64(order.VendorOrderID))
|
||
}
|
||
}
|
||
if err == nil {
|
||
c.postFakeMsg(order.VendorOrderID, FakeMsgType, fakeFinishedPickup)
|
||
}
|
||
return err
|
||
}
|
||
|
||
func (p *PurchaseHandler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool) (err error) {
|
||
return err
|
||
}
|
||
|
||
func (p *PurchaseHandler) CallCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 拣货失败后再次招唤平台配送
|
||
return err
|
||
}
|
||
|
||
func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 投递失败后确认收到退货
|
||
return err
|
||
}
|
||
|
||
// 美团预定单不能转商家自送
|
||
func (c *PurchaseHandler) CanSwitch2SelfDeliver(order *model.GoodsOrder) (isCan bool, err error) {
|
||
return order.BusinessType != model.BusinessTypeDingshida, nil
|
||
}
|
||
|
||
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 {
|
||
// 您好,之前的答复已经更正为,调用变更配送状态的接口,会校验门店的配送类型。美团配送的门店即便转自配后因门店配送类型是美团配送所以无法调用接口变更配送状态。可提醒顾客点击确认收货。谢谢
|
||
// 非自配送门店订单调用OrderArrived好像会报错:{"data":"ng","error":{"code":1038,"msg":"只允许商家配送调用该接口"}}
|
||
// 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(而不是空,0),1538103149不是特殊值,只是一个任意之前的时间,这样写可以处理
|
||
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) 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) {
|
||
// 美团外卖必须要确认订单后才能调整单
|
||
if order.Status < model.OrderStatusFinishedPickup {
|
||
err = c.PickupGoods(order, false, ctx.GetUserName())
|
||
}
|
||
if err == nil {
|
||
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 (c *PurchaseHandler) ListOrders(ctx *jxcontext.Context, vendorOrgCode string, parentTask tasksch.ITask, queryDate time.Time, vendorStoreID string) (vendorOrderIDs []string, err error) {
|
||
if utils.IsTimeZero(queryDate) {
|
||
return nil, fmt.Errorf("queryDate必须指定")
|
||
}
|
||
queryDate = utils.Time2Date(queryDate)
|
||
|
||
var vendorStoreIDs []string
|
||
if vendorStoreID == "" {
|
||
vendorStoreIDs, err = c.GetAllStoresVendorID(ctx, "")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
} else {
|
||
vendorStoreIDs = []string{vendorStoreID}
|
||
}
|
||
task := tasksch.NewParallelTask("mtwm ListOrders", nil, ctx,
|
||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||
vendorStoreID := batchItemList[0].(string)
|
||
var orderIDs []string
|
||
seqStart := 1
|
||
i := 0
|
||
for {
|
||
batchSize := int(math.Min(math.Pow(2, float64(i*3)), float64(mtwmapi.MaxGap4GetOrderIdByDaySeq)))
|
||
seqEnd := seqStart + batchSize - 1
|
||
var tmpOrderIDs []int64
|
||
if seqStart == seqEnd {
|
||
if vendorOderID, err2 := api.MtwmAPI.GetOrderIdByDaySeqSingle(vendorStoreID, queryDate, seqStart); err2 == nil {
|
||
tmpOrderIDs = []int64{vendorOderID}
|
||
}
|
||
} else {
|
||
tmpOrderIDs, err = api.MtwmAPI.GetOrderIdByDaySeq(vendorStoreID, queryDate, seqStart, seqEnd)
|
||
}
|
||
if len(tmpOrderIDs) > 0 {
|
||
for _, v := range tmpOrderIDs {
|
||
orderIDs = append(orderIDs, utils.Int64ToStr(v))
|
||
}
|
||
}
|
||
if err != nil || len(tmpOrderIDs) < batchSize {
|
||
err = nil
|
||
break
|
||
}
|
||
seqStart = seqEnd + 1
|
||
i++
|
||
}
|
||
retVal = orderIDs
|
||
return retVal, nil
|
||
}, vendorStoreIDs)
|
||
tasksch.HandleTask(task, parentTask, true).Run()
|
||
orderList, err := task.GetResult(0)
|
||
if err == nil && len(orderList) > 0 {
|
||
vendorOrderIDs = make([]string, len(orderList))
|
||
for k, v := range orderList {
|
||
vendorOrderIDs[k] = v.(string)
|
||
}
|
||
}
|
||
return vendorOrderIDs, err
|
||
}
|
||
|
||
// func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, order *model.GoodsOrder, tipFee int64) (err error) {
|
||
// if globals.EnableMtwmStoreWrite {
|
||
// err = api.MtwmAPI.OrderUpdateTip(utils.Str2Int64(order.VendorOrderID), jxutils.IntPrice2Standard(tipFee))
|
||
// }
|
||
// return err
|
||
// }
|
||
|
||
func (p *PurchaseHandler) GetOrderConsigneeNumber(ctx *jxcontext.Context, storeID int, vendorStoreID string) (numberList []*partner.OrderPhoneNumberInfo, err error) {
|
||
offset := 0
|
||
for {
|
||
result, err2 := api.MtwmAPI.OrderBatchPullPhoneNumber(vendorStoreID, offset, mtwmapi.MaxBatchPullPhoneNumberLimit)
|
||
if err = err2; err == nil {
|
||
for _, v := range result {
|
||
v2 := &partner.OrderPhoneNumberInfo{
|
||
VendorOrderID: utils.Int64ToStr(v.OrderID),
|
||
PhoneNumber: v.RealPhoneNumber,
|
||
}
|
||
if v2.PhoneNumber == "" {
|
||
v2.PhoneNumber = v.RealOrderPhoneNumber
|
||
}
|
||
numberList = append(numberList, v2)
|
||
}
|
||
if len(result) <= mtwmapi.MaxBatchPullPhoneNumberLimit {
|
||
break
|
||
}
|
||
offset += mtwmapi.MaxBatchPullPhoneNumberLimit
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
return numberList, err
|
||
}
|
||
|
||
func (p *PurchaseHandler) GetOrderCourierNumber(ctx *jxcontext.Context, storeID int, vendorStoreID string) (numberList []*partner.OrderPhoneNumberInfo, err error) {
|
||
offset := 0
|
||
for {
|
||
result, err2 := api.MtwmAPI.OrderGetRiderInfoPhoneNumber(vendorStoreID, offset, mtwmapi.MaxBatchPullPhoneNumberLimit)
|
||
if err = err2; err == nil {
|
||
for _, v := range result {
|
||
numberList = append(numberList, &partner.OrderPhoneNumberInfo{
|
||
VendorOrderID: utils.Int64ToStr(v.OrderID),
|
||
PhoneNumber: v.RiderRealPhoneNumber,
|
||
})
|
||
}
|
||
if len(result) <= mtwmapi.MaxBatchPullPhoneNumberLimit {
|
||
break
|
||
}
|
||
offset += mtwmapi.MaxBatchPullPhoneNumberLimit
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
return numberList, err
|
||
}
|
||
|
||
func (p *PurchaseHandler) onNumberDowngrade(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) {
|
||
userNumberMap := make(map[string]*partner.OrderPhoneNumberInfo)
|
||
courierNumberMap := make(map[string]*partner.OrderPhoneNumberInfo)
|
||
orderMap := make(map[string]int)
|
||
ctx := jxcontext.AdminCtx
|
||
task := tasksch.NewParallelTask("美团外卖平台处理隐私号降级通知", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
|
||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||
step := batchItemList[0].(int)
|
||
switch step {
|
||
case 0:
|
||
userNumberList, err2 := p.GetOrderConsigneeNumber(ctx, 0, "")
|
||
if err = err2; err == nil {
|
||
for _, v := range userNumberList {
|
||
userNumberMap[v.VendorOrderID] = v
|
||
orderMap[v.VendorOrderID] = 1
|
||
}
|
||
}
|
||
case 1:
|
||
courierNumberList, err2 := p.GetOrderCourierNumber(ctx, 0, "")
|
||
if err = err2; err == nil {
|
||
for _, v := range courierNumberList {
|
||
courierNumberMap[v.VendorOrderID] = v
|
||
orderMap[v.VendorOrderID] = 1
|
||
}
|
||
}
|
||
case 2:
|
||
orderList := jxutils.StringMap2List(orderMap)
|
||
if len(orderList) > 0 {
|
||
updateTask := tasksch.NewParallelTask("美团外卖平台处理隐私号降级通知/处理订单", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
|
||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||
vendorOrderID := batchItemList[0].(string)
|
||
db := dao.GetDB()
|
||
if userNumberMap[vendorOrderID] != nil {
|
||
_, err = dao.UpdateEntityByKV(db, &model.GoodsOrder{}, map[string]interface{}{
|
||
"ConsigneeMobile": userNumberMap[vendorOrderID].PhoneNumber,
|
||
"ConsigneeMobile2": userNumberMap[vendorOrderID].PhoneNumber,
|
||
}, map[string]interface{}{
|
||
model.FieldVendorOrderID: vendorOrderID,
|
||
model.FieldVendorID: model.VendorIDMTWM,
|
||
})
|
||
}
|
||
if courierNumberMap[vendorOrderID] != nil {
|
||
_, err = dao.UpdateEntityByKV(db, &model.Waybill{}, map[string]interface{}{
|
||
"CourierMobile": courierNumberMap[vendorOrderID].PhoneNumber,
|
||
}, map[string]interface{}{
|
||
"VendorWaybillID": vendorOrderID,
|
||
"WaybillVendorID": model.VendorIDMTWM,
|
||
})
|
||
}
|
||
return retVal, err
|
||
}, orderList)
|
||
tasksch.HandleTask(updateTask, task, true).Run()
|
||
_, err = updateTask.GetResult(0)
|
||
}
|
||
}
|
||
return retVal, err
|
||
}, []int{0, 1, 2})
|
||
tasksch.HandleTask(task, nil, true).Run()
|
||
return response
|
||
}
|
||
|
||
func (c *PurchaseHandler) GetWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (tipFee int64, err error) {
|
||
orderInfo, err := api.MtwmAPI.GetDistributeOrderDetail(vendorOrderID, vendorStoreID)
|
||
if err == nil {
|
||
tipFee = jxutils.StandardPrice2Int(orderInfo.TipAmount)
|
||
}
|
||
return tipFee, err
|
||
}
|
||
|
||
func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2, cityCode string, tipFee int64) (err error) {
|
||
if globals.EnableMtwmStoreWrite {
|
||
err = api.MtwmAPI.OrderModityTips(vendorOrderID, vendorStoreID, jxutils.IntPrice2Standard(tipFee))
|
||
}
|
||
return err
|
||
}
|