# 执行计划 - 批量执行合并报告

> **关联 PRD**：`_PRD_批量执行合并报告.md`
> **创建日期**：2026-08-20
> **预计总工时**：2 天

---

## 一、执行概述

本计划将 PRD 中的「批量执行合并报告」功能分为 3 个 Phase 实施：

1. **Phase 1**：数据模型 + 后端聚合逻辑 + API（1 天）
2. **Phase 2**：前端合并报告页面（0.5 天）
3. **Phase 3**：项目详情页适配 + 联调部署（0.5 天）

每个 Phase 完成后可独立验收，Phase 1 是后续所有 Phase 的基础。

---

## 二、任务分解

### Phase 1：数据模型 + 后端聚合逻辑 + API

**目标**：新增 PerformanceBatchExecution（持久化）和 PerformanceProjectReport 模型，实现批量执行后的自动聚合，提供合并报告查询 API。

| # | 任务 | 关键文件 | 验收标准 |
|---|------|---------|---------|
| 1.1 | 新建 `PerformanceBatchExecution` ORM 模型 | `backend/app/models/performance.py` | 表字段与 PRD 3.1 一致（id/project_id/task_ids/status/total_count/success_count/fail_count/started_at/completed_at/created_at） |
| 1.2 | 新建 `PerformanceProjectReport` ORM 模型 | `backend/app/models/performance.py` | 表字段与 PRD 3.2 一致（id/project_id/batch_id/task_ids + 所有聚合指标字段 + task_summaries JSON） |
| 1.3 | 定义合并报告 Pydantic Schema | `backend/app/schemas/performance.py` | `ProjectReportResponse`（含 summary + task_summaries）+ `ProjectReportListResponse` |
| 1.4 | 数据库迁移（SQLite ALTER TABLE） | `backend/app/database.py` | 启动时自动建表（`_ensure_columns` 或 `Base.metadata.create_all`） |
| 1.5 | 实现 `_aggregate_task_results()` 聚合方法 | `backend/app/services/performance_service.py` | 接收 N 个任务 ID，按 PRD 3.3 聚合算法计算合并指标，返回报告字典 |
| 1.6 | 改造 `batch_run_tasks()` 持久化 batch 记录 + 执行完自动聚合 | `backend/app/services/performance_service.py` | 开始执行时写入 `performance_batch_executions`（status=running），完成时更新 status + 调用 `_aggregate_task_results()` 生成合并报告并写入 `performance_project_reports` |
| 1.7 | 改造 `run_project_all()` 同样触发合并报告生成 | `backend/app/services/performance_service.py` | 调用 `batch_run_tasks()` 已自然继承 |
| 1.8 | 新增合并报告查询 API | `backend/app/routers/performance.py` | 3 条路由：GET `/projects/{project_id}/report`（最新）、GET `/batch-reports/{batch_id}`、GET `/projects/{project_id}/reports`（历史列表） |
| 1.9 | 批量执行响应扩展 `report_id` | `backend/app/routers/performance.py` | `BatchRunResponse` 新增 `report_id` 字段 |
| 1.10 | 更新 `BatchRunResponse` Schema | `backend/app/schemas/performance.py` | 新增 `report_id: Optional[str] = None` |

**验收方式**：通过 Swagger UI 手动测试——批量执行一组任务 → 确认 `performance_batch_executions` 表有记录 → 调用合并报告 API 返回聚合指标。

---

### Phase 2：前端合并报告页面

**目标**：新建合并报告页面，展示聚合指标卡片 + 各任务明细列表。

| # | 任务 | 关键文件 | 验收标准 |
|---|------|---------|---------|
| 2.1 | 新增合并报告类型定义 | `frontend/src/types/performance.ts` | `ProjectReportResponse`、`ProjectReportSummary`、`TaskSummaryItem` 接口 |
| 2.2 | 新增合并报告 API 调用 | `frontend/src/api/performance.ts` | `getProjectReport(projectId)`、`getProjectReports(projectId)`、`getBatchReport(batchId)` |
| 2.3 | 新建合并报告页面 `ProjectReport.vue` | `frontend/src/views/performance/ProjectReport.vue` | 复用 ReportPanel.vue 风格，展示聚合指标卡片（4行16卡）+ 分位数表 + 状态码分布 + 各任务明细表 |
| 2.4 | 注册合并报告路由 | `frontend/src/router/index.ts` | `/performance/project-report` 可访问，支持 query 参数 `projectId` 和 `reportId` |
| 2.5 | 批量执行完成后的跳转入口 | `frontend/src/views/performance/ProjectDetail.vue` | 批量执行完成后进度条下方出现「查看合并报告」按钮 |

