| New file |
| | |
| | | # 批量测试 - 故障车辆手动恢复 前端对接指引 |
| | | |
| | | > 适用版本:`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` 记录操作日志。前端按现有运营管理后台的权限策略管控按钮即可。 |