Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理

目录

一、项目介绍

二、项目搭建

1、配置Swagger2  (Web版说明文档)  ★★

2、统一返回结果  (delete测试)  ★

3、实现带条件带分页查询接口  ★

带参数的分页查询  (wrapper 查询构造器) ★★

4、新增业务

5、修改业务

6、批量删除

7、锁定医院设置

8、统一异常处理  ★

自定义异常


一、项目介绍

1、业务流程 

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第1张图片

2、系统架构

架构设计需要考虑的几个方面:成本、性能(分布式部署,引入缓存)、可扩展性(使用微服务架构,引入消息中间件)、高可用:集群、负载均衡、安全性。

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第2张图片

Git:分布式版本控制工具

Docker:容器虚拟化技术

Nginx:反向代理、负载均衡、动静分离。

集群:防止单机故障、用户无感知。

Redis集群,三主三从最少六台

ELK日志系统 
Kibana:数据展示
Logstash:存储,记录、分析用户行为
ElasticSearch:搜索引擎

二、项目搭建

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第3张图片

 2、项目搭建分层

*jar:java基础工程

*war:web工程包

*pom:父工程

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第4张图片

依赖使用时才会下载,

1、配置Swagger2  (Web版说明文档)  ★★

前后端分离开发模式中,api文档是最好的沟通方式Swagger 根据代码生成Web版说明文档。

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

  1. 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
  2. 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
  3. 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
  4. 可测性 (直接在接口文档上进行测试,以方便理解业务)

常用注解

@EnableSwagger2    Swagger支持。声明在配置类“@Configuration”

@Api(description = "医院设置接口")   定义接口说明

@ApiOperation(value = "医院设置列表")   定义方法说明

@ApiModelProperty(value = "是否成功")   定义属性说明

@ApiParam(name = "id", value = "讲师ID", required = true)   定义参数说明

1. common下面创建子模块service_utils,创建配置类Swagger2Config @EnableSwagger2

package com.atguigu.yygh.common.config;


import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.service.Contact;

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //只显示api路径下的页面
                //.paths(Predicates.and(PathSelectors.regex("/api/.*")))
                .build();
    }

    @Bean
    public Docket adminApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();
    }

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("atguigu", "http://atguigu.com", "[email protected]"))
                .build();
    }

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

2. service 项目添加依赖


    com.atguigu
    service_utils
    0.0.1-SNAPSHOT

3. 启动类添加注解

@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"}) //扫描其他项目配置
public class ServiceHospApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceHospApplication.class, args);
    }
}

4. 启动访问api,测试
http://localhost:8201/swagger-ui.html

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第5张图片

5、优化 

@Api(description = "医院设置接口")   定义接口说明和参数说明

@ApiOperation(value = "医院设置列表")

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第6张图片

2、统一返回结果  (delete测试)  ★

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

1、json
(1)对象:{“name”:”zhang3”,“age”:33}
(2)集合:
[{“name”:”zhang3”,“age”:33},
{“name”:”zhang3”,“age”:33},{“name”:”zhang3”,“age”:33}]

2、统一格式
{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {
    "items": [
      {
        "id": "1",
        "name": "刘德华",
        "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
      }
    ]
  }
}

在common模块下创建子模块common_utils

1. 创建接口定义返回码

public interface ResultCode {
    public static Integer SUCCESS = 20000;

    public static Integer ERROR = 20001;
}

2.  封装结果类

package com.atguigu.yygh.common;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2022/7/13 11:27
 * @Author by:Plisetsky
 */
@Data
public class R {
    @ApiModelProperty(value = "是否成功")
    private Boolean success;

    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private Map data = new HashMap();

    private R(){}

    public static R ok(){
        R r = new R();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    }

    public static R error(){
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMessage(message);
        return this;
    }

    public R code(Integer code){
        this.setCode(code);
        return this;
    }

    public R data(String key, Object value){
        this.data.put(key, value);
        return this;
    }

    public R data(Map map){
        this.setData(map);
        return this;
    }
}

3. 引入依赖


    com.atguigu
    common_utils
    0.0.1-SNAPSHOT

4. controller 接口改造

    //查询所有医院设置
    @ApiOperation(value = "医院设置列表")
    @GetMapping("findAll")
    public R findAll() { //结果对象封装
        List list = hospitalSetService.list();
        return R.ok().data("list",list);
    }

    //删除   测试引入swagger2
    @ApiOperation(value = "医院设置删除")
    @DeleteMapping("{id}") //类型如果不匹配,影响速度
    public R removeById(@PathVariable Long id){
        boolean remove = hospitalSetService.removeById(id);
        return R.ok();
    }

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第7张图片

