137 lines
4.5 KiB
Python
137 lines
4.5 KiB
Python
"""
|
|
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)
|