@startuml
!theme mono

title Analytics Service 클래스 다이어그램 (상세)

' ============================================================
' Presentation Layer - Controller
' ============================================================
package "com.kt.event.analytics.controller" <<Rectangle>> #F0F8FF {

    class AnalyticsDashboardController {
        - analyticsService: AnalyticsService
        + getEventAnalytics(eventId: String, startDate: LocalDateTime, endDate: LocalDateTime, refresh: Boolean): ResponseEntity<ApiResponse<AnalyticsDashboardResponse>>
    }

    class ChannelAnalyticsController {
        - channelAnalyticsService: ChannelAnalyticsService
        + getChannelAnalytics(eventId: String, channels: String, sortBy: String, sortOrder: String): ResponseEntity<ApiResponse<ChannelAnalyticsResponse>>
    }

    class RoiAnalyticsController {
        - roiAnalyticsService: RoiAnalyticsService
        + getRoiAnalytics(eventId: String): ResponseEntity<ApiResponse<RoiAnalyticsResponse>>
    }

    class TimelineAnalyticsController {
        - timelineAnalyticsService: TimelineAnalyticsService
        + getTimelineAnalytics(eventId: String, granularity: String, startDate: LocalDateTime, endDate: LocalDateTime): ResponseEntity<ApiResponse<TimelineAnalyticsResponse>>
    }

    class UserAnalyticsDashboardController {
        - userAnalyticsService: UserAnalyticsService
        + getUserEventAnalytics(userId: String, startDate: LocalDateTime, endDate: LocalDateTime): ResponseEntity<ApiResponse<UserAnalyticsDashboardResponse>>
    }

    class UserChannelAnalyticsController {
        - userChannelAnalyticsService: UserChannelAnalyticsService
        + getUserChannelAnalytics(userId: String, channels: String): ResponseEntity<ApiResponse<UserChannelAnalyticsResponse>>
    }

    class UserRoiAnalyticsController {
        - userRoiAnalyticsService: UserRoiAnalyticsService
        + getUserRoiAnalytics(userId: String): ResponseEntity<ApiResponse<UserRoiAnalyticsResponse>>
    }

    class UserTimelineAnalyticsController {
        - userTimelineAnalyticsService: UserTimelineAnalyticsService
        + getUserTimelineAnalytics(userId: String, granularity: String, startDate: LocalDateTime, endDate: LocalDateTime): ResponseEntity<ApiResponse<UserTimelineAnalyticsResponse>>
    }
}

