谷粒商城-服务1

一、项目简介

1、项目架构图

在这里插入图片描述

2、项目划分图

在这里插入图片描述

3、环境搭建

mysql连接工具sqlyog:https://sqlyog.en.softonic.com/download

1)安装linux虚拟机

下载&安装Virtualbox https://www.virtualbox.org/wiki/Downloads
注意:要开启CPU虚拟化
在这里插入图片描述
在这里插入图片描述
下载完成后双击安装的适合的位置,打开后如图
在这里插入图片描述

2)安装vagrant

下载地址:https://www.vagrantup.com/downloads
vagrant的镜像库:https://app.vagrantup.com/boxes/search
在这里插入图片描述
下载后双击安装
安装完成后在dos下输入vagrant 出现如下图的命令提示表示vagrant安装成功
在这里插入图片描述
dos下执行命令
vagrant的镜像库:https://app.vagrantup.com/boxes/search
在这里插入图片描述
注意

1.这个命令在哪个目录下执行的,他的Vagrantfile就生成在哪里
2.centos/7 这个名称是通过镜像库中对应的名字而来。
vagrant init centos/7

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
并且文件加会多一个Vagrantfile文件
在这里插入图片描述
启动虚拟环境

vagrant up

在这里插入图片描述

启动后出现default folder:/cygdrive/c/User/… =>/vagrant。然后ctrl+c退出
在这里插入图片描述
输入 vagrant ssh 练起虚拟机

vagrant ssh

此时进入linux系统
在这里插入图片描述
我们想要给虚拟机一个固定的ip地址,windows和虚拟机可以互相ping通
查看windows下的虚拟机的ip地址,记住ip为56段的,linux下的设置ip时必须也是56段的。
在这里插入图片描述
打开VagrantFile文件
在这里插入图片描述
在这里插入图片描述
这样,我linux的IP地址为:192.168.56.10
注意:配置完成后一定要vagrant reload 重启一下,这个文件才会被从新加载,不执行这个重启电脑都没用
vagrantfile详解:https://blog.csdn.net/raoxiaoya/article/details/93638960

vagrant reload

在这里插入图片描述
输入vagrant init centos/7,即可初始化一个centos7系统。(注意这个命令在哪个目录下执行的,他的Vagrantfile就生成在哪里)
vagrant up启动虚拟机环境。
启动后出现default folder:/cygdrive/c/User/… =>/vagrant。然后ctrl+c退出
前面的页面中有ssh账号信息。vagrant ssh 就会连上虚拟机。可以使用exit退出

## 初始化vagrant
vagrant init centos/7
## 启动vagrant
vagrant up
## 重新加载vagrant 修改了Vagrantfile 后一定要重新加载,否则不会生效
vagrant reload
## 连接接linux 以上代码只需要执行一次,安装完成后就不需要再进行上面的错做了,只需要连接就可以
vagrant ssh

3) 安装docker

下载docker:https://www.docker.com/
安装文档:https://docs.docker.com/engine/install/centos/
docker镜像仓库:https://registry.hub.docker.com/

4)安装mysql

##mysql安装#########################################################start##
## 切换到root用户 当前root密码是:vagrant
su root 
## 下载5.7版本的mysql
sudo docker pull mysql:5.7
## 创建实例并启动
## -p 3306:3306  将容器的3306端口映射到主机的3306端口
## --name指定容器名字 -v目录挂载 -p指定端口映射  -e设置mysql参数 -d后台运行
## -v /mydata/mysql/log:/var/log/mysql \   -v表示目录挂载,将日志文件夹挂在到主机,: 前面的地址是linux下的地址,:号后面的mysql容器内部的地址,挂在以后在linux下的目录中就可以修改和获取到mysql内部的数据。
## -v /mydata/mysql/data:/var/lib/mysql \  -v表示目录挂载,将库文件夹挂在到主机,: 前面的地址是linux下的地址,:号后面的mysql容器内部的地址,挂在以后在linux下的目录中就可以修改和获取到mysql内部的数据。
## -v /mydata/mysql/conf:/etc/mysql \	   -v表示目录挂载,将配日志文件夹挂在到主机,: 前面的地址是linux下的地址,:号后面的mysql容器内部的地址,挂在以后在linux下的目录中就可以修改和获取到mysql内部的数据。	
## -e MYSQL_ROOT_PASSWORD=root \      初始化root用户的密码为root
## -d mysql:5.7			    -d表示后台运行  mysql:5.7 这个容器,注意docker本身是个容器,儿docker里面的每个镜像也是一个独立的容器,可以通过命令进入容器内部。
sudo 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正在运行中的镜像
sudo docker ps
## 进入到mysql 容器内部 
## -it 表示以交互模式进入
## mysql  是容器的名称,上面设置过的也可以是容器的id输入前几位就行,通过上面sudo docker ps可以看到mysql的id
## /bin/bash 进入容器的bash控制台
sudo docker exec -it mysql  /bin/bash
## 退出
exit
## 用id进入mysql容器
sudo docker exec -it dd6027481ad1  /bin/bash
exit
## 在linux下修改mysql的配置文件
cd /
cd mydata
cd mysql
cd conf
## 打开这个文件,没有则添加
vi my.conf
## 切换到插入状态
i
## 插入文件的信息
[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
## 推出插入状态
esc
## 保存并退出
:wq
## 不保存推出
:q!
## 重启mysql
docker restart mysql
## 设置docker启动后自动启动mysql
docker update mysql --restart=always
## 删除一个镜像
docker rmi mysql:latest
## 检查镜像列表中是否已经安装成功了mysql
sudo docker images
##mysql安装#########################################################end##

5)安装redis

redis的官方配置文档:https://raw.githubusercontent.com/redis/redis/6.0/redis.conf

