From fe76d0b10896c7571fe5075e186efb6b47fd5591 Mon Sep 17 00:00:00 2001 From: OG T Date: Wed, 25 Mar 2026 21:17:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20Phase=2016=20R3.1-R3.2=20Repositor?= =?UTF-8?q?y=20=E4=BB=8B=E9=9D=A2=E5=AE=9A=E7=BE=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增: - IApprovalRepository Protocol - IIncidentRepository Protocol - ITimelineRepository Protocol 設計: DI 友好的 Protocol 介面,Service 層只依賴抽象 Co-Authored-By: Claude Opus 4.5 --- apps/api/src/repositories/__init__.py | 26 +++++ apps/api/src/repositories/interfaces.py | 128 ++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 apps/api/src/repositories/__init__.py create mode 100644 apps/api/src/repositories/interfaces.py diff --git a/apps/api/src/repositories/__init__.py b/apps/api/src/repositories/__init__.py new file mode 100644 index 00000000..22e44121 --- /dev/null +++ b/apps/api/src/repositories/__init__.py @@ -0,0 +1,26 @@ +""" +Repository Layer - 資料存取抽象層 +================================== +Phase 16 R3: 抽取 Repository 層 + +設計原則: +- Protocol 介面定義 (DI 用) +- CRUD 操作封裝 +- Service 層不直接碰 DB/Redis + +版本: v1.0 +建立: 2026-03-26 (台北時區) +建立者: Claude Code (Phase 16 架構重構) +""" + +from src.repositories.interfaces import ( + IApprovalRepository, + IIncidentRepository, + ITimelineRepository, +) + +__all__ = [ + "IApprovalRepository", + "IIncidentRepository", + "ITimelineRepository", +] diff --git a/apps/api/src/repositories/interfaces.py b/apps/api/src/repositories/interfaces.py new file mode 100644 index 00000000..8dd027e3 --- /dev/null +++ b/apps/api/src/repositories/interfaces.py @@ -0,0 +1,128 @@ +""" +Repository Interfaces - Protocol 定義 +====================================== +Phase 16 R3: Repository 層 Protocol 介面 + +設計原則: +- 使用 Python Protocol 實現 DI +- Service 層只依賴 Protocol,不依賴具體實作 +- 便於測試 (可注入 Mock Repository) + +版本: v1.0 +建立: 2026-03-26 (台北時區) +建立者: Claude Code (Phase 16 架構重構) +""" + +from datetime import datetime +from typing import Protocol, runtime_checkable +from uuid import UUID + +from src.models.approval import ApprovalRequest, ApprovalRequestCreate, ApprovalStatus +from src.models.incident import Incident + + +@runtime_checkable +class IApprovalRepository(Protocol): + """ + Approval Repository Protocol + + 職責: ApprovalRecord CRUD 操作 + 實作: ApprovalDBRepository (PostgreSQL) + """ + + async def create(self, request: ApprovalRequestCreate) -> ApprovalRequest: + """建立新的 Approval""" + ... + + async def get_by_id(self, approval_id: UUID) -> ApprovalRequest | None: + """根據 ID 取得 Approval""" + ... + + async def get_pending(self) -> list[ApprovalRequest]: + """取得所有待審核的 Approval""" + ... + + async def update_status( + self, + approval_id: UUID, + status: ApprovalStatus, + actor: str | None = None, + ) -> ApprovalRequest | None: + """更新 Approval 狀態""" + ... + + async def add_signature( + self, + approval_id: UUID, + signer: str, + decision: str, + comment: str | None = None, + ) -> ApprovalRequest | None: + """新增簽核""" + ... + + +@runtime_checkable +class IIncidentRepository(Protocol): + """ + Incident Repository Protocol + + 職責: IncidentRecord CRUD 操作 + 實作: IncidentDBRepository (PostgreSQL) + """ + + async def create(self, incident: Incident) -> Incident: + """建立新的 Incident""" + ... + + async def get_by_id(self, incident_id: str) -> Incident | None: + """根據 ID 取得 Incident""" + ... + + async def get_active(self) -> list[Incident]: + """取得所有活躍的 Incident""" + ... + + async def update(self, incident: Incident) -> Incident | None: + """更新 Incident""" + ... + + async def upsert(self, incident: Incident) -> bool: + """Upsert Incident (存在則更新,不存在則建立)""" + ... + + +@runtime_checkable +class ITimelineRepository(Protocol): + """ + Timeline Repository Protocol + + 職責: TimelineEvent CRUD 操作 + 實作: TimelineDBRepository (PostgreSQL) + """ + + async def add_event( + self, + approval_id: UUID, + event_type: str, + actor: str, + details: dict | None = None, + ) -> bool: + """新增 Timeline 事件""" + ... + + async def get_events( + self, + approval_id: UUID, + limit: int = 100, + ) -> list[dict]: + """取得 Approval 的 Timeline 事件""" + ... + + async def get_recent_events( + self, + limit: int = 50, + hours: int = 24, + ) -> list[dict]: + """取得最近的 Timeline 事件""" + ...