当前位置: 首页 > article >正文

Day2 -- QingLuoPay基础功能搭建

Day2 -- QingLuoPay基础功能搭建

    • 一,基础功能包含哪些部分?
    • 二,功能实现
      • 1. Http状态码定义
      • 2. 自定义异常
      • 3. 序列化返回数据
      • 4. 数据校验

项目git地址:https://gitee.com/jixuonline/qing-luo-pay.git

一,基础功能包含哪些部分?

我们的基础工程也就是在项目开始之前我们需要将所需要的一下通用类如 状态码,自定义异常,数据校验,序列化返回数据等实现

  • Http状态码
  • 自定义异常
  • 序列化返回数据
  • 数据校验

该部分内容我把他放在了core包下实现

二,功能实现

可以看一下项目目录

1. Http状态码定义

这部分没必要多说,就是简单定义了我们的返回状态码

com.qingluo.pay.status.HttpStatus

package com.qingluo.pay.status;

import org.springframework.lang.Nullable;

/**
 * 返回状态码
 *
 * @author dz
 * @Date 2020-12-16
 */
public enum HttpStatus {
    CONTINUE(100, HttpStatus.Series.INFORMATIONAL, "继续"),
    SWITCHING_PROTOCOLS(101, HttpStatus.Series.INFORMATIONAL, "切换协议"),
    PROCESSING(102, HttpStatus.Series.INFORMATIONAL, "处理中"),
    CHECKPOINT(103, HttpStatus.Series.INFORMATIONAL, "检查点"),
    OK(200, HttpStatus.Series.SUCCESSFUL, "请求成功"),
    CREATED(201, HttpStatus.Series.SUCCESSFUL, "已创建"),
    ACCEPTED(202, HttpStatus.Series.SUCCESSFUL, "已接受"),
    NON_AUTHORITATIVE_INFORMATION(203, HttpStatus.Series.SUCCESSFUL, "非权威信息"),
    NO_CONTENT(204, HttpStatus.Series.SUCCESSFUL, "无内容"),
    RESET_CONTENT(205, HttpStatus.Series.SUCCESSFUL, "重置内容"),
    PARTIAL_CONTENT(206, HttpStatus.Series.SUCCESSFUL, "部分内容"),
    MULTI_STATUS(207, HttpStatus.Series.SUCCESSFUL, "多状态"),
    ALREADY_REPORTED(208, HttpStatus.Series.SUCCESSFUL, "已报告"),
    IM_USED(226, HttpStatus.Series.SUCCESSFUL, "IM 已使用"),
    MULTIPLE_CHOICES(300, HttpStatus.Series.REDIRECTION, "多种选择"),
    MOVED_PERMANENTLY(301, HttpStatus.Series.REDIRECTION, "永久重定向"),
    FOUND(302, HttpStatus.Series.REDIRECTION, "已找到"),
    /** @deprecated */
    @Deprecated
    MOVED_TEMPORARILY(302, HttpStatus.Series.REDIRECTION, "临时重定向"),
    SEE_OTHER(303, HttpStatus.Series.REDIRECTION, "查看其他位置"),
    NOT_MODIFIED(304, HttpStatus.Series.REDIRECTION, "未修改"),
    /** @deprecated */
    @Deprecated
    USE_PROXY(305, HttpStatus.Series.REDIRECTION, "使用代理"),
    TEMPORARY_REDIRECT(307, HttpStatus.Series.REDIRECTION, "临时重定向"),
    PERMANENT_REDIRECT(308, HttpStatus.Series.REDIRECTION, "永久重定向"),
    BAD_REQUEST(400, HttpStatus.Series.CLIENT_ERROR, "错误请求"),
    UNAUTHORIZED(401, HttpStatus.Series.CLIENT_ERROR, "未授权"),
    PAYMENT_REQUIRED(402, HttpStatus.Series.CLIENT_ERROR, "需要付款"),
    FORBIDDEN(403, HttpStatus.Series.CLIENT_ERROR, "禁止访问"),
    NOT_FOUND(404, HttpStatus.Series.CLIENT_ERROR, "未找到"),
    METHOD_NOT_ALLOWED(405, HttpStatus.Series.CLIENT_ERROR, "方法不允许"),
    NOT_ACCEPTABLE(406, HttpStatus.Series.CLIENT_ERROR, "不可接受"),
    PROXY_AUTHENTICATION_REQUIRED(407, HttpStatus.Series.CLIENT_ERROR, "需要代理认证"),
    REQUEST_TIMEOUT(408, HttpStatus.Series.CLIENT_ERROR, "请求超时"),
    CONFLICT(409, HttpStatus.Series.CLIENT_ERROR, "冲突"),
    GONE(410, HttpStatus.Series.CLIENT_ERROR, "已删除"),
    LENGTH_REQUIRED(411, HttpStatus.Series.CLIENT_ERROR, "需要内容长度"),
    PRECONDITION_FAILED(412, HttpStatus.Series.CLIENT_ERROR, "前提条件失败"),
    PAYLOAD_TOO_LARGE(413, HttpStatus.Series.CLIENT_ERROR, "负载过大"),
    /** @deprecated */
    @Deprecated
    REQUEST_ENTITY_TOO_LARGE(413, HttpStatus.Series.CLIENT_ERROR, "请求实体过大"),
    URI_TOO_LONG(414, HttpStatus.Series.CLIENT_ERROR, "URI 过长"),
    /** @deprecated */
    @Deprecated
    REQUEST_URI_TOO_LONG(414, HttpStatus.Series.CLIENT_ERROR, "请求 URI 过长"),
    UNSUPPORTED_MEDIA_TYPE(415, HttpStatus.Series.CLIENT_ERROR, "不支持的媒体类型"),
    REQUESTED_RANGE_NOT_SATISFIABLE(416, HttpStatus.Series.CLIENT_ERROR, "请求范围不可满足"),
    EXPECTATION_FAILED(417, HttpStatus.Series.CLIENT_ERROR, "期望失败"),
    I_AM_A_TEAPOT(418, HttpStatus.Series.CLIENT_ERROR, "我是一个茶壶"),
    /** @deprecated */
    @Deprecated
    INSUFFICIENT_SPACE_ON_RESOURCE(419, HttpStatus.Series.CLIENT_ERROR, "资源空间不足"),
    /** @deprecated */
    @Deprecated
    METHOD_FAILURE(420, HttpStatus.Series.CLIENT_ERROR, "方法失败"),
    /** @deprecated */
    @Deprecated
    DESTINATION_LOCKED(421, HttpStatus.Series.CLIENT_ERROR, "目标已锁定"),
    UNPROCESSABLE_ENTITY(422, HttpStatus.Series.CLIENT_ERROR, "无法处理的实体"),
    LOCKED(423, HttpStatus.Series.CLIENT_ERROR, "已锁定"),
    FAILED_DEPENDENCY(424, HttpStatus.Series.CLIENT_ERROR, "依赖失败"),
    TOO_EARLY(425, HttpStatus.Series.CLIENT_ERROR, "过早"),
    UPGRADE_REQUIRED(426, HttpStatus.Series.CLIENT_ERROR, "需要升级"),
    PRECONDITION_REQUIRED(428, HttpStatus.Series.CLIENT_ERROR, "需要前提条件"),
    TOO_MANY_REQUESTS(429, HttpStatus.Series.CLIENT_ERROR, "请求过多"),
    REQUEST_HEADER_FIELDS_TOO_LARGE(431, HttpStatus.Series.CLIENT_ERROR, "请求头字段过大"),
    UNAVAILABLE_FOR_LEGAL_REASONS(451, HttpStatus.Series.CLIENT_ERROR, "因法律原因不可用"),
    INTERNAL_SERVER_ERROR(500, HttpStatus.Series.SERVER_ERROR, "服务器内部错误"),
    NOT_IMPLEMENTED(501, HttpStatus.Series.SERVER_ERROR, "未实现"),
    BAD_GATEWAY(502, HttpStatus.Series.SERVER_ERROR, "错误网关"),
    SERVICE_UNAVAILABLE(503, HttpStatus.Series.SERVER_ERROR, "服务不可用"),
    GATEWAY_TIMEOUT(504, HttpStatus.Series.SERVER_ERROR, "网关超时"),
    HTTP_VERSION_NOT_SUPPORTED(505, HttpStatus.Series.SERVER_ERROR, "HTTP 版本不受支持"),
    VARIANT_ALSO_NEGOTIATES(506, HttpStatus.Series.SERVER_ERROR, "变体也可协商"),
    INSUFFICIENT_STORAGE(507, HttpStatus.Series.SERVER_ERROR, "存储空间不足"),
    LOOP_DETECTED(508, HttpStatus.Series.SERVER_ERROR, "检测到循环"),
    BANDWIDTH_LIMIT_EXCEEDED(509, HttpStatus.Series.SERVER_ERROR, "超出带宽限制"),
    NOT_EXTENDED(510, HttpStatus.Series.SERVER_ERROR, "未扩展"),
    NETWORK_AUTHENTICATION_REQUIRED(511, HttpStatus.Series.SERVER_ERROR, "需要网络认证");

