Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)十(商品的规格类型以及参数管理

}

2)实体类对应的通用Mapper

在这里插入图片描述

package com.leyou.item.mapper;

import com.leyou.item.pojo.SpecParam;

import tk.mybatis.mapper.common.Mapper;

public interface SpecParamMapper extends Mapper {

}

3)实体类对应的Service,在SpecificationService当中注入SpecParamMapper 即可

在这里插入图片描述

@Autowired

private SpecParamMapper specParamMapper;

4)在SpecificationController当中

在这里插入图片描述

/*

根据组id查询参数

*/

@GetMapping(“params”)

public ResponseEntity<List> queryParamByGid(@RequestParam(“gid”) Long gid){

return ResponseEntity.ok(specificationService.queryParamByGid(gid));

}

5)完善SpecificationService

创建抛出异常的枚举

在这里插入图片描述

SPEC_PARAM_NOT_FOND(404,“商品规格参数不存在”),

完善SpecificationService

在这里插入图片描述

在这里插入图片描述

public List queryParamByGid(Long gid) {

SpecParam specParam = new SpecParam();

specParam.setGroupId(gid);

List list = specParamMapper.select(specParam);

if(CollectionUtils.isEmpty(list)){

// 没有查询到

throw new LyException(ExceptionEnum.SPEC_PARAM_NOT_FOND);

}

return list;

}

6)重新运行并测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3、SPU和SKU数据结构

(1)什么是SPU和SKU

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

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

以图为例来看:

在这里插入图片描述

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

  • 因为颜色、内存等不同,而细分出不同的手机,如亮黑色128G版。(SKU)可以看出;

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

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

(2)数据库设计分析
1)思考分析

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

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

  • SPU共享数据

id:主键

title:标题

description:描述

specification:规格

packaging_list:包装

after _service:售后服务

comment:评价

category_id:商品分类

brand_id:品牌

似乎并不复杂

  • 再看下SKU(每个商品的特殊属性)

大家觉得应该有什么字段?

id:主健

spu_id:关联的

spuprice:价格

images:图片

stock:库存颜色?

内存?

硬盘?

上述做法是错误的:SKU的特有属性也是变化的。

不同分类特有属性也不一定相同。

2)SKU的特有属性

SPU中会有一些特殊属性,用来区分不同的SKU,我们称为sKU特有属性。如华为META10的颜色、内存属性.不同种类的商品,一个手机,一个衣服,其SKU属性不相同。

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

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

SKU的特有属性是商品规格参数的一部分∶

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

  • 所有sku共享的规格属性〈称为通用属性),我们记录在SPU表中。

  • 每个sku不同的规格属性(称为特有属性),我们记录在SKU表中。

回一下之前我们设计的tb _spec_param表,是不是有一个字段,名为generic,标记通用和特有属性。就是为使用。

(3)SPU表
1)表结构

SPU表:

