# HANDOFF — 性能测试模块会话交接文档

> **生成时间**: 2026-08-20
> **当前分支**: `platform-auto-test`
> **最近提交**: `ae60263c` feat(performance): 前端合并报告页面 ProjectReport.vue + 项目详情页查看合并报告入口
> **会话窗口**: 性能测试 — 合并报告功能（Phase 1-2 完成）+ 批量执行进度推送
> **状态**: ✅ 批量执行合并报告功能已实现/提交/推送（Phase 3 待验证构建 + 部署）

---

## ⚠️ 多窗口并行开发注意

参考 `Docs/多窗口并行开发指南.md`。

| 项目 | 本窗口（性能测试） | 其他窗口 |
|------|-------------------|----------|
| 后端端口 | 8001 | 视情况 |
| 前端端口 | 3000 | 视情况 |
| 开发模块 | 性能测试 | 其他模块 |
| 数据库 | 使用 `data/test_platform.db`，注意写入错开 | 同上 |

---

## ⚡ 最新会话更新（2026-08-13 晚）

### A. API 接口预设功能 — 已完成 ✅

**实现内容**（提交 `7c1c0488`，已部署至 5.60）：

| 组件 | 文件 | 说明 |
|------|------|------|
| 模型 | `backend/app/models/api_preset.py` | `ApiPreset` 表：name/method/target_url/headers/body/body_template/auth_required/sign_request/account_key/capture_rules |
| Schema | `backend/app/schemas/api_preset.py` | `CaptureRule`（`json_path` snake_case 字段，无 to_camel） |
| 路由 | `backend/app/routers/performance.py` | `POST/GET/PUT/DELETE /api/performance/presets` |
| 服务 | `backend/app/services/api_preset_service.py` | 预设 CRUD + capture_rules 嵌套更新 |
| 继承 | `backend/app/services/performance_service.py` `_inherit_from_preset()` | 任务传 `preset_id` 时合并预设字段（仅补空，不覆盖） |
| 前端 | 预设管理页 + 任务表单预设下拉 | 选预设自动继承 method/url/headers/body/bodyTemplate/captureRules/auth/sign |

**部署脚本**：`deploy_perf_preset.py`（git pull → 上传前端 dist → restart app → 校验迁移字段 `performance_tasks.preset_id`）。

> ⚠️ **数据库现状**：服务器（5.60 MySQL）当前已重置/切换，旧 HANDOFF 记录的 `perf_f27debe671d84b4fb3908af5beac67ed`（新建会议接口压测，1046 请求）**已不在服务器上**。现存仅 2 个任务（见 6.2）。

### B. 「新建会议V3」接口预设 — 已创建 ✅

| 字段 | 值 |
|------|-----|
| ID | `preset_e62140d37c29431284a2bad8fb67528c` |
| method | `PUT` |
| target_url | `https://192.168.5.44/meetingV3/api/message/book` |
| auth_required | `true` |
| sign_request | `true`（创建后补 PUT/签名） |
| account_key | `superadmin` |

### C. 「新建会议压测」任务 — 已创建（pending）⏳

| 字段 | 值 |
|------|-----|
| ID | `perf_5a1a3b4e00e04b2f96713f4108580d2b` |
| preset_id | 继承「新建会议V3」 |
| 模式 | 并发 1 · 持续 15s · ramp_up 0 |
| bodyTemplate | `messageName={__RANDOM_NAME_8__}` / `startTime={__NOW__}` / `endTime={__NOW_+2_H__}` / `topicList={__RANDOM_NAME_4__}` |
| captureRules | `message_ids` ← `$.data.messageId`（multiple=true） |
| 状态 | `pending`（**尚未执行**，待验证 bodyTemplate 是否解决重复创建会议的 4xx 问题） |

> **关键修复点**：上次压测 1045/1046 因 body 硬编码重复创建会议返回 4xx。本次用 bodyTemplate 随机化 messageName/topicName + 动态时间，且签名基于解析后的 body。**执行后需重点核对 4xx 是否归零、message_ids 是否被成功捕获**。

### D. 账号映射（已确认）

实际压测登录固定用 **admin@xty / Ubains@13579**（验证码 csba），来自 `_DEFAULT_ACCOUNTS`（superadmin/admin/user 三档全部映射到同一账号），**与预设/任务的 `account_key` 取值无关**。用户已确认此行为正常。

