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 !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
}
dao.Rollback(db)
@@ -120,7 +120,7 @@ func (c *OrderManager) SaveOrderFinancialInfo(order *model.OrderFinancial, opera
sku.JxShopMoney = order.JxShopMoney - sku.JxShopMoney
if err = dao.CreateEntity(db, sku); err != nil {
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
}
dao.Rollback(db)

View File

@@ -2,7 +2,6 @@ package orderman
import (
"fmt"
"math"
"time"
"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/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/msghub"
"git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego/orm"
)
@@ -51,7 +49,8 @@ func (c *OrderManager) LoadPendingOrders() []*model.GoodsOrder {
// msgVendorStatus的意思是事件本身的类型类似有时收到NewOrder事件去取订单状态不一定就是New的
// 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) {
order.ConsigneeMobile2 = order.ConsigneeMobile
}
@@ -59,6 +58,7 @@ func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus strin
db := dao.GetDB()
dao.Begin(db)
defer func() {
globals.SugarLogger.Debugf("OnOrderNew exit orderID:%s", order.VendorOrderID)
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
@@ -67,12 +67,7 @@ func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus strin
if order.Status == model.OrderStatusUnknown {
order.Status = model.OrderStatusNew
}
status := model.Order2Status(order)
if status.Status > model.OrderStatusNew {
status.Status = model.OrderStatusNew
}
status.VendorStatus = msgVendorStatus
isDuplicated, err := addOrderOrWaybillStatus(status, db)
isDuplicated, err := addOrderOrWaybillStatus(orderStatus, db)
if err == nil && !isDuplicated {
isDuplicated, err = c.SaveOrder(order, false, db)
}
@@ -88,7 +83,7 @@ func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus strin
}
// 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) {
order.ConsigneeMobile2 = order.ConsigneeMobile
}
@@ -101,13 +96,12 @@ func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus st
panic(r)
}
}()
if order.Status == model.OrderStatusUnknown {
// 出现过调整单后状态回到新订单状态比如911350836000622
// 不完全确定,加一个处理
if order.Status < model.OrderStatusAccepted {
order.Status = model.OrderStatusAccepted
}
status := model.Order2Status(order)
status.Status = model.OrderStatusAdjust
status.VendorStatus = msgVendorStatus
isDuplicated, err := addOrderOrWaybillStatus(status, db)
isDuplicated, err := addOrderOrWaybillStatus(orderStatus, db)
if err == nil && !isDuplicated {
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()
@@ -128,10 +122,9 @@ func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus st
if err == nil {
dao.Commit(db)
if !isDuplicated {
msghub.OnNewOrder(order)
// 因为订单调度器需要的是真实状态所以用order的状态
_ = scheduler.CurrentScheduler.OnOrderNew(order, false)
_ = scheduler.CurrentScheduler.OnOrderStatusChanged(order, model.Order2Status(order), false)
_ = scheduler.CurrentScheduler.OnOrderStatusChanged(order, orderStatus, false)
}
} else {
dao.Rollback(db)
@@ -171,7 +164,7 @@ func (c *OrderManager) OnOrderMsg(order *model.GoodsOrder, vendorStatus, remark
VendorStatus: vendorStatus,
Status: model.OrderStatusMsg,
StatusTime: time.Now(),
Remark: remark,
Remark: utils.LimitUTF8StringLen(remark, 255),
}, nil)
return err
}
@@ -207,11 +200,12 @@ func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao
order.Status = orderStatus.Status
order.VendorStatus = orderStatus.VendorStatus
order.StatusTime = orderStatus.StatusTime
jxutils.RefreshOrderSkuRelated(order)
}
}
}
order.OrderCreatedAt = order.StatusTime
// globals.SugarLogger.Debugf("saveOrder isAdjust:%t, order:%v", isAdjust, order)
created, _, err2 := db.Db.ReadOrCreate(order, "VendorOrderID", "VendorID")
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 created {
sql := `INSERT INTO order_sku(vendor_order_id, vendor_id, count, vendor_sku_id, sku_id, jx_sku_id, sku_name,
shop_price, sale_price, weight, sku_type, promotion_type, order_created_at) VALUES`
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)
if err = dao.CreateMultiEntities(db, order.Skus); err != nil {
baseapi.SugarLogger.Warnf("saveOrder orderID:%s, save order_sku failed with error:%v", order.VendorOrderID, err)
}
} else {
isDuplicated = true
@@ -256,12 +236,35 @@ func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao
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) {
globals.SugarLogger.Debugf("updateOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID)
jxStoreID := jxutils.GetShowStoreIDFromOrder(order)
var opNumStr string
if time.Now().Sub(order.OrderCreatedAt) < 48*time.Hour && order.VendorID != model.VendorIDJD {
opNumStr = ""
opNumStr = "2"
} else {
opNumStr = "2"
}
@@ -273,11 +276,16 @@ func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.
orderSkus := order.Skus
vendorSkuIDs := make([]int64, 0)
skuIDMap := make(map[int]int)
for _, v := range orderSkus {
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
if intVendorSkuID != 0 {
vendorSkuIDs = append(vendorSkuIDs, intVendorSkuID)
}
if skuID := jxutils.GetSkuIDFromOrderSku(v); skuID > 0 {
skuIDMap[skuID] = 1
}
}
if len(vendorSkuIDs) > 0 {
tableName := "t2"
@@ -301,7 +309,16 @@ func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db *dao.
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 {
v.VendorOrderID = order.VendorOrderID
v.VendorID = order.VendorID
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
if intVendorSkuID != 0 && v.VendorSkuID != "-70000" { // todo hard code
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
@@ -336,11 +360,7 @@ func (c *OrderManager) updateOrderOtherInfo(order *model.GoodsOrder, db *dao.Dao
}
order.JxStoreID = storeMap.StoreID
if err = c.updateOrderSkuOtherInfo(order, db); err == nil {
if order.Weight == 0 {
for _, v := range order.Skus {
order.Weight += v.Weight
}
}
jxutils.RefreshOrderSkuRelated(order)
}
return err
}
@@ -369,20 +389,21 @@ func (c *OrderManager) addOrderStatus(orderStatus *model.OrderStatus, db *dao.Da
updateFields = append(updateFields, "Status", "StatusTime")
if order.LockStatus != model.OrderStatusUnknown {
order.LockStatus = model.OrderStatusUnknown
order.LockStatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "LockStatus", "LockStatusTime")
updateFields = append(updateFields, "LockStatus")
}
} else {
if model.IsOrderUnlockStatus(orderStatus.Status) {
order.LockStatus = model.OrderStatusUnknown
} else {
updateFields = append(updateFields, "LockStatus")
} else if !model.IsOrderFinalStatus(orderStatus.Status) {
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)
}
order.Flag &= ^model.OrderFlagMaskUserApplyCancel
order.LockStatus = orderStatus.Status
order.LockStatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "LockStatus", "LockStatusTime", "Flag")
}
order.LockStatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "LockStatus", "LockStatusTime")
}
if model.IsOrderFinalStatus(orderStatus.Status) {
order.OrderFinishedAt = orderStatus.StatusTime
@@ -430,6 +451,7 @@ func (c *OrderManager) loadOrder(vendorOrderID, vendorOrderID2 string, vendorID
}, "LoadOrder orderID:%s", vendorOrderID)
}
if err != nil {
order = nil
if err == orm.ErrNoRows {
err = ErrCanNotFindOrder
}
@@ -475,11 +497,12 @@ func (c *OrderManager) loadOrderFinancial(vendorOrderID, vendorOrderID2 string,
if err = db.Read(order, keyFields...); err == nil {
vendorOrderID = order.VendorOrderID
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
}, "LoadOrder orderID:%s", vendorOrderID)
}
if err != nil {
order = nil
if err == orm.ErrNoRows {
err = ErrCanNotFindOrder
}
@@ -488,30 +511,15 @@ func (c *OrderManager) loadOrderFinancial(vendorOrderID, vendorOrderID2 string,
return order, err
}
func (c *OrderManager) UpdateOrderStatusAndFlag(order *model.GoodsOrder) (err error) {
db := orm.NewOrm()
utils.CallFuncLogError(func() error {
_, err = db.Update(order, "Status", "DeliveryFlag")
return err
}, "UpdateOrderStatusAndFlag orderID:%s failed with error:%v", order.VendorOrderID, err)
return err
func (c *OrderManager) UpdateOrderStatusAndDeliveryFlag(order *model.GoodsOrder) (err error) {
return c.UpdateOrderFields(order, []string{"Status", "DeliveryFlag"})
}
//Waybill
func (c *OrderManager) UpdateWaybillVendorID(bill *model.Waybill, revertStatus bool) (err error) {
globals.SugarLogger.Debugf("UpdateWaybillVendorID bill:%v", bill)
func (c *OrderManager) UpdateOrderFields(order *model.GoodsOrder, fieldList []string) (err error) {
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 {
_, 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
}, "UpdateWaybillVendorID update order, bill:%v", bill)
}, "UpdateOrderFields orderID:%s failed with error:%v", order.VendorOrderID, 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,12 +102,16 @@ func (c *OrderManager) OnOrderComments(orderCommentList []*model.OrderComment) (
comment2.Maxmodifytime = int(orderComment.ModifyDuration)
if orderComment.VendorID != model.VendorIDELM {
var order *model.GoodsOrder
if orderComment.VendorID != model.VendorIDEBAI {
order, err = partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID)
if true /*orderComment.VendorID != model.VendorIDEBAI*/ {
order, _ = partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID)
}
if order != nil {
orderComment.StoreID = jxutils.GetSaleStoreIDFromOrder(order)
orderComment.ConsigneeMobile = order.ConsigneeMobile
if order.ConsigneeMobile2 != "" {
orderComment.ConsigneeMobile = order.ConsigneeMobile2
} else {
orderComment.ConsigneeMobile = order.ConsigneeMobile
}
}
if orderComment.StoreID > 0 {
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) {
if status.OrderType == model.OrderTypeOrder {
globals.SugarLogger.Debugf("addOrderStatus order:%v", status)
} else {
} else if status.OrderType == model.OrderTypeWaybill {
globals.SugarLogger.Debugf("addOrderStatus waybill:%v", status)
} else {
globals.SugarLogger.Debugf("addOrderStatus afsOrder:%v", status)
}
dao.Begin(db)
defer func() {
@@ -100,6 +102,17 @@ func addOrderOrWaybillStatus(status *model.OrderStatus, db *dao.DaoDB) (isDuplic
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 最好还是改成全事件回放算了
func LoadPendingOrders() {
orders := FixedOrderManager.LoadPendingOrders()
@@ -140,17 +153,17 @@ func LoadPendingOrders() {
if order, ok := item.(*model.GoodsOrder); ok {
jxutils.CallMsgHandlerAsync(func() {
scheduler.CurrentScheduler.OnOrderNew(order, true)
}, order.VendorOrderID)
}, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
} else if status, ok := item.(*model.OrderStatus); ok {
jxutils.CallMsgHandlerAsync(func() {
order := orderMap[jxutils.ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)]
scheduler.CurrentScheduler.OnOrderStatusChanged(order, status, true)
}, status.VendorOrderID)
}, jxutils.ComposeUniversalOrderID(status.RefVendorOrderID, status.RefVendorID))
} else {
bill := item.(*model.Waybill)
jxutils.CallMsgHandlerAsync(func() {
scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, true)
}, bill.VendorOrderID)
}, jxutils.ComposeUniversalOrderID(bill.VendorOrderID, bill.OrderVendorID))
}
curTime := time.Now()
timeout := sleepGap - curTime.Sub(lastTime)

View File

@@ -3,8 +3,11 @@ package orderman
import (
"fmt"
"strconv"
"strings"
"time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/excel"
@@ -27,33 +30,21 @@ type tWaybillExt struct {
StoreID int `json:"storeID" orm:"column(store_id)"`
}
//此函数会被GetStoreOrderCountInfo2取代
func (c *OrderManager) GetStoreOrderCountInfo(ctx *jxcontext.Context, storeID string, lastHours int) (countInfo []*model.GoodsOrderCountInfo, err error) {
globals.SugarLogger.Debugf("GetStoreOrderCountInfo storeID:%s", storeID)
if lastHours > maxLastHours {
lastHours = maxLastHours
} else if lastHours == 0 {
lastHours = defLastHours
}
type StoresOrderSaleInfo struct {
StoreID int `orm:"column(store_id)" json:"storeID"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
Status int `json:"status"`
Count int `json:"count"`
ShopPrice int64 `json:"shopPrice"`
VendorPrice int64 `json:"vendorPrice"`
SalePrice int64 `json:"salePrice"`
ActualPayPrice int64 `json:"actualPayPrice"`
db := orm.NewOrm()
_, 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
EarningPrice int64 `json:"earningPrice"` // 预估结算给门店老板的钱
}
func (c *OrderManager) GetStoreOrderCountInfo2(ctx *jxcontext.Context, storeID, lastHours int) (countInfo []*model.GoodsOrderCountInfo2, err error) {
globals.SugarLogger.Debugf("GetStoreOrderCountInfo2 storeID:%s", storeID)
func (c *OrderManager) GetStoreOrderCountInfo(ctx *jxcontext.Context, storeID, lastHours int) (countInfo []*model.GoodsOrderCountInfo, err error) {
globals.SugarLogger.Debugf("GetStoreOrderCountInfo storeID:%d", storeID)
if lastHours > maxLastHours {
lastHours = maxLastHours
} else if lastHours == 0 {
@@ -72,7 +63,7 @@ func (c *OrderManager) GetStoreOrderCountInfo2(ctx *jxcontext.Context, storeID,
if err == 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
}
@@ -86,7 +77,26 @@ func (c *OrderManager) GetOrderSkuInfo(ctx *jxcontext.Context, vendorOrderID str
db := dao.GetDB()
if vendorID == model.VendorIDELM {
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
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 = ?*/
@@ -96,7 +106,26 @@ func (c *OrderManager) GetOrderSkuInfo(ctx *jxcontext.Context, vendorOrderID str
`, fullSkuNameSQL), /*, utils.DefaultTimeValue, utils.DefaultTimeValue*/ vendorOrderID, vendorID)
} else if vendorID == model.VendorIDJD {
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
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 = ?*/
@@ -107,7 +136,26 @@ func (c *OrderManager) GetOrderSkuInfo(ctx *jxcontext.Context, vendorOrderID str
}
if err != nil || len(skus) == 0 {
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
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 = ?*/
@@ -157,19 +205,25 @@ func (c *OrderManager) GetOrderInfo(ctx *jxcontext.Context, vendorOrderID string
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)
db := orm.NewOrm()
_, err = db.Raw(`
db := dao.GetDB()
sql := `
SELECT t1.*
FROM waybill t1
WHERE t1.vendor_order_id = ? AND order_vendor_id = ?
`, vendorOrderID, vendorID).QueryRows(&bills)
if err == nil {
return bills, nil
`
sqlParams := []interface{}{
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)
return nil, err
return bills, err
}
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
}
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)
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)
pageSize = jxutils.FormalizePageSize(pageSize)
if offset < 0 {
@@ -229,11 +283,21 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
}
sql := `
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.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
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 (
sqlWhere string
sqlParams []interface{}
@@ -257,14 +321,14 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
} else {
fromDate, err2 := utils.TryStr2Time(fromDateStr)
if err = err2; err != nil {
return nil, err
return nil, 0, err
}
if toDateStr == "" {
toDateStr = fromDateStr
}
toDate, err2 := utils.TryStr2Time(toDateStr)
if err = err2; err != nil {
return nil, err
return nil, 0, err
}
toDate = toDate.Add(24 * time.Hour)
@@ -279,7 +343,7 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
keyword := params["keyword"].(string)
keywordLike := "%" + keyword + "%"
sqlWhere += `
AND (t1.store_name LIKE ? OR t1.vendor_order_id LIKE ? OR t1.vendor_store_id LIKE ?
AND (t1.store_name LIKE ? OR t1.vendor_order_id LIKE ? OR t1.vendor_store_id LIKE ?
OR t1.consignee_name LIKE ? OR t1.consignee_mobile LIKE ? OR t1.consignee_mobile2 LIKE ? OR t1.consignee_address LIKE ?
OR t2.vendor_waybill_id LIKE ? OR t2.courier_name LIKE ? OR t2.courier_mobile LIKE ?
`
@@ -293,17 +357,17 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["waybillVendorIDs"] != nil {
var waybillVendorIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["waybillVendorIDs"].(string)), &waybillVendorIDs); err != nil {
return nil, err
return nil, 0, err
}
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)
}
}
if params["storeIDs"] != nil {
var storeIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["storeIDs"].(string)), &storeIDs); err != nil {
return nil, err
return nil, 0, err
}
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)) + ")"
@@ -313,7 +377,7 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["statuss"] != nil {
var statuss []int
if err = utils.UnmarshalUseNumber([]byte(params["statuss"].(string)), &statuss); err != nil {
return nil, err
return nil, 0, err
}
if len(statuss) > 0 {
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 {
var lockStatuss []int
if err = utils.UnmarshalUseNumber([]byte(params["lockStatuss"].(string)), &lockStatuss); err != nil {
return nil, err
return nil, 0, err
}
if len(lockStatuss) > 0 {
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 {
var cities []int
if err = utils.UnmarshalUseNumber([]byte(params["cities"].(string)), &cities); err != nil {
return nil, err
return nil, 0, err
}
if len(cities) > 0 {
sql += " JOIN store st ON t1.store_id = st.id"
@@ -345,39 +409,147 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr
if params["vendorIDs"] != nil {
var vendorIDs []int
if err = utils.UnmarshalUseNumber([]byte(params["vendorIDs"].(string)), &vendorIDs); err != nil {
return nil, err
return nil, 0, err
}
if len(vendorIDs) > 0 {
sqlWhere += " AND t1.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDs)) + ")"
sqlParams = append(sqlParams, vendorIDs)
}
}
sql += sqlWhere
sql += `
ORDER BY t1.order_created_at DESC
LIMIT ? OFFSET ?
`
sqlParams = append(sqlParams, pageSize, offset)
var orders []*model.GoodsOrderExt
db := dao.GetDB()
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
sql += sqlWhere
if isIncludeSku {
sql += `
ORDER BY t1.id`
} else {
sql += `
ORDER BY t1.order_created_at DESC
LIMIT ? OFFSET ?`
sqlParams = append(sqlParams, pageSize, offset)
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
}
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{
TotalCount: dao.GetLastTotalRowCount(db),
TotalCount: totalCount,
Data: orders,
}
}
dao.Commit(db)
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) {
globals.SugarLogger.Debugf("GetWaybills from:%s to:%s", fromDateStr, toDateStr)
@@ -472,16 +644,14 @@ func (c *OrderManager) GetOrderStatusList(ctx *jxcontext.Context, vendorOrderID
sql := `
SELECT *
FROM order_status t1
WHERE 1 = 1
WHERE t1.ref_vendor_order_id = ? AND t1.ref_vendor_id = ?
`
sqlParams := []interface{}{
vendorOrderID,
vendorID,
}
if orderType == -1 {
sql += " AND t1.ref_vendor_order_id = ? AND t1.ref_vendor_id = ?"
} else {
sql += " AND t1.vendor_order_id = ? AND t1.vendor_id = ? AND t1.order_type = ?"
if orderType > 0 {
sql += " AND t1.order_type = ?"
sqlParams = append(sqlParams, orderType)
}
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
}
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 (
waybillOrderStatusMap = map[int]int{
model.WaybillStatusApplyFailedGetGoods: model.WaybillStatusApplyFailedGetGoods,
model.WaybillStatusAgreeFailedGetGoods: model.WaybillStatusAgreeFailedGetGoods,
model.WaybillStatusRefuseFailedGetGoods: model.WaybillStatusRefuseFailedGetGoods,
model.WaybillStatusDeliverFailed: model.WaybillStatusDeliverFailed,
model.WaybillStatusApplyFailedGetGoods: model.OrderStatusApplyFailedGetGoods,
model.WaybillStatusAgreeFailedGetGoods: model.OrderStatusAgreeFailedGetGoods,
model.WaybillStatusRefuseFailedGetGoods: model.OrderStatusRefuseFailedGetGoods,
model.WaybillStatusDeliverFailed: model.OrderStatusDeliverFailed,
}
)
@@ -92,13 +92,48 @@ func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
panic(r)
}
}()
duplicatedCount := 0
if bill.Status == model.WaybillStatusNew {
isDuplicated, err = w.onWaybillNew(bill, db)
if isDuplicated {
duplicatedCount = 1
}
} 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{}
if bill.Status >= model.WaybillStatusAccepted && bill.Status < model.WaybillStatusEndBegin {
if bill.Status == model.WaybillStatusAccepted {
addParams["desired_fee"] = bill.DesiredFee
if bill.DesiredFee > 0 {
addParams["desired_fee"] = bill.DesiredFee
}
if bill.ActualFee > 0 {
addParams["actual_fee"] = bill.ActualFee
}
}
if bill.CourierMobile != "" {
addParams["courier_name"] = bill.CourierName
@@ -107,11 +142,15 @@ func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
} else if bill.Status >= model.WaybillStatusEndBegin {
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 {
dao.Commit(db)
if !isDuplicated {
if duplicatedCount == 0 {
scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, false)
}
} else {
@@ -136,21 +175,27 @@ func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
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)
isDuplicated, err = addOrderOrWaybillStatus(waybillStatus, db)
if err == nil && !isDuplicated && waybillStatus.Status > model.WaybillStatusUnknown { // todo 这里应该和addOrderStatus一样的改法状态不能回绕
params := utils.MergeMaps(orm.Params{
"status": bill.Status,
"vendor_status": bill.VendorStatus,
"status_time": bill.StatusTime,
}, addParams)
utils.CallFuncLogError(func() error {
_, 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
}, "addWaybillStatus update waybill status, bill:%v", bill)
isDuplicated, err := addOrderOrWaybillStatus(waybillStatus, db)
if err == nil && !isDuplicated {
if waybillStatus.Status > model.WaybillStatusUnknown { // todo 这里应该和addOrderStatus一样的改法状态不能回绕
params := utils.MergeMaps(orm.Params{
"status": bill.Status,
"vendor_status": bill.VendorStatus,
"status_time": bill.StatusTime,
}, addParams)
utils.CallFuncLogError(func() error {
_, 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
}, "addWaybillStatus update waybill status, bill:%v", bill)
} else {
duplicatedCount = -1
}
} else {
duplicatedCount = 1
}
return isDuplicated, err
return duplicatedCount, err
}
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,
}
if err = db.Read(bill, "VendorWaybillID", "WaybillVendorID"); err != nil {
bill = nil
if err == orm.ErrNoRows {
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/jxutils"
"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"
)
@@ -54,16 +55,16 @@ func (c *BaseScheduler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool
func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) {
globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusFinishedPickup {
if c.IsReallyCallPlatformAPI {
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status >= model.OrderStatusFinishedPickup && order.Status <= model.OrderStatusDelivering {
if order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 && c.IsReallyCallPlatformAPI {
err = utils.CallFuncLogErrorWithInfo(func() error {
return partner.GetPurchasePlatformFromVendorID(order.VendorID).Swtich2SelfDeliver(order, userName)
}, "Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
if err == nil { // 因为有些平台转自送后,不会再发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
order.Status = model.OrderStatusDelivering
order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
}
}
if err == nil { // 因为有些平台转自送后,不会再发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
order.Status = model.OrderStatusDelivering
order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
} else {
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)
if err == nil { // 因为有些平台设置配送中后,不会发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
order.Status = model.OrderStatusDelivering
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
}
} else {
@@ -147,16 +148,28 @@ func (c *BaseScheduler) CreateWaybill(platformVendorID int, order *model.GoodsOr
globals.SugarLogger.Warnf("CreateWaybill orderID:%s, vendorID:%d is not solid!!!", order.VendorOrderID, platformVendorID)
return nil, scheduler.ErrOrderIsNotSolid
}
if c.IsReallyCallPlatformAPI {
handlerInfo := partner.GetDeliveryPlatformFromVendorID(platformVendorID)
if handlerInfo != nil && handlerInfo.Use4CreateWaybill {
// 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)
if handlerInfo != nil && handlerInfo.Use4CreateWaybill {
if c.IsReallyCallPlatformAPI {
bill, err = handlerInfo.Handler.CreateWaybill(order, policy)
if err != nil {
globals.SugarLogger.Infof("CreateWaybill failed orderID:%s vendorID:%d with error:%v", order.VendorOrderID, platformVendorID, err)
} else {
order.DeliveryFlag |= model.WaybillVendorID2Mask(platformVendorID)
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
} else {
err = scheduler.ErrDeliverProviderWrong
}
} else {
err = scheduler.ErrDeliverProviderWrong
}
return bill, err
}
@@ -170,6 +183,8 @@ func (c *BaseScheduler) CancelWaybill(bill *model.Waybill, cancelReasonID int, c
return handlerInfo.Handler.CancelWaybill(bill, cancelReasonID, cancelReason)
}, "CancelWaybill bill:%v", bill); err == nil {
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)
}

View File

@@ -13,25 +13,38 @@ import (
"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()
globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s", order.VendorOrderID, userName)
storeCourierList, err := dao.GetStoreCourierList(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), model.StoreStatusOpened)
if err != nil {
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
for _, storeCourier := range storeCourierList {
courierVendorID := storeCourier.VendorID
if order.VendorID != model.VendorIDWSC || courierVendorID != model.VendorIDDada { // 达达作为微商城的自有配送,不参与配送竞争
bill, err2 := c.CreateWaybill(courierVendorID, order, policyHandler)
if err = err2; err == nil {
globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d bill:%v", order.VendorOrderID, userName, courierVendorID, bill)
bills = append(bills, bill)
} else {
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()))
if courierVendorIDMap == nil || courierVendorIDMap[storeCourier.VendorID] {
if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil && handler.Use4CreateWaybill {
courierVendorID := storeCourier.VendorID
if order.VendorID != model.VendorIDWSC || courierVendorID != model.VendorIDDada { // 达达作为微商城的自有配送,不参与配送竞争
bill, err2 := c.CreateWaybill(courierVendorID, order, policyHandler)
if err = err2; err == nil {
globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d bill:%v", order.VendorOrderID, userName, courierVendorID, bill)
bills = append(bills, bill)
if createOnlyOne {
break
}
} else {
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()))
}
}
}
}
}
@@ -58,8 +71,8 @@ func (c *BaseScheduler) SelfDeliveredAndUpdateStatus(ctx *jxcontext.Context, ven
err = c.Swtich2SelfDelivered(order, userName)
}
if err == nil {
order.Status = model.OrderStatusFinished
if err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order); err == nil {
// order.Status = model.OrderStatusFinished // todo 是否需要强制设置完成状态?
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)
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)
if err == nil {
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)
return err
}
@@ -134,7 +147,7 @@ func (c *BaseScheduler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, ord
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AcceptOrRefuseFailedGetOrder(ctx, order, isAcceptIt)
}
if err == nil {
flag := int8(model.OrderFlagAgreeFailedGetGoods)
flag := model.OrderFlagAgreeFailedGetGoods
if !isAcceptIt {
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)
}
if err == nil {
flag := int8(model.OrderFlagAgreeUserApplyCancel)
flag := model.OrderFlagAgreeUserApplyCancel
if !isAcceptIt {
flag = model.OrderFlagRefuseUserApplyCancel
}
@@ -176,3 +189,43 @@ func (c *BaseScheduler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model
}
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

@@ -25,9 +25,10 @@ import (
)
const (
time2Delivered = 1 * time.Hour // 正常从下单到送达的时间。
minute2Schedule3rdCarrier = 20 // 收到平台方自有配送的新运单消息后等待创建三方配送运单的时间分钟如果是定时达会再根据ExpectedDeliveredTime与dingShiDaAheadTime做调整
minMinute2Schedule3rdCarrier = 5 // 转三方配送最少等待时间(分钟)
time2Delivered = 1 * time.Hour // 正常从下单到送达的时间。
minute2Schedule3rdCarrier = 20 // 收到平台方自有配送的新运单消息后等待创建三方配送运单的时间分钟如果是定时达会再根据ExpectedDeliveredTime与dingShiDaAheadTime做调整
minute2Schedule3rdCarrier4Ebai = 30 // 饿百的最少转自配送需要的时间(分钟)
minMinute2Schedule3rdCarrier = 5 // 转三方配送最少等待时间(分钟)
time2AutoPickupMin = 15 * time.Minute // 自动拣货等待时间这个只有在没有PickDeadline信息才有用否则会根据PickDeadline设置
second2AutoPickupGap = 60 //随机60秒
@@ -64,13 +65,22 @@ type WatchOrderInfo struct {
timerStatusType int // 0表示订单1表示运单
timerStatus int
timer *time.Timer
timerTime time.Time
retryCount int // 失败后尝试的次数,调试阶段可能出现死循化,阻止这种情况发生
}
type StatusActionConfig struct {
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.storeDeliveryType = FixedScheduler.GetStoreDeliveryType(order, storeMap)
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
}
@@ -136,7 +141,7 @@ func init() {
TimerType: partner.TimerTypeBaseStatusTime,
Timeout: 10 * time.Millisecond,
},
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) {
TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
order := savedOrderInfo.order
mobile := order.ConsigneeMobile
if order.ConsigneeMobile2 != "" {
@@ -144,10 +149,14 @@ func init() {
}
_ = 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 {
partner.CurOrderManager.OnOrderMsg(order, "自动接单失败", err.Error())
// 为了解决京东新消息与接单消息乱序的问题
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 {
sch.OnOrderStatusChanged(order, model.Order2Status(order2), false)
order.Status = order2.Status
jxutils.CallMsgHandlerAsync(func() {
sch.OnOrderStatusChanged(order, model.Order2Status(order2), false)
}, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
err = nil
} else {
err = err2
@@ -165,6 +174,9 @@ func init() {
})
return nil
},
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.order.Status == model.OrderStatusNew
},
},
model.OrderStatusAccepted: &StatusActionConfig{ // 自动拣货
StatusActionParams: partner.StatusActionParams{
@@ -172,12 +184,17 @@ func init() {
Timeout: time2AutoPickupMin,
TimeoutGap: second2AutoPickupGap,
},
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) {
TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
if savedOrderInfo.autoPickupTimeoutMinute > 0 {
return sch.autoPickupGood(savedOrderInfo)
if err = sch.autoPickupGood(savedOrderInfo); err != nil {
partner.CurOrderManager.OnOrderMsg(savedOrderInfo.order, "自动拣货失败", err.Error())
}
}
return nil
},
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.autoPickupTimeoutMinute > 0
},
},
model.OrderStatusFinishedPickup: &StatusActionConfig{
StatusActionParams: partner.StatusActionParams{
@@ -185,27 +202,62 @@ func init() {
Timeout: 1 * time.Second,
TimeoutGap: 0,
},
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) {
TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore { // 自配送商家使用
return sch.createWaybillOn3rdProviders(savedOrderInfo, nil)
}
return nil
},
ShouldSetTimer: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) bool {
return savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore
},
},
},
map[int]*StatusActionConfig{
// todo 平台物流二次创建运单的话这个TIMER有问题
model.WaybillStatusNew: &StatusActionConfig{
StatusActionParams: partner.StatusActionParams{
TimerType: partner.TimerTypeBaseStatusTime,
Timeout: minute2Schedule3rdCarrier * time.Minute,
},
TimeoutAction: func(savedOrderInfo *WatchOrderInfo) (err error) {
if savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore { // 非自配送商家使用
TimeoutAction: func(savedOrderInfo *WatchOrderInfo, bill *model.Waybill) (err error) {
// 饿百转自送的时机不太清楚,暂时禁用超时转自送,在饿百运单取消时还是会自动创建
if savedOrderInfo.storeDeliveryType != scheduler.StoreDeliveryTypeByStore && savedOrderInfo.order.VendorID != model.VendorIDEBAI { // 非自配送商家使用
return sch.createWaybillOn3rdProviders(savedOrderInfo, 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)
if order == nil {
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.SetOrder(order)
// s.updateOrderByStatus(savedOrderInfo.order, status)
// if status.Status == model.OrderStatusNew {
// 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)) { // 只处理状态转换,一般消息不处理
if status.Status == model.OrderStatusRefuseFailedGetGoods && order.Status != model.OrderStatusFinishedPickup && !model.IsOrderFinalStatus(order.Status) {
order.Status = model.OrderStatusFinishedPickup
partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
s.resetTimer(savedOrderInfo, nil, isPending)
if status.Status >= model.OrderStatusDelivering {
@@ -271,12 +324,12 @@ func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *mod
if !(status.Status == model.OrderStatusCanceled) { // 订单取消时,取消所有运单
curWaybill = savedOrderInfo.waybills[savedOrderInfo.order.WaybillVendorID]
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)
}
}
}
s.cancelOtherWaybills(savedOrderInfo, curWaybill, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrOrderAlreadyFinished)
s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, curWaybill, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrOrderAlreadyFinished)
if status.Status >= model.OrderStatusEndBegin {
s.orderMap.Delete(jxutils.GetUniversalOrderIDFromOrderStatus(status))
}
@@ -285,12 +338,13 @@ func (s *DefScheduler) OnOrderStatusChanged(order *model.GoodsOrder, status *mod
if order.LockStatus != model.OrderStatusUnknown {
s.stopTimer(savedOrderInfo)
}
if model.IsOrderLockStatus(status.Status) ||
model.IsOrderUnlockStatus(status.Status) ||
status.Status == model.OrderStatusApplyFailedGetGoods ||
status.Status == model.OrderStatusAgreeFailedGetGoods ||
status.Status == model.OrderStatusDeliverFailed {
if isPending {
if !isPending {
if status.Status == model.OrderStatusFinishedPickup {
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.OrderStatusDeliverFailed {
if status.Status == model.OrderStatusApplyCancel {
utils.CallFuncAsync(func() {
weixinmsg.NotifyUserApplyCancel(savedOrderInfo.order, status.Remark)
@@ -323,10 +377,10 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending boo
if !isPending {
if order.Status > model.OrderStatusEndBegin {
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)
if s.IsOrderPlatformWaybill(bill) { // 是购物平台运单
if order.VendorID != order.WaybillVendorID { // 既有运单不是购物平台运单
if model.IsWaybillPlatformOwn(bill) { // 是购物平台运单
if !model.IsOrderHaveOwnWaybill(order) { // 既有运单不是购物平台运单
globals.SugarLogger.Infof("OnWaybillStatusChanged bill:%v purchase platform bill came later than others, strange!!!", bill)
oldBill := savedOrderInfo.waybills[order.WaybillVendorID]
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)
}
}
bill.WaybillVendorID = model.VendorIDUnknown
s.updateOrderByBill(order, bill, false)
s.updateOrderByBill(order, nil, false)
} else {
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
if s.IsOrderPlatformWaybill(bill) {
if model.IsWaybillPlatformOwn(bill) {
s.resetTimer(savedOrderInfo, bill, isPending)
}
} else {
isBillExist := s.updateBillsInfo(savedOrderInfo, bill)
if !isBillExist {
// s.addWaybill2Map(savedOrderInfo, bill) // updateBillsInfo中会添加
globals.SugarLogger.Debugf("OnWaybillStatusChanged bill not exist! orderID:%s, bill:%v", bill.VendorOrderID, bill)
}
switch bill.Status {
case model.WaybillStatusAccepted:
case model.WaybillStatusAccepted, model.WaybillStatusCourierArrived, model.WaybillStatusDelivering:
s.resetTimer(savedOrderInfo, bill, isPending)
if (isBillExist || bill.WaybillVendorID != model.VendorIDDada) && !isPending { // todo 达达运单有错序的情况,临时看看
isBillAlreadyCandidate := s.isBillCandidate(order, bill)
// todo 购买平台的运单优先级最高但这样写也可能带来问题即在这个时间因为之前3方已经接单已经发出了转自送请求而且可能成功了所以加个状态判断
if order.WaybillVendorID == model.VendorIDUnknown ||
(s.IsOrderPlatformWaybill(bill) && order.VendorID != order.WaybillVendorID && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0) {
if s.IsOrderHasWaybill(order) {
if !model.IsOrderHaveWaybill(order) ||
(model.IsWaybillPlatformOwn(bill) && !model.IsOrderHaveOwnWaybill(order) && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0) {
if model.IsOrderHaveWaybill(order) {
// 进到这里的原因是在这个时间点购物平台物流已经抢单但抢单消息还没有被收到比如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.cancelOtherWaybills(savedOrderInfo, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
if !s.IsOrderPlatformWaybill(bill) {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore {
s.SelfDeliverDelivering(savedOrderInfo.order, bill.CourierMobile)
s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
if model.IsWaybillPlatformOwn(bill) {
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 {
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 {
// 发生这种情况的原因就是两个接单事件几乎同时到达(来不及取消),也算正常
s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
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) {
utils.CallFuncAsync(func() {
weixinmsg.NotifyWaybillStatus(bill, order, isBillAlreadyCandidate)
})
}
if isBillAlreadyCandidate && !s.isWaybillCourierSame(savedOrderInfo, bill) && !model.IsWaybillPlatformOwn(bill) {
utils.CallFuncAsync(func() {
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:
if s.isBillCandidate(order, bill) {
s.resetTimer(savedOrderInfo, bill, isPending)
if !isPending {
bill.WaybillVendorID = model.VendorIDUnknown
s.updateOrderByBill(order, bill, false)
// 取消抢单应该不需要发3方运单
// s.createWaybillOn3rdProviders(savedOrderInfo, bill)
s.updateOrderByBill(order, nil, true)
}
} else if s.IsOrderHasWaybill(order) {
} else if model.IsOrderHaveWaybill(order) {
s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
if !isPending {
globals.SugarLogger.Warnf("OnWaybillStatusChanged AcceptCanceled orderID:%s got multiple bill:%v, order details:%v", order.VendorOrderID, bill, order)
}
}
case model.WaybillStatusCourierArrived: // do nothing
s.resetTimer(savedOrderInfo, bill, isPending)
if s.isBillCandidate(order, bill) {
} else {
// 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)
}
// case model.WaybillStatusCourierArrived: // do nothing
// s.resetTimer(savedOrderInfo, bill, isPending)
// if s.isBillCandidate(order, bill) {
// } else {
// // 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)
// }
case model.WaybillStatusCanceled, model.WaybillStatusFailed:
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if s.isBillCandidate(order, bill) || order.WaybillVendorID == model.VendorIDUnknown {
s.resetTimer(savedOrderInfo, nil, isPending)
if !isPending {
if s.IsOrderHasWaybill(order) {
bill.WaybillVendorID = model.VendorIDUnknown
s.updateOrderByBill(order, bill, false)
if model.IsOrderHaveWaybill(order) {
s.updateOrderByBill(order, nil, true)
}
// 3方的运单取消才会重新发起创建3方订单购物平台的运单取消后它本身还会再创建新运单(NewWaybill事件有相应TIMER)),至少京东是这样的,暂时按京东的行为来
// 现在发现饿百取消订单后不会再创建运单了,所以饿百运单取消也允许直接创建三方运单
// 之前的条件是order.Status < model.OrderStatusDelivering但像订单902322817000122确实有在配送中取消状态改成非订单结束状态都可以
// OrderStatusFinishedPickup状态的订单依赖于TIMER重新建运单
if order.Status >= model.OrderStatusDelivering && order.Status < model.OrderStatusEndBegin && (bill.WaybillVendorID != order.VendorID || order.VendorID == model.VendorIDEBAI) {
s.createWaybillOn3rdProviders(savedOrderInfo, nil)
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)
}
}
}
}
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:
s.resetTimer(savedOrderInfo, bill, isPending)
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if !s.IsOrderPlatformWaybill(bill) && !isPending {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore {
s.SelfDeliverDelivered(order, "")
} else {
s.Swtich2SelfDelivered(order, "")
if !isPending {
var err2 error
if !model.IsWaybillPlatformOwn(bill) {
if savedOrderInfo.storeDeliveryType == scheduler.StoreDeliveryTypeByStore {
err2 = s.SelfDeliverDelivered(order, "")
} else {
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) {
// 一般只会消息乱序才会到这里,即新订单消息在运单接单消息后到达
// 典型的一个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)
if order.WaybillVendorID == model.VendorIDUnknown {
if !model.IsOrderHaveWaybill(order) {
s.updateOrderByBill(order, bill, false)
}
}
@@ -461,14 +537,16 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending boo
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.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) {
if _, ok := savedOrderInfo.waybills[bill.WaybillVendorID]; ok {
if !s.IsOrderPlatformWaybill(bill) { // 购买平台重复发相同号的新运单是正常的,京东就是
if !model.IsWaybillPlatformOwn(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 (order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
if savedOrderInfo.retryCount <= maxWaybillRetryCount {
_, err = s.CreateWaybillOnProviders4SavedOrder(jxcontext.AdminCtx, savedOrderInfo, false)
savedOrderInfo.retryCount++
_, err = s.CreateWaybillOnProviders4SavedOrder(jxcontext.AdminCtx, savedOrderInfo, nil, false, 0, 0)
} else {
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 {
globals.SugarLogger.Debugf("createWaybillOn3rdProviders, orderID:%s, store:%d dont't support 3rd delivery platform", order.VendorOrderID, jxutils.GetSaleStoreIDFromOrder(order))
}
if err != nil {
partner.CurOrderManager.OnOrderMsg(order, "自动创建三方运单", utils.LimitUTF8StringLen(err.Error(), 255))
partner.CurOrderManager.OnOrderMsg(order, "自动创建三方运单失败", err.Error())
}
} else {
err = nil
@@ -521,73 +599,79 @@ func (s *DefScheduler) createWaybillOn3rdProviders(savedOrderInfo *WatchOrderInf
return err
}
func (s *DefScheduler) cancelOtherWaybills(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep)
func (s *DefScheduler) cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("cancelOtherWaybillsCheckOrderDeliveryFlag, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep)
if (savedOrderInfo.order.DeliveryFlag & model.OrderDeliveryFlagMaskScheduleDisabled) == 0 {
err = s.cancelOtherWaybills2(savedOrderInfo, bill2Keep, cancelReasonID, cancelReason)
err = s.cancelOtherWaybills(savedOrderInfo, bill2Keep, cancelReasonID, cancelReason)
} 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
}
func (s *DefScheduler) cancelOtherWaybills2(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("cancelOtherWaybills2, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep)
toBeDeleted := []*model.Waybill{}
func (s *DefScheduler) cancelOtherWaybills(savedOrderInfo *WatchOrderInfo, bill2Keep *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
globals.SugarLogger.Debugf("cancelOtherWaybills, orderID:%s, bill:%v", savedOrderInfo.order.VendorOrderID, bill2Keep)
for _, v := range savedOrderInfo.waybills {
if !s.IsOrderPlatformWaybill(v) && (bill2Keep == nil || !(v.WaybillVendorID == bill2Keep.WaybillVendorID && v.VendorWaybillID == bill2Keep.VendorWaybillID)) {
err2 := s.ProxyCancelWaybill(savedOrderInfo.order, v, cancelReasonID, cancelReason)
if v.Status < model.WaybillStatusEndBegin &&
!model.IsWaybillPlatformOwn(v) &&
(bill2Keep == nil || !(v.WaybillVendorID == bill2Keep.WaybillVendorID && v.VendorWaybillID == bill2Keep.VendorWaybillID)) {
err2 := s.CancelWaybill(v, cancelReasonID, cancelReason)
if err2 == nil {
toBeDeleted = append(toBeDeleted, v)
}
// 至少返回一个错误
if err == nil && err2 != nil {
err = err2
// 在这里就从map里删除而不是等收到运单结束事件才删除可避免不必要的重复取消第二次取消还会失败
s.removeWaybillFromMap(savedOrderInfo, v.WaybillVendorID)
} else {
// 至少返回一个错误
if err == nil {
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
}
func (s *DefScheduler) swtich2SelfDeliverWithRetry(savedOrderInfo *WatchOrderInfo, bill *model.Waybill, retryCount int, duration time.Duration) {
order := savedOrderInfo.order
globals.SugarLogger.Debugf("swtich2SelfDeliverWithRetry orderID:%s", order.VendorOrderID)
if (order.DeliveryFlag & model.OrderDeliveryFlagMaskPurcahseDisabled) == 0 {
if order.WaybillVendorID != order.VendorID {
if err := s.Swtich2SelfDeliver(order, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation {
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry failed, bill:%v, err:%v", bill, err)
if retryCount > 0 {
utils.AfterFuncWithRecover(duration, func() {
jxutils.CallMsgHandlerAsync(func() {
s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, retryCount-1, duration)
}, order.VendorOrderID)
})
} else {
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry finally failed, orderID:%s bill:%v, err:%v", order.VendorOrderID, bill, err)
if s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonSwitch2SelfFailed, partner.CancelWaybillReasonStrSwitch2SelfFailed) == nil {
// 转自送失败的取消,要将订单中的运单状态更新
if s.isBillCandidate(order, bill) {
bill.WaybillVendorID = model.VendorIDUnknown
s.updateOrderByBill(order, bill, false)
}
if order.WaybillVendorID != order.VendorID {
if err := s.Swtich2SelfDeliver(order, ""); err != nil && err != scheduler.ErrOrderStatusAlreadySatisfyCurOperation {
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry failed, bill:%v, err:%v", bill, err)
if retryCount > 0 {
utils.AfterFuncWithRecover(duration, func() {
jxutils.CallMsgHandlerAsync(func() {
s.swtich2SelfDeliverWithRetry(savedOrderInfo, bill, retryCount-1, duration)
}, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
})
} else {
errStr := fmt.Sprintf("订单:%s转自配送失败, 错误信息:%v", order.VendorOrderID, err)
globals.SugarLogger.Info(errStr)
if s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonSwitch2SelfFailed, partner.CancelWaybillReasonStrSwitch2SelfFailed) == nil {
// 转自送失败的取消,要将订单中的运单状态更新
if s.isBillCandidate(order, bill) {
s.updateOrderByBill(order, nil, false)
}
}
} else {
s.removeWaybillFromMap(savedOrderInfo, order.VendorID)
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
partner.CurOrderManager.OnOrderMsg(order, "转商家自配送失败", errStr)
}
} else {
// 进到这里的原因是,在这个时间点,购物平台物流已经抢单(但抢单消息还没有被收到),所以转自送会失败 比如818810379000941更好的做法应该是判断Swtich2SelfDeliver的返回值这种情况下就不得试了
globals.SugarLogger.Infof("swtich2SelfDeliverWithRetry orderID:%s status is wrong(maybe purchase platform accepted waybill)", order.VendorOrderID)
// globals.SugarLogger.Warnf("swtich2SelfDeliverWithRetry orderID:%s status is wrong, order details:%v", order.VendorOrderID, order)
utils.CallFuncAsync(func() {
weixinmsg.NotifyWaybillStatus(bill, order, false)
})
s.removeWaybillFromMap(savedOrderInfo, order.VendorID)
}
} else {
s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrNotAcceptIntime)
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
// 进到这里的原因是,在这个时间点,购物平台物流已经抢单(但抢单消息还没有被收到),所以转自送会失败 比如818810379000941更好的做法应该是判断Swtich2SelfDeliver的返回值这种情况下就不得试了
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)
}
}
@@ -624,7 +708,7 @@ func (s *DefScheduler) stopTimer(savedOrderInfo *WatchOrderInfo) {
if savedOrderInfo.timer != nil {
globals.SugarLogger.Debugf("stopTimer orderID:%s", savedOrderInfo.order.VendorOrderID)
savedOrderInfo.timer.Stop()
savedOrderInfo.timerStatus = 0
savedOrderInfo.timerStatus = model.OrderStatusUnknown
savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown
savedOrderInfo.timer = nil
}
@@ -641,47 +725,61 @@ func (s *DefScheduler) resetTimer(savedOrderInfo *WatchOrderInfo, bill *model.Wa
statusTime = bill.StatusTime
}
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)
if config == nil || config.TimerType != partner.TimerTypeByPass {
s.stopTimer(savedOrderInfo)
}
if config != nil && config.TimeoutAction != nil && config.TimerType != partner.TimerTypeByPass {
timeout := config.GetRefTimeout(statusTime)
if config.TimeoutGap != 0 {
timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second
}
if isPending && timeout < pendingOrderTimerMaxSecond*time.Second { // 如果是PENDING的订单则将其分布到2--5秒内让后续事件有机会执行
timeout = time.Duration(jxutils.MapValue2Scope(int64(timeout), -pendingOrderTimerMinMinSecond*1000, pendingOrderTimerMaxSecond*1000, pendingOrderTimerMinSecond*1000, pendingOrderTimerMaxSecond*1000)) * time.Millisecond
} else if timeout < 0 {
timeout = 0
}
if timeout == 0 {
config.TimeoutAction(savedOrderInfo)
} else {
timerName := ""
if statusType == scheduler.TimerStatusTypeOrder {
timerName = model.OrderStatusName[status]
} else if statusType == scheduler.TimerStatusTypeWaybill {
timerName = model.WaybillStatusName[status]
if config.CallShouldSetTimer(savedOrderInfo, bill) {
timeout := config.GetRefTimeout(statusTime, order.OrderCreatedAt)
if config.TimeoutGap != 0 {
timeout += time.Duration(rand.Intn(int(config.TimeoutGap))) * time.Second
}
savedOrderInfo.timerStatusType = statusType
savedOrderInfo.timerStatus = status
savedOrderInfo.timer = utils.AfterFuncWithRecover(timeout, func() {
jxutils.CallMsgHandlerAsync(func() {
globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID)
savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), true)
config.TimeoutAction(savedOrderInfo)
savedOrderInfo.timerStatus = 0
savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown
}, order.VendorOrderID)
})
if isPending && timeout < pendingOrderTimerMaxSecond*time.Second { // 如果是PENDING的订单则将其分布到2--5秒内让后续事件有机会执行
timeout = time.Duration(jxutils.MapValue2Scope(int64(timeout), -pendingOrderTimerMinMinSecond*1000, pendingOrderTimerMaxSecond*1000, pendingOrderTimerMinSecond*1000, pendingOrderTimerMaxSecond*1000)) * time.Millisecond
} else if timeout < 0 {
timeout = 0
}
if timeout == 0 {
config.TimeoutAction(savedOrderInfo, bill)
} else {
timerName := ""
if statusType == scheduler.TimerStatusTypeOrder {
timerName = model.OrderStatusName[status]
} else if statusType == scheduler.TimerStatusTypeWaybill {
timerName = model.WaybillStatusName[status]
}
savedOrderInfo.timerStatusType = statusType
savedOrderInfo.timerStatus = status
savedOrderInfo.timerTime = time.Now().Add(timeout)
savedOrderInfo.timer = utils.AfterFuncWithRecover(timeout, func() {
jxutils.CallMsgHandlerAsync(func() {
globals.SugarLogger.Debugf("fire timer:%s, orderID:%s", timerName, order.VendorOrderID)
savedOrderInfo := s.loadSavedOrderFromMap(model.Order2Status(order), true)
config.TimeoutAction(savedOrderInfo, bill)
savedOrderInfo.timerStatus = 0
savedOrderInfo.timerStatusType = scheduler.TimerStatusTypeUnknown
}, 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) {
s.locker.RLock()
defer func() {
@@ -709,7 +807,7 @@ func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, st
TimeoutGap: -1,
}
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.TimeoutGap = 0
}
@@ -742,10 +840,18 @@ func (s *DefScheduler) mergeOrderStatusConfig(savedOrderInfo *WatchOrderInfo, st
}
}
} 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{
TimerType: partner.TimerTypeBaseNow,
Timeout: order.PickDeadline.Sub(time.Now()) - time2AutoPickupAhead - second2AutoPickupGap*time.Second,
TimeoutGap: second2AutoPickupGap,
Timeout: timeout,
TimeoutGap: realSecond2AutoPickupGap,
}
}
}
@@ -795,40 +901,39 @@ func (s *DefScheduler) handleAutoAcceptOrder(orderID string, vendorID int, userM
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) {
if order.Status > model.OrderStatusEndBegin {
return
}
if bill.WaybillVendorID == model.VendorIDUnknown {
bill.VendorWaybillID = ""
updateFields := []string{
"WaybillVendorID",
"VendorWaybillID",
}
if bill == nil {
order.WaybillVendorID = model.VendorIDUnknown
order.VendorWaybillID = ""
} else {
order.WaybillVendorID = bill.WaybillVendorID
order.VendorWaybillID = bill.VendorWaybillID
}
partner.CurOrderManager.UpdateWaybillVendorID(bill, revertStatus)
order.WaybillVendorID = bill.WaybillVendorID
order.VendorWaybillID = bill.VendorWaybillID
if revertStatus {
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) {
if savedOrderInfo != nil {
if savedBill := savedOrderInfo.waybills[bill.WaybillVendorID]; savedBill != nil {
isBillExist = true
if savedBill.Status > bill.Status {
bill.Status = savedBill.Status
} else if bill.Status > savedBill.Status {
savedBill.Status = bill.Status
}
// if savedBill.Status > bill.Status {
// bill.Status = savedBill.Status
// } else if bill.Status > savedBill.Status {
// savedBill.Status = bill.Status
// }
}
savedOrderInfo.waybills[bill.WaybillVendorID] = bill
}
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) {
globals.SugarLogger.Debugf("ProxyCancelWaybill orderID:%s", order.VendorOrderID)
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)
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"
)
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) {
jxutils.CallMsgHandler(func() {
err = func() (err error) {
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName)
status := &model.OrderStatus{
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}
savedOrderInfo := s.loadSavedOrderFromMap(status, true)
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
if savedOrderInfo != nil {
order := savedOrderInfo.order
err = s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
if err == nil {
// todo
if true { //order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 {
if err = s.isPossibleSwitch2SelfDelivery(order); err == nil {
err = s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
if err == nil {
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)
phone := userName
if err = err2; err == nil {
@@ -40,7 +42,7 @@ func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, ven
err = s.SelfDeliverDelivering(order, phone)
}
} else {
if order.Status <= model.OrderStatusFinishedPickup {
if order.Status < model.OrderStatusDelivering {
err = s.Swtich2SelfDeliver(order, userName)
} else if order.VendorID == order.WaybillVendorID { // 状态为配送中,且是购物平台运单,不能转自送了
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
@@ -51,7 +53,7 @@ func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, ven
if err == nil {
order.Status = model.OrderStatusDelivering
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)
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
return err
@@ -70,25 +72,47 @@ func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, ven
func (s *DefScheduler) canOrderCreateWaybillNormally(order *model.GoodsOrder) (err error) {
if !(order.LockStatus != model.OrderStatusLocked && order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) {
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))
}
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
err = s.canOrderCreateWaybillNormally(order)
if forceCreate || err == nil {
err = nil
if !forceCreate {
err = s.canOrderCreateWaybillNormally(order)
}
if err == nil {
feeHandler := delivery.DefCreateWaybillPolicy
if forceCreate {
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 {
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
}
if err == nil {
if forceCreate {
@@ -105,7 +129,7 @@ func (s *DefScheduler) CreateWaybillOnProviders4SavedOrder(ctx *jxcontext.Contex
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() {
bills, err = func() (bills []*model.Waybill, err error) {
userName := ctx.GetUserName()
@@ -113,19 +137,15 @@ func (s *DefScheduler) CreateWaybillOnProvidersEx(ctx *jxcontext.Context, vendor
if vendorID == model.VendorIDELM {
return nil, fmt.Errorf("不要直接使用饿了么订单号,请使用相应的饿百订单号")
}
status := &model.OrderStatus{
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}
savedOrderInfo := s.loadSavedOrderFromMap(status, true)
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
if savedOrderInfo != nil {
order := savedOrderInfo.order
if scheduler.StoreDeliveryTypeByStore != s.GetStoreDeliveryType(order, nil) &&
order.Status == model.OrderStatusFinishedPickup &&
time.Now().Sub(order.StatusTime) < minMinute2Schedule3rdCarrier*time.Minute {
return nil, fmt.Errorf("非自配送门店转3方配送至少要求拣货完成后%d分钟才能操作", minMinute2Schedule3rdCarrier)
if !forceCreate {
err = s.isPossibleSwitch2SelfDelivery(order)
}
if err == nil {
bills, err = s.CreateWaybillOnProviders4SavedOrder(ctx, savedOrderInfo, courierVendorIDs, forceCreate, maxAddFee, maxDiffFee2Mtps)
}
bills, err = s.CreateWaybillOnProviders4SavedOrder(ctx, savedOrderInfo, forceCreate)
} else {
err = scheduler.ErrCanNotFindOrder
}
@@ -141,13 +161,9 @@ func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderI
jxutils.CallMsgHandler(func() {
err = func() (err error) {
globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s", vendorOrderID, ctx.GetUserName())
status := &model.OrderStatus{
RefVendorOrderID: vendorOrderID,
RefVendorID: vendorID,
}
savedOrderInfo := s.loadSavedOrderFromMap(status, true)
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
if savedOrderInfo != nil {
err = s.cancelOtherWaybills2(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
err = s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
} else {
err = scheduler.ErrCanNotFindOrder
}
@@ -155,7 +171,7 @@ func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderI
if err == nil && isStopSchedule {
order := savedOrderInfo.order
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
if err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order); err == nil {
if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
s.stopTimer(savedOrderInfo)
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))
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)
// 以下是售后单
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

@@ -20,7 +20,12 @@ const (
)
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) {
@@ -55,6 +60,8 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
"shopChineseNames": model.ShopChineseNames,
"printerVendorInfo": model.PrinterVendorInfo,
"purchaseVendorInfo": model.PurchaseVendorInfo,
"afsReasonTypeName": model.AfsReasonTypeName,
"afsAppealTypeName": model.AfsAppealTypeName,
},
}
Init()
@@ -124,12 +131,18 @@ func UpdatePlaces(ctx *jxcontext.Context, places []map[string]interface{}, userN
if len(places) == 0 {
return 0, ErrMissingInput
}
updateFields := []string{}
for k := range places[0] {
if allowUpdatePlaceFieldsMap[k] {
updateFields = append(updateFields, k)
}
}
for _, place := range places {
if place["code"] == nil {
return 0, ErrMissingInput
}
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 {
return num, err
}

View File

@@ -13,6 +13,12 @@ import (
"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) {
db := dao.GetDB()
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) {
sql := `
SELECT SQL_CALC_FOUND_ROWS t1.*
SELECT SQL_CALC_FOUND_ROWS
t1.*,
t2.title
FROM message_status t1
JOIN message t2 ON t2.id = t1.message_id
WHERE 1 = 1
`
sqlParams := []interface{}{}
@@ -167,7 +176,7 @@ func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fro
db := dao.GetDB()
dao.Begin(db)
defer dao.Commit(db)
var msgStatusList []*model.MessageStatus
var msgStatusList []*MessageStatusExt
// globals.SugarLogger.Debug(sql)
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
if err = dao.GetRows(db, &msgStatusList, sql, sqlParams...); err == nil {

View File

@@ -6,6 +6,7 @@ import (
"strconv"
"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"
@@ -14,6 +15,7 @@ import (
"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"
)
type SkuNamesInfo struct {
@@ -25,6 +27,19 @@ var (
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表示所有
func GetVendorCategories(ctx *jxcontext.Context, vendorID int, parentID string) (vendorCats []*model.SkuVendorCategory, err error) {
cond := map[string]interface{}{
@@ -72,7 +87,7 @@ func AddCategory(ctx *jxcontext.Context, cat *model.SkuCategory, userName string
dao.WrapAddIDCULDEntity(cat, userName)
cat.JdSyncStatus = model.SyncFlagNewMask
cat.JdID = 0 //jxutils.GenFakeID()
cat.JdID = 0
cat.Name = strings.Trim(cat.Name, " ")
if cat.Seq <= 0 {
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) {
cat := &model.SkuCategory{}
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 {
syncStatus := 0
if valid["name"] != nil {
valid["name"] = strings.Trim(valid["name"].(string), " ")
syncStatus = model.SyncFlagModifiedMask
}
db := dao.GetDB()
if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, cat, valid, userName, nil, model.FieldJdSyncStatus, model.SyncFlagModifiedMask); err == nil {
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)
}
}
}
_, err = CurVendorSync.SyncCategory(ctx, db, categoryID, false, userName)
}
}
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) {
var cats []*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)
if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
sql += " OR t2.jd_id = ? OR t1.id = ? OR t1.category_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64)
sql += " OR t1.id = ? OR t2.id = ? OR t2.jd_id = ? OR t1.category_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
}
sql += ")"
}
@@ -340,6 +419,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.name,
t1.brand_id,
t1.category_id,
t1.jd_category_id,
t1.is_global,
t1.unit,
t1.price,
@@ -348,6 +428,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.status,
t1.is_spu,
t1.img_hash_code,
t1.desc_img,
t1.upc`
if isBySku {
sql += `,
@@ -365,6 +446,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.name,
t1.brand_id,
t1.category_id,
t1.jd_category_id,
t1.is_global,
t1.unit,
t1.price,
@@ -373,6 +455,7 @@ func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params ma
t1.status,
t1.is_spu,
t1.img_hash_code,
t1.desc_img,
t1.upc,
t1.jd_id,
t1.jd_sync_status,
@@ -463,30 +546,39 @@ func AddSkuName(ctx *jxcontext.Context, skuNameExt *model.SkuNameExt, userName s
skuNameExt.SpecQuality = skuNameExt.Skus[0].SpecQuality
skuNameExt.SpecUnit = skuNameExt.Skus[0].SpecUnit
}
imgContent, imgMD5, err := jxutils.DownloadFileByURL(skuNameExt.Img)
if err != nil {
dao.Rollback(db)
return nil, err
}
if skuNameExt.ImgHashCode == "" {
skuNameExt.ImgHashCode = imgMD5
} else if skuNameExt.ImgHashCode != imgMD5 {
dao.Rollback(db)
return nil, errors.New("图片HASH值不同")
}
imgHintMap, err := UploadImg2Platforms(ctx, nil, skuNameExt.Img, imgContent, "")
if err != nil {
dao.Rollback(db)
return nil, err
}
skuNameExt.ImgWeimob = imgHintMap[model.VendorIDWSC]
skuNameExt.ImgEbai = imgHintMap[model.VendorIDEBAI]
if globals.EnableStoreWrite {
imgContent, imgMD5, err := jxutils.DownloadFileByURL(skuNameExt.Img)
if err != nil {
dao.Rollback(db)
return nil, err
}
if skuNameExt.ImgHashCode == "" {
skuNameExt.ImgHashCode = imgMD5
} else if skuNameExt.ImgHashCode != imgMD5 {
dao.Rollback(db)
return nil, errors.New("图片HASH值不同")
}
imgHintMap, err := UploadImg2Platforms(ctx, nil, skuNameExt.Img, imgContent, "")
if err != nil {
dao.Rollback(db)
return nil, err
}
skuNameExt.ImgWeimob = imgHintMap[model.VendorIDWSC]
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 {
dao.Rollback(db)
return nil, err
}
// beginJDID := jxutils.GenFakeID()
for _, sku := range skuNameExt.Skus {
dao.WrapAddIDCULDEntity(sku, userName)
sku.NameID = skuNameExt.ID
@@ -496,7 +588,6 @@ func AddSkuName(ctx *jxcontext.Context, skuNameExt *model.SkuNameExt, userName s
dao.Rollback(db)
return nil, err
}
// beginJDID++
}
for _, placeCode := range skuNameExt.Places {
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 {
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 = utils.RemoveGeneralMapKeys(valid, model.FieldSpecQuality, model.FieldSpecUnit)
_, hasPlaces := payload["places"]
@@ -550,19 +644,35 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
}
err = nil
}
if valid["img"] != nil {
imgContent, imgMD5, err2 := jxutils.DownloadFileByURL(valid["img"].(string))
if err = err2; err != nil {
return 0, err
if globals.EnableStoreWrite {
if valid["img"] != nil {
imgContent, imgMD5, err2 := jxutils.DownloadFileByURL(valid["img"].(string))
if err = err2; err != nil {
return 0, err
}
valid["ImgHashCode"] = imgMD5
imgHintMap, err := UploadImg2Platforms(ctx, nil, valid["img"].(string), imgContent, "")
if err != nil {
dao.Rollback(db)
return 0, err
}
valid["ImgWeimob"] = imgHintMap[model.VendorIDWSC]
valid["ImgEbai"] = imgHintMap[model.VendorIDEBAI]
}
valid["ImgHashCode"] = imgMD5
imgHintMap, err := UploadImg2Platforms(ctx, nil, valid["img"].(string), imgContent, "")
if err != nil {
dao.Rollback(db)
return 0, err
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"] = ""
}
}
valid["ImgWeimob"] = imgHintMap[model.VendorIDWSC]
valid["ImgEbai"] = imgHintMap[model.VendorIDEBAI]
}
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 {
@@ -588,8 +698,14 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
model.FieldNameID: nameID,
}, model.FieldJdSyncStatus, model.SyncFlagModifiedMask)
if err == nil {
dao.Commit(db)
_, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName)
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 {
dao.Commit(db)
_, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName)
}
}
}
}
@@ -597,6 +713,27 @@ func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interf
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) {
db := dao.GetDB()
dao.Begin(db)
@@ -647,7 +784,7 @@ func AddSku(ctx *jxcontext.Context, nameID int, sku *model.Sku, userName string)
dao.WrapAddIDCULDEntity(sku, userName)
sku.JdSyncStatus = model.SyncFlagNewMask
sku.NameID = nameID
sku.JdID = 0 //jxutils.GenFakeID()
sku.JdID = 0
if err = dao.CreateEntity(db, sku); err == nil {
result, err2 := GetSkuNames(ctx, "", false, utils.Params2Map("skuID", sku.ID), 0, 0)
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 {
return 0, err
}
dao.Commit(db)
_, err = CurVendorSync.SyncSku(ctx, db, -1, sku.ID, false, false, userName)
if _, err = SetStoreSkuSyncStatus2(db, nil, CurVendorSync.SingleStoreVendorIDs, []int{skuID}, model.SyncFlagModifiedMask); err == nil {
dao.Commit(db)
_, err = CurVendorSync.SyncSku(ctx, db, -1, sku.ID, false, false, userName)
}
} else {
err = ErrEntityNotExist
}

View File

@@ -10,8 +10,10 @@ import (
"git.rosy.net.cn/baseapi/platformapi/dadaapi"
"git.rosy.net.cn/baseapi/platformapi/feieapi"
"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/excel"
"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/tasksch"
@@ -19,6 +21,7 @@ import (
"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/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/api"
)
@@ -59,6 +62,37 @@ var (
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 门店绑定信息可以考虑以数组形式返回,而不是现在这样
func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interface{}, offset, pageSize int) (retVal *StoresInfo, err error) {
sql := `
@@ -92,17 +126,46 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
t1.printer_sn,
t1.printer_key,
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,
district.name district_name,
CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m1.vendor_store_id, '", "vendorID":', m1.vendor_id,
', "status":', m1.status, ', "pricePercentage":', m1.price_percentage, ', "vendorStoreName":"',
CONCAT('[', GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m1.vendor_store_id, '", "vendorID":', m1.vendor_id,
', "status":', m1.status, ', "pricePercentage":', m1.price_percentage, ', "vendorStoreName":"',
CASE m1.vendor_id
WHEN 0 THEN IF(jd.name IS NULL, '', jd.name)
WHEN 3 THEN IF(eb.col_name IS NULL, '', eb.col_name)
ELSE ''
END,
'"}')), ']') store_map_str,
CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"vendorStoreID":"', m2.vendor_store_id, '", "vendorID":', m2.vendor_id, ', "status":', m2.status, "}")), "]") courier_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
FROM store t1
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
@@ -228,7 +291,7 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
}
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
LIMIT ? OFFSET ?`
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(sql)
var storeList []*StoreExt
mapLimit := false
if err = dao.GetRows(db, &storeList, sql, sqlParams...); err == nil {
mapLimit := false
// globals.SugarLogger.Debugf("GetStores, len(storeList):%d", len(storeList))
var (
mapLatitude, mapLongitude float64
mapRadius int
@@ -289,8 +353,9 @@ func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interfa
}
}
dao.Commit(db)
retVal.MapCenterLng, retVal.MapCenterLat = getMapCenter(retVal.Stores)
if mapLimit {
retVal.MapCenterLng, retVal.MapCenterLat = getMapCenter(retVal.Stores)
}
return retVal, err
}
@@ -365,6 +430,15 @@ func GetVendorStore(ctx *jxcontext.Context, vendorStoreID string, vendorID int)
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) {
globals.SugarLogger.Debugf("UpdateStore storeID:%d, payload:%s", storeID, utils.Format4Output(payload, false))
@@ -458,14 +532,18 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa
dao.Rollback(db)
}()
if num, err = dao.UpdateEntityLogically(db, store, valid, userName, nil); err == nil && num == 1 {
dummy := &model.StoreMap{}
_, err2 := dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, dummy, nil, userName, map[string]interface{}{
model.FieldStoreID: store.ID,
}, model.FieldSyncStatus, syncStatus)
if err = err2; err == nil {
if isUpdateStoreNeedSync(valid) {
dummy := &model.StoreMap{}
_, err2 := dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, dummy, nil, userName, map[string]interface{}{
model.FieldStoreID: store.ID,
}, model.FieldSyncStatus, syncStatus)
if err = err2; err == nil {
dao.Commit(db)
globals.SugarLogger.Debugf("UpdateStore track:%s, before call SyncStore", ctx.GetTrackInfo())
_, err = CurVendorSync.SyncStore(ctx, db, -1, store.ID, false, userName)
}
} else {
dao.Commit(db)
globals.SugarLogger.Debugf("UpdateStore track:%s, before call SyncStore", ctx.GetTrackInfo())
_, err = CurVendorSync.SyncStore(ctx, db, -1, store.ID, false, userName)
}
}
} else {
@@ -474,6 +552,42 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa
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) {
globals.SugarLogger.Debugf("CreateStore storeExt:%s", utils.Format4Output(storeExt, false))
store := &storeExt.Store
@@ -547,6 +661,9 @@ func AddStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID
if storeID == 0 {
return nil, fmt.Errorf("storeID不能为0")
}
if vendorID != model.VendorIDJD && (storeMap.AutoPickup == 0) {
return nil, fmt.Errorf("非京东平台要求必须自动拣货")
}
userName := ctx.GetUserName()
if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
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) {
if vendorID != model.VendorIDJD {
if autoPickup, ok := payload["autoPickup"]; ok && autoPickup == 0 {
return 0, fmt.Errorf("非京东平台要求必须自动拣货")
}
}
storeHandler := CurVendorSync.GetStoreHandler(vendorID)
if storeHandler == nil {
return 0, ErrCanNotFindVendor
@@ -651,7 +773,7 @@ func UpdateStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendor
storeSkuBind := &model.StoreSkuBind{}
if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, storeSkuBind, nil, userName, map[string]interface{}{
model.FieldStoreID: storeID,
}, dao.GetSyncStatusStructField(model.VendorNames[vendorID]), model.SyncFlagModifiedMask); err != nil {
}, dao.GetSyncStatusStructField(model.VendorNames[vendorID]), model.SyncFlagPriceMask); err != nil {
return 0, err
}
}
@@ -797,9 +919,22 @@ func AddStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID
dao.WrapAddIDCULDEntity(storeCourierMap, userName)
storeCourierMap.StoreID = storeID
storeCourierMap.VendorID = vendorID
if db == nil {
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)
defer func() {
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 {
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
if err == nil {
_, err = CurVendorSync.SyncStore(ctx, db, storeCourierMap.VendorID, storeID, false, userName)
@@ -871,46 +997,74 @@ func RefreshMissingDadaStores(ctx *jxcontext.Context, storeID int, isAsync, isCo
task := tasksch.NewParallelTask("RefreshMissingDadaStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeDetail := batchItemList[0].(*dao.StoreDetail2)
var resultList []interface{}
if storeDetail.DadaStoreID == "" {
if storeDetail.DistrictName == "" || storeDetail.CityName == "" {
return nil, fmt.Errorf("门店:%s的城市码或区码有错误", storeDetail.Name)
}
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),
Status: model.StoreStatusOpened,
})
}); err == nil {
resultList = append(resultList, 1)
}
}
return nil, err
return resultList, err
}, storeList)
tasksch.HandleTask(task, nil, true).Run()
hint = task.ID
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
}
func updateOrCreateDadaStore(storeDetail *dao.StoreDetail2) (err error) {
_, err = api.DadaAPI.ShopDetail(storeDetail.DadaStoreID)
if err != nil {
if codeErr, ok := err.(*utils.ErrorWithCode); ok && codeErr.IntCode() == dadaapi.ResponseCodeShopNotExist {
_, err = api.DadaAPI.ShopAdd(storeDetail.DadaStoreID, composeDadaStoreName(storeDetail), dadaapi.BusinessTypeConvStore, storeDetail.CityName,
storeDetail.DistrictName, storeDetail.Address, jxutils.IntCoordinate2Standard(storeDetail.Lng), jxutils.IntCoordinate2Standard(storeDetail.Lat),
storeDetail.Tel1, storeDetail.Tel1, nil)
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))
}
} else {
params := map[string]interface{}{
"station_name": composeDadaStoreName(storeDetail),
"business": dadaapi.BusinessTypeConvStore,
"city_name": storeDetail.CityName,
"area_name": storeDetail.DistrictName,
"station_address": storeDetail.Address,
"lng": jxutils.IntCoordinate2Standard(storeDetail.Lng),
"lat": jxutils.IntCoordinate2Standard(storeDetail.Lat),
"contact_name": storeDetail.Tel1,
"phone": storeDetail.Tel1,
if dadaDistrictMap[storeDetail.DistrictName] != "" {
storeDetail.DistrictName = dadaDistrictMap[storeDetail.DistrictName]
}
}
if globals.EnableStoreWrite {
_, err = api.DadaAPI.ShopDetail(storeDetail.DadaStoreID)
if err != nil {
if codeErr, ok := err.(*utils.ErrorWithCode); ok && codeErr.IntCode() == dadaapi.ResponseCodeShopNotExist {
_, err = api.DadaAPI.ShopAdd(storeDetail.DadaStoreID, composeDadaStoreName(storeDetail), dadaapi.BusinessTypeConvStore, storeDetail.CityName,
storeDetail.DistrictName, storeDetail.Address, jxutils.IntCoordinate2Standard(storeDetail.Lng), jxutils.IntCoordinate2Standard(storeDetail.Lat),
storeDetail.Tel1, storeDetail.Tel1, nil)
}
} else {
params := map[string]interface{}{
"station_name": composeDadaStoreName(storeDetail),
"business": dadaapi.BusinessTypeConvStore,
"city_name": storeDetail.CityName,
"area_name": storeDetail.DistrictName,
"station_address": storeDetail.Address,
"lng": jxutils.IntCoordinate2Standard(storeDetail.Lng),
"lat": jxutils.IntCoordinate2Standard(storeDetail.Lat),
"contact_name": storeDetail.Tel1,
"phone": storeDetail.Tel1,
}
err = api.DadaAPI.ShopUpdate(storeDetail.DadaStoreID, params)
}
err = api.DadaAPI.ShopUpdate(storeDetail.DadaStoreID, params)
}
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)
}
return err
@@ -919,3 +1073,91 @@ func updateOrCreateDadaStore(storeDetail *dao.StoreDetail2) (err error) {
func composeDadaStoreName(storeDetail *dao.StoreDetail2) (storeName string) {
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"
"sort"
"strconv"
"sync"
"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/jx-callback/business/jxcallback/auth/weixin"
"git.rosy.net.cn/jx-callback/business/jxutils"
@@ -20,6 +26,8 @@ import (
)
const (
MaxSkuUnitPrice = 100000
CopyStoreSkuModeFresh = "fresh"
CopyStoreSkuModeUpdate = "update"
// CopyStoreSkuModeAdd = "add"
@@ -48,24 +56,26 @@ type StoreSkuNamesInfo struct {
// UpdateStoreSku用API调用时
type StoreSkuBindSkuInfo struct {
SkuID int `json:"skuID"`
IsSale int `json:"isSale"` // -1不可售0忽略1可售
IsSale int `json:"isSale,omitempty"` // -1不可售0忽略1可售
ElmID int64 `json:"elmID"`
EbaiID int64 `json:"ebaiID"`
ElmID int64 `json:"elmID,omitempty"`
EbaiID int64 `json:"ebaiID,omitempty"`
}
// UpdateStoreSku用API调用时
type StoreSkuBindInfo struct {
StoreID int `json:"storeID"`
NameID int `json:"nameID"`
UnitPrice int `json:"unitPrice"` // 对于是份的SKU就是单价每斤价格其它则为总价
IsFocus int `json:"isFocus"` // -1不关注0忽略1关注
IsSale int `json:"isSale"` // -1不可售0忽略1可售
SubStoreID int `json:"subStoreID"`
Skus []*StoreSkuBindSkuInfo `json:"skus"`
SubStoreID int `json:"subStoreID,omitempty"`
Skus []*StoreSkuBindSkuInfo `json:"skus,omitempty"`
}
type tStoreSkuBindAndSpec struct {
model.StoreSkuBind
Name string
SpecQuality float32
SpecUnit string
SkuNamePrice int
@@ -88,6 +98,11 @@ type StoreOpRequestInfo struct {
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) {
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)) + `)
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
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{}{
utils.DefaultTimeValue,
@@ -110,7 +125,7 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
storeIDs,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
model.SkuStatusNormal,
utils.Bool2Int(isFocus),
// model.SkuStatusNormal,
}
if isFocus {
@@ -122,12 +137,12 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
}
if keyword != "" {
keywordLike := "%" + keyword + "%"
sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t2.comment LIKE ?"
sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike)
sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t1.upc LIKE ? OR t2.comment LIKE ?"
sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike)
if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
sql += " OR t2.jd_id = ? OR t4.id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64)
sql += " OR t1.id = ? OR t2.id = ? OR t2.jd_id = ? OR t4.ebai_id = ? OR t4.mtwm_id = ?"
sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
}
sql += ")"
}
@@ -197,14 +212,36 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
sqlParams = append(sqlParams, skuIDs)
}
}
if isFocus && params["fromStatus"] != nil {
fromStatus := params["fromStatus"].(int)
toStatus := fromStatus
if params["toStatus"] != nil {
toStatus = params["toStatus"].(int)
if isFocus {
if params["fromStatus"] != nil {
fromStatus := params["fromStatus"].(int)
toStatus := fromStatus
if params["toStatus"] != nil {
toStatus = params["toStatus"].(int)
}
sql += " AND t4.status >= ? AND t4.status <= ?"
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 += " AND t4.status >= ? AND t4.status <= ?"
sqlParams = append(sqlParams, fromStatus, toStatus)
}
sql += `
GROUP BY
@@ -392,6 +429,80 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs []int, isFocus bool, keyword
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) {
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) {
// 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)
}
func UpdateStoresSkus(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
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))
if num > 0 {
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 == "" {
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()
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 == "" {
hint = utils.Int64ToStr(num)
@@ -504,13 +656,41 @@ func checkStoresSkusSaleCity(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []i
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)
globals.SugarLogger.Debugf("updateStoresSkusWithoutSync, storeIDs:%v, skuBindInfos:%s", storeIDs, utils.Format4Output(skuBindInfos, false))
db := dao.GetDB()
if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil {
return nil, err
if db == nil {
db = dao.GetDB()
}
// if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil {
// return nil, err
// }
if storeIDs, skuBindInfos, err = filterStorePriceChange(ctx, storeIDs, skuBindInfos); err != nil {
return nil, err
}
@@ -527,27 +707,45 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
}()
for _, storeID := range storeIDs {
for _, skuBindInfo := range skuBindInfos {
// 关注且没有给价时需要尝试从store_sku_bind中得到已有的单价
needGetExistingUnitPrice := skuBindInfo.UnitPrice == 0 && skuBindInfo.IsFocus == 1
inSkuBinds := skuBindInfo.Skus
var allBinds []*tStoreSkuBindAndSpec
sql := `
SELECT
t2.*,
t1.id real_sku_id, t1.spec_quality, t1.spec_unit,
t3.price sku_name_price, t3.unit sku_name_unit
t1.id real_sku_id, t1.spec_quality, t1.spec_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
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 = ?
JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ?
WHERE t1.name_id = ? AND t1.deleted_at = ?
FOR UPDATE`
JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ?`
sqlParams := []interface{}{
storeID,
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 len(allBinds) > 0 {
// globals.SugarLogger.Debug(utils.Format4Output(allBinds, false))
@@ -557,6 +755,10 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
}
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
} else {
unitPrice = allBinds[0].UnitPrice
@@ -650,7 +852,7 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBind
updateFieldMap[model.FieldUpdatedAt] = 1
updateFieldMap[model.FieldLastOperator] = 1
setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask)
// setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask)
dao.WrapUpdateULEntity(skuBind, userName)
if num, err = dao.UpdateEntity(db, skuBind /*, utils.Map2KeySlice(updateFieldMap)...*/); err != nil {
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{}{
model.FieldStatus: skuBind.Status,
model.FieldJdSyncStatus: skuBind.JdSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask,
model.FieldEbaiSyncStatus: skuBind.EbaiSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask,
model.FieldMtwmSyncStatus: skuBind.MtwmSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask,
model.FieldElmSyncStatus: skuBind.ElmSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask,
model.FieldWscSyncStatus: skuBind.WscSyncStatus | model.SyncFlagSaleMask | model.SyncFlagModifiedMask,
model.FieldJdSyncStatus: skuBind.JdSyncStatus | model.SyncFlagSaleMask,
model.FieldEbaiSyncStatus: skuBind.EbaiSyncStatus | model.SyncFlagSaleMask,
model.FieldMtwmSyncStatus: skuBind.MtwmSyncStatus | model.SyncFlagSaleMask,
model.FieldElmSyncStatus: skuBind.ElmSyncStatus | model.SyncFlagSaleMask,
model.FieldWscSyncStatus: skuBind.WscSyncStatus | model.SyncFlagSaleMask,
}, userName, map[string]interface{}{
model.FieldStoreID: storeID,
model.FieldSkuID: v.SkuID,
@@ -734,7 +936,21 @@ func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBind
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) {
storeIDs = uniqueStoreIDs(storeIDs)
skuBindSkuInfos = uniqueStoreSkuBind(skuBindSkuInfos)
var num int64
for _, storeID := range storeIDs {
skuIDs, err2 := updateStoreSkusSaleWithoutSync(ctx, storeID, skuBindSkuInfos, userName)
@@ -749,7 +965,7 @@ func UpdateStoresSkusSale(ctx *jxcontext.Context, storeIDs []int, skuBindSkuInfo
skuIDs = append(skuIDs, v.SkuID)
}
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 == "" {
hint = utils.Int64ToStr(num)
@@ -823,11 +1039,11 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID, toStoreID int, copyMode
now,
pricePercentage,
pricePercentage,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask,
model.SyncFlagPriceMask,
model.SyncFlagPriceMask,
model.SyncFlagPriceMask,
model.SyncFlagPriceMask,
model.SyncFlagPriceMask,
toStoreID,
utils.DefaultTimeValue,
}
@@ -927,11 +1143,11 @@ func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID, toStoreID int, copyMode
pricePercentage,
pricePercentage,
pricePercentage,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask,
model.SyncFlagModifiedMask | model.SyncFlagPriceMask | model.SyncFlagSaleMask,
model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagStoreSkuOnlyMask,
model.SyncFlagStoreSkuOnlyMask,
toStoreID,
utils.DefaultTimeValue,
}
@@ -1349,3 +1565,186 @@ func checkStoreExisting(db *dao.DaoDB, storeID int) (err error) {
}
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) {
return err
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeStatus:%d", vendorStoreID, storeStatus)
db := dao.GetDB()
storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, vendorStoreID, vendorID)

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"reflect"
"strings"
"git.rosy.net.cn/baseapi/utils"
"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) {
if jxutils.IsFakeID(cat.JdID) {
if jxutils.IsEmptyID(cat.JdID) {
return nil
}
return p.IMultipleStoresHandler.DeleteCategory(db, cat, userName)
}
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))
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) {
globals.SugarLogger.Debugf("wrapper DeleteSku, sku:%s", utils.Format4Output(sku, false))
if jxutils.IsFakeID(sku.JdID) {
if jxutils.IsEmptyID(sku.JdID) {
return nil
}
return p.IMultipleStoresHandler.DeleteSku(db, sku, userName)
}
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))
return nil
}
@@ -213,32 +214,36 @@ func (v *VendorSync) SyncStore(ctx *jxcontext.Context, db *dao.DaoDB, 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)
handler := v.GetStoreHandler(loopMapInfo.VendorID)
if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewParallelTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
var resultList []interface{}
storeMap := batchItemList[0].(*model.StoreMap)
if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil {
storeMap.SyncStatus = 0
_, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus)
resultList = append(resultList, 1)
}
return nil, err
return resultList, err
}, loopMapInfo.StoreMapList)
t.AddChild(loopStoreTask).Run()
_, err = loopStoreTask.GetResult(0)
return nil, err
resultList, err = loopStoreTask.GetResult(0)
} else {
storeMap := loopMapInfo.StoreMapList[0]
if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil {
storeMap.SyncStatus = 0
_, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus)
}
if err == nil {
resultList = []interface{}{1}
}
}
storeMap := loopMapInfo.StoreMapList[0]
if err = handler.UpdateStore(db, storeMap.StoreID, userName); err == nil {
storeMap.SyncStatus = 0
_, err = dao.UpdateEntity(db, storeMap, model.FieldSyncStatus)
}
err = jxutils.AddVendorInfo2Err(err, loopMapInfo.VendorID)
return nil, err
})
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) {
@@ -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)
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) {
var resultList []interface{}
vendorID := batchItemList[0].(int)
multiStoresHandler := v.GetMultiStoreHandler(vendorID)
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可能会有多线程问题
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) {
var resultList []interface{}
skuName := batchItemList[0].(*model.SkuName)
var skuList []*model.Sku
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))
if _, err = dao.UpdateEntity(db, sku, updateFields...); err != nil {
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))
_, err = dao.UpdateEntity(db, skuName, syncStatusFieldName)
}
return nil, err
return resultList, err
}, skuNameList)
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")
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) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetSingleStoreHandler(loopMapInfo.VendorID); handler != nil {
if isForce {
dao.SetStoreCategorySyncStatus(db, loopMapInfo.VendorID, storeIDs, nil, model.SyncFlagModifiedMask)
}
if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
@@ -367,73 +382,120 @@ func (v *VendorSync) SyncStoresCategory(ctx *jxcontext.Context, db *dao.DaoDB, v
}, len(loopMapInfo.StoreMapList))
t.AddChild(loopStoreTask).Run()
_, 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")
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) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil {
if isForce {
dao.SetStoreSkuSyncStatus(db, loopMapInfo.VendorID, storeIDs, skuIDs, model.SyncFlagModifiedMask)
}
if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
storeID := loopMapInfo.StoreMapList[step].StoreID
if _, err = handler.SyncStoreSkus(ctx, task, storeID, skuIDs, false, isContinueWhenError); err != nil {
globals.SugarLogger.Debugf("SyncStoresSkus failed1 store:%d failed with error:%v", storeID, err)
if isContinueWhenError {
err = nil
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) {
storeID := loopMapInfo.StoreMapList[step].StoreID
if _, err = handler.SyncStoreSkus(ctx, task, storeID, skuIDs, false, isContinueWhenError); err != nil {
globals.SugarLogger.Debugf("SyncStoresSkus failed1 store:%d failed with error:%v", storeID, err)
if isContinueWhenError {
err = nil
}
}
}
return nil, err
}, len(loopMapInfo.StoreMapList))
return nil, err
}, 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()
_, 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) {
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) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
if handler := v.GetStoreHandler(loopMapInfo.VendorID); handler != nil {
if len(loopMapInfo.StoreMapList) > 1 {
loopStoreTask := tasksch.NewSeqTask(fmt.Sprintf("处理平台%s", model.VendorChineseNames[loopMapInfo.VendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
storeID := loopMapInfo.StoreMapList[step].StoreID
_, err = handler.FullSyncStoreSkus(ctx, task, storeID, false, isContinueWhenError)
return nil, err
}, len(loopMapInfo.StoreMapList))
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) {
storeID := loopMapInfo.StoreMapList[step].StoreID
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
}, 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()
_, 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) {
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) {
loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
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))
t.AddChild(loopStoreTask).Run()
_, 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 := `
SELECT 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"
var storeMapList []*model.StoreMap
if err = dao.GetRows(db, &storeMapList, sql, sqlParams...); err != nil {
return "", err
return nil, "", err
}
if len(storeMapList) == 0 {
return "", nil
return nil, "", nil
}
vendorStoreMap := make(map[int][]*model.StoreMap)
for _, v := range storeMapList {
@@ -497,21 +559,40 @@ func (v *VendorSync) LoopStoresMap(ctx *jxcontext.Context, db *dao.DaoDB, taskNa
if len(loopInfoList) == 1 {
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()
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)
}
}
} else {
hint = task.GetID()
}
return task.ID, makeSyncError(err)
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) {
task := tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, handler, v.MultiStoreVendorIDs)
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
result, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(result))
}
} else {
hint = task.ID
}
return task.ID, makeSyncError(err)
return hint, makeSyncError(err)
}
func (v *VendorSync) RefreshAllSkusID(ctx *jxcontext.Context, isAsync bool, vendorIDs []int, storeIDs []int) (hint string, err error) {
@@ -552,8 +633,10 @@ func (v *VendorSync) RefreshAllStoresID(ctx *jxcontext.Context, isAsync bool, ve
func makeSyncError(err error) (newErr error) {
if err != nil {
return &SyncError{
Original: err,
if _, ok := err.(*SyncError); !ok {
return &SyncError{
Original: 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)
if err == nil {
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)
for i := 0; i < 3; i++ {
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()
billRec := &legacymodel.StoreBill{
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,
BillName: fileHeader.Filename,
ShopName: shopName,

View File

@@ -57,86 +57,135 @@ func insertPlace(ctx *jxcontext.Context, db *dao.DaoDB, parent *autonavi.Distric
}
func InitPlace(ctx *jxcontext.Context) (err error) {
placeList, err2 := api.AutonaviAPI.GetDistricts(autonavi.DistrictLevelDistrict, "")
if err = err2; err != nil {
return err
}
placeList = placeList[0].Districts
db := dao.GetDB()
if err = TruncateTable(db, "place"); err == nil {
placeList, err2 := api.AutonaviAPI.GetDistricts(autonavi.DistrictLevelDistrict, "")
if err = err2; err != nil {
return err
}
placeList = placeList[0].Districts
dao.Begin(db)
defer func() {
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
}()
if err = insertPlace(ctx, db, nil, placeList); err != nil {
return err
}
updateSqls := []string{
`
UPDATE place t1
JOIN jde_city t2 ON t1.code = t2.col_tencentAddressCode
SET t1.jd_code = t2.col_areaCode;
`,
`
UPDATE place t1
JOIN place t2 ON t1.parent_code = t2.code AND t2.jd_code != 0
JOIN jde_district t3 ON t1.name = t3.col_areaName AND t2.jd_code = t3.col_cityCode
SET t1.jd_code = t3.col_areaCode
WHERE t1.level = 3;
`,
`
UPDATE
place t1
JOIN ebde_places t2 ON t1.name = t2.col_city_name
SET t1.ebai_code = t2.col_city_id
WHERE t1.level = 1 OR t1.level = 2;
`,
`
UPDATE
place t1
JOIN place t1p ON t1.parent_code = t1p.code
JOIN ebde_places t2 ON t1.name = t2.col_city_name
JOIN ebde_places t2p ON t2.col_parent_id = t2p.col_city_id AND t1p.ebai_code = t2p.col_city_id
SET t1.ebai_code = t2.col_city_id
WHERE t1.level = 3;
`,
`
UPDATE
place t1
JOIN mtpsdeliveryprice t2 ON t1.code = t2.citycode
SET t1.mtps_price = t2.price;
`,
`
UPDATE
place t1
JOIN mtpsdeliveryprice t2 ON t1.name LIKE CONCAT(t2.cityname, '%')
SET t1.mtps_price = t2.price
WHERE t1.level = 2 AND t1.mtps_price = 0;
`,
`
UPDATE place t1
LEFT JOIN (
SELECT DISTINCT city_code
FROM store
UNION DISTINCT
SELECT DISTINCT place_code city_code
FROM sku_name_place_bind
) t2 ON t1.code = t2.city_code
SET t1.enabled = 0
WHERE t1.level = 2 AND t2.city_code IS NULL;
`,
}
for _, v := range updateSqls {
if _, err = dao.ExecuteSQL(db, v); err != nil {
return err
if r != nil {
panic(r)
}
}
dao.Commit(db)
}()
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 {
return err
}
updateSqls := []string{
`
UPDATE place t1
JOIN jde_city t2 ON t1.code = t2.col_tencentAddressCode
SET t1.jd_code = t2.col_areaCode;
`,
`
UPDATE place t1
JOIN place t2 ON t1.parent_code = t2.code AND t2.jd_code != 0
JOIN jde_district t3 ON t1.name = t3.col_areaName AND t2.jd_code = t3.col_cityCode
SET t1.jd_code = t3.col_areaCode
WHERE t1.level = 3;
`,
`
UPDATE
place t1
JOIN ebde_places t2 ON t1.name = t2.col_city_name
SET t1.ebai_code = t2.col_city_id
WHERE t1.level = 1 OR t1.level = 2;
`,
`
UPDATE
place t1
JOIN place t1p ON t1.parent_code = t1p.code
JOIN ebde_places t2 ON t1.name = t2.col_city_name
JOIN ebde_places t2p ON t2.col_parent_id = t2p.col_city_id AND t1p.ebai_code = t2p.col_city_id
SET t1.ebai_code = t2.col_city_id
WHERE t1.level = 3;
`,
`
UPDATE
place t1
JOIN mtpsdeliveryprice t2 ON t1.code = t2.citycode
SET t1.mtps_price = t2.price;
`,
`
UPDATE
place t1
JOIN mtpsdeliveryprice t2 ON t1.name LIKE CONCAT(t2.cityname, '%')
SET t1.mtps_price = t2.price
WHERE t1.level = 2 AND t1.mtps_price = 0;
`,
`
UPDATE place t1
LEFT JOIN (
SELECT DISTINCT city_code
FROM store
UNION DISTINCT
SELECT DISTINCT place_code city_code
FROM sku_name_place_bind
) t2 ON t1.code = t2.city_code
SET t1.enabled = 0
WHERE t1.level = 2 AND t2.city_code IS NULL;
`,
}
for _, v := range updateSqls {
if _, err = dao.ExecuteSQL(db, v); err != nil {
return err
}
}
dao.Commit(db)
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()
var skuNameList []*model.SkuName
if err = dao.GetRows(db, &skuNameList, `
@@ -169,7 +218,7 @@ func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError b
}
return nil, err
}, skuNameList)
tasksch.ManageTask(task).Run()
tasksch.HandleTask(task, parentTask, true).Run()
if !isAsync {
_, err = task.GetResult(0)
} else {
@@ -178,6 +227,26 @@ func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError b
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) {
if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
cats, err2 := handler.GetVendorCategories(ctx)
@@ -437,7 +506,7 @@ func BuildSkuFromEbaiStore(ctx *jxcontext.Context, baiduShopID int64, isAsync, i
}
price := sku.LinkID
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
if sku.Status != model.SkuStatusNormal {
fixedStatus = 2

View File

@@ -8,6 +8,7 @@ import (
"time"
"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/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
@@ -47,11 +48,10 @@ func RefreshRealMobile(ctx *jxcontext.Context, vendorID int, fromTime, toTime ti
sql := `
SELECT *
FROM goods_order
WHERE vendor_id = ? AND consignee_mobile2 = '' AND order_created_at <= ?
WHERE vendor_id = ? AND consignee_mobile2 = ''
`
sqlParams := []interface{}{
vendorID,
time.Now().Add(-4 * time.Hour), // 最近的刷新意义不大
}
if !utils.IsTimeZero(fromTime) {
sql += " AND order_created_at >= ?"
@@ -97,29 +97,6 @@ func StartGetCityStoreInfo() {
}
cityCenters = append(cityCenters, guiyang)
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) {
@@ -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) {
oneDu := 111319.55
for a := 0; a <= 80; a++ {
@@ -309,3 +229,25 @@ func GetCityPoints(lng float64, lat float64, cityName string) (cityPoints [][]st
}
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的活动价
LimitSkuCount int `json:"limitSkuCount"`
IsLock int8 `json:"isLock"`
EarningPrice int `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
}
type tPromotionItemInfo struct {
@@ -131,7 +133,7 @@ var (
type JdPromotionHandler interface {
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)
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)
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) {
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) {
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) {
return api.JdAPI.CreatePromotionSkuSingle(infoId, outInfoId, skus)
func (p *JdDirectDownHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error) {
return api.JdAPI.CreatePromotionSkuSingle(infoId, outInfoId, skus, "")
}
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) {
return api.JdAPI.CancelPromotionSingle(infoId, outInfoId)
return api.JdAPI.CancelPromotionSingle(infoId, outInfoId, "")
}
type JdLimitedTimeHandler struct {
}
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) {
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) {
return api.JdAPI.CreatePromotionSkuLimitTime(infoId, outInfoId, skus)
func (p *JdLimitedTimeHandler) CreatePromotionSku(infoId int64, outInfoId string, skus []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error) {
return api.JdAPI.CreatePromotionSkuLimitTime(infoId, outInfoId, skus, "")
}
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) {
return api.JdAPI.CancelPromotionLimitTime(infoId, outInfoId)
return api.JdAPI.CancelPromotionLimitTime(infoId, outInfoId, "")
}
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) {
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
}
func (p *JdNullHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
@@ -230,7 +232,10 @@ func Init() {
// 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") {
return "", fmt.Errorf("%s看起来不像是一个有效的京东活动ID请仔细检查一下", vendorPromotionID)
}
@@ -260,12 +265,12 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
userName := ctx.GetUserName()
db := dao.GetDB()
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
promotion := &model.Promotion{
Name: params.Name,
Advertising: params.Advertising,
VendorID: model.VendorIDJD,
VendorID: vendorID,
Type: params.Type,
Status: model.PromotionStatusLocalCreated,
LimitDevice: int8(limitDevice),
@@ -278,7 +283,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
Source: PromotionSourceOpenPlatform,
}
if vendorPromotionID == "" {
if vendorPromotionID == "" && vendorID == model.VendorIDJD {
skuIDs := make([]int, len(params.SkuPrices))
skuPriceMap := make(map[int64]*SkuPrice)
for k, v := range params.SkuPrices {
@@ -323,7 +328,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
if promotionSkuPrice.PriceType == PriceTypePercentage {
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)
}
if promotionSkuPrice.LimitSkuCount <= 0 {
@@ -338,13 +343,11 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
})
}
}
promotionPrices[index] = map[string]interface{}{
jdapi.KeyStationNo: utils.Str2Int64(skuBind.VendorStoreID),
jdapi.KeySkuId: skuBind.JdSkuID,
// jdapi.KeyOutStationNo: utils.Int2Str(skuBind.StoreID),
// jdapi.KeyOutSkuId: utils.Int2Str(skuBind.SkuID),
jdapi.KeyPromotionPrice: promotionSkuPrice.Price,
jdapi.KeyLimitSkuCount: promotionSkuPrice.LimitSkuCount,
promotionPrices[index] = &jdapi.PromotionSku{
StationNo: utils.Str2Int64(skuBind.VendorStoreID),
SkuID: skuBind.JdSkuID,
PromotionPrice: int64(promotionSkuPrice.Price),
LimitSkuCount: promotionSkuPrice.LimitSkuCount,
}
index++
}
@@ -402,6 +405,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
Price: skuPrice.Price,
LimitSkuCount: skuPrice.LimitSkuCount,
IsLock: skuPrice.IsLock,
EarningPrice: skuPrice.EarningPrice,
}
dao.WrapAddIDCULDEntity(promotionSku, ctx.GetUserName())
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)
if promotionHandler == nil {
return "", errors.New("非法的活动类型")
@@ -436,7 +440,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
for k, v := range modifyPrices {
modifyPrices2[k] = v.(*jdapi.SkuPriceInfo)
}
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
if _, err = api.JdAPI.UpdateVendorStationPrice(utils.Int2Str(storeID), "", modifyPrices2); err != nil {
return nil, err
}
@@ -451,9 +455,9 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
} else if step == 2 {
task2 := tasksch.NewParallelTask("CreateJdPromotion CreatePromotionSku", tasksch.NewParallelConfig().SetBatchSize(MaxPromotionSkuCount).SetIsContinueWhenError(isContinueWhenError), ctx,
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 {
skus[k] = v.(map[string]interface{})
skus[k] = v.(*jdapi.PromotionSku)
}
_, err = promotionHandler.CreatePromotionSku(infoId, "", skus)
return nil, err
@@ -510,7 +514,8 @@ func GetJdPromotions(ctx *jxcontext.Context, keyword string, params map[string]i
t1.end_at,
t1.advertising,
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
JOIN promotion_store t2 ON t1.id = t2.promotion_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
}
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) {
var err error
// 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) {
storeIDMap := make(map[int64]int)
skuIDMap := make(map[int64]int)
skuMap := make(map[int64]*jdapi.PromotionSkuResult)
skuMap := make(map[int64]*jdapi.PromotionLspQuerySkuResult)
// 注意这样处理可能是有问题我们假定的是门店信息与SKU信息的叉乘
for _, v := range result.SkuResultList {
storeIDMap[v.StationNo] = 1
skuIDMap[v.SkuId] = 1
skuMap[v.SkuId] = v
skuIDMap[v.SkuID] = 1
skuMap[v.SkuID] = v
}
jdStoreIDs := make([]string, len(storeIDMap))
index := 0
@@ -970,7 +1003,7 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
jxStoreIDs[k] = v.StoreID
}
priceList := make([]*SkuPrice, len(skuList))
var skuResult *jdapi.PromotionSkuResult
var skuResult *jdapi.PromotionLspQuerySkuResult
for k, v := range skuList {
skuResult = skuMap[v.JdID]
priceList[k] = &SkuPrice{
@@ -985,11 +1018,11 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
// globals.SugarLogger.Debugf("priceList:%s", utils.Format4Output(priceList, false))
source := strings.Trim(result.Source, "来源")
promotionParams := &PromotionParams{
Name: source + "-" + utils.Int64ToStr(result.PromotionInfoId),
Name: source + "-" + utils.Int64ToStr(result.PromotionInfoID),
Advertising: "",
Type: result.PromotionType,
BeginAt: result.BeginTime,
EndAt: result.EndTime,
BeginAt: result.BeginTime.GoTime(),
EndAt: result.EndTime.GoTime(),
StoreIDs: jxStoreIDs,
SkuPrices: priceList,
}
@@ -1002,7 +1035,7 @@ func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.Callba
mapData[keyLimitDevice] = skuResult.LimitDevice
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 {
err = nil
}
@@ -1048,7 +1081,7 @@ func getPromotionHander(promotionType int) JdPromotionHandler {
// panic(fmt.Sprintf("unknown promotion type:%d", promotionType))
return nil
}
if !globals.EnableStoreWrite {
if !globals.EnableJdStoreWrite {
promotionHandler = &JdNullHandler{}
}
return promotionHandler

View File

@@ -8,6 +8,8 @@ import (
"sync"
"time"
"git.rosy.net.cn/jx-callback/business/partner/delivery"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/orderman"
@@ -102,7 +104,7 @@ func Convert2JDSPU(ctx *jxcontext.Context, count int, isAsync, isContinueWhenErr
skuNew2 := *sku
skuNew := &skuNew2
dao.WrapAddIDCULEntity(skuNew, ctx.GetUserName())
skuNew.JdID = 0 //jxutils.GenFakeID()
skuNew.JdID = 0
skuNew.LinkID = sku.ID
skuNew.NameID = skuNameNew.ID
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 {
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
}
@@ -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,
func(subTask *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
skuName := batchItemList[0].(*model.SkuName)
if !jxutils.IsFakeID(skuName.JdID) {
if !jxutils.IsEmptyID(skuName.JdID) {
sql = `
SELECT *
FROM sku
@@ -727,8 +729,8 @@ func TransformJdSpu2Sku(ctx *jxcontext.Context, skuNameIDs []int, count int, isA
locker.Lock()
skuIDs = append(skuIDs, sku.ID)
locker.Unlock()
if !jxutils.IsFakeID(sku.JdID) {
if globals.EnableStoreWrite {
if !jxutils.IsEmptyID(sku.JdID) {
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 errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 11004 {
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 {
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 {
@@ -777,7 +779,7 @@ func TransformJdSpu2Sku(ctx *jxcontext.Context, skuNameIDs []int, count int, isA
rootTask.AddChild(subTask).Run()
if _, err = subTask.GetResult(0); err == nil {
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)
// _, 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
}
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 {
excelFile.NewSheet(sheetConfig.Title)
}
for index, name := range sheetConfig.CaptionList {
excelFile.SetCellStr(sheetConfig.Title, genAxis(0, index), name)
}
for i := 0; i < valueInfo.Len(); i++ {
var mapData map[string]interface{}
if typeInfo.Kind() == reflect.Struct {
mapData = utils.FlatMap(utils.Struct2MapByJson(valueInfo.Index(i).Interface()))
} else {
mapData = valueInfo.Index(i).Interface().(map[string]interface{})
isMemberStruct := typeInfo.Kind() == reflect.Struct
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 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]))
for i := 0; i < valueInfo.Len(); i++ {
for col, index := range indexSlice {
excelFile.SetCellStr(sheetConfig.Title, genAxis(i+1, col), fmt.Sprint(reflect.Indirect(valueInfo.Index(i)).FieldByIndex(index).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]))
}
}
}
}
@@ -77,6 +102,10 @@ func Excel2Slice(reader io.Reader) (contents map[string][][]string) {
return contents
}
func genAxis(row, col int) string {
return fmt.Sprintf("%c%d", col+65, row+1)
func genAxis(row, col int) (pos string) {
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
import (
"reflect"
"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) {
// kk := make([]*model.SkuName, 1)
// kk[0] = &model.SkuName{
@@ -24,3 +62,49 @@ func TestObj2Excel(t *testing.T) {
}
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/model"
"git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego"
)
type IAuther interface {
@@ -71,7 +70,7 @@ func New(notUsed interface{}, token string, w http.ResponseWriter, r *http.Reque
}
}
if err == model.ErrTokenIsInvalid {
if beego.BConfig.RunMode != "prod" {
if !globals.IsProductEnv() {
err = nil
} else {
errCode = model.ErrCodeTokenIsInvalid

View File

@@ -1,10 +1,13 @@
package jxutils
import (
"bytes"
"context"
"fmt"
"math"
"math/rand"
"regexp"
"sort"
"strings"
"sync"
"time"
@@ -14,7 +17,9 @@ import (
"git.rosy.net.cn/baseapi/utils/routinepool"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/qiniu/api.v7/storage"
)
var (
@@ -27,6 +32,25 @@ type SyncMapWithTimeout struct {
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() {
rand.Seed(time.Now().Unix())
routinePool = routinepool.New(1000, 1000)
@@ -82,6 +106,13 @@ func GetSkuIDFromOrderSku(sku *model.OrderSku) (skuID int) {
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) {
index := strings.Index(universalOrderID, "|")
if index != -1 {
@@ -122,6 +153,19 @@ func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) {
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 {
// return fmt.Sprintf("%s|%d", orderID, vendorID)
return orderID // 当前用长度就能区分先不加上vendorID
@@ -276,10 +320,7 @@ func ComposeSkuName(prefix, name, comment, unit string, spec_quality float32, sp
skuName += "(" + comment + ")"
}
if maxLen > 0 {
runeList := []rune(skuName)
if len(runeList) > maxLen {
skuName = string(runeList[:maxLen])
}
skuName = utils.LimitUTF8StringLen(skuName, maxLen)
}
return skuName
}
@@ -421,3 +462,122 @@ func HandleUserWXRemark(db *dao.DaoDB, mobile string) (err error) {
}
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"
"regexp"
"strings"
"sync"
"time"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
)
var (
@@ -82,9 +84,9 @@ func SplitStoreName(fullName, separator, defaultPrefix string) (prefix, bareName
func ComposeStoreName(bareName string, vendorID int) (fullName string) {
bareName = TrimDecorationChar(strings.Trim(bareName, "-"))
if vendorID == model.VendorIDJD {
fullName = "京西菜市-" + bareName
fullName = globals.StoreName + "-" + bareName
} else {
fullName = "京西菜市(" + bareName + ")"
fullName = globals.StoreName + "(" + bareName + ")"
}
return fullName
}
@@ -152,19 +154,24 @@ func Int64Map2List(int64Map map[int64]int) []int64 {
return retVal
}
// 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
if skuNameUnit != "份" {
return unitPrice
}
func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) {
lowerSpecUnit := strings.ToLower(specUnit)
if lowerSpecUnit == "kg" || lowerSpecUnit == "l" {
specQuality *= 1000
}
price := int(math.Round(float64(float32(unitPrice) * specQuality / 500)))
if specQuality < 250 {
return int(specQuality)
}
// 计算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
} else if specQuality < 500 {
} else if specQuality2 < 500 {
price = price * 105 / 100
}
if price <= 0 {
@@ -173,35 +180,100 @@ func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNa
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 {
return reflect.ValueOf(list).Len()
}
func CaculateSkuVendorPrice(price int, percentage int) int {
storePrice := int(math.Round(float64(price*percentage) / 100))
if storePrice < 0 {
storePrice = 0
func CaculateSkuVendorPrice(price, percentage, catPercentage int) int {
if percentage <= 10 || percentage >= 400 {
percentage = 100
}
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
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 {
return genFakeID1() * 3
}
func IsFakeID(id int64) bool {
if id == 0 {
if IsEmptyID(id) {
return true
}
multiple := id / genFakeID1()
return multiple >= 2 && multiple <= 4
}
func IsEmptyID(id int64) bool {
return id == 0
}
func FormalizePageSize(pageSize int) int {
if pageSize == 0 {
return model.DefPageSize
@@ -238,11 +310,7 @@ func IsLegalStoreID(id int) bool {
// 将规格转为重量
func FormatSkuWeight(specQuality float32, specUnit string) int {
lowerSpecUnit := strings.ToLower(specUnit)
if lowerSpecUnit == "kg" || lowerSpecUnit == "l" {
specQuality *= 1000
}
return int(specQuality)
return RegularizeSkuQuality(specQuality, specUnit)
}
type SkuList []*model.Sku
@@ -284,7 +352,7 @@ func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err err
/////
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) {
@@ -293,7 +361,7 @@ func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) {
vendorID = model.VendorIDJD
} else if vendorStoreID > 1234567 && vendorStoreID < 9876543 { // 美团外卖 24617137位
vendorID = model.VendorIDMTWM
} else if vendorStoreID > 1234567890 && vendorStoreID < 9987654321 { // 饿百 216700260710位
} else if vendorStoreID > 1234567890 && vendorStoreID < 99876543210 { // 饿百 216700260710位11位
vendorID = model.VendorIDEBAI
} else if vendorStoreID > 123456789 && vendorStoreID < 987654321 { // 微盟微商城 1320910489位
vendorID = model.VendorIDWSC
@@ -306,10 +374,3 @@ func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) {
func GetVendorName(vendorID int) (vendorName string) {
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 {
content = content[:dingdingapi.MaxWorkContentLen-4] + "..."
}
err = api.DingDingAPI.CorpAsyncSendSimple(auth.AuthID, content)
if globals.IsProductEnv() {
err = api.DingDingAPI.CorpAsyncSendSimple(auth.AuthID, content)
}
break
}
}

View File

@@ -14,7 +14,7 @@ import (
const (
testVendorOrderID = "test"
realTestVendorOrderID = "817102016000041"
realTestVendorOrderID = "901234567890123"
realTestOrderVendorID = model.VendorIDJD
)
@@ -27,14 +27,6 @@ func PrintOrder(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (pri
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
if err == nil {
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.JxStoreID = storeID
}

View File

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

View File

@@ -134,13 +134,13 @@ func (task *ParallelTask) Run() {
}
} 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)
task.locker.Lock()
task.detailErrList = append(task.detailErrList, err)
task.locker.Unlock()
if !task.IsContinueWhenError { // 出错
chanRetVal = err
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 {
task.OriginalErr = taskErr
task.Err = NewTaskError(task.Name, taskErr)
} else {
if len(task.detailErrList) > 0 {
task.OriginalErr = task.detailErrList[0]
}
task.Err = task.buildTaskErrFromDetail()
}
task.Result = taskResult
task.TerminatedAt = time.Now()
task.jobList = nil // 如果不释放,任务被管理的话,会导致内存不能释放
task.locker.Unlock()
globals.SugarLogger.Debugf("ParallelTask.Run %s, err:%v", task.Name, task.Err)
close(task.subFinishChan)

View File

@@ -44,13 +44,13 @@ func (task *SeqTask) Run() {
})
task.finishedOneJob(1, err)
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)
if !task.IsContinueWhenError {
break
}
task.locker.Lock()
task.detailErrMsgList = append(task.detailErrMsgList, err.Error())
task.locker.Unlock()
} else if result != nil {
taskResult = append(taskResult, utils.Interface2Slice(result)...)
}
@@ -68,8 +68,12 @@ func (task *SeqTask) Run() {
}
}
if taskErr != nil {
task.OriginalErr = taskErr
task.Err = NewTaskError(task.Name, taskErr)
} else {
if len(task.detailErrList) > 0 {
task.OriginalErr = task.detailErrList[0]
}
task.Err = task.buildTaskErrFromDetail()
}
task.Result = taskResult

View File

@@ -57,6 +57,8 @@ type ITask interface {
AddChild(task ITask) ITask
GetChildren() TaskList
SetParent(parentTask ITask)
GetOriginalErr() error
GetDetailErrList() []error
json.Marshaler
}
@@ -103,15 +105,19 @@ type BaseTask struct {
FailedJobCount int `json:"failedJobCount"`
Status int `json:"status"`
Result []interface{} `json:"-"`
Children TaskList `json:"children"`
Err error `json:"err"`
NoticeMsg string `json:"noticeMsg"`
detailErrMsgList []string
finishChan chan struct{}
C <-chan struct{} `json:"-"`
params []interface{}
quitChan chan int
Result []interface{} `json:"-"`
Children TaskList `json:"children"`
Err error `json:"err"`
OriginalErr error `json:"-"`
detailErrList []error
finishChan chan struct{}
C <-chan struct{} `json:"-"`
params []interface{}
quitChan chan int
locker sync.RWMutex
parent ITask
@@ -161,7 +167,7 @@ func (t *BaseTask) GetID() string {
func (t *BaseTask) GetResult(duration time.Duration) (retVal []interface{}, err error) {
if t.GetStatus() >= TaskStatusEndBegin {
return t.Result, t.Err
return t.Result, t.OriginalErr
}
if 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:
t.isGetResultCalled = true
timer.Stop()
return t.Result, t.Err
return t.Result, t.OriginalErr
case <-timer.C:
}
return nil, ErrTaskNotFinished
@@ -257,6 +263,30 @@ func (t *BaseTask) SetParent(parentTask ITask) {
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 {
if parentTask != nil {
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))
if t.Err == nil {
content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status])
noticeMsg := t.GetNoticeMsg()
if noticeMsg != "" {
content += ",通知消息:" + noticeMsg
}
} else {
if t.Status == TaskStatusFinished {
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) {
if len(t.detailErrMsgList) > 0 {
return NewTaskError(t.Name, fmt.Errorf("总共:%d, 失败:%d, 详情:\n%s", t.TotalItemCount, t.FailedItemCount, strings.Join(t.detailErrMsgList, "\n")))
if len(t.detailErrList) > 0 {
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
}

View File

@@ -5,6 +5,8 @@ import (
"strings"
"time"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
@@ -49,7 +51,10 @@ const (
WX_NORMAL_STORE_MSG_TEMPLATE_ID = "7ngcTFYiUFw66BMzIYntM1tpy-xZkJwlcCT5pVtXwtw"
WX_CHANGE_APPROVED_TEMPLATE_ID = "gIG2olBZtQbjXmp6doNB_dESu60By5xuXYOGxksLv3Y"
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 (
@@ -305,7 +310,7 @@ func NotifyUserApplyCancel(order *model.GoodsOrder, cancelReason string) (err er
},
}
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 = "当期账单"
}
if shopName == "" {
shopName = "京西菜市"
shopName = globals.StoreName
}
data := 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)
}
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 {
var tmpTime time.Time
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 {
GoodsOrder
EarningPrice int64 `json:"earningPrice"` // 预估结算给门店老板的钱
WaybillStatus int `json:"waybillStatus"`
CourierName string `orm:"size(32)" json:"courierName"`
CourierMobile string `orm:"size(32)" json:"courierMobile"`
CurrentConsigneeMobile string `orm:"-" json:"currentConsigneeMobile"`
CourierVendorName string `json:"courierVendorName"`
Status2 string `json:"status2"`
ActualFee int64 `json:"actualFee"` // 实际要支付给快递公司的费用
DesiredFee int64 `json:"desiredFee"` // 运单总费用
WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"`
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 {
@@ -30,11 +40,6 @@ type OrderSkuExt struct {
}
type GoodsOrderCountInfo struct {
Status int `json:"status"`
Count int `json:"count"`
}
type GoodsOrderCountInfo2 struct {
LockStatus int `json:"lockStatus"`
Status int `json:"status"`
Count int `json:"count"`
@@ -61,3 +66,8 @@ type OrderFinancialExt struct {
Skus []*OrderSkuFinancial `orm:"-" json:"skus"` // 正向订单购买商品列表
Discounts []*OrderDiscountFinancial `orm:"-" json:"discounts"` // 正向订单享受优惠列表
}
type OrderFinancialSkuExt struct {
OrderSkuFinancial
Image string `json:"image"`
}

View File

@@ -14,9 +14,9 @@ const (
VendorIDMTWM = 1
VendorIDELM = 2
VendorIDEBAI = 3
VendorIDJX = 9 // 这是一个假的京西VendorID
VendorIDWSC = 11 // 微盟微商城
VendorIDPurchaseEnd = 11
VendorIDJX = 99 // 这是一个假的京西VendorID
VendorIDDeliveryBegin = 101
VendorIDDada = 101
@@ -112,22 +112,34 @@ var (
OrderStatusDeliverFailed: "投递失败",
OrderStatusFinished: "完成",
OrderStatusCanceled: "取消",
AfsOrderStatusWait4Approve: "待审核",
AfsOrderStatusNew: "已审核",
AfsOrderStatusWait4ReceiveGoods: "退货待确认",
AfsOrderStatusReceivedGoods: "退货已收到",
AfsOrderStatusFinished: "售后成功",
AfsOrderStatusFailed: "售后失败",
}
WaybillStatusName = map[int]string{
WaybillStatusUnknown: "一般事件",
WaybillStatusNew: "新运单",
WaybillStatusAcceptCanceled: "取消接受",
WaybillStatusAccepted: "已接单",
WaybillStatusCourierArrived: "已到店",
WaybillStatusDelivering: "配送中",
WaybillStatusDelivered: "送达",
WaybillStatusCanceled: "取",
WaybillStatusFailed: "失败",
WaybillStatusNew: "新运单",
WaybillStatusPending: "压单",
WaybillStatusAcceptCanceled: "取消接受",
WaybillStatusAccepted: "已接单",
WaybillStatusCourierArrived: "已到店",
WaybillStatusApplyFailedGetGoods: "取货失败待审核",
WaybillStatusAgreeFailedGetGoods: "取货失败",
WaybillStatusDelivering: "配送中",
WaybillStatusDeliverFailed: "投递失败",
WaybillStatusDelivered: "送达",
WaybillStatusCanceled: "取消",
WaybillStatusFailed: "失败",
}
OrderTypeName = map[int]string{
OrderTypeOrder: "订单",
OrderTypeWaybill: "运单",
OrderTypeOrder: "订单",
OrderTypeWaybill: "运单",
OrderTypeAfsOrder: "售后单",
}
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 (
OrderTypeOrder = 1
OrderTypeWaybill = 2
OrderTypeOrder = 1
OrderTypeWaybill = 2
OrderTypeAfsOrder = 3
)
// https://blog.csdn.net/a13570320979/article/details/51366355
@@ -212,6 +243,14 @@ const (
OrderStatusEndBegin = 100 // 以下的状态就是结束状态
OrderStatusFinished = 110 // 订单已完成
OrderStatusCanceled = 115 // 订单已取消
OrderStatusEndEnd = 120
AfsOrderStatusWait4Approve = 155 // 待审核售后单
AfsOrderStatusNew = 160 // 已审核或不需要审核售后单
AfsOrderStatusWait4ReceiveGoods = 165 // 退款退货的,需要商家确认收到货
AfsOrderStatusReceivedGoods = 167 // 已确认收到货
AfsOrderStatusFinished = 180 // 售后单成功完成
AfsOrderStatusFailed = 190 // 售后单失败
)
const (
@@ -224,6 +263,7 @@ const (
WaybillStatusUnknown = 0
WaybillStatusNew = 5
WaybillStatusPending = 7
WaybillStatusAcceptCanceled = 8
WaybillStatusAccepted = 10
WaybillStatusCourierArrived = 15 // 此状态是可选的,明确写出来是因为还是较重要的状态,但业务逻辑不应依赖此状态
@@ -238,7 +278,6 @@ const (
WaybillStatusDelivered = 105 // todo 这个应该改为110与订单对应
WaybillStatusCanceled = 115
WaybillStatusFailed = 120 // 这个状态存在的意义是区分于WaybillStatusCanceled比如达达平台在这种状态下再次创建运单的方式不一样
WaybillStatusNeverSend = 125 // 这个状态指的是平台方不愿意配送门店自己想办法。与WaybillStatusAcceptCanceled不一样WaybillStatusAcceptCanceled可能之后还会尝试配送
)
const (
@@ -261,6 +300,13 @@ const (
const (
OrderDeliveryFlagMaskScheduleDisabled = 1 // 禁止三方配送调度
OrderDeliveryFlagMaskPurcahseDisabled = 2 // 购物平台已不配送(一般为门店配送类型本身为自配送,或已经转自配送)
OrderDeliveryFlagMaskDada = 16 // 创建达达运单中
OrderDeliveryFlagMaskMtps = 32 // 创建美团配送运单中
)
const (
WaybillDeliveryFlagMaskActiveCancel = 1 // 主动取消
)
const (
@@ -275,7 +321,42 @@ const (
OrderFlagRefuseFailedGetGoods = 24
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 {
@@ -306,9 +387,22 @@ func IsOrderMainStatus(status int) bool {
}
func IsOrderFinalStatus(status int) bool {
return status >= OrderStatusEndBegin
return status >= OrderStatusEndBegin && status <= OrderStatusEndEnd
}
func IsOrderImportantStatus(status int) bool {
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) {
if conditions != nil {
if conditions != nil && refutil.IsFieldExist(item, model.FieldDeletedAt) {
conditions = utils.MergeMaps(conditions, map[string]interface{}{
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) {
if conditions != nil {
if conditions != nil && refutil.IsFieldExist(item, model.FieldDeletedAt) {
conditions = utils.MergeMaps(conditions, map[string]interface{}{
model.FieldDeletedAt: utils.DefaultTimeValue,
})
@@ -115,6 +115,7 @@ func AddStoreCategoryMap(db *DaoDB, storeID, categoryID int, vendorID int, vendo
StoreID: storeID,
CategoryID: categoryID,
MtwmSyncStatus: model.SyncFlagNewMask,
EbaiSyncStatus: model.SyncFlagNewMask,
WscSyncStatus: model.SyncFlagNewMask,
}
storeCat.DeletedAt = utils.DefaultTimeValue

View File

@@ -29,12 +29,12 @@ func SetOrderPrintFlag(db *DaoDB, userName string, vendorOrderID string, vendorI
if isPrinted {
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, model.OrderFlagMaskPrinted)
} else {
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, ^int8(model.OrderFlagMaskPrinted))
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, ^model.OrderFlagMaskPrinted)
}
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, `
UPDATE goods_order
SET flag = flag | ?
@@ -43,7 +43,7 @@ func SetOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int
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, `
UPDATE goods_order
SET flag = flag & ?
@@ -51,3 +51,34 @@ func ClearOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID i
`, flag, vendorOrderID, vendorID)
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{}) {
// copyBetweenNoramAndNullObj(src, dst, 2)
// }
func IsVendorThingIDEmpty(vendorThingID string) bool {
return vendorThingID == "" || vendorThingID == "0"
}

View File

@@ -1,8 +1,11 @@
package dao
import (
"fmt"
"git.rosy.net.cn/baseapi/utils"
"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) {
@@ -52,6 +55,47 @@ func GetSkuNameByHashCode(db *DaoDB, hashCode string) (skuName *model.SkuName, e
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) {
sql := `
SELECT *
@@ -70,3 +114,76 @@ func GetSkuNames(db *DaoDB, nameIDs []int) (skuNameList []*model.SkuName, err er
}
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 {
model.Store
VendorStoreID string `orm:"column(vendor_store_id)` // 这个在GetMissingDadaStores返回中指的是到家的vendorStoreID
DadaStoreID string `orm:"column(dada_store_id)`
VendorStoreID string `orm:"column(vendor_store_id)"` // 这个在GetMissingDadaStores返回中指的是到家的vendorStoreID
DadaStoreID string `orm:"column(dada_store_id)"`
DistrictName string
CityName string
}
@@ -136,8 +136,8 @@ func GetMissingDadaStores(db *DaoDB, storeID int, isMustHaveJdStore bool) (store
t3.vendor_store_id dada_store_id
FROM store t1
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
JOIN place district ON district.code = t1.district_code
LEFT JOIN place city ON city.code = t1.city_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 = ?
WHERE t1.deleted_at = ?
`
@@ -181,6 +181,33 @@ func GetStoreCourierList(db *DaoDB, storeID, status int) (courierStoreList []*mo
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则把京西门店状态改为之前那个统一的平台门店状态
func FormalizeStoreStatus(db *DaoDB, storeID, storeStatus int) (err error) {

View File

@@ -2,6 +2,7 @@ package dao
import (
"fmt"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
@@ -13,13 +14,16 @@ var (
model.VendorIDWSC: "img_weimob",
model.VendorIDEBAI: "img_ebai",
}
descImgFieldMap = map[int]string{
model.VendorIDEBAI: "desc_img_ebai",
}
)
type SkuStoreCatInfo struct {
model.SkuCategory
MapID int `orm:"column(map_id)"` // 这个主要用于判断是否有store_sku_category_map
VendorCatID string `orm:"column(vendor_cat_id)"`
CatSyncStatus int8
MapID int `orm:"column(map_id)"` // 这个主要用于判断是否有store_sku_category_map
VendorCatID string `orm:"column(vendor_cat_id)"`
StoreCatSyncStatus int8
ParentCatName string
ParentMapID int `orm:"column(parent_map_id)"` // 这个主要用于判断是否有父store_sku_category_map
@@ -27,47 +31,63 @@ type SkuStoreCatInfo struct {
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 {
BindID int `orm:"column(bind_id)"`
Price int64
UnitPrice int64
StoreSkuStatus int
SkuSyncStatus int8
// 平台无关的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
UnitPrice int64
// 平台相关的store sku信息
StoreSkuStatus int
StoreSkuSyncStatus int8
VendorSkuID string `orm:"column(vendor_sku_id)"`
model.Sku
VendorSkuID string `orm:"column(vendor_sku_id)"`
// sku_name
Prefix string
NameID int `orm:"column(name_id)"`
VendorNameID string `orm:"column(vendor_name_id)"`
Name string
Unit string
Img string
Upc string
Seq int
VendorVendorCatID int64 `orm:"column(vendor_vendor_cat_id)"`
// 平台相关的图片信息
Img string
DescImg string
CatSyncStatus int8
VendorCatID string `orm:"column(vendor_cat_id)"`
VendorVendorCatID int64 `orm:"column(vendor_vendor_cat_id)"` // 平台商品分类(叶子结点)
VendorVendorCatID2 int64 `orm:"column(vendor_vendor_cat_id2)"` // 平台商品分类上一级
VendorVendorCatID3 int64 `orm:"column(vendor_vendor_cat_id3)"` // 平台商品分类再上一级
SkuCatSyncStatus int8
SkuVendorCatID string `orm:"column(sku_vendor_cat_id)"`
// sku的商家分类信息
SkuStoreCatSyncStatus int8
SkuVendorCatID string `orm:"column(sku_vendor_cat_id)"`
// sku_name的商家分类信息
StoreCatSyncStatus int8
VendorCatID string `orm:"column(vendor_cat_id)"`
CatPricePercentage int
}
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) {
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
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 = ?
@@ -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 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 = ?
WHERE t1.deleted_at = ? AND t1.store_id = ?
WHERE t1.deleted_at = ? AND t1.store_id = ? AND t1.status = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
@@ -98,6 +118,7 @@ func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int
utils.DefaultTimeValue,
utils.DefaultTimeValue,
storeID,
model.SkuStatusNormal,
}
if len(skuIDs) > 0 {
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])
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
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 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 = ?
`, fieldPrefix, fieldPrefix, fieldPrefix)
`, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix)
if err = GetRows(db, &cats, sql, utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, level, utils.DefaultTimeValue); err != nil {
return nil, err
}
return cats, err
}
// 单门店模式厂商适
func GetStoreSkus(db *DaoDB, vendorID, storeID int, skuIDs []int) (skus []*StoreSkuSyncInfo, err error) {
// 以store_sku_bind为基础来做同步正常情况下使
// 单多门店模式厂商通用
func GetStoreSkus2(db *DaoDB, vendorID, storeID int, skuIDs []int, isDirty bool) (skus []*StoreSkuSyncInfo, err error) {
isSingleStorePF := model.MultiStoresVendorMap[vendorID] != 1
tableName := "t1"
if model.MultiStoresVendorMap[vendorID] == 1 { // 多店模式平台
if !isSingleStorePF {
tableName = "t2"
}
vendorSkuNameField := "0"
if vendorID == model.VendorIDWSC {
vendorSkuNameField = "t1.wsc_id2"
}
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
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.*,
t3.id name_id, t3.prefix, t3.name, t3.unit, t3.%s img, t3.upc,
t4.%s_category_id vendor_vendor_cat_id,
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
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_price_percentage cat_price_percentage
`
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
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 = ?
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
`
LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ?`
sqlParams := []interface{}{
utils.DefaultTimeValue,
model.SkuStatusNormal,
utils.DefaultTimeValue,
model.SkuStatusNormal,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
utils.DefaultTimeValue,
storeID,
}
if vendorID == model.VendorIDEBAI {
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 {
sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql = fmt.Sprintf(sql, tableName, fieldPrefix, fieldPrefix, vendorSkuNameField, GetImgFieldName(vendorID), fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix, fieldPrefix)
if !isSingleStorePF {
sql += " AND t2.%s_id <> 0"
fmtParams = append(fmtParams, fieldPrefix)
}
sql = fmt.Sprintf(sql, fmtParams...)
sql += " ORDER BY t1.price"
// globals.SugarLogger.Debug(sql)
if err = GetRows(db, &skus, sql, sqlParams...); err != nil {
return nil, err
}
index := 1
for _, sku := range skus {
sku.Seq = index
index++
}
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) {
globals.SugarLogger.Debugf("GetFullStoreSkus, storeID:%d, vendorID:%d", storeID, vendorID)
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,
t2.*,
t3.id name_id, t3.prefix, t3.name, t3.unit, t3.%s img,
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.id sku_id,
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_sync_status cat_sync_status, t4.%s_id vendor_cat_id,
t5sku.%s_sync_status sku_cat_sync_status, t5sku.%s_id sku_vendor_cat_id
t4.%s_sync_status store_cat_sync_status, t4.%s_id vendor_cat_id,
t5sku.%s_sync_status sku_store_cat_sync_status, t5sku.%s_id sku_vendor_cat_id
FROM sku t2
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_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ?
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{}{
storeID,
@@ -210,7 +270,8 @@ func GetFullStoreSkus(db *DaoDB, vendorID, storeID int) (skus []*StoreSkuSyncInf
model.SkuStatusNormal,
}
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(utils.Format4Output(sqlParams, false))
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
}
func SetStoreSkuSyncStatus(db *DaoDB, vendorID, storeID int, skuIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("SetStoreSkuSyncStatus, storeID:%d, vendorID:%d", storeID, vendorID)
// 这个函数之前是要设置没有删除或同步标志不为0的会导致将同步标志不为0且删除了的把标志去掉现在改为只设置没有删除的
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])
sql := fmt.Sprintf(`
UPDATE store_sku_bind
SET %s_sync_status = IF(deleted_at = ?, %s_sync_status | ?, 0)
`, fieldPrefix, fieldPrefix)
sql := `
UPDATE store_sku_bind t1
SET t1.%s_sync_status = t1.%s_sync_status | ?
`
fmtParams := []interface{}{
fieldPrefix,
fieldPrefix,
}
sqlParams := []interface{}{
utils.DefaultTimeValue,
syncStatus,
}
if (syncStatus & model.SyncFlagNewMask) != 0 {
sql += fmt.Sprintf(`,
%s_id = 0
`, fieldPrefix)
if isSingleStorePF && (syncStatus&model.SyncFlagNewMask) != 0 {
sql += `,
t1.%s_id = 0
`
fmtParams = append(fmtParams, fieldPrefix)
}
sql += " WHERE 1 = 1"
if storeID > 0 {
sql += " AND store_id = ?"
sqlParams = append(sqlParams, storeID)
sql += " WHERE (t1.deleted_at = ?)"
// 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(skuIDs) > 0 {
sql += " AND sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sql += " AND t1.sku_id IN (" + GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
sql = fmt.Sprintf(sql, fmtParams...)
return ExecuteSQL(db, sql, sqlParams...)
}
func SetStoreCategorySyncStatus(db *DaoDB, vendorID, storeID int, catIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("SetStoreCategorySyncStatus, storeID:%d, vendorID:%d", storeID, vendorID)
func SetStoreCategorySyncStatus(db *DaoDB, vendorID int, storeIDs []int, catIDs []int, syncStatus int) (num int64, err error) {
globals.SugarLogger.Debugf("SetStoreCategorySyncStatus, storeIDs:%v, vendorID:%d", storeIDs, vendorID)
isSingleStorePF := model.MultiStoresVendorMap[vendorID] != 1
fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID])
sql := fmt.Sprintf(`
UPDATE store_sku_category_map
SET %s_sync_status = %s_sync_status | ?
WHERE deleted_at = ? AND store_id = ?
`, fieldPrefix, fieldPrefix)
sql := `
UPDATE store_sku_category_map t1
SET t1.%s_sync_status = IF(t1.deleted_at = ?, t1.%s_sync_status | ?, 0)
`
fmtParams := []interface{}{
fieldPrefix,
fieldPrefix,
}
sqlParams := []interface{}{
syncStatus,
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 {
sql += " AND category_id IN (" + GenQuestionMarks(len(catIDs)) + ")"
sql += " AND t1.category_id IN (" + GenQuestionMarks(len(catIDs)) + ")"
sqlParams = append(sqlParams, catIDs)
}
sql = fmt.Sprintf(sql, fmtParams...)
return ExecuteSQL(db, sql, sqlParams...)
}
@@ -276,3 +363,66 @@ func GetImgFieldName(vendorID int) (fieldName string) {
}
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"
FieldVendorOrderID2 = "VendorOrderID2"
FieldActID = "ActID"
FieldVendorActID = "VendorActID"
)
type ModelIDCUL struct {
@@ -67,16 +70,43 @@ const (
SyncFlagModifiedMask = 1
SyncFlagNewMask = 2
SyncFlagDeletedMask = 4
SyncFlagChangedMask = SyncFlagModifiedMask | SyncFlagNewMask | SyncFlagDeletedMask
SyncFlagSaleMask = 8
SyncFlagPriceMask = 16
SyncFlagSaleMask = 8 // 改了门店商品可售状态必须设置此标志
SyncFlagPriceMask = 16 // 改了门店商品价格必须设置此标志
SyncFlagSpecMask = 32
SyncFlagStoreSkuOnlyMask = SyncFlagSaleMask | SyncFlagPriceMask
SyncFlagStoreSkuModifiedMask = SyncFlagStoreSkuOnlyMask | SyncFlagModifiedMask
SyncFlagChangedMask = SyncFlagSpecMask | SyncFlagNewMask | SyncFlagDeletedMask | SyncFlagStoreSkuModifiedMask
SyncFlagStoreName = 8
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 (
// KeyJdFlag = "jdFlag"
// KeyJdSyncedAt = "jdSyncedAt"

View File

@@ -16,9 +16,12 @@ type GoodsOrder struct {
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"` // 单位为分 售卖
ShopPrice int64 `json:"shopPrice"` // 京西
VendorPrice int64 `json:"vendorPrice"` // 平台
SalePrice int64 `json:"salePrice"` // 售卖价
ActualPayPrice int64 `json:"actualPayPrice"` // 单位为分 顾客实际支付
TotalShopMoney int64 `json:"shopMoney"` // 应结金额-第三方平台结算给京西的金额(包括了所有的补贴,扣除)
PmSubsidyMoney int64 `json:"pmSubsidyMoney"` // 平台活动补贴(订单主体活动补贴+订单单条sku补贴1+
Weight int `json:"weight"` // 单位为克
ConsigneeName string `orm:"size(32)" json:"consigneeName"`
ConsigneeMobile string `orm:"size(32)" json:"consigneeMobile"`
@@ -30,10 +33,10 @@ type GoodsOrder struct {
SkuCount int `json:"skuCount"` // 商品类别数量即有多少种商品注意在某些情况下相同SKU的商品由于售价不同也会当成不同商品在这个值里
GoodsCount int `json:"goodsCount"` // 商品个数
Status int `json:"status"` // 参见OrderStatus*相关的常量定义
VendorStatus string `orm:"size(255)" json:"-"`
VendorStatus string `orm:"size(255)" json:"vendorStatus"`
LockStatus int `json:"lockStatus"`
LockStatusTime time.Time `orm:"type(datetime);null" json:"-"` // last lock status time
OrderSeq int `json:"orderSeq"` // 门店订单序号
LockStatusTime time.Time `orm:"type(datetime);null" json:"lockStatusTime"` // last lock status time
OrderSeq int `json:"orderSeq"` // 门店订单序号
BuyerComment string `orm:"size(255)" json:"buyerComment"`
BusinessType int `json:"businessType"`
ExpectedDeliveredTime time.Time `orm:"type(datetime)" json:"expectedDeliveredTime"` // 预期送达时间
@@ -44,19 +47,12 @@ type GoodsOrder struct {
DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的
OrderCreatedAt time.Time `orm:"type(datetime);index" json:"orderCreatedAt"` // 这里记录的是订单生效时间,即用户支付完成(货到付款即为下单时间)
OrderFinishedAt time.Time `orm:"type(datetime)" json:"orderFinishedAt"`
StatusTime time.Time `orm:"type(datetime)" json:"-"` // last status time
StatusTime time.Time `orm:"type(datetime)" json:"statusTime"` // last status time
PickDeadline time.Time `orm:"type(datetime)" json:"pickDeadline"`
ModelTimeInfo `json:"-"`
OriginalData string `orm:"-" json:"-"` // 只是用于传递数据
Skus []*OrderSku `orm:"-" json:"-"`
Flag int8 `json:"flag"` //非运单调整相关的其它状态
SkuPmFee int64 `json:"-"` //门店商品活动总支出
OrderPmFee int64 `json:"-"` //门店订单活动支出
SkuPmSubsidy int64 `json:"-"` //平台商品活动总补贴
OrderPmSubsidy int64 `json:"-"` //平台订单活动补贴
BoxFee int64 `json:"-"` //餐盒费
PlatformFeeRate int16 `json:"-"` //平台费
BillStoreFreightFee int64 `json:"-"` //需要回调,门店所承担的运费
Flag int `json:"flag"` //非运单调整相关的其它状态
}
func (o *GoodsOrder) TableUnique() [][]string {
@@ -88,18 +84,18 @@ type OrderSku struct {
StoreSubID int `orm:"column(store_sub_id)" json:"storeSubID"`
StoreSubName string `orm:"size(64)" json:"storeSubName"`
Count int `json:"count"`
VendorSkuID string `orm:"column(vendor_sku_id);size(48)" json:"-"`
VendorSkuID string `orm:"column(vendor_sku_id);size(48)" json:"vendorSkuID"`
SkuID int `orm:"column(sku_id)" json:"skuID"` // 外部系统里记录的 jxskuid
JxSkuID int `orm:"column(jx_sku_id)" json:"jxSkuID"` // 根据VendorSkuID在本地系统里查询出来的 jxskuid
SkuName string `orm:"size(255)" json:"skuName"`
ShopPrice int64 `json:"shopPrice"` // 门店标
ShopPrice int64 `json:"shopPrice"` // 京西
VendorPrice int64 `json:"vendorPrice"` // 平台价
SalePrice int64 `json:"salePrice"` // 售卖价
Weight int `json:"-"` // 单位为克
SkuType int `json:"-"` // 当前如果为gift就为1否则缺省为0
PromotionType int `json:"-"` // todo 当前是用于记录京东的PromotionType(生成jxorder用),没有做转换
EarningPrice int64 `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
Weight int `json:"weight"` // 单位为克
SkuType int `json:"skuType"` // 当前如果为gift就为1否则缺省为0
PromotionType int `json:"promotionType"` // todo 当前是用于记录京东的PromotionType(生成jxorder用),没有做转换
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"` // 实际要支付给快递公司的费用
DesiredFee int64 `json:"desiredFee"` // 运单总费用
DuplicatedCount int `json:"-"` // 重复新订单消息数这个一般不是由于消息重发造成的消息重发由OrderStatus过滤一般是业务逻辑造成的
DeliveryFlag int8 `json:"deliveryFlag"`
WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"`
WaybillFinishedAt time.Time `orm:"type(datetime)" json:"waybillFinishedAt"`
StatusTime time.Time `orm:"type(datetime)" json:"-"` // last status time
@@ -240,3 +237,24 @@ type OrderComment struct {
CommentUpdatedAt time.Time
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

@@ -70,25 +70,40 @@ func (o *OrderDiscountFinancial) TableUnique() [][]string {
type AfsOrder struct {
ModelIDCUL
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
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
AfsCreateAt time.Time `orm:"type(datetime);index" json:"afsCreateAt"` // 单生成时间
VendorStoreID string `orm:"column(vendor_store_id);size(48)" json:"vendorStoreID"` // 外部系统里记录的storeid
StoreID int `orm:"column(store_id)" json:"storeID"` // 接口返回的京西门店ID
JxStoreID int `orm:"column(jx_store_id)" json:"jxStoreID"` // 根据VendorStoreID在本地系统里查询出来的 jxstoreid
SkuUserMoney int64 `json:"skuUserMoney"` // 用户支付菜品金额
FreightUserMoney int64 `json:"freightUserMoney"` // 用户支付运费金额
AfsFreightMoney int64 `json:"afsFreightMoney"` // 退货取件费
BoxMoney int64 `json:"boxMoney"` // 应退包装费金额
TongchengFreightMoney int64 `json:"tongchengFreightMoney"` // 退货单产生的同城送费用
SkuBoxMoney int64 `json:"skuBoxMoney"` // 应退订单餐盒费
PmSubsidyMoney int64 `json:"pmSubsidyMoney"` // 平台总补贴金额
PmSkuSubsidyMoney int64 `json:"pmSkuSubsidyMoney"` // 平台sku补贴金额
PmRefundMoney int64 `json:"pmRefundMoney"` // 订单取消后可能存在佣金减少,平台退回部分金额的情况
RefundMoney int64 `json:"refundMoney"` // 平台扣款总额 1
RefundMoneyByCal int64 `json:"refundMoneyByCal"` // 平台扣款总额-通过公式计算平台扣除京西的金额M
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
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
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
StoreID int `orm:"column(store_id)" json:"storeID"` // 接口返回的京西门店ID
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"` // 用户支付菜品金额
FreightUserMoney int64 `json:"freightUserMoney"` // 用户支付运费金额
AfsFreightMoney int64 `json:"afsFreightMoney"` // 退货取件费
BoxMoney int64 `json:"boxMoney"` // 应退包装费金额
TongchengFreightMoney int64 `json:"tongchengFreightMoney"` // 退货单产生的同城送费用
SkuBoxMoney int64 `json:"skuBoxMoney"` // 应退订单餐盒费
PmSubsidyMoney int64 `json:"pmSubsidyMoney"` // 平台总补贴金额
PmSkuSubsidyMoney int64 `json:"pmSkuSubsidyMoney"` // 平台sku补贴金额
PmRefundMoney int64 `json:"pmRefundMoney"` // 订单取消后可能存在佣金减少,平台退回部分金额的情况
RefundMoney int64 `json:"refundMoney"` // 平台扣款总额 1
RefundMoneyByCal int64 `json:"refundMoneyByCal"` // 平台扣款总额-通过公式计算平台扣除京西的金额M
// JxSkuMoney int64 `json:"jxSkuMoney"` // 京西补贴金额,现阶段是平台扣京西多少钱,京西扣商家多少钱,暂不考虑撤回京西补贴
Skus []*OrderSkuFinancial `orm:"-" json:"skus"`
}
@@ -102,11 +117,10 @@ func (o *AfsOrder) TableUnique() [][]string {
type OrderSkuFinancial struct {
ModelIDCUL
VendorID int `orm:"column(vendor_id)" json:"vendorID"` // 平台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(order_financial_id);size(48)" json:"afsOrderID"` // 订单结账ID
IsAfsOrder int8 `json:"isAfsOrder"` // 0--正向单, 1--售后单
VendorID int `orm:"column(vendor_id)" json:"vendorID"` // 平台id
VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"` // 关联原始订单ID
AfsOrderID string `orm:"column(afs_order_id);size(48)" json:"afsOrderID"` // 售后单ID
IsAfsOrder int8 `json:"isAfsOrder"` // 0--正向单, 1--售后单
// ConfirmTime time.Time `orm:"type(datetime)" json:"confirmTime"` // 订单生成/完成时间
VendorStoreID string `orm:"column(vendor_store_id);size(48)" json:"vendorStoreID"` // 外部系统里记录的storeid
@@ -141,7 +155,8 @@ type OrderSkuFinancial struct {
func (o *OrderSkuFinancial) TableIndex() [][]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 {
ModelIDCULD
VendorID int `orm:"column(vendor_id)"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
Name string `orm:"size(64)" json:"name"`
Advertising string `orm:"size(255)" json:"advertising"`
Type int `json:"type"`
@@ -75,6 +75,8 @@ type PromotionSku struct {
Price int `json:"price"` // 分,活动价,这个不是单价
LimitSkuCount int `json:"limitSkuCount"`
IsLock int8 `json:"isLock"` // 是否锁定门店商品信息
EarningPrice int `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
}
func (*PromotionSku) TableUnique() [][]string {

View File

@@ -89,6 +89,7 @@ var (
SpecialUnit = "份"
SpecialSpecQuality = 500
SpecialSpecUnit = "g"
SpecialSpecUnit2 = "ml"
)
var (
@@ -131,7 +132,12 @@ type SkuCategory struct {
Type int8 `json:"type"` // 类别类型,即是普通类别还是特殊用于做活动的类别
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"` // 这个是指对应的饿了么商品类别
EbaiCategoryID int64 `orm:"column(ebai_category_id)" json:"ebaiCategoryID"` // 这个是指对应的饿百商品类别
MtwmCategoryID int64 `orm:"column(mtwm_category_id)" json:"mtwmCategoryID"` // 这个是指对应的美团外卖商品类别
@@ -159,8 +165,9 @@ type SkuName struct {
Prefix string `orm:"size(255)" json:"prefix"`
Name string `orm:"size(255);index" json:"name"`
BrandID int `orm:"column(brand_id);default(0)" json:"brandID"` // todo此属性暂时没有使用且有问题应该是不同平台都有一个brandid
CategoryID int `orm:"column(category_id);index" json:"categoryID"` // 标准类别
BrandID int `orm:"column(brand_id);default(0)" json:"brandID"` // todo此属性暂时没有使用且有问题应该是不同平台都有一个brandid
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决定
Unit string `orm:"size(8)" json:"unit"`
@@ -176,6 +183,9 @@ type SkuName struct {
Status int `orm:"default(1)" json:"status"` // skuname状态取值同sku.Status
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"`
JdSyncStatus int8 `orm:"default(2)" json:"jdSyncStatus"`
@@ -206,6 +216,12 @@ type Sku struct {
LinkID int `orm:"column(link_id);null;index" json:"linkID"`
}
type SkuAndName struct {
Sku
Name string
Unit string
}
// func (*Sku) TableUnique() [][]string {
// return [][]string{
// []string{"JdID", "DeletedAt"},

View File

@@ -2,8 +2,9 @@ package model
const (
StoreStatusAll = -9
StoreStatusDisabled = -1
StoreStatusClosed = 0
StoreStatusDisabled = -2
StoreStatusClosed = -1
StoreStatusHaveRest = 0
StoreStatusOpened = 1
)
@@ -32,7 +33,8 @@ const (
var (
StoreStatusName = map[int]string{
StoreStatusDisabled: "禁用",
StoreStatusClosed: "休息",
StoreStatusClosed: "长期休息",
StoreStatusHaveRest: "临时休息",
StoreStatusOpened: "营业中",
}
DeliveryRangeTypeName = map[int]string{
@@ -62,6 +64,7 @@ var (
"NBB": "宁波银行",
"SPDB": "浦发银行",
"GDB": "广发银行",
"BOCOM": "交通银行",
"SPAB": "平安银行",
"BSB": "包商银行",
"CSCB": "长沙银行",
@@ -131,6 +134,8 @@ type Store struct {
Status int `json:"status"`
ChangePriceType int8 `json:"changePriceType"` // 修改价格类型,即是否需要审核
DeliveryType int8 `orm:"-" json:"deliveryType"` // 仅用于传值
PrinterVendorID int `orm:"column(printer_vendor_id);" json:"printerVendorID"`
PrinterSN string `orm:"size(32);column(printer_sn);index" json:"printerSN"`
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"`
IDCardBack string `orm:"size(255);column(id_card_back)" json:"idCardBack"`
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"`
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 {

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 {
ModelIDCULD
@@ -88,6 +111,12 @@ func (*StoreSkuBind) TableUnique() [][]string {
}
}
func (*StoreSkuBind) TableIndex() [][]string {
return [][]string{
[]string{"SkuID", "StoreID", "DeletedAt"},
}
}
type StoreOpRequest struct {
ModelIDCULD // DeletedAt用于表示请求操作结束而并不一定是删除

View File

@@ -18,7 +18,11 @@ const (
ServerMsgPing = "ping"
ServerMsgNewOrder = "newOrder"
ServerMsgFinishedPickup = "finishedPickup"
ServerMsgKeyOrderStatusChanged = "keyOrderStatusChanged" // 重要订单状态变化
ServerMsgNewWait4ApproveAfsOrder = "newWait4ApproveAfsOrder"
ServerMsgKeyAfsOrderStatusChanged = "keyAfsOrderStatusChanged" // 重要售后单状态变化
)
const (
@@ -67,8 +71,8 @@ func routinueFunc() {
registerMsg := msg.MsgData.(*MsgOp)
delete(channelMap[registerMsg.StoreID], registerMsg.Chan2Listen)
close(registerMsg.Chan2Close)
case ServerMsgNewOrder, ServerMsgKeyOrderStatusChanged:
globals.SugarLogger.Debugf("msghub routinueFunc, msg:%s", utils.Format4Output(msg, false))
case ServerMsgNewOrder, ServerMsgFinishedPickup, ServerMsgKeyOrderStatusChanged, ServerMsgNewWait4ApproveAfsOrder, ServerMsgKeyAfsOrderStatusChanged:
globals.SugarLogger.Debugf("msghub routinueFunc, msg:%s", utils.Format4Output(msg, true))
utils.CallFuncAsync(func() {
for chan2Send := range channelMap[msg.StoreID] {
chan2Send <- msg
@@ -138,7 +142,7 @@ func GetMsg(ctx *jxcontext.Context, storeID int, lastOrderTime time.Time, lastOr
case msg2, ok := <-chan2Listen:
timer.Stop()
if ok {
msg.MsgData = msg2.MsgData
msg = msg2
}
case <-timer.C:
unregisterChan(storeID, chan2Listen)
@@ -152,7 +156,7 @@ func GetMsg(ctx *jxcontext.Context, storeID int, lastOrderTime time.Time, lastOr
}
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() {
msgChan <- &ServerMsg{
Type: ServerMsgNewOrder,
@@ -167,13 +171,13 @@ func OnNewOrder(order *model.GoodsOrder) {
})
}
func OnKeyOrderStatusChanged(order *model.GoodsOrder) {
globals.SugarLogger.Debugf("msghub OnKeyOrderStatusChanged, order:%s", utils.Format4Output(order, false))
func OnFinishedPickup(order *model.GoodsOrder) {
globals.SugarLogger.Debugf("msghub OnFinishedPickup, order:%s", utils.Format4Output(order, true))
utils.CallFuncAsync(func() {
msgChan <- &ServerMsg{
Type: ServerMsgKeyOrderStatusChanged,
Type: ServerMsgFinishedPickup,
StoreID: jxutils.GetSaleStoreIDFromOrder(order),
MsgData: order,
MsgData: 1,
// MsgData: []*model.GoodsOrderExt{
// &model.GoodsOrderExt{
// 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) {
jxutils.CallMsgHandler(func() {
retVal = c.onWaybillMsg(msg)
}, msg.OrderID)
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDDada))
return retVal
}
@@ -58,12 +58,16 @@ func (c *DeliveryHandler) onWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaap
order := c.callbackMsg2Waybill(msg)
switch msg.OrderStatus {
case dadaapi.OrderStatusWaitingForAccept:
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.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
case dadaapi.OrderStatusDelivering:
order.Status = model.WaybillStatusDelivering
@@ -99,17 +103,23 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *dadaapi.CallbackMsg) (retVal
return retVal
}
// IDeliveryPlatformHandler
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
func (c *DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) {
db := dao.GetDB()
deliveryFee, addFee, err := delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
if err != nil {
return nil, err
deliveryFeeInfo = &partner.WaybillFeeInfo{}
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
}
deliveryFeeInfo.DeliveryFee = jxutils.StandardPrice2Int(result.Fee)
}
if err = delivery.CallCreateWaybillPolicy(policy, deliveryFee, addFee, deliveryFee, order, model.VendorIDDada); err != nil {
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), // 当前达达的门店号与京西是一样的
OriginID: jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID),
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.CityCode, err = c.getDataCityCodeFromOrder(order, db); err == nil {
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)),
// "origin_mark": model.VendorNames[order.VendorID], // 订单来源标示该字段可以显示在达达app订单详情页面只支持字母最大长度为10
// "origin_mark_no": fmt.Sprintf("%d", order.OrderSeq), // 订单来源编号该字段可以显示在达达app订单详情页面支持字母和数字最大长度为30
@@ -129,11 +139,27 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
"cargo_weight": jxutils.IntWeight2Float(limitOrderWeight(order.Weight)),
"cargo_num": order.GoodsCount,
}
}
}
return billParams, addParams, err
}
if globals.EnableStoreWrite {
// 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单
var waybillList []*model.Waybill
err2 := dao.GetRows(db, &waybillList, `
// 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 {
// 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单
var waybillList []*model.Waybill
err2 := dao.GetRows(db, &waybillList, `
SELECT *
FROM waybill
WHERE waybill_created_at > DATE_ADD(NOW(), interval -2 day)
@@ -141,39 +167,38 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
AND waybill_vendor_id = ?
ORDER BY id DESC
`, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), model.VendorIDDada)
var result *dadaapi.CreateOrderResponse
if err = err2; err == nil && len(waybillList) > 0 && waybillList[0].Status != model.WaybillStatusFailed {
globals.SugarLogger.Debugf("CreateWaybill orderID:%s len(waybillList)=%d use ReaddOrder", order.VendorOrderID, len(waybillList))
result, err = api.DadaAPI.ReaddOrder(billParams, addParams)
} else {
if err != nil {
globals.SugarLogger.Warnf("CreateWaybill orderID:%s error:%v", order.VendorOrderID, err)
}
if false {
result, err = api.DadaAPI.AddOrder(billParams, addParams)
} else {
if result, err = api.DadaAPI.QueryDeliverFee(billParams, addParams); err != nil {
return nil, err
}
dadaFee := jxutils.StandardPrice2Int(result.Fee)
if err = delivery.CallCreateWaybillPolicy(policy, deliveryFee, addFee, dadaFee, order, model.VendorIDDada); err != nil {
return nil, err
}
err = api.DadaAPI.AddOrderAfterQuery(result.DeliveryNo)
}
}
if err == nil && result != nil {
bill = &model.Waybill{
VendorOrderID: order.VendorOrderID,
OrderVendorID: order.VendorID,
WaybillVendorID: model.VendorIDDada,
DesiredFee: deliveryFee,
ActualFee: jxutils.StandardPrice2Int(result.Fee),
}
}
var result *dadaapi.CreateOrderResponse
if err = err2; err == nil && len(waybillList) > 0 && waybillList[0].Status != model.WaybillStatusFailed {
globals.SugarLogger.Debugf("CreateWaybill orderID:%s len(waybillList)=%d use ReaddOrder", order.VendorOrderID, len(waybillList))
result, err = api.DadaAPI.ReaddOrder(billParams, addParams)
} else {
err = fmt.Errorf("测试环境不能真正创建运单")
if err != nil {
globals.SugarLogger.Warnf("CreateWaybill orderID:%s error:%v", order.VendorOrderID, err)
}
if false {
result, err = api.DadaAPI.AddOrder(billParams, addParams)
} else {
if result, err = api.DadaAPI.QueryDeliverFee(billParams, addParams); err != nil {
return nil, err
}
dadaFee := jxutils.StandardPrice2Int(result.Fee)
if err = delivery.CallCreateWaybillPolicy(policy, deliveryFee, addFee, dadaFee, order, model.VendorIDDada); err != nil {
return nil, err
}
err = api.DadaAPI.AddOrderAfterQuery(result.DeliveryNo)
}
}
if err == nil && result != nil {
bill = &model.Waybill{
VendorOrderID: order.VendorOrderID,
OrderVendorID: order.VendorID,
WaybillVendorID: model.VendorIDDada,
DesiredFee: deliveryFee,
ActualFee: jxutils.StandardPrice2Int(result.Fee),
}
}
} else {
err = fmt.Errorf("测试环境不能真正创建运单")
}
}
return bill, err

View File

@@ -17,21 +17,33 @@ import (
const (
warningDistance = 10 // 公里
warningWeight = 50 * 1000 // 克
maxDiffFee2Mtps = 150 // 与美团配送最多差价
maxAddFee = 300 // 最大增加费用,单位为分,超过不发三方配送了
maxDiffFee2Mtps = 500 // 与美团配送最多差价
maxAddFee = 200 // 最大增加费用,单位为分,超过不发三方配送了
)
var (
DefCreateWaybillPolicy = CreateWaybillPolicy(maxDiffFee2Mtps, maxAddFee)
)
func NullCreateWaybillPolicy(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) {
return ""
}
func DefCreateWaybillPolicy(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) {
if refDeliveryFee-deliveryFee > maxDiffFee2Mtps {
errStr = fmt.Sprintf("超参考价太多, 费用:%d参考价:%d, 最高超价:%d", deliveryFee, refDeliveryFee, maxDiffFee2Mtps)
} else if refAddFee > maxAddFee {
errStr = fmt.Sprintf("超基础价太多, 当前加价:%d, 最高加价:%d", refAddFee, maxAddFee)
func CreateWaybillPolicy(maxDiffFee2Mtps2, maxAddFee2 int64) func(refDeliveryFee, refAddFee, deliveryFee int64) (errStr string) {
if maxDiffFee2Mtps2 == 0 {
maxDiffFee2Mtps2 = maxDiffFee2Mtps
}
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) {
@@ -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 {
policy = NullCreateWaybillPolicy
}
if errStr := policy(deliveryFee, deliveryFee, addFee); errStr != "" {
if errStr := policy(refDeliveryFee, refAddFee, deliveryFee); errStr != "" {
waybillVendorName := jxutils.GetVendorName(waybillVendorID)
errStr = fmt.Sprintf("oderID:%s创建运单出错:%s", order.VendorOrderID, 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) {
jxutils.CallMsgHandler(func() {
retVal = c.onWaybillMsg(msg)
}, msg.OrderID)
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDMTPS))
return retVal
}
@@ -73,7 +73,7 @@ func (c *DeliveryHandler) OnWaybillExcept(msg *mtpsapi.CallbackOrderExceptionMsg
}
order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
retVal = mtpsapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), "mtps OnWaybillExcept")
}, msg.OrderID)
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDDada))
return retVal
}
@@ -81,9 +81,10 @@ func (c *DeliveryHandler) onWaybillMsg(msg *mtpsapi.CallbackOrderMsg) (retVal *m
order := c.callbackMsg2Waybill(msg)
switch msg.Status {
case mtpsapi.OrderStatusWaitingForSchedule:
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order)
order.Status = model.WaybillStatusNew
case mtpsapi.OrderStatusAccepted:
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order)
order.DesiredFee, _ = delivery.CalculateBillDeliveryFee(order) // 美团外卖可能会丢失新运单事件,这里补一下
order.Status = model.WaybillStatusAccepted
case mtpsapi.OrderStatusPickedUp:
order.Status = model.WaybillStatusDelivering
@@ -113,6 +114,18 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *mtpsapi.CallbackOrderMsg) (re
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
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
db := dao.GetDB()
@@ -167,7 +180,6 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
if globals.EnableStoreWrite {
result, err2 := api.MtpsAPI.CreateOrderByShop(billParams, addParams)
if err = err2; err == nil {
globals.SugarLogger.Debugf("CreateWaybill failed, orderID:%s, billParams:%v, addParams:%v, error:%v", order.VendorOrderID, billParams, addParams, err)
bill = &model.Waybill{
VendorOrderID: order.VendorOrderID,
OrderVendorID: order.VendorID,
@@ -176,6 +188,8 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
WaybillVendorID: model.VendorIDMTPS,
DesiredFee: deliveryFee,
}
} else {
globals.SugarLogger.Debugf("CreateWaybill failed, orderID:%s, billParams:%v, addParams:%v, error:%v", order.VendorOrderID, billParams, addParams, err)
}
} else {
err = fmt.Errorf("测试环境不能真正创建运单")

View File

@@ -33,6 +33,12 @@ const (
PrinterStatusOnlineAbnormal = 3
)
const (
AfsApproveTypeRefund = 1 // 退款
AfsApproveTypeReturnGoods = 2 // 退货
AfsApproveTypeRefused = 3 // 驳回
)
const (
PrintResultSuccess = 0
PrintResultNoPrinter = 1
@@ -51,10 +57,11 @@ type PrinterStatus struct {
}
const (
TimerTypeNoOverride = 0 // GetStatusActionConfig 返回表示不修改缺省配置
TimerTypeByPass = 1
TimerTypeBaseNow = 2
TimerTypeBaseStatusTime = 3
TimerTypeNoOverride = 0 // GetStatusActionConfig 返回表示不修改缺省配置
TimerTypeByPass = 1
TimerTypeBaseNow = 2
TimerTypeBaseStatusTime = 3
TimerTypeBaseOrderCreatedAt = 4
)
type StatusActionParams struct {
@@ -63,15 +70,37 @@ type StatusActionParams struct {
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 {
case TimerTypeBaseNow:
timeout = s.Timeout
case TimerTypeBaseStatusTime:
timeout = statusTime.Sub(time.Now()) + s.Timeout
case TimerTypeBaseOrderCreatedAt:
timeout = orderCreatedAt.Sub(time.Now()) + s.Timeout
default:
timeout = 0
}
if timeout < 0 {
timeout = 0
}
return timeout
}
@@ -100,8 +129,8 @@ var (
type IOrderManager interface {
SaveOrder(order *model.GoodsOrder, isAdjust bool, db *dao.DaoDB) (isDuplicated bool, err error)
OnOrderNew(order *model.GoodsOrder, msgVendorStatus string) (err error)
OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus string) (err error)
OnOrderNew(order *model.GoodsOrder, orderStatus *model.OrderStatus) (err error)
OnOrderAdjust(order *model.GoodsOrder, orderStatus *model.OrderStatus) (err error)
OnOrderStatusChanged(orderStatus *model.OrderStatus) (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)
LoadOrderFinancial2(vendorOrderID2 string, vendorID int) (order *model.OrderFinancial, err error)
UpdateWaybillVendorID(bill *model.Waybill, revertStatus bool) (err error)
UpdateOrderStatusAndFlag(order *model.GoodsOrder) (err error)
UpdateOrderStatusAndDeliveryFlag(order *model.GoodsOrder) (err error)
UpdateOrderFields(order *model.GoodsOrder, fieldList []string) (err error)
LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error)
OnOrderComments(orderCommentList []*model.OrderComment) (err error)
SaveOrderFinancialInfo(order *model.OrderFinancial, operation string) (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 {
@@ -132,6 +172,7 @@ type IStoreManager interface {
// 所有非以SyncRefresh开头的函数不用自己清理sync_status标记VendorSync统一处理
type IPurchasePlatformHandler interface {
IPurchasePlatformPromotionHandler
GetVendorID() int
GetStatusFromVendorStatus(vendorStatus string) int
@@ -167,6 +208,16 @@ type IPurchasePlatformHandler interface {
// order.Skus要包含原始订单中的Sku信息removedSkuList中是要移除的Sku信息
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
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)
GetStoreStatus(ctx *jxcontext.Context, vendorStoreID string) (storeStatus int, 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,
@@ -219,6 +274,7 @@ type IDeliveryPlatformHandler interface {
CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error)
GetVendorID() int
GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *WaybillFeeInfo, err error)
}
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)
}
orderFmt := `
<CB>京西菜市</CB><BR><BR>
<CB>%s</CB><BR><BR>
<C>手机买菜上京西</C><BR>
<C>极速到家送惊喜</C><BR>
--------------------------------<BR>
@@ -59,6 +59,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
品名 数量 单价 小计<BR>
--------------------------------<BR>`
orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime),
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>%s:%s</BOLD></L><BR></C><BR>
<BR>
更多信息请关注官方微信: 京西菜市<BR>
更多信息请关注官方微信: %s<BR>
<BR>
<BR><BR>
--------------------------------<BR>
@@ -92,7 +93,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
<BR><BR>
`
// <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...)
}

View File

@@ -34,7 +34,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
}
orderFmt := `
<big> 京西菜市**
<big> %s**
手机买菜上京西*
极速到家送惊喜*
------------------------------*
@@ -58,6 +58,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
--------------------------------*
`
orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID,
@@ -82,16 +83,80 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
<S020>商品质量问题请联系:*
<S020>%s:%s*
*
更多信息请关注官方微信: 京西菜市*
更多信息请关注官方微信: %s*
--------------------------------
--------------------------------
*<BEEP13500,3,2,1>*
`
// <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)...)
}
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 {
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) {
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)
}
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) {
globals.SugarLogger.Debugf("xiaowm RegisterPrinter printerNumber:%s", printerNumber)
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)
}
orderFmt := `
<FS2><center>京西菜市</center></FS2>\n\n
<FS2><center>%s</center></FS2>\n\n
<center>手机买菜上京西</center>
<center>极速到家送惊喜</center>\n
--------------------------------
@@ -57,6 +57,7 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
品名 数量 单价 小计\n
--------------------------------\n`
orderParams := []interface{}{
globals.StoreName,
utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID,
@@ -81,12 +82,12 @@ func (c *PrinterHandler) getOrderContent(order *model.GoodsOrder, storeTel strin
--------------------------------\n
<center><FH2>商品质量问题请联系:</FH2></center>
<center><FH2>%s:%s</FH2></center>\n
更多信息请关注官方微信: 京西菜市\n
更多信息请关注官方微信: %s\n
--------------------------------\n
--------------------------------\n
`
// <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)
}

View File

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

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))
}
if msg.Cmd == ebaiapi.CmdOrderPartRefund || msg.Cmd == ebaiapi.CmdOrderUserCancel || msg.Cmd == ebaiapi.CmdOrderDeliveryStatus {
utils.CallFuncAsync(func() {
OnFinancialMsg(msg)
})
if /*msg.Cmd == ebaiapi.CmdOrderPartRefund || msg.Cmd == ebaiapi.CmdOrderUserCancel || */ msg.Cmd == ebaiapi.CmdOrderDeliveryStatus {
response = CurPurchaseHandler.OnFinancialMsg(msg)
} else if msg.Cmd == ebaiapi.CmdShopMsgPush {
response = CurPurchaseHandler.onShopMsgPush(msg)
}
@@ -31,7 +29,11 @@ func OnCallbackMsg(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse
}
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 {
return tryOrderID
}

View File

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

View File

@@ -9,25 +9,33 @@ import (
"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 utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["status"])) == ebaiapi.OrderPartRefundSuccess {
if int(utils.MustInterface2Int64(msg.Body["status"])) == ebaiapi.OrderPartRefundSuccess {
// 获取到部分退款订单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 {
afsOrder := CurPurchaseHandler.AfsOrderDetail2Financial(orderData)
err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(afsOrder)
}
}
} else if msg.Cmd == ebaiapi.CmdOrderUserCancel { // 全额退款处理
messageType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"]))
if utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["cancel_type"])) == ebaiapi.OrderUserCancelTypeAfterSale &&
messageType := int(utils.MustInterface2Int64(msg.Body["type"]))
if int(utils.MustInterface2Int64(msg.Body["cancel_type"])) == ebaiapi.OrderUserCancelTypeAfterSale &&
(messageType == ebaiapi.OrderUserCancelCSAgreed || messageType == ebaiapi.OrderUserCancelMerchantAgreed) {
globals.SugarLogger.Debug(utils.Interface2String(msg.Body["order_id"])) // 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号但是没有查询全额退款的接口只有部分退款才可以查询
afsOrderID := utils.Interface2String(msg.Body["order_id"])
afsOrderID := GetOrderIDFromMsg(msg)
// 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号但是没有查询全额退款的接口只有部分退款才可以查询
orderFinancial, err := partner.CurOrderManager.LoadOrderFinancial(afsOrderID, model.VendorIDEBAI)
if err == nil {
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) {
afsOrder = &model.AfsOrder{
VendorID: model.VendorIDEBAI,
AfsOrderID: utils.Interface2String(msg.Body["order_id"]),
VendorOrderID: utils.Interface2String(msg.Body["order_id"]),
AfsCreateAt: utils.Timestamp2Time(msg.Timestamp),
AfsOrderID: GetOrderIDFromMsg(msg),
VendorOrderID: GetOrderIDFromMsg(msg),
AfsCreatedAt: utils.Timestamp2Time(msg.Timestamp),
// BoxMoney: orderFinancial.BoxMoney, // 饿百的餐盒费已经拆分到单条Sku里面退款时直接计算用户支付sku金额就好了
// SkuBoxMoney: orderFinancial.SkuBoxMoney,
FreightUserMoney: orderFinancial.FreightMoney,
@@ -78,10 +86,9 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
}
for _, sku := range orderFinancial.Skus {
orderSkuFinancial := &model.OrderSkuFinancial{
VendorID: sku.VendorID,
VendorOrderID: sku.VendorOrderID,
VendorOrderID2: sku.VendorOrderID2,
AfsOrderID: sku.VendorOrderID,
VendorID: sku.VendorID,
VendorOrderID: sku.VendorOrderID,
AfsOrderID: sku.VendorOrderID,
// ConfirmTime: afsOrder.AfsCreateAt,
VendorStoreID: afsOrder.VendorStoreID,
StoreID: afsOrder.StoreID,
@@ -106,8 +113,8 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interface{}) (afsOrder *model.AfsOrder) {
afsOrder = &model.AfsOrder{
VendorID: model.VendorIDEBAI,
AfsOrderID: utils.Interface2String(orderData["order_id"]),
VendorOrderID: utils.Interface2String(orderData["order_id"]),
AfsOrderID: GetOrderIDFromMap(orderData),
VendorOrderID: GetOrderIDFromMap(orderData),
}
order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID)
if err == nil {
@@ -125,7 +132,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interfac
afsOrder.PmRefundMoney = orderFinancial.PmMoney - utils.MustInterface2Int64(orderData["commission"])
} 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
}
if orderData["refund_detail"] != nil {
@@ -149,7 +156,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interfac
afsOrder.PmSubsidyMoney += orderSkuFinancial.PmSubsidyMoney
}
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 {
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{}) {
// order := result1["order"].(map[string]interface{})
// 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 {
// return p.GetTrueEbaiOrder(result)
// }
@@ -181,7 +188,7 @@ func (p *PurchaseHandler) OrderDetail2Financial(result map[string]interface{}) (
VendorID: model.VendorIDEBAI,
}
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.DeliveryConfirmTime = getTimeFromInterface(order1["finished_time"])
orderFinancial.TotalDiscountMoney = utils.MustInterface2Int64(order1["discount_fee"])
@@ -224,9 +231,8 @@ func (p *PurchaseHandler) OrderDetail2Financial(result map[string]interface{}) (
for _, y := range x.([]interface{}) {
product := y.(map[string]interface{})
orderSkuFinancial := &model.OrderSkuFinancial{
VendorID: orderFinancial.VendorID,
VendorOrderID: orderFinancial.VendorOrderID,
VendorOrderID2: orderFinancial.VendorOrderID2,
VendorID: orderFinancial.VendorID,
VendorOrderID: orderFinancial.VendorOrderID,
// OrderFinancialID: orderFinancial.VendorOrderID,
// ConfirmTime: getTimeFromInterface(order1["create_time"]),
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["cancel_type"] = json.Number("2")
res := OnFinancialMsg(msg)
res := CurPurchaseHandler.onAfsOrderMsg(msg)
fmt.Println(res)
}

View File

@@ -4,6 +4,8 @@ import (
"math"
"time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
@@ -19,7 +21,8 @@ import (
const (
// acceptOrderDelay = 180 * time.Second
pickupOrderDelay = 260 * time.Second
// pickupOrderDelay = 260 * time.Second
pickupOrderDelay = 1 * time.Second
callDeliveryDelay = 10 * time.Minute
callDeliveryDelayGap = 30
@@ -34,6 +37,7 @@ const (
var (
VendorStatus2StatusMap = map[string]int{
ebaiapi.CmdOrderCreate: model.OrderStatusNew,
ebaiapi.OrderStatusNew: model.OrderStatusNew,
fakeAcceptOrder: model.OrderStatusAccepted,
ebaiapi.OrderStatusAccepted: model.OrderStatusFinishedPickup,
@@ -46,6 +50,11 @@ var (
fakeUserApplyCancel: model.OrderStatusApplyCancel,
fakeUserUndoApplyCancel: model.OrderStatusUndoApplyCancel,
}
skuActTypeMap = map[string]int{
ebaiapi.OrderSkuDiscountTypeZhe: 1,
ebaiapi.OrderSkuDiscountTypeReduce: 1,
}
)
func (p *PurchaseHandler) GetStatusFromVendorStatus(vendorStatus string) int {
@@ -68,6 +77,65 @@ func (p *PurchaseHandler) getOrder(vendorOrderID string) (order *model.GoodsOrde
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) {
result := orderData
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"]),
OriginalData: string(utils.MustMarshal(result)),
ActualPayPrice: utils.MustInterface2Int64(orderMap["user_fee"]),
Skus: []*model.OrderSku{},
TotalShopMoney: utils.MustInterface2Int64(orderMap["shop_fee"]),
}
if utils.IsTimeZero(order.PickDeadline) && !utils.IsTimeZero(order.StatusTime) {
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{})
skuName := product["product_name"].(string)
_, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName)
productAmount := int(utils.MustInterface2Int64(product["product_amount"]))
sku := &model.OrderSku{
VendorOrderID: order.VendorOrderID,
VendorID: model.VendorIDEBAI,
Count: int(utils.MustInterface2Int64(product["product_amount"])),
Count: productAmount,
SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)),
VendorSkuID: utils.Interface2String(product["baidu_product_id"]),
SkuName: skuName,
Weight: jxutils.FormatSkuWeight(specQuality, specUnit), // 订单信息里没有重量,只有名字里尝试找
SalePrice: utils.MustInterface2Int64(product["product_price"]),
// PromotionType: int(utils.MustInterface2Int64(product["promotionType"])),
Weight: int(utils.Interface2Int64WithDefault(product["total_weight"], 0)) / productAmount,
VendorPrice: utils.MustInterface2Int64(product["product_price"]),
}
var baiduRate int64
sku.SalePrice, baiduRate, sku.StoreSubName = getSkuSalePrice(product)
order.PmSubsidyMoney += baiduRate
if sku.Weight == 0 {
sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值
sku.Weight = jxutils.FormatSkuWeight(specQuality, specUnit) // 订单信息里没有重量,只有名字里尝试找
}
// if product["isGift"].(bool) {
// sku.SkuType = 1
// }
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
}
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) {
globals.SugarLogger.Debugf("ebai AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, 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) {
if ebaiapi.CmdOrderCreate == msg.Cmd {
retVal = c.onOrderNew(msg)
if c.isAfsMsg(msg) {
retVal = c.OnAfsOrderMsg(msg)
} else {
status := c.callbackMsg2Status(msg)
var err error
if status != nil {
err = partner.CurOrderManager.OnOrderStatusChanged(status)
if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 {
return nil
}
if ebaiapi.CmdOrderCreate == msg.Cmd {
retVal = c.onOrderNew(msg, status)
} else {
var err error
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)
}
}
retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil)
}
retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil)
}
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)
order, orderMap, err := c.getOrder(vendorOrderID)
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() {
c.OnOrderDetail(orderMap, partner.CreatedPeration)
})
@@ -279,27 +386,24 @@ func (c *PurchaseHandler) callbackMsg2Status(msg *ebaiapi.CallbackMsg) (orderSta
VendorStatus: msg.Cmd,
}
if msg.Cmd == ebaiapi.CmdOrderUserCancel {
msgType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"]))
cancelType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["cancel_type"]))
orderStatus.Remark = utils.Interface2String(msg.Body["cancel_reason"])
orderStatus.VendorStatus = msg.Cmd + "-" + msgType
if additionReason := utils.Interface2String(msg.Body["addition_reason"]); additionReason != "" {
orderStatus.Remark += ",额外原因:" + additionReason
}
msgType := int(utils.MustInterface2Int64(msg.Body["type"]))
cancelType := int(utils.MustInterface2Int64(msg.Body["cancel_type"]))
orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["cancel_reason"]), utils.Interface2String(msg.Body["addition_reason"]))
orderStatus.VendorStatus = msg.Cmd + "-" + utils.Int2Str(msgType)
if cancelType == ebaiapi.OrderUserCancelTypeBeforeSale {
if msgType == ebaiapi.OrderUserCancelApply {
if msgType == ebaiapi.OrderUserCancelApply ||
msgType == ebaiapi.OrderUserCancelCSIntervene {
orderStatus.VendorStatus = fakeUserApplyCancel
} else if msgType == ebaiapi.OrderUserCancelInvalid {
} else if msgType == ebaiapi.OrderUserCancelInvalid ||
msgType == ebaiapi.OrderUserCancelMerchantRefused ||
msgType == ebaiapi.OrderUserCancelCSRefused {
orderStatus.VendorStatus = fakeUserUndoApplyCancel
}
}
} else if msg.Cmd == ebaiapi.CmdOrderPartRefund {
msgType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"]))
status := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["status"]))
orderStatus.Remark = utils.Interface2String(msg.Body["reason"])
if additionReason := utils.Interface2String(msg.Body["addition_reason"]); additionReason != "" {
orderStatus.Remark += ",额外原因:" + additionReason
}
msgType := int(utils.MustInterface2Int64(msg.Body["type"]))
status := int(utils.MustInterface2Int64(msg.Body["status"]))
orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["reason"]), utils.Interface2String(msg.Body["addition_reason"]))
if msgType == ebaiapi.OrderPartRefuncTypeMerchant && status == ebaiapi.OrderPartRefundSuccess {
orderStatus.VendorStatus = fakeOrderAdjustFinished
}
@@ -315,6 +419,14 @@ func (c *PurchaseHandler) callbackMsg2Status(msg *ebaiapi.CallbackMsg) (orderSta
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) {
if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted {
params = &partner.StatusActionParams{ // PickDeadline没有设置时才有效饿百要求在5分钟内拣货不然订单会被取消
@@ -398,15 +510,21 @@ 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) {
var skuList []*ebaiapi.RefundSku
for _, sku := range removedSkuList {
skuList = append(skuList, &ebaiapi.RefundSku{
CustomeSkuID: utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku)),
Number: utils.Int2Str(sku.Count),
})
// 饿百必须要确认订单后才能调整单
if order.Status < model.OrderStatusFinishedPickup {
err = c.PickupGoods(order, false, ctx.GetUserName())
}
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.OrderPartRefund(order.VendorOrderID, skuList)
if err == nil {
var skuList []*ebaiapi.RefundSku
for _, sku := range removedSkuList {
skuList = append(skuList, &ebaiapi.RefundSku{
CustomeSkuID: utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku)),
Number: utils.Int2Str(sku.Count),
})
}
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.OrderPartRefund(order.VendorOrderID, skuList)
}
}
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["category2"] = ""
params["category3"] = ""
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if globals.EnableEbaiStoreWrite {
intVendorStoreID, err2 := api.EbaiAPI.ShopCreate(params)
if err = err2; err == nil {
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 {
for _, store := range stores {
// globals.SugarLogger.Debug(utils.Format4Output(params, false))
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if globals.EnableEbaiStoreWrite {
shopID := 0
if store.SyncStatus&model.SyncFlagDeletedMask == 0 {
shopID = store.ID
@@ -181,10 +181,10 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
}
if err == nil {
mergeStatus := jxutils.MergeStoreStatus(store.Status, store.EbaiStoreStatus)
if store2.Status != mergeStatus {
if !isStoreStatusSame(store2.Status, mergeStatus) {
if mergeStatus == model.StoreStatusOpened {
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))
} else if mergeStatus == model.StoreStatusDisabled {
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 {
return err
}
// todo 饿百 开店审核通过后不允许修改商户信息
// params := genStoreMapFromStore(store)
// if err = api.EbaiAPI.ShopUpdate(params); err == nil {
// }
params := genStoreMapFromStore(store)
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
}
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) {
globals.SugarLogger.Debugf("ebai RefreshAllStoresID")
const batchSize = 50
@@ -248,7 +257,7 @@ func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask
shopIDs[k] = utils.GetUUID()
}
}
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopIDBatchUpdate(baiduShopIDs, shopIDs)
}
return nil, err
@@ -311,7 +320,7 @@ func EbaiDeliveryRegion2Jx(deliveryRegion interface{}) string {
func JxDeliveryRegion2Ebai(store *model.Store) (deliveryRegion interface{}) {
rangeStr := strings.Trim(store.DeliveryRange, ";")
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)
} else {
rangeStr = ""
@@ -363,19 +372,20 @@ func JxBusinessTime2Ebai(store *model.Store) interface{} {
func genStoreMapFromStore(store *tEbaiStoreInfo) map[string]interface{} {
params := map[string]interface{}{
"phone": store.Tel1,
"business_time": JxBusinessTime2Ebai(&store.Store),
}
// if store.Tel2 != "" {
// params["ivr_phone"] = store.Tel2
// }
// params["phone"] = store.Tel1
if store.VendorStoreID != "" {
params["baidu_shop_id"] = store.VendorStoreID
}
if store.Tel2 != "" {
params["ivr_phone"] = store.Tel2
if store.SyncStatus&(model.SyncFlagNewMask /*|model.SyncFlagStoreName*/) != 0 {
params["name"] = jxutils.ComposeStoreName(store.Name, model.VendorIDEBAI)
}
if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreName) != 0 {
// params["name"] = jxutils.ComposeStoreName(store.Name, model.VendorIDEBAI)
}
if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreAddress) != 0 {
// todo 饿百 开店审核通过后不允许修改商户信息
if store.SyncStatus&(model.SyncFlagNewMask /*|model.SyncFlagStoreAddress*/) != 0 {
params["longitude"] = jxutils.IntCoordinate2Standard(store.Lng)
params["latitude"] = jxutils.IntCoordinate2Standard(store.Lat)
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 {
storeStatus = model.StoreStatusOpened
} else {
storeStatus = model.StoreStatusClosed
storeStatus = model.StoreStatusHaveRest
}
}
if err == nil {
@@ -423,3 +433,11 @@ func (c *PurchaseHandler) onShopMsgPush(msg *ebaiapi.CallbackMsg) (response *eba
}
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"
"fmt"
"strings"
"time"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"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/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"
)
@@ -23,7 +25,6 @@ const (
type tStoreSkuFullInfo struct {
model.StoreSkuBind
SkuID int `orm:"column(sku_id)"`
NameID int `orm:"column(name_id)"`
SpecQuality float32 `json:"specQuality"`
@@ -31,14 +32,15 @@ type tStoreSkuFullInfo struct {
Weight int `json:"weight"` // 重量/质量单位为克当相应的SkuName的SpecUnit为g或kg时必须等于SpecQuality
SkuStatus int
Prefix string `orm:"size(255)" json:"prefix"`
Name string `orm:"size(255);index" json:"name"`
Comment string `orm:"size(255)" json:"comment"`
IsGlobal int8 `orm:"default(1)" json:"isGlobal"` // 是否是全部全国可见如果否的话可见性由SkuPlace决定
Unit string `orm:"size(8)" json:"unit"`
Img string `orm:"size(255)" json:"img"`
PlaceStr string
Upc string
Prefix string `orm:"size(255)" json:"prefix"`
Name string `orm:"size(255);index" json:"name"`
Comment string `orm:"size(255)" json:"comment"`
IsGlobal int8 `orm:"default(1)" json:"isGlobal"` // 是否是全部全国可见如果否的话可见性由SkuPlace决定
Unit string `orm:"size(8)" json:"unit"`
Img string `orm:"size(255)" json:"img"`
PlaceStr string
Upc string
DescImgEbai string
CatName string `orm:"size(255)"`
@@ -54,7 +56,8 @@ type tStoreSkuFullInfo struct {
EbaiCat2ID int64 `orm:"column(ebai_cat2_id)"`
EbaiCat3ID int64 `orm:"column(ebai_cat3_id)"`
PricePercentage int
PricePercentage int
CatPricePercentage int
}
type tStoreCatInfo struct {
@@ -80,23 +83,23 @@ var (
func (p *PurchaseHandler) getDirtyStoreSkus(db *dao.DaoDB, storeID int, skuIDs []int) (storeSkuInfoList []*tStoreSkuFullInfo, err error) {
sql := `
SELECT t8.price_percentage, t1.*, t2.id sku_id, 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,
t4.name cat_name,
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.desc_img_ebai,
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,
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
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_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 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 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 = ?
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{}{
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)) + ")"
sqlParams = append(sqlParams, skuIDs)
}
sql += " ORDER BY t1.price"
sql += " ORDER BY t1.price DESC"
err = dao.GetRows(db, &storeSkuInfoList, sql, sqlParams...)
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) {
catList2Add := make(map[int]int)
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 {
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)
case 2:
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:
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)
case 2:
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
@@ -248,34 +251,65 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeSku := batchItemList[0].(*tStoreSkuFullInfo)
updateFields := []string{model.FieldEbaiSyncStatus}
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if storeSku.SkuID == 0 || storeSku.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 {
if storeSku.EbaiSyncStatus&model.SyncFlagNewMask == 0 && storeSku.EbaiID != 0 {
if storeSku.NameID == 0 || storeSku.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 {
if storeSku.EbaiSyncStatus&model.SyncFlagNewMask == 0 && !jxutils.IsEmptyID(storeSku.EbaiID) {
if globals.EnableEbaiStoreWrite {
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))
// todo 适当处理重复(即已经创建)的情况
if storeSku.EbaiID, err = api.EbaiAPI.SkuCreate(strStoreID, storeSku.SkuID, genSkuParamsFromStoreSkuInfo(storeSku)); err == nil {
updateFields = append(updateFields, model.FieldEbaiID)
} else if storeSku.EbaiID = ebaiapi.GetEbaiSkuIDFromError(err); storeSku.EbaiID > 0 {
// globals.SugarLogger.Debugf("SyncStoreSkus test storeSku.EbaiID:%d, err:%v", storeSku.EbaiID, err)
updateFields = append(updateFields, model.FieldEbaiID)
_, err = api.EbaiAPI.SkuUpdate(strStoreID, storeSku.EbaiID, genSkuParamsFromStoreSkuInfo(storeSku))
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)
} else if storeSku.EbaiID = ebaiapi.GetEbaiSkuIDFromError(err); storeSku.EbaiID > 0 {
// globals.SugarLogger.Debugf("SyncStoreSkus test storeSku.EbaiID:%d, err:%v", storeSku.EbaiID, err)
updateFields = append(updateFields, model.FieldEbaiID)
err = skuUpdate(strStoreID, storeSku)
}
} else {
err = fmt.Errorf("SKUANME%d:%s没有图片同步失败", storeSku.NameID, storeSku.Name)
}
} else {
updateFields = nil
}
} else if storeSku.EbaiSyncStatus&model.SyncFlagModifiedMask != 0 {
if jxutils.IsFakeID(storeSku.EbaiID) {
} else if storeSku.EbaiSyncStatus&model.SyncFlagStoreSkuModifiedMask != 0 {
if jxutils.IsEmptyID(storeSku.EbaiID) {
err = fmt.Errorf("京西数据异常,修改一个没有创建的饿百商品:%d, store:%s", storeSku.SkuID, strStoreID)
} else {
if _, err = api.EbaiAPI.SkuUpdate(strStoreID, storeSku.EbaiID, genSkuParamsFromStoreSkuInfo(storeSku)); err == nil {
// err = api.EbaiAPI.SkuShopCategoryMap(strStoreID, storeSku.EbaiID, utils.Int64ToStr(storeSku.CatEbaiID))
if storeSku.Img != "" {
err = skuUpdate(strStoreID, storeSku)
} else {
err = fmt.Errorf("SKUANME%d:%s没有图片同步失败", storeSku.NameID, storeSku.Name)
}
}
}
}
if err == nil {
storeSku.EbaiSyncStatus = 0
_, err = dao.UpdateEntity(nil, &storeSku.StoreSkuBind, updateFields...)
if len(updateFields) > 0 {
storeSku.EbaiSyncStatus = 0
_, err = dao.UpdateEntity(nil, &storeSku.StoreSkuBind, updateFields...)
}
} else if isErrModifyPrice(err) {
err = partner.NewErrorCode(err.Error(), partner.ErrCodeChangePriceFailed, model.VendorIDEBAI)
}
return nil, err
}, storeSkuInfoList)
@@ -291,9 +325,58 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
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())
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 {
skus = append(skus, page1.List...)
if page1.Pages > 1 {
@@ -303,9 +386,9 @@ func (p *PurchaseHandler) GetAllRemoteSkus(ctx *jxcontext.Context, storeID int,
}
task := tasksch.NewParallelTask("GetAllRemoteSkus", nil, ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
callParams := map[string]interface{}{
"pagesize": MaxPageSize,
"page": batchItemList[0],
callParams := &ebaiapi.SkuListParams{
PageSize: MaxPageSize,
Page: batchItemList[0].(int),
}
pageSku, err2 := api.EbaiAPI.SkuList(utils.Int2Str(storeID), callParams)
if err2 == nil {
@@ -318,7 +401,7 @@ func (p *PurchaseHandler) GetAllRemoteSkus(ctx *jxcontext.Context, storeID int,
result, err2 := task.GetResult(0)
if err = err2; err == nil {
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 {
vendorSkuIDs = make([]string, len(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 {
strList[k] = v.(string)
}
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.SkuDelete(utils.Int2Str(storeID), strings.Join(strList, ","))
}
return nil, err
@@ -369,7 +452,7 @@ func (p *PurchaseHandler) DeleteRemoteCategories(ctx *jxcontext.Context, parentT
}
task := tasksch.NewParallelTask("DeleteRemoteCategories", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
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))
}
return nil, err
@@ -385,11 +468,12 @@ func (p *PurchaseHandler) RefreshStoresAllSkusID(ctx *jxcontext.Context, parentT
///////////
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{}{
"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,
"category_id": storeSku.CatEbaiID,
"predict_cat": 0, // 不使用推荐类目
"cat1_id": getEbaiCat(storeSku.EbaiCat1ID, 1),
"cat2_id": getEbaiCat(storeSku.EbaiCat2ID, 2),
"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 {
params["sale_price"] = price
params["market_price"] = price
@@ -516,18 +603,23 @@ func (p *PurchaseHandler) SyncStoreCategory(ctx *jxcontext.Context, parentTask t
updateFields := []string{model.FieldEbaiSyncStatus}
catInfo := batchItemList[0].(*tStoreCatInfo)
// globals.SugarLogger.Debug(utils.Format4Output(catInfo, false))
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if catInfo.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 { // 删除
if catInfo.EbaiSyncStatus&model.SyncFlagNewMask == 0 && catInfo.EbaiID != 0 {
if catInfo.EbaiSyncStatus&model.SyncFlagDeletedMask != 0 { // 删除
if catInfo.EbaiSyncStatus&model.SyncFlagNewMask == 0 && catInfo.EbaiID != 0 {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryDelete(strStoreID, catInfo.EbaiID)
}
} else if catInfo.EbaiSyncStatus&model.SyncFlagNewMask != 0 { // 新增
ebaiID, err2 := api.EbaiAPI.ShopCategoryCreate(strStoreID, catInfo.ParentEbaiID, formatName(catInfo.Name), jxCatSeq2Ebai(catInfo.Seq))
if err = err2; err == nil {
catInfo.EbaiID = ebaiID
updateFields = append(updateFields, model.FieldEbaiID)
}
} else if catInfo.EbaiSyncStatus&model.SyncFlagModifiedMask != 0 { // 修改
}
} else if catInfo.EbaiSyncStatus&model.SyncFlagNewMask != 0 { // 新增
if globals.EnableEbaiStoreWrite {
catInfo.EbaiID, err = api.EbaiAPI.ShopCategoryCreate(strStoreID, catInfo.ParentEbaiID, formatName(catInfo.Name), jxCatSeq2Ebai(catInfo.Seq))
} else {
catInfo.EbaiID = jxutils.GenFakeID()
}
if err == nil {
updateFields = append(updateFields, model.FieldEbaiID)
}
} else if catInfo.EbaiSyncStatus&model.SyncFlagModifiedMask != 0 { // 修改
if globals.EnableEbaiStoreWrite {
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) {
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 {
@@ -614,3 +706,7 @@ func formatName(name string) string {
func jxCatSeq2Ebai(seq int) int {
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.WaybillStatusDeliveryCancled: model.WaybillStatusCanceled,
ebaiapi.WaybillStatusFinished: model.WaybillStatusDelivered,
ebaiapi.WaybillStatusExceptional: model.WaybillStatusUnknown,
ebaiapi.WaybillStatusExceptional: model.WaybillStatusCanceled,
ebaiapi.WaybillStatusSelfDelivery: model.WaybillStatusUnknown,
ebaiapi.WaybillStatusNotInDelivering: model.WaybillStatusUnknown,
ebaiapi.WaybillStatusDeliveryRejected: model.WaybillStatusNeverSend,
ebaiapi.WaybillStatusDontDeliver: model.WaybillStatusCanceled,
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/jxcontext"
"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"
)
@@ -186,47 +187,23 @@ func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])),
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日饿了么迁移到饿百后这个字段发生了变化
}
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
}
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) {
// todo 这里应该可以直接用msg里的内容而不用再次去查
order, err := c.GetOrder(msg["orderId"].(string))
if err == nil {
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 {
// 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 (
"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"
)
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) {
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
import (
"errors"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
)
func OnOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil {
if retVal = curPurchaseHandler.OnOrderMsg(msg); retVal == nil {
retVal = jdapi.Err2CallbackResponse(errors.New("Internal Error"), "")
}
retVal = curPurchaseHandler.OnOrderMsg(msg)
}
return retVal
}
func OnWaybillMsg(msg *jdapi.CallbackDeliveryStatusMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil {
if retVal = curPurchaseHandler.OnWaybillMsg(msg); retVal == nil {
retVal = jdapi.Err2CallbackResponse(errors.New("Internal Error"), "")
}
retVal = curPurchaseHandler.OnWaybillMsg(msg)
}
return retVal
}
func OnStoreMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil {
retVal = curPurchaseHandler.onStoreMsg(msg)
}
return retVal
}
func OnAfterSaleMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if curPurchaseHandler != nil {
utils.CallFuncAsync(func() {
OnFinancialMsg(msg)
})
retVal = curPurchaseHandler.OnStoreMsg(msg)
}
return retVal
}

View File

@@ -9,8 +9,15 @@ import (
"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
// 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 { // 如果是正向单
@@ -30,7 +37,7 @@ func OnFinancialMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse
} else {
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)
if err = err2; err == nil {
err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(curPurchaseHandler.AfsOrderDetail2Financial(orderData))
@@ -127,18 +134,20 @@ func (p *PurchaseHandler) OrderDetail2Financial(orderData map[string]interface{}
orderFinancial.TotalDiscountMoney += discountPrice
if xMap["orderShareRatioData"] != nil {
orderShareRatioData, _ := utils.HTTPBody2Values([]byte(utils.Interface2String(xMap["orderShareRatioData"])), false)
activity := &model.OrderDiscountFinancial{
VendorID: orderFinancial.VendorID,
VendorOrderID: orderFinancial.VendorOrderID,
VendorActivityID: utils.Interface2String(orderShareRatioData["promotionId"][0]),
Type: utils.Int64ToStr(int64(discountType)),
// ActivityName: utils.Interface2String(xMap["discountName"]),
// ActivityMoney: discountPrice,
// Remark: utils.Interface2String(xMap["orderShareRatioData"]),
if promotionID := orderShareRatioData.Get("promotionId"); promotionID != "" {
activity := &model.OrderDiscountFinancial{
VendorID: orderFinancial.VendorID,
VendorOrderID: orderFinancial.VendorOrderID,
VendorActivityID: promotionID, // utils.Interface2String(orderShareRatioData["promotionId"][0]),
Type: utils.Int64ToStr(int64(discountType)),
// ActivityName: utils.Interface2String(xMap["discountName"]),
// ActivityMoney: discountPrice,
// Remark: utils.Interface2String(xMap["orderShareRatioData"]),
}
orderFinancial.Discounts = append(orderFinancial.Discounts, activity)
// 通过活动Id去取京西活动补贴
// orderFinancial.JxSubsidyMoney +=
}
orderFinancial.Discounts = append(orderFinancial.Discounts, activity)
// 通过活动Id去取京西活动补贴
// orderFinancial.JxSubsidyMoney +=
}
}
globals.SugarLogger.Debug(utils.Format4Output(orderFinancial.Discounts, false))
@@ -154,7 +163,7 @@ func (p *PurchaseHandler) OrderDetail2Financial(orderData map[string]interface{}
orderFinancial.PmSubsidyMoney = utils.Interface2Int64WithDefault(order1["platOrderGoodsDiscountMoney"], 0) + orderFinancial.PmSkuSubsidyMoney
} else {
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
@@ -168,7 +177,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData map[string]interfac
VendorOrderID: utils.Interface2String(orderData["orderId"]),
VendorStoreID: utils.Interface2String(orderData["stationId"]),
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"]),
AfsFreightMoney: utils.MustInterface2Int64(orderData["afsFreight"]),
BoxMoney: utils.MustInterface2Int64(orderData["packagingMoney"]),

View File

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

View File

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

View File

@@ -5,6 +5,8 @@ import (
"strings"
"time"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
@@ -19,8 +21,8 @@ import (
var (
VendorStatus2StatusMap = map[string]int{
jdapi.OrderStatusPurchased: model.OrderStatusNew,
jdapi.OrderStatusNew: model.OrderStatusNew,
jdapi.OrderStatusWaitOutStore: model.OrderStatusAccepted,
jdapi.StatusIDWaitOutStore: model.OrderStatusAccepted,
jdapi.OrderStatusFinishedPickup: model.OrderStatusFinishedPickup,
jdapi.OrderStatusDelivering: model.OrderStatusDelivering,
jdapi.OrderStatusDelivered: model.OrderStatusFinished,
@@ -42,38 +44,83 @@ func (c *PurchaseHandler) OnOrderMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi
}
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)
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 {
status := c.callbackMsg2Status(msg)
if msg.StatusID == jdapi.OrderStatusAddComment || msg.StatusID == jdapi.OrderStatusModifyComment {
utils.CallFuncAsync(func() {
c.onOrderComment2(msg)
})
// 新订单事件,与订单状态有点冲突
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 {
utils.CallFuncAsync(func() {
c.onOrderComment2(msg)
})
}
err := partner.CurOrderManager.OnOrderStatusChanged(status)
// if globals.HandleLegacyJxOrder && err == nil {
// c.legacyJdOrderStatusChanged(status)
// }
retVal = jdapi.Err2CallbackResponse(err, status.VendorStatus)
}
err := partner.CurOrderManager.OnOrderStatusChanged(status)
// if globals.HandleLegacyJxOrder && err == nil {
// c.legacyJdOrderStatusChanged(status)
// }
retVal = jdapi.Err2CallbackResponse(err, status.VendorStatus)
}
return retVal
}
func (c *PurchaseHandler) getOrder(orderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) {
globals.SugarLogger.Debugf("jd GetOrder orderID:%s", orderID)
if orderMap, err = api.JdAPI.QuerySingleOrder(orderID); err == nil {
order = c.Map2Order(orderMap)
if jxutils.IsMobileFake(order.ConsigneeMobile) {
if realMobile, err := api.JdAPI.GetRealMobile4Order(orderID, order.VendorStoreID); err == nil { // 故意强制忽略取不到真实手机号错误
order.ConsigneeMobile2 = jxutils.FormalizeMobile(realMobile)
} else {
// globals.SugarLogger.Warnf("jd GetOrder orderID:%s, GetRealMobile4Order failed with error:%v", orderID, err2)
globals.SugarLogger.Debugf("jd getOrder orderID:%s", orderID)
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)
realMobile, err = api.JdAPI.GetRealMobile4Order(orderID, order.VendorStoreID)
if realMobile != "" {
order.ConsigneeMobile2 = jxutils.FormalizeMobile(realMobile)
}
}
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
}
@@ -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) {
result := orderData
orderID := utils.Int64ToStr(utils.MustInterface2Int64(result["orderId"]))
globals.SugarLogger.Debugf("jd Map2Order orderID:%s", orderID)
const defaultStatusTimeField = "orderPurchaseTime"
statusTimeField := defaultStatusTimeField
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)),
OriginalData: string(utils.MustMarshal(result)),
ActualPayPrice: utils.MustInterface2Int64(result["orderBuyerPayableMoney"]),
Skus: []*model.OrderSku{},
}
order.Status = c.GetStatusFromVendorStatus(order.VendorStatus)
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"])),
SkuName: product["skuName"].(string),
Weight: jxutils.FloatWeight2Int(float32(utils.MustInterface2Float64(product["skuWeight"]))),
VendorPrice: utils.MustInterface2Int64(product["skuStorePrice"]),
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 {
sku.SkuName += skuCostumeProperty.(string)
@@ -153,26 +204,18 @@ func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *mo
sku.SkuType = 1
}
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
}
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)
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() {
c.OnOrderDetail(orderMap, partner.CreatedPeration)
})
@@ -181,10 +224,10 @@ func (c *PurchaseHandler) onOrderNew(msg *jdapi.CallbackOrderMsg) (response *jda
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)
if err == nil {
err = partner.CurOrderManager.OnOrderAdjust(order, msg.StatusID)
err = partner.CurOrderManager.OnOrderAdjust(order, orderStatus)
if err == nil {
utils.CallFuncAsync(func() {
c.OnOrderDetail(orderMap, partner.UpdatedPeration)
@@ -233,14 +276,14 @@ func (c *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptI
if globals.EnableStoreWrite {
err = api.JdAPI.OrderAcceptOperate(order.VendorOrderID, isAcceptIt, userName)
} else {
c.postFakeMsg(order.VendorOrderID, jdapi.OrderStatusWaitOutStore)
c.postFakeMsg(order.VendorOrderID, jdapi.StatusIDWaitOutStore)
}
return err
}
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)
if !isSelfDelivery && globals.EnableStoreWrite {
if !isSelfDelivery && globals.EnableJdStoreWrite {
_, err = api.JdAPI.OrderJDZBDelivery(order.VendorOrderID, userName)
} else {
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) {
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
err = api.JdAPI.ReceiveFailedAudit(order.VendorOrderID, isAcceptIt, ctx.GetUserName(), "")
}
return err
}
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())
}
return err
}
func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 投递失败后确认收到退货
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
err = api.JdAPI.ConfirmReceiveGoods(order.VendorOrderID)
}
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) {
globals.SugarLogger.Debugf("jd Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
_, err = api.JdAPI.ModifySellerDelivery(order.VendorOrderID, userName)
if err != nil {
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) {
globals.SugarLogger.Debugf("jd Swtich2SelfDelivered orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
_, err = api.JdAPI.DeliveryEndOrder(order.VendorOrderID, userName)
}
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) {
globals.SugarLogger.Debugf("jd SelfDeliverDelivering orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
_, err = api.JdAPI.OrderSerllerDelivery(order.VendorOrderID, userName)
}
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) {
globals.SugarLogger.Debugf("jd SelfDeliverDelivered orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
err = c.Swtich2SelfDelivered(order, userName)
}
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) {
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
err = api.JdAPI.OrderCancelOperate(order.VendorOrderID, isAgree, ctx.GetUserName(), reason)
}
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) {
removedSkuMap := make(map[int]*model.OrderSku)
for _, sku := range removedSkuList {
removedSkuMap[jxutils.GetSkuIDFromOrderSku(sku)] = sku
}
order = jxutils.RemoveSkuFromOrder(order, removedSkuList)
var oaosAdjustDTOList []*jdapi.OAOSAdjustDTO
for _, sku := range order.Skus {
skuID := jxutils.GetSkuIDFromOrderSku(sku)
tmp := &jdapi.OAOSAdjustDTO{
OutSkuID: utils.Int2Str(skuID),
oaosAdjustDTOList = append(oaosAdjustDTOList, &jdapi.OAOSAdjustDTO{
OutSkuID: utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku)),
SkuCount: sku.Count,
}
if removedSkuMap[skuID] != nil {
if removedSkuMap[skuID].Count >= sku.Count {
tmp = nil
} else {
tmp.SkuCount -= removedSkuMap[skuID].Count
}
}
if tmp != nil {
oaosAdjustDTOList = append(oaosAdjustDTOList, tmp)
}
})
}
if globals.EnableStoreWrite {
if globals.EnableJdStoreWrite {
err = api.JdAPI.AdjustOrder(order.VendorOrderID, ctx.GetUserName(), reason, oaosAdjustDTOList)
}
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了
import (
"fmt"
"unicode/utf8"
"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) {
var skuInfoExt tSkuInfoExt
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
JOIN sku_name t2 ON t1.name_id = t2.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 {
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 {
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)
if 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
globals.SugarLogger.Debugf("syncSkuNameAsSpu1 sku.id=%d, bareSkuName:%s, skuName:%s, skuNameJdID:%d", sku.ID, skuExt.Name, skuName, skuNameJdID)
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 {
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 {
spuName := jxutils.ComposeSpuName(skuExt.Prefix, skuExt.Name, 0)
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 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)

View File

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

View File

@@ -3,6 +3,8 @@ package jd
import (
"fmt"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
@@ -17,140 +19,137 @@ import (
type tStoreSkuBindExt struct {
model.StoreSkuBind
PricePercentage int
VendorStoreID string `orm:"column(vendor_store_id)"`
JdID int64 `orm:"column(jd_id)"`
PricePercentage int
CatPricePercentage int
VendorStoreID string `orm:"column(vendor_store_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) {
globals.SugarLogger.Debugf("jd SyncStoresSkus, storeID:%d, skuIDs:%v", storeID, skuIDs)
sqlWhere0 := `
WHERE (t1.jd_sync_status <> 0) AND t1.store_id = ?
`
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 {
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 syncStoreSkus, storeID:%d", storeID)
storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJD)
if err != nil {
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) {
var skuPriceInfoList []*jdapi.SkuPriceInfo
var skuVendibilityList []*jdapi.StockVendibility
var skuStockList []*jdapi.SkuStock
stationNo := batchItemList[0].(*tStoreSkuBindExt).VendorStoreID
var batchSkuIDs []int
for _, v := range batchItemList {
storeSku := v.(*tStoreSkuBindExt)
alreadyAddStock := false
if storeSku.JdSyncStatus&model.SyncFlagChangedMask != 0 {
batchSkuIDs = append(batchSkuIDs, storeSku.SkuID)
if storeSku.JdSyncStatus&(model.SyncFlagDeletedMask|model.SyncFlagNewMask) != 0 { // 关注或取消关注
stock := &jdapi.SkuStock{
OutSkuId: utils.Int2Str(storeSku.SkuID),
StockQty: model.MaxStoreSkuStockQty,
doWork := func(batchItemList []interface{}) (isPartialFailed bool, err error) {
var skuPriceInfoList []*jdapi.SkuPriceInfo
var skuVendibilityList []*jdapi.StockVendibility
var skuStockList []*jdapi.SkuStock
stationNo := storeDetail.VendorStoreID
var batchBindIDs []int
for _, v := range batchItemList {
storeSku := v.(*dao.StoreSkuSyncInfo)
alreadyAddStock := false
if storeSku.StoreSkuSyncStatus&model.SyncFlagChangedMask != 0 || storeSku.BindID == 0 || storeSku.NameID == 0 {
if storeSku.BindID > 0 {
batchBindIDs = append(batchBindIDs, storeSku.BindID)
}
if storeSku.DeletedAt != utils.DefaultTimeValue {
stock.StockQty = 0
} else {
alreadyAddStock = true
}
if stock.StockQty != 0 || !storeskulock.IsJdStoreSkuLocked(stationNo, storeSku.JdID) {
skuStockList = append(skuStockList, stock)
}
}
if storeSku.JdSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
skuPriceInfoList = append(skuPriceInfoList, &jdapi.SkuPriceInfo{
OutSkuId: utils.Int2Str(storeSku.SkuID),
Price: constrainPrice(jxutils.CaculateSkuVendorPrice(storeSku.Price, storeSku.PricePercentage)),
})
}
if storeSku.JdSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
vendibility := &jdapi.StockVendibility{
OutSkuId: utils.Int2Str(storeSku.SkuID),
DoSale: true,
}
if storeSku.Status != model.StoreSkuBindStatusNormal {
vendibility.DoSale = false
} else if !alreadyAddStock { // 如果是设置可售则自动将库存加满
if storeSku.StoreSkuSyncStatus&(model.SyncFlagDeletedMask|model.SyncFlagNewMask) != 0 || storeSku.BindID == 0 || storeSku.NameID == 0 { // 关注或取消关注
stock := &jdapi.SkuStock{
OutSkuId: utils.Int2Str(storeSku.SkuID),
StockQty: model.MaxStoreSkuStockQty,
}
skuStockList = append(skuStockList, stock)
if storeSku.StoreSkuSyncStatus&model.SyncFlagDeletedMask != 0 || storeSku.DeletedAt != utils.DefaultTimeValue || storeSku.BindID == 0 || storeSku.NameID == 0 {
stock.StockQty = 0
} else {
alreadyAddStock = true
}
if stock.StockQty != 0 || !storeskulock.IsJdStoreSkuLocked(stationNo, storeSku.JdID) {
skuStockList = append(skuStockList, stock)
}
}
if vendibility.DoSale || !storeskulock.IsJdStoreSkuLocked(stationNo, storeSku.JdID) {
skuVendibilityList = append(skuVendibilityList, vendibility)
if storeSku.StoreSkuSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
skuPriceInfoList = append(skuPriceInfoList, &jdapi.SkuPriceInfo{
OutSkuId: utils.Int2Str(storeSku.SkuID),
Price: constrainPrice(jxutils.CaculateSkuVendorPrice(int(storeSku.Price), int(storeDetail.PricePercentage), storeSku.CatPricePercentage)),
})
}
if storeSku.StoreSkuSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
vendibility := &jdapi.StockVendibility{
OutSkuId: utils.Int2Str(storeSku.SkuID),
DoSale: true,
}
if storeSku.StoreSkuStatus != model.StoreSkuBindStatusNormal {
vendibility.DoSale = false
} else if !alreadyAddStock { // 如果是设置可售则自动将库存加满
stock := &jdapi.SkuStock{
OutSkuId: utils.Int2Str(storeSku.SkuID),
StockQty: model.MaxStoreSkuStockQty,
}
skuStockList = append(skuStockList, stock)
}
if vendibility.DoSale || !storeskulock.IsJdStoreSkuLocked(stationNo, storeSku.JdID) {
skuVendibilityList = append(skuVendibilityList, vendibility)
}
}
}
}
}
syncMask := 0
errList := []error{}
if globals.EnableStoreWrite {
// todo 以下可以优化为并行操作
globals.SugarLogger.Debug(utils.Format4Output(skuVendibilityList, false), utils.Format4Output(skuPriceInfoList, false), utils.Format4Output(skuStockList, false))
if len(skuVendibilityList) > 0 {
if _, err = api.JdAPI.BatchUpdateVendibility("", stationNo, skuVendibilityList, ctx.GetUserName()); err == nil {
syncMask |= model.SyncFlagSaleMask
} else {
syncMask := 0
errList := []error{}
if globals.EnableJdStoreWrite {
// todo 以下可以优化为并行操作
globals.SugarLogger.Debug(utils.Format4Output(skuVendibilityList, false), utils.Format4Output(skuPriceInfoList, false), utils.Format4Output(skuStockList, false))
if len(skuVendibilityList) > 0 {
if _, err = api.JdAPI.BatchUpdateVendibility("", stationNo, skuVendibilityList, ctx.GetUserName()); err == nil {
syncMask |= model.SyncFlagSaleMask
} else {
errList = append(errList, err)
}
}
if (err == nil || isContinueWhenError) && len(skuStockList) > 0 {
if _, err = api.JdAPI.BatchUpdateCurrentQtys("", stationNo, skuStockList, ctx.GetUserName()); err == nil {
syncMask |= model.SyncFlagNewMask | model.SyncFlagDeletedMask
} else {
errList = append(errList, err)
}
}
if (err == nil || isContinueWhenError) && len(skuPriceInfoList) > 0 {
if _, err = api.JdAPI.UpdateVendorStationPrice("", stationNo, skuPriceInfoList); err == nil {
syncMask |= model.SyncFlagPriceMask
} else {
isPartialFailed = isErrPartialFailed(err)
errList = append(errList, partner.NewErrorCode(err.Error(), partner.ErrCodeChangePriceFailed, model.VendorIDJD))
}
}
}
if len(errList) == 0 {
syncMask = -1
}
if syncMask != 0 && len(batchBindIDs) > 0 {
// db := dao.GetDB() // 多线程问题
sql := `
UPDATE store_sku_bind t1
SET t1.jd_sync_status = t1.jd_sync_status & ?
WHERE t1.id IN (` + dao.GenQuestionMarks(len(batchBindIDs)) + ")"
if _, err = dao.ExecuteSQL(db, sql, ^syncMask, batchBindIDs); err != nil {
errList = append(errList, err)
}
}
if (err == nil || isContinueWhenError) && len(skuStockList) > 0 {
if _, err = api.JdAPI.BatchUpdateCurrentQtys("", stationNo, skuStockList, ctx.GetUserName()); err == nil {
syncMask |= model.SyncFlagNewMask | model.SyncFlagDeletedMask
} else {
errList = append(errList, err)
}
if len(errList) == 1 {
err = errList[0]
} else if len(errList) > 1 {
err = fmt.Errorf("%v", errList)
}
if (err == nil || isContinueWhenError) && len(skuPriceInfoList) > 0 {
if _, err = api.JdAPI.UpdateVendorStationPrice("", stationNo, skuPriceInfoList); err == nil {
syncMask |= model.SyncFlagPriceMask
} else {
errList = append(errList, err)
}
return isPartialFailed, err
}
isErrPartialFailed, err := doWork(batchItemList)
if isErrPartialFailed && len(batchItemList) > 1 {
for _, v := range batchItemList {
doWork([]interface{}{v})
}
}
if len(errList) == 0 {
syncMask = -1
}
if syncMask != 0 && len(batchSkuIDs) > 0 {
db := dao.GetDB() // 多线程问题
sql := `
UPDATE store_sku_bind t1
SET t1.jd_sync_status = t1.jd_sync_status & ?
` + sqlWhere0 + " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(batchSkuIDs)) + ")"
if _, err = dao.ExecuteSQL(db, sql, ^syncMask, storeID, batchSkuIDs); err != nil {
errList = append(errList, err)
}
}
if len(errList) == 1 {
err = errList[0]
} else if len(errList) > 1 {
err = fmt.Errorf("%v", errList)
}
return nil, err
}, storeSkus)
tasksch.HandleTask(task, parentTask, false).Run()
@@ -160,112 +159,35 @@ func (p *PurchaseHandler) SyncStoreSkus(ctx *jxcontext.Context, parentTask tasks
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) {
globals.SugarLogger.Debugf("jd FullSyncStoreSkus, storeID:%d", storeID)
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 {
return "", err
}
skus, err := dao.GetFullStoreSkus(db, model.VendorIDJD, storeID)
storeSkus, err := dao.GetFullStoreSkus(db, model.VendorIDJD, storeID)
if err != nil {
return "", err
}
return p.syncStoreSkus(ctx, parentTask, db, storeID, skus, 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
return p.syncStoreSkus(ctx, parentTask, db, storeID, storeSkus, isAsync, isContinueWhenError)
}
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
}
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 (
"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/model"
)
@@ -19,11 +18,11 @@ func OnOrderCallbackMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackRes
}
}, 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() {
OnFinancialMsg(msg)
})
} else if msg.Cmd == mtwmapi.MsgTypeStoreStatusChanged {
} else */if msg.Cmd == mtwmapi.MsgTypeStoreStatusChanged {
response = curPurchaseHandler.onStoreStatusChanged(msg)
}
}
@@ -31,5 +30,5 @@ func OnOrderCallbackMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackRes
}
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) {
if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { // 部分退款处理
orderData := msg.Data
orderData := msg.FormData
if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess {
err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(curPurchaseHandler.AfsOrderDetail2Financial(orderData))
}
}
if msg.Cmd == mtwmapi.MsgTypeOrderRefund { // todo 全额退款处理
orderData := msg.Data
orderData := msg.FormData
if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess {
globals.SugarLogger.Debug(orderData.Get("order_id")) // 获得退款订单ID去本地数据库拿饿百消息推送只给了订单号也没有通过订单号查询退款信息的接口
afsOrderID := orderData.Get("order_id")
@@ -45,7 +45,7 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
VendorID: model.VendorIDMTWM,
AfsOrderID: 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,
// SkuBoxMoney: orderFinancial.SkuBoxMoney, // 美团的餐盒费已经拆到单条SKU里面去了,退款时直接计算用户支付sku金额就好了
FreightUserMoney: orderFinancial.FreightMoney,
@@ -64,9 +64,8 @@ func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.Orde
}
for _, sku := range orderFinancial.Skus {
orderSkuFinancial := &model.OrderSkuFinancial{
VendorID: sku.VendorID,
VendorOrderID: sku.VendorOrderID,
VendorOrderID2: sku.VendorOrderID2,
VendorID: sku.VendorID,
VendorOrderID: sku.VendorOrderID,
// OrderFinancialID: sku.VendorOrderID,
// ConfirmTime: afsOrder.AfsCreateAt,
VendorStoreID: afsOrder.VendorStoreID,
@@ -93,7 +92,7 @@ func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData url.Values) (afsOrd
VendorID: model.VendorIDMTWM,
AfsOrderID: 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"))),
}
// if orderData.Get("timestamp") != "" {

View File

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

View File

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

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