diff --git a/business/auth2/authprovider/weixin/weixin_mini.go b/business/auth2/authprovider/weixin/weixin_mini.go index 6e079b7ee..82e37b2ca 100644 --- a/business/auth2/authprovider/weixin/weixin_mini.go +++ b/business/auth2/authprovider/weixin/weixin_mini.go @@ -72,6 +72,7 @@ func (a *MiniAuther) DecryptData(authInfo *auth2.AuthInfo, jsCode, encryptedData if err != nil { return "", err } + authInfo.AuthBindInfo.UserData = sessionKey return string(decryptedData), nil } diff --git a/business/jxcallback/orderman/order.go b/business/jxcallback/orderman/order.go index f80328328..19a947619 100644 --- a/business/jxcallback/orderman/order.go +++ b/business/jxcallback/orderman/order.go @@ -303,7 +303,7 @@ func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao return isDuplicated, err } -func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.DaoDB, storePayPercentage int) (err error) { +func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.DaoDB, storePayPercentage, changePriceType int) (err error) { globals.SugarLogger.Debugf("updateOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID) jxStoreID := jxutils.GetShowStoreIDFromOrder(order) var opNumStr string @@ -372,9 +372,17 @@ func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao. } } } - v.EarningPrice = jxutils.CaculateSkuEarningPrice(v.ShopPrice, v.SalePrice, storePayPercentage) + // 直营店始终按比例结算,不考虑活动与结算表 + salePrice := v.SalePrice + if changePriceType == model.StoreChangePriceTypeManagedStore && v.ShopPrice != 0 { + salePrice = 0 + } + v.EarningPrice = jxutils.CaculateSkuEarningPrice(v.ShopPrice, salePrice, storePayPercentage) + } + // 直营店始终按比例结算,不考虑活动与结算表 + if changePriceType != model.StoreChangePriceTypeManagedStore { + updateSingleOrderEarningPrice(order, db) } - updateSingleOrderEarningPrice(order, db) } return nil } @@ -409,6 +417,7 @@ func (c *OrderManager) updateOrderOtherInfo(order *model.GoodsOrder, db *dao.Dao globals.SugarLogger.Debugf("updateOrderOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID) payPercentage := 0 + changePriceType := model.StoreChangePriceTypeDirect storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, order.VendorStoreID, order.VendorID) if err != nil { if !dao.IsNoRowsError(err) { @@ -422,8 +431,9 @@ func (c *OrderManager) updateOrderOtherInfo(order *model.GoodsOrder, db *dao.Dao } else { order.JxStoreID = storeDetail.Store.ID payPercentage = storeDetail.PayPercentage + changePriceType = int(storeDetail.ChangePriceType) } - if err = c.updateOrderSkuOtherInfo(order, db, payPercentage); err == nil { + if err = c.updateOrderSkuOtherInfo(order, db, payPercentage, changePriceType); err == nil { jxutils.RefreshOrderSkuRelated(order) // caculateOrderEarningPrice(order, payPercentage) } @@ -454,6 +464,11 @@ func (c *OrderManager) addOrderStatus(orderStatus *model.OrderStatus, db *dao.Da VendorID: orderStatus.VendorID, } if err = db.Db.ReadForUpdate(order, "VendorOrderID", "VendorID"); err == nil { + // todo 美团在订单完成后,还可能收到订单取消(应该当成售后单处理才合适),强制忽略这种情况,比如订单:80662201436073600 + // 后来又发现有订单(81710104014426376)在完成后,直接再被取消的情况,不能生成售后单,还是再允许完成后取消。。。 + // if orderStatus.VendorID == model.VendorIDMTWM && model.IsOrderFinalStatus(order.Status) { + // return false, order, nil + // } if (model.IsOrderLockStatus(orderStatus.Status) || model.IsOrderUnlockStatus(orderStatus.Status)) || (model.IsOrderMainStatus(orderStatus.Status) && orderStatus.Status >= order.Status) { // todo 要求status不能回绕 order.VendorStatus = orderStatus.VendorStatus diff --git a/business/jxcallback/scheduler/defsch/defsch.go b/business/jxcallback/scheduler/defsch/defsch.go index 54d5b14df..0da8d7bca 100644 --- a/business/jxcallback/scheduler/defsch/defsch.go +++ b/business/jxcallback/scheduler/defsch/defsch.go @@ -63,6 +63,15 @@ 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里的信息是保持更新的 @@ -74,10 +83,12 @@ type WatchOrderInfo struct { waybills map[int]*model.Waybill // 这个waybills里的状态信息是不真实的,只使用id相关的信息 - timerStatusType int // 0表示订单,1表示运单 - timerStatus int - timer *time.Timer - timerTime time.Time + // timerStatusType int // 0表示订单,1表示运单 + // timerStatus int + // timer *time.Timer + // timerTime time.Time + + timerList []*tTimerInfo retryCount int // 失败后尝试的次数,调试阶段可能出现死循化,阻止这种情况发生 } @@ -88,6 +99,14 @@ type StatusActionConfig struct { 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) @@ -145,6 +164,40 @@ func (s *WatchOrderInfo) GetWaybillVendorIDs() (vendorIDs []int) { 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) 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 @@ -159,41 +212,39 @@ func init() { Timeout: 10 * time.Millisecond, }, TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - if savedOrderInfo.order.LockStatus == model.LockStatusUnlocked && savedOrderInfo.order.Status == model.OrderStatusNew { - order := savedOrderInfo.order - 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.VendorOrderID); err2 == nil { - if order2.Status > order.Status { - order.Status = order2.Status - jxutils.CallMsgHandlerAsync(func() { - sch.OnOrderStatusChanged(order, model.Order2Status(order2), false) - }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) - err = nil - } - } else { - err = err2 - } - } - } - if isAcceptIt { - if err == nil { - sch.notifyNewOrder(order) - msghub.OnNewOrder(order) - } - } else { - partner.CurOrderManager.OnOrderMsg(order, "黑名单拒单", "") - } - return err - }) + order := savedOrderInfo.order + 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.VendorOrderID); err2 == nil { + if order2.Status > order.Status { + order.Status = order2.Status + jxutils.CallMsgHandlerAsync(func() { + sch.OnOrderStatusChanged(order, model.Order2Status(order2), false) + }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) + err = nil + } + } else { + err = err2 + } + } + } + if isAcceptIt { + if err == nil { + sch.notifyNewOrder(order) + msghub.OnNewOrder(order) + } + } else { + partner.CurOrderManager.OnOrderMsg(order, "黑名单拒单", "") + } + return err + }) return nil }, ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { @@ -207,10 +258,8 @@ func init() { TimeoutGap: second2AutoPickupGap, }, TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - if savedOrderInfo.autoPickupTimeoutMinute > 0 { - if err = sch.autoPickupGood(savedOrderInfo); err != nil { - partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "自动拣货失败", err.Error()) - } + if err = sch.autoPickupGood(savedOrderInfo); err != nil { + partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "自动拣货失败", err.Error()) } return nil }, @@ -225,15 +274,12 @@ func init() { TimeoutGap: 0, }, TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - if model.IsOrderDeliveryByStore(savedOrderInfo.order) { // 自配送商家使用 - // 启动抢单TIMER - sch.saveDeliveryFeeFromAndStartWatch(savedOrderInfo, savedOrderInfo.order.StatusTime) - return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - } - return nil + // 启动抢单TIMER + sch.saveDeliveryFeeFromAndStartWatch(savedOrderInfo, savedOrderInfo.order.StatusTime) + return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) }, ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { - return model.IsOrderDeliveryByStore(savedOrderInfo.order) + return model.IsOrderDeliveryByStore(savedOrderInfo.order) // 自配送商家使用 }, }, }, @@ -245,17 +291,11 @@ func init() { Timeout: minute2Schedule3rdCarrier * time.Minute, }, TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - // 饿百转自送的时机不太清楚,暂时禁用超时转自送,在饿百运单取消时还是会自动创建 - if savedOrderInfo.isDeliveryCompetition && - model.IsOrderDeliveryByPlatform(savedOrderInfo.order) && - savedOrderInfo.order.VendorID == bill.WaybillVendorID && - savedOrderInfo.order.VendorID != model.VendorIDEBAI && - savedOrderInfo.order.DeliveryType != model.OrderDeliveryTypeSelfTake { // 非自配送商家使用 - return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) - } - return nil + return sch.createWaybillOn3rdProviders(savedOrderInfo, 0, nil) }, ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { + // 饿百转自送的时机不太清楚,暂时禁用超时转自送,在饿百运单取消时还是会自动创建 + // 非自配送商家使用 return savedOrderInfo.isDeliveryCompetition && model.IsOrderDeliveryByPlatform(savedOrderInfo.order) && savedOrderInfo.order.VendorID == bill.WaybillVendorID && @@ -270,19 +310,11 @@ func init() { Timeout: 5 * time.Second, }, TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { - order := savedOrderInfo.order - if (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) && - savedOrderInfo.isDeliveryCompetition && - savedOrderInfo.order.VendorID == bill.WaybillVendorID && - model.IsOrderDeliveryByPlatform(savedOrderInfo.order) && - order.VendorID == model.VendorIDEBAI && - savedOrderInfo.order.DeliveryType != model.OrderDeliveryTypeSelfTake { // 非自配送商家使用 - return sch.createWaybillOn3rdProviders(savedOrderInfo, ebaiCancelWaybillMaxFee, nil) - } - return nil + return sch.createWaybillOn3rdProviders(savedOrderInfo, ebaiCancelWaybillMaxFee, nil) }, ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool { order := savedOrderInfo.order + // 非自配送商家使用 return (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) && savedOrderInfo.isDeliveryCompetition && savedOrderInfo.order.VendorID == bill.WaybillVendorID && @@ -739,91 +771,169 @@ func (s *DefScheduler) loadSavedOrderFromMap(status *model.OrderStatus, isForceL return realSavedInfo } -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 + vendorID := order.VendorID statusTime := order.StatusTime if bill != nil { status = bill.Status statusType = scheduler.TimerStatusTypeWaybill + vendorID = bill.WaybillVendorID statusTime = bill.StatusTime } globals.SugarLogger.Debugf("resetTimer, orderID:%s statusType:%d status:%d", order.VendorOrderID, statusType, status) - 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) + 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.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.TimeoutAction(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.TimeoutAction(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) + } + if config == nil || config.TimerType != partner.TimerTypeByPass { + savedOrderInfo.StopTimer(stopStatusType, -1, stopStatus) + } + + if config != nil && config.TimeoutAction != nil && config.TimerType != partner.TimerTypeByPass { + if config.CallShouldSetTimer(savedOrderInfo, bill) { + timeout := config.GetRefTimeout(statusTime, order.OrderCreatedAt) + if config.TimeoutGap != 0 { + timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second } + if isPending && timeout < pendingOrderTimerMaxSecond*time.Second { // 如果是PENDING的订单,则将其分布到2--5秒内,让后续事件有机会执行 + timeout = time.Duration(jxutils.MapValue2Scope(int64(timeout), -pendingOrderTimerMinMinSecond*1000, pendingOrderTimerMaxSecond*1000, pendingOrderTimerMinSecond*1000, pendingOrderTimerMaxSecond*1000)) * time.Millisecond + } else if timeout < 0 { + timeout = 0 + } + if timeout == 0 { + config.CallTimeoutAction(savedOrderInfo, bill) + } else { + timerName := "" + if statusType == model.OrderTypeOrder { + timerName = model.OrderStatusName[status] + } else if statusType == model.OrderTypeWaybill { + timerName = model.WaybillStatusName[status] + } + timerInfo := &tTimerInfo{ + statusType: statusType, + vendorID: vendorID, + status: status, + timerTime: time.Now().Add(timeout), + } + timerInfo.timer = utils.AfterFuncWithRecover(timeout, func() { + jxutils.CallMsgHandlerAsync(func() { + globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID) + ts := s.loadSavedOrderFromMap(model.Order2Status(order), true) + config.CallTimeoutAction(ts, bill) + timerInfo.timer = nil + ts.StopTimer(statusType, vendorID, status) + }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)) + }) + } + globals.SugarLogger.Debugf("resetTimer, orderID:%s, statusType:%d, status:%d, timeout:%v", order.VendorOrderID, statusType, status, timeout) } else { - globals.SugarLogger.Debugf("resetTimer bypass2, orderID:%s statusType:%d status:%v, config:%s", order.VendorOrderID, statusType, status, utils.Format4Output(config, true)) + globals.SugarLogger.Debugf("resetTimer, orderID:%s, statusType:%d, status:%d, should not set timer", order.VendorOrderID, statusType, status) } } else { - globals.SugarLogger.Debugf("resetTimer bypass1, orderID:%s statusType:%d status:%v", order.VendorOrderID, statusType, status) + globals.SugarLogger.Debugf("resetTimer bypass2, orderID:%s statusType:%d status:%v, config:%s", order.VendorOrderID, statusType, status, utils.Format4Output(config, true)) } } -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 - } - } - if curStatusType == scheduler.TimerStatusTypeWaybill { - return curStatus != status - } - return curStatusType != statusType || status >= curStatus +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() { @@ -1015,6 +1125,10 @@ func getMaxDeliveryFee(order *model.GoodsOrder) (maxDeliveryFee int64) { } else { maxDeliveryFee = baseWaybillFee + order.DistanceFreightMoney + getWaybillTip(order) } + if maxDeliveryFee < ebaiCancelWaybillMaxFee && + order.DeliveryType == model.OrderDeliveryTypeStoreSelf { + maxDeliveryFee = ebaiCancelWaybillMaxFee + } return maxDeliveryFee } diff --git a/business/jxcallback/scheduler/defsch/defsch_ext.go b/business/jxcallback/scheduler/defsch/defsch_ext.go index 3b875dbad..0681eb9e8 100644 --- a/business/jxcallback/scheduler/defsch/defsch_ext.go +++ b/business/jxcallback/scheduler/defsch/defsch_ext.go @@ -226,9 +226,7 @@ func (s *DefScheduler) QueryOrderWaybillFeeInfoEx(ctx *jxcontext.Context, vendor var timeoutSecond int if savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, false); savedOrderInfo != nil { - if savedOrderInfo.timerStatusType == scheduler.TimerStatusTypeWaybill && savedOrderInfo.timerStatus == model.WaybillStatusNew { - timeoutSecond = int(savedOrderInfo.timerTime.Sub(time.Now()) / time.Second) - } + timeoutSecond = savedOrderInfo.GetCreateWaybillTimeout() } for _, storeCourier := range storeCourierList { var feeInfo *partner.WaybillFeeInfo diff --git a/business/jxstore/act/act.go b/business/jxstore/act/act.go index b7f2a4e15..05c8a5498 100644 --- a/business/jxstore/act/act.go +++ b/business/jxstore/act/act.go @@ -150,7 +150,6 @@ func ActStoreSkuParam2Model(ctx *jxcontext.Context, db *dao.DaoDB, act *model.Ac jxPrice := storeSkuInfo.Price actSkuMap.VendorPrice = int64(getVendorPriceFromStoreSkuBind(storeSkuInfo, vendorID)) v.OriginalPrice = int64(jxPrice) - v.OriginalPrice = actSkuMap.VendorPrice // 暂时返回平台价 } var err2 error if act.Type != model.ActSkuFake { // 非结算,要计算实际活动价格 @@ -629,10 +628,6 @@ func DeleteActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, act if len(actMap) == 0 { return 0, fmt.Errorf("找不到活动:%d,或已被取消", actID) } - if actMap[0].Status != model.ActStatusCreated { - // 如果不是正常状态直接跳过 - return 0, nil - } actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil) if err != nil { diff --git a/business/jxstore/cms/store.go b/business/jxstore/cms/store.go index 520a657ef..9ede205ee 100644 --- a/business/jxstore/cms/store.go +++ b/business/jxstore/cms/store.go @@ -754,8 +754,10 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa if (outStore.OpenTime2 == 0 && outStore.CloseTime2 != 0) || (outStore.OpenTime2 != 0 && outStore.CloseTime2 == 0) { return 0, errors.New(fmt.Sprintf("门店营业时间2设置不合法!时间范围2 :[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1)) } - if outStore.OpenTime2 > outStore.OpenTime1 { - return 0, errors.New(fmt.Sprintf("门店营业时间设置不合法!第二段营业时间应该在第一段营业时间之后!")) + if outStore.OpenTime1 != 0 && outStore.CloseTime1 != 0 && outStore.OpenTime2 != 0 && outStore.CloseTime2 != 0 { + if outStore.OpenTime2 < outStore.CloseTime1 { + return 0, errors.New(fmt.Sprintf("门店营业时间设置不合法!第二段营业时间应该在第一段营业时间之后!")) + } } if beginAt, endAt := GetTimeMixByInt(outStore.OpenTime1, outStore.CloseTime1, outStore.OpenTime2, outStore.CloseTime2); beginAt != 0 && endAt != 0 { return 0, errors.New(fmt.Sprintf("两段门店营业时间不可交叉!时间范围1 :[%v] 至 [%v], 时间范围2 :[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1, outStore.OpenTime2, outStore.CloseTime2)) diff --git a/business/jxstore/cms/store_sku.go b/business/jxstore/cms/store_sku.go index f026cc107..de46a02a4 100644 --- a/business/jxstore/cms/store_sku.go +++ b/business/jxstore/cms/store_sku.go @@ -79,6 +79,9 @@ type StoreSkuExt struct { RealEarningPrice int `json:"realEarningPrice"` + StatusSaleBegin int16 `json:"statusSaleBegin"` //商品可售时间范围 + StatusSaleEnd int16 `json:"statusSaleEnd"` + Count int `json:"count"` Times int `json:"times"` } @@ -451,7 +454,8 @@ func GetStoresSkusNew(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bo t4.sub_store_id, t4.price bind_price, IF(t4.unit_price IS NOT NULL, t4.unit_price, t1.price) unit_price, t4.status store_sku_status, t4.auto_sale_at, t4.ebai_id, t4.mtwm_id, t4.jd_sync_status, t4.ebai_sync_status, t4.mtwm_sync_status, - t4.jd_price, t4.ebai_price, t4.mtwm_price, t4.jx_price + t4.jd_price, t4.ebai_price, t4.mtwm_price, t4.jx_price, + t4.status_sale_begin, t4.status_sale_end ` + sql var tmpList []*tGetStoresSkusInfo beginTime := time.Now() diff --git a/business/jxstore/cms/sync_store_sku.go b/business/jxstore/cms/sync_store_sku.go index e37a9f2ad..f12d6da86 100644 --- a/business/jxstore/cms/sync_store_sku.go +++ b/business/jxstore/cms/sync_store_sku.go @@ -441,7 +441,8 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo } return skuList } - task := tasksch.NewParallelTask("syncStoreSkuNew", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx, + isContinueWhenError2 := true + task := tasksch.NewParallelTask("syncStoreSkuNew", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError2), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { step := batchItemList[0].(int) // globals.SugarLogger.Debugf("step:%d", step) @@ -462,7 +463,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagDeletedMask) } return nil, len(successList), err - }, ctx, task, deleteList, 1 /*singleStoreHandler.GetStoreSkusBatchSize(partner.FuncDeleteStoreSkus)*/, isContinueWhenError) + }, ctx, task, deleteList, 1 /*singleStoreHandler.GetStoreSkusBatchSize(partner.FuncDeleteStoreSkus)*/, isContinueWhenError2) } case 1: if len(createList) > 0 { @@ -494,7 +495,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo updateStoreSku(dao.GetDB(), vendorID, successList, model.SyncFlagNewMask) } return nil, len(successList), err - }, ctx, task, createList, 1 /*singleStoreHandler.GetStoreSkusBatchSize(partner.FuncCreateStoreSkus)*/, isContinueWhenError) + }, ctx, task, createList, 1 /*singleStoreHandler.GetStoreSkusBatchSize(partner.FuncCreateStoreSkus)*/, isContinueWhenError2) } case 2: if len(updateList) > 0 { @@ -507,7 +508,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo updateStoreSku(dao.GetDB(), vendorID, successList, model.SyncFlagModifiedMask) } return nil, len(successList), err - }, ctx, task, updateList, singleStoreHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkus), isContinueWhenError) + }, ctx, task, updateList, singleStoreHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkus), isContinueWhenError2) } case 3: for k, list := range [][]*partner.StoreSkuInfo{stockList /*, onlineList*/} { @@ -526,7 +527,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagStockMask) } return nil, len(successList), err - }, ctx, task, list, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStock), isContinueWhenError) + }, ctx, task, list, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStock), isContinueWhenError2) } } case 4, 5: @@ -550,7 +551,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagSaleMask) } return nil, len(successList), err - }, ctx, task, statusList, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStatus), isContinueWhenError) + }, ctx, task, statusList, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStatus), isContinueWhenError2) } case 6: if len(priceList) > 0 { @@ -565,7 +566,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagPriceMask) } return nil, len(successList), err - }, ctx, task, priceList, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusPrice), isContinueWhenError) + }, ctx, task, priceList, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusPrice), isContinueWhenError2) } case 7: if len(reorderSkuMap) > 0 { @@ -573,7 +574,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo for vendorCatID := range reorderSkuMap { vendorCatIDs = append(vendorCatIDs, vendorCatID) } - reorderTask := tasksch.NewParallelTask("门店商品排序", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, + reorderTask := tasksch.NewParallelTask("门店商品排序", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError2), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { vendorCatID := batchItemList[0].(string) skuList := reorderSkuMap[vendorCatID] diff --git a/business/jxstore/misc/misc.go b/business/jxstore/misc/misc.go index ffbf0b5c0..d5ecebe95 100644 --- a/business/jxstore/misc/misc.go +++ b/business/jxstore/misc/misc.go @@ -146,11 +146,14 @@ func doDailyWork() { cms.SyncStoresCourierInfo(jxcontext.AdminCtx, nil, false, true) netprinter.RebindAllPrinters(jxcontext.AdminCtx, false, true) // cms.CurVendorSync.FullSyncStoresSkus(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDJD}, nil, false, true, true) - - cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDJD}, nil, false, nil, []int{27379}, model.SyncFlagSaleMask|model.SyncFlagPriceMask, true, true) + syncFlag := model.SyncFlagPriceMask + if (time.Now().Unix()/24*3600)%10 == 0 { + syncFlag |= model.SyncFlagSaleMask + } + cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDJD}, nil, false, nil, []int{27379}, syncFlag, true, true) SaveImportantTaskID(TaskNameSyncStoreSku, SpecialTaskID) - taskID, _ := cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDEBAI, model.VendorIDMTWM}, nil, false, nil, nil, model.SyncFlagSaleMask|model.SyncFlagPriceMask, true, true) + taskID, _ := cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDEBAI, model.VendorIDMTWM}, nil, false, nil, nil, syncFlag, true, true) SaveImportantTaskID(TaskNameSyncStoreSku, taskID) InitEx() diff --git a/business/jxstore/tempop/tempop.go b/business/jxstore/tempop/tempop.go index 7a20d959d..eed263e6c 100644 --- a/business/jxstore/tempop/tempop.go +++ b/business/jxstore/tempop/tempop.go @@ -8,11 +8,13 @@ import ( "image/png" "net/http" "regexp" + "strings" "sync" "time" "git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin" "git.rosy.net.cn/jx-callback/business/partner/delivery" + "github.com/360EntSecGroup-Skylar/excelize" "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/mtwmapi" @@ -1302,3 +1304,58 @@ func FixMtwmCategory(ctx *jxcontext.Context, mtwmStoreIDs []int, isAsync, isCont } return hint, err } + +func JdStoreInfo1125() (hint string, err error) { + fileName := "/Users/xujianhua/Downloads/老格恢复拓店进度.xlsx" + db := dao.GetDB() + storeList, err := dao.GetStoresMapList(db, []int{model.VendorIDJD}, nil, model.StoreStatusAll, model.StoreIsSyncYes, "") + if err == nil { + var validStoreList []*dao.StoreDetail + for _, v := range storeList { + if v.Status != model.StoreStatusDisabled && v.CreatedAt.Sub(utils.Str2Time("2019-10-01")) > 0 { + storeInfo, err := api.JdAPI.GetStoreInfoByStationNo2(v.VendorStoreID) + if err == nil && storeInfo.CreateTime.GoTime().Sub(utils.Str2Time("2019-10-25")) > 0 { + if storeDetail, err := dao.GetStoreDetail(db, v.StoreID, v.VendorID); err == nil { + validStoreList = append(validStoreList, storeDetail) + } + } + } + } + getStoreList := func(lng, lat, lng2, lat2 int) (vendorStoreIDs []string) { + for _, v := range validStoreList { + if v.Lng >= lng && v.Lng <= lng2 && v.Lat >= lat && v.Lat <= lat2 { + vendorStoreIDs = append(vendorStoreIDs, v.VendorStoreID) + } + } + return vendorStoreIDs + } + sheetName := "老格明细" + file, err2 := excelize.OpenFile(fileName) + if err = err2; err == nil { + // globals.SugarLogger.Debug(err, file) + rows, err2 := file.GetRows(sheetName) + if err = err2; err == nil { + str2Coords := func(str string) (lng, lat int) { + list := strings.Split(str, ",") + if len(list) >= 2 { + lng, lat = jxutils.StandardCoordinate2Int(utils.Str2Float64WithDefault(list[1], 0)), jxutils.StandardCoordinate2Int(utils.Str2Float64WithDefault(list[0], 0)) + } + return lng, lat + } + for i := 1; i < len(rows); i++ { + lng, lat := str2Coords(rows[i][8]) + lng2, lat2 := str2Coords(rows[i][7]) + vendorStoreIDs := getStoreList(lng, lat, lng2, lat2) + // fmt.Printf("%d,%v", i, vendorStoreIDs) + countInfo := fmt.Sprintf("京西已拓%d", len(vendorStoreIDs)) + axis, _ := excelize.CoordinatesToCellName(5, i+1) + file.SetCellStr(sheetName, axis, countInfo) + axis2, _ := excelize.CoordinatesToCellName(6, i+1) + file.SetCellStr(sheetName, axis2, strings.Join(vendorStoreIDs, ",")) + } + file.SaveAs("ffff.xlsx") + } + } + } + return hint, err +} diff --git a/business/jxstore/tempop/tempop_test.go b/business/jxstore/tempop/tempop_test.go new file mode 100644 index 000000000..4ad4b6e38 --- /dev/null +++ b/business/jxstore/tempop/tempop_test.go @@ -0,0 +1,26 @@ +package tempop + +import ( + "testing" + + "git.rosy.net.cn/jx-callback/globals/api2" + "git.rosy.net.cn/jx-callback/globals/testinit" + + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc" +) + +func init() { + testinit.Init() + api2.Init() +} + +func TestJdStoreInfo1125(t *testing.T) { + _, err := JdStoreInfo1125() + if err != nil { + t.Fatal(err) + } +} diff --git a/business/jxstore/yonghui/yonghui.go b/business/jxstore/yonghui/yonghui.go index 4374bea0c..ec558980f 100644 --- a/business/jxstore/yonghui/yonghui.go +++ b/business/jxstore/yonghui/yonghui.go @@ -6,16 +6,25 @@ import ( "io" "math" "mime/multipart" + "strings" + "sync" + "time" "unicode" "git.rosy.net.cn/baseapi" "git.rosy.net.cn/baseapi/utils" "github.com/360EntSecGroup-Skylar/excelize" + "git.rosy.net.cn/baseapi/platformapi/dingdingapi" "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/weimobapi" + "git.rosy.net.cn/jx-callback/business/jxstore/cms" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" + "git.rosy.net.cn/jx-callback/business/jxutils/excel" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals" @@ -26,64 +35,156 @@ import ( type SheetParam struct { SkuIDCol int SkuPriceCol int + SkuNameCol int OrgSkuIdCol int OrgSkuPriceCol int + OrgSkuNameCol int SkuRow int } +type DataSuccessLock struct { + dataSuccessList []DataSuccess + locker sync.RWMutex +} + +type DataFailedLock struct { + dataFailedList []DataFailed + locker sync.RWMutex +} + +type DataSuccess struct { + NameID string `json:"商品nameID"` + Name string `json:"商品名称"` + Unit string `json:"单位"` + OrgPrice float64 `json:"原价"` + NowPrice float64 `json:"现价"` + MixPrice float64 `json:"涨跌"` +} + +type DataFailed struct { + GoodsID string `json:"商品ID"` + GoodsName string `json:"商品名称"` + Comment string `json:"备注"` +} + +type Data struct { + GoodsID string `json:"商品编码"` + GoodsName string `json:"商品名称"` + GoodsNum int `json:"订货数量"` +} + +type ExcelParam struct { + SpuCode string + Name string + Price float64 +} + +type OrderList struct { + Name string `json:"name"` + Phone string `json:"phone"` + OrderNo int64 `json:"orderNo"` +} + var ( sheetMap = map[string]*SheetParam{ "蔬菜": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 14, + SkuNameCol: 1, OrgSkuIdCol: 5, OrgSkuPriceCol: 8, + OrgSkuNameCol: 6, SkuRow: 2, }, "水果": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 14, + SkuNameCol: 1, OrgSkuIdCol: 5, OrgSkuPriceCol: 8, + OrgSkuNameCol: 6, SkuRow: 2, }, "肉禽": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 12, + SkuNameCol: 1, OrgSkuIdCol: 4, OrgSkuPriceCol: 7, + OrgSkuNameCol: 5, SkuRow: 1, }, "净配": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 12, + SkuNameCol: 1, OrgSkuIdCol: 4, OrgSkuPriceCol: 7, + OrgSkuNameCol: 5, SkuRow: 1, }, "水产": &SheetParam{ SkuIDCol: 1, SkuPriceCol: 15, + SkuNameCol: 2, OrgSkuIdCol: 6, OrgSkuPriceCol: 9, + OrgSkuNameCol: 7, SkuRow: 1, }, "干货": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 13, + SkuNameCol: 1, OrgSkuIdCol: 4, OrgSkuPriceCol: 7, + OrgSkuNameCol: 5, SkuRow: 2, }, "MINI肉禽价格": &SheetParam{ SkuIDCol: 1, SkuPriceCol: 5, + SkuNameCol: 2, OrgSkuIdCol: -1, OrgSkuPriceCol: -1, + OrgSkuNameCol: -1, SkuRow: 1, }, } + titleListSuccess = []string{ + "商品nameID", + "商品名称", + "单位", + "原价", + "现价", + "涨跌", + } + titleListFailed = []string{ + "商品ID", + "商品名称", + "备注", + } + titleList = []string{ + "商品编码", + "商品名称", + "订货数量", + } + dataSuccess DataSuccessLock + dataFailed DataFailedLock ) const ( parallelCount = 5 UpdateGoodsShelfStatusCount = 50 //微盟下架商品api限制一次50个 + fileExt = ".xlsx" ) +func (d *DataSuccessLock) AppendData(dataSuccess DataSuccess) { + d.locker.Lock() + defer d.locker.Unlock() + d.dataSuccessList = append(d.dataSuccessList, dataSuccess) +} + +func (d *DataFailedLock) AppendData2(dataFailed DataFailed) { + d.locker.Lock() + defer d.locker.Unlock() + d.dataFailedList = append(d.dataFailedList, dataFailed) +} + func LoadExcelByYongHui(ctx *jxcontext.Context, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) { if len(files) == 0 { return "", errors.New("没有文件上传!") @@ -99,11 +200,12 @@ func LoadExcelByYongHui(ctx *jxcontext.Context, files []*multipart.FileHeader, i func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, isContinueWhenError bool) (hint string, err error) { var ( - skuMap = make(map[string]float64) + skuMap = make(map[string]*ExcelParam) errMsg string costPrice float64 //成本价 goodsList []*weimobapi.GoodsInfo goodsIDListForPutAway []interface{} + isCompare bool ) db := dao.GetDB() taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { @@ -121,7 +223,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is if rowNum < sheetParam.SkuRow { continue } - GetCellIntoMap(sheetParam.SkuIDCol, sheetParam.SkuPriceCol, sheetParam.OrgSkuIdCol, sheetParam.OrgSkuPriceCol, skuMap, row, k, rowNum) + GetCellIntoMap(sheetParam, skuMap, row, k, rowNum) if len(skuMap) < 1 { errMsg += fmt.Sprintf("读取Excel数据失败,Excel格式排版可能发生了变化!sheetName: [%v]\n", k) } @@ -133,10 +235,14 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is //修改分组名 // 分类名格式为:可定XX日 // XX为上传永辉 提供的 价格表时间 +2天 - + isCompare, err = UpdateClassifyAndGetLastClassify() case 1: //获取微盟所有商品 - goodsList, err = GetWeiMobGoodsList() + param := &weimobapi.QueryGoodsListParam{ + PageNum: 1, + PageSize: jdapi.MaxSkuIDsCount4QueryListBySkuIds, + } + goodsList, err = GetWeiMobGoodsList(param) if err != nil { baseapi.SugarLogger.Errorf("GetWeiMobGoodsList error:%v", err) } @@ -157,12 +263,15 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is for k, _ := range skuMap { //表示excel上有,微盟上没有 if goodsInfoAndDetailMap[k] == nil { - errMsg += fmt.Sprintf("在微盟上未找到该商品!excel商品ID : [%v]\n", k) + outPutData := DataFailed{ + GoodsID: k, + GoodsName: skuMap[k].Name, + Comment: "在微盟上未找到该商品", + } + dataFailed.AppendData2(outPutData) + // errMsg += fmt.Sprintf("在微盟上未找到该商品xxx", xxx) } } - // if errMsg != "" { - // return "", errors.New(errMsg) - // } case 2: //找出微盟上有,excel上没有的,有就更新,没有就下架 taskFunc3 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { @@ -171,24 +280,28 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is spuCode := goodsDetail.OuterGoodsCode if spuCode != "" { //如果微盟商品里找得到excel中的商品 - if skuMap[spuCode] != 0 { + if skuMap[spuCode] != nil { //获取京西库商品 skuList, _ := dao.GetSkus(db, nil, []int{int(utils.Str2Int64(goodsDetail.SkuMap.SingleSku.OuterSkuCode))}, nil, nil) if len(skuList) == 0 { - return "", errors.New(fmt.Sprintf("在京西库中未找到该商品!name_id : [%v]\n", goodsDetail.SkuMap.SingleSku.OuterSkuCode)) + outPutData := DataFailed{ + GoodsID: spuCode, + GoodsName: skuMap[spuCode].Name, + Comment: "在京西库中未找到该商品", + } + dataFailed.AppendData2(outPutData) + // return "", errors.New(fmt.Sprintf("在京西库中未找到该商品!name_id : [%v]\n", goodsDetail.SkuMap.SingleSku.OuterSkuCode)) } else { if skuList[0].Unit == "份" { if goodsDetail.SkuMap.SingleSku.B2CSku.Weight == 0 { - costPrice = skuMap[spuCode] + costPrice = skuMap[spuCode].Price } else { - costPrice = Float64Round(0.5 / goodsDetail.SkuMap.SingleSku.B2CSku.Weight * skuMap[spuCode]) + costPrice = Float64Round(0.5 / goodsDetail.SkuMap.SingleSku.B2CSku.Weight * skuMap[spuCode].Price) } } else { - costPrice = skuMap[spuCode] + costPrice = skuMap[spuCode].Price } - // if errMsg == "" { - _, _, err = updateWeiMobGoods(costPrice, skuMap[spuCode], goodsDetail) - // } + _, _, _ = updateWeiMobGoods(costPrice, skuMap[spuCode].Price, skuList[0].Unit, isCompare, goodsDetail) } } else { //下架微盟商品 @@ -197,7 +310,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is } return retVal, err } - taskParallel3 := tasksch.NewParallelTask("根据获取的微盟所有商品并更新", tasksch.NewParallelConfig().SetParallelCount(parallelCount).SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc3, goodsList) + taskParallel3 := tasksch.NewParallelTask("根据获取的微盟所有商品并更新", tasksch.NewParallelConfig().SetParallelCount(parallelCount), ctx, taskFunc3, goodsList) tasksch.HandleTask(taskParallel3, task, true).Run() goodsIDListForPutAwayInterface, err2 := taskParallel3.GetResult(0) if err = err2; err != nil { @@ -206,7 +319,6 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is goodsIDListForPutAway = goodsIDListForPutAwayInterface case 3: // 批量下架微盟商品 - // if errMsg == "" { taskFunc4 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { int64Slice := []int64{} for _, v := range batchItemList { @@ -218,14 +330,15 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, is taskParallel4 := tasksch.NewParallelTask("下架微盟商品", tasksch.NewParallelConfig().SetParallelCount(parallelCount).SetBatchSize(UpdateGoodsShelfStatusCount), ctx, taskFunc4, goodsIDListForPutAway) tasksch.HandleTask(taskParallel4, task, true).Run() _, err = taskParallel4.GetResult(0) - // } - } - if errMsg != "" { - return result, errors.New(errMsg) + case 4: + WriteToExcel(task, dataSuccess.dataSuccessList, dataFailed.dataFailedList) } + // if errMsg != "" { + // return result, errors.New(errMsg) + // } return result, err } - taskSeq := tasksch.NewSeqTask2("读取永辉Excel文件修改微盟商品价格可售状态-序列任务", ctx, isContinueWhenError, taskSeqFunc, 4) + taskSeq := tasksch.NewSeqTask2("读取永辉Excel文件修改微盟商品价格可售状态-序列任务", ctx, isContinueWhenError, taskSeqFunc, 5) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) @@ -265,13 +378,14 @@ func GetGoodsInfoAndDetailMap(goodsList []*weimobapi.GoodsInfo) (goodsMap map[st // return list // } -func updateWeiMobGoods(costPrice, salePrice float64, goodsDetail *weimobapi.GoodsDetailInfo) (goodsID int64, skuMap map[string]int64, err error) { +func updateWeiMobGoods(costPrice, salePrice float64, unit string, isCompare bool, goodsDetail *weimobapi.GoodsDetailInfo) (goodsID int64, skuMap map[string]int64, err error) { var ( categoryList []*weimobapi.CategoryList skuListInfo = goodsDetail.SkuList[0] skuListParam []*weimobapi.SkuList categoryID int deliveryTypeListSlice []int64 + newSkuTitle string ) //查询配送方式 @@ -298,16 +412,51 @@ func updateWeiMobGoods(costPrice, salePrice float64, goodsDetail *weimobapi.Good return 0, nil, errors.New(fmt.Sprintf("未查询到此商品的分类信息!goodsID : [%v] ,", goodsDetail.GoodsID)) } - //获取分组 + //商品分组(原有的分组IDs) selectedClassifyList := goodsDetail.SelectedClassifyList - var selectedClassifyListID []int64 + selectedClassifyListIDMap := make(map[int64]int64) if len(selectedClassifyList) > 0 { for _, v := range selectedClassifyList { - for _, vv := range v.ChildrenClassify { - selectedClassifyListID = append(selectedClassifyListID, vv.ClassifyID) + for _, v := range v.ChildrenClassify { + selectedClassifyListIDMap[v.ClassifyID] = v.ClassifyID } } } + + //上次上传价格和这次上传价格对比 + // 4、微盟商品。两个重要分组 + // 降价超0.2元,与前一日数据对比,需要将此类商品增加分类到这里,不改变原分类 + // 涨价超0.2元,与前一日数据对比,需要将此类商品增加分类到这里,不改变原分类 + // 其他商品,取消 这两个分类 + // 同一天第二次上传的处理,需要注意是与前一天价格的对比,不是当天的价格对比。 + + // 5. 涨价和跌价值保留2位小数 + // 需要在每个商品中增加↑0.01或↓0.02。 + // 显示在商品最前的括号里,如果没有括号,需要增加。都用小写的括号,大写括号需要删除。 + // 包装菜需要增加“精”,其他不需要加字,比如: + // (精↓2.11)新土豆1kg/袋 + // (↓2.11)新土豆1kg + //salePrice 这次上传的价格 goodsDetail.SkuMap.SingleSku.SalePrice 上次上传的价格 priceMix 两次上传价格差 priceMixRound 保留两位后的价格差 + if !isCompare { + priceMix := salePrice - goodsDetail.SkuMap.SingleSku.SalePrice + if priceMix > 0.2 { + delete(selectedClassifyListIDMap, 1064198248) + delete(selectedClassifyListIDMap, 1065246248) + selectedClassifyListIDMap[1064198248] = 1064198248 + } else if priceMix < -0.2 { + delete(selectedClassifyListIDMap, 1064198248) + delete(selectedClassifyListIDMap, 1065246248) + selectedClassifyListIDMap[1065246248] = 1065246248 + } else { + delete(selectedClassifyListIDMap, 1064198248) + delete(selectedClassifyListIDMap, 1065246248) + } + priceMixRound := Float64Round(priceMix) + newSkuTitle = GetNewSkuTitle(priceMixRound, goodsDetail.Title, goodsDetail.OuterGoodsCode) + } else { + newSkuTitle = goodsDetail.Title + } + b2CSku := &weimobapi.B2CSku{ Weight: skuListInfo.B2CSku.Weight, Volume: skuListInfo.B2CSku.Volume, @@ -329,7 +478,7 @@ func updateWeiMobGoods(costPrice, salePrice float64, goodsDetail *weimobapi.Good goods := &weimobapi.Goods{ B2CGoods: b2CGoods, SkuList: skuListParam, - Title: goodsDetail.Title, + Title: newSkuTitle, IsMultiSku: goodsDetail.IsMultiSku, IsPutAway: weimobapi.GoodsTypeNormal, GoodsImageURL: goodsDetail.GoodsImageURL, @@ -337,22 +486,38 @@ func updateWeiMobGoods(costPrice, salePrice float64, goodsDetail *weimobapi.Good CategoryID: categoryID, OuterGoodsCode: goodsDetail.OuterGoodsCode, PointDeductRatio: goodsDetail.PointDeductRatio, - SelectedClassifyIDList: selectedClassifyListID, + SelectedClassifyIDList: Map2Int64Slice(selectedClassifyListIDMap), } updateGoodsParam := &weimobapi.UpdateGoodsParam{ Goods: goods, } if globals.EnableStoreWrite { goodsID, skuMap, err = api.WeimobAPI.UpdateGoods3(updateGoodsParam) + if err != nil { + if errExt, ok := err.(*utils.ErrorWithCode); ok { + outPutData := DataFailed{ + GoodsID: goodsDetail.OuterGoodsCode, + GoodsName: goodsDetail.Title, + Comment: errExt.ErrMsg(), + } + dataFailed.AppendData2(outPutData) + } + } else { + outPutData := DataSuccess{ + NameID: goodsDetail.SkuMap.SingleSku.OuterSkuCode, + Name: goodsDetail.Title, + Unit: unit, + OrgPrice: goodsDetail.SkuMap.SingleSku.SalePrice, + NowPrice: salePrice, + MixPrice: Float64Round(salePrice - goodsDetail.SkuMap.SingleSku.SalePrice), + } + dataSuccess.AppendData(outPutData) + } } return goodsID, skuMap, err } -func GetWeiMobGoodsList() (goodsList []*weimobapi.GoodsInfo, err error) { - param := &weimobapi.QueryGoodsListParam{ - PageNum: 1, - PageSize: jdapi.MaxSkuIDsCount4QueryListBySkuIds, - } +func GetWeiMobGoodsList(param *weimobapi.QueryGoodsListParam) (goodsList []*weimobapi.GoodsInfo, err error) { for { goodsInfoList, _, err := api.WeimobAPI.QueryGoodsList(param) if err != nil { @@ -378,52 +543,427 @@ func IsChineseChar(str string) bool { return false } -func GetCellIntoMap(skuIDCol, skuPriceCol, orgSkuIDCol, orgSkuPriceCol int, skuMap map[string]float64, row []string, sheetName string, rowNum int) { +func GetCellIntoMap(sheetParam *SheetParam, skuMap map[string]*ExcelParam, row []string, sheetName string, rowNum int) { var ( - skuID string - orgSkuID string - skuPrice float64 - orgSkuPrice float64 + skuID string + orgSkuID string + skuPrice float64 + orgSkuPrice float64 + skuName string + orgSkuName string + skuIDCol = sheetParam.SkuIDCol + skuPriceCol = sheetParam.SkuPriceCol + skuNameCol = sheetParam.SkuNameCol + orgSkuIDCol = sheetParam.OrgSkuIdCol + orgSkuPriceCol = sheetParam.OrgSkuPriceCol + orgSkuNameCol = sheetParam.OrgSkuNameCol ) for k, cell := range row { - if !IsChineseChar(cell) && cell != "" { - if k == skuIDCol && skuIDCol >= 0 { - skuID = cell + if cell != "" { + if !IsChineseChar(cell) { + if k == skuIDCol && skuIDCol >= 0 { + skuID = cell + } + if k == skuPriceCol && skuPriceCol >= 0 { + skuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + } + if k == orgSkuIDCol && orgSkuIDCol >= 0 { + orgSkuID = "0" + cell + } + if k == orgSkuPriceCol && orgSkuPriceCol >= 0 { + orgSkuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + } } - if k == skuPriceCol && skuPriceCol >= 0 { - skuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + if k == skuNameCol && skuNameCol >= 0 { + skuName = cell } - if k == orgSkuIDCol && orgSkuIDCol >= 0 { - orgSkuID = "0" + cell - } - if k == orgSkuPriceCol && orgSkuPriceCol >= 0 { - orgSkuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + if k == orgSkuNameCol && orgSkuNameCol >= 0 { + orgSkuName = cell } } } - if skuMap[skuID] != 0 && skuMap[skuID] != skuPrice { - if skuPrice > skuMap[skuID] { - skuMap[skuID] = skuPrice + if len(skuMap) > 0 { + if skuMap[skuID] != nil { + if skuMap[skuID].Price != 0 && skuMap[skuID].Price != skuPrice && skuPrice != 0 { + if skuPrice > skuMap[skuID].Price { + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + } + } else { + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + } + } else if skuPrice != 0 { + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + } + if skuMap[orgSkuID] != nil { + if skuMap[orgSkuID].Price != 0 && skuMap[orgSkuID].Price != orgSkuPrice && orgSkuPrice != 0 { + if orgSkuPrice > skuMap[orgSkuID].Price { + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) + } + } else if orgSkuPriceCol >= 0 && orgSkuIDCol >= 0 && orgSkuNameCol >= 0 { + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) + } + } else if orgSkuPrice != 0 { + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) } - // fmt.Sprintf("读取excel表格出错!有商品ID重复且价格不同,sheetName : [%v] ,行数 : [%v] , 商品编码 :[%v] , 价格:[%v]\n", sheetName, rowNum, skuID, skuPrice) } else { - skuMap[skuID] = skuPrice - } - if skuMap[orgSkuID] != 0 && skuMap[orgSkuID] != orgSkuPrice { - if orgSkuPrice > skuMap[orgSkuID] { - skuMap[orgSkuID] = orgSkuPrice - } - // fmt.Sprintf("读取excel表格出错!有商品ID重复且价格不同,sheetName : [%v] ,行数 : [%v], 商品编码 :[%v] , 价格:[%v]\n", sheetName, rowNum, orgSkuID, orgSkuPrice) - } else if orgSkuPriceCol >= 0 && orgSkuIDCol >= 0 { - skuMap[orgSkuID] = orgSkuPrice + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) } delete(skuMap, "") } +func BuildSkuMap(id, name string, price float64, skuMap map[string]*ExcelParam) { + excelParam := &ExcelParam{ + SpuCode: id, + Price: price, + Name: name, + } + skuMap[id] = excelParam +} func Float64Round(f float64) (flt float64) { return math.Round(f*100) / 100 } -func UpdateClassifyName() { - +func Map2Int64Slice(m map[int64]int64) (result []int64) { + for _, v := range m { + result = append(result, v) + } + return result +} + +func UpdateClassifyAndGetLastClassify() (isCompare bool, err error) { + var lastTitle string + classfiyList, err := api.WeimobAPI.QueryClassifyInfoList() + if len(classfiyList) > 0 { + for _, v := range classfiyList { + if v.ClassifyID == 1065244148 { + lastTitle = v.Title + } + } + } + title := "可定" + now := time.Now() + afterDay := now.AddDate(0, 0, 2).Day() + title += utils.Int2Str(afterDay) + "日" + if lastTitle == title { + return true, err + } else { + err = api.WeimobAPI.UpdateClassify(1065244148, title, "") + return false, err + } +} + +func GetNewSkuTitle(priceMixRound float64, orgTitle, outerGoodsCode string) (name string) { + var prefix = "" + title1 := strings.ReplaceAll(orgTitle, "(", "(") + title := strings.ReplaceAll(title1, ")", ")") + if outerGoodsCode[0:1] != "0" { + prefix += "(精" + if priceMixRound > 0 { + prefix += "↑" + } else if priceMixRound < 0 { + prefix += "↓" + } else { + prefix += ")" + return GetReplaceNewTitle(title, prefix) + } + } else { + if priceMixRound > 0 { + prefix += "(↑" + } else if priceMixRound < 0 { + prefix += "(↓" + } else { + return GetReplaceNewTitle(title, prefix) + } + } + prefix += utils.Float64ToStr(math.Abs(priceMixRound)) + ")" + return GetReplaceNewTitle(title, prefix) +} + +func GetReplaceNewTitle(title, prefix string) (newTitle string) { + if title[0:1] == "(" { + return strings.Replace(title, title[0:strings.Index(title, ")")+1], prefix, 1) + } else { + return prefix + title + } +} + +func WriteToExcel(task *tasksch.SeqTask, dataSuccess []DataSuccess, dataFailed []DataFailed) (err error) { + var sheetList1 []*excel.Obj2ExcelSheetConfig + var sheetList2 []*excel.Obj2ExcelSheetConfig + var downloadURL1, downloadURL2, fileName1, fileName2 string + excelConf1 := &excel.Obj2ExcelSheetConfig{ + Title: "sheet1", + Data: dataSuccess, + CaptionList: titleListSuccess, + } + sheetList1 = append(sheetList1, excelConf1) + excelConf2 := &excel.Obj2ExcelSheetConfig{ + Title: "sheet1", + Data: dataFailed, + CaptionList: titleListFailed, + } + sheetList2 = append(sheetList2, excelConf2) + if excelConf1 != nil { + downloadURL1, fileName1, err = UploadExeclAndPushMsg(sheetList1, "已更新商品") + } else { + baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!") + } + if excelConf2 != nil { + downloadURL2, fileName2, err = UploadExeclAndPushMsg(sheetList2, "缺少商品_微盟") + } else { + baseapi.SugarLogger.Debug("WriteToExcel: dataFailed is nil!") + } + if err != nil { + baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName1, fileName2, err) + } else { + noticeMsg := fmt.Sprintf("[详情点我]path1=%s, path2=%s \n", globals.BackstageHost, downloadURL1, downloadURL2) + task.SetNoticeMsg(noticeMsg) + baseapi.SugarLogger.Debugf("WriteToExcel:upload %s ,%s success, downloadURL1:%s ,downloadURL2:%s", fileName1, fileName2, downloadURL1, downloadURL2) + } + return err +} + +func UploadExeclAndPushMsg(sheetList []*excel.Obj2ExcelSheetConfig, name string) (downloadURL, fileName string, err error) { + excelBin := excel.Obj2Excel(sheetList) + timeStr := utils.Int64ToStr(time.Now().Unix()) + fileName = name + timeStr + fileExt + baseapi.SugarLogger.Debugf("WriteToExcel:save %s success", fileName) + downloadURL, err = jxutils.UploadExportContent(excelBin, fileName) + return downloadURL, fileName, err +} + +func UpdateJxPriceByWeimob(ctx *jxcontext.Context, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { + var ( + storeSkuBindInfoList []interface{} + skuBindInfos []*cms.StoreSkuBindInfo + ) + //获取微盟所有上架商品 + queryParameter := &weimobapi.QueryGoodsListRequestVo{ + GoodsStatus: weimobapi.GoodsTypeNormal, + } + param := &weimobapi.QueryGoodsListParam{ + PageNum: 1, + PageSize: jdapi.MaxSkuIDsCount4QueryListBySkuIds, + QueryParameter: queryParameter, + } + goodsList, err := GetWeiMobGoodsList(param) + if err != nil { + baseapi.SugarLogger.Errorf("GetWeiMobGoodsList error:%v", err) + } + taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { + switch step { + case 0: + taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + v := batchItemList[0].(*weimobapi.GoodsInfo) + goodsDetail, err := api.WeimobAPI.QueryGoodsDetail(v.GoodsID) + if err != nil { + baseapi.SugarLogger.Errorf("QueryGoodsDetail error:%v", err) + } + if goodsDetail.OuterGoodsCode != "" && goodsDetail.IsPutAway == weimobapi.GoodsTypeNormal { + nameID := int(utils.Str2Int64(goodsDetail.SkuMap.SingleSku.OuterSkuCode)) + unitPrice := int(utils.Float64TwoInt64(goodsDetail.SkuMap.SingleSku.CostPrice * 100)) + storeSkuBindInfo := &cms.StoreSkuBindInfo{ + NameID: nameID, + UnitPrice: unitPrice, + } + retVal = []*cms.StoreSkuBindInfo{storeSkuBindInfo} + } + return retVal, err + } + taskParallel := tasksch.NewParallelTask("获取微盟商品", tasksch.NewParallelConfig().SetParallelCount(parallelCount), ctx, taskFunc, goodsList) + tasksch.HandleTask(taskParallel, task, true).Run() + storeSkuBindInfoList, err = taskParallel.GetResult(0) + case 1: + for _, v := range storeSkuBindInfoList { + skuBindInfos = append(skuBindInfos, v.(*cms.StoreSkuBindInfo)) + } + cms.UpdateStoresSkus(ctx, storeIDs, skuBindInfos, isAsync, isContinueWhenError) + } + return result, err + } + taskSeq := tasksch.NewSeqTask2("根据微盟商品更新京西价", ctx, isContinueWhenError, taskSeqFunc, 2) + tasksch.HandleTask(taskSeq, nil, true).Run() + if !isAsync { + _, err = taskSeq.GetResult(0) + hint = "1" + } else { + hint = taskSeq.GetID() + } + return hint, err +} + +func GetWeimobOrders(ctx *jxcontext.Context, fromTime, toTime string, params map[string]interface{}) (result []*OrderList, err error) { + if fromTime != "" && toTime != "" { + fromTimeParam := utils.Str2Time(fromTime).UnixNano() / 1e6 + toTimeParam := utils.Str2Time(toTime).UnixNano() / 1e6 + if params["keyword"] != nil { + if jxutils.GetPossibleVendorIDFromVendorOrderID(params["keyword"].(string)) > model.VendorIDUnknown { + resultList, err := GetSingleOrderResultList(params) + result = append(result, resultList...) + return result, err + } else { + orderList, err := GetWeimobOrdersList(fromTimeParam, toTimeParam, params["keyword"].(string)) + return orderList, err + } + } else { + orderList, err := GetWeimobOrdersList(fromTimeParam, toTimeParam, "") + return orderList, err + } + } + if fromTime == "" && toTime == "" && params["keyword"] != nil { + if jxutils.GetPossibleVendorIDFromVendorOrderID(params["keyword"].(string)) > model.VendorIDUnknown { + resultList, err := GetSingleOrderResultList(params) + result = append(result, resultList...) + return result, err + } else { + orderList, err := GetWeimobOrdersList(0, 0, params["keyword"].(string)) + return orderList, err + } + } + return result, err +} + +func GetWeimobOrdersList(fromTimeParam, toTimeParam int64, keyword string) (aList []*OrderList, err error) { + var queryParameter *weimobapi.MerchantOrderListQueryParameter + if fromTimeParam == 0 && toTimeParam == 0 { + queryParameter = &weimobapi.MerchantOrderListQueryParameter{ + CreateStartTime: time.Now().AddDate(0, -2, 0).UnixNano() / 1e6, + CreateEndTime: time.Now().UnixNano() / 1e6, + } + } else { + queryParameter = &weimobapi.MerchantOrderListQueryParameter{ + CreateStartTime: fromTimeParam, + CreateEndTime: toTimeParam, + } + } + param := &weimobapi.QueryOrdersListParam{ + PageNum: 1, + PageSize: weimobapi.QueryOrdersListPageSize, + QueryParameter: queryParameter, + } + for { + orderList, _, err2 := api.WeimobAPI.QueryOrdersList(param) + err = err2 + if len(orderList) > 0 { + if keyword != "" { + for _, v := range orderList { + if ContainsKeyword(v, keyword) { + var aOrder = &OrderList{ + Name: v.ReceiverName, + OrderNo: v.OrderNo, + Phone: v.ReceiverMobile, + } + aList = append(aList, aOrder) + } + } + } else { + for _, v := range orderList { + var aOrder = &OrderList{ + Name: v.ReceiverName, + OrderNo: v.OrderNo, + Phone: v.ReceiverMobile, + } + aList = append(aList, aOrder) + } + } + } + if len(orderList) < param.PageSize { + break + } + param.PageNum++ + } + return aList, err +} + +func GetWeimobOrderDetail(orderNo int64) (order *weimobapi.OrderDetail, err error) { + return api.WeimobAPI.QueryOrderDetail2(orderNo, false) +} + +func GetSingleOrderResultList(params map[string]interface{}) (result []*OrderList, err error) { + weimobOrderID := params["keyword"].(string) + orderSingle, err := GetWeimobOrderDetail(utils.Str2Int64(weimobOrderID)) + orderList := &OrderList{ + Name: orderSingle.DeliveryDetail.LogisticsDeliveryDetail.ReceiverName, + Phone: orderSingle.DeliveryDetail.LogisticsDeliveryDetail.ReceiverMobile, + OrderNo: orderSingle.OrderNo, + } + result = append(result, orderList) + return result, err +} + +func ContainsKeyword(v *weimobapi.OrderInfo, keyword string) bool { + return strings.Contains(v.ReceiverName, keyword) || strings.Contains(utils.Int64ToStr(v.OrderNo), keyword) || strings.Contains(v.ReceiverMobile, keyword) +} + +func GetWeimobOrdersExcel(ctx *jxcontext.Context, OrderNo string) (err error) { + var ( + DataFineList []*Data //精品 + DataHairyList []*Data //毛菜 + ) + orderSingle, err := GetWeimobOrderDetail(utils.Str2Int64(OrderNo)) + itemList := orderSingle.ItemList + for _, v := range itemList { + if v.GoodsCode[0:1] == "0" { + DataHairy := &Data{ + GoodsID: v.GoodsCode, + GoodsName: v.GoodsTitle, + GoodsNum: v.SkuNum, + } + DataHairyList = append(DataHairyList, DataHairy) + } else { + DataFine := &Data{ + GoodsID: v.GoodsCode, + GoodsName: v.GoodsTitle, + GoodsNum: v.SkuNum, + } + DataFineList = append(DataFineList, DataFine) + } + } + WriteToExcel2(ctx, DataFineList, DataHairyList) + return err +} + +func WriteToExcel2(ctx *jxcontext.Context, DataFineList, DataHairyList []*Data) (err error) { + var ( + sheetList1 []*excel.Obj2ExcelSheetConfig + sheetList2 []*excel.Obj2ExcelSheetConfig + downloadURL1, downloadURL2 string + fileName1, fileName2 string + noticeMsg string + ) + excelConf1 := &excel.Obj2ExcelSheetConfig{ + Title: "sheet1", + Data: DataFineList, + CaptionList: titleList, + } + sheetList1 = append(sheetList1, excelConf1) + excelConf2 := &excel.Obj2ExcelSheetConfig{ + Title: "sheet1", + Data: DataHairyList, + CaptionList: titleList, + } + sheetList2 = append(sheetList2, excelConf2) + noticeMsg += "[详情点我]" + if len(DataFineList) > 0 { + downloadURL1, fileName1, err = UploadExeclAndPushMsg(sheetList1, "京西采购_精品") + noticeMsg += "path1=" + downloadURL1 + " " + } else { + baseapi.SugarLogger.Debug("WriteToExcel: DataFineList is nil!") + } + if len(DataHairyList) > 0 { + downloadURL2, fileName2, err = UploadExeclAndPushMsg(sheetList2, "京西采购_毛菜") + noticeMsg += "path2=" + downloadURL2 + } else { + baseapi.SugarLogger.Debug("WriteToExcel: DataHairyList is nil!") + } + if err != nil { + baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName1, fileName2, err) + } else { + if authInfo, err := ctx.GetV2AuthInfo(); err == nil { + ddmsg.SendUserMessage(dingdingapi.MsgTyeText, authInfo.UserID, "导出微盟订单商品成功", noticeMsg) + baseapi.SugarLogger.Debugf("WriteToExcel:upload %s ,%s success, downloadURL1:%s ,downloadURL2:%s", fileName1, fileName2, downloadURL1, downloadURL2) + } + } + return err } diff --git a/business/jxutils/jxutils_cms.go b/business/jxutils/jxutils_cms.go index 150e96066..03d9d6951 100644 --- a/business/jxutils/jxutils_cms.go +++ b/business/jxutils/jxutils_cms.go @@ -466,7 +466,7 @@ func GetVendorName(vendorID int) (vendorName string) { func CaculateSkuEarningPrice(shopPrice, salePrice int64, storePayPercentage int) (earningPrice int64) { earningPrice = salePrice - if shopPrice > 0 && shopPrice < earningPrice { + if salePrice == 0 || shopPrice > 0 && shopPrice < earningPrice { earningPrice = shopPrice } if storePayPercentage <= 0 { diff --git a/business/model/dao/report.go b/business/model/dao/report.go index 262ef3eda..a668c5a4b 100644 --- a/business/model/dao/report.go +++ b/business/model/dao/report.go @@ -63,7 +63,7 @@ func GetStatisticsReportForOrders(db *DaoDB, storeIDs []int, fromDate time.Time, LEFT JOIN ( SELECT - a.store_id, + IF(a.jx_store_id <> 0,a.jx_store_id,store_id) store_id, COUNT(*) order_counts, SUM(sale_price) sale_price, SUM(actual_pay_price) actual_pay_price, @@ -88,11 +88,11 @@ func GetStatisticsReportForOrders(db *DaoDB, storeIDs []int, fromDate time.Time, sqlParams = append(sqlParams, fromDate, toDate) } if len(storeIDs) > 0 { - sql += ` AND a.store_id IN(` + GenQuestionMarks(len(storeIDs)) + `)` + sql += ` AND IF(a.jx_store_id != 0, a.jx_store_id, a.store_id) IN(` + GenQuestionMarks(len(storeIDs)) + `)` sqlParams = append(sqlParams, storeIDs) } sql += ` - GROUP BY a.store_id + GROUP BY 1 )s ON s.store_id = c.id ` @@ -136,7 +136,7 @@ func GetGetStatisticsReportForAfsOrders(db *DaoDB, storeIDs []int, fromDate time LEFT JOIN ( SELECT - a.store_id, + IF(a.jx_store_id <> 0,a.jx_store_id,store_id) store_id, COUNT(*) order_counts, SUM(sale_price) sale_price, SUM(actual_pay_price) actual_pay_price, @@ -160,11 +160,11 @@ func GetGetStatisticsReportForAfsOrders(db *DaoDB, storeIDs []int, fromDate time sqlParams = append(sqlParams, fromDate, toDate) } if len(storeIDs) > 0 { - sql += ` AND a.store_id IN(` + GenQuestionMarks(len(storeIDs)) + `)` + sql += ` AND IF(a.jx_store_id != 0, a.jx_store_id, a.store_id) IN(` + GenQuestionMarks(len(storeIDs)) + `)` sqlParams = append(sqlParams, storeIDs) } sql += ` - GROUP BY a.store_id + GROUP BY 1 )s ON s.store_id = c.id ` diff --git a/business/model/store.go b/business/model/store.go index 8b549576a..0250028f4 100644 --- a/business/model/store.go +++ b/business/model/store.go @@ -236,9 +236,9 @@ var ( StoreAuditStatusRejected: "拒绝", } StorePriceTypeName = map[int]string{ - StoreChangePriceTypeDirect: "可直接改价", - StoreChangePriceTypeBossDisabled: "禁止改价", - StoreChangePriceTypeManagedStore: "直营门店", + StoreChangePriceTypeDirect: "普通门店", + StoreChangePriceTypeBossDisabled: "普通门店禁止改价", + StoreChangePriceTypeManagedStore: "直营门店禁止改价", } ) diff --git a/business/partner/purchase/jd/act.go b/business/partner/purchase/jd/act.go index 2f69f6afd..668e21d05 100644 --- a/business/partner/purchase/jd/act.go +++ b/business/partner/purchase/jd/act.go @@ -18,6 +18,10 @@ import ( "git.rosy.net.cn/jx-callback/business/model" ) +const ( + actMapDuration = 2 * time.Hour +) + type LogicUpdateInfo struct { Item interface{} KVs map[string]interface{} @@ -31,6 +35,8 @@ var ( jdapi.PromotionStateCanceled: model.ActStatusCanceled, jdapi.PromotionStateEnded: model.ActStatusEnded, } + + actMap jxutils.SyncMapWithTimeout ) func splitPromotionSku(skus []*jdapi.PromotionSku, maxCount int) (skusList [][]*jdapi.PromotionSku) { @@ -62,22 +68,25 @@ func jdSkuActStatus2Jx(jdActState int) int { func CreatePromotionInfos(promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) { if globals.EnableJdStoreWrite { if promotionType == model.ActSkuDirectDown { - return getAPI("").CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising, traceId) + infoId, err = getAPI("").CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising, traceId) } else { - return getAPI("").CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising, traceId) + infoId, err = getAPI("").CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising, traceId) } } else { infoId = jxutils.GenFakeID() } + if err == nil { + actMap.StoreWithTimeout(infoId, 1, actMapDuration) + } return infoId, err } func CreatePromotionRules(promotionType int, infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) { if globals.EnableJdStoreWrite { if promotionType == model.ActSkuDirectDown { - return getAPI("").CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId) + err = getAPI("").CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId) } else { - return getAPI("").CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId) + err = getAPI("").CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId) } } return err @@ -133,9 +142,9 @@ func ConfirmPromotion(promotionType int, infoId int64, outInfoId, traceId string func CancelPromotion(promotionType int, infoId int64, outInfoId, traceId string) (err error) { if globals.EnableJdStoreWrite { if promotionType == model.ActSkuDirectDown { - return getAPI("").CancelPromotionSingle(infoId, outInfoId, traceId) + err = getAPI("").CancelPromotionSingle(infoId, outInfoId, traceId) } else { - return getAPI("").CancelPromotionLimitTime(infoId, outInfoId, traceId) + err = getAPI("").CancelPromotionLimitTime(infoId, outInfoId, traceId) } } return err @@ -144,9 +153,9 @@ func CancelPromotion(promotionType int, infoId int64, outInfoId, traceId string) func AdjustPromotionTime(promotionType int, infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) { if globals.EnableJdStoreWrite { if promotionType == model.ActSkuDirectDown { - return getAPI("").AdjustPromotionTimeSingle(infoId, outInfoId, endDate, traceId) + err = getAPI("").AdjustPromotionTimeSingle(infoId, outInfoId, endDate, traceId) } else { - return getAPI("").AdjustPromotionTimeLimitTime(infoId, outInfoId, endDate, traceId) + err = getAPI("").AdjustPromotionTimeLimitTime(infoId, outInfoId, endDate, traceId) } } return err @@ -155,9 +164,9 @@ func AdjustPromotionTime(promotionType int, infoId int64, outInfoId string, endD func AdjustPromotionSku(promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) { if globals.EnableJdStoreWrite { if promotionType == model.ActSkuDirectDown { - return getAPI("").AdjustPromotionSkuSingle(infoId, outInfoId, skus, traceId) + skusResult, err = getAPI("").AdjustPromotionSkuSingle(infoId, outInfoId, skus, traceId) } else { - return getAPI("").AdjustPromotionSkuLimitTime(infoId, outInfoId, skus, traceId) + skusResult, err = getAPI("").AdjustPromotionSkuLimitTime(infoId, outInfoId, skus, traceId) } } return skusResult, err @@ -312,19 +321,22 @@ func OnActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { func (c *PurchaseHandler) onActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { if msg.StatusID == jdapi.PromotionStatusSingleOK || msg.StatusID == jdapi.PromotionStatusLimitTimeOK { promotionID := msg.BillID - // 等几秒再执行的原因是防止通过后台自己创建时,本地还没有建好,消息就过来,导致重复记录 - // 可能的问题是在重启时丢失消息 - utils.AfterFuncWithRecover(5*time.Second, func() { - if !partner.CurActManager.IsVendorActExist(jxcontext.AdminCtx, promotionID, model.VendorIDJD) { - act, actStoreSkuList, err := getActFromJD(promotionID) - if err == nil && len(actStoreSkuList) > 0 { - _, err = partner.CurActManager.CreateActFromVendor(jxcontext.AdminCtx, act, actStoreSkuList) + intPromotionID := utils.Str2Int64(promotionID) + if _, ok := actMap.Load(intPromotionID); !ok { + utils.CallFuncAsync(func() { + if !partner.CurActManager.IsVendorActExist(jxcontext.AdminCtx, promotionID, model.VendorIDJD) { + act, actStoreSkuList, err := getActFromJD(promotionID) + if err == nil && len(actStoreSkuList) > 0 { + _, err = partner.CurActManager.CreateActFromVendor(jxcontext.AdminCtx, act, actStoreSkuList) + } + if err != nil { + retVal = jdapi.Err2CallbackResponse(err, promotionID) + } } - if err != nil { - retVal = jdapi.Err2CallbackResponse(err, promotionID) - } - } - }) + }) + } else { + actMap.Delete(intPromotionID) + } } return retVal } diff --git a/business/partner/purchase/mtwm/financial.go b/business/partner/purchase/mtwm/financial.go index b9c9cb04b..af8a4c3eb 100644 --- a/business/partner/purchase/mtwm/financial.go +++ b/business/partner/purchase/mtwm/financial.go @@ -43,7 +43,7 @@ func OnFinancialMsg(msg *mtwmapi.CallbackMsg) (err error) { func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.OrderFinancial, orderData url.Values) (afsOrder *model.AfsOrder) { afsOrder = &model.AfsOrder{ VendorID: model.VendorIDMTWM, - AfsOrderID: orderData.Get("order_id"), + AfsOrderID: orderData.Get("refund_id"), VendorOrderID: orderData.Get("order_id"), AfsCreatedAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))), // BoxMoney: orderFinancial.BoxMoney, diff --git a/business/partner/purchase/mtwm/mtwm.go b/business/partner/purchase/mtwm/mtwm.go index ceb941a8c..54b9468cb 100644 --- a/business/partner/purchase/mtwm/mtwm.go +++ b/business/partner/purchase/mtwm/mtwm.go @@ -121,7 +121,9 @@ func openTimeMtwm2JX(vendorOpenTime string) (opTimeList []int16) { timePairs := strings.Split(vendorOpenTime, ",") for _, v := range timePairs { times := strings.Split(v, "-") - opTimeList = append(opTimeList, jxutils.StrTime2JxOperationTime(times[0]+":00", 700), jxutils.StrTime2JxOperationTime(times[1]+":00", 2000)) + if len(times) >= 2 { + opTimeList = append(opTimeList, jxutils.StrTime2JxOperationTime(times[0]+":00", 700), jxutils.StrTime2JxOperationTime(times[1]+":00", 2000)) + } } return opTimeList } diff --git a/business/partner/purchase/mtwm/order.go b/business/partner/purchase/mtwm/order.go index f083e0f6e..732e44146 100644 --- a/business/partner/purchase/mtwm/order.go +++ b/business/partner/purchase/mtwm/order.go @@ -514,20 +514,6 @@ func (c *PurchaseHandler) GetOrderRealMobile(ctx *jxcontext.Context, order *mode return mobile, err } -// func (c *PurchaseHandler) GetStatusActionTimeout(order *model.GoodsOrder, statusType, status int) (params *partner.StatusActionParams) { -// if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted { -// params = &partner.StatusActionParams{ // PickDeadline没有设置时才有效,美团外卖要求在5分钟内拣货,不然订单会被取消 -// Timeout: pickupOrderDelay, -// } -// } else if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusFinishedPickup { -// params = &partner.StatusActionParams{ // 立即达订单有效,自配送延时召唤配送 -// Timeout: callDeliveryDelay, -// TimeoutGap: callDeliveryDelayGap, -// } -// } -// return params -// } - func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) { if globals.EnableMtwmStoreWrite { if isAgree { diff --git a/business/partner/purchase/mtwm/order_afs.go b/business/partner/purchase/mtwm/order_afs.go index 41b98ad4a..7928e33bc 100644 --- a/business/partner/purchase/mtwm/order_afs.go +++ b/business/partner/purchase/mtwm/order_afs.go @@ -2,6 +2,7 @@ package mtwm import ( "fmt" + "net/url" "strings" "git.rosy.net.cn/baseapi/platformapi/mtwmapi" @@ -119,8 +120,9 @@ func (c *PurchaseHandler) onAfsOrderMsg(msg *mtwmapi.CallbackMsg) (retVal *mtwma } afsOrder.PmSubsidyMoney += afsOrder.RefundMoney - afsOrder.SkuUserMoney } else { - if orderFinancial, err2 := partner.CurOrderManager.LoadOrderFinancial(orderStatus.RefVendorOrderID, model.VendorIDMTWM); err2 == nil { - afsOrder = c.OrderFinancialDetail2Refund(orderFinancial, msg.FormData) + if afsOrder = c.createAfsOrder(msg.FormData); afsOrder != nil { + // if orderFinancial, err2 := partner.CurOrderManager.LoadOrderFinancial(orderStatus.RefVendorOrderID, model.VendorIDMTWM); err2 == nil { + // afsOrder = c.OrderFinancialDetail2Refund(orderFinancial, msg.FormData) afsOrder.AfsOrderID = orderStatus.VendorOrderID afsOrder.RefundType = model.AfsTypeFullRefund afsOrder.AppealType = model.AfsAppealTypeRefund @@ -139,6 +141,52 @@ func (c *PurchaseHandler) onAfsOrderMsg(msg *mtwmapi.CallbackMsg) (retVal *mtwma return mtwmapi.Err2CallbackResponse(err, "") } +func (p *PurchaseHandler) createAfsOrder(orderData url.Values) (afsOrder *model.AfsOrder) { + afsOrder = &model.AfsOrder{ + VendorID: model.VendorIDMTWM, + AfsOrderID: orderData.Get("refund_id"), + VendorOrderID: orderData.Get("order_id"), + AfsCreatedAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))), + } + if afsOrder.AfsOrderID == "" { + afsOrder.AfsOrderID = afsOrder.VendorOrderID + } + order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID) + globals.SugarLogger.Debug(utils.Format4Output(order, false)) + if err == nil { + afsOrder.JxStoreID = order.JxStoreID + afsOrder.VendorStoreID = order.VendorStoreID + afsOrder.StoreID = order.StoreID + } else { + globals.SugarLogger.Warnf("mtwm AfsOrderDetail2Financial, afsOrderID:%s is not found from partner.CurOrderManager.LoadOrder", afsOrder.VendorOrderID) + return nil + } + + for _, sku := range order.Skus { + orderSkuFinancial := &model.OrderSkuFinancial{ + VendorID: sku.VendorID, + VendorOrderID: sku.VendorOrderID, + // OrderFinancialID: sku.VendorOrderID, + // ConfirmTime: afsOrder.AfsCreateAt, + VendorStoreID: afsOrder.VendorStoreID, + StoreID: afsOrder.StoreID, + JxStoreID: afsOrder.JxStoreID, + VendorSkuID: sku.VendorSkuID, + SkuID: sku.SkuID, + PromotionType: sku.PromotionType, + Name: sku.SkuName, + ShopPrice: sku.ShopPrice, + SalePrice: sku.SalePrice, + Count: sku.Count, + // UserMoney: sku.UserMoney, + // PmSubsidyMoney: sku.PmSubsidyMoney, + IsAfsOrder: 1, + } + afsOrder.Skus = append(afsOrder.Skus, orderSkuFinancial) + } + return afsOrder +} + func (c *PurchaseHandler) callbackAfsMsg2Status(msg *mtwmapi.CallbackMsg) (orderStatus *model.OrderStatus) { refundData := msg.Data.(*mtwmapi.CallbackRefundInfo) orderStatus = &model.OrderStatus{ @@ -153,6 +201,8 @@ func (c *PurchaseHandler) callbackAfsMsg2Status(msg *mtwmapi.CallbackMsg) (order } if refundData.RefundID > 0 { orderStatus.VendorOrderID = utils.Int64ToStr(refundData.RefundID) + } else { + orderStatus.VendorOrderID = orderStatus.RefVendorOrderID } return orderStatus } diff --git a/business/partner/purchase/mtwm/store_sku2.go b/business/partner/purchase/mtwm/store_sku2.go index da1cc6176..0196b7e78 100644 --- a/business/partner/purchase/mtwm/store_sku2.go +++ b/business/partner/purchase/mtwm/store_sku2.go @@ -25,6 +25,9 @@ const ( const ( defVendorCatID = 200001903 // 生菜 + + specialStoreID = "8171010" + // specialStoreID = "2523687" ) var ( @@ -197,12 +200,35 @@ func (p *PurchaseHandler) IsErrSkuNotExist(err error) (isNotExist bool) { return mtwmapi.IsErrSkuNotExist(err) } +// func duplicateStoreSkuList(storeSkuList []*dao.StoreSkuSyncInfo, index int) (newStoreSkuList []*dao.StoreSkuSyncInfo) { +// newStoreSkuList = make([]*dao.StoreSkuSyncInfo, len(storeSkuList)) +// for k, v := range storeSkuList { +// tmp := *v +// tmp.SkuName = fmt.Sprintf("%s.%d", tmp.SkuName, index) +// tmp.SkuID = index*1000000 + tmp.SkuID +// newStoreSkuList[k] = &tmp +// } +// return newStoreSkuList +// } + func (p *PurchaseHandler) UpdateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (successList []*dao.StoreSkuSyncInfo, err error) { - return p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, false) + successList, err = p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, false) + // if err == nil && vendorStoreID == specialStoreID { + // for i := 0; i < 2; i++ { + // p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, duplicateStoreSkuList(storeSkuList, i+1), true) + // } + // } + return successList, err } func (p *PurchaseHandler) CreateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (successList []*dao.StoreSkuSyncInfo, err error) { - return p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, true) + successList, err = p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, true) + // if err == nil && vendorStoreID == specialStoreID { + // for i := 0; i < 2; i++ { + // p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, duplicateStoreSkuList(storeSkuList, i+1), true) + // } + // } + return successList, err } // 对于多门店平台来说,storeSkuList中只有SkuID与VendorSkuID有意义 @@ -235,7 +261,15 @@ func (p *PurchaseHandler) createOrUpdateStoreSkus(ctx *jxcontext.Context, storeI foodData["category_name"] = storeSku.VendorCatID } foodData["is_sold_out"] = skuStatusJX2Mtwm(storeSku.MergedStatus) - foodData["picture"] = strings.Join(jxutils.BatchString2Slice(storeSku.Img, storeSku.Img2), ",") + if true { // vendorStoreID == specialStoreID { + img2 := storeSku.Img2 + if img2 == "" { + img2 = storeSku.Img + } + foodData["picture"] = strings.Join(jxutils.BatchString2Slice(storeSku.Img, img2, storeSku.Img, storeSku.Img, storeSku.Img), ",") + } else { + foodData["picture"] = strings.Join(jxutils.BatchString2Slice(storeSku.Img, storeSku.Img2), ",") + } if storeSku.DescImg != "" { foodData["picture_contents"] = storeSku.DescImg } diff --git a/controllers/auth2.go b/controllers/auth2.go index b769865ea..272fbaa3b 100644 --- a/controllers/auth2.go +++ b/controllers/auth2.go @@ -18,17 +18,19 @@ import ( ) func GetComposedCode(c *beego.Controller, code string) (composedCode string) { - composedCode = code - referer := c.Ctx.Request.Referer() - globals.SugarLogger.Debugf("GetComposedCode referer:%s", referer) - index := strings.Index(referer, "//") - if index > 0 { - list := strings.Split(referer[index+2:], "/") - if len(list) >= 2 { - composedCode = strings.Join([]string{ - list[1], - code, - }, ",") + if code != "" { + composedCode = code + referer := c.Ctx.Request.Referer() + globals.SugarLogger.Debugf("GetComposedCode referer:%s", referer) + index := strings.Index(referer, "//") + if index > 0 { + list := strings.Split(referer[index+2:], "/") + if len(list) >= 2 { + composedCode = strings.Join([]string{ + list[1], + code, + }, ",") + } } } return composedCode diff --git a/controllers/cms_user2.go b/controllers/cms_user2.go index 71cd2d452..301184d35 100644 --- a/controllers/cms_user2.go +++ b/controllers/cms_user2.go @@ -381,9 +381,9 @@ func (c *User2Controller) GetSelfInfo() { // @Title 根据小程序jsCode修改用户信息 // @Description 根据小程序jsCode修改用户信息 // @Param token header string true "认证token" -// @Param jsCode query string true "小程序jsCode" // @Param data query string true "加密数据" // @Param iv query string true "iv" +// @Param jsCode query string false "小程序jsCode" // @Success 200 {object} controllers.CallResult // @Failure 200 {object} controllers.CallResult // @router /UpdateUserByMiniInfo [put] diff --git a/controllers/yonghui.go b/controllers/yonghui.go index 99a397640..31dc0ce62 100644 --- a/controllers/yonghui.go +++ b/controllers/yonghui.go @@ -4,6 +4,7 @@ import ( "io" "git.rosy.net.cn/jx-callback/business/jxstore/yonghui" + "git.rosy.net.cn/jx-callback/business/jxutils" "github.com/astaxie/beego" ) @@ -14,7 +15,7 @@ type YongHuiController struct { // @Title 读取永辉excel文件 // @Description 读取永辉excel文件 -// @Param token header string false "认证token" +// @Param token header string true "认证token" // @Param isAsync query bool true "是否异步,缺省是同步" // @Param isContinueWhenError query bool true "单个同步失败是否继续,缺省false" // @Success 200 {object} controllers.CallResult @@ -42,3 +43,52 @@ func (c *YongHuiController) LoadExcelByYongHui() { }) } } + +// @Title 根据微盟商品更新京西价格 +// @Description 根据微盟商品更新京西价格 +// @Param token header string true "认证token" +// @Param storeIDs formData string true "门店ID列表" +// @Param isAsync formData bool true "是否异步,缺省是同步" +// @Param isContinueWhenError formData bool true "单个同步失败是否继续,缺省false" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /UpdateJxPriceByWeimob [post] +func (c *YongHuiController) UpdateJxPriceByWeimob() { + c.callUpdateJxPriceByWeimob(func(params *tYonghuiUpdateJxPriceByWeimobParams) (retVal interface{}, errCode string, err error) { + var storeIDList []int + if err = jxutils.Strings2Objs(params.StoreIDs, &storeIDList); err == nil { + retVal, err = yonghui.UpdateJxPriceByWeimob(params.Ctx, storeIDList, params.IsAsync, params.IsContinueWhenError) + } + return retVal, "", err + }) +} + +// @Title 查询微盟订单 +// @Description 查询微盟订单 +// @Param token header string true "认证token" +// @Param keyword query string false "查询关键字" +// @Param fromTime formData string false "订单起始时间 (yyyy-mm-dd 00:00:00)" +// @Param toTime formData string false "订单结束时间 (yyyy-mm-dd 00:00:00)" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetWeimobOrders [post] +func (c *YongHuiController) GetWeimobOrders() { + c.callGetWeimobOrders(func(params *tYonghuiGetWeimobOrdersParams) (retVal interface{}, errCode string, err error) { + retVal, err = yonghui.GetWeimobOrders(params.Ctx, params.FromTime, params.ToTime, params.MapData) + return retVal, "", err + }) +} + +// @Title 根据微盟订单号生成Excel +// @Description 根据微盟订单号生成Excel +// @Param token header string true "认证token" +// @Param orderNo formData string true "订单号" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetWeimobOrdersExcel [post] +func (c *YongHuiController) GetWeimobOrdersExcel() { + c.callGetWeimobOrdersExcel(func(params *tYonghuiGetWeimobOrdersExcelParams) (retVal interface{}, errCode string, err error) { + err = yonghui.GetWeimobOrdersExcel(params.Ctx, params.OrderNo) + return retVal, "", err + }) +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 175ed53e9..a36fc41ca 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -2016,6 +2016,24 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"], + beego.ControllerComments{ + Method: "GetWeimobOrders", + Router: `/GetWeimobOrders`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"], + beego.ControllerComments{ + Method: "GetWeimobOrdersExcel", + Router: `/GetWeimobOrdersExcel`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"], beego.ControllerComments{ Method: "LoadExcelByYongHui", @@ -2025,4 +2043,13 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:YongHuiController"], + beego.ControllerComments{ + Method: "UpdateJxPriceByWeimob", + Router: `/UpdateJxPriceByWeimob`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + }