@startuml ai-트렌드분석및추천
!theme mono

title AI Service - 트렌드 분석 및 이벤트 추천 (내부 시퀀스)

actor Client
participant "Kafka Consumer" as Consumer <<Component>>
participant "JobMessageHandler" as Handler <<Controller>>
participant "AIRecommendationService" as Service <<Service>>
participant "TrendAnalysisEngine" as TrendEngine <<Component>>
participant "RecommendationEngine" as RecommendEngine <<Component>>
participant "CacheManager" as Cache <<Component>>
participant "CircuitBreakerManager" as CB <<Component>>
participant "ExternalAIClient" as AIClient <<Component>>
participant "JobStateManager" as JobState <<Component>>
participant "Redis" as Redis <<Infrastructure>>
participant "External AI API" as ExternalAPI <<External>>
participant "Kafka Producer" as Producer <<Component>>

note over Consumer: Kafka ai-job Topic 구독\nConsumer Group: ai-service-group

== 1. Job 메시지 수신 ==
Consumer -> Handler: onMessage(jobMessage)\n{jobId, eventDraftId, 목적, 업종, 지역, 매장정보}
activate Handler

Handler -> Handler: 메시지 유효성 검증
note right
검증 항목:
- jobId 존재 여부
- eventDraftId 유효성
- 필수 파라미터 (목적, 업종, 지역)
end note

alt 유효하지 않은 메시지
    Handler -> Producer: DLQ 발행 (Dead Letter Queue)\n{jobId, error: INVALID_MESSAGE}
    Handler --> Consumer: ACK (메시지 처리 완료)
    note over Handler: 잘못된 메시지는 DLQ로 이동\n수동 검토 필요