' ============================================================
' Business Layer - Service
' ============================================================
package "com.kt.event.analytics.service" <<Rectangle>> #E6F7E6 {

    class AnalyticsService {
        - eventStatsRepository: EventStatsRepository
        - channelStatsRepository: ChannelStatsRepository
        - externalChannelService: ExternalChannelService
        - roiCalculator: ROICalculator
        - redisTemplate: RedisTemplate<String, String>
        - objectMapper: ObjectMapper
        + getDashboardData(eventId: String, startDate: LocalDateTime, endDate: LocalDateTime, refresh: boolean): AnalyticsDashboardResponse
        - buildDashboardData(eventStats: EventStats, channelStatsList: List<ChannelStats>, startDate: LocalDateTime, endDate: LocalDateTime): AnalyticsDashboardResponse
        - buildPeriodInfo(startDate: LocalDateTime, endDate: LocalDateTime): PeriodInfo
        - buildAnalyticsSummary(eventStats: EventStats, channelStatsList: List<ChannelStats>): AnalyticsSummary
        - buildChannelPerformance(channelStatsList: List<ChannelStats>, totalInvestment: BigDecimal): List<ChannelSummary>
    }

    class ChannelAnalyticsService {
        - channelStatsRepository: ChannelStatsRepository
        - externalChannelService: ExternalChannelService
        - redisTemplate: RedisTemplate<String, String>
        - objectMapper: ObjectMapper
        + getChannelAnalytics(eventId: String, channels: List<String>, sortBy: String, sortOrder: String): ChannelAnalyticsResponse
        - buildChannelMetrics(channelStats: ChannelStats): ChannelMetrics
        - buildChannelPerformance(channelStats: ChannelStats): ChannelPerformance
        - buildChannelComparison(channelStatsList: List<ChannelStats>): ChannelComparison
    }

    class RoiAnalyticsService {
        - eventStatsRepository: EventStatsRepository
        - channelStatsRepository: ChannelStatsRepository
        - roiCalculator: ROICalculator
        - redisTemplate: RedisTemplate<String, String>
        - objectMapper: ObjectMapper
        + getRoiAnalytics(eventId: String): RoiAnalyticsResponse
        - buildRoiCalculation(eventStats: EventStats, channelStatsList: List<ChannelStats>): RoiCalculation
        - buildInvestmentDetails(channelStatsList: List<ChannelStats>): InvestmentDetails
        - buildRevenueDetails(eventStats: EventStats): RevenueDetails
    }

    class TimelineAnalyticsService {
        - timelineDataRepository: TimelineDataRepository
        - eventStatsRepository: EventStatsRepository
        - redisTemplate: RedisTemplate<String, String>
        - objectMapper: ObjectMapper
        + getTimelineAnalytics(eventId: String, granularity: String, startDate: LocalDateTime, endDate: LocalDateTime): TimelineAnalyticsResponse
        - buildTimelineDataPoints(timelineDataList: List<TimelineData>): List<TimelineDataPoint>
        - buildTrendAnalysis(timelineDataList: List<TimelineData>): TrendAnalysis
        - buildPeakTimeInfo(timelineDataList: List<TimelineData>): PeakTimeInfo
    }

    class UserAnalyticsService {
        - eventStatsRepository: EventStatsRepository
        - channelStatsRepository: ChannelStatsRepository
        - roiCalculator: ROICalculator
        + getUserEventAnalytics(userId: String, startDate: LocalDateTime, endDate: LocalDateTime): UserAnalyticsDashboardResponse
        - buildUserAnalyticsSummary(eventStatsList: List<EventStats>, channelStatsList: List<ChannelStats>): AnalyticsSummary
    }

    class UserChannelAnalyticsService {
        - channelStatsRepository: ChannelStatsRepository
        - eventStatsRepository: EventStatsRepository
        + getUserChannelAnalytics(userId: String, channels: List<String>): UserChannelAnalyticsResponse
    }

    class UserRoiAnalyticsService {
        - eventStatsRepository: EventStatsRepository
        - channelStatsRepository: ChannelStatsRepository
        - roiCalculator: ROICalculator
        + getUserRoiAnalytics(userId: String): UserRoiAnalyticsResponse
    }

    class UserTimelineAnalyticsService {
        - timelineDataRepository: TimelineDataRepository
        - eventStatsRepository: EventStatsRepository
        + getUserTimelineAnalytics(userId: String, granularity: String, startDate: LocalDateTime, endDate: LocalDateTime): UserTimelineAnalyticsResponse
    }

    class ExternalChannelService {
        + updateChannelStatsFromExternalAPIs(eventId: String, channelStatsList: List<ChannelStats>): void
        - updateChannelStatsFromAPI(eventId: String, channelStats: ChannelStats): void
        - updateWooriTVStats(eventId: String, channelStats: ChannelStats): void
        - wooriTVFallback(eventId: String, channelStats: ChannelStats, e: Exception): void
        - updateGenieTVStats(eventId: String, channelStats: ChannelStats): void
        - genieTVFallback(eventId: String, channelStats: ChannelStats, e: Exception): void
        - updateRingoBizStats(eventId: String, channelStats: ChannelStats): void
        - ringoBizFallback(eventId: String, channelStats: ChannelStats, e: Exception): void
        - updateSNSStats(eventId: String, channelStats: ChannelStats): void
        - snsFallback(eventId: String, channelStats: ChannelStats, e: Exception): void
    }

    class ROICalculator {
        + calculateRoiSummary(eventStats: EventStats): RoiSummary
        + calculateRoi(investment: BigDecimal, revenue: BigDecimal): BigDecimal
        + calculateCostPerParticipant(totalInvestment: BigDecimal, participants: int): BigDecimal
        + calculateRevenueProjection(currentRevenue: BigDecimal, targetRoi: BigDecimal): RevenueProjection
    }
}

