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 }