diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index a55e035b..f547c4fe 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -2655,9 +2655,10 @@ class TelegramGateway: """ ADR-050 P2: 傳送事件頻率統計訊息 - 2026-04-01 Claude Code (ADR-050 P2): history button handler + 2026-04-10 ogt: 修復 — frequency_stats 不存 DB,改從 AnomalyCounter (Redis) 即時查詢 """ from src.repositories.incident_repository import get_incident_repository + from src.services.anomaly_counter import get_anomaly_counter, AnomalyCounter try: repo = get_incident_repository() @@ -2667,47 +2668,60 @@ class TelegramGateway: await self.send_notification(f"⚠️ 找不到事件 {html.escape(incident_id)}") return - fs = incident.frequency_stats - if not fs: + # frequency_stats 不持久化,從 AnomalyCounter (Redis) 即時查詢 + anomaly_key = AnomalyCounter.derive_key_from_incident(incident) + if not anomaly_key: await self.send_notification( - f"📊 事件歷史\n\n🔖 {html.escape(incident.incident_id)}\n\n無頻率統計資料" + f"📊 事件歷史\n\n🔖 {html.escape(incident_id)}\n\n⚠️ 無法推導告警鍵(signals 為空)" ) return + counter = get_anomaly_counter() + freq = await counter.record_anomaly({ + "alert_name": incident.signals[0].alert_name if incident.signals else "", + "service": incident.affected_services[0] if incident.affected_services else "", + "namespace": (incident.signals[0].labels or {}).get("namespace", "") if incident.signals else "", + "error_type": (incident.signals[0].labels or {}).get("reason", (incident.signals[0].labels or {}).get("error_type", "")) if incident.signals else "", + }) + disposition = await counter.get_disposition_stats(anomaly_key) + lines = [ f"📊 事件歷史統計", f"", - f"🔖 {html.escape(incident.incident_id)}", - f"🔑 告警鍵: {html.escape(fs.anomaly_key[:60])}", + f"🔖 {html.escape(incident_id)}", + f"🔑 告警鍵: {html.escape(anomaly_key)}", f"", f"⏱ 發生頻率", - f" 1小時: {fs.count_1h} 次", - f" 24小時: {fs.count_24h} 次", - f" 7天: {fs.count_7d} 次", - f" 30天: {fs.count_30d} 次", + f" 1小時: {freq.count_1h} 次", + f" 24小時: {freq.count_24h} 次", + f" 7天: {freq.count_7d} 次", + f" 30天: {freq.count_30d} 次", ] - if fs.auto_repair_count > 0: + if freq.auto_repair_count > 0: lines += [ f"", f"🔧 自動修復", - f" 執行次數: {fs.auto_repair_count}", + f" 執行次數: {freq.auto_repair_count}", ] - if fs.last_repair_action: - lines.append(f" 最後動作: {html.escape(fs.last_repair_action[:80])}") + if freq.last_repair_action: + lines.append(f" 最後動作: {html.escape(freq.last_repair_action)}") - # 2026-04-07 Claude Code: Sprint 4 D2 — 處置分佈明細 - total_res = fs.total_resolution_count + # 處置分佈 + auto_r = disposition.get("auto_repair_count", 0) + cold_s = disposition.get("cold_start_trust_count", 0) + human_a = disposition.get("human_approved_count", 0) + manual_r = disposition.get("manual_resolved_count", 0) + total_res = auto_r + cold_s + human_a + manual_r if total_res > 0: - auto_total = fs.auto_repair_count + fs.cold_start_trust_count - auto_rate = int(auto_total / total_res * 100) if total_res > 0 else 0 + auto_rate = int((auto_r + cold_s) / total_res * 100) lines += [ f"", f"📋 處置分佈 (共 {total_res} 次)", - f" 🤖 自動修復: {fs.auto_repair_count}", - f" ❄️ 冷啟動信任: {fs.cold_start_trust_count}", - f" 👤 人工審核: {fs.human_approved_count}", - f" 🔧 手動處理: {fs.manual_resolved_count}", + f" 🤖 自動修復: {auto_r}", + f" ❄️ 冷啟動信任: {cold_s}", + f" 👤 人工審核: {human_a}", + f" 🔧 手動處理: {manual_r}", f" 📈 自動化率: {auto_rate}%", ] @@ -2715,7 +2729,7 @@ class TelegramGateway: except Exception as e: logger.warning("send_incident_history_failed", incident_id=incident_id, error=str(e)) - await self.send_notification(f"⚠️ 無法取得歷史統計: {html.escape(str(e)[:100])}") + await self.send_notification(f"⚠️ 無法取得歷史統計: {html.escape(str(e))}") async def _send_reanalyze_result(self, incident_id: str) -> None: """