Prechádzať zdrojové kódy

1. 商城:调整购物车的失效逻辑实现,基于 SPU 是否下架
2. 商城:增加购物车失效的重选逻辑

YunaiV 2 rokov pred
rodič
commit
f7fe9ad7b0
11 zmenil súbory, kde vykonal 103 pridanie a 56 odobranie
  1. 0 11
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
  2. 0 1
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
  3. 3 3
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java
  4. 10 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java
  5. 0 21
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateSelectedReqVO.java
  6. 26 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartResetReqVO.java
  7. 4 4
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
  8. 5 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java
  9. 12 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java
  10. 42 5
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java
  11. 1 7
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

+ 0 - 11
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.product.api.sku.dto;
 
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import lombok.Data;
 
 import java.util.List;
@@ -22,10 +21,6 @@ public class ProductSkuRespDTO {
      * SPU 编号
      */
     private Long spuId;
-    /**
-     * SPU 名字
-     */
-    private String spuName;
 
     /**
      * 属性数组
@@ -51,12 +46,6 @@ public class ProductSkuRespDTO {
      * 图片地址
      */
     private String picUrl;
-    /**
-     * SKU 状态
-     * <p>
-     * 枚举 {@link CommonStatusEnum}
-     */
-    private Integer status;
     /**
      * 库存
      */

+ 0 - 1
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java

@@ -14,7 +14,6 @@ public interface ErrorCodeConstants {
     // ==========  Order 模块 1-011-000-000 ==========
     ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品 SKU 不存在");
     ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1011000002, "商品 SPU 不可售卖");
-    ErrorCode ORDER_CREATE_SKU_NOT_SALE = new ErrorCode(1011000003, "商品 SKU 不可售卖");
     ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品 SKU 库存不足");
     ErrorCode ORDER_CREATE_SPU_NOT_FOUND = new ErrorCode(1011000005, "商品 SPU 不可售卖");
     ErrorCode ORDER_CREATE_ADDRESS_NOT_FOUND = new ErrorCode(1011000006, "收货地址不存在");

+ 3 - 3
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java

@@ -17,12 +17,12 @@ public class AppProductSkuBaseRespVO {
     @Schema(description = "主键", required = true, example = "1024")
     private Long id;
 
-    @Schema(description = "商品 SKU 名字", required = true, example = "芋道")
-    private String name;
-
     @Schema(description = "图片地址", example = "https://www.iocoder.cn/xx.png")
     private String picUrl;
 
+    @Schema(description = "销售价格,单位:分", required = true, example = "100")
+    private Integer price;
+
     @Schema(description = "库存", required = true, example = "1")
     private Integer stock;
 

+ 10 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
+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.service.cart.TradeCartService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -42,11 +43,19 @@ public class TradeCartController {
     @PutMapping("/update")
     @Operation(summary = "更新购物车商品")
     @PreAuthenticated
-    public CommonResult<Boolean> updateCartItemQuantity(@Valid @RequestBody AppTradeCartUpdateReqVO updateReqVO) {
+    public CommonResult<Boolean> updateCart(@Valid @RequestBody AppTradeCartUpdateReqVO updateReqVO) {
         cartService.updateCart(getLoginUserId(), updateReqVO);
         return success(true);
     }
 
+    @PutMapping("/reset")
+    @Operation(summary = "重置购物车商品")
+    @PreAuthenticated
+    public CommonResult<Boolean> resetCart(@Valid @RequestBody AppTradeCartResetReqVO updateReqVO) {
+        cartService.resetCart(getLoginUserId(), updateReqVO);
+        return success(true);
+    }
+
     @DeleteMapping("/delete")
     @Operation(summary = "删除购物车商品")
     @Parameter(name = "ids", description = "购物车商品编号", required = true, example = "1024,2048")

+ 0 - 21
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateSelectedReqVO.java

@@ -1,21 +0,0 @@
-package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-import java.util.Collection;
-
-@Schema(description = "用户 App - 购物车更新是否选中 Request VO")
-@Data
-public class AppTradeCartItemUpdateSelectedReqVO {
-
-    @Schema(description = "商品 SKU 编号列表", required = true, example = "1024,2048")
-    @NotNull(message = "商品 SKU 编号列表不能为空")
-    private Collection<Long> skuIds;
-
-    @Schema(description = "是否选中", required = true, example = "true")
-    @NotNull(message = "是否选中不能为空")
-    private Boolean selected;
-
-}

+ 26 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartResetReqVO.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "用户 App - 购物车重置 Request VO")
+@Data
+public class AppTradeCartResetReqVO {
+
+    @Schema(description = "编号", required = true, example = "1024")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+    @Schema(description = "商品 SKU 编号", required = true,example = "1024")
+    @NotNull(message = "商品 SKU 编号不能为空")
+    private Long skuId;
+
+    @Schema(description = "商品数量", required = true, example = "1")
+    @NotNull(message = "数量不能为空")
+    @Min(message = "数量必须大于 0", value = 1L)
+    private Integer count;
+
+}

+ 4 - 4
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java

@@ -34,17 +34,17 @@ public interface TradeCartConvert {
             ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());
             ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());
             cartVO.setSpu(convert(spu)).setSku(convert(sku));
-            // 如果 spu 或 sku 不存在,或者 spu 被禁用,说明是非法的,或者 sku 库存不足
+            // 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的
             if (spu == null
-                || sku == null
                 || !ProductSpuStatusEnum.isEnable(spu.getStatus())
-                || sku.getStock() <= 0) {
+                || spu.getStock() <= 0) {
                 invalidList.add(cartVO);
             } else {
+                // 虽然 SKU 可能也会不存在,但是可以通过购物车重新选择
                 validList.add(cartVO);
             }
         });
