PmsSkuServiceImpl.java 6.7 KB
Newer Older
H
hxrui 已提交
1 2
package com.youlai.mall.pms.service.impl;

3
import cn.hutool.core.collection.CollectionUtil;
4
import cn.hutool.core.lang.Assert;
5
import cn.hutool.json.JSONUtil;
H
haoxr 已提交
6
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
H
haoxr 已提交
7
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
H
hxrui 已提交
8 9
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.web.exception.BizException;
10
import com.youlai.mall.pms.common.constant.PmsConstants;
H
haoxr 已提交
11
import com.youlai.mall.pms.mapper.PmsSkuMapper;
郝先瑞 已提交
12
import com.youlai.mall.pms.pojo.dto.CheckPriceDTO;
郝先瑞 已提交
13
import com.youlai.mall.pms.pojo.dto.SkuInfoDTO;
有来技术 已提交
14
import com.youlai.mall.pms.pojo.dto.app.LockStockDTO;
15
import com.youlai.mall.pms.pojo.entity.PmsSku;
H
haoxr 已提交
16
import com.youlai.mall.pms.service.IPmsSkuService;
17
import lombok.RequiredArgsConstructor;
H
hxrui 已提交
18
import lombok.extern.slf4j.Slf4j;
H
haoxr 已提交
19 20
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
郝先瑞 已提交
21
import org.springframework.cache.annotation.Cacheable;
22
import org.springframework.data.redis.core.StringRedisTemplate;
H
hxrui 已提交
23
import org.springframework.stereotype.Service;
24
import org.springframework.transaction.annotation.Transactional;
H
hxrui 已提交
25 26

import java.util.List;
27 28
import java.util.stream.Collectors;

H
hxrui 已提交
29 30
@Service
@Slf4j
31
@RequiredArgsConstructor
H
haoxr 已提交
32
public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> implements IPmsSkuService {
H
hxrui 已提交
33

34 35
    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;
9
99424 已提交
36 37


郝先瑞 已提交
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    /**
     * 获取商品库存数量
     *
     * @param skuId
     * @return
     */
    @Override
    @Cacheable(cacheNames = "pms", key = "'stock_num:'+#skuId")
    public Integer getStockNum(Long skuId) {
        Integer stockNum = 0;
        PmsSku pmsSku = this.getOne(new LambdaQueryWrapper<PmsSku>()
                .eq(PmsSku::getId, skuId)
                .select(PmsSku::getStockNum));

        if (pmsSku != null) {
            stockNum = pmsSku.getStockNum();
        }
        return stockNum;
    }
57

H
haoxr 已提交
58
    /**
59
     * 锁定库存 - 订单提交
H
haoxr 已提交
60
     */
H
hxrui 已提交
61
    @Override
62 63 64 65 66 67 68
    @Transactional
    public boolean lockStock(LockStockDTO lockStockDTO) {
        log.info("锁定商品库存:{}", JSONUtil.toJsonStr(lockStockDTO));

        List<LockStockDTO.LockedSku> lockedSkuList = lockStockDTO.getLockedSkuList();
        Assert.isTrue(CollectionUtil.isNotEmpty(lockedSkuList), "锁定的商品为空");

69
        // 锁定商品
70 71
        lockedSkuList.forEach(lockedSku -> {
            RLock lock = redissonClient.getLock(PmsConstants.LOCK_SKU_PREFIX + lockedSku.getSkuId()); // 获取商品的分布式锁
H
haoxr 已提交
72
            lock.lock();
73 74 75 76
            boolean lockResult = this.update(new LambdaUpdateWrapper<PmsSku>()
                    .setSql("locked_stock_num = locked_stock_num + " + lockedSku.getCount())
                    .eq(PmsSku::getId, lockedSku.getSkuId())
                    .apply("stock_num - locked_stock_num >= {0}", lockedSku.getCount())
H
haoxr 已提交
77
            );
78 79
            if (lockResult) {
                lock.unlock();
80
            } else {
81
                throw new BizException("锁定商品" + lockedSku.getSkuId() + "失败");
H
hxrui 已提交
82
            }
H
haoxr 已提交
83 84
        });

85 86 87 88
        // 将锁定商品库存信息保存至Redis
        String orderToken = lockStockDTO.getOrderToken();
        redisTemplate.opsForValue().set(PmsConstants.LOCKED_STOCK_PREFIX + orderToken, JSONUtil.toJsonStr(lockedSkuList));
        return true;
H
hxrui 已提交
89 90
    }

H
haoxr 已提交
91
    /**
92
     * 释放库存 - 订单超时未支付
H
haoxr 已提交
93
     */
H
hxrui 已提交
94
    @Override
H
haoxr 已提交
95
    public boolean unlockStock(String orderToken) {
96 97 98 99
        log.info("释放库存,orderToken:{}", orderToken);
        String lockedSkuJsonStr = redisTemplate.opsForValue().get(PmsConstants.LOCKED_STOCK_PREFIX + orderToken);
        List<LockStockDTO.LockedSku> lockedSkuList = JSONUtil.toList(lockedSkuJsonStr, LockStockDTO.LockedSku.class);
        lockedSkuList.forEach(item ->
H
haoxr 已提交
100 101
                this.update(new LambdaUpdateWrapper<PmsSku>()
                        .eq(PmsSku::getId, item.getSkuId())
102
                        .setSql("locked_stock_num = locked_stock_num - " + item.getCount()))
H
haoxr 已提交
103 104 105
        );

        // 删除redis中锁定的库存
106
        redisTemplate.delete(PmsConstants.LOCKED_STOCK_PREFIX + orderToken);
H
hxrui 已提交
107 108
        return true;
    }
H
haoxr 已提交
109

H
haoxr 已提交
110
    /**
111
     *  扣减库存 - 支付成功
H
haoxr 已提交
112
     */
H
haoxr 已提交
113
    @Override
H
haoxr 已提交
114
    public boolean deductStock(String orderToken) {
115 116 117
        log.info("扣减库存,orderToken:{}",orderToken);
        String lockedSkuJsonStr = redisTemplate.opsForValue().get(PmsConstants.LOCKED_STOCK_PREFIX + orderToken);
        List<LockStockDTO.LockedSku> lockedSkuList = JSONUtil.toList(lockedSkuJsonStr, LockStockDTO.LockedSku.class);
H
haoxr 已提交
118

119
        lockedSkuList.forEach(item -> {
H
haoxr 已提交
120 121
            boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
                    .eq(PmsSku::getId, item.getSkuId())
122 123
                    .setSql("stock_num = stock_num - " + item.getCount())
                    .setSql("locked_stock_num = locked_stock_num - " + item.getCount())
H
haoxr 已提交
124 125
            );
            if (!result) {
H
haoxr 已提交
126
                throw new BizException("扣减库存失败,商品" + item.getSkuId() + "库存不足");
H
haoxr 已提交
127 128
            }
        });
H
haoxr 已提交
129 130

        // 删除redis中锁定的库存
131
        redisTemplate.delete(PmsConstants.LOCKED_STOCK_PREFIX + orderToken);
H
haoxr 已提交
132
        return true;
H
haoxr 已提交
133 134
    }

