@startuml 회의종료및Todo추출 !theme mono title 회의 종료 및 Todo 추출 플로우 (UFR-MEET-040/050, UFR-AI-020, UFR-TODO-010) actor "사용자" as User participant "Frontend" as FE participant "API Gateway" as GW participant "Meeting Service" as MS participant "AI Service" as AI participant "Todo Service" as TS participant "Notification Service" as NS participant "Redis" as Cache database "Meeting DB" as MDB database "AI DB" as AIDB database "Todo DB" as TODB queue "RabbitMQ" as MQ participant "LLM Server" as LLM == 회의 종료 == User -> FE: 회의 종료 버튼 클릭 activate FE FE -> GW: POST /api/meetings/{meetingId}/end activate GW GW -> MS: 회의 종료 요청 activate MS MS -> MDB: UPDATE meetings\nSET end_time = NOW(), status = 'completed'\n통계 생성 (duration, 참석자 수, 발언 수) activate MDB MDB --> MS: 회의 종료 시간 기록 및 통계 생성 완료 deactivate MDB MS ->> MQ: MeetingEnded 이벤트 발행\n{meetingId, sessionId, endTime, statistics} activate MQ note right of MQ 이벤트: MeetingEnded Routing Key: meeting.ended end note MQ -->> MS: ACK deactivate MQ MS --> GW: 200 OK\n{status: "completed", statistics} deactivate MS GW --> FE: 회의 종료 확인 deactivate GW FE --> User: 회의 종료 화면 표시 deactivate FE == AI Todo 자동 추출 (비동기) == MQ ->> AI: MeetingEnded 이벤트 전달 activate AI note right of AI 구독: meeting.ended 큐: ai.meeting.ended end note AI -> AIDB: SELECT transcript, summary\nFROM minutes_draft\nWHERE session_id = {sessionId} activate AIDB AIDB --> AI: 회의록 전체 내용 반환 deactivate AIDB AI -> AI: 액션 아이템 식별\n(동사 패턴, 시간 표현, 담당자 언급) AI -> LLM: Todo 추출 요청\n{transcript, context, attendees} activate LLM LLM --> AI: Todo 목록 반환\n{tasks: [{description, assignee, dueDate, priority, linkedSection}]} deactivate LLM AI -> AIDB: UPDATE minutes_draft\nSET todos_extracted = TRUE\n(Todo 추출 완료 표시) activate AIDB AIDB --> AI: OK deactivate AIDB AI ->> MQ: TodoExtracted 이벤트 발행\n{meetingId, todos: [{description, assignee, dueDate, priority, linkedSection}]} activate MQ note right of MQ 이벤트: TodoExtracted Routing Key: todo.extracted end note MQ -->> AI: ACK deactivate MQ AI -->> MQ: ACK (MeetingEnded) deactivate AI == Todo 생성 및 할당 (비동기) == MQ ->> TS: TodoExtracted 이벤트 전달 activate TS note right of TS 구독: todo.extracted 큐: todo.extracted end note loop 각 Todo TS -> TODB: INSERT INTO todos\n(Todo 생성, 담당자 할당, 회의록 섹션 링크) activate TODB TODB --> TS: Todo ID 반환 deactivate TODB TS -> Cache: SET todo:list:{userId}\n(사용자별 Todo 목록 캐싱, TTL: 5분) activate Cache Cache --> TS: OK deactivate Cache TS ->> MQ: TodoCreated 이벤트 발행\n{todoId, assignee, description, dueDate, linkedSection} activate MQ note right of MQ 이벤트: TodoCreated Routing Key: todo.created end note MQ -->> TS: ACK deactivate MQ end TS -->> MQ: ACK (TodoExtracted) deactivate TS == Todo 할당 알림 (비동기) == MQ ->> NS: TodoCreated 이벤트 전달 activate NS note right of NS 구독: todo.created 큐: notification.todo.created end note NS -> NS: 알림 템플릿 생성\n(Todo 설명, 기한, 회의록 링크) NS -> NS: 담당자에게 알림 발송\n(이메일, 푸시 알림) NS -->> MQ: ACK deactivate NS == 회의 통계 및 Todo 목록 조회 == User -> FE: 회의 상세 화면 조회 activate FE FE -> GW: GET /api/meetings/{meetingId}/summary activate GW GW -> MS: 회의 통계 및 Todo 목록 조회 activate MS MS -> Cache: GET meeting:info:{meetingId} activate Cache alt Cache Hit Cache --> MS: 회의 정보 반환 else Cache Miss Cache --> MS: null MS -> MDB: SELECT * FROM meetings\nWHERE meeting_id = {meetingId} activate MDB MDB --> MS: 회의 정보 반환 deactivate MDB MS -> Cache: SET meeting:info:{meetingId}\n(TTL: 10분) Cache --> MS: OK end deactivate Cache MS -> TS: GET /api/todos?meetingId={meetingId} activate TS TS -> Cache: GET todo:list:meeting:{meetingId} activate Cache alt Cache Hit Cache --> TS: Todo 목록 반환 else Cache Miss Cache --> TS: null TS -> TODB: SELECT * FROM todos\nWHERE meeting_id = {meetingId} activate TODB TODB --> TS: Todo 목록 반환 deactivate TODB TS -> Cache: SET todo:list:meeting:{meetingId}\n(TTL: 5분) Cache --> TS: OK end deactivate Cache TS --> MS: Todo 목록 반환 deactivate TS MS --> GW: 회의 통계 및 Todo 목록 반환\n{statistics, todos} deactivate MS GW --> FE: 200 OK deactivate GW FE --> User: 회의 통계 및 Todo 목록 표시 deactivate FE @enduml