mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 12:18:42 +08:00 
			
		
		
		
	1. 商城:调整购物车的失效逻辑实现,基于 SPU 是否下架
2. 商城:增加购物车失效的重选逻辑
This commit is contained in:
		@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除购物车商品
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -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()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user