(五)从零搭建后端框架——统一基类、接口、返回对象设计

1,741 阅读3分钟

前言

在每个项目中,都有自己的一套统一开发规范。比如API接口必须返回统一格式,比如实体对象必须继承指定的类等等。 好的设计可以让开发事半功倍,而坏的设计可能让开发感到很难受。

废话不多说,下面是本项目的设计。

具体实现

API统一返回类 ApiResult

最先应该设计的就是API的返回格式,如下:

@Slf4j
@Getter
@Setter
public class ApiResult<T> implements Serializable {

    private static final long serialVersionUID = 2538767816320181885L;

    protected Integer code;

    protected T data;

    protected String message;

    public ApiResult() {
    }

    public ApiResult(Integer code) {
        this(code, null, null);
    }

    public ApiResult(Integer code, T data) {
        this(code, data, null);
    }

    public ApiResult(Integer code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
    }

    public static ApiResult success() {
        return success(null);
    }

    public static <T> ApiResult<T> success(T data) {
        ApiResult apiResult = withCode(ResultCode.SUCCESS);
        apiResult.setData(data);
        return apiResult;
    }

    public static ApiResult fail(String message) {
        ApiResult apiResult = withCode(ResultCode.FAIL);
        apiResult.setMessage(message);
        return apiResult;
    }

    public static ApiResult withCode(ResultCode resultCode) {
        return withCode(resultCode, null);
    }

    public static <T> ApiResult<T> withCode(ResultCode resultCode, T data) {
        return new ApiResult(resultCode.getCode(), data, resultCode.getMessage());
    }
}

所有的API接口返回类型必须为ApiResult,统一格式方便前端处理。使用如下:

@RestController
public class IndexController {

    @GetMapping("/index")
    public ApiResult<String> index() {
        return ApiResult.success("Hello World");
    }
}

调用成功的返回结果:

{
  "code": 200,
  "data": "Hello World",
  "message": "成功"
}

调用失败的返回结果:

{
  "code": 500,
  "data": null,
  "message": "失败"
}

API统一返回状态码 ResultCode

@Getter
@AllArgsConstructor
public enum ResultCode {

    SUCCESS(200, "成功"),

    FAIL(500, "失败"),

    /* 参数错误:1000 - 1999 */

    /* 用户错误:2000 - 2999 */
    USER_NOT_LOGIN(2001, "用户未登录"),

    /* 接口异常:3000 - 3999 */
    ;

    private Integer code;

    private String message;
}

当前状态码并没有完全,可以在开发功能的同时进行添加,当然在后期还可以做成动态可配置的。但是我们可以提前设计好错误类型的区间范围:

  • 1000 - 1999 区间表示参数错误
  • 2000 - 2999 区间表示用户错误
  • 3000 - 3999 区间表示接口异常

这样前端开发人员根据状态码就大致能确定错误。

实体类基类 Common

@Getter
@Setter
public abstract class Common {

    /**
     * 创建人
     */
    private String createUser;

    /**
     * 创建时间
     */
    private Date createDateTime;

    /**
     * 修改人
     */
    private String modifyUser;

    /**
     * 修改时间
     */
    private Date modifyDateTime;
}

Common是所有实体类的基类,用于设置所有实体类的公共属性。

分页查询

分页查询在项目中是必不可少的,下面设计了分页查询条件基类和分页查询统一返回。

分页查询条件基类 Condition

@Getter
@Setter
public abstract class Condition {

    /**
     * 每页显示条数
     */
    private int numPerPage = 20;

    /**
     * 当前页号
     */
    private int pageNum = 1;

    /**
     * 排序字段
     */
    private String orderField;

    /**
     * 排序方式,默认降序
     */
    private String orderDirection = "desc";
}

分页查询统一返回 QueryResult

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class QueryResult<T> {

    /**
     * 总条数
     */
    private Long total;

    /**
     * 返回结果集
     */
    private List<T> items;
}

控制器基类 BaseController

public abstract class BaseController {

    /**
     * 获得当前用户编号
     */
    public String getCurrentUserCode() {
        //TODO 待实现
        return null;
    }

    /**
     * 设置创建信息
     */
    public void setCreateInfo(Common common) {
        common.setCreateUser(getCurrentUserCode());
        common.setCreateDateTime(new Date());
        setModifyInfo(common);
    }

    /**
     * 设置修改信息
     */
    public void setModifyInfo(Common common) {
        common.setModifyUser(getCurrentUserCode());
        common.setModifyDateTime(new Date());
    }
}

业务异常 BusinessException

在项目中异常的设计尤为重要。 这里约定异常必须是BusinessException或者子类。方便后续对异常进行统一处理。

public class BusinessException extends RuntimeException {

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

总结

至此,一些基本的设计已经完成。不同的业务可能有不同的设计,但好的设计、适用的设计很重要。

源码

github.com/zhuqianchan…

往期回顾