From 22bca2354198b887bf7a867b9b4a88a534b36103 Mon Sep 17 00:00:00 2001 From: daolove0323 Date: Fri, 15 Nov 2024 07:37:07 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Reservation=20=EC=9C=A0=EB=8B=9B?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wouldyouin/_common/util/FileUtil.java | 14 + .../wouldyouin/_common/vo/Category.java | 2 +- .../api/AdvertisementController.java | 10 - .../application/AdvertisementService.java | 32 +- .../application/CurationCardService.java | 2 +- .../wouldyouin/image/api/ImageController.java | 6 +- .../AdvertisementImageService.java | 8 +- .../application/CurationImageService.java | 6 +- .../image/application/EventImageService.java | 9 +- .../image/application/ImageService.java | 33 +-- .../application/ImageStorageService.java | 18 +- .../image/application/MemberImageService.java | 9 +- .../CurationCuratorResponse.java | 2 + .../payment/application/PaymentService.java | 1 - .../api/ReservationController.java | 19 +- .../api/dto/KakaoPayReservationResponse.java | 24 ++ .../application/ReservationService.java | 64 ++-- .../persist/ReservationRepository.java | 7 +- src/main/resources/application.yml | 1 - .../_global/testdata/AdvertisementData.java | 5 + .../_global/testdata/CurationData.java | 35 ++- .../_global/testdata/ReservationData.java | 41 +++ .../curation/CurationCardServiceTest.java | 85 ++++++ .../curation/CurationServiceTest.java | 46 +-- .../image/CurationImageServiceTest.java | 66 +++++ .../wouldyouin/image/ImageControllerTest.java | 19 +- .../ReservationControllerUnitTest.java | 15 +- .../ReservationServiceUnitTest.java | 275 +++++++++++------- 28 files changed, 577 insertions(+), 277 deletions(-) create mode 100644 src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/KakaoPayReservationResponse.java create mode 100644 src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/AdvertisementData.java create mode 100644 src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationCardServiceTest.java create mode 100644 src/test/java/org/ktc2/cokaen/wouldyouin/image/CurationImageServiceTest.java diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/util/FileUtil.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/util/FileUtil.java index e0970c02..c5987467 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/util/FileUtil.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/util/FileUtil.java @@ -1,9 +1,12 @@ package org.ktc2.cokaen.wouldyouin._common.util; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.UUID; +import net.coobird.thumbnailator.Thumbnails; import org.ktc2.cokaen.wouldyouin._common.exception.ExtensionParsingException; import org.ktc2.cokaen.wouldyouin._common.exception.FailToReadImageException; import org.ktc2.cokaen.wouldyouin._common.exception.FailedToUploadImageException; @@ -37,6 +40,17 @@ public static void saveFile(byte[] file, Path path) { } } + public static void createThumbnail(String originalPath, String originFileName, String thumbnailPath, int width, int height) { + try { + Files.createDirectories(Paths.get(thumbnailPath)); + Thumbnails.of(new File(originalPath)) + .size(height, width) + .toFile(new File(thumbnailPath, originFileName)); + } catch (IOException e) { + throw new FailedToUploadImageException("썸네일을 생성하는데 실패했습니다."); + } + } + public static void deleteFile(Path path) { try { Files.deleteIfExists(path); diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/vo/Category.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/vo/Category.java index 55593840..1a796347 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/vo/Category.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/vo/Category.java @@ -4,8 +4,8 @@ public enum Category { 전체, 밴드, 연극, - 뮤지컬, 원데이클래스, + 뮤지컬, 전시회, 공예, 축제 diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/api/AdvertisementController.java b/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/api/AdvertisementController.java index 92487102..65f7ee62 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/api/AdvertisementController.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/api/AdvertisementController.java @@ -49,16 +49,6 @@ public ResponseEntity> createAdvertisemen return ApiResponse.created(advertisementService.create(advertisementRequest, image)); } - @PutMapping(path = "/{adId}", consumes = {MediaType.APPLICATION_JSON_VALUE, - MediaType.MULTIPART_FORM_DATA_VALUE}) - public ResponseEntity> updateAdvertisement( - @PathVariable Long adId, - @Valid @RequestPart AdvertisementRequest advertisementRequest, - @RequestPart(required = false) MultipartFile image, - @Authorize(MemberType.admin) MemberIdentifier identifier) { - return ApiResponse.ok(advertisementService.update(identifier, adId, advertisementRequest, image)); - } - @DeleteMapping("/{adId}") public ResponseEntity> deleteAdvertisement( @PathVariable Long adId, diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/application/AdvertisementService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/application/AdvertisementService.java index e033ea58..3a7fca03 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/application/AdvertisementService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/advertisement/application/AdvertisementService.java @@ -23,12 +23,6 @@ public class AdvertisementService { private final AdvertisementRepository adRepository; private final AdvertisementImageService adImageService; - @Transactional - public Advertisement getByIdOrThrow(Long adId) { - return adRepository.findById(adId) - .orElseThrow(() -> new EntityNotFoundException("해당하는 광고를 찾을 수 없습니다.")); - } - @Transactional(readOnly = true) public AdvertisementResponse getById(Long adId) { return AdvertisementResponse.from(getByIdOrThrow(adId)); @@ -40,7 +34,6 @@ public List getAllActiveAdvertisements() { .map(AdvertisementResponse::from).toList(); } - // Todo: 롤백될 경우, 저장한 이미지 삭제 @Transactional public AdvertisementResponse create(AdvertisementRequest adRequest, MultipartFile image) { AdvertisementImage adImage = adImageService.saveImage(image); @@ -49,30 +42,15 @@ public AdvertisementResponse create(AdvertisementRequest adRequest, MultipartFil return AdvertisementResponse.from(ad); } - // Todo: 수정될 때 이미지가 null인 경우 기존 이미지로 대체하는 로직 프론트와 협의 - // Todo: 롤백될 경우, 저장한 이미지 삭제 - @Transactional - public AdvertisementResponse update(MemberIdentifier identifier, Long adId, AdvertisementRequest adRequest, MultipartFile multipartFile) { - Advertisement ad = getByIdOrThrow(adId); - Optional.ofNullable(multipartFile).ifPresentOrElse( - image -> { - adImageService.deleteImage(identifier, ad.getAdvertisementImage().getId()); - AdvertisementImage adImage = adImageService.saveImage(image); - ad.updateFrom(adRequest, adImage); - adImage.setAdvertisement(ad); - }, - () -> { - AdvertisementImage adImage = ad.getAdvertisementImage(); - ad.updateFrom(adRequest, adImage); - } - ); - return AdvertisementResponse.from(ad); - } - @Transactional public void delete(MemberIdentifier identifier, Long adId) { Advertisement ad = getByIdOrThrow(adId); adImageService.deleteImage(identifier, ad.getAdvertisementImage().getId()); adRepository.deleteById(adId); } + + private Advertisement getByIdOrThrow(Long adId) { + return adRepository.findById(adId) + .orElseThrow(() -> new EntityNotFoundException("해당하는 광고를 찾을 수 없습니다.")); + } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationCardService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationCardService.java index ea73311b..b4503b13 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationCardService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationCardService.java @@ -52,7 +52,7 @@ private CurationCard getByIdOrThrow(Long id) { return curationCardRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("해당하는 큐레이션 카드를 찾을 수 없습니다.")); } - public List getImageUrls(CurationCard curationCard) { + private List getImageUrls(CurationCard curationCard) { return curationCard.getCurationImages().stream() .map(curationImageService::getImageUrl) .toList(); diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/api/ImageController.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/api/ImageController.java index bf02c6c5..587f710e 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/api/ImageController.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/api/ImageController.java @@ -11,7 +11,6 @@ import org.ktc2.cokaen.wouldyouin.image.application.ImageServiceFactory; import org.ktc2.cokaen.wouldyouin.image.application.ImageStorageService; import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; -import org.ktc2.cokaen.wouldyouin.payment.application.PaymentService; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -31,7 +30,6 @@ public class ImageController { private final ImageServiceFactory imageServiceFactory; private final ImageStorageService imageStorageService; - private final PaymentService paymentService; @GetMapping(value = "/{directory}/{file}", produces = {MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE}) public ResponseEntity getImage(@PathVariable String directory, @PathVariable String file) { @@ -39,7 +37,8 @@ public ResponseEntity getImage(@PathVariable String directory, @PathVari } @GetMapping(value = "/{directory}/{thumbnail}/{file}", produces = {MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE}) - public ResponseEntity getThumnailImage(@PathVariable String directory, @PathVariable String thumbnail, @PathVariable String file) { + public ResponseEntity getThumnailImage(@PathVariable String directory, @PathVariable String thumbnail, + @PathVariable String file) { return ResponseEntity.status(HttpStatus.OK).body(imageStorageService.readFromDirectory(Paths.get(directory, thumbnail, file))); } @@ -51,7 +50,6 @@ public ResponseEntity>> uploadImages( return ApiResponse.ok(imageServiceFactory.getImageService(imageDomain).saveImages(images)); } - // Todo: 삭제로직 수정필요 @DeleteMapping("/{imageId}") public ResponseEntity> deleteImage( @PathVariable Long imageId, diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/AdvertisementImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/AdvertisementImageService.java index ca3245e6..69531a6d 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/AdvertisementImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/AdvertisementImageService.java @@ -16,14 +16,17 @@ import org.springframework.web.multipart.MultipartFile; @Service -@RequiredArgsConstructor public class AdvertisementImageService extends ImageService { @Value("${image.upload.ad.child-path}") private String childPath; - private final ImageStorageService imageStorageService; private final AdvertisementImageRepository adImageRepository; + public AdvertisementImageService(ImageStorageService imageStorageService, AdvertisementImageRepository adImageRepository) { + this.imageStorageService = imageStorageService; + this.adImageRepository = adImageRepository; + } + @Override protected ImageRepository getImageRepository() { return adImageRepository; @@ -48,7 +51,6 @@ protected AdvertisementImage mapToEntityFrom(ImageRequest imageRequest) { .build(); } - // TODO : ad image 삭제 인가 @Override protected void validateMemberId(MemberIdentifier identifier, AdvertisementImage image) { if (!identifier.type().equals(MemberType.admin)) { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/CurationImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/CurationImageService.java index 07a82d20..03bf57cc 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/CurationImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/CurationImageService.java @@ -15,13 +15,17 @@ import org.springframework.transaction.annotation.Transactional; @Service -@RequiredArgsConstructor public class CurationImageService extends ImageService { @Value("${image.upload.curation.child-path}") private String childPath; private final CurationImageRepository curationImageRepository; + public CurationImageService(ImageStorageService imageStorageService, CurationImageRepository curationImageRepository) { + this.imageStorageService = imageStorageService; + this.curationImageRepository = curationImageRepository; + } + @Override public ImageRepository getImageRepository() { return curationImageRepository; diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/EventImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/EventImageService.java index 1b5bc460..5a57e580 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/EventImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/EventImageService.java @@ -14,7 +14,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -@RequiredArgsConstructor @Service public class EventImageService extends ImageService { @@ -22,6 +21,11 @@ public class EventImageService extends ImageService { private String childPath; private final EventImageRepository eventImageRepository; + public EventImageService(ImageStorageService imageStorageService, EventImageRepository eventImageRepository) { + this.imageStorageService = imageStorageService; + this.eventImageRepository = eventImageRepository; + } + @Override public ImageRepository getImageRepository() { return eventImageRepository; @@ -48,7 +52,8 @@ protected EventImage mapToEntityFrom(ImageRequest imageRequest) { @Override protected void validateMemberId(MemberIdentifier identifier, EventImage image) { - if (!identifier.type().equals(MemberType.admin) && !identifier.id().equals(image.getEvent().getHost().getId())) { + if (!identifier.type().equals(MemberType.admin) && + !identifier.id().equals(image.getEvent().getHost().getId())) { throw new UnauthorizedException("해당 이벤트 이미지에 접근할 권한이 없습니다."); } } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageService.java index 616f6ddc..bee01f80 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageService.java @@ -10,7 +10,6 @@ import org.ktc2.cokaen.wouldyouin.image.api.dto.ImageResponse; import org.ktc2.cokaen.wouldyouin.image.persist.Image; import org.ktc2.cokaen.wouldyouin.image.persist.ImageRepository; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,11 +19,10 @@ @RequiredArgsConstructor public abstract class ImageService { - @Autowired protected ImageStorageService imageStorageService; @Value("${image.api-url}") - private String parentPath; + private String imageApiHeader; protected abstract ImageRepository getImageRepository(); @@ -36,6 +34,12 @@ public abstract class ImageService { protected abstract void validateMemberId(MemberIdentifier identifier, T image); + @Transactional(readOnly = true) + public T getById(Long id) { + return getImageRepository().findById(id) + .orElseThrow(() -> new EntityNotFoundException(getImageDomain().name() + " 이미지를 찾을 수 없습니다.")); + } + @Transactional public List saveImages(List images) { return images.stream() @@ -43,23 +47,12 @@ public List saveImages(List images) { .toList(); } - // TODO: delete 이미지 바꿔야함 @Transactional public void deleteImage(MemberIdentifier identifier, Long imageId) { T image = getById(imageId); validateMemberId(identifier, image); delete(imageId); imageStorageService.delete(getChildPath(), image.getName()); - // ToDO : 썸네일도 지워라 - } - - public String createThumbnail(String fileName) { - return imageStorageService.createThumbnailImage(parentPath, getChildPath(), fileName); - } - - public T getById(Long id) { - return getImageRepository().findById(id) - .orElseThrow(() -> new EntityNotFoundException(getImageDomain().name() + " 이미지를 찾을 수 없습니다.")); } protected ImageResponse create(ImageRequest imageRequest) { @@ -67,12 +60,16 @@ protected ImageResponse create(ImageRequest imageRequest) { return ImageResponse.from(getImageRepository().save(image), getImageUrl(image)); } - public String getImageUrl(T image) { - return UriUtil.assembleFullUrl(parentPath, getChildPath(), image.getName()); - } - protected void delete(Long id) { getById(id); getImageRepository().deleteById(id); } + + public String createThumbnail(String fileName) { + return imageStorageService.createThumbnailImage(imageApiHeader, getChildPath(), fileName); + } + + public String getImageUrl(T image) { + return UriUtil.assembleFullUrl(imageApiHeader, getChildPath(), image.getName()); + } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageStorageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageStorageService.java index 52886c4b..425adf3e 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageStorageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/ImageStorageService.java @@ -34,9 +34,6 @@ public class ImageStorageService { @Value("${image.upload.thumbnail.width}") private Integer thumbnailWidth; - @Value("${image.upload.thumbnail.extension}") - private String thumbnailExtension; - private final RestClientUtil client; public byte[] readFromDirectory(Path childPath) { @@ -56,7 +53,6 @@ public ImageRequest saveToDirectory(String imageUrl, String childPath) { throw new FailedToUploadImageException("이미지 URL에 대한 요청을 실패하였습니다."); } ); - String contentType = Optional.ofNullable(response.getHeaders().getContentType()).orElseThrow( () -> new FailedToUploadImageException("응답 헤더에 콘텐츠 타입이 없어 이미지를 가져올 수 없습니다.") ).toString(); @@ -71,23 +67,15 @@ public ImageRequest saveToDirectory(String imageUrl, String childPath) { return ImageRequest.of(fileName, (long)body.length, extension); } - // TODO : 썸네일 생성 코드 리팩토링, 파일 유틸로 이동 public String createThumbnailImage(String apiHeader, String childPath, String originFileName) { - String fileName = originFileName; String originImagePath = Path.of(parentPath, childPath, originFileName).toString(); String thumbnailImagePath = Path.of(parentPath, childPath, thumbnailChildPath).toString(); - try { - Files.createDirectories(Paths.get(thumbnailImagePath)); - Thumbnails.of(new File(originImagePath)) - .size(thumbnailHeight, thumbnailWidth) - .toFile(new File(thumbnailImagePath, fileName)); - } catch (Exception e) { - throw new RuntimeException(e); - } - return UriUtil.assembleFullUrl(apiHeader,childPath, thumbnailChildPath, fileName); + FileUtil.createThumbnail(originImagePath, originFileName, thumbnailImagePath, thumbnailWidth, thumbnailHeight); + return UriUtil.assembleFullUrl(apiHeader,childPath, thumbnailChildPath, originFileName); } public void delete(String childPath, String fileName) { FileUtil.deleteFile(Path.of(parentPath, childPath, fileName)); + FileUtil.deleteFile(Path.of(parentPath, childPath, thumbnailChildPath, fileName)); } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/MemberImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/MemberImageService.java index efb20a97..d2656222 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/MemberImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/image/application/MemberImageService.java @@ -15,13 +15,17 @@ import org.springframework.transaction.annotation.Transactional; @Service -@RequiredArgsConstructor public class MemberImageService extends ImageService { @Value("${image.upload.member.child-path}") private String childPath; private final MemberImageRepository memberImageRepository; + public MemberImageService(ImageStorageService imageStorageService, MemberImageRepository memberImageRepository) { + this.imageStorageService = imageStorageService; + this.memberImageRepository = memberImageRepository; + } + @Override public ImageRepository getImageRepository() { return memberImageRepository; @@ -48,7 +52,8 @@ protected MemberImage mapToEntityFrom(ImageRequest imageRequest) { @Override protected void validateMemberId(MemberIdentifier identifier, MemberImage image) { - if (!identifier.type().equals(MemberType.admin) && !identifier.id().equals(image.getBaseMember().getId())) { + if (!identifier.type().equals(MemberType.admin) && + !identifier.id().equals(image.getBaseMember().getId())) { throw new UnauthorizedException("해당 프로필 이미지에 접근할 권한이 없습니다."); } } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/member/api/dto/relationResponse/CurationCuratorResponse.java b/src/main/java/org/ktc2/cokaen/wouldyouin/member/api/dto/relationResponse/CurationCuratorResponse.java index 651d748c..4a678b35 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/member/api/dto/relationResponse/CurationCuratorResponse.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/member/api/dto/relationResponse/CurationCuratorResponse.java @@ -13,6 +13,7 @@ @ToString public class CurationCuratorResponse { + private Long curatorId; private String nickname; private String email; private String phone; @@ -24,6 +25,7 @@ public class CurationCuratorResponse { public static CurationCuratorResponse from(Curator curator) { return CurationCuratorResponse.builder() + .curatorId(curator.getId()) .nickname(curator.getNickname()) .email(curator.getEmail()) .phone(curator.getPhone()) diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/payment/application/PaymentService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/payment/application/PaymentService.java index 2649750e..8327adc4 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/payment/application/PaymentService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/payment/application/PaymentService.java @@ -38,5 +38,4 @@ public KakaoPayResponse createPayment(KakaoPayRequest kakaoPayRequest) { (req, rsp) -> { throw new FailedToPayException("카카오페이 API 요청을 실패하였습니다."); } ); } - // Todo: pay 취소 기능 추가 } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/ReservationController.java b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/ReservationController.java index 256be9ef..462c9db4 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/ReservationController.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/ReservationController.java @@ -9,6 +9,7 @@ import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier; import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; import org.ktc2.cokaen.wouldyouin.payment.dto.KakaoPayResponse; +import org.ktc2.cokaen.wouldyouin.reservation.api.dto.KakaoPayReservationResponse; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationRequest; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationResponse; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationSliceResponse; @@ -33,11 +34,11 @@ public class ReservationController { @GetMapping public ResponseEntity> getReservationsByMemberId( - @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier member, + @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier identifier, @RequestParam(defaultValue = ParamDefaults.PAGE) Integer page, @RequestParam(defaultValue = ParamDefaults.PAGE_SIZE) Integer size, @RequestParam(defaultValue = ParamDefaults.LAST_ID) Long lastId) { - return ApiResponse.ok(reservationService.getAllByMemberId(member.id(), PageRequest.of(page, size), lastId)); + return ApiResponse.ok(reservationService.getAllByMemberId(identifier, PageRequest.of(page, size), lastId)); } @GetMapping("/events/{eventId}") @@ -57,24 +58,24 @@ public ResponseEntity> getReservationById( } @PostMapping - public ResponseEntity> createReservation( + public ResponseEntity> createReservation( @Valid @RequestBody ReservationRequest reservationRequest, - @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier member) { - return ApiResponse.created(reservationService.create(member.id(), reservationRequest)); + @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier identifier) { + return ApiResponse.created(reservationService.create(identifier, reservationRequest)); } @PostMapping("/test") public ResponseEntity> createTestReservation( @Valid @RequestBody ReservationRequest reservationRequest, - @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier member) { - return ApiResponse.created(reservationService.createTest(member.id(), reservationRequest)); + @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier identifier) { + return ApiResponse.created(reservationService.createTest(identifier, reservationRequest)); } @DeleteMapping("/{reservationId}") public ResponseEntity> deleteReservation( @PathVariable Long reservationId, - @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier member) { - reservationService.delete(member.id(), reservationId); + @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier identifier) { + reservationService.delete(identifier, reservationId); return ApiResponse.noContent(); } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/KakaoPayReservationResponse.java b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/KakaoPayReservationResponse.java new file mode 100644 index 00000000..599368aa --- /dev/null +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/KakaoPayReservationResponse.java @@ -0,0 +1,24 @@ +package org.ktc2.cokaen.wouldyouin.reservation.api.dto; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import org.ktc2.cokaen.wouldyouin.payment.dto.KakaoPayResponse; + +@Builder +@Getter +@EqualsAndHashCode +@ToString +public class KakaoPayReservationResponse { + + private ReservationResponse reservationResponse; + private KakaoPayResponse kakaoPayResponse; + + public static KakaoPayReservationResponse from(ReservationResponse reservationResponse, KakaoPayResponse kakaoPayResponse) { + return KakaoPayReservationResponse.builder() + .reservationResponse(reservationResponse) + .kakaoPayResponse(kakaoPayResponse) + .build(); + } +} diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java index 85bbd555..30a79660 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java @@ -1,5 +1,6 @@ package org.ktc2.cokaen.wouldyouin.reservation.application; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException; import org.ktc2.cokaen.wouldyouin._common.exception.ReservationNotFoundForReviewException; @@ -10,6 +11,7 @@ import org.ktc2.cokaen.wouldyouin.payment.application.PaymentService; import org.ktc2.cokaen.wouldyouin.payment.dto.KakaoPayRequest; import org.ktc2.cokaen.wouldyouin.payment.dto.KakaoPayResponse; +import org.ktc2.cokaen.wouldyouin.reservation.api.dto.KakaoPayReservationResponse; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationRequest; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationResponse; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationSliceResponse; @@ -29,27 +31,15 @@ public class ReservationService { private final MemberService memberService; private final EventService eventService; - @Transactional - public Reservation getByIdOrThrow(Long id) { - return reservationRepository.findById(id) - .orElseThrow(() -> new EntityNotFoundException("해당하는 예약을 찾을 수 없습니다.")); - } - @Transactional(readOnly = true) public ReservationResponse getById(Long id) { return ReservationResponse.from(getByIdOrThrow(id)); } - @Transactional - public void validateByMemberIdAndEventId(Long memberId, Long eventId) { - if (reservationRepository.findByMemberIdAndEventId(memberId, eventId) == null) { - throw new ReservationNotFoundForReviewException("해당 이벤트에 대한 리뷰를 작성할 자격이 없습니다."); - } - } - @Transactional(readOnly = true) - public ReservationSliceResponse getAllByMemberId(Long memberId, Pageable pageable, Long oldLastId) { - Slice reservations = reservationRepository.findByMemberIdOrderByReservationIdDesc(memberId, oldLastId, pageable); + public ReservationSliceResponse getAllByMemberId(MemberIdentifier identifier, Pageable pageable, Long oldLastId) { + Slice reservations = + reservationRepository.findByMemberIdOrderByReservationIdDesc(identifier.id(), oldLastId, pageable); Long newLastId = getLastId(reservations, oldLastId); return ReservationSliceResponse.from(reservations, reservations.getSize(), newLastId); } @@ -62,41 +52,55 @@ public ReservationSliceResponse getAllByEventId(MemberIdentifier identifier, Lon return ReservationSliceResponse.from(reservations, reservations.getSize(), newLastId); } - private Long getLastId(Slice reservations, Long oldLastId) { - if (reservations.hasContent()) { - return reservations.getContent().getLast().getId(); - } - return oldLastId; - } - @Transactional - public KakaoPayResponse create(Long memberId, ReservationRequest reservationRequest) { + public KakaoPayReservationResponse create(MemberIdentifier identifier, ReservationRequest reservationRequest) { Reservation reservation = reservationRepository.save(reservationRequest.toEntity( - memberService.getByIdOrThrow(memberId), + memberService.getByIdOrThrow(identifier.id()), eventService.getByIdOrThrow(reservationRequest.getEventId())) ); eventService.decreaseLeftSeat(reservation.getEvent().getId(), reservationRequest.getQuantity()); - return paymentService.createPayment(KakaoPayRequest.from(reservation)); + KakaoPayResponse kakaoPayResponse = paymentService.createPayment(KakaoPayRequest.from(reservation)); + ReservationResponse reservationResponse = ReservationResponse.from(reservation); + return KakaoPayReservationResponse.from(reservationResponse, kakaoPayResponse); } @Transactional - public ReservationResponse createTest(Long memberId, ReservationRequest reservationRequest) { + public ReservationResponse createTest(MemberIdentifier identifier, ReservationRequest reservationRequest) { Reservation reservation = reservationRepository.save(reservationRequest.toEntity( - memberService.getByIdOrThrow(memberId), + memberService.getByIdOrThrow(identifier.id()), eventService.getByIdOrThrow(reservationRequest.getEventId()))); eventService.decreaseLeftSeat(reservation.getEvent().getId(), reservationRequest.getQuantity()); return ReservationResponse.from(reservation); } @Transactional - public void delete(Long memberId, Long reservationId) { - validateMemberId(memberId, getByIdOrThrow(reservationId)); + public void delete(MemberIdentifier identifier, Long reservationId) { + validateMemberId(identifier.id(), getByIdOrThrow(reservationId)); reservationRepository.deleteById(reservationId); } + @Transactional(readOnly = true) + public void validateByMemberIdAndEventId(Long memberId, Long eventId) { + if (reservationRepository.findByMemberIdAndEventId(memberId, eventId).isEmpty()) { + throw new ReservationNotFoundForReviewException("해당 이벤트에 대한 리뷰를 작성할 수 없습니다."); + } + } + + private Reservation getByIdOrThrow(Long id) { + return reservationRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("해당하는 예약을 찾을 수 없습니다.")); + } + + private Long getLastId(Slice reservations, Long oldLastId) { + if (reservations.hasContent()) { + return reservations.getContent().getLast().getId(); + } + return oldLastId; + } + private void validateMemberId(Long memberId, Reservation reservation) { if (!memberId.equals(reservation.getMember().getId())) { - throw new UnauthorizedException("member ID가 예약의 member ID와 일치하지 않습니다."); + throw new UnauthorizedException("사용자 ID가 예약한 사용자 ID와 일치하지 않습니다."); } } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/persist/ReservationRepository.java b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/persist/ReservationRepository.java index 5c7b726b..4739e588 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/persist/ReservationRepository.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/persist/ReservationRepository.java @@ -1,5 +1,6 @@ package org.ktc2.cokaen.wouldyouin.reservation.persist; +import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -19,5 +20,9 @@ public interface ReservationRepository extends JpaRepository + "ORDER BY R.id DESC") Slice findByEventIdOrderByReservationIdDesc(Long eventId, Long lastId, Pageable pageable); - Reservation findByMemberIdAndEventId(Long memberId, Long eventId); + @Query("SELECT R FROM Reservation R JOIN FETCH R.member JOIN FETCH R.event " + + "WHERE R.member.id = :memberId " + + "AND R.event.id = :eventId " + + "AND R.event.endTime > CURRENT_TIMESTAMP") + List findByMemberIdAndEventId(Long memberId, Long eventId); } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fef18b26..258d67c9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -56,7 +56,6 @@ image: child-path: thumbnail width: 130 height: 90 - extension: png # for jwt jwt: diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/AdvertisementData.java b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/AdvertisementData.java new file mode 100644 index 00000000..7a7583d7 --- /dev/null +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/AdvertisementData.java @@ -0,0 +1,5 @@ +package org.ktc2.cokaen.wouldyouin._global.testdata; + +public class AdvertisementData { + +} diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/CurationData.java b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/CurationData.java index 6f0e7f41..f084180f 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/CurationData.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/CurationData.java @@ -5,6 +5,7 @@ import java.time.LocalDateTime; import java.util.List; import org.ktc2.cokaen.wouldyouin._common.vo.Area; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.curation1.entity; import org.ktc2.cokaen.wouldyouin._global.testdata.EventData.R.event1; import org.ktc2.cokaen.wouldyouin._global.testdata.EventData.R.event2; import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCardRequest; @@ -32,7 +33,7 @@ public static class curationCard1 { public static final String subtitle = "큐레이션 카드 부제목1"; public static final String content = "큐레이션 카드 내용1 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다."; public static final List images = List.of(ImageData.curation1.entity.get()); - public static final List imageIds = List.of(1301L); + public static final List imageIds = List.of(ImageData.R.curation1.id); public static final List imageUrls = List.of(ImageData.R.curation1.url); } @@ -41,7 +42,8 @@ public static class curationCard2 { public static final Long id = 352L; public static final String subtitle = "큐레이션 카드 부제목2"; public static final String content = "큐레이션 카드 내용2 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다."; - public static final List imageIds = List.of(1302L); + public static final List images = List.of(ImageData.curation2.entity.get()); + public static final List imageIds = List.of(ImageData.R.curation2.id); public static final List imageUrls = List.of(ImageData.R.curation2.url); } @@ -86,8 +88,8 @@ public static class curation2 { CurationData.curationCard2.response.get() ); public static final int page = 0; - public static final int pageSize = 10; - public static final Long lastId = 100L; + public static final int pageSize = 20; + public static final Long lastId = 50L; public static final PageRequest pageable = PageRequest.of(0, 10); public static final Area area = Area.광주; public static final List hashtags = List.of("수정 해시태그"); @@ -114,6 +116,17 @@ public static CurationCard get() { } } + public static class entityWithNoId { + + public static CurationCard get() { + return CurationCard.builder() + .subtitle(R.curationCard1.subtitle) + .content(R.curationCard1.content) + .images(R.curationCard1.images) + .build(); + } + } + public static class entityWithNoCuration { public static CurationCard get() { @@ -223,13 +236,6 @@ public static Curation get() { } } - public static class CurationSlice { - - public static Slice get() { - return new SliceImpl<>(List.of(entity.get()), R.curation1.pageable, true); - } - } - public static class entityWithNoCurator { public static Curation get() { @@ -385,4 +391,11 @@ public static CurationResponse get() { } } } + + public static class CurationSlice { + + public static Slice get() { + return new SliceImpl<>(List.of(entity.get()), R.curation1.pageable, true); + } + } } \ No newline at end of file diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/ReservationData.java b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/ReservationData.java index 649fd88f..2933e1e9 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/ReservationData.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/testdata/ReservationData.java @@ -2,21 +2,35 @@ import java.time.LocalDateTime; import java.util.List; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.R; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.curation1.entity; import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData.response.reservation1Member1; +import org.ktc2.cokaen.wouldyouin.curation.persist.Curation; import org.ktc2.cokaen.wouldyouin.event.api.dto.relationResonse.ReservationEventResponse; import org.ktc2.cokaen.wouldyouin.event.persist.Event; import org.ktc2.cokaen.wouldyouin.member.api.dto.relationResponse.ReservationMemberResponse; import org.ktc2.cokaen.wouldyouin.member.persist.Member; +import org.ktc2.cokaen.wouldyouin.payment.dto.KakaoPayResponse; +import org.ktc2.cokaen.wouldyouin.reservation.api.dto.KakaoPayReservationResponse; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationRequest; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationResponse; import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationSliceResponse; import org.ktc2.cokaen.wouldyouin.reservation.persist.Reservation; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; import org.springframework.test.util.ReflectionTestUtils; public class ReservationData { public static class R { + public static final int page = 0; + public static final int pageSize = 10; + public static final Long lastId = 1L; + public static final Pageable pageable = PageRequest.of(0, 10); + public static class reservation1 { public static final Long id = 1L; @@ -74,6 +88,33 @@ public static ReservationResponse get() { } } + + public static class kakaoPayResponse { + + public static KakaoPayResponse get() { + return new KakaoPayResponse( + "10L", "nextRedirectAppUrl", "nextRedirectMobileUrl", + "nextRedirectPcUrl", "androidAppScheme", "ios_app_scheme", + LocalDateTime.of(2024, 3, 23, 0, 0)); + } + } + + public static class kakaoPayReservationResponse { + + public static ReservationResponse reservationResponse = ReservationResponse.from(reservation1.entity.get()); + public static KakaoPayResponse kakaoPayResponse = ReservationData.reservation1.kakaoPayResponse.get(); + + public static KakaoPayReservationResponse get() { + return KakaoPayReservationResponse.from(reservationResponse, kakaoPayResponse); + } + } + } + + public static class ReservationSlice { + + public static Slice get() { + return new SliceImpl<>(List.of(ReservationData.reservation1.entity.get()), PageRequest.of(0, 10), true); + } } public static class sliceResponse { diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationCardServiceTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationCardServiceTest.java new file mode 100644 index 00000000..8bd2efda --- /dev/null +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationCardServiceTest.java @@ -0,0 +1,85 @@ +package org.ktc2.cokaen.wouldyouin.curation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; + +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.R.curationCard1; +import org.ktc2.cokaen.wouldyouin._global.testdata.ImageData; +import org.ktc2.cokaen.wouldyouin._global.testdata.ImageData.R.curation1; +import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData.R.curator1; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCardResponse; +import org.ktc2.cokaen.wouldyouin.curation.application.CurationCardService; +import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCard; +import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCardRepository; +import org.ktc2.cokaen.wouldyouin.image.application.CurationImageService; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class CurationCardServiceTest { + + private CurationCardService curationCardService; + + @Mock + private CurationCardRepository curationCardRepository; + + @Mock + private CurationImageService curationImageService; + + @BeforeEach + void setUp() { + curationCardService = new CurationCardService(curationCardRepository, curationImageService); + } + + @Test + @DisplayName("큐레이션 ID를 통해 해당 하는 큐레이션을 반환한다.") + void getById() { + // given + given(curationCardRepository.findById(curationCard1.id)).willReturn(Optional.of(CurationData.curationCard1.entity.get())); + given(curationImageService.getImageUrl(ImageData.curation1.entity.get())).willReturn(ImageData.R.curation1.url); + + // when + CurationCardResponse response = curationCardService.getById(curationCard1.id); + + // then + assertThat(response).isEqualTo(CurationData.curationCard1.response.get()); + } + + @Test + @DisplayName("큐레이션 카드 DTO를 통해 큐레이션 카드를 생성한다.") + void create() { + // given + given(curationImageService.getById(curation1.id)).willReturn(ImageData.curation1.entity.get()); + given(curationCardRepository.save(CurationData.curationCard1.entityWithNoId.get())) + .willReturn(CurationData.curationCard1.entity.get()); + + // when + CurationCard response = curationCardService.create(CurationData.curationCard1.request.get()); + + // then + assertThat(response).isEqualTo(CurationData.curationCard1.entity.get()); + } + + @Test + @DisplayName("큐레이션 카 ID를 통해 해당 하는 큐레이션 카드를 삭제한다.") + void delete() { + // given + given(curationCardRepository.findById(curationCard1.id)).willReturn(Optional.of(CurationData.curationCard1.entity.get())); + + // when + curationCardService.delete(curator1.memberIdentifier, curationCard1.id); + + // then + then(curationImageService).should(times(curationCard1.images.size())).deleteImage(any(), any()); + then(curationCardRepository).should(times(1)).deleteById(curationCard1.id); + } +} diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationServiceTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationServiceTest.java index 739ad27f..5ddc8fc0 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationServiceTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationServiceTest.java @@ -1,6 +1,5 @@ package org.ktc2.cokaen.wouldyouin.curation; -import static java.lang.Math.abs; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -10,7 +9,6 @@ import static org.mockito.Mockito.times; import java.util.Optional; -import java.util.Random; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -18,7 +16,6 @@ import org.ktc2.cokaen.wouldyouin._common.exception.UnauthorizedException; import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData; import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.R.curation1; -import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.curation1.entity; import org.ktc2.cokaen.wouldyouin._global.testdata.EventData; import org.ktc2.cokaen.wouldyouin._global.testdata.EventData.R.event2; import org.ktc2.cokaen.wouldyouin._global.testdata.ImageData; @@ -34,7 +31,6 @@ import org.ktc2.cokaen.wouldyouin.curation.persist.CurationRepository; import org.ktc2.cokaen.wouldyouin.event.application.EventService; import org.ktc2.cokaen.wouldyouin.image.application.CurationImageService; -import org.ktc2.cokaen.wouldyouin.image.persist.MemberImage; import org.ktc2.cokaen.wouldyouin.member.application.CuratorService; import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; import org.mockito.Mock; @@ -60,17 +56,6 @@ class CurationServiceTest { @Mock private CurationImageService curationImageService; - @Mock - private CurationResponse curationResponse; - - @Mock - private MemberImage memberImage; - - Curation validCuration = entity.get(); - CurationResponse validCurationResponse = CurationData.curation1.response.get(); - - private final long randomId = abs(new Random().nextLong()); - @BeforeEach void setUp() { curationService = new CurationService(curationRepository, curatorService, eventService, curationCardService, curationImageService); @@ -80,14 +65,14 @@ void setUp() { @DisplayName("큐레이션 ID를 통해 해당 하는 큐레이션을 반환한다.") void getById() { // given - given(curationRepository.findById(randomId)).willReturn(Optional.of(validCuration)); + given(curationRepository.findById(curation1.id)).willReturn(Optional.of(CurationData.curation1.entity.get())); given(curationImageService.getImageUrl(ImageData.curation1.entity.get())).willReturn(ImageData.R.curation1.url); // when - CurationResponse response = curationService.getById(randomId); + CurationResponse response = curationService.getById(curation1.id); // then - assertThat(response).isEqualTo(validCurationResponse); + assertThat(response).isEqualTo(CurationData.curation1.response.get()); } @Test @@ -95,7 +80,7 @@ void getById() { void getAllByAreaOrderByCreatedDateDesc() { // given given(curationRepository.findAllByAreaOrderByCreatedDateDesc(curation1.area, curation1.lastId, curation1.pageable)) - .willReturn(CurationData.curation1.CurationSlice.get()); + .willReturn(CurationData.CurationSlice.get()); given(curationImageService.getImageUrl(ImageData.curation1.entity.get())).willReturn(ImageData.R.curation1.url); // when @@ -111,7 +96,7 @@ void getAllByAreaOrderByCreatedDateDesc() { void getAllByCuratorIdOrderByCreatedDateDesc() { // given given(curationRepository.findAllByCuratorOrderByCreatedDateDesc(curator1.id, curation1.lastId, curation1.pageable)) - .willReturn(CurationData.curation1.CurationSlice.get()); + .willReturn(CurationData.CurationSlice.get()); given(curationImageService.getImageUrl(ImageData.curation1.entity.get())).willReturn(ImageData.R.curation1.url); // when @@ -123,7 +108,7 @@ void getAllByCuratorIdOrderByCreatedDateDesc() { } @Test - @DisplayName("CurationCreateReqeust를 통해 큐레이션을 생성한다.") + @DisplayName("큐레이션 생성 DTO를 통해 큐레이션을 생성한다.") void create() { // given given(curatorService.getByIdOrThrow(curator1.id)).willReturn(MemberData.curator1.entity.get()); @@ -163,13 +148,13 @@ void update1() { @DisplayName("멤버 ID가 다르면 큐레이션을 수정할 수 없다.") void update2() { // given - Long invalidCuratorId = 100L; + MemberIdentifier differentMember = new MemberIdentifier(100L, MemberType.curator); given(curationRepository.findById(curation1.id)).willReturn(Optional.of(CurationData.curation1.entity.get())); // when, then - assertThrows( - UnauthorizedException.class, () -> curationService.update( - new MemberIdentifier(invalidCuratorId, MemberType.curator), curation1.id, CurationData.curation1.request.edit.get())); + UnauthorizedException exception = assertThrows( + UnauthorizedException.class, () -> curationService.update(differentMember, curation1.id, CurationData.curation1.request.edit.get())); + assertThat(exception.getMessage()).isEqualTo("큐레이션에 접근할 권한이 없습니다."); } @Test @@ -210,21 +195,20 @@ void delete1() { @DisplayName("멤버 ID가 다르면 큐레이션을 삭제할 수 없다.") void delete2() { // given - Long invalidCuratorId = 100L; + MemberIdentifier differentMember = new MemberIdentifier(100L, MemberType.curator); given(curationRepository.findById(curation1.id)).willReturn(Optional.of(CurationData.curation1.entity.get())); // when, then - assertThrows( - UnauthorizedException.class, () -> curationService.delete( - new MemberIdentifier(invalidCuratorId, MemberType.curator), curation1.id)); + UnauthorizedException exception = assertThrows( + UnauthorizedException.class, () -> curationService.delete(differentMember, curation1.id)); + assertThat(exception.getMessage()).isEqualTo("큐레이션에 접근할 권한이 없습니다."); } @Test @DisplayName("멤버 ID가 달라도 ADMIN은 큐레이션을 삭제할 수 있다.") void delete3() { // given - Long invalidCuratorId = 100L; - MemberIdentifier admin = new MemberIdentifier(invalidCuratorId, MemberType.admin); + MemberIdentifier admin = new MemberIdentifier(100L, MemberType.admin); given(curationRepository.findById(curation1.id)).willReturn(Optional.of(CurationData.curation1.entity.get())); // when diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/image/CurationImageServiceTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/image/CurationImageServiceTest.java new file mode 100644 index 00000000..dde61a18 --- /dev/null +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/image/CurationImageServiceTest.java @@ -0,0 +1,66 @@ +package org.ktc2.cokaen.wouldyouin.image; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; + +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData; +import org.ktc2.cokaen.wouldyouin._global.testdata.CurationData.R.curation1; +import org.ktc2.cokaen.wouldyouin._global.testdata.ImageData; +import org.ktc2.cokaen.wouldyouin.image.application.AdvertisementImageService; +import org.ktc2.cokaen.wouldyouin.image.application.CurationImageService; +import org.ktc2.cokaen.wouldyouin.image.application.ImageStorageService; +import org.ktc2.cokaen.wouldyouin.image.persist.CurationImage; +import org.ktc2.cokaen.wouldyouin.image.persist.CurationImageRepository; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class CurationImageServiceTest { + + private CurationImageService curationImageService; + + @Mock + private ImageStorageService imageStorageService; + + @Mock + private CurationImageRepository curationImageRepository; + + @BeforeEach + void setUp() { + curationImageService = new CurationImageService(imageStorageService, curationImageRepository); + } + + @Test + @DisplayName("이미지 ID를 통해 큐레이션 이미지를 반환한다.") + void getById() { + // given + given(curationImageRepository.findById(curation1.id)).willReturn(Optional.of(ImageData.curation1.entity.get())); + + // when + CurationImage response = curationImageService.getById(curation1.id); + + // then + assertThat(response).isEqualTo(ImageData.curation1.entity.get()); + } + +// @Test +// @DisplayName("이미지 ID를 통해 해당 이미지를 삭제한다.") +// void deleteImage() { +// // given +// CurationImage image = ImageData.curation1.entity.get(); +// given(curationImageRepository.findById(curation1.id)).willReturn(Optional.of(image)); +// +// // when, then +// then(imageStorageService).should(times(1)).delete(eq(image.getName()), any()); +// } +} diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/image/ImageControllerTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/image/ImageControllerTest.java index ae8e174e..7d467c4e 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/image/ImageControllerTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/image/ImageControllerTest.java @@ -70,7 +70,6 @@ class ImageControllerTest { @BeforeEach public void setup() throws Exception { - mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(springSecurity()) @@ -94,6 +93,24 @@ void getImage() throws Exception { then(imageStorageService).should(times(1)).readFromDirectory(eq(Paths.get(directory, file))); } + @Test + @DisplayName("이미지 경로를 통해 썸네일 이미지를 조회한다.") + @WithMockMember1 + void getThumnailImage() throws Exception { + // given + String directory = "member"; + String thumbnail = "thumbnail"; + String file = "image.jpg"; + + // when + mockMvc.perform(get("/api/images/{directory}/{thumbnail}/{file}", directory, thumbnail, file)) + .andDo(print()) + .andExpect(status().isOk()); + + // then + then(imageStorageService).should(times(1)).readFromDirectory(eq(Paths.get(directory, thumbnail, file))); + } + @Test @DisplayName("RequestParam으로 이미지 도메인을 받아 첨부된 이미지를 업로드한다.") @WithMockCurator1 diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationControllerUnitTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationControllerUnitTest.java index c28db0c5..7d1dcc09 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationControllerUnitTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationControllerUnitTest.java @@ -22,6 +22,7 @@ import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockCurator1; import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockHost1; import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockMember1; +import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData; import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData.R.curator1; import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData.R.host1; import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData.R.normal1; @@ -76,7 +77,7 @@ void getReservationsByMemberId1() throws Exception { // then then(reservationService).should(times(1)).getAllByMemberId( - eq(normal1.id), eq(PageRequest.of(5, 20)), eq(100L)); + eq(normal1.memberIdentifier), eq(PageRequest.of(5, 20)), eq(100L)); } @Test @@ -89,7 +90,7 @@ void getReservationsByMemberId2() throws Exception { // then then(reservationService).should(times(1)).getAllByMemberId( - eq(normal1.id), eq(PageRequest.of(0, 10)), eq(Long.MAX_VALUE)); + eq(normal1.memberIdentifier), eq(PageRequest.of(0, 10)), eq(Long.MAX_VALUE)); } @Test @@ -105,7 +106,7 @@ void getReservationsByMemberId3() throws Exception { // then then(reservationService).should(times(1)).getAllByMemberId( - eq(curator1.id), eq(PageRequest.of(5, 20)), eq(100L)); + eq(curator1.memberIdentifier), eq(PageRequest.of(5, 20)), eq(100L)); } @Test @@ -213,7 +214,7 @@ void createReservation1() throws Exception { .andExpect(status().isCreated()); // then - then(reservationService).should(times(1)).create(eq(normal1.id), captor.capture()); + then(reservationService).should(times(1)).create(eq(normal1.memberIdentifier), captor.capture()); assertThat(captor.getValue()).isEqualTo(request); } @@ -235,7 +236,7 @@ void createReservation2() throws Exception { .andExpect(status().isCreated()); // then - then(reservationService).should(times(1)).create(eq(curator1.id), captor.capture()); + then(reservationService).should(times(1)).create(eq(curator1.memberIdentifier), captor.capture()); assertThat(captor.getValue()).isEqualTo(request); } @@ -330,7 +331,7 @@ void deleteReservation1() throws Exception { .andExpect(status().isNoContent()); // then - then(reservationService).should(times(1)).delete(normal1.id, randomId); + then(reservationService).should(times(1)).delete(normal1.memberIdentifier, randomId); } @Test @@ -344,7 +345,7 @@ void deleteReservation2() throws Exception { .andExpect(status().isNoContent()); // then - then(reservationService).should(times(1)).delete(curator1.id, randomId); + then(reservationService).should(times(1)).delete(curator1.memberIdentifier, randomId); } @Test diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationServiceUnitTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationServiceUnitTest.java index 86fa090a..fc44cc7b 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationServiceUnitTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationServiceUnitTest.java @@ -1,101 +1,174 @@ -//package org.ktc2.cokaen.wouldyouin.reservation; -// -//import static java.lang.Math.abs; -//import static org.junit.jupiter.api.Assertions.assertThrows; -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.Mockito.times; -//import static org.mockito.Mockito.verify; -//import static org.mockito.Mockito.when; -// -//import java.util.List; -//import java.util.Optional; -//import java.util.Random; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.DisplayName; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.extension.ExtendWith; -//import org.ktc2.cokaen.wouldyouin.event.application.EventService; -//import org.ktc2.cokaen.wouldyouin.global.TestData; -//import org.ktc2.cokaen.wouldyouin.member.application.MemberService; -//import org.ktc2.cokaen.wouldyouin.payment.application.PaymentService; -//import org.ktc2.cokaen.wouldyouin.reservation.application.ReservationService; -//import org.ktc2.cokaen.wouldyouin.reservation.persist.ReservationRepository; -//import org.mockito.Mock; -//import org.mockito.junit.jupiter.MockitoExtension; -// -//@ExtendWith({MockitoExtension.class}) -//class ReservationServiceUnitTest { -// -// private ReservationService reservationService; -// -// @Mock -// private ReservationRepository reservationRepository; -// @Mock -// private MemberService memberService; -// @Mock -// private EventService eventService; -// @Mock -// private PaymentService paymentService; -// -// private Long id; -// -// @BeforeEach -// void setUp() { -// reservationService = new ReservationService(reservationRepository, paymentService, memberService, eventService); -// id = abs(new Random().nextLong()); -// } -// -// @Test -// @DisplayName("사용자 id를 통한 모든 예약 조회 - 성공") -// void getAllByMemberId() { -// when(reservationRepository.findByMemberIdOrderByReservationIdDesc(id)).thenReturn(List.of()); -// reservationService.getAllByMemberId(id); -// verify(reservationRepository, times(1)).findByMemberIdOrderByReservationIdDesc(id); -// } -// -// @Test -// @DisplayName("행사 id를 통한 모든 예약 조회 - 성공") -// void getAllByEventId() { -// when(reservationRepository.findByEventIdOrderByReservationIdDesc(id)).thenReturn(List.of()); -// reservationService.getAllByEventId(id); -// verify(reservationRepository, times(1)).findByEventIdOrderByReservationIdDesc(id); -// } -// -// @Test -// @DisplayName("예약 id를 통한 예약 조회 - 성공") -// void getById() { -// when(reservationRepository.findById(id)).thenReturn(Optional.of(TestData.validReservation)); -// reservationService.getById(id); -// verify(reservationRepository, times(1)).findById(id); -// } -// -// @Test -// @DisplayName("유효하지 않은 예약 id를 통한 모든 예약 조회 - 실패") -// void getByInvalidId() { -// when(reservationRepository.findById(id)).thenThrow(RuntimeException.class); -// assertThrows(RuntimeException.class, () -> reservationService.getById(id)); -// } -// -// @Test -// @DisplayName("예약 생성 - 성공") -// void create() { -// when(reservationRepository.save(any())).thenReturn(TestData.validReservation); -// reservationService.create(1L, TestData.validReservationRequest); -// verify(reservationRepository, times(1)).save(any()); -// } -// -// @Test -// @DisplayName("예약 삭제 - 성공") -// void delete() { -// when(reservationRepository.findById(id)).thenReturn(Optional.of(TestData.validReservation)); -// reservationService.delete(id); -// verify(reservationRepository, times(1)).findById(id); -// } -// -// @Test -// @DisplayName("예약 삭제 - 실패") -// void deleteByInvalidId() { -// when(reservationRepository.findById(id)).thenThrow(RuntimeException.class); -// assertThrows(RuntimeException.class, () -> reservationService.delete(id)); -// } -//} \ No newline at end of file +package org.ktc2.cokaen.wouldyouin.reservation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; + +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.ktc2.cokaen.wouldyouin._common.exception.ReservationNotFoundForReviewException; +import org.ktc2.cokaen.wouldyouin._common.exception.UnauthorizedException; +import org.ktc2.cokaen.wouldyouin._global.testdata.MemberData.R.normal1; +import org.ktc2.cokaen.wouldyouin._global.testdata.ReservationData; +import org.ktc2.cokaen.wouldyouin._global.testdata.ReservationData.R; +import org.ktc2.cokaen.wouldyouin._global.testdata.ReservationData.R.reservation1; +import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier; +import org.ktc2.cokaen.wouldyouin.event.application.EventService; +import org.ktc2.cokaen.wouldyouin.member.application.MemberService; +import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; +import org.ktc2.cokaen.wouldyouin.payment.application.PaymentService; +import org.ktc2.cokaen.wouldyouin.reservation.api.dto.KakaoPayReservationResponse; +import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationResponse; +import org.ktc2.cokaen.wouldyouin.reservation.api.dto.ReservationSliceResponse; +import org.ktc2.cokaen.wouldyouin.reservation.application.ReservationService; +import org.ktc2.cokaen.wouldyouin.reservation.persist.ReservationRepository; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith({MockitoExtension.class}) +class ReservationServiceUnitTest { + + private ReservationService reservationService; + + @Mock + private ReservationRepository reservationRepository; + @Mock + private MemberService memberService; + @Mock + private EventService eventService; + @Mock + private PaymentService paymentService; + + @BeforeEach + void setUp() { + reservationService = new ReservationService(reservationRepository, paymentService, memberService, eventService); + } + + @Test + @DisplayName("예약 ID를 통한 해당 예약을 반환한다.") + void getById() { + // given + given(reservationRepository.findById(reservation1.id)).willReturn(Optional.of(ReservationData.reservation1.entity.get())); + + // when + ReservationResponse response = reservationService.getById(reservation1.id); + + // then + assertThat(response).isEqualTo(ReservationResponse.from(ReservationData.reservation1.entity.get())); + } + + @Test + @DisplayName("멤버 ID를 통해 자신의 모든 예약을 반환한다.") + void getAllByMemberId() { + // given + given(reservationRepository.findByMemberIdOrderByReservationIdDesc(normal1.id, R.lastId, ReservationData.R.pageable)) + .willReturn(ReservationData.ReservationSlice.get()); + + // when + ReservationSliceResponse response = + reservationService.getAllByMemberId(normal1.memberIdentifier, R.pageable, R.lastId); + + // then + assertThat(response).isEqualTo(ReservationData.sliceResponse.get()); + } + + @Test + @DisplayName("이벤트 ID를 통해 해당 행사의 모든 예약을 반환한다.") + void getAllByEventId() { + // given + given(eventService.getByIdOrThrow(R.reservation1._Relation.event().getId())).willReturn(R.reservation1._Relation.event()); + given(reservationRepository.findByEventIdOrderByReservationIdDesc(R.reservation1._Relation.event().getId(), R.lastId, R.pageable)) + .willReturn(ReservationData.ReservationSlice.get()); + + // when + ReservationSliceResponse response = + reservationService.getAllByEventId(normal1.memberIdentifier, R.reservation1._Relation.event().getId(), R.pageable, R.lastId); + + // then + assertThat(response).isEqualTo(ReservationData.sliceResponse.get()); + } + + @Test + @DisplayName("예약 생성 DTO를 통해 결제를 진행하고 예약을 생성한다.") + void create() { + // given + given(reservationRepository.save(any())).willReturn(ReservationData.reservation1.entity.get()); + given(eventService.getByIdOrThrow(R.reservation1._Relation.event().getId())).willReturn(R.reservation1._Relation.event()); + given(reservationRepository.save(any())).willReturn(ReservationData.reservation1.entity.get()); + given(paymentService.createPayment(any())).willReturn(ReservationData.reservation1.kakaoPayResponse.get()); + + // when + KakaoPayReservationResponse response = + reservationService.create(normal1.memberIdentifier, ReservationData.reservation1.request.get()); + + // then + assertThat(response).isEqualTo(ReservationData.reservation1.kakaoPayReservationResponse.get()); + } + + @Test + @DisplayName("예약 생성 DTO를 통해 예약을 생성한다.") + void createTest() { + // given + given(reservationRepository.save(any())).willReturn(ReservationData.reservation1.entity.get()); + given(eventService.getByIdOrThrow(R.reservation1._Relation.event().getId())).willReturn(R.reservation1._Relation.event()); + given(reservationRepository.save(any())).willReturn(ReservationData.reservation1.entity.get()); + + // when + ReservationResponse response = + reservationService.createTest(normal1.memberIdentifier, ReservationData.reservation1.request.get()); + + // then + assertThat(response).isEqualTo(ReservationData.reservation1.response.get()); + } + + @Test + @DisplayName("예약 ID를 통해 해당 예약을 삭제한다.") + void delete1() { + // given + given(reservationRepository.findById(reservation1.id)).willReturn(Optional.of(ReservationData.reservation1.entity.get())); + + // when + reservationService.delete(normal1.memberIdentifier, reservation1.id); + + // then + then(reservationRepository).should(times(1)).deleteById(reservation1.id); + } + + @Test + @DisplayName("멤버 ID와 예약의 멤버 ID가 일치하지 않으면 예약을 삭제할 수 없다.") + void delete2() { + // given + MemberIdentifier anotherMember = new MemberIdentifier(100L, MemberType.normal); + given(reservationRepository.findById(reservation1.id)).willReturn(Optional.of(ReservationData.reservation1.entity.get())); + + // when + UnauthorizedException exception = + assertThrows(UnauthorizedException.class, () -> reservationService.delete(anotherMember, reservation1.id)); + + // then + then(reservationRepository).should(times(0)).deleteById(reservation1.id); + assertThat(exception.getMessage()).isEqualTo("사용자 ID가 예약한 사용자 ID와 일치하지 않습니다."); + } + + @Test + @DisplayName("사용자는 해당 이벤트에 대한 예약이 없으면 리뷰를 작성할 수 없다.") + void validateByMemberIdAndEventId() { + // given + given(reservationRepository.findByMemberIdAndEventId(normal1.id, R.reservation1._Relation.event().getId())).willReturn(List.of()); + + // when + ReservationNotFoundForReviewException exception = + assertThrows(ReservationNotFoundForReviewException.class, + () -> reservationService.validateByMemberIdAndEventId(normal1.id, R.reservation1._Relation.event().getId())); + + // then + assertThat(exception.getMessage()).isEqualTo("해당 이벤트에 대한 리뷰를 작성할 수 없습니다."); + } +} \ No newline at end of file