[苍穹外卖]-02员工管理接口开发

效果预览

新增员工

1.需求分析

查看产品原型

接口设计

说明

  1. 管理端发出的请求, 统一使用/admin作为前缀
  2. 用户端发出的请求, 统一使用/user作为前缀

数据表设计

employee(员工)表

2.代码编写

设计DTO

当前端提交的数据和实体类中对应的属性差别较大时, 一般使用专门的DTO来封装数据

controller

  /**
     * 新增员工
     *
     * @param employeeDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增员工")
    public Result save(@RequestBody EmployeeDTO employeeDTO) {
        log.info("新增员工: {}", employeeDTO);
        employeeService.save(employeeDTO);
        return Result.success();
    }

service

/**
 * 保存员工
 * @param employeeDTO
*/
public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        // 对象属性拷贝: 把前端属性拷贝到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());
        // 设置创建人和修改人
        // 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,status,create_time,update_time,create_user,update_user)" +
            "values" +
            "(#{name},#{username},#{password},#{phone},#{sex},#{id_number},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")
void insert(Employee employee);

说明

数据表的字段名是_分隔, 实体类中的字段名是驼峰命名, 自动映射的前提要开启驼峰命名

3.功能测试

接口测试

全局设置请求头

测试接口

前后端联调

4.代码优化

员工账户重名异常处理

问题复现

修复

在全局异常处理器中处理sql异常

/**
* 处理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

问题复现

问题分析

前端请求时, 会携带JWT令牌, 通过解析令牌可以拿到当前登录员工的id, 所以在拦截器中可以拿到当前用户id, 但是我们需要在server中使用这个id

ThreadLocal

ThreadLocal为每个线程提供一份储存空间, 具有线程隔离的效果, 只有在线程内才能获取到对应的值, 线程外不能访问

常用方法

  1. 设置当前线程的线程局部变量的值: public void set(T value)
  2. 返回当前线程所对应的线程局部变量的值: public T get()
  3. 移除当前线程的线程局部变量: public void remove()
  4. 为了方便使用, 在项目中会把ThreadLocal封装成工具类

tomcat服务器会把每一次的请求响应封装到一份线程中, 所以可以把解析出来的id保存到当前线程中, 然后在server中取值使用

代码修复

在拦截器中储存身份id

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

    @Autowired
    private JwtProperties jwtProperties;


    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);
            // 保存当前用户id
            BaseContext.setCurrentId(empId);
           
        } catch (Exception ex) {
            
        }
    }
}

在service中取用身份id

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 保存员工
     *
     * @param employeeDTO
     */
    public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        ... ...
        
        // 设置创建人和修改人
        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());

    }

}

员工分页查询

1.需求分析

查看产品原型

分析业务规则

  1. 根据页码展示员工信息
  2. 每页展示10条数据
  3. 分页查询时可以根据需要, 输入员工姓名进行查询

接口设计

2.代码编写

controller

1.设计DTO

根据接口参数设计DTO, 使用设计好的DTO对象封装前端参数

2.PageResult对象

所有的分页查询结果, 统一封装成PageResult对象

/**
 * 封装分页查询结果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

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

}
3.分页查询返回类型

分页查询返回的对象类型为: Result<PageResult>

4.封装接口
 /**
     * 员工分页查询
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("员工分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {
        log.info("员工分页查询,参数为: {}", employeePageQueryDTO);
        PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
        return Result.success(pageResult);
    }

service

使用MyBatis提供的 pagehelper 分页插件 快捷完成分页查询

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
/**
     * 分页查询员工
     *
     * @param employeePageQueryDTO
     * @return
     */
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        // 使用pagehelper进行分页查询
        // startPage(当前页码值, 每页展示条数)
        // 1, 设置查询参数
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
        // 2, 执行查询语句
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
        // 3, 获取查询结果
        long total = page.getTotal();
        List<Employee> records = page.getResult();

        return new PageResult(total, records);
    }

mapper

执行普通的查询语句即可, 插件会自动填充分页查询条件

这里还要手动拼接name查询条件, 使用到动态sql

Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
<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>

3.测试

接口测试

前后端联调

4.优化

问题

测试出时间格式展示问题, 要进行时间格式处理

解决方式1: 手动指定

在属性上加入注解, 对日期进行格式化

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

解决方式2: 统一处理

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

说明: 项目中已经提供了对象转换器 json/JackonObjectMapper

/**
   * 扩展消息转换器
   *
   * @param converters
*/
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    log.info("扩展消息转换器...");
    // 创建一个消息转换器对象
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    // 为消息转换器设置一个对象转换器, 对象转换器可以把java对象序列化为json对象
    converter.setObjectMapper(new JacksonObjectMapper());
    // 将自己的消息转换器加入到容器中(0代表索引->添加到集合前面优先执行)
    converters.add(0, converter);
}

启用禁用员工账号

1.需求分析

查看产品原型

分析业务规则

  1. 当前"启用"的账号可以设置为"禁用"
  2. 当前"禁用"的账号可以设置为"启用"
  3. "禁用"状态的员工账号不能登录系统

接口设计

2.代码实现

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

service

使用构建器创建对象

   /**
     * 启用禁用员工账号
     * @param status
     * @param id
     */
    public void startOrStop(Integer status, Long id) {
        // 使用构建器创建对象, 是一种代码风格
        // 作用与 new Employee一直
        // 使用前提是该类使用了 @Builder 注解
        Employee employee = Employee.builder()
                .status(status)
                .id(id)
                .build();

        employeeMapper.update(employee);
    }

mapper

把所有的修改操作封装到一个动态sql中, 方便代码复用

 <update id="update">
        update employee
        <set>
            <if test="name!=null and name!=''">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="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>

3.测试

接口测试

联调测试

编辑员工

1.需求分析

查看原型

  1. 页面回显: 根据id查询员工信息
  2. 保存修改: 编辑员工信息

设计接口

1.查询员工

2.编辑员工

2.代码实现

查询接口

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

service

把查询到的员工信息的秘密重置一下, 进一步增加安全性

  /**
     * 根据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

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

service

/**
     * 编辑员工信息
     *
     * @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);
    }

mapper

复用之前的update方法, 完成数据修改

 /**
     * 编辑员工信息
     *
     * @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);
    }

3.测试

接口测试

联调测试

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值