refactor(market):优化代码结构

This commit is contained in:
JIAN
2024-09-26 23:31:08 +08:00
parent c09e6bf680
commit 136715846c
5 changed files with 138 additions and 105 deletions

View File

@@ -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<Activity> 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<Coupon> 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<Activity> 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<SeizeCouponInfoResDTO> 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<String, Object, Object> 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个月内的活动任务完成");
}
}

View File

@@ -46,6 +46,16 @@ public interface IActivityService extends IService<Activity> {
*/
void revoke(Long id);
/**
* 根据当前时间更新活动状态
*/
void updateActivityStatus();
/**
* 缓存近1个月生效的活动信息
*/
void cacheComingActivity();
/**
* 用户端进行抢卷操作
*/

View File

@@ -28,4 +28,9 @@ public interface ICouponService extends IService<Coupon> {
* 用户端滚动查询(抢卷时间降序)
*/
List<CouponSimpleInfoResDTO> getCurrentUserCoupon(CouponStatusEnum status, Integer lastId);
/**
* 更新过期优惠卷的状态为失效中
*/
void invalidExpiredCoupon();
}

View File

@@ -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<ActivityMapper, Activity> i
.update();
}
@Override
public void updateActivityStatus() {
LocalDateTime nowTime = LocalDateTime.now();
List<Activity> 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<Activity> 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<SeizeCouponInfoResDTO> 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<String, Object, Object> 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();

View File

@@ -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<CouponMapper, Coupon> implements ICouponService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private RedisSyncProperties redisSyncProperties;
@Resource
private CommonUserApi commonUserApi;
@Resource
private IActivityService activityService;
@Override
public PageResult<CouponPageInfoResDTO> page(CouponPageQueryDTO couponPageQueryDTO) {
if (ObjectUtils.isEmpty(couponPageQueryDTO.getActivityId())) {
@@ -69,4 +86,28 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
.map(coupon -> BeanUtils.toBean(coupon, CouponSimpleInfoResDTO.class))
.collect(Collectors.toList());
}
@Override
@Transactional
public void invalidExpiredCoupon() {
List<Coupon> 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("更新优惠卷状态失败");
}
}
}