Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -19,19 +19,20 @@ public interface OrderRepository extends JpaRepository<Order, Long> {
// 사용자별 주문 목록 (페이징)
Page<Order> findByUserOrderByOrderDateDesc(User user, Pageable pageable);

// 사용자별 주문 목록 조회 - N+1 방지를 위한 Fetch Join
// 사용자별 주문 목록 조회 - N+1 방지를 위한 Fetch Join (NORMAL + FUNDING)
@Query("SELECT DISTINCT o FROM Order o " +
"LEFT JOIN FETCH o.orderItems oi " +
"LEFT JOIN FETCH oi.product p " +
"LEFT JOIN FETCH oi.funding f " +
"WHERE o.user = :user " +
"ORDER BY o.orderDate DESC")
List<Order> findByUserWithOrderItemsAndProducts(@Param("user") User user, Pageable pageable);

// 주문 상세 조회 - Fetch Join (상품 정보만)
@Query("SELECT o FROM Order o " +
// 주문 상세 조회 - Fetch Join (상품 정보 + 펀딩 정보)
@Query("SELECT DISTINCT o FROM Order o " +
"LEFT JOIN FETCH o.orderItems oi " +
"LEFT JOIN FETCH oi.product p " +
"LEFT JOIN FETCH p.user " +
"LEFT JOIN FETCH oi.funding f " +
"WHERE o.id = :orderId")
Optional<Order> findByIdWithOrderItems(@Param("orderId") Long orderId);

Expand Down Expand Up @@ -68,24 +69,26 @@ Page<Order> findOrdersForDashboard(
);

/**
* 대시보드용 주문 목록 조회 - 상품명 정렬용
* 대시보드용 주문 목록 조회 - 상품명 정렬용 (N+1 방지)
* 상품명 정렬 시 각 주문의 상품 중 이름이 가장 빠른 상품(ㄱ에 가까운)을 기준으로 정렬
* 1차 정렬: 상품명 (ASC/DESC)
* 2차 정렬: 주문 날짜 (최신순)
*/
@Query("SELECT o FROM Order o " +
"LEFT JOIN o.orderItems oi " +
@Query("SELECT DISTINCT o FROM Order o " +
"LEFT JOIN FETCH o.orderItems oi " +
"LEFT JOIN FETCH oi.product p " +
"LEFT JOIN FETCH oi.funding f " +
"WHERE o.user = :user " +
"AND o.status IN (com.back.domain.order.order.entity.OrderStatus.PAYMENT_COMPLETED, " +
" com.back.domain.order.order.entity.OrderStatus.PREPARING_SHIPMENT, " +
" com.back.domain.order.order.entity.OrderStatus.SHIPPING, " +
" com.back.domain.order.order.entity.OrderStatus.DELIVERED) " +
"AND (:keyword IS NULL OR :keyword = '' OR " +
" EXISTS (SELECT 1 FROM OrderItem oi2 WHERE oi2.order = o AND oi2.product.name LIKE CONCAT('%', :keyword, '%'))) " +
"AND oi.product.name = (SELECT MIN(oi3.product.name) FROM OrderItem oi3 WHERE oi3.order = o) " +
"AND p.name = (SELECT MIN(p2.name) FROM OrderItem oi3 JOIN oi3.product p2 WHERE oi3.order = o) " +
"ORDER BY " +
"CASE WHEN :direction = 'ASC' THEN oi.product.name END ASC, " +
"CASE WHEN :direction = 'DESC' THEN oi.product.name END DESC, " +
"CASE WHEN :direction = 'ASC' THEN p.name END ASC, " +
"CASE WHEN :direction = 'DESC' THEN p.name END DESC, " +
"o.orderDate DESC")
Page<Order> findOrdersForDashboardSortedByProductName(
@Param("user") User user,
Expand All @@ -95,24 +98,26 @@ Page<Order> findOrdersForDashboardSortedByProductName(
);

/**
* 주문 상세 정보 조회 (OrderItem, Product 포함)
* 주문 상세 정보 조회 (OrderItem, Product, Funding 포함)
* 대시보드에서 페이징 후 실제 데이터를 가져올 때 사용
* Product의 Images는 @BatchSize로 최적화 (N+1 방지)
*/
@Query("SELECT DISTINCT o FROM Order o " +
"LEFT JOIN FETCH o.orderItems oi " +
"LEFT JOIN FETCH oi.product p " +
"LEFT JOIN FETCH oi.funding f " +
"WHERE o.id IN :orderIds")
List<Order> findOrdersWithDetailsById(@Param("orderIds") List<Long> orderIds);

/**
* 작가별 주문 목록 조회 (작가용 대시보드)
* 작가별 주문 목록 조회 (작가용 대시보드) - N+1 방지
* 작가가 판매한 상품의 주문만 조회
* 주문 상태, 키워드 검색, 날짜 범위 필터 지원
*/
@Query("SELECT DISTINCT o FROM Order o " +
"JOIN o.orderItems oi " +
"JOIN oi.product p " +
"LEFT JOIN FETCH o.orderItems oi " +
"LEFT JOIN FETCH oi.product p " +
"LEFT JOIN FETCH oi.funding f " +
"WHERE p.user.id = :artistId " +
"AND (:status IS NULL OR o.status = :status) " +
"AND (:keyword IS NULL OR :keyword = '' OR " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public Page<OrderResponseDto> getOrderList(User user, Pageable pageable) {
/**
* 주문 상세 조회
*/
@Transactional(readOnly = true)
public OrderResponseDto getOrderDetail(Long orderId, User user) {
Order order = orderRepository.findByIdWithOrderItems(orderId)
.orElseThrow(() -> new IllegalArgumentException("주문을 찾을 수 없습니다."));
Expand Down Expand Up @@ -572,11 +573,20 @@ private String getProductThumbnailUrl(Product product) {
* 펀딩 썸네일 이미지 URL 조회
*/
private String getFundingThumbnailUrl(Funding funding) {
return funding.getImages().stream()
.filter(image -> "THUMBNAIL".equals(image.getFileType().name()))
.findFirst()
.map(image -> image.getFileUrl())
.orElse(funding.getImageUrl()); // 썸네일이 없으면 메인 이미지 사용
try {
// Funding.images는 LAZY 로딩이므로 안전하게 접근
if (funding.getImages() != null && !funding.getImages().isEmpty()) {
return funding.getImages().stream()
.filter(image -> "THUMBNAIL".equals(image.getFileType().name()))
.findFirst()
.map(image -> image.getFileUrl())
.orElse(funding.getImageUrl()); // 썸네일이 없으면 메인 이미지 사용
}
} catch (Exception e) {
// LAZY 로딩 실패 시 메인 이미지 사용
log.warn("Funding 이미지 로딩 실패, 메인 이미지 사용: fundingId={}", funding.getId());
}
return funding.getImageUrl(); // 기본 이미지 사용
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/back/global/s3/S3Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public List<UploadResultResponse> uploadFile(MultipartFile file, String folder,
results.add(new UploadResultResponse(url, type, s3Key, originalFilename));

if (category == FileCategory.IMAGE && type == FileType.MAIN) {
byte[] thumbBytes = resizeImageSafe(file.getBytes(), 300, 300, extension);
byte[] thumbBytes = resizeImageSafe(file.getBytes(), 300, 400, extension);
// 썸네일 S3 Key 생성
String thumbKey = folder + "/thumbnail-" + UUID.randomUUID() + "." + extension;
//S3에 썸네일 이미지 업로드
Expand Down