log-center/docs/bug-repair-workflow-optimization.md
zyc 5611839fd8
Some checks failed
Build and Deploy Log Center / build-and-deploy (push) Failing after 1m55s
fix git pr
2026-02-25 10:55:26 +08:00

1021 lines
36 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Bug 自动修复工作流优化方案
> 文档版本: v1.0
> 创建日期: 2026-02-25
> 状态: 待实施
---
## 📋 目录
- [背景](#背景)
- [当前问题](#当前问题)
- [优化方案](#优化方案)
- [状态设计](#状态设计)
- [完整工作流程](#完整工作流程)
- [Web 界面设计](#web-界面设计)
- [技术实施](#技术实施)
- [实施计划](#实施计划)
- [决策清单](#决策清单)
---
## 背景
### 现状
当前 Repair Agent 实现了 Bug 的自动修复功能,但存在以下问题:
1. **Git 工作流不规范**:修复后直接在本地合并到 main 分支,无法在 Gitea 追溯
2. **缺少人工审核**AI 修复的代码直接进入主分支,存在风险
3. **状态过于复杂**9 个状态流转复杂,难以维护
4. **Web 界面功能缺失**:无法在日志中台操作 PR 合并
### 目标
- ✅ 规范 Git 工作流,通过 PR 合并代码
- ✅ 增加人工审核环节,保障代码质量
- ✅ 简化 Bug 状态,优化状态流转
- ✅ 完善 Web 界面,提供便捷的操作入口
---
## 当前问题
### 问题 1本地合并无法追溯
**当前流程:**
```bash
git checkout main
git merge fix/auto-xxx # ← 本地合并
git push origin main
git branch -d fix/auto-xxx
```
**问题:**
- ❌ Gitea 仓库里没有 PR 记录
- ❌ 无法在 Gitea UI 追溯这个修复来自哪个分支
- ❌ 团队成员不知道这个变更是怎么来的
- ❌ 无法进行代码审核和讨论
### 问题 2状态过多且流转复杂
**当前状态9 个):**
```python
NEW VERIFYING CANNOT_REPRODUCE
PENDING_FIX FIXING FIXED VERIFIED DEPLOYED
FIX_FAILED
```
**问题:**
-`VERIFYING``PENDING_FIX` 功能重叠
-`FIXED``VERIFIED` 区分不明确
-`CANNOT_REPRODUCE``FIX_FAILED` 都表示失败
### 问题 3缺少人工审核环节
**当前流程:**
```
AI 修复 → commit → push → 本地 merge → push main
```
- ❌ 没有审核环节
- ❌ AI 改错了直接污染主分支
- ❌ 无法事前检查代码质量
---
## 优化方案
### 核心改进
1. **通过 PR 合并代码**AI 修复后自动创建 PR保留审核环节
2. **简化 Bug 状态**:从 9 个减少到 6 个,流转更清晰
3. **完善 Web 界面**:提供 PR 审核入口
### 关键对比
| 维度 | 当前方式(本地合并) | 优化方式PR 合并) |
|------|---------------------|-------------------|
| **追溯性** | ❌ 无 PR 记录 | ✅ Gitea 有完整记录 |
| **审核** | ❌ 无审核环节 | ✅ 人工审核 |
| **安全性** | ⚠️ 直接影响 main | ✅ 合并前可检查 |
| **协作** | ❌ 团队不可见 | ✅ Gitea UI 可见 |
| **回滚** | 需要 revert | 关闭 PR 即可 |
---
## 状态设计
### 优化后的状态6 个)
```python
class BugStatus(str, Enum):
"""优化后的 Bug 状态"""
NEW = "NEW" # 新发现的 Bug
FIXING = "FIXING" # AI 正在修复
PENDING_REVIEW = "PENDING_REVIEW" # 已修复,等待人工审核(有 PR
MERGED = "MERGED" # PR 已合并到 main 分支
DEPLOYED = "DEPLOYED" # 已部署到生产环境
FAILED = "FAILED" # 修复失败或无法修复
```
### 状态流转图
```
┌─────┐ Agent扫描 ┌────────┐ AI修复成功 ┌──────────────┐
│ NEW │ ───────────▶ │ FIXING │ ────────────▶ │PENDING_REVIEW│
└─────┘ └────────┘ └──────────────┘
│ │
AI修复失败 人工审核合并
↓ ↓
┌────────┐ ┌────────┐
│ FAILED │ │ MERGED │
└────────┘ └────────┘
CI/CD部署
┌──────────┐
│ DEPLOYED │
└──────────┘
```
### 状态说明
| 状态 | 说明 | 触发条件 | 后续操作 |
|------|------|---------|---------|
| **NEW** | 新发现的 Bug | Log Center 接收到错误日志 | Agent 定时扫描并修复 |
| **FIXING** | AI 正在修复 | Agent 开始执行修复任务 | AI 修复完成或失败 |
| **PENDING_REVIEW** | 等待审核 | AI 修复成功PR 已创建 | 人工在 Gitea 审核 |
| **MERGED** | 已合并 | PR 在 Gitea 被合并 | CI/CD 自动部署 |
| **DEPLOYED** | 已部署 | 部署到生产环境成功 | 结束 |
| **FAILED** | 修复失败 | AI 无法修复或测试失败 | 人工介入 |
---
## 完整工作流程
### 阶段 1自动修复Repair Agent
```python
1. 扫描 NEW 状态的 Bug
2. 更新状态: NEW FIXING
3. 调用 Claude Code CLI 修复代码
4. 运行测试验证修复
5. commit + push fix 分支到远程
6. 调用 Gitea API 创建 PR
7. 更新状态: FIXING PENDING_REVIEW
8. 上传修复报告包含 PR 链接
```
**关键数据结构:**
```json
{
"bug_id": 123,
"status": "PENDING_REVIEW",
"repair_report": {
"pr_number": 45,
"pr_url": "https://gitea.xxx/owner/repo/pulls/45",
"branch_name": "fix/auto-20260225-1430",
"modified_files": ["app/views.py", "app/models.py"],
"diff": "...",
"test_passed": true,
"test_output": "..."
}
}
```
---
### 阶段 2人工审核Web 界面)
#### 方案 A跳转到 Gitea 审核(推荐)
**优势:**
- ✅ 实现简单,只需提供跳转链接
- ✅ 利用 Gitea 原生 PR 功能
- ✅ 支持完整的代码审核流程
**用户操作流程:**
```
1. 打开日志中台 Bug 列表
2. 筛选 "PENDING_REVIEW" 状态
3. 点击 Bug 查看详情
4. 查看修复报告AI 分析、修改文件、测试结果)
5. 点击 "前往 Gitea 审核 PR" 按钮 → 新标签页打开
6. 在 Gitea 查看完整 diff
7. 确认无误 → 点击 "Merge" 按钮
或发现问题 → 点击 "Close" 并标记为 FAILED
```
#### 方案 B在日志中台内审核高级
**优势:**
- ✅ 无需跳转,统一操作界面
- ✅ 可定制审核流程
**劣势:**
- ⚠️ 需要开发 diff viewer
- ⚠️ 需要对接 Gitea API
**用户操作流程:**
```
1. 在日志中台 Bug 详情页
2. 查看嵌入的代码 diff
3. 点击 "批准并合并" 按钮
4. 后端调用 Gitea API 合并 PR
```
---
### 阶段 3自动部署CI/CD + Webhook
#### 3.1 Gitea Webhook 通知
**Gitea 配置:**
```
Settings → Webhooks → Add Webhook
- URL: https://your-log-center.com/api/webhooks/gitea
- Events: Pull Requests (merged)
- Secret: <配置密钥>
```
**日志中台接收:**
```python
@router.post("/webhooks/gitea")
async def gitea_webhook(payload: dict):
"""接收 Gitea PR 合并事件"""
if payload["action"] == "merged":
pr_number = payload["pull_request"]["number"]
# 通过 PR 号找到对应的 Bug
bug = await find_bug_by_pr_number(pr_number)
if bug and bug.status == BugStatus.PENDING_REVIEW:
# 更新状态: PENDING_REVIEW → MERGED
await update_bug_status(bug.id, BugStatus.MERGED)
logger.info(f"Bug #{bug.id} PR 已合并,等待部署")
```
#### 3.2 部署成功通知
**CI/CD Pipeline 配置:**
```yaml
# .gitlab-ci.yml 或 Jenkinsfile
deploy:
stage: deploy
script:
- deploy_to_production.sh
- |
# 部署成功后通知日志中台
curl -X POST https://your-log-center.com/api/webhooks/deployment \
-H "Content-Type: application/json" \
-H "X-Secret: ${WEBHOOK_SECRET}" \
-d '{
"project_id": "rtc_backend",
"status": "success",
"commit": "'$CI_COMMIT_SHA'"
}'
```
**日志中台更新状态:**
```python
@router.post("/webhooks/deployment")
async def deployment_webhook(payload: dict):
"""接收部署成功通知"""
if payload["status"] == "success":
commit = payload["commit"]
# 找到该 commit 关联的 BugMERGED 状态)
bugs = await find_bugs_by_commit(commit, status=BugStatus.MERGED)
for bug in bugs:
# 更新状态: MERGED → DEPLOYED
await update_bug_status(bug.id, BugStatus.DEPLOYED)
logger.info(f"Bug #{bug.id} 已部署到生产环境")
```
---
## Web 界面设计
### 1. Bug 列表页
```
┌────────────────────────────────────────────────────────────┐
│ Bug 自动修复看板 🔍 [搜索框] [筛选▼] │
├────────────────────────────────────────────────────────────┤
│ │
│ 📊 今日统计 │
│ ┌──────────┬──────────┬──────────┬──────────┬──────────┐ │
│ │ 🆕 新发现 │ 🔧 修复中 │ 🟡 待审核 │ ✅ 已合并 │ 🚀 已部署 │ │
│ │ 12 │ 3 │ 5 │ 8 │ 15 │ │
│ └──────────┴──────────┴──────────┴──────────┴──────────┘ │
│ │
│ 状态筛选: [ 全部 ] [ 🟡 待审核 ] [ 🔧 修复中 ] [ ❌ 失败 ] │
│ │
│ 🟡 待审核 (需要你的操作) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ #123 │ TypeError in user_login │ 🟡 待审核 │ 详情 │ │
│ │ │ rtc_backend | 2h ago │ │ → │ │
│ ├──────┼──────────────────────────────┼──────────┼─────┤ │
│ │ #124 │ NullPointerException │ 🟡 待审核 │ 详情 │ │
│ │ │ rtc_backend | 1h ago │ │ → │ │
│ ├──────┼──────────────────────────────┼──────────┼─────┤ │
│ │ #125 │ DB connection timeout │ 🟡 待审核 │ 详情 │ │
│ │ │ rtc_web | 30m ago │ │ → │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ 🔧 修复中 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ #126 │ API endpoint error │ 🔧 修复中 │ ... │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
```
---
### 2. Bug 详情页(方案 A跳转到 Gitea
```
┌─────────────────────────────────────────────────────────────┐
│ ← 返回列表 Bug #123 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📌 基本信息 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 标题: TypeError: 'NoneType' object is not iterable │ │
│ │ 项目: rtc_backend │ │
│ │ 状态: 🟡 PENDING_REVIEW (待审核) │ │
│ │ 环境: production │ │
│ │ 发现时间: 2026-02-25 12:30:15 │ │
│ │ 修复时间: 2026-02-25 14:30:45 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 🐛 错误详情 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 文件: app/views.py │ │
│ │ 行号: 24 │ │
│ │ 错误: TypeError: 'NoneType' object is not iterable │ │
│ │ │ │
│ │ 堆栈: │ │
│ │ File "app/views.py", line 24, in user_login │ │
│ │ for item in request.user: │ │
│ │ TypeError: 'NoneType' object is not iterable │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 📋 修复报告 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ AI 分析: │ │
│ │ request.user 可能为 None需要添加 null 检查 │ │
│ │ │ │
│ │ 修改文件: 2 个 │ │
│ │ - app/views.py (+3, -1) │ │
│ │ - tests/test_views.py (+12, -0) │ │
│ │ │ │
│ │ 测试结果: ✅ 通过 (2 tests passed) │ │
│ │ │ │
│ │ 修复分支: fix/auto-20260225-1430 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 🔗 Pull Request │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ PR #45: fix: auto repair bug #123 │ │
│ │ fix/auto-20260225-1430 → main │ │
│ │ │ │
│ │ 修改: 2 files changed (+15, -1) │ │
│ │ 状态: 🟡 Open (等待审核) │ │
│ │ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ 🔍 前往 Gitea 审核 PR → │ ← 主要操作 │ │
│ │ └──────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 📄 代码变更预览 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ app/views.py │ │
│ │ ───────────────────────────────────── │ │
│ │ 23 │ def user_login(request): │ │
│ │ -24 │ for item in request.user: │ │ ← 删除
│ │ +24 │ if request.user is not None: │ │ ← 新增
│ │ +25 │ for item in request.user: │ │ ← 新增
│ │ 26 │ # ... rest of code │ │
│ │ │ │
│ │ [查看完整 diff →] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 📝 操作日志 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 2026-02-25 14:30 状态更新: FIXING → PENDING_REVIEW │ │
│ │ 2026-02-25 14:28 AI 修复完成PR 已创建 │ │
│ │ 2026-02-25 14:25 开始自动修复 │ │
│ │ 2026-02-25 12:30 Bug 已记录 │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
### 3. 统计看板
```
┌────────────────────────────────────────────────────────────┐
│ 📊 Bug 修复统计 时间范围: [最近 7 天 ▼]│
├────────────────────────────────────────────────────────────┤
│ │
│ 整体指标 │
│ ┌──────────┬──────────┬──────────┬──────────┐ │
│ │ 总计发现 │ 自动修复 │ 修复成功率│ 平均耗时 │ │
│ │ 156 │ 142 │ 91.0% │ 12 min │ │
│ └──────────┴──────────┴──────────┴──────────┘ │
│ │
│ 每日趋势 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 40 ┤ │ │
│ │ 30 ┤ ● ● │ │
│ │ 20 ┤ ● ● ● ● │ │
│ │ 10 ┤ ● ● ● ● ● ● │ │
│ │ 0 └────────────────────────────────────── │ │
│ │ Mon Tue Wed Thu Fri Sat Sun │ │
│ │ │ │
│ │ ■ 新发现 ■ 自动修复 ■ 修复失败 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 项目分布 │
│ ┌──────────────┬───────────┬───────────┬─────────┐ │
│ │ 项目 │ 待审核 │ 已合并 │ 失败 │ │
│ ├──────────────┼───────────┼───────────┼─────────┤ │
│ │ rtc_backend │ 3 │ 12 │ 1 │ │
│ │ rtc_web │ 2 │ 8 │ 0 │ │
│ │ airhub_app │ 0 │ 5 │ 2 │ │
│ └──────────────┴───────────┴───────────┴─────────┘ │
└────────────────────────────────────────────────────────────┘
```
---
## 技术实施
### 修改清单
#### 1. Repair Agent 端
**文件:`log_center/repair_agent/agent/git_manager.py`**
```python
def create_pull_request(
self,
title: str,
description: str,
gitea_url: str,
gitea_token: str,
) -> tuple[bool, str]:
"""
调用 Gitea API 创建 Pull Request
Args:
title: PR 标题
description: PR 描述
gitea_url: Gitea 服务器地址
gitea_token: Gitea API Token
Returns:
(是否成功, PR URL 或错误信息)
"""
if not self.repo or not self.github_repo:
return False, "未配置 Git 仓库"
try:
# 解析仓库信息
# github_repo 格式: https://gitea.xxx/owner/repo.git
import re
match = re.search(r'([^/]+)/([^/]+?)(?:\.git)?$', self.github_repo)
if not match:
return False, "无法解析仓库信息"
owner, repo = match.groups()
current_branch = self.repo.active_branch.name
base_branch = "main" if "main" in self.repo.heads else "master"
# 调用 Gitea API
import httpx
api_url = f"{gitea_url}/api/v1/repos/{owner}/{repo}/pulls"
headers = {
"Authorization": f"token {gitea_token}",
"Content-Type": "application/json",
}
payload = {
"title": title,
"head": current_branch,
"base": base_branch,
"body": description,
}
response = httpx.post(api_url, json=payload, headers=headers, timeout=30)
response.raise_for_status()
data = response.json()
pr_url = data.get("html_url", "")
pr_number = data.get("number", 0)
logger.info(f"PR 创建成功: #{pr_number} - {pr_url}")
return True, pr_url
except Exception as e:
logger.error(f"创建 PR 失败: {e}")
return False, str(e)
```
**文件:`log_center/repair_agent/agent/core.py`**
修改自动提交部分Line 256-267
```python
# 自动提交并创建 PR仅在 Git 启用时)
if git_enabled and auto_commit and modified_files and git_manager:
bug_ids = ", ".join([f"#{b.id}" for b in bugs])
commit_msg = f"fix: auto repair bugs {bug_ids}"
# Step 1: commit 代码
git_manager.commit(commit_msg)
logger.info("代码已提交")
# Step 2: push fix 分支
git_manager.push()
logger.info("fix 分支已推送")
# Step 3: 创建 PR替代原来的 merge_to_main_and_push
success, pr_url = git_manager.create_pull_request(
title=commit_msg,
description=self._generate_pr_description(bugs, output, modified_files),
gitea_url=settings.gitea_url,
gitea_token=settings.gitea_token,
)
if success:
logger.info(f"PR 已创建: {pr_url}")
# 更新修复报告,添加 PR 信息
for bug in bugs:
self._update_pr_info(bug.id, pr_url)
else:
logger.warning(f"PR 创建失败: {pr_url},请手动创建")
elif not git_enabled and auto_commit:
logger.info("未配置 GitHub 仓库,跳过自动提交")
```
新增辅助方法:
```python
def _generate_pr_description(
self,
bugs: list[Bug],
ai_output: str,
modified_files: list[str],
) -> str:
"""生成 PR 描述"""
lines = [
f"## 🤖 AI 自动修复",
"",
f"本 PR 修复了 {len(bugs)} 个 Bug",
"",
]
for bug in bugs:
lines.append(f"- Bug #{bug.id}: {bug.error.type} - {bug.error.message[:50]}")
lines.extend([
"",
"## 📝 修复说明",
"",
ai_output[:500], # 截取前 500 字符
"",
"## 📄 修改文件",
"",
])
for file in modified_files[:10]: # 最多显示 10 个文件
lines.append(f"- `{file}`")
lines.extend([
"",
"---",
"",
"⚠️ **请仔细审核代码变更后再合并**",
])
return "\n".join(lines)
def _update_pr_info(self, bug_id: int, pr_url: str):
"""更新 Bug 的 PR 信息"""
try:
# 从 PR URL 提取 PR 号
import re
match = re.search(r'/pulls/(\d+)', pr_url)
pr_number = int(match.group(1)) if match else 0
# 调用 TaskManager 更新
self.task_manager.update_pr_info(bug_id, pr_number, pr_url)
except Exception as e:
logger.error(f"更新 PR 信息失败: {e}")
```
**文件:`log_center/repair_agent/models/bug.py`**
简化状态枚举:
```python
class BugStatus(str, Enum):
"""Bug 状态"""
NEW = "NEW" # 新发现
FIXING = "FIXING" # 修复中
PENDING_REVIEW = "PENDING_REVIEW" # 待审核(有 PR
MERGED = "MERGED" # 已合并
DEPLOYED = "DEPLOYED" # 已部署
FAILED = "FAILED" # 失败
```
**文件:`log_center/repair_agent/config/settings.py`**
新增配置项:
```python
class Settings(BaseSettings):
# ... 现有配置 ...
# Gitea 配置
gitea_url: str = Field(
default="https://gitea.airlabs.art",
description="Gitea 服务器地址"
)
gitea_token: str = Field(
default="",
description="Gitea API Token"
)
# PR 配置
auto_create_pr: bool = Field(
default=True,
description="是否自动创建 PR"
)
```
---
#### 2. Log Center 端
**文件:`log_center/app/models.py`**
更新 Bug 模型:
```python
class ErrorLog(Base):
# ... 现有字段 ...
# 新增字段
pr_number = Column(Integer, nullable=True, comment="PR 编号")
pr_url = Column(String(500), nullable=True, comment="PR 链接")
branch_name = Column(String(200), nullable=True, comment="修复分支名")
```
**文件:`log_center/app/api/webhooks.py`**
新增 Gitea Webhook 接收端点:
```python
@router.post("/webhooks/gitea")
async def gitea_webhook(
request: Request,
db: Session = Depends(get_db),
):
"""接收 Gitea PR 合并事件"""
try:
payload = await request.json()
# 验证签名(可选)
# verify_gitea_signature(request.headers, payload)
action = payload.get("action")
if action == "merged":
# PR 已合并
pr_number = payload["pull_request"]["number"]
repo_name = payload["repository"]["name"]
# 通过 PR 号和项目名找到对应的 Bug
bug = db.query(ErrorLog).filter(
ErrorLog.pr_number == pr_number,
ErrorLog.project_id.like(f"%{repo_name}%"),
ErrorLog.status == BugStatus.PENDING_REVIEW.value,
).first()
if bug:
# 更新状态
bug.status = BugStatus.MERGED.value
bug.updated_at = datetime.now()
db.commit()
logger.info(f"Bug #{bug.id} PR #{pr_number} 已合并")
return {"message": "Status updated"}
return {"message": "Event ignored"}
except Exception as e:
logger.error(f"处理 Gitea Webhook 失败: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.post("/webhooks/deployment")
async def deployment_webhook(
payload: dict,
db: Session = Depends(get_db),
):
"""接收部署成功通知"""
try:
if payload.get("status") == "success":
commit_sha = payload["commit"]
project_id = payload["project_id"]
# 找到该 commit 关联的 BugMERGED 状态)
bugs = db.query(ErrorLog).filter(
ErrorLog.project_id == project_id,
ErrorLog.status == BugStatus.MERGED.value,
).all()
# TODO: 通过 commit SHA 精确匹配(需要记录 commit
for bug in bugs:
bug.status = BugStatus.DEPLOYED.value
bug.deployed_at = datetime.now()
db.commit()
logger.info(f"已更新 {len(bugs)} 个 Bug 状态为 DEPLOYED")
return {"message": f"Updated {len(bugs)} bugs"}
return {"message": "Not a success deployment"}
except Exception as e:
logger.error(f"处理部署 Webhook 失败: {e}")
raise HTTPException(status_code=500, detail=str(e))
```
**文件:`log_center/app/api/tasks.py`**
新增接口:
```python
@router.put("/bugs/{bug_id}/pr-info")
async def update_pr_info(
bug_id: int,
pr_number: int,
pr_url: str,
db: Session = Depends(get_db),
):
"""更新 Bug 的 PR 信息"""
bug = db.query(ErrorLog).filter(ErrorLog.id == bug_id).first()
if not bug:
raise HTTPException(status_code=404, detail="Bug not found")
bug.pr_number = pr_number
bug.pr_url = pr_url
db.commit()
return {"message": "PR info updated"}
```
**文件:`log_center/web/src/pages/BugDetail.tsx`**
添加 PR 信息展示:
```tsx
{bug.status === 'PENDING_REVIEW' && bug.pr_url && (
<Card title="Pull Request" style={{ marginTop: 16 }}>
<Space direction="vertical" style={{ width: '100%' }}>
<div>
<Tag color="orange">待审核</Tag>
<Text strong>PR #{bug.pr_number}</Text>: {bug.branch_name} main
</div>
<Statistic
title="修改文件"
value={bug.repair_report?.modified_files?.length || 0}
suffix="个"
/>
<Button
type="primary"
icon={<LinkOutlined />}
href={bug.pr_url}
target="_blank"
>
前往 Gitea 审核 PR
</Button>
</Space>
</Card>
)}
```
---
## 实施计划
### Phase 1核心功能必须实施
**时间2-3 天**
#### 任务清单
- [ ] **Repair Agent 端**
- [ ] 新增 `create_pull_request()` 方法
- [ ] 修改 `fix_project()` 逻辑,移除 `merge_to_main_and_push()`
- [ ] 状态更新为 `PENDING_REVIEW`
- [ ] 修复报告包含 PR 信息
- [ ] 配置文件添加 Gitea 相关参数
- [ ] **Log Center 端**
- [ ] 数据库添加 `pr_number`, `pr_url`, `branch_name` 字段
- [ ] 简化 Bug 状态枚举6 个)
- [ ] 新增 Gitea Webhook 接收端点
- [ ] Bug 详情页显示 PR 信息
- [ ] "前往 Gitea 审核" 按钮
- [ ] **Gitea 配置**
- [ ] 创建 API Token
- [ ] 配置 WebhookPR 合并事件)
- [ ] **测试验证**
- [ ] 端到端测试完整流程
- [ ] 验证 PR 创建成功
- [ ] 验证状态自动更新
#### 预期效果
```
NEW → FIXING → PENDING_REVIEW (有 PR) → [人工审核] → MERGED
```
---
### Phase 2优化体验可选后续迭代
**时间1 周**
#### 任务清单
- [ ] **Web 界面优化**
- [ ] 嵌入 diff viewer 组件
- [ ] 在日志中台直接批准/拒绝 PR
- [ ] 批量审核功能
- [ ] 统计看板优化
- [ ] **通知系统**
- [ ] 邮件通知PR 创建、合并)
- [ ] 钉钉/企业微信通知
- [ ] 消息中心
- [ ] **高级功能**
- [ ] PR 自动审批(测试通过 + 低风险)
- [ ] 审批流程配置(指定审批人)
- [ ] PR 评论同步到日志中台
---
## 决策清单
### 需要确认的事项
- [ ] **1. 状态简化方案**
- ✅ 采用 6 个状态NEW, FIXING, PENDING_REVIEW, MERGED, DEPLOYED, FAILED
- ❌ 保留现有 9 个状态
- [ ] **2. 审核方式**
-**方案 A**:跳转到 Gitea 审核(推荐,简单快速)
-**方案 B**:在日志中台内审核(高级,需要更多开发)
- [ ] **3. 实施阶段**
-**Phase 1**核心功能必须2-3 天)
- ⚠️ **Phase 2**优化体验可选1 周)
- [ ] **4. Gitea 配置信息**
- [ ] Gitea 服务器地址:`https://gitea.xxx.com`
- [ ] Gitea Token已生成并配置
- [ ] Webhook Secret已配置
---
## 附录
### A. 数据库迁移脚本
```python
# alembic/versions/xxx_add_pr_info.py
def upgrade():
op.add_column('error_logs', sa.Column('pr_number', sa.Integer(), nullable=True))
op.add_column('error_logs', sa.Column('pr_url', sa.String(500), nullable=True))
op.add_column('error_logs', sa.Column('branch_name', sa.String(200), nullable=True))
# 迁移旧状态到新状态
op.execute("""
UPDATE error_logs
SET status = 'FAILED'
WHERE status IN ('FIX_FAILED', 'CANNOT_REPRODUCE')
""")
op.execute("""
UPDATE error_logs
SET status = 'NEW'
WHERE status IN ('VERIFYING', 'PENDING_FIX')
""")
def downgrade():
op.drop_column('error_logs', 'branch_name')
op.drop_column('error_logs', 'pr_url')
op.drop_column('error_logs', 'pr_number')
```
---
### B. Gitea Webhook 配置示例
**Webhook URL:**
```
https://your-log-center.com/api/webhooks/gitea
```
**Webhook Events:**
- ✅ Pull Requests (merged)
**Payload 示例:**
```json
{
"action": "merged",
"number": 45,
"pull_request": {
"id": 45,
"number": 45,
"title": "fix: auto repair bugs #123",
"head": {
"ref": "fix/auto-20260225-1430"
},
"base": {
"ref": "main"
},
"merged": true,
"merged_at": "2026-02-25T15:30:00Z"
},
"repository": {
"name": "rtc_backend",
"full_name": "airlabs/rtc_backend"
}
}
```
---
### C. CI/CD 部署通知配置
**.gitlab-ci.yml 示例:**
```yaml
stages:
- test
- build
- deploy
deploy:production:
stage: deploy
only:
- main
script:
- echo "Deploying to production..."
- ./deploy.sh
- |
# 通知日志中台部署成功
curl -X POST https://your-log-center.com/api/webhooks/deployment \
-H "Content-Type: application/json" \
-H "X-Secret: ${WEBHOOK_SECRET}" \
-d '{
"project_id": "rtc_backend",
"status": "success",
"commit": "'${CI_COMMIT_SHA}'",
"deployed_at": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'"
}'
```
---
## 参考资料
- [Gitea API 文档](https://docs.gitea.io/en-us/api-usage/)
- [Gitea Webhooks](https://docs.gitea.io/en-us/webhooks/)
- [Git 工作流最佳实践](https://www.atlassian.com/git/tutorials/comparing-workflows)
---
**文档维护:**
- 创建2026-02-25
- 更新:待更新
- 负责人:待指定