瑞吉外卖 —— 3、员工管理

目录

1、新增员工

1.1、分析

1.1.1、需求分析

1.1.2、接口分析

1.1.3、操作逻辑

1.2、代码

1.2.1、全局异常捕获

1.2.2、controller 方法

2、员工信息分页查询

2.1、分析

2.2、代码

2.2.1、MybatisPlus 分页插件

2.2.2、controller 方法

3、启用和禁用员工账号

3.1、分析

3.2、代码

3.2.1、controller 方法

3.2.2、Long 精度丢失问题

4、编辑员工信息

4.1、分析

4.2、代码

4.2.1、controller 方法


1、新增员工

1.1、分析

1.1.1、需求分析

新增员工,将新增页面录入的员工数据添加到 employee 表。注意:employee 表中对 username 字段加入了唯一的约束,因为 username 是员工的登陆账号,必须是唯一的! employee 表中的 status 字段默认设置为 1,表示员工状态可以正常登陆

1.1.2、接口分析

输入信息后,点击保存,发送 POST 请求 http://localhost:8080/employee ,且含有添加的员工信息

 在页面点击保存按钮后,前端执行 addMemberHandle 方法,将 iframe 的内容转为添加员工页面

点击保存按钮后,前端执行 submitForm 方法,首先对填入的内容进行校验

1.1.3、操作逻辑

1、页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端

2、服务端Controller接收页面提交的数据并调用Service将数据进行保存

3、Service调用Mapper操作数据库,保存数据

1.2、代码

1.2.1、全局异常捕获

由于新增员工时,若用户名 username 已存在,保存时会报错  SQLIntegrityConstraintViolationException 异常错误,错误信息例如:Duplicate entry 'zhangsan' for key 'idx_username'。因此新增一个全局异常处理类

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;

/**
 * @Author zhang
 * @Date 2022/8/31 - 17:30
 * @Version 1.0
 */
// 全局异常处理
@ControllerAdvice(annotations = {RestController.class, Controller.class})   // 拦截添加了指定注解的类的方法
@ResponseBody   // 转为JSON数据
@Slf4j
public class GlobalExceptionHandle {

    /**
     * 异常处理方法:主键不唯一,异常信息包含Duplicate entry
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException exception){
        log.error(exception.getMessage());
        if(exception.getMessage().contains("Duplicate entry")){
            String[] split = exception.getMessage().split(" ");
            String msg = split[2] + " 已存在";
            return R.error(msg);
        }
        return R.error("未知错误");
    }

}

1.2.2、controller 方法

    /**
     * 新增员工
     * @param employee
     * @param request
     * @return
     */
    @PostMapping
    public R<String> save(
            @RequestBody Employee employee,
            HttpServletRequest request
    ){
        //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("新增员工成功");
    }

2、员工信息分页查询

2.1、分析

员工信息页面是 /backend/page/member/list.html ,刚打开页面就会发送获取员工信息的请求是通过 created (Vue创建完成后) 完成的。

2.2、代码

2.2.1、MybatisPlus 分页插件

为了方便分页,先配置 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;

/**
 * @Author zhang
 * @Date 2022/8/31 - 19:22
 * @Version 1.0
 */
// 配置MyBatisPlus的分页插件
@Configuration
public class MyBatisPlusConfig {

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

}

2.2.2、controller 方法

    /**
     * 员工信息分页查询
     * @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);
        // 构造分页构造器
        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);
    }

3、启用和禁用员工账号

3.1、分析

在员工管理列表页面中,可以对某个员工账号进行启用或者是禁用操作。账号禁用的员工不能登陆系统,启用后的员工可以正常登陆。

注意:只有管理员(admin用户)才可以对其他普通用户进行启用操作,禁用操作,普通用户登录系统后启用,禁用按钮不显示。并且如果某个员工账号的状态为正常,则按钮显示为’‘禁用’,如果员工账号状态为已禁用,则按钮显示为“启用”。普通员工登录系统后,启用,禁用按钮不显示;

禁用、启用显示的前端流程:

① 访问员工信息前要登录,而登录后会将员工信息存储到浏览器的 localStorage 中,而 list.html 在 created 生命周期会获取存储在 localStorage 的员工信息的 username,如下

② 在表格中,前端会判断登录用户的 username,若为 admin,则显示禁用、启用按钮,否则不显示,显示启用还是禁用则是通过员工账号状态来判断

处理逻辑:

 请求发送过程

3.2、代码

3.2.1、controller 方法

    /**
     * 根据id修改员工信息
     * @param employee
     * @return
     */
    @PutMapping
    public R<String> update(
            @RequestBody Employee employee,
            HttpServletRequest request
    ){
        //log.info(employee.toString());
        employee.setUpdateUser((Long) request.getSession().getAttribute("employee"));
        employee.setUpdateTime(LocalDateTime.now());
        employeeService.updateById(employee);
        return R.success("员工信息修改成功");
    }

3.2.2、Long 精度丢失问题

测试的时候我们发现出现了问题,就是我们修改员工的状态,提示信息显示修改成功,但是我们去数据库查验证的时候,发现员工的状态码压根就没有变化。

原因:通过调试观察 id ,发现后台的SQL语句使用的 id 和数据库中的 id 不一样,因为 mybatis-plus 的 id 主键生成策略是雪花算法,所以存入数据库中的 id 是 19 为长度,但是前端的 js 只能保证数据的前 16 位的数据的精度,对 id 后三位数据进行了四舍五入,出现了精度丢失,就会出现前度传过来的 id 和数据里面的 id 不匹配,就没办法正确的修改到我们想要的数据;

解决方法:将 Long 类型转化为 String 类型来处理

① 导入对象转换器

package com.itheima.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)

                // 将Long型数据转化为String
                .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);
    }
}

② 在 WebMvcConfig 类拓展mvc框架的消息转换器

    /**
     * 拓展mvc框架的消息转换器
     * @param converters
     */
    @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);
    }

结果:

前端获取的来自 page 方法的数据的 Long 类型变为 String 类型

而修改请求的 id 的精度问题也解决了

4、编辑员工信息

4.1、分析

需求分析:注意,add.html 编辑员工与添加员工共用一个页面

程序执行流程:

跳转到 add.html 的请求格式如下:

向后端查询 id 对应的员工信息的请求格式如下: 

保存修改后的员工信息的请求格式如下:

前端流程如下:

① 在 created 阶段,通过请求路径是否有 id 参数判断是新增员工还是编辑员工

② 若为编辑员工,则通过 init() 方法查询 id 对应的员工信息,并进行回显 

4.2、代码

4.2.1、controller 方法

修改方法与切换账号启动/禁用状态的方法一样 

    /**
     * 拓展mvc框架的消息转换器
     * @param converters
     */
    @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);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值