diff --git a/apps/api/src/api/v1/approvals.py b/apps/api/src/api/v1/approvals.py index 96793e928..9045828eb 100644 --- a/apps/api/src/api/v1/approvals.py +++ b/apps/api/src/api/v1/approvals.py @@ -43,6 +43,7 @@ from src.models.approval import ( SignResponse, ) from src.services.executor import OperationType, get_executor +from src.services.proposal_service import get_proposal_service router = APIRouter(prefix="/approvals", tags=["HITL Approvals"]) logger = get_logger("awoooi.approvals") @@ -629,6 +630,20 @@ async def sign_approval( background_tasks.add_task(execute_approved_action, approval) + # Phase 6.5: 更新關聯的 Incident 狀態為 RESOLVED + incident_id = approval.metadata.get("incident_id") if approval.metadata else None + if incident_id: + proposal_svc = get_proposal_service() + await proposal_svc.resolve_incident_after_approval( + incident_id=incident_id, + approval_id=str(approval_id), + ) + logger.info( + "incident_resolved_after_sign", + incident_id=incident_id, + approval_id=str(approval_id), + ) + return SignResponse( success=True, message=message, diff --git a/apps/api/src/services/proposal_service.py b/apps/api/src/services/proposal_service.py index 585057c62..1fc45edc5 100644 --- a/apps/api/src/services/proposal_service.py +++ b/apps/api/src/services/proposal_service.py @@ -501,6 +501,70 @@ class ProposalService: error=str(e), ) + # ========================================================================= + # Phase 6.5: 簽核完成後更新 Incident 狀態 + # ========================================================================= + + async def resolve_incident_after_approval( + self, + incident_id: str, + approval_id: str | None = None, + ) -> bool: + """ + 簽核完成後更新 Incident 狀態為 RESOLVED + + 當 Approval 達到所需簽核數時呼叫,更新: + 1. incident.status → RESOLVED + 2. incident.decision.state → completed (如果有) + + Args: + incident_id: Incident ID + approval_id: 簽核的 Approval ID (用於日誌) + + Returns: + 是否更新成功 + """ + try: + incident = await self._load_incident(incident_id) + if not incident: + logger.warning( + "resolve_incident_not_found", + incident_id=incident_id, + approval_id=approval_id, + ) + return False + + # 更新狀態 + old_status = incident.status + incident.status = IncidentStatus.RESOLVED + incident.updated_at = datetime.now(timezone.utc) + + # 更新 decision.state (如果有) + if incident.decision: + incident.decision.state = "completed" + + # 持久化 + await self._persist_incident(incident) + + logger.info( + "incident_resolved_after_approval", + incident_id=incident_id, + approval_id=approval_id, + old_status=old_status.value, + new_status="resolved", + ) + + return True + + except Exception as e: + logger.exception( + "resolve_incident_error", + incident_id=incident_id, + approval_id=approval_id, + error=str(e), + ) + return False + # ============================================================================= # Singleton