diff --git a/backend/src/main/java/endolphin/backend/domain/calendar/CalendarController.java b/backend/src/main/java/endolphin/backend/domain/calendar/CalendarController.java index ccda083a..4c6ad072 100644 --- a/backend/src/main/java/endolphin/backend/domain/calendar/CalendarController.java +++ b/backend/src/main/java/endolphin/backend/domain/calendar/CalendarController.java @@ -23,7 +23,7 @@ @RequestMapping("/api/v1") public class CalendarController { - private final CalendarService calendarService; + private final CalendarFacade calendarFacade; @Value("${app.frontend.url}") private String frontendUrl; @@ -38,7 +38,7 @@ public void subscribeGoogleCalendar(@RequestParam(value = "code", required = fal if (error != null) { log.error("[subscribeGoogleCalendar] error: {}", error); } - calendarService.linkGoogleCalender(code); + calendarFacade.linkGoogleCalendar(code); String redirectUrl = UriComponentsBuilder.fromUriString(frontendUrl) .path(frontendCallback) @@ -49,7 +49,7 @@ public void subscribeGoogleCalendar(@RequestParam(value = "code", required = fal @GetMapping("/calendar/list") public ResponseEntity> calendarList() { - ListResponse response = calendarService.getCalendarList(); + ListResponse response = calendarFacade.getCalendarList(); return ResponseEntity.ok(response); } } diff --git a/backend/src/main/java/endolphin/backend/domain/calendar/CalendarFacade.java b/backend/src/main/java/endolphin/backend/domain/calendar/CalendarFacade.java new file mode 100644 index 00000000..86570484 --- /dev/null +++ b/backend/src/main/java/endolphin/backend/domain/calendar/CalendarFacade.java @@ -0,0 +1,56 @@ +package endolphin.backend.domain.calendar; + +import endolphin.backend.domain.calendar.dto.CalendarResponse; +import endolphin.backend.domain.calendar.entity.Calendar; +import endolphin.backend.domain.calendar.event.GoogleCalendarLinkEvent; +import endolphin.backend.domain.user.UserService; +import endolphin.backend.domain.user.entity.User; +import endolphin.backend.global.dto.ListResponse; +import endolphin.backend.global.google.GoogleCalendarService; +import endolphin.backend.global.google.GoogleOAuthService; +import endolphin.backend.global.google.dto.GoogleCalendarDto; +import endolphin.backend.global.google.dto.GoogleTokens; +import endolphin.backend.global.google.dto.GoogleUserInfo; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CalendarFacade { + private final CalendarService calendarService; + private final UserService userService; + + private final GoogleCalendarService googleCalendarService; + private final GoogleOAuthService googleOAuthService; + + private final ApplicationEventPublisher eventPublisher; + + public void linkGoogleCalendar(String code) { + if (code == null || code.isBlank()) { + return; + } + GoogleTokens tokens = googleOAuthService.getGoogleTokens(code, false); + GoogleUserInfo userInfo = googleOAuthService.getUserInfo(tokens.accessToken()); + + + User user = userService.getUserByEmail(userInfo.email()); + user = userService.updateTokens(user, tokens.accessToken(), tokens.refreshToken()); + + GoogleCalendarDto calendarDto = googleCalendarService.getPrimaryCalendar(user); + calendarService.attachGoogleCalendar(calendarDto, user); + + eventPublisher.publishEvent(new GoogleCalendarLinkEvent(user)); + } + + public ListResponse getCalendarList() { + User user = userService.getCurrentUser(); + + List calendars = calendarService.findAllByUserId(user.getId()); + + List response = calendars.stream() + .map(c -> new CalendarResponse(c.getName())).toList(); + return new ListResponse<>(response); + } +} diff --git a/backend/src/main/java/endolphin/backend/domain/calendar/CalendarService.java b/backend/src/main/java/endolphin/backend/domain/calendar/CalendarService.java index 8eb2a174..af00ce0e 100644 --- a/backend/src/main/java/endolphin/backend/domain/calendar/CalendarService.java +++ b/backend/src/main/java/endolphin/backend/domain/calendar/CalendarService.java @@ -1,29 +1,20 @@ package endolphin.backend.domain.calendar; -import endolphin.backend.domain.calendar.dto.CalendarResponse; import endolphin.backend.domain.calendar.entity.Calendar; -import endolphin.backend.domain.calendar.event.GoogleCalendarLinkEvent; -import endolphin.backend.domain.user.UserService; import endolphin.backend.domain.user.entity.User; -import endolphin.backend.global.dto.ListResponse; import endolphin.backend.global.error.exception.CalendarException; import endolphin.backend.global.error.exception.ErrorCode; -import endolphin.backend.global.google.GoogleOAuthService; import endolphin.backend.global.google.dto.GoogleCalendarDto; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import endolphin.backend.global.google.dto.GoogleTokens; -import endolphin.backend.global.google.dto.GoogleUserInfo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -34,17 +25,14 @@ public class CalendarService { private final CalendarRepository calendarRepository; - private final GoogleOAuthService googleOAuthService; - private final UserService userService; - private final ApplicationEventPublisher eventPublisher; - public Calendar attachGoogleCalendar(GoogleCalendarDto googleCalendarDto, User user) { + public void attachGoogleCalendar(GoogleCalendarDto googleCalendarDto, User user) { Calendar calendar = Calendar.builder() .user(user) .calendarId(googleCalendarDto.id()) .name(googleCalendarDto.summary()) .description(googleCalendarDto.description()).build(); - return calendarRepository.save(calendar); + calendarRepository.save(calendar); } @Transactional(readOnly = true) @@ -106,30 +94,8 @@ public Map getCalendarIdByUsers(List userIds) { )); } - public void linkGoogleCalender(String code) { - if (code == null || code.isBlank()) { - return; - } - GoogleTokens tokens = googleOAuthService.getGoogleTokens(code, false); - GoogleUserInfo userInfo = googleOAuthService.getUserInfo(tokens.accessToken()); - - User user = userService.getUserByEmail(userInfo.email()); - if (user.getAccessToken() != null) { - return; - } - user.setAccessToken(tokens.accessToken()); - user.setRefreshToken(tokens.refreshToken()); - - eventPublisher.publishEvent(new GoogleCalendarLinkEvent(user)); - } - - public ListResponse getCalendarList() { - User user = userService.getCurrentUser(); - - List calendars = calendarRepository.findAllByUserId(user.getId()); - - List response = calendars.stream() - .map(c -> new CalendarResponse(c.getName())).toList(); - return new ListResponse<>(response); + @Transactional(readOnly = true) + public List findAllByUserId(Long userId) { + return calendarRepository.findAllByUserId(userId); } } diff --git a/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventController.java b/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventController.java index c4be13b3..ab9de0f4 100644 --- a/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventController.java +++ b/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventController.java @@ -82,8 +82,7 @@ public ResponseEntity> getPersonalEvents( @PostMapping public ResponseEntity createPersonalEvent( @Valid @RequestBody PersonalEventRequest request) { - PersonalEvent personalEvent = personalEventService.createWithRequest(request); - PersonalEventResponse response = PersonalEventResponse.fromEntity(personalEvent); + PersonalEventResponse response = personalEventService.createWithRequest(request); URI location = URIUtil.buildResourceUri(response.id()); return ResponseEntity.created(location).body(response); } diff --git a/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventService.java b/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventService.java index 9df0a3ec..0223c7d9 100644 --- a/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventService.java +++ b/backend/src/main/java/endolphin/backend/domain/personal_event/PersonalEventService.java @@ -66,9 +66,26 @@ public ListResponse listPersonalEvents( return new ListResponse<>(personalEventResponseList); } - public PersonalEvent createWithRequest(PersonalEventRequest request) { + public PersonalEventResponse createWithRequest(PersonalEventRequest request) { User user = userService.getCurrentUser(); + List discussions = discussionParticipantService + .getDiscussionsByUserId(user.getId()); + + PersonalEvent personalEvent = createPersonalEvent(request, user, discussions); + + if (request.syncWithGoogleCalendar()) { + String calendarId = calendarService.getCalendarIdByUser(user); + personalEvent.setGoogleEventId(IdGenerator.generateId(user.getId())); + personalEvent.setCalendarId(calendarId); + eventPublisher.publishEvent(new InsertPersonalEvent(List.of(personalEvent))); + } + + return PersonalEventResponse.fromEntity(personalEvent); + } + + private PersonalEvent createPersonalEvent(PersonalEventRequest request, User user, + List discussions) { Validator.validateDateTimeRange(request.startDateTime(), request.endDateTime()); PersonalEvent personalEvent = PersonalEvent.builder() @@ -79,29 +96,17 @@ public PersonalEvent createWithRequest(PersonalEventRequest request) { .user(user) .build(); - PersonalEvent result = personalEventRepository.save(personalEvent); - - if (request.syncWithGoogleCalendar()) { - String calendarId = calendarService.getCalendarIdByUser(user); - personalEvent.setGoogleEventId(IdGenerator.generateId(user.getId())); - personalEvent.setCalendarId(calendarId); - eventPublisher.publishEvent(new InsertPersonalEvent(List.of(personalEvent))); - } - - List discussions = discussionParticipantService.getDiscussionsByUserId( - user.getId()); + PersonalEvent savedPersonalEvent = personalEventRepository.save(personalEvent); discussions.forEach(discussion -> { - personalEventPreprocessor.preprocessOne(result, discussion, user, - true); + personalEventPreprocessor.preprocessOne(savedPersonalEvent, discussion, user, true); }); - return result; + return savedPersonalEvent; } public void createPersonalEventsForParticipants(List participants, - Discussion discussion, - SharedEventDto sharedEvent) { + Discussion discussion, SharedEventDto sharedEvent) { List userIds = participants.stream().map(User::getId).toList(); Map calendarIdMap = calendarService.getCalendarIdByUsers(userIds); List events = participants.stream() @@ -251,14 +256,14 @@ private Optional syncPersonalEventFromGoogleEvent( if (personalEventOpt.isPresent()) { PersonalEvent personalEvent = personalEventOpt.get(); PersonalEvent updatedPersonalEvent = updatePersonalEvent( - PersonalEventRequest.of(googleEvent, personalEvent.getIsAdjustable()), - personalEvent, user, discussions); + PersonalEventRequest.of(googleEvent, personalEvent.getIsAdjustable()), + personalEvent, user, discussions); yield Optional.of(SyncPersonalEvent.from(updatedPersonalEvent, Status.UPDATED)); } - PersonalEvent personalEvent = - createWithRequest(PersonalEventRequest.of(googleEvent, false)); + PersonalEvent personalEvent = createPersonalEvent( + PersonalEventRequest.of(googleEvent, false), user, discussions); yield Optional.of(SyncPersonalEvent.from(personalEvent, Status.CREATED)); } diff --git a/backend/src/main/java/endolphin/backend/domain/user/UserService.java b/backend/src/main/java/endolphin/backend/domain/user/UserService.java index 1b049e57..ef898f42 100644 --- a/backend/src/main/java/endolphin/backend/domain/user/UserService.java +++ b/backend/src/main/java/endolphin/backend/domain/user/UserService.java @@ -44,6 +44,16 @@ public void updateAccessToken(User user, String accessToken) { userRepository.save(user); } + public User updateTokens(User user, String accessToken, String refreshToken) { + if (user.getAccessToken() != null) { + return user; + } + user.setAccessToken(accessToken); + user.setRefreshToken(refreshToken); + + return userRepository.save(user); + } + public LoginUserDto upsertUser(GoogleUserInfo userInfo) { Optional userOpt = userRepository.findByEmail(userInfo.email()); Boolean isFirstLogin = userOpt.isEmpty(); diff --git a/backend/src/main/java/endolphin/backend/global/google/GoogleCalendarService.java b/backend/src/main/java/endolphin/backend/global/google/GoogleCalendarService.java index 468a28f0..4dd951fc 100644 --- a/backend/src/main/java/endolphin/backend/global/google/GoogleCalendarService.java +++ b/backend/src/main/java/endolphin/backend/global/google/GoogleCalendarService.java @@ -34,14 +34,12 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.UUID; @Service @RequiredArgsConstructor -@Transactional @Slf4j public class GoogleCalendarService { @@ -55,13 +53,6 @@ public class GoogleCalendarService { private final RetryExecutor retryExecutor; private final ApplicationEventPublisher eventPublisher; - public void upsertGoogleCalendar(User user) { - GoogleCalendarDto googleCalendarDto = getPrimaryCalendar(user); - Calendar calendar = calendarService.attachGoogleCalendar(googleCalendarDto, user); - syncWithCalendar(calendar.getCalendarId(), user); - subscribeToCalendar(calendar, user); - } - public void subscribeGoogleCalendar(User user) { Calendar calendar = calendarService.getCalendarByUserId(user.getId()); subscribeToCalendar(calendar, user); @@ -146,8 +137,8 @@ public GoogleCalendarDto getPrimaryCalendar(User user) { return retryExecutor.executeCalendarApiWithRetry( () -> { - GoogleCalendarDto result = googleCalendarApi.getPrimaryCalendar( - eventsUrl, user.getAccessToken()); + GoogleCalendarDto result = + googleCalendarApi.getPrimaryCalendar(eventsUrl, user.getAccessToken()); log.info("[getPrimaryCalendar] success: {}", result); return result; }, user, null diff --git a/backend/src/main/java/endolphin/backend/global/google/event/handler/GoogleEventHandler.java b/backend/src/main/java/endolphin/backend/global/google/event/handler/GoogleEventHandler.java index f5b62676..6436ebf1 100644 --- a/backend/src/main/java/endolphin/backend/global/google/event/handler/GoogleEventHandler.java +++ b/backend/src/main/java/endolphin/backend/global/google/event/handler/GoogleEventHandler.java @@ -1,6 +1,5 @@ package endolphin.backend.global.google.event.handler; -import endolphin.backend.domain.calendar.entity.Calendar; import endolphin.backend.domain.calendar.event.GoogleCalendarLinkEvent; import endolphin.backend.domain.personal_event.entity.PersonalEvent; import endolphin.backend.domain.personal_event.event.DeletePersonalEvent; @@ -11,6 +10,7 @@ import endolphin.backend.domain.personal_event.event.InsertPersonalEvent; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionalEventListener; @@ -30,15 +30,15 @@ public void insert(InsertPersonalEvent event) { } @Async - @TransactionalEventListener(classes = {GoogleCalendarLinkEvent.class}) - public void link(GoogleCalendarLinkEvent event) { + @TransactionalEventListener(classes = {LoginEvent.class}) + public void link(LoginEvent event) { User user = event.user(); - googleCalendarService.upsertGoogleCalendar(user); + googleCalendarService.subscribeGoogleCalendar(user); } @Async - @TransactionalEventListener(classes = {LoginEvent.class}) - public void link(LoginEvent event) { + @EventListener(classes = {GoogleCalendarLinkEvent.class}) + public void link(GoogleCalendarLinkEvent event) { User user = event.user(); googleCalendarService.subscribeGoogleCalendar(user); } diff --git a/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventControllerTest.java b/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventControllerTest.java index b2840fbd..dc8e01c7 100644 --- a/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventControllerTest.java +++ b/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventControllerTest.java @@ -49,13 +49,13 @@ void createPersonalEvent() throws Exception { "isAdjustable": false } """; - PersonalEvent personalEvent = mock(PersonalEvent.class); - given(personalEvent.getId()).willReturn(1L); +// PersonalEvent personalEvent = mock(PersonalEvent.class); +// given(personalEvent.getId()).willReturn(1L); PersonalEventResponse personalEventResponse = new PersonalEventResponse(1L, "title", LocalDateTime.of(2025, 2, 2, 10, 0), LocalDateTime.of(2025, 2, 2, 12, 0), false, "testCalendarId"); - given(personalEventService.createWithRequest(any())).willReturn(personalEvent); + given(personalEventService.createWithRequest(any())).willReturn(personalEventResponse); MvcResult result = mockMvc.perform(post("/api/v1/personal-event"). contentType(MediaType.APPLICATION_JSON) diff --git a/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventServiceTest.java b/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventServiceTest.java index 6913f7b2..9b4eb286 100644 --- a/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventServiceTest.java +++ b/backend/src/test/java/endolphin/backend/domain/personal_event/PersonalEventServiceTest.java @@ -106,12 +106,12 @@ void testCreateWithRequest() { given(personalEventRepository.save(any(PersonalEvent.class))).willReturn(savedEvent); // when - PersonalEvent response = personalEventService.createWithRequest(request); + PersonalEventResponse response = personalEventService.createWithRequest(request); // then assertThat(response).isNotNull(); - assertThat(response.getTitle()).isEqualTo(request.title()); - assertThat(response.getStartTime()).isEqualTo(startTime); + assertThat(response.title()).isEqualTo(request.title()); + assertThat(response.startDateTime()).isEqualTo(startTime); verify(userService, times(1)).getCurrentUser(); verify(personalEventRepository, times(1)).save(any(PersonalEvent.class)); diff --git a/backend/src/test/java/endolphin/backend/global/google/GoogleCalendarServiceTest.java b/backend/src/test/java/endolphin/backend/global/google/GoogleCalendarServiceTest.java index 7121a856..370bcfd3 100644 --- a/backend/src/test/java/endolphin/backend/global/google/GoogleCalendarServiceTest.java +++ b/backend/src/test/java/endolphin/backend/global/google/GoogleCalendarServiceTest.java @@ -63,33 +63,6 @@ void upsertGoogleCalendar_existingCalendar_validExpiration() { then(retryExecutor).should(never()).executeCalendarApiWithRetry(any(), any(), anyString()); } - @Test - @DisplayName("사용자가 처음 연동하는 경우") - void upsertGoogleCalendar_noExistingCalendar() { - // Given - User user = Mockito.mock(User.class); - - GoogleCalendarDto googleCalendarDto = new GoogleCalendarDto("primary", "title", "test"); - - doReturn(googleCalendarDto).when(googleCalendarService).getPrimaryCalendar(user); - doNothing().when(googleCalendarService).syncWithCalendar(any(), any()); - doNothing().when(googleCalendarService).subscribeToCalendar(any(), any()); - - Calendar attachedCalendar = Mockito.mock(Calendar.class); - given(attachedCalendar.getCalendarId()).willReturn(googleCalendarDto.id()); - - given(calendarService.attachGoogleCalendar(googleCalendarDto, user)).willReturn(attachedCalendar); - - // When - googleCalendarService.upsertGoogleCalendar(user); - - // Then - then(googleCalendarService).should(times(1)).getPrimaryCalendar(user); - then(calendarService).should(times(1)).attachGoogleCalendar(googleCalendarDto, user); - then(googleCalendarService).should(times(1)).syncWithCalendar(googleCalendarDto.id(), user); - then(googleCalendarService).should(times(1)).subscribeToCalendar(attachedCalendar, user); - } - @Test @DisplayName("사용자에게 캘린더가 이미 존재하고 구독 채널이 만료된 경우") void upsertGoogleCalendar_existingCalendar_expired() {