CREATE TABLE tb_spu (

id bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘spu id’,

title varchar(128) NOT NULL DEFAULT ‘’ COMMENT ‘标题’,

sub_title varchar(256) DEFAULT ‘’ COMMENT ‘子标题’,

cid1 bigint(20) NOT NULL COMMENT ‘1级类目id’,

cid2 bigint(20) NOT NULL COMMENT ‘2级类目id’,

cid3 bigint(20) NOT NULL COMMENT ‘3级类目id’,

brand_id bigint(20) NOT NULL COMMENT ‘商品所属品牌id’,

saleable tinyint(1) NOT NULL DEFAULT ‘1’ COMMENT ‘是否上架,0下架,1上架’,

valid tinyint(1) NOT NULL DEFAULT ‘1’ COMMENT ‘是否有效,0已删除,1有效’,

create_time datetime DEFAULT NULL COMMENT ‘添加时间’,

last_update_time datetime DEFAULT NULL COMMENT ‘最后修改时间’,

PRIMARY KEY (id) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=195 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘spu表,该表描述的是一个抽象性的商品,比如 iphone8’;

与我们前面分析的基本类似,但是似乎少了一些字段,比如商品描述。

我们做了表的垂直拆分,将SPu的详情放到了另一张表: tb_spu_detail

CREATE TABLE tb_spu_detail (

spu_id bigint(20) NOT NULL,

description text COMMENT ‘商品描述信息’,

generic_spec varchar(2048) NOT NULL DEFAULT ‘’ COMMENT ‘通用规格参数数据’,

special_spec varchar(1024) NOT NULL COMMENT ‘特有规格参数及可选值信息,json格式’,

packing_list varchar(1024) DEFAULT ‘’ COMMENT ‘包装清单’,

after_service varchar(1024) DEFAULT ‘’ COMMENT ‘售后服务’,

PRIMARY KEY (spu_id) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

在这里插入图片描述

下面的参数字段是JSON格式的字符串,与规格参数表当中的数据是一致的

在这里插入图片描述

在这里插入图片描述

(4)SKU表
1)表结构

CREATE TABLE tb_sku (

id bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘sku id’,

spu_id bigint(20) NOT NULL COMMENT ‘spu id’,

title varchar(256) NOT NULL COMMENT ‘商品标题’,

images varchar(1024) DEFAULT ‘’ COMMENT ‘商品的图片,多个图片以‘,’分割’,

price bigint(15) NOT NULL DEFAULT ‘0’ COMMENT ‘销售价格,单位为分’,

indexes varchar(32) DEFAULT ‘’ COMMENT ‘特有规格属性在spu属性模板中的对应下标组合’,

own_spec varchar(1024) DEFAULT ‘’ COMMENT ‘sku的特有规格参数键值对,json格式,反序列化时请使用linkedHashMap,保证有序’,

enable tinyint(1) NOT NULL DEFAULT ‘1’ COMMENT ‘是否有效,0无效,1有效’,

create_time datetime NOT NULL COMMENT ‘添加时间’,

last_update_time datetime NOT NULL COMMENT ‘最后修改时间’,

PRIMARY KEY (id) USING BTREE,

KEY key_spu_id (spu_id) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=27359021729 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘sku表,该表表示具体的商品实体,如黑色的 64g的iphone 8’;

还有一张表代表库存

CREATE TABLE tb_stock (

sku_id bigint(20) NOT NULL COMMENT ‘库存对应的商品sku id’,

seckill_stock int(9) DEFAULT ‘0’ COMMENT ‘可秒杀库存’,

seckill_total int(9) DEFAULT ‘0’ COMMENT ‘秒杀总数量’,

stock int(9) NOT NULL COMMENT ‘库存数量’,

PRIMARY KEY (sku_id) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘库存表,代表库存,秒杀库存等信息’;

问题:为什么要将库存独立—张表?

因为库存字段写频率较高,而SKu的其它字段以读为主,因此我们将两张表分离,读写不会干扰。

特别需要注意的是sku表中的 indexes 字段和 own_spec字段。

sku中应该保存特有规格参数的值,就在这两个字段中。

在这里插入图片描述

4、功能实现

(1)页面分析

请求方式为GET

请求参数:

上架saleable是true

下降saleable是false

全部是没有saleable

key搜索的关键字

初始也page

以及显示的行数rows

在这里插入图片描述

在这里插入图片描述

发起的请求

在这里插入图片描述

(2)后台代码实现
1)实体类
a)SPU

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.pojo;

import lombok.Data;

import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;

import javax.persistence.Table;

import java.util.Date;

@Table(name = “tb_spu”)

@Data

public class Spu {

@Id

@KeySql(useGeneratedKeys = true)

private Long id;

private Long brandId;

private Long cid1; //1级类目

private Long cid2; //2级类目

private Long cid3; //2级类目

private String title;//标题

private String subTitle;//子标题

private Boolean saleable;//是否上架

private Boolean valid;//是否有效。逻辑删除用

private Date createTime;//创建时间

private Date lastUpdateTime;//最后修改时间

}

b)SPU详情SpuDetail

在这里插入图片描述

package com.leyou.item.pojo;

import lombok.Data;

import javax.persistence.Id;

import javax.persistence.Table;

@Table(name = “tb_spu_detail”)

public class SpuDetail {

@Id

private Long spuId;//对应SPU的id

private String description ; //商品描述

private String specialSpec ; //商品特殊规格的名称及可选值模板

private String genericSpec; //商品的全局规格属性

private String packingList; //包装清单

private String afterService ; //售后服务

}

2)实体类对应的mapper
a)SpuMapper

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.mapper;

import com.leyou.item.pojo.Spu;

import tk.mybatis.mapper.common.Mapper;

public interface SpuMapper extends Mapper {

}

b)SpuDetailMapper

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.mapper;

import com.leyou.item.pojo.SpuDetail;

import tk.mybatis.mapper.common.Mapper;

public interface SpuDetailMapper extends Mapper {

}

3)实体类对应的service

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.service;

import com.leyou.item.mapper.SpuDetailMapper;

import com.leyou.item.mapper.SpuMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service

public class GoodsService {

@Autowired

private SpuMapper spuMapper;

@Autowired

private SpuDetailMapper spuDetailMapper;

}

4)实体类对应的Controller

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.web;

import com.leyou.item.pojo.Spu;

import com.leyou.item.service.GoodsService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

@RequestMapping(“spu”)

public class GoodsController {

@Autowired

private GoodsService goodsService;

}

5)完善实体类对应返回的字段
a、添加依赖

在这里插入图片描述

