hl-api-changelog/changelogs-v2/2026-05/21_2785_车型管理库-新增接口-管理后台.md

21 KiB

【新增接口·管理后台】车型管理库 9 接口(含新增的大类/型号分页 2 接口) (#2785)

PR: #2786 | 服务: hl-fleet-service | 更新时间: 2026-05-21 14:25

存放目录: changelogs-v2/2026-05/(前端 v3 项目仓库读取) 影响范围: 管理后台「车管 / 车型管理库」页(左侧大类导航 + 右侧型号列表 + 全模块 CRUD


⚠️ 关键变化30 秒速读)

本次实质变化是 2 个新增分页接口§3.2 大类分页 / §3.7 型号分页),原 7 个 CRUD/树接口契约零变化。一并把整模块 9 接口契约打包发给前端,方便对接「左侧大类导航 + 右侧型号列表」联动场景。

# 接口 状态 用途
§3.1 GET /admin/fleet/vehicle-types 已存在·未变 一次拉全模块树(大类+型号)
§3.2 GET /admin/fleet/vehicle-types/page 新增 大类分页+过滤(左侧导航专用)
§3.3 POST /admin/fleet/vehicle-types 已存在·未变 新增大类
§3.4 PUT /admin/fleet/vehicle-types/{typeId} 已存在·未变 编辑大类
§3.5 DELETE /admin/fleet/vehicle-types/{typeId} 已存在·未变 删除大类(软删)
§3.6 POST /admin/fleet/vehicle-types/{typeId}/models 已存在·未变 新增型号
§3.7 GET /admin/fleet/vehicle-types/models/page 新增 型号分页+过滤vehicleTypeId 可选)
§3.8 PUT /admin/fleet/vehicle-types/models/{modelId} 已存在·未变 编辑型号
§3.9 DELETE /admin/fleet/vehicle-types/models/{modelId} 已存在·未变 删除型号(软删)

1. 接口背景

车型管理库是车队基础字典:大类(如 SUV/MPV/BUS/Sedan→ 型号(如 丰田汉兰达/本田奥德赛)。管理后台需要:

  1. 左侧大类导航 + 右侧型号列表联动 → 大类分页 + 型号按 typeId 过滤的分页
  2. 跨大类按车型名搜索 → 型号分页 typeId 可选 + modelName 模糊
  3. 已有的 listTree 一次拉树仍保留(其他场景如派单弹窗筛选 / 价格日历分组继续用)

2. 变更清单

# 方法 路径 变更类型 说明
1 GET /admin/fleet/vehicle-types 未变 列出大类+型号整树
2 GET /admin/fleet/vehicle-types/page 新增 大类分页
3 POST /admin/fleet/vehicle-types 未变 新增大类
4 PUT /admin/fleet/vehicle-types/{typeId} 未变 编辑大类typeKey 不可改)
5 DELETE /admin/fleet/vehicle-types/{typeId} 未变 删除大类(软删)
6 POST /admin/fleet/vehicle-types/{typeId}/models 未变 新增型号
7 GET /admin/fleet/vehicle-types/models/page 新增 型号分页
8 PUT /admin/fleet/vehicle-types/models/{modelId} 未变 编辑型号
9 DELETE /admin/fleet/vehicle-types/models/{modelId} 未变 删除型号(软删)

3. 接口详情

3.1 列出大类 + 型号树

GET /admin/fleet/vehicle-types

  • 使用场景:一次拉取整棵车型树(大类含 models 数组),用于派单弹窗、价格日历分组等需要全量树的场景
  • 认证:管理后台 JWT
  • 幂等:是(只读)
  • 入参:无

出参Result<List<VehicleTypeTreeRespVO>>

sort_order 升序。每个大类含 models 数组(同大类内按 sort_order 升序)。无大类时返回空数组 []

典型示例 请求

GET /admin/fleet/vehicle-types
Authorization: Bearer <token>
(无请求体)

典型示例 响应

