乐优商城之规格参数商品查询(十)
文章目录
(一)规格参数表结构
(二)数据库表单分析
(三)规格参数组页面分析
(四)规格参数组查询的代码实现
(五)规格参数页面分析
(六)规格参数查询的代码实现
(七)表关系
(八)标结构场景解析
(九)商品查询
(一)规格参数表结构
SPU 和 SKU
SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集
SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品
以图为例来看:
- 本页的 华为Mate10 就是一个商品集(SPU)
- 因为颜色、内存等不同,而细分出不同的Mate10,如亮黑色128G版(SKU)
可以看出:
- SPU是一个抽象的商品集概念,为了方便后台的管理
- SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样
- 用户购买的是SKU而不是SPU
数据库设计分析
首先来看SPU,应该有哪些字段来描述?
id:主键
title:标题
description:描述
specification:规格
packaging_list:包装
after_service:售后服务
comment:评价
category_id:商品分类
brand_id:品牌
似乎并不复杂,但是仔细思考一下,商品的规格字段要如何填写?
不同商品的规格不一定相同,数据库中要如何保存?
看下SKU,大家觉得应该有什么字段?
id:主键
spu_id:关联的spu
price:价格
images:图片
stock:库存
颜色?
内存?
硬盘?
碰到难题了,不同的商品分类,可能属性是不一样的,比如手机有内存,衣服有尺码,我们是全品类的电商网站,这些不同的商品的不同属性,如何设计到一张表中?
其实颜色、内存、硬盘属性都是规格参数中的字段
所以,要解决这个问题,首先要能清楚规格参数,下面详细介绍规格参数
分析规格参数
仔细查看每一种商品的规格会发现:虽然商品规格千变万化,但是同一类商品(如手机)的规格是统一的
- 华为的规格
- 三星的规格
SKU的特有属性
SPU中会有一些特殊属性,用来区分不同的SKU,我们称为SKU特有属性
如:华为META10的颜色、内存属性
如:衣服的颜色、尺码
不同种类的商品,一个手机,一个衣服,其SKU属性不相同
同一种类的商品,比如都是衣服,SKU属性基本是一样的,都是颜色、尺码等
这样说起来,似乎SKU的特有属性也是与分类相关的?
事实上,仔细观察会发现,SKU的特有属性是商品规格参数的一部分,如下:
也就是说,我们没必要单独对SKU的特有属性进行设计,它可以看做是规格参数中的一部分
这样规格参数中的属性可以标记成两部分:
- spu下所有sku共享的规格属性(称为全局属性)
- 每个sku不同的规格属性(称为特有属性)
搜索属性
打开一个搜索页,我们来看看过滤的条件:
过滤条件中的屏幕尺寸、运行内存、网路、机身内存、电池容量、CPU核数等,在规格参数中都能找到:
也就是说,规格参数中的数据,将来会有一部分作为搜索条件来使用
我们可以在设计时,将这部分属性标记出来,将来做搜索的时候,作为过滤条件
要注意的是,无论是SPU的全局属性,还是SKU的特有属性,都有可能作为搜索过滤条件的,并不冲突,而是有一个交集:
(二)数据库表单分析
我们看下规格参数的格式:
可以看到规格参数是分组的,每一组都有多个参数键值对
不过对于规格参数的模板而言,其值现在是不确定的,不同的商品值肯定不同,模板中只要保存组信息、组内参数信息即可
因此我们设计了两张表:
- tb_spec_group:组,与商品分类关联
- tb_spec_param:参数名,与组关联,一对多
首先看看规格组的表单:tb_spec_group
CREATE TABLE `tb_spec_group` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`cid` bigint(20) NOT NULL COMMENT '商品分类id,一个分类下有多个规格组',`name` varchar(50) NOT NULL COMMENT '规格组的名称',PRIMARY KEY (`id`),KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='规格参数的分组表,每个商品分类下有多个规格参数组';
规格组有3个字段:
- id:主键
- cid:商品分类id,一个分类下有多个模板
- name:该规格组的名称
再看看规格参数表单:tb_spec_param
CREATE TABLE `tb_spec_param` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`cid` bigint(20) NOT NULL COMMENT '商品分类id',`group_id` bigint(20) NOT NULL,`name` varchar(255) NOT NULL COMMENT '参数名',`numeric` tinyint(1) NOT NULL COMMENT '是否是数字类型参数,true或false',`unit` varchar(255) DEFAULT '' COMMENT '数字类型参数的单位,非数字类型可以为空',`generic` tinyint(1) NOT NULL COMMENT '是否是sku通用属性,true或false',`searching` tinyint(1) NOT NULL COMMENT '是否用于搜索过滤,true或false',`segments` varchar(1000) DEFAULT '' COMMENT '数值类型参数,如果需要搜索,则添加分段间隔值,如CPU频率间隔:0.5-1.0',PRIMARY KEY (`id`),KEY `key_group` (`group_id`),KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='规格参数组下的参数名';
通用属性
用一个布尔类型字段来标记是否为通用:
- generic:标记是否为通用属性:
- true:代表通用属性
- false:代表sku特有属性
搜索过滤
与搜索相关的有两个字段:
- searching:标记是否用作过滤
- true:用于过滤搜索
- false:不用于过滤
- segments:某些数值类型的参数,在搜索时需要按区间划分,这里提前确定好划分区间
- 比如电池容量,
0~2000mAh
,2000mAh~3000mAh
,3000mAh~4000mAh
- 比如电池容量,
数值类型
某些规格参数可能为数值类型,这样的数据才需要划分区间,我们有两个字段来描述:
- numberic:是否为数值类型
- true:数值类型
- false:不是数值类型
- unit:参数的单位
(三)规格参数组页面分析
(四)规格参数组查询的代码实现
首先在leyou-item-interface中添加实体类,如下:
@Table(name = "tb_spec_group")
public class SpecGroup {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private Long cid;private String name;@Transient //由于表中没有对应的列名,所以要加该注解private List<SpecParam> params;// getter和setter省略
}
@Table(name = "tb_spec_param")
public class SpecParam {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private Long cid;private Long groupId;private String name;@Column(name = "`numeric`")private Boolean numeric; //如果列名和字段名一致是不需要加该注解的,但是numeric在mysql中属于关键字private String unit;private Boolean generic;private Boolean searching;private String segments;// getter和setter ...
}
创建对应的Mapper接口,如下:
然后编写业务层,如下:
@Service
public class SpecificationService {@Autowiredprivate SpecGroupMapper groupMapper;@Autowiredprivate SpecParamMapper paramMapper;/*** 根据分类id查询参数组** @param cid* @return*/public List<SpecGroup> queryGroupsByCid(Long cid) {SpecGroup record = new SpecGroup();record.setCid(cid);return groupMapper.select(record);}
}
最后编写表现层,如下:
@Controller
@RequestMapping("spec")
public class SpecificationController {@Autowiredprivate SpecificationService specificationService;/*** 根据分类id查询参数组** @param cid* @return*/@GetMapping("groups/{cid}")public ResponseEntity<List<SpecGroup>> queryGroupsByCid(@PathVariable("cid") Long cid) {List<SpecGroup> groups = specificationService.queryGroupsByCid(cid);if (CollectionUtils.isEmpty(groups)) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(groups);}
}
效果如下:
(五)规格参数页面分析
(六)规格参数查询的代码实现
/*** 根据gid查询规格参数** @param gid* @return*/@GetMapping("params")public ResponseEntity<List<SpecParam>> queryParams(@RequestParam("gid") Long gid) {List<SpecParam> params = specificationService.queryParams(gid);if (CollectionUtils.isEmpty(params)) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(params);}
/*** 根据gid查询规格参数** @param gid* @return*/public List<SpecParam> queryParams(Long gid) {SpecParam record = new SpecParam();record.setGroupId(gid);return paramMapper.select(record);}
效果如下:
(七)表关系
注意:
- 分类与spu是一对多的关系,比如:华为mate30是一个spu、小米8又是一个spu
- 品牌与spu也是一对多的关系,比如:小米8是一个spu、小米6也是一个spu
注意:tb_spu和tb_spu_detail其实是同一张表,只是tb_spu_detail存储的数据量太大了,单独做成一张表,后期可以把它放在Redis里面
注意:
- tb_sku中的indexes是为了方便索引(比较的字段比较短)
- own_spec是为了方便渲染(再存储一份,不需要做多表查询,用空间换时间)
注意:
- 如果generic为1,就从tb_spu_detail中获取值
- 如果generic为0,就从tb_sku中获取值
注意:tb_stock是库存表,本质上跟tb_sku是同一张表,考虑到关系型数据库的性能,单独做成一张表
(八)标结构场景解析
最后根据spuId和indexes就可以查询具体的sku了
(九)商品查询
预期效果如下:
编写SPU实体类,如下:
编写Mapper接口,如下:
要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,怎么办?
我们可以新建一个类,继承SPU,并且拓展cname和bname属性,如下:
编写contoller,如下:
@Controller
public class GoodsController {@Autowiredprivate GoodsService goodsService;/*** 根据条件分页查询spu** @param key* @param saleable* @param page* @param rows* @return*/@GetMapping("spu/page")public ResponseEntity<PageResult<SpuBo>> querySpuByPage(@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<SpuBo> pageResult = this.goodsService.querySpuBoByPage(key, saleable, page, rows);if (CollectionUtils.isEmpty(pageResult.getItems())) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(pageResult);}
}
编写service,如下:
@Service
public class GoodsService {@Autowiredprivate SpuMapper spuMapper;@Autowiredprivate SpuDetailMapper spuDetailMapper;@Autowiredprivate BrandMapper brandMapper;@Autowiredprivate CategoryService categoryService;/*** 根据条件分页查询spu** @param key* @param saleable* @param page* @param rows* @return*/public PageResult<SpuBo> 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);//执行查询,获取spu集合List<Spu> spus = spuMapper.selectByExample(example);PageInfo<Spu> spusPageInfo = new PageInfo<>(spus);//转化成spubo集合List<SpuBo> spuBos = spus.stream().map(spu -> {SpuBo spuBo = new SpuBo();BeanUtils.copyProperties(spu, spuBo);//查询品牌名称Brand brand = brandMapper.selectByPrimaryKey(spu.getBrandId());spuBo.setBname(brand.getName());//查询分类名称List<String> names = categoryService.queryNamesByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));spuBo.setCname(StringUtils.join(names, "-"));return spuBo;}).collect(Collectors.toList());//返回pageResult<SpuBo>return new PageResult<>(spusPageInfo.getTotal(), spuBos);}
}
乐优商城之规格参数商品查询(十)相关推荐
- 乐优商城day08(规格参数和商品列表)
所有代码发布在 [https://github.com/hades0525/leyou] Day08 2019年1月27日 13:24 把商品的图片存到虚拟机 数据库的image属性值http://i ...
- 乐优商城(05)--商品管理
乐优商城(05)–商品管理 一.导入图片资源 现在商品表中虽然有数据,但是所有的图片信息都是无法访问的,因此需要把图片导入到服务器中: 将images.zip文件上传至/leyou/static目录: ...
- 乐优商城(四)商品规格管理
文章目录 1. 商品规格 1.1 SPU 和 SKU 1.2 分析商品规格的关系 1.3 数据库设计 1.3.1 商品规格组表 1.3.2 商品规格参数表 2. 商品规格组 2.1 商品规格组前端 2 ...
- 乐优商城学习笔记五-商品规格管理
0.学习目标 了解商品规格数据结构设计思路 实现商品规格查询 了解SPU和SKU数据结构设计思路 实现商品查询 了解商品新增的页面实现 独立编写商品新增后台功能 1.商品规格数据结构 乐优商城是一个全 ...
- 乐优商城笔记六:商品详情页
使用模板引擎 Thymeleaf + nginx 完成商品详情页静态化 完成乐优商城商品详情页 搭建商品详情页微服务 创建子工程 GroupId:com.leyou.service ArtifactI ...
- 乐优商城--服务(三) : 商品微服务(LyItemApplication)--前半部分
商品微服务--前半部分: 1. 微服务结构(ly-item) 1.0 顶级父工程(le-you) 1.1 ly-item 1.1.1 ly-item-interface 1.1.1.1 添加依赖 1. ...
- 乐优商城(三)商品分类管理
目录 一.数据 二.页面实现 2.1 页面分析 2.2 功能实现 2.2.1 url异步请求 2.2.2 后台接口实现 2.3 解决跨域请求 2.3.1 什么是跨域 2.3.2 解决跨域问题的方案 2 ...
- 【javaWeb微服务架构项目——乐优商城day05】——商品规格参数管理(增、删、改,查已完成),SPU和SKU数据结构,商品查询
乐优商城day05 0.学习目标 1.商品规格数据结构 1.1.SPU和SKU 1.2.数据库设计分析 1.2.1.思考并发现问题 1.2.2.分析规格参数 1.2.3.SKU的特有属性 1.2.4. ...
- 乐优商城 Day 09(thymeleaf,Rabbitmq,商品详情页,非教程)
乐优商城学习Day09: 注意:此次代码都是在第八天的基础上 第八天的链接如下: https://blog.csdn.net/zcylxzyh/article/details/100859210 此次 ...
最新文章
- AndroidStudio 在工具栏上设置显示前进和后台的方法
- BZOJ.3527.[ZJOI2014]力(FFT)
- 系统架构性能问题诊断及优化思路,纯干货!
- 从技术谈到管理,把系统优化的技术用到企业管理
- Oracle数据库中的违规策略规则的修正
- 哥哥,请原谅妹妹的自私!妹妹想做你的新娘...超级感人
- NFS问题:clnt_create: RPC: Port mapper failure - Unable to receive: errno 113 (No route to host)
- 【Spring学习】spring依赖注入用法总结
- 春节抽空读了8本书,只有这本书我1字不落的读完了!(上)
- mysql order by 语句_Mysql优化order by语句的方法详解
- 分分钟学会系列:mac地址泛洪攻击实验
- 解决办法:/usr/bin/ld: 找不到 -lstdc++
- pyqt5 treewidget图标_Python基础之PyQt5写TreeWidget(二)--代码篇
- laravel如何利用数据库的形式发送通知
- C语言面试题---结构体
- [网络安全自学篇] 三十五.恶意代码攻击溯源及恶意样本分析
- 汉诺塔详解(超详细)
- fatal error caffe/proto/caffe pb h No such file or directo
- linux 的时区设置函数tzset()
- pg.Pool 的 query 用法
热门文章
- 表白套路计算机公式,高级表白密码我喜欢你公式 套路喜欢的人
- 源程序的相似性分析 —— 基于Python实现哈希表
- AI美颜SDK功能算法代码解析
- win10下RTX 2080ti安装cuda10.0和cudnn7.6.5
- “大数据杀熟”?商家对数据的使用可能远超出想象
- When you want to give up, remember why you started.
- 用js验证敏感词汇(汉字)
- 主板怎么安装在计算机主机箱,计算机主板、主机、机箱和计算机的制作方法
- 416. 分割等和子集
- JS判断页面是否被iframe嵌套