    private final int value;
    private final Series series;
    private final String reasonPhrase;

    HttpStatus(int value, Series series, String reasonPhrase) {
        this.value = value;
        this.series = series;
        this.reasonPhrase = reasonPhrase;
    }

    /**
     * 获取状态码的值
     */
    public int value() {
        return this.value;
    }

    /**
     * 获取状态码的系列
     */
    public Series series() {
        return this.series;
    }

    /**
     * 获取状态码的描述
     */
    public String getReasonPhrase() {
        return this.reasonPhrase;
    }

    /**
     * 根据状态码的值查找对应的枚举值
     */
    public static HttpStatus valueOf(int statusCode) {
        for (HttpStatus status : values()) {
            if (status.value == statusCode) {
                return status;
            }
        }
        throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
    }

    /**
     * 判断状态码是否为 1xx 信息响应
     */
    public boolean is1xxInformational() {
        return this.series == Series.INFORMATIONAL;
    }

    /**
     * 判断状态码是否为 2xx 成功响应
     */
    public boolean is2xxSuccessful() {
        return this.series == Series.SUCCESSFUL;
    }

    /**
     * 判断状态码是否为 3xx 重定向
     */
    public boolean is3xxRedirection() {
        return this.series == Series.REDIRECTION;
    }

    /**
     * 判断状态码是否为 4xx 客户端错误
     */
    public boolean is4xxClientError() {
        return this.series == Series.CLIENT_ERROR;
    }

