회의종료 시퀀스에 AI 제안 반영 로직 추가

This commit is contained in:
djeon 2025-10-22 17:40:40 +09:00
parent 18f2416414
commit 8dbc1df647
2 changed files with 336 additions and 11 deletions

View File

@ -1,13 +1,17 @@
@startuml meeting-회의종료 @startuml meeting-회의종료_new
!theme mono !theme mono
title Meeting Service - 회의종료 내부 시퀀스 title Meeting Service - 회의종료 내부 시퀀스 (AI 제안 데이터 반영 포함)
participant "MeetingController" as Controller participant "MeetingController" as Controller
participant "MeetingService" as Service participant "MeetingService" as Service
participant "SessionService" as SessionService participant "SessionService" as SessionService
participant "AISuggestionService" as AISuggestion
participant "MinutesService" as MinutesService
participant "MeetingRepository" as MeetingRepo participant "MeetingRepository" as MeetingRepo
participant "SessionRepository" as SessionRepo participant "SessionRepository" as SessionRepo
participant "AISuggestionRepository" as SuggestionRepo
participant "MinutesRepository" as MinutesRepo
participant "StatisticsService" as StatService participant "StatisticsService" as StatService
database "Meeting DB<<E>>" as DB database "Meeting DB<<E>>" as DB
database "Redis Cache<<E>>" as Cache database "Redis Cache<<E>>" as Cache
@ -106,15 +110,136 @@ else 종료 가능
StatService --> Service: Statistics StatService --> Service: Statistics
deactivate StatService deactivate StatService
' 회의록 상태 업데이트 == AI 제안 데이터를 회의록에 최종 반영 ==
Service -> MeetingRepo: updateMinutesStatus(meetingId, "DRAFT")
activate MeetingRepo note over Service
MeetingRepo -> DB: UPDATE minutes\nSET status = 'DRAFT',\n endedAt = NOW()\nWHERE meetingId = ?\nAND status = 'IN_PROGRESS' 핵심 추가 로직:
회의 진행 중 사용자가 "적용"한 AI 제안들을
회의록(minutes)에 최종 반영하여 완성도 향상
end note
' 1. APPLIED 상태의 AI 제안 조회
Service -> AISuggestion: getAppliedSuggestions(meetingId)
activate AISuggestion
AISuggestion -> SuggestionRepo: findAppliedSuggestions(meetingId)
activate SuggestionRepo
SuggestionRepo -> DB: SELECT *\nFROM ai_suggestions\nWHERE meeting_id = ?\nAND status = 'APPLIED'\nORDER BY suggestion_type, applied_at
activate DB activate DB
DB --> MeetingRepo: 업데이트 완료 note right
조회 대상:
- suggestion_type: DISCUSSION, DECISION, TODO
- status: APPLIED (사용자가 적용한 것만)
- content: 제안 내용
- priority, confidence_score 등
end note
DB --> SuggestionRepo: AI 제안 목록
deactivate DB deactivate DB
MeetingRepo --> Service: 업데이트 성공 SuggestionRepo --> AISuggestion: List<AISuggestion>
deactivate MeetingRepo deactivate SuggestionRepo
AISuggestion --> Service: appliedSuggestions
deactivate AISuggestion
' 2. 회의록 조회
Service -> MinutesRepo: findByMeetingId(meetingId)
activate MinutesRepo
MinutesRepo -> DB: SELECT * FROM minutes\nWHERE meeting_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: SELECT section_content\nFROM minutes_sections\nWHERE minutes_id = ?\nAND section_type = ?
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: UPDATE minutes_sections\nSET section_content = ?,\n updated_at = NOW(),\n ai_enhanced = true\nWHERE minutes_id = ?\nAND section_type = ?
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: UPDATE ai_suggestions\nSET status = 'MERGED',\n merged_at = NOW()\nWHERE id IN (...)
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: UPDATE minutes\nSET status = 'DRAFT',\n endedAt = NOW(),\n ai_suggestions_merged = true,\n merged_count = ?\nWHERE meetingId = ?\nAND status = 'IN_PROGRESS'
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} Service -> Cache: DELETE meeting:info:{meetingId}
@ -122,6 +247,11 @@ else 종료 가능
Cache --> Service: 삭제 완료 Cache --> Service: 삭제 완료
deactivate Cache deactivate Cache
Service -> Cache: DELETE minutes:content:{minutesId}
activate Cache
Cache --> Service: 삭제 완료
deactivate Cache
note over Service note over Service
비동기 이벤트 발행: 비동기 이벤트 발행:
- STT 서비스에 녹음 중지 요청 - STT 서비스에 녹음 중지 요청
@ -130,7 +260,7 @@ else 종료 가능
end note end note
' 이벤트 발행 ' 이벤트 발행
Service -> EventHub: publish(MeetingEnded)\n{\n meetingId, sessionId,\n endedAt, statistics,\n minutesId\n} Service -> EventHub: publish(MeetingEnded)\n{\n meetingId, sessionId,\n endedAt, statistics,\n minutesId,\n aiSuggestionsMerged: true,\n mergedCount: N\n}
activate EventHub activate EventHub
EventHub --> Service: 발행 완료 EventHub --> Service: 발행 완료
deactivate EventHub deactivate EventHub
@ -150,7 +280,16 @@ else 종료 가능
"participantCount": 5, "participantCount": 5,
"utteranceCount": 120 "utteranceCount": 120
}, },
"minutesId": "uuid" "minutesId": "uuid",
"aiEnhancement": {
"merged": true,
"suggestionsApplied": 8,
"sections": {
"discussion": 3,
"decision": 2,
"todo": 3
}
}
} }
end note end note
@ -159,4 +298,28 @@ end
deactivate Controller 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 @enduml

