【金融项目】尚融宝项目(二)

4、后端接口工程搭建

4.1、创建尚融宝接口工程

4.1.1、需求

积分等级CRUD列表和表单

【金融项目】尚融宝项目(二)_第1张图片

【金融项目】尚融宝项目(二)_第2张图片

4.1.2、创建父工程srb

4.1.2.1、创建SpringBoot项目

Group:com.atguigu

Artifact:srb

4.1.2.2、删除src目录

4.1.2.3、配置SpringBoot版本

<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.3.4.RELEASEversion>
parent>

4.1.2.4、配置pom依赖版本号

<properties>
    <java.version>1.8java.version>
    <spring-cloud-alibaba.version>2.2.2.RELEASEspring-cloud-alibaba.version>
    <spring-cloud.version>Hoxton.SR8spring-cloud.version>
    <mybatis-plus.version>3.4.1mybatis-plus.version>
    <velocity.version>2.0velocity.version>
    <swagger.version>2.9.2swagger.version>
    <swagger-bootstrap-ui.version>1.9.2swagger-bootstrap-ui.version>
    <commons-lang3.version>3.9commons-lang3.version>
    <commons-fileupload.version>1.3.1commons-fileupload.version>
    <commons-io.version>2.6commons-io.version>
    <alibaba.easyexcel.version>2.1.1alibaba.easyexcel.version>
    <apache.xmlbeans.version>3.1.0apache.xmlbeans.version>
    <fastjson.version>1.2.28fastjson.version>
    <gson.version>2.8.2gson.version>
    <json.version>20170516json.version>
    <aliyun-java-sdk-core.version>4.3.3aliyun-java-sdk-core.version>
    <aliyun-sdk-oss.version>3.10.2aliyun-sdk-oss.version>
    <jodatime.version>2.10.1jodatime.version>
    <jwt.version>0.7.0jwt.version>
    <httpclient.version>4.5.1httpclient.version>
properties>

4.1.2.5、配置pom依赖

