

手撕商城体系之产商品系统 - 陈国利
source link: https://www.cnblogs.com/cgli/p/17280122.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

继续接前文手撕商城系统架构设计与实现
本文主要讲解商城体系下产商品系统的设计。商城系统可以拆分成多个业务中台和多个应用服务。
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()));
}
}
Recommend
-
10
您需要回复本文后才能查看完整内容 立即回复 已经回复?立即刷新...
-
9
随着用户量级的快速增长,vivo官方商城v1.0的单体架构逐渐暴露出弊端:模块愈发臃肿、开发效率低下、性能出现瓶颈、系统维护困难。 从2017年开始启动的v2.0架构升级,基于业务模块进行垂直的系统物理拆分,拆分出来业务线各司其职,提供服务化的能力,...
-
4
陈国立 天行健,君子以自强不息! ...
-
2
RabbitMQ真实生产故障问题还原与分析 RabbitMQ生产故障问题分析 由某一次真实生产...
-
6
高并发环境下生成序列编码重复问题分析 一、背景 有个业务系统(订单系统),通过后台日志和监控观察,系统...
-
5
扫码登录认证技术原理介绍及实践 一、背景 最近业务要求PC端系统登录使用APP应用扫码登录。 主要目的...
-
11
如何设计一个最简化的推荐系统 本文目录结构 1、背景 2、推荐系统初...
-
3
统一授权认证架构设计及实现 统一鉴权认证是一个基础服务。它几乎在所有企业内部都需要,企业内部只要有两个以上系统存在,就有必要实...
-
5
那些年那些神码 首先说明一下什么是神码?神码就是神奇代码的意思(也是糟糕的意思),在这里是为了表达引以为戒!
-
3
手撕商城系统架构设计与实现 目录 背景 商城整体架构
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK