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模式的电商平台。

  1. B2B模式(Business to Business),商家与商家建立的商业关系,如阿里巴巴
  2. B2C模式(Business to Consumer),供应商直接把商品卖给用户,也就是通常说的商业零售,如:苏宁易购,京东,天猫,小米商城
  3. C2B模式(Consumer to Business),先有消费者需求后有商家生产
  4. C2C模式(Consumer to Consumer),客户之间把东西放到网上卖,如淘宝,闲鱼
  5. O2O模式(Online to Offline),线下商务与互联网结合,互联网成为线下交易的平台,线上快速支付,线下享受服务,如饿了么,美团,淘票票,京东到家

1.1.3 项目技术&特色

  1. 前后端分离开发,开发基于vue的后台管理系统
  2. SpringCloud全新的解决方案
  3. 应用监控、限流、网关、熔断降级等分布式方案
  4. 分布式事务,分布式锁等分布式系统的难点
  5. 分析高并发场景的编码方式,线程池,异步编排等使用
  6. 压力测试与性能优化
  7. 各种集群技术的区别及使用
  8. CI/CD使用

1.1.4 项目前置要求

  1. 熟悉SpringBoot及常见解决方案
  2. 了解SpringCloud
  3. 熟悉git,maven
  4. 熟悉redis,linux,docker基本操作
  5. 了解html,css,javascript,vue
  6. 熟练使用idea开发项目

1.2 分布式基础概念

1.2.1 微服务

  简而言之,拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。

1.2.2 集群、分布式、节点

  集群是物理形态,分布式是工作方式。分布式中的每一个节点,都可以做集群,而集群并不一定是分布式。节点:集群中的一个服务器。

1.2.3 远程调用

  在分布式系统中,每个服务可能存在不同的主机,但服务之间不可避免地需要相互调用,称为远程调用。SpringCloud使用HTTP+JSON的方式完成远程调用。

1.2.4 负载均衡

  分布式系统中,A服务需要调用B服务,而B服务在多台服务器运行,为了使每个服务器不要太忙或者太闲,需要负载均衡地调用每个服务器,提升网络健壮性。
  常见的负载均衡算法:

  1. 轮询,即按顺序循环调用
  2. 最小连接,优先选择连接数最少的服务器
  3. 散列:根据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端口。这样的话,每次安装一个软件都需要配置端口映射,会很麻烦。


  解决方法有两种:

  1. 修改虚拟机的网络配置文件,比较麻烦
  2. 在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步骤如下:

  1. 使用官方文档中的命令卸载以前Docker
sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
  1. 设置Docker仓库
sudo yum install -y yum-utils
sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo
  1. 安装Docker
sudo yum install docker-ce docker-ce-cli containerd.io
  1. 启动Docker
sudo systemctl start docker
  1. 设置Docker开机自启动
sudo systemctl enable docker

  最后使用docker -v检查版本,效果如下:

1.3.2.1 配置Docker国内镜像加速

  注册阿里云,点击控制台,点击镜像加速服务,点击镜像加速器,选择centos下的命令,按顺序执行命令即可配置。

1.3.3 Docker安装MySQL

  一直用sudo比较麻烦,可以切换到root用户,使用su,密码为vagrant

  1. 从镜像仓库拉取
sudo docker pull mysql:5.7
//检查当前有哪些镜像
sudo docker images
  1. 创建实例并运行
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用户,再执行命令。

  1. 下载Redis镜像,不写版本号默认下载最新版,为了保持一致下载5.0.5
docker pull redis:5.0.5
  1. 创建实例并启动。这里有个坑,-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
  1. 测试是否启动成功