##redis安装#########################################################start##
## 切换为root权限
su root
## 下载redis镜像 如果后面不写版本号,表示最新版本
sudo docker pull redis
# 在虚拟机中新建配置文件
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf
## 创建实例并启动
## redis-server /etc/redis/redis.conf 通过加载后面这个配置文件来启动redis  
## -v /mydata/redis/conf/redis.conf 如果没有redis.conf这个文件时,挂在时认为这是个文件夹,所以我们提前先在这个目录下新建这个文件
docker run -p 6379:6379 --name redis \
-v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf
## 设置持久化
打开配置文件
vim /mydata/redis/conf/redis.conf
# 插入下面内容 持久化
	appendonly yes
# 保存并退出
# 设置redis在docker启动时启动
docker update redis --restart=always

# 直接进去redis客户端。
docker exec -it redis redis-cli
exit
## 保存,重启并保存配置
docker restart redis



##redis安装#########################################################end####

6)开发环境

IDEA(后端

6.1 maven3.6 配置jdk和仓库

在settings中配置阿里云镜像,配置jdk1.8。这个基本都配置过
D:\Program Files\Java\apache-maven-3.6.0\conf\settings.xml

<!-- 阿里云仓库 -->
      <mirror>? 
????    <id>alimaven</id>? 
????    <name>aliyun maven</name>? 
????    <url>https://maven.aliyun.com/repository/public</url>? 
????    <mirrorOf>central</mirrorOf>? 
??    </mirror>
  </mirrors>
<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>

在这里插入图片描述

6.2 安装插件

IDEA安装插件lombok,mybatisX,gitee
IDEA设置里配置好maven

6.3 本地安装git

下载git:https://git-scm.com/
安装后在任意位置右键点击》Git GUI/bash Here。去bash

# 配置用户名
git config --global user.name "username"  //(名字,随意写)

# 配置邮箱
git config --global user.email "[email protected]" // 注册账号时使用的邮箱

# 配置ssh免密登录
ssh-keygen -t rsa -C "[email protected]"
三次回车后生成了密钥:公钥私钥
cat ~/.ssh/id_rsa.pub

也可以查看密钥
浏览器登录码云后,个人头像上点 设置--ssh公钥---随便填个标题---复制
# 登录码云:https://gitee.com/
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6MWhGXSKdRxr1mGPZysDrcwABMTrxc8Va2IWZyIMMRHH9Qn/wy3PN2I9144UUqg65W0CDE/thxbOdn78MygFFsIG4j0wdT9sdjmSfzQikLHFsJ02yr58V6J2zwXcW9AhIlaGr+XIlGKDUy5mXb4OF+6UMXM6HKF7rY9FYh9wL6bun9f1jV4Ydlxftb/xtV8oQXXNJbI6OoqkogPKBYcNdWzMbjJdmbq2bSQugGaPVnHEqAD74Qgkw1G7SIDTXnY55gBlFPVzjLWUu74OWFCx4pFHH6LRZOCLlMaJ9haTwT2DB/sFzOG/Js+cEExx/arJ2rvvdmTMwlv/T+6xhrMS3 553736044@qq.com

# 测试
ssh -T [email protected]
测试成功,就可以无密给码云推送仓库了

**vsCode(前端)**
下载vsCode用于前端管理系统。在vsCode里安装插件。
Auto Close Tag   开闭和标签用的
Auto Rename Tag
Chinese 中文包
ESlint  语法检查
HTML CSS Support
HTML Snippets
JavaScript ES6
Live Server
open in brower
Vetur
## 7)git安装

4、搭建微服务

4.1在码云上创建项目并clone下来

在码云新建仓库,仓库名gulimall,选择语言java,在.gitignore选中maven(就会忽略掉maven一些个人无需上传的配置文件),许可证选Apache-2.0,开发模型选生成/开发模型,开发时在dev分支,发布时在master分支,创建。
在这里插入图片描述

在IDEA中New–Project from version control–git–复制刚才项目的地址,如https://github.com/1046762075/mall

IDEA然后New Module–Spring Initializer–com.atguigu.gulimall , Artifact填 gulimall-product。Next—选择web(web开发),springcloud routing里选中openFeign(rpc调用)。

依次创建出以下服务

商品服务product
存储服务ware
订单服务order
优惠券服务coupon
用户服务member

共同点:

导入web和openFeign
group:com.atguigu.gulimall
Artifact:gulimall-XXX
每一个服务,包名com.atguigu.gulimall.XXX{product/order/ware/coupon/member}
模块名:gulimall-XXX

4.2 设置聚合服务

从微服务项目冲复制出一个pom.xml文件进行修改,最终修改成一下的结构

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.atguigu.gulimall</groupId>
    <artifactId>gulimall</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gulimall</name>
    <description>聚合服务</description>
    <packaging>pom</packaging>

    <modules>
        <module>gulimall-coupon</module>
        <module>gulimall-member</module>
        <module>gulimall-order</module>
        <module>gulimall-ware</module>
        <module>gulimall-product</module>
    </modules>

</project>

maven工具中添加一个聚合maven来控制整体
`在这里插入图片描述

最终效果
在这里插入图片描述

4.3 设置提交的过滤文件

设置聚合项目的的 .gitignore 文件

target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar

# 一下内容都是不提交到码云的数据
**/mvnw
**/mvnw.cmd

**/.mvn
**/target/

.idea
**/.gitignore

1)数据库初始化

https://www.fujieace.com/software/powerdesigner.html
安装powerDesigner软件。http://forspeed.onlinedown.net/down/powerdesigner1029.zip

5、搭建后台管理系统

https://gitee.com/renrenio
在这里插入图片描述
在这里插入图片描述
http://localhost:8080/renren-fast/
在这里插入图片描述

6、搭建vue环境

6.1 配置node.js

node历史版本:这里一定选择 10.16.3:https://nodejs.org/zh-cn/download/releases/
安装完成后打开dos
NPM是随同NodeJS一起安装的包管理工具。JavaScript-NPM类似于java-Maven。
命令行输入node -v 检查配置好了,配置npm的镜像仓库地址,再执

## 查看版本
node -v
## 设置淘宝镜像,下载依赖更快
npm config set registry http://registry.npm.taobao.org/

6.2 在vs中运行代码

## 下载package.json 中的依赖 是要去拉取依赖(package.json类似于pom.xml的dependency)
npm install
## 运行项目
npm run dev

正常启动后
在这里插入图片描述
P16 npm install报错问题
视频评论区没几个说对的,个人的各种分析写到了这里:https://blog.csdn.net/hancoder/article/details/113821646

7、使用ganerator构建微服务

通过开源项目:https://gitee.com/renrenio
通过git下载:renren-generator
找到项目
在这里插入图片描述
在这里插入图片描述

任意位置右键:
在这里插入图片描述

 git clone https://gitee.com/renrenio/renren-generator.git

在这里插入图片描述
在这里插入图片描述
打开文件夹,删除原有的git
在这里插入图片描述
把项目复制到我们的微服务路径下面
在这里插入图片描述
在ideal中添加到聚合服务中
在这里插入图片描述
对renren-generator中的controller模板进行修改
在这里插入图片描述
对人人-generator进行配置
在这里插入图片描述
在这里插入图片描述

启动renren-generator
调用:localhost 查看页面

在这里插入图片描述
把生成的文件放在项目中。
解压下载的文件,找到main文件夹,直接拷贝到微服务的src下面。
这样gennerator就构建构建完成了。

8、设置微服务的公共依赖

有一些公共的依赖,是所有的微服务都需要的,我们把它构建在一个公共的maven项目中,其他的微服务都要依赖这个公共的微服务。
创建公共的maven项目
在这里插入图片描述
在pom中依赖公共的依赖:如mysql,mybaitsplus,工具类等

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gulimall</artifactId>
        <groupId>com.atguigu.gulimall</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gulimall-common</artifactId>
    <description>每个微服务的公共依赖,bean,工具类等</description>
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>cn.itlym.shoulder</groupId>
            <artifactId>lombok</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.12</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.45</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.2-android</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.6</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

其他的微服务每个pom中都要依赖这个公共的微服务。

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

9、整合mybaitsplus

mybaitspllus的官方配置:https://mp.baomidou.com/guide/quick-start.html
第一步

/**
 * 整合mybaitsplus
 * 1.导入依赖
 *              <dependency>
 *             <groupId>mysql</groupId>
 *             <artifactId>mysql-connector-java</artifactId>
 *             <version>8.0.17</version>
 *         </dependency>
 * 2.配置
 *      1)导入数据库驱动
 *      2)在application.yml中配置数据源相关的信息
 * 3.配置mybaits-plus
 *      1)使用@mapperScan
 *      2) 告诉mybaits-plus,sql的映射文件在哪儿
 *
 */

classpath 和 classpath* 区别:

classpath 和 classpath* 区别:
classpath:只会到你的class路径中查找找文件;
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找

classpath*的使用:当项目中有多个classpath路径,并同时加载多个classpath路径下(此种情况多数不会遇到)的文件,*就发挥了作用,如果不加*,则表示仅仅加载第一个classpath路径

mbaitsplus配置

spring:
  datasource:
    username: root
    password: root
#    url: jdbc:mysql://192.168.56.10:3306/gulimall_pms # 可能中文会乱码
    url: jdbc:mysql://192.168.56.10:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.jdbc.Driver

#  classpath* 表示扫描自己和所有依赖的路径
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto # 设置主键进行自增

第二步:配置分页
在config目录下定义自己的分页配置类

package com.atguigu.gulimall.product.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * @Description:
 * @Created: with IntelliJ IDEA.
 * @author: 夏沫止水
 * @createTime: 2020-05-28 18:53
 **/

@Configuration
//开启事务功能
@EnableTransactionManagement
// 指定mapper接口的位置
@MapperScan("com.atguigu.gulimall.product.dao")
public class MyBatisConfig {
    
    

    //引入分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
    
    
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        paginationInterceptor.setOverflow(true);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInterceptor.setLimit(1000);
        return paginationInterceptor;
    }

}

10、SpringCloud Alibaba简介

10.1 结合 SpringCloud Alibaba 我们最终的技术搭配方案

SpringCloud Alibaba - Nacos :注册中心(服务发现/ 注册)
SpringCloud Alibaba - Nacos :配置中心(动态配置管理)
SpringCloud - Ribbon :负载均衡
SpringCloud - Feign :声明式 HTTP  客户端(调用远程服务)
SpringCloud Alibaba - Sentinel :服务容错(限流、降级、熔断)
SpringCloud - Gateway :API  网关(webflux  编程模式)
SpringCloud - Sleuth :调用链监控
SpringCloud Alibaba - Seata原 :原 Fescar

10.2 版本的选择

springalibaba地址:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
在这里插入图片描述
我当卡的springboot使用的是2.4.5版本,所以,要选择的springalibaba的版本为2021.x
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

10.3 服务发现与注册(nacos)

第一步:下载并启动nacos服务器
nocos下载地址:https://github.com/alibaba/nacos/releases
在这里插入图片描述
下载完成以后
启动nacos服务。右键管理员身份运行
在这里插入图片描述
如果无法启动,可能是由于你的jdk不是1.8版造成的,修改一下环境变量的JAVA_HOME换成1.8 或者编辑startup.cmd文件,手动设置成1.8
在这里插入图片描述
启动完成后访问服务器地址:http://localhost:8848/nacos/index.html 用户名密码都是:nacos
配置依赖,在common中

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies> 下面添加
```bash
<!--    alibabacloud 的依赖管理用于统一管理版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

第二步:在微服务中配置nacos服务器地址

```bash
 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

更多配置:
https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md#more
第三步:在微服务中开启服务的注册与发现功能
@EnableDiscoveryClient

 @SpringBootApplication
 @EnableDiscoveryClient
 public class ProviderApplication {
    
    

 	public static void main(String[] args) {
    
    
 		SpringApplication.run(ProviderApplication.class, args);
 	}

 	@RestController
 	class EchoController {
    
    
 		@GetMapping(value = "/echo/{string}")
 		public String echo(@PathVariable String string) {
    
    
 				return string;
 		}
 	}
 }

最终效果
在这里插入图片描述

10.4 Feign(远程调用)与注册中心

Feign传递对象的原理
调用远程的接口

    /**
     * 1、CouponFeignService.saveSpuBounds(spuBoundTo);
     *      1)、@RequestBody将这个对象转为json。
     *      2)、找到gulimall-coupon服务,给/coupon/spubounds/save发送请求。
     *          将上一步转的json放在请求体位置,发送请求;
     *      3)、对方服务收到请求。请求体里有json数据。
     *          (@RequestBody SpuBoundsEntity spuBounds);将请求体的json转为SpuBoundsEntity;
     * 只要json数据模型是兼容的。双方服务无需使用同一个to
     * @param spuBoundTo
     * @return
     */
    @PostMapping("/coupon/spubounds/save")
    R saveSpuBounds(@RequestBody SpuBoundTo spuBoundTo);

被调用的接口

    /**
     * 保存
     */
    @PostMapping("/save")
    //@RequiresPermissions("coupon:spubounds:save")
    public R save(@RequestBody SpuBoundsEntity spuBounds){
    
    
		spuBoundsService.save(spuBounds);

        return R.ok();
    }

第一步:在common中添加依赖 和 版本控制
在common中添加依赖
添加springcloud和springcloudalibaba的版本控制配置

<!--    alibabacloud 的依赖管理用于统一管理版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

添加openfeign的依赖

<!--微服务之间的通信组件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

第二步:把所有的远程接口都放在feign包下面
所有的远程接口都在feign包下编写

package com.atguigu.gulimall.member.feign;

import com.atguigu.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

// gulimall-coupon 服务的名称,要与注册中心里面的名称相对应
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
    
    
	// 获取会员的而优惠卷
	@RequestMapping("/coupon/coupon/member/list")
	public R membercoupons();
}

在这里插入图片描述
第三步:开启远程调用注解
@EnableFeignClients(basePackages=“com.atguigu.gulimall.member.feign”)

package com.atguigu.gulimall.member;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * 想要远程调用别的服务
 * 1)、引入open-feign
 * 2)、编写一个接口,告诉springCloud,把所有的远程接口都放在feign包下
 * 3)、申明接口的每个方法都是调用哪个远程服务的那个接口
 * 4)、开启远程调用注解
 */
//开启远程访问
@EnableFeignClients(basePackages="com.atguigu.gulimall.member.feign")
//注册到注册中心
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallMemberApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(GulimallMemberApplication.class, args);
	}

}

10.5 配置中心(nacos)

配置文档:https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md.
第一步:在common中添加配置中心的依赖

<!--配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

第二步:在微服务的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 元数据

spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

第三步:在应用的 /src/main/resources/application.properties 配置文件中添加测试数据

coupon.user.name=张三
coupon.user.age = 18

第四步:添加测试接口


    // 测试配置中心
    @Value("${coupon.user.name}")
    private String name;
    @Value("${coupon.user.age}")
    private Integer age;
    @RequestMapping("/test")
    public R test(){
    
    
        return R.ok().put("name",name).put("age",age);
    }

在这里插入图片描述
第五步:动态刷新配置
@RefreshScope

package com.atguigu.gulimall.coupon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;

/**
 * 如何使用配置中心管理配置
 * 1)、引入依赖
 *         <dependency>
 *             <groupId>com.alibaba.cloud</groupId>
 *             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
 *         </dependency>
 * 2)、创建一个bootstrap.properties
	 *# 应用的名字
	 * spring.application.name=gulimall-coupon
	 * # nacos地址
	 * spring.cloud.nacos.config.server-addr=127.0.0.1:8848
 * 3)、需要给配置中心默认添加一个数据集(Data Id)gulimall-coupon.properties 应用名.properties
 * 4)、动态获取并刷新配置
 * //配置刷新
	 * @RefreshScope
	 * @Value("${coupon.user.name}")
 */
//配置刷新
@RefreshScope
//nacos 注册到nacos服务器
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallCouponApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(GulimallCouponApplication.class, args);
	}

}

第六步:在nacos服务器中添加配置
注意:nacos中的配置优先级高于微服务本身
在这里插入图片描述
在这里插入图片描述

最佳实战

用命名空间指定微服务,用group指定发布版本。
设置:D:\workerspace_idea_2019\gulimall\gulimall-coupon\src\main\resources\bootstrap.properties

# 应用的名字
spring.application.name=gulimall-coupon
# nacos地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# 设置命名空间,注意时ID,命名空间指定微服务
spring.cloud.nacos.config.namespace=eb8f707c-224a-4268-b5f9-928334c52ea6
# 设置分组,指定开发测试状态Group
spring.cloud.nacos.config.group=dev
#dataID
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yaml
# 分组
spring.cloud.nacos.config.ext-config[0].group=dev
# nacos 配置文件更改后自动刷新
spring.cloud.nacos.config.ext-config[0].refresh=true
#dataID
spring.cloud.nacos.config.ext-config[1].data-id=mybaits.yml
# 分组
spring.cloud.nacos.config.ext-config[1].group=dev
# nacos 配置文件更改后自动刷新
spring.cloud.nacos.config.ext-config[1].refresh=true
#dataID
spring.cloud.nacos.config.ext-config[2].data-id=other.yml
# 分组
spring.cloud.nacos.config.ext-config[2].group=dev
# nacos 配置文件更改后自动刷新
spring.cloud.nacos.config.ext-config[2].refresh=true

在nacos服务其中,添加如下配置
在这里插入图片描述
在这里插入图片描述
配置集与组的结合应用
我们一个微服务的配置并不是在一个配置文件中的,如果有多个配置文件时我们应该这么做
比如,我把关于数据库的配置放在一个配置集中,把mybaits的放在一个配置集中,剩下的放在other中

在这里插入图片描述

10.6 网关gateway

官网:https://spring.io/projects/spring-cloud-gateway
官方文档:https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/
动态上下线:发送请求需要知道商品服务的地址,如果商品服务器有123服务器,1号掉线后,还得改,所以需要网关动态地管理,他能从注册中心中实时地感知某个服务上线还是下线。【先通过网关,网关路由到服务提供者】拦截:请求也要加上询问权限,看用户有没有权限访问这个请求,也需要网关。
所以我们使用spring cloud的gateway组件做网关功能。
网关是请求流量的入口,常用功能包括路由转发,权限校验,限流控制等。springcloud gateway取代了zuul网关。
三大核心概念:
Route(路由): The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates断言, and a collection of filters. A route is matched if the aggregate predicate is true.发一个请求给网关,网关要将请求路由到指定的服务。路由有id,目的地uri,断言的集合,匹配了断言就能到达指定位置,
Predicate(断言): This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters.就是java里的断言函数,匹配请求里的任何信息,包括请求头等。根据请求头路由哪个服务
Filter(过滤): These are instances of Spring Framework GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.过滤器请求和响应都可以被修改。
网关流程
在这里插入图片描述
客户端请求,会带着路由给网关,网关通过断言进行判断,通过后进行过滤,完成后跳转到指定的路由。
在这里插入图片描述
第一步:添加依赖

        <!--网关的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

第二步:把网关添加到注册与发现中,由于我们的nacos注册和配置在common中,所以添加common的依赖即可

        <dependency>
            <groupId>com.atguigu.gulimall</groupId>
            <artifactId>gulimall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

第三步:添加版本控制,版本控制不能从common中被依赖
注意:在common中一定不能有webstartor和mvcstartor启动器,会与gateway冲突造成gateway无法启动

<!--    alibabacloud 的依赖管理用于统一管理版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

第四步:去除datasouce的报错

package com.atguigu.gulimall.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;

@RefreshScope
@EnableDiscoveryClient
// 不加载数据库相关的配置
@SpringBootApplication(exclude = {
    
    DataSourceAutoConfiguration.class})
public class GulimallGatewayApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(GulimallGatewayApplication.class, args);
	}

}

10.6.1 gateway的使用

使用文档:https://spring.io/projects/spring-cloud-gateway#learn

spring:
  cloud:
    nacos:
      config:
        namespace: 04bb1f5e-af43-4684-81c3-439fd7baae56
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes: # 路由
        - id: test_route # 唯一id
          uri: https://www.baidu.com # 路由地址
          predicates: # 断言
            - Query=url,baidu # 参数rul中包含baidu就跳转

        - id: qq_route
          uri: https://www.qq.com
          predicates:
            - Query=url,qq

        - id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{
    
    segment}

        - id: third_party_route
          uri: lb://gulimall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?<segment>.*),/$\{
    
    segment}

        - id: member_route
          uri: lb://gulimall-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{
    
    segment}

        - id: ware_route
          uri: lb://gulimall-ware
          predicates:
            - Path=/api/ware/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{
    
    segment}

        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:  # 这段过滤器和验证码有关,api内容缓存了/renren-fast,还得注意/renren-fast也注册到nacos中
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{
    
    segment}
  application:
    name: gulimall-gateway

测试效果:
在这里插入图片描述

10.6.2 gateway的注意事项

  1. gateway的依赖不能与spring-web共存
  2. gateway的依赖愈能与spring-webmvc共存
  3. gateway的依赖不能与org.apache.tomcat.embed共存,有tomcat 依赖时会以tomcat服务器启动儿不能以netty启动

这三种情况的报错信息如下:
在这里插入图片描述
在这里插入图片描述
@RequestBody 获取请求体中的内容,只有post请求才会有请求体

在这里插入图片描述

11、renren-generator快速生成前后端代码

11.1 后端代码

通过renren-generator 快速生成前后端的代码
使用步骤

第一步:在appllication.yml 中配置数据库的连接`在这里插入代码片`
server:
  port: 8888

# mysql
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    #MySQL配置
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.56.10:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
#    url: jdbc:mysql://192.168.56.10:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
#    url: jdbc:mysql://192.168.56.10:3306/gulimall_ums?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
#    url: jdbc:mysql://192.168.56.10:3306/gulimall_oms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
#    url: jdbc:mysql://192.168.56.10:3306/gulimall_wms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root
    #oracle配置
    #    driverClassName: oracle.jdbc.OracleDriver
    #    url: jdbc:oracle:thin:@47.100.206.162:1521:xe
    #    username: renren
    #    password: 123456
    #SQLServer配置
    #    driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
    #    url: jdbc:sqlserver://192.168.10.10:1433;DatabaseName=renren_fast
    #    username: sa
    #    password: 123456
    #PostgreSQL配置
  #    driverClassName: org.postgresql.Driver
  #    url: jdbc:postgresql://192.168.10.10:5432/renren_fast
  #    username: postgres
  #    password: 123456



  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  resources:
    static-locations: classpath:/static/,classpath:/views/

#mongodb:
#  host: localhost
#  port: 27017
#  auth: false #是否使用密码验证
#  username: tincery
#  password: renren
#  source: 123456
#  database: test

mybatis-plus:
  mapperLocations: classpath:mapper/**/*.xml


pagehelper:
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql


#指定数据库,可选值有【mysql、oracle、sqlserver、postgresql、mongodb】
renren:
  database: mysql


第二步:在generator.properties 中配置 主要路径,包名, module名称  以及表前缀
#代码生成器,配置信息

mainPath=com.atguigu
#包名
package=com.atguigu.gulimall
# 商品服务
moduleName=product
# 优惠服务
#moduleName=coupon
#用户服务
#moduleName=member
# 订单服务
#moduleName=order
# 存储服务
#moduleName=ware
#作者
author=maruis
#Email
email=[email protected]
#表前缀(类名不会包含表前缀)
tablePrefix=pms_
#tablePrefix=sms_
#tablePrefix=ums_
#tablePrefix=oms_
#tablePrefix=wms_

#类型转换,配置信息
tinyint=Integer
smallint=Integer
mediumint=Integer
int=Integer
integer=Integer
bigint=Long
float=Float
double=Double
decimal=BigDecimal
bit=Boolean

char=String
varchar=String
tinytext=String
text=String
mediumtext=String
longtext=String


date=Date
datetime=Date
timestamp=Date

NUMBER=Integer
INT=Integer
INTEGER=Integer
BINARY_INTEGER=Integer
LONG=String
FLOAT=Float
BINARY_FLOAT=Float
DOUBLE=Double
BINARY_DOUBLE=Double
DECIMAL=BigDecimal
CHAR=String
VARCHAR=String
VARCHAR2=String
NVARCHAR=String
NVARCHAR2=String
CLOB=String
BLOB=String
DATE=Date
DATETIME=Date
TIMESTAMP=Date
TIMESTAMP(6)=Date

int8=Long
int4=Integer
int2=Integer
numeric=BigDecimal

nvarchar=String

在这里插入图片描述

第三步:运行项目,把生成文件后解压放在项目适当位置即可

11.2 前端代码

位置:E:\谷粒商城\genery\prodect\main\resources\src\views\modules

在这里插入图片描述
根据不同的表,生成的前端的代码,把代码拷贝到相应的位置
在这里插入图片描述

11.3 前端按钮的权限控制

在这里插入图片描述
暂时去除掉权限
在这里插入图片描述

11.4 由于语法检查太严格vscode报错的解决

在这里插入图片描述

12、微服务的文件上传

由于有负载均衡,需要把文件放在一个统一的地方,有两种方式

1.自己搭建服务器:如FastDFS 或 vs ftpd
特点:搭建复杂,维护成本高,前期费用高
2.云存储,如阿里云对象存储,七牛云存储
特点:即开即用,无需维护,按批收费

使用案例云对象存储使用文档:
https://help.aliyun.com/document_detail/31925.html?spm=a2c4g.11174283.6.1731.e0547da2smGWE2

最佳实战:我们把文件存储到云服务器上,上传文件通过前端访问我们自己的服务,拿到云服务器的令牌,前端带着令牌直接把文件上传到云服务器,这样可以很好的解决我们服务器对于文件存储的压力。
原理:
在这里插入图片描述

第一步:在阿里云上开通,对象存储oss ,并创建bucket

在这里插入图片描述

第二步:在阿里云上对oss进行跨域设置,否则无法上传,在概览》基础设置 中找到 跨域访问 进行设置

在这里插入图片描述
第三步:在阿里云服务器通过子账号设置令牌

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第四步:在java后台编写接口用于给客户端返回令牌
我们把所有的第三方的服务单独的放在一个微服务中
1)pom中添加依赖

        <!--阿里云oss对象存储-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alicloud-oss</artifactId>
        </dependency>

2)配置文件中添加配置

spring:
  cloud:
    alicloud:
      access-key: "LTAI5tLZWW3zqJDyerm"
      secret-key: "D8Vqn9nMQcuZFYBeOp"
      oss:
        endpoint: "oss-cn-beijing.aliyuncs.com"
      bucket: "gulimall-2010" # 自定义的
3)写一个接口用于生成令牌
package com.atguigu.gulimall.thirdparty.controller;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.atguigu.common.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description:
 * @Created: with IntelliJ IDEA.
 * @createTime: 2020-05-27 15:56
 **/

@RestController
public class OssController {
    
    

    @Autowired
    OSS ossClient;
    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessKeyId;
    @Value("${spring.cloud.alicloud.secret-key}")
    private String accessKeySecret;
    @Value("${spring.cloud.alicloud.bucket}")
    private String bucket = "gulimall-20210530";

    /**
     *    * 获取签名令牌
     */
    @RequestMapping("/oss/policy")
    public R policy() {
    
    
        //https://gulimall-clouds.oss-cn-beijing.aliyuncs.com/iqiyi.png
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
        //String callbackUrl = "http://88.88.88.88:8888";

        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format ; // 用户上传文件时指定的前缀。

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        Map<String, String> respMap = null;
        try {
    
    
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessKeyId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));

        } catch (Exception e) {
    
    
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        } finally {
    
    
            ossClient.shutdown();
        }
        return R.ok().put("data",respMap);
    }


}


在这里插入图片描述

第五步:前台带这令牌上传文件请求

a) 添加element-ui 上传组件

:before-upload 上传文件之前调用的方法,在这个方法中获取后台服务的令牌
:data=“dataObj” 包含令牌中的所有数据
:on-success=“handleUploadSuccess” 上传成功后的回调

<el-upload
      action="http://gulimall-20210530.oss-cn-beijing.aliyuncs.com"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showFileList"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="fileList[0].url" alt="">
    </el-dialog>
data() {
    
    
      return {
    
    
        dataObj: {
    
    
          policy: '',
          signature: '',
          key: '',
          ossaccessKeyId: '',
          dir: '',
          host: '',
          // callback:'',
        },
        dialogVisible: false
      };
    },
 beforeUpload(file) {
    
    
        let _self = this;
        return new Promise((resolve, reject) => {
    
    
          policy().then(response => {
    
    
            _self.dataObj.policy = response.data.policy;
            _self.dataObj.signature = response.data.signature;
            _self.dataObj.ossaccessKeyId = response.data.accessid;
            _self.dataObj.key = response.data.dir + '/'+getUUID()+'_${filename}';
            _self.dataObj.dir = response.data.dir;
            _self.dataObj.host = response.data.host;
            console.log("data-->",response.data)
            resolve(true)
          }).catch(err => {
    
    
            reject(false)
          })
        })
      },

在这个方法中,把上传后的文件路径保存在了fileList 了,前台保存时,调用我们自己的服务保存到后台。

 handleUploadSuccess(res, file) {
    
    
        console.log("上传成功...")
        this.showFileList = true;
        this.fileList.pop();
        this.fileList.push({
    
    name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
        this.emitInput(this.fileList[0].url);
      }
 <single-upload v-model="dataForm.logo"></single-upload>

13、后台数据校验JSR303

@NotEmpty 用在集合类上面
加了@NotEmpty的String类、Collection、Map、数组,是不能为null或者长度为0的(String Collection Map的isEmpty()方法)
@NotBlank只用于String,不能为null且trim()之后size>0
@NotNull:不能为null,但可以为empty,没有Size的约束

java规范体验第303号

  • 3、JSR303
  • 1)、给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示
  • 2)、开启校验功能@Valid
  •  效果:校验错误以后会有默认的响应;
    
  • 3)、给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果
  • 4)、分组校验(多场景的复杂校验)
  •     1)、	@NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
    
  •      给校验注解标注什么情况需要进行校验
    
  •     2)、@Validated({AddGroup.class})
    
  •     3)、默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
    
  • 5)、自定义校验
  •  1)、编写一个自定义的校验注解
    
  •  2)、编写一个自定义的校验器 ConstraintValidator
    
  •  3)、关联自定义的校验器和自定义的校验注解
    
  •  @Documented
    
  • @Constraint(validatedBy = { ListValueConstraintValidator.class【可以指定多个不同的校验器,适配不同类型的校验】 })
  • @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
  • @Retention(RUNTIME)
  • public @interface ListValue {

第一步:给bean加上javax.validation.constraints下的校验注解

	@NotBlank(message = "品牌名不能为空")
	private String name;
	/**
	 * 检索首字母,
	 * 利用正则表达式进行验证
	 */
	@Pattern(regexp = "/^a-zA-Z$/",message = "首字母必须是a到z或者A到Z的字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@Min(value = 0,message = "排序必须大于0")
	private Integer sort;

第二步:开启校验 @Validated

   /**
     * 保存
     */
    @RequestMapping("/save")
    //@RequiresPermissions("product:brand:save")
    public R save(@Validated @RequestBody BrandEntity brand, BindingResult bindingResult){
    
    
       // 在校验对象,紧跟着一个参数bindingResult,用于获取校验的信息
        if(bindingResult.hasErrors()){
    
    
            Map<String,String>map = new HashMap<>();
            bindingResult.getFieldErrors().forEach((item)->{
    
    
                // 校验的错误信息
                String message = item.getDefaultMessage();
                // 校验的字段名称
                String field = item.getField();
                map.put(field,message);
            });
            return R.error(400,"提交的数据不合法").put("data",map);

        }else{
    
    
            brandService.save(brand);
            return R.ok();
        }
    }

第三步:分组校验
1)、 @NotBlank(message = “品牌名必须提交”,groups = {AddGroup.class,UpdateGroup.class})

  •      给校验注解标注什么情况需要进行校验
    
  •     2)、@Validated({AddGroup.class})
    
  •     3)、默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
    
	/**
	 * 品牌id
	 */
	@NotNull(message = "修改是必须指定ID",groups = {
    
    UpdateGroup.class})
	@Null(message = "添加是不需要指定ID",groups = {
    
    AddGroup.class})
	@TableId
	private Long brandId;
	/**
	 * 品牌名
	 */
	@NotBlank(message = "品牌名不能为空",groups = {
    
    UpdateGroup.class,AddGroup.class})
	private String name;

@Validated(AddGroup.class)

 public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand /* , BindingResult bindingResult */)

第四步:自定义校验规则
自定义的校验规则我们放在common下
1) 、添加依赖
2)、编写一个自定义的校验注解
3)、编写一个自定义的校验器 ConstraintValidator
4)、关联自定义的校验器和自定义的校验注解
添加依赖

        <!--用于校验-->
        <dependency>
            <groupId>jakarta.validation</groupId>
            <artifactId>jakarta.validation-api</artifactId>
            <version>2.0.2</version>
            <scope>compile</scope>
        </dependency>

编写一个自定义的校验注解


/**
 * @Description: 自定义注解规则
 * @Created: with IntelliJ IDEA.
 * @author: 夏沫止水
 * @createTime: 2020-05-27 17:48
 **/

@Documented
// 指定校验器
@Constraint(validatedBy = {
    
     ListValueConstraintValidator.class })
// 注解可以标注的位置
@Target({
    
     METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
// 运行时校验
@Retention(RUNTIME)
public @interface ListValue {
    
    
    // 默认提示的错误信息会从ValidationMessages.properties 文件下查找com.atguigu.common.valid.ListValue.message 信息进行输出
    // alidationMessages.properties 也可以在resources去自定义
    String message() default "{com.atguigu.common.valid.ListValue.message}";

    Class<?>[] groups() default {
    
     };

    Class<? extends Payload>[] payload() default {
    
     };

    int[] vals() default {
    
     };

}

自定义自己的错误提示配置文件ValidationMessages.properties,放在resources下

com.atguigu.common.valid.ListValue.message=必须提交指定的值

编写一个自定义的校验器 ConstraintValidator

package com.atguigu.common.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

/**
 * ConstraintValidator<ListValue,Integer>
 *     第一个参数:指定注解
 *     第二个参数:指定什么类型的数据
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    
    

    private Set<Integer> set = new HashSet<>();

    /**
     * 初始化方法
     * @param constraintAnnotation
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
    
    

        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
    
    
            set.add(val);
        }

    }

    /**
     * 判断是否效验成功
     * @param value 需要效验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
    
    

        //判断是否有包含的值
        boolean contains = set.contains(value);

        return contains;
    }

}

关联自定义的校验器和自定义的校验注解

	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@ListValue(vals = {
    
    0,1})
	private Integer showStatus;

14、统一的异常处理

第一步:自己的为服务下新建一个exception包,用于存放专门进行异步处理的controller
第二部:在common中新建一个exception包,存放所有的状态码和信息
错误码的规则用五位数,前两位表微服务,后3位标具体错误西南西

package com.atguigu.common.exception;

/**
 * @Description: 错误状态码枚举
 * @Created: with IntelliJ IDEA.
 * @createTime: 2020-05-27 17:29
 *
 * 错误码和错误信息定义类
 * 1. 错误码定义规则为5为数字
 * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
 * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
 * 错误码列表:
 *  10: 通用
 *      001:参数格式校验
 *      002:短信验证码频率太高
 *  11: 商品
 *  12: 订单
 *  13: 购物车
 *  14: 物流
 *  15:用户
 *
 *
 *
 **/

public enum BizCodeEnum {
    
    

    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VAILD_EXCEPTION(10001,"参数格式校验失败"),
    TO_MANY_REQUEST(10002,"请求流量过大,请稍后再试"),
    SMS_CODE_EXCEPTION(10002,"验证码获取频率太高,请稍后再试"),
    PRODUCT_UP_EXCEPTION(11000,"商品上架异常"),
    USER_EXIST_EXCEPTION(15001,"存在相同的用户"),
    PHONE_EXIST_EXCEPTION(15002,"存在相同的手机号"),
    NO_STOCK_EXCEPTION(21000,"商品库存不足"),
    LOGINACCT_PASSWORD_EXCEPTION(15003,"账号或密码错误"),
    ;

    private Integer code;

    private String message;

    BizCodeEnum(Integer code, String message) {
    
    
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
    
    
        return code;
    }

    public String getMessage() {
    
    
        return message;
    }
}

第三步:在第一步中的controller中利用两个注解完成统一的已处理逻辑@RestControllerAdvice,

package com.atguigu.gulimall.product.exception;

import com.atguigu.common.exception.BizCodeEnum;
import com.atguigu.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * User: maruis
 * Date: 2021/6/4  20:31
 * Description:
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.atguigu.gulimall")
public class GulimallExceptionControllerAdvice {
    
    
	// 用于捕捉数据校验的异常
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public R handlerVaildException(MethodArgumentNotValidException e){
    
    
		BindingResult result = e.getBindingResult();
		Map<String,String> map = new HashMap<>();
		result.getFieldErrors().forEach((item)->{
			map.put(item.getField(),item.getDefaultMessage());
		});
		e.printStackTrace();
		return R.error(BizCodeEnum.VAILD_EXCEPTION.getCode(),BizCodeEnum.VAILD_EXCEPTION.getMessage()).put("data",map);
	}
	// 用于捕捉所有异常
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public R handlerException(Throwable e){
    
    
		log.error("异常发生的类{}------>异常信息{}",e.getClass(),e.getMessage());
		e.printStackTrace();
		return  R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(),BizCodeEnum.UNKNOW_EXCEPTION.getMessage());
	}
}

注意

idea更新至2020.3之后打开以前的项目,发现测试类莫名奇妙没有启动按钮了,方法名上还有黄色的警告。

解决方法,在测试类和测试方法加上public关键字即可。
在这里插入图片描述

java entity中的正则表达式写法与js不同

@Pattern(regexp = "^[a-zA-Z]+$",message = "首字母必须是a到z或者A到Z的字母")
	private String firstLetter;

使用案例云对象存储使用文档:
https://help.aliyun.com/document_detail/31925.html?spm=a2c4g.11174283.6.1731.e0547da2smGWE2
JSR303 java规范体验303号
给需要校验的数据加上校验注解,数据校验不通过时会返回400,步骤

第一步:给需要校验的数据加上校验注解,在import javax.validation 包中
如:@Email  @NotBlank(message = "品牌名必须提交")  @NotEmpty
第二步:在controller中添加校验注解的开关,只有添加了@Valid 才会进行校验
如:public R save(@Valid @RequestBody BrandEntity brand)
package com.atguigu.gulimall.product.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;

import lombok.Data;
import org.apache.ibatis.annotations.Param;
import org.hibernate.validator.constraints.URL;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

/**
 * 品牌
 * 
 * @author maruis
 * @email [email protected]
 * @date 2021-05-22 19:42:32
 */
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    
    
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	private Long brandId;
	/**
	 * 品牌名
	 */
	@NotBlank(message = "品牌名必须提交")
	private String name;
	/**
	 * 品牌logo地址
	 */
	@URL(message = "必须是一个合法的url地址")
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	// 自定义的校验规则
	@Pattern(regexp = "/^[a-zA-Z]]$/",message = "检索首字母必须时一个字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	private Integer sort;

}

/**
     * 保存
     */
    @RequestMapping("/save")
    //@RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
    
    
        if (result.hasErrors()){
    
    
            Map<String,Object> map = new HashMap<>();
            result.getFieldErrors().forEach((item)->{
    
    
                // 获取错误的提示信息
                String message = item.getDefaultMessage();
                // 获取错误字段的名字
                String field = item.getField();
                map.put(field,message);

            });
            return R.error(400,"提交的数据不合法").put("data",map);
        }
		brandService.save(brand);

        return R.ok();
    }

统一的异常处理
使用springmvc为我们提供的ControllerAdvice

在这里插入图片描述

15、mybaitsplus中配置逻辑删除

第一步:在配置文件中进行全局配置状态标志,这个全局的逻辑删除规则也可以不设置

#  classpath* 表示扫描自己和所有依赖的路径
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto # 设置主键进行自增
      logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

第二步:给bean上加上逻辑删除注解,这个中的配置规则比全局配置的规则优先级高

	/**
	 * 是否显示[0-不显示,1显示]
	 */
	@TableLogic(value = "1",delval = "0")
	private Integer showStatus;

猜你喜欢

转载自blog.csdn.net/fen_dou_shao_nian/article/details/116948955
今日推荐