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

    이벤트 전체 생명주기 관리 (생성, 조회, 수정, 배포, 종료)
    - AI 기반 이벤트 추천 및 커스터마이징
    - 이미지 생성 및 편집 오케스트레이션
    - 배포 채널 관리 및 최종 배포
    - 이벤트 상태 관리 (DRAFT, PUBLISHED, ENDED)
  version: 1.0.0
  contact:
    name: Digital Garage Team
    email: support@kt-event-marketing.com

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

security:
  - BearerAuth: []

tags:
  - name: Dashboard
    description: 대시보드 및 이벤트 목록 조회
  - name: Event Creation
    description: 이벤트 생성 플로우
  - name: Event Management
    description: 이벤트 수정, 삭제, 종료
  - name: Job Status
    description: 비동기 작업 상태 조회

paths:
  /events:
    get:
      tags:
        - Dashboard
      summary: 이벤트 목록 조회
      description: |
        사용자의 이벤트 목록을 조회합니다 (대시보드, 전체보기).
        필터, 검색, 페이징을 지원합니다.
      operationId: getEvents
      x-user-story: UFR-EVENT-010, UFR-EVENT-070
      x-controller: EventController.getEvents
      parameters:
        - name: status
          in: query
          description: 이벤트 상태 필터 (DRAFT, PUBLISHED, ENDED)
          required: false
          schema:
            type: string
            enum: [DRAFT, PUBLISHED, ENDED]
            example: PUBLISHED
        - name: objective
          in: query
          description: 이벤트 목적 필터
          required: false
          schema:
            type: string
            example: 신규 고객 유치
        - name: search
          in: query
          description: 검색어 (이벤트명)
          required: false
          schema:
            type: string
            example: 봄맞이
        - name: page
          in: query
          description: 페이지 번호 (0부터 시작)
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
            example: 0
        - name: size
          in: query
          description: 페이지 크기
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
            example: 20
        - name: sort
          in: query
          description: 정렬 기준 (createdAt, startDate, endDate)
          required: false
          schema:
            type: string
            enum: [createdAt, startDate, endDate]
            default: createdAt
            example: createdAt
        - name: order
          in: query
          description: 정렬 순서 (asc, desc)
          required: false
          schema:
            type: string
            enum: [asc, desc]
            default: desc
            example: desc
      responses:
        '200':
          description: 이벤트 목록 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}:
    get:
      tags:
        - Dashboard
      summary: 이벤트 상세 조회
      description: 특정 이벤트의 상세 정보를 조회합니다.
      operationId: getEvent
      x-user-story: UFR-EVENT-060
      x-controller: EventController.getEvent
      parameters:
        - $ref: '#/components/parameters/EventId'
      responses:
        '200':
          description: 이벤트 상세 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventDetailResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      tags:
        - Event Management
      summary: 이벤트 수정
      description: |
        기존 이벤트의 정보를 수정합니다.
        DRAFT 상태의 이벤트만 전체 수정 가능하며,
        PUBLISHED 상태에서는 제한적 수정만 가능합니다.
      operationId: updateEvent
      x-user-story: UFR-EVENT-060
      x-controller: EventController.updateEvent
      parameters:
        - $ref: '#/components/parameters/EventId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEventRequest'
      responses:
        '200':
          description: 이벤트 수정 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventDetailResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태로 인해 수정 불가
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: EVENT_NOT_MODIFIABLE
                message: PUBLISHED 상태의 이벤트는 제한적으로만 수정 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

    delete:
      tags:
        - Event Management
      summary: 이벤트 삭제
      description: |
        이벤트를 삭제합니다.
        DRAFT 상태의 이벤트만 삭제 가능합니다.
      operationId: deleteEvent
      x-user-story: UFR-EVENT-070
      x-controller: EventController.deleteEvent
      parameters:
        - $ref: '#/components/parameters/EventId'
      responses:
        '204':
          description: 이벤트 삭제 성공
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태로 인해 삭제 불가
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: EVENT_NOT_DELETABLE
                message: DRAFT 상태의 이벤트만 삭제 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/objectives:
    post:
      tags:
        - Event Creation
      summary: 이벤트 목적 선택 (Step 1)
      description: |
        이벤트 생성 플로우의 첫 단계입니다.
        사용자가 이벤트 목적을 선택하고 DRAFT 상태의 이벤트를 생성합니다.
      operationId: selectObjective
      x-user-story: UFR-EVENT-020
      x-controller: EventController.selectObjective
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SelectObjectiveRequest'
      responses:
        '201':
          description: 이벤트 생성 성공 (DRAFT 상태)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventCreatedResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/ai-recommendations:
    post:
      tags:
        - Event Creation
      summary: AI 추천 요청 (Step 2)
      description: |
        AI 서비스에 이벤트 추천 생성을 요청합니다.
        Kafka Job을 발행하고 jobId를 반환합니다.
        Job 상태는 /jobs/{jobId}로 폴링하여 확인합니다.
      operationId: requestAiRecommendations
      x-user-story: UFR-EVENT-030
      x-controller: EventController.requestAiRecommendations
      parameters:
        - $ref: '#/components/parameters/EventId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AiRecommendationRequest'
      responses:
        '202':
          description: AI 추천 요청 접수
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobAcceptedResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: INVALID_EVENT_STATE
                message: DRAFT 상태의 이벤트만 AI 추천을 요청할 수 있습니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/recommendations:
    put:
      tags:
        - Event Creation
      summary: AI 추천 선택 및 커스터마이징 (Step 2-2)
      description: |
        AI가 생성한 추천 중 하나를 선택하고,
        필요시 이벤트명, 문구, 기간 등을 커스터마이징합니다.
      operationId: selectRecommendation
      x-user-story: UFR-EVENT-030
      x-controller: EventController.selectRecommendation
      parameters:
        - $ref: '#/components/parameters/EventId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SelectRecommendationRequest'
      responses:
        '200':
          description: AI 추천 선택 및 커스터마이징 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventDetailResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: INVALID_EVENT_STATE
                message: AI 추천이 완료된 DRAFT 상태의 이벤트만 선택 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/images:
    post:
      tags:
        - Event Creation
      summary: 이미지 생성 요청 (Step 3)
      description: |
        Content Service에 이미지 생성을 요청합니다.
        Kafka Job을 발행하고 jobId를 반환합니다.
        Job 상태는 /jobs/{jobId}로 폴링하여 확인합니다.
      operationId: requestImageGeneration
      x-user-story: UFR-CONT-010
      x-controller: EventController.requestImageGeneration
      parameters:
        - $ref: '#/components/parameters/EventId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImageGenerationRequest'
      responses:
        '202':
          description: 이미지 생성 요청 접수
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobAcceptedResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: INVALID_EVENT_STATE
                message: AI 추천이 선택된 DRAFT 상태의 이벤트만 이미지 생성이 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/images/{imageId}/select:
    put:
      tags:
        - Event Creation
      summary: 이미지 선택 (Step 3-2)
      description: |
        생성된 이미지 중 하나를 선택합니다.
        선택된 이미지는 이벤트의 대표 이미지로 설정됩니다.
      operationId: selectImage
      x-user-story: UFR-CONT-010
      x-controller: EventController.selectImage
      parameters:
        - $ref: '#/components/parameters/EventId'
        - name: imageId
          in: path
          description: 이미지 ID
          required: true
          schema:
            type: string
            format: uuid
            example: "550e8400-e29b-41d4-a716-446655440006"
      responses:
        '200':
          description: 이미지 선택 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventDetailResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 또는 이미지 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: INVALID_IMAGE_STATE
                message: 해당 이미지는 이 이벤트에 속하지 않습니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/images/{imageId}/edit:
    put:
      tags:
        - Event Creation
      summary: 이미지 편집 (Step 3-3)
      description: |
        선택된 이미지를 편집합니다.
        Content Service에 편집 요청을 보내고 새로운 이미지 URL을 받습니다.
      operationId: editImage
      x-user-story: UFR-CONT-020
      x-controller: EventController.editImage
      parameters:
        - $ref: '#/components/parameters/EventId'
        - name: imageId
          in: path
          description: 이미지 ID
          required: true
          schema:
            type: string
            format: uuid
            example: "550e8400-e29b-41d4-a716-446655440006"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImageEditRequest'
      responses:
        '200':
          description: 이미지 편집 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImageEditResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이미지 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: IMAGE_NOT_EDITABLE
                message: 선택된 이미지만 편집 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/channels:
    put:
      tags:
        - Event Creation
      summary: 배포 채널 선택 (Step 4)
      description: |
        이벤트를 배포할 채널을 선택합니다.
        (웹사이트, 카카오톡, Instagram, Facebook 등)
      operationId: selectChannels
      x-user-story: UFR-EVENT-040
      x-controller: EventController.selectChannels
      parameters:
        - $ref: '#/components/parameters/EventId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SelectChannelsRequest'
      responses:
        '200':
          description: 배포 채널 선택 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventDetailResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: INVALID_EVENT_STATE
                message: 이미지가 선택된 DRAFT 상태의 이벤트만 채널 선택이 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /events/{eventId}/publish:
    post:
      tags:
        - Event Creation
      summary: 최종 승인 및 배포 (Step 5)
      description: |
        이벤트를 최종 승인하고 선택된 채널에 배포합니다.
        Distribution Service를 동기 호출하여 배포하고,
        이벤트 상태를 PUBLISHED로 변경합니다.
        Kafka Event (EventCreated)를 발행합니다.
      operationId: publishEvent
      x-user-story: UFR-EVENT-050
      x-controller: EventController.publishEvent
      parameters:
        - $ref: '#/components/parameters/EventId'
      responses:
        '200':
          description: 이벤트 배포 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventPublishedResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태가 적절하지 않음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: EVENT_NOT_PUBLISHABLE
                message: 배포 채널이 선택된 DRAFT 상태의 이벤트만 배포 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          description: Distribution Service 호출 실패
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: DISTRIBUTION_SERVICE_UNAVAILABLE
                message: 배포 서비스를 일시적으로 사용할 수 없습니다.

  /events/{eventId}/end:
    post:
      tags:
        - Event Management
      summary: 이벤트 조기 종료
      description: |
        진행 중인 이벤트를 조기 종료합니다.
        PUBLISHED 상태의 이벤트만 종료 가능하며,
        종료 시 상태가 ENDED로 변경됩니다.
      operationId: endEvent
      x-user-story: UFR-EVENT-060
      x-controller: EventController.endEvent
      parameters:
        - $ref: '#/components/parameters/EventId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EndEventRequest'
      responses:
        '200':
          description: 이벤트 종료 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventDetailResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 이벤트 상태로 인해 종료 불가
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: EVENT_NOT_ENDABLE
                message: PUBLISHED 상태의 이벤트만 종료 가능합니다.
        '500':
          $ref: '#/components/responses/InternalServerError'

  /jobs/{jobId}:
    get:
      tags:
        - Job Status
      summary: Job 상태 폴링
      description: |
        비동기 작업(AI 추천 생성, 이미지 생성)의 상태를 조회합니다.
        클라이언트는 COMPLETED 또는 FAILED가 될 때까지 폴링합니다.
        COMPLETED 시 Redis에서 결과를 조회할 수 있습니다.
      operationId: getJobStatus
      x-user-story: UFR-EVENT-030, UFR-CONT-010
      x-controller: JobController.getJobStatus
      parameters:
        - name: jobId
          in: path
          description: Job ID
          required: true
          schema:
            type: string
            format: uuid
            example: "550e8400-e29b-41d4-a716-446655440005"
      responses:
        '200':
          description: Job 상태 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        JWT Bearer 토큰 인증

        **형식:** Authorization: Bearer {JWT_TOKEN}

        **토큰 만료:** 7일

        **Claims:**
        - userId: 사용자 ID
        - role: 사용자 역할 (OWNER)
        - iat: 발급 시각
        - exp: 만료 시각

  parameters:
    EventId:
      name: eventId
      in: path
      description: 이벤트 ID
      required: true
      schema:
        type: string
        format: uuid
        example: "550e8400-e29b-41d4-a716-446655440000"

  schemas:
    EventListResponse:
      type: object
      properties:
        content:
          type: array
          items:
            $ref: '#/components/schemas/EventSummary'
        page:
          $ref: '#/components/schemas/PageInfo'
      required:
        - content
        - page

    EventSummary:
      type: object
      properties:
        eventId:
          type: string
          format: uuid
          description: 이벤트 ID
          example: "550e8400-e29b-41d4-a716-446655440000"
        eventName:
          type: string
          description: 이벤트명
          example: "봄맞이 20% 할인 이벤트"
        objective:
          type: string
          description: 이벤트 목적
          example: "신규 고객 유치"
        status:
          type: string
          enum: [DRAFT, PUBLISHED, ENDED]
          description: 이벤트 상태
          example: "PUBLISHED"
        startDate:
          type: string
          format: date
          description: 시작일
          example: "2025-03-01"
        endDate:
          type: string
          format: date
          description: 종료일
          example: "2025-03-31"
        thumbnailUrl:
          type: string
          format: uri
          description: 썸네일 이미지 URL
          example: "https://cdn.kt-event.com/images/event-thumb-001.jpg"
        createdAt:
          type: string
          format: date-time
          description: 생성일시
          example: "2025-02-15T10:30:00Z"
      required:
        - eventId
        - eventName
        - objective
        - status
        - startDate
        - endDate
        - createdAt

    EventDetailResponse:
      type: object
      properties:
        eventId:
          type: string
          format: uuid
          description: 이벤트 ID
          example: "550e8400-e29b-41d4-a716-446655440000"
        userId:
          type: string
          format: uuid
          description: 사용자 ID
          example: "550e8400-e29b-41d4-a716-446655440001"
        storeId:
          type: string
          format: uuid
          description: 매장 ID
          example: "550e8400-e29b-41d4-a716-446655440002"
        eventName:
          type: string
          description: 이벤트명
          example: "봄맞이 20% 할인 이벤트"
        objective:
          type: string
          description: 이벤트 목적
          example: "신규 고객 유치"
        description:
          type: string
          description: 이벤트 설명
          example: "봄을 맞이하여 모든 상품 20% 할인 행사를 진행합니다."
        targetAudience:
          type: string
          description: 타겟 고객
          example: "20-30대 여성"
        promotionType:
          type: string
          description: 프로모션 유형
          example: "할인"
        discountRate:
          type: integer
          description: 할인율 (%)
          example: 20
        startDate:
          type: string
          format: date
          description: 시작일
          example: "2025-03-01"
        endDate:
          type: string
          format: date
          description: 종료일
          example: "2025-03-31"
        status:
          type: string
          enum: [DRAFT, PUBLISHED, ENDED]
          description: 이벤트 상태
          example: "PUBLISHED"
        selectedImageId:
          type: string
          format: uuid
          description: 선택된 이미지 ID
          example: "550e8400-e29b-41d4-a716-446655440006"
        selectedImageUrl:
          type: string
          format: uri
          description: 선택된 이미지 URL
          example: "https://cdn.kt-event.com/images/event-img-001.jpg"
        generatedImages:
          type: array
          description: 생성된 이미지 목록
          items:
            $ref: '#/components/schemas/GeneratedImage'
        channels:
          type: array
          description: 배포 채널 목록
          items:
            type: string
            example: "WEBSITE"
        aiRecommendations:
          type: array
          description: AI 추천 목록
          items:
            $ref: '#/components/schemas/AiRecommendation'
        createdAt:
          type: string
          format: date-time
          description: 생성일시
          example: "2025-02-15T10:30:00Z"
        updatedAt:
          type: string
          format: date-time
          description: 수정일시
          example: "2025-02-20T14:45:00Z"
      required:
        - eventId
        - userId
        - storeId
        - eventName
        - objective
        - status
        - startDate
        - endDate
        - createdAt

    GeneratedImage:
      type: object
      properties:
        imageId:
          type: string
          format: uuid
          description: 이미지 ID
          example: "550e8400-e29b-41d4-a716-446655440006"
        imageUrl:
          type: string
          format: uri
          description: 이미지 URL
          example: "https://cdn.kt-event.com/images/event-img-001.jpg"
        isSelected:
          type: boolean
          description: 선택 여부
          example: true
        createdAt:
          type: string
          format: date-time
          description: 생성일시
          example: "2025-02-16T11:00:00Z"
      required:
        - imageId
        - imageUrl
        - isSelected
        - createdAt

    AiRecommendation:
      type: object
      properties:
        recommendationId:
          type: string
          format: uuid
          description: 추천 ID
          example: "550e8400-e29b-41d4-a716-446655440007"
        eventName:
          type: string
          description: 추천 이벤트명
          example: "봄맞이 20% 할인 이벤트"
        description:
          type: string
          description: 추천 설명
          example: "봄을 맞이하여 모든 상품 20% 할인 행사를 진행합니다."
        promotionType:
          type: string
          description: 추천 프로모션 유형
          example: "할인"
        targetAudience:
          type: string
          description: 추천 타겟 고객
          example: "20-30대 여성"
        isSelected:
          type: boolean
          description: 선택 여부
          example: true
      required:
        - recommendationId
        - eventName
        - description
        - isSelected

    SelectObjectiveRequest:
      type: object
      properties:
        objective:
          type: string
          description: 이벤트 목적
          example: "신규 고객 유치"
      required:
        - objective

    EventCreatedResponse:
      type: object
      properties:
        eventId:
          type: string
          format: uuid
          description: 생성된 이벤트 ID
          example: "550e8400-e29b-41d4-a716-446655440000"
        status:
          type: string
          enum: [DRAFT]
          description: 이벤트 상태 (항상 DRAFT)
          example: "DRAFT"
        objective:
          type: string
          description: 선택된 이벤트 목적
          example: "신규 고객 유치"
        createdAt:
          type: string
          format: date-time
          description: 생성일시
          example: "2025-02-15T10:30:00Z"
      required:
        - eventId
        - status
        - objective
        - createdAt

    AiRecommendationRequest:
      type: object
      properties:
        storeInfo:
          type: object
          description: 매장 정보 (User Service에서 조회)
          properties:
            storeId:
              type: string
              format: uuid
              example: "550e8400-e29b-41d4-a716-446655440002"
            storeName:
              type: string
              example: "우진네 고깃집"
            category:
              type: string
              example: "음식점"
            description:
              type: string
              example: "신선한 한우를 제공하는 고깃집"
          required:
            - storeId
            - storeName
            - category
      required:
        - storeInfo

    JobAcceptedResponse:
      type: object
      properties:
        jobId:
          type: string
          format: uuid
          description: 생성된 Job ID
          example: "550e8400-e29b-41d4-a716-446655440005"
        status:
          type: string
          enum: [PENDING]
          description: Job 상태 (초기 상태는 PENDING)
          example: "PENDING"
        message:
          type: string
          description: 안내 메시지
          example: "AI 추천 생성 요청이 접수되었습니다. /jobs/{jobId}로 상태를 확인하세요."
      required:
        - jobId
        - status
        - message

    JobStatusResponse:
      type: object
      properties:
        jobId:
          type: string
          format: uuid
          description: Job ID
          example: "550e8400-e29b-41d4-a716-446655440005"
        jobType:
          type: string
          enum: [AI_RECOMMENDATION, IMAGE_GENERATION]
          description: Job 유형
          example: "AI_RECOMMENDATION"
        status:
          type: string
          enum: [PENDING, PROCESSING, COMPLETED, FAILED]
          description: Job 상태
          example: "COMPLETED"
        progress:
          type: integer
          minimum: 0
          maximum: 100
          description: 진행률 (%)
          example: 100
        resultKey:
          type: string
          description: Redis 결과 키 (COMPLETED 시)
          example: "ai:recommendation:550e8400-e29b-41d4-a716-446655440005"
        errorMessage:
          type: string
          description: 에러 메시지 (FAILED 시)
          example: "AI 서비스 연결 실패"
        createdAt:
          type: string
          format: date-time
          description: Job 생성일시
          example: "2025-02-15T10:31:00Z"
        completedAt:
          type: string
          format: date-time
          description: Job 완료일시
          example: "2025-02-15T10:31:30Z"
      required:
        - jobId
        - jobType
        - status
        - progress
        - createdAt

    SelectRecommendationRequest:
      type: object
      properties:
        recommendationId:
          type: string
          format: uuid
          description: 선택한 추천 ID
          example: "550e8400-e29b-41d4-a716-446655440007"
        customizations:
          type: object
          description: 커스터마이징 항목
          properties:
            eventName:
              type: string
              description: 수정된 이벤트명
              example: "봄맞이 특별 할인 이벤트"
            description:
              type: string
              description: 수정된 설명
              example: "봄을 맞이하여 전 메뉴 20% 할인"
            startDate:
              type: string
              format: date
              description: 수정된 시작일
              example: "2025-03-01"
            endDate:
              type: string
              format: date
              description: 수정된 종료일
              example: "2025-03-31"
            discountRate:
              type: integer
              description: 수정된 할인율
              example: 20
      required:
        - recommendationId

    ImageGenerationRequest:
      type: object
      properties:
        eventInfo:
          type: object
          description: 이벤트 정보 (이미지 생성에 필요한 정보)
          properties:
            eventName:
              type: string
              example: "봄맞이 20% 할인 이벤트"
            description:
              type: string
              example: "봄을 맞이하여 모든 상품 20% 할인 행사를 진행합니다."
            promotionType:
              type: string
              example: "할인"
          required:
            - eventName
            - description
        imageCount:
          type: integer
          minimum: 1
          maximum: 5
          description: 생성할 이미지 개수
          default: 3
          example: 3
      required:
        - eventInfo

    ImageEditRequest:
      type: object
      properties:
        editType:
          type: string
          enum: [TEXT_OVERLAY, COLOR_ADJUST, CROP, FILTER]
          description: 편집 유형
          example: "TEXT_OVERLAY"
        parameters:
          type: object
          description: 편집 파라미터 (편집 유형에 따라 다름)
          additionalProperties: true
          example:
            text: "20% 할인"
            fontSize: 48
            color: "#FF0000"
            position: "center"
      required:
        - editType
        - parameters

    ImageEditResponse:
      type: object
      properties:
        imageId:
          type: string
          format: uuid
          description: 편집된 이미지 ID
          example: "550e8400-e29b-41d4-a716-446655440008"
        imageUrl:
          type: string
          format: uri
          description: 편집된 이미지 URL
          example: "https://cdn.kt-event.com/images/event-img-001-edited.jpg"
        editedAt:
          type: string
          format: date-time
          description: 편집일시
          example: "2025-02-16T15:20:00Z"
      required:
        - imageId
        - imageUrl
        - editedAt

    SelectChannelsRequest:
      type: object
      properties:
        channels:
          type: array
          description: 배포 채널 목록
          items:
            type: string
            enum: [WEBSITE, KAKAO, INSTAGRAM, FACEBOOK, NAVER_BLOG]
          example: ["WEBSITE", "KAKAO", "INSTAGRAM"]
          minItems: 1
      required:
        - channels

    EventPublishedResponse:
      type: object
      properties:
        eventId:
          type: string
          format: uuid
          description: 이벤트 ID
          example: "550e8400-e29b-41d4-a716-446655440000"
        status:
          type: string
          enum: [PUBLISHED]
          description: 이벤트 상태 (항상 PUBLISHED)
          example: "PUBLISHED"
        publishedAt:
          type: string
          format: date-time
          description: 배포일시
          example: "2025-02-20T16:00:00Z"
        channels:
          type: array
          description: 배포된 채널 목록
          items:
            type: string
            example: "WEBSITE"
        distributionResults:
          type: array
          description: 채널별 배포 결과
          items:
            $ref: '#/components/schemas/DistributionResult'
      required:
        - eventId
        - status
        - publishedAt
        - channels
        - distributionResults

    DistributionResult:
      type: object
      properties:
        channel:
          type: string
          description: 채널명
          example: "WEBSITE"
        success:
          type: boolean
          description: 배포 성공 여부
          example: true
        url:
          type: string
          format: uri
          description: 배포된 URL
          example: "https://store.kt-event.com/event/550e8400-e29b-41d4-a716-446655440000"
        message:
          type: string
          description: 배포 결과 메시지
          example: "웹사이트에 성공적으로 배포되었습니다."
      required:
        - channel
        - success

    UpdateEventRequest:
      type: object
      properties:
        eventName:
          type: string
          description: 이벤트명
          example: "봄맞이 특별 할인 이벤트"
        description:
          type: string
          description: 이벤트 설명
          example: "봄을 맞이하여 전 메뉴 20% 할인"
        startDate:
          type: string
          format: date
          description: 시작일
          example: "2025-03-01"
        endDate:
          type: string
          format: date
          description: 종료일
          example: "2025-03-31"
        discountRate:
          type: integer
          description: 할인율
          example: 20

    EndEventRequest:
      type: object
      properties:
        reason:
          type: string
          description: 종료 사유
          example: "목표 달성으로 조기 종료"
      required:
        - reason

    PageInfo:
      type: object
      properties:
        page:
          type: integer
          description: 현재 페이지 번호
          example: 0
        size:
          type: integer
          description: 페이지 크기
          example: 20
        totalElements:
          type: integer
          description: 전체 요소 개수
          example: 45
        totalPages:
          type: integer
          description: 전체 페이지 개수
          example: 3
      required:
        - page
        - size
        - totalElements
        - totalPages

    ErrorResponse:
      type: object
      required:
        - code
        - message
        - timestamp
      properties:
        code:
          type: string
          description: 에러 코드
          example: "INVALID_REQUEST"
        message:
          type: string
          description: 에러 메시지
          example: "요청 파라미터가 올바르지 않습니다."
        timestamp:
          type: string
          format: date-time
          description: 에러 발생 시각
          example: "2025-02-15T10:30:00Z"
        details:
          type: array
          description: 상세 에러 정보 (선택 사항)
          items:
            type: string
          example: ["objective 필드는 필수입니다."]

  responses:
    BadRequest:
      description: 잘못된 요청
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: INVALID_REQUEST
            message: 요청 파라미터가 올바르지 않습니다.
            details:
              - "objective 필드는 필수입니다."
            timestamp: "2025-02-15T10:30:00Z"

    Unauthorized:
      description: 인증 실패
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: UNAUTHORIZED
            message: 인증에 실패했습니다.
            timestamp: "2025-02-15T10:30:00Z"

    Forbidden:
      description: 권한 없음
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: FORBIDDEN
            message: 해당 리소스에 접근할 권한이 없습니다.
            timestamp: "2025-02-15T10:30:00Z"

    NotFound:
      description: 리소스를 찾을 수 없음
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: NOT_FOUND
            message: 요청한 리소스를 찾을 수 없습니다.
            timestamp: "2025-02-15T10:30:00Z"

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