' ============================================================
' Data Access Layer - Repository
' ============================================================
package "com.kt.event.analytics.repository" <<Rectangle>> #FFF8DC {

    interface EventStatsRepository {
        + findByEventId(eventId: String): Optional<EventStats>
        + findByUserId(userId: String): List<EventStats>
        + save(eventStats: EventStats): EventStats
        + findAll(): List<EventStats>
    }

    interface ChannelStatsRepository {
        + findByEventId(eventId: String): List<ChannelStats>
        + findByEventIdAndChannelName(eventId: String, channelName: String): Optional<ChannelStats>
        + findByEventIdIn(eventIds: List<String>): List<ChannelStats>
        + save(channelStats: ChannelStats): ChannelStats
    }

    interface TimelineDataRepository {
        + findByEventIdOrderByTimestampAsc(eventId: String): List<TimelineData>
        + findByEventIdAndTimestampBetween(eventId: String, startDate: LocalDateTime, endDate: LocalDateTime): List<TimelineData>
        + findByEventIdIn(eventIds: List<String>): List<TimelineData>
        + save(timelineData: TimelineData): TimelineData
    }
}

' ============================================================
' Domain Layer - Entity
' ============================================================
package "com.kt.event.analytics.entity" <<Rectangle>> #FFFACD {

    class EventStats {
        - id: Long
        - eventId: String
        - eventTitle: String
        - userId: String
        - totalParticipants: Integer
        - totalViews: Integer
        - estimatedRoi: BigDecimal
        - targetRoi: BigDecimal
        - salesGrowthRate: BigDecimal
        - totalInvestment: BigDecimal
        - expectedRevenue: BigDecimal
        - status: String
        + incrementParticipants(): void
        + incrementParticipants(count: int): void
    }

    class ChannelStats {
        - id: Long
        - eventId: String
        - channelName: String
        - channelType: String
        - impressions: Integer
        - views: Integer
        - clicks: Integer
        - participants: Integer
        - conversions: Integer
        - distributionCost: BigDecimal
        - likes: Integer
        - comments: Integer
        - shares: Integer
        - totalCalls: Integer
        - completedCalls: Integer
        - averageDuration: Integer
    }

    class TimelineData {
        - id: Long
        - eventId: String
        - timestamp: LocalDateTime
        - participants: Integer
        - views: Integer
        - engagement: Integer
        - conversions: Integer
        - cumulativeParticipants: Integer
    }
}

