package defsch

import (
	"fmt"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
	"math/rand"
	"sort"
	"strings"
	"sync"
	"time"

	"git.rosy.net.cn/baseapi/platformapi/dadaapi"
	"git.rosy.net.cn/baseapi/platformapi/fnpsapi"
	"git.rosy.net.cn/baseapi/platformapi/mtpsapi"
	"git.rosy.net.cn/baseapi/platformapi/uuptapi"
	"git.rosy.net.cn/jx-callback/business/jxcallback/orderman"
	"git.rosy.net.cn/jx-callback/business/jxutils/smsmsg"

	"git.rosy.net.cn/jx-callback/globals/api"

	push "git.rosy.net.cn/jx-callback/business/jxutils/unipush"

	"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/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/client/orm"
)

const (
	time2Delivered                 = 1 * time.Hour // 正常从下单到送达的时间。
	minute2Schedule3rdCarrier      = 20            // 收到平台方自有配送的新运单消息后，等待创建三方配送运单的时间（分钟），如果是定时达，会再根据ExpectedDeliveredTime与dingShiDaAheadTime做调整
	minute2Schedule3rdCarrier4Ebai = 30            // 饿百的最少转自配送需要的时间（分钟）
	minMinute2Schedule3rdCarrier   = 5             // 转三方配送最少等待时间（分钟）

	time2AutoPickupMin   = 9 * 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) {
	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
	}
	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, order.VendorStoreID); 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 && storeDetail.PrinterVendorID == model.VendorIDJxprint {
						//	sch.notifyNewOrder(order, false)
						//	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())
					} else {
						partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "自动拣货成功", utils.Time2TimeStr(time.Now()))
						smsmsg.NotifyPickOrder(savedOrderInfo.order)
					}
					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, isAuto bool) (err error) {
	savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), false)
	savedOrderInfo.SetOrder(order)
	s.resetTimer(savedOrderInfo, nil, isPending)
	if order.VendorID == model.VendorIDEBAI { //饿百订单待接单的时候就推所有东西
		if order.Status == model.OrderStatusWaitAccepted || order.Status == model.OrderStatusNew || order.Status >= model.OrderStatusFinishedPickup { //错序
			if order.Flag&model.OrderFlagMaskFake == 0 {
				utils.CallFuncAsync(func() {
					// order = setFakeActualPayPrice(order)
					netprinter.PrintOrderByOrder(jxcontext.AdminCtx, order, model.PrintTypeOrder, nil)
					//目前暂且认为AdjustCount > 0 就是调整单
					if order.AdjustCount > 0 {
						weixinmsg.NotifyAdjustOrder(order)
					} else {
						weixinmsg.NotifyNewOrder(order)
					}
					OrderProfitWarning(order)
					if !isAuto {
						//smsmsg.NotifyNewOrder(order)
					}
					// smsmsg.NotifyNewUserOrder(order)
					push.NotifyNewOrder(order)
				})
			}
			msghub.OnNewOrder(order)
		}
	} else if order.VendorID == model.VendorIDMTWM {
		mtwm.GetAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), order.VendorStoreID).PushPrintMsg(order.VendorOrderID)
	}
	if order.Status >= model.OrderStatusNew {
		if !isPending && order.Status >= model.OrderStatusNew { // 有订单消息错序，先收到接单消息，再收到新订单消息，导致接单TIMER不动作，这里补一下
			s.notifyNewOrder(order, isAuto)
			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 {
		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+"13")
				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 {
		savedOrderInfo := s.loadSavedOrderFromMap(model.Waybill2Status(bill), true)
		order := savedOrderInfo.order

		// 暂时只处理抖音平台,抖音暂无配送,只要是抖音订单,三方配送压单了,直接通知送出
		if bill.Status == model.WaybillStatusNew {
			s.addWaybill2Map(savedOrderInfo, bill)
			if !isPending {
				if order.Status >= model.OrderStatusDelivering && order.Status <= model.OrderStatusFinished {
					if order.DeliveryType == model.OrderDeliveryTypeStoreSelf {
						// 门店配送方式仅为自配送的商家在接单后，如商品已开始配送
						s.SelfDeliverDelivering(order, "自配送中")
						// 将订单冲购物平台转自送（混合送转自送）
						s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap)
					} else {
						s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime+"14")
					}
				} else if order.Status > model.OrderStatusEndBegin {
					s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime+"15") // 取消三方配送调度
					s.SelfDeliverDelivering(order, "自配送中")
					s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap) // 转自送
					s.SelfDeliverDelivered(order, "自送或三方")                                                                        // 取消当前运单调度
					partner.CurOrderManager.OnOrderMsg(order, "订单处于结束状态来的运单", "取消三方运单，转自送1")
				} else {
					needAddTip := order.WaybillTipMoney > 0 && !model.IsWaybillPlatformOwn(bill)
					if model.IsOrderHaveWaybill(order) { // 是否有有效运单
						if model.IsWaybillPlatformOwn(bill) { // 是购物平台运单
							if !model.IsOrderHaveOwnWaybill(order) { // 既有运单不是购物平台运单
								oldBill := savedOrderInfo.waybills[order.WaybillVendorID]
								if oldBill != nil {
									s.ProxyCancelWaybill(order, oldBill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime+"16")
								} 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+"20")
						}
					}
					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)
			// 抖店在创建运单时,推送发单
			if order.VendorID == model.VendorIDDD && bill.Status == model.WaybillStatusNew {
				partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).SelfDeliverDelivering(order, "")
			}
		} 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)
			}
			// 美团订单会存在:用户直接申请退款,不推送取消消息!导致三方配送未取消!
			if order.Status >= model.OrderStatusCanceled {
				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+"21")
					if !isPending {
						globals.SugarLogger.Warnf("OnWaybillStatusChanged AcceptCanceled orderID:%s got multiple bill:%v, order details:%v", order.VendorOrderID, bill, order)
					}
				}
			}
			switch bill.Status {
			case model.WaybillStatusAccepted, model.WaybillStatusCourierAssigned, 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)

						//京东商城的话，需要去把订单出库,如果是转移过的订单，则需要修改转移订单号
						if order.VendorID == model.VendorIDJDShop {
							s.solutionJdsOrder(bill)
						}

						// 当运单时三方配送时,美团状态20
						if bill.WaybillVendorID == model.VendorIDDada || bill.WaybillVendorID == model.VendorIDMTPS || bill.WaybillVendorID == model.VendorIDFengNiao || bill.WaybillVendorID == model.VendorIDUUPT {
							if (bill.VendorStatus == utils.Int64ToStr(mtpsapi.OrderStatusPickedUp) && bill.OrderVendorID == model.VendorIDMTWM) ||
								(bill.VendorStatus == utils.Int64ToStr(dadaapi.OrderStatusDelivering) && bill.OrderVendorID == model.VendorIDDada) ||
								(bill.VendorStatus == utils.Int64ToStr(fnpsapi.OrderStatusArrived) && bill.OrderVendorID == model.VendorIDFengNiao) ||
								(bill.VendorStatus == uuptapi.StateArrivedStore && bill.OrderVendorID == model.VendorIDUUPT) {
								if storeDetail, err2 := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, ""); err2 == nil {
									if storeDetail.CreateDeliveryType == model.YES {
										s.updateStoreAccount(order, bill)
									} else {
										s.updateBrandAccount(storeDetail, bill)
									}
									//if !model.IsWaybillPlatformOwn(bill) {
									//	s.updateBrandAccount(storeDetail, bill)
									//}
								}
							}
						}

						//若接单时间不在门店的营业时间范围内要取消运单
						s.cancelWaybillNotInStoreOpentime(savedOrderInfo, bill)

						//针对快送的订单（订单接单后会立马召唤骑手，不会到自动拣货完成）
						//如果骑手已接单，没有通知过的门店,需要发送通知
						if bill.Status == model.WaybillStatusAccepted && order.NotifyType == 0 {
							smsmsg.NotifyPickOrder(order)
						}

						// 订单处于配送状态来的新分配骑手运单
						if order.Status >= model.OrderStatusDelivering && order.Status < model.OrderStatusEndBegin && bill.Status <= model.OrderStatusDelivering {
							if order.DeliveryType == model.OrderDeliveryTypeStoreSelf || order.DeliveryType == model.OrderDeliveryTypeSelfTake {
								s.SelfDeliverDelivering(order, "自配送中")
								s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap) // 转自送
							} else {
								if order.VendorWaybillID != bill.VendorWaybillID {
									s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime+"22")
								}
							}
						}

						//订单已经是结束状态之后来的运单143945553920000001
						if order.Status > model.OrderStatusEndBegin {
							s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime+"23")
							s.SelfDeliverDelivering(order, "自配送中")
							s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap) // 转自送
							s.SelfDeliverDelivered(order, "自配送结束")                                                                        // 取消当前运单调度
							partner.CurOrderManager.OnOrderMsg(order, "订单处于结束状态来的运单", "取消三方运单，转自送")
						}
						// 判断订单来源与运单是否为同一来源
						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(order, bill.CourierMobile); err != nil {
									partner.CurOrderManager.OnOrderMsg(order, "转自送失败，并多次转自送", err.Error())
								}
								s.notify3rdPartyWaybill(order, bill, isBillAlreadyCandidate)
							} else {
								s.SelfDeliverDelivering(order, "自配送中")
								s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap)
							}
						}
					} else if !s.isBillCandidate(order, bill) && bill.WaybillVendorID != order.VendorID {
						// 发生这种情况的原因就是两个接单事件几乎同时到达（来不及取消），也算正常
						s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime+"24")
					}
					if isBillAlreadyCandidate && !s.isWaybillCourierSame(savedOrderInfo, bill) && !model.IsWaybillPlatformOwn(bill) {
						s.notify3rdPartyWaybill(order, bill, isBillAlreadyCandidate)
					}
					// 订单在三方配送中，转自送
					if order.Status >= model.OrderStatusDelivering && order.VendorOrderID != order.VendorWaybillID && order.VendorWaybillID != "" {
						s.SelfDeliverDelivering(order, "自配送中")
						s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, switch2SelfDeliverRetryCount, switch2SelfDeliverRetryGap)
					}
					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+"25")
					if !isPending {
						globals.SugarLogger.Warnf("OnWaybillStatusChanged AcceptCanceled orderID:%s got multiple bill:%v, order details:%v", order.VendorOrderID, bill, order)
					}
				}
			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重新建运单
						//TODO 自动取消运单后，暂不重发
						if bill.DeliveryFlag&model.WaybillDeliveryFlagMaskActiveCancel == 0 {
							//if (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) && (bill.WaybillVendorID != order.VendorID /* || bill.WaybillVendorID == model.VendorIDEBAI*/) && (order.DeliveryFlag&model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
							//	s.createWaybillOn3rdProviders(savedOrderInfo, 0, nil)
							//}
							globals.SugarLogger.Debugf("auto cancelWaybill created second, orderID：%s", order.VendorOrderID)
						}
					}
				}
				if order.Status == model.OrderStatusDelivering && bill.Status == model.WaybillStatusCanceled {
					order.Status = model.OrderStatusFinishedPickup
					dao.UpdateEntity(dao.GetDB(), order, "Status")
				}

				if err := orderman.ResetCreateWaybillFee(nil, order, bill); err != nil {
					globals.SugarLogger.Errorf("ResetCreateWaybillFee err : %v", err)
				}

			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
					if !model.IsOrderHaveWaybill(order) {
						s.updateOrderByBill(order, bill, false)
					}
				}
				if !isPending {
					s.notify3rdPartyWaybill(order, bill, false)
				}
				// 将订单修改为完成状态
				if order.Status != model.OrderStatusCanceled {
					order.Status = model.OrderStatusFinished // 有订单取消之后点击送达回把订单改成完成状态
				}
				order.OrderFinishedAt = time.Now()
				partner.CurOrderManager.UpdateOrderFields(order, []string{"status", "OrderFinishedAt"})

				if err := orderman.FinisOrderWaybillFee(nil, order, bill); err != nil {
					globals.SugarLogger.Debugf("FinisOrderWaybillFee count err : %v", err)
				}

			default:
				s.resetTimer(savedOrderInfo, bill, isPending)
			}
		}
	}
	return err
}

