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,13 +10,22 @@ import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinResp | ||
| 10 | public interface SeckillActivityApi { | 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,8 +33,8 @@ public interface SeckillActivityApi { | ||
| 24 | * 如果校验失败,则抛出业务异常 | 33 | * 如果校验失败,则抛出业务异常 |
| 25 | * | 34 | * |
| 26 | * @param activityId 活动编号 | 35 | * @param activityId 活动编号 |
| 27 | - * @param skuId SKU 编号 | ||
| 28 | - * @param count 数量 | 36 | + * @param skuId SKU 编号 |
| 37 | + * @param count 数量 | ||
| 29 | * @return 秒杀信息 | 38 | * @return 秒杀信息 |
| 30 | */ | 39 | */ |
| 31 | SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); | 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,8 +18,13 @@ public class SeckillActivityApiImpl implements SeckillActivityApi { | ||
| 18 | private SeckillActivityService activityService; | 18 | private SeckillActivityService activityService; |
| 19 | 19 | ||
| 20 | @Override | 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 | @Override | 30 | @Override |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/SeckillActivityDO.java
| @@ -77,7 +77,8 @@ public class SeckillActivityDO extends BaseDO { | @@ -77,7 +77,8 @@ public class SeckillActivityDO extends BaseDO { | ||
| 77 | private Integer singleLimitCount; | 77 | private Integer singleLimitCount; |
| 78 | 78 | ||
| 79 | /** | 79 | /** |
| 80 | - * 秒杀库存(剩余库存秒杀时扣减) | 80 | + * 秒杀库存-秒杀下单时怎加,恢复库存是减少 |
| 81 | + * 也就是说这个是记录当前秒杀活动用户购买的商品数量和 | ||
| 81 | */ | 82 | */ |
| 82 | private Integer stock; | 83 | private Integer stock; |
| 83 | /** | 84 | /** |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
| 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.hutool.core.util.ObjectUtil; | 4 | import cn.hutool.core.util.ObjectUtil; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 6 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| @@ -39,13 +40,14 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { | @@ -39,13 +40,14 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { | ||
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | /** | 42 | /** |
| 42 | - * 更新活动库存 | 43 | + * 更新活动库存(减少) |
| 43 | * | 44 | * |
| 44 | * @param id 活动编号 | 45 | * @param id 活动编号 |
| 45 | - * @param count 扣减的库存数量 | 46 | + * @param count 扣减的库存数量(正数) |
| 46 | * @return 影响的行数 | 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 | return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() | 51 | return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() |
| 50 | .eq(SeckillActivityDO::getId, id) | 52 | .eq(SeckillActivityDO::getId, id) |
| 51 | .gt(SeckillActivityDO::getTotalStock, 0) | 53 | .gt(SeckillActivityDO::getTotalStock, 0) |
| @@ -53,6 +55,21 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { | @@ -53,6 +55,21 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { | ||
| 53 | .setSql("total_stock = total_stock - " + count)); | 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 | default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) { | 73 | default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) { |
| 57 | return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>() | 74 | return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>() |
| 58 | .eqIfPresent(SeckillActivityDO::getStatus, status) | 75 | .eqIfPresent(SeckillActivityDO::getStatus, status) |
| @@ -62,6 +79,7 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { | @@ -62,6 +79,7 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { | ||
| 62 | 79 | ||
| 63 | /** | 80 | /** |
| 64 | * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 | 81 | * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 |
| 82 | + * | ||
| 65 | * @param spuIds spu 编号 | 83 | * @param spuIds spu 编号 |
| 66 | * @param status 状态 | 84 | * @param status 状态 |
| 67 | * @return 包含 spuId 和 activityId 的 map 对象列表 | 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 | package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; | 5 | import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; |
| 5 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | 6 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| @@ -30,17 +31,32 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> { | @@ -30,17 +31,32 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> { | ||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | /** | 33 | /** |
| 33 | - * 更新活动库存 | 34 | + * 更新活动库存(减少) |
| 34 | * | 35 | * |
| 35 | * @param id 活动编号 | 36 | * @param id 活动编号 |
| 36 | - * @param count 扣减的库存数量 | 37 | + * @param count 扣减的库存数量(减少库存) |
| 37 | * @return 影响的行数 | 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 | return update(null, new LambdaUpdateWrapper<SeckillProductDO>() | 42 | return update(null, new LambdaUpdateWrapper<SeckillProductDO>() |
| 41 | .eq(SeckillProductDO::getId, id) | 43 | .eq(SeckillProductDO::getId, id) |
| 42 | .gt(SeckillProductDO::getStock, count) | 44 | .gt(SeckillProductDO::getStock, count) |
| 43 | .setSql("stock = stock - " + count)); | 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,13 +36,22 @@ public interface SeckillActivityService { | ||
| 36 | void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO); | 36 | void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO); |
| 37 | 37 | ||
| 38 | /** | 38 | /** |
| 39 | - * 更新秒杀库存 | 39 | + * 更新秒杀库存(减少) |
| 40 | * | 40 | * |
| 41 | * @param id 活动编号 | 41 | * @param id 活动编号 |
| 42 | * @param skuId sku 编号 | 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,8 +122,8 @@ public interface SeckillActivityService { | ||
| 113 | * 如果校验失败,则抛出业务异常 | 122 | * 如果校验失败,则抛出业务异常 |
| 114 | * | 123 | * |
| 115 | * @param activityId 活动编号 | 124 | * @param activityId 活动编号 |
| 116 | - * @param skuId SKU 编号 | ||
| 117 | - * @param count 数量 | 125 | + * @param skuId SKU 编号 |
| 126 | + * @param count 数量 | ||
| 118 | * @return 秒杀信息 | 127 | * @return 秒杀信息 |
| 119 | */ | 128 | */ |
| 120 | SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); | 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,7 +157,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { | ||
| 157 | 157 | ||
| 158 | @Override | 158 | @Override |
| 159 | @Transactional(rollbackFor = Exception.class) | 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 | // 1.1 校验活动库存是否充足 | 161 | // 1.1 校验活动库存是否充足 |
| 162 | SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); | 162 | SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); |
| 163 | if (count > seckillActivity.getTotalStock()) { | 163 | if (count > seckillActivity.getTotalStock()) { |
| @@ -170,18 +170,28 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { | @@ -170,18 +170,28 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { | ||
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | // 2.1 更新活动商品库存 | 172 | // 2.1 更新活动商品库存 |
| 173 | - int updateCount = seckillProductMapper.updateStock(product.getId(), count); | 173 | + int updateCount = seckillProductMapper.updateStockDecr(product.getId(), count); |
| 174 | if (updateCount == 0) { | 174 | if (updateCount == 0) { |
| 175 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); | 175 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | // 2.2 更新活动库存 | 178 | // 2.2 更新活动库存 |
| 179 | - updateCount = seckillActivityMapper.updateStock(seckillActivity.getId(), count); | 179 | + updateCount = seckillActivityMapper.updateStockDecr(seckillActivity.getId(), count); |
| 180 | if (updateCount == 0) { | 180 | if (updateCount == 0) { |
| 181 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); | 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 | package cn.iocoder.yudao.module.trade.service.order.handler; | 1 | package cn.iocoder.yudao.module.trade.service.order.handler; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; | 4 | import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; |
| 4 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | 5 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 5 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | 6 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| @@ -25,9 +26,11 @@ public class TradeSeckillHandler implements TradeOrderHandler { | @@ -25,9 +26,11 @@ public class TradeSeckillHandler implements TradeOrderHandler { | ||
| 25 | if (TradeOrderTypeEnum.isSeckill(order.getType())) { | 26 | if (TradeOrderTypeEnum.isSeckill(order.getType())) { |
| 26 | return; | 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 | orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); | 34 | orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); |
| 32 | } | 35 | } |
| 33 | 36 | ||
| @@ -36,7 +39,12 @@ public class TradeSeckillHandler implements TradeOrderHandler { | @@ -36,7 +39,12 @@ public class TradeSeckillHandler implements TradeOrderHandler { | ||
| 36 | if (TradeOrderTypeEnum.isSeckill(order.getType())) { | 39 | if (TradeOrderTypeEnum.isSeckill(order.getType())) { |
| 37 | return; | 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 | } |