    /**
     * 判断状态码是否为 5xx 服务器错误
     */
    public boolean is5xxServerError() {
        return this.series == Series.SERVER_ERROR;
    }

    /**
     * HTTP 状态码系列
     */
    public enum Series {
        INFORMATIONAL(1),
        SUCCESSFUL(2),
        REDIRECTION(3),
        CLIENT_ERROR(4),
        SERVER_ERROR(5);

        private final int value;

        Series(int value) {
            this.value = value;
        }

        /**
         * 获取系列的值
         */
        public int value() {
            return this.value;
        }

        /**
         * 根据状态码的值查找对应的系列
         */
        public static Series valueOf(int statusCode) {
            int seriesCode = statusCode / 100;
            for (Series series : values()) {
                if (series.value == seriesCode) {
                    return series;
                }
            }
            throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
        }
    }
}

2. 自定义异常

先来看下面两张图,这是异常类的核心

在图一中我们自定义了一个异常类继承了RunTimeException,这样在抛出异常的时候就可以抛QLException

那么方法定义好了接受的参数怎么设置?在这里使用了枚举类来实现参数的介绍

我们来看一下枚举类的代码

public interface BaseEnum {
    /**
     * 业务状态码
     */
    Integer getCode();

    /**
     * 业务说明
     */
    String getValue();

}
public interface BaseExecptionEnum extends BaseEnum{
    /**
     * http响应状态码
     */
    Integer getStatus();

}

