博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
后台管理系统之商品管理
阅读量:2389 次
发布时间:2019-05-10

本文共 13048 字,大约阅读时间需要 43 分钟。

文章目录

1.商品查询

效果预览

接下来,我们实现商品管理的页面,先看下我们要实现的效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PMAWButm-1586587739098)(assets/1526268595873.png)]

可以看出整体是一个table,然后有新增按钮。是不是跟昨天写品牌管理很像?

页面请求

先看整体页面结构(Goods.vue):

并且在Vue实例挂载后就会发起查询(mounted调用getDataFromServer方法初始化数据):

我们刷新页面,可以看到浏览器发起已经发起了查询商品数据的请求:

在这里插入图片描述

因此接下来,我们编写接口即可。

后台(提供)接口

页面已经准备好,接下来在后台提供分页查询SPU的功能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1qwI3qQ-1586587739099)(assets/1543414148030.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6InLV9Mw-1586587739099)(assets/1543413983147.png)]

实体类

在leyou-item-interface工程中添加实体类:

SPU

@Table(name = "tb_spu")public class Spu {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long brandId; private Long cid1;// 1级类目 private Long cid2;// 2级类目 private Long cid3;// 3级类目 private String title;// 标题 private String subTitle;// 子标题 private Boolean saleable;// 是否上架 private Boolean valid;// 是否有效,逻辑删除用 private Date createTime;// 创建时间 private Date lastUpdateTime;// 最后修改时间 // 省略getter和setter}

SPU详情

@Table(name="tb_spu_detail")public class SpuDetail {
@Id private Long spuId;// 对应的SPU的id private String description;// 商品描述 private String specialSpec;// 商品特殊规格的名称及可选值模板 private String genericSpec;// 商品的全局规格属性 private String packingList;// 包装清单 private String afterService;// 售后服务 // 省略getter和setter}

mapper

public interface SpuMapper extends Mapper
{
}

controller

先分析:

  • 请求方式:GET

  • 请求路径:/spu/page

  • 请求参数:

    • page:当前页
    • rows:每页大小
    • key:过滤条件
    • saleable:上架或下架
  • 返回结果:商品SPU的分页信息。

    • 要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,怎么办?

      我们可以新建一个类,继承SPU,并且拓展cname和bname属性,写到leyou-item-interface

      public class SpuBo extends Spu {
      String cname;// 商品分类名称 String bname;// 品牌名称 // 略 。。}

编写controller代码:

我们把与商品相关的一切业务接口都放到一起,起名为GoodsController,业务层也是这样

@Controllerpublic class GoodsController {
@Autowired private GoodsService goodsService; @GetMapping("spu/page") public ResponseEntity
> querySpuBoByPage( @RequestParam(value = "key", required = false)String key, @RequestParam(value = "saleable", required = false)Boolean saleable, @RequestParam(value = "page", defaultValue = "1")Integer page, @RequestParam(value = "rows", defaultValue = "5")Integer rows ){
PageResult
pageResult = this.goodsService.querySpuBoByPage(key, saleable, page, rows); if(CollectionUtils.isEmpty(pageResult.getItems())){
return ResponseEntity.notFound().build(); } return ResponseEntity.ok(pageResult); }}

service

所有商品相关的业务(包括SPU和SKU)放到一个业务下:GoodsService。

@Servicepublic class GoodsService {
@Autowired private SpuMapper spuMapper; @Autowired private CategoryService categoryService; @Autowired private BrandMapper brandMapper; public PageResult
querySpuBoByPage(String key, Boolean saleable, Integer page, Integer rows) {
Example example = new Example(Spu.class); Example.Criteria criteria = example.createCriteria(); // 搜索条件 if (StringUtils.isNotBlank(key)) {
criteria.andLike("title", "%" + key + "%"); } if (saleable != null) {
criteria.andEqualTo("saleable", saleable); } // 分页条件 PageHelper.startPage(page, rows); // 执行查询 List
spus = this.spuMapper.selectByExample(example); PageInfo
pageInfo = new PageInfo<>(spus); List
spuBos = new ArrayList<>(); spus.forEach(spu->{
SpuBo spuBo = new SpuBo(); // copy共同属性的值到新的对象 BeanUtils.copyProperties(spu, spuBo); // 查询分类名称 List
names = this.categoryService.queryNamesByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3())); spuBo.setCname(StringUtils.join(names, "/")); // 查询品牌的名称 spuBo.setBname(this.brandMapper.selectByPrimaryKey(spu.getBrandId()).getName()); spuBos.add(spuBo); }); return new PageResult<>(pageInfo.getTotal(), spuBos); }}

Category中拓展查询名称的功能

页面需要商品的分类名称需要在这里查询,因此要额外提供查询分类名称的功能,

在CategoryService中添加功能:

public List
queryNamesByIds(List
ids) {
List
list = this.categoryMapper.selectByIdList(ids); List
names = new ArrayList<>(); for (Category category : list) {
names.add(category.getName()); } return names; // return list.stream().map(category -> category.getName()).collect(Collectors.toList());}

mapper的selectByIdList方法是来自于通用mapper。不过需要我们在mapper上继承一个通用mapper接口:

public interface CategoryMapper extends Mapper
, SelectByIdListMapper
{
}

测试

刷新页面,查看效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0C0I0qpA-1586587739100)(assets/1528450054541.png)]

基本与预览的效果一致,OK!

2.商品新增

在这里插入图片描述

里面把商品的数据分为了4部分来填写:

  • 基本信息:主要是一些简单的文本数据,包含了SPU和SpuDetail的部分数据,如
    • 商品分类:是SPU中的cid1cid2cid3属性
    • 品牌:是spu中的brandId属性
    • 标题:是spu中的title属性
    • 子标题:是spu中的subTitle属性
    • 售后服务:是SpuDetail中的afterService属性
    • 包装列表:是SpuDetail中的packingList属性
  • 商品描述:是SpuDetail中的description属性,数据较多,所以单独放一个页面
  • 规格参数:商品规格信息,对应SpuDetail中的genericSpec属性
  • SKU属性:spu下的所有Sku信息

对应到页面中的四个stepper-content

在这里插入图片描述

品牌选择

页面需要去后台查询品牌信息,我们自然需要提供:

请求方式:GET

请求路径:/brand/cid/{cid}

请求参数:cid

响应数据:品牌集合

BrandController

@GetMapping("cid/{cid}")public ResponseEntity
> queryBrandsByCid(@PathVariable("cid")Long cid){
List
brands = this.brandService.queryBrandsByCid(cid); if (CollectionUtils.isEmpty(brands)) {
return ResponseEntity.notFound().build(); } return ResponseEntity.ok(brands);}

BrandService

public List
queryBrandsByCid(Long cid) {
return this.brandMapper.selectBrandByCid(cid);}

BrandMapper

根据分类查询品牌有中间表,需要自己编写Sql:

@Select("SELECT b.* from tb_brand b INNER JOIN tb_category_brand cb on b.id=cb.brand_id where cb.category_id=#{cid}")List
selectBrandByCid(Long cid);

商品描述

商品描述信息比较复杂,而且图文并茂,甚至包括视频。

这样的内容,一般都会使用富文本编辑器一款支持Vue的富文本编辑器vue-quill-editor

GitHub的主页:https://github.com/surmon-china/vue-quill-editor

Vue-Quill-Editor是一个基于Quill的富文本编辑器:

使用指南

使用非常简单:已经在项目中集成。以下步骤不需操作,仅供参考

第一步:安装,使用npm命令:

npm install vue-quill-editor --save

第二步:加载,在js中引入:

全局引入:

import Vue from 'vue'import VueQuillEditor from 'vue-quill-editor'const options = {
}; /* { default global options } */Vue.use(VueQuillEditor, options); // options可选

局部引入:

import 'quill/dist/quill.core.css'import 'quill/dist/quill.snow.css'import 'quill/dist/quill.bubble.css'import {
quillEditor} from 'vue-quill-editor'var vm = new Vue({
components:{
quillEditor }})

我们这里采用局部引用:

在这里插入图片描述

第三步:页面使用:

自定义的富文本编辑器

不过这个组件有个小问题,就是图片上传的无法直接上传到后台,因此我们对其进行了封装,支持了图片的上传。

  • upload-url:是图片上传的路径
  • v-model:双向绑定,将富文本编辑器的内容绑定到goods.spuDetail.description

可以看到这里是根据商品分类id查询规格参数:SpecParam。我们之前写过一个根据gid(分组id)来查询规格参数的接口,我们接下来完成根据分类id查询规格参数。

改造查询规格参数接口

我们在原来的根据 gid(规格组id)查询规格参数的接口上,添加一个参数:cid,即商品分类id。

等一下, 考虑到以后可能还会根据是否搜索、是否为通用属性等条件过滤,我们多添加几个过滤条件:

@GetMapping("params")    public ResponseEntity
> queryParams( @RequestParam(value = "gid", required = false)Long gid, @RequestParam(value = "cid", required = false)Long cid, @RequestParam(value = "generic", required = false)Boolean generic, @RequestParam(value = "searching", required = false)Boolean searching ){
List
params = this.specificationService.queryParams(gid, cid, generic, searching); if (CollectionUtils.isEmpty(params)){
return ResponseEntity.notFound().build(); } return ResponseEntity.ok(params); }

改造SpecificationService:

/**     * 根据gid查询规格参数     * @param gid     * @return     */    public List
queryParams(Long gid, Long cid, Boolean generic, Boolean searching) {
SpecParam record = new SpecParam(); record.setGroupId(gid); record.setCid(cid); record.setGeneric(generic); record.setSearching(searching); return this.specParamMapper.select(record); }

如果param中有属性为null,则不会把属性作为查询条件,因此该方法具备通用性,即可根据gid查询,也可根据cid查询。

在这里插入图片描述

后台实现

实体类

SPU和SpuDetail实体类已经添加过,添加Sku和Stock(库存)对象:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cqyqd55B-1586588712837)(assets/1528472531490.png)]

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
skus;// sku列表}
  • 返回类型:无

在GoodsController中添加新增商品的代码:

@PostMapping("goods")public ResponseEntity
saveGoods(@RequestBody SpuBo spuBo){
this.goodsService.saveGoods(spuBo); return ResponseEntity.status(HttpStatus.CREATED).build();}

注意:通过@RequestBody注解来接收Json请求

GoodsService

这里的逻辑比较复杂,我们除了要对SPU新增以外,还要对SpuDetail、Sku、Stock进行保存

/**     * 新增商品     * @param spuBo     */@Transactionalpublic 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,略

目录结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yIe1OiZY-1586588712837)(assets/1543416129953.png)]

