feat: rag용 회의록확정 event 발행 및 meeting 참석자 데이터 삭제 에러 개선

This commit is contained in:
djeon 2025-10-29 05:51:01 +09:00
parent 8aea75c718
commit 44ae9c546f
9 changed files with 247 additions and 28 deletions

View File

@ -341,6 +341,36 @@ public class MinutesService implements
log.warn("참석자 수 계산 실패 - meetingId: {}", minutes.getMeetingId(), e); log.warn("참석자 수 계산 실패 - meetingId: {}", minutes.getMeetingId(), e);
} }
// 섹션 정보 변환
List<com.unicorn.hgzero.meeting.biz.dto.SectionDTO> sectionDTOs = null;
try {
List<MinutesSection> sections = minutes.getSections();
// Minutes 도메인에 섹션이 없으면 DB에서 조회
if (sections == null || sections.isEmpty()) {
sections = minutesSectionReader.findByMinutesIdOrderByOrder(minutes.getMinutesId());
}
// MinutesSection을 SectionDTO로 변환
if (sections != null && !sections.isEmpty()) {
sectionDTOs = sections.stream()
.map(section -> com.unicorn.hgzero.meeting.biz.dto.SectionDTO.builder()
.sectionId(section.getSectionId())
.minutesId(section.getMinutesId())
.title(section.getTitle())
.content(section.getContent())
.sectionOrder(section.getOrder())
.sectionType(section.getType())
.isVerified(section.getVerified())
.isLocked(section.getLocked())
.lockedBy(section.getLockedBy())
.build())
.collect(Collectors.toList());
}
} catch (Exception e) {
log.warn("섹션 정보 변환 실패 - minutesId: {}", minutes.getMinutesId(), e);
}
return MinutesDTO.builder() return MinutesDTO.builder()
.minutesId(minutes.getMinutesId()) .minutesId(minutes.getMinutesId())
.meetingId(minutes.getMeetingId()) .meetingId(minutes.getMeetingId())
@ -356,6 +386,7 @@ public class MinutesService implements
.completedTodoCount(completedTodoCount) .completedTodoCount(completedTodoCount)
.participantCount(participantCount) .participantCount(participantCount)
.memo("") // 메모 필드는 추후 구현 .memo("") // 메모 필드는 추후 구현
.sections(sectionDTOs) // 섹션 정보 추가
.build(); .build();
} }
} }

View File