<dependencyManagement>
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>${spring-cloud.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-dependenciesartifactId>
            <version>${spring-cloud-alibaba.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>${mybatis-plus.version}version>
        dependency>
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
            <version>${mybatis-plus.version}version>
        dependency>
        
        <dependency>
            <groupId>org.apache.velocitygroupId>
            <artifactId>velocity-engine-coreartifactId>
            <version>${velocity.version}version>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>${swagger.version}version>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger-uiartifactId>
            <version>${swagger.version}version>
        dependency>
        
        <dependency>
            <groupId>com.github.xiaoymingroupId>
            <artifactId>swagger-bootstrap-uiartifactId>
            <version>${swagger-bootstrap-ui.version}version>
        dependency>
        
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>${commons-lang3.version}version>
        dependency>
        
        <dependency>
            <groupId>commons-fileuploadgroupId>
            <artifactId>commons-fileuploadartifactId>
            <version>${commons-fileupload.version}version>
        dependency>
        
        <dependency>
            <groupId>commons-iogroupId>
            <artifactId>commons-ioartifactId>
            <version>${commons-io.version}version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>easyexcelartifactId>
            <version>${alibaba.easyexcel.version}version>
        dependency>
        
        <dependency>
            <groupId>org.apache.xmlbeansgroupId>
            <artifactId>xmlbeansartifactId>
            <version>${apache.xmlbeans.version}version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>${fastjson.version}version>
        dependency>
        <dependency>
            <groupId>org.jsongroupId>
            <artifactId>jsonartifactId>
            <version>${json.version}version>
        dependency>
        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
            <version>${gson.version}version>
        dependency>
        
        <dependency>
            <groupId>com.aliyungroupId>
            <artifactId>aliyun-java-sdk-coreartifactId>
            <version>${aliyun-java-sdk-core.version}version>
        dependency>
        
        <dependency>
            <groupId>com.aliyun.ossgroupId>
            <artifactId>aliyun-sdk-ossartifactId>
            <version>${aliyun-sdk-oss.version}version>
        dependency>
        
        <dependency>
            <groupId>joda-timegroupId>
            <artifactId>joda-timeartifactId>
            <version>${jodatime.version}version>
        dependency>
        
        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>${jwt.version}version>
        dependency>
        
        <dependency>
            <groupId>org.apache.httpcomponentsgroupId>
            <artifactId>httpclientartifactId>
            <version>${httpclient.version}version>
        dependency>
        
        
    dependencies>
dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
dependencies>

4.1.3、创建模块guigu-common

4.1.3.1、创建Maven模块

在srb下创建普通maven模块

Group:com.atguigu

Artifact:guigu-common

4.1.3.2、配置pom

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
dependencies>

4.1.4、创建模块service-base

4.1.4.1、创建Maven模块

在srb下创建普通maven模块

Group:com.atguigu

Artifact:service-base

4.1.4.2、配置pom

注意:依赖guigu-common

<dependencies>
    <dependency>
        <groupId>com.atguigugroupId>
        <artifactId>guigu-commonartifactId>
        <version>0.0.1-SNAPSHOTversion>
    dependency>
    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger2artifactId>
    dependency>
    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger-uiartifactId>
    dependency>
dependencies>

4.1.5、创建模块service-core

4.1.5.1、创建Maven模块

在srb下创建普通maven模块

Group:com.atguigu

Artifact:service-core

4.1.5.2、配置pom

注意:依赖service-base

<dependencies>
    <dependency>
        <groupId>com.atguigugroupId>
        <artifactId>service-baseartifactId>
        <version>0.0.1-SNAPSHOTversion>
    dependency>
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
    dependency>
    
    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
    dependency>
    
    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-generatorartifactId>
    dependency>
    
    <dependency>
        <groupId>org.apache.velocitygroupId>
        <artifactId>velocity-engine-coreartifactId>
    dependency>
dependencies>

4.1.6、代码生成器

4.1.6.1、创建数据库

创建数据库srb_core

并执行sql脚本初始化数据结构和数据

4.1.6.2、创建代码生成器

在test目录中创建测试用例,并执行

package com.atguigu.srb.core;
public class CodeGenerator {
    @Test
    public void genCode() {
        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("Helen");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setServiceName("%sService"); //去掉Service接口的首字母I
        gc.setIdType(IdType.AUTO); //主键策略
        gc.setSwagger2(true);//开启Swagger2模式
        mpg.setGlobalConfig(gc);
        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf-8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);
        // 4、包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.atguigu.srb.core");
        pc.setEntity("pojo.entity"); //此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
        mpg.setPackageInfo(pc);
        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok
        strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名
        strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀(确保tinyint(1))
        strategy.setRestControllerStyle(true); //restful api风格控制器
        mpg.setStrategy(strategy);
        // 6、执行
        mpg.execute();
    }
}

4.1.7、启动应用程序

4.1.7.1、创建application.yml

server:
  port: 8110 # 服务端口
spring:
  profiles:
    active: dev # 环境设置
  application:
    name: service-core # 服务名
  datasource: # mysql数据库连接
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf-8
    username: root
    password: 123456
mybatis-plus: #mybatis
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:com/atguigu/srb/core/mapper/xml/*.xml

4.1.7.2、创建SpringBoot配置文件

在service-core中创建config包,创建MybatisPlusConfig类

package com.atguigu.srb.core.config;
@Configuration
@MapperScan("com.atguigu.srb.core.mapper")
@EnableTransactionManagement //事务处理
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//分页
        return interceptor;
    }
}

4.1.7.3、创建SpringBoot启动类

注意:扫描com.atguigu.srb

package com.atguigu.srb.core;
@SpringBootApplication
@ComponentScan({"com.atguigu.srb"})
public class ServiceCoreApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceCoreApplication.class, args);
    }
}

4.1.7.4、运行启动类

查看控制台8110端口是否成功启动

4.2、积分等级CRUD

4.2.1、积分等级列表接口

4.2.1.1、编写积分等级管理接口

在controller中添加admin包,添加AdminIntegralGradeController类

package com.atguigu.srb.core.controller.admin;
@CrossOrigin
@RestController
@RequestMapping("/admin/core/integralGrade")
public class AdminIntegralGradeController {
    @Resource
    private IntegralGradeService integralGradeService;
    @GetMapping("/list")
    public List<IntegralGrade> listAll(){
        return integralGradeService.list();
    }
}

4.2.1.2、测试

重启服务,访问: http://localhost:8110/admin/core/integralGrade/list 查看结果json数据

4.2.2、逻辑删除接口

4.2.2.1、添加删除方法

AdminIntegralGradeController添加removeById方法

@DeleteMapping("/remove/{id}")
public boolean removeById(@PathVariable Long id){
    return integralGradeService.removeById(id);
}

4.2.2.2、使用postman测试删除

【金融项目】尚融宝项目(二)_第3张图片

4.2.3、配置Swagger2

4.2.3.1、Swagger2配置文件

在service-base中创建Swagger2Config

package com.atguigu.srb.base.config;
@Configuration
@EnableSwagger2
public class Swagger2Config {
    
    @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 adminApiInfo(){
        return new ApiInfoBuilder()
                .title("尚融宝后台管理系统-API文档")
                .description("本文档描述了尚融宝后台管理系统接口")
                .version("1.0")
                .contact(new Contact("Helen", "http://atguigu.com", "[email protected]"))
                .build();
    }
}

4.2.3.2、查看Swagger文档

重启服务器查看接口文档:http://localhost:8110/swagger-ui.html

4.2.3.3、常见注解

**实体类注解:**entity的实体类中可以添加一些自定义设置,例如:

@ApiModelProperty(value = "创建时间", example = "2019-01-01 8:00:00")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间", example = "2019-01-01 8:00:00")
private LocalDateTime updateTime;

controller注解:

定义在类上

@Api(tags = "积分等级管理")

定义在方法上

@ApiOperation("积分等级列表")
@ApiOperation(value = "根据id删除积分等级", notes = "逻辑删除")

定义在参数上

@ApiParam(value = "数据id", required = true, example = "100")

4.3、统一返回结果

4.3.1、定义统一返回结果

4.3.1.1、数据格式的定义

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

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

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

成功:

    {
      "code": 0,
      "message": "成功",
      "data": 数据
    }

失败:

{
  "code": -1,
  "message": "失败",
  "data": null
}

因此,我们定义统一结果

{
  "code": 数字, //业务响应码
  "message": 字符串, //返回消息
  "data": 对象 //返回数据
}

4.3.1.2、创建枚举

在guigu-common中创建result包,创建枚举 ResponseEnum

package com.atguigu.common.result;
@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {
    SUCCESS(0, "成功"),
    ERROR(-1, "服务器内部错误"),
    ;
    // 响应状态码
    private Integer code;
    // 响应信息
    private String message;
}

完整的枚举源代码:ResponseEnum.java

4.3.1.3、定义同统一结果类

package com.atguigu.common.result;
@Data
public class R {
    private Integer code;
    private String message;
    private Map<String, Object> data = new HashMap();
    /**
     * 构造器私有
     */
    private R(){}
    /**
     * 返回成功
     */
    public static R ok(){
        R r = new R();
        r.setCode(ResponseEnum.SUCCESS.getCode());
        r.setMessage(ResponseEnum.SUCCESS.getMessage());
        return r;
    }
    /**
     * 返回失败
     */
    public static R error(){
        R r = new R();
        r.setCode(ResponseEnum.ERROR.getCode());
        r.setMessage(ResponseEnum.ERROR.getMessage());
        return r;
    }
    /**
     * 设置特定结果
     */
    public static R setResult(ResponseEnum responseEnum){
        R r = new R();
        r.setCode(responseEnum.getCode());
        r.setMessage(responseEnum.getMessage());
        return r;
    }
    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<String, Object> map){
        this.setData(map);
        return this;
    }
}

