Skip to content

Commit a95a4df

Browse files
feat: Spring Batch 기반 TripReport 연관 엔티티 삭제 및 하드 딜리트 기능 추가(#92)
* feat: TripReportQueryRepository, TripReportQueryRepositoryAdapter 구현 * feat: TripReportStudyLogQueryRepository, TripReportStudyLogQueryRepositoryAdapter 구현 * feat: TripReportCommandService, TripReportStudyLogCommandService에 하드 딜리트 비즈니스 로직 추가 * feat: HardDeleteFacade에 TripReport, TripReportStudyLog에 하드 딜리트 비즈니스 로직 추가 * test: TripReportCommandServiceTest에 HardDeleteTripReportsOwnedByDeletedMember 단위 테스트 추가 * test: TripReportStudyLogCommandServiceTest에 HardDeleteTripReportStudyLogsOwnedByDeletedMember 단위 테스트 추가
1 parent f25d6aa commit a95a4df

9 files changed

Lines changed: 208 additions & 0 deletions

File tree

src/main/java/com/ject/studytrip/cleanup/application/facade/HardDeleteFacade.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import com.ject.studytrip.studylog.application.service.StudyLogDailyMissionCommandService;
1111
import com.ject.studytrip.trip.application.service.DailyGoalCommandService;
1212
import com.ject.studytrip.trip.application.service.TripCommandService;
13+
import com.ject.studytrip.trip.application.service.TripReportCommandService;
14+
import com.ject.studytrip.trip.application.service.TripReportStudyLogCommandService;
1315
import java.util.LinkedHashMap;
1416
import java.util.Map;
1517
import lombok.RequiredArgsConstructor;
@@ -29,6 +31,8 @@ public class HardDeleteFacade {
2931
private final StudyLogDailyMissionCommandService studyLogDailyMissionCommandService;
3032
private final DailyGoalCommandService dailyGoalCommandService;
3133
private final PomodoroCommandService pomodoroCommandService;
34+
private final TripReportCommandService tripReportCommandService;
35+
private final TripReportStudyLogCommandService tripReportStudyLogCommandService;
3236

3337
private final HardDeleteExecutor executor;
3438

@@ -46,6 +50,10 @@ public class HardDeleteFacade {
4650
"dailyMissionsOwnedByDeletedMission";
4751
private static final String DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_GOAL =
4852
"dailyMissionsOwnedByDeletedDailyGoal";
53+
private static final String TRIP_REPORT_STUDY_LOGS_OWNED_BY_DELETED_MEMBER =
54+
"tripReportStudyLogsOwnedByDeletedMember";
55+
private static final String TRIP_REPORTS_OWNED_BY_DELETED_MEMBER =
56+
"tripReportsOwnedByDeletedMember";
4957

5058
private static final String POMODOROS = "pomodoros";
5159
private static final String STUDY_LOG_DAILY_MISSIONS = "studyLogDailyMissions";
@@ -68,6 +76,8 @@ public void hardDeleteAll() {
6876
deletePomodoros(phases); // 뽀모도로 삭제
6977
deleteStudyLogDailyMissions(phases); // StudyLogDailyMission 삭제
7078
deleteDailyMissions(phases); // 데일리 미션 삭제
79+
deleteTripReportStudyLogs(phases); // TripReportStudyLog 삭제
80+
deleteTripReports(phases); // 여행 리포트 삭제
7181
deleteStudyLogs(phases); // 학습 로그 삭제
7282
deleteDailyGoals(phases); // 데일리 목표 삭제
7383
deleteMissions(phases); // 미션 삭제
@@ -122,6 +132,23 @@ private void deleteDailyMissions(Map<String, Long> phases) {
122132
executor.run(DAILY_MISSIONS, dailyMissionCommandService::hardDeleteDailyMissions));
123133
}
124134

135+
private void deleteTripReportStudyLogs(Map<String, Long> phases) {
136+
phases.put(
137+
TRIP_REPORT_STUDY_LOGS_OWNED_BY_DELETED_MEMBER,
138+
executor.run(
139+
TRIP_REPORT_STUDY_LOGS_OWNED_BY_DELETED_MEMBER,
140+
tripReportStudyLogCommandService
141+
::hardDeleteTripReportStudyLogsOwnedByDeletedMember));
142+
}
143+
144+
private void deleteTripReports(Map<String, Long> phases) {
145+
phases.put(
146+
TRIP_REPORTS_OWNED_BY_DELETED_MEMBER,
147+
executor.run(
148+
TRIP_REPORTS_OWNED_BY_DELETED_MEMBER,
149+
tripReportCommandService::hardDeleteTripReportsOwnedByDeletedMember));
150+
}
151+
125152
private void deleteStudyLogs(Map<String, Long> phases) {
126153
phases.put(
127154
STUDY_LOGS_OWNED_BY_DELETED_MEMBER,

src/main/java/com/ject/studytrip/trip/application/service/TripReportCommandService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.ject.studytrip.member.domain.model.Member;
44
import com.ject.studytrip.trip.domain.factory.TripReportFactory;
55
import com.ject.studytrip.trip.domain.model.TripReport;
6+
import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository;
67
import com.ject.studytrip.trip.domain.repository.TripReportRepository;
78
import com.ject.studytrip.trip.presentation.dto.request.CreateTripReportRequest;
89
import lombok.RequiredArgsConstructor;
@@ -12,6 +13,7 @@
1213
@RequiredArgsConstructor
1314
public class TripReportCommandService {
1415
private final TripReportRepository tripReportRepository;
16+
private final TripReportQueryRepository tripReportQueryRepository;
1517

1618
public TripReport createTripReport(Member member, CreateTripReportRequest request) {
1719
TripReport tripReport =
@@ -32,4 +34,8 @@ public TripReport createTripReport(Member member, CreateTripReportRequest reques
3234
public void updateImageUrl(TripReport tripReport, String imageUrl) {
3335
tripReport.updateImageUrl(imageUrl);
3436
}
37+
38+
public long hardDeleteTripReportsOwnedByDeletedMember() {
39+
return tripReportQueryRepository.deleteAllByDeletedMemberOwner();
40+
}
3541
}

src/main/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.ject.studytrip.trip.domain.factory.TripReportStudyLogFactory;
55
import com.ject.studytrip.trip.domain.model.TripReport;
66
import com.ject.studytrip.trip.domain.model.TripReportStudyLog;
7+
import com.ject.studytrip.trip.domain.repository.TripReportStudyLogQueryRepository;
78
import com.ject.studytrip.trip.domain.repository.TripReportStudyLogRepository;
89
import java.util.List;
910
import lombok.RequiredArgsConstructor;
@@ -13,6 +14,7 @@
1314
@RequiredArgsConstructor
1415
public class TripReportStudyLogCommandService {
1516
private final TripReportStudyLogRepository tripReportStudyLogRepository;
17+
private final TripReportStudyLogQueryRepository tripReportStudyLogQueryRepository;
1618

1719
public void createTripReportStudyLogs(TripReport tripReport, List<StudyLog> studyLogs) {
1820
List<TripReportStudyLog> tripReportStudyLogs =
@@ -22,4 +24,8 @@ public void createTripReportStudyLogs(TripReport tripReport, List<StudyLog> stud
2224

2325
tripReportStudyLogRepository.saveAll(tripReportStudyLogs);
2426
}
27+
28+
public long hardDeleteTripReportStudyLogsOwnedByDeletedMember() {
29+
return tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner();
30+
}
2531
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.ject.studytrip.trip.domain.repository;
2+
3+
public interface TripReportQueryRepository {
4+
long deleteAllByDeletedMemberOwner();
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.ject.studytrip.trip.domain.repository;
2+
3+
public interface TripReportStudyLogQueryRepository {
4+
long deleteAllByDeletedMemberOwner();
5+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.ject.studytrip.trip.infra.querydsl;
2+
3+
import com.ject.studytrip.member.domain.model.QMember;
4+
import com.ject.studytrip.trip.domain.model.QTripReport;
5+
import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository;
6+
import com.querydsl.jpa.JPAExpressions;
7+
import com.querydsl.jpa.impl.JPAQueryFactory;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.stereotype.Repository;
10+
11+
@Repository
12+
@RequiredArgsConstructor
13+
public class TripReportQueryRepositoryAdapter implements TripReportQueryRepository {
14+
private final JPAQueryFactory queryFactory;
15+
private final QTripReport tripReport = QTripReport.tripReport;
16+
private final QMember member = QMember.member;
17+
18+
@Override
19+
public long deleteAllByDeletedMemberOwner() {
20+
return queryFactory
21+
.delete(tripReport)
22+
.where(
23+
tripReport.member.id.in(
24+
JPAExpressions.select(member.id)
25+
.from(member)
26+
.where(member.deletedAt.isNotNull())))
27+
.execute();
28+
}
29+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.ject.studytrip.trip.infra.querydsl;
2+
3+
import com.ject.studytrip.member.domain.model.QMember;
4+
import com.ject.studytrip.studylog.domain.model.QStudyLog;
5+
import com.ject.studytrip.trip.domain.model.QTripReport;
6+
import com.ject.studytrip.trip.domain.model.QTripReportStudyLog;
7+
import com.ject.studytrip.trip.domain.repository.TripReportStudyLogQueryRepository;
8+
import com.querydsl.jpa.JPAExpressions;
9+
import com.querydsl.jpa.impl.JPAQueryFactory;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.stereotype.Repository;
12+
13+
@Repository
14+
@RequiredArgsConstructor
15+
public class TripReportStudyLogQueryRepositoryAdapter implements TripReportStudyLogQueryRepository {
16+
private final JPAQueryFactory queryFactory;
17+
private final QTripReportStudyLog tripReportStudyLog = QTripReportStudyLog.tripReportStudyLog;
18+
private final QTripReport tripReport = QTripReport.tripReport;
19+
private final QStudyLog studyLog = QStudyLog.studyLog;
20+
private final QMember member = QMember.member;
21+
22+
@Override
23+
public long deleteAllByDeletedMemberOwner() {
24+
return queryFactory
25+
.delete(tripReportStudyLog)
26+
.where(
27+
tripReportStudyLog
28+
.tripReport
29+
.id
30+
.in(
31+
JPAExpressions.select(tripReport.id)
32+
.from(tripReport)
33+
.join(tripReport.member, member)
34+
.where(member.deletedAt.isNotNull()))
35+
.or(
36+
tripReportStudyLog.studyLog.id.in(
37+
JPAExpressions.select(studyLog.id)
38+
.from(studyLog)
39+
.join(studyLog.member, member)
40+
.where(member.deletedAt.isNotNull()))))
41+
.execute();
42+
}
43+
}

src/test/java/com/ject/studytrip/trip/application/service/TripReportCommandServiceTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.ject.studytrip.member.domain.model.Member;
99
import com.ject.studytrip.member.fixture.MemberFixture;
1010
import com.ject.studytrip.trip.domain.model.TripReport;
11+
import com.ject.studytrip.trip.domain.repository.TripReportQueryRepository;
1112
import com.ject.studytrip.trip.domain.repository.TripReportRepository;
1213
import com.ject.studytrip.trip.fixture.CreateTripReportRequestFixture;
1314
import com.ject.studytrip.trip.fixture.TripReportFixture;
@@ -23,6 +24,7 @@
2324
class TripReportCommandServiceTest extends BaseUnitTest {
2425
@InjectMocks private TripReportCommandService tripReportCommandService;
2526
@Mock private TripReportRepository tripReportRepository;
27+
@Mock private TripReportQueryRepository tripReportQueryRepository;
2628

2729
private Member member;
2830
private TripReport tripReport;
@@ -72,4 +74,35 @@ void shouldUpdateImageUrlWhenTripReportIsValid() {
7274
assertThat(tripReport.getImageUrl()).isNotEqualTo(oldImageUrl);
7375
}
7476
}
77+
78+
@Nested
79+
@DisplayName("hardDeleteTripReportsOwnedByDeletedMember 메서드는")
80+
class HardDeleteTripReportsOwnedByDeletedMember {
81+
82+
@Test
83+
@DisplayName("삭제된 멤버가 소유한 여행 리포트가 없으면 0을 반환한다.")
84+
void shouldReturnZeroWhenTripReportsOwnedByDeletedMemberDoNotExist() {
85+
// given
86+
given(tripReportQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(0L);
87+
88+
// when
89+
long result = tripReportCommandService.hardDeleteTripReportsOwnedByDeletedMember();
90+
91+
// then
92+
assertThat(result).isEqualTo(0L);
93+
}
94+
95+
@Test
96+
@DisplayName("삭제된 멤버가 소유한 여행 리포트가 있으면 해당 개수를 반환한다.")
97+
void shouldReturnCountWhenTripReportsOwnedByDeletedMemberExist() {
98+
// given
99+
given(tripReportQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(5L);
100+
101+
// when
102+
long result = tripReportCommandService.hardDeleteTripReportsOwnedByDeletedMember();
103+
104+
// then
105+
assertThat(result).isEqualTo(5L);
106+
}
107+
}
75108
}

src/test/java/com/ject/studytrip/trip/application/service/TripReportStudyLogCommandServiceTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.ject.studytrip.trip.application.service;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
34
import static org.mockito.ArgumentMatchers.anyList;
5+
import static org.mockito.BDDMockito.given;
46
import static org.mockito.BDDMockito.willDoNothing;
57
import static org.mockito.Mockito.times;
68
import static org.mockito.Mockito.verify;
@@ -11,6 +13,7 @@
1113
import com.ject.studytrip.studylog.domain.model.StudyLog;
1214
import com.ject.studytrip.studylog.fixture.StudyLogFixture;
1315
import com.ject.studytrip.trip.domain.model.*;
16+
import com.ject.studytrip.trip.domain.repository.TripReportStudyLogQueryRepository;
1417
import com.ject.studytrip.trip.domain.repository.TripReportStudyLogRepository;
1518
import com.ject.studytrip.trip.fixture.DailyGoalFixture;
1619
import com.ject.studytrip.trip.fixture.TripFixture;
@@ -27,6 +30,7 @@
2730
class TripReportStudyLogCommandServiceTest extends BaseUnitTest {
2831
@InjectMocks private TripReportStudyLogCommandService tripReportStudyLogCommandService;
2932
@Mock private TripReportStudyLogRepository tripReportStudyLogRepository;
33+
@Mock private TripReportStudyLogQueryRepository tripReportStudyLogQueryRepository;
3034

3135
private TripReport tripReport;
3236
private List<StudyLog> studyLogs;
@@ -59,4 +63,54 @@ void shouldCreateTripReportStudyLogs() {
5963
verify(tripReportStudyLogRepository, times(1)).saveAll(anyList());
6064
}
6165
}
66+
67+
@Nested
68+
@DisplayName("hardDeleteTripReportStudyLogsOwnedByDeletedMember 메서드는")
69+
class HardDeleteTripReportStudyLogsOwnedByDeletedMember {
70+
71+
@Test
72+
@DisplayName("삭제된 멤버가 소유한 여행 리포트가 없으면 0을 반환한다.")
73+
void shouldReturnZeroWhenTripReportsOwnedByDeletedMemberDoNotExist() {
74+
// given
75+
given(tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(0L);
76+
77+
// when
78+
long result =
79+
tripReportStudyLogCommandService
80+
.hardDeleteTripReportStudyLogsOwnedByDeletedMember();
81+
82+
// then
83+
assertThat(result).isEqualTo(0L);
84+
}
85+
86+
@Test
87+
@DisplayName("삭제된 멤버가 소유한 학습 로그가 없으면 0을 반환한다.")
88+
void shouldReturnZeroWhenStudyLogsOwnedByDeletedMemberDoNotExist() {
89+
// given
90+
given(tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(0L);
91+
92+
// when
93+
long result =
94+
tripReportStudyLogCommandService
95+
.hardDeleteTripReportStudyLogsOwnedByDeletedMember();
96+
97+
// then
98+
assertThat(result).isEqualTo(0L);
99+
}
100+
101+
@Test
102+
@DisplayName("삭제된 멤버가 소유한 여행 리포트 또는 학습 로그가 있으면 삭제된 TripReportStudyLog 개수를 반환한다.")
103+
void shouldReturnCountWhenTripReportsOrStudyLogsOwnedByDeletedMemberExist() {
104+
// given
105+
given(tripReportStudyLogQueryRepository.deleteAllByDeletedMemberOwner()).willReturn(5L);
106+
107+
// when
108+
long result =
109+
tripReportStudyLogCommandService
110+
.hardDeleteTripReportStudyLogsOwnedByDeletedMember();
111+
112+
// then
113+
assertThat(result).isEqualTo(5L);
114+
}
115+
}
62116
}

0 commit comments

Comments
 (0)