查询秒杀商品详情

该文详细描述了如何在Java后端实现秒杀系统的商品详情查询,利用Redis存储和布隆过滤器进行高效判断。首先,通过spuid查询商品信息时会检查Redis和布隆过滤器,接着从数据库获取数据并保存到Redis。同时,文章还介绍了如何根据时间范围决定是否显示秒杀链接,并使用Dubbo调用其他服务获取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根据spuid查询正在进行秒杀的商品

和普通的spuid查询商品详情相比,它的业务判断更复杂

  1. 页面上显示秒杀价和剩余秒杀时间等信息

  1. 判断请求的spuid是否在布隆过滤器中

  1. 判断Redis中是否包含商品信息

  1. 如果一切正常,在返回商品详情信息前,要为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测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿码德乌斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值