1.分析前端保存商品发布信息的json数据
1.分析commoditylaunch.vue的submitSkus
1.将后面的都注销,只保留查看数据的部分
![image-20240423095755248](https://img-blog.csdnimg.cn/img_convert/d552acbbc288a76285760907aba922c8.png)
2.填写基本信息
![image-20240423095919741](https://img-blog.csdnimg.cn/img_convert/895222d800fd776a371544da21b18e7c.png)
![image-20240423095953088](https://img-blog.csdnimg.cn/img_convert/c5f439717ee275c428a6032ae189a976.png)
![image-20240423100005414](https://img-blog.csdnimg.cn/img_convert/33b58adb5c693d57b98e495c285a744c.png)
![image-20240423100013921](https://img-blog.csdnimg.cn/img_convert/dee22f1a5bf2b63b294e424162c5e2ff.png)
3.保存信息,得到json
![image-20240423100045156](https://img-blog.csdnimg.cn/img_convert/d3a81d95d4c3f64571ca3bbafff6f2ab.png)
4.使用工具格式化一下
![image-20240423100406121](https://img-blog.csdnimg.cn/img_convert/aa9d067229cfec2ffd220cf970c48ea3.png)
2.使用工具将json转为model
![image-20240423105118842](https://img-blog.csdnimg.cn/img_convert/b5b513b07ce15146a5dd3f2ed0214279.png)
3.根据业务修改vo,放到vo包下
![image-20240423110715861](https://img-blog.csdnimg.cn/img_convert/8668fc1b707373ada92a2b2886b29905.png)
2.保存spu基本信息
1.创建表commodity_spu_info
1.用这个表来存储json的前六条信息
![image-20240423112229157](https://img-blog.csdnimg.cn/img_convert/e6153a769360f273a60463613536d045.png)
2.sql
use sunliving_commodity;
CREATE TABLE commodity_spu_info
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '商品 id',
spu_name VARCHAR(200) COMMENT '商品名称',
spu_description VARCHAR(1000) COMMENT '商品描述',
catalog_id BIGINT COMMENT '所属分类 id',
brand_id BIGINT COMMENT '品牌 id',
weight DECIMAL(18,4),
publish_status TINYINT COMMENT '上架状态[0 - 下架,1 - 上架]',
create_time DATETIME,
update_time DATETIME,
PRIMARY KEY (id)
)CHARSET=utf8mb4 COMMENT='商品 spu 信息';
SELECT * FROM commodity_spu_info
2.生成基本的CRUD
1.启动项目,访问localhost:81,点击生成
![image-20240423112537477](https://img-blog.csdnimg.cn/img_convert/dae1175d76d0968877e06fcda3460f14.png)
2.将生成的main目录覆盖sunliving-commodity的main目录
![image-20240423112806104](https://img-blog.csdnimg.cn/img_convert/6babcfa611ff558c08ffe5728b943439.png)
3.注释掉SpuInfoController.java中跟shiro相关的
![image-20240423113012171](https://img-blog.csdnimg.cn/img_convert/56e33ccf3fe41a132dadfd7628f16248.png)
4.重启测试接口
1.访问 http://localhost:5050/api/sunliving-commodity/commodity/spuinfo/list
![image-20240423113337244](https://img-blog.csdnimg.cn/img_convert/afaeba664320b3e3c829b0a03a3753c4.png)
2.注意,如果提示VO不识别之类的就先刷新maven然后build一下即可
3.完成基本信息的保存
1.后端 sunliving-commodity模块
1.SpuInfoService.java 接受SpuSaveVO,保存到数据库
public interface SpuInfoService extends IService<SpuInfoEntity> {
PageUtils queryPage(Map<String, Object> params);
public void saveSpuInfo(SpuSaveVO spuSaveVO);
}
2.SpuInfoServiceImpl.java 实现方法
@Override
public void saveSpuInfo(SpuSaveVO spuSaveVO) {
SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
spuInfoEntity.setCreateTime(new Date());
spuInfoEntity.setUpdateTime(new Date());
this.save(spuInfoEntity);
}
3.SpuInfoController.java 编写保存信息的接口
@RequestMapping("/save")
public R save(@RequestBody SpuSaveVO spuSaveVO) {
spuInfoService.saveSpuInfo(spuSaveVO);
return R.ok();
}
4.前端 commoditylaunch.vue的submitSkus方法
1.修改url为环境变量+资源路径的方式
![image-20240423134925019](https://img-blog.csdnimg.cn/img_convert/cfbde899ca4d0b3bb9a21de7f2dafeb2.png)
2.解掉下面的注释
![image-20240423134945642](https://img-blog.csdnimg.cn/img_convert/de3890405226de9087f3eda59f443a84.png)
5.前后端联调
1.前端输入信息
![image-20240423135116668](https://img-blog.csdnimg.cn/img_convert/7f5fbe3f151204721cf44f47a0193b1f.png)
2.后端查看数据库
![image-20240423135140107](https://img-blog.csdnimg.cn/img_convert/ba599d94fc503887da752bfb22f404b8.png)
3.保存spu图片描述url
1.数据库表设计
use sunliving_commodity;
CREATE TABLE commodity_spu_info_desc
(
spu_id BIGINT NOT NULL COMMENT '商品 id',
decript LONGTEXT COMMENT '商品介绍图片',
PRIMARY KEY (spu_id)
) CHARSET = utf8mb4 COMMENT ='商品 spu 信息介绍';
select * from commodity_spu_info_desc;
2.生成基本CRUD
1.将生成的main目录覆盖sunliving-commodity的main目录
![image-20240423141530797](https://img-blog.csdnimg.cn/img_convert/7577fad6ee971df598536edb73a7d24d.png)
2.注释掉SpuInfoDescController.java的与shiro有关的部分
3.重启测试
![image-20240423141844270](https://img-blog.csdnimg.cn/img_convert/6dd6011139ddc81e130546f3a33578d5.png)
3.完成保存spu的图片描述url
1.后端 sunliving-commodity模块
1.将SpuInfoDescEntity.java的id修改为不是自增的
![image-20240423152006569](https://img-blog.csdnimg.cn/img_convert/7a3d02d79e8e9b8951a03fd4384457fb.png)
2.修改SpuInfoServiceImpl.java的saveSpuInfo方法即可,注意需要添加@Transactional进行事务管理
@Transactional
@Override
public void saveSpuInfo(SpuSaveVO spuSaveVO) {
SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
spuInfoEntity.setCreateTime(new Date());
spuInfoEntity.setUpdateTime(new Date());
List<String> decript = spuSaveVO.getDecript();
SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
if (decript != null && !decript.isEmpty()) {
spuInfoDescEntity.setDecript(String.join(",", decript));
} else {
spuInfoDescEntity.setDecript("暂无描述");
}
spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
spuInfoDescService.save(spuInfoDescEntity);
this.save(spuInfoEntity);
}
3.前后端联调
1.前端保存信息
2.后端在保存图片描述url的时候发现是空,原因是在将spuInfoEntity的id保存到db之前的确是空的,更改一下执行顺序即可
![image-20240423153604583](https://img-blog.csdnimg.cn/img_convert/1250c891249d5088e82494f7f1eec7fd.png)
3.将spuInfoEntity的保存提前
![image-20240423153819500](https://img-blog.csdnimg.cn/img_convert/9d0502ed6458cbfbf32a981a6ca361f5.png)
4.再次测试,成功保存信息
![image-20240423154059819](https://img-blog.csdnimg.cn/img_convert/517802ee069f9b85c681dc11ea8fe41b.png)
4.保存SPU图片集信息
1.数据库表设计
use sunliving_commodity;
CREATE TABLE commodity_spu_images
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
spu_id BIGINT COMMENT 'spu_id',
img_name VARCHAR(200) COMMENT '图片名',
img_url VARCHAR(255) COMMENT '图片地址',
img_sort INT COMMENT '顺序',
default_img TINYINT COMMENT '是否默认图',
PRIMARY KEY (id)
) CHARSET = utf8mb4 COMMENT ='spu 图片集';
SELECT *
FROM commodity_spu_images;
2.生成基本CRUD(不再赘述)
![image-20240423155424779](https://img-blog.csdnimg.cn/img_convert/78f95c09020c8a15b40fe51b2e21f828.png)
3.完成保存图片集信息
1.后端 sunliving-commodity 模块
1.修改SpuInfoServiceImpl.java的saveSpuInfo即可
@Transactional
@Override
public void saveSpuInfo(SpuSaveVO spuSaveVO) {
SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
BeanUtils.copyProperties(spuSaveVO, spuInfoEntity);
spuInfoEntity.setCreateTime(new Date());
spuInfoEntity.setUpdateTime(new Date());
List<String> decript = spuSaveVO.getDecript();
SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
if (decript != null && !decript.isEmpty()) {
spuInfoDescEntity.setDecript(String.join(",", decript));
} else {
spuInfoDescEntity.setDecript("暂无描述");
}
this.save(spuInfoEntity);
spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
spuInfoDescService.save(spuInfoDescEntity);
List<String> images = spuSaveVO.getImages();
if (images == null || images.isEmpty()) {
SpuImagesEntity spuImagesEntity = new SpuImagesEntity();
spuImagesEntity.setSpuId(spuInfoEntity.getId());
spuImagesEntity.setDefaultImg(0);
spuImagesEntity.setImgUrl("暂无图片");
spuImagesEntity.setImgSort(0);
spuImagesService.save(spuImagesEntity);
} else {
List<SpuImagesEntity> collect = images.stream().map(img -> {
SpuImagesEntity spuImagesEntity = new SpuImagesEntity();
spuImagesEntity.setSpuId(spuInfoEntity.getId());
spuImagesEntity.setDefaultImg(0);
spuImagesEntity.setImgUrl(img);
spuImagesEntity.setImgSort(0);
return spuImagesEntity;
}).collect(Collectors.toList());
spuImagesService.saveBatch(collect);
}
}
2.前后端联调
1.前端保存信息
2.后端查看数据库
![image-20240423163624417](https://img-blog.csdnimg.cn/img_convert/9a13724a5bc19eda15a58fcc4b3785ea.png)
5.保存SPU的基本属性
1.数据库表创建
use sunliving_commodity;
CREATE TABLE commodity_product_attr_value
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
spu_id BIGINT COMMENT '商品 id',
attr_id BIGINT COMMENT '属性 id',
attr_name VARCHAR(200) COMMENT '属性名',
attr_value VARCHAR(200) COMMENT '属性值',
attr_sort INT COMMENT '顺序',
quick_show TINYINT COMMENT '快速展示【是否展示在介绍上;0-否 1-是】',
PRIMARY KEY (id)
) CHARSET = utf8mb4 COMMENT ='spu 基本属性值';
select * from commodity_product_attr_value;
2.生成基本CRUD(不再赘述)
![image-20240423171620380](https://img-blog.csdnimg.cn/img_convert/41d95e324c65e4795e71e7769a57ac90.png)
3.保存SPU的基本属性
1.后端 sunliving-commodity模块
1.ProductAttrValueService.java 新增方法,批量保存
void saveProductAttrValue(List<ProductAttrValueEntity> productAttrValueEntities);
2.ProductAttrValueServiceImpl.java 实现方法
@Override
public void saveProductAttrValue(List<ProductAttrValueEntity> productAttrValueEntities) {
this.saveBatch(productAttrValueEntities);
}
3.修改SpuInfoServiceImpl.java 的saveSpuInfo方法,新增代码
List<BaseAttrs> baseAttrs = spuSaveVO.getBaseAttrs();
List<ProductAttrValueEntity> collect = baseAttrs.stream().map(
baseAttr -> {
ProductAttrValueEntity productAttrValueEntity = new ProductAttrValueEntity();
productAttrValueEntity.setSpuId(spuInfoEntity.getId());
productAttrValueEntity.setAttrId(baseAttr.getAttrId());
productAttrValueEntity.setQuickShow(baseAttr.getShowDesc());
productAttrValueEntity.setAttrSort(0);
productAttrValueEntity.setAttrValue(baseAttr.getAttrValues());
productAttrValueEntity.setAttrName(attrService.getById(baseAttr.getAttrId()).getAttrName());
return productAttrValueEntity;
}
).collect(Collectors.toList());
productAttrValueService.saveProductAttrValue(collect);
2.前后端联调
1.前端保存基本属性
2.后端查看数据库
![image-20240423192720404](https://img-blog.csdnimg.cn/img_convert/31a6487282ce46ff69b43aa2515c2302.png)
6.保存SKU的基本信息
1.数据库表设计
use sunliving_commodity;
CREATE TABLE commodity_sku_info
(
sku_id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'skuId',
spu_id BIGINT COMMENT 'spuId',
sku_name VARCHAR(255) COMMENT 'sku 名称',
sku_desc VARCHAR(2000) COMMENT 'sku 介绍描述',
catalog_id BIGINT COMMENT '所属分类 id',
brand_id BIGINT COMMENT '品牌 id',
sku_default_img VARCHAR(255) COMMENT '默认图片',
sku_title VARCHAR(255) COMMENT '标题',
sku_subtitle VARCHAR(2000) COMMENT '副标题',
price DECIMAL(18, 4) COMMENT '价格',
sale_count BIGINT COMMENT '销量',
PRIMARY KEY (sku_id)
) CHARSET = utf8mb4 COMMENT ='sku 信息';
SELECT *
FROM commodity_sku_info;
2.生成基本CRUD(不再赘述)
3.保存SKU基本信息 sunliving-commodity模块
1.SkuInfoService.java 新增批量添加的方法
void saveBatch(List<SkuInfoEntity> skuInfoEntities);
2.SkuInfoServiceImpl.java 实现方法
@Override
public void saveBatch(List<SkuInfoEntity> skuInfoEntities) {
this.saveBatch(skuInfoEntities);
}
3.SpuInfoServiceImpl.java 修改saveSpuInfo方法,新增代码
List<Skus> skus = spuSaveVO.getSkus();
List<SkuInfoEntity> skusCollect = skus.stream().map(sku -> {
SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
skuInfoEntity.setSkuDesc("");
skuInfoEntity.setSkuName(sku.getSkuName());
skuInfoEntity.setPrice(sku.getPrice());
skuInfoEntity.setSpuId(spuInfoEntity.getId());
List<Images> skuImages = sku.getImages();
for (Images image : skuImages) {
if (image.getDefaultImg() == 1) {
skuInfoEntity.setSkuDefaultImg(image.getImgUrl());
}
else {
skuInfoEntity.setSkuDefaultImg("暂无默认图片");
}
}
skuInfoEntity.setSkuSubtitle(sku.getSkuSubtitle());
skuInfoEntity.setSkuTitle(sku.getSkuTitle());
skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
skuInfoEntity.setSaleCount(0L);
return skuInfoEntity;
}).collect(Collectors.toList());
skuInfoService.saveSkuInfoEntitys(skusCollect);
4.前后端联调
1.后端报错,java.lang.StackOverflowError
![image-20240424152315435](https://img-blog.csdnimg.cn/img_convert/830ecc15f0406be493b6cf5d7c5b1772.png)
2.问题分析
- 这里只报了这一个StackOverflowError,没有任何其他的提示信息
- 栈溢出,只可能是无限递归,debug查看数据没问题,在调用saveBatch报错
- 检查一下saveBatch发现,我自定义的名字跟IService接口的saveBatch相同,这样就相当于重写了IService接口中的方法,然后不断递归,所以出现了栈溢出
- 解决方式:修改一下自定义方法的名字即可
![image-20240424153407345](https://img-blog.csdnimg.cn/img_convert/3a91891b46aba9726854cf218edcba25.png)
3.问题解决
1.修改自定义方法的名字
![image-20240424153745459](https://img-blog.csdnimg.cn/img_convert/0c1149a136b386142b32178b4b171d8d.png)
2.修改SpuInfoServiceImpl.java调用的方法
![image-20240424153844730](https://img-blog.csdnimg.cn/img_convert/f0f0538d373d9fbc3b00522bef136ccb.png)
3.重启测试,保存成功!
![image-20240424154050572](https://img-blog.csdnimg.cn/img_convert/a8b98c7534caaeb2bde33938855044bb.png)
![image-20240424154217814](https://img-blog.csdnimg.cn/img_convert/f144edbe3c7a14b63ca9f5084f82f546.png)
7.保存SPU和SKU图片信息
1.数据库表创建
use sunliving_commodity;
CREATE TABLE commodity_sku_images
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
sku_id BIGINT COMMENT 'sku_id',
img_url VARCHAR(255) COMMENT '图片地址',
img_sort INT COMMENT '排序',
default_img INT COMMENT '默认图[0 - 不是默认图,1 - 是默认图]',
PRIMARY KEY (id)
) CHARSET = utf8mb4 COMMENT ='sku 图片';
SELECT *
FROM commodity_sku_images
2.生成基本CRUD(不再赘述)
![image-20240424165537977](https://img-blog.csdnimg.cn/img_convert/a345d5b19e3480797b4cd2081c8e05c5.png)
3.完成保存SPU和SKU图片信息 sunliving-commodity模块
1.SkuImagesService.java 新增方法,批量保存sku图片
void saveSkuImages(List<SkuImagesEntity> skuImagesEntity);
2.SkuImagesServiceImpl.java 实现方法
@Override
public void saveSkuImages(List<SkuImagesEntity> skuImagesEntity) {
this.saveBatch(skuImagesEntity);
}
3.修改 SpuInfoServiceImpl.java,修改代码
List<Skus> skus = spuSaveVO.getSkus();
skus.forEach(sku -> {
SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
skuInfoEntity.setSkuDesc("");
skuInfoEntity.setSkuName(sku.getSkuName());
skuInfoEntity.setPrice(sku.getPrice());
skuInfoEntity.setSpuId(spuInfoEntity.getId());
List<Images> skuImages = sku.getImages();
for (Images image : skuImages) {
if (image.getDefaultImg() == 1) {
skuInfoEntity.setSkuDefaultImg(image.getImgUrl());
}
else {
skuInfoEntity.setSkuDefaultImg("暂无默认图片");
}
}
skuInfoEntity.setSkuSubtitle(sku.getSkuSubtitle());
skuInfoEntity.setSkuTitle(sku.getSkuTitle());
skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
skuInfoEntity.setSaleCount(0L);
skuInfoService.save(skuInfoEntity);
List<SkuImagesEntity> skuImagesEntities = skuImages.stream().map(image -> {
SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
skuImagesEntity.setSkuId(skuInfoEntity.getSkuId());
skuImagesEntity.setDefaultImg(image.getDefaultImg());
skuImagesEntity.setImgUrl(image.getImgUrl());
skuImagesEntity.setImgSort(0);
return skuImagesEntity;
}).collect(Collectors.toList());
skuImagesService.saveSkuImages(skuImagesEntities);
});
4.前后端联调
1.前端保存信息
2.后端测试,发现commodity_sku_images表插入了两条空的字段
![image-20240424200701966](https://img-blog.csdnimg.cn/img_convert/0f21e0c4a42b7eced24733cedcda6b10.png)
3.debug分析
1.第一个sku有两张图片,第二个sku没有图片,在遍历第二个sku时发现,即使第二个没有图片,前端会传一个url为空的数组,包含两个元素
![image-20240424195959111](https://img-blog.csdnimg.cn/img_convert/b50de4c6abb1120e7557b6c3933547f7.png)
2.在保存sku图片信息的时候,就会将这个图片的信息保存到数据库中
![image-20240424200456325](https://img-blog.csdnimg.cn/img_convert/269e50952964aadfb395214085146e48.png)
3.解决方式:在保存sku图片信息时加一个过滤即可,把url为空的过滤掉
![image-20240424200616141](https://img-blog.csdnimg.cn/img_convert/04ab1ac16b659b9af525c9f584393fa9.png)
4.重启测试,再添加一次,没有空值了
![image-20240424201542051](https://img-blog.csdnimg.cn/img_convert/49b4ce34752712d7ab358dba1c2e5f06.png)
8.保存SKU销售属性
1.数据库表设计
use sunliving_commodity;
CREATE TABLE commodity_sku_sale_attr_value
(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
sku_id BIGINT COMMENT 'sku_id',
attr_id BIGINT COMMENT 'attr_id',
attr_name VARCHAR(200) COMMENT '销售属性名',
attr_value VARCHAR(200) COMMENT '销售属性值',
attr_sort INT COMMENT '顺序',
PRIMARY KEY (id)
) CHARSET = utf8mb4 COMMENT ='sku 的销售属性/值表';
SELECT *
FROM commodity_sku_sale_attr_value;
2.生成基本CRUD(不再赘述)
![image-20240425092806248](https://img-blog.csdnimg.cn/img_convert/ce42a5e13c16b94ea1b83fd85bcd3809.png)
3.完成保存SKU销售属性 sunliving-commodity模块
1.SkuSaleAttrValueService.java 新增方法批量保存sku销售属性
void saveSkuSaleAttrValues(List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities);
2.SkuSaleAttrValueServiceImpl.java 实现方法
@Override
public void saveSkuSaleAttrValues(List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities) {
this.saveBatch(skuSaleAttrValueEntities);
}
3.SpuInfoServiceImpl.java 新增代码保存sku的销售属性信息
List<Attr> attr = sku.getAttr();
List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {
SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();
BeanUtils.copyProperties(a, skuSaleAttrValueEntity);
skuSaleAttrValueEntity.setSkuId(skuInfoEntity.getSkuId());
skuSaleAttrValueEntity.setAttrSort(0);
return skuSaleAttrValueEntity;
}).collect(Collectors.toList());
skuSaleAttrValueService.saveSkuSaleAttrValues(skuSaleAttrValueEntities);
4.重启测试
![image-20240425094745439](https://img-blog.csdnimg.cn/img_convert/46e05d0563d714ff2d46d92e99798b2c.png)
9.将vo转化为entity的总结
1.整体的 SpuSaveVO预览
![image-20240425095813804](https://img-blog.csdnimg.cn/img_convert/1e37478aa5f896dc743628ab8cdd3da1.png)
2.转换结构
3.转换规则
- 首先,一定是在Controller层,接收到一个完整的VO
- 将这个VO的基本属性,转换为一个主entity
- 将每个特殊属性再单独转为一个entity,需要与主entity的id关联,当然也可以关联其他属性
4.List类型的特殊属性细节说明
- 关于List类型的特殊属性,有两种选择,一种是将里面的内容,比如是String类型的图片url放到一个entity中使用逗号间隔,第二种是将每一个String都放到一个entity中,都是可以的
5.关于List里面还有多个特殊属性的处理
1.查看这里的 List
![image-20240425100758080](https://img-blog.csdnimg.cn/img_convert/d6d6e42a5305530f981f6dc80d94e626.png)
2.分析
- 可以看到一个SpuSaveVO中不仅有List类型的Skus,而且每个Skus中还有List类型的Attr
- 虽然看上去复杂,不过确实也复杂。
- 但是,关于List类型的属性,只需要记住一点,遍历出每一个元素,将其转化为entity,这个entity至少需要与上一层的entity的id关联