Files
jx-callback/business/jxstore/act/act.go
2020-08-12 08:51:43 +08:00

1595 lines
52 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package act
import (
"fmt"
"math"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/errlist"
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"github.com/360EntSecGroup-Skylar/excelize"
"git.rosy.net.cn/jx-callback/business/jxutils/jsonerr"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"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"
)
const (
DefActSkuStock = 200 // 缺省活动库存
)
type ActOrderRuleParam struct {
SalePrice int64 `orm:"" json:"salePrice"` // 满的价格
DeductPrice int64 `orm:"" json:"deductPrice"` // 减的价格
}
type ActStoreSkuParam struct {
model.ActStoreSku
ActualActPrice int64 `json:"actualActPrice,omitempty"` // 单品级活动用,创建活动时商品的活动价格
VendorPrice int64 `json:"vendorPrice,omitempty"` // 创建活动时的平台价格
ErrMsg string `json:"errMsg,omitempty"`
}
type ActDetail struct {
model.Act2
}
type tPreCreateActVendorInfo struct {
VendorID int
VendorPrice int64 `orm:"" json:"vendorPrice"` // 单品级活动用,创建活动时商品的原始平台价
ActualActPrice int64 `orm:"" json:"actualActPrice"` // 单品级活动用,创建活动时商品的活动价格
}
type tPreCreateActStoreSku struct {
model.ActStoreSku
VendorInfoList []*tPreCreateActVendorInfo `json:"vendorInfoList"`
}
type tPreCreateActInfo struct {
model.Act
ValidVendorIDs []int
ActStoreSku []*tPreCreateActStoreSku `json:"actStoreSku"`
}
type tActRuleInfo struct {
MinDiscount int
MaxDiscount int
}
type ActManager struct {
}
type SheetParamAct struct {
StoreIDCol int
SkuIDCol int
SkuPricePercentageCol int
ActPriceCol int
EarningPriceCol int
StockCol int
ActTypeCol int
ActPricePercentageCol int
ActNameCol int
BeginTimeCol int
EndTimeCol int
}
var (
FixedActManager *ActManager
actRuleMap = map[int]map[int]*tActRuleInfo{
model.VendorIDJD: map[int]*tActRuleInfo{
model.ActSkuFake: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 500,
},
model.ActSkuDirectDown: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 99,
},
model.ActSkuSecKill: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 80,
},
},
model.VendorIDMTWM: map[int]*tActRuleInfo{
model.ActSkuFake: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 500,
},
model.ActSkuDirectDown: &tActRuleInfo{
MinDiscount: 30,
MaxDiscount: 99,
},
model.ActSkuSecKill: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 30,
},
},
model.VendorIDEBAI: map[int]*tActRuleInfo{
model.ActSkuFake: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 500,
},
model.ActSkuDirectDown: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 99,
},
},
model.VendorIDJX: map[int]*tActRuleInfo{
model.ActSkuFake: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 500,
},
model.ActSkuDirectDown: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 99,
},
model.ActSkuSecKill: &tActRuleInfo{
MinDiscount: 0,
MaxDiscount: 80,
},
model.ActSkuDiscount: &tActRuleInfo{
MinDiscount: 100,
MaxDiscount: 100,
},
},
}
)
func init() {
FixedActManager = &ActManager{}
partner.InitActManager(FixedActManager)
}
func getVendorPriceFromStoreSkuBind(bind *model.StoreSkuBind, vendorID int) (vendorPrice int) {
switch vendorID {
case model.VendorIDJD:
vendorPrice = bind.JdPrice
case model.VendorIDMTWM:
vendorPrice = bind.MtwmPrice
case model.VendorIDEBAI:
vendorPrice = bind.EbaiPrice
case model.VendorIDJX:
vendorPrice = bind.JxPrice
}
return vendorPrice
}
func ActStoreSkuParam2Model(ctx *jxcontext.Context, db *dao.DaoDB, act *model.Act, vendorIDs []int, actStoreSku []*ActStoreSkuParam) (
validVendorIDs []int, actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap, conflictActStoreSku []*model.ActStoreSku2, err error) {
wholeValidVendorMap := make(map[int]int)
if len(actStoreSku) > 0 {
storeIDMap := make(map[int]int)
skuIDMap := make(map[int]int)
storeSkuParamMap := make(map[int][]*ActStoreSkuParam)
actStoreSkuMap := make(map[int64]bool)
var wrongSkuList []*ActStoreSkuParam
for _, v := range actStoreSku {
if act.Type == model.ActSkuFake && v.EarningPrice == 0 {
wrongSkuList = append(wrongSkuList, v)
} else {
storeIDMap[v.StoreID] = 1
skuIDMap[v.SkuID] = 1
storeSkuParamMap[v.StoreID] = append(storeSkuParamMap[v.StoreID], v)
actStoreSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = true
}
}
if len(wrongSkuList) > 0 {
return nil, nil, nil, nil, jsonerr.New(wrongSkuList, model.ErrCodeJsonActEarningPriceIsZero)
}
storeIDs := jxutils.IntMap2List(storeIDMap)
skuIDs := jxutils.IntMap2List(skuIDMap)
// 判断活动是否重叠的检查,当前忽略京东平台及所有结算信息
if act.Type != model.ActSkuFake {
effectActStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, 0, vendorIDs, act.Type, storeIDs, skuIDs, act.BeginAt, act.EndAt)
if err != nil {
globals.SugarLogger.Errorf("GetEffectiveActStoreSkuInfo can not get sku promotion info for error:%v", err)
return nil, nil, nil, nil, err
}
if len(effectActStoreSkuList) > 0 {
for _, v := range effectActStoreSkuList {
if actStoreSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] {
conflictActStoreSku = append(conflictActStoreSku, v)
}
}
}
}
storeSkuList, err2 := dao.GetStoresSkusInfo(db, storeIDs, skuIDs)
if err = err2; err != nil {
return nil, nil, nil, nil, err
}
storeSkuMap := make(map[int64]*model.StoreSkuBind)
for _, v := range storeSkuList {
storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v
}
for storeID, oneStoreSkuParam := range storeSkuParamMap {
validVendorMap := make(map[int]int)
validSkuMap := make(map[int]int)
for _, vendorID := range vendorIDs {
storeDetail, err2 := dao.GetStoreDetail(db, storeID, vendorID)
if err = err2; err == nil {
if storeDetail.IsSync != 0 {
if act.Type == model.ActSkuFake ||
storeDetail.Status != model.StoreStatusDisabled && storeDetail.VendorStatus != model.StoreStatusDisabled {
for _, v := range oneStoreSkuParam {
validVendorMap[vendorID] = 1
validSkuMap[v.SkuID] = 1
v.ActID = act.ID
actSkuMap := &model.ActStoreSkuMap{
ActID: act.ID,
StoreID: storeID,
SkuID: v.SkuID,
VendorID: vendorID,
}
storeSkuInfo := storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)]
if storeSkuInfo != nil {
jxPrice := storeSkuInfo.Price
actSkuMap.VendorPrice = int64(getVendorPriceFromStoreSkuBind(storeSkuInfo, vendorID))
v.OriginalPrice = int64(jxPrice)
}
var err2 error
if act.Type != model.ActSkuFake { // 非结算,要计算实际活动价格
if storeSkuInfo == nil {
v.ErrMsg = fmt.Sprintf("门店:%d没有关注商品:%d", v.StoreID, v.SkuID)
wrongSkuList = append(wrongSkuList, v)
continue
}
if !(vendorID == model.VendorIDJX || act.Type == model.ActSkuFake) {
actSkuMap.SyncStatus = model.SyncFlagNewMask
}
if v.ActPrice != 0 {
actSkuMap.ActualActPrice = v.ActPrice
} else {
percentage := act.PricePercentage
if v.PricePercentage != 0 {
percentage = v.PricePercentage
}
actSkuMap.ActualActPrice = int64(jxutils.CaculateSkuActVendorPrice(int(actSkuMap.VendorPrice), percentage, 0))
if actSkuMap.ActualActPrice > 10 {
actSkuMap.ActualActPrice = int64(math.Floor(float64(actSkuMap.ActualActPrice)/10) * 10)
}
}
if actSkuMap.ActualActPrice <= 0 {
actSkuMap.ActualActPrice = 1
}
if err2 = checkDiscountValidation(vendorIDs, act.Type, float64(actSkuMap.ActualActPrice)*100/float64(actSkuMap.VendorPrice)); err2 != nil {
v.ErrMsg = err2.Error()
v.ActualActPrice = actSkuMap.ActualActPrice
v.VendorPrice = actSkuMap.VendorPrice
wrongSkuList = append(wrongSkuList, v)
}
}
if err2 == nil {
actSkuMap.EarningPrice = v.EarningPrice
dao.WrapAddIDCULDEntity(actSkuMap, ctx.GetUserName())
actStoreSkuMapList = append(actStoreSkuMapList, actSkuMap)
}
}
wholeValidVendorMap[vendorID] = 1
}
}
} else if !dao.IsNoRowsError(err) {
return nil, nil, nil, nil, err
} else {
err = nil
}
}
if len(wrongSkuList) == 0 {
for _, v := range oneStoreSkuParam {
if validSkuMap[v.SkuID] == 1 { // todo 这里是否需要判断
storeSku := &v.ActStoreSku
dao.WrapAddIDCULDEntity(storeSku, ctx.GetUserName())
actStoreSkuList = append(actStoreSkuList, storeSku)
}
}
}
}
if len(wrongSkuList) > 0 {
return nil, nil, nil, nil, jsonerr.New(wrongSkuList, model.ErrCodeJsonActPriceTooLarger)
}
}
return jxutils.IntMap2List(wholeValidVendorMap), actStoreSkuList, actStoreSkuMapList, conflictActStoreSku, err
}
func addActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap) (err error) {
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
storeSkuMap := make(map[int64]int)
for _, v := range actStoreSkuList {
if v.Stock == 0 {
v.Stock = DefActSkuStock
}
err = dao.CreateEntity(db, v)
if err != nil {
dao.Rollback(db)
return err
}
storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v.ID
}
for _, v := range actStoreSkuMapList {
v.BindID = storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)]
}
if len(actStoreSkuMapList) > 0 {
err = dao.CreateMultiEntities(db, actStoreSkuMapList)
if err != nil {
dao.Rollback(db)
return err
}
}
dao.Commit(db)
return err
}
func checkActUpdate(actID int, actMap map[int]*model.Act2) (err error) {
if len(actMap) == 0 {
return fmt.Errorf("活动%d不存在或已被取消", actID)
}
errList := errlist.New()
for vendorID, act := range actMap {
if vendorID == model.VendorIDEBAI && act.CreateType != model.ActCreateTypeAPI {
errList.AddErr(fmt.Errorf("饿百平台不支持修改或取消网页活动"))
}
}
return errList.GetErrListAsOne()
}
func AddActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, actStoreSku []*ActStoreSkuParam) (err error) {
actMap, err := dao.GetActVendorInfo(db, actID, nil)
if err != nil {
return err
}
if err = checkActUpdate(actID, actMap); err != nil {
return err
}
vendorIDs := partner.GetVendorIDsFromActMap(actMap)
var act *model.Act
if len(vendorIDs) > 0 {
act = &actMap[vendorIDs[0]].Act
} else {
act = &model.Act{}
act.ID = actID
if err = dao.GetEntity(db, act); err != nil {
return err
}
}
if act.Status != model.ActStatusCreated || time.Now().Sub(act.EndAt) > 0 {
return fmt.Errorf("当前活动状态:%s不能进行此操作或已过期", model.ActStatusName[act.Status])
}
// TODO conflictActStoreSku的处理
_, actStoreSkuList, actStoreSkuMapList, _, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
if err != nil {
return err
}
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil {
return err
}
if act.Type != model.ActSkuFake {
for _, act := range actMap {
if act.VendorID != model.VendorIDJX {
if _, err = dao.UpdateEntityLogically(db, partner.Act2ActMap(act),
map[string]interface{}{
model.FieldSyncStatus: act.SyncStatus | model.SyncFlagModifiedMask,
}, ctx.GetUserName(), nil); err != nil {
return err
}
}
}
}
dao.Commit(db)
return err
}
func getActRule(vendorID, actType int) (actRule *tActRuleInfo, err error) {
if actRuleMap[vendorID] != nil {
actRule = actRuleMap[vendorID][actType]
}
if actRule == nil {
err = fmt.Errorf("%s不支持%s活动", model.VendorChineseNames[vendorID], model.ActTypeName[actType])
}
return actRule, err
}
func checkDiscountValidation(vendorIDs []int, actType int, pricePercentage float64) (err error) {
pricePercentageMin := int(math.Floor(pricePercentage))
pricePercentageMax := int(math.Ceil(pricePercentage))
errList := errlist.New()
for _, vendorID := range vendorIDs {
actRule, err2 := getActRule(vendorID, actType)
if err2 == nil {
if pricePercentageMin < actRule.MinDiscount {
errList.AddErr(fmt.Errorf("%s%s活动折扣必须大于:%d", model.VendorChineseNames[vendorID], model.ActTypeName[actType], actRule.MinDiscount))
} else if pricePercentageMax > actRule.MaxDiscount {
errList.AddErr(fmt.Errorf("%s%s活动折扣必须小于:%d", model.VendorChineseNames[vendorID], model.ActTypeName[actType], actRule.MaxDiscount))
}
} else {
errList.AddErr(err2)
}
}
return errList.GetErrListAsOne()
}
func checkActValidation(act *model.Act, vendorIDs []int) (err error) {
errList := errlist.New()
if utils.IsTimeZero(act.BeginAt) || utils.IsTimeZero(act.EndAt) {
errList.AddErr(fmt.Errorf("活动开始与结束时间必须指定"))
} else if act.EndAt.Sub(act.BeginAt) < 0 {
errList.AddErr(fmt.Errorf("活动开始时间必须小于活动结束时间"))
}
vendorIDMap := make(map[int]int)
for _, vendorID := range vendorIDs {
vendorIDMap[vendorID] = 1
}
if act.Type == model.ActSkuDirectDown || act.Type == model.ActSkuSecKill {
if act.PricePercentage == 0 {
errList.AddErr(fmt.Errorf("必须指定缺省活动折扣"))
} else if err = checkDiscountValidation(vendorIDs, act.Type, float64(act.PricePercentage)); err != nil {
errList.AddErr(err)
}
} else if act.Type == model.ActSkuFake {
} else if act.Type == model.ActSkuDiscount {
if act.DiscountType == 0 {
errList.AddErr(fmt.Errorf("折扣活动必须选择折扣类型"))
}
if act.DiscountValue1 == 0 || act.DiscountValue2 == 0 {
errList.AddErr(fmt.Errorf("折扣活动必须填入两档"))
}
} else {
errList.AddErr(fmt.Errorf("当前只支持%s与%s活动", model.ActTypeName[model.ActSkuDirectDown], model.ActTypeName[model.ActSkuSecKill]))
}
err = errList.GetErrListAsOne()
return err
}
func setActDefault(act *model.Act) {
if act.LimitCount == 0 {
act.LimitCount = 1 // 缺省限购一份,如果确定不限,明确给一个很大的值
}
if act.LimitUser == 0 {
act.LimitUser = 1
}
act.Status = model.ActStatusCreated
}
func PreCreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, actRules []*ActOrderRuleParam, actStoreSku []*ActStoreSkuParam) (preCreateActInfo *tPreCreateActInfo, err error) {
if err = checkActValidation(act, vendorIDs); err != nil {
return nil, err
}
setActDefault(act)
db := dao.GetDB()
validVendorIDs, actStoreSkuList, actStoreSkuMapList, conflictActStoreSku, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
if err != nil {
return nil, err
}
if len(validVendorIDs) == 0 {
return nil, fmt.Errorf("没有一个合法平台可以创建活动")
}
if act.OverlapRule == model.OverlapRuleNormal && len(conflictActStoreSku) > 0 {
return nil, jsonerr.New(conflictActStoreSku, model.ErrCodeJsonActSkuConflict)
}
preCreateActInfo = &tPreCreateActInfo{
ValidVendorIDs: validVendorIDs,
}
storeSkuMap := make(map[int64]*tPreCreateActStoreSku)
for _, v := range actStoreSkuList {
tmp := &tPreCreateActStoreSku{
ActStoreSku: *v,
}
storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = tmp
preCreateActInfo.ActStoreSku = append(preCreateActInfo.ActStoreSku, tmp)
}
for _, v := range actStoreSkuMapList {
index := jxutils.Combine2Int(v.StoreID, v.SkuID)
storeSkuMap[index].VendorInfoList = append(storeSkuMap[index].VendorInfoList, &tPreCreateActVendorInfo{
VendorID: v.VendorID,
VendorPrice: v.VendorPrice,
ActualActPrice: v.ActualActPrice,
})
}
return preCreateActInfo, nil
}
func CreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, vendorOrgCode string, actRules []*ActOrderRuleParam, actStoreSku []*ActStoreSkuParam, isAsync bool) (hint string, err error) {
if err = checkActValidation(act, vendorIDs); err != nil {
return "", err
}
act.VendorMask = model.GetVendorMask(vendorIDs...)
if act.VendorMask&(^model.GetVendorMask(model.VendorIDJX)) != 0 &&
vendorOrgCode == "" {
return "", fmt.Errorf("必须指定平台分账号信息")
}
setActDefault(act)
db := dao.GetDB()
// TODO
// 事务应该从ActStoreSkuParam2Model之后开始
// 创建Act也应该往后放
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
dao.WrapAddIDCULDEntity(act, ctx.GetUserName())
err = dao.CreateEntity(db, act)
if err != nil {
dao.Rollback(db)
return "", err
}
validVendorIDs, actStoreSkuList, actStoreSkuMapList, conflictActStoreSku, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
if err != nil {
dao.Rollback(db)
return "", err
}
if len(validVendorIDs) == 0 {
dao.Rollback(db)
return "", fmt.Errorf("没有一个合法平台可以创建活动")
}
var neeSyncActIDs []int
if len(conflictActStoreSku) > 0 {
if act.OverlapRule == model.OverlapRuleNormal {
dao.Rollback(db)
return "", jsonerr.New(conflictActStoreSku, model.ErrCodeJsonActSkuConflict)
}
if neeSyncActIDs, err = DeleteActStoreSkuList(ctx, db, conflictActStoreSku); err != nil {
dao.Rollback(db)
return "", err
}
}
var actMapList []*model.ActMap
for _, vendorID := range validVendorIDs {
actMap := &model.ActMap{
ActID: act.ID,
VendorID: vendorID,
VendorOrgCode: vendorOrgCode,
}
if !(vendorID == model.VendorIDJX || act.Type == model.ActSkuFake) {
actMap.SyncStatus = model.SyncFlagNewMask
}
dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName())
actMapList = append(actMapList, actMap)
}
err = dao.CreateMultiEntities(db, actMapList)
if err != nil {
dao.Rollback(db)
return "", err
}
if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil {
dao.Rollback(db)
return "", err
}
dao.Commit(db)
neeSyncActIDs = append(neeSyncActIDs, act.ID)
task := tasksch.NewParallelTask(fmt.Sprintf("处理活动创建%d", act.ID), tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
actID := batchItemList[0].(int)
_, err = SyncAct(ctx, task, actID, nil, false)
return retVal, err
}, neeSyncActIDs)
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
hint = utils.Int2Str(act.ID)
} else {
_, err = task.GetResult(0)
hint = utils.Int2Str(len(neeSyncActIDs))
}
return hint, err
}
func vendorActInfo2Model(ctx *jxcontext.Context, db *dao.DaoDB, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap, err error) {
vendorStoreIDMap := make(map[string]int)
vendorSkuIDMap := make(map[string]int)
for _, v := range actStoreSku {
vendorStoreIDMap[v.VendorStoreID] = 1
vendorSkuIDMap[v.VendorSkuID] = 1
}
// globals.SugarLogger.Debug(utils.Format4Output(vendorStoreIDMap, false))
// globals.SugarLogger.Debug(utils.Format4Output(vendorSkuIDMap, false))
vendorID := act2.VendorID
storeSkuList, err2 := dao.GetStoresSkusInfoByVendorInfo(db, vendorID, jxutils.StringMap2List(vendorStoreIDMap), jxutils.StringMap2List(vendorSkuIDMap))
if err = err2; err != nil {
return nil, nil, err
}
storeSkuMap := make(map[string]*dao.StoreSkuBindWithVendorInfo)
for _, v := range storeSkuList {
storeSkuMap[v.VendorStoreID+"/"+v.VendorSkuID] = v
}
storeSkuMap2 := make(map[int64]int)
for _, v := range actStoreSku {
if storeSkuInfo := storeSkuMap[v.VendorStoreID+"/"+v.VendorSkuID]; storeSkuInfo != nil {
index := jxutils.Combine2Int(storeSkuInfo.StoreID, storeSkuInfo.SkuID)
if storeSkuMap2[index] == 0 {
storeSkuMap2[index] = 1
actSku := &model.ActStoreSku{
ActID: act2.ID,
StoreID: storeSkuInfo.StoreID,
SkuID: storeSkuInfo.SkuID,
Stock: v.Stock,
ActPrice: v.ActualActPrice,
OriginalPrice: int64(storeSkuInfo.Price),
}
dao.WrapAddIDCULDEntity(actSku, ctx.GetUserName())
actStoreSkuList = append(actStoreSkuList, actSku)
actSkuMap := &model.ActStoreSkuMap{
ActID: act2.ID,
VendorActID: act2.VendorActID,
StoreID: storeSkuInfo.StoreID,
SkuID: storeSkuInfo.SkuID,
VendorID: vendorID,
SyncStatus: 0,
VendorPrice: 0,
ActualActPrice: v.ActualActPrice,
}
dao.WrapAddIDCULDEntity(actSkuMap, ctx.GetUserName())
actStoreSkuMapList = append(actStoreSkuMapList, actSkuMap)
}
}
}
return actStoreSkuList, actStoreSkuMapList, err
}
func (a *ActManager) CreateActFromVendor(ctx *jxcontext.Context, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actID int, err error) {
globals.SugarLogger.Debugf("CreateActFromVendor vendorID:%d, vendorActID:%s", act2.VendorID, act2.VendorActID)
db := dao.GetDB()
return createActFromVendor(ctx, db, act2, actStoreSku)
}
func createActFromVendor(ctx *jxcontext.Context, db *dao.DaoDB, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actID int, err error) {
actMap := &model.ActMap{
VendorID: act2.VendorID,
VendorOrgCode: act2.VendorOrgCode,
VendorActID: act2.VendorActID,
SyncStatus: 0,
}
dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName())
if actMap.VendorActID != "" {
if err = dao.GetEntity(db, actMap, model.FieldVendorActID, model.FieldVendorID, model.FieldDeletedAt); err == nil {
return actMap.ActID, nil
} else if !dao.IsNoRowsError(err) {
return 0, err
}
err = nil
}
dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db)
panic(r)
}
}()
act := &act2.Act
act.VendorMask = model.GetVendorMask(act2.VendorID)
dao.WrapAddIDCULDEntity(act, ctx.GetUserName())
err = dao.CreateEntity(db, act)
if err != nil {
dao.Rollback(db)
return 0, err
}
actMap.ActID = act.ID
err = dao.CreateEntity(db, actMap)
if err != nil {
dao.Rollback(db)
return 0, err
}
actStoreSkuList, actStoreSkuMapList, err := vendorActInfo2Model(ctx, db, act2, actStoreSku)
if err != nil {
dao.Rollback(db)
return 0, err
}
if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil {
dao.Rollback(db)
return 0, err
}
dao.Commit(db)
return act.ID, nil
}
func (a *ActManager) IsVendorActExist(ctx *jxcontext.Context, vendorActID string, vendorID int) (isExist bool) {
db := dao.GetDB()
actMap := &model.ActMap{
VendorActID: vendorActID,
VendorID: vendorID,
}
actMap.DeletedAt = utils.DefaultTimeValue
if err := dao.GetEntity(db, actMap, "VendorActID", "VendorID", "DeletedAt"); err == nil {
isExist = true
}
return isExist
}
func QueryActs(ctx *jxcontext.Context, actID int, offset, pageSize int, syncStatus int, keyword string, vendorID int, statusList, actTypeList, createTypeList []int, storeID, skuID, cityCode int, beginAt, endAt, createdAtFrom, createdAtTo time.Time) (pagedInfo *dao.PagedActListInfo, err error) {
var skuIDs []int
if skuID > 0 {
skuIDs = []int{skuID}
}
return dao.QueryActs(dao.GetDB(), actID, offset, pageSize, syncStatus, keyword, vendorID, statusList, actTypeList, createTypeList, storeID, skuIDs, cityCode, beginAt, endAt, createdAtFrom, createdAtTo)
}
func GetActStoreSkuInfo(ctx *jxcontext.Context, actID int, vendorIDs []int, keyword string, offset, pageSize int) (retVal interface{}, err error) {
db := dao.GetDB()
totalCount, actStoreSkuList, err := dao.GetActStoreSkuVendorList(db, actID, vendorIDs, nil, nil, keyword, offset, pageSize)
if err != nil {
return nil, err
}
for _, v := range actStoreSkuList {
v.SkuName = jxutils.ComposeSkuName(v.Prefix, v.SkuNameName, v.Comment, v.Unit, v.SpecQuality, v.SpecUnit, 0, v.ExPrefix, v.ExPrefixBegin, v.ExPrefixEnd)
}
if pageSize > 0 && pageSize != model.UnlimitedPageSize {
pagedInfo := &model.PagedInfo{
TotalCount: totalCount,
Data: actStoreSkuList,
}
retVal = pagedInfo
} else {
retVal = actStoreSkuList
}
return retVal, err
}
func CancelAct(ctx *jxcontext.Context, actID int) (err error) {
db := dao.GetDB()
if _, err = DeleteActStoreSkuBind(ctx, db, actID, nil); err != nil {
return err
}
_, err = SyncAct(ctx, nil, actID, nil, false)
return err
}
// actStoreSkuParam为空不会删除act_store_sku但会删除act_store_sku_map
func DeleteActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, actStoreSkuParam []*ActStoreSkuParam) (originSyncStatus int8, err error) {
actMap, err := dao.GetActVendorInfo(db, actID, nil)
if err != nil {
return 0, err
}
if err = checkActUpdate(actID, actMap); err != nil {
return 0, err
}
actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil)
if err != nil {
return 0, err
}
act := actMap[partner.GetVendorIDsFromActMap(actMap)[0]]
if act.Status != model.ActStatusCreated || time.Now().Sub(act.EndAt) > 0 {
return 0, fmt.Errorf("当前活动状态:%s不能进行此操作或已过期", model.ActStatusName[act.Status])
}
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
actStoreSkuParamMap := make(map[int64]*ActStoreSkuParam)
for _, v := range actStoreSkuParam {
actStoreSkuParamMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v
if _, err = dao.DeleteEntityLogically(db, &model.ActStoreSku{}, nil, ctx.GetUserName(),
map[string]interface{}{
model.FieldActID: actID,
model.FieldStoreID: v.StoreID,
model.FieldSkuID: v.SkuID,
}); err != nil {
return 0, err
}
}
isNeedCancelAct := true
for vendorID, act := range actMap {
originSyncStatus |= act.SyncStatus
isDeleteAll := true
isDeleteAtLeastOne := false
if true { //actStoreSkuParam != nil {
actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuMap[vendorID])
for storeID := range actStoreSkuMap {
for _, actStoreSku := range actStoreSkuMap[storeID] {
if actStoreSkuParam == nil || actStoreSkuParamMap[jxutils.Combine2Int(actStoreSku.StoreID, actStoreSku.SkuID)] != nil {
if act.Type == model.ActSkuFake {
_, err = dao.DeleteEntityLogically(db, &model.ActStoreSkuMap{}, nil, ctx.GetUserName(),
map[string]interface{}{
model.FieldActID: actID,
model.FieldStoreID: actStoreSku.StoreID,
model.FieldSkuID: actStoreSku.SkuID,
})
} else {
_, err = dao.UpdateEntityLogically(db, partner.ActStoreSku2ActStoreSkuMap(actStoreSku),
map[string]interface{}{
model.FieldSyncStatus: actStoreSku.SyncStatus | model.SyncFlagDeletedMask,
}, ctx.GetUserName(), nil)
}
if err != nil {
return 0, err
}
isDeleteAtLeastOne = true
} else {
isNeedCancelAct = false
isDeleteAll = false
}
}
}
} else {
isDeleteAll = true
isDeleteAtLeastOne = true
}
if isDeleteAll || isDeleteAtLeastOne {
syncStatus := int8(model.SyncFlagModifiedMask)
if isDeleteAll {
syncStatus = model.SyncFlagDeletedMask
}
syncStatus |= act.SyncStatus
if act.Type != model.ActSkuFake && vendorID != model.VendorIDJX {
if _, err = dao.UpdateEntityLogically(db, partner.Act2ActMap(act),
map[string]interface{}{
model.FieldSyncStatus: syncStatus,
}, ctx.GetUserName(), nil); err != nil {
return 0, err
}
}
}
if isDeleteAll != isNeedCancelAct {
globals.SugarLogger.Warnf("deleteActStoreBind, actID:%d isDeleteAll:%t != isNeedCancelAct:%t", act.ID, isDeleteAll, isNeedCancelAct)
}
}
if isNeedCancelAct {
act := &model.Act{}
act.ID = actID
if _, err = dao.UpdateEntityLogically(db, act,
map[string]interface{}{
model.FieldStatus: model.ActStatusCanceled,
}, ctx.GetUserName(), nil); err != nil {
return 0, err
}
}
dao.Commit(db)
return originSyncStatus, err
}
func DeleteActStoreSkuList(ctx *jxcontext.Context, db *dao.DaoDB, actStoreSkuList []*model.ActStoreSku2) (actIDs []int, err error) {
if globals.IsStoreSkuAct {
actStoreSkuParamMap := make(map[int][]*ActStoreSkuParam)
for _, v := range actStoreSkuList {
param := &ActStoreSkuParam{
ActStoreSku: model.ActStoreSku{
ActID: v.ActID,
StoreID: v.StoreID,
SkuID: v.SkuID,
},
}
actStoreSkuParamMap[v.ActID] = append(actStoreSkuParamMap[v.ActID], param)
}
for actID := range actStoreSkuParamMap {
if _, err = DeleteActStoreSkuBind(ctx, db, actID, actStoreSkuParamMap[actID]); err != nil {
return nil, err
}
}
for actID := range actStoreSkuParamMap {
actIDs = append(actIDs, actID)
}
}
return actIDs, nil
}
// todo 当前逻辑要求传入活动的全部SKU信息以便低层做一些判断比如全部删除时要取消所以暂时删除storeIDs与skuIDs这两个参数
func SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITask, actID int, vendorIDs /*, storeIDs, skuIDs */ []int, isAsync bool) (hint string, err error) {
db := dao.GetDB()
actMap, err := dao.GetActVendorInfo(db, actID, vendorIDs)
if err != nil {
return "", err
}
actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil)
if err != nil {
return "", err
}
if vendorIDs == nil {
vendorIDs = partner.GetVendorIDsFromActMap(actMap)
}
if len(vendorIDs) == 0 || vendorIDs[0] == model.VendorIDJX || actMap[vendorIDs[0]].Type == model.ActSkuFake {
return "", nil
}
task := tasksch.NewParallelTask("SyncAct", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
vendorID := batchItemList[0].(int)
if handler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformActHandler); handler != nil {
tmpActMap := &model.ActMap{}
tmpActMap.ID = actMap[vendorID].MapID
if !globals.IsStoreSkuAct || actMap[vendorID].IsSpecial == 0 {
if err = handler.SyncAct(ctx, task, actMap[vendorID], nil, actStoreSkuMap[vendorID]); err == nil {
retVal = []int{1}
} else {
tmpActMap.Remark = utils.LimitUTF8StringLen(err.Error(), 1024)
}
// 保存最后一次同步错误信息
dao.UpdateEntity(db, tmpActMap, "Remark")
} else {
err = SyncSpecialAct(ctx, task, actMap[vendorID], nil, actStoreSkuMap[vendorID])
}
} else {
globals.SugarLogger.Warnf("SyncAct strange actID:%d, vendorID:%d", actID, vendorID)
}
return retVal, err
}, vendorIDs)
tasksch.HandleTask(task, parentTask, true).Run()
if !isAsync {
result, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(result))
}
} else {
hint = task.GetID()
}
return hint, err
}
func RefreshPageActs(ctx *jxcontext.Context, vendorIDs []int, createdFrom time.Time, isAsync bool) (hint string, err error) {
task := tasksch.NewParallelTask("RefreshPageActs", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
vendorID := batchItemList[0].(int)
if handler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformPageActHandler); handler != nil {
db := dao.GetDB()
actList, err2 := handler.GetPageActList(ctx, createdFrom)
globals.SugarLogger.Debug(utils.Format4Output(actList, false))
if err = err2; err != nil {
return nil, err
}
// actInfo, err2 := dao.QueryActs(db, 0, 0, -1, -1, "", vendorID, nil, []int{model.ActCreateTypeSpider},
// nil, 0, nil, 0, utils.DefaultTimeValue, utils.DefaultTimeValue, createdFrom, utils.DefaultTimeValue)
vendorActIDs, err2 := dao.GetExistVendorActIDs(db, vendorID)
if err = err2; err != nil {
return nil, err
}
localActMap := jxutils.StringList2Map(vendorActIDs)
// localActMap := make(map[string]*dao.ActVendorInfo)
// for _, v := range vendorActIDs {
// if v.VendorList[0].VendorActID != "" {
// localActMap[v.VendorList[0].VendorActID] = v
// }
// }
var needAddActList []*model.Act2
// var needUpdateActList []*model.Act
for _, v := range actList {
localAct := localActMap[v.VendorActID]
if localAct == 0 {
if v.Status == model.ActStatusCreated {
needAddActList = append(needAddActList, v)
}
}
// else if v.Status != localAct.Status {
// localAct.Status = v.Status
// needUpdateActList = append(needUpdateActList, &localAct.Act)
// }
}
subTask := tasksch.NewParallelTask("RefreshPageAct Sub", nil, ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
act2 := batchItemList[0].(*model.Act2)
actStoreSkuList, err := handler.GetPageActSkuList(ctx, act2.VendorActID)
if err == nil {
retVal = actStoreSkuList
}
return retVal, err
}, needAddActList)
tasksch.AddChild(task, subTask).Run()
skuList, err2 := subTask.GetResult(0)
if err = err2; err != nil {
return nil, err
}
needAddActSkuMap := make(map[string][]*model.ActStoreSku2)
for _, v := range skuList {
actStoreSku := v.(*model.ActStoreSku2)
needAddActSkuMap[actStoreSku.VendorActID] = append(needAddActSkuMap[actStoreSku.VendorActID], actStoreSku)
}
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
for _, v := range needAddActList {
if len(needAddActSkuMap[v.VendorActID]) > 0 {
if _, err = createActFromVendor(ctx, db, v, needAddActSkuMap[v.VendorActID]); err != nil {
return nil, err
}
}
}
// for _, v := range needUpdateActList {
// if _, err = dao.UpdateEntity(db, v, model.FieldStatus); err != nil {
// return nil, err
// }
// }
dao.Commit(db)
}
return retVal, err
}, vendorIDs)
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
hint = "1"
_, err = task.GetResult(0)
} else {
hint = task.GetID()
}
return hint, err
}
func DeleteStoresFromAct(ctx *jxcontext.Context, vendorID int, actTypes, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB()
task := tasksch.NewParallelTask("将SKU从所有活动中删除", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeID := batchItemList[0].(int)
acts, _ := dao.QueryActs(db, 0, 0, 50, 0, "", vendorID, []int{1}, actTypes, nil, storeID, nil, 0, utils.ZeroTimeValue, utils.ZeroTimeValue, time.Now().AddDate(0, -3, 0), time.Now())
for _, v := range acts.Data {
var actStoreSkuParam []*ActStoreSkuParam
_, actStoreSkus, _ := dao.GetActStoreSkuVendorList(db, v.ID, []int{vendorID}, nil, nil, "", 0, 99999)
for _, actStoreSku := range actStoreSkus {
if actStoreSku.StoreID == storeID {
aa := &ActStoreSkuParam{
ActStoreSku: model.ActStoreSku{
StoreID: storeID,
SkuID: actStoreSku.SkuID,
ActID: v.ID,
},
}
actStoreSkuParam = append(actStoreSkuParam, aa)
}
}
_, err = DeleteActStoreSkuBind(ctx, db, v.ID, actStoreSkuParam)
if err == nil {
_, err = SyncAct(ctx, nil, v.ID, nil, true)
}
}
return retVal, err
}, storeIDs)
tasksch.HandleTask(task, nil, true).Run()
if isAsync {
hint = task.GetID()
} else {
resultList, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(resultList))
}
}
return hint, err
}
func DeleteSkusFromAct(ctx *jxcontext.Context, vendorID int, actTypes, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB()
actMap := make(map[int]*model.Act)
pagedInfo, err2 := dao.QueryActs(db, 0, 0, -1, -1, "", vendorID, []int{model.ActStatusCreated}, actTypes, nil, 0, skuIDs, 0,
utils.DefaultTimeValue, utils.DefaultTimeValue, time.Now().Add(-24*30*3*time.Hour), utils.DefaultTimeValue)
if err = err2; err != nil {
return "", err
}
for _, v := range pagedInfo.Data {
actMap[v.Act.ID] = &v.Act
}
if len(actMap) == 0 {
return "", nil
}
skuIDMap := jxutils.IntList2Map(skuIDs)
var actIDList []int
for k := range actMap {
actIDList = append(actIDList, k)
}
task := tasksch.NewParallelTask("将SKU从所有活动中删除", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
actID := batchItemList[0].(int)
_, actStoreSkuList, err := dao.GetActStoreSkuVendorList(db, actID, []int{-1}, nil, skuIDs, "", 0, -1)
if err == nil {
// db := dao.GetDB()
var deleteList []*ActStoreSkuParam
for _, v := range actStoreSkuList {
if skuIDMap[v.SkuID] == 1 {
deleteList = append(deleteList, &ActStoreSkuParam{
ActStoreSku: model.ActStoreSku{
StoreID: v.StoreID,
SkuID: v.SkuID,
},
})
}
}
if len(deleteList) > 0 {
// globals.SugarLogger.Debug(utils.Format4Output(deleteList, false))
originSyncStatus, err2 := DeleteActStoreSkuBind(ctx, db, actID, deleteList)
if err = err2; err == nil && originSyncStatus == 0 {
if _, err = SyncAct(ctx, task, actID, nil, false); err == nil {
retVal = deleteList
}
}
}
}
return retVal, err
}, actIDList)
tasksch.HandleTask(task, nil, true).Run()
if isAsync {
hint = task.GetID()
} else {
resultList, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(len(resultList))
}
}
return hint, err
}
func ForceUpdateVendorPrice(ctx *jxcontext.Context, vendorID int, actType int, storeSkuList []*ActStoreSkuParam, lockTime time.Time, isAsync bool) (hint string, err error) {
var wrongSkuList []*ActStoreSkuParam
var storeSkuBindList []*model.StoreSkuBind
actRule, _ := getActRule(vendorID, actType)
db := dao.GetDB()
errList := errlist.New()
for _, v := range storeSkuList {
storeSkuBind := &model.StoreSkuBind{
StoreID: v.StoreID,
SkuID: v.SkuID,
}
storeSkuBind.DeletedAt = utils.DefaultTimeValue
if err = dao.GetEntity(db, storeSkuBind, model.FieldStoreID, model.FieldSkuID, model.FieldDeletedAt); err == nil {
vendorPrice := int(v.VendorPrice)
if vendorPrice != 0 {
if err2 := checkDiscountValidation([]int{vendorID}, actType, float64(v.ActPrice)*100/float64(v.VendorPrice)); err2 != nil {
v.ErrMsg = err2.Error()
v.ActualActPrice = v.ActPrice
wrongSkuList = append(wrongSkuList, v)
storeSkuBind = nil
}
} else {
vendorPrice = dao.GetStoreSkuBindVendorPrice(storeSkuBind, vendorID)
if checkDiscountValidation([]int{vendorID}, actType, float64(v.ActPrice)*100/float64(vendorPrice)) != nil {
vendorPrice = int(v.ActPrice)*100/actRule.MaxDiscount + 10
} else {
storeSkuBind = nil
}
}
if storeSkuBind != nil {
dao.SetStoreSkuBindVendorPrice(storeSkuBind, vendorID, vendorPrice, lockTime)
if vendorID != model.VendorIDJX {
dao.SetStoreSkuBindSyncStatus(storeSkuBind, vendorID, dao.GetStoreSkuBindSyncStatus(storeSkuBind, vendorID)|model.SyncFlagPriceMask)
}
storeSkuBind.LastOperator = ctx.GetUserName()
storeSkuBindList = append(storeSkuBindList, storeSkuBind)
}
} else if dao.IsNoRowsError(err) { // 忽略不存在错
err = nil
} else {
errList.AddErr(fmt.Errorf("获取门店:%d商品:%d出错:%s", v.StoreID, v.SkuID, err))
}
}
if err = errList.GetErrListAsOne(); err != nil {
return "", err
}
if len(wrongSkuList) > 0 {
return "", jsonerr.New(wrongSkuList, model.ErrCodeJsonActPriceTooLarger)
}
storeVendorIDMap := make(map[int]string)
storeSkuIDMap := make(map[int][]int)
task := tasksch.NewSeqTask2(fmt.Sprintf("强制刷新门店商品%s平台价", model.VendorChineseNames[vendorID]), ctx, false,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
errList := errlist.New()
for _, storeSkuBind := range storeSkuBindList {
if _, err2 := dao.UpdateEntity(db, storeSkuBind); err2 == nil {
if storeVendorIDMap[storeSkuBind.StoreID] == "" {
if storeDetail, err2 := dao.GetStoreDetail(db, storeSkuBind.StoreID, vendorID); err2 == nil {
storeVendorIDMap[storeSkuBind.StoreID] = storeDetail.VendorStoreID
}
}
storeSkuIDMap[storeSkuBind.StoreID] = append(storeSkuIDMap[storeSkuBind.StoreID], storeSkuBind.SkuID)
errList.AddErr(err2)
}
}
err = errList.GetErrListAsOne()
case 1:
if vendorID != model.VendorIDJX && len(storeVendorIDMap) > 0 {
var storeIDs []int
for storeID := range storeVendorIDMap {
storeIDs = append(storeIDs, storeID)
}
subTask := tasksch.NewParallelTask("同步平台价格", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeID := batchItemList[0].(int)
_, err = cms.SyncStoreSkuNew2(ctx, task, 0 /*model.SyncFlagPriceMask*/, vendorID, storeID, storeVendorIDMap[storeID], nil, storeSkuIDMap[storeID], nil, true, false, true)
return retVal, err
}, storeIDs)
tasksch.HandleTask(subTask, task, true).Run()
_, err = subTask.GetResult(0)
}
}
return result, err
}, 2)
tasksch.HandleTask(task, nil, true).Run()
if isAsync {
hint = task.GetID()
} else {
_, err2 := task.GetResult(0)
if err = err2; err == nil {
hint = utils.Int2Str(1)
}
}
return hint, err
}
func SyncActStoreSku2StoreSkuAct(ctx *jxcontext.Context, db *dao.DaoDB, parentTask tasksch.ITask, act *model.Act2) (err error) {
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
// 创建时会直接覆盖后续修改时会判断是否是相同的act_id
// MySql的SET语句顺序是有关系的如果当前SET语句中有判断且依赖于前一条SET过的字段是用的是SET后的值的
sql := `
UPDATE store_sku_act t1
JOIN act_store_sku_map t2 ON t2.store_id = t1.store_id AND t2.sku_id = t1.sku_id AND t2.vendor_id = t1.vendor_id AND t2.act_id = ? AND t2.sync_status <> 0
JOIN act_store_sku t3 ON t3.store_id = t1.store_id AND t3.sku_id = t1.sku_id AND t3.act_id = t2.act_id
JOIN act_map t4 ON t4.act_id = t2.act_id AND t4.vendor_id = t2.vendor_id
SET
t1.sync_status = IF(t1.act_percentage = IF(t2.sync_status & ? = 0 AND t3.deleted_at = ?, t3.price_percentage, 0), 0, ?),
t1.act_percentage = IF(t2.sync_status & ? = 0 AND t3.deleted_at = ?, t3.price_percentage, 0),
t1.updated_at = NOW(),
t1.last_operator = ?,
t1.hint_act_id = t2.act_id
WHERE t1.vendor_id = ? AND (t4.sync_status & ? <> 0 OR t1.hint_act_id = t2.act_id)
`
sqlParams := []interface{}{
act.ID,
model.SyncFlagDeletedMask, utils.DefaultTimeValue, model.SyncFlagModifiedMask,
model.SyncFlagDeletedMask, utils.DefaultTimeValue,
ctx.GetUserName(),
act.VendorID, model.SyncFlagNewMask,
}
_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
if err != nil {
return err
}
sql = `
INSERT INTO store_sku_act(id, created_at, updated_at, last_operator, store_id, sku_id, vendor_id, act_percentage, sync_status, hint_act_id)
SELECT 0, NOW(), NOW(), ?, t2.store_id, t2.sku_id, t2.vendor_id, t3.price_percentage, ?, t2.act_id
FROM act_store_sku_map t2
JOIN act_store_sku t3 ON t3.store_id = t2.store_id AND t3.sku_id = t2.sku_id AND t3.act_id = t2.act_id
LEFT JOIN store_sku_act t1 ON t2.store_id = t1.store_id AND t2.sku_id = t1.sku_id AND t2.vendor_id = t1.vendor_id
WHERE t2.vendor_id = ? AND t2.act_id = ? AND t2.sync_status <> 0 AND t1.id IS NULL
`
sqlParams = []interface{}{
ctx.GetUserName(), model.SyncFlagModifiedMask,
act.VendorID, act.ID,
}
_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
if err != nil {
return err
}
sql = `
UPDATE act_store_sku_map t2
SET
t2.deleted_at = IF(t2.sync_status & ? = 0, t2.deleted_at, NOW()),
t2.sync_status = 0
WHERE t2.vendor_id = ? AND t2.act_id = ?
`
sqlParams = []interface{}{
model.SyncFlagDeletedMask,
act.VendorID, act.ID,
}
_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
if err != nil {
return err
}
sql = `
UPDATE act_map t4
SET
t4.sync_status = 0
WHERE t4.vendor_id = ? AND t4.act_id = ?
`
sqlParams = []interface{}{
act.VendorID, act.ID,
}
_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
if err != nil {
return err
}
dao.Commit(db)
return err
}
func SyncSpecialAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreSkuList []*model.ActStoreSku2) (err error) {
db := dao.GetDB()
if err = SyncActStoreSku2StoreSkuAct(ctx, db, parentTask, act); err == nil {
err = cms.FullSyncStoreSkuBindAct(ctx, parentTask, act.ID, nil, nil)
}
return err
}
func CreateActByExcel(ctx *jxcontext.Context, files string, vendorID int, vendorOrgCode string, mixType int, isFocus, isSync, isAsync, isContinueWhenError bool) (hint string, err error) {
// if len(files) == 0 {
// return "", errors.New("没有文件上传!")
// }
// fileHeader := files[0]
// file, err := fileHeader.Open()
hint, err = CreateActByExcelBin(ctx, files, vendorID, vendorOrgCode, mixType, isFocus, isSync, isAsync, isContinueWhenError)
// file.Close()
return hint, err
}
func CreateActByExcelBin(ctx *jxcontext.Context, reader string, vendorID int, vendorOrgCode string, mixType int, isFocus, isSync, isAsync, isContinueWhenError bool) (hint string, err error) {
sheetParam := &SheetParamAct{
StoreIDCol: 0,
SkuIDCol: 2,
SkuPricePercentageCol: 4,
ActPriceCol: 5,
EarningPriceCol: 6,
StockCol: 7,
ActTypeCol: 8,
ActPricePercentageCol: 9,
ActNameCol: 10,
BeginTimeCol: 11,
EndTimeCol: 12,
}
taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
var (
actObj = &model.Act{}
actStoreSkuList []*ActStoreSkuParam
actStoreSkusResult []*ActStoreSkuParam
storeIDs []int
)
switch step {
case 0:
//读取excel文件
xlsx, err := excelize.OpenFile("111.xlsx")
// xlsx, err := excelize.OpenReader(reader)
if err != nil {
return result, err
}
rows, _ := xlsx.GetRows(xlsx.GetSheetName(1))
for rowNum, row := range rows {
if rowNum < 1 {
continue
}
loadExcelForCreateAct(rowNum, mixType, row, sheetParam, actObj, actStoreSkuList, storeIDs)
}
case 1:
//叉乘
if mixType == 2 {
for _, v := range storeIDs {
for _, vv := range actStoreSkuList {
actStoreSkus := &ActStoreSkuParam{}
actStoreSkus = vv
actStoreSkus.StoreID = v
actStoreSkusResult = append(actStoreSkusResult, actStoreSkus)
}
}
_, err = CreateAct(ctx, actObj, []int{vendorID}, vendorOrgCode, nil, actStoreSkusResult, isAsync)
} else if mixType == 1 {
_, err = CreateAct(ctx, actObj, []int{vendorID}, vendorOrgCode, nil, actStoreSkuList, isAsync)
}
}
return result, err
}
taskSeq := tasksch.NewSeqTask2("根据Excel创建活动-序列任务", ctx, isContinueWhenError, taskSeqFunc, 5)
tasksch.HandleTask(taskSeq, nil, true).Run()
if !isAsync {
_, err = taskSeq.GetResult(0)
hint = "1"
} else {
hint = taskSeq.GetID()
}
return hint, err
}
func loadExcelForCreateAct(rowNum, mixType int, row []string, sheetParam *SheetParamAct, actObj *model.Act, actStoreSkuList []*ActStoreSkuParam, storeIDs []int) {
actStoreSku := &ActStoreSkuParam{}
for k, cell := range row {
if rowNum == 1 {
if k == sheetParam.ActTypeCol {
var actType int
for k, v := range model.ActTypeName {
if cell == v {
actType = k
break
}
}
actObj.Type = actType
}
if k == sheetParam.ActPricePercentageCol {
actObj.PricePercentage = int(utils.Str2Float64(cell) * 10)
}
if k == sheetParam.ActNameCol {
actObj.Name = cell
}
if k == sheetParam.BeginTimeCol {
begin, err := time.Parse("2006年1月2日 15:04:05", cell)
if err != nil {
begin = utils.ZeroTimeValue
}
actObj.BeginAt = begin
}
if k == sheetParam.EndTimeCol {
end, err := time.Parse("2006年1月2日 15:04:05", cell)
if err != nil {
end = utils.ZeroTimeValue
}
actObj.EndAt = end
}
}
if k == sheetParam.StoreIDCol {
//一行一行
if mixType == 1 {
actStoreSku.StoreID = int(utils.Str2Int64(cell))
} else if mixType == 2 {
storeIDs = append(storeIDs, int(utils.Str2Int64(cell)))
}
}
if k == sheetParam.SkuIDCol {
actStoreSku.SkuID = int(utils.Str2Int64(cell))
}
if k == sheetParam.SkuPricePercentageCol {
actStoreSku.PricePercentage = int(utils.Str2Float64WithDefault(cell, 0) * 10)
}
if k == sheetParam.ActPriceCol {
actStoreSku.ActPrice = int64(utils.Str2Float64(cell) * 100)
}
if k == sheetParam.EarningPriceCol {
actStoreSku.EarningPrice = int64(utils.Str2Float64(cell) * 100)
}
if k == sheetParam.StockCol {
actStoreSku.Stock = int(utils.Str2Int64(cell))
}
}
actStoreSkuList = append(actStoreSkuList, actStoreSku)
}
func ChangeJxPriceByDiscountAct(ctx *jxcontext.Context) {
var (
db = dao.GetDB()
pageSize = 9999
)
page, err := dao.QueryActs(db, 0, 0, pageSize, -1, "", model.VendorIDJX, []int{model.YES}, []int{model.ActSkuDiscount}, nil, 0, nil, 0, utils.ZeroTimeValue, utils.ZeroTimeValue, time.Now().AddDate(0, -3, 0), time.Now())
if err != nil {
return
}
if len(page.Data) == 0 {
return
}
for _, act := range page.Data {
_, actStoreSkus, _ := dao.GetActStoreSkuVendorList(db, act.ID, nil, nil, nil, "", 0, pageSize)
if len(actStoreSkus) == 0 {
continue
}
for _, actStoreSku := range actStoreSkus {
storeSkus, _ := dao.GetStoresSkusInfo(db, []int{actStoreSku.StoreID}, []int{actStoreSku.SkuID})
if len(storeSkus) == 0 {
continue
}
var (
storeSku = storeSkus[0]
shouldStockOut = utils.Float64TwoInt64(math.Ceil(float64(storeSku.Stock) / float64(60))) //每个时间点应出货 = 总库存/60 = N
actualStockOut int64 //每个时间点实际出货 = C
pricePercentage float64 //每次涨跌值为 原价的 2%即原价100每个时间点降价为2涨价也为2
actualPricePercentage float64
minJxPrice int64
)
if storeSku.Stock == 0 {
continue
}
actualStockOut, err = dao.GetOrderStoreSkusCount(db, actStoreSku.StoreID, actStoreSku.SkuID, time.Now().Add(-time.Minute*10), time.Now())
dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db)
if r != nil {
panic(r)
}
}
}()
//第一档时间内
if (time.Now().Hour() >= 10 && time.Now().Hour() < 20) || (time.Now().Hour() == 20 && time.Now().Minute() < 1) {
pricePercentage = 0.02
if actStoreSku.TrendType == model.TrendTypeUp {
actualPricePercentage = float64(actStoreSku.OriginalPrice) * (float64(1) + pricePercentage)
} else if actStoreSku.TrendType == model.TrendTypeDown {
actualPricePercentage = float64(actStoreSku.OriginalPrice) * (float64(1) - pricePercentage)
} else {
actualPricePercentage = 1
}
if utils.Float64TwoInt64(float64(storeSku.JxPrice)*actualPricePercentage) >= actStoreSku.OriginalPrice {
storeSku.JxPrice = int(actStoreSku.OriginalPrice)
}
//判断活动的折扣类型是最低价还是最低折扣
if act.DiscountType == model.ActDiscountTypePrice {
minJxPrice = int64(act.DiscountValue1)
} else if act.DiscountType == model.ActDiscountTypePercentage {
minJxPrice = actStoreSku.OriginalPrice * int64(act.DiscountValue1) / 100
}
if utils.Float64TwoInt64(float64(storeSku.JxPrice)*actualPricePercentage) <= minJxPrice {
storeSku.JxPrice = int(minJxPrice)
}
if _, err = dao.UpdateEntity(db, storeSku, "JxPrice"); err != nil {
dao.Rollback(db)
}
//C >= 2N 涨价趋势,最高涨价到无折扣
if actualStockOut >= 2*shouldStockOut {
actStoreSku.TrendType = model.TrendTypeUp
} else if actualStockOut < shouldStockOut/2 { //C <= N/2 降价趋势,最低降价到设置到最低折扣
actStoreSku.TrendType = model.TrendTypeDown
} else {
actStoreSku.TrendType = model.TrendTypeNothing
}
if _, err = dao.UpdateEntity(db, actStoreSku, "TrendType"); err != nil {
dao.Rollback(db)
}
} else { //第二档时间内
pricePercentage = 0.1
}
dao.Commit(db)
}
}
}