openapi: 3.0.3
info:
  title: Distribution Service API
  description: |
    KT AI 기반 소상공인 이벤트 자동 생성 서비스의 다중 채널 배포 관리 API

    ## 주요 기능
    - 다중 채널 동시 배포 (우리동네TV, 링고비즈, 지니TV, SNS)
    - 배포 상태 실시간 모니터링
    - Circuit Breaker 기반 장애 격리
    - Retry 패턴 및 Fallback 처리

    ## 배포 채널
    - **우리동네TV**: 영상 콘텐츠 업로드
    - **링고비즈**: 연결음 업데이트
    - **지니TV**: 광고 등록
    - **SNS**: Instagram, Naver Blog, Kakao Channel

    ## Resilience 패턴
    - Circuit Breaker: 채널별 독립적 장애 격리
    - Retry: 지수 백오프 (1s, 2s, 4s) 최대 3회
    - Bulkhead: 리소스 격리
    - Fallback: 실패 채널 스킵 및 알림

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

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

tags:
  - name: Distribution
    description: 다중 채널 배포 관리
  - name: Monitoring
    description: 배포 상태 모니터링

paths:
  /distribution/distribute:
    post:
      tags:
        - Distribution
      summary: 다중 채널 배포 요청
      description: |
        이벤트 콘텐츠를 선택된 채널들에 동시 배포합니다.

        ## 처리 흐름
        1. 배포 요청 검증 (이벤트 ID, 채널 목록, 콘텐츠 데이터)
        2. 채널별 병렬 배포 실행 (1분 이내 완료 목표)
        3. Circuit Breaker로 장애 채널 격리
        4. 실패 시 Retry (지수 백오프: 1s, 2s, 4s)
        5. Fallback: 실패 채널 스킵 및 알림
        6. 배포 결과 집계 및 로그 저장
        7. DistributionCompleted 이벤트 Kafka 발행

        ## Resilience 처리
        - 각 채널별 독립적인 Circuit Breaker 적용
        - 최대 3회 재시도 (지수 백오프)
        - 일부 채널 실패 시에도 성공 채널은 유지
        - 실패 채널 정보는 응답에 포함

      x-user-story: UFR-DIST-010
      x-controller: DistributionController
      operationId: distributeToChannels
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DistributionRequest'
            examples:
              multiChannel:
                summary: 다중 채널 배포 예시
                value:
                  eventId: "evt-12345"
                  channels:
                    - type: "WOORIDONGNE_TV"
                      config:
                        radius: "1km"
                        timeSlots:
                          - "weekday_evening"
                          - "weekend_lunch"
                    - type: "INSTAGRAM"
                      config:
                        scheduledTime: "2025-11-01T10:00:00Z"
                    - type: "NAVER_BLOG"
                      config:
                        scheduledTime: "2025-11-01T10:30:00Z"
                  contentUrls:
                    instagram: "https://cdn.example.com/images/event-instagram.jpg"
                    naverBlog: "https://cdn.example.com/images/event-naver.jpg"
                    kakaoChannel: "https://cdn.example.com/images/event-kakao.jpg"
      responses:
        '200':
          description: 배포 완료
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DistributionResponse'
              examples:
                allSuccess:
                  summary: 모든 채널 배포 성공
                  value:
                    distributionId: "dist-12345"
                    eventId: "evt-12345"
                    status: "COMPLETED"
                    completedAt: "2025-11-01T09:00:00Z"
                    results:
                      - channel: "WOORIDONGNE_TV"
                        status: "SUCCESS"
                        distributionId: "wtv-uuid-12345"
                        estimatedViews: 1000
                        message: "배포 완료"
                      - channel: "INSTAGRAM"
                        status: "SUCCESS"
                        postUrl: "https://instagram.com/p/generated-post-id"
                        postId: "ig-post-12345"
                        message: "게시 완료"
                      - channel: "NAVER_BLOG"
                        status: "SUCCESS"
                        postUrl: "https://blog.naver.com/store123/generated-post"
                        message: "게시 완료"
        '400':
          description: 잘못된 요청
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidEventId:
                  summary: 유효하지 않은 이벤트 ID
                  value:
                    error: "BAD_REQUEST"
                    message: "유효하지 않은 이벤트 ID입니다"
                    timestamp: "2025-11-01T09:00:00Z"
                noChannels:
                  summary: 선택된 채널 없음
                  value:
                    error: "BAD_REQUEST"
                    message: "최소 1개 이상의 채널을 선택해야 합니다"
                    timestamp: "2025-11-01T09:00:00Z"
        '404':
          description: 이벤트를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                eventNotFound:
                  summary: 존재하지 않는 이벤트
                  value:
                    error: "NOT_FOUND"
                    message: "이벤트를 찾을 수 없습니다: evt-12345"
                    timestamp: "2025-11-01T09:00:00Z"
        '500':
          description: 서버 내부 오류
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                internalError:
                  summary: 서버 오류
                  value:
                    error: "INTERNAL_SERVER_ERROR"
                    message: "배포 처리 중 오류가 발생했습니다"
                    timestamp: "2025-11-01T09:00:00Z"

  /distribution/{eventId}/status:
    get:
      tags:
        - Monitoring
      summary: 배포 상태 조회
      description: |
        특정 이벤트의 배포 상태를 실시간으로 조회합니다.

        ## 조회 정보
        - 전체 배포 상태 (진행중, 완료, 부분성공, 실패)
        - 채널별 배포 상태 및 결과
        - 실패 채널 상세 정보 (오류 유형, 재시도 횟수)
        - 배포 시작/완료 시간 및 소요 시간
        - 외부 채널 ID 및 배포 URL

        ## 상태 값
        - **IN_PROGRESS**: 배포 진행 중
        - **COMPLETED**: 모든 채널 배포 완료
        - **PARTIAL_SUCCESS**: 일부 채널 배포 성공
        - **FAILED**: 모든 채널 배포 실패

      x-user-story: UFR-DIST-020
      x-controller: DistributionController
      operationId: getDistributionStatus
      parameters:
        - name: eventId
          in: path
          required: true
          description: 이벤트 ID
          schema:
            type: string
            example: "evt-12345"
      responses:
        '200':
          description: 배포 상태 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DistributionStatusResponse'
              examples:
                completed:
                  summary: 배포 완료 상태
                  value:
                    eventId: "evt-12345"
                    overallStatus: "COMPLETED"
                    completedAt: "2025-11-01T09:00:00Z"
                    channels:
                      - channel: "WOORIDONGNE_TV"
                        status: "COMPLETED"
                        distributionId: "wtv-uuid-12345"
                        estimatedViews: 1500
                        completedAt: "2025-11-01T09:00:00Z"
                      - channel: "RINGO_BIZ"
                        status: "COMPLETED"
                        updateTimestamp: "2025-11-01T09:00:00Z"
                      - channel: "GENIE_TV"
                        status: "COMPLETED"
                        adId: "gtv-uuid-12345"
                        impressionSchedule:
                          - "2025-11-01 18:00-20:00"
                          - "2025-11-02 12:00-14:00"
                      - channel: "INSTAGRAM"
                        status: "COMPLETED"
                        postUrl: "https://instagram.com/p/generated-post-id"
                        postId: "ig-post-12345"
                      - channel: "NAVER_BLOG"
                        status: "COMPLETED"
                        postUrl: "https://blog.naver.com/store123/generated-post"
                      - channel: "KAKAO_CHANNEL"
                        status: "COMPLETED"
                        messageId: "kakao-msg-12345"
                inProgress:
                  summary: 배포 진행중 상태
                  value:
                    eventId: "evt-12345"
                    overallStatus: "IN_PROGRESS"
                    startedAt: "2025-11-01T08:58:00Z"
                    channels:
                      - channel: "WOORIDONGNE_TV"
                        status: "COMPLETED"
                        distributionId: "wtv-uuid-12345"
                        estimatedViews: 1500
                      - channel: "INSTAGRAM"
                        status: "IN_PROGRESS"
                        progress: 50
                      - channel: "NAVER_BLOG"
                        status: "PENDING"
                partialFailure:
                  summary: 일부 채널 실패 상태
                  value:
                    eventId: "evt-12345"
                    overallStatus: "PARTIAL_FAILURE"
                    completedAt: "2025-11-01T09:00:00Z"
                    channels:
                      - channel: "WOORIDONGNE_TV"
                        status: "COMPLETED"
                        distributionId: "wtv-uuid-12345"
                        estimatedViews: 1500
                      - channel: "INSTAGRAM"
                        status: "FAILED"
                        errorMessage: "Instagram API 타임아웃"
                        retries: 3
                        lastRetryAt: "2025-11-01T08:59:30Z"
                      - channel: "NAVER_BLOG"
                        status: "COMPLETED"
                        postUrl: "https://blog.naver.com/store123/generated-post"
        '404':
          description: 배포 이력을 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  summary: 배포 이력 없음
                  value:
                    error: "NOT_FOUND"
                    message: "배포 이력을 찾을 수 없습니다: evt-12345"
                    timestamp: "2025-11-01T09:00:00Z"
        '500':
          description: 서버 내부 오류
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    DistributionRequest:
      type: object
      required:
        - eventId
        - channels
        - contentUrls
      properties:
        eventId:
          type: string
          description: 이벤트 ID
          example: "evt-12345"
        channels:
          type: array
          description: 배포할 채널 목록
          minItems: 1
          items:
            $ref: '#/components/schemas/ChannelConfig'
        contentUrls:
          type: object
          description: 플랫폼별 콘텐츠 URL
          properties:
            wooridongneTV:
              type: string
              description: 우리동네TV 영상 URL (15초)
              example: "https://cdn.example.com/videos/event-15s.mp4"
            ringoBiz:
              type: string
              description: 링고비즈 연결음 파일 URL
              example: "https://cdn.example.com/audio/ringtone.mp3"
            genieTV:
              type: string
              description: 지니TV 광고 영상 URL
              example: "https://cdn.example.com/videos/event-ad.mp4"
            instagram:
              type: string
              description: Instagram 이미지 URL (1080x1080)
              example: "https://cdn.example.com/images/event-instagram.jpg"
            naverBlog:
              type: string
              description: Naver Blog 이미지 URL (800x600)
              example: "https://cdn.example.com/images/event-naver.jpg"
            kakaoChannel:
              type: string
              description: Kakao Channel 이미지 URL (800x800)
              example: "https://cdn.example.com/images/event-kakao.jpg"

    ChannelConfig:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          description: 채널 타입
          enum:
            - WOORIDONGNE_TV
            - RINGO_BIZ
            - GENIE_TV
            - INSTAGRAM
            - NAVER_BLOG
            - KAKAO_CHANNEL
          example: "INSTAGRAM"
        config:
          type: object
          description: 채널별 설정 (채널에 따라 다름)
          additionalProperties: true
          example:
            scheduledTime: "2025-11-01T10:00:00Z"
            caption: "이벤트 안내"
            hashtags:
              - "이벤트"
              - "할인"

    DistributionResponse:
      type: object
      required:
        - distributionId
        - eventId
        - status
        - results
      properties:
        distributionId:
          type: string
          description: 배포 ID
          example: "dist-12345"
        eventId:
          type: string
          description: 이벤트 ID
          example: "evt-12345"
        status:
          type: string
          description: 전체 배포 상태
          enum:
            - PENDING
            - IN_PROGRESS
            - COMPLETED
            - PARTIAL_FAILURE
            - FAILED
          example: "COMPLETED"
        startedAt:
          type: string
          format: date-time
          description: 배포 시작 시각
          example: "2025-11-01T08:59:00Z"
        completedAt:
          type: string
          format: date-time
          description: 배포 완료 시각
          example: "2025-11-01T09:00:00Z"
        results:
          type: array
          description: 채널별 배포 결과
          items:
            $ref: '#/components/schemas/ChannelResult'

    ChannelResult:
      type: object
      required:
        - channel
        - status
      properties:
        channel:
          type: string
          description: 채널 타입
          enum:
            - WOORIDONGNE_TV
            - RINGO_BIZ
            - GENIE_TV
            - INSTAGRAM
            - NAVER_BLOG
            - KAKAO_CHANNEL
          example: "INSTAGRAM"
        status:
          type: string
          description: 채널별 배포 상태
          enum:
            - PENDING
            - IN_PROGRESS
            - SUCCESS
            - FAILED
          example: "SUCCESS"
        distributionId:
          type: string
          description: 채널별 배포 ID (우리동네TV, 지니TV)
          example: "wtv-uuid-12345"
        estimatedViews:
          type: integer
          description: 예상 노출 수 (우리동네TV, 지니TV)
          example: 1500
        updateTimestamp:
          type: string
          format: date-time
          description: 업데이트 완료 시각 (링고비즈)
          example: "2025-11-01T09:00:00Z"
        adId:
          type: string
          description: 광고 ID (지니TV)
          example: "gtv-uuid-12345"
        impressionSchedule:
          type: array
          description: 노출 스케줄 (지니TV)
          items:
            type: string
          example:
            - "2025-11-01 18:00-20:00"
            - "2025-11-02 12:00-14:00"
        postUrl:
          type: string
          description: 게시물 URL (Instagram, Naver Blog)
          example: "https://instagram.com/p/generated-post-id"
        postId:
          type: string
          description: 게시물 ID (Instagram)
          example: "ig-post-12345"
        messageId:
          type: string
          description: 메시지 ID (Kakao Channel)
          example: "kakao-msg-12345"
        message:
          type: string
          description: 결과 메시지
          example: "배포 완료"
        errorMessage:
          type: string
          description: 오류 메시지 (실패 시)
          example: "Instagram API 타임아웃"
        retries:
          type: integer
          description: 재시도 횟수
          example: 0
        lastRetryAt:
          type: string
          format: date-time
          description: 마지막 재시도 시각
          example: "2025-11-01T08:59:30Z"

    DistributionStatusResponse:
      type: object
      required:
        - eventId
        - overallStatus
        - channels
      properties:
        eventId:
          type: string
          description: 이벤트 ID
          example: "evt-12345"
        overallStatus:
          type: string
          description: 전체 배포 상태
          enum:
            - PENDING
            - IN_PROGRESS
            - COMPLETED
            - PARTIAL_FAILURE
            - FAILED
            - NOT_FOUND
          example: "COMPLETED"
        startedAt:
          type: string
          format: date-time
          description: 배포 시작 시각
          example: "2025-11-01T08:59:00Z"
        completedAt:
          type: string
          format: date-time
          description: 배포 완료 시각
          example: "2025-11-01T09:00:00Z"
        channels:
          type: array
          description: 채널별 배포 상태
          items:
            $ref: '#/components/schemas/ChannelStatus'

    ChannelStatus:
      type: object
      required:
        - channel
        - status
      properties:
        channel:
          type: string
          description: 채널 타입
          enum:
            - WOORIDONGNE_TV
            - RINGO_BIZ
            - GENIE_TV
            - INSTAGRAM
            - NAVER_BLOG
            - KAKAO_CHANNEL
          example: "INSTAGRAM"
        status:
          type: string
          description: 채널별 배포 상태
          enum:
            - PENDING
            - IN_PROGRESS
            - COMPLETED
            - FAILED
          example: "COMPLETED"
        progress:
          type: integer
          description: 진행률 (0-100, IN_PROGRESS 상태일 때)
          minimum: 0
          maximum: 100
          example: 75
        distributionId:
          type: string
          description: 채널별 배포 ID
          example: "wtv-uuid-12345"
        estimatedViews:
          type: integer
          description: 예상 노출 수
          example: 1500
        updateTimestamp:
          type: string
          format: date-time
          description: 업데이트 완료 시각
          example: "2025-11-01T09:00:00Z"
        adId:
          type: string
          description: 광고 ID
          example: "gtv-uuid-12345"
        impressionSchedule:
          type: array
          description: 노출 스케줄
          items:
            type: string
          example:
            - "2025-11-01 18:00-20:00"
        postUrl:
          type: string
          description: 게시물 URL
          example: "https://instagram.com/p/generated-post-id"
        postId:
          type: string
          description: 게시물 ID
          example: "ig-post-12345"
        messageId:
          type: string
          description: 메시지 ID
          example: "kakao-msg-12345"
        completedAt:
          type: string
          format: date-time
          description: 완료 시각
          example: "2025-11-01T09:00:00Z"
        errorMessage:
          type: string
          description: 오류 메시지
          example: "Instagram API 타임아웃"
        retries:
          type: integer
          description: 재시도 횟수
          example: 3
        lastRetryAt:
          type: string
          format: date-time
          description: 마지막 재시도 시각
          example: "2025-11-01T08:59:30Z"

    ErrorResponse:
      type: object
      required:
        - error
        - message
        - timestamp
      properties:
        error:
          type: string
          description: 오류 코드
          enum:
            - BAD_REQUEST
            - NOT_FOUND
            - INTERNAL_SERVER_ERROR
          example: "BAD_REQUEST"
        message:
          type: string
          description: 오류 메시지
          example: "유효하지 않은 이벤트 ID입니다"
        timestamp:
          type: string
          format: date-time
          description: 오류 발생 시각
          example: "2025-11-01T09:00:00Z"
        details:
          type: object
          description: 추가 오류 정보 (선택 사항)
          additionalProperties: true
