商品显示秒杀-一人一单业务

基于redis优惠卷秒杀_xzm_的博客-CSDN博客进行改进

原流程

  

改进后流程

 实现代码

@Override
    @Transactional
    public Result seckillVoucher(Long voucherId) {
        //查询优惠卷
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            //秒杀尚未开始
            return Result.fail("秒杀尚未开始,请耐心等待");
        }
        //判断秒杀是否结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            //秒杀已经结束
            return Result.fail("本次秒杀已经结束");
        }
        //秒杀处于正常时间段
        //判断库存是否充足
        if (voucher.getStock()<1) {
            //库存不足
            return Result.fail("本次秒杀已经被抢完");
        }
        //库存充足

        
        //从拦截器中获取用户id
        Long userId = UserHolder.getUser().getId();
        /**
         * 进行判断,一人一单
         */
        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
        if (count>0) {
            //用户已经购买过商品
            return Result.fail("每位用户仅限购一单");
        }

        //扣减库存
        boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock",0)
                .update();
        //判断库存扣减是否成功
        if (!update) {
            //库存扣减失败,返回信息
            return Result.fail("库存不足");
        }

        //库存扣减成功,添加订单信息
        VoucherOrder voucherOrder = new VoucherOrder();
        //添加订单id
            //使用订单生成器生成id
        long id = redisIdWorker.nextId("order");
        voucherOrder.setId(id);
        //添加用户id

        voucherOrder.setUserId(userId);
        //添加消费卷id
        voucherOrder.setVoucherId(voucherId);
        //添加到数据库
        boolean save = this.save(voucherOrder);
        if (!save){
            //添加失败
            return Result.fail("下单失败");
        }
        return Result.ok(id);
    }

出现问题:依旧为超卖问题,因为为新增操作,所以无法进行添加乐观锁,进行添加悲观锁

1.添加依赖

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

2.在启动类上添加注解

//暴露代理对象
@EnableAspectJAutoProxy(exposeProxy = true)

3.修改后代码(需要在service中添加方法-自行添加即可)


    @Override
    public Result seckillVoucher(Long voucherId) {
        //查询优惠卷
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            //秒杀尚未开始
            return Result.fail("秒杀尚未开始,请耐心等待");
        }
        //判断秒杀是否结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            //秒杀已经结束
            return Result.fail("本次秒杀已经结束");
        }
        //秒杀处于正常时间段
        //判断库存是否充足
        if (voucher.getStock()<1) {
            //库存不足
            return Result.fail("本次秒杀已经被抢完");
        }
        //库存充足

        //从拦截器中获取用户id
        Long userId = UserHolder.getUser().getId();
        //对相同的id进行加锁
        synchronized(userId.toString().intern()) {
            //获取代理对象
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            //在service中添加createVoucherOrder方法,也可利用idea代码修复功能自动添加                                        
           return proxy.createVoucherOrder(voucherId);
            //return createVoucherOrder(voucherId);
        }

    }

    @Transactional
    public  Result createVoucherOrder(Long voucherId) {
        //从拦截器中获取用户id
        Long userId = UserHolder.getUser().getId();
         /**
          * 进行判断,一人一单
          */
         int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
         if (count > 0) {
             //用户已经购买过商品
             return Result.fail("每位用户仅限购一单");
         }

         //扣减库存
         boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1")
                 .eq("voucher_id", voucherId)
                 .gt("stock", 0)
                 .update();
         //判断库存扣减是否成功
         if (!update) {
             //库存扣减失败,返回信息
             return Result.fail("库存不足");
         }
         //库存扣减成功,添加订单信息
         VoucherOrder voucherOrder = new VoucherOrder();
         //添加订单id
         //使用订单生成器生成id
         long id = redisIdWorker.nextId("order");
         voucherOrder.setId(id);
         //添加用户id

         voucherOrder.setUserId(userId);
         //添加消费卷id
         voucherOrder.setVoucherId(voucherId);
         //添加到数据库
         boolean save = this.save(voucherOrder);
         if (!save) {
             //添加失败
             return Result.fail("下单失败");
         }
         return Result.ok(id);
    }
}

如果报错可将代理注解关闭将serviceImpl中的内容更改为(作者的事务依旧可以实现,原理暂时未知,如有知道的大神希望可以告诉一声)

 @Override
    public Result seckillVoucher(Long voucherId) {
        //查询优惠卷
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            //秒杀尚未开始
            return Result.fail("秒杀尚未开始,请耐心等待");
        }
        //判断秒杀是否结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            //秒杀已经结束
            return Result.fail("本次秒杀已经结束");
        }
        //秒杀处于正常时间段
        //判断库存是否充足
        if (voucher.getStock()<1) {
            //库存不足
            return Result.fail("本次秒杀已经被抢完");
        }
        //库存充足

        //从拦截器中获取用户id
        Long userId = UserHolder.getUser().getId();
        //对相同的id进行加锁
        synchronized(userId.toString().intern()) {
            //获取代理对象
//            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            //在service中添加createVoucherOrder方法,也可利用idea代码修复功能自动添加
//            return proxy.createVoucherOrder(voucherId);
            return createVoucherOrder(voucherId);
        }

    }

    @Transactional
    public  Result createVoucherOrder(Long voucherId) {
        //从拦截器中获取用户id
        Long userId = UserHolder.getUser().getId();
         /**
          * 进行判断,一人一单
          */
         int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
         if (count > 0) {
             //用户已经购买过商品
             return Result.fail("每位用户仅限购一单");
         }

         //扣减库存
         boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1")
                 .eq("voucher_id", voucherId)
                 .gt("stock", 0)
                 .update();
         //判断库存扣减是否成功
         if (!update) {
             //库存扣减失败,返回信息
             return Result.fail("库存不足");
         }
         //库存扣减成功,添加订单信息
         VoucherOrder voucherOrder = new VoucherOrder();
         //添加订单id
         //使用订单生成器生成id
         long id = redisIdWorker.nextId("order");
         voucherOrder.setId(id);
         //添加用户id

         voucherOrder.setUserId(userId);
         //添加消费卷id
         voucherOrder.setVoucherId(voucherId);
         //添加到数据库
         boolean save = this.save(voucherOrder);
         if (!save) {
             //添加失败
             return Result.fail("下单失败");
         }
         return Result.ok(id);
    }

到目前依旧有问题——在集群模式下依旧存在并发问题,依旧存在一人多单问题

解决方法------分布式锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值