diff --git a/ai-python/app/config.py b/ai-python/app/config.py index 798b319..fd74ade 100644 --- a/ai-python/app/config.py +++ b/ai-python/app/config.py @@ -14,7 +14,7 @@ class Settings(BaseSettings): # Claude API claude_api_key: str = "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" - claude_model: str = "claude-3-5-sonnet-20241022" + claude_model: str = "claude-3-5-sonnet-20240620" claude_max_tokens: int = 250000 claude_temperature: float = 0.7 diff --git a/ai-python/app/models/transcript.py b/ai-python/app/models/transcript.py index 5eaa7a6..b9398ee 100644 --- a/ai-python/app/models/transcript.py +++ b/ai-python/app/models/transcript.py @@ -28,9 +28,8 @@ class AgendaSummary(BaseModel): """안건별 요약""" agenda_number: int = Field(..., description="안건 번호") agenda_title: str = Field(..., description="안건 제목") - summary_short: str = Field(..., description="짧은 요약 (1줄)") - discussion: str = Field(..., description="논의 주제") - decisions: List[str] = Field(default_factory=list, description="결정 사항") + summary_short: str = Field(..., description="AI 생성 짧은 요약 (1줄, 20자 이내)") + summary: str = Field(..., description="안건별 회의록 요약 (논의사항+결정사항, 사용자 수정 가능)") pending: List[str] = Field(default_factory=list, description="보류 사항") todos: List[ExtractedTodo] = Field(default_factory=list, description="Todo 목록 (제목만)") @@ -40,5 +39,6 @@ class ConsolidateResponse(BaseModel): meeting_id: str = Field(..., description="회의 ID") keywords: List[str] = Field(..., description="주요 키워드") statistics: Dict[str, int] = Field(..., description="통계 정보") + decisions: str = Field(..., description="회의 전체 결정사항 (TEXT 형식)") agenda_summaries: List[AgendaSummary] = Field(..., description="안건별 요약") generated_at: datetime = Field(default_factory=datetime.utcnow, description="생성 시각") diff --git a/ai-python/app/prompts/consolidate_prompt.py b/ai-python/app/prompts/consolidate_prompt.py index aeb2810..411b915 100644 --- a/ai-python/app/prompts/consolidate_prompt.py +++ b/ai-python/app/prompts/consolidate_prompt.py @@ -15,7 +15,7 @@ def get_consolidate_prompt(participant_minutes: list, agendas: list = None) -> s # 안건 정보 (있는 경우) agendas_info = "" if agendas: - agendas_info = f"\n\n**사전 정의된 안건**:\n" + "\n".join([ + agendas_info = "\n\n**사전 정의된 안건**:\n" + "\n".join([ f"{i+1}. {agenda}" for i, agenda in enumerate(agendas) ]) @@ -37,15 +37,22 @@ def get_consolidate_prompt(participant_minutes: list, agendas: list = None) -> s - agendas_count: 안건 개수 (내용 기반 추정) - todos_count: 추출된 Todo 총 개수 -3. **안건별 요약 (agenda_summaries)**: +3. **회의 전체 결정사항 (decisions)**: + - 회의 전체에서 최종 결정된 사항들을 TEXT 형식으로 정리 + - 안건별 결정사항을 모두 포함하여 회의록 수정 페이지에서 사용자가 확인 및 수정할 수 있도록 작성 + - 형식: "**안건1 결정사항:**\n- 결정1\n- 결정2\n\n**안건2 결정사항:**\n- 결정3" + +4. **안건별 요약 (agenda_summaries)**: 회의 내용을 분석하여 안건별로 구조화: 각 안건마다: - **agenda_number**: 안건 번호 (1, 2, 3...) - **agenda_title**: 안건 제목 (간결하게) - - **summary_short**: 1줄 요약 (20자 이내) - - **discussion**: 논의 주제 (핵심 내용 3-5문장) - - **decisions**: 결정 사항 배열 (해당 안건 관련) + - **summary_short**: AI가 생성한 1줄 요약 (20자 이내, 사용자 수정 불가) + - **summary**: 안건별 회의록 요약 (논의사항과 결정사항을 포함한 전체 요약) + * 회의록 수정 페이지에서 사용자가 수정할 수 있는 입력 필드 + * 형식: "**논의 사항:**\n- 논의내용1\n- 논의내용2\n\n**결정 사항:**\n- 결정1\n- 결정2" + * 사용자가 자유롭게 편집할 수 있도록 구조화된 텍스트로 작성 - **pending**: 보류 사항 배열 (추가 논의 필요 사항) - **todos**: Todo 배열 (제목만, 담당자/마감일/우선순위 없음) - title: Todo 제목만 추출 (예: "시장 조사 보고서 작성") @@ -63,13 +70,13 @@ def get_consolidate_prompt(participant_minutes: list, agendas: list = None) -> s "agendas_count": 숫자, "todos_count": 숫자 }}, + "decisions": "**안건1 결정사항:**\\n- 결정1\\n- 결정2\\n\\n**안건2 결정사항:**\\n- 결정3", "agenda_summaries": [ {{ "agenda_number": 1, "agenda_title": "안건 제목", - "summary_short": "짧은 요약", - "discussion": "논의 내용", - "decisions": ["결정사항"], + "summary_short": "짧은 요약 (20자 이내)", + "summary": "**논의 사항:**\\n- 논의내용1\\n- 논의내용2\\n\\n**결정 사항:**\\n- 결정1\\n- 결정2", "pending": ["보류사항"], "todos": [ {{ @@ -89,8 +96,14 @@ def get_consolidate_prompt(participant_minutes: list, agendas: list = None) -> s 2. **객관성**: 추측이나 가정 없이 사실만 기록 3. **완전성**: 모든 필드를 빠짐없이 작성 4. **구조화**: 안건별로 명확히 분리 -5. **Todo 추출**: 제목만 추출 (담당자나 마감일 없어도 됨) -6. **JSON만 출력**: 추가 설명 없이 JSON만 반환 +5. **결정사항 추출**: + - 회의 전체 결정사항(decisions)은 모든 안건의 결정사항을 포함 + - 안건별 summary에도 결정사항을 포함하여 사용자가 수정 가능하도록 작성 +6. **summary 작성**: + - summary_short: AI가 자동 생성한 1줄 요약 (사용자 수정 불가) + - summary: 논의사항과 결정사항을 포함한 전체 요약 (사용자 수정 가능) +7. **Todo 추출**: 제목만 추출 (담당자나 마감일 없어도 됨) +8. **JSON만 출력**: 추가 설명 없이 JSON만 반환 이제 위 회의록들을 분석하여 통합 요약을 JSON 형식으로 생성해주세요. """ diff --git a/ai-python/app/services/transcript_service.py b/ai-python/app/services/transcript_service.py index 2f2342d..6bdd0b5 100644 --- a/ai-python/app/services/transcript_service.py +++ b/ai-python/app/services/transcript_service.py @@ -41,6 +41,14 @@ class TranscriptService: agendas=request.agendas ) + # 입력 데이터 로깅 + logger.info("=" * 80) + logger.info("INPUT - 참석자별 회의록:") + for pm in participant_data: + logger.info(f"\n[{pm['user_name']}]") + logger.info(f"{pm['content'][:500]}..." if len(pm['content']) > 500 else pm['content']) + logger.info("=" * 80) + # 2. Claude API 호출 start_time = datetime.utcnow() ai_result = await claude_service.generate_completion(prompt) @@ -87,8 +95,7 @@ class TranscriptService: agenda_number=agenda_data.get("agenda_number", 0), agenda_title=agenda_data.get("agenda_title", ""), summary_short=agenda_data.get("summary_short", ""), - discussion=agenda_data.get("discussion", ""), - decisions=agenda_data.get("decisions", []), + summary=agenda_data.get("summary", ""), pending=agenda_data.get("pending", []), todos=todos ) @@ -105,6 +112,7 @@ class TranscriptService: meeting_id=meeting_id, keywords=ai_result.get("keywords", []), statistics=statistics, + decisions=ai_result.get("decisions", ""), agenda_summaries=agenda_summaries, generated_at=datetime.utcnow() ) diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/AgendaSection.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/AgendaSection.java index 30370ba..8b0a086 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/AgendaSection.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/AgendaSection.java @@ -49,25 +49,17 @@ public class AgendaSection { private String aiSummaryShort; /** - * 논의 사항 (핵심 내용 3-5문장) + * 안건별 회의록 요약 + * 사용자가 입력한 회의록 내용을 요약한 결과 + * (기존 discussions, decisions, opinions를 통합) */ - private String discussions; - - /** - * 결정 사항 목록 - */ - private List decisions; + private String summary; /** * 보류 사항 목록 */ private List pendingItems; - /** - * 참석자별 의견 - */ - private List opinions; - /** * AI 추출 Todo 목록 */ @@ -83,25 +75,6 @@ public class AgendaSection { */ private LocalDateTime updatedAt; - /** - * 참석자 의견 내부 클래스 - */ - @Getter - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class ParticipantOpinion { - /** - * 발언자 이름 - */ - private String speaker; - - /** - * 의견 내용 - */ - private String opinion; - } - /** * Todo 항목 내부 클래스 */ diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/MeetingAnalysis.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/MeetingAnalysis.java index 57ff112..a3f1b2d 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/MeetingAnalysis.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/MeetingAnalysis.java @@ -37,6 +37,11 @@ public class MeetingAnalysis { */ private List keywords; + /** + * 회의 전체 결정사항 + */ + private String decisions; + /** * 안건별 분석 결과 */ diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/Minutes.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/Minutes.java index e82054c..506a5b5 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/Minutes.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/domain/Minutes.java @@ -76,6 +76,11 @@ public class Minutes { */ private String lastModifiedBy; + /** + * 회의 전체 결정사항 + */ + private String decisions; + /** * 확정자 ID */ diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/MinutesDTO.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/MinutesDTO.java index 5201c9d..6f6711a 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/MinutesDTO.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/MinutesDTO.java @@ -106,6 +106,11 @@ public class MinutesDTO { */ private final LocalDateTime lastModifiedAt; + /** + * 회의 전체 결정사항 + */ + private final String decisions; + /** * Todo 개수 */ diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/service/EndMeetingService.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/service/EndMeetingService.java index bec4033..f6d2ec8 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/service/EndMeetingService.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/service/EndMeetingService.java @@ -140,8 +140,8 @@ public class EndMeetingService implements EndMeetingUseCase { .agendaId(UUID.randomUUID().toString()) .title(summary.getAgendaTitle()) .aiSummaryShort(summary.getSummaryShort()) - .discussion(summary.getDiscussion() != null ? summary.getDiscussion() : "") - .decisions(summary.getDecisions() != null ? summary.getDecisions() : List.of()) + .discussion(summary.getSummary() != null ? summary.getSummary() : "") + .decisions(List.of()) .pending(summary.getPending() != null ? summary.getPending() : List.of()) .extractedTodos(summary.getTodos() != null ? summary.getTodos().stream() @@ -157,6 +157,7 @@ public class EndMeetingService implements EndMeetingUseCase { .meetingId(meeting.getMeetingId()) .minutesId(meeting.getMeetingId()) // 실제로는 minutesId 필요 .keywords(aiResponse.getKeywords()) + .decisions(aiResponse.getDecisions()) .agendaAnalyses(agendaAnalyses) .status("COMPLETED") .completedAt(LocalDateTime.now()) diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingAiController.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingAiController.java index 550a6ab..b309727 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingAiController.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingAiController.java @@ -181,8 +181,7 @@ public class MeetingAiController { .agendaNumber(section.getAgendaNumber()) .agendaTitle(section.getAgendaTitle()) .aiSummaryShort(section.getAiSummaryShort()) - .discussions(section.getDiscussions()) - .decisions(section.getDecisions()) + .summary(section.getSummary()) .pendingItems(section.getPendingItems()) .todos(todos) .createdAt(section.getCreatedAt()) diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/AgendaSummaryDTO.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/AgendaSummaryDTO.java index 926cdd3..609ce19 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/AgendaSummaryDTO.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/AgendaSummaryDTO.java @@ -36,14 +36,10 @@ public class AgendaSummaryDTO { private String summaryShort; /** - * 논의 주제 + * 안건별 회의록 요약 + * 사용자가 입력한 회의록 내용을 요약한 결과 */ - private String discussion; - - /** - * 결정 사항 - */ - private List decisions; + private String summary; /** * 보류 사항 diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ConsolidateResponse.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ConsolidateResponse.java index 42124e8..f362635 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ConsolidateResponse.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ConsolidateResponse.java @@ -39,6 +39,11 @@ public class ConsolidateResponse { */ private Map statistics; + /** + * 회의 전체 결정사항 + */ + private String decisions; + /** * 안건별 요약 */ diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ParticipantMinutesDTO.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ParticipantMinutesDTO.java index bc08aa7..ccd7e5b 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ParticipantMinutesDTO.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/ai/ParticipantMinutesDTO.java @@ -1,5 +1,6 @@ package com.unicorn.hgzero.meeting.infra.dto.ai; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -17,11 +18,13 @@ public class ParticipantMinutesDTO { /** * 사용자 ID */ + @JsonProperty("user_id") private String userId; /** * 사용자 이름 */ + @JsonProperty("user_name") private String userName; /** diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/response/AgendaSectionResponse.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/response/AgendaSectionResponse.java index d98aab3..7cafd93 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/response/AgendaSectionResponse.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/response/AgendaSectionResponse.java @@ -49,11 +49,8 @@ public class AgendaSectionResponse { @Schema(description = "AI 생성 짧은 요약", example = "타겟 고객을 20-30대로 설정") private String aiSummaryShort; - @Schema(description = "논의 사항", example = "신제품의 주요 타겟 고객층을 20-30대 직장인으로 설정하고...") - private String discussions; - - @Schema(description = "결정 사항 목록") - private List decisions; + @Schema(description = "안건별 회의록 요약", example = "신제품의 주요 타겟 고객층을 20-30대 직장인으로 설정하고...") + private String summary; @Schema(description = "보류 사항 목록") private List pendingItems; diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/AgendaSectionEntity.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/AgendaSectionEntity.java index 044b9eb..e24ff30 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/AgendaSectionEntity.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/AgendaSectionEntity.java @@ -47,18 +47,12 @@ public class AgendaSectionEntity extends BaseTimeEntity { @Column(name = "ai_summary_short", columnDefinition = "TEXT") private String aiSummaryShort; - @Column(name = "discussions", columnDefinition = "TEXT") - private String discussions; - - @Column(name = "decisions", columnDefinition = "json") - private String decisionsJson; + @Column(name = "summary", columnDefinition = "TEXT") + private String summary; @Column(name = "pending_items", columnDefinition = "json") private String pendingItemsJson; - @Column(name = "opinions", columnDefinition = "json") - private String opinionsJson; - @Column(name = "todos", columnDefinition = "json") private String todosJson; @@ -73,10 +67,8 @@ public class AgendaSectionEntity extends BaseTimeEntity { .agendaNumber(this.agendaNumber) .agendaTitle(this.agendaTitle) .aiSummaryShort(this.aiSummaryShort) - .discussions(this.discussions) - .decisions(parseJsonToList(this.decisionsJson, new TypeReference>() {})) + .summary(this.summary) .pendingItems(parseJsonToList(this.pendingItemsJson, new TypeReference>() {})) - .opinions(parseJsonToList(this.opinionsJson, new TypeReference>() {})) .todos(parseJsonToList(this.todosJson, new TypeReference>() {})) .createdAt(this.getCreatedAt()) .updatedAt(this.getUpdatedAt()) @@ -94,10 +86,8 @@ public class AgendaSectionEntity extends BaseTimeEntity { .agendaNumber(section.getAgendaNumber()) .agendaTitle(section.getAgendaTitle()) .aiSummaryShort(section.getAiSummaryShort()) - .discussions(section.getDiscussions()) - .decisionsJson(toJson(section.getDecisions())) + .summary(section.getSummary()) .pendingItemsJson(toJson(section.getPendingItems())) - .opinionsJson(toJson(section.getOpinions())) .todosJson(toJson(section.getTodos())) .build(); } diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MeetingAnalysisEntity.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MeetingAnalysisEntity.java index 43ed9a6..82e4505 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MeetingAnalysisEntity.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MeetingAnalysisEntity.java @@ -38,6 +38,9 @@ public class MeetingAnalysisEntity { @Column(name = "keyword") private List keywords; + @Column(name = "decisions", columnDefinition = "TEXT") + private String decisions; + @Column(name = "agenda_analyses", columnDefinition = "TEXT") private String agendaAnalysesJson; // JSON 문자열로 저장 @@ -55,12 +58,13 @@ public class MeetingAnalysisEntity { */ public MeetingAnalysis toDomain() { List agendaAnalyses = parseAgendaAnalyses(); - + return MeetingAnalysis.builder() .analysisId(this.analysisId) .meetingId(this.meetingId) .minutesId(this.minutesId) .keywords(this.keywords) + .decisions(this.decisions) .agendaAnalyses(agendaAnalyses) .status(this.status) .completedAt(this.completedAt) @@ -90,12 +94,13 @@ public class MeetingAnalysisEntity { */ public static MeetingAnalysisEntity fromDomain(MeetingAnalysis domain) { String agendaAnalysesJson = convertAgendaAnalysesToJson(domain.getAgendaAnalyses()); - + return MeetingAnalysisEntity.builder() .analysisId(domain.getAnalysisId()) .meetingId(domain.getMeetingId()) .minutesId(domain.getMinutesId()) .keywords(domain.getKeywords()) + .decisions(domain.getDecisions()) .agendaAnalysesJson(agendaAnalysesJson) .status(domain.getStatus()) .completedAt(domain.getCompletedAt()) diff --git a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesEntity.java b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesEntity.java index 219553a..d40dcec 100644 --- a/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesEntity.java +++ b/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesEntity.java @@ -48,6 +48,9 @@ public class MinutesEntity extends BaseTimeEntity { @Column(name = "created_by", length = 50, nullable = false) private String createdBy; + @Column(name = "decisions", columnDefinition = "TEXT") + private String decisions; + @Column(name = "finalized_by", length = 50) private String finalizedBy; @@ -66,6 +69,7 @@ public class MinutesEntity extends BaseTimeEntity { .createdBy(this.createdBy) .createdAt(this.getCreatedAt()) .lastModifiedAt(this.getUpdatedAt()) + .decisions(this.decisions) .finalizedBy(this.finalizedBy) .finalizedAt(this.finalizedAt) .build(); @@ -80,6 +84,7 @@ public class MinutesEntity extends BaseTimeEntity { .status(minutes.getStatus()) .version(minutes.getVersion()) .createdBy(minutes.getCreatedBy()) + .decisions(minutes.getDecisions()) .finalizedBy(minutes.getFinalizedBy()) .finalizedAt(minutes.getFinalizedAt()) .build(); diff --git a/meeting/src/main/resources/application.yml b/meeting/src/main/resources/application.yml index 1ea1f43..ca342cf 100644 --- a/meeting/src/main/resources/application.yml +++ b/meeting/src/main/resources/application.yml @@ -137,5 +137,5 @@ azure: # AI Service Configuration ai: service: - url: ${AI_SERVICE_URL:http://localhost:8087} + url: ${AI_SERVICE_URL:http://localhost:8086} timeout: ${AI_SERVICE_TIMEOUT:30000} diff --git a/meeting/src/main/resources/db/migration/V7__add_decisions_and_refactor_agenda_sections.sql b/meeting/src/main/resources/db/migration/V7__add_decisions_and_refactor_agenda_sections.sql new file mode 100644 index 0000000..8583bdc --- /dev/null +++ b/meeting/src/main/resources/db/migration/V7__add_decisions_and_refactor_agenda_sections.sql @@ -0,0 +1,58 @@ +-- ======================================== +-- V7: minutes 테이블에 decisions 추가 및 agenda_sections 리팩토링 +-- ======================================== +-- 작성일: 2025-10-29 +-- 설명: +-- 1. minutes 테이블에 decisions (결정사항) 컬럼 추가 +-- 2. agenda_sections 테이블에서 discussions, decisions, opinions를 summary로 통합 + +-- ======================================== +-- 1. minutes 테이블에 decisions 컬럼 추가 +-- ======================================== +-- 회의 전체 결정사항을 TEXT 형식으로 저장 + +ALTER TABLE minutes +ADD COLUMN IF NOT EXISTS decisions TEXT; + +COMMENT ON COLUMN minutes.decisions IS '회의 전체 결정사항 (회의록 수정 시 입력)'; + +-- ======================================== +-- 2. agenda_sections 테이블 백업 및 데이터 마이그레이션 +-- ======================================== +-- 기존 데이터 보존을 위한 임시 백업 테이블 생성 + +CREATE TABLE IF NOT EXISTS agenda_sections_backup AS +SELECT * FROM agenda_sections; + +-- ======================================== +-- 3. agenda_sections 테이블 컬럼 변경 +-- ======================================== +-- discussions, decisions, opinions → summary 통합 + +-- summary 컬럼 추가 (기존 discussions 내용으로 초기화) +ALTER TABLE agenda_sections +ADD COLUMN IF NOT EXISTS summary TEXT; + +-- 기존 데이터 마이그레이션: discussions 내용을 summary로 복사 +UPDATE agenda_sections +SET summary = COALESCE(discussions, ''); + +-- 기존 컬럼 삭제 +ALTER TABLE agenda_sections +DROP COLUMN IF EXISTS discussions, +DROP COLUMN IF EXISTS decisions, +DROP COLUMN IF EXISTS opinions; + +-- 코멘트 추가 +COMMENT ON COLUMN agenda_sections.summary IS '안건별 회의록 요약 (사용자가 입력한 회의록 내용을 AI가 요약한 결과)'; + +-- ======================================== +-- 4. 인덱스 및 트리거 유지 +-- ======================================== +-- 기존 인덱스 및 트리거는 그대로 유지됨 + +-- ======================================== +-- 5. 백업 테이블 정리 안내 +-- ======================================== +-- agenda_sections_backup 테이블은 수동으로 검증 후 삭제 +COMMENT ON TABLE agenda_sections_backup IS 'V7 마이그레이션 백업 - 검증 후 수동 삭제 필요';