Commit 245f14b8f1f4e6b5f1cf38db55a6f81505da11c7
1 parent
831983e3
秒杀活动:完成订单取消时恢复活动库存和商品库存
Showing
8 changed files
with
101 additions
and
25 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java
| ... | ... | @@ -10,13 +10,22 @@ import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinResp |
| 10 | 10 | public interface SeckillActivityApi { |
| 11 | 11 | |
| 12 | 12 | /** |
| 13 | - * 更新秒杀库存 | |
| 13 | + * 更新秒杀库存(减少) | |
| 14 | 14 | * |
| 15 | - * @param id 活动编号 | |
| 16 | - * @param skuId sku 编号 | |
| 17 | - * @param count 数量 | |
| 15 | + * @param id 活动编号 | |
| 16 | + * @param skuId sku 编号 | |
| 17 | + * @param count 数量(正数) | |
| 18 | + */ | |
| 19 | + void updateSeckillStockDecr(Long id, Long skuId, Integer count); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 更新秒杀库存(增加) | |
| 23 | + * | |
| 24 | + * @param id 活动编号 | |
| 25 | + * @param skuId sku 编号 | |
| 26 | + * @param count 数量(正数) | |
| 18 | 27 | */ |
| 19 | - void updateSeckillStock(Long id, Long skuId, Integer count); | |
| 28 | + void updateSeckillStockIncr(Long id, Long skuId, Integer count); | |
| 20 | 29 | |
| 21 | 30 | /** |
| 22 | 31 | * 【下单前】校验是否参与秒杀活动 |
| ... | ... | @@ -24,8 +33,8 @@ public interface SeckillActivityApi { |
| 24 | 33 | * 如果校验失败,则抛出业务异常 |
| 25 | 34 | * |
| 26 | 35 | * @param activityId 活动编号 |
| 27 | - * @param skuId SKU 编号 | |
| 28 | - * @param count 数量 | |
| 36 | + * @param skuId SKU 编号 | |
| 37 | + * @param count 数量 | |
| 29 | 38 | * @return 秒杀信息 |
| 30 | 39 | */ |
| 31 | 40 | SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java
| ... | ... | @@ -18,8 +18,13 @@ public class SeckillActivityApiImpl implements SeckillActivityApi { |
| 18 | 18 | private SeckillActivityService activityService; |
| 19 | 19 | |
| 20 | 20 | @Override |
| 21 | - public void updateSeckillStock(Long id, Long skuId, Integer count) { | |
| 22 | - activityService.updateSeckillStock(id, skuId, count); | |
| 21 | + public void updateSeckillStockDecr(Long id, Long skuId, Integer count) { | |
| 22 | + activityService.updateSeckillStockDecr(id, skuId, count); | |
| 23 | + } | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public void updateSeckillStockIncr(Long id, Long skuId, Integer count) { | |
| 27 | + activityService.updateSeckillStockIncr(id, skuId, count); | |
| 23 | 28 | } |
| 24 | 29 | |
| 25 | 30 | @Override | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/SeckillActivityDO.java
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 3 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| ... | ... | @@ -39,13 +40,14 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { |
| 39 | 40 | } |
| 40 | 41 | |
| 41 | 42 | /** |
| 42 | - * 更新活动库存 | |
| 43 | + * 更新活动库存(减少) | |
| 43 | 44 | * |
| 44 | 45 | * @param id 活动编号 |
| 45 | - * @param count 扣减的库存数量 | |
| 46 | + * @param count 扣减的库存数量(正数) | |
| 46 | 47 | * @return 影响的行数 |
| 47 | 48 | */ |
| 48 | - default int updateStock(Long id, int count) { | |
| 49 | + default int updateStockDecr(Long id, int count) { | |
| 50 | + Assert.isTrue(count > 0); | |
| 49 | 51 | return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() |
| 50 | 52 | .eq(SeckillActivityDO::getId, id) |
| 51 | 53 | .gt(SeckillActivityDO::getTotalStock, 0) |
| ... | ... | @@ -53,6 +55,21 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { |
| 53 | 55 | .setSql("total_stock = total_stock - " + count)); |
| 54 | 56 | } |
| 55 | 57 | |
| 58 | + /** | |
| 59 | + * 更新活动库存(增加) | |
| 60 | + * | |
| 61 | + * @param id 活动编号 | |
| 62 | + * @param count 增加的库存数量(正数) | |
| 63 | + * @return 影响的行数 | |
| 64 | + */ | |
| 65 | + default int updateStockIncr(Long id, int count) { | |
| 66 | + Assert.isTrue(count > 0); | |
| 67 | + return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() | |
| 68 | + .eq(SeckillActivityDO::getId, id) | |
| 69 | + .setSql("stock = stock - " + count) | |
| 70 | + .setSql("total_stock = total_stock + " + count)); | |
| 71 | + } | |
| 72 | + | |
| 56 | 73 | default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) { |
| 57 | 74 | return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>() |
| 58 | 75 | .eqIfPresent(SeckillActivityDO::getStatus, status) |
| ... | ... | @@ -62,6 +79,7 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { |
| 62 | 79 | |
| 63 | 80 | /** |
| 64 | 81 | * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 |
| 82 | + * | |
| 65 | 83 | * @param spuIds spu 编号 |
| 66 | 84 | * @param status 状态 |
| 67 | 85 | * @return 包含 spuId 和 activityId 的 map 对象列表 | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 3 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | 5 | import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; |
| 5 | 6 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| ... | ... | @@ -30,17 +31,32 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> { |
| 30 | 31 | } |
| 31 | 32 | |
| 32 | 33 | /** |
| 33 | - * 更新活动库存 | |
| 34 | + * 更新活动库存(减少) | |
| 34 | 35 | * |
| 35 | 36 | * @param id 活动编号 |
| 36 | - * @param count 扣减的库存数量 | |
| 37 | + * @param count 扣减的库存数量(减少库存) | |
| 37 | 38 | * @return 影响的行数 |
| 38 | 39 | */ |
| 39 | - default int updateStock(Long id, int count) { | |
| 40 | + default int updateStockDecr(Long id, int count) { | |
| 41 | + Assert.isTrue(count > 0); | |
| 40 | 42 | return update(null, new LambdaUpdateWrapper<SeckillProductDO>() |
| 41 | 43 | .eq(SeckillProductDO::getId, id) |
| 42 | 44 | .gt(SeckillProductDO::getStock, count) |
| 43 | 45 | .setSql("stock = stock - " + count)); |
| 44 | 46 | } |
| 45 | 47 | |
| 48 | + /** | |
| 49 | + * 更新活动库存(增加) | |
| 50 | + * | |
| 51 | + * @param id 活动编号 | |
| 52 | + * @param count 需要增加的库存(增加库存) | |
| 53 | + * @return 影响的行数 | |
| 54 | + */ | |
| 55 | + default int updateStockIncr(Long id, int count) { | |
| 56 | + Assert.isTrue(count > 0); | |
| 57 | + return update(null, new LambdaUpdateWrapper<SeckillProductDO>() | |
| 58 | + .eq(SeckillProductDO::getId, id) | |
| 59 | + .setSql("stock = stock + " + count)); | |
| 60 | + } | |
| 61 | + | |
| 46 | 62 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
| ... | ... | @@ -36,13 +36,22 @@ public interface SeckillActivityService { |
| 36 | 36 | void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO); |
| 37 | 37 | |
| 38 | 38 | /** |
| 39 | - * 更新秒杀库存 | |
| 39 | + * 更新秒杀库存(减少) | |
| 40 | 40 | * |
| 41 | 41 | * @param id 活动编号 |
| 42 | 42 | * @param skuId sku 编号 |
| 43 | - * @param count 数量 | |
| 43 | + * @param count 数量(正数) | |
| 44 | 44 | */ |
| 45 | - void updateSeckillStock(Long id, Long skuId, Integer count); | |
| 45 | + void updateSeckillStockDecr(Long id, Long skuId, Integer count); | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * 更新秒杀库存(增加) | |
| 49 | + * | |
| 50 | + * @param id 活动编号 | |
| 51 | + * @param skuId sku 编号 | |
| 52 | + * @param count 数量(正数) | |
| 53 | + */ | |
| 54 | + void updateSeckillStockIncr(Long id, Long skuId, Integer count); | |
| 46 | 55 | |
| 47 | 56 | /** |
| 48 | 57 | * 关闭秒杀活动 |
| ... | ... | @@ -113,8 +122,8 @@ public interface SeckillActivityService { |
| 113 | 122 | * 如果校验失败,则抛出业务异常 |
| 114 | 123 | * |
| 115 | 124 | * @param activityId 活动编号 |
| 116 | - * @param skuId SKU 编号 | |
| 117 | - * @param count 数量 | |
| 125 | + * @param skuId SKU 编号 | |
| 126 | + * @param count 数量 | |
| 118 | 127 | * @return 秒杀信息 |
| 119 | 128 | */ |
| 120 | 129 | SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
| ... | ... | @@ -157,7 +157,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { |
| 157 | 157 | |
| 158 | 158 | @Override |
| 159 | 159 | @Transactional(rollbackFor = Exception.class) |
| 160 | - public void updateSeckillStock(Long id, Long skuId, Integer count) { | |
| 160 | + public void updateSeckillStockDecr(Long id, Long skuId, Integer count) { | |
| 161 | 161 | // 1.1 校验活动库存是否充足 |
| 162 | 162 | SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); |
| 163 | 163 | if (count > seckillActivity.getTotalStock()) { |
| ... | ... | @@ -170,18 +170,28 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | 172 | // 2.1 更新活动商品库存 |
| 173 | - int updateCount = seckillProductMapper.updateStock(product.getId(), count); | |
| 173 | + int updateCount = seckillProductMapper.updateStockDecr(product.getId(), count); | |
| 174 | 174 | if (updateCount == 0) { |
| 175 | 175 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | 178 | // 2.2 更新活动库存 |
| 179 | - updateCount = seckillActivityMapper.updateStock(seckillActivity.getId(), count); | |
| 179 | + updateCount = seckillActivityMapper.updateStockDecr(seckillActivity.getId(), count); | |
| 180 | 180 | if (updateCount == 0) { |
| 181 | 181 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 182 | 182 | } |
| 183 | 183 | } |
| 184 | 184 | |
| 185 | + @Override | |
| 186 | + @Transactional(rollbackFor = Exception.class) | |
| 187 | + public void updateSeckillStockIncr(Long id, Long skuId, Integer count) { | |
| 188 | + SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(id, skuId); | |
| 189 | + // 更新活动商品库存 | |
| 190 | + seckillProductMapper.updateStockIncr(product.getId(), count); | |
| 191 | + // 更新活动库存 | |
| 192 | + seckillActivityMapper.updateStockIncr(id, count); | |
| 193 | + } | |
| 194 | + | |
| 185 | 195 | /** |
| 186 | 196 | * 更新秒杀商品 |
| 187 | 197 | * | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeSeckillHandler.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.service.order.handler; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 3 | 4 | import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; |
| 4 | 5 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 5 | 6 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| ... | ... | @@ -25,9 +26,11 @@ public class TradeSeckillHandler implements TradeOrderHandler { |
| 25 | 26 | if (TradeOrderTypeEnum.isSeckill(order.getType())) { |
| 26 | 27 | return; |
| 27 | 28 | } |
| 29 | + // 明确校验一下 | |
| 30 | + Assert.isTrue(orderItems.size() == 1, "秒杀时,只允许选择一个商品"); | |
| 28 | 31 | |
| 29 | 32 | // 扣减秒杀活动的库存 |
| 30 | - seckillActivityApi.updateSeckillStock(order.getSeckillActivityId(), | |
| 33 | + seckillActivityApi.updateSeckillStockDecr(order.getSeckillActivityId(), | |
| 31 | 34 | orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); |
| 32 | 35 | } |
| 33 | 36 | |
| ... | ... | @@ -36,7 +39,12 @@ public class TradeSeckillHandler implements TradeOrderHandler { |
| 36 | 39 | if (TradeOrderTypeEnum.isSeckill(order.getType())) { |
| 37 | 40 | return; |
| 38 | 41 | } |
| 42 | + // 明确校验一下 | |
| 43 | + Assert.isTrue(orderItems.size() == 1, "秒杀时,只允许选择一个商品"); | |
| 39 | 44 | |
| 45 | + // 恢复秒杀活动的库存 | |
| 46 | + seckillActivityApi.updateSeckillStockIncr(order.getSeckillActivityId(), | |
| 47 | + orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); | |
| 40 | 48 | } |
| 41 | 49 | |
| 42 | 50 | } | ... | ... |