func (s *DefScheduler) cancelWaybillNotInStoreOpentime(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
	if savedOrderInfo == nil {
		return nil
	}

	//TODO 2020-09-07 不在门店的营业时间内取消运单
	//TODO 2021-07-26 实时取门店营业时间，运营可能在订单来之后修改门店营业时间
	storeDetail, _ := partner.CurOrderManager.LoadStoreDetail(jxutils.GetSaleStoreIDFromOrder(savedOrderInfo.order), savedOrderInfo.order.VendorID)
	if storeDetail == nil {
		return nil
	}

	// 系统手动发单,不做营业时间校验
	if savedOrderInfo.order.SendOrderType == 1 {
		return nil
	}

	if storeDetail.OpenTime1 != 0 && storeDetail.CloseTime1 != 0 {
		time1 := jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime1, time.Now())
		time2 := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime1, time.Now())
		if time.Now().Sub(time1) < 0 || time.Now().Sub(time2.Add(time.Minute*180)) > 0 { // 营业时间
			if storeDetail.OpenTime2 != 0 && storeDetail.CloseTime2 != 0 { // 休息时间
				time3 := jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime2, time.Now())
				time4 := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime2, time.Now())
				if time.Now().Sub(time3) < 0 || time.Now().Sub(time4.Add(time.Minute*180)) > 0 {
					s.CancelWaybill(bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonNotInStoreOpenTime)
					//s.reverseBrandAccount(storeDetail, bill)
				}
			} else {
				s.CancelWaybill(bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonNotInStoreOpenTime)
				//s.reverseBrandAccount(storeDetail, bill)
			}
		}
	}
	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
			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)
	}
	if err = s.canOrderCreateWaybillNormally(order, savedOrderInfo); err == nil {
		if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
			if savedOrderInfo.retryCount <= maxWaybillRetryCount {
				//TODO 2020-07-21 发单时间要在门店的营业时间内
				var storeDetail *dao.StoreDetail
				storeDetail, err = dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
				if storeDetail != nil {
					if storeDetail.OpenTime1 != 0 && storeDetail.CloseTime1 != 0 {
						time1 := jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime1, time.Now())
						time2 := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime1, time.Now())
						if time.Now().Sub(time1) < 0 || time.Now().Sub(time2.Add(time.Minute*180)) > 0 {
							if storeDetail.OpenTime2 != 0 && storeDetail.CloseTime2 != 0 {
								time3 := jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime2, time.Now())
								time4 := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime2, time.Now())
								if time.Now().Sub(time3) < 0 || time.Now().Sub(time4.Add(time.Minute*180)) > 0 {
									err = fmt.Errorf("不在门店营业时间范围内！")
								}
							} else {
								err = fmt.Errorf("不在门店营业时间范围内！")
							}
						}
					}
				}
				if storeDetail.Status != model.StoreStatusOpened {
					err = fmt.Errorf("门店非营业状态,不允许自动发单")
				}

				var waybillVendorIDs = []int{model.VendorIDMTPS, model.VendorIDDada, model.VendorIDFengNiao, model.VendorIDUUPT, model.VendorIDSFPS}
				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 || order.VendorID == model.VendorIDJD || order.VendorID == model.VendorIDDD {
					if order.DeliveryType != model.OrderDeliveryTypeStoreSelf {
						excludeVendorIDs = append(excludeVendorIDs, waybillVendorIDs...)
					}
					//全都排除了，换个提示
					if len(excludeVendorIDs) == len(waybillVendorIDs) {
						err = fmt.Errorf("订单配送方式不是门店自送！")
					}
				}

				// 获取配送记录,
				statusList, _ := orderman.FixedOrderManager.GetWayBillStatusList(order.VendorOrderID, order.VendorOrderID, 0)
				for _, v := range statusList {
					if v.Status == model.OrderStatusMsg && utils.Str2Int64WithDefault(v.VendorStatus, 0) != model.NO {
						excludeVendorIDs = append(excludeVendorIDs, utils.Str2Int(v.VendorStatus))
					}
				}

				//2020-11-16 预计收益-配送费 > 0 再发
				if order.EarningType == model.EarningTypeQuote && order.VendorID != model.VendorIDTaoVegetable && order.VendorID != model.VendorIDDD { // 淘宝,抖店 是第二天更新结算信息的
					for _, vendorID := range waybillVendorIDs {
						handler := partner.GetDeliveryPlatformFromVendorID(vendorID)
						if deliveryFeeInfo, err := handler.Handler.GetWaybillFee(order); err == nil && deliveryFeeInfo != nil {
							if order.TotalShopMoney-order.EarningPrice-deliveryFeeInfo.DeliveryFee <= -600 && storeDetail.LossOrderDelivery == -1 {
								excludeVendorIDs = append(excludeVendorIDs, vendorID)
							}
						} else {
							excludeVendorIDs = append(excludeVendorIDs, vendorID)
						}
					}
					//全都排除了，换个提示
					if len(excludeVendorIDs) == len(waybillVendorIDs) {
						err = fmt.Errorf("报价订单扣除配送费后预计收益小于0")
					}
				}

				//1表示为门店发单，需要验证门店账户余额情况
				_, err2 := s.CheckStoreBalance(jxcontext.AdminCtx, order, waybillVendorIDs)
				// 检查是否有有效运单
				if err == nil && err2 == nil {
					// 获取所有平台运单费用
					feeList, _ := s.QueryOrderWaybillFeeInfoEx(jxcontext.AdminCtx, order.VendorOrderID, order.VendorID)
					if len(feeList) == model.NO {
						return fmt.Errorf("自动发单异常,未获取到有效的配送门店")
					}
					// excludeVendorIDs 包括上面已经发送的平台id 被排除的配送
					excludeVendorIDsMap := make(map[int]int, 0)
					for _, v := range excludeVendorIDs {
						excludeVendorIDsMap[v] = model.YES
					}

					// 可以发运单的平台
					canDeliveryVendor := make(map[int64]int, 0)
					canDeliveryKey := make([]int64, 0, 0)
					for k, v := range feeList {
						if excludeVendorIDsMap[k] != model.YES && v.DeliveryFee > model.NO && v.DeliveryFee <= 5000 { // 限制配送价格
							canDeliveryVendor[v.DeliveryFee] = k
							canDeliveryKey = append(canDeliveryKey, v.DeliveryFee)
						}
					}
					if len(canDeliveryKey) == model.NO {
						return fmt.Errorf("无可配送平台")
					}
					sort.Slice(canDeliveryKey, func(i, j int) bool {
						return canDeliveryKey[i] <= canDeliveryKey[j]
					})

					vendorID := 0
					switch canDeliveryVendor[canDeliveryKey[0]] {
					case model.VendorIDDada:
						excludeVendorIDs = []int{model.VendorIDFengNiao, model.VendorIDMTPS, model.VendorIDUUPT, model.VendorIDSFPS}
						vendorID = model.VendorIDDada
					case model.VendorIDMTPS:
						excludeVendorIDs = []int{model.VendorIDDada, model.VendorIDFengNiao, model.VendorIDUUPT, model.VendorIDSFPS}
						vendorID = model.VendorIDMTPS
					case model.VendorIDUUPT:
						excludeVendorIDs = []int{model.VendorIDDada, model.VendorIDMTPS, model.VendorIDFengNiao, model.VendorIDSFPS}
						vendorID = model.VendorIDUUPT
					case model.VendorIDSFPS:
						excludeVendorIDs = []int{model.VendorIDDada, model.VendorIDMTPS, model.VendorIDUUPT, model.VendorIDFengNiao}
						vendorID = model.VendorIDSFPS
					case model.VendorIDFengNiao:
						excludeVendorIDs = []int{model.VendorIDDada, model.VendorIDMTPS, model.VendorIDUUPT, model.VendorIDSFPS}
						vendorID = model.VendorIDFengNiao
					}

					if vendorID != model.NO {
						if _, err = s.CreateWaybillOnProviders4SavedOrder(jxcontext.AdminCtx, savedOrderInfo, nil, excludeVendorIDs, false, maxDeliveryFee); err == nil {
							savedOrderInfo.retryCount++
							partner.CurOrderManager.OnOrderMsg(order, utils.Int2Str(vendorID), fmt.Sprintf("订单自动创建三方运单成功:%s", jxutils.GetVendorName(vendorID)))
						} else {
							partner.CurOrderManager.OnOrderMsg(order, utils.Int2Str(vendorID), fmt.Sprintf("发起自动创建三方运单,目标创建运单平台[%s],创建失败:%v", jxutils.GetVendorName(vendorID), err))
						}
					}
				}
			} else {
				errMsg := fmt.Sprintf("订单:%s已经自动创建过了%d次运单，仍然无法配送，请关注！", order.VendorOrderID, savedOrderInfo.retryCount)
				err = fmt.Errorf(errMsg)
				s.notifyOrderStakeHolder(order, "", errMsg)
			}
		} 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) {
	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) {
	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)
				partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, utils.Int64ToStr(model.WaybillStatusCancel), "管理系统取消或自动取消")
			} 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
	if order.WaybillVendorID != order.VendorID {
		if err := s.Swtich2SelfDeliver(order, "Swtich2SelfDeliver转自送"); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation { //301251饿了转自送 1047美团混合送
			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)
				if strings.Contains(err.Error(), "1047") {
					if err := s.SelfDeliverDelivering(order, "非专送、快送、混合送订单，转自送失败，调用deriving转自送"); err != nil {
						s.notify3rdPartyWaybill(order, bill, false)
						s.removeWaybillFromMap(savedOrderInfo, order.VendorID)
					}
				} else if strings.Contains(err.Error(), "301251") || strings.Contains(err.Error(), "1071") { //elm 不做处理
					//s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrNotAcceptIntime)
					//order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
					//partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
					//partner.CurOrderManager.OnOrderMsg(order, "饿了么专送平台门店,无法切自送,需要平台推送配送异常才能转自送!", err.Error())
				} else {
					if s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonSwitch2SelfFailed, partner.CancelWaybillReasonStrSwitch2SelfFailed+"26") == 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 {
			//if order.VendorID == model.VendorIDEBAI && strings.Contains(order.ConsigneeAddress, "隐私保护") {
			//	orderMap, err := api.EbaiAPI.OrderGet(order.VendorOrderID)
			//	if err == nil {
			//		// 部分订单在隐私条约的保护下会隐藏用户收货地址,转自送成功自后才可以获取到正确的地址.
			//		userMap := orderMap["user"].(map[string]interface{})
			//		order.ConsigneeAddress = utils.Interface2String(userMap["address"])
			//		partner.CurOrderManager.UpdateOrderFields(order, []string{"ConsigneeAddress"})
			//	}
			//}
			s.notify3rdPartyWaybill(order, bill, false)
			s.removeWaybillFromMap(savedOrderInfo, order.VendorID)
			data := "断点打印"
			if err != nil {
				data = err.Error()
			}
			partner.CurOrderManager.OnOrderMsg(order, "转自送成功", data)
		}
	} else {
		s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrNotAcceptIntime)
		order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
		partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
		// 进到这里的原因是，在这个时间点，购物平台物流已经抢单（但抢单消息还没有被收到），所以转自送会失败 （比如：818810379000941），更好的做法应该是判断Swtich2SelfDeliver的返回值，这种情况下就不得试了
		partner.CurOrderManager.OnOrderMsg(order, "转商家自配送失败", "平台物流已接单")
	}
}

