From 7a2f1cf620265c9e0472b55c45ef96c497e7ba4e Mon Sep 17 00:00:00 2001 From: harper <35358294+yooyouny@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:38:47 +0900 Subject: [PATCH] =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=A1=B0=ED=9A=8C=20API?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(#59): 단일상품조회 API 구현 * feat(#59): 주문시 상품정보를 가져오는 내부 API 정의 - 요청에서 삭제된 상품정보는 아예 넘어오지 않아서 없는 경우 not found exception * feat(#59): 상품엔티티에 필터링과 관련된 컬럼 추가 - 상품 조회시 필요한 필터링 요소로 브랜드이름, 메인컬러, 사이즈, 태그 추가 및 쿠폰여부 제거 - 태그로 "COUPON", "EXPRESS_DELIVERY", "FREE_SHIPPING", "DISCOUNT", "NEW", "1+1", "RECOMMEND"가 추가될 수 있음 - 상품 조회시 필요한 정렬 요소로 리뷰카운트, 세일즈카운트 속성 추가 나중에 리뷰추가나 주문시 해당 count 증가가 필요 - 엔티티 속성추가로 변동되는 모든 dto, mapper 요소를 함께 수정 --- .../com/sparta/product_dto/ProductDto.java | 39 +++++++++++++ .../product_dto/ProductReadRequest.java | 5 ++ .../product/application/ProductMapper.java | 25 +++++++- .../product/application/ProductService.java | 14 +++++ .../sparta/product/domain/model/Product.java | 42 +++++++++----- .../elasticsearch/dto/ProductSearchDto.java | 20 +++++-- .../controller/ProductController.java | 9 +++ .../controller/ProductInternalController.java | 23 ++++++++ .../request/ProductCreateRequest.java | 6 +- .../request/ProductUpdateRequest.java | 6 +- .../response/ProductResponse.java | 57 +++++++++++++------ .../ProductServerApplicationTests.java | 3 +- 12 files changed, 205 insertions(+), 44 deletions(-) create mode 100644 service/product/product_dto/src/main/java/com/sparta/product_dto/ProductDto.java create mode 100644 service/product/product_dto/src/main/java/com/sparta/product_dto/ProductReadRequest.java create mode 100644 service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductInternalController.java diff --git a/service/product/product_dto/src/main/java/com/sparta/product_dto/ProductDto.java b/service/product/product_dto/src/main/java/com/sparta/product_dto/ProductDto.java new file mode 100644 index 00000000..8abbff25 --- /dev/null +++ b/service/product/product_dto/src/main/java/com/sparta/product_dto/ProductDto.java @@ -0,0 +1,39 @@ +package com.sparta.product_dto; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ProductDto { + private UUID productId; + private String productName; + private BigDecimal originalPrice; + private BigDecimal discountedPrice; + private Double discountPercent; + private int stock; + private List tags; + + @Builder + private ProductDto( + UUID productId, + String productName, + BigDecimal originalPrice, + BigDecimal discountedPrice, + Double discountPercent, + int stock, + List tags, + boolean isCoupon) { + this.productId = productId; + this.productName = productName; + this.originalPrice = originalPrice; + this.discountedPrice = discountedPrice; + this.discountPercent = discountPercent; + this.stock = stock; + this.tags = tags; + } +} diff --git a/service/product/product_dto/src/main/java/com/sparta/product_dto/ProductReadRequest.java b/service/product/product_dto/src/main/java/com/sparta/product_dto/ProductReadRequest.java new file mode 100644 index 00000000..f3b2ec36 --- /dev/null +++ b/service/product/product_dto/src/main/java/com/sparta/product_dto/ProductReadRequest.java @@ -0,0 +1,5 @@ +package com.sparta.product_dto; + +import java.util.List; + +public record ProductReadRequest(List productIds) {} diff --git a/service/product/product_server/src/main/java/com/sparta/product/application/ProductMapper.java b/service/product/product_server/src/main/java/com/sparta/product/application/ProductMapper.java index 4190d321..2d4d80c0 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/application/ProductMapper.java +++ b/service/product/product_server/src/main/java/com/sparta/product/application/ProductMapper.java @@ -3,12 +3,28 @@ import com.sparta.product.domain.model.Product; import com.sparta.product.presentation.request.ProductCreateRequest; import com.sparta.product.presentation.request.ProductUpdateRequest; +import com.sparta.product_dto.ProductDto; public class ProductMapper { + + public static ProductDto fromEntity(Product product) { + return ProductDto.builder() + .productId(product.getProductId()) + .productName(product.getProductName()) + .discountPercent(product.getDiscountPercent()) + .discountedPrice(product.getDiscountedPrice()) + .stock(product.getStock()) + .tags(product.getTags()) + .build(); + } + public static Product toEntity(ProductCreateRequest request) { return Product.builder() .categoryId(request.categoryId()) .productName(request.productName()) + .brandName(request.brandName()) + .mainColor(request.mainColor()) + .size(request.size()) .description(request.description()) .originalPrice(request.originalPrice()) .discountPercent(request.discountPercent()) @@ -16,7 +32,7 @@ public static Product toEntity(ProductCreateRequest request) { .detailImgUrl(request.detailImgUrl()) .stock(request.stock()) .limitCountPerUser(request.limitCountPerUser()) - .isCoupon(request.isCoupon()) + .tags(request.tags()) .build(); } @@ -24,6 +40,9 @@ public static void updateProduct(ProductUpdateRequest request, Product existingP existingProduct.updateProduct( request.categoryId(), request.productName(), + request.brandName(), + request.mainColor(), + request.size(), request.originalPrice(), request.discountPercent(), request.stock(), @@ -31,7 +50,7 @@ public static void updateProduct(ProductUpdateRequest request, Product existingP request.thumbnailImgUrl(), request.detailImgUrl(), request.limitCountPerUser(), - request.isPublic(), - request.isCoupon()); + request.tags(), + request.isPublic()); } } diff --git a/service/product/product_server/src/main/java/com/sparta/product/application/ProductService.java b/service/product/product_server/src/main/java/com/sparta/product/application/ProductService.java index 1d7d055b..5eae5870 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/application/ProductService.java +++ b/service/product/product_server/src/main/java/com/sparta/product/application/ProductService.java @@ -7,7 +7,10 @@ import com.sparta.product.presentation.request.ProductCreateRequest; import com.sparta.product.presentation.request.ProductUpdateRequest; import com.sparta.product.presentation.response.ProductResponse; +import com.sparta.product_dto.ProductDto; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -49,6 +52,17 @@ public ProductResponse deleteProduct(UUID productId) { return ProductResponse.fromEntity(product); } + public ProductResponse getProduct(UUID productId) { + return ProductResponse.fromEntity(getSavedProduct(productId)); + } + + public List getProductList(List productIds) { + return productIds.stream() + .map(productId -> getSavedProduct(UUID.fromString(productId))) + .map(ProductMapper::fromEntity) + .collect(Collectors.toList()); + } + private Product getSavedProduct(UUID productId) { return productRepository .findByProductIdAndIsDeletedFalse(productId) diff --git a/service/product/product_server/src/main/java/com/sparta/product/domain/model/Product.java b/service/product/product_server/src/main/java/com/sparta/product/domain/model/Product.java index 5c47f53a..83d88545 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/domain/model/Product.java +++ b/service/product/product_server/src/main/java/com/sparta/product/domain/model/Product.java @@ -2,6 +2,7 @@ import com.sparta.common.domain.entity.BaseEntity; import java.math.BigDecimal; +import java.util.List; import java.util.UUID; import lombok.Builder; import lombok.Getter; @@ -15,31 +16,30 @@ @Getter public class Product extends BaseEntity implements Persistable { @PrimaryKey private UUID productId = UUID.randomUUID(); - @Column public Long categoryId; - @Column public String productName; + @Column public String brandName; + @Column public String mainColor; + @Column public String size; + @Column public String description; @Column public BigDecimal originalPrice; - @Column public BigDecimal discountedPrice; - @Column public Double discountPercent; - @Column public int stock; - @Column public String description; - @Column public String thumbnailImgUrl; - @Column public String detailImgUrl; @Column public int limitCountPerUser = 0; - @Column public double averageRating = 0.0; // TODO :: 리뷰가 등록될떄마다 평균평점 계산 + @Column public double averageRating = 0.0; + @Column public long reviewCount = 0; + @Column public long salesCount = 0; + @Column public boolean isPublic = true; @Column public boolean soldout = false; @Column public boolean isDeleted = false; - @Column public boolean isCoupon; + @Column public List tags; @Transient private boolean isNew = false; @Override @@ -57,6 +57,9 @@ public boolean isNew() { private Product( Long categoryId, String productName, + String brandName, + String mainColor, + String size, BigDecimal originalPrice, Double discountPercent, int stock, @@ -64,9 +67,12 @@ private Product( String thumbnailImgUrl, String detailImgUrl, int limitCountPerUser, - boolean isCoupon) { + List tags) { this.categoryId = categoryId; this.productName = productName; + this.brandName = brandName; + this.mainColor = mainColor; + this.size = size; this.originalPrice = originalPrice; this.discountPercent = discountPercent; applyDiscount(discountPercent); @@ -75,12 +81,15 @@ private Product( this.thumbnailImgUrl = thumbnailImgUrl; this.detailImgUrl = detailImgUrl; this.limitCountPerUser = limitCountPerUser; - this.isCoupon = isCoupon; + this.tags = tags; } public void updateProduct( Long categoryId, String productName, + String brandName, + String mainColor, + String size, BigDecimal originalPrice, Double discountPercent, Integer stock, @@ -88,10 +97,13 @@ public void updateProduct( String thumbnailImgUrl, String detailImgUrl, Integer limitCountPerUser, - boolean isPublic, - boolean isCoupon) { + List tags, + boolean isPublic) { this.categoryId = categoryId; this.productName = productName; + this.brandName = brandName; + this.mainColor = mainColor; + this.size = size; this.originalPrice = originalPrice; this.discountPercent = discountPercent; applyDiscount(discountPercent); @@ -99,9 +111,9 @@ public void updateProduct( this.description = description; this.thumbnailImgUrl = thumbnailImgUrl; this.detailImgUrl = detailImgUrl; + this.tags = tags; this.limitCountPerUser = limitCountPerUser; this.isPublic = isPublic; - this.isCoupon = isCoupon; } public UUID getProductId() { diff --git a/service/product/product_server/src/main/java/com/sparta/product/infrastructure/elasticsearch/dto/ProductSearchDto.java b/service/product/product_server/src/main/java/com/sparta/product/infrastructure/elasticsearch/dto/ProductSearchDto.java index 6b538097..e7688393 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/infrastructure/elasticsearch/dto/ProductSearchDto.java +++ b/service/product/product_server/src/main/java/com/sparta/product/infrastructure/elasticsearch/dto/ProductSearchDto.java @@ -2,6 +2,7 @@ import com.sparta.product.presentation.response.ProductResponse; import java.math.BigDecimal; +import java.util.List; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -15,6 +16,9 @@ public class ProductSearchDto { @Id private String productId; private Long categoryId; private String productName; + private String brandName; + private String mainColor; + private String size; private BigDecimal originalPrice; private BigDecimal discountedPrice; private Double discountPercent; @@ -24,14 +28,17 @@ public class ProductSearchDto { private double averageRating; private boolean isPublic; private boolean soldout; + private List tags; private boolean isDeleted; - private boolean isCoupon; @Builder private ProductSearchDto( String productId, Long categoryId, String productName, + String brandName, + String mainColor, + String size, BigDecimal originalPrice, BigDecimal discountedPrice, Double discountPercent, @@ -42,10 +49,13 @@ private ProductSearchDto( boolean isPublic, boolean soldout, boolean isDeleted, - boolean isCoupon) { + List tags) { this.productId = productId; this.categoryId = categoryId; this.productName = productName; + this.brandName = brandName; + this.mainColor = mainColor; + this.size = size; this.originalPrice = originalPrice; this.discountedPrice = discountedPrice; this.discountPercent = discountPercent; @@ -56,7 +66,7 @@ private ProductSearchDto( this.isPublic = isPublic; this.soldout = soldout; this.isDeleted = isDeleted; - this.isCoupon = isCoupon; + this.tags = tags; } public static ProductSearchDto toDto(ProductResponse product) { @@ -64,6 +74,9 @@ public static ProductSearchDto toDto(ProductResponse product) { .productId(product.getProductId()) .categoryId(product.getCategoryId()) .productName(product.getProductName()) + .brandName(product.getBrandName()) + .mainColor(product.getMainColor()) + .size(product.getSize()) .originalPrice(product.getOriginalPrice()) .discountedPrice(product.getDiscountedPrice()) .discountPercent(product.getDiscountPercent()) @@ -73,7 +86,6 @@ public static ProductSearchDto toDto(ProductResponse product) { .averageRating(product.getAverageRating()) .isPublic(product.isPublic()) .isDeleted(product.isDeleted()) - .isCoupon(product.isCoupon()) .soldout(product.isSoldout()) .build(); } diff --git a/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductController.java b/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductController.java index bad7e767..1a8db6f9 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductController.java +++ b/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductController.java @@ -2,6 +2,7 @@ import com.sparta.common.domain.response.ApiResponse; import com.sparta.product.application.ProductFacadeService; +import com.sparta.product.application.ProductService; import com.sparta.product.presentation.request.ProductCreateRequest; import com.sparta.product.presentation.request.ProductUpdateRequest; import com.sparta.product.presentation.response.ProductResponse; @@ -10,6 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -24,6 +26,7 @@ @Validated public class ProductController { private final ProductFacadeService facadeService; + private final ProductService productService; @PostMapping public ApiResponse createProduct(@RequestBody ProductCreateRequest request) { @@ -45,4 +48,10 @@ public ApiResponse updateStatus( public ApiResponse deleteProduct(@PathVariable("productId") @NotNull UUID productId) { return ApiResponse.ok(facadeService.deleteProduct(productId)); } + + @GetMapping("/{productId}") + public ApiResponse getProduct( + @PathVariable("productId") @NotNull UUID productId) { + return ApiResponse.ok(productService.getProduct(productId)); + } } diff --git a/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductInternalController.java b/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductInternalController.java new file mode 100644 index 00000000..9e099060 --- /dev/null +++ b/service/product/product_server/src/main/java/com/sparta/product/presentation/controller/ProductInternalController.java @@ -0,0 +1,23 @@ +package com.sparta.product.presentation.controller; + +import com.sparta.product.application.ProductService; +import com.sparta.product_dto.ProductDto; +import com.sparta.product_dto.ProductReadRequest; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/internal/products") +public class ProductInternalController { + private final ProductService productService; + + @GetMapping + public List getProductList(@RequestBody ProductReadRequest request) { + return productService.getProductList(request.productIds()); + } +} diff --git a/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductCreateRequest.java b/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductCreateRequest.java index ad97f41a..10e38a9e 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductCreateRequest.java +++ b/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductCreateRequest.java @@ -3,10 +3,14 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.math.BigDecimal; +import java.util.List; public record ProductCreateRequest( @NotNull(message = "카테고리아이디는 필수입니다") Long categoryId, @NotBlank(message = "상품이름은 필수입니다") String productName, + @NotBlank(message = "브랜드이름은 필수입니다") String brandName, + @NotBlank(message = "메인컬러는 필수입니다") String mainColor, + @NotBlank(message = "상품사이즈는 필수입니다") String size, @NotNull(message = "상품가격은 필수입니다") BigDecimal originalPrice, Double discountPercent, @NotNull(message = "상품재고수량은 필수입니다") Integer stock, @@ -14,4 +18,4 @@ public record ProductCreateRequest( @NotBlank(message = "썸네일이미지주소는 필수입니다") String thumbnailImgUrl, @NotBlank(message = "상세이미지주소는 필수입니다") String detailImgUrl, @NotNull(message = "상품이름은 필수입니다") Integer limitCountPerUser, - @NotNull(message = "쿠폰적용여부는 필수입니다") Boolean isCoupon) {} + List tags) {} diff --git a/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductUpdateRequest.java b/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductUpdateRequest.java index 637340b7..6460c162 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductUpdateRequest.java +++ b/service/product/product_server/src/main/java/com/sparta/product/presentation/request/ProductUpdateRequest.java @@ -3,12 +3,16 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.math.BigDecimal; +import java.util.List; import java.util.UUID; public record ProductUpdateRequest( @NotNull(message = "상품아이디는 필수입니다") UUID productId, @NotNull(message = "카테고리아이디는 필수입니다") Long categoryId, @NotBlank(message = "상품이름은 필수입니다") String productName, + @NotBlank(message = "브랜드이름은 필수입니다") String brandName, + @NotBlank(message = "메인컬러는 필수입니다") String mainColor, + @NotBlank(message = "상품사이즈는 필수입니다") String size, @NotNull(message = "상품가격은 필수입니다") BigDecimal originalPrice, Double discountPercent, @NotNull(message = "상품재고수량은 필수입니다") Integer stock, @@ -17,4 +21,4 @@ public record ProductUpdateRequest( @NotBlank(message = "상세이미지주소는 필수입니다") String detailImgUrl, @NotNull(message = "상품이름은 필수입니다") Integer limitCountPerUser, @NotNull(message = "공개여부는 필수입니다") Boolean isPublic, - @NotNull(message = "쿠폰적용여부는 필수입니다") Boolean isCoupon) {} + List tags) {} diff --git a/service/product/product_server/src/main/java/com/sparta/product/presentation/response/ProductResponse.java b/service/product/product_server/src/main/java/com/sparta/product/presentation/response/ProductResponse.java index 743b9db3..66e1cbb0 100644 --- a/service/product/product_server/src/main/java/com/sparta/product/presentation/response/ProductResponse.java +++ b/service/product/product_server/src/main/java/com/sparta/product/presentation/response/ProductResponse.java @@ -2,6 +2,7 @@ import com.sparta.product.domain.model.Product; import java.math.BigDecimal; +import java.util.List; import java.util.UUID; import lombok.Builder; import lombok.Getter; @@ -12,6 +13,9 @@ public class ProductResponse { private String productId; private Long categoryId; + private String brandName; + private String mainColor; + private String size; private String productName; private BigDecimal originalPrice; private BigDecimal discountedPrice; @@ -22,16 +26,21 @@ public class ProductResponse { private String detailImgUrl; private int limitCountPerUser; private double averageRating; + private long reviewCount; + private long salesCount; private boolean isPublic; private boolean soldout; private boolean isDeleted; - private boolean isCoupon; + private List tags; @Builder private ProductResponse( UUID productId, Long categoryId, String productName, + String brandName, + String mainColor, + String size, BigDecimal originalPrice, BigDecimal discountedPrice, Double discountPercent, @@ -41,13 +50,18 @@ private ProductResponse( String detailImgUrl, int limitCountPerUser, double averageRating, + long reviewCount, + long salesCount, boolean isPublic, boolean soldout, boolean isDeleted, - boolean isCoupon) { + List tags) { this.productId = productId.toString(); this.categoryId = categoryId; this.productName = productName; + this.brandName = brandName; + this.mainColor = mainColor; + this.size = size; this.originalPrice = originalPrice; this.discountedPrice = discountedPrice; this.discountPercent = discountPercent; @@ -57,30 +71,37 @@ private ProductResponse( this.detailImgUrl = detailImgUrl; this.limitCountPerUser = limitCountPerUser; this.averageRating = averageRating; + this.reviewCount = reviewCount; + this.salesCount = salesCount; this.isPublic = isPublic; this.soldout = soldout; this.isDeleted = isDeleted; - this.isCoupon = isCoupon; + this.tags = tags; } public static ProductResponse fromEntity(Product product) { return ProductResponse.builder() .productId(product.getProductId()) - .categoryId(product.categoryId) - .productName(product.productName) - .description(product.description) - .thumbnailImgUrl(product.thumbnailImgUrl) - .detailImgUrl(product.detailImgUrl) - .originalPrice(product.originalPrice) - .discountPercent(product.discountPercent) - .discountedPrice(product.discountedPrice) - .stock(product.stock) - .limitCountPerUser(product.limitCountPerUser) - .averageRating(product.averageRating) - .isCoupon(product.isCoupon) - .isDeleted(product.isDeleted) - .isPublic(product.isPublic) - .soldout(product.soldout) + .categoryId(product.getCategoryId()) + .productName(product.getProductName()) + .brandName(product.getBrandName()) + .mainColor(product.getMainColor()) + .size(product.getSize()) + .description(product.getDescription()) + .thumbnailImgUrl(product.getThumbnailImgUrl()) + .detailImgUrl(product.getDetailImgUrl()) + .originalPrice(product.getOriginalPrice()) + .discountPercent(product.getDiscountPercent()) + .discountedPrice(product.getDiscountedPrice()) + .stock(product.getStock()) + .limitCountPerUser(product.getLimitCountPerUser()) + .reviewCount(product.getReviewCount()) + .salesCount(product.getSalesCount()) + .averageRating(product.getAverageRating()) + .isDeleted(product.isDeleted()) + .isPublic(product.isPublic()) + .soldout(product.isSoldout()) + .tags(product.getTags()) .build(); } } diff --git a/service/product/product_server/src/test/java/com/sparta/product/ProductServerApplicationTests.java b/service/product/product_server/src/test/java/com/sparta/product/ProductServerApplicationTests.java index 7bdb8c00..4262b0e3 100644 --- a/service/product/product_server/src/test/java/com/sparta/product/ProductServerApplicationTests.java +++ b/service/product/product_server/src/test/java/com/sparta/product/ProductServerApplicationTests.java @@ -1,9 +1,8 @@ package com.sparta.product; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest +//@SpringBootTest class ProductServerApplicationTests { @Test