Springboot 如何自动上传秒杀商品数据到Redis中上架商品

一、概述

如下图秒杀活动:

在这里插入图片描述
在这个秒杀活动中,需要自动上架一定时间段的商品,我们如何实现自动上传呢?

我们可以通过定时任务来实现的。在秒杀活动开始前,需要将商品信息存储到数据库中,并设置好库存和价格等信息。然后,可以通过定时任务的方式,每天定时从数据库中读取商品信息,并将其上传到秒杀页面上。这样,就可以实现自动上传商品的功能了。

二、Springboot定时任务配置

由于秒杀活动涉及的商品比较多,采用异步上传的方式

@EnableAsync
// 开启定时任务
@EnableScheduling
// 配置类
@Configuration
public class ScheduledConfig {}

三、秒杀表设计

1. 秒杀场次表,主要展示哪个时间段进行秒杀

在这里插入图片描述

CREATE TABLE `kmall_coupon`.`sms_seckill_session`  (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '场次名称',`start_time` datetime(0) NULL DEFAULT NULL COMMENT '每日开始时间',`end_time` datetime(0) NULL DEFAULT NULL COMMENT '每日结束时间',`status` tinyint(1) NULL DEFAULT NULL COMMENT '启用状态',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '秒杀活动场次' ROW_FORMAT = Dynamic;

2 秒杀商品表,主要存储哪个时间段内进行秒杀的商品

与场次关联的字段是 promotion_session_id
在这里插入图片描述

四、具体业务实现

1. 流程图

在这里插入图片描述

2. 开启定时任务,上传商品到redis

由于是异步开启定时任务,在上架商品时采用了redisson分布式锁机制

@Slf4j
@Service
public class SeckillScheduled {@AutowiredSeckillService seckillService;@AutowiredRedissonClient redissonClient;/*** 秒杀商品定时上架,保证幂等性问题*  每天晚上3点,上架最近三天需要秒杀的商品*  当天00:00:00 - 23:59:59*  明天00:00:00 - 23:59:59*  后天00:00:00 - 23:59:59*/@Scheduled(cron = "*/10 * * * * ? ")   public void uploadSeckillSkuLatest3Days() {// 重复上架无需处理log.info("上架秒杀的商品...");// 分布式锁(幂等性)RLock lock = redissonClient.getLock(SeckillConstant.UPLOAD_LOCK);try {lock.lock(10, TimeUnit.SECONDS);// 上架最近三天需要秒杀的商品seckillService.uploadSeckillSkuLatest3Days();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}

3. 上架最近三天需要秒杀的商品

  1. 查询最近三天需要参加秒杀的场次+商品
  2. 上架场次信息
  3. 上架商品信息
@Override
public void uploadSeckillSkuLatest3Days() {// 1.查询最近三天需要参加秒杀的场次+商品R lates3DaySession = couponFeignService.getLates3DaySession();if (lates3DaySession.getCode() == 0) {// 获取场次List<SeckillSessionWithSkusTO> sessions = lates3DaySession.getData("data", new TypeReference<List<SeckillSessionWithSkusTO>>() {});// 2.上架场次信息saveSessionInfos(sessions);// 3.上架商品信息saveSessionSkuInfo(sessions);}
}
2.1 查询最近三天需要参加秒杀的场次+商品
  1. 计算最近三天起止时间
  2. 查询起止时间内的秒杀场次
  3. 组合秒杀关联的商品信息
 @Overridepublic List<SeckillSessionEntity> getLates3DaySession() {// 计算最近三天起止时间String startTime = DateUtils.currentStartTime();// 当天00:00:00String endTime = DateUtils.getTimeByOfferset(2);// 后天23:59:59// 查询起止时间内的秒杀场次List<SeckillSessionEntity> sessions = baseMapper.selectList(new QueryWrapper<SeckillSessionEntity>().between("start_time", startTime, endTime));// 组合秒杀关联的商品信息if (!CollectionUtils.isEmpty(sessions)) {// 组合场次IDList<Long> sessionIds = sessions.stream().map(SeckillSessionEntity::getId).collect(Collectors.toList());// 查询秒杀场次关联商品信息Map<Long, List<SeckillSkuRelationEntity>> skuMap = seckillSkuRelationService.list(new QueryWrapper<SeckillSkuRelationEntity>().in("promotion_session_id", sessionIds)).stream().collect(Collectors.groupingBy(SeckillSkuRelationEntity::getPromotionSessionId));sessions.forEach(session -> session.setRelationSkus(skuMap.get(session.getId())));}return sessions;}
2.2 上架场次信息
  1. 遍历场次
  2. 判断场次是否已上架(幂等性)
  3. 封装场次信息
  4. 上架 redisTemplate.opsForList().leftPushAll(key, skuIds);
private void saveSessionInfos(List<SeckillSessionWithSkusTO> sessions) {if (!CollectionUtils.isEmpty(sessions)) {sessions.stream().forEach(session -> {// 1.遍历场次long startTime = session.getStartTime().getTime();// 场次开始时间戳long endTime = session.getEndTime().getTime();// 场次结束时间戳String key = SeckillConstant.SESSION_CACHE_PREFIX + startTime + "_" + endTime;// 场次的key// 2.判断场次是否已上架(幂等性)Boolean hasKey = redisTemplate.hasKey(key);if (!hasKey) {// 未上架// 3.封装场次信息List<String> skuIds = session.getRelationSkus().stream().map(item -> item.getPromotionSessionId() + "_" + item.getSkuId().toString()).collect(Collectors.toList());// skuId集合// 4.上架redisTemplate.opsForList().leftPushAll(key, skuIds);}});}}
2.3 上架商品信息
  1. 查询所有商品信息
  2. 将查询结果封装成Map集合
  3. 绑定秒杀商品hash
  4. 遍历场次,遍历商品,判断商品是否已上架(幂等性)
  5. 封装商品信息
  6. 上架商品(序列化成json格式存入Redis中)
  7. 上架商品的分布式信号量,key:商品随机码 值:库存(限流)
private void saveSessionSkuInfo(List<SeckillSessionWithSkusTO> sessions) {if (!CollectionUtils.isEmpty(sessions)) {// 查询所有商品信息List<Long> skuIds = new ArrayList<>();sessions.stream().forEach(session -> {List<Long> ids = session.getRelationSkus().stream().map(SeckillSkuVO::getSkuId).collect(Collectors.toList());skuIds.addAll(ids);});R info = productFeignService.getSkuInfos(skuIds);if (info.getCode() == 0) {// 将查询结果封装成Map集合Map<Long, SkuInfoTO> skuMap = info.getData(new TypeReference<List<SkuInfoTO>>() {}).stream().collect(Collectors.toMap(SkuInfoTO::getSkuId, val -> val));// 绑定秒杀商品hashBoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(SeckillConstant.SECKILL_CHARE_KEY);// 1.遍历场次sessions.stream().forEach(session -> {// 2.遍历商品session.getRelationSkus().stream().forEach(seckillSku -> {// 判断商品是否已上架(幂等性)String skuKey = seckillSku.getPromotionSessionId().toString() + "_" + seckillSku.getSkuId().toString();// 商品的key(需要添加场次ID前缀,同一款商品可能场次不同)if (!operations.hasKey(skuKey)) {// 未上架// 3.封装商品信息SeckillSkuRedisTO redisTo = new SeckillSkuRedisTO();// 存储到redis的对象SkuInfoTO sku = skuMap.get(seckillSku.getSkuId());BeanUtils.copyProperties(seckillSku, redisTo);// 商品秒杀信息redisTo.setSkuInfo(sku);// 商品详细信息redisTo.setStartTime(session.getStartTime().getTime());// 秒杀开始时间redisTo.setEndTime(session.getEndTime().getTime());// 秒杀结束时间// 商品随机码:用户参与秒杀时,请求需要带上随机码(防止恶意攻击)String token = UUID.randomUUID().toString().replace("-", "");// 商品随机码(随机码只会在秒杀开始时暴露)redisTo.setRandomCode(token);// 设置商品随机码// 4.上架商品(序列化成json格式存入Redis中)String jsonString = JSONObject.toJSONString(redisTo);operations.put(skuKey, jsonString);// 5.上架商品的分布式信号量,key:商品随机码 值:库存(限流)RSemaphore semaphore = redissonClient.getSemaphore(SeckillConstant.SKU_STOCK_SEMAPHORE + token);// 信号量(扣减成功才进行后续操作,否则快速返回)semaphore.trySetPermits(seckillSku.getSeckillCount());}});});}}}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部