乐优商城(五)商品管理

1. 商品新增

1.1 商品新增前端

  1. 进入商品列表,点击新增商品

    在这里插入图片描述

  2. 出现一个弹窗

    在这里插入图片描述

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

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

1.2 基本信息

1.2.1 商品分类

商品分类的前端

商品分类的查询之前做过了,所以这里点击就能选择商品分类

在这里插入图片描述

1.2.2 品牌选择

品牌选择前端

选择分类后,我们需要这个分类下的所有品牌,所以会发送一个根据分类 id 查询品牌的请求

在这里插入图片描述
我们找到前端请求品牌数据的代码

在这里插入图片描述

由此可以得知:

  • 请求方式:GET
  • 请求路径:brand/cid
  • 请求参数:分类 id,这里用的是 Rest 风格的占位符
  • 返回参数:品牌的集合

后台接口

  1. 在 BrandController 中添加 queryBrandsByCid 方法

    /**
     * 根据分类 id 查询品牌信息
     * @param cid
     * @return
     */
    @GetMapping("/cid/{cid}")
    public ResponseEntity<List<Brand>> queryBrandsByCid(@PathVariable("cid") Long cid) {
        List<Brand> brands = brandService.queryBrandsByCid(cid);
        if(CollectionUtils.isEmpty(brands)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(brands);
    }
    
  2. 在 BrandService 中添加 queryBrandsByCid 方法

    /**
     * 根据分类 id 查询品牌信息
     * @param cid
     * @return
     */
    public List<Brand> queryBrandsByCid(Long cid) {
        List<Brand> brands = brandMapper.queryBrandsByCid(cid);
        return brands;
    }
    
  3. 在 BrandMapper 中添加 queryBrandsByCid 方法

    /**
     * 根据分类 id 查询品牌信息
     * @param cid
     * @return
     */
    @Select("SELECT * FROM tb_brand WHERE id IN (SELECT brand_id FROM tb_category_brand WHERE category_id = #{cid})")
    List<Brand> queryBrandsByCid(Long cid);
    

测试

成功得到手机分类下的所有品牌

在这里插入图片描述

1.2.3 其他文本框

剩下的都是普通的文本框,直接填写即可

在这里插入图片描述

1.3 商品描述

商品描述经常需要图文并茂,甚至视频,所以这里使用了一个富文本编辑器 Vue-Quill-Editor,它支持文字、图片、视频等功能。

在这里插入图片描述

1.4 规格参数

规格参数前端

选择分类后,我们需要这个分类下的所有规格参数,所以会发送一个根据分类 id 查询规格参数的请求

在这里插入图片描述
我们找到前端请求规格参数数据的代码

在这里插入图片描述

由此可以得知:

  • 请求方式:GET
  • 请求路径:spec/params
  • 请求参数:分类 id
  • 返回参数:规格参数集合

后台接口

  1. 前面在 SpecificationController 中已经写过一个 querySpecParams 方法,路径为 spec/params,参数为规格组 id,如下:

    /**
     * 根据条件查询规格参数
     *
     * @param gid
     * @return
     */
    @GetMapping("/params")
    public ResponseEntity<List<SpecParam>> querySpecParams(@RequestParam("gid") Long gid) {
        List<SpecParam> params = specificationService.querySpecParams(gid);
        if (CollectionUtils.isEmpty(params)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(params);
    }
    
  2. 所以我们就不用再写新的方法了,直接在 querySpecParams 方法中增加分类 id 参数。考虑到以后可能还会根据是否搜索、是否为通用属性等条件过滤,我们多添加几个过滤条件

    /**
     * 根据条件查询规格参数
     *
     * @param gid
     * @return
     */
    @GetMapping("/params")
    public ResponseEntity<List<SpecParam>> querySpecParams(
        @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<SpecParam> params = specificationService.querySpecParams(gid, cid, generic, searching);
        if (CollectionUtils.isEmpty(params)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(params);
    }
    
  3. 修改 SpecificationService 中的 querySpecParams 方法

    /**
     * 根据条件查询规格参数
     * @param gid
     * @return
     */
    public List<SpecParam> querySpecParams(Long gid,Long cid, Boolean generic, Boolean searching) {
        SpecParam specParam = new SpecParam();
        specParam.setGroupId(gid);
        specParam.setCid(cid);
        specParam.setGeneric(generic);
        specParam.setSearching(searching);
        List<SpecParam> params = specParamMapper.select(specParam);
        return params;
    }
    

测试

成功得到规格参数,这部分规格参数是 sku 通用的属性,即 tb_spec_param 中 generic 为 1 的字段。

在这里插入图片描述

1.5 SKU 属性

成功得到 sku 属性,这部分规格参数是 sku 特有的属性,即 tb_spec_param 中 generic 为 0 的字段。

在这里插入图片描述

当我们填写一些 sku 属性后,会在页面下方生成一个 sku 表格,可以添加价格、库存、是否启用等

在这里插入图片描述

1.6 提交商品信息

1.6.1 提交表单前端

填写完所有商品信息后,只剩下最后一步了,那就是提交商品信息

在这里插入图片描述
点击提交商品信息后,可以看到发送给了一个提交商品信息的请求

在这里插入图片描述
再来看看请求参数

在这里插入图片描述

整体是一个 JSON 格式数据,包含了 tb_spu、tb_spu_detail、tb_sku、tb_stock 四张表的数据

  • brandId:品牌 id
  • cid1、cid2、cid3:商品分类 id
  • subTitle:副标题
  • title:标题
  • spuDetail:
    • afterService:售后服务
    • description:商品描述
    • packingList:包装列表
    • specialSpec:sku规格属性模板
    • genericSpec:通用规格参数
  • skus:
    • sku:
      • title:标题
      • images:图片
      • price:价格
      • stock:库存
      • ownSpec:特有规格参数
      • indexes:特有规格参数的下标

我们找到前端提交商品信息请求的代码

在这里插入图片描述

由此可以得知:

  • 请求方式:POST
  • 请求路径:spu
  • 请求参数:商品信息,JSON 格式
  • 返回参数:无

1.6.2 后台接口

实体类

  1. 在 leyou-item-interface 项目中添加实体类 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;// 库存
        
        // getter、setter、toString 方法省略
    }
    
  2. 在 leyou-item-interface 项目中添加实体类 Stock

    @Table(name = "tb_stock")
    public class Stock {
        @Id
        private Long skuId;
        private Integer seckillStock;// 秒杀可用库存
        private Integer seckillTotal;// 已秒杀数量
        private Integer stock;// 正常库存
        
        // getter、setter、toString 方法省略
    }
    
  3. 在实体类 SpuBo 中添加 spuDetail、skus 属性,用来封装前端提交过来的商品信息

    public class SpuBo extends Spu{
        private String cname;
        private String bname;
        private SpuDetail spuDetail;
        private List<Sku> skus;
    	
        // getter、setter、toString 方法省略
    }
    
    

Mapper

  1. 在 leyou-item-service 项目中添加 SkuMapper

    public interface SkuMapper extends Mapper<Sku> {
    }
    
  2. 在 leyou-item-service 项目中添加 StockMapper

    public interface StockMapper extends Mapper<Stock> {
    }
    

Controller

在 SpuController 中添加 saveSpu 方法

/**
 * 新增商品
 * @param spuBo
 * @return
 */
@PostMapping
public ResponseEntity<Void> saveSpu(@RequestBody SpuBo spuBo) {
    spuService.saveSpu(spuBo);
    return ResponseEntity.status(HttpStatus.CREATED).build();
}

Service

在 SpuService 中添加 saveSpu 方法

/**
 * 新增商品
 * @param spuBo
 * @return
 */
@Transactional
public void saveSpu(SpuBo spuBo) {
    // 先新增 Spu
    spuBo.setId(null);
    spuBo.setSaleable(true);
    spuBo.setValid(true);
    spuBo.setCreateTime(new Date());
    spuBo.setLastUpdateTime(spuBo.getCreateTime());
    spuMapper.insertSelective(spuBo);

    // 再新增 SpuDetail
    SpuDetail spuDetail = spuBo.getSpuDetail();
    spuDetail.setSpuId(spuBo.getId());
    spuDetailMapper.insertSelective(spuDetail);

    // 再新增 Sku
    List<Sku> skus = spuBo.getSkus();
    for (Sku sku : skus) {
        sku.setId(null);
        sku.setSpuId(spuBo.getId());
        sku.setCreateTime(new Date());
        sku.setLastUpdateTime(sku.getCreateTime());
        skuMapper.insertSelective(sku);
        // 新增 Stock
        Stock stock = new Stock();
        stock.setSkuId(sku.getId());
        stock.setStock(sku.getStock());
        stockMapper.insertSelective(stock);
    }
}

1.6.3 测试

看到新增的商品了

在这里插入图片描述

2. 商品修改

商品修改需要以下步骤:

  1. 将商品信息回显到页面
  2. 修改商品信息
  3. 提交修改

2.1 商品回显

2.1.1 商品回显前端

点击修改按钮就会把商品信息回显到页面上

在这里插入图片描述

我们找到前端商品信息回显请求的代码

在这里插入图片描述

由此可知,发送了两个请求:

  • 查询 spuDetail
    • 请求方式:GET
    • 请求路径:spu/detail
    • 请求参数:spuId,这里用的是 Rest 风格的占位符
    • 返回参数:SpuDetail
  • 查询 Sku 集合
    • 请求方式:GET
    • 请求路径:spu/sku/list
    • 请求参数:spuId
    • 返回参数:Sku 集合

2.1.2 后台接口

Controller

  1. 在 SpuController 中添加 querySpuDetailBySpuId 方法

    /**
     * 通过 spuId 查询 SpuDetail
     * @param spuId
     * @return
     */
    @GetMapping("/detail/{spuId}")
    public ResponseEntity<SpuDetail> querySpuDetailBySpuId(@PathVariable("spuId") Long spuId) {
        SpuDetail spuDetail = spuService.querySpuDetailBySpuId(spuId);
        if (spuDetail == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(spuDetail);
    }
    
  2. 在 SpuController 中添加 querySkusBySpuId 方法

    /**
     * 通过 spuId 查询 Sku 集合
     * @param spuId
     * @return
     */
    @GetMapping("/sku/list")
    public ResponseEntity<List<Sku>> querySkusBySpuId(@RequestParam("id") Long spuId) {
        List<Sku> skus = spuService.querySkusBySpuId(spuId);
        if(CollectionUtils.isEmpty(skus)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(skus);
    }
    

Service

  1. 在 SpuService 中添加 querySpuDetailBySpuId 方法

    /**
     * 通过 spuId 查询 SpuDetail
     * @param spuId
     * @return
     */
    public SpuDetail querySpuDetailBySpuId(Long spuId) {
        SpuDetail spuDetail = spuDetailMapper.selectByPrimaryKey(spuId);
        return spuDetail;
    }
    
  2. 在 SpuService 中添加 querySkusBySpuId 方法

    /**
     * 通过 spuId 查询 Sku 集合
     * @param spuId
     * @return
     */
    public List<Sku> querySkusBySpuId(Long spuId) {
        Sku sku = new Sku();
        sku.setSpuId(spuId);
        List<Sku> skus = skuMapper.select(sku);
        for (Sku sku1 : skus) {
            Stock stock = stockMapper.selectByPrimaryKey(sku1.getId());
            sku1.setStock(stock.getStock());
        }
        return skus;
    }
    

2.1.3 测试

商品信息回显成功

在这里插入图片描述

2.2 提交修改

2.2.1 提交表单前端

我们找到前端提交商品信息请求的代码,商品新增时是 POST 请求,商品修改是 PUT 请求

在这里插入图片描述

由此可以得知:

  • 请求方式:PUT
  • 请求路径:spu
  • 请求参数:商品信息,JSON 格式
  • 返回参数:无

2.2.2 后台接口

Controller

在 SpuController 中添加 updateSpu 方法

/**
 * 更新商品
 * @param spuBo
 * @return
 */
@PutMapping
public ResponseEntity<Void> updateSpu(@RequestBody SpuBo spuBo) {
    spuService.updateSpu(spuBo);
    return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

Service

在 SpuService 中添加 updateSpu 方法

/**
 * 更新商品
 * @param spuBo
 * @return
 */
@Transactional
public void updateSpu(SpuBo spuBo) {
    // 获取要删除的 Sku
    Sku sku = new Sku();
    sku.setSpuId(spuBo.getId());
    List<Sku> skus = skuMapper.select(sku);

    // 删除原来的 Stock
    for (Sku sku1 : skus) {
        Stock stock = new Stock();
        stock.setSkuId(sku1.getId());
        stockMapper.delete(stock);
    }

    // 删除原来的 Sku
    skuMapper.delete(sku);

    // 新增 Sku 和 Stock
    saveSkuAndStock(spuBo);

    // 更新 Spu
    spuBo.setCreateTime(null);
    spuBo.setLastUpdateTime(new Date());
    spuBo.setValid(null);
    spuBo.setSaleable(null);
    spuMapper.updateByPrimaryKeySelective(spuBo);

    // 更新 SpuDetail
    spuDetailMapper.updateByPrimaryKeySelective(spuBo.getSpuDetail());
}

新增 Sku 和 Stock 的代码之前写过了,为了避免代码冗余,所以抽取成 saveSkuAndStock 方法

/**
 * 新增 Sku 和 Stock
 * @param spuBo
 */
private void saveSkuAndStock(SpuBo spuBo) {
    // 再新增 Sku
    List<Sku> skus = spuBo.getSkus();
    for (Sku sku : skus) {
        sku.setId(null);
        sku.setSpuId(spuBo.getId());
        sku.setCreateTime(new Date());
        sku.setLastUpdateTime(sku.getCreateTime());
        skuMapper.insertSelective(sku);
        // 新增 Stock
        Stock stock = new Stock();
        stock.setSkuId(sku.getId());
        stock.setStock(sku.getStock());
        stockMapper.insertSelective(stock);
    }
}

2.2.3 测试

把小米 10 修改为小米 11

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3. 搭建门户系统

后台系统的内容暂时告一段落,有了商品,接下来我们就要在页面展示商品,给用户提供浏览和购买的入口,那就是我们的门户系统。

门户系统面向的是用户,安全性很重要,而且搜索引擎对于单页应用并不友好。因此我们的门户系统不再采用与后台系统类似的 SPA(单页应用),而是采用多页应用,不过依旧是前后端分离。

3.1 创建工程

在这里插入图片描述

在这里插入图片描述

3.2 导入静态资源

将 leyou-portal 解压并复制到工程中

在这里插入图片描述

3.3 live-server

webpack 打包多页应用配置比较繁琐,所以我们使用另一种热部署的方式:live-server

3.3.1 live-server 简介

live-server 是一款带有热加载功能的小型开发服务器。用它来展示你的 HTML / JavaScript / CSS,但不能用于部署最终的网站。

3.3.2 安装 live-server

在 terminal 中输入以下命令:

npm install -g live-server

3.4 启动项目

在 terminal 中输入以下命令:

live-server --port=9002

启动成功

在这里插入图片描述

3.5 域名访问

现在访问:http://127.0.0.1:9002/

域名访问:http://www.leyou.com

接下来我们就来实现域名访问:

  1. 配置 host 文件

    127.0.0.1 www.leyou.com
    
  2. 在 nginx.conf 添加以下配置,实现将 www.leyou.com 反向代理到 http://127.0.0.1:9002

    server {
        listen       80;
        server_name  www.leyou.com;
    
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
        location / {
            proxy_pass http://127.0.0.1:9002;
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
        }
    }
    
  3. 重新加载 nginx

    nginx.exe -s reload
    
  4. 成功通过域名访问

    在这里插入图片描述

发布了80 篇原创文章 · 获赞 176 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/bm1998/article/details/104942833