后台业务系统通用开发规范

适用于 Spring Boot、Quarkus、Solon 等 Java 后台业务系统。
本文重点规范接口对象设计、数据访问方式、更新策略、命名约定和长期维护原则。


1. 背景

在后台业务系统中,很多数据并不是一次性完整写入并完整更新的。

更常见的业务过程是:

  1. 创建时只插入部分基础字段。
  2. 审核流程更新审核字段。
  3. 状态流转更新状态字段。
  4. 支付流程更新支付字段。
  5. 回调流程更新回调字段。
  6. 人工处理或补偿任务更新各自负责的字段。

也就是说,一条业务数据往往会被多个业务动作逐步补全。
因此,后台业务系统不适合默认采用“对象整体保存”的开发方式,而应优先采用“部分字段插入 + 指定字段更新”的方式。

本文用于统一后台业务系统中的接口对象、数据更新、命名和维护规范,降低长期维护成本,避免误更新、字段覆盖和对象复用混乱。


2. 核心原则

2.1 一个业务动作对应一个 Req

每个接口入参对象应当对应一个明确的业务动作。

例如:

CreateOrderReq
UpdateOrderNameReq
AuditOrderReq
UpdateOrderStatusReq
CallbackOrderReq

不推荐使用一个大对象承载多个业务动作的所有字段。

错误示例:

OrderReq
OrderDTO
OrderVO

如果一个对象中同时包含创建、编辑、审核、支付、回调等字段,后续维护者很难判断:

  1. 当前接口到底使用哪些字段。
  2. 哪些字段是前端允许传入的。
  3. 哪些字段是后端内部维护的。
  4. 空值表示“不修改”还是“清空”。
  5. 当前接口是否会误更新其他业务字段。

2.2 Req 只包含当前动作允许提交的字段

Req 不是 Entity 的子集复制品,而是某个业务动作的输入契约。

例如,修改名称接口只允许提交名称相关字段:

public class UpdateOrderNameReq {

    private Long id;

    private String name;
}

审核接口只允许提交审核相关字段:

public class AuditOrderReq {

    private Long id;

    private Integer auditResult;

    private String auditRemark;
}

不应该为了复用而把所有字段都塞进一个大对象中。


2.3 Resp 只包含当前接口需要返回的字段

响应对象也应按接口场景拆分。

例如:

OrderListResp
OrderDetailResp
OrderAuditDetailResp

列表接口和详情接口不要无脑复用同一个大对象。

列表接口通常只需要返回摘要字段:

public class OrderListResp {

    private Long id;

    private String orderNo;

    private Integer status;

    private BigDecimal amount;

    private LocalDateTime createTime;
}

详情接口可以返回更完整的信息:

public class OrderDetailResp {

    private Long id;

    private String orderNo;

    private Integer status;

    private BigDecimal amount;

    private String auditRemark;

    private LocalDateTime auditTime;

    private LocalDateTime createTime;
}

这样可以减少接口字段污染,也能降低前端和后端对无关字段的依赖。


2.4 创建时部分字段 insert,后续指定字段 update

后台业务系统中,创建数据时通常只插入基础字段。

示例:

insert into order_table (
    merchant_id,
    order_no,
    amount,
    status,
    create_time
) values (
    ?, ?, ?, ?, now()
);

后续不同业务动作只更新自己负责的字段。

修改名称:

update order_table
set name = ?
where id = ?;

审核:

update order_table
set status = ?,
    audit_remark = ?,
    audit_time = now()
where id = ?;

支付:

update order_table
set pay_status = ?,
    pay_time = now(),
    pay_no = ?
where id = ?;

回调:

update order_table
set callback_status = ?,
    callback_result = ?,
    callback_time = now()
where id = ?;

2.5 默认禁止对象整体保存

不推荐默认使用如下模式:

Entity entity = repository.findById(id);
BeanUtils.copyProperties(req, entity);
repository.save(entity);

也不推荐:

repository.persist(entity);

或者类似的对象整体保存方式作为复杂业务的默认更新方式。

原因是对象整体保存容易覆盖其他业务流程已经修改过的字段。

典型问题:

  1. A 用户编辑名称。
  2. B 用户处理状态。
  3. A 和 B 都基于旧对象保存。
  4. 后保存的一方可能覆盖前一方已经修改的字段。

业务真实期望通常是:

update order_table set name = ? where id = ?;

和:

update order_table set status = ? where id = ?;

两个动作互不影响。

