fix(api): reconcile completed stuck incidents
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 1m2s
CD Pipeline / build-and-deploy (push) Successful in 3m34s
CD Pipeline / post-deploy-checks (push) Successful in 1m35s

This commit is contained in:
Your Name
2026-05-19 11:45:15 +08:00
parent 50833a0efb
commit d0835a7be1
7 changed files with 393 additions and 61 deletions

View File

@@ -19,7 +19,12 @@ from sqlalchemy import select
from src.db.base import get_db_context
from src.db.models import IncidentRecord
from src.models.incident import Incident, IncidentFrequencyStats, IncidentStatus, Severity
from src.models.incident import (
Incident,
IncidentFrequencyStats,
IncidentStatus,
Severity,
)
from src.repositories.interfaces import IIncidentRepository
logger = structlog.get_logger(__name__)
@@ -41,8 +46,8 @@ def _record_to_incident(record: IncidentRecord) -> Incident:
return Incident(
incident_id=record.incident_id,
status=IncidentStatus(record.status),
severity=Severity(record.severity),
status=IncidentStatus(_normalize_status(record.status)),
severity=Severity(_normalize_severity(record.severity)),
signals=record.signals or [],
affected_services=record.affected_services or [],
proposal_ids=record.proposal_ids or [],
@@ -93,6 +98,36 @@ def _incident_to_record_data(incident: Incident) -> dict[str, Any]:
}
def _normalize_status(value: str | IncidentStatus) -> str:
if isinstance(value, IncidentStatus):
return value.value
raw = str(value)
if raw in IncidentStatus.__members__:
return IncidentStatus[raw].value
normalized = raw.strip().lower()
if normalized == "open":
return IncidentStatus.INVESTIGATING.value
return normalized
def _normalize_severity(value: str | Severity) -> str:
if isinstance(value, Severity):
return value.value
raw = str(value)
if raw in Severity.__members__:
return Severity[raw].value
legacy_map = {
"critical": Severity.P0.value,
"high": Severity.P1.value,
"warning": Severity.P2.value,
"medium": Severity.P2.value,
"info": Severity.P3.value,
"low": Severity.P3.value,
"none": Severity.P3.value,
}
return legacy_map.get(raw.strip().lower(), raw)
# =============================================================================
# IncidentDBRepository
# =============================================================================
@@ -136,8 +171,8 @@ class IncidentDBRepository(IIncidentRepository):
async def get_active(self) -> list[Incident]:
"""取得所有活躍的 Incident"""
active_statuses = [
IncidentStatus.INVESTIGATING.value,
IncidentStatus.MITIGATING.value,
IncidentStatus.INVESTIGATING,
IncidentStatus.MITIGATING,
]
async with get_db_context() as db:
result = await db.execute(