瑞吉外卖 - 启用与禁用员工账号功能(8)
某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 + JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。
相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PDe_7uIg?pwd=x548
提取码:x548
文章目录
- 1.需求分析
- 2.代码开发
- 2.1 分析页面效果
- 2.2 分析执行过程
- 2.2.3 代码实现
- 3.功能测试
- 4.代码修复
1.需求分析
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。需要注意的是,只有管理员(admin 用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用禁用按钮不显示。
下面是管理员登陆后的界面:
下面是普通员工登陆后的页面:
2.代码开发
2.1 分析页面效果
那么页面中是如何做到只有管理员(admin)才能够看到启用、禁用员工按钮的呢?我们可以追踪到 static/backend/page/member/list.html 中的如下位置:
可以看到,代码中会判断模型数据 user 的值是否为 admin,如果是则展示当前按钮,否则不展示。对应的 user 我们可以往上追踪到如下位置:
可以看到,前端会从 localStorage 获取当前登陆员工的 username,并赋值给模型数据 user。
2.2 分析执行过程
在代码开发之前,我们需要先梳理一下整个程序的执行过程:
- 客户端发送 ajax 请求,将参数(id,status)提交到服务端;
- 服务端 Controller 接收客户端提交的数据并调用 Service 更新数据;
- Service 调用 Mapper 操作数据库。
我们可以使用 admin 账户点击启用禁用按钮,然后查看浏览器调试工具对应的请求 URL 和携带的参数:
可以看出,当管理员点击禁用或启用按钮时,会发送一个 POST 请求,请求 URL 为 /employee,同时携带 json 数据为 id(员工 id)和 status(员工状态)。
2.2.3 代码实现
对应的 EmployeeController
中的处理方法如下:
@RestController
@RequestMapping("/employee")
public class EmployeeController {/*** 处理更新员工请求* @param request 请求对象* @param employee 员工对象* @return 响应对象*/@PutMappingpublic R<String> update(HttpServletRequest request,@RequestBody Employee employee){// 设置更新时间employee.setUpdateTime(LocalDateTime.now());// 获取当前登录的员工 id,作为更新人Long empId = (Long) request.getSession().getAttribute("employee");employee.setUpdateUser(empId);// 更新员工信息employeeService.updateById(employee);// 返回更新成功结果return R.success("更新员工成功");}
}
实现思路比较简单,就是将客户端传送的 json 数据通过 @RequestBody
注解封装为 Employee 对象,在完成更新员工信息的同时也更新了对应的更新时间(update_time)和更新人(update_user)。
3.功能测试
下面我们直接进行功能测试,以 admin 账户登陆后点击【禁用】按钮:
可以看到,更新操作成功了,但是为什么状态仍旧是正常呢?我们不妨再查看数据库端的对应数据:
咦?这是为什么呢?我们接着来看看 IDEA 控制台的 SQL 日志:
执行结果是 Updates:0 ,也就意味着没有匹配到对应的记录,然而从 SQL 语句中不难看出,该语句是根据 id 进行匹配的。那我们重点关注传入的 id,值为 1658001441572294700,重点关注后三位 “700”。
然后我们再查看数据库中对应的 id:
发现端倪了吗?后三位居然与数据库的值不一样!我们再看看客户端请求中携带的 id 值:
可见当执行修改操作时客户端发送的 id 就是错的,我们在服务端通过该 id 值进行修改操作,自然是修改不成功的。那么是不是因为在进行分页操作的时候拿到的响应就是错的呢?我们对应找到分页完成客户端的响应数据:
很明显客户端返回的数据是没有问题的,该 id 与数据库中的一致。可是在我们点击【编辑】或【启用、禁用】按钮时获取到的 id 确实是错的,那么问题出来哪儿呢?
其实是因为服务端在进行分页查询时,响应给客户端的员工 id 是一个 long 类型的 19 位整数值,从上面的截图也能看出来了。但是问题就出现在,前端页面中是通过 js 去处理 long 类型的数值的,但是只能精确到 16 位,也就意味着后三位进行了四舍五入,因此最终通过 ajax 请求提交给服务端时 id 的后三位就由 “657”变成了 “700”,自然也就不可能更新成功了。
针对这个问题,为了避免 js 处理 long 类型数值时出现精度丢失,我们可以将原本响应给客户端的 long 类型的 id,将 long 型的数据进行处理转为 String 类型即可。
4.代码修复
具体实现步骤如下:
-
提供对象转换器 JacksonObjectMapper,基于 jackson 进行 Java 对象到 json 数据的转换;
资料位置:瑞吉外卖\瑞吉外卖项目\资料\对象映射器\JacksonObjectMapper.java
-
在 WebMvcConfig 配置类中扩展 Spring MVC 的消息转换器,在此消息转换器中使用提供的对象转换器进行 Java 对象到 jason 数据的转换。
我们直接将准备好的 JacksonObjectMapper.java 复制到项目的 common
包下即可,对应的完整代码如下:
package cn.javgo.reggie_take_out.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 对象* 说明:* 1.将 JSON 解析为 Java 对象的过程称为 [从 JSON 反序列化 Java 对象]* 2.从 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)))// 将 BigInteger、Long 类型的数据序列化为字符串类型.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);}
}
接下来新建 config
包用于存放各种配置类,然后新建一个 WebMvcConfig
类继承 WebMvcConfigurationSupport
类,并在中扩展 Spring MVC 的消息转换器即可,其实就是重写父类的 extendMessageConverters()
方法从而替换 Spring MVC 默认的消息转换器。
完整代码如下:
package cn.javgo.reggie_take_out.config;import cn.javgo.reggie_take_out.common.JacksonObjectMapper;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;/*** @author: JavGo* @description: TODO* @date: 2023/5/16 12:49*/
@SpringBootConfiguration
public class WebMvcConfig extends WebMvcConfigurationSupport {/*** 配置静态资源映射* @param registry 静态资源注册器*/@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {// 配置静态资源映射(将所有的静态资源都映射到 classpath:/static/ 目录下)registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");}/*** 扩展 Spring MVC 消息转换器** @param converters 消息转换器列表*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {// 创建 Jackson 消息转换器MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();// 设置具体的序列化和反序列化器(其实就是对象映射器)messageConverter.setObjectMapper(new JacksonObjectMapper());// 通过设置消息转换器的顺序,来改变 Spring MVC 消息转换器的优先级,从而实现自定义消息转换器的优先级高于默认的消息转换器converters.add(0, messageConverter);}
}
上述代码中笔者同时重写
addResourceHandlers()
方法进行了静态资源映射的处理,否则点击【编辑】按钮跳转静态资源页面时会显示 404。
在 Spring Boot 应用程序启动时会自动调用上述方法进行消息转换器的注册,我们可以在该方法上打上断点:
OK,现在以 Debug 方式重启应用再次进行测试,当执行完该方法后可以看到我们扩展的消息转换器被放到了第一个位置,具有最高优先级:
跳过调试,项目启动完成后再进行客户端功能测试,就能测试成功:
此时分页查询返回的 id 也加上了双引号作为字符串,并且与数据库端的 id 一致:
对应更新操作的客户端请求的 json 数据也获取到的正确的 id:
禁用后,我们将管理员账户退出登录,使用被禁用的 zhangsan 进行登录(默认密码为 12346)就会登陆失败:
相关文章:

瑞吉外卖 - 启用与禁用员工账号功能(8)
某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…...

【MySQL】索引
记录MySQL学习笔记,大部分图片来自黑马程序员MySQL教程。 文章目录 概述索引结构BTree为什么InnoDB使用BTree索引结构? 索引分类索引语法SQL性能分析1、查看执行频次2、慢查询日志3、profile详情4、explain执行计划 索引使用最左前缀法则索引失效情况1、…...

JavaScript全解析——express
express 的基本使用 ●express 是什么? ○是一个 node 的第三方开发框架 ■把启动服务器包括操作的一系列内容进行的完整的封装 ■在使用之前, 需要下载第三方 ■指令: npm install express 1.基本搭建 // 0. 下载: npm install express// 0. 导入 const express express()…...

【JavaScript数据结构与算法】字符串类(计算二进制子串)
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,也会涉及到服务端(Node.js) 📃个人状态: 在校大学生一枚,已拿多个前端 offer(…...

TCP连接不释放,应用产生大量CLOSE_WAIT状态TCP
一、起源 23年元旦期间,大家都沉浸在一片祥和的过节气氛当中。 “滴滴滴”,这头同事的电话响起,具体说些什么我也没太在意,但见同事接完电话之后展现出了一副懊恼夹杂着些许不耐烦的表情。 我不解问道:“怎么了&…...

Spring基础核心概念理解(常见面试题:什么是IoC?什么是DI?什么是Spring?)
目录 IoC 和 SpringIoC DI Spring IoC 和 SpringIoC IoC是控制反转的意思,它意味着控制权(依赖对象)的反转,将控制权进行反转,它是一种思想. 举个例子,理解一下什么是控制反转 现在有三个对象A,B,C. A的创建依赖于B,B的创建依赖于C,当我们想要创建A的时候创建B,同理也要…...

牛客小白月赛 D.遗迹探险 - DP
题目描述 小Z是一名探险家。有一天,小Z误入了一个魔法遗迹。以下是该遗迹的具体组成: 1. 在 x 轴和 y 轴构成的平面上,满足在 1≤x≤n,1≤y≤m 的区域中(坐标(x,y)表示平面上的第x行的第y列),每个整数坐标 (x,y) 都有…...

前端架构师-week6-require源码解析
require 源码解析——彻底搞懂 npm 模块加载原理 require 的使用场景 加载模块类型 加载内置模块:require(fs)加载 node_modules 模块:require(ejs)加载本地模块:require(./utils)支持文件类型 加载 .js 文件加载 .mjs 文件加载 .json 文件…...

作为 IT 行业的过来人,你有什么话想对后辈说的?
作为 IT 行业的过来人,我想对后辈们说,要不断学习和探索新技术,但同时也要注意保持专注和耐心。在这个快速变化的时代,技术更新换代太快,可能会让人感到焦虑和无助,但只要有耐心并专注于自己所做的事情&…...

表数据编辑(数据库)
目录 一、插入数据 1.插入单个元组: INSERT…VALUES语句 2.插入子查询的结果: INSERT…SELECT语句 3.使用SELECT…INTO语句进行数据插入 二、修改数据 1、数据修改语句:UPDATE 2、修改给定表的所有行 3、基于给定表修改某…...

考虑多能负荷不确定性的区域综合能源系统鲁棒规划(Python代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

RocketMQ整理
RocketMQ在阿里云上的商业版本,集成了阿里内部一些更深层次的功能及运维定制。开源版本,功能上略有缺失,但大体上是一样的。 使用Java开发,便于深度定制。最早叫MetaQ。消息吞吐量虽然依然不如Kafka,但是却比RabbitMQ高很多。在阿里内部,RocketMQ集群每天处理的请求数超过…...

Springboot +Flowable,会签、或签简单使用(二)
一.简介 **会签:**在一个流程中的某一个 Task 上,这个 Task 需要多个用户审批,当多个用户全部审批通过,或者多个用户中的某几个用户审批通过,就算通过。 例如:之前的请假流程,假设这个请假流程…...

将核心交换机配置为NTP服务器
AR配置外源NTP 1.配置ntp <XQ-R1220>sys [XQ-R1220]ntp-service unicast-server 120.25.115.20 #阿里云ntp [XQ-R1220]ntp-service unicast-server 203.107.6.88 #阿里云ntp 2.查看ntp状态 <XQ-R1220>display ntp status clock sta…...

application.properties文件注释
这是一个常用的Spring Boot配置文件 在这里,我们可以配置应用程序的各种属性 服务器端口号 server.port8080 数据库配置 spring.datasource.urljdbc:mysql://localhost:3306/test spring.datasource.usernameroot spring.datasource.password123456 spring.datasou…...

MySql查询报错this is incompatible with sql_mode=only_full_group_by
错误示例 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘yiliaohaocai_new.a.id’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_modeonly_full_group_by 原因 SQL …...

VMware Workstation 网络备忘 + 集群规模
概述 在虚拟机中部署服务,进行IP规划,进行相关的前期准备 3 张网卡 2个不同的网段 1个NAT 概述截图 NAT 截图 VMnet0 截图 VMnet1 截图 总结: 网卡(网络适配器)名称IP网段备注NATens33192.168.139.0VMnet0ens34VMne…...

被裁现状,给找工作的同学一些建议
2022 到 2023 国内知名互联网公司腾讯、阿里、百度、快手、滴滴、京东、阿里、爱奇艺、知乎、字节跳动、小米等公司均有裁员,其中有不少公司,在过去年的一整年,进行了多轮裁员,以下是网传的一张 “2022 年裁员企业名单”。 这些裁…...

编程到底难在哪里?
编程是一门非常有挑战性的技术,能够让人们使用计算机来完成各种任务。它不仅需要掌握各种计算机语言和框架,还需要在实际应用中充分发挥自己的专业知识和创造力。 然而,对于初学者来说,在编程过程中遇到的难点可能是多方面的。以…...

C++ 仿函数(一)
目录 一、仿函数是什么? 二、仿函数的特点 1.仿函数在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值 2.仿函数超出普通函数的概念,可以有自己的状态 编辑3.仿函数可以作为参数传递。 三、谓词 一元谓词示例&a…...

MATLAB连续LTI系统的时域分析(十)
目录 1、实验目的: 2、实验内容: 1、实验目的: 1)掌握利用MATLAB对系统进行时域分析的方法; 2)掌握连续时间系统零输入响应的求解方法; 3)掌握连续时间系统零状态响应、冲激响应和…...

