角色管理_spring

1. 测试controller层  10

1.1 添加Controller   10

service-oa模块

SysRoleController

package com.atguigu.auth.controller;

import com.atguigu.auth.service.SysRoleService;
import com.atguigu.model.system.SysRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

//角色管理 控制层  10
@RestController //交给spring管理和返回json数据
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
    //注入
    @Autowired
    private SysRoleService sysRoleService;

    
}

2. 查询所有角色   10

service-oa模块SysRoleController

//1 查询所有角色 和 当前用户所属角色   10
    //测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
    @GetMapping("findAll")
    public List<SysRole> findAll(){
        //调用serivice的方法
        List<SysRole> list = sysRoleService.list();
        return list;
    }

测试,启动启动类成功

角色管理_List_02

浏览器输入http://localhost:8800/admin/system/sysRole/findAll

角色管理_List_03

当然也可以使用postman测试

角色管理_spring_04

3. 定义统一返回结果对象   11

项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

例如,我们的系统要求返回的基本数据格式如下:

列表:

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "id": 2,
      "roleName": "系统管理员"
    }
  ],
  "ok": true
}

分页:

{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [
      {
        "id": 2,
        "roleName": "系统管理员"
      },
      {
        "id": 3,
        "name": "普通管理员"
      }
    ],
    "total": 10,
    "size": 3,
    "current": 1,
    "orders": [],
    "hitCount": false,
    "searchCount": true,
    "pages": 2
  },
  "ok": true
}

没有返回数据:

{
  "code": 200,
  "message": "成功",
  "data": null,
  "ok": true
}

失败:

{
  "code": 201,
  "message": "失败",
  "data": null,
  "ok": false
}

3.1 定义统一返回结果对象   11

操作模块:common-util

后续其他模块也会用到,故抽取到common-util模块

枚举ResultCodeEnum

package com.atguigu.common.result;

import lombok.Getter;

//这是一个枚举配置定义统一返回结果对象使用的   11
@Getter
public enum ResultCodeEnum {
    SUCCESS(200,"成功"),
    FAIL(201, "失败"),
    ;

    private Integer code;

    private String message;

    private ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

统一结果返回类Result

package com.atguigu.common.result;

import lombok.Data;

//定义统一结果返回类   11
@Data
public class Result<T> {
    private Integer code;//状态码
    private String message;//返回信息
    private T data;//数据

    //私有化 ,私有化构造代表这个类不能new了,别人要想操作这个类的话只能使用此类中的static方法
    private Result() {}

    //封装返回是数据   11
    public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
        Result<T> result = new Result<>();
        //封装数据
        if(body != null) {
            result.setData(body);
        }
        //状态码
        result.setCode(resultCodeEnum.getCode());
        //返回信息
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }

    //成功 无数据的   11
    public static<T> Result<T> ok() {
        return build(null,ResultCodeEnum.SUCCESS);
    }
    //成功 有数据的  11
    public static<T> Result<T> ok(T data) {
        return build(data,ResultCodeEnum.SUCCESS);
    }

    //失败 无数据的   11
    public static<T> Result<T> fail() {
        return build(null,ResultCodeEnum.FAIL);
    }
    //失败 有数据的   11
    public static<T> Result<T> fail(T data) {
        return build(data,ResultCodeEnum.FAIL);
    }


    public Result<T> message(String msg){
        this.setMessage(msg);
        return this;
    }

    public Result<T> code(Integer code){
        this.setCode(code);
        return this;
    }
}

3.2 改造查询所有角色  11

service-oa模块

//改造 查询所有角色 和 当前用户所属角色   11
    //测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
    @GetMapping("findAll")
    public Result findAll(){
        //调用serivice的方法
        List<SysRole> list = sysRoleService.list();
        return Result.ok(list);
    }

浏览器测试

角色管理_knife4j_05

postman测试

角色管理_spring_06

4. knife4j   12

文档地址:https://doc.xiaominfo.com/

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。

4.1 Swagger介绍

前后端分离开发模式中,api文档是最好的沟通方式。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

1、及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)

2、规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)

3、一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)

4、可测性 (直接在接口文档上进行测试,以方便理解业务)

4.2 集成knife4j

knife4j属于service模块公共资源,因此我们集成到service-uitl模块

4.2.1  添加依赖

操作模块:service-uitl

pom.xml

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

说明:guigu-auth-parent已加入版本管理

4.2.2 添加knife4j配置类   12

操作模块:service-uitl

Knife4jConfig
package com.atguigu.common.config.knife4j;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import java.util.ArrayList;
import java.util.List;

