dto.java.ftl 和 vo.java.ftl 两个核心数据传输对象模板文件1

以下是符合 企业级 RESTful API 设计规范、遵循 OpenAPI 3.0 标准 的两个核心数据传输对象模板文件:

  • dto.java.ftl(Data Transfer Object,用于请求入参)
  • vo.java.ftl(View Object,用于响应出参)

这两个模板均包含完整的 OpenAPI 3.0 注解(使用 SpringDoc),并集成 JSR-303 参数校验,附有详尽的中文注释,可直接用于 MyBatis-Plus Generator 或自定义代码生成器。


✅ 1. dto.java.ftl(请求 DTO 模板)

package ${package.DTO};

<#-- 导入 OpenAPI 和校验注解 -->
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;

import javax.validation.constraints.*;
import java.io.Serializable;
<#-- 根据字段类型动态导入时间类 -->
<#list table.fields as field>
<#if field.propertyType == "LocalDateTime" || field.propertyType == "LocalDate">
import java.time.${field.propertyType};
</#if>
</#list>

/**
 * <p>
 * ${table.comment!} 创建/更新请求 DTO
 * </p>
 *
 * <p>
 * 【设计目标】
 * 1. 作为 Controller 层 {@code @RequestBody} 的接收对象
 * 2. 定义 API 请求的契约(字段、校验规则、示例值)
 * 3. 与内部 Entity 解耦,避免暴露数据库结构
 * 4. 集成 JSR-303 参数校验,确保数据合法性
 * </p>
 *
 * <p>
 * 【关键原则】
 * - 仅包含前端需要提交的字段(可少于 Entity)
 * - 字段命名与业务语义一致,不强制与数据库一致
 * - 必须添加 OpenAPI 注解({@code @Schema})生成高质量 API 文档
 * - 必须添加校验注解(如 {@code @NotBlank}、{@code @Email})
 * - 不包含敏感字段(如密码明文、内部状态码等)
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Data
@Accessors(chain = true)
@Schema(description = "${table.comment!} 请求参数")
public class ${entity}DTO implements Serializable {

    private static final long serialVersionUID = 1L;

<#-- 遍历表字段生成属性(排除基类字段) -->
<#list table.fields as field>
<#-- 跳过基类字段(createTime, updateTime, deleted)和主键(DTO 通常不需要传 ID) -->
<#if !(field.name == 'id' || field.name == 'create_time' || field.name == 'update_time' || field.name == 'deleted')>
    /**
     * ${field.comment!}
     */
    @Schema(
        description = "${field.comment!}",
        <#-- 根据字段类型设置示例值 -->
        <#if field.propertyType == "String">
        example = "${field.propertyName}",
        <#elseif field.propertyType == "Integer" || field.propertyType == "Long">
        example = "${field_index + 1}",
        <#elseif field.propertyType == "Boolean">
        example = "true",
        <#elseif field.propertyType == "LocalDateTime">
        example = "2023-01-01T12:00:00",
        <#elseif field.propertyType == "LocalDate">
        example = "2023-01-01",
        </#if>
        requiredMode = Schema.RequiredMode.REQUIRED // 可根据业务调整为 NOT_REQUIRED
    )
    <#-- 添加 JSR-303 校验注解(根据字段类型和业务规则) -->
    <#if field.propertyType == "String">
    @NotBlank(message = "${field.comment!}不能为空")
    @Size(max = 50, message = "${field.comment!}长度不能超过50")
    <#elseif field.propertyType == "Integer" || field.propertyType == "Long">
    @NotNull(message = "${field.comment!}不能为空")
    <#if field.name == "email">
    @Email(message = "邮箱格式不正确")
    </#if>
    <#elseif field.propertyType == "Boolean">
    @NotNull(message = "${field.comment!}不能为空")
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#if>
</#list>

    /**
     * 【扩展建议】以下为常见场景示例(按需取消注释)
     */

    /*
    // 示例:密码字段(需特殊校验)
    @Schema(description = "密码", example = "StrongPass123!")
    @NotBlank(message = "密码不能为空")
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d@$!%*?&]{8,}$",
             message = "密码必须包含大小写字母、数字,且长度至少8位")
    private String password;

    // 示例:确认密码(需与 password 一致)
    @Schema(description = "确认密码", example = "StrongPass123!")
    @NotBlank(message = "确认密码不能为空")
    private String confirmPassword;

    // 示例:枚举字段(前端传 code,后端转 Enum)
    @Schema(description = "用户状态:0-禁用,1-启用", allowableValues = {"0", "1"}, example = "1")
    @NotNull(message = "状态不能为空")
    private Integer status;
    */
}

✅ 2. vo.java.ftl(响应 VO 模板)

package ${package.VO};

<#-- 导入 OpenAPI 注解 -->
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
<#-- 根据字段类型动态导入时间类 -->
<#list table.fields as field>
<#if field.propertyType == "LocalDateTime" || field.propertyType == "LocalDate">
import java.time.${field.propertyType};
</#if>
</#list>

