目录
新增员工,将新增页面录入的员工数据添加到 employee 表。注意:employee 表中对 username 字段加入了唯一的约束,因为 username 是员工的登陆账号,必须是唯一的!且 employee 表中的 status 字段默认设置为 1,表示员工状态可以正常登陆
输入信息后,点击保存,发送 POST 请求 http://localhost:8080/employee ,且含有添加的员工信息
在页面点击保存按钮后,前端执行 addMemberHandle 方法,将 iframe 的内容转为添加员工页面
点击保存按钮后,前端执行 submitForm 方法,首先对填入的内容进行校验
1、页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端
2、服务端Controller接收页面提交的数据并调用Service将数据进行保存
3、Service调用Mapper操作数据库,保存数据
由于新增员工时,若用户名 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
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("未知错误");
- }
-
- }
- /**
- * 新增员工
- * @param employee
- * @param request
- * @return
- */
- @PostMapping
- public R
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("新增员工成功");
- }
员工信息页面是 /backend/page/member/list.html ,刚打开页面就会发送获取员工信息的请求是通过 created (Vue创建完成后) 完成的。
为了方便分页,先配置 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;
- }
-
- }
- /**
- * 员工信息分页查询
- * @param page
- * @param pageSize
- * @param name
- * @return
- */
- @GetMapping("/page")
- public R
page(int page, int pageSize, String name){ - //log.info("page = {}, pageSize = {}, name = {}", page, pageSize, name);
- // 构造分页构造器
- Page pageInfo = new Page(page, pageSize);
- // 构造条件构造器
- LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper(); - queryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name);
- queryWrapper.orderByDesc(Employee::getUpdateTime);
- // 执行查询
- employeeService.page(pageInfo, queryWrapper);
- return R.success(pageInfo);
- }
在员工管理列表页面中,可以对某个员工账号进行启用或者是禁用操作。账号禁用的员工不能登陆系统,启用后的员工可以正常登陆。
注意:只有管理员(admin用户)才可以对其他普通用户进行启用操作,禁用操作,普通用户登录系统后启用,禁用按钮不显示。并且如果某个员工账号的状态为正常,则按钮显示为’‘禁用’,如果员工账号状态为已禁用,则按钮显示为“启用”。普通员工登录系统后,启用,禁用按钮不显示;
禁用、启用显示的前端流程:
① 访问员工信息前要登录,而登录后会将员工信息存储到浏览器的 localStorage 中,而 list.html 在 created 生命周期会获取存储在 localStorage 的员工信息的 username,如下
② 在表格中,前端会判断登录用户的 username,若为 admin,则显示禁用、启用按钮,否则不显示,显示启用还是禁用则是通过员工账号状态来判断
处理逻辑:
请求发送过程
- /**
- * 根据id修改员工信息
- * @param employee
- * @return
- */
- @PutMapping
- public R
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("员工信息修改成功");
- }
测试的时候我们发现出现了问题,就是我们修改员工的状态,提示信息显示修改成功,但是我们去数据库查验证的时候,发现员工的状态码压根就没有变化。
原因:通过调试观察 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
> converters) { - //log.info("拓展消息转换器...");
- // 创建消息转换器对象
- MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
- // 设置对象转换器,底层使用Jackson将Java对象转为Json
- messageConverter.setObjectMapper(new JacksonObjectMapper());
- // 将消息转换器追加到mvc框架的转换器容器,且放在首位
- converters.add(0, messageConverter);
- }
结果:
前端获取的来自 page 方法的数据的 Long 类型变为 String 类型
而修改请求的 id 的精度问题也解决了
需求分析:注意,add.html 编辑员工与添加员工共用一个页面
程序执行流程:
跳转到 add.html 的请求格式如下:
向后端查询 id 对应的员工信息的请求格式如下:
保存修改后的员工信息的请求格式如下:
前端流程如下:
① 在 created 阶段,通过请求路径是否有 id 参数判断是新增员工还是编辑员工
② 若为编辑员工,则通过 init() 方法查询 id 对应的员工信息,并进行回显
修改方法与切换账号启动/禁用状态的方法一样
- /**
- * 拓展mvc框架的消息转换器
- * @param converters
- */
- @Override
- protected void extendMessageConverters(List
> converters) { - log.info("拓展消息转换器...");
- // 创建消息转换器对象
- MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
- // 设置对象转换器,底层使用Jackson将Java对象转为Json
- messageConverter.setObjectMapper(new JacksonObjectMapper());
- // 将消息转换器追加到mvc框架的转换器容器,且放在首位
- converters.add(0, messageConverter);
- }