瑞吉外卖项目学习笔记(二)后台系统的员工管理业务开发

一、完善登录功能

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
     */
    @Override
    public 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、获取本次请求的URI
        String 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循环都遍历完了都没有匹配上,就返回false
        return 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 {

    //自动装配
    @Autowired
    private EmployeeService employeeService;


    /**
     * 员工登录
     * @param request
     * @param employee
     * @return
     */
    //前端发送的请求是 post 请求
    @PostMapping("/login")
    //接收json数据
    //requset对象可以get
    public 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中保存的当前登录员工的id
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }

    /**
     * 新增员工
     * @param employee
     * @return
     */
    @PostMapping
    public 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());

        //获得当前登录用户的id
        Long 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 梳理程序执行流程

  1. 页面发送 ajax 请求,将分页查询参数(page、pageSize、name)提交到服务器
  2. 服务端 Controller 接收页面提交的数据并调用 Service 查询数据
  3. Service 调用 Mapper 操作数据库,查询分页数据
  4. Controller 将查询到的分页数据转成 JSON 响应给页面
  5. 页面接收到分页数据并通过 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 {
    
    //拦截器
    @Bean
    public 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 需求分析

  1. 只有管理员(admin 用户)可以对其他普通用户进行启用、禁用操作
  2. 普通用户登录系统后启用、禁用按钮不显示
  3. 账户禁用的员工不能登录系统
  4. 账户启用的员工可以正常登录
  5. 如果某个员工账户状态为正常,则按钮显示为 “禁用”
  6. 如果员工账户状态为已禁用,则按钮显示为 “启用”

4.2 代码开发

在开发代码之前,需要梳理一下整个程序的执行流程:

  1. 页面发送 ajax 请求,将参数(id、status)提交到服务端
  2. 服务端 Controller 接收页面提交的数据并调用 Service 更新数据
  3. Service 调用 Mapper 操作数据库

本质:是一个更新操作(Update),修改状态码

启用、禁用(或者是编辑)员工账号,本质上就是一个更新操作,也就是对 status 状态字段进行操作。

在 Controller 中创建 update 方法,此方法是一个通用的修改员工信息的方法。

4.3 功能测试

4.4 代码修复

五、编辑员工信息

5.1 需求分析

在员工管理列表页面点击编辑按钮,跳转到编辑页面,在编辑页面回显员工信息并进行修改,最后点击保存按钮完成编辑操作。

5.2 代码开发

在开发代码之前需要梳理一下操作过程喝对应的程序的执行流程:

  1. 点击编辑按钮时,页面跳转到 add.html ,并在 url 中携带参数【员工 id】
  2. 在 add.html 页面获取 url 中的参数【员工 id】
  3. 发送 ajax 请求,请求服务端,同时提交员工 id 参数
  4. 服务端接收请求,根据员工 id 查询员工信息,将员工信息以 json 形式响应给页面
  5. 页面接收服务端响应的 json 数据,通过 VUE 的数据绑定进行员工信息回显
  6. 点击保存按钮,发送 ajax 请求,将页面中的员工信息以 json 方式提交给服务端
  7. 服务端接收员工信息,并进行处理,完成后给页面响应
  8. 页面接收到服务端响应信息后进行相应处理

5.3 功能测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值