meeting 서비스 빌드 에러 1차 해결

This commit is contained in:
cyjadela
2025-10-23 18:46:44 +09:00
parent 71d6675d25
commit 000a54cd20
14 changed files with 923 additions and 66 deletions
@@ -1,5 +1,6 @@
package com.unicorn.hgzero.meeting.biz.dto;
import com.unicorn.hgzero.meeting.biz.domain.Dashboard;
import lombok.Builder;
import lombok.Getter;
@@ -87,4 +88,50 @@ public class DashboardDTO {
private final Integer activeTodosCount;
private final Double todoCompletionRate;
}
/**
* Dashboard 도메인 객체로부터 DashboardDTO 생성
*/
public static DashboardDTO from(Dashboard dashboard) {
return DashboardDTO.builder()
.upcomingMeetings(dashboard.getUpcomingMeetings().stream()
.map(meeting -> UpcomingMeetingDTO.builder()
.meetingId(meeting.getMeetingId())
.title(meeting.getTitle())
.startTime(meeting.getScheduledAt())
.endTime(null) // Meeting 도메인에 endTime이 없음
.location(null) // Meeting 도메인에 location이 없음
.participantCount(meeting.getParticipants() != null ? meeting.getParticipants().size() : 0)
.status(meeting.getStatus())
.build())
.toList())
.activeTodos(dashboard.getAssignedTodos().stream()
.filter(todo -> "PENDING".equals(todo.getStatus()) || "IN_PROGRESS".equals(todo.getStatus()))
.map(todo -> ActiveTodoDTO.builder()
.todoId(todo.getTodoId())
.content(todo.getDescription())
.dueDate(todo.getDueDate() != null ? todo.getDueDate().toString() : null)
.priority(todo.getPriority())
.status(todo.getStatus())
.minutesId(todo.getMinutesId())
.build())
.toList())
.myMinutes(dashboard.getRecentMinutes().stream()
.map(minutes -> RecentMinutesDTO.builder()
.minutesId(minutes.getMinutesId())
.title(minutes.getTitle())
.meetingDate(minutes.getCreatedAt()) // Minutes에 meetingDate가 없어서 createdAt 사용
.status(minutes.getStatus())
.participantCount(0) // Minutes 도메인에 participantCount가 없음
.lastModified(minutes.getLastModifiedAt())
.build())
.toList())
.statistics(StatisticsDTO.builder()
.upcomingMeetingsCount(dashboard.getStatistics().getScheduledMeetings())
.activeTodosCount(dashboard.getStatistics().getPendingTodos())
.todoCompletionRate(dashboard.getStatistics().getTotalTodos() > 0 ?
(double) dashboard.getStatistics().getCompletedTodos() / dashboard.getStatistics().getTotalTodos() * 100 : 0.0)
.build())
.build();
}
}
@@ -1,5 +1,6 @@
package com.unicorn.hgzero.meeting.biz.dto;
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
import lombok.Builder;
import lombok.Getter;
@@ -69,6 +70,32 @@ public class MeetingDTO {
*/
private final LocalDateTime updatedAt;
/**
* Meeting 도메인 객체로부터 MeetingDTO 생성
*/
public static MeetingDTO from(Meeting meeting) {
return MeetingDTO.builder()
.meetingId(meeting.getMeetingId())
.title(meeting.getTitle())
.startTime(meeting.getStartedAt() != null ? meeting.getStartedAt() : meeting.getScheduledAt())
.endTime(meeting.getEndedAt())
.location(null) // Meeting 도메인에 location 필드가 없어서 null로 설정
.agenda(meeting.getDescription())
.participants(meeting.getParticipants().stream()
.map(participantId -> ParticipantDTO.builder()
.userId(participantId)
.email(null) // 참석자 정보는 별도 조회 필요
.name(null)
.role("PARTICIPANT")
.build())
.toList())
.status(meeting.getStatus())
.organizer(meeting.getOrganizerId())
.createdAt(null) // Meeting 도메인에 createdAt 필드가 없음
.updatedAt(null) // Meeting 도메인에 updatedAt 필드가 없음
.build();
}
/**
* 참석자 정보
*/
@@ -1,5 +1,6 @@
package com.unicorn.hgzero.meeting.biz.dto;
import com.unicorn.hgzero.meeting.biz.domain.Todo;
import lombok.Builder;
import lombok.Getter;
@@ -113,4 +114,32 @@ public class TodoDTO {
* 회의 제목
*/
private final String meetingTitle;
/**
* Todo 도메인 객체로부터 TodoDTO 생성
*/
public static TodoDTO from(Todo todo) {
return TodoDTO.builder()
.todoId(todo.getTodoId())
.minutesId(todo.getMinutesId())
.content(todo.getDescription())
.assignee(todo.getAssigneeId())
.dueDate(todo.getDueDate())
.priority(todo.getPriority())
.status(todo.getStatus())
.progress(todo.getProgress())
.description(todo.getDescription())
.createdBy(todo.getCreatedBy())
.createdAt(todo.getCreatedAt())
.updatedAt(todo.getLastModifiedAt())
.completedAt(todo.getCompletedAt())
.title(todo.getTitle())
.assigneeId(todo.getAssigneeId())
.assigneeName(null) // 담당자 이름은 별도 조회 필요
.completedBy(todo.getCompletedBy())
.minutesTitle(null) // 회의록 제목은 별도 조회 필요
.meetingId(todo.getMeetingId())
.meetingTitle(null) // 회의 제목은 별도 조회 필요
.build();
}
}
@@ -1,6 +1,7 @@
package com.unicorn.hgzero.meeting.infra.controller;
import com.unicorn.hgzero.common.dto.ApiResponse;
import com.unicorn.hgzero.meeting.biz.dto.DashboardDTO;
import com.unicorn.hgzero.meeting.biz.usecase.in.dashboard.GetDashboardUseCase;
import com.unicorn.hgzero.meeting.infra.dto.response.DashboardResponse;
import io.swagger.v3.oas.annotations.Operation;
@@ -49,7 +50,8 @@ public class DashboardController {
log.info("대시보드 데이터 조회 요청 - userId: {}", userId);
var dashboardData = getDashboardUseCase.getDashboard(userId);
var response = DashboardResponse.from(dashboardData);
var dashboardDTO = DashboardDTO.from(dashboardData);
var response = DashboardResponse.from(dashboardDTO);
log.info("대시보드 데이터 조회 완료 - userId: {}", userId);
@@ -1,6 +1,7 @@
package com.unicorn.hgzero.meeting.infra.controller;
import com.unicorn.hgzero.common.dto.ApiResponse;
import com.unicorn.hgzero.meeting.biz.dto.MeetingDTO;
import com.unicorn.hgzero.meeting.biz.usecase.in.meeting.*;
import com.unicorn.hgzero.meeting.infra.dto.request.CreateMeetingRequest;
import com.unicorn.hgzero.meeting.infra.dto.request.SelectTemplateRequest;
@@ -60,16 +61,18 @@ public class MeetingController {
log.info("회의 예약 요청 - userId: {}, title: {}", userId, request.getTitle());
var meetingData = createMeetingUseCase.createMeeting(
request.getTitle(),
request.getStartTime(),
request.getEndTime(),
request.getLocation(),
request.getAgenda(),
request.getParticipants(),
userId
new CreateMeetingUseCase.CreateMeetingCommand(
request.getTitle(),
request.getAgenda(),
request.getStartTime(),
userId,
request.getParticipants(),
null // 템플릿 ID는 나중에 적용
)
);
var response = MeetingResponse.from(meetingData);
var meetingDTO = MeetingDTO.from(meetingData);
var response = MeetingResponse.from(meetingDTO);
log.info("회의 예약 완료 - userId: {}, meetingId: {}", userId, meetingData.getMeetingId());
@@ -105,7 +108,8 @@ public class MeetingController {
log.info("템플릿 적용 요청 - meetingId: {}, templateId: {}", meetingId, request.getTemplateId());
var meetingData = getMeetingUseCase.getMeeting(meetingId);
var response = MeetingResponse.from(meetingData);
var meetingDTO = MeetingDTO.from(meetingData);
var response = MeetingResponse.from(meetingDTO);
log.info("템플릿 적용 완료 - meetingId: {}", meetingId);
@@ -137,10 +141,10 @@ public class MeetingController {
log.info("회의 시작 요청 - meetingId: {}, userId: {}", meetingId, userId);
var sessionData = startMeetingUseCase.startMeeting(meetingId, userId);
var sessionData = startMeetingUseCase.startMeeting(meetingId);
var response = SessionResponse.from(sessionData);
log.info("회의 시작 완료 - meetingId: {}, sessionId: {}", meetingId, sessionData.getSessionId());
log.info("회의 시작 완료 - meetingId: {}", meetingId);
return ResponseEntity.ok(ApiResponse.success(response));
}
@@ -170,8 +174,9 @@ public class MeetingController {
log.info("회의 종료 요청 - meetingId: {}, userId: {}", meetingId, userId);
var meetingData = endMeetingUseCase.endMeeting(meetingId, userId);
var response = MeetingResponse.from(meetingData);
var meetingData = endMeetingUseCase.endMeeting(meetingId);
var meetingDTO = MeetingDTO.from(meetingData);
var response = MeetingResponse.from(meetingDTO);
log.info("회의 종료 완료 - meetingId: {}", meetingId);
@@ -199,7 +204,8 @@ public class MeetingController {
log.info("회의 정보 조회 요청 - meetingId: {}", meetingId);
var meetingData = getMeetingUseCase.getMeeting(meetingId);
var response = MeetingResponse.from(meetingData);
var meetingDTO = MeetingDTO.from(meetingData);
var response = MeetingResponse.from(meetingDTO);
log.info("회의 정보 조회 완료 - meetingId: {}", meetingId);
@@ -227,7 +233,7 @@ public class MeetingController {
log.info("회의 취소 요청 - meetingId: {}, userId: {}", meetingId, userId);
cancelMeetingUseCase.cancelMeeting(meetingId, userId);
cancelMeetingUseCase.cancelMeeting(meetingId);
log.info("회의 취소 완료 - meetingId: {}", meetingId);
@@ -100,7 +100,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("회의록 목록 조회 실패 - userId: {}", userId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("회의록 목록 조회에 실패했습니다"));
.body(ApiResponse.errorWithType("회의록 목록 조회에 실패했습니다"));
}
}
@@ -140,7 +140,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("회의록 상세 조회 실패 - minutesId: {}", minutesId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("회의록 상세 조회에 실패했습니다"));
.body(ApiResponse.errorWithType("회의록 상세 조회에 실패했습니다"));
}
}
@@ -178,7 +178,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("회의록 수정 실패 - minutesId: {}", minutesId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("회의록 수정에 실패했습니다"));
.body(ApiResponse.errorWithType("회의록 수정에 실패했습니다"));
}
}
@@ -215,7 +215,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("회의록 확정 실패 - minutesId: {}", minutesId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("회의록 확정에 실패했습니다"));
.body(ApiResponse.errorWithType("회의록 확정에 실패했습니다"));
}
}
@@ -247,7 +247,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("섹션 검증 완료 실패 - sectionId: {}", sectionId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("섹션 검증 완료에 실패했습니다"));
.body(ApiResponse.errorWithType("섹션 검증 완료에 실패했습니다"));
}
}
@@ -279,7 +279,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("섹션 잠금 실패 - sectionId: {}", sectionId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("섹션 잠금에 실패했습니다"));
.body(ApiResponse.errorWithType("섹션 잠금에 실패했습니다"));
}
}
@@ -311,7 +311,7 @@ public class MinutesController {
} catch (Exception e) {
log.error("섹션 잠금 해제 실패 - sectionId: {}", sectionId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("섹션 잠금 해제에 실패했습니다"));
.body(ApiResponse.errorWithType("섹션 잠금 해제에 실패했습니다"));
}
}
@@ -1,8 +1,11 @@
package com.unicorn.hgzero.meeting.infra.controller;
import com.unicorn.hgzero.common.dto.ApiResponse;
import com.unicorn.hgzero.meeting.biz.domain.Todo;
import com.unicorn.hgzero.meeting.biz.dto.TodoDTO;
import com.unicorn.hgzero.meeting.biz.service.TodoService;
import com.unicorn.hgzero.meeting.biz.usecase.in.todo.CreateTodoUseCase;
import com.unicorn.hgzero.meeting.biz.usecase.in.todo.UpdateTodoUseCase;
import com.unicorn.hgzero.meeting.infra.dto.request.CreateTodoRequest;
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateTodoRequest;
import com.unicorn.hgzero.meeting.infra.dto.response.TodoListResponse;
@@ -61,19 +64,21 @@ public class TodoController {
try {
// Todo 생성
TodoDTO createdTodo = todoService.createTodo(
request.getMinutesId(),
request.getTitle(),
request.getDescription(),
request.getAssigneeId(),
request.getAssigneeName(),
request.getDueDate(),
request.getPriority(),
userId
Todo createdTodo = todoService.createTodo(
new CreateTodoUseCase.CreateTodoCommand(
request.getMinutesId(),
null, // meetingId는 나중에 회의록에서 가져올 예정
request.getTitle(),
request.getDescription(),
request.getAssigneeId(),
request.getDueDate(),
request.getPriority()
)
);
// 응답 DTO 생성
TodoListResponse.TodoItem response = convertToTodoItem(createdTodo);
TodoDTO todoDTO = TodoDTO.from(createdTodo);
TodoListResponse.TodoItem response = convertToTodoItem(todoDTO);
// 캐시 무효화
cacheService.evictCacheTodoList(request.getAssigneeId());
@@ -98,7 +103,7 @@ public class TodoController {
log.error("Todo 생성 실패 - minutesId: {}, title: {}",
request.getMinutesId(), request.getTitle(), e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("Todo 생성에 실패했습니다"));
.body(ApiResponse.errorWithType("Todo 생성에 실패했습니다"));
}
}
@@ -118,19 +123,20 @@ public class TodoController {
try {
// Todo 수정
TodoDTO updatedTodo = todoService.updateTodo(
todoId,
request.getTitle(),
request.getDescription(),
request.getAssigneeId(),
request.getAssigneeName(),
request.getDueDate(),
request.getPriority(),
userId
Todo updatedTodo = todoService.updateTodo(
new UpdateTodoUseCase.UpdateTodoCommand(
todoId,
request.getTitle(),
request.getDescription(),
request.getAssigneeId(),
request.getDueDate(),
request.getPriority()
)
);
// 응답 DTO 생성
TodoListResponse.TodoItem response = convertToTodoItem(updatedTodo);
TodoDTO todoDTO = TodoDTO.from(updatedTodo);
TodoListResponse.TodoItem response = convertToTodoItem(todoDTO);
// 캐시 무효화
cacheService.evictCacheTodoDetail(todoId);
@@ -146,7 +152,7 @@ public class TodoController {
} catch (Exception e) {
log.error("Todo 수정 실패 - todoId: {}", todoId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("Todo 수정에 실패했습니다"));
.body(ApiResponse.errorWithType("Todo 수정에 실패했습니다"));
}
}
@@ -165,23 +171,24 @@ public class TodoController {
try {
// Todo 완료 처리
TodoDTO completedTodo = todoService.completeTodo(todoId, userId);
Todo completedTodo = todoService.completeTodo(todoId);
// 응답 DTO 생성
TodoListResponse.TodoItem response = convertToTodoItem(completedTodo);
TodoDTO todoDTO = TodoDTO.from(completedTodo);
TodoListResponse.TodoItem response = convertToTodoItem(todoDTO);
// 캐시 무효화
cacheService.evictCacheTodoDetail(todoId);
cacheService.evictCacheTodoList(completedTodo.getAssigneeId());
cacheService.evictCacheMinutesDetail(completedTodo.getMinutesId());
cacheService.evictCacheDashboard(completedTodo.getAssigneeId());
cacheService.evictCacheTodoList(todoDTO.getAssigneeId());
cacheService.evictCacheMinutesDetail(todoDTO.getMinutesId());
cacheService.evictCacheDashboard(todoDTO.getAssigneeId());
// Todo 완료 이벤트 발행
eventPublisher.publishTodoCompleted(
completedTodo.getTodoId(),
completedTodo.getTitle(),
completedTodo.getAssigneeId(),
completedTodo.getAssigneeName(),
todoDTO.getTodoId(),
todoDTO.getTitle(),
todoDTO.getAssigneeId(),
todoDTO.getAssigneeName(),
userId,
userName
);
@@ -192,7 +199,7 @@ public class TodoController {
} catch (Exception e) {
log.error("Todo 완료 실패 - todoId: {}", todoId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("Todo 완료 처리에 실패했습니다"));
.body(ApiResponse.errorWithType("Todo 완료 처리에 실패했습니다"));
}
}
@@ -256,7 +263,7 @@ public class TodoController {
} catch (Exception e) {
log.error("Todo 목록 조회 실패 - assigneeId: {}", targetAssigneeId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("Todo 목록 조회에 실패했습니다"));
.body(ApiResponse.errorWithType("Todo 목록 조회에 실패했습니다"));
}
}
@@ -270,7 +277,7 @@ public class TodoController {
.assigneeName(todoDTO.getAssigneeName())
.priority(todoDTO.getPriority())
.status(todoDTO.getStatus())
.dueDate(todoDTO.getDueDate())
.dueDate(todoDTO.getDueDate() != null ? todoDTO.getDueDate().atStartOfDay() : null)
.createdAt(todoDTO.getCreatedAt())
.completedAt(todoDTO.getCompletedAt())
.completedBy(todoDTO.getCompletedBy())
@@ -1,5 +1,6 @@
package com.unicorn.hgzero.meeting.infra.dto.response;
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;
@@ -33,16 +34,15 @@ public class SessionResponse {
private final LocalDateTime expiresAt;
/**
* 임시 SessionDTO를 위한 정적 팩토리 메서드
* 실제로는 MeetingDTO나 SessionDTO로부터 생성
* Meeting 객체로부터 SessionResponse 생성
*/
public static SessionResponse from(Object sessionData) {
public static SessionResponse from(Meeting meeting) {
return SessionResponse.builder()
.sessionId("session-" + System.currentTimeMillis())
.meetingId("meeting-id-from-session")
.sessionId("session-" + meeting.getMeetingId())
.meetingId(meeting.getMeetingId())
.websocketUrl("ws://localhost:8080/ws/collaboration")
.sessionToken("session-token-" + System.currentTimeMillis())
.startedAt(LocalDateTime.now())
.startedAt(meeting.getStartedAt() != null ? meeting.getStartedAt() : LocalDateTime.now())
.expiresAt(LocalDateTime.now().plusHours(4))
.build();
}
@@ -127,7 +127,7 @@ public class EventHubPublisher implements EventPublisher {
.relatedEntityType("TODO")
.requestedBy(completedBy)
.requestedByName(completedByName)
.requestedAt(LocalDateTime.now())
.eventTime(LocalDateTime.now())
.build();
String payload = objectMapper.writeValueAsString(event);
@@ -153,7 +153,7 @@ public class EventHubPublisher implements EventPublisher {
.relatedEntityType("MINUTES")
.requestedBy(finalizedBy)
.requestedByName(finalizedByName)
.requestedAt(LocalDateTime.now())
.eventTime(LocalDateTime.now())
.build();
String payload = objectMapper.writeValueAsString(event);
@@ -36,6 +36,7 @@ public class MinutesEntity extends BaseTimeEntity {
@OneToMany(mappedBy = "minutes", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("order ASC")
@Builder.Default
private List<MinutesSectionEntity> sections = new ArrayList<>();
@Column(name = "status", length = 20, nullable = false)