Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/kariskan step3 #13

Merged
merged 45 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4521acd
feat: Review entity 작성
kariskan May 16, 2024
6531d84
feat: 상품에 대한 주문 주량, 평균 평점 column 추가
kariskan May 21, 2024
d9235d3
feat: 리뷰 추가 로직
kariskan May 21, 2024
d1146c9
feat: 리뷰 객체 관리하는 factory 클래스 작성
kariskan May 21, 2024
9cee426
fix: 상품 구매 확정 시 상품에 대한 구매 횟수 증가
kariskan May 21, 2024
7a4bc18
feat: 중복 리뷰 에러 코드 추가
kariskan May 21, 2024
417d558
feat: ReviewController 요청에 인터셉터 거치도록 수정
kariskan May 21, 2024
5f1e396
feat: ReviewController 작성
kariskan May 21, 2024
b9ba65f
feat: 상품 검색 기준 SortType 작성
kariskan May 21, 2024
720f044
feat: 상품 검색 Controller 작성
kariskan May 21, 2024
fcc83f1
feat: 검색 조건에 따른 쿼리 작성
kariskan May 21, 2024
55c8f38
feat: @ModelAttribute 검증기 작성
kariskan May 21, 2024
ab83678
fix: column definition 변경
kariskan May 21, 2024
ca6f424
fix: ddl-auto 옵션 변경
kariskan May 21, 2024
100aff7
test: ProductController test 추가
kariskan May 21, 2024
9dce566
test: ReviewController test 추가
kariskan May 21, 2024
eafc4f7
test: ProductReadService test 추가
kariskan May 21, 2024
ce558c8
test: ReviewService test 추가
kariskan May 21, 2024
58e11b7
style: reformat code
kariskan May 21, 2024
03f2f88
refactor: code smell 제거
kariskan May 21, 2024
ba453e6
test: entity 테스트 추가
kariskan May 21, 2024
dd0100d
refactor: @Min, @Max를 @Range로 변경
kariskan May 21, 2024
3bbbc72
refactor: 변수명 간소화
kariskan May 21, 2024
e110bca
refactor: 불필요한 import 제거
kariskan May 21, 2024
22ba3bf
refactor: magic number 제거
kariskan May 21, 2024
ae58853
fix: 해당 상품에 구매 이력이 존재하는 경우에만 리뷰 작성하도록 수정
kariskan May 21, 2024
4361cd9
test: ReviewServiceTest 추가
kariskan May 21, 2024
71fba2f
style: 주석 추가
kariskan May 21, 2024
415e5de
test: 상품 검색 테스트 수정
kariskan May 21, 2024
01d0876
test: @SpringBootTest 제거
kariskan May 21, 2024
edb548b
test: @SpringBootTest 제거
kariskan May 21, 2024
0a83f2b
refactor: 형식 일치, asc 제거
kariskan May 23, 2024
53064d6
refactor: requireNonNullElse 활용
kariskan May 23, 2024
b55169e
chore: ddl-auto 옵션 변경
kariskan May 23, 2024
83ffe38
fix: count 쿼리가 아닌 exists 쿼리로 변경
kariskan May 23, 2024
797fce9
refactor: factory class 의존성 삭제하고 static 메서드로 변경
kariskan May 23, 2024
ae37561
fix: http status code 변경
kariskan May 23, 2024
1d88ac1
fix: avgScore 고정 소수점 계산으로 변경
kariskan May 24, 2024
19ab651
feat: 동적 쿼리 MyBatis 도입
kariskan May 24, 2024
040c2b2
bug: 잘못된 쿼리 수정
kariskan May 24, 2024
5925e88
feat: ReviewFactory private 생성자 추가
kariskan May 24, 2024
6f3bb69
feat: OrderProduct Scheduler 추가
kariskan May 24, 2024
7116384
style: code reformat
kariskan May 24, 2024
1de95b3
chore: build.gradle 수정
kariskan May 24, 2024
2b9b59e
fix: error message 수정
kariskan May 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,18 @@ public void confirmOrder(Long orderId, Consumer consumer) {
order.updateOrderStatus(CONFIRM);
List<OrderProduct> orderProducts = orderProductReadService.findByOrderJoinFetchProductAndSeller(orderId);
addSellerBalance(orderProducts);
addOrderCount(orderProducts);

savePointLog(consumer, order, true);
}

/**
* 주문 시 product에 대한 구매 횟수를 증가함
*/
private void addOrderCount(List<OrderProduct> orderProducts) {
orderProducts.forEach(orderProduct -> orderProduct.getProduct().increaseOrderCount());
}