-        return new AppTradeCartListRespVO().setValidList(validList).setValidList(invalidList);
+        return new AppTradeCartListRespVO().setValidList(validList).setInvalidList(invalidList);
     }
     AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu);
     AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku);

+ 5 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java

@@ -45,8 +45,11 @@ public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
                 .eq(TradeCartDO::getUserId, userId));
     }
 
-    default List<TradeCartDO> selectListByUserId(Long userId) {
-        return selectList(TradeCartDO::getUserId, userId);
+    default List<TradeCartDO> selectListByUserId(Long userId, Boolean addStatus, Boolean orderStatus) {
+        return selectList(new LambdaQueryWrapper<TradeCartDO>()
+                .eq(TradeCartDO::getUserId, userId)
+                .eq(TradeCartDO::getAddStatus, addStatus)
+                .eq(TradeCartDO::getOrderStatus, orderStatus));
     }
 
     default void updateByIds(Collection<Long> ids, TradeCartDO updateObject) {

+ 12 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java

@@ -1,7 +1,8 @@
 package cn.iocoder.yudao.module.trade.service.cart;
 
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
 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 javax.validation.Valid;
@@ -31,6 +32,16 @@ public interface TradeCartService {
      */
     void updateCart(Long userId, AppTradeCartUpdateReqVO updateCountReqVO);
 
+    /**
+     * 重置购物车商品
+     *
+     * 使用场景:在一个购物车项对应的商品失效(例如说 SPU 被下架),可以重新选择对应的 SKU
+     *
+     * @param userId 用户编号
+     * @param updateReqVO 重置信息
+     */
+    void resetCart(Long userId, AppTradeCartResetReqVO updateReqVO);
+
     /**
      * 删除购物车商品
      *

+ 42 - 5
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java

@@ -1,18 +1,19 @@
 package cn.iocoder.yudao.module.trade.service.cart;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 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.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.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;
@@ -86,6 +87,28 @@ public class TradeCartServiceImpl implements TradeCartService {
                 .setCount(updateReqVO.getCount()));
     }
 
+    @Override
+    @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()));
+        }
+    }
+
     /**
      * 购物车删除商品
      *
@@ -111,8 +134,8 @@ public class TradeCartServiceImpl implements TradeCartService {
 
     @Override
     public AppTradeCartListRespVO getCartList(Long userId) {
-        // 获得购物车的商品
-        List<TradeCartDO> carts = cartMapper.selectListByUserId(userId);
+        // 获得购物车的商品,只查询未下单的
+        List<TradeCartDO> carts = cartMapper.selectListByUserId(userId, true, false);
         carts.sort(Comparator.comparing(TradeCartDO::getId).reversed());
         // 如果未空,则返回空结果
         if (CollUtil.isEmpty(carts)) {
@@ -122,12 +145,26 @@ public class TradeCartServiceImpl implements TradeCartService {
 
         // 查询 SPU、SKU 列表
         List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(carts, TradeCartDO::getSpuId));
-        List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(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;
+        });
+    }
+
     /**
      * 校验商品 SKU 是否合法
      * 1. 是否存在
@@ -140,7 +177,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()) {

+ 1 - 7
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

@@ -5,7 +5,6 @@ import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -125,15 +124,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         if (items.size() != skus.size()) {
             throw exception(ORDER_CREATE_SKU_NOT_FOUND);
         }
-        // 校验是否禁用 or 库存不足
+        // 校验库存不足
         Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
         items.forEach(item -> {
             ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
-            // SKU 禁用
-            if (ObjectUtil.notEqual(CommonStatusEnum.ENABLE.getStatus(), sku.getStatus())) {
-                throw exception(ORDER_CREATE_SKU_NOT_SALE);
-            }
-            // SKU 库存不足
             if (item.getCount() > sku.getStock()) {
                 throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH);
             }