|
@@ -1,39 +1,39 @@
|
|
|
package cn.iocoder.yudao.module.trade.service.cart;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
-import cn.hutool.core.lang.Assert;
|
|
|
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
|
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|
|
-import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
|
|
|
-import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
|
|
-import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum;
|
|
|
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
|
|
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
|
|
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
|
|
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
|
|
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
|
|
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
|
|
+import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
|
|
+import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
|
|
+import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
|
|
|
+import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
|
|
+import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
|
|
|
+import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
|
|
|
import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert;
|
|
|
-import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
|
|
-import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartItemMapper;
|
|
|
+import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
|
|
+import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartMapper;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
-import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
+import java.util.Comparator;
|
|
|
import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
|
|
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
|
|
|
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
|
|
|
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND;
|
|
|
+import static java.util.Collections.emptyList;
|
|
|
|
|
|
/**
|
|
|
* 购物车 Service 实现类
|
|
|
*
|
|
|
+ * // TODO 芋艿:秒杀、拼团、砍价对购物车的影响
|
|
|
+ * // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息
|
|
|
+ *
|
|
|
* @author 芋道源码
|
|
|
*/
|
|
|
@Service
|
|
@@ -41,123 +41,134 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_N
|
|
|
public class TradeCartServiceImpl implements TradeCartService {
|
|
|
|
|
|
@Resource
|
|
|
- private TradeCartItemMapper cartItemMapper;
|
|
|
+ private TradeCartMapper cartMapper;
|
|
|
|
|
|
@Resource
|
|
|
- private ProductSkuApi productSkuApi;
|
|
|
+ private ProductSpuApi productSpuApi;
|
|
|
@Resource
|
|
|
- private PriceApi priceApi;
|
|
|
+ private ProductSkuApi productSkuApi;
|
|
|
|
|
|
@Override
|
|
|
- public void addCartItemCount(Long userId, AppTradeCartItemAddCountReqVO addCountReqVO) {
|
|
|
- Long skuId = addCountReqVO.getSkuId();
|
|
|
- Integer count = addCountReqVO.getCount();
|
|
|
- // 查询 CartItemDO
|
|
|
- TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, addCountReqVO.getSkuId());
|
|
|
-
|
|
|
- // 存在,则进行数量更新
|
|
|
- if (tradeItem != null) {
|
|
|
- checkProductSku(skuId, tradeItem.getCount() + count);
|
|
|
- cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId())
|
|
|
- .setSelected(true).setCount(tradeItem.getCount() + count));
|
|
|
- return;
|
|
|
+ public Long addCart(Long userId, AppTradeCartAddReqVO addReqVO) {
|
|
|
+ // 查询 TradeCartDO
|
|
|
+ TradeCartDO cart = cartMapper.selectByUserIdAndSkuId(userId, addReqVO.getSkuId(),
|
|
|
+ addReqVO.getAddStatus(), false);
|
|
|
+ // 校验 SKU
|
|
|
+ Integer count = cart != null && addReqVO.getAddStatus() ?
|
|
|
+ cart.getCount() + addReqVO.getCount() : addReqVO.getCount();
|
|
|
+ ProductSkuRespDTO sku = checkProductSku(addReqVO.getSkuId(), count);
|
|
|
+
|
|
|
+ // 情况零:特殊,count 小于等于 0,说明前端项目删除
|
|
|
+ // 情况一:存在,则进行数量更新
|
|
|
+ if (cart != null) {
|
|
|
+ // 特殊情况,如果 count 小于等于 0,说明前端想要删除
|
|
|
+ if (count <= 0) {
|
|
|
+ cartMapper.deleteById(cart.getId());
|
|
|
+ } else {
|
|
|
+ cartMapper.updateById(new TradeCartDO().setId(cart.getId()).setCount(count));
|
|
|
+ }
|
|
|
+ return cart.getId();
|
|
|
+ // 情况二:不存在,则进行插入
|
|
|
+ } else {
|
|
|
+ cart = new TradeCartDO().setUserId(userId)
|
|
|
+ .setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setCount(count)
|
|
|
+ .setAddStatus(addReqVO.getAddStatus()).setOrderStatus(false);
|
|
|
+ cartMapper.insert(cart);
|
|
|
}
|
|
|
-
|
|
|
- // 不存在,则进行插入
|
|
|
- ProductSkuRespDTO sku = checkProductSku(skuId, count);
|
|
|
- cartItemMapper.insert(new TradeCartItemDO().setUserId(userId).setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
|
|
- .setSelected(true).setCount(count));
|
|
|
+ return cart.getId();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
|
|
|
- // 校验 TradeCartItemDO 存在
|
|
|
- TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, updateCountReqVO.getSkuId());
|
|
|
- if (tradeItem == null) {
|
|
|
+ public void updateCart(Long userId, AppTradeCartUpdateReqVO updateReqVO) {
|
|
|
+ // 校验 TradeCartDO 存在
|
|
|
+ TradeCartDO cart = cartMapper.selectById(updateReqVO.getId(), userId);
|
|
|
+ if (cart == null) {
|
|
|
throw exception(CARD_ITEM_NOT_FOUND);
|
|
|
}
|
|
|
// 校验商品 SKU
|
|
|
- checkProductSku(updateCountReqVO.getSkuId(), updateCountReqVO.getCount());
|
|
|
+ checkProductSku(cart.getSkuId(), updateReqVO.getCount());
|
|
|
|
|
|
// 更新数量
|
|
|
- cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()).setCount(updateCountReqVO.getCount()));
|
|
|
+ cartMapper.updateById(new TradeCartDO().setId(cart.getId())
|
|
|
+ .setCount(updateReqVO.getCount()));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
|
|
|
- // 查询 CartItemDO 列表
|
|
|
- List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, updateSelectedReqVO.getSkuIds());
|
|
|
- if (CollUtil.isEmpty(cartItems)) {
|
|
|
- return;
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void resetCart(Long userId, AppTradeCartResetReqVO resetReqVO) {
|
|
|
+ // 第一步:删除原本的购物项
|
|
|
+ TradeCartDO oldCart = cartMapper.selectById(resetReqVO.getId(), userId);
|
|
|
+ if (oldCart == null) {
|
|
|
+ throw exception(CARD_ITEM_NOT_FOUND);
|
|
|
+ }
|
|
|
+ cartMapper.deleteById(oldCart.getId());
|
|
|
+
|
|
|
+ // 第二步:添加新的购物项
|
|
|
+ TradeCartDO newCart = cartMapper.selectByUserIdAndSkuId(userId, resetReqVO.getSkuId(),
|
|
|
+ true, false);
|
|
|
+ if (newCart != null) {
|
|
|
+ updateCart(userId, new AppTradeCartUpdateReqVO()
|
|
|
+ .setId(newCart.getId()).setCount(resetReqVO.getCount()));
|
|
|
+ } else {
|
|
|
+ addCart(userId, new AppTradeCartAddReqVO().setAddStatus(true)
|
|
|
+ .setSkuId(resetReqVO.getSkuId()).setCount(resetReqVO.getCount()));
|
|
|
}
|
|
|
-
|
|
|
- // 更新选中
|
|
|
- cartItemMapper.updateByIds(CollectionUtils.convertList(cartItems, TradeCartItemDO::getId),
|
|
|
- new TradeCartItemDO().setSelected(updateSelectedReqVO.getSelected()));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 购物车删除商品
|
|
|
*
|
|
|
* @param userId 用户编号
|
|
|
- * @param skuIds 商品 SKU 编号的数组
|
|
|
+ * @param ids 商品 SKU 编号的数组
|
|
|
*/
|
|
|
@Override
|
|
|
- public void deleteCartItems(Long userId, Collection<Long> skuIds) {
|
|
|
- // 查询 CartItemDO 列表
|
|
|
- List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
|
|
|
- if (CollUtil.isEmpty(cartItems)) {
|
|
|
+ public void deleteCart(Long userId, Collection<Long> ids) {
|
|
|
+ // 查询 TradeCartDO 列表
|
|
|
+ List<TradeCartDO> carts = cartMapper.selectListByIds(ids, userId);
|
|
|
+ if (CollUtil.isEmpty(carts)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 批量标记删除
|
|
|
- cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(cartItems, TradeCartItemDO::getId));
|
|
|
+ cartMapper.deleteBatchIds(ids);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public Integer getCartCount(Long userId) {
|
|
|
- return cartItemMapper.selectSumByUserId(userId);
|
|
|
+ return cartMapper.selectSumByUserId(userId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public AppTradeCartDetailRespVO getCartDetail(Long userId) {
|
|
|
- // 获得购物车的商品
|
|
|
- List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserId(userId, null);
|
|
|
+ public AppTradeCartListRespVO getCartList(Long userId) {
|
|
|
+ // 获得购物车的商品,只查询未下单的
|
|
|
+ List<TradeCartDO> carts = cartMapper.selectListByUserId(userId, true, false);
|
|
|
+ carts.sort(Comparator.comparing(TradeCartDO::getId).reversed());
|
|
|
// 如果未空,则返回空结果
|
|
|
- if (CollUtil.isEmpty(cartItems)) {
|
|
|
- return TradeCartConvert.INSTANCE.buildEmptyAppTradeCartDetailRespVO();
|
|
|
+ if (CollUtil.isEmpty(carts)) {
|
|
|
+ return new AppTradeCartListRespVO().setValidList(emptyList())
|
|
|
+ .setInvalidList(emptyList());
|
|
|
}
|
|
|
|
|
|
- // 调用价格服务,计算价格
|
|
|
- PriceCalculateRespDTO priceCalculate = priceApi.calculatePrice(TradeCartConvert.INSTANCE.convert(userId, cartItems));
|
|
|
-
|
|
|
- // 转换返回
|
|
|
- Map<Long, TradeCartItemDO> cartItemMap = convertMap(cartItems, TradeCartItemDO::getSkuId);
|
|
|
- Map<Long, PriceCalculateRespDTO.OrderItem> orderItemMap = convertMap(priceCalculate.getOrder().getItems(),
|
|
|
- PriceCalculateRespDTO.OrderItem::getSkuId);
|
|
|
- List<AppTradeCartDetailRespVO.ItemGroup> itemGroups = new ArrayList<>(cartItems.size());
|
|
|
- // ① 场景一,营销活动,订单级别 TODO 芋艿:待测试
|
|
|
- priceCalculate.getPromotions().stream().filter(promotion -> PromotionLevelEnum.ORDER.getLevel().equals(promotion.getLevel()))
|
|
|
- .forEach(promotion -> {
|
|
|
- AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>())
|
|
|
- .setPromotion(TradeCartConvert.INSTANCE.convert(promotion));
|
|
|
- itemGroups.add(itemGroup);
|
|
|
- promotion.getItems().forEach(promotionItem -> {
|
|
|
- PriceCalculateRespDTO.OrderItem orderItem = orderItemMap.remove(promotionItem.getSkuId());
|
|
|
- Assert.notNull(orderItem, "商品 SKU({}) 对应的订单项不能为空", promotionItem.getSkuId());
|
|
|
- TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
|
|
|
- itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
|
|
|
- });
|
|
|
- });
|
|
|
- // ② 场景二,营销活动,商品级别
|
|
|
- orderItemMap.values().forEach(orderItem -> {
|
|
|
- AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>(1)).setPromotion(null);
|
|
|
- itemGroups.add(itemGroup);
|
|
|
- TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
|
|
|
- itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
|
|
|
+ // 查询 SPU、SKU 列表
|
|
|
+ List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(carts, TradeCartDO::getSpuId));
|
|
|
+ List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(carts, TradeCartDO::getSkuId));
|
|
|
+
|
|
|
+ // 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
|
|
|
+ deleteCartIfSpuDeleted(carts, spus);
|
|
|
+
|
|
|
+ // 拼接数据
|
|
|
+ return TradeCartConvert.INSTANCE.convertList(carts, spus, skus);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void deleteCartIfSpuDeleted(List<TradeCartDO> carts, List<ProductSpuRespDTO> spus) {
|
|
|
+ // 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
|
|
|
+ carts.removeIf(cart -> {
|
|
|
+ if (spus.stream().noneMatch(spu -> spu.getId().equals(cart.getSpuId()))) {
|
|
|
+ cartMapper.deleteById(cart.getId());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
});
|
|
|
- return new AppTradeCartDetailRespVO().setItemGroups(itemGroups)
|
|
|
- .setOrder(TradeCartConvert.INSTANCE.convert(priceCalculate.getOrder()));
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -172,7 +183,7 @@ public class TradeCartServiceImpl implements TradeCartService {
|
|
|
*/
|
|
|
private ProductSkuRespDTO checkProductSku(Long skuId, Integer count) {
|
|
|
ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
|
|
|
- if (sku == null || CommonStatusEnum.DISABLE.getStatus().equals(sku.getStatus())) {
|
|
|
+ if (sku == null) {
|
|
|
throw exception(SKU_NOT_EXISTS);
|
|
|
}
|
|
|
if (count > sku.getStock()) {
|