@startuml 회의록확정및공유 !theme mono title 회의록 확정 및 공유 플로우 (UFR-MEET-060) actor "사용자" as User participant "Frontend" as FE participant "API Gateway" as GW participant "Meeting Service" as MS participant "Notification Service" as NS participant "Redis" as Cache database "Meeting DB" as MDB queue "RabbitMQ" as MQ == 회의록 확정 == User -> FE: 회의록 확정 버튼 클릭 activate FE FE -> GW: POST /api/meetings/{meetingId}/minutes/finalize activate GW GW -> MS: 회의록 확정 요청 activate MS MS -> MS: 필수 항목 검사\n(제목, 참석자, 논의 내용, 결정 사항) alt 필수 항목 누락 MS --> GW: 400 Bad Request\n{error: "필수 항목 누락", missingFields} deactivate MS GW --> FE: 오류 반환 deactivate GW FE --> User: 필수 항목 누락 안내 deactivate FE else 필수 항목 완료 MS -> MDB: INSERT INTO minutes_versions\n(확정 버전 생성, 확정 시간 기록)\nUPDATE meetings SET minutes_status = 'finalized' activate MDB MDB --> MS: 확정 버전 ID 반환 deactivate MDB MS -> Cache: DEL meeting:info:{meetingId}\n(캐시 무효화) activate Cache Cache --> MS: OK deactivate Cache MS --> GW: 200 OK\n{versionId, status: "finalized"} deactivate MS GW --> FE: 회의록 확정 완료 deactivate GW FE --> User: 회의록 확정 완료 화면 표시 deactivate FE end == 회의록 공유 설정 == User -> FE: 공유 설정 입력\n(대상: 참석자/전체/특정인, 권한: 읽기/편집, 방식: 링크/이메일) activate FE FE -> GW: POST /api/meetings/{meetingId}/share\n{targets, permission, method, password, expiresAt} activate GW GW -> MS: 공유 설정 요청 activate MS MS -> MS: 고유 공유 링크 생성\n(UUID 기반 URL) MS -> MDB: INSERT INTO share_settings\n(공유 대상, 권한, 링크, 비밀번호, 유효기간 저장) activate MDB MDB --> MS: 공유 설정 ID 반환 deactivate MDB MS -> Cache: SET share:link:{shareId}\n(공유 링크 정보 캐싱, TTL: 유효기간) activate Cache Cache --> MS: OK deactivate Cache MS ->> MQ: TranscriptShared 이벤트 발행\n{meetingId, shareId, targets, shareUrl, method} activate MQ note right of MQ 이벤트: TranscriptShared Routing Key: transcript.shared end note MQ -->> MS: ACK deactivate MQ MS --> GW: 200 OK\n{shareUrl, shareId, expiresAt} deactivate MS GW --> FE: 공유 링크 반환 deactivate GW FE --> User: 공유 링크 및 설정 표시 deactivate FE == 공유 알림 발송 (비동기) == MQ ->> NS: TranscriptShared 이벤트 전달 activate NS note right of NS 구독: transcript.shared 큐: notification.transcript.shared end note alt 공유 방식: 이메일 loop 각 대상 NS -> NS: 이메일 템플릿 생성\n(회의 제목, 공유 링크, 유효기간, 비밀번호) NS -> NS: 이메일 발송 end else 공유 방식: 링크만 NS -> NS: 알림 생성\n(링크 복사 완료) end NS -->> MQ: ACK deactivate NS == 다음 회의 일정 자동 등록 (선택) == opt 회의록에 다음 회의 언급 존재 User -> FE: 다음 회의 일정 자동 등록 확인 activate FE FE -> GW: POST /api/meetings/{meetingId}/schedule-next activate GW GW -> MS: 다음 회의 일정 등록 요청 activate MS MS -> MDB: SELECT next_meeting_info\nFROM minutes\nWHERE meeting_id = {meetingId} activate MDB MDB --> MS: 다음 회의 정보 반환\n{suggestedDate, suggestedTime, suggestedAttendees} deactivate MDB MS -> MS: 캘린더 등록 데이터 생성\n(제목, 날짜, 시간, 참석자) MS -> MDB: INSERT INTO meetings\n(다음 회의 생성, 이전 회의 ID 링크) activate MDB MDB --> MS: 다음 회의 ID 반환 deactivate MDB MS ->> MQ: MeetingCreated 이벤트 발행\n(다음 회의 초대) activate MQ MQ -->> MS: ACK deactivate MQ MS --> GW: 200 OK\n{nextMeetingId, status: "scheduled"} deactivate MS GW --> FE: 다음 회의 등록 완료 deactivate GW FE --> User: 다음 회의 등록 완료 표시 deactivate FE end == 공유 링크 접근 (외부 사용자) == actor "외부 사용자" as External External -> FE: 공유 링크 접근\n(GET /share/{shareId}) activate FE FE -> GW: 공유 링크 검증 요청 activate GW GW -> MS: 공유 설정 조회 activate MS MS -> Cache: GET share:link:{shareId} activate Cache alt Cache Hit Cache --> MS: 공유 설정 반환 else Cache Miss Cache --> MS: null MS -> MDB: SELECT * FROM share_settings\nWHERE share_id = {shareId}\nAND expires_at > NOW() activate MDB alt 유효한 공유 링크 MDB --> MS: 공유 설정 반환 deactivate MDB MS -> Cache: SET share:link:{shareId}\n(TTL: 남은 유효기간) Cache --> MS: OK else 만료 또는 존재하지 않음 MDB --> MS: null deactivate MDB Cache --> MS: null MS --> GW: 404 Not Found\n{error: "공유 링크가 만료되었거나 존재하지 않습니다"} deactivate MS GW --> FE: 오류 반환 deactivate GW FE --> External: 오류 화면 표시 deactivate FE end end deactivate Cache alt 비밀번호 설정된 경우 MS --> GW: 비밀번호 요청 deactivate MS GW --> FE: 비밀번호 입력 화면 deactivate GW External -> FE: 비밀번호 입력 FE -> GW: 비밀번호 검증 요청 activate GW GW -> MS: 비밀번호 검증 activate MS alt 비밀번호 일치 MS -> MDB: SELECT minutes\nFROM meetings\nWHERE meeting_id = {meetingId} activate MDB MDB --> MS: 회의록 반환 deactivate MDB MS --> GW: 200 OK\n{minutes, permission} deactivate MS GW --> FE: 회의록 데이터 반환 deactivate GW FE --> External: 회의록 표시\n(권한에 따라 읽기/편집) deactivate FE else 비밀번호 불일치 MS --> GW: 401 Unauthorized\n{error: "비밀번호가 일치하지 않습니다"} deactivate MS GW --> FE: 오류 반환 deactivate GW FE --> External: 오류 화면 표시 deactivate FE end else 비밀번호 미설정 MS -> MDB: SELECT minutes\nFROM meetings\nWHERE meeting_id = {meetingId} activate MDB MDB --> MS: 회의록 반환 deactivate MDB MS --> GW: 200 OK\n{minutes, permission} deactivate MS GW --> FE: 회의록 데이터 반환 deactivate GW FE --> External: 회의록 표시\n(권한에 따라 읽기/편집) deactivate FE end @enduml