//Knife4j配置类,knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
//便于我们测试,前后端统一接口等作用    12
public class Knife4jConfig {
    @Bean
    public Docket adminApiConfig(){
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("token")
                .description("用户token")
                .defaultValue("")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());
        //添加head参数end

        Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.atguigu"))
                .paths(PathSelectors.regex("/admin/.*"))
                .build()
                .globalOperationParameters(pars);
        return adminApi;
    }

    private ApiInfo adminApiInfo(){

        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统微服务接口定义")
                .version("1.0")
                .contact(new Contact("atguigu", "http://atguigu.com", "atguigu@qq.com"))
                .build();
    }
}

4.2.3 Controller层添加注解   12

service-oa 模块

SysRoleController

角色管理_spring_07

角色管理_分页_08

测试启动服务

浏览器输入http://localhost:8800/doc.html  成功

角色管理_List_09

角色管理_List_10

5. 条件分页查询角色   13

第一步配置分页插件

第二步编写controller的分页方法

(1)需要参数:分页相关参数(当前页和每页显示记录数)

(2)需要参数:条件参数

第三步调用service的方法实现条件分页查询

5.1 启动类修改   13

service-oa模块启动类ServiceAuthApplication

package com.atguigu.auth;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

//启动类  6
@SpringBootApplication
@ComponentScan("com.atguigu") //设置扫描规则,要扫描那些包这里是扫描com.atguigu包及其子包
public class ServiceAuthApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceAuthApplication.class, args);
    }
}

5.1 配置分页插件    13

操作模块:service-uitl,service公共资源说明:我们将@MapperScan("com.atguigu.auth.mapper")提取到该配置类上面,统一管理,启动不需要了。

MybatisPlusConfig

package com.atguigu.common.config.mp;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//这是一个分页插件   13
@Configuration
@MapperScan("com.atguigu.auth.mapper")//表示能找到mapper动态创建的对象
public class MybatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,   13
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false 
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

5.2 条件分页查询实现   13

service-oa模块

SysRoleController

//条件分页查询    13
    //page 当前页  limit 每页显示记录数
    //SysRoleQueryVo 条件对象
    @ApiOperation("条件分页查询")
    @GetMapping("{page}/{limit}")
    public Result pageQueryRole(@PathVariable Long page,
                                @PathVariable Long limit,
                                SysRoleQueryVo sysRoleQueryVo) {
        //调用service的方法实现
        //1 创建Page对象,传递分页相关参数
        //page 当前页  limit 每页显示记录数
        Page<SysRole> pageParam = new Page<>(page,limit);

        //2 封装条件,判断条件是否为空,不为空进行封装
        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
        String roleName = sysRoleQueryVo.getRoleName();
        if(!StringUtils.isEmpty(roleName)) {
            //封装 like模糊查询
            wrapper.like(SysRole::getRoleName,roleName);
        }

        //3 调用方法实现
        IPage<SysRole> pageModel = sysRoleService.page(pageParam, wrapper);
        return Result.ok(pageModel);
    }

测试http://localhost:8800/doc.html

角色管理_分页_11

添加条件

角色管理_spring_12

6. 添加角色     14

service-oa模块

SysRoleController

//添加角色   14
    @ApiOperation("添加角色")
    @PostMapping("save")
    public Result save(@RequestBody SysRole role) {//@RequestBody是通过json格式传递封装进对象中
        //调用service的方法
        boolean is_success = sysRoleService.save(role);
        if(is_success) {
            return Result.ok();
        } else {
            return Result.fail();
        }
    }

角色管理_List_13

角色管理_分页_14

7. 修改角色   14

service-oa模块

SysRoleController

//修改角色-根据id查询   14
    //这个方法说白了就是根据id查询
    @ApiOperation("根据id查询")
    @GetMapping("get/{id}")//使用了restful风格
    public Result get(@PathVariable Long id) {//@PathVariable得到restful传来的值
        SysRole sysRole = sysRoleService.getById(id);
        return Result.ok(sysRole);
    }

角色管理_spring_15

//修改角色-最终修改   14
    @ApiOperation("修改角色")
    @PutMapping ("update")
    public Result update(@RequestBody SysRole role) {
        //调用service的方法
        boolean is_success = sysRoleService.updateById(role);
        if(is_success) {
            return Result.ok();
        } else {
            return Result.fail();
        }
    }

角色管理_List_16

角色管理_分页_17

8. 删除角色(id删除和批量删除)   14

service-oa模块

SysRoleController

