新增员工
需求分析和设计
产品原型:
注意事项:
- 账号必须是唯一的
- 手机号为合法的11位手机号码
- 身份证号为合法的18位身份证号码
- 密码默认为123456
接口设计:
本项目约定:
- 管理端发出的请求,统一使用**/admin**作为前缀。
- 用户端发出的请求,统一使用**/user**作为前缀。
数据库设计(employee表):
字段名 | 数据类型 | 说明 | 备注 |
id | bigint | 主键 | 自增 |
name | varchar(32) | 姓名 | |
username | varchar(32) | 用户名 | 唯一 |
password | varchar(64) | 密码 | |
phone | varchar(11) | 手机号 | |
sex | varchar(2) | 性别 | |
id_number | varchar(18) | 身份证号 | |
status | Int | 账号状态 | 1正常 0锁定 |
create_time | Datetime | 创建时间 | |
update_time | datetime | 最后修改时间 | |
create_user | bigint | 创建人id | |
update_user | bigint | 最后修改人id |
代码开发
注意:
- 当前端提交的数据和实体类中对应的属性差别比较大时,建议使用 DTO 来封装数据
在EmployeeController中创建新增员工方法,接收前端提交的参数:
在EmployeeService接口中声明新增员工方法:
在EmployeeServiceImpl中实现新增员工方法:
在EmployeeMapper中声明insert方法:
功能测试
功能测试方式:
• 通过接口文档测试• 通过前后端联调测试
注意:由于开发阶段前端和后端是并行开发的,后端完成某个功能后,此时前端对应的功能可能还没有开发完成,导致无法进行前后端联调测试。所以在开发阶段,后端测试主要以接口文档测试为主。
启动服务: 访问http://localhost:8080/doc.html,进入新增员工接口
响应码:401 报错
报错原因: 由于JWT令牌校验失败,导致EmployeeController的save方法没有被调用
解决方法: 调用员工登录接口获得一个合法的JWT令牌
使用admin用户登录获取令牌
添加令牌:
- 将合法的JWT令牌添加到全局参数中
- 文档管理–>全局参数设置–>添加参数
接口测试:
前后端联调测试
代码完善
目前,程序存在的问题主要有两个:
- 录入的用户名已存,抛出的异常后没有处理
- 新增员工时,创建人id和修改人id设置为固定值
问题一
描述: 录入的用户名已存在,抛出的异常后没有处理
问题二
描述: 新增员工时,创建人 id 和修改人 id 设置为固定值
思考: 解析出登录员工 id 后,如何传递给 Service 的 save 方法?
- 通过 ThreadLocal 进行传递。
ThreadLocal
- ThreadLocal 并不是一个 Thread,而是 Thread 的局部变量。
- ThreadLocal 为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
注意:
- 客户端发送的每次请求,后端的 Tomcat 服务器都会分配一个单独的线程来处理请求
ThreadLocal 常用方法:
- public void set(T value) 设置当前线程的线程局部变量的值
- public T get() 返回当前线程所对应的线程局部变量的值
- public void remove() 移除当前线程的线程局部变量
在拦截器中解析出当前登录员工id,并放入线程局部变量中:
在Service中获取线程局部变量中的值:
员工分页查询
需求分析和设计
业务规则:
- 根据页码展示员工信息
- 每页展示10条数据
- 分页查询时可以根据需要,输入员工姓名进行查询
接口设计:
代码开发
根据分页查询接口设计对应的DTO:
后面所有的分页查询,统一都封装成PageResult对象:
员工信息分页查询后端返回的对象类型为:Result<PageResult>
根据接口定义创建分页查询方法:
在EmployeeService接口中声明pageQuery方法:
在 EmployeeServiceImpl 中实现 pageQuery 方法:
注意:此处使用 mybatis 的分页插件 PageHelper 来简化分页代码的开发。底层基于 mybatis 的拦截器实现。
在 EmployeeMapper 中声明 pageQuery 方法:
在 EmployeeMapper.xml 中编写SQL:
功能测试
注意jwt令牌的时间有效期
可以通过接口文档进行测试,也可以进行前后端联调测试,最后操作时间字段展示有问题,如下:
代码完善
问题描述: 操作时间字段显示有问题。
方式一:在属性上加入注解,对日期进行格式化
方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
/**
* 扩展Spring MVC框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// super.extendMessageConverters(converters);
log.info("扩展消息转换器");
//创建一个消息转换器
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以讲java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入容器中
converters.add(0,converter);
}
package com.sky.json;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
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.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对象
* 将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_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
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(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);
}
}
注意:不要写错了
启用禁用员工账号
需求分析和设计
产品原型:
业务规则:
- 分类名称必须是唯一的
- 分类按照类型可以分为菜品分类和套餐分类
- 新添加的分类状态默认为“禁用”
接口设计:
代码开发
根据接口设计中的请求参数形式对应的在 EmployeeController 中创建启用禁用员工账号的方法:
在 EmployeeService 接口中声明启用禁用员工账号的业务方法:
在 EmployeeServiceImpl 中实现启用禁用员工账号的业务方法:
在 EmployeeMapper 接口中声明 update 方法:
在 EmployeeMapper.xml 中编写SQL:
功能测试
可以通过接口文档进行测试,最后完成前后端联调测试即可
编辑员工
需求分析和设计
产品原型:
编辑员工功能涉及到两个接口:
•根据id查询员工信息•编辑员工信息
代码开发
在 EmployeeController 中创建 getById 方法:
在 EmployeeService 接口中声明 getById 方法:
在 EmployeeServiceImpl 中实现 getById 方法:
在 EmployeeMapper 接口中声明 getById 方法:可以先通过接口测试确认数据回显是否有问题,如果没有问题再继续开发
在 EmployeeController 中创建 update 方法:
在 EmployeeService 接口中声明 update 方法:
在 EmployeeServiceImpl 中实现 update 方法:
/**
* 编辑员工信息
* @param employeeDTO
*/
@Override
public void update(EmployeeDTO employeeDTO) {
// update employee set ... where id = ?
Employee employee = new Employee();
//对象的属性拷贝
BeanUtils.copyProperties(employeeDTO, employee);
//设置修改人和修改时间
employee.setUpdateUser(BaseContext.getCurrentId());
employee.setUpdateTime(LocalDateTime.now());
employeeMapper.update(employee);
}
功能测试
通过Swagger接口文档进行测试,通过后再前后端联调测试即可
导入分类模块功能代码
需求分析和设计
产品原型:
业务规则:
• 分类名称必须是 唯一 的• 分类按照类型可以分为 菜品分类 和 套餐分类• 新添加的分类状态默认为 “禁用”
接口设计:
• 新增分类• 分类分页查询• 根据 id 删除分类• 修改分类• 启用禁用分类• 根据类型查询分类
数据库设计(category表):