@startuml analytics-대시보드조회
!theme mono

title Analytics Service - 대시보드 조회 내부 시퀀스\n(UFR-ANAL-010: 실시간 성과분석 대시보드 조회)

participant "AnalyticsController" as Controller
participant "AnalyticsService" as Service
participant "CacheService" as Cache
participant "AnalyticsRepository" as Repository
participant "ExternalChannelService" as ChannelService
participant "ROICalculator" as Calculator
participant "CircuitBreaker" as CB
participant "Redis<<E>>" as Redis
database "Analytics DB<<E>>" as DB

-> Controller: GET /api/events/{id}/analytics\n+ Authorization: Bearer {token}
activate Controller

Controller -> Service: getDashboardData(eventId, userId)
activate Service

note right of Service
    **입력 검증**
    - eventId: UUID 형식 검증
    - userId: JWT에서 추출
    - 권한 확인: 매장 소유자 여부
end note

Service -> Cache: get("analytics:dashboard:{eventId}")
activate Cache

note right of Cache
    **Cache-Aside 패턴**
    - Redis GET 호출
    - Cache Key 구조:
      analytics:dashboard:{eventId}
    - TTL: 3600초 (1시간)
end note

Cache -> Redis: GET analytics:dashboard:{eventId}
activate Redis

alt Cache HIT

    Redis --> Cache: **Cache HIT**\n캐시된 데이터 반환\n{\n  totalParticipants: 1234,\n  totalViews: 17200,\n  roi: 250,\n  channelStats: [...],\n  lastUpdated: "2025-10-22T10:30:00Z"\n}
    deactivate Redis

    Cache --> Service: Dashboard 데이터 (JSON)
    deactivate Cache

    note right of Service
        **응답 데이터 구조**
        - 4개 요약 카드
          * 총 참여자 수, 달성률
          * 총 노출 수, 증감률
          * 예상 ROI, 업종 평균 대비
          * 매출 증가율
        - 채널별 성과
        - 시간대별 참여 추이
        - 참여자 프로필 분석
        - 비교 분석 (업종 평균, 이전 이벤트)
    end note

    Service --> Controller: DashboardResponse\n(200 OK)
    deactivate Service

    Controller --> : 200 OK\nDashboard Data (JSON)
    deactivate Controller

    note over Controller, Redis
        **Cache HIT 시나리오 성능**
        - 응답 시간: 약 0.5초
        - Redis 조회 시간: 0.01초
        - 직렬화/역직렬화: 0.05초
        - HTTP 오버헤드: 0.44초
        - 예상 히트율: 95%
    end note

