mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 23:06:23 +00:00
34 KiB
34 KiB
Meeting Service 데이터 플로우 다이어그램
1. 전체 시스템 플로우
┌──────────────────────────────────────────────────────────────────────────────┐
│ 회의 생명주기 데이터 플로우 │
└──────────────────────────────────────────────────────────────────────────────┘
Phase 1: 회의 준비 단계
════════════════════════════════════════════════════════════════════════════════
사용자가 회의 생성
↓
┌─────────────────────────────────────────────────────────────┐
│ 1-1. CreateMeeting API │
│ ────────────────────────────────────────────────────────────│
│ INSERT INTO meetings ( │
│ meeting_id, title, purpose, scheduled_at, │
│ organizer_id, status, created_at │
│ ) │
│ VALUES (...) │
│ │
│ + INSERT INTO meeting_participants [V2] │
│ (meeting_id, user_id, invitation_status) │
│ FOR EACH participant │
└─────────────────────────────────────────────────────────────┘
↓
DB State:
✓ meetings: SCHEDULED status
✓ meeting_participants: PENDING status
Phase 2: 회의 진행 중
════════════════════════════════════════════════════════════════════════════════
회의 시작 (start_meeting API)
↓
┌─────────────────────────────────────────────────────────────┐
│ 2-1. StartMeeting UseCase │
│ ────────────────────────────────────────────────────────────│
│ UPDATE meetings SET │
│ status = 'IN_PROGRESS', │
│ started_at = NOW() │
│ WHERE meeting_id = ? │
└─────────────────────────────────────────────────────────────┘
↓
회의 중 회의록 작성
↓
┌─────────────────────────────────────────────────────────────┐
│ 2-2. CreateMinutes API (회의 시작 후) │
│ ────────────────────────────────────────────────────────────│
│ INSERT INTO minutes ( │
│ id, meeting_id, user_id, title, status, │
│ created_by, version, created_at │
│ ) VALUES ( │
│ 'consolidated-minutes-1', 'meeting-001', │
│ NULL, [V3] ← AI 통합 회의록 표시 │
│ '2025년 1월 10일 회의', 'DRAFT', ... │
│ ) │
│ │
│ + 각 참석자별 회의록도 동시 생성: │
│ INSERT INTO minutes ( │
│ id, meeting_id, user_id, ... │
│ ) VALUES ( │
│ 'user-minutes-user1', 'meeting-001', │
│ 'user1@example.com', [V3] ← 참석자 구분 │
│ ... │
│ ) │
└─────────────────────────────────────────────────────────────┘
↓
DB State:
✓ meetings: IN_PROGRESS
✓ minutes (multiple records):
- 1개의 통합 회의록 (user_id=NULL)
- N개의 참석자별 회의록 (user_id=참석자ID)
✓ minutes_sections: 초기 섹션 생성
Phase 3: 회의록 작성 중
════════════════════════════════════════════════════════════════════════════════
사용자가 회의록 섹션 작성
↓
┌─────────────────────────────────────────────────────────────┐
│ 3-1. UpdateMinutes API (여러 번) │
│ ────────────────────────────────────────────────────────────│
│ │
│ 각 섹션별로: │
│ INSERT INTO minutes_sections ( │
│ id, minutes_id, type, title, content, order │
│ ) VALUES ( │
│ 'section-1', 'consolidated-minutes-1', │
│ 'DISCUSSION', '신제품 기획 방향', │
│ '신제품의 주요 타겟은 20-30대 직장인으로 설정...', │
│ 1 │
│ ) │
│ │
│ UPDATE minutes_sections SET │
│ content = '...', │
│ updated_at = NOW() │
│ WHERE id = 'section-1' │
│ │
│ ★ 중요: content 컬럼에 실제 회의록 내용 저장! │
│ minutes 테이블에는 content가 없음 │
└─────────────────────────────────────────────────────────────┘
↓
DB State:
✓ minutes: status='DRAFT'
✓ minutes_sections: 사용자가 작성한 내용 축적
✓ 각 참석자가 자신의 회의록을 독립적으로 작성
Phase 4: 회의 종료
════════════════════════════════════════════════════════════════════════════════
회의 종료 (end_meeting API)
↓
┌─────────────────────────────────────────────────────────────┐
│ 4-1. EndMeeting UseCase [V3 추가] │
│ ────────────────────────────────────────────────────────────│
│ UPDATE meetings SET │
│ status = 'COMPLETED', │
│ ended_at = NOW() [V3] ← 종료 시간 기록 │
│ WHERE meeting_id = ? │
│ │
│ ★ 중요: 회의 종료와 동시에 회의록 준비 시작 │
└─────────────────────────────────────────────────────────────┘
↓
DB State:
✓ meetings: status='COMPLETED', ended_at=현재시간
✓ minutes: 계속 DRAFT (사용자 추가 편집 가능)
Phase 5: 회의록 최종화
════════════════════════════════════════════════════════════════════════════════
사용자가 회의록 최종화 요청
↓
┌─────────────────────────────────────────────────────────────┐
│ 5-1. FinalizeMinutes API │
│ ────────────────────────────────────────────────────────────│
│ UPDATE minutes SET │
│ status = 'FINALIZED', │
│ finalized_by = ?, │
│ finalized_at = NOW(), │
│ version = version + 1 │
│ WHERE id = 'consolidated-minutes-1' │
│ │
│ UPDATE minutes_sections SET │
│ locked = TRUE, │
│ locked_by = ?, │
│ verified = TRUE │
│ WHERE minutes_id = 'consolidated-minutes-1' │
│ │
│ ★ 중요: minutes_id를 통해 관련된 모든 섹션 잠금 │
└─────────────────────────────────────────────────────────────┘
↓
Event 발생: MinutesAnalysisRequestEvent (Async)
↓
DB State:
✓ minutes: status='FINALIZED'
✓ minutes_sections: locked=TRUE, verified=TRUE
✓ 모든 섹션이 수정 불가능
Phase 6: AI 분석 처리 (비동기 - MinutesAnalysisEventConsumer)
════════════════════════════════════════════════════════════════════════════════
이벤트 수신: MinutesAnalysisRequestEvent
↓
┌─────────────────────────────────────────────────────────────┐
│ 6-1. 통합 회의록 조회 (user_id=NULL) │
│ ────────────────────────────────────────────────────────────│
│ SELECT m.*, GROUP_CONCAT(ms.content) AS full_content │
│ FROM minutes m │
│ LEFT JOIN minutes_sections ms ON m.id = ms.minutes_id │
│ WHERE m.meeting_id = ? AND m.user_id IS NULL │
│ ORDER BY ms.order │
│ │
│ ★ 참석자별 회의록은 AI 분석 대상이 아님 │
│ user_id IS NOT NULL인 것들은 개인 기록용 │
└─────────────────────────────────────────────────────────────┘
↓
AI Service 호출 (Claude API)
↓
AI가 회의록 분석
- 안건별로 분리
- 요약 생성
- 결정사항 추출
- 보류사항 추출
- Todo 추출
↓
┌─────────────────────────────────────────────────────────────┐
│ 6-2. agenda_sections 생성 [V3] │
│ ────────────────────────────────────────────────────────────│
│ INSERT INTO agenda_sections ( │
│ id, minutes_id, meeting_id, agenda_number, │
│ agenda_title, ai_summary_short, discussions, │
│ decisions, pending_items, opinions, todos [V4] │
│ ) VALUES ( │
│ 'uuid-1', 'consolidated-minutes-1', 'meeting-001', │
│ 1, '신제품 기획 방향성', │
│ '타겟 고객을 20-30대로 설정...', │
│ '신제품의 주요 타겟 고객층을 20-30대...', │
│ ["타겟 고객: 20-30대 직장인", "UI 개선 최우선"], │
│ [], │
│ [{"speaker": "김민준", "opinion": "..."}], │
│ [ │
│ {"title": "시장 조사", "assignee": "김민준", │
│ "dueDate": "2025-02-15", "priority": "HIGH"} │
│ ] [V4] │
│ ) │
│ │
│ FOR EACH agenda detected by AI │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 6-3. ai_summaries 저장 [V3] │
│ ────────────────────────────────────────────────────────────│
│ INSERT INTO ai_summaries ( │
│ id, meeting_id, summary_type, │
│ source_minutes_ids, result, processing_time_ms, │
│ model_version, keywords, statistics, created_at │
│ ) VALUES ( │
│ 'summary-uuid-1', 'meeting-001', 'CONSOLIDATED', │
│ ["consolidated-minutes-1"], │
│ {AI 응답 전체 JSON}, │
│ 2500, │
│ 'claude-3.5-sonnet', │
│ ["신제품", "타겟층", "UI개선"], │
│ {"participants": 5, "agendas": 3, "todos": 8}, │
│ NOW() │
│ ) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 6-4. todos 저장 [V3 확장] │
│ ────────────────────────────────────────────────────────────│
│ INSERT INTO todos ( │
│ todo_id, meeting_id, minutes_id, title, │
│ assignee_id, due_date, status, priority, │
│ extracted_by, section_reference, │
│ extraction_confidence, created_at │
│ ) VALUES ( │
│ 'todo-uuid-1', 'meeting-001', │
│ 'consolidated-minutes-1', '시장 조사 보고서 작성', │
│ 'user1@example.com', '2025-02-15', 'PENDING', 'HIGH', │
│ 'AI', '안건 1: 신제품 기획', [V3] │
│ 0.95, [V3] 신뢰도 │
│ NOW() │
│ ) │
│ │
│ ★ 주의: agenda_sections.todos (JSON)에도 동시 저장 │
│ 개별 관리 필요시만 todos 테이블에 저장 │
└─────────────────────────────────────────────────────────────┘
↓
DB State:
✓ agenda_sections: AI 요약 결과 저장됨 (안건별)
✓ ai_summaries: AI 처리 결과 캐시
✓ todos: AI 추출 Todo (extracted_by='AI')
Phase 7: 회의록 및 분석 결과 조회
════════════════════════════════════════════════════════════════════════════════
Case 1: 통합 회의록 조회
─────────────────────────────────────────────────────────────
SELECT m.*, ms.*, ag.*, ai.* FROM minutes m
LEFT JOIN minutes_sections ms ON m.id = ms.minutes_id
LEFT JOIN agenda_sections ag ON m.id = ag.minutes_id
LEFT JOIN ai_summaries ai ON m.meeting_id = ai.meeting_id
WHERE m.meeting_id = 'meeting-001'
AND m.user_id IS NULL [V3]
ORDER BY ms.order
Case 2: 특정 사용자의 개인 회의록 조회
─────────────────────────────────────────────────────────────
SELECT m.*, ms.* FROM minutes m
LEFT JOIN minutes_sections ms ON m.id = ms.minutes_id
WHERE m.meeting_id = 'meeting-001'
AND m.user_id = 'user1@example.com' [V3]
ORDER BY ms.order
→ 개인이 작성한 회의록만 조회
→ AI 분석 결과(agenda_sections) 미포함
Case 3: AI 분석 결과만 조회
─────────────────────────────────────────────────────────────
SELECT ag.* FROM agenda_sections ag
WHERE ag.meeting_id = 'meeting-001'
ORDER BY ag.agenda_number
→ 안건별 AI 요약
→ todos JSON 필드 포함 (V4)
Case 4: 추출된 Todo 조회
─────────────────────────────────────────────────────────────
SELECT * FROM todos
WHERE meeting_id = 'meeting-001'
AND extracted_by = 'AI' [V3]
ORDER BY priority DESC, due_date ASC
또는 agenda_sections의 JSON todos 필드 사용
└──────────────────────────────────────────────────────────────────────────────┘
2. 상태 전이 다이어그램 (State Transition)
┌─────────────────────────────────────────────────────────────────────────────┐
│ meetings 테이블 상태 │
└─────────────────────────────────────────────────────────────────────────────┘
[생성]
│
├─────────────────────────┐
▼ │
SCHEDULED │ (시간 경과)
(scheduled_at 설정) │
│ │
│ start_meeting API │
▼ │
IN_PROGRESS │
(started_at 설정) │
│ │
│ end_meeting API [V3] │
▼ │
COMPLETED │
(ended_at 설정) [V3 추가] ├─────────────────────────┐
│ │ │
└─────────────────────────┘ │
│ 회의록 최종화 │
│ (finalize_minutes API) │
▼ │
minutes: FINALIZED │
(status='FINALIZED') │
│ │
│ (비동기 이벤트) │
▼ │
AI 분석 완료 │
agenda_sections 생성 │
ai_summaries 생성 │
todos 추출 │
│ │
└─────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ minutes 테이블 상태 │
└─────────────────────────────────────────────────────────────────────────────┘
CREATE DRAFT
(minutes 생성) ───────────► (사용자 작성 중)
│
update_minutes API
│
(섹션 추가/수정)
│
│
finalize_minutes API
│
▼
FINALIZED
(AI 분석 대기 중)
│
(비동기 처리 완료)
│
▼
분석 완료 (상태 유지)
agenda_sections 생성됨
ai_summaries 생성됨
┌─────────────────────────────────────────────────────────────────────────────┐
│ minutes_sections 잠금 상태 │
└─────────────────────────────────────────────────────────────────────────────┘
편집 가능
(locked=FALSE)
│
│ finalize_minutes
│
▼
잠금됨
(locked=TRUE, locked_by=user_id)
│
└─────► 수정 불가
verified=TRUE
┌─────────────────────────────────────────────────────────────────────────────┐
│ todos 완료 상태 │
└─────────────────────────────────────────────────────────────────────────────┘
PENDING
(생성됨)
│
│ todo 완료 API
│
▼
COMPLETED
(completed_at 설정)
3. 사용자별 회의록 데이터 구조
┌──────────────────────────────────────────────────────────────────────────────┐
│ 1개 회의 (meetings: meeting-001)
│ ├─ 참석자: user1, user2, user3
└──────────────────────────────────────────────────────────────────────────────┘
회의 종료 → minutes 테이블에 여러 레코드 생성
┌─────────────────────────────────────────────────────────────────┐
│ minutes 테이블 (3개 레코드 생성) │
├─────────────────────────────────────────────────────────────────┤
│ id │ meeting_id │ user_id │ status
├─────────────────────┼─────────────┼──────────────────────┼────────
│ consol-minutes-001 │ meeting-001 │ NULL [V3] │ DRAFT
│ user1-minutes-001 │ meeting-001 │ user1@example.com │ DRAFT
│ user2-minutes-001 │ meeting-001 │ user2@example.com │ DRAFT
│ user3-minutes-001 │ meeting-001 │ user3@example.com │ DRAFT
└─────────────────────┴─────────────┴──────────────────────┴────────
↓ (각각 minutes_sections 참조)
┌─────────────────────────────────────────────────────────────────┐
│ minutes_sections 테이블 (4그룹 × N개 섹션) │
├─────────────────────────────────────────────────────────────────┤
│ id │ minutes_id │ type │ title │ content
├────────┼────────────────────┼─────────────┼──────────┼─────────
│ sec-1 │ consol-minutes-001 │ DISCUSSION │ 안건1 │ "AI가..."
│ sec-2 │ consol-minutes-001 │ DECISION │ 결정1 │ "..."
│ │ │ │ │
│ sec-3 │ user1-minutes-001 │ DISCUSSION │ 안건1 │ "사용자1..."
│ sec-4 │ user1-minutes-001 │ DISCUSSION │ 안건2 │ "..."
│ │ │ │ │
│ sec-5 │ user2-minutes-001 │ DISCUSSION │ 안건1 │ "사용자2..."
│ sec-6 │ user2-minutes-001 │ DECISION │ 결정1 │ "..."
│ │ │ │ │
│ sec-7 │ user3-minutes-001 │ DISCUSSION │ 안건1 │ "사용자3..."
└────────┴────────────────────┴─────────────┴──────────┴─────────
각 사용자가 독립적으로 작성:
- User1: consol-minutes-001의 sec-3, sec-4 편집
- User2: user2-minutes-001의 sec-5, sec-6 편집
- User3: user3-minutes-001의 sec-7 편집
AI 분석 (user_id=NULL인 것만):
┌─────────────────────────────────────────────────────────────────┐
│ agenda_sections 테이블 │
├─────────────────────────────────────────────────────────────────┤
│ id │ minutes_id │ meeting_id │ agenda_number
├────────┼────────────────────┼─────────────┼──────────────────
│ ag-1 │ consol-minutes-001 │ meeting-001 │ 1
│ ag-2 │ consol-minutes-001 │ meeting-001 │ 2
└────────┴────────────────────┴─────────────┴──────────────────
→ minutes_id를 통해 통합 회의록만 참조
→ user_id='user1@example.com'인 회의록은 참조하지 않음
4. 인덱스 활용 쿼리 예시
-- 쿼리 1: 특정 회의의 통합 회의록 조회 (V3 인덱스 활용)
SELECT * FROM minutes
WHERE meeting_id = 'meeting-001' AND user_id IS NULL
ORDER BY created_at DESC;
└─► 인덱스: idx_minutes_meeting_user (meeting_id, user_id)
-- 쿼리 2: 특정 사용자의 회의록 조회 (복합 인덱스 활용)
SELECT * FROM minutes
WHERE meeting_id = 'meeting-001' AND user_id = 'user1@example.com'
ORDER BY created_at DESC;
└─► 인덱스: idx_minutes_meeting_user (meeting_id, user_id)
-- 쿼리 3: 안건별 AI 요약 조회 (V3 인덱스 활용)
SELECT * FROM agenda_sections
WHERE meeting_id = 'meeting-001'
ORDER BY agenda_number ASC;
└─► 인덱스: idx_sections_meeting (meeting_id)
-- 쿼리 4: 특정 안건의 세부 요약 (복합 인덱스 활용)
SELECT * FROM agenda_sections
WHERE meeting_id = 'meeting-001' AND agenda_number = 1;
└─► 인덱스: idx_sections_agenda (meeting_id, agenda_number)
-- 쿼리 5: AI 추출 Todo 조회 (V3 인덱스 활용)
SELECT * FROM todos
WHERE meeting_id = 'meeting-001' AND extracted_by = 'AI'
ORDER BY priority DESC, due_date ASC;
└─► 인덱스: idx_todos_extracted (extracted_by)
└─► 인덱스: idx_todos_meeting (meeting_id)
-- 쿼리 6: 특정 회의의 모든 데이터 조회 (JOIN)
SELECT
m.*,
ms.content,
ag.ai_summary_short,
ag.todos,
ai.keywords
FROM minutes m
LEFT JOIN minutes_sections ms ON m.id = ms.minutes_id
LEFT JOIN agenda_sections ag ON m.id = ag.minutes_id
LEFT JOIN ai_summaries ai ON m.meeting_id = ai.meeting_id
WHERE m.meeting_id = 'meeting-001' AND m.user_id IS NULL
ORDER BY ms.order ASC, ag.agenda_number ASC;
└─► 인덱스: idx_minutes_meeting_user (meeting_id, user_id)
└─► 인덱스: idx_sections_minutes (minutes_id)
5. 데이터 저장 크기 예상
1개 회의 (참석자 5명) 데이터 크기:
├─ meetings: ~500 bytes
├─ meeting_participants (5명): ~5 × 150 = 750 bytes
├─ minutes (6개: 1 통합 + 5 개인): ~6 × 400 = 2.4 KB
├─ minutes_sections (30개 섹션): ~30 × 2 KB = 60 KB
├─ agenda_sections (5개 안건): ~5 × 4 KB = 20 KB
├─ ai_summaries: ~10 KB
└─ todos (8개): ~8 × 800 bytes = 6.4 KB
Total: ~100 KB/회의
1년 (250개 회의) 예상:
└─► 25 MB + 인덱스 ~5 MB = ~30 MB
JSON 필드 데이터 크기:
├─ agenda_sections.decisions: ~200 bytes/건
├─ agenda_sections.opinions: ~300 bytes/건
├─ agenda_sections.todos: ~500 bytes/건 [V4]
├─ ai_summaries.result: ~5-10 KB/건
└─ ai_summaries.statistics: ~200 bytes/건