- CreatePromotion

This commit is contained in:
gazebo
2018-10-13 20:28:20 +08:00
parent a96e0f4d9f
commit b158fd09ea
8 changed files with 355 additions and 24 deletions

View File

@@ -0,0 +1,236 @@
package promotion
import (
"encoding/gob"
"errors"
"fmt"
"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/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/api"
)
const (
PromotionTypeNormal = 1
PromotionTypeDirectDown = 3
PromotionTypeLimitedTime = 4
)
const (
PriceTypePrice = 1 // 绝对价格
PriceTypePercentage = 2 // 百分比
)
const (
PromotionLimitedTimeMinPercentage = 80
)
const (
PromotionStatusCreated = 1
PromotionStatusEnded = 2
)
type SkuPrice struct {
SkuID int `json:"skuID"`
PriceType int `json:"priceType"`
Price int `json:"price"` // 分,这个不是单价
}
type PromotionParams struct {
Name string
Advertising string
Type int
BeginAt time.Time
EndAt time.Time
StoreIDs []int
SkuPrices []*SkuPrice
}
var (
ErrEmptySkus = errors.New("空sku")
)
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)
ConfirmPromotion(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.CreatePromotionRules(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) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
return api.JdAPI.ConfirmPromotionSingle(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.CreatePromotionRules(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) ConfirmPromotion(infoId int64, outInfoId string) (err error) {
return api.JdAPI.ConfirmPromotionLimitTime(infoId, outInfoId)
}
func init() {
gob.Register(&PromotionParams{})
gob.Register([]*SkuPrice{})
}
func CreateJdPromotion(isAsync bool, params *PromotionParams, userName string) (hint string, err error) {
if len(params.SkuPrices) == 0 {
return "", ErrEmptySkus
}
db := dao.GetDB()
skuIDs := make([]int, len(params.SkuPrices))
skuPriceMap := make(map[int]*SkuPrice)
for k, v := range params.SkuPrices {
skuIDs[k] = v.SkuID
skuPriceMap[v.SkuID] = v
}
sql := `
SELECT t1.*
FROM store_sku_bind t1
WHERE t1.jd_sync_status = 0 AND t1.deleted_at = ? AND t1.store_id = ? AND t1.sku_id IN (
` + dao.GenQuestionMarks(len(skuIDs)) + ")"
errMsg := ""
modifyPricesList := make(map[int][]*jdapi.SkuPriceInfo)
promotionPrices := make([]map[string]interface{}, len(params.StoreIDs)*len(params.SkuPrices))
index := 0
for _, storeID := range params.StoreIDs {
var skuBinds []*model.StoreSkuBind
if err = dao.GetRows(db, &skuBinds, sql, utils.DefaultTimeValue, storeID, skuIDs); err != nil {
return "", err
}
for _, skuBind := range skuBinds {
promotionSkuPrice := skuPriceMap[skuBind.SkuID]
if promotionSkuPrice.PriceType == PriceTypePercentage {
promotionSkuPrice.Price = skuBind.Price * promotionSkuPrice.Price / 100
}
if promotionSkuPrice.Price >= skuBind.Price {
errMsg += fmt.Sprintf("促销价大于等于原价storeID:%d, skuID:%d\n", storeID, skuBind.SkuID)
}
if errMsg == "" {
if params.Type == PromotionTypeLimitedTime {
if skuBind.Price*PromotionLimitedTimeMinPercentage/100 < promotionSkuPrice.Price {
modifyPricesList[storeID] = append(modifyPricesList[storeID], &jdapi.SkuPriceInfo{
OutSkuId: utils.Int2Str(skuBind.SkuID),
Price: promotionSkuPrice.Price*100/PromotionLimitedTimeMinPercentage + 1,
})
}
}
promotionPrices[index] = map[string]interface{}{
jdapi.KeyOutStationNo: utils.Int2Str(storeID),
jdapi.KeyOutSkuId: utils.Int2Str(skuBind.SkuID),
jdapi.KeyPromotionPrice: promotionSkuPrice.Price,
}
index++
}
}
}
promotionPrices = promotionPrices[:index]
if len(promotionPrices) == 0 {
return "", ErrEmptySkus
}
if errMsg != "" {
return "", errors.New(errMsg)
}
promotion := &model.Promotion{
Name: params.Name,
VendorID: model.VendorIDJD,
Type: params.Type,
Status: PromotionStatusCreated,
SyncStatus: model.SyncFlagNewMask,
BeginAt: params.BeginAt,
EndAt: params.EndAt,
}
dao.WrapAddIDCULDEntity(promotion, userName)
if promotion.Params, err = jxutils.SerializeData(params); err != nil {
return "", err
}
dao.Begin(db)
defer func() {
dao.Rollback(db)
}()
if err = dao.CreateEntity(db, promotion); err != nil {
return "", err
}
var promotionHandler JdPromotionHandler
if params.Type == PromotionTypeDirectDown {
promotionHandler = &JdDirectDownHandler{}
} else if params.Type == PromotionTypeLimitedTime {
promotionHandler = &JdLimitedTimeHandler{}
} else {
panic(fmt.Sprintf("unknown promotion type:%d", params.Type))
}
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)
task := tasksch.RunTask("CreateJdPromotion update sku price", false, nil, 0, 1, userName, func(batchItemList []interface{}, params ...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 _, err = api.JdAPI.UpdateVendorStationPrice(utils.Int2Str(storeID), "", modifyPrices2); err != nil {
return nil, err
}
}
return nil, nil
}, params.StoreIDs)
if _, err = task.GetResult(0); err != nil {
return "", err
}
if err = promotionHandler.CreatePromotionRules(infoId, "", 1, 1, 1, 1); err != nil {
return "", err
}
task = tasksch.RunManagedTask("CreateJdPromotion CreatePromotionSku", false, nil, 0, jdapi.MaxPromotionSkuCount, userName, func(batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
skus := make([]map[string]interface{}, len(batchItemList))
for k, v := range batchItemList {
skus[k] = v.(map[string]interface{})
}
_, err = promotionHandler.CreatePromotionSku(infoId, "", skus)
return nil, err
}, promotionPrices)
if _, err = task.GetResult(0); err != nil {
return "", err
}
err = promotionHandler.ConfirmPromotion(infoId, "")
return "", err
}

