fix: AgendaSection opinions 필드 제거 및 DB 마이그레이션 스크립트 추가

- AgendaSection 도메인 및 Entity에서 opinions 필드 제거
- ParticipantOpinion 내부 클래스 삭제
- MeetingAiController 및 AgendaSectionResponse에서 opinions 관련 로직 제거
- agenda_sections 테이블 마이그레이션 SQL 스크립트 추가
  * agenda_number: varchar(50) → integer 변환
  * decisions, pending_items, todos: text → json 변환
  * opinions 컬럼 삭제
- 자동 백업 및 롤백 기능 포함

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Minseo-Jo 2025-10-29 11:19:16 +09:00
parent 621d4c16df
commit 96e09ae83d
3 changed files with 142 additions and 28 deletions

View File

@ -0,0 +1,142 @@
-- ====================================================================
-- agenda_sections 테이블 마이그레이션 스크립트
--
-- 목적:
-- 1. agenda_number: varchar(50) → integer 변환
-- 2. decisions, pending_items, todos: text → json 변환
-- 3. opinions 컬럼 삭제 (서비스에서 미사용)
--
-- 실행 전 필수 작업:
-- 1. 데이터베이스 백업 (pg_dump)
-- 2. 테스트 환경에서 먼저 실행 및 검증
-- ====================================================================
-- 트랜잭션 시작
BEGIN;
-- ====================================================================
-- 1단계: 백업 테이블 생성 (롤백용)
-- ====================================================================
CREATE TABLE IF NOT EXISTS agenda_sections_backup AS
SELECT * FROM agenda_sections;
SELECT '✓ 백업 테이블 생성 완료: agenda_sections_backup' AS status;
-- ====================================================================
-- 2단계: agenda_number 컬럼 타입 변경 (varchar → integer)
-- ====================================================================
-- 데이터 검증: 숫자가 아닌 값이 있는지 확인
DO $$
DECLARE
invalid_count INTEGER;
BEGIN
SELECT COUNT(*) INTO invalid_count
FROM agenda_sections
WHERE agenda_number !~ '^[0-9]+$';
IF invalid_count > 0 THEN
RAISE EXCEPTION '숫자가 아닌 agenda_number 값이 % 건 발견됨. 데이터 정리 필요.', invalid_count;
END IF;
RAISE NOTICE '✓ agenda_number 데이터 검증 완료 (모두 숫자)';
END $$;
-- 타입 변경 실행
ALTER TABLE agenda_sections
ALTER COLUMN agenda_number TYPE integer
USING agenda_number::integer;
SELECT '✓ agenda_number 타입 변경 완료: varchar(50) → integer' AS status;
-- ====================================================================
-- 3단계: JSON 컬럼 타입 변경 (text → json)
-- ====================================================================
-- 3-1. decisions 컬럼 변경
ALTER TABLE agenda_sections
ALTER COLUMN decisions TYPE json
USING CASE
WHEN decisions IS NULL OR decisions = '' THEN NULL
ELSE decisions::json
END;
SELECT '✓ decisions 타입 변경 완료: text → json' AS status;
-- 3-2. pending_items 컬럼 변경
ALTER TABLE agenda_sections
ALTER COLUMN pending_items TYPE json
USING CASE
WHEN pending_items IS NULL OR pending_items = '' THEN NULL
ELSE pending_items::json
END;
SELECT '✓ pending_items 타입 변경 완료: text → json' AS status;
-- 3-3. todos 컬럼 변경
ALTER TABLE agenda_sections
ALTER COLUMN todos TYPE json
USING CASE
WHEN todos IS NULL OR todos = '' THEN NULL
ELSE todos::json
END;
SELECT '✓ todos 타입 변경 완료: text → json' AS status;
-- ====================================================================
-- 4단계: opinions 컬럼 삭제 (서비스에서 미사용)
-- ====================================================================
ALTER TABLE agenda_sections
DROP COLUMN IF EXISTS opinions;
SELECT '✓ opinions 컬럼 삭제 완료' AS status;
-- ====================================================================
-- 5단계: 변경 사항 검증
-- ====================================================================
DO $$
DECLARE
rec RECORD;
BEGIN
-- 테이블 구조 확인
SELECT
column_name,
data_type,
character_maximum_length,
is_nullable
INTO rec
FROM information_schema.columns
WHERE table_name = 'agenda_sections'
AND column_name = 'agenda_number';
RAISE NOTICE '========================================';
RAISE NOTICE '✓ 마이그레이션 검증 결과';
RAISE NOTICE '========================================';
RAISE NOTICE 'agenda_number 타입: %', rec.data_type;
-- 데이터 건수 확인
RAISE NOTICE '원본 데이터 건수: %', (SELECT COUNT(*) FROM agenda_sections_backup);
RAISE NOTICE '마이그레이션 후 건수: %', (SELECT COUNT(*) FROM agenda_sections);
RAISE NOTICE '========================================';
END $$;
-- ====================================================================
-- 커밋 또는 롤백 선택
-- ====================================================================
-- 문제가 없으면 COMMIT, 문제가 있으면 ROLLBACK 실행
-- 성공 시: COMMIT;
-- 실패 시: ROLLBACK;
COMMIT;
SELECT '
====================================================================
!
:
1.
2.
3. :
DROP TABLE agenda_sections_backup;
====================================================================
' AS next_steps;

