外卖项目–Springboot回顾Day2

任务:

  1. 新增员工
  2. 员工分页查询
  3. 启用禁用员工账号
  4. 编辑员工
  5. 导入分类模块功能代码

每一个任务的完成逻辑:

  1. 需求分析和设计(接口设计)
  2. 代码开发(根据新增员工接口设计对应DTO)
  3. 功能测试
  4. 代码完善

本项目约定:

  • 管理端发出的请求,统一使用/admin作为前缀

  • 用户端发出的请求,统一使用/user作为前缀

新增员工

需求分析和设计

1

1. 在Controller中写方法save

接受前端的数据并封装为EmployeeDTO类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 新增员工
* @param employeeDTO
* @return
*/
@PostMapping
@ApiOperation("新增员工")
public Result save(@RequestBody EmployeeDTO employeeDTO){
log.info("新增员工{}",employeeDTO);
System.out.println("当前线程的id:" + Thread.currentThread().getId());
employeeService.save(employeeDTO);
return Result.success();
}

2. 在Service层中的接口声明方法save,并在实现类中实现

需要在Service层中将EmployeeDTO类转为实体类,方便插入到数据库

EmployeeService.java

1
2
3
4
5
/**
* 新增员工
* @param employeeDTO
*/
void save(EmployeeDTO employeeDTO);

EmployeeServicelmpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 新增员工
* @param employeeDTO
*/
public void save(EmployeeDTO employeeDTO) {
System.out.println("当前线程的id:" + Thread.currentThread().getId());
Employee employee= new Employee();

//对象属性拷贝
BeanUtils.copyProperties(employeeDTO,employee);

//设置剩余属性
//设置账号状态,默认正常状态,1表示正常,0表示锁定
employee.setStatus(StatusConstant.ENABLE);

//设置密码,默认为123456
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

//设置当前记录的创建时间你和修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());

//修改当前记录创建人id和修改人id
employee.setCreateUser(10L);
employee.setUpdateUser(10L);

employeeMapper.insert(employee);
}

在Service持久层中使用实体类

所以需要将EmployeeDTO转换为实体类,通过使用对象属性拷贝,来避免过多的get,set方法造成代码冗余

3. 在Mapper层中实现数据插入

由于操作较为简洁,我通过@Insert注解的方式直接在EmployeeMapper.java中实现

EmployeeMapper.java

1
2
3
4
5
6
7
8
/**
* 插入员工数据
* @param employee
*/
@Insert("insert into employee (name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user) " +
"values " +
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")
void insert(Employee employee);

功能测试

  • 通过接口文档测试
  • 前后端调测

代码完善

问题:

  1. 录入的用户名若已存在,抛出异常后没有处理
  2. 新增员工时,创建人id和修改人id设为了固定值”10L”

解决:

重载方法exceptionHandler

在全局异常处理器GlobalExceptionHandler.java中重载方法exceptionHandler来处理报出的SQLIntegrityConstraintViolationException类异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 处理SQL异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
//Duplicate entry 'zhangsan' for key 'idx_username'
String message = ex.getMessage();
if(message.contains("Duplicate entry")){
String[] split = message.split(" ");
String username =split[2];
String msg = username + MessageConstant.ALREADY_EXIST;
return Result.error(msg);
}else{
return(Result.error(MessageConstant.UNKNOWN_ERROR));
}
}

使用TreadLocal来保存真实创建人id和修改人id

思路:

  1. 员工登录成功后会生成Jwt令牌并响应给前端
  2. 后续请求中,前端会携带Jwt令牌,通过令牌可以解析出当前员工id
  3. 通过TreadLocal将解析出的当前员工id传递给Service的save方法

Treadlocal:并不是一个线程,而是线程的一个局部变量:为每个线程单独提供一份存储空间,具有县城隔离效果,只有在同一线程内才能获取到对应的值

Treadlocal常用方法:

  • public void set(T value) //设置当前线程的局部变量的值
  • public T get() //得到当前线程的局部变量的值
  • public void remove() //移除当前线程的局部变量的值

本项目已经把这三个方法封装到了工具类BaseContext

员工分类查询

需求分析和设计

2

代码开发

依据分页查询接口设计对应的DTO

后面所有的分页查询,统一封装成PageResult对象,再封装为Result,返回给前端

PageResult.java

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 封装分页查询结果
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

private long total; //总记录数

private List records; //当前页数据集合
}

之后完成剩余代码

EmployeeServicelmpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 员工分页查询
* @param employeePageQueryDTO
* @return
*/
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO){
//使用PageHelper插件进行分页查询
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());

Page<Employee> page=employeeMapper.pageQuery(employeePageQueryDTO);

long total = page.getTotal();
List<Employee> record = page.getResult();

return new PageResult(total,record);
}

问题:返回给前端的日期显示不明确,eg:202309221823,期望显示则是2023年9月22日18:23

解决方法:

  1. 在属性上加入注释,对日期进行格式化
  2. 在WebMvcConfignration中拓展Spring MVC的消息转换器,统一对日期类型进行格式化处理

WebMvcConfignration.java

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 扩展spring MVC框架的消息转化器
* @param converters
*/
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters){
log.info("扩展消息转换器...");
//创建一个消息转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//为消息转换器设置一个对象转换器,对象转换器可以将Java对象转化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入到容器中
converters.add(0,converter);
}

序列化:Java对象—–>Json对象

启用/禁用员工账号

需求分析和设计

3

4

代码开发

EmployeeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 启用或禁用员工账号
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("启用或禁用员工账号")
public Result startOrStop(@PathVariable("status") Integer status,Long id){
log.info("启用或禁用员工账号:{},{}",status,id);
employeeService.startOrStop(status,id);
return Result.success();
}

EmployeeServicelmpl.java

1
2
3
4
5
6
7
8
public void startOrStop(Integer status, Long id) {
Employee employee = Employee.builder()
.status(status)
.id(id)
.build();

employeeMapper.update(employee);
}

EmployeeMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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>

编辑员工

EmployeeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 根据id查询员工信息
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询员工信息")
public Result<Employee> getById(@PathVariable("id") Long id){
Employee employee = employeeService.getById(id);
return Result.success(employee);
}

/**
* 编辑员工信息
* @param employeeDTO
* @return
*/
@PutMapping
@ApiOperation("编辑员工信息")
public Result update(@RequestBody EmployeeDTO employeeDTO){
log.info("编辑员工信息:{}",employeeDTO);
employeeService.update(employeeDTO);
return Result.success();
}

EmployeeServicelmpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 根据id查询员工信息
* @param id
* @return
*/
public Employee getById(Long id){
Employee employee = employeeMapper.getById(id);
employee.setPassword("******");
return employee;
}


/**
* 剪辑员工信息
* @param employeeDTO
*/
public void update(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
BeanUtils.copyProperties(employeeDTO,employee);

employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId());

employeeMapper.update(employee);
}