""" GitHub Webhook Service - Phase 13.1 ==================================== 封裝 GitHub Webhook 相關的 Redis 操作 遵循 leWOOOgo 積木化原則: - Router 層不直接存取 Redis - 透過 Service 層封裝資料存取邏輯 """ import json from typing import Protocol import structlog from src.core.redis_client import get_redis logger = structlog.get_logger(__name__) # Redis TTL: 7 天 GITHUB_REVIEW_TTL_SECONDS = 7 * 24 * 60 * 60 class IGitHubReviewRepository(Protocol): """GitHub Review Repository Interface""" async def save_review(self, review_id: str, review_data: dict) -> bool: """儲存審查結果""" ... async def get_review(self, review_id: str) -> dict | None: """取得審查結果""" ... class GitHubReviewRedisRepository: """Redis 實作的 GitHub Review Repository""" KEY_PREFIX = "github_review:" async def save_review(self, review_id: str, review_data: dict) -> bool: """ 儲存審查結果到 Redis Args: review_id: 審查 ID review_data: 審查結果資料 Returns: bool: 儲存是否成功 """ try: redis_client = get_redis() key = f"{self.KEY_PREFIX}{review_id}" await redis_client.set( key, json.dumps(review_data, ensure_ascii=False), ex=GITHUB_REVIEW_TTL_SECONDS, ) logger.debug("github_review_saved", review_id=review_id) return True except Exception as e: logger.error("github_review_save_failed", review_id=review_id, error=str(e)) return False async def get_review(self, review_id: str) -> dict | None: """ 取得審查結果 Args: review_id: 審查 ID Returns: 審查結果資料,或 None """ try: redis_client = get_redis() key = f"{self.KEY_PREFIX}{review_id}" result = await redis_client.get(key) if result: return json.loads(result) return None except Exception as e: logger.error("github_review_get_failed", review_id=review_id, error=str(e)) return None class GitHubWebhookService: """ GitHub Webhook 服務 封裝審查結果的儲存與查詢 """ def __init__(self, repository: IGitHubReviewRepository | None = None): self._repository = repository or GitHubReviewRedisRepository() async def save_review_result( self, review_id: str, review_data: dict, ) -> bool: """儲存審查結果""" return await self._repository.save_review(review_id, review_data) async def get_review_result(self, review_id: str) -> dict | None: """取得審查結果""" return await self._repository.get_review(review_id) # 單例 _service: GitHubWebhookService | None = None def get_github_webhook_service() -> GitHubWebhookService: """取得 GitHubWebhookService 單例""" global _service if _service is None: _service = GitHubWebhookService() return _service