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 b7364ea7..c4be13b3 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 @@ -3,6 +3,7 @@ import endolphin.backend.domain.personal_event.dto.PersonalEventRequest; import endolphin.backend.domain.personal_event.dto.PersonalEventResponse; import endolphin.backend.domain.personal_event.dto.SyncResponse; +import endolphin.backend.domain.personal_event.entity.PersonalEvent; import endolphin.backend.domain.user.UserService; import endolphin.backend.domain.user.entity.User; import endolphin.backend.global.dto.ListResponse; @@ -81,7 +82,8 @@ public ResponseEntity> getPersonalEvents( @PostMapping public ResponseEntity createPersonalEvent( @Valid @RequestBody PersonalEventRequest request) { - PersonalEventResponse response = personalEventService.createWithRequest(request); + PersonalEvent personalEvent = personalEventService.createWithRequest(request); + PersonalEventResponse response = PersonalEventResponse.fromEntity(personalEvent); URI location = URIUtil.buildResourceUri(response.id()); return ResponseEntity.created(location).body(response); } @@ -130,7 +132,8 @@ public ResponseEntity deletePersonalEvent( @Operation(summary = "개인 일정 동기화", description = "개인 일정을 실시간으로 동기화합니다.") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "일정 동기화 성공"), + @ApiResponse(responseCode = "200", description = "일정 동기화 성공", + content = @Content(schema = @Schema(implementation = SyncResponse.class))), @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), @ApiResponse(responseCode = "500", description = "서버 오류", 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 965da432..9df0a3ec 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 @@ -6,6 +6,8 @@ import endolphin.backend.domain.personal_event.dto.PersonalEventRequest; import endolphin.backend.domain.personal_event.dto.PersonalEventResponse; import endolphin.backend.domain.personal_event.dto.PersonalEventWithStatus; +import endolphin.backend.domain.personal_event.dto.SyncPersonalEvent; +import endolphin.backend.domain.personal_event.dto.SyncPersonalEvent.Status; import endolphin.backend.domain.personal_event.entity.PersonalEvent; import endolphin.backend.domain.personal_event.enums.PersonalEventStatus; import endolphin.backend.domain.personal_event.event.DeletePersonalEvent; @@ -30,6 +32,7 @@ import java.util.List; import java.util.Map; +import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -63,7 +66,7 @@ public ListResponse listPersonalEvents( return new ListResponse<>(personalEventResponseList); } - public PersonalEventResponse createWithRequest(PersonalEventRequest request) { + public PersonalEvent createWithRequest(PersonalEventRequest request) { User user = userService.getCurrentUser(); Validator.validateDateTimeRange(request.startDateTime(), request.endDateTime()); @@ -93,7 +96,7 @@ public PersonalEventResponse createWithRequest(PersonalEventRequest request) { true); }); - return PersonalEventResponse.fromEntity(result); + return result; } public void createPersonalEventsForParticipants(List participants, @@ -171,8 +174,8 @@ private PersonalEvent updatePersonalEvent(PersonalEventRequest request, if (isDateTimeChanged(personalEvent, request)) { discussions.forEach(discussion -> { - personalEventPreprocessor. - preprocessOne(oldEvent, discussion, user, false); + personalEventPreprocessor + .preprocessOne(oldEvent, discussion, user, false); personalEventPreprocessor .preprocessOne(personalEvent, discussion, user, true); }); @@ -219,19 +222,55 @@ public void preprocessPersonalEvents(User user, Discussion discussion) { personalEventPreprocessor.preprocess(personalEvents, discussion, user); } - public void syncWithGoogleEvents(List googleEvents, User user, + public List syncWithGoogleEvents(List googleEvents, User user, String googleCalendarId) { List discussions = discussionParticipantService.getDiscussionsByUserId( user.getId()); + + List syncEvents = new ArrayList<>(); + for (GoogleEvent googleEvent : googleEvents) { log.info("Processing Google event: {}", googleEvent); - switch (googleEvent.status()) { - case CONFIRMED -> upsertPersonalEventByGoogleEvent( - googleEvent, discussions, user, googleCalendarId); - case CANCELLED -> deletePersonalEventByGoogleEvent( + Optional opt = + syncPersonalEventFromGoogleEvent(user, googleCalendarId, googleEvent, discussions); + + opt.ifPresent(syncEvents::add); + } + + return syncEvents; + } + + private Optional syncPersonalEventFromGoogleEvent( + User user, String googleCalendarId, GoogleEvent googleEvent, List discussions) { + + return switch (googleEvent.status()) { + case CONFIRMED -> { + Optional personalEventOpt = personalEventRepository + .findByGoogleEventIdAndCalendarId(googleEvent.eventId(), googleCalendarId); + + if (personalEventOpt.isPresent()) { + PersonalEvent personalEvent = personalEventOpt.get(); + PersonalEvent updatedPersonalEvent = updatePersonalEvent( + PersonalEventRequest.of(googleEvent, personalEvent.getIsAdjustable()), + personalEvent, user, discussions); + + yield Optional.of(SyncPersonalEvent.from(updatedPersonalEvent, Status.UPDATED)); + } + + PersonalEvent personalEvent = + createWithRequest(PersonalEventRequest.of(googleEvent, false)); + + yield Optional.of(SyncPersonalEvent.from(personalEvent, Status.CREATED)); + } + case CANCELLED -> { + Optional opt = deletePersonalEventByGoogleEvent( googleEvent, discussions, user, googleCalendarId); + + yield opt.map( + personalEvent -> SyncPersonalEvent.from(personalEvent, Status.DELETED)); } - } + case TENTATIVE -> Optional.empty(); + }; } private void validatePersonalEventUser(PersonalEvent personalEvent, User user) { @@ -240,37 +279,15 @@ private void validatePersonalEventUser(PersonalEvent personalEvent, User user) { } } - private void upsertPersonalEventByGoogleEvent( - GoogleEvent googleEvent, List discussions, User user, String googleCalendarId) { - log.info("Upserting personal event by Google event: {}", googleEvent); - - personalEventRepository - .findByGoogleEventIdAndCalendarId(googleEvent.eventId(), googleCalendarId) - .ifPresentOrElse(personalEvent -> { - updatePersonalEvent( - PersonalEventRequest.of(googleEvent, personalEvent.getIsAdjustable()), - personalEvent, user, discussions); - }, - () -> { - PersonalEvent personalEvent = - PersonalEvent.fromGoogleEvent(googleEvent, user, googleCalendarId); - personalEventRepository.save(personalEvent); - // 비트맵 수정 - discussions.forEach(discussion -> { - personalEventPreprocessor.preprocessOne(personalEvent, discussion, user, - true); - }); - }); - } - - private void deletePersonalEventByGoogleEvent(GoogleEvent googleEvent, + private Optional deletePersonalEventByGoogleEvent(GoogleEvent googleEvent, List discussions, User user, String googleCalendarId) { log.info("Deleting personal event by Google event: {}", googleEvent); - personalEventRepository - .findByGoogleEventIdAndCalendarId(googleEvent.eventId(), googleCalendarId) - .ifPresent(personalEvent -> { - deletePersonalEvent(personalEvent, user, discussions); - }); + Optional opt = personalEventRepository + .findByGoogleEventIdAndCalendarId(googleEvent.eventId(), googleCalendarId); + + opt.ifPresent(personalEvent -> deletePersonalEvent(personalEvent, user, discussions)); + + return opt; } private boolean isDateTimeChanged(PersonalEvent personalEvent, PersonalEventRequest newEvent) { diff --git a/backend/src/main/java/endolphin/backend/domain/personal_event/dto/PersonalEventResponse.java b/backend/src/main/java/endolphin/backend/domain/personal_event/dto/PersonalEventResponse.java index ce715ea2..292d5c0e 100644 --- a/backend/src/main/java/endolphin/backend/domain/personal_event/dto/PersonalEventResponse.java +++ b/backend/src/main/java/endolphin/backend/domain/personal_event/dto/PersonalEventResponse.java @@ -9,8 +9,7 @@ public record PersonalEventResponse( LocalDateTime startDateTime, LocalDateTime endDateTime, Boolean isAdjustable, - String calendarId, - String googleEventId + String calendarId ) { public static PersonalEventResponse fromEntity(PersonalEvent event) { @@ -20,8 +19,7 @@ public static PersonalEventResponse fromEntity(PersonalEvent event) { event.getStartTime(), event.getEndTime(), event.getIsAdjustable(), - event.getCalendarId(), - event.getGoogleEventId() + event.getCalendarId() ); } } diff --git a/backend/src/main/java/endolphin/backend/domain/personal_event/dto/SyncPersonalEvent.java b/backend/src/main/java/endolphin/backend/domain/personal_event/dto/SyncPersonalEvent.java index 382b9b9e..e94b6c64 100644 --- a/backend/src/main/java/endolphin/backend/domain/personal_event/dto/SyncPersonalEvent.java +++ b/backend/src/main/java/endolphin/backend/domain/personal_event/dto/SyncPersonalEvent.java @@ -1,17 +1,26 @@ package endolphin.backend.domain.personal_event.dto; +import endolphin.backend.domain.personal_event.entity.PersonalEvent; import endolphin.backend.global.google.dto.GoogleEvent; +import endolphin.backend.global.google.enums.GoogleEventStatus; import java.time.LocalDateTime; public record SyncPersonalEvent( - String googleEventId, + Long id, + Boolean isAdjustable, + String calendarId, String title, LocalDateTime startDateTime, LocalDateTime endDateTime, - String status + Status status ) { - public static SyncPersonalEvent from(GoogleEvent event) { - return new SyncPersonalEvent(event.eventId(), event.summary(), event.startDateTime(), - event.endDateTime(), event.status().getValue()); + + public static SyncPersonalEvent from(PersonalEvent event, Status status) { + return new SyncPersonalEvent(event.getId(), event.getIsAdjustable(), event.getCalendarId(), + event.getTitle(), event.getStartTime(), event.getEndTime(), status); + } + + public enum Status { + CREATED, UPDATED, DELETED } } diff --git a/backend/src/main/java/endolphin/backend/domain/personal_event/entity/PersonalEvent.java b/backend/src/main/java/endolphin/backend/domain/personal_event/entity/PersonalEvent.java index abd0ee2f..0670e4cb 100644 --- a/backend/src/main/java/endolphin/backend/domain/personal_event/entity/PersonalEvent.java +++ b/backend/src/main/java/endolphin/backend/domain/personal_event/entity/PersonalEvent.java @@ -87,17 +87,4 @@ public PersonalEvent copy() { .user(this.user) .build(); } - - public static PersonalEvent fromGoogleEvent(GoogleEvent googleEvent, User user, - String googleCalenderId) { - return PersonalEvent.builder() - .title(googleEvent.summary()) - .startTime(googleEvent.startDateTime()) - .endTime(googleEvent.endDateTime()) - .googleEventId(googleEvent.eventId()) - .isAdjustable(false) - .calendarId(googleCalenderId) - .user(user) - .build(); - } } diff --git a/backend/src/main/java/endolphin/backend/domain/personal_event/event/handler/PersonalEventHandler.java b/backend/src/main/java/endolphin/backend/domain/personal_event/event/handler/PersonalEventHandler.java index 2e44566d..b6cdde24 100644 --- a/backend/src/main/java/endolphin/backend/domain/personal_event/event/handler/PersonalEventHandler.java +++ b/backend/src/main/java/endolphin/backend/domain/personal_event/event/handler/PersonalEventHandler.java @@ -26,16 +26,19 @@ public void sync(GoogleEventChanged event) { log.info("Syncing personal events with Google events"); try { List events = event.events(); + String googleCalendarId = event.googleCalendarId(); User user = event.user(); + log.info("Syncing personal events for user {}", user.getId()); - String googleCalendarId = event.googleCalendarId(); - personalEventService.syncWithGoogleEvents(events, user, googleCalendarId); + + List syncPersonalEvents = + personalEventService.syncWithGoogleEvents(events, user, googleCalendarId); if (deferredResultManager.hasActiveConnection(user)) { - List syncPersonalEvents = - events.stream().map(SyncPersonalEvent::from).toList(); deferredResultManager.setResult(user, SyncResponse.sync(syncPersonalEvents)); } + + } catch (Exception e) { log.error("Failed to sync personal events", e); } 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 a02d19c0..b2840fbd 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 @@ -6,6 +6,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import endolphin.backend.domain.personal_event.dto.PersonalEventResponse; +import endolphin.backend.domain.personal_event.entity.PersonalEvent; import java.time.LocalDateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -13,6 +14,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -47,11 +49,14 @@ void createPersonalEvent() throws Exception { "isAdjustable": false } """; + 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", "testGoogleEventId"); - given(personalEventService.createWithRequest(any())).willReturn(personalEventResponse); + false, "testCalendarId"); + given(personalEventService.createWithRequest(any())).willReturn(personalEvent); + MvcResult result = mockMvc.perform(post("/api/v1/personal-event"). contentType(MediaType.APPLICATION_JSON) .content(requestJson)) 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 3f1e9034..6913f7b2 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 - PersonalEventResponse response = personalEventService.createWithRequest(request); + PersonalEvent response = personalEventService.createWithRequest(request); // then assertThat(response).isNotNull(); - assertThat(response.title()).isEqualTo(request.title()); - assertThat(response.startDateTime()).isEqualTo(startTime); + assertThat(response.getTitle()).isEqualTo(request.title()); + assertThat(response.getStartTime()).isEqualTo(startTime); verify(userService, times(1)).getCurrentUser(); verify(personalEventRepository, times(1)).save(any(PersonalEvent.class)); @@ -233,14 +233,20 @@ public void testUpdateWithRequestByGoogleSync_Success() { PersonalEvent existingEvent = createWithRequest("new Title"); given(existingEvent.getStartTime()).willReturn(LocalDateTime.of(2024, 3, 10, 10, 0)); -// given(existingEvent.getEndTime()).willReturn(LocalDateTime.of(2024, 3, 10, 12, 0)); PersonalEvent oldExistingEvent = createWithRequest("Old Title"); given(existingEvent.copy()).willReturn(oldExistingEvent); + PersonalEvent updatedPersonalEvent = createWithRequest("new Title"); + given(updatedPersonalEvent.getId()).willReturn(1L); + given(updatedPersonalEvent.getTitle()).willReturn("new Title"); + given(updatedPersonalEvent.getStartTime()).willReturn(LocalDateTime.of(2024, 3, 10, 10, 0)); + given(updatedPersonalEvent.getEndTime()).willReturn(LocalDateTime.of(2024, 3, 10, 12, 0)); + given(updatedPersonalEvent.getCalendarId()).willReturn("testCalendarId"); + given(updatedPersonalEvent.getIsAdjustable()).willReturn(false); + given(personalEventRepository.save(any())).willReturn(updatedPersonalEvent); + PersonalEvent existingEvent2 = createWithRequest("Old Title2"); -// given(existingEvent2.getStartTime()).willReturn(LocalDateTime.of(2024, 5, 10, 7, 0)); -// given(existingEvent2.getEndTime()).willReturn(LocalDateTime.of(2024, 5, 10, 12, 0)); given(personalEventRepository.findByGoogleEventIdAndCalendarId(eq(updatedGoogleEvent.eventId()), eq(googleCalendarId))) .willReturn(Optional.of(existingEvent));