fix bug
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m27s

This commit is contained in:
zyc 2026-02-12 13:23:59 +08:00
parent 7ee724f8ac
commit fe62f9ca81
4 changed files with 92 additions and 127 deletions

View File

@ -16,8 +16,9 @@ GITHUB_REPO_RTC_WEB=
GITHUB_REPO_AIRHUB_APP= GITHUB_REPO_AIRHUB_APP=
# 项目路径映射 (project_id -> 本地路径) # 项目路径映射 (project_id -> 本地路径)
PATH_rtc_backend=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_backend PATH_RTC_BACKEND=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_backend
PATH_rtc_web=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_web PATH_RTC_WEB=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_web
PATH_AIRHUB_APP=/Users/maidong/Desktop/zyc/qiyuan_gitea/rtc_prd/airhub_app
# 安全配置 # 安全配置
MAX_RETRY_COUNT=3 MAX_RETRY_COUNT=3

View File

@ -20,16 +20,17 @@ class ClaudeService:
self, self,
prompt: str, prompt: str,
cwd: str, cwd: str,
tools: str = "Edit,Read,Write", allowed_tools: Optional[str] = None,
) -> tuple[bool, str]: ) -> tuple[bool, str]:
""" """
执行 Claude CLI 命令 执行 Claude CLI 命令
Args: Args:
prompt: 提示词 prompt: 提示词
cwd: 工作目录 cwd: 工作目录
tools: 可用工具 allowed_tools: 可选限制可用工具逗号分隔
None 时配合 --dangerously-skip-permissions 允许所有工具
Returns: Returns:
(成功与否, 输出内容) (成功与否, 输出内容)
""" """
@ -37,12 +38,15 @@ class ClaudeService:
cmd = [ cmd = [
self.cli_path, self.cli_path,
"-p", prompt, "-p", prompt,
"--tools", tools,
"--dangerously-skip-permissions", "--dangerously-skip-permissions",
] ]
logger.debug(f"执行 Claude CLI: {' '.join(cmd[:3])}...") if allowed_tools:
cmd.extend(["--allowedTools", allowed_tools])
logger.debug(f"执行 Claude CLI (cwd={cwd})")
logger.debug(f"Prompt 长度: {len(prompt)} 字符")
result = subprocess.run( result = subprocess.run(
cmd, cmd,
cwd=cwd, cwd=cwd,
@ -50,16 +54,17 @@ class ClaudeService:
text=True, text=True,
timeout=self.timeout, timeout=self.timeout,
) )
output = result.stdout + result.stderr output = result.stdout + result.stderr
if result.returncode == 0: if result.returncode == 0:
logger.info("Claude CLI 执行成功") logger.info("Claude CLI 执行成功")
logger.debug(f"输出前 500 字符: {output[:500]}")
return True, output.strip() return True, output.strip()
else: else:
logger.error(f"Claude CLI 执行失败: {output}") logger.error(f"Claude CLI 执行失败 (code={result.returncode}): {output[:500]}")
return False, output.strip() return False, output.strip()
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
logger.error(f"Claude CLI 超时 ({self.timeout}秒)") logger.error(f"Claude CLI 超时 ({self.timeout}秒)")
return False, "执行超时" return False, "执行超时"
@ -77,39 +82,44 @@ class ClaudeService:
) -> tuple[bool, str]: ) -> tuple[bool, str]:
""" """
批量修复多个 Bug 批量修复多个 Bug
Args: Args:
bugs: Bug 列表 bugs: Bug 列表
project_path: 项目路径 project_path: 项目路径
Returns: Returns:
(成功与否, Claude 输出) (成功与否, Claude 输出)
""" """
if not bugs: if not bugs:
return False, "没有需要修复的 Bug" return False, "没有需要修复的 Bug"
# 构造批量修复 Prompt # 构造批量修复 Prompt
prompt_parts = [ prompt_parts = [
f"请修复以下 {len(bugs)} 个 Bug", f"你是一个自动化 Bug 修复代理。请直接修复以下 {len(bugs)} 个 Bug。",
"",
"重要你必须使用工具Read、Edit、Write 等)直接修改源代码文件来修复 Bug。",
"不要只是分析问题或给出建议,你需要实际定位文件并编辑代码。",
"", "",
] ]
for bug in bugs: for bug in bugs:
prompt_parts.append(bug.format_for_prompt()) prompt_parts.append(bug.format_for_prompt())
prompt_parts.append("") prompt_parts.append("")
prompt_parts.extend([ prompt_parts.extend([
"## 修复要求", "## 修复要求",
"1. 依次修复上述所有 Bug", "1. 先用 Grep/Glob 定位相关源代码文件",
"2. 每个 Bug 只做最小必要的改动", "2. 用 Read 读取文件内容,理解上下文",
"3. 确保不破坏现有功能", "3. 用 Edit 或 Write 直接修改代码来修复 Bug",
"4. 修复完成后简要说明每个 Bug 的修复方式", "4. 每个 Bug 只做最小必要的改动",
"5. 确保不破坏现有功能",
"6. 修复完成后简要说明每个 Bug 的修复方式",
"", "",
"请开始修复。", "立即开始修复,直接编辑文件",
]) ])
prompt = "\n".join(prompt_parts) prompt = "\n".join(prompt_parts)
logger.info(f"开始批量修复 {len(bugs)} 个 Bug...") logger.info(f"开始批量修复 {len(bugs)} 个 Bug...")
return self.execute_prompt(prompt, project_path) return self.execute_prompt(prompt, project_path)
@ -135,7 +145,7 @@ class ClaudeService:
3. 可能影响的其他文件 3. 可能影响的其他文件
""" """
return self.execute_prompt(prompt, project_path, tools="Read") return self.execute_prompt(prompt, project_path, allowed_tools="Read,Grep,Glob")
def review_changes(self, diff: str, project_path: str) -> tuple[bool, str]: def review_changes(self, diff: str, project_path: str) -> tuple[bool, str]:
""" """
@ -164,4 +174,4 @@ class ClaudeService:
如果审核通过回复 "APPROVED"否则说明问题 如果审核通过回复 "APPROVED"否则说明问题
""" """
return self.execute_prompt(prompt, project_path, tools="Read") return self.execute_prompt(prompt, project_path, allowed_tools="Read,Grep,Glob")

