猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合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;
}