diff --git a/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java b/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java index 5c84b564..5031796c 100644 --- a/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java +++ b/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java @@ -292,7 +292,7 @@ public Reservation cancelApproveReservation(ReservationCriteria.CancelApproval c approvedReservation.getReservationId(), approvedReservation.getTrainer().getTrainerId(), criteria.isApprove(), token.getPushToken())); - // 해당 알림에 대한 읽음 처리 + // 해당 트레이너 알림에 대한 읽음 처리 Notification notificationByRef = notificationService.getNotificationByReferenceAndMember(criteria.reservationId(), Notification.ReferenceType.RESERVATION_CANCEL, criteria.memberId()); notificationService.markAsRead(notificationByRef.getNotificationId()); @@ -351,7 +351,7 @@ public Reservation changeApproveReservation(ReservationCriteria.ChangeApproval c approvedReservation.getReservationId(), approvedReservation.getTrainer().getTrainerId(), criteria.isApprove(), token.getPushToken())); - // 해당 알림에 대한 읽음 처리 + // 해당 트레이너 알림에 대한 읽음 처리 Notification notificationByRef = notificationService.getNotificationByReferenceAndMember(criteria.reservationId(), Notification.ReferenceType.RESERVATION_CHANGE, criteria.memberId()); notificationService.markAsRead(notificationByRef.getNotificationId()); @@ -385,9 +385,14 @@ public Session completeSession(ReservationCriteria.Complete criteria, SecurityUs sessionInfo.getSessionInfoId(), user.getTrainerId(), memberToken.getPushToken())); } - // 해당 알림에 대한 읽음 처리 - Notification notificationByRef = notificationService.getNotificationByReferenceAndMember(criteria.reservationId(), - Notification.ReferenceType.RESERVATION_REQUEST, criteria.memberId()); + // 해당 트레이너 알림에 대한 읽음 처리 + Notification notificationByRef = notificationService.getNotificationByReferenceAndType( + completedSession.getSessionId(), + Notification.ReferenceType.SESSION, + Notification.NotificationType.SESSION_FINISHED, + memberDetail.getMemberId() + ); + notificationService.markAsRead(notificationByRef.getNotificationId()); @@ -438,6 +443,7 @@ public void sendSessionCompleteReminder() { session.getSessionId(), r.getMember().getMemberId(), r.getName(), + r.getConfirmDate(), token.getPushToken())); }); } diff --git a/src/main/java/spring/fitlinkbe/domain/notification/Notification.java b/src/main/java/spring/fitlinkbe/domain/notification/Notification.java index 8cfd74e0..c5a6ecbc 100644 --- a/src/main/java/spring/fitlinkbe/domain/notification/Notification.java +++ b/src/main/java/spring/fitlinkbe/domain/notification/Notification.java @@ -182,9 +182,9 @@ public static Notification requestReservation(PersonalDetail trainerDetail, Long } public static Notification completeReminderSession(PersonalDetail trainerDetail, Long sessionId, - Long memberId, String name) { + Long memberId, String name, LocalDateTime reservationDate) { - String content = " %s 회원님의 PT가 곧 종료됩니다. 세션 참석 여부를 확인해주세요.".formatted(name); + String content = " %s 회원님의 PT가 곧 종료됩니다. 세션 참석 여부를 확인해주세요.\n 날짜: %s".formatted(name, formatDateTime(reservationDate)); return Notification.builder() .refId(sessionId) @@ -199,7 +199,7 @@ public static Notification completeReminderSession(PersonalDetail trainerDetail, .build(); } - public static Notification completeSession(PersonalDetail memberDetail, Long sessionId, + public static Notification completeSession(PersonalDetail trainerDetail, Long sessionId, Long memberId, String name) { String content = " %s 회원님의 PT가 종료되었습니다.".formatted(name); @@ -209,7 +209,7 @@ public static Notification completeSession(PersonalDetail memberDetail, Long ses .refType(ReferenceType.SESSION) .target(UserRole.TRAINER) .notificationType(NotificationType.SESSION_COMPLETED) - .personalDetail(memberDetail) + .personalDetail(trainerDetail) .partnerId(memberId) .name(NotificationType.SESSION_COMPLETED.name) .content(content) diff --git a/src/main/java/spring/fitlinkbe/domain/notification/NotificationService.java b/src/main/java/spring/fitlinkbe/domain/notification/NotificationService.java index 91f6494d..3ac4cd60 100644 --- a/src/main/java/spring/fitlinkbe/domain/notification/NotificationService.java +++ b/src/main/java/spring/fitlinkbe/domain/notification/NotificationService.java @@ -47,10 +47,29 @@ public Notification getNotification(Long notificationId) { "알림을 찾을 수 없습니다. [notificationId: %d]".formatted(notificationId))); } - public Notification getNotificationByReferenceAndMember(Long refId, Notification.ReferenceType refType, Long memberId) { - List notifications = notificationRepository.getNotification(refId, refType); + public Notification getNotificationByReferenceAndType( + Long refId, + Notification.ReferenceType refType, + Notification.NotificationType notificationType, + Long memberId) { + List notifications = notificationRepository.getNotification(refId, UserRole.TRAINER, refType); - return notifications.stream().filter(n -> n.getPartnerId().equals(memberId)).findFirst() + return notifications.stream() + .filter(n -> n.getNotificationType() == notificationType) + .filter(n -> n.getPartnerId().equals(memberId)) + .findFirst() + .orElseThrow(() -> new CustomException(ErrorCode.NOTIFICATION_NOT_FOUND)); + } + + public Notification getNotificationByReferenceAndMember( + Long refId, + Notification.ReferenceType refType, + Long memberId) { + List notifications = notificationRepository.getNotification(refId, UserRole.TRAINER, refType); + + return notifications.stream() + .filter(n -> n.getPartnerId().equals(memberId)) + .findFirst() .orElseThrow(() -> new CustomException(ErrorCode.NOTIFICATION_NOT_FOUND)); } diff --git a/src/main/java/spring/fitlinkbe/domain/notification/NotificationStrategyHandler.java b/src/main/java/spring/fitlinkbe/domain/notification/NotificationStrategyHandler.java index a0b6e8bc..43253b4a 100644 --- a/src/main/java/spring/fitlinkbe/domain/notification/NotificationStrategyHandler.java +++ b/src/main/java/spring/fitlinkbe/domain/notification/NotificationStrategyHandler.java @@ -105,7 +105,8 @@ private Notification handleCompleteSession(NotificationRequest request) { private Notification handleCompleteReminderSession(NotificationRequest request) { NotificationCommand.SessionCompleteReminder dto = (NotificationCommand.SessionCompleteReminder) request; - return Notification.completeReminderSession(dto.trainerDetail(), dto.sessionId(), dto.memberId(), dto.name()); + return Notification.completeReminderSession(dto.trainerDetail(), dto.sessionId(), dto.memberId(), dto.name(), + dto.reservationDate()); } private Notification handleDeductSession(NotificationRequest request) { diff --git a/src/main/java/spring/fitlinkbe/domain/notification/command/NotificationCommand.java b/src/main/java/spring/fitlinkbe/domain/notification/command/NotificationCommand.java index 6e6442af..be66c311 100644 --- a/src/main/java/spring/fitlinkbe/domain/notification/command/NotificationCommand.java +++ b/src/main/java/spring/fitlinkbe/domain/notification/command/NotificationCommand.java @@ -451,7 +451,7 @@ public static SessionChargeReminder of(PersonalDetail memberDetail, Long session @Builder public record SessionCompleteReminder(PersonalDetail trainerDetail, Long sessionId, Long memberId, - String name, + String name, LocalDateTime reservationDate, String pushToken) implements NotificationRequest { @Override @@ -465,13 +465,14 @@ public String getPushToken() { } public static SessionCompleteReminder of(PersonalDetail trainerDetail, Long sessionId, Long memberId, - String name, + String name, LocalDateTime reservationDate, String pushToken) { return SessionCompleteReminder.builder() .trainerDetail(trainerDetail) .sessionId(sessionId) .memberId(memberId) .name(name) + .reservationDate(reservationDate) .pushToken(pushToken) .build(); } diff --git a/src/main/java/spring/fitlinkbe/interfaces/controller/notification/dto/NotificationResponseDto.java b/src/main/java/spring/fitlinkbe/interfaces/controller/notification/dto/NotificationResponseDto.java index d6696947..2e49b2b9 100644 --- a/src/main/java/spring/fitlinkbe/interfaces/controller/notification/dto/NotificationResponseDto.java +++ b/src/main/java/spring/fitlinkbe/interfaces/controller/notification/dto/NotificationResponseDto.java @@ -13,6 +13,7 @@ public class NotificationResponseDto { public record Summary( Long notificationId, String type, + String notificationType, String content, LocalDateTime sendDate, boolean isProcessed @@ -24,6 +25,7 @@ public static NotificationResponseDto.Summary of(Notification notification) { return Summary.builder() .notificationId(notification.getNotificationId()) .type(notification.getRefType().getName()) + .notificationType(notification.getNotificationType().getName()) .content(notification.getContent()) .sendDate(notification.getSendDate()) .isProcessed(notification.isProcessed()) @@ -36,6 +38,7 @@ public record Detail( Long notificationId, Long refId, String type, + String notificationType, String content, LocalDateTime sendDate, boolean isProcessed, @@ -54,6 +57,7 @@ public static NotificationResponseDto.Detail of(NotificationResult.NotificationD .notificationId(notificationDetail.notification().getNotificationId()) .refId(notificationDetail.notification().getRefId()) .type(notificationDetail.notification().getRefType().getName()) + .notificationType(notificationDetail.notification().getNotificationType().getName()) .content(notificationDetail.notification().getContent()) .sendDate(notificationDetail.notification().getSendDate()) .isProcessed(notificationDetail.notification().isProcessed()) diff --git a/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java b/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java index 89ed2d4b..27636a9e 100644 --- a/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java +++ b/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java @@ -2614,12 +2614,17 @@ void completeSessionWithJoinSession() { .status(SESSION_WAITING) .build(); - reservationRepository.saveSession(session); + Session savedSession = reservationRepository.saveSession(session).orElseThrow(); // 알림 생성 PersonalDetail memberDetail = personalDetailRepository.getMemberDetail(1L).orElseThrow(); - Notification notification = Notification.approveReservation(memberDetail, savedReservation.getReservationId(), - reservation.getReservationDate(), 1L, true); + Notification notification = Notification.completeReminderSession( + personalDetail, + savedSession.getSessionId(), + memberDetail.getMemberId(), + memberDetail.getName(), + LocalDateTime.now().plusSeconds(2) + ); notificationRepository.save(notification); // when @@ -2638,7 +2643,7 @@ void completeSessionWithJoinSession() { softly.assertThat(content.sessionId()).isEqualTo(1L); softly.assertThat(content.status()).isEqualTo(Notification.NotificationType.SESSION_COMPLETED.getName()); softly.assertThat(notification).isNotNull(); - softly.assertThat(notification.getNotificationType()).isEqualTo(RESERVATION_APPROVE); + softly.assertThat(notification.getNotificationType()).isEqualTo(SESSION_FINISHED); }); } @@ -2675,12 +2680,18 @@ void completeSessionWithNotJoinSession() { .status(SESSION_WAITING) .build(); - reservationRepository.saveSession(session); + Session savedSession = reservationRepository.saveSession(session).orElseThrow(); // 알림 생성 PersonalDetail memberDetail = personalDetailRepository.getMemberDetail(1L).orElseThrow(); - Notification notification = Notification.approveReservation(memberDetail, savedReservation.getReservationId(), - reservation.getReservationDate(), 1L, true); + Notification notification = Notification.completeReminderSession( + personalDetail, + savedSession.getSessionId(), + memberDetail.getMemberId(), + memberDetail.getName(), + LocalDateTime.now().plusSeconds(2) + ); + notificationRepository.save(notification); // when @@ -3371,6 +3382,19 @@ void changeReqeustReservationWithApprove() { reservationRepository.saveSession(session); + + // 예약 변경 요청 알림 생성 + Member member = memberRepository.getMember(1L).orElseThrow(); + Notification notification = Notification.changeRequestReservation( + personalDetail, + savedReservation.getReservationId(), + member.getMemberId(), + member.getName(), + LocalDateTime.now(), + reservationDate + ); + notificationRepository.save(notification); + // when ExtractableResponse result = post(LOCAL_HOST + port + PATH + "/%s/change-approve".formatted(1), request, @@ -3392,7 +3416,7 @@ void changeReqeustReservationWithApprove() { Notification.ReferenceType.RESERVATION_CHANGE); softly.assertThat(notifications.get(0)).isNotNull(); softly.assertThat(notifications.get(0).getNotificationType()).isEqualTo( - Notification.NotificationType.RESERVATION_CHANGE_REQUEST_APPROVED); + Notification.NotificationType.RESERVATION_CHANGE_REQUEST); }); } @@ -3434,6 +3458,18 @@ void changeReqeustReservationWithRefuse() { reservationRepository.saveSession(session); + // 예약 변경 요청 알림 생성 + Member member = memberRepository.getMember(1L).orElseThrow(); + Notification notification = Notification.changeRequestReservation( + personalDetail, + savedReservation.getReservationId(), + member.getMemberId(), + member.getName(), + LocalDateTime.now(), + reservationDate + ); + notificationRepository.save(notification); + // when ExtractableResponse result = post(LOCAL_HOST + port + PATH + "/%s/change-approve".formatted(1), request, @@ -3455,7 +3491,7 @@ void changeReqeustReservationWithRefuse() { Notification.ReferenceType.RESERVATION_CHANGE); softly.assertThat(notifications.get(0)).isNotNull(); softly.assertThat(notifications.get(0).getNotificationType()).isEqualTo( - Notification.NotificationType.RESERVATION_CHANGE_REQUEST_REFUSED); + Notification.NotificationType.RESERVATION_CHANGE_REQUEST); }); } @@ -3492,6 +3528,18 @@ void changeReqeustReservationWithRefuseOtherReservations() { Member member2 = testDataHandler.createMember("하하"); testDataHandler.createTokenInfo(member2); + // 예약 변경 요청 알림 생성 + Member member = memberRepository.getMember(1L).orElseThrow(); + Notification notification = Notification.changeRequestReservation( + personalDetail, + reservation1.getReservationId(), + member.getMemberId(), + member.getName(), + LocalDateTime.now(), + reservationDate + ); + notificationRepository.save(notification); + // 다른 예약 대기 상태 예약 생성 Reservation reservation2 = Reservation.builder() .trainer(Trainer.builder().trainerId(1L).build()) @@ -3531,7 +3579,7 @@ void changeReqeustReservationWithRefuseOtherReservations() { Notification.ReferenceType.RESERVATION_CHANGE); softly.assertThat(notifications.get(0)).isNotNull(); softly.assertThat(notifications.get(0).getNotificationType()).isEqualTo( - Notification.NotificationType.RESERVATION_CHANGE_REQUEST_APPROVED); + Notification.NotificationType.RESERVATION_CHANGE_REQUEST); // 거절 알림이 잘 생성됐는지 확인 List notifications2 = notificationRepository.getNotification(savedReservation2.getReservationId(), @@ -3671,6 +3719,19 @@ void cancelApproveReservationWithApprove() { reservationRepository.saveSession(session); + // 예약 취소 요청 알림 생성 + Member member = memberRepository.getMember(1L).orElseThrow(); + Notification notification = Notification.cancelRequestReservation( + personalDetail, + savedReservation.getReservationId(), + member.getMemberId(), + member.getName(), + LocalDateTime.now(), + "개인 사유", + Notification.Reason.RESERVATION_CANCEL + ); + notificationRepository.save(notification); + // when ExtractableResponse result = post(LOCAL_HOST + port + PATH + "/%s/cancel-approve".formatted(1), request, @@ -3697,7 +3758,7 @@ void cancelApproveReservationWithApprove() { Notification.ReferenceType.RESERVATION_CANCEL); softly.assertThat(notifications.get(0)).isNotNull(); softly.assertThat(notifications.get(0).getNotificationType()).isEqualTo( - Notification.NotificationType.RESERVATION_CANCEL_REQUEST_APPROVED); + Notification.NotificationType.RESERVATION_CANCEL); }); } @@ -3736,6 +3797,19 @@ void cancelApproveReservationWithRefuse() { reservationRepository.saveSession(session); + // 예약 취소 요청 알림 생성 + Member member = memberRepository.getMember(1L).orElseThrow(); + Notification notification = Notification.cancelRequestReservation( + personalDetail, + savedReservation.getReservationId(), + member.getMemberId(), + member.getName(), + LocalDateTime.now(), + "개인 사유", + Notification.Reason.RESERVATION_CANCEL + ); + notificationRepository.save(notification); + // when ExtractableResponse result = post(LOCAL_HOST + port + PATH + "/%s/cancel-approve".formatted(1), request, @@ -3757,7 +3831,7 @@ void cancelApproveReservationWithRefuse() { Notification.ReferenceType.RESERVATION_CANCEL); softly.assertThat(notifications.get(0)).isNotNull(); softly.assertThat(notifications.get(0).getNotificationType()).isEqualTo( - Notification.NotificationType.RESERVATION_CANCEL_REQUEST_REFUSED); + Notification.NotificationType.RESERVATION_CANCEL); }); }