一、完善登录功能
1.1 问题分析
问题:index页面不用登录直接输入url也可以访问
理想效果:只有登录成功后才可以访问系统中的页面,如果没有登录, 访问系统中的任何界面都直接跳转到登录页面。
实现方式:通过过滤器或拦截器来实现,在过滤器、拦截器中拦截前端发起的请求,判断用户是否已经完成登录,如果没有登录则返回提示信息,跳转到登录页面。
1.2 思路分析
过滤器具体的处理逻辑如下:
- 获取本次请求的URI
- 判断本次请求, 是否需要登录, 才可以访问
- 如果不需要,则直接放行
- 判断登录状态,如果已登录,则直接放行
- 如果未登录, 则返回未登录结果
如果没登录,根据前端的处理来实现后端代码
1.3 代码实现
创建过滤器
创建filter包,并创建LoginCheckFilter过滤器
代码如下:
1 | /** |
在启动类上加上Servlet组件扫描的注解, 来扫描过滤器配置的@WebFilter注解, 扫描上之后, 过滤器在运行时就生效了。
@ServletComponentScan 的作用:
在SpringBoot项目中, 在引导类/配置类上加了该注解后, 会自动扫描项目中(当前包及其子包下)的@WebServlet , @WebFilter , @WebListener 注解, 自动注册Servlet的相关组件 ;
测试一下:
发现过滤器是可以生效的。
AntPathMatcher 拓展:
介绍: Spring中提供的路径匹配器 ;
通配符规则:
符号 含义 ? 匹配一个字符 * 匹配0个或多个字符 ** 匹配0个或多个目录/字符
完整代码:
1 | /** |
测试:
成功实现
二、新增员工
2.1 代码执行流程
- 点击”保存”按钮, 页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端, 请求方式POST, 请求路径 /employee
- 服务端Controller接收页面提交的数据并调用Service将数据进行保存
- Service调用Mapper操作数据库,保存数据
2.2 代码实现
在EmployeeController中增加save方法, 用于保存用户员工信息。
在新增员工时, 按钮页面原型中的需求描述, 需要给员工设置初始默认密码 123456, 并对密码进行MD5加密。
在组装员工信息时, 还需要封装创建时间、修改时间,创建人、修改人信息(从session中获取当前登录用户)。
1 | /** |
测试一下,插入成功
但是这里有一个问题,如果再次插入同账号的员工
发现这里出现系统接口500异常
控制台也报错了,因为在 employee 表结构中,我们针对于username字段,建立了唯一索引,添加重复的username数据时,违背该约束,就会报错。但是此时前端提示的信息并不具体,用户并不知道是因为什么原因造成的该异常,我们需要给用户提示详细的错误信息 。
三、全局异常处理器
3.1 思路分析
要想解决上述测试中存在的问题,我们需要对程序中可能出现的异常进行捕获,通常有两种处理方式:
A. 在Controller方法中加入 try…catch 进行异常捕获
形式如下:
如果采用这种方式,虽然可以解决,但是存在弊端,需要我们在保存其他业务数据时,也需要在Controller方法中加上try…catch进行处理,代码冗余,不通用。
B. 使用异常处理器进行全局异常捕获
采用这种方式来实现,我们只需要在项目中定义一个通用的全局异常处理器,就可以解决本项目的所有异常。
3.2 全局异常处理器
在项目中自定义一个全局异常处理器,在异常处理器上加上注解 @ControllerAdvice,可以通过属性annotations指定拦截哪一类的Controller方法。 并在异常处理器的方法上加上注解 @ExceptionHandler 来指定拦截的是那一类型的异常。
异常处理方法逻辑:
- 指定捕获的异常类型为 SQLIntegrityConstraintViolationException
- 解析异常的提示信息, 获取出是那个值违背了唯一约束
- 组装错误信息并返回
代码实现:
1 | /** |
注解说明:
上述的全局异常处理器上使用了的两个注解 @ControllerAdvice , @ResponseBody , 他们的作用分别为:
@ControllerAdvice : 指定拦截那些类型的控制器;
@ResponseBody: 将方法的返回值 R 对象转换为json格式的数据, 响应给页面;
上述使用的两个注解, 也可以合并成为一个注解 @RestControllerAdvice
测试:
成功检查出异常并返回错误信息
四、员工分页查询
4.1 需求分析
系统中的员工很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。而在我们的分页查询页面中, 除了分页条件以外,还有一个查询条件 “员工姓名”。
请求参数
搜索条件: 员工姓名(模糊查询)
分页条件: 每页展示条数 , 页码
响应数据
总记录数
结果列表
4.2 程序执行流程分析
在开发代码之前,需要梳理一下整个程序的执行过程。
A. 点击菜单,打开员工管理页面时,执行查询:
B. 搜索栏输入员工姓名,回车,执行查询:
页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端
服务端Controller接收页面提交的数据, 并组装条件调用Service查询数据
Service调用Mapper操作数据库,查询分页数据
Controller将查询到的分页数据, 响应给前端页面
页面接收到分页数据, 并通过ElementUI的Table组件展示到页面上
4.3 代码实现
要实现分页查询功能,就需要用到MybatisPlus中提供的分页插件,要使用分页插件,就要在配置类中声明分页插件的bean对象。
在config目录下新建MybatisPlusConfig类
代码如下:
1 |
|
分页查询实现
页面在进行分页查询时, 具体的请求信息如下:
请求 | 说明 |
---|---|
请求方式 | GET |
请求路径 | /employee/page |
请求参数 | page , pageSize , name |
那么查询完毕后我们需要给前端返回什么样的结果呢?
查询返回的结果数据data中应该封装两项信息, 分别为: records 封装分页列表数据, total 中封装符合条件的总记录数。
那么这个时候, 在定义controller方法的返回值类型R时, 我们可以直接将 MybatisPlus 分页查询的结果 Page 直接封装返回, 因为Page中的属性如下:
那么接下来就依据于这些已知的需求和条件完成分页查询的代码实现。 具体的逻辑如下:
A. 构造分页条件
B. 构建搜索条件 - name进行模糊匹配
C. 构建排序条件 - 更新时间倒序排序
D. 执行查询
E. 组装结果并返回
代码如下:
1 | /** |
测试:
前端正常拿到数据
后端也执行了查询
五、启动、禁用员工账号
5.1 需求分析
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。如果某个员工账号状态为正常,则按钮显示为 “禁用”,如果员工账号状态为已禁用,则按钮显示为”启用”。
==注意:只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示。==
5.2 程序执行流程
1 页面按钮动态展示
1). 当管理员admin点击 “启用” 或 “禁用” 按钮时, 调用方法statusHandle
scope.row : 获取到的是这一行的数据信息 ;
2). statusHandle方法中进行二次确认, 然后发起ajax请求, 传递id、status参数
最终发起异步请求, 请求服务端, 请求信息如下:
请求 | 说明 |
---|---|
请求方式 | PUT |
请求路径 | /employee |
请求参数 | {“id”:xxx,”status”:xxx} |
{…params} : 三点是ES6中出现的扩展运算符。作用是遍历当前使用的对象能够访问到的所有属性,并将属性放入当前对象中。
5.3 代码实现
程序的执行过程:
1). 页面发送ajax请求,将参数(id、status)提交到服务端
2). 服务端Controller接收页面提交的数据并调用Service更新数据
3). Service调用Mapper操作数据库
启用、禁用员工账号,本质上就是一个更新操作,也就是对status状态字段进行操作。在Controller中创建update方法,此方法是一个通用的修改员工信息的方法。
1 | /** |
5.4 代码修复
在前端JS中, js在对长度较长的长整型数据进行处理时, 会损失精度, 从而导致提交的id和数据库中的id不一致。
想解决这个问题,只需要让js处理的ID数据类型转为字符串类型即可, 这样就不会损失精度了。
由于在SpringMVC中, 将Controller方法返回值转换为json对象, 是通过jackson来实现的, 涉及到SpringMVC中的一个消息转换器MappingJackson2HttpMessageConverter, 所以我们要解决这个问题, 就需要对该消息转换器的功能进行拓展。
具体实现步骤:
1). 提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象到json数据的转换(资料中已经提供,直接复制到项目中使用)
2). 在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行Java对象到json数据的转换
1). 引入JacksonObjectMapper
1 | import com.fasterxml.jackson.databind.DeserializationFeature; |
该自定义的对象转换器, 主要指定了, 在进行json数据序列化及反序列化时, LocalDateTime、LocalDate、LocalTime的处理方式, 以及BigInteger及Long类型数据,直接转换为字符串。
2). 在WebMvcConfig中重写方法extendMessageConverters
1 | /** |
测试:
状态更改成功
六、编辑员工信息
6.1 程序执行流程
点击编辑按钮时,页面跳转到add.html,并在url中携带参数[员工id]
在add.html页面获取url中的参数[员工id]
发送ajax请求,请求服务端,同时提交员工id参数
服务端接收请求,根据员工id查询员工信息,将员工信息以json形式响应给页面
页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显
点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端
服务端接收员工信息,并进行处理,完成后给页面响应
页面接收到服务端响应信息后进行相应处理
注意:add.html页面为公共页面,新增员工和编辑员工都是在此页面操作
6.2 代码实现
根据ID查询
经过上述的分析,我们看到,在根据ID查询员工信息时,请求信息如下:
请求 | 说明 |
---|---|
请求方式 | GET |
请求路径 | /employee/{id} |
代码实现:
在EmployeeController中增加方法, 根据ID查询员工信息。
1 | /** |
修改员工
经过上述的分析,我们看到,在修改员工信息时,请求信息如下:
请求 | 说明 |
---|---|
请求方式 | PUT |
请求路径 | /employee |
请求参数 | {…….} json格式数据 |
代码实现:
在EmployeeController中增加方法, 根据ID更新员工信息。
1 | /** |
6.4 功能测试
数据可以正常修改