javaee之黑马乐优商城5

分析一下spu与sku的数据结构

再来说一下什么是spu

standard product unit 标准产品单元 :SPU级别的规格参数通常是与整个产品类型或产品系列相关的通用参数。比如华为手机下面的p系列、荣耀系列,都可以标识为spu级别规格参数

sku

stock keeping unit 库存保管单位:SKU级别的规格参数是具体到每个独立的产品变种(SKU)的特有参数。它们描述了每个SKU的唯一特性,如颜色、型号、配置等。

比如对于同一个spu,下面不同的颜色名称,内存大小,内核数,都构成了不同的sku参数

那么spu表是整个产品的相关通用信息,具体我们要设计什么样的字段

我们大致想一下,id,title有个标题,sub_title子标题可能有,可能没有,然后我们还要考虑与分类的关系,我们之前是通过手机这个分类,找到它相关的参数组,然后通过参数组,又去找它具体的子参数,具体的子参数找到了,然后我们就要去找它具体的值,这个值在什么地方存在?我们这里只是简单的设想,这个值是分为了通用参数值,也就是这个参数值大家一起共用,肯定是存放在tb_spu这张表里面,那如果是特殊的值,那就是在sku里面获取。

上面的大致思想就可以用下面这张图来表示

回过头来,继续分析字段,那里面还需要有cid,也就是分类的id,只有拿到分类id,才能找到参数组

然后这个表和品牌表brand表的关系,一个品牌会有多个spu ,比如华为,华为下面有x1系列,x2系列......系列的手机,他每一个手机系列都是一个spu,所以应该有一个brand_id,另外可能就是一些杂七杂八的东西,比如saleable是否上架,valid是否还有效,创建时间和最后修改的时间

那么我们下面就把这个tb_spu表给创建出来

但是再去仔细分析一下这张表,也就是说,这张表有没有什么缺漏

,似乎没有商品的描述,也没有我们通用规格参数的部分,还有一些共用的,比如包装清单啊,比如售后服务啊之类的

这些的数据比较大,为了不影响主表的查询效率,我们就把这些数据拆分了出来

这张表的名字就叫tb_spu_detail,这个表其实就是tb_spu的里面的内容,那我们分析一下字段,上面就是简单的分析了一把

spu_id这个必有嘛,因为它本身就是spu里面的东西

description text 存放的是商品的描述信息

text这个字段我来解释一下

packing_list varchar(3000) 这些字段设计的都是比较大的,这个是包装清单

after_service varchar(3000) 售后服务

下面重点说一下下面两个字段

generic_spec里面保存的是什么?保存的其实就是通用规格参数数据,这些数据就是每一个spu都会固定的东西,就是说,你不管点哪一个spu,这个通用规格参数都一样

我们先来看一下这个generic_spec这样一个字段,它保存的是什么样的信息

说一下,通用规格参数这个参数名是放在tb_spec_param这张表里面的,所以这里设定的是通用值 ,那么如果说,仅仅存放值,没有键的话,他知道是哪一个参数的值吗?很明显不知道,所以这里左边给了tb_spec_param这个表里面的键,右边是值

下面我们再去看一下special_spec这个字段是怎么样的,这个是保存的sku的特有属性,你在spu里面不具备这样的属性。比如内存,颜色,这就是sku,比如下面这一段参数

这里面为什么上面每一个参数,它对应的值都是数组类型的呢?

因为很简单哪怕就是说,同一个spu下面每一个sku,其值都不一样,所以值会有很多,形成数组

上面说了,说special_spec这个字段,存放的是sku特有的属性,所以这个special_spec里面存放的是规格参数对应的内容数组选项,前者为什么是规格参数,因为具体的参数还是放到tb_spec_param里面,你要给它赋值,就对应tb_spec_param里面的id,然后值呢,是一个数组类型,可供选择。下面我们看一下格式

下面我们要把从spu拆分出来的细节表tb_spu_detail给做出来

下面我们就要去分析sku表了

这张表是特有属性信息,那简单来分析一下它的字段

id,spu_id(它是哪一个商品下面的sku啊),title'存放商品的标题',images存放图片,这里可能要存放图片的地址,还有price价格,enable这个商品是否有效啊这样一个判定,创建时间,最后修改时间

还是那句话,重点关注下面两个字段

这个indexes一看名字就是一个索引的表示?你说它怎么来表示首先我们刚刚分析了一下spu表,我们是在spu表里面插入了特有属性的,这个特有属性它是键值对