4.3.2、使用统一返回结果

4.3.2.1、修改listAll

@ApiOperation("积分等级列表")
@GetMapping("/list")
public R listAll(){
    List<IntegralGrade> list = integralGradeService.list();
    return R.ok().data("list", list);
}

4.3.2.2、修改removeById

@ApiOperation(value = "根据id删除积分等级", notes="逻辑删除")
@DeleteMapping("/remove/{id}")
public R removeById(
    @ApiParam(value = "数据id", required = true, example = "1")
    @PathVariable Long id){
    boolean result = integralGradeService.removeById(id);
    if(result){
        //return R.setResult(ResponseEnum.UPLOAD_ERROR);
        return R.ok().message("删除成功");
    }else{
        return R.error().message("删除失败");
    }
}

4.3.2.3、新增数据

@ApiOperation("新增积分等级")
@PostMapping("/save")
public R save(
    @ApiParam(value = "积分等级对象", required = true)
    @RequestBody IntegralGrade integralGrade){
    boolean result = integralGradeService.save(integralGrade);
    if (result) {
        return R.ok().message("保存成功");
    } else {
        return R.error().message("保存失败");
    }
}

4.3.2.4、根据id查询

@ApiOperation("根据id获取积分等级")
@GetMapping("/get/{id}")
public R getById(
    @ApiParam(value = "数据id", required = true, example = "1")
    @PathVariable Long id
    ){
        IntegralGrade integralGrade = integralGradeService.getById(id);
        if(integralGrade != null){
            return R.ok().data("record", integralGrade);
        }else{
            return R.error().message("数据不存在");
        }
}

