0购物车的设计.md 6.9 KB

我们的购物车只有一个表:tz_basket 非常简单,但是关联了非常多的表。 比如:

  • 购物车有商品,关联商品表
  • 每个商品都有sku,关联sku表
  • 一个购物车有多个店铺的商品,关联店铺表
  • 一个购物车肯定是和用户有关的,关联用户表

具体表的设计,可去【数据库表关系.md】和【数据库说明文档.doc】进行查看


我们对商品进行添加,修改,其实都很简单,最为让人难以理解的是如何将这些字段进行组合,关联满减满折等一系列的活动。

我们先来看下是如何获取商品信息的

    @PostMapping("/info")
    @Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组")
    @Parameter(name = "addrId", description = "地址id")
    public ServerResponseEntity<ShopCartWithAmountVO> info(@RequestParam(value = "addrId" , required = false, defaultValue = "0") Long addrId) throws ExecutionException, InterruptedException {
        String userId = SecurityUtils.getUser().getUserId();
        // 拿到购物车的所有item
        List<ShopCartItemDto> shopCartItems = shopCartItemAdapter.getShopCartItems(userId, addrId);
        List<ShopCartItemDto> filterShopCartItems = shopCartItems.stream()
                .filter(shopCartItemDto -> Objects.equals(shopCartItemDto.getIsChecked(), 1)).collect(Collectors.toList());
        // 组合每个店铺的购物车信息
        List<ShopCartDto> shopCarts = shopCartAdapter.getShopCarts(shopCartItems);
        ShopCartWithAmountVO shopCartWithAmount = new ShopCartWithAmountVO();
        shopCartWithAmount.setShopCarts(shopCarts);
        List<CouponOrderDto> couponOrderDtoList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(shopCarts)) {
            List<Long> shopIds = shopCarts.stream().map(ShopCartDto::getShopId).collect(Collectors.toList());
            //获取店铺的用户领取的投放优惠券
            List<Coupon> couponList = couponService.listPutonByShopIds(shopIds, 0);
            couponOrderDtoList = BeanUtil.mapAsList(couponList, CouponOrderDto.class);
            couponOrderDtoList.forEach(couponOrderDto -> couponOrderDto.setCanUse(true));
        }
        Map<Long, List<CouponOrderDto>> couponMap = couponOrderDtoList.stream().collect(Collectors.groupingBy(CouponOrderDto::getShopId));
        // 购物车项,按购物id顺序倒序返回
        for (ShopCartDto shopCart : shopCarts) {
            shopCart.setCoupons(couponMap.get(shopCart.getShopId()));
            for (ShopCartItemDiscountDto shopCartItemDiscount : shopCart.getShopCartItemDiscounts()) {
                shopCartItemDiscount.getShopCartItems().sort(Comparator.comparing(ShopCartItemDto::getBasketId).reversed());
            }
        }
        // 组装满减折,套餐,赠品的优惠数据
        if (discountShopCartManager != null) {
            discountShopCartManager.calculateDiscountAndMakeUpShopCartAndAmount(shopCartWithAmount);
        }
        if (comboShopCartManager != null) {
            comboShopCartManager.calculateComboAndMakeUpShopCartAndAmount(shopCartWithAmount);
        }
        // 先计算满减,再计算购物车商品的会员价(跳转:LoadProdMemberPriceListener)
        applicationContext.publishEvent(new LoadProdOffersEvent(shopCartItems,null,null, userId));
        // 重新计算下运费
        List<DvyTypeDTO> dvyTypes = Collections.singletonList(new DvyTypeDTO(null, DeliveryType.EXPRESS.getValue()));
        UserDeliveryInfoVO userDeliveryInfoVO = deliveryOrderManager.calculateAndGetDeliverInfo(userId, addrId, filterShopCartItems, dvyTypes, null);
        shopCartWithAmount.setUserDeliveryInfo(userDeliveryInfoVO);
        this.calculateMakeUpShopCartAndAmount(shopCartWithAmount);
        return ServerResponseEntity.success(shopCartWithAmount);
    }

获取购物车信息无需传参,只需要通过token拿到用户的id之后,再去通过shopCartItemAdapter.getShopCartItems(userId)查表即可。

订单重构后将运费计算给单独提出来,异步计算之后再set进返回数据中,由于用户查看到的购物车信息都是快递配送方式的,所以这里查到的运费也都是写死快递配送的。

之后根据每个店铺进行购物车数据的组装,详见shopCartAdapter.getShopCarts(shopCartItems),组装结束后再去获取满减,套餐以及赠品活动的相关信息,包括计算经过这些营销活动之后商品的价格。

我们现在看看购物车返回的数据ServerResponseEntity<ShopCartWithAmountVO>,我们清楚一个购物车是分多个店铺的,每一个店铺就是一个ShopCartDto,我们看下这个bean

@Data
@ApiModel("购物车合计")
public class ShopCartWithAmountVO {

    @ApiModelProperty("总额")
    private Double totalMoney;

    @ApiModelProperty("总计")
    private Double finalMoney;

    @ApiModelProperty("减额")
    private Double subtractMoney;

    @ApiModelProperty("商品数量")
    private Integer count;

    @ApiModelProperty(value = "运费",required=true)
    private Double freightAmount;

    @ApiModelProperty(value = "等级免运费金额", required = true)
    private Double freeTransFee;

    @ApiModelProperty("多个店铺的购物车信息")
    private List<ShopCartDto> shopCarts;

    @ApiModelProperty("运费信息")
    private UserDeliveryInfoVO userDeliveryInfo;
}

其实一个店铺下面是有多个商品的,但是根据京东的划分,每当有满减之类的活动时,满减活动的商品总是要归到一类的,如果有套餐活动则把套餐商品归为一类。

所以,每个店铺下面是多个营销活动(List<ShopCartItemDiscountDto>),包括满减活动,套餐活动,相关营销活动下面是多个商品(购物项List<ShopCartItemDto>),其中满减和套餐不会同时存在。

public class ShopCartItemDiscountDto implements Serializable {

    @ApiModelProperty(value = "已选满减项", required = true)
    private ChooseDiscountItemDto chooseDiscountItemDto;

    @ApiModelProperty(value = "已选套餐项", required = true)
    private ChooseComboItemDto chooseComboItemDto;

    @ApiModelProperty(value = "商品列表")
    private List<ShopCartItemDto> shopCartItems;

    @ApiModelProperty(value = "订单活动类型:0无 1满减 2套餐")
    private Integer activityType;
}

以前版本需要传入Map<Long, ShopCartParam> basketIdShopCartParamMap来改变该购物项选择的满减活动的id

重构后修改满减活动的话前端会调用/p/shopCart/changeItem来改变该购物车项的discountId,如果不参与促销活动则该值为-1

商品在加入购物车时默认满减活动id为0,之后在组装满减活动的时候会主动查询该商品适用于什么满减活动并且返回给前端。