package com.hmdp.service.impl; import cn.hutool.db.handler.RsHandler; import com.hmdp.config.RedissonConfig; import com.hmdp.dto.Result; import com.hmdp.entity.SeckillVoucher; import com.hmdp.entity.Voucher; import com.hmdp.entity.VoucherOrder; import com.hmdp.mapper.VoucherOrderMapper; import com.hmdp.service.ISeckillVoucherService; import com.hmdp.service.IVoucherOrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hmdp.service.IVoucherService; import com.hmdp.utils.RedisIdWorker; import com.hmdp.utils.SimpleRedisLock; import com.hmdp.utils.UserHolder; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.aop.framework.AopContext; import org.springframework.aop.framework.AopProxy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.time.LocalDateTime; /** *

* 服务实现类 *

* * @author 虎哥 * @since 2021-12-22 */ @Service public class VoucherOrderServiceImpl extends ServiceImpl implements IVoucherOrderService { @Autowired private ISeckillVoucherService iSeckillVoucherService; @Autowired private RedisIdWorker redisIdWorker; @Resource private StringRedisTemplate stringRedisTemplate; @Resource private RedissonClient redissonClient; @Override public Result seckillVoucher(Long voucherId) { // 根据vorcherId查询优惠券 SeckillVoucher seckillVoucher = iSeckillVoucherService.getById(voucherId); // 判断秒杀功能是否已经开始或结束 if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) { return Result.fail("秒杀功能还未开始"); } else if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) { return Result.fail("秒杀功能已经结束"); } // 判断库存是否充足 if(seckillVoucher.getStock()<1){ return Result.fail("库存不足"); } Long userID = UserHolder.getUser().getId(); // // 基于悲观锁,但是这个在集群环境下还是会导致线程不安全 // synchronized(userID.toString().intern()) { // // 获取代理对象,才能让事务生效 // IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy(); // return proxy.creatVoucheOrder(voucherId); // } // 不再用自己实现的分布式锁了,因为它存在不可重入,不可重试,超时释放,主从一致的问题 // SimpleRedisLock simpleRedisLock = new SimpleRedisLock("oreder:" + userID, stringRedisTemplate); // boolean isLock = simpleRedisLock.tryLock(1200L); // 利用redission 的分布式锁可以解决上述问题 RLock lock = redissonClient.getLock("lock:oreder:" + userID); boolean isLock = lock.tryLock(); if(!isLock){ return Result.fail("请勿重复下单"); } // 获取代理对象,才能让事务生效 try { IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy(); return proxy.creatVoucheOrder(voucherId); } finally { // 释放锁 lock.unlock(); } } @Transactional public Result creatVoucheOrder(Long voucherId) { Long userID = UserHolder.getUser().getId(); // 判断当前用户是否已经抢过订单了 Integer count = query().eq("user_id", userID).eq("voucher_id", voucherId).count(); if(count>0){ return Result.fail("您已经抢过该优惠券了"); } // 扣减库存,乐观锁,CAS法 // boolean succed = iSeckillVoucherService.update().setSql("stock = stock-1"). // eq("voucher_id",voucherId).eq("stock",seckillVoucher.getStock()).update(); 这种情况会导致即使有很多余额也会说库存不足 boolean succed = iSeckillVoucherService.update().setSql("stock = stock-1"). eq("voucher_id",voucherId).gt("stock",0).update(); if(!succed){ System.out.println("失败"); return Result.fail("库存不足"); } // 创建一个新的voucherOrder订单 VoucherOrder voucherOrder = new VoucherOrder(); long orderId = redisIdWorker.nextId("order"); voucherOrder.setId(orderId); voucherOrder.setUserId(userID); voucherOrder.setVoucherId(voucherId); save(voucherOrder); // 返回订单id return Result.ok(orderId); } }