1108 lines
37 KiB
Go
1108 lines
37 KiB
Go
package promotion
|
||
|
||
import (
|
||
"encoding/gob"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"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/jxutils/storeskulock"
|
||
"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/globals"
|
||
"git.rosy.net.cn/jx-callback/globals/api"
|
||
)
|
||
|
||
const (
|
||
PromotionTypeNormal = 1
|
||
PromotionTypeDirectDown = 3
|
||
PromotionTypeLimitedTime = 4
|
||
)
|
||
|
||
const (
|
||
PriceTypePrice = 1 // 绝对价格
|
||
PriceTypePercentage = 2 // 百分比
|
||
)
|
||
|
||
const (
|
||
PromotionLimitedTimeMinPercentage = 79
|
||
)
|
||
|
||
const (
|
||
colSkuIDIndex = 0
|
||
colSkuPriceIndex = 3
|
||
colNameIndex = 5
|
||
colStoreIDIndex = 6
|
||
colBeginAtIndex = 9
|
||
colEndAtIndex = 10
|
||
)
|
||
|
||
const (
|
||
DefaultLimitSkuCount = 100
|
||
MaxPromotionSkuCount = jdapi.MaxPromotionSkuCount
|
||
)
|
||
|
||
const (
|
||
defSearchDays = 7
|
||
stockRefreshGap = 5 * time.Minute
|
||
userName = "jdpromotion"
|
||
)
|
||
|
||
const (
|
||
keyPromotionSource = "promotionSource"
|
||
keyPromotionStatus = "promotionStatus"
|
||
keyLimitDevice = "limitDevice"
|
||
keyLimitPin = "limitPin"
|
||
keyLimitCount = "limitCount"
|
||
keyLimitDaily = "limitDaily"
|
||
)
|
||
const (
|
||
PromotionSourceOpenPlatform = "开放平台"
|
||
)
|
||
|
||
type SkuPrice struct {
|
||
SkuID int `json:"skuID"`
|
||
PriceType int `json:"priceType"`
|
||
Price int `json:"price"` // 分,这个不是单价,是这个sku的活动价
|
||
LimitSkuCount int `json:"limitSkuCount"`
|
||
IsLock int8 `json:"isLock"`
|
||
|
||
EarningPrice int `json:"earningPrice"` // 活动商品设置,结算给门店老板的钱
|
||
}
|
||
|
||
type tPromotionItemInfo struct {
|
||
SkuID int `orm:"column(sku_id)"`
|
||
IsLock int8 // 是否锁定门店商品信息
|
||
EndAt time.Time
|
||
JdStoreID string `orm:"column(vendor_store_id)"`
|
||
JdSkuID int64 `orm:"column(jd_id)"`
|
||
}
|
||
|
||
type tSimpleStore struct {
|
||
StoreID int `orm:"column(store_id)" json:"storeID"`
|
||
Name string `orm:"column(name)" json:"name"`
|
||
}
|
||
|
||
type PromotionParams struct {
|
||
Name string
|
||
Advertising string
|
||
Type int
|
||
BeginAt time.Time
|
||
EndAt time.Time
|
||
StoreIDs []int
|
||
SkuPrices []*SkuPrice
|
||
}
|
||
|
||
type tStoreSkuBindExt struct {
|
||
model.StoreSkuBind
|
||
JdSkuID int64 `orm:"column(jd_id)"`
|
||
VendorStoreID string `orm:"column(vendor_store_id)"`
|
||
}
|
||
|
||
type tPromotionInfo struct {
|
||
model.Promotion
|
||
StoreStr string `orm:"column(store_str)" json:"-"`
|
||
SkuPriceStr string `orm:"column(sku_price_str)" json:"-"`
|
||
Stores []*tSimpleStore `orm:"-" json:"stores"`
|
||
SkuPrices []*SkuPrice `orm:"-" json:"skuPrices"`
|
||
}
|
||
|
||
var (
|
||
ErrEmptySkus = errors.New("空sku或指定的SKU没有被门店认领,请检查")
|
||
)
|
||
|
||
var (
|
||
jd2jxPromotionStatusMap = map[int]int{
|
||
jdapi.PromotionStateNotConfirm: model.PromotionStatusRemoteCreated,
|
||
jdapi.PromotionStateConfirmed: model.PromotionStatusRemoteCreated,
|
||
jdapi.PromotionStateCanceled: model.PromotionStatusCanceled,
|
||
jdapi.PromotionStateEnded: model.PromotionStatusEnded,
|
||
}
|
||
)
|
||
|
||
var (
|
||
ErrLimitDeviceIsInvalid = errors.New("必须指定一个limitCount,当limitPin或limitDevice不都为0时")
|
||
)
|
||
|
||
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 []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error)
|
||
ConfirmPromotion(infoId int64, outInfoId string) (err error)
|
||
CancelPromotion(infoId int64, outInfoId string) (err error)
|
||
}
|
||
|
||
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, "")
|
||
}
|
||
func (p *JdDirectDownHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) {
|
||
return api.JdAPI.CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, "")
|
||
}
|
||
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, "")
|
||
}
|
||
func (p *JdDirectDownHandler) CancelPromotion(infoId int64, outInfoId string) (err error) {
|
||
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, "")
|
||
}
|
||
func (p *JdLimitedTimeHandler) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) {
|
||
return api.JdAPI.CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, "")
|
||
}
|
||
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, "")
|
||
}
|
||
func (p *JdLimitedTimeHandler) CancelPromotion(infoId int64, outInfoId string) (err error) {
|
||
return api.JdAPI.CancelPromotionLimitTime(infoId, outInfoId, "")
|
||
}
|
||
|
||
type JdNullHandler struct {
|
||
}
|
||
|
||
func (p *JdNullHandler) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) {
|
||
return jxutils.GenFakeID(), nil
|
||
}
|
||
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 []*jdapi.PromotionSku) (skusResult []*jdapi.PromotionSku, err error) {
|
||
return nil, nil
|
||
}
|
||
func (p *JdNullHandler) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
|
||
return nil
|
||
}
|
||
func (p *JdNullHandler) CancelPromotion(infoId int64, outInfoId string) (err error) {
|
||
return nil
|
||
}
|
||
|
||
func init() {
|
||
gob.Register(&PromotionParams{})
|
||
gob.Register([]*SkuPrice{})
|
||
}
|
||
|
||
func scheduleDailyRoutine(isFirst bool) {
|
||
executeTime := utils.GetCurDate().Add(24*time.Hour + 5*time.Minute) // 凌晨00:05执行
|
||
duration := executeTime.Sub(time.Now())
|
||
if isFirst && duration > 1*time.Hour {
|
||
UpdateJdPromotionStatus()
|
||
}
|
||
// globals.SugarLogger.Debug(duration)
|
||
utils.AfterFuncWithRecover(duration, func() {
|
||
UpdateJdPromotionStatus()
|
||
scheduleDailyRoutine(false)
|
||
})
|
||
}
|
||
|
||
func scheduleRoutine(isFirst bool) {
|
||
if isFirst {
|
||
utils.CallFuncAsync(func() {
|
||
RefreshJdLockStoreSku()
|
||
RefreshJdStoreSkuStock(0, nil)
|
||
})
|
||
}
|
||
utils.AfterFuncWithRecover(stockRefreshGap, func() {
|
||
RefreshJdLockStoreSku()
|
||
RefreshJdStoreSkuStock(0, nil)
|
||
scheduleRoutine(false)
|
||
})
|
||
}
|
||
|
||
func Init() {
|
||
scheduleDailyRoutine(true)
|
||
// scheduleRoutine(true)
|
||
}
|
||
|
||
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)
|
||
}
|
||
if len(params.SkuPrices) == 0 {
|
||
return "", ErrEmptySkus
|
||
}
|
||
limitDaily := 1
|
||
if limitDaily2, ok := mapData[keyLimitDaily]; ok {
|
||
limitDaily = jxutils.Int2OneZero(limitDaily2.(int))
|
||
}
|
||
limitPin := 1
|
||
if limitPin2, ok := mapData[keyLimitPin]; ok {
|
||
limitPin = jxutils.Int2OneZero(limitPin2.(int))
|
||
}
|
||
limitDevice := 1
|
||
if limitDevice2, ok := mapData[keyLimitDevice]; ok {
|
||
limitDevice = jxutils.Int2OneZero(limitDevice2.(int))
|
||
}
|
||
limitCount := 1
|
||
if limitCount2, ok := mapData[keyLimitCount]; ok {
|
||
limitCount = limitCount2.(int)
|
||
}
|
||
if (limitDevice == 1 || limitPin == 1) && limitCount == 0 {
|
||
return "", ErrLimitDeviceIsInvalid
|
||
}
|
||
|
||
userName := ctx.GetUserName()
|
||
db := dao.GetDB()
|
||
modifyPricesList := make(map[int][]*jdapi.SkuPriceInfo)
|
||
promotionPrices := make([]*jdapi.PromotionSku, len(params.StoreIDs)*len(params.SkuPrices))
|
||
var jxStoreIDs []int
|
||
promotion := &model.Promotion{
|
||
Name: params.Name,
|
||
Advertising: params.Advertising,
|
||
VendorID: vendorID,
|
||
Type: params.Type,
|
||
Status: model.PromotionStatusLocalCreated,
|
||
LimitDevice: int8(limitDevice),
|
||
LimitPin: int8(limitPin),
|
||
LimitCount: limitCount,
|
||
LimitDaily: int8(limitDaily),
|
||
BeginAt: params.BeginAt,
|
||
EndAt: params.EndAt,
|
||
CreateType: model.PromotionCreateTypeByJX,
|
||
Source: PromotionSourceOpenPlatform,
|
||
}
|
||
|
||
skuIDs := make([]int, len(params.SkuPrices))
|
||
skuPriceMap := make(map[int64]*SkuPrice)
|
||
for k, v := range params.SkuPrices {
|
||
skuIDs[k] = v.SkuID
|
||
skuPriceMap[int64(v.SkuID)] = v
|
||
}
|
||
if len(skuIDs) == 0 {
|
||
return "", fmt.Errorf("商品列表为空")
|
||
}
|
||
if vendorID == model.VendorIDJX {
|
||
conflictPromotion, err2 := dao.GetPromotionSkuPriceMap(db, model.VendorIDJX, params.StoreIDs, skuIDs, promotion.BeginAt, promotion.EndAt)
|
||
if err = err2; err != nil {
|
||
return "", err
|
||
}
|
||
if len(conflictPromotion) > 0 {
|
||
return "", fmt.Errorf("有冲突配置:%s", utils.Format4Output(conflictPromotion, false))
|
||
}
|
||
}
|
||
if vendorPromotionID == "" {
|
||
if vendorID == model.VendorIDJD {
|
||
sql := `
|
||
SELECT t1.*, t2.jd_id, t3.vendor_store_id
|
||
FROM store_sku_bind t1
|
||
JOIN sku t2 ON t1.sku_id = t2.id
|
||
JOIN store_map t3 ON t1.store_id = t3.store_id AND t3.vendor_id = ? AND t3.deleted_at = ?
|
||
WHERE t1.deleted_at = ?
|
||
`
|
||
sqlParam := []interface{}{
|
||
model.VendorIDJD,
|
||
utils.DefaultTimeValue,
|
||
utils.DefaultTimeValue,
|
||
skuIDs,
|
||
}
|
||
if isIDJd {
|
||
sql += " AND t2.jd_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ") AND t3.vendor_store_id = ?"
|
||
} else {
|
||
sql += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ") AND t1.store_id = ?"
|
||
}
|
||
|
||
errMsg := ""
|
||
index := 0
|
||
for _, storeID := range params.StoreIDs {
|
||
var skuBinds []*tStoreSkuBindExt
|
||
if err = dao.GetRows(db, &skuBinds, sql, append(sqlParam, storeID)...); err != nil {
|
||
return "", err
|
||
}
|
||
for k, skuBind := range skuBinds {
|
||
if k == 0 {
|
||
jxStoreIDs = append(jxStoreIDs, skuBind.StoreID)
|
||
}
|
||
mapSkuID := int64(skuBind.SkuID)
|
||
if isIDJd {
|
||
mapSkuID = skuBind.JdSkuID
|
||
}
|
||
promotionSkuPrice := skuPriceMap[mapSkuID]
|
||
if promotionSkuPrice.PriceType == PriceTypePercentage {
|
||
promotionSkuPrice.Price = skuBind.Price * promotionSkuPrice.Price / 100
|
||
}
|
||
if vendorID != model.VendorIDJX && promotionSkuPrice.Price >= skuBind.Price {
|
||
errMsg += fmt.Sprintf("活动价大于等于原价,storeID:%d, skuID:%d\n", skuBind.StoreID, skuBind.SkuID)
|
||
}
|
||
if promotionSkuPrice.LimitSkuCount <= 0 {
|
||
promotionSkuPrice.LimitSkuCount = DefaultLimitSkuCount
|
||
}
|
||
if errMsg == "" {
|
||
if params.Type == PromotionTypeLimitedTime {
|
||
if skuBind.Price*PromotionLimitedTimeMinPercentage/100 < promotionSkuPrice.Price {
|
||
modifyPricesList[skuBind.StoreID] = append(modifyPricesList[skuBind.StoreID], &jdapi.SkuPriceInfo{
|
||
OutSkuId: utils.Int2Str(skuBind.SkuID),
|
||
Price: promotionSkuPrice.Price*100/PromotionLimitedTimeMinPercentage + 5,
|
||
})
|
||
}
|
||
}
|
||
promotionPrices[index] = &jdapi.PromotionSku{
|
||
StationNo: utils.Str2Int64(skuBind.VendorStoreID),
|
||
SkuID: skuBind.JdSkuID,
|
||
PromotionPrice: int64(promotionSkuPrice.Price),
|
||
LimitSkuCount: promotionSkuPrice.LimitSkuCount,
|
||
}
|
||
index++
|
||
}
|
||
}
|
||
}
|
||
if errMsg != "" {
|
||
return "", errors.New(errMsg)
|
||
}
|
||
promotionPrices = promotionPrices[:index]
|
||
if len(promotionPrices) == 0 {
|
||
return "", ErrEmptySkus
|
||
}
|
||
}
|
||
} else {
|
||
promotion.VendorPromotionID = vendorPromotionID
|
||
promotion.CreateType = model.PromotionCreateTypeByVendor
|
||
if status, ok := mapData[keyPromotionStatus]; ok {
|
||
promotion.Status = status.(int)
|
||
} else {
|
||
promotion.Status = model.PromotionStatusRemoteCreated
|
||
}
|
||
if source, ok := mapData[keyPromotionSource]; ok {
|
||
promotion.Source = source.(string)
|
||
}
|
||
}
|
||
|
||
dao.WrapAddIDCULDEntity(promotion, userName)
|
||
// if promotion.Params, err = refutil.SerializeData(params); err != nil {
|
||
// return "", err
|
||
// }
|
||
// promotion.Params = string(utils.MustMarshal(params))
|
||
dao.Begin(db)
|
||
defer func() {
|
||
dao.Rollback(db)
|
||
}()
|
||
|
||
if err = dao.CreateEntity(db, promotion); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
for _, storeID := range params.StoreIDs {
|
||
promotionStore := &model.PromotionStore{
|
||
PromotionID: promotion.ID,
|
||
StoreID: storeID,
|
||
}
|
||
dao.WrapAddIDCULDEntity(promotionStore, ctx.GetUserName())
|
||
if err = dao.CreateEntity(db, promotionStore); err != nil {
|
||
return "", err
|
||
}
|
||
}
|
||
for _, skuPrice := range params.SkuPrices {
|
||
promotionSku := &model.PromotionSku{
|
||
PromotionID: promotion.ID,
|
||
SkuID: skuPrice.SkuID,
|
||
PriceType: skuPrice.PriceType,
|
||
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 {
|
||
return "", err
|
||
}
|
||
}
|
||
|
||
if vendorID != model.VendorIDJX && vendorPromotionID == "" {
|
||
promotionHandler := getPromotionHander(params.Type)
|
||
if promotionHandler == nil {
|
||
return "", errors.New("非法的活动类型")
|
||
}
|
||
infoId, err2 := promotionHandler.CreatePromotionInfos(params.Name, params.BeginAt, params.EndAt, utils.Int2Str(promotion.ID), params.Advertising)
|
||
if err = err2; err != nil {
|
||
return "", err
|
||
}
|
||
promotion.VendorPromotionID = utils.Int64ToStr(infoId)
|
||
if _, err = dao.UpdateEntity(db, promotion); err != nil {
|
||
return "", err
|
||
}
|
||
dao.Commit(db)
|
||
|
||
rootTask := tasksch.NewSeqTask("CreateJdPromotion", ctx,
|
||
func(task *tasksch.SeqTask, step int, params2 ...interface{}) (result interface{}, err error) {
|
||
if step == 0 {
|
||
task1 := tasksch.NewParallelTask("CreateJdPromotion update sku price", nil, ctx,
|
||
func(t *tasksch.ParallelTask, batchItemList []interface{}, params2 ...interface{}) (retVal interface{}, err error) {
|
||
storeID := batchItemList[0].(int)
|
||
modifyPricesList := jxutils.SplitSlice(modifyPricesList[storeID], jdapi.MaxStoreSkuBatchSize)
|
||
for _, modifyPrices := range modifyPricesList {
|
||
modifyPrices2 := make([]*jdapi.SkuPriceInfo, len(modifyPrices))
|
||
for k, v := range modifyPrices {
|
||
modifyPrices2[k] = v.(*jdapi.SkuPriceInfo)
|
||
}
|
||
if globals.EnableJdStoreWrite {
|
||
if _, err = api.JdAPI.UpdateVendorStationPrice(utils.Int2Str(storeID), "", modifyPrices2); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
return nil, nil
|
||
}, jxStoreIDs)
|
||
task.AddChild(task1).Run()
|
||
_, err = task1.GetResult(0)
|
||
} else if step == 1 {
|
||
err = promotionHandler.CreatePromotionRules(infoId, "", limitDevice, limitPin, limitCount, limitDaily)
|
||
} 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([]*jdapi.PromotionSku, len(batchItemList))
|
||
for k, v := range batchItemList {
|
||
skus[k] = v.(*jdapi.PromotionSku)
|
||
}
|
||
_, err = promotionHandler.CreatePromotionSku(infoId, "", skus)
|
||
return nil, err
|
||
}, promotionPrices)
|
||
task.AddChild(task2).Run()
|
||
_, err = task2.GetResult(0)
|
||
if isContinueWhenError && err != nil { // todo isContinueWhenError为true时,强制忽略此步的错误
|
||
err = nil
|
||
}
|
||
} else if step == 3 {
|
||
err = promotionHandler.ConfirmPromotion(infoId, "")
|
||
if err == nil {
|
||
db := dao.GetDB()
|
||
if _, err = dao.UpdateEntityLogically(db, promotion, map[string]interface{}{
|
||
model.FieldStatus: model.PromotionStatusRemoteCreated,
|
||
}, ctx.GetUserName(), nil); err == nil {
|
||
RefreshJdPromotionLockStatus(ctx, promotion.ID)
|
||
}
|
||
}
|
||
}
|
||
if err != nil {
|
||
db := dao.GetDB()
|
||
dao.UpdateEntityLogically(db, promotion, map[string]interface{}{
|
||
model.FieldStatus: model.PromotionStatusRemoteFailed,
|
||
model.FieldRemark: err.Error(),
|
||
}, ctx.GetUserName(), nil)
|
||
}
|
||
return nil, err
|
||
}, 4)
|
||
tasksch.HandleTask(rootTask, nil, true).Run()
|
||
if !isAsync {
|
||
_, err = rootTask.GetResult(0)
|
||
}
|
||
hint = rootTask.ID
|
||
} else {
|
||
dao.Commit(db)
|
||
}
|
||
return hint, err
|
||
}
|
||
|
||
func GetJdPromotions(ctx *jxcontext.Context, keyword string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
|
||
sql := `
|
||
SELECT SQL_CALC_FOUND_ROWS
|
||
t1.id,
|
||
t1.created_at,
|
||
t1.updated_at,
|
||
t1.last_operator,
|
||
t1.vendor_id,
|
||
t1.name,
|
||
t1.type,
|
||
t1.status,
|
||
t1.vendor_promotion_id,
|
||
t1.begin_at,
|
||
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, ', "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
|
||
JOIN promotion_sku t3 ON t1.id = t3.promotion_id
|
||
WHERE t1.deleted_at = ?
|
||
`
|
||
sqlParams := []interface{}{
|
||
utils.DefaultTimeValue,
|
||
}
|
||
if keyword != "" {
|
||
keywordLike := "%" + keyword + "%"
|
||
sql += " AND ( t1.name LIKE ?"
|
||
sqlParams = append(sqlParams, keywordLike)
|
||
keywordInt := utils.Str2Int64WithDefault(keyword, 0)
|
||
if keywordInt > 0 {
|
||
sql += `
|
||
OR t1.id = ? OR t1.vendor_promotion_id = ?
|
||
OR (SELECT COUNT(*) FROM promotion_store tt1 WHERE tt1.promotion_id = t1.id AND tt1.store_id = ?) > 0
|
||
OR (SELECT COUNT(*) FROM promotion_sku tt1 WHERE tt1.promotion_id = t1.id AND tt1.sku_id = ?) > 0
|
||
`
|
||
sqlParams = append(sqlParams, keywordInt, keywordInt, keywordInt, keywordInt)
|
||
}
|
||
sql += ")"
|
||
}
|
||
if params["vendorID"] != nil {
|
||
sql += " AND t1.vendor_id = ?"
|
||
sqlParams = append(sqlParams, params["vendorID"].(int))
|
||
}
|
||
if params["promotionID"] != nil {
|
||
sql += " AND t1.id = ?"
|
||
sqlParams = append(sqlParams, params["promotionID"].(int))
|
||
}
|
||
if params["vendorPromotionID"] != nil {
|
||
sql += " AND t1.vendor_promotion_id = ?"
|
||
sqlParams = append(sqlParams, params["vendorPromotionID"].(string))
|
||
}
|
||
if params["name"] != nil {
|
||
sql += " AND t1.name LIKE ?"
|
||
sqlParams = append(sqlParams, "%"+params["name"].(string)+"%")
|
||
}
|
||
if params["beginAt"] != nil {
|
||
sql += " AND t1.begin_at <= ?"
|
||
beginAt, err2 := utils.TryStr2Time(params["beginAt"].(string))
|
||
if err = err2; err != nil {
|
||
return nil, err
|
||
}
|
||
sqlParams = append(sqlParams, beginAt)
|
||
}
|
||
if params["endAt"] != nil {
|
||
sql += " AND t1.end_at >= ?"
|
||
endAt, err2 := utils.TryStr2Time(params["endAt"].(string))
|
||
if err = err2; err != nil {
|
||
return nil, err
|
||
}
|
||
sqlParams = append(sqlParams, endAt)
|
||
}
|
||
days := defSearchDays
|
||
if params["days"] != nil {
|
||
days = params["days"].(int)
|
||
}
|
||
sql += " AND t1.created_at >= ?"
|
||
sqlParams = append(sqlParams, time.Now().Add(-time.Duration(days)*24*time.Hour))
|
||
if params["type"] != nil {
|
||
sql += " AND t1.type = ?"
|
||
sqlParams = append(sqlParams, params["type"].(int))
|
||
}
|
||
if params["storeID"] != nil || params["cityCode"] != nil {
|
||
sql += " AND (SELECT COUNT(*) FROM promotion_store tt1 "
|
||
if params["cityCode"] != nil {
|
||
sql += " JOIN store st ON st.id = tt1.store_id AND st.city_code = ?"
|
||
sqlParams = append(sqlParams, params["cityCode"].(int))
|
||
}
|
||
sql += " WHERE tt1.promotion_id = t1.id"
|
||
if params["storeID"] != nil {
|
||
sql += " AND tt1.store_id = ?"
|
||
sqlParams = append(sqlParams, params["storeID"].(int))
|
||
}
|
||
sql += ") > 0"
|
||
}
|
||
if params["skuID"] != nil {
|
||
sql += " AND (SELECT COUNT(*) FROM promotion_sku tt1 WHERE tt1.promotion_id = t1.id AND tt1.sku_id = ?) > 0"
|
||
sqlParams = append(sqlParams, params["skuID"].(int))
|
||
}
|
||
sql += `
|
||
GROUP BY
|
||
1,2,3,4,5,6,7,8,9,10,11,12
|
||
ORDER BY t1.id DESC
|
||
LIMIT ? OFFSET ?
|
||
`
|
||
pageSize = jxutils.FormalizePageSize(pageSize)
|
||
if offset < 0 {
|
||
offset = 0
|
||
}
|
||
sqlParams = append(sqlParams, pageSize, offset)
|
||
|
||
db := dao.GetDB()
|
||
dao.Begin(db)
|
||
defer func() {
|
||
dao.Rollback(db)
|
||
if r := recover(); r != nil {
|
||
panic(r)
|
||
}
|
||
}()
|
||
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
|
||
// globals.SugarLogger.Debug(sql)
|
||
var promotionList []*tPromotionInfo
|
||
if err = dao.GetRows(db, &promotionList, sql, sqlParams...); err == nil {
|
||
for _, v := range promotionList {
|
||
if v.StoreStr != "" {
|
||
if err = utils.UnmarshalUseNumber([]byte(v.StoreStr), &v.Stores); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
if v.SkuPriceStr != "" {
|
||
if err = utils.UnmarshalUseNumber([]byte(v.SkuPriceStr), &v.SkuPrices); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
pagedInfo = &model.PagedInfo{
|
||
TotalCount: dao.GetLastTotalRowCount(db),
|
||
Data: promotionList,
|
||
}
|
||
}
|
||
dao.Commit(db)
|
||
return pagedInfo, err
|
||
}
|
||
|
||
func CancelJdPromotion(ctx *jxcontext.Context, promotionID int) (err error) {
|
||
globals.SugarLogger.Debug("CancelJdPromotion promotionID:%d", promotionID)
|
||
|
||
db := dao.GetDB()
|
||
promotion := &model.Promotion{}
|
||
promotion.ID = promotionID
|
||
if err = dao.GetEntity(db, promotion); err != nil {
|
||
return err
|
||
}
|
||
if promotion.Status == model.PromotionStatusCanceled {
|
||
return errors.New("当前状态已经是取消")
|
||
}
|
||
if promotion.Status == model.PromotionStatusRemoteCreated {
|
||
if promotion.VendorPromotionID != "" {
|
||
promotionHandler := getPromotionHander(promotion.Type)
|
||
if promotionHandler == nil {
|
||
return errors.New("非法的活动类型")
|
||
}
|
||
if err = promotionHandler.CancelPromotion(utils.Str2Int64(promotion.VendorPromotionID), ""); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
if _, err = dao.UpdateEntityLogically(db, promotion, map[string]interface{}{
|
||
"Status": model.PromotionStatusCanceled,
|
||
}, ctx.GetUserName(), nil); err == nil {
|
||
// RefreshJdPromotionLockStatus(ctx, promotionID)
|
||
}
|
||
return err
|
||
}
|
||
|
||
// 每一段时间运行一次
|
||
func RefreshJdLockStoreSku() (err error) {
|
||
globals.SugarLogger.Debug("RefreshJdLockStoreSku")
|
||
|
||
sql := `
|
||
SELECT t22.vendor_store_id, t3.sku_id, t32.jd_id, MAX(t3.is_lock) is_lock, MAX(t1.end_at) end_at
|
||
FROM promotion t1
|
||
JOIN promotion_store t2 ON t1.id = t2.promotion_id
|
||
JOIN store_map t22 ON t2.store_id = t22.store_id AND t22.vendor_id = ? AND t22.deleted_at = ?
|
||
JOIN promotion_sku t3 ON t1.id = t3.promotion_id AND t3.is_lock = 1
|
||
JOIN sku t32 ON t3.sku_id = t32.id
|
||
WHERE t1.deleted_at = ? AND t1.vendor_id = ? AND t1.status = ? AND (t1.begin_at <= ? AND t1.end_at >= ?)
|
||
GROUP BY 1,2,3
|
||
`
|
||
nowDate := time.Now()
|
||
sqlParams := []interface{}{
|
||
model.VendorIDJD,
|
||
utils.DefaultTimeValue,
|
||
utils.DefaultTimeValue,
|
||
model.VendorIDJD,
|
||
model.PromotionStatusRemoteCreated,
|
||
nowDate,
|
||
nowDate,
|
||
}
|
||
var promotionItemList []*tPromotionItemInfo
|
||
db := dao.GetDB()
|
||
if err = dao.GetRows(db, &promotionItemList, sql, sqlParams...); err != nil {
|
||
if !dao.IsNoRowsError(err) {
|
||
globals.SugarLogger.Warnf("RefreshJdLockStoreSku GetRows failed with error:%v", err)
|
||
}
|
||
return err
|
||
}
|
||
storeskulock.ClearJdStoreSkuLock()
|
||
return RefreshJdPromotionItemListLockStatus(promotionItemList)
|
||
}
|
||
|
||
func RefreshJdStoreSkuStock(promotionID int, skuIDs []int) (err error) {
|
||
globals.SugarLogger.Debugf("RefreshJdStoreSkuStock promotionID:%d", promotionID)
|
||
|
||
sql := `
|
||
SELECT t22.vendor_store_id, t3.sku_id, t32.jd_id, MAX(t3.is_lock) is_lock, MAX(t1.end_at) end_at
|
||
FROM promotion t1
|
||
JOIN promotion_store t2 ON t1.id = t2.promotion_id
|
||
JOIN store_map t22 ON t2.store_id = t22.store_id AND t22.vendor_id = ? AND t22.deleted_at = ?
|
||
JOIN promotion_sku t3 ON t1.id = t3.promotion_id AND t3.is_lock = 1
|
||
JOIN sku t32 ON t3.sku_id = t32.id
|
||
JOIN store_sku_bind t4 ON t2.store_id = t4.store_id AND t3.sku_id = t4.sku_id AND t4.deleted_at = ?
|
||
WHERE t1.deleted_at = ? AND t1.vendor_id = ? AND t1.status = ? AND (t1.begin_at <= ? AND t1.end_at >= ?)
|
||
`
|
||
nowDate := time.Now()
|
||
sqlParams := []interface{}{
|
||
model.VendorIDJD,
|
||
utils.DefaultTimeValue,
|
||
utils.DefaultTimeValue,
|
||
utils.DefaultTimeValue,
|
||
model.VendorIDJD,
|
||
model.PromotionStatusRemoteCreated,
|
||
nowDate,
|
||
nowDate,
|
||
}
|
||
if promotionID != 0 {
|
||
sql += " AND t1.id = ?"
|
||
sqlParams = append(sqlParams, promotionID)
|
||
}
|
||
if len(skuIDs) > 0 {
|
||
sql += " AND t3.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
|
||
sqlParams = append(sqlParams, skuIDs)
|
||
}
|
||
sql += `
|
||
GROUP BY 1,2,3
|
||
ORDER BY 1,2,3
|
||
`
|
||
var promotionItemList []*tPromotionItemInfo
|
||
db := dao.GetDB()
|
||
if err = dao.GetRows(db, &promotionItemList, sql, sqlParams...); err != nil {
|
||
if !dao.IsNoRowsError(err) {
|
||
globals.SugarLogger.Warnf("RefreshJdStoreSkuStock GetRows failed with error:%v", err)
|
||
}
|
||
return err
|
||
}
|
||
globals.SugarLogger.Debugf("RefreshJdStoreSkuStock promotionID:%d, len(promotionItemList)=%d", promotionID, len(promotionItemList))
|
||
if len(promotionItemList) > 0 {
|
||
task := tasksch.NewParallelTask("RefreshJdStoreSkuStock", tasksch.NewParallelConfig().SetBatchSize(jdapi.MaxStoreSkuBatchSize).SetIsContinueWhenError(true), jxcontext.AdminCtx,
|
||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||
stockList := make([]*jdapi.SkuStock, 0)
|
||
stationNo := batchItemList[0].(*tPromotionItemInfo).JdStoreID
|
||
for _, v := range batchItemList {
|
||
promotionItem := v.(*tPromotionItemInfo)
|
||
if promotionItem.JdStoreID != stationNo {
|
||
// globals.SugarLogger.Debugf("RefreshJdStoreSkuStock BatchUpdateCurrentQtys stationNo:%s, stockList:%s", stationNo, utils.Format4Output(stockList, false))
|
||
_, err = api.JdAPI.BatchUpdateCurrentQtys("", stationNo, stockList, userName)
|
||
if err != nil {
|
||
globals.SugarLogger.Warnf("RefreshJdStoreSkuStock BatchUpdateCurrentQtys failed with error:%v", err)
|
||
}
|
||
stockList = make([]*jdapi.SkuStock, 0)
|
||
stationNo = promotionItem.JdStoreID
|
||
}
|
||
stockList = append(stockList, &jdapi.SkuStock{
|
||
OutSkuId: utils.Int2Str(promotionItem.SkuID),
|
||
StockQty: model.MaxStoreSkuStockQty,
|
||
})
|
||
}
|
||
// globals.SugarLogger.Debugf("RefreshJdStoreSkuStock BatchUpdateCurrentQtys stationNo:%s, stockList:%s", stationNo, utils.Format4Output(stockList, false))
|
||
_, err = api.JdAPI.BatchUpdateCurrentQtys("", stationNo, stockList, userName)
|
||
if err != nil {
|
||
globals.SugarLogger.Warnf("RefreshJdStoreSkuStock BatchUpdateCurrentQtys failed with error:%v", err)
|
||
}
|
||
return nil, err
|
||
}, promotionItemList)
|
||
task.Run()
|
||
_, err = task.GetResult(0)
|
||
}
|
||
return err
|
||
}
|
||
|
||
// 每晚凌晨运行一次
|
||
func UpdateJdPromotionStatus() (num int64, err error) {
|
||
sql := `
|
||
UPDATE promotion t1
|
||
SET t1.status = ?
|
||
WHERE t1.deleted_at = ? AND t1.vendor_id = ? AND t1.status = ? AND t1.end_at < ?
|
||
`
|
||
nowDate := utils.GetCurDate()
|
||
sqlParams := []interface{}{
|
||
model.PromotionStatusEnded,
|
||
utils.DefaultTimeValue,
|
||
model.VendorIDJD,
|
||
model.PromotionStatusRemoteCreated,
|
||
nowDate,
|
||
}
|
||
db := dao.GetDB()
|
||
return dao.ExecuteSQL(db, sql, sqlParams...)
|
||
}
|
||
|
||
func RefreshJdPromotionItemListLockStatus(promotionItemList []*tPromotionItemInfo) (err error) {
|
||
globals.SugarLogger.Debugf("RefreshJdPromotionItemListLockStatus len(promotionItemList):%d", len(promotionItemList))
|
||
if len(promotionItemList) > 0 {
|
||
expire := promotionItemList[0].EndAt.Add(24 * time.Hour)
|
||
for _, item := range promotionItemList {
|
||
if item.IsLock != 0 {
|
||
storeskulock.LockJdStoreSku(item.JdStoreID, item.JdSkuID, expire)
|
||
} else {
|
||
storeskulock.UnlockJdStoreSku(item.JdStoreID, item.JdSkuID)
|
||
}
|
||
}
|
||
}
|
||
return err
|
||
}
|
||
|
||
func RefreshJdPromotionLockStatus(ctx *jxcontext.Context, promotionID int) (err error) {
|
||
globals.SugarLogger.Debugf("RefreshJdPromotionLockStatus promotionID:%d", promotionID)
|
||
|
||
sql := `
|
||
SELECT t22.vendor_store_id, t3.sku_id, t32.jd_id, IF(t1.begin_at <= ? AND t1.end_at >= ? AND t1.status = ?, t3.is_lock, 0) is_lock, t1.end_at
|
||
FROM promotion t1
|
||
JOIN promotion_store t2 ON t1.id = t2.promotion_id
|
||
JOIN store_map t22 ON t2.store_id = t22.store_id AND t22.vendor_id = ? AND t22.deleted_at = ?
|
||
JOIN promotion_sku t3 ON t1.id = t3.promotion_id
|
||
JOIN sku t32 ON t3.sku_id = t32.id
|
||
WHERE t1.id = ?
|
||
`
|
||
nowDate := time.Now()
|
||
sqlParams := []interface{}{
|
||
nowDate,
|
||
nowDate,
|
||
model.PromotionStatusRemoteCreated,
|
||
model.VendorIDJD,
|
||
utils.DefaultTimeValue,
|
||
promotionID,
|
||
}
|
||
var promotionItemList []*tPromotionItemInfo
|
||
db := dao.GetDB()
|
||
// globals.SugarLogger.Debug(sql)
|
||
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
|
||
if err = dao.GetRows(db, &promotionItemList, sql, sqlParams...); err != nil {
|
||
return err
|
||
}
|
||
return RefreshJdPromotionItemListLockStatus(promotionItemList)
|
||
}
|
||
|
||
func LockPromotionSkus(ctx *jxcontext.Context, promotionID int, isLock int, skuIDs []int) (num int64, err error) {
|
||
globals.SugarLogger.Debugf("begin LockPromotionSkus promotionID:%d, isLock:%d, skuIDs:%v", promotionID, isLock, skuIDs)
|
||
if isLock != 0 {
|
||
isLock = 1
|
||
}
|
||
|
||
sql := `
|
||
UPDATE promotion_sku t1
|
||
SET t1.is_lock = ?
|
||
WHERE t1.promotion_id = ?
|
||
`
|
||
sqlParams := []interface{}{
|
||
isLock,
|
||
promotionID,
|
||
}
|
||
if len(skuIDs) > 0 {
|
||
sql += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
|
||
sqlParams = append(sqlParams, skuIDs)
|
||
}
|
||
db := dao.GetDB()
|
||
num, err = dao.ExecuteSQL(db, sql, sqlParams...)
|
||
if err == nil {
|
||
if isLock != 0 {
|
||
if err = UpdatePromotionStatusFromRemote(ctx, promotionID); err != nil {
|
||
return 0, err
|
||
}
|
||
}
|
||
RefreshJdPromotionLockStatus(ctx, promotionID)
|
||
if isLock != 0 {
|
||
// RefreshJdStoreSkuStock(promotionID, skuIDs) 这里比较耗时,实时更新意义也不大,留到定时任务自动处理
|
||
}
|
||
}
|
||
globals.SugarLogger.Debugf("end LockPromotionSkus promotionID:%d, isLock:%d, skuIDs:%v", promotionID, isLock, skuIDs)
|
||
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))
|
||
if (msg.Vendibility == 1 || !msg.Have) && storeskulock.IsJdStoreSkuLocked(msg.StationNo, msg.SkuId) {
|
||
globals.SugarLogger.Debugf("OnStoreStockMsg msg:%s", utils.Format4Output(msg, false))
|
||
db := dao.GetDB()
|
||
sku := &model.Sku{}
|
||
sku.JdID = msg.SkuId
|
||
if err = dao.GetEntity(db, sku, model.FieldJdID); err == nil {
|
||
utils.CallFuncAsync(func() {
|
||
if msg.Vendibility == 1 {
|
||
vendibility := &jdapi.StockVendibility{
|
||
OutSkuId: utils.Int2Str(sku.ID),
|
||
DoSale: true,
|
||
}
|
||
_, err = api.JdAPI.BatchUpdateVendibility("", msg.StationNo, []*jdapi.StockVendibility{
|
||
vendibility,
|
||
}, userName)
|
||
}
|
||
if !msg.Have {
|
||
stock := &jdapi.SkuStock{
|
||
OutSkuId: utils.Int2Str(sku.ID),
|
||
StockQty: model.MaxStoreSkuStockQty,
|
||
}
|
||
_, err = api.JdAPI.BatchUpdateCurrentQtys("", msg.StationNo, []*jdapi.SkuStock{
|
||
stock,
|
||
}, userName)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
return jdapi.Err2CallbackResponse(err, "")
|
||
}
|
||
|
||
func OnNewPromotionMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
|
||
return createLocalPromotionFromRemote(utils.Str2Int64(msg.BillID))
|
||
}
|
||
|
||
func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.CallbackResponse) {
|
||
utils.CallFuncAsync(func() {
|
||
result, err := api.JdAPI.QueryPromotionInfo(promotionInfoId)
|
||
if err == nil {
|
||
db := dao.GetDB()
|
||
promotion := &model.Promotion{
|
||
VendorPromotionID: utils.Int64ToStr(promotionInfoId),
|
||
}
|
||
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.PromotionLspQuerySkuResult)
|
||
// 注意,这样处理可能是有问题,我们假定的是门店信息与SKU信息的叉乘
|
||
for _, v := range result.SkuResultList {
|
||
storeIDMap[v.StationNo] = 1
|
||
skuIDMap[v.SkuID] = 1
|
||
skuMap[v.SkuID] = v
|
||
}
|
||
jdStoreIDs := make([]string, len(storeIDMap))
|
||
index := 0
|
||
for k := range storeIDMap {
|
||
jdStoreIDs[index] = utils.Int64ToStr(k)
|
||
index++
|
||
}
|
||
jdSkuIDs := jxutils.Int64Map2List(skuIDMap)
|
||
|
||
var skuList []*model.Sku
|
||
var storeMapList []*model.StoreMap
|
||
if err = dao.GetRows(db, &storeMapList, `
|
||
SELECT *
|
||
FROM store_map
|
||
WHERE vendor_id = ? AND deleted_at = ? AND vendor_store_id IN (`+
|
||
dao.GenQuestionMarks(len(jdStoreIDs))+")",
|
||
model.VendorIDJD, utils.DefaultTimeValue, jdStoreIDs); err != nil {
|
||
globals.SugarLogger.Warnf("createLocalPromotionFromRemote get storeMapList failed with error:%v", err)
|
||
return
|
||
}
|
||
if err = dao.GetRows(db, &skuList, `
|
||
SELECT *
|
||
FROM sku
|
||
WHERE jd_id IN (`+
|
||
dao.GenQuestionMarks(len(jdSkuIDs))+")",
|
||
jdSkuIDs); err != nil {
|
||
globals.SugarLogger.Warnf("createLocalPromotionFromRemote get skuList failed with error:%v", err)
|
||
return
|
||
}
|
||
jxStoreIDs := make([]int, len(storeMapList))
|
||
for k, v := range storeMapList {
|
||
jxStoreIDs[k] = v.StoreID
|
||
}
|
||
priceList := make([]*SkuPrice, len(skuList))
|
||
var skuResult *jdapi.PromotionLspQuerySkuResult
|
||
for k, v := range skuList {
|
||
skuResult = skuMap[v.JdID]
|
||
priceList[k] = &SkuPrice{
|
||
SkuID: v.ID,
|
||
PriceType: PriceTypePrice,
|
||
Price: skuResult.PromotionPrice,
|
||
LimitSkuCount: 0,
|
||
IsLock: 0,
|
||
}
|
||
}
|
||
// globals.SugarLogger.Debugf("jxStoreIDs:%s", utils.Format4Output(jxStoreIDs, false))
|
||
// globals.SugarLogger.Debugf("priceList:%s", utils.Format4Output(priceList, false))
|
||
source := strings.Trim(result.Source, "来源")
|
||
promotionParams := &PromotionParams{
|
||
Name: source + "-" + utils.Int64ToStr(result.PromotionInfoID),
|
||
Advertising: "",
|
||
Type: result.PromotionType,
|
||
BeginAt: result.BeginTime.GoTime(),
|
||
EndAt: result.EndTime.GoTime(),
|
||
StoreIDs: jxStoreIDs,
|
||
SkuPrices: priceList,
|
||
}
|
||
mapData := map[string]interface{}{
|
||
keyPromotionStatus: jd2jxPromotionStatusMap[result.PromotionState],
|
||
keyPromotionSource: source,
|
||
}
|
||
if skuResult != nil {
|
||
mapData[keyLimitDaily] = skuResult.LimitDaily
|
||
mapData[keyLimitDevice] = skuResult.LimitDevice
|
||
mapData[keyLimitPin] = skuResult.LimitPin
|
||
}
|
||
_, err = CreateJdPromotion(jxcontext.AdminCtx, model.VendorIDJD, false, true, false, utils.Int64ToStr(promotionInfoId), promotionParams, mapData)
|
||
if dao.IsDuplicateError(err) || err == ErrLimitDeviceIsInvalid {
|
||
err = nil
|
||
}
|
||
}
|
||
}
|
||
})
|
||
return jdapi.Err2CallbackResponse(nil, "")
|
||
}
|
||
|
||
func UpdatePromotionStatusFromRemote(ctx *jxcontext.Context, promotionID int) (err error) {
|
||
globals.SugarLogger.Debugf("UpdatePromotionStatusFromRemote promotionID:%d", promotionID)
|
||
|
||
db := dao.GetDB()
|
||
promotion := &model.Promotion{}
|
||
promotion.ID = promotionID
|
||
if err = dao.GetEntity(db, promotion); err != nil {
|
||
return err
|
||
}
|
||
result, err := api.JdAPI.QueryPromotionInfo(utils.Str2Int64(promotion.VendorPromotionID))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
newStatus := jd2jxPromotionStatusMap[result.PromotionState]
|
||
if newStatus != promotion.Status {
|
||
_, err = dao.UpdateEntityLogically(db, promotion, map[string]interface{}{
|
||
model.FieldStatus: newStatus,
|
||
}, ctx.GetUserName(), nil)
|
||
}
|
||
return err
|
||
}
|
||
|
||
func excelStr2Time(timeStr string) (tm time.Time, err error) {
|
||
return time.ParseInLocation("2006年1月2日15点4分5秒", timeStr, time.Local)
|
||
}
|
||
|
||
func getPromotionHander(promotionType int) JdPromotionHandler {
|
||
var promotionHandler JdPromotionHandler
|
||
if promotionType == PromotionTypeDirectDown {
|
||
promotionHandler = &JdDirectDownHandler{}
|
||
} else if promotionType == PromotionTypeLimitedTime {
|
||
promotionHandler = &JdLimitedTimeHandler{}
|
||
} else {
|
||
// panic(fmt.Sprintf("unknown promotion type:%d", promotionType))
|
||
return nil
|
||
}
|
||
if !globals.EnableJdStoreWrite {
|
||
promotionHandler = &JdNullHandler{}
|
||
}
|
||
return promotionHandler
|
||
}
|