• 商城体系之产商品系统


    继续接前文手撕商城系统架构设计与实现

    本文主要讲解商城体系下产商品系统的设计。商城系统可以拆分成多个业务中台和多个应用服务。

    1、产商品系统业务架构

    产商品系统作为商城重要的基础信息组成部分,主要划分为产品信息和商品信息,产品信息保持最原始的产品基础属性和内容,商品信息则根据不同的售卖策略、营销价格属性或SKU进行组装而成。

    因此商品源于产品而不同于产品。简单概括来说,商品是营销属性的产品。

    2、产商品关键内容信息

    产商品中心关键内容包括:产品信息、商品信息、目录管理、标签信息、产品字典库、产品分类、产品属性、价格版本管理、商品SKU组合。

    产品信息应该包括产品基本信息,产品价格(与报价系统产品价格统一库),产品工艺术属性信息。

    3、产商品系统边界

    产商品系统与其他系统关系

    订单系统与产商品系统调用关系

    4、产商品结构模型设计

     

    5、关键代码片断

    @RestController
    @RequestMapping("/customer-api/v1/collect")
    public class GoodsCollectController {
    
        /**
         * 分页获取当前会员的收藏列表
         *
         * @return 收藏列表
         */
        @RestApi(module = "商品收藏-C端", name = "分页获取当前会员的收藏列表", logabled = true)
        @PostMapping("/page")
        public DataResponse<CustomizePage<GoodsCollectVO>> pageCurrentMemberGoodsCollect(@RequestBody @Valid GoodsCollectQry qry) {
            CustomizePage<MallGoodsCollectE> goodsCollectE = MallGoodsCollectE.queryInstance().pageCurrentMemberGoodsCollect(qry);
            return DataResponse.of(GoodsCollectVOConverter.convert(goodsCollectE));
        }
    
        /**
         * 加入收藏
         *
         * @param cmd 商品id
         * @return 收藏列表
         */
        @RestApi(module = "商品收藏-C端", name = "加入收藏", logabled = true)
        @PostMapping("/add")
        public DataResponse<Boolean> addGoodsCollect(@RequestBody @Valid AddGoodsCollectCmd cmd) {
            return DataResponse.of(MallGoodsCollectE.queryInstance().addGoodsCollect(cmd));
        }
    
        /**
         * 取消收藏
         *
         * @param id 收藏id
         * @return 操作结果
         */
        @RestApi(module = "商品收藏-C端", name = "取消收藏", logabled = true)
        @DeleteMapping("/{id}")
        public DataResponse<Boolean> deleteGoodsCollect(@PathVariable Long id) {
            return DataResponse.of(MallGoodsCollectE.queryInstance().deleteGoodsCollect(id));
        }
    
        /**
         * 根据 商品id 查询当前商品收藏情况
         *
         * @param cmd 商品id
         * @return 当前商品收藏情况
         */
        @RestApi(module = "商品收藏-C端", name = "根据 商品id 查询当前商品收藏情况", logabled = true)
        @PostMapping("/getGoodsCollect")
        public DataResponse<GetGoodsCollectVO> getGoodsCollect(@RequestBody GetGoodsCollectCmd cmd) {
            MallGoodsCollectE mallGoodsCollectE = MallGoodsCollectE.queryInstance().getGoodsCollect(cmd);
            return DataResponse.of(BeanToolkit.instance().copy(mallGoodsCollectE, GetGoodsCollectVO.class));
        }
    }
    @Slf4j
    @Service
    public class GoodsService {
        @Autowired
        private GoodsSkuRpcService goodsSkuRpcService;
        @Autowired
        private GoodsGateway goodsGateway;
    
        /**
         * 查询商品详情
         */
        public Map<String, SpuApiCO> mapSkuCO(List<String> skuIds) {
            if (CollUtil.isEmpty(skuIds)) {
                return Collections.emptyMap();
            }
            DataResponse<Map<String, SpuApiCO>> dataResponse = goodsSkuRpcService.mapByIds(skuIds);
            return ResponseUtil.resultValidate(dataResponse);
        }
    
        /**
         * 批量更新商品库存
         */
        public void updateInventory(List<UpdateInventoryDTO> dtoList) {
            goodsGateway.updateInventory(dtoList);
        }
    
        /**
         * 获取商品供应商集合
         */
        public Map<String, SupplierDTO> mapSupplierCO() {
            return goodsGateway.mapSupplierCO();
        }
    
        /**
         * 计算商品购买价格
         *
         * @param skuCO       商品信息
         * @param count       购买数量
         * @param memberLevel 会员等级
         * @param region      购买区域
         * @return 购买价格
         */
        public CalcPayPriceDTO calcPayPrice(SkuCO skuCO, Integer count, Integer memberLevel, String region) {
            //万
            BigDecimal tenThousand = BigDecimal.valueOf(10000);
            //该方法的中的价格单位为分
            //商品原价,原积分
            Long price = BigDecimalUtils.yuan2Penny(skuCO.getPrice());
            Long integral = skuCO.getIntegral();
    
            //需支付价格,积分,运费
            Long goodsTotalPrice = price;
            Long goodsTotalIntegral = integral;
            Long freight = 0L;
    
            // 1、计算会员等级差异化
            DiffPriceOption levelDifference = skuCO.getLevelDifference();
            if (levelDifference.enabled()) {
                DiffPriceTmpl.DiffPriceForLevel diffPriceForLevel = levelDifference.getTmpl().getDiffs().stream()
                        .filter(tmpl -> tmpl.getLevel().equals(memberLevel))
                        .findFirst()
                        .get();
    
                if (DiffPriceMode.PERCENTAGE_DISCOUNT.getValue().equals(levelDifference.getTmpl().getMode())) {
                    // 1.1、结算比例调整
                    Long percent = diffPriceForLevel.getPercent().multiply(BigDecimal.valueOf(100)).longValue();
                    goodsTotalPrice = BigDecimal.valueOf(price * percent).divide(tenThousand, RoundingMode.HALF_UP).longValue();
                    // 积分不足1取1
                    BigDecimal integralDecimal = BigDecimal.valueOf(integral * percent);
                    goodsTotalIntegral = integralDecimal.compareTo(tenThousand) > 0 ?
                            integralDecimal.divide(tenThousand, RoundingMode.HALF_UP).longValue()
                            : integralDecimal.divide(tenThousand, RoundingMode.UP).longValue();
                } else if (DiffPriceMode.EXTRA_PAYMENT.getValue().equals(levelDifference.getTmpl().getMode())) {
                    // 1.2、需额外支付
                    if (diffPriceForLevel.getExtraPrice() != null) {
                        Long extraPrice = BigDecimalUtils.yuan2Penny(diffPriceForLevel.getExtraPrice());
                        goodsTotalPrice = (price + extraPrice);
                    }
                    if (diffPriceForLevel.getExtraIntegral() != null) {
                        goodsTotalIntegral = (integral + diffPriceForLevel.getExtraIntegral());
                    }
                } else {
                    throw new ServiceException("价格结算失败");
                }
            }
            // 购物车结算时,收货地址还没选,选了再计算
            if (StringUtil.isNotEmpty(region)) {
                // 2、计算运费
                ShippingCostOption freeShippingRange = skuCO.getFreeShippingRange();
                if (freeShippingRange.enabled()) {
                    UCRegionCacheCO customerRegion = MtdsBaseUCRegionCacheUtils.getUCRegionCacheCOById(region);
                    Optional<ShippingCostTmpl.RegionalCost> regionalCostOptional = freeShippingRange.getTmpl().getRegionalCosts().stream()
                            .filter(tmpl -> customerRegion.getPids().contains(tmpl.getRegionId()))
                            .findFirst();
    
                    if (regionalCostOptional.isPresent()) {
                        ShippingCostTmpl.RegionalCost regionalCost = regionalCostOptional.get();
                        // 2.1 满足包邮条件
                        if (regionalCost.getFreeEnabled() == 1 && count >= regionalCost.getFreeQty()) {
                            freight = 0L;
                        } else {
                            // 2.2 计算运费
                            if (count <= regionalCost.getBaseQty()) {
                                freight = freight + BigDecimalUtils.yuan2Penny(regionalCost.getBasePrice());
                            } else {
                                freight = freight + BigDecimalUtils.yuan2Penny(regionalCost.getBasePrice());
    
                                int increaseCount = (count - regionalCost.getBaseQty());
                                long extraFreight = BigDecimalUtils.yuan2Penny(regionalCost.getIncreasePrice())
                                        * increaseCount;
                                freight = freight + (extraFreight);
                            }
                        }
                    }
                }
            }
    
            //支付金额
            Long payPrice = (goodsTotalPrice * count) + freight;
            return CalcPayPriceDTO.builder()
                    .skuId(Long.valueOf(skuCO.getId()))
                    .oldGoodsTotalPrice(price * count)
                    .goodsTotalPrice(goodsTotalPrice * count)
                    .payPrice(payPrice)
                    .freight(freight)
                    .oldGoodsTotalIntegral(integral * count)
                    .goodsTotalIntegral(goodsTotalIntegral * count)
                    .build();
        }
    }
    @Slf4j
    @Component
    public class GoodsGatewayImpl implements GoodsGateway {
        @Autowired
        private GoodsSkuRpcService goodsSkuRpcService;
        @Autowired
        private GoodsSupplierRpcService supplierRpcService;
    
        @Override
        public void updateInventory(List<UpdateInventoryDTO> dtoList) {
            List<SkuIncrementCmd> skuIncrementCmds = BeanToolkit.instance().copyList(dtoList, SkuIncrementCmd.class);
            Response response = goodsSkuRpcService.increment(skuIncrementCmds);
            if (!response.getStatus()) {
                throw new RpcErrorException(response.getMessage(), "商品");
            }
        }
    
        @Override
        public Map<String, SupplierDTO> mapSupplierCO() {
            DataResponse<List<SupplierCO>> response = supplierRpcService.listAll();
            List<SupplierCO> supplierCOS = ResponseUtil.resultValidate(response);
            if (CollUtil.isEmpty(supplierCOS)) {
                return Collections.emptyMap();
            }
    
            List<SupplierDTO> supplierDTOS = BeanToolkit.instance().copyList(supplierCOS, SupplierDTO.class);
            return supplierDTOS.stream().collect(Collectors.toMap(SupplierDTO::getId, Function.identity()));
        }
    }

     

  • 相关阅读:
    Java笔记(五)
    YOLOv7优化:感受野注意力卷积运算(RFAConv),效果秒杀CBAM和CA等 | 即插即用系列
    LeetCode - 解题笔记 - 189 - Rotate Array
    Java程序设计——execute()与executeUpdate()(JDBC编程)
    在springboot项目中整合Druid
    文盘Rust -- 把程序作为守护进程启动
    Android开发笔记1:Android Studio 2021.3.1.17 Toolbar工具栏调整
    什么是单点登录?什么又是 OAuth2.0?
    java基于微信小程序的竞赛报名管理系统
    python实现读取,修改excel数据
  • 原文地址:https://www.cnblogs.com/cgli/p/17280122.html