diff --git a/src/main/java/com/ddd/moodof/adapter/infrastructure/persistence/querydsl/TrashPhotoQuerydslRepository.java b/src/main/java/com/ddd/moodof/adapter/infrastructure/persistence/querydsl/TrashPhotoQuerydslRepository.java index e1beb03..f505a7d 100644 --- a/src/main/java/com/ddd/moodof/adapter/infrastructure/persistence/querydsl/TrashPhotoQuerydslRepository.java +++ b/src/main/java/com/ddd/moodof/adapter/infrastructure/persistence/querydsl/TrashPhotoQuerydslRepository.java @@ -47,6 +47,7 @@ public TrashPhotoDTO.TrashPhotoPageResponse findPage(Long userId, Pageable pagea .applyPagination(pageable, jpaQuery) .fetch(); - return new TrashPhotoDTO.TrashPhotoPageResponse(paginationUtils.getTotalPageCount(jpaQuery.fetchCount(), pageable.getPageSize()), trashPhotoResponses); + long totalCount = jpaQuery.fetchCount(); + return new TrashPhotoDTO.TrashPhotoPageResponse(totalCount, paginationUtils.getTotalPageCount(totalCount, pageable.getPageSize()), trashPhotoResponses); } } diff --git a/src/main/java/com/ddd/moodof/adapter/presentation/StoragePhotoController.java b/src/main/java/com/ddd/moodof/adapter/presentation/StoragePhotoController.java index ace4513..5b86cdc 100644 --- a/src/main/java/com/ddd/moodof/adapter/presentation/StoragePhotoController.java +++ b/src/main/java/com/ddd/moodof/adapter/presentation/StoragePhotoController.java @@ -2,7 +2,9 @@ import com.ddd.moodof.adapter.presentation.api.StoragePhotoAPI; import com.ddd.moodof.application.StoragePhotoService; +import com.ddd.moodof.application.TrashPhotoService; import com.ddd.moodof.application.dto.StoragePhotoDTO; +import com.ddd.moodof.application.dto.TrashPhotoDTO; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -18,6 +20,7 @@ public class StoragePhotoController implements StoragePhotoAPI { public static final String API_STORAGE_PHOTO = "/api/storage-photos"; private final StoragePhotoService storagePhotoService; + private final TrashPhotoService trashPhotoService; @Override @PostMapping @@ -57,8 +60,8 @@ public ResponseEntity findDetail( @Override @DeleteMapping - public ResponseEntity delete(@LoginUserId Long userId, @RequestBody StoragePhotoDTO.DeleteStoragePhotos request) { - storagePhotoService.delete(userId, request); - return ResponseEntity.noContent().build(); + public ResponseEntity> addToTrash(@LoginUserId Long userId, @RequestBody TrashPhotoDTO.CreateTrashPhotos request) { + List response = trashPhotoService.add(userId, request); + return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/ddd/moodof/adapter/presentation/TrashPhotoController.java b/src/main/java/com/ddd/moodof/adapter/presentation/TrashPhotoController.java index 9b05b92..2a429e7 100644 --- a/src/main/java/com/ddd/moodof/adapter/presentation/TrashPhotoController.java +++ b/src/main/java/com/ddd/moodof/adapter/presentation/TrashPhotoController.java @@ -2,6 +2,7 @@ import com.ddd.moodof.adapter.presentation.api.TrashPhotoAPI; import com.ddd.moodof.application.TrashPhotoService; +import com.ddd.moodof.application.dto.StoragePhotoDTO; import com.ddd.moodof.application.dto.TrashPhotoDTO; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -24,6 +25,12 @@ public ResponseEntity> add(@LoginU return ResponseEntity.ok(responses); } + @PostMapping("/restore") + public ResponseEntity> restore(@LoginUserId Long userId, @RequestBody TrashPhotoDTO.TrashPhotosRequest request) { + List responses = trashPhotoService.restore(request, userId); + return ResponseEntity.ok(responses); + } + @Override @GetMapping public ResponseEntity findPage( @@ -38,8 +45,8 @@ public ResponseEntity findPage( @Override @DeleteMapping - public ResponseEntity cancel(@LoginUserId Long userId, @RequestBody TrashPhotoDTO.CancelTrashPhotos request) { - trashPhotoService.cancel(request, userId); + public ResponseEntity delete(@LoginUserId Long userId, @RequestBody TrashPhotoDTO.TrashPhotosRequest request) { + trashPhotoService.deletePhoto(request, userId); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/ddd/moodof/adapter/presentation/api/StoragePhotoAPI.java b/src/main/java/com/ddd/moodof/adapter/presentation/api/StoragePhotoAPI.java index 4495152..db0c1e0 100644 --- a/src/main/java/com/ddd/moodof/adapter/presentation/api/StoragePhotoAPI.java +++ b/src/main/java/com/ddd/moodof/adapter/presentation/api/StoragePhotoAPI.java @@ -2,6 +2,7 @@ import com.ddd.moodof.adapter.presentation.LoginUserId; import com.ddd.moodof.application.dto.StoragePhotoDTO; +import com.ddd.moodof.application.dto.TrashPhotoDTO; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiParam; import org.springframework.http.ResponseEntity; @@ -32,5 +33,5 @@ ResponseEntity findPage( @ApiImplicitParam(name = "Authorization", value = "Access Token", required = true, paramType = "header", dataTypeClass = String.class, example = "Bearer access_token") @DeleteMapping - ResponseEntity delete(@ApiIgnore @LoginUserId Long userId, @RequestBody StoragePhotoDTO.DeleteStoragePhotos request); + ResponseEntity> addToTrash(@ApiIgnore @LoginUserId Long userId, @RequestBody TrashPhotoDTO.CreateTrashPhotos request); } diff --git a/src/main/java/com/ddd/moodof/adapter/presentation/api/TrashPhotoAPI.java b/src/main/java/com/ddd/moodof/adapter/presentation/api/TrashPhotoAPI.java index 43c7088..733ae58 100644 --- a/src/main/java/com/ddd/moodof/adapter/presentation/api/TrashPhotoAPI.java +++ b/src/main/java/com/ddd/moodof/adapter/presentation/api/TrashPhotoAPI.java @@ -26,5 +26,5 @@ ResponseEntity findPage( @ApiImplicitParam(name = "Authorization", value = "Access Token", required = true, paramType = "header", dataTypeClass = String.class, example = "Bearer access_token") @DeleteMapping - ResponseEntity cancel(@ApiIgnore @LoginUserId Long userId, @RequestBody TrashPhotoDTO.CancelTrashPhotos request); + ResponseEntity delete(@ApiIgnore @LoginUserId Long userId, @RequestBody TrashPhotoDTO.TrashPhotosRequest request); } diff --git a/src/main/java/com/ddd/moodof/application/StoragePhotoService.java b/src/main/java/com/ddd/moodof/application/StoragePhotoService.java index 30715f1..3f963b0 100644 --- a/src/main/java/com/ddd/moodof/application/StoragePhotoService.java +++ b/src/main/java/com/ddd/moodof/application/StoragePhotoService.java @@ -45,4 +45,9 @@ public void delete(Long userId, StoragePhotoDTO.DeleteStoragePhotos request) { public StoragePhotoDTO.StoragePhotoDetailResponse findDetail(Long userId, Long id, List tagIds) { return storagePhotoQueryRepository.findDetail(userId, id, tagIds); } + + public List findAllById(List storagePhotoIds) { + List storagePhotos = storagePhotoRepository.findAllById(storagePhotoIds); + return StoragePhotoDTO.StoragePhotoResponse.listFrom(storagePhotos); + } } diff --git a/src/main/java/com/ddd/moodof/application/TrashPhotoService.java b/src/main/java/com/ddd/moodof/application/TrashPhotoService.java index 03a73dc..cc09acb 100644 --- a/src/main/java/com/ddd/moodof/application/TrashPhotoService.java +++ b/src/main/java/com/ddd/moodof/application/TrashPhotoService.java @@ -1,6 +1,7 @@ package com.ddd.moodof.application; import com.ddd.moodof.adapter.infrastructure.persistence.PaginationUtils; +import com.ddd.moodof.application.dto.StoragePhotoDTO; import com.ddd.moodof.application.dto.TrashPhotoDTO; import com.ddd.moodof.domain.model.trash.photo.TrashPhoto; import com.ddd.moodof.domain.model.trash.photo.TrashPhotoQueryRepository; @@ -11,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.stream.Collectors; @RequiredArgsConstructor @Service @@ -39,10 +41,31 @@ public TrashPhotoDTO.TrashPhotoPageResponse findPage(Long userId, int page, int } @Transactional - public void cancel(TrashPhotoDTO.CancelTrashPhotos request, Long userId) { - if (!trashPhotoRepository.existsByIdInAndUserId(request.getTrashPhotoIds(), userId)) { - throw new IllegalArgumentException("요청과 일치하는 휴지통 사진이 없습니다."); + public void deletePhoto(TrashPhotoDTO.TrashPhotosRequest request, Long userId) { + List trashPhotos = trashPhotoRepository.findAllById(request.getTrashPhotoIds()); + + List storagePhotoIds = trashPhotos.stream() + .map(TrashPhoto::getStoragePhotoId) + .collect(Collectors.toList()); + + storagePhotoService.delete(userId, new StoragePhotoDTO.DeleteStoragePhotos(storagePhotoIds)); + trashPhotoRepository.deleteAllByIdIn(request.getTrashPhotoIds()); + } + + @Transactional + public List restore(TrashPhotoDTO.TrashPhotosRequest request, Long userId) { + List trashPhotos = trashPhotoRepository.findAllById(request.getTrashPhotoIds()); + + if (trashPhotos.stream().anyMatch(trashPhoto -> trashPhoto.isUserNotEqual(userId))) { + throw new IllegalArgumentException("휴지통 사진의 userId가 요청과 다릅니다."); } + trashPhotoRepository.deleteAllByIdIn(request.getTrashPhotoIds()); + + List storagePhotoIds = trashPhotos.stream() + .map(TrashPhoto::getStoragePhotoId) + .collect(Collectors.toList()); + + return storagePhotoService.findAllById(storagePhotoIds); } } diff --git a/src/main/java/com/ddd/moodof/application/dto/StoragePhotoDTO.java b/src/main/java/com/ddd/moodof/application/dto/StoragePhotoDTO.java index 5cb7007..8a21c72 100644 --- a/src/main/java/com/ddd/moodof/application/dto/StoragePhotoDTO.java +++ b/src/main/java/com/ddd/moodof/application/dto/StoragePhotoDTO.java @@ -55,7 +55,7 @@ public static List listFrom(List storagePhot public static class StoragePhotoPageResponse { private long totalStoragePhotoCount; private long totalPageCount; - private List storagePhotos; + private List data; } @AllArgsConstructor diff --git a/src/main/java/com/ddd/moodof/application/dto/TrashPhotoDTO.java b/src/main/java/com/ddd/moodof/application/dto/TrashPhotoDTO.java index 3e3a297..b0adac4 100644 --- a/src/main/java/com/ddd/moodof/application/dto/TrashPhotoDTO.java +++ b/src/main/java/com/ddd/moodof/application/dto/TrashPhotoDTO.java @@ -64,14 +64,15 @@ public static TrashPhotoCreatedResponse of(TrashPhoto trashPhoto) { @Getter @AllArgsConstructor public static class TrashPhotoPageResponse { + private long totalTrashPhotoCount; private long totalPageCount; - private List responses; + private List data; } @NoArgsConstructor @Getter @AllArgsConstructor - public static class CancelTrashPhotos { + public static class TrashPhotosRequest { private List trashPhotoIds; } } diff --git a/src/main/java/com/ddd/moodof/domain/model/trash/photo/TrashPhoto.java b/src/main/java/com/ddd/moodof/domain/model/trash/photo/TrashPhoto.java index 3547080..f8fcb2c 100644 --- a/src/main/java/com/ddd/moodof/domain/model/trash/photo/TrashPhoto.java +++ b/src/main/java/com/ddd/moodof/domain/model/trash/photo/TrashPhoto.java @@ -30,4 +30,8 @@ public class TrashPhoto { @LastModifiedDate private LocalDateTime lastModifiedDate; + + public boolean isUserNotEqual(Long userId) { + return !this.userId.equals(userId); + } } diff --git a/src/test/java/com/ddd/moodof/acceptance/AcceptanceTest.java b/src/test/java/com/ddd/moodof/acceptance/AcceptanceTest.java index e103c27..800b975 100644 --- a/src/test/java/com/ddd/moodof/acceptance/AcceptanceTest.java +++ b/src/test/java/com/ddd/moodof/acceptance/AcceptanceTest.java @@ -17,6 +17,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -258,7 +259,7 @@ protected void deleteWithLogin(String uri, Long resourceId, Long userId) { } } - protected void deleteListWithLogin(String uri, T request, Long userId) { + protected void deleteListWithLogin(String uri, T request, Long userId, ResultMatcher expectResult) { try { String token = tokenProvider.createToken(userId); @@ -267,7 +268,7 @@ protected void deleteListWithLogin(String uri, T request, Long userId) { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .header(AUTHORIZATION, BEARER + token)) - .andExpect(MockMvcResultMatchers.status().isNoContent()) + .andExpect(expectResult) .andReturn(); } catch (Exception e) { diff --git a/src/test/java/com/ddd/moodof/acceptance/BoardPhotoAcceptanceTest.java b/src/test/java/com/ddd/moodof/acceptance/BoardPhotoAcceptanceTest.java index a16973b..b5ce6ce 100644 --- a/src/test/java/com/ddd/moodof/acceptance/BoardPhotoAcceptanceTest.java +++ b/src/test/java/com/ddd/moodof/acceptance/BoardPhotoAcceptanceTest.java @@ -6,6 +6,7 @@ import com.ddd.moodof.application.dto.StoragePhotoDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import java.util.List; import java.util.stream.Collectors; @@ -67,6 +68,6 @@ void setUp() { List boardPhotoIds = responses.stream() .map(BoardPhotoDTO.BoardPhotoResponse::getId) .collect(Collectors.toList()); - deleteListWithLogin(API_BOARD_PHOTO, new BoardPhotoDTO.RemoveBoardPhotos(boardPhotoIds), userId); + deleteListWithLogin(API_BOARD_PHOTO, new BoardPhotoDTO.RemoveBoardPhotos(boardPhotoIds), userId, MockMvcResultMatchers.status().isNoContent()); } } diff --git a/src/test/java/com/ddd/moodof/acceptance/StoragePhotoAcceptanceTest.java b/src/test/java/com/ddd/moodof/acceptance/StoragePhotoAcceptanceTest.java index 766d6b9..b0fbe8d 100644 --- a/src/test/java/com/ddd/moodof/acceptance/StoragePhotoAcceptanceTest.java +++ b/src/test/java/com/ddd/moodof/acceptance/StoragePhotoAcceptanceTest.java @@ -7,6 +7,7 @@ import com.ddd.moodof.application.dto.TagDTO; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.web.util.UriComponentsBuilder; import java.util.Collections; @@ -84,9 +85,9 @@ public class StoragePhotoAcceptanceTest extends AcceptanceTest { // then assertAll( - () -> assertThat(response.getStoragePhotos().size()).isEqualTo(3), - () -> assertThat(response.getStoragePhotos().get(0)).usingRecursiveComparison().isEqualTo(top), - () -> assertThat(response.getStoragePhotos().get(1)).usingRecursiveComparison().isEqualTo(second), + () -> assertThat(response.getData().size()).isEqualTo(3), + () -> assertThat(response.getData().get(0)).usingRecursiveComparison().isEqualTo(top), + () -> assertThat(response.getData().get(1)).usingRecursiveComparison().isEqualTo(second), () -> assertThat(response.getTotalPageCount()).isEqualTo(2), () -> assertThat(response.getTotalStoragePhotoCount()).isEqualTo(4) ); @@ -121,9 +122,9 @@ public class StoragePhotoAcceptanceTest extends AcceptanceTest { // then assertAll( - () -> assertThat(response.getStoragePhotos().size()).isEqualTo(2), - () -> assertThat(response.getStoragePhotos().get(0)).usingRecursiveComparison().isEqualTo(top), - () -> assertThat(response.getStoragePhotos().get(1)).usingRecursiveComparison().isEqualTo(second), + () -> assertThat(response.getData().size()).isEqualTo(2), + () -> assertThat(response.getData().get(0)).usingRecursiveComparison().isEqualTo(top), + () -> assertThat(response.getData().get(1)).usingRecursiveComparison().isEqualTo(second), () -> assertThat(response.getTotalPageCount()).isEqualTo(2), () -> assertThat(response.getTotalStoragePhotoCount()).isEqualTo(4) ); @@ -135,7 +136,7 @@ public class StoragePhotoAcceptanceTest extends AcceptanceTest { StoragePhotoDTO.StoragePhotoResponse storagePhotoResponse = 보관함사진_생성(userId, "uri", "color"); // when then - deleteListWithLogin(API_STORAGE_PHOTO, new StoragePhotoDTO.DeleteStoragePhotos(List.of(storagePhotoResponse.getId())), userId); + deleteListWithLogin(API_STORAGE_PHOTO, new StoragePhotoDTO.DeleteStoragePhotos(List.of(storagePhotoResponse.getId())), userId, MockMvcResultMatchers.status().isOk()); } @Test diff --git a/src/test/java/com/ddd/moodof/acceptance/TrashPhotoAcceptanceTest.java b/src/test/java/com/ddd/moodof/acceptance/TrashPhotoAcceptanceTest.java index 9aaf213..0c35cab 100644 --- a/src/test/java/com/ddd/moodof/acceptance/TrashPhotoAcceptanceTest.java +++ b/src/test/java/com/ddd/moodof/acceptance/TrashPhotoAcceptanceTest.java @@ -3,6 +3,7 @@ import com.ddd.moodof.application.dto.StoragePhotoDTO; import com.ddd.moodof.application.dto.TrashPhotoDTO; import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.web.util.UriComponentsBuilder; import java.util.List; @@ -58,14 +59,15 @@ public class TrashPhotoAcceptanceTest extends AcceptanceTest { // then assertAll( + () -> assertThat(response.getTotalTrashPhotoCount()).isEqualTo(3), () -> assertThat(response.getTotalPageCount()).isEqualTo(2), - () -> assertThat(response.getResponses().get(0).getStoragePhoto()).usingRecursiveComparison().isEqualTo(storagePhoto3), - () -> assertThat(response.getResponses().get(0).getId()).isEqualTo(top.get(0).getId()) + () -> assertThat(response.getData().get(0).getStoragePhoto()).usingRecursiveComparison().isEqualTo(storagePhoto3), + () -> assertThat(response.getData().get(0).getId()).isEqualTo(top.get(0).getId()) ); } @Test - void 휴지통의_사진을_복구한다() { + void 휴지통_사진을_삭제한다() { // given StoragePhotoDTO.StoragePhotoResponse storagePhoto = 보관함사진_생성(userId, "uri", "representativeColor"); List responses = 보관함사진_휴지통_이동(List.of(storagePhoto.getId()), userId); @@ -75,6 +77,23 @@ public class TrashPhotoAcceptanceTest extends AcceptanceTest { .collect(Collectors.toList()); // when then - deleteListWithLogin(API_TRASH_PHOTO, new TrashPhotoDTO.CancelTrashPhotos(trashPhotoIds), userId); + deleteListWithLogin(API_TRASH_PHOTO, new TrashPhotoDTO.TrashPhotosRequest(trashPhotoIds), userId, MockMvcResultMatchers.status().isNoContent()); + } + + @Test + void 휴지통_사진을_복구한다() { + // given + StoragePhotoDTO.StoragePhotoResponse storagePhoto = 보관함사진_생성(userId, "uri", "representativeColor"); + List trashPhotos = 보관함사진_휴지통_이동(List.of(storagePhoto.getId()), userId); + + List trashPhotoIds = trashPhotos.stream() + .map(TrashPhotoDTO.TrashPhotoCreatedResponse::getId) + .collect(Collectors.toList()); + + // when + List responses = postListWithLogin(new TrashPhotoDTO.TrashPhotosRequest(trashPhotoIds), API_TRASH_PHOTO + "/restore", StoragePhotoDTO.StoragePhotoResponse.class, userId); + + // then + assertThat(responses.get(0)).usingRecursiveComparison().isEqualTo(storagePhoto); } }