View File

@@ -1,8 +1,6 @@
package dtask
import (
"bytes"
"encoding/base64"
"encoding/gob"
"errors"
"reflect"
@@ -10,6 +8,7 @@ import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler/basesch"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
)
@@ -93,7 +92,7 @@ func (m *DurableTaskMan) AddItem(taskID, objHint string, funcName string, params
TaskIndex: d.data.TotalItem,
FinishedAt: utils.DefaultTimeValue,
}
if item.Params, err = SerializeData(params); err == nil {
if item.Params, err = jxutils.SerializeData(params); err == nil {
db := dao.GetDB()
if err = dao.CreateEntity(db, item); err == nil {
_, err = dao.UpdateEntity(db, d.data, "TotalItem")
@@ -116,7 +115,7 @@ func (m *DurableTaskMan) StartTask(taskID string) error {
objValue := reflect.ValueOf(obj)
func2Call := objValue.MethodByName(taskItem.FuncName)
params := []interface{}{}
DeSerializeData(taskItem.Params, &params)
jxutils.DeSerializeData(taskItem.Params, &params)
valueParams := make([]reflect.Value, len(params))
for k, v := range params {
@@ -147,22 +146,3 @@ func (m *DurableTaskMan) StartTask(taskID string) error {
func defObjCreator(objHint string) interface{} {
return basesch.FixedBaseScheduler.GetPurchasePlatformFromVendorID(0)
}
func SerializeData(data interface{}) (strValue string, err error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err = enc.Encode(data); err == nil {
strValue = base64.StdEncoding.EncodeToString(buf.Bytes())
return strValue, nil
}
return "", err
}
func DeSerializeData(strValue string, dataPtr interface{}) (err error) {
byteData, err := base64.StdEncoding.DecodeString(strValue)
if err == nil {
dec := gob.NewDecoder(bytes.NewReader(byteData))
return dec.Decode(dataPtr)
}
return err
}

View File

@@ -1,6 +1,11 @@
package jxutils
import "reflect"
import (
"bytes"
"encoding/base64"
"encoding/gob"
"reflect"
)
func CheckAndGetStructValue(item interface{}) *reflect.Value {
value := reflect.ValueOf(item)
@@ -20,3 +25,22 @@ func SetObjFieldByName(obj interface{}, fieldName string, value interface{}) {
refValue := CheckAndGetStructValue(obj)
refValue.FieldByName(fieldName).Set(reflect.ValueOf(value))
}
func SerializeData(data interface{}) (strValue string, err error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err = enc.Encode(data); err == nil {
strValue = base64.StdEncoding.EncodeToString(buf.Bytes())
return strValue, nil
}
return "", err
}
func DeSerializeData(strValue string, dataPtr interface{}) (err error) {
byteData, err := base64.StdEncoding.DecodeString(strValue)
if err == nil {
dec := gob.NewDecoder(bytes.NewReader(byteData))
return dec.Decode(dataPtr)
}
return err
}

View File

@@ -0,0 +1,25 @@
package model
import (
"time"
)
type Promotion struct {
ModelIDCULD
VendorID int
Name string `orm:"size(64)" json:"name"`
Type int
Status int
SyncStatus int
VendorPromotionID string `orm:"size(64)" json:"vendorPromotionID"`
BeginAt time.Time `orm:"type(datetime);index" json:"beginAt"`
EndAt time.Time `orm:"type(datetime);index" json:"endAt"`
Params string `orm:"type(text)" json:"params"`
}
func (*Promotion) TableUnique() [][]string {
return [][]string{
[]string{"Name", "VendorID", "Type", "DeletedAt"},
}
}

51
controllers/promotion.go Normal file
View File

@@ -0,0 +1,51 @@
package controllers
import (
"errors"
"github.com/astaxie/beego"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxstore/promotion"
"git.rosy.net.cn/jx-callback/business/model"
)
type PromotionController struct {
beego.Controller
}
// @Title 创建促销
// @Description 创建促销
// @Param token header string true "认证token"
// @Param vendorID formData int true "厂商ID当前只支持京东0 "
// @Param name formData string true "促销名,必须唯一(所以名子上最好带上日期)"
// @Param beginAt formData string true "开始时间"
// @Param endAt formData string true "结束时间"
// @Param type formData int true "促销类型3直降4限时抢购"
// @Param storeIDs formData string true "json数据storeID列表[1,2,3]"
// @Param skuPrices formData string true "json数据价格信息列表"
// @Param isAsync formData bool false "是否异常,缺省否(暂时只支持同步)"
// @Param advertising formData string false "广告语"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /CreatePromotion [post]
func (c *PromotionController) CreatePromotion() {
c.callCreatePromotion(func(params *tPromotionCreatePromotionParams) (retVal interface{}, errCode string, err error) {
if params.VendorID != model.VendorIDJD {
return nil, "", errors.New("当前只支持创建京东促销")
}
promotionParams := &promotion.PromotionParams{
Name: params.Name,
BeginAt: utils.Str2Time(params.BeginAt),
EndAt: utils.Str2Time(params.EndAt),
Type: params.Type,
Advertising: params.Advertising,
}
if err = utils.UnmarshalUseNumber([]byte(params.StoreIDs), &promotionParams.StoreIDs); err == nil {
if err = utils.UnmarshalUseNumber([]byte(params.SkuPrices), &promotionParams.SkuPrices); err == nil {
retVal, err = promotion.CreateJdPromotion(params.IsAsync, promotionParams, GetUserNameFromToken(params.Token))
}
}
return retVal, "", err
})
}

View File

@@ -38,6 +38,8 @@ func Init() {
orm.RegisterModel(&model.SkuCategory{})
orm.RegisterModel(&model.WeiXins{}, &model.JxBackendUser{})
// orm.RegisterModel(&model.DurableTask{}, &model.DurableTaskItem{})
orm.RegisterModel(&model.Promotion{})
}
// create table
orm.RunSyncdb("default", false, true)

View File

@@ -199,6 +199,14 @@ func init() {
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:PromotionController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:PromotionController"],
beego.ControllerComments{
Method: "CreatePromotion",
Router: `/CreatePromotion`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:SkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:SkuController"],
beego.ControllerComments{
Method: "AddCategory",

View File

@@ -61,6 +61,11 @@ func init() {
&controllers.FinancialController{},
),
),
beego.NSNamespace("/promotion",
beego.NSInclude(
&controllers.PromotionController{},
),
),
)
beego.AddNamespace(ns)