@@ -3,6 +3,8 @@ package defsch
import (
"time"
"math/rand"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
@@ -13,12 +15,16 @@ import (
)
const (
defTime2Delivery = 1 * time . Hour
defTime2Delivered = 1 * time . Hour // 正常订单都是1小时达
defTime2Schedule3rdCarrier = 330 * time . Second // 京东要求5分钟后才能转自送, 保险起见, 设置为5分半钟
time2Schedule3rdCarrierGap4OrderStatus = 3 * time . Minute // 京东要求是运单状态为待抢单且超时5分钟, 但为了防止没有运单事件, 所以就拣货完成事件开始算, 添加3分钟
defTime2AutoPickupMin = 25 * time . Minute
time2AutoPickupGap = 5 * time . Minute
)
type WatchOrderInfo struct {
order * model . GoodsOrder
waybills [ ] * model . Waybill
order * model . GoodsOrder // order里的信息是保持更新的
waybills [ ] * model . Waybill // 这个waybills里的状态信息是不真实的, 只使用id相关的信息
timer * time . Timer
}
@@ -33,7 +39,6 @@ func init() {
sch := & DefScheduler { }
sch . Init ( )
scheduler . CurrentScheduler = sch
sch . defWorkflowConfig = map [ int ] * scheduler . StatusActionConfig {
model . OrderStatusNew : & scheduler . StatusActionConfig { // 自动接单
Timeout : 1 * time . Second ,
@@ -44,16 +49,16 @@ func init() {
return nil
} ,
} ,
model . OrderStatusAccepted : & scheduler . StatusActionConfig { // 20分钟后 自动拣货
Timeout : 20 * time . Minute ,
model . OrderStatusAccepted : & scheduler . StatusActionConfig { // 自动拣货
Timeout : defTime2AutoPickupMin ,
TimeoutAction : func ( order * model . GoodsOrder ) ( err error ) {
return sch . GetPurchasePlatformFromVendorID ( order . VendorID ) . PickedUpGoods ( order )
} ,
} ,
model . OrderStatusFinishedPickup : & scheduler . StatusActionConfig { // 拣货完成8分钟后开始在外部快递平台创建运单
Timeout : 8 * time . Minute ,
model . OrderStatusFinishedPickup : & scheduler . StatusActionConfig { // 尝试召唤更多物流
Timeout : defTime2Schedule3rdCarrier ,
TimeoutAction : func ( order * model . GoodsOrder ) ( err error ) {
return sch . createWaybillOn3rdProviders ( order )
return sch . createWaybillOn3rdProviders ( order , nil )
} ,
} ,
}
@@ -61,40 +66,28 @@ func init() {
// 以下是订单
func ( s * DefScheduler ) OnOrderNew ( order * model . GoodsOrder ) ( err error ) {
config := s . mergeOrderStatusConfig ( order . Status , s . GetPurchasePlatformFromVendorID ( order . VendorID ) . GetStatusActionConfig ( order . Status ) )
watchInfo := & WatchOrderInfo {
order : order ,
timer : time . AfterFunc ( jxutils . GetRealTimeout ( order . OrderCreatedAt , config . Timeout ) , func ( ) {
config . TimeoutAction ( order )
} ) ,
}
s . resetTimer ( model . OrderStatusNew , watchInfo , 0 )
s . orderMap . Store ( jxutils . ComposeUniversalOrderID ( order . VendorOrderID , order . VendorID ) , watchInfo )
return err
}
func ( s * DefScheduler ) OnOrderStatusChanged ( status * model . OrderStatus ) ( err error ) {
if savedOrderInfo := s . loadWatchOrderFromMap ( status . VendorOrderID , status . VendorID ) ; savedOrderInfo != nil {
if savedOrderInfo . timer != nil {
savedOrderInfo . tim er. Stop ( )
}
if status . Status > model . OrderStatusUnknown && status . Status < model . OrderStatusEndBegin {
s . updateOrderByStatus ( savedOrderInfo . order , status )
config := s . mergeOrderStatusConfig ( status . Status , s . GetPurchasePlatformFromVendorID ( status . VendorID ) . GetStatusActionConfig ( status . Status ) )
if config ! = nil && config . TimeoutAction != nil {
var timeout time . Duration
if status . Status == model . OrderStatusAccepted {
savedOrderInfo := s . loadWatchOrderFromMap ( status . VendorOrderID , status . VendorID )
timeout = s . getLatestPickupTimeout ( savedOrderInfo . order , config . Timeout )
} else {
timeout = jxutils . GetRealTimeout ( savedOrderInfo . order . StatusTime , config . Timeout )
}
savedOrderInfo . timer = time . AfterFunc ( timeout , func ( ) {
config . TimeoutAction ( savedOrderInfo . order )
} )
}
} else {
s . orderMap . Delete ( jxutils . GetUniversalOrderIDFromOrderStatus ( status ) )
savedOrderInfo := s . loadWatchOrderFromMap ( status . VendorOrderID , status . VendorID )
if status . Status > model . OrderStatusUnknown && status . Status < model . OrderStatusEndBegin {
s . updateOrderByStatus ( savedOrderInfo . ord er, status )
gap := 0 * time . Second
if status . Status == model . OrderStatusAccepted {
gap = time . Duration ( rand . Int63n ( int64 ( time2AutoPickupGap ) ) )
} else if status . Status == model . OrderStatusFinishedPickup {
gap = time2Schedule3rdCarrierGap4OrderStatus
}
s . resetTimer ( status . Status , savedOrderInfo , gap )
} else {
savedOrderInfo . timer . Stop ( )
s . orderMap . Delete ( jxutils . GetUniversalOrderIDFromOrderStatus ( status ) )
}
return err
}
@@ -103,7 +96,13 @@ func (s *DefScheduler) OnOrderStatusChanged(status *model.OrderStatus) (err erro
func ( s * DefScheduler ) OnWaybillStatusChanged ( bill * model . Waybill ) ( err error ) {
savedOrderInfo := s . loadWatchOrderFromMap ( bill . VendorOrderID , bill . OrderVendorID )
if bill . Status == model . WaybillStatusNew {
err = s . addWaybill2Map ( bill )
if savedOrderInfo . order . WaybillVendorID == model . VendorIDUnknown {
s . resetTimer ( model . OrderStatusFinishedPickup , savedOrderInfo , 0 )
err = s . addWaybill2Map ( bill )
} else {
globals . SugarLogger . Infof ( "OnWaybillStatusChanged multiple waybill created, bill:%v" , bill )
s . GetDeliveryPlatformFromVendorID ( bill . WaybillVendorID ) . CancelWaybill ( bill )
}
} else {
findIt := false
for _ , v := range savedOrderInfo . waybills {
@@ -115,23 +114,35 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
if findIt {
switch bill . Status {
case model . WaybillStatusAccepted :
savedOrderInfo . timer . Stop ( ) // todo 这里应该另外启动一个TIMER
s . cancelOtherWaybills ( bill )
if bill . WaybillVendorID != bill . OrderVendorID {
s . swtich2SelfDeliverWithRetry ( bill , 2 , 10 * time . Second )
}
s . CurOrderManager . UpdateWaybillVendorID ( bill )
savedOrderInfo . order . WaybillVendorID = bill . WaybillVendorID
case model . WaybillStatusAcceptCanceled , model . WaybillStatusCanceled , model . WaybillStatusFailed :
s . remov eWaybillFromMap ( bill , savedOrderInfo )
case model . WaybillStatusAcceptCanceled :
s . c reat eWaybillOn3rdProviders ( savedOrderInfo . order , bill )
if savedOrderInfo . order . WaybillVendorID == bill . WaybillVendorID {
s . createWaybillOn3rdProviders ( savedOrderInfo . order )
bill . WaybillVendorID = model . VendorIDUnknown
s . CurOrderManager . UpdateWaybillVendorID ( bill )
savedOrderInfo . order . WaybillVendorID = bill . WaybillVendorID
}
case model . WaybillStatusCanceled , model . WaybillStatusFailed :
s . removeWaybillFromMap ( bill , savedOrderInfo )
if savedOrderInfo . order . WaybillVendorID == bill . WaybillVendorID {
s . createWaybillOn3rdProviders ( savedOrderInfo . order , nil )
bill . WaybillVendorID = model . VendorIDUnknown
s . CurOrderManager . UpdateWaybillVendorID ( bill )
savedOrderInfo . order . WaybillVendorID = bill . WaybillVendorID
}
case model . WaybillStatusDelivering :
s . GetPurchasePlatformFromVendorID ( bill . O rderVendorID) . SelfDeliverDelievering ( savedOrderInfo . order )
if savedOrderInfo . o rder. VendorID != bill . WaybillVendorID {
s . GetPurchasePlatformFromVendorID ( bill . OrderVendorID ) . SelfDeliverDelievering ( savedOrderInfo . order )
}
case model . WaybillStatusDelivered :
s . GetPurchasePlatformFromVendorID ( bill . O rderVendorID) . SelfDeliverDelievered ( savedOrderInfo . order )
if savedOrderInfo . o rder. VendorID != bill . WaybillVendorID {
s . GetPurchasePlatformFromVendorID ( bill . OrderVendorID ) . SelfDeliverDelievered ( savedOrderInfo . order )
}
s . removeWaybillFromMap ( bill , savedOrderInfo )
}
} else {
@@ -154,20 +165,13 @@ func (s *DefScheduler) addWaybill2Map(bill *model.Waybill) (err error) {
return nil
}
func ( s * DefScheduler ) on WaybillFailed ( b ill * model . Waybill ) ( err error ) {
savedOrderInfo := s . loadWatchOrderFromMap ( bill . VendorOrderID , bill . OrderVendorID )
s . removeWaybillFromMap ( bill , savedOrderInfo )
if len ( savedOrderInfo . waybills ) == 0 {
s . createWaybillOn3rdProviders ( savedOrderInfo . order )
}
return nil
}
func ( s * DefScheduler ) createWaybillOn3rdProviders ( order * model . GoodsOrder ) ( err error ) {
func ( s * DefScheduler ) create WaybillOn3rdProviders ( order * model . GoodsOrder , excludeB ill * model . Waybill ) ( err error ) {
successCount := 0
for _ , v := range s . DeliveryPlatformHandlers {
if err = v . CreateWaybill ( order ) ; err = = nil {
successCount ++
for k , v := range s . DeliveryPlatformHandlers {
if excludeBill = = nil || k ! = excludeBill . WaybillVendorID {
if err = v . CreateWaybill ( order ) ; err == nil {
successCount ++
}
}
}
if successCount != 0 {
@@ -179,26 +183,27 @@ func (s *DefScheduler) createWaybillOn3rdProviders(order *model.GoodsOrder) (err
func ( s * DefScheduler ) cancelOtherWaybills ( bill * model . Waybill ) ( err error ) {
savedOrderInfo := s . loadWatchOrderFromMap ( bill . VendorOrderID , bill . OrderVendorID )
for _ , v := range savedOrderInfo . waybills {
if ! ( v . WaybillVendorID == bill . WaybillVendorID && v . VendorWaybillID == bill . VendorWaybillID ) {
if v . WaybillVendorID != bill . OrderVendorID && ! ( v . WaybillVendorID == bill . WaybillVendorID && v . VendorWaybillID == bill . VendorWaybillID ) {
_ = s . GetDeliveryPlatformFromVendorID ( v . WaybillVendorID ) . CancelWaybill ( v )
}
}
if bill . WaybillVendorID != bill . OrderVendorID {
s . swtich2SelfDeliverWithRetry ( bill , 2 , 10 * time . Second )
}
return nil
}
func ( s * DefScheduler ) swtich2SelfDeliverWithRetry ( bill * model . Waybill , retryCount int , duration time . Duration ) {
if err := s . GetPurchasePlatformFromVendorID ( bill . OrderVendorID ) . Swtich2SelfDeliver ( bill . VendorOrderID ) ; err != nil {
if retryCount >= 0 {
time . AfterFunc ( duration , func ( ) {
s . swtich2SelfDeliverWithRetry ( bill , retryCount - 1 , duration )
} )
} else {
utils . CallFuncRetryAsync ( func ( index int ) error {
err := s . GetPurchasePlatformFromVendorID ( bill . OrderVendorID ) . Swtich2SelfDeliver ( bill . VendorOrderID )
if err == nil {
s . removeWaybillFromMap ( bill , nil ) // todo 是否在这里删除运单,还是在运单事件里处理更好些?
} else if index == 0 {
// 如果购买平台转商家自送失败, 最终还是要取消3方物流
s . GetDeliveryPlatformFromVendorID ( bill . WaybillVendorID ) . CancelWaybill ( bill )
}
} else {
s . removeWaybillFromMap ( bill , nil )
}
return err
} , duration , retryCount )
}
func ( s * DefScheduler ) loadWatchOrderFromMap ( vendorOrderID string , vendorID int ) * WatchOrderInfo {
@@ -235,11 +240,29 @@ func (s *DefScheduler) removeWaybillFromMap(bill *model.Waybill, savedOrderInfo
func ( s * DefScheduler ) getLatestPickupTimeout ( order * model . GoodsOrder , configTimeout time . Duration ) ( retVal time . Duration ) {
beginTime := order . StatusTime
if order . ExpectedDeliveredTime != utils . DefaultTimeValue {
beginTime = order . ExpectedDeliveredTime . Add ( - defTime2Delivery )
beginTime = order . ExpectedDeliveredTime . Add ( - defTime2Delivered )
}
return jxutils . GetRealTimeout ( beginTime , configTimeout )
}
func ( s * DefScheduler ) resetTimer ( status int , savedOrderInfo * WatchOrderInfo , gap time . Duration ) {
if savedOrderInfo . timer != nil {
savedOrderInfo . timer . Stop ( )
}
config := s . mergeOrderStatusConfig ( status , s . GetPurchasePlatformFromVendorID ( savedOrderInfo . order . VendorID ) . GetStatusActionConfig ( status ) )
if config != nil && config . TimeoutAction != nil {
var timeout time . Duration
if status == model . OrderStatusAccepted {
timeout = s . getLatestPickupTimeout ( savedOrderInfo . order , config . Timeout )
} else {
timeout = jxutils . GetRealTimeout ( savedOrderInfo . order . StatusTime , config . Timeout )
}
savedOrderInfo . timer = time . AfterFunc ( timeout + gap , func ( ) {
config . TimeoutAction ( savedOrderInfo . order )
} )
}
}
func ( s * DefScheduler ) handleAutoAcceptOrder ( orderID string , vendorID int , userMobile string , jxStoreID int , db orm . Ormer , handler func ( accepted bool ) error ) int {
handleType := 0
if userMobile != "" {