@@ -44,6 +44,7 @@ public class DashboardServiceImpl implements DashboardService {
4444 private final com .back .domain .payment .moriCash .repository .MoriCashPaymentRepository moriCashPaymentRepository ;
4545 private final com .back .domain .payment .moriCash .repository .MoriCashBalanceRepository moriCashBalanceRepository ;
4646 private final com .back .domain .follow .repository .FollowRepository followRepository ;
47+ private final com .back .domain .wishlist .repository .WishlistRepository wishlistRepository ;
4748
4849 private static final DateTimeFormatter ORDER_DATE_FORMATTER = DateTimeFormatter .ofPattern ("yyyy. MM. dd" );
4950 private static final DateTimeFormatter FUNDING_DATE_FORMATTER = DateTimeFormatter .ofPattern ("yyyy. MM. dd" );
@@ -119,7 +120,7 @@ public ArtistApplicationResponse.List getArtistApplications(Long userId, ArtistA
119120 int start = request .page () * request .size ();
120121 int end = Math .min (start + request .size (), applications .size ());
121122 List <com .back .domain .artist .entity .ArtistApplication > pagedApplications =
122- applications .subList (start , Math . min ( end , applications . size ()) );
123+ applications .subList (start , end );
123124
124125 // 5. DTO 변환
125126 List <ArtistApplicationResponse .Summary > content = pagedApplications .stream ()
@@ -408,15 +409,23 @@ private String getProductThumbnailUrl(com.back.domain.product.product.entity.Pro
408409 return null ;
409410 }
410411
411- if (product .getImages () == null || product .getImages ().isEmpty ()) {
412+ try {
413+ if (product .getImages () == null || product .getImages ().isEmpty ()) {
414+ return null ;
415+ }
416+
417+ return product .getImages ().stream ()
418+ .filter (image -> image != null &&
419+ image .getFileType () != null &&
420+ "THUMBNAIL" .equals (image .getFileType ().name ()))
421+ .findFirst ()
422+ .map (com .back .domain .product .product .entity .ProductImage ::getFileUrl )
423+ .orElse (null );
424+ } catch (Exception e ) {
425+ // LazyInitializationException 등의 에러 발생 시 null 반환
426+ log .warn ("상품 이미지 컬렉션 접근 실패 - productId: {}" , product .getId (), e );
412427 return null ;
413428 }
414-
415- return product .getImages ().stream ()
416- .filter (image -> "THUMBNAIL" .equals (image .getFileType ().name ()))
417- .findFirst ()
418- .map (com .back .domain .product .product .entity .ProductImage ::getFileUrl )
419- .orElse (null );
420429 }
421430
422431 /**
@@ -451,83 +460,127 @@ private String mapOrderStatusText(com.back.domain.order.order.entity.OrderStatus
451460 public FollowingResponse .List getFollowingArtists (Long userId , FollowingSearchRequest request ) {
452461 log .debug ("팔로우한 작가 목록 조회 - userId: {}, request: {}" , userId , request );
453462
454- // 1. 사용자 조회
455- User user = userRepository .findById (userId )
456- .orElseThrow (() -> new ServiceException ("USER_NOT_FOUND" , "사용자를 찾을 수 없습니다." ));
463+ // 1. 사용자 존재 여부 확인
464+ if (!userRepository .existsById (userId )) {
465+ throw new ServiceException ("USER_NOT_FOUND" , "사용자를 찾을 수 없습니다." );
466+ }
457467
458468 // 2. 팔로우 목록 조회
459469 List <com .back .domain .follow .entity .Follow > follows =
460470 followRepository .findFollowingsByFollowerId (userId );
461471
462472 // 3. 페이징 처리
463- long total = follows .size ();
464473 int start = request .page () * request .size ();
465474 int end = Math .min (start + request .size (), follows .size ());
466475 List <com .back .domain .follow .entity .Follow > pagedFollows =
467- follows .subList (start , Math . min ( end , follows . size ()) );
476+ start < follows .size () ? follows . subList (start , end ) : List . of ( );
468477
469478 // 4. DTO 변환
470479 List <FollowingResponse .Artist > content = pagedFollows .stream ()
471480 .map (this ::convertToFollowingArtist )
472481 .collect (Collectors .toList ());
473482
474483 // 5. 페이징 정보 계산
475- int totalPages = (int ) Math .ceil ((double ) total / request .size ());
476- boolean hasNext = request .page () < totalPages - 1 ;
477- boolean hasPrevious = request .page () > 0 ;
484+ int totalPages = (int ) Math .ceil ((double ) follows .size () / request .size ());
478485
479486 return new FollowingResponse .List (
480487 content ,
481488 request .page (), request .size (),
482- total , totalPages , hasNext , hasPrevious );
489+ follows .size (), totalPages ,
490+ end < follows .size (),
491+ request .page () > 0
492+ );
483493 }
484494
485495 /**
486496 * Follow 엔티티를 Artist DTO로 변환
487497 */
488498 private FollowingResponse .Artist convertToFollowingArtist (com .back .domain .follow .entity .Follow follow ) {
489- com .back .domain .artist .entity .ArtistProfile artist = follow .getFollowingArtist ();
499+ com .back .domain .artist .entity .ArtistProfile artistProfile = follow .getFollowingArtist ();
490500
491501 return new FollowingResponse .Artist (
492- artist .getId ().toString (),
493- artist .getArtistName (),
494- artist .getProfileImageUrl (), // null인 경우 프론트에서 기본 이미지 처리
495- "/artists/" + artist .getId ()
502+ artistProfile .getId ().toString (),
503+ artistProfile .getArtistName (),
504+ artistProfile .getProfileImageUrl (),
505+ "/artists/" + artistProfile .getId ()
496506 );
497507 }
498508
499509 @ Override
500510 public WishlistResponse .List getWishlist (Long userId , WishlistSearchRequest request ) {
501- // TODO: 실제 데이터베이스 조회 로직 구현
502511 log .debug ("찜한 상품 목록 조회 - userId: {}, request: {}" , userId , request );
503512
504- WishlistResponse .SummaryDto summary = new WishlistResponse .SummaryDto (15 );
513+ // 1. 사용자 존재 여부 확인
514+ if (!userRepository .existsById (userId )) {
515+ throw new ServiceException ("USER_NOT_FOUND" , "사용자를 찾을 수 없습니다." );
516+ }
517+
518+ // 2. 페이징 설정 (페이지 크기 8개 고정)
519+ int fixedPageSize = 8 ;
520+ Pageable pageable = PageRequest .of (request .page (), fixedPageSize );
521+
522+ // 3. 찜 목록 조회 (Product, Artist 정보 포함, 삭제된 상품 제외)
523+ Page <com .back .domain .wishlist .entity .Wishlist > wishlistPage =
524+ wishlistRepository .findByUserIdWithProductAndArtist (userId , pageable );
525+
526+ // 4. DTO 변환
527+ List <WishlistResponse .Item > content = wishlistPage .getContent ().stream ()
528+ .map (this ::convertToWishlistItem )
529+ .collect (Collectors .toList ());
530+
531+ // 5. 통계 계산 (삭제되지 않은 상품만 카운트)
532+ long totalWishItems = wishlistPage .getTotalElements ();
533+ WishlistResponse .SummaryDto summary = new WishlistResponse .SummaryDto ((int ) totalWishItems );
505534
535+ // 6. 일괄 작업 옵션
506536 List <WishlistResponse .BulkAction > bulkActions = List .of (
507537 new WishlistResponse .BulkAction ("BULK_UNWISH" , "선택 항목 해제" , true )
508538 );
509539
510- List <WishlistResponse .Item > content = List .of (
511- new WishlistResponse .Item (
512- "w-001" , 123157L , "0123157" , "감성 일러스트 포스터" , 25000 ,
513- new WishlistResponse .Artist ("artist001" , "감성작가" ),
514- "https://cdn.example.com/p/123157/main.jpg" , "SELLING" , "2025-09-18" ,
515- LocalDateTime .now (), "/products/0123157" ,
516- new WishlistResponse .Permission (true )
517- ),
518- new WishlistResponse .Item (
519- "w-002" , 123158L , "0123158" , "귀여운 스티커 세트" , 15000 ,
520- new WishlistResponse .Artist ("artist002" , "캐릭터작가" ),
521- "https://cdn.example.com/p/123158/main.jpg" , "SELLING" , "2025-09-17" ,
522- LocalDateTime .now ().minusDays (1 ), "/products/0123158" ,
523- new WishlistResponse .Permission (true )
524- )
525- );
526-
527540 return new WishlistResponse .List (
528541 summary , bulkActions , content ,
529- request .page (), request .size (),
530- 15 , 2 , true , false );
542+ wishlistPage .getNumber (), fixedPageSize ,
543+ wishlistPage .getTotalElements (), wishlistPage .getTotalPages (),
544+ wishlistPage .hasNext (), wishlistPage .hasPrevious ()
545+ );
546+ }
547+
548+ /**
549+ * Wishlist 엔티티를 Item DTO로 변환
550+ */
551+ private WishlistResponse .Item convertToWishlistItem (com .back .domain .wishlist .entity .Wishlist wishlist ) {
552+ com .back .domain .product .product .entity .Product product = wishlist .getProduct ();
553+
554+ // 작가 정보 (Product의 user가 작가)
555+ WishlistResponse .Artist artist = null ;
556+ if (product .getUser () != null ) {
557+ artist = new WishlistResponse .Artist (
558+ product .getUser ().getId ().toString (),
559+ product .getUser ().getName ()
560+ );
561+ }
562+
563+ // 상품 상태 매핑
564+ String sellingStatus = product .getSellingStatus () != null ?
565+ product .getSellingStatus ().name () : "UNKNOWN" ;
566+
567+ // 썸네일 이미지 URL
568+ String imageUrl = getProductThumbnailUrl (product );
569+
570+ return new WishlistResponse .Item (
571+ "w-" + wishlist .getId (),
572+ product .getId (),
573+ String .format ("%07d" , product .getId ()),
574+ product .getBrandName () != null ? product .getBrandName () : "" ,
575+ product .getName (),
576+ product .getPrice (),
577+ artist ,
578+ imageUrl ,
579+ sellingStatus ,
580+ wishlist .getCreateDate (),
581+ "/products/" + product .getId (),
582+ new WishlistResponse .Permission (true )
583+ );
531584 }
532585
533586 @ Override
@@ -626,7 +679,9 @@ private String getFundingThumbnailUrl(Funding funding) {
626679 // images 컬렉션에서 THUMBNAIL 타입 찾기
627680 if (funding .getImages () != null && !funding .getImages ().isEmpty ()) {
628681 String thumbnailUrl = funding .getImages ().stream ()
629- .filter (image -> image != null && "THUMBNAIL" .equals (image .getFileType ().name ()))
682+ .filter (image -> image != null &&
683+ image .getFileType () != null &&
684+ "THUMBNAIL" .equals (image .getFileType ().name ()))
630685 .findFirst ()
631686 .map (FundingImage ::getFileUrl )
632687 .orElse (null );
@@ -729,8 +784,8 @@ public CashResponse.HistoryList getCashHistory(Long userId, CashHistorySearchReq
729784 tx .getBalanceAfter () != null ? tx .getBalanceAfter () : 0 ,
730785 "모리캐시" ,
731786 "COMPLETED" ,
732- tx .getOrder () != null ?
733- new CashResponse .Link ("/orders/" + tx .getOrder ().getOrderNumber ()) : null
787+ tx .getOrder () != null ?
788+ new CashResponse .Link ("/orders/" + tx .getOrder ().getOrderNumber ()) : null
734789 )));
735790
736791 // 4. 날짜순 정렬 (최신순)
@@ -739,7 +794,7 @@ public CashResponse.HistoryList getCashHistory(Long userId, CashHistorySearchReq
739794 // 5. 페이징 처리
740795 int start = request .page () * request .size ();
741796 int end = Math .min (start + request .size (), allTransactions .size ());
742- var pagedContent = start < allTransactions .size () ?
797+ var pagedContent = start < allTransactions .size () ?
743798 allTransactions .subList (start , end ) : List .<CashResponse .Transaction >of ();
744799
745800 // 6. 통계 계산
0 commit comments