diff --git a/src/main/java/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.java b/src/main/java/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.java index da2d0f6..bc197f3 100644 --- a/src/main/java/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.java +++ b/src/main/java/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.java @@ -10,6 +10,8 @@ import com.ject.studytrip.studylog.application.service.StudyLogDailyMissionCommandService; import com.ject.studytrip.trip.application.service.DailyGoalCommandService; import com.ject.studytrip.trip.application.service.TripCommandService; +import com.ject.studytrip.trip.application.service.TripReportCommandService; +import com.ject.studytrip.trip.application.service.TripReportStudyLogCommandService; import java.util.LinkedHashMap; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -29,6 +31,8 @@ public class HardDeleteFacade { private final StudyLogDailyMissionCommandService studyLogDailyMissionCommandService; private final DailyGoalCommandService dailyGoalCommandService; private final PomodoroCommandService pomodoroCommandService; + private final TripReportCommandService tripReportCommandService; + private final TripReportStudyLogCommandService tripReportStudyLogCommandService; private final HardDeleteExecutor executor; @@ -46,6 +50,10 @@ public class HardDeleteFacade { "dailyMissionsOwnedByDeletedMission"; private static final String DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_GOAL = "dailyMissionsOwnedByDeletedDailyGoal"; + private static final String TRIP_REPORT_STUDY_LOGS_OWNED_BY_DELETED_MEMBER = + "tripReportStudyLogsOwnedByDeletedMember"; + private static final String TRIP_REPORTS_OWNED_BY_DELETED_MEMBER = + "tripReportsOwnedByDeletedMember"; private static final String POMODOROS = "pomodoros"; private static final String STUDY_LOG_DAILY_MISSIONS = "studyLogDailyMissions"; @@ -68,6 +76,8 @@ public void hardDeleteAll() { deletePomodoros(phases); // 뽀모도로 삭제 deleteStudyLogDailyMissions(phases); // StudyLogDailyMission 삭제 deleteDailyMissions(phases); // 데일리 미션 삭제 + deleteTripReportStudyLogs(phases); // TripReportStudyLog 삭제 + deleteTripReports(phases); // 여행 리포트 삭제 deleteStudyLogs(phases); // 학습 로그 삭제 deleteDailyGoals(phases); // 데일리 목표 삭제 deleteMissions(phases); // 미션 삭제 @@ -122,6 +132,23 @@ private void deleteDailyMissions(Map phases) { executor.run(DAILY_MISSIONS, dailyMissionCommandService::hardDeleteDailyMissions)); } + private void deleteTripReportStudyLogs(Map phases) { + phases.put( + TRIP_REPORT_STUDY_LOGS_OWNED_BY_DELETED_MEMBER, + executor.run( + TRIP_REPORT_STUDY_LOGS_OWNED_BY_DELETED_MEMBER, + tripReportStudyLogCommandService + ::hardDeleteTripReportStudyLogsOwnedByDeletedMember)); + } + + private void deleteTripReports(Map phases) { + phases.put( + TRIP_REPORTS_OWNED_BY_DELETED_MEMBER, + executor.run( + TRIP_REPORTS_OWNED_BY_DELETED_MEMBER, + tripReportCommandService::hardDeleteTripReportsOwnedByDeletedMember)); + } + private void deleteStudyLogs(Map phases) { phases.put( STUDY_LOGS_OWNED_BY_DELETED_MEMBER, diff --git a/src/main/java/com/ject/studytrip/trip/application/service/TripReportCommandService.java b/src/main/java/com/ject/studytrip/trip/application/service/TripReportCommandService.java index 48f4869..973da01 100644 --- a/src/main/java/com/ject/studytrip/trip/application/service/TripReportCommandService.java +++ b/src/main/java/com/ject/studytrip/trip/application/service/TripReportCommandService.java @@ -3,6 +3,7 @@ import com.ject.studytrip.member.domain.model.Member; import com.ject.studytrip.trip.domain.factory.TripReportFactory; import com.ject.studytrip.trip.domain.model.TripReport; +import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository; import com.ject.studytrip.trip.domain.repository.TripReportRepository; import com.ject.studytrip.trip.presentation.dto.request.CreateTripReportRequest; import lombok.RequiredArgsConstructor; @@ -12,6 +13,7 @@ @RequiredArgsConstructor public class TripReportCommandService { private final TripReportRepository tripReportRepository; + private final TripReportQueryRepository tripReportQueryRepository; public TripReport createTripReport(Member member, CreateTripReportRequest request) { TripReport tripReport = @@ -32,4 +34,8 @@ public TripReport createTripReport(Member member, CreateTripReportRequest reques public void updateImageUrl(TripReport tripReport, String imageUrl) { tripReport.updateImageUrl(imageUrl); } + + public long hardDeleteTripReportsOwnedByDeletedMember() { + return tripReportQueryRepository.deleteAllByDeletedMemberOwner(); + } } diff --git a/src/main/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.java b/src/main/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.java index d12ec8f..5689c4c 100644 --- a/src/main/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.java +++ b/src/main/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.java @@ -4,6 +4,7 @@ import com.ject.studytrip.trip.domain.factory.TripReportStudyLogFactory; import com.ject.studytrip.trip.domain.model.TripReport; import com.ject.studytrip.trip.domain.model.TripReportStudyLog; +import com.ject.studytrip.trip.domain.repository.TripReportStudyLogQueryRepository; import com.ject.studytrip.trip.domain.repository.TripReportStudyLogRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -13,6 +14,7 @@ @RequiredArgsConstructor public class TripReportStudyLogCommandService { private final TripReportStudyLogRepository tripReportStudyLogRepository; + private final TripReportStudyLogQueryRepository tripReportStudyLogQueryRepository; public void createTripReportStudyLogs(TripReport tripReport, List studyLogs) { List tripReportStudyLogs = @@ -22,4 +24,8 @@ public void createTripReportStudyLogs(TripReport tripReport, List stud tripReportStudyLogRepository.saveAll(tripReportStudyLogs); } + + public long hardDeleteTripReportStudyLogsOwnedByDeletedMember() { + return tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner(); + } } diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.java new file mode 100644 index 0000000..820d62b --- /dev/null +++ b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportQueryRepository.java @@ -0,0 +1,5 @@ +package com.ject.studytrip.trip.domain.repository; + +public interface TripReportQueryRepository { + long deleteAllByDeletedMemberOwner(); +} diff --git a/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportStudyLogQueryRepository.java b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportStudyLogQueryRepository.java new file mode 100644 index 0000000..26d597e --- /dev/null +++ b/src/main/java/com/ject/studytrip/trip/domain/repository/TripReportStudyLogQueryRepository.java @@ -0,0 +1,5 @@ +package com.ject.studytrip.trip.domain.repository; + +public interface TripReportStudyLogQueryRepository { + long deleteAllByDeletedMemberOwner(); +} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.java new file mode 100644 index 0000000..056ed12 --- /dev/null +++ b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportQueryRepositoryAdapter.java @@ -0,0 +1,29 @@ +package com.ject.studytrip.trip.infra.querydsl; + +import com.ject.studytrip.member.domain.model.QMember; +import com.ject.studytrip.trip.domain.model.QTripReport; +import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class TripReportQueryRepositoryAdapter implements TripReportQueryRepository { + private final JPAQueryFactory queryFactory; + private final QTripReport tripReport = QTripReport.tripReport; + private final QMember member = QMember.member; + + @Override + public long deleteAllByDeletedMemberOwner() { + return queryFactory + .delete(tripReport) + .where( + tripReport.member.id.in( + JPAExpressions.select(member.id) + .from(member) + .where(member.deletedAt.isNotNull()))) + .execute(); + } +} diff --git a/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogQueryRepositoryAdapter.java b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogQueryRepositoryAdapter.java new file mode 100644 index 0000000..118bf41 --- /dev/null +++ b/src/main/java/com/ject/studytrip/trip/infra/querydsl/TripReportStudyLogQueryRepositoryAdapter.java @@ -0,0 +1,43 @@ +package com.ject.studytrip.trip.infra.querydsl; + +import com.ject.studytrip.member.domain.model.QMember; +import com.ject.studytrip.studylog.domain.model.QStudyLog; +import com.ject.studytrip.trip.domain.model.QTripReport; +import com.ject.studytrip.trip.domain.model.QTripReportStudyLog; +import com.ject.studytrip.trip.domain.repository.TripReportStudyLogQueryRepository; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class TripReportStudyLogQueryRepositoryAdapter implements TripReportStudyLogQueryRepository { + private final JPAQueryFactory queryFactory; + private final QTripReportStudyLog tripReportStudyLog = QTripReportStudyLog.tripReportStudyLog; + private final QTripReport tripReport = QTripReport.tripReport; + private final QStudyLog studyLog = QStudyLog.studyLog; + private final QMember member = QMember.member; + + @Override + public long deleteAllByDeletedMemberOwner() { + return queryFactory + .delete(tripReportStudyLog) + .where( + tripReportStudyLog + .tripReport + .id + .in( + JPAExpressions.select(tripReport.id) + .from(tripReport) + .join(tripReport.member, member) + .where(member.deletedAt.isNotNull())) + .or( + tripReportStudyLog.studyLog.id.in( + JPAExpressions.select(studyLog.id) + .from(studyLog) + .join(studyLog.member, member) + .where(member.deletedAt.isNotNull())))) + .execute(); + } +} diff --git a/src/test/java/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.java b/src/test/java/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.java index e9dcadd..371d8e1 100644 --- a/src/test/java/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.java +++ b/src/test/java/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.java @@ -8,6 +8,7 @@ import com.ject.studytrip.member.domain.model.Member; import com.ject.studytrip.member.fixture.MemberFixture; import com.ject.studytrip.trip.domain.model.TripReport; +import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository; import com.ject.studytrip.trip.domain.repository.TripReportRepository; import com.ject.studytrip.trip.fixture.CreateTripReportRequestFixture; import com.ject.studytrip.trip.fixture.TripReportFixture; @@ -23,6 +24,7 @@ class TripReportCommandServiceTest extends BaseUnitTest { @InjectMocks private TripReportCommandService tripReportCommandService; @Mock private TripReportRepository tripReportRepository; + @Mock private TripReportQueryRepository tripReportQueryRepository; private Member member; private TripReport tripReport; @@ -72,4 +74,35 @@ void shouldUpdateImageUrlWhenTripReportIsValid() { assertThat(tripReport.getImageUrl()).isNotEqualTo(oldImageUrl); } } + + @Nested + @DisplayName("hardDeleteTripReportsOwnedByDeletedMember 메서드는") + class HardDeleteTripReportsOwnedByDeletedMember { + + @Test + @DisplayName("삭제된 멤버가 소유한 여행 리포트가 없으면 0을 반환한다.") + void shouldReturnZeroWhenTripReportsOwnedByDeletedMemberDoNotExist() { + // given + given(tripReportQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(0L); + + // when + long result = tripReportCommandService.hardDeleteTripReportsOwnedByDeletedMember(); + + // then + assertThat(result).isEqualTo(0L); + } + + @Test + @DisplayName("삭제된 멤버가 소유한 여행 리포트가 있으면 해당 개수를 반환한다.") + void shouldReturnCountWhenTripReportsOwnedByDeletedMemberExist() { + // given + given(tripReportQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(5L); + + // when + long result = tripReportCommandService.hardDeleteTripReportsOwnedByDeletedMember(); + + // then + assertThat(result).isEqualTo(5L); + } + } } diff --git a/src/test/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.java b/src/test/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.java index 6f32291..437e892 100644 --- a/src/test/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.java +++ b/src/test/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.java @@ -1,6 +1,8 @@ package com.ject.studytrip.trip.application.service; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -11,6 +13,7 @@ import com.ject.studytrip.studylog.domain.model.StudyLog; import com.ject.studytrip.studylog.fixture.StudyLogFixture; import com.ject.studytrip.trip.domain.model.*; +import com.ject.studytrip.trip.domain.repository.TripReportStudyLogQueryRepository; import com.ject.studytrip.trip.domain.repository.TripReportStudyLogRepository; import com.ject.studytrip.trip.fixture.DailyGoalFixture; import com.ject.studytrip.trip.fixture.TripFixture; @@ -27,6 +30,7 @@ class TripReportStudyLogCommandServiceTest extends BaseUnitTest { @InjectMocks private TripReportStudyLogCommandService tripReportStudyLogCommandService; @Mock private TripReportStudyLogRepository tripReportStudyLogRepository; + @Mock private TripReportStudyLogQueryRepository tripReportStudyLogQueryRepository; private TripReport tripReport; private List studyLogs; @@ -59,4 +63,54 @@ void shouldCreateTripReportStudyLogs() { verify(tripReportStudyLogRepository, times(1)).saveAll(anyList()); } } + + @Nested + @DisplayName("hardDeleteTripReportStudyLogsOwnedByDeletedMember 메서드는") + class HardDeleteTripReportStudyLogsOwnedByDeletedMember { + + @Test + @DisplayName("삭제된 멤버가 소유한 여행 리포트가 없으면 0을 반환한다.") + void shouldReturnZeroWhenTripReportsOwnedByDeletedMemberDoNotExist() { + // given + given(tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(0L); + + // when + long result = + tripReportStudyLogCommandService + .hardDeleteTripReportStudyLogsOwnedByDeletedMember(); + + // then + assertThat(result).isEqualTo(0L); + } + + @Test + @DisplayName("삭제된 멤버가 소유한 학습 로그가 없으면 0을 반환한다.") + void shouldReturnZeroWhenStudyLogsOwnedByDeletedMemberDoNotExist() { + // given + given(tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(0L); + + // when + long result = + tripReportStudyLogCommandService + .hardDeleteTripReportStudyLogsOwnedByDeletedMember(); + + // then + assertThat(result).isEqualTo(0L); + } + + @Test + @DisplayName("삭제된 멤버가 소유한 여행 리포트 또는 학습 로그가 있으면 삭제된 TripReportStudyLog 개수를 반환한다.") + void shouldReturnCountWhenTripReportsOrStudyLogsOwnedByDeletedMemberExist() { + // given + given(tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(5L); + + // when + long result = + tripReportStudyLogCommandService + .hardDeleteTripReportStudyLogsOwnedByDeletedMember(); + + // then + assertThat(result).isEqualTo(5L); + } + } }