' ============================================================
' DTO Layer
' ============================================================
package "com.kt.event.analytics.dto.response" <<Rectangle>> #E6E6FA {

    class AnalyticsDashboardResponse {
        - eventId: String
        - eventTitle: String
        - period: PeriodInfo
        - summary: AnalyticsSummary
        - channelPerformance: List<ChannelSummary>
        - roi: RoiSummary
        - lastUpdatedAt: LocalDateTime
        - dataSource: String
    }

    class AnalyticsSummary {
        - participants: Integer
        - participantsDelta: Integer
        - totalViews: Integer
        - totalReach: Integer
        - engagementRate: Double
        - conversionRate: Double
        - averageEngagementTime: Integer
        - targetRoi: Double
        - socialInteractions: SocialInteractionStats
    }

    class ChannelSummary {
        - channel: String
        - views: Integer
        - participants: Integer
        - engagementRate: Double
        - conversionRate: Double
        - roi: Double
    }

    class PeriodInfo {
        - startDate: LocalDateTime
        - endDate: LocalDateTime
        - durationDays: Integer
    }

    class SocialInteractionStats {
        - likes: Integer
        - comments: Integer
        - shares: Integer
    }

    class RoiSummary {
        - currentRoi: Double
        - targetRoi: Double
        - achievementRate: Double
        - expectedReturn: BigDecimal
        - totalInvestment: BigDecimal
    }

    class ChannelAnalyticsResponse {
        - eventId: String
        - eventTitle: String
        - totalChannels: Integer
        - channels: List<ChannelAnalytics>
        - comparison: ChannelComparison
        - lastUpdatedAt: LocalDateTime
    }

    class ChannelAnalytics {
        - channelName: String
        - channelType: String
        - metrics: ChannelMetrics
        - performance: ChannelPerformance
        - costs: ChannelCosts
        - voiceCallStats: VoiceCallStats
        - socialStats: SocialInteractionStats
    }

    class ChannelMetrics {
        - impressions: Integer
        - views: Integer
        - clicks: Integer
        - participants: Integer
        - conversions: Integer
    }

    class ChannelPerformance {
        - engagementRate: Double
        - clickThroughRate: Double
        - conversionRate: Double
        - participationRate: Double
    }

    class ChannelCosts {
        - distributionCost: BigDecimal
        - costPerImpression: BigDecimal
        - costPerClick: BigDecimal
        - costPerParticipant: BigDecimal
    }

    class ChannelComparison {
        - bestPerformingChannel: String
        - mostCostEffectiveChannel: String
        - highestEngagementChannel: String
    }

    class VoiceCallStats {
        - totalCalls: Integer
        - completedCalls: Integer
        - completionRate: Double
        - averageDuration: Integer
    }

    class RoiAnalyticsResponse {
        - eventId: String
        - eventTitle: String
        - roiCalculation: RoiCalculation
        - investment: InvestmentDetails
        - revenue: RevenueDetails
        - costEfficiency: CostEfficiency
        - projection: RevenueProjection
        - lastUpdatedAt: LocalDateTime
    }

    class RoiCalculation {
        - currentRoi: Double
        - targetRoi: Double
        - achievementRate: Double
        - breakEvenPoint: BigDecimal
    }

    class InvestmentDetails {
        - totalInvestment: BigDecimal
        - channelDistribution: Map<String, BigDecimal>
        - costPerChannel: Map<String, BigDecimal>
    }

    class RevenueDetails {
        - expectedRevenue: BigDecimal
        - currentRevenue: BigDecimal
        - salesGrowthRate: Double
    }

    class CostEfficiency {
        - costPerParticipant: BigDecimal
        - costPerConversion: BigDecimal
        - costPerView: BigDecimal
    }

    class RevenueProjection {
        - projectedRevenue: BigDecimal
        - projectedRoi: Double
        - estimatedGrowth: Double
    }

    class TimelineAnalyticsResponse {
        - eventId: String
        - eventTitle: String
        - granularity: String
        - period: PeriodInfo
        - dataPoints: List<TimelineDataPoint>
        - trend: TrendAnalysis
        - peakTime: PeakTimeInfo
        - lastUpdatedAt: LocalDateTime
    }

    class TimelineDataPoint {
        - timestamp: LocalDateTime
        - participants: Integer
        - views: Integer
        - engagement: Integer
        - conversions: Integer
        - cumulativeParticipants: Integer
    }

    class TrendAnalysis {
        - growthRate: Double
        - averageParticipantsPerHour: Double
        - totalEngagement: Integer
        - conversionTrend: String
    }

    class PeakTimeInfo {
        - peakTimestamp: LocalDateTime
        - peakParticipants: Integer
        - peakHour: Integer
    }

    class UserAnalyticsDashboardResponse {
        - userId: String
        - totalEvents: Integer
        - period: PeriodInfo
        - summary: AnalyticsSummary
        - events: List<AnalyticsDashboardResponse>
        - lastUpdatedAt: LocalDateTime
    }

    class UserChannelAnalyticsResponse {
        - userId: String
        - totalEvents: Integer
        - channels: List<ChannelAnalytics>
        - comparison: ChannelComparison
        - lastUpdatedAt: LocalDateTime
    }

    class UserRoiAnalyticsResponse {
        - userId: String
        - totalEvents: Integer
        - roiCalculation: RoiCalculation
        - investment: InvestmentDetails
        - revenue: RevenueDetails
        - lastUpdatedAt: LocalDateTime
    }

    class UserTimelineAnalyticsResponse {
        - userId: String
        - totalEvents: Integer
        - granularity: String
        - period: PeriodInfo
        - dataPoints: List<TimelineDataPoint>
        - lastUpdatedAt: LocalDateTime
    }
}

