瑞吉外卖项目笔记02——员工管理、设置公共字段自动填充

news2025/1/21 4:44:20

三、员工信息管理

3.1 添加员工

注意:在设计数据库表字段时,给userName添加了唯一索引(所以员工用户名是无法重复的)

流程

前端页面发送POST请求,后端接收到请求和数据,将用户数据添加到数据库内,返回重成功信息 !
在这里插入图片描述

实现

employController :

@ApiOperation("添加新员工")
@PostMapping
public MyResult addEmployee(HttpServletRequest request,@RequestBody Employee employee){
    // 1.打印日志信息
    log.info("添加新员工:"+employee.getUsername());
    // 2.设置初始密码并加密
    String MD5Password = DigestUtils.md5DigestAsHex("123456".getBytes());
    // 3.添加一些员工创建信息
    employee.setPassword(MD5Password);
    employee.setCreateTime(LocalDateTime.now());
    employee.setUpdateTime(LocalDateTime.now());
    Long emplId = (Long) request.getSession().getAttribute("employee");
    employee.setCreateUser(emplId);
    employee.setUpdateUser(emplId);
    // 4.调用方法将数据保存到数据库捏
    employService.save(employee);
    // 5.返回成功信息
    return MyResult.success("添加新员工成功!");
}

完善

  • 因为Employee数据库表的字段username加入了唯一索引,所以在添加了重复数据时会爆出异常,我们需要自定义处理该异常

    • 方法一:直接使用try…catch捕获异常
    • 方法二:定义全局异常处理器处理异常推荐
  • @ControllerAdvice(annotations = {RestController.class, Controller.class}) 指定该异常处理器会拦截处理哪些异常

  • @ExceptionHandler(SQLIntegrityConstraintViolationException.class) 指定方法处理那些异常情况

@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
//进行异常处理方法
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
    log.error(ex.getMessage());

    if(ex.getMessage().contains("Duplicate entry")){
        String[] split = ex.getMessage().split(" ");
        String msg=split[2]+"已存在";
        return R.error(msg);
    }
	// 打印异常信息 指明哪个账号信息重复了
    return R.error("未知错误");
}

3.2 分页查询员工信息

流程

  1. 前端页面发送get请求 将分页查询参数(page.pageSize、name)提交到服务端
  2. controller接收调用service的page方法获取数据 将数据存储到page对象内(所以需要提前创建好page对象作为参数传入方法)
  3. 返回数据

employController

/**
 *
 * @param page 当前椰树
 * @param pageSize 一页数据数目
 * @param name 搜索条件(没有的话为 “”)
 * @return 返回封装好的查询page数据
 */
@ApiOperation("带搜索条件的分页查询")
@GetMapping("/page")
public MyResult<Page> page(int page, int pageSize, String name){
    log.info("page={},pageSize={},name={}", page, pageSize, name);
    // 1.初始化page对象
    Page pageInfo = new Page(page,pageSize);
    // 2.定义查询条件  模糊匹配 + 根据修改时间降序排序
    LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
    wrapper.like(!StringUtils.isEmpty(name),Employee::getName,name).orderByDesc(Employee::getUpdateTime);
    // 3.调用page方法获取分页查询数据
    Page page1 = employService.page(pageInfo,wrapper);
    // 4.封装返回信息
    return MyResult.success(page1);
}

3.3 启用/禁用 员工账号状态

需求

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

  • 需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示。

实现

  • 只有管理员能看到和操作相关启用/禁用 按钮是由前端设置的,在此就不多进行讲解

(但是这样做其实还会有一个问题:如果直接向后端发送对应恶意请求(不通过前端发送),会造成数据库信息的恶意修改,所以后端也需要进行对权限的检验——检验当前登录用户是否为admin

流程

1、页面发送ajax请求,将参数(id、 status)提交到服务端

2、服务端Controller接收页面提交的数据并调用Service更新数据

3、Service调用Mapper操作数据库

employController:

    @ApiOperation("修改员工状态")
    @PutMapping
    public MyResult<String> update(HttpServletRequest request,@RequestBody Employee employee){
        log.info(employee.toString());

        Long empId = (Long) request.getSession().getAttribute("employee");
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(empId);
        employService.updateById(employee);

        return MyResult.success("员工信息修改成功");
    }

问题发现与修复

通过观察控制台输出的SQL发现页面传递过来的员工id的值和数据库中的id值不一致,这是怎么回事呢?

分页查询时服务端响应给页面的数据中id的值为19位数字,类型为long

页面中js处理long型数字只能精确到前16位,所以最终通过ajax请求提交给服务端的时候id就改变了

前面我们已经发现了问题的原因,即js对long型数据进行处理时丢失精度,导致提交的id和数据库中的id不一致。

如何解决这个问题?

我们可以在服务端给页面响应json数据时进行处理将long型数据统一转为String字符串

具体实现步骤:

1) 提供对象转换器JacksonobjectMapper,基于Jackson进行Java对象到json数据的转换(资料中已经提供,直接复制到项目中使用)

