Commit 7fb455096c89d125fe44091e546db4fd2c3cb5fc
1 parent
3976011a
商城活动: 完善活动商品并发库存扣减逻辑
Showing
6 changed files
with
66 additions
and
21 deletions
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<SeckillActivityDO> { |
| 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<SeckillProductDO> { |
| 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 | ... | ... |