4.3.2.5、根据id修改

@ApiOperation("更新积分等级")
@PutMapping("/update")
public R updateById(
    @ApiParam(value = "积分等级对象", required = true)
    @RequestBody IntegralGrade integralGrade){
    boolean result = integralGradeService.updateById(integralGrade);
    if(result){
        return R.ok().message("修改成功");
    }else{
        return R.error().message("修改失败");
    }
}

4.4、统一异常处理

4.4.1、项目中的异常

4.4.1.1、制造异常

屏蔽 IntegralGrade 中的 @TableField注解

@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
//@TableField("is_deleted")
@TableLogic
private Boolean deleted;

4.4.1.2、Swagger中测试

测试列表查询功能,查看结果,发生错误,显示响应失败

【金融项目】尚融宝项目(二)_第4张图片

4.4.2、统一异常处理

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

4.4.2.1、创建统一异常处理器

guigu-common中创建exception包,创建统一异常处理器类UnifiedExceptionHandler

package com.atguigu.common.exception;
@Slf4j
@Component //Spring容易自动管理
@RestControllerAdvice //在controller层添加通知。如果使用@ControllerAdvice,则方法上需要添加@ResponseBody
public class UnifiedExceptionHandler {
    /**
     * 未定义异常
     */
    @ExceptionHandler(value = Exception.class) //当controller中抛出Exception,则捕获
    public R handleException(Exception e) {
        log.error(e.getMessage(), e);
        return R.error();
    }
}

4.4.2.2、service-core添加扫描

添加 “com.atguigu.common”

@SpringBootApplication
@ComponentScan({"com.atguigu.srb", "com.atguigu.common"})
public class ServiceCoreApplication {

4.4.2.3、测试

返回统一错误结果

【金融项目】尚融宝项目(二)_第5张图片

4.4.3、处理特定异常

如果我们不想显示统一的“服务器内部错误”,需要个性化的显示异常信息,那么需要针对特定的异常做处理

4.4.3.1、添加依赖

在guigu-common中添加jdbc依赖


