苍穹外卖
Day2:
a. 员工管理
(1). 新增员工
- 在EmployeeServiceImpl中,及其父类接口
/**
* 新增员工
* @param employeeDTO
*/
@Override
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
// 对象属性拷贝
BeanUtils.copyProperties(employeeDTO, employee);
// 设置账号状态,默认正常
employee.setStatus(StatusConstant.ENABLE);
// 设置密码,默认密码123456
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
// 设置当前记录的创建时间和修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
// 设置当前记录创建人id和修改人id
// TODO 后期需要改为当前登录用户的Id
employee.setCreateUser(10L);
employee.setUpdateUser(10L);
employeeMapper.insert(employee);
}
- EmployeeMapper,写入相关SQL代码
/**
* 插入员工数据
* @param employee
*/
@Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) " +
"values " +
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
void insert(Employee employee);
- EmployeeController中,将DTO传入service类中进行处理
/**
* 新增员工
* @param employeeDTO
* @return
*/
@PostMapping
@ApiOperation("新增员工")
public Result save(@RequestBody EmployeeDTO employeeDTO){
log.info("新增员工: {}", employeeDTO);
employeeService.save(employeeDTO);
return Result.success();
}
注意:由于开发阶段前端和后端是并行开发的,后端完成某个功能后,此时前端对应的功能可能还没有开发完成,导致无法进行前后端联调测试。所以在开发阶段,后端测试主要以接口文档测试为主。
一. 代码完善
程序存在的问题:
- 录入的用户名已存在,抛出异常后没有处理
解决方法:
- GlobalExceptionHandler中,自定义一个处理SQL异常的方法
/**
* 处理SQL异常
* @param exception
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException exception){
// Duplicate entry 'zhangsan' for key 'idx_username'
String message = exception.getMessage();
if (message.contains("Duplicate entry")){
String[] split = message.split(" ");
String username = split[2];
String msg = username.substring(1, username.length() - 1) + MessageConstant.ALREADY_EXISTS;
return Result.error(msg);
}else {
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
二. 代码完善
程序存在的问题:
- 新增员工时,创建人id和修改人id设置为了固定值
解决方法:
- ThreadLocal 并不是一个Thread,而是Thread的局部变量。
- ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
ThreadLocal常用方法:
- public void set(T value) 设置当前线程的线程局部变量的值
- public T get() 返回当前线程所对应的线程局部变量的值
- public void remove() 移除当前线程的线程局部变量
在JwtTokenAdminInterceptor中,加入BaseContext.setCurrentId(empId);,设置id
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
在EmployeeServiceImpl中,BaseContext.getCurrentId() 获取id,并存入数据库
// 设置当前记录创建人id和修改人id
// TODO 后期需要改为当前登录用户的Id
employee.setCreateUser(BaseContext.getCurrentId());
employee.setUpdateUser(BaseContext.getCurrentId());
(2). 员工分页查询
业务规则:
-
根据页码展示员工信息
-
每页展示10条数据
-
分页查询时可以根据需要,输入员工姓名进行查询
EmployeeController,根据接口定义创建分页查询方法:
/**
* 员工分页查询
* @param employeePageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("员工分页查询")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
log.info("员工分页查询,参数为:{}",employeePageQueryDTO);
PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
return Result.success(pageResult);
}
在EmployeeServiceImpl声明pageQuery方法,及其父类接口:
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//select * from employee limit 10,20
//基于PageHelper插件实现动态分
PageHelper.startPage(employeePageQueryDTO.getPage(),
employeePageQueryDTO.getPageSize());
Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
return new PageResult(page.getTotal(), page.getResult());
}
注意:此处使用 mybatis 的分页插件 PageHelper 来简化分页代码的开发。底层基于 mybatis 的拦截器实现
在 EmployeeMapper 中声明 pageQuery 方法:
/**
* 分页查询
* @param employeePageQueryDTO
* @return
*/
Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
在 EmployeeMapper.xml 中编写SQL:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.EmployeeMapper">
<select id="pageQuery" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name},'%')
</if>
</where>
order by create_time desc
</select>
</mapper>
一. 代码完善
程序存在的问题:
- 日期显示并不是正常按年月日输出
解决办法:
- 方式一:在属性上加入注解,对日期进行格式化
在实体类中,添加注解
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
- 方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
/**
* 扩展Spring MVC框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器");
// 创建一个小希转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// 需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
// 将自己的消息转换器加入到容器中
converters.add(0, converter);
}
(3). 启用禁用员工账号
业务规则:
- 可以对状态为“启用” 的员工账号进行“禁用”操作
- 可以对状态为“禁用”的员工账号进行“启用”操作状态
- 为“禁用”的员工账号不能登录系统
根据接口设计中的请求参数形式对应的在 EmployeeController 中创建启用禁用员工账号的方法:
/**
* 启用禁用员工账号
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("启用禁用员工账号")
public Result startOrStop(@PathVariable Integer status, Long id){
log.info("启用禁用员工账号:{}, {}", status, id);
employeeService.startOrStop(status, id);
return Result.success();
}
在 EmployeeServiceImpl 中声明启用禁用员工账号的业务方法,及其父类接口:
/**
* 启用禁用员工账号
* @param status
* @param id
*/
@Override
public void startOrStop(Integer status, Long id) {
Employee employee = Employee.builder()
.status(status)
.id(id)
.build();
employeeMapper.update(employee);
}
在 EmployeeMapper 接口中声明 update 方法:
/**
* 根据id修改员工信息
* @param employee
*/
void update(Employee employee);
在 EmployeeMapper.xml 中编写SQL:
<update id="update" parameterType="Employee">
update employee
<set>
<if test="name != null">name = #{name},</if>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="sex != null">sex = #{sex},</if>
<if test="idNumber != null">id_number = #{idNumber},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUser != null">update_user = #{updateUser},</if>
<if test="status != null">status = #{status},</if>
</set>
where id = #{id}
</update>
(4). 编辑员工
编辑员工功能涉及到两个接口:
- 根据id查询员工信息 (GET)
- 编辑员工信息 (POST)
一. 根据id查询员工信息 (GET)
在 EmployeeController 中创建 getById 方法:
/**
* 根据Id查询员工
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据Id查询员工信息")
public Result<Employee> getById(@PathVariable Long id){
Employee employee = employeeService.getById(id);
return Result.success(employee);
}
在 EmployeeServiceImpl 中实现 getById 方法,及其父类接口:
/**
* 根据Id查询员工
* @param id
* @return
*/
@Override
public Employee getById(Long id) {
Employee employee = employeeMapper.getById(id);
employee.setPassword("****");
return employee;
}
在 EmployeeMapper 接口中声明 getById 方法:
/**
* 根据Id查询员工
* @param id
* @return
*/
@Select("select * from employee where id = #{id}")
Employee getById(Long id);
二. 编辑员工信息 (POST)
在 EmployeeController 中创建 update 方法:
/**
* 编辑员工信息
* @param employeeDTO
* @return
*/
@PutMapping
@ApiOperation("编辑员工信息")
public Result update(@RequestBody EmployeeDTO employeeDTO){
log.info("编辑员工信息,{}", employeeDTO);
employeeService.update(employeeDTO);
return Result.success();
}
在 EmployeeServiceImpl 中实现 update 方法,及其父类接口:
/**
* 编辑员工信息
* @param employeeDTO
*/
@Override
public void update(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
BeanUtils.copyProperties(employeeDTO, employee);
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.update(employee);
}
在启用禁用员工账号开发时,已在EmployeMapper.xml中编写了动态的update方法,所以在此方法中需要更新员工信息,也可以调用此方法
b. 分类模块功能代码
业务规则:
- 分类名称必须是唯一的
- 分类按照类型可以分为菜品分类和套餐分类
- 新添加的分类状态默认为“禁用”
接口设计:
- 新增分类
- 分类分页查询
- 根据id删除分类
- 修改分类
- 启用禁用分类
- 根据类型查询分类