1.点击分类,连级查询分类功能
2.根据父分类查询品牌功能
3.新增商品功能
3.1.实体类
3.3.GoodsService
4.修改商品
4.1 修改spu,回显数据
- GET请求:传递spuid,查询spu对象spuDetail和skus对象,将对象信息回显到表单
- GoodsController
- public ResponseEntity querySpuDetailBySpuId(@PathVariable(“spuId”)Long spuId)
- public ResponseEntity querySkusBySpuId(@RequestParam(“id”)Long spuId)
- GoodsService
- public SpuDetail querySpuDetailBySpuId(Long spuId)
- public List querySkusBySpuId(Long spuId) 还要forEach setstock
4.2 提交修改数据
- POST请求:传递SpuBo对象
- GoodsController
- public ResponseEntity updateGoods(@RequestBody SpuBo spuBo)
- GoodsService
- public void update(SpuBo spu)
- 注意:新增表单提交和编辑表单提交不一样的地方
- 编辑表单提交:
- 查询以前的skus, 如果存在,则删除,spuId得到skus,删除数据库中的skus
- 新增skus和库存, spuId得到skus
- 更新补全spu,更新spuDetail
- 新增表单提交:
- 补全spu
- 新增skus
实现功能:新增商品——选择商品分类后——下拉菜单出现所有对应品牌
Request URL: http://api.leyou.com/api/item/category/list?pid=0
Request URL: http://api.leyou.com/api/item/category/list?pid=74
Request URL: http://api.leyou.com/api/item/category/list?pid=75
Request URL: http://api.leyou.com/api/item/brand/cid/76
Request URL: http://api.leyou.com/api/item/spec/params?cid=76
根据父类pid查询子类,
根据分类id查询品牌名称,多表查询,tb_brand INNER JOIN tb_brand_ccategory
点击提交,查看控制台提交的数据格式:
整体是一个json格式数据,包含Spu表所有数据:
SPU和SpuDetail实体类已经添加过,添加Sku和Stock对象:
Sku
@Table(name = "tb_sku")
public class Sku {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long spuId;
private String title;
private String images;
private Long price;
private String ownSpec;// 商品特殊规格的键值对
private String indexes;// 商品特殊规格的下标
private Boolean enable;// 是否有效,逻辑删除用
private Date createTime;// 创建时间
private Date lastUpdateTime;// 最后修改时间
@Transient
private Integer stock;// 库存
}
注意:这里保存了一个库存字段,在数据库中是另外一张表保存的,方便查询。
Stock
@Table(name = "tb_stock")
public class Stock {
@Id
private Long skuId;
private Integer seckillStock;// 秒杀可用库存
private Integer seckillTotal;// 已秒杀数量
private Integer stock;// 正常库存
}
结合浏览器页面控制台,可以发现:
请求方式:POST
请求路径:/goods
请求参数:Spu的json格式的对象,spu中包含spuDetail和Sku集合。这里我们该怎么接收?我们之前定义了一个SpuBo对象,作为业务对象。这里也可以用它,不过需要再扩展spuDetail和skus字段:
public class SpuBo extends Spu {
String cname;// 商品分类名称
String bname;// 品牌名称
SpuDetail spuDetail;// 商品详情
List<Sku> skus;// sku列表
}
返回类型:无
在GoodsController中添加新增商品的代码:
@PostMapping("goods")
public ResponseEntity<Void> saveGoods(@RequestBody SpuBo spuBo){
this.goodsService.saveGoods(spuBo);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
注意:通过@RequestBody注解来接收Json请求
这里的逻辑比较复杂,我们除了要对SPU新增以外,还要对SpuDetail、Sku、Stock进行保存
/**
* 新增商品
* @param spuBo
*/
@Transactional
public void saveGoods(SpuBo spuBo) {
// 新增spu
// 设置默认字段
spuBo.setId(null);
spuBo.setSaleable(true);
spuBo.setValid(true);
spuBo.setCreateTime(new Date());
spuBo.setLastUpdateTime(spuBo.getCreateTime());
this.spuMapper.insertSelective(spuBo);
// 新增spuDetail
SpuDetail spuDetail = spuBo.getSpuDetail();
spuDetail.setSpuId(spuBo.getId());
this.spuDetailMapper.insertSelective(spuDetail);
saveSkuAndStock(spuBo);
}
private void saveSkuAndStock(SpuBo spuBo) {
spuBo.getSkus().forEach(sku -> {
// 新增sku
sku.setSpuId(spuBo.getId());
sku.setCreateTime(new Date());
sku.setLastUpdateTime(sku.getCreateTime());
this.skuMapper.insertSelective(sku);
// 新增库存
Stock stock = new Stock();
stock.setSkuId(sku.getId());
stock.setStock(sku.getStock());
this.stockMapper.insertSelective(stock);
});
}
通用Mapper
在商品详情页,每一个商品后面,都会有一个编辑按钮:
点击这个按钮,就会打开一个商品编辑窗口,我们看下它所绑定的点击事件:(在item/Goods.vue)
对应的方法:
可以看到这里发起了两个请求,在查询商品详情和sku信息。
因为在商品列表页面,只有spu的基本信息:id、标题、品牌、商品分类等。比较复杂的商品详情(spuDetail)和sku信息都没有,编辑页面要回显数据,就需要查询这些内容。
因此,接下来我们就编写后台接口,提供查询服务接口。
GoodsController
需要分析的内容:
@GetMapping("spu/detail/{spuId}")
public ResponseEntity<SpuDetail> querySpuDetailBySpuId(@PathVariable("spuId")Long spuId){
SpuDetail spuDetail = this.goodsService.querySpuDetailBySpuId(spuId);
if (spuDetail == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(spuDetail);
}
GoodsService
/**
* 根据spuId查询spuDetail
* @param spuId
* @return
*/
public SpuDetail querySpuDetailBySpuId(Long spuId) {
return this.spuDetailMapper.selectByPrimaryKey(spuId);
}
分析
GoodsController
@GetMapping("sku/list")
public ResponseEntity<List<Sku>> querySkusBySpuId(@RequestParam("id")Long spuId){
List<Sku> skus = this.goodsService.querySkusBySpuId(spuId);
if (CollectionUtils.isEmpty(skus)) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(skus);
}
GoodsService
需要注意的是,为了页面回显方便,我们一并把sku的库存stock也查询出来
/**
* 根据spuId查询sku的集合
* @param spuId
* @return
*/
public List<Sku> querySkusBySpuId(Long spuId) {
Sku sku = new Sku();
sku.setSpuId(spuId);
List<Sku> skus = this.skuMapper.select(sku);
skus.forEach(s -> {
Stock stock = this.stockMapper.selectByPrimaryKey(s.getId());
s.setStock(stock.getStock());
});
return skus;
}
随便点击一个编辑按钮,发现数据回显完成:
这里的保存按钮与新增其实是同一个,因此提交的逻辑也是一样的,这里不再赘述。
接下来,我们编写后台,实现修改商品接口。
@PutMapping("goods")
public ResponseEntity<Void> updateGoods(@RequestBody SpuBo spuBo){
this.goodsService.updateGoods(spuBo);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
spu数据可以修改,但是SKU数据无法修改,因为有可能之前存在的SKU现在已经不存在了,或者以前的sku属性都不存在了。比如以前内存有4G,现在没了。
因此这里直接删除以前的SKU,然后新增即可。
代码:
@Transactional
public void update(SpuBo spu) {
// 查询以前sku
List<Sku> skus = this.querySkuBySpuId(spu.getId());
// 如果以前存在,则删除
if(!CollectionUtils.isEmpty(skus)) {
List<Long> ids = skus.stream().map(s -> s.getId()).collect(Collectors.toList());
// 删除以前库存
Example example = new Example(Stock.class);
example.createCriteria().andIn("skuId", ids);
this.stockMapper.deleteByExample(example);
// 删除以前的sku
Sku record = new Sku();
record.setSpuId(spu.getId());
this.skuMapper.delete(record);
}
// 新增sku和库存
saveSkuAndStock(spuBo);
// 更新spu
spu.setLastUpdateTime(new Date());
spu.setCreateTime(null);
spu.setValid(null);
spu.setSaleable(null);
this.spuMapper.updateByPrimaryKeySelective(spu);
// 更新spu详情
this.spuDetailMapper.updateByPrimaryKeySelective(spu.getSpuDetail());
}
与以前一样。
商品的删除、上下架大家自行实现。