/**
 * <p>
 * ${table.comment!} 响应视图对象(VO)
 * </p>
 *
 * <p>
 * 【设计目标】
 * 1. 作为 Controller 层 {@code @ResponseBody} 的返回对象
 * 2. 定义 API 响应的数据结构和语义
 * 3. 与内部 Entity 解耦,隐藏敏感字段和内部逻辑
 * 4. 通过 OpenAPI 注解生成清晰的响应文档
 * </p>
 *
 * <p>
 * 【关键原则】
 * - 仅包含前端需要的字段(可少于 Entity)
 * - 可包含计算字段(如 formattedCreateTime)
 * - **绝不包含敏感信息**(如密码、密钥、内部 ID 等)
 * - 必须添加 OpenAPI 注解({@code @Schema})
 * - 字段命名应面向前端业务,而非数据库
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Data
@Accessors(chain = true)
@Schema(description = "${table.comment!} 响应数据")
public class ${entity}VO implements Serializable {

    private static final long serialVersionUID = 1L;

<#-- 遍历表字段生成属性(包含主键,排除敏感字段) -->
<#list table.fields as field>
<#-- 跳过敏感字段(如 password, salt)和逻辑删除字段 -->
<#if !(field.name == 'password' || field.name == 'salt' || field.name == 'deleted')>
    /**
     * ${field.comment!}
     */
    @Schema(
        description = "${field.comment!}",
        <#-- 设置示例值 -->
        <#if field.propertyType == "String">
        example = "${field.propertyName}Value",
        <#elseif field.propertyType == "Integer" || field.propertyType == "Long">
        example = "${field_index + 1}",
        <#elseif field.propertyType == "Boolean">
        example = "true",
        <#elseif field.propertyType == "LocalDateTime">
        example = "2023-01-01T12:00:00",
        <#elseif field.propertyType == "LocalDate">
        example = "2023-01-01",
        </#if>
        accessMode = Schema.AccessMode.READ_ONLY // VO 字段通常只读
    )
    private ${field.propertyType} ${field.propertyName};
</#if>
</#list>

    /**
     * 【扩展建议】以下为常见场景示例(按需取消注释)
     */

    /*
    // 示例:格式化时间(前端友好显示)
    @Schema(description = "注册时间(格式化字符串)", example = "2023年01月01日 12:00")
    private String formattedCreateTime;

    // 示例:关联对象(避免 N+1 查询,通常通过 Service 组装)
    @Schema(description = "所属部门信息")
    private DepartmentVO department;

    // 示例:状态文本(将 code 转为可读文本)
    @Schema(description = "用户状态文本", example = "启用")
    private String statusText;
    */
}

🔍 关键设计说明与最佳实践

1. DTO 与 VO 的核心区别
维度DTO(请求)VO(响应)
用途接收前端请求参数返回前端响应数据
字段范围通常不含 ID、创建时间等通常包含 ID、状态等
敏感字段绝不包含密码等绝对禁止包含密码、密钥等
校验必须 JSR-303 校验无需校验
OpenAPI 注解requiredModeexampleaccessMode = READ_ONLY
命名面向操作(如 createUserDTO面向展示(如 UserDetailVO
2. OpenAPI 注解最佳实践
  • @Schema 必加:每个字段都应有描述和示例
  • requiredMode:明确是否必填(DTO 通常为 REQUIRED
  • accessMode = READ_ONLY:VO 字段标记为只读,Swagger UI 会灰显
  • allowableValues:枚举字段指定可选值
3. 安全红线
  • 🔒 Entity 绝不直接用于 API:避免 passworddeleted 等字段泄露
  • 🔒 VO 中显式过滤敏感字段:模板中已排除 passwordsaltdeleted
  • 🔒 DTO 不接收 ID:创建操作不应由前端传 ID(更新操作可单独设计 UpdateDTO
4. 扩展性设计
  • 支持 动态导入时间类(仅当字段使用 LocalDateTime 时才导入)
  • 预留 密码、确认密码、枚举转换 等常见场景示例
  • 支持 计算字段(如 formattedCreateTime

🛠️ 配套使用建议

(1) Controller 层使用示例
@PostMapping
@Operation(summary = "创建用户")
public ApiResponse<UserVO> createUser(@Valid @RequestBody UserCreateDTO dto) {
    User user = userService.create(dto);
    UserVO vo = userMapper.toVO(user); // 使用 MapStruct 转换
    return ApiResponse.success(vo);
}
(2) MapStruct 转换示例(推荐)
@Mapper
public interface UserConvert {
    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
    
    UserVO toVO(User entity);
    User toEntity(UserCreateDTO dto);
}
(3) DTO 分类建议
  • UserCreateDTO:创建用户
  • UserUpdateDTO:更新用户(ID 通常从 PathVariable 获取)
  • UserQuery:查询条件(配合 @ParameterObject

📦 项目结构示例

src/main/java
└── com.example.demo
    ├── dto
    │   ├── user
    │   │   ├── UserCreateDTO.java
    │   │   └── UserUpdateDTO.java
    ├── vo
    │   ├── user
    │   │   └── UserVO.java
    └── entity
        └── User.java

✅ 这两份模板体现了 “API 契约清晰化”“数据安全隔离化” 的现代 API 设计思想:

  • DTO/VO 专注 API 层,与持久层完全解耦
  • OpenAPI 注解全覆盖,生成专业级文档
  • JSR-303 校验保障数据合法性
  • 安全红线明确,杜绝敏感信息泄露

可作为团队标准模板,在代码生成器中直接使用,大幅提升 API 开发效率与质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值