第9天-商品服务(电商核心概念,属性分组开发及分类和品牌的级联更新)

news2024/11/17 9:33:38

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 {
	@Autowired
	private 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
	 */
	@Override
	public 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) {
	//收集当前节点id
	paths.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 分页插件拦截器实例
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求,默认false
        paginationInterceptor.setOverflow(true);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInterceptor.setLimit(100);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.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()));
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/353798.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第43天| 123.买卖股票的最佳时机III、 188.买卖股票的最佳时机IV

1.题目链接&#xff1a;123. 买卖股票的最佳时机 III 题目描述&#xff1a; 给定一个数组&#xff0c;它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意&#xff1a;你不能同时参与多笔交易&#…

基于深度神经网络的3D模型合成【Transformer vs. CNN】

本文介绍用于3D模型合成的transformer网络与深度卷积网络。 推荐&#xff1a;使用 NSDT场景设计器 快速搭建 3D场景。 1、概述 从单一视角合成 3D 数据是一种基本的人类视觉功能&#xff0c;这对计算机视觉算法来说极具挑战性&#xff0c;这是一个共识。 但在 3D 传感器&#…

Fedora Linux未来五年规划

Fedora 委员会一直致力于起草战略计划&#xff0c;以帮助 Fedora Linux 更好地发展。近日 Fedora 委员会公布了一份 “《未来五年的 Fedora Linux 》” 战略计划草案&#xff0c;这份草案里面包含了他们的雄心壮志&#xff1a;每周将 Fedora 的活跃贡献者人数增加一倍。 Fedora…

如何获取docpelx求解目标函数后的数据;在解决目标优化问题之后,如何获取相关数据;获取决策变量的具体数值

获取优化问题的自变量取值和目标函数取值 说明通过 mdl.integer_var() 定义的决策变量&#xff0c;获取求解值决策变量获取目标函数取值获取具体代码&#xff1a;通过 mdl.continuous_var_list() 定义的决策变量&#xff0c;获取求解值具体代码说明 本次的代码环境是 python中…

【WEB安全】SQL注入挖掘

文章目录前言一、sql注入的分类注入漏洞存在位置二、漏洞挖掘Google语法疑似注入点手工挖掘批量挖取此类漏洞已知sql注入漏洞挖掘总结免责声明&#xff1a;前言 2021年OWASP发布漏洞威胁榜单&#xff0c;SQL注入从第一名下降到第三&#xff08;https://owasp.org/Top10/&#…

从0开始学python -34

Python3 输入和输出-2 读和写文件 open() 将会返回一个 file 对象&#xff0c;基本语法格式如下: open(filename, mode)filename&#xff1a;包含了你要访问的文件名称的字符串值。mode&#xff1a;决定了打开文件的模式&#xff1a;只读&#xff0c;写入&#xff0c;追加等。…

数据分析| Pandas200道练习题,使用Pandas连接MySQL数据库

文章目录使用Pandas连接数据库编码环境依赖包read_sql_query()的使用read_sql_table()的使用read_sql() 函数的使用to_sql()写入数据库的操作删除操作更新操作总结&#xff1a;使用Pandas连接数据库 通过pandas实现数据库的读&#xff0c;写操作时&#xff0c;首先需要进行数据…

kubernetes教程 -- k8s组件

k8s组件 maste节点 apiServer&#xff1a;资源操作的唯一入口&#xff0c;接受用户的输入&#xff0c;提供认证&#xff0c;控制访问等功能Scheduler&#xff1a;负责集群的资源调度&#xff0c;按照预定的调度策略将Pod调度到相应的node节点上controllerManager&#xff1a;负…

JVM学习篇剖析JVM类加载机制