View File

@ -162,16 +162,6 @@ public class MeetingAiController {
} }
private AgendaSectionResponse.AgendaSectionItem convertToAgendaSectionItem(AgendaSection section) { private AgendaSectionResponse.AgendaSectionItem convertToAgendaSectionItem(AgendaSection section) {
List<AgendaSectionResponse.AgendaSectionItem.ParticipantOpinion> opinions = null;
if (section.getOpinions() != null) {
opinions = section.getOpinions().stream()
.map(op -> AgendaSectionResponse.AgendaSectionItem.ParticipantOpinion.builder()
.speaker(op.getSpeaker())
.opinion(op.getOpinion())
.build())
.collect(Collectors.toList());
}
List<AgendaSectionResponse.AgendaSectionItem.TodoItem> todos = null; List<AgendaSectionResponse.AgendaSectionItem.TodoItem> todos = null;
if (section.getTodos() != null) { if (section.getTodos() != null) {
todos = section.getTodos().stream() todos = section.getTodos().stream()
@ -194,7 +184,6 @@ public class MeetingAiController {
.discussions(section.getDiscussions()) .discussions(section.getDiscussions())
.decisions(section.getDecisions()) .decisions(section.getDecisions())
.pendingItems(section.getPendingItems()) .pendingItems(section.getPendingItems())
.opinions(opinions)
.todos(todos) .todos(todos)
.createdAt(section.getCreatedAt()) .createdAt(section.getCreatedAt())
.updatedAt(section.getUpdatedAt()) .updatedAt(section.getUpdatedAt())

View File

@ -58,9 +58,6 @@ public class AgendaSectionResponse {
@Schema(description = "보류 사항 목록") @Schema(description = "보류 사항 목록")
private List<String> pendingItems; private List<String> pendingItems;
@Schema(description = "참석자별 의견")
private List<ParticipantOpinion> opinions;
@Schema(description = "AI 추출 Todo 목록") @Schema(description = "AI 추출 Todo 목록")
private List<TodoItem> todos; private List<TodoItem> todos;
@ -72,20 +69,6 @@ public class AgendaSectionResponse {
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "참석자 의견")
public static class ParticipantOpinion {
@Schema(description = "발언자 이름", example = "김민준")
private String speaker;
@Schema(description = "의견 내용", example = "타겟 고객층을 명확히 설정하여 마케팅 전략 수립 필요")
private String opinion;
}
@Getter @Getter
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor