# HANDOFF_性能测试

> 生成时间：2026-08-12 15:00
> 最后更新：2026-08-18 00:30
> 当前分支：`platform-auto-test`
> 最近提交：`f3035587 feat: 点击策略优化/性能测试A0027修复/报告模板与设备模拟增强`
> 主分支：`master`

---

## 整体状态

| 阶段 | 状态 | 说明 |
|------|------|------|
| Phase 1 - 数据模型 + Schema | ✅ 完成 | ORM 模型 + Pydantic 校验 |
| Phase 2 - 压测执行引擎 | ✅ 完成 | 轻量级 asyncio + aiohttp 自研引擎 |
| Phase 3 - 后端路由 + Service | ✅ 完成 | 11 个 API 路由 + Service 层 |
| Phase 4 - 前端页面 | ✅ 完成 | 任务管理/监控/报告 三个面板 |
| Phase 5 - 集成测试 | ✅ 完成核心 | 真实 API（会议预约）端到端验证通过（2026-08-17） |
| Phase 6 - 响应捕获 + 跨任务引用 | ✅ 完成核心 | capture_rules + performance_task_outputs + REF 占位符（2026-08-17） |

---

## 🆕 最新会话进度（2026-08-17）

### 本次会话完成的核心工作

#### 1. 「新建会议压测」任务端到端验证通过 ✅

**任务**：`perf_5a1a3b4e00e04b2f96713f4108580d2b`（新建会议压测）
**目标接口**：`PUT https://192.168.5.44/meetingV3/api/message/book`（JWT + X-RANDOM/X-TIMESTAMP/X-SIGN 签名）
**最终配置**：concurrency=2（用户要求 2 线程快速验证）/ duration=15s / rampUp=0 / 时段 +37H~+38H / duration字段=60

**最终验证结果（干净代码，调试日志已清除）**：

| 指标 | 值 |
|------|-----|
| 总请求数 | 169 |
| 成功数 | 169（HTTP 200） |
| 失败数 | 0 |
| 错误率 | 0.0% |
| 实际 TPS | 10.96 |
| 平均响应时间 | 178.41ms（min 145 / max 231） |
| **message_ids 捕获** | ✅ `['2026-0817013311822', '2026-0817013311824']` |

捕获输出 API 验证：`GET /api/performance/tasks/{id}/outputs` → `{"total":1,"items":[{"key":"message_ids","value":[...]}]}`

#### 2. A0027「时间格式不正确」根因定位与修复 ✅（用户反馈的问题）

**现象**：服务日志持续打印 `body={"success":false,"code":"A0027","message":"时间格式不正确"}`，但 HTTP 状态 200 → 统计全"成功"，捕获为空。

**根因（双层）**：
1. **服务器上的 `template_resolver.py` 是旧版本**：`_resolve_token()` 对所有 token 用 `token.split(":")` 分割，而任务占位符是 `NOW_+30_M`（下划线分隔）→ `kind="NOW_+30_M"` 不匹配 `"NOW"` → 占位符**原样发送**（字面量 `{__NOW_+30_M__}`）→ API 返回 A0027。
   - 正确版本（本地）：REF 用 `:` 分割，其余（NOW/RANDOM_NAME/RANDOM_INT/UUID/INDEX）用 `_` 分割。
2. **stale `.pyc` 字节码缓存**：修复源码后容器仍加载旧字节码（`__pycache__/template_resolver.cpython-310.pyc` mtime 早于源码修改时间），必须删除 pyc + 重启容器。

**修复步骤（5.60 服务器）**：
```bash
# 1. 通过 paramiko base64 部署正确的 template_resolver.py（本地版本）
# 2. 删除字节码缓存
docker exec plat-auto-test-app sh -c "find /app -name '*.pyc' -delete"
# 3. 重启容器
docker restart plat-auto-test-app
# 4. 验证（容器内）
python3 /tmp/test_tr.py   # 输出 NOW: 2026-08-17 12:59:09（带秒）✓
```