View File

@ -88,15 +88,12 @@ class RepairScheduler:
try: try:
for project_id in self.projects: for project_id in self.projects:
bugs = task_manager.fetch_pending_bugs(project_id) bugs = task_manager.fetch_pending_bugs(project_id)
# 也拉取 UI 触发的 PENDING_FIX 状态
pending_bugs = self._fetch_pending_fix_bugs(task_manager, project_id)
all_bugs = bugs + pending_bugs
if not all_bugs: if not bugs:
logger.info(f" [{project_id}] 无 Bug") logger.info(f" [{project_id}] 无待修复 Bug")
continue continue
count = len(all_bugs) count = len(bugs)
total_found += count total_found += count
logger.info(f" [{project_id}] 发现 {count} 个待修复 Bug") logger.info(f" [{project_id}] 发现 {count} 个待修复 Bug")
@ -120,51 +117,6 @@ class RepairScheduler:
finally: finally:
task_manager.close() task_manager.close()
@staticmethod
def _fetch_pending_fix_bugs(
task_manager: TaskManager, project_id: str
) -> list:
"""获取 PENDING_FIX 状态的 Bug由 UI 触发)"""
try:
import httpx
params = {"status": "PENDING_FIX", "project_id": project_id}
response = task_manager.client.get(
f"{task_manager.base_url}/api/v1/bugs", params=params
)
response.raise_for_status()
data = response.json()
from ..models import Bug, BugStatus
bugs = []
for item in data.get("items", []):
stack_trace = item.get("stack_trace")
if isinstance(stack_trace, str):
stack_trace = stack_trace.split("\n")
bug = Bug(
id=item["id"],
project_id=item["project_id"],
environment=item.get("environment", "production"),
level=item.get("level", "ERROR"),
error={
"type": item.get("error_type", "Unknown"),
"message": item.get("error_message", ""),
"file_path": item.get("file_path"),
"line_number": item.get("line_number"),
"stack_trace": stack_trace,
},
context=item.get("context"),
status=BugStatus(item.get("status", "PENDING_FIX")),
retry_count=item.get("retry_count", 0),
)
bugs.append(bug)
return bugs
except Exception as e:
logger.debug(f"获取 PENDING_FIX Bug 失败: {e}")
return []
def _run_repair(self, project_id: str) -> int: def _run_repair(self, project_id: str) -> int:
"""对指定项目执行修复,返回成功修复的数量""" """对指定项目执行修复,返回成功修复的数量"""
if self._repairing: if self._repairing:

