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" ) 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, vendorStoreID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) { result, err := getAPI(vendorOrgCode, 0, vendorStoreID).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, vendorStoreID string) (order *model.GoodsOrder, err error) { order, _, err = p.getOrder(vendorOrgCode, vendorOrderID, vendorStoreID) return order, err } func (p *PurchaseHandler) GetOrderStatus(vendorOrgCode, vendorOrderID string) (status int, err error) { if order, _ := partner.CurOrderManager.LoadOrder(vendorOrderID, model.VendorIDMTWM); order != nil { status, err = getAPI(vendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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]), } if result["order_completed_time"] != nil { order.OrderFinishedAt = getTimeFromTimestamp(utils.MustInterface2Int64(result["order_completed_time"])) } else { order.OrderFinishedAt = utils.DefaultTimeValue } 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 || logisticsCode == mtwmapi.PeiSongTypeMTZSPT { 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{}) multiSkuMap := make(map[int]int) 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.VendorSkuID == "" { if !strings.Contains(product["app_food_code"].(string), "mtcode") { sku.VendorSkuID = product["app_food_code"].(string) } else { sku.VendorSkuID = utils.Int64ToStr(utils.Interface2Int64WithDefault(product["mt_sku_id"], 0)) } } 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 { if sku.SalePrice-jxutils.StandardPrice2Int(v.MtCharge+v.PoiCharge) < 0 { continue } else { ignoreSkuMap[sku.SkuID] = 1 sku.SalePrice -= jxutils.StandardPrice2Int(v.MtCharge + v.PoiCharge) } sku.StoreSubName = utils.Int2Str(v.Type) } } } if sku.SalePrice < 0 { sku.SalePrice = jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])) } // if product["isGift"].(bool) { // sku.SkuType = 1 // } order.Skus = append(order.Skus, sku) multiSkuMap[sku.SkuID]++ } for _, v := range order.Skus { if multiSkuMap[v.SkuID] > 1 && v.SalePrice == v.VendorPrice { v.IsVendorAct = model.YES } } jxutils.RefreshOrderSkuRelated(order) return order } func getRefundSkuDetailList(msg *mtwmapi.CallbackMsg, order *model.GoodsOrder) (skuList []*mtwmapi.RefundSkuDetail, err error) { if false { skuList = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetRefundSkuDetailFromMsg(msg) } else { refundOrderDetailList, err2 := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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), GetVendorStoreIDFromMsg(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 { var order *model.GoodsOrder if order, err = partner.CurOrderManager.LoadOrder(GetOrderIDFromMsg(msg), model.VendorIDMTWM); err == nil { // if order, err = c.GetOrder(msg.AppID, GetOrderIDFromMsg(msg)); err == nil { if status.Status == model.OrderStatusAdjust { skuList, err2 := getRefundSkuDetailList(msg, order) 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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").PreparationMealComplete(utils.Str2Int64(status.VendorOrderID)) } }) } err = partner.CurOrderManager.OnOrderStatusChanged(msg.AppID, status) if err == nil && msg.Cmd == mtwmapi.MsgTypeOrderFinished { utils.CallFuncAsync(func() { orderMap, err := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 { //if order, _ := partner.CurOrderManager.LoadOrder(orderID, model.VendorIDMTWM); order != nil { // getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderRefundAgree(utils.Str2Int64(orderID), "自动确认退款") //} // goods, err := dao.GetSimpleOrder(dao.GetDB(), orderID) // if err == nil { // if goods.Status < model.OrderStatusDelivering { // } else { // api.MtwmAPI.OrderRefundReject(utils.Str2Int64(orderID), "商品配送中,请联系门店。") // todo 京东与饿百都没有售前用户提出订单调整的,自动拒绝调整单 // } // } } } 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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderRefundAgree(utils.Str2Int64(order.VendorOrderID), reason) } else { err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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 = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").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, vendorOrgCode) 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 := getAPI(vendorOrgCode, 0, vendorStoreID).GetOrderIdByDaySeqSingle(vendorStoreID, queryDate, seqStart); err2 == nil { tmpOrderIDs = []int64{vendorOderID} } } else { tmpOrderIDs, err = getAPI(vendorOrgCode, 0, vendorStoreID).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 { store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") result, err2 := getAPI(store.VendorOrgCode, storeID, "").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 { store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") result, err2 := getAPI(store.VendorOrgCode, 0, "").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 := getAPI(vendorOrgCode, 0, vendorStoreID).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 = getAPI(vendorOrgCode, 0, vendorStoreID).OrderModityTips(vendorOrderID, vendorStoreID, jxutils.IntPrice2Standard(tipFee)) } return err } func (c *PurchaseHandler) GetSelfTakeCode(ctx *jxcontext.Context, order *model.GoodsOrder) (selfTakeCode string, err error) { return selfTakeCode, err } func (c *PurchaseHandler) ConfirmSelfTake(ctx *jxcontext.Context, order *model.GoodsOrder, selfTakeCode string) (err error) { return err } func (c *PurchaseHandler) ComplaintRider(vendorOrderId string, resonID int, resonContent string) (err error) { return err }