mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 10:16:25 +00:00
Merge pull request #12 from ktds-dg0501/feature/distribution
Merge feature/distribution into develop
This commit is contained in:
commit
0117b64e0e
@ -11,10 +11,10 @@ info:
|
|||||||
- Retry 패턴 및 Fallback 처리
|
- Retry 패턴 및 Fallback 처리
|
||||||
|
|
||||||
## 배포 채널
|
## 배포 채널
|
||||||
- **우리동네TV**: 영상 콘텐츠 업로드
|
- **우리동네TV** (URIDONGNETV): 영상 콘텐츠 업로드
|
||||||
- **링고비즈**: 연결음 업데이트
|
- **링고비즈** (RINGOBIZ): 연결음 업데이트
|
||||||
- **지니TV**: 광고 등록
|
- **지니TV** (GINITV): 광고 등록
|
||||||
- **SNS**: Instagram, Naver Blog, Kakao Channel
|
- **SNS**: Instagram (INSTAGRAM), Naver Blog (NAVER), Kakao Channel (KAKAO)
|
||||||
|
|
||||||
## Resilience 패턴
|
## Resilience 패턴
|
||||||
- Circuit Breaker: 채널별 독립적 장애 격리
|
- Circuit Breaker: 채널별 독립적 장애 격리
|
||||||
@ -79,23 +79,21 @@ paths:
|
|||||||
summary: 다중 채널 배포 예시
|
summary: 다중 채널 배포 예시
|
||||||
value:
|
value:
|
||||||
eventId: "evt-12345"
|
eventId: "evt-12345"
|
||||||
|
title: "신규 고객 환영 이벤트"
|
||||||
|
description: "신규 고객님을 위한 특별 할인 이벤트"
|
||||||
|
imageUrl: "https://cdn.example.com/images/event-main.jpg"
|
||||||
channels:
|
channels:
|
||||||
- type: "WOORIDONGNE_TV"
|
- "URIDONGNETV"
|
||||||
config:
|
- "INSTAGRAM"
|
||||||
|
- "NAVER"
|
||||||
|
channelSettings:
|
||||||
|
URIDONGNETV:
|
||||||
radius: "1km"
|
radius: "1km"
|
||||||
timeSlots:
|
timeSlot: "evening"
|
||||||
- "weekday_evening"
|
INSTAGRAM:
|
||||||
- "weekend_lunch"
|
scheduledTime: "2025-11-01T10:00:00"
|
||||||
- type: "INSTAGRAM"
|
NAVER:
|
||||||
config:
|
scheduledTime: "2025-11-01T10:30:00"
|
||||||
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:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: 배포 완료
|
description: 배포 완료
|
||||||
@ -107,25 +105,29 @@ paths:
|
|||||||
allSuccess:
|
allSuccess:
|
||||||
summary: 모든 채널 배포 성공
|
summary: 모든 채널 배포 성공
|
||||||
value:
|
value:
|
||||||
distributionId: "dist-12345"
|
|
||||||
eventId: "evt-12345"
|
eventId: "evt-12345"
|
||||||
status: "COMPLETED"
|
success: true
|
||||||
completedAt: "2025-11-01T09:00:00Z"
|
channelResults:
|
||||||
results:
|
- channel: "URIDONGNETV"
|
||||||
- channel: "WOORIDONGNE_TV"
|
success: true
|
||||||
status: "SUCCESS"
|
|
||||||
distributionId: "wtv-uuid-12345"
|
distributionId: "wtv-uuid-12345"
|
||||||
estimatedViews: 1000
|
estimatedReach: 1000
|
||||||
message: "배포 완료"
|
executionTimeMs: 234
|
||||||
- channel: "INSTAGRAM"
|
- channel: "INSTAGRAM"
|
||||||
status: "SUCCESS"
|
success: true
|
||||||
postUrl: "https://instagram.com/p/generated-post-id"
|
distributionId: "ig-uuid-12345"
|
||||||
postId: "ig-post-12345"
|
estimatedReach: 500
|
||||||
message: "게시 완료"
|
executionTimeMs: 456
|
||||||
- channel: "NAVER_BLOG"
|
- channel: "NAVER"
|
||||||
status: "SUCCESS"
|
success: true
|
||||||
postUrl: "https://blog.naver.com/store123/generated-post"
|
distributionId: "naver-uuid-12345"
|
||||||
message: "게시 완료"
|
estimatedReach: 300
|
||||||
|
executionTimeMs: 123
|
||||||
|
successCount: 3
|
||||||
|
failureCount: 0
|
||||||
|
completedAt: "2025-11-01T09:00:00"
|
||||||
|
totalExecutionTimeMs: 1234
|
||||||
|
message: "배포가 성공적으로 완료되었습니다"
|
||||||
'400':
|
'400':
|
||||||
description: 잘못된 요청
|
description: 잘못된 요청
|
||||||
content:
|
content:
|
||||||
@ -217,67 +219,77 @@ paths:
|
|||||||
value:
|
value:
|
||||||
eventId: "evt-12345"
|
eventId: "evt-12345"
|
||||||
overallStatus: "COMPLETED"
|
overallStatus: "COMPLETED"
|
||||||
completedAt: "2025-11-01T09:00:00Z"
|
startedAt: "2025-11-01T08:58:00"
|
||||||
|
completedAt: "2025-11-01T09:00:00"
|
||||||
channels:
|
channels:
|
||||||
- channel: "WOORIDONGNE_TV"
|
- channel: "URIDONGNETV"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
distributionId: "wtv-uuid-12345"
|
distributionId: "wtv-uuid-12345"
|
||||||
estimatedViews: 1500
|
estimatedViews: 1500
|
||||||
completedAt: "2025-11-01T09:00:00Z"
|
completedAt: "2025-11-01T09:00:00"
|
||||||
- channel: "RINGO_BIZ"
|
- channel: "RINGOBIZ"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
updateTimestamp: "2025-11-01T09:00:00Z"
|
updateTimestamp: "2025-11-01T09:00:00"
|
||||||
- channel: "GENIE_TV"
|
completedAt: "2025-11-01T09:00:00"
|
||||||
|
- channel: "GINITV"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
adId: "gtv-uuid-12345"
|
adId: "gtv-uuid-12345"
|
||||||
impressionSchedule:
|
impressionSchedule:
|
||||||
- "2025-11-01 18:00-20:00"
|
- "2025-11-01 18:00-20:00"
|
||||||
- "2025-11-02 12:00-14:00"
|
- "2025-11-02 12:00-14:00"
|
||||||
|
completedAt: "2025-11-01T09:00:00"
|
||||||
- channel: "INSTAGRAM"
|
- channel: "INSTAGRAM"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
postUrl: "https://instagram.com/p/generated-post-id"
|
postUrl: "https://instagram.com/p/generated-post-id"
|
||||||
postId: "ig-post-12345"
|
postId: "ig-post-12345"
|
||||||
- channel: "NAVER_BLOG"
|
completedAt: "2025-11-01T09:00:00"
|
||||||
|
- channel: "NAVER"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
postUrl: "https://blog.naver.com/store123/generated-post"
|
postUrl: "https://blog.naver.com/store123/generated-post"
|
||||||
- channel: "KAKAO_CHANNEL"
|
completedAt: "2025-11-01T09:00:00"
|
||||||
|
- channel: "KAKAO"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
messageId: "kakao-msg-12345"
|
messageId: "kakao-msg-12345"
|
||||||
|
completedAt: "2025-11-01T09:00:00"
|
||||||
inProgress:
|
inProgress:
|
||||||
summary: 배포 진행중 상태
|
summary: 배포 진행중 상태
|
||||||
value:
|
value:
|
||||||
eventId: "evt-12345"
|
eventId: "evt-12345"
|
||||||
overallStatus: "IN_PROGRESS"
|
overallStatus: "IN_PROGRESS"
|
||||||
startedAt: "2025-11-01T08:58:00Z"
|
startedAt: "2025-11-01T08:58:00"
|
||||||
channels:
|
channels:
|
||||||
- channel: "WOORIDONGNE_TV"
|
- channel: "URIDONGNETV"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
distributionId: "wtv-uuid-12345"
|
distributionId: "wtv-uuid-12345"
|
||||||
estimatedViews: 1500
|
estimatedViews: 1500
|
||||||
|
completedAt: "2025-11-01T08:59:00"
|
||||||
- channel: "INSTAGRAM"
|
- channel: "INSTAGRAM"
|
||||||
status: "IN_PROGRESS"
|
status: "IN_PROGRESS"
|
||||||
progress: 50
|
progress: 50
|
||||||
- channel: "NAVER_BLOG"
|
- channel: "NAVER"
|
||||||
status: "PENDING"
|
status: "PENDING"
|
||||||
partialFailure:
|
partialFailure:
|
||||||
summary: 일부 채널 실패 상태
|
summary: 일부 채널 실패 상태
|
||||||
value:
|
value:
|
||||||
eventId: "evt-12345"
|
eventId: "evt-12345"
|
||||||
overallStatus: "PARTIAL_FAILURE"
|
overallStatus: "PARTIAL_FAILURE"
|
||||||
completedAt: "2025-11-01T09:00:00Z"
|
startedAt: "2025-11-01T08:58:00"
|
||||||
|
completedAt: "2025-11-01T09:00:00"
|
||||||
channels:
|
channels:
|
||||||
- channel: "WOORIDONGNE_TV"
|
- channel: "URIDONGNETV"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
distributionId: "wtv-uuid-12345"
|
distributionId: "wtv-uuid-12345"
|
||||||
estimatedViews: 1500
|
estimatedViews: 1500
|
||||||
|
completedAt: "2025-11-01T08:59:00"
|
||||||
- channel: "INSTAGRAM"
|
- channel: "INSTAGRAM"
|
||||||
status: "FAILED"
|
status: "FAILED"
|
||||||
errorMessage: "Instagram API 타임아웃"
|
errorMessage: "Instagram API 타임아웃"
|
||||||
retries: 3
|
retries: 3
|
||||||
lastRetryAt: "2025-11-01T08:59:30Z"
|
lastRetryAt: "2025-11-01T08:59:30"
|
||||||
- channel: "NAVER_BLOG"
|
- channel: "NAVER"
|
||||||
status: "COMPLETED"
|
status: "COMPLETED"
|
||||||
postUrl: "https://blog.naver.com/store123/generated-post"
|
postUrl: "https://blog.naver.com/store123/generated-post"
|
||||||
|
completedAt: "2025-11-01T09:00:00"
|
||||||
'404':
|
'404':
|
||||||
description: 배포 이력을 찾을 수 없음
|
description: 배포 이력을 찾을 수 없음
|
||||||
content:
|
content:
|
||||||
@ -305,196 +317,133 @@ components:
|
|||||||
required:
|
required:
|
||||||
- eventId
|
- eventId
|
||||||
- channels
|
- channels
|
||||||
- contentUrls
|
|
||||||
properties:
|
properties:
|
||||||
eventId:
|
eventId:
|
||||||
type: string
|
type: string
|
||||||
description: 이벤트 ID
|
description: 이벤트 ID
|
||||||
example: "evt-12345"
|
example: "evt-12345"
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
description: 이벤트 제목
|
||||||
|
example: "신규 고객 환영 이벤트"
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: 이벤트 설명
|
||||||
|
example: "신규 고객님을 위한 특별 할인 이벤트"
|
||||||
|
imageUrl:
|
||||||
|
type: string
|
||||||
|
description: 이미지 URL (CDN)
|
||||||
|
example: "https://cdn.example.com/images/event-main.jpg"
|
||||||
channels:
|
channels:
|
||||||
type: array
|
type: array
|
||||||
description: 배포할 채널 목록
|
description: 배포할 채널 목록
|
||||||
minItems: 1
|
minItems: 1
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/ChannelConfig'
|
|
||||||
contentUrls:
|
|
||||||
type: object
|
|
||||||
description: 플랫폼별 콘텐츠 URL
|
|
||||||
properties:
|
|
||||||
wooridongneTV:
|
|
||||||
type: string
|
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:
|
enum:
|
||||||
- WOORIDONGNE_TV
|
- URIDONGNETV
|
||||||
- RINGO_BIZ
|
- RINGOBIZ
|
||||||
- GENIE_TV
|
- GINITV
|
||||||
- INSTAGRAM
|
- INSTAGRAM
|
||||||
- NAVER_BLOG
|
- NAVER
|
||||||
- KAKAO_CHANNEL
|
- KAKAO
|
||||||
example: "INSTAGRAM"
|
example: ["URIDONGNETV", "INSTAGRAM", "NAVER"]
|
||||||
config:
|
channelSettings:
|
||||||
|
type: object
|
||||||
|
description: 채널별 추가 설정 (Optional)
|
||||||
|
additionalProperties:
|
||||||
type: object
|
type: object
|
||||||
description: 채널별 설정 (채널에 따라 다름)
|
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
example:
|
example:
|
||||||
scheduledTime: "2025-11-01T10:00:00Z"
|
URIDONGNETV:
|
||||||
caption: "이벤트 안내"
|
radius: "1km"
|
||||||
hashtags:
|
timeSlot: "evening"
|
||||||
- "이벤트"
|
INSTAGRAM:
|
||||||
- "할인"
|
scheduledTime: "2025-11-01T10:00:00"
|
||||||
|
|
||||||
DistributionResponse:
|
DistributionResponse:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- distributionId
|
|
||||||
- eventId
|
- eventId
|
||||||
- status
|
- success
|
||||||
- results
|
- channelResults
|
||||||
|
- successCount
|
||||||
|
- failureCount
|
||||||
properties:
|
properties:
|
||||||
distributionId:
|
|
||||||
type: string
|
|
||||||
description: 배포 ID
|
|
||||||
example: "dist-12345"
|
|
||||||
eventId:
|
eventId:
|
||||||
type: string
|
type: string
|
||||||
description: 이벤트 ID
|
description: 이벤트 ID
|
||||||
example: "evt-12345"
|
example: "evt-12345"
|
||||||
status:
|
success:
|
||||||
type: string
|
type: boolean
|
||||||
description: 전체 배포 상태
|
description: 배포 성공 여부 (모든 채널 또는 일부 채널 성공)
|
||||||
enum:
|
example: true
|
||||||
- PENDING
|
channelResults:
|
||||||
- IN_PROGRESS
|
type: array
|
||||||
- COMPLETED
|
description: 채널별 배포 결과
|
||||||
- PARTIAL_FAILURE
|
items:
|
||||||
- FAILED
|
$ref: '#/components/schemas/ChannelDistributionResult'
|
||||||
example: "COMPLETED"
|
successCount:
|
||||||
startedAt:
|
type: integer
|
||||||
type: string
|
description: 성공한 채널 수
|
||||||
format: date-time
|
example: 3
|
||||||
description: 배포 시작 시각
|
failureCount:
|
||||||
example: "2025-11-01T08:59:00Z"
|
type: integer
|
||||||
|
description: 실패한 채널 수
|
||||||
|
example: 0
|
||||||
completedAt:
|
completedAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: 배포 완료 시각
|
description: 배포 완료 시각
|
||||||
example: "2025-11-01T09:00:00Z"
|
example: "2025-11-01T09:00:00"
|
||||||
results:
|
totalExecutionTimeMs:
|
||||||
type: array
|
type: integer
|
||||||
description: 채널별 배포 결과
|
format: int64
|
||||||
items:
|
description: 전체 배포 소요 시간 (ms)
|
||||||
$ref: '#/components/schemas/ChannelResult'
|
example: 1234
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
description: 메시지
|
||||||
|
example: "배포가 성공적으로 완료되었습니다"
|
||||||
|
|
||||||
ChannelResult:
|
ChannelDistributionResult:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- channel
|
- channel
|
||||||
- status
|
- success
|
||||||
properties:
|
properties:
|
||||||
channel:
|
channel:
|
||||||
type: string
|
type: string
|
||||||
description: 채널 타입
|
description: 채널 타입
|
||||||
enum:
|
enum:
|
||||||
- WOORIDONGNE_TV
|
- URIDONGNETV
|
||||||
- RINGO_BIZ
|
- RINGOBIZ
|
||||||
- GENIE_TV
|
- GINITV
|
||||||
- INSTAGRAM
|
- INSTAGRAM
|
||||||
- NAVER_BLOG
|
- NAVER
|
||||||
- KAKAO_CHANNEL
|
- KAKAO
|
||||||
example: "INSTAGRAM"
|
example: "INSTAGRAM"
|
||||||
status:
|
success:
|
||||||
type: string
|
type: boolean
|
||||||
description: 채널별 배포 상태
|
description: 배포 성공 여부
|
||||||
enum:
|
example: true
|
||||||
- PENDING
|
|
||||||
- IN_PROGRESS
|
|
||||||
- SUCCESS
|
|
||||||
- FAILED
|
|
||||||
example: "SUCCESS"
|
|
||||||
distributionId:
|
distributionId:
|
||||||
type: string
|
type: string
|
||||||
description: 채널별 배포 ID (우리동네TV, 지니TV)
|
description: 배포 ID (성공 시)
|
||||||
example: "wtv-uuid-12345"
|
example: "dist-uuid-12345"
|
||||||
estimatedViews:
|
estimatedReach:
|
||||||
type: integer
|
type: integer
|
||||||
description: 예상 노출 수 (우리동네TV, 지니TV)
|
description: 예상 노출 수 (성공 시)
|
||||||
example: 1500
|
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:
|
errorMessage:
|
||||||
type: string
|
type: string
|
||||||
description: 오류 메시지 (실패 시)
|
description: 에러 메시지 (실패 시)
|
||||||
example: "Instagram API 타임아웃"
|
example: "Instagram API 타임아웃"
|
||||||
retries:
|
executionTimeMs:
|
||||||
type: integer
|
type: integer
|
||||||
description: 재시도 횟수
|
format: int64
|
||||||
example: 0
|
description: 배포 소요 시간 (ms)
|
||||||
lastRetryAt:
|
example: 234
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
description: 마지막 재시도 시각
|
|
||||||
example: "2025-11-01T08:59:30Z"
|
|
||||||
|
|
||||||
DistributionStatusResponse:
|
DistributionStatusResponse:
|
||||||
type: object
|
type: object
|
||||||
@ -544,12 +493,12 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: 채널 타입
|
description: 채널 타입
|
||||||
enum:
|
enum:
|
||||||
- WOORIDONGNE_TV
|
- URIDONGNETV
|
||||||
- RINGO_BIZ
|
- RINGOBIZ
|
||||||
- GENIE_TV
|
- GINITV
|
||||||
- INSTAGRAM
|
- INSTAGRAM
|
||||||
- NAVER_BLOG
|
- NAVER
|
||||||
- KAKAO_CHANNEL
|
- KAKAO
|
||||||
example: "INSTAGRAM"
|
example: "INSTAGRAM"
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
@ -569,7 +518,7 @@ components:
|
|||||||
distributionId:
|
distributionId:
|
||||||
type: string
|
type: string
|
||||||
description: 채널별 배포 ID
|
description: 채널별 배포 ID
|
||||||
example: "wtv-uuid-12345"
|
example: "dist-uuid-12345"
|
||||||
estimatedViews:
|
estimatedViews:
|
||||||
type: integer
|
type: integer
|
||||||
description: 예상 노출 수
|
description: 예상 노출 수
|
||||||
@ -578,35 +527,35 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: 업데이트 완료 시각
|
description: 업데이트 완료 시각
|
||||||
example: "2025-11-01T09:00:00Z"
|
example: "2025-11-01T09:00:00"
|
||||||
adId:
|
adId:
|
||||||
type: string
|
type: string
|
||||||
description: 광고 ID
|
description: 광고 ID (지니TV)
|
||||||
example: "gtv-uuid-12345"
|
example: "gtv-uuid-12345"
|
||||||
impressionSchedule:
|
impressionSchedule:
|
||||||
type: array
|
type: array
|
||||||
description: 노출 스케줄
|
description: 노출 스케줄 (지니TV)
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
example:
|
example:
|
||||||
- "2025-11-01 18:00-20:00"
|
- "2025-11-01 18:00-20:00"
|
||||||
postUrl:
|
postUrl:
|
||||||
type: string
|
type: string
|
||||||
description: 게시물 URL
|
description: 게시물 URL (Instagram, Naver Blog)
|
||||||
example: "https://instagram.com/p/generated-post-id"
|
example: "https://instagram.com/p/generated-post-id"
|
||||||
postId:
|
postId:
|
||||||
type: string
|
type: string
|
||||||
description: 게시물 ID
|
description: 게시물 ID (Instagram)
|
||||||
example: "ig-post-12345"
|
example: "ig-post-12345"
|
||||||
messageId:
|
messageId:
|
||||||
type: string
|
type: string
|
||||||
description: 메시지 ID
|
description: 메시지 ID (Kakao Channel)
|
||||||
example: "kakao-msg-12345"
|
example: "kakao-msg-12345"
|
||||||
completedAt:
|
completedAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: 완료 시각
|
description: 완료 시각
|
||||||
example: "2025-11-01T09:00:00Z"
|
example: "2025-11-01T09:00:00"
|
||||||
errorMessage:
|
errorMessage:
|
||||||
type: string
|
type: string
|
||||||
description: 오류 메시지
|
description: 오류 메시지
|
||||||
@ -619,7 +568,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: 마지막 재시도 시각
|
description: 마지막 재시도 시각
|
||||||
example: "2025-11-01T08:59:30Z"
|
example: "2025-11-01T08:59:30"
|
||||||
|
|
||||||
ErrorResponse:
|
ErrorResponse:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { cardStyles, colors, responsiveText } from '@/shared/lib/button-styles';
|
|||||||
import { eventApi } from '@/entities/event/api/eventApi';
|
import { eventApi } from '@/entities/event/api/eventApi';
|
||||||
import type { EventObjective } from '@/entities/event/model/types';
|
import type { EventObjective } from '@/entities/event/model/types';
|
||||||
|
|
||||||
|
|
||||||
interface ApprovalStepProps {
|
interface ApprovalStepProps {
|
||||||
eventData: EventData;
|
eventData: EventData;
|
||||||
onApprove: () => void;
|
onApprove: () => void;
|
||||||
@ -34,6 +35,7 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
|
|||||||
const [termsDialogOpen, setTermsDialogOpen] = useState(false);
|
const [termsDialogOpen, setTermsDialogOpen] = useState(false);
|
||||||
const [successDialogOpen, setSuccessDialogOpen] = useState(false);
|
const [successDialogOpen, setSuccessDialogOpen] = useState(false);
|
||||||
const [isDeploying, setIsDeploying] = useState(false);
|
const [isDeploying, setIsDeploying] = useState(false);
|
||||||
|
const DISTRIBUTION_API_BASE_URL = process.env.NEXT_PUBLIC_DISTRIBUTION_HOST || 'http://kt-event-marketing-api.20.214.196.128.nip.io';
|
||||||
|
|
||||||
const handleApprove = async () => {
|
const handleApprove = async () => {
|
||||||
if (!agreeTerms) return;
|
if (!agreeTerms) return;
|
||||||
@ -86,39 +88,46 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
|
|||||||
});
|
});
|
||||||
console.log('✅ Event details updated');
|
console.log('✅ Event details updated');
|
||||||
|
|
||||||
// 3. 배포 채널 선택
|
|
||||||
if (eventData.channels && eventData.channels.length > 0) {
|
|
||||||
console.log('📞 Selecting channels:', eventData.channels);
|
|
||||||
|
|
||||||
// 채널명 매핑 (Frontend → Backend)
|
// 채널명 매핑 (Frontend → Backend)
|
||||||
const channelMap: Record<string, string> = {
|
const channelMap: Record<string, string[]> = {
|
||||||
'uriTV': 'WEBSITE',
|
uriTV: ['URIDONGNETV'],
|
||||||
'ringoBiz': 'EMAIL',
|
ringoBiz: ['RINGOBIZ'],
|
||||||
'genieTV': 'KAKAO',
|
genieTV: ['GINITV'],
|
||||||
'sns': 'INSTAGRAM',
|
sns: ['INSTAGRAM', 'NAVER', 'KAKAO'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const backendChannels = eventData.channels.map(ch => channelMap[ch] || ch.toUpperCase());
|
const apiChannels = eventData.channels?.flatMap(ch => channelMap[ch] || []) || [];
|
||||||
|
|
||||||
await eventApi.selectChannels(eventId, {
|
const distributionRequest = {
|
||||||
channels: backendChannels,
|
eventId: eventId,
|
||||||
|
title: eventName,
|
||||||
|
description: eventData.contentEdit?.guide || eventData.recommendation?.recommendation?.description || '',
|
||||||
|
imageUrl: '', // TODO: 이미지 URL 연동 필요
|
||||||
|
channels: apiChannels,
|
||||||
|
channelSettings: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('🚀 Distributing event:', distributionRequest);
|
||||||
|
|
||||||
|
const response = await fetch(`${DISTRIBUTION_API_BASE_URL}/api/v1/distribution/distribute`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(distributionRequest),
|
||||||
});
|
});
|
||||||
console.log('✅ Channels selected');
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(errorData.error || '배포 중 오류가 발생했습니다');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. TODO: 이미지 선택
|
const data = await response.json();
|
||||||
// 현재 frontend에서 selectedImageId를 추적하지 않음
|
console.log('✅ Distribution completed:', data);
|
||||||
// 향후 contentPreview 단계에서 선택된 이미지 ID를 eventData에 저장 필요
|
|
||||||
console.log('⚠️ Image selection skipped - imageId not tracked in frontend');
|
|
||||||
|
|
||||||
// 5. 이벤트 배포 API 호출
|
|
||||||
console.log('📞 Publishing event:', eventId);
|
|
||||||
const publishResponse = await eventApi.publishEvent(eventId);
|
|
||||||
console.log('✅ Event published:', publishResponse);
|
|
||||||
|
|
||||||
// 성공 다이얼로그 표시
|
|
||||||
setIsDeploying(false);
|
setIsDeploying(false);
|
||||||
setSuccessDialogOpen(true);
|
setSuccessDialogOpen(true);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Event creation failed: No event ID returned');
|
throw new Error('Event creation failed: No event ID returned');
|
||||||
}
|
}
|
||||||
@ -129,6 +138,7 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleSaveDraft = () => {
|
const handleSaveDraft = () => {
|
||||||
// TODO: 임시저장 API 연동
|
// TODO: 임시저장 API 연동
|
||||||
alert('임시저장되었습니다');
|
alert('임시저장되었습니다');
|
||||||
|
|||||||
44
src/app/api/distribution/[eventId]/status/route.ts
Normal file
44
src/app/api/distribution/[eventId]/status/route.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
const DISTRIBUTION_API_BASE_URL = process.env.NEXT_PUBLIC_DISTRIBUTION_HOST || 'http://kt-event-marketing-api.20.214.196.128.nip.io';
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: NextRequest,
|
||||||
|
{ params }: { params: { eventId: string } }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const { eventId } = params;
|
||||||
|
|
||||||
|
console.log('🔄 Proxying distribution status request to Distribution API:', {
|
||||||
|
url: `${DISTRIBUTION_API_BASE_URL}/api/v1/distribution/${eventId}/status`,
|
||||||
|
eventId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(`${DISTRIBUTION_API_BASE_URL}/distribution/${eventId}/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error('❌ Distribution API error:', response.status, errorText);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Failed to get distribution status', details: errorText },
|
||||||
|
{ status: response.status }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('✅ Distribution status retrieved:', data);
|
||||||
|
|
||||||
|
return NextResponse.json(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Proxy error:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Internal server error', details: error instanceof Error ? error.message : 'Unknown error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,6 +33,15 @@ export const participationClient: AxiosInstance = axios.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Distribution API 전용 클라이언트
|
||||||
|
export const distributionClient: AxiosInstance = axios.create({
|
||||||
|
baseURL: `${API_HOSTS.distribution}`,
|
||||||
|
timeout: 90000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 공통 Request interceptor 함수
|
// 공통 Request interceptor 함수
|
||||||
const requestInterceptor = (config: InternalAxiosRequestConfig) => {
|
const requestInterceptor = (config: InternalAxiosRequestConfig) => {
|
||||||
console.log('🚀 API Request:', {
|
console.log('🚀 API Request:', {
|
||||||
@ -92,4 +101,8 @@ apiClient.interceptors.response.use(responseInterceptor, responseErrorIntercepto
|
|||||||
participationClient.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
participationClient.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
||||||
participationClient.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
participationClient.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
||||||
|
|
||||||
|
// Distribution API Client 인터셉터 적용
|
||||||
|
distributionClient.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
||||||
|
distributionClient.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
||||||
|
|
||||||
export default apiClient;
|
export default apiClient;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user