1509 lines
49 KiB
Go
1509 lines
49 KiB
Go
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: 0,
|
||
MaxDiscount: 0,
|
||
},
|
||
},
|
||
}
|
||
)
|
||
|
||
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
|
||
}
|
||
fmt.Println("actSkuMap", utils.Format4Output(actSkuMap, false))
|
||
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)
|
||
}
|