### E. 本次会话待办（交接给下一窗口）

| # | 任务 | 说明 |
|---|------|------|
| 1 | **执行 `perf_5a1a3b4e00e04b2f96713f4108580d2b`** | 验证 bodyTemplate 动态替换 + sign 是否解决 4xx；检查 `message_ids` 捕获 |
| 2 | 若 4xx 仍存在 | 排查 body 结构（PRD 示例 vs 实际接口字段）、签名 body 内容、token 有效性 |
| 3 | 配置断言 | 当前 `assertions=[]`，4xx 会被算"成功"（无断言），建议加 `status_code equals 200` |
| 4 | 创建「修改会议」「取消会议」压测任务 | 取消会议用 `{__REF:perf_5a1a3b4e00e04b2f96713f4108580d2b:message_ids__}` 引用 |

---

## 一、历史任务与完成情况

### 1.1 核心任务：新建会议 API 性能测试

**用户需求**：对被测系统（https://192.168.5.44）的"新建会议"接口进行性能压测。

**接口信息**：
- `PUT https://192.168.5.44/meetingV3/api/message/book`
- 需要 `Authorization: Bearer {token}`
- 需要 `X-RANDOM/X-TIMESTAMP/X-SIGN` 签名头
- 请求体为 JSON **数组** `[{...}]`

**已完成**：
1. ✅ **修复 Pydantic schema body 类型**：`body: Optional[Dict[str, Any]]` → `Optional[Any]`，支持 JSON 数组（涉及 PerformanceTaskCreate、PerformanceTaskUpdate、PerformanceTaskResponse）
2. ✅ **修复执行器签名支持**：`_build_headers()` 原忽略 `sign_request` 字段，现当 `signRequest=True` 时自动调用 `_generate_sign()` 生成签名头
3. ✅ **创建并执行压测任务**：
   - 任务 ID：`perf_f27debe671d84b4fb3908af5beac67ed`
   - 并发 1 / 时长 15s / 预热 5s
   - 结果：1046 请求，1046 成功（但 1045 个 4xx，因 body 硬编码重复创建会议）

### 1.2 文档产出

| 文档 | 路径 | 说明 |
|------|------|------|
| PRD 需求文档 | `Docs/PRD/性能测试/需求文档/_PRD_bodyTemplate动态变量替换与响应捕获.md` | bodyTemplate 变量替换 + 响应捕获 + 跨任务引用 |
| 计划执行文档 | `Docs/PRD/性能测试/需求文档/_PRD_bodyTemplate动态变量替换与响应捕获_计划执行.md` | 6 个 Phase 详细执行计划 |
| 接口 Curl 记录 | `Docs/meeting_api_curls.md` | 新建/修改/取消会议三个 API 的 curl 原文 |

### 1.3 其他相关文件

| 文件 | 说明 |
|------|------|
| `backend/capture_meeting_api.py` | Playwright 脚本（已废弃，用户提供 curl 替代） |
| `backend/tmp_create_meeting_task.json` | 创建任务时使用的临时 JSON 文件 |

---

## 二、后端变更摘要

### 2.1 已修改文件（工作区未提交）

| 文件 | 修改内容 |
|------|---------|
| `backend/app/schemas/performance.py` | `body: Optional[Dict[str, Any]]` → `Optional[Any]`（3 处），增加 `description` 说明"支持 dict 或 list" |
| `backend/app/executors/performance_executor.py` | `_build_headers()` 新增 `sign_request=True` 时自动生成 X-RANDOM/X-TIMESTAMP/X-SIGN 签名头 |

### 2.2 关键代码位置

- **性能测试执行器**：`backend/app/executors/performance_executor.py`（817 行）
  - `_build_headers()` 约 470-507 行（含新加的 sign_request 逻辑）
  - `_send_request()` 约 739-790 行
  - `execute()` 入口约 509-549 行
- **性能测试 Schema**：`backend/app/schemas/performance.py`（271 行）
- **性能测试模型**：`backend/app/models/performance.py`（284 行）
- **签名算法**：`backend/app/executors/http_client.py` 约 179-230 行 `_generate_sign()`
- **性能测试路由**：`backend/app/routers/performance.py`（297 行）
- **性能测试服务**：`backend/app/services/performance_service.py`

### 2.3 服务启动信息

