瑞吉外卖 - 启用与禁用员工账号功能(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…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...