' ============================================================
' Messaging Layer - Kafka Consumer
' ============================================================
package "com.kt.event.analytics.messaging.consumer" <<Rectangle>> #FFE4E1 {

    class EventCreatedConsumer {
        - eventStatsRepository: EventStatsRepository
        - objectMapper: ObjectMapper
        - redisTemplate: RedisTemplate<String, String>
        + handleEventCreated(message: String): void
    }

    class ParticipantRegisteredConsumer {
        - eventStatsRepository: EventStatsRepository
        - timelineDataRepository: TimelineDataRepository
        - objectMapper: ObjectMapper
        - redisTemplate: RedisTemplate<String, String>
        + handleParticipantRegistered(message: String): void
        - updateTimelineData(eventId: String): void
    }

    class DistributionCompletedConsumer {
        - channelStatsRepository: ChannelStatsRepository
        - objectMapper: ObjectMapper
        - redisTemplate: RedisTemplate<String, String>
        + handleDistributionCompleted(message: String): void
    }
}

package "com.kt.event.analytics.messaging.event" <<Rectangle>> #FFE4E1 {

    class EventCreatedEvent {
        - eventId: String
        - eventTitle: String
        - storeId: String
        - totalInvestment: BigDecimal
        - status: String
    }

    class ParticipantRegisteredEvent {
        - eventId: String
        - participantId: String
        - channelName: String
        - registeredAt: LocalDateTime
    }

    class DistributionCompletedEvent {
        - eventId: String
        - channelName: String
        - distributionCost: BigDecimal
        - estimatedReach: Integer
        - completedAt: LocalDateTime
    }
}

' ============================================================
' Batch Layer
' ============================================================
package "com.kt.event.analytics.batch" <<Rectangle>> #FFF5EE {

    class AnalyticsBatchScheduler {
        - analyticsService: AnalyticsService
        - eventStatsRepository: EventStatsRepository
        - redisTemplate: RedisTemplate<String, String>
        + refreshAnalyticsDashboard(): void
        + initialDataLoad(): void
    }
}

' ============================================================
' Configuration Layer
' ============================================================
package "com.kt.event.analytics.config" <<Rectangle>> #F5F5F5 {

    class RedisConfig {
        + redisConnectionFactory(): RedisConnectionFactory
        + redisTemplate(): RedisTemplate<String, String>
    }

    class KafkaConsumerConfig {
        + consumerFactory(): ConsumerFactory<String, String>
        + kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, String>
    }

    class KafkaTopicConfig {
        + sampleEventCreatedTopic(): NewTopic
        + sampleParticipantRegisteredTopic(): NewTopic
        + sampleDistributionCompletedTopic(): NewTopic
    }

    class Resilience4jConfig {
        + customize(factory: Resilience4JCircuitBreakerFactory): Customizer<Resilience4JCircuitBreakerFactory>
    }

    class SecurityConfig {
        + securityFilterChain(http: HttpSecurity): SecurityFilterChain
    }

    class SwaggerConfig {
        + openAPI(): OpenAPI
    }
}