```bash
# 后端
cd backend && uvicorn app.main:app --reload --port 8001

# 前端
cd frontend && npm run dev
```

---

## 三、会议模块 API 信息

### 3.1 三个接口

| 接口 | 方法 | URL | 说明 |
|------|------|-----|------|
| 新建会议 | PUT | `/meetingV3/api/message/book` | 已完成压测 |
| 修改会议 | PUT | `/meetingV3/api/message/update` | 已记录 curl |
| 取消会议 | DELETE | `/meetingV3/api/message/cancel/{mid}` | 已记录 curl |

### 3.2 认证与签名要求

- **认证**：`Authorization: Bearer {token}`（通过 `authRequired=true` + `accountKey="superadmin"` 自动获取）
- **签名**：`X-RANDOM` + `X-TIMESTAMP` + `X-SIGN`（通过 `signRequest=true` 自动生成，AES-CBC 加密，密钥从 Bearer Token 派生）
- **请求体**：JSON 数组格式（已修复 Pydantic 类型限制，`body: Optional[Any]`）

### 3.3 curl 原文位置

`Docs/meeting_api_curls.md` 记录了三个接口的完整 curl 命令（含 Authorization 和签名头）。

---

## 四、已知问题与待办

### 4.1 已发现的问题

| # | 问题 | 影响 | 状态 |
|---|------|------|------|
| 1 | body 硬编码，请求数据重复 | 1045/1046 请求返回 4xx（重复创建会议） | ✅ **已解决**（bodyTemplate 动态变量，见最新会话更新 C） |
| 2 | 错误率统计依赖断言，非 HTTP 状态码 | 虽 4xx 但无断言→全部算成功 | 待配置断言后验证；或考虑改进统计逻辑 |
| 3 | `body_template` 字段已定义但执行器未使用 | 无法动态替换变量 | ✅ **已解决**（`template_resolver.py` 已实现，8 种占位符，执行器已集成） |
| 4 | 无 `capture_rules` 字段 | 无法捕获响应中的 messageId | ✅ **已解决**（模型/Schema/执行器 `_capture_response` 已实现） |

### 4.2 待办事项（按优先级）

| 优先级 | 任务 | 对应 Phase | 说明 |
|--------|------|-----------|------|
| ✅ 已完成 | 新增 PerformanceTaskOutput 模型 + capture_rules 字段 | Phase 1 | 模型/Schema 扩展 |
| ✅ 已完成 | 实现 TemplateResolver 解析器 | Phase 2 | 8 种占位符 |
| ✅ 已完成 | 执行器集成模板替换 + 响应捕获 | Phase 3 | `_send_request` + `_capture_response` |
| ✅ 已完成 | 服务/路由：输出保存 + 查询 API | Phase 4 | 跨任务引用（`{__REF:...__}`） |
| ✅ 已完成 | 前端 UI：预设管理 + 任务表单继承 | — | 接口预设功能（`7c1c0488`） |
| P0 | **执行新建会议压测任务，验证全链路** | Phase 6 | `perf_5a1a3b4e00e04b2f96713f4108580d2b`，确认 4xx 归零 + messageIds 捕获 |
| P1 | 集成验证：新建→取消会议全链路 | Phase 6 | 取消会议任务用 REF 引用 message_ids |
| P1 | 改进错误率统计（HTTP 状态码维度） | — | 当前仅依赖断言，4xx 无断言会误判成功 |

### 4.3 实施建议

**Phase 1 实现要点**：
- 新建 `models/performance_output.py`，`PerformanceTaskOutput` 表含 `(task_id, key, value, created_at)`，唯一约束 `(task_id, key)`
- 新建 `schemas/performance_output.py`，定义 `CaptureRule` Schema
- `PerformanceTaskCreate` 增加 `capture_rules: Optional[List[CaptureRule]]` 和 `body_template: Optional[Dict[str, Any]]`（后者已有）
- 注册新表到 `database.py`

**Phase 2 实现要点**：
- 新建 `executors/template_resolver.py`，`TemplateResolver` 类
- 正则 `r"\{__([A-Z_+\d:]+?)__\}"` 匹配占位符
- 支持 `NOW` / `NOW_+N_H` / `NOW_+N_M` / `RANDOM_NAME_N` / `UUID` / `RANDOM_INT_N_M` / `INDEX` / `REF:task_id:key`
- 不修改原模板，返回新容器

