目录

1.商品规格数据结构

1.1.SPU和SKU

1.2.数据库设计分析

1.2.1.思考并发现问题

1.2.2.分析规格参数

1.2.3.SKU的特有属性

1.3.规格参数表

1.3.1.表结构

1.3.2.json结构分析

2.商品规格参数管理

2.1.页面实现

2.1.1.整体布局

2.2.2.后端代码

3.3.导入图片信息

4.商品查询

4.1.效果预览

4.2.后台提供接口

4.4.1.实体类

4.4.2.controller

4.4.3.service

4.4.4.mapper

4.4.5.Category中拓展查询名称的功能

4.5.测试


1.商品规格数据结构

乐优商城是一个全品类的电商网站,因此商品的种类繁多,每一件商品,其属性又有差别。为了更准确描述商品及细分差别,抽象出两个概念:SPU和SKU,了解一下:

1.1.SPU和SKU

SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集

SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品

以图为例来看:

  • 本页的 华为Mate10 就是一个商品集(SPU)

  • 因为颜色、内存等不同,而细分出不同的Mate10,如亮黑色128G版。(SKU)

可以看出:

  • SPU是一个抽象的商品集概念,为了方便后台的管理。

  • SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样,用户购买的是SKU而不是SPU

1.2.数据库设计分析

1.2.1.思考并发现问题

弄清楚了SPU和SKU的概念区分,接下来我们一起思考一下该如何设计数据库表。

首先来看SPU,大家一起思考下SPU应该有哪些字段来描述?

id:主键
title:标题
description:描述
specification:规格
packaging_list:包装
after_service:售后服务
comment:评价
category_id:商品分类
brand_id:品牌

似乎并不复杂,但是大家仔细思考一下,商品的规格字段你如何填写?

不同商品的规格不一定相同,数据库中要如何保存?

再看下SKU,大家觉得应该有什么字段?

id:主键
spu_id:关联的spu
price:价格
images:图片
stock:库存
颜色?
内存?
硬盘?

碰到难题了,不同的商品分类,可能属性是不一样的,比如手机有内存,衣服有尺码,我们是全品类的电商网站,这些不同的商品的不同属性,如何设计到一张表中?

1.2.2.分析规格参数

仔细查看每一种商品的规格你会发现:

虽然商品规格千变万化,但是同一类商品(如手机)的规格是统一的,有图为证:

华为的规格:

三星的规格:

也就是说,商品的规格参数应该是与分类绑定的。每一个分类都有统一的规格参数模板,但不同商品其参数值可能不同

如下图所示:

1.2.3.SKU的特有属性

SPU中会有一些特殊属性,用来区分不同的SKU,我们称为SKU特有属性。如华为META10的颜色、内存属性。

不同种类的商品,一个手机,一个衣服,其SKU属性不相同。

同一种类的商品,比如都是衣服,SKU属性基本是一样的,都是颜色、尺码等。

这样说起来,似乎SKU的特有属性也是与分类相关的?事实上,仔细观察你会发现,SKU的特有属性是商品规格参数的一部分

也就是说,我们没必要单独对SKU的特有属性进行设计,它可以看做是规格参数中的一部分。这样规格参数中的属性可以标记成两部分:

  • 所有sku共享的规格属性(称为全局属性)

  • 每个sku不同的规格属性(称为特有属性)

 1.2.4.搜索属性

打开一个搜索页,我们来看看过滤的条件:

你会发现,过滤条件中的屏幕尺寸、运行内存、网路、机身内存、电池容量、CPU核数等,在规格参数中都能找到:

也就是说,规格参数中的数据,将来会有一部分作为搜索条件来使用。我们可以在设计时,将这部分属性标记出来,将来做搜索的时候,作为过滤条件。要注意的是,无论是SPU的全局属性,还是SKU的特有属性,都有可能作为搜索过滤条件的,并不冲突,而是有一个交集:

1.3.规格参数表

1.3.1.表结构

先看下规格参数表:

