- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
继续接前文 手撕商城系统架构设计与实现 。
本文主要讲解商城体系下产商品系统的设计。商城系统可以拆分成多个业务中台和多个应用服务.
1、产商品系统业务架构 。
产商品系统作为商城重要的基础信息组成部分,主要划分为产品信息和商品信息,产品信息保持最原始的产品基础属性和内容,商品信息则根据不同的售卖策略、营销价格属性或SKU进行组装而成.
因此商品源于产品而不同于产品。简单概括来说,商品是营销属性的产品.
2、产商品关键内容信息 。
产商品中心关键内容包括:产品信息、商品信息、目录管理、标签信息、产品字典库、产品分类、产品属性、价格版本管理、商品SKU组合.
产品信息应该包括产品基本信息,产品价格(与报价系统产品价格统一库),产品工艺术属性信息.
3、产商品系统边界 。
产商品系统与其他系统关系 。
订单系统与产商品系统调用关系 。
。
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()));
}
}
。
最后此篇关于手撕商城体系之产商品系统的文章就讲到这里了,如果你想了解更多关于手撕商城体系之产商品系统的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
假设我有 15 个商店,每个商店有 4 种产品和自定义价格: 每个商店不会拥有全部 4 种产品,但只能有 2 种、3 种、4 种甚至 1 种。 价格相似,但根据每个商店定制。所有商店的价格每 2-3
我这辈子似乎无法获得我想要的结构并使其正常运行,所以我一怒之下来找你们。 设置:我有一个名为 Futures_Contracts 的目录,里面有大约 30 个文件夹,全部以标的 Assets 命名,最
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题吗? 更新问题,以便 editing this post 提供事实和引用来回答它. 关闭 9 年前。 Improve
所以我已经使用 Smalltalk 大约 6 个月了(Squeak 和 Pharo),主要是做数据分析,我即将开始我的第一个 Seaside 应用程序。所以我对所有 Smalltalkers 的问题是
我选择的选项对象如下所示: { 3 : 'c', 5 : 'a', 6 : 'b', ... } 我的重复看起来像这样: {{v}} 我想按值 (v) 排序,但据我所知
什么是最好的方法(使用的算法/数据结构)来获得在购物网站上订购的前 k 项商品,相关信息在其 n 个服务器中的每一个的日志中? 我正在考虑一种方法,该方法涉及维护一个固定大小的双向链表 k 每个节点都
我正在尝试编写一个模式来验证以下 XML: AK47 The USSR's chief export back in the day, and the guer
我们正在尝试将 Android Market 集成到我们的服务中,我们需要验证用户确实是特定应用程序的所有者,我们需要自动检索该特定应用程序的销售情况。 因此,我们一直在评估 Google Check
我们有一个代表客户购物记录的数组。例如,它是这样一个数组: custA, item1, custB, item1, custA, item2, custB, item3, custC, item1,
我是一名优秀的程序员,十分优秀!