huangxz
2026-06-08 cd5993fa92e48d9adffb6ebd0aa9007f43065b75
增加批量测试计划单车重试的需求
1个文件已添加
173 ■■■■■ 已修改文件
docs/virtualOrderFeat.md 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/virtualOrderFeat.md
New file
@@ -0,0 +1,173 @@
# 批量测试 - 故障车辆手动恢复 前端对接指引
> 适用版本:`feat-virture-order-contine-for-single-car` 分支
> 后端服务:`xp-robotaxi-operation-boot`
> 文档版本:2026-06-07
---
## 一、需求背景
批量测试执行过程中,若某辆车发生故障(GPS 卡死、车端异常等)导致 `ORDER_NEAR_DESTINATION_EVENT` 事件无法上送,链式回调链路会中断,该车辆停留在某一段订单上无法继续。
为此新增「**故障车辆手动恢复**」功能:人工确认车辆已修复且旧订单已无法继续后,前端调用恢复接口,后端将卡住的旧订单标记为已取消并模拟链式回调推进下一段,使该车辆重新进入批量测试流程。
---
## 二、新增接口
### POST `/api/v1/xp-test-plan/manualResume`
**用途**:手动恢复故障车辆,跳过当前卡死的订单,推进至下一段。
**请求方式**:`application/x-www-form-urlencoded`(Query / Form 参数,**非 JSON Body**)
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `testPlanId` | Long | 是 | 测试计划 ID |
| `vehicleId` | Long | 是 | 故障车辆 ID |
| `currentOrderId` | Long | 是 | 该车辆当前进行中的故障订单 ID(必须等于车辆进度表中的 `currentOrderId`) |
**响应**:
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": true
}
```
**业务校验**(任一失败均返回 500 错误,`msg` 中带具体原因):
| 校验 | 错误信息 |
| --- | --- |
| 订单 ID 必须能在进度表中查到 | `未找到订单对应的车辆进度: {orderId}` |
| 订单所属计划/车辆必须与传入参数一致 | `订单与测试计划/车辆不匹配,无法恢复` |
| 车辆进度状态必须是 RUNNING(0) | `车辆进度非运行中,无法恢复` |
**幂等性**:恢复成功后,车辆进度的 `currentOrderId` 会被链式回调清空并替换为下一段订单 ID。前端再传旧 `currentOrderId` 会触发"订单与测试计划/车辆不匹配"。
---
## 三、前端调用前置条件
调用 `manualResume` 之前,必须确认以下信息(建议从 `vehicleProgress` 接口获取):
1. 当前批次状态为 RUNNING(批次已取消则恢复无意义)
2. 目标车辆 `status` = `RUNNING`(COMPLETED / ERROR / CANCELLED 不能恢复)
3. 当前 `currentOrderId` 真实存在且确实卡死(建议先与现场人员确认)
---
## 四、推荐 UI 交互
### 入口位置
批量测试详情页 → 车辆进度列表 → 每行操作列。
### 显示条件
仅当 **`status === RUNNING`** 时显示「故障恢复」按钮。
### 二次确认弹窗
执行前必须强提示用户:
> ⚠️ 故障恢复将跳过订单 `{currentOrderId}`,并立即派发该车辆的下一段订单。
> - 此操作**仅适用于车辆故障已修复、旧订单确认无法继续的场景**
> - 旧订单将被标记为「已取消」,**不会**通知车端
> - 操作前请确认车端已不再执行旧订单,否则可能与新订单冲突
>
> 是否继续?
### 调用示例(伪代码)
```typescript
// services/testPlan.ts
export const manualResume = (params: {
  testPlanId: number | string
  vehicleId: number | string
  currentOrderId: number | string
}) => request.post('/api/v1/xp-test-plan/manualResume', null, { params })
// 组件调用
const handleManualResume = async (row: VehicleProgressVO) => {
  await Modal.confirm({ /* 上面的二次确认文案 */ })
  const res = await manualResume({
    testPlanId,
    vehicleId: row.vehicleId,
    currentOrderId: row.currentOrderId,
  })
  if (res.code === 200) {
    message.success('已触发故障恢复')
    refreshVehicleProgress() // 刷新车辆进度列表
  } else {
    message.error(res.msg)
  }
}
```
---
## 五、相关接口(对接时可能用到)
完整批量测试模块可参考下表,**前端根据现有页面已有的对接情况选择性使用**:
| 接口 | Method | 路径 | 主要作用 |
| --- | --- | --- | --- |
| 启动批量 | POST | `/api/v1/xp-test-plan/batchStart` | 启动批量测试,参数:`{testPlanId, lapCount}` |
| 批次进度 | GET | `/api/v1/xp-test-plan/batchProgress?testPlanId=` | 总进度(创建/完成/异常数 + 状态) |
| 车辆进度 | GET | `/api/v1/xp-test-plan/vehicleProgress?batchId=` | **故障恢复按钮所在列表的数据来源** |
| 重试失败 | POST | `/api/v1/xp-test-plan/batchRetry?testPlanId=` | 批次结束后重跑 ERROR 车辆 |
| **故障恢复** | **POST** | **`/api/v1/xp-test-plan/manualResume`** | **本次新增** |
| 取消批次 | POST | `/api/v1/xp-test-plan/batchCancel?testPlanId=` | 强制中止整个批次 |
### `VehicleProgressVO` 字段(车辆进度列表项)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `vehicleId` | Long | 车辆 ID |
| `plateNumber` | String | 车牌号 |
| `currentLap` | Integer | 当前圈数 |
| `currentSegment` | Integer | 当前路线段索引 |
| `currentOrderId` | Long | **当前进行中订单 ID**(manualResume 必传) |
| `totalLaps` | Integer | 总圈数 |
| `totalSegments` | Integer | 每圈路线段数 |
| `status` | String | `RUNNING` / `COMPLETED` / `ERROR` / `CANCELLED` |
| `errorMsg` | String | 错误信息(仅 ERROR 状态有值) |
### `BatchProgressVO` 字段(批次进度)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `batchId` | Long | 批次 ID |
| `totalCount` | Integer | 预计总订单数 = 车辆数 × 段数 × 圈数 |
| `createdCount` | Integer | 已创建数 |
| `completedCount` | Integer | 已完结数(**含手动恢复跳过的订单**) |
| `errorCount` | Integer | 异常数 |
| `status` | String | `RUNNING` / `SUCCESS` / `PARTIAL_SUCCESS` / `FAILED` / `CANCELLED` |
| `startTime` | DateTime | 开始时间 |
| `endTime` | DateTime | 结束时间 |
---
## 六、注意事项与对接 FAQ
**Q1:故障恢复后,统计数字会怎么变化?**
A:`completedCount` 会 +1(与正常订单完成等同处理),`createdCount` 在派发下一段后再 +1。**前端无需特殊展示"恢复"标记**,统计接口本身不区分。如需区分需另开发审计字段,本期未做。
**Q2:手动恢复成功后是否需要前端轮询?**
A:建议保持现有的车辆进度轮询节奏即可(一般 2-5s 一次),无需为本接口单独加轮询逻辑。后端动作是同步的,接口返回成功即代表"标记取消 + 链式回调推进"已执行完毕。
**Q3:能否在批次已 CANCELLED 后调用?**
A:不能。批次取消后会写入 Redis 取消标记,链式回调内部自动拦截。但前端最好在 UI 层就根据 `status` 隐藏按钮,避免误触发。
**Q4:连续点击多次会不会重复推进?**
A:不会。`currentOrderId` 校验天然幂等——第二次调用时旧 orderId 已不在进度表,会返回"订单与测试计划/车辆不匹配"。
**Q5:恢复失败后该怎么办?**
A:根据 `msg` 提示分情况:
- "未找到订单"/"不匹配" → 数据已变化,先刷新进度列表
- "非运行中" → 该车辆已 ERROR/CANCELLED,使用 `batchRetry` 而非本接口
- 网络/系统异常 → 建议提示用户稍后重试
**Q6:是否需要权限控制?**
A:与 `batchStart`/`batchCancel` 一致,已通过后端 `@AdminOperationLog` 记录操作日志。前端按现有运营管理后台的权限策略管控按钮即可。