员工管理
新增员工
Controller:
@PostMapping//post类型的请求@ApiOperation("添加员工")public Result save(@RequestBody EmployeeDTO employeeDTO) {log.info("新增员工{}", employeeDTO);employeeService.save(employeeDTO);return Result.success();}
代码完善–存在问题
问题1.控制台抛出异常,程序处理。使用全局异常处理器
处理sql异常 Duplicate entry 'zhangsan’ for key 'employee.idx username
全局异常处理器:GlobalExceptionHandler.java
@ExceptionHandlerpublic Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {//处理sql异常 Duplicate entry 'zhangsan’ for key 'employee.idx usernameString 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);//未知错误提示信息}}
问题2.
动态获取登录用户ID
先看一下基于JWT令牌认证流程
ThreadLocal
并不是一个thread,而是Thread的局部变量。
每个县城独享一份存储空间,具有线程隔离的效果,只有在县城内才能获取到对应的值,线程外不行。
客户端发起的每一次请求,Tomcat服务器会分配一个进程,然后单独的执行代码。每次请求都是一个单独的线程。
也就是在这个线程内能共享一份存储空间。
所以我们可以讲给当前用户ID存到这个空间内。
JwtTokenAdminInterceptor.java // JWT拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}//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);BaseContext.setCurrentId(empId);//将ID放入线程空间//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}
上图就是调用这个方法类将员工ID存到当前线程空间中。
下图是添加员工信息Service中的方法实现,重点在于从线程空间取出登录用户ID
Service:
public void save(EmployeeDTO employeeDTO) {Employee employee = new Employee();//提交给数据库的最好是entityBeanUtils.copyProperties(employeeDTO, employee);//进行一个属性复制//填充字段employee.setStatus(StatusConstant.ENABLE);employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));employee.setCreateTime(LocalDateTime.now());employee.setUpdateTime(LocalDateTime.now());//通过ThreadLocal获得当前用户的idemployee.setCreateUser(BaseContext.getCurrentId());employee.setUpdateUser(BaseContext.getCurrentId());employeeMapper.insert(employee);}
员工分页查询
Controller:
@GetMapping("/page")//get类型的请求@ApiOperation("员工分页查询")public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {//数据不是json,不需要注解PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);return Result.success(pageResult);}
PageHelper插件来辅助代码
Service:
@Overridepublic PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {//select * from employee linit 0,10//调用PageHelper 开始分页查询 页码,每页记录数PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());Page<Employee> page = employeeMapper.selectByPage(employeePageQueryDTO);//返回的是Page//接下来处理一下,变成pageresult,需要两个参数long total = page.getTotal();List<Employee> result = page.getResult();return new PageResult(total, result);}
mapper:
不用注解,动态sql使用映射文件
在resource文件夹里都是xml文件
<select id="selectByPage" resultType="com.sky.entity.Employee">select * from employee<where><if test="name!=null and name !='' ">and name like concat('%',#{name},'%') <!--模糊查询 like --></if></where>order by create_time desc <!--创建时间降序-->
</select>
方式一:在实体类里加
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime updateTime;
方式二:扩展消息转换器,在配置类中
重写父类方法
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {//创建一个消息转换器MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据converter.setObjectMapper(new JacksonObjectMapper());//将自己的消息转化器加入容器中converters.add(0, converter);//0为索引,让这个转换器排前面}
启用禁用员工账号
Controller:
@PostMapping("/status/{status}")@ApiOperation("启用禁用员工账号")public Result startOrStop(@PathVariable("status") Integer status, Long id) {employeeService.startOrStop(status, id);return Result.success();}
Service:本质是修改员工Status 即update
public void startOrStop(Integer status, Long id) {Employee employee =//本质就是创建一个实体类对象Employee.builder()// 构建器对象.id(id)// 方法名和属性名一致.status(status).build();employeeMapper.update(employee);}
根据主键动态修改,所以又到xml里
<!--因为在yml文件中声明了别名包的扫描,所以此处可以不使用全类名--><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>
编辑员工
查询员工信息:
@GetMapping("/{id}")@ApiOperation("根据Id查询员工")public Result<Employee> getById(@PathVariable Long id) {//加个路径参数的注解@PathVariable来得到idEmployee employee = employeeService.getById(id);return Result.success(employee);}
@Overridepublic Employee getById(Long id) {Employee employee = employeeMapper.getById(id);employee.setPassword("****");// 查出的密码,给前端传****return employee;}
@Select("select * from employee where id=#{id}")Employee getById(Long id);
编辑员工信息:
@PutMapping@ApiOperation("编辑员工信息")public Result update(@RequestBody EmployeeDTO employeeDTO) {employeeService.update(employeeDTO);return Result.success();}
public void update(EmployeeDTO employeeDTO) {Employee employee = new Employee();//对象属性拷贝BeanUtils.copyProperties(employeeDTO,employee);employee.setUpdateTime(LocalDateTime.now());employee.setUpdateUser(BaseContext.getCurrentId());employeeMapper.update(employee);}
导入分类模块功能代码
直接粘贴到对应文件夹(在IDEA里操作)
导入完手动编译一下