**验收方式**：批量执行完成后点击「查看合并报告」→ 跳转到合并报告页面，看到聚合指标和各任务明细。

---

### Phase 3：项目详情页适配 + 联调

**目标**：完善项目详情页与合并报告的联动，整体联调验证。

| # | 任务 | 关键文件 | 验收标准 |
|---|------|---------|---------|
| 3.1 | 项目详情页批量执行反馈 | `ProjectDetail.vue` | 批量执行完成后取消 loading，显示「查看合并报告」按钮，点击跳转 `ProjectReport.vue` |
| 3.2 | 前端构建验证 | `npm run build` | 无 TypeScript 错误，构建通过 |
| 3.3 | 端到端联调 | 全部相关文件 | 创建项目 → 创建 2 个任务归入项目 → 批量执行 → 查看合并报告 → 看到聚合指标 + 各任务明细 |
| 3.4 | 部署到 5.60 服务器 | 后端 5 文件 + 前端 dist | 容器重启后功能正常 |

**验收方式**：完整端到端流程验证通过。

---

## 三、聚合算法实现细节

### 3.1 `_aggregate_task_results(tasks_summary, snapshots_all)` 伪代码

```
输入：tasks_data = [task1.to_dict(), task2.to_dict(), ...]
输出：合并指标字典

1. 基础求和
   total_requests = Σ task.total_requests
   success_count = Σ task.success_count
   fail_count = Σ task.fail_count
   status_2xx = Σ task.status_2xx (来自快照汇总)
   status_4xx = Σ task.status_4xx
   status_5xx = Σ task.status_5xx
   total_sent_bytes = Σ task.total_sent_bytes
   total_received_bytes = Σ task.total_received_bytes

2. 重算比率
   error_rate = fail_count / total_requests
   duration_actual = Σ task.duration_actual
   actual_tps = total_requests / duration_actual (如果 > 0)

3. 响应时间
   min_response_time = min(task.min_response_time)
   max_response_time = max(task.max_response_time)

   # 加权平均
   avg_response_time = Σ(task.avg_response_time × task.total_requests) / total_requests

   # 百分位数（加权近似）
   p50 = 加权百分位数(p50_i, request_count_i)
   p90 = 加权百分位数(p90_i, request_count_i)
   p95 = 加权百分位数(p95_i, request_count_i)
   p99 = 加权百分位数(p99_i, request_count_i)

4. 其他
   peak_tps = max(task.peak_tps)
   apdex = Σ(task.apdex × task.total_requests) / total_requests
   std_dev = 基于聚合均值和方差计算（近似：√(Σ((std_dev_i² + mean_i²) × n_i) / N - 总均值²)）

5. 任务明细
   task_summaries = [{task_id, task_name, status, total_requests, success_count, 
                      fail_count, avg_response_time, actual_tps, error_rate}]
```

### 3.2 加权百分位数算法

由于各任务的分位数是独立计算的，合并时无法精确计算全局百分位数，采用**加权近似法**：

```python
def weighted_percentile(percentiles, counts, p):
    """
    percentiles: [p50_1, p50_2, ...] 各任务的指定分位数
    counts: 各任务请求数
    p: 目标分位数 (50/90/95/99)
    """
    # 按分位数值排序
    pairs = sorted(zip(percentiles, counts))
    sorted_vals, sorted_counts = zip(*pairs)
    total = sum(sorted_counts)
    target = total * p / 100
    cumulative = 0
    for val, cnt in zip(sorted_vals, sorted_counts):
        cumulative += cnt
        if cumulative >= target:
            return val
    return sorted_vals[-1]
```

