From e59f8181b3c3fcc076f44cdfd69965b5c033c258 Mon Sep 17 00:00:00 2001 From: OG T Date: Fri, 10 Apr 2026 00:56:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(telegram):=20=E6=AD=B7=E5=8F=B2=E6=8C=89?= =?UTF-8?q?=E9=88=95=E6=94=B9=E5=BE=9E=20AnomalyCounter(Redis)=20=E8=AE=80?= =?UTF-8?q?=E9=A0=BB=E7=8E=87=EF=BC=8C=E4=BF=AE=E5=BE=A9=E6=B0=B8=E9=81=A0?= =?UTF-8?q?=E9=A1=AF=E7=A4=BA=E3=80=8C=E7=84=A1=E9=A0=BB=E7=8E=87=E7=B5=B1?= =?UTF-8?q?=E8=A8=88=E8=B3=87=E6=96=99=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根本原因: frequency_stats 從未持久化到 DB,get_by_id() 回傳永遠是 None 修復: 用 AnomalyCounter.derive_key_from_incident() 推導 anomaly_key, 直接從 Redis 查即時頻率與處置分佈統計 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/services/telegram_gateway.py | 60 ++++++++++++++--------- 1 file changed, 37 insertions(+), 23 deletions(-) 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: """