CREATE TABLE `tb_specification` (`category_id` bigint(20) NOT NULL COMMENT '规格模板所属商品分类id',`specifications` varchar(3000) NOT NULL DEFAULT '' COMMENT '规格参数模板,json格式',PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品规格参数模板,json格式。';

很奇怪是吧,只有两个字段。特别需要注意的是第二个字段:

  • specificatons:规格参数模板,json格式

为什么是一个json?我们看下规格参数的格式:

如果按照传统数据库设计,这里至少需要3张表:

  • group:代表组,与商品分类关联

  • param_key:属性名,与组关联,一对多

  • param_value:属性备选值,与属性名关联,一对多

这样程序的复杂度大大增加,但是提高了数据的复用性。

我们的解决方案是,采用json来保存整个规格参数模板,不需要额外的表,一个字符串就够了。

1.3.2.json结构分析

先整体看一下:

  • 因为规格参数分为很多组,所以json最外层是一个数组。

  • 数组中是对象类型,每个对象代表一个组的数据,对象的属性包括:

    • group:组的名称

    • params:该组的所有属性

接下来是params:

主芯片这一组为例:

  • group:注明,这里是主芯片

  • params:该组的所有规格属性,因为不止一个,所以是一个数组。这里包含四个规格属性:CPU品牌,CPU型号,CPU频率,CPU核数。每个规格属性都是一个对象,包含以下信息:

    • k:属性名称

    • searchable:是否作为搜索字段,将来在搜索页面使用,boolean类型

    • global:是否是SPU全局属性,boolean类型。true为全局属性,false为SKU的特有属性

    • options:属性值的可选项,数组结构。起约束作用,不允许填写可选项以外的值,比如CPU核数,有人添10000核岂不是很扯淡

    • numerical:是否为数值,boolean类型,true则为数值,false则不是。为空也代表非数值

    • unit:单位,如:克,毫米。如果是数值类型,那么就需要有单位,否则可以不填。

上面的截图中所有属性都是全局属性,我们来看看内存,应该是特有属性:

总结下:

  • 规格参数分组,每组有多个参数

  • 参数的 k代表属性名称,没有值,具体的SPU才能确定值

  • 参数会有不同的属性:是否可搜索,是否是全局、是否是数值,这些都用boolean值进行标记:

    • SPU下的多个SKU共享的参数称为全局属性,用global标记

    • SPU下的多个SKU特有的参数称为特有属性

    • 如果参数是数值类型,用numerical标记,并且指定单位unit

    • 如果参数可搜索,用searchable标记

2.商品规格参数管理

2.1.页面实现

页面比较复杂,这里就不带着大家去实现完整页面效果了,我们一起分析一下即可。

2.1.1.整体布局

打开规格参数页面,看到如下内容:

因为规格是跟商品分类绑定的,因此首先会展现商品分类树,并且提示你要选择商品分类,才能看到规格参数的模板。一起了解下页面的实现:

可以看出页面分成3个部分:

  • v-card-title:标题部分,这里是提示信息,告诉用户要先选择分类,才能看到模板

  • v-tree:这里用到的是我们之前讲过的树组件,展示商品分类树,不过现在是假数据,我们只要把treeData属性删除,它就会走url属性指定的路径去查询真实的商品分类树了。

    <v-tree url="/item/category/list" :isEdit="false"  @handleClick="handleClick" />
  • v-dialog:Vuetify提供的对话框组件,v-model绑定的dialog属性是boolean类型:

    • true则显示弹窗

    • false则隐藏弹窗

2.2.2.后端代码

实体类

@Table(name = "tb_specification")
public class Specification {@Idprivate Long categoryId;private String specifications;public Long getCategoryId() {return categoryId;}public void setCategoryId(Long categoryId) {this.categoryId = categoryId;}public String getSpecifications() {return specifications;}public void setSpecifications(String specifications) {this.specifications = specifications;}
}

mapper

public interface SpecificationMapper extends Mapper<Specification> {
}

controller

先分析下需要的东西,在页面的ajax请求中可以看出:

  • 请求方式:查询,肯定是get

  • 请求路径:/spec/{cid} ,这里通过路径占位符传递商品分类的id

  • 请求参数:商品分类id

  • 返回结果:页面是直接把resp.data赋值给了specifications:

代码:

@RestController
@RequestMapping("spec")
public class SpecificationController {@Autowiredprivate SpecificationService specificationService;@GetMapping("{id}")public ResponseEntity<String> querySpecificationByCategoryId(@PathVariable("id") Long id){Specification spec = this.specificationService.queryById(id);if (spec == null) {return new ResponseEntity<>(HttpStatus.NOT_FOUND);}return ResponseEntity.ok(spec.getSpecifications());}
}

service:

@Service
public class SpecificationService {@Autowiredprivate SpecificationMapper specificationMapper;public Specification queryById(Long id) {return this.specificationMapper.selectByPrimaryKey(id);}
}

页面访问测试:

目前,我们数据库只提供了3条规格参数信息:

我们访问:http://api.leyou.com/api/item/spec/76

然后在后台系统中测试:

3.3.导入图片信息

现在商品表中虽然有数据,但是所有的图片信息都是无法访问的,我们需要把图片导入到虚拟机:

首先,把资料提供的数据上传到虚拟机下:/leyou/static目录:

然后,使用命令解压缩:

unzip images.zip

修改Nginx配置,使nginx反向代理这些图片地址:

vim /opt/nginx/config/nginx.conf

修改成如下配置:

server {listen       80;server_name  image.leyou.com;# 监听域名中带有group的,交给FastDFS模块处理location ~/group([0-9])/ {ngx_fastdfs_module;}# 将其它图片代理指向本地的/leyou/static目录location / {root   /leyou/static/;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}

4.商品查询

4.1.效果预览

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

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

模板代码在分别在Goods.vue

4.2.后台提供接口

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

4.4.1.实体类

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 {@Idprivate Long spuId;// 对应的SPU的idprivate String description;// 商品描述private String specTemplate;// 商品特殊规格的名称及可选值模板private String specifications;// 商品的全局规格属性private String packingList;// 包装清单private String afterService;// 售后服务// 省略getter和setter
}

4.4.2.controller

先分析:

  • 请求方式:GET

  • 请求路径:/spu/page

  • 请求参数:

    • page:当前页

    • rows:每页大小

    • key:过滤条件

    • saleable:上架或下架

  • 返回结果:商品SPU的分页信息。

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

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

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

编写controller代码:

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

@RestController
public class GoodsController {@Autowiredprivate GoodsService goodsService;/*** 分页查询SPU* @param page* @param rows* @param key* @return*/@GetMapping("/spu/page")public ResponseEntity<PageResult<SpuBo>> querySpuByPage(@RequestParam(value = "page", defaultValue = "1") Integer page,@RequestParam(value = "rows", defaultValue = "5") Integer rows,@RequestParam(value = "key", required = false) String key) {// 分页查询spu信息PageResult<SpuBo> result = this.goodsService.querySpuByPageAndSort(page, rows, key);if (result == null || result.getItems().size() == 0) {return new ResponseEntity<>(HttpStatus.NOT_FOUND);}return ResponseEntity.ok(result);}
}

4.4.3.service

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

@Service
public class GoodsService {@Autowiredprivate SpuMapper spuMapper;@Autowiredprivate CategoryService categoryService;@Autowiredprivate BrandMapper brandMapper;public PageResult<SpuBo> querySpuByPageAndSort(Integer page, Integer rows, Boolean saleable, String key) {// 1、查询SPU// 分页,最多允许查100条PageHelper.startPage(page, Math.min(rows, 100));// 创建查询条件Example example = new Example(Spu.class);Example.Criteria criteria = example.createCriteria();// 是否过滤上下架if (saleable != null) {criteria.orEqualTo("saleable", saleable);}// 是否模糊查询if (StringUtils.isNotBlank(key)) {criteria.andLike("title", "%" + key + "%");}Page<Spu> pageInfo = (Page<Spu>) this.spuMapper.selectByExample(example);List<SpuBo> list = pageInfo.getResult().stream().map(spu -> {// 2、把spu变为 spuBoSpuBo spuBo = new SpuBo();// 属性拷贝BeanUtils.copyProperties(spu, spuBo);// 3、查询spu的商品分类名称,要查三级分类List<String> names = this.categoryService.queryNameByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));// 将分类名称拼接后存入spuBo.setCname(StringUtils.join(names, "/"));// 4、查询spu的品牌名称Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());spuBo.setBname(brand.getName());return spuBo;}).collect(Collectors.toList());return new PageResult<>(pageInfo.getTotal(), list);}
}

4.4.4.mapper

public interface SpuMapper extends Mapper<Spu> {
}

4.4.5.Category中拓展查询名称的功能

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

在CategoryService中添加功能:

public List<String> queryNameByIds(List<Long> ids) {return this.categoryMapper.selectByIdList(ids).stream().map(Category::getName).collect(Collectors.toList());
}

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

public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category, Long> { // ...coding
}

4.5.测试

刷新页面,查看效果:

微服务项目之电商--17.商品规格数据结构SPU和SKU相关推荐

  1. 微服务项目之电商--9.商城架构图及商城管理系统前端页面介绍及电商项目初步搭建(1)

    目录 一.商城架构图 前端: 二.商城管理系统前端页面 1.SPA介绍 2.webpack 四个核心概念 3.vue-cli 安装 4.项目测试 三.电商项目搭建 创建父模块管理 创建子模板注册中心l ...

  2. 微服务项目之电商--15.后台分页查询及新增品牌业务添加

    目录 一.后台提供查询接口 1.数据库表 2.实体类 3.mapper 4.controller 分页业务与通用mapper的使用 前端数据接收 二.新增品牌 1.controller 2.servi ...

  3. 微服务项目之电商--19.ElasticSearch基本、高级查询和 过滤、结果过滤、 排序和聚合aggregations

    接上一篇 目录 3.查询 3.1.基本查询: 3.1.1 查询所有(match_all) 3.1.2 匹配查询(match) 3.1.3 多字段查询(multi_match) 3.1.4 词条匹配(t ...

  4. 商城-商品规格管理-SPU和SKU数据结构

    商城-商品规格管理-SPU和SKU数据结构 3.SPU和SKU数据结构 3.1.SPU表 3.1.1.表结构 3.1.2.spu中的规格参数 3.1.2.1.specifications字段 3.1. ...

  5. 基于微服务的分布式电商系统-妖猫商城

    妖猫商城是一个采用微服务框架springcloud 实现的电商项目. 本项目实现了权限管理.商品管理(商品.属性.品牌.类目)等,并实现了商品详情页到支付的整个流程. 主要采用的技术如下: redis ...

  6. 搭建微服务架构的电商平台系统

    业务介绍 基于传统电商业务搭建一套线上商城平台,主要涉及到三个角色分为平台.供应商.普通会员用户: 平台审核注册的供应商.审核供应商发布的商品.定义全平台商品促销活动等功能 供应商新建商品.发布商品. ...

  7. 电商系统-商品规格与销售属性的研究及数据库设计

    摘要:在电商系统中,商品规格与销售属性往往是一类较为复杂的数据结构,不仅仅在于数据的查询存储上,还在于数据库中的属性数据的查询.与Java Bean的转换等.本文将从属性数据的展示为起点,数据库的设计 ...

  8. 微服务项目【秒杀商品展示及商品秒杀】

    登录方式调整 第1步:从zmall-common的pom.xml中移除spring-session-data-redis依赖 注意: 1)本次不采用spring-session方式,改用redis直接 ...

  9. 数商云B2B电商系统商品管理功能剖析,助力家用电器企业业务提效

    如今,传统家用电器企业的发展空间不断受到电商渠道的积压,由于许多家电企业缺乏数字化的管理工具,导致管理低效,还很容易产生存货积压.供不应求等问题.随着家用电器市场需求疲软.竞争日趋白热化,家用电器企业 ...

最新文章

  1. GPU—加速数据科学工作流程
  2. 数组是逻辑结构还是存储结构_数据结构之存储方式
  3. 第一次上课:特征值特征向量的几何直观
  4. 浅谈如何增强ASP程序性能
  5. Space-Filling Designs
  6. java中md5怎么实现的_java中md5算法的实现
  7. devexpress卸载不干净_最好用的卸载工具,清理彻底,专治各种流氓软件
  8. Django中Form组件的使用
  9. [百万级]通用分页存储过程.[千万级]实现千万级数据的分页显示!
  10. 射频识别技术漫谈(26)——Felica的文件系统
  11. Java毕设项目-学生档案管理系统
  12. VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-S CALE IMAGE RECOGNITION-论文笔记
  13. elemen-ui表格默认样式的修改
  14. python+scapy实现ARP欺骗
  15. c语言作业答,C语言练习作业及答案
  16. 关于Ubuntu20.04下向日葵远程软件连接中断的解决方法
  17. 长短期记忆网络(LSTM)
  18. Office 2016系列下载地址
  19. ant--Java工程打包工具
  20. 遍历和添加json对象的属性 和 遍历普通js对象的属性

热门文章

  1. Python练习题2.2计算分段函数
  2. UnicodeMath编码教程(转载)
  3. 用C语言实现简单的计算器
  4. 用于钢结构桥梁局部计算的ansa建模教程1
  5. JAVA完全参考手册(第8版) 第1章至第5章
  6. ruoyi笔记0-尝试启动ruoyi
  7. VS Code配置Java环境(Java17)
  8. 篇三:Jmeter察看结果树与保存
  9. 【openwrt】使用4G模块 移远EC20/25(2)pppd拨号与配置
  10. 51单片机的出租车计价器设计