我们查询spu的时候,根据不同的sku组合,我们需要取出这些特殊的键值对 ,那么这里的设计就是,我们选择上面各自分组当中的哪一个值,也就是下面这些组合当中,我们从中怎么去抽取

继续往下

先来贴一张他实际存放的值吧

因此这里存放的就是

下面再来讲own_spec字段

下面我们要向上面的表插入信息,先向tb_spu表插入数据

看一下数据库

在向tb_spu_detail这张表插入商品信息

看一下这张表的大致内容

下面向tb_sku里面插入一些信息

下面我们去实现商品查询的部分

先去分析一下前端页面,首先点击下面这个位置

就直接会走一个查询

很明显上面就是spu表的一个查询

这个商品查询需要实现的效果是如下

下面分析页面

找到Goods.vue组件

上面看路径我们就知道,很明显这是对spu表进行的一个查询

在写具体的业务逻辑之前,先来看一下其他方面的基础知识

1.PageHelper怎么使用

首先我们必须知道它是Mybiitas中实现分页的插件,它的原理是通过拦截Mybatis执行sql语句

我们只需要在服务端,比如service这个地方,就是写业务代码的地方,,在真正的sql实现之前去开启

注意它这时候给我们只是拼接了sql语句,没有做别的

类似于下面的拼接

他会在前端每一次比如点击当前页面显示多少数据,比如下一页,他又会传进来进行执行上面的语句

在说回来,那我们要给前端一个什么呢?我们就必须传过去关于分页的信息数据,前端才可以获取使用,同时还有实际对象数据

所以必须传过去PageResult对象

2.采用Stream流里面的map方法,把一种流变成另外一种流

下面直接拿出一个实际案例来讲一下

3.下面说几个开发过程中的常用工具

1.BeanUtils工具类

这个是Spring给我们提供的操作对象的工具

上面就是把一个对象的数据复制给另外一个对象

2.StringUtils工具类

下面说一下商品查询的完整思路

首先,我们分析一下页面需要的数据

再去看一下spu表

也就是在数据库中查询的字段就只有id,title在spu表里面存在,那么我们这里就想着在开发中间去扩展一下这个对象

去对外接口模块里面添加一个supbo对象,生产开发中产生的对象,临时对象,单独放一个包

我发现我还没创建sup对象,我先把sup这个对象给创建出来

java 复制代码
package com.leyou.item.pojo;

import lombok.Data;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

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

下面在去创建SpuBo对象,这个对象需要继承它,然后新增两个查询字段

目前来讲,我们只需要一下的值

上面就把我们额外需要的一个对象创建出来了,下面我们把这个查询对象需要的mapper给创建出来

下面去写controller层面的代码

我们先去拿到请求路径

这个是操作商品相关的内容,所以控制器就直接是GoodsController,我们去新创建一下这个控制器

下面我们去goodsService里面实现业务方法

在完整的代码展示之前,我先贴上在过程中,我们需要调用的方法

在我们查询三集分类的分类名称的时候,我们要去categoryService里面去实现下面这个方法

这个方法是调用了通用Mapper的一个根据一个List结合来返回相应数据的Lis集合的这样一个方法

但这个方法有一个要求就是,你的通用Mapper必须继承下面这个类

问题报错来了

因为上面我怎么改都有点问题,于是我把这些步骤拆分了出来

下面在继续去写主干代码

这里贴上主干代码

GoodsController

java 复制代码
package com.leyou.item.web;


import com.leyou.common.pojo.PageResult;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    //下面我们要做的是通过分页查询商品信息
    @GetMapping("/spu/page")
    public ResponseEntity<PageResult<SpuBo>> querySpuBoByPage (
            @RequestParam(value = "page", defaultValue = "1") Integer page,
            @RequestParam(value = "rows", defaultValue = "5") Integer rows,
            @RequestParam(value = "key",required = false) String key,
            @RequestParam(value = "saleable", required = false) Boolean saleable
    ) {
        //分页查询spu信息
        PageResult<SpuBo> result = goodsService.querySpuByPageAndSort(page, rows, key, saleable);
        if (result == null || result.getItems().size() == 0) {
            return ResponseEntity.notFound().build();
        }
        //正常返回创建成功
        return ResponseEntity.ok(result);
    }

}

下面贴上业务层的代码

GoodsService

