课程内容
- 新增员工
- 员工分页查询
- 启用禁用员工账号
- 编辑员工
- 导入分类模块功能代码
功能实现: 员工管理 菜品分类管理
开发前准备
- 需求分析与设计
- 根据产品原型进行分析
- 需要添加以下信息
- 账号
- 员工姓名
- 手机号
- 性别
- 身份证号
- 注意事项
- 账号必须唯一
- 手机号是合法的11位手机号
- 身份证号为合法的18位数字
- 密码暂时为123456
- 接口设计
- 根据提供资料
- 请求路径 /admin/employee
- 请求方式 post
- 请求参数以及返回数据
- 管理端使用/damin前缀
- 用户端用/user前嘴
- 数据表设计
字段名 | 数据类型 | 说明 | 备注 |
---|---|---|---|
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
- 因为前端提交的数据与实体类对应的属性差别比较大
- 建议使用DTO来封转数据
- 进入sky-pojo模块,在com.sky.dto包下,已定义EmployeeDTO
package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class EmployeeDTO implements Serializable {
private Long id;
private String username;
private String name;
private String phone;
private String sex;
private String idNumber;
}
- Controller层
EmployeeController中创建新增员工方法
进入到sky-server模块中,在com.sky.controller.admin包下,在EmployeeController中创建新增员工方法,接收前端提交的参数。
/**
* 新增员工
* @param employeeDTO
* @return
*/
@PostMapping
@ApiOperation("新增员工")
public Result save(@RequestBody EmployeeDTO employeeDTO){
log.info("新增员工:{}",employeeDTO);
employeeService.save(employeeDTO);//该方法后续步骤会定义
return Result.success();
}
**注:**Result类定义了后端统一返回结果格式。
进入sky-common模块,在com.sky.result包下定义了Result.java
package com.sky.result;
import lombok.Data;
import java.io.Serializable;
/**
* 后端统一返回结果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
#### 1.2.2 Controller层
**EmployeeController中创建新增员工方法**
进入到sky-server模块中,在com.sky.controller.admin包下,在EmployeeController中创建新增员工方法,接收前端提交的参数。
```java
/**
* 新增员工
* @param employeeDTO
* @return
*/
@PostMapping
@ApiOperation("新增员工")
public Result save(@RequestBody EmployeeDTO employeeDTO){
log.info("新增员工:{}",employeeDTO);
employeeService.save(employeeDTO);//该方法后续步骤会定义
return Result.success();
}
**注:Result类定义了后端统一返回结果格式。
进入sky-common模块,在com.sky.result包下定义了Result.java
package com.sky.result;
import lombok.Data;
import java.io.Serializable;
/**
* 后端统一返回结果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
- Service层接口
/**
* 新增员工
* @param employeeDTO
*/
void save(EmployeeDTO employeeDTO);
- Service层实现类
在EmployeeServiceImpl中实现新增员工方法
com.sky.server.impl.EmployeeServiceImpl中创建方法
/**
* 新增员工
*
* @param employeeDTO
*/
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
//对象属性拷贝
BeanUtils.copyProperties(employeeDTO, employee);
//设置账号的状态,默认正常状态 1表示正常 0表示锁定
employee.setStatus(StatusConstant.ENABLE);
//设置密码,默认密码123456employee.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);//后续步骤定义
}
在sky-common模块com.sky.constants包下已定义StatusConstant.java
package com.sky.constant;
/**
* 状态常量,启用或者禁用
*/
public class StatusConstant {
//启用
public static final Integer ENABLE = 1;
//禁用
public static final Integer DISABLE = 0;
}
- Mapper层
- 在EmployeeMapper中声明insert方法
/**
* 插入员工数据
* @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);
在application.yml中已开启驼峰命名,故id_number和idNumber可对应。
mybatis:
configuration:
#开启驼峰命名
map-underscore-to-camel-case: true
接口测试
访问Swaggger接口文档
json数据:
{
"id": 0,
"idNumber": "111222333444555666",
"name": "xiaozhi",
"phone": "13812344321",
"sex": "1",
"username": "小智"
}
响应码:401 报错
- debug日志:
- 通过断点调试
- 请求头中不包含令牌
- 调用原先准备好的登录接口 获取到令牌
- 在文档管理中找到全局参数设置
- 新增token参数
代码完善
程序存在的问题:
- 录入员工名存在的异常未处理
- 通过全局异常处理器来处理
- 进入到sky-server模块,com.sky.hander包下,GlobalExceptionHandler.java添加方法
/**
* 处理SQL异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
//Duplicate entry 'zhangsan' for key 'employee.idx_username'
String message = ex.getMessage();
if(message.contains("Duplicate entry")){
String[] split = message.split(" ");
String username = split[2];
String msg = username + MessageConstant.ALREADY_EXISTS;
return Result.error(msg);
}else{
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
进入到sky-common模块,在MessageConstant.java添加
public static final String ALREADY_EXISTS = "已存在";
- 新增员工时 创建人 修改人id为固定值
- 需要获取到当前登录员工的id
- 在员工登录成功后会生成JWT令牌给前端
- 使用ThreadLocal实现
- ThreadLocal是TThread的局部变量
- 为每个线程单独提供一份存储空间 具有线程隔离的作用
- 只有在线程内才能获取到对应的值 线程外不能访问
- 原理补充
- 在拦截器 server controller中内添加代码`System.out.println(“当前线程的Id:”+ Thread.currentThread().getId());
- 查看到每次请求在不同位置都是同一个线程
- 常用方法
- void set(T value) 设置当前线程局部变量的值
- T get() 返回当前线程所对应的线程局部变量的值
- Void remove() 移除当前线程局部变量的值
- 使用步骤
- 在拦截器添加代码`BaseContext.setCurrentId(empId);
- 取出当前线程id
- 在server中添加代码`BaseContext.getCurrentId();
员工分页查询
需求分析设计
- 每页10条数据分页展示
- 可以通过员工姓名查询
- 根据号码展示员工信息
- 请求参数类型为Query 不是json
- 直接在路径后拼接
- /admin/employee/page?name=zhangsan
- 返回数据中的records数据中使用Employee实体类封转
代码开发
- 设计DTO类
- 根据请求参数进行封装 在pojo模块中
package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class EmployeePageQueryDTO implements Serializable {
//员工姓名
private String name;
//页码
private int page;
//每页显示记录数
private int pageSize;
}
- 封装PageResult对象作为分页查询统一返回对象
public class PageResult implements Serializable {
private long total; //总记录数
private List records; //当前页数据集合
}
- 在controller层总添加分页查询方法
- 在server层声明并在实现类中实现
- 基于pageHelper分页插件
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//使用pageHelper进行分页查询
//开始分页查询
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
long total = page.getTotal();
List<Employee> records = page.getResult();
//封装分页结果
PageResult pageResult = new PageResult(total , records);
return pageResult;
}
- 考虑到需要实现动态sql 用xml配置实现
<select id = "pageQuery" resultType="com.sky.entity.Employee">
SELECT * FROM employee
<where>
<if test="name != null and name != ''">
name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
order by create_time desc
</select>
代码完善
- 经过前后端联调后发现问题
- 最好操作时间格式不清晰
- 处理方式
- 在属性上加入注解 对日期进行格式化
- 在WebMvcConfiguration中拓展Spring MVC 的消息转换器 统一对日期类型进行格式化处理
- 在配置类中添加
/**
* 扩展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);
}
启用禁用员工账号
需求分析设计
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。如果某个员工账号状态为正常,则按钮显示为 “禁用”,如果员工账号状态为已禁用,则按钮显示为"启用"。
- 请求方式 POST
- 请求路径 /admin/employee/status/{status}
- 路径参数携带状态值
- 同时传递id 明确操作的用户
- 必须返回code状态
代码开发
- Controller层
//启用禁用员工账号
@PostMapping("/status/{status}")
@ApiOperation("启用禁用员工账号")
public Result startOrstop(Long id, @PathVariable Integer status) {
//通过pathVariable获取路径参数
log.info("修改员工状态: id={}, status={}", id, status);
employeeService.startOrStop(id, status);
return Result.success();
}
- 在server层声明并在实现类中实现
/**
* 启用或禁用员工
*
* @param id 员工id
* @param status 状态
*/
@Override
public void startOrStop(Long id, Integer status) {
Employee employee = Employee.builder()
.status(status)
.id(id)
.build();
employeeMapper.update(employee);
}
- 基于xml编写sql语句
<update id="update" parameterType="com.sky.entity.Employee">
UPDATE employee
<set>
<if test="name != null and name != ''">name = #{name}, </if>
<if test="username != null and username != ''">username = #{username},</if>
<if test="password != null and password != ''">password = #{password},</if>
<if test="phone != null and phone != ''">phone = #{phone},</if>
<if test="sex != null and sex != ''">sex = #{sex},</if>
<if test="idNumber != null and idNumber != ''">id_number = #{idNumber},</if>
<if test="status != null">status = #{status},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUser != null and updateUser != ''">update_user = #{updateUser},</if>
</set>
</update>
编辑员工
需求分析与设计
在员工管理列表页面点击 “编辑” 按钮,跳转到编辑页面,在编辑页面回显员工信息并进行修改,最后点击 “保存” 按钮完成编辑操作。
- 涉及两个接口
- 根据id查询员工信息
- 编辑员工信息
代码开发
- 回显员工信息功能
- controller层
- 在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);
}
- 实现…
/**
* 根据id查询员工
*
* @param id
* @return
*/
public Employee getById(Long id) {
Employee employee = employeeMapper.getById(id);
employee.setPassword("****");
return employee;
}
- mapper层中声明
- 较简单 就不用xml了
/**
* 根据id查询员工信息
* @param id
* @return
*/
@Select("select * from employee where id = #{id}")
Employee getById(Long id);
- 修改员工信息功能
- 在 EmployeeController 中创建 update 方法:
/** * 编辑员工信息 * @param employeeDTO * @return */ @PutMapping @ApiOperation("编辑员工信息") public Result update(@RequestBody EmployeeDTO employeeDTO){ log.info("编辑员工信息:{}", employeeDTO); employeeService.update(employeeDTO); return Result.success(); }
- 实现
```java
/**
* 编辑员工信息
*
* @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);
}
- 在实现启用禁用员工账号功能时,已实现employeeMapper.update(employee),在此不需写Mapper层代码。
- debug日志
- 完成上述操作后在前后端联调时出现问题
- 我的update方法没有做用户名唯一性校验,导致当用户名和其他员工重复时,数据库唯一索引冲突或业务层报“用户名已存在”。
- 在xml文件中添加`where id = #{id}解决
导入分类模块功能代码
- 直接导入现成的代码即可
- 分类管理模块功能
新增菜品分类:当我们在后台系统中添加菜品时需要选择一个菜品分类,在移动端也会按照菜品分类来展示对应的菜品。
菜品分类分页查询:*系统中的分类很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。
根据id删除菜品分类:在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时,此分类不允许删除。
修改菜品分类:在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作。
启用禁用菜品分类:在分类管理列表页面,可以对某个分类进行启用或者禁用操作。
分类类型查询:当点击分类类型下拉框时,从数据库中查询所有的菜品分类数据进行展示。
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 |