else Cache MISS

    Redis --> Cache: **Cache MISS** (null)
    deactivate Redis

    Cache --> Service: null (캐시 미스)
    deactivate Cache

    note right of Service
        **Cache MISS 처리**
        - 데이터 통합 작업 시작
        - 로컬 DB 조회 + 외부 API 병렬 호출
    end note

    |||
    == 1. Analytics DB 조회 (로컬 데이터) ==

    Service -> Repository: getEventStats(eventId)
    activate Repository

    Repository -> DB: 이벤트 통계 조회\n(이벤트ID로 통계 데이터 조회)
    activate DB

    DB --> Repository: EventStatsEntity\n- totalParticipants\n- estimatedROI\n- salesGrowthRate
    deactivate DB

    Repository --> Service: EventStats
    deactivate Repository

    note right of Service
        **로컬 데이터 확보**
        - 총 참여자 수
        - 예상 ROI (DB 캐시)
        - 매출 증가율 (POS 연동)
    end note

    |||
    == 2. 외부 채널 API 병렬 호출 (Circuit Breaker 적용) ==

    note right of Service
        **병렬 처리 시작**
        - CompletableFuture 4개 생성
        - 우리동네TV, 지니TV, 링고비즈, SNS APIs 동시 호출
        - Circuit Breaker 적용 (채널별 독립)
    end note

    par 외부 API 병렬 호출

        Service -> ChannelService: getWooriTVStats(eventId)
        activate ChannelService

        ChannelService -> CB: execute("wooriTV", () -> callAPI())
        activate CB

        note right of CB
            **Circuit Breaker**
            - State: CLOSED (정상)
            - Failure Rate: 50% 초과 시 OPEN
            - Timeout: 10초
        end note

        CB -> CB: 외부 API 호출\nGET /stats/{eventId}

        alt Circuit Breaker CLOSED (정상)
            CB --> ChannelService: ChannelStats\n- views: 5000\n- clicks: 1200
            deactivate CB

            ChannelService --> Service: WooriTVStats
            deactivate ChannelService
        else Circuit Breaker OPEN (장애)
            CB -> CB: **Fallback 실행**\n캐시된 이전 데이터 반환
            note right of CB
                Fallback 전략:
                - Redis에서 이전 통계 조회
                - 없으면 기본값 (0) 반환
                - 알림: "일부 채널 데이터 로딩 실패"
            end note
            CB --> ChannelService: Fallback 데이터
            deactivate CB
            ChannelService --> Service: WooriTVStats (Fallback)
            deactivate ChannelService
        end

    else

        Service -> ChannelService: getGenieTVStats(eventId)
        activate ChannelService

        ChannelService -> CB: execute("genieTV", () -> callAPI())
        activate CB

        CB -> CB: 외부 API 호출\nGET /campaign/{id}/stats

        alt 정상 응답
            CB --> ChannelService: ChannelStats\n- adViews: 10000\n- clicks: 500
            deactivate CB
            ChannelService --> Service: GenieTVStats
            deactivate ChannelService
        else Timeout (10초 초과)
            CB -> CB: **Timeout 처리**\n기본값 반환
            note right of CB
                Timeout 발생:
                - 리소스 점유 방지
                - Fallback으로 기본값 (0) 설정
                - 알림: "지니TV 데이터 로딩 지연"
            end note
            CB --> ChannelService: 기본값 (0)
            deactivate CB
            ChannelService --> Service: GenieTVStats (기본값)
            deactivate ChannelService
        end

    else

        Service -> ChannelService: getRingoBizStats(eventId)
        activate ChannelService

        ChannelService -> CB: execute("ringoBiz", () -> callAPI())
        activate CB

        note right of CB
            **Circuit Breaker**
            - State: CLOSED (정상)
            - Failure Rate: 50% 초과 시 OPEN
            - Timeout: 10초
        end note

        CB -> CB: 외부 API 호출\nGET /voice-stats/{eventId}

        alt 정상 응답
            CB --> ChannelService: ChannelStats\n- calls: 3000\n- completed: 2500\n- avgDuration: 45초
            deactivate CB
            ChannelService --> Service: RingoBizStats
            deactivate ChannelService
        else Timeout 또는 장애
            CB -> CB: **Fallback 실행**\n기본값 반환
            note right of CB
                링고비즈 API 장애:
                - 기본값 (0) 반환
                - 알림: "링고비즈 데이터 로딩 실패"
            end note
            CB --> ChannelService: 기본값 (0)
            deactivate CB
            ChannelService --> Service: RingoBizStats (기본값)
            deactivate ChannelService
        end

    else

        Service -> ChannelService: getSNSStats(eventId)
        activate ChannelService

        ChannelService -> CB: execute("SNS", () -> callAPIs())
        activate CB

        note right of CB
            **SNS APIs 통합 호출**
            - Instagram API
            - Naver Blog API
            - Kakao Channel API
            - 3개 API 병렬 호출
        end note

        CB -> CB: 외부 APIs 호출\n(Instagram, Naver, Kakao)

        alt 정상 응답
            CB --> ChannelService: SNSStats\n- Instagram: likes 300, comments 50\n- Naver: views 2000\n- Kakao: shares 100
            deactivate CB
            ChannelService --> Service: SNSStats
            deactivate ChannelService
        else 장애 또는 Timeout
            CB -> CB: **Fallback 실행**\n기본값 반환
            note right of CB
                SNS API 장애:
                - 기본값 (0) 반환
                - 알림: "SNS 데이터 로딩 실패"
            end note
            CB --> ChannelService: 기본값 (0)
            deactivate CB
            ChannelService --> Service: SNSStats (기본값)
            deactivate ChannelService
        end

    end

    |||
    == 3. 데이터 통합 및 ROI 계산 ==

    Service -> Service: mergeChannelStats(\n  wooriTV, genieTV, ringoBiz, sns\n)

    note right of Service
        **데이터 통합**
        - 총 노출 수 = 외부 채널 노출 합계
        - 총 참여자 수 = Analytics DB
        - 채널별 전환율 = 참여자 수 / 노출 수
        - 링고비즈: 통화 완료 수 포함
    end note

    Service -> Calculator: calculateROI(\n  eventStats, channelStats\n)
    activate Calculator

    note right of Calculator
        **ROI 계산 로직**
        총 비용 = 경품 비용 + 플랫폼 비용
        예상 수익 = 매출 증가액 + 신규 고객 LTV
        ROI = (수익 - 비용) / 비용 × 100
    end note

    Calculator --> Service: ROIData\n- roi: 250%\n- totalCost: 100만원\n- totalRevenue: 350만원\n- breakEvenPoint: 달성
    deactivate Calculator

    Service -> Service: buildDashboardData(\n  eventStats, channelStats, roiData\n)

    note right of Service
        **대시보드 데이터 구조 생성**
        - 4개 요약 카드
        - 채널별 성과 차트 데이터
        - 시간대별 참여 추이
        - 참여자 프로필 분석
        - 비교 분석 (업종 평균, 이전 이벤트)
    end note

    |||
    == 4. Redis 캐싱 및 응답 ==

    Service -> Cache: set(\n  "analytics:dashboard:{eventId}",\n  dashboardData,\n  TTL=3600\n)
    activate Cache

    Cache -> Redis: 캐시 저장\nSET analytics:dashboard:{eventId}\nvalue={통합 데이터}\nEX 3600 (1시간)
    activate Redis

    Redis --> Cache: OK (저장 완료)
    deactivate Redis

    Cache --> Service: OK (캐싱 완료)
    deactivate Cache

    note right of Service
        **캐싱 완료**
        - TTL: 3600초 (1시간)
        - 다음 조회 시 Cache HIT
        - 예상 크기: 5KB
        - 갱신 주기: 1시간마다 새 데이터 조회
    end note

    Service --> Controller: DashboardResponse\n(200 OK)
    deactivate Service

    Controller --> : 200 OK\nDashboard Data (JSON)
    deactivate Controller

    note over Controller, DB
        **Cache MISS 시나리오 성능**
        - 응답 시간: 약 3초
        - Analytics DB 조회: 0.1초
        - 외부 API 병렬 호출: 2초 (병렬 처리)
        - ROI 계산: 0.05초
        - Redis 캐싱: 0.01초
        - 직렬화/HTTP: 0.84초
    end note

end

@enduml