3、实现带条件带分页查询接口  

1. 分析接口

确认需求:

需要的参数:当前页,每页记录数

返回值:R (Page 分页对象)
Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第8张图片

2. 添加分页插件


@Configuration
@EnableTransactionManagement
@MapperScan("com.atguigu.yygh.hosp.mapper")
public class HospConfig {

    //分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
    
}

3. 实现接口

    @ApiOperation(value = "分页查询医院设置")
    @GetMapping("{page}/{limit}")
    public R pageList(@PathVariable Long page,
                      @PathVariable Long limit){

        //1.创建分页对象,传入参数 当前页 每页记录数
        Page pageParam = new Page<>(page,limit);

        //2.分页查询
        Page pageModel = hospitalSetService.page(pageParam);

        //3.封装结果
        return R.ok().data("pageModel",pageModel);
    }

4. 测试

 Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第9张图片

带参数的分页查询  (wrapper 查询构造器) ★★

1. 分析接口

确认需求:

需要的参数:当前页、每页记录数、查询条件对象(vo:View Object)Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第10张图片

返回值:R (Page 分页对象)

2. 实现接口

@RequestBody :Json 转 Java

@ResponseBod:Java 转 Json串

ApiOperation(value = "条件的分页查询医院设置")
    //@GetMapping("pageQuery/{page}/{limit}")
    @PostMapping("pageQuery/{page}/{limit}")
    public R pageList(@PathVariable Long page,
                      @PathVariable Long limit,
                      @RequestBody HospitalSetQueryVo hospitalSetQueryVo){
        //1.获取参数,验空,存入 wrapper 条件查询构造器
        QueryWrapper wrapper = new QueryWrapper<>();
        String hosname = hospitalSetQueryVo.getHosname();
        String hoscode = hospitalSetQueryVo.getHoscode();
        if(!StringUtils.isEmpty(hosname)){
            //模糊查询
            wrapper.like("hosname",hosname);
        }
        if(!StringUtils.isEmpty(hoscode)){
            wrapper.eq("hoscode",hoscode);
        }

        //2.创建分页对象,传入参数 当前页 每页记录数
        Page pageParam = new Page<>(page,limit);

        //3.分页查询,传入wrapper条件查询构造器
        Page pageModel = hospitalSetService.page(pageParam,wrapper);

        //4.封装结果返回
        return R.ok().data("pageModel",pageModel);
    }

3. 测试结果

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第11张图片 Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第12张图片

4、新增业务

1. 分析接口

需要的参数:HospitalSet

返回值:R.ok()
Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第13张图片

2. 实现接口

    @ApiOperation(value = "新增医院设置")
    @PostMapping("save")
    public R save(@RequestBody HospitalSet hospitalSet){

        boolean save = hospitalSetService.save(hospitalSet);

        return save ? R.ok() : R.error();
    }

注意 测试时把时间和id删掉,会自动生成,hoscode外键唯一不能重复

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第14张图片

Mysql5.5会出现的问题:

Mysql5.5需要使用MP自动填充日期类Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第15张图片

 在实体中添加注解参数Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第16张图片

5、修改业务

1. 分析接口
Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第17张图片

*数据回显:

参数:id

返回值:HospitalSet

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第18张图片

*更新保存

      参数:HospitalSet

      返回值:R.ok()

2. 实现接口

    @ApiOperation(value = "根据id查询医院设置")
    @GetMapping("getHospsetById/{id}")
    public R getById(@PathVariable Long id){
        HospitalSet hospitalSet = hospitalSetService.getById(id);
        return R.ok().data("hospitalSet",hospitalSet);
    }

    @ApiOperation(value = "修改医院设置")
    @PostMapping("update")
    public R update(@RequestBody HospitalSet hospitalSet){
        //hospitalSet,存在id
        boolean save = hospitalSetService.updateById(hospitalSet);

        return save ? R.ok() : R.error();
    }

6、批量删除

1. 分析需求

参数:List 勾选的Id集合

返回值:R.ok()

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第19张图片

2. 接口实现

    //删除   测试引入swagger2
    @ApiOperation(value = "批量删除医院设置")
    @DeleteMapping("batchRemove")
    public R removeById(@RequestBody List idList){
        boolean remove = hospitalSetService.removeByIds(idList);
        
        return remove ? R.ok() : R.error();
    }

3. 测试 

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第20张图片

7、锁定医院设置

1. 分析需求

参数:id,status

返回值:R.ok()
Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第21张图片

