feat(market):新增持久化Redis抢卷信息的功能

This commit is contained in:
JIAN 2024-09-27 20:20:40 +08:00
parent 136715846c
commit 31b5b7c8f2
7 changed files with 129 additions and 12 deletions

View File

@ -0,0 +1,28 @@
package com.jzo2o.market.config;
import com.jzo2o.redis.properties.RedisSyncProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 线程池配置
* @author JIAN
*/
@Configuration
public class TreadPoolConfiguration {
@Bean("syncThreadPool")
public ThreadPoolExecutor threadPoolExecutor(RedisSyncProperties redisSyncProperties) {
return new ThreadPoolExecutor(
1,
redisSyncProperties.getQueueNum(),
120,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadPoolExecutor.DiscardPolicy()
);
}
}

View File

@ -0,0 +1,38 @@
package com.jzo2o.market.handler;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.redis.handler.SyncProcessHandler;
import com.jzo2o.redis.model.SyncMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.COUPON_SEIZE_SYNC_QUEUE_NAME;
/**
* 抢卷同步队列同步处理器
* @author JIAN
*/
@Slf4j
@Component(COUPON_SEIZE_SYNC_QUEUE_NAME)
public class SeizeCouponSyncProcessHandler implements SyncProcessHandler<Object> {
@Resource
private ICouponService couponService;
@Override
public void batchProcess(List<SyncMessage<Object>> multiData) {
throw new CommonException("不支持批量处理");
}
@Override
public void singleProcess(SyncMessage<Object> singleData) {
long userId = Long.parseLong(singleData.getKey());
long activityId = Long.parseLong(singleData.getValue().toString());
log.info("同步优惠卷信息, 活动id: {}, 用户id: {}", activityId, userId);
couponService.syncCouponRecord(activityId, userId);
}
}

View File

@ -2,14 +2,20 @@ package com.jzo2o.market.handler;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.redis.sync.SyncManager;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.COUPON_SEIZE_SYNC_QUEUE_NAME;
import static com.jzo2o.redis.constants.RedisSyncQueueConstants.MODE_SINGLE;
import static com.jzo2o.redis.constants.RedisSyncQueueConstants.STORAGE_TYPE_HASH;
/**
* XxlJob任务处理器
* @author JIAN
@ -22,8 +28,10 @@ public class XxlJobHandler {
private IActivityService activityService;
@Resource
private ICouponService couponService;
@Resource(name = "syncThreadPool")
private ThreadPoolExecutor syncThreadPool;
@Resource
private RedisTemplate<String, Object> redisTemplate;
private SyncManager syncManager;
/**
* 自动修改活动状态(1分钟1次)
@ -57,4 +65,13 @@ public class XxlJobHandler {
activityService.cacheComingActivity();
log.info("自动预热1个月内的活动任务完成");
}
/**
* 自动从Redis同步抢卷结果到数据库
*/
@XxlJob("seizeCouponSyncJob")
public void seizeCouponSyncJob() {
log.info("自动从Redis同步抢卷结果到数据库任务开始");
syncManager.start(COUPON_SEIZE_SYNC_QUEUE_NAME, STORAGE_TYPE_HASH, MODE_SINGLE, syncThreadPool);
}
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.jzo2o.market.enums.CouponTypeEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -20,6 +21,7 @@ import java.time.LocalDateTime;
* @since 2023-09-16
*/
@Data
@Builder
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class Coupon implements Serializable {
@ -29,7 +31,7 @@ public class Coupon implements Serializable {
/**
* 优惠券id
*/
@TableId(value = "id", type = IdType.NONE)
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**

View File

@ -1,6 +1,7 @@
package com.jzo2o.market.model.dto.response;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.enums.CouponTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -25,7 +26,7 @@ public class CouponSimpleInfoResDTO implements Serializable {
private Long activityId;
@ApiModelProperty(value = "使用类型1满减2折扣", required = true)
private Integer type;
private CouponTypeEnum type;
@ApiModelProperty(value = "折扣")
private Integer discountRate;

View File

@ -12,9 +12,8 @@ import java.util.List;
/**
* <p>
* 服务类
* 服务类
* </p>
*
* @author itcast
* @since 2023-09-16
*/
@ -33,4 +32,9 @@ public interface ICouponService extends IService<Coupon> {
* 更新过期优惠卷的状态为失效中
*/
void invalidExpiredCoupon();
/**
* 同步优惠卷数据到数据库中
*/
void syncCouponRecord(long activityId, long userId);
}

View File

@ -6,6 +6,7 @@ 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.api.customer.dto.response.CommonUserResDTO;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
@ -13,6 +14,7 @@ import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.CouponMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.CouponPageQueryDTO;
import com.jzo2o.market.model.dto.response.CouponPageInfoResDTO;
@ -21,9 +23,7 @@ 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;
@ -43,10 +43,6 @@ 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
@ -110,4 +106,35 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
throw new DBException("更新优惠卷状态失败");
}
}
@Override
@Transactional
public void syncCouponRecord(long activityId, long userId) {
Activity activity = activityService.getById(activityId);
CommonUserResDTO user = commonUserApi.findById(userId);
if (!this.save(Coupon.builder()
.userId(userId)
.userName(user.getNickname())
.userPhone(user.getPhone())
.activityId(activityId)
.name(activity.getName())
.type(activity.getType())
.amountCondition(activity.getAmountCondition())
.discountRate(activity.getDiscountRate())
.discountAmount(activity.getDiscountAmount())
.validityTime(LocalDateTime.now().plusDays(activity.getValidityDays()))
.status(CouponStatusEnum.NO_USE)
.build())) {
throw new DBException("插入优惠卷表失败");
}
if (!activityService.lambdaUpdate()
.setSql("stock_num = stock_num - 1")
.eq(Activity::getId, activityId)
.gt(Activity::getStockNum, 0)
.update()) {
throw new DBException("更新活动库存失败");
}
}
}