# 【修改接口·管理后台】车辆档案 6 接口(全模块首次推送 changelog) > **PR**: 无(本次为首次推送,实质接口契约未变更,配合 §1 §3 模块 changelog 一并补齐 fleet 全档案契约) > **服务**: hl-fleet-service | **更新时间**: 2026-05-21 > > **存放目录**: `changelogs-v2/2026-05/` > **影响范围**: 管理后台「车管 / 车辆档案」页(列表 + 详情 + 新增 + 编辑 + 软删 + Excel 批量导入) --- ## ⚠️ 关键说明 车辆档案模块 6 接口自 fleet 服务首次落地(PR #2724)以来契约**未变化**,本次仅作**首次完整 changelog 推送**,让前端 v3 项目仓库一次拿全 6 接口契约。 **与车型管理库(§1 changelog)配套**:车辆档案的 `vehicleModelId` 引用车型管理库的型号 ID,前端可在新增车辆页用 §1.5 / §1.7 拉型号下拉。 --- ## 1. 接口背景 车辆档案是车队所有车辆的主数据:车牌 / 车型 / VIN / 行驶证 / 保险 / 年检 / 多类目附件(外观/内饰/保险/年检/营运证/其他 6 类)。 前端"车管 / 车辆档案"页面提供: - 列表筛选(按车牌、车队、车型大类、状态、保险/年检到期日) - 详情查看(含附件按类目分组) - 新增 / 编辑(含附件 diff 语义批量提交) - 软删(业务流水模块上线后会按"未完成派单"拒删) - Excel 批量导入 --- ## 2. 变更清单 | # | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|----------|------| | 1 | GET | `/admin/fleet/vehicles/page` | 首推 | 分页列表(多条件过滤) | | 2 | GET | `/admin/fleet/vehicles/{vehicleId}` | 首推 | 详情(含附件) | | 3 | POST | `/admin/fleet/vehicles` | 首推 | 新增(含附件批量 INSERT) | | 4 | PUT | `/admin/fleet/vehicles/{vehicleId}` | 首推 | 编辑(附件 diff 语义) | | 5 | DELETE | `/admin/fleet/vehicles/{vehicleId}` | 首推 | 软删 | | 6 | POST | `/admin/fleet/vehicles/import` | 首推 | Excel 批量导入 | --- ## 3. 接口详情 ### 3.1 分页查询 **GET** `/admin/fleet/vehicles/page` - **使用场景**:车管车辆档案列表页 - **认证**:管理后台 JWT - **幂等**:是(只读) **Query 入参**: | 字段 | 类型 | 必填 | 默认 | 校验 | 说明 | |---|---|---|---|---|---| | `page` | int | ❌ | 1 | `>= 1` | 页码 | | `pageSize` | int | ❌ | 20 | `1-100` | 每页条数 | | `keyword` | string | ❌ | — | — | 车牌模糊匹配 | | `fleet` | string | ❌ | — | `own/coopA/coopB` | 车队精确过滤 | | `typeKey` | string | ❌ | — | — | 车型大类 key 精确(如 suv) | | `vehicleStatus` | string | ❌ | — | `idle/busy/maint` | 状态精确 | | `insureDueBefore` | date | ❌ | — | yyyy-MM-dd | 保险到期日早于此日(含) | | `inspectDueBefore` | date | ❌ | — | yyyy-MM-dd | 年检到期日早于此日(含) | **出参**:`Result>` (按 create_time DESC) | 字段 | 类型 | 说明 | |---|---|---| | `records[].id` | string | 车辆 ID(Long 序列化为 String) | | `records[].plate` | string | 车牌 | | `records[].vehicleModelId` | string | 型号 ID | | `records[].vehicleTypeId` | string | 大类 ID | | `records[].modelName` | string | 型号名(如丰田汉兰达) | | `records[].seats` | int | 座位数 | | `records[].fleet` | string | 车队(own/coopA/coopB) | | `records[].vehicleStatus` | string | 状态(idle/busy/maint) | | `records[].insurer` | string | 保险公司 | | `records[].insureDue` | date | 保险到期日 | | `records[].inspectDue` | date | 年检到期日 | | `records[].createTime` | datetime | 创建时间 | | `total` / `page` / `pageSize` | int | 分页元数据 | **典型示例 请求**: ``` GET /admin/fleet/vehicles/page?page=1&pageSize=20&fleet=own&insureDueBefore=2025-12-31 Authorization: Bearer (无请求体) ``` **典型示例 响应**: ```json { "code": 200, "data": { "records": [ { "id": "1234567890123456789", "plate": "蒙A-88888", "vehicleModelId": "1234567890123456001", "vehicleTypeId": "1234567890123456002", "modelName": "丰田汉兰达", "seats": 7, "fleet": "own", "vehicleStatus": "idle", "insurer": "中国人保财险", "insureDue": "2025-12-31", "inspectDue": "2025-12-31", "createTime": "2025-05-21 14:00:00" } ], "total": 1, "page": 1, "pageSize": 20 }, "msg": "成功" } ``` **错误码**:400 参数校验 / 401 未登录 --- ### 3.2 详情 **GET** `/admin/fleet/vehicles/{vehicleId}` - **使用场景**:详情页 / 编辑前预填 - **认证**:管理后台 JWT - **幂等**:是 **路径入参**: | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `vehicleId` | Long | ✅ | 车辆 ID | **出参**:`Result` 包含 §3.1 全部字段 + 以下扩展: | 字段 | 类型 | 说明 | |---|---|---| | `vin` | string | 车架号(VIN) | | `regDate` | date | 注册日期 | | `policyNo` | string | 保单号 | | `regCertNo` | string | 行驶证号 | | `regCertOwner` | string | 行驶证所有人 | | `regCertUsage` | string | 使用性质 | | `regCertFrontUrl` | string | 行驶证正面 OSS URL | | `regCertBackUrl` | string | 行驶证副页 OSS URL | | `attachments[]` | array | 附件列表(按 category + sortNo 升序) | | `attachments[].id` | string | 附件 ID | | `attachments[].category` | string | 类目(见 §6.1) | | `attachments[].sortNo` | int | 同类目排序(0-based) | | `attachments[].url` | string | OSS URL | | `attachments[].mimeType` | string | 例 `image/jpeg` | | `primaryDriverName` | string\|null | **目前恒为 null**(待业务流水 PR 注入司机姓名) | | `updateTime` | datetime | — | **典型示例 响应**: ```json { "code": 200, "data": { "id": "1234567890123456789", "plate": "蒙A-88888", "vehicleModelId": "1234567890123456001", "vehicleTypeId": "1234567890123456002", "modelName": "丰田汉兰达", "seats": 7, "fleet": "own", "vehicleStatus": "idle", "vin": "LSVNV2182E2100001", "regDate": "2020-01-01", "inspectDue": "2025-12-31", "insurer": "中国人保财险", "policyNo": "PICC-2024-XXX", "insureDue": "2025-12-31", "regCertNo": "12345678", "regCertOwner": "呼籁旅游服务有限公司", "regCertUsage": "营运租赁", "regCertFrontUrl": "https://oss.example.com/fleet/cert-front.jpg", "regCertBackUrl": "https://oss.example.com/fleet/cert-back.jpg", "attachments": [ { "id": "1234567890123456900", "vehicleId": "1234567890123456789", "category": "exterior", "sortNo": 0, "url": "https://oss.example.com/fleet/ext-1.jpg", "mimeType": "image/jpeg" } ], "primaryDriverName": null, "createTime": "2025-05-21 14:00:00", "updateTime": "2025-05-21 14:00:00" }, "msg": "成功" } ``` **错误码**:**600110** 车辆不存在 / 401 未登录 --- ### 3.3 新增 **POST** `/admin/fleet/vehicles` - **使用场景**:新增车辆 + 附件批量提交(前端 STS 直传 OSS 后把 URL 数组放主表单一并提交) - **认证**:管理后台 JWT - **幂等**:否(重复抛 600100/600107) **Body 入参**(`VehicleSaveReqVO`): | 字段 | 类型 | 必填 | 校验 | 说明 | |---|---|---|---|---| | `plate` | string | ✅ | `@Size(max=16)` | 车牌(全局唯一) | | `vehicleModelId` | Long | ✅ | — | 车型型号 ID(引用 §1 模块) | | `fleet` | string | ✅ | `@Pattern(own\|coopA\|coopB)` | 车队 | | `vehicleStatus` | string | ❌ | `@Pattern(idle\|busy\|maint)` | 状态,默认 idle | | `vin` | string | ❌ | `@Size(max=32)` | 车架号(传则全局唯一) | | `regDate` | date | ❌ | — | 注册日期 | | `inspectDue` | date | ❌ | — | 年检到期日 | | `insurer` | string | ❌ | `@Size(max=64)` | 保险公司 | | `policyNo` | string | ❌ | `@Size(max=64)` | 保单号 | | `insureDue` | date | ❌ | — | 保险到期日 | | `regCertNo` | string | ❌ | `@Size(max=64)` | 行驶证号 | | `regCertOwner` | string | ❌ | `@Size(max=128)` | 行驶证所有人 | | `regCertUsage` | string | ❌ | `@Size(max=32)` | 使用性质 | | `regCertFrontUrl` | string | ❌ | `@Size(max=512)` | 行驶证正面 OSS URL | | `regCertBackUrl` | string | ❌ | `@Size(max=512)` | 行驶证副页 OSS URL | | `attachments[]` | array | ❌ | — | 附件数组(见下) | **`attachments[]` 单项**(`VehicleAttachmentReqVO`): | 字段 | 类型 | 必填 | 校验 | 说明 | |---|---|---|---|---| | `id` | Long | ❌ | — | 附件 ID(新增项不传) | | `category` | string | ✅ | `@Size(max=32)` | 类目(见 §6.1) | | `url` | string | ✅ | `@Size(max=512)` | OSS URL | | `mimeType` | string | ✅ | `@Size(max=64)` | 例 `image/jpeg` | | `sortNo` | int | ❌ | — | 同类目排序(不传时按入参顺序自动重写 0..N-1) | **出参**:`Result` | 字段 | 类型 | 说明 | |---|---|---| | `id` | string | 新增车辆 ID | | `attachmentIds[]` | array | 附件 ID 列表(按入参顺序) | **典型示例 请求**: ``` POST /admin/fleet/vehicles Authorization: Bearer Content-Type: application/json { "plate": "蒙A-88888", "vehicleModelId": "1234567890123456001", "fleet": "own", "vehicleStatus": "idle", "vin": "LSVNV2182E2100001", "regDate": "2020-01-01", "inspectDue": "2025-12-31", "insurer": "中国人保财险", "policyNo": "PICC-2024-XXX", "insureDue": "2025-12-31", "regCertNo": "12345678", "regCertOwner": "呼籁旅游服务有限公司", "regCertUsage": "营运租赁", "regCertFrontUrl": "https://oss.example.com/fleet/cert-front.jpg", "regCertBackUrl": "https://oss.example.com/fleet/cert-back.jpg", "attachments": [ { "category": "exterior", "url": "https://oss.example.com/fleet/ext-1.jpg", "mimeType": "image/jpeg" }, { "category": "exterior", "url": "https://oss.example.com/fleet/ext-2.jpg", "mimeType": "image/jpeg" }, { "category": "insurance", "url": "https://oss.example.com/fleet/ins.jpg", "mimeType": "image/jpeg" } ] } ``` **典型示例 响应**: ```json { "code": 200, "data": { "id": "1234567890123456789", "attachmentIds": ["1234567890123456900", "1234567890123456901", "1234567890123456902"] }, "msg": "成功" } ``` **异常 请求**(车牌重复): ```json { "code": 600100, "msg": "车牌已存在", "data": null } ``` **异常 请求**(附件类目张数超上限 `exterior` > 6): ```json { "code": 601003, "msg": "该类目张数已达上限", "data": null } ``` **错误码**:400 参数校验 / **600100** 车牌已存在 / **600107** VIN 已存在 / **600108** 车型不存在 / **601002** 附件类目非法 / **601003** 类目张数达上限 / 401 未登录 --- ### 3.4 编辑(附件 diff 语义) **PUT** `/admin/fleet/vehicles/{vehicleId}` - **使用场景**:编辑车辆基本信息 + 附件增删改一次性提交 - **认证**:管理后台 JWT - **幂等**:是 **路径入参**: | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `vehicleId` | Long | ✅ | 车辆 ID | **Body 入参**:同 §3.3(共用 `VehicleSaveReqVO`) **附件 diff 语义**: | 入参 attachments[] 项 | DB 中的对应行 | 行为 | |---|---|---| | 有 `id` + DB 命中 | 存在 | **保留**(可更新 sortNo) | | 无 `id`(新增项) | — | **INSERT** | | — | 存在但本次未传 | **软删** | **出参**:`Result` | 字段 | 类型 | 说明 | |---|---|---| | `kept` | int | 保留的附件数 | | `inserted` | int | 新插入的附件数 | | `softDeleted` | int | 软删的附件数 | **典型示例 响应**: ```json { "code": 200, "data": { "kept": 2, "inserted": 1, "softDeleted": 1 }, "msg": "成功" } ``` **错误码**:400 参数校验 / **600100** 车牌已存在 / **600107** VIN 已存在 / **600108** 车型不存在 / **600110** 车辆不存在 / **601002** 类目非法 / **601003** 类目张数达上限 / 401 未登录 --- ### 3.5 软删 **DELETE** `/admin/fleet/vehicles/{vehicleId}` - **使用场景**:下架车辆 - **认证**:管理后台 JWT - **幂等**:是 **路径入参**: | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `vehicleId` | Long | ✅ | 车辆 ID | **出参**:`Result`(`data: null`) **典型示例 响应**: ```json { "code": 200, "data": null, "msg": "成功" } ``` **异常 请求**(车辆有未完成派单 —— **业务流水 PR 落地后才生效**): ```json { "code": 600109, "msg": "车辆有未完成派单,不能删除", "data": null } ``` **错误码**:**600109** 有未完成派单(占位,业务流水 PR 上线后生效)/ **600110** 车辆不存在 / 401 未登录 --- ### 3.6 Excel 批量导入 **POST** `/admin/fleet/vehicles/import` - **使用场景**:运营批量录入车辆 - **认证**:管理后台 JWT - **幂等**:否(重复行按车牌冲突拒绝单行) **入参**(multipart/form-data): | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `file` | file | ✅ | Excel(`.xlsx` / `.xls`)或 CSV | **表头约定(第 1 行 12 列,顺序固定)**: ``` 车牌 | 车型型号ID | 车队(own/coopA/coopB) | 车架号(VIN) | 注册日期(yyyy-MM-dd) | 年检到期日(yyyy-MM-dd) | 保险公司 | 保单号 | 保险到期日(yyyy-MM-dd) | 行驶证号 | 行驶证所有人 | 使用性质 ``` **出参**:`Result` | 字段 | 类型 | 说明 | |---|---|---| | `total` | int | 总行数(不含表头) | | `success` | int | 成功导入数 | | `failed` | int | 失败行数 | | `errorRows[]` | array | 失败行详情 | | `errorRows[].row` | int | Excel 行号(从 2 开始,1 为表头) | | `errorRows[].plate` | string | 车牌(用于定位) | | `errorRows[].msg` | string | 错误信息(如"车牌已存在") | **典型示例 响应**: ```json { "code": 200, "data": { "total": 10, "success": 8, "failed": 2, "errorRows": [ { "row": 3, "plate": "蒙A-88888", "msg": "车牌已存在" }, { "row": 7, "plate": "蒙A-99999", "msg": "车型型号 ID 不存在" } ] }, "msg": "成功" } ``` **错误码**:400 文件解析失败 / 401 未登录 --- ## 6. 枚举 / 数据字典 ### 6.1 附件类目(`category`,`VehicleAttachmentCategoryEnum`) **所属字段**:`VehicleAttachmentReqVO.category` / `VehicleAttachmentRespVO.category` | **类型**:`String` | **必填**:✅ **真枚举**(后端硬编码,含同类目张数上限): | 值 | 中文 | 张数上限 | |---|---|---| | `exterior` | 车辆外观 | 6 | | `interior` | 车辆内饰 | 3 | | `insurance` | 保险单据 | 2 | | `inspection` | 年检单据 | 2 | | `operation_license` | 营运证 | 2 | | `other` | 其他附件 | 5 | > 超出上限触发 **601003** "该类目张数已达上限"。 ### 6.2 车队(`fleet`) **所属字段**:`VehicleSaveReqVO.fleet` / `VehiclePageReqVO.fleet` / `VehiclePageItemRespVO.fleet` | **类型**:`String` | **必填**:✅(新增时) **`@Pattern` 强校验枚举**: | 值 | 含义 | |---|---| | `own` | 自营车辆 | | `coopA` | 合作车队 A | | `coopB` | 合作车队 B | ### 6.3 车辆状态(`vehicleStatus`) **所属字段**:同 `fleet` | **类型**:`String` | **必填**:❌(默认 idle) **`@Pattern` 强校验枚举**: | 值 | 含义 | |---|---| | `idle` | 空闲 | | `busy` | 在途 | | `maint` | 维修中 | --- ## 7. 错误码(全模块汇总) | code | 含义 | 触发条件 | 涉及接口 | |---|---|---|---| | 400 | 参数校验失败 | 字段缺失/长度/枚举值不合法/MIME 非法 | 所有写入接口 | | 401 | 未登录 | JWT 无效或缺失 | 全部 6 接口 | | **600100** | 车牌已存在 | `plate` 全局唯一约束 | §3.3 §3.4 | | **600107** | VIN 已存在 | `vin` 全局唯一约束 | §3.3 §3.4 | | **600108** | 车型不存在 | `vehicleModelId` 无效或软删 | §3.3 §3.4 | | **600109** | 有未完成派单,不能删除 | 业务流水 PR 上线后才触发(目前占位) | §3.5 | | **600110** | 车辆不存在 | `vehicleId` 无效或软删 | §3.2 §3.4 §3.5 | | **601001** | 实体不存在 | 附件关联的主车辆不存在 | §3.3 §3.4 | | **601002** | 附件类目非法 | `category` 不在枚举内 | §3.3 §3.4 | | **601003** | 该类目张数已达上限 | 超出 §6.1 张数上限 | §3.3 §3.4 | | **601004** | 文件超出大小限制 | 应用层 + OSS HEAD 校验 | §3.6(导入),§3.3/§3.4 主体不直接校验 | | **601005** | 文件类型非法 | MIME 不在白名单 | §3.3 §3.4 | > 错误码段位 600100-600110 归属车辆档案;601001-601005 归属附件管理(车辆/司机共用)。 > > **注意:与文档 v1.4 的差异** —— 文档原计划 VIN 用 600101 / 车型不存在 600102 / 派单冲突 600103,但因这 3 个段位被车型管理库占用(见 §1 changelog),实际代码避让到 **600107/600108/600109**。本 changelog 反映**代码实际值**。 --- ## 9. 业务边界 - ✅ **附件 diff 语义统一**:§3.3 新增时所有项无 id;§3.4 编辑时按 `id` 命中度决定保留/插入/软删 - ✅ **sortNo 自动重写**:附件 `sortNo` 不传时按入参顺序自动写 0..N-1(同 category 内 0-based) - ✅ **行驶证 OSS URL 留主表**:`regCertFrontUrl` / `regCertBackUrl` 是主表字段(不走附件表),与附件分离 - ✅ **唯一约束**:`plate` / `vin` **全局**唯一(违反抛 600100 / 600107) - ✅ **车队 / 状态强枚举**:`fleet` `vehicleStatus` 走 `@Pattern` 校验,违反返 400 - ⚠️ **primaryDriverName 占位**:§3.2 详情返 `primaryDriverName: null`,待业务流水 PR 上线后注入司机姓名 - ⚠️ **派单删除保护占位**:§3.5 错误码 600109 在业务流水 PR 上线后才会真触发 --- ## 11. 影响评估 - **是否破坏向后兼容**:否(接口契约自首次落地以来未变更) - **前端是否必须同步上线**:否(本次仅补发 changelog,无代码改动) - **影响已有数据**:无 --- ## 12. 注意事项 - **分页参数名**:`page` / `pageSize`(**不是** pageNo) - **Long 主键序列化**:`id` / `vehicleModelId` / `vehicleTypeId` / `attachments[].id` 等 Long 字段 JSON 返回为 String,前端**不要**当 Number 解析 - **错误码不连续**:600100 / 600107-600110(中间 600101-600106 是车型管理库的段位),见 §7 注解 - **附件 URL 不可改**:§3.4 编辑时 `attachments[].url` 不可改(后端忽略),只能保留 / 新增 / 软删 - **`primaryDriverName` 当前恒为 null**:业务流水 PR 上线前前端**不要**展示该字段或处理为"未指派" --- ## 13. 关联 / 联系人 ### 13.1 链接 - **API 文档**: `docs/order-v3/api/API-SPEC-FLEET-V1.5.html` §2 车辆档案 - **DB 文档**: `docs/order-v3/database/DATABASE-SCHEMA-FLEET-V1.5.html` §2.3 / §2.6 - **同期相关 changelog**: - 车型管理库 9 接口([`21_2785_车型管理库-新增接口-管理后台.md`](./21_2785_车型管理库-新增接口-管理后台.md)) - 司机档案 6 接口([`21_2791_司机档案-修改接口-管理后台.md`](./21_2791_司机档案-修改接口-管理后台.md)) ### 13.2 联系人 - **后端负责人**: @yst(腰苏图) - **前端对接(管理后台)**: 待指派