# 问题处理：批量详情 API 500 错误 — actual_concurrency 字段缺失

> 处理时间：2026-08-21
> 现象报告人：用户（批量执行操作时）
> 关联文档：`_执行计划_批量执行跳转监控页.md`（问题处理）、`_PRD_批量执行跳转监控页.md`（需求）
> 状态：✅ 已修复并验证

---

## 1. 问题现象

用户执行「执行全部」等批量执行操作后，前端调用 **批量详情 API** 报错：

```
加载批次详情失败: Request failed with status code 500
```

前端页面无法展示任务列表与实时监控内容。

---

## 2. 初步定位

1. 查看前端 `BatchMonitor.vue` 的报错点为调用 `GET /api/performance/batch-details/{batch_id}`。
2. 在后端容器日志中确认异常堆栈：

```
'PerformanceTask' object has no attribute 'actual_concurrency'. Did you mean: 'step_concurrency'?
```

3. 定位到 `backend/app/services/performance_service.py` 的 `get_batch_details()` 方法（约第 1648 行）访问了 `task.actual_concurrency`。

---

## 3. 根因分析

### 3.1 字段不一致

| 位置 | 字段 | 说明 |
|------|------|------|
| `PerformanceTask` ORM 模型（`models/performance.py`） | **无 `actual_concurrency`** | 只有 `concurrency`（配置的并发数）和 `step_concurrency` |
| `BatchTaskItem` schema（`schemas/performance.py`） | `actual_concurrency: Optional[int]` | API 响应字段名正确 |
| 前端 TS 类型（`types/performance.ts`） | `actualConcurrency: number \| null` | 前端类型正确 |
| **service 层 `get_batch_details()`** | **`task.actual_concurrency` ❌** | **问题所在：ORM 对象上不存在该属性** |

### 3.2 为什么会 500

`get_batch_details()` 在组装任务字典时直接读取 `task.actual_concurrency`，而 `PerformanceTask` ORM 模型**从未定义过该字段**。Python 访问不存在的属性时抛出 `AttributeError`，FastAPI 捕获后返回 500。

> `suggest` 提示的 `step_concurrency` 是阶梯模式（step mode）的并发字段，与本场景无关；正确的取值是任务的**配置并发数 `concurrency`**。

### 3.3 为什么 schema 和前端没报错

- schema `BatchTaskItem.actual_concurrency` 只是响应模型（response_model），负责序列化输出，不涉及 ORM 属性访问，因此没问题。
- 前端 `BatchTaskItem.actualConcurrency` 只是 TS 类型声明，同样没问题。

**问题只出在 service 层读取 ORM 对象属性这一环。**

---

## 4. 修复方案

将 `get_batch_details()` 中访问不存在的 `task.actual_concurrency` 改为使用模型上真实存在的 `task.concurrency`（任务的配置并发数）。

```python
# backend/app/services/performance_service.py — get_batch_details()
tasks.append({
    "task_id": task.id,
    "name": task.name,
    "status": task.status,
    "actual_tps": task.actual_tps,
    "avg_response_time": task.avg_response_time,
    "error_rate": task.error_rate,
    "actual_concurrency": task.concurrency,   # 修复前：task.actual_concurrency（不存在）
    "duration_actual": task.duration_actual,
})
```

---

## 5. 部署与验证

### 5.1 部署步骤

1. 上传修复后的文件到 5.60 服务器：
   ```bash
   scp app/services/performance_service.py ubains@192.168.5.60:/data/third_party/plat-auto-test/backend/app/services/performance_service.py
   ```
2. 重启后端容器：
   ```bash
   ssh ubains@192.168.5.60 "cd /data/third_party/plat-auto-test/deploy && docker compose restart app"
   ```

### 5.2 验证结果

| 验证项 | 结果 |
|--------|------|
| 容器重启 | ✅ 成功（`Container plat-auto-test-app Started`） |
| `GET /api/performance/batch-details/{batch_id}`（真实批次） | ✅ 返回 200，任务列表含 `actualConcurrency` 字段 |
| 不存在的 batch_id | ✅ 返回 404 语义（`{"detail":"批量执行记录不存在"}`） |

真实批次返回示例（节选）：

```json
{
    "batchId": "batch_0951f3cfb77842ea87f4d43b8296fc0a",
    "projectId": "proj_5a7213144b5740cabd13fae3882c1901",
    "status": "running",
    "totalCount": 2,
    "tasks": [
        {
            "taskId": "perf_82d72c5c17f94f9ebba1f899b41bc018",
            "name": "会议模板列表查询",
            "status": "completed",
            "actualTps": 244.6,
            "avgResponseTime": 39.87,
            "errorRate": 0.0,
            "actualConcurrency": 10,
            "durationActual": 62.7588
        }
    ]
}
```

---

## 6. 经验总结（避免再犯）

1. **ORM 属性访问前先确认字段存在**：service 层直接访问 ORM 对象属性时，务必先核对 `models/*.py` 中的字段定义，不要凭 schema/Types 猜测。
2. **三端字段命名契约**：ORM 模型 ↔ Pydantic schema（`from_attributes`）↔ 前端 TS 类型，三者的字段来源要一致；schema 字段名可以和 ORM 不同（靠手动组装或 alias），但 **service 组装时引用的必须是 ORM 真实属性**。
3. **500 快速定位**：`AttributeError` 类 500 错误，直接在容器日志里搜 `AttributeError` 一行即可定位到具体行号与属性名。
4. **部署验证闭环**：接口报 500 后，修复完必须重启容器 + 用真实数据调一次接口确认 200，才算闭环。

---

*本文档由 Claude Code 生成*