Commit 7fb455096c89d125fe44091e546db4fd2c3cb5fc

Authored by puhui999
1 parent 3976011a

商城活动: 完善活动商品并发库存扣减逻辑

yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
... ... @@ -55,7 +55,7 @@ public interface ErrorCodeConstants {
55 55 ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
56 56 ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除");
57 57 ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
58   - ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "更新秒杀活动库存失败,原因秒杀库存不足");
  58 + ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足");
59 59  
60 60 // ========== 秒杀时段 1013009000 ==========
61 61 ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
... ... @@ -83,6 +83,7 @@ public interface ErrorCodeConstants {
83 83 ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
84 84 ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
85 85 ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除");
  86 + ErrorCode BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013012004, "砍价失败,原因:该砍价活动库存不足");
86 87  
87 88 // ========== 砍价记录 1013013000 ==========
88 89 ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在");
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
... ... @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityPageReqVO;
7 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 9 import org.apache.ibatis.annotations.Mapper;
9 10  
10 11 import java.util.List;
... ... @@ -28,4 +29,18 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
28 29 return selectList(BargainActivityDO::getStatus, status);
29 30 }
30 31  
  32 + /**
  33 + * 更新活动库存
  34 + *
  35 + * @param id 活动编号
  36 + * @param count 扣减的库存数量
  37 + * @return 影响的行数
  38 + */
  39 + default int updateActivityStock(Long id, int count) {
  40 + return update(null, new LambdaUpdateWrapper<BargainActivityDO>()
  41 + .eq(BargainActivityDO::getId, id)
  42 + .gt(BargainActivityDO::getStock, 0)
  43 + .setSql("stock = stock - " + count));
  44 + }
  45 +
31 46 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 7 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
8 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
  9 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
9 10 import org.apache.ibatis.annotations.Mapper;
10 11  
11 12 import java.util.List;
... ... @@ -32,4 +33,19 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
32 33 .eqIfPresent(SeckillActivityDO::getStatus, status));
33 34 }
34 35  
  36 + /**
  37 + * 更新活动库存
  38 + *
  39 + * @param id 活动编号
  40 + * @param count 扣减的库存数量
  41 + * @return 影响的行数
  42 + */
  43 + default int updateActivityStock(Long id, int count) {
  44 + return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
  45 + .eq(SeckillActivityDO::getId, id)
  46 + .gt(SeckillActivityDO::getTotalStock, 0)
  47 + .setSql("stock = stock + " + count)
  48 + .setSql("totalStock = totalStock - " + count));
  49 + }
  50 +
35 51 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
  5 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
5 6 import org.apache.ibatis.annotations.Mapper;
6 7  
7 8 import java.util.Collection;
... ... @@ -23,4 +24,18 @@ public interface SeckillProductMapper extends BaseMapperX&lt;SeckillProductDO&gt; {
23 24 return selectList(SeckillProductDO::getActivityId, ids);
24 25 }
25 26  
  27 + /**
  28 + * 更新活动库存
  29 + *
  30 + * @param id 活动编号
  31 + * @param count 扣减的库存数量
  32 + * @return 影响的行数
  33 + */
  34 + default int updateActivityStock(Long id, int count) {
  35 + return update(null, new LambdaUpdateWrapper<SeckillProductDO>()
  36 + .eq(SeckillProductDO::getId, id)
  37 + .gt(SeckillProductDO::getStock, 0)
  38 + .setSql("stock = stock - " + count));
  39 + }
  40 +
26 41 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
... ... @@ -82,11 +82,10 @@ public class BargainActivityServiceImpl implements BargainActivityService {
82 82 }
83 83  
84 84 // 更新砍价库存
85   - // TODO @puhui999:考虑下并发更新问题
86   - BargainActivityUpdateReqVO reqVO = new BargainActivityUpdateReqVO();
87   - reqVO.setId(id);
88   - reqVO.setStock(activity.getStock() - count);
89   - //bargainActivityService.updateBargainActivity(reqVO);
  85 + int row = bargainActivityMapper.updateActivityStock(id, count);
  86 + if (row == 0) {
  87 + throw exception(BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL);
  88 + }
90 89 }
91 90  
92 91 private void validateBargainConflict(Long spuId, Long activityId) {
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
... ... @@ -156,32 +156,31 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
156 156 // 1、校验秒杀活动是否存在
157 157 SeckillActivityDO seckillActivity = getSeckillActivity(updateStockReqDTO.getActivityId());
158 158 // 1.1、校验库存是否充足
159   - if (seckillActivity.getStock() < updateStockReqDTO.getCount()) {
  159 + if (seckillActivity.getTotalStock() < updateStockReqDTO.getCount()) {
160 160 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
161 161 }
162 162  
163   - // 2、更新活动库存
164   - // TODO @puhui999:考虑下并发更新
165   - seckillActivity.setStock(seckillActivity.getStock() + updateStockReqDTO.getCount());
166   - seckillActivity.setTotalStock(seckillActivity.getTotalStock() - updateStockReqDTO.getCount());
167   - updateSeckillActivity(seckillActivity);
168   -
169   - // 3、获取活动商品
  163 + // 2、获取活动商品
170 164 List<SeckillProductDO> products = getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
171   - // 3.1、过滤出购买的商品
  165 + // 2.1、过滤出购买的商品
172 166 SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(updateStockReqDTO.getItem().getSkuId(), item.getSkuId()));
173   - // 3.2、检查活动商品库存是否充足
  167 + // 2.2、检查活动商品库存是否充足
174 168 boolean isSufficient = product == null || (product.getStock() == 0 || (product.getStock() < updateStockReqDTO.getItem().getCount()) || (product.getStock() - updateStockReqDTO.getItem().getCount()) < 0);
175 169 if (isSufficient) {
176 170 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
177 171 }
178 172  
179   - // 4、更新活动商品库存
180   - SeckillProductDO updateProduct = new SeckillProductDO();
181   - updateProduct.setId(product.getId());
182   - updateProduct.setStock(product.getStock() - updateStockReqDTO.getItem().getCount());
183   - // TODO @puhui999:考虑下并发更新
  173 + // 3、更新活动商品库存
  174 + int itemRow = seckillProductMapper.updateActivityStock(product.getId(), updateStockReqDTO.getItem().getCount());
  175 + if (itemRow == 0) {
  176 + throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
  177 + }
184 178  
  179 + // 4、更新活动库存
  180 + int row = seckillActivityMapper.updateActivityStock(seckillActivity.getId(), updateStockReqDTO.getCount());
  181 + if (row == 0) {
  182 + throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
  183 + }
185 184 }
186 185  
187 186 @Override
... ...