瑞吉外卖——Day02

导读:本篇文章讲解 瑞吉外卖——Day02,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

完善登录功能

问题分析

代码实现

新增员工

需求分析

​编辑数据模型

​编辑代码开发

编写全局异常处理器

完善全局异常处理器并测试

小结

员工信息分页查询

需求分析

梳理程序执行过程&代码开发

功能测试

启用/禁用员工账号

需求分析

分析页面按钮动态显示效果

​编辑

分析页面ajax请求发送过程

代码开发和功能测试

代码修复配置消息转换器

编辑员工信息

需求分析

页面效果分析


完善登录功能

问题分析

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

实现步骤:

  1. 创建自定义过滤器LoginCheckFilter
  2. 在启动类.上加入注解@ServletComponentScan(开启组件扫描,开启过滤器)
  3. 完善过滤器的处理逻辑

过滤器的处理逻辑:

瑞吉外卖——Day02

代码实现

  • 创建过滤器
package com.zqf.reggie.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;

@Slf4j
//设置过滤器名称和拦截路径
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        log.info("拦截到请求:{}",request.getRequestURI());
        filterChain.doFilter(request,response);//放行
    }
}
  • 添加扫描组件的注解
package com.zqf.reggie;


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//扫描Servlet组件
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功...");
    }
}

瑞吉外卖——Day02

  •  实现过滤器的实现逻辑
package com.zqf.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.zqf.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
//设置过滤器名称和拦截路径
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
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();

        log.info("拦截到请求:{}",requestURI);

        //不用处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/login",
                "/backend/**",       //静态资源
                "/front/**"
        };
        //2、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);
        //3、如果不需要处理,则直接放行
        if (check){
            log.info("本次请求不需要处理:{}",requestURI);
            filterChain.doFilter(request,response);//放行
            return;
        }
        //4、判断登录状态,如果已登录,则直接放行
        Object employee = request.getSession().getAttribute("employee");
        if (employee != null){
            log.info("用户已登录,id为:{}",employee);
            filterChain.doFilter(request,response);//放行
            return;
        }

        /*
          // 响应拦截器
      service.interceptors.response.use(res => {
      if (res.data.code === 0 && res.data.msg === 'NOTLOGIN') {// 返回登录页面
        console.log('---/backend/page/login/login.html---')
        localStorage.removeItem('userInfo')
        window.top.location.href = '/backend/page/login/login.html'
      } else {
        return res.data
      }
    },如果重复跳转login,请在urls放行路径中,添加 /employee/page,因为进入index时候,会自动发起/employee/page请求
         */

        //5、如果未登录则返回未登录结果    通过输出流的方式向客户端页面响应数据
        log.info("用户未登录");
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }


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

关于PATH_MATCHER的细节:Spring 概念模型 : PathMatcher 路径匹配器

新增员工

需求分析

        后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。点击[添加员工]按钮跳转到新增页面。

瑞吉外卖——Day02
数据模型

employee表中username唯一,status默认为1,表示状态正常 

瑞吉外卖——Day02
代码开发

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

    @PostMapping//无需加路径了
    public R<String> save(@RequestBody Employee employee){
        log.info("新增员工信息:{}",employee.toString());
        return null;
    }

瑞吉外卖——Day02

新增员工很多属性为空,可以设置MD5加密初始密码和其他属性

补充:fill自动填充:8.使用fill完成字段自动填充

    @PostMapping//无需加路径了
    public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
        log.info("新增员工信息:{}",employee.toString());
        //设置初始密码123456, 需要进行md5加密处理
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        //设置其他属性
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());


        Long empId = (Long)request.getSession().getAttribute("employee");
        //设置创建人,即先获取当前登录用户的id
        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);

        employeeService.save(employee);

        return R.success("新增员工成功!");
    }

功能测试

添加成功:

瑞吉外卖——Day02

小问题:

当再次输入相同用户名时, sql报错,因为username加了唯一约束

瑞吉外卖——Day02

编写全局异常处理器

瑞吉外卖——Day02

 当要处理的代码段很多很明显try catch不好用

//拦截那些类上面加了@RestController和@Controller注解的controller
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody       //将结果封装为json
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.error(ex.getMessage());
        return R.error("失败");
    }

}

        现在@Controller出现异常就会被拦截到,如果是SQLIntegrityConstraintViolationException类异常就会被分到这个函数进行处理。

结果

瑞吉外卖——Day02

完善全局异常处理器并测试

瑞吉外卖——Day02

现在就要取出001

//拦截那些类上面加了@RestController和@Controller注解的controller
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody       //将结果封装为json
@Slf4j
public class GlobalExceptionHandler {

    @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("失败");
    }

}

结果

瑞吉外卖——Day02

小结

瑞吉外卖——Day02

员工信息分页查询

需求分析

瑞吉外卖——Day02

