- auto create jd promotion on jd new promotion event

This commit is contained in:
gazebo
2018-11-08 16:08:35 +08:00
parent bd6cf367a0
commit 69aad912cd
5 changed files with 227 additions and 40 deletions

View File

@@ -53,6 +53,18 @@ const (
userName = "jdpromotion" userName = "jdpromotion"
) )
const (
keyPromotionSource = "promotionSource"
keyPromotionStatus = "promotionStatus"
keyLimitDevice = "limitDevice"
keyLimitPin = "limitPin"
keyLimitCount = "limitCount"
keyLimitDaily = "limitDaily"
)
const (
PromotionSourceOpenPlatform = "开放平台"
)
type SkuPrice struct { type SkuPrice struct {
SkuID int `json:"skuID"` SkuID int `json:"skuID"`
PriceType int `json:"priceType"` PriceType int `json:"priceType"`
@@ -86,7 +98,8 @@ type PromotionParams struct {
type tStoreSkuBindExt struct { type tStoreSkuBindExt struct {
model.StoreSkuBind model.StoreSkuBind
JdID int64 `orm:"column(jd_id)"` JdSkuID int64 `orm:"column(jd_id)"`
VendorStoreID string `orm:"column(vendor_store_id)"`
} }
type tPromotionInfo struct { type tPromotionInfo struct {
@@ -101,6 +114,19 @@ var (
ErrEmptySkus = errors.New("空sku或指定的SKU没有被门店认领请检查") 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 { type JdPromotionHandler interface {
CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) CreatePromotionInfos(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error)
CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error)
@@ -198,17 +224,49 @@ func Init() {
scheduleRoutine() scheduleRoutine()
} }
func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueWhenError bool, vendorPromotionID string, params *PromotionParams, userName string) (hint string, err error) { func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueWhenError bool, vendorPromotionID string, params *PromotionParams, mapData map[string]interface{}) (hint string, err error) {
if vendorPromotionID != "" && len(vendorPromotionID) != len("14863853") { if vendorPromotionID != "" && len(vendorPromotionID) != len("14863853") {
return "", fmt.Errorf("%s看起来不像是一个有效的京东活动ID请仔细检查一下", vendorPromotionID) return "", fmt.Errorf("%s看起来不像是一个有效的京东活动ID请仔细检查一下", vendorPromotionID)
} }
if len(params.SkuPrices) == 0 { if len(params.SkuPrices) == 0 {
return "", ErrEmptySkus 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() db := dao.GetDB()
modifyPricesList := make(map[int][]*jdapi.SkuPriceInfo) modifyPricesList := make(map[int][]*jdapi.SkuPriceInfo)
promotionPrices := make([]map[string]interface{}, len(params.StoreIDs)*len(params.SkuPrices)) promotionPrices := make([]map[string]interface{}, len(params.StoreIDs)*len(params.SkuPrices))
var jxStoreIDs []int var jxStoreIDs []int
promotion := &model.Promotion{
Name: params.Name,
Advertising: params.Advertising,
VendorID: model.VendorIDJD,
Type: params.Type,
Status: model.PromotionStatusLocalCreated,
BeginAt: params.BeginAt,
EndAt: params.EndAt,
CreateType: model.PromotionCreateTypeByJX,
Source: PromotionSourceOpenPlatform,
}
if vendorPromotionID == "" { if vendorPromotionID == "" {
skuIDs := make([]int, len(params.SkuPrices)) skuIDs := make([]int, len(params.SkuPrices))
@@ -217,25 +275,24 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
skuIDs[k] = v.SkuID skuIDs[k] = v.SkuID
skuPriceMap[int64(v.SkuID)] = v skuPriceMap[int64(v.SkuID)] = v
} }
sql := "" sql := `
var sqlParam []interface{} SELECT t1.*, t2.jd_id, t3.vendor_store_id
if isIDJd { FROM store_sku_bind t1
sql = ` JOIN sku t2 ON t1.sku_id = t2.id
SELECT t1.*, t2.jd_id JOIN store_map t3 ON t1.store_id = t3.store_id AND t3.vendor_id = ? AND t3.deleted_at = ?
FROM store_sku_bind t1 WHERE t1.deleted_at = ?
JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? `
JOIN store_map t3 ON t1.store_id = t3.store_id AND t3.vendor_id = ? AND t3.deleted_at = ? sqlParam := []interface{}{
WHERE t1.jd_sync_status = 0 AND t1.deleted_at = ? AND t2.jd_id IN ( model.VendorIDJD,
` + dao.GenQuestionMarks(len(skuIDs)) + ") AND t3.vendor_store_id = ?" utils.DefaultTimeValue,
sqlParam = append(sqlParam, utils.DefaultTimeValue, model.VendorIDJD, utils.DefaultTimeValue) utils.DefaultTimeValue,
} else { skuIDs,
sql = ` }
SELECT t1.*, 0 jd_id if isIDJd {
FROM store_sku_bind t1 sql += " AND t2.jd_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ") AND t3.vendor_store_id = ?"
WHERE t1.jd_sync_status = 0 AND t1.deleted_at = ? AND t1.sku_id IN ( } else {
` + dao.GenQuestionMarks(len(skuIDs)) + ") AND t1.store_id = ?" sql += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ") AND t1.store_id = ?"
} }
sqlParam = append(sqlParam, utils.DefaultTimeValue, skuIDs)
errMsg := "" errMsg := ""
index := 0 index := 0
@@ -250,7 +307,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
} }
mapSkuID := int64(skuBind.SkuID) mapSkuID := int64(skuBind.SkuID)
if isIDJd { if isIDJd {
mapSkuID = skuBind.JdID mapSkuID = skuBind.JdSkuID
} }
promotionSkuPrice := skuPriceMap[mapSkuID] promotionSkuPrice := skuPriceMap[mapSkuID]
if promotionSkuPrice.PriceType == PriceTypePercentage { if promotionSkuPrice.PriceType == PriceTypePercentage {
@@ -272,8 +329,10 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
} }
} }
promotionPrices[index] = map[string]interface{}{ promotionPrices[index] = map[string]interface{}{
jdapi.KeyOutStationNo: utils.Int2Str(skuBind.StoreID), jdapi.KeyStationNo: utils.Str2Int64(skuBind.VendorStoreID),
jdapi.KeyOutSkuId: utils.Int2Str(skuBind.SkuID), jdapi.KeySkuId: skuBind.JdSkuID,
// jdapi.KeyOutStationNo: utils.Int2Str(skuBind.StoreID),
// jdapi.KeyOutSkuId: utils.Int2Str(skuBind.SkuID),
jdapi.KeyPromotionPrice: promotionSkuPrice.Price, jdapi.KeyPromotionPrice: promotionSkuPrice.Price,
jdapi.KeyLimitSkuCount: promotionSkuPrice.LimitSkuCount, jdapi.KeyLimitSkuCount: promotionSkuPrice.LimitSkuCount,
} }
@@ -288,22 +347,17 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
if len(promotionPrices) == 0 { if len(promotionPrices) == 0 {
return "", ErrEmptySkus return "", ErrEmptySkus
} }
} } else {
promotion := &model.Promotion{
Name: params.Name,
Advertising: params.Advertising,
VendorID: model.VendorIDJD,
Type: params.Type,
Status: model.PromotionStatusLocalCreated,
BeginAt: params.BeginAt,
EndAt: params.EndAt,
CreateType: model.PromotionCreateTypeByJX,
}
if vendorPromotionID != "" {
promotion.VendorPromotionID = vendorPromotionID promotion.VendorPromotionID = vendorPromotionID
promotion.CreateType = model.PromotionCreateTypeByVendor promotion.CreateType = model.PromotionCreateTypeByVendor
if status, ok := mapData[keyPromotionStatus]; ok {
promotion.Status = status.(int)
}
if source, ok := mapData[keyPromotionSource]; ok {
promotion.Source = source.(string)
}
} }
dao.WrapAddIDCULDEntity(promotion, userName) dao.WrapAddIDCULDEntity(promotion, userName)
// if promotion.Params, err = jxutils.SerializeData(params); err != nil { // if promotion.Params, err = jxutils.SerializeData(params); err != nil {
// return "", err // return "", err
@@ -379,7 +433,7 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync, isContinueW
task.AddChild(task1).Run() task.AddChild(task1).Run()
_, err = task1.GetResult(0) _, err = task1.GetResult(0)
} else if step == 1 { } else if step == 1 {
err = promotionHandler.CreatePromotionRules(infoId, "", 1, 1, 1, 1) err = promotionHandler.CreatePromotionRules(infoId, "", limitDevice, limitPin, limitCount, limitDaily)
} else if step == 2 { } else if step == 2 {
task2 := tasksch.NewParallelTask("CreateJdPromotion CreatePromotionSku", tasksch.NewParallelConfig().SetBatchSize(MaxPromotionSkuCount).SetIsContinueWhenError(isContinueWhenError), userName, func(task *tasksch.ParallelTask, batchItemList []interface{}, params2 ...interface{}) (retVal interface{}, err error) { task2 := tasksch.NewParallelTask("CreateJdPromotion CreatePromotionSku", tasksch.NewParallelConfig().SetBatchSize(MaxPromotionSkuCount).SetIsContinueWhenError(isContinueWhenError), userName, func(task *tasksch.ParallelTask, batchItemList []interface{}, params2 ...interface{}) (retVal interface{}, err error) {
skus := make([]map[string]interface{}, len(batchItemList)) skus := make([]map[string]interface{}, len(batchItemList))
@@ -873,9 +927,93 @@ func OnStoreStockMsg(msg *jdapi.CallbackStoreStockMsg) (retVal *jdapi.CallbackRe
} }
func OnNewPromotionMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { func OnNewPromotionMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
result, err := api.JdAPI.QueryPromotionInfo(utils.Str2Int64(msg.BillID)) return createLocalPromotionFromRemote(utils.Str2Int64(msg.BillID))
}
func createLocalPromotionFromRemote(promotionInfoId int64) (retVal *jdapi.CallbackResponse) {
result, err := api.JdAPI.QueryPromotionInfo(promotionInfoId)
if err == nil { if err == nil {
globals.SugarLogger.Debug(utils.Format4Output(result, false)) 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.PromotionSkuResult)
// 注意这样处理可能是有问题我们假定的是门店信息与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 {
return jdapi.Err2CallbackResponse(err, "")
}
if err = dao.GetRows(db, &skuList, `
SELECT *
FROM sku
WHERE jd_id IN (`+
dao.GenQuestionMarks(len(jdSkuIDs))+")",
jdSkuIDs); err != nil {
return jdapi.Err2CallbackResponse(err, "")
}
jxStoreIDs := make([]int, len(storeMapList))
for k, v := range storeMapList {
jxStoreIDs[k] = v.StoreID
}
priceList := make([]*SkuPrice, len(skuList))
var skuResult *jdapi.PromotionSkuResult
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))
promotionParams := &PromotionParams{
Name: result.Source + "-" + utils.Int64ToStr(result.PromotionInfoId),
Advertising: "",
Type: result.PromotionType,
BeginAt: result.BeginTime,
EndAt: result.EndTime,
StoreIDs: jxStoreIDs,
SkuPrices: priceList,
}
mapData := map[string]interface{}{
keyPromotionStatus: jd2jxPromotionStatusMap[result.PromotionState],
keyPromotionSource: result.Source,
}
if skuResult != nil {
mapData[keyLimitDaily] = skuResult.LimitDaily
mapData[keyLimitDevice] = skuResult.LimitDevice
mapData[keyLimitPin] = skuResult.LimitPin
}
_, err = CreateJdPromotion(jxcontext.AdminCtx, false, true, false, utils.Int64ToStr(promotionInfoId), promotionParams, mapData)
if dao.IsDuplicateError(err) || err == ErrLimitDeviceIsInvalid {
err = nil
}
}
} }
return jdapi.Err2CallbackResponse(err, "") return jdapi.Err2CallbackResponse(err, "")
} }

