diff --git a/src/main/java/gift/client/KakaoApiClient.java b/src/main/java/gift/client/KakaoApiClient.java index 06d088b8d..c2cdf78bb 100644 --- a/src/main/java/gift/client/KakaoApiClient.java +++ b/src/main/java/gift/client/KakaoApiClient.java @@ -11,7 +11,7 @@ import gift.dto.kakao.template.KakaoTemplateContent; import gift.dto.kakao.template.KakaoTemplateLink; import gift.exception.BadRequestException; -import gift.exception.InvalidKakaoTokenException; +import gift.exception.UnauthorizedAccessException; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -63,10 +63,7 @@ public KakaoTokenResponse getRefreshedTokenResponse(String refreshToken) { .body(body) .retrieve() .onStatus(statusCode -> statusCode.equals(HttpStatus.UNAUTHORIZED), (req, res) -> { - throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); - }) - .onStatus(statusCode -> statusCode.equals(HttpStatus.BAD_REQUEST), (req, res) -> { - throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); + throw new UnauthorizedAccessException("유효하지 않은 카카오 리프레시 토큰입니다."); }) .body(String.class); @@ -102,7 +99,7 @@ public void sendSelfMessageOrder(String accessToken, GiftOrderResponse giftOrder .body(body) .retrieve() .onStatus(statusCode -> statusCode.equals(HttpStatus.UNAUTHORIZED), (req, res) -> { - throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); + throw new UnauthorizedAccessException(INVALID_TOKEN_MESSAGE); }) .body(String.class); } catch (JsonProcessingException exception) { @@ -122,7 +119,7 @@ private KakaoTemplate getCommerceTemplate(GiftOrderResponse giftOrderResponse) { var objectType = "commerce"; var link = new KakaoTemplateLink("https://gift.kakao.com/product/2370524"); var content = new KakaoTemplateContent(giftOrderResponse.message(), "https://img1.kakaocdn.net/thumb/C320x320@2x.fwebp.q82/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240417111629_616eccb9d4cd464fa06d3430947dce15.jpg", giftOrderResponse.message(), link); - var commerce = new KakaoTemplateCommerce(giftOrderResponse.optionInformation().productName() + "[" + giftOrderResponse.optionInformation().name() + "]", giftOrderResponse.optionInformation().price() * giftOrderResponse.quantity()); + var commerce = new KakaoTemplateCommerce(giftOrderResponse.productBasicInformation().name() + "[" + giftOrderResponse.optionResponse().name() + "]", giftOrderResponse.productBasicInformation().price() * giftOrderResponse.quantity()); return new KakaoTemplate(objectType, content, commerce); } } diff --git a/src/main/java/gift/controller/CategoryController.java b/src/main/java/gift/controller/CategoryController.java index 31108fcab..0b8eafedb 100644 --- a/src/main/java/gift/controller/CategoryController.java +++ b/src/main/java/gift/controller/CategoryController.java @@ -5,9 +5,6 @@ import gift.dto.category.CategoryResponse; import gift.service.CategoryService; import jakarta.validation.Valid; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -50,9 +47,8 @@ public ResponseEntity<CategoryResponse> getCategory(@PathVariable Long id) { } @GetMapping - public ResponseEntity<List<CategoryResponse>> getCategories( - @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - var categories = categoryService.getCategories(pageable); + public ResponseEntity<List<CategoryResponse>> getCategories() { + var categories = categoryService.getCategories(); return ResponseEntity.ok(categories); } diff --git a/src/main/java/gift/controller/OptionController.java b/src/main/java/gift/controller/OptionController.java index 39229bba7..968707c70 100644 --- a/src/main/java/gift/controller/OptionController.java +++ b/src/main/java/gift/controller/OptionController.java @@ -1,14 +1,10 @@ package gift.controller; import gift.controller.api.OptionApi; -import gift.dto.option.OptionAddRequest; +import gift.dto.option.OptionRequest; import gift.dto.option.OptionResponse; -import gift.dto.option.OptionUpdateRequest; import gift.service.OptionService; import jakarta.validation.Valid; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -17,14 +13,13 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.net.URI; import java.util.List; @RestController -@RequestMapping("/api/options") +@RequestMapping("/api/products/{productId}/options") public class OptionController implements OptionApi { private final OptionService optionService; @@ -34,26 +29,32 @@ public OptionController(OptionService optionService) { } @PostMapping - public ResponseEntity<Void> addOption(@Valid @RequestBody OptionAddRequest optionAddRequest) { - var option = optionService.addOption(optionAddRequest); - return ResponseEntity.created(URI.create("/api/options/" + option.id())).build(); + public ResponseEntity<Void> addOption(@PathVariable Long productId, @Valid @RequestBody OptionRequest optionRequest) { + var option = optionService.addOption(productId, optionRequest); + return ResponseEntity.created(URI.create("/api/products/" + productId + "/options/" + option.id())).build(); } @PutMapping("/{id}") - public ResponseEntity<Void> updateOption(@PathVariable Long id, @Valid @RequestBody OptionUpdateRequest optionUpdateRequest) { - optionService.updateOption(id, optionUpdateRequest); + public ResponseEntity<Void> updateOption(@PathVariable Long productId, @PathVariable Long id, @Valid @RequestBody OptionRequest optionUpdateRequest) { + optionService.updateOption(productId, id, optionUpdateRequest); return ResponseEntity.noContent().build(); } + @GetMapping("/{id}") + public ResponseEntity<OptionResponse> getOption(@PathVariable Long productId, @PathVariable Long id) { + var option = optionService.getOption(productId, id); + return ResponseEntity.ok(option); + } + @GetMapping - public ResponseEntity<List<OptionResponse>> getOptions(@RequestParam Long productId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - var options = optionService.getOptions(productId, pageable); + public ResponseEntity<List<OptionResponse>> getOptions(@PathVariable Long productId) { + var options = optionService.getOptions(productId); return ResponseEntity.ok(options); } @DeleteMapping("/{id}") - public ResponseEntity<Void> deleteOption(@PathVariable Long id) { - optionService.deleteOption(id); + public ResponseEntity<Void> deleteOption(@PathVariable Long productId, @PathVariable Long id) { + optionService.deleteOption(productId, id); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java index f04534fad..0f5965211 100644 --- a/src/main/java/gift/controller/ProductController.java +++ b/src/main/java/gift/controller/ProductController.java @@ -1,8 +1,9 @@ package gift.controller; import gift.controller.api.ProductApi; -import gift.dto.product.ProductRequest; +import gift.dto.product.ProductAddRequest; import gift.dto.product.ProductResponse; +import gift.dto.product.ProductUpdateRequest; import gift.service.ProductService; import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; @@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.net.URI; @@ -32,14 +34,14 @@ public ProductController(ProductService productService) { } @PostMapping - public ResponseEntity<Void> addProduct(@Valid @RequestBody ProductRequest productRequest) { - var product = productService.addProduct(productRequest); + public ResponseEntity<Void> addProduct(@Valid @RequestBody ProductAddRequest productAddRequest) { + var product = productService.addProduct(productAddRequest); return ResponseEntity.created(URI.create("/api/products/" + product.id())).build(); } @PutMapping("/{id}") - public ResponseEntity<Void> updateProduct(@PathVariable Long id, @Valid @RequestBody ProductRequest productRequest) { - productService.updateProduct(id, productRequest); + public ResponseEntity<Void> updateProduct(@PathVariable Long id, @Valid @RequestBody ProductUpdateRequest productUpdateRequest) { + productService.updateProduct(id, productUpdateRequest); return ResponseEntity.noContent().build(); } @@ -50,8 +52,12 @@ public ResponseEntity<ProductResponse> getProduct(@PathVariable Long id) { } @GetMapping - public ResponseEntity<List<ProductResponse>> getProducts(@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - var products = productService.getProducts(pageable); + public ResponseEntity<List<ProductResponse>> getProducts(@RequestParam(required = false) Long categoryId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { + if (categoryId == null) { + var products = productService.getProducts(pageable); + return ResponseEntity.ok(products); + } + var products = productService.getProducts(categoryId, pageable); return ResponseEntity.ok(products); } diff --git a/src/main/java/gift/controller/WishProductController.java b/src/main/java/gift/controller/WishProductController.java index 65b3919b4..a6e2dadd4 100644 --- a/src/main/java/gift/controller/WishProductController.java +++ b/src/main/java/gift/controller/WishProductController.java @@ -45,6 +45,12 @@ public ResponseEntity<Void> updateWishProduct(@PathVariable Long id, @Valid @Req return ResponseEntity.noContent().build(); } + @GetMapping("/{id}") + public ResponseEntity<WishProductResponse> getWishProduct(@RequestAttribute("memberId") Long memberId, @PathVariable Long id) { + var wishProduct = wishProductService.getWishProduct(memberId, id); + return ResponseEntity.ok(wishProduct); + } + @GetMapping public ResponseEntity<List<WishProductResponse>> getWishProducts(@RequestAttribute("memberId") Long memberId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { var wishProducts = wishProductService.getWishProducts(memberId, pageable); diff --git a/src/main/java/gift/controller/api/CategoryApi.java b/src/main/java/gift/controller/api/CategoryApi.java index a27f4a433..e25a88e39 100644 --- a/src/main/java/gift/controller/api/CategoryApi.java +++ b/src/main/java/gift/controller/api/CategoryApi.java @@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import java.util.List; @@ -20,6 +19,7 @@ public interface CategoryApi { @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "카테고리 생성 성공"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "409", description = "카테고리 생성 실패(사유 : 이미 존재하는 이름입니다. )"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) ResponseEntity<Void> addCategory(CategoryRequest categoryRequest); @@ -28,6 +28,7 @@ public interface CategoryApi { @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "카테고리 수정 성공"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "409", description = "카테고리 수정 실패(사유 : 이미 존재하는 이름입니다. )"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) ResponseEntity<Void> updateCategory(Long id, CategoryRequest categoryRequest); @@ -46,7 +47,7 @@ public interface CategoryApi { @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) }) - ResponseEntity<List<CategoryResponse>> getCategories(Pageable pageable); + ResponseEntity<List<CategoryResponse>> getCategories(); @Operation(summary = "특정 카테고리를 삭제한다.") @ApiResponses(value = { diff --git a/src/main/java/gift/controller/api/GiftOrderApi.java b/src/main/java/gift/controller/api/GiftOrderApi.java index b70f01768..aeb20697c 100644 --- a/src/main/java/gift/controller/api/GiftOrderApi.java +++ b/src/main/java/gift/controller/api/GiftOrderApi.java @@ -20,7 +20,7 @@ public interface GiftOrderApi { @Operation(summary = "회원의 새 주문을 생성한다.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "주문 생성 성공"), - @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "401", description = "주문 생성 실패(사유 : 카카오 토큰이 만료되었거나, 허용되지 않은 요청입니다.)"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) ResponseEntity<Void> orderOption(Long memberId, GiftOrderRequest giftOrderRequest); diff --git a/src/main/java/gift/controller/api/OptionApi.java b/src/main/java/gift/controller/api/OptionApi.java index 03a84b511..395c034b8 100644 --- a/src/main/java/gift/controller/api/OptionApi.java +++ b/src/main/java/gift/controller/api/OptionApi.java @@ -1,8 +1,7 @@ package gift.controller.api; -import gift.dto.option.OptionAddRequest; +import gift.dto.option.OptionRequest; import gift.dto.option.OptionResponse; -import gift.dto.option.OptionUpdateRequest; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -10,7 +9,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import java.util.List; @@ -22,17 +20,29 @@ public interface OptionApi { @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "옵션 추가 성공"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "409", description = "옵션 추가 실패(사유 : 이미 존재하는 이름입니다. )"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) - ResponseEntity<Void> addOption(OptionAddRequest optionAddRequest); + ResponseEntity<Void> addOption(Long productId, OptionRequest optionRequest); @Operation(summary = "기존 옵션을 수정한다.") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "옵션 수정 성공"), + @ApiResponse(responseCode = "400", description = "옵션 수정 실패(사유 : 옵션과 연결된 상품 ID 가 아닙니다.)"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "409", description = "옵션 수정 실패(사유 : 이미 존재하는 이름입니다. )"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) - ResponseEntity<Void> updateOption(Long id, OptionUpdateRequest optionUpdateRequest); + ResponseEntity<Void> updateOption(Long productId, Long id, OptionRequest optionRequest); + + @Operation(summary = "특정 옵션을 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 옵션 조회 성공", content = @Content(schema = @Schema(implementation = OptionResponse.class))), + @ApiResponse(responseCode = "400", description = "특정 옵션 조회 실패(사유 : 옵션과 연결된 상품 ID 가 아닙니다.)"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity<OptionResponse> getOption(Long productId, Long id); @Operation(summary = "모든 옵션을 페이지 단위로 조회한다.") @ApiResponses(value = { @@ -40,14 +50,15 @@ public interface OptionApi { @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) }) - ResponseEntity<List<OptionResponse>> getOptions(Long productId, Pageable pageable); + ResponseEntity<List<OptionResponse>> getOptions(Long productId); @Operation(summary = "특정 옵션을 삭제한다.") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "옵션 삭제 성공"), + @ApiResponse(responseCode = "400", description = "옵션 삭제 실패(사유 : 옵션과 연결된 상품 ID 가 아닙니다.)"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), @ApiResponse(responseCode = "404", description = "옵션 삭제 실패(사유 : 존재하지 않는 ID 입니다.)"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) - ResponseEntity<Void> deleteOption(Long id); + ResponseEntity<Void> deleteOption(Long productId, Long id); } diff --git a/src/main/java/gift/controller/api/ProductApi.java b/src/main/java/gift/controller/api/ProductApi.java index 1ff91a45c..1fd85f538 100644 --- a/src/main/java/gift/controller/api/ProductApi.java +++ b/src/main/java/gift/controller/api/ProductApi.java @@ -1,7 +1,8 @@ package gift.controller.api; -import gift.dto.product.ProductRequest; +import gift.dto.product.ProductAddRequest; import gift.dto.product.ProductResponse; +import gift.dto.product.ProductUpdateRequest; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -20,18 +21,20 @@ public interface ProductApi { @Operation(summary = "새 상품을 등록한다.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "상품 등록 성공"), + @ApiResponse(responseCode = "400", description = "상품 등록 실패(사유 : 카카오가 포함된 이름입니다.)"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) - ResponseEntity<Void> addProduct(ProductRequest productRequest); + ResponseEntity<Void> addProduct(ProductAddRequest productAddRequest); @Operation(summary = "기존 상품을 수정한다.") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "상품 수정 성공"), + @ApiResponse(responseCode = "400", description = "상품 수정 실패(사유 : 카카오가 포함된 이름입니다.)"), @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), @ApiResponse(responseCode = "500", description = "내부 서버의 오류") }) - ResponseEntity<Void> updateProduct(Long id, ProductRequest productRequest); + ResponseEntity<Void> updateProduct(Long id, ProductUpdateRequest productUpdateRequest); @Operation(summary = "특정 상품을 조회한다.") @ApiResponses(value = { @@ -47,7 +50,7 @@ public interface ProductApi { @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) }) - ResponseEntity<List<ProductResponse>> getProducts(Pageable pageable); + ResponseEntity<List<ProductResponse>> getProducts(Long categoryId, Pageable pageable); @Operation(summary = "특정 상품을 삭제한다.") @ApiResponses(value = { diff --git a/src/main/java/gift/controller/api/WishProductApi.java b/src/main/java/gift/controller/api/WishProductApi.java index 1cebb2dce..ae83c3165 100644 --- a/src/main/java/gift/controller/api/WishProductApi.java +++ b/src/main/java/gift/controller/api/WishProductApi.java @@ -1,5 +1,6 @@ package gift.controller.api; +import gift.dto.option.OptionResponse; import gift.dto.wishproduct.WishProductAddRequest; import gift.dto.wishproduct.WishProductResponse; import gift.dto.wishproduct.WishProductUpdateRequest; @@ -34,6 +35,15 @@ public interface WishProductApi { }) ResponseEntity<Void> updateWishProduct(Long id, WishProductUpdateRequest wishProductUpdateRequest); + @Operation(summary = "회원의 특정 위시 리스트를 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 위시 리스트 조회 성공", content = @Content(schema = @Schema(implementation = OptionResponse.class))), + @ApiResponse(responseCode = "400", description = "특정 위시 리스트 조회 실패(사유 : 다른 사람의 위시 리스트는 접근할 수 없습니다.)", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity<WishProductResponse> getWishProduct(Long memberId, Long id); + @Operation(summary = "회원의 위시 리스트에 있는 상품을 페이지 단위로 조회한다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "모든 위시 리스트 조회 성공", content = @Content(array = @ArraySchema(schema = @Schema(implementation = WishProductResponse.class)))), diff --git a/src/main/java/gift/dto/auth/AuthResponse.java b/src/main/java/gift/dto/auth/AuthResponse.java index 10dac2a0e..d06dfbaa7 100644 --- a/src/main/java/gift/dto/auth/AuthResponse.java +++ b/src/main/java/gift/dto/auth/AuthResponse.java @@ -1,6 +1,8 @@ package gift.dto.auth; -public record AuthResponse(String token) { +public record AuthResponse( + String token +) { public static AuthResponse of(String token) { return new AuthResponse(token); } diff --git a/src/main/java/gift/dto/category/CategoryInformation.java b/src/main/java/gift/dto/category/CategoryInformation.java index 11c6e1c74..1eff662b6 100644 --- a/src/main/java/gift/dto/category/CategoryInformation.java +++ b/src/main/java/gift/dto/category/CategoryInformation.java @@ -1,6 +1,9 @@ package gift.dto.category; -public record CategoryInformation(Long id, String name) { +public record CategoryInformation( + Long id, + String name +) { public static CategoryInformation of(Long id, String name) { return new CategoryInformation(id, name); } diff --git a/src/main/java/gift/dto/category/CategoryResponse.java b/src/main/java/gift/dto/category/CategoryResponse.java index 370a9a4ae..2ab654bf9 100644 --- a/src/main/java/gift/dto/category/CategoryResponse.java +++ b/src/main/java/gift/dto/category/CategoryResponse.java @@ -1,6 +1,12 @@ package gift.dto.category; -public record CategoryResponse(Long id, String name, String description, String color, String imageUrl) { +public record CategoryResponse( + Long id, + String name, + String description, + String color, + String imageUrl +) { public static CategoryResponse of(Long id, String name, String description, String color, String imageUrl) { return new CategoryResponse(id, name, description, color, imageUrl); } diff --git a/src/main/java/gift/dto/giftorder/GiftOrderResponse.java b/src/main/java/gift/dto/giftorder/GiftOrderResponse.java index 5bda70e3c..7e21cc0cf 100644 --- a/src/main/java/gift/dto/giftorder/GiftOrderResponse.java +++ b/src/main/java/gift/dto/giftorder/GiftOrderResponse.java @@ -1,11 +1,22 @@ package gift.dto.giftorder; -import gift.dto.option.OptionInformation; +import com.fasterxml.jackson.annotation.JsonProperty; +import gift.dto.option.OptionResponse; +import gift.dto.product.ProductBasicInformation; import java.time.LocalDateTime; -public record GiftOrderResponse(Long id, OptionInformation optionInformation, Integer quantity, LocalDateTime orderDateTime, String message) { - public static GiftOrderResponse of(Long id, OptionInformation optionInformation, Integer quantity, LocalDateTime orderDateTime, String message) { - return new GiftOrderResponse(id, optionInformation, quantity, orderDateTime, message); +public record GiftOrderResponse( + Long id, + @JsonProperty("product") + ProductBasicInformation productBasicInformation, + @JsonProperty("option") + OptionResponse optionResponse, + Integer quantity, + LocalDateTime orderDateTime, + String message +) { + public static GiftOrderResponse of(Long id, ProductBasicInformation productBasicInformation, OptionResponse optionResponse, Integer quantity, LocalDateTime orderDateTime, String message) { + return new GiftOrderResponse(id, productBasicInformation, optionResponse, quantity, orderDateTime, message); } } diff --git a/src/main/java/gift/dto/kakao/KakaoAuthInformation.java b/src/main/java/gift/dto/kakao/KakaoAuthInformation.java index 4c408e5e7..5bde93a88 100644 --- a/src/main/java/gift/dto/kakao/KakaoAuthInformation.java +++ b/src/main/java/gift/dto/kakao/KakaoAuthInformation.java @@ -1,6 +1,9 @@ package gift.dto.kakao; -public record KakaoAuthInformation(String name, String email) { +public record KakaoAuthInformation( + String name, + String email +) { public static KakaoAuthInformation of(String name, String email) { return new KakaoAuthInformation(name, email); } diff --git a/src/main/java/gift/dto/option/OptionAddRequest.java b/src/main/java/gift/dto/option/OptionAddRequest.java deleted file mode 100644 index bb0e3ea62..000000000 --- a/src/main/java/gift/dto/option/OptionAddRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package gift.dto.option; - -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Length; - -public record OptionAddRequest( - @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") - @Length(max = 50, message = "이름의 길이는 50자를 초과할 수 없습니다.") - @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") - String name, - @Min(value = 1, message = "수량은 최소 1개 이상, 1억개 미만입니다.") - @Max(value = 100_000_000, message = "수량은 최소 1개 이상, 1억개 미만입니다.") - Integer quantity, - @NotNull(message = "상품은 반드시 선택되어야 합니다.") - Long productId -) { -} diff --git a/src/main/java/gift/dto/option/OptionInformation.java b/src/main/java/gift/dto/option/OptionInformation.java deleted file mode 100644 index 6d039dfcb..000000000 --- a/src/main/java/gift/dto/option/OptionInformation.java +++ /dev/null @@ -1,7 +0,0 @@ -package gift.dto.option; - -public record OptionInformation(Long id, String productName, Integer price, String name) { - public static OptionInformation of(Long id, String productName, Integer price, String name) { - return new OptionInformation(id, productName, price, name); - } -} diff --git a/src/main/java/gift/dto/option/OptionUpdateRequest.java b/src/main/java/gift/dto/option/OptionRequest.java similarity index 95% rename from src/main/java/gift/dto/option/OptionUpdateRequest.java rename to src/main/java/gift/dto/option/OptionRequest.java index d309e1130..9c7f0f982 100644 --- a/src/main/java/gift/dto/option/OptionUpdateRequest.java +++ b/src/main/java/gift/dto/option/OptionRequest.java @@ -6,7 +6,7 @@ import jakarta.validation.constraints.Pattern; import org.hibernate.validator.constraints.Length; -public record OptionUpdateRequest( +public record OptionRequest( @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") @Length(max = 50, message = "이름의 길이는 50자를 초과할 수 없습니다.") @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") diff --git a/src/main/java/gift/dto/option/OptionResponse.java b/src/main/java/gift/dto/option/OptionResponse.java index 91b41ced6..6d76c4fbb 100644 --- a/src/main/java/gift/dto/option/OptionResponse.java +++ b/src/main/java/gift/dto/option/OptionResponse.java @@ -1,6 +1,10 @@ package gift.dto.option; -public record OptionResponse(Long id, String name, Integer quantity) { +public record OptionResponse( + Long id, + String name, + Integer quantity +) { public static OptionResponse of(Long id, String name, Integer quantity) { return new OptionResponse(id, name, quantity); } diff --git a/src/main/java/gift/dto/product/ProductAddRequest.java b/src/main/java/gift/dto/product/ProductAddRequest.java new file mode 100644 index 000000000..317eab665 --- /dev/null +++ b/src/main/java/gift/dto/product/ProductAddRequest.java @@ -0,0 +1,27 @@ +package gift.dto.product; + +import gift.dto.option.OptionRequest; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.PositiveOrZero; +import org.hibernate.validator.constraints.Length; + +import java.util.List; + +public record ProductAddRequest( + @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") + @Length(max = 15, message = "이름의 길이는 15자를 초과할 수 없습니다.") + @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") + String name, + @PositiveOrZero(message = "금액은 0보다 크거나 같아야 합니다.") + Integer price, + @NotBlank(message = "상품 이미지는 필수로 입력해야 합니다.") + String imageUrl, + @NotNull(message = "상품 카테고리는 반드시 선택되어야 합니다.") + Long categoryId, + @NotEmpty(message = "상품의 옵션은 반드시 1개 이상 존재해야 합니다.") + List<OptionRequest> options +) { +} diff --git a/src/main/java/gift/dto/product/ProductBasicInformation.java b/src/main/java/gift/dto/product/ProductBasicInformation.java index 3d8a0ae57..71990461b 100644 --- a/src/main/java/gift/dto/product/ProductBasicInformation.java +++ b/src/main/java/gift/dto/product/ProductBasicInformation.java @@ -1,6 +1,10 @@ package gift.dto.product; -public record ProductBasicInformation(Long id, String name, Integer price) { +public record ProductBasicInformation( + Long id, + String name, + Integer price +) { public static ProductBasicInformation of(Long id, String name, Integer price) { return new ProductBasicInformation(id, name, price); } diff --git a/src/main/java/gift/dto/product/ProductResponse.java b/src/main/java/gift/dto/product/ProductResponse.java index 806988f31..4c53a6cb6 100644 --- a/src/main/java/gift/dto/product/ProductResponse.java +++ b/src/main/java/gift/dto/product/ProductResponse.java @@ -1,9 +1,21 @@ package gift.dto.product; +import com.fasterxml.jackson.annotation.JsonProperty; import gift.dto.category.CategoryInformation; +import gift.dto.option.OptionResponse; -public record ProductResponse(Long id, String name, Integer price, String imageUrl, CategoryInformation categoryInformation) { - public static ProductResponse of(Long id, String name, Integer price, String imageUrl, CategoryInformation categoryInformation) { - return new ProductResponse(id, name, price, imageUrl, categoryInformation); +import java.util.List; + +public record ProductResponse( + Long id, + String name, + Integer price, + String imageUrl, + @JsonProperty("category") + CategoryInformation categoryInformation, + List<OptionResponse> options +) { + public static ProductResponse of(Long id, String name, Integer price, String imageUrl, CategoryInformation categoryInformation, List<OptionResponse> options) { + return new ProductResponse(id, name, price, imageUrl, categoryInformation, options); } } diff --git a/src/main/java/gift/dto/product/ProductRequest.java b/src/main/java/gift/dto/product/ProductUpdateRequest.java similarity index 96% rename from src/main/java/gift/dto/product/ProductRequest.java rename to src/main/java/gift/dto/product/ProductUpdateRequest.java index 70182125c..19bafe1a5 100644 --- a/src/main/java/gift/dto/product/ProductRequest.java +++ b/src/main/java/gift/dto/product/ProductUpdateRequest.java @@ -6,7 +6,7 @@ import jakarta.validation.constraints.PositiveOrZero; import org.hibernate.validator.constraints.Length; -public record ProductRequest( +public record ProductUpdateRequest( @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") @Length(max = 15, message = "이름의 길이는 15자를 초과할 수 없습니다.") @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") diff --git a/src/main/java/gift/dto/wishproduct/WishProductResponse.java b/src/main/java/gift/dto/wishproduct/WishProductResponse.java index 2eb609312..b2e5d3353 100644 --- a/src/main/java/gift/dto/wishproduct/WishProductResponse.java +++ b/src/main/java/gift/dto/wishproduct/WishProductResponse.java @@ -1,8 +1,14 @@ package gift.dto.wishproduct; +import com.fasterxml.jackson.annotation.JsonProperty; import gift.dto.product.ProductBasicInformation; -public record WishProductResponse(Long id, ProductBasicInformation productBasicInformation, Integer quantity) { +public record WishProductResponse( + Long id, + @JsonProperty("product") + ProductBasicInformation productBasicInformation, + Integer quantity +) { public static WishProductResponse of(Long id, ProductBasicInformation productBasicInformation, Integer quantity) { return new WishProductResponse(id, productBasicInformation, quantity); } diff --git a/src/main/java/gift/repository/OptionRepository.java b/src/main/java/gift/repository/OptionRepository.java index aa8ee4394..089597def 100644 --- a/src/main/java/gift/repository/OptionRepository.java +++ b/src/main/java/gift/repository/OptionRepository.java @@ -2,7 +2,6 @@ import gift.model.Option; import jakarta.persistence.LockModeType; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; @@ -13,7 +12,7 @@ @Repository public interface OptionRepository extends JpaRepository<Option, Long> { - List<Option> findAllByProductId(Long productId, Pageable pageable); + List<Option> findAllByProductId(Long productId); boolean existsOptionByProductIdAndName(Long productId, String name); diff --git a/src/main/java/gift/repository/ProductRepository.java b/src/main/java/gift/repository/ProductRepository.java index 51ec2a939..2ba6cd06c 100644 --- a/src/main/java/gift/repository/ProductRepository.java +++ b/src/main/java/gift/repository/ProductRepository.java @@ -1,6 +1,7 @@ package gift.repository; import gift.model.Product; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -9,4 +10,6 @@ @Repository public interface ProductRepository extends JpaRepository<Product, Long> { List<Product> findAllByCategoryId(Long categoryId); + + List<Product> findAllByCategoryId(Long categoryId, Pageable pageable); } diff --git a/src/main/java/gift/service/CategoryService.java b/src/main/java/gift/service/CategoryService.java index ab080a051..d147130fc 100644 --- a/src/main/java/gift/service/CategoryService.java +++ b/src/main/java/gift/service/CategoryService.java @@ -6,7 +6,6 @@ import gift.exception.NotFoundElementException; import gift.model.Category; import gift.repository.CategoryRepository; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -44,8 +43,8 @@ public CategoryResponse getCategory(Long id) { } @Transactional(readOnly = true) - public List<CategoryResponse> getCategories(Pageable pageable) { - return categoryRepository.findAll(pageable) + public List<CategoryResponse> getCategories() { + return categoryRepository.findAll() .stream() .map(this::getCategoryResponseFromCategory) .toList(); diff --git a/src/main/java/gift/service/GiftOrderService.java b/src/main/java/gift/service/GiftOrderService.java index a4eb4f558..09ca4d96a 100644 --- a/src/main/java/gift/service/GiftOrderService.java +++ b/src/main/java/gift/service/GiftOrderService.java @@ -2,7 +2,8 @@ import gift.dto.giftorder.GiftOrderRequest; import gift.dto.giftorder.GiftOrderResponse; -import gift.dto.option.OptionInformation; +import gift.dto.option.OptionResponse; +import gift.dto.product.ProductBasicInformation; import gift.exception.NotFoundElementException; import gift.model.GiftOrder; import gift.model.Option; @@ -69,7 +70,9 @@ private GiftOrder saveGiftOrderWithGiftOrderRequest(Long memberId, Option option } private GiftOrderResponse getGiftOrderResponseFromGiftOrder(GiftOrder giftOrder) { - var optionInformation = OptionInformation.of(giftOrder.getId(), giftOrder.getOption().getProduct().getName(), giftOrder.getOption().getProduct().getPrice(), giftOrder.getOption().getName()); - return GiftOrderResponse.of(giftOrder.getId(), optionInformation, giftOrder.getQuantity(), giftOrder.getCreatedDate(), giftOrder.getMessage()); + var product = giftOrder.getOption().getProduct(); + var productBasicInformation = ProductBasicInformation.of(product.getId(), product.getName(), product.getPrice()); + var optionResponse = OptionResponse.of(giftOrder.getId(), giftOrder.getOption().getProduct().getName(), giftOrder.getQuantity()); + return GiftOrderResponse.of(giftOrder.getId(), productBasicInformation, optionResponse, giftOrder.getQuantity(), giftOrder.getCreatedDate(), giftOrder.getMessage()); } } diff --git a/src/main/java/gift/service/KakaoService.java b/src/main/java/gift/service/KakaoService.java index 30292d38f..db3e1d7da 100644 --- a/src/main/java/gift/service/KakaoService.java +++ b/src/main/java/gift/service/KakaoService.java @@ -7,6 +7,7 @@ import gift.dto.kakao.KakaoTokenResponse; import gift.exception.InvalidKakaoTokenException; import gift.exception.NotFoundElementException; +import gift.exception.UnauthorizedAccessException; import gift.model.Member; import gift.model.OauthToken; import gift.model.OauthType; @@ -59,7 +60,7 @@ public KakaoAuthInformation getKakaoAuthInformation(KakaoTokenResponse kakaoToke public void sendOrderResponseWithKakaoMessage(Long memberId, GiftOrderResponse giftOrderResponse) { var kakaoToken = oauthTokenRepository.findByMemberIdAndOauthType(memberId, OauthType.KAKAO) - .orElseThrow(() -> new InvalidKakaoTokenException(memberId + "를 가진 이용자의 카카오 토큰 정보가 존재하지 않습니다.")); + .orElseThrow(() -> new NotFoundElementException(memberId + "를 가진 이용자의 카카오 토큰 정보가 존재하지 않습니다.")); var validatedKakaoToken = tokenValidation(kakaoToken); kakaoApiClient.sendSelfMessageOrder(validatedKakaoToken.getAccessToken(), giftOrderResponse); } @@ -71,7 +72,7 @@ public void deleteByMemberId(Long memberId) { private OauthToken tokenValidation(OauthToken oauthToken) { if (!oauthToken.canUseRefreshToken()) { - throw new InvalidKakaoTokenException("유효하지 않은 토큰입니다. 갱신이 필요합니다."); + throw new UnauthorizedAccessException("유효하지 않은 카카오 토큰입니다. 갱신이 필요합니다."); } if (!oauthToken.canUseAccessToken()) { var kakaoTokenResponse = kakaoApiClient.getRefreshedTokenResponse(oauthToken.getRefreshToken()); diff --git a/src/main/java/gift/service/OptionService.java b/src/main/java/gift/service/OptionService.java index 61d755579..68d5bf8e8 100644 --- a/src/main/java/gift/service/OptionService.java +++ b/src/main/java/gift/service/OptionService.java @@ -2,16 +2,15 @@ import gift.dto.giftorder.GiftOrderRequest; import gift.dto.giftorder.GiftOrderResponse; -import gift.dto.option.OptionAddRequest; +import gift.dto.option.OptionRequest; import gift.dto.option.OptionResponse; -import gift.dto.option.OptionUpdateRequest; +import gift.exception.BadRequestException; import gift.exception.DuplicatedNameException; import gift.exception.NotFoundElementException; import gift.model.Option; import gift.model.Product; import gift.repository.OptionRepository; import gift.repository.ProductRepository; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,27 +30,37 @@ public OptionService(OptionRepository optionRepository, ProductRepository produc this.giftOrderService = giftOrderService; } - public OptionResponse addOption(OptionAddRequest optionAddRequest) { - optionNameValidation(optionAddRequest.productId(), optionAddRequest.name()); - var option = saveOptionWithOptionRequest(optionAddRequest); + public OptionResponse addOption(Long productId, OptionRequest optionRequest) { + optionNameValidation(productId, optionRequest.name()); + var option = saveOptionWithOptionRequest(productId, optionRequest); return getOptionResponseFromOption(option); } - public void updateOption(Long id, OptionUpdateRequest optionUpdateRequest) { + public void updateOption(Long productId, Long id, OptionRequest optionRequest) { var option = findOptionById(id); - option.updateOptionInfo(optionUpdateRequest.name(), optionUpdateRequest.quantity()); + optionProductValidation(productId, option); + option.updateOptionInfo(optionRequest.name(), optionRequest.quantity()); optionRepository.save(option); } @Transactional(readOnly = true) - public List<OptionResponse> getOptions(Long productId, Pageable pageable) { - return optionRepository.findAllByProductId(productId, pageable) + public OptionResponse getOption(Long productId, Long id) { + var option = findOptionById(id); + optionProductValidation(productId, option); + return getOptionResponseFromOption(option); + } + + @Transactional(readOnly = true) + public List<OptionResponse> getOptions(Long productId) { + return optionRepository.findAllByProductId(productId) .stream() .map(this::getOptionResponseFromOption) .toList(); } - public void deleteOption(Long optionId) { + public void deleteOption(Long productId, Long optionId) { + var option = findOptionById(optionId); + optionProductValidation(productId, option); if (!optionRepository.existsById(optionId)) { throw new NotFoundElementException("존재하지 않는 상품 옵션의 ID 입니다."); } @@ -63,11 +72,6 @@ public void deleteAllByProductId(Long productId) { optionRepository.deleteAllByProductId(productId); } - public void makeDefaultOption(Product product) { - var option = new Option(product, "기본", 1000); - optionRepository.save(option); - } - public GiftOrderResponse orderOption(Long memberId, GiftOrderRequest giftOrderRequest) { var option = subtractOptionQuantity(giftOrderRequest.optionId(), giftOrderRequest.quantity()); return giftOrderService.addGiftOrder(memberId, option, giftOrderRequest); @@ -80,9 +84,9 @@ private Option subtractOptionQuantity(Long id, Integer quantity) { return optionRepository.save(option); } - private Option saveOptionWithOptionRequest(OptionAddRequest optionAddRequest) { - var product = findProductById(optionAddRequest.productId()); - var option = new Option(product, optionAddRequest.name(), optionAddRequest.quantity()); + private Option saveOptionWithOptionRequest(Long productId, OptionRequest optionRequest) { + var product = findProductById(productId); + var option = new Option(product, optionRequest.name(), optionRequest.quantity()); return optionRepository.save(option); } @@ -105,4 +109,10 @@ private void optionNameValidation(Long productId, String name) { throw new DuplicatedNameException("이미 존재하는 상품의 상품 옵션입니다."); } } + + private void optionProductValidation(Long productId, Option option) { + if (!option.getProduct().getId().equals(productId)) { + throw new BadRequestException("잘못된 접근입니다."); + } + } } diff --git a/src/main/java/gift/service/ProductService.java b/src/main/java/gift/service/ProductService.java index 8a54344be..e8c66f5cf 100644 --- a/src/main/java/gift/service/ProductService.java +++ b/src/main/java/gift/service/ProductService.java @@ -1,14 +1,15 @@ package gift.service; import gift.dto.category.CategoryInformation; -import gift.dto.product.ProductRequest; +import gift.dto.option.OptionRequest; +import gift.dto.product.ProductAddRequest; import gift.dto.product.ProductResponse; +import gift.dto.product.ProductUpdateRequest; import gift.exception.InvalidProductNameWithKAKAOException; import gift.exception.NotFoundElementException; import gift.model.Category; import gift.model.Product; import gift.repository.CategoryRepository; -import gift.repository.OptionRepository; import gift.repository.ProductRepository; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -25,23 +26,24 @@ public class ProductService { private final WishProductService wishProductService; private final OptionService optionService; - public ProductService(ProductRepository productRepository, CategoryRepository categoryRepository, WishProductService wishProductService, OptionRepository optionRepository, OptionService optionService) { + public ProductService(ProductRepository productRepository, CategoryRepository categoryRepository, WishProductService wishProductService, OptionService optionService) { this.productRepository = productRepository; this.categoryRepository = categoryRepository; this.wishProductService = wishProductService; this.optionService = optionService; } - public ProductResponse addProduct(ProductRequest productRequest) { - productNameValidation(productRequest); - var product = saveProductWithProductRequest(productRequest); - optionService.makeDefaultOption(product); + public ProductResponse addProduct(ProductAddRequest productAddRequest) { + productNameValidation(productAddRequest.name()); + var product = saveProductWithProductRequest(productAddRequest); + makeOptionsWithProductRequest(product, productAddRequest.options()); return getProductResponseFromProduct(product); } - public void updateProduct(Long id, ProductRequest productRequest) { + public void updateProduct(Long id, ProductUpdateRequest productUpdateRequest) { + productNameValidation(productUpdateRequest.name()); var product = findProductById(id); - updateProductWithProductRequest(product, productRequest); + updateProductWithProductRequest(product, productUpdateRequest); } @Transactional(readOnly = true) @@ -58,6 +60,14 @@ public List<ProductResponse> getProducts(Pageable pageable) { .toList(); } + @Transactional(readOnly = true) + public List<ProductResponse> getProducts(Long categoryId, Pageable pageable) { + return productRepository.findAllByCategoryId(categoryId, pageable) + .stream() + .map(this::getProductResponseFromProduct) + .toList(); + } + public void deleteProduct(Long productId) { if (!productRepository.existsById(productId)) { throw new NotFoundElementException("존재하지 않는 상품의 ID 입니다."); @@ -74,26 +84,33 @@ public void deleteAllProductWithCategoryId(Long categoryId) { } } - private Product saveProductWithProductRequest(ProductRequest productRequest) { - var category = categoryRepository.findById(productRequest.categoryId()) - .orElseThrow(() -> new NotFoundElementException(productRequest.categoryId() + "를 가진 상품 카테고리가 존재하지 않습니다.")); - var product = new Product(productRequest.name(), productRequest.price(), productRequest.imageUrl(), category); + private Product saveProductWithProductRequest(ProductAddRequest productAddRequest) { + var category = categoryRepository.findById(productAddRequest.categoryId()) + .orElseThrow(() -> new NotFoundElementException(productAddRequest.categoryId() + "를 가진 상품 카테고리가 존재하지 않습니다.")); + var product = new Product(productAddRequest.name(), productAddRequest.price(), productAddRequest.imageUrl(), category); return productRepository.save(product); } - private void updateProductWithProductRequest(Product product, ProductRequest productRequest) { - product.updateProductInfo(productRequest.name(), productRequest.price(), productRequest.imageUrl()); + private void makeOptionsWithProductRequest(Product product, List<OptionRequest> options) { + for (var option : options) { + optionService.addOption(product.getId(), option); + } + } + + private void updateProductWithProductRequest(Product product, ProductUpdateRequest productUpdateRequest) { + product.updateProductInfo(productUpdateRequest.name(), productUpdateRequest.price(), productUpdateRequest.imageUrl()); productRepository.save(product); } - private void productNameValidation(ProductRequest productRequest) { - if (!productRequest.name().contains("카카오")) return; + private void productNameValidation(String name) { + if (!name.contains("카카오")) return; throw new InvalidProductNameWithKAKAOException("카카오가 포함된 문구는 담당 MD와 협의한 경우에만 사용할 수 있습니다."); } private ProductResponse getProductResponseFromProduct(Product product) { var categoryInformation = getCategoryInformationFromCategory(product.getCategory()); - return ProductResponse.of(product.getId(), product.getName(), product.getPrice(), product.getImageUrl(), categoryInformation); + var options = optionService.getOptions(product.getId()); + return ProductResponse.of(product.getId(), product.getName(), product.getPrice(), product.getImageUrl(), categoryInformation, options); } private Product findProductById(Long id) { diff --git a/src/main/java/gift/service/WishProductService.java b/src/main/java/gift/service/WishProductService.java index 7b0503592..41254faa4 100644 --- a/src/main/java/gift/service/WishProductService.java +++ b/src/main/java/gift/service/WishProductService.java @@ -4,6 +4,7 @@ import gift.dto.wishproduct.WishProductAddRequest; import gift.dto.wishproduct.WishProductResponse; import gift.dto.wishproduct.WishProductUpdateRequest; +import gift.exception.BadRequestException; import gift.exception.NotFoundElementException; import gift.model.Member; import gift.model.Product; @@ -52,6 +53,15 @@ public void updateWishProduct(Long id, WishProductUpdateRequest wishProductUpdat updateWishProductWithQuantity(wishProduct, wishProductUpdateRequest.quantity()); } + @Transactional(readOnly = true) + public WishProductResponse getWishProduct(Long memberId, Long id) { + var wishProduct = findWishProductById(id); + if (!wishProduct.getMember().getId().equals(memberId)) { + throw new BadRequestException("다른 사람의 위시 리스트는 접근할 수 없습니다."); + } + return getWishProductResponseFromWishProduct(wishProduct); + } + @Transactional(readOnly = true) public List<WishProductResponse> getWishProducts(Long memberId, Pageable pageable) { return wishProductRepository.findAllByMemberId(memberId, pageable) diff --git a/src/test/java/gift/controller/OptionControllerTest.java b/src/test/java/gift/controller/OptionControllerTest.java index 0595d3782..97464c132 100644 --- a/src/test/java/gift/controller/OptionControllerTest.java +++ b/src/test/java/gift/controller/OptionControllerTest.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import gift.dto.auth.LoginRequest; -import gift.dto.option.OptionAddRequest; +import gift.dto.option.OptionRequest; import gift.exception.ExceptionResponse; import gift.service.OptionService; import gift.service.auth.AuthService; @@ -48,10 +48,10 @@ void setBaseData() { @DisplayName("잘못된 수량으로 된 오류 상품 옵션 생성하기") void failAddOptionWithWrongQuantity() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("기본", 0, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("기본", 0))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -64,10 +64,10 @@ void failAddOptionWithWrongQuantity() throws Exception { @DisplayName("빈 이름을 가진 오류 상품 옵션 생성하기") void failAddOptionWithEmptyName() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("", 1000, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("", 1000))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -80,10 +80,10 @@ void failAddOptionWithEmptyName() throws Exception { @DisplayName("이름의 길이가 50초과인 오류 상품 생성하기") void failAddOptionWithNameOverLength() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("aaaaaaaaaaaaaaaaaabbbbbbbbbbbbcccccccccccccccddddddddddddddddddddwwwwwwwwwwqqqqqqqqqqqqqqq", 1000, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("aaaaaaaaaaaaaaaaaabbbbbbbbbbbbcccccccccccccccddddddddddddddddddddwwwwwwwwwwqqqqqqqqqqqqqqq", 1000))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -96,10 +96,10 @@ void failAddOptionWithNameOverLength() throws Exception { @DisplayName("정상 상품 옵션 생성하기") void successAddOption() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("Large", 1500, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("Large", 1500))); //when var result = mockMvc.perform(postRequest); //then @@ -112,10 +112,10 @@ void successAddOption() throws Exception { @DisplayName("존재하지 않는 상품에 대한 옵션 생성하기") void failAddOptionWithNotExistProductId() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1000/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("Large", 1500, 1000L))); + .content(objectMapper.writeValueAsString(new OptionRequest("Large", 1500))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -127,10 +127,10 @@ void failAddOptionWithNotExistProductId() throws Exception { @DisplayName("정상 옵션 생성하기 - 특수문자 포함") void successAddOptionWithSpecialChar() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("햄버거()[]+-&/_", 1000, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("햄버거()[]+-&/_", 1000))); //when var result = mockMvc.perform(postRequest); //then @@ -143,10 +143,10 @@ void successAddOptionWithSpecialChar() throws Exception { @DisplayName("정상 옵션 생성하기 - 공백 포함") void successAddOptionWithEmptySpace() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("햄버거 햄버거 햄버거", 1000, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("햄버거 햄버거 햄버거", 1000))); //when var result = mockMvc.perform(postRequest); //then @@ -159,10 +159,10 @@ void successAddOptionWithEmptySpace() throws Exception { @DisplayName("오류 상품 생성하기 - 허용되지 않은 특수문자 포함") void failAddOptionWithSpecialChar() throws Exception { //given - var postRequest = post("/api/options") + var postRequest = post("/api/products/1/options") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new OptionAddRequest("햄버거()[]+-&/_**", 1000, 1L))); + .content(objectMapper.writeValueAsString(new OptionRequest("햄버거()[]+-&/_**", 1000))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -173,8 +173,10 @@ void failAddOptionWithSpecialChar() throws Exception { private void deleteOptionWithCreatedHeader(MvcResult mvcResult) { var location = mvcResult.getResponse().getHeader("Location"); - var optionId = location.replaceAll("/api/options/", ""); - optionService.deleteOption(Long.parseLong(optionId)); + var splitResult = location.split("/"); + var productId = Long.parseLong(splitResult[splitResult.length - 3]); + var optionId = Long.parseLong(splitResult[splitResult.length - 1]); + optionService.deleteOption(productId, optionId); } private ExceptionResponse getResponseMessage(MvcResult result) throws Exception { diff --git a/src/test/java/gift/controller/ProductControllerTest.java b/src/test/java/gift/controller/ProductControllerTest.java index 00d42ff86..874784b91 100644 --- a/src/test/java/gift/controller/ProductControllerTest.java +++ b/src/test/java/gift/controller/ProductControllerTest.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import gift.dto.auth.LoginRequest; -import gift.dto.product.ProductRequest; +import gift.dto.option.OptionRequest; +import gift.dto.product.ProductAddRequest; import gift.dto.product.ProductResponse; import gift.exception.ExceptionResponse; import gift.service.OptionService; @@ -16,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -61,10 +61,14 @@ void setBaseData() { @DisplayName("잘못된 가격으로 된 오류 상품 생성하기") void failAddProductWithWrongPrice() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); + var productRequest = new ProductAddRequest("상품1", -10000, "이미지 주소", 1L, options); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("상품1", -1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(productRequest)); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -77,10 +81,13 @@ void failAddProductWithWrongPrice() throws Exception { @DisplayName("이름의 길이가 15초과인 오류 상품 생성하기") void failAddProductWithNameOverLength() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("햄버거햄버거햄버거햄버거햄버거햄", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("햄버거햄버거햄버거햄버거햄버거햄", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -93,10 +100,13 @@ void failAddProductWithNameOverLength() throws Exception { @DisplayName("카카오를 포함한 이름을 가진 오류 상품 생성하기") void failAddProductWithNameKakao() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("카카오456", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("카카오456", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -109,10 +119,13 @@ void failAddProductWithNameKakao() throws Exception { @DisplayName("빈 이름을 가진 오류 상품 생성하기") void failAddProductWithEmptyName() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -125,10 +138,13 @@ void failAddProductWithEmptyName() throws Exception { @DisplayName("정상 상품 생성하기 - 특수문자 포함") void successAddProductWithSpecialChar() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("햄버거()[]+-&/_", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("햄버거()[]+-&/_", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest); //then @@ -141,10 +157,13 @@ void successAddProductWithSpecialChar() throws Exception { @DisplayName("정상 상품 생성하기 - 공백 포함") void successAddProductWithEmptySpace() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("햄버거 햄버거 햄버거", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("햄버거 햄버거 햄버거", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest); //then @@ -157,10 +176,13 @@ void successAddProductWithEmptySpace() throws Exception { @DisplayName("오류 상품 생성하기 - 허용되지 않은 특수문자 포함") void failAddProductWithSpecialChar() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("햄버거()[]+-&/_**", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("햄버거()[]+-&/_**", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest).andReturn(); //then @@ -174,7 +196,10 @@ void failAddProductWithSpecialChar() throws Exception { void successGetProductsWithPageable() throws Exception { List<ProductResponse> productResponseList = new ArrayList<>(); //given - var productRequest = new ProductRequest("햄버거()[]+-&/_**", 1000, "이미지 주소", 1L); + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); + var productRequest = new ProductAddRequest("햄버거()[]+-&/_**", 1000, "이미지 주소", 1L, options); for (int i = 0; i < 11; i++) { var product = productService.addProduct(productRequest); productResponseList.add(product); @@ -212,10 +237,13 @@ void failGetProductsWithInvalidPageSort() throws Exception { @DisplayName("상품이 추가되면 옵션이 자동적으로 생성된다.") void successAddDefaultOption() throws Exception { //given + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); var postRequest = post("/api/products") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + memberToken) - .content(objectMapper.writeValueAsString(new ProductRequest("햄버거", 1000, "이미지 주소", 1L))); + .content(objectMapper.writeValueAsString(new ProductAddRequest("햄버거", 1000, "이미지 주소", 1L, options))); //when var result = mockMvc.perform(postRequest); //then @@ -224,9 +252,8 @@ void successAddDefaultOption() throws Exception { var location = createdResult.getResponse().getHeader("Location"); var productId = Long.parseLong(location.replaceAll("/api/products/", "")); - var optionResponses = optionService.getOptions(productId, Pageable.unpaged()); + var optionResponses = optionService.getOptions(productId); Assertions.assertThat(optionResponses.size()).isEqualTo(1); - Assertions.assertThat(optionResponses.get(0).name()).isEqualTo("기본"); productService.deleteProduct(productId); } diff --git a/src/test/java/gift/service/CategoryServiceTest.java b/src/test/java/gift/service/CategoryServiceTest.java index 739e5caf4..024655a10 100644 --- a/src/test/java/gift/service/CategoryServiceTest.java +++ b/src/test/java/gift/service/CategoryServiceTest.java @@ -59,7 +59,7 @@ void successGetCategory() { @DisplayName("전체 카테고리 조회하기") void successGatCategories() { //given, when - var categories = categoryService.getCategories(pageable); + var categories = categoryService.getCategories(); //then Assertions.assertThat(categories.size()).isEqualTo(4); } diff --git a/src/test/java/gift/service/OptionServiceTest.java b/src/test/java/gift/service/OptionServiceTest.java index 4c576557d..bf28b1860 100644 --- a/src/test/java/gift/service/OptionServiceTest.java +++ b/src/test/java/gift/service/OptionServiceTest.java @@ -1,8 +1,7 @@ package gift.service; import gift.dto.giftorder.GiftOrderRequest; -import gift.dto.option.OptionAddRequest; -import gift.dto.option.OptionUpdateRequest; +import gift.dto.option.OptionRequest; import gift.exception.BadRequestException; import gift.exception.DuplicatedNameException; import org.assertj.core.api.Assertions; @@ -10,8 +9,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; import java.util.concurrent.CountDownLatch; @@ -22,7 +19,6 @@ @Transactional class OptionServiceTest { - private final Pageable pageable = PageRequest.of(0, 10); @Autowired private OptionService optionService; @@ -30,75 +26,75 @@ class OptionServiceTest { @DisplayName("정상 옵션 추가하기") void successAddOption() { //given - var optionRequest = new OptionAddRequest("옵션1", 1000, 1L); + var optionRequest = new OptionRequest("옵션1", 1000); //when - var savedOption = optionService.addOption(optionRequest); + var savedOption = optionService.addOption(1L, optionRequest); //then - var options = optionService.getOptions(1L, pageable); + var options = optionService.getOptions(1L); Assertions.assertThat(options.size()).isEqualTo(1); - optionService.deleteOption(savedOption.id()); + optionService.deleteOption(1L, savedOption.id()); } @Test @DisplayName("둘 이상의 옵션 추가하기") void successAddOptions() { //given - var optionRequest1 = new OptionAddRequest("옵션1", 1000, 1L); - var optionRequest2 = new OptionAddRequest("옵션2", 1000, 1L); + var optionRequest1 = new OptionRequest("옵션1", 1000); + var optionRequest2 = new OptionRequest("옵션2", 1000); //when - var savedOption1 = optionService.addOption(optionRequest1); - var savedOption2 = optionService.addOption(optionRequest2); + var savedOption1 = optionService.addOption(1L, optionRequest1); + var savedOption2 = optionService.addOption(1L, optionRequest2); //then - var options = optionService.getOptions(1L, pageable); + var options = optionService.getOptions(1L); Assertions.assertThat(options.size()).isEqualTo(2); - optionService.deleteOption(savedOption1.id()); - optionService.deleteOption(savedOption2.id()); + optionService.deleteOption(1L, savedOption1.id()); + optionService.deleteOption(1L, savedOption2.id()); } @Test @DisplayName("중복된 이름으로 된 상품 옵션 추가시 예외가 발생한다.") void failAddOptionWithDuplicatedName() { //given - var optionRequest = new OptionAddRequest("옵션1", 1000, 1L); - var savedOption = optionService.addOption(optionRequest); + var optionRequest = new OptionRequest("옵션1", 1000); + var savedOption = optionService.addOption(1L, optionRequest); //when, then - Assertions.assertThatThrownBy(() -> optionService.addOption(optionRequest)).isInstanceOf(DuplicatedNameException.class); + Assertions.assertThatThrownBy(() -> optionService.addOption(1L, optionRequest)).isInstanceOf(DuplicatedNameException.class); - optionService.deleteOption(savedOption.id()); + optionService.deleteOption(1L, savedOption.id()); } @Test @DisplayName("옵션 수정하기") void successUpdateOption() { //given - var optionRequest = new OptionAddRequest("옵션1", 1000, 1L); - var savedOption = optionService.addOption(optionRequest); - var optionUpdateDto = new OptionUpdateRequest("수정된 옵션", 12345); + var optionRequest = new OptionRequest("옵션1", 1000); + var savedOption = optionService.addOption(1L, optionRequest); + var optionUpdateDto = new OptionRequest("수정된 옵션", 12345); //when - optionService.updateOption(savedOption.id(), optionUpdateDto); + optionService.updateOption(1L, savedOption.id(), optionUpdateDto); //then - var options = optionService.getOptions(1L, pageable); + var options = optionService.getOptions(1L); var filteredOptions = options.stream().filter(productOptionResponse -> productOptionResponse.id().equals(savedOption.id())).toList(); Assertions.assertThat(filteredOptions.size()).isEqualTo(1); Assertions.assertThat(filteredOptions.get(0).name()).isEqualTo("수정된 옵션"); Assertions.assertThat(filteredOptions.get(0).quantity()).isEqualTo(12345); - optionService.deleteOption(savedOption.id()); + optionService.deleteOption(1L, savedOption.id()); } @Test @DisplayName("옵션의 잔여수량이 0인 경우에 차감요청이 들어오면 예외를 발생시킨다.") void failAddOptionWithZeroQuantity() { //given - var optionRequest = new OptionAddRequest("옵션1", 0, 1L); - var savedOption = optionService.addOption(optionRequest); + var optionRequest = new OptionRequest("옵션1", 0); + var savedOption = optionService.addOption(1L, optionRequest); var orderRequest = new GiftOrderRequest(savedOption.id(), 1, "hello"); //when, then Assertions.assertThatThrownBy(() -> optionService.orderOption(savedOption.id(), orderRequest)).isInstanceOf(BadRequestException.class); - optionService.deleteOption(savedOption.id()); + optionService.deleteOption(1L, savedOption.id()); } @Test @@ -121,7 +117,7 @@ public void concurrencyTest() throws InterruptedException { } countDownLatch.await(); //then - var option = optionService.getOptions(3L, Pageable.unpaged()).stream() + var option = optionService.getOptions(3L).stream() .filter((op) -> op.id().equals(1L)) .findFirst() .get(); diff --git a/src/test/java/gift/service/ProductServiceTest.java b/src/test/java/gift/service/ProductServiceTest.java index 71c54b7e5..486e85efb 100644 --- a/src/test/java/gift/service/ProductServiceTest.java +++ b/src/test/java/gift/service/ProductServiceTest.java @@ -1,6 +1,8 @@ package gift.service; -import gift.dto.product.ProductRequest; +import gift.dto.option.OptionRequest; +import gift.dto.product.ProductAddRequest; +import gift.dto.product.ProductUpdateRequest; import gift.exception.InvalidProductNameWithKAKAOException; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; @@ -9,6 +11,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; + @SpringBootTest @Transactional class ProductServiceTest { @@ -20,7 +24,10 @@ class ProductServiceTest { @DisplayName("정상 상품 추가하기") void successAddProduct() { //given - var productRequest = new ProductRequest("상품1", 10000, "이미지 주소", 1L); + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); + var productRequest = new ProductAddRequest("상품1", 10000, "이미지 주소", 1L, options); //when var savedProduct = productService.addProduct(productRequest); //then @@ -33,7 +40,10 @@ void successAddProduct() { @DisplayName("이용자로 카카오가 포함된 상품 추가하기") void failAddProductWithNameKakao() { //given - var productRequest = new ProductRequest("카카오상품", 10000, "이미지 주소", 1L); + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); + var productRequest = new ProductAddRequest("카카오상품", 10000, "이미지 주소", 1L, options); //when, then Assertions.assertThatThrownBy(() -> productService.addProduct(productRequest)) .isInstanceOf(InvalidProductNameWithKAKAOException.class); @@ -43,10 +53,13 @@ void failAddProductWithNameKakao() { @DisplayName("상품 수정하기") void successUpdateProduct() { //given - var productRequest = new ProductRequest("상품1", 10000, "이미지 주소", 1L); + var optionRequest = new OptionRequest("옵션", 1000); + var options = new ArrayList<OptionRequest>(); + options.add(optionRequest); + var productRequest = new ProductAddRequest("상품1", 10000, "이미지 주소", 1L, options); var savedProduct = productService.addProduct(productRequest); var id = savedProduct.id(); - var updateDto = new ProductRequest("상품1", 7000, "이미지 주소2", 1L); + var updateDto = new ProductUpdateRequest("상품1", 7000, "이미지 주소2", 1L); //when productService.updateProduct(id, updateDto); //then