当前位置: 首页 > news >正文

第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&#xff1a;Standard Product Unit&#xff08;标准化产品单元&#xff09; 是商品信息聚合的最小单位&#xff0c;是一组可复用、易检索的标准化信息的集合&#xff0c;该集合描述了一个 产品的特性。 决定商品属性的值 SKU&#xff1a;Stock…...

动漫人物眼睛画法

本期的动漫绘画课程教大家来学习动漫人物眼睛画法&#xff0c;结合板绘软件从草稿开始一步步教你画出动漫人物眼睛&#xff0c;不用报动漫培训班也能学会&#xff0c;快来跟着本期的动漫人物眼睛画法教程试试吧&#xff01; 动漫人物眼睛画法步骤教程&#xff1a; 注意&#x…...

张晨光-JAVA零基础保姆式JDBC技术教程

JDBC文档 JDBC概述 JDBC概述 Java DataBase Connectivity Java 数据库连接技术 JDBC的作用 通过Java语言操作数据库&#xff0c;操作表中的数据 SUN公司为**了简化、**统一对数据库的操作&#xff0c;定义了一套Java操作数据库的规范&#xff0c;称之为JDBC JDBC的本质 是官方…...

华为OD机试 - 最多提取子串数目(Python)

最多提取子串数目 题目 给定由 [a-z] 26 个英文小写字母组成的字符串 A 和 B,其中 A 中可能存在重复字母,B 中不会存在重复字母 现从字符串 A 中按规则挑选一些字母,可以组成字符串 B。 挑选规则如下: 1) 同一个位置的字母只能被挑选一次 2) 被挑选字母的相对先后顺序不…...

LeetCode-1237. 找出给定方程的正整数解【双指针,二分查找】

LeetCode-1237. 找出给定方程的正整数解【双指针&#xff0c;二分查找】题目描述&#xff1a;解题思路一&#xff1a;双指针。首先我们不管f是什么&#xff0c;即function_id等于什么不管。但是我们可以调用customfunction中的f函数&#xff0c;然后我们遍历x,y(1 < x, y &l…...

广度优先搜索算法 - 迷宫找路

广度优先搜索算法1 思考问题1.1 这个迷宫需不需要指定入口和出口&#xff1f;2 先粗略实现2.1 源码2.2 源码解释3 优化代码3.1 优化读取文件部分3.2 增加错误处理4 再优化-让程序变得更加灵活4.1 用户外部可以循环输入入口和出口5 完整代码这是一个提问者的提出的问题&#xff…...

泡脚材料简记

文章目录一般条件中药包&#xff08;药粉&#xff09;泡脚丸中药包&#xff08;药材&#xff09;艾叶生姜益母草藏红花食盐花椒白醋柠檬藿香泡脚私方紫苏叶、白术、白芍、黄芪、青皮、柴胡、夜交藤、丹参、当归&#xff0c;每种各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"&#xff1a;查询日志中含有某个关键字error的信息&#xff0c;显示行号。 cat -n app.log | grep "error" --color&#xff1a;查询日志中含有某个关键字error的信息&#xff0c;显示行号&#xff0c;带颜色…...

CleanMyMac4.12.5最新版安装下载教程

告别硬盘空间不足&#xff0c;让您的Mac极速如新CleanMyMac是一款强大的 Mac 清理、加速工具和健康卫士&#xff0c;让您的 Mac 加快启动速度。CleanMyMac是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻…...

RFID射频识别技术(四) RFID高频电路基础|课堂笔记|10月11日

2022年10月11日 week7 ​​​​​​​ 目录 ​​​​​​​ 第四讲: RFID高频电路基础 一、RLC(串联)电路的阻抗...

数据库系统是什么?它由哪几部分组成?

数据库系统&#xff08;Database System&#xff0c;DBS&#xff09;由硬件和软件共同构成。硬件主要用于存储数据库中的数据&#xff0c;包括计算机、存储设备等。软件部分主要包括数据库管理系统、支持数据库管理系统运行的操作系统&#xff0c;以及支持多种语言进行应用开发…...

华为OD机试题 - 任务混部(JavaScript)

最近更新的博客 2023新华为OD机试题 - 斗地主(JavaScript)2023新华为OD机试题 - 箱子之形摆放(JavaScript)2023新华为OD机试题 - 考古学家(JavaScript)2023新华为OD机试题 - 相同数字的积木游戏 1(JavaScript)2023新华为OD机试题 - 最多等和不相交连续子序列(JavaScri…...

键盘输入a,到屏幕显示,操作系统做了什么

首先&#xff0c;假定操作系统有中断系统。 等待的键盘写入的时候&#xff0c;txt进程被read函数阻塞。输入a之后&#xff0c;首先控制器&#xff0c;把扫描到的a放入到了控制器的寄存器中。触发硬中断通知cpu—> 中断IO控制方式&#xff0c;由硬件触发的。键盘读入中断cpu…...

Python机器学习入门笔记(2)—— 分类算法

目录 转换器&#xff08;transformer&#xff09;和估计器&#xff08;estimator&#xff09; K-近邻&#xff08;K-Nearest Neighbors&#xff0c;简称KNN&#xff09;算法 模型选择与调优 交叉验证&#xff08;Cross-validation&#xff09; GridSearchCV API 朴素贝叶…...

Docker镜像发布到阿里云和私有库

目录 一、Docker镜像 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;Docker镜像加载原理 &#xff08;三&#xff09;镜像分层结构优势 &#xff08;四&#xff09;重点理解 &#xff08;五&#xff09;docker commit操作实例 &#xff08;六&#xff09;总…...

初识CSS,美化HTML

CSS称为&#xff1a;层叠样式表&#xff08;Cascading style sheets&#xff09;美化HTML即给页面种的HTML标签设置样式CSS语法规则css要写在head标签的里边&#xff0c;title标签的下面&#xff0c;用style标签框住<head> <title>...</title> <style>…...

华为OD机试 - 二维矩阵的最大值(Python)

题目二维矩阵的最大值 给定一个仅包含0和1的n*n二维矩阵 请计算二维矩阵的最大值 计算规则如下 每行元素按下标顺序组成一个二进制数(下标越大约排在低位), 二进制数的值就是该行的值,矩阵各行之和为矩阵的值允许通过向左或向右整体循环移动每个元素来改变元素在行中的位置 …...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)

题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...

二叉树-144.二叉树的前序遍历-力扣(LeetCode)

一、题目解析 对于递归方法的前序遍历十分简单&#xff0c;但对于一位合格的程序猿而言&#xff0c;需要掌握将递归转化为非递归的能力&#xff0c;毕竟递归调用的时候会调用大量的栈帧&#xff0c;存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧&#xff0c;而非…...

运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.

报错 找到package.json文件 找到这个修改成 "lint": "eslint --fix --ext .js,.vue src" 为elsint有配置结尾换行符&#xff0c;最后运行&#xff1a;npm run lint --fix...