com.fasterxml.jackson.core

jackson-databind

b、完善Spu实体类

在这里插入图片描述

package com.leyou.item.pojo;

import com.fasterxml.jackson.annotation.JsonIgnore;

import lombok.Data;

import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;

import javax.persistence.Table;

import javax.persistence.Transient;

import java.util.Date;

@Table(name = “tb_spu”)

@Data

public class Spu {

@Id

@KeySql(useGeneratedKeys = true)

private Long id;

private Long brandId;

private Long cid1; //1级类目

private Long cid2; //2级类目

private Long cid3; //2级类目

private String title;//标题

private String subTitle;//子标题

private Boolean saleable;//是否上架

@JsonIgnore //设置返回页面数据的时候,忽略当前字段

private Boolean valid;//是否有效。逻辑删除用

private Date createTime;//创建时间

@JsonIgnore //设置返回页面数据的时候,忽略当前字段

private Date lastUpdateTime;//最后修改时间

@Transient //Transient声明当前字段不是数据对应的字段

private String cname;

@Transient //Transient声明当前字段不是数据对应的字段

private String bname;

}

c、完善GoodsController

在这里插入图片描述

package com.leyou.item.web;

import com.leyou.common.vo.PageResult;

import com.leyou.item.pojo.Spu;

import com.leyou.item.service.GoodsService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

@RestController

@RequestMapping

public class GoodsController {

@Autowired

private GoodsService goodsService;

/*

分页查询SPU

*/

@GetMapping(“/spu/page”)

public ResponseEntity<PageResult> querySpuByPage(

@RequestParam(value = “page”,defaultValue = “1”) Integer page,

@RequestParam(value = “rows”,defaultValue = “5”) Integer rows,

@RequestParam(value = “saleable”,required = false) Boolean saleable,

@RequestParam(value = “key”,required = false) String key

){

return ResponseEntity.ok(goodsService.querySpuByPage(page,rows,saleable,key));

}

}

d、完善GoodsService
  • 设置对应商品查询不存在的枚举

在这里插入图片描述

GOODS_NOT_FOND(404,“商品不存在”),

  • 完善GoodsService

在这里插入图片描述

package com.leyou.item.service;

import com.github.pagehelper.PageHelper;

import com.github.pagehelper.PageInfo;

import com.leyou.common.enums.ExceptionEnum;

import com.leyou.common.exception.LyException;

import com.leyou.common.vo.PageResult;

import com.leyou.item.mapper.SpuDetailMapper;

import com.leyou.item.mapper.SpuMapper;

import com.leyou.item.pojo.Spu;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.util.CollectionUtils;

import tk.mybatis.mapper.entity.Example;

import java.util.List;

@Service

public class GoodsService {

@Autowired

private SpuMapper spuMapper;

@Autowired

private SpuDetailMapper spuDetailMapper;

public PageResult querySpuByPage(Integer page, Integer rows, Boolean saleable, String key) {

//分页

PageHelper.startPage(page,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);

}

//设置默认排序方式为商品的更新时间

example.setOrderByClause(“last_update_time DESC”);

//查询

List spus = spuMapper.selectByExample(example);

if(CollectionUtils.isEmpty(spus)){

throw new LyException(ExceptionEnum.GOODS_NOT_FOND);

}

//解析父类和品牌的名称

loadCategoryAndBrandName(spus);

//解析分页的结果

PageInfo info = new PageInfo<>(spus);

return new PageResult<>(info.getTotal(),spus);//将分页信息和结果集合放入PageResult当前返回到页面

}

private void loadCategoryAndBrandName(List spus) {

for (Spu spu : spus) {

//处理父类名称

//处理品牌名称

}

}

}

  • 完善CategoryService商品分类

在这里插入图片描述

public List queryByIds(List ids){

}

  • 需要扩展CategoryMapper,可以继承多个接口

在这里插入图片描述

package com.leyou.item.mapper;

import com.leyou.item.pojo.Category;

import org.apache.ibatis.annotations.Select;

import tk.mybatis.mapper.additional.idlist.IdListMapper;

import tk.mybatis.mapper.common.Mapper;

import java.util.List;