/**
 * 对象映射器:基于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)
                .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);
    }
}

2) 在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行Java对象到json数据的转换

@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    //创建消息转换器
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    //设置对象转换器,底层使用Jackson将Java转换为json
    messageConverter.setObjectMapper(new JacksonObjectMapper());
    //将上面的消息转换器对象追加到mvc框架的转换器集合中
    converters.add(0,messageConverter);
    super.extendMessageConverters(converters);
}

3.4 编辑员工信息

流程
在这里插入图片描述
点击编辑按钮后,请求携带员工id查询员工,将员工信息回显到页面上,当我们编辑好新的信息后,点击保存,执行之前编写的update方法,将编辑修改后的信息保存到数据库内。

实现

employController

    @ApiOperation("根据id获取员工信息")
    @GetMapping("/{id}")
    public MyResult<Employee> getByID(@PathVariable("id") Long id){
        log.info("根据id查询员工信息!");
        Employee employee = employService.getById(id);
        if (employee != null)
            return MyResult.success(employee);
        return MyResult.error("获取失败!");
    }

四、优化——公共字段自动填充

4.1 简单介绍与实现

问题分析
在这里插入图片描述
公共字段自动填充

Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码

实现步骤

1、在实体类的属性上加入@TableField注解,指定自动填充的策略

@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime; // 账号创建时间
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime; // 账号信息更新时间

@TableField(fill = FieldFill.INSERT) // 与数据库内表属性进行绑定
private Long createUser; // 账号创建者

@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;    // 账号更新者

自动填充策略

DEFAULT, // 默认
INSERT,  // 插入时
UPDATE,   // 更新时
INSERT_UPDATE; // 插入与更新时

2、按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

  • 就是要编写一个实现自动填充策略的处理器类(要求该类需要实现MetaObjectHandler接口)
// 元数据处理器类
@Slf4j
public class MyMetaHandler implements MetaObjectHandler {
    // 插入数据时自动填充字段
    @Override
    public void insertFill(MetaObject metaObject) {
      log.info("插入时自动填充字段.....");
      metaObject.setValue("createTime", LocalDateTime.now());
      metaObject.setValue("createUser",new Long(1)); // 后续会修改为动态数据
      metaObject.setValue("updateTime",LocalDateTime.now());
      metaObject.setValue("updateUser",new Long(1));
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("更新时自动填入数据");
        metaObject.setValue("createUser",new Long(1));
        metaObject.setValue("updateUser",new Long(1));
    }
}

4.2 问题完善与优化

问题完善

问题:

​ 在创建者,修改者这些数据字段上的自动填入我们出现了问题,我们无法通过获取HTTPSession来获取保存的当前用户ID (因为重写的方法内没有该参数传入) 所以我们需要改变方式来动态获取到当前用户ID

于是我们使用ThreadLocal类来实现,

一个小知识:一次请求,对服务器来将都是会创建一个线程来单独处理

什么是ThreadLocal?

ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

(相当于ThreadLocal为当前线程开辟的一份全局独立空间,可以存储专属的数据)

ThreadLocal常用方法:

  • public void set(T value) 设置当前线程局部变量的值
  • public T get() 返回当前线程所对应的线程局部变量的值

我们可以在拦截器方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。

完善问题

  1. 编写一个用于保存和获取当前线程ThreadLocal变量值的工具类MyBaseContext
// ThreadLocal工具类 用于保存获取当前登录用户Id
public class MyBaseContext {
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    public static Long getCurrentID(){ // 获取当前用户ID
        return threadLocal.get();
    }
    public static void setCurrentID(Long id){ // 保存当前用户ID
        threadLocal.set(id);
    }
}
  1. 修改MyMetaHandler
// 元数据处理器类
@Slf4j
public class MyMetaHandler implements MetaObjectHandler {
    // 插入数据时自动填充字段
    @Override
    public void insertFill(MetaObject metaObject) {
      log.info("插入时自动填充字段.....");
      metaObject.setValue("createTime", LocalDateTime.now());
      metaObject.setValue("createUser",MyBaseContext.getCurrentID());
      metaObject.setValue("updateTime",LocalDateTime.now());
      metaObject.setValue("updateUser",MyBaseContext.getCurrentID());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("更新时自动填入数据");
        metaObject.setValue("createUser",MyBaseContext.getCurrentID());
        metaObject.setValue("updateUser",MyBaseContext.getCurrentID());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/522720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PyQt5桌面应用开发(14):数据库+ModelView+QCharts

本文目录 PyQt5桌面应用系列下一个玩具报表一&#xff1a;Markdown文档的列表显示Widget的树、表、列报表Qt中的MVC框架模型视图代理 报表二&#xff1a;Markdown文档的长度图形数据&#xff1a;Markdown文档和Sqlite数据库代码总结 PyQt5桌面应用系列 PyQt5桌面应用开发&#…

FE_Vue学习笔记 框架的执行流程详解

1 分析脚手架结构 &#xff08;1&#xff09;CLI就是 command line interface 的缩写。Vue CLI官网&#xff1a;Vue CLI &#xff08;2&#xff09;安装过程&#xff1a; &#xff08;PS&#xff1a; 提前安装过node.js了&#xff0c;没有安装的可以打开这个&#xff1a;Downl…

Flutter - 搭建引擎调试环境(iOS)

文章目录 前言开发环境安装依赖环境1. python32. depot_tools 获取引擎项目1. 创建engine空目录2. 创建.gclient文件并配置3. 指定引擎版本4. 同步引擎源码 搭建调试环境1. 构建编译1.1 生成构建所需文件1.1.1 主机端1.1.2 iOS端 1.2 完成构建编译 2. Xcode调试2.1 设置本地引擎…

Qt扫盲-QAreaSeries理论总结

QAreaSeries理论总结 一、概述二、使用1. 创建QAreaSeries 对象2. 填充数据3. 设置区域4. 将面积图 与绘图设备关联5. 将绘图设备与 GUI窗口关联 一、概述 QAreaSeries类以面积图的形式显示数据。QAreaSeries用于显示定量数据。它基于QLineSeries 类&#xff0c;边界线条之间的…

二、Docker在Linux下的安装

文章目录 Docker在Linux下的安装Docker安装的前提环境安装1. 卸载旧的版本2. 安装yum-utils包3. 设置镜像的仓库4. 安装Docker相关的内容 启动Docker运行hello-world查看下载的hello-world镜像 了解卸载Docker Docker在Linux下的安装 Docker安装的前提环境 CentOS Docker 安装…

【LLM系列之PaLM】PaLM: Scaling Language Modeling with Pathways

论文题目&#xff1a;《Scaling Instruction-Finetuned Language Models》 论文链接&#xff1a;https://arxiv.org/abs/2204.02311 github链接1&#xff1a;https://github.com/lucidrains/PaLM-pytorch/tree/main; github链接2:https://github.com/conceptofmind/PaLM huggin…

每天一个提高效率的Matlab编程小技巧(1)-dbstop if error

相信在matlab调试程序的时候都遇到过这种情况&#xff1a;运行程序时命令行报错&#xff0c;而且出错的位置在我们自己定义的函数里&#xff0c;比如下面这个例子&#xff1a; 主函数main.m: a[1 2 3]; b[4 5]; csum_squares(a,b); 子函数sum_squares.m function csum_squa…

AI孙燕姿 ?AI东雪莲 !—— 本地部署DDSP-SVC一键包,智能音频切片,本地训练,模型推理,为你喜欢的角色训练AI语音模型小教程

目录 感谢B站UP羽毛布团 演示视频 稻香——东雪莲 虚拟——东雪莲 反方向的钟——东雪莲 晴天龙卷风——东雪莲 DDSP-SVC 3.0 (D3SP) 是什么&#xff1f; 下载资源&#xff1a; 解压整合包 准备数据集 智能音频切片 数据集准备 填写训练设置和超参数 开始训练 推…

这个抓包工具太强了,科来网络分析系统强烈推荐

一直以来抓包工具&#xff0c;都推荐和使用wireshark&#xff0c;简单好用。最近发现一款更强大好用的网络分析工具&#xff0c;科来网络分析系统。且技术交流版是完全免费的&#xff0c;无需注册激活。这里强烈推荐和分享给大家。这可是个网络报文分析和监控神器。有多强大&am…

【CSS系列】第七章 · CSS盒子模型,看这一篇就够了

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

Protobuf-net3.2.8中的protogen.exe之使用

目录 protobuf是个好东西 遇到问题 顺便研究一下命令行程序如何调试 protobuf是个好东西 protobuf是一个轻量级的数据格式&#xff0c;相比json&#xff0c;它的数据量为json的1/3&#xff0c;且存储方式为2进制&#xff0c;并进行了压缩&#xff0c;序列化和反序列化更快&…

效率与性能并存——离不开 Visual Studio Code 的前端开发与我

文章目录 &#x1f4cb;前言&#x1f3af;题外话&#xff1a;我与 VSCode 的那些事&#x1f3af;VSCode 的强大之处&#x1f9e9;VSCode 的诞生&#x1f9e9;VSCode 的一些功能 &#x1f3af;优与劣&#xff08;简单小结&#xff09;&#x1f4dd;最后 &#x1f4cb;前言 许久…

JVM 原理简介

JVM一直是java知识里面进阶阶段的重要部分&#xff0c;如果希望在java领域研究的更深入&#xff0c;则JVM则是如论如何也避开不了的话题&#xff0c;本系列试图通过简洁易读的方式&#xff0c;讲解JVM必要的知识点。 运行流程 我们都知道java一直宣传的口号是&#xff1a;一次编…

股票K线基础知识1

K线图 K线图是反映价格在某一时间周期内波动情况的图表&#xff0c;它由开盘价、收盘价、最高价、最低价四个要素构成&#xff0c;若当日收盘价高于开盘价&#xff0c;这表明价格处于上涨状态&#xff0c;此时K线图多用红色表示&#xff1b;若当日收盘价低于开盘价&#xff0c…

(转载)从0开始学matlab(第1天)—变量和数组

MATLAB 程序的基本数据单元是数组。一个数组是以行和列组织起来的数据集合&#xff0c;并且拥有一个数组名。数组中的单个数据是可以被访问的&#xff0c;访问的方法是数组名后带一个括号&#xff0c;括号内是这个数据所对应行标和列标。标量在 MATLAB 中也被当作数组来处理——…

JavaScript实现输入文字,指定输出遍数的代码

以下为实现输入文字&#xff0c;指定输出遍数的程序代码和运行截图 目录 前言 一、实现输入文字&#xff0c;指定输出遍数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.…

Prometheus+Alertmanager+webhook-dingtalk实现钉钉告警

文章目录 一、前提准备及规划二、安装及启动2.1 Prometheus安装启动2.2 Node_export安装启动2.3 Alertmanager安装启动2.4 Webhook-dingtalk安装启动 三、配置及测试3.1 Webhook-dingtalk配置钉钉webhook地址3.2 Alertmanager配置钉钉告警3.3 Prometheus集成Alertmanager及告警…

基于Docker的深度学习环境部署以及WSL和linux镜像问题

基于Docker的深度学习环境部署 1. 什么是Docker&#xff1f;2. 深度学习环境的基本要求3. Docker的基本操作3.1 在Windows上安装Docker3.2 在Ubuntu上安装Docker3.3 拉取一个pytorch的镜像3.4 部署自己的项目3.5 导出配置好项目的新镜像 4. 分享新镜像4.1 将镜像导出为tar分享给…

android应用的一种图标隐藏

在Android10之前&#xff0c;应用程序通过调用PackageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)函数来实现图标隐藏。 但是在android10之后&#xff0c;所有具有四大组件和需要申请…

C语言函数

C语言函数 一 函数的分类举例&#xff1a;*比较两个整数的大小**交换两个整数的值*&#xff08;传地址&#xff09; 二 参数实参形参 三 练习1.写一个函数判断一个数是不是素数2.写一个函数判断这一年是不是闰年3.写一个函数实现一个整型有序数组的二分查找4.写一个函数&#x…