# 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 关联的 Bug(MERGED 状态) 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 关联的 Bug(MERGED 状态) 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 && (
待审核 PR #{bug.pr_number}: {bug.branch_name} → main
)} ``` --- ## 实施计划 ### 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 - [ ] 配置 Webhook(PR 合并事件) - [ ] **测试验证** - [ ] 端到端测试完整流程 - [ ] 验证 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 - 更新:待更新 - 负责人:待指定