猿实战21——商品发布之商品数据存储

时间:2022-07-28
本文章向大家介绍猿实战21——商品发布之商品数据存储,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,变身猿人找到工作不是问题。还等什么呢?关注公号,取基础代码,一起实战吧。

上个章节,猿人君教会了你如何生成sku数据,你还学到了一个新的算法——笛卡尔乘积的相关运算。

今天猿人君继续带你来实现商品发布的核心功能——商品数据的保存。

功能概览

在商品发布详情页面,填写完商品的相关信息后,点击保存按钮,记录商品相关信息,并将这些商品信息记录为仓库中的状态。

数据库设计

根据之前的设计相关文章,我们整理得出商品信息的数据库物理信息如下。

商品主表

商品扩展表

商品图片表

商品销售属性表

SKU表

SKU图片表

VO设计

描述商品的完整信息,实际上是一个数据聚合的过程。简单点来讲,商品除了需要描述自身的一些基本特性(名称,描述,包装信息等等)外,一些其他的特征,比如商品属性、比如图片、比如sku等等,都需要通过一个良好的数据结构描述出来。我们看下图:

对于1:1的关系,我们将其定义为属性即可,对于1:N的关系,我们可以使用List来表达含义。于是我们很可以较为容易的得出用于传递商品信息的VO.

public class ProductVo extends BaseDO {
 
    /**商品ID**/
    private Long productId;
 
    /**商品名称**/
    private String productName;
 
    /**商家ID**/
    private Long sellerId;
 
    /**商家名称**/
    private String sellerName;
 
    /**一级类目ID**/
    private Long categoryOneId;
 
    /**一级类目名称**/
    private String categoryOneName;
 
    /**二级类目ID**/
    private Long categoryTwoId;
 
    /**二级类目名称**/
    private String categoryTwoName;
 
    /**三级类目ID**/
    private Long categoryThreeId;
 
    /**三级类目名称**/
    private String categoryThreeName;
 
    /**预留商品编码**/
    private String productCode;
 
    /**预留商家商品编码**/
    private String sellerProductCode;
 
    /**是否预售**/
    private Integer preSale;
 
    /**供货价**/
    private BigDecimal supplyPrice;
 
    /**成本价**/
    private BigDecimal costPrice;
 
    /**品牌ID**/
    private Long brandId;
 
    /**品牌名称**/
    private String brandName;
 
    /**
     * 商品属性
     */
    private List<MallCategoryPropertyVo> propertites;
 
 
 
    /**预留扩展**/
    private String features;
 
    /**商品描述**/
    private String productDesc;
 
    /**商品主图**/
    private String imgUrl;
 
    /**商品图片**/
    private List<String> imgList;
 
    /**商品主skuId**/
    private Long mainSkuId;
 
    /**运费模板ID**/
    private Long freightTempleteId;
 
    /**发货时长**/
    private Integer deliveryHours;
 
    /**1下架2上架-1删除**/
    private Integer status;
 
    /**毛重**/
    private BigDecimal grossWeight;
 
    /**净重**/
    private BigDecimal netWeight;
 
    /**长**/
    private BigDecimal length;
 
    /**宽**/
    private BigDecimal width;
 
    /**高**/
    private BigDecimal height;
 
    /**包装长**/
    private BigDecimal packLength;
 
    /**包装宽**/
    private BigDecimal packWidth;
 
    /**包装高**/
    private BigDecimal packHeight;
 
    /**包装清单**/
    private String packageInfo;
    /**
     * 商品属性
     */
    private List<MallCategoryPropertyVo> salePropertites;
 
 
    /**
     * sku列表
     */
    private List<SkuVo> skuList;
}
public class SkuVo extends BaseDO {
 
    /**
     * sku Id
     */
    private Long id;
 
    private Long skuId;
 
    private String skuName;
    // 供货价
    private BigDecimal supplyPrice;
    // 卖家SKU编码
    private String skuCode;
    // 成本价
    private BigDecimal costPrice;
 
    //展示的销售属性名称
    private String proValueName;
 
    private List<String> imgList = new ArrayList<String>();
 
