某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 + JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。
相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PDe_7uIg?pwd=x548
提取码:x548
文章目录
- 1.问题分析
- 2.数据模型
- 3.代码实现
- 3.1 实现逻辑分析
- 3.2 Controller 层具体实现
- 3.3 编写全局异常处理器
- 4.功能测试
1.问题分析
后台系统中可以直接管理员工信息,通过新增员工来添加后台系统用户。点击【添加员工】按钮跳转到新增页面,如下:
2.数据模型
新增员工其实就是将我们新增页面录入的员工数据插入到 employee 表中,需要注意的是,employee 表单中对 username 字段加了唯一约束,因为 username 是员工的登陆账号,必须是唯一的:
另外,employee 表中的 status 字段已经设置了默认值为 1,表示员工状态正常:
3.代码实现
3.1 实现逻辑分析
在开发代码之前,需要梳理一下整个程序的执行过程:
- 页面发送 ajax 请求,将新增员工页面中输入的数据以 json 格式提交到服务端;
- 服务端 Controller 接收页面提交的数据并调用 Service 将数据进行保存;
- Service 调用 Mapper(dao)操作数据库,保存数据。
下面通过新增员工页面填写一些测试数据,看一下对应的请求 URL 和携带的数据形式:
点击保存后查看浏览器控制台相关请求数据:
首先可以明确的是它会发送一个 POST 请求,请求的 URL 为 “http://localhost:8080/employee”。下面再看看该请求携带的参数:
可以看到,携带的 json 参数就是我们在新增页面填写的信息。
3.2 Controller 层具体实现
EmployeeController
中新增员工部分代码如下:
@RestController
@RequestMapping("/employee")
public class EmployeeController {
/**
* 处理新增员工请求
* @param request 请求对象
* @param employee 员工对象
* @return 响应对象
*/
@PostMapping
public R<String> save(HttpServletRequest request, @RequestBody Employee employee) {
// 设置初始密码为 12346,需要进行 md5 加密再存入数据库
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
// 设置创建时间
employee.setCreateTime(LocalDateTime.now());
// 设置更新时间
employee.setUpdateTime(LocalDateTime.now());
// 获取当前登录的员工 id,作为创建人
Long empId = (Long) request.getSession().getAttribute("employee");
employee.setCreateUser(empId);
employee.setUpdateUser(empId);
// 保存员工信息
employeeService.save(employee);
// 返回保存成功结果
return R.success("新增员工成功");
}
// 省略其他方法
}
下面重启应用,我们再次在新增员工页面进行测试:
点击保存后,刷新并查看数据库端对应的 employee 表中就能看到新增的员工记录:
3.3 编写全局异常处理器
上面我们提到,username 将作为员工账号,是唯一的。那么上面我们已经添加过 username=zhangsan 了,现在我们再次添加一个 username=zhangsan 的员工,控制台就会报错并且数据表中也不会有具体的记录:
此时需要我们的程序进行异常捕获,通常有两种处理方式:
- 在 Controller 层方法中加入 try-catch 进行异常捕获(不建议);
- 使用异常处理器进行全局异常捕获。
我们以第二种方式为例,在项目下新建一个 handler
包用于存放我们的各种处理器,然后再该报下实现我们的自定义全局异常处理器 GlobalExceptionHandler
,代码如下:
package cn.javgo.reggie_take_out.handler;
import cn.javgo.reggie_take_out.common.R;
import org.springframework.context.annotation.Conditional;
import org.springframework.web.bind.annotation.*;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 全局异常处理器
*/
@ControllerAdvice(annotations = {RestController.class, Conditional.class})
@ResponseBody
public class GlobalExceptionHandler {
/**
* 处理 SQLIntegrityConstraintViolationException 异常
* @param ex 异常对象
* @return 响应对象
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
if(ex.getMessage().contains("Duplicate entry")){
return R.error("该用户名已存在");
}
return R.error("未知错误");
}
}
简单解释一下上面涉及到的几个注解:
@ControllerAdvice
:用于全局异常处理和全局数据绑定的注解。它允许开发人员定义一个通用的类,该类将应用于整个应用程序中的所有控制器。通常,它被用于编写全局异常处理程序,以便集中处理和管理应用程序中的异常情况。上面我们通过该注解的annotations
参数指定了处理使用了@RestController
和@Controller
注解的类抛出的异常。@ResponseBody
:用于将方法的返回值直接绑定到响应体中,而不是将其解析为视图。即当一个方法或类带有@ResponseBody
注解时,Spring 将自动将方法的返回值转换为适当的响应体格式(如 JSON)并返回给客户端。@ExceptionHandler
:用于在控制器中处理特定类型的异常。当控制器中抛出异常时,如果存在匹配的@ExceptionHandler
方法,它将被调用来处理该异常。上面我们传入的异常其实就是控制台错误信息中的异常。
判断逻辑也很简单,那就是根据异常信息的特点进行判断:
如果出现 SQLIntegrityConstraintViolationException 异常,且异常信息中包含 “Duplicate entry”,则返回 “该用户已存在”即可。
4.功能测试
下面我们再次添加一个 username=zhangsan 的员工记录,点击【添加按钮】后页面顶部就会显示服务端处理后的传回的错误信息: