黑马程序员Java项目实战《瑞吉外卖》 笔记 Day2:视频P15-P25

黑马程序员Java项目实战《瑞吉外卖》 笔记Day2:视频P15-P25

本课程以当前热门的外卖点餐为业务基础,业务真实、实用、广泛。基于流行的Spring Boot、mybatis plus等技术框架进行开发,带领学员体验真实项目开发流程、需求分析过程和代码实现过程。学完本课程能够收获:锻炼需求分析能力、编码能力、bug调试能力,增长开发经验。

  • 链接:https://www.bilibili.com/video/BV13a411q753

20230319 Day2:视频P15-25

  • 完善登录功能
  • 新增员工
  • 员工信息分页查询
  • 启用/禁用员工账号
  • 编辑员工信息

完善登录功能:

问题分析:
前面我们已经完成了后台系统的员工登录功能开发,但是还存在一个问题:用户如果不登录,直接访问系统首页面,照样可以正常访问。
这种设计并不合理,我们希望看到的效果应该是,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面。
那么,具体应该怎么实现呢?
答案就是使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。

实现步骤:

1.创建自定义过滤器LoginCheckFilter
2.在启动类上加入注解@ServletComponentScan
3.完善过滤器的处理逻辑

创建filter包,在filter内创建LoginCheckFilter过滤器类,添加@WebFilter注解,设定过滤器名filterName = “LoginCheckFilter”,拦截路径urlPatterns = “/*”(全部拦截 ),继承Filter接口,并实现doFilter方法

package com.raggie.filter;

import lombok.extern.slf4j.Slf4j;

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 {
    
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
    }
}

通过强转,将servletRequest,servletResponse转型为HttpServletRequest和HttpServletResponse,并通过日志输出拦截信息,添加放行指令

HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
log.info("拦截到请求:{}",request);
filterChain.doFilter(request,response);

在启动类中添加@ServletComponentScan注解(用于扫描filter)

package com.raggie;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@Slf4j
@SpringBootApplication
@ServletComponentScan
public class ReggieApplication {
    
    
    public static void main(String[] args) {
    
    

        SpringApplication.run(ReggieApplication.class, args);
        log.info("Spring项目启动成功...");
    }
}

运行程序,控制台返回一下信息,说明拦截成功

[nio-8080-exec-1] com.raggie.filter.LoginCheckFilter : 拦截到请求:org.apache.catalina.connector.RequestFacade@286ca83e
[nio-8080-exec-2] com.raggie.filter.LoginCheckFilter : 拦截到请求:org.apache.catalina.connector.RequestFacade@286ca83e

过滤器具体的处理逻辑如下:
1.获取本次请求的URI

String requestURI = request.getRequestURI();

2.判断本次请求是否需要处理
定义一个urls数组,存储不需要处理的访问路径

String[] urls = new String[]{
    
    
        "/employee/login",
        "/employee/logout",
        "backend/**",
        "front/**"
};

定义一个路径匹配器

public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

新建一个boolean型的方法check,将定义的urls数组和requestURI传值

public boolean check(String[] urls,String requestURI){
    
    
    for(String url:urls){
    
    
    boolean match = PATH_MATCHER.match(url, requestURI);
    if(match==true)
    return true;
    }
    return false;
}

3.如果不需要处理,则直接放行

boolean check = check(urls,requestURI);
if(check){
    
    
    filterChain.doFilter(request,response);
}

4.判断登录状态,如果已登录,则直接放行

if(request.getSession().getAttribute("employee")!=null){
    
    
    filterChain.doFilter(request,response);
    return;
}

5.如果未登录则返回未登录结果,通过输出流的方式,向页面响应数据

response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));

LoginCheckFilter完整代码:

package com.raggie.filter;

import com.alibaba.fastjson.JSON;
import com.raggie.common.R;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
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();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //1.获取本次请求的URI
        String requestURI = request.getRequestURI();
        //定义不需要处理的路径
        String[] urls = new String[]{
    
    
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };
        log.info("拦截到请求:{}",request);
        //2.判断本次请求是否需要处理
        boolean check = check(urls,requestURI);
        if(check){
    
    
            // 3.如果不需要处理,则直接放行
            filterChain.doFilter(request,response);
            return;
        }
        //4.判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee")!=null){
    
    
            filterChain.doFilter(request,response);
            return;
        }
        //5.如果未登录则返回未登录结果,通过输出流的方式,向页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
    }

    /**
     * 进行路径匹配,检查本次请求是否需要放行
     * @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==true)
                return true;
        }
        return false;
    }
}

新增员工功能:

  • 需求分析
  • 数据模型
  • 代码开发
  • 功能测试

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

1.页面发送ajax请求,将新增员工页面中输入的数据以)json的形式提交到服务端
2.服务端Controller接收页面提交的数据并调用Service将数据进行保存
3.Service调用Mapper操作数据库,保存数据

创建返回值为R的方法save,并将接收到的json数据通过@注解转换类型

@PostMapping
public R<String> save(@RequestBody Employee employee){
    
    
    return null;
}

设置员工的初始密码为123456的md5加密结果

employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));

设置员工更新和创建时间为系统时间

employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());

设置员工创建和更新者为session中存储的employee(需要在参数中添加HttpServletRequest request)

Long empID = (Long) request.getSession().getAttribute("employee");
employee.setCreateUser(empID);
employee.setUpdateUser(empID);

调用employeeService的save方法,并返回R.success

employeeService.save(employee);
return R.success("新增员工成功!");

完整的save代码如下:

@PostMapping
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
    
    
    //设置初始密码为123456,进行md5加密处理
    employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
    employee.setCreateTime(LocalDateTime.now());
    employee.setUpdateTime(LocalDateTime.now());
    Long empID = (Long) request.getSession().getAttribute("employee");
    employee.setCreateUser(empID);
    employee.setUpdateUser(empID);
    employeeService.save(employee);
    log.info("新增员工,员工信息:{}",employee.toString());
    return R.success("新增员工成功!");
}

创建全局异常处理器:

在common包中新建一个GlobalExceptionHandler类,用于捕获全局的异常信息

package com.raggie.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.RestControllerAdvice;

import java.sql.SQLIntegrityConstraintViolationException;

@ControllerAdvice(annotations = {
    
    RestControllerAdvice.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
    
    
    /**
     * 异常处理方法
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
    
    
        log.error(ex.getMessage());
        return R.error("操作失败!");
    }
}

在exceptionHandler中判断,若错误信息内包含Duplicate entry,则返回xxx已存在

@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
    
    
    log.error(ex.getMessage());
    if(ex.getMessage().contains("Duplicate entry")){
    
    
        String[] split = ex.getMessage().split(" ");
        String msg=split[2]+"已存在!";
        return R.error(msg);
    }
    return R.error("未知错误!");
}

员工信息分页查询:

需求分析:

系统中的员工很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会
以分页的方式来展示列表数据。

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

1.页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端
2.服务端Controller接收页面提交的数据并调用Service查询数据
3.Service调用Mapper操作数据库,查询分页数据
4.Controller将查询到的分页数据响应给页面
5.页面接收到分页数据并通过ElementUl的Table:组件展示到页面上

在config包中新建MyBatisPlusConfig,用于配置MyBatisPlus分页插件

package com.raggie.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;

@Configuration
public class MyBatisPlusConfig {
    
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
    
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

在EmployeeController中新建Page方法,泛型为mybatis提供的实体类Page,通过分析前端请求,需要传入page/pageSize/name三个参数,并添加@GetMapping(“/page”)注解

@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
    
    
    return null;
    }
}

构造分页构造器

Page pageinfo=new Page(page,pageSize);

构造条件构造器

LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();

添加过滤条件

queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);

添加排序条件

queryWrapper.orderByDesc(Employee::getUpdateTime);

执行查询,并返回结果

employeeService.page(pageinfo,queryWrapper);
return R.success(pageinfo);

page方法完整代码如下:

@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
    
    
    log.info("接收到查询请求,page={},pagesize={},name={}",page,pageSize,name);

    //构造分页构造器
    Page pageinfo=new Page(page,pageSize);

    //构造条件构造器
    LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();

    //添加过滤条件
    queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);

    //添加排序条件
    queryWrapper.orderByDesc(Employee::getUpdateTime);

    //执行查询
    employeeService.page(pageinfo,queryWrapper);

    return R.success(pageinfo);
}

启用/禁用员工账号:

需求分析
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工 可以正常登录。
需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、 禁用按钮不显示。

在EmployeeController中新建update方法,并添加@PutMapping注解
设定employee的UpdateTime和UpdateUser

Long empID = (Long) request.getSession().getAttribute("employee");
employee.setUpdateUser(empID);
employee.setUpdateTime(LocalDateTime.now());

调用employeeService的updateById方法,更新员工信息,并返回成功。
update方法完整代码如下:

@PutMapping
public  R<String> update(HttpServletRequest request,@RequestBody Employee employee){
    
    
    log.info(employee.toString());

    Long empID = (Long) request.getSession().getAttribute("employee");
    employee.setUpdateUser(empID);
    employee.setUpdateTime(LocalDateTime.now());

    employeeService.updateById(employee);
    log.info("员工信息修改为:{}",employee.toString());
    return R.success("员工信息修改成功!");
}

编辑员工信息:

需求分析:

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

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

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

在EmployeeController中新建getById方法,传入@PathVariable Long id变量,并添加@GetMapping(“/{id}”)注解

@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id){
    
    
    return null;
}

调用employeeService的getById方法,查询员工信息,判断其是否为空,并调用R.success返回信息给前端

@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id){
    
    
    log.info("根据id查询员工信息...");
    Employee employee = employeeService.getById(id);
    if(employee!=null){
    
    
        log.info("查询到员工信息为:{}",employee.toString());
        return R.success(employee);
    }
    log.info("未查询到员工信息!");
    return R.error("未查询到对应员工信息!");
}

猜你喜欢

转载自blog.csdn.net/m0_46504802/article/details/129659399
今日推荐