浏览代码

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

puhui999 1 年之前
父节点
当前提交
7fb455096c

+ 2 - 1
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 {
     ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
     ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除");
     ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
-    ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "更新秒杀活动库存失败,原因秒杀库存不足");
+    ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足");
 
     // ========== 秒杀时段 1013009000 ==========
     ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
@@ -83,6 +83,7 @@ public interface ErrorCodeConstants {
     ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
     ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
     ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除");
+    ErrorCode BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013012004, "砍价失败,原因:该砍价活动库存不足");
 
     // ========== 砍价记录 1013013000 ==========
     ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在");

+ 15 - 0
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;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
@@ -28,4 +29,18 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
         return selectList(BargainActivityDO::getStatus, status);
     }
 
+    /**
+     * 更新活动库存
+     *
+     * @param id    活动编号
+     * @param count 扣减的库存数量
+     * @return 影响的行数
+     */
+    default int updateActivityStock(Long id, int count) {
+        return update(null, new LambdaUpdateWrapper<BargainActivityDO>()
+                .eq(BargainActivityDO::getId, id)
+                .gt(BargainActivityDO::getStock, 0)
+                .setSql("stock = stock - " + count));
+    }
+
 }

+ 16 - 0
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;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
@@ -32,4 +33,19 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
                 .eqIfPresent(SeckillActivityDO::getStatus, status));
     }
 
+    /**
+     * 更新活动库存
+     *
+     * @param id    活动编号
+     * @param count 扣减的库存数量
+     * @return 影响的行数
+     */
+    default int updateActivityStock(Long id, int count) {
+        return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
+                .eq(SeckillActivityDO::getId, id)
+                .gt(SeckillActivityDO::getTotalStock, 0)
+                .setSql("stock = stock + " + count)
+                .setSql("totalStock = totalStock - " + count));
+    }
+
 }

+ 15 - 0
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;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.Collection;
@@ -23,4 +24,18 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> {
         return selectList(SeckillProductDO::getActivityId, ids);
     }
 
+    /**
+     * 更新活动库存
+     *
+     * @param id    活动编号
+     * @param count 扣减的库存数量
+     * @return 影响的行数
+     */
+    default int updateActivityStock(Long id, int count) {
+        return update(null, new LambdaUpdateWrapper<SeckillProductDO>()
+                .eq(SeckillProductDO::getId, id)
+                .gt(SeckillProductDO::getStock, 0)
+                .setSql("stock = stock - " + count));
+    }
+
 }

+ 4 - 5
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 {
         }
 
         // 更新砍价库存
-        // TODO @puhui999:考虑下并发更新问题
-        BargainActivityUpdateReqVO reqVO = new BargainActivityUpdateReqVO();
-        reqVO.setId(id);
-        reqVO.setStock(activity.getStock() - count);
-        //bargainActivityService.updateBargainActivity(reqVO);
+        int row = bargainActivityMapper.updateActivityStock(id, count);
+        if (row == 0) {
+            throw exception(BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
     }
 
     private void validateBargainConflict(Long spuId, Long activityId) {

+ 14 - 15
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 {
         // 1、校验秒杀活动是否存在
         SeckillActivityDO seckillActivity = getSeckillActivity(updateStockReqDTO.getActivityId());
         // 1.1、校验库存是否充足
-        if (seckillActivity.getStock() < updateStockReqDTO.getCount()) {
+        if (seckillActivity.getTotalStock() < updateStockReqDTO.getCount()) {
             throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
         }
 
-        // 2、更新活动库存
-        // TODO @puhui999:考虑下并发更新
-        seckillActivity.setStock(seckillActivity.getStock() + updateStockReqDTO.getCount());
-        seckillActivity.setTotalStock(seckillActivity.getTotalStock() - updateStockReqDTO.getCount());
-        updateSeckillActivity(seckillActivity);
-
-        // 3、获取活动商品
+        // 2、获取活动商品
         List<SeckillProductDO> products = getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
-        // 3.1、过滤出购买的商品
+        // 2.1、过滤出购买的商品
         SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(updateStockReqDTO.getItem().getSkuId(), item.getSkuId()));
-        // 3.2、检查活动商品库存是否充足
+        // 2.2、检查活动商品库存是否充足
         boolean isSufficient = product == null || (product.getStock() == 0 || (product.getStock() < updateStockReqDTO.getItem().getCount()) || (product.getStock() - updateStockReqDTO.getItem().getCount()) < 0);
         if (isSufficient) {
             throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
         }
 
-        // 4、更新活动商品库存
-        SeckillProductDO updateProduct = new SeckillProductDO();
-        updateProduct.setId(product.getId());
-        updateProduct.setStock(product.getStock() - updateStockReqDTO.getItem().getCount());
-        // TODO @puhui999:考虑下并发更新
+        // 3、更新活动商品库存
+        int itemRow = seckillProductMapper.updateActivityStock(product.getId(), updateStockReqDTO.getItem().getCount());
+        if (itemRow == 0) {
+            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
 
+        // 4、更新活动库存
+        int row = seckillActivityMapper.updateActivityStock(seckillActivity.getId(), updateStockReqDTO.getCount());
+        if (row == 0) {
+            throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
     }
 
     @Override