' ============================================================
' Common Components (from common-base)
' ============================================================
package "com.kt.event.common" <<Rectangle>> #DCDCDC {

    abstract class BaseTimeEntity {
        - createdAt: LocalDateTime
        - updatedAt: LocalDateTime
    }

    class "ApiResponse<T>" {
        - success: boolean
        - data: T
        - errorCode: String
        - message: String
        - timestamp: LocalDateTime
        + success(data: T): ApiResponse<T>
        + success(): ApiResponse<T>
        + error(errorCode: String, message: String): ApiResponse<T>
    }

    interface ErrorCode {
        + getCode(): String
        + getMessage(): String
    }

    class BusinessException {
        - errorCode: ErrorCode
        - details: String
    }
}

' ============================================================
' Relationships
' ============================================================

' Controller -> Service
AnalyticsDashboardController --> AnalyticsService : uses
ChannelAnalyticsController --> ChannelAnalyticsService : uses
RoiAnalyticsController --> RoiAnalyticsService : uses
TimelineAnalyticsController --> TimelineAnalyticsService : uses
UserAnalyticsDashboardController --> UserAnalyticsService : uses
UserChannelAnalyticsController --> UserChannelAnalyticsService : uses
UserRoiAnalyticsController --> UserRoiAnalyticsService : uses
UserTimelineAnalyticsController --> UserTimelineAnalyticsService : uses

' Service -> Repository
AnalyticsService --> EventStatsRepository : uses
AnalyticsService --> ChannelStatsRepository : uses
ChannelAnalyticsService --> ChannelStatsRepository : uses
RoiAnalyticsService --> EventStatsRepository : uses
RoiAnalyticsService --> ChannelStatsRepository : uses
TimelineAnalyticsService --> TimelineDataRepository : uses
TimelineAnalyticsService --> EventStatsRepository : uses
UserAnalyticsService --> EventStatsRepository : uses
UserAnalyticsService --> ChannelStatsRepository : uses
UserChannelAnalyticsService --> ChannelStatsRepository : uses
UserChannelAnalyticsService --> EventStatsRepository : uses
UserRoiAnalyticsService --> EventStatsRepository : uses
UserRoiAnalyticsService --> ChannelStatsRepository : uses
UserTimelineAnalyticsService --> TimelineDataRepository : uses
UserTimelineAnalyticsService --> EventStatsRepository : uses

' Service -> Service
AnalyticsService --> ExternalChannelService : uses
AnalyticsService --> ROICalculator : uses
ChannelAnalyticsService --> ExternalChannelService : uses
RoiAnalyticsService --> ROICalculator : uses
UserAnalyticsService --> ROICalculator : uses
UserRoiAnalyticsService --> ROICalculator : uses

' Service -> External Components
ExternalChannelService --> ChannelStats : modifies

' Consumer -> Repository
EventCreatedConsumer --> EventStatsRepository : uses
ParticipantRegisteredConsumer --> EventStatsRepository : uses
ParticipantRegisteredConsumer --> TimelineDataRepository : uses
DistributionCompletedConsumer --> ChannelStatsRepository : uses

' Consumer -> Event
EventCreatedConsumer --> EventCreatedEvent : consumes
ParticipantRegisteredConsumer --> ParticipantRegisteredEvent : consumes
DistributionCompletedConsumer --> DistributionCompletedEvent : consumes

' Batch -> Service/Repository
AnalyticsBatchScheduler --> AnalyticsService : uses
AnalyticsBatchScheduler --> EventStatsRepository : uses

' Repository -> Entity
EventStatsRepository --> EventStats : manages
ChannelStatsRepository --> ChannelStats : manages
TimelineDataRepository --> TimelineData : manages

' Entity -> BaseTimeEntity
EventStats --|> BaseTimeEntity : extends
ChannelStats --|> BaseTimeEntity : extends
TimelineData --|> BaseTimeEntity : extends

