hl-api-changelog/changelogs-v2/2026-05/21_2812_司机详情attachments强类型化-修改接口-管理后台.md
yaosutu ab2aaef3f5 修正 司机详情 attachments 字段类型 Map -> 强类型 DriverAttachmentsVO (#2812 / PR #2818)
前序 21_2791_司机档案 changelog §3.2 描述的 Map<String,List<>> 已废,以本文档为准。
前端需要把 data.attachments['id_card'] 改成 data.attachments.idCard。
2026-05-21 15:52:46 +08:00

198 行
7.8 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

# 【修改接口·管理后台】司机详情 attachments 弱类型 Map 改强类型 DriverAttachmentsVO (#2812)
> **PR**: #2818 | **服务**: hl-fleet-service | **更新时间**: 2026-05-21 15:30
>
> **存放目录**: `changelogs-v2/2026-05/`
> **关联前序**: [`21_2791_司机档案-修改接口-管理后台.md`](./21_2791_司机档案-修改接口-管理后台.md)commit `761ca07`
---
## ⚠️ 关键变化30 秒速读)
**`GET /admin/fleet/drivers/{driverId}` 的响应字段 `attachments` 类型变了**
| 项 | 改前 | 改后 |
|---|---|---|
| 类型 | `Map<string, DriverAttachmentRespVO[]>`(动态 key | `DriverAttachmentsVO`8 个固定字段) |
| 前端用法 | `data.attachments['id_card']` 字符串访问 | `data.attachments.idCard` 字段访问 |
| TypeScript 类型 | 没有具体字段名 | 8 个具体字段(含类型提示) |
**前序 changelog 废止内容**[`21_2791_司机档案-修改接口-管理后台.md`](./21_2791_司机档案-修改接口-管理后台.md) **§3.2 详情接口** + **§9 业务边界** 中所有把 attachments 描述为 `Map<String, List<>>` 的部分**已废**,以本文档为准。
**前端必须改动**(不是兼容的):所有 `data.attachments[key]` 形式的访问要改成 `data.attachments.xxx` 字段访问。
---
## 1. 接口背景
`attachments` 当前用 `Map<String, List<DriverAttachmentRespVO>>` 返回违反 HL 项目规则 [禁止弱类型返回值]
- Swagger / TypeScript 生成的类型是 `{ [key: string]: DriverAttachmentRespVO[] }`,**没有具体字段名**
- 前端 IDE 不能自动补全
- 拼错 key 编译期不报错
- 文档无法自包含描述每个类目出参
改为强类型 `DriverAttachmentsVO`,8 个 List 字段对应 8 个固定类目(与 `DriverAttachmentCategoryEnum` 严格对齐)。
---
## 2. 变更清单
| # | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|----------|------|
| 1 | GET | `/admin/fleet/drivers/{driverId}` | **修改** | 响应字段 `attachments` 类型变化(详见 §3.1 |
仅 1 个端点受影响。其余 5 个司机档案接口page / create / update / delete / import**不受影响**。
---
## 3. 接口详情
### 3.1 司机详情 — `attachments` 字段类型变化
**GET** `/admin/fleet/drivers/{driverId}`
#### 改后的 `attachments` 字段结构
字段:`DriverAttachmentsVO` 对象(**不再是 Map**)。
| 字段 | 类型 | 默认 | 张数上限(来自 DriverAttachmentCategoryEnum |
|---|---|---|---|
| `idCard` | `DriverAttachmentRespVO[]` | `[]` | 2"换证留历史":支持正反 2 张或新旧 2 套) |
| `driverLicense` | `DriverAttachmentRespVO[]` | `[]` | 2换证留历史 |
| `portrait` | `DriverAttachmentRespVO[]` | `[]` | 2换证留历史 |
| `healthCert` | `DriverAttachmentRespVO[]` | `[]` | 2 |
| `trainingCert` | `DriverAttachmentRespVO[]` | `[]` | 5 |
| `award` | `DriverAttachmentRespVO[]` | `[]` | 5 |
| `qualification` | `DriverAttachmentRespVO[]` | `[]` | 3 |
| `other` | `DriverAttachmentRespVO[]` | `[]` | 5 |
> **每个字段默认空数组**(不为 null,前端**无需判空**,直接 `.map()` 渲染即可。
`DriverAttachmentRespVO[]` 单条项字段(**不变**
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | string | 附件 IDLong 序列化为 String |
| `driverId` | string | 所属司机 ID |
| `category` | string | 类目 |
| `sortNo` | int | 同类目排序0-based |
| `url` | string | OSS URL |
| `mimeType` | string | 例 `image/jpeg` |
#### 改后响应示例(节选)
```json
{
"code": 200,
"data": {
"id": "1234567890123456789",
"name": "张三",
"...其他字段保持不变...": "...",
"attachments": {
"idCard": [
{ "id": "...", "driverId": "...", "category": "id_card", "sortNo": 0, "url": "https://oss/.../id-front.jpg", "mimeType": "image/jpeg" },
{ "id": "...", "driverId": "...", "category": "id_card", "sortNo": 1, "url": "https://oss/.../id-back.jpg", "mimeType": "image/jpeg" }
],
"driverLicense": [
{ "id": "...", "driverId": "...", "category": "driver_license", "sortNo": 0, "url": "https://oss/.../license.jpg", "mimeType": "image/jpeg" }
],
"portrait": [],
"healthCert": [],
"trainingCert": [],
"award": [],
"qualification": [],
"other": []
},
"relatedOrders": [...]
},
"msg": "成功"
}
```
---
## 6. 枚举 / 数据字典
无新增枚举。`category` 字段语义和取值与前序 changelog [`21_2791_司机档案-修改接口-管理后台.md`](./21_2791_司机档案-修改接口-管理后台.md) §6.1 一致8 个值),唯一差异是 **API 响应不再以 category 字符串作为 key**,而是用 VO 字段名(驼峰)。
类目 ↔ VO 字段对应关系:
| DB / category 字段值 | VO 字段(驼峰) |
|---|---|
| `id_card` | `idCard` |
| `driver_license` | `driverLicense` |
| `portrait` | `portrait` |
| `health_cert` | `healthCert` |
| `training_cert` | `trainingCert` |
| `award` | `award` |
| `qualification` | `qualification` |
| `other` | `other` |
注意单条 `DriverAttachmentRespVO.category` 仍然是 `id_card` / `driver_license` 等下划线值(不变)。改的是**外层分组的 key 形式**。
---
## 7. 错误码
无变化。
---
## 10. 修改前后对比
### 10.1 字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
| `attachments` 类型 | `Map<string, DriverAttachmentRespVO[]>` | `DriverAttachmentsVO`8 个固定 List 字段) |
| `attachments` key 形式 | DB 下划线值(`id_card` / `driver_license` / `portrait` / ... | VO 驼峰字段(`idCard` / `driverLicense` / `portrait` / ... |
| 类目缺失 | 该 key 不在 Map 里 | 该字段是空数组 `[]` |
### 10.2 前端代码对照
```typescript
// 改前
const idCardAttachments = data.attachments['id_card'] ?? [];
const licenseAttachments = data.attachments['driver_license'] ?? [];
// 改后
const idCardAttachments = data.attachments.idCard; // 不用判空
const licenseAttachments = data.attachments.driverLicense;
```
---
## 11. 影响评估
- **是否破坏向后兼容**:⚠️ **是**(字段类型变了,前端必须改)
- **前端是否必须同步上线****是**(老前端按 Map 解析会拿到 undefined
- **影响其他后端服务**DriverDetailRespVO 仅 admin 接口返回,不走 Feign
- **影响已有数据**:无(仅 API 序列化形式变化,DB 无变)
---
## 12. 注意事项
- **前端 workaround 清理点**
- 改前如果前端有 `const map = data.attachments; const idCard = map['id_card'] ?? [];` 之类 workaround,**改成 `data.attachments.idCard`** 即可
- 改前如果前端硬编码遍历类目(如 `for (const key of ['id_card', 'driver_license'])`),**改成直接读 VO 字段**
- 改前如果前端有 `if (map['portrait'])` 判空逻辑,**直接删** —— 强类型 VO 字段必非 null默认空数组
- **前序 changelog 描述废止**[`21_2791_司机档案-修改接口-管理后台.md`](./21_2791_司机档案-修改接口-管理后台.md) 已发出的 attachments Map 描述以本文档为准,前序 changelog **不会修改**changelog 只追加不回写历史)
---
## 13. 关联 / 联系人
### 13.1 链接
- **Issue**: [#2812](https://git.1814.love:8443/wx/HL/issues/2812)
- **PR**: [#2818](https://git.1814.love:8443/wx/HL/pulls/2818)
- **Merge commit**: [`8c340550`](https://git.1814.love:8443/wx/HL/commit/8c340550656bdee3ee2243b98d694b818474aeba)
- **前序 changelogattachments Map 描述废)**: [`21_2791_司机档案-修改接口-管理后台.md`](./21_2791_司机档案-修改接口-管理后台.md)
- **规则**: HL `MEMORY.md``feedback_no-map-return-type.md` (禁止弱类型返回值)
### 13.2 联系人
- **后端负责人**: @yst(腰苏图)
- **前端对接(管理后台)**: 待指派