**修复后 DBG_RESOLVE 日志确认**：`startTime: '2026-08-17 13:30:02'`（秒精度，占位符已替换）

#### 3. 会议预约 API 业务限制探测（重要经验）

探测脚本（本地 backend/meeting_test25.py 等）验证会议室 `29dcd33aa47797e7d29d79ae6711d597` 的规则：

| 错误码 | 含义 | 触发条件 | 规避方法 |
|--------|------|---------|---------|
| A0027 | 时间格式不正确 | startTime/endTime 非秒精度或占位符未解析 | 修复 resolver（已完成） |
| A0023 | 该时间段已有会议 | 目标时段与已有会议重叠 | 选无人占用时段（如 +37H 凌晨）；**注意探测脚本自己创建的会议也会占用时段** |
| A0026 | 超过会议室可预订的最大时长 | body 中 `duration=120`（该会议室上限 90 分钟） | `duration` ≤ 90 且与 start/end 跨度一致 |
| B0001 | failure | `cnum=""`（不指定会议室） | 必须携带有效会议室 ID |

**预约接口 body 必需字段**：JSON **数组** `[{...}]`、`convergentMediaTypes:"1"`、`companyNumber:"CN-WQF-UBAINS"`（JWT 声明一致）、`uid:8`（数字）、`configValueId:2`（数字）、有效 `cnum`、`participantList:[]`、秒精度时间。

#### 4. 压测任务设计注意事项（同一时段冲突问题）

**发现**：concurrent 模式下所有并发请求解析出**相同的 startTime/endTime**（`{__NOW_+37_H__}` 对所有请求解析结果相同）→ 第一批请求订走时段后，后续请求全部 A0023。

**当前状态**：2 并发时前 2 个请求成功（message_ids 捕获 2 个），其余 A0023（HTTP 200，统计不报错）。

**待优化方案**：给模板解析器增加按请求序号偏移时间的语法，例如 `{__NOW_+37_H_INDEX__}`（每请求 +1 分钟错开时段），需要在 `template_resolver.py` 的 `_resolve_token` 中处理 `parts[3] == "INDEX"` 分支（尚未实现）。

### 响应捕获（capture_rules）链路（本次已全链路验证）

```
任务 capture_rules（JSON）
  → executor._capture_response(rules, body_text, status)   # JSONPath $.result[*].messageId 提取
  → self._captured.setdefault(key, []).append(value)         # _capture_max=10000 上限
  → result["captured"]（execute() finally 块）
  → service._save_outputs(task_id, captured)                 # upsert 到 performance_task_outputs 表
  → GET /api/performance/tasks/{id}/outputs                  # 前端/下游任务读取
```

- 捕获仅在 HTTP 2xx 时执行；`_save_outputs` 空捕获时 early return（不写库）
- `PerformanceTaskOutput` 表（MySQL `performance_task_outputs`）按 task_id+key upsert
- 新增文件：`models/performance_output.py` / `routers/performance_output.py` / `schemas/performance_output.py`
- service 侧 `_save_outputs` 在 `run_task` 的 finally 中调用（此前服务器旧版 service 缺此函数，已部署本地新版修复）

### 服务器（192.168.5.60）部署情况

- 容器：`plat-auto-test-app`（volume mount `/data/third_party/plat-auto-test/backend:/app`，内部端口 80）
- **本次部署的文件**（paramiko + base64 方式，scp 不可用）：
  - `app/executors/template_resolver.py` — 正确的 `_` 分割版本（修复 A0027）
  - `app/services/performance_service.py` — 含 `_save_outputs` 的新版
  - `app/executors/performance_executor.py` — 已含捕获逻辑，临时加过 DBG_RESOLVE/DBG_CAPTURE 调试日志，**现已清除**（验证 grep DBG = 0）