private void saveOrderInfo(PurchaseProductRequest request, Consumer consumer, Order order,
List<OrderProduct> orderProducts, long totalAmount) {
orderProductJdbcRepository.saveAllBatch(orderProducts);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private void validateRequest(UpdateDeliveryStatusRequest request, Delivery deliv
*/
private boolean isInvalidChangeStatus(DeliveryStatus future, DeliveryStatus current) {
return future.isPending()
|| (future.isDelivering() && !current.isPending())
|| (future.isDelivered() && !current.isDelivering());
|| (future.isDelivering() && !current.isPending())
|| (future.isDelivered() && !current.isDelivering());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,15 @@ public interface OrderProductRepository extends JpaRepository<OrderProduct, Long

@Query("select op from OrderProduct op join fetch op.product join fetch op.product.seller where op.order.id = :id")
List<OrderProduct> findByOrderJoinFetchProductAndSeller(@Param("id") Long orderId);

@Query(value = "select order_id from order_product_tbl op where op.product_id = :product_id", nativeQuery = true)
List<Long> findOrderIdByProductId(@Param("product_id") Long id);

@Query(value = """
select count(*)
from order_tbl o
where consumer_id = :consumer_id
and order_id in (:ids)
""", nativeQuery = true)
Long countByIdsAndConsumerId(@Param("ids") List<Long> ids, @Param("consumer_id") Long consumerId);
Copy link

@kochungcheon kochungcheon May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다 계산 하지 말고 조건에 만족하는 게 1개 있으면 true 반환하는 건 어떤가요!?

OrderProductReadService 의

@Transactional(readOnly = true)
	public boolean existsByConsumerIdAndProductId(Long consumerId, Long productId) {
		List<Long> orderIds = orderProductRepository.findOrderIdByProductId(productId);
		return orderProductRepository.countByIdsAndConsumerId(orderIds, consumerId) > 0;
	}

보면 결국 있는 지 여부만 찾는 거 같아서요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exists로 수정했씁니다!

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ public List<OrderProduct> findByOrderJoinFetchProduct(Long orderId) {
public List<OrderProduct> findByOrderJoinFetchProductAndSeller(Long orderId) {
return orderProductRepository.findByOrderJoinFetchProductAndSeller(orderId);
}

/**
* consumer id와 product id로 구매 이력 조회
*/
@Transactional(readOnly = true)
public boolean existsByConsumerIdAndProductId(Long consumerId, Long productId) {
List<Long> orderIds = orderProductRepository.findOrderIdByProductId(productId);
return orderProductRepository.countByIdsAndConsumerId(orderIds, consumerId) > 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그냥 exist query를 날릴 순 없을까요?

select 1
from ~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇네요 굳이 셀 필요가 없네요. exists query로 수정했습니다.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.c4marathon.assignment.domain.product.controller;

import org.c4marathon.assignment.domain.product.dto.request.ProductSearchRequest;
import org.c4marathon.assignment.domain.product.dto.response.ProductSearchResponse;
import org.c4marathon.assignment.domain.product.service.ProductReadService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/products")
public class ProductController {

private final ProductReadService productReadService;

@GetMapping
public ProductSearchResponse searchProduct(
@Valid @ModelAttribute ProductSearchRequest request
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가능하면 @ModelAttribute 보다는 @RequestBody 를 사용하면 좋을 것 같아요.
참고: https://tecoble.techcourse.co.kr/post/2021-05-11-requestbody-modelattribute/

Copy link
Author

@kariskan kariskan May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이유가 왜일까요? 참고 문서에서는 @RequestBody는 http body가 java object로 변환되고, @ModelAttribute는 form or query param을 java object로 변환된다고 하는데 GetMapping에서는 body를 통한 데이터 전달을 지양하고, 보통 query string으로 요청을 하는 것으로 알고 있어서요.
차이가 있다면 RequestBody는 기본 생성자 + getter or setter이고 ModelAttribute는 생성자 or setter인 것 같습니다.
그런데 record로 정의하면 생성자, getter, 기타 등등 포함되기 때문에 어떤 형태로 오든 변환이 가능하지 않나요?

ProductSearchRequest.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.c4marathon.assignment.domain.product.dto.request;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.Objects;
import org.c4marathon.assignment.global.constant.SortType;

public record ProductSearchRequest(@NotNull @Size(
    min = 2,
    message = "keyword length less than 2"
) String keyword, @NotNull SortType sortType, LocalDateTime createdAt, Long productId, Long amount, Long orderCount, Double score, int pageSize) {
    public ProductSearchRequest(String keyword, SortType sortType, LocalDateTime createdAt, Long productId, Long amount, Long orderCount, Double score, int pageSize) {
        this.keyword = keyword;
        this.sortType = sortType;
        this.createdAt = (LocalDateTime)Objects.requireNonNullElse(createdAt, LocalDateTime.now());
        this.productId = (Long)Objects.requireNonNullElse(productId, Long.MIN_VALUE);
        this.amount = (Long)Objects.requireNonNullElse(amount, this.getDefaultAmount(sortType));
        this.orderCount = (Long)Objects.requireNonNullElse(orderCount, Long.MAX_VALUE);
        this.score = (Double)Objects.requireNonNullElse(score, Double.MAX_VALUE);
        this.pageSize = pageSize;
    }

    private Long getDefaultAmount(SortType sortType) {
        return sortType == SortType.PRICE_ASC ? Long.MIN_VALUE : Long.MAX_VALUE;
    }

    public static ProductSearchRequestBuilder builder() {
        return new ProductSearchRequestBuilder();
    }

    public @NotNull @Size(
    min = 2,
    message = "keyword length less than 2"
) String keyword() {
        return this.keyword;
    }

    public @NotNull SortType sortType() {
        return this.sortType;
    }

    public LocalDateTime createdAt() {
        return this.createdAt;
    }

    public Long productId() {
        return this.productId;
    }

    public Long amount() {
        return this.amount;
    }

    public Long orderCount() {
        return this.orderCount;
    }

    public Double score() {
        return this.score;
    }

    public int pageSize() {
        return this.pageSize;
    }

    public static class ProductSearchRequestBuilder {
        private String keyword;
        private SortType sortType;
        private LocalDateTime createdAt;
        private Long productId;
        private Long amount;
        private Long orderCount;
        private Double score;
        private int pageSize;

        ProductSearchRequestBuilder() {
        }

        public ProductSearchRequestBuilder keyword(final String keyword) {
            this.keyword = keyword;
            return this;
        }

        public ProductSearchRequestBuilder sortType(final SortType sortType) {
            this.sortType = sortType;
            return this;
        }

        public ProductSearchRequestBuilder createdAt(final LocalDateTime createdAt) {
            this.createdAt = createdAt;
            return this;
        }

        public ProductSearchRequestBuilder productId(final Long productId) {
            this.productId = productId;
            return this;
        }

        public ProductSearchRequestBuilder amount(final Long amount) {
            this.amount = amount;
            return this;
        }

        public ProductSearchRequestBuilder orderCount(final Long orderCount) {
            this.orderCount = orderCount;
            return this;
        }

        public ProductSearchRequestBuilder score(final Double score) {
            this.score = score;
            return this;
        }

        public ProductSearchRequestBuilder pageSize(final int pageSize) {
            this.pageSize = pageSize;
            return this;
        }

        public ProductSearchRequest build() {
            return new ProductSearchRequest(this.keyword, this.sortType, this.createdAt, this.productId, this.amount, this.orderCount, this.score, this.pageSize);
        }

        public String toString() {
            return "ProductSearchRequest.ProductSearchRequestBuilder(keyword=" + this.keyword + ", sortType=" + this.sortType + ", createdAt=" + this.createdAt + ", productId=" + this.productId + ", amount=" + this.amount + ", orderCount=" + this.orderCount + ", score=" + this.score + ", pageSize=" + this.pageSize + ")";
        }
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 Get이었네요 ㅋㅋ
제가 잘못봄

) {
return productReadService.searchProduct(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.c4marathon.assignment.domain.product.dto.request;

import java.time.LocalDateTime;

import org.c4marathon.assignment.global.constant.SortType;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;

public record ProductSearchRequest(
@NotNull @Size(min = 2, message = "keyword length less than 2") String keyword,
@NotNull SortType sortType,
LocalDateTime createdAt,
Long productId,
Long amount,
Long orderCount,
Double score,
int pageSize
) {

@Builder
public ProductSearchRequest(
String keyword,
SortType sortType,
LocalDateTime createdAt,
Long productId,
Long amount,
Long orderCount,
Double score,
int pageSize
) {
this.keyword = keyword;
this.sortType = sortType;
this.createdAt = setDefaultValue(createdAt, LocalDateTime.now());
this.productId = setDefaultValue(productId, Long.MIN_VALUE);
this.amount = setDefaultValue(amount, getDefaultAmount(sortType));
this.orderCount = setDefaultValue(orderCount, Long.MAX_VALUE);
this.score = setDefaultValue(score, Double.MAX_VALUE);
this.pageSize = pageSize;
}

private <T> T setDefaultValue(T value, T defaultValue) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objects.requireNonNullElse(value, defaultValue) 로 대체 가능합니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 메서드가 이런곳에서 쓰는 것이었군요..

return value != null ? value : defaultValue;
}

private Long getDefaultAmount(SortType sortType) {
return sortType == SortType.PRICE_ASC ? Long.MIN_VALUE : Long.MAX_VALUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.c4marathon.assignment.domain.product.dto.response;

import java.time.LocalDateTime;

public record ProductSearchEntry(
long id,
String name,
String description,
long amount,
int stock,
LocalDateTime createdAt,
Long orderCount,
Double avgScore
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.c4marathon.assignment.domain.product.dto.response;

import java.util.List;

import org.c4marathon.assignment.domain.product.entity.Product;

public record ProductSearchResponse(
List<ProductSearchEntry> productSearchEntries
) {
public static ProductSearchResponse of(List<Product> products) {
return new ProductSearchResponse(products.stream()
.map(product -> new ProductSearchEntry(
product.getId(),
product.getName(),
product.getDescription(),
product.getAmount(),
product.getStock(),
product.getCreatedAt(),
product.getOrderCount(),
product.getAvgScore()))
.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
@Table(
name = "product_tbl",
indexes = {
@Index(name = "product_name_seller_id_idx", columnList = "name,seller_id")
@Index(name = "product_name_seller_id_idx", columnList = "name,seller_id"),
@Index(name = "amount_product_id_idx", columnList = "amount, product_id"),
@Index(name = "amount_desc_product_id_idx", columnList = "amount desc, product_id"),
@Index(name = "created_at_product_id_idx", columnList = "created_at desc, product_id"),
@Index(name = "avg_score_desc_product_id_idx", columnList = "avg_score desc, product_id asc"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

형식 일치가 필요합니다. 여기에만 asc가 있네요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

세세한 리뷰 감사합니다.
개인적으로 asc를 적는 것과 안 적는 것 중에 어떤거 선호하시나요?
datagrip에서는 asc 쓰면 채찍질 엄청 하더라구요

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기왕이면 적는게 좋습니다.

@Index(name = "order_count_desc_product_id_idx", columnList = "order_count desc, product_id")
}
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand Down Expand Up @@ -64,6 +69,14 @@ public class Product extends BaseEntity {
@Column(name = "status", columnDefinition = "VARCHAR(20)")
private ProductStatus productStatus;

@NotNull
@Column(name = "avg_score", columnDefinition = "DECIMAL(5, 4) DEFAULT 0.0000")
private Double avgScore;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가능하면 해당 로직에 대해선 이후 성능 테스트를 꼭 진행해 주세요.
여러 유저가 동시에 리뷰를 날렸을 때 발생할 수 있는 이슈가 있을"수도" 있습니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 동시에 날렸을 때 같은 avgScore를 읽고 update 하면 동시성 문제가 발생할 수도 있겠네요.
차후 스텝에서 꼭 진행해 보겠습니다!


@NotNull
@Column(name = "order_count", columnDefinition = "BIGINT DEFAULT 0")
private Long orderCount;

@Builder
public Product(
String name,
Expand All @@ -78,6 +91,8 @@ public Product(
this.stock = stock;
this.seller = seller;
this.productStatus = IN_STOCK;
this.orderCount = 0L;
this.avgScore = (double)0;
}

public void decreaseStock(Integer quantity) {
Expand All @@ -90,4 +105,12 @@ public void decreaseStock(Integer quantity) {
public boolean isSoldOut() {
return this.productStatus == OUT_OF_STOCK;
}

public void increaseOrderCount() {
this.orderCount++;
}

public void updateAvgScore(Double avgScore) {
this.avgScore = avgScore;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.c4marathon.assignment.domain.product.repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import org.c4marathon.assignment.domain.product.entity.Product;
Expand All @@ -18,4 +20,88 @@ public interface ProductRepository extends JpaRepository<Product, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select p from Product p join fetch p.seller where p.id = :id")
Optional<Product> findByIdJoinFetch(@Param("id") Long id);

@Query(value = """
select count(*)
from product_tbl p
where p.product_id = :product_id
""", nativeQuery = true)
Long findReviewCount(@Param("product_id") Long id);

@Query(value = """
select *
from product_tbl p
where name like :keyword
and (p.created_at < :createdAt or (p.created_at = :createdAt and p.product_id > :id))
order by p.created_at desc, p.product_id
limit :pageSize
""", nativeQuery = true)
List<Product> findByNewest(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5개 메소드가 특정 컬럼을 기준으로 정렬한 값을 리턴하는 것 같은데 이걸 하나의 메소드로 사용하는건 어떻게 생각하시나요?
해당 파라미터가 null이면 처리하지 않고, 입력값이 들어오면 처리하는 방식으로요.
MyBatis가 이런 작업 하기 좋았던 것으로 기억합니다.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마 바 조 아

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MyBatis 적용했는데 이런 상황에선 굉장히 좋은것 같네요 굿굿

@Param("keyword") String keyword,
@Param("createdAt") LocalDateTime createdAt,
@Param("id") Long id,
@Param("pageSize") int pageSize
);

@Query(value = """
select *
from product_tbl p
where name like :keyword
and (p.amount > :amount or (p.amount = :amount and p.product_id > :id))
order by p.amount, p.product_id
limit :pageSize
""", nativeQuery = true)
List<Product> findByPriceAsc(
@Param("keyword") String keyword,
@Param("amount") Long amount,
@Param("id") Long id,
@Param("pageSize") int pageSize
);

@Query(value = """
select *
from product_tbl p
where (p.amount < :amount or (p.amount = :amount and p.product_id > :id))
and name like :keyword
order by p.amount desc, p.product_id
limit :pageSize
""", nativeQuery = true)
List<Product> findByPriceDesc(
@Param("keyword") String keyword,
@Param("amount") Long amount,
@Param("id") Long id,
@Param("pageSize") int pageSize
);

@Query(value = """
select *
from product_tbl
where name like :keyword
and order_count <= :order_count
or (order_count = :order_count and product_id > :product_id)
order by order_count desc, product_id
limit :pageSize
""", nativeQuery = true)
List<Product> findByPopularity(
@Param("keyword") String keyword,
@Param("order_count") Long orderCount,
@Param("product_id") Long id,
@Param("pageSize") int pageSize
);

@Query(value = """
select *
from product_tbl
where name like :keyword
and avg_score <= :avgScore
or (avg_score = :avgScore and product_id > :product_id)
order by avg_score desc, product_id
limit :pageSize
""", nativeQuery = true)
List<Product> findByTopRated(
@Param("keyword") String keyword,
@Param("avgScore") Double avgScore,
@Param("product_id") Long id,
@Param("pageSize") int pageSize
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.c4marathon.assignment.domain.product.service;

import java.util.List;

import org.c4marathon.assignment.domain.product.dto.request.ProductSearchRequest;
import org.c4marathon.assignment.domain.product.dto.response.ProductSearchResponse;
import org.c4marathon.assignment.domain.product.entity.Product;
import org.c4marathon.assignment.domain.product.repository.ProductRepository;
import org.c4marathon.assignment.domain.seller.entity.Seller;
Expand Down Expand Up @@ -31,4 +35,37 @@ public Product findById(Long id) {
return productRepository.findByIdJoinFetch(id)
.orElseThrow(() -> ErrorCode.PRODUCT_NOT_FOUND.baseException("id: %d", id));
}

@Transactional(readOnly = true)
public Long findReviewCount(Long productId) {
return productRepository.findReviewCount(productId);
}

/**
* sortType에 해당하는 조건으로 pagination
* Newest: 최신순, created_at desc, product_id asc
* PriceAsc: 가격 낮은 순, amount asc, product_id asc
* PriceDesc: 가격 높은 순, amount desc, product_id asc
* Popularity: 인기 순(주문 많은 순), order_count desc, product_id asc
* TopRated: 평점 높은 순(review score), avg_score desc, product_id asc
*/
@Transactional(readOnly = true)
public ProductSearchResponse searchProduct(ProductSearchRequest request) {
String keyword = toQueryKeyword(request.keyword());
Long productId = request.productId();
int pageSize = request.pageSize();

List<Product> products = switch (request.sortType()) {
case NEWEST -> productRepository.findByNewest(keyword, request.createdAt(), productId, pageSize);
case PRICE_ASC -> productRepository.findByPriceAsc(keyword, request.amount(), productId, pageSize);
case PRICE_DESC -> productRepository.findByPriceDesc(keyword, request.amount(), productId, pageSize);
case POPULARITY -> productRepository.findByPopularity(keyword, request.orderCount(), productId, pageSize);
case TOP_RATED -> productRepository.findByTopRated(keyword, request.score(), productId, pageSize);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위의 쿼리를 동적 쿼리로 바꾸면 여기서도 switch case가 아닌 입력값만 전부 받아서 하나의 메소드만 호출하는 것도 좋을 것 같아요.
음.. 근데 어떤 값을 기준으로 정렬이 되는지 확인하려면 동적 쿼리를 생성하는 코드를 봐야하는 불편함은 있겠네요.

};
return ProductSearchResponse.of(products);
}

private String toQueryKeyword(String keyword) {
return "%" + keyword + "%";
}
}
Loading
Loading