目录项目来源:B站黑马程序员《苍穹外卖》
本帖仅为学习日志
一、新增员工
(一)需求分析和设计
1.产品原型:
2.接口设计:
本项目约定:
- 管理端发出的的请求,统一使用/admin作为前缀
- 用户端发出的请求,统一使用/user作为前缀
3.数据库设计(employee表):
(二)代码开发
根据新增员工接口设计对应的DTO:
注意:当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据
- Controller层
/** * 新增员工 * * @return */ @PostMapping @ApiOperation("新增员工") public Result save(@RequestBody EmployeeDTO employeeDTO){ log.info("新增员工:{}",employeeDTO); //调用Service层新增员工 employeeService.save(employeeDTO); //alt+enter 扩展方法 return Result.success(); }
- Service层
/** * 新增员工 * @param employeeDTO * @return */ void save(EmployeeDTO employeeDTO);
/** * 新增员工 * @param employeeDTO * @return */ public void save(EmployeeDTO employeeDTO){ Employee employee = new Employee(); //employee.setName(employeeDTO.getName()); //使用对象属性拷贝,一次性将employeeDTO中的数据拷贝到employee中 BeanUtils.copyProperties(employeeDTO,employee); //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 // TODO 需要改为当前登录用户id employee.setCreateUser(10L); employee.setUpdateUser(10L); employeeMapper.insert(employee); }
- Mapper层
/** * 插入员工数据 * @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);
(三)功能测试
功能测试方法:
- 通过接口文档测试
- 通过前后端联调测试
注意:由于开发阶段前端和后端是并行开发的,后端完成某个功能后,此时前端对应的功能可能还没有开发完成,导致无法进行前后端联调测试。所以在开发阶段,后端测试主要以接口文档测试为主。
进行登录测试获取token,并将其存储为全局参数
接口文档测试成功
前后端联调测试成功
(四)代码完善
当前问题:
- 录入的用户名已存在,抛出异常后没有处理
/**
* 处理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);
}
}
- 新增员工时,创建人id和修改人id设置了固定值
ThreadLocal并不是一个Thread,而是一个Thread的局部变量。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外无法访问。
ThreadLocal常用方法:
- public void set(T value) 设置当前线程的线程局部变量的值
- public T get() 返回当前线程所对应的线程局部变量的值
- public void remove() 移除当前线程的线程局部变量
//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; }
//设置当前记录创建人id和修改人id employee.setCreateUser(BaseContext.getCurrentId()); employee.setUpdateUser(BaseContext.getCurrentId());
二、员工分页查询
(一)需求分析和设计
产品原型:
业务规则:
- 根据页码展示员工信息
- 每页展示10条数据
- 分页查询时可以根据需要,输入员工姓名进行查询
接口设计:
根据分页查询接口设计对应的DTO:
分页查询统一封装成PageResult对象:
员工信息分页查询后端返回的对象类型为:Result< PageResult>
(二)代码开发
pageHelper: 实现分页查询的mybatis插件
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency>
- Controller
/** * 员工分页查询 * @param employeePageQueryDTO * @return */ @GetMapping("/page") @ApiOperation("员工分页查询") public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){ log.info("员工分页查询,参数为:{}",employeePageQueryDTO); PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO); return Result.success(pageResult); }
*Server
/** * 分页查询 * @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); }
*Mapper
/** * 分页查询 * @param employeePageQueryDTO * @return */ PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
<!-- 动态xml --> <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>
(三)功能测试
注:token有效时间为2小时。
# 设置jwt过期时间
admin-ttl: 7200000
接口文档测试成功
(四)代码完善
时间显示有问题
解决方式:
- 方式一:在属性上加入注释,对日记进行格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime;
只能处理单一属性
- 方式二:在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); }
三、启用禁用员工账号
(一)需求分析和设计
产品原型:
业务规则:
- 可以对状态为“启用”的员工账号进行“禁用”操作,反之亦可
- 状态为“禁用”的员工账号不能登陆系统
接口设计:
(二)代码开发
该接口不涉及查询,不需要泛型
- Controller层
/** * 启用/禁用员工账号 * @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(); }
*Server层
/** * 启用/禁用员工账号 * @param status * @param id */ void startOrStop(Integer status, Long id);
/** * 启用/禁用员工账号 * @param status * @param id */ public void startOrStop(Integer status,Long id){ // update employee set status = ? where id = ?
/* Employee employee = new Employee();
employee.setStatus(status);
employee.setId(id);*/
//使用builder实现更简洁的构造 Employee employee = Employee.builder() .status(status) .id(id) .build();
employeeMapper.update(employee); }
- Mapper层
/** * 根据主键动态修改属性 * @param employee */ // 使用动态sql void update(Employee employee);
<update id="update" parameterType="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">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">update_user = #{updateUser},</if> </set> <where> id = #{id} </where> </update>
(三)功能测试
接口测试:
前后端联调测试:
四、编辑员工
(一)需求分析和设计
产品原型:
编辑员工功能涉及两个接口
- 根据id查询员工信息
- 编辑员工信息
(二)代码开发
根据id查询员工信息接口
- Controller层
/** * 根据id查询员工信息 * @param id * @return */ @GetMapping("/{id}") @ApiOperation("根据id查询员工信息") public Result<Employee> getById(@PathVariable Long id){ Employee employee = employeeService.getById(id); return Result.success(employee); }
*Server层
/** * 根据id查询员工 * @param id * @return */ Employee getById(Long id);
/** * 根据id查询员工 * @param id * @return */ public Employee getById(Long id) { Employee employee = employeeMapper.getById(id); //将密码设置不可见 employee.setPassword("******"); return employee; }
- Mapper层
/** * 根据id查询员工信息 * @param id * @return */ @Select("select * from employee where id = #{id}") Employee getById(Long id);
编辑员工信息接口:
- Controller层
/** * 编辑员工信息 * @param employeeDTO * @return */ @PutMapping @ApiOperation("编辑员工信息") public Result update(@RequestBody EmployeeDTO employeeDTO){ log.info("编辑员工信息:{}",employeeDTO); employeeService.update(employeeDTO); return Result.success(); }
*Server层
/** * 编辑员工信息 * @param employeeDTO */ void update(EmployeeDTO employeeDTO);
/** * 编辑员工信息 * @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); }
(三)功能测试
根据id查询员工信息:
接口文档测试:
前后端联调测试:
编辑员工信息:
接口文档测试:
前后端联调测试:
五、导入分类模块功能代码
(一)需求分析
产品原型:
业务规则:
- 分类名称必须是唯一的
- 分类按照类型可以分为菜品分类和套餐分类
- 新添加的分类状态默认为“禁用”
接口设计:
- 新增分类
- 分类分页查询
- 根据id删除分类
- 修改分类
- 启用/禁用分类
- 根据类型查询分类
数据库设计(category表):