openapi: 3.0.3
info:
  title: Participation Service API
  description: |
    이벤트 참여 및 당첨자 관리 서비스 API
    - 이벤트 참여 등록
    - 참여자 목록 조회 및 관리
    - 당첨자 추첨 및 관리
  version: 1.0.0
  contact:
    name: Digital Garage Team
    email: support@kt-event-marketing.com

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

tags:
  - name: participation
    description: 이벤트 참여 관리
  - name: participant
    description: 참여자 조회 및 관리
  - name: winner
    description: 당첨자 추첨 및 관리

paths:
  /events/{eventId}/participate:
    post:
      tags:
        - participation
      summary: 이벤트 참여
      description: |
        고객이 이벤트에 참여합니다.
        - 중복 참여 검증 (전화번호 기반)
        - 이벤트 진행 상태 검증
        - Kafka 이벤트 발행 (ParticipantRegistered)
      operationId: participateEvent
      x-user-story: UFR-PART-010
      x-controller: ParticipationController
      parameters:
        - name: eventId
          in: path
          required: true
          description: 이벤트 ID
          schema:
            type: string
            example: "evt_20250123_001"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ParticipationRequest'
            examples:
              standard:
                summary: 일반 참여
                value:
                  name: "홍길동"
                  phoneNumber: "010-1234-5678"
                  email: "hong@example.com"
                  agreeMarketing: true
                  agreePrivacy: true
                  storeVisited: false
              storeVisit:
                summary: 매장 방문 참여
                value:
                  name: "김철수"
                  phoneNumber: "010-9876-5432"
                  email: "kim@example.com"
                  agreeMarketing: false
                  agreePrivacy: true
                  storeVisited: true
      responses:
        '201':
          description: 참여 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ParticipationResponse'
              examples:
                success:
                  summary: 참여 성공
                  value:
                    success: true
                    message: "이벤트 참여가 완료되었습니다"
                    data:
                      participantId: "prt_20250123_001"
                      eventId: "evt_20250123_001"
                      name: "홍길동"
                      phoneNumber: "010-1234-5678"
                      email: "hong@example.com"
                      participatedAt: "2025-01-23T10:30:00Z"
                      storeVisited: false
                      bonusEntries: 1
        '400':
          description: 잘못된 요청
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidPhone:
                  summary: 유효하지 않은 전화번호
                  value:
                    success: false
                    error:
                      code: "INVALID_PHONE_NUMBER"
                      message: "유효하지 않은 전화번호 형식입니다"
                duplicateParticipation:
                  summary: 중복 참여
                  value:
                    success: false
                    error:
                      code: "DUPLICATE_PARTICIPATION"
                      message: "이미 참여하신 이벤트입니다"
        '404':
          description: 이벤트를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  summary: 이벤트 없음
                  value:
                    success: false
                    error:
                      code: "EVENT_NOT_FOUND"
                      message: "이벤트를 찾을 수 없습니다"
        '409':
          description: 이벤트 진행 불가 상태
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notActive:
                  summary: 진행중이 아닌 이벤트
                  value:
                    success: false
                    error:
                      code: "EVENT_NOT_ACTIVE"
                      message: "현재 참여할 수 없는 이벤트입니다"

  /events/{eventId}/participants:
    get:
      tags:
        - participant
      summary: 참여자 목록 조회
      description: |
        이벤트의 참여자 목록을 조회합니다.
        - 페이징 지원
        - 참여일시 기준 정렬
        - 매장 방문 여부 필터링
      operationId: getParticipants
      x-user-story: UFR-PART-020
      x-controller: ParticipantController
      parameters:
        - name: eventId
          in: path
          required: true
          description: 이벤트 ID
          schema:
            type: string
            example: "evt_20250123_001"
        - name: page
          in: query
          description: 페이지 번호 (0부터 시작)
          schema:
            type: integer
            default: 0
            minimum: 0
            example: 0
        - name: size
          in: query
          description: 페이지 크기
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
            example: 20
        - name: storeVisited
          in: query
          description: 매장 방문 여부 필터
          schema:
            type: boolean
            example: true
      responses:
        '200':
          description: 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ParticipantListResponse'
              examples:
                success:
                  summary: 참여자 목록
                  value:
                    success: true
                    message: "참여자 목록을 조회했습니다"
                    data:
                      participants:
                        - participantId: "prt_20250123_001"
                          name: "홍길동"
                          phoneNumber: "010-1234-5678"
                          email: "hong@example.com"
                          participatedAt: "2025-01-23T10:30:00Z"
                          storeVisited: false
                          bonusEntries: 1
                          isWinner: false
                        - participantId: "prt_20250123_002"
                          name: "김철수"
                          phoneNumber: "010-9876-5432"
                          email: "kim@example.com"
                          participatedAt: "2025-01-23T11:15:00Z"
                          storeVisited: true
                          bonusEntries: 2
                          isWinner: true
                      pagination:
                        currentPage: 0
                        pageSize: 20
                        totalElements: 156
                        totalPages: 8
                        hasNext: true
                        hasPrevious: false
        '404':
          description: 이벤트를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /events/{eventId}/participants/{participantId}:
    get:
      tags:
        - participant
      summary: 참여자 상세 조회
      description: 특정 참여자의 상세 정보를 조회합니다.
      operationId: getParticipantDetail
      x-user-story: UFR-PART-020
      x-controller: ParticipantController
      parameters:
        - name: eventId
          in: path
          required: true
          description: 이벤트 ID
          schema:
            type: string
            example: "evt_20250123_001"
        - name: participantId
          in: path
          required: true
          description: 참여자 ID
          schema:
            type: string
            example: "prt_20250123_001"
      responses:
        '200':
          description: 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ParticipantDetailResponse'
              examples:
                success:
                  summary: 참여자 상세 정보
                  value:
                    success: true
                    message: "참여자 정보를 조회했습니다"
                    data:
                      participantId: "prt_20250123_001"
                      eventId: "evt_20250123_001"
                      name: "홍길동"
                      phoneNumber: "010-1234-5678"
                      email: "hong@example.com"
                      participatedAt: "2025-01-23T10:30:00Z"
                      storeVisited: false
                      bonusEntries: 1
                      agreeMarketing: true
                      agreePrivacy: true
                      isWinner: false
                      winnerInfo: null
        '404':
          description: 참여자를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  summary: 참여자 없음
                  value:
                    success: false
                    error:
                      code: "PARTICIPANT_NOT_FOUND"
                      message: "참여자를 찾을 수 없습니다"

  /events/{eventId}/draw-winners:
    post:
      tags:
        - winner
      summary: 당첨자 추첨
      description: |
        이벤트 당첨자를 추첨합니다.
        - 랜덤 추첨 알고리즘 사용
        - 매장 방문 보너스 가중치 적용
        - 중복 당첨 방지
      operationId: drawWinners
      x-user-story: UFR-PART-030
      x-controller: WinnerController
      parameters:
        - name: eventId
          in: path
          required: true
          description: 이벤트 ID
          schema:
            type: string
            example: "evt_20250123_001"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DrawWinnersRequest'
            examples:
              standard:
                summary: 일반 추첨
                value:
                  winnerCount: 10
                  applyStoreVisitBonus: true
      responses:
        '200':
          description: 추첨 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DrawWinnersResponse'
              examples:
                success:
                  summary: 추첨 완료
                  value:
                    success: true
                    message: "당첨자 추첨이 완료되었습니다"
                    data:
                      eventId: "evt_20250123_001"
                      totalParticipants: 156
                      winnerCount: 10
                      drawnAt: "2025-01-24T15:00:00Z"
                      winners:
                        - participantId: "prt_20250123_002"
                          name: "김철수"
                          phoneNumber: "010-9876-5432"
                          rank: 1
                        - participantId: "prt_20250123_045"
                          name: "이영희"
                          phoneNumber: "010-5555-1234"
                          rank: 2
                        - participantId: "prt_20250123_089"
                          name: "박민수"
                          phoneNumber: "010-7777-8888"
                          rank: 3
        '400':
          description: 잘못된 요청
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidCount:
                  summary: 잘못된 당첨자 수
                  value:
                    success: false
                    error:
                      code: "INVALID_WINNER_COUNT"
                      message: "당첨자 수가 참여자 수보다 많습니다"
        '404':
          description: 이벤트를 찾을 수 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 이미 추첨 완료
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                alreadyDrawn:
                  summary: 추첨 완료 상태
                  value:
                    success: false
                    error:
                      code: "ALREADY_DRAWN"
                      message: "이미 당첨자 추첨이 완료되었습니다"

  /events/{eventId}/winners:
    get:
      tags:
        - winner
      summary: 당첨자 목록 조회
      description: |
        이벤트의 당첨자 목록을 조회합니다.
        - 당첨 순위별 정렬
        - 페이징 지원
      operationId: getWinners
      x-user-story: UFR-PART-030
      x-controller: WinnerController
      parameters:
        - name: eventId
          in: path
          required: true
          description: 이벤트 ID
          schema:
            type: string
            example: "evt_20250123_001"
        - name: page
          in: query
          description: 페이지 번호 (0부터 시작)
          schema:
            type: integer
            default: 0
            minimum: 0
            example: 0
        - name: size
          in: query
          description: 페이지 크기
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
            example: 20
      responses:
        '200':
          description: 조회 성공
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WinnerListResponse'
              examples:
                success:
                  summary: 당첨자 목록
                  value:
                    success: true
                    message: "당첨자 목록을 조회했습니다"
                    data:
                      eventId: "evt_20250123_001"
                      drawnAt: "2025-01-24T15:00:00Z"
                      totalWinners: 10
                      winners:
                        - participantId: "prt_20250123_002"
                          name: "김철수"
                          phoneNumber: "010-9876-5432"
                          email: "kim@example.com"
                          rank: 1
                          wonAt: "2025-01-24T15:00:00Z"
                        - participantId: "prt_20250123_045"
                          name: "이영희"
                          phoneNumber: "010-5555-1234"
                          email: "lee@example.com"
                          rank: 2
                          wonAt: "2025-01-24T15:00:00Z"
                      pagination:
                        currentPage: 0
                        pageSize: 20
                        totalElements: 10
                        totalPages: 1
                        hasNext: false
                        hasPrevious: false
        '404':
          description: 이벤트를 찾을 수 없음 또는 당첨자가 없음
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                noWinners:
                  summary: 당첨자 없음
                  value:
                    success: false
                    error:
                      code: "NO_WINNERS_YET"
                      message: "아직 당첨자 추첨이 진행되지 않았습니다"

