Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ public ResponseEntity<Void> 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 = "서버 오류",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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.entity.PersonalEvent;
import endolphin.backend.domain.personal_event.enums.PersonalEventStatus;
import endolphin.backend.domain.personal_event.event.DeletePersonalEvent;
Expand All @@ -30,6 +31,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;
Expand Down Expand Up @@ -219,19 +221,38 @@ public void preprocessPersonalEvents(User user, Discussion discussion) {
personalEventPreprocessor.preprocess(personalEvents, discussion, user);
}

public void syncWithGoogleEvents(List<GoogleEvent> googleEvents, User user,
public List<SyncPersonalEvent> syncWithGoogleEvents(List<GoogleEvent> googleEvents, User user,
String googleCalendarId) {
List<Discussion> discussions = discussionParticipantService.getDiscussionsByUserId(
user.getId());

List<SyncPersonalEvent> syncEvents = new ArrayList<>();

for (GoogleEvent googleEvent : googleEvents) {
log.info("Processing Google event: {}", googleEvent);
switch (googleEvent.status()) {
case CONFIRMED -> upsertPersonalEventByGoogleEvent(
SyncPersonalEvent syncPersonalEvent =
syncPersonalEventFromGoogleEvent(user, googleCalendarId, googleEvent, discussions);
syncEvents.add(syncPersonalEvent);
}

return syncEvents;
}

private SyncPersonalEvent syncPersonalEventFromGoogleEvent(User user, String googleCalendarId,
GoogleEvent googleEvent, List<Discussion> discussions) {
return switch (googleEvent.status()) {
case CONFIRMED -> {
PersonalEvent personalEvent = upsertPersonalEventByGoogleEvent(
googleEvent, discussions, user, googleCalendarId);
case CANCELLED -> deletePersonalEventByGoogleEvent(
yield SyncPersonalEvent.from(personalEvent, googleEvent);
}
case CANCELLED -> {
deletePersonalEventByGoogleEvent(
googleEvent, discussions, user, googleCalendarId);
yield SyncPersonalEvent.from(googleEvent);
}
}
case TENTATIVE -> SyncPersonalEvent.from(googleEvent);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tentative를 클라이언트에 주기로한건가요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

db 거치는데 confirmed를 그대로 주는 이유가 있을까요?
update와 create 분기하는 건 어떻게 생각하시나요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • tentative는 제외하도록 하겠습니다!
  • confirmed를 주는 건 cancelled와 구분하기 위해 필요할 거 같습니다!
  • update와 create를 upsertPersonalEventByGoogleEvent에서 분기하고 있는데 혹시 어떤 걸 의미하는지 궁금합니다!

Copy link
Collaborator

@efdao efdao May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프론트에서 confirmed를 받고 어떤 동작을 수행하고 있는지는 정확히 모르지만,
백에서 내려주는 응답을 생성, 변경, 삭제 세가지의 상태를 정의해서 주면 더 편한 부분이 있지 않을까 하는 제 생각이었습니다.

백엔드 로직에서도 구글로부터 confirmed를 받으면 해당 id의 일정이 존재하는지 확인 후 update, create로 분기를 하는데요, 프론트도 동일한 방법을 수행해야 한다면 효율성이 떨어지지 않을까 해서요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

백에서 수행한 로직을 프론트에서도 수행하면 비효율적이긴 하겠네요!
어떤 것이 편한 지 프론트와 협의 후 수정하겠습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프론트와 협의한 결과, 백엔드에서 생성, 변경, 삭제를 구분해서 응답하는 걸로 수정하였습니다.!

};
}

private void validatePersonalEventUser(PersonalEvent personalEvent, User user) {
Expand All @@ -240,27 +261,30 @@ private void validatePersonalEventUser(PersonalEvent personalEvent, User user) {
}
}

private void upsertPersonalEventByGoogleEvent(
private PersonalEvent upsertPersonalEventByGoogleEvent(
GoogleEvent googleEvent, List<Discussion> discussions, User user, String googleCalendarId) {
log.info("Upserting personal event by Google event: {}", googleEvent);
Optional<PersonalEvent> personalEventOpt =
personalEventRepository
.findByGoogleEventIdAndCalendarId(googleEvent.eventId(), googleCalendarId);

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);
});
});
if (personalEventOpt.isPresent()) {
PersonalEvent personalEvent = personalEventOpt.get();
updatePersonalEvent(
PersonalEventRequest.of(googleEvent, personalEvent.getIsAdjustable()),
personalEvent, user, discussions);

return personalEvent;
}

PersonalEvent personalEvent =
PersonalEvent.fromGoogleEvent(googleEvent, user, googleCalendarId);
personalEventRepository.save(personalEvent);
// 비트맵 수정
discussions.forEach(discussion ->
personalEventPreprocessor.preprocessOne(personalEvent, discussion, user, true));

return personalEvent;
}

private void deletePersonalEventByGoogleEvent(GoogleEvent googleEvent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
package endolphin.backend.domain.personal_event.dto;

import endolphin.backend.domain.personal_event.entity.PersonalEvent;
import endolphin.backend.global.google.dto.GoogleEvent;
import java.time.LocalDateTime;

public record SyncPersonalEvent(
Long id,
Boolean isAdjustable,
String calendarId,
String googleEventId,
String title,
LocalDateTime startDateTime,
LocalDateTime endDateTime,
String status
) {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just ask; status를 enum 말고 String으로 했던 이유가 있었던가요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

db를 거쳐도 googleEventId 응답에 포함해야 하나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 가독성을 위해 enum으로 수정하겠습니다!
  • 이전 업데이트 때 일정들을 googleEventId로 구분하도록 프론트에서 반영했을거라 googleEventId를 응답에서 빼는 건 프론트와 협의가 필요할 거 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프론트와 협의한 결과, googleEventId를 응답에서 삭제하기로 결정하였습니다.!

public static SyncPersonalEvent from(GoogleEvent event) {
return new SyncPersonalEvent(event.eventId(), event.summary(), event.startDateTime(),
event.endDateTime(), event.status().getValue());
return new SyncPersonalEvent(null, null, null,
event.eventId(), event.summary(), event.startDateTime(),
event.endDateTime(), event.status().getValue());
}

public static SyncPersonalEvent from(PersonalEvent event, GoogleEvent googleEvent) {
return new SyncPersonalEvent(event.getId(), event.getIsAdjustable(), event.getCalendarId(),
googleEvent.eventId(), googleEvent.summary(), googleEvent.startDateTime(),
googleEvent.endDateTime(), googleEvent.status().getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ public void sync(GoogleEventChanged event) {
log.info("Syncing personal events with Google events");
try {
List<GoogleEvent> 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<SyncPersonalEvent> syncPersonalEvents =
personalEventService.syncWithGoogleEvents(events, user, googleCalendarId);

if (deferredResultManager.hasActiveConnection(user)) {
List<SyncPersonalEvent> syncPersonalEvents =
events.stream().map(SyncPersonalEvent::from).toList();
deferredResultManager.setResult(user, SyncResponse.sync(syncPersonalEvents));
}


} catch (Exception e) {
log.error("Failed to sync personal events", e);
}
Expand Down
Loading