From 136715846c7207bcffea12b41d63d3d490e06d61 Mon Sep 17 00:00:00 2001 From: JIAN Date: Thu, 26 Sep 2024 23:31:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(market):=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jzo2o/market/handler/XxlJobHandler.java | 111 +----------------- .../market/service/IActivityService.java | 10 ++ .../jzo2o/market/service/ICouponService.java | 5 + .../service/impl/ActivityServiceImpl.java | 76 ++++++++++++ .../service/impl/CouponServiceImpl.java | 41 +++++++ 5 files changed, 138 insertions(+), 105 deletions(-) diff --git a/jzo2o-market/src/main/java/com/jzo2o/market/handler/XxlJobHandler.java b/jzo2o-market/src/main/java/com/jzo2o/market/handler/XxlJobHandler.java index fe3a9ac..6cdb8cc 100644 --- a/jzo2o-market/src/main/java/com/jzo2o/market/handler/XxlJobHandler.java +++ b/jzo2o-market/src/main/java/com/jzo2o/market/handler/XxlJobHandler.java @@ -1,30 +1,14 @@ package com.jzo2o.market.handler; -import com.jzo2o.common.expcetions.DBException; -import com.jzo2o.common.utils.BeanUtils; -import com.jzo2o.common.utils.CollUtils; -import com.jzo2o.common.utils.JsonUtils; -import com.jzo2o.market.constants.RedisConstants; -import com.jzo2o.market.enums.ActivityStatusEnum; -import com.jzo2o.market.enums.CouponStatusEnum; -import com.jzo2o.market.model.domain.Activity; -import com.jzo2o.market.model.domain.Coupon; -import com.jzo2o.market.model.dto.response.SeizeCouponInfoResDTO; import com.jzo2o.market.service.IActivityService; import com.jzo2o.market.service.ICouponService; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; /** * XxlJob任务处理器 @@ -50,65 +34,18 @@ public class XxlJobHandler { @Transactional public void updateActivityStatus() { log.info("自动修改活动状态任务开始"); - LocalDateTime nowTime = LocalDateTime.now(); - List activityList = activityService.lambdaQuery() - .eq(Activity::getStatus, ActivityStatusEnum.NO_DISTRIBUTE) - .or().eq(Activity::getStatus, ActivityStatusEnum.DISTRIBUTING) - // 不处理还没有发生的活动 - .le(Activity::getDistributeStartTime, nowTime) - .list(); - - if (CollUtils.isEmpty(activityList)) { - return; - } - - for (Activity activity : activityList) { - if (activity.getDistributeEndTime().isBefore(nowTime)) { - // 活动结束 - if (!activityService.lambdaUpdate() - .eq(Activity::getId, activity.getId()) - .set(Activity::getStatus, ActivityStatusEnum.LOSE_EFFICACY) - .update()) { - throw new DBException("更新活动状态失败"); - } - } else if (activity.getDistributeStartTime().isBefore(nowTime)) { - // 活动开始 - if (!activityService.lambdaUpdate() - .eq(Activity::getId, activity.getId()) - .set(Activity::getStatus, ActivityStatusEnum.DISTRIBUTING) - .update()) { - throw new DBException("更新活动状态失败"); - } - } - } + activityService.updateActivityStatus(); + log.info("自动修改活动状态任务完成"); } /** * 自动过期已领取优惠券(1小时1次) */ @XxlJob("processExpireCoupon") - @Transactional public void processExpireCoupon() { log.info("自动过期已领取优惠券任务开始"); - List couponList = couponService.lambdaQuery() - .eq(Coupon::getStatus, CouponStatusEnum.NO_USE) - // 不处理还没到有效期的优惠卷 - .le(Coupon::getValidityTime, LocalDateTime.now()) - .select(Coupon::getId) - .list(); - - if (CollUtils.isEmpty(couponList)) { - return; - } - - // 设置状态已失效 - couponList = couponList.stream() - .map(coupon -> coupon.setStatus(CouponStatusEnum.INVALID)) - .collect(Collectors.toList()); - - if (!couponService.updateBatchById(couponList)) { - throw new DBException("更新优惠卷状态失败"); - } + couponService.invalidExpiredCoupon(); + log.info("自动过期已领取优惠券任务完成"); } /** @@ -117,43 +54,7 @@ public class XxlJobHandler { @XxlJob("activityPreheat") public void activityPreheat() { log.info("自动预热1个月内的活动任务开始"); - LocalDateTime nowTime = LocalDateTime.now(); - - // 获取近一个月未开始/已开始的活动 - @SuppressWarnings("unchecked") - List activityList = activityService.lambdaQuery() - .le(Activity::getDistributeStartTime, nowTime.plusDays(30)) - .in(Activity::getStatus, Arrays.asList(ActivityStatusEnum.NO_DISTRIBUTE, ActivityStatusEnum.DISTRIBUTING)) - .orderByAsc(Activity::getDistributeStartTime) - .list(); - - if (CollUtils.isEmpty(activityList)) { - activityList = new ArrayList<>(); - } - - List couponInfoList = activityList.stream() - .map(activity -> BeanUtils - .toBean(activity, SeizeCouponInfoResDTO.class) - .setRemainNum(activity.getStockNum()) - .setType(activity.getType().getType())) - .collect(Collectors.toList()); - String couponInfoListJsonStr = JsonUtils.toJsonStr(couponInfoList); - - // 缓存活动列表 - redisTemplate - .opsForValue() - .set(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST, couponInfoListJsonStr); - - // 缓存优惠卷库存 - HashOperations hashOperations = redisTemplate.opsForHash(); - couponInfoList.forEach(coupon -> { - String key = String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK, coupon.getId() % 10); - // 防止进行中的活动的库存被覆盖 - if (coupon.getStatus() == ActivityStatusEnum.NO_DISTRIBUTE) { - hashOperations.put(key, coupon.getId(), coupon.getRemainNum()); - } else { - hashOperations.putIfAbsent(key, coupon.getId(), coupon.getRemainNum()); - } - }); + activityService.cacheComingActivity(); + log.info("自动预热1个月内的活动任务完成"); } } \ No newline at end of file diff --git a/jzo2o-market/src/main/java/com/jzo2o/market/service/IActivityService.java b/jzo2o-market/src/main/java/com/jzo2o/market/service/IActivityService.java index b7d8573..6719f28 100644 --- a/jzo2o-market/src/main/java/com/jzo2o/market/service/IActivityService.java +++ b/jzo2o-market/src/main/java/com/jzo2o/market/service/IActivityService.java @@ -46,6 +46,16 @@ public interface IActivityService extends IService { */ void revoke(Long id); + /** + * 根据当前时间更新活动状态 + */ + void updateActivityStatus(); + + /** + * 缓存近1个月生效的活动信息 + */ + void cacheComingActivity(); + /** * 用户端进行抢卷操作 */ diff --git a/jzo2o-market/src/main/java/com/jzo2o/market/service/ICouponService.java b/jzo2o-market/src/main/java/com/jzo2o/market/service/ICouponService.java index 72c5348..c8fe82a 100644 --- a/jzo2o-market/src/main/java/com/jzo2o/market/service/ICouponService.java +++ b/jzo2o-market/src/main/java/com/jzo2o/market/service/ICouponService.java @@ -28,4 +28,9 @@ public interface ICouponService extends IService { * 用户端滚动查询(抢卷时间降序) */ List getCurrentUserCoupon(CouponStatusEnum status, Integer lastId); + + /** + * 更新过期优惠卷的状态为失效中 + */ + void invalidExpiredCoupon(); } \ No newline at end of file diff --git a/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/ActivityServiceImpl.java b/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/ActivityServiceImpl.java index c3aa948..7b499d1 100644 --- a/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/ActivityServiceImpl.java +++ b/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/ActivityServiceImpl.java @@ -14,6 +14,7 @@ import com.jzo2o.common.model.PageResult; import com.jzo2o.common.utils.BeanUtils; import com.jzo2o.common.utils.CollUtils; import com.jzo2o.common.utils.JsonUtils; +import com.jzo2o.market.constants.RedisConstants; import com.jzo2o.market.enums.ActivityStatusEnum; import com.jzo2o.market.enums.CouponStatusEnum; import com.jzo2o.market.enums.CouponTypeEnum; @@ -29,7 +30,9 @@ import com.jzo2o.market.service.IActivityService; import com.jzo2o.market.service.ICouponService; import com.jzo2o.mvc.utils.UserContext; import com.jzo2o.mysql.utils.PageUtils; +import com.jzo2o.redis.properties.RedisSyncProperties; import com.jzo2o.redis.utils.RedisSyncQueueUtils; +import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Service; @@ -178,6 +181,79 @@ public class ActivityServiceImpl extends ServiceImpl i .update(); } + @Override + public void updateActivityStatus() { + LocalDateTime nowTime = LocalDateTime.now(); + List activityList = lambdaQuery() + .in(Activity::getStatus, ActivityStatusEnum.NO_DISTRIBUTE, ActivityStatusEnum.DISTRIBUTING) + // 不处理还没有发生的活动 + .le(Activity::getDistributeStartTime, nowTime) + .list(); + + if (CollUtils.isEmpty(activityList)) { + return; + } + + for (Activity activity : activityList) { + ActivityStatusEnum status; + if (activity.getDistributeEndTime().isBefore(nowTime)) { + // 活动结束 + status = ActivityStatusEnum.LOSE_EFFICACY; + } else if (activity.getDistributeStartTime().isBefore(nowTime)) { + // 活动开始 + status = ActivityStatusEnum.DISTRIBUTING; + } else { + continue; + } + + if (!lambdaUpdate() + .eq(Activity::getId, activity.getId()) + .set(Activity::getStatus, status) + .update()) { + throw new DBException("更新活动状态失败"); + } + } + } + + @Override + public void cacheComingActivity() { + LocalDateTime nowTime = LocalDateTime.now(); + + // 获取近一个月未开始/已开始的活动 + @SuppressWarnings("unchecked") + List activityList = lambdaQuery() + .le(Activity::getDistributeStartTime, nowTime.plusDays(30)) + .in(Activity::getStatus, Arrays.asList(ActivityStatusEnum.NO_DISTRIBUTE, ActivityStatusEnum.DISTRIBUTING)) + .orderByAsc(Activity::getDistributeStartTime) + .list(); + + if (CollUtils.isEmpty(activityList)) { + activityList = new ArrayList<>(); + } + + List couponInfoList = activityList.stream() + .map(activity -> BeanUtils + .toBean(activity, SeizeCouponInfoResDTO.class) + .setRemainNum(activity.getStockNum()) + .setType(activity.getType().getType())) + .collect(Collectors.toList()); + + // 缓存活动列表 + redisTemplate.opsForValue().set(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST, JsonUtils.toJsonStr(couponInfoList)); + + // 缓存优惠卷库存 + HashOperations hashOperations = redisTemplate.opsForHash(); + couponInfoList.forEach(coupon -> { + String key = String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK, coupon.getId() % redisSyncProperties.getQueueNum()); + // 防止进行中的活动的库存被覆盖 + if (coupon.getStatus() == ActivityStatusEnum.NO_DISTRIBUTE) { + hashOperations.put(key, coupon.getId(), coupon.getRemainNum()); + } else { + hashOperations.putIfAbsent(key, coupon.getId(), coupon.getRemainNum()); + } + }); + } + @Override public void seizeCoupon(SeizeCouponReqDTO seizeCouponReqDTO) { Long activityId = seizeCouponReqDTO.getId(); diff --git a/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/CouponServiceImpl.java b/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/CouponServiceImpl.java index c82151e..f1a4511 100644 --- a/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/CouponServiceImpl.java +++ b/jzo2o-market/src/main/java/com/jzo2o/market/service/impl/CouponServiceImpl.java @@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.jzo2o.api.customer.CommonUserApi; import com.jzo2o.common.expcetions.BadRequestException; +import com.jzo2o.common.expcetions.DBException; import com.jzo2o.common.model.PageResult; import com.jzo2o.common.utils.BeanUtils; import com.jzo2o.common.utils.CollUtils; @@ -15,12 +17,18 @@ import com.jzo2o.market.model.domain.Coupon; import com.jzo2o.market.model.dto.request.CouponPageQueryDTO; import com.jzo2o.market.model.dto.response.CouponPageInfoResDTO; import com.jzo2o.market.model.dto.response.CouponSimpleInfoResDTO; +import com.jzo2o.market.service.IActivityService; import com.jzo2o.market.service.ICouponService; import com.jzo2o.mvc.utils.UserContext; import com.jzo2o.mysql.utils.PageUtils; +import com.jzo2o.redis.properties.RedisSyncProperties; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -35,6 +43,15 @@ import java.util.stream.Collectors; @Service @Slf4j public class CouponServiceImpl extends ServiceImpl implements ICouponService { + @Resource + private RedisTemplate redisTemplate; + @Resource + private RedisSyncProperties redisSyncProperties; + @Resource + private CommonUserApi commonUserApi; + @Resource + private IActivityService activityService; + @Override public PageResult page(CouponPageQueryDTO couponPageQueryDTO) { if (ObjectUtils.isEmpty(couponPageQueryDTO.getActivityId())) { @@ -69,4 +86,28 @@ public class CouponServiceImpl extends ServiceImpl impleme .map(coupon -> BeanUtils.toBean(coupon, CouponSimpleInfoResDTO.class)) .collect(Collectors.toList()); } + + @Override + @Transactional + public void invalidExpiredCoupon() { + List couponList = lambdaQuery() + .eq(Coupon::getStatus, CouponStatusEnum.NO_USE) + // 不处理还没到有效期的优惠卷 + .le(Coupon::getValidityTime, LocalDateTime.now()) + .select(Coupon::getId) + .list(); + + if (CollUtils.isEmpty(couponList)) { + return; + } + + // 设置状态已失效 + couponList = couponList.stream() + .map(coupon -> coupon.setStatus(CouponStatusEnum.INVALID)) + .collect(Collectors.toList()); + + if (!this.updateBatchById(couponList)) { + throw new DBException("更新优惠卷状态失败"); + } + } } \ No newline at end of file