//根据id删除  14
    @ApiOperation("根据id删除")
    @DeleteMapping("remove/{id}")
    public Result remove(@PathVariable Long id) {
        boolean is_success = sysRoleService.removeById(id);
        if(is_success) {
            return Result.ok();
        } else {
            return Result.fail();
        }
    }

角色管理_List_18

角色管理_knife4j_19

角色管理_knife4j_20

这里做个解释

json格式有两种 @RequestBody会根据你传的参数来进行不同的封装

角色管理_List_21

//批量删除   14
    // 前端数组 [1,2,3]
    @ApiOperation("批量删除")
    @DeleteMapping("batchRemove")
    public Result batchRemove(@RequestBody List<Long> idList) {
        boolean is_success = sysRoleService.removeByIds(idList);
        if(is_success) {
            return Result.ok();
        } else {
            return Result.fail();
        }
    }

角色管理_分页_22

角色管理_分页_23

9. 配置日期时间格式   15

service-oa模块

application-dev.yml

角色管理_List_24

jackson:   # 配置时间  15
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

再次查询时间格式正确

角色管理_spring_25

10. 统一异常处理   15

10.1 制造异常

service-oa模块

我们给findAll查询所有模拟异常

SysRoleController

//改造 查询所有角色 和 当前用户所属角色   11
    //测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
    @ApiOperation("查询所有的角色") //knife4j生成接口文档的中文说明   12
    @GetMapping("findAll")
    public Result findAll(){
        //调用serivice的方法
        List<SysRole> list = sysRoleService.list();
        
        //模拟异常  15
        int i = 10/0;
        
        return Result.ok(list);
    }

测试

角色管理_分页_26

10.2 统一异常   15

我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理。

角色管理_spring_27

全局异常处理    15

service-util模块

GlobalExceptionHandler

package com.atguigu.common.config.exception;


import com.atguigu.common.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

//统一全局异常类   15
@ControllerAdvice //通过AOP将思想将此功能加到当前的类中去
public class GlobalExceptionHandler {
    //全局异常处理,执行的方法
    @ExceptionHandler(Exception.class)//指定哪个异常出现时会执行此方法
    @ResponseBody//返回json数据    15
    public Result error(Exception e) {
        e.printStackTrace();
        return Result.fail().message("执行全局异常处理...");
    }
}

角色管理_knife4j_28

角色管理_knife4j_29

特定异常处理  16

service-util模块

GlobalExceptionHandler

//特定异常处理   16
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public Result error(ArithmeticException e) {
        e.printStackTrace();
        return Result.fail().message("执行特定异常处理...");
    }

角色管理_knife4j_30

自定义异常处理  16

第一步创建异常类,继承RuntimeException

第二步在异常类添加属性,状态码和描述信息

第三步在出现异常地方,手动抛出异常

第四步在之前创建类,添加执行的方法

service-util模块

GuiguException

package com.atguigu.common.config.exception;

import com.atguigu.common.result.ResultCodeEnum;
import lombok.Data;

//自定义异常类    16
@Data
public class GuiguException extends RuntimeException {

    private Integer code;//状态码
    private String msg;//描述信息

    public GuiguException(Integer code,String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }

    /**
     * 接收枚举类型对象
     * @param resultCodeEnum
     */
    public GuiguException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
        this.msg = resultCodeEnum.getMessage();
    }

    @Override
    public String toString() {
        return "GuliException{" +
                "code=" + code +
                ", message=" + this.getMessage() +
                '}';
    }
}

手动抛出异常(我们在查询所有中模拟)

service-oa模块

SysRoleController

//改造 查询所有角色 和 当前用户所属角色   11
    //测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
    @ApiOperation("查询所有的角色") //knife4j生成接口文档的中文说明   12
    @GetMapping("findAll")
    public Result findAll(){
        //调用serivice的方法
        List<SysRole> list = sysRoleService.list();

        //模拟异常  15
        //int i = 10/0;

        //模拟异常 手动抛出异常 验证我们的自定义异常 16
        try {
            int i = 10/0;
        }catch (Exception e){
            //抛出自定义异常
            throw new GuiguException(20001,"执行了自定义异常处理..");
        }

        return Result.ok(list);
    }

service-util模块

在全局异常类添加方法

GlobalExceptionHandler

//自定义异常处理   16
    @ExceptionHandler(GuiguException.class)
    @ResponseBody
    public Result error(GuiguException e) {
        e.printStackTrace();
        return Result.fail().code(e.getCode()).message(e.getMsg());
    }

测试,自定义异常成功

角色管理_List_31