梳理程序执行过程&代码开发


瑞吉外卖——Day02

查看前端的请求路径及其参数

瑞吉外卖——Day02

代码开发

  • 1.配置mybatis分页插件(通过拦截器的方式将插件配置进来)
package com.zqf.reggie.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;

public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
  • 2.创建controller接收查询参数(分页相关数据)

瑞吉外卖——Day02

 前端需要传入这两个数据,所以R的泛型需要填入Page,因为这个类型含有这两个参数

瑞吉外卖——Day02

瑞吉外卖——Day02

现在考虑该传入哪些参数?考虑前端会传来哪些数据那些参数:page,pageSize,name

瑞吉外卖——Day02

        现在再来考虑路径问题,前端采用get请求方式,并且路径为page,所以需要加上注解@GetMapping(“/page”)

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

结果

瑞吉外卖——Day02

  •  3.构造分页构造器,调用service相关方法

service中page方法已经在内部将各种条件封装到pageinfo里面了。

    @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);
    }

功能测试

瑞吉外卖——Day02

瑞吉外卖——Day02

没有limit应该是拦截器没写好,没加@Configuation注解

加上之后:

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

@Configuration
public class MyBatisPlusConfig {

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

瑞吉外卖——Day02

 结果

瑞吉外卖——Day02

启用/禁用员工账号

需求分析

瑞吉外卖——Day02

瑞吉外卖——Day02

分析页面按钮动态显示效果

页面中是怎么做到只有管理员admin能够看到启用、禁用按钮的?

获取当前user,判断是否为admin,并动态显示启用/禁用

瑞吉外卖——Day02

瑞吉外卖——Day02

分析页面ajax请求发送过程

瑞吉外卖——Day02

瑞吉外卖——Day02

代码开发和功能测试

瑞吉外卖——Day02

启用、禁用员工账号,本质上就是一个更新操作,也就是对status状态字段进行操作
在Controller中创建update方法,此方法是一个通用的修改员工信息的方法

确认路径和请求方式

瑞吉外卖——Day02

    /**
     * 根据id修改员工信息
     * @param employee
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody Employee employee){
        log.info(employee.toString());//查看参数是否能传进来
        return null;
    }

瑞吉外卖——Day02

瑞吉外卖——Day02

说明客户端和服务端能够正常请求处理,参数是能够正常传递的

设置参数属性之后

    /**
     * 根据id修改员工信息
     * @param employee
     * @return
     */
    @PutMapping
    public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
        log.info(employee.toString());//查看参数是否能传进来

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

        /*
        status在前端传过来的参数里,即在employee里
         */

        employeeService.updateById(employee);
        return R.success("修改成功");
    }

But出现了问题

瑞吉外卖——Day02

原因:js对于long类型只能精确到前16位,后面则做了四舍五入,所以

瑞吉外卖——Day02

瑞吉外卖——Day02

代码修复配置消息转换器

瑞吉外卖——Day02

1.在commns目录下添加类,这个类可以将java对象转换为json,也可以将json转换为 对象

package com.zqf.reggie.common;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

2.扩展消息转换器

        将controller方法中的返回结果(R对象),转换为json,然后再通过输出流的方式响应给页面,所以页面上展示的都是json数据。

    /**
     * 扩展mvc框架的消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //创建消息转换器
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到avc框架的转换器集合
        converters.add(0,messageConverter);  //转换器是有顺序的,要让转换器优先使用我们自己设置的转换器,所以设置索引为0
    }

当执行add方法之后,我们的转换器已添加,其余8个事mvc自带的消息转换器

瑞吉外卖——Day02

可以看到此时的id已经加上双引号,时间的格式也有所改变

瑞吉外卖——Day02

编辑员工信息

需求分析

瑞吉外卖——Day02

执行流程 

信息回显需要查询数据库

瑞吉外卖——Day02

页面效果分析

钩子函数,渲染页面时自动执行

瑞吉外卖——Day02

 瑞吉外卖——Day02

获取参数用来查询回显

瑞吉外卖——Day02

如果获取的id有值得话那就是编辑选项而不是add,因为这两个功能都在同一个页面进去

瑞吉外卖——Day02

瑞吉外卖——Day02

瑞吉外卖——Day02

当服务端响应过来之后,就会调用回调函数,如果res.code = 1(成功的状态码)就数据回显

瑞吉外卖——Day02

controller添加查询信息 

        @GetMapping("/{id}")        //注意id是个路径变量  通过以下方式获取
        public R<Employee> getById(@PathVariable Long id){
            log.info("根据id查询信息");
            Employee byId = employeeService.getById(id);
            if (byId!=null){
                return R.success(byId);//确保一定是查出来的
            }
            return R.error("没有查询到员工信息");
        }

效果

瑞吉外卖——Day02

瑞吉外卖——Day02

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99436.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!