@ -52,7 +52,8 @@ public class CacheService {
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl)); redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
log.debug("회의 정보 캐시 저장 - meetingId: {}", meetingId); log.debug("회의 정보 캐시 저장 - meetingId: {}", meetingId);
} catch (Exception e) { } catch (Exception e) {
log.error("회의 정보 캐시 저장 실패 - meetingId: {}", meetingId, e); log.warn("회의 정보 캐시 저장 실패 (서비스는 정상 동작) - meetingId: {}, 에러: {}",
meetingId, e.getMessage());
} }
} }
@ -72,7 +73,8 @@ public class CacheService {
return objectMapper.readValue(value, clazz); return objectMapper.readValue(value, clazz);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("회의 정보 캐시 조회 실패 - meetingId: {}", meetingId, e); log.warn("회의 정보 캐시 조회 실패 (DB에서 조회) - meetingId: {}, 에러: {}",
meetingId, e.getMessage());
} }
return null; return null;
} }
@ -91,7 +93,8 @@ public class CacheService {
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl)); redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
log.debug("회의록 정보 캐시 저장 - minutesId: {}", minutesId); log.debug("회의록 정보 캐시 저장 - minutesId: {}", minutesId);
} catch (Exception e) { } catch (Exception e) {
log.error("회의록 정보 캐시 저장 실패 - minutesId: {}", minutesId, e); log.warn("회의록 정보 캐시 저장 실패 (서비스는 정상 동작) - minutesId: {}, 에러: {}",
minutesId, e.getMessage());
} }
} }
@ -111,7 +114,8 @@ public class CacheService {
return objectMapper.readValue(value, clazz); return objectMapper.readValue(value, clazz);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("회의록 정보 캐시 조회 실패 - minutesId: {}", minutesId, e); log.warn("회의록 정보 캐시 조회 실패 (DB에서 조회) - minutesId: {}, 에러: {}",
minutesId, e.getMessage());
} }
return null; return null;
} }
@ -130,7 +134,8 @@ public class CacheService {
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl)); redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
log.debug("대시보드 데이터 캐시 저장 - userId: {}", userId); log.debug("대시보드 데이터 캐시 저장 - userId: {}", userId);
} catch (Exception e) { } catch (Exception e) {
log.error("대시보드 데이터 캐시 저장 실패 - userId: {}", userId, e); log.warn("대시보드 데이터 캐시 저장 실패 (서비스는 정상 동작) - userId: {}, 에러: {}",
userId, e.getMessage());
} }
} }
@ -150,7 +155,8 @@ public class CacheService {
return objectMapper.readValue(value, clazz); return objectMapper.readValue(value, clazz);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("대시보드 데이터 캐시 조회 실패 - userId: {}", userId, e); log.warn("대시보드 데이터 캐시 조회 실패 (DB에서 조회) - userId: {}, 에러: {}",
userId, e.getMessage());
} }
return null; return null;
} }
@ -169,7 +175,8 @@ public class CacheService {
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl)); redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
log.debug("세션 정보 캐시 저장 - sessionId: {}", sessionId); log.debug("세션 정보 캐시 저장 - sessionId: {}", sessionId);
} catch (Exception e) { } catch (Exception e) {
log.error("세션 정보 캐시 저장 실패 - sessionId: {}", sessionId, e); log.warn("세션 정보 캐시 저장 실패 (서비스는 정상 동작) - sessionId: {}, 에러: {}",
sessionId, e.getMessage());
} }
} }
@ -189,7 +196,8 @@ public class CacheService {
return objectMapper.readValue(value, clazz); return objectMapper.readValue(value, clazz);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("세션 정보 캐시 조회 실패 - sessionId: {}", sessionId, e); log.warn("세션 정보 캐시 조회 실패 - sessionId: {}, 에러: {}",
sessionId, e.getMessage());
} }
return null; return null;
} }
@ -206,7 +214,7 @@ public class CacheService {
redisTemplate.delete(key); redisTemplate.delete(key);
log.debug("캐시 삭제 - key: {}", key); log.debug("캐시 삭제 - key: {}", key);
} catch (Exception e) { } catch (Exception e) {
log.error("캐시 삭제 실패 - key: {}", prefix + id, e); log.warn("캐시 삭제 실패 - key: {}, 에러: {}", prefix + id, e.getMessage());
} }
} }
@ -223,7 +231,7 @@ public class CacheService {
log.debug("패턴 캐시 삭제 - pattern: {}, count: {}", pattern, keys.size()); log.debug("패턴 캐시 삭제 - pattern: {}, count: {}", pattern, keys.size());
} }
} catch (Exception e) { } catch (Exception e) {
log.error("패턴 캐시 삭제 실패 - pattern: {}", pattern, e); log.warn("패턴 캐시 삭제 실패 - pattern: {}, 에러: {}", pattern, e.getMessage());
} }
} }
@ -234,7 +242,7 @@ public class CacheService {
redisTemplate.opsForValue().set(MINUTES_LIST_PREFIX + cacheKey, value, Duration.ofMinutes(10)); redisTemplate.opsForValue().set(MINUTES_LIST_PREFIX + cacheKey, value, Duration.ofMinutes(10));
log.debug("회의록 목록 캐시 저장 - key: {}", cacheKey); log.debug("회의록 목록 캐시 저장 - key: {}", cacheKey);
} catch (Exception e) { } catch (Exception e) {
log.error("회의록 목록 캐시 저장 실패 - key: {}", cacheKey, e); log.warn("회의록 목록 캐시 저장 실패 (서비스는 정상 동작) - key: {}, 에러: {}", cacheKey, e.getMessage());
} }
} }
@ -245,7 +253,7 @@ public class CacheService {
return objectMapper.readValue(value, MinutesListResponse.class); return objectMapper.readValue(value, MinutesListResponse.class);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("회의록 목록 캐시 조회 실패 - key: {}", cacheKey, e); log.warn("회의록 목록 캐시 조회 실패 (DB에서 조회) - key: {}, 에러: {}", cacheKey, e.getMessage());
} }
return null; return null;
} }
@ -256,7 +264,7 @@ public class CacheService {
redisTemplate.opsForValue().set(MINUTES_DETAIL_PREFIX + minutesId, value, Duration.ofMinutes(5)); redisTemplate.opsForValue().set(MINUTES_DETAIL_PREFIX + minutesId, value, Duration.ofMinutes(5));
log.debug("회의록 상세 캐시 저장 - minutesId: {}", minutesId); log.debug("회의록 상세 캐시 저장 - minutesId: {}", minutesId);
} catch (Exception e) { } catch (Exception e) {
log.error("회의록 상세 캐시 저장 실패 - minutesId: {}", minutesId, e); log.warn("회의록 상세 캐시 저장 실패 (서비스는 정상 동작) - minutesId: {}, 에러: {}", minutesId, e.getMessage());
} }
} }
@ -267,7 +275,7 @@ public class CacheService {
return objectMapper.readValue(value, MinutesDetailResponse.class); return objectMapper.readValue(value, MinutesDetailResponse.class);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("회의록 상세 캐시 조회 실패 - minutesId: {}", minutesId, e); log.warn("회의록 상세 캐시 조회 실패 (DB에서 조회) - minutesId: {}, 에러: {}", minutesId, e.getMessage());
} }
return null; return null;
} }
@ -289,7 +297,7 @@ public class CacheService {
redisTemplate.opsForValue().set(TODO_LIST_PREFIX + cacheKey, value, Duration.ofMinutes(10)); redisTemplate.opsForValue().set(TODO_LIST_PREFIX + cacheKey, value, Duration.ofMinutes(10));
log.debug("Todo 목록 캐시 저장 - key: {}", cacheKey); log.debug("Todo 목록 캐시 저장 - key: {}", cacheKey);
} catch (Exception e) { } catch (Exception e) {
log.error("Todo 목록 캐시 저장 실패 - key: {}", cacheKey, e); log.warn("Todo 목록 캐시 저장 실패 (서비스는 정상 동작) - key: {}, 에러: {}", cacheKey, e.getMessage());
} }
} }
@ -300,7 +308,7 @@ public class CacheService {
return objectMapper.readValue(value, TodoListResponse.class); return objectMapper.readValue(value, TodoListResponse.class);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Todo 목록 캐시 조회 실패 - key: {}", cacheKey, e); log.warn("Todo 목록 캐시 조회 실패 (DB에서 조회) - key: {}, 에러: {}", cacheKey, e.getMessage());
} }
return null; return null;
} }
@ -327,7 +335,7 @@ public class CacheService {
redisTemplate.opsForValue().set(TEMPLATE_LIST_PREFIX + cacheKey, value, Duration.ofHours(1)); redisTemplate.opsForValue().set(TEMPLATE_LIST_PREFIX + cacheKey, value, Duration.ofHours(1));
log.debug("템플릿 목록 캐시 저장 - key: {}", cacheKey); log.debug("템플릿 목록 캐시 저장 - key: {}", cacheKey);
} catch (Exception e) { } catch (Exception e) {
log.error("템플릿 목록 캐시 저장 실패 - key: {}", cacheKey, e); log.warn("템플릿 목록 캐시 저장 실패 (서비스는 정상 동작) - key: {}, 에러: {}", cacheKey, e.getMessage());
} }
} }
@ -338,7 +346,7 @@ public class CacheService {
return objectMapper.readValue(value, TemplateListResponse.class); return objectMapper.readValue(value, TemplateListResponse.class);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("템플릿 목록 캐시 조회 실패 - key: {}", cacheKey, e); log.warn("템플릿 목록 캐시 조회 실패 (DB에서 조회) - key: {}, 에러: {}", cacheKey, e.getMessage());
} }
return null; return null;
} }
@ -349,7 +357,7 @@ public class CacheService {
redisTemplate.opsForValue().set(TEMPLATE_DETAIL_PREFIX + templateId, value, Duration.ofHours(1)); redisTemplate.opsForValue().set(TEMPLATE_DETAIL_PREFIX + templateId, value, Duration.ofHours(1));
log.debug("템플릿 상세 캐시 저장 - templateId: {}", templateId); log.debug("템플릿 상세 캐시 저장 - templateId: {}", templateId);
} catch (Exception e) { } catch (Exception e) {
log.error("템플릿 상세 캐시 저장 실패 - templateId: {}", templateId, e); log.warn("템플릿 상세 캐시 저장 실패 (서비스는 정상 동작) - templateId: {}, 에러: {}", templateId, e.getMessage());
} }
} }
@ -360,7 +368,7 @@ public class CacheService {
return objectMapper.readValue(value, TemplateDetailResponse.class); return objectMapper.readValue(value, TemplateDetailResponse.class);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("템플릿 상세 캐시 조회 실패 - templateId: {}", templateId, e); log.warn("템플릿 상세 캐시 조회 실패 (DB에서 조회) - templateId: {}, 에러: {}", templateId, e.getMessage());
} }
return null; return null;
} }
@ -372,7 +380,7 @@ public class CacheService {
redisTemplate.opsForValue().set(AI_ANALYSIS_PREFIX + minutesId, value, Duration.ofHours(1)); redisTemplate.opsForValue().set(AI_ANALYSIS_PREFIX + minutesId, value, Duration.ofHours(1));
log.debug("AI 분석 결과 캐시 저장 - minutesId: {}", minutesId); log.debug("AI 분석 결과 캐시 저장 - minutesId: {}", minutesId);
} catch (Exception e) { } catch (Exception e) {
log.error("AI 분석 결과 캐시 저장 실패 - minutesId: {}", minutesId, e); log.warn("AI 분석 결과 캐시 저장 실패 (서비스는 정상 동작) - minutesId: {}, 에러: {}", minutesId, e.getMessage());
} }
} }
@ -385,7 +393,7 @@ public class CacheService {
return Optional.of(analysis); return Optional.of(analysis);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("AI 분석 결과 캐시 조회 실패 - minutesId: {}", minutesId, e); log.warn("AI 분석 결과 캐시 조회 실패 (새로 생성) - minutesId: {}, 에러: {}", minutesId, e.getMessage());
} }
return Optional.empty(); return Optional.empty();
} }
@ -395,7 +403,7 @@ public class CacheService {
redisTemplate.delete(AI_ANALYSIS_PREFIX + minutesId); redisTemplate.delete(AI_ANALYSIS_PREFIX + minutesId);
log.debug("AI 분석 캐시 삭제 - minutesId: {}", minutesId); log.debug("AI 분석 캐시 삭제 - minutesId: {}", minutesId);
} catch (Exception e) { } catch (Exception e) {
log.error("AI 분석 캐시 삭제 실패 - minutesId: {}", minutesId, e); log.warn("AI 분석 캐시 삭제 실패 - minutesId: {}, 에러: {}", minutesId, e.getMessage());
} }
} }
} }

