一、完善登录功能
1.1 问题分析
1.2 代码实现
package com.itheima.reggie.filter;//这是一个过滤器类
//登录检查过滤器import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 检查用户是否已经完成登录*/
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {//路径匹配器,支持通配符写法(专门用来路径比较的)public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();/*** 过滤的方法* @param servletRequest* @param servletResponse* @param filterChain* @throws IOException* @throws ServletException*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;/*** 1、获取本次请求的URI* 2、判断本次请求是否需要处理(是否需要检查用户已经登录了)【检查登录状态】* 3、如果不需要处理,则直接放行* 4、判断登录状态,如果已登录,则直接放行* 5、如果未登录则返回未登录结果*///1、获取本次请求的URIString requestURI = request.getRequestURI();//日志:拦截到的请求log.info("拦截到的请求:{}", requestURI);//2、判断本次请求是否需要处理(是否需要检查用户已经登录了)【检查登录状态】//定义一些不需要处理的请求路径(直接放行),只拦截针对Controller的请求String[] urls = new String[]{"/employee/login","/employee/logout","/backend/**","/front/**"};//判断是否需要处理boolean check = check(urls, requestURI);//3、如果不需要处理,则直接放行//check = true时不需要处理if (check) {log.info("本次请求{}不需要处理", requestURI);//放行filterChain.doFilter(request, response);return;}//4、判断登录状态,如果已登录,则直接放行if (request.getSession().getAttribute("employee") != null) {log.info("用户已登录,用户id为{}", request.getSession().getAttribute("employee"));//放行filterChain.doFilter(request, response);return;}log.info("用户未登录");//5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));return;}/*** 路径匹配,检查本次请求是否需要放行* @param urls* @param requestURI* @return*///封装方法public boolean check(String[] urls,String requestURI) {for (String url : urls) {boolean match = PATH_MATCHER.match(url, requestURI);if (match) {return true;}}//整个for循环都遍历完了都没有匹配上,就返回falsereturn false;}
}
1.3 功能测试
二、新增员工
2.1 需求分析
2.2 数据模型
2.3 代码开发
package com.itheima.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {//自动装配@Autowiredprivate EmployeeService employeeService;/*** 员工登录* @param request* @param employee* @return*///前端发送的请求是 post 请求@PostMapping("/login")//接收json数据//requset对象可以getpublic R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){/*** 1、将页面提交的密码password进行md5的加密处理* 2、根据页面提交的用户名username查询数据库* 3、如果没有查询到则返回登录失败的结果* 4、密码比对,如果不一致则返回登录失败结果* 5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果* 6、登录成功,将员工id存入Session并返回登录成功结果*/// 1、将页面提交的密码password进行md5的加密处理//从employee中把password拿到String password = employee.getPassword();//调用工具类中的md5加密的方法password = DigestUtils.md5DigestAsHex(password.getBytes());//2、根据页面提交的用户名username查询数据库LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();//添加查询条件queryWrapper.eq(Employee::getUsername, employee.getUsername());//数据库已经对user_name做了唯一约束Employee emp = employeeService.getOne(queryWrapper);//3、如果没有查询到则返回登录失败的结果if(emp == null){return R.error("登录失败");}//4、密码比对,如果不一致则返回登录失败结果if(!password.equals(emp.getPassword())){//密码匹配不成功return R.error("登录失败");}//登录成功//5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果if (emp.getStatus() == 0){return R.error("账号已经被禁用");}//6、登录成功,将员工id存入Session并返回登录成功结果request.getSession().setAttribute("employee", emp.getId());//这是我们从数据库中查出来的对象return R.success(emp);}/*** 退出方法*//*** 员工退出* @param request* @return*/@PostMapping("/logout")public R<String> logout(HttpServletRequest request){//清理Session中保存的当前登录员工的idrequest.getSession().removeAttribute("employee");return R.success("退出成功");}/*** 新增员工* @param employee* @return*/@PostMappingpublic R<String> save(HttpServletRequest request,@RequestBody Employee employee){log.info("新增员工,员工信息:{}",employee.toString());//设置初始密码:123456,需要进行md5加密处理。getBytes():设置成getBytes()数组employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));//登录时间和更新时间employee.setCreateTime(LocalDateTime.now());employee.setUpdateTime(LocalDateTime.now());//获得当前登录用户的idLong empId = (Long) request.getSession().getAttribute("employee");employee.setCreateUser(empId);employee.setUpdateUser(empId);//保存对象employeeService.save(employee);//新增员工成功return R.success("新增员工成功");}
}
package com.itheima.reggie.common;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import java.sql.SQLIntegrityConstraintViolationException;/*** 全局异常捕获处理* RestController.class, Controller.class:只有有这两个注解的类都会被我们这个类来处理*/
@ControllerAdvice(annotations = {RestController.class, Controller.class}) //通知
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {/*** 异常处理方法* @return*/@ExceptionHandler(SQLIntegrityConstraintViolationException.class)public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {log.error(ex.getMessage());//判断异常获取信息中是否有:Duplicate entry(重复条目)if(ex.getMessage().contains("Duplicate entry")){//根据空格进行分割,把异常信息存储到 split数组中String[] split = ex.getMessage().split(" ");//获取数组中已经用户名信息(唯一约束)String msg = split[2] + "已存在";//输出错误信息(账户已存在的信息)//return 把错误信息输出到页面上return R.error(msg);}//显示到页面的信息return R.error("未知错误");}
}
2.4 功能测试
2.5 总结
1、根据产品原型明确业务需求
2、重点分析数据的流转过程和数据格式
3、通过debug断点调试跟踪程序执行过程
三、员工信息分页查询
3.1 需求分析
在后台显示界面,一页显示出所有员工信息不利于查看。
解决方法:将员工信息进行分页展示
- 输入框:可以添加过滤条件,在添加过滤条件的同时进行分页处理
- 页码展示、可以跳转到相应的页码、也可直接点击相应的页码
3.2 代码开发
3.2.1 梳理程序执行流程
- 页面发送 ajax 请求,将分页查询参数(page、pageSize、name)提交到服务器
- 服务端 Controller 接收页面提交的数据并调用 Service 查询数据
- Service 调用 Mapper 操作数据库,查询分页数据
- Controller 将查询到的分页数据转成 JSON 响应给页面
- 页面接收到分页数据并通过 ElementUI 的 Table 组件展示到页面上
分页插件的使用:
MyBatisPlus 给我们提供了一个分页插件。
package com.itheima.reggie.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 配置MybatisPlus 的分页插件,配置类要加 @Configuration 注解*/
@Configuration
public class MyBatisPlusConfig {//拦截器@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mybatisPlusInterceptor;}
}
服务端 Controller 接收页面提交的数据并调用 Service 查询数据
//返回泛型Page,这个是MyBatisPlus 封装的类//方法中的形参指的是:前端页面传递给我们的值/*** 员工信息的分页查询* @param page* @param pageSize* @param name* @return*/@GetMapping("/page")public R<Page> page(int page, int pageSize, String name){log.info("page = {},pageSize = {},name = {}",page,pageSize,name);return null;}
分页查询设置
//返回泛型Page,这个是MyBatisPlus 封装的类//方法中的形参指的是:前端页面传递给我们的值/*** 员工信息的分页查询* @param page* @param pageSize* @param name* @return*/@GetMapping("/page")public R<Page> page(int page, int pageSize, String name){log.info("page = {},pageSize = {},name = {}",page,pageSize,name);//底层是基于MyBatisPlus提供的分页插件进行分页//1、构建分页构造器(分页条件:告诉MyBatisPlus我要查第几页,第几条)Page pageInfo = new Page(page, pageSize);//2、构造条件构造器(封装过滤分页条件)LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();//添加过滤条件,like查询//判断name是否为null,然后再来添加条件queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);//添加排序条件(就是相当于在SQL语句中加一个OrderBy)queryWrapper.orderByDesc(Employee::getUpdateTime);//3、执行查询employeeService.page(pageInfo,queryWrapper);return R.success(pageInfo);}
3.3 功能测试
四、启动 / 禁用员工账号
4.1 需求分析
- 只有管理员(admin 用户)可以对其他普通用户进行启用、禁用操作
- 普通用户登录系统后启用、禁用按钮不显示
- 账户禁用的员工不能登录系统
- 账户启用的员工可以正常登录
- 如果某个员工账户状态为正常,则按钮显示为 “禁用”
- 如果员工账户状态为已禁用,则按钮显示为 “启用”
4.2 代码开发
在开发代码之前,需要梳理一下整个程序的执行流程:
- 页面发送 ajax 请求,将参数(id、status)提交到服务端
- 服务端 Controller 接收页面提交的数据并调用 Service 更新数据
- Service 调用 Mapper 操作数据库
本质:是一个更新操作(Update),修改状态码
启用、禁用(或者是编辑)员工账号,本质上就是一个更新操作,也就是对 status 状态字段进行操作。
在 Controller 中创建 update 方法,此方法是一个通用的修改员工信息的方法。
4.3 功能测试
4.4 代码修复
五、编辑员工信息
5.1 需求分析
在员工管理列表页面点击编辑按钮,跳转到编辑页面,在编辑页面回显员工信息并进行修改,最后点击保存按钮完成编辑操作。
5.2 代码开发
在开发代码之前需要梳理一下操作过程喝对应的程序的执行流程:
- 点击编辑按钮时,页面跳转到 add.html ,并在 url 中携带参数【员工 id】
- 在 add.html 页面获取 url 中的参数【员工 id】
- 发送 ajax 请求,请求服务端,同时提交员工 id 参数
- 服务端接收请求,根据员工 id 查询员工信息,将员工信息以 json 形式响应给页面
- 页面接收服务端响应的 json 数据,通过 VUE 的数据绑定进行员工信息回显
- 点击保存按钮,发送 ajax 请求,将页面中的员工信息以 json 方式提交给服务端
- 服务端接收员工信息,并进行处理,完成后给页面响应
- 页面接收到服务端响应信息后进行相应处理