master:推广营销调优;

pull/1121/head
liujiang 2025-06-07 16:03:02 +08:00
parent 9be91d4537
commit 220640c098
7 changed files with 402 additions and 406 deletions

View File

@ -33,23 +33,13 @@ public class AdvertRoundController extends XktBaseController {
final IAdvertRoundService advertRoundService;
// TODO 定时任务将档口的广告位 更新到redis中
// TODO 定时任务将档口的广告位 更新到redis中
// TODO 定时任务将档口的广告位 更新到redis中
// TODO 定时任务将档口的广告位 更新到redis中
/**
* 广
*/
@ApiOperation(value = "档口购买推广营销", httpMethod = "POST", response = R.class)
@Log(title = "档口购买推广营销", businessType = BusinessType.INSERT)
@PostMapping
public R<Integer> create(@Validated @RequestBody AdRoundStoreCreateVO createVO) throws ParseException {
public R<Integer> create(@Validated @RequestBody AdRoundStoreCreateVO createVO) {
return R.ok(advertRoundService.create(BeanUtil.toBean(createVO, AdRoundStoreCreateDTO.class)));
}

View File

@ -73,13 +73,13 @@ public class DailyTaskController extends BaseController {
}
@PostMapping("/advert-round")
public R dailyRound(SysJob sysJob) throws ParseException {
public R dailyRound(SysJob sysJob) {
task.dailyAdvertRound();
return R.ok();
}
@PostMapping("/advert-round-filter")
public R dailyRoundFilterTime(SysJob sysJob) throws ParseException {
public R dailyRoundFilterTime(SysJob sysJob) {
task.saveAdvertDeadlineToRedis();
return R.ok();
}

View File

@ -99,323 +99,52 @@ public class XktTask {
final UserBrowsingHistoryMapper userBrowHisMapper;
/**
*
* 316191121
*/
@Transactional
public void test() {
public void seasonTag() {
LocalDate today = LocalDate.now();
int month = today.getMonthValue();
String seasonLabel = "";
if (month == 3) {
seasonLabel = today.getYear() + "年春季";
} else if (month == 6) {
seasonLabel = today.getYear() + "年夏季";
} else if (month == 9) {
seasonLabel = today.getYear() + "年秋季";
} else if (month == 12) {
seasonLabel = today.getYear() + "年冬季";
}
if (StringUtils.isEmpty(seasonLabel)) {
return;
}
log.info("生成季节标签:{}", seasonLabel);
// TODO 每年2月1日生成夏季标签
// TODO 每年5月1日生成秋季标签
// TODO 每年8月1日生成冬季标签
// TODO 每年11月1日生成春季标签
// 上市季节年份 sys_dict_type sys_dict_data 两张表
// TODO 插入到sys_dict_type sys_dict_data两张表
// TODO 插入到sys_dict_type sys_dict_data两张表
// TODO 插入到sys_dict_type sys_dict_data两张表
}
/**
* 12:10
* 12:00:01 storeredis
*/
@Transactional
public void dailySale() {
// 使用LocalDate获取当前日期前一天并转为 Date 格式
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 先检查是否有该天的销售数据,若有则先删除,保证数据唯一性
List<DailySale> existList = this.dailySaleMapper.selectList(new LambdaQueryWrapper<DailySale>()
.eq(DailySale::getVoucherDate, yesterday).eq(DailySale::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailySaleMapper.deleteByIds(existList.stream().map(DailySale::getId).collect(Collectors.toList()));
}
// 查询当前的销售数据
List<DailySaleDTO> saleList = this.dailySaleMapper.selectDailySale(yesterday);
if (CollectionUtils.isEmpty(saleList)) {
public void saveStoreToRedis() {
List<Store> storeList = this.storeMapper.selectList(new LambdaQueryWrapper<Store>()
.eq(Store::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeList)) {
return;
}
this.dailySaleMapper.insert(saleList.stream().map(x -> BeanUtil.toBean(x, DailySale.class)
.setVoucherDate(yesterday)).collect(Collectors.toList()));
}
/**
* 1215
*/
@Transactional
public void dailySaleCustomer() {
// 使用LocalDate获取当前日期前一天并转为 Date 格式
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 先检查是否有该天的销售数据,若有则先删除,保证数据唯一性
List<DailySaleCustomer> existList = this.dailySaleCusMapper.selectList(new LambdaQueryWrapper<DailySaleCustomer>()
.eq(DailySaleCustomer::getVoucherDate, yesterday).eq(DailySaleCustomer::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailySaleCusMapper.deleteByIds(existList.stream().map(DailySaleCustomer::getId).collect(Collectors.toList()));
}
// 查询当前的客户销售数据
List<DailySaleCusDTO> cusSaleList = this.dailySaleCusMapper.selectDailySale(yesterday);
if (CollectionUtils.isEmpty(cusSaleList)) {
return;
}
this.dailySaleCusMapper.insert(cusSaleList.stream().map(x -> BeanUtil.toBean(x, DailySaleCustomer.class)
.setVoucherDate(yesterday)).collect(Collectors.toList()));
}
/**
* 1220
*/
@Transactional
public void dailySaleProduct() {
// 使用LocalDate获取当前日期前一天并转为 Date 格式
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 先检查是否有该天的销售数据,若有则先删除,保证数据唯一性
List<DailySaleProduct> existList = this.dailySaleProdMapper.selectList(new LambdaQueryWrapper<DailySaleProduct>()
.eq(DailySaleProduct::getVoucherDate, yesterday).eq(DailySaleProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailySaleProdMapper.deleteByIds(existList.stream().map(DailySaleProduct::getId).collect(Collectors.toList()));
}
// 查询档口商品的销售数据
List<DailySaleProdDTO> saleList = this.dailySaleProdMapper.selectDailySale(yesterday);
if (CollectionUtils.isEmpty(saleList)) {
return;
}
this.dailySaleProdMapper.insert(saleList.stream().map(x -> BeanUtil.toBean(x, DailySaleProduct.class)
.setVoucherDate(yesterday)).collect(Collectors.toList()));
}
/**
* 1225
*/
@Transactional
public void dailyCategorySort() {
// 系统所有的商品分类
List<SysProductCategory> cateList = this.prodCateMapper.selectList(new LambdaQueryWrapper<SysProductCategory>()
.eq(SysProductCategory::getDelFlag, Constants.UNDELETED).eq(SysProductCategory::getStatus, Constants.UNDELETED));
if (CollectionUtils.isEmpty(cateList)) {
throw new ServiceException("商品分类不存在!", HttpStatus.ERROR);
}
// 根据LocalDate 获取当前日期前一天
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 及当前日期前一天的前一周,并转为 Date 格式
final Date pastDate = java.sql.Date.valueOf(LocalDate.now().minusDays(8));
// 获取各项子分类最近一周的销售数量
List<WeekCateSaleDTO> weekCateSaleList = this.weekCateSaleMapper.selectWeekCateSale(yesterday, pastDate);
if (CollectionUtils.isEmpty(weekCateSaleList)) {
return;
}
// 将各个小项销售数量转化为map
Map<Long, Integer> itemCateCountMap = weekCateSaleList.stream().collect(Collectors.toMap(WeekCateSaleDTO::getProdCateId, WeekCateSaleDTO::getCount));
// 按照大类对应的各小类以此进行数量统计及排序
List<WeekCateSaleDTO> sortList = new ArrayList<>();
cateList.stream()
// 过滤掉父级为0的分类以及父级为1的分类父级为1的为子分类
.filter(x -> !Objects.equals(x.getParentId(), 0L) && !Objects.equals(x.getParentId(), 1L))
.collect(Collectors.groupingBy(SysProductCategory::getParentId))
.forEach((parentId, itemList) -> sortList.add(new WeekCateSaleDTO() {{
setCount(itemList.stream().mapToInt(x -> itemCateCountMap.getOrDefault(x.getId(), 0)).sum());
setProdCateId(parentId);
}}));
// 按照大类的数量倒序排列
sortList.sort(Comparator.comparing(WeekCateSaleDTO::getCount).reversed());
Map<Long, Integer> topCateSortMap = new LinkedHashMap<>();
// 按照sortList的顺序结合 topCateMap 依次更新SysProductCategory 的 sortNum的排序
for (int i = 0; i < sortList.size(); i++) {
topCateSortMap.put(sortList.get(i).getProdCateId(), i + 1);
}
// 顶级分类的数量
Integer topCateSize = Math.toIntExact(cateList.stream().filter(x -> Objects.equals(x.getParentId(), 1L)).count());
// 次级分类列表
List<SysProductCategory> updateList = cateList.stream().filter(x -> Objects.equals(x.getParentId(), 1L))
// 如果存在具体销售数量,则按照具体销售数量排序,否则将排序置为最大值
.peek(x -> x.setOrderNum(topCateSortMap.getOrDefault(x.getId(), topCateSize)))
.collect(Collectors.toList());
this.prodCateMapper.updateById(updateList);
}
/**
* 1230
*/
@Transactional
public void dailyStoreTag() {
// 先删除所有的档口标签,保证数据唯一性
List<DailyStoreTag> existList = this.dailyStoreTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
.eq(DailyStoreTag::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailyStoreTagMapper.deleteByIds(existList.stream().map(DailyStoreTag::getId).collect(Collectors.toList()));
}
List<DailyStoreTag> tagList = new ArrayList<>();
// 单据日期为当然
final Date now = java.sql.Date.valueOf(LocalDate.now());
// 根据LocalDate 获取当前日期前一天
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 使用LocalDate 获取当前日期前一天的前一周,并转为 Date 格式
final Date oneWeekAgo = java.sql.Date.valueOf(LocalDate.now().minusWeeks(1));
// 使用LocalDate 获取当前日期前一天的前一个月
final Date oneMonthAgo = java.sql.Date.valueOf(LocalDate.now().minusMonths(1));
// 1. 打 月销千件 标签
this.tagSaleThousand(now, yesterday, oneMonthAgo, tagList);
// 2. 打 销量榜 标签
this.tagStoreSaleRank(now, yesterday, oneMonthAgo, tagList);
// 3. 打 爆款频出 标签根据销量前50的商品中 档口 先后顺序排列
this.tagHotRank(now, yesterday, oneMonthAgo, tagList);
// 4. 打 新款频出 标签,根据最近一周档口商品上新速度,先后排序
this.tagNewProd(now, yesterday, oneWeekAgo, tagList);
// 5. 打 关注榜 标签,根据关注量,进行排序
this.tagAttentionRank(now, yesterday, tagList);
// 6. 打 库存榜 标签,根据库存量,进行排序
this.tagStockTag(yesterday, oneMonthAgo, tagList);
// 7. 打 七日上新 标签
this.tag7DaysNewTag(now, yesterday, oneWeekAgo, tagList);
if (CollectionUtils.isEmpty(tagList)) {
return;
}
this.dailyStoreTagMapper.insert(tagList);
}
/**
* 1240
*/
@Transactional
public void dailyProdTag() throws IOException {
// 先删除所有的商品标签,保证数据唯一性
List<DailyProdTag> existList = this.dailyProdTagMapper.selectList(new LambdaQueryWrapper<DailyProdTag>()
.eq(DailyProdTag::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailyProdTagMapper.deleteByIds(existList.stream().map(DailyProdTag::getId).collect(Collectors.toList()));
}
final Date now = java.sql.Date.valueOf(LocalDate.now());
// 根据LocalDate 获取当前日期前一天
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 使用LocalDate 获取当前日期4天前并转为 Date 格式
final Date fourDaysAgo = java.sql.Date.valueOf(LocalDate.now().minusDays(4));
// 使用LocalDate 获取当前日期前一天的前一周,并转为 Date 格式
final Date oneWeekAgo = java.sql.Date.valueOf(LocalDate.now().minusWeeks(1));
// 使用LocalDate 获取当前日期前一天的前一个月
final Date oneMonthAgo = java.sql.Date.valueOf(LocalDate.now().minusMonths(1));
List<DailyProdTag> tagList = new ArrayList<>();
// 1. 打 月销千件 标签
this.tagMonthSaleThousand(now, yesterday, oneMonthAgo, tagList);
// 2. 打 销量榜 标签
this.tagProdSaleTop10(now, yesterday, oneMonthAgo, tagList);
// 3. 当月(近一月)爆款
this.tagMonthHot(now, yesterday, oneMonthAgo, tagList);
// 4. 档口热卖
this.tagStoreHotTop20(now, yesterday, oneMonthAgo, tagList);
// 5. 图搜榜
this.tagImgSearchTop10(now, yesterday, oneMonthAgo, tagList);
// 6. 收藏榜
this.tagCollectionTop10(now, yesterday, oneMonthAgo, tagList);
// 7. 下载榜 铺货榜
this.tagDownloadTop10(now, yesterday, oneMonthAgo, tagList);
// 8. 三日上新
this.tagThreeDayNew(now, yesterday, fourDaysAgo, tagList);
// 9. 七日上新
this.tagSevenDayNew(now, yesterday, fourDaysAgo, oneWeekAgo, tagList);
// 10. 风格
this.tagStyle(now, yesterday, tagList);
if (CollectionUtils.isEmpty(tagList)) {
return;
}
this.dailyProdTagMapper.insert(tagList);
// 更新商品的标签到ES
this.updateESProdTags(tagList);
}
/**
* 1:00
*/
@Transactional
public void dailyProdWeight() {
List<StoreProduct> storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper<StoreProduct>()
.eq(StoreProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeProdList)) {
return;
}
// 获取 商品销售、商品浏览量、商品收藏量、商品下载量
List<StoreProductStatistics> statisticsList = this.prodStatMapper.selectList(new LambdaQueryWrapper<StoreProductStatistics>()
.eq(StoreProductStatistics::getDelFlag, Constants.UNDELETED));
// 商品浏览量、下载量
Map<Long, StoreProductStatistics> prodStatMap = statisticsList.stream().collect(Collectors.toMap(StoreProductStatistics::getStoreProdId, Function.identity()));
// 商品收藏量
List<UserFavorites> userFavList = this.userFavMapper.selectList(new LambdaQueryWrapper<UserFavorites>()
.eq(UserFavorites::getDelFlag, Constants.UNDELETED));
Map<Long, Long> userFavMap = userFavList.stream().collect(Collectors.groupingBy(UserFavorites::getStoreProdId, Collectors.summingLong(UserFavorites::getId)));
// 商品销售量
List<StoreSaleDetail> saleDetailList = this.saleDetailMapper.selectList(new LambdaQueryWrapper<StoreSaleDetail>()
.eq(StoreSaleDetail::getDelFlag, Constants.UNDELETED).eq(StoreSaleDetail::getSaleType, SaleType.GENERAL_SALE.getValue()));
Map<Long, Long> saleMap = saleDetailList.stream().collect(Collectors.groupingBy(StoreSaleDetail::getStoreProdId,
Collectors.summingLong(StoreSaleDetail::getQuantity)));
storeProdList.forEach(x -> {
final long viewCount = ObjectUtils.isEmpty(prodStatMap.get(x.getId())) ? 0L : prodStatMap.get(x.getId()).getViewCount();
final long downloadCount = ObjectUtils.isEmpty(prodStatMap.get(x.getId())) ? 0L : prodStatMap.get(x.getId()).getDownloadCount();
final long favCount = ObjectUtils.isEmpty(userFavMap.get(x.getId())) ? 0L : userFavMap.get(x.getId());
final long saleCount = ObjectUtils.isEmpty(saleMap.get(x.getId())) ? 0L : saleMap.get(x.getId());
// 计算推荐权重,权重计算公式为:(浏览次数 * 0.3 + 下载次数 + 收藏次数 + 销售量 * 0.3)
final long recommendWeight = (long) (viewCount * 0.3 + downloadCount + favCount + saleCount * 0.3);
// 根据销售数量计算销售权重权重占销售数量的30%
final long saleWeight = (long) (saleCount * 0.3);
// 计算人气权重,权重计算公式为:(浏览次数 * 0.1 和 100 取最小值) + 下载次数 + 收藏次数
final long popularityWeight = (long) (Math.min(viewCount * 0.1, 100) + downloadCount + favCount);
x.setRecommendWeight(recommendWeight).setSaleWeight(saleWeight).setPopularityWeight(popularityWeight);
storeList.forEach(store -> {
redisCache.setCacheObject(CacheConstants.STORE_KEY + store.getId(), store, 1, TimeUnit.DAYS);
});
this.storeProdMapper.updateById(storeProdList);
}
/**
* 1:10
* 12:01 广
*/
@Transactional
public void dailyStoreWeight() {
List<StoreProduct> storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper<StoreProduct>().eq(StoreProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeProdList)) {
return;
}
// 按照storeId 的维度,对档口权重 按照 recommendWeight 进行汇总,按照降序排列
Map<Long, Long> storeWeightMap = storeProdList.stream().collect(Collectors.groupingBy(StoreProduct::getStoreId,
Collectors.summingLong(x -> ObjectUtils.defaultIfNull(x.getRecommendWeight(), 0L))));
// 筛选每个档口最新的4个商品及主图
List<Store> storeList = this.storeMapper.selectList(new LambdaQueryWrapper<Store>().eq(Store::getDelFlag, Constants.UNDELETED));
Map<Long, Store> storeMap = storeList.stream().collect(Collectors.toMap(Store::getId, Function.identity()));
List<DailyStoreTag> storeTagList = this.dailyStoreTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
.eq(DailyStoreTag::getDelFlag, Constants.UNDELETED));
// 档口标签map
Map<Long, List<String>> storeTagMap = storeTagList.stream().collect(Collectors
.groupingBy(DailyStoreTag::getStoreId, Collectors.collectingAndThen(Collectors.toList(), list -> list.stream()
.sorted(Comparator.comparing(DailyStoreTag::getType)).map(DailyStoreTag::getTag).collect(Collectors.toList()))));
List<StoreProdFileLatestFourProdDTO> latest4ProdList = this.storeProdFileMapper.selectLatestFourProdList();
Map<Long, List<StoreProdFileLatestFourProdDTO>> latestProdMap = latest4ProdList.stream().collect(Collectors
.groupingBy(StoreProdFileLatestFourProdDTO::getStoreId));
List<PCStoreRecommendTempDTO> storeRecommendList = new ArrayList<>();
storeWeightMap.forEach((storeId, recommendWeight) -> {
final Store store = storeMap.get(storeId);
storeRecommendList.add(new PCStoreRecommendTempDTO().setStoreId(storeId).setTags(storeTagMap.get(storeId))
.setAdvert(Boolean.FALSE).setRecommendWeight(recommendWeight)
.setStoreWeight(ObjectUtils.isNotEmpty(store) ? store.getStoreWeight() : null)
.setStoreName(ObjectUtils.isNotEmpty(store) ? store.getStoreName() : "")
.setStoreAddress(ObjectUtils.isNotEmpty(store) ? store.getStoreAddress() : "")
.setContactPhone(ObjectUtils.isNotEmpty(store) ? store.getContactPhone() : "")
.setQqAccount(ObjectUtils.isNotEmpty(store) ? store.getQqAccount() : "")
.setWechatAccount(ObjectUtils.isNotEmpty(store) ? store.getWechatAccount() : "")
.setProdList(BeanUtil.copyToList(latestProdMap.get(storeId), PCStoreRecommendTempDTO.PCSRNewProdDTO.class)));
});
if (CollectionUtils.isEmpty(storeRecommendList)) {
return;
}
// 先按照 档口权重 倒序排,再按照 推荐权重 倒序排
storeRecommendList.sort(Comparator.comparing(PCStoreRecommendTempDTO::getStoreWeight, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(PCStoreRecommendTempDTO::getRecommendWeight, Comparator.nullsLast(Comparator.reverseOrder())));
// 返回给前端的数据 不包含 storeWeight 和 storeRecommnedWeight
List<PCStoreRecommendDTO> recommendList = BeanUtil.copyToList(storeRecommendList, PCStoreRecommendDTO.class);
// 放到redis中
redisCache.setCacheObject(CacheConstants.PC_STORE_RECOMMEND_LIST, recommendList);
}
/**
* 广 12:01:00
*/
@Transactional
public void dailyAdvertRound() throws ParseException {
public void dailyAdvertRound() {
List<Advert> advertList = this.advertMapper.selectList(new LambdaQueryWrapper<Advert>()
.eq(Advert::getDelFlag, Constants.UNDELETED).eq(Advert::getOnlineStatus, AdOnlineStatus.ONLINE.getValue()));
if (CollectionUtils.isEmpty(advertList)) {
@ -449,7 +178,8 @@ public class XktTask {
final int roundId = i + 1;
updateList.add(new AdvertRound().setAdvertId(advert.getId()).setTypeId(advert.getTypeId()).setRoundId(roundId).setLaunchStatus(launchStatus)
.setStartTime(java.sql.Date.valueOf(now)).setEndTime(java.sql.Date.valueOf(endDate)).setPosition(position).setStartPrice(advert.getStartPrice())
.setSysIntercept(AdSysInterceptType.UN_INTERCEPT.getValue()).setShowType(advert.getShowType()).setDisplayType(advert.getDisplayType())
.setSysIntercept(AdSysInterceptType.UN_INTERCEPT.getValue()).setShowType(advert.getShowType())
.setDisplayType(advert.getDisplayType()).setDeadline(advert.getDeadline())
.setSymbol(Objects.equals(advert.getShowType(), AdShowType.POSITION_ENUM.getValue())
// 如果是位置枚举的推广位则需要精确到某一个position的推广位反之若是时间范围则直接精确到播放轮次即可
? advert.getBasicSymbol() + roundId + position : advert.getBasicSymbol() + roundId));
@ -499,14 +229,12 @@ public class XktTask {
.setLaunchStatus(AdLaunchStatus.UN_LAUNCH.getValue()).setPosition(position).setStartPrice(advert.getStartPrice())
.setSysIntercept(AdSysInterceptType.UN_INTERCEPT.getValue()).setShowType(advert.getShowType()).setDisplayType(advert.getDisplayType())
// java.sql.Date 直接转化成yyyy-MM-dd格式
.setStartTime(java.sql.Date.valueOf(startDate)).setEndTime(java.sql.Date.valueOf(endDate))
.setStartTime(java.sql.Date.valueOf(startDate)).setEndTime(java.sql.Date.valueOf(endDate)).setDeadline(advert.getDeadline())
.setSymbol(Objects.equals(advert.getShowType(), AdShowType.POSITION_ENUM.getValue())
// 如果是位置枚举的推广位则需要精确到某一个position的推广位反之若是时间范围则直接精确到播放轮次即可
? advert.getBasicSymbol() + maxRoundId + position : advert.getBasicSymbol() + maxRoundId));
}
}
// 需要更新推广位轮次最新的资源锁
this.advertRoundService.initAdvertLockMap();
}
}
}
@ -514,48 +242,229 @@ public class XktTask {
if (CollectionUtils.isNotEmpty(updateList)) {
this.advertRoundMapper.insertOrUpdate(updateList);
}
// 更新推广轮次截止时间到redis
this.saveAdvertDeadlineToRedis();
}
/**
* 22:00:10广 biddingTempStatusbiddingStatus
* 00:04symbolredis
*/
@Transactional
public void updateAdvertRoundBiddingStatus() throws ParseException {
this.advertRoundService.updateBiddingStatus();
public void saveSymbolToRedis() {
advertRoundService.initAdvertLockMap();
}
/**
* 12:00:01redis广
* 5.1 - 5.3
* a. 4.30 4.30 22:00
* b. 5.2 5.2 22:00:00
* c. 5.3redis
*
* @throws ParseException
* 00:08广
*/
public void saveAdvertDeadlineToRedis() throws ParseException {
public void saveAdvertDeadlineToRedis() {
// 直接调service方法若当时redis出了问题也方便第一时间 通过业务流程弥补 两边都有一个补偿机制
this.advertRoundService.saveAdvertDeadlineToRedis();
}
/**
* 12:00:01storeredis
* 00:10
*/
public void saveStoreToRedis() {
List<Store> storeList = this.storeMapper.selectList(new LambdaQueryWrapper<Store>()
.eq(Store::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeList)) {
@Transactional
public void dailySale() {
// 使用LocalDate获取当前日期前一天并转为 Date 格式
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 先检查是否有该天的销售数据,若有则先删除,保证数据唯一性
List<DailySale> existList = this.dailySaleMapper.selectList(new LambdaQueryWrapper<DailySale>()
.eq(DailySale::getVoucherDate, yesterday).eq(DailySale::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailySaleMapper.deleteByIds(existList.stream().map(DailySale::getId).collect(Collectors.toList()));
}
// 查询当前的销售数据
List<DailySaleDTO> saleList = this.dailySaleMapper.selectDailySale(yesterday);
if (CollectionUtils.isEmpty(saleList)) {
return;
}
storeList.forEach(store -> {
redisCache.setCacheObject(CacheConstants.STORE_KEY + store.getId(), store, 1, TimeUnit.DAYS);
});
this.dailySaleMapper.insert(saleList.stream().map(x -> BeanUtil.toBean(x, DailySale.class)
.setVoucherDate(yesterday)).collect(Collectors.toList()));
}
/**
* 12:45:00 advertredis
* 00:15
*/
@Transactional
public void dailySaleCustomer() {
// 使用LocalDate获取当前日期前一天并转为 Date 格式
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 先检查是否有该天的销售数据,若有则先删除,保证数据唯一性
List<DailySaleCustomer> existList = this.dailySaleCusMapper.selectList(new LambdaQueryWrapper<DailySaleCustomer>()
.eq(DailySaleCustomer::getVoucherDate, yesterday).eq(DailySaleCustomer::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailySaleCusMapper.deleteByIds(existList.stream().map(DailySaleCustomer::getId).collect(Collectors.toList()));
}
// 查询当前的客户销售数据
List<DailySaleCusDTO> cusSaleList = this.dailySaleCusMapper.selectDailySale(yesterday);
if (CollectionUtils.isEmpty(cusSaleList)) {
return;
}
this.dailySaleCusMapper.insert(cusSaleList.stream().map(x -> BeanUtil.toBean(x, DailySaleCustomer.class)
.setVoucherDate(yesterday)).collect(Collectors.toList()));
}
/**
* 00:20
*/
@Transactional
public void dailySaleProduct() {
// 使用LocalDate获取当前日期前一天并转为 Date 格式
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 先检查是否有该天的销售数据,若有则先删除,保证数据唯一性
List<DailySaleProduct> existList = this.dailySaleProdMapper.selectList(new LambdaQueryWrapper<DailySaleProduct>()
.eq(DailySaleProduct::getVoucherDate, yesterday).eq(DailySaleProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailySaleProdMapper.deleteByIds(existList.stream().map(DailySaleProduct::getId).collect(Collectors.toList()));
}
// 查询档口商品的销售数据
List<DailySaleProdDTO> saleList = this.dailySaleProdMapper.selectDailySale(yesterday);
if (CollectionUtils.isEmpty(saleList)) {
return;
}
this.dailySaleProdMapper.insert(saleList.stream().map(x -> BeanUtil.toBean(x, DailySaleProduct.class)
.setVoucherDate(yesterday)).collect(Collectors.toList()));
}
/**
* 00:25
*/
@Transactional
public void dailyCategorySort() {
// 系统所有的商品分类
List<SysProductCategory> cateList = this.prodCateMapper.selectList(new LambdaQueryWrapper<SysProductCategory>()
.eq(SysProductCategory::getDelFlag, Constants.UNDELETED).eq(SysProductCategory::getStatus, Constants.UNDELETED));
if (CollectionUtils.isEmpty(cateList)) {
throw new ServiceException("商品分类不存在!", HttpStatus.ERROR);
}
// 根据LocalDate 获取当前日期前一天
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 及当前日期前一天的前一周,并转为 Date 格式
final Date pastDate = java.sql.Date.valueOf(LocalDate.now().minusDays(8));
// 获取各项子分类最近一周的销售数量
List<WeekCateSaleDTO> weekCateSaleList = this.weekCateSaleMapper.selectWeekCateSale(yesterday, pastDate);
if (CollectionUtils.isEmpty(weekCateSaleList)) {
return;
}
// 将各个小项销售数量转化为map
Map<Long, Integer> itemCateCountMap = weekCateSaleList.stream().collect(Collectors.toMap(WeekCateSaleDTO::getProdCateId, WeekCateSaleDTO::getCount));
// 按照大类对应的各小类以此进行数量统计及排序
List<WeekCateSaleDTO> sortList = new ArrayList<>();
cateList.stream()
// 过滤掉父级为0的分类以及父级为1的分类父级为1的为子分类
.filter(x -> !Objects.equals(x.getParentId(), 0L) && !Objects.equals(x.getParentId(), 1L))
.collect(Collectors.groupingBy(SysProductCategory::getParentId))
.forEach((parentId, itemList) -> sortList.add(new WeekCateSaleDTO() {{
setCount(itemList.stream().mapToInt(x -> itemCateCountMap.getOrDefault(x.getId(), 0)).sum());
setProdCateId(parentId);
}}));
// 按照大类的数量倒序排列
sortList.sort(Comparator.comparing(WeekCateSaleDTO::getCount).reversed());
Map<Long, Integer> topCateSortMap = new LinkedHashMap<>();
// 按照sortList的顺序结合 topCateMap 依次更新SysProductCategory 的 sortNum的排序
for (int i = 0; i < sortList.size(); i++) {
topCateSortMap.put(sortList.get(i).getProdCateId(), i + 1);
}
// 顶级分类的数量
Integer topCateSize = Math.toIntExact(cateList.stream().filter(x -> Objects.equals(x.getParentId(), 1L)).count());
// 次级分类列表
List<SysProductCategory> updateList = cateList.stream().filter(x -> Objects.equals(x.getParentId(), 1L))
// 如果存在具体销售数量,则按照具体销售数量排序,否则将排序置为最大值
.peek(x -> x.setOrderNum(topCateSortMap.getOrDefault(x.getId(), topCateSize)))
.collect(Collectors.toList());
this.prodCateMapper.updateById(updateList);
}
/**
* 00:30
*/
@Transactional
public void dailyStoreTag() {
// 先删除所有的档口标签,保证数据唯一性
List<DailyStoreTag> existList = this.dailyStoreTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
.eq(DailyStoreTag::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailyStoreTagMapper.deleteByIds(existList.stream().map(DailyStoreTag::getId).collect(Collectors.toList()));
}
List<DailyStoreTag> tagList = new ArrayList<>();
// 单据日期为当然
final Date now = java.sql.Date.valueOf(LocalDate.now());
// 根据LocalDate 获取当前日期前一天
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 使用LocalDate 获取当前日期前一天的前一周,并转为 Date 格式
final Date oneWeekAgo = java.sql.Date.valueOf(LocalDate.now().minusWeeks(1));
// 使用LocalDate 获取当前日期前一天的前一个月
final Date oneMonthAgo = java.sql.Date.valueOf(LocalDate.now().minusMonths(1));
// 1. 打 月销千件 标签
this.tagSaleThousand(now, yesterday, oneMonthAgo, tagList);
// 2. 打 销量榜 标签
this.tagStoreSaleRank(now, yesterday, oneMonthAgo, tagList);
// 3. 打 爆款频出 标签根据销量前50的商品中 档口 先后顺序排列
this.tagHotRank(now, yesterday, oneMonthAgo, tagList);
// 4. 打 新款频出 标签,根据最近一周档口商品上新速度,先后排序
this.tagNewProd(now, yesterday, oneWeekAgo, tagList);
// 5. 打 关注榜 标签,根据关注量,进行排序
this.tagAttentionRank(now, yesterday, tagList);
// 6. 打 库存榜 标签,根据库存量,进行排序
this.tagStockTag(yesterday, oneMonthAgo, tagList);
// 7. 打 七日上新 标签
this.tag7DaysNewTag(now, yesterday, oneWeekAgo, tagList);
if (CollectionUtils.isEmpty(tagList)) {
return;
}
this.dailyStoreTagMapper.insert(tagList);
}
/**
* 00:40
*/
@Transactional
public void dailyProdTag() throws IOException {
// 先删除所有的商品标签,保证数据唯一性
List<DailyProdTag> existList = this.dailyProdTagMapper.selectList(new LambdaQueryWrapper<DailyProdTag>()
.eq(DailyProdTag::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isNotEmpty(existList)) {
this.dailyProdTagMapper.deleteByIds(existList.stream().map(DailyProdTag::getId).collect(Collectors.toList()));
}
final Date now = java.sql.Date.valueOf(LocalDate.now());
// 根据LocalDate 获取当前日期前一天
final Date yesterday = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
// 使用LocalDate 获取当前日期4天前并转为 Date 格式
final Date fourDaysAgo = java.sql.Date.valueOf(LocalDate.now().minusDays(4));
// 使用LocalDate 获取当前日期前一天的前一周,并转为 Date 格式
final Date oneWeekAgo = java.sql.Date.valueOf(LocalDate.now().minusWeeks(1));
// 使用LocalDate 获取当前日期前一天的前一个月
final Date oneMonthAgo = java.sql.Date.valueOf(LocalDate.now().minusMonths(1));
List<DailyProdTag> tagList = new ArrayList<>();
// 1. 打 月销千件 标签
this.tagMonthSaleThousand(now, yesterday, oneMonthAgo, tagList);
// 2. 打 销量榜 标签
this.tagProdSaleTop10(now, yesterday, oneMonthAgo, tagList);
// 3. 当月(近一月)爆款
this.tagMonthHot(now, yesterday, oneMonthAgo, tagList);
// 4. 档口热卖
this.tagStoreHotTop20(now, yesterday, oneMonthAgo, tagList);
// 5. 图搜榜
this.tagImgSearchTop10(now, yesterday, oneMonthAgo, tagList);
// 6. 收藏榜
this.tagCollectionTop10(now, yesterday, oneMonthAgo, tagList);
// 7. 下载榜 铺货榜
this.tagDownloadTop10(now, yesterday, oneMonthAgo, tagList);
// 8. 三日上新
this.tagThreeDayNew(now, yesterday, fourDaysAgo, tagList);
// 9. 七日上新
this.tagSevenDayNew(now, yesterday, fourDaysAgo, oneWeekAgo, tagList);
// 10. 风格
this.tagStyle(now, yesterday, tagList);
if (CollectionUtils.isEmpty(tagList)) {
return;
}
this.dailyProdTagMapper.insert(tagList);
// 更新商品的标签到ES
this.updateESProdTags(tagList);
}
/**
* 12:45 advertredis
*/
public void saveAdvertToRedis() {
List<Advert> advertList = this.advertMapper.selectList(new LambdaQueryWrapper<Advert>()
@ -568,33 +477,96 @@ public class XktTask {
}
/**
* 00:00:30symbolredis
*/
public void saveSymbolToRedis() {
advertRoundService.initAdvertLockMap();
}
/**
*
* 1:00
*/
@Transactional
public void hourPublicStoreProduct() {
// TODO 定时任务发布商品同步到ES中
// TODO 定时任务发布商品同步到ES中
// TODO 定时任务发布商品同步到ES中
// 向ES索引: product_info 创建文档
// this.createESDoc(storeProd, storeProdDTO);
// 搜图服务同步
// sync2ImgSearchServer(storeProd.getId(), storeProdDTO.getFileList());
// 新增档口商品动态、关注档口用户 通知公告
public void dailyProdWeight() {
List<StoreProduct> storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper<StoreProduct>()
.eq(StoreProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeProdList)) {
return;
}
// 获取 商品销售、商品浏览量、商品收藏量、商品下载量
List<StoreProductStatistics> statisticsList = this.prodStatMapper.selectList(new LambdaQueryWrapper<StoreProductStatistics>()
.eq(StoreProductStatistics::getDelFlag, Constants.UNDELETED));
// 商品浏览量、下载量
Map<Long, StoreProductStatistics> prodStatMap = statisticsList.stream().collect(Collectors.toMap(StoreProductStatistics::getStoreProdId, Function.identity()));
// 商品收藏量
List<UserFavorites> userFavList = this.userFavMapper.selectList(new LambdaQueryWrapper<UserFavorites>()
.eq(UserFavorites::getDelFlag, Constants.UNDELETED));
Map<Long, Long> userFavMap = userFavList.stream().collect(Collectors.groupingBy(UserFavorites::getStoreProdId, Collectors.summingLong(UserFavorites::getId)));
// 商品销售量
List<StoreSaleDetail> saleDetailList = this.saleDetailMapper.selectList(new LambdaQueryWrapper<StoreSaleDetail>()
.eq(StoreSaleDetail::getDelFlag, Constants.UNDELETED).eq(StoreSaleDetail::getSaleType, SaleType.GENERAL_SALE.getValue()));
Map<Long, Long> saleMap = saleDetailList.stream().collect(Collectors.groupingBy(StoreSaleDetail::getStoreProdId,
Collectors.summingLong(StoreSaleDetail::getQuantity)));
storeProdList.forEach(x -> {
final long viewCount = ObjectUtils.isEmpty(prodStatMap.get(x.getId())) ? 0L : prodStatMap.get(x.getId()).getViewCount();
final long downloadCount = ObjectUtils.isEmpty(prodStatMap.get(x.getId())) ? 0L : prodStatMap.get(x.getId()).getDownloadCount();
final long favCount = ObjectUtils.isEmpty(userFavMap.get(x.getId())) ? 0L : userFavMap.get(x.getId());
final long saleCount = ObjectUtils.isEmpty(saleMap.get(x.getId())) ? 0L : saleMap.get(x.getId());
// 计算推荐权重,权重计算公式为:(浏览次数 * 0.3 + 下载次数 + 收藏次数 + 销售量 * 0.3)
final long recommendWeight = (long) (viewCount * 0.3 + downloadCount + favCount + saleCount * 0.3);
// 根据销售数量计算销售权重权重占销售数量的30%
final long saleWeight = (long) (saleCount * 0.3);
// 计算人气权重,权重计算公式为:(浏览次数 * 0.1 和 100 取最小值) + 下载次数 + 收藏次数
final long popularityWeight = (long) (Math.min(viewCount * 0.1, 100) + downloadCount + favCount);
x.setRecommendWeight(recommendWeight).setSaleWeight(saleWeight).setPopularityWeight(popularityWeight);
});
this.storeProdMapper.updateById(storeProdList);
}
/**
* 2
* 1:10
*/
@Transactional
public void dailyStoreWeight() {
List<StoreProduct> storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper<StoreProduct>().eq(StoreProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeProdList)) {
return;
}
// 按照storeId 的维度,对档口权重 按照 recommendWeight 进行汇总,按照降序排列
Map<Long, Long> storeWeightMap = storeProdList.stream().collect(Collectors.groupingBy(StoreProduct::getStoreId,
Collectors.summingLong(x -> ObjectUtils.defaultIfNull(x.getRecommendWeight(), 0L))));
// 筛选每个档口最新的4个商品及主图
List<Store> storeList = this.storeMapper.selectList(new LambdaQueryWrapper<Store>().eq(Store::getDelFlag, Constants.UNDELETED));
Map<Long, Store> storeMap = storeList.stream().collect(Collectors.toMap(Store::getId, Function.identity()));
List<DailyStoreTag> storeTagList = this.dailyStoreTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
.eq(DailyStoreTag::getDelFlag, Constants.UNDELETED));
// 档口标签map
Map<Long, List<String>> storeTagMap = storeTagList.stream().collect(Collectors
.groupingBy(DailyStoreTag::getStoreId, Collectors.collectingAndThen(Collectors.toList(), list -> list.stream()
.sorted(Comparator.comparing(DailyStoreTag::getType)).map(DailyStoreTag::getTag).collect(Collectors.toList()))));
List<StoreProdFileLatestFourProdDTO> latest4ProdList = this.storeProdFileMapper.selectLatestFourProdList();
Map<Long, List<StoreProdFileLatestFourProdDTO>> latestProdMap = latest4ProdList.stream().collect(Collectors
.groupingBy(StoreProdFileLatestFourProdDTO::getStoreId));
List<PCStoreRecommendTempDTO> storeRecommendList = new ArrayList<>();
storeWeightMap.forEach((storeId, recommendWeight) -> {
final Store store = storeMap.get(storeId);
storeRecommendList.add(new PCStoreRecommendTempDTO().setStoreId(storeId).setTags(storeTagMap.get(storeId))
.setAdvert(Boolean.FALSE).setRecommendWeight(recommendWeight)
.setStoreWeight(ObjectUtils.isNotEmpty(store) ? store.getStoreWeight() : null)
.setStoreName(ObjectUtils.isNotEmpty(store) ? store.getStoreName() : "")
.setStoreAddress(ObjectUtils.isNotEmpty(store) ? store.getStoreAddress() : "")
.setContactPhone(ObjectUtils.isNotEmpty(store) ? store.getContactPhone() : "")
.setQqAccount(ObjectUtils.isNotEmpty(store) ? store.getQqAccount() : "")
.setWechatAccount(ObjectUtils.isNotEmpty(store) ? store.getWechatAccount() : "")
.setProdList(BeanUtil.copyToList(latestProdMap.get(storeId), PCStoreRecommendTempDTO.PCSRNewProdDTO.class)));
});
if (CollectionUtils.isEmpty(storeRecommendList)) {
return;
}
// 先按照 档口权重 倒序排,再按照 推荐权重 倒序排
storeRecommendList.sort(Comparator.comparing(PCStoreRecommendTempDTO::getStoreWeight, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(PCStoreRecommendTempDTO::getRecommendWeight, Comparator.nullsLast(Comparator.reverseOrder())));
// 返回给前端的数据 不包含 storeWeight 和 storeRecommnedWeight
List<PCStoreRecommendDTO> recommendList = BeanUtil.copyToList(storeRecommendList, PCStoreRecommendDTO.class);
// 放到redis中
redisCache.setCacheObject(CacheConstants.PC_STORE_RECOMMEND_LIST, recommendList);
}
/**
* 02:00
*/
@Transactional
public void dailyUpdateUserSearchHistory() {
@ -631,7 +603,7 @@ public class XktTask {
}
/**
* 2:05
* 02:05
*/
@Transactional
public void dailyUpdateUserBrowsingHistory() {
@ -666,7 +638,7 @@ public class XktTask {
}
/**
* 2:10redis
* 02:10 redis
*/
@Transactional
public void dailyUpdateSearchHotToRedis() {
@ -688,6 +660,43 @@ public class XktTask {
redisCache.setCacheObject(CacheConstants.SEARCH_HOT_KEY, top20Keys);
}
/**
* 02:30
*/
public void imgSearchTopProductStatistics() {
log.info("-------------统计图搜热款开始-------------");
pictureSearchService.cacheImgSearchTopProduct();
log.info("-------------统计图搜热款结束-------------");
}
/**
* 22:00:10 广 biddingTempStatusbiddingStatus
*/
@Transactional
public void updateAdvertRoundBiddingStatus() throws ParseException {
this.advertRoundService.updateBiddingStatus();
}
/**
*
*/
@Transactional
public void hourPublicStoreProduct() {
// TODO 定时任务发布商品同步到ES中
// TODO 定时任务发布商品同步到ES中
// TODO 定时任务发布商品同步到ES中
// 向ES索引: product_info 创建文档
// this.createESDoc(storeProd, storeProdDTO);
// 搜图服务同步
// sync2ImgSearchServer(storeProd.getId(), storeProdDTO.getFileList());
// 新增档口商品动态、关注档口用户 通知公告
}
/**
*
@ -855,24 +864,6 @@ public class XktTask {
log.info("-------------商品信息每日统计结束-------------");
}
/**
* 2:30
*/
public void imgSearchTopProductStatistics() {
log.info("-------------统计图搜热款开始-------------");
pictureSearchService.cacheImgSearchTopProduct();
log.info("-------------统计图搜热款结束-------------");
}
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
// TODO 每天凌晨12:05 将当天的推广加入到redis 中
/**
*

View File

@ -73,6 +73,10 @@ public class Advert extends XktBaseEntity {
*
*/
private Integer playNum;
/**
* 广 HH:mm:ss 22:30:00
*/
private String deadline;
/**
* 广ID
*/

View File

@ -67,6 +67,10 @@ public class AdvertRound extends XktBaseEntity {
*
*/
private Date endTime;
/**
* 广 HH:mm:ss 22:30:00
*/
private String deadline;
/**
* 广 A B C D E... advertplayNum
*/

View File

@ -30,14 +30,13 @@ public interface IAdvertRoundService {
* @param createDTO
* @return Integer
*/
Integer create(AdRoundStoreCreateDTO createDTO) throws ParseException;
Integer create(AdRoundStoreCreateDTO createDTO);
/**
* 广
*
* @throws ParseException
*/
void saveAdvertDeadlineToRedis() throws ParseException;
void saveAdvertDeadlineToRedis();
/**
* 广

View File

@ -311,6 +311,9 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
}
}
public static void main(String[] args) {
System.err.println(LocalDateTime.now().with(LocalTime.parse("22:00:00")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
/**
* 广
@ -329,7 +332,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
*/
@Override
@Transactional
public Integer create(AdRoundStoreCreateDTO createDTO) throws ParseException {
public Integer create(AdRoundStoreCreateDTO createDTO) {
// 截止时间都是当天 22:00:00并且只会处理马上播放的这一轮。比如 5.1-5.3当前为4.30处理这一轮当前为5.2处理这一轮当前为5.3(最后一天),处理下一轮。
if (DateUtils.getTime().compareTo(this.getDeadline(createDTO.getSymbol())) > 0) {
throw new ServiceException("竞价失败,已经有档口出价更高了噢!", HttpStatus.ERROR);
@ -639,25 +642,22 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
* a. 4.30 4.30 22:00
* b. 5.2 5.2 22:00:00
* c. 5.3redis
*
* @throws ParseException
*/
public void saveAdvertDeadlineToRedis() throws ParseException {
public void saveAdvertDeadlineToRedis() {
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
.eq(AdvertRound::getDelFlag, Constants.UNDELETED)
.in(AdvertRound::getLaunchStatus, Arrays.asList(AdLaunchStatus.LAUNCHING.getValue(), AdLaunchStatus.UN_LAUNCH.getValue())));
if (CollectionUtils.isEmpty(advertRoundList)) {
return;
}
// 当天截止的时间 yyyy-MM-dd 22:00:00
final String todayFilterTime = DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS).format(LocalDateTime.now().withHour(22).withMinute(0).withSecond(0));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS);
advertRoundList.stream().collect(Collectors.groupingBy(AdvertRound::getAdvertId))
.forEach((advertId, roundList) -> {
// 判断当前推广类型是否为 时间范围
final boolean isTimeRange = roundList.stream().anyMatch(x -> Objects.equals(x.getShowType(), AdShowType.TIME_RANGE.getValue()));
// 时间范围处理逻辑
if (isTimeRange) {
this.setTimeRangePatternDeadline(todayFilterTime, roundList);
this.setTimeRangePatternDeadline(formatter, roundList);
} else {
// 开始时间和结束时间相同,则代表是 只有一天 的枚举类型
List<AdvertRound> onceRoundList = roundList.stream().filter(x -> Objects.equals(x.getStartTime(), x.getEndTime())).collect(Collectors.toList());
@ -667,17 +667,19 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
if (CollectionUtils.isNotEmpty(onceRoundList)) {
// 将位置枚举的每一个symbol都存放到redis中
onceRoundList.stream().filter(x -> Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
.forEach(x -> redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(), todayFilterTime, 1, TimeUnit.DAYS));
.forEach(x -> redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(),
formatter.format(LocalDateTime.now().with(LocalTime.parse(x.getDeadline()))), 1, TimeUnit.DAYS));
onceRoundList.stream().filter(x -> !Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
.forEach(x -> {
// 推广开始时间的前一天的 22:00:00
LocalDateTime futureTimeFilter = x.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().minusDays(1).withHour(22).withMinute(0).withSecond(0);
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(), futureTimeFilter, 1, TimeUnit.DAYS);
// 推广开始时间的前一天的 截止时间
final String deadline = formatter.format(x.getStartTime().toInstant().atZone(ZoneId.systemDefault())
.toLocalDateTime().with(LocalTime.parse(x.getDeadline())));
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(), deadline, 1, TimeUnit.DAYS);
});
}
// 时间范围 + 位置枚举
if (CollectionUtils.isNotEmpty(timePositionList)) {
this.setTimeRangePatternDeadline(todayFilterTime, timePositionList);
this.setTimeRangePatternDeadline(formatter, timePositionList);
}
}
});
@ -686,21 +688,27 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
/**
* "时间范围" "时间范围 + 位置枚举"
*
* @param todayFilterTime 22:00
* @param roundList
* @param formatter formatter
* @param roundList
*/
private void setTimeRangePatternDeadline(String todayFilterTime, List<AdvertRound> roundList) {
private void setTimeRangePatternDeadline(DateTimeFormatter formatter, List<AdvertRound> roundList) {
// 第一轮(有可能为 位置枚举[多个] 或 时间范围[单个]
roundList.stream().filter(x -> Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue())).distinct().forEach(x ->
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(), todayFilterTime, 1, TimeUnit.DAYS));
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(),
formatter.format(LocalDateTime.now().with(LocalTime.parse(x.getDeadline()))), 1, TimeUnit.DAYS));
// 每一个推广位置的截止时间
Map<String, String> symbolDeadlineMap = roundList.stream().collect(Collectors.toMap(AdvertRound::getSymbol, AdvertRound::getDeadline, (s1, s2) -> s2));
// 第二轮之后的轮次过期时间都为开始时间前一天
Map<String, Date> roundSymbolMap = roundList.stream().filter(x -> !Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
.collect(Collectors.toMap(AdvertRound::getSymbol, AdvertRound::getStartTime, (s1, s2) -> s2));
if (MapUtils.isNotEmpty(roundSymbolMap)) {
roundSymbolMap.forEach((symbol, startTime) -> {
// 推广开始时间的前一天的 22:00:00
LocalDateTime futureTimeFilter = startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().minusDays(1).withHour(22).withMinute(0).withSecond(0);
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + symbol, futureTimeFilter, 1, TimeUnit.DAYS);
// 每一个symbol 的截止时间
String defaultDeadline = symbolDeadlineMap.getOrDefault(symbol, "22:00:00");
// 推广开始时间的前一天的 截止时间
final String deadline = formatter.format(startTime.toInstant().atZone(ZoneId.systemDefault())
.toLocalDateTime().with(LocalTime.parse(defaultDeadline)));
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + symbol, deadline, 1, TimeUnit.DAYS);
});
}
}
@ -712,7 +720,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
* @param symbol
* @return
*/
private String getDeadline(String symbol) throws ParseException {
private String getDeadline(String symbol) {
String deadline = redisCache.getCacheObject(ADVERT_DEADLINE_KEY + symbol);
if (StringUtils.isNotBlank(deadline)) {
return deadline;