components:
  schemas:
    ParticipationRequest:
      type: object
      required:
        - name
        - phoneNumber
        - agreePrivacy
      properties:
        name:
          type: string
          description: 참여자 이름
          minLength: 2
          maxLength: 50
          example: "홍길동"
        phoneNumber:
          type: string
          description: 참여자 전화번호 (하이픈 포함)
          pattern: '^\d{3}-\d{3,4}-\d{4}$'
          example: "010-1234-5678"
        email:
          type: string
          format: email
          description: 참여자 이메일
          example: "hong@example.com"
        agreeMarketing:
          type: boolean
          description: 마케팅 정보 수신 동의
          default: false
          example: true
        agreePrivacy:
          type: boolean
          description: 개인정보 수집 및 이용 동의 (필수)
          example: true
        storeVisited:
          type: boolean
          description: 매장 방문 여부
          default: false
          example: false

    ParticipationResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "이벤트 참여가 완료되었습니다"
        data:
          $ref: '#/components/schemas/ParticipantInfo'

    ParticipantInfo:
      type: object
      properties:
        participantId:
          type: string
          description: 참여자 ID
          example: "prt_20250123_001"
        eventId:
          type: string
          description: 이벤트 ID
          example: "evt_20250123_001"
        name:
          type: string
          description: 참여자 이름
          example: "홍길동"
        phoneNumber:
          type: string
          description: 참여자 전화번호
          example: "010-1234-5678"
        email:
          type: string
          description: 참여자 이메일
          example: "hong@example.com"
        participatedAt:
          type: string
          format: date-time
          description: 참여 일시
          example: "2025-01-23T10:30:00Z"
        storeVisited:
          type: boolean
          description: 매장 방문 여부
          example: false
        bonusEntries:
          type: integer
          description: 보너스 응모권 수 (매장 방문 시 +1)
          minimum: 1
          example: 1
        isWinner:
          type: boolean
          description: 당첨 여부
          example: false

    ParticipantDetailInfo:
      allOf:
        - $ref: '#/components/schemas/ParticipantInfo'
        - type: object
          properties:
            agreeMarketing:
              type: boolean
              description: 마케팅 정보 수신 동의
              example: true
            agreePrivacy:
              type: boolean
              description: 개인정보 수집 및 이용 동의
              example: true
            winnerInfo:
              $ref: '#/components/schemas/WinnerInfo'
              nullable: true

    ParticipantListResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "참여자 목록을 조회했습니다"
        data:
          type: object
          properties:
            participants:
              type: array
              items:
                $ref: '#/components/schemas/ParticipantInfo'
            pagination:
              $ref: '#/components/schemas/Pagination'

    ParticipantDetailResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "참여자 정보를 조회했습니다"
        data:
          $ref: '#/components/schemas/ParticipantDetailInfo'

    DrawWinnersRequest:
      type: object
      required:
        - winnerCount
      properties:
        winnerCount:
          type: integer
          description: 당첨자 수
          minimum: 1
          example: 10
        applyStoreVisitBonus:
          type: boolean
          description: 매장 방문 보너스 적용 여부
          default: true
          example: true

    DrawWinnersResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "당첨자 추첨이 완료되었습니다"
        data:
          type: object
          properties:
            eventId:
              type: string
              description: 이벤트 ID
              example: "evt_20250123_001"
            totalParticipants:
              type: integer
              description: 전체 참여자 수
              example: 156
            winnerCount:
              type: integer
              description: 당첨자 수
              example: 10
            drawnAt:
              type: string
              format: date-time
              description: 추첨 일시
              example: "2025-01-24T15:00:00Z"
            winners:
              type: array
              description: 당첨자 목록
              items:
                $ref: '#/components/schemas/WinnerSummary'

    WinnerSummary:
      type: object
      properties:
        participantId:
          type: string
          description: 참여자 ID
          example: "prt_20250123_002"
        name:
          type: string
          description: 당첨자 이름
          example: "김철수"
        phoneNumber:
          type: string
          description: 당첨자 전화번호
          example: "010-9876-5432"
        rank:
          type: integer
          description: 당첨 순위
          minimum: 1
          example: 1

    WinnerInfo:
      type: object
      properties:
        participantId:
          type: string
          description: 참여자 ID
          example: "prt_20250123_002"
        name:
          type: string
          description: 당첨자 이름
          example: "김철수"
        phoneNumber:
          type: string
          description: 당첨자 전화번호
          example: "010-9876-5432"
        email:
          type: string
          description: 당첨자 이메일
          example: "kim@example.com"
        rank:
          type: integer
          description: 당첨 순위
          minimum: 1
          example: 1
        wonAt:
          type: string
          format: date-time
          description: 당첨 일시
          example: "2025-01-24T15:00:00Z"

    WinnerListResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "당첨자 목록을 조회했습니다"
        data:
          type: object
          properties:
            eventId:
              type: string
              description: 이벤트 ID
              example: "evt_20250123_001"
            drawnAt:
              type: string
              format: date-time
              description: 추첨 일시
              example: "2025-01-24T15:00:00Z"
            totalWinners:
              type: integer
              description: 전체 당첨자 수
              example: 10
            winners:
              type: array
              items:
                $ref: '#/components/schemas/WinnerInfo'
            pagination:
              $ref: '#/components/schemas/Pagination'

    Pagination:
      type: object
      properties:
        currentPage:
          type: integer
          description: 현재 페이지 번호 (0부터 시작)
          minimum: 0
          example: 0
        pageSize:
          type: integer
          description: 페이지 크기
          minimum: 1
          example: 20
        totalElements:
          type: integer
          description: 전체 요소 수
          minimum: 0
          example: 156
        totalPages:
          type: integer
          description: 전체 페이지 수
          minimum: 0
          example: 8
        hasNext:
          type: boolean
          description: 다음 페이지 존재 여부
          example: true
        hasPrevious:
          type: boolean
          description: 이전 페이지 존재 여부
          example: false

    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              description: 에러 코드
              example: "DUPLICATE_PARTICIPATION"
            message:
              type: string
              description: 에러 메시지
              example: "이미 참여하신 이벤트입니다"
            details:
              type: object
              description: 추가 에러 상세 정보
              additionalProperties: true
              nullable: true
