| docs/virtualOrderFeat.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| package-lock.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/service/api/copyText.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/testDemo/testPlanList/add.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
docs/virtualOrderFeat.md
@@ -44,7 +44,7 @@ | --- | --- | | 订单 ID 必须能在进度表中查到 | `未找到订单对应的车辆进度: {orderId}` | | 订单所属计划/车辆必须与传入参数一致 | `订单与测试计划/车辆不匹配,无法恢复` | | 车辆进度状态必须是 RUNNING(0) | `车辆进度非运行中,无法恢复` | | 车辆进度状态必须是 RUNNING(0) 或 ERROR(2) | `车辆进度状态不允许恢复(仅RUNNING/ERROR可操作)` | **幂等性**:恢复成功后,车辆进度的 `currentOrderId` 会被链式回调清空并替换为下一段订单 ID。前端再传旧 `currentOrderId` 会触发"订单与测试计划/车辆不匹配"。 @@ -55,7 +55,7 @@ 调用 `manualResume` 之前,必须确认以下信息(建议从 `vehicleProgress` 接口获取): 1. 当前批次状态为 RUNNING(批次已取消则恢复无意义) 2. 目标车辆 `status` = `RUNNING`(COMPLETED / ERROR / CANCELLED 不能恢复) 2. 目标车辆 `status` = `RUNNING` 或 `ERROR`(COMPLETED / CANCELLED 不能恢复) 3. 当前 `currentOrderId` 真实存在且确实卡死(建议先与现场人员确认) --- @@ -66,15 +66,16 @@ 批量测试详情页 → 车辆进度列表 → 每行操作列。 ### 显示条件 仅当 **`status === RUNNING`** 时显示「故障恢复」按钮。 当 **`status === RUNNING` 或 `status === ERROR`** 时显示「故障恢复」按钮。 ### 二次确认弹窗 执行前必须强提示用户: > ⚠️ 故障恢复将跳过订单 `{currentOrderId}`,并立即派发该车辆的下一段订单。 > - 此操作**仅适用于车辆故障已修复、旧订单确认无法继续的场景** > - 此操作**适用于车辆故障已修复、旧订单确认无法继续的场景**(RUNNING 卡死或 ERROR 需重跑) > - 旧订单将被标记为「已取消」,**不会**通知车端 > - 操作前请确认车端已不再执行旧订单,否则可能与新订单冲突 > - ERROR 状态的车辆执行后将重新进入 RUNNING 并派发下一段订单 > > 是否继续? @@ -149,6 +150,38 @@ --- ## 五-B、后端改动要求(本次设计变更) > **变更**:故障恢复接口同时支持 `RUNNING` 和 `ERROR` 状态的车辆。 后端 `manualResume` 方法需调整: 1. 状态校验放宽:`status == RUNNING || status == ERROR` 均允许通过 2. ERROR 状态的车辆恢复时,需先将进度状态重置为 `RUNNING`(同 `batchRetry` 逻辑),再执行链式回调推进 3. 错误信息清空:`errorMsg` 置 null ```java // 原校验 if (progress.getStatus() == null || progress.getStatus() != VEHICLE_STATUS_RUNNING) { throw new RuntimeException("车辆进度非运行中,无法恢复"); } // 改为 if (progress.getStatus() == null || (progress.getStatus() != VEHICLE_STATUS_RUNNING && progress.getStatus() != VEHICLE_STATUS_ERROR)) { throw new RuntimeException("车辆进度状态不允许恢复(仅RUNNING/ERROR可操作)"); } // ERROR 状态需先重置 if (progress.getStatus() == VEHICLE_STATUS_ERROR) { progress.setStatus(VEHICLE_STATUS_RUNNING); progress.setErrorMsg(null); progress.setUpdateTime(LocalDateTime.now()); vehicleProgressMapper.updateVehicleProgress(progress); } ``` --- ## 六、注意事项与对接 FAQ **Q1:故障恢复后,统计数字会怎么变化?** @@ -166,7 +199,7 @@ **Q5:恢复失败后该怎么办?** A:根据 `msg` 提示分情况: - "未找到订单"/"不匹配" → 数据已变化,先刷新进度列表 - "非运行中" → 该车辆已 ERROR/CANCELLED,使用 `batchRetry` 而非本接口 - "车辆进度状态不允许恢复" → 该车辆已 COMPLETED/CANCELLED,无法恢复 - 网络/系统异常 → 建议提示用户稍后重试 **Q6:是否需要权限控制?** package-lock.json
Diff too large src/service/api/copyText.ts
@@ -233,3 +233,8 @@ export function batchCancelTestPlan(testPlanId: number | string) { return request.Post<Service.ResponseResult<any>>('/operation/api/v1/xp-test-plan/batchCancel', null, { params: { testPlanId } }) } /** 批量测试 - 故障车辆手动恢复 */ export function manualResumeVehicle(params: { testPlanId: number | string; vehicleId: number | string; currentOrderId: number | string }) { return request.Post<Service.ResponseResult<any>>('/operation/api/v1/xp-test-plan/manualResume', null, { params }) } src/views/testDemo/testPlanList/add.vue
@@ -20,6 +20,7 @@ fetchTestOrderPageList, fetchTestPlanDetail, fetchVehicleProgress, manualResumeVehicle, reassignTestOrderById, } from '@/service/api/copyText' import { getAvailableVehicleList } from '@/service/api/transaction' @@ -1030,7 +1031,7 @@ }) } const vehicleProgressTableScrollX = 960 const vehicleProgressTableScrollX = 1060 const vehicleProgressColumns = computed<DataTableColumns<any>>(() => [ { title: '序号', @@ -1081,8 +1082,63 @@ ellipsis: true, render: row => row.errorMsg || '-', }, { title: '操作', key: 'actions', width: 100, align: 'left', fixed: 'right' as const, render: (row: any) => { if ((row.status === 'RUNNING' || row.status === 'ERROR') && row.currentOrderId && isBatchRunning.value) { return ( <n-button text type="warning" size="small" onClick={() => handleManualResume(row)}> 故障恢复 </n-button> ) } return '-' }, }, ]) const manualResumeLoading = ref(false) function handleManualResume(row: any) { if (manualResumeLoading.value) return if (!row.currentOrderId) { message.warning('当前订单ID为空,请刷新后重试') return } dialog.warning({ title: '故障恢复确认', content: `⚠️ 故障恢复将跳过订单 ${row.currentOrderId},并立即派发该车辆的下一段订单。\n\n• 此操作适用于车辆故障已修复、旧订单确认无法继续的场景\n• 旧订单将被标记为「已取消」,不会通知车端\n• 操作前请确认车端已不再执行旧订单,否则可能与新订单冲突\n• ERROR 状态的车辆执行后将重新进入 RUNNING 并派发下一段订单\n\n是否继续?`, positiveText: '确定', negativeText: '取消', maskClosable: false, autoFocus: false, onPositiveClick: async () => { manualResumeLoading.value = true try { const res = await manualResumeVehicle({ testPlanId: planId.value, vehicleId: row.vehicleId, currentOrderId: row.currentOrderId, }) if (res.code === 200) { message.success('已触发故障恢复') await refreshBatchData() } else { message.error(res?.message || res?.msg || '故障恢复失败') } } finally { manualResumeLoading.value = false } }, }) } // 监听批次状态,自动停止轮询 watch(() => batchProgress.value?.status, (status) => { if (status && status !== 'RUNNING') {