8.新增商品

    技术2022-07-10  118

    总结

    1.点击分类,连级查询分类功能

    分类表有id字段和父分类pid字段,根据父分类pid查询id。id又是子类的父分类pid。

    2.根据父分类查询品牌功能

    注意:分类和品牌是多对多关系,根据父分类id查询品牌,需要多表查询inner join on

    3.新增商品功能

    表单需要提交哪些数据:肯定涉及到spu, sku表, 新增表单:新增的是一个系列,系列下面有很多sku商品。 spu表:品牌id,商品分类id,副标题subTitile,标题title。spuDetail表:json对象,售后服务,商品描述,包装列表…sku表:title标题,images图片,price价格,stock库存,ownSpec特有规格参数,indexes特有规格参数下标

    3.1.实体类

    Spu, SpuBo, Stock, Sku下有Stock 3.2.GoodsController 表单请求,有很多请求参数,将请求参数封装到SpuBo对象: cname, bname, spuDetail, skus。SpuBo对象去接收传过来的字段数据

    3.3.GoodsService

    不仅要将添加数据到spu表,还要添加到spu_detail表,sku表,stock表 根据接收到的SpuBo对象,提取数据,保存到各实体对象中:Spu对象,sku对象,SpuDetal对象,stock对象 注意要补全接收数据SpuBo没有的参数到各对象中。如:spu自增的id设为null, 创建时间等 mapper将对象添加到数据库中逻辑:补全spu对象:spuBo.setId(null),spuBo.setSaleable(true)… spu到数据库补全spuDetail对象,由spuId得到spuDetail对象, spuDetail到数据库补全skus, 由spuId得到skus对象, skus到数据库补全stock, 由skuId得到stcok对象, stock到数据库

    4.修改商品

    注意:修改商品包括两个功能:回显和提交编辑回显,即根据SpuId查询spu对象,SpuDetail,skus对象,得到系列商品基本信息(品牌标题分类等)和细节信息(商品详情,包装)和skus。

    4.1 修改spu,回显数据 - GET请求:传递spuid,查询spu对象spuDetail和skus对象,将对象信息回显到表单 - GoodsController - public ResponseEntity querySpuDetailBySpuId(@PathVariable(“spuId”)Long spuId) - public ResponseEntity<List> 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表所有数据:

    brandId:品牌idcid1、cid2、cid3:商品分类idsubTitle:副标题title:标题spuDetail:是一个json对象,代表商品详情表数据 afterService:售后服务description:商品描述packingList:包装列表specialSpec:sku规格属性模板genericSpec:通用规格参数 skus:spu下的所有sku数组,元素是每个sku对象: title:标题images:图片price:价格stock:库存ownSpec:特有规格参数indexes:特有规格参数的下标

    后台实现

    实体类

    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;// 正常库存 }

    GoodsController

    结合浏览器页面控制台,可以发现:

    请求方式: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请求

    GoodsService

    这里的逻辑比较复杂,我们除了要对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

    通用Mapper

    2.修改商品

    2.1.编辑按钮点击事件

    在商品详情页,每一个商品后面,都会有一个编辑按钮: 点击这个按钮,就会打开一个商品编辑窗口,我们看下它所绑定的点击事件:(在item/Goods.vue) 对应的方法: 可以看到这里发起了两个请求,在查询商品详情和sku信息。

    因为在商品列表页面,只有spu的基本信息:id、标题、品牌、商品分类等。比较复杂的商品详情(spuDetail)和sku信息都没有,编辑页面要回显数据,就需要查询这些内容。

    因此,接下来我们就编写后台接口,提供查询服务接口。

    2.2.查询SpuDetail接口

    GoodsController

    需要分析的内容:

    请求方式:GET请求路径:/spu/detail/{id}请求参数:id,应该是spu的id返回结果:SpuDetail对象 @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); }

    测试

    2.3.查询sku

    分析

    请求方式:Get请求路径:/sku/list请求参数:id,应该是spu的id返回结果:sku的集合

    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; }

    测试:

    2.4.页面回显

    随便点击一个编辑按钮,发现数据回显完成:

    2.5.页面提交

    这里的保存按钮与新增其实是同一个,因此提交的逻辑也是一样的,这里不再赘述。

    随便修改点数据,然后点击保存,可以看到浏览器已经发出请求:

    2.6.后台实现

    接下来,我们编写后台,实现修改商品接口。

    2.6.1.GoodsController

    请求方式:PUT请求路径:/请求参数:Spu对象返回结果:无 @PutMapping("goods") public ResponseEntity<Void> updateGoods(@RequestBody SpuBo spuBo){ this.goodsService.updateGoods(spuBo); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); }

    2.6.2.GoodsService

    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()); }

    2.6.3.mapper

    与以前一样。

    2.7.其它

    商品的删除、上下架大家自行实现。

    Processed: 0.023, SQL: 9