public interface CategoryMapper extends Mapper, IdListMapper<Category,Long> {//Long第二个参数是主键类型

/**

  • 根据品牌id查询商品分类id 然后通过商品分类id查询对应所有的商品分类

*/

@Select(“SELECT * FROM tb_category WHERE id IN (SELECT category_id FROM tb_category_brand WHERE brand_id = #{bid})”)

List queryByBrandId(Long id);

}

  • 继续完善CategoryService当中queryByIds方法

在这里插入图片描述

public List queryByIds(List ids){

List list = categoryMapper.selectByIdList(ids);

if(CollectionUtils.isEmpty(list)){

//查询失败抛出自定义的通用异常

throw new LyException(ExceptionEnum.CATEGORY_NOT_FOND);

}

return list;

}

  • 继续完善GoodsService当中的loadCategoryAndBrandName方法

注入CategoryService

在这里插入图片描述

处理父类名称

在这里插入图片描述

private void loadCategoryAndBrandName(List spus) {

for (Spu spu : spus) {

//处理父类名称

//Arrays.asList直接将参数序列化为集合

List categorylist = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));

//将categorylist变为字符串平均

//提取List对象的某getName列值及排重

Stream stringStream = categorylist.stream().map(Category::getName);

//对返回的字符串集合转换为String类型的name集合

List namelist = stringStream.collect(Collectors.toList());

//将集合以/为间隔拼接为字符串

spu.setCname(StringUtils.join(namelist,“/”));

//处理品牌名称

}

《MySql面试专题》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySql性能优化的21个最佳实践》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySQL高级知识笔记》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

关注我,点赞本文给更多有需要的人

//处理父类名称

//Arrays.asList直接将参数序列化为集合

List categorylist = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));

//将categorylist变为字符串平均

//提取List对象的某getName列值及排重

Stream stringStream = categorylist.stream().map(Category::getName);

//对返回的字符串集合转换为String类型的name集合

List namelist = stringStream.collect(Collectors.toList());

//将集合以/为间隔拼接为字符串

spu.setCname(StringUtils.join(namelist,“/”));

//处理品牌名称

}

《MySql面试专题》

[外链图片转存中…(img-ElhR46Jp-1714389327562)]

[外链图片转存中…(img-iy1TqXSq-1714389327563)]

《MySql性能优化的21个最佳实践》

[外链图片转存中…(img-H3ThyYB0-1714389327563)]

[外链图片转存中…(img-fcIex6co-1714389327564)]

[外链图片转存中…(img-DtcVBKNL-1714389327564)]

[外链图片转存中…(img-349BlqSR-1714389327564)]

《MySQL高级知识笔记》

[外链图片转存中…(img-oMKtnhql-1714389327565)]

[外链图片转存中…(img-rwXExbL7-1714389327565)]

[外链图片转存中…(img-KhAn4Wig-1714389327565)]

[外链图片转存中…(img-QlJMr7yi-1714389327566)]

[外链图片转存中…(img-uD42wOTo-1714389327566)]

[外链图片转存中…(img-vvbLByjE-1714389327566)]

[外链图片转存中…(img-45Bpc9UK-1714389327567)]

[外链图片转存中…(img-cOX8awPf-1714389327567)]

[外链图片转存中…(img-I5rsnBzT-1714389327567)]

[外链图片转存中…(img-skCLzcNa-1714389327568)]

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

[外链图片转存中…(img-HCAr9R8f-1714389327568)]

关注我,点赞本文给更多有需要的人

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
本文介绍了一个基于Spring BootSpring CloudVue前后端分离项目实战。这个项目是一个简单的在线商城,包含了用户注册、登录、商品展示、购物车、订单管理功能。通过这个项目,读者可以深入理解前后端分离的架构模式和互联网应用的开发方式。 首先,文章介绍了前后端分离的基本概念和优势。前后端分离是将应用的前端和后端代码分开来开发,使得前端和后端具有独立的开发周期和技术栈,进而提高了开发效率和代码质量。同时,前后端分离还可以提供更好的用户体验和灵活性,对于互联网应用来说尤为重要。 接下来,文章介绍了项目的架构和技术栈。项目采用了Spring BootSpring Cloud框架来实现后端代码,采用MyBatis作为ORM框架和Redis作为缓存中间件。同时,项目还采用了Vue.js作为前端框架和Element UI组件库来实现前端页面。通过这些开源框架和组件,可以快速搭建一个前后端分离的互联网应用。 然后,文章介绍了项目的核心功能和代码实现。在用户注册和登录方面,项目采用了Spring Security框架和JWT令牌来实现用户认证和授权,保证了用户信息的安全性。在商品展示和购物车方面,项目采用了Vue.js来实现前端页面和事件处理。在订单管理方面,项目采用了MyBatis Plus来实现订单数据的持久化和分页查询。 最后,文章介绍了项目的测试和优化。通过对项目的压力测试和性能测试,文章发现项目还存在一些性能瓶颈和安全隐患,可以通过优化数据库查询、缓存配置和代码实现来提高应用的性能和安全性。 总之,这篇文章介绍了一个基于Spring BootSpring CloudVue前后端分离项目实战,通过实现一个在线商城的功能,展示了前后端分离的开发模式和互联网应用的开发技术栈。本文可以作为前后端分离开发的入门教程,也可以作为互联网应用开发的参考文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值