当前位置: 首页 > news >正文

瑞吉外卖 —— 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);
    }

相关文章:

  • 走到上市前夕,叮当健康如何勾画“医药检险”蓝图?
  • 批量条件赋值、文本字段计算常用表达式
  • 计算机毕业论文Java项目源码下载学生宿舍管理系统|寝室管理
  • 分子动力学后处理自编程系列(2)------聚合物回转半径
  • 剑指offer57-61排序-堆
  • 数据⼀致性模型有哪些?
  • 【2023灵动股份笔试题】~ 题目及参考答案
  • 通过分箱对连续特征离散化,以提高线性模型的表现
  • 【Swift 60秒】04 - Doubles and booleans
  • 文章介绍关于IM3536 是 Hioki 的 8 MHz lcr 仪表
  • Springboot+网上眼镜商场 毕业设计-附源码241659
  • 淘宝API接口商品详情,关键词搜索
  • 【课程作业】西瓜书 机器学习课后习题 : 第一章
  • 全球Top50搜索引擎排名,搜索引擎是什么意思
  • DS刷题目录
  • canvas 高仿 Apple Watch 表盘
  • GitUp, 你不可错过的秀外慧中的git工具
  • magento2项目上线注意事项
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • node和express搭建代理服务器(源码)
  • Python 反序列化安全问题(二)
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Python实现BT种子转化为磁力链接【实战】
  • rc-form之最单纯情况
  • Vue小说阅读器(仿追书神器)
  • WePY 在小程序性能调优上做出的探究
  • 百度小程序遇到的问题
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 时间复杂度与空间复杂度分析
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 网络应用优化——时延与带宽
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • mysql面试题分组并合并列
  • Python 之网络式编程
  • ​ubuntu下安装kvm虚拟机
  • (2)(2.10) LTM telemetry
  • (AngularJS)Angular 控制器之间通信初探
  • (LeetCode) T14. Longest Common Prefix
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (补)B+树一些思想
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (五)关系数据库标准语言SQL
  • (一)为什么要选择C++
  • (译) 函数式 JS #1:简介
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (转)EXC_BREAKPOINT僵尸错误
  • .NET Project Open Day(2011.11.13)
  • .NET 中各种混淆(Obfuscation)的含义、原理、实际效果和不同级别的差异(使用 SmartAssembly)
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .ui文件相关
  • [ JavaScript ] JSON方法
  • [1]-基于图搜索的路径规划基础