因此,更新操作默认应采用指定字段更新。


2.6 状态流转使用条件更新

状态流转类操作不应只按 id 更新,建议带上当前状态条件。

推荐写法:

update order_table
set status = ?,
    audit_time = now()
where id = ?
  and status = ?;

然后判断影响行数。

影响行数 = 1:更新成功
影响行数 = 0:数据不存在、状态已变化、重复操作或并发冲突

这样可以避免重复审核、重复处理、状态回退和并发覆盖问题。


3. 接口对象命名规范

3.1 请求对象统一使用 Req 后缀

请求对象表示前端或外部系统传入后端的数据。

推荐命名:

CreateOrderReq
UpdateOrderNameReq
AuditOrderReq
OrderPageQueryReq
CallbackOrderReq

不推荐命名:

OrderDTO
OrderBO
OrderVO
OrderParam
OrderModel

原因是 DTOBOVOModel 都不能明确表达数据方向。

看到 OrderDTO 时,很难判断它是:

  1. 前端请求对象。
  2. 后端响应对象。
  3. Service 内部传递对象。
  4. 数据库查询结果对象。
  5. 第三方接口对象。

3.2 响应对象统一使用 Resp 后缀

响应对象表示系统返回给前端或外部调用方的数据。

推荐命名:

OrderListResp
OrderDetailResp
OrderCreateResp
OrderAuditResp

Resp 的方向非常明确:只用于返回。

3.3 查询请求使用 QueryReq 或 PageQueryReq

查询类请求建议单独命名。

普通查询:

OrderQueryReq

分页查询:

OrderPageQueryReq

不建议把查询字段放入创建或编辑请求中。

3.4 禁止 Entity 直接作为接口入参或出参

Entity 是数据库持久化对象,不应直接暴露到接口层。

禁止:

public R<OrderEntity> detail(Long id)

禁止:

public R<Void> create(OrderEntity entity)

原因:

  1. Entity 字段通常多于接口需要。
  2. Entity 可能包含内部字段、敏感字段、状态字段。
  3. Entity 变更会影响接口契约。
  4. 前端可能传入不允许修改的字段。
  5. 数据库结构不应直接暴露给接口调用方。

推荐:

public R<OrderDetailResp> detail(Long id)
public R<Void> create(CreateOrderReq req)

4. Req 拆分规范

4.1 创建请求

创建请求只包含创建时允许提交的字段。

示例:

public class CreateOrderReq {

    private Long merchantId;

    private BigDecimal amount;

    private String remark;
}

不应包含审核字段、支付字段、回调字段、系统内部状态字段。


4.2 修改请求

修改请求应按具体动作拆分,而不是使用一个通用 UpdateReq。

推荐:

UpdateOrderNameReq
UpdateOrderRemarkReq
UpdateOrderAmountReq

而不是:

UpdateOrderReq

如果确实是一个“编辑基础信息”接口,可以使用:

UpdateOrderBaseInfoReq

但其中也只应包含该接口允许编辑的字段。


4.3 审核请求

审核请求只包含审核动作需要的字段。

public class AuditOrderReq {

    private Long id;

    private Integer auditResult;

    private String auditRemark;
}

对应 SQL 只更新审核字段和状态字段。

update order_table
set status = ?,
    audit_result = ?,
    audit_remark = ?,
    audit_time = now()
where id = ?
  and status = ?;

4.4 状态流转请求

状态流转请求应包含当前状态和目标状态,或者包含能判断状态流转合法性的字段。

public class UpdateOrderStatusReq {

    private Long id;

    private Integer fromStatus;

    private Integer toStatus;
}

对应 SQL:

update order_table
set status = ?
where id = ?
  and status = ?;

不建议直接:

update order_table
set status = ?
where id = ?;

4.5 回调请求

外部回调请求应单独定义,不能复用内部业务 Req。

public class CallbackOrderReq {

    private String orderNo;

    private String callbackStatus;

    private String callbackResult;

    private String sign;
}

回调接口要特别注意:

  1. 签名校验。
  2. 幂等处理。
  3. 状态条件更新。
  4. 原始报文记录。
  5. 异常返回格式。

5. Resp 拆分规范

5.1 列表响应

列表响应只返回列表展示需要的字段。

public class OrderListResp {

    private Long id;

    private String orderNo;

    private BigDecimal amount;

    private Integer status;

    private LocalDateTime createTime;
}

不要为了复用详情对象而返回大量无关字段。


