处理全局异常

紧接着上一篇博客,现在我们开始测试我们的菜单展示功能。本篇博客主要实现职位、职称管理。

目录

  • 1.测试
  • 2.职位管理
    • 2.1.实现功能
    • 2.2.测试
    • 2.3.定义全局异常
  • 3.职称管理
    • 3.1.实现功能
    • 3.2.测试

1.测试

1.1.菜单展示测试

重启项目,打开8081端口。跟着步骤点击发送。根据代码逻辑第一次查询会从数据库中查询,之后每一次查询菜单就在Redis(服务器开启状态下)中查询。
处理全局异常_第1张图片

首先先看使用了Redis的效果:

这是文档给个数据:

处理全局异常_第2张图片

而且Redis里也有我们设置的menu_1

处理全局异常_第3张图片

后端控制台也是显示了运行了SQL语句,说明这是从数据库中获取的。

在这里插入图片描述

那我们再一次点击发送,看看结果:

处理全局异常_第4张图片
在这里插入图片描述

第二查询并非时查询菜单的SQL语句,而是登录时所用的SQL查询,说明这次是从Redis中获取菜单数据。

前面也提到过,这个项目的菜单不是很多,而且我的Redis安装在虚拟机中,每次需要手动开启虚拟机,考虑到如果一次性打开过多软件我的电脑可能会奔溃,所以后面我将不会启动Redis,还是直接从数据库中查询,因为Redis服务没开启会报RedisConnectionFailureException异常,索性我直接捕获,跳过Redis。但是如果使用我下面的代码,运行速度就会受到影响,暂时还没有更好改进方法,所以根据自己需求选择代码。当然有更好的处理方式也可自行解决。

IMenuServiceImpl.java

@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {

    @Resource
    private MenuMapper menuMapper;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 通过用户ID查询菜单列表
     *
     * @return
     */
    @Override
    public List<Menu> getMenusByAdminId() {
           Integer adminId = AdminUtils.getCurrentAdmin().getId();
        // 如果Redis未开启服务,会报RedisConnectionFailureException,那么我们就直接从数据库中获取
        try {
            ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
            // 从redis获取菜单数据
            List<Menu> menus = (List<Menu>) opsForValue.get("menu_" + adminId);
            // 如果为空,就去数据库中获取
            if (CollectionUtils.isEmpty(menus)) {
                menus = menuMapper.getMenusByAdminId(adminId);
                // 将数据设置到redis中
                opsForValue.set("menu_" + adminId, menus);
            }
            return menus;
        } catch (RedisConnectionFailureException e) {
            List<Menu> menus = menuMapper.getMenusByAdminId(adminId);
            return menus;
        }
    }
}

1.2.权限测试

编写测试类,通过SQL语句查询到我们的admin用户只能查询到基本资料,高级资料无法查到,

处理全局异常_第5张图片

那我们使用admin用户测试能否访问到这两个路径

在这里插入图片描述

@GetMapping("/employee/basic/hello")
public String test2(){
    return "/employee/basic/hello";
}

@GetMapping("/employee/advanced/hello")
public String test3(){
    return "/employee/advanced/hello";
}

test2结果:

处理全局异常_第6张图片

test3结果:

处理全局异常_第7张图片

权限管理和菜单展示功能完成。

2.职位管理

职位管理功能实现起来就比较简单,就是一般的CRUD。

注意点:

  • 进行展示创建时间的时候只需展示年月日
  • 前缀必须和数据库中对应/system/basic/pos

2.1.实现功能

2.1.1.自定义日期格式
在component目录下新建日期转换类

DateConverter.java

@Component
public class DateConverter implements Converter<String, LocalDate> {