郝先瑞 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    /**
     * 商品验价
     *
     * @param checkPriceDTO
     * @return
     */
    @Override
    public boolean checkPrice(CheckPriceDTO checkPriceDTO) {
        Long orderTotalAmount = checkPriceDTO.getOrderTotalAmount(); // 订单总金额

        // 计算商品总金额
        List<CheckPriceDTO.CheckSku> checkOrderItems = checkPriceDTO.getCheckSkus();
        if (CollectionUtil.isNotEmpty(checkOrderItems)) {
            List<Long> skuIds = checkOrderItems.stream()
                    .map(orderItem -> orderItem.getSkuId()).collect(Collectors.toList());
            List<PmsSku> skuList = this.list(new LambdaQueryWrapper<PmsSku>().in(PmsSku::getId, skuIds)
                    .select(PmsSku::getId, PmsSku::getPrice));
            // 商品总金额
            Long skuTotalAmount = checkOrderItems.stream().map(checkOrderItem -> {
                Long skuId = checkOrderItem.getSkuId();
                PmsSku pmsSku = skuList.stream().filter(sku -> sku.getId().equals(skuId)).findFirst().orElse(null);
                if (pmsSku != null) {
                    return pmsSku.getPrice() * checkOrderItem.getCount();
                }
                return 0L;
            }).reduce(0L, Long::sum);
            return orderTotalAmount.compareTo(skuTotalAmount) == 0;
        }
        return false;
    }

H
haoxr 已提交
166

H
haoxr 已提交
167
    /**
郝先瑞 已提交
168
     * 获取商品库存信息
H
haoxr 已提交
169
     *
郝先瑞 已提交
170
     * @param skuId
H
haoxr 已提交
171 172 173
     * @return
     */
    @Override
郝先瑞 已提交
174
    public SkuInfoDTO getSkuInfo(Long skuId) {
郝先瑞 已提交
175
        SkuInfoDTO skuInfo = this.baseMapper.getSkuInfo(skuId);
郝先瑞 已提交
176
        return skuInfo;
H
haoxr 已提交
177
    }
H
haoxr 已提交
178

179

H
hxrui 已提交
180
}