View File

@ -19,6 +19,8 @@ import com.unicorn.hgzero.meeting.infra.event.publisher.EventPublisher;
import com.unicorn.hgzero.meeting.infra.gateway.AiServiceGateway; import com.unicorn.hgzero.meeting.infra.gateway.AiServiceGateway;
import com.unicorn.hgzero.meeting.biz.dto.AiAnalysisDTO; import com.unicorn.hgzero.meeting.biz.dto.AiAnalysisDTO;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesFinalizedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesSectionDTO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ -242,9 +244,60 @@ public class MinutesController {
// 캐시 무효화 (목록 캐시) // 캐시 무효화 (목록 캐시)
cacheService.evictCacheMinutesList(userId); cacheService.evictCacheMinutesList(userId);
// 회의록 확정 이벤트 발행 // 회의록 확정 이벤트 발행 (알림용 - 기존)
eventPublisher.publishMinutesFinalized(minutesId, finalizedMinutes.getTitle(), userId, userName); eventPublisher.publishMinutesFinalized(minutesId, finalizedMinutes.getTitle(), userId, userName);
// RAG 서비스용 회의록 확정 이벤트 발행 (완전한 데이터 포함)
try {
// 회의 정보 조회
Meeting meeting = meetingService.getMeeting(finalizedMinutes.getMeetingId());
// 섹션 정보 변환
List<MinutesSectionDTO> sectionDTOs = finalizedMinutes.getSections().stream()
.map(section -> MinutesSectionDTO.builder()
.sectionId(section.getSectionId())
.type(section.getSectionType())
.title(section.getTitle())
.content(section.getContent())
.order(section.getSectionOrder())
.verified(section.getIsVerified())
.build())
.collect(Collectors.toList());
// MinutesFinalizedEvent 생성
MinutesFinalizedEvent ragEvent = MinutesFinalizedEvent.builder()
.timestamp(LocalDateTime.now())
.data(MinutesFinalizedEvent.MinutesData.builder()
// Meeting 정보
.meetingId(meeting.getMeetingId())
.title(meeting.getTitle())
.purpose(meeting.getPurpose())
.description(meeting.getDescription())
.scheduledAt(meeting.getScheduledAt())
.location(meeting.getLocation())
.organizerId(meeting.getOrganizerId())
// Minutes 정보
.minutesId(finalizedMinutes.getMinutesId())
.status(finalizedMinutes.getStatus())
.version(finalizedMinutes.getVersion())
.createdBy(finalizedMinutes.getCreatedBy())
.finalizedBy(userId)
.finalizedAt(finalizedMinutes.getFinalizedAt())
// 섹션 정보
.sections(sectionDTOs)
.build())
.build();
// RAG용 이벤트 발행
eventPublisher.publishMinutesFinalizedForRAG(ragEvent);
log.info("RAG용 회의록 확정 이벤트 발행 완료 - minutesId: {}, meetingId: {}",
minutesId, meeting.getMeetingId());
} catch (Exception e) {
log.error("RAG용 회의록 확정 이벤트 발행 실패 - minutesId: {}, error: {}",
minutesId, e.getMessage(), e);
// RAG 이벤트 발행 실패는 무시하고 진행 (회의록 확정 자체는 성공)
}
log.info("회의록 확정 성공 - minutesId: {}", minutesId); log.info("회의록 확정 성공 - minutesId: {}", minutesId);
return ResponseEntity.ok(ApiResponse.success(response)); return ResponseEntity.ok(ApiResponse.success(response));

View File

@ -0,0 +1,58 @@
package com.unicorn.hgzero.meeting.infra.event.dto;
import lombok.Builder;
import lombok.Getter;
import java.time.LocalDateTime;
import java.util.List;
/**
* 회의록 확정 이벤트
* RAG 서비스에서 회의록 embedding 생성 저장을 위한 이벤트
*/
@Getter
@Builder
public class MinutesFinalizedEvent {
/**
* 이벤트 타입
*/
private final String eventType = "MINUTES_FINALIZED";
/**
* 이벤트 발생 시간
*/
private final LocalDateTime timestamp;
/**
* 이벤트 데이터
*/
private final MinutesData data;
/**
* 회의록 상세 데이터
*/
@Getter
@Builder
public static class MinutesData {
// Meeting 정보
private final String meetingId;
private final String title;
private final String purpose;
private final String description;
private final LocalDateTime scheduledAt;
private final String location;
private final String organizerId;
// Minutes 정보
private final String minutesId;
private final String status;
private final Integer version;
private final String createdBy;
private final String finalizedBy;
private final LocalDateTime finalizedAt;
// 섹션 정보
private final List<MinutesSectionDTO> sections;
}
}

View File

@ -0,0 +1,43 @@
package com.unicorn.hgzero.meeting.infra.event.dto;
import lombok.Builder;
import lombok.Getter;
/**
* 회의록 섹션 DTO
* Event Hub 메시지에 포함될 섹션 정보
*/
@Getter
@Builder
public class MinutesSectionDTO {
/**
* 섹션 ID
*/
private final String sectionId;
/**
* 섹션 타입 (DISCUSSION, DECISION, ACTION_ITEM )
*/
private final String type;
/**
* 섹션 제목
*/
private final String title;
/**
* 섹션 내용
*/
private final String content;
/**
* 섹션 순서
*/
private final Integer order;
/**
* 검증 여부
*/
private final Boolean verified;
}

View File

@ -11,6 +11,7 @@ import com.unicorn.hgzero.meeting.infra.event.dto.MeetingEndedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent; import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesFinalizedEvent;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -157,6 +158,16 @@ public class EventHubPublisher implements EventPublisher {
EventHubConstants.EVENT_TYPE_MINUTES_ANALYSIS_REQUEST); EventHubConstants.EVENT_TYPE_MINUTES_ANALYSIS_REQUEST);
} }
@Override
public void publishMinutesFinalizedForRAG(MinutesFinalizedEvent event) {
publishEvent(event, event.getData().getMinutesId(),
EventHubConstants.TOPIC_MINUTES,
EventHubConstants.EVENT_TYPE_MINUTES_FINALIZED);
log.info("RAG용 회의록 확정 이벤트 발행 완료 - minutesId: {}, meetingId: {}",
event.getData().getMinutesId(), event.getData().getMeetingId());
}
/** /**
* 이벤트 발행 공통 메서드 * 이벤트 발행 공통 메서드
* *

View File

@ -5,6 +5,7 @@ import com.unicorn.hgzero.meeting.infra.event.dto.MeetingEndedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent; import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesFinalizedEvent;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@ -62,6 +63,13 @@ public interface EventPublisher {
*/ */
void publishMinutesFinalized(String minutesId, String title, String finalizedBy, String finalizedByName); void publishMinutesFinalized(String minutesId, String title, String finalizedBy, String finalizedByName);
/**
* 회의록 확정 이벤트 발행 (RAG 서비스용 완전한 이벤트)
*
* @param event 회의록 확정 이벤트 (전체 데이터 포함)
*/
void publishMinutesFinalizedForRAG(MinutesFinalizedEvent event);
/** /**
* 회의 생성 알림 발행 (편의 메서드) * 회의 생성 알림 발행 (편의 메서드)
* 참석자에게 회의 초대 이메일 발송 * 참석자에게 회의 초대 이메일 발송

View File

@ -5,6 +5,7 @@ import com.unicorn.hgzero.meeting.infra.event.dto.MeetingEndedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent; import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent; import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesFinalizedEvent;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
@ -72,4 +73,10 @@ public class NoOpEventPublisher implements EventPublisher {
public void publishMinutesAnalysisRequest(MinutesAnalysisRequestEvent event) { public void publishMinutesAnalysisRequest(MinutesAnalysisRequestEvent event) {
log.debug("[NoOp] Minutes analysis request event: minutesId={}", event.getMinutesId()); log.debug("[NoOp] Minutes analysis request event: minutesId={}", event.getMinutesId());
} }
@Override
public void publishMinutesFinalizedForRAG(MinutesFinalizedEvent event) {
log.debug("[NoOp] Minutes finalized for RAG: minutesId={}, meetingId={}",
event.getData().getMinutesId(), event.getData().getMeetingId());
}
} }

View File

@ -65,7 +65,7 @@ public class MeetingEntity extends BaseTimeEntity {
/** /**
* 회의 참석자 목록 (일대다 관계) * 회의 참석자 목록 (일대다 관계)
*/ */
@OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = false)
@Builder.Default @Builder.Default
private List<MeetingParticipantEntity> participants = new ArrayList<>(); private List<MeetingParticipantEntity> participants = new ArrayList<>();