
根据spuid查询正在进行秒杀的商品
和普通的spuid查询商品详情相比,它的业务判断更复杂
页面上显示秒杀价和剩余秒杀时间等信息
判断请求的spuid是否在布隆过滤器中
判断Redis中是否包含商品信息
如果一切正常,在返回商品详情信息前,要为url属性赋值,其实就是固定路径+随机码
完成根据spuid查询商品detail详情
业务逻辑层SeckillSpuServiceImpl类中编写新的方法
// 装配操作Redis的对象
@Autowired
private RedisTemplate redisTemplate;
// 当前项目中没有定义spuDetail对应Redis的key的常量
public static final String SECKILL_SPU_DETAIL_VO_PREFIX = "seckill:spu:detail:vo";
// 查询秒杀spu信息的spuDetail
@Override
public SeckillSpuDetailSimpleVO getSeckillSpuDetail(Long spuId) {
// 使用常量拼接spuId获得Redis的key
String seckillSpuDetailKey=SECKILL_SPU_DETAIL_VO_PREFIX+spuId;
// 先声明一个返回值类型的对象,设值为null即可
// 因为后面判断无论Redis中有没有这个key,都需要使用这个对象
SeckillSpuDetailSimpleVO simpleVO=null;
// 判断Redis中是否有这个Key
if(redisTemplate.hasKey(seckillSpuDetailKey)){
// 如果 key已经存在,直接从redis中获取即可
simpleVO=(SeckillSpuDetailSimpleVO)redisTemplate
.boundValueOps(seckillSpuDetailKey).get();
}else{
// 如果Redis中没有这个key,就要从数据库中查询
// 使用Dubbo调用product模块的方法查询即可
SpuDetailStandardVO spuDetailStandardVO =
dubboSeckillSpuService.getSpuDetailById(spuId);
// 实例化SeckillSpuDetailSimpleVO对象
simpleVO=new SeckillSpuDetailSimpleVO();
BeanUtils.copyProperties(spuDetailStandardVO,simpleVO);
// simpleVO赋值完成后,将它保存到Redis中,以便后续请求获取
redisTemplate.boundValueOps(seckillSpuDetailKey)
.set(simpleVO,10*60*1000+ RandomUtils.nextInt(10000),
TimeUnit.MILLISECONDS);
}
// 返回simpleVO
return simpleVO;
}
下面开发控制层然后测试
SeckillSpuController添加方法
// localhost:10007/seckill/spu/2/detail
@GetMapping("/{spuId}/detail")
@ApiOperation("根据spuId查询detail详情")
@ApiImplicitParam(value = "spuId",name="spuId",example = "2")
public JsonResult<SeckillSpuDetailSimpleVO> getSeckillDetail(
@PathVariable Long spuId){
SeckillSpuDetailSimpleVO simpleVO =
seckillSpuService.getSeckillSpuDetail(spuId);
return JsonResult.ok(simpleVO);
}
原有已经启动的服务不停止:Nacos\Seata\Redis
启动 product,重启seckill
访问knife4j测试
根据spuid查询秒杀商品详情
下面我们直接从业务逻辑层开始编写即可
SeckillSpuServiceImpl业务逻辑层实现类
// 根据spuId查询spu详情(返回值包含常规信息和秒杀信息已经随机码)
@Override
public SeckillSpuVO getSeckillSpu(Long spuId) {
// 在最终完整版的代码中,要在这里添加布隆过滤器的判断
// 执行布隆过滤器判断spuId是否存在,如果不存在直接抛出异常
// 检查spu信息是否已经在Redis中,还是先定Redis的key
String seckillSpuKey= SeckillCacheUtils.getSeckillSpuVOKey(spuId);
// 先声明一个当前方法的返回值类型对象为null
SeckillSpuVO seckillSpuVO=null;
// 判断Redis是否包含key
if(redisTemplate.hasKey(seckillSpuKey)){
// 如果包含这个key直接从redis 中获取
seckillSpuVO=(SeckillSpuVO)redisTemplate
.boundValueOps(seckillSpuKey).get();
}else{
// redis中没有这个key,完成数据库查询
// 先根据spuId查询秒杀信息
SeckillSpu seckillSpu=seckillSpuMapper.findSeckillSpuById(spuId);
// 判断seckillSpu是否为null(因为布隆过滤器可能产生误判)
if(seckillSpu == null){
throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
"您访问的商品不存在!");
}
// 到此为止,我们已经查询出了spu的秒杀商品信息,下面还要获取常规商品信息
// dubbo调用product模块的方法获取spu常规信息
SpuStandardVO spuStandardVO= dubboSeckillSpuService
.getSpuById(spuId);
seckillSpuVO=new SeckillSpuVO();
// 将常规信息中的同名属性赋值到seckillSpuVO对象中
BeanUtils.copyProperties(spuStandardVO,seckillSpuVO);
// 将秒杀信息也赋值到seckillSpuVO
seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());
seckillSpuVO.setStartTime(seckillSpu.getStartTime());
seckillSpuVO.setEndTime(seckillSpu.getEndTime());
// seckillSpuVO对象保存到Redis中
redisTemplate.boundValueOps(seckillSpuKey).set(
seckillSpuVO,10*60*1000+RandomUtils.nextInt(10000),
TimeUnit.MILLISECONDS);
}
// 我们观察seckillSpuVO对象,除了url属性外,所有属性都会被赋值了
// 我们一旦给url赋值,就意味着允许用户指定秒杀购买操作了
// 所以必须判断当前时间是否在秒杀时间范围内
// 如果不在,就不需要给url赋值,用户就只能看不能买
// 获取当前时间判断是否在秒杀时间范围内
LocalDateTime nowTime=LocalDateTime.now();
// 因为是秒杀高并发状态,所以尽量不连接数据库
// 可以使用seckillSpuVO中秒杀开始和结束时间的属性来判断
// 判断的基本原则是开始时间小于当前时间,当前时间要小于结束时间
if(seckillSpuVO.getStartTime().isBefore(nowTime) &&
nowTime.isBefore(seckillSpuVO.getEndTime())){
// 进入if表示当前时间是允许执行秒杀的,可以为url属性赋值
// 我们从redis中根据当前spuId获取已经预热好的随机码
String randCodeKey=SeckillCacheUtils.getRandCodeKey(spuId);
// redis获取随机码
String randCode=redisTemplate.boundValueOps(randCodeKey).get()+"";
// 将随机码赋值到url
seckillSpuVO.setUrl("/seckill/"+randCode);
log.info("url赋值随机码:{}",randCode);
}
// 最后别忘了返回
// 返回的是seckillSpuVO,实际上它是秒杀spu信息,常规spu信息和url的和
return seckillSpuVO;
}
开发控制层SeckillSpuController
// localhost:10007/seckill/spu/2
@GetMapping("/{spuId}")
@ApiOperation("根据spuId查询spu相关信息")
@ApiImplicitParam(value = "spuId",name="spuId",example = "2")
public JsonResult<SeckillSpuVO> getSeckillSpuVO(
@PathVariable Long spuId){
SeckillSpuVO seckillSpuVO=seckillSpuService.getSeckillSpu(spuId);
return JsonResult.ok(seckillSpuVO);
}
重启Seckill模块
knife4j测试