通过上述两个接口我们就可以实现以上三个参数的获取(子类实现),这样就可以实现对枚举类的封装

public enum ExceptionEnum implements BaseExecptionEnum{

    // 定义异常枚举类
    PARAMS_NOT_NULL(1001,"参数不能为空");

    private Integer code;
    private Integer status;
    private String value;

    ExceptionEnum(Integer code,String msg){

    }


    ExceptionEnum(Integer code, Integer status,String msg){

    }

    /**
     * 通过相应码获取对应枚举类
     * @param code
     * @return
     */
    public static ExceptionEnum codeOf(Integer code) {
        if (ObjectUtil.isEmpty(code)){
            throw new QLException(PARAMS_NOT_NULL);
        }
        return EnumUtil.getBy(ExceptionEnum::getCode, code);
    }


    /**
     * http响应状态码
     */
    @Override
    public Integer getStatus() {
        return this.status;
    }

    /**
     * 业务状态码
     */
    @Override
    public Integer getCode() {
        return this.code;
    }

    /**
     * 业务说明
     */
    @Override
    public String getValue() {
        return this.value;
    }
}

之后的核心就是我们的QLException自定义异常类的实现,该部分相当复杂,使用了异常链来实现父类异常与自定义异常的抛出,诸位感兴趣可以详细看看代码,其中包含注释

public class QLException extends RuntimeException{


    // 序列化版本号
    private static final long serialVersionUID = 1L;

    // 错误信息
    private String msg;
    // 业务状态码,规则:4位数,从1001开始递增
    private int code = 1001;
    // Http 状态码
    private int status;



    /**
     * 用于自定义异常的手动抛出
     *
     * throw new QLException();
     * @param execptionEnum
     */
    public QLException(BaseExecptionEnum execptionEnum){
        // 调用父类设置错误信息
        super(execptionEnum.getValue());
        // 获取对应参数
        this.status = execptionEnum.getStatus();
        this.code = execptionEnum.getCode();
        this.msg = execptionEnum.getValue();

    }
    public QLException(String msg){
        // 获取对应参数
        super(msg);
        this.msg = msg;

    }

    public QLException(String msg,int code){
        // 获取对应参数
        super(msg);
        this.msg = msg;
        this.code = code;

    }

    public QLException(String msg,int code,int status){
        // 获取对应参数
        super(msg);
        this.msg = msg;
        this.code = code;
        this.status = status;

    }



    /**
     * s使用异常链的设计模式,通过自定义异常封装捕获的异常
     * try {
     *             String content = FileReaderUtil.readFile(filePath);
     *             System.out.println("文件内容: " + content);
     * } catch (FileReadException e) {
     *             // 捕获自定义异常,打印异常信息和原始异常信息
     *             System.err.println("捕获到自定义异常: " + e.getMessage());
     *             System.err.println("原始异常信息: " + e.getCause().getMessage());
     *         }
     * @param execptionEnum
     * @param err
     */
    public QLException(BaseExecptionEnum execptionEnum,Throwable err){
        // 上抛给父级构造处理
        super(execptionEnum.getValue(),err);
        // 获取对应参数
        this.status = execptionEnum.getStatus();
        this.code = execptionEnum.getCode();
        this.msg = execptionEnum.getValue();

    }

    public QLException(String msg,Throwable err){
        // 上抛给父级构造处理
        super(msg,err);
        // 获取对应参数
        this.msg = msg;

    }

