猿实战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;
}
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- MacOS安装npm全局包的权限问题
- [已解决]报错run `npm audit fix` to fix them, or `npm audit` for details
- Hexo使用攻略-添加分类及标签
- print的简单使用
- 使用Pandas读取大型Excel文件
- 微信小程序获取页面高度
- vue简单实现九宫格抽奖
- [已解决]报错:xlrd.compdoc.CompDocError: Workbook: size exceeds expected 17920 bytes; corrupt?
- [已解决]报错:have mixed types. Specify dtype option on import or set low_memory=False
- Mysql删除表数据,表文件大小不变
- 抓取模板
- 利用os和pandas来合并当前目录下所有excel文件
- Python科学计算:用NumPy快速处理数据
- Python中过滤HTML标签的函数
- 词云图展示