mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-19 11:40:05 +08:00
mall-order: 完善活动商品库存扣减逻辑(并发更新库存下一提交实现)
This commit is contained in:
parent
c023209aa5
commit
57f0ea04f7
@ -10,9 +10,9 @@ public interface BargainActivityApi {
|
||||
/**
|
||||
* 更新砍价活动库存
|
||||
*
|
||||
* @param activityId 砍价活动编号
|
||||
* @param id 砍价活动编号
|
||||
* @param count 购买数量
|
||||
*/
|
||||
void updateBargainActivityStock(Long activityId, Integer count);
|
||||
void updateBargainActivityStock(Long id, Integer count);
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.promotion.api.seckill.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 更新秒杀库存 request DTO
|
||||
@ -12,37 +12,25 @@ import java.util.List;
|
||||
@Data
|
||||
public class SeckillActivityUpdateStockReqDTO {
|
||||
|
||||
// TODO @puhui999:参数校验
|
||||
|
||||
// TODO @puhui999:秒杀的话,一次只能购买一种商品哈;不能多个哈;
|
||||
|
||||
/**
|
||||
* 活动编号
|
||||
*/
|
||||
@NotNull(message = "活动编号不能为空")
|
||||
private Long activityId;
|
||||
/**
|
||||
* 总购买数量
|
||||
*/
|
||||
|
||||
@NotNull(message = "购买数量不能为空")
|
||||
private Integer count;
|
||||
/**
|
||||
* 活动商品
|
||||
*/
|
||||
private List<Item> items;
|
||||
|
||||
@NotNull(message = "活动商品不能为空")
|
||||
private Item item;
|
||||
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
/**
|
||||
* SPU 编号
|
||||
*/
|
||||
@NotNull(message = "SPU 编号不能为空")
|
||||
private Long spuId;
|
||||
/**
|
||||
* SKU 编号
|
||||
*/
|
||||
|
||||
@NotNull(message = "SKU 编号活动商品不能为空")
|
||||
private Long skuId;
|
||||
/**
|
||||
* 购买数量
|
||||
*/
|
||||
|
||||
@NotNull(message = "购买数量不能为空")
|
||||
private Integer count;
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,10 @@
|
||||
package cn.iocoder.yudao.module.promotion.api.bargain;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.BARGAIN_ACTIVITY_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 砍价活动 Api 接口实现类
|
||||
*
|
||||
@ -22,20 +17,8 @@ public class BargainActivityApiImpl implements BargainActivityApi {
|
||||
private BargainActivityService bargainActivityService;
|
||||
|
||||
@Override
|
||||
public void updateBargainActivityStock(Long activityId, Integer count) {
|
||||
// TODO @puhui999:可以整个实现到 bargainActivityService 中
|
||||
// 查询砍价活动
|
||||
BargainActivityDO activity = bargainActivityService.getBargainActivity(activityId);
|
||||
if (activity == null) {
|
||||
throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 更新砍价库存
|
||||
// TODO @puhui999:考虑下并发更新问题
|
||||
BargainActivityUpdateReqVO reqVO = new BargainActivityUpdateReqVO();
|
||||
reqVO.setId(activityId);
|
||||
reqVO.setStock(activity.getStock() - count);
|
||||
bargainActivityService.updateBargainActivity(reqVO);
|
||||
public void updateBargainActivityStock(Long id, Integer count) {
|
||||
bargainActivityService.updateBargainActivityStock(id, count);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,20 +1,10 @@
|
||||
package cn.iocoder.yudao.module.promotion.api.seckill;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
|
||||
import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.SECKILL_ACTIVITY_UPDATE_STOCK_FAIL;
|
||||
|
||||
/**
|
||||
* 秒杀活动接口 Api 接口实现类
|
||||
@ -27,58 +17,9 @@ public class SeckillActivityApiImpl implements SeckillActivityApi {
|
||||
@Resource
|
||||
private SeckillActivityService activityService;
|
||||
|
||||
// TODO @puhui:建议这块弄到 activityService 实现哈;
|
||||
// TODO @puhui:这个方法,要考虑事务性
|
||||
@Override
|
||||
public void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO) {
|
||||
// TODO @puhui999:长方法,最好有 1.1 1.2 2.1 这种步骤哈;
|
||||
SeckillActivityDO seckillActivity = activityService.getSeckillActivity(updateStockReqDTO.getActivityId());
|
||||
if (seckillActivity.getStock() < updateStockReqDTO.getCount()) {
|
||||
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
|
||||
}
|
||||
// 获取活动商品
|
||||
// TODO @puhui999:在一个方法里,dos 和 dolist 最好保持一致,要么用 s,要么用 list 哈;
|
||||
List<SeckillProductDO> productDOs = activityService.getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
|
||||
// TODO @puhui999:这个是不是搞成 CollectionUtils.convertMultiMap()
|
||||
List<SeckillActivityUpdateStockReqDTO.Item> items = updateStockReqDTO.getItems();
|
||||
Map<Long, List<Long>> map = new HashMap<>();
|
||||
items.forEach(item -> {
|
||||
if (map.containsKey(item.getSpuId())) {
|
||||
List<Long> skuIds = map.get(item.getSpuId());
|
||||
skuIds.add(item.getSkuId());
|
||||
map.put(item.getSpuId(), skuIds);
|
||||
} else {
|
||||
List<Long> list = new ArrayList<>();
|
||||
list.add(item.getSkuId());
|
||||
map.put(item.getSpuId(), list);
|
||||
}
|
||||
});
|
||||
// 过滤出购买的商品
|
||||
// TODO @puhui999:productDOList 可以简化成 productList;一般来说,do 之类不用带着哈,在变量里;
|
||||
List<SeckillProductDO> productDOList = CollectionUtils.filterList(productDOs, item -> map.get(item.getSpuId()).contains(item.getSkuId()));
|
||||
Map<Long, SeckillActivityUpdateStockReqDTO.Item> productDOMap = CollectionUtils.convertMap(items, SeckillActivityUpdateStockReqDTO.Item::getSkuId, p -> p);
|
||||
// 检查活动商品库存是否充足
|
||||
// TODO @puhui999:避免 b 这种无业务含义的变量;
|
||||
boolean b = CollectionUtils.anyMatch(productDOList, item -> {
|
||||
SeckillActivityUpdateStockReqDTO.Item item1 = productDOMap.get(item.getSkuId());
|
||||
return (item.getStock() < item1.getCount()) || (item.getStock() - item1.getCount()) < 0;
|
||||
});
|
||||
if (b) {
|
||||
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
|
||||
}
|
||||
// TODO @puhui999:类似 doList,应该和下面的 update 逻辑粘的更紧密一点;so 在空行的时候,应该挪到 74 之后里去;甚至更合理,应该是 79 之后;说白了,逻辑要分块,每个模块涉及的代码要紧密在一起;
|
||||
List<SeckillProductDO> doList = CollectionUtils.convertList(productDOList, item -> {
|
||||
item.setStock(item.getStock() - productDOMap.get(item.getSkuId()).getCount());
|
||||
return item;
|
||||
});
|
||||
|
||||
// 更新活动库存
|
||||
// TODO @puhui999:考虑下并发更新
|
||||
seckillActivity.setStock(seckillActivity.getStock() + updateStockReqDTO.getCount());
|
||||
seckillActivity.setTotalStock(seckillActivity.getTotalStock() - updateStockReqDTO.getCount());
|
||||
activityService.updateSeckillActivity(seckillActivity);
|
||||
// 更新活动商品库存
|
||||
activityService.updateSeckillActivityProductList(doList);
|
||||
activityService.updateSeckillStock(updateStockReqDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,14 @@ public interface BargainActivityService {
|
||||
*/
|
||||
void updateBargainActivity(@Valid BargainActivityUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新砍价活动库存
|
||||
*
|
||||
* @param id 砍价活动编号
|
||||
* @param count 购买数量
|
||||
*/
|
||||
void updateBargainActivityStock(Long id, Integer count);
|
||||
|
||||
/**
|
||||
* 删除砍价活动
|
||||
*
|
||||
@ -53,5 +61,4 @@ public interface BargainActivityService {
|
||||
*/
|
||||
PageResult<BargainActivityDO> getBargainActivityPage(BargainActivityPageReqVO pageReqVO);
|
||||
|
||||
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
private ProductSkuApi productSkuApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createBargainActivity(BargainActivityCreateReqVO createReqVO) {
|
||||
// 校验商品 SPU 是否存在是否参加的别的活动
|
||||
validateBargainConflict(createReqVO.getSpuId(), null);
|
||||
@ -53,6 +54,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateBargainActivity(BargainActivityUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
BargainActivityDO activityDO = validateBargainActivityExists(updateReqVO.getId());
|
||||
@ -70,6 +72,23 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
bargainActivityMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateBargainActivityStock(Long id, Integer count) {
|
||||
// 查询砍价活动
|
||||
BargainActivityDO activity = getBargainActivity(id);
|
||||
if (activity == null) {
|
||||
throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 更新砍价库存
|
||||
// TODO @puhui999:考虑下并发更新问题
|
||||
BargainActivityUpdateReqVO reqVO = new BargainActivityUpdateReqVO();
|
||||
reqVO.setId(id);
|
||||
reqVO.setStock(activity.getStock() - count);
|
||||
//bargainActivityService.updateBargainActivity(reqVO);
|
||||
}
|
||||
|
||||
private void validateBargainConflict(Long spuId, Long activityId) {
|
||||
// 查询所有开启的砍价活动
|
||||
List<BargainActivityDO> activityList = bargainActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.seckill;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
|
||||
@ -40,6 +41,12 @@ public interface SeckillActivityService {
|
||||
*/
|
||||
void updateSeckillActivity(SeckillActivityDO activityDO);
|
||||
|
||||
/**
|
||||
* 更新秒杀库存
|
||||
*
|
||||
* @param updateStockReqDTO 更新信息
|
||||
*/
|
||||
void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO);
|
||||
/**
|
||||
* 更新秒杀活动商品
|
||||
*
|
||||
|
@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
|
||||
@ -149,6 +150,40 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
seckillActivityMapper.updateById(activityDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO) {
|
||||
// 1、校验秒杀活动是否存在
|
||||
SeckillActivityDO seckillActivity = getSeckillActivity(updateStockReqDTO.getActivityId());
|
||||
// 1.1、校验库存是否充足
|
||||
if (seckillActivity.getStock() < 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、获取活动商品
|
||||
List<SeckillProductDO> products = getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
|
||||
// 3.1、过滤出购买的商品
|
||||
SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(updateStockReqDTO.getItem().getSkuId(), item.getSkuId()));
|
||||
// 3.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:考虑下并发更新
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSeckillActivityProductList(List<SeckillProductDO> productList) {
|
||||
seckillProductMapper.updateBatch(productList);
|
||||
|
@ -81,10 +81,9 @@ public class AppTradeOrderController {
|
||||
public CommonResult<AppTradeOrderDetailRespVO> getOrder(@RequestParam("id") Long id) {
|
||||
// 查询订单
|
||||
TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id);
|
||||
// TODO @puhui999:这里建议改成,如果为 null,直接返回 success null;主要查询操作,尽量不要有非空的提示哈;交给前端处理;
|
||||
// if (order == null) {
|
||||
// return success(null, ORDER_NOT_FOUND.getMsg());
|
||||
// }
|
||||
if (order == null) {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId());
|
||||
|
@ -313,17 +313,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
Integer count = getSumValue(orderItems, TradeOrderItemDO::getCount, Integer::sum);
|
||||
// 1)如果是秒杀商品:额外扣减秒杀的库存;
|
||||
if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), tradeOrderDO.getType())) {
|
||||
SeckillActivityUpdateStockReqDTO updateStockReqDTO = new SeckillActivityUpdateStockReqDTO();
|
||||
updateStockReqDTO.setActivityId(createReqVO.getSeckillActivityId());
|
||||
updateStockReqDTO.setCount(count);
|
||||
updateStockReqDTO.setItems(CollectionUtils.convertList(orderItems, item -> {
|
||||
SeckillActivityUpdateStockReqDTO.Item item1 = new SeckillActivityUpdateStockReqDTO.Item();
|
||||
item1.setSpuId(item.getSpuId());
|
||||
item1.setSkuId(item.getSkuId());
|
||||
item1.setCount(item.getCount());
|
||||
return item1;
|
||||
}));
|
||||
seckillActivityApi.updateSeckillStock(updateStockReqDTO);
|
||||
seckillActivityApi.updateSeckillStock(getSeckillActivityUpdateStockReqDTO(createReqVO, orderItems, count));
|
||||
}
|
||||
// 2)如果是砍价活动:额外扣减砍价的库存;
|
||||
bargainActivityApi.updateBargainActivityStock(createReqVO.getBargainActivityId(), count);
|
||||
@ -351,6 +341,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
// 增加订单日志 TODO 芋艿:待实现
|
||||
}
|
||||
|
||||
private SeckillActivityUpdateStockReqDTO getSeckillActivityUpdateStockReqDTO(AppTradeOrderCreateReqVO createReqVO, List<TradeOrderItemDO> orderItems, Integer count) {
|
||||
SeckillActivityUpdateStockReqDTO updateStockReqDTO = new SeckillActivityUpdateStockReqDTO();
|
||||
updateStockReqDTO.setActivityId(createReqVO.getSeckillActivityId());
|
||||
updateStockReqDTO.setCount(count);
|
||||
// 秒杀活动只能选择一个商品
|
||||
TradeOrderItemDO item = orderItems.get(0);
|
||||
SeckillActivityUpdateStockReqDTO.Item item1 = new SeckillActivityUpdateStockReqDTO.Item();
|
||||
item1.setSpuId(item.getSpuId());
|
||||
item1.setSkuId(item.getSkuId());
|
||||
item1.setCount(item.getCount());
|
||||
updateStockReqDTO.setItem(item1);
|
||||
return updateStockReqDTO;
|
||||
}
|
||||
|
||||
private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 创建支付单,用于后续的支付
|
||||
PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
|
||||
|
Loading…
Reference in New Issue
Block a user