    public QLException(String msg,int code ,Throwable err){
        // 上抛给父级构造处理
        super(msg,err);
        // 获取对应参数
        this.msg = msg;
        this.code = code;

    }

    public QLException(String msg,int code , int status ,Throwable err){
        // 上抛给父级构造处理
        super(msg,err);
        // 获取对应参数
        this.msg = msg;
        this.code = code;
        this.status = status;

    }






}

做完这些就是对异常的拦截,在这里创建了一个全局异常处理器,通过@RestControllerAdvice注解拦截Controller层的异常信息,在这里要拦截的数据分为几部分

  • 空参异常(未实现)
  • 自定义异常
  • 未知异常
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);



    /**
     * 拦截Assert抛出的异常
     * @param e
     * @return
     */
    @ExceptionHandler
    public static ResponseEntity<Object> handle(IllegalArgumentException e){
        if (ObjectUtil.isNotEmpty(e.getMessage())){
            log.error("Assert参数断言异常 --> ",ExceptionUtil.getMessage(e));
        }

        return ResponseEntity.status(BAD_REQUEST).body(
                MapUtil.builder().put("code",BAD_REQUEST)
                        .put("msg",e.getMessage())
        );
    }

    /**
     * 对于ValidationException的异常进行处理
     * @param e
     * @return
     */
    @ExceptionHandler
    public static ResponseEntity<Object> handle(ValidationException e){
        List<String> errors = null;
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) e;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            errors = violations.stream()
                    .map(ConstraintViolation::getMessage).collect(Collectors.toList());
        }
        if (ObjectUtil.isNotEmpty(e.getCause())) {
            log.error("参数校验失败异常 -> ", e);
        }
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(MapUtil.<String, Object>builder()
                        .put("code", HttpStatus.BAD_REQUEST.value())
                        .put("msg", errors)
                        .build());
    }


    /**
     * 捕获自定义异常
     * @param e
     * @return
     */
    @ExceptionHandler
    public static ResponseEntity<Object> handle(QLException e){
        // 如果异常存在打印异常
        if (ObjectUtil.isNotEmpty(e.getMessage())){
            log.error("自定义异常 --> " , ExceptionUtil.getMessage(e));
        }
        /**
         * ResponseEntity.status(e.getStatus()) --> 获取响应对象
         * 设置Http相应正文
         *
         *
         * 400 Bad Request
         * {
         *     "code": 1001,
         *     "msg": "Invalid input parameters"
         * }
         */
        return ResponseEntity.status(e.getStatus()).body(
                MapUtil.builder().put("code",e.getCode())
                        .put("msg",e.getMsg())
        );
    }

    /**
     * 处理未知异常
     * @param e
     * @return
     */
    @ExceptionHandler
    public static ResponseEntity<Object> handle(Exception e){
        if (ObjectUtil.isNotEmpty(e.getMessage())){
            log.error("未定义异常 --> " , ExceptionUtil.getMessage(e));
        }

        return ResponseEntity.status(BAD_REQUEST).body(
                MapUtil.builder().put("code",BAD_REQUEST)
                        .put("msg", e.getMessage())
        );
    }

}

3. 序列化返回数据

用于序列化返回数据

/**
 * @Author JiXu
 * @Date 2025/1/3 下午3:24
 * @ClassName: ResSerialization
 * @Description: 用于序列化返回数据
 */