HBuilderX使用
HBuilderX使用(Vue前后端分离) 概述:DCloud开发者后台 DAccount Service 1、官网下载开发工具:HBuilderX-高效极客技巧 注意:安装目录路径中不能出现中文特殊字符,否则会造成项目无法编译。比如C:/Progr…...

【JavaSE】多态(多态实现的条件 重写 向上转移和向下转型 向上转型 向下转型 多态的优缺点 避免在构造方法种调用重写的方法)
文章目录 多态多态实现的条件重写向上转移和向下转型向上转型向下转型 多态的优缺点避免在构造方法种调用重写的方法 多态 一种事物,多种形态。 多态的概念:去完成某个行为,当不同对象去完成时会产生出不同的状态。 多态实现的条件 1.必须…...

MySQL学习---13、存储过程与存储函数
1、存储过程概述 MySQL从5.0版本开始支持存储过程和函数。存储过程和函数能够将负杂的SQL逻辑封装在一起,应用程序无序关注存储过程和函数内部复杂的SQL逻辑,而只需要简单的调用存储过程和函数就可以。 1.1 理解 含义:存储过程的英文是Sto…...

Mysql日志管理、备份与恢复
文章目录 一、Mysql日志管理1.mysql日志2.日志种类3.日志的查询4.配置日志文件 二、Mysql备份与分类1.数据备份的重要性 一、Mysql日志管理 1.mysql日志 Mysql的日志默认保存位置为/usr/local/mysql/date,Mysql的日志配置文件为/etc/my.cnf,里面有一个…...

