From 65eeef9966227ee1f183b77fa951a0b3f23550e4 Mon Sep 17 00:00:00 2001 From: gazebo Date: Fri, 20 Jul 2018 23:22:31 +0800 Subject: [PATCH] - fix weixin push msg bug. - handle jd out-of-order msg. --- business/controller/order.go | 18 +----- business/controller/waybill.go | 18 +----- business/jxutils/weixinmsg/weixinmsg.go | 20 +++++-- business/model/const.go | 4 +- business/model/model.go | 29 ++++++++++ business/scheduler/defsch/defsch.go | 73 ++++++++++++++++--------- 6 files changed, 97 insertions(+), 65 deletions(-) create mode 100644 business/model/model.go diff --git a/business/controller/order.go b/business/controller/order.go index a0fca1d47..99c810f28 100644 --- a/business/controller/order.go +++ b/business/controller/order.go @@ -24,7 +24,7 @@ func NewOrderManager() *OrderController { func (c *OrderController) OnOrderNew(order *model.GoodsOrder) (err error) { db := orm.NewOrm() order.StatusTime = order.OrderCreatedAt - isDuplicated, err := addOrderOrWaybillStatus(c.order2Status(order), db) + isDuplicated, err := addOrderOrWaybillStatus(model.Order2Status(order), db) if err == nil && !isDuplicated { if err = c.saveOrder(order, false, db); err == nil { err = scheduler.CurrentScheduler.OnOrderNew(order) @@ -37,7 +37,7 @@ func (c *OrderController) OnOrderNew(order *model.GoodsOrder) (err error) { func (c *OrderController) OnOrderAdjust(order *model.GoodsOrder) (err error) { db := orm.NewOrm() order.StatusTime = order.OrderCreatedAt - status := c.order2Status(order) + status := model.Order2Status(order) isDuplicated, err := addOrderOrWaybillStatus(status, db) if err == nil && !isDuplicated { err = utils.CallFuncLogError(func() error { @@ -207,20 +207,6 @@ func (c *OrderController) addOrderStatus(orderStatus *model.OrderStatus, db orm. return isDuplicated, err } -func (c *OrderController) order2Status(order *model.GoodsOrder) (retVal *model.OrderStatus) { - retVal = &model.OrderStatus{ - VendorOrderID: order.VendorOrderID, - VendorID: order.VendorID, - OrderType: model.OrderTypeOrder, - RefVendorOrderID: order.VendorOrderID, - RefVendorID: order.VendorID, - Status: order.Status, - VendorStatus: order.VendorStatus, - StatusTime: order.StatusTime, - } - return retVal -} - func (c *OrderController) LoadOrder(vendorOrderID string, vendorID int) (order *model.GoodsOrder, err error) { db := orm.NewOrm() order = &model.GoodsOrder{ diff --git a/business/controller/waybill.go b/business/controller/waybill.go index 86833be14..1d791f899 100644 --- a/business/controller/waybill.go +++ b/business/controller/waybill.go @@ -19,7 +19,7 @@ func NewWaybillManager() *WaybillController { func (w *WaybillController) onWaybillNew(bill *model.Waybill) (err error) { db := orm.NewOrm() - isDuplicated, err := addOrderOrWaybillStatus(w.waybill2Status(bill), db) + isDuplicated, err := addOrderOrWaybillStatus(model.Waybill2Status(bill), db) if !isDuplicated { bill.WaybillFinishedAt = utils.DefaultTimeValue bill.ID = 0 @@ -89,7 +89,7 @@ func (w *WaybillController) addWaybillStatus(bill *model.Waybill, db orm.Ormer) if db == nil { db = orm.NewOrm() } - waybillStatus := w.waybill2Status(bill) + waybillStatus := model.Waybill2Status(bill) isDuplicated, err = addOrderOrWaybillStatus(waybillStatus, db) if !isDuplicated && waybillStatus.Status > model.WaybillStatusNew { params := orm.Params{ @@ -106,17 +106,3 @@ func (w *WaybillController) addWaybillStatus(bill *model.Waybill, db orm.Ormer) } return isDuplicated, err } - -func (w *WaybillController) waybill2Status(bill *model.Waybill) (retVal *model.OrderStatus) { - retVal = &model.OrderStatus{ - VendorOrderID: bill.VendorWaybillID, - VendorID: bill.WaybillVendorID, - OrderType: model.OrderTypeWaybill, - RefVendorOrderID: bill.VendorOrderID, - RefVendorID: bill.OrderVendorID, - Status: bill.Status, - VendorStatus: bill.VendorStatus, - StatusTime: bill.WaybillCreatedAt, - } - return retVal -} diff --git a/business/jxutils/weixinmsg/weixinmsg.go b/business/jxutils/weixinmsg/weixinmsg.go index be9be5575..bb3da7e51 100644 --- a/business/jxutils/weixinmsg/weixinmsg.go +++ b/business/jxutils/weixinmsg/weixinmsg.go @@ -35,6 +35,14 @@ const ( WX_DADA_DELIVERY_DONE_TEMPLATE_ID = "YXdCrQAHZlcZX1htYUiarrLmtkmKAjp7rynjwObgODo" //微信达达众包配送员配送完成推送 ) +var ( + venderColors = map[int]string{ + model.VendorIDJD: WX_TEMPLATE_VENDERCOLOR_JDDJ, + model.VendorIDMTWM: WX_TEMPLATE_VENDERCOLOR_MT, + model.VendorIDELM: WX_TEMPLATE_VENDERCOLOR_ELM, + } +) + func GetWeixinOpenIDsFromStoreID(storeID int) (retVal []string) { db := orm.NewOrm() var lists []orm.ParamsList @@ -60,7 +68,7 @@ func GetWeixinOpenIDsFromStoreID(storeID int) (retVal []string) { } // todo,调试,只发给我 globals.SugarLogger.Debugf("GetWeixinOpenIDsFromStoreID store:%d, openids:%v", storeID, retVal) - if storeID%3 == 0 { //} storeID == 100146 { + if storeID == 100146 { retVal = []string{"oYN_ust9hXKEvEv0X6Mq6nlAWs_E"} } else { retVal = []string{} @@ -86,7 +94,7 @@ func SendMsgToStore(storeID int, templateID, downloadURL string, data interface{ func NotifyNewOrder(order *model.GoodsOrder) (err error) { sb := new(strings.Builder) - sb.WriteString("老板") + sb.WriteString("老板,") sb.WriteString(order.ConsigneeName) sb.WriteString("购买了商品") sb.WriteString(order.Skus[0].SkuName) @@ -102,7 +110,7 @@ func NotifyNewOrder(order *model.GoodsOrder) (err error) { "value": sb.String(), "color": WX_NEW_ORDER_TEMPLATE_COLOR, }, - "day": map[string]interface{}{ + "Day": map[string]interface{}{ "value": FormatDeliveryTime(order), "color": WX_NEW_ORDER_TEMPLATE_COLOR, }, @@ -111,8 +119,8 @@ func NotifyNewOrder(order *model.GoodsOrder) (err error) { "color": WX_NEW_ORDER_TEMPLATE_COLOR, }, "orderType": map[string]interface{}{ - "value": fmt.Sprintf("%s第%d号订单", model.VendorChineseNames[order.VendorID], order.OrderSeq), - "color": WX_NEW_ORDER_TEMPLATE_COLOR, + "value": fmt.Sprintf("%s 第%d号订单", model.VendorChineseNames[order.VendorID], order.OrderSeq), + "color": venderColors[order.VendorID], }, "customerName": map[string]interface{}{ "value": order.ConsigneeName, @@ -186,5 +194,5 @@ func FormatDeliveryTime(order *model.GoodsOrder) string { left := tmpTime.Sub(time.Now()) / time.Minute leftHours := left / 60 leftMinutes := left % 60 - return fmt.Sprintf("请于%s前送达(剩余时间%d小时%d分钟", utils.Time2Str(tmpTime), leftHours, leftMinutes) + return fmt.Sprintf("请于%s前送达(剩余时间%d小时%d分钟)", utils.Time2Str(tmpTime), leftHours, leftMinutes) } diff --git a/business/model/const.go b/business/model/const.go index 4c20b65d5..0db6cb6d5 100644 --- a/business/model/const.go +++ b/business/model/const.go @@ -24,8 +24,8 @@ var ( VendorIDMTPS: "MTPS", } VendorChineseNames = map[int]string{ - VendorIDJD: "京东", - VendorIDMTWM: "美团", + VendorIDJD: "京东到家", + VendorIDMTWM: "美团外卖", VendorIDELM: "饿了么", VendorIDDada: "达达众包", VendorIDMTPS: "美团配送", diff --git a/business/model/model.go b/business/model/model.go new file mode 100644 index 000000000..971b434ff --- /dev/null +++ b/business/model/model.go @@ -0,0 +1,29 @@ +package model + +func Order2Status(order *GoodsOrder) (retVal *OrderStatus) { + retVal = &OrderStatus{ + VendorOrderID: order.VendorOrderID, + VendorID: order.VendorID, + OrderType: OrderTypeOrder, + RefVendorOrderID: order.VendorOrderID, + RefVendorID: order.VendorID, + Status: order.Status, + VendorStatus: order.VendorStatus, + StatusTime: order.StatusTime, + } + return retVal +} + +func Waybill2Status(bill *Waybill) (retVal *OrderStatus) { + retVal = &OrderStatus{ + VendorOrderID: bill.VendorWaybillID, + VendorID: bill.WaybillVendorID, + OrderType: OrderTypeWaybill, + RefVendorOrderID: bill.VendorOrderID, + RefVendorID: bill.OrderVendorID, + Status: bill.Status, + VendorStatus: bill.VendorStatus, + StatusTime: bill.WaybillCreatedAt, + } + return retVal +} diff --git a/business/scheduler/defsch/defsch.go b/business/scheduler/defsch/defsch.go index 7454b0a39..f70fd4960 100644 --- a/business/scheduler/defsch/defsch.go +++ b/business/scheduler/defsch/defsch.go @@ -24,6 +24,7 @@ const ( type WatchOrderInfo struct { order *model.GoodsOrder // order里的信息是保持更新的 + dirty int // 因为京东事件序列New与Accepted有极少数情况下会错序,处理延迟加载 waybills []*model.Waybill // 这个waybills里的状态信息是不真实的,只使用id相关的信息 timerStatus int timer *time.Timer @@ -79,7 +80,7 @@ func (s *DefScheduler) OnOrderNew(order *model.GoodsOrder) (err error) { func (s *DefScheduler) OnOrderStatusChanged(status *model.OrderStatus) (err error) { if status.Status > model.OrderStatusUnknown { globals.SugarLogger.Debugf("OnOrderStatusChanged, status:%v", status) - if savedOrderInfo := s.loadWatchOrderFromMap(status.VendorOrderID, status.VendorID); savedOrderInfo != nil { + if savedOrderInfo := s.loadSavedOrderFromMap(status); savedOrderInfo != nil { s.updateOrderByStatus(savedOrderInfo.order, status) if status.Status > model.OrderStatusUnknown && status.Status < model.OrderStatusEndBegin { if !(status.Status == model.OrderStatusFinishedPickup && len(savedOrderInfo.waybills) > 0) { //饿了么还观察到运单消息早于拣货完成消息 @@ -99,6 +100,7 @@ func (s *DefScheduler) OnOrderStatusChanged(status *model.OrderStatus) (err erro } } else { s.stopTimer(savedOrderInfo) + s.cancelOtherWaybills(savedOrderInfo, nil) s.orderMap.Delete(jxutils.GetUniversalOrderIDFromOrderStatus(status)) } } else { @@ -112,7 +114,7 @@ func (s *DefScheduler) OnOrderStatusChanged(status *model.OrderStatus) (err erro func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill) (err error) { if bill.Status > model.WaybillStatusUnknown { globals.SugarLogger.Debugf("OnWaybillStatusChanged, bill:%v", bill) - if savedOrderInfo := s.loadWatchOrderFromMap(bill.VendorOrderID, bill.OrderVendorID); savedOrderInfo != nil { + if savedOrderInfo := s.loadSavedOrderFromMap(model.Waybill2Status(bill)); savedOrderInfo != nil { s.addWaybill2Map(savedOrderInfo, bill) // 这样写的原因是因为调试时,程度从中途运行,没有接受到WaybillStatusNew事件 if bill.Status == model.WaybillStatusNew { if bill.OrderVendorID == bill.WaybillVendorID { @@ -144,7 +146,10 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill) (err error) { s.CurOrderManager.UpdateWaybillVendorID(bill) savedOrderInfo.order.WaybillVendorID = bill.WaybillVendorID } - case model.WaybillStatusCanceled, model.WaybillStatusFailed: + case model.WaybillStatusFailed: // WaybillStatusFailed理解成订单整个失败了,不需要再尝试创建运单了 + s.removeWaybillFromMap(savedOrderInfo, bill) + globals.SugarLogger.Infof("OnWaybillStatusChanged WaybillStatusFailed, bill:%v", bill) + case model.WaybillStatusCanceled: s.removeWaybillFromMap(savedOrderInfo, bill) if savedOrderInfo.order.WaybillVendorID == bill.WaybillVendorID { s.createWaybillOn3rdProviders(savedOrderInfo.order, nil) @@ -158,10 +163,10 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill) (err error) { s.GetPurchasePlatformFromVendorID(bill.OrderVendorID).SelfDeliverDelievering(savedOrderInfo.order) } case model.WaybillStatusDelivered: + s.removeWaybillFromMap(savedOrderInfo, bill) if savedOrderInfo.order.VendorID != bill.WaybillVendorID { s.GetPurchasePlatformFromVendorID(bill.OrderVendorID).SelfDeliverDelievered(savedOrderInfo.order) } - s.removeWaybillFromMap(savedOrderInfo, bill) } } } else { @@ -210,11 +215,11 @@ func (s *DefScheduler) createWaybillOn3rdProviders(order *model.GoodsOrder, excl func (s *DefScheduler) cancelOtherWaybills(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) { globals.SugarLogger.Debugf("cancelOtherWaybills, order:%v, bill:%v", savedOrderInfo.order, bill) for _, v := range savedOrderInfo.waybills { - if v.WaybillVendorID != bill.OrderVendorID && !(v.WaybillVendorID == bill.WaybillVendorID && v.VendorWaybillID == bill.VendorWaybillID) { + if bill == nil || v.WaybillVendorID != bill.OrderVendorID && !(v.WaybillVendorID == bill.WaybillVendorID && v.VendorWaybillID == bill.VendorWaybillID) { _ = s.GetDeliveryPlatformFromVendorID(v.WaybillVendorID).CancelWaybill(v) } } - if bill.WaybillVendorID != bill.OrderVendorID { + if bill != nil && bill.WaybillVendorID != bill.OrderVendorID { s.swtich2SelfDeliverWithRetry(bill, 2, 10*time.Second) } return nil @@ -231,20 +236,34 @@ func (s *DefScheduler) swtich2SelfDeliverWithRetry(bill *model.Waybill, retryCou }, duration, retryCount) } -func (s *DefScheduler) loadWatchOrderFromMap(vendorOrderID string, vendorID int) *WatchOrderInfo { - universalOrderID := jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID) +// 这个函数这样写的原因是适应一些消息错序 +func (s *DefScheduler) loadSavedOrderFromMap(status *model.OrderStatus) *WatchOrderInfo { + universalOrderID := jxutils.ComposeUniversalOrderID(status.VendorOrderID, status.VendorID) var realSavedInfo *WatchOrderInfo if savedInfo, ok := s.orderMap.Load(universalOrderID); ok { realSavedInfo = savedInfo.(*WatchOrderInfo) - } else { - globals.SugarLogger.Infof("can not get saved order, vendorOrderID:%s, vendorID:%d, load it", vendorOrderID, vendorID) - if order, err := s.CurOrderManager.LoadOrder(vendorOrderID, vendorID); err == nil { - realSavedInfo = &WatchOrderInfo{ - order: order, - } + } + if realSavedInfo == nil || realSavedInfo.dirty == 1 { + if realSavedInfo == nil { + realSavedInfo = new(WatchOrderInfo) s.orderMap.Store(universalOrderID, realSavedInfo) } else { - globals.SugarLogger.Errorf("can not load order vendorOrderID:%s, vendorID:%d", vendorOrderID, vendorID) + realSavedInfo.dirty = 0 + globals.SugarLogger.Infof("order is dirty, vendorOrderID:%s, vendorID:%d, load it", status.VendorOrderID, status.VendorID) + } + if order, err := s.CurOrderManager.LoadOrder(status.VendorOrderID, status.VendorID); err == nil { + realSavedInfo.order = order + } else { + realSavedInfo.order = &model.GoodsOrder{ + VendorOrderID: status.VendorOrderID, + VendorID: status.VendorID, + Status: status.Status, + StatusTime: status.StatusTime, + OrderCreatedAt: status.StatusTime, + WaybillVendorID: model.VendorIDUnknown, + } + realSavedInfo.dirty = 1 + globals.SugarLogger.Infof("can not load order vendorOrderID:%s, vendorID:%d", status.VendorOrderID, status.VendorID) } } return realSavedInfo @@ -266,16 +285,20 @@ func (s *DefScheduler) stopTimer(savedOrderInfo *WatchOrderInfo) { func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, status int, beginTime time.Time, gap time.Duration) { globals.SugarLogger.Debugf("resetTimer status:%v, orderid:%v", status, savedOrderInfo.order.VendorOrderID) - s.stopTimer(savedOrderInfo) - config := s.mergeOrderStatusConfig(status, s.GetPurchasePlatformFromVendorID(savedOrderInfo.order.VendorID).GetStatusActionConfig(status)) - if config != nil && config.TimeoutAction != nil { - timeout := jxutils.GetRealTimeout(beginTime, config.Timeout) + gap - globals.SugarLogger.Debugf("resetTimer timeout:%v, orderid:%v", timeout, savedOrderInfo.order.VendorOrderID) - savedOrderInfo.timerStatus = status - savedOrderInfo.timer = time.AfterFunc(timeout, func() { - config.TimeoutAction(savedOrderInfo.order) - savedOrderInfo.timerStatus = 0 - }) + if status >= savedOrderInfo.timerStatus { // 新设置的TIMER不能覆盖状态在其后的TIMER + s.stopTimer(savedOrderInfo) + config := s.mergeOrderStatusConfig(status, s.GetPurchasePlatformFromVendorID(savedOrderInfo.order.VendorID).GetStatusActionConfig(status)) + if config != nil && config.TimeoutAction != nil { + timeout := jxutils.GetRealTimeout(beginTime, config.Timeout) + gap + globals.SugarLogger.Debugf("resetTimer timeout:%v, orderid:%v", timeout, savedOrderInfo.order.VendorOrderID) + savedOrderInfo.timerStatus = status + savedOrderInfo.timer = time.AfterFunc(timeout, func() { + config.TimeoutAction(savedOrderInfo.order) + savedOrderInfo.timerStatus = 0 // todo 可能有线程安全问题,考虑加入订单队列 + }) + } + } else { + globals.SugarLogger.Infof("resetTimer status revert, orderid:%s, current timer status:%d, status:%d", savedOrderInfo.order.VendorOrderID, savedOrderInfo.timerStatus, status) } }