You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
4.5 KiB
93 lines
4.5 KiB
|
4 weeks ago
|
# Controller 出入参两类对象规范(Param / DTO)
|
||
|
|
|
||
|
|
Status: ready-for-agent
|
||
|
|
ADR: [ADR-0017](../../docs/adr/0017-controller-io-param-dto-conventions.md)
|
||
|
|
|
||
|
|
> 入参查询用 `XxxParam`,入参写入和出参都用 `XxxDTO`,Entity 永不出现在 Controller 方法签名上。
|
||
|
|
|
||
|
|
## 一句话规则
|
||
|
|
|
||
|
|
- 写新接口:查询/分页 → 建 `XxxParam extends BaseParam`;写入/保存 → 参数 ≥3 建一个 `XxxDTO`,≤2 保持 `@RequestParam`
|
||
|
|
- 写新出参:建 `XxxDTO`,业务类继承 `BaseDTO` 白拿 `id` / `createTime` / `updateTime`
|
||
|
|
- 想直接返回实体:不行
|
||
|
|
|
||
|
|
## 对象职责
|
||
|
|
|
||
|
|
| 类型 | 包 | 用途 | 继承 | 硬约束 |
|
||
|
|
|---|---|---|---|---|
|
||
|
|
| `XxxParam` | `param/` | 查询/搜索/分页入参 | extends `BaseParam` | 必须经 `PageConverter.toMpPage` 转换 |
|
||
|
|
| `XxxDTO` | `dto/` | 写入参 + 出参(双向) | 业务类 extends `BaseDTO`;统计/登录等无主键语义的不继承 | `fromEntity()` / `toEntity()` 写在 DTO 上,Service 层调用 |
|
||
|
|
| Entity | `domain/entity/` | 表结构映射 | extends `BaseEntity` | **禁止出现在任何 Controller 方法签名中** |
|
||
|
|
|
||
|
|
## 硬约束
|
||
|
|
|
||
|
|
1. Entity 不出现在 Controller 方法签名上(入参出参都禁)
|
||
|
|
2. `deleted` 不出现在任何 HTTP 响应中
|
||
|
|
3. `creatorId` / `updaterId` 不出现在任何 HTTP 响应中
|
||
|
|
|
||
|
|
## Mass assignment 防御
|
||
|
|
|
||
|
|
全局 `@ControllerAdvice` + `@InitBinder`,`dataBinder.setDisallowedFields(...)` strip 掉服务端裁定字段:
|
||
|
|
|
||
|
|
`createTime`、`updateTime`、`creatorId`、`updaterId`、`deleted`、`builtin`
|
||
|
|
|
||
|
|
新增服务端裁定字段时必须同步更新此列表。
|
||
|
|
|
||
|
|
## 封装阈值
|
||
|
|
|
||
|
|
≥3 个参数封装为 `XxxDTO`;≤2 个保持 `@RequestParam`。纯参数个数,不看点参数形状。
|
||
|
|
|
||
|
|
## 命名规则
|
||
|
|
|
||
|
|
`dto/` 包内所有 HTTP 出入参类一律 `XxxDTO` 后缀。内部传输对象(`ButtonSeed`、`PermissionModuleDescriptor`、`UploadSession`)不强制后缀。
|
||
|
|
|
||
|
|
## 绑定方式
|
||
|
|
|
||
|
|
隐式表单绑定(不加 `@RequestBody`),契合项目 `application/x-www-form-urlencoded` 契约。
|
||
|
|
|
||
|
|
## 存量改名清单
|
||
|
|
|
||
|
|
| 原名 | 新名 | 模块 |
|
||
|
|
|---|---|---|
|
||
|
|
| `RoleDetailVO` | `RoleDTO` | crm-auth |
|
||
|
|
| `UserStatsVO` | `UserStatsDTO` | crm-auth |
|
||
|
|
| `DictGroupVO` | `DictGroupDTO` | crm-dict |
|
||
|
|
| `DictItemVO` | `DictItemDTO` | crm-dict |
|
||
|
|
| `ResourceNode` | `ResourceNodeDTO` | crm-auth |
|
||
|
|
|
||
|
|
## 存量违规回填清单(9 处)
|
||
|
|
|
||
|
|
| # | 违规 | 位置 | 改法 |
|
||
|
|
|---|---|---|---|
|
||
|
|
| 1 | 实体直收 | `DictGroupController.saveOrUpdate(DictGroup)` | 改为 `saveOrUpdate(DictGroupDTO)` |
|
||
|
|
| 2 | 实体直收 | `DictItemController.saveOrUpdate(DictItem)` | 改为 `saveOrUpdate(DictItemDTO)` |
|
||
|
|
| 3 | 实体直出 | `RoleController.page` → `Result<PageResult<SysRole>>` | 改为 `Result<PageResult<RoleDTO>>` |
|
||
|
|
| 4 | 实体直出 | `SystemController.menuTree` → `List<SysMenu>` | 改为 `List<ResourceNodeDTO>` |
|
||
|
|
| 5 | 实体直出 | `SystemController.deptTree` → `List<SysDept>` | 改为 `List<DeptDTO>` |
|
||
|
|
| 6 | 实体直出 | `DictGroupController.enabledList` → `List<DictGroup>` | 改为 `List<DictGroupDTO>` |
|
||
|
|
| 7 | 实体直出 | `DictItemController.enabledList` → `List<DictItem>` | 改为 `List<DictItemDTO>` |
|
||
|
|
| 8 | 分页绕过 | `RoleController.page` 自行 `new Page<>` | 改为 `RoleParam extends BaseParam`,经 `PageConverter` |
|
||
|
|
| 9 | `@RequestBody` | `SystemController.userPage` | 去掉 `@RequestBody`,改表单绑定 |
|
||
|
|
|
||
|
|
## 前置条件
|
||
|
|
|
||
|
|
`BaseDTO` 必须先重构(当前仍是未改造版本):
|
||
|
|
- 改为 `abstract` 类
|
||
|
|
- 只保留 `id`、`createTime`、`updateTime`
|
||
|
|
- 三字段加 `@JsonInclude(NON_NULL)`
|
||
|
|
- 移除 `creatorId`、`updaterId`、`deleted` 及默认值
|
||
|
|
- 移除泛型 `toEntity(Supplier)` 方法
|
||
|
|
|
||
|
|
## 术语表
|
||
|
|
|
||
|
|
| 术语 | 定义 |
|
||
|
|
|---|---|
|
||
|
|
| **Param** (`XxxParam`) | 查询/搜索/分页入参对象,放 `param/` 包,继承 `BaseParam`,经 `PageConverter` 转换 |
|
||
|
|
| **DTO** (`XxxDTO`) | 双向传输对象,既是写入参又是出参,放 `dto/` 包;业务类继承 `BaseDTO` |
|
||
|
|
| **BaseParam** | 分页参数基类,提供 `current` / `size` / `asc` / `desc` + `PageConverter` 转换 |
|
||
|
|
| **BaseDTO** | 出参基类(abstract),提供 `id` / `createTime` / `updateTime` + `@JsonInclude(NON_NULL)` |
|
||
|
|
| **InitBinder 防护** | 全局 `@InitBinder` + `setDisallowedFields`,strip 服务端裁定字段防 mass assignment |
|
||
|
|
| **PageConverter** | 分页转换器,页大小上限收敛 + 排序字段白名单校验 |
|
||
|
|
| **封装阈值** | ≥3 个参数封 `XxxDTO`,≤2 保持 `@RequestParam` |
|
||
|
|
| **Route A** | 同一个 DTO 类同时做写入参和出参(用户已接受双契约字段代价) |
|