# PR 操作功能实施完成 > 实施日期: 2026-02-25 > 状态: ✅ 已完成 --- ## 📋 已实现功能 ### ✅ 1. 数据库字段扩展 **文件**: `app/models.py` 新增字段: ```python # PR Tracking pr_number: Optional[int] # PR 编号 pr_url: Optional[str] # PR 链接 branch_name: Optional[str] # 分支名称 # Rejection Tracking rejection_reason: Optional[str] # 拒绝原因 JSON rejection_count: int # 拒绝次数 last_rejected_at: Optional[datetime] # 最后拒绝时间 merged_at: Optional[datetime] # 合并时间 ``` ### ✅ 2. Gitea API 客户端 **文件**: `app/gitea_client.py` 功能: - `merge_pr()` - 合并 PR - `close_pr()` - 关闭 PR(带原因评论) - `add_pr_comment()` - 添加评论 - `merge_pr_by_url()` - 通过 URL 直接合并 - `close_pr_by_url()` - 通过 URL 直接关闭 ### ✅ 3. 后端 API 接口 **文件**: `app/main.py` 新增接口: #### POST `/api/v1/bugs/{bug_id}/merge-pr` 批准并合并 PR **请求**:无需 body **响应**: ```json { "message": "PR 已合并", "pr_url": "https://gitea.xxx/owner/repo/pulls/45", "bug_id": 123, "new_status": "FIXED" } ``` #### POST `/api/v1/bugs/{bug_id}/close-pr` 拒绝修复并关闭 PR **请求 Body**: ```json { "reason": "测试覆盖不足,缺少边界条件测试" } ``` **响应**: ```json { "message": "PR 已拒绝,Bug 将重新修复", "rejection_count": 1, "bug_id": 123, "new_status": "PENDING_FIX" } ``` ### ✅ 4. 前端界面 **文件**: `web/src/pages/BugDetail.tsx` **新增组件**: 1. **PR 信息展示区** - 显示 PR 编号、分支名 - "查看 PR" 外链按钮 - 拒绝次数提示 2. **操作按钮** - ✅ **批准并合并** 按钮(绿色) - ❌ **拒绝修复** 按钮(红色) 3. **拒绝原因模态框** - 常用模板快速填充 - 文本输入框(详细原因) - 确认/取消按钮 4. **状态消息** - 操作成功/失败提示 - 上次拒绝原因显示 --- ## 🎯 使用流程 ### 场景 1:批准并合并 ``` 1. 打开 Bug 详情页 2. 查看 PR 信息(可点击"查看 PR"跳转到 Gitea) 3. 确认修复正确 4. 点击 "批准并合并" 按钮 ↓ ✅ PR 合并成功,Bug 状态 → FIXED ``` ### 场景 2:拒绝修复 ``` 1. 打开 Bug 详情页 2. 查看 PR 信息 3. 发现问题,点击 "拒绝修复" 按钮 4. 弹出模态框: - 可点击模板快速填充常用原因 - 或手动输入详细原因 5. 点击 "确认拒绝" ↓ ✅ PR 已关闭,Bug 状态 → PENDING_FIX ✅ 原因已记录,Agent 将结合原因重新修复 ``` --- ## ⚙️ 配置步骤 ### 1. 配置 Gitea 凭证 创建 `/log_center/app/.env` 文件: ```bash # Gitea 配置 GITEA_URL=https://gitea.airlabs.art GITEA_TOKEN=your_gitea_token_here ``` **获取 Gitea Token**: 1. 登录 Gitea 2. 设置 → 应用 → 生成新令牌 3. 权限:`repo`(读写仓库) 4. 复制 Token 到 `.env` ### 2. 数据库迁移 需要执行数据库迁移添加新字段: ```sql -- 手动执行或使用 Alembic ALTER TABLE errorlog ADD COLUMN pr_number INT; ALTER TABLE errorlog ADD COLUMN pr_url VARCHAR(500); ALTER TABLE errorlog ADD COLUMN branch_name VARCHAR(200); ALTER TABLE errorlog ADD COLUMN rejection_reason TEXT; ALTER TABLE errorlog ADD COLUMN rejection_count INT DEFAULT 0; ALTER TABLE errorlog ADD COLUMN last_rejected_at TIMESTAMP; ALTER TABLE errorlog ADD COLUMN merged_at TIMESTAMP; ``` ### 3. 重启服务 ```bash # 后端 cd /log_center/app uvicorn main:app --reload # 前端 cd /log_center/web npm run dev ``` --- ## 🧪 测试 ### 测试 Gitea API(可选) 使用测试脚本验证 API: ```bash cd /log_center/repair_agent python test_gitea_api.py \ --gitea-url https://gitea.airlabs.art \ --token YOUR_TOKEN \ --owner owner \ --repo repo \ --pr-number 45 ``` ### 手动测试流程 1. **准备测试数据**: - 确保有一个状态为 `PENDING_FIX` 的 Bug - 确保该 Bug 有 `pr_url` 字段(Repair Agent 创建的) 2. **测试合并**: - 打开 Bug 详情页 - 点击"批准并合并" - 检查: - ✅ Bug 状态变为 `FIXED` - ✅ Gitea PR 状态为 `Merged` - ✅ `merged_at` 字段有值 3. **测试拒绝**: - 打开另一个有 PR 的 Bug - 点击"拒绝修复" - 输入原因:"测试覆盖不足" - 确认 - 检查: - ✅ Bug 状态变为 `PENDING_FIX` - ✅ Gitea PR 状态为 `Closed` - ✅ PR 有评论记录原因 - ✅ `rejection_count` +1 - ✅ `rejection_reason` 有值 --- ## 📸 界面预览 ### 有 PR 的 Bug 详情页 ``` ┌─────────────────────────────────────────────────────┐ │ TypeError: 'NoneType' object is not iterable │ │ 项目: rtc_backend 来源: 运行时 级别: ERROR │ │ 状态: [待修复] │ ├─────────────────────────────────────────────────────┤ │ Pull Request │ │ PR #45 | fix/auto-20260225-1430 │ │ [查看 PR →] 已拒绝 0 次 │ ├─────────────────────────────────────────────────────┤ │ 文件位置: app/views.py : 第 24 行 │ │ ... │ ├─────────────────────────────────────────────────────┤ │ [✅ 批准并合并] [❌ 拒绝修复] │ ← 关键按钮 └─────────────────────────────────────────────────────┘ ``` ### 拒绝原因模态框 ``` ┌─────────────────────────────────────────┐ │ 拒绝修复 │ ├─────────────────────────────────────────┤ │ 请说明拒绝原因,Agent 将根据反馈重新修复 │ │ │ │ 常用模板: │ │ [测试覆盖不足] [业务逻辑调整] [代码质量] │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ [文本输入框] │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────┘ │ │ │ │ [取消] [❌ 确认拒绝] │ └─────────────────────────────────────────┘ ``` --- ## 🔍 技术细节 ### 状态流转 ``` 【合并】 PENDING_FIX → 点击"批准并合并" → 调用 Gitea API merge → 更新 Bug 状态 FIXED → 设置 merged_at 【拒绝】 PENDING_FIX → 点击"拒绝修复" → 添加评论到 PR → 调用 Gitea API close → 更新 Bug 状态 PENDING_FIX → rejection_count +1 → 记录 rejection_reason → Agent 检测到 PENDING_FIX + 有 rejection_reason → 结合原因重新修复 ``` ### 安全性 1. **权限控制**:需要有效的 Gitea Token 2. **状态检查**:只有 `PENDING_FIX` 状态的 Bug 才能操作 3. **PR 验证**:必须有 `pr_url` 才能操作 4. **错误处理**:API 失败会显示详细错误信息 --- ## ❓ FAQ ### Q1: 按钮显示条件? **A:** 只有同时满足以下条件才显示: 1. Bug 有 `pr_url`(Agent 创建了 PR) 2. Bug 状态为 `PENDING_FIX` ### Q2: 拒绝后 Agent 会立即修复吗? **A:** 取决于 Agent 扫描频率: - 如果 Agent 定时扫描(如每小时),需等待下次扫描 - 可以手动触发 Agent 立即扫描特定项目 ### Q3: 合并失败怎么办? **A:** 常见失败原因: 1. Token 权限不足 → 检查 Token 权限 2. PR 有冲突 → 需要手动在 Gitea 解决冲突 3. PR 已关闭 → 检查 PR 状态 ### Q4: 能否批量操作? **A:** 当前版本不支持批量操作,计划在下个版本添加。 --- ## 🎯 后续优化 ### Phase 2(可选) 1. **批量操作**: - 批量合并多个 PR - 批量拒绝 2. **通知系统**: - 邮件通知审核结果 - 钉钉/企业微信通知 3. **审批流程**: - 指定审批人 - 需要多人审批 - 审批历史记录 4. **统计面板**: - PR 合并率 - 平均拒绝次数 - 审核耗时 --- ## ✅ 完成清单 - [x] 数据库字段扩展 - [x] Gitea API 客户端 - [x] 后端 API 接口 - [x] 前端界面实现 - [x] 拒绝原因模态框 - [x] 环境变量配置 - [x] 文档编写 **状态**: ✅ 功能已完整实现,可以部署使用!