docker exec -it redis redis-cli

  1. 持久化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+fVSCode代码整理。需求:先查询用户,再根据用户查询课程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. 谷粒商城三阶段课件_谷粒商城分布式基础篇一

    微服务架构图 微服务划分图 搭建虚拟开发环境 1.下载安装VirtualBox 下载安装Vagrant 2.安装好后,创建一个存放vagrant box的目录,方便日后统一管理,比如叫做../cent ...

  2. 谷粒商城--分布式基础篇2

    谷粒商城–分布式基础篇2(前端基础) 目录 谷粒商城--分布式基础篇2(前端基础) 5 前端 5.1 ES6 5.1.1 简介 5.1.2 什么是ECMAStript 5.1.3 ES6 新特性 5. ...

  3. 谷粒商城-分布式基础篇-环境搭建

    1.写在前面 既个人博客系统和Java虚拟机学习后,深感技术点过于零散,于是照着尚硅谷教程写了谷粒商城这个项目.谷粒商城是一个完整的大型分布式架构电商平台,这个项目将我目前学到的知识点,以及还未学到的 ...

  4. 谷粒商城分布式基础篇1-个人版

    基础篇 1 项目简介 1.1 项目背景 1.2 电商模式 市面上有5种常见的电商模式 B2B.B2C.C2B.C2C.O2O 1.2.1 B2B 模式 B2B(Business to Business ...

  5. 谷粒商城-分布式基础篇2

    目录 1 三级分类 1.1 sql脚本 1.2 查出所有分类以及子分类 1.3 配置网关路由与路径重写 1.4 树形展示三级分类数据 1.5 删除数据 1.6 新增分类 1.7 修改分类 1.8 拖拽 ...

  6. 谷粒商城-分布式高级篇[商城业务-检索服务]

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  7. 谷粒商城分布式高级篇(中)

    谷粒商城分布式基础篇 谷粒商城分布式高级篇(上) 谷粒商城分布式高级篇(中) 谷粒商城分布式高级篇(下) 文章目录 商城业务 异步 异步复习 线程池详解 CompletableFuture Compl ...

  8. 谷粒商城-分布式高级篇【业务编写】

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  9. 谷粒商城-分布式高级篇[商城业务-秒杀服务]

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  10. 谷粒商城分布式高级篇

    ElasticSearch 商品发布代码 es索引的设计 (1)方便检索{ skuId:1 spuId:1 skuTitle:华为xx price:9988 saleCount:99 attrs:[ ...

最新文章

  1. 斯坦福大学李飞飞团队图像分类课程笔记
  2. ​不唯SCI,博士生要怎么培养?当我们反对SCI至上时,我们到底在反对什么?...
  3. u-boot之怎么实现分区
  4. UA MATH636 信息论6 微分熵
  5. git remote add Mycat https://github.com/MyCATApache/Mycat-Server.git
  6. 抽象类,接口都与继承有关
  7. Cryengine 3新的全局光照算法简介
  8. 苏州科技学院计算机组成原理考试,苏州科技学院计算机组成原理复习提纲.doc...
  9. Android 博客园客户端 (四) 基本功能完成(博客列表和内容、新闻列表和内容、推荐博主)...
  10. android 切换主题介绍一
  11. [2019杭电多校第五场][hdu6628]permutation 1
  12. 处理器管理与进程调度
  13. Java自定义生成PDF报告
  14. android viewholder模式,Android ViewHolder模式
  15. javaw java_我可以找出java程序是使用java还是javaw启动的
  16. 王巧乐菇凉的360图书馆--记录大量web日志分析的内容,非常好
  17. 202.Wex5开发环境的安装与基本使用 2019.08.29
  18. 4.2.4 图像色调处理
  19. 计算机一级ppt演示文稿第5套,PPT | 操作题第 13 套
  20. Carla在Windows上的安装与运行

热门文章

  1. [转]显卡帝揭秘3D游戏画质特效
  2. onenote桌面版的安装
  3. android+嵌入地图,Android 给app加入百度地图
  4. 基于BERT阅读理解框架的司法要素抽取方法
  5. 学习-Java包装类之Double类(9)
  6. tableau 日周月筛选器_自定义日期格式
  7. 有关java的几个日期类的转换
  8. Win11怎么把桌面文件路径改到D盘
  9. Java代码常见错误写法
  10. java判断今天是否是节假日_java 判断日期是否是节假日