文章目录
- 一、项目概述
- 二、项目基础环境搭建
- 1.开发环境搭建
- 1.1 数据库环境搭建
- 1.2 Maven项目搭建
- 1.2.1 创建Maven项目
- 1.2.2 导入pom.xml文件 (位置:资料/项目配置文件/pom.xml)
- 1.2.3 导入Spring Boot 配置文件(resources目录,在src→main→resources) application.yml (位置:资料/项目配置文件/application.yml),对于这些配置文件直接拿过来使用就行;
- 1.2.4 编写启动类(Application.java)
- 1.2.5 (导入前端资源)下一步就是把涉及到前端的页面导入进来,因为主要为了练习java,所以前端内容直接导入。(目录 瑞吉点餐项目>前端>前端资源,里面有两个目录backend后端,里面是后台所涉及到的HTML页面;front前端,也就是移动端所涉及到的HTML页面)
- 三、项目功能开发
- 四、员工管理业务开发
- 总结
一、项目概述
1.访问
- http://172.17.0.60:8200/backend/index.html
- http://172.17.0.60:8200/front/index.html
2.项目概述
2.1 APP移动端的应用:提供给餐厅的;
2.2 后台:对于菜品、套餐的维护;
3.软件开发整体介绍
3.1 软件开发流程
3.1.1 需求分析:产品原型,需求规格说明书的编写;
(1) 产品原型:通过网页的形式展现一下,当前开发项目的大体结构——涉及到的页面以及页面的结构样式,点击按钮之后需要查询哪些数据,产生什么样的效果,通过网页的形式直观的展现出来;
(2) 需求规格说明书:一个Word文档,展现这个项目都有哪些功能,需要有文字说明;
3.1.2 设计:基于上述“需求分析”,进行相应的设计工作(产品文档、UI界面设计、概要设计、详细设计、数据库设计)
(1) UI界面设计:需要把项目的界面效果展现出来;
(2) 数据库设计:需要设计出当前开发项目会用到几个数据库,每个数据库里面涉及到哪些表,具体到表的字段是什么样子的,都需要详细的把它设计出来。
3.1.3 编码:进入编码阶段(项目代码、单元测试)
3.1.4 测试:编码完成之后,进入测试阶段(测试用例、测试报告)
- 由测试人员编写一些测试用例,然后出具一份测试报告;
3.1.5 上线运维:测试报告没有问题之后,便可以上线运维了,由专门的运维人员来完成,包括软件环境安装、配置、部署项目等。
3.2 角色分工(开发软件需要涉及到的开发团队和人员)
3.2.1 项目经理:对整个项目负责,任务分配、把控进度;属于管理人员。对于一些比较大的公司,项目经理不会参于具体的编码、文档的编写,主要对整个项目人员的调配、任务分配、资源调度等等。
3.2.2 产品经理:进行需求调研,输入需求调研文档、产品原型等;一般在项目前期,产品经理就会介入;
3.2.3 UI设计师:根据产品原型输出界面效果图;
3.2.4 架构师:项目整体架构设计、技术选型等;
3.2.5 开发工程师:代码实现;
3.2.6 测试工程师:编写测试用例,输出测试报告;
3.2.7 运维工程师:软件环境搭建、项目上线;一般项目开发完之后,运维工程师才会介入;
3.3 软件环境
(1) 开发环境(development):开发人员在开发阶段使用的环境,一般外部用户无法访问。(比如:开发人员使用的MySQL数据库,有可能是安装在开发人员本地电脑上的,或者在另外一台外部服务器上);
(2) 测试环境(testing):专门测试人员使用的环境,用于测试项目,一般外部用户无法访问。(专门供测试人员使用的测试服务器,上面安装了JDK、Tomcat等等在测试环境下使用到的软件);
(3) 生产环境(production):即上线环境,正式提供对外服务的环境。(项目上线所运行的环境,);
4.吉瑞外卖项目介绍
4.1 项目介绍(从功能需求方面介绍)
(1) 本项目(瑞吉外卖)是专门为餐饮企业(餐厅、饭店)定制的一款产品,包括系统管理后台和移动端应用两部分。其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的菜品、套餐、订单等进行管理维护。移动端应用主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等等。
(2) 本项目共分为3期进行开发:
- 第一期:主要实现基本需求,其中移动端应用通过H5实现,用户可以通过手机浏览器访问;
- 第二期:主要针对移动端应用进行改进,使用微信小程序实现,用户使用起来更加方便;
- 第三期:主要针对系统进行优化升级,提高系统的访问性能。
4.2 产品原型展示(通过网页的形式直观地展示出来,具体都有哪些功能、页面、页面结构等等);
4.3 技术选型(开发过程中,需要使用哪些技术栈)
4.4 功能架构(以图形化的方式展示出这个项目都有哪些功能)
4.5 角色(分析使用这个项目的人,都有哪几类人,再通过角色的方式对这些人进行一个划分)
4.5.1 产品原型展示
(1) 产品原型,就是一款产品成型之前的一个简单的框架,就是将页面的排版布局展现出来,使产品的初步构思有一个可视化的展示。通过原型展示,可以更加直观的了解项目的需求和提供的性能。
(2) 一般产品原型都是一些HTML页面,通过这些简单的页面了解到这个网站一些大概的效果。
(3) 课程资料中已经提供了产品原型:瑞吉外卖后台(管理端)和瑞吉外卖前台(用户端)
(4) 注意:产品原型主要用于展示项目的功能,并不是最终的页面效果;
4.5.2 技术选型
4.5.3 功能架构
4.5.4 角色
- 后台系统管理员:登录后台管理系统,拥有后台系统中的所有操作权限;
- 后台系统普通员工:登录后台管理系统,对菜品、套餐、订单等进行管理;
- C端用户:登录移动端应用,可以浏览菜品、添加购物车、设置地址、在线下单等;
二、项目基础环境搭建
1.开发环境搭建
1.1 数据库环境搭建
1.1.1 创建对应的数据库(图形界面或者命令行都可以)
1.1.2 导入表结构(资料/数据模型/db_reggle.sql)
方法一: 在数据库软件(图形界面)上找到数据库点右键→运行sql文件→找到sql文件的路径→确定;
方法二: 在dos窗口下: win+R → 新建数据库/使用数据库 → 输入 source → 然后将需要的.sql文件拖入到source后面,就会自动生成一个.sql文件的路径;
注意事项:
① 导入表结构,既可以使用上面的图形界面,也可以使用MySQL命令:
② 通过命令导入表结构时,注意sql文件不要放在中文目录中。
1.1.3 数据表
(1) 员工表 employee:存储员工的数据信息,包括姓名、密码等等;
(2) 菜品和套餐分类表 category:荤菜、素菜、凉菜
1.2 Maven项目搭建
1.2.1 创建Maven项目
- 打开IDEA → New Project → GroupId : com.itheima → ArtifactId : reggie_tack_out
- 注意事项:创建完项目后,注意检查项目的编码、Maven仓库配置、JDK配置等。
1.2.2 导入pom.xml文件 (位置:资料/项目配置文件/pom.xml)
pom.xml文件内容介绍:
① <parent>标签
<parent> // 继承
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> // 继承的名称
<version>2.4.5</version> // 继承的版本号
<relativePath/> <!-- lookup parent from repository -->
</parent>
② <properties> 指定JDK的版本号
<properties>
<java.version>1.8</java.version>
</properties>
③ <dependencies></dependencies>里面包含项目所依赖的Maven坐标,也就是各种jar包
- spring-boot-starter 它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件)进行自动配置。使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖,Spring Boot就能自动扫描并加载相应的模块。
- spring-boot-starter-test 做单元测试的
- spring-boot-starter-web web应用需要的
- mybatis-plus-boot-starter
- lombok
- fastjson Java对象转json的
- commons-lang
- mysql-connector-java mysql驱动包
- druid-spring-boot-starter 阿里的数据源,数据库连接时使用的包
④ <build></build>插件
1.2.3 导入Spring Boot 配置文件(resources目录,在src→main→resources) application.yml (位置:资料/项目配置文件/application.yml),对于这些配置文件直接拿过来使用就行;
(1) 做法:找到application.yml文件,复制,在IDEA中找到src→main→resources目录,单击resources目录后,Ctrl+V粘贴即可;
(2) 解析:
- server: port:8080 → tomcat的端口号
- spring: application: name:cache_demo → 指定应用的名称,可选
- 数据源相关的配置
1.2.4 编写启动类(Application.java)
做法: 在main目录 → java目录 → 新建包com.itheima.reggie → 在这个包下新建启动类:ReggieApplication.java
1.2.5 (导入前端资源)下一步就是把涉及到前端的页面导入进来,因为主要为了练习java,所以前端内容直接导入。(目录 瑞吉点餐项目>前端>前端资源,里面有两个目录backend后端,里面是后台所涉及到的HTML页面;front前端,也就是移动端所涉及到的HTML页面)
(1) 将backend和front这两个文件夹复制一下,粘贴到项目的resources目录下;
- 注意:对于我们引入的一些静态资源,默认是放到static和templet文件夹下面,所以放到backend和front这两个文件夹直接访问是访问不到的,所以需要将服务器停掉,重新开启就可以访问了……
- 举例:访问一下backend文件夹下的index.html页面,localhost:8080/backend/index.html:
- 这样会出现404页面,原因就是默认访问的是static和templet文件夹下的静态资源,需要通过编写配置类的方式来设置静态资源的映射(编写一个配置MVC的静态资源框架,来做静态资源的映射):在com.itheima → reggie目录下新建包config,以后所有的配置类都放在这个包下 → 新建配置类 WebMvcConfig.java,如图:
- 释义:
① @Configuration 用来声明是配置类的
② @Slf4j 记录一个日志,方便调试
③ 目的:让它重新映射到backend目录下的index.html,这样就能访问到了
④ 通过通配符“/”的方式映射到相应的路径下
⑤ 用 registry 来设置哪些访问路径
⑥ addResourceHandler 资源处理器:处理前端发过来的请求是什么样子的
(2) 完成上面配置类后到RoggieApplication.java页面重新启动一下:
在下面看到“开始进行静态资源映射”的字样就说明上面写的配置类已经生效了。
到页面中重新输入网址就看到已经成功了……
(3) 到这里,项目的基础环境就已经搭建好了,在这基础之上就可以开发相应的业务功能了。
三、项目功能开发
1.后台登录功能开发
1.1 概述:从三个方面来入手:需求分析,代码开发,功能测试;
1.2 需求分析
1.2.1 页面原型展示
(1) 登录功能分析:就是在登录页面上输入用户名和密码,点击登录按钮,它就会发送请求,最终这个请求就会请求到服务端的一些组件,比如说先请求到controller 控制层,然后再通过controller层调service层,再调mapper,最终跟数据库交互,来根据我们的用户名和密码进行查询。—— 这就是我们的登录页面原型。
(2) 登录页面展示(页面位置:项目/resources/backend/page/login/login.html)
前面在进行项目搭建的时候,将项目页面一次性的导入进来了,所以这里在项目中找到注册登录页面login.html:
因为前面已经将backend和front目录放到“静态资源映射”里面了,所以可以直接访问到login.html页面(resources>backend>page>login>login.html),访问。
- localhost:8080/backend/page/login/login.html
(3) 查看登录请求信息:
在浏览器中输入用户名和密码(用户名admin,密码是123456),点击登录按钮之后,会发生什么事情呢?
注意:在数据库reggie的员工表employee中可以看到用户名和密码,注意在表格里显示的密码是经过md5加密的,编译过来就是123456。
在浏览器登录页面上,按键盘F12键,调出调试工具,然后在输入用户名和密码,点击登录按钮之后,会看到页面显示“系统接口404异常”,这是因为我们后端服务还没开发的原因,所以在这里请求发出去之后,服务端没有去相应它造成报了404,在这里先不用去管它;
在这里先看点击登录按钮之后,在F12调出的调试页面中,查看它请求到哪里去了?如图:
可以看到,请求到了employee/login下面了,并且将我们的用户名和密码以json的形式(字符串的形式),给我们提交到了服务端,
总结:通过浏览器调试工具(F12),可以发现,页面会发送请求(请求地址为http://localhost:8080/employee/login)并提交参数(username和password)
此时报404,是因为我们的后台系统还没有响应此请求的处理器,所以我们需要创建相关类来处理登录请求;
(4) 数据库模型(employee表,即员工表)
登录页面对应的就是这张员工表;
(5) 回到IDEA中,找到登录页面(resources>backend>page>login>login.html),
大致浏览一下login.html页面,不一定会写,但一定要看懂:
VUE知识点:
- el: → 指定我们唯一标识;在上面肯定有一个
的id与之相对应;
在这里是el:’#login-app’和上面相对应; - data(){} → 里面是我们需要用到的一些表单数据;
- methods:{} → js里的方法;
- handleLogin(){}方法:指的是当我们点击登录按钮的时候,它就会调用;即点击〔@click.native.prevent=”handleLogin”>登录〕时,就会触发handleLogin方法,
① 通过$refs语法找到表单loginForm,然后调用validate做一个校验,校验用户名密码是否为空;
② 如果校验通过,就把loading改为true,即“登录”和“登录中”的切换;
③ 校验通过之后,就调用loginApi这个方法,这个方法封装在一个js文件里(在backend>api>login.js里)
login.js 通过$axios向后端发送post方式的请求,请求地址是“/employee/login”,然后把数据“data”带过来,这个数据“data”就是login.html里的loginForm,里面放的就是我们的用户名和密码,这样我们的请求便发出去了;
请求发出去,我们的服务端接收到之后,就需要在我们的服务端进行处理,处理完之后,还需要给我们前端页面一个响应结果;
这个结果我们通过“res”来进行接收;
④ res接收到之后,res.code是否等于1,1表示登录成功;
⑤ 登录成功之后,将相应的数据转为json,然后把它保存起来;保存在浏览器当中;
⑥ 然后做一个页面跳转,跳转到“/backend/index.html”页面上,即主页面;
⑦ 当res.code不等于1时(else),即登录失败,就会提示一个错误信息,然后〔this.loading = false〕,登录页面上“登录中”按钮重新变为“登录”按钮,然后重新输入登录名和密码,再点登录按钮就好了;
⑧ 这段代码重点关注“res.code”“res.data”“res.msg”,它从这个返回结果里面,获得这些值,这就要求我们服务端处理完之后,数据里边应该还有我们这个code、data、msg等这些数据,这样我们的前端页面才能获取到这些数据;【在明确我们的服务端处理完这些数据之后,应该给我们的页面响应什么样的数据,而且这个数据应该是一个json格式的数据】
code、data、msg等这些数据是我们的后端(服务端)给前端页面响应回来的数据,应该就含有这些信息;
这就是整个的需求分析,一会儿就要根据这个分析来代码实现它;
1.3 代码开发
1.3.1 创建实体类Employee,和employee表进行映射;
(1) 一般规则:数据库中有一个表,便要去创建一个实体类,来跟这个表进行映射,因为后面需要通过MyBatis plus,对数据库进行操作,这个实体类的属性,需要跟我们表里的字段,进行一一对应就可以了;
(2) 在这里可以直接导入资料中提供的实体类(位置:吉瑞外卖项目>资料>实体类>Employee.java);
在IDEA项目中,src>main>java>com.itheima>reggie下新建包entity(实体包),以后所有的实体类都放在这个包下;将实体类Employee.java复制粘贴到entity包里面;
(3) 实体类Employee.java代码分析:
package com.itheima.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
}
① 代码分析一:
- 下面这些是与表格employee中的字段名一一对应的:
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
② 代码分析二:
private String idNumber;
private LocalDateTime createTime;
private LocalDateTime updateTime;
- idNumber 代表的是身份证号码(驼峰命名法);对应表employee中id_number字段;
- createTime对应表employee中create_time字段;
- updateTime对应表employee中update_time字段;
- 注意:这里实现idNumber和id_number的映射关系,需要在src/main/resources/application.yml中开启驼峰命名;
1.3.2 在IDEA中,将整个调用链条上的控制层Controller、业务层Service、持久层Mapper创建出来,但需要放到不同的包下面;
(1) 在src>main>java>com.itheima>reggie下,新建包controller、包service、包mapper;
其中包Service又分为接口和实现类,所以在包Service里面再新建实现类包impl;Service接口则直接放在Service包下即可;
上面这些 Controller、Service、Mapper 基于Mybatis plus创建的,直接根据要求继承BaseMapper或者实现IService接口就可以了;
(2) 在包src>main>java>com.itheima>reggie>mapper下,新建接口 EmployeeMapper.java;
① 继承 BaseMapper ,加一个注解@Mapper,再加一个泛型
(3) 在包src>main>java>com.itheima>reggie>service下,新建接口 EmployeeService.java;
① 继承 IService ,再加一个泛型
② 创建接口 EmployeeService.java的实现类EmployeeServiceImpl.java,放到包src>main>java>com.itheima>reggie>service>impl下;
加一个注解 @Service,继承ServiceImpl.java父类,指定泛型EmployeeMapper和Employee,实现接口EmployeeService.java
(4) 在包src>main>java>com.itheima>reggie>controller下,创建EmployeeController.java,
① 加注解:
- 加日志注解@Slf4j,能在控制台(F12)输出日志,方便调试;
- 加注解@RestController
- 加注解@RequestMapping(“/employee”) —— 加“employee”的目的是在请求路径里面有employee,如下图:
② 在代码里,把我们创建的接口 EmployeeService 注入进来,再加一个注解 @Autowired;
1.3.3 导入通用返回结果类R.java,服务端响应的数据最终都会封装成此对象
(1) 上面这些就是登录功能所需要用到的Controller、Service、Mapper都已经创建好了,接下来就是需要写我们的登录方法了,但在写登录方法之前,还需要导入返回结果类R;
① 此类是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面;
② R.java,名字叫什么都行,因为后面需要编写很多controller类,controller类里的方法也有很多,这些方法基本上都是响应客户端页面发过来的一些请求,controller处理完之后,要给我们的页面一个结果,而这个结果封装的话,我们在这个项目中是统一的、把所有的返回结果,全都封装成一个R对象,即 R.java:
③ R.java 代码展示:
package com.itheima.reggie.common;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 通用返回结果,服务端响应的数据最终都会封装成此对象
* @param <T>
*/
@Data
public class R<T> {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> R<T> success(T object) {
R<T> r = new R<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> R<T> error(String msg) {
R r = new R();
r.msg = msg;
r.code = 0;
return r;
}
public R<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
④ 在这里这个类已经写好了,直接导入进来使用就可以,在 瑞吉外卖-资料\1 瑞吉外卖项目\资料\服务端返回结果类\R.java中
在IDEA中,包src>main>java>com.itheima>reggie下,新建包common(通用的意思),将R.java直接复制粘贴到包common里面,
(2) R.java代码执行解析:
① 前面在分析登录页面login.html的时候,可以看到 handleLogin() 是我们前端页面的登录方法(在页面点击登录按钮之后,就会触发这个方法),然后就会通过if(valid)进行校验,校验之后就会通过loginApi进行发送请求,调用login.js里的loginApi()方法,这个loginApi()通过ajax,向我们的后端controller发送请求(‘url’:‘/employee/login’)……
② 请求完之后,login.html里的res就是我们后端controller响应回来的结果,然后通过res.code和res.data和res.msg获得响应的值;
③ 而我们的R.java对象就是对应的code和data和msg,这样我们的前端页面login.html 才能通过这样的属性,获取响应的值……
④ 所以只有我们的服务端R.java给前端响应的就是这种格式的数据,在我们的前端才会拿到这样的数据;
⑤ R.java中代码:提供的四个属性解析:
- private Integer code; 这里code表示编码,以响应码的形式表示返回结果,如果是1时,表示成功,如果是0则表示失败;
- private String msg; 这里msg指错误信息,比如说登录错误,就会向msg设置值,即登录失败是,就会通过msg这个属性进行封装;
- private T data; 数据,比如说数据实体,如员工实体employee,放到这里的data属性里面;这样我们前端页面login.html里的data就能转为json,然后存储到我们的浏览器当中……
- private Map map = new HashMap(); 这里封装的是一些动态数据,使用的地方不是很多,暂时不用管;
⑥ R.java中代码:提供的三个方法解析:
- success() 方法就是说当我们登录成功的时候,我们就会返回一个R对象;我们其实不用去newR对象,而是直接通过R.success,因为它是一个静态的(static),在这个方法内部就已经new了……
- error() 方法就是说当我们登录失败的时候,就会调用这个静态方法(用R.error的方式调用),error() 方法代码可以知道它也是封装成一个R对象……
- add() 方法就是用来操作map这个动态数据的,到时候用到再说;
(3) R.java 总结:以后编写的所有的controller,只要这个controller的方法有返回值,那么都会封装成R对象,进行返回,为了增强R.java的通用性,还在上面加了一个泛型,表示我们主体的数据最终就会传进来;
1.3.4 在Controller中创建登录方法(一)梳理登录方法处理逻辑:
(1) 处理逻辑如下:
① 将页面提交的密码password进行md5加密处理;
② 根据页面提交的用户名username查询数据库;
③ 如果没有查询到则返回登录失败结果;
④ 密码比对,如果不一致则返回登录失败结果;
⑤ 查看员工状态,如果为已禁用状态,则返回员工已禁用结果;
- “查看员工状态是否已禁用”:类似于锁定账号,在员工表employee里面有一个字段status,值为1时,表示正常使用状态;如果为0,则表示锁定状态;
⑥ 登录成功,将员工id存入Session并返回登录成功结果;
(2) 编写登录方法:在src>main>java>com.itheima>reggie>controller>EmployeeController.java里面:
① 返回值是R,注意导入的是R (com.itheima.reggie.com)下的R,泛型对应的是这个对象;方法名叫login(),暂时先返回 return null;
② 再加一个注解 @PostMapping ,因为我们前端发送的请求,是一个post方式的请求(Request Method:POST),所以这个地方就是 @PostMapping,这里请求的url路径需要跟(Request URL:http://localhost:8080/employee/login)匹配,所以这里注解应该写成 @PostMapping(“/login”),才能匹配到;
③ 前端点击登录按钮的时候,发送的请求还给我带过来了两个参数(username和password),但需要注意这两个参数是json形式的;所以在EmployeeController.java里面的login() 方法里的参数里面加一个注解 (@RequestBody Employee employee);
需要注意的是,在这里传的key中,一个k叫username,另一个k叫password,它需要跟(reggie/entity/Employee.java)里的username和password属性命名要一样,否则无法封装成功;
④ 在这里还需要给login()方法再加一个参数(HttpServletRequest request),因为登录成功之后,需要把employee员工对象的ID存到session一份,表示登录成功,这样的话我们想要获取登录用户的话,就会随时获取出来。到时候就可以通过request对象get一个session;所以request一会儿就会用到,在这里提前准备好;
1.3.5 在Controller中创建登录方法(二) EmployeeController.java
- 在 src>main>java>com.itheima>reggie>controller>EmployeeController.java中编写代码:
(1) 复制上面提到过的6个逻辑步骤到代码里:
按照这六步来编写代码;
(2) 代码编写:将页面提交的密码password进行md5加密处理
① 页面提交的密码其实已经封装到employee里面了,在这里只需要通过〔String password = employee.getPassword();〕拿到密码就可以;
② 拿到密码之后,调用工具类DigestUtils,这里面有md5加密的工具方法md5DigestAsHex();然后将我们的密码传进来,转成getBytes数组的方式,即md5DigestAsHex(password.getBytes());
这样,这串代码〔DigestUtils.md5DigestAsHex(password.getBytes());〕就会对我们明文的密码进行一个处理;
处理完之后,再赋给密码自己,就省的再去创建一个新的变量了,即〔password = DigestUtils.md5DigestAsHex(password.getBytes());〕
③ 代码如下图:
(3) 代码编写:根据页面提交的用户名username查询数据库;
① 因为在这里要根据用户名username查询数据库;所以在这里需要new一个LambdaQueryWrapper对象,泛型是(Employee是一个实体类),完整代码写法:〔LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();〕—— 包装一个查询对象;
② 接下来需要根据用户名Username,添加查询条件(等值查询):〔queryWrapper.eq(Employee::getUsername.employee.getUsername())〕
解析,传过来的也是在employee对象里面,所以在这里直接用employee.getUsername();
这样条件就封装好了;
③ 接下来便可以调用employeeService的getOne()方法,然后把queryWrapper对象传进来;在这里用getOne()方法的原因是我们的数据库里面,已经对Username字段做了唯一的约束(Unique),既然是唯一的,便可以调用getOne()这个方法,去查出来唯一的一个数据,查出来之后,封装成一个Employee对象;
完整代码:〔Employee emp = employeeService.getOne(queryWrapper);〕
④ 根据页面提交的用户名username查询数据库代码如下:
(4) 代码编写:如果没有查询到则返回登录失败结果;
① 看一下这个数据到底有没有查到,即使用if语句;判断emp是否等于空(null),如果等于空,则说明没有查到,直接return返回登录失败的结果;而这个登录失败的结果,则需要封装成上面的R对象,所以在这里写成〔return R.error(“登录失败”);〕
② 代码如下:
(5) 代码编写:密码比对,如果不一致则返回登录失败结果;
① 数据库查到的密码跟我们明文处理完之后的密码(即将页面提交的password进行md5加密处理后的密码)进行比对;用if判断语句;
② 数据库里的密码emp.getPassword()和处理完之后的密码password进行比对equals(),因为在这里比对的是不成立的时候,所以在这里要加一个非!
③ 返回一个提示信息 return R.error(“登录失败”);
④ 代码如下:
(6) 代码编写:查看员工状态,如果为已禁用状态,则返回员工已禁用结果;
① if判断语句,0表示禁用,1表示可用;
② 代码如下:
(7) 代码编写:登录成功,将员工id存入Session并返回登录成功结果
① 怎么获得Session呢?在EmployeeController.java上面有一个request对象,通过这个request来getSession:
② 之后,再返回 return R.success(emp),这个emp对象就是刚才从数据库里面查出来的这个对象;
③ 代码如下:
1.4 功能测试
1.4.1 Controller中 EmployeeController.java的代码功能测试:
- 在 src>main>java>com.itheima>reggie>controller>EmployeeController.java中测试代码:
(1) 测试一下上面写的EmployeeController.java代码有没有问题,需要先重新启动一下服务;然后通过加断点的方式调试跟踪一下程序处理有没有问题;
① 在 String password = employee.getPassword(); 前面加一个断点,
加断点后,服务已经启动成功的状态:
② 回到页面上,重新输入用户名admin和6位数密码,先输入一个错误的密码,点击登录按钮,就会跳到IDEA中的EmployeeController.java上:
- 然后查看数据封装有没有问题,在如下图所画的位置,点击employee下面的提示“+{Employee}”,就会跳到数据封装页面:
(2) 继续往下进行单步调试,进行md5加密处理:
(3) 处理完看看没问题之后,接下来开始查数据库getOne
① 点击emp下面的加号内容:
就会跳转到下面的页面,条件是用户名必须是正确的:
② 查出来之后,查看第一个if判断瑜伽:if(emp == null),点击下面的符号(如下图),因为不等于空,所以直接跳过去了。
③ 下一步进行密码比对,点击下图画出的那个符号,因为在上面输入的密码是错误的,所以要进到〔R.error(“登录失败”)〕这里,返回错误信息;然后点击“放行”符号,跨过去;
然后回到页面当中,点击控制台里面的Console选项,会看到一行报错:“Uncaught(in promise) Error:timeout of 1000ms exceeded”(表示已超时),原因是在后端EmployeeController.java代码里加了一个断点,一直在这里卡着,我们一直没有给前端页面响应,而且前端页面设置的超时时间是10s,如果10s不给响应,页面就会抛出上面说的异常;所以为了方便我们调试,要先将这个超时时间设置大一点;
找到前端的js(resources>backend>js>request.js),将timeout默认的1000后面再加两个零;改完之后,记得把服务器重新启动一下;
重新启动完之后,在EmployeeController.java代码页面上,下方控制台Console选项,右键选择 Clear All;
然后回到浏览器页面,将登录页面重新刷新一下,点击登录按钮,又会跳转到IDEA中的EmployeeController.java代码页面上;
然后再次点击employee下面显示的〔+{Employee@6423}〕
就会再次跳转到如下页面:
这样这一步就算完成了,然后在IDEA中,点击控制台下方的如下图按钮往下走,继续进行断点测试:
④ 测试“查看员工状态”在emp上点击下面显示的〔+Employee@{7112}〕,跳转到下面的页面,查看status=1就正确;
⑤ 继续点击控制台上的测试按钮往下走,测试“登陆成功,将员工id存入Session并返回登录成功结果”
因为上面的测试都正确,这一步就会直接存入Session里面,返回successs;
⑥ 点击IDEA中控制台上的如下图按钮,然后返回到浏览器登录页面:
注意,如果在登陆页面又看到如下报错,还是超时,是因为浏览器有缓存,在js里面改的时间没有生效;清理一下浏览器的历史记录,重新刷新一下页面就可以了;
在浏览器登录页面再次点击登录按钮,跳转到IDEA的EmployeeController.java代码页面上,继续点击控制台上的测试按钮往下走,一直到测试“登陆成功,将员工id存入Session并返回登录成功结果”上,点击IDEA的控制台的放行图标“绿三角”,然后就会跳转到浏览器页面(从登录页面进入员工管理页面)
(4) 登陆成功之后,找到登录页面 src>main>resources>backend>page>login>login.html
① 员工登录成功之后,调用了login.html里的代码localStorage.setItem然后把我们的数据data转成json,然后写到浏览器userInfo里面(放到浏览器的Application里面);
(5) 登录失败返回的是什么呢?
① 输入一个不存在的用户名,比如aaaa,然后点击登录按钮,跳转到IDEA的EmployeeController.java代码页面上,直接取消断点(在断点那个红点上再次点击就能取消)然后点击控制台的放行按钮(绿色三角形),就会直接跳转到浏览器登录页面,显示登录失败提示;这个“登录失败”提示是怎么来的呢?
② 这个“登录失败”提示是怎么来的呢?
- 用户名错误,密码正确的时候:回到IDEA中的登录页面login.html,可以看到代码code=1时,表示登录成功,那么code=0时表示登录失败,在EmployeeController.java代码上可以看到登录失败返回的是R.error,所以需要再到R.java代码里寻找,可以在R.java代码里找到:r.code=0;的代码;
- 用户名正确,密码不正确的时候:
- 用户名正确,密码正确,数据库错误的时候(手动将表格employee中的status字段的记录1改为0,表示账号被锁定),回到浏览器再次登录,就会显示“账号已禁用”,测试完之后记得改回来;
============================================
2.后台退出功能开发
2.1 概述:从三个方面来入手:需求分析,代码开发,功能测试;
2.2 概述:后台退出功能开发:需求分析
2.2.1 首页面退出按钮
(1) 员工登录成功后,页面跳转到后台系统首页面(backend/index.html),此时会显示当前登录用户的姓名:如果员工需要退出系统,直接点击右侧的退出按钮即可退出系统,退出系统后页面应跳转到登录页面;
(2) 后台系统首页面右上角的退出按钮(包括管理员+退出图标),先分析一下“管理员”这三个字是怎么显示在页面上的(是固定写死的还是动态展示的):
① IDEA中的index.html(src>main>resources>backend>page>login>login.html)
userInfo的来历:应该从数据模型里面拿,在VUE对象创建的时候,钩子函数created里面,通过localStorage把userInfo取出来,进行if判断,如果userInfo不等于空,则在数据模型里面添加了这么一个userInfo,如下图:
- 将userInfo取出来之后,在如下图的地方就可以动态展示了;
- 回到浏览器页面,就可以查看我们存储好的userInfo了,F12调出控制面板:
- 回到IDEA中src>main>resources>backend>index.html页面,再看退出
- 给这个图片加了一个@click单击事件,点击它的时候就会执行一个logout方法,通过logout方法再调用logoutApi方法,这个方法封装在了login.js文件里面了:
- 一会儿要做的就是在我们的服务端编写一个controller来接收这个请求;
2.3 后台退出功能开发:代码开发
2.3.1 代码开发逻辑:
用户点击页面中退出按钮,发送请求,请求地址为/employee/logout,请求方式为POST。
我们只需要在Controller中创建对应的处理方法即可,具体的处理逻辑:
① 清理Session中的用户id;
② 返回结果;
2.3.2 编写后台退出功能代码:
(1) 在src>main>java>com.itheima>reggie>controller>EmployeeController.java中;
- 因为在上面写了一个login(登录)方法,所以对应的在下面需要写一个退出方法:
- public 返回值是R,因为不需要返回数据了,方法名叫logout(退出),先返回个null,不让它报错,再加一个@PostMapping,地址和浏览器上logout的地址一致;然后以HttpServletRequest request对象做为参数(和上面login方法的参数一样);
- 注意:@PostMapping 声明前端发送的请求是post请求;
(2) 员工退出代码:
(3) 清理Session中保存的当前登录员工的id,将employee的属性给移除掉,然后返回R.success,代码如下:
- 这样员工退出代码就写完了,结合前端页面来看一下:前端页面会发送一个请求,请求到logout()方法,这个方法把session给清理掉,然后返回一个“退出成功”信息;
- 然后再打开index.html页面(src>main>resources>backend>index.html),logout()方法,判断code是否等于1,就可以通过removeItem移除浏览器中登录的用户信息,然后做一个页面跳转,跳转到登录页面;
2.4 后台退出功能开发:功能测试
2.4.1 功能测试
(1) 在IDEA中,EmployeeController.java中重启一下服务器,测试一下退出功能:
① 启动成功之后,右键Clear All清理一下控制台:
② 到浏览器页面(localhost:8080/backend/index.html)刷新一下,点击右上角退出按钮之后,跳转到如下页面,说明测试成功:
============================================
3.分析后台系统首页构成和效果展示方式:
3.1 系统首页面功能分析
3.1.1 系统首页面localhost:8080/backend/index.html
(1) 页面整体分析:当我们登录成功之后,跳转到了系统首页面(localhost:8080/backend/index.html),这个页面分为两部分:左侧为菜单数据,右侧为某个页面的功能列表;
点击左侧菜单时,右侧页面会跟着切换;
(2) 分两个方面分析系统首页面:
① 左侧菜单栏是怎么展示出来的?
- 在index.html页面(src>main>resources>backend>index.html),vue中,menuList里:
- v-for遍历menuList,判断当前item里面有没有children这个属性,以及这个属性的length是否大于0,因为在menuList里面id都大于0,所以这个if不会成立,会直接执行下面的else,所以这里的if先不用去管;
- 在v-else中,会将item-name展示出来:
② 为什么点击菜单时右侧会跟着变?
- 在src>main>resources>backend>index.html中,所有的菜单都加了一个@click单击事件,点击@click事件是,会执行menuHandle()方法:
- 在src>main>resources>backend>index.html中,menuHandle()方法:iframeUrl=item.url中的url对应下面如图划红线的url,这里通过iframe的方式显示了一个新的页面,<iframe></iframe>定义的是在右侧页面处展示出一个新的页面;
- 点击菜单时,就会切换到相对应的页面;
四、员工管理业务开发
1.分析“员工管理业务开发”需要完成的功能开发:
- 完善登录功能
- 新增员工
- 员工信息分页查询
- 启用/禁用员工账号
- 编辑员工信息
============================================
2.完善登录功能开发
2.1 完善登录功能开发分为三步:问题分析、代码实现、功能测试;
2.2 完善登录功能——问题分析;
(1) 前面我们已经完成了后台系统登录功能开发,但是还存在一个问题:用户如果不登录,直接访问系统首页面,照样可以正常访问。
(2) 这种设计并不合理,我们希望看到的效果应该是,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面。
(3) 那么,具体应该怎么实现呢?
- 答案就是使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。
2.3 代码实现步骤:
(1) 创建自定义过滤器 LoginCheckFilter;
(2) 在启动类上加注解@ServletComponentScan(解析:加入这个注解之后,过滤器LoginCheckFilter才会生效,开启组件扫描,才会扫描我们的过滤器)
(3) 完善过滤器的处理逻辑
① 过滤器的具体处理逻辑如下:
- 获取本次请求的URL;
- 判断本次请求是否需要处理;
- 如果不需要处理,则直接放行;
- 判断登录状态,如果已登录,则直接放行;
- 如果未登录则返回未登录结果;
2.4 创建过滤器LoginCheckFilter:
(1) 在IDEA中,src>main>java>com.itheima>reggie下,新建包filter,在包filter下,新建类LoginCheckFilter.java。(LoginCheckFilter.java类的作用是检查用户是否已经完成登录,属于自定义过滤器)
(2) 在LoginCheckFilter.java里,加一个注解〔@WebFilter(filterName=“loginCheckFilter”,urlPatterns=“/*”)〕
- 解析:filterName是指定过滤器的名称;urlPatterns是指拦截哪些请求路径;“/*”指所有请求路径;
(3) 在LoginCheckFilter.java里,过滤器LoginCheckFilter还需要实现一个过滤器接口Filter,并实现接口里的方法:
(4) 在LoginCheckFilter.java里,再加入一个注解@Slf4j //通过日志的方式来输出;
(5) 在启动类ReggieApplication.java(src>main>java>com.itheima>reggie>ReggieApplication.java)上加一个注解@ServletComponentScan,这样才会去扫描过滤器里的@WebFilter注解,从而扫描到我们创建的过滤器;
(6) 启动一下项目,看看过滤器能不能生效;
- 刷新一下浏览器页面:
- 再回到IDEA中,看到控制台有 “拦截到请求” 的字样,说明编写成功;
♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡
2.5 完善登录功能:过滤器LoginCheckFilter代码开发:
(1) 在过滤器(src>main>java>com.itheima>reggie>filter>LoginCheckFilter.java)中,复制上面的五个步骤到代码中,然后根据这五个步骤一一实现代码编写;
(2) 获取本次请求的URI:
① 在过滤器LoginCheckFilter.java中,因为已经得到了request对象,所以在这里直接通过request.getRequestURI()来获得本次请求的URI;
(3) 判断本次请求是否需要处理(检查用户是否已经完成登录了):
① 在过滤器LoginCheckFilter.java中,并不是所有的请求都需要处理,有些请求路径不需要处理,直接放行,所以定义不需要处理的请求路径;
② 在过滤器LoginCheckFilter.java中,如果获取本次请求的路径是〔/backend/index.html——系统首页面〕,但是这个写法跟“/backend/**”写法是不一致的,那么如何去匹配呢?这时就需要在上面添加一个新的对象AntPathMatcher,这个对象就是专门用来进行路径比较的;
③ 下面该判断本次请求是否需要处理,在这里就是判断请求的“/backend/index.html”是否是在“定义的不需要处理的请求路径”里面,如果是,则不需要处理;
- 在这里的判断可以直接封装一个方法check()来判断,返回值为boolean布尔类型,然后把我们请求的路径requestURI传到参数中;
- 然后把上面定义的不需要处理的路径 urls传进方法check()里进行遍历,看看里面的某一项是否跟我们这次请求的“/backend/index.html”匹配,若能匹配,则需要放行;
- 在方法check()参数中还需要传进来一个数组“String[] urls”;
- 在方法check()遍历urls(在IDEA中for的快捷写法urls.for+回车),遍历出来之后便可以使用我们上面定义的路径匹配器PATH_MATCHER;跟着的match(String pattern,String path) 就是匹配的意思,()中的第一个参数就是我们从遍历出来的一个个的url元素,第二个参数就是我们传过来的requestURI;
- 开始判断,如果返回true(即match=true)时,说明已经匹配上了,就没有必要再循环,直接返回true;如果未匹配,则返回false;
- 代码如下:
④ 在过滤器LoginCheckFilter.java中,上步方法写好之后,在上面只需要调用这个check()方法即可,代码如下:
然后通过check的返回值,就能判定我们请求的这个路径是否包含在urls当中;
(4) 如果不需要处理,则直接放行(即check=true的时候,则直接放行),代码如下:
(5) 判断登录状态,如果已登录,则直接放行
- 如果上面那一步的if不成立,就说明这次的请求需要处理,需要判断用户是否已经完成登录,如何判断?就需要从session里面获取登录用户,如果能获取出来,则直接放行。
(6) 如果未登录则返回未登录结果,通过输出流的方式向客户端响应数据;
① 如果上一步没有放行,说明没有登录,在这里不是直接跳页面,需要结合js来看,
- 在所有index.html中,都会引入一个request.js文件:
- 通过response对象,在调用write()方法,把R对象转成JSON,然后通过输出流write将它写回去,最后直接ruturn就可以。
- 注意:与request.js里面响应拦截器中的NOTLOGIN相对应;
(7) 这样过滤器LoginCheckFilter.java代码就已经编写完成,可以进行测试了;
♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡♡
2.6 完善登录功能:过滤器LoginCheckFilter代码测试:
(1) 在测试代码之前,先在代码LoginCheckFilter.java中加一些日志,方便调试
- 第一处:
- 第二处:
- 第三处:
- 第四处:
(2) 重新启动一下IDEA,然后在浏览器中,直接输入http://localhost:8080/backend/index.html+回车,就会发现,网页还会自动跳回登录页面,点击“登录”按钮登录后,才能正常跳转;
然后再回到IDEA中,查看控制面板,就会发现“拦截到请求”字样,如下图:
(3) 在这里登录页面跳转,是由backend>js>request.js里面的〔window.top.location.href = ‘/backend/page/login/login.html’〕;
① 在浏览器中调试这个js代码:在浏览器登录页面,按F12→Sources→在request.js拦截器这个地方加断点:
② 然后在浏览器中输入网址http://localhost:8080/backend/index.html+回车,查看控制面板:
3.新增员工
3.1 新增员工通过四个方面来完成:需求分析、数据模型、代码开发和功能测试;
3.2 新增员工- 需求分析:
(1) 后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。点击[添加员工]按钮跳转到新增页面,如下:
3.3 新增员工 - 数据模型:
(1) 新增员工,其实就是将我们新增页面录入的员工数据插入到employee表。需要注意,employee表中对username字段加入了唯一约束,因为username是员工的登录账号,必须是唯一的。
(2) employee表中的status字段已经设置了默认值1,表示状态正常:
- status 代表当前用户/员工账号的状态—— 0(禁用) / 1(正常);默认值是1;
- 新增员工,其实就是向表employee里插入数据;
3.4 新增员工 - 代码开发(梳理程序执行流程):
(1) 在代码开发之前,需要梳理一下整个程序的执行过程:
- 页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端;
- 服务端Controller接收页面提交的数据并调用Service将数据进行保存;
- Service调用Mapper 操作数据库,保存数据;
(2) 在浏览器中,“添加员工”页面里输入员工信息,点击【保存】按钮,按F12打开控制面板→Network:
(3) “添加员工”的代码位置:src>main>resources>backend>page>member>list.html
3.5 新增员工 - 代码开发(代码编写):
(1) 实现代码步骤:因为前端页面上请求已经发出来了,现在要做的就是在Controller里创建一个方法,把我们的页面发送的请求数据给接收到;
(2) 在IDEA中,src>main>java>com.itheima>reggie>controller>EmployeeController.java里编写代码:
解析:因为传过来的是json形式的,所以需要在方法save里面加一个注解@RequestBody
(3) 测试一下上面刚刚编写的代码:
- 在IDEA的EmployeeController.java中 log.info() 处加断点跟踪一下,要保证前端页面发送的请求能够请求到save方法,并且我们提交的参数能够封装到employee对象上;
- 然后回到浏览器首页面(http://localhost:8080/backend/page/login.html)→ 点击“登录” → 登陆成功之后,点击左上角“添加员工” → 输入员工的信息(身份证号要输入18位的数字),并点击“保存”
- 再回到 IDEA的EmployeeController.java中,就看到刚才输入的信息已经封装过来了,然后点击下图所示employee处显示的加号,跳转到参数页查看输入的信息有没有问题;
(4) 由显示的信息可以看到,密码password为空,是因为在“添加员工”时没有密码的输入框,如下图:
- 所以在这里新增员工的时候,都统一给个初始密码(123456),后期员工自己登录进来的时候可以自己修改密码;所以在 IDEA的 EmployeeController.java 中,“新增员工”处,设置初始密码:在employee对象的基础之上,设置密码 setPassword();
- 注意:在这里设置密码不要设置明文的,要用md5进行加密;
- 所以代码写法如下:
(5) 在IDEA的EmployeeController.java中,“新增员工”里手动设置其余空的属性:
① 再回到 Employee.java里面,会看到 createTime(创建时间),updateTime(更新时间),createUser(创建人),updateUser(更新人)等还都是空的,其中status在创建表的时候就默认给了1,所以在这里不用再设置;
② 创建人/当前登录用户的ID,在这里我们登录成功之后,已经把用户的ID放到session里面了,所以在save()方法的参数里面添加一个〔HttpServletRequset request〕对象来得到一个session;
③ 代码如下:
“获得当前登录用户的id”代码解析:
- 通过request.getSession().getAttribute(“employee”)
- 获得的时候是按照上面“登陆成功……”里面的setAttribute里面的k(即employee)来获得;
- 因为“登陆成功……”放进去的时候,用的是emp.getId(),所以现在取出来也是用getId;
- 因为方法getAttribute()方法返回的都是object类型,所以在这里需要添加强转,转成Long类型;
- 调用employeeService.save()方法,将employee对象传进去,最后返回成功信息:return R.success(“新增员工成功”);
- 其中employeeService调用的这个save()方法是继承了Mybatis-plus里的父接口IService里面的save()方法,在这里是直接调用的;
3.6 新增员工 - 代码测试:
(1) 现在新增员工的代码就已经写完了,启动一下项目,测试一下程序有没有问题;
(2) 回到浏览器页面(http://localhost:8080/backend/page/login/login.html),登录后添加员工,输入员工信息后点击保存;
(3) 再到IDEA中,看到控制台发了条Insert语句;
(4) 再到数据库中找到 employee表刷新一下,就能看到新加的员工数据;
3.7 新增员工 - 编写全局异常处理器
(1) 问题:现在再输入一条“账号”相同,其他内容不同的员工数据时,点击“保存”后就会显示“系统接口500异常”,因为在表格employee中的username(账号)字段加了唯一约束,不能够重复;
- 回到IDEA中,在控制台就会看到如下图的异常,说的是出现了重复的值
(2) 上面的操作出现的问题及处理方式分析:
① 前面编写的“新增员工”程序还存在一个问题,就是当我们在新增员工输入的账号已经存在时,由于employee表中对该字段加入了唯一约束,此时程序会抛出异常;
② 此时需要我们的程序进行异常捕获,通常有两种处理方式;
- 方式一:在Controller方法中加入try、catch进行异常捕获;(不建议使用)
- 方式二:使用异常处理器进行全局异常捕获;(建议使用)
try{
employeeService.save(employee);
}catch(Exception ex){
R.error("新增员工失败");
}
return R.success("新增员工成功");
(3) 编写全局异常处理器(GlobalExceptionHandler.java)
① 在 src>main>java>com.itheima>reggie>common里面新建一个全局的异常捕获类 GlobalExceptionHandler.java(即:全局异常处理器),这个类的底层是基于代理我们编写的 Controller,所以这个全局异常处理器会通过AOP(面向切面编程)把我们的save()方法等拦截到,如果抛异常,就都会在类GlobalExceptionHandler.java中的某个方法里进行处理;
② 在类GlobalExceptionHandler.java,加入注解:
③ 创建方法 exceptionHandler(),返回R对象,泛型是,加的注解是@ExceptionHandler() → 括号里面写要处理的异常;一旦Controller抛SQLIntegrityConstraintViolationException异常,就会被类GlobalExceptionHandler.java拦截到,统一在方法exceptionHandler()这里处理
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。