二手房网站建设方案,公司展示类网站模板,深圳农产品网站制作,网站栏目设计怎么写第2章 基础数据处理和分布式文件存储
1.分布式文件存储系统Ceph学习
1).掌握Ceph架构
2).掌握Ceph组件
3).搭建Ceph集群(了解)
2.Ceph使用
1).基于Ceph实现文件上传
2).基于Ceph实现文件下载
3.SKU、SPU管理
1).掌握SKU和SPU关系
2).理解商品发…第2章 基础数据处理和分布式文件存储
1.分布式文件存储系统Ceph学习
1).掌握Ceph架构
2).掌握Ceph组件
3).搭建Ceph集群(了解)
2.Ceph使用
1).基于Ceph实现文件上传
2).基于Ceph实现文件下载
3.SKU、SPU管理
1).掌握SKU和SPU关系
2).理解商品发布中商品属性、商品分类、商品品牌加载方案
3).实现SKU和SKU管理商品发布
4.品牌管理、分类管理、属性管理作业
1).实现品牌管理增删改查
2).实现分类管理增删改查
3).实现属性管理增删改查
5.掌握MyBatisPlus代码生成
1).掌握MyBatisPlus代码生成配置
2).掌握MyBatisPlus代码生成controller,service,mapper
1.分布式文件存储系统Ceph
Ceph是一个统一的分布式存储系统设计初衷是提供较好的性能、可靠性和可扩展性
对比说明FASTDFSCEPH开发语言CC数据存储方式文件/Trunk对象/文件/块在线扩容支持支持冗余备份支持支持单点故障不存在不存在易用性安装简单社区相对活跃安装有一定复杂度适用场景单集群的中小文件单集群的大中小文件
1.1 Ceph介绍
Ceph于2004年发表并随后贡献给开源社区。在经过了数年的发展之后目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack都可与Ceph整合以支持虚拟机镜像的后端存储
Ceph特点
CRUSH算法Crush算法是ceph的两大创新之一简单来说ceph摒弃了传统的集中式存储元数据寻址的方案转而使用CRUSH算法完成数据的寻址操作。CRUSH在一致性哈希基础上很好的考虑了容灾域的隔离能够实现各类负载的副本放置规则例如跨机房、机架感知等。Crush算法有相当强大的扩展性理论上支持数千个存储节点
高性能Ceph中的数据副本数量可以由管理员自行定义并可以通过CRUSH算法指定副本的物理存储位置以分隔故障域支持数据强一致性 ceph可以忍受多种故障场景并自动尝试并行修复
高扩展性Ceph本身并没有主控节点扩展起来比较容易并且理论上它的性能会随着磁盘数量的增加而线性增长
特性丰富Ceph支持三种调用接口对象存储块存储文件系统挂载。三种方式可以一同使用。在国内一些公司的云环境中通常会采用ceph作为openstack的唯一后端存储来提升数据转发效率
中文学习网http://docs.ceph.org.cn/
Ceph架构 组件对象讲解
RADOS就是这样一个可用于PB级规模数据存储集群的可伸缩的、可靠的对象存储服务可以理解成Ceph的整个存储对象包括逻辑对象
File用户上传的文件
object上传的文件被切成N个小文件块对象RADOS的基本存储单元
MDS元数据的内存缓存为了加快元数据的访问
CRUSHCeph寻址算法用于计算当前文件存储到哪个PG对应的OSD中
PG对object的存储进行组织和位置映射。具体而言一个PG负责组织若干个object可以为数千个甚至更多但一个object只能被映射到一个PG中即PG和object之间是“一对多”映射关系。同时一个PG会被映射到n个OSD上而每个OSD上都会承载大量的PG即PG和OSD之间是“多对多”映射关系
OSDRADOS中的存储节点被称为OSD
架构图讲解
1.文件上传先将文件切片成N个object如果开启了cephFS可以使用MDS缓存 2.切片后的文件object会存入到Ceph中 3.文件存储前会经过CRUSH算法计算当前文件存储归结于哪个PG 4.PG是逻辑概念上对文件存储范围划分的索引 5.根据PG索引将文件存储到指定服务器的OSD中
1.2 Ceph集群搭建 集群结构如上图server1作为主节点(Dashbaord、mon、mds、rgw、mgr、osd)server2和server3作为子节点(mon、mds、rgw、mgr、osd)
节点中信息说明
dashbaord:Ceph可视化管理界面 rgw:RADOSGW,Ceph对象网关,使客户端能够利用标准对象存储API来访问Ceph集群 mgr:ceph-mgr主要目标实现 ceph 集群的管理为外界提供统一的入口
节点信息 server1:192.168.100.131 server2:192.168.100.132 server3:192.168.100.133
1.2.1 准备工作
1)机器名称修改131、132、133都执行
我们给每台机器一个别名192.168.100.131-CENTOS1,192.168.100.132-CENTOS2,192.168.100.133-CENTOS3
修改192.168.100.131/etc/hostname添加CENTOS1 修改192.168.100.(同上)/etc/hostname添加CENTOS2(同上)
修改192.168.100.133/etc/hostname添加CENTOS3(同上)
配置名字解析IP分别修改131、132、133的/etc/hosts文件添加如下映射
192.168.100.131 CENTOS1
192.168.100.132 CENTOS2
192.168.100.133 CENTOS32)YUM源修改131、132、133都执行
这里采用清华镜像源提升加载速度
vi /etc/yum.repos.d/ceph.repo,添加如下内容
[Ceph]
nameCeph packages for $basearch
baseurlhttps://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/x86_64/
enabled1
gpgcheck1
typerpm-md
gpgkeyhttps://download.ceph.com/keys/release.asc[Ceph-noarch]
nameCeph noarch packages
# 清华源
baseurlhttps://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/noarch/
enabled1
gpgcheck1
typerpm-md
gpgkeyhttps://download.ceph.com/keys/release.asc[ceph-source]
nameCeph source packages
baseurlhttps://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/SRPMS/
enabled1
gpgcheck1
typerpm-md
gpgkeyhttps://download.ceph.com/keys/release.asc3)ceph与ceph-deploy安装131
更新yum源并安装ceph和ceph-deploy这个过程非常耗时间执行如下命令
yum update yum -y install ceph ceph-deploy
注意yum update和yum -y install python2-pip最好在每台机器都更新下yum。
安装过程中 如果执行ceph-deploy出现ImportError: No module named pkg_resources,则需要安装python2-pip执行yum -y install python2-pip 安装即可
如果遇到如下错误安装下epel即可 操作命令(最好先执行该命令)
yum install epel-release -yyum install lttng-ust -y4)NTP时间同步工具131执行
为了保证时间同步我们需要安装NTP时间同步工具
yum install ntp ntpdate ntp-doc -y设为开机启动
systemctl enable ntpd设置每隔1小时自动校准同步。编辑 vi /etc/rc.d/rc.local 追加
/usr/sbin/ntpdate ntp1.aliyun.com /dev/null 21; /sbin/hwclock -w配置定时任务, 执行crontab -e 加入
0 */1 * * * ntpdate ntp1.aliyun.com /dev/null 21; /sbin/hwclock -w5)免密配置131、132、133都执行
官方建议不用系统内置用户 创建名为cuser用户 密码也设为cuser
useradd -d /home/cuser -m cuser
passwd cuser设置sudo权限(免密只读权限)
echo cuser ALL (root) NOPASSWD:ALL | sudo tee /etc/sudoers.d/cuser
sudo chmod 0440 /etc/sudoers.d/cuser6)生成秘钥131执行
切换用户 su cuser
执行ssh-keygen一直按默认提示点击生成RSA密钥信息。 分发密钥至各机器节点
ssh-copy-id cuserCENTOS1
ssh-copy-id cuserCENTOS2
ssh-copy-id cuserCENTOS3修改管理节点上的 ~/.ssh/config 当前用户目录下的.ssh/config文件 简化SSH远程连接时的输入信息
管理节点是会有root和cuser多个用户 ssh远程连接默认会以当前用户身份进行登陆 如果我们是root身份进行远程连接 还是需要输入密码我们可以修改配置 使用root远程连接时也不用输入密码。
切换root身份
su root编辑config
vi ~/.ssh/config添加如下内容
Host CENTOS1Hostname CENTOS1User cuser
Host CENTOS2Hostname CENTOS2User cuser
Host CENTOS3Hostname CENTOS3User cuser修改文件权限
chmod 600 ~/.ssh/config禁用SELINUX
vi /etc/selinux/configSELINUXdisabled1.2.2 集群搭建
安装集群用root安装可以避免很多权限问题。
1)创建集群管理目录作为ceph配置信息存储目录
mkdir -p /usr/local/gupao/cephclustercd /usr/local/gupao/cephcluster2)创建集群
ceph-deploy new CENTOS1 CENTOS2 CENTOS3创建成功后 会生配置文件和秘钥信息 3)修改配置文件
编辑ceph.conf文件vi /usr/local/gupao/cephcluster/ceph.conf添加如下配置
#对外开放网段
public network 192.168.100.0/24
# 设置pool池默认分配数量
osd pool default size 2
# 容忍更多的时钟误差
mon clock drift allowed 2
mon clock drift warn backoff 30
# 允许删除pool
mon_allow_pool_delete true
[mgr]
# 开启WEB仪表盘
mgr modules dashboard注意Pool是存储对象的逻辑分区它规定了数据冗余的类型和对应的副本分布策略
完整内容如下 文件修改后执行安装131执行此时3台机器都会执行安装执行如下安装命令
ceph-deploy install CENTOS1 CENTOS2 CENTOS3如果出现ceph_deploy][ERROR ] RuntimeError: Failed to execute command: ceph --version错误可以直接在每个节点单独执行yum -y install ceph 进行单独安装。如果没有仓库文件ceph.repo 按上面的步骤手工创建
4)初始化Monitor信息
ceph-deploy mon create-initial此时会生成很多秘钥文件信息 5)同步管理信息
ceph-deploy admin CENTOS1 CENTOS2 CENTOS36)安装mgr(管理守护进程)
ceph-deploy mgr create CENTOS1 CENTOS2 CENTOS37)安装rgw
ceph-deploy rgw create CENTOS1 CENTOS2 CENTOS3mds服务
ceph-deploy mds create CENTOS1 CENTOS2 CENTOS3注意任意一个环节安装失败了需要卸载重装
ceph-deploy purge CENTOS1 CENTOS2 CENTOS3
ceph-deploy purgedata CENTOS1 CENTOS2 CENTOS3
ceph-deploy forgetkeys将三台节点的mon信息也删除
rm -rf /var/run/ceph/如果出现错误
ceph_deploy][ERROR ] RuntimeError: Failed to execute command: ceph --version可以在各节点上单独进行安装
yum -y install ceph 8)OSD安装
OSD服务是对象存储守护进程 负责把对象存储到本地文件系统 必须要有一块独立的磁盘作为存储。如果没有独立磁盘怎么办 可以在Linux下面创建一个虚拟磁盘进行挂载
添加磁盘:
执行fdisk -l查看磁盘信息如下我们需要添加一个磁盘直接用VMware添加即可。 使用VMware选择设置-硬盘-添加如下图 一直点击下一步设置磁盘空间大小为10G即可。操作完后重启虚拟机并输入fdisk -l查看磁盘信息如下明显多了/dev/sdb 10G大小 执行创建OSD命令(注意每条命令都是在131中执行不要在每台机器中单独执行)
ceph-deploy osd create --data /dev/sdb CENTOS1ceph-deploy osd create --data /dev/sdb CENTOS2ceph-deploy osd create --data /dev/sdb CENTOS3Monitor查看
在/usr/bin下执行./ceph -s可以查看集群状态 可以执行ntpdate ntp1.aliyun.com 同步各个节点的时间
如果出现如下情况执行systemctl restart ceph.target重启每个节点即可131,132,133都执行 1.2.3 dashboard安装
Ceph 提供了原生的Dashboard功能通过Dashboard可以获取Ceph集群的各种基本状态信息。我们接下来安装一下Dashboard并使用它的功能
1)开启dashboard模块
ceph mgr module enable dashboard2)生成签名
ceph dashboard create-self-signed-cert3)创建目录
mkdir -p /usr/local/gupao/cephcluster/mgr-dashboard4)生成密钥对
openssl req -new -nodes -x509 -subj /OIT/CNceph-mgr-dashboard -days 3650 -keyout dashboard.key -out dashboard.crt -extensions v3_ca5)启动dashboard
ceph mgr module disable dashboard
ceph mgr module enable dashboard6)设置IP与PORT
ceph config set mgr mgr/dashboard/server_addr 192.168.100.131
ceph config set mgr mgr/dashboard/server_port 90017)关闭HTTPS
ceph config set mgr mgr/dashboard/ssl false8)查看服务信息
ceph mgr services9)设置管理员账号密码
ceph dashboard set-login-credentials admin admin10)访问https://192.168.100.131:8443/#/dashboard 11)RGW访问
访问 http://192.168.100.131:7480/ 效果如下 1.3 Cephfs管理
集群创建完后 默认没有文件系统 我们创建一个Cephfs可以支持对外访问的文件系统。
1创建两个存储池, 执行两条命令
ceph osd pool create cephfs_data 128
ceph osd pool create cephfs_metadata 64少于5个OSD可把pg_num设置为128
OSD数量在5到10可以设置pg_num为512
OSD数量在10到50可以设置pg_num为4096
OSD数量大于50需要计算pg_num的值
通过下面命令可以列出当前创建的存储池
ceph osd lspools2创建fs, 名称为fs_test:
ceph fs new fs_test cephfs_metadata cephfs_data3状态查看 以下信息代表正常
ceph fs ls
name: fs_test, metadata pool: cephfs_metadata, data pools: [cephfs_data ]ceph mds stat:
fs_test-0/0/1 up4fuse挂载
先确定ceph-fuse命令能执行 如果没有 则安装 yum -y install ceph-fuse创建挂载目录
mkdir -p /usr/local/gupao/cephfs_directory挂载cephfs
ceph-fuse -k /etc/ceph/ceph.client.admin.keyring -m 192.168.100.131:6789 /usr/local/gupao/cephfs_directory出现下面信息表示挂载成功了
ceph-fuse[28003]: starting fuse5挂载信息查看
[rootCENTOS1 cephcluster]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.1G 0 4.1G 0% /dev
tmpfs 4.1G 0 4.1G 0% /dev/shm
tmpfs 4.1G 20M 4.1G 1% /run
tmpfs 4.1G 0 4.1G 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 2.0G 16G 12% /
/dev/sda1 1014M 189M 826M 19% /boot
tmpfs 4.1G 28K 4.1G 1% /var/lib/ceph/osd/ceph-0
tmpfs 838M 0 838M 0% /run/user/0
ceph-fuse 13G 0 13G 0% /usr/local/gupao/cephfs_directory1.4 Ceph Swift API接口开发
Swift是由Rackspace开发的用来为云计算提供可扩展存储的项目。专注于对象存储 并提供一套REST风格的Api来访问 与Ceph强一致性不同 它是最终一致性。两者都是优秀的开源项目 并无明显优劣之分在使用场景上有所不同 如果是专注于对象存储 那么可以选择swift即可满足需要 如果还有块存储要求 那么选择Ceph更为合适。这里选择Ceph 因为通过网关可以适配兼容swift api 同时在数据访问上具有较强的扩展性 1.4.1 准备工作
创建Swift用户, 用于接口请求认证
sudo radosgw-admin user create --subusercephtester:subtester --uidcephtester --display-namecephtester --key-typeswift --secretgupao --accessfulluid 为主用户 subuser为子用户信息 secret指定密钥 不指定则随机生成 access拥有权限设定,代码中需使用返回信息中的user和secret_key swift_keys:
swift_keys: [{user: cephtester:subtester,secret_key: gupao}
],创建管理员账号
radosgw-admin user create --uidmgruser --display-namemgruser --system返回信息如下
{user_id: mgruser,display_name: mgruser,email: ,suspended: 0,max_buckets: 1000,auid: 0,subusers: [],keys: [{user: mgruser,access_key: AZ6L40PH9WB37EKVVMCZ,secret_key: rk8PEjtYaMTo7nMDM62hqqN1tOnZPBEe4GA0LQMW}],swift_keys: [],caps: [],op_mask: read, write, delete,system: true,default_placement: ,placement_tags: [],bucket_quota: {enabled: false,check_on_raw: false,max_size: -1,max_size_kb: 0,max_objects: -1},user_quota: {enabled: false,check_on_raw: false,max_size: -1,max_size_kb: 0,max_objects: -1},temp_url_keys: [],type: rgw,mfa_ids: []
}根据生成的access_key与secret_key 执行
ceph dashboard set-rgw-api-access-key AZ6L40PH9WB37EKVVMCZ
ceph dashboard set-rgw-api-secret-key rk8PEjtYaMTo7nMDM62hqqN1tOnZPBEe4GA0LQMW打开管理界面http://192.168.100.131:9001/#/rgw/user 可以查看到我们刚才创建的两个用户 1.4.2 文件服务搭建
我们搭建一个单独的工程专门用于实现文件上传和文件下载工程坐标如下
groupIdcom.gupaoedu.vip.mall/groupId
version0.0.1-SNAPSHOT/version
artifactIdmall-file-service/artifactIdpom.xml:
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdmall-service/artifactIdgroupIdcom.gupaoedu.vip.mall/groupIdversion0.0.1-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdmall-file-service/artifactIddescription文件上传微服务/descriptiondependencies!-- Rados Java Api依赖 --dependencygroupIdcom.ceph/groupIdartifactIdrados/artifactIdversion0.6.0/version/dependency!-- Cephfs 文件系统依赖 --dependencygroupIdcom.ceph/groupIdartifactIdlibcephfs/artifactIdversion0.80.5/version/dependency!--swift--dependencygroupIdorg.javaswift/groupIdartifactIdjoss/artifactIdversion0.10.2/version/dependency/dependencies
/projectbootstrap.yml:
server:port: 8082
spring:application:name: mall-filecloud:nacos:config:file-extension: yamlserver-addr: 192.168.1.11:8848discovery:#Nacos的注册地址server-addr: 192.168.1.11:8848ceph:username: cephtester:subtester #Ceph配置 主用户名子用户名password: gupao #秘钥authUrl: http://192.168.100.131:7480/auth/1.0 #接口访问路径defaultContainerName: user_datainfo #默认容器名字
#图片路径
cephurl: http://localhost:8082/file/download/#日志配置
logging:pattern:console: %msg%n创建com.gupaoedu.vip.mall.file.ceph.ContainerConfig配置类在类中创建Account和Container对象代码如下
/*** 容器的初始化操作*/
Data
Configuration
ConfigurationProperties(prefix ceph) //yaml配置文件中配置的属性
public class ContainerConfig {private String username;private String password;private String authUrl;private String defaultContainerName;/*** 1.创建账号信息*/Beanpublic Account account(){AccountConfig config new AccountConfig();//配置认证信息config.setUsername(username);config.setPassword(password);config.setAuthUrl(authUrl);//配置认证方式config.setAuthenticationMethod(AuthenticationMethod.BASIC);return new AccountFactory(config).createAccount();}/*** 2.创建容器对象*/Beanpublic Container container(){//获取账号信息Container container account().getContainer(defaultContainerName);if(!container.exists()){return container.create();}return container;}}创建文件上传下载工具类com.gupaoedu.vip.mall.file.ceph.FileHandler代码如下
Component
public class FileHandler {Autowiredprivate Container container;/*** 文件上传*/public void upload(String filename,byte[] buffer) {//buffer:文件内容字节//获取容器对象StoredObject object container.getObject(filename);//文件上传object.uploadObject(buffer);}/** 文件下载*/public byte[] download(String filename){//获取容器中远程存储的信息StoredObject object container.getObject(filename);//执行文件下载byte[] bytes object.downloadObject();return bytes;}
}控制器创建com.gupaoedu.vip.mall.file.controller.FileController
RestController
RequestMapping(value /file)
public class FileController {Autowiredprivate FileHandler fileHandler;Value(${cephurl})private String cephurl;/*** 文件上传*/PostMapping(value /upload)public RespResult upload(MultipartFile file) throws IOException {//上传fileHandler.upload(file.getOriginalFilename(),file.getBytes());return RespResult.ok(cephurlfile.getOriginalFilename());}/*** 下载*/GetMapping(value /download/{filename})public void download(PathVariable String filename, HttpServletResponse response) throws IOException {//下载byte[] bytes fileHandler.download(filename);//输出文件ServletOutputStream os response.getOutputStream();os.write(bytes);}
}创建启动类com.gupaoedu.vip.mall.file.MallFileApplication
/*** http://localhost:8082/file/download/news1.jpg*/
SpringBootApplication(exclude DataSourceAutoConfiguration.class)//屏蔽掉数据库的设置这里不需要用到数据库
public class MallFileApplication {public static void main(String[] args) {SpringApplication.run(MallFileApplication.class,args);}
}文件上传测试http://localhost:8082/file/upload 文件下载测试http://localhost:8082/file/download/20200716-af9fc1ae9d24f880.png 2.Spu和Sku产品发布
2.1 掌握SKU和SPU关系
SPU Standard Product Unit (标准化产品单元)SPU是商品信息聚合的最小单位是一组可复用、易检索的标准化信息的集合该集合描述了一个产品的特性。通俗点讲属性值、特性相同的商品就可以称为一个SPU
SKUstock keeping unit(库存量单位)SKU即库存进出计量的单位 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态不同管理模式来处理。在服装、鞋类商品中使用最多最普遍
举个例子
购买手机的时候你可以选择华为Mate40系列手机Mate40系列手机的生产制造商是华为品牌是华为手机分类也是华为不过Mate40系列手机有多款比如 Mate40 、Mate40 Pro 、 Mate40 Pro 每款手机的架构也不一样颜色也不一定一样那么这个例子中哪些是Spu哪些是Sku呢 Spu
手机系列Mate40系列 厂家华为 品牌华为 分类手机
Sku价格、颜色、网络格式
2.2 表结构设计
2.2.1 Spu和Sku
spu:
CREATE TABLE spu (id varchar(60) NOT NULL COMMENT 主键,name varchar(100) DEFAULT NULL COMMENT SPU名,intro varchar(200) DEFAULT NULL COMMENT 简介,brand_id int(11) DEFAULT NULL COMMENT 品牌ID,category_one_id int(20) DEFAULT NULL COMMENT 一级分类,category_two_id int(10) DEFAULT NULL COMMENT 二级分类,category_three_id int(10) DEFAULT NULL COMMENT 三级分类,images varchar(1000) DEFAULT NULL COMMENT 图片列表,after_sales_service varchar(50) DEFAULT NULL COMMENT 售后服务,content longtext COMMENT 介绍,attribute_list varchar(3000) DEFAULT NULL COMMENT 规格列表,is_marketable int(1) DEFAULT 0 COMMENT 是否上架,0已下架1已上架,is_delete int(1) DEFAULT 0 COMMENT 是否删除,0:未删除1已删除,status int(1) DEFAULT 0 COMMENT 审核状态0未审核1已审核2审核不通过,PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8;sku:
CREATE TABLE sku (id varchar(60) NOT NULL COMMENT 商品id,name varchar(200) NOT NULL COMMENT SKU名称,price int(20) NOT NULL DEFAULT 1 COMMENT 价格分,num int(10) DEFAULT 100 COMMENT 库存数量,image varchar(200) DEFAULT NULL COMMENT 商品图片,images varchar(2000) DEFAULT NULL COMMENT 商品图片列表,create_time datetime DEFAULT NULL COMMENT 创建时间,update_time datetime DEFAULT NULL COMMENT 更新时间,spu_id varchar(60) DEFAULT NULL COMMENT SPUID,category_id int(10) DEFAULT NULL COMMENT 类目ID,category_name varchar(200) DEFAULT NULL COMMENT 类目名称,brand_id int(11) DEFAULT NULL COMMENT 品牌id,brand_name varchar(100) DEFAULT NULL COMMENT 品牌名称,sku_attribute varchar(200) DEFAULT NULL COMMENT 规格,status int(1) DEFAULT 1 COMMENT 商品状态 1-正常2-下架3-删除,PRIMARY KEY (id),KEY cid (category_id),KEY status (status),KEY updated (update_time)
) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT商品表;2.2.2 商品发布流程分析
商品发布流程如下 1.分类选择三级树形结构
发布商品前需要先选择发布商品所属分类分类严格定义为3级分类三级加载 分类表
CREATE TABLE category (id int(20) NOT NULL AUTO_INCREMENT COMMENT 分类ID,name varchar(50) DEFAULT NULL COMMENT 分类名称,sort int(11) DEFAULT NULL COMMENT 排序,parent_id int(20) DEFAULT NULL COMMENT 上级ID,PRIMARY KEY (id),KEY parent_id (parent_id)
) ENGINEInnoDB AUTO_INCREMENT11182 DEFAULT CHARSETutf8 COMMENT商品类目;2.选择品牌分类选择完成后需要加载品牌品牌加载并非一次性加载完成而是根据选择的分类进行加载 分类品牌关系表
CREATE TABLE category_brand (category_id int(11) NOT NULL COMMENT 分类ID,brand_id int(11) NOT NULL COMMENT 品牌ID,PRIMARY KEY (brand_id,category_id)
) ENGINEInnoDB DEFAULT CHARSETutf8;品牌表
CREATE TABLE brand (id int(11) NOT NULL AUTO_INCREMENT COMMENT 品牌id,name varchar(100) NOT NULL COMMENT 品牌名称,image varchar(1000) DEFAULT COMMENT 品牌图片地址,initial varchar(1) DEFAULT COMMENT 品牌的首字母,sort int(11) DEFAULT NULL COMMENT 排序,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT14 DEFAULT CHARSETutf8 COMMENT品牌表;3.属性加载当选择分类后加载分类对应的属性 分类属性表
CREATE TABLE category_attr (category_id int(11) NOT NULL,attr_id int(11) NOT NULL COMMENT 属性分类表,PRIMARY KEY (category_id,attr_id)
) ENGINEInnoDB DEFAULT CHARSETutf8;属性表
CREATE TABLE sku_attribute (id int(11) NOT NULL AUTO_INCREMENT COMMENT ID,name varchar(50) DEFAULT NULL COMMENT 属性名称,options varchar(2000) DEFAULT NULL COMMENT 属性选项,sort int(11) DEFAULT NULL COMMENT 排序,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT13 DEFAULT CHARSETutf8;2.3 商品发布加载功能
对应的Bean写好
Category
package com.gupaoedu.vip.mall.goods.model;Data
AllArgsConstructor
NoArgsConstructor
//MyBatisPlus表映射注解
TableName(value category)
public class Category implements Serializable {TableId(type IdType.AUTO)private Integer id;private String name;private Integer sort;private Integer parentId;
}2.3.1 分类加载
分类功能需要实现按照父ID查询最开始初始化加载的是顶级父类parent_id0后面每次点击的时候都根据传入的id查询子分类
1)Mapper
创建com.gupaoedu.vip.mall.goods.mapper.CategoryMapper,代码如下
public interface CategoryMapper extends BaseMapperCategory {
}2)Service
接口com.gupaoedu.vip.mall.goods.service.CategoryService代码如下
public interface CategoryService extends IServiceCategory {/*** 根据父ID查询子分类*/ListCategory queryByParentId(Integer pid);
}实现类com.gupaoedu.vip.mall.goods.service.impl.CategoryServiceImpl代码如下
Service
public class CategoryServiceImpl extends ServiceImplCategoryMapper,Category implements CategoryService {Autowiredprivate CategoryMapper categoryMapper;/**** 根据父ID查询子分类*/Overridepublic ListCategory queryByParentId(Integer pid) {//条件封装QueryWrapperCategory queryWrapper new QueryWrapperCategory();queryWrapper.eq(parent_id,pid);return categoryMapper.selectList(queryWrapper);}
}3)Controller
创建com.gupaoedu.vip.mall.goods.controller.CategoryController代码如下;
在SpringMVC使用CrossOrigin注解来解决跨域问题
RestController
RequestMapping(value /category)
CrossOrigin //允许跨域
public class CategoryController {Autowiredprivate CategoryService categoryService;/***** 根据父ID查询子分类*/GetMapping(value /parent/{pid})public RespResultListCategory list(PathVariable(value pid)Integer pid){ListCategory categories categoryService.queryByParentId(pid);return RespResult.ok(categories);}
}2.3.2 品牌加载
品牌需要根据分类进行加载当用户选择第3级分类的时候加载品牌品牌数据需要经过category_brand表关联查询
我们可以按照如下步骤实现
1、查询category_brand中指定分类对应的品牌ID集合 2、从brand查出品牌集合
1)Mapper
修改com.gupaoedu.vip.mall.goods.mapper.BrandMapper添加根据分类ID查询品牌ID集合
public interface BrandMapper extends BaseMapperBrand {//根据分类ID查询品牌集合Select(select brand_id from category_brand where category_id#{id})ListInteger queryBrandIds(Integer id);
}2)Service
接口com.gupaoedu.vip.mall.goods.service.BrandService中添加根据分类ID查询品牌集合方法
//根据分类ID查询品牌
ListBrand queryByCategoryId(Integer id);实现类com.gupaoedu.vip.mall.goods.service.impl.BrandServiceImpl
/**** 根据分类ID查询品牌*/
Override
public ListBrand queryByCategoryId(Integer id) {//查询分类ID对应的品牌集合ListInteger brandIds brandMapper.queryBrandIds(id);//根据品牌ID集合查询品牌信息ListBrand brands brandMapper.selectBatchIds(brandIds);return brands;
}3)Controller
修改com.gupaoedu.vip.mall.goods.controller.BrandController添加根据分类ID查询品牌集合
/***** 根据分类ID查询品牌*/
GetMapping(value /category/{id})
public RespResultListBrand categoryBrands(PathVariable(value id)Integer id){ListBrand brands brandService.queryByCategoryId(id);return RespResult.ok(brands);
}2.3.3 属性加载
属性也称为规格属性也需要根据分类查询我们可以按照如下思路实现
1、先从category_attr根据分类ID查询出当前分类拥有的属性ID集合 2、从sku_attribute中查询属性集合
1)Mapper
创建com.gupaoedu.vip.mall.goods.mapper.SkuAttributeMapper实现根据分类ID查询属性信息。
public interface SkuAttributeMapper extends BaseMapperSkuAttribute {/**** 根据分类ID查询属性集合*/Select(SELECT * FROM sku_attribute WHERE id IN(SELECT attr_id FROM category_attr WHERE category_id#{id}))ListSkuAttribute queryByCategoryId(Integer id);
}2)Service
接口com.gupaoedu.vip.mall.goods.service.SkuAttributeService添加根据分类ID查询属性集合方法
public interface SkuAttributeService extends IServiceSkuAttribute {//根据分类ID查询属性集合ListSkuAttribute queryList(Integer id);
}实现类com.gupaoedu.vip.mall.goods.service.impl.SkuAttributeServiceImpl添加如下实现方法
Service
public class SkuAttributeServiceImpl extends ServiceImplSkuAttributeMapper, SkuAttribute implements SkuAttributeService {Autowiredprivate SkuAttributeMapper skuAttributeMapper;/**** 根据分类ID查询属性集合*/Overridepublic ListSkuAttribute queryList(Integer id) {return skuAttributeMapper.queryByCategoryId(id);}
}3)Controller
创建com.gupaoedu.vip.mall.goods.controller.SkuAttributeController添加如下方法
RestController
RequestMapping(value /skuAttribute)
CrossOrigin
public class SkuAttributeController {Autowiredprivate SkuAttributeService skuAttributeService;/**** 根据分类ID查询属性集合*/GetMapping(value /category/{id})public RespResultListSkuAttributeController categorySkuAttributeList(PathVariable(value id)Integer id){ListSkuAttribute skuAttributes skuAttributeService.queryList(id);return RespResult.ok(skuAttributes);}
}2.3.4 测试
选择对应的三级分类 选择好分类就会对应渲染出品牌和属性信息 2.4 商品发布
2.4.1 复合对象分析 商品发布如上图我们可以发现发布的商品信息包含Sku和Spu因此我们应该在后端能有一个对象同时能接到Spu和多个Sku方法有很多种我们可以直接在Spu中写一个ListSku但这种方法不推荐按照对象设计原则对一个对象进行扩展时尽量避免对原始对象造成改变因此我们可以使用复合类可以创建一个Prodcut类该类中有Spu也有ListSku代码如下
Data
NoArgsConstructor
AllArgsConstructor
public class Product {// Spuprivate Spu spu;// Skuprivate ListSku skus;
}2.4.2 添加商品
添加商品的时候我们需要保存Spu同时需要添加多个Sku。我们可以在华为商城中看看真实电商中Sku名字特征每次点击不同属性的时候前部分名字一样只是将名字中的规格替换了也就是说Sku的名字其实是组合成的一部分是Spu的一部分是Sku的可以进行组合 1.名字分析
添加商品的时候会将商品的属性传入后台格式如下如果把规格名字添加到名字中那就是华为商城中的效果了我们可以这么做把属性解析成Map然后每个属性值添加到商品名字中即可
{适合人群:有一定java基础的人,书籍分类:软件编程}
2.实现代码
Mapper
com.gupaoedu.vip.mall.goods.mapper.SpuMapper代码如下
public interface SpuMapper extends BaseMapperSpu {
}com.gupaoedu.vip.mall.goods.mapper.SkuMapper代码如下
public interface SkuMapper extends BaseMapperSku {
}Service
com.gupaoedu.vip.mall.goods.service.SpuService中添加产品方法如下
public interface SpuService extends IServiceSpu {//保存商品void saveProduct(Product product);
}com.gupaoedu.vip.mall.goods.service.impl.SpuServiceImpl中添加产品方法如下
Service
public class SpuServiceImpl extends ServiceImplSpuMapper,Spu implements SpuService {Autowiredprivate SkuMapper skuMapper;Autowiredprivate SpuMapper spuMapper;Autowiredprivate CategoryMapper categoryMapper;Autowiredprivate BrandMapper brandMapper;// 保存商品Overridepublic void saveProduct(Product product) {//SpuSpu spu product.getSpu();//上架spu.setIsMarketable(1);//未删除spu.setIsDelete(0);//状态spu.setStatus(1);//添加spuMapper.insert(spu);//查询三级分类Category category categoryMapper.selectById(spu.getCategoryThreeId());//查询品牌Brand brand brandMapper.selectById(spu.getBrandId());//当前时间Date now new Date();//新增Sku集合for (Sku sku : product.getSkus()) {//设置名字String skuName spu.getName();//{适合人群:有一定java基础的人,书籍分类:软件编程}MapString,String attrMap JSON.parseObject(sku.getSkuAttribute(), Map.class);for (Map.EntryString, String entry : attrMap.entrySet()) {skuName entry.getValue();}sku.setName(skuName);//设置图片sku.setImages(spu.getImages());//设置状态sku.setStatus(1);//设置类目IDsku.setCategoryId(spu.getCategoryThreeId());//设置类目名称sku.setCategoryName(category.getName());//设置品牌IDsku.setBrandId(brand.getId());//设置品牌名称sku.setBrandName(brand.getName());//设置Spuidsku.setSpuId(spu.getId());//时间sku.setCreateTime(now);sku.setUpdateTime(now);//增加skuMapper.insert(sku);}}
}Controller
创建com.gupaoedu.vip.mall.goods.controller.SpuController添加产品代码如下
RestController
RequestMapping(/spu)
CrossOrigin
public class SpuController {Autowiredprivate SpuService spuService;/**** 保存*/PostMapping(value /save)public RespResult save(RequestBody Product product){//保存spuService.saveSpuAndSku(product);return RespResult.ok();}
}2.4.3 产品修改
产品修改其实和产品添加几乎一致只需要做小改动即可实现步骤如下
1、如果Spu的id值不为空说明是修改操作 2、如果是修改操作先删除之前对应的Sku集合 3、其他流程和添加商品一致
修改com.gupaoedu.vip.mall.goods.service.impl.SpuServiceImpl的save方法代码如下 源码如下
Override
public void saveProduct(Product product) {//SpuSpu spu product.getSpu();//如果ID为空则增加if(StringUtils.isEmpty(spu.getId())){//上架spu.setIsMarketable(1);//未删除spu.setIsDelete(0);//状态spu.setStatus(1);//添加spuMapper.insert(spu);}else{//ID 不为空则修改spuMapper.updateById(spu);//删除之前的Sku记录skuMapper.delete(new QueryWrapperSku().eq(spu_id,spu.getId()));}//查询三级分类Category category categoryMapper.selectById(spu.getCategoryThreeId());//查询品牌Brand brand brandMapper.selectById(spu.getBrandId());//当前时间Date now new Date();//新增Sku集合for (Sku sku : product.getSkus()) {//设置名字String skuName spu.getName();MapString,String attrMap JSON.parseObject(sku.getSkuAttribute(), Map.class);for (Map.EntryString, String entry : attrMap.entrySet()) {skuName entry.getValue();}sku.setName(skuName);//设置图片sku.setImages(spu.getImages());//设置状态sku.setStatus(1);//设置类目IDsku.setCategoryId(spu.getCategoryThreeId());//设置类目名称sku.setCategoryName(category.getName());//设置品牌IDsku.setBrandId(brand.getId());//设置品牌名称sku.setBrandName(brand.getName());//设置Spuidsku.setSpuId(spu.getId());//时间sku.setCreateTime(now);sku.setUpdateTime(now);//增加skuMapper.insert(sku);}
}2.4.4 测试 输入对应的数据保存