// 这个函数这样写的原因是适应一些消息错序
func (s *DefScheduler) loadSavedOrderFromMap(status *model.OrderStatus, isForceLoad bool) *WatchOrderInfo {
	universalOrderID := jxutils.ComposeUniversalOrderID(status.RefVendorOrderID, status.RefVendorID) // 等于RefVendorOrderID
	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,
			})
		}
	}
	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
	}
	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]
				//}
				//globals.SugarLogger.Debugf("timerName %s", timerName)
				timerInfo := &tTimerInfo{
					statusType: statusType,
					vendorID:   vendorID,
					status:     status,
					timerTime:  time.Now().Add(timeout),
				}
				timerInfo.timer = utils.AfterFuncWithRecover(timeout, func() {
					jxutils.CallMsgHandlerAsync(func() {
						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)
			}
		} 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 {
	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.DesiredFee != 0 {
			jxutils.RefreshOrderEarningPrice3(order, order.OrderPayPercentage, bill)
			updateFields = append(updateFields, "NewEarningPrice")
		}
	}
	if revertStatus {
		order.Status = model.OrderStatusFinishedPickup
		updateFields = append(updateFields, "Status")
	}
	partner.CurOrderManager.UpdateOrderFields(order, updateFields)
}

func (s *DefScheduler) updateStoreAccount(order *model.GoodsOrder, bill *model.Waybill) {
	realDesiredFee := bill.DesiredFee
	if expend, lastFee, err := partner.CurStoreAcctManager.GetStoreAcctExpendLastCreateWayBillFee(order.VendorOrderID); err == nil && expend != nil {
		lastFee64 := int64(lastFee)
		if lastFee64 != realDesiredFee {
			var diffFee int64
			if lastFee64 > realDesiredFee {
				diffFee = lastFee64 - realDesiredFee
				partner.CurStoreAcctManager.InsertStoreAcctIncomeAndUpdateStoreAcctBalance(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromOrder(order), int(diffFee), partner.StoreAcctTypeRealFeeIncome, order.VendorOrderID, bill.VendorWaybillID, expend.ID)
			} else {
				diffFee = realDesiredFee - lastFee64
				partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromOrder(order), int(diffFee), partner.StoreAcctTypeRealFeeExpend, order.VendorOrderID, "", expend.ID)
			}
		}
	}
}

