Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class HardDeleteFacade {
private static final String TRIPS = "trips";
private static final String DAILY_GOALS_OWNED_BY_DELETED_TRIP = "dailyGoalsOwnedByDeletedTrip";
private static final String DAILY_GOALS = "dailyGoals";
private static final String TRIP_REPORTS = "tripReports";
private static final String MEMBERS = "members";

@Transactional(propagation = Propagation.REQUIRES_NEW)
Expand Down Expand Up @@ -147,6 +148,9 @@ private void deleteTripReports(Map<String, Long> phases) {
executor.run(
TRIP_REPORTS_OWNED_BY_DELETED_MEMBER,
tripReportCommandService::hardDeleteTripReportsOwnedByDeletedMember));
phases.put(
TRIP_REPORTS,
executor.run(TRIP_REPORTS, tripReportCommandService::hardDeleteTripReports));
}

private void deleteStudyLogs(Map<String, Long> phases) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ public TripReportInfo createTripReport(Long memberId, CreateTripReportRequest re
return TripReportInfo.from(tripReport);
}

@Transactional
public void deleteTripReport(Long memberId, Long tripReportId) {
Member member = memberQueryService.getValidMember(memberId);
TripReport tripReport =
tripReportQueryService.getValidTripReport(member.getId(), tripReportId);

tripReportCommandService.deleteTripReport(tripReport);
}

@Transactional(readOnly = true)
public PresignedTripReportImageInfo issuePresignedUrl(
Long tripReportId, PresignTripReportImageRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public void updateImageUrl(TripReport tripReport, String imageUrl) {
tripReport.updateImageUrl(imageUrl);
}

public void deleteTripReport(TripReport tripReport) {
tripReport.updateDeletedAt();
}

public long hardDeleteTripReports() {
return tripReportQueryRepository.deleteAllByDeletedAtIsNotNull();
}

public long hardDeleteTripReportsOwnedByDeletedMember() {
return tripReportQueryRepository.deleteAllByDeletedMemberOwner();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ public TripReport getValidTripReport(Long memberId, Long tripReportId) {
TripReportErrorCode.TRIP_REPORT_NOT_FOUND));

TripReportPolicy.validateOwner(memberId, tripReport);
TripReportPolicy.validateNotDeleted(tripReport);

return tripReport;
}

