第9天-商品服务(电商核心概念,属性分组开发及分类和品牌的级联更新)
1.电商核心概念
1.1.SPU与SKU
-
SPU:Standard Product Unit(标准化产品单元)
是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个 产品的特性。
决定商品属性的值
-
SKU:Stock Keeping Unit(库存量单位)
即库存进出计量的基本单元,可以是件,盒,托盘等为单位。SKU这是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种 产品均对应有唯一的SKU号。
决定销售属性的值
-
举例
iPhone12,iPhone12 Pro :SPU
iPhone12 128G 白色:SKU
1.2.规格参数(基本属性)与销售属性
每个分类下的商品共享规格参数与销售属性,只是有些商品不一定要用这个分类下全部的属性。
- 属性是以三级分类组织起来的
- 规格参数中有些是可以提供检索的
- 规格参数也是基本属性,他们具有自己的分组
- 属性的分组也是以三级分类组织起来的
- 属性名确定,但是值是每一个商品不同来决定的
1.3.数据表设计
- pms_attr 属性表
- pms_attr_group 属性分组表
- pms_attr_attrgroup_relation 属性和属性分组关联表
- pms_product_attr_value 商品属性值表
- pms_spu_info 商品属性表
- pms_sku_info 销售属性表
- pms_sku_images 销售属性图片表
- pms_sku_sale_attr_value 销售属性值表
数据表采用了冗余设计,尽量减少数据关联查询,提供查询性能(互联网系统设计建议)!!!
注意:在数据表更新时要保持数据一致性,在业务代码中要手动实现多表级联更新!!!
1.4.SKU - SPU关系图
属性分组 - 规格参数 - 销售属性 - 三级分类 【关联关系】




2.查询属性分组-前端
2.1.功能说明
- 属性分组组件默认加载所有的属性分组数据
- 点击三级分类,在属性分组列表显示对应的三级分类下的所有属性分组

