package defsch import ( "fmt" "math" "time" "git.rosy.net.cn/jx-callback/business/jxstore/cms" "git.rosy.net.cn/baseapi/utils" "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 { globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus ordersavedOrderInfoID:%s", utils.Format4Output(savedOrderInfo, false)) 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) } //门店发单开始扣钱 if order.CreateDeliveryType == model.YES { deliveryFeeMap, _ := s.QueryOrderWaybillFeeInfoEx(ctx, order.VendorOrderID, order.VendorID) isEqual, isZero, _ := partner.CurStoreAcctManager.CheckStoreAcctExpendExist(order.VendorOrderID) if !isZero && !isEqual { var newPrice int64 if len(courierVendorIDs) == 1 { courierVendorID := courierVendorIDs[0] if _, ok := deliveryFeeMap[courierVendorID]; ok { newPrice = deliveryFeeMap[courierVendorID].DeliveryFee } } else if len(courierVendorIDs) == 0 { var maxFee int64 for _, v := range deliveryFeeMap { if v.DeliveryFee > maxFee { v.DeliveryFee = maxFee } } newPrice = maxFee } expend, lastFee, _ := partner.CurStoreAcctManager.GetStoreAcctExpendLastCreateWayBillFee(order.VendorOrderID) if int(newPrice) > lastFee { partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), int(newPrice)-lastFee, partner.StoreAcctTypeExpendCreateWaybill2ndMore, order.VendorOrderID, expend.ID) } } else { if len(courierVendorIDs) == 1 { courierVendorID := courierVendorIDs[0] if _, ok := deliveryFeeMap[courierVendorID]; ok { partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), int(deliveryFeeMap[courierVendorID].DeliveryFee), partner.StoreAcctTypeExpendCreateWaybillEx, order.VendorOrderID, 0) } } else if len(courierVendorIDs) == 0 { var maxFee int64 for _, v := range deliveryFeeMap { if v.DeliveryFee > maxFee { v.DeliveryFee = maxFee } } partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), int(maxFee), partner.StoreAcctTypeExpendCreateWaybillEx, order.VendorOrderID, 0) } } } 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, errCode string, err error) { savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true) if savedOrderInfo != nil { order := savedOrderInfo.order //1表示为门店发单,需要验证门店账户余额情况 if errCode, err = s.CheckStoreBalance(ctx, order, courierVendorIDs); err != nil { return nil, errCode, err } } 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 savedOrderInfo.order.VendorID == model.VendorIDEBAI { courierVendorIDs = []int{model.VendorIDMTPS, model.VendorIDDada} } order := savedOrderInfo.order order.DeliveryFlag = 0 err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order) 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, errCode, err } func (s *DefScheduler) CheckStoreBalance(ctx *jxcontext.Context, order *model.GoodsOrder, courierVendorIDs []int) (errCode string, err error) { if order.CreateDeliveryType == model.YES { //暂时这么认为,len courierVendorIDs 为1表示是老板或者运营从小程序上点的立即发单,因为小程序上是点哪个发哪个 //京西后台则是点一下发3个,len courierVendorIDs 是0 //如果是小程序上点哪个扣哪个平台的钱 //如果是后台,则选最高的那个扣 storeAcct, err := cms.GetStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order)) deliveryFeeMap, _ := s.QueryOrderWaybillFeeInfoEx(ctx, order.VendorOrderID, order.VendorID) if err != nil { return errCode, fmt.Errorf("获取账户余额失败!") } //1、先判断是不是第一次发:查询库里是否有这个订单的运费支出记录,再查询是否有相同金额并且类型为回退的收入记录(取消运单退回) //前者有,后者无, 表示已经发过了,暂未取消,若这这次的发单金额小于上次的金额则不进行判断也不多扣钱,若大于则扣除‘这次金额-上次金额’的钱,余额不足问题也根据这个判断 //前者有,后者有,表示发过并且取消过了,是多次发,直接扣 //前者无,表示就是第一次发,直接扣 //2、小程序里这次金额用发单平台的金额,后台里这次金额用所有平台最高费用 isEqual, isZero, err := partner.CurStoreAcctManager.CheckStoreAcctExpendExist(order.VendorOrderID) globals.SugarLogger.Debugf("CheckStoreBalance orderID :%v, isEqual : %v, isZero: %v", order.VendorOrderID, isEqual, isZero) //表示前者有,后者无 if !isZero && !isEqual { var newPrice int64 if len(courierVendorIDs) == 1 { courierVendorID := courierVendorIDs[0] if _, ok := deliveryFeeMap[courierVendorID]; ok { newPrice = deliveryFeeMap[courierVendorID].DeliveryFee } } else if len(courierVendorIDs) == 0 { var maxFee int64 for _, v := range deliveryFeeMap { if v.DeliveryFee > maxFee { v.DeliveryFee = maxFee } } newPrice = maxFee } _, lastFee, _ := partner.CurStoreAcctManager.GetStoreAcctExpendLastCreateWayBillFee(order.VendorOrderID) if int(newPrice) > lastFee { if storeAcct.AccountBalance < int(newPrice)-lastFee { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(newPrice-int64(lastFee))) } } } else { if storeAcct.AccountBalance < partner.MinCreateWaybillBalance { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(partner.MinCreateWaybillBalance)) } if len(courierVendorIDs) == 1 { courierVendorID := courierVendorIDs[0] if _, ok := deliveryFeeMap[courierVendorID]; ok { if deliveryFeeMap[courierVendorID].DeliveryFee > int64(storeAcct.AccountBalance) { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(deliveryFeeMap[courierVendorID].DeliveryFee)) } } } else if len(courierVendorIDs) == 0 { var maxFee int64 for _, v := range deliveryFeeMap { if v.DeliveryFee > maxFee { v.DeliveryFee = maxFee } } if maxFee > int64(storeAcct.AccountBalance) { return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(maxFee)) } } } } return errCode, 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 }