openapi: 3.0.3
info:
  title: AI Service API
  description: |
    KT AI 기반 소상공인 이벤트 자동 생성 서비스 - AI Service

    ## 서비스 개요
    - Kafka를 통한 비동기 AI 추천 처리
    - Claude API / GPT-4 API 연동
    - Redis 기반 결과 캐싱 (TTL 24시간)

    ## 처리 흐름
    1. Event Service가 Kafka Topic에 Job 메시지 발행
    2. AI Service가 메시지 구독 및 처리
    3. 트렌드 분석 수행 (Claude/GPT-4 API)
    4. 3가지 이벤트 추천안 생성
    5. 결과를 Redis에 저장 (TTL 24시간)
    6. Job 상태를 Redis에 업데이트

    ## 외부 API 통합
    - **Claude API / GPT-4 API**: 트렌드 분석 및 이벤트 추천
    - **Circuit Breaker**: 5분 타임아웃, Fallback to 캐시
    - **Retry**: 최대 3회, Exponential Backoff

  version: 1.0.0
  contact:
    name: Digital Garage Team
    email: support@kt-event-marketing.com

servers:
  - url: http://localhost:8083
    description: Local Development Server
  - url: https://dev-api.kt-event-marketing.com/ai/v1
    description: Development Server
  - url: https://api.kt-event-marketing.com/ai/v1
    description: Production Server

tags:
  - name: Health Check
    description: 서비스 상태 확인
  - name: Internal API
    description: 내부 서비스 간 통신용 API
  - name: Kafka Consumer
    description: 비동기 작업 처리 (문서화만)

