PmsSkuServiceImpl.java 7.8 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
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
H
haoxr 已提交
9
import com.youlai.common.web.exception.ApiException;
10
import com.youlai.mall.pms.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.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
    @Transactional
    public boolean lockStock(LockStockDTO lockStockDTO) {
64
        log.info("锁定商品库存:{}", JSONUtil.toJsonStr(lockStockDTO));
65 66 67 68

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

69
        // 循环遍历锁定商品
70
        lockedSkuList.forEach(lockedSku -> {
71 72
            RLock lock = redissonClient.getLock(PmsConstants.LOCK_SKU_PREFIX + lockedSku.getSkuId()); // 获取分布式锁
            // 加锁
H
haoxr 已提交
73
            lock.lock();
74 75 76 77 78 79 80 81 82
            try {
                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())
                );
                Assert.isTrue(lockResult, "锁定商品 {} 失败", lockedSku.getSkuId());
            } finally {
                // 释放锁
83
                lock.unlock();
H
hxrui 已提交
84
            }
H
haoxr 已提交
85 86
        });

87
        // 将锁定的商品ID和对应购买数量持久化至Redis,后续使用场景: 1.订单取消归还库存;2.订单支付成功扣减库存。
88 89
        String orderToken = lockStockDTO.getOrderToken();
        redisTemplate.opsForValue().set(PmsConstants.LOCKED_STOCK_PREFIX + orderToken, JSONUtil.toJsonStr(lockedSkuList));
90 91

        // 无异常直接返回true
92
        return true;
H
hxrui 已提交
93 94
    }

H
haoxr 已提交
95
    /**
96
     * 释放库存 - 订单超时未支付
H
haoxr 已提交
97
     */
H
hxrui 已提交
98
    @Override
H
haoxr 已提交
99
    public boolean unlockStock(String orderToken) {
100 101 102 103
        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 已提交
104 105
                this.update(new LambdaUpdateWrapper<PmsSku>()
                        .eq(PmsSku::getId, item.getSkuId())
106
                        .setSql("locked_stock_num = locked_stock_num - " + item.getCount()))
H
haoxr 已提交
107 108 109
        );

        // 删除redis中锁定的库存
110
        redisTemplate.delete(PmsConstants.LOCKED_STOCK_PREFIX + orderToken);
H
hxrui 已提交
111 112
        return true;
    }
H
haoxr 已提交
113

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

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

        // 删除redis中锁定的库存
135
        redisTemplate.delete(PmsConstants.LOCKED_STOCK_PREFIX + orderToken);
H
haoxr 已提交
136
        return true;
H
haoxr 已提交
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 166 167 168 169
    /**
     * 商品验价
     *
     * @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 已提交
170
    /**
郝先瑞 已提交
171
     * 获取商品库存信息
H
haoxr 已提交
172
     *
郝先瑞 已提交
173
     * @param skuId
H
haoxr 已提交
174 175 176
     * @return
     */
    @Override
郝先瑞 已提交
177
    public SkuInfoDTO getSkuInfo(Long skuId) {
郝先瑞 已提交
178
        SkuInfoDTO skuInfo = this.baseMapper.getSkuInfo(skuId);
郝先瑞 已提交
179
        return skuInfo;
H
haoxr 已提交
180
    }
H
haoxr 已提交
181

182 183 184 185 186 187 188 189
    /**
     * 「实验室」修改商品库存数量
     *
     * @param skuId
     * @param stockNum 商品库存数量
     * @return
     */
    @Override
郝先瑞 已提交
190
    @Transactional
191 192 193 194 195 196 197
    public boolean updateStockNum(Long skuId, Integer stockNum) {
        boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
                .eq(PmsSku::getId, skuId)
                .set(PmsSku::getStockNum, stockNum)
        );
        return result;
    }
198

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    /**
     * 「实验室」扣减商品库存
     *
     * @param skuId
     * @param num   商品库存数量
     * @return
     */
    @Override
    public boolean deductStock(Long skuId, Integer num) {
        boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
                .setSql("stock_num = stock_num - " + num)
                .eq(PmsSku::getId, skuId)
        );
        return result;
    }
H
hxrui 已提交
214
}