整理记录下学习整个瑞吉外卖项目,详细代码可在我的Gitee仓库瑞吉外卖实战克隆下载学习使用!
4. 登录功能
4.1 通用结果返回类
此类用于规范封装返回结果,代码如下:
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> Result<T> success(T object) {
Result<T> r = new Result<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> Result<T> error(String msg) {
Result r = new Result();
r.msg = msg;
r.code = 0;
return r;
}
public Result<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
4.2 需求分析
- 登录页面展示,点击即可,如图
- 查看登录请求信息
4.3 数据模型
查看数据模型,打开数据库员工表,结构如图
4.4 代码开发
- 编写Employee实体类,如下,有源代码也可直接复制
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;//身份证号码
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
}
- 编写Mapper层,代码如下
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee>{
}
- 编写业务层代码,如图
- 编写controller层代码,流程如图
创建EmployeeController类,代码如下:
@Slf4j
@RestController
@RequestMapping("/employee")
//用构造器方法进行bean注入
@RequiredArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
//员工登录
@PostMapping("/login")
public Result<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
//1、将页面提交的密码password进行md5加密处理
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
//2、根据页面提交的用户名username查询数据库
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
//3、如果没有查询到则返回登录失败结果
if(emp == null){
return Result.error("登录失败");
}
//4、密码比对,如果不一致则返回登录失败结果
if(!emp.getPassword().equals(password)){
return Result.error("登录失败");
}
//5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
if(emp.getStatus() == 0){
return Result.error("账号已禁用");
}
//6、登录成功,将员工id存入Session并返回登录成功结果
request.getSession().setAttribute("employee",emp.getId());
return Result.success(emp);
}
}
4.5 测试
测试登录功能,断点调试,输入正确账号密码,如图
不正确账号,如图
正确账号,错误密码,如图
5. 退出功能
5.1需求分析
5.2 代码开发
- 在EmployeeController中添加退出方法,如下:
@PostMapping("/logout")
public Result<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return Result.success("退出成功");
}
5.3 测试
启动项目并进行登录后,再次点击退出则切换到登录界面,退出成功!
6. 员工管理
6.1 效果展示
- 在进行员工登录后才能进入到员工管理界面
- 员工列表。可以添加员工,也可以编辑选中员工信息,还可以选择是否启用或禁用此员工登录
- 添加员工。
6.2 完善登录功能
6.2.1问题分析
6.2.2 代码开发
编写过滤器进行完善,并在启动类加注解@ServletComponentSacn
,代码如下:
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter{
//路径匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1、获取本次请求的URI
String requestURI = request.getRequestURI();// /backend/index.html
log.info("拦截到请求:{}",requestURI);
//定义不需要处理的请求路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
//2、判断本次请求是否需要处理
boolean check = check(urls, requestURI);
//3、如果不需要处理,则直接放行
if(check){
log.info("本次请求{}不需要处理",requestURI);
filterChain.doFilter(request,response);
return;
}
//4-1、判断登录状态,如果已登录,则直接放行
if(request.getSession().getAttribute("employee") != null){
log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
filterChain.doFilter(request,response);
return;
}
log.info("用户未登录");
//5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
response.getWriter().write(JSON.toJSONString(Result.error("NOTLOGIN")));
return;
}
/**
* 路径匹配,检查本次请求是否需要放行
* @param urls
* @param requestURI
* @return
*/
public boolean check(String[] urls,String requestURI){
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if(match){
return true;
}
} return false;
}
}
6.3 测试
经过直接访问测试后,控制台输出成功,如图
6.4 新增员工功能
6.4.1 需求分析
后台系统可以管理员工信息,通过新增员工来添加后台系统用户,点击[添加员工]按钮跳转到新增页面,如图
6.4.2 数据模型
新增员工就是将新增页面录入员工数据插入到employee表中,但注意的是employee表中对应的username字段加了唯一约束,登录账号,必须唯一。
其中对应的状态status也为默认1
6.4.3 代码开发
controller层添加新增方法
@PostMapping
public Result<String> save(HttpServletRequest request,@RequestBody Employee employee){
log.info("新增员工,员工信息:{}",employee.toString());
//设置初始密码123456,需要进行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 Result.success("新增员工成功");
}
6.4.4 测试
功能测试,登录之后添加员工,如图
后台显示插入成功,如图
6.4.5 改进
- 若再次添加同样的数据则会报错,添加exception处理类,如下:
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法,处理数据库重复添加问题
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if(ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg = split[2] + "已存在";
return Result.error(msg);
}
return Result.error("未知错误");
}
- 输入同样数据再次测试,如图,会提示已存在
6.5 员工分页查询功能
6.5.1 需求分析
系统中一般员工数据比较多,一般采用分页查询来显示列表数据,如图
6.5.2 代码开发
- 添加MyBatisPlus分页查询拦截器,如下:
@Configuration
public class MyBatisPlusConfig{
//加入分页查询拦截
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
- 添加查询接口,如下:
@GetMapping("/page")
public Result<Page> getEmployeesPage(int page, int pageSize, String name){
log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);
//构造分页构造器
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件
queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Employee::getUpdateTime);
//执行查询
employeeService.page(pageInfo,queryWrapper);
return Result.success(pageInfo);
}
6.5.3 测试
启动项目后刷新前台,测试成功,如图
6.6 启用/禁用员工账号功能
6.6.1 需求分析
管理员可以禁用或启用用户,如图
普通用户不能操作,如图
6.6.2 代码开发
- 编写对象转换器,防止数据精度丢失,比如Long数据丢精度失,代码如下:
/**
* 对象映射器:基于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);
}
}
- 配置映射器,在之前的WebMVCConfig类中添加上文定义的扩展器。如下:
/**
* 扩展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将上面的消息转换器对象追加到mvc框架的转换器集合中
converters.add(0,messageConverter);
}
- 编写禁用员工账号controller层,如下
@PutMapping
public Result<String> updateEmployee(HttpServletRequest request,@RequestBody Employee employee){
log.info(employee.toString());
Long empId = (Long) request.getSession().getAttribute("employee");
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(empId);
employeeService.updateById(employee);
return Result.success("员工信息修改成!");
}
6.6.3 测试
点击禁用或启用成功,如图
6.7 编辑员工信息功能
6.7.1 需求
在员工管理列表页面点击编辑按钮,跳转到编辑页面,在编辑页面回显员工信息进行修改,最后点击保存按钮完成编辑操作。
6.7.2 代码开发
- 编写获取员工信息代码,如下:
@GetMapping("/{id}")
public Result getEmployeeById(@PathVariable Long id){
log.info("查找id为{}的员工信息",id);
Employee employee = employeeService.getById(id);
if(employee == null) return Result.error("获取信息失败!");
return Result.success(employee);
}
6.7.3 测试
改jerry账号姓名为李四如图
保存后如图显示,修改成功