- **部署后处理**：删除全部 `*.pyc` + 重启容器（否则旧字节码仍生效）
- 调试日志补丁模式（本次使用后已移除）：
  - `[DBG_RESOLVE]` 在 `_resolve_body` 的 resolve() 调用后打印解析结果
  - `[DBG_CAPTURE]` 在 `_capture_response` 开头打印规则/状态/响应体前 200 字符

### 本地会话遗留状态

- 本地 backend 以 `python -m uvicorn app.main:app --port 8001`（无 --reload）方式运行，日志 `/tmp/backend_8001.log`
- 本地验证脚本：`backend/meeting_test22.py` ~ `meeting_test25.py`（探测 API 最小必需字段/时段/时长限制，可复用）
- 本地任务 `perf_f27debe671d84b4fb3908af5beac67ed`（同名任务）尚未做完整验证（服务器版已验证通过）

**后端代码行数**：2173 行（executor 748 + service 588 + router 296 + model 279 + schema 262）
**前端代码行数**：1768 行（index 98 + TaskList 476 + MonitorPanel 459 + ReportPanel 373 + api 121 + types 241）

---

## 多窗口并行开发注意

如需同时开多个 Claude Code 窗口开发不同子菜单功能，请先阅读 `Docs/多窗口并行开发指南.md`。

性能测试模块的端口与文件：
- 后端：8001（默认）/ 8002（当前可用）
- 前端：3000
- 数据库：`data/test_platform.db`（共享同一个 SQLite）
- Git 分支：`platform-auto-test`

---

## 已完成事项

### 后端（Phase 1-3）

#### 1. 数据模型 (`backend/app/models/performance.py`)

| 模型 | 表名 | 说明 |
|------|------|------|
| `PerformanceTask` | `performance_tasks` | 任务主表（名称/URL/方法/模式/并发/时长/状态/认证/断言） |
| `PerformanceSnapshot` | `performance_snapshots` | 每秒快照（TPS/响应时间P50/P90/P99/状态码分布） |
| `PerformanceTaskResult` | `performance_task_results` | 最终结果（汇总指标 + JSON 详情） |

**关键字段**：
- `PerformanceTask.mode` → `concurrent` / `qps` / `step`
- `PerformanceTask.status` → `pending` / `running` / `completed` / `failed` / `cancelled`
- `PerformanceTask.account_key` → `superadmin` / `admin` / `user`
- `PerformanceSnapshot` 使用 camelCase 别名（`p50_response_time` → `p50` / `p90` / `p99`）

#### 2. Pydantic Schema (`backend/app/schemas/performance.py`)

- 全部 schema 使用 `populate_by_name=True, alias_generator=to_camel` 实现 camelCase 兼容
- 断言规则：`AssertionRule`（status_code / response_time / response_body + JSON Path）
- 请求/响应结构：`PerformanceTaskCreate` / `PerformanceTaskUpdate` / `PerformanceTaskResponse` / `PerformanceTaskListItem` / `PerformanceTaskListResponse`
- 执行相关：`PerformanceRunRequest` / `PerformanceRunResponse`
- 数据：`PerformanceSnapshotResponse` / `PerformanceSnapshotListResponse`
- 报告：`PerformanceReportResponse`（含 `summary` / `metrics` / `snapshots`）

#### 3. 压测执行引擎 (`backend/app/executors/performance_executor.py`)

**核心架构**：
- `MetricsCollector` — 线程安全指标采集器（带锁的 period 累积 + 全局累加）
  - `record()` 记录单次请求结果（响应时间 + 状态码 + 错误）
  - `snapshot()` 生成每秒快照（TPS / P50/P90/P99 / 状态码分布）
  - `summary()` 汇总最终指标
  - `snapshot_callback` 实时推送（WebSocket 每 1 秒回调）
- `ResultValidator` — 三种断言类型（status_code / response_time / response_body JSON Path）
- `PerformanceExecutor` — 主执行器
  - `execute()` 入口：Token 获取 → 并发执行 → 指标采集 → 断言验证
  - 使用 `HttpClient`（`app.executors.http_client`）获取 Token
  - 三种模式：
    - **concurrent**：固定并发数，持续 duration 秒
    - **qps**：固定 QPS 限流，持续 duration 秒
    - **step**：阶梯并发，每 step_duration 秒递增 step_concurrency
  - 通过 `asyncio.Event` 实现优雅停止
  - 预热阶段（ramp_up）逐步增加并发