2. 接口实现

    // 医院设置锁定和解锁
    @PutMapping("lockHospitalSet/{id}/{status}")
    public R lockHospitalSet(@PathVariable Long id,
                             @PathVariable Integer status) {
        //1.先查询
        HospitalSet hospitalSet = hospitalSetService.getById(id);
        //2.后更新
        hospitalSet.setStatus(status);
        boolean update = hospitalSetService.updateById(hospitalSet);
        return update ? R.ok() : R.error();
    }

8、统一异常处理  

出于系统信任度、安全考虑,不能将错误信息暴露

@ControllerAdvice  spring AOP面向切面编程,对Controller进行切面环绕。作用:全局异常处理、全局数据预处理、全局数据绑定

@ExceptionHandler (Exception.class)  异常拦截器(自定义异常处理器),需要结合@ControllerAdvice一起使用

service_utils 项目下

1. 添加common项目依赖

    
        
            com.atguigu
            common_utils
            0.0.1-SNAPSHOT
        
    

2. 创建统一异常处理器 AOP?

@ControllerAdvice //AOP面向切面编程
public class GlobalExceptionHandler {

    //抓取运行时异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e){
        e.printStackTrace();
        return R.error();
    }
    //特殊异常处理
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public R error(ArithmeticException e){
        e.printStackTrace();
        return R.error().message("特殊异常处理");
    }
}

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第22张图片

自定义异常

service_utils 项目下

1. handler包内 创建自定义异常

//预约异常
@Data
@AllArgsConstructor
@NoArgsConstructor
public class YyghException extends RuntimeException{

    @ApiModelProperty(value = "状态码")
    private Integer code;
    @ApiModelProperty(value = "异常信息")
    private String msg;
}

2. 异常控制器 添加方法

    //自定义异常处理
    @ExceptionHandler(YyghException.class)
    @ResponseBody
    public R error(YyghException e){
        e.printStackTrace();
        return R.error().code(e.getCode()).message(e.getMessage());
    }

3. 方法手动抛出异常

    //查询所有医院设置
    @ApiOperation(value = "医院设置列表")
    @GetMapping("findAll")
    public R findAll() { //结果对象封装
        try {
            int i = 10/0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new YyghException(20001,"自定义异常");
        }

        List list = hospitalSetService.list();
        return R.ok().data("list",list);
    }

Day102.尚医通项目: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理_第23张图片

9、统一日志处理 (了解)

配置日志级别

日志记录器(Logger)的行为是分等级的。如下表所示:

分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL

默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别

# 设置日志级别
logging.level.root=WARN

这种方式只能将日志打印在控制台上

sout 会产生io,需要删掉

二、Logback日志

spring boot内部使用Logback作为日志实现的框架。

Logback和log4j非常相似,如果你对log4j很熟悉,那对logback很快就会得心应手。

logback相对于log4j的一些优点:比log4j更好的logback、简介和优点详解_小雄哥的博客-CSDN博客_logback优点

1、配置logback日志

删除application.properties中的日志配置

安装idea彩色日志插件:grep-console

resources 中创建 logback-spring.xml 



    
    
    
    

    logback
    
    

    
    
    
    
    
    
    
    


    
    
        
        
        
            INFO
        
        
            ${CONSOLE_LOG_PATTERN}
            
            UTF-8
        
    


    

    
    
        
        ${log.path}/log_info.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8
        
        
        
            
            ${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            INFO
            ACCEPT
            DENY
        
    

    
    
        
        ${log.path}/log_warn.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8 
        
        
        
            ${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            warn
            ACCEPT
            DENY
        
    


    
    
        
        ${log.path}/log_error.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8 
        
        
        
            ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            ERROR
            ACCEPT
            DENY
        
    

    
    
    
    
        
        

        
        
            
            
            
            
        
    


    
    

        
            
            
            
            
            
        
    

将错误日志输出到文件  

GlobalExceptionHandler.java 中,类上添加注解   @Slf4j 

异常输出语句   log.error(e.getMessage());

将日志堆栈信息输出到文件

定义工具类

guli-framework-common下创建util包,创建ExceptionUtil.java工具类

package com.guli.common.util;

public class ExceptionUtil {

	public static String getMessage(Exception e) {
		StringWriter sw = null;
		PrintWriter pw = null;
		try {
			sw = new StringWriter();
			pw = new PrintWriter(sw);
			// 将出错的栈信息输出到printWriter中
			e.printStackTrace(pw);
			pw.flush();
			sw.flush();
		} finally {
			if (sw != null) {
				try {
					sw.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
			if (pw != null) {
				pw.close();
			}
		}
		return sw.toString();
	}
}

调用

log.error(ExceptionUtil.getMessage(e));

GuliException中创建toString方法

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

你可能感兴趣的:(尚医通,spring,boot,springcloud,wrapper,Swagger2)