基于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);
}
到目前依旧有问题——在集群模式下依旧存在并发问题,依旧存在一人多单问题
解决方法------分布式锁