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 }