paths:
  /health:
    get:
      tags:
        - Health Check
      summary: 서비스 헬스체크
      description: AI Service 상태 및 외부 연동 확인
      operationId: healthCheck
      x-user-story: System
      x-controller: HealthController
      responses:
        '200':
          description: 서비스 정상
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthCheckResponse'
              example:
                status: UP
                timestamp: "2025-10-23T10:30:00Z"
                services:
                  kafka: UP
                  redis: UP
                  claude_api: UP
                  gpt4_api: UP
                  circuit_breaker: CLOSED

  /internal/jobs/{jobId}/status:
    get:
      tags:
        - Internal API
      summary: 작업 상태 조회
      description: Redis에 저장된 AI 추천 작업 상태 조회 (Event Service에서 호출)
      operationId: getJobStatus
      x-user-story: UFR-AI-010
      x-controller: InternalJobController
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
          description: Job ID
          example: "job-ai-evt001-20251023103000"
      responses:
        '200':
          description: 작업 상태 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
              examples:
                processing:
                  summary: 처리 중
                  value:
                    jobId: "job-ai-evt001-20251023103000"
                    status: "PROCESSING"
                    progress: 50
                    message: "AI 추천 생성 중"
                    createdAt: "2025-10-23T10:30:00Z"
                    startedAt: "2025-10-23T10:30:05Z"
                completed:
                  summary: 완료
                  value:
                    jobId: "job-ai-evt001-20251023103000"
                    status: "COMPLETED"
                    progress: 100
                    message: "AI 추천 완료"
                    createdAt: "2025-10-23T10:30:00Z"
                    startedAt: "2025-10-23T10:30:05Z"
                    completedAt: "2025-10-23T10:35:00Z"
                    processingTimeMs: 295000
                failed:
                  summary: 실패
                  value:
                    jobId: "job-ai-evt001-20251023103000"
                    status: "FAILED"
                    progress: 0
                    message: "Claude API timeout"
                    errorMessage: "Claude API timeout after 5 minutes"
                    createdAt: "2025-10-23T10:30:00Z"
                    startedAt: "2025-10-23T10:30:05Z"
                    failedAt: "2025-10-23T10:35:05Z"
                    retryCount: 3
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /internal/recommendations/{eventId}:
    get:
      tags:
        - Internal API
      summary: AI 추천 결과 조회
      description: Redis에 캐시된 AI 추천 결과 조회 (Event Service에서 호출)
      operationId: getRecommendation
      x-user-story: UFR-AI-010
      x-controller: InternalRecommendationController
      parameters:
        - name: eventId
          in: path
          required: true
          schema:
            type: string
          description: 이벤트 ID
          example: "evt-001"
      responses:
        '200':
          description: 추천 결과 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIRecommendationResult'
              example:
                eventId: "evt-001"
                trendAnalysis:
                  industryTrends:
                    - keyword: "프리미엄 디저트"
                      relevance: 0.85
                      description: "고급 디저트 카페 트렌드 증가"
                  regionalTrends:
                    - keyword: "핫플레이스"
                      relevance: 0.78
                      description: "강남 신논현역 주변 유동인구 증가"
                  seasonalTrends:
                    - keyword: "가을 시즌"
                      relevance: 0.92
                      description: "가을 시즌 한정 메뉴 선호도 증가"
                recommendations:
                  - optionNumber: 1
                    concept: "프리미엄 경험형"
                    title: "가을 한정 시그니처 디저트 페어링 이벤트"
                    description: "가을 제철 재료를 활용한 시그니처 디저트와 음료 페어링 체험"
                    targetAudience: "20-30대 여성, SNS 활동적인 고객"
                    duration:
                      recommendedDays: 14
                      recommendedPeriod: "10월 중순 ~ 11월 초"
                    mechanics:
                      type: "EXPERIENCE"
                      details: "디저트+음료 페어링 세트 주문 시 인스타그램 업로드 고객에게 다음 방문 시 사용 가능한 10% 할인권 제공"
                    promotionChannels:
                      - "Instagram"
                      - "카카오톡 채널"
                      - "네이버 플레이스"
                    estimatedCost:
                      min: 300000
                      max: 500000
                      breakdown:
                        material: 200000
                        promotion: 150000
                        discount: 150000
                    expectedMetrics:
                      newCustomers:
                        min: 50
                        max: 80
                      repeatVisits:
                        min: 30
                        max: 50
                      revenueIncrease:
                        min: 15.0
                        max: 25.0
                      roi:
                        min: 120.0
                        max: 180.0
                      socialEngagement:
                        estimatedPosts: 100
                        estimatedReach: 5000
                    differentiator: "프리미엄 경험 제공으로 고객 만족도와 SNS 바이럴 효과 극대화"
                generatedAt: "2025-10-23T10:35:00Z"
                expiresAt: "2025-10-24T10:35:00Z"
                aiProvider: "CLAUDE"
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  schemas:
    # ==================== Health Check ====================
    HealthCheckResponse:
      type: object
      description: 서비스 헬스체크 응답
      required:
        - status
        - timestamp
        - services
      properties:
        status:
          type: string
          enum: [UP, DOWN, DEGRADED]
          description: 전체 서비스 상태
          example: UP
        timestamp:
          type: string
          format: date-time
          description: 체크 시각
          example: "2025-10-23T10:30:00Z"
        services:
          type: object
          description: 개별 서비스 상태
          required:
            - kafka
            - redis
            - claude_api
            - circuit_breaker
          properties:
            kafka:
              type: string
              enum: [UP, DOWN]
              description: Kafka 연결 상태
              example: UP
            redis:
              type: string
              enum: [UP, DOWN]
              description: Redis 연결 상태
              example: UP
            claude_api:
              type: string
              enum: [UP, DOWN, CIRCUIT_OPEN]
              description: Claude API 상태
              example: UP
            gpt4_api:
              type: string
              enum: [UP, DOWN, CIRCUIT_OPEN]
              description: GPT-4 API 상태 (선택)
              example: UP
            circuit_breaker:
              type: string
              enum: [CLOSED, OPEN, HALF_OPEN]
              description: Circuit Breaker 상태
              example: CLOSED

    # ==================== Kafka Job Message (문서화만) ====================
    KafkaAIJobMessage:
      type: object
      description: |
        **Kafka Topic**: `ai-event-generation-job`
        **Consumer Group**: `ai-service-consumers`
        **처리 방식**: 비동기
        **최대 처리 시간**: 5분

        AI 이벤트 생성 요청 메시지
      required:
        - jobId
        - eventId
        - objective
        - industry
        - region
      properties:
        jobId:
          type: string
          description: Job 고유 ID
          example: "job-ai-evt001-20251023103000"
        eventId:
          type: string
          description: 이벤트 ID (Event Service에서 생성)
          example: "evt-001"
        objective:
          type: string
          description: 이벤트 목적
          enum:
            - "신규 고객 유치"
            - "재방문 유도"
            - "매출 증대"
            - "브랜드 인지도 향상"
          example: "신규 고객 유치"
        industry:
          type: string
          description: 업종
          example: "음식점"
        region:
          type: string
          description: 지역 (시/구/동)
          example: "서울 강남구"
        storeName:
          type: string
          description: 매장명 (선택)
          example: "맛있는 고깃집"
        targetAudience:
          type: string
          description: 목표 고객층 (선택)
          example: "20-30대 여성"
        budget:
          type: integer
          description: 예산 (원) (선택)
          example: 500000
        requestedAt:
          type: string
          format: date-time
          description: 요청 시각
          example: "2025-10-23T10:30:00Z"

    # ==================== AI Recommendation Result ====================
    AIRecommendationResult:
      type: object
      description: |
        **Redis Key**: `ai:recommendation:{eventId}`
        **TTL**: 86400초 (24시간)

        AI 이벤트 추천 결과
      required:
        - eventId
        - trendAnalysis
        - recommendations
        - generatedAt
        - aiProvider
      properties:
        eventId:
          type: string
          description: 이벤트 ID
          example: "evt-001"
        trendAnalysis:
          $ref: '#/components/schemas/TrendAnalysis'
        recommendations:
          type: array
          description: 추천 이벤트 기획안 (3개)
          minItems: 3
          maxItems: 3
          items:
            $ref: '#/components/schemas/EventRecommendation'
        generatedAt:
          type: string
          format: date-time
          description: 생성 시각
          example: "2025-10-23T10:35:00Z"
        expiresAt:
          type: string
          format: date-time
          description: 캐시 만료 시각 (생성 시각 + 24시간)
          example: "2025-10-24T10:35:00Z"
        aiProvider:
          type: string
          enum: [CLAUDE, GPT4]
          description: 사용된 AI 제공자
          example: "CLAUDE"

    TrendAnalysis:
      type: object
      description: 트렌드 분석 결과 (업종/지역/시즌)
      required:
        - industryTrends
        - regionalTrends
        - seasonalTrends
      properties:
        industryTrends:
          type: array
          description: 업종 트렌드 키워드 (최대 5개)
          maxItems: 5
          items:
            type: object
            required:
              - keyword
              - relevance
              - description
            properties:
              keyword:
                type: string
                description: 트렌드 키워드
                example: "프리미엄 디저트"
              relevance:
                type: number
                format: float
                minimum: 0
                maximum: 1
                description: 연관도 (0-1)
                example: 0.85
              description:
                type: string
                description: 트렌드 설명
                example: "고급 디저트 카페 트렌드 증가"
        regionalTrends:
          type: array
          description: 지역 트렌드 키워드 (최대 5개)
          maxItems: 5
          items:
            type: object
            required:
              - keyword
              - relevance
              - description
            properties:
              keyword:
                type: string
                example: "핫플레이스"
              relevance:
                type: number
                format: float
                minimum: 0
                maximum: 1
                example: 0.78
              description:
                type: string
                example: "강남 신논현역 주변 유동인구 증가"
        seasonalTrends:
          type: array
          description: 시즌 트렌드 키워드 (최대 5개)
          maxItems: 5
          items:
            type: object
            required:
              - keyword
              - relevance
              - description
            properties:
              keyword:
                type: string
                example: "가을 시즌"
              relevance:
                type: number
                format: float
                minimum: 0
                maximum: 1
                example: 0.92
              description:
                type: string
                example: "가을 시즌 한정 메뉴 선호도 증가"

    EventRecommendation:
      type: object
      description: 이벤트 추천안 (차별화된 3가지 옵션)
      required:
        - optionNumber
        - concept
        - title
        - description
        - targetAudience
        - duration
        - mechanics
        - promotionChannels
        - estimatedCost
        - expectedMetrics
        - differentiator
      properties:
        optionNumber:
          type: integer
          description: 옵션 번호 (1-3)
          minimum: 1
          maximum: 3
          example: 1
        concept:
          type: string
          description: 이벤트 컨셉
          example: "프리미엄 경험형"
        title:
          type: string
          description: 이벤트 제목
          maxLength: 100
          example: "가을 한정 시그니처 디저트 페어링 이벤트"
        description:
          type: string
          description: 이벤트 설명
          maxLength: 500
          example: "가을 제철 재료를 활용한 시그니처 디저트와 음료 페어링 체험"
        targetAudience:
          type: string
          description: 목표 고객층
          example: "20-30대 여성, SNS 활동적인 고객"
        duration:
          type: object
          description: 이벤트 기간
          required:
            - recommendedDays
          properties:
            recommendedDays:
              type: integer
              description: 권장 진행 일수
              minimum: 1
              example: 14
            recommendedPeriod:
              type: string
              description: 권장 진행 시기
              example: "10월 중순 ~ 11월 초"
        mechanics:
          type: object
          description: 이벤트 메커니즘
          required:
            - type
            - details
          properties:
            type:
              type: string
              enum: [DISCOUNT, GIFT, STAMP, EXPERIENCE, LOTTERY, COMBO]
              description: 이벤트 유형
              example: "EXPERIENCE"
            details:
              type: string
              description: 상세 메커니즘
              maxLength: 500
              example: "디저트+음료 페어링 세트 주문 시 인스타그램 업로드 고객에게 다음 방문 시 사용 가능한 10% 할인권 제공"
        promotionChannels:
          type: array
          description: 추천 홍보 채널 (최대 5개)
          maxItems: 5
          items:
            type: string
          example:
            - "Instagram"
            - "카카오톡 채널"
            - "네이버 플레이스"
        estimatedCost:
          type: object
          description: 예상 비용
          required:
            - min
            - max
          properties:
            min:
              type: integer
              description: 최소 비용 (원)
              minimum: 0
              example: 300000
            max:
              type: integer
              description: 최대 비용 (원)
              minimum: 0
              example: 500000
            breakdown:
              type: object
              description: 비용 구성
              properties:
                material:
                  type: integer
                  description: 재료비 (원)
                  example: 200000
                promotion:
                  type: integer
                  description: 홍보비 (원)
                  example: 150000
                discount:
                  type: integer
                  description: 할인 비용 (원)
                  example: 150000
        expectedMetrics:
          $ref: '#/components/schemas/ExpectedMetrics'
        differentiator:
          type: string
          description: 다른 옵션과의 차별점
          maxLength: 500
          example: "프리미엄 경험 제공으로 고객 만족도와 SNS 바이럴 효과 극대화, 브랜드 이미지 향상에 집중"

    ExpectedMetrics:
      type: object
      description: 예상 성과 지표
      required:
        - newCustomers
        - revenueIncrease
        - roi
      properties:
        newCustomers:
          type: object
          description: 신규 고객 수
          required:
            - min
            - max
          properties:
            min:
              type: integer
              minimum: 0
              example: 50
            max:
              type: integer
              minimum: 0
              example: 80
        repeatVisits:
          type: object
          description: 재방문 고객 수 (선택)
          properties:
            min:
              type: integer
              minimum: 0
              example: 30
            max:
              type: integer
              minimum: 0
              example: 50
        revenueIncrease:
          type: object
          description: 매출 증가율 (%)
          required:
            - min
            - max
          properties:
            min:
              type: number
              format: float
              minimum: 0
              example: 15.0
            max:
              type: number
              format: float
              minimum: 0
              example: 25.0
        roi:
          type: object
          description: ROI - 투자 대비 수익률 (%)
          required:
            - min
            - max
          properties:
            min:
              type: number
              format: float
              minimum: 0
              example: 120.0
            max:
              type: number
              format: float
              minimum: 0
              example: 180.0
        socialEngagement:
          type: object
          description: SNS 참여도 (선택)
          properties:
            estimatedPosts:
              type: integer
              description: 예상 게시물 수
              minimum: 0
              example: 100
            estimatedReach:
              type: integer
              description: 예상 도달 수
              minimum: 0
              example: 5000

    # ==================== Job Status ====================
    JobStatusResponse:
      type: object
      description: |
        **Redis Key**: `ai:job:status:{jobId}`
        **TTL**: 86400초 (24시간)

        작업 상태 응답
      required:
        - jobId
        - status
        - progress
        - message
        - createdAt
      properties:
        jobId:
          type: string
          description: Job ID
          example: "job-ai-evt001-20251023103000"
        status:
          type: string
          enum: [PENDING, PROCESSING, COMPLETED, FAILED]
          description: 작업 상태
          example: "COMPLETED"
        progress:
          type: integer
          minimum: 0
          maximum: 100
          description: 진행률 (%)
          example: 100
        message:
          type: string
          description: 상태 메시지
          example: "AI 추천 완료"
        eventId:
          type: string
          description: 이벤트 ID
          example: "evt-001"
        createdAt:
          type: string
          format: date-time
          description: 작업 생성 시각
          example: "2025-10-23T10:30:00Z"
        startedAt:
          type: string
          format: date-time
          description: 작업 시작 시각
          example: "2025-10-23T10:30:05Z"
        completedAt:
          type: string
          format: date-time
          description: 작업 완료 시각 (완료 시)
          example: "2025-10-23T10:35:00Z"
        failedAt:
          type: string
          format: date-time
          description: 작업 실패 시각 (실패 시)
          example: "2025-10-23T10:35:05Z"
        errorMessage:
          type: string
          description: 에러 메시지 (실패 시)
          example: "Claude API timeout after 5 minutes"
        retryCount:
          type: integer
          description: 재시도 횟수
          minimum: 0
          example: 0
        processingTimeMs:
          type: integer
          description: 처리 시간 (밀리초)
          minimum: 0
          example: 295000

    # ==================== Error Response ====================
    ErrorResponse:
      type: object
      description: 에러 응답
      required:
        - code
        - message
        - timestamp
      properties:
        code:
          type: string
          description: 에러 코드
          enum:
            - AI_SERVICE_ERROR
            - JOB_NOT_FOUND
            - RECOMMENDATION_NOT_FOUND
            - REDIS_ERROR
            - KAFKA_ERROR
            - CIRCUIT_BREAKER_OPEN
            - INTERNAL_ERROR
          example: "JOB_NOT_FOUND"
        message:
          type: string
          description: 에러 메시지
          example: "작업을 찾을 수 없습니다"
        timestamp:
          type: string
          format: date-time
          description: 에러 발생 시각
          example: "2025-10-23T10:30:00Z"
        details:
          type: object
          description: 추가 에러 상세
          additionalProperties: true
          example:
            jobId: "job-ai-evt001-20251023103000"

  responses:
    NotFound:
      description: 리소스를 찾을 수 없음
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: "JOB_NOT_FOUND"
            message: "작업을 찾을 수 없습니다"
            timestamp: "2025-10-23T10:30:00Z"

    InternalServerError:
      description: 서버 내부 오류
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: "INTERNAL_ERROR"
            message: "서버 내부 오류가 발생했습니다"
            timestamp: "2025-10-23T10:30:00Z"

