@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<<E>>" as DB
database "Redis Cache<<E>>" as Cache
queue "Azure Event Hubs<<E>>" 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<AISuggestion>
    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 서비스에 녹음 중지 요청
    - 참석자에게 회의 종료 알림
    end note

    ' MeetingEnded 이벤트 발행 (STT Service 구독)
    Service -> EventHub: publish(MeetingEnded)\n{\n  meetingId, sessionId,\n  endedAt, statistics\n}
    activate EventHub
    note right
      STT Service가 구독:
      - Consumer Group: stt-service-group
      - 음성 녹음 중지 처리
    end note
    EventHub --> Service: 발행 완료
    deactivate EventHub

    ' NotificationRequest 이벤트 발행 (Notification Service 구독)
    Service -> EventHub: publish(NotificationRequest)\n{\n  channel: EMAIL,\n  recipients: participants,\n  message: "회의 종료 안내",\n  metadata: {\n    meetingId, statistics,\n    minutesId,\n    aiEnhancement: {\n      merged: true,\n      count: N\n    }\n  }\n}
    activate EventHub
    note right
      Notification Service가 구독:
      - Consumer Group: notification-service-group
      - 참석자 전원에게 종료 알림 발송
    end note
    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