STM32单片机声控语音识别RGB彩灯多种模式亮度可调WS2812彩灯
实践制作DIY- GC0129-语音识别RGB彩灯 一、功能说明: 基于STM32单片机设计-语音识别RGB彩灯 二、功能介绍: STM32F103C系列最小系统板5VUSB电源64个灯珠的WS2812灯板1个开关键(3档亮度调节)1个模式切换键(白灯 红灯…...

高校9大学术工具推荐,一定要用起来哦!
1、文献管理工具:例如EndNote、Mendeley和Zotero,这些工具可以帮助您整理、管理和引用文献。 2、数据分析工具:例如SPSS、R和Python等,用于进行统计分析和数据处理。 3、学术写作工具:例如LaTeX和Microsoft Word&…...

记一次压力测试
性能测试文档 背景 为对产品性能有一定了解,现将产品展开一次性能测试; 环境与工具 本章为基本工具准备及linux命令说明,无先后顺序。 Xshell工具 本文使用Xshell在Windows界面下远程登录linux主机安装Xshell直接全部选择默认选项即可&…...

一个文明是否有竞争力,在很大程度上取决于信息传递的效率。
文章目录 引言I 有效地传递信息1.1 信息传播分类1.2 信息传递的有效性II 科技进步的必要条件和充分条件2.1 能量总量2.2 能量密度2.3 衡量科技成就的大小2.4 科学的诞生的意义:获得叠加式收益引言 科技进步的必要条件是能量总量,而充分条件是能量密度。一个文明是否有竞争力,…...

测试4年,跳槽一次涨8k,我跳了3次···
最近有人说,现在测试岗位初始工资太低了,有些刚刚入行的程序员朋友说自己工资连5位数都没有.....干了好几年也没怎么涨。看看别人动辄月薪2-3万,其实我想说也没那么难。说下如何高效地拿到3w。 1.暂且把刚入行的条件设低些吧,大专…...