• 【SpringBoot项目】SpringBoot项目-瑞吉外卖【day02】员工管理业务开发


    🌕博客x主页:己不由心王道长🌕!
    🌎文章说明:SpringBoot项目-瑞吉外卖【day02】员工管理业务开发🌎
    ✅系列专栏:SpringBoot项目
    🌴本篇内容:对黑马的瑞吉外卖项目的day02进行笔记和项目实现🌴
    ☕️每日一语:人有退路,就有些许安全感。等到哪一天,你真没了退路,你就发现眼前哪条路都能走,也能通。☕️
    🚩 交流社区:己不由心王道长(优质编程社区)

    前言

    今天是项目开发的第二天。当然,我不是第二天就写好了相应的功能,毕竟能力有限。照猫画虎也得自己思考思考再起笔吧!

    员工管理业务开发

    完善登录功能
    问题分析

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

    代码实现

    实现步骤如下:
    一、创建自定义过滤器LoginCheckFilter

    见名知意嘛,登录检查过滤器:

    package com.example.filter;
    
    /**
     * @author 不止于梦想
     * @date 2022/11/13 17:23
     */
    public class LoginCheckFilter {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二、在启动类上加入注解@ServletComponentScan:

    SpringBootApplication 上使用@ServletComponentScan 注解后
    Servlet可以直接通过@WebServlet注解自动注册
    Filter可以直接通过@WebFilter注解自动注册
    Listener可以直接通过@WebListener 注解自动注册

    其实就是组件扫描,而@ServletComponentScan顾名思义就是扫描Servlet技术相关的注解进行注册,并加载成bean。

    在这里插入图片描述
    在这里要提醒以下,SpringBoot的启动类要在所有其他包的同层或者父层,这样才能扫描到,不然是扫描不到的。

    三、完善过滤器处理逻辑
    1、获取本次请求的URI
    2、判断本次请求是否需要处理
    3、如果不需要处理,直接放行
    4、需要处理的则判断登录状态,如果已经登录,则直接放行
    5、如果未登录则返回登录结果
    在这里插入图片描述
    上面第五步返回登录结果时不能直接返回,还得看前端代码:

    // 响应拦截器
      service.interceptors.response.use(res => {
          console.log('---响应拦截器---',res)
          // 未设置状态码则默认成功状态
          const code = res.data.code;
          // 获取错误信息
          const msg = res.data.msg
          console.log('---code---',code)
          if (res.data.code === 0 && res.data.msg === '未登录') {// 返回登录页面
            // MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
            //     confirmButtonText: '重新登录',
            //     cancelButtonText: '取消',
            //     type: 'warning'
            //   }
            // ).then(() => {
            // })
            console.log('---/backend/page/login/login.html---',code)
            localStorage.removeItem('userInfo')
            window.top.location.href = '/backend/page/login/login.html'
          } else {
            return res.data
          }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    上面是一个前端响应拦截器,就是我们发请求,后台处理给的响应信息会被响应拦截器截取。当我们未登录返回登录结果时,应当按照它给的要求格式返回,这样前端的代码才能正确处理并执行正确的操作(好鸡肋,感觉严重耦合在一起)。

    这个过程中的难点是判断是否需要处理。那么我们来说说什么情况下需要处理,什么情况下需要放行。

    一、前端向后台的controller层发送的请求需要处理。这个毋庸置疑。
    二、静态资源应该放行。什么静态资源?所有静态资源,这样岂不是让别人都能看到你的资源了?看到就看到呗,真正有用的数据都要走后端的controller获取,他看到你的页面也没事,你的数据并不会被看到
    三、退出、登录请求放行。要退出就已经说明人已经登录了,要登录当然放行,不然就死循环了。
    
    • 1
    • 2
    • 3

    上面分析已经做好了,现在应该把代码整一整

    package com.example.filter;
    
    import com.alibaba.fastjson.JSON;
    import com.example.commons.R;
    import org.springframework.expression.spel.CodeFlow;
    import org.springframework.util.AntPathMatcher;
    
    import javax.management.modelmbean.RequiredModelMBean;
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.Writer;
    
    /**
     * @author 不止于梦想
     * @date 2022/11/13 17:23
     */
    @WebFilter(filterName = "loginFilter" ,urlPatterns = "/*")
    public class LoginCheckFilter implements Filter {
        private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                             FilterChain filterChain) throws IOException, ServletException {
            //先把请求和响应转换为http格式的,因为这是协议
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
    //        1、获取本次请求的URI
            String requestURI = httpServletRequest.getRequestURI();
    //        2、定义不需要处理的请求路径
            String[] urls = new String[]{//这里定义需要放行的urls
                    "/employee/login",
                    "/employee/logout",
                    "/backend/**",
                    "/front/**"
            };
    //        3、如果不需要处理,直接放行
            if (urlCheck(requestURI,urls)) {
                filterChain.doFilter(httpServletRequest,httpServletResponse);
                return;
            }
    //        4、需要处理的则判断登录状态,如果已经登录,则直接放行
            if (httpServletRequest.getSession().getAttribute("employee")!=null) {
                filterChain.doFilter(httpServletRequest,httpServletResponse);
                return;
            }
    //        5、如果未登录则返回登录结果
            httpServletResponse.getWriter().write(JSON.toJSONString(R.error("未登录")));
        }
        public boolean urlCheck(String url,String[] urls){
            for (String s : urls) {
                if (PATH_MATCHER.match(s,url)) {
                    return true;
                }
            }//如果请求与需要放行的请求不匹配,则返回false。
            return false;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    在第五步,是我们要重点注意的,因为前端的响应拦截器需要的信息是这样的

    在这里插入图片描述
    统一格式中的error中的code都统一为了0,msg是我们自己可以设置的,前端需要什么,我们就给他返回什么。
    在这里插入图片描述

    说明:

    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    
    • 1

    这是一个路径匹配器,我们在设置"/backend/**",这样的路径过滤时,如果遇到/backend/index.html时,路径并不能匹配上,/ ** 是拦截所有的文件夹及里面的子文件夹,但是当前文件夹有静态页面时则不会拦截,而路径匹配器则会让器拦截所有属于它的东西。

    功能测试

    在这里插入图片描述
    在每一步的后面根据实际情况进行日志输出,这样更能观察我们的程序到底走了哪些操作。

    现在我不登录直接进行index.html页面试试:
    在这里插入图片描述
    可以清晰的看到,我只是发了一个index.html页面。我们在进行未登录时直接访问index.html页面,它应该给我判断未登录,然后返回登录界面。但是由于我们开发了所有的静态资源,所以不需要处理,但是在index界面上会自动发一个获取page的controller请求,这时候会判断用户是否已经登录,没有登录则回退到登录界面。

    这里逻辑全部正确,就是视图并没有进行跳转,算是一个败笔,暂时没有找出解决办法。

    新增员工
    需求分析

    我们在系统中可以管理员工的信息,可以通过新增员工来添加后台系统的用户。当我们点击【添加员工】按钮则视图进行相应跳转:
    在这里插入图片描述
    在这里插入图片描述
    当我们输入数据以后,会在前端先进行一个格式校验,如手机号码和省份证号:
    在这里插入图片描述
    手机号码必须是11位数字,身份证则是18位。在以上信息都输入正确以后,点击保存按钮即可,如果保存以后还要继续添加,则点击保存并继续添加。

    在这里插入图片描述
    在保存、取消等按钮的后面都绑定了单击事件,我们看看保存的单击事件是什么样的。
    在这里插入图片描述
    过程我已经梳理了大概,所以主角是谁?是addEmployee,在我们单击保存后,由于表单已经绑定了这个事件,那么表单会作为一个数据去调用方法,传给这个方法。
    在这里插入图片描述
    可以看出来,这个函数其实也是发送一个axios请求,不同的是这个方法带有参数,就是把表单填的数据传给后端的controller,路径是/employee,类型是post。

    数据模型

    分析好了需求以后,我们看看数据模型,为什么呢?先看再说!!!
    在这里插入图片描述
    这里没有拿出id,因为id是利用雪花算法自动生成的。
    上面的由用户填写的是name(用户名)、username(员工姓名)、phone(电话号码)、sex(性别)、id_number(身份证号)。
    其他都是在添加用户时,由后端自动生成的、密码是统一的,后面由员工根据自己的需求进行更改。

    代码开发

    根据分析和模型,我们现在可以编写相应的方法了:

    @PostMapping
        public R<String> addEmployee(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");
            //添加创建者信息
           employee.setCreateUser(empId);
           //设置更新人员信息
           employee.setUpdateUser(empId);
           //调用添加方法
            employeeService.save(employee);
            //返回结果
            return R.success("新增员工成功");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    功能测试

    在这里插入图片描述
    可以看到,我们已经测试通过了,但是上面还是存在一些问题的,什么问题?异常的问题

    统一处理异常

    在这里插入图片描述
    报的是后台500错误,我们查看后台
    在这里插入图片描述
    什么意思,不知道,百度查查:
    在这里插入图片描述
    说的是username重复了,原来是我们加入数据的时候,用户名重复了,这是不允许的。

    所以要处理这种情况,这时候就需要我们的异常处理上场了。我们可以一个一个异常处理,也可以一类异常处理,当然是选择多的啦。

    package com.example.commons;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    import java.sql.SQLIntegrityConstraintViolationException;
    
    /**
     * @author 不止于梦想
     * @date 2022/11/14 18:27
     */
    @ControllerAdvice(annotations = {Controller.class, RestController.class})
    @ResponseBody
    @Slf4j
    public class GlobalExcption {
    
        @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
        public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
            //后台输出异常信息
            log.info(ex.getMessage());
            //Duplicate entry '123' for key 'employee.idx_username'
            //如果异常信息包括Duplicate entry,则可以确定是用户名字重复
            if(ex.getMessage().contains("Duplicate entry")){
                //以空格为分隔符分割异常信息
                String[] s = ex.getMessage().split(" ");
                //取出数组中的用户名,返回给客户端,提升他名字重复
                return R.error(s[2]+"已存在");
            }
            //否则,返回未知名错误
            return R.error("未知名错误,请重新输入信息");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    验证:
    在这里插入图片描述

    要说明的是,这里的功能还十分的不完善,比如手机号肯定不能重复,如果重复应该提示,该手机号已经绑定,身份证肯定不能重复吧,员工姓名可以重复,同名的人多了去了。这是后台的处理逻辑,那前台的呢?哪里出错你应该在哪提示,并且删除表格里的数据吧?只能说我们不是前端的,不能瞎动前端代码,但是这些代码漏洞很多。

    员工信息分页查询
    需求分析

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

    首先我们要看的是,当我们点击登录并且进入index.html之后,页面会发送一个controller的请求查询page,还记得吧?
    在这里插入图片描述
    并且是通过这个请求走过滤器的controller判断路径判断是否已经登录,没有登录则回退到登录页面,但是现在不研究这个,要研究的是它发送的page请求。

    无非就是下面几种情况
    在这里插入图片描述
    而它们都会走/employee/page这个controller路径。
    需要注意的是,我们发送请求时,不一定会按条件查,比如我们不一定输入员工姓名。但是这个请求发送的时候,对应的页码和每页的条数是一定存在的,你不指定也有默认值。

    整个过程梳理:
    在这里插入图片描述

    接下来看看前端的代码:
    在这里插入图片描述
    当我们进入到index界面,并且是在员工管理界面时,会自动加载一个页面,页面url如图,我们跟进去。
    在这里插入图片描述

    创建了一个vue,并且绑定了member-app,并且初始化一个init方法。
    在这里插入图片描述
    我们看看这个init方法
    在这里插入图片描述
    方法就是这个getMemberList,一样的,跟过去看看它要干嘛。
    在这里插入图片描述
    放松一个axios请求,方式是get方式,传入params参数,路径是/employee/page。
    params参数有page、pageSize、name。

    梳理思路:
    员工管理界面,当我们进去的时候会有默认提供的参数请求后台并且查询数据,我们也可以手动的选择我们的查询条件,比如按姓名并且同时有每页多少条记录,或者直接点击第几页,输入每页多少条数。总得来说,就是按姓名不一定有,但是第几页和每页多少条记录是必然存在的。所以编写代码的时候要判断是否有姓名。

    代码开发

    上面已经把前端请求需要的东西和思路都理清了一遍,现在编写代码。
    因为这里用的分页,而我们使用的MP为我们提供了分页插件,我们需要编写一个拦截器去拦截分页请求

    package com.example.config;
    
    /**
     * @author 不止于梦想
     * @date 2022/11/14 20:15
     */
    
    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;
    
    /**
     * 配置MP的分页插件
     */
    @Configuration//声明为配置类
    public class MybatisPlusConfig {
    
        @Bean//交给Spring容器管理
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            //创建MybatisPlus拦截器
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            //添加分页拦截器
            mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            //返回分页拦截器
            return mybatisPlusInterceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    有了分页插件,现在我们使用分页方法就会被拦截,并进行处理
    先创建分页构造器
    在这里插入图片描述
    代码如下:

    @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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里在页面展示顺序的时候,是按照更新时间降序输出的

    功能测试

    看看,不指定姓名能不能查询:
    在这里插入图片描述
    pageSize是10,但是明显不对,这应该是前端的问题,我们再看看。
    在这里插入图片描述
    不对的时候记得清理一下缓存就好了,啊哈哈。
    在这里插入图片描述
    不过缓存清理了,可能你的页面图片又没有了,好迷茫。

    启用/禁用员工
    需求分析

    在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。
    需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户警录系统后启用禁用按钮不显示。
    在这里插入图片描述
    管理员admin登录系统可以对所有员工账号进行启用、禁用操作如果某个员工账号状态为正常,则按钮显示为“禁用”,如果员工账号状态为已禁用,则按钮显示为“启用(只有禁用了你才需要启用,只有启用的状态你才能禁用)。

    看看前端代码吧:
    在这里插入图片描述
    当点击后面的禁用/启用按钮时,先调用一个statusHandle函数,并传入scope.row作为参数,在传参前前端判断你的账号是否时admin用户,是的话则弹出一个对话框,就判断你当前要调整的用户的状态,当前的用户是启用的,那么参数传的就是禁用,与之对应的是当前用户已经被禁用,那么参数就是启用(只有已经被禁用的用户需要启用),然后执行函数:
    在这里插入图片描述
    弹出一个选择框:
    在这里插入图片描述
    你选择了确定,那么久根据你传的参数对该用户进行相应的禁用和启用:
    重点是enableOrDisableEmployee方法和它传入的参数:
    在这里插入图片描述
    这里通过传入被操作对象的id和被操作对象的状态的取反结果。
    然后执行以下function:

    在这里插入图片描述
    发送一个put请求,并且路径是/employee,带传入的参数。

    返回值是相应的状态码,我们的controller返回值应该是R
    在这里插入图片描述

    代码实现

    前面忘了说,其实我们能不能看见后面操作有编辑,前端已经帮我们判断了,不是admin用户,你看都看不到。

    @PutMapping
        public R<String> updateStatus(HttpServletRequest request,@RequestBody Employee employee) {
            /**
             * 就是一个判断语句,当id值相等时,把数据库的status,
             * 修改为参数的status,参数的status已经跟原本的status取反了。
             * 这里要注意的是,我们每次修改信息的时候,都会把修改人的信息和修改时间也进行更新,所以需要HttpServletRequest
             * 来获取当前操作人的session,从而获取修改人的id。
             */
            log.info(employee.toString());
            //获取操作人id
            Long empId = (Long) request.getSession().getAttribute("employee");
            //设置修改人
            employee.setUpdateUser(empId);
            //设置修改时间
            employee.setUpdateTime(LocalDateTime.now());
            //调用方法,修改user
            boolean b = employeeService.saveOrUpdate(employee);
            //判断修改是否成功
            if(b){
                return R.success("员工信息修改成功");
            }
            return R.error("员工信息修改失败");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    测试

    在这里插入图片描述
    测试没有报错,但是有问题,什么问题?看到Parameters,是不是看起来跟我们的原有数据不一样?
    因为长度太长了,前端处理时丢失精度了。怎么解决?
    我们可以在服务端给页面响应json数据时进行处理,将long型数据统一转为String字符串,
    这里直接引用官方的
    JacksonObjectMapper:

    package com.example.commons;
    
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    有了对象转换器之后,在mvc配置文件种配置即可:

    @Override
        protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            log.info("扩展消息转换器...");
            //创建消息转换器对象
            MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
            //设置对象转换器,底层使用Jackson将Java对象转为json
            messageConverter.setObjectMapper(new JacksonObjectMapper());
            //将上面的消息转换器对象追加到mvc框架的转换器集合中
            converters.add(0,messageConverter);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    add(0,messageConverter);表示我们自定义的转换器放在转换器首部位置,优先使用我们自定义的转换器。

    再测试:
    在这里插入图片描述
    在这里插入图片描述
    可以看到id已经正常的传过来了,至于其中原理,以后再慢慢深究吧,会用再说。

    编辑员工信息
    需求分析

    在这里插入图片描述
    当我们点击编辑时,程序应该通过被修改的用户的id,去后台调用查询方法然后在上面的页面进行回显。当我们点击保存的时候,其实就是一个更新方法,前面我们已经写过一个通用的更新方法。就是update方法。所以这里我们只需要编写一个通过id查询用户的方法即可。我们看看前端代码:

    在这里插入图片描述
    下面是函数:st是判断当前要走的方法,如果st不等于add,则进行修改操作。在add.html上并且携带被修改对象的id。
    在这里插入图片描述
    当我们点击编辑的时候,走的应该是修改员工,所以这个方法不用写了。
    需要写的时通过id查询员工。
    点击编辑跳出以下信息,说明我们需要回显的数据是通过request方法,并且携带参数的。
    在这里插入图片描述

    代码实现

    上面已经分析清楚了,现在把代码完善:
    selectById

      @RequestMapping("/{id}")
        public R<Employee> selectById(@PathVariable Long id){
            log.info("查询用户id");
            Employee employee = employeeService.getById(id);
            if(employee!=null){
                return R.success(employee);
            }
            return R.error("没有查询到用户");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    功能测试

    当我们点击修改大朗时,跳转页面:
    在这里插入图片描述

    现在我更改其手机号码:13512345678
    注意看:这个选项…
    在这里插入图片描述
    ok兄弟们,看它看它,已经完成了,证明我们的程序没有错误。

    总结

    这是本次项目的第二天,由于各种关系,其实我没有在第二天就写好,而是推迟了一天,在第三天晚上才把第二天的内容整理好,为什么这么慢呢?一是时间不充裕,尤其是现在临近期末,也得为期末早做打算。还有就是,咱写代码,总得自己思考吧。不能把老师的抄了就是自己的啦。
    在做这个项目中,可以发现,基本上都是crud,但是有的地方又需要想象力。比如全局处理异常,要想想出现什么情况下用到它,这些都是需要经验的,而项目就是积累经验的过程,所以兄弟们,咱项目得好好做。

  • 相关阅读:
    分布式机器学习:逻辑回归的并行化实现(PySpark)
    适合小团队的任务管理软件有哪些?5款小团队使用最多的项目工具
    法语翻译器在线翻译器
    周总结【java项目】
    opencv笔记 -- 核心功能模块(Mat)
    Redis——事务,锁机制,秒杀案例 !!!!
    基于GO语言实现的X.509证书
    【细度经典】阅读spring security 官方文档Architecture部分
    vite(setup语法糖)+ts+vant+axios入门教程
    关于Go中两个模块互相调用的场景解决方案
  • 原文地址:https://blog.csdn.net/qq_63992577/article/details/127831416