Merge pull request #62 from hwanny1128/fix/dashboard

Fix: 대시보드 최근 회의 로직 수정
This commit is contained in:
Cho Yoon Jin 2025-10-31 11:10:25 +09:00 committed by GitHub
commit db16306b06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 16084 additions and 21 deletions

File diff suppressed because it is too large Load Diff

View File

@ -73,11 +73,32 @@ public class DashboardGateway implements DashboardReader {
LocalDateTime startTime = calculateStartTime(period);
LocalDateTime endTime = LocalDateTime.now();
// 1. 기간 다가오는 회의 목록 조회
List<Meeting> upcomingMeetings = getUpcomingMeetingsByPeriod(userId, startTime, endTime);
// 1. 기간 다가오는 회의 목록 조회 (UFR-USER-020 기준 적용)
List<Meeting> meetings = getUpcomingMeetingsByPeriod(userId, startTime, endTime);
// 2. 기간 최근 회의록 목록 조회
List<Minutes> recentMinutes = getRecentMinutesByPeriod(userId, startTime, endTime);
// 회의록 생성 여부 확인을 위한 생성
Set<String> meetingsWithMinutes = new HashSet<>();
minutesJpaRepository.findAll().stream()
.forEach(minutes -> meetingsWithMinutes.add(minutes.getMeetingId()));
// 정렬: 1) 회의록 미생성 우선, 2) 빠른 일시
List<Meeting> upcomingMeetings = meetings.stream()
.sorted((m1, m2) -> {
boolean m1HasMinutes = meetingsWithMinutes.contains(m1.getMeetingId());
boolean m2HasMinutes = meetingsWithMinutes.contains(m2.getMeetingId());
if (!m1HasMinutes && m2HasMinutes) return -1;
if (m1HasMinutes && !m2HasMinutes) return 1;
return m1.getScheduledAt().compareTo(m2.getScheduledAt());
})
.limit(3) // 최대 3개
.collect(Collectors.toList());
// 2. 기간 최근 회의록 목록 조회 (최대 4개, 최신순)
List<Minutes> recentMinutes = getRecentMinutesByPeriod(userId, startTime, endTime).stream()
.limit(4)
.collect(Collectors.toList());
// 3. 기간별 통계 정보 계산
Dashboard.Statistics statistics = calculateStatisticsByPeriod(userId, startTime, endTime);
@ -96,57 +117,118 @@ public class DashboardGateway implements DashboardReader {
}
/**
* 다가오는 회의 목록 조회
* 다가오는 회의 목록 조회 (유저스토리 UFR-USER-020 기준)
* - 최대 3개
* - 회의록 미생성 우선
* - 빠른 일시 (회의 시작 시간 기준)
*/
private List<Meeting> getUpcomingMeetings(String userId) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime endTime = now.plusDays(30); // 향후 30일
// 과거 7일부터 향후 30일까지의 회의를 조회 (진행중/완료된 최근 회의 포함)
LocalDateTime startTime = now.minusDays(7);
LocalDateTime endTime = now.plusDays(30);
return getUpcomingMeetingsByPeriod(userId, now, endTime);
List<Meeting> meetings = getUpcomingMeetingsByPeriod(userId, startTime, endTime);
// 회의록 생성 여부 확인을 위한 생성
Set<String> meetingsWithMinutes = new HashSet<>();
minutesJpaRepository.findAll().stream()
.forEach(minutes -> meetingsWithMinutes.add(minutes.getMeetingId()));
// 정렬: 1) 회의록 미생성 우선, 2) 빠른 일시 (오름차순)
return meetings.stream()
.sorted((m1, m2) -> {
boolean m1HasMinutes = meetingsWithMinutes.contains(m1.getMeetingId());
boolean m2HasMinutes = meetingsWithMinutes.contains(m2.getMeetingId());
// 회의록 미생성이 우선
if (!m1HasMinutes && m2HasMinutes) return -1;
if (m1HasMinutes && !m2HasMinutes) return 1;
// 같은 상태면 시간순 (빠른 일시 우선)
return m1.getScheduledAt().compareTo(m2.getScheduledAt());
})
.limit(3) // 최대 3개만
.collect(Collectors.toList());
}
/**
* 기간별 다가오는 회의 목록 조회
* SCHEDULED, IN_PROGRESS, COMPLETED 상태의 회의를 모두 포함
*/
private List<Meeting> getUpcomingMeetingsByPeriod(String userId, LocalDateTime startTime, LocalDateTime endTime) {
Set<String> userMeetingIds = new HashSet<>();
// 주최자로 참여하는 예정/진행중 회의 조회
// 주최자로 참여하는 모든 상태의 회의 조회 (CANCELLED 제외)
List<MeetingEntity> organizerMeetings = meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
.filter(m -> userId.equals(m.getOrganizerId()))
.filter(m -> "SCHEDULED".equals(m.getStatus()) || "IN_PROGRESS".equals(m.getStatus()))
.filter(m -> !"CANCELLED".equals(m.getStatus())) // CANCELLED만 제외
.toList();
organizerMeetings.forEach(m -> userMeetingIds.add(m.getMeetingId()));
// 참석자로 참여하는 예정/진행중 회의 조회
// 참석자로 참여하는 모든 상태의 회의 조회 (CANCELLED 제외)
List<String> participantMeetingIds = meetingParticipantJpaRepository.findByUserId(userId).stream()
.map(p -> p.getMeetingId())
.toList();
List<MeetingEntity> participantMeetings = meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
.filter(m -> participantMeetingIds.contains(m.getMeetingId()))
.filter(m -> "SCHEDULED".equals(m.getStatus()) || "IN_PROGRESS".equals(m.getStatus()))
.filter(m -> !"CANCELLED".equals(m.getStatus())) // CANCELLED만 제외
.toList();
participantMeetings.forEach(m -> userMeetingIds.add(m.getMeetingId()));
// 중복 제거된 회의 목록을 시간순 정렬하여 최대 10개만 반환
return meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
// 중복 제거된 회의 목록을 시간순 정렬하여 반환
// 과거 회의는 최신순(내림차순), 미래 회의는 오래된순(오름차순)으로 정렬
LocalDateTime now = LocalDateTime.now();
List<Meeting> meetings = meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
.filter(m -> userMeetingIds.contains(m.getMeetingId()))
.filter(m -> "SCHEDULED".equals(m.getStatus()) || "IN_PROGRESS".equals(m.getStatus()))
.sorted((m1, m2) -> m1.getScheduledAt().compareTo(m2.getScheduledAt()))
.limit(10)
.filter(m -> !"CANCELLED".equals(m.getStatus())) // CANCELLED만 제외
.sorted((m1, m2) -> {
boolean m1IsPast = m1.getScheduledAt().isBefore(now);
boolean m2IsPast = m2.getScheduledAt().isBefore(now);
if (m1IsPast && m2IsPast) {
// 과거 회의면 최신순(내림차순)
return m2.getScheduledAt().compareTo(m1.getScheduledAt());
} else if (!m1IsPast && !m2IsPast) {
// 미래 회의면 오래된순(오름차순)
return m1.getScheduledAt().compareTo(m2.getScheduledAt());
} else {
// 하나는 과거, 하나는 미래면 미래 회의를 먼저
return m1IsPast ? 1 : -1;
}
})
// limit 제거 - 상위 메서드에서 필터링
.map(MeetingEntity::toDomain)
.collect(Collectors.toList());
log.debug("조회된 회의 목록 - userId: {}, 총 {}개 (SCHEDULED: {}, IN_PROGRESS: {}, COMPLETED: {})",
userId,
meetings.size(),
meetings.stream().filter(m -> "SCHEDULED".equals(m.getStatus())).count(),
meetings.stream().filter(m -> "IN_PROGRESS".equals(m.getStatus())).count(),
meetings.stream().filter(m -> "COMPLETED".equals(m.getStatus())).count()
);
return meetings;
}
/**
* 최근 회의록 목록 조회
* 최근 회의록 목록 조회 (유저스토리 UFR-USER-020 기준)
* - 최대 4개
* - 최신순 (최근 수정/생성 시간 기준)
*/
private List<Minutes> getRecentMinutes(String userId) {
LocalDateTime startTime = LocalDateTime.now().minusDays(7);
return getRecentMinutesByPeriod(userId, startTime, LocalDateTime.now());
LocalDateTime startTime = LocalDateTime.now().minusDays(30); // 넓은 범위에서 조회
List<Minutes> minutes = getRecentMinutesByPeriod(userId, startTime, LocalDateTime.now());
// 이미 getRecentMinutesByPeriod에서 최신순으로 정렬되어 있으므로
// 최대 4개만 반환
return minutes.stream()
.limit(4)
.collect(Collectors.toList());
}
/**
@ -174,7 +256,7 @@ public class DashboardGateway implements DashboardReader {
participatedMinutes.forEach(m -> userMinutesIds.add(m.getMinutesId()));
// 중복 제거 최종 수정 시간순 정렬하여 최대 10개만 반환
// 중복 제거 최종 수정 시간순 정렬
return minutesJpaRepository.findAll().stream()
.filter(m -> userMinutesIds.contains(m.getMinutesId()))
.sorted((m1, m2) -> {
@ -182,7 +264,7 @@ public class DashboardGateway implements DashboardReader {
LocalDateTime time2 = m2.getUpdatedAt() != null ? m2.getUpdatedAt() : m2.getCreatedAt();
return time2.compareTo(time1); // 최신순
})
.limit(10)
// limit 제거 - 상위 메서드에서 필터링
.map(MinutesEntity::toDomain)
.collect(Collectors.toList());
}