核心:避免超卖问题,保证一人一单
业务逻辑
代码步骤分析
全部代码
java
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
@Resource
private ISeckillVoucherService seckillVoucherService;
@Resource
private RedisIdWorker redisIdWorker;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RedissonClient redissonClient;
@Override
public Result seckillVoucher(Long voucherId) {
//1 查询优惠券信息
SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//2 判断秒杀是否开始
LocalDateTime now = LocalDateTime.now(); //现在时间
LocalDateTime beginTime = seckillVoucher.getBeginTime(); //开始时间
LocalDateTime endTime = seckillVoucher.getEndTime(); //结束时间
if (now.isBefore(beginTime)) {
return Result.fail("秒杀还未开始");
}
//3 判断秒杀是否结束
if (now.isAfter(endTime)) {
return Result.fail("秒杀已结束");
}
//4 判断库存是否充足
int stock = (int) seckillVoucher.getStock();
if (stock == 0 && stock <= 0) {
return Result.fail("库存不足");
}
//确保一人一单 用户ID和代金券ID联合查询
Long userId = UserHolder.getUser().getId();
//先获取锁 再提交事务
//synchronized (userId.toString().intern()) {
//创建锁对象
//SimpleRedisLock simpleRedisLock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);
RLock lock = redissonClient.getLock("lock:order:" + userId);
//获取锁
boolean isLock = lock.tryLock();
//判断是否获取锁成功
if (!isLock){
//获取锁失败 返回错误或失败
return Result.fail("不允许重复下单");
}
try {
//获取事务的代理对象
IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
return proxy.creatVoucherOrder(voucherId);
} finally {
//释放锁
lock.unlock();
}
//}//事务提交之后在释放锁
}
/**
* 这里对于加锁做出一些解释:如果锁加在整个方法上,那么就会导
* 致锁的粒度过大,导致每个进程进来都会锁住,所以要控制锁的粒度
*/
@Transactional
public Result creatVoucherOrder(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("用户已经购买过了");
}
//5 扣减库存-
boolean success = seckillVoucherService.update()
.setSql("stock = stock -1 ") //乐观锁
.eq("voucher_id", voucherId).gt("stock", 0) //判断库存是否大于0: where id = ? and stock > 0
.update();
if (!success) {
return Result.fail("库存不足");
}
//6 创建订单
//6.1 订单ID
VoucherOrder voucherOrder = new VoucherOrder();
long orderid = RedisIdWorker.nextId("order");
voucherOrder.setVoucherId(orderid);
//6.2 用户ID
voucherOrder.setUserId(userId);
//6.3 代金券ID
voucherOrder.setVoucherId(voucherId);
save(voucherOrder);
//7 返回订单id
return Result.ok(orderid);
}
}