1. 类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把主类加载到JVM。 public class Math {private static int initData 6666;public static User user new User();private int compute() {int a 1;int b 3;int c (…

【vue后台管理系统】基于Vue+Element-UI+ECharts开发通用管理后台(下)

文章目录面包屑导航制作效果展示思路分析代码实现过程需求优化用户管理页面效果展示新增用户表单实现table组件编写分页功能编写搜索功能编写附&#xff1a;ES6的解构赋值数组模型的解构&#xff08;Array&#xff09;对象模型的解构&#xff08;Object&#xff09;面包屑导航制…

[Linux篇] Linux常见命令和权限

文章目录使用XShell登录Linux1.Linux常用基本命令&#xff1a;1.1 ls&#xff08;列出当前的目录下都有哪些文件和目录&#xff09;1.2 cd (change directory 切换目录)1.3 pwd&#xff08;查看当前目录的绝对路径&#xff09;1.4 touch&#xff08;创建文件&#xff09;1.5 ca…

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

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

TestNG单元测试框架-常用注解介绍以及testng和Junit的区别【杭州多测师_王sir】【杭州多测师】...

一、TestNG单元测试框架-常用注解介绍 testng学习网址&#xff1a;https://www.jc2182.com/testng/testng-environment.html 1、Before类别和After类别注解按照如下循序执行BeforeSuite->BeforeTest->BeforeClass->{BeforeMethod->Test->AfterMethod}->After…

微信支付Native下单API接口正确调用姿势

商户Native支付下单接口&#xff0c;微信后台系统返回链接参数code_url&#xff0c;商户后台系统将code_url值生成二维码图片&#xff0c;用户使用微信客户端扫码后发起支付。 文档地址&#xff1a;微信支付-开发者文档 目录 一、Native下单接口简介 二、如何正确调通接口 (…

5. AOP

一、如何定义一个MethodHandler? 1.Controller注解修饰的类 1.注册成Spring Bean 2.表示它是一个SpringMVC下的Controller 2.在这个类下的方法中&#xff0c;只要被RequestMapping修饰&&方法的形参符合规定&#xff08;需要看文档&#xff09; 方法的返回值符合规定…

VK0256/B/C玩具、冷气机等段码液晶驱动芯片(IC)(32EGx8COM)技术资料选表

K PP 2543型号&#xff1a;VK0256/B/C封装形式&#xff1a;QFP64/LQFP64/LQFP52 VK0256/B/C是一个点阵式存储映射的LCD驱动器&#xff0c;可支持最大256点&#xff08;32EGx8COM&#xff09;的LCD屏。单片机可通过3/4线串行接口配置显示参数和发送显示数据&#xff0c;也可通过…

与ChatGpt聊天,学习golang标签的反射机制

与ChatGpt聊天&#xff0c;学习golang标签的反射机制引ChatGPT火了以后&#xff0c;本拐先是恐惧&#xff0c;之后是拥抱。最近很多编程知识的学习&#xff0c;都是通过 chatgpt来搞定。众所周知&#xff0c;本拐就是一个啥技术都半斤八两的程序员&#xff0c;这次&#xff0c;…

05服务发现:引入etcd服务注册中心

在分布式微服务架构中,服务注册发现组件(通常称为服务注册中心)往往有着举足轻重的作用,它的性能与稳定可能会直接影响到整个服务的状态,比如Spring Cloud中的Eureka、Dubbo中的Zookeeper等等,接下来我们就gRPC微服务中最常见的服务注册中心etcd,来讲述下两者在具体是怎…

Mr. Cappuccino的第41杯咖啡——Kubernetes之Pod调度策略

Kubernetes之Pod调度策略Pod的4种调度策略定向调度nodeNamenodeSelector亲和性调度node亲和性硬限制软限制关系运算符pod亲和性pod反亲和性污点和容忍污点&#xff08;taints&#xff09;容忍&#xff08;tolerations&#xff09;默认情况下&#xff0c;Scheduler计算出一个Pod…

conda 搭建tensorflow-GPU和pycharm以及VS2022 软件环境配置

conda 搭建tensorflow-GPU和pycharm以及VS2022 软件环境配置一、TensorFlow 环境配置安装1. Anaconda下载安装2.conda创建tensorflow环境二、pycharm以及VS2022 环境配置2.1 pycharm 软件安装以及环境配置2.2.1 pycharm 软件安装2.2.2 pycharm 软件conda环境配置2.2 Visual Stu…