mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 10:46:23 +00:00
inner sequence 동기화 완료 - Gateway 패턴 및 한글화
- event-추천결과조회.puml: Gateway 패턴 추가, 레이어 아키텍처 적용, 한글화 - event-추천안선택.puml: Gateway 패턴 추가, 5단계 구조화, 한글화 - event-콘텐츠선택.puml: ContentService로 변경, Gateway 패턴, 한글화 - event-최종승인및배포.puml: Gateway 패턴 추가, 7단계 구조화, 한글화 모든 파일 공통 변경사항: - Client actor 및 API Gateway 추가 - <<API Layer>>, <<Business Layer>>, <<Data Layer>> 레이어 구분 - 모든 요청/응답 한글 표시 - Repository CRUD 한글 설명 (SQL 제거) - Redis 캐시 키 패턴 표준화 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
447d6b8e5d
commit
d7d4b0a2da
@ -3,73 +3,155 @@
|
||||
|
||||
title Event Service - 최종 승인 및 Distribution Service 동기 호출 (UFR-EVENT-050)
|
||||
|
||||
participant "EventController" as Controller <<C>>
|
||||
participant "EventService" as Service <<S>>
|
||||
participant "EventRepository" as Repo <<R>>
|
||||
actor Client
|
||||
participant "API Gateway" as Gateway
|
||||
participant "EventController" as Controller <<API Layer>>
|
||||
participant "EventService" as Service <<Business Layer>>
|
||||
participant "EventRepository" as Repo <<Data Layer>>
|
||||
participant "Redis Cache" as Cache <<E>>
|
||||
database "Event DB" as DB <<E>>
|
||||
participant "Distribution Service" as DistSvc <<E>>
|
||||
participant "Kafka Producer" as Kafka <<E>>
|
||||
|
||||
note over Controller: POST /api/events/{id}/publish
|
||||
note over Controller, Kafka
|
||||
**UFR-EVENT-050: 이벤트 최종 승인 및 배포**
|
||||
- 이벤트 준비 상태 검증
|
||||
- 이벤트 승인 및 Kafka 이벤트 발행
|
||||
- Distribution Service 동기 호출 (다중 채널 배포)
|
||||
- 이벤트 상태를 ACTIVE로 변경
|
||||
end note
|
||||
|
||||
Client -> Gateway: POST /api/events/{eventDraftId}/publish\n{"userId": 123,\n"selectedChannels": [\n "우리동네TV",\n "지니TV",\n "Instagram"\n]}
|
||||
activate Gateway
|
||||
|
||||
Gateway -> Controller: POST /api/events/{eventDraftId}/publish
|
||||
activate Controller
|
||||
|
||||
Controller -> Controller: 요청 검증\n(필수 필드, 채널 유효성)
|
||||
|
||||
Controller -> Service: publishEvent(eventDraftId, userId, selectedChannels)
|
||||
activate Service
|
||||
|
||||
== 1단계: 이벤트 초안 조회 및 검증 ==
|
||||
|
||||
Service -> Repo: findById(eventDraftId)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 초안 조회\n(초안ID와 사용자ID로 조회)
|
||||
Repo -> DB: 이벤트 초안 조회\n(초안ID로 조회)
|
||||
activate DB
|
||||
DB --> Repo: EventDraft
|
||||
DB --> Repo: EventDraft 엔티티\n{목적, 추천안, 콘텐츠, 상태}
|
||||
deactivate DB
|
||||
Repo --> Service: EventDraft entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Service: validateOwnership(userId, eventDraft)
|
||||
note right
|
||||
소유권 검증:
|
||||
- 사용자ID와 초안 소유자 일치 확인
|
||||
- 권한 없으면 403 Forbidden
|
||||
end note
|
||||
|
||||
Service -> Service: validatePublishReady()
|
||||
note right: 발행 준비 검증:\n- 목적 선택 완료\n- AI 추천 선택 완료\n- 콘텐츠 선택 완료\n- 배포 채널 최소 1개
|
||||
note right
|
||||
발행 준비 검증:
|
||||
- 목적 선택 완료
|
||||
- AI 추천 선택 완료
|
||||
- 콘텐츠 선택 완료
|
||||
- 배포 채널 최소 1개 선택
|
||||
end note
|
||||
|
||||
== 2단계: 이벤트 승인 ==
|
||||
|
||||
Service -> Repo: updateStatus(eventDraftId, APPROVED)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 초안 상태 업데이트\n(상태를 APPROVED로,\n승인일시를 현재 시각으로 설정)
|
||||
Repo -> DB: 이벤트 초안 상태 업데이트\n(상태를 APPROVED로,\n승인일시를 현재 시각으로 저장)
|
||||
activate DB
|
||||
DB --> Repo: OK
|
||||
DB --> Repo: 업데이트 완료
|
||||
deactivate DB
|
||||
Repo --> Service: EventDraft entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Kafka: publish(EventCreated,\n{eventId, userId, title,\nobjective, createdAt})
|
||||
== 3단계: Kafka 이벤트 발행 ==
|
||||
|
||||
Service -> Kafka: 이벤트 발행\nTopic: event-topic\nPayload: {eventId, userId, title,\nobjective, createdAt}
|
||||
activate Kafka
|
||||
note right: Kafka Event Topic:\nevent-topic
|
||||
Kafka --> Service: ACK
|
||||
note right
|
||||
Kafka Event Topic:
|
||||
- Topic: event-topic
|
||||
- Consumer: Analytics Service
|
||||
- Event Type: EventCreated
|
||||
end note
|
||||
Kafka --> Service: ACK (발행 확인)
|
||||
deactivate Kafka
|
||||
|
||||
Service -> DistSvc: POST /api/distribution/distribute\n{eventId, channels, content}
|
||||
== 4단계: Distribution Service 동기 호출 ==
|
||||
|
||||
Service -> DistSvc: POST /api/distribution/distribute\n{"eventId": 123,\n"channels": [...],\n"content": {...}}
|
||||
activate DistSvc
|
||||
note right: 동기 호출 (Circuit Breaker 적용)\nTimeout: 70초
|
||||
note right
|
||||
동기 호출 (Circuit Breaker 적용):
|
||||
- Timeout: 70초
|
||||
- 다중 채널 병렬 배포
|
||||
- Failure Rate: 50% 초과 시 OPEN
|
||||
end note
|
||||
|
||||
DistSvc -> DistSvc: distributeToChannels(eventId, channels)
|
||||
note right: 다중 채널 병렬 배포:\n- 우리동네TV\n- 링고비즈\n- 지니TV\n- Instagram\n- Naver Blog\n- Kakao Channel
|
||||
DistSvc --> Service: DistributionResponse\n{distributionId, channelResults}
|
||||
note right
|
||||
다중 채널 병렬 배포:
|
||||
- 우리동네TV
|
||||
- 링고비즈 (음성 안내)
|
||||
- 지니TV
|
||||
- Instagram
|
||||
- Naver Blog
|
||||
- Kakao Channel
|
||||
end note
|
||||
|
||||
DistSvc --> Service: DistributionResponse\n{"distributionId": "dist-123",\n"channelResults": [\n {"channel": "우리동네TV", "status": "SUCCESS"},\n {"channel": "지니TV", "status": "SUCCESS"},\n {"channel": "Instagram", "status": "SUCCESS"}\n]}
|
||||
deactivate DistSvc
|
||||
|
||||
== 5단계: 이벤트 활성화 ==
|
||||
|
||||
Service -> Repo: updateStatus(eventDraftId, ACTIVE)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 상태 업데이트\n(상태를 ACTIVE로,\n배포일시를 현재 시각으로 설정)
|
||||
Repo -> DB: 이벤트 상태 업데이트\n(상태를 ACTIVE로,\n배포일시를 현재 시각으로 저장)
|
||||
activate DB
|
||||
DB --> Repo: OK
|
||||
DB --> Repo: 업데이트 완료
|
||||
deactivate DB
|
||||
Repo --> Service: Event entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Cache: delete("purpose:" + userId)
|
||||
== 6단계: 캐시 무효화 ==
|
||||
|
||||
Service -> Cache: 캐시 삭제\nKey: draft:event:{eventDraftId}
|
||||
activate Cache
|
||||
note right: 캐시 무효화
|
||||
Cache --> Service: OK
|
||||
note right
|
||||
Redis 캐시 무효화:
|
||||
- 이벤트 초안 캐시 삭제
|
||||
- 활성화 완료 후 불필요
|
||||
end note
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
== 7단계: 응답 반환 ==
|
||||
|
||||
Service --> Controller: PublishResponse\n{eventId, status: ACTIVE,\ndistributionResults}
|
||||
deactivate Service
|
||||
Controller --> Client: 200 OK\n{eventId, distributionResults}
|
||||
|
||||
note over Controller, Kafka: Distribution Service는\n배포 완료 후 Kafka에\nDistributionCompleted\n이벤트 발행
|
||||
Controller --> Gateway: 200 OK\n{"eventId": 123,\n"status": "ACTIVE",\n"distributionResults": [...]}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 200 OK\n이벤트 배포 완료
|
||||
deactivate Gateway
|
||||
|
||||
note over Client, Kafka
|
||||
**배포 완료 후 처리**
|
||||
- Distribution Service는 배포 완료 후 Kafka에\n DistributionCompleted 이벤트 발행
|
||||
- Analytics Service가 구독하여 초기 통계 생성
|
||||
- 이벤트 상태: ACTIVE (참여자 접수 시작)
|
||||
|
||||
**성능 목표**
|
||||
- 응답 시간: 60초 이내 (Distribution Service 포함)
|
||||
- Distribution Service 타임아웃: 70초
|
||||
- 채널별 배포: 병렬 처리로 최적화
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
||||
@ -3,43 +3,138 @@
|
||||
|
||||
title Event Service - AI 추천 결과 폴링 조회
|
||||
|
||||
participant "EventController" as Controller <<C>>
|
||||
participant "JobService" as JobSvc <<S>>
|
||||
actor Client
|
||||
participant "API Gateway" as Gateway
|
||||
participant "EventController" as Controller <<API Layer>>
|
||||
participant "EventService" as Service <<Business Layer>>
|
||||
participant "JobManager" as JobMgr <<Component>>
|
||||
participant "Redis Cache" as Cache <<E>>
|
||||
|
||||
note over Controller: GET /api/jobs/{jobId}/status
|
||||
Controller -> JobSvc: getJobStatus(jobId)
|
||||
activate JobSvc
|
||||
note over Controller, Cache
|
||||
**폴링 방식 Job 상태 조회**
|
||||
- 최대 30초 동안 폴링 (2초 간격)
|
||||
- Job 상태: PENDING → PROCESSING → COMPLETED
|
||||
- AI 추천 결과: Redis에 저장 (TTL: 24시간)
|
||||
end note
|
||||
|
||||
JobSvc -> Cache: get("job:" + jobId)
|
||||
Client -> Gateway: GET /api/events/jobs/{jobId}/status
|
||||
activate Gateway
|
||||
|
||||
Gateway -> Controller: GET /api/events/jobs/{jobId}/status
|
||||
activate Controller
|
||||
|
||||
Controller -> Service: getJobStatus(jobId)
|
||||
activate Service
|
||||
|
||||
Service -> JobMgr: getJobStatus(jobId)
|
||||
activate JobMgr
|
||||
|
||||
JobMgr -> Cache: Job 상태 조회\nKey: job:{jobId}
|
||||
activate Cache
|
||||
|
||||
alt 캐시 히트
|
||||
Cache --> JobSvc: Job data\n{status, result, createdAt}
|
||||
alt Job 데이터 존재
|
||||
Cache --> JobMgr: Job 데이터\n{status, eventDraftId,\ntype, createdAt}
|
||||
deactivate Cache
|
||||
|
||||
alt Job 완료 (status: COMPLETED)
|
||||
JobSvc --> Controller: JobStatusResponse\n{jobId, status: COMPLETED,\nrecommendations: [...]}
|
||||
Controller --> Client: 200 OK\n{status: COMPLETED,\nrecommendations: [\n {title, prize, method, cost, roi},\n {title, prize, method, cost, roi},\n {title, prize, method, cost, roi}\n]}
|
||||
alt status = COMPLETED
|
||||
JobMgr -> Cache: AI 추천 결과 조회\nKey: ai:recommendation:{eventDraftId}
|
||||
activate Cache
|
||||
Cache --> JobMgr: AI 추천 결과\n{트렌드분석, 3가지추천안}
|
||||
deactivate Cache
|
||||
|
||||
else Job 진행중 (status: PROCESSING)
|
||||
JobSvc --> Controller: JobStatusResponse\n{jobId, status: PROCESSING}
|
||||
Controller --> Client: 200 OK\n{status: PROCESSING}
|
||||
note right: 클라이언트는 2초 후\n재요청
|
||||
JobMgr --> Service: JobStatusResponse\n{jobId, status: COMPLETED,\nrecommendations: {...}}
|
||||
deactivate JobMgr
|
||||
|
||||
else Job 실패 (status: FAILED)
|
||||
JobSvc --> Controller: JobStatusResponse\n{jobId, status: FAILED, error}
|
||||
Controller --> Client: 200 OK\n{status: FAILED, error}
|
||||
Service --> Controller: JobStatusResponse\n{status: COMPLETED, recommendations}
|
||||
deactivate Service
|
||||
|
||||
Controller --> Gateway: 200 OK\n{"status": "COMPLETED",\n"recommendations": [\n {"title": "저비용 추천안",\n "prize": "커피쿠폰",\n "method": "QR코드 스캔",\n "cost": "50만원",\n "roi": "150%"},\n {"title": "중비용 추천안",\n "prize": "상품권",\n "method": "SNS 공유",\n "cost": "100만원",\n "roi": "200%"},\n {"title": "고비용 추천안",\n "prize": "경품 추첨",\n "method": "설문 참여",\n "cost": "200만원",\n "roi": "300%"}\n]}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 200 OK\nAI 추천 결과 반환
|
||||
deactivate Gateway
|
||||
|
||||
note right of Client
|
||||
**프론트엔드 처리**
|
||||
- 3가지 추천안 카드 표시
|
||||
- 사용자가 추천안 선택
|
||||
- 트렌드 분석 정보 표시
|
||||
end note
|
||||
|
||||
else status = PROCESSING 또는 PENDING
|
||||
JobMgr --> Service: JobStatusResponse\n{jobId, status: PROCESSING}
|
||||
deactivate JobMgr
|
||||
|
||||
Service --> Controller: JobStatusResponse\n{status: PROCESSING}
|
||||
deactivate Service
|
||||
|
||||
Controller --> Gateway: 200 OK\n{"status": "PROCESSING",\n"message": "AI가 분석 중입니다"}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 200 OK\n진행 중 상태
|
||||
deactivate Gateway
|
||||
|
||||
note right of Client
|
||||
**폴링 재시도**
|
||||
- 2초 후 재요청
|
||||
- 최대 30초 (15회)
|
||||
end note
|
||||
|
||||
else status = FAILED
|
||||
JobMgr -> Cache: 에러 정보 조회\nKey: job:{jobId}:error
|
||||
activate Cache
|
||||
Cache --> JobMgr: 에러 메시지
|
||||
deactivate Cache
|
||||
|
||||
JobMgr --> Service: JobStatusResponse\n{jobId, status: FAILED, error}
|
||||
deactivate JobMgr
|
||||
|
||||
Service --> Controller: JobStatusResponse\n{status: FAILED, error}
|
||||
deactivate Service
|
||||
|
||||
Controller --> Gateway: 200 OK\n{"status": "FAILED",\n"error": "AI 분석 실패",\n"message": "다시 시도해주세요"}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 200 OK\n실패 상태
|
||||
deactivate Gateway
|
||||
|
||||
note right of Client
|
||||
**실패 처리**
|
||||
- 에러 메시지 표시
|
||||
- "다시 분석" 버튼 제공
|
||||
end note
|
||||
end
|
||||
|
||||
else 캐시 미스
|
||||
Cache --> JobSvc: null
|
||||
JobSvc --> Controller: NotFoundError
|
||||
Controller --> Client: 404 Not Found\n{error: "Job not found"}
|
||||
else Job 데이터 없음
|
||||
Cache --> JobMgr: null (캐시 미스)
|
||||
deactivate Cache
|
||||
|
||||
JobMgr --> Service: throw NotFoundException\n("Job을 찾을 수 없습니다")
|
||||
deactivate JobMgr
|
||||
|
||||
Service --> Controller: NotFoundException
|
||||
deactivate Service
|
||||
|
||||
Controller --> Gateway: 404 Not Found\n{"code": "JOB_001",\n"message": "Job을 찾을 수 없습니다"}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 404 Not Found
|
||||
deactivate Gateway
|
||||
end
|
||||
|
||||
deactivate Cache
|
||||
deactivate JobSvc
|
||||
note over Controller, Cache
|
||||
**폴링 전략**
|
||||
- 간격: 2초
|
||||
- 최대 시간: 30초 (15회)
|
||||
- Timeout 시: 사용자에게 알림 + "다시 분석" 옵션
|
||||
|
||||
note over Controller, Cache: 최대 30초 동안 폴링\n(2초 간격, 최대 15회)\n\n타임아웃 시 클라이언트는\n에러 메시지 표시
|
||||
**Redis 캐시**
|
||||
- Job 상태: TTL 1시간
|
||||
- AI 추천 결과: TTL 24시간
|
||||
|
||||
**성능 목표**
|
||||
- 평균 AI 분석 시간: 2분 이내
|
||||
- P95 AI 분석 시간: 4분 이내
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
||||
@ -3,51 +3,114 @@
|
||||
|
||||
title Event Service - 선택한 AI 추천안 저장 (UFR-EVENT-040)
|
||||
|
||||
participant "EventController" as Controller <<C>>
|
||||
participant "EventService" as Service <<S>>
|
||||
participant "EventRepository" as Repo <<R>>
|
||||
actor Client
|
||||
participant "API Gateway" as Gateway
|
||||
participant "EventController" as Controller <<API Layer>>
|
||||
participant "EventService" as Service <<Business Layer>>
|
||||
participant "EventRepository" as Repo <<Data Layer>>
|
||||
participant "Redis Cache" as Cache <<E>>
|
||||
database "Event DB" as DB <<E>>
|
||||
|
||||
note over Controller: PUT /api/events/drafts/{id}/recommendation
|
||||
note over Controller, Cache
|
||||
**UFR-EVENT-040: AI 추천안 선택 및 저장**
|
||||
- 사용자가 3가지 추천안 중 하나를 선택
|
||||
- 선택된 추천안을 이벤트 초안에 적용
|
||||
- Redis 캐시에서 AI 추천 결과 삭제
|
||||
end note
|
||||
|
||||
Client -> Gateway: PUT /api/events/drafts/{eventDraftId}/recommendation\n{"userId": 123,\n"selectedIndex": 1,\n"recommendation": {...}}
|
||||
activate Gateway
|
||||
|
||||
Gateway -> Controller: PUT /api/events/drafts/{eventDraftId}/recommendation
|
||||
activate Controller
|
||||
|
||||
Controller -> Controller: 요청 검증\n(필수 필드, 추천안 유효성)
|
||||
|
||||
Controller -> Service: updateEventRecommendation(eventDraftId, userId,\nselectedRecommendation)
|
||||
activate Service
|
||||
|
||||
== 1단계: 이벤트 초안 조회 및 검증 ==
|
||||
|
||||
Service -> Repo: findById(eventDraftId)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 초안 조회\n(초안ID와 사용자ID로 조회)
|
||||
Repo -> DB: 이벤트 초안 조회\n(초안ID로 조회)
|
||||
activate DB
|
||||
DB --> Repo: EventDraft
|
||||
DB --> Repo: EventDraft 엔티티\n{목적, 매장정보, 상태}
|
||||
deactivate DB
|
||||
Repo --> Service: EventDraft entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Service: validateOwnership(userId, eventDraft)
|
||||
note right
|
||||
소유권 검증:
|
||||
- 사용자ID와 초안 소유자 일치 확인
|
||||
- 권한 없으면 403 Forbidden
|
||||
end note
|
||||
|
||||
Service -> Service: validateRecommendation(selectedRecommendation)
|
||||
note right: 선택한 추천안\n유효성 검증
|
||||
note right
|
||||
추천안 유효성 검증:
|
||||
- 필수 필드 존재 여부
|
||||
- 비용/ROI 값 타당성
|
||||
end note
|
||||
|
||||
== 2단계: 추천안 적용 ==
|
||||
|
||||
Service -> Service: applyRecommendation(eventDraft, selectedRecommendation)
|
||||
note right: 추천안 적용:\n- 이벤트 제목\n- 경품 정보\n- 참여 방법\n- 예상 비용\n- 예상 ROI
|
||||
note right
|
||||
추천안 적용:
|
||||
- 이벤트 제목
|
||||
- 경품 정보
|
||||
- 참여 방법
|
||||
- 예상 비용
|
||||
- 예상 ROI
|
||||
- 홍보 문구
|
||||
end note
|
||||
|
||||
== 3단계: DB 저장 ==
|
||||
|
||||
Service -> Repo: update(eventDraft)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 초안 업데이트\n(선택된 추천안 정보,\n제목, 경품, 참여방법,\n예상비용, 예상ROI,\n수정일시를 업데이트)
|
||||
Repo -> DB: 이벤트 초안 업데이트\n(선택된 추천안 정보 저장:\n제목, 경품, 참여방법,\n예상비용, 예상ROI,\n수정일시)
|
||||
activate DB
|
||||
DB --> Repo: OK
|
||||
DB --> Repo: 업데이트 완료
|
||||
deactivate DB
|
||||
Repo --> Service: EventDraft entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Cache: delete("ai:recommendation:" + eventDraftId)
|
||||
== 4단계: 캐시 무효화 ==
|
||||
|
||||
Service -> Cache: 캐시 삭제\nKey: ai:recommendation:{eventDraftId}
|
||||
activate Cache
|
||||
note right: Redis 캐시 무효화\n(AI 추천 결과)
|
||||
Cache --> Service: OK
|
||||
note right
|
||||
Redis 캐시 무효화:
|
||||
- AI 추천 결과 삭제
|
||||
- 선택 완료 후 불필요
|
||||
end note
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
== 5단계: 응답 반환 ==
|
||||
|
||||
Service --> Controller: EventRecommendationResponse\n{eventDraftId, selectedRecommendation}
|
||||
deactivate Service
|
||||
Controller --> Client: 200 OK\n{eventDraftId}
|
||||
|
||||
note over Controller, Cache: 최종 선택된 추천안만\nEvent DB에 저장됨\n\nRedis에 저장된 3가지 추천안은\n선택 후 삭제됨
|
||||
Controller --> Gateway: 200 OK\n{"eventDraftId": 123,\n"status": "추천안 선택 완료",\n"selectedRecommendation": {...}}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 200 OK\n추천안 선택 완료
|
||||
deactivate Gateway
|
||||
|
||||
note over Client, Cache
|
||||
**저장 내용**
|
||||
- 최종 선택된 추천안만 Event DB에 저장
|
||||
- Redis에 저장된 3가지 추천안은 선택 후 삭제
|
||||
- 다음 단계: 콘텐츠 생성 (이미지 선택)
|
||||
|
||||
**성능 목표**
|
||||
- 응답 시간: 0.5초 이내
|
||||
- DB 업데이트: 0.1초
|
||||
- 캐시 삭제: 0.01초
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
||||
@ -1,53 +1,118 @@
|
||||
@startuml event-콘텐츠선택
|
||||
!theme mono
|
||||
|
||||
title Event Service - 선택한 콘텐츠 저장 (UFR-CONT-020)
|
||||
title Content Service - 선택한 콘텐츠 저장 (UFR-CONT-020)
|
||||
|
||||
participant "EventController" as Controller <<C>>
|
||||
participant "EventService" as Service <<S>>
|
||||
participant "EventRepository" as Repo <<R>>
|
||||
actor Client
|
||||
participant "API Gateway" as Gateway
|
||||
participant "ContentController" as Controller <<API Layer>>
|
||||
participant "ContentService" as Service <<Business Layer>>
|
||||
participant "EventRepository" as Repo <<Data Layer>>
|
||||
participant "Redis Cache" as Cache <<E>>
|
||||
database "Event DB" as DB <<E>>
|
||||
|
||||
note over Controller: PUT /api/events/drafts/{id}/content
|
||||
note over Controller, Cache
|
||||
**UFR-CONT-020: 콘텐츠 선택 및 편집 저장**
|
||||
- 사용자가 3가지 이미지 스타일 중 하나 선택
|
||||
- 선택된 이미지에 텍스트/색상 편집 적용
|
||||
- 편집된 콘텐츠를 이벤트 초안에 저장
|
||||
end note
|
||||
|
||||
Client -> Gateway: PUT /api/content/{eventDraftId}/select\n{"userId": 123,\n"selectedImageUrl": "https://cdn.../fancy.png",\n"editedContent": {...}}
|
||||
activate Gateway
|
||||
|
||||
Gateway -> Controller: PUT /api/content/{eventDraftId}/select
|
||||
activate Controller
|
||||
|
||||
Controller -> Controller: 요청 검증\n(필수 필드, URL 유효성)
|
||||
|
||||
Controller -> Service: updateEventContent(eventDraftId, userId,\nselectedImageUrl, editedContent)
|
||||
activate Service
|
||||
|
||||
== 1단계: 이벤트 초안 조회 및 검증 ==
|
||||
|
||||
Service -> Repo: findById(eventDraftId)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 초안 조회\n(초안ID와 사용자ID로 조회)
|
||||
Repo -> DB: 이벤트 초안 조회\n(초안ID로 조회)
|
||||
activate DB
|
||||
DB --> Repo: EventDraft
|
||||
DB --> Repo: EventDraft 엔티티\n{목적, 추천안, 상태}
|
||||
deactivate DB
|
||||
Repo --> Service: EventDraft entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Service: validateOwnership(userId, eventDraft)
|
||||
note right
|
||||
소유권 검증:
|
||||
- 사용자ID와 초안 소유자 일치 확인
|
||||
- 권한 없으면 403 Forbidden
|
||||
end note
|
||||
|
||||
Service -> Service: validateImageUrl(selectedImageUrl)
|
||||
note right: 선택한 이미지 URL\n유효성 검증
|
||||
note right
|
||||
이미지 URL 검증:
|
||||
- URL 형식 유효성
|
||||
- CDN 경로 확인
|
||||
- 이미지 존재 여부
|
||||
end note
|
||||
|
||||
== 2단계: 콘텐츠 편집 적용 ==
|
||||
|
||||
Service -> Service: applyContentEdits(eventDraft, editedContent)
|
||||
note right: 편집 내용 적용:\n- 텍스트 수정\n- 색상 변경
|
||||
note right
|
||||
편집 내용 적용:
|
||||
- 제목 텍스트
|
||||
- 경품 정보 텍스트
|
||||
- 참여 안내 텍스트
|
||||
- 배경색
|
||||
- 텍스트 색상
|
||||
- 강조 색상
|
||||
end note
|
||||
|
||||
== 3단계: DB 저장 ==
|
||||
|
||||
Service -> Repo: update(eventDraft)
|
||||
activate Repo
|
||||
Repo -> DB: 이벤트 초안 업데이트\n(선택된 이미지 URL,\n편집된 제목/텍스트,\n배경색/텍스트색,\n수정일시를 업데이트)
|
||||
Repo -> DB: 이벤트 초안 업데이트\n(선택된 이미지 URL,\n편집된 제목/텍스트,\n배경색/텍스트색,\n수정일시 저장)
|
||||
activate DB
|
||||
DB --> Repo: OK
|
||||
DB --> Repo: 업데이트 완료
|
||||
deactivate DB
|
||||
Repo --> Service: EventDraft entity
|
||||
deactivate Repo
|
||||
|
||||
Service -> Cache: delete("purpose:" + userId)
|
||||
== 4단계: 캐시 무효화 ==
|
||||
|
||||
Service -> Cache: 캐시 삭제\nKey: content:image:{eventDraftId}
|
||||
activate Cache
|
||||
note right: 캐시 무효화
|
||||
Cache --> Service: OK
|
||||
note right
|
||||
Redis 캐시 무효화:
|
||||
- 이미지 URL 캐시 삭제
|
||||
- 선택 완료 후 불필요
|
||||
end note
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
== 5단계: 응답 반환 ==
|
||||
|
||||
Service --> Controller: EventContentResponse\n{eventDraftId, selectedImageUrl,\neditedContent}
|
||||
deactivate Service
|
||||
Controller --> Client: 200 OK\n{eventDraftId}
|
||||
|
||||
note over Controller, Cache: 콘텐츠 편집 내용:\n- 제목 텍스트\n- 경품 정보 텍스트\n- 참여 안내 텍스트\n- 배경색\n- 텍스트 색상\n- 강조 색상
|
||||
Controller --> Gateway: 200 OK\n{"eventDraftId": 123,\n"status": "콘텐츠 선택 완료",\n"selectedImageUrl": "...",\n"editedContent": {...}}
|
||||
deactivate Controller
|
||||
|
||||
Gateway --> Client: 200 OK\n콘텐츠 선택 완료
|
||||
deactivate Gateway
|
||||
|
||||
note over Client, Cache
|
||||
**저장 내용**
|
||||
- 선택된 이미지 URL
|
||||
- 편집된 텍스트 (제목, 경품 정보, 참여 안내)
|
||||
- 편집된 색상 (배경색, 텍스트색, 강조색)
|
||||
- 다음 단계: 최종 승인 및 배포
|
||||
|
||||
**성능 목표**
|
||||
- 응답 시간: 0.5초 이내
|
||||
- DB 업데이트: 0.1초
|
||||
- 캐시 삭제: 0.01초
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user