@@ -0,0 +1,293 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "6811"
|
||||
title: "车队保存(无供应商)自动落停用;仍有在役车辆时以 601112 拒绝保存"
|
||||
consumer: "admin"
|
||||
author: "wx(GIT)"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: "2026-08-31"
|
||||
status_note: "#6717 只在「本次保存清除供应商」时强制停用,存量无供应商车队原样再保存仍停留启用。本单把守卫改为按保存结果判定,并对齐 disable 的在役车辆不变量新增 601112。TEST 已实测两条分支。"
|
||||
updated_at: "2026-08-31"
|
||||
base: "dev-v3"
|
||||
generated: "2026-08-31T10:45:00+08:00"
|
||||
---
|
||||
|
||||
# 车队编辑保存:无供应商时自动落停用,仍有在役车辆则以 601112 拒绝
|
||||
|
||||
> **服务**: hl-fleet-service (端口 8087/8187)
|
||||
> **PR**: #6827
|
||||
> **Issue**: #6811
|
||||
> **日期**: 2026-08-31
|
||||
> **影响范围**: 管理后台车务 → 车队管理 → 编辑车队保存
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
- 保存车队时若**结果为无供应商**(`supplierId` 传 `null` 或不传),后端不再允许车队停留在「启用」:无在役车辆时自动把状态落为 `DISABLED`,前端保存成功后需按响应/列表刷新后的状态展示,不能沿用提交前的「启用」。
|
||||
- 上一版(#6717)只在「本次保存把已绑供应商清空」时才强制停用,存量无供应商车队原样再保存不变状态。**该行为已收口**:现在按保存结果判定,`null → null` 同样强制停用。
|
||||
- 新增错误码 **601112**:无供应商需自动停用但车队仍有在役车辆时,保存整体失败、零写入。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
`fleet_team` 的不变量是 **ACTIVE ⇒ 已绑供应商**。此前新建(无供应商落停用)和启用(无供应商 601108 拒绝)都有守卫,唯独编辑保存漏了「结果无供应商」这一路,导致管理后台车队列表出现「供应商列为 —(无供应商)+ 状态启用」的行,且反复保存也不收敛。
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 修改车队 | PUT | `/admin/fleet/teams/{fleetTeamId}` | 状态副作用 + 新增错误码 | 保存结果无供应商时自动落停用;仍有在役车辆则 601112 拒绝 |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 修改车队 `PUT /admin/fleet/teams/{fleetTeamId}`
|
||||
|
||||
**VO**: `FleetTeamSaveReqVO` → `Result<FleetTeamRespVO>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
车务 → 车队管理 → 列表行「编辑」→ 弹窗改车队名称/类型/负责人/电话/付款方式/排序/备注/供应商后点「保存」。本次仅状态副作用与错误码变化,路径、请求字段、字段类型与必填性都不变。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| fleetTeamId | Path | String(雪花 ID) | ✅ | - | 车队 ID,字符串传递,禁止数值化 |
|
||||
| teamName | Body | String | ✅ | 非空、全局唯一 | 车队名称 |
|
||||
| teamType | Body | String | ✅ | `SELF_OPERATED` / `COOPERATIVE` | 已关联车辆时不可切换 |
|
||||
| leaderName | Body | String | ✅ | 非空 | 负责人 |
|
||||
| leaderPhone | Body | String | ✅ | 手机号 | 负责人电话 |
|
||||
| settleType | Body | String | ✅ | `cash` / `sign` / `company` | 付款方式(资源付款方式字典) |
|
||||
| sortOrder | Body | Integer | ✅ | ≥ 0 | 排序 |
|
||||
| remark | Body | String | ❌ | ≤ 256 | 备注,空串按 null 落库 |
|
||||
| supplierId | Body | String(雪花 ID) | ❌ | 供应商需生效且含 FLEET 类型 | **不传或传 null = 不选供应商**,触发本次自动停用规则 |
|
||||
|
||||
#### 出参 `Result<FleetTeamRespVO>`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| fleetTeamId | String | 车队 ID |
|
||||
| teamName | String | 车队名称 |
|
||||
| teamType | String | `SELF_OPERATED` / `COOPERATIVE` |
|
||||
| status | String | **`ACTIVE` / `DISABLED`;保存结果无供应商时返回 `DISABLED`** |
|
||||
| settleType | String | 付款方式 |
|
||||
| sortOrder | Integer | 排序 |
|
||||
| supplierId | String / null | 关联供应商 ID,null = 未关联 |
|
||||
| supplierName | String / null | 供应商全称快照,供应商为 null 时同步清空 |
|
||||
| vehicleCount | Number | 名下车辆总数 |
|
||||
| activeVehicleCount | Number | 在役车辆数 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
无供应商的车队原样保存(触发自动停用):
|
||||
|
||||
```json
|
||||
{
|
||||
"teamName": "测试车队P4-1787131721",
|
||||
"teamType": "COOPERATIVE",
|
||||
"leaderName": "测试负责人",
|
||||
"leaderPhone": "13900000001",
|
||||
"settleType": "cash",
|
||||
"sortOrder": 99,
|
||||
"remark": "由 fleet_attribution/历史业务数据迁移,负责人待完善",
|
||||
"supplierId": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
无供应商 + 无在役车辆 → 200,且 `status` 已变 `DISABLED`:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"fleetTeamId": "348397854705979392",
|
||||
"teamName": "测试车队P4-1787131721",
|
||||
"teamType": "COOPERATIVE",
|
||||
"status": "DISABLED",
|
||||
"settleType": "cash",
|
||||
"sortOrder": 99,
|
||||
"supplierId": null,
|
||||
"supplierName": null,
|
||||
"vehicleCount": 0,
|
||||
"activeVehicleCount": 0
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
本接口为写接口,不返回空集。供应商资格校验依赖(resource 侧)不可用时**失败关闭**,不降级放行:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 601110,
|
||||
"message": "暂时无法校验供应商,请稍后重试",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
新增(本次)——无供应商需自动停用但仍有在役车辆:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 601112,
|
||||
"message": "车队未关联供应商需自动停用,但仍有在役车辆,请先关联供应商或转移在役车辆",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
本接口其他错误码(本次未变,供自包含联调):
|
||||
|
||||
| code | message | 触发条件 |
|
||||
|------|---------|----------|
|
||||
| 601100 | 车队不存在 | `fleetTeamId` 无对应车队 |
|
||||
| 601101 | 车队名称已存在 | `teamName` 与其他车队重名 |
|
||||
| 601104 | 车队已关联车辆,不能修改自有/合作类型 | 名下有车辆时改 `teamType` |
|
||||
| 601106 | 付款方式不是有效的资源付款方式 | `settleType` 不在 `cash`/`sign`/`company` |
|
||||
| 601109 | 供应商不存在、未生效或不包含车队类型 | 绑定的 `supplierId` 不合格 |
|
||||
| 601110 | 暂时无法校验供应商,请稍后重试 | 供应商资格校验依赖不可用 |
|
||||
| 601111 | 车队已关联订单,不能更换供应商 | 已绑供应商的车队换绑/清除且名下车辆有非取消派单 |
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 鉴权:需登录且具备车务车队管理权限;未登录网关返 401。
|
||||
- **状态不由前端入参驱动**:请求体没有 `status` 字段,启停仍由 `/disable`、`/enable` 切;唯一例外是本次的自动停用副作用。
|
||||
- 供应商未变时不调供应商资格校验,也不查订单围栏;仅当 `supplierId` 发生变化才校验。
|
||||
- 已绑供应商的车队换绑或清除时,名下车辆若有非取消派单一律 601111(该守卫先于 601112 判定)。
|
||||
- 失败零写入:601112、601111、601109、601110 均在写库前抛出,名称、排序、备注等字段都不会部分保存。
|
||||
- 并发:同一 `fleetTeamId` 的保存互斥(分布式锁),重复提交不会产生中间态。
|
||||
- 已停用且无供应商的车队保存时保持停用,不报错。
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### ✅ 正确 / ❌ 错误 payload 对照
|
||||
|
||||
| 场景 | payload 与结果 |
|
||||
|------|---------------|
|
||||
| ✅ 绑定合法供应商保存 | `{"supplierId": "2094238127517319170", ...}` → 200,`status` 保持原值 |
|
||||
| ✅ 无供应商 + 无在役车辆 | `{"supplierId": null, ...}` → 200,`status=DISABLED` |
|
||||
| ✅ 已停用 + 无供应商 | `{"supplierId": null, ...}` → 200,`status` 保持 `DISABLED` |
|
||||
| ❌ 无供应商 + 有在役车辆 | `{"supplierId": null, ...}` → 601112,零写入 |
|
||||
| ❌ 靠不传 `supplierId` 保留原供应商 | 不传等同传 `null`,会被当成「清除供应商」 |
|
||||
|
||||
### 切换状态时的必要动作
|
||||
|
||||
- 编辑回显后提交必须**原样回传详情里的 `supplierId`**;字段缺失就是清除语义,不存在「不传 = 不改」。
|
||||
- 保存成功后不能沿用提交前的状态展示:用响应体 `data.status` 或重拉列表/详情。
|
||||
- 收到 601112 时弹后端 message 即可;修复路径是「选一个合法供应商」或「先把在役车辆转走/停用」。
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
| 前端提交 | 保存后外部可观察结果 |
|
||||
|----------|--------------------|
|
||||
| `supplierId` 合法值 | 车队关联该供应商,`supplierName` 刷为当前全称快照,状态不变 |
|
||||
| `supplierId=null`,无在役车辆 | 供应商关联与全称快照同时置空,状态变 `DISABLED` |
|
||||
| `supplierId=null`,有在役车辆 | 零写入(名称/排序/备注等也不保存) |
|
||||
|
||||
本次无表结构变更、无 Flyway 迁移,存量脏数据不做批量刷状态(下次编辑保存时自然收口)。
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- 未登录 → 401(网关拦截)。
|
||||
- 车队不存在 → 601100。
|
||||
- 供应商资格依赖降级 → 601110 失败关闭,不静默放行。
|
||||
- 存量无供应商且仍启用的车队:不会被后台任务批量改状态,只在下次编辑保存时收口。
|
||||
- 已绑供应商的车队保存行为与之前一致,无新增拦截。
|
||||
- 供应商改名后快照陈旧:仍需重新编辑车队刷新(行为未变)。
|
||||
|
||||
---
|
||||
|
||||
## 六.6、修改前后对比
|
||||
|
||||
### 字段级对比
|
||||
|
||||
| 字段 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 请求体字段 | 无变化 | 无变化(路径、字段名、类型、必填性全部不变) |
|
||||
| 响应 `data.status` | 仅当本次清除已绑供应商时可能返回 `DISABLED` | **只要保存结果无供应商且原为启用,就返回 `DISABLED`** |
|
||||
|
||||
### 行为级对比
|
||||
|
||||
| 行为 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 存量无供应商车队(`null → null`)原样保存 | 保持「启用」 | 无在役车辆 → 自动「停用」;有在役车辆 → 601112 |
|
||||
| 清除已绑供应商(`X → null`) | 启用→停用(不查在役车辆) | 有在役车辆时改为 601112 拒绝,不再产出「停用车队挂在役车辆」脏态 |
|
||||
| 绑定/换绑合法供应商 | 200,状态不变 | 不变 |
|
||||
|
||||
## 六.7、影响评估
|
||||
|
||||
- **是否破坏向后兼容**:否(请求/响应结构不变,新增一个错误码与一个状态副作用)。
|
||||
- **前端是否必须同步上线**:否。旧前端不报错,但若保存后不刷新列表,页面会短暂显示陈旧的「启用」。
|
||||
- **前端 workaround 清理点**:若前端曾为「无供应商仍显示启用」做过本地推断或提示,可改为直读后端 `status`。
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**:管理后台车队编辑保存(`PUT /admin/fleet/teams/{fleetTeamId}`)。
|
||||
- **零影响**:
|
||||
- 新建车队 `POST /admin/fleet/teams`(无供应商本来就落停用)
|
||||
- 启用/停用 `POST /admin/fleet/teams/{id}/enable`、`/disable`
|
||||
- 车队列表、详情、options 读接口字段
|
||||
- 车辆、司机、派单、看板、对账等其他车务接口
|
||||
- 存量数据(不批量刷状态)
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
部署:`dev-v3` 合入 PR #6827(merge `740fdb834`)后部署 hl-fleet-service(8087/8187 滚动,两实例健康)。
|
||||
|
||||
```
|
||||
PUT /admin/fleet/teams/348397854705979392 (无供应商 + 启用 + activeVehicleCount=0)
|
||||
→ 200 成功,保存后 GET 详情 status=DISABLED, supplierId=null ✓
|
||||
|
||||
PUT /admin/fleet/teams/348398422098841600 (无供应商 + 启用 + activeVehicleCount=1)
|
||||
→ 601112 「车队未关联供应商需自动停用,但仍有在役车辆…」
|
||||
保存后 GET 详情 status=ACTIVE(零写入) ✓
|
||||
|
||||
PUT /admin/fleet/teams/2026072200010000001 (已绑供应商 + 有订单, 清除供应商)
|
||||
→ 601111 既有订单围栏优先,行为未变 ✓
|
||||
```
|
||||
|
||||
验证用车队:`348397854705979392`(测试车队P4-1787131721)、`348398422098841600`(测试车队P4-1787131856,验证用临时车辆已删除并回到 0 车)。
|
||||
|
||||
单测与构建:`mvn -pl hl-fleet-service -am verify` → Tests run 3887 / Failures 0 / Errors 0(Skipped 5 为既有 Release-E 跳过项);`mvn -pl hl-fleet-service spotless:check` → 799 文件 0 违规。
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 前置契约:`changelogs-v2/2026-08/30_6717_车队关联供应商并展示供应商全名-修改接口-管理后台.md`
|
||||
- 本次 PR:[#6827](https://git.1814.love:8443/wx/HL/pulls/6827)
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
- Issue:[#6811](https://git.1814.love:8443/wx/HL/issues/6811)
|
||||
- 后端联系人:@wx
|
||||
在新工单中引用
屏蔽一个用户