**运行模式（Windows 兼容）**：
- 引擎运行在独立线程中
- `asyncio.set_event_loop(None)` → `new_event_loop()` → `run_until_complete(executor.execute())`
- 复用 `playwright_executor.py` 已验证的 Windows 兼容模式

#### 4. Service 层 (`backend/app/services/performance_service.py`)

**CRUD 操作**：
- `create_task()` / `get_task()` / `list_tasks()` / `update_task()` / `delete_task()`

**执行控制**：
- `run_task()` — 启动执行（`_run_sync` 线程池模式）
- `stop_task()` — 发送停止信号（`asyncio.Event.set()`）
- `_run_sync()` — 线程内执行完整流程（登录 → 执行 → 保存快照/结果 → 更新状态）
- `_running_executors: Dict[str, PerformanceExecutor]` 类缓存跟踪运行中任务

**数据查询**：
- `get_snapshots()` / `get_report()` / `get_task_results()`

**认证配置**：
```python
_DEFAULT_BASE_URL = "https://192.168.5.44"
_DEFAULT_ACCOUNTS = {
    "superadmin": {"username": "admin@xty", "password": "Ubains@13579"},
    "admin": {"username": "admin@xty", "password": "Ubains@13579"},
    "user": {"username": "admin@xty", "password": "Ubains@13579"},
}
```
- 验证码固定 `csba`
- 认证路径：`/platform/api/auth/login`
- 验证码路径：`/platform/api/code`
- Token 通过 `HttpClient.login(account_key)` 获取 → 注入请求头

#### 5. API 路由 (`backend/app/routers/performance.py`)