5.2 详情响应

详情响应可以返回更多字段,但也不应无脑返回 Entity 全字段。

public class OrderDetailResp {

    private Long id;

    private String orderNo;

    private BigDecimal amount;

    private Integer status;

    private String auditRemark;

    private LocalDateTime auditTime;

    private LocalDateTime createTime;
}

如果不同角色看到的详情不同,可以继续拆分:

AdminOrderDetailResp
MerchantOrderDetailResp

5.3 创建响应

如果创建后只需要返回 id,可以单独定义:

public class CreateOrderResp {

    private Long id;
}

不要为了返回一个 id,就返回完整 Entity。


6. 数据访问规范

6.1 主方案:Repository / Mapper 风格

后台业务系统建议优先采用 Repository / Mapper 风格。

优点:

  1. SQL 可控。
  2. 更新字段明确。
  3. 条件更新方便。
  4. 批量更新方便。
  5. 复杂查询容易落地。
  6. 更适合后台业务流程式数据修改。

长期方向可以采用 MyBatis、MyBatis-Plus 或类似能力。


6.2 JPA/Panache 使用边界

JPA/Panache 不作为复杂后台业务的主数据访问方案。

它可以用于:

  1. 简单表。
  2. 只读查询。
  3. 配置类小表。
  4. Demo 或快速原型。
  5. 无并发修改风险的数据。

不建议用于:

  1. 多人并发编辑。
  2. 后台审核状态流转。
  3. 只更新部分字段。
  4. 复杂 SQL。
  5. 批量更新。
  6. 需要精确控制 SQL 的核心业务表。

原因是 JPA/Panache 更偏向对象状态管理和整体持久化,而后台业务系统更常见的是字段级更新。


6.3 指定字段更新

更新操作默认只更新当前业务动作负责的字段。

推荐:

update order_table
set name = ?
where id = ?;

不推荐:

OrderEntity entity = findById(id);
entity.setName(req.getName());
save(entity);

如果使用框架自动更新,也必须确认最终 SQL 只更新允许修改的字段。


6.4 动态更新要限制字段范围

动态更新不是“前端传什么就更新什么”。

正确原则是:

当前业务动作允许哪些字段,后端只在这些字段范围内做动态更新。

例如 UpdateOrderNameReq 只有 name 字段,那么这个接口最多只能更新 name

不能因为前端额外传了 statuspayStatusauditResult,后端就跟着更新。


6.5 状态流转必须检查影响行数

状态流转类 SQL 必须检查影响行数。

示例:

update order_table
set status = 2
where id = ?
  and status = 1;

处理逻辑:

影响行数为 1:状态流转成功。
影响行数为 0:状态已变化或数据不存在,应返回业务失败。

这可以避免重复处理、并发处理和状态错乱。


6.6 批量更新

批量更新应明确更新范围和更新字段。

推荐:

update order_table
set sync_status = ?
where id in (?, ?, ?);

或者:

update order_table
set expire_status = ?
where status = ?
  and expire_time < now();

批量更新尤其要避免无条件更新。

禁止:

update order_table
set status = ?;

除非有非常明确的维护脚本场景,并且经过确认。


7. 参数校验规范

7.1 Req 中添加必要校验

请求对象中应添加必要的参数校验。

示例:

public class AuditOrderReq {

    @NotNull(message = "ID不能为空")
    private Long id;

    @NotNull(message = "审核结果不能为空")
    private Integer auditResult;

    private String auditRemark;
}

7.2 不同动作使用不同校验规则

不同业务动作不要共用一个大对象后再通过复杂分组校验解决。

如果创建、编辑、审核需要的字段不同,优先拆成不同 Req。

推荐:

CreateOrderReq
AuditOrderReq

不推荐:

OrderReq + ValidationGroup

分组校验可以使用,但不应成为大对象复用的借口。


8. 统一返回规范

接口返回建议统一使用统一结构。

示例:

public class R<T> {

    private Integer code;

    private String msg;

    private T data;
}

常见返回:

R<OrderDetailResp>
R<List<OrderListResp>>
R<PageResp<OrderListResp>>
R<Void>

分页响应建议统一:

public class PageResp<T> {

    private Long total;

    private List<T> records;
}

也可以根据前端习惯使用:

total
rows

但整个系统应保持一致。


9. LocalDateTime 规范

9.1 接口返回格式

接口返回时间建议统一为:

yyyy-MM-dd HH:mm:ss