    org.springframework
    spring-jdbc

4.4.3.2、添加异常处理方法

在 UnifiedExceptionHandler 中添加

/**
* 特定异常
*/
@ExceptionHandler(BadSqlGrammarException.class)
public R handleBadSqlGrammarException(BadSqlGrammarException e){
    log.error(e.getMessage(), e);
    return R.setResult(ResponseEnum.BAD_SQL_GRAMMAR_ERROR);
}

4.4.3.3、测试

【金融项目】尚融宝项目(二)_第6张图片

问题:上面的例子虽然针对特定的异常显示个性化的错误信息,但是你会发现,针对每个不同的异常我们都需要在项目中添加对应的处理方法,并捕获对应的异常对象,可能还要针对这个异常添加额外的依赖。这显然不是最好的方式。

方案:此类异常直接抛出,并且用Exception类捕获就可以了。

4.4.3.4、恢复制造的异常

@TableField(value = "is_deleted")

4.4.4、自定义异常

目标:使用一个或较少的异常类,可以捕获和显示所有的异常信息。

方案:因此,我们可以创建一个自定义异常类(必须是运行时异常),在程序中抛出这个自定义异常对象,并在统一异常处理器中捕获自定义异常对象

4.4.4.1、创建自定义异常类

package com.atguigu.common.exception;
@Data
@NoArgsConstructor
public class BusinessException extends RuntimeException {
    //状态码
    private Integer code;
    //错误消息
    private String message;
}

完整的源代码:BusinessException.java

4.4.4.2、添加异常处理方法

UnifiedExceptionHandler类中添加

/**
* 自定义异常
*/
@ExceptionHandler(BusinessException.class)
public R handleBusinessException(BusinessException e){
    log.error(e.getMessage(), e);
    return R.error().message(e.getMessage()).code(e.getCode());
}

4.4.4.3、修改Controller

在AdminIntegralGradeController的方法中添加异常处理,业务中需要的位置抛出BusinessException自定义异常。

@ApiOperation("新增积分等级")
@PostMapping("/save")
public R save(
    @ApiParam(value = "积分等级对象", required = true)
    @RequestBody IntegralGrade integralGrade){
    //如果借款额度为空就手动抛出一个自定义的异常!
    if(integralGrade.getBorrowAmount() == null){
        //BORROW_AMOUNT_NULL_ERROR(-201, "借款额度不能为空"),
        throw new BusinessException(ResponseEnum.BORROW_AMOUNT_NULL_ERROR);
    }
    boolean result = integrationService.save(integralGrade);
    if (result) {
        return R.ok().message("保存成功");
    } else {
        return R.error().message("保存失败");
    }
}

4.4.4.4、测试

测试

【金融项目】尚融宝项目(二)_第7张图片

结果

【金融项目】尚融宝项目(二)_第8张图片

4.4.5、异常处理优化

目标:以优雅的 Assert(断言) 方式来校验业务的异常情况,消除 if else

4.4.5.1、什么是断言

package com.atguigu.srb.core;
import org.junit.jupiter.api.Test;
import org.springframework.util.Assert;
public class AssertTests {
    //if else的用法
    @Test
    public void test1() {
        Object o = null;
        if (o == null) {
            throw new IllegalArgumentException("用户不存在.");
        }
    }
    //断言的用法:更为简洁
    @Test
    public void test2() {
        // 另一种写法
        Object o = null;
        Assert.notNull(o, "用户不存在.");
    }
}

4.4.5.2、自定义断言

用断言的方式封装异常的抛出

package com.atguigu.common.exception;
@Slf4j
public abstract class Assert {
    /**
     * 断言对象不为空
     * 如果对象obj为空,则抛出异常
     * @param obj 待判断对象
     */
    public static void notNull(Object obj, ResponseEnum responseEnum) {
        if (obj == null) {
            log.info("obj is null...............");
            throw new BusinessException(responseEnum);
        }
    }
}

完整的源代码:Assert.java

4.4.5.3、修改controller

在controller中用断言替换if else

Assert.notNull(integralGrade.getBorrowAmount(), ResponseEnum.BORROW_AMOUNT_NULL_ERROR);

4.4.6、Controller上层异常

4.4.6.1、异常分类

对异常按阶段进行分类,大体可以分成:进入Controller前的异常 和 业务层异常,具体可以参考下图:

【金融项目】尚融宝项目(二)_第9张图片

4.4.6.2、处理Controller上层异常

UnifiedExceptionHandler中添加

/**
     * Controller上一层相关异常
     */
@ExceptionHandler({
    NoHandlerFoundException.class,
    HttpRequestMethodNotSupportedException.class,
    HttpMediaTypeNotSupportedException.class,
    MissingPathVariableException.class,
    MissingServletRequestParameterException.class,
    TypeMismatchException.class,
    HttpMessageNotReadableException.class,
    HttpMessageNotWritableException.class,
    MethodArgumentNotValidException.class,
    HttpMediaTypeNotAcceptableException.class,
    ServletRequestBindingException.class,
    ConversionNotSupportedException.class,
    MissingServletRequestPartException.class,
    AsyncRequestTimeoutException.class
        })
public R handleServletException(Exception e) {
    log.error(e.getMessage(), e);
    //SERVLET_ERROR(-102, "servlet请求异常"),
    return R.error().message(ResponseEnum.SERVLET_ERROR.getMessage()).code(ResponseEnum.SERVLET_ERROR.getCode());
}

4.4.6.3、测试

在save测试用例中输入非法的json参数,则得到下面的结果。我们可以在控制台日志中查看具体的错误原因。前端只需要返回相对简单友好的提示即可。

【金融项目】尚融宝项目(二)_第10张图片

4.5、统一日志处理

4.5.1、Logback日志

4.5.1.1、什么是日志

通过日志查看程序的运行过程,运行信息,异常信息等

4.5.1.2、日志级别

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

分为:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF

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

# 设置日志级别
logging:
  level:
    root: ERROR

这种方式能将ERROR级别以及以上级别的日志输出到控制台上,其他级别将不会输出

4.5.1.3、创建日志文件

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

先删除前面在application.yml中的日志级别配置

resources 中创建 logback-spring.xml (默认日志文件的名字)


<configuration>
configuration>

4.5.1.4、创建测试日志输出

将以下日志输出到任意controller的方法中即可,例如list方法中

@ApiOperation("积分等级列表")
@GetMapping("/list")
public R listAll(){
    log.info("hi i'm helen");
    log.warn("warning!!!");
    log.error("it's a error");
    List<IntegralGrade> list = integrationService.list();
    return R.ok().data("list", list);
}

4.5.2、基本配置说明

4.5.2.1、configuration

日志配置的根节点

<configuration>configuration>

4.5.2.2、contextName

是的子节点。

每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,用于区分不同的应用程序。

<contextName>atguiguSrbcontextName>

4.5.2.3、property

是的子节点,用来定义变量。

有两个属性,name和value:name的值是变量的名称,value是变量的值。

通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。


<property name="log.path" value="D:/project/finance/srb_log/core" />






<property name="CONSOLE_LOG_PATTERN"
          value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) %highlight([%-5level]) %green(%logger) %msg%n"/>

