@startuml meeting-회의종료_new !theme mono title Meeting Service - 회의종료 내부 시퀀스 (AI 제안 데이터 반영 포함) participant "MeetingController" as Controller participant "MeetingService" as Service participant "SessionService" as SessionService participant "AISuggestionService" as AISuggestion participant "MinutesService" as MinutesService participant "MeetingRepository" as MeetingRepo participant "SessionRepository" as SessionRepo participant "AISuggestionRepository" as SuggestionRepo participant "MinutesRepository" as MinutesRepo participant "StatisticsService" as StatService database "Meeting DB<>" as DB database "Redis Cache<>" as Cache queue "Azure Event Hubs<>" as EventHub [-> Controller: POST /meetings/{meetingId}/end activate Controller note over Controller 경로 변수: meetingId 사용자 정보: userId, userName, email end note Controller -> Controller: meetingId 유효성 검증 Controller -> Service: endMeeting(meetingId, userId) activate Service ' 회의 정보 조회 Service -> MeetingRepo: findById(meetingId) activate MeetingRepo MeetingRepo -> DB: 회의 정보 조회\n(회의ID 기준) activate DB DB --> MeetingRepo: 회의 정보 deactivate DB MeetingRepo --> Service: Meeting deactivate MeetingRepo note over Service 비즈니스 규칙 검증: - 회의가 존재하는지 확인 - 회의 종료 권한 확인 (생성자만) - 회의 상태 확인 (IN_PROGRESS만 종료 가능) end note Service -> Service: 권한 검증\n(생성자만 종료 가능) Service -> Service: 회의 상태 확인 alt 회의가 진행 중이 아님 Service --> Controller: 409 Conflict\n진행 중인 회의가 아님 note right 에러 응답 형식: { "error": { "code": "MEETING_NOT_IN_PROGRESS", "message": "진행 중인 회의가 아닙니다", "details": "회의 종료는 진행 중 상태에서만 가능합니다", "timestamp": "2025-10-23T12:00:00Z", "path": "/api/meetings/{meetingId}/end" } } end note return 409 Conflict else 종료 가능 ' 세션 정보 조회 Service -> SessionRepo: findActiveSession(meetingId) activate SessionRepo SessionRepo -> DB: 활성 세션 조회\n(회의ID, 상태='진행중') activate DB DB --> SessionRepo: 세션 정보 deactivate DB SessionRepo --> Service: Session deactivate SessionRepo ' 세션 종료 Service -> SessionRepo: endSession(sessionId) activate SessionRepo SessionRepo -> DB: 세션 종료 처리\n(상태='종료', 종료일시) activate DB DB --> SessionRepo: 업데이트 완료 deactivate DB SessionRepo --> Service: 종료 성공 deactivate SessionRepo ' 회의 상태 업데이트 Service -> MeetingRepo: updateStatus(meetingId, "ENDED") activate MeetingRepo MeetingRepo -> DB: 회의 상태 업데이트\n(상태='종료', 실제종료시각) activate DB DB --> MeetingRepo: 업데이트 완료 deactivate DB MeetingRepo --> Service: 업데이트 성공 deactivate MeetingRepo note over Service 회의 통계 생성: - 회의 총 시간 - 참석자 수 - 발언 횟수 (STT 데이터 기반) - 주요 키워드 end note ' 회의 통계 생성 Service -> StatService: generateMeetingStatistics(sessionId) activate StatService StatService -> DB: 회의 통계 데이터 조회\n(화자수, 발언횟수, 진행시간) activate DB DB --> StatService: 통계 데이터 deactivate DB StatService -> DB: 회의 통계 저장\n(회의ID, 세션ID, 진행시간, 참석자수, 발언횟수) activate DB DB --> StatService: 통계 저장 완료 deactivate DB StatService --> Service: Statistics deactivate StatService == AI 제안 데이터를 회의록에 최종 반영 == note over Service 핵심 추가 로직: 회의 진행 중 사용자가 "적용"한 AI 제안들을 회의록(minutes)에 최종 반영하여 완성도 향상 end note ' 1. APPLIED 상태의 AI 제안 조회 Service -> AISuggestion: getAppliedSuggestions(meetingId) activate AISuggestion AISuggestion -> SuggestionRepo: findAppliedSuggestions(meetingId) activate SuggestionRepo SuggestionRepo -> DB: 적용된 AI 제안 조회\n(회의ID, 상태='적용됨', 유형별 정렬) activate DB note right 조회 대상: - suggestion_type: DISCUSSION, DECISION, TODO - status: APPLIED (사용자가 적용한 것만) - content: 제안 내용 - priority, confidence_score 등 end note DB --> SuggestionRepo: AI 제안 목록 deactivate DB SuggestionRepo --> AISuggestion: List deactivate SuggestionRepo AISuggestion --> Service: appliedSuggestions deactivate AISuggestion ' 2. 회의록 조회 Service -> MinutesRepo: findByMeetingId(meetingId) activate MinutesRepo MinutesRepo -> DB: 회의록 정보 조회\n(회의ID 기준) activate DB DB --> MinutesRepo: 회의록 정보 deactivate DB MinutesRepo --> Service: Minutes deactivate MinutesRepo ' 3. AI 제안을 회의록 섹션별로 반영 Service -> MinutesService: mergeAISuggestionsToMinutes(minutesId, appliedSuggestions) activate MinutesService note over MinutesService 처리 단계: 1. suggestion_type별로 그룹화 - DISCUSSION → 논의사항 섹션 - DECISION → 결정사항 섹션 - TODO → 액션아이템 섹션 2. 각 섹션별로 기존 내용과 병합 3. 중복 제거 및 정렬 4. minutes 테이블에 최종 반영 end note loop 각 suggestion_type별 (DISCUSSION, DECISION, TODO) MinutesService -> MinutesService: 해당 타입의 제안 필터링 MinutesService -> MinutesRepo: getSectionContent(minutesId, sectionType) activate MinutesRepo MinutesRepo -> DB: 회의록 섹션 내용 조회\n(회의록ID, 섹션유형) activate DB DB --> MinutesRepo: 기존 섹션 내용 deactivate DB MinutesRepo --> MinutesService: sectionContent deactivate MinutesRepo MinutesService -> MinutesService: 기존 내용과 AI 제안 병합 note right 병합 규칙: - 중복 항목 제거 (유사도 90% 이상) - 우선순위/신뢰도 높은 순 정렬 - 사용자 수동 작성 내용 우선 - AI 제안은 추가/보완 역할 - 타임스탬프 기준 정렬 end note MinutesService -> MinutesRepo: updateSectionContent(minutesId, sectionType, mergedContent) activate MinutesRepo MinutesRepo -> DB: 회의록 섹션 내용 업데이트\n(병합된 내용, AI보강 플래그, 갱신일시) activate DB note right 저장 내용: - section_content: 병합된 최종 내용 - ai_enhanced: AI가 보강했음을 표시 - updated_at: 최종 업데이트 시간 end note DB --> MinutesRepo: 업데이트 완료 deactivate DB MinutesRepo --> MinutesService: 성공 deactivate MinutesRepo end MinutesService --> Service: 병합 완료 deactivate MinutesService ' 4. AI 제안 상태 업데이트 (APPLIED → MERGED) Service -> SuggestionRepo: updateSuggestionsStatus(appliedSuggestions, "MERGED") activate SuggestionRepo SuggestionRepo -> DB: AI 제안 상태 업데이트\n(상태='병합완료', 병합일시) activate DB note right 상태 변경: - APPLIED → MERGED - 회의록에 최종 반영 완료 표시 - merged_at: 반영 시간 기록 end note DB --> SuggestionRepo: 업데이트 완료 deactivate DB SuggestionRepo --> Service: 성공 deactivate SuggestionRepo ' 회의록 상태 업데이트 Service -> MinutesRepo: updateMinutesStatus(meetingId, "DRAFT") activate MinutesRepo MinutesRepo -> DB: 회의록 상태 업데이트\n(상태='초안', 종료일시, AI제안병합 플래그, 병합건수) activate DB note right 회의록 완성도 표시: - ai_suggestions_merged: AI 제안 반영됨 - merged_count: 반영된 제안 수 - 사용자가 최종 검토 가능한 DRAFT 상태 end note DB --> MinutesRepo: 업데이트 완료 deactivate DB MinutesRepo --> Service: 업데이트 성공 deactivate MinutesRepo ' 캐시 무효화 Service -> Cache: DELETE meeting:info:{meetingId} activate Cache Cache --> Service: 삭제 완료 deactivate Cache Service -> Cache: DELETE minutes:content:{minutesId} activate Cache Cache --> Service: 삭제 완료 deactivate Cache note over Service 비동기 이벤트 발행: - STT 서비스에 녹음 중지 요청 - AI 서비스에 최종 회의록 생성 요청 - 참석자에게 회의 종료 알림 end note ' 이벤트 발행 Service -> EventHub: publish(MeetingEnded)\n{\n meetingId, sessionId,\n endedAt, statistics,\n minutesId,\n aiSuggestionsMerged: true,\n mergedCount: N\n} activate EventHub EventHub --> Service: 발행 완료 deactivate EventHub Service --> Controller: MeetingEndResponse deactivate Service note over Controller 응답 데이터: { "meetingId": "uuid", "sessionId": "uuid", "status": "ENDED", "endedAt": "2025-01-23T15:00:00", "statistics": { "duration": 60, "participantCount": 5, "utteranceCount": 120 }, "minutesId": "uuid", "aiEnhancement": { "merged": true, "suggestionsApplied": 8, "sections": { "discussion": 3, "decision": 2, "todo": 3 } } } end note return 200 OK\nMeetingEndResponse end deactivate Controller note over Controller, EventHub 처리 시간: - 회의/세션 종료: 200-300ms - 통계 생성: 500-800ms - AI 제안 조회: 200-300ms - 회의록 병합: 1-2초 (제안 수에 따라) - 상태 업데이트: 300-500ms - 캐시 무효화: 100-200ms 총 처리 시간: 약 3-5초 AI 제안 병합 규칙: - APPLIED 상태의 제안만 반영 - 중복 제거 (유사도 90% 이상) - 우선순위/신뢰도 높은 순 - 사용자 수동 작성 내용 우선 - 섹션별 최대 제안 수 제한 없음 - 모든 제안은 추적 가능 (merged_at) 트랜잭션 관리: - 회의 종료, AI 제안 병합, 상태 업데이트는 단일 트랜잭션 - 실패 시 전체 롤백 - 이벤트 발행은 트랜잭션 성공 후 end note @enduml