All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 2m16s
- 新增 Project 模型(repo_url, local_path, name, description) - 项目 CRUD API(GET/PUT /api/v1/projects) - 日志上报自动 upsert Project 记录 - ErrorLog 增加 failure_reason 字段 - update_task_status / create_repair_report 写入失败原因 - Repair Agent 优先从 API 获取项目配置,回退 .env - 新增 Web 端「项目管理」页面(表格 + 行内编辑) - BugList/BugDetail/RepairList 展示失败原因 - 更新接入指南文档 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
318 lines
10 KiB
Markdown
318 lines
10 KiB
Markdown
# Log Center 改进方案:项目管理 + 修复失败追踪
|
||
|
||
## 背景
|
||
|
||
Log Center 当前存在三个核心问题:
|
||
|
||
1. **Repair Agent 无法识别项目仓库和本地路径** — 仓库地址(`repo_url`)和本地项目路径(`local_path`)都硬编码在 `repair_agent/.env`,需手动配置,且当前全部为空。应在接入日志收集时自动注册项目信息,并可在 Web 端统一管理。
|
||
2. **修复失败原因未上报** — `ErrorLog` 表没有 `failure_reason` 字段;`update_task_status` API 接收了 `message` 参数但直接丢弃(`main.py:113`);`upload_report` 失败时原因静默丢失。
|
||
3. **前端未展示失败原因** — BugList / BugDetail 页面无法看到修复失败的具体原因,用户必须逐一查看 RepairTask 记录才能找到。
|
||
|
||
---
|
||
|
||
## 第一部分:项目管理(仓库地址 + 本地路径 + Web 管理页)
|
||
|
||
### 1.1 新增 `Project` 数据模型
|
||
|
||
**文件**: `app/models.py`
|
||
|
||
```python
|
||
class Project(SQLModel, table=True):
|
||
id: Optional[int] = Field(default=None, primary_key=True)
|
||
project_id: str = Field(unique=True, index=True) # "rtc_backend"
|
||
name: Optional[str] = Field(default=None) # 显示名称
|
||
repo_url: Optional[str] = Field(default=None) # GitHub/Gitea 仓库地址
|
||
local_path: Optional[str] = Field(default=None) # 本地项目路径(Agent 修复时使用)
|
||
description: Optional[str] = Field(default=None) # 项目描述
|
||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||
```
|
||
|
||
新增 `ProjectUpdate` schema 用于编辑:
|
||
|
||
```python
|
||
class ProjectUpdate(SQLModel):
|
||
name: Optional[str] = None
|
||
repo_url: Optional[str] = None
|
||
local_path: Optional[str] = None
|
||
description: Optional[str] = None
|
||
```
|
||
|
||
### 1.2 日志上报自动注册项目
|
||
|
||
**文件**: `app/models.py` — `ErrorLogCreate` 增加字段
|
||
|
||
```python
|
||
class ErrorLogCreate(SQLModel):
|
||
# ... 现有字段 ...
|
||
repo_url: Optional[str] = None # 可选:上报时附带仓库地址
|
||
```
|
||
|
||
**文件**: `app/main.py` — `report_log()` 增加 upsert 逻辑
|
||
|
||
收到日志上报时:
|
||
1. 根据 `project_id` 查找 Project 记录
|
||
2. 不存在 → 自动创建(`project_id` + `repo_url`)
|
||
3. 已存在且 `repo_url` 不为空 → 更新 `repo_url`
|
||
|
||
> 向后兼容:不传 `repo_url` 时仍正常创建 Project 记录(`repo_url` 为空),后续可在 Web 端手动补充。
|
||
|
||
### 1.3 项目管理 API
|
||
|
||
**文件**: `app/main.py`
|
||
|
||
| 端点 | 方法 | 用途 |
|
||
|------|------|------|
|
||
| `GET /api/v1/projects` | GET | 项目列表(改造现有端点,从 Project 表查询,返回完整信息) |
|
||
| `GET /api/v1/projects/{project_id}` | GET | 获取单个项目详情(含 repo_url、local_path) |
|
||
| `PUT /api/v1/projects/{project_id}` | PUT | 编辑项目信息(repo_url、local_path、name、description) |
|
||
|
||
#### GET /api/v1/projects 响应示例
|
||
|
||
```json
|
||
{
|
||
"projects": [
|
||
{
|
||
"id": 1,
|
||
"project_id": "rtc_backend",
|
||
"name": "RTC 后端",
|
||
"repo_url": "https://gitea.example.com/team/rtc_backend.git",
|
||
"local_path": "/Users/maidong/Desktop/zyc/qy_gitlab/rtc_backend",
|
||
"description": "Django 后端服务",
|
||
"created_at": "2026-01-15T08:00:00",
|
||
"updated_at": "2026-02-20T10:30:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### PUT /api/v1/projects/{project_id} 请求示例
|
||
|
||
```json
|
||
{
|
||
"name": "RTC 后端",
|
||
"repo_url": "https://gitea.example.com/team/rtc_backend.git",
|
||
"local_path": "/Users/maidong/Desktop/zyc/qy_gitlab/rtc_backend",
|
||
"description": "Django 后端服务"
|
||
}
|
||
```
|
||
|
||
### 1.4 Repair Agent 适配
|
||
|
||
#### task_manager.py — 新增 `get_project_info()`
|
||
|
||
```python
|
||
def get_project_info(self, project_id: str) -> Optional[dict]:
|
||
"""从 Log Center API 获取项目配置(repo_url + local_path)"""
|
||
response = self.client.get(f"{self.base_url}/api/v1/projects/{project_id}")
|
||
return response.json() # { repo_url, local_path, ... }
|
||
```
|
||
|
||
#### core.py — 修改 `fix_project()` 和 `fix_single_bug()`
|
||
|
||
优先级链:
|
||
- **local_path**:API 返回值 → `settings.get_project_path()` (.env) → 报错退出
|
||
- **repo_url**:API 返回值 → `settings.get_github_repo()` (.env) → 跳过 Git 操作
|
||
|
||
```python
|
||
# 获取项目配置(API 优先)
|
||
project_info = self.task_manager.get_project_info(project_id)
|
||
|
||
project_path = (project_info and project_info.get("local_path")) or settings.get_project_path(project_id)
|
||
github_repo = (project_info and project_info.get("repo_url")) or settings.get_github_repo(project_id)
|
||
```
|
||
|
||
### 1.5 Web 端项目管理页面
|
||
|
||
**新增文件**: `web/src/pages/ProjectList.tsx`
|
||
|
||
功能:
|
||
- 表格展示所有项目:project_id、名称、仓库地址、本地路径、描述、更新时间
|
||
- 行内编辑:点击「编辑」按钮进入编辑模式,可修改所有可编辑字段
|
||
- 状态指示:`repo_url` / `local_path` 为空时标红显示「未配置」
|
||
- 移动端适配:卡片式布局
|
||
|
||
**修改文件**: `web/src/App.tsx`
|
||
- 侧边栏新增「项目管理」入口(使用 `FolderGit2` 图标)
|
||
- 路由新增 `/projects` → `ProjectList`
|
||
|
||
**修改文件**: `web/src/api.ts`
|
||
|
||
```typescript
|
||
export interface Project {
|
||
id: number;
|
||
project_id: string;
|
||
name: string | null;
|
||
repo_url: string | null;
|
||
local_path: string | null;
|
||
description: string | null;
|
||
created_at: string;
|
||
updated_at: string;
|
||
}
|
||
|
||
export const getProjectList = () =>
|
||
api.get<{ projects: Project[] }>('/api/v1/projects');
|
||
|
||
export const getProjectDetail = (projectId: string) =>
|
||
api.get<Project>(`/api/v1/projects/${projectId}`);
|
||
|
||
export const updateProject = (projectId: string, data: Partial<Project>) =>
|
||
api.put(`/api/v1/projects/${projectId}`, data);
|
||
```
|
||
|
||
### 1.6 数据库变更
|
||
|
||
```sql
|
||
-- 新建 Project 表
|
||
CREATE TABLE project (
|
||
id SERIAL PRIMARY KEY,
|
||
project_id VARCHAR UNIQUE NOT NULL,
|
||
name VARCHAR,
|
||
repo_url VARCHAR,
|
||
local_path VARCHAR,
|
||
description VARCHAR,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
CREATE UNIQUE INDEX ix_project_project_id ON project (project_id);
|
||
```
|
||
|
||
---
|
||
|
||
## 第二部分:修复失败原因录入数据库
|
||
|
||
### 2.1 ErrorLog 增加 `failure_reason` 字段
|
||
|
||
**文件**: `app/models.py`
|
||
|
||
```python
|
||
class ErrorLog(SQLModel, table=True):
|
||
# ... 现有字段 ...
|
||
failure_reason: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
|
||
```
|
||
|
||
### 2.2 `update_task_status` API 存储 message
|
||
|
||
**文件**: `app/main.py` — `update_task_status()`
|
||
|
||
当前代码(第 113 行):
|
||
```python
|
||
task.status = status_update.status
|
||
# We could log the message to a history table if needed ← message 被丢弃了
|
||
```
|
||
|
||
修改为:
|
||
```python
|
||
task.status = status_update.status
|
||
if status_update.message and status_update.status == LogStatus.FIX_FAILED:
|
||
task.failure_reason = status_update.message
|
||
```
|
||
|
||
### 2.3 `create_repair_report` API 同步更新 ErrorLog
|
||
|
||
**文件**: `app/main.py` — `create_repair_report()`
|
||
|
||
当前仅在 `FIXED` / `FIX_FAILED` 时更新 ErrorLog 的 status。增加逻辑:
|
||
|
||
```python
|
||
if error_log:
|
||
error_log.status = report.status
|
||
if report.failure_reason and report.status == LogStatus.FIX_FAILED:
|
||
error_log.failure_reason = report.failure_reason
|
||
session.add(error_log)
|
||
```
|
||
|
||
### 2.4 数据库变更
|
||
|
||
```sql
|
||
ALTER TABLE errorlog ADD COLUMN failure_reason TEXT;
|
||
```
|
||
|
||
---
|
||
|
||
## 第三部分:前端展示失败原因
|
||
|
||
### 3.1 TypeScript 类型更新
|
||
|
||
**文件**: `web/src/api.ts`
|
||
|
||
```typescript
|
||
export interface ErrorLog {
|
||
// ... 现有字段 ...
|
||
failure_reason: string | null;
|
||
}
|
||
```
|
||
|
||
### 3.2 BugList 页面
|
||
|
||
**文件**: `web/src/pages/BugList.tsx`
|
||
|
||
当 bug 状态为 `FIX_FAILED` 时,在状态列旁或下方显示 `failure_reason` 摘要(超长截断)。
|
||
|
||
### 3.3 BugDetail 页面
|
||
|
||
**文件**: `web/src/pages/BugDetail.tsx`
|
||
|
||
当状态为 `FIX_FAILED` 且 `failure_reason` 不为空时,增加醒目的失败原因卡片:
|
||
|
||
```tsx
|
||
{bug.failure_reason && (
|
||
<div className="card" style={{ borderLeft: '3px solid var(--error)' }}>
|
||
<h2><AlertTriangle size={16} /> 修复失败原因</h2>
|
||
<pre className="code-block error">{bug.failure_reason}</pre>
|
||
</div>
|
||
)}
|
||
```
|
||
|
||
### 3.4 RepairList 页面
|
||
|
||
**文件**: `web/src/pages/RepairList.tsx`
|
||
|
||
表格新增「失败原因」列,显示截断文本(`text-overflow: ellipsis`)。
|
||
|
||
---
|
||
|
||
## 文件变更清单
|
||
|
||
### 后端
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `app/models.py` | 修改 | 新增 Project + ProjectUpdate;ErrorLog 增加 failure_reason |
|
||
| `app/main.py` | 修改 | 项目 CRUD API;修改 report_log / update_task_status / create_repair_report |
|
||
| `app/database.py` | 修改 | 追加迁移 SQL(Project 表 + failure_reason 列) |
|
||
| `repair_agent/agent/task_manager.py` | 修改 | 新增 get_project_info() |
|
||
| `repair_agent/agent/core.py` | 修改 | fix_project / fix_single_bug 优先使用 API 配置 |
|
||
|
||
### 前端
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `web/src/api.ts` | 修改 | Project 类型 + API 函数;ErrorLog 增加 failure_reason |
|
||
| `web/src/App.tsx` | 修改 | 路由 + 侧边栏增加「项目管理」 |
|
||
| `web/src/pages/ProjectList.tsx` | **新增** | 项目列表 + 行内编辑 |
|
||
| `web/src/pages/BugList.tsx` | 修改 | 展示 failure_reason |
|
||
| `web/src/pages/BugDetail.tsx` | 修改 | 突出展示 failure_reason |
|
||
| `web/src/pages/RepairList.tsx` | 修改 | 增加失败原因列 |
|
||
|
||
---
|
||
|
||
## 实施顺序
|
||
|
||
1. **后端模型** — 新增 Project 模型 + ErrorLog 增加 failure_reason + 数据库迁移
|
||
2. **后端 API** — 项目 CRUD + 修改 report_log / update_task_status / create_repair_report
|
||
3. **Repair Agent** — task_manager 获取项目配置 + core.py 优先使用 API 配置
|
||
4. **前端 API 层** — api.ts 类型和函数更新
|
||
5. **前端页面** — ProjectList 新页面 + BugList / BugDetail / RepairList 展示 failure_reason
|
||
6. **路由 / 侧边栏** — App.tsx 接入项目管理入口
|
||
|
||
---
|
||
|
||
## 验证方式
|
||
|
||
1. 调用 `POST /api/v1/logs/report` 带 `repo_url` → 检查 Project 表自动创建
|
||
2. Web 端「项目管理」页 → 编辑 `repo_url` / `local_path` → 验证保存成功
|
||
3. Repair Agent `status` 命令 → 验证读取到 API 配置的 `repo_url` + `local_path`
|
||
4. 触发一次修复失败 → 检查 `ErrorLog.failure_reason` 已写入
|
||
5. Web 端 BugList / BugDetail / RepairList → 确认失败原因正确展示
|