3.商品修改

查询SpuDetail接口

GoodsController

需要分析的内容:

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

查询sku

分析

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

GoodsController

@GetMapping("sku/list")public ResponseEntity
> querySkusBySpuId(@RequestParam("id")Long spuId){
List
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
querySkusBySpuId(Long spuId) {
Sku sku = new Sku(); sku.setSpuId(spuId); List
skus = this.skuMapper.select(sku); skus.forEach(s -> {
Stock stock = this.stockMapper.selectByPrimaryKey(s.getId()); s.setStock(stock.getStock()); }); return skus;}

页面回显

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

在这里插入图片描述

页面提交

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ndVldnyk-1586588712840)(assets/1528478194128.png)]

后台实现

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

GoodsController

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

GoodsService

spu数据可以修改,但是SKU数据无法修改,因为有可能之前存在的SKU现在已经不存在了,或者以前的sku属性都不存在了。比如以前内存有4G,现在没了。

因此这里直接删除以前的SKU,然后新增即可。

代码:

@Transactionalpublic void updateGoods(SpuBo spu) {
// 查询以前sku List
skus = this.querySkuBySpuId(spu.getId()); // 如果以前存在,则删除 if(!CollectionUtils.isEmpty(skus)) {
List
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());}

转载地址:http://bxxab.baihongyu.com/

你可能感兴趣的文章
符号执行
查看>>
Remote Installation Service (RIS) in Windows Server 2003
查看>>
Layer Four Traceroute
查看>>
Hardening guide for Apache 2.2.15 on RedHat 5.4 (64bit edition)
查看>>
Microsoft Outlook Web Access (OWA) version 8.2.254.0 information disclosure vulnerability
查看>>
STP mitm attack idea
查看>>
Month of PHP Security - Summary
查看>>
近期将要购买的图书
查看>>
nginx Directory Traversal Vulnerability
查看>>
Linux下apache+svn+ssl完美结合搭建安全版本控制平台
查看>>
Nginx 0.8.35 Space Character Remote Source Disclosure
查看>>
showrun的cissp经验谈
查看>>
6月4日要买的书
查看>>
nginx Remote Source Code Disclosure and Denial of Service Vulnerabilities
查看>>
Anti-sec安全培训 部分试看视频
查看>>
FreeBSD kernel NFS client local vulnerabilities
查看>>
JXplorer 的简单使用
查看>>
如何启用 LDAP 签名 Windows Server 2008 中
查看>>
获取ngnix,apache,php,mysql的编译参数 zz from xi4oyu
查看>>
使用ettercap嗅探ssh口令
查看>>