hgzero/claude/data-flow-diagram.md

34 KiB
Raw Blame History

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/건