diff --git a/backend/src/main/java/kr/momo/controller/annotation/ApiErrorResponse.java b/backend/src/main/java/kr/momo/controller/annotation/ApiErrorResponse.java index b124b5eff..778b387b1 100644 --- a/backend/src/main/java/kr/momo/controller/annotation/ApiErrorResponse.java +++ b/backend/src/main/java/kr/momo/controller/annotation/ApiErrorResponse.java @@ -75,4 +75,18 @@ @interface NotFound { @AliasFor(annotation = ApiResponse.class, attribute = "description") String value() default ""; } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @Operation + @ApiResponse( + responseCode = "500", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = CustomProblemDetail.class) + ) + ) + @interface InternalServerError { + @AliasFor(annotation = ApiResponse.class, attribute = "description") String value() default ""; + } } diff --git a/backend/src/main/java/kr/momo/controller/meeting/MeetingControllerDocs.java b/backend/src/main/java/kr/momo/controller/meeting/MeetingControllerDocs.java index 03b0bb9bd..6e31009d7 100644 --- a/backend/src/main/java/kr/momo/controller/meeting/MeetingControllerDocs.java +++ b/backend/src/main/java/kr/momo/controller/meeting/MeetingControllerDocs.java @@ -75,6 +75,9 @@ ResponseEntity> confirm( | NOT_FOUND_MEETING | 존재하지 않는 약속 정보 입니다. | | NOT_CONFIRMED | 아직 확정되지 않은 약속입니다. | """) + @ApiErrorResponse.InternalServerError(ERROR_CODE_TABLE_HEADER + """ + | HOST_NOT_FOUND | 약속의 주최자 정보를 찾을 수 없습니다. 관리자에게 문의하세요. | + """) MomoApiResponse findConfirmedMeeting( @PathVariable @Schema(description = "약속 UUID") String uuid ); diff --git a/backend/src/main/java/kr/momo/domain/attendee/AttendeeGroup.java b/backend/src/main/java/kr/momo/domain/attendee/AttendeeGroup.java index b8204c86d..076a5ef6a 100644 --- a/backend/src/main/java/kr/momo/domain/attendee/AttendeeGroup.java +++ b/backend/src/main/java/kr/momo/domain/attendee/AttendeeGroup.java @@ -7,6 +7,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.IntStream; import kr.momo.exception.MomoException; @@ -79,6 +80,18 @@ public boolean isSameSize(AttendeeGroup attendeeGroup) { return attendees.size() == attendeeGroup.size(); } + public Optional findHost() { + return attendees.stream() + .dropWhile(attendee -> !attendee.isHost()) + .findFirst(); + } + + public List names() { + return attendees.stream() + .map(Attendee::name) + .toList(); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/backend/src/main/java/kr/momo/domain/meeting/ConfirmedMeeting.java b/backend/src/main/java/kr/momo/domain/meeting/ConfirmedMeeting.java index 0daed2135..95baf2790 100644 --- a/backend/src/main/java/kr/momo/domain/meeting/ConfirmedMeeting.java +++ b/backend/src/main/java/kr/momo/domain/meeting/ConfirmedMeeting.java @@ -18,6 +18,7 @@ import java.util.Map; import kr.momo.domain.BaseEntity; import kr.momo.domain.attendee.Attendee; +import kr.momo.domain.attendee.AttendeeGroup; import kr.momo.domain.schedule.Schedule; import lombok.AccessLevel; import lombok.Getter; @@ -51,15 +52,16 @@ public ConfirmedMeeting(Meeting meeting, LocalDateTime startDateTime, LocalDateT this.endDateTime = endDateTime; } - public List availableAttendeesOf(List schedules) { + public AttendeeGroup availableAttendeesOf(List schedules) { Map groupAttendeeByScheduleCount = schedules.stream() .filter(this::isScheduleWithinDateTimeRange) .collect(groupingBy(Schedule::getAttendee, counting())); long confirmedTimeSlotCount = countTimeSlotOfConfirmedMeeting(); - return groupAttendeeByScheduleCount.keySet().stream() + List availableAttendees = groupAttendeeByScheduleCount.keySet().stream() .filter(key -> groupAttendeeByScheduleCount.get(key) == confirmedTimeSlotCount) .toList(); + return new AttendeeGroup(availableAttendees); } private boolean isScheduleWithinDateTimeRange(Schedule schedule) { diff --git a/backend/src/main/java/kr/momo/exception/code/AttendeeErrorCode.java b/backend/src/main/java/kr/momo/exception/code/AttendeeErrorCode.java index b17c4c8dd..afe20246c 100644 --- a/backend/src/main/java/kr/momo/exception/code/AttendeeErrorCode.java +++ b/backend/src/main/java/kr/momo/exception/code/AttendeeErrorCode.java @@ -12,7 +12,8 @@ public enum AttendeeErrorCode implements ErrorCodeType { NOT_FOUND_ATTENDEE(HttpStatus.NOT_FOUND, "해당 약속에 참여하는 참가자 정보가 없습니다."), ACCESS_DENIED(HttpStatus.FORBIDDEN, "접근이 거부되었습니다."), DUPLICATED_ATTENDEE_NAME(HttpStatus.BAD_REQUEST, "참여자의 이름이 중복됩니다."), - INVALID_ATTENDEE_SIZE(HttpStatus.BAD_REQUEST, "참여자의 수는 최소 1명입니다."); + INVALID_ATTENDEE_SIZE(HttpStatus.BAD_REQUEST, "참여자의 수는 최소 1명입니다."), + HOST_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "약속의 주최자 정보를 찾을 수 없습니다. 관리자에게 문의하세요."),; private final HttpStatus httpStatus; private final String message; diff --git a/backend/src/main/java/kr/momo/service/meeting/MeetingConfirmService.java b/backend/src/main/java/kr/momo/service/meeting/MeetingConfirmService.java index 4164f2eab..27c20c529 100644 --- a/backend/src/main/java/kr/momo/service/meeting/MeetingConfirmService.java +++ b/backend/src/main/java/kr/momo/service/meeting/MeetingConfirmService.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.util.List; import kr.momo.domain.attendee.Attendee; +import kr.momo.domain.attendee.AttendeeGroup; import kr.momo.domain.attendee.AttendeeRepository; import kr.momo.domain.availabledate.AvailableDateRepository; import kr.momo.domain.availabledate.AvailableDates; @@ -107,12 +108,13 @@ public ConfirmedMeetingResponse findByUuid(String uuid) { ConfirmedMeeting confirmedMeeting = confirmedMeetingRepository.findByMeeting(meeting) .orElseThrow(() -> new MomoException(MeetingErrorCode.NOT_CONFIRMED)); - List attendees = attendeeRepository.findAllByMeeting(meeting); - List schedules = scheduleRepository.findAllByAttendeeIn(attendees); + AttendeeGroup attendees = new AttendeeGroup(attendeeRepository.findAllByMeeting(meeting)); + Attendee host = attendees.findHost() + .orElseThrow(() -> new MomoException(AttendeeErrorCode.HOST_NOT_FOUND)); + List schedules = scheduleRepository.findAllByAttendeeIn(attendees.getAttendees()); + AttendeeGroup availableAttendees = confirmedMeeting.availableAttendeesOf(schedules); - attendees = confirmedMeeting.availableAttendeesOf(schedules); - - return ConfirmedMeetingResponse.from(meeting, attendees, confirmedMeeting); + return ConfirmedMeetingResponse.from(meeting, host, availableAttendees, confirmedMeeting); } @Transactional diff --git a/backend/src/main/java/kr/momo/service/meeting/dto/ConfirmedMeetingResponse.java b/backend/src/main/java/kr/momo/service/meeting/dto/ConfirmedMeetingResponse.java index c0c67450d..3f641eaa8 100644 --- a/backend/src/main/java/kr/momo/service/meeting/dto/ConfirmedMeetingResponse.java +++ b/backend/src/main/java/kr/momo/service/meeting/dto/ConfirmedMeetingResponse.java @@ -8,11 +8,13 @@ import java.util.List; import java.util.Locale; import kr.momo.domain.attendee.Attendee; +import kr.momo.domain.attendee.AttendeeGroup; import kr.momo.domain.meeting.ConfirmedMeeting; import kr.momo.domain.meeting.Meeting; public record ConfirmedMeetingResponse( String meetingName, + String hostName, List availableAttendeeNames, @JsonFormat(pattern = "yyyy-MM-dd", shape = Shape.STRING) LocalDate startDate, @@ -27,11 +29,12 @@ public record ConfirmedMeetingResponse( ) { public static ConfirmedMeetingResponse from( - Meeting meeting, List attendees, ConfirmedMeeting confirmedMeeting + Meeting meeting, Attendee host, AttendeeGroup attendees, ConfirmedMeeting confirmedMeeting ) { return new ConfirmedMeetingResponse( meeting.getName(), - attendees.stream().map(Attendee::name).toList(), + host.name(), + attendees.names(), confirmedMeeting.getStartDateTime().toLocalDate(), confirmedMeeting.getStartDateTime().toLocalTime(), confirmedMeeting.getStartDateTime().getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN), diff --git a/backend/src/test/java/kr/momo/controller/meeting/MeetingControllerTest.java b/backend/src/test/java/kr/momo/controller/meeting/MeetingControllerTest.java index 547c915f5..a471f8de2 100644 --- a/backend/src/test/java/kr/momo/controller/meeting/MeetingControllerTest.java +++ b/backend/src/test/java/kr/momo/controller/meeting/MeetingControllerTest.java @@ -409,7 +409,8 @@ void confirmScheduleUnlock() { @Test void confirmInvalidRequest() { Meeting meeting = createLockedMovieMeeting(); - AvailableDate availableDate = availableDateRepository.save(new AvailableDate(LocalDate.now().plusDays(1), meeting)); + AvailableDate availableDate = availableDateRepository.save( + new AvailableDate(LocalDate.now().plusDays(1), meeting)); String tomorrow = availableDate.getDate().format(DateTimeFormatter.ISO_DATE); Attendee guest = attendeeRepository.save(AttendeeFixture.GUEST_MARK.create(meeting)); String token = getToken(guest, meeting); @@ -490,6 +491,7 @@ void findConfirmedMeeting() { Meeting meeting = MeetingFixture.MOVIE.create(); meeting.lock(); meeting = meetingRepository.save(meeting); + attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting)); confirmedMeetingRepository.save(ConfirmedMeetingFixture.MOVIE.create(meeting)); RestAssured.given().log().all() diff --git a/backend/src/test/java/kr/momo/domain/meeting/ConfirmedMeetingTest.java b/backend/src/test/java/kr/momo/domain/meeting/ConfirmedMeetingTest.java index fefa16d5b..ec267eaa7 100644 --- a/backend/src/test/java/kr/momo/domain/meeting/ConfirmedMeetingTest.java +++ b/backend/src/test/java/kr/momo/domain/meeting/ConfirmedMeetingTest.java @@ -8,6 +8,7 @@ import java.time.LocalTime; import java.util.List; import kr.momo.domain.attendee.Attendee; +import kr.momo.domain.attendee.AttendeeGroup; import kr.momo.domain.availabledate.AvailableDate; import kr.momo.domain.schedule.Schedule; import kr.momo.domain.timeslot.Timeslot; @@ -37,7 +38,8 @@ void availableAttendeesOf() { new Schedule(attendee2, new AvailableDate(today, meeting), Timeslot.TIME_0000) ); - List attendees = confirmedMeeting.availableAttendeesOf(schedules); + AttendeeGroup availableAttendees = confirmedMeeting.availableAttendeesOf(schedules); + List attendees = availableAttendees.getAttendees(); assertAll( () -> assertThat(attendees).hasSize(1),