    private List<PropertyValueVo>  proValues = new  ArrayList<PropertyValueVo>();
}
 
public class PropertyValueVo extends BaseDO {
 
    /**
     * 属性ID
     */
    private Long  propertyId;
 
    /**
     * 属性值ID
     */
    private Long  propertyValueId;
}

VO数据持久化

我们将需要持久化的数据,通过VO的形式封装起来,这部分数据和我们最终要用于持久的Domain是有一些差距的。那么持久数据之前,你需要做一些准备。

大致可以分为以下几个步骤:

1. 构建商品主数据

private MallProduct buildMallProduct(ProductVo product){
MallProduct mallProduct = new MallProduct();
BeanUtils.copyProperties(product,mallProduct);
List<PropertyValueVo> dataList =
buildCheckedPropertyList(product.getPropertites(),product.getProCheckedList());
List<PropertyValueVo> proList =
buildCheckedPropertyList(product.getPropertites(),product.getProCheckedList());
Set<Long> proIdList=proList.stream().map(e -> e.getPropertyId()).collect(Collectors.toSet());
List<Long> propertyIdList= new ArrayList<Long>();
propertyIdList.addAll(proIdList);
mallProduct.setProperties(JSON.toJSONString(proIdList));
mallProduct.setPropertityValues(JSON.toJSONString(dataList));
mallProduct.setStatus(ProductStatusEnum.STATUS_IN_HOURSE.getStatusValue());
    return  mallProduct;
 
}
 
private List<PropertyValueVo> buildCheckedPropertyList
(List<MallCategoryPropertyVo> propertyList,List<Long> checkedList) {
List<PropertyValueVo> dataList = new ArrayList<PropertyValueVo>();
for(MallCategoryPropertyVo vo:propertyList){
 
for(MallCategoryPropertyValueVo valueVo:vo.getValueList()){
for(Long valueId:checkedList) {
if (valueId.longValue() == valueVo.getId().longValue()) {
 
PropertyValueVo dataVo = new PropertyValueVo();
dataVo.setPropertyId(vo.getId());
dataVo.setPropertyValueId(valueId);
dataList.add(dataVo);
}
}
}
 
}
return dataList;
}

注意,文中为了实现方便,使用了BeanUtils来处理数据,但是实际的场景中,前后端的字段名往往是不一样的,你用了也没啥大用,也不推荐你使用这种方式来处理。一个一个字段去匹配书写是你的职责,同时也能加深你对业务的把握,同时也是对代码负责的一种态度。

2. 构建商品扩展数据