public List<TripReport> getTripReportsByMemberId(Long memberId) {
return tripReportRepository.findAllByMemberIdOrderByCreatedAtDesc(memberId);
return tripReportRepository.findAllByMemberIdAndDeletedAtIsNullOrderByCreatedAtDesc(
memberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

@RequiredArgsConstructor
public enum TripReportErrorCode implements ErrorCode {
// 400
TRIP_REPORT_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 여행 리포트입니다."),

// 403
NOT_TRIP_REPORT_OWNER(HttpStatus.FORBIDDEN, "요청한 여행 리포트 정보를 조회할 권한이 없습니다."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.ject.studytrip.global.common.entity.BaseTimeEntity;
import com.ject.studytrip.member.domain.model.Member;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import lombok.*;

@Entity
Expand Down Expand Up @@ -72,4 +73,8 @@ public static TripReport of(
public void updateImageUrl(String imageUrl) {
if (hasText(imageUrl)) this.imageUrl = imageUrl;
}

public void updateDeletedAt() {
this.deletedAt = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ public static void validateOwner(Long memberId, TripReport tripReport) {
throw new CustomException(TripReportErrorCode.NOT_TRIP_REPORT_OWNER);
}
}

public static void validateNotDeleted(TripReport tripReport) {
if (tripReport.getDeletedAt() != null)
throw new CustomException(TripReportErrorCode.TRIP_REPORT_ALREADY_DELETED);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.ject.studytrip.trip.domain.repository;

public interface TripReportQueryRepository {
long deleteAllByDeletedAtIsNotNull();

long deleteAllByDeletedMemberOwner();
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
public interface TripReportRepository {
Optional<TripReport> findById(Long tripReportId);

List<TripReport> findAllByMemberIdOrderByCreatedAtDesc(Long memberId);
List<TripReport> findAllByMemberIdAndDeletedAtIsNullOrderByCreatedAtDesc(Long memberId);

TripReport save(TripReport tripReport);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface TripReportJpaRepository extends JpaRepository<TripReport, Long> {
List<TripReport> findAllByMember_IdOrderByCreatedAtDesc(Long memberId);
List<TripReport> findAllByMember_IdAndDeletedAtIsNullOrderByCreatedAtDesc(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ public Optional<TripReport> findById(Long tripReportId) {
}

@Override
public List<TripReport> findAllByMemberIdOrderByCreatedAtDesc(Long memberId) {
return tripReportJpaRepository.findAllByMember_IdOrderByCreatedAtDesc(memberId);
public List<TripReport> findAllByMemberIdAndDeletedAtIsNullOrderByCreatedAtDesc(Long memberId) {
return tripReportJpaRepository.findAllByMember_IdAndDeletedAtIsNullOrderByCreatedAtDesc(
memberId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public class TripReportQueryRepositoryAdapter implements TripReportQueryReposito
private final QTripReport tripReport = QTripReport.tripReport;
private final QMember member = QMember.member;

@Override
public long deleteAllByDeletedAtIsNotNull() {
return queryFactory.delete(tripReport).where(tripReport.deletedAt.isNotNull()).execute();
}

@Override
public long deleteAllByDeletedMemberOwner() {
return queryFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ public ResponseEntity<StandardResponse> createTripReport(
HttpStatus.CREATED.value(), CreateTripReportResponse.of(result)));
}

@Operation(summary = "여행 리포트 삭제", description = "사용자가 작성한 여행 리포트를 삭제합니다.")
@DeleteMapping("/api/trip-reports/{tripReportId}")
public ResponseEntity<StandardResponse> deleteTripReport(
@AuthenticationPrincipal String memberId,
@PathVariable @NotNull(message = "여행 리포트 ID는 필수 요청 파라미터입니다.") Long tripReportId) {
tripReportFacade.deleteTripReport(Long.valueOf(memberId), tripReportId);

return ResponseEntity.status(HttpStatus.OK)
.body(StandardResponse.success(HttpStatus.OK.value(), null));
}

@Operation(
summary = "여행 리포트 이미지 업로드용 Presigned URL 발급",
description =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ private static TripReportSummary of(List<TripReportInfo> tripReportInfos) {

private record LoadTripReportInfoResponse(
@Schema(description = "여행 리포트 ID") Long tripReportId,
@Schema(description = "여행 리포트 제목") String title,
@Schema(description = "여행 시작일 (여행 회고)") String startDate,
@Schema(description = "여행 종료일 (여행 회고)") String endDate,
@Schema(description = "총 학습 시간") long totalFocusHours,
@Schema(description = "여행 리포트 이미지 URL") String imageUrl) {
private static LoadTripReportInfoResponse of(TripReportInfo tripReportInfo) {
return new LoadTripReportInfoResponse(
tripReportInfo.tripReportId(),
tripReportInfo.title(),
tripReportInfo.startDate(),
tripReportInfo.endDate(),
tripReportInfo.totalFocusHours(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,52 @@ void shouldUpdateImageUrlWhenTripReportIsValid() {
}
}

@Nested
@DisplayName("deleteTripReport 메서드는")
class DeleteTripReport {

@Test
@DisplayName("특정 여행 리포트의 deletedAt 필드를 현재 시간으로 업데이트한다")
void shouldDeleteTripForUpdateDeletedAt() {
// when
tripReportCommandService.deleteTripReport(tripReport);

// then
assertThat(tripReport.getDeletedAt()).isNotNull();
}
}

@Nested
@DisplayName("hardDeleteTripReports 메서드는")
class HardDeleteTripReports {

@Test
@DisplayName("삭제된 여행 리포트가 하나라도 없으면 0을 반환한다.")
void shouldReturnZeroWhenDeletedTripReportDoesNotExist() {
// given
given(tripReportQueryRepository.deleteAllByDeletedAtIsNotNull()).willReturn(0L);

// when
long result = tripReportCommandService.hardDeleteTripReports();

// then
assertThat(result).isEqualTo(0L);
}

@Test
@DisplayName("삭제된 여행 리포트가 하나라도 있으면 해당 개수를 반환한다.")
void shouldReturnCountWhenDeletedTripReportExist() {
// given
given(tripReportQueryRepository.deleteAllByDeletedAtIsNotNull()).willReturn(5L);

// when
long result = tripReportCommandService.hardDeleteTripReports();

// then
assertThat(result).isEqualTo(5L);
}
}

@Nested
@DisplayName("hardDeleteTripReportsOwnedByDeletedMember 메서드는")
class HardDeleteTripReportsOwnedByDeletedMember {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,23 @@ void shouldThrowExceptionWhenTripReportDoNotExist() {
.hasMessage(TripReportErrorCode.TRIP_REPORT_NOT_FOUND.getMessage());
}

@Test
@DisplayName("여행 리포트가 이미 삭제되었다면 예외가 발생한다.")
void shouldThrowExceptionWhenTripReportAlreadyDeleted() {
// given
Long tripReportId = tripReport1.getId();
tripReport1.updateDeletedAt();
given(tripReportRepository.findById(tripReportId)).willReturn(Optional.of(tripReport1));

// when & then
assertThatThrownBy(
() ->
tripReportQueryService.getValidTripReport(
member.getId(), tripReportId))
.isInstanceOf(CustomException.class)
.hasMessage(TripReportErrorCode.TRIP_REPORT_ALREADY_DELETED.getMessage());
}

@Test
@DisplayName("여행 리포트가 존재하면 여행 리포트를 반환한다.")
void shouldReturnValidTripReportWhenTripReportExist() {
Expand Down Expand Up @@ -140,7 +157,10 @@ class GetTripReportsByMemberId {
void shouldReturnEmptyListWhenTripReportDoNotExist() {
// given
Long memberId = member.getId();
given(tripReportRepository.findAllByMemberIdOrderByCreatedAtDesc(memberId))
given(
tripReportRepository
.findAllByMemberIdAndDeletedAtIsNullOrderByCreatedAtDesc(
memberId))
.willReturn(List.of());

// when
Expand All @@ -155,7 +175,10 @@ void shouldReturnEmptyListWhenTripReportDoNotExist() {
void shouldReturnTripReportsWhenTripReportExists() {
// given
Long memberId = member.getId();
given(tripReportRepository.findAllByMemberIdOrderByCreatedAtDesc(memberId))
given(
tripReportRepository
.findAllByMemberIdAndDeletedAtIsNullOrderByCreatedAtDesc(
memberId))
.willReturn(List.of(tripReport1, tripReport2));

// when
Expand Down
Loading