View File

@ -0,0 +1,162 @@
@startuml meeting-회의종료
!theme mono
title Meeting Service - 회의종료 내부 시퀀스
participant "MeetingController" as Controller
participant "MeetingService" as Service
participant "SessionService" as SessionService
participant "MeetingRepository" as MeetingRepo
participant "SessionRepository" as SessionRepo
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: SELECT * FROM meetings WHERE 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진행 중인 회의가 아님
return 409 Conflict
else 종료 가능
' 세션 정보 조회
Service -> SessionRepo: findActiveSession(meetingId)
activate SessionRepo
SessionRepo -> DB: SELECT * FROM meeting_sessions\nWHERE meetingId = ?\nAND status = 'ACTIVE'
activate DB
DB --> SessionRepo: 세션 정보
deactivate DB
SessionRepo --> Service: Session
deactivate SessionRepo
' 세션 종료
Service -> SessionRepo: endSession(sessionId)
activate SessionRepo
SessionRepo -> DB: UPDATE meeting_sessions\nSET status = 'ENDED',\n endedAt = NOW()\nWHERE id = ?
activate DB
DB --> SessionRepo: 업데이트 완료
deactivate DB
SessionRepo --> Service: 종료 성공
deactivate SessionRepo
' 회의 상태 업데이트
Service -> MeetingRepo: updateStatus(meetingId, "ENDED")
activate MeetingRepo
MeetingRepo -> DB: UPDATE meetings\nSET status = 'ENDED',\n actualEndTime = NOW()\nWHERE id = ?
activate DB
DB --> MeetingRepo: 업데이트 완료
deactivate DB
MeetingRepo --> Service: 업데이트 성공
deactivate MeetingRepo
note over Service
회의 통계 생성:
- 회의 총 시간
- 참석자 수
- 발언 횟수 (STT 데이터 기반)
- 주요 키워드
end note
' 회의 통계 생성
Service -> StatService: generateMeetingStatistics(sessionId)
activate StatService
StatService -> DB: SELECT\n COUNT(DISTINCT speakerId) as speakerCount,\n COUNT(*) as utteranceCount,\n TIMESTAMPDIFF(MINUTE, startedAt, endedAt) as duration\nFROM transcripts WHERE sessionId = ?
activate DB
DB --> StatService: 통계 데이터
deactivate DB
StatService -> DB: INSERT INTO meeting_statistics\n(meetingId, sessionId, duration,\nparticipantCount, utteranceCount, createdAt)
activate DB
DB --> StatService: 통계 저장 완료
deactivate DB
StatService --> Service: Statistics
deactivate StatService
' 회의록 상태 업데이트
Service -> MeetingRepo: updateMinutesStatus(meetingId, "DRAFT")
activate MeetingRepo
MeetingRepo -> DB: UPDATE minutes\nSET status = 'DRAFT',\n endedAt = NOW()\nWHERE meetingId = ?\nAND status = 'IN_PROGRESS'
activate DB
DB --> MeetingRepo: 업데이트 완료
deactivate DB
MeetingRepo --> Service: 업데이트 성공
deactivate MeetingRepo
' 캐시 무효화
Service -> Cache: DELETE meeting:info:{meetingId}
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}
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"
}
end note
return 200 OK\nMeetingEndResponse
end
deactivate Controller
@enduml