# ==================== 기술 구성 문서화 ====================
x-technical-specifications:
  circuit-breaker:
    claude-api:
      failureThreshold: 5
      successThreshold: 2
      timeout: 300000
      resetTimeout: 60000
      fallbackStrategy: CACHED_RECOMMENDATION
    gpt4-api:
      failureThreshold: 5
      successThreshold: 2
      timeout: 300000
      resetTimeout: 60000
      fallbackStrategy: CACHED_RECOMMENDATION

  redis-cache:
    patterns:
      recommendation: "ai:recommendation:{eventId}"
      jobStatus: "ai:job:status:{jobId}"
      fallback: "ai:fallback:{industry}:{region}"
    ttl:
      recommendation: 86400
      jobStatus: 86400
      fallback: 604800

  kafka:
    topics:
      input: "ai-event-generation-job"
    consumer:
      groupId: "ai-service-consumers"
      maxRetries: 3
      retryBackoffMs: 5000
      maxPollRecords: 10
      sessionTimeoutMs: 30000

  external-apis:
    claude:
      endpoint: "https://api.anthropic.com/v1/messages"
      model: "claude-3-5-sonnet-20241022"
      maxTokens: 4096
      timeout: 300000
    gpt4:
      endpoint: "https://api.openai.com/v1/chat/completions"
      model: "gpt-4-turbo-preview"
      maxTokens: 4096
      timeout: 300000