<property name="FILE_LOG_PATTERN"
          value="%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n" />

<property name="ENCODING"
          value="UTF-8" />

4.5.2.4、appender

是的子节点,是负责写日志的组件

有两个必要属性name和class:name指定appender名称,class指定appender的全限定名

对日志进行格式化

定义日志的具体输出格式

编码方式

控制台日志配置


<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>${CONSOLE_LOG_PATTERN}pattern>
        <charset>${ENCODING}charset>
    encoder>
appender>

**文件日志配置 **

表示日志文件的位置,如果上级目录不存在会自动创建,没有默认值。

默认 true,日志被追加到文件结尾,如果是 false,服务重启后清空现存文件。


<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${log.path}/log.logfile>
    <append>trueappend>
    <encoder>
        <pattern>${FILE_LOG_PATTERN}pattern>
        <charset>${ENCODING}charset>
    encoder>
appender>

4.5.2.5、logger

可以是的子节点,用来设置某一个包或具体某一个类的日志打印级别、指定

name:用来指定受此logger约束的某一个包或者具体的某一个类

level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF。默认继承上级的级别

可以包含零个或多个元素,标识这个appender将会添加到这个logger


<logger name="com.atguigu" level="INFO">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="FILE" />
logger>

4.5.2.6、测试

测试日志记录的控制台输出、文件输出、以及日志级别

