This commit is contained in:
cherry2250
2025-10-22 16:37:32 +09:00
parent fb63850f9d
commit 65a9f8161b
8 changed files with 5992 additions and 0 deletions
@@ -0,0 +1,942 @@
openapi: 3.0.3
info:
title: Analytics Service API
description: |
KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Analytics Service API
이벤트 성과 분석 및 통합 대시보드를 제공하는 서비스입니다.
**주요 기능:**
- 실시간 성과 분석 대시보드 (UFR-ANAL-010)
- 채널별 성과 추적
- ROI 계산 및 비교 분석
- 참여자 프로필 분석
**데이터 소스:**
- Participation Service: 참여자 데이터
- Distribution Service: 채널별 노출 수
- 외부 API: 우리동네TV, 지니TV, SNS 통계
- POS 시스템: 매출 데이터 (연동 시)
version: 1.0.0
contact:
name: Analytics Service Team
email: analytics@kt-event-service.com
servers:
- url: https://api.kt-event-service.com/analytics/v1
description: Production Server
- url: https://dev-api.kt-event-service.com/analytics/v1
description: Development Server
- url: http://localhost:8084/api
description: Local Development Server
tags:
- name: Analytics
description: 성과 분석 대시보드 API
- name: Health
description: 서비스 헬스 체크
paths:
/health:
get:
tags:
- Health
summary: 헬스 체크
description: Analytics Service의 상태를 확인합니다.
operationId: healthCheck
responses:
'200':
description: 서비스 정상
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: "UP"
service:
type: string
example: "analytics-service"
timestamp:
type: string
format: date-time
example: "2025-10-22T10:00:00Z"
/events/{eventId}/analytics:
get:
tags:
- Analytics
summary: 이벤트 성과 분석 대시보드 조회
description: |
특정 이벤트의 실시간 성과 분석 데이터를 조회합니다.
**유저스토리:** UFR-ANAL-010 - 성과 분석 대시보드
**주요 기능:**
- 4개 요약 카드 (참여자 수, 노출 수, ROI, 매출 증가율)
- 채널별 성과 분석
- 시간대별 참여 추이
- 참여자 프로필 분석
- 비교 분석 (업종 평균, 이전 이벤트)
**캐싱 전략:**
- Redis Cache-Aside 패턴
- TTL: 300초 (5분)
- Cache HIT 시 응답 시간: 약 0.5초
- Cache MISS 시 응답 시간: 약 3초
**데이터 업데이트:**
- 실시간 업데이트: Kafka 이벤트 구독
- EventCreated: 통계 초기화
- ParticipantRegistered: 참여자 수 증가
- DistributionCompleted: 배포 통계 업데이트
operationId: getEventAnalytics
security:
- BearerAuth: []
parameters:
- name: eventId
in: path
required: true
description: 이벤트 ID (UUID)
schema:
type: string
format: uuid
example: "550e8400-e29b-41d4-a716-446655440000"
responses:
'200':
description: 대시보드 데이터 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/DashboardResponse'
examples:
success:
summary: 성공 응답 예시
value:
eventId: "550e8400-e29b-41d4-a716-446655440000"
storeId: "660e8400-e29b-41d4-a716-446655440001"
eventTitle: "신규 고객 환영 이벤트"
summaryCards:
totalParticipants:
count: 1234
targetGoal: 1000
achievementRate: 123.4
dailyChange: 150
totalViews:
count: 17200
yesterdayChange: 5.2
channelBreakdown:
- channel: "우리동네TV"
views: 5000
- channel: "지니TV"
views: 10000
- channel: "Instagram"
views: 2000
- channel: "Naver Blog"
views: 200
roi:
value: 250.0
industryAverage: 180.0
comparisonRate: 138.9
totalCost: 1000000
totalRevenue: 3500000
breakEvenStatus: "ACHIEVED"
salesGrowth:
rate: 15.2
beforeEventSales: 5000000
afterEventSales: 5760000
periodComparison: "이벤트 전후 7일 비교"
channelPerformance:
- channel: "우리동네TV"
views: 5000
participants: 400
conversionRate: 8.0
costPerAcquisition: 2500
status: "SUCCESS"
- channel: "지니TV"
views: 10000
participants: 500
conversionRate: 5.0
costPerAcquisition: 4000
status: "SUCCESS"
- channel: "Instagram"
views: 2000
participants: 200
conversionRate: 10.0
costPerAcquisition: 1000
status: "SUCCESS"
- channel: "Naver Blog"
views: 200
participants: 100
conversionRate: 50.0
costPerAcquisition: 500
status: "SUCCESS"
- channel: "Kakao Channel"
views: 0
participants: 34
conversionRate: 0
costPerAcquisition: 0
status: "SUCCESS"
participationTrend:
timeUnit: "DAILY"
dataPoints:
- timestamp: "2025-10-15T00:00:00Z"
participantCount: 100
- timestamp: "2025-10-16T00:00:00Z"
participantCount: 250
- timestamp: "2025-10-17T00:00:00Z"
participantCount: 400
- timestamp: "2025-10-18T00:00:00Z"
participantCount: 600
- timestamp: "2025-10-19T00:00:00Z"
participantCount: 850
- timestamp: "2025-10-20T00:00:00Z"
participantCount: 1050
- timestamp: "2025-10-21T00:00:00Z"
participantCount: 1234
peakTime:
timestamp: "2025-10-20T18:00:00Z"
participantCount: 200
roiAnalysis:
totalCost:
prizeCost: 500000
channelCosts:
- channel: "우리동네TV"
cost: 100000
- channel: "지니TV"
cost: 200000
- channel: "Instagram"
cost: 50000
- channel: "Naver Blog"
cost: 50000
- channel: "Kakao Channel"
cost: 0
otherCosts: 100000
total: 1000000
expectedRevenue:
salesIncrease: 760000
newCustomerLTV: 2740000
total: 3500000
roiCalculation:
formula: "(수익 - 비용) / 비용 × 100"
roi: 250.0
breakEvenPoint:
required: 1000000
achieved: 3500000
status: "ACHIEVED"
participantProfile:
ageDistribution:
- ageGroup: "10대"
count: 50
percentage: 4.1
- ageGroup: "20대"
count: 300
percentage: 24.3
- ageGroup: "30대"
count: 450
percentage: 36.5
- ageGroup: "40대"
count: 300
percentage: 24.3
- ageGroup: "50대 이상"
count: 134
percentage: 10.8
genderDistribution:
- gender: "남성"
count: 600
percentage: 48.6
- gender: "여성"
count: 634
percentage: 51.4
regionDistribution:
- region: "서울"
count: 500
percentage: 40.5
- region: "경기"
count: 400
percentage: 32.4
- region: "기타"
count: 334
percentage: 27.1
timeDistribution:
- timeSlot: "오전 (06:00-12:00)"
count: 200
percentage: 16.2
- timeSlot: "오후 (12:00-18:00)"
count: 500
percentage: 40.5
- timeSlot: "저녁 (18:00-24:00)"
count: 500
percentage: 40.5
- timeSlot: "새벽 (00:00-06:00)"
count: 34
percentage: 2.8
comparativeAnalysis:
industryComparison:
- metric: "참여율"
myValue: 7.2
industryAverage: 5.5
percentageDifference: 30.9
- metric: "ROI"
myValue: 250.0
industryAverage: 180.0
percentageDifference: 38.9
- metric: "전환율"
myValue: 8.5
industryAverage: 6.0
percentageDifference: 41.7
previousEventComparison:
- metric: "참여자 수"
currentValue: 1234
previousBest: 1000
improvementRate: 23.4
- metric: "ROI"
currentValue: 250.0
previousBest: 200.0
improvementRate: 25.0
- metric: "매출 증가율"
currentValue: 15.2
previousBest: 12.0
improvementRate: 26.7
lastUpdated: "2025-10-22T10:30:00Z"
cacheStatus: "HIT"
'400':
description: 잘못된 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
invalidEventId:
summary: 잘못된 이벤트 ID
value:
error: "INVALID_REQUEST"
message: "유효하지 않은 이벤트 ID 형식입니다."
timestamp: "2025-10-22T10:00:00Z"
'401':
description: 인증 실패
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
unauthorized:
summary: 인증되지 않은 요청
value:
error: "UNAUTHORIZED"
message: "인증이 필요합니다."
timestamp: "2025-10-22T10:00:00Z"
'403':
description: 권한 없음
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
forbidden:
summary: 권한 없음
value:
error: "FORBIDDEN"
message: "해당 이벤트의 통계를 조회할 권한이 없습니다."
timestamp: "2025-10-22T10:00:00Z"
'404':
description: 이벤트를 찾을 수 없음
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
notFound:
summary: 이벤트 미존재
value:
error: "EVENT_NOT_FOUND"
message: "해당 이벤트를 찾을 수 없습니다."
timestamp: "2025-10-22T10:00:00Z"
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
internalError:
summary: 서버 오류
value:
error: "INTERNAL_SERVER_ERROR"
message: "서버 내부 오류가 발생했습니다."
timestamp: "2025-10-22T10:00:00Z"
'503':
description: 외부 API 서비스 이용 불가
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
externalServiceUnavailable:
summary: 외부 서비스 장애
value:
error: "EXTERNAL_SERVICE_UNAVAILABLE"
message: "일부 채널 데이터를 불러올 수 없습니다. Fallback 데이터를 사용합니다."
timestamp: "2025-10-22T10:00:00Z"
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT 토큰 기반 인증. Authorization 헤더에 "Bearer {token}" 형식으로 전달합니다.
schemas:
DashboardResponse:
type: object
required:
- eventId
- storeId
- eventTitle
- summaryCards
- channelPerformance
- participationTrend
- roiAnalysis
- participantProfile
- comparativeAnalysis
- lastUpdated
properties:
eventId:
type: string
format: uuid
description: 이벤트 ID
example: "550e8400-e29b-41d4-a716-446655440000"
storeId:
type: string
format: uuid
description: 매장 ID
example: "660e8400-e29b-41d4-a716-446655440001"
eventTitle:
type: string
description: 이벤트 제목
example: "신규 고객 환영 이벤트"
summaryCards:
$ref: '#/components/schemas/SummaryCards'
channelPerformance:
type: array
description: 채널별 성과 분석
items:
$ref: '#/components/schemas/ChannelPerformance'
participationTrend:
$ref: '#/components/schemas/ParticipationTrend'
roiAnalysis:
$ref: '#/components/schemas/ROIAnalysis'
participantProfile:
$ref: '#/components/schemas/ParticipantProfile'
comparativeAnalysis:
$ref: '#/components/schemas/ComparativeAnalysis'
lastUpdated:
type: string
format: date-time
description: 마지막 업데이트 시각
example: "2025-10-22T10:30:00Z"
cacheStatus:
type: string
enum: [HIT, MISS]
description: 캐시 상태 (HIT/MISS)
example: "HIT"
SummaryCards:
type: object
description: 4개 요약 카드
required:
- totalParticipants
- totalViews
- roi
- salesGrowth
properties:
totalParticipants:
$ref: '#/components/schemas/TotalParticipantsCard'
totalViews:
$ref: '#/components/schemas/TotalViewsCard'
roi:
$ref: '#/components/schemas/ROICard'
salesGrowth:
$ref: '#/components/schemas/SalesGrowthCard'
TotalParticipantsCard:
type: object
description: 총 참여자 수 카드
required:
- count
- targetGoal
- achievementRate
properties:
count:
type: integer
description: 현재 참여자 수
example: 1234
targetGoal:
type: integer
description: 목표 참여자 수
example: 1000
achievementRate:
type: number
format: float
description: 목표 대비 달성률 (%)
example: 123.4
dailyChange:
type: integer
description: 전일 대비 증가 수
example: 150
TotalViewsCard:
type: object
description: 총 노출 수 카드
required:
- count
- yesterdayChange
properties:
count:
type: integer
description: 총 노출 수 (채널별 노출 합계)
example: 17200
yesterdayChange:
type: number
format: float
description: 전일 대비 증감률 (%)
example: 5.2
channelBreakdown:
type: array
description: 채널별 노출 수 분포
items:
type: object
properties:
channel:
type: string
description: 채널명
example: "우리동네TV"
views:
type: integer
description: 노출 수
example: 5000
ROICard:
type: object
description: 예상 투자 대비 수익률 카드
required:
- value
- industryAverage
- comparisonRate
- totalCost
- totalRevenue
- breakEvenStatus
properties:
value:
type: number
format: float
description: 실시간 ROI (%)
example: 250.0
industryAverage:
type: number
format: float
description: 업종 평균 ROI (%)
example: 180.0
comparisonRate:
type: number
format: float
description: 업종 평균 대비 비율 (%)
example: 138.9
totalCost:
type: integer
description: 총 비용 (원)
example: 1000000
totalRevenue:
type: integer
description: 총 수익 (원)
example: 3500000
breakEvenStatus:
type: string
enum: [ACHIEVED, NOT_ACHIEVED]
description: 손익분기점 달성 여부
example: "ACHIEVED"
SalesGrowthCard:
type: object
description: 매출 증가율 카드
required:
- rate
properties:
rate:
type: number
format: float
description: 매출 증가율 (%)
example: 15.2
beforeEventSales:
type: integer
description: 이벤트 전 매출 (원)
example: 5000000
afterEventSales:
type: integer
description: 이벤트 후 매출 (원)
example: 5760000
periodComparison:
type: string
description: 비교 기간 설명
example: "이벤트 전후 7일 비교"
ChannelPerformance:
type: object
description: 채널별 성과 분석
required:
- channel
- views
- participants
- conversionRate
- costPerAcquisition
- status
properties:
channel:
type: string
description: 채널명
example: "우리동네TV"
views:
type: integer
description: 노출 수
example: 5000
participants:
type: integer
description: 참여자 수
example: 400
conversionRate:
type: number
format: float
description: 전환율 (%)
example: 8.0
costPerAcquisition:
type: integer
description: 비용 대비 효율 (CPA, 원)
example: 2500
status:
type: string
enum: [SUCCESS, FAILED, PENDING]
description: 배포 상태
example: "SUCCESS"
ParticipationTrend:
type: object
description: 시간대별 참여 추이
required:
- timeUnit
- dataPoints
properties:
timeUnit:
type: string
enum: [HOURLY, DAILY]
description: 시간 단위 (시간별/일별)
example: "DAILY"
dataPoints:
type: array
description: 시간대별 데이터 포인트
items:
type: object
properties:
timestamp:
type: string
format: date-time
description: 시각
example: "2025-10-15T00:00:00Z"
participantCount:
type: integer
description: 참여자 수
example: 100
peakTime:
type: object
description: 피크 시간대
properties:
timestamp:
type: string
format: date-time
description: 피크 시각
example: "2025-10-20T18:00:00Z"
participantCount:
type: integer
description: 피크 참여자 수
example: 200
ROIAnalysis:
type: object
description: 투자 대비 수익률 상세 분석
required:
- totalCost
- expectedRevenue
- roiCalculation
- breakEvenPoint
properties:
totalCost:
$ref: '#/components/schemas/TotalCost'
expectedRevenue:
$ref: '#/components/schemas/ExpectedRevenue'
roiCalculation:
$ref: '#/components/schemas/ROICalculation'
breakEvenPoint:
$ref: '#/components/schemas/BreakEvenPoint'
TotalCost:
type: object
description: 총 비용 산출
required:
- prizeCost
- channelCosts
- otherCosts
- total
properties:
prizeCost:
type: integer
description: 경품 비용 (원)
example: 500000
channelCosts:
type: array
description: 채널별 플랫폼 비용
items:
type: object
properties:
channel:
type: string
description: 채널명
example: "우리동네TV"
cost:
type: integer
description: 비용 (원)
example: 100000
otherCosts:
type: integer
description: 기타 비용 (원)
example: 100000
total:
type: integer
description: 총 비용 (원)
example: 1000000
ExpectedRevenue:
type: object
description: 예상 수익 산출
required:
- salesIncrease
- newCustomerLTV
- total
properties:
salesIncrease:
type: integer
description: 매출 증가액 (원)
example: 760000
newCustomerLTV:
type: integer
description: 신규 고객 LTV (원)
example: 2740000
total:
type: integer
description: 총 예상 수익 (원)
example: 3500000
ROICalculation:
type: object
description: ROI 계산
required:
- formula
- roi
properties:
formula:
type: string
description: ROI 계산 공식
example: "(수익 - 비용) / 비용 × 100"
roi:
type: number
format: float
description: ROI (%)
example: 250.0
BreakEvenPoint:
type: object
description: 손익분기점
required:
- required
- achieved
- status
properties:
required:
type: integer
description: 손익분기점 (원)
example: 1000000
achieved:
type: integer
description: 달성 금액 (원)
example: 3500000
status:
type: string
enum: [ACHIEVED, NOT_ACHIEVED]
description: 달성 여부
example: "ACHIEVED"
ParticipantProfile:
type: object
description: 참여자 프로필 분석
required:
- ageDistribution
- genderDistribution
- regionDistribution
- timeDistribution
properties:
ageDistribution:
type: array
description: 연령대별 분포
items:
type: object
properties:
ageGroup:
type: string
description: 연령대
example: "20대"
count:
type: integer
description: 인원 수
example: 300
percentage:
type: number
format: float
description: 비율 (%)
example: 24.3
genderDistribution:
type: array
description: 성별 분포
items:
type: object
properties:
gender:
type: string
description: 성별
example: "남성"
count:
type: integer
description: 인원 수
example: 600
percentage:
type: number
format: float
description: 비율 (%)
example: 48.6
regionDistribution:
type: array
description: 지역별 분포
items:
type: object
properties:
region:
type: string
description: 지역
example: "서울"
count:
type: integer
description: 인원 수
example: 500
percentage:
type: number
format: float
description: 비율 (%)
example: 40.5
timeDistribution:
type: array
description: 참여 시간대 분석
items:
type: object
properties:
timeSlot:
type: string
description: 시간대
example: "오전 (06:00-12:00)"
count:
type: integer
description: 참여자 수
example: 200
percentage:
type: number
format: float
description: 비율 (%)
example: 16.2
ComparativeAnalysis:
type: object
description: 비교 분석
required:
- industryComparison
- previousEventComparison
properties:
industryComparison:
type: array
description: 업종 평균과 비교
items:
type: object
properties:
metric:
type: string
description: 지표명
example: "참여율"
myValue:
type: number
format: float
description: 내 값
example: 7.2
industryAverage:
type: number
format: float
description: 업종 평균
example: 5.5
percentageDifference:
type: number
format: float
description: 차이율 (%)
example: 30.9
previousEventComparison:
type: array
description: 내 이전 이벤트와 비교
items:
type: object
properties:
metric:
type: string
description: 지표명
example: "참여자 수"
currentValue:
type: number
format: float
description: 현재 값
example: 1234
previousBest:
type: number
format: float
description: 이전 최고 기록
example: 1000
improvementRate:
type: number
format: float
description: 개선율 (%)
example: 23.4
ErrorResponse:
type: object
description: 에러 응답
required:
- error
- message
- timestamp
properties:
error:
type: string
description: 에러 코드
example: "INVALID_REQUEST"
message:
type: string
description: 에러 메시지
example: "유효하지 않은 요청입니다."
timestamp:
type: string
format: date-time
description: 에러 발생 시각
example: "2025-10-22T10:00:00Z"