From 643bcac0f8bcca403d99a445ba2b9b557b94b83f Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 21 Aug 2018 17:18:08 +0800 Subject: [PATCH] - order manager added. --- business/jxcallback/orderman/order.go | 2 +- business/jxcallback/orderman/orderman.go | 10 +- business/jxcallback/orderman/orderman_ext.go | 53 ++++++ .../jxcallback/scheduler/basesch/basesch.go | 52 +++--- .../scheduler/basesch/basesch_ext.go | 43 +++++ .../jxcallback/scheduler/defsch/defsch.go | 30 +++- business/jxcallback/scheduler/scheduler.go | 1 + business/jxutils/jxutils.go | 12 ++ business/model/api.go | 19 +++ business/model/model.go | 5 - business/model/order.go | 158 +++++++++--------- business/partner/delivery/dada/waybill.go | 21 ++- .../partner/delivery/dada/waybill_test.go | 3 +- business/partner/delivery/mtps/waybill.go | 40 ++--- .../partner/delivery/mtps/waybill_test.go | 3 +- business/partner/partner.go | 2 +- controllers/jx_order.go | 140 ++++++++++++++-- routers/commentsRouter_controllers.go | 34 +++- 18 files changed, 463 insertions(+), 165 deletions(-) create mode 100644 business/jxcallback/orderman/orderman_ext.go create mode 100644 business/jxcallback/scheduler/basesch/basesch_ext.go create mode 100644 business/model/api.go diff --git a/business/jxcallback/orderman/order.go b/business/jxcallback/orderman/order.go index cb3cfd036..0f5e51f1a 100644 --- a/business/jxcallback/orderman/order.go +++ b/business/jxcallback/orderman/order.go @@ -342,7 +342,7 @@ func (c *OrderManager) LoadOrder(vendorOrderID string, vendorID int) (order *mod func (c *OrderManager) UpdateOrderStatusDirectly(order *model.GoodsOrder) (err error) { db := orm.NewOrm() utils.CallFuncLogError(func() error { - _, err = db.Update(db, "Status") + _, err = db.Update(order, "Status") return err }, "UpdateOrderStatusDirectly orderID:%s failed with error:%v", order.VendorOrderID, err) return err diff --git a/business/jxcallback/orderman/orderman.go b/business/jxcallback/orderman/orderman.go index 9f09f2185..e07ed3879 100644 --- a/business/jxcallback/orderman/orderman.go +++ b/business/jxcallback/orderman/orderman.go @@ -21,7 +21,7 @@ const ( ) var ( - curOrderManager *OrderManager + FixedOrderManager *OrderManager ) // 所有公共接口调用前,要求在order里或status中设置合适的Status @@ -53,8 +53,8 @@ func (s StatusTimerSlice) Swap(i, j int) { } func init() { - curOrderManager = NewOrderManager() - partner.Init(curOrderManager) + FixedOrderManager = NewOrderManager() + partner.Init(FixedOrderManager) } func addOrderOrWaybillStatus(status *model.OrderStatus, db orm.Ormer) (isDuplicated bool, err error) { @@ -86,12 +86,12 @@ func addOrderOrWaybillStatus(status *model.OrderStatus, db orm.Ormer) (isDuplica // todo 最好还是改成全事件回放算了 func LoadPendingOrders() { - orders := curOrderManager.LoadPendingOrders() + orders := FixedOrderManager.LoadPendingOrders() globals.SugarLogger.Infof("LoadPendingOrders orders count:%d", len(orders)) ordersCount := len(orders) if ordersCount > 0 { - bills := curOrderManager.LoadPendingWaybills() + bills := FixedOrderManager.LoadPendingWaybills() globals.SugarLogger.Infof("LoadPendingOrders waybills count:%d", len(bills)) var sortOrders StatusTimerSlice for _, order := range orders { diff --git a/business/jxcallback/orderman/orderman_ext.go b/business/jxcallback/orderman/orderman_ext.go new file mode 100644 index 000000000..da54c684b --- /dev/null +++ b/business/jxcallback/orderman/orderman_ext.go @@ -0,0 +1,53 @@ +package orderman + +import ( + "time" + + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/globals" + + "github.com/astaxie/beego/orm" +) + +const ( + maxLastHours = 2 * 24 // 最多只能查询两天内的订单数据 +) + +func (c *OrderManager) GetStoreOrderInfo(storeID string, lastHours int, fromStatus, toStatus int) (orders []*model.GoodsOrderExt, err error) { + if lastHours > maxLastHours { + lastHours = maxLastHours + } + if toStatus == 0 { + toStatus = fromStatus + } + + db := orm.NewOrm() + _, err = db.Raw(` + SELECT t1.*, t2.status waybill_status, t2.courier_name, t2.courier_mobile + FROM goods_order t1 + LEFT JOIN waybill t2 ON t1.vendor_waybill_id = t2.vendor_waybill_id AND t1.waybill_vendor_id = t2.waybill_vendor_id + WHERE IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) = ? + AND t1.order_created_at >= ? + AND t1.Status >= ? AND t1.Status <= ? + `, storeID, time.Now().Add(-time.Duration(lastHours)*time.Hour), fromStatus, toStatus).QueryRows(&orders) + if err == nil { + return orders, nil + } + globals.SugarLogger.Infof("GetStoreOrderInfo storeID:%s failed with error:%v", storeID, err) + return nil, err +} + +func (c *OrderManager) GetOrderSkuInfo(vendorOrderID string, vendorID int) (skus []*model.OrderSkuExt, err error) { + db := orm.NewOrm() + _, err = db.Raw(` + SELECT t1.*, t2.img image + FROM order_sku t1 + LEFT JOIN jx_sku_name t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id + WHERE vendor_order_id = ? AND vendor_id = ? + `, vendorOrderID, vendorID).QueryRows(&skus) + if err == nil { + return skus, nil + } + globals.SugarLogger.Infof("GetOrderSkuInfo orderID:%s failed with error:%v", vendorOrderID, err) + return nil, err +} diff --git a/business/jxcallback/scheduler/basesch/basesch.go b/business/jxcallback/scheduler/basesch/basesch.go index aa4f54087..828f72aff 100644 --- a/business/jxcallback/scheduler/basesch/basesch.go +++ b/business/jxcallback/scheduler/basesch/basesch.go @@ -1,6 +1,7 @@ package basesch import ( + "errors" "fmt" "git.rosy.net.cn/baseapi/utils" @@ -16,6 +17,14 @@ type BaseScheduler struct { IsReallyCallPlatformAPI bool } +var ( + FixedBaseScheduler *BaseScheduler +) + +var ( + ErrOrderStatusIsNotSuitable = errors.New("订单状态不适合当前操作") +) + func (c *BaseScheduler) Init() { c.PurchasePlatformHandlers = make(map[int]partner.IPurchasePlatformHandler) c.DeliveryPlatformHandlers = make(map[int]*scheduler.DeliveryPlatformHandlerInfo) @@ -75,25 +84,12 @@ func (c *BaseScheduler) PickupGoods(order *model.GoodsOrder) (err error) { }, "PickupGoods orderID:%s", order.VendorOrderID) } } else { + err = ErrOrderStatusIsNotSuitable globals.SugarLogger.Infof("PickupGoods orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) } return err } -func (c *BaseScheduler) PickupGoodsExternal(vendorOrderID string, vendorID int) (err error) { - order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err == nil { - err = c.PickupGoods(order) - if err == nil { - order.Status = model.OrderStatusFinishedPickup - err = utils.CallFuncLogErrorWithInfo(func() error { - return partner.CurOrderManager.UpdateOrderStatusDirectly(order) - }, "PickupGoodsExternal orderID:%s", order.VendorOrderID) - } - } - return err -} - func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder) (err error) { globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s", order.VendorOrderID) if order.LockStatus == model.OrderStatusUnknown && order.Status == model.OrderStatusFinishedPickup { @@ -106,25 +102,12 @@ func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder) (err error) } } } else { + err = ErrOrderStatusIsNotSuitable globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status) } return err } -func (c *BaseScheduler) Swtich2SelfDeliverExternal(vendorOrderID string, vendorID int) (err error) { - order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) - if err == nil { - err = c.Swtich2SelfDeliver(order) - if err == nil { - order.Status = model.OrderStatusDelivering - err = utils.CallFuncLogErrorWithInfo(func() error { - return partner.CurOrderManager.UpdateOrderStatusDirectly(order) - }, "Swtich2SelfDeliverExternal orderID:%s", order.VendorOrderID) - } - } - return err -} - func (c *BaseScheduler) Swtich2SelfDelivered(order *model.GoodsOrder) (err error) { globals.SugarLogger.Infof("Swtich2SelfDelivered orderID:%s", order.VendorOrderID) if order.LockStatus == model.OrderStatusUnknown && order.Status == model.OrderStatusDelivering { @@ -172,21 +155,24 @@ func (c *BaseScheduler) SelfDeliverDelievered(order *model.GoodsOrder) (err erro return err } -func (c *BaseScheduler) CreateWaybill(platformVendorID int, order *model.GoodsOrder) (err error) { +func (c *BaseScheduler) CreateWaybill(platformVendorID int, order *model.GoodsOrder, policy func(deliveryFee, addFee int64) error) (bill *model.Waybill, err error) { globals.SugarLogger.Infof("CreateWaybill orderID:%s, vendorID:%d", order.VendorOrderID, platformVendorID) if !model.IsOrderSolid(order) { // 如果订单是不完整的 globals.SugarLogger.Warnf("CreateWaybill orderID:%s, vendorID:%d is not solid!!!", order.VendorOrderID, platformVendorID) - return scheduler.ErrOrderIsNotSolid + return nil, scheduler.ErrOrderIsNotSolid } if c.IsReallyCallPlatformAPI { handlerInfo := c.GetDeliveryPlatformFromVendorID(platformVendorID) - if handlerInfo.Use4CreateWaybill { - if err = handlerInfo.Handler.CreateWaybill(order); err != nil { + if handlerInfo != nil && handlerInfo.Use4CreateWaybill { + bill, err = handlerInfo.Handler.CreateWaybill(order, policy) + if err != nil { globals.SugarLogger.Infof("CreateWaybill failed orderID:%s vendorID:%d with error:%v", order.VendorOrderID, platformVendorID, err) } + } else { + err = scheduler.ErrDeliverProviderWrong } } - return err + return bill, err } func (c *BaseScheduler) CancelWaybill(bill *model.Waybill) (err error) { diff --git a/business/jxcallback/scheduler/basesch/basesch_ext.go b/business/jxcallback/scheduler/basesch/basesch_ext.go new file mode 100644 index 000000000..a5f81d218 --- /dev/null +++ b/business/jxcallback/scheduler/basesch/basesch_ext.go @@ -0,0 +1,43 @@ +package basesch + +import ( + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/partner" +) + +func (c *BaseScheduler) CreateWaybillOnProviders(vendorOrderID string, vendorID int) (bills []*model.Waybill, err error) { + order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) + if err == nil { + bill, err2 := c.CreateWaybill(model.VendorIDMTPS, order, nil) + if err = err2; err == nil { + return []*model.Waybill{ + bill, + }, nil + } + } + return nil, err +} + +func (c *BaseScheduler) Swtich2SelfDeliverAndUpdateStatus(vendorOrderID string, vendorID int) (err error) { + order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) + if err == nil { + err = c.Swtich2SelfDeliver(order) + if err == nil { + order.Status = model.OrderStatusDelivering + err = partner.CurOrderManager.UpdateOrderStatusDirectly(order) + } + } + return err +} + +func (c *BaseScheduler) PickupGoodsAndUpdateStatus(vendorOrderID string, vendorID int) (err error) { + order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) + if err == nil { + err = c.PickupGoods(order) + if err == nil { + order.Status = model.OrderStatusFinishedPickup + err = partner.CurOrderManager.UpdateOrderStatusDirectly(order) + } + } + return err +} diff --git a/business/jxcallback/scheduler/defsch/defsch.go b/business/jxcallback/scheduler/defsch/defsch.go index cbb74621e..9d670e485 100644 --- a/business/jxcallback/scheduler/defsch/defsch.go +++ b/business/jxcallback/scheduler/defsch/defsch.go @@ -1,6 +1,7 @@ package defsch import ( + "errors" "fmt" "math/rand" "time" @@ -34,6 +35,14 @@ const ( orderMapStoreMaxTime = 4 * 24 * time.Hour // cache最长存储时间 ) +const ( + maxAddFee = 200 // 最大增加费用,单位为分,超过不发三方配送了 +) + +var ( + ErrAddFeeExceeded = errors.New("配送超过基准价太多") +) + type WatchOrderInfo struct { autoPickupTimeoutMinute int // 0表示禁用,1表示用缺省值time2AutoPickupMin,其它表示分钟数 storeDeliveryType int @@ -133,6 +142,7 @@ func init() { sch := &DefScheduler{} sch.IsReallyCallPlatformAPI = globals.ReallyCallPlatformAPI sch.Init() + basesch.FixedBaseScheduler = &sch.BaseScheduler scheduler.CurrentScheduler = sch sch.defWorkflowConfig = []map[int]*StatusActionConfig{ map[int]*StatusActionConfig{ @@ -405,8 +415,24 @@ func (s *DefScheduler) createWaybillOn3rdProviders(savedOrderInfo *WatchOrderInf if savedOrderInfo.retryCount <= maxWaybillRetryCount { successCount := 0 for _, vendorID := range savedOrderInfo.supported3rdCarriers { - if s.DeliveryPlatformHandlers[vendorID] != nil && s.DeliveryPlatformHandlers[vendorID].Use4CreateWaybill && savedOrderInfo.waybills[vendorID] == nil && (excludeBill == nil || vendorID != excludeBill.WaybillVendorID) { - if err = s.CreateWaybill(vendorID, order); err == nil { + handlerInfo := s.GetDeliveryPlatformFromVendorID(vendorID) + if handlerInfo != nil && handlerInfo.Use4CreateWaybill && savedOrderInfo.waybills[vendorID] == nil && (excludeBill == nil || vendorID != excludeBill.WaybillVendorID) { + if _, err = s.CreateWaybill(vendorID, order, func(deliveryFee, addFee int64) error { + if addFee > maxAddFee { + db := orm.NewOrm() + globals.SugarLogger.Infof("CreateWaybill orderID:%s addFee exceeded too much, it's %d", order.VendorOrderID, addFee) + + tmpLog := &legacymodel.TempLog{ + VendorOrderID: order.VendorOrderID, + RefVendorOrderID: order.VendorOrderID, + IntValue1: addFee, + Msg: fmt.Sprintf("CreateWaybill orderID:%s addFee exceeded too much, it's %d", order.VendorOrderID, addFee), + } + db.Insert(tmpLog) + return ErrAddFeeExceeded + } + return nil + }); err == nil { successCount++ } } diff --git a/business/jxcallback/scheduler/scheduler.go b/business/jxcallback/scheduler/scheduler.go index 479d89eb8..b76c402e0 100644 --- a/business/jxcallback/scheduler/scheduler.go +++ b/business/jxcallback/scheduler/scheduler.go @@ -38,6 +38,7 @@ var ( ErrCanNotFindOrder = errors.New("不能找到订单(一般是由于事件错序)") ErrCanNotFindWaybill = errors.New("不能找到运单(一般是由于事件错序)") ErrOrderIsNotSolid = errors.New("订单是临时订单,不完整,不能用于创建运单") + ErrDeliverProviderWrong = errors.New("快递商不存在或不能用于创建运单") ) type DeliveryPlatformHandlerInfo struct { diff --git a/business/jxutils/jxutils.go b/business/jxutils/jxutils.go index 8f30abbea..d44325383 100644 --- a/business/jxutils/jxutils.go +++ b/business/jxutils/jxutils.go @@ -179,3 +179,15 @@ func MapValue2Scope(value, fromMin, fromMax, toMin, toMax int64) int64 { } return int64(math.Round(float64(value-fromMin)/float64(fromMax-fromMin)*float64(toMax-toMin) + float64(toMin))) } + +func Errs2Str(sep string, errs ...error) (retVal string) { + if sep == "" { + sep = "\n" + } + for _, err := range errs { + if err != nil { + retVal += err.Error() + sep + } + } + return retVal +} diff --git a/business/model/api.go b/business/model/api.go new file mode 100644 index 000000000..098b3c72f --- /dev/null +++ b/business/model/api.go @@ -0,0 +1,19 @@ +package model + +type CallResult struct { + Code string `json:"code"` + Desc string `json:"desc"` + Data string `json:"data"` +} + +type GoodsOrderExt struct { + GoodsOrder + WaybillStatus int `json:"waybillStatus"` + CourierName string `orm:"size(32)" json:"courierName"` + CourierMobile string `orm:"size(32)" json:"courierMobile"` +} + +type OrderSkuExt struct { + OrderSku + Image string `json:"image"` +} diff --git a/business/model/model.go b/business/model/model.go index 766f710b0..801d5a656 100644 --- a/business/model/model.go +++ b/business/model/model.go @@ -22,8 +22,3 @@ type ModelIDCUO struct { UpdatedAt time.Time LastOperator string `gorm:"type:varchar(32)"` // 最后操作员 } - -type CallResult struct { - Code int `json:"code"` - Result string `json:"result"` -} diff --git a/business/model/order.go b/business/model/order.go index 3f38164a7..0b0d9c703 100644 --- a/business/model/order.go +++ b/business/model/order.go @@ -8,49 +8,49 @@ type ModelTimeInfo struct { } type GoodsOrder struct { - ID int64 `orm:"column(id)"` - VendorOrderID string `orm:"column(vendor_order_id);size(48)"` - VendorID int `orm:"column(vendor_id)"` - VendorStoreID string `orm:"column(vendor_store_id);size(48)"` - StoreID int `orm:"column(store_id)"` // 外部系统里记录的 jxstoreid - JxStoreID int `orm:"column(jx_store_id)"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid - StoreName string `orm:"size(64)"` - ShopPrice int64 // 单位为分 门店标价 - SalePrice int64 // 单位为分 售卖价 - ActualPayPrice int64 // 单位为分 顾客实际支付 - Weight int // 单位为克 - ConsigneeName string `orm:"size(32)"` - ConsigneeMobile string `orm:"size(32)"` - ConsigneeAddress string `orm:"size(255)"` - CoordinateType int - ConsigneeLng int // 坐标 * (10的六次方) - ConsigneeLat int // 坐标 * (10的六次方) - SkuCount int // 商品类别数量,即有多少种商品(注意在某些情况下,相同SKU的商品由于售价不同,也会当成不同商品在这个值里) - GoodsCount int // 商品个数 - Status int // 参见OrderStatus*相关的常量定义 - VendorStatus string `orm:"size(255)"` - LockStatus int - OrderSeq int // 门店订单序号 - BuyerComment string `orm:"size(255)"` - BusinessType int - ExpectedDeliveredTime time.Time `orm:"type(datetime)"` // 预期送达时间 - CancelApplyReason string `orm:"size(255)"` // ""表示没有申请,不为null表示用户正在取消申请 - VendorWaybillID string `orm:"column(vendor_waybill_id);size(48)"` - WaybillVendorID int `orm:"column(waybill_vendor_id)"` // 表示当前承运商,-1表示还没有安排 + ID int64 `orm:"column(id)" json:"_"` + VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` + VendorID int `orm:"column(vendor_id)" json:"vendorID"` + VendorStoreID string `orm:"column(vendor_store_id);size(48)" json:"vendorStoreID"` + StoreID int `orm:"column(store_id)" json:"storeID"` // 外部系统里记录的 jxstoreid + JxStoreID int `orm:"column(jx_store_id)" json:"jxStoreID"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid + StoreName string `orm:"size(64)" json:"_"` + ShopPrice int64 `json:"shopPrice"` // 单位为分 门店标价 + SalePrice int64 `json:"salePrice"` // 单位为分 售卖价 + ActualPayPrice int64 `json:"actualPayPrice"` // 单位为分 顾客实际支付 + Weight int `json:"weight"` // 单位为克 + ConsigneeName string `orm:"size(32)" json:"consigneeName"` + ConsigneeMobile string `orm:"size(32)" json:"consigneeMobile"` + ConsigneeAddress string `orm:"size(255)" json:"consigneeAddress"` + CoordinateType int `json:"_"` + ConsigneeLng int `json:"_"` // 坐标 * (10的六次方) + ConsigneeLat int `json:"_"` // 坐标 * (10的六次方) + SkuCount int `json:"skuCount"` // 商品类别数量,即有多少种商品(注意在某些情况下,相同SKU的商品由于售价不同,也会当成不同商品在这个值里) + GoodsCount int `json:"goodsCount"` // 商品个数 + Status int `json:"status"` // 参见OrderStatus*相关的常量定义 + VendorStatus string `orm:"size(255)" json:"_"` + LockStatus int `json:"lockStatus"` + OrderSeq int `json:"orderSeq"` // 门店订单序号 + BuyerComment string `orm:"size(255)" json:"buyerComment"` + BusinessType int `json:"businessType"` + ExpectedDeliveredTime time.Time `orm:"type(datetime)" json:"expectedDeliveredTime"` // 预期送达时间 + CancelApplyReason string `orm:"size(255)" json:"_"` // ""表示没有申请,不为null表示用户正在取消申请 + VendorWaybillID string `orm:"column(vendor_waybill_id);size(48)" json:"vendorWaybillID"` + WaybillVendorID int `orm:"column(waybill_vendor_id)" json:"waybillVendorID"` // 表示当前承运商,-1表示还没有安排 DuplicatedCount int // 重复新订单消息数,这个一般不是由于消息重发造成的(消息重发由OrderStatus过滤),一般是业务逻辑造成的 - OrderCreatedAt time.Time `orm:"type(datetime);index"` // 这里记录的是订单生效时间,即用户支付完成(货到付款即为下单时间) - OrderFinishedAt time.Time `orm:"type(datetime)"` - StatusTime time.Time `orm:"type(datetime)"` // last status time - ModelTimeInfo - OriginalData string `orm:"type(text)"` - Skus []*OrderSku `orm:"-"` - SkuPmFee int64 //门店商品促销总支出 - OrderPmFee int64 //门店订单促销支出 - SkuPmSubsidy int64 //平台商品促销总补贴 - OrderPmSubsidy int64 //平台订单促销补贴 - BoxFee int64 //餐盒费 - PlatformFeeRate int16 //平台费 - BillStoreFreightFee int64 //需要回调,门店所承担的运费 + OrderCreatedAt time.Time `orm:"type(datetime);index" json:"orderCreatedAt"` // 这里记录的是订单生效时间,即用户支付完成(货到付款即为下单时间) + OrderFinishedAt time.Time `orm:"type(datetime)" json:"OrderFinishedAt"` + StatusTime time.Time `orm:"type(datetime)" json:"_"` // last status time + ModelTimeInfo `json:"_"` + OriginalData string `orm:"type(text)" json:"_"` + Skus []*OrderSku `orm:"-" json:"_"` + SkuPmFee int64 `json:"_"` //门店商品促销总支出 + OrderPmFee int64 `json:"_"` //门店订单促销支出 + SkuPmSubsidy int64 `json:"_"` //平台商品促销总补贴 + OrderPmSubsidy int64 `json:"_"` //平台订单促销补贴 + BoxFee int64 `json:"_"` //餐盒费 + PlatformFeeRate int16 `json:"_"` //平台费 + BillStoreFreightFee int64 `json:"_"` //需要回调,门店所承担的运费 } func (o *GoodsOrder) TableUnique() [][]string { @@ -60,24 +60,24 @@ func (o *GoodsOrder) TableUnique() [][]string { } type OrderSku struct { - ID int64 `orm:"column(id)"` - VendorOrderID string `orm:"column(vendor_order_id);size(48)"` - VendorID int `orm:"column(vendor_id)"` - StoreSubID int `orm:"column(store_sub_id)"` - StoreSubName string `orm:"size(64)"` - Count int - VendorSkuID string `orm:"column(vendor_sku_id);size(48)"` - SkuID int `orm:"column(sku_id)"` // 外部系统里记录的 jxskuid - JxSkuID int `orm:"column(jx_sku_id)"` // 根据VendorSkuID在本地系统里查询出来的 jxskuid - SkuName string `orm:"size(255)"` - ShopPrice int64 // 门店标价 - SalePrice int64 // 售卖价 - Weight int // 单位为克 - SkuType int // 当前如果为gift就为1,否则缺省为0 - PromotionType int // todo 当前是用于记录京东的PromotionType(生成jxorder用),没有做转换 - OrderCreatedAt time.Time `orm:"type(datetime);index"` // 分区考虑 - SkuPmSubsidy int64 //平台商品活动补贴 - SkuPmFee int64 //门店商品促销支出 + ID int64 `orm:"column(id)" json:"_"` + VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` + VendorID int `orm:"column(vendor_id)" json:"vendorID"` + StoreSubID int `orm:"column(store_sub_id)" json:"storeSubID"` + StoreSubName string `orm:"size(64)" json:"storeSubName"` + Count int `json:"count"` + VendorSkuID string `orm:"column(vendor_sku_id);size(48)" json:"_"` + SkuID int `orm:"column(sku_id)" json:"skuID"` // 外部系统里记录的 jxskuid + JxSkuID int `orm:"column(jx_sku_id)" json:"jxSkuID"` // 根据VendorSkuID在本地系统里查询出来的 jxskuid + SkuName string `orm:"size(255)" json:"skuName"` + ShopPrice int64 `json:"shopPrice"` // 门店标价 + SalePrice int64 `json:"salePrice"` // 售卖价 + Weight int `json:"_"` // 单位为克 + SkuType int `json:"_"` // 当前如果为gift就为1,否则缺省为0 + PromotionType int `json:"_"` // todo 当前是用于记录京东的PromotionType(生成jxorder用),没有做转换 + OrderCreatedAt time.Time `orm:"type(datetime);index" json:"_"` // 分区考虑 + SkuPmSubsidy int64 `json:"_"` //平台商品活动补贴 + SkuPmFee int64 `json:"_"` //门店商品促销支出 } // 同样商品在一个订单中可能重复出现(比如搞活动时,相同商品价格不一样,第一个有优惠) @@ -89,25 +89,25 @@ func (o *OrderSku) TableIndex() [][]string { } type Waybill struct { - ID int64 `orm:"column(id)"` - VendorWaybillID string `orm:"column(vendor_waybill_id);size(48)"` - VendorWaybillID2 string `orm:"column(vendor_waybill_id2);size(48)"` // 某些平台有多个ID,比如美团配送,当前美团配送的 delivery_id存这里 - WaybillVendorID int `orm:"column(waybill_vendor_id)"` - VendorOrderID string `orm:"column(vendor_order_id);size(48)"` - OrderVendorID int `orm:"column(order_vendor_id)"` - CourierName string `orm:"size(32)"` - CourierMobile string `orm:"size(32)"` - Status int // 参见WaybillStatus*相关的常量定义 - VendorStatus string `orm:"size(255)"` - ActualFee int64 // 实际要支付给快递公司的实际费用 - DesiredFee int64 // 根据合同计算出来的预期费用 - DuplicatedCount int // 重复新订单消息数,这个一般不是由于消息重发造成的(消息重发由OrderStatus过滤),一般是业务逻辑造成的 - WaybillCreatedAt time.Time `orm:"type(datetime);index"` - WaybillFinishedAt time.Time `orm:"type(datetime)"` - StatusTime time.Time `orm:"type(datetime)"` // last status time - ModelTimeInfo - OriginalData string `orm:"type(text)"` - Remark string `orm:"-"` // 用于传递remark + ID int64 `orm:"column(id)" json:"-"` + VendorWaybillID string `orm:"column(vendor_waybill_id);size(48)" json:"vendorWaybillID"` + VendorWaybillID2 string `orm:"column(vendor_waybill_id2);size(48)" json:"vendorWaybillID2"` // 某些平台有多个ID,比如美团配送,当前美团配送的 delivery_id存这里 + WaybillVendorID int `orm:"column(waybill_vendor_id)" json:"waybillVendorID"` + VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` + OrderVendorID int `orm:"column(order_vendor_id)" json:"orderVendorID"` + CourierName string `orm:"size(32)" json:"-"` + CourierMobile string `orm:"size(32)" json:"-"` + Status int `json:"-"` // 参见WaybillStatus*相关的常量定义 + VendorStatus string `orm:"size(255)" json:"-"` + ActualFee int64 `json:"actual_fee"` // 实际要支付给快递公司的费用 + DesiredFee int64 `json:"desired_fee"` // 运单总费用 + DuplicatedCount int `json:"-"` // 重复新订单消息数,这个一般不是由于消息重发造成的(消息重发由OrderStatus过滤),一般是业务逻辑造成的 + WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"_"` + WaybillFinishedAt time.Time `orm:"type(datetime)" json:"-"` + StatusTime time.Time `orm:"type(datetime)" json:"-"` // last status time + ModelTimeInfo `json:"-"` + OriginalData string `orm:"type(text)" json:"-"` + Remark string `orm:"-" json:"-"` // 用于传递remark } func (w *Waybill) TableUnique() [][]string { diff --git a/business/partner/delivery/dada/waybill.go b/business/partner/delivery/dada/waybill.go index 6a9346c8c..5e8b3192d 100644 --- a/business/partner/delivery/dada/waybill.go +++ b/business/partner/delivery/dada/waybill.go @@ -91,7 +91,7 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *dadaapi.CallbackMsg) (retVal } // IDeliveryPlatformHandler -func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder) (err error) { +func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy func(deliveryFee, addFee int64) error) (bill *model.Waybill, err error) { billParams := &dadaapi.OperateOrderRequiredParams{ ShopNo: utils.Int2Str(order.StoreID), // 当前达达的门店号与京西是一样的 OriginID: jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), @@ -111,6 +111,9 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder) (err error) { "info": order.BuyerComment, // "origin_mark": model.VendorNames[order.VendorID], "origin_mark_no": fmt.Sprintf("%d", order.OrderSeq), + "cargo_type": 13, + "cargo_weight": float64(order.Weight) / 1000.0, + "cargo_num": order.GoodsCount, } // 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单 @@ -122,17 +125,27 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder) (err error) { AND vendor_order_id = ? AND waybill_vendor_id = ? `, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), model.VendorIDDada).ValuesList(&lists) + var result *dadaapi.CreateOrderResponse if err2 == nil && num > 0 { globals.SugarLogger.Debugf("CreateWaybill orderID:%s num=%d use ReaddOrder", order.VendorOrderID, num) - _, err = api.DadaAPI.ReaddOrder(billParams, addParams) + result, err = api.DadaAPI.ReaddOrder(billParams, addParams) } else { if err2 != nil { globals.SugarLogger.Warnf("CreateWaybill orderID:%s error:%v", order.VendorOrderID, err2) } - _, err = api.DadaAPI.AddOrder(billParams, addParams) + result, err = api.DadaAPI.AddOrder(billParams, addParams) + } + if err == nil && result != nil { + bill = &model.Waybill{ + VendorOrderID: order.VendorOrderID, + OrderVendorID: order.VendorID, + WaybillVendorID: model.VendorIDDada, + DesiredFee: jxutils.StandardPrice2Int(result.DeliverFee), + ActualFee: jxutils.StandardPrice2Int(result.Fee), + } } } - return err + return bill, err } func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill) (err error) { diff --git a/business/partner/delivery/dada/waybill_test.go b/business/partner/delivery/dada/waybill_test.go index 317027c9b..2bd0031aa 100644 --- a/business/partner/delivery/dada/waybill_test.go +++ b/business/partner/delivery/dada/waybill_test.go @@ -27,7 +27,8 @@ func TestCreateWaybill(t *testing.T) { if order, err := partner.CurOrderManager.LoadOrder(orderID, model.VendorIDJD); err == nil { // globals.SugarLogger.Debug(order) c := new(DeliveryHandler) - if err = c.CreateWaybill(order); err == nil { + _, err = c.CreateWaybill(order, nil) + if err == nil { time.Sleep(1 * time.Second) bill := &model.Waybill{ VendorOrderID: orderID, diff --git a/business/partner/delivery/mtps/waybill.go b/business/partner/delivery/mtps/waybill.go index 24d18b20d..4f1fdb4d3 100644 --- a/business/partner/delivery/mtps/waybill.go +++ b/business/partner/delivery/mtps/waybill.go @@ -19,13 +19,8 @@ import ( "github.com/astaxie/beego/orm" ) -const ( - maxAddFee = 200 // 最大增加费用,单位为分,超过不发美团了 -) - var ( ErrCanNotFindMTPSStore = errors.New("不能找到美团配送站点配置") - ErrAddFeeExceeded = errors.New("美团配送超过基准价太多") ErrStoreNoPriceInfo = errors.New("找不到门店的美团配送价格信息") ErrStoreNoCoordinate = errors.New("找不到门店的坐标信息") ) @@ -176,11 +171,14 @@ func (c *DeliveryHandler) calculateBillDeliveryFee(bill *model.Waybill) (deliver } // IDeliveryPlatformHandler -func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder) (err error) { +func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy func(deliveryFee, addFee int64) error) (bill *model.Waybill, err error) { db := orm.NewOrm() - _, addFee, err := c.calculateOrderDeliveryFee(order, time.Now(), db) + deliveryFee, addFee, err := c.calculateOrderDeliveryFee(order, time.Now(), db) if err == nil { - if addFee <= maxAddFee { + if policy != nil { + err = policy(deliveryFee, addFee) + } + if err == nil { // 忽略坐标转换错误,即使是转换出错,也只能当成转换成功来处理,底层会有错误日志输出 lngFloat, latFloat, _ := jxutils.IntCoordinate2MarsStandard(order.ConsigneeLng, order.ConsigneeLat, order.CoordinateType) billParams := &mtpsapi.CreateOrderByShopParam{ @@ -219,8 +217,8 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder) (err error) { } } addParams := utils.Params2Map("note", order.BuyerComment, "goods_detail", string(utils.MustMarshal(goods)), "poi_seq", fmt.Sprintf("#%d", order.OrderSeq)) - _, err = api.MtpsAPI.CreateOrderByShop(billParams, addParams) - if err != nil { + result, err2 := api.MtpsAPI.CreateOrderByShop(billParams, addParams) + if err = err2; err != nil { globals.SugarLogger.Debugf("CreateWaybill failed, orderID:%s, billParams:%v, addParams:%v, error:%v", order.VendorOrderID, billParams, addParams, err) tmpLog := &legacymodel.TempLog{ @@ -230,23 +228,21 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder) (err error) { Msg: fmt.Sprintf("CreateWaybill failed, orderID:%s, billParams:%v, addParams:%v, error:%v", order.VendorOrderID, billParams, addParams, err), } db.Insert(tmpLog) + } else { + bill = &model.Waybill{ + VendorOrderID: order.VendorOrderID, + OrderVendorID: order.VendorID, + VendorWaybillID: result.MtPeisongID, + VendorWaybillID2: utils.Int64ToStr(result.DeliveryID), + WaybillVendorID: model.VendorIDMTPS, + DesiredFee: deliveryFee, + } } } } - } else { - err = ErrAddFeeExceeded - globals.SugarLogger.Infof("CreateWaybill orderID:%s addFee exceeded too much, it's %d", order.VendorOrderID, addFee) - - tmpLog := &legacymodel.TempLog{ - VendorOrderID: order.VendorOrderID, - RefVendorOrderID: order.VendorOrderID, - IntValue1: addFee, - Msg: fmt.Sprintf("CreateWaybill orderID:%s addFee exceeded too much, it's %d", order.VendorOrderID, addFee), - } - db.Insert(tmpLog) } } - return err + return bill, err } func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill) (err error) { diff --git a/business/partner/delivery/mtps/waybill_test.go b/business/partner/delivery/mtps/waybill_test.go index 28a473a9e..64a12d88d 100644 --- a/business/partner/delivery/mtps/waybill_test.go +++ b/business/partner/delivery/mtps/waybill_test.go @@ -26,7 +26,8 @@ func TestCreateWaybill(t *testing.T) { order, _ := partner.CurOrderManager.LoadOrder(orerID, model.VendorIDJD) // globals.SugarLogger.Debug(order) c := new(DeliveryHandler) - if err := c.CreateWaybill(order); err != nil { + _, err := c.CreateWaybill(order, nil) + if err != nil { t.Fatal(err.Error()) } } diff --git a/business/partner/partner.go b/business/partner/partner.go index 44e880a64..55e2c7f25 100644 --- a/business/partner/partner.go +++ b/business/partner/partner.go @@ -44,7 +44,7 @@ type IPurchasePlatformHandler interface { } type IDeliveryPlatformHandler interface { - CreateWaybill(order *model.GoodsOrder) (err error) + CreateWaybill(order *model.GoodsOrder, policy func(deliveryFee, addFee int64) error) (bill *model.Waybill, err error) CancelWaybill(bill *model.Waybill) (err error) } diff --git a/controllers/jx_order.go b/controllers/jx_order.go index 6dcf0931c..cf9ae74e5 100644 --- a/controllers/jx_order.go +++ b/controllers/jx_order.go @@ -1,6 +1,10 @@ package controllers import ( + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" + "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler/basesch" + "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/model" "github.com/astaxie/beego" ) @@ -11,18 +15,134 @@ type OrderController struct { func (c *OrderController) URLMapping() { c.Mapping("FinishedPickup", c.FinishedPickup) + c.Mapping("GetStoreOrderInfo", c.GetStoreOrderInfo) + c.Mapping("GetOrderSkuInfo", c.GetOrderSkuInfo) + c.Mapping("CreateWaybillOnProviders", c.CreateWaybillOnProviders) + c.Mapping("Swtich2SelfDeliver", c.Swtich2SelfDeliver) } -// @Title 完成拣货 -// @Description 完成拣货,如果是购物平台负责配送,则自动召唤相应配送 -// @Param orderid path string true "订单ID" -// @Success 200 {object} business.model.CallResult -// @Failure 200 {object} business.model.CallResult -// @router /finishedpickup/:orderid [post] -func (c *OrderController) FinishedPickup() { - c.Data["json"] = &model.CallResult{ - Code: 0, - Result: c.Ctx.Input.Param(":orderid"), +func (c *OrderController) orderOperate(handler func(vendorOrderID string, vendorID int) (interface{}, error)) { + var ( + vendorOrderID string + vendorID int + err error + ) + vendorOrderID = c.GetString("vendorOrderID") + vendorID, err1 := c.GetInt("vendorID", 0) + if vendorOrderID != "" && err1 == nil { + result, err2 := handler(vendorOrderID, vendorID) + if err = err2; err == nil { + retObj := &model.CallResult{ + Code: "0", + } + if result != nil { + retObj.Data = string(utils.MustMarshal(result)) + } + c.Data["json"] = retObj + } + } + errMsg := jxutils.Errs2Str("", err1, err) + if vendorOrderID == "" { + errMsg += "vendorOrderID is empty\n" + } + if errMsg != "" { + c.Data["json"] = &model.CallResult{ + Code: "-1", + Data: errMsg, + } } c.ServeJSON() } + +// @Title 完成拣货 +// @Description 完成拣货 +// @Param vendorOrderID formData string true "订单ID" +// @Param vendorID formData int true "订单所属的厂商ID" +// @Success 200 {object} business.model.CallResult +// @Failure 200 {object} business.model.CallResult +// @router /FinishedPickup [post] +func (c *OrderController) FinishedPickup() { + c.orderOperate(func(vendorOrderID string, vendorID int) (interface{}, error) { + return nil, basesch.FixedBaseScheduler.PickupGoodsAndUpdateStatus(vendorOrderID, vendorID) + }) +} + +// @Title 转自送 +// @Description 转自送 +// @Param vendorOrderID formData string true "订单ID" +// @Param vendorID formData int true "订单所属的厂商ID" +// @Success 200 {object} business.model.CallResult +// @Failure 200 {object} business.model.CallResult +// @router /Swtich2SelfDeliver [post] +func (c *OrderController) Swtich2SelfDeliver() { + c.orderOperate(func(vendorOrderID string, vendorID int) (interface{}, error) { + return nil, basesch.FixedBaseScheduler.Swtich2SelfDeliverAndUpdateStatus(vendorOrderID, vendorID) + }) +} + +// @Title 创建三方运单 +// @Description 创建三方运单 +// @Param vendorOrderID formData string true "订单ID" +// @Param vendorID formData int true "订单所属的厂商ID" +// @Success 200 {object} business.model.CallResult +// @Failure 200 {object} business.model.CallResult +// @router /CreateWaybillOnProviders [post] +func (c *OrderController) CreateWaybillOnProviders() { + c.orderOperate(func(vendorOrderID string, vendorID int) (interface{}, error) { + return basesch.FixedBaseScheduler.CreateWaybillOnProviders(vendorOrderID, vendorID) + }) +} + +// @Title 得到门店订单信息 +// @Description 得到门店订单信息 +// @Param storeID query string true "京西门店ID" +// @Param lastHours query int false "最近多少小时的信息" +// @Param fromStatus query int true "起始状态" +// @Param toStatus query int false "起始状态" +// @Success 200 {object} business.model.CallResult +// @Failure 200 {object} business.model.CallResult +// @router /GetStoreOrderInfo [get] +func (c *OrderController) GetStoreOrderInfo() { + var ( + storeID string + lastHours, fromStatus, toStatus int + err error + ) + storeID = c.GetString("storeID") + lastHours, err1 := c.GetInt("lastHours", 0) + fromStatus, err2 := c.GetInt("fromStatus", 0) + toStatus, err3 := c.GetInt("toStatus", 0) + if storeID != "" && err1 == nil && err2 == nil && err3 == nil { + result, err2 := orderman.FixedOrderManager.GetStoreOrderInfo(storeID, lastHours, fromStatus, toStatus) + if err = err2; err == nil { + c.Data["json"] = &model.CallResult{ + Code: "0", + Data: string(utils.MustMarshal(result)), + } + } + } + errMsg := jxutils.Errs2Str("", err1, err2, err3, err) + if storeID == "" { + errMsg += "storeID is empty\n" + } + if errMsg != "" { + c.Data["json"] = &model.CallResult{ + Code: "-1", + Desc: errMsg, + } + } + c.ServeJSON() +} + +// @Title 得到订单SKU信息 +// @Description 得到订单SKU信息 +// @Param vendorOrderID query string true "订单ID" +// @Param vendorID query int true "订单所属的厂商ID" +// @Success 200 {object} business.model.CallResult +// @Failure 200 {object} business.model.CallResult +// @router /GetOrderSkuInfo [get] +func (c *OrderController) GetOrderSkuInfo() { + c.orderOperate(func(vendorOrderID string, vendorID int) (interface{}, error) { + return orderman.FixedOrderManager.GetOrderSkuInfo(vendorOrderID, vendorID) + }) +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 7dbce193f..c4fd13600 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -7,10 +7,42 @@ import ( func init() { + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], + beego.ControllerComments{ + Method: "CreateWaybillOnProviders", + Router: `/CreateWaybillOnProviders`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], beego.ControllerComments{ Method: "FinishedPickup", - Router: `/finishedpickup/:orderid`, + Router: `/FinishedPickup`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], + beego.ControllerComments{ + Method: "GetOrderSkuInfo", + Router: `/GetOrderSkuInfo`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], + beego.ControllerComments{ + Method: "GetStoreOrderInfo", + Router: `/GetStoreOrderInfo`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], + beego.ControllerComments{ + Method: "Swtich2SelfDeliver", + Router: `/Swtich2SelfDeliver`, AllowHTTPMethods: []string{"post"}, MethodParams: param.Make(), Params: nil})