---

## 四、验收标准总览

| 验收项 | 对应 Phase |
|--------|-----------|
| `performance_batch_executions` 表有数据 | Phase 1 |
| `performance_project_reports` 表有数据 | Phase 1 |
| 合并报告 API 返回聚合指标 | Phase 1 |
| 批量执行响应含 `report_id` | Phase 1 |
| 合并报告页面展示聚合指标卡片 | Phase 2 |
| 合并报告页面展示各任务明细 | Phase 2 |
| 批量执行完成后可跳转合并报告 | Phase 3 |
| 单任务报告不受影响 | Phase 3 |
| 前端构建通过 | Phase 3 |

---

## 五、测试计划

### 单元测试
- `_aggregate_task_results()` 聚合逻辑测试（验证加权平均、百分位数、求和等）
- 批量执行记录持久化测试

### 集成测试
- 创建 2 个任务 → 批量执行 → 验证合并报告指标正确性
- 单任务执行 → 验证不生成合并报告

### 手动测试
- Phase 1 完成后通过 Swagger UI 验证 API
- Phase 2 完成后通过浏览器验证前端页面

---

## 六、风险评估

| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 加权百分位数不精确 | 合并报告的 P95/P99 与真实值有偏差 | 文档说明是近似值，标注算法；如需精确值需原始数据全量排序 |
| 批量执行后聚合计算耗时 | 用户等待时间长 | 聚合计算在内存中完成（O(N) 复杂度），通常 < 100ms |
| 大数据量下快照内存占用 | 大批量任务聚合时快照数据量大 | 聚合时不加载快照明细，仅从 task 表汇总指标 |
| 数据库锁（SQLite） | 并发写入合并报告失败 | 批量执行本身已串行，写库在 finally 块中 |

---

## 七、实施顺序建议

### 推荐顺序（前端 + 后端可并行）

**Step 1（后端核心）**：新建模型 + Schema → database.py 迁移 → `_aggregate_task_results()` 实现 → 改造 `batch_run_tasks()` 持久化 + 聚合 → 合并报告查询 API

**Step 2（前端核心）**：类型定义 → API 封装 → ProjectReport.vue 页面 → 路由注册

**Step 3（联调）**：ProjectDetail.vue 适配 → 端到端验证 → 构建 → 部署

---

## 八、后续工作

- [ ] Phase 1-3 实施完成后更新 CLAUDE.md 当前进度
- [ ] 更新 HANDOFF.md 交接文档
- [ ] Git 提交（Conventional Commits）
- [ ] 部署到 5.60 服务器

---

## 九、附录

### 现有文件参考

| 文件 | 用途 |
|------|------|
| `backend/app/services/performance_service.py` | `batch_run_tasks()` 约 998 行，需要改造持久化 + 聚合 |
| `backend/app/routers/performance.py` | 批量执行路由约 506 行，需要扩展 `report_id` |
| `backend/app/models/performance.py` | 现有模型约 380 行，新增 2 个模型 |
| `backend/app/schemas/performance.py` | 现有 Schema 约 377 行，新增合并报告 Schema |
| `frontend/src/views/performance/ReportPanel.vue` | 626 行，合并报告页面布局参考 |
| `frontend/src/views/performance/ProjectDetail.vue` | 439 行，需适配批量执行后的跳转 |

### 关键数据流

```
批量执行请求 → POST /api/performance/tasks/batch-run
    ↓
batch_run_tasks() 开始
    ↓
创建 performance_batch_executions 记录 (status=running)
    ↓
串行逐个执行任务（已有的 run_task 逻辑不变）
    ↓
所有任务执行完毕
    ↓
_aggregate_task_results() 聚合所有任务指标
    ↓
创建 performance_project_reports 记录
    ↓
更新 performance_batch_executions (status=completed)
    ↓
返回 { batch_id, report_id, ... }
    ↓
前端跳转合并报告页 → GET /api/performance/projects/{id}/report
```

---

*本文档由 prd-plan skill 生成，关联 PRD：`_PRD_批量执行合并报告.md`*