前缀 `/api/performance`，注册于 `main.py` 第 216-219 行，tags `["性能测试"]`

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/performance/tasks` | 任务列表（分页 + 状态过滤） |
| POST | `/api/performance/tasks` | 创建任务 |
| GET | `/api/performance/tasks/{task_id}` | 获取单个任务 |
| PUT | `/api/performance/tasks/{task_id}` | 更新任务 |
| DELETE | `/api/performance/tasks/{task_id}` | 删除任务 |
| POST | `/api/performance/tasks/{task_id}/run` | 执行任务 |
| POST | `/api/performance/tasks/{task_id}/stop` | 停止任务 |
| GET | `/api/performance/tasks/{task_id}/snapshots` | 获取快照列表 |
| GET | `/api/performance/tasks/{task_id}/report` | 获取报告 |
| GET | `/api/performance/tasks/{task_id}/results` | 获取执行结果 |
| WS | `/api/performance/ws/{task_id}` | 实时监控 WebSocket |

**WebSocket**：
- 端点：`/api/performance/ws/{task_id}`
- 分组名：`perf_{task_id}`
- 复用 `app/websocket/manager.py` 的 `ConnectionManager`
- 推送格式：执行器每秒回调的 JSON 快照

#### 6. 后端注册 (`backend/app/main.py`)

```python
from app.routers import performance as performance_router
# ...
app.include_router(performance_router.router, prefix="/api/performance", tags=["性能测试"])
```

### 前端（Phase 4）

#### 1. 路由 (`frontend/src/router/index.ts`)

```
/performance → Performance index.vue (懒加载)
/cases/performance → redirect /performance
/execution/performance → redirect /performance
/reports/performance → redirect /performance
```

#### 2. 菜单 (`frontend/src/App.vue`)

三个侧边栏菜单项指向 `/performance`：
- 用例管理 → 性能测试
- 执行中心 → 性能测试
- 报告中心 → 性能测试

#### 3. 主容器 (`frontend/src/views/performance/index.vue`)

三个 el-tabs：任务管理 / 执行监控 / 报告查看
- 状态管理：`monitorTaskId`、`reportTaskId`
- Tab 切换时联动（点击"监控"→ 切换到监控 tab 并传入 taskId）
- 通过 `defineExpose` 暴露 `loadReport()` / `refresh()`

#### 4. 任务列表 (`frontend/src/views/performance/TaskList.vue`)

- el-table 展示：名称/URL/方法/模式/并发/时长/状态/TPS/错误率/创建时间
- 操作列：执行/停止/监控/报告/编辑/删除
- 状态过滤下拉框 + 分页
- 创建/编辑弹窗（el-dialog）：名称/URL/方法/模式/并发/时长/预热/QPS目标/阶梯并发/请求头/请求体/认证开关/账号选择/描述
- 表单校验：名称/URL/方法/模式 必填
- `defineEmits<{ viewMonitor: [taskId: string]; viewReport: [taskId: string] }>()`

#### 5. 实时监控 (`frontend/src/views/performance/MonitorPanel.vue`)

- WebSocket 实时连接（`createPerfMonitorStream`）
- 三张 ECharts 图表：TPS 趋势（折线图 + 面积渐变）/ 响应时间趋势（平均/P50/P90/P99 四条线）/ 状态码分布（环形饼图）
- 顶部 4 个指标卡片：当前 TPS / 平均响应时间 / 总请求数 / 错误率
- 连接状态指示器（绿/黄/灰圆点）
- 数据缓冲区上限 300 点，先进先出
- 自动断开处理 + 错误提示

#### 6. 报告查看 (`frontend/src/views/performance/ReportPanel.vue`)

- 执行摘要（el-descriptions：任务名/URL/方法/模式/并发数/账号/开始/结束/实际时长）
- 核心指标卡片（8 个）：总请求数/成功数/失败数/错误率/TPS/平均/最小/最大响应时间
- 响应时间分位数表（P50/P90/P99）
- 状态码分布表（2xx/3xx/4xx/5xx + 数量 + 占比）
- TPS 趋势图 + 响应时间趋势图（ECharts）
- 导出 JSON 功能

#### 7. API 封装 (`frontend/src/api/performance.ts`)

```typescript
BASE = '/api/performance'
listTasks(params) → GET /tasks
getTask(id) → GET /tasks/{id}
createTask(data) → POST /tasks
updateTask(id, data) → PUT /tasks/{id}
deleteTask(id) → DELETE /tasks/{id}
runTask(id) → POST /tasks/{id}/run
stopTask(id) → POST /tasks/{id}/stop
getSnapshots(id) → GET /tasks/{id}/snapshots
getReport(id) → GET /tasks/{id}/report
getTaskResults(id) → GET /tasks/{id}/results
createPerfMonitorStream(id, onSnapshot, onComplete, onError, onClose) → WebSocket
```

#### 8. 类型定义 (`frontend/src/types/performance.ts`)

所有接口类型（camelCase 风格）：
- `PerformanceTask` / `PerformanceTaskListItem` / `PerformanceTaskCreate`
- `PerformanceSnapshot` / `PerformanceSnapshotListResponse`
- `PerformanceReportResponse` / `PerformanceReportSummary` / `PerformanceReportMetrics`
- `AssertionRule`
- `PerformanceRunRequest` / `PerformanceRunResponse`

#### 9. 前端构建验证

```bash
cd frontend && npm run build
# ✓ 构建成功 (34.48s), 无 TypeScript 错误
```

---

## 错误修复记录

### 1. 后端 404 / OpenAPI 不显示性能测试路由

**现象**：访问 `http://localhost:8001/docs` 看不到性能测试 API，`/api/performance/tasks` 返回 404

**根因**：运行在 8001 端口的 uvicorn 是**旧进程**（PID 10912，启动于 2026-08-11 15:50），早于性能模块代码的编写时间。代码中的 `performance` 路由注册在 `main.py` 中，但旧进程加载的是旧代码。

