Files
jx-callback/business/partner/purchase/jd/act.go
2022-10-24 11:22:38 +08:00

425 lines
15 KiB
Go

package jd
import (
"fmt"
"time"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/errlist"
"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"
)
const (
actMapDuration = 2 * time.Hour
)
type LogicUpdateInfo struct {
Item interface{}
KVs map[string]interface{}
Condition map[string]interface{}
}
var (
jdSkuActStatusMap = map[int]int{
jdapi.PromotionStateNotConfirm: model.ActStatusNA,
jdapi.PromotionStateConfirmed: model.ActStatusCreated,
jdapi.PromotionStateCanceled: model.ActStatusCanceled,
jdapi.PromotionStateEnded: model.ActStatusEnded,
}
actMap jxutils.SyncMapWithTimeout
)
// 是否按单一门店商品维度创建活动
func isCreateTypeSingleStoreSku() bool {
return false
// return !globals.IsProductEnv()
}
func splitPromotionSku(skus []*jdapi.PromotionSku, maxCount int) (skusList [][]*jdapi.PromotionSku) {
for {
skusLen := len(skus)
if skusLen <= maxCount {
skusList = append(skusList, skus)
break
}
skusList = append(skusList, skus[:maxCount])
skus = skus[maxCount:]
}
return skusList
}
func jdSkuActType2Jx(actType int) int {
if actType == jdapi.PromotionTypeDirectDown {
return model.ActSkuDirectDown
} else if actType == jdapi.PromotionTypeSeckill || actType == jdapi.PromotionTypeLimitedTime {
return model.ActSkuSecKill
}
return 0
}
func jdSkuActStatus2Jx(jdActState int) int {
return jdSkuActStatusMap[jdActState]
}
func CreatePromotionInfos(vendorOrgCode string, promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
infoId, err = getAPI(vendorOrgCode).CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising, traceId)
} else {
infoId, err = getAPI(vendorOrgCode).CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising, traceId)
}
} else {
infoId = jxutils.GenFakeID()
}
if err == nil {
actMap.StoreWithTimeout(infoId, 1, actMapDuration)
}
return infoId, err
}
func CreatePromotionRules(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
err = getAPI(vendorOrgCode).CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
} else {
err = getAPI(vendorOrgCode).CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
}
}
return err
}
func CreatePromotionSku(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) {
if globals.EnableJdStoreWrite {
for _, batchSkus := range splitPromotionSku(skus, jdapi.MaxPromotionSkuCount) {
var tmpSkusResult []*jdapi.PromotionSku
var tmpErr error
if promotionType == model.ActSkuDirectDown {
tmpSkusResult, tmpErr = getAPI(vendorOrgCode).CreatePromotionSkuSingle(infoId, outInfoId, batchSkus, traceId)
} else {
tmpSkusResult, tmpErr = getAPI(vendorOrgCode).CreatePromotionSkuLimitTime(infoId, outInfoId, batchSkus, traceId)
}
if err = tmpErr; err != nil {
break
}
skusResult = append(skusResult, tmpSkusResult...)
}
}
return skusResult, err
}
func CancelPromotionSku(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (err error) {
if globals.EnableJdStoreWrite {
for _, batchSkus := range splitPromotionSku(skus, jdapi.MaxPromotionSkuCount) {
var tmpErr error
if promotionType == model.ActSkuDirectDown {
tmpErr = getAPI(vendorOrgCode).CancelPromotionSkuSingle(infoId, outInfoId, batchSkus, traceId)
} else {
tmpErr = getAPI(vendorOrgCode).CancelPromotionSkuLimitTime(infoId, outInfoId, batchSkus, traceId)
}
if err = tmpErr; err != nil {
break
}
}
}
return err
}
func ConfirmPromotion(vendorOrgCode string, promotionType int, infoId int64, outInfoId, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
return getAPI(vendorOrgCode).ConfirmPromotionSingle(infoId, outInfoId, traceId)
} else {
return getAPI(vendorOrgCode).ConfirmPromotionLimitTime(infoId, outInfoId, traceId)
}
}
return err
}
func CancelPromotion(vendorOrgCode string, promotionType int, infoId int64, outInfoId, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
err = getAPI(vendorOrgCode).CancelPromotionSingle(infoId, outInfoId, traceId)
} else {
err = getAPI(vendorOrgCode).CancelPromotionLimitTime(infoId, outInfoId, traceId)
}
}
return err
}
func AdjustPromotionTime(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
err = getAPI(vendorOrgCode).AdjustPromotionTimeSingle(infoId, outInfoId, endDate, traceId)
} else {
err = getAPI(vendorOrgCode).AdjustPromotionTimeLimitTime(infoId, outInfoId, endDate, traceId)
}
}
return err
}
func AdjustPromotionSku(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) {
if globals.EnableJdStoreWrite {
if promotionType == model.ActSkuDirectDown {
skusResult, err = getAPI(vendorOrgCode).AdjustPromotionSkuSingle(infoId, outInfoId, skus, traceId)
} else {
skusResult, err = getAPI(vendorOrgCode).AdjustPromotionSkuLimitTime(infoId, outInfoId, skus, traceId)
}
}
return skusResult, err
}
func storeSku2Jd(actStoreSku []*model.ActStoreSku2, handler func(syncStatus int8) bool) (jdActStoreSku []*jdapi.PromotionSku) {
for _, v := range actStoreSku {
if handler(v.SyncStatus) {
if v.VendorStoreID != "" && v.VendorSkuID != "" {
jdActStoreSku = append(jdActStoreSku, &jdapi.PromotionSku{
StationNo: utils.Str2Int64(v.VendorStoreID),
SkuID: utils.Str2Int64(v.VendorSkuID),
PromotionPrice: v.ActualActPrice,
LimitSkuCount: v.Stock,
})
}
}
}
return jdActStoreSku
}
func createSkuAct(ctx *jxcontext.Context, act *model.Act2, actStoreSku []*model.ActStoreSku2) (vendorActID string, err error) {
traceInfo := ctx.GetTrackInfo()
outInfoID := ""
if act.VendorActID == "" {
outInfoID = utils.Int2Str(act.ID)
}
infoID, err2 := CreatePromotionInfos(act.VendorOrgCode, act.Type, act.GetRealActName(), act.BeginAt, act.EndAt, outInfoID, act.Advertising, traceInfo)
if err = err2; err == nil {
vendorActID = utils.Int64ToStr(infoID)
if err = CreatePromotionRules(act.VendorOrgCode, act.Type, infoID, "", act.LimitUser, act.LimitUser, act.LimitCount, 1, traceInfo); err == nil {
if _, err = CreatePromotionSku(act.VendorOrgCode, act.Type, infoID, utils.Int2Str(act.ID), storeSku2Jd(actStoreSku, model.IsSyncStatusNeedCreate), traceInfo); err == nil {
if err = ConfirmPromotion(act.VendorOrgCode, act.Type, infoID, "", traceInfo); err == nil {
for _, v := range actStoreSku {
v.VendorActID = vendorActID
}
}
}
}
if err != nil {
CancelPromotion(act.VendorOrgCode, act.Type, infoID, "", traceInfo)
}
}
return vendorActID, err
}
func proxyCreateSkuAct(ctx *jxcontext.Context, act *model.Act2, actStoreSku []*model.ActStoreSku2) (vendorActID string, err error) {
if isCreateTypeSingleStoreSku() && len(actStoreSku) > 1 {
errList := errlist.New()
vendorActID := act.VendorActID
act.VendorActID = "placeholder"
for _, v := range actStoreSku {
_, err := createSkuAct(ctx, act, []*model.ActStoreSku2{v})
errList.AddErr(err)
}
act.VendorActID = vendorActID
err = errList.GetErrListAsOne()
} else {
vendorActID, err = createSkuAct(ctx, act, actStoreSku)
}
return vendorActID, err
}
func cancelSkuActSkus(ctx *jxcontext.Context, act *model.Act2, vendorActID string, actStoreSku []*model.ActStoreSku2) (err error) {
if vendorActID != "" {
if skuList := storeSku2Jd(actStoreSku, model.IsSyncStatusNeedDelete); len(skuList) > 0 {
err = CancelPromotionSku(act.VendorOrgCode, act.Type, utils.Str2Int64(vendorActID), "", skuList, ctx.GetTrackInfo())
}
}
return err
}
func cancelSkuAct(ctx *jxcontext.Context, act *model.Act2, vendorActID string) (err error) {
if vendorActID != "" {
err = CancelPromotion(act.VendorOrgCode, act.Type, utils.Str2Int64(vendorActID), "", ctx.GetTrackInfo())
}
return err
}
func (c *PurchaseHandler) SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreSkuList []*model.ActStoreSku2) (err error) {
vendorActInfoMap := make(map[string][]*model.ActStoreSku2)
deleteActInfoMap := make(map[string][]*model.ActStoreSku2)
var actStoreSkuList4Create []*model.ActStoreSku2
var updateItems []*dao.KVUpdateItem
actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuList)
actSkuCount := 0
toDelActSkuCount := 0
for storeID := range actStoreSkuMap {
for _, actStoreSku := range actStoreSkuMap[storeID] {
vendorActID := actStoreSku.VendorActID
if vendorActID == "" {
vendorActID = act.VendorActID
}
actSkuCount++
vendorActInfoMap[vendorActID] = append(vendorActInfoMap[vendorActID], actStoreSku)
if model.IsSyncStatusDelete(actStoreSku.SyncStatus) {
toDelActSkuCount++
deleteActInfoMap[vendorActID] = append(deleteActInfoMap[vendorActID], actStoreSku)
} else if model.IsSyncStatusNew(actStoreSku.SyncStatus) {
actStoreSkuList4Create = append(actStoreSkuList4Create, actStoreSku)
}
}
}
// 如果是全删,直接添加删除(即取消)标志
if actSkuCount == toDelActSkuCount {
act.SyncStatus |= model.SyncFlagDeletedMask
}
db := dao.GetDB()
err = func() (err error) {
if model.IsSyncStatusDelete(act.SyncStatus) {
errList := errlist.New()
for vendorActID := range vendorActInfoMap {
if vendorActID != "" {
if err = cancelSkuAct(ctx, act, vendorActID); err == nil {
updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, vendorActInfoMap[vendorActID], model.SyncFlagModifiedMask)...)
} else {
errList.AddErr(err)
}
}
}
if err = errList.GetErrListAsOne(); err == nil {
updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagModifiedMask))
}
} else if model.IsSyncStatusNew(act.SyncStatus) {
if act.VendorActID, err = proxyCreateSkuAct(ctx, act, actStoreSkuList4Create); err == nil {
updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, actStoreSkuList4Create, model.SyncFlagNewMask)...)
updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagNewMask))
} else {
if act.VendorActID != "" {
actMap := partner.Act2ActMap(act)
dao.UpdateEntity(db, actMap, model.FieldVendorActID)
}
}
} else if model.IsSyncStatusUpdate(act.SyncStatus) {
errList := errlist.New()
if len(actStoreSkuList4Create) > 0 {
if _, err = proxyCreateSkuAct(ctx, act, actStoreSkuList4Create); err == nil {
updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, actStoreSkuList4Create, model.SyncFlagNewMask)...)
} else {
errList.AddErr(err)
}
}
for vendorActID := range deleteActInfoMap {
if vendorActID != "" {
if len(vendorActInfoMap[vendorActID]) == len(deleteActInfoMap[vendorActID]) {
err = cancelSkuAct(ctx, act, vendorActID)
} else {
err = cancelSkuActSkus(ctx, act, vendorActID, deleteActInfoMap[vendorActID])
}
if err == nil {
updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, deleteActInfoMap[vendorActID], model.SyncFlagDeletedMask)...)
} else {
errList.AddErr(err)
}
} else {
updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, deleteActInfoMap[vendorActID], model.SyncFlagDeletedMask)...)
}
}
if err = errList.GetErrListAsOne(); err == nil {
updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagModifiedMask))
}
}
return err
}()
_, err2 := dao.BatchUpdateActEntity(db, model.IsSyncStatusDelete(act.SyncStatus), updateItems)
if err == nil {
err = err2
}
return err
}
func OnActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
jxutils.CallMsgHandler(func() {
retVal = CurPurchaseHandler.onActMsg(msg)
}, jxutils.ComposeUniversalOrderID(msg.BillID, model.VendorIDJD))
return retVal
}
func (c *PurchaseHandler) onActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
if msg.StatusID == jdapi.PromotionStatusSingleOK || msg.StatusID == jdapi.PromotionStatusLimitTimeOK {
promotionID := msg.BillID
intPromotionID := utils.Str2Int64(promotionID)
if _, ok := actMap.Load(intPromotionID); !ok {
utils.CallFuncAsync(func() {
if !partner.CurActManager.IsVendorActExist(jxcontext.AdminCtx, promotionID, model.VendorIDJD) {
act, actStoreSkuList, err := getActFromJD(AppKey2OrgCode(msg.AppKey), promotionID)
if err == nil && len(actStoreSkuList) > 0 {
_, err = partner.CurActManager.CreateActFromVendor(jxcontext.AdminCtx, act, actStoreSkuList)
}
if err != nil {
retVal = jdapi.Err2CallbackResponse(err, promotionID)
}
}
})
} else {
actMap.Delete(intPromotionID)
}
}
return retVal
}
func getActFromJD(vendorOrgCode, promotionID string) (act *model.Act2, actStoreSkuList []*model.ActStoreSku2, err error) {
result, err := getAPI(vendorOrgCode).QueryPromotionInfo(utils.Str2Int64(promotionID))
if err == nil && len(result.SkuResultList) > 0 {
act = &model.Act2{
Act: model.Act{
Name: fmt.Sprintf("%s-%d", result.Source, result.PromotionInfoID),
Type: jdSkuActType2Jx(result.PromotionType),
Status: jdSkuActStatus2Jx(result.PromotionState),
BeginAt: result.SkuResultList[0].BeginTime.GoTime(),
EndAt: result.SkuResultList[0].EndTime.GoTime(),
Source: result.Source,
CreateType: model.ActCreateTypeCallback,
PricePercentage: 0,
LimitDaily: result.SkuResultList[0].LimitDaily,
LimitCount: 1,
},
VendorID: model.VendorIDJD,
VendorOrgCode: vendorOrgCode,
VendorActID: promotionID,
}
if utils.IsTimeZero(act.BeginAt) {
act.BeginAt = result.BeginTime.GoTime()
}
if utils.IsTimeZero(act.EndAt) {
act.EndAt = result.EndTime.GoTime().Add(24*time.Hour - 1*time.Second)
}
if result.SkuResultList[0].LimitPin == 1 || result.SkuResultList[0].LimitDevice == 1 {
act.LimitUser = 1
}
for _, v := range result.SkuResultList {
if v.PromotionState != jdapi.PromotionStateCanceled {
actStoreSkuList = append(actStoreSkuList, &model.ActStoreSku2{
VendorStoreID: utils.Int64ToStr(v.StationNo),
VendorSkuID: utils.Int64ToStr(v.SkuID),
ActualActPrice: int64(v.PromotionPrice),
})
}
}
}
return act, actStoreSkuList, err
}
func (c *PurchaseHandler) GetActAmple(ctx *jxcontext.Context, vendorStoreID, vendorOrgCode string) (ample int, err error) {
return ample, err
}