mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-01-21 13:46:26 +00:00
Chore: 회의록 목록 조회 API 실제 데이터 연동
This commit is contained in:
parent
e5337385f4
commit
45dc77cddf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -116,6 +116,11 @@ public class MinutesDTO {
|
|||||||
*/
|
*/
|
||||||
private final Integer completedTodoCount;
|
private final Integer completedTodoCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 참석자 수
|
||||||
|
*/
|
||||||
|
private final Integer participantCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회의 정보
|
* 회의 정보
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -44,6 +44,7 @@ public class MinutesService implements
|
|||||||
private final MinutesSectionReader minutesSectionReader;
|
private final MinutesSectionReader minutesSectionReader;
|
||||||
private final MinutesSectionWriter minutesSectionWriter;
|
private final MinutesSectionWriter minutesSectionWriter;
|
||||||
private final CacheService cacheService;
|
private final CacheService cacheService;
|
||||||
|
private final com.unicorn.hgzero.meeting.biz.usecase.out.ParticipantReader participantReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회의록 생성
|
* 회의록 생성
|
||||||
@ -317,6 +318,29 @@ public class MinutesService implements
|
|||||||
* Minutes 도메인을 MinutesDTO로 변환
|
* Minutes 도메인을 MinutesDTO로 변환
|
||||||
*/
|
*/
|
||||||
private MinutesDTO convertToMinutesDTO(Minutes minutes) {
|
private MinutesDTO convertToMinutesDTO(Minutes minutes) {
|
||||||
|
// 회의 정보 조회
|
||||||
|
String meetingTitle = "회의 제목 없음";
|
||||||
|
try {
|
||||||
|
Meeting meeting = meetingReader.findById(minutes.getMeetingId()).orElse(null);
|
||||||
|
if (meeting != null) {
|
||||||
|
meetingTitle = meeting.getTitle();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("회의 정보 조회 실패 - meetingId: {}", minutes.getMeetingId(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 정보는 추후 구현 (현재는 기본값)
|
||||||
|
int todoCount = 0;
|
||||||
|
int completedTodoCount = 0;
|
||||||
|
|
||||||
|
// 참석자 수 계산 (모든 참석자)
|
||||||
|
int participantCount = 0;
|
||||||
|
try {
|
||||||
|
participantCount = participantReader.countParticipantsByMeetingId(minutes.getMeetingId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("참석자 수 계산 실패 - meetingId: {}", minutes.getMeetingId(), e);
|
||||||
|
}
|
||||||
|
|
||||||
return MinutesDTO.builder()
|
return MinutesDTO.builder()
|
||||||
.minutesId(minutes.getMinutesId())
|
.minutesId(minutes.getMinutesId())
|
||||||
.meetingId(minutes.getMeetingId())
|
.meetingId(minutes.getMeetingId())
|
||||||
@ -327,11 +351,11 @@ public class MinutesService implements
|
|||||||
.lastModifiedAt(minutes.getLastModifiedAt())
|
.lastModifiedAt(minutes.getLastModifiedAt())
|
||||||
.createdBy(minutes.getCreatedBy())
|
.createdBy(minutes.getCreatedBy())
|
||||||
.lastModifiedBy(minutes.getLastModifiedBy())
|
.lastModifiedBy(minutes.getLastModifiedBy())
|
||||||
// 추가 필드들은 임시로 기본값 설정
|
.meetingTitle(meetingTitle)
|
||||||
.meetingTitle("임시 회의 제목")
|
.todoCount(todoCount)
|
||||||
.todoCount(0)
|
.completedTodoCount(completedTodoCount)
|
||||||
.completedTodoCount(0)
|
.participantCount(participantCount)
|
||||||
.memo("")
|
.memo("") // 메모 필드는 추후 구현
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,4 +21,9 @@ public interface ParticipantReader {
|
|||||||
* 특정 회의에 특정 사용자가 참석자로 등록되어 있는지 확인
|
* 특정 회의에 특정 사용자가 참석자로 등록되어 있는지 확인
|
||||||
*/
|
*/
|
||||||
boolean existsParticipant(String meetingId, String userId);
|
boolean existsParticipant(String meetingId, String userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회의 ID로 참석자 수 조회 (모든 참석자)
|
||||||
|
*/
|
||||||
|
int countParticipantsByMeetingId(String meetingId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.unicorn.hgzero.meeting.infra.controller;
|
|||||||
|
|
||||||
import com.unicorn.hgzero.common.dto.ApiResponse;
|
import com.unicorn.hgzero.common.dto.ApiResponse;
|
||||||
import com.unicorn.hgzero.common.exception.BusinessException;
|
import com.unicorn.hgzero.common.exception.BusinessException;
|
||||||
|
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||||
import com.unicorn.hgzero.meeting.biz.dto.MinutesDTO;
|
import com.unicorn.hgzero.meeting.biz.dto.MinutesDTO;
|
||||||
import com.unicorn.hgzero.meeting.biz.service.MinutesService;
|
import com.unicorn.hgzero.meeting.biz.service.MinutesService;
|
||||||
import com.unicorn.hgzero.meeting.biz.service.MinutesSectionService;
|
import com.unicorn.hgzero.meeting.biz.service.MinutesSectionService;
|
||||||
@ -16,6 +17,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
@ -69,40 +71,37 @@ public class MinutesController {
|
|||||||
userId, page, size, status, participationType, search);
|
userId, page, size, status, participationType, search);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Mock 데이터 생성 (프론트엔드 테스트용)
|
// 정렬 및 페이징 설정
|
||||||
List<MinutesListResponse.MinutesItem> mockMinutes = createMockMinutesList(userId);
|
Sort sort = createSort(sortBy, sortDir);
|
||||||
|
Pageable pageable = PageRequest.of(page, size, sort);
|
||||||
|
|
||||||
|
// 실제 데이터 조회
|
||||||
|
Page<MinutesDTO> minutesPage = minutesService.getMinutesListByUserId(userId, pageable);
|
||||||
|
|
||||||
// 필터링 적용
|
// DTO를 Response 형식으로 변환
|
||||||
List<MinutesListResponse.MinutesItem> filteredMinutes = mockMinutes.stream()
|
List<MinutesListResponse.MinutesItem> minutesList = minutesPage.getContent().stream()
|
||||||
.filter(item -> filterByStatus(item, status))
|
.map(this::convertToMinutesItem)
|
||||||
.filter(item -> filterByParticipationType(item, participationType, userId))
|
|
||||||
.filter(item -> filterBySearch(item, search))
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// 정렬 적용
|
// 필터링 적용 (상태별)
|
||||||
applySorting(filteredMinutes, sortBy, sortDir);
|
List<MinutesListResponse.MinutesItem> filteredMinutes = minutesList.stream()
|
||||||
|
.filter(item -> filterByStatus(item, status))
|
||||||
// 페이징 적용
|
.filter(item -> filterBySearch(item, search))
|
||||||
int startIndex = page * size;
|
.collect(Collectors.toList());
|
||||||
int endIndex = Math.min(startIndex + size, filteredMinutes.size());
|
|
||||||
List<MinutesListResponse.MinutesItem> pagedMinutes =
|
|
||||||
startIndex < filteredMinutes.size() ?
|
|
||||||
filteredMinutes.subList(startIndex, endIndex) :
|
|
||||||
List.of();
|
|
||||||
|
|
||||||
// 통계 계산
|
// 통계 계산 (전체 데이터 기준)
|
||||||
MinutesListResponse.Statistics stats = calculateStatistics(mockMinutes, participationType, userId);
|
MinutesListResponse.Statistics stats = calculateRealStatistics(userId, participationType);
|
||||||
|
|
||||||
MinutesListResponse response = MinutesListResponse.builder()
|
MinutesListResponse response = MinutesListResponse.builder()
|
||||||
.minutesList(pagedMinutes)
|
.minutesList(filteredMinutes)
|
||||||
.totalCount(filteredMinutes.size())
|
.totalCount((int) minutesPage.getTotalElements())
|
||||||
.currentPage(page)
|
.currentPage(page)
|
||||||
.totalPages((int) Math.ceil((double) filteredMinutes.size() / size))
|
.totalPages(minutesPage.getTotalPages())
|
||||||
.statistics(stats)
|
.statistics(stats)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
log.info("회의록 목록 조회 성공 - userId: {}, total: {}, filtered: {}, paged: {}",
|
log.info("회의록 목록 조회 성공 - userId: {}, total: {}, filtered: {}",
|
||||||
userId, mockMinutes.size(), filteredMinutes.size(), pagedMinutes.size());
|
userId, minutesPage.getTotalElements(), filteredMinutes.size());
|
||||||
return ResponseEntity.ok(ApiResponse.success(response));
|
return ResponseEntity.ok(ApiResponse.success(response));
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -126,13 +125,14 @@ public class MinutesController {
|
|||||||
log.info("회의록 상세 조회 요청 - userId: {}, minutesId: {}", userId, minutesId);
|
log.info("회의록 상세 조회 요청 - userId: {}, minutesId: {}", userId, minutesId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Mock 데이터 생성 (프론트엔드 테스트용)
|
// 실제 데이터 조회
|
||||||
MinutesDetailResponse response = createMockMinutesDetail(minutesId, userId);
|
MinutesDTO minutesDTO = minutesService.getMinutesById(minutesId);
|
||||||
|
MinutesDetailResponse response = convertToMinutesDetailResponse(minutesDTO);
|
||||||
|
|
||||||
// 캐시 저장
|
// 캐시 저장
|
||||||
cacheService.cacheMinutesDetail(minutesId, response);
|
cacheService.cacheMinutesDetail(minutesId, response);
|
||||||
|
|
||||||
log.info("회의록 상세 조회 성공 (Mock) - minutesId: {}", minutesId);
|
log.info("회의록 상세 조회 성공 - minutesId: {}", minutesId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(response));
|
return ResponseEntity.ok(ApiResponse.success(response));
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -326,7 +326,60 @@ public class MinutesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 정렬 옵션 생성
|
||||||
|
*/
|
||||||
|
private Sort createSort(String sortBy, String sortDir) {
|
||||||
|
Sort.Direction direction = "asc".equalsIgnoreCase(sortDir) ? Sort.Direction.ASC : Sort.Direction.DESC;
|
||||||
|
|
||||||
|
switch (sortBy) {
|
||||||
|
case "title":
|
||||||
|
return Sort.by(direction, "title");
|
||||||
|
case "meeting":
|
||||||
|
return Sort.by(direction, "createdAt"); // 회의 일시로 정렬 (임시로 생성일시 사용)
|
||||||
|
case "modified":
|
||||||
|
default:
|
||||||
|
return Sort.by(direction, "lastModifiedAt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 실제 통계 계산
|
||||||
|
*/
|
||||||
|
private MinutesListResponse.Statistics calculateRealStatistics(String userId, String participationType) {
|
||||||
|
try {
|
||||||
|
// 전체 회의록 조회 (작성자 기준)
|
||||||
|
List<Minutes> allMinutes = minutesService.getMinutesByCreator(userId);
|
||||||
|
|
||||||
|
long totalCount = allMinutes.size();
|
||||||
|
long draftCount = allMinutes.stream()
|
||||||
|
.filter(m -> "DRAFT".equals(m.getStatus()))
|
||||||
|
.count();
|
||||||
|
long completeCount = allMinutes.stream()
|
||||||
|
.filter(m -> "FINALIZED".equals(m.getStatus()))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
return MinutesListResponse.Statistics.builder()
|
||||||
|
.totalCount(totalCount)
|
||||||
|
.draftCount(draftCount)
|
||||||
|
.completeCount(completeCount)
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("통계 계산 실패, 기본값 반환 - userId: {}", userId, e);
|
||||||
|
return MinutesListResponse.Statistics.builder()
|
||||||
|
.totalCount(0L)
|
||||||
|
.draftCount(0L)
|
||||||
|
.completeCount(0L)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MinutesListResponse.MinutesItem convertToMinutesItem(MinutesDTO minutesDTO) {
|
private MinutesListResponse.MinutesItem convertToMinutesItem(MinutesDTO minutesDTO) {
|
||||||
|
// 완료율 계산
|
||||||
|
int completionRate = minutesDTO.getTodoCount() > 0 ?
|
||||||
|
(minutesDTO.getCompletedTodoCount() * 100) / minutesDTO.getTodoCount() : 100;
|
||||||
|
|
||||||
return MinutesListResponse.MinutesItem.builder()
|
return MinutesListResponse.MinutesItem.builder()
|
||||||
.minutesId(minutesDTO.getMinutesId())
|
.minutesId(minutesDTO.getMinutesId())
|
||||||
.title(minutesDTO.getTitle())
|
.title(minutesDTO.getTitle())
|
||||||
@ -335,10 +388,14 @@ public class MinutesController {
|
|||||||
.version(minutesDTO.getVersion())
|
.version(minutesDTO.getVersion())
|
||||||
.createdAt(minutesDTO.getCreatedAt())
|
.createdAt(minutesDTO.getCreatedAt())
|
||||||
.lastModifiedAt(minutesDTO.getLastModifiedAt())
|
.lastModifiedAt(minutesDTO.getLastModifiedAt())
|
||||||
|
.meetingDate(minutesDTO.getCreatedAt()) // 임시로 생성일시 사용
|
||||||
.createdBy(minutesDTO.getCreatedBy())
|
.createdBy(minutesDTO.getCreatedBy())
|
||||||
.lastModifiedBy(minutesDTO.getLastModifiedBy())
|
.lastModifiedBy(minutesDTO.getLastModifiedBy())
|
||||||
|
.participantCount(minutesDTO.getParticipantCount() != null ? minutesDTO.getParticipantCount() : 0)
|
||||||
.todoCount(minutesDTO.getTodoCount())
|
.todoCount(minutesDTO.getTodoCount())
|
||||||
.completedTodoCount(minutesDTO.getCompletedTodoCount())
|
.completedTodoCount(minutesDTO.getCompletedTodoCount())
|
||||||
|
.completionRate(completionRate)
|
||||||
|
.isCreatedByUser(true) // 현재는 작성자 기준으로만 조회하므로 true
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,18 +554,10 @@ public class MinutesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 참여 유형별 필터링
|
* 참여 유형별 필터링 - 현재는 사용하지 않음 (작성자 기준으로만 조회)
|
||||||
*/
|
*/
|
||||||
private boolean filterByParticipationType(MinutesListResponse.MinutesItem item, String participationType, String userId) {
|
private boolean filterByParticipationType(MinutesListResponse.MinutesItem item, String participationType, String userId) {
|
||||||
if (participationType == null || participationType.isEmpty()) {
|
// 현재는 작성자 기준으로만 조회하므로 항상 true 반환
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ("created".equals(participationType)) {
|
|
||||||
return item.isCreatedByUser();
|
|
||||||
}
|
|
||||||
if ("attended".equals(participationType)) {
|
|
||||||
return !item.isCreatedByUser();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,8 +919,59 @@ public class MinutesController {
|
|||||||
|
|
||||||
|
|
||||||
private MinutesDetailResponse convertToMinutesDetailResponse(MinutesDTO minutesDTO) {
|
private MinutesDetailResponse convertToMinutesDetailResponse(MinutesDTO minutesDTO) {
|
||||||
// Mock 데이터로 대체 (프로토타입용)
|
// 기본 회의록 정보는 실제 데이터 사용
|
||||||
return createMockMinutesDetail(minutesDTO.getMinutesId(), "user123");
|
MinutesDetailResponse.MeetingInfo meetingInfo = MinutesDetailResponse.MeetingInfo.builder()
|
||||||
|
.meetingId(minutesDTO.getMeetingId())
|
||||||
|
.title(minutesDTO.getMeetingTitle())
|
||||||
|
.location("회의실 정보 없음") // 추후 실제 데이터로 변경 필요
|
||||||
|
.participants(List.of()) // 추후 실제 참석자 정보로 변경 필요
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MinutesDetailResponse.Statistics stats = MinutesDetailResponse.Statistics.builder()
|
||||||
|
.participantCount(minutesDTO.getParticipantCount() != null ? minutesDTO.getParticipantCount() : 0)
|
||||||
|
.durationMinutes(90) // 기본값 - 추후 실제 데이터로 변경 필요
|
||||||
|
.agendaCount(0) // 기본값 - 추후 실제 데이터로 변경 필요
|
||||||
|
.todoCount(minutesDTO.getTodoCount() != null ? minutesDTO.getTodoCount() : 0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MinutesDetailResponse.DashboardInfo dashboardInfo = MinutesDetailResponse.DashboardInfo.builder()
|
||||||
|
.keyPoints(List.of()) // 추후 실제 데이터로 변경 필요
|
||||||
|
.keywords(List.of()) // 추후 실제 데이터로 변경 필요
|
||||||
|
.stats(stats)
|
||||||
|
.decisions(List.of()) // 추후 실제 데이터로 변경 필요
|
||||||
|
.todoProgress(MinutesDetailResponse.TodoProgress.builder()
|
||||||
|
.totalCount(minutesDTO.getTodoCount() != null ? minutesDTO.getTodoCount() : 0)
|
||||||
|
.completedCount(minutesDTO.getCompletedTodoCount() != null ? minutesDTO.getCompletedTodoCount() : 0)
|
||||||
|
.progressPercentage(calculateProgressPercentage(minutesDTO.getTodoCount(), minutesDTO.getCompletedTodoCount()))
|
||||||
|
.todos(List.of()) // 추후 실제 데이터로 변경 필요
|
||||||
|
.build())
|
||||||
|
.relatedMinutes(List.of()) // 추후 실제 데이터로 변경 필요
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return MinutesDetailResponse.builder()
|
||||||
|
.minutesId(minutesDTO.getMinutesId())
|
||||||
|
.title(minutesDTO.getTitle())
|
||||||
|
.memo(minutesDTO.getMemo() != null ? minutesDTO.getMemo() : "")
|
||||||
|
.status(minutesDTO.getStatus())
|
||||||
|
.version(minutesDTO.getVersion())
|
||||||
|
.createdAt(minutesDTO.getCreatedAt())
|
||||||
|
.lastModifiedAt(minutesDTO.getLastModifiedAt())
|
||||||
|
.createdBy(minutesDTO.getCreatedBy())
|
||||||
|
.lastModifiedBy(minutesDTO.getLastModifiedBy())
|
||||||
|
.meeting(meetingInfo)
|
||||||
|
.dashboard(dashboardInfo)
|
||||||
|
.agendas(List.of()) // 추후 실제 안건 데이터로 변경 필요
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateProgressPercentage(Integer totalCount, Integer completedCount) {
|
||||||
|
if (totalCount == null || totalCount == 0) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
if (completedCount == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (completedCount * 100) / totalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -45,6 +45,12 @@ public class ParticipantGateway implements ParticipantReader, ParticipantWriter
|
|||||||
return participantRepository.existsByMeetingIdAndUserId(meetingId, userId);
|
return participantRepository.existsByMeetingIdAndUserId(meetingId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public int countParticipantsByMeetingId(String meetingId) {
|
||||||
|
return participantRepository.countByMeetingId(meetingId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void saveParticipant(String meetingId, String userId) {
|
public void saveParticipant(String meetingId, String userId) {
|
||||||
|
|||||||
@ -42,4 +42,9 @@ public interface MeetingParticipantJpaRepository extends JpaRepository<MeetingPa
|
|||||||
* 회의 ID와 사용자 ID로 참석자 존재 여부 확인
|
* 회의 ID와 사용자 ID로 참석자 존재 여부 확인
|
||||||
*/
|
*/
|
||||||
boolean existsByMeetingIdAndUserId(String meetingId, String userId);
|
boolean existsByMeetingIdAndUserId(String meetingId, String userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회의 ID로 참석자 수 조회 (모든 참석자)
|
||||||
|
*/
|
||||||
|
int countByMeetingId(String meetingId);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user