# 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(`/api/v1/projects/${projectId}`); export const updateProject = (projectId: string, data: Partial) => 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 && (

修复失败原因

{bug.failure_reason}
)} ``` ### 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 → 确认失败原因正确展示