""" Dashboard API 라우터 YouTube Analytics 기반 대시보드 통계를 제공합니다. """ import logging from typing import Literal from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from app.dashboard.utils.redis_cache import delete_cache_pattern from app.dashboard.schemas import ( CacheDeleteResponse, ConnectedAccountsResponse, DashboardResponse, ) from app.dashboard.services import DashboardService from app.database.session import get_session from app.user.dependencies.auth import get_current_user from app.user.models import User logger = logging.getLogger(__name__) router = APIRouter(prefix="/dashboard", tags=["Dashboard"]) @router.get( "/accounts", response_model=ConnectedAccountsResponse, summary="연결된 소셜 계정 목록 조회", description=""" 연결된 소셜 계정 목록을 반환합니다. 여러 계정이 연결된 경우, 반환된 `platformUserId` 값을 `/dashboard/stats?platform_user_id=<값>`에 전달하여 계정을 선택합니다. """, ) async def get_connected_accounts( current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> ConnectedAccountsResponse: service = DashboardService() connected = await service.get_connected_accounts(current_user, session) return ConnectedAccountsResponse(accounts=connected) @router.get( "/stats", response_model=DashboardResponse, summary="대시보드 통계 조회", description=""" YouTube Analytics API를 활용한 대시보드 통계를 조회합니다. ## 주요 기능 - 최근 30개 업로드 영상 기준 통계 제공 - KPI 지표: 조회수, 시청시간, 평균 시청시간, 신규 구독자, 좋아요, 댓글, 공유, 업로드 영상 - 월별 추이: 최근 12개월 vs 이전 12개월 비교 - 인기 영상 TOP 4 - 시청자 분석: 연령/성별/지역 분포 ## 성능 최적화 - 7개 YouTube Analytics API를 병렬로 호출 - Redis 캐싱 적용 (TTL: 12시간) ## 사전 조건 - YouTube 계정이 연동되어 있어야 합니다 ## 조회 모드 - `day`: 최근 30일 통계 (현재 날짜 -2일 기준) - `month`: 최근 12개월 통계 (현재 날짜 -2일 기준, 기본값) ## 데이터 특성 - **지연 시간**: 48시간 (2일) - 2월 14일 요청 시 2월 12일 자까지 확정 - **업데이트 주기**: 하루 1회 (PT 자정, 한국 시간 오후 5~8시) - **실시간 아님**: 전날 데이터가 다음날 확정됩니다 """, ) async def get_dashboard_stats( mode: Literal["day", "month"] = Query( default="month", description="조회 모드: day(최근 30일), month(최근 12개월)", ), platform_user_id: str | None = Query( default=None, description="사용할 YouTube 채널 ID (platform_user_id)", ), current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> DashboardResponse: service = DashboardService() return await service.get_stats(mode, platform_user_id, current_user, session) @router.delete( "/cache", response_model=CacheDeleteResponse, summary="대시보드 캐시 삭제", description=""" 대시보드 Redis 캐시를 삭제합니다. 인증 없이 호출 가능합니다. 삭제 후 다음 `/stats` 요청 시 YouTube Analytics API를 새로 호출하여 최신 데이터를 반환합니다. ## 사용 시나리오 - 코드 배포 후 즉시 최신 데이터 반영이 필요할 때 - 데이터 이상 발생 시 캐시 강제 갱신 ## 캐시 키 구조 `dashboard:{user_uuid}:{platform_user_id}:{mode}` (mode: day 또는 month) ## 파라미터 - `user_uuid`: 삭제할 사용자 UUID (필수) - `mode`: day / month / all (기본값: all) """, ) async def delete_dashboard_cache( mode: Literal["day", "month", "all"] = Query( default="all", description="삭제할 캐시 모드: day, month, all(기본값, 모두 삭제)", ), user_uuid: str = Query( description="대상 사용자 UUID", ), ) -> CacheDeleteResponse: if mode == "all": deleted = await delete_cache_pattern(f"dashboard:{user_uuid}:*") message = f"전체 캐시 삭제 완료 ({deleted}개)" else: deleted = await delete_cache_pattern(f"dashboard:{user_uuid}:*:{mode}") message = f"{mode} 캐시 삭제 완료 ({deleted}개)" logger.info( f"[CACHE DELETE] user_uuid={user_uuid or 'ALL'}, mode={mode}, deleted={deleted}" ) return CacheDeleteResponse(deleted_count=deleted, message=message)