func (s *DefScheduler) updateBrandAccount(store *dao.StoreDetail, bill *model.Waybill) {
	brandBills, _ := dao.GetBrandBill(dao.GetDB(), store.BrandID, bill.VendorOrderID, model.BrandBillTypeExpend, model.BrandBillFeeTypeDelivery, bill.VendorWaybillID)
	if len(brandBills) == 0 {
		partner.CurStoreAcctManager.InsertBrandBill(jxcontext.AdminCtx, store.BrandID, int(bill.DesiredFee), model.BrandBillTypeExpend, model.BrandBillFeeTypeDelivery, bill.VendorOrderID, bill.VendorWaybillID)
	} else {
		//可能有对冲的
		sum := 0
		for _, v := range brandBills {
			sum += v.Price
		}
		if sum == 0 {
			partner.CurStoreAcctManager.InsertBrandBill(jxcontext.AdminCtx, store.BrandID, int(bill.DesiredFee), model.BrandBillTypeExpend, model.BrandBillFeeTypeDelivery, bill.VendorOrderID, bill.VendorWaybillID)
		} else if bill.DesiredFee > int64(sum) {
			partner.CurStoreAcctManager.InsertBrandBill(jxcontext.AdminCtx, store.BrandID, int(bill.DesiredFee-int64(sum)), model.BrandBillTypeExpend, model.BrandBillFeeTypeDelivery, bill.VendorOrderID, bill.VendorWaybillID)
		} else if bill.DesiredFee < int64(sum) {
			partner.CurStoreAcctManager.InsertBrandBill(jxcontext.AdminCtx, store.BrandID, int(int64(sum)-bill.DesiredFee), model.BrandBillTypeIncome, model.BrandBillFeeTypeDelivery, bill.VendorOrderID, bill.VendorWaybillID)
		}
	}
}