else 유효한 메시지
    Handler -> JobState: updateJobStatus(jobId, PROCESSING)
    JobState -> Redis: SET job:{jobId}:status = PROCESSING
    Redis --> JobState: OK
    JobState --> Handler: 상태 업데이트 완료

    Handler -> Service: generateRecommendations(\neventDraftId, 목적, 업종, 지역, 매장정보)
    activate Service

    == 2. 트렌드 분석 ==
    Service -> TrendEngine: analyzeTrends(업종, 지역, 목적)
    activate TrendEngine

    TrendEngine -> Cache: getCachedTrend(업종, 지역)
    Cache -> Redis: GET trend:{업종}:{지역}
    Redis --> Cache: 캐시 결과

    alt 캐시 히트
        Cache --> TrendEngine: 캐시된 트렌드 데이터
        note right
        캐시 키: trend:{업종}:{지역}
        TTL: 1시간
        데이터: {
          industry_trends,
          regional_characteristics,
          seasonal_patterns
        }
        end note

    else 캐시 미스
        note right of TrendEngine
        **트렌드 분석 입력 데이터**
        - 업종 정보
        - 지역 정보
        - 현재 시즌 (계절, 월)
        - 이벤트 목적

        **외부 AI API 호출**
        - 과거 이벤트 데이터 사용 안 함
        - 실시간 시장 트렌드 분석
        - 업종별/지역별 일반적 특성
        end note

        TrendEngine -> CB: executeWithCircuitBreaker(\nAI API 트렌드 분석 호출)
        activate CB

        CB -> CB: Circuit Breaker 상태 확인
        note right
        **Circuit Breaker 설정**
        - Failure Rate Threshold: 50%
        - Timeout: 5분 (300초)
        - Half-Open Wait Duration: 1분 (60초)
        - Permitted Calls in Half-Open: 3
        - Sliding Window Size: 10
        end note

        alt Circuit CLOSED (정상)
            CB -> AIClient: callAIAPI(\nmethod: "trendAnalysis",\nprompt: 트렌드 분석 프롬프트,\ntimeout: 5분)
            activate AIClient

            AIClient -> AIClient: 프롬프트 구성
            note right of AIClient
            **AI 프롬프트 구성**
            "당신은 마케팅 트렌드 분석 전문가입니다.

            **입력 정보**
            - 업종: {업종}
            - 지역: {지역}
            - 현재 시즌: {계절/월}
            - 이벤트 목적: {목적}

            **분석 요청사항**
            1. 업종별 일반적 트렌드
               (업종 특성 기반 효과적인 이벤트 유형)

            2. 지역별 특성
               (지역 고객 특성, 선호도)

            3. 시즌별 추천
               (현재 시기에 적합한 이벤트)"
            end note

            AIClient -> ExternalAPI: AI API 호출\nPOST /api/v1/analyze\nAuthorization: Bearer {API_KEY}\nTimeout: 5분\nPayload: {업종, 지역, 시즌, 목적}
            activate ExternalAPI

            ExternalAPI --> AIClient: 200 OK\n{"industryTrend": "...",\n"regionalCharacteristics": "...",\n"seasonalRecommendation": "..."}
            deactivate ExternalAPI

            AIClient -> AIClient: 응답 검증 및 파싱
            AIClient --> CB: 분석 결과
            deactivate AIClient

            CB -> CB: 성공 기록 (Circuit Breaker)
            CB --> TrendEngine: 트렌드 분석 결과
            deactivate CB

            TrendEngine -> Cache: cacheTrend(\nkey: trend:{업종}:{지역},\ndata: 분석결과,\nTTL: 1시간)
            Cache -> Redis: SETEX trend:{업종}:{지역} 3600 {분석결과}
            Redis --> Cache: OK
            Cache --> TrendEngine: 캐싱 완료

        else Circuit OPEN (장애)
            CB --> TrendEngine: CircuitBreakerOpenException
            TrendEngine -> TrendEngine: Fallback 실행\n(기본 트렌드 데이터 사용)
            note right
            Fallback 전략:
            - 이전 캐시 데이터 반환
            - 또는 기본 트렌드 템플릿 사용
            - 클라이언트에 안내 메시지 포함
            end note

        else Circuit HALF-OPEN (복구 시도)
            CB -> AIClient: 제한된 요청 허용 (3개)
            AIClient -> ExternalAPI: POST /api/v1/analyze
            ExternalAPI --> AIClient: 200 OK
            AIClient --> CB: 성공
            CB -> CB: 연속 성공 시 CLOSED로 전환
            CB --> TrendEngine: 트렌드 분석 결과

        else Timeout (5분 초과)
            CB --> TrendEngine: TimeoutException
            note right of TrendEngine
            **Timeout 처리**
            - 5분 초과 시 즉시 실패
            - Fallback: 기본 트렌드 사용
            - 사용자에게 안내 메시지 제공
            end note
            TrendEngine -> TrendEngine: Fallback 실행\n(기본 트렌드 템플릿 사용)
        end
    end

    TrendEngine --> Service: 트렌드 분석 완료\n{업종트렌드, 지역특성, 시즌특성}
    deactivate TrendEngine

    == 3. 이벤트 추천 생성 (3가지 옵션) ==
    Service -> RecommendEngine: generateRecommendations(\n목적, 트렌드, 매장정보)
    activate RecommendEngine

    RecommendEngine -> RecommendEngine: 추천 컨텍스트 구성
    note right
    추천 입력:
    - 이벤트 목적 (신규 고객 유치 등)
    - 트렌드 분석 결과
    - 매장 정보 (업종, 위치, 크기)
    - 예산 범위 (저/중/고)
    end note

    group parallel
        RecommendEngine -> CB: executeWithCircuitBreaker(\nAI API 추천 생성 - 옵션 1: 저비용)
        activate CB
        CB -> AIClient: callAIAPI(\nmethod: "generateRecommendation",\nprompt: 저비용 추천 프롬프트,\ntimeout: 5분)
        activate AIClient

        AIClient -> AIClient: 프롬프트 구성
        note right
        옵션 1 프롬프트:
        "저비용, 높은 참여율 중심 이벤트 기획
        목적: {목적}
        트렌드: {트렌드}
        매장: {매장정보}

        출력 형식:
        - 이벤트 제목
        - 추천 경품 (예산: 저)
        - 참여 방법 (난이도: 낮음)
        - 예상 참여자 수
        - 예상 비용
        - 예상 ROI"
        end note

        AIClient -> ExternalAPI: POST /api/v1/recommend\n(저비용 옵션)
        ExternalAPI --> AIClient: 200 OK\n{추천안 1}
        AIClient --> CB: 추천안 1
        deactivate AIClient
        CB --> RecommendEngine: 옵션 1 완료
        deactivate CB

        RecommendEngine -> CB: executeWithCircuitBreaker(\nAI API 추천 생성 - 옵션 2: 중비용)
        activate CB
        CB -> AIClient: callAIAPI(\nmethod: "generateRecommendation",\nprompt: 중비용 추천 프롬프트,\ntimeout: 5분)
        activate AIClient

        AIClient -> AIClient: 프롬프트 구성
        note right
        옵션 2 프롬프트:
        "중비용, 균형잡힌 ROI 이벤트 기획
        목적: {목적}
        트렌드: {트렌드}
        매장: {매장정보}

        출력 형식:
        - 이벤트 제목
        - 추천 경품 (예산: 중)
        - 참여 방법 (난이도: 중간)
        - 예상 참여자 수
        - 예상 비용
        - 예상 ROI"
        end note

        AIClient -> ExternalAPI: POST /api/v1/recommend\n(중비용 옵션)
        ExternalAPI --> AIClient: 200 OK\n{추천안 2}
        AIClient --> CB: 추천안 2
        deactivate AIClient
        CB --> RecommendEngine: 옵션 2 완료
        deactivate CB

        RecommendEngine -> CB: executeWithCircuitBreaker(\nAI API 추천 생성 - 옵션 3: 고비용)
        activate CB
        CB -> AIClient: callAIAPI(\nmethod: "generateRecommendation",\nprompt: 고비용 추천 프롬프트,\ntimeout: 5분)
        activate AIClient

        AIClient -> AIClient: 프롬프트 구성
        note right
        옵션 3 프롬프트:
        "고비용, 높은 매출 증대 이벤트 기획
        목적: {목적}
        트렌드: {트렌드}
        매장: {매장정보}

        출력 형식:
        - 이벤트 제목
        - 추천 경품 (예산: 고)
        - 참여 방법 (난이도: 높음)
        - 예상 참여자 수
        - 예상 비용
        - 예상 ROI"
        end note

        AIClient -> ExternalAPI: POST /api/v1/recommend\n(고비용 옵션)
        ExternalAPI --> AIClient: 200 OK\n{추천안 3}
        AIClient --> CB: 추천안 3
        deactivate AIClient
        CB --> RecommendEngine: 옵션 3 완료
        deactivate CB
    end

    RecommendEngine -> RecommendEngine: 3가지 추천안 통합 및 검증
    note right
    검증 항목:
    - 필수 필드 존재 여부
    - 예상 성과 계산 (ROI)
    - 추천안 차별화 확인
    - 홍보 문구 생성 (각 5개)
    - SNS 해시태그 자동 생성
    end note

    RecommendEngine --> Service: 3가지 추천안 생성 완료
    deactivate RecommendEngine

    == 4. 결과 저장 및 Job 상태 업데이트 ==
    Service -> Cache: cacheRecommendations(\nkey: ai:recommendation:{eventDraftId},\ndata: {트렌드+추천안},\nTTL: 24시간)
    Cache -> Redis: SETEX ai:recommendation:{eventDraftId} 86400 {결과}
    Redis --> Cache: OK
    Cache --> Service: 캐싱 완료

    Service -> JobState: updateJobStatus(\njobId,\nstatus: COMPLETED,\nresult: {트렌드, 추천안})
    JobState -> Redis: HSET job:{jobId} status COMPLETED result {JSON}
    Redis --> JobState: OK
    JobState --> Service: 상태 업데이트 완료

    Service --> Handler: 추천 생성 완료\n{트렌드분석, 3가지추천안}
    deactivate Service

    Handler --> Consumer: ACK (메시지 처리 완료)
    deactivate Handler

    note over Consumer: Job 처리 완료\nRedis에 저장된 결과를\n클라이언트는 폴링으로 조회
end

== 예외 처리 ==
note over Handler, Producer
**AI API 장애 시**
- Circuit Breaker Open
- Fallback: 기본 트렌드 템플릿 사용
- Job 상태: COMPLETED (안내 메시지 포함)
- 사용자에게 "AI 분석이 제한적으로 제공됩니다" 안내

**Timeout (5분 초과)**
- Circuit Breaker로 즉시 실패
- Retry 없음 (비동기 Job)
- Job 상태: FAILED
- 사용자에게 재시도 요청 안내

**Kafka 메시지 처리 실패**
- DLQ(Dead Letter Queue)로 이동
- 수동 검토 및 재처리
- 에러 로그 기록

**Redis 장애**
- 캐싱 스킵
- Job 상태는 메모리에 임시 저장
- 성능 저하 가능 (매 요청마다 AI API 호출)

**성능 목표**
- 평균 응답 시간: 2분 이내
- P95 응답 시간: 4분 이내
- Circuit Breaker Timeout: 5분
- Redis 캐시 TTL: 24시간

**데이터 처리 원칙**
- 과거 이벤트 데이터 사용 안 함
- 외부 AI API로 실시간 트렌드 분석
- 업종/지역 기반 일반적 마케팅 트렌드 활용
end note

@enduml
