Java实战项目——《谷粒商城》分布式基础篇
Java实战项目——《谷粒商城》的学习笔记——分布式基础
- 分布式基础(全栈开发)
- 1.1 项目简介
- 1.1.1 项目架构
- 1.1.2 电商模式
- 1.1.3 项目技术&特色
- 1.1.4 项目前置要求
- 1.2 分布式基础概念
- 1.2.1 微服务
- 1.2.2 集群、分布式、节点
- 1.2.3 远程调用
- 1.2.4 负载均衡
- 1.2.5 服务注册/发现、注册中心
- 1.2.6 配置中心
- 1.2.7 服务熔断、服务降级
- 1.2.8 API服务网关
- 1.3 环境搭建
- 1.3.1 安装Linux虚拟机
- 1.3.1.1 Linux虚拟机网络设置
- 1.3.2 安装Docker
- 1.3.2.1 配置Docker国内镜像加速
- 1.3.3 Docker安装MySQL
- 1.3.3.1 命令详细信息介绍
- 1.3.3.2 配置MySQL
- 1.3.4 Docker安装Redis
- 1.3.4.1 Redis可视化客户端
- 1.3.4.2 Redis配置
- 1.3.4.3 设置MySQL和Redis容器自启动
- 1.3.5 开发环境统一
- 1.3.5.1 Maven
- 1.3.5.2 Idea&VsCode
- 1.3.5.3 Git
- 1.3.6 创建项目微服务
- 1.3.6.1 从gitee初始化一个项目
- 1.3.7 数据库设计
- 1.4 后台管理系统
- 1.5 前端项目
- 1.5.1 Node.js
- 1.6 逆向工程搭建&使用
- 1.6.1 整合MyBatis-plus
- 1.7 微服务-注册中心、配置中心、网关
- 1.8 SpringCloud Alibaba
- 1.8.1 SCA Nacos 注册中心
- 1.8.2 SCA Nacos 配置中心
- 1.8.2.1 核心概念
- 1.8.2.2 同时加载多个配置集
- 1.9 SpringCloud
- 1.9.1 Feign声明式远程调用
- 1.9.2 Gateway
- 2 前端基础
- 2.1 ES6
- 2.1.1 ES6新特性
- 2.1.2 解构表达式&字符串拓展
- 2.1.3 函数优化
- 2.1.4 对象优化
- 2.1.5 map和reduce
- 2.1.6 Promise
- 2.1.7 模块化
- 2.2 Vue
- 2.2.1 MVVM
- 2.2.2 Vue指令
- 2.2.3 组件化
- 2.2.4 Vue模块化开发
- 2.2.5 整合ElementUI
- 3 商品服务
- 3.1 三级分类
- 3.2 品牌管理
- 3.2.1 文件上传功能
- 3.2.2 JSR303
- 3.3 SPU和SKU
- 3.3 平台属性
- 3.4 新增商品
- 4 分布式基础篇总结
- 4.1 分布式基础概念
- 4.2 基础开发
- 4.3 环境
- 4.4 开发规范
视频链接:Java项目《谷粒商城》.
分布式基础(全栈开发)
1.1 项目简介
1.1.1 项目架构
微服务架构图:
微服务划分图:
1.1.2 电商模式
常见的五种电商模式为:B2B,B2C,C2B,C2C,O2O。谷粒商城属于B2C模式的电商平台。
- B2B模式(Business to Business),商家与商家建立的商业关系,如阿里巴巴
- B2C模式(Business to Consumer),供应商直接把商品卖给用户,也就是通常说的商业零售,如:苏宁易购,京东,天猫,小米商城
- C2B模式(Consumer to Business),先有消费者需求后有商家生产
- C2C模式(Consumer to Consumer),客户之间把东西放到网上卖,如淘宝,闲鱼
- O2O模式(Online to Offline),线下商务与互联网结合,互联网成为线下交易的平台,线上快速支付,线下享受服务,如饿了么,美团,淘票票,京东到家
1.1.3 项目技术&特色
- 前后端分离开发,开发基于vue的后台管理系统
- SpringCloud全新的解决方案
- 应用监控、限流、网关、熔断降级等分布式方案
- 分布式事务,分布式锁等分布式系统的难点
- 分析高并发场景的编码方式,线程池,异步编排等使用
- 压力测试与性能优化
- 各种集群技术的区别及使用
- CI/CD使用
- …
1.1.4 项目前置要求
- 熟悉SpringBoot及常见解决方案
- 了解SpringCloud
- 熟悉git,maven
- 熟悉redis,linux,docker基本操作
- 了解html,css,javascript,vue
- 熟练使用idea开发项目
1.2 分布式基础概念
1.2.1 微服务
简而言之,拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。
1.2.2 集群、分布式、节点
集群是物理形态,分布式是工作方式。分布式中的每一个节点,都可以做集群,而集群并不一定是分布式。节点:集群中的一个服务器。
1.2.3 远程调用
在分布式系统中,每个服务可能存在不同的主机,但服务之间不可避免地需要相互调用,称为远程调用。SpringCloud使用HTTP+JSON的方式完成远程调用。
1.2.4 负载均衡
分布式系统中,A服务需要调用B服务,而B服务在多台服务器运行,为了使每个服务器不要太忙或者太闲,需要负载均衡地调用每个服务器,提升网络健壮性。
常见的负载均衡算法:
- 轮询,即按顺序循环调用
- 最小连接,优先选择连接数最少的服务器
- 散列:根据IP地址选择要转发的服务器,可以保证同一用户连接到相同服务器
1.2.5 服务注册/发现、注册中心
A服务调用B服务,但是不知道B服务在哪个服务器,也不知道哪个服务器可用,哪个服务已经下线,解决这个问题可以引入注册中心,可以实时感受到其他服务状态。
1.2.6 配置中心
每个服务都有大量配置,每个服务可能部署到多台机器上,经常需要变更配置,可以让每个服务在服务中心获取自己的配置。配置中心用来集中管理微服务的配置信息。
1.2.7 服务熔断、服务降级
在微服务架构中,可能存在依赖关系,如果一个服务不可用时,有可能造成雪崩效应,要避免这种情况,需要有容错机制来保护服务。
服务熔断:首先设置服务的超时,当多次调用服务失败达到阈值时,开启短路保护机制,后来的请求不再调用这个服务,而是本地直接返回默认的数据。
服务降级:运维期间,当系统处于高峰期,系统资源紧张,让非核心业务降级运行。降级:某些业务不处理,或者简单处理(抛异常、返回NULL,调用Mock数据、调用FallBack处理逻辑)
1.2.8 API服务网关
API网关(API Gateway)抽象了每个服务都需要的公共功能,提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流流控,日志统计等丰富功能。
1.3 环境搭建
1.3.1 安装Linux虚拟机
下载安转VirtualBox,需要进入bios界面开启CPU虚拟化,一般是开启了的。安装后界面如下:
下载安装Vagrant,可以在VirtualBox上快速创建虚拟机,安装后需要重启,重启后在cmd命令行输入vagrant
如果显示相关信息,表明安装成功。在cmd命令行中输入Vagrant init centos/7
Vagrant官方镜像仓库做好了许多镜像,其中一个便是centos/7。回车后效果如下,并且会在当前目录下生成Vagrantfile。然后使用vagrant up
会自动在官方镜像仓库下载镜像,并将环境启动,以后可以使用这个命令启动。
可以看到已经有一个虚拟机在运行了。
先用ctrl+c停止,再使用命令vagrant ssh
连接正在运行的虚拟机,从上图可以看到默认创建了SSH连接,用户名是vagrant,这时候可以正常使用linux命令了。
虚拟机关机后,可以使用命令vagrant up
启动虚拟机,前提是在Vagrantfile目录下。
1.3.1.1 Linux虚拟机网络设置
按照上面的步骤安装好虚拟机后,默认网络配置是网络地址转换+端口转发,如下图所示,在虚拟机中安装的软件比如redis使用端口6379,需要和windows端口3333绑定,当客户端访问windows3333端口,就会将请求转发到虚拟机3306端口。这样的话,每次安装一个软件都需要配置端口映射,会很麻烦。
解决方法有两种:
- 修改虚拟机的网络配置文件,比较麻烦
- 在Vagrantfile(默认在C:\Users\你的用户名\)里修改
config.vm.network "private_network", ip: "192.168.33.10"
,去掉注释,并修改私有ip地址。在cmd命令行使用ipconfig找到VirtualBox,由于这里的IP地址为192.168.56.1,因此Vagrantfile里的IP地址改成192.168.56.XX就行,比如192.168.56.10。改完后重启虚拟机,使用vagrant reload
。
再打开cmd,使用ping 192.168.56.10
看是否能ping通虚拟机,相同的,使用虚拟机看是否能ping通主机。
1.3.2 安装Docker
已经安装好了虚拟机,下面在虚拟机中安装Docker,简单介绍一下Docker。Docker是虚拟化容器技术,基于镜像,可以秒级启动各种容器,每一个容器都是完整的运行环境,容器之间互相隔离。Docker官方镜像网站,Docker会去软件仓库里面下载镜像,下载后在本地基于镜像直接启动容器,某一容器出现问题不会影响其他容器。Docker官方文档,安装docker步骤如下:
- 使用官方文档中的命令卸载以前Docker
sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
- 设置Docker仓库
sudo yum install -y yum-utils
sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo
- 安装Docker
sudo yum install docker-ce docker-ce-cli containerd.io
- 启动Docker
sudo systemctl start docker
- 设置Docker开机自启动
sudo systemctl enable docker
最后使用docker -v检查版本,效果如下:
1.3.2.1 配置Docker国内镜像加速
注册阿里云,点击控制台,点击镜像加速服务,点击镜像加速器,选择centos下的命令,按顺序执行命令即可配置。
1.3.3 Docker安装MySQL
一直用sudo比较麻烦,可以切换到root用户,使用su
,密码为vagrant
。
- 从镜像仓库拉取
sudo docker pull mysql:5.7
//检查当前有哪些镜像
sudo docker images
- 创建实例并运行
docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
查看运行中的容器:docker ps
。
1.3.3.1 命令详细信息介绍
对于上面创建实例并运行的命令,可以借助上图来解释,docker容器挂载与端口映射。
- 第1行,run启动容器,端口映射,虚拟机3306端口映射到mysql3306端口,前面一个是linux端口号,后面一个是容器里mysql的端口号,起名为mysql,反斜杠表示换行
- 第2、3、4行称为目录挂载,将容器内部的文件夹映射到linux目录里边,这样避免了每次都要进到容器内部修改配置文件,查看日志文件等麻烦的操作。冒号左边是linux目录,冒号右边是容器内mysql的目录。第一个是日志文件,第二个是数据,相当于备份数据文件,第三个是配置文件,以后在linux中修改即可。
- 第5行,设置root密码为root
- 以后台方式运行,启动的哪个镜像
每次创建一个mysql容器,都包含了一个完整的mysql运行环境,mysql装在linux里,因此容器就是一个完整的linux。验证如下,首先进入mysql容器内部,进入/bin/bash,可以看到能够访问,并且里面也有完整的linux目录结构。
docker exec -it 容器id或者名字 /bin/bash
1.3.3.2 配置MySQL
在/mydata/mysql/conf/my.cnf里粘贴这些配置,如果没有my.cnf会自动创建,先vi my.cnf
再粘贴,这些步骤都是在root用户下执行的,如果有问题可以尝试切换到root用户。
[client]
default-character-set=utf8[mysql]
default-character-set=utf8[mysqld]
init_connect='SET collation_connection=utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
重启Docker中的MySQL,使配置文件生效,docker restart mysql
。
1.3.4 Docker安装Redis
先切换到root用户,再执行命令。
- 下载Redis镜像,不写版本号默认下载最新版,为了保持一致下载5.0.5
docker pull redis:5.0.5
- 创建实例并启动。这里有个坑,-d后面要加上版本号,否则默认latest,会重新到官网下载redis镜像,可以先删除容器
docker rm -f 容器id
,再删除镜像文件docker rmi -f redis
。
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.confdocker run -p 6379:6379 --name redis \
-v /mydata/redis/data:/data \
-v /mydata/redis/config/redis.conf:/etc/redis/redis.conf \
-d redis:5.0.5 redis-server /etc/redis/redis.conf
- 测试是否启动成功
docker exec -it redis redis-cli
- 持久化Redis数据,
vi redis.conf
,加入下面内容,使用aof持久化方式(现在似乎默认是aof)
appendonly=yes
1.3.4.1 Redis可视化客户端
为了方便,可以安装redis可视化客户端RedisDesktopManager。
1.3.4.2 Redis配置
要了解可以配置哪些参数,可以参考Redis官方文档。
1.3.4.3 设置MySQL和Redis容器自启动
docker update mysql --restart=always
docker update redis --restart=always
1.3.5 开发环境统一
1.3.5.1 Maven
下载安装好maven3.6.1,在conf目录下修改settings.xml,配置镜像源
<mirrors><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>https://maven.aliyun.com/repository/public</url></mirror>
</mirrors>
配置jdk1.8编译项目
<profiles><profile><id>jdk-1.8</id><activation><activeByDefault>true</activeByDefault><jdk>1.8</jdk></activation><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion></properties></profile>
</profiles>
1.3.5.2 Idea&VsCode
在开始界面的All settings设置里配置mvn。
在Plugins里安装常用的插件Lombok(2021内置不需要安装)和MyBatisX。
使用Idea开发后台微服务项目,前端的后台管理系统使用VsCode开发,下载并安装。安装好后再VsCode里安装常用插件(比较多):Auto Close Tag,Auto Renam Tag,Chinese(Simplified),ESLint,HTML CSS Support,HTML Snippets,JavaScript (ES6) code snippets,Live Server,open in browser,Vetur。
1.3.5.3 Git
使用Git进行版本控制,由于GitHub有时登录不上,因此选择码云Gitee。下载Git,下载慢可以使用Git镜像源,安装好后,右键Git Bash进行基本配置。
//配置用户名
git config --global user.name "zkp"
//配置邮箱
git config --global user.email "2512139262@qq.com"
使用SSH免密连接:
ssh-keygen -t rsa -C "2512139262@qq.com"
//查看秘钥内容
cat ~/.ssh/id_rsa.pub
将秘钥内容复制到码云里,点击设置——SSH公钥,进行添加。ssh -T git@Gitee.com
测试是否成功。
1.3.6 创建项目微服务
将商城的微服务划分为:商品服务,仓储服务,订单服务,优惠券服务,用户服务。
这些微服务共同点:
1、都要导入Spring Web和OpenFeign
2、规范,包名为:com.atguigu.gulimall.XXX(product、ware、order、coupon、member)
3、模块名为:gulimall-XXX
4、组织名为:com.atguigu.gulimall
1.3.6.1 从gitee初始化一个项目
首先创建仓库,设置名称,初始化,模板,分支类型。
创建好仓库后,复制仓库地址,我这里是gulimall,打开Idea,新建项目,选择从版本控制新建项目,输入仓库地址进行clone。
gulimall项目作为总项目,微服务模块在总项目里进行创建。
以商品服务为例,设置好Name,Group等后,导入Spring Web和OpenFeign。
都创建好后,可以打开Run DashBoard,在Idea2019后变成了Services。
接下来,将gulimall设置为总项目来聚合小项目,复制一个pom.xml到gulimall下,并修改内容如下:
修改ignore模板,有些文件不需要上传。
安装码云插件gitee,点击commit and push即可提交到本地仓库并提交到码云。
1.3.7 数据库设计
数据库ip:192.168.56.10
需要先安装PowerDesigner,安装好后可以打开pdm文件。
五个微服务对应五个数据库,每个数据库下有多个表,虽然有很多的表,但是表之间不建立外键,这是因为电商系统中数据量很大,做外键关联是非常耗费数据库性能的操作。假设有几十万或者几百万条数据,每次插入或者删除一条数据,都需要对外键进行检查,来保证数据一致性和完整性。
在PowerDesigner中可以方便地设计表和字段,也可以在Name中起名字作为注释。
导入到数据库中
1.4 后台管理系统
为了简化后台管理系统前后端的开发,使用码云上开源项目人人开源,使用renren-fast和renren-fast-vue。
克隆到本地:
git clone https://gitee.com/renrenio/renren-fast.git
git clone https://gitee.com/renrenio/renren-fast-vue.git
将renren-fast里的.git删除,再粘贴到gulimall中,在gulimall的pom.xml中将renren-fast添加为模块,再启动renren-fast可以看到成功运行。
1.5 前端项目
接下来用VSCode打开renren-fast-vue,并安装前端项目的运行环境
1.5.1 Node.js
下载安装Node.js,我们要用随同安装的NPM,它相当于Java的Maven,是管理Javascript的工具。安装好后使用node -v
查看版本。
配置NPM,使用淘宝镜像npm config set registry http://registry.npm.taobao.org/
。
安装好后,在VSCode的终端中输入npm install
,下载项目需要的依赖,因为package.json里描述了项目需要什么依赖。这里可能容易报错,需要排查一下哪里有问题,我是没有安装好python3.0及以上版本,并配置到环境变量中。再把项目文件夹下的package.json里面的node-sass4.9.0改成4.9.2,最后npm config set msvs_version 2022
设置为我已经安装的Visual Studio版本2022即可。
下载完需要的组件后运行项目:npm run dev
,需要后端项目已经启动。
1.6 逆向工程搭建&使用
克隆人人开源的代码生成器renren-generator,删除.git文件夹,添加到gulimall模块,修改application配置文件,不同模块对应不同数据库;修改generator.properties配置文件,包名模块名需要修改,后续方便直接把生成的代码粘贴到项目中。
启动项目,勾选所有表,生成代码。
在main文件夹里已经生成好了mapper和三层架构加实体类,直接将main文件夹复制到相应微服务项目即可。会发现许多包都没有导入或者创建,因此额外创建一个gulimall-commom,每个微服务公共的类,公共的依赖都放到这里。别的项目依赖这个项目,并在gulimall-common中添加公共依赖。
<dependency><groupId>com.atguigu.gulimall</groupId><artifactId>gulimall-common</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
最后添加模块的application.yml,配置数据源。
对其他模块也进行同样的操作,生成代码之前要修改代码生成器的generator.properties配置和连接数据库配置application.yml。注意coupon对应sms表,member对应ums表。在application.yml中用server:port可以编排端口
server:port:7000
1.6.1 整合MyBatis-plus
在gulimall-common里导入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version>
</dependency>
参照MyBatis-plus官方文档进行配置,包括导入驱动
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version>
</dependency>
配置数据源
配置MyBatis-plus,使用MapperScan,以及映射文件地址,使用classpath*表示除了自己的classpath,依赖的classpath也会扫描映射文件。
设置主键自增
测试一下插入数据,成功
测试查询操作,成功
1.7 微服务-注册中心、配置中心、网关
每一个微服务上线,都应该先将自己注册到注册中心,这样其他服务就能知道想要调用的服务是否可用。每个服务在配置中心拉取自己的配置。网关有分配,权限控制等功能。
我们不使用官网的SpringCloud,而是用SpringCloud Alibaba,只需要少量注解和少量配置,就能迅速搭建分布式应用系统。使用步骤可以参考SpringCloud Alibaba中文文档。
不使用官网的SpringCloud几大组件的原因是
- 1、部分组件停止维护和更新,例如eureka停止维护
- 2、部分环境搭建复杂,没有完善的可视化,需要二次开发和定制
- 3、配置复杂,难以上手,部分配置难以区分和合理应用
SpringCloud Alibaba的优势为:
- 1、性能强悍,经过考验,设计合理
- 2、可视化界面开发,搭建简单,学习成本低
最终我们的技术搭配方案为: - 1、SpringCloud Alibaba Nacos:注册中心
- 2、SpringCloud Alibaba Nacos:配置中心
- 3、SpringCloud Ribbon:负载均衡
- 4、SpirngCloud Feign:声明式HTTP客户端
- 5、SpringCloud Alibaba Sentinel:服务容错
- 6、SpringCloud GateWay:API网关
- 7、SpringCloud Sleuth:调用链监控
- 8、SpringCloud Alibaba Seata(原Fescar):分布式事务解决方案
1.8 SpringCloud Alibaba
这里修改和视频中SpringBoot和SpringCloud的版本一致,并添加了junit-jupiter-api依赖,能够成功运行,可以把junit-jupiter-api放到gulimall_common依赖中,因为其他微服务都要依赖gulimall_common,去掉scope。或者把Test重新导包!下面把SpringCloud Alibaba简称为SCA。
1.8.1 SCA Nacos 注册中心
具体可以参考nacos官方文档。
1、导入Nacos Discovery Starter
2、下载nacos server 1.1.3版本,运行bin文件夹下的start.up启动注册中心,默认端口是8848
3、每个微服务在注册中心选择注册中心的端口(spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
),服务名(spring.application.name=gulimall-coupon
)
4、在Application前面加上注解@EnableDiscoveryClient
完成上述步骤后,启动微服务,并在浏览器中打开127.0.0.1:8848/nacos
,登录的账号名密码默认都是nacos,可以看到服务列表里有gulimall-coupon。
Hello!
OpenFeign测试远程调用,以member微服务调用coupon微服务为例
1、member微服务引入openfeign依赖
2、coupon服务返回优惠券
@RequestMapping("/member/list")public R memberCoupons(){CouponEntity couponEntity = new CouponEntity();couponEntity.setCouponName("满100减10");return R.ok().put("coupon",Arrays.asList(couponEntity));}
3、member服务里编写一个接口,告诉openfeign这个接口需要调用远程服务,放在feign包下
4、在接口上加注解@FeignClient("gulimall-coupon")
,把要调用的方法的完整签名复制过来,注意路径要写全
@RequestMapping("、coupon/coupon/member/list")public R memberCoupons();
5、开启远程调用功能,在Application加上注解@EnableFeignClients(basePackages = "com.atguigu.gulimall.member.feign")
6、编写测试方法
@RequestMapping("/coupons")public R test(){MemberEntity memberEntity = new MemberEntity();memberEntity.setNickname("张三");R memberCoupons = couponFeignService.memberCoupons();return R.ok().put("member",memberEntity).put("coupons",memberCoupons.get("coupons"));}
调用成功!
1.8.2 SCA Nacos 配置中心
可以参考上面的SpringCloud Alibaba中文文档,使用步骤如下:
1、在gulimall-common引入Nacos Config Starter依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>
2、在src/main/resources/下创建bootstrap.properties
,这个配置文件会先于application.properties读取
spring.application.name=gulimall-couponspring.cloud.nacos.config.server-addr=127.0.0.1:8848
在不设置配置中心时,是这样获取配置文件的内容(user.name会获取当前用户的名字,因此使用coupon.user.name):
获取成功!
3、在Nacos配置中心创建配置,Data ID一般为微服务名.properties
可以看到name和age都和配置中心的一样。
如果是使用application.xml,如果需要修改配置文件,需要重新打包再上线,而用了配置中心可以直接在配置中心修改,再点击发布,可以看到idea中显示刷新了配置信息。
4、但是刷新页面没有修改信息,需要在controlle里加入注解:@RefreshScope
,重新运行后再重复上述修改,可以看到配置信息实时改变!
1.8.2.1 核心概念
- 命名空间
用来做配置隔离,默认是public(保留空间),默认新增的所有配置都在public命名空间下。开发配置,测试配置,生产配置需要隔离。可以创建命名空间:
在bootstrp.properties中加上spring.cloud.nacos.config.namespace=4f10e303-0d6a-4b4e-b157-fb77e8d38087
,后面的ID复制dev后面的一串字符。
如果微服务很多,配置文件也很多,可以设置每个微服务的命名空间。因此命名空间可以基于环境进行隔离,也可以基于微服务进行隔离。 - 配置集
所有配置的集合。 - 配置集ID
类似于配置文件的文件名,即Data ID。 - 配置分组
默认所有配置集都属于DEFAULT_GROUP,新建时可以设置GROUP,例如双十一使用一组配置 1111,618使用另外一组配置。需要在bootstrap.properties加上spring.cloud.nacos.config.group=1111
。
我们使用这样的方案:每个微服务创建自己的命名空间,每个命名空间根据配置文组区分环境,dev,test,prod。
1.8.2.2 同时加载多个配置集
每个微服务的一个环境中会有多个配置文件,方便维护,例如和数据源有关的放在一个配置文件,和框架有关的配置放在一个配置文件,在bootstrap.properties中注释掉spring.cloud.nacos.config.group=dev
,加上
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=dev
spring.cloud.nacos.config.ext-config[0].refresh=true
就可以加载数据源配置文件,其他配置文件类似。读取顺序:先读取配置中心的,配置中心没有再读取配置文件。
小插曲:完成上述配置后尝试访问数据库时报错,在url后加上?useSSL=false
就可以了。
小插曲:端口号8848被占用,首先net -ano | findstr 8848
查看哪些进程ID使用了端口号,最右侧为PID;再使用taskkill -PID 5812 -F
。
1.9 SpringCloud
1.9.1 Feign声明式远程调用
Feign式声明式远程HTTP客户端,发送的是HTTP,
使用步骤如下:
1、引入OpenFeign依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、使用见1.8.1
1.9.2 Gateway
网关作为流量的入口,把请求路由到各个服务,统一做鉴权、限流、日志输出等功能。官方文档。下面创建并测试API网关。
创建模块
添加到配置中心和注册中心,详细步骤见上一节。运行时报错,因为依赖了gulimall-common,而它依赖了mybatis,因此可以排除与数据库有关的配置。设置端口为88。
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
在application.yml写配置
当输入http://localhost:88/?url=qq就能跳转到qq页面。
2 前端基础
前后端技术栈类比,Javascript也有新特性ES 6,7,8。
2.1 ES6
ECMAScript6.0,是JavaScript语言的标准,每年一个新版本,从ES6开始以年号作为版本。ECMAScript是浏览器脚本语言的规范,JavaScript是规范的具体实现。
2.1.1 ES6新特性
新建一个项目es6,新建let.html文件,使用快捷键shift+!+enter
快速生成html。右键Open With Live Server,ctrl+s保存后可以实时看到变化。
1、let新特性
- let声明的变量有严格作用域,var声明的变量可能越狱
- var可以声明多次,let只能声明一次
- let不存在变量提升,var存在变量提升
使用const声明常量,一旦声明之后不允许改变。
2.1.2 解构表达式&字符串拓展
数组解构:
对象解构:可以为对象重命名,name:abc,取对象中的name属性,赋给abc变量。
字符串拓展:
字符串模板:使用反引号`,多行字符串;${}插值在字符串中插入变量和表达,或者调用函数。
2.1.3 函数优化
1、函数参数默认值
2、不定参数
用...变量名
来表示
3、箭头函数
有点类似Java的lambda表达式,从->变成=>。在类中,箭头函数不能使用this。
还可以将解构表达式与箭头函数结合:
2.1.4 对象优化
1、新增的API
声明对象简写:
对象的函数属性简写:
对象拓展运算符:
用于对象深拷贝,或者合并对象
2.1.5 map和reduce
2.1.6 Promise
JavaScript是单线程执行的,因此所有的网络操作,浏览器事件都必须是异步执行的。如果后面的函数需要前面的结果,就要用回调函数层层嵌套。
Promise可以封装异步操作,alt+shift+f
VSCode代码整理。需求:先查询用户,再根据用户查询课程ID,再根据课程ID查询成绩。没提取代码之前需要这样写:
let p = new Promise((resolve, reject) => {$.ajax({url: "mock/user.json",success: function (data) {console.log("查询用户成功:", data)resolve(data);},error: function (err) {reject(err);}});
});
p.then((obj) => {new Promise((resolve, reject) => {$.ajax({url: `mock/user_course_${obj.id}.json`,success: function (data) {console.log("查询用户课程成功:", data)resolve(data);},error: function (err) {reject(err);}});});
}).then((data) => {console / log("上一步的结果:", data)$.ajax({url: `mock/course_score_${obj.id}.json`,success: function (data) {console.log("查询课程得分成功:", data)resolve(data);},error: function (err) {reject(err);}});
})
提取之后代码如下:
function get(url, data) {return new Promise((resolve, reject) => {$.ajax({url: url,data: data,success: function (data) {resolve(data);},error: function (err) {reject(err);}});})
}get("mock/user.json").then((data) => {console.log("用户查询成功",data)return get(`mock/user_course_${data.id}.json`);}).then((data) => {console.log("课程查询成功",data)return get(`mock/corse_scoes_${data.id}.json`);}).then((data)=>{console.log("课程成绩查询成功",data)}).catch((err)=>{console.log("出现异常",err)})
2.1.7 模块化
模块化就是把代码拆分,方便重复利用,类似Java导包的概念,但是Javascript对应的是导入模块。模块功能主要有两个命令:export规定模块的对外接口,import导入其他模块提供的功能。
- export
export可以导入对象,变量,只有导出的才可以被其他js文件导入
const util = {sum(a, b) {return a + b;}
}
var name = "jack";
var age = 21;export{util,name,age}
或者直接在变量名之前使用export。
- import
单个导入或者多个导入
import util from "./hello.js"
import {name,age} from "./hello.js"
2.2 Vue
2.2.1 MVVM
数据和页面能同时变,只要有一方变了,另外一方能跟着变。新建文件夹,初始化,安装vue2:
npm init -y
npm install vue@2
1、声明式渲染
<div id="app"><h1>{{name}},hello,vue</h1>
</div>
<script>let vm = new Vue({el: "#app", //绑定元素data: { //封装数据name: "张三",num: 1},methods: { //封装方法cancel(){this.num -- ;}}});
</script>
2、双向绑定
3、事件处理
安装插件:
Vue 2 Snippets
安装浏览器插件:
vue-devtools
2.2.2 Vue指令
v-html表示将数据转换成html表示
v-text展示原来的文本
插值闪烁:直接用{{name}},会出现先显示{{name}},再显示值,因此最好用v-html或者v-text
v-bind:给属性绑定,有短横杠需要单引号括起来
例如
v-bind:class="{active:isActive,'text-danger':hasDanger}"
双向绑定:v-model
v-on:绑定事件,例如:v-on:click="cancel"
可以简写成@click="cancel"
事件冒泡:点小div会触发大div,v-on:click.stop="cancel"
,stop阻止父类冒泡
按键修饰符:监听按键,v-on:keyup.up="num++"
组合键:@click.ctrl="num=10"
v-for:遍历循环,v-for="(item,index) in items"
,遍历的时候加上:key
区分不同数据,加快渲染速度。例如,用index区分:
<li v-for="(num,index) in nums" :key="index"></li>
v-if:是否显示,如果不显示,标签会消失,可以和v-for联用,后于v-for执行
v-show:是否显示,如果不显示,只是style改变
v-else,v-else-if:和v-if联用
计算属性:在Vue对象中的属性,computed
可以动态计算
监听器:例如监听商品数量是否超过库存,使用watch
属性
过滤器:filters
属性
2.2.3 组件化
1、全局声明注册一个组件
注意要放到Vue对象前面
Vue.component("counter", {template: '<button v-on:click="count++">我被点击了{{count}}次</button>',data() {return {count: 1}}});
还可以注册局部组件,在Vue实例中在components中进行引用。
生命周期:在合适的时候钩子函数会被触发。
2.2.4 Vue模块化开发
搭建脚手架工程,win+r,cmd回车,打开Windows命令行窗口
1、全局安装webpack
npm install webpack -g
2、全局安装Vue脚手架
npm install -g @vue/cli
3、初始化Vue项目
在新建的文件夹中打开终端,输入:
vue init webpack vue-demo
如果出现node.js版本需要升级,先查看node版本,再去官网下载需要更新的版本,然后安装覆盖以前的node。
node -v
where node
前面三个直接回车,第四个选择上面的运行环境加编译环境,再回车
4、运行:
cd vue-demo
npm run dev
然后在浏览器中打开相应端口,这里因为有后端项目占用了8080,因此在8081打开:
界面如下:
主要在src下编写,单文件组件:xxx.vue。
2.2.5 整合ElementUI
1、安装
npm install element-ui
2、在mian.js中导入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
3、使用
Vue.use(ElementUI);
使用VSCode生成Vue模板:
文件-首选项-用户代码片段-新建全局代码片段,起名vue,回车后粘贴以下代码:
{"Print to console": {"prefix": "vue","body": ["<!-- $1 -->","<template>","<div class='$2'>$5</div>","</template>","","<script>","//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)","//例如:import 《组件名称》 from '《组件路径》';","","export default {","//import引入的组件需要注入到对象中才能使用","components: {},","data() {","//这里存放数据","return {","","};","},","//监听属性 类似于data概念","computed: {},","//监控data中的数据变化","watch: {},","//方法集合","methods: {","","},","//生命周期 - 创建完成(可以访问当前this实例)","created() {","","},","//生命周期 - 挂载完成(可以访问DOM元素)","mounted() {","","},","beforeCreate() {}, //生命周期 - 创建之前","beforeMount() {}, //生命周期 - 挂载之前","beforeUpdate() {}, //生命周期 - 更新之前","updated() {}, //生命周期 - 更新之后","beforeDestroy() {}, //生命周期 - 销毁之前","destroyed() {}, //生命周期 - 销毁完成","activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发","}","</script>","<style scoped>","$4","</style>"],"description": "生成vue模板"},"http-get请求": {"prefix": "httpget","body": ["this.\\$http({","url: this.\\$http.adornUrl(''),","method: 'get',","params: this.\\$http.adornParams({})","}).then(({ data }) => {","})"],"description": "httpGET请求"},"http-post请求": {"prefix": "httppost","body": ["this.\\$http({","url: this.\\$http.adornUrl(''),","method: 'post',","data: this.\\$http.adornData(data, false)","}).then(({ data }) => { });"],"description": "httpPOST请求"}
}
3 商品服务
3.1 三级分类
前端项目运行:
npm run dev
如果报错但是正常运行,可以注释掉eslint,把build下的webpack.base.conf.js这部分注释掉:
再重启项目即可。
配置网关路由和路径重写,在gulimall-gateway的application.yml中:
spring:cloud:gateway:routes:- id: test_routeuri: https://www.baidu.compredicates:- Query=url, baidu- id: qq_routeuri: https://www.qq.compredicates:- Query=url, qq- id: admin_routeuri: lb://renren-fastpredicates:- Path=/api/**filters:- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
跨域:浏览器对JavaScript施加的安全限制,浏览器不能执行其他网站的脚本。
同源策略:协议,域名,端口都需要一样,只要有一个不一样就会产生跨域。
跨域流程:
1、非简单请求(put,delete),先发送预检请求,options
2、响应允许跨域
3、发送真实数据
4、响应数据
为什么获取验证码不会跨域?
因为get是简单请求,简单请求包括(get,head,post),且值要是三种之一:
解决跨域:
1、使用nginx配置为同一域
2、配置当次请求允许跨域
选择第二个方案,并且复用,直接在gateway中配置filter加上响应头
网关统一配置跨域,renren-fast配置的跨域需要注释掉
@Configuration
public class GulimallCorsConfiguration {@Beanpublic CorsWebFilter corsWebFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration = new CorsConfiguration();//1、配置跨域corsConfiguration.addAllowedHeader("*");corsConfiguration.addAllowedMethod("*");corsConfiguration.addAllowedOrigin("*");corsConfiguration.setAllowCredentials(true);source.registerCorsConfiguration("/**",corsConfiguration);return new CorsWebFilter(source);}
}
配置路由时,如果都是路径断言,有优先级,要把更精确的路由放在前面。效果如下:
使用MyBatis-plus逻辑删除:可以参考官网
1、配置application.yml
mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xmlglobal-config:db-config:id-type: autologic-delete-value: 1logic-not-delete-value: 0
2、实体字段加上@TableLogic注解
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
拖拽节点实现修改
3.2 品牌管理
基本的增删改查使用逆向生成好的代码,无论是前后端。真正的核心业务逻辑,才会自己写。
品牌表对应数据库中的表pms_brand。
在系统管理——菜单管理中新增:
以前生成的代码中复制这两个文件,粘贴到前端项目的product目录下
可以把权限返回为true,这样才能增加和删除。
3.2.1 文件上传功能
普通上传,普通上传的分布式情况,云存储。
对象存储(Object Storage Service,OSS)。
打开阿里云开通OSS,一个项目创建一个Bucket,
普通上传:用户上传给服务器,服务器再上传给OSS
服务端签名后上传:1、用户向服务器请求上传Policy
2、服务器返回上传Policy
3、用户直接上传数据到OSS
阿里云可以验证防伪签名。
安装SDK:
<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.10.2</version>
</dependency>
创建子用户,添加权限。
可以通过上传文件流,也可以使用封装好的SpringCloud Alibaba-OSS进行对象存储。
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。String endpoint = "oss-cn-beijing.aliyuncs.com";// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。String accessKeyId = "LTAI5tSh95txzVw97xq3BKMh";String accessKeySecret = "ZvbB3Q9VhpfrJGPNszutwL5PAN5knh";// 填写Bucket名称,例如examplebucket。String bucketName = "zkp-gulimall";// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。String objectName = "exampledir/java.jpg";// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。String filePath= "E:\\c\\博客\\CSDN\\2022-5-13-jar包\\java.jpg";// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {InputStream inputStream = new FileInputStream(filePath);// 创建PutObject请求。ossClient.putObject(bucketName, objectName, inputStream);System.out.println("上传完成...");} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}
1、导入starter依赖,由于其他微服务也要使用到,因此导入到common的pom.xml中
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
2、配置文件中配置
spring:cloud:alicloud:access-key: LTAI5tSh95txzVw97xq3BKMhsecret-key: ZvbB3Q9VhpfrJGPNszutwL5PAN5knhoss:endpoint: oss-cn-beijing.aliyuncs.com
OSS获取服务端签名:
创建第三方服务模块gulimall-third-party,将之前的对象存储的依赖放到这里,并将common的依赖管理也放过来。
在nacos中新建oss.yml,并将对象存储的配置复制过来。排除数据库相关的依赖,否则还需要配置数据库。添加注解@EnableDiscoveryClient,占用30000端口。这里要修改Spring Cloud版本为Greenwich.SR3。
包括签名直传服务和上传回调服务。
使用@RestController注解,创建OssController,包含了@ResponseBody注解,将方法返回的对象写成json返回给浏览器。要注入OSS接口,不要写OSSClient实现类。效果如下:
配置网关,使得访问http://localhost:88/api/thirdparty/oss/policy
即可。
- id: third_party_routeuri: lb://gulimall-third-partypredicates:- Path=/api/thirdparty/**filters:- RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}
前后联调:
直接用写好的upload中的三个文件,放到components下,修改访问地址
有跨域问题,开启跨域
概览——基础设置——跨域访问,设置——创建规则
表单校验。服务器也要校验,因为如果绕过前端来提交数据,后端不校验会很危险。
3.2.2 JSR303
1、给需要校验的数据添加校验注解,javax.validation.constrains
2、给方法的形参加注解@Valid,告诉SpringMVC
3、紧跟校验bean后加BindingResult,就可以得到校验结果
规定统一返回[code,msg,data]。
自定义校验注解 @Pattern
@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是一个字母")
private String firstLetter;
对校验做统一的处理,使用@ControllerAdvice
@Slf4j
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {@ExceptionHandler(value = MethodArgumentNotValidException.class)public R handleValidException(MethodArgumentNotValidException e){log.error("数据校验出现问题{},异常类型{}",e.getMessage(),e.getClass());BindingResult bindingResult = e.getBindingResult();Map<String,String> errorMap = new HashMap<>();bindingResult.getFieldErrors().forEach((fieldError)->{errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());});return R.error(400,"数据校验出现问题").put("data",errorMap);}}
如果不能精确匹配,就来到最大的异常处理:
写业务时,如果有异常,放心大胆地抛出去,统一进行处理。前两位业务场景,最后三位表示错误码。创建枚举类BizCodeEnume放在common中。
public enum BizCodeEnume {UNKNOWN_EXCEPTION(10000,"系统未知异常"),VALID_EXCEPTION(10001,"参数格式校验失败");private int code;private String msg;BizCodeEnume(int code, String msg){this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}
}
分组校验,例如新增时品牌id是自增的,不需要携带,而修改时需要携带品牌id。
1、在校验注解上有属性groups,并且必须为接口的class。
2、在Controller上不使用@Valid注解,使用@Validated注解,可以指定校验分组。如果属性的注解不加分组将会不起作用,因此要加上分组。
自定义校验:
1、编写自定义的校验注解
在JSR303中校验注解必须拥有三个属性,message,groups,payload
还要创建配置文件ValidationMessages.properties
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {String message() default "{com.atguigu.common.valid.ListValue.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };int[] vals() default { };
}
com.atguigu.common.valid.ListValue.message=必须提交指定的值
如果中文乱码,先删除文件,然后修改File——Settings——File Encoding:
再重启项目。
2、编写自定义的校验器
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {private Set<Integer> set = new HashSet<>();@Overridepublic void initialize(ListValue constraintAnnotation) {int[] vals = constraintAnnotation.vals();for (int val : vals) {set.add(val);}}//判断是否校验成功/**** @param value 需要校验的值* @param context* @return*/@Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {return set.contains(value);}
}
3、关联自定义的校验器和自定义的校验注解,一个校验注解可以有多个校验器
@Constraint(validatedBy = { ListValueConstraintValidator.class })
更新状态时,如果提示品牌名必须提交,
3.3 SPU和SKU
标准化产品单元(Standard Product Unit,SPU),是商品信息聚合的最小单元。
库存量单位(Stock Keeping Unit,SKU),即库存进出计量的基本单位。
例如:Iphone X是一个SPU,但买到手的具体配置是一个SKU。
在gulimall_admin中执行sys_menus.sql,建立这些菜单:
接口文档地址:https://easydoc.xyz/#/s/78237135。
接下来编写平台属性——属性分组。
父子组件传递数据:
1、子组件给父组件传递数据使用事件机制,子组件给父组件发送一个数据,this.$emit(“事件名”,若干个需要携带的数据);
获取分类属性分组。
分组新增。
给children属性使用注解 @JsonInclude(JsonInclude.Include.NON_EMPTY),使得如果该属性为空,则不携带该数据。
修改回显分类。
MyBatis-Plus引入分页插件。
一个品牌对应多个分类。一个分类可以有多个品牌。多对多的关系,需要有中间表。如果有冗余数据,要确保数据修改的一致性,需要业务代码保证。加上@Transactional注解,并且要在配置类MyBatisConfig中开启事务@EnableTransactionManagement。
3.3 平台属性
VO(Value/View Object),值对象,接收页面传递的数据,封装对象。或者是将业务处理完的对象,封装成页面要用的数据。@Data会使lombok自动生成getter和setter。使用Spring的方法复制属性。
BeanUtils.copyProperties(attr,attrEntity);
使用注解@RequestBody将请求体中的数据封装成指定类。
注意实体类要标注@Data,自动生成setter和getter,否则没有getter和setter不能转换成指定类型。
3.4 新增商品
执行架构篇的gulimall_pms.sql,插入一些数据。
遇到没有发送请求的情况,评论中做法:
1、执行命令
npm install --save pubsub-js
2、在src的main.js中加上
import PubSub from 'pubsub-js'Vue.prototype.PubSub = PubSub
把gulimall-member注册到注册中心,注解开启服务注册与发现。网关配置路由关系。将前端的modules下的member,ware等文件夹拷到项目modules中。
json格式化工具,粘贴后点击JSON转Java实体类,价格使用Big Decimal,不用double,有的int也需要该成BigDecimal。这样运算不会丢失精度。getter和setter可以删除,并使用注解@Data。
openfeign远程调用。在product中吧所有需要远程调用的接口都写在feign包下。
1、写成接口
2、注解@FeignClient(“gulimall-coupon”)声明调用哪个远程服务
3、在启动类上加注解@EnableFeignClients(basePackages = “com.atguigu.gulimall.product.feign”)
4、接收和发送都需要,因此在common中建立TO
5、在接口中加上注解@PostMapping(“/coupon/spubounds/save”)
6、接口形参使用@RequestBody
修改完后重启服务,为了批量重启,并且设置每个微服务占用内存,首先Edit Configurations
创建Compound
将服务加入进来
每个服务可以点击Edit修改
在VM options中设置-Xmx100m
,设置最大占用100M就够用。
最后给Compound起名gulimall
以后直接选中gulimall,点击右边重启就行
可以在product服务中设置断点,然后以debug模式重启,由于设置了事务,而MySQL默认事务隔离级别为可重复读,为了能够实时看到表中数据变化,可以设置隔离级别,当前窗口就能读到未提交的数据。
set session transaction isolation level read uncommitted;
断点调试时如果前面加了一行,断点会失效,可以把要加的放在后面。
对于空图片路径的需要过滤掉。filter返回true就是需要保留。
此外,满0件打0折,满0元减0元是无用信息,需要剔除。BigDecimal的比较使用方法compareTo
skuReductionTo.getFullPrice().compareTo(new BigDecimal("0"))==1
只要有一个就保存,更深入地在具体保存时再选择性保存存在的信息。
过滤会员价格,如果是0过滤。
还有其他更多细节在高级篇完善。
SPU检索
返回到前端的时间需要格式化,否则是下面的样子:
在application.yml中添加:
spring:jackson:date-format: yyyy-MM-dd HH:mm:ss
如果设置后不对可以加上时区:time-zone: GMT+8
。
商品管理
这里检索sku信息。
ge表示大于等于,le表示小于等于。
仓库管理
开启注解:@EnableTransactionManagement
@MapperScan(“com.atguigu.gulimall.ware.dao”)
配置网关
采购单
订单状态是枚举类,写到common中
完成采购
分页有问题,需要做MyBatis的配置,新建配置类,内容和product中的一样。
远程查询sku的名字
前端页面出错,先在src/router/index.js里面的mainRoutes——>children加上:
{ path: '/product-attrupdate', component: _import('modules/product/attrupdate'), name: 'attr-update', meta: { title: '规格维护', isTab: true } }
再把spuinfo.vue的catalogId改成catelogId即可。
获取Spu规格
4 分布式基础篇总结
4.1 分布式基础概念
- 微服务
- 注册中心
- 配置中心
- 远程调用
- Feign
- 网关
4.2 基础开发
- SpingBoot2.0
- SpringCloud
- MyBatis-Plus
- Vue组件化
- 阿里云对象存储
4.3 环境
- Vagrant
- Linux
- Docker
- MySQL
- Redis
- 逆向工程&人人开源
4.4 开发规范
- 数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理
- 枚举状态、业务状态码、VO与TO与PO区分、逻辑删除
- Lombok:@Data @Slf4j
Java实战项目——《谷粒商城》分布式基础篇相关推荐
- 谷粒商城三阶段课件_谷粒商城分布式基础篇一
微服务架构图 微服务划分图 搭建虚拟开发环境 1.下载安装VirtualBox 下载安装Vagrant 2.安装好后,创建一个存放vagrant box的目录,方便日后统一管理,比如叫做../cent ...
- 谷粒商城--分布式基础篇2
谷粒商城–分布式基础篇2(前端基础) 目录 谷粒商城--分布式基础篇2(前端基础) 5 前端 5.1 ES6 5.1.1 简介 5.1.2 什么是ECMAStript 5.1.3 ES6 新特性 5. ...
- 谷粒商城-分布式基础篇-环境搭建
1.写在前面 既个人博客系统和Java虚拟机学习后,深感技术点过于零散,于是照着尚硅谷教程写了谷粒商城这个项目.谷粒商城是一个完整的大型分布式架构电商平台,这个项目将我目前学到的知识点,以及还未学到的 ...
- 谷粒商城分布式基础篇1-个人版
基础篇 1 项目简介 1.1 项目背景 1.2 电商模式 市面上有5种常见的电商模式 B2B.B2C.C2B.C2C.O2O 1.2.1 B2B 模式 B2B(Business to Business ...
- 谷粒商城-分布式基础篇2
目录 1 三级分类 1.1 sql脚本 1.2 查出所有分类以及子分类 1.3 配置网关路由与路径重写 1.4 树形展示三级分类数据 1.5 删除数据 1.6 新增分类 1.7 修改分类 1.8 拖拽 ...
- 谷粒商城-分布式高级篇[商城业务-检索服务]
谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...
- 谷粒商城分布式高级篇(中)
谷粒商城分布式基础篇 谷粒商城分布式高级篇(上) 谷粒商城分布式高级篇(中) 谷粒商城分布式高级篇(下) 文章目录 商城业务 异步 异步复习 线程池详解 CompletableFuture Compl ...
- 谷粒商城-分布式高级篇【业务编写】
谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...
- 谷粒商城-分布式高级篇[商城业务-秒杀服务]
谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...
- 谷粒商城分布式高级篇
ElasticSearch 商品发布代码 es索引的设计 (1)方便检索{ skuId:1 spuId:1 skuTitle:华为xx price:9988 saleCount:99 attrs:[ ...
最新文章
- 斯坦福大学李飞飞团队图像分类课程笔记
- ​不唯SCI,博士生要怎么培养?当我们反对SCI至上时,我们到底在反对什么?...
- u-boot之怎么实现分区
- UA MATH636 信息论6 微分熵
- git remote add Mycat https://github.com/MyCATApache/Mycat-Server.git
- 抽象类,接口都与继承有关
- Cryengine 3新的全局光照算法简介
- 苏州科技学院计算机组成原理考试,苏州科技学院计算机组成原理复习提纲.doc...
- Android 博客园客户端 (四) 基本功能完成(博客列表和内容、新闻列表和内容、推荐博主)...
- android 切换主题介绍一
- [2019杭电多校第五场][hdu6628]permutation 1
- 处理器管理与进程调度
- Java自定义生成PDF报告
- android viewholder模式,Android ViewHolder模式
- javaw java_我可以找出java程序是使用java还是javaw启动的
- 王巧乐菇凉的360图书馆--记录大量web日志分析的内容,非常好
- 202.Wex5开发环境的安装与基本使用 2019.08.29
- 4.2.4 图像色调处理
- 计算机一级ppt演示文稿第5套,PPT | 操作题第 13 套
- Carla在Windows上的安装与运行