kt-event-marketing-fe/design/backend/api/participation-service-api.yaml
cherry2250 3f6e005026 초기 프로젝트 설정 및 설계 문서 추가
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 10:10:16 +09:00

821 lines
25 KiB
YAML

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