4.5.3、多环境配置

springProfile

在一个基于Spring boot开发的项目里,常常需要有多套环境的配置:开发,测试以及产品。使用springProfile 可以分别配置开发(dev),测试(test)以及生产(prod)等不同的环境


<springProfile name="dev,test">
    <logger name="com.atguigu" level="INFO">
        <appender-ref ref="CONSOLE" />
    logger>
springProfile>

<springProfile name="prod">
    <logger name="com.atguigu" level="ERROR">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    logger>
springProfile>

4.5.4、滚动日志

问题:生产环境下,如果系统长时间运行,那么日志文件会变得越来越大,系统读取和写入日志的时间会越来越慢,严重的情况会耗尽系统内存,导致系统宕机。

解决方案:可以设置滚动日志。

4.5.4.1、设置时间滚动策略

RollingFileAppender是Appender的另一个实现,表示滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将旧日志备份到其他文件

是的子节点,用来定义滚动策略。

TimeBasedRollingPolicy:最常用的滚动策略,根据时间来制定滚动策略。

:包含文件名及转换符, “%d”可以包含指定的时间格式,如:%d{yyyy-MM-dd}。如果直接使用 %d,默认格式是 yyyy-MM-dd。:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且是6,则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。

<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    
    <file>${log.path}/log-rolling.logfile>
    <encoder>
        <pattern>${FILE_LOG_PATTERN}pattern>
        <charset>${ENCODING}charset>
    encoder>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        
        <fileNamePattern>${log.path}/info/log-rolling-%d{yyyy-MM-dd}.logfileNamePattern>
        
        <maxHistory>15maxHistory>
    rollingPolicy>
appender>

4.5.4.2、设置触发滚动时机

放在的子节点的位置,基于实践策略的触发滚动策略

设置触发滚动条件:单个文件大于100M时生成新的文件

注意:修改日志文件名 此时 ${log.path}/info/log-rolling-%d{yyyy-MM-dd}.%i.log

<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    <maxFileSize>1KBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>

4.5.5、完整的日志配置文件


<configuration>
    <contextName>atguiguSrbcontextName>
    
    <property name="log.path" value="D:/project/test/srb_log/core" />
    
    
    
    
    
    
    <property name="CONSOLE_LOG_PATTERN"
              value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) %highlight([%-5level]) %green(%logger) %msg%n"/>
    
    <property name="FILE_LOG_PATTERN"
              value="%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n" />
    
    <property name="ENCODING"
              value="UTF-8" />
    
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}pattern>
            <charset>${ENCODING}charset>
        encoder>
    appender>
    
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${log.path}/log.logfile>
        <append>trueappend>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}pattern>
            <charset>${ENCODING}charset>
        encoder>
    appender>
    <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        
        <file>${log.path}/log-rolling.logfile>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}pattern>
            <charset>${ENCODING}charset>
        encoder>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            
            <fileNamePattern>${log.path}/info/log-rolling-%d{yyyy-MM-dd}.%i.logfileNamePattern>
            
            <maxHistory>15maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>1KBmaxFileSize>
            timeBasedFileNamingAndTriggeringPolicy>
        rollingPolicy>
    appender>
    
    
    
    
    
    <springProfile name="dev,test">
        <logger name="com.atguigu" level="INFO">
            <appender-ref ref="CONSOLE" />
        logger>
    springProfile>
    
    <springProfile name="prod">
        <logger name="com.atguigu" level="ERROR">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="ROLLING_FILE" />
        logger>
    springProfile>
configuration>

本文章参考B站 尚硅谷《尚融宝》Java微服务分布式金融项目,仅供个人学习使用,部分内容为本人自己见解,与尚硅谷无关。

你可能感兴趣的:(Java项目,金融,java,spring,boot,spring,cloud,vue)