{
  "code": 200,
  "data": [
    {
      "id": "1234567890123456789",
      "typeKey": "suv",
      "typeName": "SUV 越野",
      "icon": "🚙",
      "description": "适合山地越野、多人出行",
      "sortOrder": 1,
      "models": [
        {
          "id": "9876543210987654321",
          "vehicleTypeId": "1234567890123456789",
          "modelName": "丰田汉兰达",
          "seats": 7,
          "basePrice": "800.00",
          "alias": "汉兰达,HIGHLANDER",
          "sortOrder": 1
        }
      ]
    }
  ],
  "msg": "成功"
}

错误码:仅 401 未登录


3.2 大类分页列表 新增

GET /admin/fleet/vehicle-types/page

  • 使用场景管理后台「车型管理库」左侧大类导航的分页查询,支持名称模糊、key 精确过滤
  • 认证:管理后台 JWT
  • 幂等:是(只读)

Query 入参

字段 类型 必填 默认 校验 说明
page int 1 >= 1 页码
pageSize int 20 1-100 每页条数
typeName string 大类中文名模糊匹配
typeKey string 大类 key 精确匹配

出参Result<PageResult<VehicleTypeRespVO>>

sort_order 升序。models 数组(需要型号请调 §3.7)。

字段 类型 说明
records[].id string 大类 IDLong 序列化为 String 防精度丢失)
records[].typeKey string 大类 key如 suv/mpv/bus/sedan
records[].typeName string 大类中文名
records[].icon string emoji 图标(可空)
records[].description string 描述(可空)
records[].sortOrder int 排序权重
total int 总记录数
page int 当前页码
pageSize int 每页条数

典型示例 请求

GET /admin/fleet/vehicle-types/page?page=1&pageSize=20&typeName=SUV
Authorization: Bearer <token>
(无请求体)

典型示例 响应

{
  "code": 200,
  "data": {
    "records": [
      {
        "id": "1234567890123456789",
        "typeKey": "suv",
        "typeName": "SUV 越野",
        "icon": "🚙",
        "description": "适合山地越野、多人出行",
        "sortOrder": 1
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  },
  "msg": "成功"
}

边界 请求无过滤拿全部,pageSize=100 极限):

GET /admin/fleet/vehicle-types/page?page=1&pageSize=100

异常 请求pageSize=101 超限):

GET /admin/fleet/vehicle-types/page?page=1&pageSize=101

响应:

{ "code": 400, "msg": "每页条数最大为100", "data": null }

错误码400 参数校验 / 401 未登录


3.3 新增大类

POST /admin/fleet/vehicle-types

  • 使用场景:扩展车型大类(初始化已灌 4 条 suv/mpv/bus/sedan,后续如新增小型客车等
  • 认证:管理后台 JWT
  • 幂等:否(重复调用会触发 600101 重复)

Body 入参VehicleTypeSaveReqVO

字段 类型 必填 校验 说明
typeKey string @Size(max=16) 大类 key全局唯一
typeName string @Size(max=64) 大类中文名
icon string @Size(max=8) emoji 图标(可为空)
description string @Size(max=256) 描述(可为空)
sortOrder int 排序权重(升序)

出参Result<VehicleTypeRespVO>(同 §3.2 records 项字段)

典型示例 请求

POST /admin/fleet/vehicle-types
Authorization: Bearer <token>
Content-Type: application/json

{
  "typeKey": "minibus",
  "typeName": "小型客车",
  "icon": "🚐",
  "description": "10-19 座中巴",
  "sortOrder": 5
}

典型示例 响应

{
  "code": 200,
  "data": {
    "id": "1234567890123456789",
    "typeKey": "minibus",
    "typeName": "小型客车",
    "icon": "🚐",
    "description": "10-19 座中巴",
    "sortOrder": 5
  },
  "msg": "成功"
}

异常 请求typeKey 重复):

{ "typeKey": "suv", "typeName": "重复 SUV", "sortOrder": 99 }

响应:

{ "code": 600101, "msg": "车型大类 key 已存在", "data": null }

错误码400 参数校验 / 600101 typeKey 已存在 / 401 未登录


3.4 编辑大类