// 保证在序列化的过程当中如果value值为null,那么其key也为null
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResSerialization<T> implements Serializable {
    // 声明序列化版本
    private static final long serialVersionUID = 7735505903525411467L;

    // 响应状态码
    private int code;
    // 响应消息
    private String msg;
    // 返回数据
    private T data;

    /**
     * 创建构造函数
     */
    public ResSerialization(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public ResSerialization(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public ResSerialization(int code, T data) {
        this.code = code;
        this.data = data;
    }

    /**
     * 声明泛型方法
     */
    public static <T> ResSerialization<T> success(int code, String msg){
        return new ResSerialization<T>(code,msg);
    }
    public static <T> ResSerialization<T> success(T data){
        return new ResSerialization<T>(HttpStatus.SUCCESS,null,data);
    }
    public static <T> ResSerialization<T> success(int code, String msg,T data){
        return new ResSerialization<T>(code,msg,data);
    }

    public static <T> ResSerialization<T> error(int code, String msg,T data){
        return new ResSerialization<T>(code,msg,data);
    }
    public static <T> ResSerialization<T> error(int code, String msg){
        return new ResSerialization<T>(code,msg);
    }
    public static <T> ResSerialization<T> error(T data){
        return new ResSerialization<T>(HttpStatus.SUCCESS,null,data);
    }

    public int getCode(){
        return code;
    }
    public String getMsg(){
        return msg;
    }
    public T getData(){
        return data;
    }


}

4. 数据校验

对于数据校验我们采用一下两种方法

  • hibernate-validator所提供的注解
  • 业务层面中使用Assert断言以及if语句进行判断

如何使用hibernate-validator

在com.qingluo.pay.aop;包下创建ValidatedAspect实现切面增强

@Aspect
@Slf4j
@EnableAspectJAutoProxy
@Component
public class ValidatedAspect {

    @Resource
    private Validator validator;

    @Around("execution(* com.sl..controller.*Controller.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取@RequestBody映射的对象
        Object body = AspectUtil.getBody(proceedingJoinPoint);
        // 不为空的body进行拦截校验
        if (!ObjectUtil.isEmpty(body)) {
            // 进行校验
            Set<ConstraintViolation<Object>> validateResult = validator.validate(body);
            if (CollUtil.isNotEmpty(validateResult)) {
                //没有通过校验,抛出异常,由统一异常处理机制进行处理,响应400
                String info = JSONUtil.toJsonStr(validateResult.stream()
                        .map(ConstraintViolation::getMessage).collect(Collectors.toList()));
                throw new SLException(info, HttpStatus.BAD_REQUEST.value());
            }
        }
        //校验通过,执行原方法
        return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
    }

}

http://www.kler.cn/a/467143.html

相关文章:

  • [paddle] 非线性拟合问题的训练
  • FQ-GAN代码解析
  • 【ArcGIS Pro二次开发实例教程】(1):图层的前置、后置
  • Qt 5.14.2 学习记录 —— 일 新项目
  • JVM学习:CMS和G1收集器浅析
  • Docker新手:在tencent云上实现Python服务打包到容器
  • window11 wsl mysql8 错误分析:1698 - Access denied for user ‘root‘@‘kong.mshome.net‘
  • vue3 ui组件子组件封装v-model绑定props modelValue
  • 使用SSH建立内网穿透,能够访问内网的web服务器
  • 使用Docker部署最新版JupyterHub
  • 如何利用群晖NAS实现远程访问你的网页版Linux虚拟桌面环境
  • [gcc]代码演示-O使用场景
  • SQL中聚类后字段数据串联字符串方法研究
  • kernel32.dll动态链接库报错要怎解决?详细解析kernel32.dll文件缺失解决方案
  • 什么是 C++ 的序列化?
  • 【一文解析】新能源汽车VCU电控开发——能量回收模块
  • STM32-笔记23-超声波传感器HC-SR04
  • kubernets基础入门
  • 基于STM32的热带鱼缸控制系统的设计
  • 大模型数据采集和预处理:把所有数据格式,word、excel、ppt、jpg、pdf、表格等转为数据
  • 高清监控视频的管理与展示:从摄像头到平台的联接过程
  • 呼叫中心中间件实现IVR进入排队,判断排队超时播放提示音
  • Git快速入门(一)·Git软件的安装以及GitHubDesktop客户端的安装
  • 装饰器模式详解
  • clickhouse Cannot execute replicated DDL query, maximum retries exceeded报错解决
  • Android 14.0 系统限制上网系列之iptables用IOemNetd实现app上网黑名单的实现