以下是符合 企业级 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 注解 | requiredMode、example | accessMode = READ_ONLY |
| 命名 | 面向操作(如 createUserDTO) | 面向展示(如 UserDetailVO) |
2. OpenAPI 注解最佳实践
- ✅
@Schema必加:每个字段都应有描述和示例 - ✅
requiredMode:明确是否必填(DTO 通常为REQUIRED) - ✅
accessMode = READ_ONLY:VO 字段标记为只读,Swagger UI 会灰显 - ✅
allowableValues:枚举字段指定可选值
3. 安全红线
- 🔒 Entity 绝不直接用于 API:避免
password、deleted等字段泄露 - 🔒 VO 中显式过滤敏感字段:模板中已排除
password、salt、deleted - 🔒 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 开发效率与质量。
257

被折叠的 条评论
为什么被折叠?