// 取消退费
func (s *DefScheduler) reverseBrandAccount(store *dao.StoreDetail, bill *model.Waybill) {
	brandBills, _ := dao.GetBrandBill(dao.GetDB(), store.BrandID, bill.VendorOrderID, model.BrandBillTypeExpend, model.BrandBillFeeTypeDelivery, bill.VendorWaybillID)
	if len(brandBills) > 0 {
		sum := 0
		for _, v := range brandBills {
			sum += v.Price
		}
		if sum > 0 {
			partner.CurStoreAcctManager.InsertBrandBill(jxcontext.AdminCtx, store.BrandID, -int(bill.DesiredFee), model.BrandBillTypeExpend, model.BrandBillFeeTypeDelivery, bill.VendorOrderID, bill.VendorWaybillID)
		}
	}
}

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
	store, _ := dao.GetStoreDetail(dao.GetDB(), order.JxStoreID, order.VendorID, "")
	//flag := (model.IsOrderDeliveryByStore(order) || model.IsOrderDeliveryBySelf(order)) && store.AutoPickup == model.YES
	flag := store.AutoPickup == model.YES && store.VendorStatus == model.StoreStatusOpened // 只要打开了就可以自动拣货
	if err = s.PickupGoods(order, flag, "autoPickup"); 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) {
	if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
		if err = s.CancelWaybill(bill, cancelReasonID, cancelReason); err != nil {
			partner.CurOrderManager.OnOrderMsg(order, "取消三方运单失败", err.Error())
		} else {
			if cancelReasonID != partner.CancelWaybillReasonNotAcceptIntime {
				if storeDetail, _ := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, order.VendorOrgCode); storeDetail != nil {
					//s.reverseBrandAccount(storeDetail, bill)
				}
			}
		}
		return err
	}
	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
		}
	}
}

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, isAuto bool) {
	if order.Flag&model.OrderFlagMaskFake == 0 && order.VendorID != model.VendorIDEBAI {
		utils.CallFuncAsync(func() {
			// order = setFakeActualPayPrice(order)
			netprinter.PrintOrderByOrder(jxcontext.AdminCtx, order, model.PrintTypeOrder, nil)
			//目前暂且认为AdjustCount > 0 就是调整单
			if order.AdjustCount > 0 {
				weixinmsg.NotifyAdjustOrder(order)
			} else {
				weixinmsg.NotifyNewOrder(order)
			}
			OrderProfitWarning(order)
			if !isAuto {
				//smsmsg.NotifyNewOrder(order)
			}
			// smsmsg.NotifyNewUserOrder(order)
			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 {
		return
	}
	storeID := jxutils.GetShowStoreIDFromOrder(order)
	storeDetail, err := dao.GetStoreDetail(db, storeID, order.VendorID, "")
	payPercentage := 0
	if storeDetail != nil && err == nil {
		if storeDetail.VendorPayPercentage != 0 {
			payPercentage = storeDetail.VendorPayPercentage
		} else {
			payPercentage = storeDetail.PayPercentage
		}
		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
		}
		if profit < 0 {
			operatorPhone, operatorName := getOrderOperatorInfo(order, storeDetail)
			operatorRole := getOrderOperatorRoleInfo(order, storeDetail)
			if operatorPhone != "" {
				var (
					roleList []*authz.RoleInfo
					userIDs  []string
					flag     = false
					storeIDt int
				)
				if operatorRole != "" {
					roleList = append(roleList, autils.NewRole(operatorRole, 0))
					if userIDMap, err := cms.GetRolesUserList(jxcontext.AdminCtx, roleList); err == nil {
						for _, v := range userIDMap {
							for _, vv := range v {
								userIDs = append(userIDs, vv)
							}
						}
					}
				}
				storeIDt = order.StoreID
				if storeIDt == 0 {
					storeIDt = order.JxStoreID
				}
				noticeMsg := fmt.Sprintf("利润 ：[%v]，运营负责人：[%v]，商家负责人：[%v]，门店ID：[%v]，平台门店ID[%v]，门店名：[%v]，订单序号：[%v]，订单号(点击进入详情)：%v", profit, operatorName, storeDetail.MarketManName, storeIDt, order.VendorStoreID, order.StoreName, order.OrderSeq, globals.BackstageHost+"/#/ordermanager/"+order.VendorOrderID)
				user, err := dao.GetUserByID(db, "mobile", operatorPhone)
				user2, err := dao.GetUserByID(db, "mobile", storeDetail.MarketManPhone)
				if user != nil && err == nil {
					for _, v := range userIDs {
						if v == user.UserID {
							flag = true
							break
						}
					}
					if !flag {
						userIDs = append(userIDs, user.UserID)
					}
					userIDs = append(userIDs, user2.UserID)
					for _, v := range userIDs {
						if api.DingDingAPI.GetToken() != "" {
							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
}
