2-2 SPU 和 SKU 详解及 MyBatisPlus 自动生成
2)选择品牌
分类选择完成后,需要加载品牌,品牌加载并非一次性加载完成,而是根据选择的分类进行加载。
分类品牌关系表:
CREATE TABLE category_brand
(
category_id
int(11) NOT NULL COMMENT '分类 ID',
brand_id
int(11) NOT NULL COMMENT '品牌 ID',
PRIMARY KEY (brand_id
,category_id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
品牌表:
CREATE TABLE brand
(
id
int(11) NOT NULL AUTO_INCREMENT COMMENT '品牌 id',
name
varchar(100) NOT NULL COMMENT '品牌名称',
image
varchar(1000) DEFAULT '' COMMENT '品牌图片地址',
initial
varchar(1) DEFAULT '' COMMENT '品牌的首字母',
sort
int(11) DEFAULT NULL COMMENT '排序',
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='品牌表';
3)属性加载
当选择分类后,加载分类对应的属性。
分类属性表:
CREATE TABLE category_attr
(
category_id
int(11) NOT NULL,
attr_id
int(11) NOT NULL COMMENT '属性分类表',
PRIMARY KEY (category_id
,attr_id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
属性表:
CREATE TABLE sku_attribute
(
id
int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
name
varchar(50) DEFAULT NULL COMMENT '属性名称',
options
varchar(2000) DEFAULT NULL COMMENT '属性选项',
sort
int(11) DEFAULT NULL COMMENT '排序',
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
对应的 Bean 我们已经提前写好,如下
package com.bobo.vip.mall.goods.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/*****
@Author: 波波
@Description: 云商城
****/
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus 表映射注解
@TableName(value = "category")
public class Category implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer sort;
private Integer parentId;
}
package com.bobo.vip.mall.goods.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/*****
@Author: 波波
@Description: 云商城
****/
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus 表映射注解
@TableName(value = "category_attr")
public class CategoryAttr {
@TableField
private Integer categoryId;
@TableField
private Integer attrId;
}
/*****
@Author: 波波
@Description: 云商城
****/
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus 表映射注解
@TableName(value = "category_brand")
public class CategoryBrand {
@TableField
private Integer categoryId;
@TableField
private Integer brandId;
}
/*****
@Author: 波波
@Description: 云商城
****/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
// Spu
private Spu spu;
// Sku
private List<Sku> skus;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus 表映射注解
@TableName(value = "sku")
public class Sku {
@TableId(type = IdType.ASSIGN_ID)
private String id;
private String name;
private Integer price;
private Integer num;
private String image;
private String images;
private Date createTime;
private Date updateTime;
private String spuId;
private Integer categoryId;
private String categoryName;
private Integer brandId;
private String brandName;
private String skuAttribute;
private Integer status;
}
/*****
@Author: 波波
@Description: 云商城
****/
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus 表映射注解
@TableName(value = "sku_attribute")
public class SkuAttribute implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String options;
private Integer sort;
//对应分类
@TableField(exist = false)
private List<Category> categories;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus 表映射注解
@TableName(value = "spu")
public class Spu {
@TableId(type = IdType.ASSIGN_ID)
private String id;
private String name;
private String intro;
private Integer brandId;
private Integer categoryOneId;
private Integer categoryTwoId;
private Integer categoryThreeId;
private String images;
private String afterSalesService;
private String content;
private String attributeList;
private Integer isMarketable;
private Integer isDelete;
private Integer status;
}
3.1 分类加载
分类功能需要实现按照父 ID 查询,最开始初始化加载的是顶级父类,parent_id=0,后面每次点击的时候都根据传入的 id 查询子分类。
1)Mapper
创建com.bobo.vip.mall.goods.mapper.CategoryMapper
,代码如下:
public interface CategoryMapper extends BaseMapper<Category> {
}
2)Service
接口:com.bobo.vip.mall.goods.service.CategoryService
代码如下:
public interface CategoryService extends IService<Category> {
/**
根据父 ID 查询子分类
@param pid
@return
*/
List<Category> queryByParentId(Integer pid);
}
实现类:com.bobo.vip.mall.goods.service.impl.CategoryServiceImpl
代码如下:
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper,Category> implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
/***
根据父 ID 查询子分类
@param pid
@return
*/
@Override
public List<Category> queryByParentId(Integer pid) {
//条件封装
QueryWrapper<Category> queryWrapper = new QueryWrapper<Category>();
queryWrapper.eq("parent_id",pid);
return categoryMapper.selectList(queryWrapper);
}
}
3)Controller
创建com.bobo.vip.mall.goods.controller.CategoryController
代码如下;
@RestController
@RequestMapping(value = "/category")
@CrossOrigin
public class CategoryController {
@Autowired
private CategoryService categoryService;
/****
根据父 ID 查询子分类
*/
@GetMapping(value = "/parent/{pid}")
public RespResult<List<Category>> list(@PathVariable(value = "pid")Integer pid){
List<Category> categories = categoryService.queryByParentId(pid);
return RespResult.ok(categories);
}
}
3.2 品牌加载
品牌需要根据分类进行加载,当用户选择第 3 级分类的时候,加载品牌,品牌数据需要经过category_brand
表关联查询。
我们可以按照如下步骤实现:
1、查询 category_brand 中指定分类对应的品牌 ID 集合
2、从 brand 查出品牌集合
1)Mapper
修改com.bobo.vip.mall.goods.mapper.BrandMapper
,添加根据分类 ID 查询品牌 ID 集合:
//根据分类 ID 查询品牌集合
@Select("select brand_id from category_brand where category_id=#{id}")
List<Integer> queryBrandIds(Integer id);
2)Service
接口:com.bobo.vip.mall.goods.service.BrandService
中添加根据分类 ID 查询品牌集合方法
//根据分类 ID 查询品牌
List<Brand> queryByCategoryId(Integer id);
实现类:com.bobo.vip.mall.goods.service.impl.BrandServiceImpl
/***
根据分类 ID 查询品牌
@param id
@return
*/
@Override
public List<Brand> queryByCategoryId(Integer id) {
//查询分类 ID 对应的品牌集合
List<Integer> brandIds = brandMapper.queryBrandIds(id);
//根据品牌 ID 集合查询品牌信息
List<Brand> brands = brandMapper.selectBatchIds(brandIds);
return brands;
}
3)Controller
修改com.bobo.vip.mall.goods.controller.BrandController
添加根据分类 ID 查询品牌集合
/****
根据分类 ID 查询品牌
*/
@GetMapping(value = "/category/{id}")
public RespResult<List<Brand>> categoryBrands(@PathVariable(value = "id")Integer id){
List<Brand> brands = brandService.queryByCategoryId(id);
return RespResult.ok(brands);
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/
20210115110726332.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NTI2NTcz,size_16,color_FFFFFF,t_70)
3.3 属性加载
属性也称为规格,属性也需要根据分类查询,我们可以按照如下思路实现:
1、先从 category_attr 根据分类 ID 查询出当前分类拥有的属性 ID 集合
2、从 sku_attribute 中查询属性集合
1)Mapper
创建com.bobo.vip.mall.goods.mapper.SkuAttributeMapper
实现根据分类 ID 查询属性信息。
/***
根据分类 ID 查询属性集合
@param id
@return
*/
@Select("SELECT * FROM sku_attribute WHERE id IN(SELECT attr_id FROM category_attr WHERE category_id=#{id})")
List<SkuAttribute> queryByCategoryId(Integer id);
2)Service
接口:com.bobo.vip.mall.goods.service.SkuAttributeService
添加根据分类 ID 查询属性集合方法
//根据分类 ID 查询属性集合
List<SkuAttribute> queryList(Integer id);
实现类:com.bobo.vip.mall.goods.service.impl.SkuAttributeServiceImpl
添加如下实现方法
/***
根据分类 ID 查询属性集合
@param id
@return
*/
@Override
public List<SkuAttribute> queryList(Integer id) {
return skuAttributeMapper.queryByCategoryId(id);
}
3)Controller
创建com.bobo.vip.mall.goods.controller.SkuAttributeController
,添加如下方法
/***
根据分类 ID 查询
*/
@GetMapping(value = "/category/{id}")
public RespResult<SkuAttribute> categoryAttributeList(@PathVariable(value = "id")Integer id){
//根据分类 ID 查询属性参数
List<SkuAttribute> skuAttributes = skuAttributeService.queryList(id);
return RespResult.ok(skuAttributes);
}
4.1 复合对象分析
商品发布,如上图,我们可以发现发布的商品信息包含 Sku 和 Spu,因此我们应该在后端能有一个对象同时能接到 Spu 和多个 Sku,方法有很多种,我们可以直接在 Spu 中写一个List<Sku>
,但这种方法不推荐,按照对象设计原则,对一个对象进行扩展时,尽量避免对原始对象造成改变,因此我们可以使用复合类,可以创建一个Prodcut
类,该类中有 Spu 也有List<Sku>
,代码如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
// Spu
private Spu spu;
// Sku
private List<Sku> skus;
}
4.2 添加商品
添加商品的时候,我们需要保存 Spu,同时需要添加多个 Sku。我们可以在华为商城中看看真实电商中 Sku 名字特征,每次点击不同属性的时候,前部分名字一样,只是将名字中的规格替换了,也就是说 Sku 的名字其实是组合成的,一部分是 Spu 的一部分是 Sku 的,可以进行组合。
1)名字分析
添加商品的时候,会将商品的属性传入后台,格式如下,如果把规格名字添加到名字中,那就是华为商城中的效果了,我们可以这么做,把属性解析成 Map,然后每个属性值添加到商品名字中即可。
{"适合人群":"有一定java基础的人","书籍分类":"软件编程"}
2)实现代码
Mapper
com.bobo.vip.mall.goods.mapper.SpuMapper
代码如下:
public interface SpuMapper extends BaseMapper<Spu> {
}
com.bobo.vip.mall.goods.mapper.SkuMapper
代码如下:
public interface SkuMapper extends BaseMapper<Sku> {
}
Service
com.bobo.vip.mall.goods.service.SpuService
中添加产品方法如下
public interface SpuService extends IService<Spu> {
//保存商品
void saveProduct(Product product);
}
com.bobo.vip.mall.goods.service.impl.SpuServiceImpl
中添加产品方法如下:
@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper,Spu> implements SpuService {
@Autowired
private SkuMapper skuMapper;
@Autowired
private SpuMapper spuMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private BrandMapper brandMapper;
// 保存商品
@Override
public void saveProduct(Product product) {
//Spu
Spu spu = product.getSpu();
//上架
spu.setIsMarketable(1);
//未删除
spu.setIsDelete(0);
//状态
spu.setStatus(1);
//添加
spuMapper.insert(spu);
//查询三级分类
Category category = categoryMapper.selectById(spu.getCategoryThreeId());
//查询品牌
Brand brand = brandMapper.selectById(spu.getBrandId());
//当前时间
Date now = new Date();
//新增 Sku 集合
for (Sku sku : product.getSkus()) {
//设置名字
String skuName = spu.getName();
Map<String,String> attrMap = JSON.parseObject(sku.getSkuAttribute(), Map.class);
for (Map.Entry<String, String> entry : attrMap.entrySet()) {
skuName+= " "+entry.getValue();
}
sku.setName(skuName);
//设置图片
sku.setImages(spu.getImages());
//设置状态
sku.setStatus(1);
//设置类目 ID
sku.setCategoryId(spu.getCategoryThreeId());
//设置类目名称
sku.setCategoryName(category.getName());
//设置品牌 ID
sku.setBrandId(brand.getId());
//设置品牌名称
sku.setBrandName(brand.getName());
//设置 Spuid
sku.setSpuId(spu.getId());
//时间
sku.setCreateTime(now);
sku.setUpdateTime(now);
//增加
skuMapper.insert(sku);
}
}
}
Controller
创建com.bobo.vip.mall.goods.controller.SpuController
,添加产品代码如下:
@Autowired
private SpuService spuService;
/***
保存
*/
@PostMapping(value = "/save")
public RespResult save(@RequestBody Product product){
//保存
spuService.saveProduct(product);
return RespResult.ok();
}
4.3 产品修改
产品修改其实和产品添加几乎一致,只需要做小改动即可,实现步骤如下:
1、如果 Spu 的 id 值不为空,说明是修改操作
2、如果是修改操作,先删除之前对应的 Sku 集合
3、其他流程和添加商品一致
评论