**验证**：通过 Python 直接 import 确认路由注册正确：
```python
from app.main import app
routes = [r.path for r in app.routes if "/api/performance" in str(r.path)]
# 输出: 11 条路由，全部正确
```

**修复**：尝试 kill 旧进程失败（access denied / not found），改用端口 8002 启动新后端。已验证 8002 正常工作。

### 2. 无法 kill 旧 uvicorn 进程

**尝试过的方法**（全部失败）：
- `taskkill /F /PID 10912` → 访问被拒绝
- `taskkill /F /PID 27100` → 未找到进程
- PowerShell `Stop-Process` → not found
- `kill -9` → no such process
- `os.kill(pid, 9)` → WinError 5 拒绝访问
- `wmic` → not found in bash

**原因**：进程由不同上下文的用户启动（可能是通过 Windows 服务或 Administrator 启动的），当前 bash 会话无权限操作。

**当前解决**：使用端口 8002 启动新后端：
```bash
cd backend && uvicorn app.main:app --reload --port 8002
```

### 3. 前端 TS 类型错误（本会话前已修复）

- TS6133: `emit` 未使用 → 移除
- TS2339: `tasks`/`total` 找不到 → 修复类型定义
- TS2345: `string | null` 不能赋值给 `string` → 修复 `formatTime` 参数类型

### 4. A0027 时间格式不正确（2026-08-17 已修复）

**现象**：会议预约压测全部返回 `{"success":false,"code":"A0027","message":"时间格式不正确"}`，HTTP 200 导致统计显示成功但捕获为空。

**根因**：服务器 `template_resolver.py` 旧版 `_resolve_token()` 用 `token.split(":")` 分割所有 token；任务占位符 `NOW_+30_M` 无冒号 → `kind="NOW_+30_M"` 匹配失败 → 占位符原样发送。本地版本已用 `_` 分割（仅 REF 用 `:`），部署时曾发现更早版本 `_now_str` 为 `%H:%M`（无秒）——两层问题叠加。

**修复**：部署本地正确版 `template_resolver.py` 到 5.60 → 删 `*.pyc` → 重启容器。验证 `_now_str()` → `2026-08-17 12:59:09`。

**踩坑**：
- 源码改了但容器仍旧行为 → stale `__pycache__` 字节码（mtime 早于源码），必须删 pyc + 重启
- 部署方式：paramiko exec + base64（scp 因 UTF-8 内容失败；sshpass 不在 PATH）
- 5.60 容器内部端口是 **80**（不是 8001），`docker port` 查映射

### 5. A0026 超过会议室可预订的最大时长（2026-08-17 定位）

**现象**：任务 body `duration: 120` 时全部返回 A0026。

**根因**：会议室 `29dcd33aa47797e7d29d79ae6711d597` 单次预订上限 **90 分钟**。

**修复**：`duration` 改为 60（与 start/end 跨度一致）。

### 6. A0023 该时间段已有会议（2026-08-17 规避）

**现象**：任务时段与已有会议重叠时全部 A0023。

**根因**：a) 时段本身有真实会议；b) **探测脚本自己创建的会议也占用时段**（探测 + 验证共用同一会议室时极易自我冲突）。

**修复**：改用 +37H/+38H（次日次日凌晨）无占用时段。教训：探测可用性会**污染**日程，探测与正式压测要用不同时段/会议室。

---

## 当前运行状态

### 生产服务器 192.168.5.60（2026-08-17 13:45）
- 容器 `plat-auto-test-app`：healthy，内部端口 80（宿主机 80）
- 部署内容：修复版 `template_resolver.py` + 含 `_save_outputs` 的 `performance_service.py` + 含捕获的 `performance_executor.py`（DBG 调试日志已清除，grep 验证 0 残留）
- 字节码缓存已清理，容器已重启加载新代码
- 任务 `perf_5a1a3b4e00e04b2f96713f4108580d2b` 验证通过（169 请求 / 0 失败 / TPS 10.96 / message_ids 捕获 2 个）
- MySQL 表 `performance_task_outputs` 已存在且有数据