避免同一系统中出现多种时间格式。

9.2 数据库字段

常见字段建议统一命名:

create_time
update_time
audit_time
pay_time
callback_time
finish_time

Java 类型优先使用:

LocalDateTime

9.3 时区

如果系统涉及跨时区业务,需要单独制定时区规范。
如果只服务单一时区业务,也应明确数据库、应用、日志的时区设置,避免排查问题时混乱。


10. 异常处理规范

系统应统一异常返回格式。

需要统一处理:

  1. 业务异常。
  2. 参数校验异常。
  3. 数据不存在异常。
  4. 状态不允许异常。
  5. 数据库异常。
  6. 认证授权异常。
  7. 未知系统异常。

业务异常示例:

throw new BizException("当前状态不允许审核");

返回示例:

{
  "code": -1,
  "msg": "当前状态不允许审核",
  "data": null
}

内部日志应记录完整异常堆栈,但返回前端的信息不应暴露数据库、SQL、堆栈等敏感细节。


11. 命名规范总结

11.1 推荐后缀

后缀 含义
Req 请求入参
Resp 响应结果
Entity 数据库实体
Repository 数据访问封装
Mapper SQL 映射
Service 业务服务
Controller / Resource 接口入口
Client 外部服务客户端
Config 配置类
Properties 配置属性类
Exception 异常类

11.2 谨慎使用的后缀

后缀 问题
DTO 数据方向不明确
BO 边界不清,容易变成杂物对象
VO 不同团队理解不一致
Model 过于泛化
Param 可以用,但不如 Req 统一
Form 偏页面表单,不适合所有接口

11.3 基本要求

接口层对象优先使用 Req / Resp,避免使用 DTO / BO / VO 作为通用命名。


12. 推荐调用链路

推荐结构:

前端
  ↓
Controller / Resource
  ↓
Req
  ↓
Service
  ↓
Repository / Mapper
  ↓
Entity / Table

返回结构:

Entity / QueryResult
  ↓
Service 组装
  ↓
Resp
  ↓
Controller / Resource
  ↓
前端

原则:

  1. Req 只向内流动。
  2. Resp 只向外返回。
  3. Entity 不直接暴露给前端。
  4. Repository / Mapper 不接收前端大对象。
  5. Service 负责业务编排。
  6. SQL 只更新当前动作负责的字段。

13. 最终检查清单

分类 检查项 要求
接口对象 请求对象是否以 Req 结尾 必须
接口对象 响应对象是否以 Resp 结尾 必须
接口对象 是否一个业务动作一个 Req 必须
接口对象 是否存在大 Req 多接口复用 禁止
接口对象 Entity 是否直接作为入参 禁止
接口对象 Entity 是否直接作为出参 禁止
数据访问 是否采用 Repository / Mapper 风格 推荐
数据访问 是否默认指定字段更新 必须
数据访问 是否存在对象整体保存覆盖风险 必须检查
数据访问 状态流转是否带当前状态条件 必须
数据访问 状态流转是否检查影响行数 必须
数据访问 批量更新是否有明确条件 必须
数据访问 动态更新是否限制字段范围 必须
校验 Req 是否添加必要参数校验 必须
返回 是否统一返回 R 推荐
返回 分页结构是否统一 必须
时间 LocalDateTime 返回格式是否统一 必须
异常 业务异常是否统一处理 必须
异常 参数校验异常是否统一处理 必须
命名 是否避免 DTO/BO/VO 混用 推荐
维护 代码是否能从方法名和 Req 名看出业务意图 必须

14. 结论

后台业务系统的长期维护重点不是少写几个类,而是降低理解成本和误修改风险。

因此,本规范强调:

  1. 一个业务动作一个 Req。
  2. Req / Resp 明确数据方向。
  3. Entity 不直接暴露。
  4. 创建时部分字段 insert。
  5. 后续默认指定字段 update。
  6. 状态流转使用条件更新。
  7. Repository / Mapper 风格优先。
  8. 禁止一个大对象在多个业务动作中复用。
  9. 禁止对象整体保存导致无关字段被覆盖。

宁可多建几个小对象,也不要复用一个职责不清的大对象。

代码应当让维护者一眼看出:

这个接口做什么;
允许前端传什么;
后端会更新哪些字段;
不会影响哪些字段。

这比短期少写几个类更重要。

作者:张三  创建时间:2026-06-10 11:30
最后编辑:张三  更新时间:2026-06-10 11:30