黑马点评项目之超卖和一人一单问题

package com.hmdp.service.impl;

import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.SeckillVoucher;
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.utils.RedisWorker;
import com.hmdp.utils.UserHolder;
import org.springframework.aop.framework.AopContext;
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 {
    @Resource
    private RedisWorker redisWorker;
    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Override
    public Result seckillVoucher(Long voucherId) {
        //查询优惠卷
        SeckillVoucher voucherid = seckillVoucherService.getById(voucherId);
        判断秒杀是否开始
        if(voucherid.getBeginTime().isAfter(LocalDateTime.now())){
            return Result.fail("优惠尚未开始");
        }
        //判断秒杀是否结束
        if(voucherid.getEndTime().isBefore(LocalDateTime.now())){
            return Result.fail("优惠已经结束");
        }
        //判断库存是否充足
        if (voucherid.getStock()<1) {
            return Result.fail("库存不足");
        }
        Long userID = UserHolder.getUser().getId();
        synchronized(userID.toString().intern()){
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        }
    }
    //插入情况使用悲观锁
    //不能把synchronized加在方法上,这样每个用户都是用的一个锁,应该在用户ID上加锁
    @Transactional
    public  Result createVoucherOrder(Long voucherId) {
        Long userID = UserHolder.getUser().getId();
//        //tostring中没一次调用都会新生成一个string对象,导致每个锁的对象还是不一样,所以要加intern方法去常量池中找到值相等地址
//        synchronized(userID.toString()){
        int count = query()
                .eq("voucher_id", voucherId)
                .eq("user_id", userID)
                .count();//任然出现一人多单的问题,用户多并发查询了,属于插入情况,要使用悲观锁,相当于获取互斥锁
        if(count>0){
            return Result.fail("用户已经买过了");
        }
        //扣减库存
        boolean sucess=seckillVoucherService.update()
                .setSql("stock=stock-1")
                .eq("voucher_id", voucherId)
                .gt("stock",0)//乐观锁是更新数据的情况下使用(超卖问题)
                .update();
        //一人一单

        //扣減失敗
        if(!sucess){
            return Result.fail("库存不足");
        }
        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //订单id
        long orderID = redisWorker.nextID("order");
        voucherOrder.setId(orderID);
        //优惠卷id
        voucherOrder.setVoucherId(voucherId);
        //用户id

        voucherOrder.setUserId(userID);
        save(voucherOrder);
        //返回订单id
        return Result.ok(voucherId);
//        }//锁已经释放了,但是事务还没有提交
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值