前端Vue组件:
- attrgroup.vue
- attrgroup-add-or-update.vue
- attr-group-relation.vue
2.2.抽取三级分类组件
2.2.1.创建 category.vue 组件
在 views/modules/common/ 目录下新建 category.vue 三级分类Vue组件
2.2.2.使用 category.vue 组件
在 attrgroup.vue 属性分组父组件中使用 category.vue 组件
<template><category> </category>
</template>
<script>
import Category from ' ../common/category'
export default {components: { Category }
}
</script>
2.3.父子组件传递数据
2.3.1.子组件给父组件传值
子组件给父组件传递数据:事件机制(子组件给父组件发送一个事件,携带上数据)
//$emit('事件名', 参数...)
this.$emit('node-click', data, node, component)
3.查询属性分组-后台
3.1.获取分类属性分组
3.1.1.API
GET /product/attrgroup/list/{catelogId}//请求参数
{page: 1, //当前页码limit: 10, //每页记录数sidx: 'id', //排序字段order: 'asc',//排序方式key: '华为' //检索关键字
}
3.1.2.接口实现
AttrGroupController
@RestController
@RequestMapping("product/attrgroup")
public class AttrGroupController {@Autowiredprivate AttrGroupService attrGroupService;/*** 根据三级分类ID查询父类下的属性分组*/@RequestMapping("/list/{catelogId}")public R list(@RequestParam Map<String, Object> params,@PathVariable Long catelogId){PageUtils page = attrGroupService.queryPage(params, catelogId);return R.ok().put("page", page);}
}
AttrGroupServiceImpl
@Service("attrGroupService")
public class AttrGroupServiceImpl extends ServiceImpl<AttrGroupDao,AttrGroupEntity> implements AttrGroupService {/*** 根据三级分类id查询属性分组* @param params 封装了分页信息参数* @param catelogId 三级分类id* @return*/@Overridepublic PageUtils queryPage(Map<String, Object> params, Long catelogId) {String key = (String) params.get("key");QueryWrapper<AttrGroupEntity> queryWrapper = new QueryWrapper<AttrGroupEntity>();if (!StringUtils.isEmpty(key)) {queryWrapper.and(obj -> {obj.eq("attr_group_id", key).or().like("attr_group_name", key);});}//如果三级分类id为0,则查询所有属性分组if (catelogId ==0) {IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),queryWrapper);return new PageUtils(page);} else {queryWrapper.eq("catelog_id", catelogId);IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),queryWrapper);return new PageUtils(page);}}
}
4.新增属性分组
4.1.Cascader 级联选择器
Element UI 级联选择器组件: Cascader
4.2.过滤三级分类子分类
商品分类只有三级分类,所以查询出来的三级分类不应该再有子分类显示

在 CategoryEntity 里面,将children属性加上 @JsonInclude
//children 为空,则不输出该属性
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<CategoryEntity> children;
5.修改属性分组
5.1.前端级联选择器回显
Cascader 级联选择器的回显需要三级分类的完整路径 [level1Id, level2Id, level3Id]
5.2.查询三级分类完整路径
5.2.1.API
GET /product/attrgroup/info/{attrGroupId}
5.2.2.后台接口实现
AttrGroupController
/**
* 查询属性分组
* @param attrGroupId 属性分组ID
* @return 属性分组信息
*/
@RequestMapping("/info/{attrGroupId}")
public R info(@PathVariable("attrGroupId") Long attrGroupId){AttrGroupEntity attrGroup = attrGroupService.getById(attrGroupId);//查找三级分类的完整路径 [level1Id, level2Id, level3Id]Long[] catelogPath =categoryService.findCategoryPath(attrGroup.getCatelogId());attrGroup.setCatelogPath(catelogPath);return R.ok().put("attrGroup", attrGroup);
}
CategoryServiceImpl
/*** 查找三级分类的完整路径* @param catelogId 三级分类id* @return 三级分类的完整路径*/
@Override
public Long[] findCategoryPath(Long catelogId) {List<Long> paths = new ArrayList<>();List<Long> fullPath = findParentPath(catelogId, paths);//集合数据进行逆序Collections.reverse(fullPath);return fullPath.toArray(new Long[fullPath.size()]);
}
/*** 递归收集三级分类的父id* @param catelogId* @param paths* @return*/
private List<Long> findParentPath(Long catelogId, List<Long> paths) {//收集当前节点idpaths.add(catelogId);//查询当前分类的信息CategoryEntity categoryEntity = getById(catelogId);if (categoryEntity.getParentCid() != 0) {//递归findParentPath(categoryEntity.getParentCid(), paths);}return paths;
}
6.分页插件
6.1.MyBatis-Plus分页插件
6.1.1.PaginationInterceptor 拦截器
package com.atguigu.gmall.product.config;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;/*** MyBatisPlus 配置类 {@link MybatisPlusConfig}** @author zhangwen* @email: 1466787185@qq.com*/
@Configuration
//开启事务
@EnableTransactionManagement
@MapperScan("com.atguigu.gmall.product.dao")
public class MybatisPlusConfig {/*** 引入分页插件* @return 分页插件拦截器实例*/@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求,默认falsepaginationInterceptor.setOverflow(true);// 设置最大单页限制数量,默认 500 条,-1 不受限制paginationInterceptor.setLimit(100);// 开启 count 的 join 优化,只针对部分 left joinpaginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));return paginationInterceptor;}
}
7.品牌分类关联
7.1.数据表
pms_category_brand_relation 品牌分类关联表
7.2.获取品牌关联的分类
7.2.1.API
GET /product/categorybrandrelation/catelog/list//请求参数
brandId Long //品牌id//响应数据
{"msg": "success","code": 0,"data": [{"catelogId": 4,"catelogName": "华为",}]
}
7.2.2.后台接口实现
CategoryBrandRelationController
/*** 获取品牌关联的所有分类*/
@GetMapping("/catelog/list")
public R list(@RequestParam("brandId") Long brandId){List<CategoryBrandRelationEntity> list = categoryBrandRelationService.list(brandId);return R.ok().put("data", list);
}
CategoryBrandRelationServiceImpl
/*** 获取品牌关联的所有分类* @param brandId* @return*/
@Override
public List<CategoryBrandRelationEntity> list(Long brandId) {List<CategoryBrandRelationEntity> brandRelationEntities = this.list(new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId));return brandRelationEntities;
}
7.3.新增品牌分类
7.3.1.API
POST product/categorybrandrelation/save//请求参数
{"brandId": 1,"catelogId": 2
}//响应数据
{"msg": "success","code": 0
}
7.3.2.后台接口实现
CategoryBrandRelationController
/*** 保存*/
@RequestMapping("/save")
public R save(@RequestBody CategoryBrandRelationEntity categoryBrandRelation){categoryBrandRelationService.saveDetail(categoryBrandRelation);return R.ok();
}
CategoryBrandRelationServiceImpl
/*** 新增品牌分类* @param categoryBrandRelation*/
@Override
public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) {//获取品牌名BrandEntity brandEntity = brandDao.selectById(categoryBrandRelation.getBrandId());//获取分类名CategoryEntity categoryEntity = categoryDao.selectById(categoryBrandRelation.getCatelogId());//设置categoryBrandRelation.setBrandName(brandEntity.getName());categoryBrandRelation.setCatelogName(categoryEntity.getName());this.save(categoryBrandRelation);
}
8.冗余数据同步
数据表冗余设计
在品牌分类关联表中有两个冗余数据:
- brand_name 品牌名
- catelog_name 分类名
提示:在修改品牌和修改分类的业务操作中,需要同步更新品牌分类关联表中的品牌名和分类名!
8.1.级联更新品牌
BrandController
/*** 级联更新品牌*/
@RequestMapping("/update")
public R update(@RequestBody BrandEntity brand){brandService.updateCascade(brand);return R.ok();
}
BrandService
/*** 级联更新品牌* @param brand*/
void updateCascade(BrandEntity brand);
BrandServiceImpl
/**
* 级联更新品牌
* @param brand
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(BrandEntity brand) {//保证数据表冗余字段的数据一致//更新品牌表的数据this.updateById(brand);if (!StringUtils.isEmpty(brand.getName())) {//同步更新品牌分类关联表中的数据CategoryBrandRelationEntity categoryBrandRelationEntity = new CategoryBrandRelationEntity();categoryBrandRelationEntity.setBrandId(brand.getBrandId());categoryBrandRelationEntity.setBrandName(brand.getName());categoryBrandRelationDao.update(categoryBrandRelationEntity,new UpdateWrapper<CategoryBrandRelationEntity>().eq("brand_id", brand.getBrandId()));//TODO 更新其他关联}
}
8.2.级联更新商品分类
CategoryController
/*** 级联更新*/
@RequestMapping("/update")
public R update(@RequestBody CategoryEntity category){categoryService.updateCascade(category);return R.ok();
}
CategoryService
/*** 级联更新分类* @param category*/
void updateCascade(CategoryEntity category);
CategoryServiceImpl
/*** 级联更新分类* @param category*/
@Transactional(rollbackFor = Exception.class)
@Override
public void updateCascade(CategoryEntity category) {this.updateById(category);if (!StringUtils.isEmpty(category.getName())) {//同步更新品牌分类关联表中的数据CategoryBrandRelationEntity categoryBrandRelationEntity = new CategoryBrandRelationEntity();categoryBrandRelationEntity.setCatelogId(category.getCatId());categoryBrandRelationEntity.setCatelogName(category.getName());categoryBrandRelationDao.update(categoryBrandRelationEntity,new UpdateWrapper<CategoryBrandRelationEntity>().eq("catelog_id", category.getCatId()));}
}
相关文章:
第9天-商品服务(电商核心概念,属性分组开发及分类和品牌的级联更新)
1.电商核心概念 1.1.SPU与SKU SPU:Standard Product Unit(标准化产品单元) 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个 产品的特性。 决定商品属性的值 SKU:Stock…...
动漫人物眼睛画法
本期的动漫绘画课程教大家来学习动漫人物眼睛画法,结合板绘软件从草稿开始一步步教你画出动漫人物眼睛,不用报动漫培训班也能学会,快来跟着本期的动漫人物眼睛画法教程试试吧! 动漫人物眼睛画法步骤教程: 注意&#x…...
张晨光-JAVA零基础保姆式JDBC技术教程
JDBC文档 JDBC概述 JDBC概述 Java DataBase Connectivity Java 数据库连接技术 JDBC的作用 通过Java语言操作数据库,操作表中的数据 SUN公司为**了简化、**统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC JDBC的本质 是官方…...
华为OD机试 - 最多提取子串数目(Python)
最多提取子串数目 题目 给定由 [a-z] 26 个英文小写字母组成的字符串 A 和 B,其中 A 中可能存在重复字母,B 中不会存在重复字母 现从字符串 A 中按规则挑选一些字母,可以组成字符串 B。 挑选规则如下: 1) 同一个位置的字母只能被挑选一次 2) 被挑选字母的相对先后顺序不…...
LeetCode-1237. 找出给定方程的正整数解【双指针,二分查找】
LeetCode-1237. 找出给定方程的正整数解【双指针,二分查找】题目描述:解题思路一:双指针。首先我们不管f是什么,即function_id等于什么不管。但是我们可以调用customfunction中的f函数,然后我们遍历x,y(1 < x, y &l…...
广度优先搜索算法 - 迷宫找路
广度优先搜索算法1 思考问题1.1 这个迷宫需不需要指定入口和出口?2 先粗略实现2.1 源码2.2 源码解释3 优化代码3.1 优化读取文件部分3.2 增加错误处理4 再优化-让程序变得更加灵活4.1 用户外部可以循环输入入口和出口5 完整代码这是一个提问者的提出的问题ÿ…...
泡脚材料简记
文章目录一般条件中药包(药粉)泡脚丸中药包(药材)艾叶生姜益母草藏红花食盐花椒白醋柠檬藿香泡脚私方紫苏叶、白术、白芍、黄芪、青皮、柴胡、夜交藤、丹参、当归,每种各10g艾叶、花椒、肉桂、桂枝、红花干姜30克、小茴…...
【计算机网络】因特网概述
文章目录因特网概述网络、互联网和因特网互联网历史与ISP标准化与RFC因特网的组成三种交换方式电路交换分组交换和报文交换三种交换方式的对比与总结计算机网络的定义和分类计算机网络的定义计算机网络的分类计算机网络的性能指标速率带宽吞吐量时延时延带宽积往返时间利用率丢…...
STC单片机 VS/HX1838红外接收和发送实验
STC单片机 VS/HX1838红外接收和发送实验 📌相关篇《STC单片机获取红外解码从串口输出》🔨所使用的红外接收头VS1838 📋VS1838引脚定义🌿5MM发射头,940nm红外发射二极管 红外遥控发射头。(外观看起来和普通的发光二极管没有什么差异,购买时需要注意确认)。 🔰采用的…...
前端开发常用案例(一)
前端开发常用案例1.实现三角形百度热榜样式分页效果小米商城自动轮播图效果二级下拉菜单效果时间轴效果展示音乐排行榜效果鼠标移入文字加载动画鼠标悬停缩放效果1.实现三角形 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8…...
Linux 日志查找常用命令
1.1 cat、zcat cat -n app.log | grep "error":查询日志中含有某个关键字error的信息,显示行号。 cat -n app.log | grep "error" --color:查询日志中含有某个关键字error的信息,显示行号,带颜色…...
CleanMyMac4.12.5最新版安装下载教程
告别硬盘空间不足,让您的Mac极速如新CleanMyMac是一款强大的 Mac 清理、加速工具和健康卫士,让您的 Mac 加快启动速度。CleanMyMac是一款专业的Mac清理软件,可智能清理mac磁盘垃圾和多余语言安装包,快速释放电脑内存,轻…...
RFID射频识别技术(四) RFID高频电路基础|课堂笔记|10月11日
2022年10月11日 week7 目录 第四讲: RFID高频电路基础 一、RLC(串联)电路的阻抗...
数据库系统是什么?它由哪几部分组成?
数据库系统(Database System,DBS)由硬件和软件共同构成。硬件主要用于存储数据库中的数据,包括计算机、存储设备等。软件部分主要包括数据库管理系统、支持数据库管理系统运行的操作系统,以及支持多种语言进行应用开发…...
华为OD机试题 - 任务混部(JavaScript)
最近更新的博客 2023新华为OD机试题 - 斗地主(JavaScript)2023新华为OD机试题 - 箱子之形摆放(JavaScript)2023新华为OD机试题 - 考古学家(JavaScript)2023新华为OD机试题 - 相同数字的积木游戏 1(JavaScript)2023新华为OD机试题 - 最多等和不相交连续子序列(JavaScri…...
键盘输入a,到屏幕显示,操作系统做了什么
首先,假定操作系统有中断系统。 等待的键盘写入的时候,txt进程被read函数阻塞。输入a之后,首先控制器,把扫描到的a放入到了控制器的寄存器中。触发硬中断通知cpu—> 中断IO控制方式,由硬件触发的。键盘读入中断cpu…...
Python机器学习入门笔记(2)—— 分类算法
目录 转换器(transformer)和估计器(estimator) K-近邻(K-Nearest Neighbors,简称KNN)算法 模型选择与调优 交叉验证(Cross-validation) GridSearchCV API 朴素贝叶…...
Docker镜像发布到阿里云和私有库
目录 一、Docker镜像 (一)概述 (二)Docker镜像加载原理 (三)镜像分层结构优势 (四)重点理解 (五)docker commit操作实例 (六)总…...
初识CSS,美化HTML
CSS称为:层叠样式表(Cascading style sheets)美化HTML即给页面种的HTML标签设置样式CSS语法规则css要写在head标签的里边,title标签的下面,用style标签框住<head> <title>...</title> <style>…...
华为OD机试 - 二维矩阵的最大值(Python)
题目二维矩阵的最大值 给定一个仅包含0和1的n*n二维矩阵 请计算二维矩阵的最大值 计算规则如下 每行元素按下标顺序组成一个二进制数(下标越大约排在低位), 二进制数的值就是该行的值,矩阵各行之和为矩阵的值允许通过向左或向右整体循环移动每个元素来改变元素在行中的位置 …...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