View File

@@ -0,0 +1,23 @@
package promotion
import (
"testing"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/jx-callback/globals/beegodb"
"github.com/astaxie/beego"
)
func init() {
beego.InitBeegoBeforeTest("/Users/xujianhua/go/src/git.rosy.net.cn/jx-callback/conf/app.conf")
beego.BConfig.RunMode = "alpha" // InitBeegoBeforeTest会将runmode设置为test
globals.Init()
beegodb.Init()
api.Init()
}
func TestCreateLocalPromotionFromRemote(t *testing.T) {
t.Log(createLocalPromotionFromRemote(14510904))
}

View File

@@ -115,6 +115,16 @@ func IntMap2List(intMap map[int]int) []int {
return retVal return retVal
} }
func Int64Map2List(int64Map map[int64]int) []int64 {
retVal := make([]int64, len(int64Map))
index := 0
for k := range int64Map {
retVal[index] = k
index++
}
return retVal
}
// 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克 // 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int { func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
if skuNameUnit != "份" { if skuNameUnit != "份" {
@@ -170,3 +180,10 @@ func FormalizePageSize(pageSize int) int {
func FormalizeName(name string) string { func FormalizeName(name string) string {
return utils.TrimBlankChar(strings.Replace(strings.Replace(name, "\t", "", -1), "\"", "", -1)) return utils.TrimBlankChar(strings.Replace(strings.Replace(name, "\t", "", -1), "\"", "", -1))
} }
func Int2OneZero(value int) int {
if value != 0 {
return 1
}
return 0
}

View File

@@ -29,6 +29,11 @@ type Promotion struct {
Advertising string `orm:"size(255)" json:"advertising"` Advertising string `orm:"size(255)" json:"advertising"`
Type int `json:"type"` Type int `json:"type"`
Status int `json:"status"` Status int `json:"status"`
LimitDevice int8 `json:"limitDevice"`
LimitPint int8 `json:"limitPint"`
LimitDaily int8 `json:"limitDaily"`
LimitCount int `json:"limitCount"`
Source string `orm:"size(255)" json:"source"`
CreateType int8 `json:"createType"` CreateType int8 `json:"createType"`
VendorPromotionID string `orm:"size(64);column(vendor_promotion_id);index" json:"vendorPromotionID"` VendorPromotionID string `orm:"size(64);column(vendor_promotion_id);index" json:"vendorPromotionID"`
BeginAt time.Time `orm:"type(datetime);index" json:"beginAt"` BeginAt time.Time `orm:"type(datetime);index" json:"beginAt"`

View File

@@ -24,6 +24,10 @@ type PromotionController struct {
// @Param type formData int true "活动类型3直降4限时抢购" // @Param type formData int true "活动类型3直降4限时抢购"
// @Param storeIDs formData string true "json数据storeID列表[1,2,3]" // @Param storeIDs formData string true "json数据storeID列表[1,2,3]"
// @Param skuPrices formData string true "json数据价格信息列表" // @Param skuPrices formData string true "json数据价格信息列表"
// @Param limitDaily formData int false "是否按日0-不限1-限购 (限时抢需填)"
// @Param limitDevice formData int false "是否设备限购0-不限1-限购"
// @Param limitPin formData int false "是否账号限购0-不限1-限购"
// @Param limitCount formData int false "限购件数 0-不限如账号限购、设备限购有一个为1则限购件数必须大于0的整数"
// @Param isAsync formData bool false "是否异步,缺省否(暂时只支持同步)" // @Param isAsync formData bool false "是否异步,缺省否(暂时只支持同步)"
// @Param isContinueWhenError formData bool false "单个同步失败是否继续缺省false" // @Param isContinueWhenError formData bool false "单个同步失败是否继续缺省false"
// @Param advertising formData string false "广告语" // @Param advertising formData string false "广告语"
@@ -53,7 +57,7 @@ func (c *PromotionController) CreatePromotion() {
} }
if err = utils.UnmarshalUseNumber([]byte(params.StoreIDs), &promotionParams.StoreIDs); err == nil { if err = utils.UnmarshalUseNumber([]byte(params.StoreIDs), &promotionParams.StoreIDs); err == nil {
if err = utils.UnmarshalUseNumber([]byte(params.SkuPrices), &promotionParams.SkuPrices); err == nil { if err = utils.UnmarshalUseNumber([]byte(params.SkuPrices), &promotionParams.SkuPrices); err == nil {
retVal, err = promotion.CreateJdPromotion(params.Ctx, false, params.IsAsync, params.IsContinueWhenError, params.VendorPromotionID, promotionParams, params.Ctx.GetUserName()) retVal, err = promotion.CreateJdPromotion(params.Ctx, false, params.IsAsync, params.IsContinueWhenError, params.VendorPromotionID, promotionParams, params.MapData)
} }
} }
return retVal, "", err return retVal, "", err