PUT /admin/fleet/vehicle-types/{typeId}

  • 使用场景:修改大类的 typeName / icon / description / sortOrder。typeKey 不可改(后端忽略入参中的 typeKey 字段)
  • 认证:管理后台 JWT
  • 幂等:是(相同 body 重复调结果一致)

路径入参

字段 类型 必填 说明
typeId Long 大类 ID

Body 入参:同 §3.3typeKey 字段后端忽略;其他字段同新增)

出参Result<VehicleTypeRespVO>(更新后的大类)

典型示例 请求

PUT /admin/fleet/vehicle-types/1234567890123456789
Authorization: Bearer <token>
Content-Type: application/json

{
  "typeKey": "suv",
  "typeName": "SUV 越野 (改名)",
  "icon": "🚙",
  "description": "新描述",
  "sortOrder": 2
}

典型示例 响应

{
  "code": 200,
  "data": {
    "id": "1234567890123456789",
    "typeKey": "suv",
    "typeName": "SUV 越野 (改名)",
    "icon": "🚙",
    "description": "新描述",
    "sortOrder": 2
  },
  "msg": "成功"
}

异常 请求typeId 不存在):

{ "code": 600102, "msg": "车型大类不存在", "data": null }

错误码400 参数校验 / 600102 大类不存在 / 401 未登录


3.5 删除大类(软删)

DELETE /admin/fleet/vehicle-types/{typeId}

  • 使用场景:下架某个大类。大类下存在型号时禁止删除(应用层校验)
  • 认证:管理后台 JWT
  • 幂等:是(重复删返同样结果)

路径入参

字段 类型 必填 说明
typeId Long 大类 ID