    @Override
    public LocalDate convert(String s) {
        try {
            return LocalDate.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd").withLocale(Locale.CHINA));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

修改职位实体类

Position.java

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_position")
@ApiModel(value="Position对象", description="")
public class Position implements Serializable {
	
	......

    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
    private LocalDateTime createDate;

    @ApiModelProperty(value = "是否启用")
    private Boolean enabled;


}

2.1.2.编写控制层

PositionController.java

@RestController
@RequestMapping("/system/basic/pos")
public class PositionController {

    @Resource
    private IPositionService positionService;

    @ApiOperation(value = "获取所有职位信息")
    @GetMapping("/")
    public List<Position> getAllPositions() {
        return positionService.list();
    }

    @ApiOperation(value = "添加职位信息")
    @PostMapping("/")
    public RespBean addPosition(@RequestBody Position position) {
        position.setCreateDate(LocalDateTime.now());
        if (positionService.save(position)) {
            return RespBean.success("添加成功");
        }
        return RespBean.error("添加失败");
    }

    @ApiOperation(value = "更新职位信息")
    @PutMapping("/")
    public RespBean updatePosition(@RequestBody Position position) {
        if (positionService.updateById(position)) {
            return RespBean.success("更新成功!");
        }
        return RespBean.error("更新失败。");
    }

    @ApiOperation(value = "删除职位信息")
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public RespBean deletePosition(@PathVariable Integer id) {
        if (positionService.removeById(id)) {
            return RespBean.success("删除成功");
        }
        return RespBean.error("删除失败");
    }

    @ApiOperation(value = "批量删除职位信息")
    @DeleteMapping("/")
    public RespBean deletePositionByIds(Integer[] ids) {
        if (positionService.removeByIds(Arrays.asList(ids))) {
            return RespBean.success("删除成功");
        }
        return RespBean.error("删除失败");
    }
}

2.2.测试

添加职位功能

处理全局异常_第8张图片

点击发送

处理全局异常_第9张图片

测试前的t_position:

处理全局异常_第10张图片

测试后的t_position:

处理全局异常_第11张图片

更新职位功能

处理全局异常_第12张图片

处理全局异常_第13张图片

删除职位功能

处理全局异常_第14张图片
数据库:

处理全局异常_第15张图片

2.3.定义全局异常

当我们删除技术总监的时候:

处理全局异常_第16张图片

我们看看后台情况:

在这里插入图片描述

翻译过来就是无法删除或更新父行:外键约束失败

对于程序员来讲这样的响应内容用来定位bug的确没什么问题,但是对于用户来说是不可取,我们应该给用户一个友好的提示信息。当然我们的程序可能不止这一个异常或者用户的误操作,为了把这些异常或者错误收集到一起处理,我们需要定义一个全局异常处理的方法。

处理全局异常方法:

  • @ControllerAdvice和@ExceptionHandler注解
  • ErrorController类

前者只能处理控制器抛出的异常,后者能处理所以异常,包括还没进入控制器的错误(404,401…),但是我们在项目中一般两者都会使用到,通过我们的@ControllerAdvice方式去处理我们控制器抛出的异常,通过ErrorController类处理未进入控制器的异常,所以他们并不是只能二选一,是能够二选一的。@ControllerAdvice可以定义多个拦截方法,拦截不同的异常类,并且可以获取抛出的异常信息。

新建Exception目录,创建全局异常处理类

GlobalException.java

@RestControllerAdvice
public class GlobalException {
    @ExceptionHandler(SQLException.class)
    public RespBean mySqlException(SQLException e) {
        if (e instanceof SQLIntegrityConstraintViolationException) {
            return RespBean.error("该数据有关联数据,操作失败");
        }
        return RespBean.error("数据库异常,操作失败");
    }
}

3.职称管理

与职位管理功能差不多,步骤基本差不多所以直接上代码。

3.1.实现功能

3.1.1.自定义日期格式

修改职称实体类

Joblevel.java

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_joblevel")
@ApiModel(value="Joblevel对象", description="")
public class Joblevel implements Serializable {

	......
	
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
    private LocalDateTime createDate;

    @ApiModelProperty(value = "是否启用")
    private Boolean enabled;

}

3.1.2.编写控制层

JoblevelController.java

@RestController
@RequestMapping("/system/basic/joblevel")
public class JoblevelController {

    @Resource
    private IJoblevelService joblevelService;

    @ApiOperation(value = "获取所有职称")
    @GetMapping("/")
    public List<Joblevel> getAllJobLevels() {
        return joblevelService.list();
    }

    @ApiOperation(value = "添加职称")
    @PostMapping("/")
    public RespBean addJobLevel(@RequestBody Joblevel joblevel) {
        joblevel.setCreateDate(LocalDateTime.now());
        if (joblevelService.save(joblevel)) {
            return RespBean.success("添加成功");
        }
        return RespBean.error("添加失败");
    }

    @ApiOperation(value = "更新职称")
    @PutMapping("/")
    public RespBean updateJobLevel(@RequestBody Joblevel joblevel) {
        if (joblevelService.updateById(joblevel)) {
            return RespBean.success("更新成功!");
        }
        return RespBean.error("更新失败。");
    }

    @ApiOperation(value = "删除职称")
    @DeleteMapping("/{id}")
    public RespBean deleteJobLevel(@PathVariable Integer id) {
        if (joblevelService.removeById(id)) {
            return RespBean.success("删除成功");
        }
        return RespBean.error("删除失败");
    }

    @ApiOperation(value = "批量删除职称")
    @DeleteMapping("/")
    public RespBean deleteJobLevelByIds(Integer[] ids) {
        if (joblevelService.removeByIds(Arrays.asList(ids))) {
            return RespBean.success("删除成功");
        }
        return RespBean.error("删除失败");
    }
}

3.2.测试

这里我就只演示获取所有职称测试,其他自行测试,与职位管理测试不同的是,需要输入titleLevel参考值:‘正高级’,‘副高级’,‘中级’,‘初级’,‘高级’,数据库已固定。

处理全局异常_第17张图片

处理全局异常_第18张图片
到这里职位、职称管理模块就完成了,下一篇博客实现权限组:


Spring Security 权限控制

你可能感兴趣的:(Java,项目练习,java,spring,boot,后端)