' Controller -> DTO
AnalyticsDashboardController --> AnalyticsDashboardResponse : returns
ChannelAnalyticsController --> ChannelAnalyticsResponse : returns
RoiAnalyticsController --> RoiAnalyticsResponse : returns
TimelineAnalyticsController --> TimelineAnalyticsResponse : returns
UserAnalyticsDashboardController --> UserAnalyticsDashboardResponse : returns
UserChannelAnalyticsController --> UserChannelAnalyticsResponse : returns
UserRoiAnalyticsController --> UserRoiAnalyticsResponse : returns
UserTimelineAnalyticsController --> UserTimelineAnalyticsResponse : returns

' Service -> DTO
AnalyticsService --> AnalyticsDashboardResponse : creates
ChannelAnalyticsService --> ChannelAnalyticsResponse : creates
RoiAnalyticsService --> RoiAnalyticsResponse : creates
TimelineAnalyticsService --> TimelineAnalyticsResponse : creates
UserAnalyticsService --> UserAnalyticsDashboardResponse : creates
UserChannelAnalyticsService --> UserChannelAnalyticsResponse : creates
UserRoiAnalyticsService --> UserRoiAnalyticsResponse : creates
UserTimelineAnalyticsService --> UserTimelineAnalyticsResponse : creates

' DTO Composition
AnalyticsDashboardResponse *-- PeriodInfo : contains
AnalyticsDashboardResponse *-- AnalyticsSummary : contains
AnalyticsDashboardResponse *-- RoiSummary : contains
AnalyticsSummary *-- SocialInteractionStats : contains
ChannelAnalyticsResponse *-- ChannelAnalytics : contains
ChannelAnalyticsResponse *-- ChannelComparison : contains
ChannelAnalytics *-- ChannelMetrics : contains
ChannelAnalytics *-- ChannelPerformance : contains
ChannelAnalytics *-- ChannelCosts : contains
ChannelAnalytics *-- VoiceCallStats : contains
RoiAnalyticsResponse *-- RoiCalculation : contains
RoiAnalyticsResponse *-- InvestmentDetails : contains
RoiAnalyticsResponse *-- RevenueDetails : contains
RoiAnalyticsResponse *-- CostEfficiency : contains
RoiAnalyticsResponse *-- RevenueProjection : contains
TimelineAnalyticsResponse *-- PeriodInfo : contains
TimelineAnalyticsResponse *-- TimelineDataPoint : contains
TimelineAnalyticsResponse *-- TrendAnalysis : contains
TimelineAnalyticsResponse *-- PeakTimeInfo : contains
UserAnalyticsDashboardResponse *-- PeriodInfo : contains
UserAnalyticsDashboardResponse *-- AnalyticsSummary : contains

' Common Dependencies
BusinessException --> ErrorCode : uses
AnalyticsDashboardController --> "ApiResponse<T>" : uses
ChannelAnalyticsController --> "ApiResponse<T>" : uses
RoiAnalyticsController --> "ApiResponse<T>" : uses
TimelineAnalyticsController --> "ApiResponse<T>" : uses

note top of AnalyticsService
  **핵심 서비스**
  - Redis 캐싱 (1시간 TTL)
  - 외부 API 병렬 호출
  - Circuit Breaker 패턴
  - Cache-Aside 패턴
end note

note top of ExternalChannelService
  **외부 채널 통합**
  - 우리동네TV API
  - 지니TV API
  - 링고비즈 API
  - SNS APIs
  - Resilience4j Circuit Breaker
end note

note top of EventCreatedConsumer
  **Kafka Event Consumer**
  - EventCreated 이벤트 구독
  - 멱등성 처리 (Redis Set)
  - 캐시 무효화
end note

note top of AnalyticsBatchScheduler
  **배치 스케줄러**
  - 5분 단위 캐시 갱신
  - 초기 데이터 로딩
  - 캐시 워밍업
end note

@enduml
