diff --git a/business/jxcallback/scheduler/basesch/basesch.go b/business/jxcallback/scheduler/basesch/basesch.go deleted file mode 100644 index e0077e04e..000000000 --- a/business/jxcallback/scheduler/basesch/basesch.go +++ /dev/null @@ -1,200 +0,0 @@ -package basesch - -import ( - "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/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" -) - -var ( - FixedBaseScheduler *BaseScheduler -) - -type BaseScheduler struct { - IsReallyCallPlatformAPI bool -} - -func (c *BaseScheduler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { - globals.SugarLogger.Infof("AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt) - if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusNew || order.Status == model.OrderStatusWaitAccepted { - if c.IsReallyCallPlatformAPI { - err = utils.CallFuncLogErrorWithInfo(func() error { - return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AcceptOrRefuseOrder(order, isAcceptIt, userName) - }, "AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt) - } - } else { - return scheduler.ErrOrderStatusAlreadySatisfyCurOperation - globals.SugarLogger.Debugf("AcceptOrRefuseOrder orderID:%s, status:%d is not suitable, isAcceptIt:%t", order.VendorOrderID, order.Status, isAcceptIt) - } - return err -} - -func (c *BaseScheduler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { - globals.SugarLogger.Infof("PickupGoods orderID:%s", order.VendorOrderID) - if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusAccepted { - if c.IsReallyCallPlatformAPI { - handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID) - err = utils.CallFuncLogErrorWithInfo(func() (err error) { - if err = handler.PickupGoods(order, isSelfDelivery, userName); err != nil { - if status, err2 := handler.GetOrderStatus(order.VendorOrgCode, order.VendorOrderID); err2 == nil && status >= model.OrderStatusFinished { - err = nil - } - } - return err - }, "PickupGoods orderID:%s", order.VendorOrderID) - } - } else { - if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusAccepted { - err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation - globals.SugarLogger.Infof("PickupGoods orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) - } else { - err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation - globals.SugarLogger.Debugf("PickupGoods orderID:%s status:%d already ok", order.VendorOrderID, order.Status) - } - } - return err -} - -func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { - globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s", order.VendorOrderID) - if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status >= model.OrderStatusFinishedPickup && order.Status <= model.OrderStatusDelivering { - if order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 && c.IsReallyCallPlatformAPI { - err = utils.CallFuncLogErrorWithInfo(func() error { - return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).Swtich2SelfDeliver(order, userName) - }, "Swtich2SelfDeliver orderID:%s", order.VendorOrderID) - } - if err == nil { // 因为有些平台转自送后,不会再发送订单在配送中消息过来,所以成功后就强制设置状态为配送中 - order.Status = model.OrderStatusDelivering - order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled - err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - } else { - if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusFinishedPickup || order.VendorID == order.WaybillVendorID { - err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation - globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) - } else { - err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation - globals.SugarLogger.Debugf("Swtich2SelfDeliver orderID:%s status:%d already ok", order.VendorOrderID, order.Status) - } - } - return err -} - -func (c *BaseScheduler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) { - globals.SugarLogger.Infof("Swtich2SelfDelivered orderID:%s", order.VendorOrderID) - if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusDelivering { - if c.IsReallyCallPlatformAPI { - err = utils.CallFuncLogError(func() error { - return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).Swtich2SelfDelivered(order, userName) - }, "Swtich2SelfDelivered orderID:%s", order.VendorOrderID) - } - } else { - if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusDelivering { - err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation - globals.SugarLogger.Infof("Swtich2SelfDelivered orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) - } else { - err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation - globals.SugarLogger.Debugf("Swtich2SelfDelivered orderID:%s status:%d already ok", order.VendorOrderID, order.Status) - } - } - return err -} - -func (c *BaseScheduler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { - globals.SugarLogger.Infof("SelfDeliverDelivering orderID:%s", order.VendorOrderID) - if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusFinishedPickup { - if c.IsReallyCallPlatformAPI { - err = utils.CallFuncLogError(func() error { - return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).SelfDeliverDelivering(order, userName) - }, "SelfDeliverDelivering orderID:%s", order.VendorOrderID) - if err == nil { // 因为有些平台设置配送中后,不会发送订单在配送中消息过来,所以成功后就强制设置状态为配送中 - order.Status = model.OrderStatusDelivering - err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - } - } else { - if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusFinishedPickup { - err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation - globals.SugarLogger.Infof("SelfDeliverDelivering orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) - } else { - err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation - globals.SugarLogger.Debugf("SelfDeliverDelivering orderID:%s, status:%d already ok", order.VendorOrderID, order.Status) - } - } - return err -} - -func (c *BaseScheduler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { - globals.SugarLogger.Infof("SelfDeliverDelivered orderID:%s", order.VendorOrderID) - if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status >= model.OrderStatusFinishedPickup && - order.Status <= model.OrderStatusDelivering { - if c.IsReallyCallPlatformAPI { - err = utils.CallFuncLogError(func() error { - return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).SelfDeliverDelivered(order, userName) - }, "SelfDeliverDelivered orderID:%s", order.VendorOrderID) - } - } else { - if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusDelivering { - err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation - globals.SugarLogger.Infof("SelfDeliverDelivered orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) - } else { - err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation - globals.SugarLogger.Debugf("SelfDeliverDelivered orderID:%s, status:%d already ok", order.VendorOrderID, order.Status) - } - } - return err -} - -func (c *BaseScheduler) CreateWaybill(platformVendorID int, order *model.GoodsOrder, maxDeliveryFee int64) (bill *model.Waybill, err error) { - globals.SugarLogger.Infof("CreateWaybill orderID:%s, vendor:%s", order.VendorOrderID, jxutils.GetVendorName(platformVendorID)) - if !model.IsOrderSolid(order) { // 如果订单是不完整的 - globals.SugarLogger.Warnf("CreateWaybill orderID:%s, vendorID:%d is not solid!!!", order.VendorOrderID, platformVendorID) - return nil, scheduler.ErrOrderIsNotSolid - } - // if order.DeliveryFlag&model.OrderDeliveryFlagMaskScheduleDisabled != 0 { - // waybillList, err := partner.CurOrderManager.GetOrderWaybillInfo(jxcontext.AdminCtx, order.VendorOrderID, order.VendorID, true) - // if err != nil { - // return nil, err - // } - // if len(waybillList) > 0 { - // return nil, fmt.Errorf("转商家自送的订单只允许有一个有效运单,当前已经有%s运单", jxutils.GetVendorName(waybillList[0].WaybillVendorID)) - // } - // } - handlerInfo := partner.GetDeliveryPlatformFromVendorID(platformVendorID) - if handlerInfo != nil && handlerInfo.Use4CreateWaybill { - if c.IsReallyCallPlatformAPI { - bill, err = handlerInfo.Handler.CreateWaybill(order, maxDeliveryFee) - if err != nil { - globals.SugarLogger.Infof("CreateWaybill failed orderID:%s vendorID:%d with error:%v", order.VendorOrderID, platformVendorID, err) - } else { - order.DeliveryFlag |= model.WaybillVendorID2Mask(platformVendorID) - err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - } - } else { - err = scheduler.ErrDeliverProviderWrong - } - return bill, err -} - -func (c *BaseScheduler) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) { - globals.SugarLogger.Infof("CancelWaybill bill:%v, cancelReasonID:%d cancelReason:%s", bill, cancelReasonID, cancelReason) - // 部分快递平台在取消成功后有时会不发运单取消消息过来(比如达达,904200512000442),为避免二次取消报错,添加状态判断 - if c.IsReallyCallPlatformAPI && bill.OrderVendorID != bill.WaybillVendorID && bill.Status != model.WaybillStatusCanceled { - if handlerInfo := partner.GetDeliveryPlatformFromVendorID(bill.WaybillVendorID); handlerInfo != nil { - if err = utils.CallFuncLogErrorWithInfo(func() error { - return handlerInfo.Handler.CancelWaybill(bill, cancelReasonID, cancelReason) - }, "CancelWaybill bill:%v", bill); err == nil { - bill.Status = model.WaybillStatusCanceled - bill.DeliveryFlag |= model.WaybillDeliveryFlagMaskActiveCancel - _, err = dao.UpdateEntity(nil, bill, "Status", "DeliveryFlag") - } - globals.SugarLogger.Debugf("CancelWaybill bill:%v canceled by myself", bill) - } - } - return err -} diff --git a/business/jxcallback/scheduler/basesch/basesch_ext.go b/business/jxcallback/scheduler/basesch/basesch_ext.go deleted file mode 100644 index 95e9f3328..000000000 --- a/business/jxcallback/scheduler/basesch/basesch_ext.go +++ /dev/null @@ -1,422 +0,0 @@ -package basesch - -import ( - "fmt" - "strings" - "time" - - "git.rosy.net.cn/baseapi/platformapi/jdshopapi" - - "git.rosy.net.cn/baseapi/platformapi/dingdingapi" - "git.rosy.net.cn/baseapi/utils" - "git.rosy.net.cn/baseapi/utils/errlist" - "git.rosy.net.cn/jx-callback/business/jxstore/cms" - "git.rosy.net.cn/jx-callback/business/jxutils" - "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" - "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/business/partner/purchase/jd" - "git.rosy.net.cn/jx-callback/globals" -) - -const ( - autoSelfTakeCode = "135246" -) - -func (c *BaseScheduler) CreateWaybillOnProviders(ctx *jxcontext.Context, order *model.GoodsOrder, courierVendorIDs, excludeCourierVendorIDs []int, maxDeliveryFee int64, createOnlyOne bool) (bills []*model.Waybill, err error) { - userName := ctx.GetUserName() - globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s, courierVendorIDs:%v, excludeCourierVendorIDs:%v", order.VendorOrderID, userName, courierVendorIDs, excludeCourierVendorIDs) - storeCourierList, err := dao.GetStoreCourierList(dao.GetDB(), []int{jxutils.GetSaleStoreIDFromOrder(order)}, nil, model.StoreStatusOpened, model.StoreAuditStatusOnline) - if err != nil { - return nil, err - } - courierVendorIDMap := jxutils.IntList2Map(courierVendorIDs) - excludeCourierVendorIDMap := jxutils.IntList2Map(excludeCourierVendorIDs) - errList := errlist.New() - for _, storeCourier := range storeCourierList { - if (courierVendorIDs == nil || courierVendorIDMap[storeCourier.VendorID] == 1) && - (excludeCourierVendorIDs == nil || excludeCourierVendorIDMap[storeCourier.VendorID] == 0) { - if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil && handler.Use4CreateWaybill { - courierVendorID := storeCourier.VendorID - bill, err2 := c.CreateWaybill(courierVendorID, order, maxDeliveryFee) - if err = err2; err == nil { - globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d bill:%v", order.VendorOrderID, userName, courierVendorID, bill) - bills = append(bills, bill) - if createOnlyOne { - break - } - } else { - globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d failed with error:%v", order.VendorOrderID, userName, courierVendorID, err) - errList.AddErr(fmt.Errorf("平台:%s,%s", jxutils.GetVendorName(courierVendorID), err.Error())) - } - } - } - } - if len(bills) > 0 { - err = errList.GetErrListAsOne() - if err != nil { - partner.CurOrderManager.OnOrderMsg(order, "创建三方运单部分失败", err.Error()) - } - err = nil - } else if errList.GetErrListAsOne() == nil { - err = fmt.Errorf("orderID:%s没有绑定有效的三方配送门店或没有剩下可用的三方配送", order.VendorOrderID) - } else { - err = fmt.Errorf("orderID:%s所有运单失败:%s", order.VendorOrderID, errList.GetErrListAsOne().Error()) - } - globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s error:%v", order.VendorOrderID, userName, err) - return bills, err -} - -func (c *BaseScheduler) SelfDeliveredAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) { - jxutils.CallMsgHandler(func() { - err = func() (err error) { - globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName) - order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err == nil { - if model.IsOrderDeliveryByStore(order) { - err = c.SelfDeliverDelivered(order, userName) - } else if model.IsOrderDeliveryByPlatform(order) { - err = c.Swtich2SelfDelivered(order, userName) - } - if err == nil { - // order.Status = model.OrderStatusFinished // todo 是否需要强制设置完成状态? - if err = dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskSetDelivered); err == nil { - globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName) - return err - } - } - } - globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s error:%v", vendorOrderID, userName, err) - return err - }() - }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) - return err -} - -func (c *BaseScheduler) PickupGoodsAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) { - jxutils.CallMsgHandler(func() { - err = func() (err error) { - globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName) - order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err == nil { - err = c.PickupGoods(order, model.IsOrderDeliveryByStore(order), userName) - if err == nil { - order.Status = model.OrderStatusFinishedPickup - if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil { - globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName) - return err - } - } - } - globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s error:%v", vendorOrderID, userName, err) - return err - }() - }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) - return err -} - -func (c *BaseScheduler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AdjustOrder(ctx, order, removedSkuList, reason) - if err == nil { - var skuIDs []string - for _, v := range removedSkuList { - skuIDs = append(skuIDs, utils.Int2Str(v.SkuID)) - } - noticeMsg := fmt.Sprintf("商品skuID列表:%v,订单号(点击进入详情):%v", strings.Join(skuIDs, ","), globals.BackstageHost+"/#/ordermanager/"+order.VendorOrderID) - user, err := dao.GetUserByID(dao.GetDB(), "mobile", "18982250714") - if user != nil && err == nil { - ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "调整单调整商品", noticeMsg) - } - - } - } - return err -} - -func (c *BaseScheduler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { - if c.IsReallyCallPlatformAPI { - if globals.IsAddEvent { - err = cms.AddEventDetail(dao.GetDB(), ctx, model.OperateUpdate, order.StoreID, model.ThingTypeOrder, order.StoreID, order.VendorOrderID, order.StoreName) - } - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).CancelOrder(ctx, order, reason) - } - return err -} - -func (c *BaseScheduler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool, reason string) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AcceptOrRefuseFailedGetOrder(ctx, order, isAcceptIt) - } - if err == nil { - flag := model.OrderFlagAgreeFailedGetGoods - if !isAcceptIt { - flag = model.OrderFlagRefuseFailedGetGoods - } - dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, flag) - } - return err -} - -func (c *BaseScheduler) CallPMCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).CallCourier(ctx, order) - } - if err == nil { - dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskCallPMCourier) - } - return err -} - -func (c *BaseScheduler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).ConfirmReceiveGoods(ctx, order) - } - if err == nil { - dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskFailedDeliver) - } - return err -} - -func (c *BaseScheduler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool, reason string) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AgreeOrRefuseCancel(ctx, order, isAcceptIt, reason) - } - if err == nil { - flag := model.OrderFlagAgreeUserApplyCancel - if !isAcceptIt { - flag = model.OrderFlagRefuseUserApplyCancel - } - dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, flag) - } - return err -} - -func (c *BaseScheduler) CancelWaybillByID(ctx *jxcontext.Context, vendorWaybillID string, waybillVendorID int, cancelReasonID int, cancelReason string) (err error) { - bill, err := partner.CurOrderManager.LoadWaybill(vendorWaybillID, waybillVendorID) - if err == nil { - err = c.CancelWaybill(bill, cancelReasonID, cancelReason) - } - return err -} - -func (c *BaseScheduler) AgreeOrRefuseRefund(ctx *jxcontext.Context, afsOrderID string, vendorID, approveType int, reason string) (err error) { - afsOrder, err := partner.CurOrderManager.LoadAfsOrder(afsOrderID, vendorID) - if err == nil { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(vendorID).AgreeOrRefuseRefund(ctx, afsOrder, approveType, reason) - } - if err == nil { - flag := model.AfsOrderFlagAgreeUserRefund - if approveType == partner.AfsApproveTypeRefused { - flag = model.AfsOrderFlagRefuseUserRefund - afsOrder.RefuseReason = reason - partner.CurOrderManager.UpdateAfsOrderFields(afsOrder, []string{"RefuseReason"}) - } else { - if order, _ := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID); order != nil { - if order.EarningType == model.EarningTypePoints { - var ( - skuMap = make(map[int]*model.OrderSku) - diff int64 - db = dao.GetDB() - ) - for _, sku := range order.Skus { - skuMap[sku.SkuID] = sku - } - storeDetail, _ := dao.GetStoreDetail(db, jxutils.GetSaleStoreIDFromOrder(order), order.VendorID) - waybills, _ := dao.GetWaybills(db, order.VendorOrderID) - //京东商城和京西要重新算totalshopmoney等 - if order.VendorID == model.VendorIDJDShop || order.VendorID == model.VendorIDJX { - skus, _ := dao.GetAfsOrderSkuInfo(db, order.VendorOrderID, afsOrderID, order.VendorID, false) - for _, v := range skus { - if skuMap[v.SkuID] != nil { - diff += skuMap[v.SkuID].SalePrice * int64(v.Count) - } - } - order.TotalShopMoney = utils.Float64TwoInt64(float64(float64(order.TotalShopMoney)/jdshopapi.JdsPayPercentage-float64(diff)) * jdshopapi.JdsPayPercentage) - if len(waybills) > 0 { - jxutils.RefreshOrderEarningPrice3(order, storeDetail.PayPercentage, waybills[0]) - } else { - jxutils.RefreshOrderEarningPrice2(order, storeDetail.PayPercentage) - } - dao.UpdateEntity(db, order, "TotalShopMoney", "NewEarningPrice") - } - } - } - } - dao.SetAfsOrderFlag(dao.GetDB(), ctx.GetUserName(), afsOrderID, vendorID, flag) - } - } - return err -} - -func (c *BaseScheduler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, afsOrderID string, vendorID int) (err error) { - afsOrder, err := partner.CurOrderManager.LoadAfsOrder(afsOrderID, vendorID) - if err == nil { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(vendorID).ConfirmReceivedReturnGoods(ctx, afsOrder) - } - if err == nil { - dao.SetAfsOrderFlag(dao.GetDB(), ctx.GetUserName(), afsOrderID, vendorID, model.AfsOrderFlagMaskReturnGoods) - } - } - return err -} - -func (c *BaseScheduler) PartRefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, refundSkuList []*model.OrderSku, reason string) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).PartRefundOrder(ctx, order, refundSkuList, reason) - } - return err -} - -func (c *BaseScheduler) RefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { - if c.IsReallyCallPlatformAPI { - err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).RefundOrder(ctx, order, reason) - } - return err -} - -func (c *BaseScheduler) ConfirmSelfTake(ctx *jxcontext.Context, vendorOrderID string, vendorID int, selfTakeCode string) (err error) { - order, err2 := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err = err2; err == nil { - err = c.confirmSelfTake(ctx, order, selfTakeCode) - } - return err -} - -func (c *BaseScheduler) SetOrderWaybillTip(ctx *jxcontext.Context, vendorOrderID string, vendorID int, tipFee int64) (err error) { - order, err2 := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err = err2; err == nil { - err = c.SetOrderWaybillTipByOrder(ctx, order, tipFee) - } - return err -} - -func isWaybillCanAddTip(waybill *model.Waybill) (isCan bool) { - isCan = waybill.Status >= model.WaybillStatusNew && waybill.Status < model.WaybillStatusAccepted && partner.GetWaybillTipUpdater(waybill.WaybillVendorID) != nil - return isCan -} - -func (c *BaseScheduler) SetOrderWaybillTipByOrder(ctx *jxcontext.Context, order *model.GoodsOrder, tipFee int64) (err error) { - roundTipFee := tipFee / 100 * 100 - if roundTipFee != tipFee { - return fmt.Errorf("小费必须是1元的整数倍") - } - if order.WaybillTipMoney >= tipFee { - return fmt.Errorf("当前小费已经是%s元,想要设置%s元", jxutils.IntPrice2StandardString(order.WaybillTipMoney), jxutils.IntPrice2StandardString(tipFee)) - } - - db := dao.GetDB() - storeDetail, err2 := dao.GetStoreDetail(db, jxutils.GetSaleStoreIDFromOrder(order), order.VendorID) - if err = err2; err != nil { - return err - } - - // 如果平台支持设置配送小费,必须要成功设置 - if handler := partner.GetWaybillTipUpdater(order.VendorID); handler != nil { - if err = handler.UpdateWaybillTip(ctx, order.VendorOrgCode, order.VendorStoreID, order.VendorOrderID, "", "", utils.Int2Str(storeDetail.CityCode), tipFee); err != nil { - return err - } - } - order.WaybillTipMoney = tipFee - partner.CurOrderManager.UpdateOrderFields(order, []string{"WaybillTipMoney"}) - - waybills, err := dao.GetWayBillByOrderID(db, 0, order.VendorID, 0, order.VendorOrderID) - if err == nil { - var waybills2 []*model.Waybill - for _, v := range waybills { - // 必须是三方配送 - if !model.IsWaybillPlatformOwn(v) && isWaybillCanAddTip(v) { - waybills2 = append(waybills2, v) - } - } - if len(waybills2) > 0 { - task := tasksch.NewParallelTask("SetOrderWaybillTipByOrder", nil, ctx, - func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { - waybill := batchItemList[0].(*model.Waybill) - handler := partner.GetWaybillTipUpdater(waybill.WaybillVendorID) - if err == nil { - err = handler.UpdateWaybillTip(ctx, waybill.VendorOrgCode, storeDetail.VendorStoreID, waybill.VendorOrderID, waybill.VendorWaybillID, waybill.VendorWaybillID2, utils.Int2Str(storeDetail.CityCode), tipFee) - } - return nil, err - }, waybills2) - tasksch.HandleTask(task, nil, false).Run() - _, err = task.GetResult(0) - } - } - return err -} - -func (c *BaseScheduler) confirmSelfTake(ctx *jxcontext.Context, order *model.GoodsOrder, selfTakeCode string) (err error) { - globals.SugarLogger.Debugf("confirmSelfTake orderID:%s, selfTakeCode:%s", order.VendorOrderID, selfTakeCode) - vendorID := order.VendorID - if vendorID == model.VendorIDJD || vendorID == model.VendorIDJX { - if vendorID == model.VendorIDJD { - if selfTakeCode == autoSelfTakeCode { - if selfTakeCode, err = jd.CurPurchaseHandler.GetSelfTakeCode(ctx, order); err != nil { - return fmt.Errorf("获取订单:%s自提货码失败,原始错误:%s", order.VendorOrderID, err.Error()) - } - if selfTakeCode == "" { - return fmt.Errorf("订单:%s看起来不是一个自提订单,如果确认是自提订单,请联系开发", order.VendorOrderID) - } - } - err = jd.CurPurchaseHandler.ConfirmSelfTake(ctx, order, selfTakeCode) - } else { - orderStatus := &model.OrderStatus{ - VendorOrderID: order.VendorOrderID, - VendorID: model.VendorIDJX, - OrderType: model.OrderTypeOrder, - RefVendorOrderID: order.VendorOrderID, - RefVendorID: model.VendorIDJX, - VendorStatus: utils.Int2Str(model.OrderStatusFinished), - Status: model.OrderStatusFinished, - StatusTime: time.Now(), - Remark: "自提完成", - } - jxutils.CallMsgHandlerAsync(func() { - err = partner.CurOrderManager.OnOrderStatusChanged("", orderStatus) - }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, model.VendorIDJX)) - } - } else { - err = fmt.Errorf("自提核销不支持%s平台订单", model.VendorChineseNames[order.VendorID]) - } - return err -} - -func (c *BaseScheduler) ConfirmSelfTakeOrders(ctx *jxcontext.Context, vendorIDs []int, orderCreatedAfter, orderCreatedBefore time.Time, isAsync, isContinueWhenError bool) (hint string, err error) { - orderList, err := dao.GetPendingFakeOrders(dao.GetDB(), vendorIDs, orderCreatedAfter, orderCreatedBefore) - if err == nil { - if len(orderList) > 0 { - task := tasksch.NewParallelTask(fmt.Sprintf("自动完成内部自提单%v,%s", vendorIDs, utils.Time2Str(orderCreatedAfter)), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, - func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { - order := batchItemList[0].(*model.GoodsOrder) - if order.Status == model.OrderStatusAccepted { - if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil { - err = handler.AcceptOrRefuseOrder(order, true, ctx.GetUserName()) - time.Sleep(2 * time.Second) - } - } - if err == nil { - if err = c.confirmSelfTake(ctx, order, autoSelfTakeCode); err == nil { - retVal = []int{1} - } - } - return retVal, err - }, orderList) - tasksch.HandleTask(task, nil, true).Run() - if isAsync { - hint = task.GetID() - } else { - resultList, err2 := task.GetResult(0) - if err = err2; err == nil { - hint = utils.Int2Str(len(resultList)) - } - } - } - } - return hint, err -} diff --git a/business/jxcallback/scheduler/defsch/defsch.go b/business/jxcallback/scheduler/defsch/defsch.go deleted file mode 100644 index 88c29c18d..000000000 --- a/business/jxcallback/scheduler/defsch/defsch.go +++ /dev/null @@ -1,1634 +0,0 @@ -package defsch - -import ( - "fmt" - "math/rand" - "sync" - "time" - - push "git.rosy.net.cn/jx-callback/business/jxutils/unipush" - - "github.com/astaxie/beego" - - "git.rosy.net.cn/jx-callback/business/jxstore/cms" - - "git.rosy.net.cn/jx-callback/business/authz" - "git.rosy.net.cn/jx-callback/business/authz/autils" - "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" - - "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" - "git.rosy.net.cn/jx-callback/business/jxutils/netprinter" - "git.rosy.net.cn/jx-callback/business/jxutils/smsmsg" - "git.rosy.net.cn/jx-callback/business/msghub" - - "git.rosy.net.cn/baseapi/platformapi/dingdingapi" - "git.rosy.net.cn/baseapi/utils" - "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler" - "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler/basesch" - "git.rosy.net.cn/jx-callback/business/jxutils" - "git.rosy.net.cn/jx-callback/business/jxutils/configindb" - "git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg" - "git.rosy.net.cn/jx-callback/business/model" - "git.rosy.net.cn/jx-callback/business/model/dao" - "git.rosy.net.cn/jx-callback/business/model/legacymodel" - "git.rosy.net.cn/jx-callback/business/partner" - "git.rosy.net.cn/jx-callback/globals" - "github.com/astaxie/beego/orm" -) - -const ( - time2Delivered = 1 * time.Hour // 正常从下单到送达的时间。 - minute2Schedule3rdCarrier = 20 // 收到平台方自有配送的新运单消息后,等待创建三方配送运单的时间(分钟),如果是定时达,会再根据ExpectedDeliveredTime与dingShiDaAheadTime做调整 - minute2Schedule3rdCarrier4Ebai = 30 // 饿百的最少转自配送需要的时间(分钟) - minMinute2Schedule3rdCarrier = 5 // 转三方配送最少等待时间(分钟) - - time2AutoPickupMin = 14 * time.Minute // 自动拣货等待时间,这个只有在没有PickDeadline信息才有用,否则会根据PickDeadline设置 - second2AutoPickupGap = 60 //随机60秒 - time2AutoPickupAhead = 120 * time.Second // 有最后拣货时间的提前值 - - switch2SelfDeliverRetryGap = 3 * time.Second // 转自送失败尝试的时间间隙 - switch2SelfDeliverRetryCount = 2 // 转自送失败尝试次数 - - // (把pending order timerout 在-pendingOrderTimerMinMinSecond至pendingOrderTimerMaxSecond映射到pendingOrderTimerMinSecond至pendingOrderTimerMaxSecond) - pendingOrderTimerMinMinSecond = 5 * 60 // 5分钟 - pendingOrderTimerMinSecond = 2 - pendingOrderTimerMaxSecond = 5 - - maxWaybillRetryCount = 3 - - orderMapStoreMaxTime = 4 * 24 * time.Hour // cache最长存储时间 - - time2Schedule3rdCarrierKey = "waitminute4mt" - dingShiDaAheadTime = 30 * time.Minute // 定时达订单开始召唤配送的提前时间 - - minAddWaybillTipMinute = 20 // 最少开始加小费分钟(距离拣货完成) - addWaybillTipGap = 15 // 加一元小费间隔的分钟数 - maxWaybillTipMoney = 400 // 最大小费 - baseWaybillFee = 600 // 基本运费 - ebaiCancelWaybillMaxFee = 1000 // 饿百取消运单最高运费,门店自送最高运费 - maxJxStoreDeliveryFee = 2000 - baseWaybillAddFee = 200 //增加运费上限2块 - - ebaiCancelWaybillWarningMinute = 8 -) - -const ( - maxAddFee = 300 // 最大增加费用,单位为分,超过不发三方配送了 -) - -var ( - FixedScheduler *DefScheduler -) - -type tTimerInfo struct { - statusType int - vendorID int - status int - - timer *time.Timer - timerTime time.Time -} - -type WatchOrderInfo struct { - order *model.GoodsOrder // order里的信息是保持更新的 - - autoPickupTimeoutMinute int // 0表示禁用,1表示用缺省值time2AutoPickupMin,其它表示分钟数 - - isDeliveryCompetition bool - isNeedCreate3rdWaybill bool - watchWabillStartAt *time.Time - - waybills map[int]*model.Waybill // 这个waybills里的状态信息是不真实的,只使用id相关的信息 - - // timerStatusType int // 0表示订单,1表示运单 - // timerStatus int - // timer *time.Timer - // timerTime time.Time - - timerList []*tTimerInfo - - retryCount int // 失败后尝试的次数,调试阶段可能出现死循化,阻止这种情况发生 - storeDetail *dao.StoreDetail -} - -type StatusActionConfig struct { - partner.StatusActionParams - TimeoutAction func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) // 超时后需要执行的动作,为nil表示此状态不需要执行监控, nil在GetStatusActionConfig返回时表示不修改缺省 - ShouldSetTimer func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool -} - -func (c *StatusActionConfig) CallTimeoutAction(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - if c.TimeoutAction != nil && - c.CallShouldSetTimer(savedOrderInfo, bill) { - err = c.TimeoutAction(savedOrderInfo, bill) - } - return err -} - -func (c *StatusActionConfig) CallShouldSetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - if c.ShouldSetTimer != nil { - return c.ShouldSetTimer(savedOrderInfo, bill) - } - return true -} - -// 重要:此调度器要求同一定单的处理逻辑必须是序列化了的,不然会有并发问题 -type DefScheduler struct { - basesch.BaseScheduler - locker sync.RWMutex - defWorkflowConfig []map[int]*StatusActionConfig - orderMap jxutils.SyncMapWithTimeout -} - -func NewWatchOrderInfo(order *model.GoodsOrder) (retVal *WatchOrderInfo) { - retVal = &WatchOrderInfo{ - autoPickupTimeoutMinute: 1, - waybills: map[int]*model.Waybill{}, - } - retVal.SetOrder(order) - return retVal -} - -func (s *WatchOrderInfo) SetOrder(order *model.GoodsOrder) (retVal *model.GoodsOrder) { - retVal = s.order - if s.order != order { - if order != nil { - s.updateOrderStoreFeature(order) - } - s.order = order - } - return retVal -} - -func (s *WatchOrderInfo) updateOrderStoreFeature(order *model.GoodsOrder) (err error) { - globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s", order.VendorOrderID) - jxStoreID := jxutils.GetSaleStoreIDFromOrder(order) - if jxStoreID > 0 { - db := dao.GetDB() - storeDetail, err2 := dao.GetStoreDetail(db, jxStoreID, order.VendorID) - if err = err2; err != nil { - return err - } - s.autoPickupTimeoutMinute = int(storeDetail.AutoPickup) - s.isDeliveryCompetition = storeDetail.DeliveryCompetition != 0 - } - globals.SugarLogger.Debugf("updateOrderStoreFeature2 orderID:%s, isDeliveryCompetition:%t", order.VendorOrderID, s.isDeliveryCompetition) - return err -} - -func (s *WatchOrderInfo) GetWaybillVendorIDs() (vendorIDs []int) { - for vendorID := range s.waybills { - vendorIDs = append(vendorIDs, vendorID) - } - return vendorIDs -} - -// orderType,-1:全部 -// vendorID,-1:全部 -// status,-1:全部 -func (w *WatchOrderInfo) StopTimer(statusType, vendorID, status int) { - var newTimerList []*tTimerInfo - for _, timerInfo := range w.timerList { - if (statusType == -1 || statusType == timerInfo.statusType) && - (vendorID == -1 || vendorID == timerInfo.vendorID) && - (status == -1 || status >= timerInfo.status) { - if timerInfo.timer != nil { - timerInfo.timer.Stop() - timerInfo.timer = nil - } - } else { - newTimerList = append(newTimerList, timerInfo) - } - } - w.timerList = newTimerList -} - -func (w *WatchOrderInfo) AddTimer(timerInfo *tTimerInfo) { - w.timerList = append(w.timerList, timerInfo) -} - -func (w *WatchOrderInfo) GetCreateWaybillTimeout() (timeoutSecond int) { - // if w.timerStatusType == scheduler.TimerStatusTypeWaybill && w.timerStatus == model.WaybillStatusNew { - // timeoutSecond = int(w.timerTime.Sub(time.Now()) / time.Second) - // } - for _, timerInfo := range w.timerList { - if timerInfo.statusType == scheduler.TimerStatusTypeWaybill && - timerInfo.status == model.WaybillStatusNew { - timeoutSecond = int(timerInfo.timerTime.Sub(time.Now()) / time.Second) - break - } - } - return timeoutSecond -} - -func init() { - sch := &DefScheduler{} - basesch.FixedBaseScheduler = &sch.BaseScheduler - FixedScheduler = sch - sch.IsReallyCallPlatformAPI = globals.ReallyCallPlatformAPI - scheduler.CurrentScheduler = sch - statusAccept := func(time time.Duration) *StatusActionConfig { - return &StatusActionConfig{ // 自动接单 - StatusActionParams: partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseStatusTime, - Timeout: time, - }, - TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - order := savedOrderInfo.order - storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID) - if err == nil { - savedOrderInfo.storeDetail = storeDetail - } - mobile := order.ConsigneeMobile - if order.ConsigneeMobile2 != "" { - mobile = order.ConsigneeMobile2 - } - _ = sch.handleAutoAcceptOrder(order.VendorOrderID, order.VendorID, mobile, jxutils.GetSaleStoreIDFromOrder(order), nil, func(isAcceptIt bool) error { - if err = sch.AcceptOrRefuseOrder(order, isAcceptIt, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation { - partner.CurOrderManager.OnOrderMsg(order, "自动接单失败", err.Error()) - // 为了解决京东新消息与接单消息乱序的问题 - if errWithCode, ok := err.(*utils.ErrorWithCode); ok && errWithCode.Level() == 1 && errWithCode.IntCode() == -1 { - if order2, err2 := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).GetOrder(order.VendorOrgCode, order.VendorOrderID); err2 == nil { - if order2.Status > order.Status { - order.Status = order2.Status - jxutils.CallMsgHandlerAsync(func() { - sch.OnOrderStatusChanged(order, model.Order2Status(order2), false) - }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) - err = nil - } - } else { - err = err2 - } - } - } - if isAcceptIt { - if err == nil { - sch.notifyNewOrder(order) - msghub.OnNewOrder(order) - } - } else { - partner.CurOrderManager.OnOrderMsg(order, "黑名单拒单", "") - } - return err - }) - return nil - }, - ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - return savedOrderInfo.order.LockStatus == model.LockStatusUnlocked && (savedOrderInfo.order.Status == model.OrderStatusNew || savedOrderInfo.order.Status == model.OrderStatusWaitAccepted) - }, - } - } - sch.defWorkflowConfig = []map[int]*StatusActionConfig{ - map[int]*StatusActionConfig{ - model.OrderStatusWaitAccepted: statusAccept(2 * time.Minute), //饿百假的自动接单 - model.OrderStatusNew: statusAccept(10 * time.Millisecond), - model.OrderStatusAccepted: &StatusActionConfig{ // 自动拣货 - StatusActionParams: partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseStatusTime, - Timeout: time2AutoPickupMin, - TimeoutGap: second2AutoPickupGap, - }, - TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - if err = sch.autoPickupGood(savedOrderInfo); err != nil { - partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "自动拣货失败", err.Error()) - } - return nil - }, - ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - return savedOrderInfo.autoPickupTimeoutMinute > 0 && savedOrderInfo.order.Status < model.OrderStatusFinishedPickup - }, - }, - model.OrderStatusFinishedPickup: &StatusActionConfig{ - StatusActionParams: partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseStatusTime, - Timeout: 1 * time.Second, - TimeoutGap: 0, - }, - TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - // 启动抢单TIMER - sch.saveDeliveryFeeFromAndStartWatch(savedOrderInfo, savedOrderInfo.order.StatusTime) - return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - }, - ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - return savedOrderInfo.isDeliveryCompetition && model.IsOrderDeliveryByStore(savedOrderInfo.order) // 自配送商家使用 - }, - }, - }, - map[int]*StatusActionConfig{ - // todo 平台物流二次创建运单的话,这个TIMER有问题 - model.WaybillStatusNew: &StatusActionConfig{ - StatusActionParams: partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseStatusTime, - Timeout: minute2Schedule3rdCarrier * time.Minute, - }, - TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - }, - ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - // 饿百转自送的时机不太清楚,暂时禁用超时转自送,在饿百运单取消时还是会自动创建 - // 非自配送商家使用 - order := savedOrderInfo.order - return order.VendorID != model.VendorIDEBAI && - order.VendorID == bill.WaybillVendorID && - savedOrderInfo.isDeliveryCompetition && - model.IsOrderDeliveryByPlatform(order) && - isOrderCanSwitch2SelfDeliver(order) && - (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) - }, - }, - //* - model.WaybillStatusCanceled: &StatusActionConfig{ - StatusActionParams: partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseNow, - Timeout: 1 * time.Second, - }, - TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - return sch.onEbaiWaybillCanceled(savedOrderInfo, nil) - // return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - }, - ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - order := savedOrderInfo.order - // 非自配送商家使用 - return order.VendorID == model.VendorIDEBAI && - order.VendorID == bill.WaybillVendorID && - savedOrderInfo.isDeliveryCompetition && - model.IsOrderDeliveryByPlatform(order) && - isOrderCanSwitch2SelfDeliver(order) && - (order.Status >= model.OrderStatusAccepted && order.Status < model.OrderStatusEndBegin) // 运单与订单时间有错序的情况,放开订单条件至OrderStatusAccepted - }, - }, - //*/ - }, - } -} - -func Init() { - configindb.WatchConfigChange(time2Schedule3rdCarrierKey, OnDefSchConfChanged) - if configTime, err := configindb.GetConfig(time2Schedule3rdCarrierKey, utils.Int2Str(minute2Schedule3rdCarrier)); err == nil { - OnDefSchConfChanged(time2Schedule3rdCarrierKey, configTime) - } else { - globals.SugarLogger.Errorf("defsch Init, error:%v", err) - } -} - -// 以下是订单 -func (s *DefScheduler) OnOrderNew(order *model.GoodsOrder, isPending bool) (err error) { - globals.SugarLogger.Debugf("OnOrderNew orderID:%s", order.VendorOrderID) - savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), false) - savedOrderInfo.SetOrder(order) - if order.Status == model.OrderStatusWaitAccepted { - s.resetTimer(savedOrderInfo, nil, isPending) - push.NotifyNewOrder(order) - } - if order.Status >= model.OrderStatusNew { - s.resetTimer(savedOrderInfo, nil, isPending) - if !isPending && order.Status >= model.OrderStatusAccepted { // 有订单消息错序,先收到接单消息,再收到新订单消息,导致接单TIMER不动作,这里补一下 - s.notifyNewOrder(order) - msghub.OnNewOrder(order) - } - } - return err -} - -// todo 这个接口应该可以直接传order的,因为在OrderManager中每次都生成了 -func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *model.OrderStatus, isPending bool) (err error) { - if status.Status > model.OrderStatusMsg && status.Status != model.OrderStatusUnknown { - globals.SugarLogger.Debugf("OnOrderStatusChanged orderID:%s %s, status:%v", status.VendorOrderID, model.OrderStatusName[status.Status], status) - if order == nil { - globals.SugarLogger.Warnf("OnOrderStatusChanged order is nil, status:%s", utils.Format4Output(status, true)) - } else if order.Status > model.OrderStatusUnknown && status.Status > model.OrderStatusUnknown && order.Status != status.Status { - globals.SugarLogger.Warnf("OnOrderStatusChanged strange order:%s, status:%s", utils.Format4Output(order, true), utils.Format4Output(status, true)) - } - savedOrderInfo := s.loadSavedOrderFromMap(status, false) // 可能会有重复的消息(比如订单取消) - statusChanged := savedOrderInfo.order == nil || savedOrderInfo.order.Status != order.Status - savedOrderInfo.SetOrder(order) - - if (model.IsOrderUnlockStatus(status.Status)) || - (order.LockStatus == model.OrderStatusUnknown && (status.Status > model.OrderStatusUnknown || status.Status == model.OrderStatusRefuseFailedGetGoods)) { // 只处理状态转换,一般消息不处理 - if status.Status == model.OrderStatusRefuseFailedGetGoods && order.Status != model.OrderStatusFinishedPickup && !model.IsOrderFinalStatus(order.Status) { - order.Status = model.OrderStatusFinishedPickup - partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - s.resetTimer(savedOrderInfo, nil, isPending) - if status.Status >= model.OrderStatusDelivering { - // 会出现创建运单后,门店自己转自送了(例如:828400083000222),当前逻辑会导致此运单不会被取消,有三个可能的修改方法,考虑1: - // 1,在订单相应事件中取消不是candidate的运单 - // 2,在接收到运单接单时根据状态判断马上取消些运单 - // 3,在转自送失败(或状态已经是自送时)取消运单 - var curWaybill *model.Waybill - if !(status.Status == model.OrderStatusCanceled) { // 订单取消时,取消所有运单 - curWaybill = savedOrderInfo.waybills[savedOrderInfo.order.WaybillVendorID] - if status.Status == model.OrderStatusFinished { - if curWaybill != nil && !model.IsWaybillPlatformOwn(curWaybill) { - globals.SugarLogger.Infof("OnOrderStatusChanged [运营2]订单orderID:%s可能被手动点击送达,会对程序状态产生不利影响,请通知门店不要这样操作!", status.VendorOrderID) - } - } - } - s.cancelOtherWaybills(savedOrderInfo, curWaybill, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrOrderAlreadyFinished) - if status.Status >= model.OrderStatusEndBegin { - s.orderMap.Delete(jxutils.GetUniversalOrderIDFromOrderStatus(status)) - } - } - if !isPending { - if status.Status == model.OrderStatusAgreeFailedGetGoods || status.Status == model.OrderStatusDeliverFailed { - s.updateOrderByBill(order, nil, false) //status.Status != model.OrderStatusAgreeFailedGetGoods) - s.removeWaybillFromMap(savedOrderInfo, order.VendorID) - clearFlag := 0 - if status.Status == model.OrderStatusAgreeFailedGetGoods { - clearFlag = model.OrderFlagMaskFailedGetGoods - } else if status.Status == model.OrderFlagMaskFailedDeliver { - clearFlag = model.OrderFlagMaskFailedDeliver - } - if order.Flag > clearFlag { - dao.ClearOrderFlag2(dao.GetDB(), model.AdminName, order.VendorOrderID, order.VendorID, clearFlag|model.OrderFlagMaskPrinted) - } else { - dao.ClearOrderFlag(dao.GetDB(), model.AdminName, order.VendorOrderID, order.VendorID, clearFlag) - } - } - } - } - if order.LockStatus != model.OrderStatusUnknown { - s.stopTimer(savedOrderInfo) - } - if !isPending { - if order.Flag&model.OrderFlagMaskFake != 0 && status.Status == model.OrderStatusAccepted { - s.autoPickupGood(savedOrderInfo) - } else if status.Status == model.OrderStatusFinishedPickup || status.Status == model.OrderStatusCanceled { - if statusChanged && status.Status == model.OrderStatusCanceled { - s.notifyOrderCanceled(savedOrderInfo.order) - } - msghub.OnFinishedPickup(savedOrderInfo.order) - } else if status.Status == model.OrderStatusApplyCancel || //model.IsOrderLockStatus(status.Status) || - status.Status == model.OrderStatusApplyFailedGetGoods || //model.IsOrderUnlockStatus(status.Status) || - status.Status == model.OrderStatusAgreeFailedGetGoods || - status.Status == model.OrderStatusDeliverFailed { - if status.Status == model.OrderStatusApplyFailedGetGoods { - dao.ClearOrderFlag(dao.GetDB(), model.AdminName, order.VendorOrderID, order.VendorID, model.OrderFlagMaskFailedGetGoods|model.OrderFlagMaskCallPMCourier) - } - if status.Status == model.OrderStatusApplyCancel { - s.notifyUserApplyCancel(savedOrderInfo.order, status.Remark) - } - msghub.OnKeyOrderStatusChanged(savedOrderInfo.order) - } - } - } - return err -} - -// 以下是运单 -func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending bool) (err error) { - if bill.Status > model.WaybillStatusUnknown { - globals.SugarLogger.Debugf("OnWaybillStatusChanged orderID:%s %s, bill:%v", bill.VendorOrderID, model.WaybillStatusName[bill.Status], bill) - savedOrderInfo := s.loadSavedOrderFromMap(model.Waybill2Status(bill), true) - order := savedOrderInfo.order - // todo 当前收到的事件顺序有时是乱的,不能严格限制,暂时放开 - // if order.Status < model.OrderStatusFinishedPickup || order.Status > model.OrderStatusEndBegin { // 如果当前order状态是不应该出现运单状态 - // globals.SugarLogger.Infof("OnWaybillStatusChanged orderID:%s status:%s is not suitable for waybill", order.VendorOrderID, model.OrderStatusName[order.Status]) - // s.ProxyCancelWaybill(order, bill) - // s.stopTimer(savedOrderInfo) - // s.orderMap.Delete(jxutils.GetUniversalOrderIDFromOrderStatus(model.Order2Status(order))) - // return nil - // } - // if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { // 如果被停止调度,整个不动作 - if bill.Status == model.WaybillStatusNew { - s.addWaybill2Map(savedOrderInfo, bill) - if !isPending { - if order.Status > model.OrderStatusEndBegin { - s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - } else { - needAddTip := order.WaybillTipMoney > 0 && !model.IsWaybillPlatformOwn(bill) - if model.IsOrderHaveWaybill(order) { - globals.SugarLogger.Debugf("OnWaybillStatusChanged multiple waybill created, bill:%v", bill) - if model.IsWaybillPlatformOwn(bill) { // 是购物平台运单 - if !model.IsOrderHaveOwnWaybill(order) { // 既有运单不是购物平台运单 - globals.SugarLogger.Infof("OnWaybillStatusChanged bill:%v purchase platform bill came later than others, strange!!!", bill) - oldBill := savedOrderInfo.waybills[order.WaybillVendorID] - if oldBill != nil { - s.ProxyCancelWaybill(order, oldBill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - } else { - globals.SugarLogger.Warnf("OnWaybillStatusChanged bill:%v, oldBill is null, strange!!!", bill) - } - } - s.updateOrderByBill(order, nil, false) - if time.Now().Sub(order.OrderCreatedAt) < 2*time.Minute { // 京东一些门店设置成了接单即拣货完成,这种情况下自动调用拣货完成 - s.autoPickupGood(savedOrderInfo) - } - } else { - needAddTip = false - s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - } - } - if needAddTip { - if handler := partner.GetWaybillTipUpdater(bill.WaybillVendorID); handler != nil { - if storeDetail, err2 := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID); err2 == nil { - handler.UpdateWaybillTip(jxcontext.AdminCtx, order.VendorOrgCode, order.VendorStoreID, order.VendorOrderID, bill.VendorWaybillID, bill.VendorWaybillID2, utils.Int2Str(storeDetail.CityCode), order.WaybillTipMoney) - } - } - } - } - flag2Clear := model.WaybillVendorID2Mask(bill.WaybillVendorID) - if order.DeliveryFlag&flag2Clear != 0 { - order.DeliveryFlag &= ^flag2Clear - err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - } - // 购物平台的新运单消息会启动抢单TIMER - if model.IsWaybillPlatformOwn(bill) { - s.resetTimer(savedOrderInfo, bill, isPending) - s.saveDeliveryFeeFromAndStartWatch(savedOrderInfo, bill.StatusTime) - } - s.sendCourierOrderSMS(bill) - } else { - isBillExist := s.updateBillsInfo(savedOrderInfo, bill) - if !isBillExist { - // s.addWaybill2Map(savedOrderInfo, bill) // updateBillsInfo中会添加 - globals.SugarLogger.Debugf("OnWaybillStatusChanged bill not exist! orderID:%s, bill:%v", bill.VendorOrderID, bill) - } - switch bill.Status { - case model.WaybillStatusAccepted, model.WaybillStatusCourierArrived, model.WaybillStatusDelivering: - s.resetTimer(savedOrderInfo, bill, isPending) - if (isBillExist || bill.WaybillVendorID != model.VendorIDDada) && !isPending { // todo 达达运单有错序的情况,临时看看 - isBillAlreadyCandidate := s.isBillCandidate(order, bill) - // todo 购买平台的运单,优先级最高,但这样写也可能带来问题,即在这个时间,因为之前3方已经接单,已经发出了转自送请求(而且可能成功了),所以加个状态判断 - if !model.IsOrderHaveWaybill(order) || - (model.IsWaybillPlatformOwn(bill) && !model.IsOrderHaveOwnWaybill(order) && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0) { - if model.IsOrderHaveWaybill(order) { - // 进到这里的原因是,在这个时间点,购物平台物流已经抢单(但抢单消息还没有被收到)(比如:818810379000941) - globals.SugarLogger.Infof("OnWaybillStatusChanged orderID:%s purchase platform waybill arrvied later, may cause problem", order.VendorOrderID) - } - s.updateOrderByBill(order, bill, false) - s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - //若接单时间不在门店的营业时间范围内要取消运单 - s.cancelWaybillNotInStoreOpentime(savedOrderInfo, bill) - //京东商城的话,需要去把订单出库,如果是转移过的订单,则需要修改转移订单号 - if order.VendorID == model.VendorIDJDShop { - s.solutionJdsOrder(bill) - } - if model.IsWaybillPlatformOwn(bill) { - if bill.Status == model.WaybillStatusDelivering && order.Status < model.OrderStatusEndBegin { - // 强制将订单状态置为配送中? - order.Status = model.OrderStatusDelivering - partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - } else { - if model.IsOrderDeliveryByStore(savedOrderInfo.order) { - if err := s.SelfDeliverDelivering(savedOrderInfo.order, bill.CourierMobile); err != nil { - partner.CurOrderManager.OnOrderMsg(order, "自送出设置失败", err.Error()) - } - s.notify3rdPartyWaybill(order, bill, isBillAlreadyCandidate) - } else { - s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap) - } - } - } else if !s.isBillCandidate(order, bill) && bill.WaybillVendorID != order.VendorID { - // 发生这种情况的原因就是两个接单事件几乎同时到达(来不及取消),也算正常 - s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - globals.SugarLogger.Infof("OnWaybillStatusChanged Accepted orderID:%s got multiple bill:%v", order.VendorOrderID, bill) - } - if isBillAlreadyCandidate && !s.isWaybillCourierSame(savedOrderInfo, bill) && !model.IsWaybillPlatformOwn(bill) { - s.notify3rdPartyWaybill(order, bill, isBillAlreadyCandidate) - } - order.Flag &= ^model.OrderFlagMaskFailedGetGoods - err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - case model.WaybillStatusAcceptCanceled: - s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID) - if s.isBillCandidate(order, bill) { - s.resetTimer(savedOrderInfo, bill, isPending) - if !isPending { - s.updateOrderByBill(order, nil, true) - } - } else if model.IsOrderHaveWaybill(order) { - s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - if !isPending { - globals.SugarLogger.Warnf("OnWaybillStatusChanged AcceptCanceled orderID:%s got multiple bill:%v, order details:%v", order.VendorOrderID, bill, order) - } - } - // case model.WaybillStatusCourierArrived: // do nothing - // s.resetTimer(savedOrderInfo, bill, isPending) - // if s.isBillCandidate(order, bill) { - // } else { - // // s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - // globals.SugarLogger.Infof("OnWaybillStatusChanged CourierArrived order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill) - // } - case model.WaybillStatusCanceled, model.WaybillStatusFailed: - s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID) - if s.isBillCandidate(order, bill) || order.WaybillVendorID == model.VendorIDUnknown { - s.resetTimer(savedOrderInfo, bill, isPending) - if !isPending { - if model.IsOrderHaveWaybill(order) { - s.updateOrderByBill(order, nil, true) - } - // 3方的运单取消才会重新发起创建3方订单,购物平台的运单取消后,它本身还会再创建新运单(NewWaybill事件有相应TIMER)),至少京东是这样的,暂时按京东的行为来 - // 现在发现饿百取消订单后不会再创建运单了,所以饿百运单取消也允许直接创建三方运单 - // 之前的条件是order.Status < model.OrderStatusDelivering,但像订单902322817000122确实有在配送中取消状态,改成非订单结束状态都可以 - // OrderStatusFinishedPickup状态的订单依赖于TIMER重新建运单 - if bill.DeliveryFlag&model.WaybillDeliveryFlagMaskActiveCancel == 0 { - if (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) && (bill.WaybillVendorID != order.VendorID /* || bill.WaybillVendorID == model.VendorIDEBAI*/) { - s.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - } - } - } - } - // case model.WaybillStatusDelivering: - // s.resetTimer(savedOrderInfo, bill, isPending) - // if s.isBillCandidate(order, bill) { - // // do nothing - // } else { - // // s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) - // globals.SugarLogger.Infof("OnWaybillStatusChanged Delivering order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill) - // } - case model.WaybillStatusDelivered: - s.resetTimer(savedOrderInfo, bill, isPending) - s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID) - if !isPending { - var err2 error - if !model.IsWaybillPlatformOwn(bill) { - if model.IsOrderDeliveryByStore(order) { - err2 = s.SelfDeliverDelivered(order, "") - } else if model.IsOrderDeliveryByPlatform(order) { - err2 = s.Swtich2SelfDelivered(order, "") - } - } - if err2 != nil { - partner.CurOrderManager.OnOrderMsg(order, "送达设置失败", err2.Error()) - } - } - if !s.isBillCandidate(order, bill) { - // 一般只会消息乱序才会到这里,即新订单消息在运单接单消息后到达 - // 典型的一个:1223633660228537567 - globals.SugarLogger.Infof("OnWaybillStatusChanged Delivered order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill) - if !model.IsOrderHaveWaybill(order) { - s.updateOrderByBill(order, bill, false) - } - } - if !isPending { - s.notify3rdPartyWaybill(order, bill, false) - } - // case model.WaybillStatusNeverSend: // 平台不配送,直接创建三方运单 - // s.resetTimer(savedOrderInfo, bill, isPending) - // s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID) - // if order.WaybillVendorID == model.VendorIDUnknown { - // s.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - // } - default: - s.resetTimer(savedOrderInfo, bill, isPending) - } - // s.updateBillsInfo(savedOrderInfo, bill) // 更新可能的运单状态变化 - } - // } - } - return err -} - -func (s *DefScheduler) cancelWaybillNotInStoreOpentime(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - if savedOrderInfo != nil { - //TODO 2020-09-07 不在门店的营业时间内取消运单 - order := savedOrderInfo.order - if savedOrderInfo.storeDetail != nil { - if savedOrderInfo.storeDetail.OpenTime1 != 0 && savedOrderInfo.storeDetail.CloseTime1 != 0 { - time1 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.OpenTime1, time.Now()) - time2 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.CloseTime1, time.Now()) - if time.Now().Sub(time1) < 0 || time.Now().Sub(time2) > 0 { - if savedOrderInfo.storeDetail.OpenTime2 != 0 && savedOrderInfo.storeDetail.CloseTime2 != 0 { - time3 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.OpenTime2, time.Now()) - time4 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.CloseTime2, time.Now()) - if time.Now().Sub(time3) < 0 || time.Now().Sub(time4) > 0 { - s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonNotInStoreOpenTime) - } - } else { - s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonNotInStoreOpenTime) - } - } - } - } - } - return err -} - -func (s *DefScheduler) sendCourierOrderSMS(bill *model.Waybill) (err error) { - err = smsmsg.NotifyNewCourierOrder(bill) - return err -} - -func (s *DefScheduler) isWaybillCourierSame(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - return savedOrderInfo.waybills[bill.WaybillVendorID] != nil && savedOrderInfo.waybills[bill.WaybillVendorID].CourierMobile == bill.CourierMobile -} - -func (s *DefScheduler) addWaybill2Map(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) { - if _, ok := savedOrderInfo.waybills[bill.WaybillVendorID]; ok { - if !model.IsWaybillPlatformOwn(bill) && savedOrderInfo.order.StoreID != 666666 { // 购买平台重复发相同号的新运单是正常的,京东就是 - globals.SugarLogger.Warnf("addWaybill2Map bill:%v already exists", bill) - } - } - if bill.ID == 0 { - globals.SugarLogger.Infof("addWaybill2Map bill id is 0:%s", utils.Format4Output(bill, true)) - } - savedOrderInfo.waybills[bill.WaybillVendorID] = bill -} - -func (s *DefScheduler) removeWaybillFromMap(savedOrderInfo *WatchOrderInfo, waybillVendorID int) { - if _, ok := savedOrderInfo.waybills[waybillVendorID]; ok { - delete(savedOrderInfo.waybills, waybillVendorID) - } -} - -func (s *DefScheduler) onEbaiWaybillCanceled(savedOrderInfo *WatchOrderInfo, excludeBill *model.Waybill) (err error) { - order := savedOrderInfo.order - timeout := time.Duration(ebaiCancelWaybillWarningMinute) * time.Minute - utils.AfterFuncWithRecover(timeout, func() { - jxutils.CallMsgHandlerAsync(func() { - ts := s.loadSavedOrderFromMap(model.Order2Status(order), true) - order := ts.order - globals.SugarLogger.Debugf("fire timer ebai waybill cancled, orderID:%s, status:%d", order.VendorOrderID, order.Status) - if order.Status < model.OrderStatusDelivering && order.LockStatus == model.OrderStatusLocked { - msgContent := fmt.Sprintf("门店:%d,订单:%s的平台运单被取消了,可能导致订单被取消,请重点关注,必要的话可以先手动转自送", jxutils.GetSaleStoreIDFromOrder(order), order.VendorOrderID) - s.notifyOrderStakeHolder(order, "", msgContent) - } - }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) - }) - err = s.createWaybillOn3rdProviders(savedOrderInfo, 0, excludeBill) - return err -} - -func (s *DefScheduler) createWaybillOn3rdProviders(savedOrderInfo *WatchOrderInfo, maxDeliveryFee int64, excludeBill *model.Waybill) (err error) { - order := savedOrderInfo.order - if order.VendorID == model.VendorIDELM { - return nil - } - if maxDeliveryFee == 0 { - maxDeliveryFee = getMaxDeliveryFee(order) - } - if !savedOrderInfo.isDeliveryCompetition { - globals.SugarLogger.Debugf("createWaybillOn3rdProviders orderID:%s门店没有设置配送竞争", order.VendorOrderID) - } - globals.SugarLogger.Debugf("createWaybillOn3rdProviders, orderID:%s, status:%d, maxDeliveryFee:%d, excludeBill:%v", order.VendorOrderID, order.Status, maxDeliveryFee, excludeBill) - if err = s.canOrderCreateWaybillNormally(order, savedOrderInfo); err == nil { - if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { - if savedOrderInfo.retryCount <= maxWaybillRetryCount { - savedOrderInfo.isNeedCreate3rdWaybill = true - excludeVendorIDs := savedOrderInfo.GetWaybillVendorIDs() - //TODO 取消京西不自动发美团 2020-06-02 - // if order.VendorID == model.VendorIDJX { - // excludeVendorIDs = append(excludeVendorIDs, model.VendorIDMTPS) - // } - //TODO 2020-08-18 饿百美团订单暂时不自动发送三方 - if order.VendorID == model.VendorIDMTWM || order.VendorID == model.VendorIDEBAI { - if order.DeliveryType != model.OrderDeliveryTypeStoreSelf { - excludeVendorIDs = append(excludeVendorIDs, model.VendorIDMTPS, model.VendorIDDada) - } - } - - if savedOrderInfo != nil { - //TODO 2020-07-21 发单时间要在门店的营业时间内 - if savedOrderInfo.storeDetail != nil { - if savedOrderInfo.storeDetail.OpenTime1 != 0 && savedOrderInfo.storeDetail.CloseTime1 != 0 { - time1 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.OpenTime1, time.Now()) - time2 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.CloseTime1, time.Now()) - if time.Now().Sub(time1) < 0 || time.Now().Sub(time2) > 0 { - if savedOrderInfo.storeDetail.OpenTime2 != 0 && savedOrderInfo.storeDetail.CloseTime2 != 0 { - time3 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.OpenTime2, time.Now()) - time4 := jxutils.JxOperationTime2TimeByDate(savedOrderInfo.storeDetail.CloseTime2, time.Now()) - if time.Now().Sub(time3) < 0 || time.Now().Sub(time4) > 0 { - err = fmt.Errorf("不在门店营业时间范围内!") - } - } else { - err = fmt.Errorf("不在门店营业时间范围内!") - } - } - } - } - } - if err == nil { - if _, err = s.CreateWaybillOnProviders4SavedOrder(jxcontext.AdminCtx, savedOrderInfo, nil, excludeVendorIDs, false, maxDeliveryFee); err == nil { - savedOrderInfo.retryCount++ - } - } - } else { - errMsg := fmt.Sprintf("订单:%s已经自动创建过了%d次运单,仍然无法配送,请关注!", order.VendorOrderID, savedOrderInfo.retryCount) - err = fmt.Errorf(errMsg) - s.notifyOrderStakeHolder(order, "", errMsg) - globals.SugarLogger.Infof("createWaybillOn3rdProviders [运营2]同一订单orderID:%s尝试了%d次创建运单失败, 停止调度,如果还需要发单,请人工处理", order.VendorOrderID, savedOrderInfo.retryCount) - } - } else { - globals.SugarLogger.Debugf("createWaybillOn3rdProviders, orderID:%s, store:%d dont't support 3rd delivery platform", order.VendorOrderID, jxutils.GetSaleStoreIDFromOrder(order)) - } - if err != nil { - partner.CurOrderManager.OnOrderMsg(order, "自动创建三方运单失败", err.Error()) - } - } else { - err = nil - } - return err -} - -func (s *DefScheduler) cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) { - globals.SugarLogger.Debugf("cancelOtherWaybillsCheckOrderDeliveryFlag, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep) - if (savedOrderInfo.order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { - err = s.cancelOtherWaybills(savedOrderInfo, bill2Keep, cancelReasonID, cancelReason) - } else { - globals.SugarLogger.Debugf("cancelOtherWaybillsCheckOrderDeliveryFlag, orderID:%s, bill:%v stop schedule", savedOrderInfo.order.VendorOrderID, bill2Keep) - } - return err -} - -func (s *DefScheduler) solutionJdsOrder(bill *model.Waybill) (err error) { - if len(bill.VendorOrderID) > 12 { - //表示此订单是京东商城2次转移的订单,不用出库,但要去修改运单号 - // if utils.Str2Int(bill.VendorOrderID[12:len(bill.VendorOrderID)]) > 2 { - // err = jdshop.CurPurchaseHandler.OrderTransfer(jxcontext.AdminCtx, bill.VendorOrderID, bill.VendorWaybillID, true) - // if err != nil { - // globals.SugarLogger.Errorf("京东商城订单自动转移失败!", err) - // } - // } else { - // err = jdshop.CurPurchaseHandler.OrderExport(jxcontext.AdminCtx, bill.VendorOrderID, bill.VendorWaybillID, true) - // } - } - return err -} - -func (s *DefScheduler) cancelOtherWaybills(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) { - globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep) - for _, v := range savedOrderInfo.waybills { - if v.Status < model.WaybillStatusEndBegin && - !model.IsWaybillPlatformOwn(v) && - (bill2Keep == nil || !(v.WaybillVendorID == bill2Keep.WaybillVendorID && v.VendorWaybillID == bill2Keep.VendorWaybillID)) { - err2 := s.CancelWaybill(v, cancelReasonID, cancelReason) - if err2 == nil { - // 在这里就从map里删除,而不是等收到运单结束事件才删除,可避免不必要的重复取消(第二次取消还会失败) - s.removeWaybillFromMap(savedOrderInfo, v.WaybillVendorID) - } else { - // 至少返回一个错误 - if err == nil { - err = err2 - } - partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "取消三方运单失败", err2.Error()) - } - } - } - return err -} - -func (s *DefScheduler) swtich2SelfDeliverWithRetry(savedOrderInfo *WatchOrderInfo, bill *model.Waybill, retryCount int, duration time.Duration) { - order := savedOrderInfo.order - globals.SugarLogger.Debugf("swtich2SelfDeliverWithRetry orderID:%s", order.VendorOrderID) - if order.WaybillVendorID != order.VendorID { - if err := s.Swtich2SelfDeliver(order, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation { - globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry failed, bill:%v, err:%v", bill, err) - if retryCount > 0 { - utils.AfterFuncWithRecover(duration, func() { - jxutils.CallMsgHandlerAsync(func() { - s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, retryCount-1, duration) - }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) - }) - } else { - errStr := fmt.Sprintf("订单:%s转自配送失败, 错误信息:%v", order.VendorOrderID, err) - globals.SugarLogger.Info(errStr) - if s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonSwitch2SelfFailed, partner.CancelWaybillReasonStrSwitch2SelfFailed) == nil { - // 转自送失败的取消,要将订单中的运单状态更新 - if s.isBillCandidate(order, bill) { - s.updateOrderByBill(order, nil, false) - } - } - // todo 之前这里为什么要设置OrderDeliveryFlagMaskScheduleDisabled标志呢 - // order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled - // partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"}) - - partner.CurOrderManager.OnOrderMsg(order, "转商家自配送失败", errStr) - } - } else { - s.notify3rdPartyWaybill(order, bill, false) - s.removeWaybillFromMap(savedOrderInfo, order.VendorID) - partner.CurOrderManager.OnOrderMsg(order, "转自送成功", "") - } - } else { - s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrNotAcceptIntime) - order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled - partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"}) - - // 进到这里的原因是,在这个时间点,购物平台物流已经抢单(但抢单消息还没有被收到),所以转自送会失败 (比如:818810379000941),更好的做法应该是判断Swtich2SelfDeliver的返回值,这种情况下就不得试了 - globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry orderID:%s status is wrong(maybe purchase platform accepted waybill)", order.VendorOrderID) - partner.CurOrderManager.OnOrderMsg(order, "转商家自配送失败", "平台物流已接单") - // globals.SugarLogger.Warnf("swtich2SelfDeliverWithRetry orderID:%s status is wrong, order details:%v", order.VendorOrderID, order) - } -} - -// 这个函数这样写的原因是适应一些消息错序 -func (s *DefScheduler) loadSavedOrderFromMap(status *model.OrderStatus, isForceLoad bool) *WatchOrderInfo { - globals.SugarLogger.Debugf("loadSavedOrderFromMap status:%v", status) - universalOrderID := jxutils.ComposeUniversalOrderID(status.RefVendorOrderID, status.RefVendorID) - var realSavedInfo *WatchOrderInfo - if savedInfo, ok := s.orderMap.Load(universalOrderID); ok { - realSavedInfo = savedInfo.(*WatchOrderInfo) - } else { - realSavedInfo = NewWatchOrderInfo(nil) - s.orderMap.StoreWithTimeout(universalOrderID, realSavedInfo, orderMapStoreMaxTime) - } - if isForceLoad { - if order, err := partner.CurOrderManager.LoadOrder(status.RefVendorOrderID, status.RefVendorID); err == nil { - realSavedInfo.SetOrder(order) - } else { - realSavedInfo.SetOrder(&model.GoodsOrder{ - VendorOrderID: status.RefVendorOrderID, - VendorID: status.RefVendorID, - Status: status.Status, - StatusTime: status.StatusTime, - OrderCreatedAt: status.StatusTime, - WaybillVendorID: model.VendorIDUnknown, - }) - globals.SugarLogger.Infof("loadSavedOrderFromMap can not load order orderID:%s with error:%v", status.RefVendorOrderID, err) - } - } - return realSavedInfo -} - -func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Waybill, isPending bool) { - order := savedOrderInfo.order - status := order.Status - statusType := scheduler.TimerStatusTypeOrder - vendorID := order.VendorID - statusTime := order.StatusTime - if bill != nil { - status = bill.Status - statusType = scheduler.TimerStatusTypeWaybill - vendorID = bill.WaybillVendorID - statusTime = bill.StatusTime - } - globals.SugarLogger.Debugf("resetTimer, orderID:%s statusType:%d status:%d", order.VendorOrderID, statusType, status) - config := s.mergeOrderStatusConfig(savedOrderInfo, statusTime, statusType, status) - - stopStatusType := statusType - stopStatus := status - if statusType == scheduler.TimerStatusTypeOrder { - if status >= model.OrderStatusDelivering { - stopStatusType = -1 - stopStatus = -1 - } - } - if config == nil || config.TimerType != partner.TimerTypeByPass { - savedOrderInfo.StopTimer(stopStatusType, -1, stopStatus) - } - - if config != nil && config.TimeoutAction != nil && config.TimerType != partner.TimerTypeByPass { - if config.CallShouldSetTimer(savedOrderInfo, bill) { - timeout := config.GetRefTimeout(statusTime, order.OrderCreatedAt) - if config.TimeoutGap != 0 { - timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second - } - if isPending && timeout < pendingOrderTimerMaxSecond*time.Second { // 如果是PENDING的订单,则将其分布到2--5秒内,让后续事件有机会执行 - timeout = time.Duration(jxutils.MapValue2Scope(int64(timeout), -pendingOrderTimerMinMinSecond*1000, pendingOrderTimerMaxSecond*1000, pendingOrderTimerMinSecond*1000, pendingOrderTimerMaxSecond*1000)) * time.Millisecond - } else if timeout < 0 { - timeout = 0 - } - if timeout == 0 { - config.CallTimeoutAction(savedOrderInfo, bill) - } else { - timerName := "" - if statusType == model.OrderTypeOrder { - timerName = model.OrderStatusName[status] - } else if statusType == model.OrderTypeWaybill { - timerName = model.WaybillStatusName[status] - } - timerInfo := &tTimerInfo{ - statusType: statusType, - vendorID: vendorID, - status: status, - timerTime: time.Now().Add(timeout), - } - timerInfo.timer = utils.AfterFuncWithRecover(timeout, func() { - jxutils.CallMsgHandlerAsync(func() { - globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID) - ts := s.loadSavedOrderFromMap(model.Order2Status(order), true) - config.CallTimeoutAction(ts, bill) - timerInfo.timer = nil - ts.StopTimer(statusType, vendorID, status) - }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) - }) - savedOrderInfo.AddTimer(timerInfo) - } - globals.SugarLogger.Debugf("resetTimer, orderID:%s, statusType:%d, status:%d, timeout:%v", order.VendorOrderID, statusType, status, timeout) - } else { - globals.SugarLogger.Debugf("resetTimer, orderID:%s, statusType:%d, status:%d, should not set timer", order.VendorOrderID, statusType, status) - } - } else { - globals.SugarLogger.Debugf("resetTimer bypass2, orderID:%s statusType:%d status:%v, config:%s", order.VendorOrderID, statusType, status, utils.Format4Output(config, true)) - } -} - -func (s *DefScheduler) stopTimer(savedOrderInfo *WatchOrderInfo) { - savedOrderInfo.StopTimer(-1, -1, -1) -} - -// func (s *DefScheduler) stopTimer(savedOrderInfo *WatchOrderInfo) { -// if savedOrderInfo.timer != nil { -// globals.SugarLogger.Debugf("stopTimer orderID:%s", savedOrderInfo.order.VendorOrderID) -// savedOrderInfo.timer.Stop() -// savedOrderInfo.timerStatus = model.OrderStatusUnknown -// savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown -// savedOrderInfo.timer = nil -// } -// } - -// func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Waybill, isPending bool) { -// order := savedOrderInfo.order -// status := order.Status -// statusType := scheduler.TimerStatusTypeOrder -// statusTime := order.StatusTime -// if bill != nil { -// status = bill.Status -// statusType = scheduler.TimerStatusTypeWaybill -// statusTime = bill.StatusTime -// } -// globals.SugarLogger.Debugf("resetTimer, orderID:%s statusType:%d status:%d", order.VendorOrderID, statusType, status) -// if isStatusNewer(order.VendorID, savedOrderInfo.timerStatusType, savedOrderInfo.timerStatus, statusType, status) { // 新设置的TIMER不能覆盖状态在其后的TIMER,如果状态回绕,需要注意 -// config := s.mergeOrderStatusConfig(savedOrderInfo, statusTime, statusType, status) -// if config == nil || config.TimerType != partner.TimerTypeByPass { -// s.stopTimer(savedOrderInfo) -// } -// if config != nil && config.TimeoutAction != nil && config.TimerType != partner.TimerTypeByPass { -// if config.CallShouldSetTimer(savedOrderInfo, bill) { -// timeout := config.GetRefTimeout(statusTime, order.OrderCreatedAt) -// if config.TimeoutGap != 0 { -// timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second -// } -// if isPending && timeout < pendingOrderTimerMaxSecond*time.Second { // 如果是PENDING的订单,则将其分布到2--5秒内,让后续事件有机会执行 -// timeout = time.Duration(jxutils.MapValue2Scope(int64(timeout), -pendingOrderTimerMinMinSecond*1000, pendingOrderTimerMaxSecond*1000, pendingOrderTimerMinSecond*1000, pendingOrderTimerMaxSecond*1000)) * time.Millisecond -// } else if timeout < 0 { -// timeout = 0 -// } -// if timeout == 0 { -// config.CallTimeoutAction(savedOrderInfo, bill) -// } else { -// timerName := "" -// if statusType == scheduler.TimerStatusTypeOrder { -// timerName = model.OrderStatusName[status] -// } else if statusType == scheduler.TimerStatusTypeWaybill { -// timerName = model.WaybillStatusName[status] -// } -// savedOrderInfo.timerStatusType = statusType -// savedOrderInfo.timerStatus = status -// savedOrderInfo.timerTime = time.Now().Add(timeout) -// savedOrderInfo.timer = utils.AfterFuncWithRecover(timeout, func() { -// jxutils.CallMsgHandlerAsync(func() { -// globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID) -// savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), true) -// config.CallTimeoutAction(savedOrderInfo, bill) -// savedOrderInfo.timerStatus = 0 -// savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown -// }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) -// }) -// } -// globals.SugarLogger.Debugf("resetTimer, orderID:%s, statusType:%d, status:%d, timeout:%v", order.VendorOrderID, statusType, status, timeout) -// } else { -// globals.SugarLogger.Debugf("resetTimer, orderID:%s, statusType:%d, status:%d, should not set timer", order.VendorOrderID, statusType, status) -// } -// } else { -// globals.SugarLogger.Debugf("resetTimer bypass2, orderID:%s statusType:%d status:%v, config:%s", order.VendorOrderID, statusType, status, utils.Format4Output(config, true)) -// } -// } else { -// globals.SugarLogger.Debugf("resetTimer bypass1, orderID:%s statusType:%d status:%v", order.VendorOrderID, statusType, status) -// } -// } - -// func isStatusNewer(vendorID int, curStatusType, curStatus, statusType, status int) bool { -// // 拣货完成及之前的订单事件TIMER不能覆盖运单TIMER(一般是消息错序引起的) -// // 美团订单在接单后就会收到新运单事件,因当前只支持一个TIMER,暂时舍弃三方配送调度,而要自动拣货调度 -// if vendorID != model.VendorIDMTWM { -// if curStatusType == scheduler.TimerStatusTypeWaybill && statusType == scheduler.TimerStatusTypeOrder && status <= model.OrderStatusFinishedPickup { -// return false -// } -// } else { -// return statusType == scheduler.TimerStatusTypeOrder && status >= curStatus -// } -// if curStatusType == scheduler.TimerStatusTypeWaybill { -// return curStatus != status -// } -// return curStatusType != statusType || status >= curStatus -// } - -func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, statusTime time.Time, statusType, status int) (retVal *StatusActionConfig) { - s.locker.RLock() - defer func() { - s.locker.RUnlock() - }() - defConfig := s.defWorkflowConfig[statusType][status] - if defConfig == nil { - return nil - } - retVal = &StatusActionConfig{} - *retVal = *defConfig - - order := savedOrderInfo.order - var vendorActionParams *partner.StatusActionParams - - // 非立即达订单timer设置 - if model.IsOrderSolid(order) { - if order.BusinessType != model.BusinessTypeImmediate { // 非立即达订单 - if utils.IsTimeZero(order.ExpectedDeliveredTime) { - globals.SugarLogger.Warnf("mergeOrderStatusConfig orderID:%s 非立即达订单没有预计送达时间, orderDetail:%s", order.VendorOrderID, utils.Format4Output(order, false)) - } else if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusFinishedPickup { // 这个只针对自配送门店才有效 - vendorActionParams = &partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseNow, - Timeout: order.ExpectedDeliveredTime.Sub(time.Now()) - dingShiDaAheadTime, - TimeoutGap: -1, - } - timeout := statusTime.Sub(time.Now()) + minMinute2Schedule3rdCarrier*time.Minute - if vendorActionParams.GetRefTimeout(statusTime, order.OrderCreatedAt) < timeout { // 如果非立即达订单,根据ExpectedDeliveredTime算出来的timeout太早 - vendorActionParams.Timeout = timeout - vendorActionParams.TimeoutGap = 0 - } - } else if statusType == scheduler.TimerStatusTypeWaybill && status == model.WaybillStatusNew { // 因为有些平台(比如美团外卖)的定时达单,很早就创建运单了 - vendorActionParams = &partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseNow, - Timeout: order.ExpectedDeliveredTime.Sub(time.Now()) - dingShiDaAheadTime, - TimeoutGap: -1, - } - } - } - } - if vendorActionParams == nil { - vendorActionParams = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).GetStatusActionTimeout(order, statusType, status) - } - // 自动拣货TIMER - if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted { - if savedOrderInfo.autoPickupTimeoutMinute > 0 { - if utils.IsTimeZero(order.PickDeadline) { // 没有最后拣货时间,正推,以订单中的配置为准 - if utils.IsTimeZero(order.ExpectedDeliveredTime) || order.BusinessType == model.BusinessTypeImmediate { // 立即达或没有最后送达时间 - // 以缺省配置或平台相关中的配置为准 - } else { // 非立即达且有最后送达时间 - timeout := time2AutoPickupMin - if savedOrderInfo.autoPickupTimeoutMinute > 1 { - timeout = time.Duration(savedOrderInfo.autoPickupTimeoutMinute) * time.Minute - } - vendorActionParams = &partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseNow, - Timeout: order.ExpectedDeliveredTime.Add(-time2Delivered).Sub(time.Now()) + timeout, - } - } - } else { // 有最后拣货时间,反推 - timeout := order.PickDeadline.Sub(time.Now()) - (time2AutoPickupAhead + second2AutoPickupGap*time.Second) - realSecond2AutoPickupGap := second2AutoPickupGap - if realSecond2AutoPickupGap > int(timeout/time.Second) { - realSecond2AutoPickupGap = int(timeout / time.Second) - if realSecond2AutoPickupGap < 0 { - realSecond2AutoPickupGap = 0 - } - } - vendorActionParams = &partner.StatusActionParams{ - TimerType: partner.TimerTypeBaseNow, - Timeout: timeout, - TimeoutGap: realSecond2AutoPickupGap, - } - } - } - } - - if vendorActionParams != nil { - retVal.Timeout = vendorActionParams.Timeout - if vendorActionParams.TimeoutGap >= 0 { - retVal.TimeoutGap = vendorActionParams.TimeoutGap - } - if vendorActionParams.TimerType != partner.TimerTypeNoOverride { - retVal.TimerType = vendorActionParams.TimerType - } - } - return retVal -} - -func (s *DefScheduler) handleAutoAcceptOrder(orderID string, vendorID int, userMobile string, jxStoreID int, db orm.Ormer, handler func(accepted bool) error) int { - globals.SugarLogger.Debugf("handleAutoAcceptOrder order:%s, vendorID:%d", orderID, vendorID) - handleType := 0 - if userMobile != "" { - if db == nil { - db = orm.NewOrm() - } - user := &legacymodel.BlackClient{ - Mobile: userMobile, - } - if err := db.Read(user, "Mobile"); err != nil { - if err != orm.ErrNoRows { - globals.SugarLogger.Errorf("handleAutoAcceptOrder orderID:%s, read data error:%v, data:%v, vendorID:%d", orderID, err, user, vendorID) - } - // 在访问数据库出错的情况下,也需要自动接单 - handleType = 1 - } else { - // 强制拒单 - globals.SugarLogger.Infof("handleAutoAcceptOrder force reject order:%s, vendorID:%d", orderID, vendorID) - handleType = -1 - } - } else { - globals.SugarLogger.Infof("handleAutoAcceptOrder order:%s, vendorID:%d, mobile is empty, should accept order", orderID, vendorID) - handleType = 1 - } - if handleType == 1 { - handler(true) - } else if handleType == -1 { - handler(false) - } - return handleType -} - -func (s *DefScheduler) updateOrderByBill(order *model.GoodsOrder, bill *model.Waybill, revertStatus bool) { - if order.Status > model.OrderStatusEndBegin { - return - } - updateFields := []string{ - "WaybillVendorID", - "VendorWaybillID", - } - if bill == nil { - order.WaybillVendorID = model.VendorIDUnknown - order.VendorWaybillID = "" - } else { - order.WaybillVendorID = bill.WaybillVendorID - order.VendorWaybillID = bill.VendorWaybillID - if bill.Status == model.WaybillStatusDelivered { - storeDetail, _ := partner.CurOrderManager.LoadStoreDetail(jxutils.GetSaleStoreIDFromOrder(order), order.VendorID) - if storeDetail != nil { - jxutils.RefreshOrderEarningPrice3(order, storeDetail.PayPercentage, bill) - updateFields = append(updateFields, "NewEarningPrice") - } - } - } - if revertStatus { - order.Status = model.OrderStatusFinishedPickup - updateFields = append(updateFields, "Status") - } - partner.CurOrderManager.UpdateOrderFields(order, updateFields) -} - -func (s *DefScheduler) updateBillsInfo(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (isBillExist bool) { - if savedOrderInfo != nil { - if savedBill := savedOrderInfo.waybills[bill.WaybillVendorID]; savedBill != nil { - isBillExist = true - // if savedBill.Status > bill.Status { - // bill.Status = savedBill.Status - // } else if bill.Status > savedBill.Status { - // savedBill.Status = bill.Status - // } - } - savedOrderInfo.waybills[bill.WaybillVendorID] = bill - } - return isBillExist -} - -func (s *DefScheduler) autoPickupGood(savedOrderInfo *WatchOrderInfo) (err error) { - order := savedOrderInfo.order - if err = s.PickupGoods(order, model.IsOrderDeliveryByStore(order), ""); err == nil { - order.DeliveryFlag |= model.OrderDeliveryFlagMaskAutoPickup - partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"}) - } else if err == scheduler.ErrOrderStatusAlreadySatisfyCurOperation { - err = nil - } - return err -} - -func getWaybillTip(order *model.GoodsOrder) (tipFee int64) { - if !utils.IsPtrTimeZero(order.DeliveryFeeFrom) { - startTime := order.DeliveryFeeFrom.Add(minAddWaybillTipMinute * time.Minute) - timeGap1 := time.Now().Sub(startTime) - if timeGap1 > 0 { - timeGap := int64(timeGap1/(addWaybillTipGap*time.Minute)) + 1 - tipFee = timeGap * 100 - if tipFee > maxWaybillTipMoney { - tipFee = maxWaybillTipMoney - } - } - } - return tipFee -} - -func getMaxDeliveryFee(order *model.GoodsOrder) (maxDeliveryFee int64) { - if order.VendorID == model.VendorIDJX { - maxDeliveryFee = maxJxStoreDeliveryFee - } else { - orderBaseFreightMoney := order.BaseFreightMoney - if orderBaseFreightMoney == 0 { - orderBaseFreightMoney = baseWaybillFee - } - maxDeliveryFee = orderBaseFreightMoney + order.DistanceFreightMoney + getWaybillTip(order) + baseWaybillAddFee - if maxDeliveryFee < ebaiCancelWaybillMaxFee && - (order.VendorID == model.VendorIDEBAI || - order.DeliveryType == model.OrderDeliveryTypeStoreSelf) { - maxDeliveryFee = ebaiCancelWaybillMaxFee - } - } - return maxDeliveryFee -} - -func isNeedWatchWaybillTip(order *model.GoodsOrder) bool { - return order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 && // 没有转出 - order.DeliveryType == model.OrderDeliveryTypePlatform && // 订单配送类型为平台 - !utils.IsPtrTimeZero(order.DeliveryFeeFrom) // 已经有了开始计费时间 -} - -func isNeedWatch3rdWaybill(order *model.GoodsOrder) bool { - return (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusDelivering) && // 订单状态 - order.DeliveryFlag&model.OrderDeliveryFlagMaskScheduleDisabled == 0 && // 没有禁止调度 - !model.IsOrderHaveWaybill(order) && // 没有有效运单 - order.LockStatus == model.OrderStatusUnknown -} - -func (s *DefScheduler) setWatchOrderWaybills(savedOrderInfo *WatchOrderInfo, duration time.Duration) { - if utils.IsPtrTimeZero(savedOrderInfo.watchWabillStartAt) { - savedOrderInfo.watchWabillStartAt = utils.Time2Pointer(time.Now()) - utils.AfterFuncWithRecover(5*time.Minute, func() { - jxutils.CallMsgHandlerAsync(func() { - savedOrderInfo.watchWabillStartAt = nil - s.watchOrderWaybills(savedOrderInfo) - }, jxutils.ComposeUniversalOrderID(savedOrderInfo.order.VendorOrderID, savedOrderInfo.order.VendorID)) - }) - } -} - -func (s *DefScheduler) saveDeliveryFeeFromAndStartWatch(savedOrderInfo *WatchOrderInfo, statusTime time.Time) { - order := savedOrderInfo.order - if utils.IsPtrTimeZero(order.DeliveryFeeFrom) { - order.DeliveryFeeFrom = utils.Time2Pointer(statusTime) - partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFeeFrom"}) - } - duration := order.DeliveryFeeFrom.Add(minAddWaybillTipMinute * time.Minute).Sub(time.Now()) - if duration <= 0 { - duration = 5 * time.Second - } - s.setWatchOrderWaybills(savedOrderInfo, duration) -} - -func (s *DefScheduler) watchOrderWaybills(savedOrderInfo *WatchOrderInfo) { - order2 := savedOrderInfo.order - if model.IsOrderDeliveryByPlatform(order2) && savedOrderInfo.isDeliveryCompetition || - model.IsOrderDeliveryByStore(order2) { - if order, err := partner.CurOrderManager.LoadOrder(order2.VendorOrderID, order2.VendorID); err == nil { - savedOrderInfo.SetOrder(order) - if isNeedWatch3rdWaybill(order) { - if isNeedWatchWaybillTip(order) { - // tipFee := getWaybillTip(order) - // if tipFee > order.WaybillTipMoney { - // vendorStatus := fmt.Sprintf("应设置小费:%s", jxutils.IntPrice2StandardCurrencyString(tipFee)) - // remark := "" - // if false { - // err = s.SetOrderWaybillTipByOrder(jxcontext.AdminCtx, order, tipFee) - // if err == nil { - // vendorStatus += "成功" - // } else { - // vendorStatus += "失败" - // remark = fmt.Sprint(err) - // } - // } else { - // vendorStatus += "空操作" - // } - // partner.CurOrderManager.OnOrderMsg(order, vendorStatus, remark) - // } - - // if handler, ok := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).(partner.IAddWaybillTip); ok && handler != nil { - // var remark string - // tipFee := getWaybillTip(order) - // vendorStatus := fmt.Sprintf("设置小费:%s", jxutils.IntPrice2StandardCurrencyString(tipFee)) - // tipFee2Add := tipFee - order.WaybillTipMoney - // vendorStatus += fmt.Sprintf(", 本次添加:%s", jxutils.IntPrice2StandardCurrencyString(tipFee2Add)) - // if false { //tipFee2Add > 0 { - // err := handler.UpdateWaybillTip(jxcontext.AdminCtx, order.VendorOrgCode, order.VendorStoreID, order.VendorOrderID, order.VendorOrderID, "", "", tipFee) - // if err == nil { - // vendorStatus += "成功" - // order.WaybillTipMoney = tipFee - // partner.CurOrderManager.UpdateOrderFields(order, []string{"WaybillTipMoney"}) - // } else { - // vendorStatus += "失败" - // remark = fmt.Sprint(err) - // } - // } else { - // vendorStatus += "空操作" - // } - // partner.CurOrderManager.OnOrderMsg(order, vendorStatus, remark) - // } - } - if savedOrderInfo.isNeedCreate3rdWaybill { - s.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - } - s.setWatchOrderWaybills(savedOrderInfo, 5*time.Minute) - } - } - } -} - -func (s *DefScheduler) isBillCandidate(order *model.GoodsOrder, bill *model.Waybill) bool { - return order.WaybillVendorID == bill.WaybillVendorID && order.VendorWaybillID == bill.VendorWaybillID -} - -func (s *DefScheduler) ProxyCancelWaybill(order *model.GoodsOrder, bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) { - globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s", order.VendorOrderID) - if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { - if err = s.CancelWaybill(bill, cancelReasonID, cancelReason); err != nil { - partner.CurOrderManager.OnOrderMsg(order, "取消三方运单失败", err.Error()) - } - return err - } - globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s stop schedule, bypass CancelWaybill", order.VendorOrderID) - return nil -} - -func OnDefSchConfChanged(key, value string) { - if key == time2Schedule3rdCarrierKey { - waitMinutes := int(utils.Str2Int64WithDefault(value, minute2Schedule3rdCarrier)) - if waitMinutes >= 0 { - FixedScheduler.locker.Lock() - defer func() { - FixedScheduler.locker.Unlock() - }() - conf := FixedScheduler.defWorkflowConfig[1][model.WaybillStatusNew] - conf.Timeout = time.Duration(waitMinutes) * time.Minute - globals.SugarLogger.Debugf("defsch wait miniutes 4 3rd delivery changed to:%d", waitMinutes) - } - } -} - -func setFakeActualPayPrice(order *model.GoodsOrder) (newOrder *model.GoodsOrder) { - orderCopy := *order - storeDetail, err := dao.GetStoreDetail(dao.GetDB(), order.JxStoreID, order.VendorID) - if err == nil { - if storeDetail.PayPercentage < 50 { - orderCopy.ActualPayPrice = order.TotalShopMoney * (100 - int64(storeDetail.PayPercentage/2)) / 100 - } else { - orderCopy.ActualPayPrice = order.EarningPrice - } - } - newOrder = &orderCopy - return newOrder -} - -func (s *DefScheduler) notifyNewOrder(order *model.GoodsOrder) { - if order.Flag&model.OrderFlagMaskFake == 0 { - utils.CallFuncAsync(func() { - order = setFakeActualPayPrice(order) - netprinter.PrintOrderByOrder(jxcontext.AdminCtx, order) - //目前暂且认为AdjustCount > 0 就是调整单 - if order.AdjustCount > 0 { - weixinmsg.NotifyAdjustOrder(order) - } else { - weixinmsg.NotifyNewOrder(order) - } - OrderProfitWarning(order) - if order.VendorID != model.VendorIDJDShop { - smsmsg.NotifyNewOrder(order) - smsmsg.NotifyNewUserOrder(order) - } - if order.VendorID != model.VendorIDEBAI { - push.NotifyNewOrder(order) - } - }) - } -} - -func (s *DefScheduler) notifyUserApplyCancel(order *model.GoodsOrder, cancelReason string) { - utils.CallFuncAsync(func() { - order = setFakeActualPayPrice(order) - weixinmsg.NotifyUserApplyCancel(order, cancelReason) - }) -} - -func (s *DefScheduler) notifyOrderCanceled(order *model.GoodsOrder) { - if order.Flag&model.OrderFlagMaskFake == 0 { - utils.CallFuncAsync(func() { - order = setFakeActualPayPrice(order) - weixinmsg.NotifyOrderCanceled(order) - smsmsg.NotifyOrderCanceled(order) - push.NotifyOrderCanceled(order) - }) - } -} - -func (s *DefScheduler) notify3rdPartyWaybill(order *model.GoodsOrder, bill *model.Waybill, isBillAlreadyCandidate bool) { - if order.Flag&model.OrderFlagMaskFake == 0 { - utils.CallFuncAsync(func() { - weixinmsg.NotifyWaybillStatus(bill, order, isBillAlreadyCandidate) - }) - } -} - -func isOrderCanSwitch2SelfDeliver(order *model.GoodsOrder) (isCan bool) { - isCan = true - if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil { - isCan, _ = handler.CanSwitch2SelfDeliver(order) - } - return isCan -} - -//订单预计利润若低于0,则向门店运营负责人发送钉钉消息 -func OrderProfitWarning(order *model.GoodsOrder) { - var profit float64 - - db := dao.GetDB() - if order == nil { - return - } - if order.TotalShopMoney == 0 { - globals.SugarLogger.Debugf("OrderProfitWarning TotalShopMoney=0 orderID:%s", order.VendorOrderID) - return - } - storeID := jxutils.GetShowStoreIDFromOrder(order) - storeDetail, err := dao.GetStoreDetail(db, storeID, order.VendorID) - if storeDetail != nil && err == nil { - payPercentage := storeDetail.PayPercentage - if beego.BConfig.RunMode == "jxgy" { - if payPercentage >= 50 { - profit = utils.Str2Float64(utils.Int64ToStr(order.TotalShopMoney-order.EarningPrice)) / 100 - } else { - profit = utils.Str2Float64(utils.Int64ToStr(order.TotalShopMoney*int64(payPercentage)/200)) / 100 - } - } else { - if payPercentage >= 50 { - profit = utils.Str2Float64(utils.Int64ToStr(order.TotalShopMoney-order.ShopPrice)) / 100 - } else { - profit = utils.Str2Float64(utils.Int64ToStr(order.TotalShopMoney*int64(payPercentage)/200)) / 100 - } - } - if profit < 0 { - operatorPhone, operatorName := getOrderOperatorInfo(order, storeDetail) - operatorRole := getOrderOperatorRoleInfo(order, storeDetail) - if operatorPhone != "" { - var ( - roleList []*authz.RoleInfo - userIDs []string - flag = false - ) - roleList = append(roleList, autils.NewRole(operatorRole, 0)) - userIDMap, err := cms.GetRolesUserList(jxcontext.AdminCtx, roleList) - noticeMsg := fmt.Sprintf("利润 :[%v],运营负责人:[%v],商家负责人:[%v],门店ID:[%v],平台门店ID[%v],门店名:[%v],订单序号:[%v],订单号(点击进入详情):%v", profit, operatorName, storeDetail.MarketManName, order.StoreID, order.VendorStoreID, order.StoreName, order.OrderSeq, globals.BackstageHost+"/#/ordermanager/"+order.VendorOrderID) - user, err := dao.GetUserByID(db, "mobile", operatorPhone) - if user != nil && err == nil { - for _, v := range userIDMap { - for _, vv := range v { - userIDs = append(userIDs, vv) - } - } - for _, v := range userIDs { - if v == user.UserID { - flag = true - } - } - if !flag { - userIDs = append(userIDs, user.UserID) - } - for _, v := range userIDs { - ddmsg.SendUserMessage(dingdingapi.MsgTyeText, v, "警告!此订单利润低于0", noticeMsg) - } - } - } - } - } -} - -func (s *DefScheduler) notifyOrderStakeHolder(order *model.GoodsOrder, msgTitle, msgContent string) (err error) { - userMobiles := []string{ - // "18180948107", - } - db := dao.GetDB() - storeDetail, err := dao.GetStoreDetail(db, jxutils.GetSaleStoreIDFromOrder(order), order.VendorID) - if err == nil { - operatorPhone, _ := getOrderOperatorInfo(order, storeDetail) - if operatorPhone != "" { - userMobiles = append(userMobiles, operatorPhone) - } - } - if len(userMobiles) > 0 { - if msgTitle == "" { - msgTitle = fmt.Sprintf("%s平台订单%s异常", model.VendorChineseNames[order.VendorID], order.VendorOrderID) - } - users, _, err := dao.GetUsers(db, 0, "", nil, nil, userMobiles, 0, 0) - if err == nil && len(users) > 0 { - var userIDs []string - for _, v := range users { - userIDs = append(userIDs, v.UserID) - } - ddmsg.SendUsersMessage(dingdingapi.MsgTyeText, userIDs, msgTitle, msgContent) - } - } - return err -} - -func getOrderOperatorInfo(order *model.GoodsOrder, storeDetail *dao.StoreDetail) (operatorPhone, operatorName string) { - switch order.VendorID { - case model.VendorIDJD: - operatorPhone = storeDetail.OperatorPhone - operatorName = storeDetail.OperatorName - case model.VendorIDMTWM: - operatorPhone = storeDetail.OperatorPhone2 - operatorName = storeDetail.OperatorName2 - case model.VendorIDEBAI: - operatorPhone = storeDetail.OperatorPhone3 - operatorName = storeDetail.OperatorName3 - } - return operatorPhone, operatorName -} - -func getOrderOperatorRoleInfo(order *model.GoodsOrder, storeDetail *dao.StoreDetail) (roleName string) { - switch order.VendorID { - case model.VendorIDJD: - roleName = storeDetail.OperatorRole - case model.VendorIDMTWM: - roleName = storeDetail.OperatorRole2 - case model.VendorIDEBAI: - roleName = storeDetail.OperatorRole3 - } - return roleName -} diff --git a/business/jxcallback/scheduler/defsch/defsch_afs.go b/business/jxcallback/scheduler/defsch/defsch_afs.go deleted file mode 100644 index 260aec703..000000000 --- a/business/jxcallback/scheduler/defsch/defsch_afs.go +++ /dev/null @@ -1,59 +0,0 @@ -package defsch - -import ( - "git.rosy.net.cn/jx-callback/business/jxutils" - "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" - push "git.rosy.net.cn/jx-callback/business/jxutils/unipush" - "git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg" - "git.rosy.net.cn/jx-callback/business/model" - "git.rosy.net.cn/jx-callback/business/msghub" - "git.rosy.net.cn/jx-callback/business/partner" - "git.rosy.net.cn/jx-callback/globals" -) - -var ( - autoRejectSkuMap = map[int]int{ - 33996: 1, - 33995: 1, - 33994: 1, - 33991: 1, - } -) - -func (s *DefScheduler) OnAfsOrderNew(order *model.AfsOrder, isPending bool) (err error) { - if order.Status == model.AfsOrderStatusWait4Approve { - if !isPending { - if isAutoRejectAfsOrder(order) { - if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil { - if err := handler.AgreeOrRefuseRefund(jxcontext.AdminCtx, order, partner.AfsApproveTypeRefused, "抱歉,蟹券不接受退货或换货"); err != nil { - globals.SugarLogger.Debugf("OnAfsOrderNew, orderID:%s, afsOrderID:%s failed with err:%v", order.VendorOrderID, order.AfsOrderID, err) - } - } - } - msghub.OnNewWait4ApproveAfsOrder(order) - weixinmsg.NotifyAfsOrderStatus(order) - push.NotifyAfsOrder(order) - } - } - return err -} - -func (s *DefScheduler) OnAfsOrderStatusChanged(order *model.AfsOrder, status *model.OrderStatus, isPending bool) (err error) { - if status.Status == model.AfsOrderStatusWait4ReceiveGoods { - if !isPending { - msghub.OnKeyAfsOrderStatusChanged(order) - weixinmsg.NotifyAfsOrderStatus(order) - } - } - return err -} - -func isAutoRejectAfsOrder(order *model.AfsOrder) (isReject bool) { - for _, v := range order.Skus { - if autoRejectSkuMap[jxutils.GetSkuIDFromOrderSkuFinancial(v)] == 1 { - isReject = true - break - } - } - return isReject -} diff --git a/business/jxcallback/scheduler/defsch/defsch_ext.go b/business/jxcallback/scheduler/defsch/defsch_ext.go deleted file mode 100644 index d76f3e3fc..000000000 --- a/business/jxcallback/scheduler/defsch/defsch_ext.go +++ /dev/null @@ -1,275 +0,0 @@ -package defsch - -import ( - "fmt" - "math" - "time" - - "git.rosy.net.cn/jx-callback/business/model/dao" - - "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" -) - -func (s *DefScheduler) loadSavedOrderByID(vendorOrderID string, vendorID int, isForceLoad bool) *WatchOrderInfo { - return s.loadSavedOrderFromMap(&model.OrderStatus{ - RefVendorOrderID: vendorOrderID, - RefVendorID: vendorID, - }, isForceLoad) -} - -func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) { - var order *model.GoodsOrder - jxutils.CallMsgHandler(func() { - err = func() (err error) { - globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName) - savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true) - if savedOrderInfo != nil { - order = savedOrderInfo.order - if err = s.isPossibleSwitch2SelfDelivery(order); err == nil { - err = s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive) - if err == nil { - if model.IsOrderDeliveryByStore(order) { - if order.Status < model.OrderStatusDelivering { - storeDetail, err2 := dao.GetStoreDetail(dao.GetDB(), order.StoreID, order.VendorID) - phone := userName - if err = err2; err == nil { - phone = storeDetail.Tel1 - } - err = s.SelfDeliverDelivering(order, phone) - } - } else { - if order.Status < model.OrderStatusDelivering { - err = s.Swtich2SelfDeliver(order, userName) - } else if order.VendorID == order.WaybillVendorID { // 状态为配送中,且是购物平台运单,不能转自送了 - err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation - } - } - } - } - if err == nil { - order.Status = model.OrderStatusDelivering - order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled | model.OrderDeliveryFlagMaskPurcahseDisabled - if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil { - s.stopTimer(savedOrderInfo) - globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName) - return err - } - } - } else { - order = &model.GoodsOrder{ - VendorOrderID: vendorOrderID, - VendorID: vendorID, - } - err = scheduler.ErrCanNotFindOrder - } - globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s error:%v", vendorOrderID, userName, err) - return err - }() - }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) - vendorStatus := fmt.Sprintf("%s转商户自送成功", ctx.GetUserName()) - remark := "" - if err != nil { - vendorStatus = fmt.Sprintf("%s转商户自送失败", ctx.GetUserName()) - remark = err.Error() - } - partner.CurOrderManager.OnOrderMsg(order, vendorStatus, remark) - return err -} - -func (s *DefScheduler) canOrderCreateWaybillNormally(order *model.GoodsOrder, savedOrderInfo *WatchOrderInfo) (err error) { - if !(order.LockStatus != model.OrderStatusLocked && order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) { - err = fmt.Errorf("当前订单%s没有处于拣货完成且没有结束没有锁定的订单才能进行召唤配送操作", order.VendorOrderID) - } else if model.IsOrderHaveWaybill(order) { - err = fmt.Errorf("当前订单%s已经有了有效的承运人%s了", order.VendorOrderID, jxutils.GetVendorName(order.WaybillVendorID)) - } - return err -} - -func (s *DefScheduler) isPossibleSwitch2SelfDelivery(order *model.GoodsOrder) (err error) { - if model.IsOrderDeliveryByPlatform(order) { - if order.Status < model.OrderStatusFinishedPickup { - err = fmt.Errorf("拣货完成后才能转自配送") - } else if order.Status == model.OrderStatusFinishedPickup { - if time.Now().Sub(order.StatusTime) < minMinute2Schedule3rdCarrier*time.Minute { - err = fmt.Errorf("非自配送门店转3方配送至少要求拣货完成后%d分钟才能操作", minMinute2Schedule3rdCarrier) - } - } else if order.Status >= model.OrderStatusDelivering && order.Status < model.OrderStatusEndBegin { - if model.IsOrderHaveOwnWaybill(order) { - err = fmt.Errorf("%s物流已在配送中,不能转自配送", jxutils.GetVendorName(order.VendorID)) - } - } else if order.Status >= model.OrderStatusEndBegin { - err = fmt.Errorf("订单%s已经结束,请刷新状态", order.VendorOrderID) - } - } - return err -} - -func (s *DefScheduler) CreateWaybillOnProviders4SavedOrder(ctx *jxcontext.Context, savedOrderInfo *WatchOrderInfo, courierVendorIDs, excludeCourierVendorIDs []int, forceCreate bool, maxDeliveryFee int64) (bills []*model.Waybill, err error) { - order := savedOrderInfo.order - if !forceCreate { - err = s.canOrderCreateWaybillNormally(order, nil) - } - if err == nil { - if forceCreate { - maxDeliveryFee = math.MaxInt64 - } - if bills, err = s.CreateWaybillOnProviders(ctx, order, courierVendorIDs, excludeCourierVendorIDs, maxDeliveryFee, forceCreate); err == nil { - if forceCreate { - order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled - err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) - } - if err == nil { - if forceCreate { - s.stopTimer(savedOrderInfo) - } - globals.SugarLogger.Debugf("CreateWaybillOnProviders4SavedOrder orderID:%s userName:%s successfully", order.VendorOrderID, ctx.GetUserName()) - return bills, err - } - } - } - if err != nil { - globals.SugarLogger.Debugf("CreateWaybillOnProviders4SavedOrder orderID:%s failed with error:%v", order.VendorOrderID, err) - } - return nil, err -} - -func (s *DefScheduler) CreateWaybillOnProvidersEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int, courierVendorIDs []int, forceCreate bool, maxDeliveryFee int64) (bills []*model.Waybill, err error) { - jxutils.CallMsgHandler(func() { - bills, err = func() (bills []*model.Waybill, err error) { - userName := ctx.GetUserName() - globals.SugarLogger.Debugf("CreateWaybillOnProvidersEx orderID:%s userName:%s", vendorOrderID, userName) - if vendorID == model.VendorIDELM { - return nil, fmt.Errorf("不要直接使用饿了么订单号,请使用相应的饿百订单号") - } - savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true) - if savedOrderInfo != nil { - order := savedOrderInfo.order - if order.DeliveryType == model.OrderDeliveryTypeSelfTake { - return nil, fmt.Errorf("订单:%s是自提单", vendorOrderID) - } - if !forceCreate { - err = s.isPossibleSwitch2SelfDelivery(order) - } - if err == nil { - if bills, err = s.CreateWaybillOnProviders4SavedOrder(ctx, savedOrderInfo, courierVendorIDs, nil, forceCreate, maxDeliveryFee); err == nil && len(bills) > 0 { - partner.CurOrderManager.OnOrderMsg(order, "手动创建运单成功", fmt.Sprintf("%s创建%s平台运单,强发:%t,最高限价:%d", ctx.GetUserName(), model.VendorChineseNames[bills[0].WaybillVendorID], forceCreate, maxDeliveryFee)) - } - } - } else { - err = scheduler.ErrCanNotFindOrder - } - globals.SugarLogger.Infof("CreateWaybillOnProvidersEx orderID:%s userName:%s error:%v", vendorOrderID, userName, err) - return bills, err - }() - }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) - return bills, err -} - -// todo 这个函数可以和SelfDeliveringAndUpdateStatus合并 -func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderID string, vendorID int, isStopSchedule bool) (err error) { - jxutils.CallMsgHandler(func() { - err = func() (err error) { - globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s", vendorOrderID, ctx.GetUserName()) - savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true) - if savedOrderInfo != nil { - err = s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive) - } else { - err = scheduler.ErrCanNotFindOrder - } - globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s error:%v", vendorOrderID, ctx.GetUserName(), err) - if err == nil && isStopSchedule { - order := savedOrderInfo.order - order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled - if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil { - s.stopTimer(savedOrderInfo) - globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s successfully", vendorOrderID, ctx.GetUserName()) - } - } - return err - }() - }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) - return err -} - -func (s *DefScheduler) QueryOrderWaybillFeeInfoEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (deliveryFeeMap map[int]*partner.WaybillFeeInfo, err error) { - jxutils.CallMsgHandler(func() { - deliveryFeeMap, err = func() (deliveryFeeMap map[int]*partner.WaybillFeeInfo, err error) { - userName := ctx.GetUserName() - globals.SugarLogger.Infof("GetWaybillsInfoEx orderID:%s userName:%s", vendorOrderID, userName) - - db := dao.GetDB() - order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err != nil { - return nil, err - } - if order.DeliveryType == model.OrderDeliveryTypeSelfTake { - return nil, fmt.Errorf("订单:%s是自提单", vendorOrderID) - } - storeCourierList, err := dao.GetStoreCourierList(db, []int{jxutils.GetSaleStoreIDFromOrder(order)}, nil, model.StoreStatusAll, model.StoreAuditStatusOnline) - if err != nil { - return nil, err - } - waybillList, err := partner.CurOrderManager.GetOrderWaybillInfo(ctx, vendorOrderID, vendorID, true, false) - if err != nil { - return nil, err - } - waybillMap := make(map[int]*model.Waybill) - for _, bill := range waybillList { - waybillMap[bill.WaybillVendorID] = &bill.Waybill - } - deliveryFeeMap = make(map[int]*partner.WaybillFeeInfo) - - var timeoutSecond int - if savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, false); savedOrderInfo != nil { - timeoutSecond = savedOrderInfo.GetCreateWaybillTimeout() - } - for _, storeCourier := range storeCourierList { - var feeInfo *partner.WaybillFeeInfo - if waybillMap[storeCourier.VendorID] != nil { - feeInfo = &partner.WaybillFeeInfo{ - Waybill: waybillMap[storeCourier.VendorID], - } - } else { - if storeCourier.Status != model.StoreStatusOpened { - feeInfo = &partner.WaybillFeeInfo{ - ErrCode: partner.WaybillFeeErrCodeCourierNotOpen, - ErrStr: fmt.Sprintf("暂未开通,联系运营"), - } - } else { - if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil { - if handler.Use4CreateWaybill { - if feeInfo, err = handler.Handler.GetWaybillFee(order); err != nil { - feeInfo = &partner.WaybillFeeInfo{ - ErrCode: partner.WaybillFeeErrCodeCourierOthers, - ErrStr: err.Error(), - } - } else { - feeInfo.TimeoutSecond = timeoutSecond - } - } else { - feeInfo = &partner.WaybillFeeInfo{ - ErrCode: partner.WaybillFeeErrCodeCourierForbidden, - ErrStr: fmt.Sprintf("内部错误,%d不能用于创建运单", storeCourier.VendorID), - } - } - } else { - feeInfo = &partner.WaybillFeeInfo{ - ErrCode: partner.WaybillFeeErrCodeCourierNotSupported, - ErrStr: fmt.Sprintf("内部错误,%d不被支持", storeCourier.VendorID), - } - } - } - } - deliveryFeeMap[storeCourier.VendorID] = feeInfo - } - err = nil - return deliveryFeeMap, err - }() - }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) - return deliveryFeeMap, err -} diff --git a/business/jxcallback/scheduler/scheduler.go b/business/jxcallback/scheduler/scheduler.go deleted file mode 100644 index e4a57e155..000000000 --- a/business/jxcallback/scheduler/scheduler.go +++ /dev/null @@ -1,47 +0,0 @@ -package scheduler - -import ( - "errors" - - "git.rosy.net.cn/jx-callback/business/model" -) - -const ( - StoreDeliveryTypeCrowdSourcing = 0 //缺省,平台众包配送,可转自送 - StoreDeliveryTypeByPlatform = 1 //平台专送 - StoreDeliveryTypeByStore = 2 //完全门店自送,这个表示的意思是平台的门店属性(就是购物平台不负责配送),而不是真正是否是老板自己送 -) - -const ( - TimerStatusTypeUnknown = -1 - TimerStatusTypeOrder = 0 - TimerStatusTypeWaybill = 1 -) - -var ( - CurrentScheduler IScheduler -) - -var ( - ErrOrderStatusIsNotSuitable4CurOperation = errors.New("订单锁定或状态不适合当前操作") - ErrOrderStatusAlreadySatisfyCurOperation = errors.New("订单当前状态已满足当前操作") - - ErrCanNotCreateAtLeastOneWaybill = errors.New("一个运单都不能创建") - ErrCanNotFindOrder = errors.New("不能找到订单(一般是由于事件错序)") - ErrCanNotFindWaybill = errors.New("不能找到运单(一般是由于事件错序)") - ErrOrderIsNotSolid = errors.New("订单是临时订单,不完整,不能用于创建运单") - ErrDeliverProviderWrong = errors.New("快递商不存在或不能用于创建运单") -) - -type IScheduler interface { - // 以下是订单 - OnOrderNew(order *model.GoodsOrder, isPending bool) (err error) - OnOrderStatusChanged(order *model.GoodsOrder, status *model.OrderStatus, isPending bool) (err error) - - // 以下是运单 - OnWaybillStatusChanged(bill *model.Waybill, isPending bool) (err error) - - // 以下是售后单 - OnAfsOrderNew(order *model.AfsOrder, isPending bool) (err error) - OnAfsOrderStatusChanged(order *model.AfsOrder, status *model.OrderStatus, isPending bool) (err error) -}