### 本地（Windows 开发机）
- 后端：`python -m uvicorn app.main:app --port 8001`（无 --reload，日志 `/tmp/backend_8001.log`）
- 前端：`http://localhost:3000`
- 本地数据库 `data/test_platform.db` 含同名任务 `perf_f27debe671d84b4fb3908af5beac67ed`（待完整回归）

---

## 未提交的代码

性能测试模块代码尚未 commit。影响文件：

```
# 后端新文件
backend/app/executors/performance_executor.py       (NEW)
backend/app/services/performance_service.py          (NEW)
backend/app/routers/performance.py                   (NEW)
backend/app/models/performance.py                    (NEW)
backend/app/schemas/performance.py                   (NEW)
backend/app/main.py                                  (MODIFIED: 注册 performance router)

# 前端新文件
frontend/src/views/performance/index.vue             (NEW)
frontend/src/views/performance/TaskList.vue          (NEW)
frontend/src/views/performance/MonitorPanel.vue      (NEW)
frontend/src/views/performance/ReportPanel.vue       (NEW)
frontend/src/api/performance.ts                      (NEW)
frontend/src/types/performance.ts                    (NEW)
frontend/src/router/index.ts                         (MODIFIED: 添加 /performance 路由)
frontend/src/App.vue                                 (MODIFIED: 添加侧边栏菜单项)
```

---

## 下一步任务

### Phase 5 - 集成测试（✅ 核心已通过，2026-08-17）

以下环节已在 5.60 服务器 + 真实会议预约 API 上验证通过：
- 创建/更新任务（PUT 更新 concurrency/duration/body）✅
- 执行任务（`{"message":"任务已开始执行"}` → completed）✅
- 捕获输出落库与查询（`/outputs` 返回 message_ids）✅
- 真实 API 认证（JWT + 签名）+ 模板占位符解析 ✅

仍待验证：
- WebSocket `/api/performance/ws/{task_id}` 实时快照推送
- 前端任务列表/监控图表/报告面板对新数据（outputs）的展示
- 本地任务 `perf_f27debe671d84b4fb3908af5beac67ed` 的完整回归

### P0 - 按请求序号错开时间段（并发压测预约类接口的关键）

**问题**：concurrent 模式下所有请求解析出相同 startTime → 第一批订走时段，后续全部 A0023。

**方案**：`template_resolver.py` 的 `_resolve_token()` 增加 `INDEX` 后缀支持，如 `{__NOW_+37_H_INDEX__}`（token parts = `["NOW","+37","H","INDEX"]`），解析时在基准时间上再加 `request_index` 分钟。实现位置：`_resolve_token` 的 NOW 分支处理 `len(parts) > 3 and parts[3] == "INDEX"`，需要新增 `_now_with_offset` 的带 index 变体。实现后任务 body 改用该语法即可全并发成功。

### P1 - 修改/取消会议压测任务

- 修改会议：`PUT /meetingV3/api/message/{...}`（需先拿 book 的 messageId → 已可通过 REF 占位符 `{__REF:task_id:message_ids__}` 引用）
- 取消会议：需探测取消接口路径与 body 格式（`meeting_test*.py` 系列脚本可扩展）

### P2 - 后续优化

- 断言规则验证（`ResultValidator` 执行中启用；注意当前 `success:false` + HTTP 200 会被 status_code 断言判成功，建议增加 response_body/JSONPath 断言 `$.success == true`）
- 更详细的错误处理（Token 过期重试、网络错误分类）
- 报告对比功能（多次执行结果对比）
- 批量导出报告
- 压力测试数据清理（定期清理旧快照；探测脚本创建的真实会议也需清理策略）
- schemas/performance.py 服务器版同步：本地 `body_template` 已放宽为 `Optional[Any]`，服务器 schema 若仍为 `Optional[Dict]` 需同步部署

---

*本文档由 Claude Code 自动生成，用于多会话协作开发时的上下文传递。*