hm-dianping/src/main/java/com/hmdp/service/impl/VoucherOrderServiceImpl.java

121 lines
4.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/**
* <p>
* 服务实现类
* </p>
*
* @author 虎哥
* @since 2021-12-22
*/
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> 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);
}
}