From a7549e6059f2e1fa9ce10ab8d4a184378fba4c2c Mon Sep 17 00:00:00 2001 From: rt3310 Date: Wed, 5 Nov 2025 15:34:48 +0900 Subject: [PATCH] =?UTF-8?q?feature[#345]:=20=EC=9D=BC=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=8B=9C=20=EB=8B=A4=EB=A5=B8=20CalendarMember?= =?UTF-8?q?=EC=97=90=EA=B2=8C=20SSE=20=EC=A0=84=EC=86=A1=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CalendarController.java | 5 +- .../service/CalendarMemberService.java | 7 +- .../goal/service/WeeklyGoalQueryService.java | 4 +- .../controller/AlarmLogController.java | 2 +- .../recordit/pushalarm/domain/AlarmLog.java | 20 +--- .../recordit/pushalarm/dto/AlarmType.java | 4 +- .../pushalarm/dto/FeedLikeMessage.java | 3 + .../recordit/pushalarm/dto/FollowMessage.java | 3 + .../recordit/pushalarm/dto/InviteMessage.java | 3 + .../recordit/pushalarm/dto/PushMessage.java | 65 +++++++------ .../pushalarm/dto/ScheduleAddMessage.java | 28 ++++++ .../pushalarm/dto/ScheduleAlarmMessage.java | 3 + .../pushalarm/dto/ScheduleDeleteMessage.java | 19 ++++ .../pushalarm/job/ScheduleAlarmJob.java | 3 +- .../pushalarm/service/AlarmLogService.java | 12 ++- .../pushalarm/service/AlarmService.java | 35 ++++--- .../pushalarm/service/SseEmitterManager.java | 49 +++++++--- .../controller/ScheduleController.java | 6 +- .../service/ScheduleCommandService.java | 96 +++++++++++++++---- .../service/ScheduleQueryService.java | 10 +- .../task/service/TaskQueryService.java | 16 +--- src/main/resources/application-local.yml | 5 +- .../controller/CalendarControllerTest.java | 5 +- .../service/WeeklyGoalQueryServiceTest.java | 2 + .../service/AlarmLogServiceTest.java | 8 +- .../service/SseEmitterManagerTest.java | 4 +- .../controller/ScheduleControllerTest.java | 3 +- .../service/ScheduleCommandServiceTest.java | 46 +++++---- .../service/ScheduleQueryServiceTest.java | 6 +- .../task/service/TaskQueryServiceTest.java | 9 +- 30 files changed, 323 insertions(+), 158 deletions(-) create mode 100644 src/main/java/com/sillim/recordit/pushalarm/dto/FeedLikeMessage.java create mode 100644 src/main/java/com/sillim/recordit/pushalarm/dto/FollowMessage.java create mode 100644 src/main/java/com/sillim/recordit/pushalarm/dto/InviteMessage.java create mode 100644 src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAddMessage.java create mode 100644 src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAlarmMessage.java create mode 100644 src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleDeleteMessage.java diff --git a/src/main/java/com/sillim/recordit/calendar/controller/CalendarController.java b/src/main/java/com/sillim/recordit/calendar/controller/CalendarController.java index 4b17036a..06ca9bcb 100644 --- a/src/main/java/com/sillim/recordit/calendar/controller/CalendarController.java +++ b/src/main/java/com/sillim/recordit/calendar/controller/CalendarController.java @@ -32,10 +32,7 @@ public class CalendarController { @GetMapping public ResponseEntity> calendarList(@CurrentMember Member member) { - return ResponseEntity.ok( - calendarMemberService.searchCalendarsByMemberId(member.getId()).stream() - .map(CalendarResponse::from) - .toList()); + return ResponseEntity.ok(calendarMemberService.searchCalendarsByMemberId(member.getId())); } @PostMapping diff --git a/src/main/java/com/sillim/recordit/calendar/service/CalendarMemberService.java b/src/main/java/com/sillim/recordit/calendar/service/CalendarMemberService.java index 32fd9b63..454a5c27 100644 --- a/src/main/java/com/sillim/recordit/calendar/service/CalendarMemberService.java +++ b/src/main/java/com/sillim/recordit/calendar/service/CalendarMemberService.java @@ -3,6 +3,7 @@ import com.sillim.recordit.calendar.domain.Calendar; import com.sillim.recordit.calendar.domain.CalendarMember; import com.sillim.recordit.calendar.dto.response.CalendarMemberResponse; +import com.sillim.recordit.calendar.dto.response.CalendarResponse; import com.sillim.recordit.calendar.repository.CalendarMemberRepository; import com.sillim.recordit.global.exception.ErrorCode; import com.sillim.recordit.global.exception.common.InvalidRequestException; @@ -50,8 +51,10 @@ public List searchCalendarMembers(Long calendarId) { } @Transactional(readOnly = true) - public List searchCalendarsByMemberId(Long memberId) { - return calendarMemberRepository.findCalendarsByMemberId(memberId); + public List searchCalendarsByMemberId(Long memberId) { + return calendarMemberRepository.findCalendarsByMemberId(memberId).stream() + .map(CalendarResponse::from) + .toList(); } public Long addCalendarMember(Long calendarId, Long memberId) { diff --git a/src/main/java/com/sillim/recordit/goal/service/WeeklyGoalQueryService.java b/src/main/java/com/sillim/recordit/goal/service/WeeklyGoalQueryService.java index 0e0124e9..71ae48e3 100644 --- a/src/main/java/com/sillim/recordit/goal/service/WeeklyGoalQueryService.java +++ b/src/main/java/com/sillim/recordit/goal/service/WeeklyGoalQueryService.java @@ -1,6 +1,7 @@ package com.sillim.recordit.goal.service; import com.sillim.recordit.calendar.domain.Calendar; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.calendar.service.CalendarQueryService; import com.sillim.recordit.global.exception.ErrorCode; import com.sillim.recordit.global.exception.common.RecordNotFoundException; @@ -18,11 +19,12 @@ public class WeeklyGoalQueryService { private final CalendarQueryService calendarQueryService; private final WeeklyGoalRepository weeklyGoalRepository; + private final CalendarMemberService calendarMemberService; public List searchAllWeeklyGoalByDate( final Integer year, final Integer month, final Long memberId, final Long calendarId) { Calendar calendar = calendarQueryService.searchByCalendarId(calendarId); - calendar.validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendar.getId(), memberId); return weeklyGoalRepository.findWeeklyGoalInMonth(year, month, calendarId); } diff --git a/src/main/java/com/sillim/recordit/pushalarm/controller/AlarmLogController.java b/src/main/java/com/sillim/recordit/pushalarm/controller/AlarmLogController.java index 25298f64..7716852f 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/controller/AlarmLogController.java +++ b/src/main/java/com/sillim/recordit/pushalarm/controller/AlarmLogController.java @@ -17,7 +17,7 @@ public class AlarmLogController { private final AlarmLogService alarmLogService; @GetMapping - public ResponseEntity> alarmLogList( + public ResponseEntity>> alarmLogList( Pageable pageable, @CurrentMember Member member) { return ResponseEntity.ok(alarmLogService.searchRecentCreated(pageable, member.getId())); } diff --git a/src/main/java/com/sillim/recordit/pushalarm/domain/AlarmLog.java b/src/main/java/com/sillim/recordit/pushalarm/domain/AlarmLog.java index 9bac4260..5f15fbae 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/domain/AlarmLog.java +++ b/src/main/java/com/sillim/recordit/pushalarm/domain/AlarmLog.java @@ -18,18 +18,12 @@ public class AlarmLog extends BaseTime { @Column(name = "alarm_log_id", nullable = false) private Long id; - @Column(nullable = false) - private Long activeId; - @Column(nullable = false) @Enumerated(EnumType.STRING) private AlarmType alarmType; @Column(nullable = false) - private String title; - - @Column(nullable = false) - private String body; + private String content; @Column(nullable = false) private Long senderId; @@ -41,17 +35,9 @@ public class AlarmLog extends BaseTime { private boolean deleted; @Builder - public AlarmLog( - Long activeId, - AlarmType alarmType, - String title, - String body, - Long senderId, - Long receiverId) { - this.activeId = activeId; + public AlarmLog(AlarmType alarmType, String content, Long senderId, Long receiverId) { this.alarmType = alarmType; - this.title = title; - this.body = body; + this.content = content; this.senderId = senderId; this.receiverId = receiverId; this.deleted = false; diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/AlarmType.java b/src/main/java/com/sillim/recordit/pushalarm/dto/AlarmType.java index f09ae544..72f6f4d3 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/dto/AlarmType.java +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/AlarmType.java @@ -1,7 +1,9 @@ package com.sillim.recordit.pushalarm.dto; public enum AlarmType { - SCHEDULE, + SCHEDULE_ADD, + SCHEDULE_DELETE, + SCHEDULE_ALARM, INVITE, FOLLOWING, FEED_LIKE, diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/FeedLikeMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/FeedLikeMessage.java new file mode 100644 index 00000000..41139749 --- /dev/null +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/FeedLikeMessage.java @@ -0,0 +1,3 @@ +package com.sillim.recordit.pushalarm.dto; + +public record FeedLikeMessage(Long feedLikeId, String title) {} diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/FollowMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/FollowMessage.java new file mode 100644 index 00000000..ec0f134c --- /dev/null +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/FollowMessage.java @@ -0,0 +1,3 @@ +package com.sillim.recordit.pushalarm.dto; + +public record FollowMessage(Long followerId, String title, String body) {} diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/InviteMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/InviteMessage.java new file mode 100644 index 00000000..83765554 --- /dev/null +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/InviteMessage.java @@ -0,0 +1,3 @@ +package com.sillim.recordit.pushalarm.dto; + +public record InviteMessage(Long inviteLogId, String title, String body) {} diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/PushMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/PushMessage.java index 6129db3f..45ed3618 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/dto/PushMessage.java +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/PushMessage.java @@ -1,49 +1,58 @@ package com.sillim.recordit.pushalarm.dto; -import com.sillim.recordit.pushalarm.domain.AlarmLog; +import com.sillim.recordit.schedule.domain.Schedule; +import java.util.List; -public record PushMessage(Long id, AlarmType type, Long activeId, String title, String body) { +public record PushMessage(Long id, AlarmType type, T content) { - public static PushMessage fromInvite( + public static PushMessage fromInvite( Long activeId, String calendarTitle, String inviterPersonalId) { - return new PushMessage( + return new PushMessage<>( null, AlarmType.INVITE, - activeId, - inviterPersonalId + "님의 " + calendarTitle + "캘린더 초대", - "참가하시겠습니까?"); + new InviteMessage( + activeId, + inviterPersonalId + "님의 " + calendarTitle + "캘린더 초대", + "참가하시겠습니까?")); } - public static PushMessage fromFollowing(Long activeId, String followerPersonalId) { - return new PushMessage( + public static PushMessage fromFollowing( + Long activeId, String followerPersonalId) { + return new PushMessage<>( null, AlarmType.FOLLOWING, - activeId, - followerPersonalId + "님이 팔로우했습니다.", - "맞팔로우하시겠습니까?"); + new FollowMessage(activeId, followerPersonalId + "님이 팔로우했습니다.", "맞팔로우하시겠습니까?")); } - public static PushMessage fromSchedule( + public static PushMessage fromScheduleAlarm( Long activeId, String scheduleTitle, String scheduledTime) { - return new PushMessage(null, AlarmType.SCHEDULE, activeId, scheduleTitle, scheduledTime); - } - - public static PushMessage fromAlarmLog(AlarmLog alarmLog) { - return new PushMessage( - alarmLog.getId(), - alarmLog.getAlarmType(), - alarmLog.getActiveId(), - alarmLog.getTitle(), - alarmLog.getBody()); + return new PushMessage<>( + null, + AlarmType.SCHEDULE_ALARM, + new ScheduleAlarmMessage(activeId, scheduleTitle, scheduledTime)); } - public static PushMessage fromFeedLike( + public static PushMessage fromFeedLike( Long feedLikeId, String likerPersonalId, String feedTitle) { - return new PushMessage( + return new PushMessage<>( null, AlarmType.FEED_LIKE, - feedLikeId, - likerPersonalId + "님이 " + feedTitle + " 피드에 좋아요를 눌렀습니다.", - ""); + new FeedLikeMessage( + feedLikeId, likerPersonalId + "님이 " + feedTitle + " 피드에 좋아요를 눌렀습니다.")); + } + + public static PushMessage> fromAddSchedules(List schedules) { + return new PushMessage<>( + null, + AlarmType.SCHEDULE_ADD, + schedules.stream().map(ScheduleAddMessage::from).toList()); + } + + public static PushMessage> fromDeleteSchedules( + List schedules) { + return new PushMessage<>( + null, + AlarmType.SCHEDULE_DELETE, + schedules.stream().map(ScheduleDeleteMessage::from).toList()); } } diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAddMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAddMessage.java new file mode 100644 index 00000000..f21453d3 --- /dev/null +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAddMessage.java @@ -0,0 +1,28 @@ +package com.sillim.recordit.pushalarm.dto; + +import com.sillim.recordit.schedule.domain.Schedule; +import java.time.LocalDateTime; +import lombok.Builder; + +@Builder +public record ScheduleAddMessage( + Long id, + String title, + boolean isAllDay, + LocalDateTime startDateTime, + LocalDateTime endDateTime, + String colorHex, + Long calendarId) { + + public static ScheduleAddMessage from(Schedule schedule) { + return ScheduleAddMessage.builder() + .id(schedule.getId()) + .title(schedule.getTitle()) + .isAllDay(schedule.isAllDay()) + .startDateTime(schedule.getStartDateTime()) + .endDateTime(schedule.getEndDateTime()) + .colorHex(schedule.getColorHex()) + .calendarId(schedule.getCalendar().getId()) + .build(); + } +} diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAlarmMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAlarmMessage.java new file mode 100644 index 00000000..46a5de87 --- /dev/null +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleAlarmMessage.java @@ -0,0 +1,3 @@ +package com.sillim.recordit.pushalarm.dto; + +public record ScheduleAlarmMessage(Long scheduleId, String title, String body) {} diff --git a/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleDeleteMessage.java b/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleDeleteMessage.java new file mode 100644 index 00000000..62f5e4dc --- /dev/null +++ b/src/main/java/com/sillim/recordit/pushalarm/dto/ScheduleDeleteMessage.java @@ -0,0 +1,19 @@ +package com.sillim.recordit.pushalarm.dto; + +import com.sillim.recordit.schedule.domain.Schedule; +import java.time.LocalDateTime; +import lombok.Builder; + +@Builder +public record ScheduleDeleteMessage( + Long scheduleId, LocalDateTime startDateTime, LocalDateTime endDateTime, Long calendarId) { + + public static ScheduleDeleteMessage from(Schedule schedule) { + return ScheduleDeleteMessage.builder() + .scheduleId(schedule.getId()) + .startDateTime(schedule.getStartDateTime()) + .endDateTime(schedule.getEndDateTime()) + .calendarId(schedule.getCalendar().getId()) + .build(); + } +} diff --git a/src/main/java/com/sillim/recordit/pushalarm/job/ScheduleAlarmJob.java b/src/main/java/com/sillim/recordit/pushalarm/job/ScheduleAlarmJob.java index c452d300..1e85c2b3 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/job/ScheduleAlarmJob.java +++ b/src/main/java/com/sillim/recordit/pushalarm/job/ScheduleAlarmJob.java @@ -47,6 +47,7 @@ public void execute(JobExecutionContext context) { } }); - alarmService.pushAlarm(-1L, memberId, PushMessage.fromSchedule(scheduleId, title, body)); + alarmService.pushAlarm( + -1L, memberId, PushMessage.fromScheduleAlarm(scheduleId, title, body)); } } diff --git a/src/main/java/com/sillim/recordit/pushalarm/service/AlarmLogService.java b/src/main/java/com/sillim/recordit/pushalarm/service/AlarmLogService.java index 9c6cb778..4552bfbe 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/service/AlarmLogService.java +++ b/src/main/java/com/sillim/recordit/pushalarm/service/AlarmLogService.java @@ -20,14 +20,22 @@ public class AlarmLogService { private final AlarmLogRepository alarmLogRepository; - public SliceResponse searchRecentCreated(Pageable pageable, Long memberId) { + public SliceResponse> searchRecentCreated( + Pageable pageable, Long memberId) { Slice alarmSlice = alarmLogRepository.findByDeletedIsFalseAndReceiverIdOrderByCreatedAtDesc( pageable, memberId); return SliceResponse.of( new SliceImpl<>( - alarmSlice.stream().map(PushMessage::fromAlarmLog).toList(), + alarmSlice.stream() + .map( + alarmLog -> + new PushMessage<>( + alarmLog.getId(), + alarmLog.getAlarmType(), + alarmLog.getContent())) + .toList(), pageable, alarmSlice.hasNext())); } diff --git a/src/main/java/com/sillim/recordit/pushalarm/service/AlarmService.java b/src/main/java/com/sillim/recordit/pushalarm/service/AlarmService.java index d5752198..32eb8ce2 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/service/AlarmService.java +++ b/src/main/java/com/sillim/recordit/pushalarm/service/AlarmService.java @@ -1,5 +1,9 @@ package com.sillim.recordit.pushalarm.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sillim.recordit.global.exception.ErrorCode; +import com.sillim.recordit.global.exception.common.ApplicationException; import com.sillim.recordit.pushalarm.domain.AlarmLog; import com.sillim.recordit.pushalarm.dto.PushMessage; import com.sillim.recordit.pushalarm.repository.AlarmLogRepository; @@ -13,20 +17,25 @@ public class AlarmService { private final SseEmitterManager sseEmitterManager; private final AlarmLogRepository alarmLogRepository; + private final ObjectMapper objectMapper; - public boolean pushAlarm(Long senderId, Long receiverId, PushMessage message) { - AlarmLog alarmLog = - alarmLogRepository.save( - AlarmLog.builder() - .activeId(message.activeId()) - .alarmType(message.type()) - .title(message.title()) - .body(message.body()) - .senderId(senderId) - .receiverId(receiverId) - .build()); - - return sseEmitterManager.sendToClient(receiverId, PushMessage.fromAlarmLog(alarmLog)); + public void pushAlarm(Long senderId, Long receiverId, PushMessage message) { + try { + AlarmLog alarmLog = + alarmLogRepository.save( + AlarmLog.builder() + .alarmType(message.type()) + .content(objectMapper.writeValueAsString(message.content())) + .senderId(senderId) + .receiverId(receiverId) + .build()); + sseEmitterManager.sendToClient( + receiverId, + new PushMessage<>(alarmLog.getId(), message.type(), message.content())); + } catch (JsonProcessingException e) { + throw new ApplicationException( + ErrorCode.UNHANDLED_EXCEPTION, "AlarmLog content를 직렬화하지 못했습니다."); + } } public void deleteAlarm(Long alarmLogId) { diff --git a/src/main/java/com/sillim/recordit/pushalarm/service/SseEmitterManager.java b/src/main/java/com/sillim/recordit/pushalarm/service/SseEmitterManager.java index e60591dd..6bdb816d 100644 --- a/src/main/java/com/sillim/recordit/pushalarm/service/SseEmitterManager.java +++ b/src/main/java/com/sillim/recordit/pushalarm/service/SseEmitterManager.java @@ -16,30 +16,53 @@ public class SseEmitterManager { private final ConcurrentHashMap clients = new ConcurrentHashMap<>(); public SseEmitter subscribe(Long memberId) { + SseEmitter oldEmitter = clients.get(memberId); + if (oldEmitter != null) { + log.info("[SSE CLEANUP] Old Connection 삭제, Member: {}", memberId); + oldEmitter.complete(); + clients.remove(memberId); + } + SseEmitter emitter = new SseEmitter(TIMEOUT); clients.put(memberId, emitter); - emitter.onCompletion(() -> clients.remove(memberId)); - emitter.onTimeout(() -> clients.remove(memberId)); + emitter.onCompletion( + () -> { + log.info("[SSE COMPLETE] Member: {}", memberId); + clients.remove(memberId); + }); + emitter.onTimeout( + () -> { + log.info("[SSE TIMEOUT] Member: {}", memberId); + clients.remove(memberId); + }); + emitter.onError( + throwable -> { + log.info("[SSE ERROR] Member: {}, Error: {}", memberId, throwable.getMessage()); + clients.remove(memberId); + }); log.info("[SSE SUBSCRIBE] {}", memberId); return emitter; } - public boolean sendToClient(Long memberId, PushMessage message) { + public boolean sendToClient(Long memberId, PushMessage message) { SseEmitter emitter = clients.get(memberId); - if (emitter != null) { - try { - emitter.send(SseEmitter.event().name(message.type().name()).data(message)); - return true; - } catch (IOException exception) { - clients.remove(memberId); - emitter.completeWithError(exception); - return false; - } + if (emitter == null) { + log.warn("[SSE NOT FOUND] Member: {}", memberId); + return false; + } + + log.info("[SSE SEND] {} {}", memberId, emitter); + try { + emitter.send(SseEmitter.event().name(message.type().name()).data(message)); + return true; + } catch (IOException exception) { + clients.remove(memberId); + emitter.completeWithError(exception); + return false; } - return false; } } diff --git a/src/main/java/com/sillim/recordit/schedule/controller/ScheduleController.java b/src/main/java/com/sillim/recordit/schedule/controller/ScheduleController.java index 3850b76e..0de4aacc 100644 --- a/src/main/java/com/sillim/recordit/schedule/controller/ScheduleController.java +++ b/src/main/java/com/sillim/recordit/schedule/controller/ScheduleController.java @@ -27,10 +27,12 @@ public class ScheduleController { @PostMapping public ResponseEntity> schedulesAdd( - @Validated @RequestBody ScheduleAddRequest request, @PathVariable Long calendarId) + @Validated @RequestBody ScheduleAddRequest request, + @PathVariable Long calendarId, + @CurrentMember Member member) throws SchedulerException { return ResponseEntity.ok( - scheduleCommandService.addSchedules(request, calendarId).stream() + scheduleCommandService.addSchedules(request, calendarId, member.getId()).stream() .map(MonthScheduleResponse::from) .toList()); } diff --git a/src/main/java/com/sillim/recordit/schedule/service/ScheduleCommandService.java b/src/main/java/com/sillim/recordit/schedule/service/ScheduleCommandService.java index c11f82cc..fde325a0 100644 --- a/src/main/java/com/sillim/recordit/schedule/service/ScheduleCommandService.java +++ b/src/main/java/com/sillim/recordit/schedule/service/ScheduleCommandService.java @@ -1,11 +1,17 @@ package com.sillim.recordit.schedule.service; import com.sillim.recordit.calendar.domain.Calendar; +import com.sillim.recordit.calendar.dto.response.CalendarMemberResponse; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.calendar.service.CalendarQueryService; import com.sillim.recordit.category.domain.ScheduleCategory; import com.sillim.recordit.category.service.ScheduleCategoryQueryService; import com.sillim.recordit.global.exception.ErrorCode; import com.sillim.recordit.global.exception.common.RecordNotFoundException; +import com.sillim.recordit.pushalarm.dto.AlarmType; +import com.sillim.recordit.pushalarm.dto.PushMessage; +import com.sillim.recordit.pushalarm.dto.ScheduleDeleteMessage; +import com.sillim.recordit.pushalarm.service.AlarmService; import com.sillim.recordit.pushalarm.service.PushAlarmReserver; import com.sillim.recordit.schedule.domain.Schedule; import com.sillim.recordit.schedule.domain.ScheduleAlarm; @@ -33,6 +39,8 @@ public class ScheduleCommandService { private static final long TO_SKIP_ADD_TEMPORAL = 0L; private static final long TO_SKIP_MODIFY_TEMPORAL = 1L; private static final String SCHEDULE_GROUP_PREFIX = "SCHEDULE/"; + public static final DateTimeFormatter ALARM_FORMATTER = + DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 (E)"); private final ScheduleRepository scheduleRepository; private final CalendarQueryService calendarQueryService; @@ -40,16 +48,19 @@ public class ScheduleCommandService { private final RepetitionPatternService repetitionPatternService; private final PushAlarmReserver pushAlarmReserver; private final ScheduleCategoryQueryService scheduleCategoryQueryService; + private final AlarmService alarmService; + private final CalendarMemberService calendarMemberService; - public List addSchedules(ScheduleAddRequest request, Long calendarId) + public List addSchedules(ScheduleAddRequest request, Long calendarId, Long memberId) throws SchedulerException { + calendarMemberService.validateCalendarMember(calendarId, memberId); ScheduleCategory scheduleCategory = scheduleCategoryQueryService.searchScheduleCategory(request.categoryId()); ScheduleGroup scheduleGroup = scheduleGroupService.newScheduleGroup(request.isRepeated()); + Calendar calendar = calendarQueryService.searchByCalendarId(calendarId); List schedules; if (request.isRepeated()) { - Calendar calendar = calendarQueryService.searchByCalendarId(calendarId); schedules = addRepeatingSchedule( temporalAmount -> @@ -62,15 +73,11 @@ public List addSchedules(ScheduleAddRequest request, Long calendarId) request.repetition(), scheduleGroup, TO_SKIP_ADD_TEMPORAL); - } else { schedules = List.of( scheduleRepository.save( - request.toSchedule( - scheduleCategory, - calendarQueryService.searchByCalendarId(calendarId), - scheduleGroup))); + request.toSchedule(scheduleCategory, calendar, scheduleGroup))); } Schedule standSchedule = schedules.get(0); @@ -89,6 +96,14 @@ public List addSchedules(ScheduleAddRequest request, Long calendarId) .map(ScheduleAlarm::getAlarmTime) .toList()); + calendarMemberService + .searchCalendarMembers(calendarId) + .forEach( + member -> + alarmService.pushAlarm( + memberId, + member.id(), + PushMessage.fromAddSchedules(schedules))); return schedules; } @@ -128,8 +143,7 @@ public void modifySchedule(ScheduleModifyRequest request, Long scheduleId, Long memberId, SCHEDULE_GROUP_PREFIX + memberId + "/" + newScheduleGroup.getId(), schedule.getTitle(), - schedule.getStartDateTime() - .format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 (E)")), + schedule.getStartDateTime().format(ALARM_FORMATTER), Map.of("scheduleId", schedule.getId()), schedule.getScheduleAlarms().stream().map(ScheduleAlarm::getAlarmTime).toList()); @@ -209,11 +223,21 @@ public void removeSchedule(Long scheduleId, Long memberId) throws SchedulerExcep .findByScheduleId(scheduleId) .orElseThrow( () -> new RecordNotFoundException(ErrorCode.SCHEDULE_NOT_FOUND)); - schedule.validateAuthenticatedMember(memberId); + Long calendarId = schedule.getCalendar().getId(); + calendarMemberService.validateCalendarMember(calendarId, memberId); + pushAlarmReserver.deletePushAlarmJobs( SCHEDULE_GROUP_PREFIX + memberId + "/" + schedule.getScheduleGroup().getId()); - schedule.delete(); + + List calendarMembers = + calendarMemberService.searchCalendarMembers(calendarId); + calendarMembers.forEach( + calendarMember -> + alarmService.pushAlarm( + memberId, + calendarMember.memberId(), + PushMessage.fromDeleteSchedules(List.of(schedule)))); } public void removeGroupSchedules(Long scheduleId, Long memberId) throws SchedulerException { @@ -222,13 +246,28 @@ public void removeGroupSchedules(Long scheduleId, Long memberId) throws Schedule .findByScheduleId(scheduleId) .orElseThrow( () -> new RecordNotFoundException(ErrorCode.SCHEDULE_NOT_FOUND)); - schedule.validateAuthenticatedMember(memberId); + Long calendarId = schedule.getCalendar().getId(); + calendarMemberService.validateCalendarMember(calendarId, memberId); + pushAlarmReserver.deletePushAlarmJobs( SCHEDULE_GROUP_PREFIX + memberId + "/" + schedule.getScheduleGroup().getId()); + List messages = + scheduleRepository.findGroupSchedules(schedule.getScheduleGroup().getId()).stream() + .map( + s -> { + s.delete(); + return ScheduleDeleteMessage.from(s); + }) + .toList(); - scheduleRepository - .findGroupSchedules(schedule.getScheduleGroup().getId()) - .forEach(Schedule::delete); + List calendarMembers = + calendarMemberService.searchCalendarMembers(calendarId); + calendarMembers.forEach( + calendarMember -> + alarmService.pushAlarm( + memberId, + calendarMember.memberId(), + new PushMessage<>(null, AlarmType.SCHEDULE_DELETE, messages))); } public void removeGroupSchedulesAfterCurrent(Long scheduleId, Long memberId) { @@ -237,12 +276,29 @@ public void removeGroupSchedulesAfterCurrent(Long scheduleId, Long memberId) { .findByScheduleId(scheduleId) .orElseThrow( () -> new RecordNotFoundException(ErrorCode.SCHEDULE_NOT_FOUND)); - schedule.validateAuthenticatedMember(memberId); + Long calendarId = schedule.getCalendar().getId(); + calendarMemberService.validateCalendarMember(calendarId, memberId); + + List messages = + scheduleRepository + .findGroupSchedulesAfterCurrent( + schedule.getScheduleGroup().getId(), schedule.getStartDateTime()) + .stream() + .map( + s -> { + s.delete(); + return ScheduleDeleteMessage.from(s); + }) + .toList(); - scheduleRepository - .findGroupSchedulesAfterCurrent( - schedule.getScheduleGroup().getId(), schedule.getStartDateTime()) - .forEach(Schedule::delete); + List calendarMembers = + calendarMemberService.searchCalendarMembers(calendarId); + calendarMembers.forEach( + calendarMember -> + alarmService.pushAlarm( + memberId, + calendarMember.memberId(), + new PushMessage<>(null, AlarmType.SCHEDULE_DELETE, messages))); } public void replaceScheduleCategoriesWithDefaultCategory( diff --git a/src/main/java/com/sillim/recordit/schedule/service/ScheduleQueryService.java b/src/main/java/com/sillim/recordit/schedule/service/ScheduleQueryService.java index e541d65d..11d319c9 100644 --- a/src/main/java/com/sillim/recordit/schedule/service/ScheduleQueryService.java +++ b/src/main/java/com/sillim/recordit/schedule/service/ScheduleQueryService.java @@ -1,6 +1,6 @@ package com.sillim.recordit.schedule.service; -import com.sillim.recordit.calendar.service.CalendarQueryService; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.global.exception.ErrorCode; import com.sillim.recordit.global.exception.common.RecordNotFoundException; import com.sillim.recordit.schedule.domain.Schedule; @@ -22,7 +22,7 @@ public class ScheduleQueryService { private final ScheduleRepository scheduleRepository; private final RepetitionPatternService repetitionPatternService; - private final CalendarQueryService calendarQueryService; + private final CalendarMemberService calendarMemberService; public DayScheduleResponse searchSchedule(Long scheduleId, Long memberId) { Schedule schedule = @@ -51,7 +51,7 @@ public DayScheduleResponse searchSchedule(Long scheduleId, Long memberId) { public List searchSchedulesInMonth( Long calendarId, Integer year, Integer month, Long memberId) { - calendarQueryService.searchByCalendarId(calendarId).validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendarId, memberId); return scheduleRepository.findScheduleInMonth(calendarId, year, month).stream() .map(MonthScheduleResponse::from) .toList(); @@ -59,7 +59,7 @@ public List searchSchedulesInMonth( public List searchSchedulesInDay( Long calendarId, LocalDate date, Long memberId) { - calendarQueryService.searchByCalendarId(calendarId).validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendarId, memberId); return scheduleRepository.findScheduleInDay(calendarId, date).stream() .map( schedule -> @@ -78,7 +78,7 @@ public List searchSchedulesInDay( public List searchSchedulesContainQuery( String query, Long calendarId, Long memberId) { - calendarQueryService.searchByCalendarId(calendarId).validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendarId, memberId); return scheduleRepository.findScheduleMatchedQuery(query, calendarId).stream() .map( schedule -> diff --git a/src/main/java/com/sillim/recordit/task/service/TaskQueryService.java b/src/main/java/com/sillim/recordit/task/service/TaskQueryService.java index 32f3f571..7e5f918b 100644 --- a/src/main/java/com/sillim/recordit/task/service/TaskQueryService.java +++ b/src/main/java/com/sillim/recordit/task/service/TaskQueryService.java @@ -1,7 +1,6 @@ package com.sillim.recordit.task.service; -import com.sillim.recordit.calendar.domain.Calendar; -import com.sillim.recordit.calendar.service.CalendarQueryService; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.global.exception.ErrorCode; import com.sillim.recordit.global.exception.common.RecordNotFoundException; import com.sillim.recordit.task.domain.Task; @@ -18,31 +17,26 @@ @RequiredArgsConstructor public class TaskQueryService { - private final CalendarQueryService calendarQueryService; private final TaskRepository taskRepository; + private final CalendarMemberService calendarMemberService; public List searchAllByDate( final Long calendarId, final LocalDate date, final Long memberId) { - - Calendar calendar = calendarQueryService.searchByCalendarId(calendarId); - calendar.validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendarId, memberId); return taskRepository.findAllByCalendarIdAndDate(calendarId, date); } public List searchTasksInMonth( Long calendarId, Long memberId, Integer year, Integer month) { - Calendar calendar = calendarQueryService.searchByCalendarId(calendarId); - calendar.validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendarId, memberId); return taskRepository.findTasksInMonth(calendarId, year, month); } public TaskDetailsResponse searchByIdAndCalendarId( final Long taskId, final Long calendarId, final Long memberId) { - - Calendar calendar = calendarQueryService.searchByCalendarId(calendarId); - calendar.validateAuthenticatedMember(memberId); + calendarMemberService.validateCalendarMember(calendarId, memberId); Task task = taskRepository .findByIdAndCalendarId(taskId, calendarId) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index e3e65ffc..d7491edc 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -11,7 +11,8 @@ spring: auto: create dialect: org.hibernate.dialect.H2Dialect datasource: - jdbc-url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +# jdbc-url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + jdbc-url: jdbc:h2:~/h2;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE driver-class-name: org.h2.Driver username: sa password: @@ -65,7 +66,7 @@ spring: port: 6379 rabbitmq: - host: 34.64.157.50 + host: localhost port: 5672 username: guest password: guest diff --git a/src/test/java/com/sillim/recordit/calendar/controller/CalendarControllerTest.java b/src/test/java/com/sillim/recordit/calendar/controller/CalendarControllerTest.java index a17c42f3..72d97192 100644 --- a/src/test/java/com/sillim/recordit/calendar/controller/CalendarControllerTest.java +++ b/src/test/java/com/sillim/recordit/calendar/controller/CalendarControllerTest.java @@ -18,6 +18,7 @@ import com.sillim.recordit.calendar.dto.request.CalendarModifyRequest; import com.sillim.recordit.calendar.dto.request.JoinInCalendarRequest; import com.sillim.recordit.calendar.dto.response.CalendarMemberResponse; +import com.sillim.recordit.calendar.dto.response.CalendarResponse; import com.sillim.recordit.calendar.fixture.CalendarCategoryFixture; import com.sillim.recordit.calendar.fixture.CalendarFixture; import com.sillim.recordit.calendar.service.CalendarCommandService; @@ -59,7 +60,9 @@ void initObjects() { void calendarList() throws Exception { CalendarCategory category = CalendarCategoryFixture.DEFAULT.getCalendarCategory(memberId); Calendar calendar = CalendarFixture.DEFAULT.getCalendar(category, memberId); - given(calendarMemberService.searchCalendarsByMemberId(any())).willReturn(List.of(calendar)); + CalendarResponse calendarResponse = CalendarResponse.from(calendar); + given(calendarMemberService.searchCalendarsByMemberId(any())) + .willReturn(List.of(calendarResponse)); ResultActions perform = mockMvc.perform(get("/api/v1/calendars")); diff --git a/src/test/java/com/sillim/recordit/goal/service/WeeklyGoalQueryServiceTest.java b/src/test/java/com/sillim/recordit/goal/service/WeeklyGoalQueryServiceTest.java index 34206390..60c16534 100644 --- a/src/test/java/com/sillim/recordit/goal/service/WeeklyGoalQueryServiceTest.java +++ b/src/test/java/com/sillim/recordit/goal/service/WeeklyGoalQueryServiceTest.java @@ -11,6 +11,7 @@ import com.sillim.recordit.calendar.domain.CalendarCategory; import com.sillim.recordit.calendar.fixture.CalendarCategoryFixture; import com.sillim.recordit.calendar.fixture.CalendarFixture; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.calendar.service.CalendarQueryService; import com.sillim.recordit.category.domain.ScheduleCategory; import com.sillim.recordit.category.fixture.ScheduleCategoryFixture; @@ -36,6 +37,7 @@ public class WeeklyGoalQueryServiceTest { @Mock WeeklyGoalRepository weeklyGoalRepository; @Mock CalendarQueryService calendarQueryService; + @Mock CalendarMemberService calendarMemberService; @InjectMocks WeeklyGoalQueryService weeklyGoalQueryService; long memberId = 1L; diff --git a/src/test/java/com/sillim/recordit/pushalarm/service/AlarmLogServiceTest.java b/src/test/java/com/sillim/recordit/pushalarm/service/AlarmLogServiceTest.java index 560e5662..6569a816 100644 --- a/src/test/java/com/sillim/recordit/pushalarm/service/AlarmLogServiceTest.java +++ b/src/test/java/com/sillim/recordit/pushalarm/service/AlarmLogServiceTest.java @@ -35,14 +35,14 @@ class AlarmLogServiceTest { void searchRecentCreated() { long memberId = 1L; PageRequest pageable = PageRequest.of(0, 1); - AlarmLog alarmLog = new AlarmLog(1L, AlarmType.FOLLOWING, "title", "body", 1L, 1L); + AlarmLog alarmLog = new AlarmLog(AlarmType.FOLLOWING, "content", 1L, 1L); SliceImpl slice = new SliceImpl<>(List.of(alarmLog), pageable, false); given( alarmLogRepository.findByDeletedIsFalseAndReceiverIdOrderByCreatedAtDesc( eq(pageable), eq(memberId))) .willReturn(slice); - SliceResponse response = + SliceResponse> response = alarmLogService.searchRecentCreated(pageable, memberId); assertThat(response.content()).hasSize(1); @@ -53,7 +53,7 @@ void searchRecentCreated() { void deleteAlarmLog() { long alarmLogId = 1L; long memberId = 1L; - AlarmLog alarmLog = spy(new AlarmLog(1L, AlarmType.FOLLOWING, "title", "body", 1L, 1L)); + AlarmLog alarmLog = spy(new AlarmLog(AlarmType.FOLLOWING, "content", 1L, 1L)); given(alarmLogRepository.findById(eq(alarmLogId))).willReturn(Optional.of(alarmLog)); alarmLogService.deleteAlarmLog(alarmLogId, memberId); @@ -66,7 +66,7 @@ void deleteAlarmLog() { void throwInvalidRequestExceptionIfNotReceiverWhenDeleteAlarmLog() { long alarmLogId = 1L; long memberId = 1L; - AlarmLog alarmLog = new AlarmLog(1L, AlarmType.FOLLOWING, "title", "body", 1L, 2L); + AlarmLog alarmLog = new AlarmLog(AlarmType.FOLLOWING, "content", 1L, 2L); given(alarmLogRepository.findById(eq(alarmLogId))).willReturn(Optional.of(alarmLog)); assertThatCode(() -> alarmLogService.deleteAlarmLog(alarmLogId, memberId)) diff --git a/src/test/java/com/sillim/recordit/pushalarm/service/SseEmitterManagerTest.java b/src/test/java/com/sillim/recordit/pushalarm/service/SseEmitterManagerTest.java index 288529ac..dde8f1e1 100644 --- a/src/test/java/com/sillim/recordit/pushalarm/service/SseEmitterManagerTest.java +++ b/src/test/java/com/sillim/recordit/pushalarm/service/SseEmitterManagerTest.java @@ -19,7 +19,7 @@ void sendToClient() { boolean result = sseEmitterManager.sendToClient( - memberId, new PushMessage(1L, AlarmType.INVITE, 1L, "title", "body")); + memberId, new PushMessage<>(1L, AlarmType.INVITE, "content")); assertThat(result).isTrue(); } @@ -31,7 +31,7 @@ void cantSendToClientIfNotExistsClientSubscribeSseEmitter() { boolean result = sseEmitterManager.sendToClient( - memberId, new PushMessage(1L, AlarmType.INVITE, 1L, "title", "body")); + memberId, new PushMessage<>(1L, AlarmType.INVITE, "content")); assertThat(result).isFalse(); } diff --git a/src/test/java/com/sillim/recordit/schedule/controller/ScheduleControllerTest.java b/src/test/java/com/sillim/recordit/schedule/controller/ScheduleControllerTest.java index 2b7ace3b..7b2d2959 100644 --- a/src/test/java/com/sillim/recordit/schedule/controller/ScheduleControllerTest.java +++ b/src/test/java/com/sillim/recordit/schedule/controller/ScheduleControllerTest.java @@ -72,6 +72,7 @@ void initObjects() { @DisplayName("일정을 생성한다.") void addSchedule() throws Exception { long calendarId = 1L; + long memberId = 1L; ScheduleAddRequest scheduleAddRequest = new ScheduleAddRequest( "title", @@ -97,7 +98,7 @@ void addSchedule() throws Exception { calendar, LocalDateTime.of(2024, 1, 1, 0, 0), LocalDateTime.of(2024, 2, 1, 0, 0)); - given(scheduleCommandService.addSchedules(scheduleAddRequest, calendarId)) + given(scheduleCommandService.addSchedules(scheduleAddRequest, calendarId, memberId)) .willReturn(List.of(schedule)); ResultActions perform = diff --git a/src/test/java/com/sillim/recordit/schedule/service/ScheduleCommandServiceTest.java b/src/test/java/com/sillim/recordit/schedule/service/ScheduleCommandServiceTest.java index e7a6c0a0..f0746a1f 100644 --- a/src/test/java/com/sillim/recordit/schedule/service/ScheduleCommandServiceTest.java +++ b/src/test/java/com/sillim/recordit/schedule/service/ScheduleCommandServiceTest.java @@ -3,22 +3,22 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.*; import com.google.firebase.messaging.FirebaseMessagingException; import com.sillim.recordit.calendar.domain.Calendar; import com.sillim.recordit.calendar.domain.CalendarCategory; import com.sillim.recordit.calendar.fixture.CalendarCategoryFixture; import com.sillim.recordit.calendar.fixture.CalendarFixture; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.calendar.service.CalendarQueryService; import com.sillim.recordit.category.domain.ScheduleCategory; import com.sillim.recordit.category.fixture.ScheduleCategoryFixture; import com.sillim.recordit.category.service.ScheduleCategoryQueryService; import com.sillim.recordit.global.exception.ErrorCode; -import com.sillim.recordit.global.exception.common.InvalidRequestException; +import com.sillim.recordit.global.exception.common.RecordNotFoundException; import com.sillim.recordit.member.domain.Member; import com.sillim.recordit.member.fixture.MemberFixture; import com.sillim.recordit.pushalarm.service.PushAlarmReserver; @@ -48,6 +48,7 @@ class ScheduleCommandServiceTest { @Mock ScheduleRepository scheduleRepository; @Mock CalendarQueryService calendarQueryService; @Mock ScheduleGroupService scheduleGroupService; + @Mock CalendarMemberService calendarMemberService; @Mock RepetitionPatternService repetitionPatternService; @Mock PushAlarmReserver pushAlarmReserver; @Mock ScheduleCategoryQueryService scheduleCategoryQueryService; @@ -85,7 +86,8 @@ void addSchedule() throws SchedulerException { given(scheduleRepository.save(any(Schedule.class))).willReturn(schedule); given(scheduleGroupService.newScheduleGroup(any())).willReturn(scheduleGroup); - List schedules = scheduleCommandService.addSchedules(scheduleAddRequest, 1L); + List schedules = + scheduleCommandService.addSchedules(scheduleAddRequest, 1L, memberId); assertAll( () -> { @@ -152,7 +154,8 @@ void addRepeatingSchedules() throws FirebaseMessagingException, SchedulerExcepti given(repetitionPatternService.addRepetitionPattern(repetitionUpdateRequest, scheduleGroup)) .willReturn(repetitionPattern); - List schedules = scheduleCommandService.addSchedules(scheduleAddRequest, 1L); + List schedules = + scheduleCommandService.addSchedules(scheduleAddRequest, 1L, memberId); assertAll( () -> { @@ -363,8 +366,10 @@ void modifyGroupSchedulesToNotRepeated() throws SchedulerException { @Test @DisplayName("일정을 삭제할 수 있다.") void removeSchedule() throws SchedulerException { + Calendar calendar = mock(Calendar.class); Schedule schedule = mock(Schedule.class); ScheduleGroup scheduleGroup = new ScheduleGroup(false); + given(schedule.getCalendar()).willReturn(calendar); given(schedule.getScheduleGroup()).willReturn(scheduleGroup); given(scheduleRepository.findByScheduleId(any())).willReturn(Optional.of(schedule)); @@ -378,7 +383,9 @@ void removeSchedule() throws SchedulerException { void removeGroupSchedules() throws SchedulerException { Schedule schedule = mock(Schedule.class); ScheduleGroup scheduleGroup = mock(ScheduleGroup.class); + Calendar calendar = mock(Calendar.class); given(schedule.getScheduleGroup()).willReturn(scheduleGroup); + given(schedule.getCalendar()).willReturn(calendar); given(scheduleGroup.getId()).willReturn(1L); given(scheduleRepository.findByScheduleId(any())).willReturn(Optional.of(schedule)); given(scheduleRepository.findGroupSchedules(eq(1L))) @@ -392,8 +399,10 @@ void removeGroupSchedules() throws SchedulerException { @Test @DisplayName("그룹 내 특정 일 이후 일정을 삭제할 수 있다.") void removeGroupSchedulesAfter() { + Calendar calendar = mock(Calendar.class); Schedule schedule = mock(Schedule.class); ScheduleGroup scheduleGroup = mock(ScheduleGroup.class); + given(schedule.getCalendar()).willReturn(calendar); given(schedule.getScheduleGroup()).willReturn(scheduleGroup); given(schedule.getStartDateTime()).willReturn(LocalDateTime.of(2024, 1, 1, 0, 0)); given(scheduleGroup.getId()).willReturn(1L); @@ -407,10 +416,9 @@ void removeGroupSchedulesAfter() { } @Test - @DisplayName("그룹 내 일정 삭제 시 해당 일정의 유저가 아니면 InvalidRequestException이 발생한다.") - void throwInvalidRequestExceptionIfNotOwnerWhenRemoveGroupSchedules() { + @DisplayName("그룹 내 일정 삭제 시 해당 일정의 유저가 아니면 RecordNotFoundException이 발생한다.") + void throwRecordNotFoundExceptionIfNotOwnerWhenRemoveGroupSchedules() { long memberId = 1L; - Member member = mock(Member.class); CalendarCategory category = CalendarCategoryFixture.DEFAULT.getCalendarCategory(memberId); Calendar calendar = CalendarFixture.DEFAULT.getCalendar(category, memberId); ScheduleGroup scheduleGroup = new ScheduleGroup(false); @@ -419,15 +427,18 @@ void throwInvalidRequestExceptionIfNotOwnerWhenRemoveGroupSchedules() { Schedule schedule = ScheduleFixture.DEFAULT.getSchedule(scheduleCategory, scheduleGroup, calendar); given(scheduleRepository.findByScheduleId(any())).willReturn(Optional.of(schedule)); + willThrow(new RecordNotFoundException(ErrorCode.CALENDAR_MEMBER_NOT_FOUND)) + .given(calendarMemberService) + .validateCalendarMember(any(), eq(2L)); assertThatCode(() -> scheduleCommandService.removeGroupSchedules(schedule.getId(), 2L)) - .isInstanceOf(InvalidRequestException.class) - .hasMessage(ErrorCode.INVALID_REQUEST.getDescription()); + .isInstanceOf(RecordNotFoundException.class) + .hasMessage(ErrorCode.CALENDAR_MEMBER_NOT_FOUND.getDescription()); } @Test - @DisplayName("그룹 내 특정 일 이후 일정 삭제 시 해당 일정의 유저가 아니면 InvalidRequestException이 발생한다.") - void throwInvalidRequestExceptionIfNotOwnerWhenRemoveGroupSchedulesAfter() { + @DisplayName("그룹 내 특정 일 이후 일정 삭제 시 해당 일정의 유저가 아니면 RecordNotFoundException이 발생한다.") + void throwRecordNotFoundExceptionIfNotOwnerWhenRemoveGroupSchedulesAfter() { long memberId = 1L; Member member = mock(Member.class); CalendarCategory category = CalendarCategoryFixture.DEFAULT.getCalendarCategory(memberId); @@ -438,12 +449,15 @@ void throwInvalidRequestExceptionIfNotOwnerWhenRemoveGroupSchedulesAfter() { Schedule schedule = ScheduleFixture.DEFAULT.getSchedule(scheduleCategory, scheduleGroup, calendar); given(scheduleRepository.findByScheduleId(any())).willReturn(Optional.of(schedule)); + willThrow(new RecordNotFoundException(ErrorCode.CALENDAR_MEMBER_NOT_FOUND)) + .given(calendarMemberService) + .validateCalendarMember(any(), eq(2L)); assertThatCode( () -> scheduleCommandService.removeGroupSchedulesAfterCurrent( schedule.getId(), 2L)) - .isInstanceOf(InvalidRequestException.class) - .hasMessage(ErrorCode.INVALID_REQUEST.getDescription()); + .isInstanceOf(RecordNotFoundException.class) + .hasMessage(ErrorCode.CALENDAR_MEMBER_NOT_FOUND.getDescription()); } } diff --git a/src/test/java/com/sillim/recordit/schedule/service/ScheduleQueryServiceTest.java b/src/test/java/com/sillim/recordit/schedule/service/ScheduleQueryServiceTest.java index 77c733df..540ab1a2 100644 --- a/src/test/java/com/sillim/recordit/schedule/service/ScheduleQueryServiceTest.java +++ b/src/test/java/com/sillim/recordit/schedule/service/ScheduleQueryServiceTest.java @@ -9,7 +9,7 @@ import static org.mockito.Mockito.mock; import com.sillim.recordit.calendar.domain.Calendar; -import com.sillim.recordit.calendar.service.CalendarQueryService; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.category.domain.ScheduleCategory; import com.sillim.recordit.global.exception.ErrorCode; import com.sillim.recordit.global.exception.common.InvalidRequestException; @@ -35,7 +35,7 @@ class ScheduleQueryServiceTest { @Mock ScheduleRepository scheduleRepository; - @Mock CalendarQueryService calendarQueryService; + @Mock CalendarMemberService calendarMemberService; @Mock RepetitionPatternService repetitionPatternService; @InjectMocks ScheduleQueryService scheduleQueryService; @@ -167,7 +167,6 @@ void searchScheduleInMonth() { .calendar(calendar) .scheduleAlarms(List.of(LocalDateTime.of(2024, 1, 1, 0, 0))) .build(); - given(calendarQueryService.searchByCalendarId(anyLong())).willReturn(calendar); given(scheduleRepository.findScheduleInMonth(anyLong(), eq(2024), eq(1))) .willReturn(List.of(expectedSchedule)); @@ -201,7 +200,6 @@ void searchScheduleInDay() { .calendar(calendar) .scheduleAlarms(List.of(LocalDateTime.of(2024, 1, 1, 0, 0))) .build(); - given(calendarQueryService.searchByCalendarId(anyLong())).willReturn(calendar); given(scheduleRepository.findScheduleInDay(anyLong(), eq(LocalDate.of(2024, 1, 15)))) .willReturn(List.of(expectedSchedule)); diff --git a/src/test/java/com/sillim/recordit/task/service/TaskQueryServiceTest.java b/src/test/java/com/sillim/recordit/task/service/TaskQueryServiceTest.java index 867fedab..eb515edc 100644 --- a/src/test/java/com/sillim/recordit/task/service/TaskQueryServiceTest.java +++ b/src/test/java/com/sillim/recordit/task/service/TaskQueryServiceTest.java @@ -10,7 +10,7 @@ import static org.mockito.Mockito.spy; import com.sillim.recordit.calendar.domain.Calendar; -import com.sillim.recordit.calendar.service.CalendarQueryService; +import com.sillim.recordit.calendar.service.CalendarMemberService; import com.sillim.recordit.category.domain.ScheduleCategory; import com.sillim.recordit.category.fixture.ScheduleCategoryFixture; import com.sillim.recordit.global.exception.ErrorCode; @@ -40,7 +40,7 @@ class TaskQueryServiceTest { @InjectMocks TaskQueryService taskQueryService; - @Mock CalendarQueryService calendarQueryService; + @Mock CalendarMemberService calendarMemberService; @Mock TaskRepository taskRepository; private Member member; @@ -67,7 +67,6 @@ void searchAllByCalendarIdAndDate() { LocalDate.of(2024, 6, 12), taskCategory, calendar, taskGroup), TaskFixture.DEFAULT.getWithDate( LocalDate.of(2024, 6, 12), taskCategory, calendar, taskGroup)); - given(calendarQueryService.searchByCalendarId(eq(calendarId))).willReturn(calendar); given(taskRepository.findAllByCalendarIdAndDate(calendarId, date)).willReturn(tasks); List found = taskQueryService.searchAllByDate(calendarId, date, memberId); @@ -85,7 +84,6 @@ void searchByIdAndCalendarIdWithNoRepeat() { Task task = spy(TaskFixture.DEFAULT.get(taskCategory, calendar, taskGroup)); given(task.getId()).willReturn(taskId); - given(calendarQueryService.searchByCalendarId(eq(calendarId))).willReturn(calendar); given(taskRepository.findByIdAndCalendarId(eq(taskId), eq(calendarId))) .willReturn(Optional.of(task)); @@ -117,7 +115,6 @@ void searchByIdAndCalendarIdWithRepeat() { Task task = spy(TaskFixture.DEFAULT.get(taskCategory, calendar, taskGroup)); given(task.getId()).willReturn(taskId); - given(calendarQueryService.searchByCalendarId(eq(calendarId))).willReturn(calendar); given(taskRepository.findByIdAndCalendarId(eq(taskId), eq(calendarId))) .willReturn(Optional.of(task)); @@ -160,7 +157,6 @@ void searchByIdAndCalendarIdWithRelatedGoals() { Task task = spy(TaskFixture.DEFAULT.get(taskCategory, calendar, taskGroup)); given(task.getId()).willReturn(taskId); - given(calendarQueryService.searchByCalendarId(eq(calendarId))).willReturn(calendar); given(taskRepository.findByIdAndCalendarId(eq(taskId), eq(calendarId))) .willReturn(Optional.of(task)); @@ -191,7 +187,6 @@ void throwRecordNotFoundExceptionIfTaskNotExists() { Long calendarId = 2L; Long taskId = 3L; - given(calendarQueryService.searchByCalendarId(eq(calendarId))).willReturn(calendar); given(taskRepository.findByIdAndCalendarId(anyLong(), anyLong())) .willThrow(new RecordNotFoundException(ErrorCode.TASK_NOT_FOUND));