**Phase 3 实现要点**：
- `_send_request` 中先 `_resolve_body` 再签名
- `_build_headers` 接受 `body_for_sign` 参数
- 每次响应后按 `capture_rules` 提取字段
- `_worker` 循环传递 `request_index`

---

## 五、关键决策记录

| 日期 | 决策 | 理由 |
|------|------|------|
| 2026-08-13 | 使用用户提供的 curl 替代 Playwright 抓包 | 用户明确要求："或者我把请求给你 请求的curl给你" |
| 2026-08-13 | body 类型改为 `Optional[Any]` 而非新增 `ListBody` | 减少 Schema 复杂度，SQLAlchemy JSON 列和 aiohttp 原生支持 list |
| 2026-08-13 | sign_request 独立于 auth_required | 登录接口签名用空 token，业务接口签名用真实 token，两个开关独立控制 |
| 2026-08-13 | 选择 bodyTemplate 方案而非动态脚本 | 配置化，无需写代码，前端可编辑 |
| 2026-08-13 晚 | 引入「接口预设」抽象 | 重复接口配置（method/url/auth/sign/body）固化为预设，任务继承 + 增量覆盖，避免每次重填 |
| 2026-08-13 晚 | `_DEFAULT_ACCOUNTS` 三档全部映射到 admin@xty | 测试环境仅有单一管理员账号，superadmin/admin/user 统一映射，与预设 account_key 取值解耦 |
| 2026-08-13 晚 | 新建会议压测任务传 `preset_id` 继承 + 显式 `target_url` | schema 把 `target_url` 列为必填，即便 preset_id 继承也必须显式传（校验在前，继承在后） |
| 2026-08-13 晚 | `capture_rules` 用 snake_case `json_path` | 嵌套 `CaptureRule`（`performance_output.py`）未挂 `to_camel`，顶层任务 schema 虽用 camelCase 别名，但嵌套字段名必须原样 `json_path` |

---

## 六、会话资源

### 6.1 参考文档

| 文档 | 路径 |
|------|------|
| 性能测试 PRD | `Docs/PRD/性能测试/需求文档/_PRD_性能测试模块需求文档.md` |
| 性能测试计划执行 | `Docs/PRD/性能测试/需求文档/_PRD_性能测试模块_计划执行.md` |
| bodyTemplate PRD | `Docs/PRD/性能测试/需求文档/_PRD_bodyTemplate动态变量替换与响应捕获.md` |
| bodyTemplate 计划执行 | `Docs/PRD/性能测试/需求文档/_PRD_bodyTemplate动态变量替换与响应捕获_计划执行.md` |
| 会议 API Curl | `Docs/meeting_api_curls.md` |
| CLAUDE.md | `CLAUDE.md`（项目全局约束、踩坑记录） |

### 6.2 已创建的性能测试任务（5.60 服务器实际现状）

> 服务器 MySQL 库已重置/切换，旧任务 `perf_f27debe671d84b4fb3908af5beac67ed`（1046 请求）已不存在。当前现存：

| 任务 ID | 名称 | 状态 | 说明 |
|---------|------|------|------|
| `perf_5a1a3b4e00e04b2f96713f4108580d2b` | 新建会议压测 | `pending` | **本次会话新建**，PUT meetingV3/book，bodyTemplate+captureRules，待执行验证 |
| `perf_0fbcb792dcc143fd80b42cb405a75176` | 登录接口性能基准测试 | completed | 10983 请求 |

### 6.3 已创建的接口预设

| 预设 ID | 名称 | 方法/URL | 说明 |
|---------|------|----------|------|
| `preset_e62140d37c29431284a2bad8fb67528c` | 新建会议V3 | `PUT https://192.168.5.44/meetingV3/api/message/book` | auth+sign，被「新建会议压测」任务继承 |

### 6.4 用户提到的后续需求

1. ~~实现 bodyTemplate 变量替换 + 响应捕获~~ ✅ 已完成
2. 创建修改会议、取消会议的性能测试任务 ⏳ 待做
3. 取消会议引用新建会议产出的 messageId ⏳ 待做（`{__REF:perf_5a1a3b4e00e04b2f96713f4108580d2b:message_ids__}`）

---

*本 HANDOFF 文档由 Claude Code 自动生成，用于会话间交接。*