- order manager added.

This commit is contained in:
gazebo
2018-08-21 17:18:08 +08:00
parent c29bcea27c
commit 643bcac0f8
18 changed files with 463 additions and 165 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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++
}
}

View File

@@ -38,6 +38,7 @@ var (
ErrCanNotFindOrder = errors.New("不能找到订单(一般是由于事件错序)")
ErrCanNotFindWaybill = errors.New("不能找到运单(一般是由于事件错序)")
ErrOrderIsNotSolid = errors.New("订单是临时订单,不完整,不能用于创建运单")
ErrDeliverProviderWrong = errors.New("快递商不存在或不能用于创建运单")
)
type DeliveryPlatformHandlerInfo struct {

View File

@@ -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
}

19
business/model/api.go Normal file
View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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())
}
}

View File

@@ -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)
}

View File

@@ -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)
})
}

View File

@@ -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})