From 3e8d237a5fbc260d799a45fe97a00f18dd5c8df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=ED=99=8D=EC=8B=9D?= <90020593+Daolove0323@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:00:31 +0900 Subject: [PATCH] =?UTF-8?q?[Weekly/10/Reservation/unitTest]=20Reservation?= =?UTF-8?q?=20unit=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: reservation unit test * feat: 행사명으로 검색 로직 추가 * feat: url을 이미지로 저장하는 로직 --- .../wouldyouin/Image/api/ImageController.java | 14 +- .../Image/api/dto/ImageResponse.java | 5 +- .../AdvertisementImageService.java | 6 +- .../Image/application/EventImageService.java | 5 - .../Image/application/ImageService.java | 6 +- .../Image/application/ImageStorage.java | 45 ++++++- .../Image/application/MemberImageService.java | 32 +---- .../_common/config/ParamDefaults.java | 5 +- .../wouldyouin/_common/error/ErrorCode.java | 8 +- .../CurrentLocationEmptyException.java | 10 ++ .../FailedToDeleteImageException.java | 10 ++ .../FailedToUploadImageException.java | 10 ++ .../wouldyouin/event/api/EventController.java | 5 +- .../event/application/EventService.java | 12 +- .../event/persist/EventRepository.java | 3 +- .../api/ReservationController.java | 4 +- .../api/dto/ReservationRequest.java | 10 ++ src/main/resources/application.yml | 3 + .../cokaen/wouldyouin/_global/TestData.java | 62 ++++++++- .../ReservationControllerUnitTest.java | 126 +++++++++++------- 20 files changed, 264 insertions(+), 117 deletions(-) create mode 100644 src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/CurrentLocationEmptyException.java create mode 100644 src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToDeleteImageException.java create mode 100644 src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToUploadImageException.java 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 13f22fa6..cc4fa869 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 @@ -7,9 +7,13 @@ import lombok.RequiredArgsConstructor; import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageResponse; import org.ktc2.cokaen.wouldyouin.Image.application.ImageServiceFactory; +import org.ktc2.cokaen.wouldyouin.Image.application.ImageStorage; import org.ktc2.cokaen.wouldyouin._common.api.ApiResponse; import org.ktc2.cokaen.wouldyouin._common.api.ApiResponseBody; import org.ktc2.cokaen.wouldyouin._common.exception.FailToReadImageException; +import org.ktc2.cokaen.wouldyouin.auth.Authorize; +import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier; +import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -29,8 +33,10 @@ public class ImageController { private final ImageServiceFactory imageServiceFactory; + // Todo: authorize @PostMapping("/images") - public ResponseEntity>> uploadImages(@RequestParam List images, + public ResponseEntity>> uploadImages( + @RequestParam List images, @RequestParam(value = "type") ImageDomain imageDomain) { return ApiResponse.ok(imageServiceFactory.getImageServiceByImageType(imageDomain).saveAndCreateImages(images)); } @@ -44,9 +50,11 @@ public ResponseEntity getImage(@PathVariable String path) { } } - // Authorize 로직 + // Todo: authorize @DeleteMapping("/images/{id}") - public ResponseEntity> deleteImage(@PathVariable Long id, @RequestParam ImageDomain imageDomain) { + public ResponseEntity> deleteImage( + @PathVariable Long id, + @RequestParam(value = "type") ImageDomain imageDomain) { imageServiceFactory.getImageServiceByImageType(imageDomain).deleteAndDelete(id); return ApiResponse.noContent(); } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/api/dto/ImageResponse.java b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/api/dto/ImageResponse.java index 9a0c2df6..5c219943 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/api/dto/ImageResponse.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/api/dto/ImageResponse.java @@ -14,10 +14,11 @@ public class ImageResponse { private LocalDateTime createdDate; private Long size; - public static ImageResponse from(Image image) { + public static ImageResponse from(Image image, String domainName) { + System.out.println("도메인" + domainName); return ImageResponse.builder() .id(image.getId()) - .url(image.getUrl()) + .url(domainName + image.getUrl()) .size(image.getSize()) .createdDate(image.getCreatedDate()) .build(); 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 43c64738..967b22a1 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 @@ -18,12 +18,10 @@ @RequiredArgsConstructor public class AdvertisementImageService extends ImageService { - @Value("${image.upload.ad.sub-path}") - private String subPath; - private final ImageStorage imageStorage; - private final AdvertisementImageRepository adImageRepository; + @Value("${image.upload.ad.sub-path}") + private String subPath; @Override protected ImageRepository getImageRepository() { 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 14e0fbd9..ecfc2a77 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 @@ -1,18 +1,13 @@ package org.ktc2.cokaen.wouldyouin.Image.application; -import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.ktc2.cokaen.wouldyouin.Image.api.ImageDomain; import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageRequest; -import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage; import org.ktc2.cokaen.wouldyouin.Image.persist.EventImage; import org.ktc2.cokaen.wouldyouin.Image.persist.EventImageRepository; import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository; -import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException; import org.ktc2.cokaen.wouldyouin._common.exception.EntityParamIsNullException; -import org.ktc2.cokaen.wouldyouin.advertisement.persist.Advertisement; -import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCard; import org.ktc2.cokaen.wouldyouin.event.persist.Event; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; 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 e76286bf..aaac70b4 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 @@ -9,6 +9,7 @@ import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository; import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException; 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; import org.springframework.web.multipart.MultipartFile; @@ -20,6 +21,9 @@ public abstract class ImageService { @Autowired protected ImageStorage imageStorage; + @Value("${spring.wouldyouin-domain-name}") + private String domainName; + protected abstract ImageRepository getImageRepository(); protected abstract ImageDomain getImageDomain(); @@ -34,7 +38,7 @@ public T getById(Long id) { } protected ImageResponse create(ImageRequest imageRequest) { - return ImageResponse.from(getImageRepository().save(toEntity(imageRequest))); + return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)), domainName); } protected void delete(Long id) { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/ImageStorage.java b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/ImageStorage.java index 9a11128c..046aea29 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/ImageStorage.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/ImageStorage.java @@ -4,11 +4,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.UUID; -import org.ktc2.cokaen.wouldyouin.Image.api.ImageDomain; +import org.ktc2.cokaen.wouldyouin._common.exception.FailedToDeleteImageException; +import org.ktc2.cokaen.wouldyouin._common.exception.FailedToUploadImageException; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; import org.springframework.web.multipart.MultipartFile; @Component @@ -18,13 +23,38 @@ public class ImageStorage { private String commonPath; public String save(MultipartFile image, String subPath) { - String fileName = generateUuidName() + "." + getExtension(image); - Path path = Paths.get(commonPath, subPath, fileName); + String fileName = ""; try { + fileName = generateUuidName() + "." + getExtension(image); + Path path = Paths.get(commonPath, subPath, fileName); Files.createDirectories(path.getParent()); Files.write(path, image.getBytes()); } catch (IOException ex) { - throw new RuntimeException("failed to upload image."); + throw new FailedToUploadImageException(); + } + return subPath + "/" + fileName; + } + + // Todo: payment랑 이부분 restclient 유틸로 빼기 + public String save(String imageUrl, String subPath) { + RestClient client = RestClient.builder().build(); + String fileName = ""; + try { + ResponseEntity response = client.get() + .uri(imageUrl) + .retrieve() + .toEntity(byte[].class); + + if (Optional.ofNullable(response).isPresent() && response.getStatusCode().is2xxSuccessful()) { + byte[] imageBytes = response.getBody(); + + fileName = generateUuidName() + "." + getExtension(imageUrl); + Path path = Paths.get(commonPath, subPath, fileName); + Files.createDirectories(path.getParent()); + Files.write(path, response.getBody()); + } + } catch (IOException ex) { + throw new FailedToUploadImageException(); } return subPath + "/" + fileName; } @@ -33,7 +63,7 @@ public void delete(String imagePath) { try { Files.deleteIfExists(Paths.get(imagePath)); } catch (IOException ex) { - throw new RuntimeException("failed to delete image."); + throw new FailedToDeleteImageException(); } } @@ -41,6 +71,11 @@ protected static String getExtension(MultipartFile image) { return Objects.requireNonNull(image.getContentType()).split("/")[1]; } + protected static String getExtension(String imageUrl) { + String[] splitted = imageUrl.split("\\."); + return splitted[splitted.length - 1]; + } + private static String generateUuidName() { return UUID.randomUUID().toString().replace("-", ""); } 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 817d14d4..621679b3 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 @@ -61,35 +61,9 @@ public void setBaseMember(MemberImage image, BaseMember member) { } // TODO: imageUrl을 MemberImage로 변환하는 로직 추가 필요 + // Todo: extension과 size 불러오기 public MemberImage convert(String imageUrl) { - - RestClient client = RestClient.builder().build(); - - try { - ResponseEntity response = client.get() - .uri(imageUrl) - .retrieve() - .toEntity(byte[].class); - - // 요청 성공 시 이미지 저장 - if (response != null && response.getStatusCode() == HttpStatus.OK) { - byte[] imageBytes = response.getBody(); - - if (imageBytes != null) { - // 파일 이름과 경로 설정 - Path path = Paths.get("src/main/resources/static", subPath, "testFilename"); - Files.createDirectories(path.getParent()); - Files.write(path, response.getBody()); - } - } - } catch (Exception ex) { - throw new RuntimeException("failed to get image."); - } - - return MemberImage.builder() - .url("http://example.com/images/MockMemberImageUrl") - .size(10L) - .extension(".jpeg") - .build(); + var request = ImageRequest.of(imageStorage.save(imageUrl, subPath), 123123L, "jpg"); + return memberImageRepository.save(toEntity(request)); } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/config/ParamDefaults.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/config/ParamDefaults.java index e8e57b88..5e4bfdd3 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/config/ParamDefaults.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/config/ParamDefaults.java @@ -5,10 +5,7 @@ public class ParamDefaults { public static final String PAGE = "0"; public static final String PAGE_SIZE = "10"; public static final String LAST_ID = Long.MAX_VALUE + ""; - public static final String START_LATITUDE = "-90.0"; - public static final String END_LATITUDE = "90.0"; - public static final String START_LONGITUDE = "-180.0"; - public static final String END_LONGITUDE = "180.0"; + public static final String TITLE = ""; public static final String AREA = "전체"; public static final String CATEGORY = "전체"; } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java index f4555d4d..1e52d1d9 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java @@ -20,7 +20,13 @@ public enum ErrorCode { UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "-20400", "Unauthorized, %s."), - ENTITY_PARAM_IS_NULL(HttpStatus.BAD_REQUEST.value(), "-20400", "%s is null"); + ENTITY_PARAM_IS_NULL(HttpStatus.BAD_REQUEST.value(), "-20400", "%s is null"), + + FAIL_TO_UPLOAD_IMAGE(HttpStatus.CONFLICT.value(), "-20400", "Fail to upload image"), + + FAIL_TO_DELETE_IMAGE(HttpStatus.CONFLICT.value(), "-20400", "Fail to delete image"), + + CURRENT_LOCATION_EMPTY(HttpStatus.BAD_REQUEST.value(), "-20400", "현재 위치 정보를 찾을 수 없습니다."); private final Integer status; private final String code; diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/CurrentLocationEmptyException.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/CurrentLocationEmptyException.java new file mode 100644 index 00000000..28e1effd --- /dev/null +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/CurrentLocationEmptyException.java @@ -0,0 +1,10 @@ +package org.ktc2.cokaen.wouldyouin._common.exception; + +import org.ktc2.cokaen.wouldyouin._common.error.ErrorCode; + +public class CurrentLocationEmptyException extends BusinessException { + + public CurrentLocationEmptyException() { + super(ErrorCode.CURRENT_LOCATION_EMPTY, "현재 위치 정보를 찾을 수 없습니다."); + } +} diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToDeleteImageException.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToDeleteImageException.java new file mode 100644 index 00000000..e470f6d1 --- /dev/null +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToDeleteImageException.java @@ -0,0 +1,10 @@ +package org.ktc2.cokaen.wouldyouin._common.exception; + +import org.ktc2.cokaen.wouldyouin._common.error.ErrorCode; + +public class FailedToDeleteImageException extends BusinessException { + + public FailedToDeleteImageException() { + super(ErrorCode.FAIL_TO_DELETE_IMAGE, ErrorCode.FAIL_TO_DELETE_IMAGE.getMessage()); + } +} \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToUploadImageException.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToUploadImageException.java new file mode 100644 index 00000000..813b211a --- /dev/null +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/exception/FailedToUploadImageException.java @@ -0,0 +1,10 @@ +package org.ktc2.cokaen.wouldyouin._common.exception; + +import org.ktc2.cokaen.wouldyouin._common.error.ErrorCode; + +public class FailedToUploadImageException extends BusinessException { + + public FailedToUploadImageException() { + super(ErrorCode.FAIL_TO_UPLOAD_IMAGE, ErrorCode.FAIL_TO_UPLOAD_IMAGE.getMessage()); + } +} diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java b/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java index 8dc290ab..abf24bd9 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java @@ -41,6 +41,7 @@ public class EventController { public ResponseEntity> getEventsByFilterOrderByDistanceAsc( @ModelAttribute LocationFilter locationFilter, @ModelAttribute Location currentLocation, + @RequestParam(defaultValue = ParamDefaults.TITLE) String title, @RequestParam(defaultValue = ParamDefaults.CATEGORY) Category category, @RequestParam(defaultValue = ParamDefaults.AREA) Area area, @RequestParam(defaultValue = ParamDefaults.PAGE) Integer page, @@ -48,7 +49,7 @@ public ResponseEntity> getEventsByFilterOrde @RequestParam(defaultValue = ParamDefaults.LAST_ID) Long lastId ) { return ApiResponse.ok(eventService.getAllByFilterOrderByDistanceAsc( - locationFilter, currentLocation, category, area, PageRequest.of(page, size), lastId)); + locationFilter, currentLocation, title, category, area, PageRequest.of(page, size), lastId)); } @GetMapping("/hosts/{hostId}") @@ -62,8 +63,6 @@ public ResponseEntity> getEventsByHostId( hostId, PageRequest.of(page, size), lastId)); } - // Todo: 이름으로 검색 - @GetMapping("/{eventId}") public ResponseEntity> getEventByEventId( @PathVariable("eventId") Long eventId) { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java index 1d54cccf..8a31d149 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java @@ -3,6 +3,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.ktc2.cokaen.wouldyouin.Image.application.EventImageService; +import org.ktc2.cokaen.wouldyouin._common.exception.CurrentLocationEmptyException; import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException; import org.ktc2.cokaen.wouldyouin._common.exception.UnauthorizedException; import org.ktc2.cokaen.wouldyouin._common.vo.Area; @@ -30,12 +31,19 @@ public class EventService { private final EventImageService eventImageService; @Transactional(readOnly = true) - public EventSliceResponse getAllByFilterOrderByDistanceAsc(LocationFilter location, Location currentLocation, + public EventSliceResponse getAllByFilterOrderByDistanceAsc(LocationFilter location, Location currentLocation, String title, Category category, Area area, Pageable pageable, Long lastId) { + validateCurrentLocation(currentLocation); return getEventSliceResponse( eventRepository.findAllByFilterOrderByDistance(location.getStartLatitude(), location.getStartLongitude(), location.getEndLatitude(), location.getEndLongitude(), currentLocation.getLatitude(), currentLocation.getLongitude(), - category, area, pageable), lastId); + title, category, area, pageable), lastId); + } + + private void validateCurrentLocation(Location currentLocation) { + if (currentLocation.getLatitude() == null || currentLocation.getLongitude() == null) { + throw new CurrentLocationEmptyException(); + } } @Transactional(readOnly = true) diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/event/persist/EventRepository.java b/src/main/java/org/ktc2/cokaen/wouldyouin/event/persist/EventRepository.java index 4cfabf22..837f5463 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/event/persist/EventRepository.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/event/persist/EventRepository.java @@ -20,11 +20,12 @@ public interface EventRepository extends JpaRepository { @Query("SELECT E FROM Event E JOIN FETCH E.host " + "WHERE (E.location.latitude between :startLatitude AND :endLatitude) " + "AND (E.location.longitude between :startLongitude AND :endLongitude) " + + "AND (E.title LIKE %:title%) " + "AND (:category = '전체' OR E.category = :category) " + "AND (:area = '전체' OR E.area = :area) " + "ORDER BY (:currentLatitude - E.location.latitude) * (:currentLatitude - E.location.latitude) + " + "(:currentLongitude - E.location.longitude) * (:currentLongitude - E.location.longitude) ASC") Slice findAllByFilterOrderByDistance( Double startLatitude, Double startLongitude, Double endLatitude, Double endLongitude, - Double currentLatitude, Double currentLongitude, Category category, Area area, Pageable pageable); + Double currentLatitude, Double currentLongitude, String title, Category category, Area area, Pageable pageable); } \ 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 4afed216..8bdc91ea 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 @@ -33,11 +33,11 @@ public class ReservationController { @GetMapping public ResponseEntity> getReservationsByMemberId( - @Authorize({MemberType.normal, MemberType.curator}) Long memberId, + @Authorize({MemberType.normal, MemberType.curator}) MemberIdentifier member, @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(memberId, PageRequest.of(page, size), lastId)); + return ApiResponse.ok(reservationService.getAllByMemberId(member.id(), PageRequest.of(page, size), lastId)); } @GetMapping("/events/{eventId}") diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/ReservationRequest.java b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/ReservationRequest.java index 43e77130..aa7c11e1 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/ReservationRequest.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/api/dto/ReservationRequest.java @@ -1,5 +1,8 @@ package org.ktc2.cokaen.wouldyouin.reservation.api.dto; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; import org.ktc2.cokaen.wouldyouin.event.persist.Event; @@ -10,8 +13,15 @@ @Builder(toBuilder = true) public class ReservationRequest { + @NotNull(message = "이벤트 ID는 필수입니다.") private Long eventId; + + @NotNull(message = "가격은 필수입니다.") + @Min(value = 0, message = "가격은 0 이상이어야 합니다.") + @Max(value = 1000000, message = "가격은 1,000,000원 이하이어야 합니다.") private Integer price; + + @NotNull(message = "수량은 필수입니다.") private Integer quantity; public Reservation toEntity(Member member, Event event) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3bcdaaff..e76ba79b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,6 +3,9 @@ spring: application.name: WouldYouIn + + # 임시 프로젝트 url + wouldyouin-domain-name: http://52.78.71.136/ profiles: default: dev diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/TestData.java b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/TestData.java index 5d58e098..d1bdbf13 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/_global/TestData.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/_global/TestData.java @@ -5,6 +5,7 @@ import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage; import org.ktc2.cokaen.wouldyouin.Image.persist.EventImage; import org.ktc2.cokaen.wouldyouin.Image.persist.MemberImage; +import org.ktc2.cokaen.wouldyouin._common.api.SliceInfo; import org.ktc2.cokaen.wouldyouin._common.vo.Area; import org.ktc2.cokaen.wouldyouin._common.vo.Category; import org.ktc2.cokaen.wouldyouin._common.vo.Location; @@ -18,7 +19,9 @@ import org.ktc2.cokaen.wouldyouin.event.api.dto.EventCreateRequest; import org.ktc2.cokaen.wouldyouin.event.api.dto.EventEditRequest; import org.ktc2.cokaen.wouldyouin.event.api.dto.relationResonse.CurationEventResponse; +import org.ktc2.cokaen.wouldyouin.event.api.dto.relationResonse.ReservationEventResponse; import org.ktc2.cokaen.wouldyouin.event.persist.Event; +import org.ktc2.cokaen.wouldyouin.member.application.dto.relation.ReservationMemberResponse; import org.ktc2.cokaen.wouldyouin.member.application.dto.relationResponse.CurationCuratorResponse; import org.ktc2.cokaen.wouldyouin.member.persist.AccountType; import org.ktc2.cokaen.wouldyouin.member.persist.Curator; @@ -26,11 +29,20 @@ import org.ktc2.cokaen.wouldyouin.member.persist.Member; import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; 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.test.util.ReflectionTestUtils; public class TestData { + public static SliceInfo createSliceInfo() { + return SliceInfo.builder() + .sliceSize(10) + .lastId(100L) + .build(); + } + public static class ImageDomain { public static MemberImage createValidMemberImage(Long id) { @@ -140,6 +152,16 @@ public static Member createValidWelcomeMember() { ReflectionTestUtils.setField(memberImage, "baseMember", ret); return ret; } + + public static ReservationMemberResponse createValidReservationMemberResponse() { + return ReservationMemberResponse.builder() + .id(validMemberId) + .email("member1@example.com") + .nickname("nick_normal_123") + .phone("010-1112-2233") + .gender("Men") + .build(); + } } public static class EventDomain { @@ -200,6 +222,14 @@ public static CurationEventResponse createValidCurationEventResponse() { .hostNickname("nick_curator_12") .build(); } + + public static ReservationEventResponse createValidReservationEventResponse() { + return ReservationEventResponse.builder() + .id(1L) + .title("title") + .price(15000) + .build(); + } } public static class ReservationDomain { @@ -207,17 +237,37 @@ public static class ReservationDomain { public static ReservationRequest createValidReservationRequest() { return ReservationRequest.builder() .eventId(1L) - .price(10000) - .quantity(1) + .price(15000) + .quantity(2) .build(); } public static Reservation createValidReservation() { - return Reservation.builder() + Reservation reservation = Reservation.builder() .member(MemberDomain.createValidMember()) .event(EventDomain.createValidEvent()) - .price(10000) - .quantity(3) + .price(15000) + .quantity(2) + .build(); + ReflectionTestUtils.setField(reservation, "id", 1L); + return reservation; + } + + public static ReservationResponse createValidReservationResponse() { + return ReservationResponse.builder() + .id(1L) + .member(MemberDomain.createValidReservationMemberResponse()) + .event(EventDomain.createValidReservationEventResponse()) + .price(15000) + .quantity(2) + .reservationDate(LocalDateTime.of(2024, 3, 23, 0, 0)) + .build(); + } + + public static ReservationSliceResponse createValidReservationSliceResponse() { + return ReservationSliceResponse.builder() + .reservations(List.of(createValidReservationResponse())) + .sliceInfo(TestData.createSliceInfo()) .build(); } } @@ -318,7 +368,7 @@ public static CurationResponse createValidCurationResponse() { public static CurationSliceResponse createValidCurationSliceResponse() { return CurationSliceResponse.builder() .curations(List.of(createValidCurationResponse())) - .slice(null) + .slice(TestData.createSliceInfo()) .build(); } } 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 3f620615..7b9124ff 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationControllerUnitTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/reservation/ReservationControllerUnitTest.java @@ -1,12 +1,12 @@ //package org.ktc2.cokaen.wouldyouin.reservation; // //import static java.lang.Math.abs; -//import static org.mockito.Mockito.verify; -//import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +//import static org.mockito.ArgumentMatchers.eq; +//import static org.mockito.BDDMockito.then; +//import static org.mockito.Mockito.times; //import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; //import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; //import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; // //import com.fasterxml.jackson.databind.ObjectMapper; @@ -16,29 +16,28 @@ //import org.junit.jupiter.api.BeforeEach; //import org.junit.jupiter.api.DisplayName; //import org.junit.jupiter.api.Test; +//import org.ktc2.cokaen.wouldyouin._global.TestData.MemberDomain; +//import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockCurator; +//import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockHost; +//import org.ktc2.cokaen.wouldyouin._global.mockMember.WithMockMember; //import org.ktc2.cokaen.wouldyouin.auth.application.JwtAuthFilter; -//import org.ktc2.cokaen.wouldyouin.global.TestData; -//import org.ktc2.cokaen.wouldyouin.member.application.MemberService; //import org.ktc2.cokaen.wouldyouin.reservation.api.ReservationController; //import org.ktc2.cokaen.wouldyouin.reservation.application.ReservationService; //import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; //import org.springframework.boot.test.mock.mockito.MockBean; -//import org.springframework.http.MediaType; -//import org.springframework.security.test.context.support.WithMockUser; +//import org.springframework.data.domain.PageRequest; //import org.springframework.test.web.servlet.MockMvc; //import org.springframework.test.web.servlet.setup.MockMvcBuilders; //import org.springframework.web.context.WebApplicationContext; // -//@WithMockUser(username = "user", roles = {"USER"}) //@WebMvcTest(ReservationController.class) //class ReservationControllerUnitTest { // -// @MockBean -// private ReservationService reservationService; +// private static ObjectMapper objectMapper; // // @MockBean -// private MemberService memberService; +// private ReservationService reservationService; // // @MockBean // private JwtAuthFilter jwtAuthFilter; @@ -48,11 +47,13 @@ // // @Autowired // private WebApplicationContext context; +// private static final long randomId = abs(new Random().nextLong()); // -// private static Long id; -// -// private static ObjectMapper objectMapper; -// +// @BeforeAll +// public static void init() { +// objectMapper = new ObjectMapper(); +// objectMapper.registerModule(new JavaTimeModule()); +// } // // @BeforeEach // public void setup() throws Exception { @@ -62,51 +63,78 @@ // .build(); // } // -// @BeforeAll -// public static void init() { -// id = abs(new Random().nextLong()); -// objectMapper = new ObjectMapper(); -// objectMapper.registerModule(new JavaTimeModule()); -// } -// // @Test -// @DisplayName("예약 조회 - 성공") -// void getReservationById() throws Exception { -// mockMvc.perform(get("/api/reservations/" + id)).andExpect(status().isOk()); -// verify(reservationService).getById(id); +// @DisplayName("jwt 토큰 정보에 해당하는 member의 예약 목록을 조회한다.") +// @WithMockMember +// void getReservationsByMemberId1() throws Exception { +// // given, when +// mockMvc.perform(get("/api/reservations") +// .param("page", "5") +// .param("size", "20") +// .param("lastId", "100")).andDo(print()) +// .andExpect(status().isOk()); +// +// // then +// then(reservationService).should(times(1)).getAllByMemberId( +// eq(MemberDomain.validMemberId), eq(PageRequest.of(5, 20)), eq(100L)); // } // // @Test -// @DisplayName("사용자 id를 통한 예약 조회 - 성공") -// void getReservationsByMemberIdById() throws Exception { -// mockMvc.perform(get("/api/reservations/members/" + id)).andExpect(status().isOk()); -// verify(reservationService).getAllByMemberId(id); -// +// @DisplayName("RequestParam으로 페이지 정보가 주어지지 않으면 기본값으로 페이지를 생성한다") +// @WithMockMember +// void getReservationsByMemberId2() throws Exception { +// // given, when +// mockMvc.perform(get("/api/reservations")).andDo(print()) +// .andExpect(status().isOk()); +// +// // then +// then(reservationService).should(times(1)).getAllByMemberId( +// eq(MemberDomain.validMemberId), eq(PageRequest.of(0, 10)), eq(Long.MAX_VALUE)); // } // // @Test -// @DisplayName("행사 id를 통한 예약 조회 - 성공") -// void getReservationsByEventIdById() throws Exception { -// mockMvc.perform(get("/api/reservations/events/" + id)).andExpect(status().isOk()); -// verify(reservationService).getAllByEventId(id); +// @DisplayName("jwt 토큰 정보에 해당하는 curator의 예약 목록을 조회한다.") +// @WithMockCurator +// void getReservationsByMemberId3() throws Exception { +// // given, when +// mockMvc.perform(get("/api/reservations") +// .param("page", "5") +// .param("size", "20") +// .param("lastId", "100")).andDo(print()) +// .andExpect(status().isOk()); +// +// // then +// then(reservationService).should(times(1)).getAllByMemberId( +// eq(MemberDomain.validMemberId), eq(PageRequest.of(5, 20)), eq(100L)); // } // // @Test -// @DisplayName("예약 생성 - 성공") -// void createReservation() throws Exception { -// mockMvc.perform(post("/api/reservations") -// .with(csrf()) -// .contentType(MediaType.APPLICATION_JSON) -// .content(objectMapper.writeValueAsString(TestData.validReservationRequest))) -// .andExpect(status().isCreated()); +// @DisplayName("Host의 권한으로 자신의 예약을 조회할 수 없다.") +// @WithMockHost +// void getReservationsByMemberId4() throws Exception { +// // given, when +// mockMvc.perform(get("/api/reservations") +// .param("page", "5") +// .param("size", "20") +// .param("lastId", "100")).andDo(print()) +// .andExpect(status().isUnauthorized()); +// +// // then +// then(reservationService).shouldHaveNoInteractions(); // } // // @Test -// @DisplayName("예약 삭제 - 성공") -// void deleteReservation() throws Exception { -// mockMvc.perform(delete("/api/reservations/" + id) -// .with(csrf())) -// .andExpect(status().isNoContent()); -// verify(reservationService).delete(id); +// @DisplayName("PathVariable로 이벤트 ID를 받아 해당하는 이벤트를 조회한다.") +// @WithMockHost +// void getReservationsByMemberId4() throws Exception { +// // given, when +// mockMvc.perform(get("/api/reservations") +// .param("page", "5") +// .param("size", "20") +// .param("lastId", "100")).andDo(print()) +// .andExpect(status().isUnauthorized()); +// +// // then +// then(reservationService).shouldHaveNoInteractions(); // } //} \ No newline at end of file