Merge branch 'master' into get-store

This commit is contained in:
gazebo
2019-06-24 10:17:56 +08:00
127 changed files with 9409 additions and 2126 deletions

View File

@@ -102,7 +102,7 @@ func (c *OrderManager) SaveOrderFinancialInfo(order *model.OrderFinancial, opera
} }
if err = dao.CreateEntity(db, sku); err != nil { if err = dao.CreateEntity(db, sku); err != nil {
if !dao.IsDuplicateError(err) { if !dao.IsDuplicateError(err) {
globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err: order is err", order.VendorOrderID) globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err:%v", order.VendorOrderID, err)
return err return err
} }
dao.Rollback(db) dao.Rollback(db)
@@ -120,7 +120,7 @@ func (c *OrderManager) SaveOrderFinancialInfo(order *model.OrderFinancial, opera
sku.JxShopMoney = order.JxShopMoney - sku.JxShopMoney sku.JxShopMoney = order.JxShopMoney - sku.JxShopMoney
if err = dao.CreateEntity(db, sku); err != nil { if err = dao.CreateEntity(db, sku); err != nil {
if !dao.IsDuplicateError(err) { if !dao.IsDuplicateError(err) {
globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err: order is err", order.VendorOrderID) globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err:%v", order.VendorOrderID, err)
return err return err
} }
dao.Rollback(db) dao.Rollback(db)

View File

@@ -2,7 +2,6 @@ package orderman
import ( import (
"fmt" "fmt"
"math"
"time" "time"
"git.rosy.net.cn/baseapi" "git.rosy.net.cn/baseapi"
@@ -11,7 +10,6 @@ import (
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/msghub"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego/orm" "github.com/astaxie/beego/orm"
) )
@@ -51,7 +49,8 @@ func (c *OrderManager) LoadPendingOrders() []*model.GoodsOrder {
// msgVendorStatus的意思是事件本身的类型类似有时收到NewOrder事件去取订单状态不一定就是New的 // msgVendorStatus的意思是事件本身的类型类似有时收到NewOrder事件去取订单状态不一定就是New的
// OnOrderAdjust也类似而OrderStatus要记录的是消息所以添加这个 // OnOrderAdjust也类似而OrderStatus要记录的是消息所以添加这个
func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus string) (err error) { func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, orderStatus *model.OrderStatus) (err error) {
globals.SugarLogger.Debugf("OnOrderNew orderID:%s", order.VendorOrderID)
if order.ConsigneeMobile2 == "" && !jxutils.IsMobileFake(order.ConsigneeMobile) { if order.ConsigneeMobile2 == "" && !jxutils.IsMobileFake(order.ConsigneeMobile) {
order.ConsigneeMobile2 = order.ConsigneeMobile order.ConsigneeMobile2 = order.ConsigneeMobile
} }
@@ -59,6 +58,7 @@ func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus strin
db := dao.GetDB() db := dao.GetDB()
dao.Begin(db) dao.Begin(db)
defer func() { defer func() {
globals.SugarLogger.Debugf("OnOrderNew exit orderID:%s", order.VendorOrderID)
if r := recover(); r != nil { if r := recover(); r != nil {
dao.Rollback(db) dao.Rollback(db)
panic(r) panic(r)
@@ -67,12 +67,7 @@ func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus strin
if order.Status == model.OrderStatusUnknown { if order.Status == model.OrderStatusUnknown {
order.Status = model.OrderStatusNew order.Status = model.OrderStatusNew
} }
status := model.Order2Status(order) isDuplicated, err := addOrderOrWaybillStatus(orderStatus, db)
if status.Status > model.OrderStatusNew {
status.Status = model.OrderStatusNew
}
status.VendorStatus = msgVendorStatus
isDuplicated, err := addOrderOrWaybillStatus(status, db)
if err == nil && !isDuplicated { if err == nil && !isDuplicated {
isDuplicated, err = c.SaveOrder(order, false, db) isDuplicated, err = c.SaveOrder(order, false, db)
} }
@@ -88,7 +83,7 @@ func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus strin
} }
// todo 调整单的处理可能还需要再细化一点,当前只是简单的删除重建 // todo 调整单的处理可能还需要再细化一点,当前只是简单的删除重建
func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus string) (err error) { func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, orderStatus *model.OrderStatus) (err error) {
if order.ConsigneeMobile2 == "" && !jxutils.IsMobileFake(order.ConsigneeMobile) { if order.ConsigneeMobile2 == "" && !jxutils.IsMobileFake(order.ConsigneeMobile) {
order.ConsigneeMobile2 = order.ConsigneeMobile order.ConsigneeMobile2 = order.ConsigneeMobile
} }
@@ -101,13 +96,12 @@ func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus st
panic(r) panic(r)
} }
}() }()
if order.Status == model.OrderStatusUnknown { // 出现过调整单后状态回到新订单状态比如911350836000622
// 不完全确定,加一个处理
if order.Status < model.OrderStatusAccepted {
order.Status = model.OrderStatusAccepted order.Status = model.OrderStatusAccepted
} }
status := model.Order2Status(order) isDuplicated, err := addOrderOrWaybillStatus(orderStatus, db)
status.Status = model.OrderStatusAdjust
status.VendorStatus = msgVendorStatus
isDuplicated, err := addOrderOrWaybillStatus(status, db)
if err == nil && !isDuplicated { if err == nil && !isDuplicated {
err = utils.CallFuncLogError(func() error { err = utils.CallFuncLogError(func() error {
_, err = db.Db.Raw("DELETE FROM order_sku WHERE vendor_order_id = ? AND vendor_id = ?", order.VendorOrderID, order.VendorID).Exec() _, err = db.Db.Raw("DELETE FROM order_sku WHERE vendor_order_id = ? AND vendor_id = ?", order.VendorOrderID, order.VendorID).Exec()
@@ -128,10 +122,9 @@ func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus st
if err == nil { if err == nil {
dao.Commit(db) dao.Commit(db)
if !isDuplicated { if !isDuplicated {
msghub.OnNewOrder(order)
// 因为订单调度器需要的是真实状态所以用order的状态 // 因为订单调度器需要的是真实状态所以用order的状态
_ = scheduler.CurrentScheduler.OnOrderNew(order, false) _ = scheduler.CurrentScheduler.OnOrderNew(order, false)
_ = scheduler.CurrentScheduler.OnOrderStatusChanged(order, model.Order2Status(order), false) _ = scheduler.CurrentScheduler.OnOrderStatusChanged(order, orderStatus, false)
} }
} else { } else {
dao.Rollback(db) dao.Rollback(db)
@@ -171,7 +164,7 @@ func (c *OrderManager) OnOrderMsg(order *model.GoodsOrder, vendorStatus, remark
VendorStatus: vendorStatus, VendorStatus: vendorStatus,
Status: model.OrderStatusMsg, Status: model.OrderStatusMsg,
StatusTime: time.Now(), StatusTime: time.Now(),
Remark: remark, Remark: utils.LimitUTF8StringLen(remark, 255),
}, nil) }, nil)
return err return err
} }
@@ -207,11 +200,12 @@ func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao
order.Status = orderStatus.Status order.Status = orderStatus.Status
order.VendorStatus = orderStatus.VendorStatus order.VendorStatus = orderStatus.VendorStatus
order.StatusTime = orderStatus.StatusTime order.StatusTime = orderStatus.StatusTime
jxutils.RefreshOrderSkuRelated(order)
} }
} }
} }
order.OrderCreatedAt = order.StatusTime
// globals.SugarLogger.Debugf("saveOrder isAdjust:%t, order:%v", isAdjust, order) // globals.SugarLogger.Debugf("saveOrder isAdjust:%t, order:%v", isAdjust, order)
created, _, err2 := db.Db.ReadOrCreate(order, "VendorOrderID", "VendorID") created, _, err2 := db.Db.ReadOrCreate(order, "VendorOrderID", "VendorID")
if err = err2; err == nil { if err = err2; err == nil {
@@ -223,22 +217,8 @@ func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao
} }
if _, _, err = db.Db.ReadOrCreate(originalOrder, "VendorOrderID", "VendorID"); err == nil { if _, _, err = db.Db.ReadOrCreate(originalOrder, "VendorOrderID", "VendorID"); err == nil {
if created { if created {
sql := `INSERT INTO order_sku(vendor_order_id, vendor_id, count, vendor_sku_id, sku_id, jx_sku_id, sku_name, if err = dao.CreateMultiEntities(db, order.Skus); err != nil {
shop_price, sale_price, weight, sku_type, promotion_type, order_created_at) VALUES` baseapi.SugarLogger.Warnf("saveOrder orderID:%s, save order_sku failed with error:%v", order.VendorOrderID, err)
params := []interface{}{}
for _, sku := range order.Skus {
sql += "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),"
// 有时不是通过京西平台建立的SKU不范围要超过
skuID := 0
if sku.SkuID < math.MaxInt32 {
skuID = sku.SkuID
}
params = append(params, sku.VendorOrderID, sku.VendorID, sku.Count, sku.VendorSkuID, skuID, sku.JxSkuID, sku.SkuName,
sku.ShopPrice, sku.SalePrice, sku.Weight, sku.SkuType, sku.PromotionType, order.OrderCreatedAt)
}
sql = sql[:len(sql)-1] + ";"
if _, err = db.Db.Raw(sql, params...).Exec(); err != nil {
baseapi.SugarLogger.Warnf("saveOrder insert order:%v, order_sku error:%v", order, err)
} }
} else { } else {
isDuplicated = true isDuplicated = true
@@ -256,12 +236,35 @@ func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao
return isDuplicated, err return isDuplicated, err
} }
func getPromotionSkuPriceMap(db *dao.DaoDB, storeID int, skuIDs []int) (skuPriceMap map[int64]*model.PromotionSku, err error) {
sql := `
SELECT t3.*
FROM promotion t1
JOIN promotion_store t2 ON t2.promotion_id = t1.id AND t2.store_id = ?
JOIN promotion_sku t3 ON t3.promotion_id = t1.id AND t3.sku_id IN (` + dao.GenQuestionMarks(len(skuIDs)) + `) AND t3.earning_price > 0
WHERE t1.deleted_at = ? AND (t1.status = ? OR t1.status = ?) AND (t1.begin_at <= NOW() AND t1.end_at >= NOW()) AND t1.vendor_id = ?`
var skuPriceList []*model.PromotionSku
if err = dao.GetRows(db, &skuPriceList, sql, storeID, skuIDs, utils.DefaultTimeValue, model.PromotionStatusLocalCreated, model.PromotionStatusRemoteCreated, model.VendorIDJX); err != nil {
return nil, err
}
skuPriceMap = make(map[int64]*model.PromotionSku)
for _, v := range skuPriceList {
if v.EarningPrice > 0 {
index := jxutils.Combine2Int(v.SkuID, v.Price)
if skuPriceMap[index] == nil || v.EarningPrice < skuPriceMap[index].EarningPrice {
skuPriceMap[index] = v
}
}
}
return skuPriceMap, err
}
func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.DaoDB) (err error) { func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.DaoDB) (err error) {
globals.SugarLogger.Debugf("updateOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID) globals.SugarLogger.Debugf("updateOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID)
jxStoreID := jxutils.GetShowStoreIDFromOrder(order) jxStoreID := jxutils.GetShowStoreIDFromOrder(order)
var opNumStr string var opNumStr string
if time.Now().Sub(order.OrderCreatedAt) < 48*time.Hour && order.VendorID != model.VendorIDJD { if time.Now().Sub(order.OrderCreatedAt) < 48*time.Hour && order.VendorID != model.VendorIDJD {
opNumStr = "" opNumStr = "2"
} else { } else {
opNumStr = "2" opNumStr = "2"
} }
@@ -273,11 +276,16 @@ func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.
orderSkus := order.Skus orderSkus := order.Skus
vendorSkuIDs := make([]int64, 0) vendorSkuIDs := make([]int64, 0)
skuIDMap := make(map[int]int)
for _, v := range orderSkus { for _, v := range orderSkus {
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0) intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
if intVendorSkuID != 0 { if intVendorSkuID != 0 {
vendorSkuIDs = append(vendorSkuIDs, intVendorSkuID) vendorSkuIDs = append(vendorSkuIDs, intVendorSkuID)
} }
if skuID := jxutils.GetSkuIDFromOrderSku(v); skuID > 0 {
skuIDMap[skuID] = 1
}
} }
if len(vendorSkuIDs) > 0 { if len(vendorSkuIDs) > 0 {
tableName := "t2" tableName := "t2"
@@ -301,7 +309,16 @@ func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.
skumapper[v.VendorSkuID] = v skumapper[v.VendorSkuID] = v
} }
skuPriceMap, err2 := getPromotionSkuPriceMap(db, jxStoreID, jxutils.IntMap2List(skuIDMap))
if err = err2; err != nil {
globals.SugarLogger.Errorf("updateOrderSkuOtherInfo can not get sku promotion info for orderID:%s, error:%v", order.VendorOrderID, err)
return err
}
for _, v := range orderSkus { for _, v := range orderSkus {
v.VendorOrderID = order.VendorOrderID
v.VendorID = order.VendorID
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0) intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
if intVendorSkuID != 0 && v.VendorSkuID != "-70000" { // todo hard code if intVendorSkuID != 0 && v.VendorSkuID != "-70000" { // todo hard code
skuBindInfo := skumapper[intVendorSkuID] skuBindInfo := skumapper[intVendorSkuID]
@@ -317,6 +334,13 @@ func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.
} }
} }
} }
if skuID := jxutils.GetSkuIDFromOrderSku(v); skuID > 0 && v.StoreSubName != "" {
index := jxutils.Combine2Int(jxStoreID, int(v.SalePrice))
if skuPriceMap[index] != nil {
v.EarningPrice = int64(skuPriceMap[index].EarningPrice)
}
}
} }
} }
return nil return nil
@@ -336,11 +360,7 @@ func (c *OrderManager) updateOrderOtherInfo(order *model.GoodsOrder, db *dao.Dao
} }
order.JxStoreID = storeMap.StoreID order.JxStoreID = storeMap.StoreID
if err = c.updateOrderSkuOtherInfo(order, db); err == nil { if err = c.updateOrderSkuOtherInfo(order, db); err == nil {
if order.Weight == 0 { jxutils.RefreshOrderSkuRelated(order)
for _, v := range order.Skus {
order.Weight += v.Weight
}
}
} }
return err return err
} }
@@ -369,20 +389,21 @@ func (c *OrderManager) addOrderStatus(orderStatus *model.OrderStatus, db *dao.Da
updateFields = append(updateFields, "Status", "StatusTime") updateFields = append(updateFields, "Status", "StatusTime")
if order.LockStatus != model.OrderStatusUnknown { if order.LockStatus != model.OrderStatusUnknown {
order.LockStatus = model.OrderStatusUnknown order.LockStatus = model.OrderStatusUnknown
order.LockStatusTime = orderStatus.StatusTime updateFields = append(updateFields, "LockStatus")
updateFields = append(updateFields, "LockStatus", "LockStatusTime")
} }
} else { } else {
if model.IsOrderUnlockStatus(orderStatus.Status) { if model.IsOrderUnlockStatus(orderStatus.Status) {
order.LockStatus = model.OrderStatusUnknown order.LockStatus = model.OrderStatusUnknown
} else { updateFields = append(updateFields, "LockStatus")
} else if !model.IsOrderFinalStatus(orderStatus.Status) {
if order.LockStatus != model.OrderStatusUnknown { if order.LockStatus != model.OrderStatusUnknown {
globals.SugarLogger.Warnf("addOrderStatus refOrderID:%s, orderID:%s, order.LockStatus:%d, status.LockStatus:%d", orderStatus.RefVendorOrderID, orderStatus.VendorOrderID, order.LockStatus, orderStatus.Status) globals.SugarLogger.Warnf("addOrderStatus refOrderID:%s, orderID:%s, order.LockStatus:%d, status.LockStatus:%d", orderStatus.RefVendorOrderID, orderStatus.VendorOrderID, order.LockStatus, orderStatus.Status)
} }
order.Flag &= ^model.OrderFlagMaskUserApplyCancel
order.LockStatus = orderStatus.Status order.LockStatus = orderStatus.Status
}
order.LockStatusTime = orderStatus.StatusTime order.LockStatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "LockStatus", "LockStatusTime") updateFields = append(updateFields, "LockStatus", "LockStatusTime", "Flag")
}
} }
if model.IsOrderFinalStatus(orderStatus.Status) { if model.IsOrderFinalStatus(orderStatus.Status) {
order.OrderFinishedAt = orderStatus.StatusTime order.OrderFinishedAt = orderStatus.StatusTime
@@ -430,6 +451,7 @@ func (c *OrderManager) loadOrder(vendorOrderID, vendorOrderID2 string, vendorID
}, "LoadOrder orderID:%s", vendorOrderID) }, "LoadOrder orderID:%s", vendorOrderID)
} }
if err != nil { if err != nil {
order = nil
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
err = ErrCanNotFindOrder err = ErrCanNotFindOrder
} }
@@ -475,11 +497,12 @@ func (c *OrderManager) loadOrderFinancial(vendorOrderID, vendorOrderID2 string,
if err = db.Read(order, keyFields...); err == nil { if err = db.Read(order, keyFields...); err == nil {
vendorOrderID = order.VendorOrderID vendorOrderID = order.VendorOrderID
err = utils.CallFuncLogError(func() error { err = utils.CallFuncLogError(func() error {
_, err = db.QueryTable("order_sku_financial").Filter("vendor_order_id", vendorOrderID).Filter("vendor_id", vendorID).All(&order.Skus) _, err = db.QueryTable("order_sku_financial").Filter("vendor_order_id", vendorOrderID).Filter("vendor_id", vendorID).Filter("is_afs_order", 0).All(&order.Skus)
return err return err
}, "LoadOrder orderID:%s", vendorOrderID) }, "LoadOrder orderID:%s", vendorOrderID)
} }
if err != nil { if err != nil {
order = nil
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
err = ErrCanNotFindOrder err = ErrCanNotFindOrder
} }
@@ -488,30 +511,15 @@ func (c *OrderManager) loadOrderFinancial(vendorOrderID, vendorOrderID2 string,
return order, err return order, err
} }
func (c *OrderManager) UpdateOrderStatusAndFlag(order *model.GoodsOrder) (err error) { func (c *OrderManager) UpdateOrderStatusAndDeliveryFlag(order *model.GoodsOrder) (err error) {
db := orm.NewOrm() return c.UpdateOrderFields(order, []string{"Status", "DeliveryFlag"})
utils.CallFuncLogError(func() error {
_, err = db.Update(order, "Status", "DeliveryFlag")
return err
}, "UpdateOrderStatusAndFlag orderID:%s failed with error:%v", order.VendorOrderID, err)
return err
} }
//Waybill func (c *OrderManager) UpdateOrderFields(order *model.GoodsOrder, fieldList []string) (err error) {
func (c *OrderManager) UpdateWaybillVendorID(bill *model.Waybill, revertStatus bool) (err error) {
globals.SugarLogger.Debugf("UpdateWaybillVendorID bill:%v", bill)
db := orm.NewOrm() db := orm.NewOrm()
params := orm.Params{
"vendor_waybill_id": bill.VendorWaybillID,
"waybill_vendor_id": bill.WaybillVendorID,
}
// 如果运单被取消,则要保持在已拣货状态
if revertStatus && bill.WaybillVendorID == model.VendorIDUnknown {
params["status"] = model.OrderStatusFinishedPickup
}
utils.CallFuncLogError(func() error { utils.CallFuncLogError(func() error {
_, err = db.QueryTable("goods_order").Filter("vendor_order_id", bill.VendorOrderID).Filter("vendor_id", bill.OrderVendorID).Update(params) _, err = db.Update(order, fieldList...)
return err return err
}, "UpdateWaybillVendorID update order, bill:%v", bill) }, "UpdateOrderFields orderID:%s failed with error:%v", order.VendorOrderID, err)
return err return err
} }

View File

@@ -0,0 +1,351 @@
package orderman
import (
"fmt"
"strings"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego/orm"
)
func (c *OrderManager) LoadAfsOrder(vendorAfsOrderID string, vendorID int) (afsOrder *model.AfsOrder, err error) {
return c.loadAfsOrder(dao.GetDB(), vendorAfsOrderID, vendorID)
}
func (c *OrderManager) loadAfsOrder(db *dao.DaoDB, vendorAfsOrderID string, vendorID int) (afsOrder *model.AfsOrder, err error) {
afsOrder = &model.AfsOrder{
AfsOrderID: vendorAfsOrderID,
VendorID: vendorID,
}
if err = dao.GetEntity(db, afsOrder, "AfsOrderID", "VendorID"); err != nil {
afsOrder = nil
}
return afsOrder, err
}
func (c *OrderManager) OnAfsOrderAdjust(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus) (err error) {
return c.onAfsOrderNew(afsOrder, orderStatus, true)
}
func (c *OrderManager) OnAfsOrderNew(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus) (err error) {
return c.onAfsOrderNew(afsOrder, orderStatus, false)
}
func (c *OrderManager) onAfsOrderNew(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus, isAdjust bool) (err error) {
db := dao.GetDB()
// globals.SugarLogger.Debugf("onAfsOrderNew1 afsOrder:%s", utils.Format4Output(afsOrder, true))
c.setAfsOrderID(db, orderStatus)
if afsOrder.AfsOrderID == "" {
afsOrder.AfsOrderID = orderStatus.VendorOrderID
}
if afsOrder.VendorStatus == "" {
afsOrder.VendorStatus = orderStatus.VendorStatus
}
if afsOrder.Status == model.OrderStatusUnknown {
afsOrder.Status = orderStatus.Status
}
// globals.SugarLogger.Debugf("onAfsOrderNew2 afsOrder:%s", utils.Format4Output(afsOrder, true))
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
panic(r)
}
}()
isDuplicated, err := addOrderOrWaybillStatus(orderStatus, db)
globals.SugarLogger.Debugf("onAfsOrderNew afsOrderID:%s, isDuplicated:%t", afsOrder.AfsOrderID, isDuplicated)
if err != nil || isDuplicated {
if err == nil {
dao.Commit(db)
}
return err
}
var existAfsOrder *model.AfsOrder
if existAfsOrder, err = c.loadAfsOrder(db, afsOrder.AfsOrderID, afsOrder.VendorID); err != nil {
if !dao.IsNoRowsError(err) {
return err
}
}
if existAfsOrder != nil {
existAfsOrder.Status = afsOrder.Status
existAfsOrder.VendorStatus = afsOrder.VendorStatus
if _, err = dao.UpdateEntity(db, existAfsOrder, "Status", "VendorStatus"); err != nil {
return err
}
} else {
// 外退都要先全删除再建
if afsOrder.RefundType == model.AfsTypeFullRefund {
isAdjust = true
}
if err = c.SaveAfsOrder(db, afsOrder, isAdjust); err != nil {
return err
}
}
dao.Commit(db)
scheduler.CurrentScheduler.OnAfsOrderNew(afsOrder, false)
return err
}
func (c *OrderManager) SaveAfsOrder(db *dao.DaoDB, afsOrder *model.AfsOrder, isDeleteFirst bool) (err error) {
globals.SugarLogger.Debug(afsOrder.AfsOrderID)
if db == nil {
db = dao.GetDB()
}
if err = c.updateAfsOrderOtherInfo(db, afsOrder); err != nil {
return err
}
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
if isDeleteFirst {
err = utils.CallFuncLogError(func() error {
_, err = dao.DeleteEntity(db, afsOrder, "VendorOrderID", "VendorID")
return err
}, "SaveAfsOrder delete AfsOrder, afsOrderID:%s", afsOrder.AfsOrderID)
if err != nil {
return err
}
err = utils.CallFuncLogError(func() error {
_, err = dao.DeleteEntity(db, &model.OrderSkuFinancial{
VendorOrderID: afsOrder.VendorOrderID,
VendorID: afsOrder.VendorID,
IsAfsOrder: 1,
}, "VendorOrderID", "VendorID", "IsAfsOrder")
return err
}, "SaveAfsOrder delete OrderSkuFinancial, afsOrderID:%s", afsOrder.AfsOrderID)
if err != nil {
return err
}
}
// 平台结算扣除汇总--平台补贴,售后产生运费,平台收包装费,同城运费、、、
deductionsByPm := afsOrder.PmSubsidyMoney + afsOrder.AfsFreightMoney + afsOrder.BoxMoney + afsOrder.TongchengFreightMoney
afsOrder.RefundMoneyByCal = afsOrder.SkuUserMoney + afsOrder.FreightUserMoney + deductionsByPm - afsOrder.PmRefundMoney
// order.TotalMoney += order.SkuJxMoney // 退款单京西补贴部分先不作计算
if err = dao.CreateEntity(db, afsOrder); err != nil {
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err:%v", afsOrder.AfsOrderID, err)
return err
}
// 京西结算扣除汇总,先不作计算,计算单条sku最终扣款金额+该条sku承担的平台结算扣除金额
for _, orderSku := range afsOrder.Skus[1:] {
orderSku.RefundMoneyByCal = orderSku.PmSkuSubsidyMoney +
utils.Float64TwoInt64(float64(afsOrder.RefundMoneyByCal-afsOrder.PmSkuSubsidyMoney)*float64(orderSku.UserMoney+orderSku.PmSubsidyMoney-orderSku.PmSkuSubsidyMoney)/float64(afsOrder.SkuUserMoney+afsOrder.PmSubsidyMoney-afsOrder.PmSkuSubsidyMoney))
afsOrder.Skus[0].RefundMoneyByCal += orderSku.RefundMoneyByCal
if err = dao.CreateEntity(db, orderSku); err != nil {
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err:%v, orderSku:%s", afsOrder.AfsOrderID, err, utils.Format4Output(orderSku, true))
return err
}
}
if len(afsOrder.Skus) > 0 {
orderSku := afsOrder.Skus[0]
orderSku.RefundMoneyByCal = afsOrder.RefundMoneyByCal - orderSku.RefundMoneyByCal
if err = dao.CreateEntity(db, orderSku); err != nil {
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err:%v, orderSku:%s", afsOrder.AfsOrderID, err, utils.Format4Output(orderSku, true))
return err
}
} else {
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err: afsOrder have no sku", afsOrder.AfsOrderID)
}
dao.Commit(db)
return err
}
func (c *OrderManager) OnAfsOrderStatusChanged(orderStatus *model.OrderStatus) (err error) {
db := dao.GetDB()
c.setAfsOrderID(db, orderStatus)
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
isDuplicated, afsOrder, err := c.addAfsOrderStatus(db, orderStatus)
if err != nil || isDuplicated {
if err == nil {
dao.Commit(db)
} else {
dao.Rollback(db)
}
return err
}
dao.Commit(db)
scheduler.CurrentScheduler.OnAfsOrderStatusChanged(afsOrder, orderStatus, false)
return err
}
func (c *OrderManager) addAfsOrderStatus(db *dao.DaoDB, orderStatus *model.OrderStatus) (isDuplicated bool, order *model.AfsOrder, err error) {
globals.SugarLogger.Debugf("addAfsOrderStatus refOrderID:%s, orderID:%s", orderStatus.RefVendorOrderID, orderStatus.VendorOrderID)
if db == nil {
db = dao.GetDB()
}
isDuplicated, err = addOrderOrWaybillStatus(orderStatus, db)
if err == nil && !isDuplicated && (orderStatus.Status != model.OrderStatusUnknown && orderStatus.Status != model.OrderStatusMsg) {
order = &model.AfsOrder{
AfsOrderID: orderStatus.VendorOrderID,
VendorID: orderStatus.VendorID,
}
if err = db.Db.ReadForUpdate(order, "AfsOrderID", "VendorID"); err == nil {
if orderStatus.Status > model.OrderStatusUnknown { // todo 要求status不能回绕
order.VendorStatus = orderStatus.VendorStatus
order.Status = orderStatus.Status
updateFields := []string{
"VendorStatus",
"Status",
}
if model.IsAfsOrderFinalStatus(orderStatus.Status) {
order.AfsFinishedAt = orderStatus.StatusTime
updateFields = append(updateFields, "AfsFinishedAt")
}
utils.CallFuncLogError(func() error {
_, err = dao.UpdateEntity(db, order, updateFields...)
return err
}, "addAfsOrderStatus update orderID:%s, status:%v", order.VendorOrderID, orderStatus)
} else {
isDuplicated = true
}
} else {
if dao.IsNoRowsError(err) { // todo 消息错序
err = nil
} else {
globals.SugarLogger.Warnf("addAfsOrderStatus orderID:%s read failed with error:%v", order.VendorOrderID, err)
}
}
}
return isDuplicated, order, err
}
func (c *OrderManager) updateAfsOrderSkuOtherInfo(db *dao.DaoDB, order *model.AfsOrder) (err error) {
globals.SugarLogger.Debugf("updateAfsOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID)
jxStoreID := jxutils.GetSaleStoreIDFromAfsOrder(order)
opNumStr := "2"
if jxStoreID == 0 {
globals.SugarLogger.Infof("updateAfsOrderSkuOtherInfo [运营%s]订单在京西与平台都找不到京西门店信息orderID:%s, VendorStoreID:%s", opNumStr, order.VendorOrderID, order.VendorStoreID)
return nil
}
orderSkus := order.Skus
vendorSkuIDs := make([]int64, 0)
for _, v := range orderSkus {
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
if intVendorSkuID != 0 {
vendorSkuIDs = append(vendorSkuIDs, intVendorSkuID)
}
}
if len(vendorSkuIDs) > 0 {
tableName := "t2"
if model.MultiStoresVendorMap[order.VendorID] == 1 {
tableName = "t1"
}
fieldPrefix := dao.ConvertDBFieldPrefix(model.VendorNames[order.VendorID])
sql := `
SELECT %s.%s_id vendor_sku_id, t1.id sku_id, t2.price, t1.weight
FROM sku t1
LEFT JOIN store_sku_bind t2 ON t1.id = t2.sku_id AND t2.deleted_at = ? AND t2.store_id = ?
WHERE t1.deleted_at = ? AND %s.%s_id IN (-1, ` + dao.GenQuestionMarks(len(vendorSkuIDs)) + ")"
sql = fmt.Sprintf(sql, tableName, fieldPrefix, tableName, fieldPrefix)
var skuInfos []*tStoreSkuBindAndVendorSkuID
if err = dao.GetRows(db, &skuInfos, sql, utils.DefaultTimeValue, jxStoreID, utils.DefaultTimeValue, vendorSkuIDs); err != nil {
globals.SugarLogger.Errorf("updateAfsOrderSkuOtherInfo can not get sku info for orderID:%s, error:%v", order.VendorOrderID, err)
return err
}
skumapper := make(map[int64]*tStoreSkuBindAndVendorSkuID)
for _, v := range skuInfos {
skumapper[v.VendorSkuID] = v
}
for _, v := range orderSkus {
v.AfsOrderID = order.AfsOrderID
v.VendorID = order.VendorID
v.VendorOrderID = order.VendorOrderID
v.IsAfsOrder = 1
v.VendorStoreID = order.VendorStoreID
v.StoreID = order.StoreID
v.JxStoreID = jxStoreID
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
if intVendorSkuID != 0 && v.VendorSkuID != "-70000" { // todo hard code
skuBindInfo := skumapper[intVendorSkuID]
if skuBindInfo == nil {
globals.SugarLogger.Infof("updateAfsOrderSkuOtherInfo [运营%s]%s订单sku找不到门店价格或商品映射orderID:%s, StoreID:%d, VendorSkuID:%s, sku:%v", opNumStr, model.VendorChineseNames[order.VendorID], order.VendorOrderID, jxStoreID, v.VendorSkuID, v)
} else {
v.JxSkuID = skuBindInfo.SkuID
}
}
}
}
return nil
}
func (c *OrderManager) updateAfsOrderOtherInfo(db *dao.DaoDB, afsOrder *model.AfsOrder) (err error) {
globals.SugarLogger.Debugf("updateAfsOrderOtherInfo orderID:%s, VendorStoreID:%s", afsOrder.VendorOrderID, afsOrder.VendorStoreID)
if afsOrder.VendorStoreID != "" {
if storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, afsOrder.VendorStoreID, 0); err == nil {
afsOrder.JxStoreID = storeDetail.Store.ID
}
}
if afsOrder.StoreID == 0 && afsOrder.JxStoreID == 0 {
if order, err2 := c.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID); err2 == nil {
afsOrder.JxStoreID = order.JxStoreID
if afsOrder.StoreID == 0 {
afsOrder.StoreID = order.StoreID
}
if afsOrder.VendorStoreID == "" {
afsOrder.VendorStoreID = order.VendorStoreID
}
}
}
if err == nil {
if err = c.updateAfsOrderSkuOtherInfo(db, afsOrder); err == nil {
jxutils.RefreshAfsOrderSkuRelated(afsOrder)
}
}
return err
}
func (c *OrderManager) UpdateAfsOrderFields(afsOrder *model.AfsOrder, fieldList []string) (err error) {
db := orm.NewOrm()
utils.CallFuncLogError(func() error {
_, err = db.Update(afsOrder, fieldList...)
return err
}, "UpdateAfsOrderFields orderID:%s failed with error:%v", afsOrder.VendorOrderID, err)
return err
}
func (c *OrderManager) setAfsOrderID(db *dao.DaoDB, orderStatus *model.OrderStatus) {
// globals.SugarLogger.Debugf("setAfsOrderID1 orderStatus:%v", utils.Format4Output(orderStatus, true))
if dao.IsVendorThingIDEmpty(orderStatus.VendorOrderID) {
index := 1
if afsOrderList, err2 := dao.GetAfsOrders(db, orderStatus.RefVendorID, orderStatus.RefVendorOrderID, ""); err2 == nil {
if len(afsOrderList) > 0 {
list := strings.Split(afsOrderList[0].AfsOrderID, "-")
if len(list) > 1 {
index = int(utils.Str2Int64WithDefault(list[1], 0))
if afsOrderList[0].Status >= model.AfsOrderStatusFinished {
index++
}
}
}
} else {
globals.SugarLogger.Warnf("setAfsOrderID err2:%v", err2)
}
orderStatus.VendorOrderID = composeAfsOrderID(orderStatus.RefVendorOrderID, index)
}
// globals.SugarLogger.Debugf("setAfsOrderID2 orderStatus:%v", utils.Format4Output(orderStatus, true))
}
func composeAfsOrderID(vendorOrderID string, index int) (afsOrderID string) {
return strings.Join([]string{
vendorOrderID,
utils.Int2Str(index),
}, "-")
}

View File

@@ -102,13 +102,17 @@ func (c *OrderManager) OnOrderComments(orderCommentList []*model.OrderComment) (
comment2.Maxmodifytime = int(orderComment.ModifyDuration) comment2.Maxmodifytime = int(orderComment.ModifyDuration)
if orderComment.VendorID != model.VendorIDELM { if orderComment.VendorID != model.VendorIDELM {
var order *model.GoodsOrder var order *model.GoodsOrder
if orderComment.VendorID != model.VendorIDEBAI { if true /*orderComment.VendorID != model.VendorIDEBAI*/ {
order, err = partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID) order, _ = partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID)
} }
if order != nil { if order != nil {
orderComment.StoreID = jxutils.GetSaleStoreIDFromOrder(order) orderComment.StoreID = jxutils.GetSaleStoreIDFromOrder(order)
if order.ConsigneeMobile2 != "" {
orderComment.ConsigneeMobile = order.ConsigneeMobile2
} else {
orderComment.ConsigneeMobile = order.ConsigneeMobile orderComment.ConsigneeMobile = order.ConsigneeMobile
} }
}
if orderComment.StoreID > 0 { if orderComment.StoreID > 0 {
comment2.Jxstoreid = utils.Int2Str(orderComment.StoreID) comment2.Jxstoreid = utils.Int2Str(orderComment.StoreID)
} }

View File

@@ -65,8 +65,10 @@ func init() {
func addOrderOrWaybillStatus(status *model.OrderStatus, db *dao.DaoDB) (isDuplicated bool, err error) { func addOrderOrWaybillStatus(status *model.OrderStatus, db *dao.DaoDB) (isDuplicated bool, err error) {
if status.OrderType == model.OrderTypeOrder { if status.OrderType == model.OrderTypeOrder {
globals.SugarLogger.Debugf("addOrderStatus order:%v", status) globals.SugarLogger.Debugf("addOrderStatus order:%v", status)
} else { } else if status.OrderType == model.OrderTypeWaybill {
globals.SugarLogger.Debugf("addOrderStatus waybill:%v", status) globals.SugarLogger.Debugf("addOrderStatus waybill:%v", status)
} else {
globals.SugarLogger.Debugf("addOrderStatus afsOrder:%v", status)
} }
dao.Begin(db) dao.Begin(db)
defer func() { defer func() {
@@ -100,6 +102,17 @@ func addOrderOrWaybillStatus(status *model.OrderStatus, db *dao.DaoDB) (isDuplic
return isDuplicated, err return isDuplicated, err
} }
func (c *OrderManager) GetStatusDuplicatedCount(status *model.OrderStatus) (duplicatedCount int) {
if status == nil {
return 0
}
db := dao.GetDB()
if err := dao.GetEntity(db, status, "VendorOrderID", "VendorID", "OrderType", "VendorStatus", "StatusTime"); err == nil {
return status.DuplicatedCount
}
return 0
}
// todo 最好还是改成全事件回放算了 // todo 最好还是改成全事件回放算了
func LoadPendingOrders() { func LoadPendingOrders() {
orders := FixedOrderManager.LoadPendingOrders() orders := FixedOrderManager.LoadPendingOrders()
@@ -140,17 +153,17 @@ func LoadPendingOrders() {
if order, ok := item.(*model.GoodsOrder); ok { if order, ok := item.(*model.GoodsOrder); ok {
jxutils.CallMsgHandlerAsync(func() { jxutils.CallMsgHandlerAsync(func() {
scheduler.CurrentScheduler.OnOrderNew(order, true) scheduler.CurrentScheduler.OnOrderNew(order, true)
}, order.VendorOrderID) }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
} else if status, ok := item.(*model.OrderStatus); ok { } else if status, ok := item.(*model.OrderStatus); ok {
jxutils.CallMsgHandlerAsync(func() { jxutils.CallMsgHandlerAsync(func() {
order := orderMap[jxutils.ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)] order := orderMap[jxutils.ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)]
scheduler.CurrentScheduler.OnOrderStatusChanged(order, status, true) scheduler.CurrentScheduler.OnOrderStatusChanged(order, status, true)
}, status.VendorOrderID) }, jxutils.ComposeUniversalOrderID(status.RefVendorOrderID, status.RefVendorID))
} else { } else {
bill := item.(*model.Waybill) bill := item.(*model.Waybill)
jxutils.CallMsgHandlerAsync(func() { jxutils.CallMsgHandlerAsync(func() {
scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, true) scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, true)
}, bill.VendorOrderID) }, jxutils.ComposeUniversalOrderID(bill.VendorOrderID, bill.OrderVendorID))
} }
curTime := time.Now() curTime := time.Now()
timeout := sleepGap - curTime.Sub(lastTime) timeout := sleepGap - curTime.Sub(lastTime)

View File

@@ -3,8 +3,11 @@ package orderman
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"time" "time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/excel" "git.rosy.net.cn/jx-callback/business/jxutils/excel"
@@ -27,33 +30,21 @@ type tWaybillExt struct {
StoreID int `json:"storeID" orm:"column(store_id)"` StoreID int `json:"storeID" orm:"column(store_id)"`
} }
//此函数会被GetStoreOrderCountInfo2取代 type StoresOrderSaleInfo struct {
func (c *OrderManager) GetStoreOrderCountInfo(ctx *jxcontext.Context, storeID string, lastHours int) (countInfo []*model.GoodsOrderCountInfo, err error) { StoreID int `orm:"column(store_id)" json:"storeID"`
globals.SugarLogger.Debugf("GetStoreOrderCountInfo storeID:%s", storeID) VendorID int `orm:"column(vendor_id)" json:"vendorID"`
if lastHours > maxLastHours { Status int `json:"status"`
lastHours = maxLastHours Count int `json:"count"`
} else if lastHours == 0 { ShopPrice int64 `json:"shopPrice"`
lastHours = defLastHours VendorPrice int64 `json:"vendorPrice"`
} SalePrice int64 `json:"salePrice"`
ActualPayPrice int64 `json:"actualPayPrice"`
db := orm.NewOrm() EarningPrice int64 `json:"earningPrice"` // 预估结算给门店老板的钱
_, err = db.Raw(`
SELECT t1.status, COUNT(*) count
FROM goods_order t1
WHERE t1.vendor_id <> 2 AND IF(t1.vendor_id = ?, t1.store_id, IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) ) = ?
AND t1.order_created_at >= ? AND t1.lock_status = ?
GROUP BY 1
ORDER BY 1
`, model.VendorIDWSC, storeID, time.Now().Add(-time.Duration(lastHours)*time.Hour), model.OrderStatusUnknown).QueryRows(&countInfo)
if err == nil {
return countInfo, nil
}
globals.SugarLogger.Infof("GetStoreOrderCountInfo storeID:%s failed with error:%v", storeID, err)
return nil, err
} }
func (c *OrderManager) GetStoreOrderCountInfo2(ctx *jxcontext.Context, storeID, lastHours int) (countInfo []*model.GoodsOrderCountInfo2, err error) { func (c *OrderManager) GetStoreOrderCountInfo(ctx *jxcontext.Context, storeID, lastHours int) (countInfo []*model.GoodsOrderCountInfo, err error) {
globals.SugarLogger.Debugf("GetStoreOrderCountInfo2 storeID:%s", storeID) globals.SugarLogger.Debugf("GetStoreOrderCountInfo storeID:%d", storeID)
if lastHours > maxLastHours { if lastHours > maxLastHours {
lastHours = maxLastHours lastHours = maxLastHours
} else if lastHours == 0 { } else if lastHours == 0 {
@@ -72,7 +63,7 @@ func (c *OrderManager) GetStoreOrderCountInfo2(ctx *jxcontext.Context, storeID,
if err == nil { if err == nil {
return countInfo, nil return countInfo, nil
} }
globals.SugarLogger.Infof("GetStoreOrderCountInfo2 storeID:%s failed with error:%v", storeID, err) globals.SugarLogger.Infof("GetStoreOrderCountInfo storeID:%d failed with error:%v", storeID, err)
return nil, err return nil, err
} }
@@ -86,7 +77,26 @@ func (c *OrderManager) GetOrderSkuInfo(ctx *jxcontext.Context, vendorOrderID str
db := dao.GetDB() db := dao.GetDB()
if vendorID == model.VendorIDELM { if vendorID == model.VendorIDELM {
err = dao.GetRows(db, &skus, fmt.Sprintf(` err = dao.GetRows(db, &skus, fmt.Sprintf(`
SELECT t1.*, IF(t3.img IS NULL OR t3.img = '', t4.col_imageUrl, t3.img) image, %s full_sku_name SELECT
t1.id,
t1.vendor_order_id,
t1.vendor_id,
t1.count,
t1.vendor_sku_id,
t1.sku_id,
t1.jx_sku_id,
t1.sku_name,
IF(t1.shop_price = 0, t1.sale_price, t1.shop_price) shop_price,
t1.sale_price,
t1.earning_price,
t1.weight,
t1.sku_type,
t1.promotion_type,
t1.order_created_at,
t1.store_sub_id,
t1.store_sub_name,
t1.vendor_price,
IF(t3.img IS NULL OR t3.img = '', t4.col_imageUrl, t3.img) image, %s full_sku_name
FROM order_sku t1 FROM order_sku t1
LEFT JOIN sku t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id/* AND t2.deleted_at = ?*/ LEFT JOIN sku t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id/* AND t2.deleted_at = ?*/
LEFT JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/ LEFT JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/
@@ -96,7 +106,26 @@ func (c *OrderManager) GetOrderSkuInfo(ctx *jxcontext.Context, vendorOrderID str
`, fullSkuNameSQL), /*, utils.DefaultTimeValue, utils.DefaultTimeValue*/ vendorOrderID, vendorID) `, fullSkuNameSQL), /*, utils.DefaultTimeValue, utils.DefaultTimeValue*/ vendorOrderID, vendorID)
} else if vendorID == model.VendorIDJD { } else if vendorID == model.VendorIDJD {
err = dao.GetRows(db, &skus, fmt.Sprintf(` err = dao.GetRows(db, &skus, fmt.Sprintf(`
SELECT t1.*, IF(t3.img IS NULL OR t3.img = '', t4.image, t3.img) image, %s full_sku_name SELECT
t1.id,
t1.vendor_order_id,
t1.vendor_id,
t1.count,
t1.vendor_sku_id,
t1.sku_id,
t1.jx_sku_id,
t1.sku_name,
IF(t1.shop_price = 0, t1.sale_price, t1.shop_price) shop_price,
t1.sale_price,
t1.earning_price,
t1.weight,
t1.sku_type,
t1.promotion_type,
t1.order_created_at,
t1.store_sub_id,
t1.store_sub_name,
t1.vendor_price,
IF(t3.img IS NULL OR t3.img = '', t4.image, t3.img) image, %s full_sku_name
FROM order_sku t1 FROM order_sku t1
LEFT JOIN sku t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id/* AND t2.deleted_at = ?*/ LEFT JOIN sku t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id/* AND t2.deleted_at = ?*/
LEFT JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/ LEFT JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/
@@ -107,7 +136,26 @@ func (c *OrderManager) GetOrderSkuInfo(ctx *jxcontext.Context, vendorOrderID str
} }
if err != nil || len(skus) == 0 { if err != nil || len(skus) == 0 {
err = dao.GetRows(db, &skus, fmt.Sprintf(` err = dao.GetRows(db, &skus, fmt.Sprintf(`
SELECT t1.*, t3.img image, %s full_sku_name SELECT
t1.id,
t1.vendor_order_id,
t1.vendor_id,
t1.count,
t1.vendor_sku_id,
t1.sku_id,
t1.jx_sku_id,
t1.sku_name,
IF(t1.shop_price = 0, t1.sale_price, t1.shop_price) shop_price,
t1.sale_price,
t1.earning_price,
t1.weight,
t1.sku_type,
t1.promotion_type,
t1.order_created_at,
t1.store_sub_id,
t1.store_sub_name,
t1.vendor_price,
t3.img image, %s full_sku_name
FROM order_sku t1 FROM order_sku t1
LEFT JOIN sku t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id/* AND t2.deleted_at = ?*/ LEFT JOIN sku t2 ON IF(t1.jx_sku_id != 0, t1.jx_sku_id, t1.sku_id) = t2.id/* AND t2.deleted_at = ?*/
LEFT JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/ LEFT JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/
@@ -157,19 +205,25 @@ func (c *OrderManager) GetOrderInfo(ctx *jxcontext.Context, vendorOrderID string
return nil, err return nil, err
} }
func (c *OrderManager) GetOrderWaybillInfo(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (bills []*model.Waybill, err error) { func (c *OrderManager) GetOrderWaybillInfo(ctx *jxcontext.Context, vendorOrderID string, vendorID int, isNotEnded bool) (bills []*model.Waybill, err error) {
globals.SugarLogger.Debugf("GetOrderWaybillInfo orderID:%s", vendorOrderID) globals.SugarLogger.Debugf("GetOrderWaybillInfo orderID:%s", vendorOrderID)
db := orm.NewOrm() db := dao.GetDB()
_, err = db.Raw(` sql := `
SELECT t1.* SELECT t1.*
FROM waybill t1 FROM waybill t1
WHERE t1.vendor_order_id = ? AND order_vendor_id = ? WHERE t1.vendor_order_id = ? AND order_vendor_id = ?
`, vendorOrderID, vendorID).QueryRows(&bills) `
if err == nil { sqlParams := []interface{}{
return bills, nil vendorOrderID,
vendorID,
} }
if isNotEnded {
sql += " AND t1.status < ?"
sqlParams = append(sqlParams, model.OrderStatusEndBegin)
}
err = dao.GetRows(db, &bills, sql, sqlParams...)
globals.SugarLogger.Infof("GetOrderWaybillInfo orderID:%s failed with error:%v", vendorOrderID, err) globals.SugarLogger.Infof("GetOrderWaybillInfo orderID:%s failed with error:%v", vendorOrderID, err)
return nil, err return bills, err
} }
func (c *OrderManager) ExportMTWaybills(ctx *jxcontext.Context, fromDateStr, toDateStr string) (excelContent []byte, err error) { func (c *OrderManager) ExportMTWaybills(ctx *jxcontext.Context, fromDateStr, toDateStr string) (excelContent []byte, err error) {
@@ -220,8 +274,8 @@ func (c *OrderManager) ExportMTWaybills(ctx *jxcontext.Context, fromDateStr, toD
return nil, err return nil, err
} }
func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { func (c *OrderManager) getOrders(ctx *jxcontext.Context, isIncludeSku bool, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (orders []*model.GoodsOrderExt, totalCount int, err error) {
globals.SugarLogger.Debugf("GetOrders from:%s to:%s", fromDateStr, toDateStr) globals.SugarLogger.Debugf("getOrders from:%s to:%s", fromDateStr, toDateStr)
pageSize = jxutils.FormalizePageSize(pageSize) pageSize = jxutils.FormalizePageSize(pageSize)
if offset < 0 { if offset < 0 {
@@ -229,11 +283,21 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
} }
sql := ` sql := `
SELECT SQL_CALC_FOUND_ROWS t1.*, SELECT SQL_CALC_FOUND_ROWS t1.*,
CAST(IF(t1.shop_price <= t1.vendor_price, IF(t1.shop_price = 0, t1.sale_price, t1.shop_price), IF(t1.vendor_price = 0, t1.sale_price, t1.vendor_price))*IF(t5.pay_percentage IS NULL OR t5.pay_percentage <= 0, 70, t5.pay_percentage)/100 AS SIGNED) earning_price,
t2.status waybill_status, t2.courier_name, t2.courier_mobile, t2.status waybill_status, t2.courier_name, t2.courier_mobile,
t2.actual_fee, t2.desired_fee, t2.waybill_created_at, t2.waybill_finished_at t2.actual_fee, t2.desired_fee, t2.waybill_created_at, t2.waybill_finished_at`
if isIncludeSku {
sql += `,
t3.sku_id, t3.count sku_count2, t3.shop_price sku_shop_price, IF(t3.earning_price <> 0, t3.earning_price, t3.sale_price) sku_sale_price`
}
sql += `
FROM goods_order t1 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 LEFT JOIN waybill t2 ON t1.vendor_waybill_id = t2.vendor_waybill_id AND t1.waybill_vendor_id = t2.waybill_vendor_id
` LEFT JOIN store t5 ON t5.id = IF(t1.jx_store_id <> 0, t1.jx_store_id, t1.store_id)`
if isIncludeSku {
sql += `
JOIN order_sku t3 ON t3.vendor_order_id = t1.vendor_order_id AND t3.vendor_id = t1.vendor_id`
}
var ( var (
sqlWhere string sqlWhere string
sqlParams []interface{} sqlParams []interface{}
@@ -257,14 +321,14 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
} else { } else {
fromDate, err2 := utils.TryStr2Time(fromDateStr) fromDate, err2 := utils.TryStr2Time(fromDateStr)
if err = err2; err != nil { if err = err2; err != nil {
return nil, err return nil, 0, err
} }
if toDateStr == "" { if toDateStr == "" {
toDateStr = fromDateStr toDateStr = fromDateStr
} }
toDate, err2 := utils.TryStr2Time(toDateStr) toDate, err2 := utils.TryStr2Time(toDateStr)
if err = err2; err != nil { if err = err2; err != nil {
return nil, err return nil, 0, err
} }
toDate = toDate.Add(24 * time.Hour) toDate = toDate.Add(24 * time.Hour)
@@ -293,17 +357,17 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["waybillVendorIDs"] != nil { if params["waybillVendorIDs"] != nil {
var waybillVendorIDs []int var waybillVendorIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["waybillVendorIDs"].(string)), &waybillVendorIDs); err != nil { if err = utils.UnmarshalUseNumber([]byte(params["waybillVendorIDs"].(string)), &waybillVendorIDs); err != nil {
return nil, err return nil, 0, err
} }
if len(waybillVendorIDs) > 0 { if len(waybillVendorIDs) > 0 {
sqlWhere += " AND t2.waybill_vendor_id IN (" + dao.GenQuestionMarks(len(waybillVendorIDs)) + ")" sqlWhere += " AND t1.waybill_vendor_id IN (" + dao.GenQuestionMarks(len(waybillVendorIDs)) + ")"
sqlParams = append(sqlParams, waybillVendorIDs) sqlParams = append(sqlParams, waybillVendorIDs)
} }
} }
if params["storeIDs"] != nil { if params["storeIDs"] != nil {
var storeIDs []int var storeIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["storeIDs"].(string)), &storeIDs); err != nil { if err = utils.UnmarshalUseNumber([]byte(params["storeIDs"].(string)), &storeIDs); err != nil {
return nil, err return nil, 0, err
} }
if len(storeIDs) > 0 { if len(storeIDs) > 0 {
sqlWhere += " AND IF(t1.vendor_id = ?, t1.store_id, IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) ) IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" sqlWhere += " AND IF(t1.vendor_id = ?, t1.store_id, IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) ) IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
@@ -313,7 +377,7 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["statuss"] != nil { if params["statuss"] != nil {
var statuss []int var statuss []int
if err = utils.UnmarshalUseNumber([]byte(params["statuss"].(string)), &statuss); err != nil { if err = utils.UnmarshalUseNumber([]byte(params["statuss"].(string)), &statuss); err != nil {
return nil, err return nil, 0, err
} }
if len(statuss) > 0 { if len(statuss) > 0 {
sqlWhere += " AND t1.status IN (" + dao.GenQuestionMarks(len(statuss)) + ")" sqlWhere += " AND t1.status IN (" + dao.GenQuestionMarks(len(statuss)) + ")"
@@ -323,7 +387,7 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["lockStatuss"] != nil { if params["lockStatuss"] != nil {
var lockStatuss []int var lockStatuss []int
if err = utils.UnmarshalUseNumber([]byte(params["lockStatuss"].(string)), &lockStatuss); err != nil { if err = utils.UnmarshalUseNumber([]byte(params["lockStatuss"].(string)), &lockStatuss); err != nil {
return nil, err return nil, 0, err
} }
if len(lockStatuss) > 0 { if len(lockStatuss) > 0 {
sqlWhere += " AND t1.lock_status IN (" + dao.GenQuestionMarks(len(lockStatuss)) + ")" sqlWhere += " AND t1.lock_status IN (" + dao.GenQuestionMarks(len(lockStatuss)) + ")"
@@ -333,7 +397,7 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["cities"] != nil { if params["cities"] != nil {
var cities []int var cities []int
if err = utils.UnmarshalUseNumber([]byte(params["cities"].(string)), &cities); err != nil { if err = utils.UnmarshalUseNumber([]byte(params["cities"].(string)), &cities); err != nil {
return nil, err return nil, 0, err
} }
if len(cities) > 0 { if len(cities) > 0 {
sql += " JOIN store st ON t1.store_id = st.id" sql += " JOIN store st ON t1.store_id = st.id"
@@ -345,22 +409,24 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["vendorIDs"] != nil { if params["vendorIDs"] != nil {
var vendorIDs []int var vendorIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["vendorIDs"].(string)), &vendorIDs); err != nil { if err = utils.UnmarshalUseNumber([]byte(params["vendorIDs"].(string)), &vendorIDs); err != nil {
return nil, err return nil, 0, err
} }
if len(vendorIDs) > 0 { if len(vendorIDs) > 0 {
sqlWhere += " AND t1.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDs)) + ")" sqlWhere += " AND t1.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDs)) + ")"
sqlParams = append(sqlParams, vendorIDs) sqlParams = append(sqlParams, vendorIDs)
} }
} }
db := dao.GetDB()
sql += sqlWhere sql += sqlWhere
if isIncludeSku {
sql += `
ORDER BY t1.id`
} else {
sql += ` sql += `
ORDER BY t1.order_created_at DESC ORDER BY t1.order_created_at DESC
LIMIT ? OFFSET ? LIMIT ? OFFSET ?`
`
sqlParams = append(sqlParams, pageSize, offset) sqlParams = append(sqlParams, pageSize, offset)
var orders []*model.GoodsOrderExt
db := dao.GetDB()
dao.Begin(db) dao.Begin(db)
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -368,16 +434,122 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
panic(r) panic(r)
} }
}() }()
}
if err = dao.GetRows(db, &orders, sql, sqlParams...); err == nil { if err = dao.GetRows(db, &orders, sql, sqlParams...); err == nil {
totalCount = dao.GetLastTotalRowCount(db)
}
if !isIncludeSku {
dao.Commit(db)
}
return orders, totalCount, err
}
func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
globals.SugarLogger.Debugf("GetOrders from:%s to:%s", fromDateStr, toDateStr)
orders, totalCount, err := c.getOrders(ctx, false, fromDateStr, toDateStr, params, offset, pageSize)
if err == nil {
pagedInfo = &model.PagedInfo{ pagedInfo = &model.PagedInfo{
TotalCount: dao.GetLastTotalRowCount(db), TotalCount: totalCount,
Data: orders, Data: orders,
} }
} }
dao.Commit(db)
return pagedInfo, err return pagedInfo, err
} }
func (c *OrderManager) ExportOrders(ctx *jxcontext.Context, fromDateStr, toDateStr string, mapParams map[string]interface{}) (hint string, err error) {
globals.SugarLogger.Debugf("ExportOrders from:%s to:%s", fromDateStr, toDateStr)
var (
orders, orders2 []*model.GoodsOrderExt
order *model.GoodsOrderExt
excelBin []byte
)
task := tasksch.NewSeqTask("导出订单SKU信息", ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
orders, _, err = c.getOrders(ctx, true, fromDateStr, toDateStr, mapParams, 0, -1)
case 1:
for _, v := range orders {
skuStr := strings.Join([]string{
utils.Int2Str(v.SkuID),
utils.Int2Str(v.SkuCount2),
utils.Int2Str(v.SkuShopPrice),
utils.Int2Str(v.SkuSalePrice),
}, ",")
if order == nil || v.ID != order.ID {
order = v
v.CourierVendorName = model.VendorChineseNames[v.WaybillVendorID]
v.Status2 = model.OrderStatusName[v.Status]
v.SkuInfo = skuStr
orders2 = append(orders2, v)
} else {
order.SkuInfo += ";" + skuStr
}
}
case 2:
excelConf := &excel.Obj2ExcelSheetConfig{
Title: "订单导出",
Data: orders2,
CaptionList: []string{
"vendorOrderID",
"vendorID",
"vendorStoreID",
"jxStoreID",
"storeName",
"salePrice",
"shopPrice",
"weight",
"consigneeName",
"consigneeMobile",
"consigneeMobile2",
"consigneeAddress",
"skuCount",
"status",
"orderSeq",
"buyerComment",
"businessType",
"expectedDeliveredTime",
"vendorWaybillID",
"waybillVendorID",
"orderCreatedAt",
"orderFinishedAt",
"courierVendorName",
"courierName",
"courierMobile",
"courierMobile",
"desiredFee",
"waybillCreatedAt",
"waybillFinishedAt",
"status2",
"skuInfo",
},
}
excelBin = excel.Obj2Excel([]*excel.Obj2ExcelSheetConfig{excelConf})
case 3:
keyPart := []string{
ctx.GetUserName(),
}
if fromDateStr != "" {
keyPart = append(keyPart, fromDateStr)
}
if toDateStr != "" {
keyPart = append(keyPart, toDateStr)
}
keyPart = append(keyPart, time.Now().Format("20060102T150405")+".xlsx")
key := "export/" + strings.Join(keyPart, "_")
excelURL, err2 := jxutils.UploadExportContent(excelBin, key)
if err = err2; err == nil {
task.SetNoticeMsg(excelURL)
}
globals.SugarLogger.Debugf("导出订单SKU信息excelURL:%s, err:%v", excelURL, err)
}
return nil, err
}, 4)
tasksch.ManageTask(task).Run()
hint = task.GetID()
return hint, err
}
func (c *OrderManager) GetWaybills(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { func (c *OrderManager) GetWaybills(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
globals.SugarLogger.Debugf("GetWaybills from:%s to:%s", fromDateStr, toDateStr) globals.SugarLogger.Debugf("GetWaybills from:%s to:%s", fromDateStr, toDateStr)
@@ -472,16 +644,14 @@ func (c *OrderManager) GetOrderStatusList(ctx *jxcontext.Context, vendorOrderID
sql := ` sql := `
SELECT * SELECT *
FROM order_status t1 FROM order_status t1
WHERE 1 = 1 WHERE t1.ref_vendor_order_id = ? AND t1.ref_vendor_id = ?
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
vendorOrderID, vendorOrderID,
vendorID, vendorID,
} }
if orderType == -1 { if orderType > 0 {
sql += " AND t1.ref_vendor_order_id = ? AND t1.ref_vendor_id = ?" sql += " AND t1.order_type = ?"
} else {
sql += " AND t1.vendor_order_id = ? AND t1.vendor_id = ? AND t1.order_type = ?"
sqlParams = append(sqlParams, orderType) sqlParams = append(sqlParams, orderType)
} }
sql += " ORDER BY t1.status_time, t1.order_type DESC" sql += " ORDER BY t1.status_time, t1.order_type DESC"
@@ -604,3 +774,184 @@ func (c *OrderManager) GetOrdersFinancial(ctx *jxcontext.Context, fromDateStr, t
return pagedInfo, err return pagedInfo, err
} }
func (c *OrderManager) GetStoresOrderSaleInfo(ctx *jxcontext.Context, storeIDList []int, fromTime time.Time, toTime time.Time, statusList []int) (saleInfoList []*StoresOrderSaleInfo, err error) {
if toTime.Sub(fromTime) > time.Hour*24*60 {
return nil, fmt.Errorf("查询时间范围不能超过60天")
}
// 用int64类型去取float型的数据库返回值会取不到
sql := `
SELECT IF(t1.jx_store_id > 0, t1.jx_store_id, t1.store_id) store_id, t1.vendor_id, IF(t1.status < ?, 0, t1.status) status,
COUNT(*) count, SUM(t1.shop_price) shop_price, SUM(t1.vendor_price) vendor_price, SUM(t1.sale_price) sale_price, SUM(t1.actual_pay_price) actual_pay_price,
CAST(SUM(IF(t1.shop_price <= t1.vendor_price, IF(t1.shop_price = 0, t1.sale_price, t1.shop_price), IF(t1.vendor_price = 0, t1.sale_price, t1.vendor_price))*IF(t5.pay_percentage IS NULL OR t5.pay_percentage <= 0, 70, t5.pay_percentage)/100) AS SIGNED) earning_price
FROM goods_order t1
LEFT JOIN store t5 ON t5.id = IF(t1.jx_store_id <> 0, t1.jx_store_id, t1.store_id)
WHERE t1.order_created_at >= ? AND t1.order_created_at <= ?
`
sqlParams := []interface{}{
model.OrderStatusEndBegin,
fromTime,
toTime,
}
if len(storeIDList) > 0 {
sql += " AND IF(t1.jx_store_id > 0, t1.jx_store_id, t1.store_id) IN (" + dao.GenQuestionMarks(len(storeIDList)) + ")"
sqlParams = append(sqlParams, storeIDList)
}
if len(statusList) > 0 {
sql += " AND t1.status IN (" + dao.GenQuestionMarks(len(statusList)) + ")"
sqlParams = append(sqlParams, statusList)
}
sql += `
GROUP BY 1,2,3
ORDER BY 1,2,3`
err = dao.GetRows(dao.GetDB(), &saleInfoList, sql, sqlParams...)
return saleInfoList, err
}
func (c *OrderManager) GetAfsOrders(ctx *jxcontext.Context, keyword, afsOrderID, vendorOrderID string, vendorIDList, appealTypeList, storeIDList, statusList []int, fromTime, toTime time.Time, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
globals.SugarLogger.Debugf("GetAfsOrders")
pageSize = jxutils.FormalizePageSize(pageSize)
if offset < 0 {
offset = 0
}
sql := `
SELECT SQL_CALC_FOUND_ROWS t1.*
FROM afs_order t1
`
var (
sqlWhere string
sqlParams []interface{}
)
// 如果搜索关键字可能为订单或售后单号,则当成订单或售后单查询
if keyword != "" {
if jxutils.GetPossibleVendorIDFromAfsOrderID(keyword) > model.VendorIDUnknown && afsOrderID == "" {
afsOrderID = keyword
keyword = ""
} else if jxutils.GetPossibleVendorIDFromVendorOrderID(keyword) > model.VendorIDUnknown && vendorOrderID == "" {
vendorOrderID = keyword
keyword = ""
}
}
if vendorOrderID != "" || afsOrderID != "" {
if vendorOrderID != "" {
sqlWhere = " WHERE (t1.vendor_order_id = ? OR t1.vendor_order_id2 = ?)"
sqlParams = []interface{}{
vendorOrderID,
vendorOrderID,
}
} else {
sqlWhere = " WHERE (t1.afs_order_id = ?)"
sqlParams = []interface{}{
afsOrderID,
}
}
} else {
if toTime.Sub(fromTime) > 24*time.Hour*60 {
return nil, fmt.Errorf("售后单查询时间不能超过60天")
}
sqlWhere = `
WHERE t1.afs_created_at >= ? AND t1.afs_created_at <= ?
`
sqlParams = []interface{}{
fromTime,
toTime,
}
if keyword != "" {
keywordLike := "%" + keyword + "%"
sqlWhere += `
AND (t1.vendor_order_id2 LIKE ? OR t1.vendor_order_id LIKE ? OR t1.afs_order_id LIKE ?
OR t1.vendor_store_id LIKE ? OR t1.reason_desc LIKE ?
`
sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike)
if keywordInt64 := utils.Str2Int64WithDefault(keyword, 0); keywordInt64 > 0 {
sqlWhere += " OR t1.store_id = ? OR t1.jx_store_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64)
}
sqlWhere += ")"
}
if len(storeIDList) > 0 {
sqlWhere += " AND IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) IN (" + dao.GenQuestionMarks(len(storeIDList)) + ")"
sqlParams = append(sqlParams, storeIDList)
}
if len(statusList) > 0 {
sqlWhere += " AND t1.status IN (" + dao.GenQuestionMarks(len(statusList)) + ")"
sqlParams = append(sqlParams, statusList)
}
if len(appealTypeList) > 0 {
sqlWhere += " AND t1.appeal_type IN (" + dao.GenQuestionMarks(len(appealTypeList)) + ")"
sqlParams = append(sqlParams, appealTypeList)
}
}
if len(vendorIDList) > 0 {
sqlWhere += " AND t1.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDList)) + ")"
sqlParams = append(sqlParams, vendorIDList)
}
sql += sqlWhere
sql += `
ORDER BY t1.afs_created_at DESC
LIMIT ? OFFSET ?
`
sqlParams = append(sqlParams, pageSize, offset)
var orders []*model.AfsOrder
db := dao.GetDB()
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
if err = dao.GetRows(db, &orders, sql, sqlParams...); err == nil {
pagedInfo = &model.PagedInfo{
TotalCount: dao.GetLastTotalRowCount(db),
Data: orders,
}
dao.Commit(db)
}
return pagedInfo, err
}
func (c *OrderManager) GetAfsOrderSkuInfo(ctx *jxcontext.Context, afsOrderID string, vendorID int) (skus []*model.OrderFinancialSkuExt, err error) {
sql := `
SELECT t1.*, t3.img image
FROM order_sku_financial t1
JOIN sku t2 ON t2.id = IF(t1.jx_sku_id <> 0, t1.jx_sku_id, t1.sku_id)
JOIN sku_name t3 ON t3.id = t2.name_id
WHERE t1.afs_order_id = ? AND t1.vendor_id = ?
`
sqlParams := []interface{}{
afsOrderID,
vendorID,
}
err = dao.GetRows(dao.GetDB(), &skus, sql, sqlParams...)
return skus, err
}
func (c *OrderManager) GetStoreAfsOrderCountInfo(ctx *jxcontext.Context, storeID, lastHours int) (countInfo []*model.GoodsOrderCountInfo, err error) {
globals.SugarLogger.Debugf("GetStoreAfsOrderCountInfo storeID:%d", storeID)
if lastHours > maxLastHours {
lastHours = maxLastHours
} else if lastHours == 0 {
lastHours = defLastHours
}
db := dao.GetDB()
err = dao.GetRows(db, &countInfo, `
SELECT 0 lock_status, t1.status, COUNT(*) count
FROM afs_order t1
WHERE t1.vendor_id <> 2 AND IF(t1.vendor_id = ?, t1.store_id, IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) ) = ?
AND t1.afs_created_at >= ?
GROUP BY 1,2
ORDER BY 1,2
`, model.VendorIDWSC, storeID, time.Now().Add(-time.Duration(lastHours)*time.Hour))
if err == nil {
return countInfo, nil
}
globals.SugarLogger.Infof("GetStoreAfsOrderCountInfo storeID:%d failed with error:%v", storeID, err)
return nil, err
}

View File

@@ -13,10 +13,10 @@ import (
var ( var (
waybillOrderStatusMap = map[int]int{ waybillOrderStatusMap = map[int]int{
model.WaybillStatusApplyFailedGetGoods: model.WaybillStatusApplyFailedGetGoods, model.WaybillStatusApplyFailedGetGoods: model.OrderStatusApplyFailedGetGoods,
model.WaybillStatusAgreeFailedGetGoods: model.WaybillStatusAgreeFailedGetGoods, model.WaybillStatusAgreeFailedGetGoods: model.OrderStatusAgreeFailedGetGoods,
model.WaybillStatusRefuseFailedGetGoods: model.WaybillStatusRefuseFailedGetGoods, model.WaybillStatusRefuseFailedGetGoods: model.OrderStatusRefuseFailedGetGoods,
model.WaybillStatusDeliverFailed: model.WaybillStatusDeliverFailed, model.WaybillStatusDeliverFailed: model.OrderStatusDeliverFailed,
} }
) )
@@ -92,14 +92,49 @@ func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
panic(r) panic(r)
} }
}() }()
duplicatedCount := 0
if bill.Status == model.WaybillStatusNew { if bill.Status == model.WaybillStatusNew {
isDuplicated, err = w.onWaybillNew(bill, db) isDuplicated, err = w.onWaybillNew(bill, db)
if isDuplicated {
duplicatedCount = 1
}
} else { } else {
if bill.Status == model.WaybillStatusAccepted { // 处理美团配送丢失新运单消息的情况
existingBill, err2 := w.LoadWaybill(bill.VendorWaybillID, bill.WaybillVendorID)
if err2 != nil {
if dao.IsNoRowsError(err2) || err2 == ErrCanNotFindWaybill {
if isDuplicated, err = w.onWaybillNew(bill, db); err != nil {
dao.Rollback(db)
return err
}
existingBill = bill
billCopy := *bill
billCopy.Status = model.WaybillStatusNew
dao.Commit(db)
// 进运单调度器OnWaybillStatusChanged之前要确保事务是提交了的否则会导致死锁
scheduler.CurrentScheduler.OnWaybillStatusChanged(&billCopy, false)
dao.Begin(db)
} else {
dao.Rollback(db)
return err2
}
}
// 运单消息错序,之前已经结束了,直接返回
if existingBill.Status >= model.WaybillStatusEndBegin {
dao.Commit(db)
return nil
}
}
addParams := orm.Params{} addParams := orm.Params{}
if bill.Status >= model.WaybillStatusAccepted && bill.Status < model.WaybillStatusEndBegin { if bill.Status >= model.WaybillStatusAccepted && bill.Status < model.WaybillStatusEndBegin {
if bill.Status == model.WaybillStatusAccepted { if bill.Status == model.WaybillStatusAccepted {
if bill.DesiredFee > 0 {
addParams["desired_fee"] = bill.DesiredFee addParams["desired_fee"] = bill.DesiredFee
} }
if bill.ActualFee > 0 {
addParams["actual_fee"] = bill.ActualFee
}
}
if bill.CourierMobile != "" { if bill.CourierMobile != "" {
addParams["courier_name"] = bill.CourierName addParams["courier_name"] = bill.CourierName
addParams["courier_mobile"] = bill.CourierMobile addParams["courier_mobile"] = bill.CourierMobile
@@ -107,11 +142,15 @@ func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
} else if bill.Status >= model.WaybillStatusEndBegin { } else if bill.Status >= model.WaybillStatusEndBegin {
addParams["waybill_finished_at"] = bill.StatusTime addParams["waybill_finished_at"] = bill.StatusTime
} }
isDuplicated, err = w.addWaybillStatus(bill, db, addParams) duplicatedCount, err = w.addWaybillStatus(bill, db, addParams)
if err != nil {
dao.Rollback(db)
return err
}
} }
if err == nil { if err == nil {
dao.Commit(db) dao.Commit(db)
if !isDuplicated { if duplicatedCount == 0 {
scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, false) scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, false)
} }
} else { } else {
@@ -136,10 +175,11 @@ func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
return err return err
} }
func (w *OrderManager) addWaybillStatus(bill *model.Waybill, db *dao.DaoDB, addParams orm.Params) (isDuplicated bool, err error) { func (w *OrderManager) addWaybillStatus(bill *model.Waybill, db *dao.DaoDB, addParams orm.Params) (duplicatedCount int, err error) {
waybillStatus := model.Waybill2Status(bill) waybillStatus := model.Waybill2Status(bill)
isDuplicated, err = addOrderOrWaybillStatus(waybillStatus, db) isDuplicated, err := addOrderOrWaybillStatus(waybillStatus, db)
if err == nil && !isDuplicated && waybillStatus.Status > model.WaybillStatusUnknown { // todo 这里应该和addOrderStatus一样的改法状态不能回绕 if err == nil && !isDuplicated {
if waybillStatus.Status > model.WaybillStatusUnknown { // todo 这里应该和addOrderStatus一样的改法状态不能回绕
params := utils.MergeMaps(orm.Params{ params := utils.MergeMaps(orm.Params{
"status": bill.Status, "status": bill.Status,
"vendor_status": bill.VendorStatus, "vendor_status": bill.VendorStatus,
@@ -149,8 +189,13 @@ func (w *OrderManager) addWaybillStatus(bill *model.Waybill, db *dao.DaoDB, addP
_, err = db.Db.QueryTable("waybill").Filter("vendor_waybill_id", bill.VendorWaybillID).Filter("waybill_vendor_id", bill.WaybillVendorID).Filter("status__lte", bill.Status).Update(params) _, err = db.Db.QueryTable("waybill").Filter("vendor_waybill_id", bill.VendorWaybillID).Filter("waybill_vendor_id", bill.WaybillVendorID).Filter("status__lte", bill.Status).Update(params)
return err return err
}, "addWaybillStatus update waybill status, bill:%v", bill) }, "addWaybillStatus update waybill status, bill:%v", bill)
} else {
duplicatedCount = -1
} }
return isDuplicated, err } else {
duplicatedCount = 1
}
return duplicatedCount, err
} }
func (c *OrderManager) LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error) { func (c *OrderManager) LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error) {
@@ -160,6 +205,7 @@ func (c *OrderManager) LoadWaybill(vendorWaybillID string, waybillVendorID int)
WaybillVendorID: waybillVendorID, WaybillVendorID: waybillVendorID,
} }
if err = db.Read(bill, "VendorWaybillID", "WaybillVendorID"); err != nil { if err = db.Read(bill, "VendorWaybillID", "WaybillVendorID"); err != nil {
bill = nil
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
err = ErrCanNotFindWaybill err = ErrCanNotFindWaybill
} }

View File

@@ -5,6 +5,7 @@ import (
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler" "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
) )
@@ -54,16 +55,16 @@ func (c *BaseScheduler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool
func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s", order.VendorOrderID) globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusFinishedPickup { if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status >= model.OrderStatusFinishedPickup && order.Status <= model.OrderStatusDelivering {
if c.IsReallyCallPlatformAPI { if order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 && c.IsReallyCallPlatformAPI {
err = utils.CallFuncLogErrorWithInfo(func() error { err = utils.CallFuncLogErrorWithInfo(func() error {
return partner.GetPurchasePlatformFromVendorID(order.VendorID).Swtich2SelfDeliver(order, userName) return partner.GetPurchasePlatformFromVendorID(order.VendorID).Swtich2SelfDeliver(order, userName)
}, "Swtich2SelfDeliver orderID:%s", order.VendorOrderID) }, "Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
}
if err == nil { // 因为有些平台转自送后,不会再发送订单在配送中消息过来,所以成功后就强制设置状态为配送中 if err == nil { // 因为有些平台转自送后,不会再发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
order.Status = model.OrderStatusDelivering order.Status = model.OrderStatusDelivering
order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order) err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
} }
} else { } else {
if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusFinishedPickup || order.VendorID == order.WaybillVendorID { if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusFinishedPickup || order.VendorID == order.WaybillVendorID {
@@ -106,7 +107,7 @@ func (c *BaseScheduler) SelfDeliverDelivering(order *model.GoodsOrder, userName
}, "SelfDeliverDelivering orderID:%s", order.VendorOrderID) }, "SelfDeliverDelivering orderID:%s", order.VendorOrderID)
if err == nil { // 因为有些平台设置配送中后,不会发送订单在配送中消息过来,所以成功后就强制设置状态为配送中 if err == nil { // 因为有些平台设置配送中后,不会发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
order.Status = model.OrderStatusDelivering order.Status = model.OrderStatusDelivering
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order) err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
} }
} }
} else { } else {
@@ -147,17 +148,29 @@ func (c *BaseScheduler) CreateWaybill(platformVendorID int, order *model.GoodsOr
globals.SugarLogger.Warnf("CreateWaybill orderID:%s, vendorID:%d is not solid!!!", order.VendorOrderID, platformVendorID) globals.SugarLogger.Warnf("CreateWaybill orderID:%s, vendorID:%d is not solid!!!", order.VendorOrderID, platformVendorID)
return nil, scheduler.ErrOrderIsNotSolid return nil, scheduler.ErrOrderIsNotSolid
} }
if c.IsReallyCallPlatformAPI { // if order.DeliveryFlag&model.OrderDeliveryFlagMaskScheduleDisabled != 0 {
// waybillList, err := partner.CurOrderManager.GetOrderWaybillInfo(jxcontext.AdminCtx, order.VendorOrderID, order.VendorID, true)
// if err != nil {
// return nil, err
// }
// if len(waybillList) > 0 {
// return nil, fmt.Errorf("转商家自送的订单只允许有一个有效运单,当前已经有%s运单", jxutils.GetVendorName(waybillList[0].WaybillVendorID))
// }
// }
handlerInfo := partner.GetDeliveryPlatformFromVendorID(platformVendorID) handlerInfo := partner.GetDeliveryPlatformFromVendorID(platformVendorID)
if handlerInfo != nil && handlerInfo.Use4CreateWaybill { if handlerInfo != nil && handlerInfo.Use4CreateWaybill {
if c.IsReallyCallPlatformAPI {
bill, err = handlerInfo.Handler.CreateWaybill(order, policy) bill, err = handlerInfo.Handler.CreateWaybill(order, policy)
if err != nil { if err != nil {
globals.SugarLogger.Infof("CreateWaybill failed orderID:%s vendorID:%d with error:%v", order.VendorOrderID, platformVendorID, err) globals.SugarLogger.Infof("CreateWaybill failed orderID:%s vendorID:%d with error:%v", order.VendorOrderID, platformVendorID, err)
} else {
order.DeliveryFlag |= model.WaybillVendorID2Mask(platformVendorID)
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
} }
} else { } else {
err = scheduler.ErrDeliverProviderWrong err = scheduler.ErrDeliverProviderWrong
} }
}
return bill, err return bill, err
} }
@@ -170,6 +183,8 @@ func (c *BaseScheduler) CancelWaybill(bill *model.Waybill, cancelReasonID int, c
return handlerInfo.Handler.CancelWaybill(bill, cancelReasonID, cancelReason) return handlerInfo.Handler.CancelWaybill(bill, cancelReasonID, cancelReason)
}, "CancelWaybill bill:%v", bill); err == nil { }, "CancelWaybill bill:%v", bill); err == nil {
bill.Status = model.WaybillStatusCanceled bill.Status = model.WaybillStatusCanceled
bill.DeliveryFlag |= model.WaybillDeliveryFlagMaskActiveCancel
_, err = dao.UpdateEntity(nil, bill, "Status", "DeliveryFlag")
} }
globals.SugarLogger.Debugf("CancelWaybill bill:%v canceled by myself", bill) globals.SugarLogger.Debugf("CancelWaybill bill:%v canceled by myself", bill)
} }

View File

@@ -13,28 +13,41 @@ import (
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
) )
func (c *BaseScheduler) CreateWaybillOnProviders(ctx *jxcontext.Context, order *model.GoodsOrder, policyHandler partner.CreateWaybillPolicy) (bills []*model.Waybill, err error) { func (c *BaseScheduler) CreateWaybillOnProviders(ctx *jxcontext.Context, order *model.GoodsOrder, courierVendorIDs []int, policyHandler partner.CreateWaybillPolicy, createOnlyOne bool) (bills []*model.Waybill, err error) {
userName := ctx.GetUserName() userName := ctx.GetUserName()
globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s", order.VendorOrderID, userName) globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s", order.VendorOrderID, userName)
storeCourierList, err := dao.GetStoreCourierList(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), model.StoreStatusOpened) storeCourierList, err := dao.GetStoreCourierList(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), model.StoreStatusOpened)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var courierVendorIDMap map[int]bool
if len(courierVendorIDs) > 0 {
courierVendorIDMap = make(map[int]bool)
for _, courierVendorID := range courierVendorIDs {
courierVendorIDMap[courierVendorID] = true
}
}
var errList []string var errList []string
for _, storeCourier := range storeCourierList { for _, storeCourier := range storeCourierList {
if courierVendorIDMap == nil || courierVendorIDMap[storeCourier.VendorID] {
if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil && handler.Use4CreateWaybill {
courierVendorID := storeCourier.VendorID courierVendorID := storeCourier.VendorID
if order.VendorID != model.VendorIDWSC || courierVendorID != model.VendorIDDada { // 达达作为微商城的自有配送,不参与配送竞争 if order.VendorID != model.VendorIDWSC || courierVendorID != model.VendorIDDada { // 达达作为微商城的自有配送,不参与配送竞争
bill, err2 := c.CreateWaybill(courierVendorID, order, policyHandler) bill, err2 := c.CreateWaybill(courierVendorID, order, policyHandler)
if err = err2; err == nil { if err = err2; err == nil {
globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d bill:%v", order.VendorOrderID, userName, courierVendorID, bill) globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d bill:%v", order.VendorOrderID, userName, courierVendorID, bill)
bills = append(bills, bill) bills = append(bills, bill)
if createOnlyOne {
break
}
} else { } else {
globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d failed with error:%v", order.VendorOrderID, userName, courierVendorID, err) globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d failed with error:%v", order.VendorOrderID, userName, courierVendorID, err)
errList = append(errList, fmt.Sprintf("平台:%s,%s", jxutils.GetVendorName(courierVendorID), err.Error())) errList = append(errList, fmt.Sprintf("平台:%s,%s", jxutils.GetVendorName(courierVendorID), err.Error()))
} }
} }
} }
}
}
if len(bills) > 0 { if len(bills) > 0 {
err = nil err = nil
} else if len(errList) == 0 { } else if len(errList) == 0 {
@@ -58,8 +71,8 @@ func (c *BaseScheduler) SelfDeliveredAndUpdateStatus(ctx *jxcontext.Context, ven
err = c.Swtich2SelfDelivered(order, userName) err = c.Swtich2SelfDelivered(order, userName)
} }
if err == nil { if err == nil {
order.Status = model.OrderStatusFinished // order.Status = model.OrderStatusFinished // todo 是否需要强制设置完成状态?
if err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order); err == nil { if err = dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskSetDelivered); err == nil {
globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName) globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
return err return err
} }
@@ -81,7 +94,7 @@ func (c *BaseScheduler) PickupGoodsAndUpdateStatus(ctx *jxcontext.Context, vendo
err = c.PickupGoods(order, c.GetStoreDeliveryType(order, nil) == scheduler.StoreDeliveryTypeByStore, userName) err = c.PickupGoods(order, c.GetStoreDeliveryType(order, nil) == scheduler.StoreDeliveryTypeByStore, userName)
if err == nil { if err == nil {
order.Status = model.OrderStatusFinishedPickup order.Status = model.OrderStatusFinishedPickup
if err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order); err == nil { if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName) globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
return err return err
} }
@@ -134,7 +147,7 @@ func (c *BaseScheduler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, ord
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AcceptOrRefuseFailedGetOrder(ctx, order, isAcceptIt) err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AcceptOrRefuseFailedGetOrder(ctx, order, isAcceptIt)
} }
if err == nil { if err == nil {
flag := int8(model.OrderFlagAgreeFailedGetGoods) flag := model.OrderFlagAgreeFailedGetGoods
if !isAcceptIt { if !isAcceptIt {
flag = model.OrderFlagRefuseFailedGetGoods flag = model.OrderFlagRefuseFailedGetGoods
} }
@@ -168,7 +181,7 @@ func (c *BaseScheduler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AgreeOrRefuseCancel(ctx, order, isAcceptIt, reason) err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AgreeOrRefuseCancel(ctx, order, isAcceptIt, reason)
} }
if err == nil { if err == nil {
flag := int8(model.OrderFlagAgreeUserApplyCancel) flag := model.OrderFlagAgreeUserApplyCancel
if !isAcceptIt { if !isAcceptIt {
flag = model.OrderFlagRefuseUserApplyCancel flag = model.OrderFlagRefuseUserApplyCancel
} }
@@ -176,3 +189,43 @@ func (c *BaseScheduler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model
} }
return err return err
} }
func (c *BaseScheduler) CancelWaybillByID(ctx *jxcontext.Context, vendorWaybillID string, waybillVendorID int, cancelReasonID int, cancelReason string) (err error) {
bill, err := partner.CurOrderManager.LoadWaybill(vendorWaybillID, waybillVendorID)
if err == nil {
err = c.CancelWaybill(bill, cancelReasonID, cancelReason)
}
return err
}
func (c *BaseScheduler) AgreeOrRefuseRefund(ctx *jxcontext.Context, afsOrderID string, vendorID, approveType int, reason string) (err error) {
afsOrder, err := partner.CurOrderManager.LoadAfsOrder(afsOrderID, vendorID)
if err == nil {
if c.IsReallyCallPlatformAPI {
err = partner.GetPurchasePlatformFromVendorID(vendorID).AgreeOrRefuseRefund(ctx, afsOrder, approveType, reason)
}
if err == nil {
flag := model.AfsOrderFlagAgreeUserRefund
if approveType == partner.AfsApproveTypeRefused {
flag = model.AfsOrderFlagRefuseUserRefund
afsOrder.RefuseReason = reason
partner.CurOrderManager.UpdateAfsOrderFields(afsOrder, []string{"RefuseReason"})
}
dao.SetAfsOrderFlag(dao.GetDB(), ctx.GetUserName(), afsOrderID, vendorID, flag)
}
}
return err
}
func (c *BaseScheduler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, afsOrderID string, vendorID int) (err error) {
afsOrder, err := partner.CurOrderManager.LoadAfsOrder(afsOrderID, vendorID)
if err == nil {
if c.IsReallyCallPlatformAPI {
err = partner.GetPurchasePlatformFromVendorID(vendorID).ConfirmReceivedReturnGoods(ctx, afsOrder)
}
if err == nil {
dao.SetAfsOrderFlag(dao.GetDB(), ctx.GetUserName(), afsOrderID, vendorID, model.AfsOrderFlagMaskReturnGoods)
}
}
return err
}

View File

@@ -27,6 +27,7 @@ import (
const ( const (
time2Delivered = 1 * time.Hour // 正常从下单到送达的时间。 time2Delivered = 1 * time.Hour // 正常从下单到送达的时间。
minute2Schedule3rdCarrier = 20 // 收到平台方自有配送的新运单消息后等待创建三方配送运单的时间分钟如果是定时达会再根据ExpectedDeliveredTime与dingShiDaAheadTime做调整 minute2Schedule3rdCarrier = 20 // 收到平台方自有配送的新运单消息后等待创建三方配送运单的时间分钟如果是定时达会再根据ExpectedDeliveredTime与dingShiDaAheadTime做调整
minute2Schedule3rdCarrier4Ebai = 30 // 饿百的最少转自配送需要的时间(分钟)
minMinute2Schedule3rdCarrier = 5 // 转三方配送最少等待时间(分钟) minMinute2Schedule3rdCarrier = 5 // 转三方配送最少等待时间(分钟)
time2AutoPickupMin = 15 * time.Minute // 自动拣货等待时间这个只有在没有PickDeadline信息才有用否则会根据PickDeadline设置 time2AutoPickupMin = 15 * time.Minute // 自动拣货等待时间这个只有在没有PickDeadline信息才有用否则会根据PickDeadline设置
@@ -64,13 +65,22 @@ type WatchOrderInfo struct {
timerStatusType int // 0表示订单1表示运单 timerStatusType int // 0表示订单1表示运单
timerStatus int timerStatus int
timer *time.Timer timer *time.Timer
timerTime time.Time
retryCount int // 失败后尝试的次数,调试阶段可能出现死循化,阻止这种情况发生 retryCount int // 失败后尝试的次数,调试阶段可能出现死循化,阻止这种情况发生
} }
type StatusActionConfig struct { type StatusActionConfig struct {
partner.StatusActionParams partner.StatusActionParams
TimeoutAction func(savedOrderInfo *WatchOrderInfo) (err error) // 超时后需要执行的动作为nil表示此状态不需要执行监控 nil在GetStatusActionConfig返回时表示不修改缺省 TimeoutAction func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) // 超时后需要执行的动作为nil表示此状态不需要执行监控 nil在GetStatusActionConfig返回时表示不修改缺省
ShouldSetTimer func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool
}
func (c *StatusActionConfig) CallShouldSetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
if c.ShouldSetTimer != nil {
return c.ShouldSetTimer(savedOrderInfo, bill)
}
return true
} }
// 重要:此调度器要求同一定单的处理逻辑必须是序列化了的,不然会有并发问题 // 重要:此调度器要求同一定单的处理逻辑必须是序列化了的,不然会有并发问题
@@ -114,11 +124,6 @@ func (s *WatchOrderInfo) updateOrderStoreFeature(order *model.GoodsOrder) (err e
s.autoPickupTimeoutMinute = int(storeMap.AutoPickup) s.autoPickupTimeoutMinute = int(storeMap.AutoPickup)
s.storeDeliveryType = FixedScheduler.GetStoreDeliveryType(order, storeMap) s.storeDeliveryType = FixedScheduler.GetStoreDeliveryType(order, storeMap)
globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s, s.storeDeliveryType:%d", order.VendorOrderID, s.storeDeliveryType) globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s, s.storeDeliveryType:%d", order.VendorOrderID, s.storeDeliveryType)
// if s.storeDeliveryType == scheduler.StoreDeliveryTypeByStore && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0 {
// order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
// err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
// }
// globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s, s.storeDeliveryType:%d, order.DeliveryFlag:%d", order.VendorOrderID, s.storeDeliveryType, order.DeliveryFlag)
} }
return err return err
} }
@@ -136,7 +141,7 @@ func init() {
TimerType: partner.TimerTypeBaseStatusTime, TimerType: partner.TimerTypeBaseStatusTime,
Timeout: 10 * time.Millisecond, Timeout: 10 * time.Millisecond,
}, },
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) { TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
order := savedOrderInfo.order order := savedOrderInfo.order
mobile := order.ConsigneeMobile mobile := order.ConsigneeMobile
if order.ConsigneeMobile2 != "" { if order.ConsigneeMobile2 != "" {
@@ -144,10 +149,14 @@ func init() {
} }
_ = sch.handleAutoAcceptOrder(order.VendorOrderID, order.VendorID, mobile, jxutils.GetSaleStoreIDFromOrder(order), nil, func(isAcceptIt bool) error { _ = sch.handleAutoAcceptOrder(order.VendorOrderID, order.VendorID, mobile, jxutils.GetSaleStoreIDFromOrder(order), nil, func(isAcceptIt bool) error {
if err = sch.AcceptOrRefuseOrder(order, isAcceptIt, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation { if err = sch.AcceptOrRefuseOrder(order, isAcceptIt, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation {
partner.CurOrderManager.OnOrderMsg(order, "自动接单失败", err.Error())
// 为了解决京东新消息与接单消息乱序的问题 // 为了解决京东新消息与接单消息乱序的问题
if errWithCode, ok := err.(*utils.ErrorWithCode); ok && errWithCode.Level() == 1 && errWithCode.IntCode() == -1 { if errWithCode, ok := err.(*utils.ErrorWithCode); ok && errWithCode.Level() == 1 && errWithCode.IntCode() == -1 {
if order2, err2 := partner.GetPurchasePlatformFromVendorID(order.VendorID).GetOrder(order.VendorOrderID); err2 == nil && order2.Status > order.Status { if order2, err2 := partner.GetPurchasePlatformFromVendorID(order.VendorID).GetOrder(order.VendorOrderID); err2 == nil && order2.Status > order.Status {
order.Status = order2.Status
jxutils.CallMsgHandlerAsync(func() {
sch.OnOrderStatusChanged(order, model.Order2Status(order2), false) sch.OnOrderStatusChanged(order, model.Order2Status(order2), false)
}, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
err = nil err = nil
} else { } else {
err = err2 err = err2
@@ -165,6 +174,9 @@ func init() {
}) })
return nil return nil
}, },
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.order.Status == model.OrderStatusNew
},
}, },
model.OrderStatusAccepted: &StatusActionConfig{ // 自动拣货 model.OrderStatusAccepted: &StatusActionConfig{ // 自动拣货
StatusActionParams: partner.StatusActionParams{ StatusActionParams: partner.StatusActionParams{
@@ -172,12 +184,17 @@ func init() {
Timeout: time2AutoPickupMin, Timeout: time2AutoPickupMin,
TimeoutGap: second2AutoPickupGap, TimeoutGap: second2AutoPickupGap,
}, },
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) { TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
if savedOrderInfo.autoPickupTimeoutMinute > 0 { if savedOrderInfo.autoPickupTimeoutMinute > 0 {
return sch.autoPickupGood(savedOrderInfo) if err = sch.autoPickupGood(savedOrderInfo); err != nil {
partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "自动拣货失败", err.Error())
}
} }
return nil return nil
}, },
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.autoPickupTimeoutMinute > 0
},
}, },
model.OrderStatusFinishedPickup: &StatusActionConfig{ model.OrderStatusFinishedPickup: &StatusActionConfig{
StatusActionParams: partner.StatusActionParams{ StatusActionParams: partner.StatusActionParams{
@@ -185,28 +202,63 @@ func init() {
Timeout: 1 * time.Second, Timeout: 1 * time.Second,
TimeoutGap: 0, TimeoutGap: 0,
}, },
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) { TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore { // 自配送商家使用 if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore { // 自配送商家使用
return sch.createWaybillOn3rdProviders(savedOrderInfo, nil) return sch.createWaybillOn3rdProviders(savedOrderInfo, nil)
} }
return nil return nil
}, },
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore
},
}, },
}, },
map[int]*StatusActionConfig{ map[int]*StatusActionConfig{
// todo 平台物流二次创建运单的话这个TIMER有问题
model.WaybillStatusNew: &StatusActionConfig{ model.WaybillStatusNew: &StatusActionConfig{
StatusActionParams: partner.StatusActionParams{ StatusActionParams: partner.StatusActionParams{
TimerType: partner.TimerTypeBaseStatusTime, TimerType: partner.TimerTypeBaseStatusTime,
Timeout: minute2Schedule3rdCarrier * time.Minute, Timeout: minute2Schedule3rdCarrier * time.Minute,
}, },
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) { TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
if savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore { // 非自配送商家使用 // 饿百转自送的时机不太清楚,暂时禁用超时转自送,在饿百运单取消时还是会自动创建
if savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore && savedOrderInfo.order.VendorID != model.VendorIDEBAI { // 非自配送商家使用
return sch.createWaybillOn3rdProviders(savedOrderInfo, nil) return sch.createWaybillOn3rdProviders(savedOrderInfo, nil)
} }
return nil return nil
}, },
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore &&
savedOrderInfo.order.VendorID == bill.WaybillVendorID &&
savedOrderInfo.order.VendorID != model.VendorIDEBAI
}, },
}, },
//*
model.WaybillStatusCanceled: &StatusActionConfig{
StatusActionParams: partner.StatusActionParams{
TimerType: partner.TimerTypeBaseNow,
Timeout: 5 * time.Second,
},
TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
order := savedOrderInfo.order
if (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) &&
savedOrderInfo.order.VendorID == bill.WaybillVendorID &&
savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore &&
order.VendorID == model.VendorIDEBAI { // 非自配送商家使用
return sch.createWaybillOn3rdProviders(savedOrderInfo, nil)
}
return nil
},
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
order := savedOrderInfo.order
return (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) &&
savedOrderInfo.order.VendorID == bill.WaybillVendorID &&
savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore &&
order.VendorID == model.VendorIDEBAI
},
},
//*/
},
} }
} }
@@ -243,10 +295,11 @@ func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *mod
globals.SugarLogger.Debugf("OnOrderStatusChanged orderID:%s %s, status:%v", status.VendorOrderID, model.OrderStatusName[status.Status], status) globals.SugarLogger.Debugf("OnOrderStatusChanged orderID:%s %s, status:%v", status.VendorOrderID, model.OrderStatusName[status.Status], status)
if order == nil { if order == nil {
globals.SugarLogger.Warnf("OnOrderStatusChanged order is nil, status:%s", utils.Format4Output(status, true)) globals.SugarLogger.Warnf("OnOrderStatusChanged order is nil, status:%s", utils.Format4Output(status, true))
} else if order.Status > model.OrderStatusUnknown && status.Status > model.OrderStatusUnknown && order.Status != status.Status {
globals.SugarLogger.Warnf("OnOrderStatusChanged strange order:%s, status:%s", utils.Format4Output(order, true), utils.Format4Output(status, true))
} }
savedOrderInfo := s.loadSavedOrderFromMap(status, false) savedOrderInfo := s.loadSavedOrderFromMap(status, false)
savedOrderInfo.SetOrder(order) savedOrderInfo.SetOrder(order)
// s.updateOrderByStatus(savedOrderInfo.order, status)
// if status.Status == model.OrderStatusNew { // if status.Status == model.OrderStatusNew {
// if !isPending { // if !isPending {
@@ -259,7 +312,7 @@ func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *mod
(order.LockStatus == model.OrderStatusUnknown && (status.Status > model.OrderStatusUnknown || status.Status == model.OrderStatusRefuseFailedGetGoods)) { // 只处理状态转换,一般消息不处理 (order.LockStatus == model.OrderStatusUnknown && (status.Status > model.OrderStatusUnknown || status.Status == model.OrderStatusRefuseFailedGetGoods)) { // 只处理状态转换,一般消息不处理
if status.Status == model.OrderStatusRefuseFailedGetGoods && order.Status != model.OrderStatusFinishedPickup && !model.IsOrderFinalStatus(order.Status) { if status.Status == model.OrderStatusRefuseFailedGetGoods && order.Status != model.OrderStatusFinishedPickup && !model.IsOrderFinalStatus(order.Status) {
order.Status = model.OrderStatusFinishedPickup order.Status = model.OrderStatusFinishedPickup
partner.CurOrderManager.UpdateOrderStatusAndFlag(order) partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
} }
s.resetTimer(savedOrderInfo, nil, isPending) s.resetTimer(savedOrderInfo, nil, isPending)
if status.Status >= model.OrderStatusDelivering { if status.Status >= model.OrderStatusDelivering {
@@ -271,12 +324,12 @@ func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *mod
if !(status.Status == model.OrderStatusCanceled) { // 订单取消时,取消所有运单 if !(status.Status == model.OrderStatusCanceled) { // 订单取消时,取消所有运单
curWaybill = savedOrderInfo.waybills[savedOrderInfo.order.WaybillVendorID] curWaybill = savedOrderInfo.waybills[savedOrderInfo.order.WaybillVendorID]
if status.Status == model.OrderStatusFinished { if status.Status == model.OrderStatusFinished {
if curWaybill != nil && curWaybill.WaybillVendorID != curWaybill.OrderVendorID { if curWaybill != nil && !model.IsWaybillPlatformOwn(curWaybill) {
globals.SugarLogger.Infof("OnOrderStatusChanged [运营2]订单orderID:%s可能被手动点击送达会对程序状态产生不利影响请通知门店不要这样操作", status.VendorOrderID) globals.SugarLogger.Infof("OnOrderStatusChanged [运营2]订单orderID:%s可能被手动点击送达会对程序状态产生不利影响请通知门店不要这样操作", status.VendorOrderID)
} }
} }
} }
s.cancelOtherWaybills(savedOrderInfo, curWaybill, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrOrderAlreadyFinished) s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, curWaybill, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrOrderAlreadyFinished)
if status.Status >= model.OrderStatusEndBegin { if status.Status >= model.OrderStatusEndBegin {
s.orderMap.Delete(jxutils.GetUniversalOrderIDFromOrderStatus(status)) s.orderMap.Delete(jxutils.GetUniversalOrderIDFromOrderStatus(status))
} }
@@ -285,12 +338,13 @@ func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *mod
if order.LockStatus != model.OrderStatusUnknown { if order.LockStatus != model.OrderStatusUnknown {
s.stopTimer(savedOrderInfo) s.stopTimer(savedOrderInfo)
} }
if model.IsOrderLockStatus(status.Status) || if !isPending {
model.IsOrderUnlockStatus(status.Status) || if status.Status == model.OrderStatusFinishedPickup {
status.Status == model.OrderStatusApplyFailedGetGoods || msghub.OnFinishedPickup(savedOrderInfo.order)
} else if status.Status == model.OrderStatusApplyCancel || //model.IsOrderLockStatus(status.Status) ||
status.Status == model.OrderStatusApplyFailedGetGoods || //model.IsOrderUnlockStatus(status.Status) ||
status.Status == model.OrderStatusAgreeFailedGetGoods || status.Status == model.OrderStatusAgreeFailedGetGoods ||
status.Status == model.OrderStatusDeliverFailed { status.Status == model.OrderStatusDeliverFailed {
if isPending {
if status.Status == model.OrderStatusApplyCancel { if status.Status == model.OrderStatusApplyCancel {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
weixinmsg.NotifyUserApplyCancel(savedOrderInfo.order, status.Remark) weixinmsg.NotifyUserApplyCancel(savedOrderInfo.order, status.Remark)
@@ -323,10 +377,10 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending boo
if !isPending { if !isPending {
if order.Status > model.OrderStatusEndBegin { if order.Status > model.OrderStatusEndBegin {
s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
} else if s.IsOrderHasWaybill(order) { } else if model.IsOrderHaveWaybill(order) {
globals.SugarLogger.Debugf("OnWaybillStatusChanged multiple waybill created, bill:%v", bill) globals.SugarLogger.Debugf("OnWaybillStatusChanged multiple waybill created, bill:%v", bill)
if s.IsOrderPlatformWaybill(bill) { // 是购物平台运单 if model.IsWaybillPlatformOwn(bill) { // 是购物平台运单
if order.VendorID != order.WaybillVendorID { // 既有运单不是购物平台运单 if !model.IsOrderHaveOwnWaybill(order) { // 既有运单不是购物平台运单
globals.SugarLogger.Infof("OnWaybillStatusChanged bill:%v purchase platform bill came later than others, strange!!!", bill) globals.SugarLogger.Infof("OnWaybillStatusChanged bill:%v purchase platform bill came later than others, strange!!!", bill)
oldBill := savedOrderInfo.waybills[order.WaybillVendorID] oldBill := savedOrderInfo.waybills[order.WaybillVendorID]
if oldBill != nil { if oldBill != nil {
@@ -335,124 +389,146 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending boo
globals.SugarLogger.Warnf("OnWaybillStatusChanged bill:%v, oldBill is null, strange!!!", bill) globals.SugarLogger.Warnf("OnWaybillStatusChanged bill:%v, oldBill is null, strange!!!", bill)
} }
} }
bill.WaybillVendorID = model.VendorIDUnknown s.updateOrderByBill(order, nil, false)
s.updateOrderByBill(order, bill, false)
} else { } else {
s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
} }
} }
flag2Clear := model.WaybillVendorID2Mask(bill.WaybillVendorID)
if order.DeliveryFlag&flag2Clear != 0 {
order.DeliveryFlag &= ^flag2Clear
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
} }
// 只有购物平台的新运单消息才会启动抢单TIMER // 只有购物平台的新运单消息才会启动抢单TIMER
if s.IsOrderPlatformWaybill(bill) { if model.IsWaybillPlatformOwn(bill) {
s.resetTimer(savedOrderInfo, bill, isPending) s.resetTimer(savedOrderInfo, bill, isPending)
} }
} else { } else {
isBillExist := s.updateBillsInfo(savedOrderInfo, bill) isBillExist := s.updateBillsInfo(savedOrderInfo, bill)
if !isBillExist { if !isBillExist {
// s.addWaybill2Map(savedOrderInfo, bill) // updateBillsInfo中会添加
globals.SugarLogger.Debugf("OnWaybillStatusChanged bill not exist! orderID:%s, bill:%v", bill.VendorOrderID, bill) globals.SugarLogger.Debugf("OnWaybillStatusChanged bill not exist! orderID:%s, bill:%v", bill.VendorOrderID, bill)
} }
switch bill.Status { switch bill.Status {
case model.WaybillStatusAccepted: case model.WaybillStatusAccepted, model.WaybillStatusCourierArrived, model.WaybillStatusDelivering:
s.resetTimer(savedOrderInfo, bill, isPending) s.resetTimer(savedOrderInfo, bill, isPending)
if (isBillExist || bill.WaybillVendorID != model.VendorIDDada) && !isPending { // todo 达达运单有错序的情况,临时看看 if (isBillExist || bill.WaybillVendorID != model.VendorIDDada) && !isPending { // todo 达达运单有错序的情况,临时看看
isBillAlreadyCandidate := s.isBillCandidate(order, bill) isBillAlreadyCandidate := s.isBillCandidate(order, bill)
// todo 购买平台的运单优先级最高但这样写也可能带来问题即在这个时间因为之前3方已经接单已经发出了转自送请求而且可能成功了所以加个状态判断 // todo 购买平台的运单优先级最高但这样写也可能带来问题即在这个时间因为之前3方已经接单已经发出了转自送请求而且可能成功了所以加个状态判断
if order.WaybillVendorID == model.VendorIDUnknown || if !model.IsOrderHaveWaybill(order) ||
(s.IsOrderPlatformWaybill(bill) && order.VendorID != order.WaybillVendorID && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0) { (model.IsWaybillPlatformOwn(bill) && !model.IsOrderHaveOwnWaybill(order) && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0) {
if s.IsOrderHasWaybill(order) { if model.IsOrderHaveWaybill(order) {
// 进到这里的原因是在这个时间点购物平台物流已经抢单但抢单消息还没有被收到比如818810379000941 // 进到这里的原因是在这个时间点购物平台物流已经抢单但抢单消息还没有被收到比如818810379000941
globals.SugarLogger.Infof("OnWaybillStatusChanged orderID:%s purchase platform waybill arrvied later, may case problem", order.VendorOrderID) globals.SugarLogger.Infof("OnWaybillStatusChanged orderID:%s purchase platform waybill arrvied later, may cause problem", order.VendorOrderID)
} }
s.updateOrderByBill(order, bill, false) s.updateOrderByBill(order, bill, false)
s.cancelOtherWaybills(savedOrderInfo, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
if !s.IsOrderPlatformWaybill(bill) {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore { if model.IsWaybillPlatformOwn(bill) {
s.SelfDeliverDelivering(savedOrderInfo.order, bill.CourierMobile) if bill.Status == model.WaybillStatusDelivering {
// 强制将订单状态置为配送中?
order.Status = model.OrderStatusDelivering
partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
} else {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore ||
model.IsSpecialOrderPlatformWaybill(bill) {
if err := s.SelfDeliverDelivering(savedOrderInfo.order, bill.CourierMobile); err != nil {
partner.CurOrderManager.OnOrderMsg(order, "自送出设置失败", err.Error())
}
utils.CallFuncAsync(func() {
weixinmsg.NotifyWaybillStatus(bill, order, isBillAlreadyCandidate)
})
} else { } else {
s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, 2, 10*time.Second) s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, 2, 10*time.Second)
} }
} else if s.IsSpecialOrderPlatformWaybill(bill) {
s.SelfDeliverDelivering(savedOrderInfo.order, bill.CourierMobile)
} }
} else if !s.isBillCandidate(order, bill) && bill.WaybillVendorID != order.VendorID { } else if !s.isBillCandidate(order, bill) && bill.WaybillVendorID != order.VendorID {
// 发生这种情况的原因就是两个接单事件几乎同时到达(来不及取消),也算正常 // 发生这种情况的原因就是两个接单事件几乎同时到达(来不及取消),也算正常
s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
globals.SugarLogger.Infof("OnWaybillStatusChanged Accepted orderID:%s got multiple bill:%v", order.VendorOrderID, bill) globals.SugarLogger.Infof("OnWaybillStatusChanged Accepted orderID:%s got multiple bill:%v", order.VendorOrderID, bill)
} }
if s.isBillCandidate(order, bill) && order.WaybillVendorID != order.VendorID { if isBillAlreadyCandidate && !s.isWaybillCourierSame(savedOrderInfo, bill) && !model.IsWaybillPlatformOwn(bill) {
if !isBillAlreadyCandidate || !s.isWaybillCourierSame(savedOrderInfo, bill) {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
weixinmsg.NotifyWaybillStatus(bill, order, isBillAlreadyCandidate) weixinmsg.NotifyWaybillStatus(bill, order, isBillAlreadyCandidate)
}) })
} }
flag2Clear := model.WaybillVendorID2Mask(bill.WaybillVendorID)
if order.DeliveryFlag&flag2Clear != 0 {
order.DeliveryFlag &= ^flag2Clear
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
} }
} }
case model.WaybillStatusAcceptCanceled: case model.WaybillStatusAcceptCanceled:
if s.isBillCandidate(order, bill) { if s.isBillCandidate(order, bill) {
s.resetTimer(savedOrderInfo, bill, isPending) s.resetTimer(savedOrderInfo, bill, isPending)
if !isPending { if !isPending {
bill.WaybillVendorID = model.VendorIDUnknown s.updateOrderByBill(order, nil, true)
s.updateOrderByBill(order, bill, false)
// 取消抢单应该不需要发3方运单
// s.createWaybillOn3rdProviders(savedOrderInfo, bill)
} }
} else if s.IsOrderHasWaybill(order) { } else if model.IsOrderHaveWaybill(order) {
s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
if !isPending { if !isPending {
globals.SugarLogger.Warnf("OnWaybillStatusChanged AcceptCanceled orderID:%s got multiple bill:%v, order details:%v", order.VendorOrderID, bill, order) globals.SugarLogger.Warnf("OnWaybillStatusChanged AcceptCanceled orderID:%s got multiple bill:%v, order details:%v", order.VendorOrderID, bill, order)
} }
} }
case model.WaybillStatusCourierArrived: // do nothing // case model.WaybillStatusCourierArrived: // do nothing
s.resetTimer(savedOrderInfo, bill, isPending) // s.resetTimer(savedOrderInfo, bill, isPending)
if s.isBillCandidate(order, bill) { // if s.isBillCandidate(order, bill) {
} else { // } else {
// s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime) // // s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
globals.SugarLogger.Infof("OnWaybillStatusChanged CourierArrived order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill) // globals.SugarLogger.Infof("OnWaybillStatusChanged CourierArrived order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill)
} // }
case model.WaybillStatusCanceled, model.WaybillStatusFailed: case model.WaybillStatusCanceled, model.WaybillStatusFailed:
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID) s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if s.isBillCandidate(order, bill) || order.WaybillVendorID == model.VendorIDUnknown { if s.isBillCandidate(order, bill) || order.WaybillVendorID == model.VendorIDUnknown {
s.resetTimer(savedOrderInfo, nil, isPending) s.resetTimer(savedOrderInfo, nil, isPending)
if !isPending { if !isPending {
if s.IsOrderHasWaybill(order) { if model.IsOrderHaveWaybill(order) {
bill.WaybillVendorID = model.VendorIDUnknown s.updateOrderByBill(order, nil, true)
s.updateOrderByBill(order, bill, false)
} }
// 3方的运单取消才会重新发起创建3方订单购物平台的运单取消后它本身还会再创建新运单(NewWaybill事件有相应TIMER)),至少京东是这样的,暂时按京东的行为来 // 3方的运单取消才会重新发起创建3方订单购物平台的运单取消后它本身还会再创建新运单(NewWaybill事件有相应TIMER)),至少京东是这样的,暂时按京东的行为来
// 现在发现饿百取消订单后不会再创建运单了,所以饿百运单取消也允许直接创建三方运单 // 现在发现饿百取消订单后不会再创建运单了,所以饿百运单取消也允许直接创建三方运单
// 之前的条件是order.Status < model.OrderStatusDelivering但像订单902322817000122确实有在配送中取消状态改成非订单结束状态都可以 // 之前的条件是order.Status < model.OrderStatusDelivering但像订单902322817000122确实有在配送中取消状态改成非订单结束状态都可以
// OrderStatusFinishedPickup状态的订单依赖于TIMER重新建运单 // OrderStatusFinishedPickup状态的订单依赖于TIMER重新建运单
if order.Status >= model.OrderStatusDelivering && order.Status < model.OrderStatusEndBegin && (bill.WaybillVendorID != order.VendorID || order.VendorID == model.VendorIDEBAI) { if bill.DeliveryFlag&model.WaybillDeliveryFlagMaskActiveCancel == 0 {
if (order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) && (bill.WaybillVendorID != order.VendorID /* || bill.WaybillVendorID == model.VendorIDEBAI*/) {
s.createWaybillOn3rdProviders(savedOrderInfo, nil) s.createWaybillOn3rdProviders(savedOrderInfo, nil)
} }
} }
} }
case model.WaybillStatusDelivering:
s.resetTimer(savedOrderInfo, bill, isPending)
if s.isBillCandidate(order, bill) {
// do nothing
} else {
// s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
globals.SugarLogger.Infof("OnWaybillStatusChanged Delivering order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill)
} }
// case model.WaybillStatusDelivering:
// s.resetTimer(savedOrderInfo, bill, isPending)
// if s.isBillCandidate(order, bill) {
// // do nothing
// } else {
// // s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
// globals.SugarLogger.Infof("OnWaybillStatusChanged Delivering order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill)
// }
case model.WaybillStatusDelivered: case model.WaybillStatusDelivered:
s.resetTimer(savedOrderInfo, bill, isPending) s.resetTimer(savedOrderInfo, bill, isPending)
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID) s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if !s.IsOrderPlatformWaybill(bill) && !isPending { if !isPending {
var err2 error
if !model.IsWaybillPlatformOwn(bill) {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore { if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore {
s.SelfDeliverDelivered(order, "") err2 = s.SelfDeliverDelivered(order, "")
} else { } else {
s.Swtich2SelfDelivered(order, "") err2 = s.Swtich2SelfDelivered(order, "")
}
} else if model.IsSpecialOrderPlatformWaybill(bill) {
err2 = s.SelfDeliverDelivered(savedOrderInfo.order, "")
}
if err2 != nil {
partner.CurOrderManager.OnOrderMsg(order, "送达设置失败", err2.Error())
} }
} else if s.IsSpecialOrderPlatformWaybill(bill) {
s.SelfDeliverDelivered(savedOrderInfo.order, "")
} }
if !s.isBillCandidate(order, bill) { if !s.isBillCandidate(order, bill) {
// 一般只会消息乱序才会到这里,即新订单消息在运单接单消息后到达 // 一般只会消息乱序才会到这里,即新订单消息在运单接单消息后到达
// 典型的一个1223633660228537567 // 典型的一个1223633660228537567
globals.SugarLogger.Infof("OnWaybillStatusChanged Delivered order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill) globals.SugarLogger.Infof("OnWaybillStatusChanged Delivered order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill)
if order.WaybillVendorID == model.VendorIDUnknown { if !model.IsOrderHaveWaybill(order) {
s.updateOrderByBill(order, bill, false) s.updateOrderByBill(order, bill, false)
} }
} }
@@ -461,14 +537,16 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending boo
weixinmsg.NotifyWaybillStatus(bill, order, false) weixinmsg.NotifyWaybillStatus(bill, order, false)
}) })
} }
case model.WaybillStatusNeverSend: // 平台不配送,直接创建三方运单 // case model.WaybillStatusNeverSend: // 平台不配送,直接创建三方运单
// s.resetTimer(savedOrderInfo, bill, isPending)
// s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
// if order.WaybillVendorID == model.VendorIDUnknown {
// s.createWaybillOn3rdProviders(savedOrderInfo, nil)
// }
default:
s.resetTimer(savedOrderInfo, bill, isPending) s.resetTimer(savedOrderInfo, bill, isPending)
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if order.WaybillVendorID == model.VendorIDUnknown {
s.createWaybillOn3rdProviders(savedOrderInfo, nil)
} }
} // s.updateBillsInfo(savedOrderInfo, bill) // 更新可能的运单状态变化
s.updateBillsInfo(savedOrderInfo, bill) // 更新可能的运单状态变化
} }
// } // }
} }
@@ -481,7 +559,7 @@ func (s *DefScheduler) isWaybillCourierSame(savedOrderInfo *WatchOrderInfo, bill
func (s *DefScheduler) addWaybill2Map(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) { func (s *DefScheduler) addWaybill2Map(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) {
if _, ok := savedOrderInfo.waybills[bill.WaybillVendorID]; ok { if _, ok := savedOrderInfo.waybills[bill.WaybillVendorID]; ok {
if !s.IsOrderPlatformWaybill(bill) { // 购买平台重复发相同号的新运单是正常的,京东就是 if !model.IsWaybillPlatformOwn(bill) { // 购买平台重复发相同号的新运单是正常的,京东就是
globals.SugarLogger.Warnf("addWaybill2Map bill:%v already exists", bill) globals.SugarLogger.Warnf("addWaybill2Map bill:%v already exists", bill)
} }
} }
@@ -503,17 +581,17 @@ func (s *DefScheduler) createWaybillOn3rdProviders(savedOrderInfo *WatchOrderInf
if err = s.canOrderCreateWaybillNormally(order); err == nil { if err = s.canOrderCreateWaybillNormally(order); err == nil {
if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
if savedOrderInfo.retryCount <= maxWaybillRetryCount { if savedOrderInfo.retryCount <= maxWaybillRetryCount {
_, err = s.CreateWaybillOnProviders4SavedOrder(jxcontext.AdminCtx, savedOrderInfo, false) savedOrderInfo.retryCount++
_, err = s.CreateWaybillOnProviders4SavedOrder(jxcontext.AdminCtx, savedOrderInfo, nil, false, 0, 0)
} else { } else {
err = fmt.Errorf("订单:%s已经自动创建过了%d次运单请人工处理", order.VendorOrderID, savedOrderInfo.retryCount) err = fmt.Errorf("订单:%s已经自动创建过了%d次运单请人工处理", order.VendorOrderID, savedOrderInfo.retryCount)
globals.SugarLogger.Infof("createWaybillOn3rdProviders [运营]同一订单orderID:%s尝试了%d次创建运单失败, 停止调度,如果还需要发单,请人工处理", order.VendorOrderID, savedOrderInfo.retryCount) globals.SugarLogger.Infof("createWaybillOn3rdProviders [运营2]同一订单orderID:%s尝试了%d次创建运单失败, 停止调度,如果还需要发单,请人工处理", order.VendorOrderID, savedOrderInfo.retryCount)
} }
} else { } else {
globals.SugarLogger.Debugf("createWaybillOn3rdProviders, orderID:%s, store:%d dont't support 3rd delivery platform", order.VendorOrderID, jxutils.GetSaleStoreIDFromOrder(order)) globals.SugarLogger.Debugf("createWaybillOn3rdProviders, orderID:%s, store:%d dont't support 3rd delivery platform", order.VendorOrderID, jxutils.GetSaleStoreIDFromOrder(order))
} }
if err != nil { if err != nil {
partner.CurOrderManager.OnOrderMsg(order, "自动创建三方运单", utils.LimitUTF8StringLen(err.Error(), 255)) partner.CurOrderManager.OnOrderMsg(order, "自动创建三方运单失败", err.Error())
} }
} else { } else {
err = nil err = nil
@@ -521,38 +599,34 @@ func (s *DefScheduler) createWaybillOn3rdProviders(savedOrderInfo *WatchOrderInf
return err return err
} }
func (s *DefScheduler) cancelOtherWaybills(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) { func (s *DefScheduler) cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep) globals.SugarLogger.Debugf("cancelOtherWaybillsCheckOrderDeliveryFlag, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep)
if (savedOrderInfo.order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { if (savedOrderInfo.order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
err = s.cancelOtherWaybills2(savedOrderInfo, bill2Keep, cancelReasonID, cancelReason) err = s.cancelOtherWaybills(savedOrderInfo, bill2Keep, cancelReasonID, cancelReason)
} else { } else {
globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v stop schedule", savedOrderInfo.order.VendorOrderID, bill2Keep) globals.SugarLogger.Debugf("cancelOtherWaybillsCheckOrderDeliveryFlag, orderID:%s, bill:%v stop schedule", savedOrderInfo.order.VendorOrderID, bill2Keep)
} }
return err return err
} }
func (s *DefScheduler) cancelOtherWaybills2(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) { func (s *DefScheduler) cancelOtherWaybills(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("cancelOtherWaybills2, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep) globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep)
toBeDeleted := []*model.Waybill{}
for _, v := range savedOrderInfo.waybills { for _, v := range savedOrderInfo.waybills {
if !s.IsOrderPlatformWaybill(v) && (bill2Keep == nil || !(v.WaybillVendorID == bill2Keep.WaybillVendorID && v.VendorWaybillID == bill2Keep.VendorWaybillID)) { if v.Status < model.WaybillStatusEndBegin &&
err2 := s.ProxyCancelWaybill(savedOrderInfo.order, v, cancelReasonID, cancelReason) !model.IsWaybillPlatformOwn(v) &&
(bill2Keep == nil || !(v.WaybillVendorID == bill2Keep.WaybillVendorID && v.VendorWaybillID == bill2Keep.VendorWaybillID)) {
err2 := s.CancelWaybill(v, cancelReasonID, cancelReason)
if err2 == nil { if err2 == nil {
toBeDeleted = append(toBeDeleted, v) // 在这里就从map里删除而不是等收到运单结束事件才删除可避免不必要的重复取消第二次取消还会失败
} s.removeWaybillFromMap(savedOrderInfo, v.WaybillVendorID)
} else {
// 至少返回一个错误 // 至少返回一个错误
if err == nil && err2 != nil { if err == nil {
err = err2 err = err2
} }
partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "取消三方运单失败", err2.Error())
} }
} }
if len(toBeDeleted) > 0 {
// todo 这里为什么要删除运单,应该只需要在运单完成,取消或失败时才删除
// for _, v := range toBeDeleted {
// s.removeWaybillFromMap(savedOrderInfo, v.WaybillVendorID)
// }
} else {
globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v cancel 0 bills", savedOrderInfo.order.VendorOrderID, bill2Keep)
} }
return err return err
} }
@@ -560,7 +634,6 @@ func (s *DefScheduler) cancelOtherWaybills2(savedOrderInfo *WatchOrderInfo, bill
func (s *DefScheduler) swtich2SelfDeliverWithRetry(savedOrderInfo *WatchOrderInfo, bill *model.Waybill, retryCount int, duration time.Duration) { func (s *DefScheduler) swtich2SelfDeliverWithRetry(savedOrderInfo *WatchOrderInfo, bill *model.Waybill, retryCount int, duration time.Duration) {
order := savedOrderInfo.order order := savedOrderInfo.order
globals.SugarLogger.Debugf("swtich2SelfDeliverWithRetry orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("swtich2SelfDeliverWithRetry orderID:%s", order.VendorOrderID)
if (order.DeliveryFlag & model.OrderDeliveryFlagMaskPurcahseDisabled) == 0 {
if order.WaybillVendorID != order.VendorID { if order.WaybillVendorID != order.VendorID {
if err := s.Swtich2SelfDeliver(order, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation { if err := s.Swtich2SelfDeliver(order, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation {
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry failed, bill:%v, err:%v", bill, err) globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry failed, bill:%v, err:%v", bill, err)
@@ -568,27 +641,38 @@ func (s *DefScheduler) swtich2SelfDeliverWithRetry(savedOrderInfo *WatchOrderInf
utils.AfterFuncWithRecover(duration, func() { utils.AfterFuncWithRecover(duration, func() {
jxutils.CallMsgHandlerAsync(func() { jxutils.CallMsgHandlerAsync(func() {
s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, retryCount-1, duration) s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, retryCount-1, duration)
}, order.VendorOrderID) }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
}) })
} else { } else {
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry finally failed, orderID:%s bill:%v, err:%v", order.VendorOrderID, bill, err) errStr := fmt.Sprintf("订单:%s转自配送失败, 错误信息:%v", order.VendorOrderID, err)
globals.SugarLogger.Info(errStr)
if s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonSwitch2SelfFailed, partner.CancelWaybillReasonStrSwitch2SelfFailed) == nil { if s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonSwitch2SelfFailed, partner.CancelWaybillReasonStrSwitch2SelfFailed) == nil {
// 转自送失败的取消,要将订单中的运单状态更新 // 转自送失败的取消,要将订单中的运单状态更新
if s.isBillCandidate(order, bill) { if s.isBillCandidate(order, bill) {
bill.WaybillVendorID = model.VendorIDUnknown s.updateOrderByBill(order, nil, false)
s.updateOrderByBill(order, bill, false)
} }
} }
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
partner.CurOrderManager.OnOrderMsg(order, "转商家自配送失败", errStr)
} }
} else { } else {
utils.CallFuncAsync(func() {
weixinmsg.NotifyWaybillStatus(bill, order, false)
})
s.removeWaybillFromMap(savedOrderInfo, order.VendorID) s.removeWaybillFromMap(savedOrderInfo, order.VendorID)
} }
} else { } else {
s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrNotAcceptIntime)
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
// 进到这里的原因是,在这个时间点,购物平台物流已经抢单(但抢单消息还没有被收到),所以转自送会失败 比如818810379000941更好的做法应该是判断Swtich2SelfDeliver的返回值这种情况下就不得试了 // 进到这里的原因是,在这个时间点,购物平台物流已经抢单(但抢单消息还没有被收到),所以转自送会失败 比如818810379000941更好的做法应该是判断Swtich2SelfDeliver的返回值这种情况下就不得试了
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry orderID:%s status is wrong(maybe purchase platform accepted waybill)", order.VendorOrderID) globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry orderID:%s status is wrong(maybe purchase platform accepted waybill)", order.VendorOrderID)
partner.CurOrderManager.OnOrderMsg(order, "转商家自配送失败", "平台物流已接单")
// globals.SugarLogger.Warnf("swtich2SelfDeliverWithRetry orderID:%s status is wrong, order details:%v", order.VendorOrderID, order) // globals.SugarLogger.Warnf("swtich2SelfDeliverWithRetry orderID:%s status is wrong, order details:%v", order.VendorOrderID, order)
} }
}
} }
// 这个函数这样写的原因是适应一些消息错序 // 这个函数这样写的原因是适应一些消息错序
@@ -624,7 +708,7 @@ func (s *DefScheduler) stopTimer(savedOrderInfo *WatchOrderInfo) {
if savedOrderInfo.timer != nil { if savedOrderInfo.timer != nil {
globals.SugarLogger.Debugf("stopTimer orderID:%s", savedOrderInfo.order.VendorOrderID) globals.SugarLogger.Debugf("stopTimer orderID:%s", savedOrderInfo.order.VendorOrderID)
savedOrderInfo.timer.Stop() savedOrderInfo.timer.Stop()
savedOrderInfo.timerStatus = 0 savedOrderInfo.timerStatus = model.OrderStatusUnknown
savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown
savedOrderInfo.timer = nil savedOrderInfo.timer = nil
} }
@@ -641,13 +725,14 @@ func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Wa
statusTime = bill.StatusTime statusTime = bill.StatusTime
} }
globals.SugarLogger.Debugf("resetTimer, orderID:%s statusType:%d status:%v", order.VendorOrderID, statusType, status) globals.SugarLogger.Debugf("resetTimer, orderID:%s statusType:%d status:%v", order.VendorOrderID, statusType, status)
if statusType != savedOrderInfo.timerStatusType || status >= savedOrderInfo.timerStatus { // 新设置的TIMER不能覆盖状态在其后的TIMER如果状态回绕需要注意 if isStatusNewer(savedOrderInfo.timerStatusType, savedOrderInfo.timerStatus, statusType, status) { // 新设置的TIMER不能覆盖状态在其后的TIMER如果状态回绕需要注意
config := s.mergeOrderStatusConfig(savedOrderInfo, statusTime, statusType, status) config := s.mergeOrderStatusConfig(savedOrderInfo, statusTime, statusType, status)
if config == nil || config.TimerType != partner.TimerTypeByPass { if config == nil || config.TimerType != partner.TimerTypeByPass {
s.stopTimer(savedOrderInfo) s.stopTimer(savedOrderInfo)
} }
if config != nil && config.TimeoutAction != nil && config.TimerType != partner.TimerTypeByPass { if config != nil && config.TimeoutAction != nil && config.TimerType != partner.TimerTypeByPass {
timeout := config.GetRefTimeout(statusTime) if config.CallShouldSetTimer(savedOrderInfo, bill) {
timeout := config.GetRefTimeout(statusTime, order.OrderCreatedAt)
if config.TimeoutGap != 0 { if config.TimeoutGap != 0 {
timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second
} }
@@ -657,7 +742,7 @@ func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Wa
timeout = 0 timeout = 0
} }
if timeout == 0 { if timeout == 0 {
config.TimeoutAction(savedOrderInfo) config.TimeoutAction(savedOrderInfo, bill)
} else { } else {
timerName := "" timerName := ""
if statusType == scheduler.TimerStatusTypeOrder { if statusType == scheduler.TimerStatusTypeOrder {
@@ -667,19 +752,32 @@ func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Wa
} }
savedOrderInfo.timerStatusType = statusType savedOrderInfo.timerStatusType = statusType
savedOrderInfo.timerStatus = status savedOrderInfo.timerStatus = status
savedOrderInfo.timerTime = time.Now().Add(timeout)
savedOrderInfo.timer = utils.AfterFuncWithRecover(timeout, func() { savedOrderInfo.timer = utils.AfterFuncWithRecover(timeout, func() {
jxutils.CallMsgHandlerAsync(func() { jxutils.CallMsgHandlerAsync(func() {
globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID) globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID)
savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), true) savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), true)
config.TimeoutAction(savedOrderInfo) config.TimeoutAction(savedOrderInfo, bill)
savedOrderInfo.timerStatus = 0 savedOrderInfo.timerStatus = 0
savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown
}, order.VendorOrderID) }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
}) })
} }
globals.SugarLogger.Debugf("resetTimer, orderID:%s, status:%d, timeout:%v", order.VendorOrderID, status, timeout) globals.SugarLogger.Debugf("resetTimer, orderID:%s, status:%d, timeout:%v", order.VendorOrderID, status, timeout)
} }
} }
}
}
func isStatusNewer(curStatusType, curStatus, statusType, status int) bool {
// 拣货完成及之前的订单事件TIMER不能覆盖运单TIMER一般是消息错序引起的
if curStatusType == scheduler.TimerStatusTypeWaybill && statusType == scheduler.TimerStatusTypeOrder && status <= model.OrderStatusFinishedPickup {
return false
}
if curStatusType == scheduler.TimerStatusTypeWaybill {
return curStatus != status
}
return curStatusType != statusType || status >= curStatus
} }
func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, statusTime time.Time, statusType, status int) (retVal *StatusActionConfig) { func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, statusTime time.Time, statusType, status int) (retVal *StatusActionConfig) {
@@ -709,7 +807,7 @@ func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, st
TimeoutGap: -1, TimeoutGap: -1,
} }
timeout := statusTime.Sub(time.Now()) + minMinute2Schedule3rdCarrier*time.Minute timeout := statusTime.Sub(time.Now()) + minMinute2Schedule3rdCarrier*time.Minute
if vendorActionParams.GetRefTimeout(statusTime) < timeout { // 如果非立即达订单根据ExpectedDeliveredTime算出来的timeout太早 if vendorActionParams.GetRefTimeout(statusTime, order.OrderCreatedAt) < timeout { // 如果非立即达订单根据ExpectedDeliveredTime算出来的timeout太早
vendorActionParams.Timeout = timeout vendorActionParams.Timeout = timeout
vendorActionParams.TimeoutGap = 0 vendorActionParams.TimeoutGap = 0
} }
@@ -742,10 +840,18 @@ func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, st
} }
} }
} else { // 有最后拣货时间,反推 } else { // 有最后拣货时间,反推
timeout := order.PickDeadline.Sub(time.Now()) - (time2AutoPickupAhead + second2AutoPickupGap*time.Second)
realSecond2AutoPickupGap := second2AutoPickupGap
if realSecond2AutoPickupGap > int(timeout/time.Second) {
realSecond2AutoPickupGap = int(timeout / time.Second)
if realSecond2AutoPickupGap < 0 {
realSecond2AutoPickupGap = 0
}
}
vendorActionParams = &partner.StatusActionParams{ vendorActionParams = &partner.StatusActionParams{
TimerType: partner.TimerTypeBaseNow, TimerType: partner.TimerTypeBaseNow,
Timeout: order.PickDeadline.Sub(time.Now()) - time2AutoPickupAhead - second2AutoPickupGap*time.Second, Timeout: timeout,
TimeoutGap: second2AutoPickupGap, TimeoutGap: realSecond2AutoPickupGap,
} }
} }
} }
@@ -795,40 +901,39 @@ func (s *DefScheduler) handleAutoAcceptOrder(orderID string, vendorID int, userM
return handleType return handleType
} }
// func (s *DefScheduler) updateOrderByStatus(order *model.GoodsOrder, status *model.OrderStatus) (retVal *model.GoodsOrder) {
// order.Status = status.Status
// order.VendorStatus = status.VendorStatus
// order.StatusTime = status.StatusTime
// order.LockStatus = status.LockStatus
// return order
// }
func (s *DefScheduler) updateOrderByBill(order *model.GoodsOrder, bill *model.Waybill, revertStatus bool) { func (s *DefScheduler) updateOrderByBill(order *model.GoodsOrder, bill *model.Waybill, revertStatus bool) {
if order.Status > model.OrderStatusEndBegin { if order.Status > model.OrderStatusEndBegin {
return return
} }
if bill.WaybillVendorID == model.VendorIDUnknown { updateFields := []string{
bill.VendorWaybillID = "" "WaybillVendorID",
"VendorWaybillID",
} }
partner.CurOrderManager.UpdateWaybillVendorID(bill, revertStatus) if bill == nil {
order.WaybillVendorID = model.VendorIDUnknown
order.VendorWaybillID = ""
} else {
order.WaybillVendorID = bill.WaybillVendorID order.WaybillVendorID = bill.WaybillVendorID
order.VendorWaybillID = bill.VendorWaybillID order.VendorWaybillID = bill.VendorWaybillID
}
if revertStatus { if revertStatus {
order.Status = model.OrderStatusFinishedPickup order.Status = model.OrderStatusFinishedPickup
partner.CurOrderManager.UpdateOrderStatusAndFlag(order) updateFields = append(updateFields, "Status")
} }
partner.CurOrderManager.UpdateOrderFields(order, updateFields)
} }
func (s *DefScheduler) updateBillsInfo(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (isBillExist bool) { func (s *DefScheduler) updateBillsInfo(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (isBillExist bool) {
if savedOrderInfo != nil { if savedOrderInfo != nil {
if savedBill := savedOrderInfo.waybills[bill.WaybillVendorID]; savedBill != nil { if savedBill := savedOrderInfo.waybills[bill.WaybillVendorID]; savedBill != nil {
isBillExist = true isBillExist = true
if savedBill.Status > bill.Status { // if savedBill.Status > bill.Status {
bill.Status = savedBill.Status // bill.Status = savedBill.Status
} else if bill.Status > savedBill.Status { // } else if bill.Status > savedBill.Status {
savedBill.Status = bill.Status // savedBill.Status = bill.Status
} // }
} }
savedOrderInfo.waybills[bill.WaybillVendorID] = bill
} }
return isBillExist return isBillExist
} }
@@ -848,7 +953,10 @@ func (s *DefScheduler) isBillCandidate(order *model.GoodsOrder, bill *model.Wayb
func (s *DefScheduler) ProxyCancelWaybill(order *model.GoodsOrder, bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) { func (s *DefScheduler) ProxyCancelWaybill(order *model.GoodsOrder, bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s", order.VendorOrderID)
if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 { if (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
return s.CancelWaybill(bill, cancelReasonID, cancelReason) if err = s.CancelWaybill(bill, cancelReasonID, cancelReason); err != nil {
partner.CurOrderManager.OnOrderMsg(order, "取消三方运单失败", err.Error())
}
return err
} }
globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s stop schedule, bypass CancelWaybill", order.VendorOrderID) globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s stop schedule, bypass CancelWaybill", order.VendorOrderID)
return nil return nil
@@ -868,18 +976,3 @@ func OnDefSchConfChanged(key, value string) {
} }
} }
} }
// 判断是否是购买平台自有物流
// 对于京东,饿百来说,就是其自有的物流,对于微商城来说,是达达
func (s *DefScheduler) IsOrderPlatformWaybill(bill *model.Waybill) bool {
return bill.OrderVendorID == bill.WaybillVendorID || s.IsSpecialOrderPlatformWaybill(bill)
}
// 是否是特殊物流
func (s *DefScheduler) IsSpecialOrderPlatformWaybill(bill *model.Waybill) bool {
return (bill.OrderVendorID == model.VendorIDWSC && bill.WaybillVendorID == model.VendorIDDada)
}
func (s *DefScheduler) IsOrderHasWaybill(order *model.GoodsOrder) bool {
return order.WaybillVendorID != model.VendorIDUnknown
}

View File

@@ -0,0 +1,27 @@
package defsch
import (
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/msghub"
)
func (s *DefScheduler) OnAfsOrderNew(order *model.AfsOrder, isPending bool) (err error) {
if order.Status == model.AfsOrderStatusWait4Approve {
if !isPending {
msghub.OnNewWait4ApproveAfsOrder(order)
weixinmsg.NotifyAfsOrderStatus(order)
}
}
return err
}
func (s *DefScheduler) OnAfsOrderStatusChanged(order *model.AfsOrder, status *model.OrderStatus, isPending bool) (err error) {
if status.Status == model.AfsOrderStatusWait4ReceiveGoods {
if !isPending {
msghub.OnKeyAfsOrderStatusChanged(order)
weixinmsg.NotifyAfsOrderStatus(order)
}
}
return err
}

View File

@@ -15,23 +15,25 @@ import (
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
) )
func (s *DefScheduler) loadSavedOrderByID(vendorOrderID string, vendorID int, isForceLoad bool) *WatchOrderInfo {
return s.loadSavedOrderFromMap(&model.OrderStatus{
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}, isForceLoad)
}
func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) { func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) {
jxutils.CallMsgHandler(func() { jxutils.CallMsgHandler(func() {
err = func() (err error) { err = func() (err error) {
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName) globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName)
status := &model.OrderStatus{ savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}
savedOrderInfo := s.loadSavedOrderFromMap(status, true)
if savedOrderInfo != nil { if savedOrderInfo != nil {
order := savedOrderInfo.order order := savedOrderInfo.order
err = s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive) if err = s.isPossibleSwitch2SelfDelivery(order); err == nil {
err = s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
if err == nil { if err == nil {
// todo
if true { //order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore { if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore {
if order.Status <= model.OrderStatusFinishedPickup { if order.Status < model.OrderStatusDelivering {
storeDetail, err2 := dao.GetStoreDetail(dao.GetDB(), order.StoreID, order.VendorID) storeDetail, err2 := dao.GetStoreDetail(dao.GetDB(), order.StoreID, order.VendorID)
phone := userName phone := userName
if err = err2; err == nil { if err = err2; err == nil {
@@ -40,7 +42,7 @@ func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, ven
err = s.SelfDeliverDelivering(order, phone) err = s.SelfDeliverDelivering(order, phone)
} }
} else { } else {
if order.Status <= model.OrderStatusFinishedPickup { if order.Status < model.OrderStatusDelivering {
err = s.Swtich2SelfDeliver(order, userName) err = s.Swtich2SelfDeliver(order, userName)
} else if order.VendorID == order.WaybillVendorID { // 状态为配送中,且是购物平台运单,不能转自送了 } else if order.VendorID == order.WaybillVendorID { // 状态为配送中,且是购物平台运单,不能转自送了
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
@@ -51,7 +53,7 @@ func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, ven
if err == nil { if err == nil {
order.Status = model.OrderStatusDelivering order.Status = model.OrderStatusDelivering
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled | model.OrderDeliveryFlagMaskPurcahseDisabled order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled | model.OrderDeliveryFlagMaskPurcahseDisabled
if err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order); err == nil { if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
s.stopTimer(savedOrderInfo) s.stopTimer(savedOrderInfo)
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName) globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
return err return err
@@ -70,25 +72,47 @@ func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, ven
func (s *DefScheduler) canOrderCreateWaybillNormally(order *model.GoodsOrder) (err error) { func (s *DefScheduler) canOrderCreateWaybillNormally(order *model.GoodsOrder) (err error) {
if !(order.LockStatus != model.OrderStatusLocked && order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) { if !(order.LockStatus != model.OrderStatusLocked && order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) {
err = fmt.Errorf("当前订单%s没有处于拣货完成且没有结束没有锁定的订单才能进行召唤配送操作", order.VendorOrderID) err = fmt.Errorf("当前订单%s没有处于拣货完成且没有结束没有锁定的订单才能进行召唤配送操作", order.VendorOrderID)
} else if s.IsOrderHasWaybill(order) { } else if model.IsOrderHaveWaybill(order) {
err = fmt.Errorf("当前订单%s已经有了有效的承运人%s了", order.VendorOrderID, jxutils.GetVendorName(order.WaybillVendorID)) err = fmt.Errorf("当前订单%s已经有了有效的承运人%s了", order.VendorOrderID, jxutils.GetVendorName(order.WaybillVendorID))
} }
return err return err
} }
func (s *DefScheduler) CreateWaybillOnProviders4SavedOrder(ctx *jxcontext.Context, savedOrderInfo *WatchOrderInfo, forceCreate bool) (bills []*model.Waybill, err error) { func (s *DefScheduler) isPossibleSwitch2SelfDelivery(order *model.GoodsOrder) (err error) {
if scheduler.StoreDeliveryTypeByStore != s.GetStoreDeliveryType(order, nil) {
if order.Status < model.OrderStatusFinishedPickup {
err = fmt.Errorf("拣货完成后才能转自配送")
} else if order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusDelivering {
if time.Now().Sub(order.StatusTime) < minMinute2Schedule3rdCarrier*time.Minute {
err = fmt.Errorf("非自配送门店转3方配送至少要求拣货完成后%d分钟才能操作", minMinute2Schedule3rdCarrier)
}
} else if order.Status >= model.OrderStatusDelivering && order.Status < model.OrderStatusEndBegin {
if model.IsOrderHaveOwnWaybill(order) {
err = fmt.Errorf("%s物流已在配送中不能转自配送", jxutils.GetVendorName(order.VendorID))
}
} else {
err = fmt.Errorf("订单%s已经结束请刷新状态", order.VendorOrderID)
}
}
return err
}
func (s *DefScheduler) CreateWaybillOnProviders4SavedOrder(ctx *jxcontext.Context, savedOrderInfo *WatchOrderInfo, courierVendorIDs []int, forceCreate bool, maxAddFee, maxDiffFee2Mtps int64) (bills []*model.Waybill, err error) {
order := savedOrderInfo.order order := savedOrderInfo.order
if !forceCreate {
err = s.canOrderCreateWaybillNormally(order) err = s.canOrderCreateWaybillNormally(order)
if forceCreate || err == nil { }
err = nil if err == nil {
feeHandler := delivery.DefCreateWaybillPolicy feeHandler := delivery.DefCreateWaybillPolicy
if forceCreate { if forceCreate {
feeHandler = delivery.NullCreateWaybillPolicy feeHandler = delivery.NullCreateWaybillPolicy
} else if maxAddFee != 0 {
feeHandler = delivery.CreateWaybillPolicy(maxDiffFee2Mtps, maxAddFee)
} }
if bills, err = s.CreateWaybillOnProviders(ctx, order, feeHandler); err == nil { if bills, err = s.CreateWaybillOnProviders(ctx, order, courierVendorIDs, feeHandler, forceCreate); err == nil {
if forceCreate { if forceCreate {
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order) err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
} }
if err == nil { if err == nil {
if forceCreate { if forceCreate {
@@ -105,7 +129,7 @@ func (s *DefScheduler) CreateWaybillOnProviders4SavedOrder(ctx *jxcontext.Contex
return nil, err return nil, err
} }
func (s *DefScheduler) CreateWaybillOnProvidersEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int, forceCreate bool) (bills []*model.Waybill, err error) { func (s *DefScheduler) CreateWaybillOnProvidersEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int, courierVendorIDs []int, forceCreate bool, maxAddFee, maxDiffFee2Mtps int64) (bills []*model.Waybill, err error) {
jxutils.CallMsgHandler(func() { jxutils.CallMsgHandler(func() {
bills, err = func() (bills []*model.Waybill, err error) { bills, err = func() (bills []*model.Waybill, err error) {
userName := ctx.GetUserName() userName := ctx.GetUserName()
@@ -113,19 +137,15 @@ func (s *DefScheduler) CreateWaybillOnProvidersEx(ctx *jxcontext.Context, vendor
if vendorID == model.VendorIDELM { if vendorID == model.VendorIDELM {
return nil, fmt.Errorf("不要直接使用饿了么订单号,请使用相应的饿百订单号") return nil, fmt.Errorf("不要直接使用饿了么订单号,请使用相应的饿百订单号")
} }
status := &model.OrderStatus{ savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}
savedOrderInfo := s.loadSavedOrderFromMap(status, true)
if savedOrderInfo != nil { if savedOrderInfo != nil {
order := savedOrderInfo.order order := savedOrderInfo.order
if scheduler.StoreDeliveryTypeByStore != s.GetStoreDeliveryType(order, nil) && if !forceCreate {
order.Status == model.OrderStatusFinishedPickup && err = s.isPossibleSwitch2SelfDelivery(order)
time.Now().Sub(order.StatusTime) < minMinute2Schedule3rdCarrier*time.Minute { }
return nil, fmt.Errorf("非自配送门店转3方配送至少要求拣货完成后%d分钟才能操作", minMinute2Schedule3rdCarrier) if err == nil {
bills, err = s.CreateWaybillOnProviders4SavedOrder(ctx, savedOrderInfo, courierVendorIDs, forceCreate, maxAddFee, maxDiffFee2Mtps)
} }
bills, err = s.CreateWaybillOnProviders4SavedOrder(ctx, savedOrderInfo, forceCreate)
} else { } else {
err = scheduler.ErrCanNotFindOrder err = scheduler.ErrCanNotFindOrder
} }
@@ -141,13 +161,9 @@ func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderI
jxutils.CallMsgHandler(func() { jxutils.CallMsgHandler(func() {
err = func() (err error) { err = func() (err error) {
globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s", vendorOrderID, ctx.GetUserName()) globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s", vendorOrderID, ctx.GetUserName())
status := &model.OrderStatus{ savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}
savedOrderInfo := s.loadSavedOrderFromMap(status, true)
if savedOrderInfo != nil { if savedOrderInfo != nil {
err = s.cancelOtherWaybills2(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive) err = s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
} else { } else {
err = scheduler.ErrCanNotFindOrder err = scheduler.ErrCanNotFindOrder
} }
@@ -155,7 +171,7 @@ func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderI
if err == nil && isStopSchedule { if err == nil && isStopSchedule {
order := savedOrderInfo.order order := savedOrderInfo.order
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
if err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order); err == nil { if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
s.stopTimer(savedOrderInfo) s.stopTimer(savedOrderInfo)
globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s successfully", vendorOrderID, ctx.GetUserName()) globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s successfully", vendorOrderID, ctx.GetUserName())
} }
@@ -165,3 +181,80 @@ func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderI
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID)) }, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
return err return err
} }
func (s *DefScheduler) QueryOrderWaybillFeeInfoEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (deliveryFeeMap map[int]*partner.WaybillFeeInfo, err error) {
jxutils.CallMsgHandler(func() {
deliveryFeeMap, err = func() (deliveryFeeMap map[int]*partner.WaybillFeeInfo, err error) {
userName := ctx.GetUserName()
globals.SugarLogger.Infof("GetWaybillsInfoEx orderID:%s userName:%s", vendorOrderID, userName)
db := dao.GetDB()
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
if err != nil {
return nil, err
}
storeCourierList, err := dao.GetStoreCourierList(db, jxutils.GetSaleStoreIDFromOrder(order), model.StoreStatusAll)
if err != nil {
return nil, err
}
waybillList, err := partner.CurOrderManager.GetOrderWaybillInfo(ctx, vendorOrderID, vendorID, true)
if err != nil {
return nil, err
}
waybillMap := make(map[int]*model.Waybill)
for _, bill := range waybillList {
waybillMap[bill.WaybillVendorID] = bill
}
deliveryFeeMap = make(map[int]*partner.WaybillFeeInfo)
var timeoutSecond int
if savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, false); savedOrderInfo != nil {
if savedOrderInfo.timerStatusType == scheduler.TimerStatusTypeWaybill && savedOrderInfo.timerStatus == model.WaybillStatusNew {
timeoutSecond = int(savedOrderInfo.timerTime.Sub(time.Now()) / time.Second)
}
}
for _, storeCourier := range storeCourierList {
var feeInfo *partner.WaybillFeeInfo
if waybillMap[storeCourier.VendorID] != nil {
feeInfo = &partner.WaybillFeeInfo{
Waybill: waybillMap[storeCourier.VendorID],
}
} else {
if storeCourier.Status != model.StoreStatusOpened {
feeInfo = &partner.WaybillFeeInfo{
ErrCode: partner.WaybillFeeErrCodeCourierNotOpen,
ErrStr: fmt.Sprintf("%d配送门店没有启用", storeCourier.VendorID),
}
} else {
if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil {
if handler.Use4CreateWaybill {
if feeInfo, err = handler.Handler.GetWaybillFee(order); err != nil {
feeInfo = &partner.WaybillFeeInfo{
ErrCode: partner.WaybillFeeErrCodeCourierOthers,
ErrStr: err.Error(),
}
} else {
feeInfo.TimeoutSecond = timeoutSecond
}
} else {
feeInfo = &partner.WaybillFeeInfo{
ErrCode: partner.WaybillFeeErrCodeCourierForbidden,
ErrStr: fmt.Sprintf("内部错误,%d不能用于创建运单", storeCourier.VendorID),
}
}
} else {
feeInfo = &partner.WaybillFeeInfo{
ErrCode: partner.WaybillFeeErrCodeCourierNotSupported,
ErrStr: fmt.Sprintf("内部错误,%d不被支持", storeCourier.VendorID),
}
}
}
}
deliveryFeeMap[storeCourier.VendorID] = feeInfo
}
err = nil
return deliveryFeeMap, err
}()
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
return deliveryFeeMap, err
}

View File

@@ -40,4 +40,8 @@ type IScheduler interface {
// 以下是运单 // 以下是运单
OnWaybillStatusChanged(bill *model.Waybill, isPending bool) (err error) OnWaybillStatusChanged(bill *model.Waybill, isPending bool) (err error)
// 以下是售后单
OnAfsOrderNew(order *model.AfsOrder, isPending bool) (err error)
OnAfsOrderStatusChanged(order *model.AfsOrder, status *model.OrderStatus, isPending bool) (err error)
} }

355
business/jxstore/act/act.go Normal file
View File

@@ -0,0 +1,355 @@
package act
import (
"fmt"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
)
const (
ActionTypeNA = 0
)
type ActOrderRuleParam struct {
SalePrice int64 `orm:"" json:"salePrice"` // 满的价格
DeductPrice int64 `orm:"" json:"deductPrice"` // 减的价格
}
type ActStoreSkuParam struct {
Action int // -1删除1修改2新增
StoreID int `orm:"column(store_id)" json:"storeID"`
SkuID int `orm:"column(sku_id)" json:"skuID"`
PricePercentage int `orm:"" json:"pricePercentage"` // 单品级活动用SKU级的价格比例非0覆盖Act中的PricePercentage
ActPrice int64 `orm:"" json:"actPrice"` // 单品级活动用SKU级指定的价格非0覆盖CustomPricePercentage与Act中的PricePercentage
Stock int `orm:"" json:"stock"` // 订单级活动用
}
type ActDetail struct {
model.Act2
}
func ActStoreSkuParam2Map(actStoreSku []*ActStoreSkuParam) (actStoreSkuMap map[int][]*ActStoreSkuParam) {
if len(actStoreSku) > 0 {
for _, v := range actStoreSku {
actStoreSkuMap[v.StoreID] = append(actStoreSkuMap[v.StoreID], v)
}
}
return actStoreSkuMap
}
func genStoreSkuMapKey(storeID, skuID int) int64 {
return int64(storeID) + int64(skuID)*1000000
}
func ActStoreSkuParam2Model(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, actStoreSku []*ActStoreSkuParam) (actMapList []*model.ActMap, actStoreMapList []*model.ActStoreMap, actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap, err error) {
if len(actStoreSku) > 0 {
storeIDMap := make(map[int]int)
skuIDMap := make(map[int]int)
storeSkuParamMap := make(map[int][]*ActStoreSkuParam)
for _, v := range actStoreSku {
storeIDMap[v.StoreID] = 1
skuIDMap[v.SkuID] = 1
storeSkuParamMap[v.StoreID] = append(storeSkuParamMap[v.StoreID], v)
}
db := dao.GetDB()
storeSkuList, err2 := dao.GetStoresSkusInfo(db, jxutils.IntMap2List(storeIDMap), jxutils.IntMap2List(skuIDMap))
if err = err2; err != nil {
return nil, nil, nil, nil, err
}
storeSkuMap := make(map[int64]*model.StoreSkuBind)
for _, v := range storeSkuList {
storeSkuMap[genStoreSkuMapKey(v.StoreID, v.SkuID)] = v
}
wholeValidVendorMap := make(map[int]int)
for storeID, oneStoreSkuParam := range storeSkuParamMap {
validVendorMap := make(map[int]int)
validSkuMap := make(map[int]int)
for _, vendorID := range vendorIDs {
storeDetail, err2 := dao.GetStoreDetail(db, storeID, vendorID)
if err = err2; err == nil {
for _, v := range oneStoreSkuParam {
if storeSkuInfo := storeSkuMap[genStoreSkuMapKey(v.StoreID, v.SkuID)]; storeSkuInfo != nil {
validVendorMap[vendorID] = 1
validSkuMap[v.SkuID] = 1
actSkuMap := &model.ActStoreSkuMap{
ActID: act.ID,
StoreID: storeID,
SkuID: v.SkuID,
VendorID: vendorID,
SyncStatus: model.SyncFlagNewMask,
}
if v.ActPrice != 0 {
actSkuMap.ActualActPrice = v.ActPrice
} else {
percentage := act.PricePercentage
if v.PricePercentage != 0 {
percentage = v.PricePercentage
}
percentage = percentage * int(storeDetail.PricePercentage) / 100
actSkuMap.ActualActPrice = int64(jxutils.CaculateSkuVendorPrice(storeSkuInfo.Price, percentage, 0))
}
dao.WrapAddIDCULDEntity(actSkuMap, ctx.GetUserName())
actStoreSkuMapList = append(actStoreSkuMapList, actSkuMap)
}
}
} else if !dao.IsNoRowsError(err) {
return nil, nil, nil, nil, err
} else {
err = nil
}
}
for _, v := range oneStoreSkuParam {
if validSkuMap[v.SkuID] == 1 {
if storeSkuInfo := storeSkuMap[genStoreSkuMapKey(v.StoreID, v.SkuID)]; storeSkuInfo != nil {
storeSku := &model.ActStoreSku{
ActID: act.ID,
StoreID: v.StoreID,
SkuID: v.SkuID,
OriginalPrice: int64(storeSkuInfo.Price),
PricePercentage: v.PricePercentage,
ActPrice: v.ActPrice,
Stock: v.Stock,
}
dao.WrapAddIDCULDEntity(storeSku, ctx.GetUserName())
actStoreSkuList = append(actStoreSkuList, storeSku)
}
}
}
for vendorID := range validVendorMap {
wholeValidVendorMap[vendorID] = 1
actStoreMap := &model.ActStoreMap{
ActID: act.ID,
StoreID: storeID,
VendorID: vendorID,
SyncStatus: model.SyncFlagNewMask,
}
dao.WrapAddIDCULDEntity(actStoreMap, ctx.GetUserName())
actStoreMapList = append(actStoreMapList, actStoreMap)
}
}
for vendorID := range wholeValidVendorMap {
actMap := &model.ActMap{
ActID: act.ID,
VendorID: vendorID,
SyncStatus: model.SyncFlagNewMask,
}
dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName())
actMapList = append(actMapList, actMap)
}
}
return actMapList, actStoreMapList, actStoreSkuList, actStoreSkuMapList, err
}
func CreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, actRules []*ActOrderRuleParam, actStoreSku []*ActStoreSkuParam) (actID int, err error) {
vendorIDMap := make(map[int]bool)
for _, v := range vendorIDs {
vendorIDMap[v] = true
}
db := dao.GetDB()
dao.WrapAddIDCULDEntity(act, ctx.GetUserName())
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
err = dao.CreateEntity(db, act)
if err != nil {
return 0, err
}
actMapList, actStoreMapList, actStoreSkuList, actStoreSkuMapList, err := ActStoreSkuParam2Model(ctx, act, vendorIDs, actStoreSku)
if err != nil {
return 0, err
}
isEmptyAct := true
for _, list := range []interface{}{
actMapList, actStoreMapList, actStoreSkuList, actStoreSkuMapList,
} {
if len(utils.Interface2Slice(list)) > 0 {
err = dao.CreateMultiEntities(db, list)
if err != nil {
dao.Rollback(db)
return 0, err
}
isEmptyAct = false
}
}
if isEmptyAct {
dao.Rollback(db)
return 0, fmt.Errorf("没有门店及SKU满足需求空操作")
}
dao.Commit(db)
actID = act.ID
err = SyncAct(ctx, actID, nil, nil, nil)
return actID, err
}
func QueryActs(ctx *jxcontext.Context, actID int, keyword string, statusList []int, actTypeList []int, storeID, skuID int, beginAt, endAt time.Time) (actList []*model.Act, err error) {
return actList, err
}
func GetActDetail(ctx *jxcontext.Context, actID int) (actDetail *ActDetail, err error) {
return actDetail, err
}
// func GetAcVendorInfo(ctx *jxcontext.Context, actID int) (err error) {
// return err
// }
// func GetAcStoresVendorInfo(ctx *jxcontext.Context, actID int, storeIDs []int) (err error) {
// return err
// }
// func GetAcStoresSkusVendorInfo(ctx *jxcontext.Context, actID int, storeIDs, skuIDs []int) (err error) {
// return err
// }
func parseActStoreSkuParam(actStoreSku []*ActStoreSkuParam) {
}
func UpdateAct(ctx *jxcontext.Context, act *model.Act, actRules []*ActOrderRuleParam, actStoreSku []*ActStoreSkuParam) (err error) {
return err
}
func CancelAct(ctx *jxcontext.Context, actID int) (err error) {
db := dao.GetDB()
act := &model.Act{}
act.ID = actID
if err = dao.GetEntity(db, act); err != nil {
return err
}
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
dao.UpdateEntityLogically(db, act, map[string]interface{}{
model.FieldStatus: model.ActStatusCanceled,
}, ctx.GetUserName(), nil)
_, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, &model.ActMap{}, nil, ctx.GetUserName(), map[string]interface{}{
model.FieldActID: actID,
}, model.FieldSyncStatus, model.SyncFlagModifiedMask)
if err == nil {
dao.Commit(db)
globals.SugarLogger.Debugf("CancelAct track:%s", ctx.GetTrackInfo())
err = SyncAct(ctx, actID, nil, nil, nil)
} else {
dao.Rollback(db)
}
return err
}
func SyncAct(ctx *jxcontext.Context, actID int, vendorIDs, storeIDs, skuIDs []int) (err error) {
var actOrderRules []*model.ActOrderRule
db := dao.GetDB()
actMap, err := dao.GetActVendorInfo(db, actID, vendorIDs)
if err != nil {
return err
}
actStoreMap, err := dao.GetActStoreVendorInfo(db, actID, vendorIDs, storeIDs)
if err != nil {
return err
}
actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, vendorIDs, storeIDs, skuIDs)
if err != nil {
return err
}
var realVendorIDs []int
for vendorID := range actMap {
realVendorIDs = append(realVendorIDs, vendorID)
}
task := tasksch.NewParallelTask("SyncAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
vendorID := batchItemList[0].(int)
handler := partner.GetPurchasePlatformFromVendorID(vendorID)
if handler == nil {
err = fmt.Errorf("不被支持的vendorID:%d", vendorID)
} else {
act := actMap[vendorID]
actStore := actStoreMap[vendorID]
actStoreSku := actStoreSkuMap[vendorID]
// globals.SugarLogger.Debugf("%s", utils.Format4Output(act, false))
// globals.SugarLogger.Debugf("%s", utils.Format4Output(actStore, false))
// globals.SugarLogger.Debugf("%s", utils.Format4Output(actStoreSku, false))
if act != nil && actStore != nil && actStoreSku != nil {
if model.IsSyncStatusNeedCreate(act.SyncStatus) {
err = handler.CreateAct(ctx, task, act, actOrderRules, actStore, actStoreSku)
} else if model.IsSyncStatusNeedUpdate(act.SyncStatus) {
if act.Status == model.ActStatusCanceled {
err = handler.CancelAct(ctx, task, act, actStore, actStoreSku)
} else {
// actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update := splitActStore(actStore)
// err = handler.UpdateAct(ctx, task, act, actOrderRules, actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update, actStoreSku)
}
}
if err == nil {
actMap := &model.ActMap{}
actMap.ID = act.MapID
dao.UpdateEntityLogically(db, actMap, map[string]interface{}{
model.FieldSyncStatus: 0,
model.FieldVendorActID: act.VendorActID,
}, ctx.GetUserName(), nil)
for _, v := range actStore {
storeMap := model.ActStoreMap{}
storeMap.ID = v.MapID
dao.UpdateEntityLogically(db, storeMap, map[string]interface{}{
model.FieldSyncStatus: 0,
model.FieldVendorActID: v.VendorActID,
}, ctx.GetUserName(), nil)
}
for _, v := range actStoreSku {
storeSkuMap := model.ActStoreSkuMap{}
storeSkuMap.ID = v.MapID
dao.UpdateEntityLogically(db, storeSkuMap, map[string]interface{}{
model.FieldSyncStatus: 0,
model.FieldVendorActID: v.VendorActID,
}, ctx.GetUserName(), nil)
}
}
}
}
return nil, err
}, realVendorIDs)
tasksch.ManageTask(task).Run()
_, err = task.GetResult(0)
return err
}
func splitActStore(actStore []*model.ActStore2) (actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update []*model.ActStore2) {
for _, v := range actStore {
if model.IsSyncStatusNeedDelete(v.SyncStatus) {
if !dao.IsVendorThingIDEmpty(v.VendorActID) {
actStoreMap2Remove = append(actStoreMap2Remove, v)
}
} else if model.IsSyncStatusNeedCreate(v.SyncStatus) {
actStoreMap2Add = append(actStoreMap2Add, v)
} else if model.IsSyncStatusNeedUpdate(v.SyncStatus) {
actStoreMap2Update = append(actStoreMap2Update, v)
}
}
return actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update
}

View File

@@ -0,0 +1,68 @@
package act
import (
"testing"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals/testinit"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
)
func init() {
testinit.Init()
}
func TestInitDb(t *testing.T) {
dao.ExecuteSQL(dao.GetDB(), `
DROP TABLE IF EXISTS act,act_map, act_order_rule, act_store_map, act_store_sku, act_store_sku_map;
`)
}
func TestCreateAct(t *testing.T) {
actID, err := CreateAct(jxcontext.AdminCtx, &model.Act{
Name: "测试活动2",
PricePercentage: 80,
}, []int{0, 1, 3}, nil, []*ActStoreSkuParam{
&ActStoreSkuParam{
StoreID: 100119,
SkuID: 30828,
},
&ActStoreSkuParam{
StoreID: 100119,
SkuID: 30827,
},
&ActStoreSkuParam{
StoreID: 100118,
SkuID: 30592,
},
&ActStoreSkuParam{
StoreID: 100118,
SkuID: 30565,
},
})
if err != nil {
t.Fatal(err)
}
globals.SugarLogger.Debug(actID)
}
func TestCancelAct(t *testing.T) {
err := CancelAct(jxcontext.AdminCtx, 1)
if err != nil {
t.Fatal(err)
}
}
func TestSyncAct(t *testing.T) {
err := SyncAct(jxcontext.AdminCtx, 1, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -21,6 +21,11 @@ const (
var ( var (
serviceInfo map[string]interface{} serviceInfo map[string]interface{}
allowUpdatePlaceFieldsMap = map[string]bool{
"name": true,
"enabled": true,
"mtpsPrice": true,
}
) )
func InitServiceInfo(version string, buildTime time.Time, gitCommit string) { func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
@@ -55,6 +60,8 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
"shopChineseNames": model.ShopChineseNames, "shopChineseNames": model.ShopChineseNames,
"printerVendorInfo": model.PrinterVendorInfo, "printerVendorInfo": model.PrinterVendorInfo,
"purchaseVendorInfo": model.PurchaseVendorInfo, "purchaseVendorInfo": model.PurchaseVendorInfo,
"afsReasonTypeName": model.AfsReasonTypeName,
"afsAppealTypeName": model.AfsAppealTypeName,
}, },
} }
Init() Init()
@@ -124,12 +131,18 @@ func UpdatePlaces(ctx *jxcontext.Context, places []map[string]interface{}, userN
if len(places) == 0 { if len(places) == 0 {
return 0, ErrMissingInput return 0, ErrMissingInput
} }
updateFields := []string{}
for k := range places[0] {
if allowUpdatePlaceFieldsMap[k] {
updateFields = append(updateFields, k)
}
}
for _, place := range places { for _, place := range places {
if place["code"] == nil { if place["code"] == nil {
return 0, ErrMissingInput return 0, ErrMissingInput
} }
placeid := &model.Place{} placeid := &model.Place{}
valid := dao.NormalMakeMapByFieldList(place, []string{"jdCode", "enabled", "mtpsPrice"}, userName) valid := dao.NormalMakeMapByFieldList(place, updateFields, userName)
if num, err = dao.UpdateEntityLogically(nil, placeid, valid, userName, utils.Params2Map("Code", place["code"])); err != nil { if num, err = dao.UpdateEntityLogically(nil, placeid, valid, userName, utils.Params2Map("Code", place["code"])); err != nil {
return num, err return num, err
} }

View File

@@ -13,6 +13,12 @@ import (
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
) )
type MessageStatusExt struct {
model.MessageStatus
Title string `json:"title"`
}
func SendStoreMessage(ctx *jxcontext.Context, title, content string, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { func SendStoreMessage(ctx *jxcontext.Context, title, content string, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB() db := dao.GetDB()
dao.Begin(db) dao.Begin(db)
@@ -128,8 +134,11 @@ func GetStoreMessages(ctx *jxcontext.Context, msgIDs, storeIDs, types []int, fro
func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fromReadCount, toReadCount int, fromTime, toTime time.Time, keyword string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fromReadCount, toReadCount int, fromTime, toTime time.Time, keyword string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
sql := ` sql := `
SELECT SQL_CALC_FOUND_ROWS t1.* SELECT SQL_CALC_FOUND_ROWS
t1.*,
t2.title
FROM message_status t1 FROM message_status t1
JOIN message t2 ON t2.id = t1.message_id
WHERE 1 = 1 WHERE 1 = 1
` `
sqlParams := []interface{}{} sqlParams := []interface{}{}
@@ -167,7 +176,7 @@ func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fro
db := dao.GetDB() db := dao.GetDB()
dao.Begin(db) dao.Begin(db)
defer dao.Commit(db) defer dao.Commit(db)
var msgStatusList []*model.MessageStatus var msgStatusList []*MessageStatusExt
// globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(sql)
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
if err = dao.GetRows(db, &msgStatusList, sql, sqlParams...); err == nil { if err = dao.GetRows(db, &msgStatusList, sql, sqlParams...); err == nil {

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
@@ -14,6 +15,7 @@ import (
"git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
) )
type SkuNamesInfo struct { type SkuNamesInfo struct {
@@ -25,6 +27,19 @@ var (
ErrInputCatsDoesntMatch = errors.New("输入的类别列表不合法需要输入一个父ID下的所有子类别") ErrInputCatsDoesntMatch = errors.New("输入的类别列表不合法需要输入一个父ID下的所有子类别")
) )
var (
ebaiUploadRTFShopID string // 饿百找一个店用于调用SkuUploadRTF
)
func getAndSetEbaiUploadRTFShopID() (shopID string) {
if ebaiUploadRTFShopID == "" {
if storeDetail, err := dao.GetStoreDetail(dao.GetDB(), 0, model.VendorIDEBAI); err == nil {
ebaiUploadRTFShopID = utils.Int2Str(storeDetail.Store.ID)
}
}
return ebaiUploadRTFShopID
}
// parentID 为-1表示所有 // parentID 为-1表示所有
func GetVendorCategories(ctx *jxcontext.Context, vendorID int, parentID string) (vendorCats []*model.SkuVendorCategory, err error) { func GetVendorCategories(ctx *jxcontext.Context, vendorID int, parentID string) (vendorCats []*model.SkuVendorCategory, err error) {
cond := map[string]interface{}{ cond := map[string]interface{}{
@@ -72,7 +87,7 @@ func AddCategory(ctx *jxcontext.Context, cat *model.SkuCategory, userName string
dao.WrapAddIDCULDEntity(cat, userName) dao.WrapAddIDCULDEntity(cat, userName)
cat.JdSyncStatus = model.SyncFlagNewMask cat.JdSyncStatus = model.SyncFlagNewMask
cat.JdID = 0 //jxutils.GenFakeID() cat.JdID = 0
cat.Name = strings.Trim(cat.Name, " ") cat.Name = strings.Trim(cat.Name, " ")
if cat.Seq <= 0 { if cat.Seq <= 0 {
var maxSeq struct { var maxSeq struct {
@@ -93,19 +108,83 @@ func AddCategory(ctx *jxcontext.Context, cat *model.SkuCategory, userName string
func UpdateCategory(ctx *jxcontext.Context, categoryID int, payload map[string]interface{}, userName string) (num int64, err error) { func UpdateCategory(ctx *jxcontext.Context, categoryID int, payload map[string]interface{}, userName string) (num int64, err error) {
cat := &model.SkuCategory{} cat := &model.SkuCategory{}
cat.ID = categoryID cat.ID = categoryID
valid := dao.NormalMakeMapByStructObject(payload, cat, userName) db := dao.GetDB()
if err = dao.GetEntity(db, cat); err != nil {
return 0, err
}
valid := dao.StrictMakeMapByStructObject(payload, cat, userName)
if len(valid) > 0 { if len(valid) > 0 {
syncStatus := 0
if valid["name"] != nil { if valid["name"] != nil {
valid["name"] = strings.Trim(valid["name"].(string), " ") valid["name"] = strings.Trim(valid["name"].(string), " ")
syncStatus = model.SyncFlagModifiedMask
}
if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, cat, valid, userName, nil, model.FieldJdSyncStatus, syncStatus); err == nil {
SetStoreCategorySyncStatus2(db, nil, []int{categoryID}, model.SyncFlagModifiedMask)
if valid["jdCategoryID"] != nil || valid["ebaiCategoryID"] != nil || valid["mtwmCategoryID"] != nil ||
valid["jdPricePercentage"] != nil || valid["ebaiPricePercentage"] != nil || valid["mtwmPricePercentage"] != nil {
if skuList, err2 := dao.GetSkuByCats(db, []int{categoryID}); err2 == nil && len(skuList) > 0 {
var skuIDs []int
for _, sku := range skuList {
skuIDs = append(skuIDs, sku.ID)
}
if valid["jdCategoryID"] != nil {
dao.SetSkuSyncStatus(db, model.VendorIDJD, skuIDs, model.SyncFlagModifiedMask)
}
// todo 如下逻辑在不同平台同时改pricePercentage与平台分类映射时会不必要的打上多余的标记
var vendorIDs []int
syncStatus := model.SyncFlagModifiedMask
if valid["jdPricePercentage"] != nil {
vendorIDs = append(vendorIDs, model.VendorIDJD)
syncStatus |= model.SyncFlagPriceMask
}
if valid["ebaiPricePercentage"] != nil {
vendorIDs = append(vendorIDs, model.VendorIDEBAI)
syncStatus |= model.SyncFlagPriceMask
} else if valid["ebaiCategoryID"] != nil {
vendorIDs = append(vendorIDs, model.VendorIDEBAI)
}
if valid["mtwmPricePercentage"] != nil {
vendorIDs = append(vendorIDs, model.VendorIDMTWM)
syncStatus |= model.SyncFlagPriceMask
} else if valid["mtwmCategoryID"] != nil {
vendorIDs = append(vendorIDs, model.VendorIDMTWM)
}
if len(vendorIDs) > 0 {
SetStoreSkuSyncStatus2(db, nil, vendorIDs, skuIDs, syncStatus)
}
}
} }
db := dao.GetDB()
if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, cat, valid, userName, nil, model.FieldJdSyncStatus, model.SyncFlagModifiedMask); err == nil {
_, err = CurVendorSync.SyncCategory(ctx, db, categoryID, false, userName) _, err = CurVendorSync.SyncCategory(ctx, db, categoryID, false, userName)
} }
} }
return num, err return num, err
} }
func SetStoreCategorySyncStatus2(db *dao.DaoDB, storeIDs []int, catIDs []int, syncStatus int) (num int64, err error) {
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
for _, vendorID := range CurVendorSync.SingleStoreVendorIDs {
num2, err2 := dao.SetStoreCategorySyncStatus(db, vendorID, storeIDs, catIDs, syncStatus)
if err = err2; err != nil {
return 0, err
}
num += num2
}
dao.Commit(db)
return num, nil
}
func ReorderCategories(ctx *jxcontext.Context, parentID int, categoryIDs []int, userName string) (err error) { func ReorderCategories(ctx *jxcontext.Context, parentID int, categoryIDs []int, userName string) (err error) {
var cats []*model.SkuCategory var cats []*model.SkuCategory
parentCat := &model.SkuCategory{} parentCat := &model.SkuCategory{}
@@ -211,8 +290,8 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike) sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike)
if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
sql += " OR t2.jd_id = ? OR t1.id = ? OR t1.category_id = ?" sql += " OR t1.id = ? OR t2.id = ? OR t2.jd_id = ? OR t1.category_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64) sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
} }
sql += ")" sql += ")"
} }
@@ -340,6 +419,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.name, t1.name,
t1.brand_id, t1.brand_id,
t1.category_id, t1.category_id,
t1.jd_category_id,
t1.is_global, t1.is_global,
t1.unit, t1.unit,
t1.price, t1.price,
@@ -348,6 +428,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.status, t1.status,
t1.is_spu, t1.is_spu,
t1.img_hash_code, t1.img_hash_code,
t1.desc_img,
t1.upc` t1.upc`
if isBySku { if isBySku {
sql += `, sql += `,
@@ -365,6 +446,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.name, t1.name,
t1.brand_id, t1.brand_id,
t1.category_id, t1.category_id,
t1.jd_category_id,
t1.is_global, t1.is_global,
t1.unit, t1.unit,
t1.price, t1.price,
@@ -373,6 +455,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.status, t1.status,
t1.is_spu, t1.is_spu,
t1.img_hash_code, t1.img_hash_code,
t1.desc_img,
t1.upc, t1.upc,
t1.jd_id, t1.jd_id,
t1.jd_sync_status, t1.jd_sync_status,
@@ -463,6 +546,8 @@ func AddSkuName(ctx *jxcontext.Context, skuNameExt *model.SkuNameExt, userName s
skuNameExt.SpecQuality = skuNameExt.Skus[0].SpecQuality skuNameExt.SpecQuality = skuNameExt.Skus[0].SpecQuality
skuNameExt.SpecUnit = skuNameExt.Skus[0].SpecUnit skuNameExt.SpecUnit = skuNameExt.Skus[0].SpecUnit
} }
if globals.EnableStoreWrite {
imgContent, imgMD5, err := jxutils.DownloadFileByURL(skuNameExt.Img) imgContent, imgMD5, err := jxutils.DownloadFileByURL(skuNameExt.Img)
if err != nil { if err != nil {
dao.Rollback(db) dao.Rollback(db)
@@ -482,11 +567,18 @@ func AddSkuName(ctx *jxcontext.Context, skuNameExt *model.SkuNameExt, userName s
skuNameExt.ImgWeimob = imgHintMap[model.VendorIDWSC] skuNameExt.ImgWeimob = imgHintMap[model.VendorIDWSC]
skuNameExt.ImgEbai = imgHintMap[model.VendorIDEBAI] skuNameExt.ImgEbai = imgHintMap[model.VendorIDEBAI]
if skuNameExt.DescImg != "" && getAndSetEbaiUploadRTFShopID() != "" {
skuNameExt.DescImgEbai, err = api.EbaiAPI.SkuUploadRTF(getAndSetEbaiUploadRTFShopID(), ebaiapi.BuildRFTFromImgs(skuNameExt.DescImg))
}
if err != nil {
dao.Rollback(db)
return nil, err
}
}
if err = dao.CreateEntity(db, &skuNameExt.SkuName); err != nil { if err = dao.CreateEntity(db, &skuNameExt.SkuName); err != nil {
dao.Rollback(db) dao.Rollback(db)
return nil, err return nil, err
} }
// beginJDID := jxutils.GenFakeID()
for _, sku := range skuNameExt.Skus { for _, sku := range skuNameExt.Skus {
dao.WrapAddIDCULDEntity(sku, userName) dao.WrapAddIDCULDEntity(sku, userName)
sku.NameID = skuNameExt.ID sku.NameID = skuNameExt.ID
@@ -496,7 +588,6 @@ func AddSkuName(ctx *jxcontext.Context, skuNameExt *model.SkuNameExt, userName s
dao.Rollback(db) dao.Rollback(db)
return nil, err return nil, err
} }
// beginJDID++
} }
for _, placeCode := range skuNameExt.Places { for _, placeCode := range skuNameExt.Places {
placeBind := &model.SkuNamePlaceBind{} placeBind := &model.SkuNamePlaceBind{}
@@ -528,9 +619,12 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
if err = dao.GetEntity(db, skuName); err != nil { if err = dao.GetEntity(db, skuName); err != nil {
return 0, err return 0, err
} }
if _, ok := payload["isSpu"]; ok {
delete(payload, "isSpu") delete(payload, "isSpu")
} delete(payload, "ImgHashCode")
delete(payload, "ImgWeimob")
delete(payload, "ImgEbai")
delete(payload, "descImgEbai")
valid := dao.StrictMakeMapByStructObject(payload, skuName, userName) valid := dao.StrictMakeMapByStructObject(payload, skuName, userName)
valid = utils.RemoveGeneralMapKeys(valid, model.FieldSpecQuality, model.FieldSpecUnit) valid = utils.RemoveGeneralMapKeys(valid, model.FieldSpecQuality, model.FieldSpecUnit)
_, hasPlaces := payload["places"] _, hasPlaces := payload["places"]
@@ -550,6 +644,7 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
} }
err = nil err = nil
} }
if globals.EnableStoreWrite {
if valid["img"] != nil { if valid["img"] != nil {
imgContent, imgMD5, err2 := jxutils.DownloadFileByURL(valid["img"].(string)) imgContent, imgMD5, err2 := jxutils.DownloadFileByURL(valid["img"].(string))
if err = err2; err != nil { if err = err2; err != nil {
@@ -564,6 +659,21 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
valid["ImgWeimob"] = imgHintMap[model.VendorIDWSC] valid["ImgWeimob"] = imgHintMap[model.VendorIDWSC]
valid["ImgEbai"] = imgHintMap[model.VendorIDEBAI] valid["ImgEbai"] = imgHintMap[model.VendorIDEBAI]
} }
if valid["descImg"] != nil {
descImg := valid["descImg"].(string)
if descImg != "" {
if getAndSetEbaiUploadRTFShopID() != "" {
valid["descImgEbai"], err = api.EbaiAPI.SkuUploadRTF(getAndSetEbaiUploadRTFShopID(), ebaiapi.BuildRFTFromImgs(descImg))
if err != nil {
dao.Rollback(db)
return 0, err
}
}
} else {
valid["descImgEbai"] = ""
}
}
}
if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, skuName, valid, userName, nil, model.FieldJdSyncStatus, model.SyncFlagModifiedMask); err == nil && num == 1 { if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, skuName, valid, userName, nil, model.FieldJdSyncStatus, model.SyncFlagModifiedMask); err == nil && num == 1 {
if utils.Interface2Int64WithDefault(payload["isGlobal"], 0) == 0 && payload["places"] != nil { if utils.Interface2Int64WithDefault(payload["isGlobal"], 0) == 0 && payload["places"] != nil {
if places, ok := payload["places"].([]interface{}); ok { if places, ok := payload["places"].([]interface{}); ok {
@@ -587,6 +697,11 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
_, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, sku, nil, userName, map[string]interface{}{ _, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, sku, nil, userName, map[string]interface{}{
model.FieldNameID: nameID, model.FieldNameID: nameID,
}, model.FieldJdSyncStatus, model.SyncFlagModifiedMask) }, model.FieldJdSyncStatus, model.SyncFlagModifiedMask)
if err == nil {
skuIDs, err2 := dao.GetSkuIDByNames(db, []int{nameID})
if err = err2; err == nil && len(skuIDs) > 0 {
_, err = SetStoreSkuSyncStatus2(db, nil, CurVendorSync.SingleStoreVendorIDs, skuIDs, model.SyncFlagModifiedMask)
}
if err == nil { if err == nil {
dao.Commit(db) dao.Commit(db)
_, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName) _, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName)
@@ -594,9 +709,31 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
} }
} }
} }
}
return num, err return num, err
} }
func SetStoreSkuSyncStatus2(db *dao.DaoDB, storeIDs []int, vendorIDs, skuIDs []int, syncStatus int) (num int64, err error) {
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
for _, vendorID := range vendorIDs {
num2, err2 := dao.SetStoreSkuSyncStatus(db, vendorID, storeIDs, skuIDs, syncStatus)
if err = err2; err != nil {
return 0, err
}
num += num2
}
dao.Commit(db)
return num, nil
}
func DeleteSkuName(ctx *jxcontext.Context, nameID int, userName string) (num int64, err error) { func DeleteSkuName(ctx *jxcontext.Context, nameID int, userName string) (num int64, err error) {
db := dao.GetDB() db := dao.GetDB()
dao.Begin(db) dao.Begin(db)
@@ -647,7 +784,7 @@ func AddSku(ctx *jxcontext.Context, nameID int, sku *model.Sku, userName string)
dao.WrapAddIDCULDEntity(sku, userName) dao.WrapAddIDCULDEntity(sku, userName)
sku.JdSyncStatus = model.SyncFlagNewMask sku.JdSyncStatus = model.SyncFlagNewMask
sku.NameID = nameID sku.NameID = nameID
sku.JdID = 0 //jxutils.GenFakeID() sku.JdID = 0
if err = dao.CreateEntity(db, sku); err == nil { if err = dao.CreateEntity(db, sku); err == nil {
result, err2 := GetSkuNames(ctx, "", false, utils.Params2Map("skuID", sku.ID), 0, 0) result, err2 := GetSkuNames(ctx, "", false, utils.Params2Map("skuID", sku.ID), 0, 0)
if err = err2; err == nil { if err = err2; err == nil {
@@ -691,8 +828,10 @@ func UpdateSku(ctx *jxcontext.Context, skuID int, payload map[string]interface{}
`, utils.DefaultTimeValue, skuID, model.SpecialUnit); err != nil { `, utils.DefaultTimeValue, skuID, model.SpecialUnit); err != nil {
return 0, err return 0, err
} }
if _, err = SetStoreSkuSyncStatus2(db, nil, CurVendorSync.SingleStoreVendorIDs, []int{skuID}, model.SyncFlagModifiedMask); err == nil {
dao.Commit(db) dao.Commit(db)
_, err = CurVendorSync.SyncSku(ctx, db, -1, sku.ID, false, false, userName) _, err = CurVendorSync.SyncSku(ctx, db, -1, sku.ID, false, false, userName)
}
} else { } else {
err = ErrEntityNotExist err = ErrEntityNotExist
} }

View File

@@ -10,8 +10,10 @@ import (
"git.rosy.net.cn/baseapi/platformapi/dadaapi" "git.rosy.net.cn/baseapi/platformapi/dadaapi"
"git.rosy.net.cn/baseapi/platformapi/feieapi" "git.rosy.net.cn/baseapi/platformapi/feieapi"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/excel"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/netprinter" "git.rosy.net.cn/jx-callback/business/jxutils/netprinter"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
@@ -19,6 +21,7 @@ import (
"git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel" "git.rosy.net.cn/jx-callback/business/model/legacymodel"
"git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
) )
@@ -59,6 +62,37 @@ var (
ErrCanNotFindVendor = errors.New("vendorID参数不合法") ErrCanNotFindVendor = errors.New("vendorID参数不合法")
) )
var (
dadaDistrictMap = map[string]string{
"苏州工业园区": "工业园区",
"郫都区": "郫县",
"管城回族区": "管城区",
"昆山市": "1",
"常熟市": "1",
"太仓市": "1",
"虞山街道": "虞山镇",
"常福街道": "虞山镇",
}
storeKeyPropertyMap = map[string]int{
"name": 1,
"cityCode": 1,
"districtCode": 1,
"address": 1,
"tel1": 1,
"tel2": 1,
"openTime1": 1,
"closeTime1": 1,
"openTime2": 1,
"closeTime2": 1,
"lng": 1,
"lat": 1,
"deliveryRangeType": 1,
"deliveryRange": 1,
"status": 1,
}
)
// todo 门店绑定信息可以考虑以数组形式返回,而不是现在这样 // todo 门店绑定信息可以考虑以数组形式返回,而不是现在这样
func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interface{}, offset, pageSize int) (retVal *StoresInfo, err error) { func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interface{}, offset, pageSize int) (retVal *StoresInfo, err error) {
sql := ` sql := `
@@ -92,17 +126,46 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
t1.printer_sn, t1.printer_sn,
t1.printer_key, t1.printer_key,
t1.printer_vendor_id, t1.printer_vendor_id,
t1.licence_type,
t1.licence_corp_name,
t1.licence_owner_name,
t1.licence_address,
t1.licence_valid,
t1.licence_expire,
t1.id_name,
t1.id_code,
t1.id_valid,
t1.id_expire,
t1.licence2_image,
t1.licence2_code,
t1.licence2_valid,
t1.licence2_expire,
t1.market_man_name,
t1.market_man_phone,
t1.jx_brand_fee_factor,
t1.market_add_fee_factor,
t1.payee_name,
t1.payee_account_no,
t1.payee_bank_branch_name,
t1.payee_bank_name,
t1.pay_percentage,
t1.operator_name,
t1.operator_phone,
city.name city_name, city.name city_name,
district.name district_name, district.name district_name,
CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m1.vendor_store_id, '", "vendorID":', m1.vendor_id, CONCAT('[', GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m1.vendor_store_id, '", "vendorID":', m1.vendor_id,
', "status":', m1.status, ', "pricePercentage":', m1.price_percentage, ', "vendorStoreName":"', ', "status":', m1.status, ', "pricePercentage":', m1.price_percentage, ', "vendorStoreName":"',
CASE m1.vendor_id CASE m1.vendor_id
WHEN 0 THEN IF(jd.name IS NULL, '', jd.name) WHEN 0 THEN IF(jd.name IS NULL, '', jd.name)
WHEN 3 THEN IF(eb.col_name IS NULL, '', eb.col_name) WHEN 3 THEN IF(eb.col_name IS NULL, '', eb.col_name)
ELSE '' ELSE ''
END, END,
'"}')), ']') store_map_str, '", "isSync":', m1.is_sync, '}')), ']') store_map_str,
CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m2.vendor_store_id, '", "vendorID":', m2.vendor_id, ', "status":', m2.status, "}")), "]") courier_map_str CONCAT('[', GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m2.vendor_store_id, '", "vendorID":', m2.vendor_id,
', "status":', m2.status, '}')), ']') courier_map_str
FROM store t1 FROM store t1
LEFT JOIN place city ON t1.city_code = city.code AND city.level = 2 LEFT JOIN place city ON t1.city_code = city.code AND city.level = 2
LEFT JOIN place district ON t1.district_code = district.code AND district.level = 3 LEFT JOIN place district ON t1.district_code = district.code AND district.level = 3
@@ -228,7 +291,7 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
} }
sql += sqlWhere + ` sql += sqlWhere + `
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53
ORDER BY t1.id DESC ORDER BY t1.id DESC
LIMIT ? OFFSET ?` LIMIT ? OFFSET ?`
pageSize = jxutils.FormalizePageSize(pageSize) pageSize = jxutils.FormalizePageSize(pageSize)
@@ -249,8 +312,9 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
// globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(sql)
var storeList []*StoreExt var storeList []*StoreExt
if err = dao.GetRows(db, &storeList, sql, sqlParams...); err == nil {
mapLimit := false mapLimit := false
if err = dao.GetRows(db, &storeList, sql, sqlParams...); err == nil {
// globals.SugarLogger.Debugf("GetStores, len(storeList):%d", len(storeList))
var ( var (
mapLatitude, mapLongitude float64 mapLatitude, mapLongitude float64
mapRadius int mapRadius int
@@ -289,8 +353,9 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
} }
} }
dao.Commit(db) dao.Commit(db)
if mapLimit {
retVal.MapCenterLng, retVal.MapCenterLat = getMapCenter(retVal.Stores) retVal.MapCenterLng, retVal.MapCenterLat = getMapCenter(retVal.Stores)
}
return retVal, err return retVal, err
} }
@@ -365,6 +430,15 @@ func GetVendorStore(ctx *jxcontext.Context, vendorStoreID string, vendorID int)
return nil, ErrCanNotFindVendor return nil, ErrCanNotFindVendor
} }
func isUpdateStoreNeedSync(valid map[string]interface{}) bool {
for k := range valid {
if storeKeyPropertyMap[k] == 1 {
return true
}
}
return false
}
func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interface{}, userName string) (num int64, err error) { func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interface{}, userName string) (num int64, err error) {
globals.SugarLogger.Debugf("UpdateStore storeID:%d, payload:%s", storeID, utils.Format4Output(payload, false)) globals.SugarLogger.Debugf("UpdateStore storeID:%d, payload:%s", storeID, utils.Format4Output(payload, false))
@@ -458,6 +532,7 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa
dao.Rollback(db) dao.Rollback(db)
}() }()
if num, err = dao.UpdateEntityLogically(db, store, valid, userName, nil); err == nil && num == 1 { if num, err = dao.UpdateEntityLogically(db, store, valid, userName, nil); err == nil && num == 1 {
if isUpdateStoreNeedSync(valid) {
dummy := &model.StoreMap{} dummy := &model.StoreMap{}
_, err2 := dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, dummy, nil, userName, map[string]interface{}{ _, err2 := dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, dummy, nil, userName, map[string]interface{}{
model.FieldStoreID: store.ID, model.FieldStoreID: store.ID,
@@ -467,6 +542,9 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa
globals.SugarLogger.Debugf("UpdateStore track:%s, before call SyncStore", ctx.GetTrackInfo()) globals.SugarLogger.Debugf("UpdateStore track:%s, before call SyncStore", ctx.GetTrackInfo())
_, err = CurVendorSync.SyncStore(ctx, db, -1, store.ID, false, userName) _, err = CurVendorSync.SyncStore(ctx, db, -1, store.ID, false, userName)
} }
} else {
dao.Commit(db)
}
} }
} else { } else {
globals.SugarLogger.Debugf("UpdateStore track:%s, store:%s", ctx.GetTrackInfo(), utils.Format4Output(store, true)) globals.SugarLogger.Debugf("UpdateStore track:%s, store:%s", ctx.GetTrackInfo(), utils.Format4Output(store, true))
@@ -474,6 +552,42 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa
return num, err return num, err
} }
func SetStoreStatus(ctx *jxcontext.Context, storeID, status int) (err error) {
payload := map[string]interface{}{
"status": status,
}
_, err = UpdateStore(ctx, storeID, payload, ctx.GetUserName())
return err
}
func EnableHaveRestStores(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
storeInfo, err := GetStores(ctx, "", map[string]interface{}{
"statuss": string(utils.MustMarshal([]int{model.StoreStatusHaveRest})),
}, 0, model.UnlimitedPageSize)
if err != nil {
return "", err
}
if len(storeInfo.Stores) == 0 {
return "0", nil
}
task := tasksch.NewParallelTask("EnableHaveRestStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
store := batchItemList[0].(*StoreExt)
err = SetStoreStatus(ctx, store.ID, model.StoreStatusOpened)
return nil, err
}, storeInfo.Stores)
tasksch.ManageTask(task).Run()
if !isAsync {
if _, err = task.GetResult(0); err == nil {
hint = utils.Int2Str(len(storeInfo.Stores))
}
} else {
hint = task.GetID()
}
return hint, err
}
func CreateStore(ctx *jxcontext.Context, storeExt *StoreExt, userName string) (id int, err error) { func CreateStore(ctx *jxcontext.Context, storeExt *StoreExt, userName string) (id int, err error) {
globals.SugarLogger.Debugf("CreateStore storeExt:%s", utils.Format4Output(storeExt, false)) globals.SugarLogger.Debugf("CreateStore storeExt:%s", utils.Format4Output(storeExt, false))
store := &storeExt.Store store := &storeExt.Store
@@ -547,6 +661,9 @@ func AddStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID
if storeID == 0 { if storeID == 0 {
return nil, fmt.Errorf("storeID不能为0") return nil, fmt.Errorf("storeID不能为0")
} }
if vendorID != model.VendorIDJD && (storeMap.AutoPickup == 0) {
return nil, fmt.Errorf("非京东平台要求必须自动拣货")
}
userName := ctx.GetUserName() userName := ctx.GetUserName()
if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil { if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
store, err2 := handler.ReadStore(storeMap.VendorStoreID) store, err2 := handler.ReadStore(storeMap.VendorStoreID)
@@ -605,6 +722,11 @@ func DeleteStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendor
} }
func UpdateStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, payload map[string]interface{}, userName string) (num int64, err error) { func UpdateStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, payload map[string]interface{}, userName string) (num int64, err error) {
if vendorID != model.VendorIDJD {
if autoPickup, ok := payload["autoPickup"]; ok && autoPickup == 0 {
return 0, fmt.Errorf("非京东平台要求必须自动拣货")
}
}
storeHandler := CurVendorSync.GetStoreHandler(vendorID) storeHandler := CurVendorSync.GetStoreHandler(vendorID)
if storeHandler == nil { if storeHandler == nil {
return 0, ErrCanNotFindVendor return 0, ErrCanNotFindVendor
@@ -651,7 +773,7 @@ func UpdateStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendor
storeSkuBind := &model.StoreSkuBind{} storeSkuBind := &model.StoreSkuBind{}
if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, storeSkuBind, nil, userName, map[string]interface{}{ if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, storeSkuBind, nil, userName, map[string]interface{}{
model.FieldStoreID: storeID, model.FieldStoreID: storeID,
}, dao.GetSyncStatusStructField(model.VendorNames[vendorID]), model.SyncFlagModifiedMask); err != nil { }, dao.GetSyncStatusStructField(model.VendorNames[vendorID]), model.SyncFlagPriceMask); err != nil {
return 0, err return 0, err
} }
} }
@@ -797,9 +919,22 @@ func AddStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID
dao.WrapAddIDCULDEntity(storeCourierMap, userName) dao.WrapAddIDCULDEntity(storeCourierMap, userName)
storeCourierMap.StoreID = storeID storeCourierMap.StoreID = storeID
storeCourierMap.VendorID = vendorID storeCourierMap.VendorID = vendorID
if db == nil { if db == nil {
db = dao.GetDB() db = dao.GetDB()
} }
if vendorID == model.VendorIDDada {
storeList, err2 := dao.GetMissingDadaStores(db, storeID, false)
if err = err2; err == nil && len(storeList) > 0 {
storeList[0].DadaStoreID = storeCourierMap.VendorStoreID
err = updateOrCreateDadaStore(storeList[0])
} else {
globals.SugarLogger.Debugf("AddStoreCourierMap GetMissingDadaStores error:%v, len(storeList):%d", err, len(storeList))
}
}
if err != nil {
return nil, err
}
dao.Begin(db) dao.Begin(db)
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -809,15 +944,6 @@ func AddStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID
}() }()
if err = dao.CreateEntity(db, storeCourierMap); err == nil { if err = dao.CreateEntity(db, storeCourierMap); err == nil {
dao.Commit(db) dao.Commit(db)
if vendorID == model.VendorIDDada {
storeList, err := dao.GetMissingDadaStores(db, storeID, false)
if err == nil && len(storeList) > 0 {
storeList[0].DadaStoreID = storeCourierMap.VendorStoreID
err = updateOrCreateDadaStore(storeList[0])
} else {
globals.SugarLogger.Debugf("AddStoreCourierMap GetMissingDadaStores error:%v, len(storeList):%d", err, len(storeList))
}
}
outStoreCourierMap = storeCourierMap outStoreCourierMap = storeCourierMap
if err == nil { if err == nil {
_, err = CurVendorSync.SyncStore(ctx, db, storeCourierMap.VendorID, storeID, false, userName) _, err = CurVendorSync.SyncStore(ctx, db, storeCourierMap.VendorID, storeID, false, userName)
@@ -871,24 +997,50 @@ func RefreshMissingDadaStores(ctx *jxcontext.Context, storeID int, isAsync, isCo
task := tasksch.NewParallelTask("RefreshMissingDadaStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, task := tasksch.NewParallelTask("RefreshMissingDadaStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeDetail := batchItemList[0].(*dao.StoreDetail2) storeDetail := batchItemList[0].(*dao.StoreDetail2)
var resultList []interface{}
if storeDetail.DadaStoreID == "" { if storeDetail.DadaStoreID == "" {
if storeDetail.DistrictName == "" || storeDetail.CityName == "" {
return nil, fmt.Errorf("门店:%s的城市码或区码有错误", storeDetail.Name)
}
db := dao.GetDB() db := dao.GetDB()
_, err = AddStoreCourierMap(ctx, db, storeDetail.ID, model.VendorIDDada, &model.StoreCourierMap{ if _, err = AddStoreCourierMap(ctx, db, storeDetail.ID, model.VendorIDDada, &model.StoreCourierMap{
VendorStoreID: utils.Int2Str(storeDetail.ID), VendorStoreID: utils.Int2Str(storeDetail.ID),
Status: model.StoreStatusOpened, Status: model.StoreStatusOpened,
}) }); err == nil {
resultList = append(resultList, 1)
} }
return nil, err }
return resultList, err
}, storeList) }, storeList)
tasksch.HandleTask(task, nil, true).Run() tasksch.HandleTask(task, nil, true).Run()
hint = task.ID
if !isAsync { if !isAsync {
_, err = task.GetResult(0) resultList, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(resultList))
}
} else {
hint = task.ID
} }
return hint, err return hint, err
} }
func updateOrCreateDadaStore(storeDetail *dao.StoreDetail2) (err error) { func updateOrCreateDadaStore(storeDetail *dao.StoreDetail2) (err error) {
if storeDetail.DistrictName == "" {
return fmt.Errorf("门店的区码有问题,请检查")
}
if storeDetail.CityName == "" {
return fmt.Errorf("门店的城市码有问题,请检查")
}
if dadaDistrictMap[storeDetail.DistrictName] != "" {
if dadaDistrictMap[storeDetail.DistrictName] == "1" { // 区镇信息
storeDetail.CityName = storeDetail.DistrictName
storeDetail.DistrictName, _ = api.AutonaviAPI.GetCoordinateTownInfo(jxutils.IntCoordinate2Standard(storeDetail.Lng), jxutils.IntCoordinate2Standard(storeDetail.Lat))
}
if dadaDistrictMap[storeDetail.DistrictName] != "" {
storeDetail.DistrictName = dadaDistrictMap[storeDetail.DistrictName]
}
}
if globals.EnableStoreWrite {
_, err = api.DadaAPI.ShopDetail(storeDetail.DadaStoreID) _, err = api.DadaAPI.ShopDetail(storeDetail.DadaStoreID)
if err != nil { if err != nil {
if codeErr, ok := err.(*utils.ErrorWithCode); ok && codeErr.IntCode() == dadaapi.ResponseCodeShopNotExist { if codeErr, ok := err.(*utils.ErrorWithCode); ok && codeErr.IntCode() == dadaapi.ResponseCodeShopNotExist {
@@ -910,7 +1062,9 @@ func updateOrCreateDadaStore(storeDetail *dao.StoreDetail2) (err error) {
} }
err = api.DadaAPI.ShopUpdate(storeDetail.DadaStoreID, params) err = api.DadaAPI.ShopUpdate(storeDetail.DadaStoreID, params)
} }
}
if err != nil { if err != nil {
err = fmt.Errorf("门店ID:%d,门店名:%s,错误描述:%s", storeDetail.Store.ID, storeDetail.Name, err.Error())
globals.SugarLogger.Debugf("updateOrCreateDadaStore storeID:%d failed with error:%v", storeDetail.ID, err) globals.SugarLogger.Debugf("updateOrCreateDadaStore storeID:%d failed with error:%v", storeDetail.ID, err)
} }
return err return err
@@ -919,3 +1073,91 @@ func updateOrCreateDadaStore(storeDetail *dao.StoreDetail2) (err error) {
func composeDadaStoreName(storeDetail *dao.StoreDetail2) (storeName string) { func composeDadaStoreName(storeDetail *dao.StoreDetail2) (storeName string) {
return storeDetail.Name + "-" + storeDetail.DadaStoreID return storeDetail.Name + "-" + storeDetail.DadaStoreID
} }
func ExportShopsHealthInfo(ctx *jxcontext.Context, vendorIDs, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB()
vendorID := model.VendorIDEBAI
storeMapList, err := dao.GetStoresMapList(db, []int{vendorID}, storeIDs, model.StoreStatusAll)
if err != nil {
return "", err
}
storeMap2 := make(map[string]*model.StoreMap)
for _, v := range storeMapList {
storeMap2[v.VendorStoreID] = v
}
if len(storeMapList) > 0 {
var healthInfoList []interface{}
var excelBin []byte
var excelURL string
task := tasksch.NewSeqTask(fmt.Sprintf("ExportShopHealthInfo[%s]", model.VendorChineseNames[vendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
subTask := tasksch.NewParallelTask(fmt.Sprintf("ExportShopHealthInfo2[%s]", model.VendorChineseNames[vendorID]), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeMap := batchItemList[0].(*model.StoreMap)
healthInfo, err := ebai.CurPurchaseHandler.GetShopHealthInfo(storeMap.VendorStoreID)
if err == nil {
retVal = []map[string]interface{}{healthInfo}
}
return retVal, err
}, storeMapList)
tasksch.AddChild(task, subTask).Run()
healthInfoList, err = subTask.GetResult(0)
if isContinueWhenError && err != nil && len(healthInfoList) > 0 {
err = nil
}
case 1:
var healthInfoList2 []map[string]interface{}
for _, v := range healthInfoList {
mapInfo := v.(map[string]interface{})
mapInfo["real_shop_id"] = storeMap2[utils.Interface2String(mapInfo["merchant_id"])].StoreID
healthInfoList2 = append(healthInfoList2, mapInfo)
}
excelConf := &excel.Obj2ExcelSheetConfig{
Title: "饿百门店情况导出",
Data: healthInfoList2,
CaptionList: []string{
"real_shop_id",
"merchant_id",
"merchant_name",
"hours",
"sku_num",
"target_jiedan",
"is_healthy",
"bad_order_rate",
},
}
excelBin = excel.Obj2Excel([]*excel.Obj2ExcelSheetConfig{excelConf})
case 2:
keyPart := []string{
ctx.GetUserName(),
"饿百门店情况",
}
keyPart = append(keyPart, time.Now().Format("20060102T150405")+".xlsx")
key := "export/" + strings.Join(keyPart, "_")
excelURL, err = jxutils.UploadExportContent(excelBin, key)
if err == nil {
task.SetNoticeMsg(excelURL)
}
globals.SugarLogger.Debugf("导出饿百门店情况excelURL:%s, err:%v", excelURL, err)
}
return nil, err
}, 3)
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
if err == nil {
hint = excelURL
}
} else {
hint = task.GetID()
}
}
return hint, err
}
func GetCorporationInfo(ctx *jxcontext.Context, licenceCode string) (corporationInfo *jdapi.CorporationInfo, err error) {
corporationInfo, err = api.JdAPI.GetCorporationInfo("", licenceCode)
return corporationInfo, err
}

View File

@@ -6,8 +6,14 @@ import (
"math" "math"
"sort" "sort"
"strconv" "strconv"
"sync"
"time" "time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/auth/weixin" "git.rosy.net.cn/jx-callback/business/jxcallback/auth/weixin"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
@@ -20,6 +26,8 @@ import (
) )
const ( const (
MaxSkuUnitPrice = 100000
CopyStoreSkuModeFresh = "fresh" CopyStoreSkuModeFresh = "fresh"
CopyStoreSkuModeUpdate = "update" CopyStoreSkuModeUpdate = "update"
// CopyStoreSkuModeAdd = "add" // CopyStoreSkuModeAdd = "add"
@@ -48,24 +56,26 @@ type StoreSkuNamesInfo struct {
// UpdateStoreSku用API调用时 // UpdateStoreSku用API调用时
type StoreSkuBindSkuInfo struct { type StoreSkuBindSkuInfo struct {
SkuID int `json:"skuID"` SkuID int `json:"skuID"`
IsSale int `json:"isSale"` // -1不可售0忽略1可售 IsSale int `json:"isSale,omitempty"` // -1不可售0忽略1可售
ElmID int64 `json:"elmID"` ElmID int64 `json:"elmID,omitempty"`
EbaiID int64 `json:"ebaiID"` EbaiID int64 `json:"ebaiID,omitempty"`
} }
// UpdateStoreSku用API调用时 // UpdateStoreSku用API调用时
type StoreSkuBindInfo struct { type StoreSkuBindInfo struct {
StoreID int `json:"storeID"`
NameID int `json:"nameID"` NameID int `json:"nameID"`
UnitPrice int `json:"unitPrice"` // 对于是份的SKU就是单价每斤价格其它则为总价 UnitPrice int `json:"unitPrice"` // 对于是份的SKU就是单价每斤价格其它则为总价
IsFocus int `json:"isFocus"` // -1不关注0忽略1关注 IsFocus int `json:"isFocus"` // -1不关注0忽略1关注
IsSale int `json:"isSale"` // -1不可售0忽略1可售 IsSale int `json:"isSale"` // -1不可售0忽略1可售
SubStoreID int `json:"subStoreID"` SubStoreID int `json:"subStoreID,omitempty"`
Skus []*StoreSkuBindSkuInfo `json:"skus"` Skus []*StoreSkuBindSkuInfo `json:"skus,omitempty"`
} }
type tStoreSkuBindAndSpec struct { type tStoreSkuBindAndSpec struct {
model.StoreSkuBind model.StoreSkuBind
Name string
SpecQuality float32 SpecQuality float32
SpecUnit string SpecUnit string
SkuNamePrice int SkuNamePrice int
@@ -88,6 +98,11 @@ type StoreOpRequestInfo struct {
UnitPrice int `json:"unitPrice"` UnitPrice int `json:"unitPrice"`
} }
const (
maxStoreNameBind = 3000 // 最大门店SkuName bind个数
maxStoreNameBind2 = 10000 // 最大门店乘SkuName个数
)
func GetStoreSkus(ctx *jxcontext.Context, storeID int, isFocus bool, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) { func GetStoreSkus(ctx *jxcontext.Context, storeID int, isFocus bool, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) {
return GetStoresSkus(ctx, []int{storeID}, isFocus, keyword, isBySku, params, offset, pageSize) return GetStoresSkus(ctx, []int{storeID}, isFocus, keyword, isBySku, params, offset, pageSize)
} }
@@ -102,7 +117,7 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
JOIN store t3 ON t3.id IN (` + dao.GenQuestionMarks(len(storeIDs)) + `) JOIN store t3 ON t3.id IN (` + dao.GenQuestionMarks(len(storeIDs)) + `)
LEFT JOIN store_sku_bind t4 ON t4.sku_id = t2.id AND t4.deleted_at = ? AND t4.store_id = t3.id LEFT JOIN store_sku_bind t4 ON t4.sku_id = t2.id AND t4.deleted_at = ? AND t4.store_id = t3.id
LEFT JOIN sku_name_place_bind t5 ON t1.id = t5.name_id AND t3.city_code = t5.place_code LEFT JOIN sku_name_place_bind t5 ON t1.id = t5.name_id AND t3.city_code = t5.place_code
WHERE t1.deleted_at = ? AND (t1.is_global = 1 OR t5.id IS NOT NULL OR t4.status = ?)/* AND t1.status = ?*/ WHERE t1.deleted_at = ? AND (t1.is_global = 1 OR t5.id IS NOT NULL OR 1 = ?)/* AND t1.status = ?*/
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
@@ -110,7 +125,7 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
storeIDs, storeIDs,
utils.DefaultTimeValue, utils.DefaultTimeValue,
utils.DefaultTimeValue, utils.DefaultTimeValue,
model.SkuStatusNormal, utils.Bool2Int(isFocus),
// model.SkuStatusNormal, // model.SkuStatusNormal,
} }
if isFocus { if isFocus {
@@ -122,12 +137,12 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
} }
if keyword != "" { if keyword != "" {
keywordLike := "%" + keyword + "%" keywordLike := "%" + keyword + "%"
sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t2.comment LIKE ?" sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t1.upc LIKE ? OR t2.comment LIKE ?"
sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike) sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike)
if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
sql += " OR t2.jd_id = ? OR t4.id = ?" sql += " OR t1.id = ? OR t2.id = ? OR t2.jd_id = ? OR t4.ebai_id = ? OR t4.mtwm_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64) sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
} }
sql += ")" sql += ")"
} }
@@ -197,7 +212,8 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
sqlParams = append(sqlParams, skuIDs) sqlParams = append(sqlParams, skuIDs)
} }
} }
if isFocus && params["fromStatus"] != nil { if isFocus {
if params["fromStatus"] != nil {
fromStatus := params["fromStatus"].(int) fromStatus := params["fromStatus"].(int)
toStatus := fromStatus toStatus := fromStatus
if params["toStatus"] != nil { if params["toStatus"] != nil {
@@ -206,6 +222,27 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
sql += " AND t4.status >= ? AND t4.status <= ?" sql += " AND t4.status >= ? AND t4.status <= ?"
sqlParams = append(sqlParams, fromStatus, toStatus) sqlParams = append(sqlParams, fromStatus, toStatus)
} }
if params["jdSyncStatus"] != nil || params["ebaiSyncStatus"] != nil || params["mtwmSyncStatus"] != nil {
realVendorMap, err2 := getValidStoreVendorMap(db, storeIDs)
if err = err2; err != nil {
return nil, err
}
sql += " AND ( 1 = 0"
if params["jdSyncStatus"] != nil && realVendorMap[model.VendorIDJD] == 1 {
sql += " OR (t4.jd_sync_status & ? <> 0 AND t4.jd_sync_status & ? = 0 AND t2.jd_id <> 0 AND t1.status = ? AND t2.status = ?)"
sqlParams = append(sqlParams, params["jdSyncStatus"], model.SyncFlagDeletedMask|model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
}
if params["ebaiSyncStatus"] != nil && realVendorMap[model.VendorIDEBAI] == 1 {
sql += " OR (t4.ebai_sync_status & ? <> 0 AND t4.ebai_sync_status & ? = 0)"
sqlParams = append(sqlParams, params["ebaiSyncStatus"], model.SyncFlagDeletedMask|model.SyncFlagNewMask)
}
if params["mtwmSyncStatus"] != nil && realVendorMap[model.VendorIDMTWM] == 1 {
sql += " OR (t4.mtwm_sync_status & ? <> 0 AND t4.mtwm_sync_status & ? = 0)"
sqlParams = append(sqlParams, params["mtwmSyncStatus"], model.SyncFlagDeletedMask|model.SyncFlagNewMask)
}
sql += ")"
}
}
sql += ` sql += `
GROUP BY GROUP BY
t1.id, t1.id,
@@ -392,6 +429,80 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
return skuNamesInfo, err return skuNamesInfo, err
} }
func getValidStoreVendorMap(db *dao.DaoDB, storeIDs []int) (realVendorMap map[int]int, err error) {
storeMapList, err := dao.GetStoresMapList(db, nil, storeIDs, model.StoreStatusAll)
if err != nil {
return nil, err
}
realVendorMap = make(map[int]int)
for _, v := range storeMapList {
if v.IsSync != 0 {
realVendorMap[v.VendorID] = 1
}
}
return realVendorMap, nil
}
func GetStoreAbnormalSkuCount(ctx *jxcontext.Context, storeID, syncStatus int, isBySku bool, params map[string]interface{}) (count int, err error) {
db := dao.GetDB()
realVendorMap, err2 := getValidStoreVendorMap(db, []int{storeID})
if err = err2; err != nil {
return 0, err
}
sql := `
SELECT COUNT(*) ct`
if !isBySku {
sql += `
FROM (
SELECT DISTINCT t3.id`
}
sql += `
FROM store_sku_bind t1
JOIN sku t2 ON t2.id = t1.sku_id AND t2.deleted_at = ?
JOIN sku_name t3 ON t3.id = t2.name_id AND t3.deleted_at = ?
WHERE t1.deleted_at = ? AND t1.store_id = ? AND
((t2.status = ? AND t3.status = ?) OR t1.status = ?) AND
(1 = 0`
sqlParams := []interface{}{
utils.DefaultTimeValue,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
storeID,
model.SkuStatusNormal,
model.SkuStatusNormal,
model.SkuStatusNormal,
}
for _, vendorID := range []int{model.VendorIDJD, model.VendorIDEBAI, model.VendorIDMTWM} {
if realVendorMap[vendorID] != 0 {
prefix := dao.ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql += fmt.Sprintf(" OR (t1.%s_sync_status & ? <> 0 AND t1.%s_sync_status & ? = 0", prefix, prefix)
sqlParams = append(sqlParams, syncStatus, model.SyncFlagDeletedMask|model.SyncFlagNewMask)
if model.MultiStoresVendorMap[vendorID] == 1 {
sql += fmt.Sprintf(" AND t2.%s_id <> 0 AND t2.status = ? AND t3.status = ?", prefix)
sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal)
}
sql += ")"
}
}
sql += ")"
if params["fromStatus"] != nil {
fromStatus := params["fromStatus"].(int)
toStatus := fromStatus
if params["toStatus"] != nil {
toStatus = params["toStatus"].(int)
}
sql += " AND t1.status >= ? AND t1.status <= ?"
sqlParams = append(sqlParams, fromStatus, toStatus)
}
if !isBySku {
sql += `
) t1`
}
err = dao.GetRow(db, &count, sql, sqlParams...)
return count, err
}
func GetStoresSkusSaleInfo(ctx *jxcontext.Context, storeIDs []int, skuIDs []int, fromTime, toTime time.Time, fromCount, toCount int) (saleInfoList []*SkuSaleInfo, err error) { func GetStoresSkusSaleInfo(ctx *jxcontext.Context, storeIDs []int, skuIDs []int, fromTime, toTime time.Time, fromCount, toCount int) (saleInfoList []*SkuSaleInfo, err error) {
globals.SugarLogger.Debugf("GetStoresSkusSaleInfo storeIDs:%v, fromTime:%v, toTime:%v, fromCount:%d, toCount:%d", storeIDs, fromTime, toTime, fromCount, toCount) globals.SugarLogger.Debugf("GetStoresSkusSaleInfo storeIDs:%v, fromTime:%v, toTime:%v, fromCount:%d, toCount:%d", storeIDs, fromTime, toTime, fromCount, toCount)
@@ -436,24 +547,65 @@ func UpdateStoreSku(ctx *jxcontext.Context, storeID int, skuBindInfo *StoreSkuBi
} }
func UpdateStoreSkus(ctx *jxcontext.Context, storeID int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) { func UpdateStoreSkus(ctx *jxcontext.Context, storeID int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
// skuIDs, err := updateStoresSkusWithoutSync(ctx, []int{storeID}, skuBindInfos)
// num = int64(len(skuIDs))
// if err == nil && num > 0 {
// db := dao.GetDB()
// _, err = CurVendorSync.SyncStoresSkus(ctx, db, nil, []int{storeID}, skuIDs, false, false)
// return int64(len(skuIDs)), err
// }
// return 0, err
return UpdateStoresSkus(ctx, []int{storeID}, skuBindInfos, isAsync, isContinueWhenError) return UpdateStoresSkus(ctx, []int{storeID}, skuBindInfos, isAsync, isContinueWhenError)
} }
func UpdateStoresSkus(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) { func UpdateStoresSkus(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
var num int64 var num int64
skuIDs, err := updateStoresSkusWithoutSync(ctx, storeIDs, skuBindInfos) db := dao.GetDB()
skuIDs, err := updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos)
if err != nil {
return "", err
}
num = int64(len(skuIDs)) num = int64(len(skuIDs))
if num > 0 { if num > 0 {
hint, err = CurVendorSync.SyncStoresSkus(ctx, db, nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError)
}
if num == 0 || !isAsync || hint == "" {
hint = utils.Int64ToStr(num)
}
return hint, err
}
func UpdateStoresSkusByBind(ctx *jxcontext.Context, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
if len(skuBindInfos) > maxStoreNameBind {
return "", fmt.Errorf("门店商品信息大于%d", maxStoreNameBind)
}
skuBindInfosMap := make(map[int][]*StoreSkuBindInfo)
storeIDMap := make(map[int]int)
for _, v := range skuBindInfos {
if v.StoreID > 9 {
skuBindInfosMap[v.StoreID] = append(skuBindInfosMap[v.StoreID], v)
storeIDMap[v.StoreID] = 1
}
}
storeIDs := jxutils.IntMap2List(storeIDMap)
sort.Ints(storeIDs)
var num int64
skuIDMap := make(map[int]int)
db := dao.GetDB() db := dao.GetDB()
hint, err = CurVendorSync.SyncStoresSkus(ctx, db, nil, storeIDs, skuIDs, isAsync, isContinueWhenError) dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
for _, storeID := range storeIDs {
skuIDs, err2 := updateStoresSkusWithoutSync(ctx, db, []int{storeID}, skuBindInfosMap[storeID])
if err = err2; err != nil {
dao.Rollback(db)
return "", err
}
for _, v := range skuIDs {
skuIDMap[v] = 1
}
num += int64(len(skuIDs))
}
dao.Commit(db)
if num > 0 {
skuIDs := jxutils.IntMap2List(skuIDMap)
hint, err = CurVendorSync.SyncStoresSkus(ctx, db, nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError)
} }
if num == 0 || !isAsync || hint == "" { if num == 0 || !isAsync || hint == "" {
hint = utils.Int64ToStr(num) hint = utils.Int64ToStr(num)
@@ -504,13 +656,41 @@ func checkStoresSkusSaleCity(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []i
return err return err
} }
func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (needSyncSkus []int, err error) { func uniqueStoreIDs(storeIDs []int) []int {
storeIDMap := make(map[int]int)
for _, v := range storeIDs {
storeIDMap[v] = 1
}
return jxutils.IntMap2List(storeIDMap)
}
func uniqueStoreNameBind(skuBindInfos []*StoreSkuBindInfo) (outSkuBindInfos []*StoreSkuBindInfo) {
nameIDMap := make(map[int]int)
for _, v := range skuBindInfos {
if nameIDMap[v.NameID] != 1 {
outSkuBindInfos = append(outSkuBindInfos, v)
nameIDMap[v.NameID] = 1
}
}
return outSkuBindInfos
}
func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (needSyncSkus []int, err error) {
if len(storeIDs)*len(skuBindInfos) > maxStoreNameBind2 {
return nil, fmt.Errorf("门店商品信息大于%d", maxStoreNameBind2)
}
storeIDs = uniqueStoreIDs(storeIDs)
skuBindInfos = uniqueStoreNameBind(skuBindInfos)
sort.Ints(storeIDs) sort.Ints(storeIDs)
globals.SugarLogger.Debugf("updateStoresSkusWithoutSync, storeIDs:%v, skuBindInfos:%s", storeIDs, utils.Format4Output(skuBindInfos, false)) globals.SugarLogger.Debugf("updateStoresSkusWithoutSync, storeIDs:%v, skuBindInfos:%s", storeIDs, utils.Format4Output(skuBindInfos, false))
db := dao.GetDB() if db == nil {
if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil { db = dao.GetDB()
return nil, err
} }
// if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil {
// return nil, err
// }
if storeIDs, skuBindInfos, err = filterStorePriceChange(ctx, storeIDs, skuBindInfos); err != nil { if storeIDs, skuBindInfos, err = filterStorePriceChange(ctx, storeIDs, skuBindInfos); err != nil {
return nil, err return nil, err
} }
@@ -527,27 +707,45 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
}() }()
for _, storeID := range storeIDs { for _, storeID := range storeIDs {
for _, skuBindInfo := range skuBindInfos { for _, skuBindInfo := range skuBindInfos {
// 关注且没有给价时需要尝试从store_sku_bind中得到已有的单价
needGetExistingUnitPrice := skuBindInfo.UnitPrice == 0 && skuBindInfo.IsFocus == 1
inSkuBinds := skuBindInfo.Skus inSkuBinds := skuBindInfo.Skus
var allBinds []*tStoreSkuBindAndSpec var allBinds []*tStoreSkuBindAndSpec
sql := ` sql := `
SELECT SELECT
t2.*, t2.*,
t1.id real_sku_id, t1.spec_quality, t1.spec_unit, t1.id real_sku_id, t1.spec_quality, t1.spec_unit,`
t3.price sku_name_price, t3.unit sku_name_unit if needGetExistingUnitPrice {
sql += " IF(t5.unit_price > 0, t5.unit_price, t3.price) sku_name_price,"
}
sql += `
t3.unit sku_name_unit, t3.name
FROM sku t1 FROM sku t1
JOIN store ts ON ts.id = ? AND ts.deleted_at = ? JOIN store ts ON ts.id = ? AND ts.deleted_at = ?
LEFT JOIN store_sku_bind t2 ON t2.sku_id = t1.id AND t2.store_id = ts.id AND t2.deleted_at = ? LEFT JOIN store_sku_bind t2 ON t2.sku_id = t1.id AND t2.store_id = ts.id AND t2.deleted_at = ?
JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ? JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ?`
WHERE t1.name_id = ? AND t1.deleted_at = ?
FOR UPDATE`
sqlParams := []interface{}{ sqlParams := []interface{}{
storeID, storeID,
utils.DefaultTimeValue, utils.DefaultTimeValue,
utils.DefaultTimeValue, utils.DefaultTimeValue,
utils.DefaultTimeValue, utils.DefaultTimeValue,
skuBindInfo.NameID,
utils.DefaultTimeValue,
} }
if needGetExistingUnitPrice {
sql += `
LEFT JOIN (
SELECT t7.store_id, t8.name_id, CAST(AVG(t7.unit_price) AS SIGNED) unit_price
FROM store_sku_bind t7
JOIN sku t8 ON t8.id = t7.sku_id AND t8.name_id = ?
WHERE t7.deleted_at = ? AND t7.store_id = ?
GROUP BY 1,2
) t5 ON t5.store_id = ts.id AND t5.name_id = t1.name_id`
sqlParams = append(sqlParams, skuBindInfo.NameID, utils.DefaultTimeValue, storeID)
}
sql += `
WHERE t1.name_id = ? AND t1.deleted_at = ?
FOR UPDATE`
sqlParams = append(sqlParams, skuBindInfo.NameID, utils.DefaultTimeValue)
// globals.SugarLogger.Debug(sql)
if err = dao.GetRows(db, &allBinds, sql, sqlParams...); err == nil { if err = dao.GetRows(db, &allBinds, sql, sqlParams...); err == nil {
if len(allBinds) > 0 { if len(allBinds) > 0 {
// globals.SugarLogger.Debug(utils.Format4Output(allBinds, false)) // globals.SugarLogger.Debug(utils.Format4Output(allBinds, false))
@@ -557,6 +755,10 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
} }
unitPrice := 0 unitPrice := 0
if skuBindInfo.UnitPrice != 0 { if skuBindInfo.UnitPrice != 0 {
if skuBindInfo.UnitPrice > MaxSkuUnitPrice {
dao.Rollback(db)
return nil, fmt.Errorf("商品:%s价格:%s太夸张", allBinds[0].Name, jxutils.IntPrice2StandardCurrencyString(int64(skuBindInfo.UnitPrice)))
}
unitPrice = skuBindInfo.UnitPrice unitPrice = skuBindInfo.UnitPrice
} else { } else {
unitPrice = allBinds[0].UnitPrice unitPrice = allBinds[0].UnitPrice
@@ -650,7 +852,7 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
updateFieldMap[model.FieldUpdatedAt] = 1 updateFieldMap[model.FieldUpdatedAt] = 1
updateFieldMap[model.FieldLastOperator] = 1 updateFieldMap[model.FieldLastOperator] = 1
setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask) // setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask)
dao.WrapUpdateULEntity(skuBind, userName) dao.WrapUpdateULEntity(skuBind, userName)
if num, err = dao.UpdateEntity(db, skuBind /*, utils.Map2KeySlice(updateFieldMap)...*/); err != nil { if num, err = dao.UpdateEntity(db, skuBind /*, utils.Map2KeySlice(updateFieldMap)...*/); err != nil {
dao.Rollback(db) dao.Rollback(db)
@@ -712,11 +914,11 @@ func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBind
} }
if num, err = dao.UpdateEntityLogically(db, skuBind, map[string]interface{}{ if num, err = dao.UpdateEntityLogically(db, skuBind, map[string]interface{}{
model.FieldStatus: skuBind.Status, model.FieldStatus: skuBind.Status,
model.FieldJdSyncStatus: skuBind.JdSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask, model.FieldJdSyncStatus: skuBind.JdSyncStatus | model.SyncFlagSaleMask,
model.FieldEbaiSyncStatus: skuBind.EbaiSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask, model.FieldEbaiSyncStatus: skuBind.EbaiSyncStatus | model.SyncFlagSaleMask,
model.FieldMtwmSyncStatus: skuBind.MtwmSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask, model.FieldMtwmSyncStatus: skuBind.MtwmSyncStatus | model.SyncFlagSaleMask,
model.FieldElmSyncStatus: skuBind.ElmSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask, model.FieldElmSyncStatus: skuBind.ElmSyncStatus | model.SyncFlagSaleMask,
model.FieldWscSyncStatus: skuBind.WscSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask, model.FieldWscSyncStatus: skuBind.WscSyncStatus | model.SyncFlagSaleMask,
}, userName, map[string]interface{}{ }, userName, map[string]interface{}{
model.FieldStoreID: storeID, model.FieldStoreID: storeID,
model.FieldSkuID: v.SkuID, model.FieldSkuID: v.SkuID,
@@ -734,7 +936,21 @@ func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBind
return needSyncSkus, err return needSyncSkus, err
} }
func uniqueStoreSkuBind(skuBindSkuInfos []*StoreSkuBindSkuInfo) (outSkuBindSkuInfos []*StoreSkuBindSkuInfo) {
skuIDMap := make(map[int]int)
for _, v := range skuBindSkuInfos {
if skuIDMap[v.SkuID] != 1 {
outSkuBindSkuInfos = append(outSkuBindSkuInfos, v)
skuIDMap[v.SkuID] = 1
}
}
return outSkuBindSkuInfos
}
func UpdateStoresSkusSale(ctx *jxcontext.Context, storeIDs []int, skuBindSkuInfos []*StoreSkuBindSkuInfo, userName string, isAsync, isContinueWhenError bool) (hint string, err error) { func UpdateStoresSkusSale(ctx *jxcontext.Context, storeIDs []int, skuBindSkuInfos []*StoreSkuBindSkuInfo, userName string, isAsync, isContinueWhenError bool) (hint string, err error) {
storeIDs = uniqueStoreIDs(storeIDs)
skuBindSkuInfos = uniqueStoreSkuBind(skuBindSkuInfos)
var num int64 var num int64
for _, storeID := range storeIDs { for _, storeID := range storeIDs {
skuIDs, err2 := updateStoreSkusSaleWithoutSync(ctx, storeID, skuBindSkuInfos, userName) skuIDs, err2 := updateStoreSkusSaleWithoutSync(ctx, storeID, skuBindSkuInfos, userName)
@@ -749,7 +965,7 @@ func UpdateStoresSkusSale(ctx *jxcontext.Context, storeIDs []int, skuBindSkuInfo
skuIDs = append(skuIDs, v.SkuID) skuIDs = append(skuIDs, v.SkuID)
} }
db := dao.GetDB() db := dao.GetDB()
hint, err = CurVendorSync.SyncStoresSkus(ctx, db, nil, storeIDs, skuIDs, isAsync, isContinueWhenError) hint, err = CurVendorSync.SyncStoresSkus(ctx, db, nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError)
} }
if num == 0 || !isAsync || hint == "" { if num == 0 || !isAsync || hint == "" {
hint = utils.Int64ToStr(num) hint = utils.Int64ToStr(num)
@@ -823,11 +1039,11 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID, toStoreID int, copyMode
now, now,
pricePercentage, pricePercentage,
pricePercentage, pricePercentage,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask, model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask, model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask, model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask, model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask, model.SyncFlagPriceMask,
toStoreID, toStoreID,
utils.DefaultTimeValue, utils.DefaultTimeValue,
} }
@@ -927,11 +1143,11 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID, toStoreID int, copyMode
pricePercentage, pricePercentage,
pricePercentage, pricePercentage,
pricePercentage, pricePercentage,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask, model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask, model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask, model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask, model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask, model.SyncFlagStoreSkuOnlyMask,
toStoreID, toStoreID,
utils.DefaultTimeValue, utils.DefaultTimeValue,
} }
@@ -1349,3 +1565,186 @@ func checkStoreExisting(db *dao.DaoDB, storeID int) (err error) {
} }
return nil return nil
} }
func RefreshStoresSkuByVendor(ctx *jxcontext.Context, storeIDs []int, vendorID int, isAsync bool) (hint string, err error) {
if vendorID != model.VendorIDJD {
return "", fmt.Errorf("此功能当前只支持京东到家平台")
}
db := dao.GetDB()
storeMapList, err := dao.GetStoresMapList(db, nil, storeIDs, model.StoreStatusAll)
if err != nil {
return "", err
}
if len(storeMapList) != len(storeIDs) {
return "", fmt.Errorf("门店绑定信息不匹配,请确定门店绑定且只绑定了京东平台")
}
storeMap := make(map[int]*model.StoreMap)
for _, v := range storeMapList {
if v.VendorID != vendorID {
return "", fmt.Errorf("门店%d绑定的不是京东", v.StoreID)
}
storeMap[v.StoreID] = v
}
handler := partner.GetPurchasePlatformFromVendorID(vendorID)
var storeSkuList []*model.StoreSkuBind
rootTask := tasksch.NewSeqTask(fmt.Sprintf("根据厂家门店商品信息相应刷新本地数据:%v", storeIDs), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
storeSkuList, err = handler.GetStoresSku(ctx, task, storeIDs)
case 1:
if len(storeSkuList) > 0 {
var skuList []*model.SkuAndName
skuList, err = dao.GetSkus(db, nil, nil, nil, nil)
if err == nil {
skuNameMap := make(map[int]*model.SkuName)
skuMap := make(map[int]*model.SkuAndName)
for _, sku := range skuList {
if skuNameMap[sku.NameID] == nil {
skuNameMap[sku.NameID] = &model.SkuName{
Unit: sku.Unit,
}
}
skuMap[sku.ID] = sku
}
for _, v := range storeSkuList {
sku := skuMap[v.SkuID]
skuName := skuNameMap[sku.NameID]
if skuName.IsGlobal == 0 && (jxutils.IsSkuSpecial(sku.SpecQuality, sku.SpecUnit) || skuName.Unit != model.SpecialUnit) {
skuName.Price = v.Price
skuName.IsGlobal = 1 // 标准价
}
}
for _, v := range storeSkuList {
sku := skuMap[v.SkuID]
skuName := skuNameMap[sku.NameID]
if skuName.IsGlobal == 0 {
if skuName.Price == 0 {
skuName.Price = jxutils.CaculateUnitPrice(v.Price, sku.SpecQuality, sku.SpecUnit, skuName.Unit)
} else {
skuName.Price = (skuName.Price + jxutils.CaculateUnitPrice(v.Price, sku.SpecQuality, sku.SpecUnit, skuName.Unit)) / 2
}
}
}
for _, v := range storeSkuList {
pricePercentage := int(storeMap[v.StoreID].PricePercentage)
skuName := skuNameMap[skuMap[v.SkuID].NameID]
v.Price = jxutils.CaculateSkuPriceFromVendor(v.Price, pricePercentage, 0)
v.UnitPrice = jxutils.CaculateSkuPriceFromVendor(skuName.Price, pricePercentage, 0)
dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
setStoreSkuBindStatus(v, model.SyncFlagNewMask)
v.JdSyncStatus = 0
}
}
}
case 2:
if len(storeSkuList) > 0 {
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
if _, err = dao.ExecuteSQL(db, `
DELETE t1
FROM store_sku_bind t1
WHERE t1.store_id IN (
`+dao.GenQuestionMarks(len(storeIDs))+")", storeIDs); err == nil {
if err = dao.CreateMultiEntities(db, storeSkuList); err == nil {
hint = utils.Int2Str(len(storeSkuList))
dao.Commit(db)
}
}
}
}
return nil, err
}, 3)
tasksch.ManageTask(rootTask).Run()
if isAsync {
hint = rootTask.GetID()
} else {
_, err = rootTask.GetResult(0)
}
return hint, err
}
func GetVendorStoreSkusInfo(ctx *jxcontext.Context, storeID int, vendorIDs, skuIDs []int, isContinueWhenError bool) (skuVendorMap map[int][]*partner.BareStoreSkuInfo, err error) {
globals.SugarLogger.Debugf("GetVendorStoreSkusInfo, storeID:%d, vendorIDs:%v, skuID:%v", storeID, vendorIDs, skuIDs)
db := dao.GetDB()
var locker sync.RWMutex
skuVendorMap = make(map[int][]*partner.BareStoreSkuInfo)
_, err = CurVendorSync.LoopStoresMap(ctx, db, fmt.Sprintf("GetVendorStoreSkusInfo storeID:%d", storeID), false, false, vendorIDs, []int{storeID},
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := CurVendorSync.GetStoreHandler(loopMapInfo.VendorID); handler != nil {
storeSkuList, err2 := dao.GetStoreSkus2(db, loopMapInfo.VendorID, storeID, skuIDs, false)
if err = err2; err == nil && len(storeSkuList) > 0 {
bareStoreSkuInfoList := make([]*partner.BareStoreSkuInfo, len(skuIDs))
for k, v := range storeSkuList {
bareStoreSkuInfoList[k] = &partner.BareStoreSkuInfo{
SkuID: v.SkuID,
VendorSkuID: v.VendorSkuID,
}
}
outBareStoreSkuInfoList, err2 := handler.GetStoreSkusInfo(ctx, t, loopMapInfo.StoreMapList[0].StoreID, loopMapInfo.StoreMapList[0].VendorStoreID, bareStoreSkuInfoList)
if err = err2; err == nil && outBareStoreSkuInfoList != nil {
locker.Lock()
defer locker.Unlock()
skuVendorMap[loopMapInfo.VendorID] = outBareStoreSkuInfoList
}
}
}
return nil, err
}, true)
if err != nil {
skuVendorMap = nil
}
return skuVendorMap, err
}
func SyncJdStoreProducts(ctx *jxcontext.Context, storeIDs, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB()
isManageIt := len(storeIDs) != 1 || len(skuIDs) == 0 || len(skuIDs) > 8
hint, err = CurVendorSync.LoopStoresMap(ctx, db, fmt.Sprintf("京东商家商品状态同步:%v", storeIDs), isAsync, isManageIt, []int{model.VendorIDJD}, storeIDs,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := partner.GetPurchasePlatformFromVendorID(loopMapInfo.VendorID); handler != nil {
jdHandler := handler.(*jd.PurchaseHandler)
hint, err2 := jdHandler.SyncStoreProducts(ctx, t, loopMapInfo.StoreMapList[0].StoreID, skuIDs, false, isContinueWhenError)
if err = err2; err == nil {
retVal = []interface{}{hint}
}
}
return retVal, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
}, isContinueWhenError)
return hint, err
}
func GetMissingStoreSkuFromOrder(ctx *jxcontext.Context, fromTime time.Time) (missingList []*StoreSkuBindInfo, err error) {
storeSkuList, err := dao.GetMissingStoreSkuFromOrder(dao.GetDB(), nil, fromTime)
if err == nil {
storeSkuNameMap := make(map[int64]*StoreSkuBindInfo)
for _, v := range storeSkuList {
skuName := storeSkuNameMap[jxutils.Combine2Int(v.StoreID, v.NameID)]
if skuName == nil {
skuName = &StoreSkuBindInfo{
StoreID: v.StoreID,
NameID: v.NameID,
IsFocus: 1,
IsSale: 1,
// 这里没有考虑平台价格比例
UnitPrice: jxutils.CaculateUnitPrice(v.RefPrice, v.SpecQuality, v.SpecUnit, v.Unit),
}
missingList = append(missingList, skuName)
}
skuName.Skus = append(skuName.Skus, &StoreSkuBindSkuInfo{
SkuID: v.SkuID,
})
}
}
return missingList, err
}

View File

@@ -21,6 +21,7 @@ func init() {
} }
func (s *StoreManager) OnStoreStatusChanged(vendorStoreID string, vendorID int, storeStatus int) (err error) { func (s *StoreManager) OnStoreStatusChanged(vendorStoreID string, vendorID int, storeStatus int) (err error) {
return err
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeStatus:%d", vendorStoreID, storeStatus) globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeStatus:%d", vendorStoreID, storeStatus)
db := dao.GetDB() db := dao.GetDB()
storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, vendorStoreID, vendorID) storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, vendorStoreID, vendorID)

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
@@ -81,14 +82,14 @@ func Init() {
} }
func (p *MultiStoreHandlerWrapper) DeleteCategory(db *dao.DaoDB, cat *model.SkuCategory, userName string) (err error) { func (p *MultiStoreHandlerWrapper) DeleteCategory(db *dao.DaoDB, cat *model.SkuCategory, userName string) (err error) {
if jxutils.IsFakeID(cat.JdID) { if jxutils.IsEmptyID(cat.JdID) {
return nil return nil
} }
return p.IMultipleStoresHandler.DeleteCategory(db, cat, userName) return p.IMultipleStoresHandler.DeleteCategory(db, cat, userName)
} }
func (p *MultiStoreHandlerWrapper) UpdateCategory(db *dao.DaoDB, cat *model.SkuCategory, userName string) (err error) { func (p *MultiStoreHandlerWrapper) UpdateCategory(db *dao.DaoDB, cat *model.SkuCategory, userName string) (err error) {
if jxutils.IsFakeID(cat.JdID) { if jxutils.IsEmptyID(cat.JdID) {
globals.SugarLogger.Warnf("UpdateCategory fakeid cat:%s should not get here", utils.Format4Output(cat, true)) globals.SugarLogger.Warnf("UpdateCategory fakeid cat:%s should not get here", utils.Format4Output(cat, true))
return nil return nil
} }
@@ -97,14 +98,14 @@ func (p *MultiStoreHandlerWrapper) UpdateCategory(db *dao.DaoDB, cat *model.SkuC
func (p *MultiStoreHandlerWrapper) DeleteSku(db *dao.DaoDB, sku *model.Sku, userName string) (err error) { func (p *MultiStoreHandlerWrapper) DeleteSku(db *dao.DaoDB, sku *model.Sku, userName string) (err error) {
globals.SugarLogger.Debugf("wrapper DeleteSku, sku:%s", utils.Format4Output(sku, false)) globals.SugarLogger.Debugf("wrapper DeleteSku, sku:%s", utils.Format4Output(sku, false))
if jxutils.IsFakeID(sku.JdID) { if jxutils.IsEmptyID(sku.JdID) {
return nil return nil
} }
return p.IMultipleStoresHandler.DeleteSku(db, sku, userName) return p.IMultipleStoresHandler.DeleteSku(db, sku, userName)
} }
func (p *MultiStoreHandlerWrapper) UpdateSku(db *dao.DaoDB, sku *model.Sku, userName string) (err error) { func (p *MultiStoreHandlerWrapper) UpdateSku(db *dao.DaoDB, sku *model.Sku, userName string) (err error) {
if jxutils.IsFakeID(sku.JdID) { if jxutils.IsEmptyID(sku.JdID) {
globals.SugarLogger.Warnf("UpdateSku fakeid sku:%s should not get here", utils.Format4Output(sku, true)) globals.SugarLogger.Warnf("UpdateSku fakeid sku:%s should not get here", utils.Format4Output(sku, true))
return nil return nil
} }
@@ -213,32 +214,36 @@ func (v *VendorSync) SyncStore(ctx *jxcontext.Context, db *dao.DaoDB, vendorID,
vendorID, vendorID,
} }
} }
hint, err = v.LoopStoresMap(ctx, db, fmt.Sprintf("同步门店信息:%d", storeID), isAsync, false, vendorIDs, []int{storeID}, func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { hint, err = v.LoopStoresMap(ctx, db, fmt.Sprintf("同步门店信息:%d", storeID), isAsync, false, vendorIDs, []int{storeID}, func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (resultList interface{}, err error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
handler := v.GetStoreHandler(loopMapInfo.VendorID) handler := v.GetStoreHandler(loopMapInfo.VendorID)
if len(loopMapInfo.StoreMapList) > 1 { if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), nil, ctx, loopStoreTask := tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
var resultList []interface{}
storeMap := batchItemList[0].(*model.StoreMap) storeMap := batchItemList[0].(*model.StoreMap)
if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil { if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil {
storeMap.SyncStatus = 0 storeMap.SyncStatus = 0
_, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus) _, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus)
resultList = append(resultList, 1)
} }
return nil, err return resultList, err
}, loopMapInfo.StoreMapList) }, loopMapInfo.StoreMapList)
t.AddChild(loopStoreTask).Run() t.AddChild(loopStoreTask).Run()
_, err = loopStoreTask.GetResult(0) resultList, err = loopStoreTask.GetResult(0)
return nil, err } else {
}
storeMap := loopMapInfo.StoreMapList[0] storeMap := loopMapInfo.StoreMapList[0]
if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil { if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil {
storeMap.SyncStatus = 0 storeMap.SyncStatus = 0
_, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus) _, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus)
} }
err = jxutils.AddVendorInfo2Err(err, loopMapInfo.VendorID) if err == nil {
return nil, err resultList = []interface{}{1}
}) }
return hint, err }
return resultList, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
}, true)
return hint, makeSyncError(err)
} }
func (v *VendorSync) SyncSku(ctx *jxcontext.Context, db *dao.DaoDB, nameID, skuID int, isAsync, isContinueWhenError bool, userName string) (hint string, err error) { func (v *VendorSync) SyncSku(ctx *jxcontext.Context, db *dao.DaoDB, nameID, skuID int, isAsync, isContinueWhenError bool, userName string) (hint string, err error) {
@@ -259,6 +264,7 @@ func (v *VendorSync) SyncSkus(ctx *jxcontext.Context, db *dao.DaoDB, nameIDs []i
globals.SugarLogger.Debugf("SyncSku trackInfo:%s, nameIDs:%v, skuIDs:%v, userName:%s", ctx.GetTrackInfo(), nameIDs, skuIDs, userName) globals.SugarLogger.Debugf("SyncSku trackInfo:%s, nameIDs:%v, skuIDs:%v, userName:%s", ctx.GetTrackInfo(), nameIDs, skuIDs, userName)
return v.LoopMultiStoresVendors(ctx, db, fmt.Sprintf("同步商品信息, nameIDs:%v, skuIDs:%v", nameIDs, skuIDs), isAsync, userName, return v.LoopMultiStoresVendors(ctx, db, fmt.Sprintf("同步商品信息, nameIDs:%v, skuIDs:%v", nameIDs, skuIDs), isAsync, userName,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
var resultList []interface{}
vendorID := batchItemList[0].(int) vendorID := batchItemList[0].(int)
multiStoresHandler := v.GetMultiStoreHandler(vendorID) multiStoresHandler := v.GetMultiStoreHandler(vendorID)
syncStatusFieldName := dao.GetSyncStatusStructField(model.VendorNames[multiStoresHandler.GetVendorID()]) syncStatusFieldName := dao.GetSyncStatusStructField(model.VendorNames[multiStoresHandler.GetVendorID()])
@@ -297,6 +303,7 @@ func (v *VendorSync) SyncSkus(ctx *jxcontext.Context, db *dao.DaoDB, nameIDs []i
// todo 同一skuName下的sku顺序处理的原因是京东SPU特殊类型必须要序列化同步才能正常处理, db可能会有多线程问题 // todo 同一skuName下的sku顺序处理的原因是京东SPU特殊类型必须要序列化同步才能正常处理, db可能会有多线程问题
task := tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[vendorID]), tasksch.NewParallelConfig().SetParallelCount(10).SetIsContinueWhenError(isContinueWhenError), ctx, task := tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[vendorID]), tasksch.NewParallelConfig().SetParallelCount(10).SetIsContinueWhenError(isContinueWhenError), ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
var resultList []interface{}
skuName := batchItemList[0].(*model.SkuName) skuName := batchItemList[0].(*model.SkuName)
var skuList []*model.Sku var skuList []*model.Sku
if err = dao.GetRows(db, &skuList, fmt.Sprintf(` if err = dao.GetRows(db, &skuList, fmt.Sprintf(`
@@ -333,6 +340,8 @@ func (v *VendorSync) SyncSkus(ctx *jxcontext.Context, db *dao.DaoDB, nameIDs []i
refutil.SetObjFieldByName(sku, syncStatusFieldName, int8(0)) refutil.SetObjFieldByName(sku, syncStatusFieldName, int8(0))
if _, err = dao.UpdateEntity(db, sku, updateFields...); err != nil { if _, err = dao.UpdateEntity(db, sku, updateFields...); err != nil {
break break
} else {
resultList = append(resultList, 1)
} }
} }
} }
@@ -342,22 +351,28 @@ func (v *VendorSync) SyncSkus(ctx *jxcontext.Context, db *dao.DaoDB, nameIDs []i
refutil.SetObjFieldByName(skuName, syncStatusFieldName, int8(0)) refutil.SetObjFieldByName(skuName, syncStatusFieldName, int8(0))
_, err = dao.UpdateEntity(db, skuName, syncStatusFieldName) _, err = dao.UpdateEntity(db, skuName, syncStatusFieldName)
} }
return nil, err return resultList, err
}, skuNameList) }, skuNameList)
t.AddChild(task).Run() t.AddChild(task).Run()
_, err = task.GetResult(0) result, err2 := task.GetResult(0)
if err = err2; err == nil {
resultList = append(resultList, result...)
} }
return nil, err }
return resultList, err
}) })
} }
func (v *VendorSync) SyncStoresCategory(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, isAsync bool) (hint string, err error) { func (v *VendorSync) SyncStoresCategory(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debug("SyncStoresCategory") globals.SugarLogger.Debug("SyncStoresCategory")
isManageIt := len(storeIDs) != 1 isManageIt := len(storeIDs) != 1
return v.LoopStoresMap(ctx, db, fmt.Sprintf("同步门店分类信息:%v", storeIDs), isAsync, isManageIt, vendorIDs, storeIDs, hint, err = v.LoopStoresMap(ctx, db, fmt.Sprintf("同步门店分类信息:%v", storeIDs), isAsync, isManageIt, vendorIDs, storeIDs,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetSingleStoreHandler(loopMapInfo.VendorID); handler != nil { if handler := v.GetSingleStoreHandler(loopMapInfo.VendorID); handler != nil {
if isForce {
dao.SetStoreCategorySyncStatus(db, loopMapInfo.VendorID, storeIDs, nil, model.SyncFlagModifiedMask)
}
if len(loopMapInfo.StoreMapList) > 1 { if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx, loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
@@ -367,25 +382,30 @@ func (v *VendorSync) SyncStoresCategory(ctx *jxcontext.Context, db *dao.DaoDB, v
}, len(loopMapInfo.StoreMapList)) }, len(loopMapInfo.StoreMapList))
t.AddChild(loopStoreTask).Run() t.AddChild(loopStoreTask).Run()
_, err = loopStoreTask.GetResult(0) _, err = loopStoreTask.GetResult(0)
return nil, err } else {
}
_, err = handler.SyncStoreCategory(ctx, t, loopMapInfo.StoreMapList[0].StoreID, false) _, err = handler.SyncStoreCategory(ctx, t, loopMapInfo.StoreMapList[0].StoreID, false)
err = jxutils.AddVendorInfo2Err(err, loopMapInfo.VendorID)
} }
return nil, err }
}) return nil, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
}, isContinueWhenError)
return hint, makeSyncError(err)
} }
// //
func (v *VendorSync) SyncStoresSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { func (v *VendorSync) SyncStoresSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, skuIDs []int, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debug("SyncStoresSkus") globals.SugarLogger.Debug("SyncStoresSkus")
isManageIt := len(storeIDs) != 1 || len(skuIDs) == 0 || len(skuIDs) > 8 isManageIt := len(storeIDs) != 1 || len(skuIDs) == 0 || len(skuIDs) > 8
return v.LoopStoresMap(ctx, db, fmt.Sprintf("同步门店商品信息:%v", storeIDs), isAsync, isManageIt, vendorIDs, storeIDs, task, hint, err := v.LoopStoresMap2(ctx, db, fmt.Sprintf("同步门店商品信息:%v", storeIDs), isAsync, isManageIt, vendorIDs, storeIDs,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil { if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil {
if isForce {
dao.SetStoreSkuSyncStatus(db, loopMapInfo.VendorID, storeIDs, skuIDs, model.SyncFlagModifiedMask)
}
if len(loopMapInfo.StoreMapList) > 1 { if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx, var loopStoreTask tasksch.ITask
if model.MultiStoresVendorMap[loopMapInfo.VendorID] == 1 {
loopStoreTask = tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
storeID := loopMapInfo.StoreMapList[step].StoreID storeID := loopMapInfo.StoreMapList[step].StoreID
if _, err = handler.SyncStoreSkus(ctx, task, storeID, skuIDs, false, isContinueWhenError); err != nil { if _, err = handler.SyncStoreSkus(ctx, task, storeID, skuIDs, false, isContinueWhenError); err != nil {
@@ -396,44 +416,86 @@ func (v *VendorSync) SyncStoresSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendo
} }
return nil, err return nil, err
}, len(loopMapInfo.StoreMapList)) }, len(loopMapInfo.StoreMapList))
} else {
loopStoreTask = tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeMap := batchItemList[0].(*model.StoreMap)
if _, err = handler.SyncStoreSkus(ctx, task, storeMap.StoreID, skuIDs, false, isContinueWhenError); err != nil {
globals.SugarLogger.Debugf("SyncStoresSkus failed2 store:%d failed with error:%v", storeMap.StoreID, err)
}
return nil, err
}, loopMapInfo.StoreMapList)
}
t.AddChild(loopStoreTask).Run() t.AddChild(loopStoreTask).Run()
_, err = loopStoreTask.GetResult(0) _, err = loopStoreTask.GetResult(0)
return nil, err } else {
}
_, err = handler.SyncStoreSkus(ctx, t, loopMapInfo.StoreMapList[0].StoreID, skuIDs, false, isContinueWhenError) _, err = handler.SyncStoreSkus(ctx, t, loopMapInfo.StoreMapList[0].StoreID, skuIDs, false, isContinueWhenError)
err = jxutils.AddVendorInfo2Err(err, loopMapInfo.VendorID)
} }
return nil, err }
}) return nil, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
}, isContinueWhenError)
if task != nil {
if vendorErr := partner.IsErrChangePriceFailed(task.GetOriginalErr()); vendorErr != nil {
platformList := make([]string, len(task.GetDetailErrList()))
for k, v := range task.GetDetailErrList() {
if vendorErr := partner.IsErrVendorError(v); vendorErr != nil {
platformList[k] = model.VendorChineseNames[vendorErr.VendorID()]
} else {
platformList[k] = "未知"
}
}
err = fmt.Errorf("同步价格失败\n失败平台%s", strings.Join(platformList, ","))
} else {
err = makeSyncError(err)
}
}
return hint, err
} }
func (v *VendorSync) FullSyncStoresSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { func (v *VendorSync) FullSyncStoresSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debug("FullSyncStoresSkus") globals.SugarLogger.Debug("FullSyncStoresSkus")
return v.LoopStoresMap(ctx, db, fmt.Sprintf("初始化门店商品信息:%v", storeIDs), isAsync, true, vendorIDs, storeIDs, hint, err = v.LoopStoresMap(ctx, db, fmt.Sprintf("初始化门店商品信息:%v", storeIDs), isAsync, true, vendorIDs, storeIDs,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil { if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil {
if len(loopMapInfo.StoreMapList) > 1 { if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx, var loopStoreTask tasksch.ITask
if model.MultiStoresVendorMap[loopMapInfo.VendorID] == 1 {
loopStoreTask = tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
storeID := loopMapInfo.StoreMapList[step].StoreID storeID := loopMapInfo.StoreMapList[step].StoreID
_, err = handler.FullSyncStoreSkus(ctx, task, storeID, false, isContinueWhenError) if _, err = handler.FullSyncStoreSkus(ctx, task, storeID, false, isContinueWhenError); err != nil {
globals.SugarLogger.Debugf("FullSyncStoresSkus failed1 store:%d failed with error:%v", storeID, err)
if isContinueWhenError {
err = nil
}
}
return nil, err return nil, err
}, len(loopMapInfo.StoreMapList)) }, len(loopMapInfo.StoreMapList))
} else {
loopStoreTask = tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeMap := batchItemList[0].(*model.StoreMap)
if _, err = handler.FullSyncStoreSkus(ctx, task, storeMap.StoreID, false, isContinueWhenError); err != nil {
globals.SugarLogger.Debugf("FullSyncStoresSkus failed2 store:%d failed with error:%v", storeMap.StoreID, err)
}
return nil, err
}, loopMapInfo.StoreMapList)
}
t.AddChild(loopStoreTask).Run() t.AddChild(loopStoreTask).Run()
_, err = loopStoreTask.GetResult(0) _, err = loopStoreTask.GetResult(0)
return nil, err } else {
}
_, err = handler.FullSyncStoreSkus(ctx, t, loopMapInfo.StoreMapList[0].StoreID, false, isContinueWhenError) _, err = handler.FullSyncStoreSkus(ctx, t, loopMapInfo.StoreMapList[0].StoreID, false, isContinueWhenError)
err = jxutils.AddVendorInfo2Err(err, loopMapInfo.VendorID)
} }
return nil, err }
}) return nil, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
}, isContinueWhenError)
return hint, makeSyncError(err)
} }
func (v *VendorSync) DeleteRemoteStoreSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { func (v *VendorSync) DeleteRemoteStoreSkus(ctx *jxcontext.Context, db *dao.DaoDB, vendorIDs []int, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debug("DeleteRemoteStoreSkus") globals.SugarLogger.Debug("DeleteRemoteStoreSkus")
return v.LoopStoresMap(ctx, db, fmt.Sprintf("删除远程门店商品信息:%v", storeIDs), isAsync, true, vendorIDs, storeIDs, hint, err = v.LoopStoresMap(ctx, db, fmt.Sprintf("删除远程门店商品信息:%v", storeIDs), isAsync, true, vendorIDs, storeIDs,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil { if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil {
@@ -446,16 +508,16 @@ func (v *VendorSync) DeleteRemoteStoreSkus(ctx *jxcontext.Context, db *dao.DaoDB
}, len(loopMapInfo.StoreMapList)) }, len(loopMapInfo.StoreMapList))
t.AddChild(loopStoreTask).Run() t.AddChild(loopStoreTask).Run()
_, err = loopStoreTask.GetResult(0) _, err = loopStoreTask.GetResult(0)
return nil, err } else {
}
_, err = handler.DeleteRemoteStoreSkus(ctx, t, loopMapInfo.StoreMapList[0].StoreID, false, isContinueWhenError) _, err = handler.DeleteRemoteStoreSkus(ctx, t, loopMapInfo.StoreMapList[0].StoreID, false, isContinueWhenError)
err = jxutils.AddVendorInfo2Err(err, loopMapInfo.VendorID)
} }
return nil, err }
}) return nil, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
}, isContinueWhenError)
return hint, makeSyncError(err)
} }
func (v *VendorSync) LoopStoresMap(ctx *jxcontext.Context, db *dao.DaoDB, taskName string, isAsync, isManageIt bool, vendorIDs []int, storeIDs []int, handler tasksch.WorkFunc) (hint string, err error) { func (v *VendorSync) LoopStoresMap2(ctx *jxcontext.Context, db *dao.DaoDB, taskName string, isAsync, isManageIt bool, vendorIDs []int, storeIDs []int, handler tasksch.WorkFunc, isContinueWhenError bool) (task tasksch.ITask, hint string, err error) {
sql := ` sql := `
SELECT t1.* SELECT t1.*
FROM store_map t1 FROM store_map t1
@@ -475,11 +537,11 @@ func (v *VendorSync) LoopStoresMap(ctx *jxcontext.Context, db *dao.DaoDB, taskNa
sql += " ORDER BY t1.store_id, t1.vendor_id" sql += " ORDER BY t1.store_id, t1.vendor_id"
var storeMapList []*model.StoreMap var storeMapList []*model.StoreMap
if err = dao.GetRows(db, &storeMapList, sql, sqlParams...); err != nil { if err = dao.GetRows(db, &storeMapList, sql, sqlParams...); err != nil {
return "", err return nil, "", err
} }
if len(storeMapList) == 0 { if len(storeMapList) == 0 {
return "", nil return nil, "", nil
} }
vendorStoreMap := make(map[int][]*model.StoreMap) vendorStoreMap := make(map[int][]*model.StoreMap)
for _, v := range storeMapList { for _, v := range storeMapList {
@@ -497,21 +559,40 @@ func (v *VendorSync) LoopStoresMap(ctx *jxcontext.Context, db *dao.DaoDB, taskNa
if len(loopInfoList) == 1 { if len(loopInfoList) == 1 {
taskName = fmt.Sprintf("%s,处理平台%s", taskName, model.VendorChineseNames[loopInfoList[0].VendorID]) taskName = fmt.Sprintf("%s,处理平台%s", taskName, model.VendorChineseNames[loopInfoList[0].VendorID])
} }
task := tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, handler, loopInfoList) task = tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, handler, loopInfoList)
tasksch.HandleTask(task, nil, isManageIt).Run() tasksch.HandleTask(task, nil, isManageIt).Run()
if !isAsync { if !isAsync {
_, err = task.GetResult(0) resultList, err2 := task.GetResult(0)
if err = err2; err == nil {
if len(resultList) == 0 {
hint = "1" // todo 暂时这样
} else {
hint = jxutils.TaskResult2Hint(resultList)
} }
return task.ID, makeSyncError(err) }
} else {
hint = task.GetID()
}
return task, hint, err
}
func (v *VendorSync) LoopStoresMap(ctx *jxcontext.Context, db *dao.DaoDB, taskName string, isAsync, isManageIt bool, vendorIDs []int, storeIDs []int, handler tasksch.WorkFunc, isContinueWhenError bool) (hint string, err error) {
_, hint, err = v.LoopStoresMap2(ctx, db, taskName, isAsync, isManageIt, vendorIDs, storeIDs, handler, isContinueWhenError)
return hint, err
} }
func (v *VendorSync) LoopMultiStoresVendors(ctx *jxcontext.Context, db *dao.DaoDB, taskName string, isAsync bool, userName string, handler tasksch.WorkFunc) (hint string, err error) { func (v *VendorSync) LoopMultiStoresVendors(ctx *jxcontext.Context, db *dao.DaoDB, taskName string, isAsync bool, userName string, handler tasksch.WorkFunc) (hint string, err error) {
task := tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, handler, v.MultiStoreVendorIDs) task := tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, handler, v.MultiStoreVendorIDs)
tasksch.HandleTask(task, nil, true).Run() tasksch.HandleTask(task, nil, true).Run()
if !isAsync { if !isAsync {
_, err = task.GetResult(0) result, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(result))
} }
return task.ID, makeSyncError(err) } else {
hint = task.ID
}
return hint, makeSyncError(err)
} }
func (v *VendorSync) RefreshAllSkusID(ctx *jxcontext.Context, isAsync bool, vendorIDs []int, storeIDs []int) (hint string, err error) { func (v *VendorSync) RefreshAllSkusID(ctx *jxcontext.Context, isAsync bool, vendorIDs []int, storeIDs []int) (hint string, err error) {
@@ -552,10 +633,12 @@ func (v *VendorSync) RefreshAllStoresID(ctx *jxcontext.Context, isAsync bool, ve
func makeSyncError(err error) (newErr error) { func makeSyncError(err error) (newErr error) {
if err != nil { if err != nil {
if _, ok := err.(*SyncError); !ok {
return &SyncError{ return &SyncError{
Original: err, Original: err,
} }
} }
}
return err return err
} }

View File

@@ -61,7 +61,7 @@ func SendFilesToStores(ctx *jxcontext.Context, files []*multipart.FileHeader, ti
globals.SugarLogger.Debugf("SendFilesToStores upload file:%s", fileHeader.Filename) globals.SugarLogger.Debugf("SendFilesToStores upload file:%s", fileHeader.Filename)
if err == nil { if err == nil {
ret := storage.PutRet{} ret := storage.PutRet{}
key := "storeBill_" + utils.Int2Str(storeID) + "_" + strings.ToLower(utils.GetUUID()) + path.Ext(fileHeader.Filename) key := "storeBill/" + utils.Int2Str(storeID) + "_" + strings.ToLower(utils.GetUUID()) + path.Ext(fileHeader.Filename)
formUploader := storage.NewFormUploader(cfg) formUploader := storage.NewFormUploader(cfg)
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
if err = formUploader.Put(context.Background(), &ret, upToken, key, file, fileHeader.Size, &storage.PutExtra{}); err == nil { if err = formUploader.Put(context.Background(), &ret, upToken, key, file, fileHeader.Size, &storage.PutExtra{}); err == nil {
@@ -73,7 +73,7 @@ func SendFilesToStores(ctx *jxcontext.Context, files []*multipart.FileHeader, ti
db := dao.GetDB() db := dao.GetDB()
billRec := &legacymodel.StoreBill{ billRec := &legacymodel.StoreBill{
Date: time.Now(), Date: time.Now(),
Url: jxutils.ComposeQiniuResURL(strings.Replace(ret.Key, "http://", "https://", -1)), Url: strings.Replace(jxutils.ComposeQiniuResURL(ret.Key), "http://", "https://", -1),
StoreId: storeID, StoreId: storeID,
BillName: fileHeader.Filename, BillName: fileHeader.Filename,
ShopName: shopName, ShopName: shopName,

View File

@@ -57,17 +57,28 @@ func insertPlace(ctx *jxcontext.Context, db *dao.DaoDB, parent *autonavi.Distric
} }
func InitPlace(ctx *jxcontext.Context) (err error) { func InitPlace(ctx *jxcontext.Context) (err error) {
db := dao.GetDB()
if err = TruncateTable(db, "place"); err == nil {
placeList, err2 := api.AutonaviAPI.GetDistricts(autonavi.DistrictLevelDistrict, "") placeList, err2 := api.AutonaviAPI.GetDistricts(autonavi.DistrictLevelDistrict, "")
if err = err2; err != nil { if err = err2; err != nil {
return err return err
} }
placeList = placeList[0].Districts placeList = placeList[0].Districts
db := dao.GetDB()
dao.Begin(db) dao.Begin(db)
defer func() { defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db) dao.Rollback(db)
if r != nil {
panic(r)
}
}
}() }()
if _, err = dao.ExecuteSQL(db, `
DELETE t1
FROM place t1
WHERE code < 9000000;
`); err != nil {
return err
}
if err = insertPlace(ctx, db, nil, placeList); err != nil { if err = insertPlace(ctx, db, nil, placeList); err != nil {
return err return err
} }
@@ -132,11 +143,49 @@ func InitPlace(ctx *jxcontext.Context) (err error) {
} }
} }
dao.Commit(db) dao.Commit(db)
}
return err return err
} }
func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError bool) (hint string, err error) { func RefreshSkuNameImg(ctx *jxcontext.Context, parentTask tasksch.ITask, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB()
var skuNameList []*model.SkuName
if err = dao.GetRows(db, &skuNameList, `
SELECT t1.id, t1.img, MAX(t2.jd_id) jd_id
FROM sku_name t1
JOIN sku t2 ON t2.name_id = t1.id AND t2.deleted_at = ?
WHERE t1.deleted_at = ?
GROUP BY 1,2
ORDER BY t1.id
`, utils.DefaultTimeValue, utils.DefaultTimeValue); err != nil {
return "", err
}
task := tasksch.NewParallelTask("RefreshSkuNameImg", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
skuName := batchItemList[0].(*model.SkuName)
if !jxutils.IsEmptyID(skuName.JdID) {
if skuName.Img == "" || isForce {
var imgList []*jdapi.SkuPageImg
if imgList, err = api.JdAPI.GetSkuPageImageInfo(skuName.JdID); err == nil {
if len(imgList) > 0 {
skuName.Img = imgList[0].Big
_, err = dao.UpdateEntity(db, skuName, "Img")
}
}
}
}
return nil, err
}, skuNameList)
tasksch.HandleTask(task, parentTask, true).Run()
if !isAsync {
_, err = task.GetResult(0)
} else {
hint = task.ID
}
return hint, err
}
func RefreshImgMd5(ctx *jxcontext.Context, parentTask tasksch.ITask, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB() db := dao.GetDB()
var skuNameList []*model.SkuName var skuNameList []*model.SkuName
if err = dao.GetRows(db, &skuNameList, ` if err = dao.GetRows(db, &skuNameList, `
@@ -169,7 +218,7 @@ func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError b
} }
return nil, err return nil, err
}, skuNameList) }, skuNameList)
tasksch.ManageTask(task).Run() tasksch.HandleTask(task, parentTask, true).Run()
if !isAsync { if !isAsync {
_, err = task.GetResult(0) _, err = task.GetResult(0)
} else { } else {
@@ -178,6 +227,26 @@ func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError b
return hint, err return hint, err
} }
func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
rootTask := tasksch.NewSeqTask("InitSkuName", ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
_, err = RefreshSkuNameImg(ctx, task, isForce, false, isContinueWhenError)
case 1:
_, err = RefreshImgMd5(ctx, task, isForce, false, isContinueWhenError)
}
return nil, err
}, 2)
tasksch.ManageTask(rootTask).Run()
if !isAsync {
_, err = rootTask.GetResult(0)
} else {
hint = rootTask.ID
}
return hint, err
}
func InitVendorCategory(ctx *jxcontext.Context, vendorID int) (num int64, err error) { func InitVendorCategory(ctx *jxcontext.Context, vendorID int) (num int64, err error) {
if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil { if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
cats, err2 := handler.GetVendorCategories(ctx) cats, err2 := handler.GetVendorCategories(ctx)
@@ -437,7 +506,7 @@ func BuildSkuFromEbaiStore(ctx *jxcontext.Context, baiduShopID int64, isAsync, i
} }
price := sku.LinkID price := sku.LinkID
sku.LinkID = 0 sku.LinkID = 0
skuName := jxutils.ComposeSkuName(skuNameExt.Prefix, skuNameExt.Name, sku.Comment, skuNameExt.Unit, sku.SpecQuality, sku.SpecUnit, jdapi.MaxSkuNameLen) skuName := jxutils.ComposeSkuName(skuNameExt.Prefix, skuNameExt.Name, sku.Comment, skuNameExt.Unit, sku.SpecQuality, sku.SpecUnit, jdapi.MaxSkuNameCharCount)
fixedStatus := 1 fixedStatus := 1
if sku.Status != model.SkuStatusNormal { if sku.Status != model.SkuStatusNormal {
fixedStatus = 2 fixedStatus = 2

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
@@ -47,11 +48,10 @@ func RefreshRealMobile(ctx *jxcontext.Context, vendorID int, fromTime, toTime ti
sql := ` sql := `
SELECT * SELECT *
FROM goods_order FROM goods_order
WHERE vendor_id = ? AND consignee_mobile2 = '' AND order_created_at <= ? WHERE vendor_id = ? AND consignee_mobile2 = ''
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
vendorID, vendorID,
time.Now().Add(-4 * time.Hour), // 最近的刷新意义不大
} }
if !utils.IsTimeZero(fromTime) { if !utils.IsTimeZero(fromTime) {
sql += " AND order_created_at >= ?" sql += " AND order_created_at >= ?"
@@ -97,29 +97,6 @@ func StartGetCityStoreInfo() {
} }
cityCenters = append(cityCenters, guiyang) cityCenters = append(cityCenters, guiyang)
GetCityStoreInfo(cityCenters) GetCityStoreInfo(cityCenters)
// countries, err := api.AutonaviAPI.GetDistricts(2, "")
// if err == nil {
// cityCenters := make([]*CityCenter, 0)
// country := countries[0]
// districts := country.Districts
// for _, province := range districts { // 省
// for _, city := range province.Districts { // 市
// // globals.SugarLogger.Debug(utils.Format4Output(city.Name, false))
// cityCenter := &CityCenter{
// Lng: city.Lng,
// Lat: city.Lat,
// CityName: city.Name,
// }
// // globals.SugarLogger.Debug(utils.Format4Output(cityCenter, false))
// cityCenters = append(cityCenters, cityCenter)
// }
// }
// // globals.SugarLogger.Debug(utils.Format4Output(cityCenters, false))
// GetCityStoreInfo(cityCenters)
// }
// utils.AfterFuncWithRecover(12*time.Hour, func() {
// StartGetCityStoreInfo()
// })
} }
func GetCityStoreInfo(cityCenters []*CityCenter) { func GetCityStoreInfo(cityCenters []*CityCenter) {
@@ -233,63 +210,6 @@ func SaveEbaiStoreInfo(storeId, cityName string) {
} }
} }
// func GetCityStoreInfo(cityCenters []*CityCenter, i int) {
// cityCenter := cityCenters[i]
// cityPoints := GetCityPoints(cityCenter.lng, cityCenter.lat)
// GetJdCityPointsStores(jxcontext.AdminCtx, cityPoints, true, true)
// utils.AfterFuncWithRecover(10*time.Minute, func() {
// GetEbaiCityPointsStores(jxcontext.AdminCtx, cityPoints, true, true)
// utils.AfterFuncWithRecover(10*time.Minute, func() {
// i++
// if i < len(cityCenters) {
// GetCityStoreInfo(cityCenters, i)
// }
// })
// })
// }
// func GetJdCityPointsStores(ctx *jxcontext.Context, cityPoints [][]string, isAsync, isContinueWhenError bool) (hint string, err error) {
// if len(cityPoints) > 0 {
// task := tasksch.NewParallelTask("misc GetJdCityPointsStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
// // globals.SugarLogger.Debug(batchItemList)
// point := batchItemList[0].([]string)
// err2 := jd.OnSaveStoreListInfo(point[0], point[1])
// if err = err2; err != nil {
// globals.SugarLogger.Infof("GetJdCityPointsStores point:%s,%s failed with error:%v", point[0], point[1], err)
// }
// return nil, err
// }, cityPoints)
// // globals.SugarLogger.Debug(utils.Format4Output(task, false))
// tasksch.HandleTask(task, nil, true).Run()
// hint = task.ID
// if !isAsync {
// _, err = task.GetResult(0)
// }
// }
// return hint, err
// }
// func GetEbaiCityPointsStores(ctx *jxcontext.Context, cityPoints [][]string, isAsync, isContinueWhenError bool) (hint string, err error) {
// if len(cityPoints) > 0 {
// task := tasksch.NewParallelTask("misc GetEbaiCityPointsStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
// point := batchItemList[0].([]interface{})
// err2 := ebai.OnSaveStoreListInfo(point[0].(string), point[1].(string))
// if err = err2; err != nil {
// globals.SugarLogger.Infof("GetEbaiCityPointsStores point:%s,%s failed with error:%v", point[0].(string), point[1].(string), err)
// }
// return nil, err
// }, cityPoints)
// tasksch.HandleTask(task, nil, true).Run()
// hint = task.ID
// if !isAsync {
// _, err = task.GetResult(0)
// }
// }
// return hint, err
// }
func GetCityPoints(lng float64, lat float64, cityName string) (cityPoints [][]string) { func GetCityPoints(lng float64, lat float64, cityName string) (cityPoints [][]string) {
oneDu := 111319.55 oneDu := 111319.55
for a := 0; a <= 80; a++ { for a := 0; a <= 80; a++ {
@@ -309,3 +229,25 @@ func GetCityPoints(lng float64, lat float64, cityName string) (cityPoints [][]st
} }
return cityPoints return cityPoints
} }
func StartDailyWork() {
if globals.ReallyCallPlatformAPI {
now := time.Now()
runTime := time.Date(now.Year(), now.Month(), now.Day(), 21, 0, 0, 0, time.Local) // 凌晨00:25点开始执行
waitDuration := runTime.Sub(now)
if waitDuration < 5*time.Second {
waitDuration += 24 * time.Hour
}
globals.SugarLogger.Debugf("dailyWork waitDuration:%d minutes", waitDuration/time.Minute)
utils.AfterFuncWithRecover(waitDuration, func() {
doDailyWork()
StartDailyWork()
})
}
}
func doDailyWork() {
globals.SugarLogger.Debug("doDailyWork")
cms.EnableHaveRestStores(jxcontext.AdminCtx, true, true)
// cms.CurVendorSync.FullSyncStoresSkus(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDJD}, nil, true, true)
cms.CurVendorSync.SyncStoresSkus(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDJD, model.VendorIDEBAI, model.VendorIDMTWM}, nil, nil, false, true, true)
}

View File

@@ -72,6 +72,8 @@ type SkuPrice struct {
Price int `json:"price"` // 分这个不是单价是这个sku的活动价 Price int `json:"price"` // 分这个不是单价是这个sku的活动价
LimitSkuCount int `json:"limitSkuCount"` LimitSkuCount int `json:"limitSkuCount"`
IsLock int8 `json:"isLock"` IsLock int8 `json:"isLock"`
EarningPrice int `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
} }
type tPromotionItemInfo struct { type tPromotionItemInfo struct {
@@ -131,7 +133,7 @@ var (
type JdPromotionHandler interface { type JdPromotionHandler interface {
CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error)
CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error)
CreatePromotionSku(infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) CreatePromotionSku(infoId int64, outInfoId string, skus []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error)
ConfirmPromotion(infoId int64, outInfoId string) (err error) ConfirmPromotion(infoId int64, outInfoId string) (err error)
CancelPromotion(infoId int64, outInfoId string) (err error) CancelPromotion(infoId int64, outInfoId string) (err error)
} }
@@ -140,38 +142,38 @@ type JdDirectDownHandler struct {
} }
func (p *JdDirectDownHandler) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) { func (p *JdDirectDownHandler) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) {
return api.JdAPI.CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising) return api.JdAPI.CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising, "")
} }
func (p *JdDirectDownHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) { func (p *JdDirectDownHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) {
return api.JdAPI.CreatePromotionRules(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily) return api.JdAPI.CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, "")
} }
func (p *JdDirectDownHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) { func (p *JdDirectDownHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error) {
return api.JdAPI.CreatePromotionSkuSingle(infoId, outInfoId, skus) return api.JdAPI.CreatePromotionSkuSingle(infoId, outInfoId, skus, "")
} }
func (p *JdDirectDownHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) { func (p *JdDirectDownHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
return api.JdAPI.ConfirmPromotionSingle(infoId, outInfoId) return api.JdAPI.ConfirmPromotionSingle(infoId, outInfoId, "")
} }
func (p *JdDirectDownHandler) CancelPromotion(infoId int64, outInfoId string) (err error) { func (p *JdDirectDownHandler) CancelPromotion(infoId int64, outInfoId string) (err error) {
return api.JdAPI.CancelPromotionSingle(infoId, outInfoId) return api.JdAPI.CancelPromotionSingle(infoId, outInfoId, "")
} }
type JdLimitedTimeHandler struct { type JdLimitedTimeHandler struct {
} }
func (p *JdLimitedTimeHandler) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) { func (p *JdLimitedTimeHandler) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) {
return api.JdAPI.CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising) return api.JdAPI.CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising, "")
} }
func (p *JdLimitedTimeHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) { func (p *JdLimitedTimeHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) {
return api.JdAPI.CreatePromotionRules(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily) return api.JdAPI.CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, "")
} }
func (p *JdLimitedTimeHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) { func (p *JdLimitedTimeHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error) {
return api.JdAPI.CreatePromotionSkuLimitTime(infoId, outInfoId, skus) return api.JdAPI.CreatePromotionSkuLimitTime(infoId, outInfoId, skus, "")
} }
func (p *JdLimitedTimeHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) { func (p *JdLimitedTimeHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
return api.JdAPI.ConfirmPromotionLimitTime(infoId, outInfoId) return api.JdAPI.ConfirmPromotionLimitTime(infoId, outInfoId, "")
} }
func (p *JdLimitedTimeHandler) CancelPromotion(infoId int64, outInfoId string) (err error) { func (p *JdLimitedTimeHandler) CancelPromotion(infoId int64, outInfoId string) (err error) {
return api.JdAPI.CancelPromotionLimitTime(infoId, outInfoId) return api.JdAPI.CancelPromotionLimitTime(infoId, outInfoId, "")
} }
type JdNullHandler struct { type JdNullHandler struct {
@@ -183,7 +185,7 @@ func (p *JdNullHandler) CreatePromotionInfos(name string, beginDate, endDate tim
func (p *JdNullHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) { func (p *JdNullHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) {
return nil return nil
} }
func (p *JdNullHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) { func (p *JdNullHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error) {
return nil, nil return nil, nil
} }
func (p *JdNullHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) { func (p *JdNullHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
@@ -230,7 +232,10 @@ func Init() {
// scheduleRoutine(true) // scheduleRoutine(true)
} }
func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueWhenError bool, vendorPromotionID string, params *PromotionParams, mapData map[string]interface{}) (hint string, err error) { func CreateJdPromotion(ctx *jxcontext.Context, vendorID int, isIDJd bool, isAsync, isContinueWhenError bool, vendorPromotionID string, params *PromotionParams, mapData map[string]interface{}) (hint string, err error) {
if vendorID != model.VendorIDJD && vendorID != model.VendorIDJX {
return "", fmt.Errorf("当前只支持京西与京东活动")
}
if vendorPromotionID != "" && len(vendorPromotionID) != len("14863853") { if vendorPromotionID != "" && len(vendorPromotionID) != len("14863853") {
return "", fmt.Errorf("%s看起来不像是一个有效的京东活动ID请仔细检查一下", vendorPromotionID) return "", fmt.Errorf("%s看起来不像是一个有效的京东活动ID请仔细检查一下", vendorPromotionID)
} }
@@ -260,12 +265,12 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
userName := ctx.GetUserName() userName := ctx.GetUserName()
db := dao.GetDB() db := dao.GetDB()
modifyPricesList := make(map[int][]*jdapi.SkuPriceInfo) modifyPricesList := make(map[int][]*jdapi.SkuPriceInfo)
promotionPrices := make([]map[string]interface{}, len(params.StoreIDs)*len(params.SkuPrices)) promotionPrices := make([]*jdapi.PromotionSku, len(params.StoreIDs)*len(params.SkuPrices))
var jxStoreIDs []int var jxStoreIDs []int
promotion := &model.Promotion{ promotion := &model.Promotion{
Name: params.Name, Name: params.Name,
Advertising: params.Advertising, Advertising: params.Advertising,
VendorID: model.VendorIDJD, VendorID: vendorID,
Type: params.Type, Type: params.Type,
Status: model.PromotionStatusLocalCreated, Status: model.PromotionStatusLocalCreated,
LimitDevice: int8(limitDevice), LimitDevice: int8(limitDevice),
@@ -278,7 +283,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
Source: PromotionSourceOpenPlatform, Source: PromotionSourceOpenPlatform,
} }
if vendorPromotionID == "" { if vendorPromotionID == "" && vendorID == model.VendorIDJD {
skuIDs := make([]int, len(params.SkuPrices)) skuIDs := make([]int, len(params.SkuPrices))
skuPriceMap := make(map[int64]*SkuPrice) skuPriceMap := make(map[int64]*SkuPrice)
for k, v := range params.SkuPrices { for k, v := range params.SkuPrices {
@@ -323,7 +328,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
if promotionSkuPrice.PriceType == PriceTypePercentage { if promotionSkuPrice.PriceType == PriceTypePercentage {
promotionSkuPrice.Price = skuBind.Price * promotionSkuPrice.Price / 100 promotionSkuPrice.Price = skuBind.Price * promotionSkuPrice.Price / 100
} }
if promotionSkuPrice.Price >= skuBind.Price { if vendorID != model.VendorIDJX && promotionSkuPrice.Price >= skuBind.Price {
errMsg += fmt.Sprintf("活动价大于等于原价storeID:%d, skuID:%d\n", skuBind.StoreID, skuBind.SkuID) errMsg += fmt.Sprintf("活动价大于等于原价storeID:%d, skuID:%d\n", skuBind.StoreID, skuBind.SkuID)
} }
if promotionSkuPrice.LimitSkuCount <= 0 { if promotionSkuPrice.LimitSkuCount <= 0 {
@@ -338,13 +343,11 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
}) })
} }
} }
promotionPrices[index] = map[string]interface{}{ promotionPrices[index] = &jdapi.PromotionSku{
jdapi.KeyStationNo: utils.Str2Int64(skuBind.VendorStoreID), StationNo: utils.Str2Int64(skuBind.VendorStoreID),
jdapi.KeySkuId: skuBind.JdSkuID, SkuID: skuBind.JdSkuID,
// jdapi.KeyOutStationNo: utils.Int2Str(skuBind.StoreID), PromotionPrice: int64(promotionSkuPrice.Price),
// jdapi.KeyOutSkuId: utils.Int2Str(skuBind.SkuID), LimitSkuCount: promotionSkuPrice.LimitSkuCount,
jdapi.KeyPromotionPrice: promotionSkuPrice.Price,
jdapi.KeyLimitSkuCount: promotionSkuPrice.LimitSkuCount,
} }
index++ index++
} }
@@ -402,6 +405,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
Price: skuPrice.Price, Price: skuPrice.Price,
LimitSkuCount: skuPrice.LimitSkuCount, LimitSkuCount: skuPrice.LimitSkuCount,
IsLock: skuPrice.IsLock, IsLock: skuPrice.IsLock,
EarningPrice: skuPrice.EarningPrice,
} }
dao.WrapAddIDCULDEntity(promotionSku, ctx.GetUserName()) dao.WrapAddIDCULDEntity(promotionSku, ctx.GetUserName())
if err = dao.CreateEntity(db, promotionSku); err != nil { if err = dao.CreateEntity(db, promotionSku); err != nil {
@@ -409,7 +413,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
} }
} }
if vendorPromotionID == "" { if vendorID != model.VendorIDJX && vendorPromotionID == "" {
promotionHandler := getPromotionHander(params.Type) promotionHandler := getPromotionHander(params.Type)
if promotionHandler == nil { if promotionHandler == nil {
return "", errors.New("非法的活动类型") return "", errors.New("非法的活动类型")
@@ -436,7 +440,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
for k, v := range modifyPrices { for k, v := range modifyPrices {
modifyPrices2[k] = v.(*jdapi.SkuPriceInfo) modifyPrices2[k] = v.(*jdapi.SkuPriceInfo)
} }
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
if _, err = api.JdAPI.UpdateVendorStationPrice(utils.Int2Str(storeID), "", modifyPrices2); err != nil { if _, err = api.JdAPI.UpdateVendorStationPrice(utils.Int2Str(storeID), "", modifyPrices2); err != nil {
return nil, err return nil, err
} }
@@ -451,9 +455,9 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
} else if step == 2 { } else if step == 2 {
task2 := tasksch.NewParallelTask("CreateJdPromotion CreatePromotionSku", tasksch.NewParallelConfig().SetBatchSize(MaxPromotionSkuCount).SetIsContinueWhenError(isContinueWhenError), ctx, task2 := tasksch.NewParallelTask("CreateJdPromotion CreatePromotionSku", tasksch.NewParallelConfig().SetBatchSize(MaxPromotionSkuCount).SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params2 ...interface{}) (retVal interface{}, err error) { func(task *tasksch.ParallelTask, batchItemList []interface{}, params2 ...interface{}) (retVal interface{}, err error) {
skus := make([]map[string]interface{}, len(batchItemList)) skus := make([]*jdapi.PromotionSku, len(batchItemList))
for k, v := range batchItemList { for k, v := range batchItemList {
skus[k] = v.(map[string]interface{}) skus[k] = v.(*jdapi.PromotionSku)
} }
_, err = promotionHandler.CreatePromotionSku(infoId, "", skus) _, err = promotionHandler.CreatePromotionSku(infoId, "", skus)
return nil, err return nil, err
@@ -510,7 +514,8 @@ func GetJdPromotions(ctx *jxcontext.Context, keyword string, params map[string]i
t1.end_at, t1.end_at,
t1.advertising, t1.advertising,
CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"storeID":', t2.store_id, ', "name":"', REPLACE(REPLACE(t22.name, '\t', ''), '"', ''), '"}')), "]") store_str, CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"storeID":', t2.store_id, ', "name":"', REPLACE(REPLACE(t22.name, '\t', ''), '"', ''), '"}')), "]") store_str,
CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"skuID":', t3.sku_id, ', "priceType":', t3.price_type, ', "price":', t3.price, ', "limitSkuCount":', t3.limit_sku_count, ', "isLock":', t3.is_lock, '}')), "]") sku_price_str CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"skuID":', t3.sku_id, ', "priceType":', t3.price_type, ', "price":', t3.price,
', "limitSkuCount":', t3.limit_sku_count, ', "isLock":', t3.is_lock, ', "earningPrice":', t3.earning_price, '}')), "]") sku_price_str
FROM promotion t1 FROM promotion t1
JOIN promotion_store t2 ON t1.id = t2.promotion_id JOIN promotion_store t2 ON t1.id = t2.promotion_id
JOIN store t22 ON t2.store_id = t22.id JOIN store t22 ON t2.store_id = t22.id
@@ -881,6 +886,34 @@ func LockPromotionSkus(ctx *jxcontext.Context, promotionID int, isLock int, skuI
return num, err return num, err
} }
func UpdatePromotionSkusEarningPrice(ctx *jxcontext.Context, promotionID int, skuPriceList []*SkuPrice) (num int64, err error) {
db := dao.GetDB()
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
for _, v := range skuPriceList {
var tmpNum int64
if tmpNum, err = dao.UpdateEntityLogically(db, &model.PromotionSku{}, map[string]interface{}{
"EarningPrice": v.EarningPrice,
}, ctx.GetUserName(), map[string]interface{}{
"PromotionID": promotionID,
model.FieldSkuID: v.SkuID,
model.FieldDeletedAt: utils.DefaultTimeValue,
}); err != nil {
return 0, err
}
num += tmpNum
}
dao.Commit(db)
return num, err
}
func OnStoreStockMsg(msg *jdapi.CallbackStoreStockMsg) (retVal *jdapi.CallbackResponse) { func OnStoreStockMsg(msg *jdapi.CallbackStoreStockMsg) (retVal *jdapi.CallbackResponse) {
var err error var err error
// globals.SugarLogger.Debugf("OnStoreStockMsg IsJdStoreSkuLocked:%t", storeskulock.IsJdStoreSkuLocked(msg.StationNo, msg.SkuId)) // globals.SugarLogger.Debugf("OnStoreStockMsg IsJdStoreSkuLocked:%t", storeskulock.IsJdStoreSkuLocked(msg.StationNo, msg.SkuId))
@@ -930,12 +963,12 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
if err = dao.GetEntity(db, promotion, "VendorPromotionID"); dao.IsNoRowsError(err) { if err = dao.GetEntity(db, promotion, "VendorPromotionID"); dao.IsNoRowsError(err) {
storeIDMap := make(map[int64]int) storeIDMap := make(map[int64]int)
skuIDMap := make(map[int64]int) skuIDMap := make(map[int64]int)
skuMap := make(map[int64]*jdapi.PromotionSkuResult) skuMap := make(map[int64]*jdapi.PromotionLspQuerySkuResult)
// 注意这样处理可能是有问题我们假定的是门店信息与SKU信息的叉乘 // 注意这样处理可能是有问题我们假定的是门店信息与SKU信息的叉乘
for _, v := range result.SkuResultList { for _, v := range result.SkuResultList {
storeIDMap[v.StationNo] = 1 storeIDMap[v.StationNo] = 1
skuIDMap[v.SkuId] = 1 skuIDMap[v.SkuID] = 1
skuMap[v.SkuId] = v skuMap[v.SkuID] = v
} }
jdStoreIDs := make([]string, len(storeIDMap)) jdStoreIDs := make([]string, len(storeIDMap))
index := 0 index := 0
@@ -970,7 +1003,7 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
jxStoreIDs[k] = v.StoreID jxStoreIDs[k] = v.StoreID
} }
priceList := make([]*SkuPrice, len(skuList)) priceList := make([]*SkuPrice, len(skuList))
var skuResult *jdapi.PromotionSkuResult var skuResult *jdapi.PromotionLspQuerySkuResult
for k, v := range skuList { for k, v := range skuList {
skuResult = skuMap[v.JdID] skuResult = skuMap[v.JdID]
priceList[k] = &SkuPrice{ priceList[k] = &SkuPrice{
@@ -985,11 +1018,11 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
// globals.SugarLogger.Debugf("priceList:%s", utils.Format4Output(priceList, false)) // globals.SugarLogger.Debugf("priceList:%s", utils.Format4Output(priceList, false))
source := strings.Trim(result.Source, "来源") source := strings.Trim(result.Source, "来源")
promotionParams := &PromotionParams{ promotionParams := &PromotionParams{
Name: source + "-" + utils.Int64ToStr(result.PromotionInfoId), Name: source + "-" + utils.Int64ToStr(result.PromotionInfoID),
Advertising: "", Advertising: "",
Type: result.PromotionType, Type: result.PromotionType,
BeginAt: result.BeginTime, BeginAt: result.BeginTime.GoTime(),
EndAt: result.EndTime, EndAt: result.EndTime.GoTime(),
StoreIDs: jxStoreIDs, StoreIDs: jxStoreIDs,
SkuPrices: priceList, SkuPrices: priceList,
} }
@@ -1002,7 +1035,7 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
mapData[keyLimitDevice] = skuResult.LimitDevice mapData[keyLimitDevice] = skuResult.LimitDevice
mapData[keyLimitPin] = skuResult.LimitPin mapData[keyLimitPin] = skuResult.LimitPin
} }
_, err = CreateJdPromotion(jxcontext.AdminCtx, false, true, false, utils.Int64ToStr(promotionInfoId), promotionParams, mapData) _, err = CreateJdPromotion(jxcontext.AdminCtx, model.VendorIDJD, false, true, false, utils.Int64ToStr(promotionInfoId), promotionParams, mapData)
if dao.IsDuplicateError(err) || err == ErrLimitDeviceIsInvalid { if dao.IsDuplicateError(err) || err == ErrLimitDeviceIsInvalid {
err = nil err = nil
} }
@@ -1048,7 +1081,7 @@ func getPromotionHander(promotionType int) JdPromotionHandler {
// panic(fmt.Sprintf("unknown promotion type:%d", promotionType)) // panic(fmt.Sprintf("unknown promotion type:%d", promotionType))
return nil return nil
} }
if !globals.EnableStoreWrite { if !globals.EnableJdStoreWrite {
promotionHandler = &JdNullHandler{} promotionHandler = &JdNullHandler{}
} }
return promotionHandler return promotionHandler

View File

@@ -8,6 +8,8 @@ import (
"sync" "sync"
"time" "time"
"git.rosy.net.cn/jx-callback/business/partner/delivery"
"git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/orderman" "git.rosy.net.cn/jx-callback/business/jxcallback/orderman"
@@ -102,7 +104,7 @@ func Convert2JDSPU(ctx *jxcontext.Context, count int, isAsync, isContinueWhenErr
skuNew2 := *sku skuNew2 := *sku
skuNew := &skuNew2 skuNew := &skuNew2
dao.WrapAddIDCULEntity(skuNew, ctx.GetUserName()) dao.WrapAddIDCULEntity(skuNew, ctx.GetUserName())
skuNew.JdID = 0 //jxutils.GenFakeID() skuNew.JdID = 0
skuNew.LinkID = sku.ID skuNew.LinkID = sku.ID
skuNew.NameID = skuNameNew.ID skuNew.NameID = skuNameNew.ID
skuNew.JdSyncStatus = model.SyncFlagNewMask skuNew.JdSyncStatus = model.SyncFlagNewMask
@@ -277,7 +279,7 @@ func Change2JDSPU4Store(ctx *jxcontext.Context, storeIDs []int, step int, isAsyn
if err = dao.GetRows(db, &skuIDs, sql, sqlParams...); err != nil { if err = dao.GetRows(db, &skuIDs, sql, sqlParams...); err != nil {
return "", err return "", err
} }
hint, err = cms.CurVendorSync.SyncStoresSkus(ctx, db, []int{model.VendorIDJD}, storeIDs, skuIDs, isAsync, isContinueWhenError) hint, err = cms.CurVendorSync.SyncStoresSkus(ctx, db, []int{model.VendorIDJD}, storeIDs, skuIDs, false, isAsync, isContinueWhenError)
return hint, err return hint, err
} }
@@ -706,7 +708,7 @@ func TransformJdSpu2Sku(ctx *jxcontext.Context, skuNameIDs []int, count int, isA
subTask := tasksch.NewParallelTask(fmt.Sprintf("TransformJdSpu2Sku:%d", step), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, subTask := tasksch.NewParallelTask(fmt.Sprintf("TransformJdSpu2Sku:%d", step), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(subTask *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(subTask *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
skuName := batchItemList[0].(*model.SkuName) skuName := batchItemList[0].(*model.SkuName)
if !jxutils.IsFakeID(skuName.JdID) { if !jxutils.IsEmptyID(skuName.JdID) {
sql = ` sql = `
SELECT * SELECT *
FROM sku FROM sku
@@ -727,8 +729,8 @@ func TransformJdSpu2Sku(ctx *jxcontext.Context, skuNameIDs []int, count int, isA
locker.Lock() locker.Lock()
skuIDs = append(skuIDs, sku.ID) skuIDs = append(skuIDs, sku.ID)
locker.Unlock() locker.Unlock()
if !jxutils.IsFakeID(sku.JdID) { if !jxutils.IsEmptyID(sku.JdID) {
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
if err = api.JdAPI.UpdateSkuBaseInfo(utils.Int2Str(skuName.ID), utils.Int2Str(sku.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusDeleted)); err != nil { if err = api.JdAPI.UpdateSkuBaseInfo(utils.Int2Str(skuName.ID), utils.Int2Str(sku.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusDeleted)); err != nil {
if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 11004 { if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 11004 {
err = nil err = nil
@@ -740,7 +742,7 @@ func TransformJdSpu2Sku(ctx *jxcontext.Context, skuNameIDs []int, count int, isA
} }
} }
} }
if err == nil && globals.EnableStoreWrite { if err == nil && globals.EnableJdStoreWrite {
if err = api.JdAPI.UpdateSpu(utils.Int2Str(skuName.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusOffline)); err == nil { if err = api.JdAPI.UpdateSpu(utils.Int2Str(skuName.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusOffline)); err == nil {
err = api.JdAPI.UpdateSpu(utils.Int2Str(skuName.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusDeleted)) err = api.JdAPI.UpdateSpu(utils.Int2Str(skuName.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusDeleted))
} else if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 11035 { } else if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 11035 {
@@ -777,7 +779,7 @@ func TransformJdSpu2Sku(ctx *jxcontext.Context, skuNameIDs []int, count int, isA
rootTask.AddChild(subTask).Run() rootTask.AddChild(subTask).Run()
if _, err = subTask.GetResult(0); err == nil { if _, err = subTask.GetResult(0); err == nil {
if len(skuIDs) > 0 { if len(skuIDs) > 0 {
if _, err = dao.SetStoreSkuSyncStatus(db, model.VendorIDJD, -1, skuIDs, model.SyncFlagModifiedMask|model.SyncFlagPriceMask|model.SyncFlagSaleMask); err == nil { if _, err = dao.SetStoreSkuSyncStatus(db, model.VendorIDJD, nil, skuIDs, model.SyncFlagStoreSkuModifiedMask); err == nil {
// time.Sleep(20 * time.Second) // time.Sleep(20 * time.Second)
// _, err = cms.CurVendorSync.SyncStoresSkus(ctx, db, []int{model.VendorIDJD}, nil, skuIDs, false, isContinueWhenError) // _, err = cms.CurVendorSync.SyncStoresSkus(ctx, db, []int{model.VendorIDJD}, nil, skuIDs, false, isContinueWhenError)
} }
@@ -960,3 +962,29 @@ func RetrieveEbaiShopLicence(ctx *jxcontext.Context, isAsync, isContinueWhenErro
} }
return hint, err return hint, err
} }
func RefreshMtpsWaybillFee(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
var waybillList []*model.Waybill
db := dao.GetDB()
if err = dao.GetRows(db, &waybillList, `
SELECT *
FROM waybill
WHERE status_time > '2019-04-01' AND waybill_vendor_id = 102 AND desired_fee = 0
`); err == nil {
globals.SugarLogger.Debugf("RefreshMtpsWaybillFee, count:%d", len(waybillList))
rootTask := tasksch.NewParallelTask("RefreshMtpsWaybillFee", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
bill := batchItemList[0].(*model.Waybill)
bill.DesiredFee, _ = delivery.CalculateBillDeliveryFee(bill)
_, err = dao.UpdateEntity(db, bill, "DesiredFee")
return nil, err
}, waybillList)
tasksch.ManageTask(rootTask).Run()
if !isAsync {
_, err = rootTask.GetResult(0)
} else {
hint = rootTask.ID
}
}
return hint, err
}

View File

@@ -45,19 +45,44 @@ func Obj2Excel(sheetList []*Obj2ExcelSheetConfig) []byte {
} else { } else {
excelFile.NewSheet(sheetConfig.Title) excelFile.NewSheet(sheetConfig.Title)
} }
for index, name := range sheetConfig.CaptionList { isMemberStruct := typeInfo.Kind() == reflect.Struct
excelFile.SetCellStr(sheetConfig.Title, genAxis(0, index), name) if isMemberStruct {
var indexSlice [][]int
name2IndexMap := utils.GetStructNameIndex(typeInfo, "json")
for col, name := range sheetConfig.CaptionList {
if _, ok := name2IndexMap[name]; !ok {
panic(fmt.Sprintf("col:%s不能找到相应的数据", name))
}
indexSlice = append(indexSlice, name2IndexMap[name])
excelFile.SetCellStr(sheetConfig.Title, genAxis(0, col), name)
} }
for i := 0; i < valueInfo.Len(); i++ { for i := 0; i < valueInfo.Len(); i++ {
var mapData map[string]interface{} for col, index := range indexSlice {
if typeInfo.Kind() == reflect.Struct { excelFile.SetCellStr(sheetConfig.Title, genAxis(i+1, col), fmt.Sprint(reflect.Indirect(valueInfo.Index(i)).FieldByIndex(index).Interface()))
mapData = utils.FlatMap(utils.Struct2MapByJson(valueInfo.Index(i).Interface())) }
} else { }
mapData = valueInfo.Index(i).Interface().(map[string]interface{}) } else {
var name2IndexMap map[string]int
if valueInfo.Len() > 0 {
oneData := valueInfo.Index(0).Interface().(map[string]interface{})
name2IndexMap = make(map[string]int)
for k := range oneData {
name2IndexMap[k] = 1
}
}
for col, name := range sheetConfig.CaptionList {
if name2IndexMap != nil {
if _, ok := name2IndexMap[name]; !ok {
panic(fmt.Sprintf("col:%s不能找到相应的数据", name))
}
}
excelFile.SetCellStr(sheetConfig.Title, genAxis(0, col), name)
}
for i := 0; i < valueInfo.Len(); i++ {
mapData := valueInfo.Index(i).Interface().(map[string]interface{})
for col, name := range sheetConfig.CaptionList {
excelFile.SetCellStr(sheetConfig.Title, genAxis(i+1, col), fmt.Sprint(mapData[name]))
} }
for index, name := range sheetConfig.CaptionList {
// globals.SugarLogger.Debug(sheetConfig.Title, " ", genAxis(i+1, index), " ", fmt.Sprintf("%v", mapData[name]))
excelFile.SetCellStr(sheetConfig.Title, genAxis(i+1, index), fmt.Sprintf("%v", mapData[name]))
} }
} }
} }
@@ -77,6 +102,10 @@ func Excel2Slice(reader io.Reader) (contents map[string][][]string) {
return contents return contents
} }
func genAxis(row, col int) string { func genAxis(row, col int) (pos string) {
return fmt.Sprintf("%c%d", col+65, row+1) pos, err := excelize.CoordinatesToCellName(col+1, row+1)
if err != nil {
globals.SugarLogger.Debugf("err:%v", err)
}
return pos
} }

View File

@@ -1,9 +1,47 @@
package excel package excel
import ( import (
"reflect"
"testing" "testing"
) )
type XXXX struct {
ID int64 `orm:"column(id)" json:"id"`
VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"`
VendorOrderID2 string `orm:"column(vendor_order_id2);size(48);index" json:"vendorOrderID2"`
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:"storeName"`
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"`
ConsigneeMobile2 string `orm:"size(32)" json:"consigneeMobile2"`
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:"vendorStatus"`
LockStatus int `json:"lockStatus"`
OrderSeq int `json:"orderSeq"` // 门店订单序号
BuyerComment string `orm:"size(255)" json:"buyerComment"`
BusinessType int `json:"businessType"`
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表示还没有安排
DeliveryFlag int8 `json:"deliveryFlag"` // 第1位为1表示禁止调度器调度三方配送
DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的
OriginalData string `orm:"-" json:"-"` // 只是用于传递数据
Flag int8 `json:"flag"` //非运单调整相关的其它状态
}
func TestObj2Excel(t *testing.T) { func TestObj2Excel(t *testing.T) {
// kk := make([]*model.SkuName, 1) // kk := make([]*model.SkuName, 1)
// kk[0] = &model.SkuName{ // kk[0] = &model.SkuName{
@@ -24,3 +62,49 @@ func TestObj2Excel(t *testing.T) {
} }
Obj2Excel([]*Obj2ExcelSheetConfig{cc}) Obj2Excel([]*Obj2ExcelSheetConfig{cc})
} }
func BenchmarkObj2Excel(b *testing.B) {
const sliceLen = 1000
oneData := &XXXX{}
cc := &Obj2ExcelSheetConfig{
Title: "Title",
CaptionList: nil,
}
elmType := reflect.TypeOf(oneData)
if elmType.Kind() == reflect.Ptr {
elmType = elmType.Elem()
}
for i := 0; i < elmType.NumField(); i++ {
if jsonTag := elmType.Field(i).Tag.Get("json"); jsonTag != "" && jsonTag != "-" {
cc.CaptionList = append(cc.CaptionList, jsonTag)
}
}
value := reflect.Indirect(reflect.ValueOf(oneData))
for i := 0; i < elmType.NumField(); i++ {
value2 := value.Field(i)
if value2.Kind() == reflect.String {
value2.SetString(elmType.Field(i).Name)
}
}
/*
data := make([]map[string]interface{}, sliceLen)
for k := range data {
data[k] = utils.Struct2MapByJson(oneData)
}
//*/
//*
data := make([]*XXXX, 1000)
for k := range data {
copied := *oneData
data[k] = &copied
}
//*/
cc.Data = data
for i := 0; i < b.N; i++ {
Obj2Excel([]*Obj2ExcelSheetConfig{cc})
}
// b.Log(utils.Format4Output(data, false))
}

View File

@@ -9,7 +9,6 @@ import (
"git.rosy.net.cn/jx-callback/business/jxcallback/auth" "git.rosy.net.cn/jx-callback/business/jxcallback/auth"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego"
) )
type IAuther interface { type IAuther interface {
@@ -71,7 +70,7 @@ func New(notUsed interface{}, token string, w http.ResponseWriter, r *http.Reque
} }
} }
if err == model.ErrTokenIsInvalid { if err == model.ErrTokenIsInvalid {
if beego.BConfig.RunMode != "prod" { if !globals.IsProductEnv() {
err = nil err = nil
} else { } else {
errCode = model.ErrCodeTokenIsInvalid errCode = model.ErrCodeTokenIsInvalid

View File

@@ -1,10 +1,13 @@
package jxutils package jxutils
import ( import (
"bytes"
"context"
"fmt" "fmt"
"math" "math"
"math/rand" "math/rand"
"regexp" "regexp"
"sort"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -14,7 +17,9 @@ import (
"git.rosy.net.cn/baseapi/utils/routinepool" "git.rosy.net.cn/baseapi/utils/routinepool"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
"github.com/qiniu/api.v7/storage"
) )
var ( var (
@@ -27,6 +32,25 @@ type SyncMapWithTimeout struct {
timers sync.Map timers sync.Map
} }
type OrderSkuList []*model.OrderSku
func (l OrderSkuList) Len() int {
return len(l)
}
// Less reports whether the element with
// index i should sort before the element with index j.
func (l OrderSkuList) Less(i, j int) bool {
return l[i].SalePrice < l[j].SalePrice
}
// Swap swaps the elements with indexes i and j.
func (l OrderSkuList) Swap(i, j int) {
tmp := l[i]
l[i] = l[j]
l[j] = tmp
}
func init() { func init() {
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
routinePool = routinepool.New(1000, 1000) routinePool = routinepool.New(1000, 1000)
@@ -82,6 +106,13 @@ func GetSkuIDFromOrderSku(sku *model.OrderSku) (skuID int) {
return sku.SkuID return sku.SkuID
} }
func GetSaleStoreIDFromAfsOrder(order *model.AfsOrder) (retVal int) {
if order.JxStoreID > 0 {
return order.JxStoreID
}
return order.StoreID
}
func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID int) { func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID int) {
index := strings.Index(universalOrderID, "|") index := strings.Index(universalOrderID, "|")
if index != -1 { if index != -1 {
@@ -122,6 +153,19 @@ func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) {
return vendorID return vendorID
} }
func GetPossibleVendorIDFromAfsOrderID(afsOrderID string) (vendorID int) {
vendorID = model.VendorIDUnknown
if afsOrderIDInt64 := utils.Str2Int64WithDefault(afsOrderID, 0); afsOrderIDInt64 > 0 {
orderIDLen := len(afsOrderID)
if orderIDLen == len("22586438") {
vendorID = model.VendorIDJD
} else if orderIDLen == len("579557034") {
vendorID = model.VendorIDEBAI
}
}
return vendorID
}
func ComposeUniversalOrderID(orderID string, vendorID int) string { func ComposeUniversalOrderID(orderID string, vendorID int) string {
// return fmt.Sprintf("%s|%d", orderID, vendorID) // return fmt.Sprintf("%s|%d", orderID, vendorID)
return orderID // 当前用长度就能区分先不加上vendorID return orderID // 当前用长度就能区分先不加上vendorID
@@ -276,10 +320,7 @@ func ComposeSkuName(prefix, name, comment, unit string, spec_quality float32, sp
skuName += "(" + comment + ")" skuName += "(" + comment + ")"
} }
if maxLen > 0 { if maxLen > 0 {
runeList := []rune(skuName) skuName = utils.LimitUTF8StringLen(skuName, maxLen)
if len(runeList) > maxLen {
skuName = string(runeList[:maxLen])
}
} }
return skuName return skuName
} }
@@ -421,3 +462,122 @@ func HandleUserWXRemark(db *dao.DaoDB, mobile string) (err error) {
} }
return err return err
} }
func RefreshOrderSkuRelated(order *model.GoodsOrder) *model.GoodsOrder {
order.SkuCount = 0
order.GoodsCount = 0
order.SalePrice = 0
order.VendorPrice = 0
order.Weight = 0
order.OrderCreatedAt = order.StatusTime
for _, sku := range order.Skus {
if sku.SkuID > math.MaxInt32 {
sku.SkuID = 0
}
sku.OrderCreatedAt = order.OrderCreatedAt
order.SkuCount++
order.GoodsCount += sku.Count
order.SalePrice += sku.SalePrice * int64(sku.Count)
order.VendorPrice += sku.VendorPrice * int64(sku.Count)
order.Weight += sku.Weight * sku.Count
}
return order
}
func RefreshAfsOrderSkuRelated(afsOrder *model.AfsOrder) *model.AfsOrder {
afsOrder.SkuUserMoney = 0
afsOrder.PmSkuSubsidyMoney = 0
for _, orderSku := range afsOrder.Skus {
if orderSku.SkuID > math.MaxInt32 {
orderSku.SkuID = 0
}
afsOrder.SkuUserMoney += orderSku.UserMoney
afsOrder.PmSkuSubsidyMoney += orderSku.PmSkuSubsidyMoney
}
return afsOrder
}
func RemoveSkuFromOrder(order *model.GoodsOrder, removedSkuList []*model.OrderSku) *model.GoodsOrder {
removedSkuMap := make(map[int]*model.OrderSku)
removedSkuMap2 := make(map[string]*model.OrderSku)
for _, sku := range removedSkuList {
if skuID := GetSkuIDFromOrderSku(sku); skuID > 0 {
if removedSkuMap[skuID] == nil {
removedSkuMap[skuID] = sku
} else {
removedSkuMap[skuID].Count += sku.Count
}
}
if vendorSkuID := sku.VendorSkuID; vendorSkuID != "" {
if removedSkuMap2[vendorSkuID] == nil {
removedSkuMap2[vendorSkuID] = sku
} else {
removedSkuMap2[vendorSkuID].Count += sku.Count
}
}
}
var skuList []*model.OrderSku
sort.Sort(sort.Reverse(OrderSkuList(order.Skus)))
for _, sku := range order.Skus {
var removedSku *model.OrderSku
if skuID := GetSkuIDFromOrderSku(sku); skuID > 0 {
removedSku = removedSkuMap[skuID]
}
if removedSku == nil {
if vendorSkuID := sku.VendorSkuID; vendorSkuID != "" {
removedSku = removedSkuMap2[vendorSkuID]
}
}
copiedSku := *sku
tmp := &copiedSku
if removedSku != nil {
if removedSku.Count >= sku.Count {
tmp = nil
removedSku.Count -= sku.Count
} else {
tmp.Count -= removedSku.Count
removedSku.Count = 0
}
}
if tmp != nil {
skuList = append(skuList, tmp)
}
}
order.Skus = skuList
return RefreshOrderSkuRelated(order)
}
func UploadExportContent(content []byte, key string) (downloadURL string, err error) {
putPolicy := storage.PutPolicy{
Scope: globals.QiniuBucket,
Expires: 10 * 60,
DeleteAfterDays: 1,
}
upToken := putPolicy.UploadToken(api.QiniuAPI)
cfg := &storage.Config{}
formUploader := storage.NewFormUploader(cfg)
ret := storage.PutRet{}
for i := 0; i < 3; i++ {
if err = formUploader.Put(context.Background(), &ret, upToken, key, bytes.NewReader(content), int64(len(content)), &storage.PutExtra{}); err == nil {
break
}
}
if err == nil {
downloadURL = ComposeQiniuResURL(key)
}
return downloadURL, err
}
func TaskResult2Hint(resultList []interface{}) (hint string) {
strList := make([]string, len(resultList))
for k, v := range resultList {
strList[k] = fmt.Sprint(v)
}
hint = strings.Join(strList, ",")
return hint
}
// 这个函数用于将两个整数合并为一单一int64不要用于持久化的场景
func Combine2Int(int1, int2 int) (outInt int64) {
return int64(int1)*100000 + int64(int2)
}

View File

@@ -9,11 +9,13 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"git.rosy.net.cn/baseapi/platformapi" "git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
) )
var ( var (
@@ -82,9 +84,9 @@ func SplitStoreName(fullName, separator, defaultPrefix string) (prefix, bareName
func ComposeStoreName(bareName string, vendorID int) (fullName string) { func ComposeStoreName(bareName string, vendorID int) (fullName string) {
bareName = TrimDecorationChar(strings.Trim(bareName, "-")) bareName = TrimDecorationChar(strings.Trim(bareName, "-"))
if vendorID == model.VendorIDJD { if vendorID == model.VendorIDJD {
fullName = "京西菜市-" + bareName fullName = globals.StoreName + "-" + bareName
} else { } else {
fullName = "京西菜市(" + bareName + ")" fullName = globals.StoreName + "(" + bareName + ")"
} }
return fullName return fullName
} }
@@ -152,19 +154,24 @@ func Int64Map2List(int64Map map[int64]int) []int64 {
return retVal return retVal
} }
// 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克 func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) {
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
if skuNameUnit != "份" {
return unitPrice
}
lowerSpecUnit := strings.ToLower(specUnit) lowerSpecUnit := strings.ToLower(specUnit)
if lowerSpecUnit == "kg" || lowerSpecUnit == "l" { if lowerSpecUnit == "kg" || lowerSpecUnit == "l" {
specQuality *= 1000 specQuality *= 1000
} }
price := int(math.Round(float64(float32(unitPrice) * specQuality / 500))) return int(specQuality)
if specQuality < 250 { }
// 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
if skuNameUnit != model.SpecialUnit {
return unitPrice
}
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
price := int(math.Round(float64(unitPrice * specQuality2 / model.SpecialSpecQuality)))
if specQuality2 < 250 {
price = price * 110 / 100 price = price * 110 / 100
} else if specQuality < 500 { } else if specQuality2 < 500 {
price = price * 105 / 100 price = price * 105 / 100
} }
if price <= 0 { if price <= 0 {
@@ -173,35 +180,100 @@ func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNa
return price return price
} }
// 计算SKU标准价格CaculateSkuPrice的逆过程
func CaculateUnitPrice(skuPrice int, specQuality float32, specUnit string, skuNameUnit string) (unitPrice int) {
if skuNameUnit != model.SpecialUnit {
return skuPrice
}
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
unitPrice = skuPrice * model.SpecialSpecQuality / specQuality2
if specQuality2 < 250 {
unitPrice = unitPrice * 100 / 110
} else if specQuality2 < 500 {
unitPrice = unitPrice * 100 / 105
}
if unitPrice <= 0 {
unitPrice = 1
}
return unitPrice
}
func GetSliceLen(list interface{}) int { func GetSliceLen(list interface{}) int {
return reflect.ValueOf(list).Len() return reflect.ValueOf(list).Len()
} }
func CaculateSkuVendorPrice(price int, percentage int) int { func CaculateSkuVendorPrice(price, percentage, catPercentage int) int {
storePrice := int(math.Round(float64(price*percentage) / 100)) if percentage <= 10 || percentage >= 400 {
if storePrice < 0 { percentage = 100
storePrice = 0
} }
return storePrice if catPercentage <= 10 || catPercentage >= 400 {
catPercentage = 100
}
percentage = percentage * catPercentage / 100
vendorPrice := int(math.Round(float64(price*percentage) / 100))
if vendorPrice < 0 {
vendorPrice = 0
}
return vendorPrice
} }
func CaculateSkuPriceFromVendor(vendorPrice, percentage, catPercentage int) int {
if percentage <= 10 || percentage >= 400 {
percentage = 100
}
if catPercentage <= 10 || catPercentage >= 400 {
catPercentage = 100
}
percentage = percentage * catPercentage / 100
price := int(math.Round(float64(vendorPrice * 100 / percentage)))
if price < 0 {
price = 0
}
return price
}
func IsSkuSpecial(specQuality float32, specUnit string) bool {
return int(specQuality) == model.SpecialSpecQuality && (specUnit == model.SpecialSpecUnit || specUnit == model.SpecialSpecUnit2)
}
var lastFakeID int64
var lastFakeIDMutex sync.RWMutex
// 生成一个不重复的临时ID // 生成一个不重复的临时ID
func genFakeID1() int64 { func genFakeID1() int64 {
return time.Now().UnixNano() / 1000000 for {
fakeID := time.Now().UnixNano() / 1000
lastFakeIDMutex.RLock()
if fakeID == lastFakeID {
lastFakeIDMutex.RUnlock()
time.Sleep(1 * time.Microsecond)
} else {
lastFakeIDMutex.RUnlock()
lastFakeIDMutex.Lock()
defer lastFakeIDMutex.Unlock()
lastFakeID = fakeID
return fakeID
}
}
} }
// 这个用于没有打开远程同步时的假同步生成ID使用
func GenFakeID() int64 { func GenFakeID() int64 {
return genFakeID1() * 3 return genFakeID1() * 3
} }
func IsFakeID(id int64) bool { func IsFakeID(id int64) bool {
if id == 0 { if IsEmptyID(id) {
return true return true
} }
multiple := id / genFakeID1() multiple := id / genFakeID1()
return multiple >= 2 && multiple <= 4 return multiple >= 2 && multiple <= 4
} }
func IsEmptyID(id int64) bool {
return id == 0
}
func FormalizePageSize(pageSize int) int { func FormalizePageSize(pageSize int) int {
if pageSize == 0 { if pageSize == 0 {
return model.DefPageSize return model.DefPageSize
@@ -238,11 +310,7 @@ func IsLegalStoreID(id int) bool {
// 将规格转为重量 // 将规格转为重量
func FormatSkuWeight(specQuality float32, specUnit string) int { func FormatSkuWeight(specQuality float32, specUnit string) int {
lowerSpecUnit := strings.ToLower(specUnit) return RegularizeSkuQuality(specQuality, specUnit)
if lowerSpecUnit == "kg" || lowerSpecUnit == "l" {
specQuality *= 1000
}
return int(specQuality)
} }
type SkuList []*model.Sku type SkuList []*model.Sku
@@ -284,7 +352,7 @@ func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err err
///// /////
func GenPicFileName(suffix string) string { func GenPicFileName(suffix string) string {
return fmt.Sprintf("%x%s", md5.Sum([]byte(utils.GetUUID()+suffix)), suffix) return fmt.Sprintf("image/%x%s", md5.Sum([]byte(utils.GetUUID()+suffix)), suffix)
} }
func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) { func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) {
@@ -293,7 +361,7 @@ func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) {
vendorID = model.VendorIDJD vendorID = model.VendorIDJD
} else if vendorStoreID > 1234567 && vendorStoreID < 9876543 { // 美团外卖 24617137位 } else if vendorStoreID > 1234567 && vendorStoreID < 9876543 { // 美团外卖 24617137位
vendorID = model.VendorIDMTWM vendorID = model.VendorIDMTWM
} else if vendorStoreID > 1234567890 && vendorStoreID < 9987654321 { // 饿百 216700260710位 } else if vendorStoreID > 1234567890 && vendorStoreID < 99876543210 { // 饿百 216700260710位11位
vendorID = model.VendorIDEBAI vendorID = model.VendorIDEBAI
} else if vendorStoreID > 123456789 && vendorStoreID < 987654321 { // 微盟微商城 1320910489位 } else if vendorStoreID > 123456789 && vendorStoreID < 987654321 { // 微盟微商城 1320910489位
vendorID = model.VendorIDWSC vendorID = model.VendorIDWSC
@@ -306,10 +374,3 @@ func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) {
func GetVendorName(vendorID int) (vendorName string) { func GetVendorName(vendorID int) (vendorName string) {
return model.VendorChineseNames[vendorID] return model.VendorChineseNames[vendorID]
} }
func AddVendorInfo2Err(inErr error, vendorID int) (outErr error) {
if inErr != nil {
outErr = fmt.Errorf("处理平台%s, %s", model.VendorChineseNames[vendorID], inErr.Error())
}
return outErr
}

View File

@@ -21,7 +21,9 @@ func SendUserMessage(userID, title, content string) (err error) {
if len(content) > dingdingapi.MaxWorkContentLen { if len(content) > dingdingapi.MaxWorkContentLen {
content = content[:dingdingapi.MaxWorkContentLen-4] + "..." content = content[:dingdingapi.MaxWorkContentLen-4] + "..."
} }
if globals.IsProductEnv() {
err = api.DingDingAPI.CorpAsyncSendSimple(auth.AuthID, content) err = api.DingDingAPI.CorpAsyncSendSimple(auth.AuthID, content)
}
break break
} }
} }

View File

@@ -14,7 +14,7 @@ import (
const ( const (
testVendorOrderID = "test" testVendorOrderID = "test"
realTestVendorOrderID = "817102016000041" realTestVendorOrderID = "901234567890123"
realTestOrderVendorID = model.VendorIDJD realTestOrderVendorID = model.VendorIDJD
) )
@@ -27,14 +27,6 @@ func PrintOrder(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (pri
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID) order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
if err == nil { if err == nil {
if vendorOrderID == realTestVendorOrderID { if vendorOrderID == realTestVendorOrderID {
order.BuyerComment = "用户备注"
order.ConsigneeAddress = "四川省成都市某个地方"
order.ConsigneeLat = 30695171
order.ConsigneeLng = 104056984
order.ConsigneeName = "用户姓名"
order.ConsigneeMobile = "13812345678"
order.ConsigneeMobile2 = "13812345678"
order.StoreName = "京西菜市-测试门店"
order.StoreID = storeID order.StoreID = storeID
order.JxStoreID = storeID order.JxStoreID = storeID
} }

View File

@@ -80,7 +80,7 @@ func RefreshConfig(configKey string, expiresTime time.Duration, configGetter fun
} }
if handleType != 0 { if handleType != 0 {
if curConfig.Token, curConfig.Date = configGetter(); curConfig.Token == "" { if curConfig.Token, curConfig.Date = configGetter(); curConfig.Token == "" {
if beego.BConfig.RunMode == "prod" { if globals.IsProductEnv() {
globals.SugarLogger.Errorf("RefreshConfig %s get empty token", configKey) globals.SugarLogger.Errorf("RefreshConfig %s get empty token", configKey)
sleepDuration = errRefreshGap sleepDuration = errRefreshGap
} else { } else {
@@ -126,7 +126,7 @@ func RefreshWeixinToken() (err error) {
if api.WeixinAPI != nil { if api.WeixinAPI != nil {
err = RefreshConfig("wechat", weixinTokenExpires, func() (token string, expireTimeStr string) { err = RefreshConfig("wechat", weixinTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshWeixinToken RunMode:%s", beego.BConfig.RunMode) globals.SugarLogger.Debugf("RefreshWeixinToken RunMode:%s", beego.BConfig.RunMode)
if globals.IsProductEnv() { if globals.IsProductEnv() || beego.BConfig.RunMode == "alpha" {
if globals.IsMainProductEnv() { if globals.IsMainProductEnv() {
if tokenInfo, err := api.WeixinAPI.CBRetrieveToken(); err == nil { if tokenInfo, err := api.WeixinAPI.CBRetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshWeixinToken tokenInfo:%s", utils.Format4Output(tokenInfo, true)) globals.SugarLogger.Debugf("RefreshWeixinToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
@@ -154,7 +154,7 @@ func RefreshWeixinToken() (err error) {
func RefreshElmToken() (err error) { func RefreshElmToken() (err error) {
if api.ElmAPI != nil { if api.ElmAPI != nil {
err = RefreshConfig("eleme", elmTokenExpires, func() (string, string) { err = RefreshConfig("eleme", elmTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" { if globals.IsProductEnv() {
if tokenInfo, err := api.ElmAPI.RefreshTokenIndividual(); err == nil { if tokenInfo, err := api.ElmAPI.RefreshTokenIndividual(); err == nil {
tokenInfo2 := &ElmTokenForCompatible{ tokenInfo2 := &ElmTokenForCompatible{
Error: "", Error: "",
@@ -183,7 +183,7 @@ func RefreshElmToken() (err error) {
func RefreshWeimobToken() (err error) { func RefreshWeimobToken() (err error) {
if api.WeimobAPI != nil { if api.WeimobAPI != nil {
err = RefreshConfig("weimob", weimobTokenExpires, func() (string, string) { err = RefreshConfig("weimob", weimobTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" { if globals.IsProductEnv() {
if tokenInfo, err := api.WeimobAPI.RefreshTokenByRefreshToken(); err == nil { if tokenInfo, err := api.WeimobAPI.RefreshTokenByRefreshToken(); err == nil {
return string(utils.MustMarshal(tokenInfo)), utils.Time2Str(time.Now().Add((time.Duration(tokenInfo.ExpiresIn) - weimobTokenExpires/time.Second) * time.Second)) return string(utils.MustMarshal(tokenInfo)), utils.Time2Str(time.Now().Add((time.Duration(tokenInfo.ExpiresIn) - weimobTokenExpires/time.Second) * time.Second))
} }
@@ -204,7 +204,7 @@ func RefreshDingDingToken() (err error) {
api.DingDingAPI.RetrieveToken() api.DingDingAPI.RetrieveToken()
return RefreshConfig("dingding", dingdingTokenExpires, func() (string, string) { return RefreshConfig("dingding", dingdingTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshDingDingToken RunMode:%s", beego.BConfig.RunMode) globals.SugarLogger.Debugf("RefreshDingDingToken RunMode:%s", beego.BConfig.RunMode)
if true { //beego.BConfig.RunMode == "prod" { if true { //globals.IsProductEnv() {
if token, err := api.DingDingAPI.RetrieveToken(); err == nil { if token, err := api.DingDingAPI.RetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshDingDingToken tokenInfo:%s", token) globals.SugarLogger.Debugf("RefreshDingDingToken tokenInfo:%s", token)
return token, "" return token, ""
@@ -232,13 +232,16 @@ func SaveWeimobToken(token *weimobapi.TokenInfo) (err error) {
func RefreshYilianyunToken() (err error) { func RefreshYilianyunToken() (err error) {
return RefreshConfig("yilianyun", yilianyunTokenExpires, func() (string, string) { return RefreshConfig("yilianyun", yilianyunTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode) globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode)
if beego.BConfig.RunMode == "prod" { if globals.IsProductEnv() {
if globals.IsMainProductEnv() { // 只有京西菜市刷新易联云key
if tokenInfo, err := api.YilianyunAPI.RetrieveToken(); err == nil { if tokenInfo, err := api.YilianyunAPI.RetrieveToken(); err == nil {
return string(utils.MustMarshal(tokenInfo)), "" return string(utils.MustMarshal(tokenInfo)), ""
} else { } else {
globals.SugarLogger.Errorf("RefreshYilianyunToken RefreshToken failed with error:%v", err) globals.SugarLogger.Errorf("RefreshYilianyunToken RefreshToken failed with error:%v", err)
} }
} }
return api.YilianyunAPI.GetToken(), ""
}
return "", "" return "", ""
}, func(value string) { }, func(value string) {
var tokenInfo *yilianyunapi.TokenInfo var tokenInfo *yilianyunapi.TokenInfo

View File

@@ -134,13 +134,13 @@ func (task *ParallelTask) Run() {
} }
} else { } else {
globals.SugarLogger.Infof("ParallelTask.Run %s, subtask(job:%s, params:%s) result:%v, failed with error:%v", task.Name, utils.Format4Output(job, true), utils.Format4Output(task.params, true), result, err) globals.SugarLogger.Infof("ParallelTask.Run %s, subtask(job:%s, params:%s) result:%v, failed with error:%v", task.Name, utils.Format4Output(job, true), utils.Format4Output(task.params, true), result, err)
task.locker.Lock()
task.detailErrList = append(task.detailErrList, err)
task.locker.Unlock()
if !task.IsContinueWhenError { // 出错 if !task.IsContinueWhenError { // 出错
chanRetVal = err chanRetVal = err
goto end goto end
} }
task.locker.Lock()
task.detailErrMsgList = append(task.detailErrMsgList, err.Error())
task.locker.Unlock()
} }
} }
} }
@@ -188,12 +188,17 @@ func (task *ParallelTask) Run() {
} }
} }
if taskErr != nil { if taskErr != nil {
task.OriginalErr = taskErr
task.Err = NewTaskError(task.Name, taskErr) task.Err = NewTaskError(task.Name, taskErr)
} else { } else {
if len(task.detailErrList) > 0 {
task.OriginalErr = task.detailErrList[0]
}
task.Err = task.buildTaskErrFromDetail() task.Err = task.buildTaskErrFromDetail()
} }
task.Result = taskResult task.Result = taskResult
task.TerminatedAt = time.Now() task.TerminatedAt = time.Now()
task.jobList = nil // 如果不释放,任务被管理的话,会导致内存不能释放
task.locker.Unlock() task.locker.Unlock()
globals.SugarLogger.Debugf("ParallelTask.Run %s, err:%v", task.Name, task.Err) globals.SugarLogger.Debugf("ParallelTask.Run %s, err:%v", task.Name, task.Err)
close(task.subFinishChan) close(task.subFinishChan)

View File

@@ -44,13 +44,13 @@ func (task *SeqTask) Run() {
}) })
task.finishedOneJob(1, err) task.finishedOneJob(1, err)
if taskErr = err; taskErr != nil { if taskErr = err; taskErr != nil {
task.locker.Lock()
task.detailErrList = append(task.detailErrList, err)
task.locker.Unlock()
globals.SugarLogger.Infof("SeqTask.Run %s step:%d failed with error:%v", task.Name, i, err) globals.SugarLogger.Infof("SeqTask.Run %s step:%d failed with error:%v", task.Name, i, err)
if !task.IsContinueWhenError { if !task.IsContinueWhenError {
break break
} }
task.locker.Lock()
task.detailErrMsgList = append(task.detailErrMsgList, err.Error())
task.locker.Unlock()
} else if result != nil { } else if result != nil {
taskResult = append(taskResult, utils.Interface2Slice(result)...) taskResult = append(taskResult, utils.Interface2Slice(result)...)
} }
@@ -68,8 +68,12 @@ func (task *SeqTask) Run() {
} }
} }
if taskErr != nil { if taskErr != nil {
task.OriginalErr = taskErr
task.Err = NewTaskError(task.Name, taskErr) task.Err = NewTaskError(task.Name, taskErr)
} else { } else {
if len(task.detailErrList) > 0 {
task.OriginalErr = task.detailErrList[0]
}
task.Err = task.buildTaskErrFromDetail() task.Err = task.buildTaskErrFromDetail()
} }
task.Result = taskResult task.Result = taskResult

View File

@@ -57,6 +57,8 @@ type ITask interface {
AddChild(task ITask) ITask AddChild(task ITask) ITask
GetChildren() TaskList GetChildren() TaskList
SetParent(parentTask ITask) SetParent(parentTask ITask)
GetOriginalErr() error
GetDetailErrList() []error
json.Marshaler json.Marshaler
} }
@@ -103,11 +105,15 @@ type BaseTask struct {
FailedJobCount int `json:"failedJobCount"` FailedJobCount int `json:"failedJobCount"`
Status int `json:"status"` Status int `json:"status"`
NoticeMsg string `json:"noticeMsg"`
Result []interface{} `json:"-"` Result []interface{} `json:"-"`
Children TaskList `json:"children"` Children TaskList `json:"children"`
Err error `json:"err"` Err error `json:"err"`
OriginalErr error `json:"-"`
detailErrList []error
detailErrMsgList []string
finishChan chan struct{} finishChan chan struct{}
C <-chan struct{} `json:"-"` C <-chan struct{} `json:"-"`
params []interface{} params []interface{}
@@ -161,7 +167,7 @@ func (t *BaseTask) GetID() string {
func (t *BaseTask) GetResult(duration time.Duration) (retVal []interface{}, err error) { func (t *BaseTask) GetResult(duration time.Duration) (retVal []interface{}, err error) {
if t.GetStatus() >= TaskStatusEndBegin { if t.GetStatus() >= TaskStatusEndBegin {
return t.Result, t.Err return t.Result, t.OriginalErr
} }
if duration == 0 { if duration == 0 {
duration = time.Hour * 10000 // duration为0表示无限等待 duration = time.Hour * 10000 // duration为0表示无限等待
@@ -171,7 +177,7 @@ func (t *BaseTask) GetResult(duration time.Duration) (retVal []interface{}, err
case <-t.finishChan: case <-t.finishChan:
t.isGetResultCalled = true t.isGetResultCalled = true
timer.Stop() timer.Stop()
return t.Result, t.Err return t.Result, t.OriginalErr
case <-timer.C: case <-timer.C:
} }
return nil, ErrTaskNotFinished return nil, ErrTaskNotFinished
@@ -257,6 +263,30 @@ func (t *BaseTask) SetParent(parentTask ITask) {
t.parent = parentTask t.parent = parentTask
} }
func (t *BaseTask) SetNoticeMsg(noticeMsg string) {
t.locker.Lock()
defer t.locker.Unlock()
t.NoticeMsg = noticeMsg
}
func (t *BaseTask) GetNoticeMsg() string {
t.locker.RLock()
defer t.locker.RUnlock()
return t.NoticeMsg
}
func (t *BaseTask) GetOriginalErr() error {
t.locker.RLock()
defer t.locker.RUnlock()
return t.OriginalErr
}
func (t *BaseTask) GetDetailErrList() []error {
t.locker.RLock()
defer t.locker.RUnlock()
return t.detailErrList
}
func AddChild(parentTask ITask, task ITask) ITask { func AddChild(parentTask ITask, task ITask) ITask {
if parentTask != nil { if parentTask != nil {
return parentTask.AddChild(task) return parentTask.AddChild(task)
@@ -311,6 +341,10 @@ func (t *BaseTask) run(taskHandler func()) {
taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt)) taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt))
if t.Err == nil { if t.Err == nil {
content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status]) content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status])
noticeMsg := t.GetNoticeMsg()
if noticeMsg != "" {
content += ",通知消息:" + noticeMsg
}
} else { } else {
if t.Status == TaskStatusFinished { if t.Status == TaskStatusFinished {
content = fmt.Sprintf("%s执行部分失败,%s", taskDesc, t.Err.Error()) content = fmt.Sprintf("%s执行部分失败,%s", taskDesc, t.Err.Error())
@@ -347,8 +381,12 @@ func (t *BaseTask) setStatus(status int) {
} }
func (t *BaseTask) buildTaskErrFromDetail() (err error) { func (t *BaseTask) buildTaskErrFromDetail() (err error) {
if len(t.detailErrMsgList) > 0 { if len(t.detailErrList) > 0 {
return NewTaskError(t.Name, fmt.Errorf("总共:%d, 失败:%d, 详情:\n%s", t.TotalItemCount, t.FailedItemCount, strings.Join(t.detailErrMsgList, "\n"))) strList := make([]string, len(t.detailErrList))
for k, v := range t.detailErrList {
strList[k] = v.Error()
}
return NewTaskError(t.Name, fmt.Errorf("总共:%d, 失败:%d, 详情:\n%s", t.TotalItemCount, t.FailedItemCount, strings.Join(strList, "\n")))
} }
return nil return nil
} }

View File

@@ -5,6 +5,8 @@ import (
"strings" "strings"
"time" "time"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
@@ -49,7 +51,10 @@ const (
WX_NORMAL_STORE_MSG_TEMPLATE_ID = "7ngcTFYiUFw66BMzIYntM1tpy-xZkJwlcCT5pVtXwtw" WX_NORMAL_STORE_MSG_TEMPLATE_ID = "7ngcTFYiUFw66BMzIYntM1tpy-xZkJwlcCT5pVtXwtw"
WX_CHANGE_APPROVED_TEMPLATE_ID = "gIG2olBZtQbjXmp6doNB_dESu60By5xuXYOGxksLv3Y" WX_CHANGE_APPROVED_TEMPLATE_ID = "gIG2olBZtQbjXmp6doNB_dESu60By5xuXYOGxksLv3Y"
WX_CHANGE_REJECTED_TEMPLATE_ID = "tn2QXWi4HtSIwaztmtN6Bb2uzNL-jBxWltCZTDNJuYE" WX_CHANGE_REJECTED_TEMPLATE_ID = "tn2QXWi4HtSIwaztmtN6Bb2uzNL-jBxWltCZTDNJuYE"
WS_ORDER_CANCLED_TEMPLATE_ID = "iFozwiCsQdMs7VTiPXoBne45jKIQkoyxdGHSeAExP9U" WX_ORDER_CANCLED_TEMPLATE_ID = "iFozwiCsQdMs7VTiPXoBne45jKIQkoyxdGHSeAExP9U"
WX_AFS_ORDER_WAIT4APPROVE_TEMPLATE_ID = "X29udtANvhX6x1Lyh-T40NGNjRXBbUj5oSBTfDhZAqU"
WX_AFS_ORDER_STATUS_CHANGED_TEMPLATE_ID = "99T33rrXX0VboO1hljs4x8dDoLiSj3QX_rOikPHIXkg"
) )
var ( var (
@@ -305,7 +310,7 @@ func NotifyUserApplyCancel(order *model.GoodsOrder, cancelReason string) (err er
}, },
} }
storeID := jxutils.GetSaleStoreIDFromOrder(order) storeID := jxutils.GetSaleStoreIDFromOrder(order)
return SendMsgToStore(storeID, WS_ORDER_CANCLED_TEMPLATE_ID, "", "", data) return SendMsgToStore(storeID, WX_ORDER_CANCLED_TEMPLATE_ID, "", "", data)
} }
@@ -347,7 +352,7 @@ func NotifySaleBill(storeID int, title, shopName, fileURL string) (err error) {
title = "当期账单" title = "当期账单"
} }
if shopName == "" { if shopName == "" {
shopName = "京西菜市" shopName = globals.StoreName
} }
data := map[string]interface{}{ data := map[string]interface{}{
"first": map[string]interface{}{ "first": map[string]interface{}{
@@ -449,6 +454,54 @@ func NotifyStoreMessage(storeID, msgID, msgStatusID int, title, content string)
return SendMsgToStore(storeID, templateID, fileURL, fmt.Sprintf(WX_MINI_TO_SHOW_MSG, msgID, msgStatusID), data) return SendMsgToStore(storeID, templateID, fileURL, fmt.Sprintf(WX_MINI_TO_SHOW_MSG, msgID, msgStatusID), data)
} }
func NotifyAfsOrderStatus(afsOrder *model.AfsOrder) (err error) {
globals.SugarLogger.Debugf("NotifyAfsOrderStatus orderID:%s", afsOrder.VendorOrderID)
if afsOrder.VendorID == model.VendorIDELM {
return nil
}
var templateID, comment string
if afsOrder.Status == model.AfsOrderStatusWait4Approve {
templateID = WX_AFS_ORDER_WAIT4APPROVE_TEMPLATE_ID
comment = "您有新售后单,请尽快处理"
} else if afsOrder.Status == model.AfsOrderStatusWait4ReceiveGoods {
templateID = WX_AFS_ORDER_STATUS_CHANGED_TEMPLATE_ID
comment = "商家您好!如顾客商品已成功退回,请点击确认收货"
} else {
return err
}
order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID)
if err != nil {
return err
}
data := map[string]interface{}{
"first": map[string]interface{}{
"value": fmt.Sprintf("%s 第%d号订单订单编号:%s", model.VendorChineseNames[afsOrder.VendorID], order.OrderSeq, afsOrder.VendorOrderID),
"color": WX_HIGHLEVEL_TEMPLATE_COLOR2,
},
"keyword1": map[string]interface{}{
"value": afsOrder.AfsOrderID,
"color": WX_TEMPLATE_VENDERCOLOR_JDDJ,
},
"keyword2": map[string]interface{}{
"value": model.OrderStatusName[afsOrder.Status],
"color": WX_HIGHLEVEL_TEMPLATE_COLOR,
},
"keyword3": map[string]interface{}{
"value": utils.Time2Str(afsOrder.CreatedAt),
"color": venderColors[order.VendorID],
},
"remark": map[string]interface{}{
"value": comment,
"color": WX_NEW_ORDER_TEMPLATE_COLOR,
},
}
storeID := jxutils.GetSaleStoreIDFromAfsOrder(afsOrder)
err = SendMsgToStore(storeID, templateID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
return err
}
func FormatDeliveryTime(order *model.GoodsOrder) string { func FormatDeliveryTime(order *model.GoodsOrder) string {
var tmpTime time.Time var tmpTime time.Time
if order.ExpectedDeliveredTime == utils.DefaultTimeValue { if order.ExpectedDeliveredTime == utils.DefaultTimeValue {

135
business/model/act.go Normal file
View File

@@ -0,0 +1,135 @@
package model
import "time"
const (
ActSkuDirectDown = 1
ActSkuSecKill = 2
ActOrderBegin = 10
ActOrderMoneyOff = 11
ActOrderMoneyOffCoupon = 12
ActOrderReduceFreight = 13
ActOrderReduceFreightCoupon = 14
)
const (
ActStatusCreated = 1 // 需同步
ActStatusCanceled = 2 // 需同步
ActStatusEnded = 3 // 不需要同步,根据活动时间自动刷新的
)
type Act struct {
ModelIDCULD
Name string `orm:"size(64)" json:"name"`
Advertising string `orm:"size(255)" json:"advertising"`
Type int `json:"type"`
Status int `json:"status"`
LimitDevice int `json:"limitDevice"`
LimitPin int `json:"limitPin"`
LimitDaily int `json:"limitDaily"`
LimitCount int `json:"limitCount"`
Source string `orm:"size(255)" json:"source"`
CreateType int `json:"createType"`
PricePercentage int `json:"pricePercentage"` // 单品级活动才有效
BeginAt time.Time `orm:"type(datetime);index;null" json:"beginAt"`
EndAt time.Time `orm:"type(datetime);index;null" json:"endAt"`
}
type ActMap struct {
ModelIDCULD
ActID int `orm:"column(act_id)" json:"actID"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
VendorActID string `orm:"column(vendor_act_id);size(48)" json:"vendorActID"`
SyncStatus int `orm:"default(2)" json:"syncStatus"`
}
type Act2 struct {
MapID int `orm:"column(map_id)"`
Act
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
VendorActID string `orm:"column(vendor_act_id);size(48)" json:"vendorActID"`
SyncStatus int `orm:"default(2)" json:"syncStatus"`
}
type ActOrderRule struct {
ModelIDCULD
ActID int `orm:"column(act_id)" json:"actID"`
SalePrice int64 `orm:"" json:"salePrice"` // 满的价格
DeductPrice int64 `orm:"" json:"deductPrice"` // 减的价格
}
// type ActStore struct {
// ModelIDCULD
// ActID int `orm:"column(act_id)" json:"actID"`
// StoreID int `orm:"column(store_id)" json:"storeID"`
// }
type ActStoreMap struct {
ModelIDCULD
ActID int `orm:"column(act_id)" json:"actID"`
StoreID int `orm:"column(store_id)" json:"storeID"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
VendorActID string `orm:"column(vendor_act_id);size(48)" json:"vendorActID"`
SyncStatus int `orm:"default(2)" json:"syncStatus"`
}
type ActStore2 struct {
MapID int `orm:"column(map_id)"`
ActStoreMap
VendorStoreID string `orm:"column(vendor_store_id)" json:"vendorStoreID"`
}
type ActStoreSku struct {
ModelIDCULD
ActID int `orm:"column(act_id)" json:"actID"`
StoreID int `orm:"column(store_id)" json:"storeID"`
SkuID int `orm:"column(sku_id)" json:"skuID"`
// LocalStatus int // 这个状态是多个平台的
// RemoteStatus int // 这个状态是多个平台的
OriginalPrice int64 `orm:"" json:"originalPrice"` // 单品级活动用,创建活动时商品的原始京西价
PricePercentage int `orm:"" json:"pricePercentage"` // 单品级活动用SKU级的价格比例非0覆盖Act中的PricePercentage
ActPrice int64 `orm:"" json:"actPrice"` // 单品级活动用SKU级指定的价格非0覆盖CustomPricePercentage与Act中的PricePercentage
Stock int `orm:"" json:"stock"` // 订单级活动用
}
type ActStoreSkuMap struct {
ModelIDCULD
ActID int `orm:"column(act_id)" json:"actID"`
StoreID int `orm:"column(store_id)" json:"storeID"`
SkuID int `orm:"column(sku_id)" json:"skuID"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
VendorActID string `orm:"column(vendor_act_id);size(48)" json:"vendorActID"`
SyncStatus int `orm:"default(2)" json:"syncStatus"`
ActualActPrice int64 `orm:"" json:"actualActPrice"` // 单品级活动用,创建活动时商品的活动价格
}
type ActStoreSku2 struct {
MapID int `orm:"column(map_id)"`
ActStoreSku
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
VendorActID string `orm:"column(vendor_act_id);size(48)" json:"vendorActID"`
SyncStatus int `orm:"default(2)" json:"syncStatus"`
ActualActPrice int64 `orm:"" json:"actualActPrice"` // 单品级活动用,创建活动时商品的活动价格
VendorStoreID string `orm:"column(vendor_store_id)" json:"vendorStoreID"`
VendorSkuID string `orm:"column(vendor_sku_id)" json:"vendorSkuID"`
}

View File

@@ -12,15 +12,25 @@ const (
type GoodsOrderExt struct { type GoodsOrderExt struct {
GoodsOrder GoodsOrder
EarningPrice int64 `json:"earningPrice"` // 预估结算给门店老板的钱
WaybillStatus int `json:"waybillStatus"` WaybillStatus int `json:"waybillStatus"`
CourierName string `orm:"size(32)" json:"courierName"` CourierName string `orm:"size(32)" json:"courierName"`
CourierMobile string `orm:"size(32)" json:"courierMobile"` CourierMobile string `orm:"size(32)" json:"courierMobile"`
CurrentConsigneeMobile string `orm:"-" json:"currentConsigneeMobile"` CurrentConsigneeMobile string `orm:"-" json:"currentConsigneeMobile"`
CourierVendorName string `json:"courierVendorName"`
Status2 string `json:"status2"`
ActualFee int64 `json:"actualFee"` // 实际要支付给快递公司的费用 ActualFee int64 `json:"actualFee"` // 实际要支付给快递公司的费用
DesiredFee int64 `json:"desiredFee"` // 运单总费用 DesiredFee int64 `json:"desiredFee"` // 运单总费用
WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"` WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"`
WaybillFinishedAt time.Time `orm:"type(datetime)" json:"waybillFinishedAt"` WaybillFinishedAt time.Time `orm:"type(datetime)" json:"waybillFinishedAt"`
SkuID int `orm:"column(sku_id)" json:"skuID,omitempty"`
SkuShopPrice int `json:"skuShopPrice,omitempty"`
SkuSalePrice int `json:"skuSalePrice,omitempty"`
SkuCount2 int `json:"skuCount2,omitempty"`
SkuInfo string `json:"skuInfo,omitempty"`
} }
type OrderSkuExt struct { type OrderSkuExt struct {
@@ -30,11 +40,6 @@ type OrderSkuExt struct {
} }
type GoodsOrderCountInfo struct { type GoodsOrderCountInfo struct {
Status int `json:"status"`
Count int `json:"count"`
}
type GoodsOrderCountInfo2 struct {
LockStatus int `json:"lockStatus"` LockStatus int `json:"lockStatus"`
Status int `json:"status"` Status int `json:"status"`
Count int `json:"count"` Count int `json:"count"`
@@ -61,3 +66,8 @@ type OrderFinancialExt struct {
Skus []*OrderSkuFinancial `orm:"-" json:"skus"` // 正向订单购买商品列表 Skus []*OrderSkuFinancial `orm:"-" json:"skus"` // 正向订单购买商品列表
Discounts []*OrderDiscountFinancial `orm:"-" json:"discounts"` // 正向订单享受优惠列表 Discounts []*OrderDiscountFinancial `orm:"-" json:"discounts"` // 正向订单享受优惠列表
} }
type OrderFinancialSkuExt struct {
OrderSkuFinancial
Image string `json:"image"`
}

View File

@@ -14,9 +14,9 @@ const (
VendorIDMTWM = 1 VendorIDMTWM = 1
VendorIDELM = 2 VendorIDELM = 2
VendorIDEBAI = 3 VendorIDEBAI = 3
VendorIDJX = 9 // 这是一个假的京西VendorID
VendorIDWSC = 11 // 微盟微商城 VendorIDWSC = 11 // 微盟微商城
VendorIDPurchaseEnd = 11 VendorIDPurchaseEnd = 11
VendorIDJX = 99 // 这是一个假的京西VendorID
VendorIDDeliveryBegin = 101 VendorIDDeliveryBegin = 101
VendorIDDada = 101 VendorIDDada = 101
@@ -112,15 +112,26 @@ var (
OrderStatusDeliverFailed: "投递失败", OrderStatusDeliverFailed: "投递失败",
OrderStatusFinished: "完成", OrderStatusFinished: "完成",
OrderStatusCanceled: "取消", OrderStatusCanceled: "取消",
AfsOrderStatusWait4Approve: "待审核",
AfsOrderStatusNew: "已审核",
AfsOrderStatusWait4ReceiveGoods: "退货待确认",
AfsOrderStatusReceivedGoods: "退货已收到",
AfsOrderStatusFinished: "售后成功",
AfsOrderStatusFailed: "售后失败",
} }
WaybillStatusName = map[int]string{ WaybillStatusName = map[int]string{
WaybillStatusUnknown: "一般事件", WaybillStatusUnknown: "一般事件",
WaybillStatusNew: "新运单", WaybillStatusNew: "新运单",
WaybillStatusPending: "压单",
WaybillStatusAcceptCanceled: "取消接受", WaybillStatusAcceptCanceled: "取消接受",
WaybillStatusAccepted: "已接单", WaybillStatusAccepted: "已接单",
WaybillStatusCourierArrived: "已到店", WaybillStatusCourierArrived: "已到店",
WaybillStatusApplyFailedGetGoods: "取货失败待审核",
WaybillStatusAgreeFailedGetGoods: "取货失败",
WaybillStatusDelivering: "配送中", WaybillStatusDelivering: "配送中",
WaybillStatusDeliverFailed: "投递失败",
WaybillStatusDelivered: "送达", WaybillStatusDelivered: "送达",
WaybillStatusCanceled: "取消", WaybillStatusCanceled: "取消",
WaybillStatusFailed: "失败", WaybillStatusFailed: "失败",
@@ -128,6 +139,7 @@ var (
OrderTypeName = map[int]string{ OrderTypeName = map[int]string{
OrderTypeOrder: "订单", OrderTypeOrder: "订单",
OrderTypeWaybill: "运单", OrderTypeWaybill: "运单",
OrderTypeAfsOrder: "售后单",
} }
MultiStoresVendorMap = map[int]int{ MultiStoresVendorMap = map[int]int{
@@ -159,11 +171,30 @@ var (
"打印机密钥", "打印机密钥",
}, },
} }
AfsReasonTypeName = map[int]string{
AfsReasonTypeGoodsQuality: "商品质量",
AfsReasonTypeWrongGoods: "错误的商品",
AfsReasonTypeMissingGoods: "缺少部分商品",
AfsReasonTypeNoGoods: "全部商品未收到",
AfsReasonTypeDamagedGoods: "商品有损伤",
AfsReasonTypeGoodsQuantity: "缺斤少两",
AfsReasonTypeAgreedByMerchant: "与商家协商一致",
AfsReasonTypeGoodsNoSame: "商品与描述不符",
AfsReasonWrongPurchase: "误购",
AfsReasonNotReceivedIntime: "未在时效内送达",
AfsReasonNotOthers: "其它",
}
AfsAppealTypeName = map[int]string{
AfsAppealTypeRefund: "仅退款",
AfsAppealTypeReturnAndRefund: "退货退款",
AfsAppealTypeNewGoods: "重发商品",
}
) )
const ( const (
OrderTypeOrder = 1 OrderTypeOrder = 1
OrderTypeWaybill = 2 OrderTypeWaybill = 2
OrderTypeAfsOrder = 3
) )
// https://blog.csdn.net/a13570320979/article/details/51366355 // https://blog.csdn.net/a13570320979/article/details/51366355
@@ -212,6 +243,14 @@ const (
OrderStatusEndBegin = 100 // 以下的状态就是结束状态 OrderStatusEndBegin = 100 // 以下的状态就是结束状态
OrderStatusFinished = 110 // 订单已完成 OrderStatusFinished = 110 // 订单已完成
OrderStatusCanceled = 115 // 订单已取消 OrderStatusCanceled = 115 // 订单已取消
OrderStatusEndEnd = 120
AfsOrderStatusWait4Approve = 155 // 待审核售后单
AfsOrderStatusNew = 160 // 已审核或不需要审核售后单
AfsOrderStatusWait4ReceiveGoods = 165 // 退款退货的,需要商家确认收到货
AfsOrderStatusReceivedGoods = 167 // 已确认收到货
AfsOrderStatusFinished = 180 // 售后单成功完成
AfsOrderStatusFailed = 190 // 售后单失败
) )
const ( const (
@@ -224,6 +263,7 @@ const (
WaybillStatusUnknown = 0 WaybillStatusUnknown = 0
WaybillStatusNew = 5 WaybillStatusNew = 5
WaybillStatusPending = 7
WaybillStatusAcceptCanceled = 8 WaybillStatusAcceptCanceled = 8
WaybillStatusAccepted = 10 WaybillStatusAccepted = 10
WaybillStatusCourierArrived = 15 // 此状态是可选的,明确写出来是因为还是较重要的状态,但业务逻辑不应依赖此状态 WaybillStatusCourierArrived = 15 // 此状态是可选的,明确写出来是因为还是较重要的状态,但业务逻辑不应依赖此状态
@@ -238,7 +278,6 @@ const (
WaybillStatusDelivered = 105 // todo 这个应该改为110与订单对应 WaybillStatusDelivered = 105 // todo 这个应该改为110与订单对应
WaybillStatusCanceled = 115 WaybillStatusCanceled = 115
WaybillStatusFailed = 120 // 这个状态存在的意义是区分于WaybillStatusCanceled比如达达平台在这种状态下再次创建运单的方式不一样 WaybillStatusFailed = 120 // 这个状态存在的意义是区分于WaybillStatusCanceled比如达达平台在这种状态下再次创建运单的方式不一样
WaybillStatusNeverSend = 125 // 这个状态指的是平台方不愿意配送门店自己想办法。与WaybillStatusAcceptCanceled不一样WaybillStatusAcceptCanceled可能之后还会尝试配送
) )
const ( const (
@@ -261,6 +300,13 @@ const (
const ( const (
OrderDeliveryFlagMaskScheduleDisabled = 1 // 禁止三方配送调度 OrderDeliveryFlagMaskScheduleDisabled = 1 // 禁止三方配送调度
OrderDeliveryFlagMaskPurcahseDisabled = 2 // 购物平台已不配送(一般为门店配送类型本身为自配送,或已经转自配送) OrderDeliveryFlagMaskPurcahseDisabled = 2 // 购物平台已不配送(一般为门店配送类型本身为自配送,或已经转自配送)
OrderDeliveryFlagMaskDada = 16 // 创建达达运单中
OrderDeliveryFlagMaskMtps = 32 // 创建美团配送运单中
)
const (
WaybillDeliveryFlagMaskActiveCancel = 1 // 主动取消
) )
const ( const (
@@ -276,6 +322,41 @@ const (
OrderFlagMaskFailedDeliver = 32 OrderFlagMaskFailedDeliver = 32
OrderFlagMaskCallPMCourier = 64 // 取货失败后召唤平台配送 OrderFlagMaskCallPMCourier = 64 // 取货失败后召唤平台配送
OrderFlagMaskSetDelivered = 128 // 设置送达
)
const (
AfsOrderFlagMaskUserRefund = 3 // 门店处理售后单申请
AfsOrderFlagAgreeUserRefund = 1 // 门店同意售后单申请
AfsOrderFlagRefuseUserRefund = 3 // 门店拒绝售后单申请
AfsOrderFlagMaskReturnGoods = 4 // 门店确认收到退货
)
const (
AfsAppealTypeRefund = 1 // 仅退款
AfsAppealTypeReturnAndRefund = 2 // 退货退款
AfsAppealTypeNewGoods = 3 // 重发新商品(即京东到家的直赔)
)
const (
AfsReasonTypeGoodsQuality = 1 // 商品质量
AfsReasonTypeWrongGoods = 2 // 错误的商品
AfsReasonTypeMissingGoods = 3 // 缺少部分商品
AfsReasonTypeNoGoods = 4 // 全部商品未收到
AfsReasonTypeDamagedGoods = 5 // 商品有损伤
AfsReasonTypeGoodsQuantity = 6 // 缺斤少两
AfsReasonTypeAgreedByMerchant = 7 // 与商家协商一致
AfsReasonTypeGoodsNoSame = 8 // 商品与描述不符
AfsReasonWrongPurchase = 9 // 误购
AfsReasonNotReceivedIntime = 10 // 未在时效内送达
AfsReasonNotOthers = 0 // 其它
)
const (
AfsTypeUnknown = 0 // 未知
AfsTypePartRefund = 1 // 部分退款
AfsTypeFullRefund = 2 // 全额退款
) )
func IsPurchaseVendorExist(vendorID int) bool { func IsPurchaseVendorExist(vendorID int) bool {
@@ -306,9 +387,22 @@ func IsOrderMainStatus(status int) bool {
} }
func IsOrderFinalStatus(status int) bool { func IsOrderFinalStatus(status int) bool {
return status >= OrderStatusEndBegin return status >= OrderStatusEndBegin && status <= OrderStatusEndEnd
} }
func IsOrderImportantStatus(status int) bool { func IsOrderImportantStatus(status int) bool {
return IsOrderMainStatus(status) || IsOrderLockStatus(status) || IsOrderUnlockStatus(status) return IsOrderMainStatus(status) || IsOrderLockStatus(status) || IsOrderUnlockStatus(status)
} }
func WaybillVendorID2Mask(vendorID int) (mask int8) {
if vendorID == VendorIDDada {
mask = OrderDeliveryFlagMaskDada
} else if vendorID == VendorIDMTPS {
mask = OrderDeliveryFlagMaskMtps
}
return mask
}
func IsAfsOrderFinalStatus(status int) bool {
return status >= AfsOrderStatusFinished && status <= AfsOrderStatusFailed
}

117
business/model/dao/act.go Normal file
View File

@@ -0,0 +1,117 @@
package dao
import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
)
func GetActVendorInfo(db *DaoDB, actID int, vendorIDs []int) (actMap map[int]*model.Act2, err error) {
sql := `
SELECT t1.*,
t2.id map_id, t2.vendor_id, t2.vendor_act_id, t2.sync_status
FROM act t1
JOIN act_map t2 ON t2.act_id = t1.id AND t2.deleted_at = ?
WHERE t1.deleted_at = ? AND t1.id = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
utils.DefaultTimeValue,
actID,
}
if len(vendorIDs) > 0 {
sql += " AND t2.vendor_id IN (" + GenQuestionMarks(len(vendorIDs)) + ")"
sqlParams = append(sqlParams, vendorIDs)
}
var actList []*model.Act2
if err = GetRows(db, &actList, sql, sqlParams...); err == nil {
actMap = make(map[int]*model.Act2)
for _, v := range actList {
actMap[v.VendorID] = v
}
}
return actMap, err
}
func GetActStoreVendorInfo(db *DaoDB, actID int, vendorIDs, storeIDs []int) (actStoreMap map[int][]*model.ActStore2, err error) {
sql := `
SELECT t1.*,
t1.id map_id,
t2.vendor_store_id
FROM act_store_map t1
JOIN store_map t2 ON t2.store_id = t1.store_id AND t2.vendor_id = t1.vendor_id AND t2.deleted_at = ?
WHERE t1.deleted_at = ? AND t1.act_id = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
utils.DefaultTimeValue,
actID,
}
if len(vendorIDs) > 0 {
sql += " AND t1.vendor_id IN (" + GenQuestionMarks(len(vendorIDs)) + ")"
sqlParams = append(sqlParams, vendorIDs)
}
if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
var actStoreList []*model.ActStore2
if err = GetRows(db, &actStoreList, sql, sqlParams...); err == nil {
actStoreMap = make(map[int][]*model.ActStore2)
for _, v := range actStoreList {
actStoreMap[v.VendorID] = append(actStoreMap[v.VendorID], v)
}
}
return actStoreMap, err
}
func GetActStoreSkuVendorInfo(db *DaoDB, actID int, vendorIDs, storeIDs, skuIDs []int) (actStoreSkuMap map[int][]*model.ActStoreSku2, err error) {
sql := `
SELECT t1.*,
t2.id map_id, t2.vendor_id, t2.vendor_act_id, t2.sync_status,
t3.vendor_store_id,
CASE t2.vendor_id
WHEN 0 THEN
t4.jd_id
WHEN 1 THEN
t5.mtwm_id
WHEN 3 THEN
t5.ebai_id
ELSE
''
END vendor_sku_id
FROM act_store_sku t1
JOIN act_store_sku_map t2 ON t2.act_id = t1.act_id AND t2.sku_id = t1.sku_id AND t2.store_id = t1.store_id AND t2.deleted_at = ?
JOIN store_map t3 ON t3.store_id = t1.store_id AND t3.vendor_id = t2.vendor_id AND t3.deleted_at = ?
JOIN sku t4 ON t4.id = t1.sku_id AND t4.deleted_at = ?
JOIN store_sku_bind t5 ON t5.sku_id = t1.sku_id AND t5.store_id = t1.store_id AND t5.deleted_at = ?
WHERE t1.deleted_at = ? AND t1.act_id = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
actID,
}
if len(vendorIDs) > 0 {
sql += " AND t2.vendor_id IN (" + GenQuestionMarks(len(vendorIDs)) + ")"
sqlParams = append(sqlParams, vendorIDs)
}
if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
if len(skuIDs) > 0 {
sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
var actStoreSkuList []*model.ActStoreSku2
if err = GetRows(db, &actStoreSkuList, sql, sqlParams...); err == nil {
actStoreSkuMap = make(map[int][]*model.ActStoreSku2)
for _, v := range actStoreSkuList {
actStoreSkuMap[v.VendorID] = append(actStoreSkuMap[v.VendorID], v)
}
}
return actStoreSkuMap, err
}

View File

@@ -58,7 +58,7 @@ func UpdateEntityByKV(db *DaoDB, item interface{}, kvs map[string]interface{}, c
} }
func UpdateEntityLogically(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}) (num int64, err error) { func UpdateEntityLogically(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}) (num int64, err error) {
if conditions != nil { if conditions != nil && refutil.IsFieldExist(item, model.FieldDeletedAt) {
conditions = utils.MergeMaps(conditions, map[string]interface{}{ conditions = utils.MergeMaps(conditions, map[string]interface{}{
model.FieldDeletedAt: utils.DefaultTimeValue, model.FieldDeletedAt: utils.DefaultTimeValue,
}) })
@@ -71,7 +71,7 @@ func UpdateEntityLogically(db *DaoDB, item interface{}, kvs map[string]interface
// 此函数会更新同步标志 // 此函数会更新同步标志
func UpdateEntityLogicallyAndUpdateSyncStatus(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}, syncStatusFieldName string, valueMask int) (num int64, err error) { func UpdateEntityLogicallyAndUpdateSyncStatus(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}, syncStatusFieldName string, valueMask int) (num int64, err error) {
if conditions != nil { if conditions != nil && refutil.IsFieldExist(item, model.FieldDeletedAt) {
conditions = utils.MergeMaps(conditions, map[string]interface{}{ conditions = utils.MergeMaps(conditions, map[string]interface{}{
model.FieldDeletedAt: utils.DefaultTimeValue, model.FieldDeletedAt: utils.DefaultTimeValue,
}) })
@@ -115,6 +115,7 @@ func AddStoreCategoryMap(db *DaoDB, storeID, categoryID int, vendorID int, vendo
StoreID: storeID, StoreID: storeID,
CategoryID: categoryID, CategoryID: categoryID,
MtwmSyncStatus: model.SyncFlagNewMask, MtwmSyncStatus: model.SyncFlagNewMask,
EbaiSyncStatus: model.SyncFlagNewMask,
WscSyncStatus: model.SyncFlagNewMask, WscSyncStatus: model.SyncFlagNewMask,
} }
storeCat.DeletedAt = utils.DefaultTimeValue storeCat.DeletedAt = utils.DefaultTimeValue

View File

@@ -29,12 +29,12 @@ func SetOrderPrintFlag(db *DaoDB, userName string, vendorOrderID string, vendorI
if isPrinted { if isPrinted {
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, model.OrderFlagMaskPrinted) err = SetOrderFlag(db, userName, vendorOrderID, vendorID, model.OrderFlagMaskPrinted)
} else { } else {
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, ^int8(model.OrderFlagMaskPrinted)) err = SetOrderFlag(db, userName, vendorOrderID, vendorID, ^model.OrderFlagMaskPrinted)
} }
return err return err
} }
func SetOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, flag int8) (err error) { func SetOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, flag int) (err error) {
_, err = ExecuteSQL(db, ` _, err = ExecuteSQL(db, `
UPDATE goods_order UPDATE goods_order
SET flag = flag | ? SET flag = flag | ?
@@ -43,7 +43,7 @@ func SetOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int
return err return err
} }
func ClearOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, flag int8) (err error) { func ClearOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, flag int) (err error) {
_, err = ExecuteSQL(db, ` _, err = ExecuteSQL(db, `
UPDATE goods_order UPDATE goods_order
SET flag = flag & ? SET flag = flag & ?
@@ -51,3 +51,34 @@ func ClearOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID i
`, flag, vendorOrderID, vendorID) `, flag, vendorOrderID, vendorID)
return err return err
} }
func SetAfsOrderFlag(db *DaoDB, userName string, afsOrderID string, vendorID int, flag int) (err error) {
_, err = ExecuteSQL(db, `
UPDATE afs_order
SET flag = flag | ?
WHERE afs_order_id = ? AND vendor_id = ?
`, flag, afsOrderID, vendorID)
return err
}
func GetAfsOrders(db *DaoDB, vendorID int, vendorOrderID, afsOrderID string) (afsOrderList []*model.AfsOrder, err error) {
sql := `
SELECT *
FROM afs_order t1
WHERE t1.vendor_id = ?
`
sqlParams := []interface{}{
vendorID,
}
if vendorOrderID != "" {
sql += " AND t1.vendor_order_id = ?"
sqlParams = append(sqlParams, vendorOrderID)
}
if afsOrderID != "" {
sql += " AND t1.afs_order_id = ?"
sqlParams = append(sqlParams, afsOrderID)
}
sql += " ORDER BY t1.afs_order_id DESC"
err = GetRows(db, &afsOrderList, sql, sqlParams...)
return afsOrderList, err
}

View File

@@ -168,3 +168,7 @@ func value2Value(srcValue, dstValue reflect.Value, copyType int) {
// func ObjNull2Normal(src, dst interface{}) { // func ObjNull2Normal(src, dst interface{}) {
// copyBetweenNoramAndNullObj(src, dst, 2) // copyBetweenNoramAndNullObj(src, dst, 2)
// } // }
func IsVendorThingIDEmpty(vendorThingID string) bool {
return vendorThingID == "" || vendorThingID == "0"
}

View File

@@ -1,8 +1,11 @@
package dao package dao
import ( import (
"fmt"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
) )
func GetSellCities(db *DaoDB, nameID int, vendorID int) (cities []*model.Place, err error) { func GetSellCities(db *DaoDB, nameID int, vendorID int) (cities []*model.Place, err error) {
@@ -52,6 +55,47 @@ func GetSkuNameByHashCode(db *DaoDB, hashCode string) (skuName *model.SkuName, e
return nil, err return nil, err
} }
func GetSkus(db *DaoDB, skuIDs, nameIDs, statuss, catIDs []int) (skuList []*model.SkuAndName, err error) {
sql := `
SELECT t1.*, t2.name, t2.unit
FROM sku t1
JOIN sku_name t2 ON t2.id = t1.name_id AND t2.deleted_at = ?
`
sqlWhere := `
WHERE t1.deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
utils.DefaultTimeValue,
}
if len(skuIDs) > 0 {
sqlWhere += " AND t1.id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
if len(nameIDs) > 0 {
sqlWhere += " AND t1.name_id IN (" + GenQuestionMarks(len(nameIDs)) + ")"
sqlParams = append(sqlParams, nameIDs)
}
if len(statuss) > 0 {
sqlWhere += " AND t1.status IN (" + GenQuestionMarks(len(statuss)) + ") AND t2.status IN (" + GenQuestionMarks(len(statuss)) + ")"
sqlParams = append(sqlParams, statuss, statuss)
}
if len(catIDs) > 0 {
sql += `
JOIN sku_category t3 ON t3.id = t2.category_id
LEFT JOIN sku_category t3p ON t3p.id = t3.parent_id
`
sqlWhere += " AND (t3.id IN (" + GenQuestionMarks(len(catIDs)) + ")"
sqlWhere += " OR t3p.id IN (" + GenQuestionMarks(len(catIDs)) + ") )"
sqlParams = append(sqlParams, catIDs, catIDs)
}
sql += sqlWhere
if err = GetRows(db, &skuList, sql, sqlParams...); err == nil {
return skuList, nil
}
return nil, err
}
func GetSkuNames(db *DaoDB, nameIDs []int) (skuNameList []*model.SkuName, err error) { func GetSkuNames(db *DaoDB, nameIDs []int) (skuNameList []*model.SkuName, err error) {
sql := ` sql := `
SELECT * SELECT *
@@ -70,3 +114,76 @@ func GetSkuNames(db *DaoDB, nameIDs []int) (skuNameList []*model.SkuName, err er
} }
return nil, err return nil, err
} }
func GetSkuByNames(db *DaoDB, nameIDs []int) (skuList []*model.Sku, err error) {
sql := `
SELECT *
FROM sku t1
WHERE t1.deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if len(nameIDs) > 0 {
sql += " AND t1.name_id IN (" + GenQuestionMarks(len(nameIDs)) + ")"
sqlParams = append(sqlParams, nameIDs)
}
if err = GetRows(db, &skuList, sql, sqlParams...); err == nil {
return skuList, nil
}
return nil, err
}
func GetSkuIDByNames(db *DaoDB, nameIDs []int) (skuIDs []int, err error) {
skuList, err := GetSkuByNames(db, nameIDs)
if err == nil {
for _, sku := range skuList {
skuIDs = append(skuIDs, sku.ID)
}
}
return skuIDs, err
}
func GetSkuByCats(db *DaoDB, catIDs []int) (skuList []*model.Sku, err error) {
sql := `
SELECT t1.*
FROM sku t1
JOIN sku_name t2 ON t2.id = t1.name_id
WHERE t1.deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if len(catIDs) > 0 {
sql += " AND t2.category_id IN (" + GenQuestionMarks(len(catIDs)) + ")"
sqlParams = append(sqlParams, catIDs)
}
err = GetRows(db, &skuList, sql, sqlParams...)
globals.SugarLogger.Debugf("GetSkuByCats err:%v", err)
return skuList, err
}
func SetSkuSyncStatus(db *DaoDB, vendorID int, skuIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("SetSkuSyncStatus, vendorID:%d", vendorID)
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql := fmt.Sprintf(`
UPDATE sku t1
SET t1.%s_sync_status = IF(t1.deleted_at = ?, t1.%s_sync_status | ?, 0)
`, fieldPrefix, fieldPrefix)
sqlParams := []interface{}{
utils.DefaultTimeValue,
syncStatus,
}
if (syncStatus & model.SyncFlagNewMask) != 0 {
sql += fmt.Sprintf(`,
t1.%s_id = 0
`, fieldPrefix)
}
sql += " WHERE 1 = 1"
if len(skuIDs) > 0 {
sql += " AND t1.id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
return ExecuteSQL(db, sql, sqlParams...)
}

View File

@@ -24,8 +24,8 @@ type StoreDetail struct {
type StoreDetail2 struct { type StoreDetail2 struct {
model.Store model.Store
VendorStoreID string `orm:"column(vendor_store_id)` // 这个在GetMissingDadaStores返回中指的是到家的vendorStoreID VendorStoreID string `orm:"column(vendor_store_id)"` // 这个在GetMissingDadaStores返回中指的是到家的vendorStoreID
DadaStoreID string `orm:"column(dada_store_id)` DadaStoreID string `orm:"column(dada_store_id)"`
DistrictName string DistrictName string
CityName string CityName string
} }
@@ -136,8 +136,8 @@ func GetMissingDadaStores(db *DaoDB, storeID int, isMustHaveJdStore bool) (store
t3.vendor_store_id dada_store_id t3.vendor_store_id dada_store_id
FROM store t1 FROM store t1
LEFT JOIN store_map t2 ON t1.id = t2.store_id AND t2.vendor_id = ? AND t2.deleted_at = ? LEFT JOIN store_map t2 ON t1.id = t2.store_id AND t2.vendor_id = ? AND t2.deleted_at = ?
JOIN place city ON city.code = t1.city_code LEFT JOIN place city ON city.code = t1.city_code
JOIN place district ON district.code = t1.district_code LEFT JOIN place district ON district.code = t1.district_code
LEFT JOIN store_courier_map t3 ON t3.store_id = t1.id AND t3.vendor_id = ? AND t3.deleted_at = ? LEFT JOIN store_courier_map t3 ON t3.store_id = t1.id AND t3.vendor_id = ? AND t3.deleted_at = ?
WHERE t1.deleted_at = ? WHERE t1.deleted_at = ?
` `
@@ -181,6 +181,33 @@ func GetStoreCourierList(db *DaoDB, storeID, status int) (courierStoreList []*mo
return nil, err return nil, err
} }
func GetStoresMapList(db *DaoDB, vendorIDs, storeIDs []int, status int) (storeMapList []*model.StoreMap, err error) {
sql := `
SELECT t1.*
FROM store_map t1
WHERE t1.deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if len(vendorIDs) > 0 {
sql += " AND t1.vendor_id IN (" + GenQuestionMarks(len(vendorIDs)) + ")"
sqlParams = append(sqlParams, vendorIDs)
}
if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
if status != model.StoreStatusAll {
sql += " AND t1.status = ?"
sqlParams = append(sqlParams, status)
}
if err = GetRows(db, &storeMapList, sql, sqlParams...); err == nil {
return storeMapList, nil
}
return nil, err
}
// 此函数在检测到一个门店的所有平台状态一样且不为StoreStatusOpened时 // 此函数在检测到一个门店的所有平台状态一样且不为StoreStatusOpened时
// 将平台门店状态全部改为StoreStatusOpened则把京西门店状态改为之前那个统一的平台门店状态 // 将平台门店状态全部改为StoreStatusOpened则把京西门店状态改为之前那个统一的平台门店状态
func FormalizeStoreStatus(db *DaoDB, storeID, storeStatus int) (err error) { func FormalizeStoreStatus(db *DaoDB, storeID, storeStatus int) (err error) {

View File

@@ -2,6 +2,7 @@ package dao
import ( import (
"fmt" "fmt"
"time"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
@@ -13,13 +14,16 @@ var (
model.VendorIDWSC: "img_weimob", model.VendorIDWSC: "img_weimob",
model.VendorIDEBAI: "img_ebai", model.VendorIDEBAI: "img_ebai",
} }
descImgFieldMap = map[int]string{
model.VendorIDEBAI: "desc_img_ebai",
}
) )
type SkuStoreCatInfo struct { type SkuStoreCatInfo struct {
model.SkuCategory model.SkuCategory
MapID int `orm:"column(map_id)"` // 这个主要用于判断是否有store_sku_category_map MapID int `orm:"column(map_id)"` // 这个主要用于判断是否有store_sku_category_map
VendorCatID string `orm:"column(vendor_cat_id)"` VendorCatID string `orm:"column(vendor_cat_id)"`
CatSyncStatus int8 StoreCatSyncStatus int8
ParentCatName string ParentCatName string
ParentMapID int `orm:"column(parent_map_id)"` // 这个主要用于判断是否有父store_sku_category_map ParentMapID int `orm:"column(parent_map_id)"` // 这个主要用于判断是否有父store_sku_category_map
@@ -27,47 +31,63 @@ type SkuStoreCatInfo struct {
ParentCatSyncStatus int8 ParentCatSyncStatus int8
} }
type StoreCatSyncInfo struct {
CatName string
Seq int `json:"seq"`
model.StoreSkuCategoryMap
ParentCatName string
ParentCatID int `orm:"column(parent_cat_id)"` // 这个主要用于判断是否有父store_sku_category_map
ParentVendorCatID string `orm:"column(parent_vendor_cat_id)"`
ParentCatSyncStatus int8
}
type StoreSkuSyncInfo struct { type StoreSkuSyncInfo struct {
BindID int `orm:"column(bind_id)"` // 平台无关的store sku信息
BindID int `orm:"column(bind_id)"` // 换名的原因是与Sku.ID同名区别
StoreID int `orm:"column(store_id)"`
SkuID int `orm:"column(sku_id)"` // 这个与Sku.ID的区别是SkuID是必然存在的
Price int64 Price int64
UnitPrice int64 UnitPrice int64
// 平台相关的store sku信息
StoreSkuStatus int StoreSkuStatus int
SkuSyncStatus int8 StoreSkuSyncStatus int8
model.Sku
VendorSkuID string `orm:"column(vendor_sku_id)"` VendorSkuID string `orm:"column(vendor_sku_id)"`
model.Sku
// sku_name
Prefix string Prefix string
NameID int `orm:"column(name_id)"` NameID int `orm:"column(name_id)"`
VendorNameID string `orm:"column(vendor_name_id)"` VendorNameID string `orm:"column(vendor_name_id)"`
Name string Name string
Unit string Unit string
Img string
Upc string Upc string
Seq int
VendorVendorCatID int64 `orm:"column(vendor_vendor_cat_id)"` // 平台相关的图片信息
Img string
DescImg string
CatSyncStatus int8 VendorVendorCatID int64 `orm:"column(vendor_vendor_cat_id)"` // 平台商品分类(叶子结点)
VendorVendorCatID2 int64 `orm:"column(vendor_vendor_cat_id2)"` // 平台商品分类上一级
VendorVendorCatID3 int64 `orm:"column(vendor_vendor_cat_id3)"` // 平台商品分类再上一级
// sku的商家分类信息
SkuStoreCatSyncStatus int8
SkuVendorCatID string `orm:"column(sku_vendor_cat_id)"`
// sku_name的商家分类信息
StoreCatSyncStatus int8
VendorCatID string `orm:"column(vendor_cat_id)"` VendorCatID string `orm:"column(vendor_cat_id)"`
SkuCatSyncStatus int8 CatPricePercentage int
SkuVendorCatID string `orm:"column(sku_vendor_cat_id)"` }
type MissingStoreSkuInfo struct {
StoreID int `orm:"column(store_id)"`
NameID int `orm:"column(name_id)"`
SkuID int `orm:"column(sku_id)"`
SpecQuality float32
SpecUnit string
Unit string
RefPrice int
} }
// 单门店模式厂商适用 // 单门店模式厂商适用
// 从store_sku_bind中得到所有依赖的商家分类信息
func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int) (cats []*SkuStoreCatInfo, err error) { func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int) (cats []*SkuStoreCatInfo, err error) {
sql := ` sql := `
SELECT DISTINCT t4.*, t5.id map_id, t5.%s_id vendor_cat_id, t5.%s_sync_status cat_sync_status, t4p.name parent_cat_name, t5p.id parent_map_id, t5p.%s_id parent_vendor_cat_id, t5p.%s_sync_status parent_cat_sync_status SELECT DISTINCT t4.*, t5.id map_id, t5.%s_id vendor_cat_id, t5.%s_sync_status store_cat_sync_status, t4p.name parent_cat_name, t5p.id parent_map_id, t5p.%s_id parent_vendor_cat_id, t5p.%s_sync_status parent_cat_sync_status
FROM store_sku_bind t1 FROM store_sku_bind t1
JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? AND t2.status = ? JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? AND t2.status = ?
JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ? JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ?
@@ -86,7 +106,7 @@ func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int
LEFT JOIN store_sku_category_map t5 ON t4.id = t5.category_id AND t5.store_id = t1.store_id AND t5.deleted_at = ? LEFT JOIN store_sku_category_map t5 ON t4.id = t5.category_id AND t5.store_id = t1.store_id AND t5.deleted_at = ?
LEFT JOIN sku_category t4p ON t4.parent_id = t4p.id LEFT JOIN sku_category t4p ON t4.parent_id = t4p.id
LEFT JOIN store_sku_category_map t5p ON t4p.id = t5p.category_id AND t5p.store_id = t1.store_id AND t5p.deleted_at = ? LEFT JOIN store_sku_category_map t5p ON t4p.id = t5p.category_id AND t5p.store_id = t1.store_id AND t5p.deleted_at = ?
WHERE t1.deleted_at = ? AND t1.store_id = ? WHERE t1.deleted_at = ? AND t1.store_id = ? AND t1.status = ?
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
@@ -98,6 +118,7 @@ func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int
utils.DefaultTimeValue, utils.DefaultTimeValue,
utils.DefaultTimeValue, utils.DefaultTimeValue,
storeID, storeID,
model.SkuStatusNormal,
} }
if len(skuIDs) > 0 { if len(skuIDs) > 0 {
sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")" sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
@@ -112,93 +133,132 @@ func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int
} }
// 单门店模式厂商适用 // 单门店模式厂商适用
func GetStoreCategories(db *DaoDB, vendorID, storeID int, level int) (cats []*StoreCatSyncInfo, err error) { // 单纯的从已经创建的store_sku_category_map中得到相关的同步信息
func GetStoreCategories(db *DaoDB, vendorID, storeID int, level int) (cats []*SkuStoreCatInfo, err error) {
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID]) fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql := fmt.Sprintf(` sql := fmt.Sprintf(`
SELECT t5.*, t4.name cat_name, t4.seq, t4p.name parent_cat_name, t5p.category_id parent_cat_id, t5p.%s_id parent_vendor_cat_id, t5p.%s_sync_status parent_cat_sync_status SELECT t4.*,
t5.id map_id, t5.%s_id vendor_cat_id, t5.%s_sync_status store_cat_sync_status,
t4p.name parent_cat_name,
t5p.id parent_map_id, t5p.%s_id parent_vendor_cat_id, t5p.%s_sync_status parent_cat_sync_status
FROM store_sku_category_map t5 FROM store_sku_category_map t5
JOIN sku_category t4 ON t5.category_id = t4.id AND t4.deleted_at = ? JOIN sku_category t4 ON t5.category_id = t4.id AND t4.deleted_at = ?
LEFT JOIN sku_category t4p ON t4.parent_id = t4p.id LEFT JOIN sku_category t4p ON t4.parent_id = t4p.id
LEFT JOIN store_sku_category_map t5p ON t4p.id = t5p.category_id AND t5.store_id = t5p.store_id AND t5p.deleted_at = ? LEFT JOIN store_sku_category_map t5p ON t4p.id = t5p.category_id AND t5.store_id = t5p.store_id AND t5p.deleted_at = ?
WHERE t5.store_id = ? AND t4.level = ? AND t5.%s_sync_status <> 0 AND t5.deleted_at = ? WHERE t5.store_id = ? AND t4.level = ? AND t5.%s_sync_status <> 0 AND t5.deleted_at = ?
`, fieldPrefix, fieldPrefix, fieldPrefix) `, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix)
if err = GetRows(db, &cats, sql, utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, level, utils.DefaultTimeValue); err != nil { if err = GetRows(db, &cats, sql, utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, level, utils.DefaultTimeValue); err != nil {
return nil, err return nil, err
} }
return cats, err return cats, err
} }
// 单门店模式厂商适 // 以store_sku_bind为基础来做同步正常情况下使
func GetStoreSkus(db *DaoDB, vendorID, storeID int, skuIDs []int) (skus []*StoreSkuSyncInfo, err error) { // 单多门店模式厂商通用
func GetStoreSkus2(db *DaoDB, vendorID, storeID int, skuIDs []int, isDirty bool) (skus []*StoreSkuSyncInfo, err error) {
isSingleStorePF := model.MultiStoresVendorMap[vendorID] != 1
tableName := "t1" tableName := "t1"
if model.MultiStoresVendorMap[vendorID] == 1 { // 多店模式平台 if !isSingleStorePF {
tableName = "t2" tableName = "t2"
} }
vendorSkuNameField := "0" vendorSkuNameField := "0"
if vendorID == model.VendorIDWSC { if vendorID == model.VendorIDWSC {
vendorSkuNameField = "t1.wsc_id2" vendorSkuNameField = "t1.wsc_id2"
} }
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql := ` sql := `
SELECT t1.id bind_id, t1.price, t1.unit_price, t1.status store_sku_status, %s.%s_id vendor_sku_id, t1.%s_sync_status sku_sync_status, %s vendor_name_id, SELECT t1.id bind_id, t1.sku_id, t1.price, t1.unit_price, t1.status store_sku_status, %s.%s_id vendor_sku_id, t1.%s_sync_status store_sku_sync_status, %s vendor_name_id, t1.store_id,
t2.*, t2.*,
t3.id name_id, t3.prefix, t3.name, t3.unit, t3.%s img, t3.upc, t3.id name_id, t3.prefix, t3.name, t3.unit, IF(t3.%s <> '', t3.%s, t3.img) img, t3.upc, t3.%s desc_img,
t4.%s_category_id vendor_vendor_cat_id, t4.%s_category_id vendor_vendor_cat_id, t4.%s_price_percentage cat_price_percentage
t5.%s_sync_status cat_sync_status, t5.%s_id vendor_cat_id, `
t5sku.%s_sync_status sku_cat_sync_status, t5sku.%s_id sku_vendor_cat_id fmtParams := []interface{}{
tableName, fieldPrefix, fieldPrefix, vendorSkuNameField, GetImgFieldName(vendorID), GetImgFieldName(vendorID), GetDescImgFieldName(vendorID),
fieldPrefix, fieldPrefix,
}
if vendorID == model.VendorIDEBAI {
sql += `,
t4vp.vendor_category_id vendor_vendor_cat_id2, t4vp.parent_id vendor_vendor_cat_id3`
}
if isSingleStorePF {
sql += `,
t5.%s_sync_status store_cat_sync_status, t5.%s_id vendor_cat_id,
t5sku.%s_sync_status sku_store_cat_sync_status, t5sku.%s_id sku_vendor_cat_id`
fmtParams = append(fmtParams, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix)
}
sql += `
FROM store_sku_bind t1 FROM store_sku_bind t1
LEFT JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? AND t2.status = ? LEFT JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? AND t2.status = ?
LEFT JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ? LEFT JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ?
JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ? LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ?`
JOIN store_sku_category_map t5 ON t4.id = t5.category_id AND t5.store_id = t1.store_id AND t5.deleted_at = ?
LEFT JOIN store_sku_category_map t5sku ON t2.category_id = t5sku.category_id AND t5sku.store_id = t1.store_id AND t5sku.deleted_at = ?
WHERE t1.store_id = ? AND t1.%s_sync_status <> 0
`
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
model.SkuStatusNormal, model.SkuStatusNormal,
utils.DefaultTimeValue, utils.DefaultTimeValue,
model.SkuStatusNormal, model.SkuStatusNormal,
utils.DefaultTimeValue, utils.DefaultTimeValue,
utils.DefaultTimeValue, }
utils.DefaultTimeValue, if vendorID == model.VendorIDEBAI {
storeID, sql += `
LEFT JOIN sku_vendor_category t4v ON t4v.vendor_category_id = t4.%s_category_id AND t4v.vendor_id = ?
LEFT JOIN sku_vendor_category t4vp ON t4vp.vendor_category_id = t4v.parent_id AND t4v.vendor_id = ?`
fmtParams = append(fmtParams, fieldPrefix)
sqlParams = append(sqlParams, vendorID, vendorID)
}
if isSingleStorePF {
sql += `
LEFT JOIN store_sku_category_map t5 ON t4.id = t5.category_id AND t5.store_id = t1.store_id AND t5.deleted_at = ?
LEFT JOIN store_sku_category_map t5sku ON t2.category_id = t5sku.category_id AND t5sku.store_id = t1.store_id AND t5sku.deleted_at = ?`
sqlParams = append(sqlParams, utils.DefaultTimeValue, utils.DefaultTimeValue)
}
sql += " WHERE t1.store_id = ?"
sqlParams = append(sqlParams, storeID)
if isDirty {
sql += " AND (t1.%s_sync_status <> 0 OR (%s.%s_id <> 0 AND t3.id IS NULL))"
fmtParams = append(fmtParams, fieldPrefix, tableName, fieldPrefix)
} else {
sql += " AND t1.deleted_at = ?"
sqlParams = append(sqlParams, utils.DefaultTimeValue)
} }
if len(skuIDs) > 0 { if len(skuIDs) > 0 {
sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")" sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs) sqlParams = append(sqlParams, skuIDs)
} }
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID]) if !isSingleStorePF {
sql = fmt.Sprintf(sql, tableName, fieldPrefix, fieldPrefix, vendorSkuNameField, GetImgFieldName(vendorID), fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix) sql += " AND t2.%s_id <> 0"
fmtParams = append(fmtParams, fieldPrefix)
}
sql = fmt.Sprintf(sql, fmtParams...)
sql += " ORDER BY t1.price" sql += " ORDER BY t1.price"
// globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(sql)
if err = GetRows(db, &skus, sql, sqlParams...); err != nil { if err = GetRows(db, &skus, sql, sqlParams...); err != nil {
return nil, err return nil, err
} }
index := 1
for _, sku := range skus {
sku.Seq = index
index++
}
return skus, err return skus, err
} }
func GetStoreSkus(db *DaoDB, vendorID, storeID int, skuIDs []int) (skus []*StoreSkuSyncInfo, err error) {
return GetStoreSkus2(db, vendorID, storeID, skuIDs, true)
}
// 以sku为基础来做全同步
// 多门店模式厂商适用 // 多门店模式厂商适用
func GetFullStoreSkus(db *DaoDB, vendorID, storeID int) (skus []*StoreSkuSyncInfo, err error) { func GetFullStoreSkus(db *DaoDB, vendorID, storeID int) (skus []*StoreSkuSyncInfo, err error) {
globals.SugarLogger.Debugf("GetFullStoreSkus, storeID:%d, vendorID:%d", storeID, vendorID) globals.SugarLogger.Debugf("GetFullStoreSkus, storeID:%d, vendorID:%d", storeID, vendorID)
sql := ` sql := `
SELECT t1.id bind_id, t1.price, t1.unit_price, t1.status store_sku_status, t2.%s_id vendor_sku_id, t1.%s_sync_status sku_sync_status, SELECT t1.id bind_id, t1.price, t1.unit_price, t1.status store_sku_status, t2.%s_id vendor_sku_id, t1.%s_sync_status store_sku_sync_status, t1.store_id,
t2.*, t2.*, t2.id sku_id,
t3.id name_id, t3.prefix, t3.name, t3.unit, t3.%s img, t3.id name_id, t3.prefix, t3.name, t3.unit, IF(t3.%s <> '', t3.%s, t3.img) img,
t4.%s_category_id vendor_vendor_cat_id, t4.%s_category_id vendor_vendor_cat_id,
t4.%s_sync_status cat_sync_status, t4.%s_id vendor_cat_id, t4.%s_sync_status store_cat_sync_status, t4.%s_id vendor_cat_id,
t5sku.%s_sync_status sku_cat_sync_status, t5sku.%s_id sku_vendor_cat_id t5sku.%s_sync_status sku_store_cat_sync_status, t5sku.%s_id sku_vendor_cat_id
FROM sku t2 FROM sku t2
LEFT JOIN store_sku_bind t1 ON t1.sku_id = t2.id AND t1.store_id = ? AND t1.deleted_at = ? LEFT JOIN store_sku_bind t1 ON t1.sku_id = t2.id AND t1.store_id = ? AND t1.deleted_at = ?
JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ? JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ?
JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ? JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ?
LEFT JOIN sku_category t5sku ON t2.category_id = t5sku.id LEFT JOIN sku_category t5sku ON t2.category_id = t5sku.id
WHERE t2.deleted_at = ? AND t2.status = ? WHERE t2.deleted_at = ? AND t2.status = ? AND t2.%s_id <> 0
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
storeID, storeID,
@@ -210,7 +270,8 @@ func GetFullStoreSkus(db *DaoDB, vendorID, storeID int) (skus []*StoreSkuSyncInf
model.SkuStatusNormal, model.SkuStatusNormal,
} }
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID]) fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql = fmt.Sprintf(sql, fieldPrefix, fieldPrefix, GetImgFieldName(vendorID), fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix) sql = fmt.Sprintf(sql, fieldPrefix, fieldPrefix, GetImgFieldName(vendorID), GetImgFieldName(vendorID),
fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix)
// globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(sql)
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
if err = GetRows(db, &skus, sql, sqlParams...); err != nil { if err = GetRows(db, &skus, sql, sqlParams...); err != nil {
@@ -219,53 +280,79 @@ func GetFullStoreSkus(db *DaoDB, vendorID, storeID int) (skus []*StoreSkuSyncInf
return skus, err return skus, err
} }
func SetStoreSkuSyncStatus(db *DaoDB, vendorID, storeID int, skuIDs []int, syncStatus int) (num int64, err error) { // 这个函数之前是要设置没有删除或同步标志不为0的会导致将同步标志不为0且删除了的把标志去掉现在改为只设置没有删除的
globals.SugarLogger.Debugf("SetStoreSkuSyncStatus, storeID:%d, vendorID:%d", storeID, vendorID) func SetStoreSkuSyncStatus(db *DaoDB, vendorID int, storeIDs []int, skuIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("SetStoreSkuSyncStatus, storeIDs:%v, vendorID:%d", storeIDs, vendorID)
isSingleStorePF := model.MultiStoresVendorMap[vendorID] != 1
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID]) fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql := fmt.Sprintf(` sql := `
UPDATE store_sku_bind UPDATE store_sku_bind t1
SET %s_sync_status = IF(deleted_at = ?, %s_sync_status | ?, 0) SET t1.%s_sync_status = t1.%s_sync_status | ?
`, fieldPrefix, fieldPrefix) `
fmtParams := []interface{}{
fieldPrefix,
fieldPrefix,
}
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue,
syncStatus, syncStatus,
} }
if (syncStatus & model.SyncFlagNewMask) != 0 { if isSingleStorePF && (syncStatus&model.SyncFlagNewMask) != 0 {
sql += fmt.Sprintf(`, sql += `,
%s_id = 0 t1.%s_id = 0
`, fieldPrefix) `
fmtParams = append(fmtParams, fieldPrefix)
} }
sql += " WHERE 1 = 1" sql += " WHERE (t1.deleted_at = ?)"
if storeID > 0 { // fmtParams = append(fmtParams, fieldPrefix)
sql += " AND store_id = ?" sqlParams = append(sqlParams, utils.DefaultTimeValue)
sqlParams = append(sqlParams, storeID) if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
} }
if len(skuIDs) > 0 { if len(skuIDs) > 0 {
sql += " AND sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")" sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs) sqlParams = append(sqlParams, skuIDs)
} }
sql = fmt.Sprintf(sql, fmtParams...)
return ExecuteSQL(db, sql, sqlParams...) return ExecuteSQL(db, sql, sqlParams...)
} }
func SetStoreCategorySyncStatus(db *DaoDB, vendorID, storeID int, catIDs []int, syncStatus int) (num int64, err error) { func SetStoreCategorySyncStatus(db *DaoDB, vendorID int, storeIDs []int, catIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("SetStoreCategorySyncStatus, storeID:%d, vendorID:%d", storeID, vendorID) globals.SugarLogger.Debugf("SetStoreCategorySyncStatus, storeIDs:%v, vendorID:%d", storeIDs, vendorID)
isSingleStorePF := model.MultiStoresVendorMap[vendorID] != 1
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID]) fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql := fmt.Sprintf(` sql := `
UPDATE store_sku_category_map UPDATE store_sku_category_map t1
SET %s_sync_status = %s_sync_status | ? SET t1.%s_sync_status = IF(t1.deleted_at = ?, t1.%s_sync_status | ?, 0)
WHERE deleted_at = ? AND store_id = ? `
`, fieldPrefix, fieldPrefix) fmtParams := []interface{}{
fieldPrefix,
fieldPrefix,
}
sqlParams := []interface{}{ sqlParams := []interface{}{
syncStatus,
utils.DefaultTimeValue, utils.DefaultTimeValue,
storeID, syncStatus,
}
if isSingleStorePF && (syncStatus&model.SyncFlagNewMask) != 0 {
sql += `,
t1.%s_id = 0
`
fmtParams = append(fmtParams, fieldPrefix)
}
sql += " WHERE (t1.deleted_at = ? OR t1.%s_sync_status <> 0)"
fmtParams = append(fmtParams, fieldPrefix)
sqlParams = append(sqlParams, utils.DefaultTimeValue)
if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
} }
if len(catIDs) > 0 { if len(catIDs) > 0 {
sql += " AND category_id IN (" + GenQuestionMarks(len(catIDs)) + ")" sql += " AND t1.category_id IN (" + GenQuestionMarks(len(catIDs)) + ")"
sqlParams = append(sqlParams, catIDs) sqlParams = append(sqlParams, catIDs)
} }
sql = fmt.Sprintf(sql, fmtParams...)
return ExecuteSQL(db, sql, sqlParams...) return ExecuteSQL(db, sql, sqlParams...)
} }
@@ -276,3 +363,66 @@ func GetImgFieldName(vendorID int) (fieldName string) {
} }
return fieldName return fieldName
} }
func GetDescImgFieldName(vendorID int) (fieldName string) {
fieldName = descImgFieldMap[vendorID]
if fieldName == "" {
fieldName = "desc_img"
}
return fieldName
}
func GetStoresSkusInfo(db *DaoDB, storeIDs, skuIDs []int) (storeSkuList []*model.StoreSkuBind, err error) {
sql := `
SELECT *
FROM store_sku_bind t1
WHERE t1.deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if len(storeIDs) > 0 {
sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
if len(skuIDs) > 0 {
sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
err = GetRows(db, &storeSkuList, sql, sqlParams...)
return storeSkuList, err
}
func GetMissingStoreSkuFromOrder(db *DaoDB, storeIDs []int, fromTime time.Time) (storeSkuList []*MissingStoreSkuInfo, err error) {
if time.Now().Sub(fromTime) > 24*time.Hour*60 {
return nil, fmt.Errorf("GetMissingStoreSkuFromOrder时间超过60天")
}
sql := `
SELECT IF(t2.jx_store_id <> 0, t2.jx_store_id, t2.store_id) store_id, t4.name_id, t1.sku_id,
t4.spec_quality, t4.spec_unit, t5.unit,
COUNT(*) ct, CAST(AVG(IF(t1.vendor_price <> 0, t1.vendor_price, t1.sale_price)) AS SIGNED) ref_price
FROM order_sku t1
JOIN goods_order t2 ON t2.vendor_order_id = t1.vendor_order_id
LEFT JOIN store_sku_bind t3 ON t3.store_id = IF(t2.jx_store_id <> 0, t2.jx_store_id, t2.store_id) AND
t3.sku_id = t1.sku_id AND t3.deleted_at = ?
JOIN sku t4 ON t4.id = t1.sku_id
JOIN sku_name t5 ON t5.id = t4.name_id
WHERE t2.status = ? AND IF(t2.jx_store_id <> 0, t2.jx_store_id, t2.store_id) > 0 AND t1.sku_id > 0 AND t1.shop_price = 0 AND
t1.order_created_at > ? AND t3.id IS NULL
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
model.OrderStatusFinished,
fromTime,
}
if len(storeIDs) > 0 {
sql += " AND IF(t2.jx_store_id <> 0, t2.jx_store_id, t2.store_id) IN (" + GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
sql += `
GROUP BY 1,2,3,4,5,6
ORDER BY 1,2,3,4,5,6
`
err = GetRows(db, &storeSkuList, sql, sqlParams...)
return storeSkuList, err
}

View File

@@ -45,6 +45,9 @@ const (
FieldVendorOrderID = "VendorOrderID" FieldVendorOrderID = "VendorOrderID"
FieldVendorOrderID2 = "VendorOrderID2" FieldVendorOrderID2 = "VendorOrderID2"
FieldActID = "ActID"
FieldVendorActID = "VendorActID"
) )
type ModelIDCUL struct { type ModelIDCUL struct {
@@ -67,16 +70,43 @@ const (
SyncFlagModifiedMask = 1 SyncFlagModifiedMask = 1
SyncFlagNewMask = 2 SyncFlagNewMask = 2
SyncFlagDeletedMask = 4 SyncFlagDeletedMask = 4
SyncFlagChangedMask = SyncFlagModifiedMask | SyncFlagNewMask | SyncFlagDeletedMask
SyncFlagSaleMask = 8 SyncFlagSaleMask = 8 // 改了门店商品可售状态必须设置此标志
SyncFlagPriceMask = 16 SyncFlagPriceMask = 16 // 改了门店商品价格必须设置此标志
SyncFlagSpecMask = 32 SyncFlagSpecMask = 32
SyncFlagStoreSkuOnlyMask = SyncFlagSaleMask | SyncFlagPriceMask
SyncFlagStoreSkuModifiedMask = SyncFlagStoreSkuOnlyMask | SyncFlagModifiedMask
SyncFlagChangedMask = SyncFlagSpecMask | SyncFlagNewMask | SyncFlagDeletedMask | SyncFlagStoreSkuModifiedMask
SyncFlagStoreName = 8 SyncFlagStoreName = 8
SyncFlagStoreAddress = 16 SyncFlagStoreAddress = 16
) )
func IsSyncStatusNew(syncStatus int) bool {
return (syncStatus & SyncFlagNewMask) != 0
}
func IsSyncStatusDelete(syncStatus int) bool {
return (syncStatus & SyncFlagDeletedMask) != 0
}
func IsSyncStatusUpdate(syncStatus int) bool {
return (syncStatus & SyncFlagModifiedMask) != 0
}
func IsSyncStatusNeedCreate(syncStatus int) bool {
return IsSyncStatusNew(syncStatus) && !IsSyncStatusDelete(syncStatus)
}
func IsSyncStatusNeedDelete(syncStatus int) bool {
return !IsSyncStatusNew(syncStatus) && IsSyncStatusDelete(syncStatus)
}
func IsSyncStatusNeedUpdate(syncStatus int) bool {
return !IsSyncStatusNew(syncStatus) && !IsSyncStatusDelete(syncStatus) && IsSyncStatusUpdate(syncStatus)
}
// const ( // const (
// KeyJdFlag = "jdFlag" // KeyJdFlag = "jdFlag"
// KeyJdSyncedAt = "jdSyncedAt" // KeyJdSyncedAt = "jdSyncedAt"

View File

@@ -16,9 +16,12 @@ type GoodsOrder struct {
StoreID int `orm:"column(store_id)" json:"storeID"` // 外部系统里记录的 jxstoreid StoreID int `orm:"column(store_id)" json:"storeID"` // 外部系统里记录的 jxstoreid
JxStoreID int `orm:"column(jx_store_id)" json:"jxStoreID"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid JxStoreID int `orm:"column(jx_store_id)" json:"jxStoreID"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid
StoreName string `orm:"size(64)" json:"storeName"` StoreName string `orm:"size(64)" json:"storeName"`
ShopPrice int64 `json:"shopPrice"` // 单位为分 门店标 ShopPrice int64 `json:"shopPrice"` // 京西
SalePrice int64 `json:"salePrice"` // 单位为分 售卖 VendorPrice int64 `json:"vendorPrice"` // 平台
SalePrice int64 `json:"salePrice"` // 售卖价
ActualPayPrice int64 `json:"actualPayPrice"` // 单位为分 顾客实际支付 ActualPayPrice int64 `json:"actualPayPrice"` // 单位为分 顾客实际支付
TotalShopMoney int64 `json:"shopMoney"` // 应结金额-第三方平台结算给京西的金额(包括了所有的补贴,扣除)
PmSubsidyMoney int64 `json:"pmSubsidyMoney"` // 平台活动补贴(订单主体活动补贴+订单单条sku补贴1+
Weight int `json:"weight"` // 单位为克 Weight int `json:"weight"` // 单位为克
ConsigneeName string `orm:"size(32)" json:"consigneeName"` ConsigneeName string `orm:"size(32)" json:"consigneeName"`
ConsigneeMobile string `orm:"size(32)" json:"consigneeMobile"` ConsigneeMobile string `orm:"size(32)" json:"consigneeMobile"`
@@ -30,9 +33,9 @@ type GoodsOrder struct {
SkuCount int `json:"skuCount"` // 商品类别数量即有多少种商品注意在某些情况下相同SKU的商品由于售价不同也会当成不同商品在这个值里 SkuCount int `json:"skuCount"` // 商品类别数量即有多少种商品注意在某些情况下相同SKU的商品由于售价不同也会当成不同商品在这个值里
GoodsCount int `json:"goodsCount"` // 商品个数 GoodsCount int `json:"goodsCount"` // 商品个数
Status int `json:"status"` // 参见OrderStatus*相关的常量定义 Status int `json:"status"` // 参见OrderStatus*相关的常量定义
VendorStatus string `orm:"size(255)" json:"-"` VendorStatus string `orm:"size(255)" json:"vendorStatus"`
LockStatus int `json:"lockStatus"` LockStatus int `json:"lockStatus"`
LockStatusTime time.Time `orm:"type(datetime);null" json:"-"` // last lock status time LockStatusTime time.Time `orm:"type(datetime);null" json:"lockStatusTime"` // last lock status time
OrderSeq int `json:"orderSeq"` // 门店订单序号 OrderSeq int `json:"orderSeq"` // 门店订单序号
BuyerComment string `orm:"size(255)" json:"buyerComment"` BuyerComment string `orm:"size(255)" json:"buyerComment"`
BusinessType int `json:"businessType"` BusinessType int `json:"businessType"`
@@ -44,19 +47,12 @@ type GoodsOrder struct {
DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的 DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的
OrderCreatedAt time.Time `orm:"type(datetime);index" json:"orderCreatedAt"` // 这里记录的是订单生效时间,即用户支付完成(货到付款即为下单时间) OrderCreatedAt time.Time `orm:"type(datetime);index" json:"orderCreatedAt"` // 这里记录的是订单生效时间,即用户支付完成(货到付款即为下单时间)
OrderFinishedAt time.Time `orm:"type(datetime)" json:"orderFinishedAt"` OrderFinishedAt time.Time `orm:"type(datetime)" json:"orderFinishedAt"`
StatusTime time.Time `orm:"type(datetime)" json:"-"` // last status time StatusTime time.Time `orm:"type(datetime)" json:"statusTime"` // last status time
PickDeadline time.Time `orm:"type(datetime)" json:"pickDeadline"` PickDeadline time.Time `orm:"type(datetime)" json:"pickDeadline"`
ModelTimeInfo `json:"-"` ModelTimeInfo `json:"-"`
OriginalData string `orm:"-" json:"-"` // 只是用于传递数据 OriginalData string `orm:"-" json:"-"` // 只是用于传递数据
Skus []*OrderSku `orm:"-" json:"-"` Skus []*OrderSku `orm:"-" json:"-"`
Flag int8 `json:"flag"` //非运单调整相关的其它状态 Flag int `json:"flag"` //非运单调整相关的其它状态
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 { func (o *GoodsOrder) TableUnique() [][]string {
@@ -88,18 +84,18 @@ type OrderSku struct {
StoreSubID int `orm:"column(store_sub_id)" json:"storeSubID"` StoreSubID int `orm:"column(store_sub_id)" json:"storeSubID"`
StoreSubName string `orm:"size(64)" json:"storeSubName"` StoreSubName string `orm:"size(64)" json:"storeSubName"`
Count int `json:"count"` Count int `json:"count"`
VendorSkuID string `orm:"column(vendor_sku_id);size(48)" json:"-"` VendorSkuID string `orm:"column(vendor_sku_id);size(48)" json:"vendorSkuID"`
SkuID int `orm:"column(sku_id)" json:"skuID"` // 外部系统里记录的 jxskuid SkuID int `orm:"column(sku_id)" json:"skuID"` // 外部系统里记录的 jxskuid
JxSkuID int `orm:"column(jx_sku_id)" json:"jxSkuID"` // 根据VendorSkuID在本地系统里查询出来的 jxskuid JxSkuID int `orm:"column(jx_sku_id)" json:"jxSkuID"` // 根据VendorSkuID在本地系统里查询出来的 jxskuid
SkuName string `orm:"size(255)" json:"skuName"` SkuName string `orm:"size(255)" json:"skuName"`
ShopPrice int64 `json:"shopPrice"` // 门店标 ShopPrice int64 `json:"shopPrice"` // 京西
VendorPrice int64 `json:"vendorPrice"` // 平台价
SalePrice int64 `json:"salePrice"` // 售卖价 SalePrice int64 `json:"salePrice"` // 售卖价
Weight int `json:"-"` // 单位为克 EarningPrice int64 `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
SkuType int `json:"-"` // 当前如果为gift就为1否则缺省为0 Weight int `json:"weight"` // 单位为克
PromotionType int `json:"-"` // todo 当前是用于记录京东的PromotionType(生成jxorder用),没有做转换 SkuType int `json:"skuType"` // 当前如果为gift就为1否则缺省为0
PromotionType int `json:"promotionType"` // todo 当前是用于记录京东的PromotionType(生成jxorder用),没有做转换
OrderCreatedAt time.Time `orm:"type(datetime);index" json:"-"` // 分区考虑 OrderCreatedAt time.Time `orm:"type(datetime);index" json:"-"` // 分区考虑
SkuPmSubsidy int64 `json:"-"` //平台商品活动补贴
SkuPmFee int64 `json:"-"` //门店商品活动支出
} }
// 同样商品在一个订单中可能重复出现(比如搞活动时,相同商品价格不一样,第一个有优惠) // 同样商品在一个订单中可能重复出现(比如搞活动时,相同商品价格不一样,第一个有优惠)
@@ -124,6 +120,7 @@ type Waybill struct {
ActualFee int64 `json:"actualFee"` // 实际要支付给快递公司的费用 ActualFee int64 `json:"actualFee"` // 实际要支付给快递公司的费用
DesiredFee int64 `json:"desiredFee"` // 运单总费用 DesiredFee int64 `json:"desiredFee"` // 运单总费用
DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的 DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的
DeliveryFlag int8 `json:"deliveryFlag"`
WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"` WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"`
WaybillFinishedAt time.Time `orm:"type(datetime)" json:"waybillFinishedAt"` WaybillFinishedAt time.Time `orm:"type(datetime)" json:"waybillFinishedAt"`
StatusTime time.Time `orm:"type(datetime)" json:"-"` // last status time StatusTime time.Time `orm:"type(datetime)" json:"-"` // last status time
@@ -240,3 +237,24 @@ type OrderComment struct {
CommentUpdatedAt time.Time CommentUpdatedAt time.Time
UpdatedOriginalMsg string `orm:"type(text)" json:"-"` UpdatedOriginalMsg string `orm:"type(text)" json:"-"`
} }
// 判断是否是购买平台自有物流
// 对于京东,饿百来说,就是其自有的物流,对于微商城来说,是达达
func IsWaybillPlatformOwn(bill *Waybill) bool {
return bill.OrderVendorID == bill.WaybillVendorID || IsSpecialOrderPlatformWaybill(bill)
}
// 是否是特殊物流
func IsSpecialOrderPlatformWaybill(bill *Waybill) bool {
return (bill.OrderVendorID == VendorIDWSC && bill.WaybillVendorID == VendorIDDada)
}
// 订单是否已经有了有效运单
func IsOrderHaveWaybill(order *GoodsOrder) bool {
return order.WaybillVendorID != VendorIDUnknown && order.VendorWaybillID != ""
}
// 订单是否有自己平台的有效运单
func IsOrderHaveOwnWaybill(order *GoodsOrder) bool {
return order.VendorID == order.WaybillVendorID && order.VendorWaybillID != ""
}

View File

@@ -74,10 +74,25 @@ type AfsOrder struct {
VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` // 关联原始订单ID VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` // 关联原始订单ID
VendorOrderID2 string `orm:"column(vendor_order_id2);size(48);index" json:"vendorOrderID2"` // 关联原始订单ID2,饿百独有 VendorOrderID2 string `orm:"column(vendor_order_id2);size(48);index" json:"vendorOrderID2"` // 关联原始订单ID2,饿百独有
AfsOrderID string `orm:"column(afs_order_id);size(48)" json:"afsOrderID"` // 售后订单ID AfsOrderID string `orm:"column(afs_order_id);size(48)" json:"afsOrderID"` // 售后订单ID
AfsCreateAt time.Time `orm:"type(datetime);index" json:"afsCreateAt"` // 单生成时间 AfsCreatedAt time.Time `orm:"type(datetime);null;index" json:"afsCreatedAt"` // 售后单生成时间
AfsFinishedAt time.Time `orm:"type(datetime);null;index" json:"afsFinishedAt"` // 售后单结束时间
VendorStoreID string `orm:"column(vendor_store_id);size(48)" json:"vendorStoreID"` // 外部系统里记录的storeid VendorStoreID string `orm:"column(vendor_store_id);size(48)" json:"vendorStoreID"` // 外部系统里记录的storeid
StoreID int `orm:"column(store_id)" json:"storeID"` // 接口返回的京西门店ID StoreID int `orm:"column(store_id)" json:"storeID"` // 接口返回的京西门店ID
JxStoreID int `orm:"column(jx_store_id)" json:"jxStoreID"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid JxStoreID int `orm:"column(jx_store_id)" json:"jxStoreID"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid
// IsNeedApprove int8 `json:"isNeedApprove"` // 售后单是否需要商家审核
Status int `json:"status"`
VendorStatus string `orm:"size(255)" json:"vendorStatus"`
ReasonType int8 `json:"reasonType"` // 售后原因
VendorReasonType string `orm:"size(255)" json:"vendorReasonType"`
ReasonDesc string `orm:"size(1024)" json:"reasonDesc"` // 售后原因描述
ReasonImgList string `orm:"size(1024)" json:"reasonImgList"` // 售后描述图片
AppealType int8 `json:"appealType"` // 售后方式
VendorAppealType string `orm:"size(255)" json:"vendorAppealType"`
Flag int `json:"flag"`
RefundType int8 `json:"refundType"`
RefuseReason string `orm:"size(1024)" json:"refuseReason"`
SkuUserMoney int64 `json:"skuUserMoney"` // 用户支付菜品金额 SkuUserMoney int64 `json:"skuUserMoney"` // 用户支付菜品金额
FreightUserMoney int64 `json:"freightUserMoney"` // 用户支付运费金额 FreightUserMoney int64 `json:"freightUserMoney"` // 用户支付运费金额
AfsFreightMoney int64 `json:"afsFreightMoney"` // 退货取件费 AfsFreightMoney int64 `json:"afsFreightMoney"` // 退货取件费
@@ -104,8 +119,7 @@ type OrderSkuFinancial struct {
VendorID int `orm:"column(vendor_id)" json:"vendorID"` // 平台id VendorID int `orm:"column(vendor_id)" json:"vendorID"` // 平台id
VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` // 关联原始订单ID VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` // 关联原始订单ID
VendorOrderID2 string `orm:"column(vendor_order_id2);size(48);index" json:"vendorOrderID2"` // 关联原始订单ID2,饿百独有 AfsOrderID string `orm:"column(afs_order_id);size(48)" json:"afsOrderID"` // 售后单ID
AfsOrderID string `orm:"column(order_financial_id);size(48)" json:"afsOrderID"` // 订单结账ID
IsAfsOrder int8 `json:"isAfsOrder"` // 0--正向单, 1--售后单 IsAfsOrder int8 `json:"isAfsOrder"` // 0--正向单, 1--售后单
// ConfirmTime time.Time `orm:"type(datetime)" json:"confirmTime"` // 订单生成/完成时间 // ConfirmTime time.Time `orm:"type(datetime)" json:"confirmTime"` // 订单生成/完成时间
@@ -141,7 +155,8 @@ type OrderSkuFinancial struct {
func (o *OrderSkuFinancial) TableIndex() [][]string { func (o *OrderSkuFinancial) TableIndex() [][]string {
return [][]string{ return [][]string{
[]string{"VendorOrderID", "VendorSkuID", "PromotionType", "IsAfsOrder", "VendorID"}, []string{"VendorOrderID", "VendorSkuID"},
[]string{"AfsOrderID", "VendorSkuID"},
} }
} }

View File

@@ -30,7 +30,7 @@ var (
type Promotion struct { type Promotion struct {
ModelIDCULD ModelIDCULD
VendorID int `orm:"column(vendor_id)"` VendorID int `orm:"column(vendor_id)" json:"vendorID"`
Name string `orm:"size(64)" json:"name"` Name string `orm:"size(64)" json:"name"`
Advertising string `orm:"size(255)" json:"advertising"` Advertising string `orm:"size(255)" json:"advertising"`
Type int `json:"type"` Type int `json:"type"`
@@ -75,6 +75,8 @@ type PromotionSku struct {
Price int `json:"price"` // 分,活动价,这个不是单价 Price int `json:"price"` // 分,活动价,这个不是单价
LimitSkuCount int `json:"limitSkuCount"` LimitSkuCount int `json:"limitSkuCount"`
IsLock int8 `json:"isLock"` // 是否锁定门店商品信息 IsLock int8 `json:"isLock"` // 是否锁定门店商品信息
EarningPrice int `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
} }
func (*PromotionSku) TableUnique() [][]string { func (*PromotionSku) TableUnique() [][]string {

View File

@@ -89,6 +89,7 @@ var (
SpecialUnit = "份" SpecialUnit = "份"
SpecialSpecQuality = 500 SpecialSpecQuality = 500
SpecialSpecUnit = "g" SpecialSpecUnit = "g"
SpecialSpecUnit2 = "ml"
) )
var ( var (
@@ -131,7 +132,12 @@ type SkuCategory struct {
Type int8 `json:"type"` // 类别类型,即是普通类别还是特殊用于做活动的类别 Type int8 `json:"type"` // 类别类型,即是普通类别还是特殊用于做活动的类别
Seq int `json:"seq"` Seq int `json:"seq"`
JdCategoryID int `orm:"column(jd_category_id)" json:"jdCategoryID"` // 这个是指对应的京东商品类别 JdPricePercentage int16 `orm:"default(100)" json:"jdPricePercentage"`
EbaiPricePercentage int16 `orm:"default(100)" json:"ebaiPricePercentage"`
MtwmPricePercentage int16 `orm:"default(100)" json:"mtwmPricePercentage"`
WscPricePercentage int16 `orm:"default(100)" json:"wscPricePercentage"`
JdCategoryID int64 `orm:"column(jd_category_id)" json:"jdCategoryID"` // 这个是指对应的京东商品类别
ElmCategoryID int64 `orm:"column(elm_category_id)" json:"elmCategoryID"` // 这个是指对应的饿了么商品类别 ElmCategoryID int64 `orm:"column(elm_category_id)" json:"elmCategoryID"` // 这个是指对应的饿了么商品类别
EbaiCategoryID int64 `orm:"column(ebai_category_id)" json:"ebaiCategoryID"` // 这个是指对应的饿百商品类别 EbaiCategoryID int64 `orm:"column(ebai_category_id)" json:"ebaiCategoryID"` // 这个是指对应的饿百商品类别
MtwmCategoryID int64 `orm:"column(mtwm_category_id)" json:"mtwmCategoryID"` // 这个是指对应的美团外卖商品类别 MtwmCategoryID int64 `orm:"column(mtwm_category_id)" json:"mtwmCategoryID"` // 这个是指对应的美团外卖商品类别
@@ -161,6 +167,7 @@ type SkuName struct {
BrandID int `orm:"column(brand_id);default(0)" json:"brandID"` // todo此属性暂时没有使用且有问题应该是不同平台都有一个brandid BrandID int `orm:"column(brand_id);default(0)" json:"brandID"` // todo此属性暂时没有使用且有问题应该是不同平台都有一个brandid
CategoryID int `orm:"column(category_id);index" json:"categoryID"` // 标准类别 CategoryID int `orm:"column(category_id);index" json:"categoryID"` // 标准类别
JdCategoryID int64 `orm:"column(jd_category_id)" json:"jdCategoryID"` // 这个是指对应的京东商品类别
IsGlobal int8 `orm:"default(1)" json:"isGlobal"` // 是否是全部全国可见如果否的话可见性由SkuPlace决定 IsGlobal int8 `orm:"default(1)" json:"isGlobal"` // 是否是全部全国可见如果否的话可见性由SkuPlace决定
Unit string `orm:"size(8)" json:"unit"` Unit string `orm:"size(8)" json:"unit"`
@@ -176,6 +183,9 @@ type SkuName struct {
Status int `orm:"default(1)" json:"status"` // skuname状态取值同sku.Status Status int `orm:"default(1)" json:"status"` // skuname状态取值同sku.Status
IsSpu int8 `orm:"column(is_spu)" json:"isSpu"` // 用于指明是否SKUNAME当成SPU IsSpu int8 `orm:"column(is_spu)" json:"isSpu"` // 用于指明是否SKUNAME当成SPU
DescImg string `orm:"size(255)" json:"descImg"` // 商品详情图片描述
DescImgEbai string `orm:"size(255)" json:"descImgEbai"` // 饿百的商品详情图片描述RTF
JdID int64 `orm:"column(jd_id);null;index" json:"jdID"` JdID int64 `orm:"column(jd_id);null;index" json:"jdID"`
JdSyncStatus int8 `orm:"default(2)" json:"jdSyncStatus"` JdSyncStatus int8 `orm:"default(2)" json:"jdSyncStatus"`
@@ -206,6 +216,12 @@ type Sku struct {
LinkID int `orm:"column(link_id);null;index" json:"linkID"` LinkID int `orm:"column(link_id);null;index" json:"linkID"`
} }
type SkuAndName struct {
Sku
Name string
Unit string
}
// func (*Sku) TableUnique() [][]string { // func (*Sku) TableUnique() [][]string {
// return [][]string{ // return [][]string{
// []string{"JdID", "DeletedAt"}, // []string{"JdID", "DeletedAt"},

View File

@@ -2,8 +2,9 @@ package model
const ( const (
StoreStatusAll = -9 StoreStatusAll = -9
StoreStatusDisabled = -1 StoreStatusDisabled = -2
StoreStatusClosed = 0 StoreStatusClosed = -1
StoreStatusHaveRest = 0
StoreStatusOpened = 1 StoreStatusOpened = 1
) )
@@ -32,7 +33,8 @@ const (
var ( var (
StoreStatusName = map[int]string{ StoreStatusName = map[int]string{
StoreStatusDisabled: "禁用", StoreStatusDisabled: "禁用",
StoreStatusClosed: "休息", StoreStatusClosed: "长期休息",
StoreStatusHaveRest: "临时休息",
StoreStatusOpened: "营业中", StoreStatusOpened: "营业中",
} }
DeliveryRangeTypeName = map[int]string{ DeliveryRangeTypeName = map[int]string{
@@ -62,6 +64,7 @@ var (
"NBB": "宁波银行", "NBB": "宁波银行",
"SPDB": "浦发银行", "SPDB": "浦发银行",
"GDB": "广发银行", "GDB": "广发银行",
"BOCOM": "交通银行",
"SPAB": "平安银行", "SPAB": "平安银行",
"BSB": "包商银行", "BSB": "包商银行",
"CSCB": "长沙银行", "CSCB": "长沙银行",
@@ -131,6 +134,8 @@ type Store struct {
Status int `json:"status"` Status int `json:"status"`
ChangePriceType int8 `json:"changePriceType"` // 修改价格类型,即是否需要审核 ChangePriceType int8 `json:"changePriceType"` // 修改价格类型,即是否需要审核
DeliveryType int8 `orm:"-" json:"deliveryType"` // 仅用于传值
PrinterVendorID int `orm:"column(printer_vendor_id);" json:"printerVendorID"` PrinterVendorID int `orm:"column(printer_vendor_id);" json:"printerVendorID"`
PrinterSN string `orm:"size(32);column(printer_sn);index" json:"printerSN"` PrinterSN string `orm:"size(32);column(printer_sn);index" json:"printerSN"`
PrinterKey string `orm:"size(32)" json:"printerKey"` PrinterKey string `orm:"size(32)" json:"printerKey"`
@@ -138,10 +143,39 @@ type Store struct {
IDCardFront string `orm:"size(255);column(id_card_front)" json:"idCardFront"` IDCardFront string `orm:"size(255);column(id_card_front)" json:"idCardFront"`
IDCardBack string `orm:"size(255);column(id_card_back)" json:"idCardBack"` IDCardBack string `orm:"size(255);column(id_card_back)" json:"idCardBack"`
IDCardHand string `orm:"size(255);column(id_card_hand)" json:"idCardHand"` IDCardHand string `orm:"size(255);column(id_card_hand)" json:"idCardHand"`
Licence string `orm:"size(255)" json:"licence"` Licence string `orm:"size(255)" json:"licence"` // 营业执照图片
LicenceCode string `orm:"size(32)" json:"licenceCode"` LicenceCode string `orm:"size(32)" json:"licenceCode"`
DeliveryType int8 `orm:"-" json:"deliveryType"` // 仅用于传值 LicenceType int8 `json:"licenceType"` // 营业执照类型0个人1公司
LicenceCorpName string `orm:"size(64)" json:"licenceCorpName"` // 营业执照公司名称
LicenceOwnerName string `orm:"size(8)" json:"licenceOwnerName"` // 法人姓名
LicenceAddress string `orm:"size(255)" json:"licenceAddress"` // 地址
LicenceValid string `orm:"size(32)" json:"licenceValid"` // 有效期开始
LicenceExpire string `orm:"size(32)" json:"licenceExpire"` // 有效期结束
IDName string `orm:"size(8);column(id_name)" json:"idName"` // 身份证姓名
IDCode string `orm:"size(32);column(id_code)" json:"idCode"` // 身份证号
IDValid string `orm:"column(id_valid);size(32)" json:"idValid"` // 有效期开始
IDExpire string `orm:"column(id_expire);size(32)" json:"idExpire"` // 有效期结束
Licence2Image string `orm:"size(255)" json:"licence2Image"` // 食品经营许可证
Licence2Code string `orm:"size(32)" json:"licence2Code"` // 食品经营许可证编号
Licence2Valid string `orm:"size(32)" json:"licence2Valid"` // 有效期开始
Licence2Expire string `orm:"size(32)" json:"licence2Expire"` // 有效期结束
MarketManName string `orm:"size(8)" json:"marketManName"` // 市场负责人姓名
MarketManPhone string `orm:"size(16)" json:"marketManPhone"` // 市场负责人电话
JxBrandFeeFactor int `json:"jxBrandFeeFactor"` // 京西品牌费因子
MarketAddFeeFactor int `json:"marketAddFeeFactor"` // 市场附加费因子
PayeeName string `orm:"size(8)" json:"payeeName"` // 收款人姓名
PayeeAccountNo string `orm:"size(255)" json:"payeeAccountNo"` // 收款账号
PayeeBankBranchName string `orm:"size(255)" json:"payeeBankBranchName"` // 开户银行
PayeeBankName string `orm:"size(255)" json:"payeeBankName"` // 开户支行
PayPercentage int `json:"payPercentage"`
OperatorName string `orm:"size(8)" json:"operatorName"` // 运营人姓名
OperatorPhone string `orm:"size(16)" json:"operatorPhone"` // 运营人电话
} }
func (*Store) TableUnique() [][]string { func (*Store) TableUnique() [][]string {

View File

@@ -58,6 +58,29 @@ func (*StoreSkuCategoryMap) TableUnique() [][]string {
} }
} }
func (*StoreSkuCategoryMap) TableIndex() [][]string {
return [][]string{
[]string{"CategoryID", "StoreID", "DeletedAt"},
}
}
// type StoreSkuCategoryMap2 struct {
// ModelIDCULD
// StoreID int `orm:"column(store_id)"`
// VendorID int `orm:"column(vendor_id)"`
// CategoryID int `orm:"column(category_id)"`
// VendorCatID string `orm:"column(vendor_cat_id);size(48)" json:"vendorCatID"`
// SyncStatus int8 `orm:"default(2)"`
// }
// func (*StoreSkuCategoryMap2) TableUnique() [][]string {
// return [][]string{
// []string{"StoreID", "VendorID", "CategoryID", "DeletedAt"},
// }
// }
type StoreSkuBind struct { type StoreSkuBind struct {
ModelIDCULD ModelIDCULD
@@ -88,6 +111,12 @@ func (*StoreSkuBind) TableUnique() [][]string {
} }
} }
func (*StoreSkuBind) TableIndex() [][]string {
return [][]string{
[]string{"SkuID", "StoreID", "DeletedAt"},
}
}
type StoreOpRequest struct { type StoreOpRequest struct {
ModelIDCULD // DeletedAt用于表示请求操作结束而并不一定是删除 ModelIDCULD // DeletedAt用于表示请求操作结束而并不一定是删除

View File

@@ -18,7 +18,11 @@ const (
ServerMsgPing = "ping" ServerMsgPing = "ping"
ServerMsgNewOrder = "newOrder" ServerMsgNewOrder = "newOrder"
ServerMsgFinishedPickup = "finishedPickup"
ServerMsgKeyOrderStatusChanged = "keyOrderStatusChanged" // 重要订单状态变化 ServerMsgKeyOrderStatusChanged = "keyOrderStatusChanged" // 重要订单状态变化
ServerMsgNewWait4ApproveAfsOrder = "newWait4ApproveAfsOrder"
ServerMsgKeyAfsOrderStatusChanged = "keyAfsOrderStatusChanged" // 重要售后单状态变化
) )
const ( const (
@@ -67,8 +71,8 @@ func routinueFunc() {
registerMsg := msg.MsgData.(*MsgOp) registerMsg := msg.MsgData.(*MsgOp)
delete(channelMap[registerMsg.StoreID], registerMsg.Chan2Listen) delete(channelMap[registerMsg.StoreID], registerMsg.Chan2Listen)
close(registerMsg.Chan2Close) close(registerMsg.Chan2Close)
case ServerMsgNewOrder, ServerMsgKeyOrderStatusChanged: case ServerMsgNewOrder, ServerMsgFinishedPickup, ServerMsgKeyOrderStatusChanged, ServerMsgNewWait4ApproveAfsOrder, ServerMsgKeyAfsOrderStatusChanged:
globals.SugarLogger.Debugf("msghub routinueFunc, msg:%s", utils.Format4Output(msg, false)) globals.SugarLogger.Debugf("msghub routinueFunc, msg:%s", utils.Format4Output(msg, true))
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
for chan2Send := range channelMap[msg.StoreID] { for chan2Send := range channelMap[msg.StoreID] {
chan2Send <- msg chan2Send <- msg
@@ -138,7 +142,7 @@ func GetMsg(ctx *jxcontext.Context, storeID int, lastOrderTime time.Time, lastOr
case msg2, ok := <-chan2Listen: case msg2, ok := <-chan2Listen:
timer.Stop() timer.Stop()
if ok { if ok {
msg.MsgData = msg2.MsgData msg = msg2
} }
case <-timer.C: case <-timer.C:
unregisterChan(storeID, chan2Listen) unregisterChan(storeID, chan2Listen)
@@ -152,7 +156,7 @@ func GetMsg(ctx *jxcontext.Context, storeID int, lastOrderTime time.Time, lastOr
} }
func OnNewOrder(order *model.GoodsOrder) { func OnNewOrder(order *model.GoodsOrder) {
globals.SugarLogger.Debugf("msghub OnNewOrder, order:%s", utils.Format4Output(order, false)) globals.SugarLogger.Debugf("msghub OnNewOrder, order:%s", utils.Format4Output(order, true))
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
msgChan <- &ServerMsg{ msgChan <- &ServerMsg{
Type: ServerMsgNewOrder, Type: ServerMsgNewOrder,
@@ -167,13 +171,13 @@ func OnNewOrder(order *model.GoodsOrder) {
}) })
} }
func OnKeyOrderStatusChanged(order *model.GoodsOrder) { func OnFinishedPickup(order *model.GoodsOrder) {
globals.SugarLogger.Debugf("msghub OnKeyOrderStatusChanged, order:%s", utils.Format4Output(order, false)) globals.SugarLogger.Debugf("msghub OnFinishedPickup, order:%s", utils.Format4Output(order, true))
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
msgChan <- &ServerMsg{ msgChan <- &ServerMsg{
Type: ServerMsgKeyOrderStatusChanged, Type: ServerMsgFinishedPickup,
StoreID: jxutils.GetSaleStoreIDFromOrder(order), StoreID: jxutils.GetSaleStoreIDFromOrder(order),
MsgData: order, MsgData: 1,
// MsgData: []*model.GoodsOrderExt{ // MsgData: []*model.GoodsOrderExt{
// &model.GoodsOrderExt{ // &model.GoodsOrderExt{
// GoodsOrder: *order, // GoodsOrder: *order,
@@ -182,3 +186,36 @@ func OnKeyOrderStatusChanged(order *model.GoodsOrder) {
} }
}) })
} }
func OnKeyOrderStatusChanged(order *model.GoodsOrder) {
globals.SugarLogger.Debugf("msghub OnKeyOrderStatusChanged, order:%s", utils.Format4Output(order, true))
utils.CallFuncAsync(func() {
msgChan <- &ServerMsg{
Type: ServerMsgKeyOrderStatusChanged,
StoreID: jxutils.GetSaleStoreIDFromOrder(order),
MsgData: order,
}
})
}
func OnNewWait4ApproveAfsOrder(order *model.AfsOrder) {
globals.SugarLogger.Debugf("msghub OnNewWait4ApproveAfsOrder, order:%s", utils.Format4Output(order, true))
utils.CallFuncAsync(func() {
msgChan <- &ServerMsg{
Type: ServerMsgNewWait4ApproveAfsOrder,
StoreID: jxutils.GetSaleStoreIDFromAfsOrder(order),
MsgData: order,
}
})
}
func OnKeyAfsOrderStatusChanged(order *model.AfsOrder) {
globals.SugarLogger.Debugf("msghub OnKeyAfsOrderStatusChanged, order:%s", utils.Format4Output(order, true))
utils.CallFuncAsync(func() {
msgChan <- &ServerMsg{
Type: ServerMsgKeyAfsOrderStatusChanged,
StoreID: jxutils.GetSaleStoreIDFromAfsOrder(order),
MsgData: order,
}
})
}

View File

@@ -50,7 +50,7 @@ func (c *DeliveryHandler) GetVendorID() int {
func (c *DeliveryHandler) OnWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaapi.CallbackResponse) { func (c *DeliveryHandler) OnWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaapi.CallbackResponse) {
jxutils.CallMsgHandler(func() { jxutils.CallMsgHandler(func() {
retVal = c.onWaybillMsg(msg) retVal = c.onWaybillMsg(msg)
}, msg.OrderID) }, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDDada))
return retVal return retVal
} }
@@ -58,12 +58,16 @@ func (c *DeliveryHandler) onWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaap
order := c.callbackMsg2Waybill(msg) order := c.callbackMsg2Waybill(msg)
switch msg.OrderStatus { switch msg.OrderStatus {
case dadaapi.OrderStatusWaitingForAccept: case dadaapi.OrderStatusWaitingForAccept:
order.Status = model.WaybillStatusNew
case dadaapi.OrderStatusAccepted:
if result, err := api.DadaAPI.QueryOrderInfo(msg.OrderID); err == nil { if result, err := api.DadaAPI.QueryOrderInfo(msg.OrderID); err == nil {
order.ActualFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["actualFee"], 0.0)) order.ActualFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["actualFee"], 0.0))
order.DesiredFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["deliveryFee"], 0.0)) order.DesiredFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["deliveryFee"], 0.0))
} }
order.Status = model.WaybillStatusNew
case dadaapi.OrderStatusAccepted:
// if result, err := api.DadaAPI.QueryOrderInfo(msg.OrderID); err == nil {
// order.ActualFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["actualFee"], 0.0))
// order.DesiredFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["deliveryFee"], 0.0))
// }
order.Status = model.WaybillStatusAccepted order.Status = model.WaybillStatusAccepted
case dadaapi.OrderStatusDelivering: case dadaapi.OrderStatusDelivering:
order.Status = model.WaybillStatusDelivering order.Status = model.WaybillStatusDelivering
@@ -99,17 +103,23 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *dadaapi.CallbackMsg) (retVal
return retVal return retVal
} }
// IDeliveryPlatformHandler func (c *DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) {
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
db := dao.GetDB() db := dao.GetDB()
deliveryFee, addFee, err := delivery.CalculateOrderDeliveryFee(order, time.Now(), db) deliveryFeeInfo = &partner.WaybillFeeInfo{}
if err != nil { deliveryFeeInfo.RefDeliveryFee, deliveryFeeInfo.RefAddFee, err = delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
billParams, addParams, err := c.getBillParams(db, order)
if err == nil {
var result *dadaapi.CreateOrderResponse
if result, err = api.DadaAPI.QueryDeliverFee(billParams, addParams); err != nil {
return nil, err return nil, err
} }
if err = delivery.CallCreateWaybillPolicy(policy, deliveryFee, addFee, deliveryFee, order, model.VendorIDDada); err != nil { deliveryFeeInfo.DeliveryFee = jxutils.StandardPrice2Int(result.Fee)
return nil, err
} }
billParams := &dadaapi.OperateOrderRequiredParams{ return deliveryFeeInfo, err
}
func (c *DeliveryHandler) getBillParams(db *dao.DaoDB, order *model.GoodsOrder) (billParams *dadaapi.OperateOrderRequiredParams, addParams map[string]interface{}, err error) {
billParams = &dadaapi.OperateOrderRequiredParams{
// ShopNo: utils.Int2Str(order.StoreID), // 当前达达的门店号与京西是一样的 // ShopNo: utils.Int2Str(order.StoreID), // 当前达达的门店号与京西是一样的
OriginID: jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), OriginID: jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID),
CargoPrice: jxutils.IntPrice2Standard(limitOrderPrice(order.ActualPayPrice)), CargoPrice: jxutils.IntPrice2Standard(limitOrderPrice(order.ActualPayPrice)),
@@ -121,7 +131,7 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
if billParams.ShopNo, err = c.getDadaShopID(order, db); err == nil { if billParams.ShopNo, err = c.getDadaShopID(order, db); err == nil {
if billParams.CityCode, err = c.getDataCityCodeFromOrder(order, db); err == nil { if billParams.CityCode, err = c.getDataCityCodeFromOrder(order, db); err == nil {
billParams.ReceiverLng, billParams.ReceiverLat, _ = jxutils.IntCoordinate2MarsStandard(order.ConsigneeLng, order.ConsigneeLat, order.CoordinateType) billParams.ReceiverLng, billParams.ReceiverLat, _ = jxutils.IntCoordinate2MarsStandard(order.ConsigneeLng, order.ConsigneeLat, order.CoordinateType)
addParams := map[string]interface{}{ addParams = map[string]interface{}{
"info": fmt.Sprintf("%s第%d号订单, %s", model.VendorChineseNames[order.VendorID], order.OrderSeq, utils.FilterMb4(order.BuyerComment)), "info": fmt.Sprintf("%s第%d号订单, %s", model.VendorChineseNames[order.VendorID], order.OrderSeq, utils.FilterMb4(order.BuyerComment)),
// "origin_mark": model.VendorNames[order.VendorID], // 订单来源标示该字段可以显示在达达app订单详情页面只支持字母最大长度为10 // "origin_mark": model.VendorNames[order.VendorID], // 订单来源标示该字段可以显示在达达app订单详情页面只支持字母最大长度为10
// "origin_mark_no": fmt.Sprintf("%d", order.OrderSeq), // 订单来源编号该字段可以显示在达达app订单详情页面支持字母和数字最大长度为30 // "origin_mark_no": fmt.Sprintf("%d", order.OrderSeq), // 订单来源编号该字段可以显示在达达app订单详情页面支持字母和数字最大长度为30
@@ -129,7 +139,23 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
"cargo_weight": jxutils.IntWeight2Float(limitOrderWeight(order.Weight)), "cargo_weight": jxutils.IntWeight2Float(limitOrderWeight(order.Weight)),
"cargo_num": order.GoodsCount, "cargo_num": order.GoodsCount,
} }
}
}
return billParams, addParams, err
}
// IDeliveryPlatformHandler
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
db := dao.GetDB()
deliveryFee, addFee, err := delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
if err != nil {
return nil, err
}
if err = delivery.CallCreateWaybillPolicy(policy, deliveryFee, addFee, deliveryFee, order, model.VendorIDDada); err != nil {
return nil, err
}
billParams, addParams, err := c.getBillParams(db, order)
if err == nil {
if globals.EnableStoreWrite { if globals.EnableStoreWrite {
// 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单 // 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单
var waybillList []*model.Waybill var waybillList []*model.Waybill
@@ -175,7 +201,6 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
err = fmt.Errorf("测试环境不能真正创建运单") err = fmt.Errorf("测试环境不能真正创建运单")
} }
} }
}
return bill, err return bill, err
} }

View File

@@ -17,21 +17,33 @@ import (
const ( const (
warningDistance = 10 // 公里 warningDistance = 10 // 公里
warningWeight = 50 * 1000 // 克 warningWeight = 50 * 1000 // 克
maxDiffFee2Mtps = 150 // 与美团配送最多差价 maxDiffFee2Mtps = 500 // 与美团配送最多差价
maxAddFee = 300 // 最大增加费用,单位为分,超过不发三方配送了 maxAddFee = 200 // 最大增加费用,单位为分,超过不发三方配送了
)
var (
DefCreateWaybillPolicy = CreateWaybillPolicy(maxDiffFee2Mtps, maxAddFee)
) )
func NullCreateWaybillPolicy(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) { func NullCreateWaybillPolicy(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) {
return "" return ""
} }
func DefCreateWaybillPolicy(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) { func CreateWaybillPolicy(maxDiffFee2Mtps2, maxAddFee2 int64) func(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) {
if refDeliveryFee-deliveryFee > maxDiffFee2Mtps { if maxDiffFee2Mtps2 == 0 {
errStr = fmt.Sprintf("超参考价太多, 费用:%d参考价:%d, 最高超价:%d", deliveryFee, refDeliveryFee, maxDiffFee2Mtps) maxDiffFee2Mtps2 = maxDiffFee2Mtps
} else if refAddFee > maxAddFee { }
errStr = fmt.Sprintf("超基础价太多, 当前加价:%d, 最高加价:%d", refAddFee, maxAddFee) if maxAddFee2 == 0 {
maxAddFee2 = maxAddFee
}
return func(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) {
if deliveryFee-refDeliveryFee > maxDiffFee2Mtps2 {
errStr = fmt.Sprintf("超参考价太多, 费用:%d参考价:%d, 最高超价:%d", deliveryFee, refDeliveryFee, maxDiffFee2Mtps2)
} else if refAddFee > maxAddFee2 {
errStr = fmt.Sprintf("超基础价太多, 当前加价:%d, 最高加价:%d", refAddFee, maxAddFee2)
} }
return errStr return errStr
}
} }
func AddPolicy(prevPolicy, newPolicy partner.CreateWaybillPolicy) (outPolicy partner.CreateWaybillPolicy) { func AddPolicy(prevPolicy, newPolicy partner.CreateWaybillPolicy) (outPolicy partner.CreateWaybillPolicy) {
@@ -43,11 +55,11 @@ func AddPolicy(prevPolicy, newPolicy partner.CreateWaybillPolicy) (outPolicy par
} }
} }
func CallCreateWaybillPolicy(policy partner.CreateWaybillPolicy, refDeliveryFee, deliveryFee, addFee int64, order *model.GoodsOrder, waybillVendorID int) (err error) { func CallCreateWaybillPolicy(policy partner.CreateWaybillPolicy, refDeliveryFee, refAddFee, deliveryFee int64, order *model.GoodsOrder, waybillVendorID int) (err error) {
if policy == nil { if policy == nil {
policy = NullCreateWaybillPolicy policy = NullCreateWaybillPolicy
} }
if errStr := policy(deliveryFee, deliveryFee, addFee); errStr != "" { if errStr := policy(refDeliveryFee, refAddFee, deliveryFee); errStr != "" {
waybillVendorName := jxutils.GetVendorName(waybillVendorID) waybillVendorName := jxutils.GetVendorName(waybillVendorID)
errStr = fmt.Sprintf("oderID:%s创建运单出错:%s", order.VendorOrderID, errStr) errStr = fmt.Sprintf("oderID:%s创建运单出错:%s", order.VendorOrderID, errStr)
globals.SugarLogger.Debugf("%s CallCreateWaybillPolicy failed with %s", waybillVendorName, errStr) globals.SugarLogger.Debugf("%s CallCreateWaybillPolicy failed with %s", waybillVendorName, errStr)

View File

@@ -55,7 +55,7 @@ func OnWaybillExcept(msg *mtpsapi.CallbackOrderExceptionMsg) (retVal *mtpsapi.Ca
func (c *DeliveryHandler) OnWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *mtpsapi.CallbackResponse) { func (c *DeliveryHandler) OnWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *mtpsapi.CallbackResponse) {
jxutils.CallMsgHandler(func() { jxutils.CallMsgHandler(func() {
retVal = c.onWaybillMsg(msg) retVal = c.onWaybillMsg(msg)
}, msg.OrderID) }, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDMTPS))
return retVal return retVal
} }
@@ -73,7 +73,7 @@ func (c *DeliveryHandler) OnWaybillExcept(msg *mtpsapi.CallbackOrderExceptionMsg
} }
order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID) order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
retVal = mtpsapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), "mtps OnWaybillExcept") retVal = mtpsapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), "mtps OnWaybillExcept")
}, msg.OrderID) }, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDDada))
return retVal return retVal
} }
@@ -81,9 +81,10 @@ func (c *DeliveryHandler) onWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *m
order := c.callbackMsg2Waybill(msg) order := c.callbackMsg2Waybill(msg)
switch msg.Status { switch msg.Status {
case mtpsapi.OrderStatusWaitingForSchedule: case mtpsapi.OrderStatusWaitingForSchedule:
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order)
order.Status = model.WaybillStatusNew order.Status = model.WaybillStatusNew
case mtpsapi.OrderStatusAccepted: case mtpsapi.OrderStatusAccepted:
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order) order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order) // 美团外卖可能会丢失新运单事件,这里补一下
order.Status = model.WaybillStatusAccepted order.Status = model.WaybillStatusAccepted
case mtpsapi.OrderStatusPickedUp: case mtpsapi.OrderStatusPickedUp:
order.Status = model.WaybillStatusDelivering order.Status = model.WaybillStatusDelivering
@@ -113,6 +114,18 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *mtpsapi.CallbackOrderMsg) (re
return retVal return retVal
} }
func (c *DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) {
db := dao.GetDB()
deliveryFeeInfo = &partner.WaybillFeeInfo{}
deliveryFeeInfo.RefDeliveryFee, deliveryFeeInfo.RefAddFee, err = delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
if err == nil {
if _, err = c.getMTPSShopID(order, db); err == nil {
deliveryFeeInfo.DeliveryFee = deliveryFeeInfo.RefDeliveryFee
}
}
return deliveryFeeInfo, err
}
// IDeliveryPlatformHandler // IDeliveryPlatformHandler
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) { func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
db := dao.GetDB() db := dao.GetDB()
@@ -167,7 +180,6 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
if globals.EnableStoreWrite { if globals.EnableStoreWrite {
result, err2 := api.MtpsAPI.CreateOrderByShop(billParams, addParams) result, err2 := api.MtpsAPI.CreateOrderByShop(billParams, addParams)
if err = err2; err == nil { if err = err2; err == nil {
globals.SugarLogger.Debugf("CreateWaybill failed, orderID:%s, billParams:%v, addParams:%v, error:%v", order.VendorOrderID, billParams, addParams, err)
bill = &model.Waybill{ bill = &model.Waybill{
VendorOrderID: order.VendorOrderID, VendorOrderID: order.VendorOrderID,
OrderVendorID: order.VendorID, OrderVendorID: order.VendorID,
@@ -176,6 +188,8 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
WaybillVendorID: model.VendorIDMTPS, WaybillVendorID: model.VendorIDMTPS,
DesiredFee: deliveryFee, DesiredFee: deliveryFee,
} }
} else {
globals.SugarLogger.Debugf("CreateWaybill failed, orderID:%s, billParams:%v, addParams:%v, error:%v", order.VendorOrderID, billParams, addParams, err)
} }
} else { } else {
err = fmt.Errorf("测试环境不能真正创建运单") err = fmt.Errorf("测试环境不能真正创建运单")

View File

@@ -33,6 +33,12 @@ const (
PrinterStatusOnlineAbnormal = 3 PrinterStatusOnlineAbnormal = 3
) )
const (
AfsApproveTypeRefund = 1 // 退款
AfsApproveTypeReturnGoods = 2 // 退货
AfsApproveTypeRefused = 3 // 驳回
)
const ( const (
PrintResultSuccess = 0 PrintResultSuccess = 0
PrintResultNoPrinter = 1 PrintResultNoPrinter = 1
@@ -55,6 +61,7 @@ const (
TimerTypeByPass = 1 TimerTypeByPass = 1
TimerTypeBaseNow = 2 TimerTypeBaseNow = 2
TimerTypeBaseStatusTime = 3 TimerTypeBaseStatusTime = 3
TimerTypeBaseOrderCreatedAt = 4
) )
type StatusActionParams struct { type StatusActionParams struct {
@@ -63,15 +70,37 @@ type StatusActionParams struct {
TimeoutGap int // 以秒为单位的随机时间0在GetStatusActionConfig返回时表示不修改缺省 TimeoutGap int // 以秒为单位的随机时间0在GetStatusActionConfig返回时表示不修改缺省
} }
func (s *StatusActionParams) GetRefTimeout(statusTime time.Time) (timeout time.Duration) { const (
WaybillFeeErrCodeCourierNotOpen = 1 //配送门店没有启用
WaybillFeeErrCodeCourierNotSupported = 2 //配送门店不被系统支持
WaybillFeeErrCodeCourierForbidden = 3 //配送门店内部禁用
WaybillFeeErrCodeCourierOthers = 10 //其它错误
)
type WaybillFeeInfo struct {
ErrCode int `json:"errCode"`
ErrStr string `json:"errStr"`
RefDeliveryFee int64 `json:"refDeliveryFee"`
RefAddFee int64 `json:"refAddFee"`
DeliveryFee int64 `json:"deliveryFee"`
TimeoutSecond int `json:"timeoutSecond"` // 系统会自动发运单的倒计时
Waybill *model.Waybill `json:"waybill"`
}
func (s *StatusActionParams) GetRefTimeout(statusTime time.Time, orderCreatedAt time.Time) (timeout time.Duration) {
switch s.TimerType { switch s.TimerType {
case TimerTypeBaseNow: case TimerTypeBaseNow:
timeout = s.Timeout timeout = s.Timeout
case TimerTypeBaseStatusTime: case TimerTypeBaseStatusTime:
timeout = statusTime.Sub(time.Now()) + s.Timeout timeout = statusTime.Sub(time.Now()) + s.Timeout
case TimerTypeBaseOrderCreatedAt:
timeout = orderCreatedAt.Sub(time.Now()) + s.Timeout
default: default:
timeout = 0 timeout = 0
} }
if timeout < 0 {
timeout = 0
}
return timeout return timeout
} }
@@ -100,8 +129,8 @@ var (
type IOrderManager interface { type IOrderManager interface {
SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao.DaoDB) (isDuplicated bool, err error) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao.DaoDB) (isDuplicated bool, err error)
OnOrderNew(order *model.GoodsOrder, msgVendorStatus string) (err error) OnOrderNew(order *model.GoodsOrder, orderStatus *model.OrderStatus) (err error)
OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus string) (err error) OnOrderAdjust(order *model.GoodsOrder, orderStatus *model.OrderStatus) (err error)
OnOrderStatusChanged(orderStatus *model.OrderStatus) (err error) OnOrderStatusChanged(orderStatus *model.OrderStatus) (err error)
OnOrderMsg(order *model.GoodsOrder, vendorStatus, remark string) (err error) OnOrderMsg(order *model.GoodsOrder, vendorStatus, remark string) (err error)
@@ -113,14 +142,25 @@ type IOrderManager interface {
LoadOrderFinancial(vendorOrderID string, vendorID int) (order *model.OrderFinancial, err error) LoadOrderFinancial(vendorOrderID string, vendorID int) (order *model.OrderFinancial, err error)
LoadOrderFinancial2(vendorOrderID2 string, vendorID int) (order *model.OrderFinancial, err error) LoadOrderFinancial2(vendorOrderID2 string, vendorID int) (order *model.OrderFinancial, err error)
UpdateWaybillVendorID(bill *model.Waybill, revertStatus bool) (err error) UpdateOrderStatusAndDeliveryFlag(order *model.GoodsOrder) (err error)
UpdateOrderStatusAndFlag(order *model.GoodsOrder) (err error) UpdateOrderFields(order *model.GoodsOrder, fieldList []string) (err error)
LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error) LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error)
OnOrderComments(orderCommentList []*model.OrderComment) (err error) OnOrderComments(orderCommentList []*model.OrderComment) (err error)
SaveOrderFinancialInfo(order *model.OrderFinancial, operation string) (err error) SaveOrderFinancialInfo(order *model.OrderFinancial, operation string) (err error)
SaveAfsOrderFinancialInfo(afsOrder *model.AfsOrder) (err error) SaveAfsOrderFinancialInfo(afsOrder *model.AfsOrder) (err error)
GetOrderWaybillInfo(ctx *jxcontext.Context, vendorOrderID string, vendorID int, isNotEnded bool) (bills []*model.Waybill, err error)
// afs order
OnAfsOrderAdjust(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus) (err error)
OnAfsOrderNew(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus) (err error)
OnAfsOrderStatusChanged(orderStatus *model.OrderStatus) (err error)
LoadAfsOrder(vendorAfsOrderID string, vendorID int) (afsOrder *model.AfsOrder, err error)
UpdateAfsOrderFields(afsOrder *model.AfsOrder, fieldList []string) (err error)
GetStatusDuplicatedCount(status *model.OrderStatus) (duplicatedCount int)
} }
type IStoreManager interface { type IStoreManager interface {
@@ -132,6 +172,7 @@ type IStoreManager interface {
// 所有非以SyncRefresh开头的函数不用自己清理sync_status标记VendorSync统一处理 // 所有非以SyncRefresh开头的函数不用自己清理sync_status标记VendorSync统一处理
type IPurchasePlatformHandler interface { type IPurchasePlatformHandler interface {
IPurchasePlatformPromotionHandler
GetVendorID() int GetVendorID() int
GetStatusFromVendorStatus(vendorStatus string) int GetStatusFromVendorStatus(vendorStatus string) int
@@ -167,6 +208,16 @@ type IPurchasePlatformHandler interface {
// order.Skus要包含原始订单中的Sku信息removedSkuList中是要移除的Sku信息 // order.Skus要包含原始订单中的Sku信息removedSkuList中是要移除的Sku信息
AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error)
// 售后
// // 发起全款退款
// RefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error)
// // 发起部分退款
// PartRefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, refundSkuList []*model.OrderSku, reason string) (err error)
// 审核售后单申请
AgreeOrRefuseRefund(ctx *jxcontext.Context, order *model.AfsOrder, approveType int, reason string) (err error)
// // 确认收到退货
ConfirmReceivedReturnGoods(ctx *jxcontext.Context, order *model.AfsOrder) (err error)
//////// ////////
// Store // Store
ReadStore(vendorStoreID string) (store *model.Store, err error) ReadStore(vendorStoreID string) (store *model.Store, err error)
@@ -184,6 +235,10 @@ type IPurchasePlatformHandler interface {
UploadImg(ctx *jxcontext.Context, imgURL string, imgData []byte, imgName string) (imgHint string, err error) UploadImg(ctx *jxcontext.Context, imgURL string, imgData []byte, imgName string) (imgHint string, err error)
GetStoreStatus(ctx *jxcontext.Context, vendorStoreID string) (storeStatus int, err error) GetStoreStatus(ctx *jxcontext.Context, vendorStoreID string) (storeStatus int, err error)
GetVendorCategories(ctx *jxcontext.Context) (vendorCats []*model.SkuVendorCategory, err error) GetVendorCategories(ctx *jxcontext.Context) (vendorCats []*model.SkuVendorCategory, err error)
// todo 如下两个函数需要合并一下s
GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error)
GetStoreSkusInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, inStoreSkuList []*BareStoreSkuInfo) (outStoreSkuList []*BareStoreSkuInfo, err error)
} }
// db *dao.DaoDB, // db *dao.DaoDB,
@@ -219,6 +274,7 @@ type IDeliveryPlatformHandler interface {
CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error)
GetVendorID() int GetVendorID() int
GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *WaybillFeeInfo, err error)
} }
type IPrinterHandler interface { type IPrinterHandler interface {

View File

@@ -0,0 +1,24 @@
package partner
import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
)
type IPurchasePlatformPromotionHandler interface {
// 如果是单品级活动actOrderRules为空
// 如果是订单级活动actStoreSku可以为空表示不限制SKU
CreateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error)
UpdateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error)
// 取消整个京西活动
CancelAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error)
}
func ActStoreSku2Map(actStoreSku []*model.ActStoreSku2) (actStoreSkuMap map[int][]*model.ActStoreSku2) {
actStoreSkuMap = make(map[int][]*model.ActStoreSku2)
for _, storeSku := range actStoreSku {
actStoreSkuMap[storeSku.StoreID] = append(actStoreSkuMap[storeSku.StoreID], storeSku)
}
return actStoreSkuMap
}

View File

@@ -0,0 +1,89 @@
package partner
import (
"fmt"
"git.rosy.net.cn/jx-callback/business/model"
)
const (
ErrCodeUnknown = 1
ErrCodeChangePriceFailed = 100
)
type ErrorWithCode struct {
errMsg string
intCode int
vendorID int
storeID int
skuID int
}
func NewErrorCode(errMsg string, code, vendorID int) *ErrorWithCode {
retVal := &ErrorWithCode{
errMsg: errMsg,
intCode: code,
vendorID: vendorID,
}
return retVal
}
func (e *ErrorWithCode) SetStoreID(storeID int) {
e.storeID = storeID
}
func (e *ErrorWithCode) SetSkuID(skuID int) {
e.skuID = skuID
}
func (e *ErrorWithCode) Error() string {
return fmt.Sprintf("平台:%s, code:%d, %s", model.VendorChineseNames[e.VendorID()], e.intCode, e.errMsg)
}
func (e *ErrorWithCode) String() string {
return e.Error()
}
func (e *ErrorWithCode) Code() int {
return e.intCode
}
func (e *ErrorWithCode) ErrMsg() string {
return e.errMsg
}
func (e *ErrorWithCode) VendorID() int {
return e.vendorID
}
func (e *ErrorWithCode) StoreID() int {
return e.storeID
}
func (e *ErrorWithCode) SkuID() int {
return e.skuID
}
func IsErrChangePriceFailed(err error) *ErrorWithCode {
if vendorErr, ok := err.(*ErrorWithCode); ok && vendorErr.Code() == ErrCodeChangePriceFailed {
return vendorErr
}
return nil
}
func IsErrVendorError(err error) *ErrorWithCode {
if vendorErr, ok := err.(*ErrorWithCode); ok {
return vendorErr
}
return nil
}
func AddVendorInfo2Err(inErr error, vendorID int) (outErr error) {
outErr = inErr
if inErr != nil {
if IsErrVendorError(inErr) == nil {
outErr = NewErrorCode(inErr.Error(), ErrCodeUnknown, vendorID)
}
}
return outErr
}

View File

@@ -0,0 +1,82 @@
package partner
import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model/dao"
)
const (
FuncCreateStoreSkus = 1
FuncDeleteStoreSkus = 2
FuncUpdateStoreSkusStatus = 3
FuncUpdateStoreSkusPrice = 4
FuncUpdateStoreSkus = 5
)
type BareStoreSkuInfo struct {
SkuID int `json:"skuID,omitempty"`
VendorSkuID string `json:"vendorSkuID,omitempty"`
NameID int `json:"nameID,omitempty"`
VendorNameID string `json:"vendorNameID,omitempty"`
Price int64 `json:"price,omitempty"`
Status int `json:"status,omitempty"`
}
type BareStoreSkuInfoList []*BareStoreSkuInfo
func (l BareStoreSkuInfoList) GetVendorSkuIDList() (vendorSkuIDList []string) {
for _, v := range l {
if !dao.IsVendorThingIDEmpty(v.VendorSkuID) {
vendorSkuIDList = append(vendorSkuIDList, v.VendorSkuID)
}
}
return vendorSkuIDList
}
func (l BareStoreSkuInfoList) GetVendorSkuIDIntList() (vendorSkuIDIntList []int64) {
for _, v := range l {
if !dao.IsVendorThingIDEmpty(v.VendorSkuID) {
vendorSkuIDIntList = append(vendorSkuIDIntList, utils.Str2Int64(v.VendorSkuID))
}
}
return vendorSkuIDIntList
}
func (l BareStoreSkuInfoList) GetSkuIDList() (skuIDList []int) {
for k, v := range l {
if v.SkuID > 0 {
skuIDList[k] = v.SkuID
}
}
return skuIDList
}
type BareCategoryInfo struct {
VendorCatID string `json:"vendorCatID"`
Level int `json:"level"`
Name string `json:"name"`
Seq int `json:"seq,omitempty"`
Children []*BareCategoryInfo `json:"children,omitempty"`
}
type IPurchasePlatformStoreSkuHandler interface {
GetStoreSkusBatchSize(funcID int) int
CreateStoreSkus(ctx *jxcontext.Context, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (err error)
DeleteStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*BareStoreSkuInfo) (err error)
UpdateStoreSkusStatus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*BareStoreSkuInfo) (err error)
UpdateStoreSkusPrice(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*BareStoreSkuInfo) (err error)
}
type ISingleStoreStoreSkuHandler interface {
IPurchasePlatformStoreSkuHandler
ReadStoreCategory(ctx *jxcontext.Context, vendorStoreID string) (cats []*BareCategoryInfo, err error)
CreateStoreSkuCategory(ctx *jxcontext.Context, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error)
UpdateStoreSkuCategory(ctx *jxcontext.Context, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error)
DeleteStoreSkuCategory(ctx *jxcontext.Context, vendorStoreID, vendorCatID string) (err error)
UpdateStoreSkus(ctx *jxcontext.Context, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (err error)
}

View File

@@ -33,7 +33,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour) expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
} }
orderFmt := ` orderFmt := `
<CB>京西菜市</CB><BR><BR> <CB>%s</CB><BR><BR>
<C>手机买菜上京西</C><BR> <C>手机买菜上京西</C><BR>
<C>极速到家送惊喜</C><BR> <C>极速到家送惊喜</C><BR>
--------------------------------<BR> --------------------------------<BR>
@@ -59,6 +59,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
品名 数量 单价 小计<BR> 品名 数量 单价 小计<BR>
--------------------------------<BR>` --------------------------------<BR>`
orderParams := []interface{}{ orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt), utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime), utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID, order.VendorOrderID,
@@ -84,7 +85,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
<C><L><BOLD>商品质量问题请联系:</BOLD></L><BR></C> <C><L><BOLD>商品质量问题请联系:</BOLD></L><BR></C>
<C><L><BOLD>%s:%s</BOLD></L><BR></C><BR> <C><L><BOLD>%s:%s</BOLD></L><BR></C><BR>
<BR> <BR>
更多信息请关注官方微信: 京西菜市<BR> 更多信息请关注官方微信: %s<BR>
<BR> <BR>
<BR><BR> <BR><BR>
--------------------------------<BR> --------------------------------<BR>
@@ -92,7 +93,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
<BR><BR> <BR><BR>
` `
// <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR> // <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR>
orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel) orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel, globals.StoreName)
return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...) return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...)
} }

View File

@@ -34,7 +34,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour) expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
} }
orderFmt := ` orderFmt := `
<big> 京西菜市** <big> %s**
手机买菜上京西* 手机买菜上京西*
极速到家送惊喜* 极速到家送惊喜*
------------------------------* ------------------------------*
@@ -58,6 +58,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
--------------------------------* --------------------------------*
` `
orderParams := []interface{}{ orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt), utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime), utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID, order.VendorOrderID,
@@ -82,16 +83,80 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
<S020>商品质量问题请联系:* <S020>商品质量问题请联系:*
<S020>%s:%s* <S020>%s:%s*
* *
更多信息请关注官方微信: 京西菜市* 更多信息请关注官方微信: %s*
-------------------------------- --------------------------------
-------------------------------- --------------------------------
*<BEEP13500,3,2,1>* *<BEEP13500,3,2,1>*
` `
// <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR> // <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR>
orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel) orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel, globals.StoreName)
return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), escapeString4Printer(orderParams)...) return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), escapeString4Printer(orderParams)...)
} }
func (c *PrinterHandler) getOrderContent2(order *model.GoodsOrder, storeTel string) (content string) {
expectedDeliveryTime := order.ExpectedDeliveredTime
if utils.IsTimeZero(expectedDeliveryTime) {
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
}
orderFmt := `
|7 %s
|5 手机买菜上京西
|5 极速到家送惊喜
|5--------------------------------
|5下单时间: %s
|5预计送达: %s
|5订单编号: %s
|5
|7%s\#%d
|5
|2%s
|5客户: %s
|5电话: %s
|5地址: %s
|5
|5客户备注:
|7%s
|5
|6实际支付: %s
|5
|5商品明细:
|5品名 数量 单价 小计
|5--------------------------------
`
orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID,
jxutils.GetVendorName(order.VendorID),
order.OrderSeq,
order.VendorOrderID,
order.ConsigneeName,
order.ConsigneeMobile,
order.ConsigneeAddress,
order.BuyerComment,
jxutils.IntPrice2StandardCurrencyString(order.ActualPayPrice),
}
for _, sku := range order.Skus {
orderFmt += `|5%s`
orderFmt += `|5%8s%10s%10s`
orderParams = append(orderParams, sku.SkuName, "x"+utils.Int2Str(sku.Count), jxutils.IntPrice2StandardCurrencyString(sku.SalePrice), jxutils.IntPrice2StandardCurrencyString(sku.SalePrice*int64(sku.Count)))
}
orderFmt += `
|5
|6共%d种%d件商品
|5--------------------------------
|5商品质量问题请联系:
|5%s:%s
|5
|5更多信息请关注官方微信: %s
|5--------------------------------
|5--------------------------------
`
orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel, globals.StoreName)
return fmt.Sprintf(orderFmt, escapeString4Printer(orderParams)...)
}
func (c *PrinterHandler) GetVendorID() int { func (c *PrinterHandler) GetVendorID() int {
return model.VendorIDXiaoWM return model.VendorIDXiaoWM
} }
@@ -131,10 +196,20 @@ func (c *PrinterHandler) GetPrinterStatus(ctx *jxcontext.Context, printerNumber,
func (c *PrinterHandler) PrintOrder(ctx *jxcontext.Context, store *model.Store, order *model.GoodsOrder) (printerStatus *partner.PrinterStatus, err error) { func (c *PrinterHandler) PrintOrder(ctx *jxcontext.Context, store *model.Store, order *model.GoodsOrder) (printerStatus *partner.PrinterStatus, err error) {
globals.SugarLogger.Debugf("xiaowm PrintOrderByOrder orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("xiaowm PrintOrderByOrder orderID:%s", order.VendorOrderID)
content := c.getOrderContent(order, store.Tel1) var content string
if isV500(store.PrinterSN) {
content = c.getOrderContent2(order, store.Tel1)
} else {
content = c.getOrderContent(order, store.Tel1)
}
return c.PrintMsg(ctx, store.PrinterSN, store.PrinterKey, order.VendorOrderID, content) return c.PrintMsg(ctx, store.PrinterSN, store.PrinterKey, order.VendorOrderID, content)
} }
func isV500(printerNo string) bool {
printerNoNum := utils.Str2Int64WithDefault("1"+printerNo, 0)
return printerNoNum > 1000000000
}
func (c *PrinterHandler) RegisterPrinter(ctx *jxcontext.Context, printerNumber, notUsed, printerName string) (newID1, printerToken string, err error) { func (c *PrinterHandler) RegisterPrinter(ctx *jxcontext.Context, printerNumber, notUsed, printerName string) (newID1, printerToken string, err error) {
globals.SugarLogger.Debugf("xiaowm RegisterPrinter printerNumber:%s", printerNumber) globals.SugarLogger.Debugf("xiaowm RegisterPrinter printerNumber:%s", printerNumber)
if printerNumber == "" { //len(printerNumber) != len("7JizmSyiXNzkggaqU") { if printerNumber == "" { //len(printerNumber) != len("7JizmSyiXNzkggaqU") {

View File

@@ -33,7 +33,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour) expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
} }
orderFmt := ` orderFmt := `
<FS2><center>京西菜市</center></FS2>\n\n <FS2><center>%s</center></FS2>\n\n
<center>手机买菜上京西</center> <center>手机买菜上京西</center>
<center>极速到家送惊喜</center>\n <center>极速到家送惊喜</center>\n
-------------------------------- --------------------------------
@@ -57,6 +57,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
品名 数量 单价 小计\n 品名 数量 单价 小计\n
--------------------------------\n` --------------------------------\n`
orderParams := []interface{}{ orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt), utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime), utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID, order.VendorOrderID,
@@ -81,12 +82,12 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
--------------------------------\n --------------------------------\n
<center><FH2>商品质量问题请联系:</FH2></center> <center><FH2>商品质量问题请联系:</FH2></center>
<center><FH2>%s:%s</FH2></center>\n <center><FH2>%s:%s</FH2></center>\n
更多信息请关注官方微信: 京西菜市\n 更多信息请关注官方微信: %s\n
--------------------------------\n --------------------------------\n
--------------------------------\n --------------------------------\n
` `
// <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR> // <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR>
orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel) orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel, globals.StoreName)
return strings.Replace(fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...), "\\n", "\r\n", -1) return strings.Replace(fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...), "\\n", "\r\n", -1)
} }

View File

@@ -34,7 +34,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour) expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
} }
orderFmt := ` orderFmt := `
<S2><C>京西菜市</C></S2><RN><RN> <S2><C>%s</C></S2><RN><RN>
<C>手机买菜上京西</C> <C>手机买菜上京西</C>
<C>极速到家送惊喜</C><RN> <C>极速到家送惊喜</C><RN>
******************************** ********************************
@@ -58,6 +58,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
品名 数量 单价 小计<RN> 品名 数量 单价 小计<RN>
********************************<RN>` ********************************<RN>`
orderParams := []interface{}{ orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt), utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime), utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID, order.VendorOrderID,
@@ -82,12 +83,12 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
********************************<RN> ********************************<RN>
<C><H2>商品质量问题请联系:</H2></C> <C><H2>商品质量问题请联系:</H2></C>
<C><H2>%s:%s</H2></C><RN> <C><H2>%s:%s</H2></C><RN>
更多信息请关注官方微信: 京西菜市<RN> 更多信息请关注官方微信: %s<RN>
********************************<RN> ********************************<RN>
********************************<RN> ********************************<RN>
` `
// <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR> // <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR>
orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel) orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel, globals.StoreName)
return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...) return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...)
} }

View File

@@ -0,0 +1,186 @@
package ebai
import (
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
func actType2Ebai(actType int) int {
if actType < model.ActOrderBegin {
return ebaiapi.ActivityTypeDirectDown
}
return ebaiapi.ActivityTypeFullDiscount
}
func actOrderRules2Ebai(actOrderRules []*model.ActOrderRule) (ebaiRules []*ebaiapi.ActivityRule) {
for _, v := range actOrderRules {
ebaiRules = append(ebaiRules, &ebaiapi.ActivityRule{
Accords: int(jxutils.IntPrice2Standard(v.SalePrice)),
Sale: int(jxutils.IntPrice2Standard(v.DeductPrice)),
})
}
return ebaiRules
}
func actStoreSu2Ebai4Add(oneStoreActSku []*model.ActStoreSku2) (skus []*ebaiapi.ActivitySkuInfo4Add) {
for _, v := range oneStoreActSku {
if model.IsSyncStatusNeedCreate(v.SyncStatus) {
skus = append(skus, &ebaiapi.ActivitySkuInfo4Add{
SkuID: v.VendorSkuID,
SpecialPrice: v.ActPrice,
})
}
}
return skus
}
func actStoreSu2Ebai4Update(oneStoreActSku []*model.ActStoreSku2) (skus []*ebaiapi.ActivitySkuInfo4Update) {
for _, v := range oneStoreActSku {
if model.IsSyncStatusNeedUpdate(v.SyncStatus) {
skus = append(skus, &ebaiapi.ActivitySkuInfo4Update{
ShopID: utils.Int2Str(v.StoreID),
SkuID: v.VendorSkuID,
SpecialPrice: v.ActPrice,
})
}
}
return skus
}
func actStoreSu2Ebai4Delete(oneStoreActSku []*model.ActStoreSku2) (skus []string) {
for _, v := range oneStoreActSku {
if model.IsSyncStatusNeedDelete(v.SyncStatus) {
skus = append(skus, v.VendorSkuID)
}
}
return skus
}
func act2EbaiActivity(act *model.Act2, actOrderRules []*model.ActOrderRule) (activity *ebaiapi.ActivityInfo) {
activity = &ebaiapi.ActivityInfo{
ActivityName: act.Name,
ActivityType: actType2Ebai(act.Type),
StartTime: act.BeginAt.Unix(),
EndTime: act.EndAt.Unix(),
ActivityDesc: act.Advertising,
ShowCategory: act.Name,
PromotionSkuDesc: act.Name,
Rule: actOrderRules2Ebai(actOrderRules),
}
return activity
}
func createOneShopAct(shopID string, activity *ebaiapi.ActivityInfo, oneStoreActSku []*model.ActStoreSku2) (ebaiActIDStr string, err error) {
if globals.EnableEbaiStoreWrite {
ebaiActID, err := api.EbaiAPI.ActivityCreate(shopID, 0, 0, activity)
if err == nil {
ebaiActIDStr = utils.Int64ToStr(ebaiActID)
_, err = ActivitySkuAddBatch(ebaiActID, shopID, 0, activity.ActivityType, actStoreSu2Ebai4Add(oneStoreActSku), false)
}
} else {
ebaiActIDStr = utils.Int64ToStr(jxutils.GenFakeID())
}
return ebaiActIDStr, err
}
func ActivitySkuAddBatch(activityID int64, shopID string, baiduShopID int64, activityType int, skuList []*ebaiapi.ActivitySkuInfo4Add, isSkuIDCustom bool) (successIDs []string, err error) {
return api.EbaiAPI.ActivitySkuAddBatch(activityID, shopID, baiduShopID, activityType, skuList, isSkuIDCustom)
}
func ActivitySkuDeleteBatch(activityID int64, shopID string, baiduShopID int64, skuIDs []string, isSkuIDCustom bool) (successIDs []string, err error) {
return api.EbaiAPI.ActivitySkuDeleteBatch(activityID, shopID, baiduShopID, skuIDs, isSkuIDCustom)
}
func ActivitySkuUpdateBatch(activityID int64, actSkuInfoList []*ebaiapi.ActivitySkuInfo4Update) (faildInfoList []string, err error) {
return api.EbaiAPI.ActivitySkuUpdateBatch(activityID, actSkuInfoList)
}
func (c *PurchaseHandler) CreateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
activity := act2EbaiActivity(act, actOrderRules)
if act.Type < model.ActOrderBegin {
actStoreSkuMap := partner.ActStoreSku2Map(actStoreSku)
task := tasksch.NewParallelTask("ebai CreateAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
store := batchItemList[0].(*model.ActStore2)
store.VendorActID, err = createOneShopAct(utils.Int2Str(store.StoreID), activity, actStoreSkuMap[store.StoreID])
return nil, err
}, actStoreMap)
tasksch.HandleTask(task, parentTask, true).Run()
_, err = task.GetResult(0)
} else {
}
return err
}
func (c *PurchaseHandler) UpdateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
if act.Type < model.ActOrderBegin {
actStoreSkuMap := partner.ActStoreSku2Map(actStoreSku)
if len(actStoreMap2Remove) > 0 {
if err = c.CancelAct(ctx, parentTask, act, actStoreMap2Remove, nil); err != nil {
return err
}
for _, v := range actStoreMap2Remove {
delete(actStoreSkuMap, v.StoreID)
}
}
if len(actStoreMap2Add) > 0 {
if err = c.CreateAct(ctx, parentTask, act, actOrderRules, actStoreMap2Add, actStoreSku); err != nil {
return err
}
for _, v := range actStoreMap2Add {
delete(actStoreSkuMap, v.StoreID)
}
}
task := tasksch.NewParallelTask("ebai UpdateAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
v := batchItemList[0].(*model.ActStore2)
if storeSkus := actStoreSkuMap[v.StoreID]; storeSkus != nil {
if list := actStoreSu2Ebai4Delete(storeSkus); len(list) > 0 {
if _, err = ActivitySkuDeleteBatch(utils.Str2Int64(v.VendorActID), utils.Int2Str(v.StoreID), 0, list, false); err != nil {
return nil, err
}
}
if list := actStoreSu2Ebai4Add(storeSkus); len(list) > 0 {
if _, err = ActivitySkuAddBatch(utils.Str2Int64(v.VendorActID), utils.Int2Str(v.StoreID), 0, actType2Ebai(act.Type), list, false); err != nil {
return nil, err
}
}
if list := actStoreSu2Ebai4Update(storeSkus); len(list) > 0 {
if _, err = ActivitySkuUpdateBatch(utils.Str2Int64(v.VendorActID), list); err != nil {
return nil, err
}
}
}
return nil, err
}, actStoreMap2Update)
tasksch.HandleTask(task, parentTask, true).Run()
_, err = task.GetResult(0)
} else {
}
return err
}
func (c *PurchaseHandler) CancelAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
if act.Type < model.ActOrderBegin {
task := tasksch.NewParallelTask("ebai DeleteAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
v := batchItemList[0].(*model.ActStore2)
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ActivityDisable(utils.Str2Int64(v.VendorActID), utils.Int2Str(v.StoreID), 0, 0)
}
return nil, err
}, actStoreMap)
tasksch.HandleTask(task, parentTask, true).Run()
_, err = task.GetResult(0)
}
return err
}

View File

@@ -19,10 +19,8 @@ func OnCallbackMsg(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse
} }
}, jxutils.ComposeUniversalOrderID(orderID, model.VendorIDEBAI)) }, jxutils.ComposeUniversalOrderID(orderID, model.VendorIDEBAI))
} }
if msg.Cmd == ebaiapi.CmdOrderPartRefund || msg.Cmd == ebaiapi.CmdOrderUserCancel || msg.Cmd == ebaiapi.CmdOrderDeliveryStatus { if /*msg.Cmd == ebaiapi.CmdOrderPartRefund || msg.Cmd == ebaiapi.CmdOrderUserCancel || */ msg.Cmd == ebaiapi.CmdOrderDeliveryStatus {
utils.CallFuncAsync(func() { response = CurPurchaseHandler.OnFinancialMsg(msg)
OnFinancialMsg(msg)
})
} else if msg.Cmd == ebaiapi.CmdShopMsgPush { } else if msg.Cmd == ebaiapi.CmdShopMsgPush {
response = CurPurchaseHandler.onShopMsgPush(msg) response = CurPurchaseHandler.onShopMsgPush(msg)
} }
@@ -31,7 +29,11 @@ func OnCallbackMsg(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse
} }
func GetOrderIDFromMsg(msg *ebaiapi.CallbackMsg) string { func GetOrderIDFromMsg(msg *ebaiapi.CallbackMsg) string {
if orderID := msg.Body["order_id"]; orderID != nil { return GetOrderIDFromMap(msg.Body)
}
func GetOrderIDFromMap(orderMap map[string]interface{}) string {
if orderID := orderMap["order_id"]; orderID != nil {
if tryOrderID, ok := orderID.(string); ok { if tryOrderID, ok := orderID.(string); ok {
return tryOrderID return tryOrderID
} }

View File

@@ -27,7 +27,7 @@ func init() {
func EbaiBusStatus2JxStatus(ebaiStatus int) int { func EbaiBusStatus2JxStatus(ebaiStatus int) int {
if ebaiStatus == ebaiapi.ShopBusStatusHaveRest || ebaiStatus == ebaiapi.ShopBusStatusSuspended { if ebaiStatus == ebaiapi.ShopBusStatusHaveRest || ebaiStatus == ebaiapi.ShopBusStatusSuspended {
return model.StoreStatusClosed return model.StoreStatusHaveRest
} }
return model.StoreStatusOpened return model.StoreStatusOpened
} }

View File

@@ -9,25 +9,33 @@ import (
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
) )
func (p *PurchaseHandler) OnFinancialMsg(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse) {
utils.CallFuncAsync(func() {
response = p.onFinancialMsg(msg)
})
return response
}
// 存储饿百退款订单结账信息 // 存储饿百退款订单结账信息
func OnFinancialMsg(msg *ebaiapi.CallbackMsg) (err error) { func (p *PurchaseHandler) onFinancialMsg(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse) {
var err error
if msg.Cmd == ebaiapi.CmdOrderPartRefund { // 部分退款处理 if msg.Cmd == ebaiapi.CmdOrderPartRefund { // 部分退款处理
if utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["status"])) == ebaiapi.OrderPartRefundSuccess { if int(utils.MustInterface2Int64(msg.Body["status"])) == ebaiapi.OrderPartRefundSuccess {
// 获取到部分退款订单id // 获取到部分退款订单id
afsOrderID := utils.Interface2String(msg.Body["order_id"]) afsOrderID := GetOrderIDFromMsg(msg)
// 处理部分退款信息 // 处理部分退款信息
orderData, err2 := api.EbaiAPI.OrderPartrefundGet(afsOrderID) orderData, err2 := api.EbaiAPI.OrderPartRefundGet(afsOrderID)
if err = err2; err == nil { if err = err2; err == nil {
afsOrder := CurPurchaseHandler.AfsOrderDetail2Financial(orderData) afsOrder := CurPurchaseHandler.AfsOrderDetail2Financial(orderData)
err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(afsOrder) err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(afsOrder)
} }
} }
} else if msg.Cmd == ebaiapi.CmdOrderUserCancel { // 全额退款处理 } else if msg.Cmd == ebaiapi.CmdOrderUserCancel { // 全额退款处理
messageType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"])) messageType := int(utils.MustInterface2Int64(msg.Body["type"]))
if utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["cancel_type"])) == ebaiapi.OrderUserCancelTypeAfterSale && if int(utils.MustInterface2Int64(msg.Body["cancel_type"])) == ebaiapi.OrderUserCancelTypeAfterSale &&
(messageType == ebaiapi.OrderUserCancelCSAgreed || messageType == ebaiapi.OrderUserCancelMerchantAgreed) { (messageType == ebaiapi.OrderUserCancelCSAgreed || messageType == ebaiapi.OrderUserCancelMerchantAgreed) {
globals.SugarLogger.Debug(utils.Interface2String(msg.Body["order_id"])) // 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号但是没有查询全额退款的接口只有部分退款才可以查询 afsOrderID := GetOrderIDFromMsg(msg)
afsOrderID := utils.Interface2String(msg.Body["order_id"]) // 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号但是没有查询全额退款的接口只有部分退款才可以查询
orderFinancial, err := partner.CurOrderManager.LoadOrderFinancial(afsOrderID, model.VendorIDEBAI) orderFinancial, err := partner.CurOrderManager.LoadOrderFinancial(afsOrderID, model.VendorIDEBAI)
if err == nil { if err == nil {
globals.SugarLogger.Debug(utils.Format4Output(orderFinancial, false)) globals.SugarLogger.Debug(utils.Format4Output(orderFinancial, false))
@@ -45,15 +53,15 @@ func OnFinancialMsg(msg *ebaiapi.CallbackMsg) (err error) {
} }
} }
} }
return err return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, msg.Cmd)
} }
func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.OrderFinancial, msg *ebaiapi.CallbackMsg) (afsOrder *model.AfsOrder) { func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.OrderFinancial, msg *ebaiapi.CallbackMsg) (afsOrder *model.AfsOrder) {
afsOrder = &model.AfsOrder{ afsOrder = &model.AfsOrder{
VendorID: model.VendorIDEBAI, VendorID: model.VendorIDEBAI,
AfsOrderID: utils.Interface2String(msg.Body["order_id"]), AfsOrderID: GetOrderIDFromMsg(msg),
VendorOrderID: utils.Interface2String(msg.Body["order_id"]), VendorOrderID: GetOrderIDFromMsg(msg),
AfsCreateAt: utils.Timestamp2Time(msg.Timestamp), AfsCreatedAt: utils.Timestamp2Time(msg.Timestamp),
// BoxMoney: orderFinancial.BoxMoney, // 饿百的餐盒费已经拆分到单条Sku里面退款时直接计算用户支付sku金额就好了 // BoxMoney: orderFinancial.BoxMoney, // 饿百的餐盒费已经拆分到单条Sku里面退款时直接计算用户支付sku金额就好了
// SkuBoxMoney: orderFinancial.SkuBoxMoney, // SkuBoxMoney: orderFinancial.SkuBoxMoney,
FreightUserMoney: orderFinancial.FreightMoney, FreightUserMoney: orderFinancial.FreightMoney,
@@ -80,7 +88,6 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
orderSkuFinancial := &model.OrderSkuFinancial{ orderSkuFinancial := &model.OrderSkuFinancial{
VendorID: sku.VendorID, VendorID: sku.VendorID,
VendorOrderID: sku.VendorOrderID, VendorOrderID: sku.VendorOrderID,
VendorOrderID2: sku.VendorOrderID2,
AfsOrderID: sku.VendorOrderID, AfsOrderID: sku.VendorOrderID,
// ConfirmTime: afsOrder.AfsCreateAt, // ConfirmTime: afsOrder.AfsCreateAt,
VendorStoreID: afsOrder.VendorStoreID, VendorStoreID: afsOrder.VendorStoreID,
@@ -106,8 +113,8 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interface{}) (afsOrder *model.AfsOrder) { func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interface{}) (afsOrder *model.AfsOrder) {
afsOrder = &model.AfsOrder{ afsOrder = &model.AfsOrder{
VendorID: model.VendorIDEBAI, VendorID: model.VendorIDEBAI,
AfsOrderID: utils.Interface2String(orderData["order_id"]), AfsOrderID: GetOrderIDFromMap(orderData),
VendorOrderID: utils.Interface2String(orderData["order_id"]), VendorOrderID: GetOrderIDFromMap(orderData),
} }
order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID) order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID)
if err == nil { if err == nil {
@@ -125,7 +132,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interfac
afsOrder.PmRefundMoney = orderFinancial.PmMoney - utils.MustInterface2Int64(orderData["commission"]) afsOrder.PmRefundMoney = orderFinancial.PmMoney - utils.MustInterface2Int64(orderData["commission"])
} else { } else {
// 此处应该报错 // 此处应该报错
globals.SugarLogger.Warnf("ebai AfsOrderDetail2Financial, afsOrderID:%s is not found from partner.CurOrderManager.LoadOrderFinancial", afsOrder.VendorOrderID) // globals.SugarLogger.Warnf("ebai AfsOrderDetail2Financial, afsOrderID:%s is not found from partner.CurOrderManager.LoadOrderFinancial", afsOrder.VendorOrderID)
err = nil err = nil
} }
if orderData["refund_detail"] != nil { if orderData["refund_detail"] != nil {
@@ -149,7 +156,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interfac
afsOrder.PmSubsidyMoney += orderSkuFinancial.PmSubsidyMoney afsOrder.PmSubsidyMoney += orderSkuFinancial.PmSubsidyMoney
} }
if len(refundDetail) > 0 { if len(refundDetail) > 0 {
afsOrder.AfsCreateAt = getTimeFromInterface(refundDetail[0].(map[string]interface{})["apply_time"]) afsOrder.AfsCreatedAt = getTimeFromInterface(refundDetail[0].(map[string]interface{})["apply_time"])
} else { } else {
globals.SugarLogger.Warnf("ebai AfsOrderDetail2Financial, orderID:%s have no refund_detail", afsOrder.VendorOrderID) globals.SugarLogger.Warnf("ebai AfsOrderDetail2Financial, orderID:%s have no refund_detail", afsOrder.VendorOrderID)
} }
@@ -168,7 +175,7 @@ func (p *PurchaseHandler) OnOrderDetail(result map[string]interface{}, operation
// func (p *PurchaseHandler) GetTrueEbaiOrder(result1 map[string]interface{}) (result2 map[string]interface{}) { // func (p *PurchaseHandler) GetTrueEbaiOrder(result1 map[string]interface{}) (result2 map[string]interface{}) {
// order := result1["order"].(map[string]interface{}) // order := result1["order"].(map[string]interface{})
// if utils.MustInterface2Int64(order["down_flag"]) == 1 { // if utils.MustInterface2Int64(order["down_flag"]) == 1 {
// result, err := api.EbaiAPI.OrderGet(utils.Interface2String(order["order_id"])) // result, err := api.EbaiAPI.OrderGet(GetOrderIDFromMap(order))
// if err == nil { // if err == nil {
// return p.GetTrueEbaiOrder(result) // return p.GetTrueEbaiOrder(result)
// } // }
@@ -181,7 +188,7 @@ func (p *PurchaseHandler) OrderDetail2Financial(result map[string]interface{}) (
VendorID: model.VendorIDEBAI, VendorID: model.VendorIDEBAI,
} }
order1 := result["order"].(map[string]interface{}) order1 := result["order"].(map[string]interface{})
orderFinancial.VendorOrderID = utils.Interface2String(order1["order_id"]) orderFinancial.VendorOrderID = GetOrderIDFromMap(order1)
orderFinancial.VendorOrderID2 = utils.Interface2String(order1["eleme_order_id"]) orderFinancial.VendorOrderID2 = utils.Interface2String(order1["eleme_order_id"])
// orderFinancial.DeliveryConfirmTime = getTimeFromInterface(order1["finished_time"]) // orderFinancial.DeliveryConfirmTime = getTimeFromInterface(order1["finished_time"])
orderFinancial.TotalDiscountMoney = utils.MustInterface2Int64(order1["discount_fee"]) orderFinancial.TotalDiscountMoney = utils.MustInterface2Int64(order1["discount_fee"])
@@ -226,7 +233,6 @@ func (p *PurchaseHandler) OrderDetail2Financial(result map[string]interface{}) (
orderSkuFinancial := &model.OrderSkuFinancial{ orderSkuFinancial := &model.OrderSkuFinancial{
VendorID: orderFinancial.VendorID, VendorID: orderFinancial.VendorID,
VendorOrderID: orderFinancial.VendorOrderID, VendorOrderID: orderFinancial.VendorOrderID,
VendorOrderID2: orderFinancial.VendorOrderID2,
// OrderFinancialID: orderFinancial.VendorOrderID, // OrderFinancialID: orderFinancial.VendorOrderID,
// ConfirmTime: getTimeFromInterface(order1["create_time"]), // ConfirmTime: getTimeFromInterface(order1["create_time"]),
VendorStoreID: utils.Interface2String(shop["baidu_shop_id"]), VendorStoreID: utils.Interface2String(shop["baidu_shop_id"]),

View File

@@ -27,7 +27,7 @@ func TestOnFinancialMsg(t *testing.T) {
// msg.Body["type"] = json.Number("40") // msg.Body["type"] = json.Number("40")
// msg.Body["cancel_type"] = json.Number("2") // msg.Body["cancel_type"] = json.Number("2")
res := OnFinancialMsg(msg) res := CurPurchaseHandler.onAfsOrderMsg(msg)
fmt.Println(res) fmt.Println(res)
} }

View File

@@ -4,6 +4,8 @@ import (
"math" "math"
"time" "time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/baseapi/platformapi/autonavi" "git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi" "git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
@@ -19,7 +21,8 @@ import (
const ( const (
// acceptOrderDelay = 180 * time.Second // acceptOrderDelay = 180 * time.Second
pickupOrderDelay = 260 * time.Second // pickupOrderDelay = 260 * time.Second
pickupOrderDelay = 1 * time.Second
callDeliveryDelay = 10 * time.Minute callDeliveryDelay = 10 * time.Minute
callDeliveryDelayGap = 30 callDeliveryDelayGap = 30
@@ -34,6 +37,7 @@ const (
var ( var (
VendorStatus2StatusMap = map[string]int{ VendorStatus2StatusMap = map[string]int{
ebaiapi.CmdOrderCreate: model.OrderStatusNew,
ebaiapi.OrderStatusNew: model.OrderStatusNew, ebaiapi.OrderStatusNew: model.OrderStatusNew,
fakeAcceptOrder: model.OrderStatusAccepted, fakeAcceptOrder: model.OrderStatusAccepted,
ebaiapi.OrderStatusAccepted: model.OrderStatusFinishedPickup, ebaiapi.OrderStatusAccepted: model.OrderStatusFinishedPickup,
@@ -46,6 +50,11 @@ var (
fakeUserApplyCancel: model.OrderStatusApplyCancel, fakeUserApplyCancel: model.OrderStatusApplyCancel,
fakeUserUndoApplyCancel: model.OrderStatusUndoApplyCancel, fakeUserUndoApplyCancel: model.OrderStatusUndoApplyCancel,
} }
skuActTypeMap = map[string]int{
ebaiapi.OrderSkuDiscountTypeZhe: 1,
ebaiapi.OrderSkuDiscountTypeReduce: 1,
}
) )
func (p *PurchaseHandler) GetStatusFromVendorStatus(vendorStatus string) int { func (p *PurchaseHandler) GetStatusFromVendorStatus(vendorStatus string) int {
@@ -68,6 +77,65 @@ func (p *PurchaseHandler) getOrder(vendorOrderID string) (order *model.GoodsOrde
return order, result, err return order, result, err
} }
func (p *PurchaseHandler) GetOrder4PartRefund(vendorOrderID string) (order *model.GoodsOrder, err error) {
taskIDs := []int{1, 2}
var (
err1, err2 error
result1, result2 map[string]interface{}
)
task := tasksch.NewParallelTask("GetOrder4PartRefund", nil, jxcontext.AdminCtx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
taskID := batchItemList[0].(int)
if taskID == 1 {
result1, err1 = api.EbaiAPI.OrderGet(vendorOrderID)
} else if taskID == 2 {
result2, err2 = api.EbaiAPI.OrderPartRefundGet(vendorOrderID)
}
return nil, nil
}, taskIDs)
task.Run()
task.GetResult(0)
if err1 == nil {
order = p.Map2Order(result1)
if err2 == nil {
order.Skus = p.partRefund2OrderDetailSkuList(utils.Interface2String(result2["order_id"]), result2["order_detail"])
order.ActualPayPrice = utils.MustInterface2Int64(result2["user_fee"])
jxutils.RefreshOrderSkuRelated(order)
} else if err2Ext, ok := err2.(*utils.ErrorWithCode); !ok || err2Ext.IntCode() != ebaiapi.ErrOrderIsNotPartRefund {
err = err2
}
} else {
err = err1
}
return order, err
}
func (p *PurchaseHandler) partRefund2OrderDetailSkuList(orderID string, orderDetail2 interface{}) (skuList []*model.OrderSku) {
orderDetail := orderDetail2.([]interface{})
for _, product2 := range orderDetail {
product := product2.(map[string]interface{})
skuName := product["name"].(string)
_, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName)
number := int(utils.MustInterface2Int64(product["number"]))
sku := &model.OrderSku{
VendorOrderID: orderID,
VendorID: model.VendorIDEBAI,
Count: number,
SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)),
VendorSkuID: utils.Interface2String(product[ebaiapi.KeySkuID]),
SkuName: skuName,
// Weight: int(utils.Interface2Int64WithDefault(product["total_weight"], 0)) / number, // 退单这里的total_weight有BUG这里的total_weight还是没有退单时的值
VendorPrice: utils.MustInterface2Int64(product["product_price"]),
}
sku.SalePrice, _, sku.StoreSubName = getSkuSalePrice(product)
if sku.Weight == 0 {
sku.Weight = jxutils.FormatSkuWeight(specQuality, specUnit) // 订单信息里没有重量,只有名字里尝试找
}
skuList = append(skuList, sku)
}
return skuList
}
func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) { func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) {
result := orderData result := orderData
shopMap := result["shop"].(map[string]interface{}) shopMap := result["shop"].(map[string]interface{})
@@ -93,7 +161,7 @@ func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
StatusTime: getTimeFromInterface(orderMap["create_time"]), StatusTime: getTimeFromInterface(orderMap["create_time"]),
OriginalData: string(utils.MustMarshal(result)), OriginalData: string(utils.MustMarshal(result)),
ActualPayPrice: utils.MustInterface2Int64(orderMap["user_fee"]), ActualPayPrice: utils.MustInterface2Int64(orderMap["user_fee"]),
Skus: []*model.OrderSku{}, TotalShopMoney: utils.MustInterface2Int64(orderMap["shop_fee"]),
} }
if utils.IsTimeZero(order.PickDeadline) && !utils.IsTimeZero(order.StatusTime) { if utils.IsTimeZero(order.PickDeadline) && !utils.IsTimeZero(order.StatusTime) {
order.PickDeadline = order.StatusTime.Add(pickupOrderDelay) // 饿百要求在5分钟内拣货不然订单会被取消 order.PickDeadline = order.StatusTime.Add(pickupOrderDelay) // 饿百要求在5分钟内拣货不然订单会被取消
@@ -134,33 +202,58 @@ func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
product := product2.(map[string]interface{}) product := product2.(map[string]interface{})
skuName := product["product_name"].(string) skuName := product["product_name"].(string)
_, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName) _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName)
productAmount := int(utils.MustInterface2Int64(product["product_amount"]))
sku := &model.OrderSku{ sku := &model.OrderSku{
VendorOrderID: order.VendorOrderID, VendorOrderID: order.VendorOrderID,
VendorID: model.VendorIDEBAI, VendorID: model.VendorIDEBAI,
Count: int(utils.MustInterface2Int64(product["product_amount"])), Count: productAmount,
SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)), SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)),
VendorSkuID: utils.Interface2String(product["baidu_product_id"]), VendorSkuID: utils.Interface2String(product["baidu_product_id"]),
SkuName: skuName, SkuName: skuName,
Weight: jxutils.FormatSkuWeight(specQuality, specUnit), // 订单信息里没有重量,只有名字里尝试找 Weight: int(utils.Interface2Int64WithDefault(product["total_weight"], 0)) / productAmount,
SalePrice: utils.MustInterface2Int64(product["product_price"]), VendorPrice: utils.MustInterface2Int64(product["product_price"]),
// PromotionType: int(utils.MustInterface2Int64(product["promotionType"])),
} }
var baiduRate int64
sku.SalePrice, baiduRate, sku.StoreSubName = getSkuSalePrice(product)
order.PmSubsidyMoney += baiduRate
if sku.Weight == 0 { if sku.Weight == 0 {
sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值 sku.Weight = jxutils.FormatSkuWeight(specQuality, specUnit) // 订单信息里没有重量,只有名字里尝试找
} }
// if product["isGift"].(bool) { // if product["isGift"].(bool) {
// sku.SkuType = 1 // sku.SkuType = 1
// } // }
order.Skus = append(order.Skus, sku) order.Skus = append(order.Skus, sku)
order.SkuCount++
order.GoodsCount += sku.Count
order.SalePrice += sku.SalePrice * int64(sku.Count)
order.Weight += sku.Weight * sku.Count
} }
// setOrederDetailFee(result, order) jxutils.RefreshOrderSkuRelated(order)
return order return order
} }
func getSkuSalePrice(product map[string]interface{}) (salePrice, baiduRate int64, vendorActType string) {
var product2 *ebaiapi.OrderProductInfo
if err := utils.Map2StructByJson(product, &product2, true); err != nil {
return utils.MustInterface2Int64(product["product_price"]), 0, ""
}
return getSkuSalePrice2(product2)
}
func getSkuSalePrice2(product *ebaiapi.OrderProductInfo) (salePrice, baiduRate int64, vendorActType string) {
salePrice = int64(product.ProductPrice)
if product.ProductSubsidy != nil {
for _, v := range product.ProductSubsidy.DiscountDetail {
if skuActTypeMap[v.Type] == 1 {
skuCount := product.ProductAmount
if skuCount == 0 {
skuCount = product.Number
}
salePrice -= int64(math.Round(float64(v.BaiduRate+v.ShopRate) / float64(skuCount))) // 饿百同一SKU的优惠与非优惠没有拆开平均摊销处理
vendorActType = v.Type
}
baiduRate += int64(v.BaiduRate)
}
}
return salePrice, baiduRate, vendorActType
}
func (p *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { func (p *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) {
globals.SugarLogger.Debugf("ebai AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt) globals.SugarLogger.Debugf("ebai AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt)
if isAcceptIt { if isAcceptIt {
@@ -239,24 +332,38 @@ func (p *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName
// //
func (c *PurchaseHandler) onOrderMsg(msg *ebaiapi.CallbackMsg) (retVal *ebaiapi.CallbackResponse) { func (c *PurchaseHandler) onOrderMsg(msg *ebaiapi.CallbackMsg) (retVal *ebaiapi.CallbackResponse) {
if ebaiapi.CmdOrderCreate == msg.Cmd { if c.isAfsMsg(msg) {
retVal = c.onOrderNew(msg) retVal = c.OnAfsOrderMsg(msg)
} else { } else {
status := c.callbackMsg2Status(msg) status := c.callbackMsg2Status(msg)
if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 {
return nil
}
if ebaiapi.CmdOrderCreate == msg.Cmd {
retVal = c.onOrderNew(msg, status)
} else {
var err error var err error
if status != nil { if status != nil {
if status.Status == model.OrderStatusAdjust {
var order *model.GoodsOrder
if order, err = c.GetOrder4PartRefund(GetOrderIDFromMsg(msg)); err == nil {
err = partner.CurOrderManager.OnOrderAdjust(order, status)
}
} else {
err = partner.CurOrderManager.OnOrderStatusChanged(status) err = partner.CurOrderManager.OnOrderStatusChanged(status)
} }
}
retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil) retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil)
} }
}
return retVal return retVal
} }
func (c *PurchaseHandler) onOrderNew(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse) { func (c *PurchaseHandler) onOrderNew(msg *ebaiapi.CallbackMsg, orderStatus *model.OrderStatus) (response *ebaiapi.CallbackResponse) {
vendorOrderID := GetOrderIDFromMsg(msg) vendorOrderID := GetOrderIDFromMsg(msg)
order, orderMap, err := c.getOrder(vendorOrderID) order, orderMap, err := c.getOrder(vendorOrderID)
if err == nil { if err == nil {
if err = partner.CurOrderManager.OnOrderNew(order, order.VendorStatus); err == nil { if err = partner.CurOrderManager.OnOrderNew(order, orderStatus); err == nil {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
c.OnOrderDetail(orderMap, partner.CreatedPeration) c.OnOrderDetail(orderMap, partner.CreatedPeration)
}) })
@@ -279,27 +386,24 @@ func (c *PurchaseHandler) callbackMsg2Status(msg *ebaiapi.CallbackMsg) (orderSta
VendorStatus: msg.Cmd, VendorStatus: msg.Cmd,
} }
if msg.Cmd == ebaiapi.CmdOrderUserCancel { if msg.Cmd == ebaiapi.CmdOrderUserCancel {
msgType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"])) msgType := int(utils.MustInterface2Int64(msg.Body["type"]))
cancelType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["cancel_type"])) cancelType := int(utils.MustInterface2Int64(msg.Body["cancel_type"]))
orderStatus.Remark = utils.Interface2String(msg.Body["cancel_reason"]) orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["cancel_reason"]), utils.Interface2String(msg.Body["addition_reason"]))
orderStatus.VendorStatus = msg.Cmd + "-" + msgType orderStatus.VendorStatus = msg.Cmd + "-" + utils.Int2Str(msgType)
if additionReason := utils.Interface2String(msg.Body["addition_reason"]); additionReason != "" {
orderStatus.Remark += ",额外原因:" + additionReason
}
if cancelType == ebaiapi.OrderUserCancelTypeBeforeSale { if cancelType == ebaiapi.OrderUserCancelTypeBeforeSale {
if msgType == ebaiapi.OrderUserCancelApply { if msgType == ebaiapi.OrderUserCancelApply ||
msgType == ebaiapi.OrderUserCancelCSIntervene {
orderStatus.VendorStatus = fakeUserApplyCancel orderStatus.VendorStatus = fakeUserApplyCancel
} else if msgType == ebaiapi.OrderUserCancelInvalid { } else if msgType == ebaiapi.OrderUserCancelInvalid ||
msgType == ebaiapi.OrderUserCancelMerchantRefused ||
msgType == ebaiapi.OrderUserCancelCSRefused {
orderStatus.VendorStatus = fakeUserUndoApplyCancel orderStatus.VendorStatus = fakeUserUndoApplyCancel
} }
} }
} else if msg.Cmd == ebaiapi.CmdOrderPartRefund { } else if msg.Cmd == ebaiapi.CmdOrderPartRefund {
msgType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"])) msgType := int(utils.MustInterface2Int64(msg.Body["type"]))
status := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["status"])) status := int(utils.MustInterface2Int64(msg.Body["status"]))
orderStatus.Remark = utils.Interface2String(msg.Body["reason"]) orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["reason"]), utils.Interface2String(msg.Body["addition_reason"]))
if additionReason := utils.Interface2String(msg.Body["addition_reason"]); additionReason != "" {
orderStatus.Remark += ",额外原因:" + additionReason
}
if msgType == ebaiapi.OrderPartRefuncTypeMerchant && status == ebaiapi.OrderPartRefundSuccess { if msgType == ebaiapi.OrderPartRefuncTypeMerchant && status == ebaiapi.OrderPartRefundSuccess {
orderStatus.VendorStatus = fakeOrderAdjustFinished orderStatus.VendorStatus = fakeOrderAdjustFinished
} }
@@ -315,6 +419,14 @@ func (c *PurchaseHandler) callbackMsg2Status(msg *ebaiapi.CallbackMsg) (orderSta
return orderStatus return orderStatus
} }
func buildFullReason(reason, addReason string) (fullReason string) {
fullReason = reason
if addReason != "" {
fullReason += ",额外原因:" + addReason
}
return fullReason
}
func (c *PurchaseHandler) GetStatusActionTimeout(order *model.GoodsOrder, statusType, status int) (params *partner.StatusActionParams) { func (c *PurchaseHandler) GetStatusActionTimeout(order *model.GoodsOrder, statusType, status int) (params *partner.StatusActionParams) {
if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted { if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted {
params = &partner.StatusActionParams{ // PickDeadline没有设置时才有效饿百要求在5分钟内拣货不然订单会被取消 params = &partner.StatusActionParams{ // PickDeadline没有设置时才有效饿百要求在5分钟内拣货不然订单会被取消
@@ -398,6 +510,11 @@ func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.Goods
} }
func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) {
// 饿百必须要确认订单后才能调整单
if order.Status < model.OrderStatusFinishedPickup {
err = c.PickupGoods(order, false, ctx.GetUserName())
}
if err == nil {
var skuList []*ebaiapi.RefundSku var skuList []*ebaiapi.RefundSku
for _, sku := range removedSkuList { for _, sku := range removedSkuList {
skuList = append(skuList, &ebaiapi.RefundSku{ skuList = append(skuList, &ebaiapi.RefundSku{
@@ -408,5 +525,6 @@ func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.Goods
if globals.EnableEbaiStoreWrite { if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.OrderPartRefund(order.VendorOrderID, skuList) err = api.EbaiAPI.OrderPartRefund(order.VendorOrderID, skuList)
} }
}
return err return err
} }

View File

@@ -0,0 +1,189 @@
package ebai
import (
"fmt"
"strings"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
var (
AfsVendorStatus2Status4PartRefundMap = map[int]int{
ebaiapi.OrderPartRefundApply: model.AfsOrderStatusWait4Approve,
ebaiapi.OrderPartRefundSuccess: model.AfsOrderStatusFinished,
ebaiapi.OrderPartRefundUserApplyArbitration: model.OrderStatusUnknown, // 是否是中间状态
ebaiapi.OrderPartRefundFailed: model.AfsOrderStatusFailed,
ebaiapi.OrderPartRefundMerchantRefused: model.AfsOrderStatusFailed, // 是否是中间状态
}
AfsVendorStatus2Status4UserCancel = map[int]int{
ebaiapi.OrderUserCancelApply: model.AfsOrderStatusWait4Approve,
ebaiapi.OrderUserCancelCSIntervene: model.OrderStatusUnknown,
ebaiapi.OrderUserCancelCSRefused: model.AfsOrderStatusFailed,
ebaiapi.OrderUserCancelCSAgreed: model.AfsOrderStatusFinished,
ebaiapi.OrderUserCancelMerchantRefused: model.AfsOrderStatusFailed,
ebaiapi.OrderUserCancelMerchantAgreed: model.AfsOrderStatusFinished,
ebaiapi.OrderUserCancelInvalid: model.AfsOrderStatusFailed,
}
)
func (c *PurchaseHandler) isAfsMsg(msg *ebaiapi.CallbackMsg) bool {
if msg.Cmd == ebaiapi.CmdOrderPartRefund {
msgType := int(utils.MustInterface2Int64(msg.Body["type"]))
return msgType == ebaiapi.OrderPartRefuncTypeCustomer
} else if msg.Cmd == ebaiapi.CmdOrderUserCancel {
cancelType := int(utils.MustInterface2Int64(msg.Body["cancel_type"]))
return cancelType == ebaiapi.OrderUserCancelTypeAfterSale
}
return false
}
func (c *PurchaseHandler) OnAfsOrderMsg(msg *ebaiapi.CallbackMsg) (retVal *ebaiapi.CallbackResponse) {
jxutils.CallMsgHandlerAsync(func() {
retVal = c.onAfsOrderMsg(msg)
}, jxutils.ComposeUniversalOrderID(GetOrderIDFromMsg(msg), model.VendorIDEBAI))
return retVal
}
func (c *PurchaseHandler) onAfsOrderMsg(msg *ebaiapi.CallbackMsg) (retVal *ebaiapi.CallbackResponse) {
if orderStatus := c.callbackAfsMsg2Status(msg); orderStatus != nil {
var err error
if orderStatus.Status == model.AfsOrderStatusWait4Approve || orderStatus.Status == model.AfsOrderStatusNew {
var afsOrder *model.AfsOrder
if msg.Cmd == ebaiapi.CmdOrderPartRefund {
partRefundData := msg.Data.(*ebaiapi.CBPartRefundInfo)
afsOrder = &model.AfsOrder{
VendorID: model.VendorIDEBAI,
AfsOrderID: orderStatus.VendorOrderID,
VendorOrderID: orderStatus.RefVendorOrderID,
VendorStoreID: "",
StoreID: 0,
AfsCreatedAt: utils.Timestamp2Time(msg.Timestamp),
VendorAppealType: "",
AppealType: model.AfsAppealTypeRefund,
VendorReasonType: partRefundData.ReasonType,
ReasonType: c.convertAfsReasonType(partRefundData.ReasonType),
ReasonDesc: utils.LimitUTF8StringLen(buildFullReason(partRefundData.Reason, partRefundData.AdditionReason), 1024),
ReasonImgList: utils.LimitUTF8StringLen(strings.Join(partRefundData.Photos, ","), 1024),
RefundType: model.AfsTypePartRefund,
// FreightUserMoney: afsInfo.OrderFreightMoney,
// AfsFreightMoney: afsInfo.AfsFreight,
// BoxMoney: afsInfo.PackagingMoney,
// TongchengFreightMoney: afsInfo.TongchengFreightMoney,
// SkuBoxMoney: afsInfo.MealBoxMoney,
}
for _, sku := range partRefundData.RefundProducts {
orderSku := &model.OrderSkuFinancial{
// VendorID: model.VendorIDEBAI,
// AfsOrderID: afsOrder.AfsOrderID,
// VendorOrderID: afsOrder.VendorOrderID,
// VendorStoreID: afsOrder.VendorStoreID,
// StoreID: afsOrder.StoreID,
// IsAfsOrder: 1,
Count: sku.Number,
// ConfirmTime: afsOrder.AfsCreateAt,
VendorSkuID: sku.SkuID,
SkuID: int(utils.Str2Int64WithDefault(sku.CustomSkuID, 0)),
Name: sku.Name,
UserMoney: sku.TotalRefund,
PmSkuSubsidyMoney: sku.ShopEleRefund,
}
afsOrder.SkuUserMoney += orderSku.UserMoney
afsOrder.PmSubsidyMoney += orderSku.PmSubsidyMoney
afsOrder.Skus = append(afsOrder.Skus, orderSku)
}
} else if msg.Cmd == ebaiapi.CmdOrderUserCancel {
if orderFinancial, err2 := partner.CurOrderManager.LoadOrderFinancial(orderStatus.RefVendorOrderID, model.VendorIDEBAI); err2 == nil {
afsOrder = c.OrderFinancialDetail2Refund(orderFinancial, msg)
cancelData := msg.Data.(*ebaiapi.CBUserCancelInfo)
afsOrder.AfsOrderID = orderStatus.VendorOrderID
afsOrder.RefundType = model.AfsTypeFullRefund
afsOrder.AppealType = model.AfsAppealTypeRefund
afsOrder.VendorReasonType = ""
afsOrder.ReasonType = model.AfsReasonNotOthers
afsOrder.ReasonDesc = utils.LimitUTF8StringLen(buildFullReason(cancelData.CancelReason, cancelData.AdditionReason), 1024)
afsOrder.ReasonImgList = utils.LimitUTF8StringLen(strings.Join(cancelData.Pictures, ","), 1024)
}
}
if afsOrder != nil {
err = partner.CurOrderManager.OnAfsOrderNew(afsOrder, orderStatus)
}
} else {
err = partner.CurOrderManager.OnAfsOrderStatusChanged(orderStatus)
}
retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil)
}
return retVal
}
func (c *PurchaseHandler) convertAfsReasonType(vendorReasonType string) int8 {
return model.AfsReasonNotOthers
}
func (c *PurchaseHandler) GetAfsStatusFromVendorStatus4PartRefund(vendorStatus int) int {
return AfsVendorStatus2Status4PartRefundMap[vendorStatus]
}
func (c *PurchaseHandler) GetAfsStatusFromVendorStatus4UserCancel(vendorStatus int) int {
return AfsVendorStatus2Status4UserCancel[vendorStatus]
}
func (c *PurchaseHandler) callbackAfsMsg2Status(msg *ebaiapi.CallbackMsg) (orderStatus *model.OrderStatus) {
if msg.Cmd == ebaiapi.CmdOrderPartRefund {
partRefundData := msg.Data.(*ebaiapi.CBPartRefundInfo)
orderStatus = &model.OrderStatus{
VendorOrderID: partRefundData.RefundID, // 是售后单ID不是订单ID订单ID在RefVendorOrderID中
VendorID: model.VendorIDEBAI,
OrderType: model.OrderTypeAfsOrder,
RefVendorOrderID: utils.Int64ToStr(partRefundData.OrderID),
RefVendorID: model.VendorIDEBAI,
VendorStatus: utils.Int2Str(partRefundData.Status),
Status: c.GetAfsStatusFromVendorStatus4PartRefund(partRefundData.Status),
StatusTime: utils.Timestamp2Time(msg.Timestamp),
Remark: buildFullReason(partRefundData.Reason, partRefundData.AdditionReason),
}
if orderStatus.Status == model.AfsOrderStatusWait4Approve && partRefundData.Type != ebaiapi.OrderPartRefuncTypeCustomer {
orderStatus.Status = model.AfsOrderStatusNew
}
} else if msg.Cmd == ebaiapi.CmdOrderUserCancel {
cancelData := msg.Data.(*ebaiapi.CBUserCancelInfo)
orderStatus = &model.OrderStatus{
VendorOrderID: utils.Int64ToStr(cancelData.OrderID), // 是售后单ID不是订单ID订单ID在RefVendorOrderID中
VendorID: model.VendorIDEBAI,
OrderType: model.OrderTypeAfsOrder,
RefVendorOrderID: utils.Int64ToStr(cancelData.OrderID),
RefVendorID: model.VendorIDEBAI,
VendorStatus: utils.Int2Str(cancelData.Type),
Status: c.GetAfsStatusFromVendorStatus4UserCancel(cancelData.Type),
StatusTime: utils.Timestamp2Time(msg.Timestamp),
Remark: buildFullReason(cancelData.CancelReason, cancelData.AdditionReason),
}
}
return orderStatus
}
// 审核售后单申请
func (c *PurchaseHandler) AgreeOrRefuseRefund(ctx *jxcontext.Context, order *model.AfsOrder, approveType int, reason string) (err error) {
if globals.EnableEbaiStoreWrite {
if approveType == partner.AfsApproveTypeRefused {
err = api.EbaiAPI.OrderDisagreeRefund(order.VendorOrderID, reason)
} else {
err = api.EbaiAPI.OrderAgreeRefund(order.VendorOrderID)
}
}
return err
}
// 确认收到退货
func (c *PurchaseHandler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, order *model.AfsOrder) (err error) {
err = fmt.Errorf("内部错误,饿百平台不支持确认收到退货操作")
return err
}

View File

@@ -0,0 +1,16 @@
package ebai
import (
"testing"
"git.rosy.net.cn/baseapi/utils"
)
func TestGetOrder4PartRefund(t *testing.T) {
order, err := new(PurchaseHandler).GetOrder4PartRefund("1556529608021993938")
if err != nil {
t.Fatal(err.Error())
} else {
t.Log(utils.Format4Output(order, false))
}
}

View File

@@ -59,7 +59,7 @@ func (p *PurchaseHandler) CreateStore(db *dao.DaoDB, storeID int, userName strin
params["category1"] = "" params["category1"] = ""
params["category2"] = "" params["category2"] = ""
params["category3"] = "" params["category3"] = ""
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if globals.EnableEbaiStoreWrite {
intVendorStoreID, err2 := api.EbaiAPI.ShopCreate(params) intVendorStoreID, err2 := api.EbaiAPI.ShopCreate(params)
if err = err2; err == nil { if err = err2; err == nil {
return utils.Int64ToStr(intVendorStoreID), err return utils.Int64ToStr(intVendorStoreID), err
@@ -160,7 +160,7 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
if err = dao.GetRows(db, &stores, sql, model.VendorIDEBAI, utils.DefaultTimeValue, storeID); err == nil { if err = dao.GetRows(db, &stores, sql, model.VendorIDEBAI, utils.DefaultTimeValue, storeID); err == nil {
for _, store := range stores { for _, store := range stores {
// globals.SugarLogger.Debug(utils.Format4Output(params, false)) // globals.SugarLogger.Debug(utils.Format4Output(params, false))
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if globals.EnableEbaiStoreWrite {
shopID := 0 shopID := 0
if store.SyncStatus&model.SyncFlagDeletedMask == 0 { if store.SyncStatus&model.SyncFlagDeletedMask == 0 {
shopID = store.ID shopID = store.ID
@@ -181,10 +181,10 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
} }
if err == nil { if err == nil {
mergeStatus := jxutils.MergeStoreStatus(store.Status, store.EbaiStoreStatus) mergeStatus := jxutils.MergeStoreStatus(store.Status, store.EbaiStoreStatus)
if store2.Status != mergeStatus { if !isStoreStatusSame(store2.Status, mergeStatus) {
if mergeStatus == model.StoreStatusOpened { if mergeStatus == model.StoreStatusOpened {
err = api.EbaiAPI.ShopOpen("", utils.Str2Int64(store.VendorStoreID)) err = api.EbaiAPI.ShopOpen("", utils.Str2Int64(store.VendorStoreID))
} else if mergeStatus == model.StoreStatusClosed { } else if mergeStatus == model.StoreStatusHaveRest || mergeStatus == model.StoreStatusClosed {
err = api.EbaiAPI.ShopClose("", utils.Str2Int64(store.VendorStoreID)) err = api.EbaiAPI.ShopClose("", utils.Str2Int64(store.VendorStoreID))
} else if mergeStatus == model.StoreStatusDisabled { } else if mergeStatus == model.StoreStatusDisabled {
err = api.EbaiAPI.ShopOffline("", utils.Str2Int64(store.VendorStoreID)) err = api.EbaiAPI.ShopOffline("", utils.Str2Int64(store.VendorStoreID))
@@ -204,10 +204,9 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
if err != nil { if err != nil {
return err return err
} }
// todo 饿百 开店审核通过后不允许修改商户信息 params := genStoreMapFromStore(store)
// params := genStoreMapFromStore(store) if err = api.EbaiAPI.ShopUpdate(params); err == nil {
// if err = api.EbaiAPI.ShopUpdate(params); err == nil { }
// }
} }
} }
} }
@@ -215,6 +214,16 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
return err return err
} }
func isStoreStatusSame(status1, status2 int) bool {
if status1 == model.StoreStatusClosed {
status1 = model.StoreStatusHaveRest
}
if status2 == model.StoreStatusClosed {
status2 = model.StoreStatusHaveRest
}
return status1 == status2
}
func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask tasksch.ITask, isAsync bool) (hint string, err error) { func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask tasksch.ITask, isAsync bool) (hint string, err error) {
globals.SugarLogger.Debugf("ebai RefreshAllStoresID") globals.SugarLogger.Debugf("ebai RefreshAllStoresID")
const batchSize = 50 const batchSize = 50
@@ -248,7 +257,7 @@ func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask
shopIDs[k] = utils.GetUUID() shopIDs[k] = utils.GetUUID()
} }
} }
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopIDBatchUpdate(baiduShopIDs, shopIDs) err = api.EbaiAPI.ShopIDBatchUpdate(baiduShopIDs, shopIDs)
} }
return nil, err return nil, err
@@ -311,7 +320,7 @@ func EbaiDeliveryRegion2Jx(deliveryRegion interface{}) string {
func JxDeliveryRegion2Ebai(store *model.Store) (deliveryRegion interface{}) { func JxDeliveryRegion2Ebai(store *model.Store) (deliveryRegion interface{}) {
rangeStr := strings.Trim(store.DeliveryRange, ";") rangeStr := strings.Trim(store.DeliveryRange, ";")
if store.DeliveryRangeType == model.DeliveryRangeTypeRadius { if store.DeliveryRangeType == model.DeliveryRangeTypeRadius {
if utils.Str2Int64(store.DeliveryRange) > 100 { // todo 如果小于100米表示禁用不更新 if utils.Str2Int64WithDefault(store.DeliveryRange, 0) > 100 { // todo 如果小于100米表示禁用不更新
rangeStr = jxutils.GetPolygonFromCircleStr(jxutils.IntCoordinate2Standard(store.Lng), jxutils.IntCoordinate2Standard(store.Lat), utils.Str2Float64(store.DeliveryRange), 8) rangeStr = jxutils.GetPolygonFromCircleStr(jxutils.IntCoordinate2Standard(store.Lng), jxutils.IntCoordinate2Standard(store.Lat), utils.Str2Float64(store.DeliveryRange), 8)
} else { } else {
rangeStr = "" rangeStr = ""
@@ -363,19 +372,20 @@ func JxBusinessTime2Ebai(store *model.Store) interface{} {
func genStoreMapFromStore(store *tEbaiStoreInfo) map[string]interface{} { func genStoreMapFromStore(store *tEbaiStoreInfo) map[string]interface{} {
params := map[string]interface{}{ params := map[string]interface{}{
"phone": store.Tel1,
"business_time": JxBusinessTime2Ebai(&store.Store), "business_time": JxBusinessTime2Ebai(&store.Store),
} }
// if store.Tel2 != "" {
// params["ivr_phone"] = store.Tel2
// }
// params["phone"] = store.Tel1
if store.VendorStoreID != "" { if store.VendorStoreID != "" {
params["baidu_shop_id"] = store.VendorStoreID params["baidu_shop_id"] = store.VendorStoreID
} }
if store.Tel2 != "" { if store.SyncStatus&(model.SyncFlagNewMask /*|model.SyncFlagStoreName*/) != 0 {
params["ivr_phone"] = store.Tel2 params["name"] = jxutils.ComposeStoreName(store.Name, model.VendorIDEBAI)
} }
if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreName) != 0 { // todo 饿百 开店审核通过后不允许修改商户信息
// params["name"] = jxutils.ComposeStoreName(store.Name, model.VendorIDEBAI) if store.SyncStatus&(model.SyncFlagNewMask /*|model.SyncFlagStoreAddress*/) != 0 {
}
if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreAddress) != 0 {
params["longitude"] = jxutils.IntCoordinate2Standard(store.Lng) params["longitude"] = jxutils.IntCoordinate2Standard(store.Lng)
params["latitude"] = jxutils.IntCoordinate2Standard(store.Lat) params["latitude"] = jxutils.IntCoordinate2Standard(store.Lat)
params["address"] = store.Address params["address"] = store.Address
@@ -415,7 +425,7 @@ func (c *PurchaseHandler) onShopMsgPush(msg *ebaiapi.CallbackMsg) (response *eba
if int(utils.ForceInterface2Int64(msg.Body["business_ele"])) == 1 { if int(utils.ForceInterface2Int64(msg.Body["business_ele"])) == 1 {
storeStatus = model.StoreStatusOpened storeStatus = model.StoreStatusOpened
} else { } else {
storeStatus = model.StoreStatusClosed storeStatus = model.StoreStatusHaveRest
} }
} }
if err == nil { if err == nil {
@@ -423,3 +433,11 @@ func (c *PurchaseHandler) onShopMsgPush(msg *ebaiapi.CallbackMsg) (response *eba
} }
return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil) return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil)
} }
func (c *PurchaseHandler) GetShopHealthInfo(vendorShopID string) (shopHealthInfo map[string]interface{}, err error) {
result, err := api.EbaiAPI.GetShopHealthByDetail(utils.Str2Int64(vendorShopID))
if err == nil {
shopHealthInfo = utils.Struct2FlatMap(result)
}
return shopHealthInfo, err
}

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"time"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi" "git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
@@ -12,6 +13,7 @@ import (
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
) )
@@ -23,7 +25,6 @@ const (
type tStoreSkuFullInfo struct { type tStoreSkuFullInfo struct {
model.StoreSkuBind model.StoreSkuBind
SkuID int `orm:"column(sku_id)"`
NameID int `orm:"column(name_id)"` NameID int `orm:"column(name_id)"`
SpecQuality float32 `json:"specQuality"` SpecQuality float32 `json:"specQuality"`
@@ -39,6 +40,7 @@ type tStoreSkuFullInfo struct {
Img string `orm:"size(255)" json:"img"` Img string `orm:"size(255)" json:"img"`
PlaceStr string PlaceStr string
Upc string Upc string
DescImgEbai string
CatName string `orm:"size(255)"` CatName string `orm:"size(255)"`
@@ -55,6 +57,7 @@ type tStoreSkuFullInfo struct {
EbaiCat3ID int64 `orm:"column(ebai_cat3_id)"` EbaiCat3ID int64 `orm:"column(ebai_cat3_id)"`
PricePercentage int PricePercentage int
CatPricePercentage int
} }
type tStoreCatInfo struct { type tStoreCatInfo struct {
@@ -80,23 +83,23 @@ var (
func (p *PurchaseHandler) getDirtyStoreSkus(db *dao.DaoDB, storeID int, skuIDs []int) (storeSkuInfoList []*tStoreSkuFullInfo, err error) { func (p *PurchaseHandler) getDirtyStoreSkus(db *dao.DaoDB, storeID int, skuIDs []int) (storeSkuInfoList []*tStoreSkuFullInfo, err error) {
sql := ` sql := `
SELECT t8.price_percentage, t1.*, t2.id sku_id, t2.spec_quality, t2.spec_unit, t2.weight, t2.status sku_status, SELECT t8.price_percentage, t1.*, t2.spec_quality, t2.spec_unit, t2.weight, t2.status sku_status,
t3.id name_id, t3.prefix, t3.name, t2.comment, t3.is_global, t3.unit, IF(t3.img_ebai <> '', t3.img_ebai, t3.img) img, t3.upc, t3.id name_id, t3.prefix, t3.name, t2.comment, t3.is_global, t3.unit, IF(t3.img_ebai <> '', t3.img_ebai, t3.img) img, t3.upc, t3.desc_img_ebai,
t4.name cat_name, t4.name cat_name, t4.ebai_price_percentage cat_price_percentage,
t4.id cat_id, t4.level cat_level, t5.ebai_id cat_ebai_id, t4.id cat_id, t4.level cat_level, t5.ebai_id cat_ebai_id,
t4p.id parent_cat_id, t5p.ebai_id parent_cat_ebai_id, t5p.ebai_sync_status parent_cat_ebai_sync_status, t4p.id parent_cat_id, t5p.ebai_id parent_cat_ebai_id, t5p.ebai_sync_status parent_cat_ebai_sync_status,
cat1.vendor_category_id ebai_cat3_id, cat2.vendor_category_id ebai_cat2_id, cat2.parent_id ebai_cat1_id cat1.vendor_category_id ebai_cat3_id, cat2.vendor_category_id ebai_cat2_id, cat2.parent_id ebai_cat1_id
FROM store_sku_bind t1 FROM store_sku_bind t1
LEFT JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? AND t2.status = ? LEFT JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? AND t2.status = ?
LEFT JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ? LEFT JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? AND t3.status = ?
JOIN sku_category t4 ON t3.category_id = t4.id LEFT JOIN sku_category t4 ON t3.category_id = t4.id
LEFT JOIN sku_category t4p ON t4p.id = t4.parent_id LEFT JOIN sku_category t4p ON t4p.id = t4.parent_id
LEFT JOIN store_sku_category_map t5 ON t5.store_id = t1.store_id AND t5.category_id = t4.id AND t5.deleted_at = ? LEFT JOIN store_sku_category_map t5 ON t5.store_id = t1.store_id AND t5.category_id = t4.id AND t5.deleted_at = ?
LEFT JOIN store_sku_category_map t5p ON t5p.store_id = t1.store_id AND t5p.category_id = t4p.id AND t5p.deleted_at = ? LEFT JOIN store_sku_category_map t5p ON t5p.store_id = t1.store_id AND t5p.category_id = t4p.id AND t5p.deleted_at = ?
LEFT JOIN sku_vendor_category cat1 ON t4.ebai_category_id = cat1.vendor_category_id AND cat1.vendor_id = ? LEFT JOIN sku_vendor_category cat1 ON t4.ebai_category_id = cat1.vendor_category_id AND cat1.vendor_id = ?
LEFT JOIN sku_vendor_category cat2 ON cat1.parent_id = cat2.vendor_category_id AND cat1.vendor_id = ? LEFT JOIN sku_vendor_category cat2 ON cat1.parent_id = cat2.vendor_category_id AND cat1.vendor_id = ?
JOIN store_map t8 ON t8.store_id = t1.store_id AND t8.vendor_id = ? AND t8.deleted_at = ? JOIN store_map t8 ON t8.store_id = t1.store_id AND t8.vendor_id = ? AND t8.deleted_at = ?
WHERE t1.store_id = ? AND (t1.ebai_sync_status <> 0) WHERE t1.store_id = ? AND (t1.ebai_sync_status <> 0 OR (t1.ebai_id <> 0 AND t3.id IS NULL))
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
@@ -115,7 +118,7 @@ func (p *PurchaseHandler) getDirtyStoreSkus(db *dao.DaoDB, storeID int, skuIDs [
sql += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")" sql += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs) sqlParams = append(sqlParams, skuIDs)
} }
sql += " ORDER BY t1.price" sql += " ORDER BY t1.price DESC"
err = dao.GetRows(db, &storeSkuInfoList, sql, sqlParams...) err = dao.GetRows(db, &storeSkuInfoList, sql, sqlParams...)
return storeSkuInfoList, err return storeSkuInfoList, err
} }
@@ -123,7 +126,7 @@ func (p *PurchaseHandler) getDirtyStoreSkus(db *dao.DaoDB, storeID int, skuIDs [
func (p *PurchaseHandler) createCatByStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, db *dao.DaoDB, storeID int, storeSkuInfoList []*tStoreSkuFullInfo) (num int64, err error) { func (p *PurchaseHandler) createCatByStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, db *dao.DaoDB, storeID int, storeSkuInfoList []*tStoreSkuFullInfo) (num int64, err error) {
catList2Add := make(map[int]int) catList2Add := make(map[int]int)
for _, storeSku := range storeSkuInfoList { for _, storeSku := range storeSkuInfoList {
if storeSku.SkuID != 0 && storeSku.EbaiSyncStatus&(model.SyncFlagNewMask|model.SyncFlagModifiedMask) != 0 { if storeSku.CatID != 0 && storeSku.EbaiSyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreSkuModifiedMask) != 0 && storeSku.Status == model.SkuStatusNormal {
if storeSku.ParentCatEbaiID == 0 && storeSku.ParentCatID != 0 { if storeSku.ParentCatEbaiID == 0 && storeSku.ParentCatID != 0 {
catList2Add[storeSku.ParentCatID] = 1 catList2Add[storeSku.ParentCatID] = 1
} }
@@ -164,7 +167,7 @@ func (p *PurchaseHandler) FullSyncStoreSkus(ctx *jxcontext.Context, parentTask t
_, err = p.setStoreSkuSyncStatus(ctx, db, storeID, nil, model.SyncFlagNewMask) _, err = p.setStoreSkuSyncStatus(ctx, db, storeID, nil, model.SyncFlagNewMask)
case 2: case 2:
if err = p.DeleteRemoteCategories(ctx, rootTask, storeID, nil); err == nil { if err = p.DeleteRemoteCategories(ctx, rootTask, storeID, nil); err == nil {
_, err = dao.SetStoreCategorySyncStatus(db, model.VendorIDEBAI, storeID, nil, model.SyncFlagNewMask) _, err = dao.SetStoreCategorySyncStatus(db, model.VendorIDEBAI, []int{storeID}, nil, model.SyncFlagNewMask)
} }
case 3: case 3:
err = p.SyncLocalStoreCategory(db, storeID, userName) err = p.SyncLocalStoreCategory(db, storeID, userName)
@@ -202,7 +205,7 @@ func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTa
_, err = p.setStoreSkuSyncStatus(ctx, db, storeID, nil, model.SyncFlagNewMask) _, err = p.setStoreSkuSyncStatus(ctx, db, storeID, nil, model.SyncFlagNewMask)
case 2: case 2:
if err = p.DeleteRemoteCategories(ctx, rootTask, storeID, nil); err == nil { if err = p.DeleteRemoteCategories(ctx, rootTask, storeID, nil); err == nil {
_, err = dao.SetStoreCategorySyncStatus(db, model.VendorIDEBAI, storeID, nil, model.SyncFlagNewMask) _, err = dao.SetStoreCategorySyncStatus(db, model.VendorIDEBAI, []int{storeID}, nil, model.SyncFlagNewMask)
} }
} }
return nil, err return nil, err
@@ -248,35 +251,66 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeSku := batchItemList[0].(*tStoreSkuFullInfo) storeSku := batchItemList[0].(*tStoreSkuFullInfo)
updateFields := []string{model.FieldEbaiSyncStatus} updateFields := []string{model.FieldEbaiSyncStatus}
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if storeSku.NameID == 0 || storeSku.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 {
if storeSku.SkuID == 0 || storeSku.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 { if storeSku.EbaiSyncStatus&model.SyncFlagNewMask == 0 && !jxutils.IsEmptyID(storeSku.EbaiID) {
if storeSku.EbaiSyncStatus&model.SyncFlagNewMask == 0 && storeSku.EbaiID != 0 { if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.SkuDelete(strStoreID, utils.Int64ToStr(storeSku.EbaiID)) err = api.EbaiAPI.SkuDelete(strStoreID, utils.Int64ToStr(storeSku.EbaiID))
err = ignoreNoSkuErr(err)
} }
} else if storeSku.EbaiSyncStatus&model.SyncFlagNewMask != 0 { }
if err == nil {
if utils.IsTimeZero(storeSku.DeletedAt) {
storeSku.DeletedAt = time.Now()
updateFields = append(updateFields, model.FieldDeletedAt)
}
storeSku.EbaiID = 0
updateFields = append(updateFields, model.FieldEbaiID)
}
} else {
if storeSku.EbaiSyncStatus&model.SyncFlagNewMask != 0 {
// globals.SugarLogger.Debug(utils.Format4Output(genSkuParamsFromStoreSkuInfo(storeSku), false)) // globals.SugarLogger.Debug(utils.Format4Output(genSkuParamsFromStoreSkuInfo(storeSku), false))
// todo 适当处理重复(即已经创建)的情况 // todo 适当处理重复(即已经创建)的情况
if storeSku.EbaiID, err = api.EbaiAPI.SkuCreate(strStoreID, storeSku.SkuID, genSkuParamsFromStoreSkuInfo(storeSku)); err == nil { mergedStoreSkuStatus := jxutils.MergeSkuStatus(storeSku.SkuStatus, storeSku.Status)
if mergedStoreSkuStatus == model.SkuStatusNormal { // 待创建且不可售的,暂不新建
if storeSku.Img != "" {
if globals.EnableEbaiStoreWrite {
storeSku.EbaiID, err = api.EbaiAPI.SkuCreate(strStoreID, storeSku.SkuID, genSkuParamsFromStoreSkuInfo(storeSku))
} else {
storeSku.EbaiID = jxutils.GenFakeID()
}
if err == nil {
updateFields = append(updateFields, model.FieldEbaiID) updateFields = append(updateFields, model.FieldEbaiID)
} else if storeSku.EbaiID = ebaiapi.GetEbaiSkuIDFromError(err); storeSku.EbaiID > 0 { } else if storeSku.EbaiID = ebaiapi.GetEbaiSkuIDFromError(err); storeSku.EbaiID > 0 {
// globals.SugarLogger.Debugf("SyncStoreSkus test storeSku.EbaiID:%d, err:%v", storeSku.EbaiID, err) // globals.SugarLogger.Debugf("SyncStoreSkus test storeSku.EbaiID:%d, err:%v", storeSku.EbaiID, err)
updateFields = append(updateFields, model.FieldEbaiID) updateFields = append(updateFields, model.FieldEbaiID)
_, err = api.EbaiAPI.SkuUpdate(strStoreID, storeSku.EbaiID, genSkuParamsFromStoreSkuInfo(storeSku)) err = skuUpdate(strStoreID, storeSku)
} }
} else if storeSku.EbaiSyncStatus&model.SyncFlagModifiedMask != 0 { } else {
if jxutils.IsFakeID(storeSku.EbaiID) { err = fmt.Errorf("SKUANME%d:%s没有图片同步失败", storeSku.NameID, storeSku.Name)
}
} else {
updateFields = nil
}
} else if storeSku.EbaiSyncStatus&model.SyncFlagStoreSkuModifiedMask != 0 {
if jxutils.IsEmptyID(storeSku.EbaiID) {
err = fmt.Errorf("京西数据异常,修改一个没有创建的饿百商品:%d, store:%s", storeSku.SkuID, strStoreID) err = fmt.Errorf("京西数据异常,修改一个没有创建的饿百商品:%d, store:%s", storeSku.SkuID, strStoreID)
} else { } else {
if _, err = api.EbaiAPI.SkuUpdate(strStoreID, storeSku.EbaiID, genSkuParamsFromStoreSkuInfo(storeSku)); err == nil { if storeSku.Img != "" {
// err = api.EbaiAPI.SkuShopCategoryMap(strStoreID, storeSku.EbaiID, utils.Int64ToStr(storeSku.CatEbaiID)) err = skuUpdate(strStoreID, storeSku)
} else {
err = fmt.Errorf("SKUANME%d:%s没有图片同步失败", storeSku.NameID, storeSku.Name)
} }
} }
} }
} }
if err == nil { if err == nil {
if len(updateFields) > 0 {
storeSku.EbaiSyncStatus = 0 storeSku.EbaiSyncStatus = 0
_, err = dao.UpdateEntity(nil, &storeSku.StoreSkuBind, updateFields...) _, err = dao.UpdateEntity(nil, &storeSku.StoreSkuBind, updateFields...)
} }
} else if isErrModifyPrice(err) {
err = partner.NewErrorCode(err.Error(), partner.ErrCodeChangePriceFailed, model.VendorIDEBAI)
}
return nil, err return nil, err
}, storeSkuInfoList) }, storeSkuInfoList)
tasksch.AddChild(rootTask, task).Run() tasksch.AddChild(rootTask, task).Run()
@@ -291,9 +325,58 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
return rootTask.ID, err return rootTask.ID, err
} }
func (p *PurchaseHandler) GetAllRemoteSkus(ctx *jxcontext.Context, storeID int, parentTask tasksch.ITask) (skus []map[string]interface{}, err error) { func ignoreNoSkuErr(err error) error {
if err != nil {
if codeErr, ok := err.(*utils.ErrorWithCode); ok {
if codeErr.IntCode() == 1 {
for _, v := range []string{"SKU不存在或者已经被删除", "sku_id与shop_id不匹配"} {
if strings.Index(codeErr.ErrMsg(), v) > 0 {
err = nil
break
}
}
}
}
}
return err
}
func skuUpdate(strStoreID string, storeSku *tStoreSkuFullInfo) (err error) {
if globals.EnableEbaiStoreWrite {
if _, err = api.EbaiAPI.SkuUpdate(strStoreID, storeSku.EbaiID, genSkuParamsFromStoreSkuInfo(storeSku)); err != nil {
// 如果是改价错误,尝试把价格标志去掉再同步
if isErrModifyPrice(err) {
storeSku.EbaiSyncStatus = storeSku.EbaiSyncStatus & ^model.SyncFlagPriceMask
if storeSku.EbaiSyncStatus != 0 {
if _, err2 := api.EbaiAPI.SkuUpdate(strStoreID, storeSku.EbaiID, genSkuParamsFromStoreSkuInfo(storeSku)); err2 != nil {
err = err2
}
}
}
}
}
return err
}
func isErrModifyPrice(err error) bool {
if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 1 {
for _, v := range []string{
"无法修改价格",
"sku_参加营销活动",
} {
if strings.Index(errExt.ErrMsg(), v) >= 0 {
return true
}
}
}
return false
}
func (p *PurchaseHandler) GetAllRemoteSkus(ctx *jxcontext.Context, storeID int, parentTask tasksch.ITask) (skus []*ebaiapi.SkuInfo, err error) {
globals.SugarLogger.Debugf("ebai GetAllRemoteSkus storeID:%d, userName:%s", storeID, ctx.GetUserName()) globals.SugarLogger.Debugf("ebai GetAllRemoteSkus storeID:%d, userName:%s", storeID, ctx.GetUserName())
page1, err := api.EbaiAPI.SkuList(utils.Int2Str(storeID), utils.Params2Map("pagesize", MaxPageSize)) page1, err := api.EbaiAPI.SkuList(utils.Int2Str(storeID), &ebaiapi.SkuListParams{
PageSize: MaxPageSize,
})
if err == nil { if err == nil {
skus = append(skus, page1.List...) skus = append(skus, page1.List...)
if page1.Pages > 1 { if page1.Pages > 1 {
@@ -303,9 +386,9 @@ func (p *PurchaseHandler) GetAllRemoteSkus(ctx *jxcontext.Context, storeID int,
} }
task := tasksch.NewParallelTask("GetAllRemoteSkus", nil, ctx, task := tasksch.NewParallelTask("GetAllRemoteSkus", nil, ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
callParams := map[string]interface{}{ callParams := &ebaiapi.SkuListParams{
"pagesize": MaxPageSize, PageSize: MaxPageSize,
"page": batchItemList[0], Page: batchItemList[0].(int),
} }
pageSku, err2 := api.EbaiAPI.SkuList(utils.Int2Str(storeID), callParams) pageSku, err2 := api.EbaiAPI.SkuList(utils.Int2Str(storeID), callParams)
if err2 == nil { if err2 == nil {
@@ -318,7 +401,7 @@ func (p *PurchaseHandler) GetAllRemoteSkus(ctx *jxcontext.Context, storeID int,
result, err2 := task.GetResult(0) result, err2 := task.GetResult(0)
if err = err2; err == nil { if err = err2; err == nil {
for _, v := range result { for _, v := range result {
skus = append(skus, v.(map[string]interface{})) skus = append(skus, v.(*ebaiapi.SkuInfo))
} }
} }
} }
@@ -334,7 +417,7 @@ func (p *PurchaseHandler) DeleteRemoteSkus(ctx *jxcontext.Context, parentTask ta
if err = err2; err == nil { if err = err2; err == nil {
vendorSkuIDs = make([]string, len(result)) vendorSkuIDs = make([]string, len(result))
for k, v := range result { for k, v := range result {
vendorSkuIDs[k] = utils.Interface2String(v[ebaiapi.KeySkuID]) vendorSkuIDs[k] = utils.Int64ToStr(v.SkuID)
} }
} }
} }
@@ -344,7 +427,7 @@ func (p *PurchaseHandler) DeleteRemoteSkus(ctx *jxcontext.Context, parentTask ta
for k, v := range batchItemList { for k, v := range batchItemList {
strList[k] = v.(string) strList[k] = v.(string)
} }
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.SkuDelete(utils.Int2Str(storeID), strings.Join(strList, ",")) err = api.EbaiAPI.SkuDelete(utils.Int2Str(storeID), strings.Join(strList, ","))
} }
return nil, err return nil, err
@@ -369,7 +452,7 @@ func (p *PurchaseHandler) DeleteRemoteCategories(ctx *jxcontext.Context, parentT
} }
task := tasksch.NewParallelTask("DeleteRemoteCategories", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, task := tasksch.NewParallelTask("DeleteRemoteCategories", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryDelete(strStoreID, batchItemList[0].(int64)) err = api.EbaiAPI.ShopCategoryDelete(strStoreID, batchItemList[0].(int64))
} }
return nil, err return nil, err
@@ -385,11 +468,12 @@ func (p *PurchaseHandler) RefreshStoresAllSkusID(ctx *jxcontext.Context, parentT
/////////// ///////////
func genSkuParamsFromStoreSkuInfo(storeSku *tStoreSkuFullInfo) (params map[string]interface{}) { func genSkuParamsFromStoreSkuInfo(storeSku *tStoreSkuFullInfo) (params map[string]interface{}) {
price := jxutils.CaculateSkuVendorPrice(storeSku.Price, storeSku.PricePercentage) price := jxutils.CaculateSkuVendorPrice(storeSku.Price, storeSku.PricePercentage, storeSku.CatPricePercentage)
params = map[string]interface{}{ params = map[string]interface{}{
"name": jxutils.ComposeSkuName(storeSku.Prefix, storeSku.Name, storeSku.Comment, storeSku.Unit, storeSku.SpecQuality, storeSku.SpecUnit, 0), "name": utils.LimitMixedStringLen(jxutils.ComposeSkuName(storeSku.Prefix, storeSku.Name, storeSku.Comment, storeSku.Unit, storeSku.SpecQuality, storeSku.SpecUnit, 0), ebaiapi.MaxSkuNameByteCount),
"left_num": model.MaxStoreSkuStockQty, "left_num": model.MaxStoreSkuStockQty,
"category_id": storeSku.CatEbaiID, "category_id": storeSku.CatEbaiID,
"predict_cat": 0, // 不使用推荐类目
"cat1_id": getEbaiCat(storeSku.EbaiCat1ID, 1), "cat1_id": getEbaiCat(storeSku.EbaiCat1ID, 1),
"cat2_id": getEbaiCat(storeSku.EbaiCat2ID, 2), "cat2_id": getEbaiCat(storeSku.EbaiCat2ID, 2),
"cat3_id": getEbaiCat(storeSku.EbaiCat3ID, 3), "cat3_id": getEbaiCat(storeSku.EbaiCat3ID, 3),
@@ -401,6 +485,9 @@ func genSkuParamsFromStoreSkuInfo(storeSku *tStoreSkuFullInfo) (params map[strin
}, },
}, },
} }
if storeSku.DescImgEbai != "" {
params["rtf"] = storeSku.DescImgEbai
}
if storeSku.EbaiSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 { if storeSku.EbaiSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
params["sale_price"] = price params["sale_price"] = price
params["market_price"] = price params["market_price"] = price
@@ -516,18 +603,23 @@ func (p *PurchaseHandler) SyncStoreCategory(ctx *jxcontext.Context, parentTask t
updateFields := []string{model.FieldEbaiSyncStatus} updateFields := []string{model.FieldEbaiSyncStatus}
catInfo := batchItemList[0].(*tStoreCatInfo) catInfo := batchItemList[0].(*tStoreCatInfo)
// globals.SugarLogger.Debug(utils.Format4Output(catInfo, false)) // globals.SugarLogger.Debug(utils.Format4Output(catInfo, false))
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if catInfo.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 { // 删除 if catInfo.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 { // 删除
if catInfo.EbaiSyncStatus&model.SyncFlagNewMask == 0 && catInfo.EbaiID != 0 { if catInfo.EbaiSyncStatus&model.SyncFlagNewMask == 0 && catInfo.EbaiID != 0 {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryDelete(strStoreID, catInfo.EbaiID) err = api.EbaiAPI.ShopCategoryDelete(strStoreID, catInfo.EbaiID)
} }
}
} else if catInfo.EbaiSyncStatus&model.SyncFlagNewMask != 0 { // 新增 } else if catInfo.EbaiSyncStatus&model.SyncFlagNewMask != 0 { // 新增
ebaiID, err2 := api.EbaiAPI.ShopCategoryCreate(strStoreID, catInfo.ParentEbaiID, formatName(catInfo.Name), jxCatSeq2Ebai(catInfo.Seq)) if globals.EnableEbaiStoreWrite {
if err = err2; err == nil { catInfo.EbaiID, err = api.EbaiAPI.ShopCategoryCreate(strStoreID, catInfo.ParentEbaiID, formatName(catInfo.Name), jxCatSeq2Ebai(catInfo.Seq))
catInfo.EbaiID = ebaiID } else {
catInfo.EbaiID = jxutils.GenFakeID()
}
if err == nil {
updateFields = append(updateFields, model.FieldEbaiID) updateFields = append(updateFields, model.FieldEbaiID)
} }
} else if catInfo.EbaiSyncStatus&model.SyncFlagModifiedMask != 0 { // 修改 } else if catInfo.EbaiSyncStatus&model.SyncFlagModifiedMask != 0 { // 修改
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryUpdate(strStoreID, catInfo.EbaiID, formatName(catInfo.Name), jxCatSeq2Ebai(catInfo.Seq)) err = api.EbaiAPI.ShopCategoryUpdate(strStoreID, catInfo.EbaiID, formatName(catInfo.Name), jxCatSeq2Ebai(catInfo.Seq))
} }
} }
@@ -603,7 +695,7 @@ func (p *PurchaseHandler) updateLocalCatAsNew(db *dao.DaoDB, localCatMap map[str
func (p *PurchaseHandler) setStoreSkuSyncStatus(ctx *jxcontext.Context, db *dao.DaoDB, storeID int, skuIDs []int, syncStatus int) (num int64, err error) { func (p *PurchaseHandler) setStoreSkuSyncStatus(ctx *jxcontext.Context, db *dao.DaoDB, storeID int, skuIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("ebai setStoreSkuSyncStatus storeID:%d, userName:%s", storeID, ctx.GetUserName()) globals.SugarLogger.Debugf("ebai setStoreSkuSyncStatus storeID:%d, userName:%s", storeID, ctx.GetUserName())
return dao.SetStoreSkuSyncStatus(db, model.VendorIDEBAI, storeID, skuIDs, syncStatus) return dao.SetStoreSkuSyncStatus(db, model.VendorIDEBAI, []int{storeID}, skuIDs, syncStatus)
} }
func formatName(name string) string { func formatName(name string) string {
@@ -614,3 +706,7 @@ func formatName(name string) string {
func jxCatSeq2Ebai(seq int) int { func jxCatSeq2Ebai(seq int) int {
return 10000 - seq return 10000 - seq
} }
func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) {
return storeSkuList, err
}

View File

@@ -0,0 +1,248 @@
package ebai
import (
"fmt"
"strings"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
minBatchSize = 5 // 使用batch相关的API最少的处理数量因为饿百批处理API的调用频率限制得更低
)
// 门店分类
func (p *PurchaseHandler) ReadStoreCategory(ctx *jxcontext.Context, vendorStoreID string) (cats []*partner.BareCategoryInfo, err error) {
remoteCats, err := api.EbaiAPI.ShopCategoryGet(vendorStoreID)
if err == nil {
cats = convertVendorCatList(remoteCats)
}
return cats, err
}
func convertVendorCatList(remoteCats []*ebaiapi.CategoryInfo) (cats []*partner.BareCategoryInfo) {
for _, rCat := range remoteCats {
cat := &partner.BareCategoryInfo{
VendorCatID: utils.Int64ToStr(rCat.CategoryID),
Name: rCat.Name,
Level: rCat.Level,
Seq: jxCatSeq2Ebai(rCat.Rank),
Children: convertVendorCatList(rCat.Children),
}
cats = append(cats, cat)
}
return cats
}
func (p *PurchaseHandler) CreateStoreSkuCategory(ctx *jxcontext.Context, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) {
var vendorCatID int64
if globals.EnableEbaiStoreWrite {
vendorCatID, err = api.EbaiAPI.ShopCategoryCreate(vendorStoreID, utils.Str2Int64WithDefault(storeCat.ParentVendorCatID, 0), storeCat.Name, jxCatSeq2Ebai(storeCat.Seq))
} else {
vendorCatID = jxutils.GenFakeID()
}
storeCat.VendorCatID = utils.Int64ToStr(vendorCatID)
return err
}
func (p *PurchaseHandler) UpdateStoreSkuCategory(ctx *jxcontext.Context, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryUpdate(vendorStoreID, utils.Str2Int64WithDefault(storeCat.VendorCatID, 0), storeCat.Name, jxCatSeq2Ebai(storeCat.Seq))
}
return err
}
func (p *PurchaseHandler) DeleteStoreSkuCategory(ctx *jxcontext.Context, vendorStoreID, vendorCatID string) (err error) {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryDelete(vendorStoreID, utils.Str2Int64WithDefault(vendorCatID, 0))
}
return err
}
// 门店商品
// 多门店平台不需要实现这个接口
func (p *PurchaseHandler) UpdateStoreSkus(ctx *jxcontext.Context, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (err error) {
storeSku := storeSkuList[0]
if globals.EnableEbaiStoreWrite {
_, err = api.EbaiAPI.SkuUpdate(vendorStoreID, utils.Str2Int64(storeSku.VendorSkuID), genSkuParamsFromStoreSkuInfo2(storeSku))
}
return err
}
// 通用
func (p *PurchaseHandler) GetStoreSkusBatchSize(funcID int) int {
return 1
}
// 对于多门店平台来说storeSkuList中只有SkuID与VendorSkuID有意义
func (p *PurchaseHandler) CreateStoreSkus(ctx *jxcontext.Context, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (err error) {
storeSku := storeSkuList[0]
var vendorSkuID int64
if globals.EnableEbaiStoreWrite {
vendorSkuID, err = api.EbaiAPI.SkuCreate(vendorStoreID, storeSku.SkuID, genSkuParamsFromStoreSkuInfo2(storeSku))
} else {
vendorSkuID = jxutils.GenFakeID()
}
storeSku.VendorSkuID = utils.Int64ToStr(vendorSkuID)
return err
}
func (p *PurchaseHandler) DeleteStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*partner.BareStoreSkuInfo) (err error) {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.SkuDelete(vendorStoreID, strings.Join(partner.BareStoreSkuInfoList(storeSkuList).GetVendorSkuIDList(), ","))
}
return err
}
func (p *PurchaseHandler) UpdateStoreSkusStatus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*partner.BareStoreSkuInfo) (err error) {
var validSkus, invalidSkus []string
for _, storeSku := range storeSkuList {
if storeSku.Status == model.SkuStatusNormal {
validSkus = append(validSkus, storeSku.VendorSkuID)
} else {
invalidSkus = append(invalidSkus, storeSku.VendorSkuID)
}
}
if globals.EnableEbaiStoreWrite {
if len(invalidSkus) > minBatchSize {
err = api.EbaiAPI.SkuOffline(utils.Int2Str(storeID), strings.Join(invalidSkus, ","))
} else if len(invalidSkus) > 0 {
for _, v := range invalidSkus {
if err = api.EbaiAPI.SkuOfflineOne(utils.Int2Str(storeID), utils.Str2Int64(v), "", ""); err != nil {
break
}
}
}
if err == nil {
if len(validSkus) > minBatchSize {
err = api.EbaiAPI.SkuOnline(utils.Int2Str(storeID), strings.Join(validSkus, ","))
} else if len(validSkus) > 0 {
for _, v := range invalidSkus {
if err = api.EbaiAPI.SkuOnlineOne(utils.Int2Str(storeID), utils.Str2Int64(v), "", ""); err != nil {
break
}
}
}
}
}
return err
}
func (p *PurchaseHandler) UpdateStoreSkusPrice(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*partner.BareStoreSkuInfo) (err error) {
skuPriceList := make([]string, len(storeSkuList))
for k, v := range storeSkuList {
skuPriceList[k] = fmt.Sprintf("%s:%d", v.VendorSkuID, v.Price)
}
if globals.EnableEbaiStoreWrite {
if len(skuPriceList) > minBatchSize {
err = api.EbaiAPI.SkuPriceUpdateBatch(utils.Int2Str(storeID), strings.Join(skuPriceList, ";"), "", "")
} else {
for _, v := range skuPriceList {
if err = api.EbaiAPI.SkuPriceUpdateOne(utils.Int2Str(storeID), v, "", ""); err != nil {
break
}
}
}
}
return err
}
func genSkuParamsFromStoreSkuInfo2(storeSku *dao.StoreSkuSyncInfo) (params map[string]interface{}) {
params = map[string]interface{}{
"name": storeSku.Name,
"left_num": model.MaxStoreSkuStockQty,
"category_id": utils.Str2Int64(storeSku.VendorCatID),
"predict_cat": 0, // 不使用推荐类目
"cat1_id": getEbaiCat(storeSku.VendorVendorCatID3, 1),
"cat2_id": getEbaiCat(storeSku.VendorVendorCatID2, 2),
"cat3_id": getEbaiCat(storeSku.VendorVendorCatID, 3),
"weight": storeSku.Weight,
"photos": []map[string]interface{}{
map[string]interface{}{
"is_master": true,
"url": storeSku.Img,
},
},
}
if storeSku.DescImg != "" {
params["rtf"] = storeSku.DescImg
}
if storeSku.StoreSkuSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
params["sale_price"] = storeSku.Price
params["market_price"] = storeSku.Price
}
if storeSku.StoreSkuSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
params["status"] = jxSkuStatus2Ebai(storeSku.StoreSkuStatus)
}
// todo 饿百如果给的UPC是空要报错但如果我要删除UPC怎么弄
if storeSku.Upc != "" {
params["upc"] = storeSku.Upc
}
return params
}
func (p *PurchaseHandler) GetStoreSkusInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, inStoreSkuList []*partner.BareStoreSkuInfo) (outStoreSkuList []*partner.BareStoreSkuInfo, err error) {
vendorSkuIDIntList := partner.BareStoreSkuInfoList(inStoreSkuList).GetVendorSkuIDIntList()
var vendorSkuList []*ebaiapi.SkuInfo
if len(vendorSkuIDIntList) > 1 {
task := tasksch.NewParallelTask("获取饿百平台门店商品信息", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
vendorSkuID := batchItemList[0].(int64)
skuInfo, err := api.EbaiAPI.SkuList(utils.Int2Str(storeID), &ebaiapi.SkuListParams{
SkuID: vendorSkuID,
})
if err == nil {
vendorSkuList = skuInfo.List
return skuInfo.List, nil
}
return nil, err
}, vendorSkuIDIntList)
tasksch.HandleTask(task, parentTask, false).Run()
_, err = task.GetResult(0)
} else if len(vendorSkuIDIntList) == 1 {
skuInfo, err2 := api.EbaiAPI.SkuList(utils.Int2Str(storeID), &ebaiapi.SkuListParams{
SkuID: vendorSkuIDIntList[0],
})
if err = err2; err == nil {
vendorSkuList = skuInfo.List
}
} else {
return nil, nil
}
if err == nil {
storeSkuMap := make(map[int64]*partner.BareStoreSkuInfo)
for _, v := range inStoreSkuList {
storeSkuMap[utils.Str2Int64(v.VendorSkuID)] = v
}
for _, skuInfo := range vendorSkuList {
storeSku := storeSkuMap[skuInfo.SkuID]
globals.SugarLogger.Debug(utils.Format4Output(storeSku, false))
outStoreSkuList = append(outStoreSkuList, storeSku)
storeSku.Price = skuInfo.SalePrice
storeSku.Status = ebaiSkuStatus2Jx(skuInfo.Status)
}
}
return outStoreSkuList, err
}
func ebaiSkuStatus2Jx(ebaiSkuStatus int) (jxSkuStatus int) {
if ebaiSkuStatus == ebaiapi.SkuStatusOnline {
jxSkuStatus = model.SkuStatusNormal
} else if ebaiSkuStatus == ebaiapi.SkuStatusOffline {
jxSkuStatus = model.SkuStatusDontSale
} else if ebaiSkuStatus == ebaiapi.SkuStatusOnline {
jxSkuStatus = model.SkuStatusDeleted
}
return jxSkuStatus
}

View File

@@ -18,10 +18,10 @@ var (
ebaiapi.WaybillStatusCourierPickedup: model.WaybillStatusDelivering, ebaiapi.WaybillStatusCourierPickedup: model.WaybillStatusDelivering,
ebaiapi.WaybillStatusDeliveryCancled: model.WaybillStatusCanceled, ebaiapi.WaybillStatusDeliveryCancled: model.WaybillStatusCanceled,
ebaiapi.WaybillStatusFinished: model.WaybillStatusDelivered, ebaiapi.WaybillStatusFinished: model.WaybillStatusDelivered,
ebaiapi.WaybillStatusExceptional: model.WaybillStatusUnknown, ebaiapi.WaybillStatusExceptional: model.WaybillStatusCanceled,
ebaiapi.WaybillStatusSelfDelivery: model.WaybillStatusUnknown, ebaiapi.WaybillStatusSelfDelivery: model.WaybillStatusUnknown,
ebaiapi.WaybillStatusNotInDelivering: model.WaybillStatusUnknown, ebaiapi.WaybillStatusDontDeliver: model.WaybillStatusCanceled,
ebaiapi.WaybillStatusDeliveryRejected: model.WaybillStatusNeverSend, ebaiapi.WaybillStatusDeliveryRejected: model.WaybillStatusCanceled,
} }
) )

View File

@@ -0,0 +1,19 @@
package elm
import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
)
func (c *PurchaseHandler) CreateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
return err
}
func (c *PurchaseHandler) UpdateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
return err
}
func (c *PurchaseHandler) CancelAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
return err
}

View File

@@ -16,6 +16,7 @@ import (
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/business/partner"
) )
@@ -186,47 +187,23 @@ func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])), SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])),
Weight: int(math.Round(utils.Interface2Float64WithDefault(product["weight"], 0.0))), Weight: int(math.Round(utils.Interface2Float64WithDefault(product["weight"], 0.0))),
} }
if sku.VendorSkuID == "0" { if dao.IsVendorThingIDEmpty(sku.VendorSkuID) {
sku.VendorSkuID = utils.Int64ToStr(utils.MustInterface2Int64(product["id"])) // 2018-09-28日饿了么迁移到饿百后这个字段发生了变化 sku.VendorSkuID = utils.Int64ToStr(utils.MustInterface2Int64(product["id"])) // 2018-09-28日饿了么迁移到饿百后这个字段发生了变化
} }
order.Skus = append(order.Skus, sku) order.Skus = append(order.Skus, sku)
order.SkuCount++
order.GoodsCount += sku.Count
order.SalePrice += sku.SalePrice * int64(sku.Count)
order.Weight += sku.Weight * sku.Count
} }
} }
setOrederDetailFee(result, order) jxutils.RefreshOrderSkuRelated(order)
return order return order
} }
func setOrederDetailFee(result map[string]interface{}, order *model.GoodsOrder) {
orderActivities, ok := result["orderActivities"].([]interface{})
if ok {
for _, value := range orderActivities {
activity := value.(map[string]interface{})
categoryId := utils.MustInterface2Int64(activity["categoryId"])
restaurantPart := -jxutils.StandardPrice2Int(utils.MustInterface2Float64(activity["restaurantPart"]))
elemePart := -jxutils.StandardPrice2Int(utils.MustInterface2Float64(activity["elemePart"]))
if _, ok := model.ElmSkuPromotion[int(categoryId)]; ok {
order.SkuPmFee += restaurantPart
order.SkuPmSubsidy += elemePart
} else {
order.OrderPmFee += restaurantPart
order.OrderPmSubsidy += elemePart
}
}
}
order.PlatformFeeRate = int16(utils.MustInterface2Float64(result["serviceRate"]))
}
// //
func (c *PurchaseHandler) onOrderNew(msg map[string]interface{}) (response *elmapi.CallbackResponse) { func (c *PurchaseHandler) onOrderNew(msg map[string]interface{}) (response *elmapi.CallbackResponse) {
// todo 这里应该可以直接用msg里的内容而不用再次去查 // todo 这里应该可以直接用msg里的内容而不用再次去查
order, err := c.GetOrder(msg["orderId"].(string)) order, err := c.GetOrder(msg["orderId"].(string))
if err == nil { if err == nil {
order.VendorStatus = c.stateAndType2Str(order.VendorStatus, elmapi.MsgTypeOrderValid) order.VendorStatus = c.stateAndType2Str(order.VendorStatus, elmapi.MsgTypeOrderValid)
err = partner.CurOrderManager.OnOrderNew(order, c.stateAndType2Str(msg["status"].(string), elmapi.MsgTypeOrderValid)) err = partner.CurOrderManager.OnOrderNew(order, nil)
// if globals.HandleLegacyJxOrder && err == nil { // if globals.HandleLegacyJxOrder && err == nil {
// c.legacyWriteElmOrder(order) // c.legacyWriteElmOrder(order)
// } // }

View File

@@ -0,0 +1,16 @@
package elm
import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
)
// 审核售后单申请
func (c *PurchaseHandler) AgreeOrRefuseRefund(ctx *jxcontext.Context, order *model.AfsOrder, approveType int, reason string) (err error) {
return err
}
// 确认收到退货
func (c *PurchaseHandler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, order *model.AfsOrder) (err error) {
return err
}

View File

@@ -3,6 +3,8 @@ package elm
import ( import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
) )
func (p *PurchaseHandler) SyncStoreCategory(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync bool) (hint string, err error) { func (p *PurchaseHandler) SyncStoreCategory(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync bool) (hint string, err error) {
@@ -24,3 +26,11 @@ func (p *PurchaseHandler) FullSyncStoreSkus(ctx *jxcontext.Context, parentTask t
func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) { func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) {
return hint, err return hint, err
} }
func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) {
return storeSkuList, err
}
func (p *PurchaseHandler) GetStoreSkusInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, inStoreSkuList []*partner.BareStoreSkuInfo) (outStoreSkuList []*partner.BareStoreSkuInfo, err error) {
return outStoreSkuList, err
}

View File

@@ -0,0 +1,183 @@
package jd
import (
"time"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
)
func CreatePromotionInfos(promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising, traceId)
} else {
return api.JdAPI.CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising, traceId)
}
} else {
infoId = jxutils.GenFakeID()
}
return infoId, err
}
func CreatePromotionRules(promotionType int, infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
} else {
return api.JdAPI.CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
}
}
return err
}
func CreatePromotionSku(promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.CreatePromotionSkuSingle(infoId, outInfoId, skus, traceId)
} else {
return api.JdAPI.CreatePromotionSkuLimitTime(infoId, outInfoId, skus, traceId)
}
}
return skusResult, err
}
func CancelPromotionSku(promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.CancelPromotionSkuSingle(infoId, outInfoId, skus, traceId)
} else {
return api.JdAPI.CancelPromotionSkuLimitTime(infoId, outInfoId, skus, traceId)
}
}
return err
}
func ConfirmPromotion(promotionType int, infoId int64, outInfoId, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.ConfirmPromotionSingle(infoId, outInfoId, traceId)
} else {
return api.JdAPI.ConfirmPromotionLimitTime(infoId, outInfoId, traceId)
}
}
return err
}
func CancelPromotion(promotionType int, infoId int64, outInfoId, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.CancelPromotionSingle(infoId, outInfoId, traceId)
} else {
return api.JdAPI.CancelPromotionLimitTime(infoId, outInfoId, traceId)
}
}
return err
}
func AdjustPromotionTime(promotionType int, infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.AdjustPromotionTimeSingle(infoId, outInfoId, endDate, traceId)
} else {
return api.JdAPI.AdjustPromotionTimeLimitTime(infoId, outInfoId, endDate, traceId)
}
}
return err
}
func AdjustPromotionSku(promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return api.JdAPI.AdjustPromotionSkuSingle(infoId, outInfoId, skus, traceId)
} else {
return api.JdAPI.AdjustPromotionSkuLimitTime(infoId, outInfoId, skus, traceId)
}
}
return skusResult, err
}
func getTraceID(ctx *jxcontext.Context) (traceID string) {
traceID = ctx.GetUserName() + utils.GetUUID()
return traceID
}
func storeSku2Jd(actStoreSku []*model.ActStoreSku2, handler func(syncStatus int) bool) (jdActStoreSku []*jdapi.PromotionSku) {
for _, v := range actStoreSku {
if handler(v.SyncStatus) {
jdActStoreSku = append(jdActStoreSku, &jdapi.PromotionSku{
StationNo: utils.Str2Int64(v.VendorStoreID),
SkuID: utils.Str2Int64(v.VendorSkuID),
PromotionPrice: v.ActPrice,
// LimitSkuCount:0,
})
}
}
return jdActStoreSku
}
func (c *PurchaseHandler) CreateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
traceID := getTraceID(ctx)
if act.Type < model.ActOrderBegin {
outInfoID := utils.Int2Str(act.ID)
infoID, err2 := CreatePromotionInfos(act.Type, act.Name, act.BeginAt, act.EndAt, outInfoID, act.Advertising, traceID)
if err = err2; err == nil {
act.VendorActID = utils.Int64ToStr(infoID)
if err = CreatePromotionRules(act.Type, infoID, "", act.LimitDevice, act.LimitPin, act.LimitCount, act.LimitDaily, traceID); err == nil {
if _, err = CreatePromotionSku(act.Type, infoID, "", storeSku2Jd(actStoreSku, model.IsSyncStatusNeedCreate), traceID); err == nil {
err = ConfirmPromotion(act.Type, infoID, "", traceID)
}
}
}
} else {
}
return err
}
func (c *PurchaseHandler) UpdateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
traceID := getTraceID(ctx)
if act.Type < model.ActOrderBegin {
outInfoID := utils.Int2Str(act.ID)
if !utils.IsTimeZero(act.EndAt) {
if err = AdjustPromotionTime(act.Type, 0, outInfoID, act.EndAt, traceID); err != nil {
return err
}
}
if toBeDeleted := storeSku2Jd(actStoreSku, model.IsSyncStatusNeedDelete); len(toBeDeleted) > 0 {
if err = CancelPromotionSku(act.Type, 0, outInfoID, toBeDeleted, traceID); err != nil {
return err
}
}
// if toBeAdded := storeSku2Jd(actStoreSku, model.IsSyncStatusNeedDelete); len(toBeAdded) > 0 {
// if _, err = CreatePromotionSku(act.Type, 0, outInfoID, toBeAdded, traceID); err != nil {
// return err
// }
// }
// if toBeUpdated := storeSku2Jd(actStoreSku, model.IsSyncStatusNeedDelete); len(toBeUpdated) > 0 {
// if _, err = AdjustPromotionSku(act.Type, 0, outInfoID, toBeUpdated, traceID); err != nil {
// return err
// }
// }
} else {
}
return err
}
func (c *PurchaseHandler) CancelAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
traceID := getTraceID(ctx)
if act.Type < model.ActOrderBegin {
outInfoID := utils.Int2Str(act.ID)
err = CancelPromotion(act.Type, 0, outInfoID, traceID)
}
return err
}

View File

@@ -1,42 +1,26 @@
package jd package jd
import ( import (
"errors"
"git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
) )
func OnOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { func OnOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil { if curPurchaseHandler != nil {
if retVal = curPurchaseHandler.OnOrderMsg(msg); retVal == nil { retVal = curPurchaseHandler.OnOrderMsg(msg)
retVal = jdapi.Err2CallbackResponse(errors.New("Internal Error"), "")
}
} }
return retVal return retVal
} }
func OnWaybillMsg(msg *jdapi.CallbackDeliveryStatusMsg) (retVal *jdapi.CallbackResponse) { func OnWaybillMsg(msg *jdapi.CallbackDeliveryStatusMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil { if curPurchaseHandler != nil {
if retVal = curPurchaseHandler.OnWaybillMsg(msg); retVal == nil { retVal = curPurchaseHandler.OnWaybillMsg(msg)
retVal = jdapi.Err2CallbackResponse(errors.New("Internal Error"), "")
}
} }
return retVal return retVal
} }
func OnStoreMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { func OnStoreMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil { if curPurchaseHandler != nil {
retVal = curPurchaseHandler.onStoreMsg(msg) retVal = curPurchaseHandler.OnStoreMsg(msg)
}
return retVal
}
func OnAfterSaleMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil {
utils.CallFuncAsync(func() {
OnFinancialMsg(msg)
})
} }
return retVal return retVal
} }

View File

@@ -9,8 +9,15 @@ import (
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
) )
func (p *PurchaseHandler) OnFinancialMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
utils.CallFuncAsync(func() {
retVal = p.onFinancialMsg(msg)
})
return retVal
}
// 京东正向/退款订单类型处理--存储 // 京东正向/退款订单类型处理--存储
func OnFinancialMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { func (p *PurchaseHandler) onFinancialMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
var err error var err error
// if msg.StatusID == jdapi.OrderStatusPayFinishedSettle || msg.StatusID == jdapi.OrderStatusTipChanged || msg.StatusID == jdapi.OrderStatusSwitch2SelfSettle { // 如果是正向单 // if msg.StatusID == jdapi.OrderStatusPayFinishedSettle || msg.StatusID == jdapi.OrderStatusTipChanged || msg.StatusID == jdapi.OrderStatusSwitch2SelfSettle { // 如果是正向单
if msg.StatusID == jdapi.OrderStatusPayFinishedSettle || msg.StatusID == jdapi.OrderStatusTipChanged || msg.StatusID == jdapi.OrderStatusAdjustSettle || msg.StatusID == jdapi.OrderStatusSwitch2SelfSettle { // 如果是正向单 if msg.StatusID == jdapi.OrderStatusPayFinishedSettle || msg.StatusID == jdapi.OrderStatusTipChanged || msg.StatusID == jdapi.OrderStatusAdjustSettle || msg.StatusID == jdapi.OrderStatusSwitch2SelfSettle { // 如果是正向单
@@ -30,7 +37,7 @@ func OnFinancialMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse
} else { } else {
err = nil err = nil
} }
} else if msg.StatusID == jdapi.SaleBillStatusRefundSuccess || msg.StatusID == jdapi.SaleBillStatusSaleReturnSuccess { // 如果是退款单 } else if msg.StatusID == jdapi.AfsServiceStateRefundSuccess || msg.StatusID == jdapi.AfsServiceStateReturnGoodsSuccess { // 如果是退款单
orderData, err2 := api.JdAPI.GetAfsService(msg.BillID) orderData, err2 := api.JdAPI.GetAfsService(msg.BillID)
if err = err2; err == nil { if err = err2; err == nil {
err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(curPurchaseHandler.AfsOrderDetail2Financial(orderData)) err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(curPurchaseHandler.AfsOrderDetail2Financial(orderData))
@@ -127,10 +134,11 @@ func (p *PurchaseHandler) OrderDetail2Financial(orderData map[string]interface{}
orderFinancial.TotalDiscountMoney += discountPrice orderFinancial.TotalDiscountMoney += discountPrice
if xMap["orderShareRatioData"] != nil { if xMap["orderShareRatioData"] != nil {
orderShareRatioData, _ := utils.HTTPBody2Values([]byte(utils.Interface2String(xMap["orderShareRatioData"])), false) orderShareRatioData, _ := utils.HTTPBody2Values([]byte(utils.Interface2String(xMap["orderShareRatioData"])), false)
if promotionID := orderShareRatioData.Get("promotionId"); promotionID != "" {
activity := &model.OrderDiscountFinancial{ activity := &model.OrderDiscountFinancial{
VendorID: orderFinancial.VendorID, VendorID: orderFinancial.VendorID,
VendorOrderID: orderFinancial.VendorOrderID, VendorOrderID: orderFinancial.VendorOrderID,
VendorActivityID: utils.Interface2String(orderShareRatioData["promotionId"][0]), VendorActivityID: promotionID, // utils.Interface2String(orderShareRatioData["promotionId"][0]),
Type: utils.Int64ToStr(int64(discountType)), Type: utils.Int64ToStr(int64(discountType)),
// ActivityName: utils.Interface2String(xMap["discountName"]), // ActivityName: utils.Interface2String(xMap["discountName"]),
// ActivityMoney: discountPrice, // ActivityMoney: discountPrice,
@@ -141,6 +149,7 @@ func (p *PurchaseHandler) OrderDetail2Financial(orderData map[string]interface{}
// orderFinancial.JxSubsidyMoney += // orderFinancial.JxSubsidyMoney +=
} }
} }
}
globals.SugarLogger.Debug(utils.Format4Output(orderFinancial.Discounts, false)) globals.SugarLogger.Debug(utils.Format4Output(orderFinancial.Discounts, false))
} }
order1, err2 := api.JdAPI.OrderShoudSettlementService(orderFinancial.VendorOrderID) order1, err2 := api.JdAPI.OrderShoudSettlementService(orderFinancial.VendorOrderID)
@@ -154,7 +163,7 @@ func (p *PurchaseHandler) OrderDetail2Financial(orderData map[string]interface{}
orderFinancial.PmSubsidyMoney = utils.Interface2Int64WithDefault(order1["platOrderGoodsDiscountMoney"], 0) + orderFinancial.PmSkuSubsidyMoney orderFinancial.PmSubsidyMoney = utils.Interface2Int64WithDefault(order1["platOrderGoodsDiscountMoney"], 0) + orderFinancial.PmSkuSubsidyMoney
} else { } else {
if !isFromOrderDetail { if !isFromOrderDetail {
globals.SugarLogger.Warnf("jd OrderDetail2Financial, orderID:%s is not found from api.JdAPI.OrderShoudSettlementService", orderFinancial.VendorOrderID) globals.SugarLogger.Warnf("jd OrderDetail2Financial, orderID:%s is not found from api.JdAPI.OrderShoudSettlementService, err:%v", orderFinancial.VendorOrderID, err)
} }
} }
return orderFinancial, err return orderFinancial, err
@@ -168,7 +177,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interfac
VendorOrderID: utils.Interface2String(orderData["orderId"]), VendorOrderID: utils.Interface2String(orderData["orderId"]),
VendorStoreID: utils.Interface2String(orderData["stationId"]), VendorStoreID: utils.Interface2String(orderData["stationId"]),
StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(orderData["stationNumOutSystem"]), 0)), StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(orderData["stationNumOutSystem"]), 0)),
AfsCreateAt: utils.Timestamp2Time(utils.MustInterface2Int64(orderData["updateTime"].(map[string]interface{})["time"]) / 1000), AfsCreatedAt: utils.Timestamp2Time(utils.MustInterface2Int64(orderData["updateTime"].(map[string]interface{})["time"]) / 1000),
FreightUserMoney: utils.MustInterface2Int64(orderData["orderFreightMoney"]), FreightUserMoney: utils.MustInterface2Int64(orderData["orderFreightMoney"]),
AfsFreightMoney: utils.MustInterface2Int64(orderData["afsFreight"]), AfsFreightMoney: utils.MustInterface2Int64(orderData["afsFreight"]),
BoxMoney: utils.MustInterface2Int64(orderData["packagingMoney"]), BoxMoney: utils.MustInterface2Int64(orderData["packagingMoney"]),

View File

@@ -12,6 +12,6 @@ func TestOnFinancialMsg(t *testing.T) {
BillID: "907315020000322", BillID: "907315020000322",
StatusID: "330902", StatusID: "330902",
} }
res := OnFinancialMsg(msg) res := curPurchaseHandler.onFinancialMsg(msg)
fmt.Println(res) fmt.Println(res)
} }

View File

@@ -42,7 +42,7 @@ func JdStoreStatus2JxStatus(yn, closeStatus interface{}) int {
if yn2 == 1 { if yn2 == 1 {
return model.StoreStatusDisabled return model.StoreStatusDisabled
} else if closeStatus2 == 1 { } else if closeStatus2 == 1 {
return model.StoreStatusClosed return model.StoreStatusHaveRest
} }
return model.StoreStatusOpened return model.StoreStatusOpened
} }
@@ -51,7 +51,7 @@ func JxStoreStatus2JdStatus(status int) (yn, closeStatus int) {
switch status { switch status {
case model.StoreStatusDisabled: case model.StoreStatusDisabled:
return 1, 1 return 1, 1
case model.StoreStatusClosed: case model.StoreStatusHaveRest, model.StoreStatusClosed:
return 0, 1 return 0, 1
default: default:
return 0, 0 return 0, 0

View File

@@ -5,6 +5,8 @@ import (
"strings" "strings"
"time" "time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/baseapi/platformapi/autonavi" "git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
@@ -19,8 +21,8 @@ import (
var ( var (
VendorStatus2StatusMap = map[string]int{ VendorStatus2StatusMap = map[string]int{
jdapi.OrderStatusPurchased: model.OrderStatusNew, jdapi.OrderStatusPurchased: model.OrderStatusNew,
jdapi.OrderStatusNew: model.OrderStatusNew,
jdapi.OrderStatusWaitOutStore: model.OrderStatusAccepted, jdapi.OrderStatusWaitOutStore: model.OrderStatusAccepted,
jdapi.StatusIDWaitOutStore: model.OrderStatusAccepted,
jdapi.OrderStatusFinishedPickup: model.OrderStatusFinishedPickup, jdapi.OrderStatusFinishedPickup: model.OrderStatusFinishedPickup,
jdapi.OrderStatusDelivering: model.OrderStatusDelivering, jdapi.OrderStatusDelivering: model.OrderStatusDelivering,
jdapi.OrderStatusDelivered: model.OrderStatusFinished, jdapi.OrderStatusDelivered: model.OrderStatusFinished,
@@ -42,12 +44,24 @@ func (c *PurchaseHandler) OnOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi
} }
func (c *PurchaseHandler) onOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { func (c *PurchaseHandler) onOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if jdapi.OrderStatusNew == msg.StatusID {
retVal = c.onOrderNew(msg)
} else if jdapi.OrderStatusAdjust == msg.StatusID {
retVal = c.onOrderAdjust(msg)
} else {
status := c.callbackMsg2Status(msg) status := c.callbackMsg2Status(msg)
if jdapi.StatusIDNewOrder == msg.StatusID {
status.Status = model.OrderStatusNew // 因为京东将事件32000与状态32000混用事件32000可能是新订单也可能是已接单统一当成新订单处理
}
if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 {
return nil
}
if msg.MsgURL == jdapi.CallbackMsgOrderAccounting {
retVal = c.OnFinancialMsg(msg)
} else if msg.MsgURL == jdapi.CallbackMsgAfterSaleBillStatus {
retVal = c.OnAfsOrderMsg(msg)
} else {
// 新订单事件,与订单状态有点冲突
if jdapi.StatusIDNewOrder == msg.StatusID {
retVal = c.onOrderNew(msg, status)
} else if jdapi.OrderStatusAdjust == msg.StatusID {
retVal = c.onOrderAdjust(msg, status)
} else {
if msg.StatusID == jdapi.OrderStatusAddComment || msg.StatusID == jdapi.OrderStatusModifyComment { if msg.StatusID == jdapi.OrderStatusAddComment || msg.StatusID == jdapi.OrderStatusModifyComment {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
c.onOrderComment2(msg) c.onOrderComment2(msg)
@@ -59,21 +73,54 @@ func (c *PurchaseHandler) onOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi
// } // }
retVal = jdapi.Err2CallbackResponse(err, status.VendorStatus) retVal = jdapi.Err2CallbackResponse(err, status.VendorStatus)
} }
}
return retVal return retVal
} }
func (c *PurchaseHandler) getOrder(orderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) { func (c *PurchaseHandler) getOrder(orderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) {
globals.SugarLogger.Debugf("jd GetOrder orderID:%s", orderID) globals.SugarLogger.Debugf("jd getOrder orderID:%s", orderID)
if orderMap, err = api.JdAPI.QuerySingleOrder(orderID); err == nil { var (
realMobile string
orderSettlement *jdapi.OrderSettlementInfo
)
task := tasksch.NewParallelTask("jd getOrder", tasksch.NewParallelConfig().SetIsContinueWhenError(true), jxcontext.AdminCtx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
taskIndex := batchItemList[0].(int)
switch taskIndex {
case 0:
orderMap, err = api.JdAPI.QuerySingleOrder(orderID)
if err == nil {
order = c.Map2Order(orderMap) order = c.Map2Order(orderMap)
if jxutils.IsMobileFake(order.ConsigneeMobile) { realMobile, err = api.JdAPI.GetRealMobile4Order(orderID, order.VendorStoreID)
if realMobile, err := api.JdAPI.GetRealMobile4Order(orderID, order.VendorStoreID); err == nil { // 故意强制忽略取不到真实手机号错误 if realMobile != "" {
order.ConsigneeMobile2 = jxutils.FormalizeMobile(realMobile) order.ConsigneeMobile2 = jxutils.FormalizeMobile(realMobile)
} else {
// globals.SugarLogger.Warnf("jd GetOrder orderID:%s, GetRealMobile4Order failed with error:%v", orderID, err2)
} }
} }
case 1:
orderSettlement, err = api.JdAPI.OrderShoudSettlementService2(orderID)
} }
return nil, err
}, []int{0, 1})
task.Run()
task.GetResult(0)
if order != nil {
if orderSettlement != nil {
order.TotalShopMoney = orderSettlement.SettlementAmount
order.PmSubsidyMoney = orderSettlement.PlatOrderGoodsDiscountMoney + orderSettlement.PlatSkuGoodsDiscountMoney
}
}
// if orderMap, err = api.JdAPI.QuerySingleOrder(orderID); err == nil {
// globals.SugarLogger.Debugf("jd getOrder2 orderID:%s", orderID)
// order = c.Map2Order(orderMap)
// if jxutils.IsMobileFake(order.ConsigneeMobile) {
// if realMobile, err := api.JdAPI.GetRealMobile4Order(orderID, order.VendorStoreID); err == nil { // 故意强制忽略取不到真实手机号错误
// globals.SugarLogger.Debugf("jd getOrder3 orderID:%s", orderID)
// order.ConsigneeMobile2 = jxutils.FormalizeMobile(realMobile)
// } else {
// // globals.SugarLogger.Warnf("jd GetOrder orderID:%s, GetRealMobile4Order failed with error:%v", orderID, err2)
// }
// }
// }
return order, orderMap, err return order, orderMap, err
} }
@@ -85,6 +132,8 @@ func (c *PurchaseHandler) GetOrder(orderID string) (order *model.GoodsOrder, err
func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) { func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) {
result := orderData result := orderData
orderID := utils.Int64ToStr(utils.MustInterface2Int64(result["orderId"])) orderID := utils.Int64ToStr(utils.MustInterface2Int64(result["orderId"]))
globals.SugarLogger.Debugf("jd Map2Order orderID:%s", orderID)
const defaultStatusTimeField = "orderPurchaseTime" const defaultStatusTimeField = "orderPurchaseTime"
statusTimeField := defaultStatusTimeField statusTimeField := defaultStatusTimeField
if result[statusTimeField] == nil { // 814560888003021 orderPurchaseTime为空 if result[statusTimeField] == nil { // 814560888003021 orderPurchaseTime为空
@@ -108,7 +157,6 @@ func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
StatusTime: utils.Str2Time(result[statusTimeField].(string)), StatusTime: utils.Str2Time(result[statusTimeField].(string)),
OriginalData: string(utils.MustMarshal(result)), OriginalData: string(utils.MustMarshal(result)),
ActualPayPrice: utils.MustInterface2Int64(result["orderBuyerPayableMoney"]), ActualPayPrice: utils.MustInterface2Int64(result["orderBuyerPayableMoney"]),
Skus: []*model.OrderSku{},
} }
order.Status = c.GetStatusFromVendorStatus(order.VendorStatus) order.Status = c.GetStatusFromVendorStatus(order.VendorStatus)
businessTage := utils.Interface2String(result["businessTag"]) businessTage := utils.Interface2String(result["businessTag"])
@@ -143,8 +191,11 @@ func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
VendorSkuID: utils.Int64ToStr(utils.MustInterface2Int64(product["skuId"])), VendorSkuID: utils.Int64ToStr(utils.MustInterface2Int64(product["skuId"])),
SkuName: product["skuName"].(string), SkuName: product["skuName"].(string),
Weight: jxutils.FloatWeight2Int(float32(utils.MustInterface2Float64(product["skuWeight"]))), Weight: jxutils.FloatWeight2Int(float32(utils.MustInterface2Float64(product["skuWeight"]))),
VendorPrice: utils.MustInterface2Int64(product["skuStorePrice"]),
SalePrice: utils.MustInterface2Int64(product["skuJdPrice"]), SalePrice: utils.MustInterface2Int64(product["skuJdPrice"]),
PromotionType: int(utils.MustInterface2Int64(product["promotionType"])), }
if jdPromotionType := int(utils.MustInterface2Int64(product["promotionType"])); jdPromotionType != jdapi.PromotionTypeNormal {
sku.StoreSubName = utils.Int2Str(jdPromotionType)
} }
if skuCostumeProperty, ok := product["skuCostumeProperty"]; ok { if skuCostumeProperty, ok := product["skuCostumeProperty"]; ok {
sku.SkuName += skuCostumeProperty.(string) sku.SkuName += skuCostumeProperty.(string)
@@ -153,26 +204,18 @@ func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
sku.SkuType = 1 sku.SkuType = 1
} }
order.Skus = append(order.Skus, sku) order.Skus = append(order.Skus, sku)
order.SkuCount++
order.GoodsCount += sku.Count
order.SalePrice += sku.SalePrice * int64(sku.Count)
order.Weight += sku.Weight * sku.Count
} }
setOrederDetailFee(result, order) jxutils.RefreshOrderSkuRelated(order)
return order return order
} }
func setOrederDetailFee(result map[string]interface{}, order *model.GoodsOrder) {
order.BoxFee = utils.Interface2Int64WithDefault(result["packagingMoney"], 0)
order.PlatformFeeRate = model.JdPlatformFeeRate
order.BillStoreFreightFee = utils.Interface2Int64WithDefault(result["merchantPaymentDistanceFreightMoney"], 0) + utils.Interface2Int64WithDefault(result["tips"], 0)
}
// //
func (c *PurchaseHandler) onOrderNew(msg *jdapi.CallbackOrderMsg) (response *jdapi.CallbackResponse) { func (c *PurchaseHandler) onOrderNew(msg *jdapi.CallbackOrderMsg, orderStatus *model.OrderStatus) (response *jdapi.CallbackResponse) {
globals.SugarLogger.Debugf("onOrderNew orderID:%s", msg.BillID)
order, orderMap, err := c.getOrder(msg.BillID) order, orderMap, err := c.getOrder(msg.BillID)
if err == nil { if err == nil {
if err = partner.CurOrderManager.OnOrderNew(order, msg.StatusID); err == nil { globals.SugarLogger.Debugf("onOrderNew2 orderID:%s", msg.BillID)
if err = partner.CurOrderManager.OnOrderNew(order, orderStatus); err == nil {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
c.OnOrderDetail(orderMap, partner.CreatedPeration) c.OnOrderDetail(orderMap, partner.CreatedPeration)
}) })
@@ -181,10 +224,10 @@ func (c *PurchaseHandler) onOrderNew(msg *jdapi.CallbackOrderMsg) (response *jda
return jdapi.Err2CallbackResponse(err, "jd onOrderNew") return jdapi.Err2CallbackResponse(err, "jd onOrderNew")
} }
func (c *PurchaseHandler) onOrderAdjust(msg *jdapi.CallbackOrderMsg) *jdapi.CallbackResponse { func (c *PurchaseHandler) onOrderAdjust(msg *jdapi.CallbackOrderMsg, orderStatus *model.OrderStatus) *jdapi.CallbackResponse {
order, orderMap, err := c.getOrder(msg.BillID) order, orderMap, err := c.getOrder(msg.BillID)
if err == nil { if err == nil {
err = partner.CurOrderManager.OnOrderAdjust(order, msg.StatusID) err = partner.CurOrderManager.OnOrderAdjust(order, orderStatus)
if err == nil { if err == nil {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
c.OnOrderDetail(orderMap, partner.UpdatedPeration) c.OnOrderDetail(orderMap, partner.UpdatedPeration)
@@ -233,14 +276,14 @@ func (c *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptI
if globals.EnableStoreWrite { if globals.EnableStoreWrite {
err = api.JdAPI.OrderAcceptOperate(order.VendorOrderID, isAcceptIt, userName) err = api.JdAPI.OrderAcceptOperate(order.VendorOrderID, isAcceptIt, userName)
} else { } else {
c.postFakeMsg(order.VendorOrderID, jdapi.OrderStatusWaitOutStore) c.postFakeMsg(order.VendorOrderID, jdapi.StatusIDWaitOutStore)
} }
return err return err
} }
func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) {
globals.SugarLogger.Debugf("jd PickupGoods orderID:%s, isSelfDelivery:%t", order.VendorOrderID, isSelfDelivery) globals.SugarLogger.Debugf("jd PickupGoods orderID:%s, isSelfDelivery:%t", order.VendorOrderID, isSelfDelivery)
if !isSelfDelivery && globals.EnableStoreWrite { if !isSelfDelivery && globals.EnableJdStoreWrite {
_, err = api.JdAPI.OrderJDZBDelivery(order.VendorOrderID, userName) _, err = api.JdAPI.OrderJDZBDelivery(order.VendorOrderID, userName)
} else { } else {
c.postFakeMsg(order.VendorOrderID, jdapi.OrderStatusFinishedPickup) c.postFakeMsg(order.VendorOrderID, jdapi.OrderStatusFinishedPickup)
@@ -249,21 +292,21 @@ func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bo
} }
func (p *PurchaseHandler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool) (err error) { func (p *PurchaseHandler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool) (err error) {
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
err = api.JdAPI.ReceiveFailedAudit(order.VendorOrderID, isAcceptIt, ctx.GetUserName(), "") err = api.JdAPI.ReceiveFailedAudit(order.VendorOrderID, isAcceptIt, ctx.GetUserName(), "")
} }
return err return err
} }
func (p *PurchaseHandler) CallCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 拣货失败后再次招唤平台配送 func (p *PurchaseHandler) CallCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 拣货失败后再次招唤平台配送
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
err = api.JdAPI.UrgeDispatching(order.VendorOrderID, ctx.GetUserName()) err = api.JdAPI.UrgeDispatching(order.VendorOrderID, ctx.GetUserName())
} }
return err return err
} }
func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 投递失败后确认收到退货 func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 投递失败后确认收到退货
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
err = api.JdAPI.ConfirmReceiveGoods(order.VendorOrderID) err = api.JdAPI.ConfirmReceiveGoods(order.VendorOrderID)
} }
return err return err
@@ -271,7 +314,7 @@ func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *mod
func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("jd Swtich2SelfDeliver orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("jd Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
_, err = api.JdAPI.ModifySellerDelivery(order.VendorOrderID, userName) _, err = api.JdAPI.ModifySellerDelivery(order.VendorOrderID, userName)
if err != nil { if err != nil {
if errWithCode, ok := err.(*utils.ErrorWithCode); ok && errWithCode.Level() == 1 { if errWithCode, ok := err.(*utils.ErrorWithCode); ok && errWithCode.Level() == 1 {
@@ -292,7 +335,7 @@ func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName s
func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) { func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("jd Swtich2SelfDelivered orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("jd Swtich2SelfDelivered orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
_, err = api.JdAPI.DeliveryEndOrder(order.VendorOrderID, userName) _, err = api.JdAPI.DeliveryEndOrder(order.VendorOrderID, userName)
} }
return err return err
@@ -300,7 +343,7 @@ func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName
func (c *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { func (c *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("jd SelfDeliverDelivering orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("jd SelfDeliverDelivering orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
_, err = api.JdAPI.OrderSerllerDelivery(order.VendorOrderID, userName) _, err = api.JdAPI.OrderSerllerDelivery(order.VendorOrderID, userName)
} }
return err return err
@@ -309,7 +352,7 @@ func (c *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userNam
// 京东送达接口都是一样的 // 京东送达接口都是一样的
func (c *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { func (c *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Debugf("jd SelfDeliverDelivered orderID:%s", order.VendorOrderID) globals.SugarLogger.Debugf("jd SelfDeliverDelivered orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
err = c.Swtich2SelfDelivered(order, userName) err = c.Swtich2SelfDelivered(order, userName)
} }
return err return err
@@ -321,7 +364,7 @@ func (c *PurchaseHandler) GetOrderRealMobile(ctx *jxcontext.Context, order *mode
} }
func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) { func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) {
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
err = api.JdAPI.OrderCancelOperate(order.VendorOrderID, isAgree, ctx.GetUserName(), reason) err = api.JdAPI.OrderCancelOperate(order.VendorOrderID, isAgree, ctx.GetUserName(), reason)
} }
return err return err
@@ -333,29 +376,15 @@ func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.Goods
} }
func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) {
removedSkuMap := make(map[int]*model.OrderSku) order = jxutils.RemoveSkuFromOrder(order, removedSkuList)
for _, sku := range removedSkuList {
removedSkuMap[jxutils.GetSkuIDFromOrderSku(sku)] = sku
}
var oaosAdjustDTOList []*jdapi.OAOSAdjustDTO var oaosAdjustDTOList []*jdapi.OAOSAdjustDTO
for _, sku := range order.Skus { for _, sku := range order.Skus {
skuID := jxutils.GetSkuIDFromOrderSku(sku) oaosAdjustDTOList = append(oaosAdjustDTOList, &jdapi.OAOSAdjustDTO{
tmp := &jdapi.OAOSAdjustDTO{ OutSkuID: utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku)),
OutSkuID: utils.Int2Str(skuID),
SkuCount: sku.Count, SkuCount: sku.Count,
})
} }
if removedSkuMap[skuID] != nil { if globals.EnableJdStoreWrite {
if removedSkuMap[skuID].Count >= sku.Count {
tmp = nil
} else {
tmp.SkuCount -= removedSkuMap[skuID].Count
}
}
if tmp != nil {
oaosAdjustDTOList = append(oaosAdjustDTOList, tmp)
}
}
if globals.EnableStoreWrite {
err = api.JdAPI.AdjustOrder(order.VendorOrderID, ctx.GetUserName(), reason, oaosAdjustDTOList) err = api.JdAPI.AdjustOrder(order.VendorOrderID, ctx.GetUserName(), reason, oaosAdjustDTOList)
} }
return err return err

View File

@@ -0,0 +1,190 @@
package jd
import (
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
var (
AfsVendorStatus2StatusMap = map[string]int{
jdapi.AfsServiceStateWaiting4Audit: model.AfsOrderStatusWait4Approve, // 需要审核
jdapi.AfsServiceStateRefundProcessing: model.AfsOrderStatusNew,
jdapi.AfsServiceStateWaiting4DirectCompensate: model.AfsOrderStatusNew,
jdapi.AfsServiceStateWaiting4ReturnGoods: model.AfsOrderStatusNew,
jdapi.AfsServiceStateWaiting4MerchantReceiveGoods: model.AfsOrderStatusWait4ReceiveGoods,
jdapi.AfsServiceStateRefundSuccess: model.AfsOrderStatusFinished,
jdapi.AfsServiceStateSolved: model.AfsOrderStatusFinished,
jdapi.AfsServiceStateDirectCompensateSuccess: model.AfsOrderStatusFinished,
jdapi.AfsServiceStateReturnGoodsSuccess: model.AfsOrderStatusFinished,
jdapi.AfsServiceStateRefundFailed: model.AfsOrderStatusFailed,
jdapi.AfsServiceStateAuditRefused: model.AfsOrderStatusFailed,
jdapi.AfsServiceStateUserCanceled: model.AfsOrderStatusFailed,
jdapi.AfsServiceStateMerchantFailedReceiveGoods: model.AfsOrderStatusFailed,
jdapi.AfsServiceStateDirectCompensateFailed: model.AfsOrderStatusFailed,
jdapi.AfsServiceStateReturnGoodsFailed: model.AfsOrderStatusFailed,
}
afsReasonTypeMap = map[int]int8{
jdapi.AfsReasonTypeGoodsQuality: model.AfsReasonTypeGoodsQuality,
jdapi.AfsReasonTypeWrongGoods: model.AfsReasonTypeWrongGoods,
jdapi.AfsReasonTypeMissingGoods: model.AfsReasonTypeMissingGoods,
jdapi.AfsReasonTypeNoGoods: model.AfsReasonTypeNoGoods,
jdapi.AfsReasonTypeDamagedGoods: model.AfsReasonTypeDamagedGoods,
jdapi.AfsReasonTypeGoodsQuantity: model.AfsReasonTypeGoodsQuantity,
jdapi.AfsReasonTypeAgreedByMerchant: model.AfsReasonTypeAgreedByMerchant,
jdapi.AfsReasonTypeGoodsSizeNoSame: model.AfsReasonTypeGoodsNoSame,
jdapi.AfsReasonTypeGoodsColorNoSame: model.AfsReasonTypeGoodsNoSame,
jdapi.AfsReasonWrongPurchase: model.AfsReasonWrongPurchase,
jdapi.AfsReasonNotReceivedIntime: model.AfsReasonNotReceivedIntime,
}
afsAppealTypeMap = map[string]int8{
jdapi.AfsDealTypeRefund: model.AfsAppealTypeRefund,
jdapi.AfsDealTypeReturnGoodsRefund: model.AfsAppealTypeReturnAndRefund,
jdapi.AfsDealTypeDirectCompensate: model.AfsAppealTypeNewGoods,
}
afsApproveTypeMap = map[int]int{
partner.AfsApproveTypeRefund: jdapi.AfsApproveTypeRefund,
partner.AfsApproveTypeReturnGoods: jdapi.AfsApproveTypeReturnGoods,
partner.AfsApproveTypeRefused: jdapi.AfsApproveTypeRefused,
}
)
func (c *PurchaseHandler) OnAfsOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
jxutils.CallMsgHandlerAsync(func() {
retVal = c.onAfsOrderMsg(msg)
}, jxutils.ComposeUniversalOrderID(msg.BillID, model.VendorIDJD))
return retVal
}
func (c *PurchaseHandler) onAfsOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
afsInfo, err := api.JdAPI.GetAfsService2(msg.BillID)
if err == nil {
status := c.callbackAfsMsg2Status(msg, afsInfo)
if status.Status == model.AfsOrderStatusWait4Approve || status.Status == model.AfsOrderStatusNew {
afsOrder := c.buildAfsOrder(afsInfo)
err = partner.CurOrderManager.OnAfsOrderNew(afsOrder, status)
} else {
err = partner.CurOrderManager.OnAfsOrderStatusChanged(status)
}
retVal = jdapi.Err2CallbackResponse(err, status.VendorStatus)
}
return retVal
}
func (c *PurchaseHandler) callbackAfsMsg2Status(msg *jdapi.CallbackOrderMsg, afsInfo *jdapi.AfsServiceResponse) *model.OrderStatus {
orderStatus := &model.OrderStatus{
VendorOrderID: msg.BillID, // 是售后单ID不是订单ID订单ID在RefVendorOrderID中
VendorID: model.VendorIDJD,
OrderType: model.OrderTypeAfsOrder,
RefVendorOrderID: afsInfo.OrderID,
RefVendorID: model.VendorIDJD,
VendorStatus: msg.StatusID,
Status: c.GetAfsStatusFromVendorStatus(msg.StatusID),
StatusTime: utils.Str2Time(msg.Timestamp),
Remark: msg.Remark,
}
return orderStatus
}
func (c *PurchaseHandler) GetAfsStatusFromVendorStatus(vendorStatus string) int {
if status, ok := AfsVendorStatus2StatusMap[vendorStatus]; ok {
return status
}
return model.OrderStatusUnknown
}
func (c *PurchaseHandler) convertAfsReasonType(vendorReasonType int) int8 {
if status, ok := afsReasonTypeMap[vendorReasonType]; ok {
return status
}
return model.AfsReasonNotOthers
}
func (c *PurchaseHandler) convertAfsAppealType(vendorAppealType string) int8 {
if status, ok := afsAppealTypeMap[vendorAppealType]; ok {
return status
}
globals.SugarLogger.Warnf("jd convertAfsAppealType unknown vendorAppealType:%d", vendorAppealType)
return 0
}
func (c *PurchaseHandler) buildAfsOrder(afsInfo *jdapi.AfsServiceResponse) (afsOrder *model.AfsOrder) {
afsOrder = &model.AfsOrder{
VendorID: model.VendorIDJD,
AfsOrderID: afsInfo.AfsServiceOrder,
VendorOrderID: afsInfo.OrderID,
VendorStoreID: afsInfo.StationID,
StoreID: int(utils.Str2Int64WithDefault(afsInfo.StationNumOutSystem, 0)),
AfsCreatedAt: afsInfo.CreateTime.GoTime(),
FreightUserMoney: afsInfo.OrderFreightMoney,
AfsFreightMoney: afsInfo.AfsFreight,
BoxMoney: afsInfo.PackagingMoney,
TongchengFreightMoney: afsInfo.TongchengFreightMoney,
SkuBoxMoney: afsInfo.MealBoxMoney,
VendorStatus: utils.Int2Str(afsInfo.AfsServiceState),
VendorReasonType: utils.Int2Str(afsInfo.QuestionTypeCid),
ReasonType: c.convertAfsReasonType(afsInfo.QuestionTypeCid),
ReasonDesc: utils.LimitUTF8StringLen(afsInfo.QuestionDesc, 1024),
ReasonImgList: utils.LimitUTF8StringLen(jdapi.ProcessQuestionPic(afsInfo.QuestionPic), 1024),
VendorAppealType: afsInfo.ApplyDeal,
AppealType: c.convertAfsAppealType(afsInfo.ApplyDeal),
}
afsOrder.Status = c.GetAfsStatusFromVendorStatus(afsOrder.VendorStatus)
for _, x := range afsInfo.AfsDetailList {
orderSku := &model.OrderSkuFinancial{
// VendorID: model.VendorIDJD,
// AfsOrderID: afsOrder.AfsOrderID,
// VendorOrderID: afsOrder.VendorOrderID,
// VendorStoreID: afsOrder.VendorStoreID,
// StoreID: afsOrder.StoreID,
// IsAfsOrder: 1,
Count: x.SkuCount,
// ConfirmTime: afsOrder.AfsCreateAt,
VendorSkuID: utils.Int64ToStr(x.WareID),
SkuID: int(utils.Str2Int64WithDefault(x.SkuIDIsv, 0)),
Name: x.WareName,
UserMoney: x.AfsMoney,
PmSkuSubsidyMoney: x.PlatPayMoney,
}
afsOrder.PmSkuSubsidyMoney += orderSku.PmSkuSubsidyMoney
orderSku.PmSubsidyMoney += orderSku.PmSkuSubsidyMoney
for _, y := range x.AfsSkuDiscountList {
orderSku.PmSubsidyMoney += y.PlatPayMoney
}
afsOrder.SkuUserMoney += orderSku.UserMoney
afsOrder.PmSubsidyMoney += orderSku.PmSubsidyMoney
afsOrder.Skus = append(afsOrder.Skus, orderSku)
}
return afsOrder
}
// 审核售后单申请
func (c *PurchaseHandler) AgreeOrRefuseRefund(ctx *jxcontext.Context, order *model.AfsOrder, approveType int, reason string) (err error) {
if globals.EnableJdStoreWrite {
err = api.JdAPI.AfsOpenApprove(order.AfsOrderID, afsApproveTypeMap[approveType], reason, ctx.GetUserName())
}
return err
}
// 确认收到退货
func (c *PurchaseHandler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, order *model.AfsOrder) (err error) {
if globals.EnableJdStoreWrite {
err = api.JdAPI.ConfirmReceipt(order.AfsOrderID, ctx.GetUserName())
}
return err
}

View File

@@ -3,6 +3,7 @@ package jd
// 这里函数取得的信息除了与自身实体相关的ID比如PARENT ID都已经转换成了本地ID了 // 这里函数取得的信息除了与自身实体相关的ID比如PARENT ID都已经转换成了本地ID了
import ( import (
"fmt"
"unicode/utf8" "unicode/utf8"
"git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/jdapi"
@@ -130,7 +131,7 @@ func (p *PurchaseHandler) ReorderCategories(db *dao.DaoDB, parentCatID int, user
func (p *PurchaseHandler) cuSku(db *dao.DaoDB, sku *model.Sku, handler func(skuExt *tSkuInfoExt, price int, skuName string, shopCategories []int64, addParams map[string]interface{}) (string, error)) (err error) { func (p *PurchaseHandler) cuSku(db *dao.DaoDB, sku *model.Sku, handler func(skuExt *tSkuInfoExt, price int, skuName string, shopCategories []int64, addParams map[string]interface{}) (string, error)) (err error) {
var skuInfoExt tSkuInfoExt var skuInfoExt tSkuInfoExt
err = dao.GetRow(nil, &skuInfoExt, ` err = dao.GetRow(nil, &skuInfoExt, `
SELECT t2.*, t3.jd_id jd_cat_id, t3.jd_category_id, t4.jd_id sku_cat_id SELECT t2.*, t3.jd_id jd_cat_id, IF(t2.jd_category_id > 0, t2.jd_category_id, t3.jd_category_id) jd_category_id, t4.jd_id sku_cat_id
FROM sku t1 FROM sku t1
JOIN sku_name t2 ON t1.name_id = t2.id JOIN sku_name t2 ON t1.name_id = t2.id
JOIN sku_category t3 ON t2.category_id = t3.id JOIN sku_category t3 ON t2.category_id = t3.id
@@ -164,8 +165,14 @@ func (p *PurchaseHandler) cuSku(db *dao.DaoDB, sku *model.Sku, handler func(skuE
if addParams["sellCities"] == nil { if addParams["sellCities"] == nil {
addParams["sellCities"] = []int{0} addParams["sellCities"] = []int{0}
} }
if skuInfoExt.DescImg != "" {
addParams[jdapi.KeyProductDesc] = fmt.Sprintf(`<img src="%s" alt="一张图片" />`, skuInfoExt.DescImg)
addParams[jdapi.KeyIfViewDesc] = 0
} else {
addParams[jdapi.KeyIfViewDesc] = 1
}
if err == nil { if err == nil {
skuName := jxutils.ComposeSkuName(skuInfoExt.Prefix, skuInfoExt.Name, sku.Comment, skuInfoExt.Unit, sku.SpecQuality, sku.SpecUnit, 0) skuName := jxutils.ComposeSkuName(skuInfoExt.Prefix, skuInfoExt.Name, sku.Comment, skuInfoExt.Unit, sku.SpecQuality, sku.SpecUnit, jdapi.MaxSkuNameCharCount)
skuPrice := jxutils.CaculateSkuPrice(skuInfoExt.Price, sku.SpecQuality, sku.SpecUnit, skuInfoExt.Unit) skuPrice := jxutils.CaculateSkuPrice(skuInfoExt.Price, sku.SpecQuality, sku.SpecUnit, skuInfoExt.Unit)
if skuInfoExt.Upc != "" { if skuInfoExt.Upc != "" {
addParams[jdapi.KeyUpcCode] = skuInfoExt.Upc addParams[jdapi.KeyUpcCode] = skuInfoExt.Upc
@@ -385,7 +392,7 @@ func (p *PurchaseHandler) syncSkuNameAsSpu(db *dao.DaoDB, sku *model.Sku, skuExt
skuNameJdID := skuExt.JdID skuNameJdID := skuExt.JdID
globals.SugarLogger.Debugf("syncSkuNameAsSpu1 sku.id=%d, bareSkuName:%s, skuName:%s, skuNameJdID:%d", sku.ID, skuExt.Name, skuName, skuNameJdID) globals.SugarLogger.Debugf("syncSkuNameAsSpu1 sku.id=%d, bareSkuName:%s, skuName:%s, skuNameJdID:%d", sku.ID, skuExt.Name, skuName, skuNameJdID)
spuAddParams, skuAddParams := splitAddParams(addParams) spuAddParams, skuAddParams := splitAddParams(addParams)
if !jxutils.IsFakeID(skuNameJdID) && sku.JdSyncStatus&model.SyncFlagDeletedMask != 0 { // 删除SKU if !jxutils.IsEmptyID(skuNameJdID) && sku.JdSyncStatus&model.SyncFlagDeletedMask != 0 { // 删除SKU
if globals.EnableStoreWrite { if globals.EnableStoreWrite {
err = api.JdAPI.UpdateSkuBaseInfo(utils.Int2Str(skuExt.ID), utils.Int2Str(sku.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusDeleted)) err = api.JdAPI.UpdateSkuBaseInfo(utils.Int2Str(skuExt.ID), utils.Int2Str(sku.ID), utils.Params2Map(jdapi.KeyFixedStatus, jdapi.SkuFixedStatusDeleted))
} }
@@ -412,7 +419,7 @@ func (p *PurchaseHandler) syncSkuNameAsSpu(db *dao.DaoDB, sku *model.Sku, skuExt
} }
} }
} }
} else if skuExt.JdSyncStatus&model.SyncFlagNewMask != 0 && jxutils.IsFakeID(skuNameJdID) { } else if skuExt.JdSyncStatus&model.SyncFlagNewMask != 0 && jxutils.IsEmptyID(skuNameJdID) {
if globals.EnableStoreWrite { if globals.EnableStoreWrite {
spuName := jxutils.ComposeSpuName(skuExt.Prefix, skuExt.Name, 0) spuName := jxutils.ComposeSpuName(skuExt.Prefix, skuExt.Name, 0)
skus := []map[string]interface{}{ skus := []map[string]interface{}{
@@ -461,7 +468,7 @@ func (p *PurchaseHandler) syncSkuNameAsSpu(db *dao.DaoDB, sku *model.Sku, skuExt
} }
} }
} }
if err == nil && !jxutils.IsFakeID(skuNameJdID) { if err == nil && !jxutils.IsEmptyID(skuNameJdID) {
if sku.JdSyncStatus&model.SyncFlagNewMask != 0 { // 非首次新增SKU if sku.JdSyncStatus&model.SyncFlagNewMask != 0 { // 非首次新增SKU
if globals.EnableStoreWrite { if globals.EnableStoreWrite {
vendorSkuID2, err2 := api.JdAPI.AppendSku(utils.Int2Str(skuExt.ID), utils.Int2Str(sku.ID), skuName, price, jxutils.IntWeight2Float(sku.Weight), []string{skuExt.Img}, jxStatus2jdStatus(sku.Status), true, composeSkuSpec(sku.SpecQuality, sku.SpecUnit, skuExt.Unit), skuAddParams) vendorSkuID2, err2 := api.JdAPI.AppendSku(utils.Int2Str(skuExt.ID), utils.Int2Str(sku.ID), skuName, price, jxutils.IntWeight2Float(sku.Weight), []string{skuExt.Img}, jxStatus2jdStatus(sku.Status), true, composeSkuSpec(sku.SpecQuality, sku.SpecUnit, skuExt.Unit), skuAddParams)

View File

@@ -137,7 +137,7 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
if store.DeliveryRangeType == model.DeliveryRangeTypePolygon { if store.DeliveryRangeType == model.DeliveryRangeTypePolygon {
params["coordinatePoints"] = store.DeliveryRange params["coordinatePoints"] = store.DeliveryRange
} else { } else {
params["deliveryRangeRadius"] = utils.Str2Int64(store.DeliveryRange) params["deliveryRangeRadius"] = utils.Str2Int64WithDefault(store.DeliveryRange, 0)
} }
} }
openTime2 := JxOperationTime2JdOperationTime(store.OpenTime2) openTime2 := JxOperationTime2JdOperationTime(store.OpenTime2)
@@ -147,7 +147,7 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
} }
_, params["closeStatus"] = JxStoreStatus2JdStatus(jxutils.MergeStoreStatus(store.Status, store.JdStoreStatus)) _, params["closeStatus"] = JxStoreStatus2JdStatus(jxutils.MergeStoreStatus(store.Status, store.JdStoreStatus))
globals.SugarLogger.Debug(utils.Format4Output(params, false)) globals.SugarLogger.Debug(utils.Format4Output(params, false))
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
if err = api.JdAPI.UpdateStoreInfo4Open(store.VendorStoreID, store.RealLastOperator, params); err != nil { if err = api.JdAPI.UpdateStoreInfo4Open(store.VendorStoreID, store.RealLastOperator, params); err != nil {
return err return err
} }
@@ -238,7 +238,7 @@ func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask
if step != stepCount-1 { if step != stepCount-1 {
storeParams["outSystemId"] = utils.GetUUID() storeParams["outSystemId"] = utils.GetUUID()
} }
if true { //globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
err = api.JdAPI.UpdateStoreInfo4Open(store.VendorStoreID, ctx.GetUserName(), storeParams) err = api.JdAPI.UpdateStoreInfo4Open(store.VendorStoreID, ctx.GetUserName(), storeParams)
} }
return nil, err return nil, err
@@ -302,7 +302,7 @@ func (p *PurchaseHandler) GetStoreStatus(ctx *jxcontext.Context, vendorStoreID s
} }
// 当前京东的storeCrud消息不会在门店状态改变时发送所以意义不大先放在这里 // 当前京东的storeCrud消息不会在门店状态改变时发送所以意义不大先放在这里
func (c *PurchaseHandler) onStoreMsg(msg *jdapi.CallbackOrderMsg) (response *jdapi.CallbackResponse) { func (c *PurchaseHandler) OnStoreMsg(msg *jdapi.CallbackOrderMsg) (response *jdapi.CallbackResponse) {
var err error var err error
if msg.StatusID == jdapi.StatusIDUpdateStore { if msg.StatusID == jdapi.StatusIDUpdateStore {
var storeStatus int var storeStatus int

View File

@@ -3,6 +3,8 @@ package jd
import ( import (
"fmt" "fmt"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
@@ -18,60 +20,48 @@ import (
type tStoreSkuBindExt struct { type tStoreSkuBindExt struct {
model.StoreSkuBind model.StoreSkuBind
PricePercentage int PricePercentage int
CatPricePercentage int
VendorStoreID string `orm:"column(vendor_store_id)"` VendorStoreID string `orm:"column(vendor_store_id)"`
JdID int64 `orm:"column(jd_id)"` JdID int64 `orm:"column(jd_id)"`
} }
// 京东到家,以有库存表示关注(认领) // 京东到家,以有库存表示关注(认领)
func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { func (p *PurchaseHandler) syncStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, db *dao.DaoDB, storeID int, storeSkus []*dao.StoreSkuSyncInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debugf("jd SyncStoresSkus, storeID:%d, skuIDs:%v", storeID, skuIDs) globals.SugarLogger.Debugf("jd syncStoreSkus, storeID:%d", storeID)
sqlWhere0 := ` storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJD)
WHERE (t1.jd_sync_status <> 0) AND t1.store_id = ? if err != nil {
`
sqlWhere := sqlWhere0
sqlWhereParams := []interface{}{
storeID,
}
if len(skuIDs) > 0 {
sqlWhere += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
sqlWhereParams = append(sqlWhereParams, skuIDs)
}
sql := `
SELECT t3.jd_id, t1.*, t2.price_percentage, t2.vendor_store_id
FROM store_sku_bind t1
JOIN store_map t2 ON t1.store_id = t2.store_id AND t2.vendor_id = ? AND t2.deleted_at = ?
JOIN sku t3 ON t1.sku_id = t3.id AND t3.deleted_at = ?
` + sqlWhere + " ORDER BY t1.updated_at"
var storeSkus []*tStoreSkuBindExt
sqlParams := []interface{}{
model.VendorIDJD,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
}
db := dao.GetDB()
if err = dao.GetRows(db, &storeSkus, sql, append(sqlParams, sqlWhereParams...)...); err != nil {
return "", err return "", err
} }
task := tasksch.NewParallelTask("SyncStoresSkus京东", tasksch.NewParallelConfig().SetBatchSize(jdapi.MaxStoreSkuBatchSize).SetIsContinueWhenError(isContinueWhenError), ctx, batchSize := jdapi.MaxStoreSkuBatchSize
// storeSkusLen := len(storeSkus)
// if storeSkusLen < jdapi.MaxStoreSkuBatchSize/2 {
// batchSize = 1
// } else if storeSkusLen < jdapi.MaxStoreSkuBatchSize {
// batchSize = (storeSkusLen + 1) / 2
// } else if storeSkusLen < jdapi.MaxStoreSkuBatchSize*2 {
// batchSize = (storeSkusLen + 2) / 3
// }
task := tasksch.NewParallelTask("syncStoreSkus京东", tasksch.NewParallelConfig().SetBatchSize(batchSize).SetIsContinueWhenError(isContinueWhenError), ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
doWork := func(batchItemList []interface{}) (isPartialFailed bool, err error) {
var skuPriceInfoList []*jdapi.SkuPriceInfo var skuPriceInfoList []*jdapi.SkuPriceInfo
var skuVendibilityList []*jdapi.StockVendibility var skuVendibilityList []*jdapi.StockVendibility
var skuStockList []*jdapi.SkuStock var skuStockList []*jdapi.SkuStock
stationNo := batchItemList[0].(*tStoreSkuBindExt).VendorStoreID stationNo := storeDetail.VendorStoreID
var batchSkuIDs []int var batchBindIDs []int
for _, v := range batchItemList { for _, v := range batchItemList {
storeSku := v.(*tStoreSkuBindExt) storeSku := v.(*dao.StoreSkuSyncInfo)
alreadyAddStock := false alreadyAddStock := false
if storeSku.JdSyncStatus&model.SyncFlagChangedMask != 0 { if storeSku.StoreSkuSyncStatus&model.SyncFlagChangedMask != 0 || storeSku.BindID == 0 || storeSku.NameID == 0 {
batchSkuIDs = append(batchSkuIDs, storeSku.SkuID) if storeSku.BindID > 0 {
if storeSku.JdSyncStatus&(model.SyncFlagDeletedMask|model.SyncFlagNewMask) != 0 { // 关注或取消关注 batchBindIDs = append(batchBindIDs, storeSku.BindID)
}
if storeSku.StoreSkuSyncStatus&(model.SyncFlagDeletedMask|model.SyncFlagNewMask) != 0 || storeSku.BindID == 0 || storeSku.NameID == 0 { // 关注或取消关注
stock := &jdapi.SkuStock{ stock := &jdapi.SkuStock{
OutSkuId: utils.Int2Str(storeSku.SkuID), OutSkuId: utils.Int2Str(storeSku.SkuID),
StockQty: model.MaxStoreSkuStockQty, StockQty: model.MaxStoreSkuStockQty,
} }
if storeSku.DeletedAt != utils.DefaultTimeValue { if storeSku.StoreSkuSyncStatus&model.SyncFlagDeletedMask != 0 || storeSku.DeletedAt != utils.DefaultTimeValue || storeSku.BindID == 0 || storeSku.NameID == 0 {
stock.StockQty = 0 stock.StockQty = 0
} else { } else {
alreadyAddStock = true alreadyAddStock = true
@@ -80,18 +70,18 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
skuStockList = append(skuStockList, stock) skuStockList = append(skuStockList, stock)
} }
} }
if storeSku.JdSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 { if storeSku.StoreSkuSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
skuPriceInfoList = append(skuPriceInfoList, &jdapi.SkuPriceInfo{ skuPriceInfoList = append(skuPriceInfoList, &jdapi.SkuPriceInfo{
OutSkuId: utils.Int2Str(storeSku.SkuID), OutSkuId: utils.Int2Str(storeSku.SkuID),
Price: constrainPrice(jxutils.CaculateSkuVendorPrice(storeSku.Price, storeSku.PricePercentage)), Price: constrainPrice(jxutils.CaculateSkuVendorPrice(int(storeSku.Price), int(storeDetail.PricePercentage), storeSku.CatPricePercentage)),
}) })
} }
if storeSku.JdSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 { if storeSku.StoreSkuSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
vendibility := &jdapi.StockVendibility{ vendibility := &jdapi.StockVendibility{
OutSkuId: utils.Int2Str(storeSku.SkuID), OutSkuId: utils.Int2Str(storeSku.SkuID),
DoSale: true, DoSale: true,
} }
if storeSku.Status != model.StoreSkuBindStatusNormal { if storeSku.StoreSkuStatus != model.StoreSkuBindStatusNormal {
vendibility.DoSale = false vendibility.DoSale = false
} else if !alreadyAddStock { // 如果是设置可售则自动将库存加满 } else if !alreadyAddStock { // 如果是设置可售则自动将库存加满
stock := &jdapi.SkuStock{ stock := &jdapi.SkuStock{
@@ -108,7 +98,7 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
} }
syncMask := 0 syncMask := 0
errList := []error{} errList := []error{}
if globals.EnableStoreWrite { if globals.EnableJdStoreWrite {
// todo 以下可以优化为并行操作 // todo 以下可以优化为并行操作
globals.SugarLogger.Debug(utils.Format4Output(skuVendibilityList, false), utils.Format4Output(skuPriceInfoList, false), utils.Format4Output(skuStockList, false)) globals.SugarLogger.Debug(utils.Format4Output(skuVendibilityList, false), utils.Format4Output(skuPriceInfoList, false), utils.Format4Output(skuStockList, false))
if len(skuVendibilityList) > 0 { if len(skuVendibilityList) > 0 {
@@ -129,20 +119,21 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
if _, err = api.JdAPI.UpdateVendorStationPrice("", stationNo, skuPriceInfoList); err == nil { if _, err = api.JdAPI.UpdateVendorStationPrice("", stationNo, skuPriceInfoList); err == nil {
syncMask |= model.SyncFlagPriceMask syncMask |= model.SyncFlagPriceMask
} else { } else {
errList = append(errList, err) isPartialFailed = isErrPartialFailed(err)
errList = append(errList, partner.NewErrorCode(err.Error(), partner.ErrCodeChangePriceFailed, model.VendorIDJD))
} }
} }
} }
if len(errList) == 0 { if len(errList) == 0 {
syncMask = -1 syncMask = -1
} }
if syncMask != 0 && len(batchSkuIDs) > 0 { if syncMask != 0 && len(batchBindIDs) > 0 {
db := dao.GetDB() // 多线程问题 // db := dao.GetDB() // 多线程问题
sql := ` sql := `
UPDATE store_sku_bind t1 UPDATE store_sku_bind t1
SET t1.jd_sync_status = t1.jd_sync_status & ? SET t1.jd_sync_status = t1.jd_sync_status & ?
` + sqlWhere0 + " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(batchSkuIDs)) + ")" WHERE t1.id IN (` + dao.GenQuestionMarks(len(batchBindIDs)) + ")"
if _, err = dao.ExecuteSQL(db, sql, ^syncMask, storeID, batchSkuIDs); err != nil { if _, err = dao.ExecuteSQL(db, sql, ^syncMask, batchBindIDs); err != nil {
errList = append(errList, err) errList = append(errList, err)
} }
} }
@@ -151,6 +142,14 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
} else if len(errList) > 1 { } else if len(errList) > 1 {
err = fmt.Errorf("%v", errList) err = fmt.Errorf("%v", errList)
} }
return isPartialFailed, err
}
isErrPartialFailed, err := doWork(batchItemList)
if isErrPartialFailed && len(batchItemList) > 1 {
for _, v := range batchItemList {
doWork([]interface{}{v})
}
}
return nil, err return nil, err
}, storeSkus) }, storeSkus)
tasksch.HandleTask(task, parentTask, false).Run() tasksch.HandleTask(task, parentTask, false).Run()
@@ -160,112 +159,35 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
return task.ID, err return task.ID, err
} }
func isErrPartialFailed(err error) bool {
if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.Code() == jdapi.ResponseInnerCodePartialFailed {
return true
}
return false
}
func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debugf("jd SyncStoresSkus, storeID:%d, skuIDs:%v", storeID, skuIDs)
db := dao.GetDB()
storeSkus, err := dao.GetStoreSkus(db, model.VendorIDJD, storeID, skuIDs)
if err != nil {
return "", err
}
return p.syncStoreSkus(ctx, parentTask, db, storeID, storeSkus, isAsync, isContinueWhenError)
}
func (p *PurchaseHandler) FullSyncStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) { func (p *PurchaseHandler) FullSyncStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debugf("jd FullSyncStoreSkus, storeID:%d", storeID) globals.SugarLogger.Debugf("jd FullSyncStoreSkus, storeID:%d", storeID)
db := dao.GetDB() db := dao.GetDB()
_, err = dao.SetStoreSkuSyncStatus(db, model.VendorIDJD, storeID, nil, model.SyncFlagModifiedMask|model.SyncFlagPriceMask|model.SyncFlagSaleMask) _, err = dao.SetStoreSkuSyncStatus(db, model.VendorIDJD, []int{storeID}, nil, model.SyncFlagStoreSkuOnlyMask)
if err != nil { if err != nil {
return "", err return "", err
} }
skus, err := dao.GetFullStoreSkus(db, model.VendorIDJD, storeID) storeSkus, err := dao.GetFullStoreSkus(db, model.VendorIDJD, storeID)
if err != nil { if err != nil {
return "", err return "", err
} }
return p.syncStoreSkus(ctx, parentTask, db, storeID, skus, isAsync, isContinueWhenError) return p.syncStoreSkus(ctx, parentTask, db, storeID, storeSkus, isAsync, isContinueWhenError)
}
// todo 之后应该与SyncStoreSkus合并
func (p *PurchaseHandler) syncStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, db *dao.DaoDB, storeID int, skus []*dao.StoreSkuSyncInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debugf("jd syncStoreSkus, storeID:%d, len(skus):%d", storeID, len(skus))
if len(skus) == 0 {
return "", nil
}
storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJD)
if err != nil {
return "", err
}
stationNo := storeDetail.VendorStoreID
task := tasksch.NewParallelTask("SyncStoresSkus京东", tasksch.NewParallelConfig().SetBatchSize(jdapi.MaxStoreSkuBatchSize).SetIsContinueWhenError(isContinueWhenError), ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
var skuPriceInfoList []*jdapi.SkuPriceInfo
var skuVendibilityList []*jdapi.StockVendibility
var skuStockList []*jdapi.SkuStock
var batchSkuIDs []int
for _, v := range batchItemList {
storeSku := v.(*dao.StoreSkuSyncInfo)
alreadyAddStock := false
if storeSku.SkuSyncStatus&model.SyncFlagChangedMask != 0 || storeSku.BindID == 0 {
if storeSku.BindID != 0 {
batchSkuIDs = append(batchSkuIDs, storeSku.BindID)
}
if storeSku.SkuSyncStatus&(model.SyncFlagDeletedMask|model.SyncFlagNewMask) != 0 || storeSku.BindID == 0 { // 关注或取消关注
stock := &jdapi.SkuStock{
OutSkuId: utils.Int2Str(storeSku.ID),
StockQty: model.MaxStoreSkuStockQty,
}
if storeSku.DeletedAt != utils.DefaultTimeValue || storeSku.BindID == 0 {
stock.StockQty = 0
} else {
alreadyAddStock = true
}
if stock.StockQty != 0 || !storeskulock.IsJdStoreSkuLocked(stationNo, storeSku.JdID) {
skuStockList = append(skuStockList, stock)
}
}
if storeSku.SkuSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
skuPriceInfoList = append(skuPriceInfoList, &jdapi.SkuPriceInfo{
OutSkuId: utils.Int2Str(storeSku.ID),
Price: constrainPrice(jxutils.CaculateSkuVendorPrice(int(storeSku.Price), int(storeDetail.PricePercentage))),
})
}
if storeSku.SkuSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
vendibility := &jdapi.StockVendibility{
OutSkuId: utils.Int2Str(storeSku.ID),
DoSale: true,
}
if storeSku.StoreSkuStatus != model.StoreSkuBindStatusNormal {
vendibility.DoSale = false
} else if !alreadyAddStock { // 如果是设置可售则自动将库存加满
stock := &jdapi.SkuStock{
OutSkuId: utils.Int2Str(storeSku.ID),
StockQty: model.MaxStoreSkuStockQty,
}
skuStockList = append(skuStockList, stock)
}
if vendibility.DoSale || !storeskulock.IsJdStoreSkuLocked(stationNo, storeSku.JdID) {
skuVendibilityList = append(skuVendibilityList, vendibility)
}
}
}
}
globals.SugarLogger.Debugf("jd syncStoreSkus sync detail, storeID:%d, skuVendibilityList:%s, skuPriceInfoList:%s, skuStockList:%s", storeID, utils.Format4Output(skuVendibilityList, true), utils.Format4Output(skuPriceInfoList, true), utils.Format4Output(skuStockList, true))
if globals.EnableStoreWrite {
// todo 以下可以优化为并行操作
if len(skuVendibilityList) > 0 {
_, err = api.JdAPI.BatchUpdateVendibility("", stationNo, skuVendibilityList, ctx.GetUserName())
}
if err == nil && len(skuStockList) > 0 {
_, err = api.JdAPI.BatchUpdateCurrentQtys("", stationNo, skuStockList, ctx.GetUserName())
}
if err == nil && len(skuPriceInfoList) > 0 {
_, err = api.JdAPI.UpdateVendorStationPrice("", stationNo, skuPriceInfoList)
}
}
if err == nil && len(batchSkuIDs) > 0 {
db := dao.GetDB() // 多线程问题
sql := `
UPDATE store_sku_bind t1
SET t1.jd_sync_status = 0
WHERE t1.id IN (` + dao.GenQuestionMarks(len(batchSkuIDs)) + ")"
_, err = dao.ExecuteSQL(db, sql, batchSkuIDs)
}
return nil, err
}, skus)
tasksch.HandleTask(task, parentTask, false).Run()
if !isAsync {
_, err = task.GetResult(0)
}
return task.ID, err
} }
func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) { func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) {
@@ -278,3 +200,117 @@ func constrainPrice(price int) int {
} }
return price return price
} }
func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) {
db := dao.GetDB()
skuList, err := dao.GetSkus(db, nil, nil, []int{model.SkuStatusNormal}, nil)
if err != nil {
return nil, err
}
var skuInfoList []*jdapi.BaseStockCenterRequest
skuMap := make(map[int64]int)
for _, sku := range skuList {
if !jxutils.IsEmptyID(sku.JdID) {
skuInfoList = append(skuInfoList, &jdapi.BaseStockCenterRequest{
SkuId: sku.JdID,
})
skuMap[sku.JdID] = sku.ID
}
}
for _, storeID := range storeIDs {
storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJD)
if err != nil {
return nil, err
}
for _, sku := range skuInfoList {
sku.StationNo = storeDetail.VendorStoreID
}
task := tasksch.NewParallelTask("jd 获取京东门店商品信息", tasksch.NewParallelConfig().SetBatchSize(jdapi.MaxStoreSkuBatchSize), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
batchSkuInfoList := make([]*jdapi.BaseStockCenterRequest, len(batchItemList))
batchSkuList := make([]int64, len(batchItemList))
for k, v := range batchItemList {
batchSkuInfoList[k] = v.(*jdapi.BaseStockCenterRequest)
batchSkuList[k] = batchSkuInfoList[k].SkuId
}
stockInfo, err := api.JdAPI.QueryOpenUseable(batchSkuInfoList)
if err != nil {
return nil, err
}
priceInfo, err := api.JdAPI.GetStationInfoList(storeDetail.VendorStoreID, batchSkuList)
if err != nil {
return nil, err
}
var batchStoreSkuList []*model.StoreSkuBind
batchStoreSkuMap := make(map[int64]*model.StoreSkuBind)
for _, v := range stockInfo {
if v.UsableQty > 0 {
batchSku := &model.StoreSkuBind{
StoreID: storeID,
SkuID: skuMap[v.SkuID],
}
if v.Vendibility == 0 {
batchSku.Status = model.SkuStatusNormal
} else {
batchSku.Status = model.SkuStatusDontSale
}
batchStoreSkuMap[v.SkuID] = batchSku
batchStoreSkuList = append(batchStoreSkuList, batchSku)
}
}
for _, v := range priceInfo {
if storeSku := batchStoreSkuMap[v.SkuID]; storeSku != nil {
storeSku.Price = int(v.Price)
}
}
return batchStoreSkuList, err
}, skuInfoList)
tasksch.AddChild(parentTask, task).Run()
result, err := task.GetResult(0)
if err != nil {
return nil, err
}
for _, v := range result {
storeSkuList = append(storeSkuList, v.(*model.StoreSkuBind))
}
}
return storeSkuList, err
}
func (p *PurchaseHandler) SyncStoreProducts(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debugf("jd SyncStoreProducts, storeID:%d", storeID)
db := dao.GetDB()
storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJD)
if err != nil {
return "", err
}
storeSkuList, err := dao.GetStoreSkus2(db, model.VendorIDJD, storeID, skuIDs, false)
if err != nil {
return "", err
}
task := tasksch.NewParallelTask("SyncStoreProducts京东", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeSku := batchItemList[0].(*dao.StoreSkuSyncInfo)
if storeSku.VendorSkuID != "" && storeSku.StoreSkuStatus == model.SkuStatusNormal {
if globals.EnableJdStoreWrite {
synchronized, err2 := api.JdAPI.SyncProduct(storeDetail.VendorStoreID, storeSku.VendorSkuID)
if err = err2; err == nil && synchronized {
retVal = []int{1}
}
} else {
retVal = []int{1}
}
}
return retVal, err
}, storeSkuList)
tasksch.HandleTask(task, parentTask, true).Run()
if !isAsync {
result, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(result))
}
} else {
hint = task.GetID()
}
return hint, err
}

View File

@@ -0,0 +1,66 @@
package jd
import (
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
func (p *PurchaseHandler) GetStoreSkusInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, inStoreSkuList []*partner.BareStoreSkuInfo) (outStoreSkuList []*partner.BareStoreSkuInfo, err error) {
var batchSkuInfoList []*jdapi.BaseStockCenterRequest
batchSkuList := partner.BareStoreSkuInfoList(inStoreSkuList).GetVendorSkuIDIntList()
for _, v := range inStoreSkuList {
if !dao.IsVendorThingIDEmpty(v.VendorSkuID) {
batchSkuInfoList = append(batchSkuInfoList, &jdapi.BaseStockCenterRequest{
StationNo: vendorStoreID,
SkuId: utils.Str2Int64(v.VendorSkuID),
})
}
}
if len(batchSkuInfoList) > 0 {
var stockInfo []*jdapi.QueryStockResponse
var priceInfo []*jdapi.StorePriceInfo
task := tasksch.NewParallelTask("获取京东到家平台门店商品信息", tasksch.NewParallelConfig().SetParallelCount(2), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
subTaskID := batchItemList[0].(int)
if subTaskID == 0 {
stockInfo, err = api.JdAPI.QueryOpenUseable(batchSkuInfoList)
} else {
priceInfo, err = api.JdAPI.GetStationInfoList(vendorStoreID, batchSkuList)
globals.SugarLogger.Debug(utils.Format4Output(priceInfo, false))
}
return nil, err
}, []int{0, 1})
tasksch.HandleTask(task, parentTask, false).Run()
_, err = task.GetResult(0)
if err == nil {
storeSkuMap := make(map[int64]*partner.BareStoreSkuInfo)
for _, v := range inStoreSkuList {
storeSkuMap[utils.Str2Int64(v.VendorSkuID)] = v
}
for _, v := range stockInfo {
outStoreSkuList = append(outStoreSkuList, storeSkuMap[v.SkuID])
storeSkuMap[v.SkuID].Status = jdStoreSkuStatus2Jx(v.Vendibility)
}
for _, v := range priceInfo {
storeSkuMap[v.SkuID].Price = v.Price
}
}
}
return outStoreSkuList, err
}
func jdStoreSkuStatus2Jx(jdStoreSkuStatus int) (jxSkuStatus int) {
if jdStoreSkuStatus == 0 {
jxSkuStatus = model.SkuStatusNormal
} else {
jxSkuStatus = model.SkuStatusDontSale
}
return jxSkuStatus
}

View File

@@ -0,0 +1,169 @@
package mtwm
import (
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
func actOrderRules2Mtwm(actOrderRules []*model.ActOrderRule) (actDetails []*mtwmapi.FullDiscountActDetail) {
for _, v := range actOrderRules {
actDetails = append(actDetails, &mtwmapi.FullDiscountActDetail{
OriginalPrice: jxutils.IntPrice2Standard(v.SalePrice),
ActPrice: jxutils.IntPrice2Standard(v.DeductPrice),
})
}
return actDetails
}
func storeSku2ActData(act *model.Act2, actStoreSku []*model.ActStoreSku2, handler func(int) bool) (actData []*mtwmapi.RetailDiscountActData) {
for _, v := range actStoreSku {
if handler == nil || handler(v.SyncStatus) {
actData = append(actData, &mtwmapi.RetailDiscountActData{
AppFoodCode: utils.Int2Str(v.SkuID),
// UserType: 0,
StartTime: act.BeginAt.Unix(),
EndTime: act.EndAt.Unix(),
OrderLimit: act.LimitCount,
DayLimit: act.LimitDaily,
// Period: "",
// WeeksTime: "",
SettingType: mtwmapi.SettingTypeAsPrice,
ActPrice: jxutils.IntPrice2Standard(v.ActPrice),
// DiscountCoefficient: 0,
Sequence: int(v.ActPrice),
ItemID: utils.Str2Int64WithDefault(v.VendorActID, 0),
})
}
}
return actData
}
func storeSku2ActData4Delete(actStoreSku []*model.ActStoreSku2, handler func(int) bool) (actIDList []string) {
for _, v := range actStoreSku {
if handler == nil || handler(v.SyncStatus) {
if v.VendorActID != "" {
actIDList = append(actIDList, v.VendorActID)
}
}
}
return actIDList
}
func isCreateOrUpdate(syncStatus int) bool {
return model.IsSyncStatusNeedCreate(syncStatus) || model.IsSyncStatusNeedUpdate(syncStatus)
}
func createOneShopAct(act *model.Act2, storeMap *model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
actData := storeSku2ActData(act, actStoreSku, isCreateOrUpdate)
if len(actData) > 0 {
if globals.EnableMtwmStoreWrite {
actResult, err2 := api.MtwmAPI.RetailDiscountBatchSave(storeMap.VendorStoreID, actData)
if err = err2; err != nil {
return err
}
actResultMap := make(map[string]*mtwmapi.RetailDiscountActResult)
for _, v := range actResult {
actResultMap[v.AppFoodCode] = v
}
for _, v := range actStoreSku {
if result := actResultMap[utils.Int2Str(v.SkuID)]; result != nil {
v.VendorActID = utils.Int64ToStr(result.ActID)
}
}
} else {
for _, v := range actStoreSku {
v.VendorActID = utils.Int64ToStr(jxutils.GenFakeID())
}
}
}
return err
}
func (c *PurchaseHandler) CreateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
globals.SugarLogger.Debugf("mtwm CreateAct actID:%d", act.ID)
if act.Type < model.ActOrderBegin {
actStoreSkuMap := partner.ActStoreSku2Map(actStoreSku)
task := tasksch.NewParallelTask("mtwm CreateAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
store := batchItemList[0].(*model.ActStore2)
err = createOneShopAct(act, store, actStoreSkuMap[store.StoreID])
return nil, err
}, actStoreMap)
tasksch.HandleTask(task, parentTask, true).Run()
_, err = task.GetResult(0)
} else {
}
return err
}
func (c *PurchaseHandler) UpdateAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreMap2Remove, actStoreMap2Add, actStoreMap2Update []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
if act.Type < model.ActOrderBegin {
actStoreSkuMap := partner.ActStoreSku2Map(actStoreSku)
if len(actStoreMap2Remove) > 0 {
if err = c.CancelAct(ctx, parentTask, act, actStoreMap2Remove, nil); err != nil {
return err
}
for _, v := range actStoreMap2Remove {
delete(actStoreSkuMap, v.StoreID)
}
}
if len(actStoreMap2Add) > 0 {
if err = c.CreateAct(ctx, parentTask, act, actOrderRules, actStoreMap2Add, actStoreSku); err != nil {
return err
}
for _, v := range actStoreMap2Add {
delete(actStoreSkuMap, v.StoreID)
}
}
task := tasksch.NewParallelTask("mtwm UpdateAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
v := batchItemList[0].(*model.ActStore2)
if storeSkus := actStoreSkuMap[v.StoreID]; storeSkus != nil {
if list := storeSku2ActData4Delete(storeSkus, model.IsSyncStatusNeedDelete); len(list) > 0 {
if err = api.MtwmAPI.RetailDiscountDelete(v.VendorStoreID, list); err != nil {
return nil, err
}
}
if err = createOneShopAct(act, v, actStoreSkuMap[v.StoreID]); err != nil {
return nil, err
}
}
return nil, err
}, actStoreMap2Update)
tasksch.HandleTask(task, parentTask, true).Run()
_, err = task.GetResult(0)
} else {
}
return err
}
func (c *PurchaseHandler) CancelAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreMap []*model.ActStore2, actStoreSku []*model.ActStoreSku2) (err error) {
if act.Type < model.ActOrderBegin {
actStoreSkuMap := partner.ActStoreSku2Map(actStoreSku)
task := tasksch.NewParallelTask("mtwm DeleteAct", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
v := batchItemList[0].(*model.ActStore2)
actIDList := storeSku2ActData4Delete(actStoreSkuMap[v.StoreID], nil)
if len(actIDList) > 0 {
if globals.EnableMtwmStoreWrite {
err = api.MtwmAPI.RetailDiscountDelete(v.VendorStoreID, actIDList)
}
}
return nil, err
}, actStoreMap)
tasksch.HandleTask(task, parentTask, true).Run()
_, err = task.GetResult(0)
} else {
}
return err
}

View File

@@ -2,7 +2,6 @@ package mtwm
import ( import (
"git.rosy.net.cn/baseapi/platformapi/mtwmapi" "git.rosy.net.cn/baseapi/platformapi/mtwmapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
) )
@@ -19,11 +18,11 @@ func OnOrderCallbackMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackRes
} }
}, jxutils.ComposeUniversalOrderID(orderID, model.VendorIDMTWM)) }, jxutils.ComposeUniversalOrderID(orderID, model.VendorIDMTWM))
} }
if msg.Cmd == mtwmapi.MsgTypeOrderRefund || msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { /*if msg.Cmd == mtwmapi.MsgTypeOrderRefund || msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund {
utils.CallFuncAsync(func() { utils.CallFuncAsync(func() {
OnFinancialMsg(msg) OnFinancialMsg(msg)
}) })
} else if msg.Cmd == mtwmapi.MsgTypeStoreStatusChanged { } else */if msg.Cmd == mtwmapi.MsgTypeStoreStatusChanged {
response = curPurchaseHandler.onStoreStatusChanged(msg) response = curPurchaseHandler.onStoreStatusChanged(msg)
} }
} }
@@ -31,5 +30,5 @@ func OnOrderCallbackMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackRes
} }
func GetOrderIDFromMsg(msg *mtwmapi.CallbackMsg) string { func GetOrderIDFromMsg(msg *mtwmapi.CallbackMsg) string {
return msg.Data.Get(mtwmapi.KeyOrderID) return msg.FormData.Get(mtwmapi.KeyOrderID)
} }

View File

@@ -18,13 +18,13 @@ const (
// 存储美团退款订单结账信息 // 存储美团退款订单结账信息
func OnFinancialMsg(msg *mtwmapi.CallbackMsg) (err error) { func OnFinancialMsg(msg *mtwmapi.CallbackMsg) (err error) {
if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { // 部分退款处理 if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { // 部分退款处理
orderData := msg.Data orderData := msg.FormData
if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess { if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess {
err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(curPurchaseHandler.AfsOrderDetail2Financial(orderData)) err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(curPurchaseHandler.AfsOrderDetail2Financial(orderData))
} }
} }
if msg.Cmd == mtwmapi.MsgTypeOrderRefund { // todo 全额退款处理 if msg.Cmd == mtwmapi.MsgTypeOrderRefund { // todo 全额退款处理
orderData := msg.Data orderData := msg.FormData
if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess { if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess {
globals.SugarLogger.Debug(orderData.Get("order_id")) // 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号也没有通过订单号查询退款信息的接口 globals.SugarLogger.Debug(orderData.Get("order_id")) // 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号也没有通过订单号查询退款信息的接口
afsOrderID := orderData.Get("order_id") afsOrderID := orderData.Get("order_id")
@@ -45,7 +45,7 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
VendorID: model.VendorIDMTWM, VendorID: model.VendorIDMTWM,
AfsOrderID: orderData.Get("order_id"), AfsOrderID: orderData.Get("order_id"),
VendorOrderID: orderData.Get("order_id"), VendorOrderID: orderData.Get("order_id"),
AfsCreateAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))), AfsCreatedAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))),
// BoxMoney: orderFinancial.BoxMoney, // BoxMoney: orderFinancial.BoxMoney,
// SkuBoxMoney: orderFinancial.SkuBoxMoney, // 美团的餐盒费已经拆到单条SKU里面去了,退款时直接计算用户支付sku金额就好了 // SkuBoxMoney: orderFinancial.SkuBoxMoney, // 美团的餐盒费已经拆到单条SKU里面去了,退款时直接计算用户支付sku金额就好了
FreightUserMoney: orderFinancial.FreightMoney, FreightUserMoney: orderFinancial.FreightMoney,
@@ -66,7 +66,6 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
orderSkuFinancial := &model.OrderSkuFinancial{ orderSkuFinancial := &model.OrderSkuFinancial{
VendorID: sku.VendorID, VendorID: sku.VendorID,
VendorOrderID: sku.VendorOrderID, VendorOrderID: sku.VendorOrderID,
VendorOrderID2: sku.VendorOrderID2,
// OrderFinancialID: sku.VendorOrderID, // OrderFinancialID: sku.VendorOrderID,
// ConfirmTime: afsOrder.AfsCreateAt, // ConfirmTime: afsOrder.AfsCreateAt,
VendorStoreID: afsOrder.VendorStoreID, VendorStoreID: afsOrder.VendorStoreID,
@@ -93,7 +92,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData url.Values) (afsOrd
VendorID: model.VendorIDMTWM, VendorID: model.VendorIDMTWM,
AfsOrderID: orderData.Get("order_id"), AfsOrderID: orderData.Get("order_id"),
VendorOrderID: orderData.Get("order_id"), VendorOrderID: orderData.Get("order_id"),
AfsCreateAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))), AfsCreatedAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))),
RefundMoney: jxutils.StandardPrice2Int(utils.Str2Float64(orderData.Get("money"))), RefundMoney: jxutils.StandardPrice2Int(utils.Str2Float64(orderData.Get("money"))),
} }
// if orderData.Get("timestamp") != "" { // if orderData.Get("timestamp") != "" {

View File

@@ -14,12 +14,12 @@ import (
func TestOnFinancialMsg(t *testing.T) { func TestOnFinancialMsg(t *testing.T) {
msg := &mtwmapi.CallbackMsg{ msg := &mtwmapi.CallbackMsg{
Cmd: "orderRefund", Cmd: "orderRefund",
Data: url.Values{}, FormData: url.Values{},
} }
msg.Data.Set("timestamp", utils.Int64ToStr(time.Now().Unix())) msg.FormData.Set("timestamp", utils.Int64ToStr(time.Now().Unix()))
msg.Data.Set("order_id", "33762863167364867") msg.FormData.Set("order_id", "33762863167364867")
msg.Data.Set("notify_type", "agree") msg.FormData.Set("notify_type", "agree")
msg.Data.Set("money", "23.56") msg.FormData.Set("money", "23.56")
food := []map[string]interface{}{ food := []map[string]interface{}{
map[string]interface{}{ map[string]interface{}{
"app_food_code": "123", "app_food_code": "123",
@@ -40,8 +40,8 @@ func TestOnFinancialMsg(t *testing.T) {
"box_price": 1, "box_price": 1,
}, },
} }
msg.Data.Set("food", string(utils.MustMarshal(food))) msg.FormData.Set("food", string(utils.MustMarshal(food)))
res := OnFinancialMsg(msg) res := curPurchaseHandler.onAfsOrderMsg(msg)
fmt.Println(res) fmt.Println(res)
} }

View File

@@ -129,7 +129,7 @@ func bizStatusMtwm2JX(openLevel, online int) int {
return model.StoreStatusDisabled return model.StoreStatusDisabled
} else { } else {
if openLevel == mtwmapi.PoiOpenLevelHaveRest { if openLevel == mtwmapi.PoiOpenLevelHaveRest {
return model.StoreStatusClosed return model.StoreStatusHaveRest
} }
} }
return model.StoreStatusOpened return model.StoreStatusOpened
@@ -138,7 +138,7 @@ func bizStatusMtwm2JX(openLevel, online int) int {
func bizStatusJX2Mtwm(status int) (openLevel, online int) { func bizStatusJX2Mtwm(status int) (openLevel, online int) {
if status == model.StoreStatusDisabled { if status == model.StoreStatusDisabled {
return mtwmapi.PoiOpenLevelHaveRest, mtwmapi.PoiStatusOffline return mtwmapi.PoiOpenLevelHaveRest, mtwmapi.PoiStatusOffline
} else if status == model.StoreStatusClosed { } else if status == model.StoreStatusHaveRest || status == model.StoreStatusClosed {
return mtwmapi.PoiOpenLevelHaveRest, mtwmapi.PoiStatusOnline return mtwmapi.PoiOpenLevelHaveRest, mtwmapi.PoiStatusOnline
} }
return mtwmapi.PoiOpenLevelNormal, mtwmapi.PoiStatusOnline return mtwmapi.PoiOpenLevelNormal, mtwmapi.PoiStatusOnline

Some files were not shown because too many files have changed in this diff Show More