@startuml 이벤트생성플로우 !theme mono title 이벤트 생성 플로우 - 외부 시퀀스 다이어그램 actor "소상공인" as User participant "Frontend" as FE participant "API Gateway" as Gateway participant "Event Service" as Event participant "User Service" as UserSvc participant "AI Service" as AI participant "Content Service" as Content participant "Distribution Service" as Dist participant "Kafka" as Kafka database "Event DB" as EventDB database "User DB" as UserDB database "Redis" as Redis participant "외부 AI API" as AIApi participant "이미지 생성 API" as ImageApi participant "배포 채널 APIs" as ChannelApis == 1. 이벤트 목적 선택 (UFR-EVENT-020) == User -> FE: 이벤트 목적 선택 FE -> UserSvc: GET /api/users/{userId}/store\n회원 및 매장정보 조회 activate UserSvc UserSvc -> UserDB: 사용자 및 매장 정보 조회 activate UserDB UserDB --> UserSvc: 사용자, 매장 정보 반환 deactivate UserDB UserSvc --> FE: 200 OK\n{userId, storeName, industry, address} deactivate UserSvc FE -> Gateway: POST /events/purposes\n{목적, userId, storeName, industry, address} Gateway -> Event: 이벤트 목적 저장 요청 Event -> Redis: 이벤트 목적 정보 저장\nKey: draft:event:{eventDraftId}\n(목적, 매장정보 저장)\nTTL: 24시간 activate Redis Redis --> Event: 저장 완료 deactivate Redis Event --> Gateway: 저장 완료\n{eventDraftId} Gateway --> FE: 200 OK FE --> User: AI 추천 화면으로 이동 == 2. AI 이벤트 추천 - 비동기 처리 (UFR-EVENT-030) == User -> FE: AI 추천 요청 FE -> Gateway: POST /api/events/{eventDraftId}/ai-recommendations\n{목적, 업종, 지역} Gateway -> Event: AI 추천 요청 전달 Event -> Kafka: Publish to ai-job-topic\n{jobId, eventDraftId, 목적, 업종, 지역} Event --> Gateway: Job 생성 완료\n{jobId, status: PENDING} Gateway --> FE: 202 Accepted\n{jobId} FE --> User: "AI가 분석 중입니다..." (로딩) note over AI: Kafka Consumer\nai 이벤트 생성 topic 구독 Kafka --> AI: Consume Job Message\n{jobId, eventDraftId, ...} AI -> AIApi: 트렌드 분석 및 이벤트 추천 요청\n{목적, 업종, 지역, 매장정보}\n[Circuit Breaker, Timeout: 5분] AIApi --> AI: 3가지 추천안 + 트렌드 요약\n(예: "여름철 시원한 음료 선호도 증가") AI -> Redis: AI 추천 결과 저장\nKey: ai:event:{eventDraftId}\n(3가지 추천안, 트렌드 요약)\nTTL: 24시간 Redis --> AI: 저장 완료 AI -> Redis: Job 상태 업데이트\n(상태를 COMPLETED로 변경) note over AI, Redis: AI 추천 정보는 Redis에 저장\n- Content Service가 읽기 위함\n- 최종 승인 시 Event DB에 영구 저장 group Polling으로 상태 확인 loop 상태 확인 (최대 30초) FE -> Gateway: GET /jobs/{jobId}/status Gateway -> Event: Job 상태 조회 Event -> Redis: Job 상태 조회\n(jobId로 상태 및 결과 조회) Redis --> Event: {status, result} alt Job 완료 Event --> Gateway: 200 OK\n{status: COMPLETED, recommendations, trendSummary} Gateway --> FE: 추천 결과 및 트렌드 요약 반환 FE --> User: 트렌드 요약 표시\n3가지 추천안 표시\n(제목/경품 수정 가능) else Job 진행중 Event --> Gateway: 200 OK\n{status: PENDING/PROCESSING} Gateway --> FE: 진행중 상태 note over FE: 2초 후 재요청 end end end User -> FE: 추천안 선택\n(제목/경품 커스텀) FE -> Gateway: PUT /events/drafts/{eventDraftId}\n{선택한 추천안, 커스텀 정보} Gateway -> Event: 선택 저장 Event -> Redis: 선택한 추천안 저장\nKey: draft:event:{eventDraftId}\n(이벤트 초안 업데이트)\nTTL: 24시간 activate Redis Redis --> Event: 업데이트 완료 deactivate Redis Event --> Gateway: 200 OK Gateway --> FE: 저장 완료 FE --> User: 콘텐츠 생성 화면으로 이동 == 3. SNS 이미지 생성 - 비동기 처리 (UFR-CONT-010) == User -> FE: 이미지 생성 요청 FE -> Gateway: POST /api/content/images/{eventDraftId}/generate Gateway -> Content: 이미지 생성 요청 Content -> Content: Job 생성\n{jobId, eventDraftId, status: PENDING} Content --> Gateway: Job 생성 완료\n{jobId, status: PENDING} Gateway --> FE: 202 Accepted\n{jobId} FE --> User: "이미지 생성 중..." (로딩) note over Content: 백그라운드 워커\nRedis 폴링 또는 스케줄러 Content -> Redis: AI 이벤트 데이터 읽기\nKey: ai:event:{eventDraftId} activate Redis Redis --> Content: AI 추천 결과\n{선택된 추천안, 이벤트 정보} deactivate Redis note over Content: inner sequence 참조:\ncontent-이미지생성.puml par 심플 스타일 Content -> ImageApi: 심플 스타일 생성 요청\n[Circuit Breaker, Timeout: 5분] ImageApi --> Content: 심플 이미지 URL else 화려한 스타일 Content -> ImageApi: 화려한 스타일 생성 요청\n[Circuit Breaker, Timeout: 5분] ImageApi --> Content: 화려한 이미지 URL else 트렌디 스타일 Content -> ImageApi: 트렌디 스타일 생성 요청\n[Circuit Breaker, Timeout: 5분] ImageApi --> Content: 트렌디 이미지 URL end Content -> Redis: 이미지 URL 저장\nKey: content:image:{eventDraftId}\n{심플, 화려, 트렌디 URL}\nTTL: 7일 activate Redis Redis --> Content: 저장 완료 deactivate Redis Content -> Redis: Job 상태 업데이트\n(상태를 COMPLETED로 변경) note over Content, Redis: 이미지 URL은 Redis에 저장\n- 최종 승인 시 Event DB에 영구 저장 group Polling으로 상태 확인 loop 상태 확인 (최대 30초) FE -> Gateway: GET /api/content/jobs/{jobId}/status Gateway -> Content: Job 상태 조회 Content -> Redis: Job 상태 조회\n(jobId로 상태 및 이미지 URL 조회) Redis --> Content: {status, imageUrls} alt Job 완료 Content --> Gateway: 200 OK\n{status: COMPLETED, imageUrls} Gateway --> FE: 이미지 URL 반환 FE --> User: 3가지 스타일 카드 표시 else Job 진행중 Content --> Gateway: 200 OK\n{status: PENDING/PROCESSING} Gateway --> FE: 진행중 상태 note over FE: 2초 후 재요청 end end end User -> FE: 스타일 선택 및 편집 FE -> Gateway: PUT /events/drafts/{eventDraftId}/content\n{선택한 이미지, 편집내용} Gateway -> Event: 콘텐츠 선택 저장 Event -> Redis: 선택한 콘텐츠 저장\nKey: draft:event:{eventDraftId}\n(이벤트 초안 업데이트)\nTTL: 24시간 activate Redis Redis --> Event: 업데이트 완료 deactivate Redis Event --> Gateway: 200 OK Gateway --> FE: 저장 완료 FE --> User: 배포 채널 선택 화면으로 이동 == 4. 최종 승인 및 다중 채널 배포 - 동기 처리 (UFR-EVENT-050) == User -> FE: 배포 채널 선택\n최종 승인 요청 FE -> Gateway: POST /api/events/{eventDraftId}/publish\n{선택 채널 목록} Gateway -> Event: 최종 승인 및 배포 처리 note over Event: Redis 데이터를 Event DB에 영구 저장 Event -> Redis: 이벤트 초안 조회\nKey: draft:event:{eventDraftId} activate Redis Redis --> Event: 이벤트 초안 데이터\n(목적, 매장정보, 추천안, 콘텐츠) deactivate Redis Event -> Redis: AI 추천 결과 조회\nKey: ai:event:{eventDraftId} activate Redis Redis --> Event: AI 추천 결과 deactivate Redis Event -> Redis: 이미지 URL 조회\nKey: content:image:{eventDraftId} activate Redis Redis --> Event: 이미지 URL 목록 deactivate Redis Event -> EventDB: 이벤트 정보 영구 저장\n(목적, 매장정보, AI 추천, 이미지 URL, 배포 채널 포함) EventDB --> Event: 저장 완료 Event -> EventDB: 이벤트 상태 변경\n(DRAFT → APPROVED로 업데이트) EventDB --> Event: 상태 변경 완료 Event -> Kafka: Publish to event-topic\nEventCreated\n{eventId, 이벤트정보} note over Event: 동기 호출로 배포 진행\ninner sequence 참조:\ndistribution-다중채널배포.puml Event -> Dist: REST API - 배포 요청\nPOST /api/distribution/distribute\n{eventId, channels[], contentUrls} note over Dist: Sprint 2: Mock 처리\n- 외부 API 호출 없음\n- 모든 배포 즉시 성공 처리\n- 배포 로그만 DB 기록 Dist -> EventDB: 배포 이력 초기화\n(이벤트ID, 상태: PENDING) EventDB --> Dist: 배포 이력 ID Dist -> EventDB: 배포 이력 상태 업데이트\n(상태: IN_PROGRESS) note over Dist: 다중 채널 Mock 배포\n(내부 처리 상세는 inner sequence 참조) par 우리동네TV alt 우리동네TV 선택 Dist -> Dist: Mock 처리\n(즉시 성공) Dist -> EventDB: 채널 로그 저장\n(우리동네TV, 성공,\n배포ID, 예상노출수) end else 링고비즈 alt 링고비즈 선택 Dist -> Dist: Mock 처리\n(즉시 성공) Dist -> EventDB: 채널 로그 저장\n(링고비즈, 성공,\n업데이트 시각) end else 지니TV alt 지니TV 선택 Dist -> Dist: Mock 처리\n(즉시 성공) Dist -> EventDB: 채널 로그 저장\n(지니TV, 성공,\n광고ID, 스케줄) end else Instagram alt Instagram 선택 Dist -> Dist: Mock 처리\n(즉시 성공) Dist -> EventDB: 채널 로그 저장\n(Instagram, 성공,\npostUrl, postId) end else Naver Blog alt Naver Blog 선택 Dist -> Dist: Mock 처리\n(즉시 성공) Dist -> EventDB: 채널 로그 저장\n(NaverBlog, 성공,\npostUrl) end else Kakao Channel alt Kakao Channel 선택 Dist -> Dist: Mock 처리\n(즉시 성공) Dist -> EventDB: 채널 로그 저장\n(KakaoChannel, 성공,\nmessageId) end end note over Dist: 모든 채널 배포 완료 (즉시 처리) Dist -> EventDB: 배포 이력 상태 업데이트\n(상태: COMPLETED, 완료일시) Dist -> Kafka: Publish to event-topic\nDistributionCompleted\n{eventId, channels[], results[], completedAt} Dist --> Event: REST API 동기 응답\n200 OK\n{distributionId, status: COMPLETED, results[]} Event -> EventDB: 이벤트 상태 업데이트\n(APPROVED → ACTIVE로 변경) EventDB --> Event: 업데이트 완료 Event --> Gateway: 200 OK\n{eventId, 배포결과} Gateway --> FE: 배포 완료 FE --> User: "이벤트가 배포되었습니다"\n대시보드로 이동 note over Event, Dist: Sprint 2 제약사항\n- 외부 API 호출 없음 (Mock)\n- 모든 배포 즉시 성공 처리\n- Circuit Breaker 미구현\n- Retry 로직 미구현\n\nSprint 3 이후 구현 예정\n- 실제 외부 채널 API 연동\n- Circuit Breaker 패턴\n- Retry 및 실패 처리 @enduml