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,7 +55,7 @@ public interface ErrorCodeConstants {
55 ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改"); 55 ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
56 ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除"); 56 ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除");
57 ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭"); 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 // ========== 秒杀时段 1013009000 ========== 60 // ========== 秒杀时段 1013009000 ==========
61 ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在"); 61 ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
@@ -83,6 +83,7 @@ public interface ErrorCodeConstants { @@ -83,6 +83,7 @@ public interface ErrorCodeConstants {
83 ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动"); 83 ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
84 ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改"); 84 ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
85 ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除"); 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 // ========== 砍价记录 1013013000 ========== 88 // ========== 砍价记录 1013013000 ==========
88 ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在"); 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,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityPageReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
9 10
10 import java.util.List; 11 import java.util.List;
@@ -28,4 +29,18 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> { @@ -28,4 +29,18 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
28 return selectList(BargainActivityDO::getStatus, status); 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 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
  9 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
9 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
10 11
11 import java.util.List; 12 import java.util.List;
@@ -32,4 +33,19 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -32,4 +33,19 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
32 .eqIfPresent(SeckillActivityDO::getStatus, status)); 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,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO; 4 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
  5 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
5 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
6 7
7 import java.util.Collection; 8 import java.util.Collection;
@@ -23,4 +24,18 @@ public interface SeckillProductMapper extends BaseMapperX&lt;SeckillProductDO&gt; { @@ -23,4 +24,18 @@ public interface SeckillProductMapper extends BaseMapperX&lt;SeckillProductDO&gt; {
23 return selectList(SeckillProductDO::getActivityId, ids); 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,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 private void validateBargainConflict(Long spuId, Long activityId) { 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,32 +156,31 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
156 // 1、校验秒杀活动是否存在 156 // 1、校验秒杀活动是否存在
157 SeckillActivityDO seckillActivity = getSeckillActivity(updateStockReqDTO.getActivityId()); 157 SeckillActivityDO seckillActivity = getSeckillActivity(updateStockReqDTO.getActivityId());
158 // 1.1、校验库存是否充足 158 // 1.1、校验库存是否充足
159 - if (seckillActivity.getStock() < updateStockReqDTO.getCount()) { 159 + if (seckillActivity.getTotalStock() < updateStockReqDTO.getCount()) {
160 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); 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 List<SeckillProductDO> products = getSeckillProductListByActivityId(updateStockReqDTO.getActivityId()); 164 List<SeckillProductDO> products = getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
171 - // 3.1、过滤出购买的商品 165 + // 2.1、过滤出购买的商品
172 SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(updateStockReqDTO.getItem().getSkuId(), item.getSkuId())); 166 SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(updateStockReqDTO.getItem().getSkuId(), item.getSkuId()));
173 - // 3.2、检查活动商品库存是否充足 167 + // 2.2、检查活动商品库存是否充足
174 boolean isSufficient = product == null || (product.getStock() == 0 || (product.getStock() < updateStockReqDTO.getItem().getCount()) || (product.getStock() - updateStockReqDTO.getItem().getCount()) < 0); 168 boolean isSufficient = product == null || (product.getStock() == 0 || (product.getStock() < updateStockReqDTO.getItem().getCount()) || (product.getStock() - updateStockReqDTO.getItem().getCount()) < 0);
175 if (isSufficient) { 169 if (isSufficient) {
176 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); 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 @Override 186 @Override