diff --git a/apps/api/src/services/report_generation_service.py b/apps/api/src/services/report_generation_service.py index 15b27db8..d7056485 100644 --- a/apps/api/src/services/report_generation_service.py +++ b/apps/api/src/services/report_generation_service.py @@ -218,24 +218,30 @@ class ReportGenerationService: } async def _collect_repair_stats(self, since: datetime) -> dict: - """收集自動修復統計(approval_requests 表)""" - from sqlalchemy import func, select + """ + 收集自動修復統計(IncidentRecord.outcome JSON) + + 2026-04-14 Claude Sonnet 4.6 修復 — 原本引用不存在的 ApprovalRequestRecord, + 實際 execution_success 儲存在 IncidentRecord.outcome JSON 欄位。 + """ + from sqlalchemy import func, select, text from src.db.base import get_db_context - from src.db.models import ApprovalRequestRecord + from src.db.models import IncidentRecord async with get_db_context() as db: + # PostgreSQL JSON 路徑查詢:outcome->>'execution_success' success = await db.scalar( - select(func.count()).select_from(ApprovalRequestRecord).where( - ApprovalRequestRecord.created_at >= since, - ApprovalRequestRecord.execution_success.is_(True), + select(func.count()).select_from(IncidentRecord).where( + IncidentRecord.created_at >= since, + text("outcome->>'execution_success' = 'true'"), ) ) or 0 failed = await db.scalar( - select(func.count()).select_from(ApprovalRequestRecord).where( - ApprovalRequestRecord.created_at >= since, - ApprovalRequestRecord.execution_success.is_(False), + select(func.count()).select_from(IncidentRecord).where( + IncidentRecord.created_at >= since, + text("outcome->>'execution_success' = 'false'"), ) ) or 0 @@ -257,17 +263,21 @@ class ReportGenerationService: return int(count) async def _collect_playbook_count(self) -> int: - """收集活躍 Playbook 數量""" - from sqlalchemy import func, select + """ + 收集活躍 Playbook 數量 - from src.db.base import get_db_context - from src.db.models import PlaybookRecord + 2026-04-14 Claude Sonnet 4.6 修復 — Playbook 儲存在 Redis 非 PostgreSQL, + 改用 playbook_service.list_playbooks() 讀 Redis。 + """ + from src.services.playbook_service import get_playbook_service - async with get_db_context() as db: - count = await db.scalar( - select(func.count()).select_from(PlaybookRecord) - ) or 0 - return int(count) + try: + svc = get_playbook_service() + playbooks, total = await svc.list_playbooks(limit=1000) + return int(total or len(playbooks)) + except Exception as e: + logger.warning("daily_kpi_playbook_count_failed", error=str(e)) + return 0 def format_daily_report(self, kpi: DailyKpi) -> str: """