package com.alatus.mall.seckill.service.impl; import com.alatus.common.utils.R; import com.alatus.mall.seckill.constant.SecKillConstants; import com.alatus.mall.seckill.feign.CouponFeignService; import com.alatus.mall.seckill.feign.ProductFeignService; import com.alatus.mall.seckill.service.SecKillService; import com.alatus.mall.seckill.to.SecKillSkuRedisTo; import com.alatus.mall.seckill.vo.SeckillSessionEntityVo; import com.alatus.mall.seckill.vo.SkuInfoVo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import org.redisson.api.RSemaphore; import org.redisson.api.RedissonClient; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @Service public class SecKillServiceImpl implements SecKillService { @Autowired private CouponFeignService couponFeignService; @Autowired private ProductFeignService productFeignService; @Autowired private RedisTemplate redisTemplate; @Autowired private RedissonClient redissonClient; @Override public void uploadSecKillSkuLatest3Days() { // 扫描需要参与秒杀的活动 R daySession = couponFeignService.Latest3DaySession(); if(daySession.getCode() == 0){ List<SeckillSessionEntityVo> seckillSessionVos = daySession.getData(new TypeReference<List<SeckillSessionEntityVo>>() {}); // 缓存活动信息 saveSessionInfos(seckillSessionVos); // 缓存活动关联的商品信息 } } private void saveSessionInfos(List<SeckillSessionEntityVo> seckillSessionVos) { seckillSessionVos.stream().forEach(session -> { //获取秒杀活动开始和结束时间 long start = session.getStartTime().getTime(); long end = session.getEndTime().getTime(); String key = SecKillConstants.SESSION_CACHE_PREFIX+session.getId()+session.getName()+start+"-"+end; List<String> ids = session.getRelationSkus().stream().map(item->item.getId().toString()).collect(Collectors.toList()); // 保存活动的信息 redisTemplate.opsForList().leftPushAll(key,ids); }); } private void saveSkuInfos(List<SeckillSessionEntityVo> seckillSessionVos){ seckillSessionVos.forEach(session -> { // 准备哈希操作 BoundHashOperations ops = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX); session.getRelationSkus().forEach(relationSku -> { // 缓存商品 SecKillSkuRedisTo secKillSkuRedisTo = new SecKillSkuRedisTo(); // sku的基本信息 R info = productFeignService.getSkuInfo(relationSku.getSkuId()); if(info.getCode() == 0){ SkuInfoVo skuInfoVo = info.get("skuInfo",new TypeReference<SkuInfoVo>(){}); secKillSkuRedisTo.setSkuInfo(skuInfoVo); } // sku的秒杀信息 BeanUtils.copyProperties(relationSku,secKillSkuRedisTo); // 设置秒杀的时间 secKillSkuRedisTo.setStartTime(session.getStartTime().getTime()); secKillSkuRedisTo.setEndTime(session.getEndTime().getTime()); // 设置随机码 String token = UUID.randomUUID().toString().replace("-", ""); secKillSkuRedisTo.setRandomCode(token); // 设置信号量 RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + token); semaphore.trySetPermits(relationSku.getSeckillCount()); String skuJson = JSON.toJSONString(secKillSkuRedisTo); ops.put(relationSku.getId().toString(),skuJson); }); }); } }
package com.alatus.mall.seckill.service.impl; import com.alatus.common.utils.R; import com.alatus.mall.seckill.constant.SecKillConstants; import com.alatus.mall.seckill.feign.CouponFeignService; import com.alatus.mall.seckill.feign.ProductFeignService; import com.alatus.mall.seckill.service.SecKillService; import com.alatus.mall.seckill.to.SecKillSkuRedisTo; import com.alatus.mall.seckill.vo.SeckillSessionEntityVo; import com.alatus.mall.seckill.vo.SkuInfoVo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import org.redisson.api.RSemaphore; import org.redisson.api.RedissonClient; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @Service public class SecKillServiceImpl implements SecKillService { @Autowired private CouponFeignService couponFeignService; @Autowired private ProductFeignService productFeignService; @Autowired private RedisTemplate redisTemplate; @Autowired private RedissonClient redissonClient; @Override public void uploadSecKillSkuLatest3Days() { // 扫描需要参与秒杀的活动 R daySession = couponFeignService.Latest3DaySession(); if(daySession.getCode() == 0){ List<SeckillSessionEntityVo> seckillSessionVos = daySession.getData(new TypeReference<List<SeckillSessionEntityVo>>() {}); // 缓存活动信息 saveSessionInfos(seckillSessionVos); // 缓存活动关联的商品信息 } } private void saveSessionInfos(List<SeckillSessionEntityVo> seckillSessionVos) { seckillSessionVos.stream().forEach(session -> { //获取秒杀活动开始和结束时间 long start = session.getStartTime().getTime(); long end = session.getEndTime().getTime(); String key = SecKillConstants.SESSION_CACHE_PREFIX+session.getId()+session.getName()+start+"-"+end; List<String> ids = session.getRelationSkus().stream().map(item->item.getId().toString()).collect(Collectors.toList()); // 保存活动的信息 redisTemplate.opsForList().leftPushAll(key,ids); }); } private void saveSkuInfos(List<SeckillSessionEntityVo> seckillSessionVos){ seckillSessionVos.forEach(session -> { // 准备哈希操作 BoundHashOperations ops = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX); session.getRelationSkus().forEach(relationSku -> { // 缓存商品 SecKillSkuRedisTo secKillSkuRedisTo = new SecKillSkuRedisTo(); // sku的基本信息 R info = productFeignService.getSkuInfo(relationSku.getSkuId()); if(info.getCode() == 0){ SkuInfoVo skuInfoVo = info.get("skuInfo",new TypeReference<SkuInfoVo>(){}); secKillSkuRedisTo.setSkuInfo(skuInfoVo); } // sku的秒杀信息 BeanUtils.copyProperties(relationSku,secKillSkuRedisTo); // 设置秒杀的时间 secKillSkuRedisTo.setStartTime(session.getStartTime().getTime()); secKillSkuRedisTo.setEndTime(session.getEndTime().getTime()); // 设置随机码 String token = UUID.randomUUID().toString().replace("-", ""); secKillSkuRedisTo.setRandomCode(token); // 设置信号量 RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + token); semaphore.trySetPermits(relationSku.getSeckillCount()); String skuJson = JSON.toJSONString(secKillSkuRedisTo); ops.put(relationSku.getId().toString(),skuJson); }); }); } }
package com.alatus.mall.coupon.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.math.BigDecimal; import java.io.Serializable; import java.util.Date; import lombok.Data; /** * 秒杀活动商品关联 * * @author alatus * @date 2024-03-12 13:32:54 */ @Data @TableName("sms_seckill_sku_relation") public class SeckillSkuRelationEntity implements Serializable { private static final long serialVersionUID = 1L; /** * id */ @TableId private Long id; /** * 活动id */ private Long promotionId; /** * 活动场次id */ private Long promotionSessionId; /** * 商品id */ private Long skuId; /** * 秒杀价格 */ private BigDecimal seckillPrice; /** * 秒杀总量 */ private Integer seckillCount; /** * 每人限购数量 */ private BigDecimal seckillLimit; /** * 排序 */ private Integer seckillSort; }
package com.alatus.mall.coupon.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.math.BigDecimal; import java.io.Serializable; import java.util.Date; import lombok.Data; /** * 秒杀活动商品关联 * * @author alatus * @date 2024-03-12 13:32:54 */ @Data @TableName("sms_seckill_sku_relation") public class SeckillSkuRelationEntity implements Serializable { private static final long serialVersionUID = 1L; /** * id */ @TableId private Long id; /** * 活动id */ private Long promotionId; /** * 活动场次id */ private Long promotionSessionId; /** * 商品id */ private Long skuId; /** * 秒杀价格 */ private BigDecimal seckillPrice; /** * 秒杀总量 */ private Integer seckillCount; /** * 每人限购数量 */ private BigDecimal seckillLimit; /** * 排序 */ private Integer seckillSort; }
package com.alatus.mall.seckill.vo; import lombok.Data; import java.math.BigDecimal; @Data public class SeckillSkuRelationEntityVo { private Long id; /** * 活动id */ private Long promotionId; /** * 活动场次id */ private Long promotionSessionId; /** * 商品id */ private Long skuId; /** * 秒杀价格 */ private BigDecimal seckillPrice; /** * 秒杀总量 */ private Integer seckillCount; /** * 每人限购数量 */ private BigDecimal seckillLimit; /** * 排序 */ private Integer seckillSort; }
package com.alatus.mall.seckill.vo; import lombok.Data; import java.math.BigDecimal; @Data public class SeckillSkuRelationEntityVo { private Long id; /** * 活动id */ private Long promotionId; /** * 活动场次id */ private Long promotionSessionId; /** * 商品id */ private Long skuId; /** * 秒杀价格 */ private BigDecimal seckillPrice; /** * 秒杀总量 */ private Integer seckillCount; /** * 每人限购数量 */ private BigDecimal seckillLimit; /** * 排序 */ private Integer seckillSort; }
package com.alatus.mall.seckill.vo; import lombok.Data; import java.util.Date; import java.util.List; @Data public class SeckillSessionEntityVo { private Long id; /** * 场次名称 */ private String name; /** * 每日开始时间 */ private Date startTime; /** * 每日结束时间 */ private Date endTime; /** * 启用状态 */ private Integer status; /** * 创建时间 */ private Date createTime; private List<SeckillSkuRelationEntityVo> relationSkus; }
package com.alatus.mall.seckill.vo; import lombok.Data; import java.util.Date; import java.util.List; @Data public class SeckillSessionEntityVo { private Long id; /** * 场次名称 */ private String name; /** * 每日开始时间 */ private Date startTime; /** * 每日结束时间 */ private Date endTime; /** * 启用状态 */ private Integer status; /** * 创建时间 */ private Date createTime; private List<SeckillSkuRelationEntityVo> relationSkus; }
package com.alatus.mall.seckill.constant; public class SecKillConstants { // 秒杀活动缓存前缀 public static final String SESSION_CACHE_PREFIX = "seckill:sessions:"; // 秒杀商品缓存前缀 public static final String SKU_CACHE_PREFIX = "seckill:skus"; // 库存信号量前缀 public static final String SKU_STOCK_SEMAPHORE = "seckill:stock:"; }
package com.alatus.mall.seckill.constant; public class SecKillConstants { // 秒杀活动缓存前缀 public static final String SESSION_CACHE_PREFIX = "seckill:sessions:"; // 秒杀商品缓存前缀 public static final String SKU_CACHE_PREFIX = "seckill:skus"; // 库存信号量前缀 public static final String SKU_STOCK_SEMAPHORE = "seckill:stock:"; }
package com.alatus.mall.seckill.config; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RedissonConfig { @Bean(destroyMethod = "shutdown") public RedissonClient redissonClient(){ // 所有对redisson的操作都是通过这个client对象 Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.56.10:6379"); return Redisson.create(config); } }
package com.alatus.mall.seckill.config; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RedissonConfig { @Bean(destroyMethod = "shutdown") public RedissonClient redissonClient(){ // 所有对redisson的操作都是通过这个client对象 Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.56.10:6379"); return Redisson.create(config); } }
package com.alatus.mall.product.config; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RedissonConfig { @Bean(destroyMethod = "shutdown") public RedissonClient redissonClient(){ // 所有对redisson的操作都是通过这个client对象 Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.56.10:6379"); return Redisson.create(config); } }
package com.alatus.mall.product.config; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RedissonConfig { @Bean(destroyMethod = "shutdown") public RedissonClient redissonClient(){ // 所有对redisson的操作都是通过这个client对象 Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.56.10:6379"); return Redisson.create(config); } }
package com.alatus.mall.seckill.feign; import com.alatus.common.utils.R; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @FeignClient("Mall-product") public interface ProductFeignService { /** * 信息 */ @RequestMapping("/product/skuinfo/info/{skuId}") R getSkuInfo(@PathVariable("skuId") Long skuId); }
package com.alatus.mall.seckill.feign; import com.alatus.common.utils.R; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @FeignClient("Mall-product") public interface ProductFeignService { /** * 信息 */ @RequestMapping("/product/skuinfo/info/{skuId}") R getSkuInfo(@PathVariable("skuId") Long skuId); }
package com.alatus.mall.seckill.to; import com.alatus.mall.seckill.vo.SkuInfoVo; import lombok.Data; import java.math.BigDecimal; @Data public class SecKillSkuRedisTo { /** * 活动id */ private Long promotionId; /** * 活动场次id */ private Long promotionSessionId; /** * 商品id */ private Long skuId; /** * 秒杀价格 */ private BigDecimal seckillPrice; /** * 秒杀总量 */ private BigDecimal seckillCount; /** * 每人限购数量 */ private BigDecimal seckillLimit; /** * 排序 */ private Integer seckillSort; /** * 商品详细信息 */ private SkuInfoVo skuInfo; /** * 秒杀开始时间 */ private Long startTime; /** * 秒杀结束时间 */ private Long endTime; /** * 秒杀随机码 */ private String randomCode; }
package com.alatus.mall.seckill.to; import com.alatus.mall.seckill.vo.SkuInfoVo; import lombok.Data; import java.math.BigDecimal; @Data public class SecKillSkuRedisTo { /** * 活动id */ private Long promotionId; /** * 活动场次id */ private Long promotionSessionId; /** * 商品id */ private Long skuId; /** * 秒杀价格 */ private BigDecimal seckillPrice; /** * 秒杀总量 */ private BigDecimal seckillCount; /** * 每人限购数量 */ private BigDecimal seckillLimit; /** * 排序 */ private Integer seckillSort; /** * 商品详细信息 */ private SkuInfoVo skuInfo; /** * 秒杀开始时间 */ private Long startTime; /** * 秒杀结束时间 */ private Long endTime; /** * 秒杀随机码 */ private String randomCode; }