From cf4bfb3dd8b745de01b5beeb18dc00d4c8304afb Mon Sep 17 00:00:00 2001 From: gazebo Date: Mon, 5 Nov 2018 15:08:30 +0800 Subject: [PATCH] - get and cancel promotion --- business/jxstore/promotion/jd_promotion.go | 252 ++++++++++++++++++--- business/model/promotion.go | 47 +++- controllers/promotion.go | 44 +++- globals/beegodb/beegodb.go | 2 +- routers/commentsRouter_controllers.go | 16 ++ 5 files changed, 326 insertions(+), 35 deletions(-) diff --git a/business/jxstore/promotion/jd_promotion.go b/business/jxstore/promotion/jd_promotion.go index 8d9a5288e..ce02e7866 100644 --- a/business/jxstore/promotion/jd_promotion.go +++ b/business/jxstore/promotion/jd_promotion.go @@ -32,11 +32,6 @@ const ( PromotionLimitedTimeMinPercentage = 80 ) -const ( - PromotionStatusCreated = 1 - PromotionStatusEnded = 2 -) - const ( colSkuIDIndex = 0 colSkuPriceIndex = 3 @@ -51,11 +46,16 @@ const ( MaxPromotionSkuCount = jdapi.MaxPromotionSkuCount ) +const ( + defSearchDays = 7 +) + type SkuPrice struct { - SkuID int `json:"skuID"` - PriceType int `json:"priceType"` - Price int `json:"price"` // 分,这个不是单价 - LimitSkuCount int `json:"limitSkuCount"` + SkuID int `json:"skuID"` + PriceType int `json:"priceType"` + Price int `json:"price"` // 分,这个不是单价,是这个sku的活动价 + LimitSkuCount int `json:"limitSkuCount"` + IsLock int8 `json:"isLock"` } type PromotionParams struct { @@ -73,6 +73,14 @@ type tStoreSkuBindExt struct { JdID int64 `orm:"column(jd_id)"` } +type tPromotionInfo struct { + model.Promotion + StoreIDStr string `orm:"column(store_id_str)" json:"-"` + SkuPriceStr string `orm:"column(sku_price_str)" json:"-"` + StoreIDs []int `orm:"-" json:"storeIDs"` + SkuPrices []*SkuPrice `orm:"-" json:"skuPrices"` +} + var ( ErrEmptySkus = errors.New("空sku或指定的SKU没有被门店认领,请检查") ) @@ -82,6 +90,7 @@ type JdPromotionHandler interface { 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) + CancelPromotion(infoId int64, outInfoId string) (err error) } type JdDirectDownHandler struct { @@ -99,6 +108,9 @@ func (p *JdDirectDownHandler) CreatePromotionSku(infoId int64, outInfoId string, 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 { } @@ -115,6 +127,9 @@ func (p *JdLimitedTimeHandler) CreatePromotionSku(infoId int64, outInfoId string 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 { } @@ -131,6 +146,9 @@ func (p *JdNullHandler) CreatePromotionSku(infoId int64, outInfoId string, skus 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{}) @@ -224,19 +242,19 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync bool, params } promotion := &model.Promotion{ - Name: params.Name, - VendorID: model.VendorIDJD, - Type: params.Type, - Status: PromotionStatusCreated, - SyncStatus: model.SyncFlagNewMask, - BeginAt: params.BeginAt, - EndAt: params.EndAt, + Name: params.Name, + Advertising: params.Advertising, + VendorID: model.VendorIDJD, + Type: params.Type, + Status: model.PromotionStatusLocalCreated, + BeginAt: params.BeginAt, + EndAt: params.EndAt, } dao.WrapAddIDCULDEntity(promotion, userName) // if promotion.Params, err = jxutils.SerializeData(params); err != nil { // return "", err // } - promotion.Params = string(utils.MustMarshal(params)) + // promotion.Params = string(utils.MustMarshal(params)) dao.Begin(db) defer func() { dao.Rollback(db) @@ -246,18 +264,32 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync bool, params 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)) + for _, storeID := range params.StoreIDs { + promotionStore := &model.PromotionStore{ + PromotionID: promotion.ID, + StoreID: storeID, + } + if err = dao.CreateEntity(db, promotionStore); err != nil { + return "", err + } } - if !globals.EnableStoreWrite { - promotionHandler = &JdNullHandler{} + for _, skuPrice := range params.SkuPrices { + promotionSku := &model.PromotionSku{ + PromotionID: promotion.ID, + SkuID: skuPrice.SkuID, + PriceType: skuPrice.PriceType, + Price: skuPrice.Price, + LimitSkuCount: skuPrice.LimitSkuCount, + } + if err = dao.CreateEntity(db, promotionSku); err != nil { + return "", err + } } + 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 @@ -301,6 +333,14 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync bool, params _, err = task2.GetResult(0) } else if step == 3 { err = promotionHandler.ConfirmPromotion(infoId, "") + if err == nil { + promotion.Status = model.PromotionStatusRemoteCreated + } else { + promotion.Status = model.PromotionStatusRemoteFailed + } + db := dao.GetDB() + dao.WrapUpdateULEntity(promotion, userName) + _, err = dao.UpdateEntity(db, promotion, "Status") } return nil, err }, 4) @@ -362,6 +402,166 @@ func CreateJdPromotion(ctx *jxcontext.Context, isIDJd bool, isAsync bool, params // return CreateJdPromotion(ctx, true, isAsync, promotionParams, userName) // } +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 t2.store_id), "]") store_id_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 + FROM promotion t1 + JOIN promotion_store t2 ON t1.id = t2.promotion_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"].(int)) + } + if params["name"] != nil { + sql += " AND t1.name LIKE ?" + sqlParams = append(sqlParams, "%"+params["name"].(string)+"%") + } + if params["beginAt"] != nil { + sql += " AND t1.begin_at <= ?" + sqlParams = append(sqlParams, utils.Str2Time(params["beginAt"].(string))) + } + if params["endAt"] != nil { + sql += " AND t1.end_at >= ?" + sqlParams = append(sqlParams, utils.Str2Time(params["endAt"].(string))) + } + 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 { + sql += " AND (SELECT COUNT(*) FROM promotion_store tt1 WHERE tt1.promotion_id = t1.id AND tt1.store_id = ?) > 0" + sqlParams = append(sqlParams, params["storeID"].(int)) + } + 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.StoreIDStr != "" { + if err = utils.UnmarshalUseNumber([]byte(v.StoreIDStr), &v.StoreIDs); 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{} + countInfo := &struct{ Ct int }{} + if err = dao.GetRow(db, countInfo, "SELECT FOUND_ROWS() ct"); err == nil { + pagedInfo.TotalCount = countInfo.Ct + } + pagedInfo.Data = promotionList + } + dao.Commit(db) + return pagedInfo, err +} + +func CancelJdPromotion(ctx *jxcontext.Context, promotionID int) (err error) { + db := dao.GetDB() + promotion := model.Promotion{} + promotion.ID = promotionID + if err = dao.GetEntity(db, promotion); err != nil { + return err + } + promotionHandler := getPromotionHander(promotion.Type) + if promotionHandler == nil { + return errors.New("非法的促销类型") + } + err = promotionHandler.CancelPromotion(utils.Str2Int64(promotion.VendorPromotionID), "") + 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.EnableStoreWrite { + promotionHandler = &JdNullHandler{} + } + return promotionHandler +} diff --git a/business/model/promotion.go b/business/model/promotion.go index 8e38d5399..e7260fb9c 100644 --- a/business/model/promotion.go +++ b/business/model/promotion.go @@ -4,18 +4,25 @@ import ( "time" ) +const ( + PromotionStatusLocalCreated = 0 // 本地成功创建, + PromotionStatusRemoteFailed = 1 // 远程创建失败, + PromotionStatusRemoteCreated = 2 // 远程成功创建, + PromotionStatusCanceled = 3 // 被显示取消 + PromotionStatusEnded = 4 // 已经超过活动时间,且被显示置为结束 +) + type Promotion struct { ModelIDCULD - VendorID int `orm:"column(vendor_id)"` - Name string `orm:"size(64)" json:"name"` - Type int - Status int - SyncStatus int + VendorID int `orm:"column(vendor_id)"` + Name string `orm:"size(64)" json:"name"` + Advertising string `orm:"size(255)" json:"advertising"` + Type int `json:"type"` + Status int `json:"status"` VendorPromotionID string `orm:"size(64);column(vendor_promotion_id);index" 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 { @@ -23,3 +30,31 @@ func (*Promotion) TableUnique() [][]string { []string{"Name", "VendorID", "Type", "DeletedAt"}, } } + +type PromotionStore struct { + ID int `orm:"column(id)" json:"id"` + PromotionID int `orm:"column(promotion_id)" json:"promotionID"` + StoreID int `orm:"column(store_id)" json:"storeID"` +} + +func (*PromotionStore) TableUnique() [][]string { + return [][]string{ + []string{"PromotionID", "StoreID"}, + } +} + +type PromotionSku struct { + ID int `orm:"column(id)" json:"id"` + PromotionID int `orm:"column(promotion_id)" json:"promotionID"` + SkuID int `orm:"column(sku_id)" json:"skuID"` + PriceType int `json:"priceType"` + Price int `json:"price"` // 分,活动价,这个不是单价 + LimitSkuCount int `json:"limitSkuCount"` + IsLock int8 `json:"isLock"` // 是否锁定门店商品信息 +} + +func (*PromotionSku) TableUnique() [][]string { + return [][]string{ + []string{"PromotionID", "SkuID"}, + } +} diff --git a/controllers/promotion.go b/controllers/promotion.go index 18ac45b9c..f5ab20c32 100644 --- a/controllers/promotion.go +++ b/controllers/promotion.go @@ -19,8 +19,8 @@ type PromotionController struct { // @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 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数据,价格信息列表" @@ -99,3 +99,43 @@ func (c *PromotionController) SendAdvertingByGoodsOrder() { return retVal, "", err }) } + +// @Title 查询促销 +// @Description 查询促销 +// @Param token header string true "认证token" +// @Param keyword query string false "关键字" +// @Param vendorID query int false "厂商ID,当前只支持京东:0 " +// @Param promotionID query int false "活动id" +// @Param vendorPromotionID query int false "厂商活动id" +// @Param days query int false "多少天内创建的,缺省7天" +// @Param name query string false "促销名,不完全匹配" +// @Param beginAt query string false "开始日期,包括" +// @Param endAt query string false "结束日期,包括" +// @Param type query int false "促销类型,3:直降,4:限时抢购" +// @Param storeID query int false "包含门店" +// @Param skuID query int false "包含sku" +// @Param offset query int false "活动列表起始序号(以0开始,缺省为0)" +// @Param pageSize query int false "活动列表页大小(缺省为50,-1表示全部)" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetPromotions [get] +func (c *PromotionController) GetPromotions() { + c.callGetPromotions(func(params *tPromotionGetPromotionsParams) (retVal interface{}, errCode string, err error) { + retVal, err = promotion.GetJdPromotions(params.Ctx, params.Keyword, params.MapData, params.Offset, params.PageSize) + return retVal, "", err + }) +} + +// @Title 查询促销 +// @Description 查询促销 +// @Param token header string true "认证token" +// @Param promotionID query int true "活动id" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /CancelPomotion [put] +func (c *PromotionController) CancelPomotion() { + c.callCancelPomotion(func(params *tPromotionCancelPomotionParams) (retVal interface{}, errCode string, err error) { + err = promotion.CancelJdPromotion(params.Ctx, params.PromotionID) + return retVal, "", err + }) +} diff --git a/globals/beegodb/beegodb.go b/globals/beegodb/beegodb.go index 933a4c409..78d24cc17 100644 --- a/globals/beegodb/beegodb.go +++ b/globals/beegodb/beegodb.go @@ -34,7 +34,7 @@ func Init() { orm.RegisterModel(&model.SkuCategory{}) // orm.RegisterModel(&model.DurableTask{}, &model.DurableTaskItem{}) - orm.RegisterModel(&model.Promotion{}) + orm.RegisterModel(&model.Promotion{}, &model.PromotionStore{}, &model.PromotionSku{}) } // create table orm.RunSyncdb("default", false, true) diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 11cbad9f9..8ed82ef5b 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -215,6 +215,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: "CancelPomotion", + Router: `/CancelPomotion`, + AllowHTTPMethods: []string{"put"}, + 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", @@ -223,6 +231,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: "GetPromotions", + Router: `/GetPromotions`, + AllowHTTPMethods: []string{"get"}, + 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: "SendAdvertingByGoodsOrder",