View File

@ -18,58 +18,60 @@ class TaskManager:
def fetch_pending_bugs(self, project_id: Optional[str] = None) -> list[Bug]: def fetch_pending_bugs(self, project_id: Optional[str] = None) -> list[Bug]:
""" """
获取待修复的 Bug 列表 获取待修复的 Bug 列表包括 NEW PENDING_FIX 状态
Args: Args:
project_id: 可选筛选特定项目 project_id: 可选筛选特定项目
Returns: Returns:
Bug 列表 Bug 列表已按 id 去重
""" """
try: all_bugs: dict[int, Bug] = {}
params = {"status": "NEW"}
if project_id: for status in ("NEW", "PENDING_FIX"):
params["project_id"] = project_id try:
params: dict[str, str] = {"status": status}
response = self.client.get( if project_id:
f"{self.base_url}/api/v1/bugs", params["project_id"] = project_id
params=params
) response = self.client.get(
response.raise_for_status() f"{self.base_url}/api/v1/bugs",
params=params,
data = response.json()
bugs = []
for item in data.get("items", []):
# stack_trace 可能是列表或字符串
stack_trace = item.get("stack_trace")
if isinstance(stack_trace, str):
stack_trace = stack_trace.split("\n")
bug = Bug(
id=item["id"],
project_id=item["project_id"],
environment=item.get("environment", "production"),
level=item.get("level", "ERROR"),
error={
"type": item.get("error_type", "Unknown"),
"message": item.get("error_message", ""),
"file_path": item.get("file_path"),
"line_number": item.get("line_number"),
"stack_trace": stack_trace,
},
context=item.get("context"),
status=BugStatus(item.get("status", "NEW")),
retry_count=item.get("retry_count", 0),
) )
bugs.append(bug) response.raise_for_status()
logger.info(f"获取到 {len(bugs)} 个待修复 Bug") data = response.json()
return bugs for item in data.get("items", []):
bug_id = item["id"]
except httpx.HTTPError as e: if bug_id in all_bugs:
logger.error(f"获取 Bug 列表失败: {e}") continue
return []
stack_trace = item.get("stack_trace")
if isinstance(stack_trace, str):
stack_trace = stack_trace.split("\n")
all_bugs[bug_id] = Bug(
id=bug_id,
project_id=item["project_id"],
environment=item.get("environment", "production"),
level=item.get("level", "ERROR"),
error={
"type": item.get("error_type", "Unknown"),
"message": item.get("error_message", ""),
"file_path": item.get("file_path"),
"line_number": item.get("line_number"),
"stack_trace": stack_trace,
},
context=item.get("context"),
status=BugStatus(item.get("status", "NEW")),
retry_count=item.get("retry_count", 0),
)
except httpx.HTTPError as e:
logger.error(f"获取 {status} 状态 Bug 列表失败: {e}")
bugs = list(all_bugs.values())
logger.info(f"获取到 {len(bugs)} 个待修复 BugNEW + PENDING_FIX")
return bugs
def update_status(self, bug_id: int, status: BugStatus, message: str = "") -> bool: def update_status(self, bug_id: int, status: BugStatus, message: str = "") -> bool:
""" """