Files
jx-callback/business/jxstore/act/act.go
2020-01-03 09:00:43 +08:00

1137 lines
37 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"
"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 {
}
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,
},
},
}
)
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, 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)
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)
}
}
if len(wrongSkuList) > 0 {
return nil, nil, nil, jsonerr.New(wrongSkuList, model.ErrCodeJsonActEarningPriceIsZero)
}
storeIDs := jxutils.IntMap2List(storeIDMap)
skuIDs := jxutils.IntMap2List(skuIDMap)
// 判断活动是否重叠的检查,当前忽略京东平台及所有结算信息
if !(len(vendorIDs) == 1 && vendorIDs[0] == model.VendorIDJD || 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, err
}
if len(effectActStoreSkuList) > 0 {
return nil, nil, nil, jsonerr.New(effectActStoreSkuList, model.ErrCodeJsonActSkuConflict)
}
}
storeSkuList, err2 := dao.GetStoresSkusInfo(db, storeIDs, skuIDs)
if err = err2; err != nil {
return 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.CaculateSkuVendorPrice(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 {
dao.WrapAddIDCULDEntity(actSkuMap, ctx.GetUserName())
actStoreSkuMapList = append(actStoreSkuMapList, actSkuMap)
}
}
wholeValidVendorMap[vendorID] = 1
}
}
} else if !dao.IsNoRowsError(err) {
return 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, jsonerr.New(wrongSkuList, model.ErrCodeJsonActPriceTooLarger)
}
}
return jxutils.IntMap2List(wholeValidVendorMap), actStoreSkuList, actStoreSkuMapList, 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])
}
_, 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 {
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, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
if err != nil {
return nil, err
}
if len(validVendorIDs) == 0 {
dao.Rollback(db)
return nil, fmt.Errorf("没有一个合法平台可以创建活动")
}
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()
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, 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 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)
}
if len(actMapList) > 0 {
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)
hint, err = SyncAct(ctx, nil, act.ID, nil, isAsync)
if !isAsync {
hint = utils.Int2Str(act.ID)
}
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) {
return dao.QueryActs(dao.GetDB(), actID, offset, pageSize, syncStatus, keyword, vendorID, statusList, actTypeList, createTypeList, storeID, skuID, 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
}
// 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 err = handler.SyncAct(ctx, nil, actMap[vendorID], nil, actStoreSkuMap[vendorID]); err == nil {
retVal = []int{1}
} else {
tmpActMap.Remark = utils.LimitUTF8StringLen(err.Error(), 1024)
}
// 保存最后一次同步错误信息
dao.UpdateEntity(db, tmpActMap, "Remark")
} 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, 0, 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 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)
for _, skuID := range skuIDs {
pagedInfo, err2 := dao.QueryActs(db, 0, 0, -1, -1, "", vendorID, []int{model.ActStatusCreated}, actTypes, nil, 0, skuID, 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, 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
}