出参Result<Void>(成功时 data: null

典型示例 请求

DELETE /admin/fleet/vehicle-types/1234567890123456789
Authorization: Bearer <token>
(无请求体)

典型示例 响应

{ "code": 200, "data": null, "msg": "成功" }

异常 请求(大类下有型号):

{ "code": 600104, "msg": "大类下存在型号,不能删除", "data": null }

错误码600102 大类不存在 / 600104 大类下存在型号 / 401 未登录


3.6 新增型号(挂在指定大类下)

POST /admin/fleet/vehicle-types/{typeId}/models

  • 使用场景:在某大类下新增型号(如 SUV 大类下加"路虎揽胜"
  • 认证:管理后台 JWT
  • 幂等:否(同名重复抛 600103

路径入参

字段 类型 必填 说明
typeId Long 所属大类 ID

Body 入参VehicleModelSaveReqVO

字段 类型 必填 校验 说明
modelName string @Size(max=64) 型号名(同大类内唯一)
seats int @Min(4) @Max(19) 座位数 4-19
basePrice BigDecimal @DecimalMin("0") 基础日单价(¥,≥ 0
alias string @Size(max=256) 别名(搜索匹配用)
sortOrder int 排序权重

出参Result<VehicleModelRespVO>

字段 类型 说明
id string 型号 IDLong 序列化为 String
vehicleTypeId string 所属大类 ID
modelName string 型号名
seats int 座位数
basePrice string 基础日单价BigDecimal 序列化)
alias string 别名
sortOrder int 排序

典型示例 请求

POST /admin/fleet/vehicle-types/1234567890123456789/models
Authorization: Bearer <token>
Content-Type: application/json

{
  "modelName": "丰田汉兰达",
  "seats": 7,
  "basePrice": "800.00",
  "alias": "汉兰达,HIGHLANDER",
  "sortOrder": 1
}

典型示例 响应

{
  "code": 200,
  "data": {
    "id": "9876543210987654321",
    "vehicleTypeId": "1234567890123456789",
    "modelName": "丰田汉兰达",
    "seats": 7,
    "basePrice": "800.00",
    "alias": "汉兰达,HIGHLANDER",
    "sortOrder": 1
  },
  "msg": "成功"
}

异常 请求(座位数 20 超限):

{ "modelName": "大巴车", "seats": 20, "basePrice": "1500.00", "sortOrder": 1 }

响应:

{ "code": 400, "msg": "座位数最大为 19", "data": null }

错误码400 参数校验 / 600102 大类不存在 / 600103 型号名已存在 / 401 未登录


3.7 型号分页列表 新增

GET /admin/fleet/vehicle-types/models/page

  • 使用场景
    1. 「左侧点击大类 → 右侧加载该大类型号」 → 传 vehicleTypeId
    2. 「跨大类按车型名/俗称搜索」 → 不传 vehicleTypeId,传 modelNamealias
  • 认证:管理后台 JWT
  • 幂等:是(只读)

Query 入参

字段 类型 必填 默认 校验 说明
page int 1 >= 1 页码
pageSize int 20 1-100 每页条数
vehicleTypeId Long 可选:传则按大类过滤,不传跨大类全量
modelName string 型号名模糊匹配
seats int 座位数精确匹配
alias string 别名模糊匹配(车型俗称)

出参Result<PageResult<VehicleModelRespVO>>

sort_order 升序。返大类名称(前端从左侧大类列表自取)。

records 项字段同 §3.6 出参字段表。

典型示例 请求(按大类查):

GET /admin/fleet/vehicle-types/models/page?page=1&pageSize=20&vehicleTypeId=1234567890123456789
Authorization: Bearer <token>
(无请求体)

典型示例 响应

{
  "code": 200,
  "data": {
    "records": [
      {
        "id": "9876543210987654321",
        "vehicleTypeId": "1234567890123456789",
        "modelName": "丰田汉兰达",
        "seats": 7,
        "basePrice": "800.00",
        "alias": "汉兰达,HIGHLANDER",
        "sortOrder": 1
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  },
  "msg": "成功"
}

边界 请求(跨大类按 modelName 搜"丰田"

GET /admin/fleet/vehicle-types/models/page?modelName=丰田

边界 请求(按 seats=7 精确 + alias 模糊组合):

GET /admin/fleet/vehicle-types/models/page?seats=7&alias=ODYSSEY

异常 请求page=0 不合法):

GET /admin/fleet/vehicle-types/models/page?page=0&pageSize=20

响应:

{ "code": 400, "msg": "页码最小为1", "data": null }

错误码400 参数校验 / 401 未登录


3.8 编辑型号

PUT /admin/fleet/vehicle-types/models/{modelId}

  • 使用场景:修改型号的 modelName / seats / basePrice / alias / sortOrder
  • 认证:管理后台 JWT
  • 幂等:是

路径入参

字段 类型 必填 说明
modelId Long 型号 ID

Body 入参:同 §3.6(不含 typeId,型号所属大类不可改

出参Result<VehicleModelRespVO>

典型示例 请求

PUT /admin/fleet/vehicle-types/models/9876543210987654321
Authorization: Bearer <token>
Content-Type: application/json

{
  "modelName": "丰田汉兰达 (2025 款)",
  "seats": 7,
  "basePrice": "900.00",
  "alias": "汉兰达,HIGHLANDER",
  "sortOrder": 1
}

异常 请求(型号名同大类内已被占用):

{ "code": 600103, "msg": "车型型号已存在", "data": null }

错误码400 参数校验 / 600103 型号名已存在 / 600106 型号不存在 / 401 未登录


3.9 删除型号(软删)

DELETE /admin/fleet/vehicle-types/models/{modelId}

  • 使用场景:下架某个型号。被车辆引用时禁止删除(应用层校验)
  • 认证:管理后台 JWT
  • 幂等:是

路径入参

字段 类型 必填 说明
modelId Long 型号 ID

出参Result<Void>

典型示例 请求

DELETE /admin/fleet/vehicle-types/models/9876543210987654321
Authorization: Bearer <token>
(无请求体)

典型示例 响应

{ "code": 200, "data": null, "msg": "成功" }

异常 请求(型号被车辆引用):

{ "code": 600105, "msg": "型号被车辆引用,不能删除", "data": null }

错误码600105 型号被车辆引用 / 600106 型号不存在 / 401 未登录


6. 枚举 / 数据字典

本模块无静态枚举 / 字典

  • typeKeyfleet_vehicle_type 表的一个 VARCHAR(16) 字段(行级数据),不是后端枚举常量,没有字典表。系统初始化灌入了 4 条标准记录(详见 §9 业务边界),但 typeKey 可通过 §3.3 自由扩展
  • seats 是数值范围4-19,@Min / @Max 校验),不是枚举
  • 其他字段modelName / basePrice / alias 等)均为自由文本/数字

7. 错误码(全模块汇总)

code 含义 触发条件 涉及接口
400 参数校验失败 字段缺失 / 长度 / 范围越界 所有写入接口 + 分页接口
401 未登录 JWT 无效或缺失 全部 9 接口
600101 车型大类 key 已存在 typeKey 全局唯一约束 §3.3 新增大类
600102 车型大类不存在 typeId 无效或已软删 §3.4 §3.5 §3.6
600103 车型型号已存在 同大类内型号名唯一约束 §3.6 §3.8
600104 大类下存在型号,不能删除 应用层校验 §3.5
600105 型号被车辆引用,不能删除 应用层校验 §3.9
600106 车型型号不存在 modelId 无效或已软删 §3.8 §3.9

错误码段位 600101-600106 归属 hl-fleet-service,本次未新增段位。


9. 业务边界

  • 大类 typeKey 编辑:编辑大类时后端忽略入参的 typeKey 字段(不改),其他字段正常更新
  • 大类软删保护大类下有未删除型号时禁删600104;先删型号或迁移型号到其他大类
  • 型号软删保护型号被任一车辆fleet_vehicle引用时禁删600105
  • 同名约束typeKey 全局唯一(违反抛 600101;modelName 同大类内唯一(违反抛 600103
  • 分页默认值page=1pageSize=20pageSize 上限 100
  • 跨大类搜索§3.7 不传 vehicleTypeId + 任意过滤 = 跨大类搜索(不报错)
  • ⚠️ 新增型号副作用§3.6 新增型号后会触发价格日历初始化(见 FLEET §9.4),前端无需感知

typeKey 系统初始化数据(不是枚举,可被 §3.3 扩展)

系统初始化时灌入了 4 条标准大类(来自 DB 初始化脚本):

typeKey typeName 用途参考
suv SUV 越野 高底盘 / 多人出行
mpv MPV 商务 7 座商务车
bus 中巴 10-19 座
sedan 轿车 5 座

⚠️ 这是表里的现有数据不是枚举校验。运营可随时通过 §3.3 新增 minibus / coach 等任意 typeKey仅约束全局不重复。前端展示时建议从 §3.1 或 §3.2 实时查,不要硬编码上面 4 个值。


11. 影响评估

  • 是否破坏向后兼容§3.1-§3.6/§3.8/§3.9 契约零变化,仅新增 §3.2 §3.7 两个分页接口)
  • 前端是否必须同步上线:否(不调用新接口的页面不受影响)
  • 影响已有数据:无(无 DDL,无数据迁移

12. 注意事项

  • 分页参数名page / pageSize不是 pageNo,所有继承 PageParam 的入参都遵此约定
  • 主键 Long 序列化id / vehicleTypeId 等 Long 字段 JSON 返回为 String,前端不要当 Number 解析(精度会丢失)。表单提交时仍可用 Number/String 双向兼容
  • 整树 vs 分页的取舍:低基数字典(< 20 大类 / < 200 型号)继续用 §3.1 整树拉一次缓存到内存;高基数或要服务端搜索时用 §3.2 / §3.7
  • basePrice 序列化BigDecimal 序列化为 String"800.00"),前端按字符串接收避免精度问题

13. 关联 / 联系人

13.1 链接

  • Issue: #2785
  • PR: #2786
  • Merge commit: 90109cd58
  • API 文档: docs/order-v3/api/API-SPEC-FLEET-V1.5.html §1车型管理库 6 接口;实际代码 9 端点 = §1.4 编辑/删除合并算 1 节)
  • 同期相关 PR:
    • #2796 司机详情 relatedOrders mock
    • #2799 司机自助 H5
    • #2800 司机待审核
    • #2804 fleet 文档 v1.5 同步

13.2 联系人

  • 后端负责人: @yst腰苏图
  • 前端对接(管理后台): 待指派