「DTO / DO / VO 最小设计规范」

attachments-2026-02-Cpdi8dJX698058f6dd04a,png

③ DO 永远不暴露给前端

哪怕字段一模一样,也要用 VO

④ DTO 只关心「查询 / 命令」

VO 只关心「展示」

⑤ DTO / VO 不要求和 DO 字段完全一致

一致只是巧合,不是约定

二、DO(Data Object)最小规范

一张表 = 一个 DO

1️⃣ DO 的核心职责

  • 数据库表结构映射

  • ORM 操作唯一入口

2️⃣ DO 必须包含的内容

@TableName("user_integral") public class UserIntegralDO {

@TableId(type = IdType.AUTO) private Long id;

private Long pntCustId;

private BigDecimal integralBalance;

private Integer isDeleted;

private LocalDateTime createTime; }

3️⃣ DO 允许 / 禁止项

允许

  • MyBatis-Plus 注解

  • Getter / Setter

  • Lombok(@Data)

禁止

  • @NotNull / @Valid

  • 前端展示字段

  • 构造复杂业务逻辑

  • 引用 DTO / VO

4️⃣ DO 命名规范


表名:user_integral DO: UserIntegralDO

三、DTO(Data Transfer Object)最小规范

一个接口 ≈ 一个 DTO

1️⃣ DTO 的核心职责

  • 接收请求参数

  • 参数校验

  • 查询条件封装

2️⃣ DTO 最小结构(查询类)

public class UserIntegralQueryDTO {

@NotNull(message = "客户ID不能为空") private Long pntCustId;

private Integer operateType;

private LocalDateTime beginOperateTime; private LocalDateTime endOperateTime;

// 分页参数(推荐统一继承) private Integer pageNum = 1; private Integer pageSize = 10;

public Page toPage() { return new Page(pageNum, pageSize); } }

3️⃣ DTO 允许 / 禁止项

允许

  • @NotNull、@Valid

  • 默认值

  • 分页辅助方法

禁止

  • @TableName

  • @TableField

  • 任何数据库字段强绑定

  • Service / Mapper 调用

    attachments-2026-02-L3gy5Szh698059a318838,png

    四、VO(View Object)最小规范

    VO = 前端真正关心的数据结构

    1️⃣ VO 的核心职责

    • 聚合多个 DO

    • 返回给前端

    • 屏蔽数据库细节

      2️⃣ VO 最小结构(你现在这个例子)

      public class UserIntegralVo {

      private UserIntegralDO userIntegral;

      private Page<UserIntegralDetailDO> userIntegralDetailPage;

      private BigDecimal waitSettleIntegral;

      private IntegralProductConfigDO integralProductConfig; }

      注意
      这里返回 DO 是「业务妥协」,不是推荐做法
      理想情况应拆成 VO

      3️⃣ 更规范一点的 VO(推荐)

      public class UserIntegralVO {

      private BigDecimal integralBalance;

      private BigDecimal waitSettleIntegral;

      private List<UserIntegralDetailVO> details; }

      4️⃣ VO 允许 / 禁止项

      允许

      • 字段重命名(可读性)

      • BigDecimal / String

      • 枚举翻译后的值

      禁止

      • @TableField

      • ORM 注解

      • Mapper 调用

      五、Service 层最小规范(关键)

      Service 是 DTO / DO / VO 的“交通警察”

      正确姿势(你现在的代码已经 80% 正确)

      public UserIntegralVo selectUserIntegral2(UserIntegralQueryDTO dto) {

      UserIntegralDO userIntegralDO = iUserIntegralService.getOne( Wrappers.<UserIntegralDO>lambdaQuery() .eq(dto.getPntCustId() != null, UserIntegralDO::getPntCustId, dto.getPntCustId()) .eq(UserIntegralDO::getIsDeleted, 0) );

      if (userIntegralDO == null) { return null; }

      UserIntegralVo vo = new UserIntegralVo(); vo.setUserIntegralDO(userIntegralDO); vo.setWaitSettleIntegral(...);

      return vo; }

      ❌ Service 层禁忌

      mapper.selectById(dto.getId()); // ❌ DTO 直查 return userIntegralDO; // ❌ DO 直返

      六、MyBatis-Plus Wrapper 使用规范(强烈推荐)

      统一写法(你现在这样写是正确的)

      .eq(dto.getPntCustId() != null, UserIntegralDO::getPntCustId, dto.getPntCustId())

      永远不要写:

      .eq(UserIntegralDO::getPntCustId, dto.getPntCustId()) // ❌

      七、最小落地目录结构(推荐)


      user ├── controller │ └── UserIntegralController.java ├── service │ └── UserIntegralService.java ├── mapper │ └── UserIntegralMapper.java ├── dto │ └── UserIntegralQueryDTO.java ├── vo │ └── UserIntegralVO.java ├── entity │ └── UserIntegralDO.java

      八、一句话记忆版(给你压缩成“肌肉记忆”)

      DTO 管进,DO 管库,VO 管出;
      DTO 不碰表,VO 不查库,DO 不接参;
      Wrapper 永远三参数,Service 负责翻译。

请先 登录 后评论
  • 0 关注
  • 0 收藏,22 浏览
  • shitian 提出于 2026-02-02 15:56

相似问题