day02员工管理,分类管理

  1. 1. 三层架构
  2. 2. employee表结构
  3. 3. 1. 新增员工
    1. 3.1. 1.1 代码实现逻辑
    2. 3.2. 1.2 接口文档中添加令牌(防止JWT校验失败而无法正常测试)
    3. 3.3. 1.3 动态获取当前登录员工的id
      1. 3.3.1. 1.3.1 ThreadLocal
  4. 4. 2. 员工分页查询
    1. 4.1. 2.1 代码实现逻辑
    2. 4.2. 2.2 日期时间格式化
  5. 5. 3. 启用禁用员工账号
    1. 5.1. 3.1 代码实现逻辑
  6. 6. 4. 编辑员工
    1. 6.1. 4.1代码实现逻辑
      1. 6.1.1. 根据id查询员工信息(getById)
      2. 6.1.2. 编辑员工信息
  7. 7. category表结构:
  8. 8. 5.分类管理

三层架构

Controller 类前使用 @RestController,
@Api(使接口文档更清晰明了),
@RequestMapping(当前类下所有方法的公共url前缀),
@Slf4j(在后端控制台输出日志 log.info(“…”))。
类中使用 @Autowired 注入 Service 接口依赖。
新增使用 post 请求,对应 @PostMapping 注解,insert语句。
删除使用 delete 请求,对应 @DeleteMapping 注解,delete语句。
修改使用 put 请求,对应 @PutMapping 注解,update语句。
查询使用 get 请求,对应 @GetMapping 注解,select语句。
ServiceImpl 类前使用 @Servicr。实现 Service 接口,要使用 implements 关键字。
类中使用 @Autowired 注入 Mapper 接口依赖。
Mapper 接口前使用 @Mapper

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

其中,employee表中的status字段已经设置了默认值1,表示状态正常。

1. 新增员工

注意:当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据。
xxxServiceImpl 类实现 xxxService 接口的方法可用 ctrl + i 自动生成。

1.1 代码实现逻辑

Impl:
new一个对象,用 BeanUtils.copyProperties(xxxDTO, xxx); 将DTO拷贝过来,补齐DTO中缺少的属性

Mapper:
SQL语句写在 @Insert 注解中 “insert into 表名 属性列表 values 参数列表”,在application.yml中已开启驼峰命名,故id_number和idNumber可对应。

1.2 接口文档中添加令牌(防止JWT校验失败而无法正常测试)

使用admin用户登录获取令牌
将合法的JWT令牌添加到全局参数中:
文档管理–>全局参数设置–>添加参数

1.3 动态获取当前登录员工的id

员工登录成功后会生成JWT令牌并响应给前端,后续请求中,前端会携带JWT令牌,通过JWT令牌可以解析出当前登录员工id:JwtTokenAdminInterceptor.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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.sky.interceptor;

/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

@Autowired
private JwtProperties jwtProperties;

/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//..............

//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());

//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);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}

思考:解析出登录员工id后,如何传递给Service的save方法?
通过ThreadLocal进行传递。

1.3.1 ThreadLocal

介绍:
ThreadLocal 并不是一个Thread,而是Thread的局部变量。 ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

常用方法:

  • public void set(T value) 设置当前线程的线程局部变量的值
  • public T get() 返回当前线程所对应的线程局部变量的值
  • public void remove() 移除当前线程的线程局部变量

初始工程中已经封装了 ThreadLocal 操作的工具类 BaseContext

在拦截器中解析出当前登录员工id,并放入线程局部变量中:JwtTokenAdminInterceptor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//2、校验令牌
try {
//.................
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
/////将用户id存储到ThreadLocal////////
BaseContext.setCurrentId(empId);
////////////////////////////////////
//3、通过,放行
return true;
} catch (Exception ex) {
//......................
}

在ServiceImpl中获取线程局部变量中的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 新增员工
*
* @param employeeDTO
*/
public void save(EmployeeDTO employeeDTO) {
//.............................

//设置当前记录创建人id和修改人id
employee.setCreateUser(BaseContext.getCurrentId());
employee.setUpdateUser(BaseContext.getCurrentId());

employeeMapper.insert(employee);
}

2. 员工分页查询

注意事项:请求参数类型为Query,不是json格式提交,在路径后直接拼接。/admin/employee/page?name=zhangsan,返回数据中records数组中使用Employee实体类对属性进行封装。后面所有的分页查询,统一都封装为PageResult对象。

2.1 代码实现逻辑

在 xxxImpl 中实现 pageQuery 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 分页查询
*
* @param employeePageQueryDTO
* @return
*/
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
// select * from employee limit 0,10
//开始分页查询,获取页号和页面记录数
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());

Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);//后续定义

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

return new PageResult(total, records);
}

注意:此处使用 mybatis 的分页插件 PageHelper 来简化分页代码的开发。底层基于 mybatis 的拦截器实现。
在 src/main/resources/mapper/EmployeeMapper.xml 中编写SQL:

1
2
3
4
5
6
7
8
9
<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>

2.2 日期时间格式化

在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理

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);
}

时间格式定义,sky-common模块中

1
2
3
4
5
6
7
8
9
10
package com.sky.json;

public class JacksonObjectMapper extends ObjectMapper {

//.......
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
//.......

}
}

3. 启用禁用员工账号

业务规则:

  • 可以对状态为“启用” 的员工账号进行“禁用”操作
  • 可以对状态为“禁用” 的员工账号进行“启用”操作
  • 状态为“禁用”的员工账号不能登录系统

3.1 代码实现逻辑

xxx.buidler().build()中间加入关键属性设置。

在 EmployeeServiceImpl 中实现启用禁用员工账号的业务方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 启用禁用员工账号
*
* @param status
* @param id
*/
public void startOrStop(Integer status, Long id) {
Employee employee = Employee.builder()
.status(status)
.id(id)
.build();

employeeMapper.update(employee);
}

在 EmployeeMapper.xml 中编写SQL:

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>

4. 编辑员工

注:点击修改时,数据应该正常回显到修改页面。

根据上述分析,编辑员工功能涉及到两个接口:

  • 根据id查询员工信息
  • 编辑员工信息

4.1代码实现逻辑

根据id查询员工信息(getById)

密码不回显,故在 Impl 的方法中要有employee.setPassword(“****”);

编辑员工信息

Impl:
new一个对象,用 BeanUtils.copyProperties(xxxDTO, xxx); 将DTO拷贝过来,补齐DTO中缺少的属性(更新时间、更新人),update已在启用禁用员工时写好。

category表结构:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 分类名称 唯一
type int 分类类型 1菜品分类 2套餐分类
sort int 排序字段 用于分类数据的排序
status int 状态 1启用 0禁用
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

5.分类管理

根据原型分析,菜品分类模块共涉及6个接口。

  • 新增分类
  • 分类分页查询
  • 根据id删除分类
  • 修改分类
  • 启用禁用分类
  • 根据类型查询分类

原理与员工管理一样,略。