mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 07:56:24 +00:00
Merge branch 'main' of https://github.com/hwanny1128/HGZero
This commit is contained in:
commit
de6c68d4d1
File diff suppressed because it is too large
Load Diff
@ -41,5 +41,6 @@ public class MeetingEndDTO {
|
|||||||
@Builder
|
@Builder
|
||||||
public static class TodoSummaryDTO {
|
public static class TodoSummaryDTO {
|
||||||
private final String title;
|
private final String title;
|
||||||
|
private final String assignee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,6 +126,12 @@ public class MinutesDTO {
|
|||||||
*/
|
*/
|
||||||
private final Integer participantCount;
|
private final Integer participantCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 검증완료율 (작성중 상태일 때만 유효)
|
||||||
|
* 0-100 사이의 값
|
||||||
|
*/
|
||||||
|
private final Integer verificationRate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회의 정보
|
* 회의 정보
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -124,8 +124,8 @@ public class EndMeetingService implements EndMeetingUseCase {
|
|||||||
meetingRepository.save(meeting);
|
meetingRepository.save(meeting);
|
||||||
log.info("회의 상태 업데이트 완료 - status: {}", meeting.getStatus());
|
log.info("회의 상태 업데이트 완료 - status: {}", meeting.getStatus());
|
||||||
|
|
||||||
// 9. 응답 DTO 생성
|
// 9. 응답 DTO 생성 (AI 응답의 todos를 그대로 사용)
|
||||||
MeetingEndDTO result = createMeetingEndDTO(meeting, analysis, todos, participantMinutesList.size());
|
MeetingEndDTO result = createMeetingEndDTO(meeting, aiResponse, todos.size(), participantMinutesList.size());
|
||||||
|
|
||||||
log.info("회의 종료 처리 완료 - meetingId: {}, 안건 수: {}, Todo 수: {}",
|
log.info("회의 종료 처리 완료 - meetingId: {}, 안건 수: {}, Todo 수: {}",
|
||||||
meetingId, analysis.getAgendaAnalyses().size(), todos.size());
|
meetingId, analysis.getAgendaAnalyses().size(), todos.size());
|
||||||
@ -308,7 +308,8 @@ public class EndMeetingService implements EndMeetingUseCase {
|
|||||||
private List<TodoEntity> createAndSaveTodos(MeetingEntity meeting, ConsolidateResponse aiResponse, MeetingAnalysis analysis) {
|
private List<TodoEntity> createAndSaveTodos(MeetingEntity meeting, ConsolidateResponse aiResponse, MeetingAnalysis analysis) {
|
||||||
List<TodoEntity> todos = aiResponse.getAgendaSummaries().stream()
|
List<TodoEntity> todos = aiResponse.getAgendaSummaries().stream()
|
||||||
.<TodoEntity>flatMap(agenda -> {
|
.<TodoEntity>flatMap(agenda -> {
|
||||||
// agendaId는 향후 Todo와 안건 매핑에 사용될 수 있음 (현재는 사용하지 않음)
|
// 안건 번호를 description에 저장하여 나중에 필터링에 사용
|
||||||
|
Integer agendaNumber = agenda.getAgendaNumber();
|
||||||
List<ExtractedTodoDTO> todoList = agenda.getTodos() != null ? agenda.getTodos() : List.of();
|
List<ExtractedTodoDTO> todoList = agenda.getTodos() != null ? agenda.getTodos() : List.of();
|
||||||
return todoList.stream()
|
return todoList.stream()
|
||||||
.<TodoEntity>map(todo -> TodoEntity.builder()
|
.<TodoEntity>map(todo -> TodoEntity.builder()
|
||||||
@ -316,6 +317,7 @@ public class EndMeetingService implements EndMeetingUseCase {
|
|||||||
.meetingId(meeting.getMeetingId())
|
.meetingId(meeting.getMeetingId())
|
||||||
.minutesId(meeting.getMeetingId()) // 실제로는 minutesId 필요
|
.minutesId(meeting.getMeetingId()) // 실제로는 minutesId 필요
|
||||||
.title(todo.getTitle())
|
.title(todo.getTitle())
|
||||||
|
.description("안건" + agendaNumber) // 안건 번호를 description에 임시 저장
|
||||||
.assigneeId(todo.getAssignee() != null ? todo.getAssignee() : "") // AI가 추출한 담당자
|
.assigneeId(todo.getAssignee() != null ? todo.getAssignee() : "") // AI가 추출한 담당자
|
||||||
.status("PENDING")
|
.status("PENDING")
|
||||||
.build());
|
.build());
|
||||||
@ -342,33 +344,36 @@ public class EndMeetingService implements EndMeetingUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회의 종료 결과 DTO 생성
|
* 회의 종료 결과 DTO 생성 (AI 응답 직접 사용)
|
||||||
*/
|
*/
|
||||||
private MeetingEndDTO createMeetingEndDTO(MeetingEntity meeting, MeetingAnalysis analysis,
|
private MeetingEndDTO createMeetingEndDTO(MeetingEntity meeting, ConsolidateResponse aiResponse,
|
||||||
List<TodoEntity> todos, int participantCount) {
|
int todoCount, int participantCount) {
|
||||||
// 회의 소요 시간 계산
|
// 회의 소요 시간 계산
|
||||||
int durationMinutes = calculateDurationMinutes(meeting.getStartedAt(), meeting.getEndedAt());
|
int durationMinutes = calculateDurationMinutes(meeting.getStartedAt(), meeting.getEndedAt());
|
||||||
|
|
||||||
// 안건별 요약 DTO 생성
|
// AI 응답의 안건 정보를 그대로 DTO로 변환 (todos 포함)
|
||||||
List<MeetingEndDTO.AgendaSummaryDTO> agendaSummaries = analysis.getAgendaAnalyses().stream()
|
List<MeetingEndDTO.AgendaSummaryDTO> agendaSummaries = aiResponse.getAgendaSummaries().stream()
|
||||||
.<MeetingEndDTO.AgendaSummaryDTO>map(agenda -> {
|
.<MeetingEndDTO.AgendaSummaryDTO>map(agenda -> {
|
||||||
// 해당 안건의 Todo 필터링 (agendaId가 없을 수 있음)
|
// 안건별 todos 변환
|
||||||
List<MeetingEndDTO.TodoSummaryDTO> agendaTodos = todos.stream()
|
List<MeetingEndDTO.TodoSummaryDTO> todoList = new ArrayList<>();
|
||||||
.filter(todo -> agenda.getAgendaId().equals(todo.getMinutesId())) // 임시 매핑
|
if (agenda.getTodos() != null) {
|
||||||
.<MeetingEndDTO.TodoSummaryDTO>map(todo -> MeetingEndDTO.TodoSummaryDTO.builder()
|
for (ExtractedTodoDTO todo : agenda.getTodos()) {
|
||||||
|
todoList.add(MeetingEndDTO.TodoSummaryDTO.builder()
|
||||||
.title(todo.getTitle())
|
.title(todo.getTitle())
|
||||||
.build())
|
.assignee(todo.getAssignee())
|
||||||
.collect(Collectors.toList());
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return MeetingEndDTO.AgendaSummaryDTO.builder()
|
return MeetingEndDTO.AgendaSummaryDTO.builder()
|
||||||
.title(agenda.getTitle())
|
.title(agenda.getAgendaTitle())
|
||||||
.aiSummaryShort(agenda.getAiSummaryShort())
|
.aiSummaryShort(agenda.getSummaryShort())
|
||||||
.details(MeetingEndDTO.AgendaDetailsDTO.builder()
|
.details(MeetingEndDTO.AgendaDetailsDTO.builder()
|
||||||
.discussion(agenda.getDiscussion())
|
.discussion(agenda.getSummary())
|
||||||
.decisions(agenda.getDecisions())
|
.decisions(agenda.getDecisions())
|
||||||
.pending(agenda.getPending())
|
.pending(agenda.getPending())
|
||||||
.build())
|
.build())
|
||||||
.todos(agendaTodos)
|
.todos(todoList)
|
||||||
.build();
|
.build();
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -377,9 +382,9 @@ public class EndMeetingService implements EndMeetingUseCase {
|
|||||||
.title(meeting.getTitle())
|
.title(meeting.getTitle())
|
||||||
.participantCount(participantCount)
|
.participantCount(participantCount)
|
||||||
.durationMinutes(durationMinutes)
|
.durationMinutes(durationMinutes)
|
||||||
.agendaCount(analysis.getAgendaAnalyses().size())
|
.agendaCount(aiResponse.getAgendaSummaries().size())
|
||||||
.todoCount(todos.size())
|
.todoCount(todoCount)
|
||||||
.keywords(analysis.getKeywords())
|
.keywords(aiResponse.getKeywords())
|
||||||
.agendaSummaries(agendaSummaries)
|
.agendaSummaries(agendaSummaries)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -240,20 +240,21 @@ public class MinutesService implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 사용자 ID로 회의록 목록 조회 (페이징)
|
* 사용자 ID로 회의록 목록 조회 (페이징)
|
||||||
|
* 사용자가 생성했거나 참여한 회의의 회의록을 모두 조회
|
||||||
*/
|
*/
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Page<MinutesDTO> getMinutesListByUserId(String userId, Pageable pageable) {
|
public Page<MinutesDTO> getMinutesListByUserId(String userId, Pageable pageable) {
|
||||||
log.debug("Getting minutes list by userId: {}", userId);
|
log.debug("Getting minutes list by userId: {}", userId);
|
||||||
|
|
||||||
// 여기서는 임시로 작성자 기준으로 조회 (실제로는 참석자나 권한 기반으로 조회해야 함)
|
// 사용자가 생성했거나 참여한 회의의 회의록 조회
|
||||||
List<Minutes> minutesList = minutesReader.findByCreatedBy(userId);
|
List<Minutes> minutesList = minutesReader.findByParticipantUserId(userId);
|
||||||
|
|
||||||
// Minutes를 MinutesDTO로 변환
|
// Minutes를 MinutesDTO로 변환
|
||||||
List<MinutesDTO> minutesDTOList = minutesList.stream()
|
List<MinutesDTO> minutesDTOList = minutesList.stream()
|
||||||
.map(this::convertToMinutesDTO)
|
.map(this::convertToMinutesDTO)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// 페이징 처리 (임시로 전체 목록 반환)
|
// 페이징 처리
|
||||||
int start = (int) pageable.getOffset();
|
int start = (int) pageable.getOffset();
|
||||||
int end = Math.min((start + pageable.getPageSize()), minutesDTOList.size());
|
int end = Math.min((start + pageable.getPageSize()), minutesDTOList.size());
|
||||||
List<MinutesDTO> pageContent = minutesDTOList.subList(start, end);
|
List<MinutesDTO> pageContent = minutesDTOList.subList(start, end);
|
||||||
@ -371,6 +372,15 @@ public class MinutesService implements
|
|||||||
log.warn("섹션 정보 변환 실패 - minutesId: {}", minutes.getMinutesId(), e);
|
log.warn("섹션 정보 변환 실패 - minutesId: {}", minutes.getMinutesId(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 검증완료율 계산 (작성중 상태일 때만)
|
||||||
|
Integer verificationRate = null;
|
||||||
|
if ("DRAFT".equals(minutes.getStatus()) && sectionDTOs != null && !sectionDTOs.isEmpty()) {
|
||||||
|
long verifiedCount = sectionDTOs.stream()
|
||||||
|
.filter(section -> Boolean.TRUE.equals(section.getIsVerified()))
|
||||||
|
.count();
|
||||||
|
verificationRate = (int) ((verifiedCount * 100) / sectionDTOs.size());
|
||||||
|
}
|
||||||
|
|
||||||
// decisions 값 로깅
|
// decisions 값 로깅
|
||||||
log.info("Minutes decisions 값 확인 - minutesId: {}, decisions: {}",
|
log.info("Minutes decisions 값 확인 - minutesId: {}, decisions: {}",
|
||||||
minutes.getMinutesId(), minutes.getDecisions());
|
minutes.getMinutesId(), minutes.getDecisions());
|
||||||
@ -389,6 +399,7 @@ public class MinutesService implements
|
|||||||
.todoCount(todoCount)
|
.todoCount(todoCount)
|
||||||
.completedTodoCount(completedTodoCount)
|
.completedTodoCount(completedTodoCount)
|
||||||
.participantCount(participantCount)
|
.participantCount(participantCount)
|
||||||
|
.verificationRate(verificationRate)
|
||||||
.memo("") // 메모 필드는 추후 구현
|
.memo("") // 메모 필드는 추후 구현
|
||||||
.sections(sectionDTOs) // 섹션 정보 추가
|
.sections(sectionDTOs) // 섹션 정보 추가
|
||||||
.decisions(minutes.getDecisions()) // decisions 필드 추가
|
.decisions(minutes.getDecisions()) // decisions 필드 추가
|
||||||
|
|||||||
@ -56,4 +56,13 @@ public interface MinutesReader {
|
|||||||
* @return AI 통합 회의록
|
* @return AI 통합 회의록
|
||||||
*/
|
*/
|
||||||
Optional<Minutes> findConsolidatedMinutesByMeetingId(String meetingId);
|
Optional<Minutes> findConsolidatedMinutesByMeetingId(String meetingId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사용자가 참여한 회의의 회의록 목록 조회
|
||||||
|
* 사용자가 생성했거나 참여한 회의의 회의록을 모두 조회
|
||||||
|
*
|
||||||
|
* @param userId 사용자 ID
|
||||||
|
* @return 회의록 목록
|
||||||
|
*/
|
||||||
|
List<Minutes> findByParticipantUserId(String userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,12 +110,13 @@ public class MinutesController {
|
|||||||
|
|
||||||
// DTO를 Response 형식으로 변환
|
// DTO를 Response 형식으로 변환
|
||||||
List<MinutesListResponse.MinutesItem> minutesList = minutesPage.getContent().stream()
|
List<MinutesListResponse.MinutesItem> minutesList = minutesPage.getContent().stream()
|
||||||
.map(this::convertToMinutesItem)
|
.map(dto -> convertToMinutesItem(dto, userId))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// 필터링 적용 (상태별)
|
// 필터링 적용 (상태별, 참여 유형별, 검색어)
|
||||||
List<MinutesListResponse.MinutesItem> filteredMinutes = minutesList.stream()
|
List<MinutesListResponse.MinutesItem> filteredMinutes = minutesList.stream()
|
||||||
.filter(item -> filterByStatus(item, status))
|
.filter(item -> filterByStatus(item, status))
|
||||||
|
.filter(item -> filterByParticipationType(item, participationType))
|
||||||
.filter(item -> filterBySearch(item, search))
|
.filter(item -> filterBySearch(item, search))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
@ -489,8 +490,21 @@ public class MinutesController {
|
|||||||
*/
|
*/
|
||||||
private MinutesListResponse.Statistics calculateRealStatistics(String userId, String participationType) {
|
private MinutesListResponse.Statistics calculateRealStatistics(String userId, String participationType) {
|
||||||
try {
|
try {
|
||||||
// 전체 회의록 조회 (작성자 기준)
|
// 전체 회의록 조회 (참여자 기준)
|
||||||
List<Minutes> allMinutes = minutesService.getMinutesByCreator(userId);
|
List<MinutesDTO> allMinutes = minutesService.getMinutesListByUserId(userId, PageRequest.of(0, Integer.MAX_VALUE)).getContent();
|
||||||
|
|
||||||
|
// 참여 유형 필터링
|
||||||
|
if (participationType != null && !participationType.isEmpty()) {
|
||||||
|
if ("created".equals(participationType)) {
|
||||||
|
allMinutes = allMinutes.stream()
|
||||||
|
.filter(m -> userId.equals(m.getCreatedBy()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else if ("attended".equals(participationType)) {
|
||||||
|
allMinutes = allMinutes.stream()
|
||||||
|
.filter(m -> !userId.equals(m.getCreatedBy()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
long totalCount = allMinutes.size();
|
long totalCount = allMinutes.size();
|
||||||
long draftCount = allMinutes.stream()
|
long draftCount = allMinutes.stream()
|
||||||
@ -515,10 +529,17 @@ public class MinutesController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MinutesListResponse.MinutesItem convertToMinutesItem(MinutesDTO minutesDTO) {
|
private MinutesListResponse.MinutesItem convertToMinutesItem(MinutesDTO minutesDTO, String userId) {
|
||||||
// 완료율 계산
|
// 검증완료율 계산 (작성중 상태일 때는 verificationRate 사용, 확정완료시 100%)
|
||||||
int completionRate = minutesDTO.getTodoCount() > 0 ?
|
int completionRate;
|
||||||
(minutesDTO.getCompletedTodoCount() * 100) / minutesDTO.getTodoCount() : 100;
|
if ("DRAFT".equals(minutesDTO.getStatus()) && minutesDTO.getVerificationRate() != null) {
|
||||||
|
completionRate = minutesDTO.getVerificationRate();
|
||||||
|
} else if ("FINALIZED".equals(minutesDTO.getStatus())) {
|
||||||
|
completionRate = 100;
|
||||||
|
} else {
|
||||||
|
// 기본값 0
|
||||||
|
completionRate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return MinutesListResponse.MinutesItem.builder()
|
return MinutesListResponse.MinutesItem.builder()
|
||||||
.minutesId(minutesDTO.getMinutesId())
|
.minutesId(minutesDTO.getMinutesId())
|
||||||
@ -535,7 +556,7 @@ public class MinutesController {
|
|||||||
.todoCount(minutesDTO.getTodoCount())
|
.todoCount(minutesDTO.getTodoCount())
|
||||||
.completedTodoCount(minutesDTO.getCompletedTodoCount())
|
.completedTodoCount(minutesDTO.getCompletedTodoCount())
|
||||||
.completionRate(completionRate)
|
.completionRate(completionRate)
|
||||||
.isCreatedByUser(true) // 현재는 작성자 기준으로만 조회하므로 true
|
.isCreatedByUser(minutesDTO.getCreatedBy().equals(userId)) // 현재 사용자가 생성자인지 확인
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,10 +577,19 @@ public class MinutesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 참여 유형별 필터링 - 현재는 사용하지 않음 (작성자 기준으로만 조회)
|
* 참여 유형별 필터링
|
||||||
*/
|
*/
|
||||||
private boolean filterByParticipationType(MinutesListResponse.MinutesItem item, String participationType, String userId) {
|
private boolean filterByParticipationType(MinutesListResponse.MinutesItem item, String participationType) {
|
||||||
// 현재는 작성자 기준으로만 조회하므로 항상 true 반환
|
if (participationType == null || participationType.isEmpty()) {
|
||||||
|
return true; // 필터 미적용시 모두 표시
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("created".equals(participationType)) {
|
||||||
|
return item.isCreatedByUser(); // 사용자가 생성한 회의록만
|
||||||
|
} else if ("attended".equals(participationType)) {
|
||||||
|
return !item.isCreatedByUser(); // 사용자가 참여만 한 회의록
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,7 +637,7 @@ public class MinutesController {
|
|||||||
private MinutesListResponse.Statistics calculateStatistics(List<MinutesListResponse.MinutesItem> allItems,
|
private MinutesListResponse.Statistics calculateStatistics(List<MinutesListResponse.MinutesItem> allItems,
|
||||||
String participationType, String userId) {
|
String participationType, String userId) {
|
||||||
List<MinutesListResponse.MinutesItem> filteredItems = allItems.stream()
|
List<MinutesListResponse.MinutesItem> filteredItems = allItems.stream()
|
||||||
.filter(item -> filterByParticipationType(item, participationType, userId))
|
.filter(item -> filterByParticipationType(item, participationType))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
long totalCount = filteredItems.size();
|
long totalCount = filteredItems.size();
|
||||||
|
|||||||
@ -18,8 +18,10 @@ import org.springframework.stereotype.Component;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -44,7 +46,7 @@ public class DashboardGateway implements DashboardReader {
|
|||||||
// 1. 다가오는 회의 목록 조회 (향후 30일, 최대 10개)
|
// 1. 다가오는 회의 목록 조회 (향후 30일, 최대 10개)
|
||||||
List<Meeting> upcomingMeetings = getUpcomingMeetings(userId);
|
List<Meeting> upcomingMeetings = getUpcomingMeetings(userId);
|
||||||
|
|
||||||
// 2. 최근 회의록 목록 조회 (최근 7일, 최대 10개)
|
// 2. 최근 회의록 목록 조회 (최근 30일, 최대 4개)
|
||||||
List<Minutes> recentMinutes = getRecentMinutes(userId);
|
List<Minutes> recentMinutes = getRecentMinutes(userId);
|
||||||
|
|
||||||
// 3. 통계 정보 계산 (최근 30일 기준)
|
// 3. 통계 정보 계산 (최근 30일 기준)
|
||||||
@ -235,36 +237,27 @@ public class DashboardGateway implements DashboardReader {
|
|||||||
* 기간별 최근 회의록 목록 조회
|
* 기간별 최근 회의록 목록 조회
|
||||||
*/
|
*/
|
||||||
private List<Minutes> getRecentMinutesByPeriod(String userId, LocalDateTime startTime, LocalDateTime endTime) {
|
private List<Minutes> getRecentMinutesByPeriod(String userId, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
Set<String> userMinutesIds = new HashSet<>();
|
log.debug("회의록 조회 시작 - userId: {}, startTime: {}, endTime: {}", userId, startTime, endTime);
|
||||||
|
|
||||||
// 작성자로 참여한 회의록 조회
|
// 사용자가 작성한 회의록만 조회 (createdBy = userId)
|
||||||
List<MinutesEntity> createdMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
|
List<MinutesEntity> userMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
|
||||||
.filter(m -> m.getCreatedAt().isAfter(startTime) && m.getCreatedAt().isBefore(endTime))
|
.filter(m -> m.getCreatedAt() != null &&
|
||||||
.toList();
|
m.getCreatedAt().isAfter(startTime) &&
|
||||||
|
m.getCreatedAt().isBefore(endTime))
|
||||||
createdMinutes.forEach(m -> userMinutesIds.add(m.getMinutesId()));
|
|
||||||
|
|
||||||
// 참석한 회의의 회의록 조회
|
|
||||||
List<String> participantMeetingIds = meetingParticipantJpaRepository.findByUserId(userId).stream()
|
|
||||||
.map(p -> p.getMeetingId())
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
List<MinutesEntity> participatedMinutes = minutesJpaRepository.findAll().stream()
|
|
||||||
.filter(m -> participantMeetingIds.contains(m.getMeetingId()))
|
|
||||||
.filter(m -> m.getCreatedAt().isAfter(startTime) && m.getCreatedAt().isBefore(endTime))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
participatedMinutes.forEach(m -> userMinutesIds.add(m.getMinutesId()));
|
|
||||||
|
|
||||||
// 중복 제거 후 최종 수정 시간순 정렬
|
|
||||||
return minutesJpaRepository.findAll().stream()
|
|
||||||
.filter(m -> userMinutesIds.contains(m.getMinutesId()))
|
|
||||||
.sorted((m1, m2) -> {
|
.sorted((m1, m2) -> {
|
||||||
LocalDateTime time1 = m1.getUpdatedAt() != null ? m1.getUpdatedAt() : m1.getCreatedAt();
|
LocalDateTime time1 = m1.getUpdatedAt() != null ? m1.getUpdatedAt() : m1.getCreatedAt();
|
||||||
LocalDateTime time2 = m2.getUpdatedAt() != null ? m2.getUpdatedAt() : m2.getCreatedAt();
|
LocalDateTime time2 = m2.getUpdatedAt() != null ? m2.getUpdatedAt() : m2.getCreatedAt();
|
||||||
return time2.compareTo(time1); // 최신순
|
return time2.compareTo(time1); // 최신순
|
||||||
})
|
})
|
||||||
// limit 제거 - 상위 메서드에서 필터링
|
.toList();
|
||||||
|
|
||||||
|
log.debug("조회된 회의록 수: {}", userMinutes.size());
|
||||||
|
userMinutes.forEach(m -> {
|
||||||
|
log.debug(" - minutesId: {}, meetingId: {}, title: {}, createdBy: {}, createdAt: {}",
|
||||||
|
m.getMinutesId(), m.getMeetingId(), m.getTitle(), m.getCreatedBy(), m.getCreatedAt());
|
||||||
|
});
|
||||||
|
|
||||||
|
return userMinutes.stream()
|
||||||
.map(MinutesEntity::toDomain)
|
.map(MinutesEntity::toDomain)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -75,10 +76,33 @@ public class MinutesGateway implements MinutesReader, MinutesWriter {
|
|||||||
@Override
|
@Override
|
||||||
public Optional<Minutes> findConsolidatedMinutesByMeetingId(String meetingId) {
|
public Optional<Minutes> findConsolidatedMinutesByMeetingId(String meetingId) {
|
||||||
log.debug("회의 ID로 AI 통합 회의록 조회: {}", meetingId);
|
log.debug("회의 ID로 AI 통합 회의록 조회: {}", meetingId);
|
||||||
return minutesJpaRepository.findByMeetingIdAndUserIdIsNull(meetingId)
|
List<MinutesEntity> consolidatedMinutes = minutesJpaRepository.findByMeetingIdAndUserIdIsNull(meetingId);
|
||||||
|
|
||||||
|
if (consolidatedMinutes.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 여러 개가 있을 경우 가장 최신 것을 반환 (updatedAt 또는 createdAt 기준)
|
||||||
|
return consolidatedMinutes.stream()
|
||||||
|
.sorted((m1, m2) -> {
|
||||||
|
LocalDateTime time1 = m1.getUpdatedAt() != null ? m1.getUpdatedAt() : m1.getCreatedAt();
|
||||||
|
LocalDateTime time2 = m2.getUpdatedAt() != null ? m2.getUpdatedAt() : m2.getCreatedAt();
|
||||||
|
return time2.compareTo(time1); // 최신순
|
||||||
|
})
|
||||||
|
.findFirst()
|
||||||
.map(MinutesEntity::toDomain);
|
.map(MinutesEntity::toDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Minutes> findByParticipantUserId(String userId) {
|
||||||
|
log.debug("사용자가 참여한 회의의 회의록 조회: {}", userId);
|
||||||
|
// 사용자가 생성한 회의록과 사용자가 참여한 회의의 회의록을 모두 조회
|
||||||
|
// 현재는 생성자 기준으로만 조회 (추후 참석자 테이블과 조인하여 구현 필요)
|
||||||
|
return minutesJpaRepository.findByCreatedByOrParticipantUserId(userId).stream()
|
||||||
|
.map(MinutesEntity::toDomain)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Minutes save(Minutes minutes) {
|
public Minutes save(Minutes minutes) {
|
||||||
// 기존 엔티티 조회 (update) 또는 새로 생성 (insert)
|
// 기존 엔티티 조회 (update) 또는 새로 생성 (insert)
|
||||||
|
|||||||
@ -50,7 +50,20 @@ public interface MinutesJpaRepository extends JpaRepository<MinutesEntity, Strin
|
|||||||
List<MinutesEntity> findByMeetingIdAndUserIdIsNotNull(String meetingId);
|
List<MinutesEntity> findByMeetingIdAndUserIdIsNotNull(String meetingId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회의 ID로 AI 통합 회의록 조회 (user_id IS NULL)
|
* 회의 ID로 AI 통합 회의록 목록 조회 (user_id IS NULL)
|
||||||
*/
|
*/
|
||||||
Optional<MinutesEntity> findByMeetingIdAndUserIdIsNull(String meetingId);
|
List<MinutesEntity> findByMeetingIdAndUserIdIsNull(String meetingId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사용자가 생성했거나 참여한 회의의 회의록 조회
|
||||||
|
* 현재는 생성자 기준으로만 조회 (추후 참석자 테이블과 조인 필요)
|
||||||
|
*
|
||||||
|
* @param userId 사용자 ID
|
||||||
|
* @return 회의록 목록
|
||||||
|
*/
|
||||||
|
default List<MinutesEntity> findByCreatedByOrParticipantUserId(String userId) {
|
||||||
|
// TODO: 참석자 테이블(participants)과 조인하여 참여한 회의의 회의록도 조회하도록 구현 필요
|
||||||
|
// 현재는 임시로 생성자 기준으로만 조회
|
||||||
|
return findByCreatedBy(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user