private MallProductExt buildProductExt(ProductVo product ,Long productId){
MallProductExt productExt = new MallProductExt();
BeanUtils.copyProperties(product,productExt);
productExt.setStatus(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
productExt.setProductId(productId);
return  productExt;
}

3. 构建商品图片数据

private List<MallProductImg> buildProductImgList(ProductVo product,Long productId){
 
    List<MallProductImg> dataList = new ArrayList<MallProductImg>();
    for(String imgUrl:product.getImgList()){
MallProductImg mallProductImg = new MallProductImg();
BeanUtils.copyProperties(product,mallProductImg);
mallProductImg.setProductImgUrl(imgUrl);
mallProductImg.setProductId(productId);
mallProductImg.setStatus(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
dataList.add(mallProductImg);
}
 
return  dataList;
}

4. 构建商品销售属性数据

private MallProductSaleProperty buildProductSaleProperty(ProductVo product,Long productId){
MallProductSaleProperty saleProperty = new MallProductSaleProperty();
BeanUtils.copyProperties(product,saleProperty);
saleProperty.setProductId(productId);
saleProperty.setStatus(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
List<PropertyValueVo> dataList =
buildCheckedPropertyList(product.getSalePropertites(),product.getSaleCheckedList());
 
Set<Long> proIdList=dataList.stream().map(e -> e.getPropertyId()).collect(Collectors.toSet());
 
List<Long> propertyIdList= new ArrayList<Long>();
propertyIdList.addAll(proIdList);
//设置属性
saleProperty.setSaleProperties(JSON.toJSONString(propertyIdList));
//设置属性值
saleProperty.setSalePropertyValues(JSON.toJSONString(dataList));
 
return  saleProperty;
}
 

5. 构建sku数据

private List<MallProductSku> buildMallSkuList(ProductVo product,MallProduct mallProduct){
List<MallProductSku> dataList = new ArrayList<MallProductSku>();
    for(SkuVo skuVo:product.getSkuList()){
MallProductSku sku = new MallProductSku();
BeanUtils.copyProperties(product,sku);
//注意不要随意交换位置,要不价格信息会被覆盖掉
BeanUtils.copyProperties(skuVo,sku);
sku.setProductId(mallProduct.getProductId());
//sku.setSalePropertityValues(skuVo);
Set<Long> proIdList=skuVo.getProValues().stream().map(e -> e.getPropertyId()).collect(Collectors.toSet());
List<Long> propertyIdList= new ArrayList<Long>();
propertyIdList.addAll(proIdList);
 
//属性和属性值继承父类
sku.setProperties(mallProduct.getProperties());
sku.setPropertityValues(mallProduct.getPropertityValues());
 
//销售属性销售属性值
sku.setSaleProperties(JSON.toJSONString(proIdList));
sku.setSalePropertityValues(JSON.toJSONString(skuVo.getProValues()));
 
List<MallProductSkuImg> imgList= buildMallSkuImgList(product,skuVo,mallProduct.getProductId());
sku.setImgList(imgList);
            dataList.add(sku);
}
 
return  dataList;
}

6. 构建sku图片数据

private List<MallProductSkuImg> buildMallSkuImgList(ProductVo product,SkuVo skuVo, Long productId){
List<MallProductSkuImg> dataList = new ArrayList<MallProductSkuImg>();
for(String imgUrl:skuVo.getImgList()){
MallProductSkuImg skuImg = new MallProductSkuImg();
BeanUtils.copyProperties(product,skuImg);
BeanUtils.copyProperties(skuVo,skuImg);
skuImg.setProductId(productId);
skuImg.setStatus(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
skuImg.setId(null);
dataList.add(skuImg);
}
return  dataList;
}

最后,我们来聊一聊数据持久的具体细节。商品的数据是有关联性的,商品扩展、商品图片、商品销售属性、sku、sku图片其实都依赖于商品主数据ID的,而sku图片,还依赖于skuId的数据。所以在持久数据的时候,大概会是这样一个顺序:商品->商品扩展->商品图片->商品销售属性->商品->sku->sku图片。

考虑到你目前的学习进度和基础,在数据持久层面,猿人君并没有采取特别的处理,让你先熟悉熟悉基本的操作,后续的一些文中,可能会出现批量处理的优化方式。

   public Result<ProductVo> addMallProduct(ProductVo product) {
Result<ProductVo> result = new Result<ProductVo>();
try {
MallProduct mallProduct=buildMallProduct(product);
mallProductDao.insertMallProductModified(mallProduct);
product.setProductId(mallProduct.getProductId());
MallProductExt mallProductExt=buildProductExt(product,mallProduct.getProductId());
mallProductExtDao.insertMallProductExtModified(mallProductExt);
 
List<MallProductImg> productImgList =
buildProductImgList(product,mallProduct.getProductId());
for(MallProductImg img:productImgList){
mallProductImgDao.insertMallProductImgModified(img);
}
 
MallProductSaleProperty saleProperty=buildProductSaleProperty(product,mallProduct.getProductId());
mallProductSalePropertyDao.insertMallProductSalePropertyModified(saleProperty);
 
List<MallProductSku> skuList =
buildMallSkuList(product,mallProduct);
for(MallProductSku sku:skuList){
mallProductSkuDao.insertMallProductSku(sku);
for(MallProductSkuImg img:sku.getImgList()){
img.setSkuId(sku.getSkuId());
mallProductSkuImgDao.insertMallProductSkuImgModified(img);
}
}
 
} catch(Exception e) {
result.setSuccess(false);
}
result.addDefaultModel(product);
return result;
    }