# 问题处理文档 - 执行中心顶部状态卡在"运行中"

> **文档类型**: 问题处理文档
> **创建日期**: 2026-07-15
> **作者**: czj
> **优先级**: P1
> **状态**: 待修复

---

## 一、问题描述

### 1.1 现象

执行中心页面顶部显示"当前执行"状态卡片，执行任务已经完成（列表中状态为"已完成"），但顶部卡片仍然显示"运行中"，进度条持续流动。

### 1.2 复现步骤

1. 在用例管理页面勾选用例，点击"执行选中"
2. 跳转到执行中心，顶部显示"当前执行"卡片，状态"运行中"
3. 执行完成后，**列表中该执行记录状态已变为"已完成"**
4. **但顶部卡片仍显示"运行中"，进度条仍在流动**

### 1.3 影响范围

- 执行中心页面顶部状态卡片显示错误
- 用户误以为执行仍在进行中
- 需要手动刷新页面才能消除

---

## 二、根因分析

### 2.1 状态更新流程

```
后端执行完成
  → 广播 execution_complete 事件
  → 前端 WebSocket 收到
  → activeExecution.value = data（更新为 completed）
  → disconnectWebSocket()
  → loadExecutions()（刷新列表）
```

### 2.2 Bug 根因

**问题在于 `activeExecution` 的状态没有主动轮询刷新机制**。以下场景会导致状态卡在"运行中"：

| 场景 | 原因 |
|------|------|
| WebSocket 断连未收到 `execution_complete` | 网络波动、代理超时等导致 WebSocket 在执行完成前断开 |
| 页面刷新后重新加载 | `onMounted` 只在列表中找 `running` 的执行设为 `activeExecution`，但如果执行已完成则不设置，`activeExecution` 为 `null`——这种情况表现正常（不显示卡片），但如果 `activeExecution` 已被设置为 running 状态后，页面刷新前没来得及清除 |
| WebSocket `execution_complete` 事件丢失 | 服务端广播时客户端短暂断连 |
| `activeExecution` 被设置后不再更新 | 前端只在 `execution_complete`/`execution_cancelled` 事件中更新 `activeExecution`，如果没收到事件就永远不更新 |

**核心缺陷**：前端没有主动轮询 `activeExecution` 对应执行记录的最新状态的机制。一旦 WebSocket 事件丢失，`activeExecution` 就永远停留在旧状态。

### 2.3 代码追踪

**`Execution.vue` 第 557-562 行** — 页面加载时只关注 running 状态：

```typescript
const running = executions.value.find(e => e.status === 'running')
if (running) {
  activeExecution.value = running
  connectWebSocket(running.id)
}
```

**`Execution.vue` 第 778-783 行** — WebSocket 收到完成事件才更新：

```typescript
wsClient.on('execution_complete', (data: any) => {
  activeExecution.value = data    // ← 只有收到事件才更新
  disconnectWebSocket()
  loadExecutions()
})
```

**`Execution.vue` 无主动刷新 `activeExecution` 的逻辑** — 如果 WebSocket 断连，没有任何定时器或轮询来检查执行状态是否已变化。

---

## 三、修复方案

### 3.1 方案：增加定时轮询刷新 activeExecution

当 `activeExecution` 存在且状态为 `running` 时，启动一个定时器，每隔 N 秒主动查询该执行记录的最新状态：

1. 如果后端返回状态已变为 `completed`/`cancelled`/`failed`，更新 `activeExecution` 并断开 WebSocket
2. 如果后端返回状态仍为 `running`，继续保持轮询
3. WebSocket 正常收到 `execution_complete` 时，清除轮询（已有的逻辑）

**改动文件**：`frontend/src/views/Execution.vue`

**改动点**：

1. 新增 `activePollTimer` 变量，存储轮询定时器 ID
2. 修改 `connectWebSocket`，在连接 WebSocket 的同时启动轮询（双重保障）
3. 新增 `pollActiveExecution` 方法，定时查询执行状态
4. 修改 `disconnectWebSocket`，同时清除轮询定时器
5. `onUnmounted` 清除定时器（已有，需确认）

### 3.2 轮询参数

| 参数 | 值 | 说明 |
|------|------|------|
| 轮询间隔 | 5 秒 | 平衡实时性与请求量 |
| 触发条件 | `activeExecution` 存在且 `status === 'running'` | 只在运行中才轮询 |
| 停止条件 | 状态变为非 `running`，或组件卸载，或手动断开 | 多重保障 |

---

## 四、验收标准

| 测试项 | 预期结果 |
|--------|----------|
| 正常执行完成（WebSocket 正常） | 顶部状态卡片显示"已完成"，与列表一致 |
| WebSocket 断连后执行完成 | 5 秒内轮询检测到状态变化，顶部卡片更新为"已完成" |
| 页面刷新时执行已完成 | 顶部不显示运行中卡片（列表中无 running 记录） |
| 执行被取消 | 顶部状态卡片显示"已取消" |
| 执行仍在进行中 | 轮询不干扰正常执行，顶部保持"运行中" |
| 组件卸载 | 轮询定时器被清除，无内存泄漏 |

---

## 五、相关文件

- `frontend/src/views/Execution.vue` — 执行中心页面（改动点）
- `frontend/src/utils/websocket.ts` — WebSocket 客户端
- `backend/app/services/execution_service.py` — 执行服务（广播逻辑）

---

*本文档由 Claude Code 生成，遵循项目问题处理文档规范。*