java 复制代码
package com.leyou.item.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.mapper.BrandMapper;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.pojo.Brand;
import com.leyou.item.pojo.Spu;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class GoodsService {
    //先不考虑引入什么样的mapper
    //要查spu,所以要引入spu的mapper对象
    @Autowired
    private SpuMapper spuMapper;

    //我们在想找分类字段的时候,我们必须用到CategoryService里面相应的通过id找到分类的方法
    @Autowired
    private CategoryService categoryService;

    //我们在考虑通过spu里面的商品id去找,直接引入mapper一键搞定
    @Autowired
    private BrandMapper brandMapper;




    public PageResult<SpuBo> querySpuByPageAndSort(Integer page, Integer rows, String key, Boolean saleable) {
        //这里是查询spu
        //返回一个带有分页的结果集
        PageHelper.startPage(page,rows);//这个分页会拼到后面的所有sql语句里面
        //我们利用通用Mapper来查,我们要设置通用条件
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        //查询上下架
        if (saleable != null) {
            criteria.orEqualTo("saleable",saleable);
        }

        //模糊查询如果有key的话
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("title","%" + key + "%");
        }

        //下面通过spuMapper查Spu对象,这个需要一个集合类型的对象
        //通过上面的
        Page<Spu> pageInfo = (Page<Spu>) spuMapper.selectByExample(example);

        //上面是得到了一个spu,我们的目的是得到一个List<SpuBo>
        List<SpuBo> list = pageInfo.getResult().stream().map(spu -> {
           //处理每一个查寻到的spu对象
           //我们要把spu对象变成SpuBo对象
            SpuBo spuBo = new SpuBo();//每个对象一进来都会实例一个这个对象
            //我们把每一个spu的属性给拷贝一下
            BeanUtils.copyProperties(spu,spuBo);

            //下面考虑一下问题:1.我们的cname怎么取,也就是这个spu关联的分类
            //怎么取?是不是要通过spu里面的cid来取,这是个List<Category>
            //我们要把它变成字符串集合对象打印
            //这个是category表里面的操作,调用categoryService里面的具体方法
            //这里面针对这个品牌来讲是三级目录
            List<String> names = categoryService.queryNameByIds(Arrays.asList(spu.getCid1(),spu.getCid2(),spu.getCid3()));
            //上面我们就先去把这个queryNameByIds这个方法给写出来
            //上面就查询到了商品的所属分类
            //把分类赋值给Bo对象
            spuBo.setCname(StringUtils.join(names,"/"));//格式化一下
            //上面查了分类,下面就要去查找品牌
            //品牌的查找我们就考虑要引入品牌的servcie还是mapper
            //考虑到这里如果mapper能一下给我们找出来,就直接mapper
            //如果不能一下找出来,还要经过业务处理,那么就走service
            //这里我们通过Brand的Mapper中的主键查询就能找出来,并且没有业务转换,直接引入mapper
            Brand brand = brandMapper.selectByPrimaryKey(spu.getBrandId());
            spuBo.setBname(brand.getName());
            return spuBo;
        }).collect(Collectors.toList());//变成了List<spuBo>这样一个集合对象
        //注意要给前端返回一个分页对象,并把结果给带过去
        //现在就是上面的结果集数据全部都封装到了spuBo这个对象里面
        return new PageResult<>(pageInfo.getTotal(),list);
    }

}

然后查看展示效果

相关推荐
疯一样的码农3 分钟前
Apache Maven简介
java·maven·apache
小安同学iter15 分钟前
Java进阶五 -IO流
java·开发语言·intellij-idea
尽兴-25 分钟前
Redis模拟延时队列 实现日程提醒
java·redis·java-rocketmq·mq
书埋不住我1 小时前
java第三章
java·开发语言·servlet
boy快快长大1 小时前
将大模型生成数据存入Excel,并用增量的方式存入Excel
java·数据库·excel
孟秋与你1 小时前
【spring】spring单例模式与锁对象作用域的分析
java·spring·单例模式
菜菜-plus1 小时前
java 设计模式 模板方法模式
java·设计模式·模板方法模式
萨达大1 小时前
23种设计模式-模板方法(Template Method)设计模式
java·c++·设计模式·软考·模板方法模式·软件设计师·行为型设计模式
tian-ming1 小时前
(十八)JavaWeb后端开发案例——会话/yml/过滤器/拦截器
java·开发语言·前端
不能只会打代码1 小时前
大学课程项目中的记忆深刻 Bug —— 一次意外的数组越界
java·github·intellij-idea·话题博客