diff --git a/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java b/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java index c16bee1c..f3e98e93 100644 --- a/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java +++ b/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java @@ -76,7 +76,9 @@ private RequestMatcher getMatcherForAnyone() { antMatcher(HttpMethod.GET, "/api/v1/shows"), antMatcher(HttpMethod.GET, "/api/v1/shows/{showId}"), antMatcher(HttpMethod.GET, "/api/v1/artists/search/**"), - antMatcher(HttpMethod.GET, "/api/v1/shows/search/**") + antMatcher(HttpMethod.GET, "/api/v1/shows/search/**"), + antMatcher(HttpMethod.GET, "/api/v1/artists/filter"), + antMatcher(HttpMethod.GET, "/api/v1/artists/filter-total-count") ); } @@ -87,14 +89,12 @@ private RequestMatcher getMatcherForUserAndAdmin() { antMatcher(HttpMethod.POST, "/api/v1/users/withdrawal"), antMatcher(HttpMethod.POST, "/api/v1/shows/**/interest"), antMatcher(HttpMethod.POST, "/api/v1/shows/**/alert"), - antMatcher(HttpMethod.POST, "/api/v1/artists/subscribe"), antMatcher(HttpMethod.POST, "/api/v1/genres/subscribe"), antMatcher(HttpMethod.POST, "/api/v1/genres/unsubscribe"), antMatcher(HttpMethod.GET, "/api/v1/genres/subscriptions"), - antMatcher(HttpMethod.POST, "/api/v1/genres/**"), - antMatcher(HttpMethod.GET, "/api/v1/artists/subscribed"), antMatcher(HttpMethod.POST, "/api/v1/artists/subscribe"), - antMatcher(HttpMethod.POST, "/api/v1/artists/unsubscribe") + antMatcher(HttpMethod.POST, "/api/v1/artists/unsubscribe"), + antMatcher(HttpMethod.GET, "/api/v1/artists/subscriptions") ); } } diff --git a/app/api/show-api/build.gradle b/app/api/show-api/build.gradle index 92bcfeb6..658a2730 100644 --- a/app/api/show-api/build.gradle +++ b/app/api/show-api/build.gradle @@ -2,4 +2,8 @@ dependencies { implementation project(":app:domain:show-domain") implementation project(":app:domain:user-domain") implementation project(':app:api:common-api') + + //testFixtures + testImplementation(testFixtures(project(":app:domain:show-domain"))) + testImplementation(testFixtures(project(":app:domain:user-domain"))) } \ No newline at end of file diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/ArtistController.java b/app/api/show-api/src/main/java/com/example/artist/controller/ArtistController.java index e43d5ed5..faa415ed 100644 --- a/app/api/show-api/src/main/java/com/example/artist/controller/ArtistController.java +++ b/app/api/show-api/src/main/java/com/example/artist/controller/ArtistController.java @@ -2,20 +2,20 @@ import com.example.artist.controller.dto.param.ArtistSearchPaginationApiParam; import com.example.artist.controller.dto.param.ArtistSubscriptionPaginationApiParam; +import com.example.artist.controller.dto.param.ArtistUnsubscriptionPaginationApiParam; +import com.example.artist.controller.dto.request.ArtistFilterTotalCountApiRequest; import com.example.artist.controller.dto.request.ArtistSearchPaginationApiRequest; import com.example.artist.controller.dto.request.ArtistSubscriptionApiRequest; import com.example.artist.controller.dto.request.ArtistSubscriptionPaginationApiRequest; import com.example.artist.controller.dto.request.ArtistUnsubscriptionApiRequest; -import com.example.artist.controller.dto.response.ArtistPaginationApiResponse; -import com.example.artist.controller.dto.response.ArtistSimpleApiResponse; +import com.example.artist.controller.dto.request.ArtistUnsubscriptionPaginationApiRequest; +import com.example.artist.controller.dto.response.ArtistFilterTotalCountApiResponse; import com.example.artist.controller.dto.response.ArtistSubscriptionApiResponse; import com.example.artist.controller.dto.response.ArtistUnsubscriptionApiResponse; import com.example.artist.service.ArtistService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import java.util.List; -import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.response.PaginationApiResponse; import org.example.security.dto.AuthenticatedUser; @@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.PostMapping; 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; @RestController @@ -36,24 +35,24 @@ public class ArtistController { private final ArtistService artistService; - private String image = "https://thumb.mtstarnews.com/06/2023/06/2023062914274537673_1.jpg"; @GetMapping - @Operation(summary = "아티스트 목록 조회") - public ResponseEntity getArtists( - @RequestParam(required = false) ArtistSearchPaginationApiRequest param + @Operation(summary = "구독하지 않은 아티스트 목록 조회") + public ResponseEntity> getUnsubscribedArtists( + @AuthenticationPrincipal AuthenticatedUser user, + @ParameterObject ArtistUnsubscriptionPaginationApiRequest request ) { + var response = artistService.findArtistUnsubscriptions( + request.toServiceRequest(user.userId())); + var data = response.data().stream() + .map(ArtistUnsubscriptionPaginationApiParam::from) + .toList(); + return ResponseEntity.ok( - new ArtistPaginationApiResponse( - List.of( - new ArtistSimpleApiResponse( - UUID.randomUUID(), - "윈터", - image - ) - ), - false - ) + PaginationApiResponse.builder() + .hasNext(response.hasNext()) + .data(data) + .build() ); } @@ -96,7 +95,6 @@ public ResponseEntity unsubscribe( @AuthenticationPrincipal AuthenticatedUser user, @Valid @RequestBody ArtistUnsubscriptionApiRequest request ) { - ; return ResponseEntity.ok( ArtistUnsubscriptionApiResponse.from( artistService.unsubscribe(request.toServiceRequest(user.userId())) @@ -122,4 +120,18 @@ public ResponseEntity> sea .build() ); } + + @GetMapping("/filter-total-count") + @Operation(summary = "필터링한 데이터의 총 개수 가져오기") + public ResponseEntity filterTotalCount( + @AuthenticationPrincipal AuthenticatedUser user, + @Valid @RequestBody ArtistFilterTotalCountApiRequest request + ) { + var response = artistService.filterArtistTotalCount( + request.toServiceRequest(user.userId())); + + return ResponseEntity.ok( + ArtistFilterTotalCountApiResponse.from(response.totalCount()) + ); + } } diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/param/ArtistUnsubscriptionPaginationApiParam.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/param/ArtistUnsubscriptionPaginationApiParam.java new file mode 100644 index 00000000..a9b2c2f2 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/param/ArtistUnsubscriptionPaginationApiParam.java @@ -0,0 +1,28 @@ +package com.example.artist.controller.dto.param; + +import com.example.artist.service.dto.param.ArtistUnsubscriptionPaginationServiceParam; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.UUID; + +public record ArtistUnsubscriptionPaginationApiParam( + @Schema(description = "아티스트 ID") + UUID id, + @Schema(description = "아티스트 이미지 URL") + String imageUrl, + @Schema(description = "아티스트 한글 이름") + String koreanName, + @Schema(description = "아티스트 영문 이름") + String englishName +) { + + public static ArtistUnsubscriptionPaginationApiParam from( + ArtistUnsubscriptionPaginationServiceParam param + ) { + return new ArtistUnsubscriptionPaginationApiParam( + param.artistId(), + param.artistImageUrl(), + param.artistKoreanName(), + param.artistEnglishName() + ); + } +} diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistFilterTotalCountApiRequest.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistFilterTotalCountApiRequest.java new file mode 100644 index 00000000..ce28658d --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistFilterTotalCountApiRequest.java @@ -0,0 +1,40 @@ +package com.example.artist.controller.dto.request; + +import com.example.artist.service.dto.request.ArtistFilterTotalCountServiceRequest; +import com.example.artist.vo.ArtistApiType; +import com.example.artist.vo.ArtistGenderApiType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import java.util.UUID; +import org.example.util.ValidateStatus; + +public record ArtistFilterTotalCountApiRequest( + @Schema(description = "아티스트 성별") + List artistGenderApiTypes, + + @Schema(description = "아티스트 타입") + List artistApiTypes, + + @Schema(description = "장르 ID 목록") + List genreIds +) { + + public ArtistFilterTotalCountApiRequest( + List artistGenderApiTypes, + List artistApiTypes, + List genreIds + ) { + this.artistGenderApiTypes = ValidateStatus.checkNullOrEmpty(artistGenderApiTypes); + this.artistApiTypes = ValidateStatus.checkNullOrEmpty(artistApiTypes); + this.genreIds = ValidateStatus.checkNullOrEmpty(genreIds); + } + + public ArtistFilterTotalCountServiceRequest toServiceRequest(UUID userId) { + return ArtistFilterTotalCountServiceRequest.builder() + .artistGenderApiTypes(artistGenderApiTypes) + .artistApiTypes(artistApiTypes) + .genreIds(genreIds) + .userId(userId) + .build(); + } +} diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSearchPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSearchPaginationApiRequest.java index 39c1f91f..e0d238b3 100644 --- a/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSearchPaginationApiRequest.java +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSearchPaginationApiRequest.java @@ -17,10 +17,10 @@ public record ArtistSearchPaginationApiRequest( @Parameter(description = "이전 페이지네이션 마지막 데이터의 ID / 최초 조회라면 null") UUID cursor, - @Parameter(description = "조회하는 데이터 개수") + @Parameter(description = "조회하는 데이터 개수", required = true) int size, - @Parameter(description = "검색어") + @Parameter(description = "검색어", required = true) String search ) { diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSubscriptionPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSubscriptionPaginationApiRequest.java index a55b3d72..4a951690 100644 --- a/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSubscriptionPaginationApiRequest.java +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistSubscriptionPaginationApiRequest.java @@ -2,6 +2,7 @@ import com.example.artist.service.dto.request.ArtistSubscriptionPaginationServiceRequest; import com.example.artist.vo.ArtistSortStandardApiType; +import com.example.vo.SubscriptionStatusApiType; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import java.util.UUID; @@ -18,7 +19,7 @@ public record ArtistSubscriptionPaginationApiRequest( @Parameter(description = "이전 페이지네이션 마지막 데이터의 ID / 최초 조회라면 null") UUID cursor, - @Parameter(description = "조회하는 데이터 개수") + @Parameter(description = "조회하는 데이터 개수", required = true) int size ) { @@ -27,13 +28,15 @@ public ArtistSubscriptionPaginationApiRequest( UUID cursor, int size ) { - this.sortStandard = sortStandard == null ? ArtistSortStandardApiType.ENGLISH_NAME_ASC : sortStandard; + this.sortStandard = + sortStandard == null ? ArtistSortStandardApiType.ENGLISH_NAME_ASC : sortStandard; this.cursor = cursor; this.size = size; } public ArtistSubscriptionPaginationServiceRequest toServiceRequest(UUID userId) { return ArtistSubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.SUBSCRIBED) .size(size) .sortStandard(sortStandard) .cursor(cursor) diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistUnsubscriptionPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistUnsubscriptionPaginationApiRequest.java new file mode 100644 index 00000000..e2446293 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/request/ArtistUnsubscriptionPaginationApiRequest.java @@ -0,0 +1,68 @@ +package com.example.artist.controller.dto.request; + +import com.example.artist.service.dto.request.ArtistUnsubscriptionPaginationServiceRequest; +import com.example.artist.vo.ArtistApiType; +import com.example.artist.vo.ArtistGenderApiType; +import com.example.artist.vo.ArtistSortStandardApiType; +import com.example.vo.SubscriptionStatusApiType; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import java.util.UUID; +import org.example.util.ValidateStatus; + +@Schema +public record ArtistUnsubscriptionPaginationApiRequest( + @Parameter( + description = "정렬 기준, default: ENGLISH_NAME_ASC", + schema = @Schema(implementation = ArtistSortStandardApiType.class) + ) + ArtistSortStandardApiType sortStandard, + + @Parameter(description = "아티스트 성별 목록") + List artistGenderApiTypes, + + @Parameter(description = "아티스트 타입 목록") + List artistApiTypes, + + @Parameter(description = "장르 ID 목록") + List genreIds, + + @Parameter(description = "이전 페이지네이션 마지막 데이터의 ID / 최초 조회라면 null") + UUID cursor, + + @Parameter(description = "조회하는 데이터 개수", required = true) + int size +) { + + public ArtistUnsubscriptionPaginationApiRequest( + ArtistSortStandardApiType sortStandard, + List artistGenderApiTypes, + List artistApiTypes, + List genreIds, + UUID cursor, + int size + ) { + this.sortStandard = + sortStandard == null ? ArtistSortStandardApiType.ENGLISH_NAME_ASC : sortStandard; + this.artistGenderApiTypes = ValidateStatus.checkNullOrEmpty(artistGenderApiTypes); + this.artistApiTypes = ValidateStatus.checkNullOrEmpty(artistApiTypes); + this.genreIds = ValidateStatus.checkNullOrEmpty(genreIds); + this.cursor = cursor; + this.size = size; + } + + + public ArtistUnsubscriptionPaginationServiceRequest toServiceRequest(UUID userId) { + return ArtistUnsubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.UNSUBSCRIBED) + .sortStandard(sortStandard) + .artistGenderApiTypes(artistGenderApiTypes) + .artistApiTypes(artistApiTypes) + .genreIds(genreIds) + .userId(userId) + .cursor(cursor) + .size(size) + .build(); + } +} diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistFilterTotalCountApiResponse.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistFilterTotalCountApiResponse.java new file mode 100644 index 00000000..2a8e544e --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistFilterTotalCountApiResponse.java @@ -0,0 +1,14 @@ +package com.example.artist.controller.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record ArtistFilterTotalCountApiResponse( + @Schema(description = "필터링한 아티스트 총 개수") + int totalCount +) { + + public static ArtistFilterTotalCountApiResponse from(int totalCount) { + return new ArtistFilterTotalCountApiResponse(totalCount); + } + +} diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistPaginationApiResponse.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistPaginationApiResponse.java deleted file mode 100644 index 08b02481..00000000 --- a/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistPaginationApiResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.artist.controller.dto.response; - -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.List; - -public record ArtistPaginationApiResponse( - - @Schema(description = "아티스트 목록") - List artists, - - @Schema(description = "다음 페이지 존재 여부") - boolean hasNext -) { - -} diff --git a/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistUnsubscriptionApiResponse.java b/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistUnsubscriptionApiResponse.java index ef77672f..642d75dd 100644 --- a/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistUnsubscriptionApiResponse.java +++ b/app/api/show-api/src/main/java/com/example/artist/controller/dto/response/ArtistUnsubscriptionApiResponse.java @@ -1,10 +1,12 @@ package com.example.artist.controller.dto.response; import com.example.artist.service.dto.response.ArtistUnsubscriptionServiceResponse; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import java.util.UUID; public record ArtistUnsubscriptionApiResponse( + @Schema(description = "구독 취소한 아티스트 ID") List successUnsubscriptionArtistIds ) { diff --git a/app/api/show-api/src/main/java/com/example/artist/error/ArtistSubscriptionError.java b/app/api/show-api/src/main/java/com/example/artist/error/ArtistError.java similarity index 54% rename from app/api/show-api/src/main/java/com/example/artist/error/ArtistSubscriptionError.java rename to app/api/show-api/src/main/java/com/example/artist/error/ArtistError.java index 133143d9..cb91ca19 100644 --- a/app/api/show-api/src/main/java/com/example/artist/error/ArtistSubscriptionError.java +++ b/app/api/show-api/src/main/java/com/example/artist/error/ArtistError.java @@ -2,9 +2,8 @@ import org.example.exception.BusinessError; -public enum ArtistSubscriptionError implements BusinessError { - - ARTIST_NOT_EXIST { +public enum ArtistError implements BusinessError { + ENTITY_NOT_FOUND { @Override public int getHttpStatus() { return 404; @@ -12,17 +11,17 @@ public int getHttpStatus() { @Override public String getErrorCode() { - return "ASB-001"; + return "ART-001"; } @Override public String getClientMessage() { - return "구독하려는 아티스트가 존재하지 않습니다."; + return "존재하지 않은 아티스트입니다."; } @Override public String getLogMessage() { - return "요청으로 들어온 아티스트 ID에 해당하는 아티스트가 존재하지 않습니다."; + return "요청 값이 잘못 처리되었습니다."; } } } diff --git a/app/api/show-api/src/main/java/com/example/artist/service/ArtistAdminService.java b/app/api/show-api/src/main/java/com/example/artist/service/ArtistAdminService.java index 861970b7..66cb1137 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/ArtistAdminService.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/ArtistAdminService.java @@ -1,16 +1,18 @@ package com.example.artist.service; +import com.example.artist.error.ArtistError; import com.example.artist.service.dto.request.ArtistCreateServiceRequest; import com.example.artist.service.dto.request.ArtistUpdateServiceRequest; import com.example.artist.service.dto.response.ArtistDetailServiceResponse; import com.example.artist.service.dto.response.ArtistKoreanNameServiceResponse; import com.example.component.FileUploadComponent; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.example.dto.artist.response.ArtistDetailResponse; -import org.example.dto.artist.response.ArtistKoreanNameResponse; +import org.example.dto.artist.response.ArtistDetailDomainResponse; import org.example.entity.artist.Artist; +import org.example.exception.BusinessException; import org.example.usecase.artist.ArtistUseCase; import org.springframework.stereotype.Service; @@ -22,38 +24,55 @@ public class ArtistAdminService { private final FileUploadComponent fileUploadComponent; public void save(ArtistCreateServiceRequest artistCreateServiceRequest) { - String imageUrl = fileUploadComponent.uploadFile("artist", artistCreateServiceRequest.image()); + String imageUrl = fileUploadComponent.uploadFile("artist", + artistCreateServiceRequest.image()); Artist artist = artistCreateServiceRequest.toArtistWithImageUrl(imageUrl); artistUseCase.save(artist, artistCreateServiceRequest.genreIds()); } public List findAllWithGenreNames() { - List artistDetailResponses = artistUseCase.findAllWithGenreNames(); - return artistDetailResponses.stream() + return artistUseCase.findAllWithGenreNames().stream() .map(ArtistDetailServiceResponse::new) .toList(); } public List findAllArtistKoreanName() { - List artistKoreanNameResponses = artistUseCase.findAllArtistKoreanName(); - return artistKoreanNameResponses.stream() + return artistUseCase.findAllArtistKoreanName().stream() .map(ArtistKoreanNameServiceResponse::new) .toList(); } public ArtistDetailServiceResponse findArtistById(UUID id) { - return new ArtistDetailServiceResponse(artistUseCase.findArtistDetailById(id)); + ArtistDetailDomainResponse response; + try { + response = artistUseCase.findArtistDetailById(id); + } catch (NoSuchElementException e) { + throw new BusinessException(ArtistError.ENTITY_NOT_FOUND); + } + + return new ArtistDetailServiceResponse(response); } public void updateArtist(UUID id, ArtistUpdateServiceRequest artistUpdateServiceRequest) { - String imageUrl = fileUploadComponent.uploadFile("artist", artistUpdateServiceRequest.image()); - + String imageUrl = fileUploadComponent.uploadFile( + "artist", + artistUpdateServiceRequest.image() + ); Artist artist = artistUpdateServiceRequest.toArtist(imageUrl); - artistUseCase.updateArtist(id, artist, artistUpdateServiceRequest.genreIds()); + + try { + artistUseCase.updateArtist(id, artist, artistUpdateServiceRequest.genreIds()); + } catch (NoSuchElementException e) { + throw new BusinessException(ArtistError.ENTITY_NOT_FOUND); + } } public void deleteArtist(UUID id) { - artistUseCase.deleteArtist(id); + try { + artistUseCase.deleteArtist(id); + } catch (NoSuchElementException e) { + throw new BusinessException(ArtistError.ENTITY_NOT_FOUND); + } } } diff --git a/app/api/show-api/src/main/java/com/example/artist/service/ArtistService.java b/app/api/show-api/src/main/java/com/example/artist/service/ArtistService.java index 6617bf8e..d1f22d1b 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/ArtistService.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/ArtistService.java @@ -2,13 +2,18 @@ import com.example.artist.service.dto.param.ArtistSearchPaginationServiceParam; import com.example.artist.service.dto.param.ArtistSubscriptionPaginationServiceParam; +import com.example.artist.service.dto.param.ArtistUnsubscriptionPaginationServiceParam; +import com.example.artist.service.dto.request.ArtistFilterTotalCountServiceRequest; import com.example.artist.service.dto.request.ArtistSearchPaginationServiceRequest; import com.example.artist.service.dto.request.ArtistSubscriptionPaginationServiceRequest; import com.example.artist.service.dto.request.ArtistSubscriptionServiceRequest; +import com.example.artist.service.dto.request.ArtistUnsubscriptionPaginationServiceRequest; import com.example.artist.service.dto.request.ArtistUnsubscriptionServiceRequest; +import com.example.artist.service.dto.response.ArtistFilterTotalCountServiceResponse; import com.example.artist.service.dto.response.ArtistSubscriptionServiceResponse; import com.example.artist.service.dto.response.ArtistUnsubscriptionServiceResponse; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.response.PaginationServiceResponse; @@ -27,7 +32,8 @@ public class ArtistService { private final ArtistSubscriptionUseCase artistSubscriptionUseCase; public PaginationServiceResponse searchArtist( - ArtistSearchPaginationServiceRequest request) { + ArtistSearchPaginationServiceRequest request + ) { var response = artistUseCase.searchArtist(request.toDomainRequest()); List data = response.data().stream() @@ -37,6 +43,21 @@ public PaginationServiceResponse searchArtis return PaginationServiceResponse.of(data, response.hasNext()); } + public ArtistFilterTotalCountServiceResponse filterArtistTotalCount( + ArtistFilterTotalCountServiceRequest request + ) { + List subscriptionArtistIds = getSubscriptionArtistIds(request.userId()); + + try { + return new ArtistFilterTotalCountServiceResponse( + artistUseCase.findFilterArtistTotalCount( + request.toDomainRequest(subscriptionArtistIds)) + ); + } catch (NoSuchElementException e) { + return ArtistFilterTotalCountServiceResponse.noneTotalCount(); + } + } + public ArtistSubscriptionServiceResponse subscribe(ArtistSubscriptionServiceRequest request) { List existArtistsInRequest = artistUseCase.findAllArtistInIds(request.artistIds()); List existArtistIdsInRequest = existArtistsInRequest.stream() @@ -75,11 +96,7 @@ public ArtistUnsubscriptionServiceResponse unsubscribe( public PaginationServiceResponse findArtistSubscriptions( ArtistSubscriptionPaginationServiceRequest request ) { - List subscriptions = artistSubscriptionUseCase.findSubscriptionList( - request.userId()); - List subscriptionArtistIds = subscriptions.stream() - .map(ArtistSubscription::getArtistId) - .toList(); + List subscriptionArtistIds = getSubscriptionArtistIds(request.userId()); if (subscriptionArtistIds.isEmpty()) { return PaginationServiceResponse.of(List.of(), false); @@ -93,4 +110,27 @@ public PaginationServiceResponse findA return PaginationServiceResponse.of(data, response.hasNext()); } + + public PaginationServiceResponse findArtistUnsubscriptions( + ArtistUnsubscriptionPaginationServiceRequest request + ) { + List subscriptionArtistIds = getSubscriptionArtistIds(request.userId()); + + var response = artistUseCase.findAllArtistInCursorPagination( + request.toDomainRequest(subscriptionArtistIds)); + List data = response.data().stream() + .map(ArtistUnsubscriptionPaginationServiceParam::new) + .toList(); + + return PaginationServiceResponse.of(data, response.hasNext()); + } + + private List getSubscriptionArtistIds(UUID userId) { + List subscriptions = artistSubscriptionUseCase.findSubscriptionList( + userId); + + return subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + } } diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSearchPaginationServiceParam.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSearchPaginationServiceParam.java index 17e51ab1..b5d3b33f 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSearchPaginationServiceParam.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSearchPaginationServiceParam.java @@ -1,7 +1,7 @@ package com.example.artist.service.dto.param; import java.util.UUID; -import org.example.dto.artist.response.SimpleArtistResponse; +import org.example.dto.artist.response.ArtistSimpleDomainResponse; public record ArtistSearchPaginationServiceParam( UUID artistId, @@ -10,7 +10,7 @@ public record ArtistSearchPaginationServiceParam( String artistEnglishName ) { - public ArtistSearchPaginationServiceParam(SimpleArtistResponse response) { + public ArtistSearchPaginationServiceParam(ArtistSimpleDomainResponse response) { this( response.id(), response.image(), diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSubscriptionPaginationServiceParam.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSubscriptionPaginationServiceParam.java index 4b81534b..b5c8ef66 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSubscriptionPaginationServiceParam.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistSubscriptionPaginationServiceParam.java @@ -1,7 +1,7 @@ package com.example.artist.service.dto.param; import java.util.UUID; -import org.example.dto.artist.response.SimpleArtistResponse; +import org.example.dto.artist.response.ArtistSimpleDomainResponse; public record ArtistSubscriptionPaginationServiceParam( UUID artistId, @@ -10,7 +10,7 @@ public record ArtistSubscriptionPaginationServiceParam( String artistEnglishName ) { - public ArtistSubscriptionPaginationServiceParam(SimpleArtistResponse response) { + public ArtistSubscriptionPaginationServiceParam(ArtistSimpleDomainResponse response) { this( response.id(), response.image(), diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistUnsubscriptionPaginationServiceParam.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistUnsubscriptionPaginationServiceParam.java new file mode 100644 index 00000000..017ac06c --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/param/ArtistUnsubscriptionPaginationServiceParam.java @@ -0,0 +1,21 @@ +package com.example.artist.service.dto.param; + +import java.util.UUID; +import org.example.dto.artist.response.ArtistSimpleDomainResponse; + +public record ArtistUnsubscriptionPaginationServiceParam( + UUID artistId, + String artistImageUrl, + String artistKoreanName, + String artistEnglishName +) { + + public ArtistUnsubscriptionPaginationServiceParam(ArtistSimpleDomainResponse response) { + this( + response.id(), + response.image(), + response.koreanName(), + response.englishName() + ); + } +} diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistFilterTotalCountServiceRequest.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistFilterTotalCountServiceRequest.java new file mode 100644 index 00000000..f6d96f5c --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistFilterTotalCountServiceRequest.java @@ -0,0 +1,33 @@ +package com.example.artist.service.dto.request; + +import com.example.artist.vo.ArtistApiType; +import com.example.artist.vo.ArtistGenderApiType; +import java.util.List; +import java.util.UUID; +import lombok.Builder; +import org.example.dto.artist.request.ArtistFilterTotalCountDomainRequest; + +@Builder +public record ArtistFilterTotalCountServiceRequest( + List artistGenderApiTypes, + List artistApiTypes, + List genreIds, + UUID userId +) { + + public ArtistFilterTotalCountDomainRequest toDomainRequest(List artistIds) { + var artistGenders = artistGenderApiTypes.stream() + .map(ArtistGenderApiType::toDomainType) + .toList(); + var artistTypes = artistApiTypes.stream() + .map(ArtistApiType::toDomainType) + .toList(); + + return ArtistFilterTotalCountDomainRequest.builder() + .artistGenders(artistGenders) + .artistTypes(artistTypes) + .genreIds(genreIds) + .artistIds(artistIds) + .build(); + } +} diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistSubscriptionPaginationServiceRequest.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistSubscriptionPaginationServiceRequest.java index 3a6b5974..4356a525 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistSubscriptionPaginationServiceRequest.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistSubscriptionPaginationServiceRequest.java @@ -1,13 +1,16 @@ package com.example.artist.service.dto.request; import com.example.artist.vo.ArtistSortStandardApiType; +import com.example.vo.SubscriptionStatusApiType; import java.util.List; import java.util.UUID; import lombok.Builder; +import org.example.dto.artist.request.ArtistFilterDomain; import org.example.dto.artist.request.ArtistPaginationDomainRequest; @Builder public record ArtistSubscriptionPaginationServiceRequest( + SubscriptionStatusApiType subscriptionStatusApiType, int size, ArtistSortStandardApiType sortStandard, UUID cursor, @@ -16,10 +19,12 @@ public record ArtistSubscriptionPaginationServiceRequest( public ArtistPaginationDomainRequest toDomainRequest(List artistIds) { return ArtistPaginationDomainRequest.builder() + .subscriptionStatus(subscriptionStatusApiType.toDomainType()) .size(size) .sortStandard(sortStandard.toDomainType()) .artistIds(artistIds) .cursor(cursor) + .artistFilterDomain(ArtistFilterDomain.defaultArtistFilterDomain()) .build(); } } diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistUnsubscriptionPaginationServiceRequest.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistUnsubscriptionPaginationServiceRequest.java new file mode 100644 index 00000000..4959802d --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/request/ArtistUnsubscriptionPaginationServiceRequest.java @@ -0,0 +1,48 @@ +package com.example.artist.service.dto.request; + +import com.example.artist.vo.ArtistApiType; +import com.example.artist.vo.ArtistGenderApiType; +import com.example.artist.vo.ArtistSortStandardApiType; +import com.example.vo.SubscriptionStatusApiType; +import java.util.List; +import java.util.UUID; +import lombok.Builder; +import org.example.dto.artist.request.ArtistFilterDomain; +import org.example.dto.artist.request.ArtistPaginationDomainRequest; + +@Builder +public record ArtistUnsubscriptionPaginationServiceRequest( + SubscriptionStatusApiType subscriptionStatusApiType, + ArtistSortStandardApiType sortStandard, + List artistGenderApiTypes, + List artistApiTypes, + List genreIds, + UUID userId, + UUID cursor, + int size +) { + + public ArtistPaginationDomainRequest toDomainRequest(List artistIds) { + var artistGenders = artistGenderApiTypes.stream() + .map(ArtistGenderApiType::toDomainType) + .toList(); + var artistTypes = artistApiTypes.stream() + .map(ArtistApiType::toDomainType) + .toList(); + + ArtistFilterDomain artistFilterDomain = ArtistFilterDomain.builder() + .artistGenders(artistGenders) + .artistTypes(artistTypes) + .genreIds(genreIds) + .build(); + + return ArtistPaginationDomainRequest.builder() + .subscriptionStatus(subscriptionStatusApiType.toDomainType()) + .sortStandard(sortStandard.toDomainType()) + .artistIds(artistIds) + .cursor(cursor) + .size(size) + .artistFilterDomain(artistFilterDomain) + .build(); + } +} diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistDetailServiceResponse.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistDetailServiceResponse.java index 5ef976e2..adaa4e1d 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistDetailServiceResponse.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistDetailServiceResponse.java @@ -4,7 +4,7 @@ import com.example.artist.vo.ArtistGenderApiType; import java.util.List; import java.util.UUID; -import org.example.dto.artist.response.ArtistDetailResponse; +import org.example.dto.artist.response.ArtistDetailDomainResponse; public record ArtistDetailServiceResponse( UUID id, @@ -17,7 +17,7 @@ public record ArtistDetailServiceResponse( List genreNames ) { - public ArtistDetailServiceResponse(ArtistDetailResponse artistDetailResponse) { + public ArtistDetailServiceResponse(ArtistDetailDomainResponse artistDetailResponse) { this( artistDetailResponse.id(), artistDetailResponse.koreanName(), diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistFilterTotalCountServiceResponse.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistFilterTotalCountServiceResponse.java new file mode 100644 index 00000000..6e0b52f2 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistFilterTotalCountServiceResponse.java @@ -0,0 +1,19 @@ +package com.example.artist.service.dto.response; + +import org.example.dto.artist.response.ArtistFilterTotalCountDomainResponse; + +public record ArtistFilterTotalCountServiceResponse( + int totalCount +) { + + public static ArtistFilterTotalCountServiceResponse noneTotalCount() { + return new ArtistFilterTotalCountServiceResponse(0); + } + + public ArtistFilterTotalCountServiceResponse(ArtistFilterTotalCountDomainResponse response) { + this( + response.totalCount() + ); + } + +} diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistKoreanNameServiceResponse.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistKoreanNameServiceResponse.java index 3a6771a8..b66619c2 100644 --- a/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistKoreanNameServiceResponse.java +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistKoreanNameServiceResponse.java @@ -1,13 +1,13 @@ package com.example.artist.service.dto.response; import java.util.UUID; -import org.example.dto.artist.response.ArtistKoreanNameResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; public record ArtistKoreanNameServiceResponse( UUID id, String koreanName ) { - public ArtistKoreanNameServiceResponse(ArtistKoreanNameResponse artistKoreanNameResponse) { + public ArtistKoreanNameServiceResponse(ArtistKoreanNameDomainResponse artistKoreanNameResponse) { this( artistKoreanNameResponse.id(), artistKoreanNameResponse.koreanName() diff --git a/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistUnsubscriptionPaginationServiceResponse.java b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistUnsubscriptionPaginationServiceResponse.java new file mode 100644 index 00000000..897fcfc5 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/artist/service/dto/response/ArtistUnsubscriptionPaginationServiceResponse.java @@ -0,0 +1,13 @@ +package com.example.artist.service.dto.response; + +import com.example.artist.service.dto.param.ArtistSubscriptionPaginationServiceParam; +import java.util.List; +import lombok.Builder; + +@Builder +public record ArtistUnsubscriptionPaginationServiceResponse( + boolean hasNext, + List data +) { + +} diff --git a/app/api/show-api/src/main/java/com/example/artist/vo/ArtistApiType.java b/app/api/show-api/src/main/java/com/example/artist/vo/ArtistApiType.java index d38e6e71..282db0c5 100644 --- a/app/api/show-api/src/main/java/com/example/artist/vo/ArtistApiType.java +++ b/app/api/show-api/src/main/java/com/example/artist/vo/ArtistApiType.java @@ -1,5 +1,14 @@ package com.example.artist.vo; +import org.example.vo.ArtistType; + public enum ArtistApiType { - SOLO, GROUP + SOLO, GROUP; + + public ArtistType toDomainType() { + return switch (this) { + case SOLO -> ArtistType.SOLO; + case GROUP -> ArtistType.GROUP; + }; + } } diff --git a/app/api/show-api/src/main/java/com/example/artist/vo/ArtistGenderApiType.java b/app/api/show-api/src/main/java/com/example/artist/vo/ArtistGenderApiType.java index dba16ccd..f563d765 100644 --- a/app/api/show-api/src/main/java/com/example/artist/vo/ArtistGenderApiType.java +++ b/app/api/show-api/src/main/java/com/example/artist/vo/ArtistGenderApiType.java @@ -1,5 +1,15 @@ package com.example.artist.vo; +import org.example.vo.ArtistGender; + public enum ArtistGenderApiType { - MAN, WOMAN, MIXED + MAN, WOMAN, MIXED; + + public ArtistGender toDomainType() { + return switch (this) { + case MAN -> ArtistGender.MAN; + case WOMAN -> ArtistGender.WOMAN; + case MIXED -> ArtistGender.MIXED; + }; + } } diff --git a/app/api/show-api/src/main/java/com/example/genre/controller/GenreController.java b/app/api/show-api/src/main/java/com/example/genre/controller/GenreController.java index 5db88654..ef33506d 100644 --- a/app/api/show-api/src/main/java/com/example/genre/controller/GenreController.java +++ b/app/api/show-api/src/main/java/com/example/genre/controller/GenreController.java @@ -1,20 +1,17 @@ package com.example.genre.controller; import com.example.genre.controller.dto.param.GenreSubscriptionPaginationApiParam; -import com.example.genre.controller.dto.request.GenrePaginationApiRequest; +import com.example.genre.controller.dto.param.GenreUnsubscriptionPaginationApiParam; import com.example.genre.controller.dto.request.GenreSubscriptionApiRequest; import com.example.genre.controller.dto.request.GenreSubscriptionPaginationApiRequest; import com.example.genre.controller.dto.request.GenreUnsubscriptionApiRequest; -import com.example.genre.controller.dto.response.GenrePaginationApiResponse; -import com.example.genre.controller.dto.response.GenreSimpleApiResponse; +import com.example.genre.controller.dto.request.GenreUnsubscriptionPaginationApiRequest; import com.example.genre.controller.dto.response.GenreSubscriptionApiResponse; import com.example.genre.controller.dto.response.GenreUnsubscriptionApiResponse; import com.example.genre.service.GenreService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import java.util.List; -import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.response.PaginationApiResponse; import org.example.security.dto.AuthenticatedUser; @@ -25,7 +22,6 @@ import org.springframework.web.bind.annotation.PostMapping; 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; @RestController @@ -37,32 +33,21 @@ public class GenreController { private final GenreService genreService; @GetMapping - @Operation(summary = "장르 목록 조회") - public ResponseEntity getGenres( - @RequestParam(required = false) GenrePaginationApiRequest param + @Operation(summary = "구독하지 않은 장르 목록 조회") + public ResponseEntity> getUnsubscribedGenres( + @AuthenticationPrincipal AuthenticatedUser user, + @ParameterObject GenreUnsubscriptionPaginationApiRequest request ) { - String image = "https://thumb.mtstarnews.com/06/2023/06/2023062914274537673_1.jpg"; + var response = genreService.findGenreUnSubscriptions(request.toServiceRequest(user.userId())); + var data = response.data().stream() + .map(GenreUnsubscriptionPaginationApiParam::new) + .toList(); + return ResponseEntity.ok( - new GenrePaginationApiResponse( - List.of( - new GenreSimpleApiResponse( - UUID.randomUUID(), - "힙합", - image - ), - new GenreSimpleApiResponse( - UUID.randomUUID(), - "발라드", - image - ), - new GenreSimpleApiResponse( - UUID.randomUUID(), - "시티팝", - image - ) - ), - false - ) + PaginationApiResponse.builder() + .hasNext(response.hasNext()) + .data(data) + .build() ); } diff --git a/app/api/show-api/src/main/java/com/example/genre/controller/dto/param/GenreUnsubscriptionPaginationApiParam.java b/app/api/show-api/src/main/java/com/example/genre/controller/dto/param/GenreUnsubscriptionPaginationApiParam.java new file mode 100644 index 00000000..5e9f0451 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/genre/controller/dto/param/GenreUnsubscriptionPaginationApiParam.java @@ -0,0 +1,19 @@ +package com.example.genre.controller.dto.param; + +import com.example.genre.service.dto.param.GenreUnsubscriptionPaginationServiceParam; +import java.util.UUID; + +public record GenreUnsubscriptionPaginationApiParam( + UUID id, + String name +) { + + public GenreUnsubscriptionPaginationApiParam( + GenreUnsubscriptionPaginationServiceParam response + ) { + this( + response.id(), + response.name() + ); + } +} diff --git a/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreSubscriptionPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreSubscriptionPaginationApiRequest.java index 8e13708d..daad5791 100644 --- a/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreSubscriptionPaginationApiRequest.java +++ b/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreSubscriptionPaginationApiRequest.java @@ -1,6 +1,7 @@ package com.example.genre.controller.dto.request; import com.example.genre.service.dto.request.GenreSubscriptionPaginationServiceRequest; +import com.example.vo.SubscriptionStatusApiType; import io.swagger.v3.oas.annotations.Parameter; import java.util.UUID; @@ -14,6 +15,7 @@ public record GenreSubscriptionPaginationApiRequest( public GenreSubscriptionPaginationServiceRequest toServiceRequest(UUID userId) { return GenreSubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.SUBSCRIBED) .cursor(cursor) .size(size) .userId(userId) diff --git a/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreUnsubscriptionPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreUnsubscriptionPaginationApiRequest.java new file mode 100644 index 00000000..ef3e0476 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/genre/controller/dto/request/GenreUnsubscriptionPaginationApiRequest.java @@ -0,0 +1,24 @@ +package com.example.genre.controller.dto.request; + +import com.example.genre.service.dto.request.GenreUnsubscriptionPaginationServiceRequest; +import com.example.vo.SubscriptionStatusApiType; +import io.swagger.v3.oas.annotations.Parameter; +import java.util.UUID; + +public record GenreUnsubscriptionPaginationApiRequest( + @Parameter(description = "이전 페이지네이션 마지막 데이터의 ID / 최초 조회라면 null") + UUID cursor, + + @Parameter(description = "조회하는 데이터 개수", required = true) + int size +) { + + public GenreUnsubscriptionPaginationServiceRequest toServiceRequest(UUID userId) { + return GenreUnsubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.UNSUBSCRIBED) + .cursor(cursor) + .size(size) + .userId(userId) + .build(); + } +} diff --git a/app/domain/show-domain/src/main/java/org/example/error/GenreError.java b/app/api/show-api/src/main/java/com/example/genre/error/GenreError.java similarity index 90% rename from app/domain/show-domain/src/main/java/org/example/error/GenreError.java rename to app/api/show-api/src/main/java/com/example/genre/error/GenreError.java index ddd699cb..616957d5 100644 --- a/app/domain/show-domain/src/main/java/org/example/error/GenreError.java +++ b/app/api/show-api/src/main/java/com/example/genre/error/GenreError.java @@ -1,9 +1,9 @@ -package org.example.error; +package com.example.genre.error; import org.example.exception.BusinessError; public enum GenreError implements BusinessError { - ENTITY_NOT_FOUND_ERROR { + ENTITY_NOT_FOUND { @Override public int getHttpStatus() { return 404; diff --git a/app/api/show-api/src/main/java/com/example/genre/service/GenreAdminService.java b/app/api/show-api/src/main/java/com/example/genre/service/GenreAdminService.java index 0d24955d..5ed4d06a 100644 --- a/app/api/show-api/src/main/java/com/example/genre/service/GenreAdminService.java +++ b/app/api/show-api/src/main/java/com/example/genre/service/GenreAdminService.java @@ -1,12 +1,15 @@ package com.example.genre.service; +import com.example.genre.error.GenreError; import com.example.genre.service.dto.request.GenreCreateServiceRequest; import com.example.genre.service.dto.request.GenreUpdateServiceRequest; import com.example.genre.service.dto.response.GenreNameServiceResponse; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.entity.genre.Genre; +import org.example.exception.BusinessException; import org.example.usecase.genre.GenreUseCase; import org.springframework.stereotype.Service; @@ -29,15 +32,29 @@ public List findAllGenres() { } public void updateGenre(UUID id, GenreUpdateServiceRequest genreUpdateServiceRequest) { - genreUseCase.updateGenre(id, genreUpdateServiceRequest.name()); + try { + genreUseCase.updateGenre(id, genreUpdateServiceRequest.name()); + } catch (NoSuchElementException e) { + throw new BusinessException(GenreError.ENTITY_NOT_FOUND); + } } public void deleteGenre(UUID id) { - genreUseCase.deleteGenre(id); + try { + genreUseCase.deleteGenre(id); + } catch (NoSuchElementException e) { + throw new BusinessException(GenreError.ENTITY_NOT_FOUND); + } } public GenreNameServiceResponse findGenreById(UUID id) { - Genre genre = genreUseCase.findGenreById(id); + Genre genre; + try { + genre = genreUseCase.findGenreById(id); + } catch (NoSuchElementException e) { + throw new BusinessException(GenreError.ENTITY_NOT_FOUND); + } + return new GenreNameServiceResponse(genre.getId(), genre.getName()); } } diff --git a/app/api/show-api/src/main/java/com/example/genre/service/GenreService.java b/app/api/show-api/src/main/java/com/example/genre/service/GenreService.java index 6cabb467..8d341e3c 100644 --- a/app/api/show-api/src/main/java/com/example/genre/service/GenreService.java +++ b/app/api/show-api/src/main/java/com/example/genre/service/GenreService.java @@ -1,15 +1,17 @@ package com.example.genre.service; import com.example.genre.service.dto.param.GenreSubscriptionPaginationServiceParam; +import com.example.genre.service.dto.param.GenreUnsubscriptionPaginationServiceParam; import com.example.genre.service.dto.request.GenreSubscriptionPaginationServiceRequest; import com.example.genre.service.dto.request.GenreSubscriptionServiceRequest; +import com.example.genre.service.dto.request.GenreUnsubscriptionPaginationServiceRequest; import com.example.genre.service.dto.request.GenreUnsubscriptionServiceRequest; import com.example.genre.service.dto.response.GenreSubscriptionServiceResponse; import com.example.genre.service.dto.response.GenreUnsubscriptionServiceResponse; import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.example.dto.genre.response.GenreSubscriptionPaginationDomainResponse; +import org.example.dto.genre.response.GenrePaginationDomainResponse; import org.example.dto.response.PaginationServiceResponse; import org.example.entity.GenreSubscription; import org.example.entity.genre.Genre; @@ -45,9 +47,12 @@ public GenreSubscriptionServiceResponse subscribe(GenreSubscriptionServiceReques } public GenreUnsubscriptionServiceResponse unsubscribe( - GenreUnsubscriptionServiceRequest request) { + GenreUnsubscriptionServiceRequest request + ) { List unsubscriptionGenres = genreSubscriptionUseCase.unsubscribe( - request.genreIds(), request.userId()); + request.genreIds(), + request.userId() + ); return GenreUnsubscriptionServiceResponse.builder() .successUnsubscriptionGenreIds( @@ -59,18 +64,15 @@ public GenreUnsubscriptionServiceResponse unsubscribe( } public PaginationServiceResponse findGenreSubscriptions( - GenreSubscriptionPaginationServiceRequest request) { - List subscriptions = genreSubscriptionUseCase.findSubscriptions( - request.userId()); - List subscriptionGenreIds = subscriptions.stream() - .map(GenreSubscription::getGenreId) - .toList(); + GenreSubscriptionPaginationServiceRequest request + ) { + List subscriptionGenreIds = getSubscriptionGenreIds(request.userId()); if (subscriptionGenreIds.isEmpty()) { return PaginationServiceResponse.of(List.of(), false); } - GenreSubscriptionPaginationDomainResponse response = genreUseCase.findGenreSubscriptionsWithCursorPagination( + GenrePaginationDomainResponse response = genreUseCase.findGenreWithCursorPagination( request.toDomainRequest(subscriptionGenreIds)); List data = response.data().stream() .map(GenreSubscriptionPaginationServiceParam::new) @@ -78,4 +80,25 @@ public PaginationServiceResponse findGe return PaginationServiceResponse.of(data, response.hasNext()); } + + public PaginationServiceResponse findGenreUnSubscriptions( + GenreUnsubscriptionPaginationServiceRequest request + ) { + List subscriptionGenreIds = getSubscriptionGenreIds(request.userId()); + + GenrePaginationDomainResponse response = genreUseCase.findGenreWithCursorPagination( + request.toDomainRequest(subscriptionGenreIds)); + List data = response.data().stream() + .map(GenreUnsubscriptionPaginationServiceParam::new) + .toList(); + return PaginationServiceResponse.of(data, response.hasNext()); + } + + private List getSubscriptionGenreIds(UUID userId) { + List subscriptions = genreSubscriptionUseCase.findSubscriptions(userId); + + return subscriptions.stream() + .map(GenreSubscription::getGenreId) + .toList(); + } } diff --git a/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreSubscriptionPaginationServiceParam.java b/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreSubscriptionPaginationServiceParam.java index dbc5b12a..bde8df67 100644 --- a/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreSubscriptionPaginationServiceParam.java +++ b/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreSubscriptionPaginationServiceParam.java @@ -1,14 +1,14 @@ package com.example.genre.service.dto.param; import java.util.UUID; -import org.example.dto.genre.response.GenreSubscriptionDomainResponse; +import org.example.dto.genre.response.GenreDomainResponse; public record GenreSubscriptionPaginationServiceParam( UUID id, String name ) { - public GenreSubscriptionPaginationServiceParam(GenreSubscriptionDomainResponse response) { + public GenreSubscriptionPaginationServiceParam(GenreDomainResponse response) { this( response.id(), response.name() diff --git a/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreUnsubscriptionPaginationServiceParam.java b/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreUnsubscriptionPaginationServiceParam.java new file mode 100644 index 00000000..e67ddd61 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/genre/service/dto/param/GenreUnsubscriptionPaginationServiceParam.java @@ -0,0 +1,17 @@ +package com.example.genre.service.dto.param; + +import java.util.UUID; +import org.example.dto.genre.response.GenreDomainResponse; + +public record GenreUnsubscriptionPaginationServiceParam( + UUID id, + String name +) { + + public GenreUnsubscriptionPaginationServiceParam(GenreDomainResponse response) { + this( + response.id(), + response.name() + ); + } +} diff --git a/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreSubscriptionPaginationServiceRequest.java b/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreSubscriptionPaginationServiceRequest.java index 41bbe57c..4657a11d 100644 --- a/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreSubscriptionPaginationServiceRequest.java +++ b/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreSubscriptionPaginationServiceRequest.java @@ -1,23 +1,25 @@ package com.example.genre.service.dto.request; +import com.example.vo.SubscriptionStatusApiType; import java.util.List; import java.util.UUID; import lombok.Builder; -import org.example.dto.genre.request.GenreSubscriptionPaginationDomainRequest; +import org.example.dto.genre.request.GenrePaginationDomainRequest; @Builder public record GenreSubscriptionPaginationServiceRequest( + SubscriptionStatusApiType subscriptionStatusApiType, UUID cursor, int size, UUID userId ) { - public GenreSubscriptionPaginationDomainRequest toDomainRequest(List genreIds) { - return GenreSubscriptionPaginationDomainRequest.builder() + public GenrePaginationDomainRequest toDomainRequest(List genreIds) { + return GenrePaginationDomainRequest.builder() + .subscriptionStatus(subscriptionStatusApiType.toDomainType()) .cursor(cursor) .size(size) - .userId(userId) .genreIds(genreIds) .build(); } diff --git a/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreUnsubscriptionPaginationServiceRequest.java b/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreUnsubscriptionPaginationServiceRequest.java new file mode 100644 index 00000000..2ac06cb1 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/genre/service/dto/request/GenreUnsubscriptionPaginationServiceRequest.java @@ -0,0 +1,26 @@ +package com.example.genre.service.dto.request; + +import com.example.vo.SubscriptionStatusApiType; +import java.util.List; +import java.util.UUID; +import lombok.Builder; +import org.example.dto.genre.request.GenrePaginationDomainRequest; + +@Builder +public record GenreUnsubscriptionPaginationServiceRequest( + SubscriptionStatusApiType subscriptionStatusApiType, + UUID cursor, + int size, + UUID userId +) { + + + public GenrePaginationDomainRequest toDomainRequest(List genreIds) { + return GenrePaginationDomainRequest.builder() + .subscriptionStatus(subscriptionStatusApiType.toDomainType()) + .cursor(cursor) + .size(size) + .genreIds(genreIds) + .build(); + } +} diff --git a/app/api/show-api/src/main/java/com/example/genre/service/dto/response/GenreNameServiceResponse.java b/app/api/show-api/src/main/java/com/example/genre/service/dto/response/GenreNameServiceResponse.java index b932f97a..53cb385d 100644 --- a/app/api/show-api/src/main/java/com/example/genre/service/dto/response/GenreNameServiceResponse.java +++ b/app/api/show-api/src/main/java/com/example/genre/service/dto/response/GenreNameServiceResponse.java @@ -1,14 +1,14 @@ package com.example.genre.service.dto.response; import java.util.UUID; -import org.example.dto.genre.response.GenreNameResponse; +import org.example.dto.genre.response.GenreNameDomainResponse; public record GenreNameServiceResponse( UUID id, String name ) { - public GenreNameServiceResponse(GenreNameResponse genreNameResponse) { + public GenreNameServiceResponse(GenreNameDomainResponse genreNameResponse) { this( genreNameResponse.id(), genreNameResponse.name() diff --git a/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowCreateApiForm.java b/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowCreateApiForm.java index 148e087a..f52891e9 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowCreateApiForm.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowCreateApiForm.java @@ -1,6 +1,5 @@ package com.example.show.controller.dto.request; -import com.example.show.controller.dto.response.SeatInfoApiResponse; import com.example.show.controller.vo.TicketingApiType; import com.example.show.service.dto.request.ShowCreateServiceRequest; import jakarta.validation.constraints.NotBlank; @@ -66,19 +65,18 @@ public ShowCreateServiceRequest toServiceRequest() { .endDate(endDate) .location(location) .post(post) - .seatInfoApiResponse(getSeatInfoApiResponse()) + .priceInformation(getPriceInformation()) .showTicketingSites(getTicketingSitesApiResponse()) - .showTicketingDates(getTicketingDatesApiResponse()) + .ticketingTimes(getTicketingDatesApiResponse()) .artistIds(artistIds) .genreIds(genreIds) .build(); } - private SeatInfoApiResponse getSeatInfoApiResponse() { - Map priceInformation = IntStream.range(0, seatTypes.size()) + private Map getPriceInformation() { + return IntStream.range(0, seatTypes.size()) .boxed() .collect(Collectors.toMap(seatTypes::get, pricesPerSeatType::get)); - return new SeatInfoApiResponse(priceInformation); } private Map getTicketingSitesApiResponse() { diff --git a/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowSearchPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowSearchPaginationApiRequest.java index f5fab0ae..4109a743 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowSearchPaginationApiRequest.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowSearchPaginationApiRequest.java @@ -8,10 +8,10 @@ public record ShowSearchPaginationApiRequest( @Parameter(description = "이전 페이지네이션 마지막 데이터의 ID / 최초 조회라면 null") UUID cursor, - @Parameter(description = "조회하는 데이터 개수") + @Parameter(description = "조회하는 데이터 개수", required = true) int size, - @Parameter(description = "검색어") + @Parameter(description = "검색어", required = true) String search ) { diff --git a/app/api/show-api/src/main/java/com/example/show/controller/vo/TicketingApiType.java b/app/api/show-api/src/main/java/com/example/show/controller/vo/TicketingApiType.java index 8f93afa2..e5a69cc0 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/vo/TicketingApiType.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/vo/TicketingApiType.java @@ -16,7 +16,7 @@ public static TicketingApiType from(TicketingType ticketingType) { }; } - public TicketingType toTicketingType() { + public TicketingType toDomainType() { return switch (this) { case PRE -> TicketingType.PRE; case NORMAL -> TicketingType.NORMAL; diff --git a/app/api/show-api/src/main/java/com/example/show/service/error/ShowError.java b/app/api/show-api/src/main/java/com/example/show/error/ShowError.java similarity index 90% rename from app/api/show-api/src/main/java/com/example/show/service/error/ShowError.java rename to app/api/show-api/src/main/java/com/example/show/error/ShowError.java index 44c0b67a..e40be098 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/error/ShowError.java +++ b/app/api/show-api/src/main/java/com/example/show/error/ShowError.java @@ -1,10 +1,10 @@ -package com.example.show.service.error; +package com.example.show.error; import org.example.exception.BusinessError; public enum ShowError implements BusinessError { - DETAIL_NOT_FOUND { + ENTITY_NOT_FOUND { @Override public int getHttpStatus() { return 404; diff --git a/app/api/show-api/src/main/java/com/example/show/service/ShowAdminService.java b/app/api/show-api/src/main/java/com/example/show/service/ShowAdminService.java index b6f9fda6..1c12d0eb 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/ShowAdminService.java +++ b/app/api/show-api/src/main/java/com/example/show/service/ShowAdminService.java @@ -2,14 +2,17 @@ import com.example.component.FileUploadComponent; +import com.example.show.error.ShowError; import com.example.show.service.dto.request.ShowCreateServiceRequest; import com.example.show.service.dto.request.ShowUpdateServiceRequest; import com.example.show.service.dto.response.ShowInfoServiceResponse; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.show.response.ShowInfoDomainResponse; import org.example.entity.show.Show; +import org.example.exception.BusinessException; import org.example.usecase.show.ShowUseCase; import org.springframework.stereotype.Service; @@ -29,14 +32,20 @@ public void save(ShowCreateServiceRequest showCreateServiceRequest) { } public List findAllShowInfos() { - List showInfoDomainRespons = showUseCase.findAllShowInfos(); - return showInfoDomainRespons.stream() + List showInfoDomainResponse = showUseCase.findAllShowInfos(); + return showInfoDomainResponse.stream() .map(ShowInfoServiceResponse::new) .toList(); } public ShowInfoServiceResponse findShowInfo(UUID id) { - ShowInfoDomainResponse showInfoDomainResponse = showUseCase.findShowInfo(id); + ShowInfoDomainResponse showInfoDomainResponse; + try { + showInfoDomainResponse = showUseCase.findShowInfo(id); + } catch (NoSuchElementException e) { + throw new BusinessException(ShowError.ENTITY_NOT_FOUND); + } + return new ShowInfoServiceResponse(showInfoDomainResponse); } @@ -44,11 +53,23 @@ public void updateShow(UUID id, ShowUpdateServiceRequest showUpdateServiceReques String imageUrl = fileUploadComponent.uploadFile("show", showUpdateServiceRequest.post()); Show show = showUpdateServiceRequest.toShowWithImageUrl(imageUrl); - showUseCase.updateShow(id, show, showUpdateServiceRequest.artistIds(), - showUpdateServiceRequest.genreIds()); + try { + showUseCase.updateShow( + id, + show, + showUpdateServiceRequest.artistIds(), + showUpdateServiceRequest.genreIds() + ); + } catch (NoSuchElementException e) { + throw new BusinessException(ShowError.ENTITY_NOT_FOUND); + } } public void deleteShow(UUID id) { - showUseCase.deleteShow(id); + try { + showUseCase.deleteShow(id); + } catch (NoSuchElementException e) { + throw new BusinessException(ShowError.ENTITY_NOT_FOUND); + } } } diff --git a/app/api/show-api/src/main/java/com/example/show/service/ShowService.java b/app/api/show-api/src/main/java/com/example/show/service/ShowService.java index b8da15a0..5495c185 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/ShowService.java +++ b/app/api/show-api/src/main/java/com/example/show/service/ShowService.java @@ -1,9 +1,9 @@ package com.example.show.service; +import com.example.show.error.ShowError; import com.example.show.service.dto.param.ShowSearchPaginationServiceParam; import com.example.show.service.dto.request.ShowSearchPaginationServiceRequest; import com.example.show.service.dto.response.ShowDetailServiceResponse; -import com.example.show.service.error.ShowError; import java.util.List; import java.util.NoSuchElementException; import java.util.UUID; @@ -25,7 +25,7 @@ public ShowDetailServiceResponse getShow(UUID id) { try { showDetail = showUseCase.findShowDetail(id); } catch (NoSuchElementException e) { - throw new BusinessException(ShowError.DETAIL_NOT_FOUND); + throw new BusinessException(ShowError.ENTITY_NOT_FOUND); } return ShowDetailServiceResponse.from(showDetail); diff --git a/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowCreateServiceRequest.java b/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowCreateServiceRequest.java index 32c9c769..681766be 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowCreateServiceRequest.java +++ b/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowCreateServiceRequest.java @@ -1,18 +1,15 @@ package com.example.show.service.dto.request; -import com.example.show.controller.dto.response.SeatInfoApiResponse; import com.example.show.controller.vo.TicketingApiType; import java.time.LocalDate; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; import lombok.Builder; import org.example.dto.show.request.ShowCreationDomainRequest; -import org.example.entity.show.Show; import org.example.entity.show.info.SeatPrices; +import org.example.entity.show.info.ShowTicketingTimes; import org.example.entity.show.info.TicketingSites; -import org.example.vo.TicketingType; import org.springframework.web.multipart.MultipartFile; @Builder @@ -24,29 +21,13 @@ public record ShowCreateServiceRequest( LocalDate endDate, String location, MultipartFile post, - SeatInfoApiResponse seatInfoApiResponse, + Map priceInformation, Map showTicketingSites, - Map showTicketingDates, + Map ticketingTimes, List artistIds, List genreIds ) { - public Show toShowWithImageUrl(String imageUrl) { - TicketingSites ticketingSites = new TicketingSites(); - showTicketingSites.forEach(ticketingSites::saveTicketingSite); - - return Show.builder() - .title(title) - .content(content) - .startDate(startDate) - .endDate(endDate) - .location(location) - .image(imageUrl) - .seatPrices(getSeatPrice()) - .ticketingSites(ticketingSites) - .build(); - } - public ShowCreationDomainRequest toDomainRequest(String imageURL) { return ShowCreationDomainRequest.builder() .title(title) @@ -57,7 +38,7 @@ public ShowCreationDomainRequest toDomainRequest(String imageURL) { .posterImageURL(imageURL) .showSeats(getSeatPrice()) .showTicketingSites(getTicketingSites()) - .showTicketingDates(getTicketingDates()) + .showTicketingTimes(getTicketingTimes()) .artistIds(artistIds) .genreIds(genreIds) .build(); @@ -65,7 +46,7 @@ public ShowCreationDomainRequest toDomainRequest(String imageURL) { private SeatPrices getSeatPrice() { SeatPrices seatPrices = new SeatPrices(); - seatInfoApiResponse.priceInformation().forEach(seatPrices::savePriceInformation); + priceInformation.forEach(seatPrices::savePriceInformation); return seatPrices; } @@ -77,13 +58,15 @@ private TicketingSites getTicketingSites() { return ticketingSites; } - private Map getTicketingDates() { - return showTicketingDates.entrySet().stream() - .collect( - Collectors.toMap( - entry -> entry.getKey().toTicketingType(), - Map.Entry::getValue + private ShowTicketingTimes getTicketingTimes() { + ShowTicketingTimes showTicketingTimes = new ShowTicketingTimes(); + ticketingTimes.forEach( + (apiType, date) -> + showTicketingTimes.saveTicketingTimes( + apiType.toDomainType(), + date ) - ); + ); + return showTicketingTimes; } } diff --git a/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowInfoServiceResponse.java b/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowInfoServiceResponse.java index d6f48cdb..435df340 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowInfoServiceResponse.java +++ b/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowInfoServiceResponse.java @@ -9,8 +9,8 @@ import java.util.Set; import java.util.UUID; import lombok.Builder; -import org.example.dto.artist.response.ArtistKoreanNameResponse; -import org.example.dto.genre.response.GenreNameResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; +import org.example.dto.genre.response.GenreNameDomainResponse; import org.example.dto.show.response.ShowInfoDomainResponse; @Builder @@ -52,7 +52,7 @@ public ShowInfoServiceResponse(ShowInfoDomainResponse showInfo) { } private static List toArtistKoreanNameServiceResponses( - Set artistKoreanNameResponses) { + Set artistKoreanNameResponses) { return artistKoreanNameResponses .stream() .map(ArtistKoreanNameServiceResponse::new) @@ -60,7 +60,7 @@ private static List toArtistKoreanNameServiceRe } private static List toGenreNameServiceResponses( - Set genreNameResponses) { + Set genreNameResponses) { return genreNameResponses .stream() .map(GenreNameServiceResponse::new) diff --git a/app/api/show-api/src/main/java/com/example/vo/SubscriptionStatusApiType.java b/app/api/show-api/src/main/java/com/example/vo/SubscriptionStatusApiType.java new file mode 100644 index 00000000..4800c059 --- /dev/null +++ b/app/api/show-api/src/main/java/com/example/vo/SubscriptionStatusApiType.java @@ -0,0 +1,15 @@ +package com.example.vo; + +import org.example.vo.SubscriptionStatus; + +public enum SubscriptionStatusApiType { + SUBSCRIBED, + UNSUBSCRIBED; + + public SubscriptionStatus toDomainType() { + return switch (this) { + case SUBSCRIBED -> SubscriptionStatus.SUBSCRIBED; + case UNSUBSCRIBED -> SubscriptionStatus.UNSUBSCRIBED; + }; + } +} diff --git a/app/api/show-api/src/test/java/artist/fixture/dto/ArtistDtoFixture.java b/app/api/show-api/src/test/java/artist/fixture/dto/ArtistDtoFixture.java deleted file mode 100644 index dab10726..00000000 --- a/app/api/show-api/src/test/java/artist/fixture/dto/ArtistDtoFixture.java +++ /dev/null @@ -1,44 +0,0 @@ -package artist.fixture.dto; - -import com.example.artist.service.dto.request.ArtistCreateServiceRequest; -import com.example.artist.service.dto.request.ArtistUpdateServiceRequest; -import com.example.artist.vo.ArtistApiType; -import com.example.artist.vo.ArtistGenderApiType; -import java.util.List; -import java.util.UUID; -import org.springframework.mock.web.MockMultipartFile; - -public class ArtistDtoFixture { - - private static final MockMultipartFile image = new MockMultipartFile( - "image", - "test_image.jpg", - "image/jpeg", - "test posterImageURL content".getBytes() - ); - - - public static ArtistCreateServiceRequest artistCreateServiceRequest() { - return ArtistCreateServiceRequest.builder() - .koreanName("test_koreanName") - .englishName("test_englishName") - .image(image) - .country("test_country") - .artistGenderApiType(ArtistGenderApiType.MAN) - .artistApiType(ArtistApiType.SOLO) - .genreIds(List.of(UUID.randomUUID())) - .build(); - } - - public static ArtistUpdateServiceRequest artistUpdateServiceRequest() { - return ArtistUpdateServiceRequest.builder() - .koreanName("test_koreanName") - .englishName("test_englishName") - .image(image) - .country("test_country") - .artistGenderApiType(ArtistGenderApiType.MAN) - .artistApiType(ArtistApiType.SOLO) - .genreIds(List.of(UUID.randomUUID())) - .build(); - } -} diff --git a/app/api/show-api/src/test/java/artist/fixture/dto/ArtistRequestDtoFixture.java b/app/api/show-api/src/test/java/artist/fixture/dto/ArtistRequestDtoFixture.java new file mode 100644 index 00000000..d6872f6c --- /dev/null +++ b/app/api/show-api/src/test/java/artist/fixture/dto/ArtistRequestDtoFixture.java @@ -0,0 +1,106 @@ +package artist.fixture.dto; + +import com.example.artist.service.dto.request.ArtistCreateServiceRequest; +import com.example.artist.service.dto.request.ArtistFilterTotalCountServiceRequest; +import com.example.artist.service.dto.request.ArtistSearchPaginationServiceRequest; +import com.example.artist.service.dto.request.ArtistSubscriptionPaginationServiceRequest; +import com.example.artist.service.dto.request.ArtistUnsubscriptionPaginationServiceRequest; +import com.example.artist.service.dto.request.ArtistUpdateServiceRequest; +import com.example.artist.vo.ArtistApiType; +import com.example.artist.vo.ArtistGenderApiType; +import com.example.artist.vo.ArtistSortStandardApiType; +import com.example.vo.SubscriptionStatusApiType; +import java.util.List; +import java.util.UUID; +import org.springframework.mock.web.MockMultipartFile; + +public class ArtistRequestDtoFixture { + + private static final MockMultipartFile image = new MockMultipartFile( + "image", + "test_image.jpg", + "image/jpeg", + "test image content".getBytes() + ); + + + public static ArtistCreateServiceRequest artistCreateServiceRequest() { + return ArtistCreateServiceRequest.builder() + .koreanName("test_koreanName") + .englishName("test_englishName") + .image(image) + .country("test_country") + .artistGenderApiType(ArtistGenderApiType.MAN) + .artistApiType(ArtistApiType.SOLO) + .genreIds(List.of(UUID.randomUUID())) + .build(); + } + + public static ArtistUpdateServiceRequest artistUpdateServiceRequest() { + return ArtistUpdateServiceRequest.builder() + .koreanName("test_koreanName") + .englishName("test_englishName") + .image(image) + .country("test_country") + .artistGenderApiType(ArtistGenderApiType.MAN) + .artistApiType(ArtistApiType.SOLO) + .genreIds(List.of(UUID.randomUUID())) + .build(); + } + + public static ArtistSearchPaginationServiceRequest artistSearchPaginationServiceRequest( + int size, + String search + ) { + return ArtistSearchPaginationServiceRequest.builder() + .sortStandard(ArtistSortStandardApiType.ENGLISH_NAME_ASC) + .cursor(UUID.randomUUID()) + .size(size) + .search(search) + .build(); + } + + public static ArtistFilterTotalCountServiceRequest artistFilterTotalCountServiceRequest() { + return ArtistFilterTotalCountServiceRequest.builder() + .artistGenderApiTypes(getArtistGenderApiTypes()) + .artistApiTypes(getArtistApiTypes()) + .genreIds(List.of(UUID.randomUUID())) + .userId(UUID.randomUUID()) + .build(); + } + + public static ArtistSubscriptionPaginationServiceRequest artistSubscriptionPaginationServiceRequest( + int size + ) { + return ArtistSubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.SUBSCRIBED) + .size(size) + .sortStandard(ArtistSortStandardApiType.ENGLISH_NAME_ASC) + .cursor(UUID.randomUUID()) + .userId(UUID.randomUUID()) + .build(); + } + + public static ArtistUnsubscriptionPaginationServiceRequest artistUnsubscriptionPaginationServiceRequest( + int size + ) { + return ArtistUnsubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.UNSUBSCRIBED) + .sortStandard(ArtistSortStandardApiType.ENGLISH_NAME_ASC) + .artistGenderApiTypes(getArtistGenderApiTypes()) + .artistApiTypes(getArtistApiTypes()) + .genreIds(List.of(UUID.randomUUID())) + .userId(UUID.randomUUID()) + .cursor(UUID.randomUUID()) + .size(size) + .build(); + } + + private static List getArtistApiTypes() { + return List.of(ArtistApiType.GROUP, ArtistApiType.SOLO); + } + + private static List getArtistGenderApiTypes() { + return List.of(ArtistGenderApiType.MAN, ArtistGenderApiType.WOMAN); + } +} diff --git a/app/api/show-api/src/test/java/artist/fixture/dto/ArtistResponseDtoFixture.java b/app/api/show-api/src/test/java/artist/fixture/dto/ArtistResponseDtoFixture.java new file mode 100644 index 00000000..06e84278 --- /dev/null +++ b/app/api/show-api/src/test/java/artist/fixture/dto/ArtistResponseDtoFixture.java @@ -0,0 +1,47 @@ +package artist.fixture.dto; + +import java.util.List; +import java.util.UUID; +import java.util.stream.IntStream; +import org.example.dto.artist.response.ArtistFilterTotalCountDomainResponse; +import org.example.dto.artist.response.ArtistPaginationDomainResponse; +import org.example.dto.artist.response.ArtistSimpleDomainResponse; + +public class ArtistResponseDtoFixture { + + public static ArtistFilterTotalCountDomainResponse artistFilterTotalCountDomainResponse( + int totalCount + ) { + return new ArtistFilterTotalCountDomainResponse(totalCount); + } + + public static ArtistPaginationDomainResponse artistPaginationDomainResponse( + int size, + boolean hasNext + ) { + return ArtistPaginationDomainResponse.builder() + .data(artistSimpleDomainResponses(size)) + .hasNext(hasNext) + .build(); + } + + public static ArtistPaginationDomainResponse emptyDataArtistPaginationDomainResponse() { + return ArtistPaginationDomainResponse.builder() + .data(List.of()) + .hasNext(false) + .build(); + } + + public static List artistSimpleDomainResponses( + int size + ) { + return IntStream.range(0, size) + .mapToObj(i -> ArtistSimpleDomainResponse.builder() + .id(UUID.randomUUID()) + .koreanName(i + "testKoreanName") + .image(i + "testImage") + .englishName(i + "testEnglishName") + .build()) + .toList(); + } +} diff --git a/app/api/show-api/src/test/java/artist/service/ArtistAdminServiceTest.java b/app/api/show-api/src/test/java/artist/service/ArtistAdminServiceTest.java index 9d8461c9..de112222 100644 --- a/app/api/show-api/src/test/java/artist/service/ArtistAdminServiceTest.java +++ b/app/api/show-api/src/test/java/artist/service/ArtistAdminServiceTest.java @@ -8,7 +8,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import artist.fixture.dto.ArtistDtoFixture; +import artist.fixture.dto.ArtistRequestDtoFixture; import com.example.artist.service.ArtistAdminService; import com.example.artist.service.dto.request.ArtistCreateServiceRequest; import com.example.artist.service.dto.request.ArtistUpdateServiceRequest; @@ -31,7 +31,7 @@ class ArtistAdminServiceTest { @DisplayName("아티스트는 업로드된 이미지 URL과 함께 생성된다.") void artistCreateWithUploadedImageUrl() { //given - ArtistCreateServiceRequest artistCreateServiceRequest = ArtistDtoFixture.artistCreateServiceRequest(); + ArtistCreateServiceRequest artistCreateServiceRequest = ArtistRequestDtoFixture.artistCreateServiceRequest(); given( fileUploadComponent.uploadFile( "artist", @@ -50,7 +50,7 @@ void artistCreateWithUploadedImageUrl() { @DisplayName("아티스트는 업로드된 이미지 URL과 함께 업데이트 된다.") void artistUpdateWithUploadedImageUrl() { //given - ArtistUpdateServiceRequest artistUpdateServiceRequest = ArtistDtoFixture.artistUpdateServiceRequest(); + ArtistUpdateServiceRequest artistUpdateServiceRequest = ArtistRequestDtoFixture.artistUpdateServiceRequest(); UUID artistId = UUID.randomUUID(); given( fileUploadComponent.uploadFile( diff --git a/app/api/show-api/src/test/java/artist/service/ArtistServiceTest.java b/app/api/show-api/src/test/java/artist/service/ArtistServiceTest.java new file mode 100644 index 00000000..3a4fe8e8 --- /dev/null +++ b/app/api/show-api/src/test/java/artist/service/ArtistServiceTest.java @@ -0,0 +1,376 @@ +package artist.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import artist.fixture.dto.ArtistRequestDtoFixture; +import artist.fixture.dto.ArtistResponseDtoFixture; +import com.example.artist.service.ArtistService; +import com.example.artist.service.dto.request.ArtistSubscriptionServiceRequest; +import com.example.artist.service.dto.request.ArtistUnsubscriptionServiceRequest; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; +import org.assertj.core.api.SoftAssertions; +import org.example.entity.ArtistSubscription; +import org.example.entity.artist.Artist; +import org.example.fixture.ArtistSubscriptionFixture; +import org.example.fixture.domain.ArtistFixture; +import org.example.usecase.ArtistSubscriptionUseCase; +import org.example.usecase.artist.ArtistUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ArtistServiceTest { + + private final ArtistUseCase artistUseCase = mock(ArtistUseCase.class); + private final ArtistSubscriptionUseCase artistSubscriptionUseCase = mock( + ArtistSubscriptionUseCase.class + ); + private final ArtistService artistService = new ArtistService( + artistUseCase, + artistSubscriptionUseCase + ); + + @Test + @DisplayName("페이지네이션을 이용해 아티스트를 검색할 수 있다.") + void artistSearchWithPagination() { + //given + String search = "testArtistName"; + int size = 3; + boolean hasNext = true; + var request = ArtistRequestDtoFixture.artistSearchPaginationServiceRequest(size, search); + given( + artistUseCase.searchArtist(request.toDomainRequest()) + ).willReturn( + ArtistResponseDtoFixture.artistPaginationDomainResponse(size, hasNext) + ); + + //when + var result = artistService.searchArtist(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } + + @Test + @DisplayName("아티스트 검색 결과가 없으면 빈 리스트를 반환한다.") + void artistSearchEmptyResultWithPagination() { + //given + String search = "testArtistName"; + int size = 3; + var request = ArtistRequestDtoFixture.artistSearchPaginationServiceRequest(size, search); + given( + artistUseCase.searchArtist(request.toDomainRequest()) + ).willReturn( + ArtistResponseDtoFixture.emptyDataArtistPaginationDomainResponse() + ); + + //when + var result = artistService.searchArtist(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data()).isEmpty(); + soft.assertThat(result.hasNext()).isFalse(); + } + ); + } + + @Test + @DisplayName("페이지네이션을 이용해 아티스트를 필터링 할 수 있다.") + void artistFilterWithPagination() { + //given + int subscriptionCount = 2; + int size = 3; + boolean hasNext = true; + var request = ArtistRequestDtoFixture.artistUnsubscriptionPaginationServiceRequest(size); + var subscriptions = ArtistSubscriptionFixture.artistSubscriptions(subscriptionCount); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionArtistIds = subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + given( + artistUseCase.findAllArtistInCursorPagination( + request.toDomainRequest(subscriptionArtistIds)) + ).willReturn( + ArtistResponseDtoFixture.artistPaginationDomainResponse(size, hasNext) + ); + + //when + var result = artistService.findArtistUnsubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } + + @Test + @DisplayName("아티스트 필터링 결과가 없으면 빈 리스트를 반환한다.") + void artistFilterEmptyResultWithPagination() { + //given + int subscriptionCount = 2; + int size = 3; + var request = ArtistRequestDtoFixture.artistUnsubscriptionPaginationServiceRequest(size); + var subscriptions = ArtistSubscriptionFixture.artistSubscriptions(subscriptionCount); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionArtistIds = subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + given( + artistUseCase.findAllArtistInCursorPagination( + request.toDomainRequest(subscriptionArtistIds)) + ).willReturn( + ArtistResponseDtoFixture.emptyDataArtistPaginationDomainResponse() + ); + + //when + var result = artistService.findArtistUnsubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data()).isEmpty(); + soft.assertThat(result.hasNext()).isFalse(); + } + ); + } + + @Test + @DisplayName("아티스트 필터링한 총 개수를 반환한다.") + void artistFilterToTalCount() { + //given + var request = ArtistRequestDtoFixture.artistFilterTotalCountServiceRequest(); + var subscriptions = ArtistSubscriptionFixture.artistSubscriptions(2); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionArtistIds = subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + int totalCount = 3; + given( + artistUseCase.findFilterArtistTotalCount(request.toDomainRequest(subscriptionArtistIds)) + ).willReturn( + ArtistResponseDtoFixture.artistFilterTotalCountDomainResponse(totalCount) + ); + + //when + var result = artistService.filterArtistTotalCount(request); + + //then + assertThat(result.totalCount()).isEqualTo(totalCount); + } + + @Test + @DisplayName("아티스트 필터링 후 결과가 없다면 0을 반환한다.") + void artistFilterTotalCountZero() { + //given + var request = ArtistRequestDtoFixture.artistFilterTotalCountServiceRequest(); + var subscriptions = ArtistSubscriptionFixture.artistSubscriptions(2); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionArtistIds = subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + given( + artistUseCase.findFilterArtistTotalCount(request.toDomainRequest(subscriptionArtistIds)) + ).willThrow( + NoSuchElementException.class + ); + + //when + var result = artistService.filterArtistTotalCount(request); + + //then + assertThat(result.totalCount()).isZero(); + } + + @Test + @DisplayName("아티스트를 구독하면 구독 성공한 아티스트 ID들을 반환한다.") + void artistSubscribe() { + //given + List artistsId = List.of(UUID.randomUUID(), UUID.randomUUID()); + UUID userId = UUID.randomUUID(); + var request = new ArtistSubscriptionServiceRequest(artistsId, userId); + var existArtistsInRequest = ArtistFixture.manSoloArtists(3); + given( + artistUseCase.findAllArtistInIds(request.artistIds()) + ).willReturn( + existArtistsInRequest + ); + + var existArtistIdsInRequest = existArtistsInRequest.stream() + .map(Artist::getId) + .toList(); + int artistSubscriptionCount = 2; + given( + artistSubscriptionUseCase.subscribe(existArtistIdsInRequest, userId) + ).willReturn( + ArtistSubscriptionFixture.artistSubscriptions(artistSubscriptionCount) + ); + + //when + var result = artistService.subscribe(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result).isNotNull(); + soft.assertThat(result.successSubscriptionArtistIds().size()) + .isEqualTo(artistSubscriptionCount); + } + ); + } + + @Test + @DisplayName("아티스트를 구독 취소하면 구독 취소 성공한 아티스트 ID들을 반환한다.") + void artistUnsubscribe() { + //given + List artistsId = List.of(UUID.randomUUID(), UUID.randomUUID()); + UUID userId = UUID.randomUUID(); + var request = new ArtistUnsubscriptionServiceRequest(artistsId, userId); + int artistSubscriptionCount = 2; + given( + artistSubscriptionUseCase.unsubscribe(request.artistIds(), userId) + ).willReturn( + ArtistSubscriptionFixture.artistSubscriptions(artistSubscriptionCount) + ); + + //when + var result = artistService.unsubscribe(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result).isNotNull(); + soft.assertThat(result.successUnsubscriptionArtistIds().size()) + .isEqualTo(artistSubscriptionCount); + } + ); + } + + @Test + @DisplayName("페이지네이션을 이용해 구독한 아티스트를 반환한다.") + void artistSubscribeWithPagination() { + //given + int size = 2; + boolean hasNext = true; + var request = ArtistRequestDtoFixture.artistSubscriptionPaginationServiceRequest(size); + var subscriptions = ArtistSubscriptionFixture.artistSubscriptions(3); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionArtistIds = subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + given( + artistUseCase.findAllArtistInCursorPagination( + request.toDomainRequest(subscriptionArtistIds)) + ).willReturn( + ArtistResponseDtoFixture.artistPaginationDomainResponse(size, hasNext) + ); + + //when + var result = artistService.findArtistSubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } + + @Test + @DisplayName("구독한 아티스트가 없을 경우 빈 리스트를 반환하다.") + void artistSubscribeEmptyResultWithPagination() { + //given + int size = 2; + var request = ArtistRequestDtoFixture.artistSubscriptionPaginationServiceRequest(size); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + List.of() + ); + + //when + var result = artistService.findArtistSubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data()).isEmpty(); + soft.assertThat(result.hasNext()).isFalse(); + } + ); + } + + @Test + @DisplayName("페이지네이션을 이용해 구독하지 않은 아티스트를 반환한다.") + void artistUnsubscribeWithPagination() { + //given + int size = 2; + boolean hasNext = true; + var request = ArtistRequestDtoFixture.artistUnsubscriptionPaginationServiceRequest(size); + var subscriptions = ArtistSubscriptionFixture.artistSubscriptions(2); + given( + artistSubscriptionUseCase.findSubscriptionList(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionArtistIds = subscriptions.stream() + .map(ArtistSubscription::getArtistId) + .toList(); + given( + artistUseCase.findAllArtistInCursorPagination( + request.toDomainRequest(subscriptionArtistIds)) + ).willReturn( + ArtistResponseDtoFixture.artistPaginationDomainResponse(size, hasNext) + ); + + //when + var result = artistService.findArtistUnsubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } +} diff --git a/app/api/show-api/src/test/java/genre/fixture/GenreRequestDtoFixture.java b/app/api/show-api/src/test/java/genre/fixture/GenreRequestDtoFixture.java new file mode 100644 index 00000000..a2d4f4bc --- /dev/null +++ b/app/api/show-api/src/test/java/genre/fixture/GenreRequestDtoFixture.java @@ -0,0 +1,32 @@ +package genre.fixture; + +import com.example.genre.service.dto.request.GenreSubscriptionPaginationServiceRequest; +import com.example.genre.service.dto.request.GenreUnsubscriptionPaginationServiceRequest; +import com.example.vo.SubscriptionStatusApiType; +import java.util.UUID; + +public class GenreRequestDtoFixture { + + public static GenreSubscriptionPaginationServiceRequest genrePaginationServiceRequest( + int size + ) { + return GenreSubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.SUBSCRIBED) + .cursor(UUID.randomUUID()) + .size(size) + .userId(UUID.randomUUID()) + .build(); + } + + public static GenreUnsubscriptionPaginationServiceRequest genreUnsubscriptionPaginationServiceRequest( + int size + ) { + return GenreUnsubscriptionPaginationServiceRequest.builder() + .subscriptionStatusApiType(SubscriptionStatusApiType.UNSUBSCRIBED) + .cursor(UUID.randomUUID()) + .size(size) + .userId(UUID.randomUUID()) + .build(); + } + +} diff --git a/app/api/show-api/src/test/java/genre/fixture/GenreResponseDtoFixture.java b/app/api/show-api/src/test/java/genre/fixture/GenreResponseDtoFixture.java new file mode 100644 index 00000000..e72db081 --- /dev/null +++ b/app/api/show-api/src/test/java/genre/fixture/GenreResponseDtoFixture.java @@ -0,0 +1,29 @@ +package genre.fixture; + +import java.util.List; +import java.util.UUID; +import java.util.stream.IntStream; +import org.example.dto.genre.response.GenreDomainResponse; +import org.example.dto.genre.response.GenrePaginationDomainResponse; + +public class GenreResponseDtoFixture { + + public static GenrePaginationDomainResponse genrePaginationDomainResponse( + int size, + boolean hasNext + ) { + return GenrePaginationDomainResponse.builder() + .data(genreDomainResponses(size)) + .hasNext(hasNext) + .build(); + } + + public static List genreDomainResponses(int size) { + return IntStream.range(0, size) + .mapToObj(i -> GenreDomainResponse.builder() + .id(UUID.randomUUID()) + .name("testName" + i) + .build()) + .toList(); + } +} diff --git a/app/api/show-api/src/test/java/genre/service/GenreServiceTest.java b/app/api/show-api/src/test/java/genre/service/GenreServiceTest.java new file mode 100644 index 00000000..9fcfd5bb --- /dev/null +++ b/app/api/show-api/src/test/java/genre/service/GenreServiceTest.java @@ -0,0 +1,195 @@ +package genre.service; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import com.example.genre.service.GenreService; +import com.example.genre.service.dto.request.GenreSubscriptionServiceRequest; +import com.example.genre.service.dto.request.GenreUnsubscriptionServiceRequest; +import genre.fixture.GenreRequestDtoFixture; +import genre.fixture.GenreResponseDtoFixture; +import java.util.List; +import java.util.UUID; +import org.assertj.core.api.SoftAssertions; +import org.example.entity.GenreSubscription; +import org.example.entity.genre.Genre; +import org.example.fixture.GenreSubscriptionFixture; +import org.example.fixture.domain.GenreFixture; +import org.example.usecase.GenreSubscriptionUseCase; +import org.example.usecase.genre.GenreUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class GenreServiceTest { + + private final GenreUseCase genreUseCase = mock(GenreUseCase.class); + private final GenreSubscriptionUseCase genreSubscriptionUseCase = mock( + GenreSubscriptionUseCase.class); + private final GenreService genreService = new GenreService( + genreUseCase, + genreSubscriptionUseCase + ); + + @Test + @DisplayName("장르를 구독하면 구독 성공한 장르 ID들을 반환하다.") + void genreSubscribe() { + //given + List genreIds = List.of(UUID.randomUUID(), UUID.randomUUID()); + UUID userId = UUID.randomUUID(); + var request = new GenreSubscriptionServiceRequest(genreIds, userId); + var existGenresInRequest = GenreFixture.genres(2); + given( + genreUseCase.findAllGenresInIds(request.genreIds()) + ).willReturn( + existGenresInRequest + ); + + var existGenreIdsInRequest = existGenresInRequest.stream() + .map(Genre::getId) + .toList(); + int genreSubscriptionCount = 2; + given( + genreSubscriptionUseCase.subscribe(existGenreIdsInRequest, request.userId()) + ).willReturn( + GenreSubscriptionFixture.genreSubscriptions(genreSubscriptionCount) + ); + + //when + var result = genreService.subscribe(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result).isNotNull(); + soft.assertThat(result.successSubscriptionGenreIds().size()) + .isEqualTo(genreSubscriptionCount); + } + ); + } + + @Test + @DisplayName("장르를 구독 취소하면 구독 취소 성공한 장르 ID들을 반환하다.") + void genreUnsubscribe() { + //given + List genreIds = List.of(UUID.randomUUID(), UUID.randomUUID()); + UUID userId = UUID.randomUUID(); + var request = new GenreUnsubscriptionServiceRequest(genreIds, userId); + int genreUnsubscriptionCount = 2; + given( + genreSubscriptionUseCase.unsubscribe(request.genreIds(), request.userId()) + ).willReturn( + GenreSubscriptionFixture.genreSubscriptions(genreUnsubscriptionCount) + ); + + //when + var result = genreService.unsubscribe(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result).isNotNull(); + soft.assertThat(result.successUnsubscriptionGenreIds().size()) + .isEqualTo(genreUnsubscriptionCount); + } + ); + } + + @Test + @DisplayName("페이지네이션을 이용해 구독한 장르를 반환한다.") + void genreSubscribeWithPagination() { + //given + int size = 2; + boolean hasNext = true; + var request = GenreRequestDtoFixture.genrePaginationServiceRequest(size); + var subscriptions = GenreSubscriptionFixture.genreSubscriptions(3); + given( + genreSubscriptionUseCase.findSubscriptions(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionGenreIds = subscriptions.stream() + .map(GenreSubscription::getGenreId) + .toList(); + given( + genreUseCase.findGenreWithCursorPagination( + request.toDomainRequest(subscriptionGenreIds)) + ).willReturn( + GenreResponseDtoFixture.genrePaginationDomainResponse(size, hasNext) + ); + + //when + var result = genreService.findGenreSubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } + + @Test + @DisplayName("구독한 장르가 없을 경우 빈 리스트를 반환하다.") + void genreSubscribeEmptyResultWithPagination() { + //given + int size = 2; + var request = GenreRequestDtoFixture.genrePaginationServiceRequest(size); + given( + genreSubscriptionUseCase.findSubscriptions(request.userId()) + ).willReturn( + List.of() + ); + + //when + var result = genreService.findGenreSubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data()).isEmpty(); + soft.assertThat(result.hasNext()).isFalse(); + } + ); + } + + @Test + @DisplayName("페이지네이션을 이용해 구독하지 않은 아티스트를 반환한다.") + void genreUnsubscribeWithPagination() { + //given + int size = 2; + boolean hasNext = true; + var request = GenreRequestDtoFixture.genreUnsubscriptionPaginationServiceRequest(size); + var subscriptions = GenreSubscriptionFixture.genreSubscriptions(3); + given( + genreSubscriptionUseCase.findSubscriptions(request.userId()) + ).willReturn( + subscriptions + ); + + var subscriptionGenreIds = subscriptions.stream() + .map(GenreSubscription::getGenreId) + .toList(); + + given( + genreUseCase.findGenreWithCursorPagination( + request.toDomainRequest(subscriptionGenreIds) + ) + ).willReturn( + GenreResponseDtoFixture.genrePaginationDomainResponse(size, hasNext) + ); + + //when + var result = genreService.findGenreUnSubscriptions(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } + +} diff --git a/app/api/show-api/src/test/java/show/fixture/dto/ShowDtoFixture.java b/app/api/show-api/src/test/java/show/fixture/dto/ShowRequestDtoFixture.java similarity index 72% rename from app/api/show-api/src/test/java/show/fixture/dto/ShowDtoFixture.java rename to app/api/show-api/src/test/java/show/fixture/dto/ShowRequestDtoFixture.java index fad9b07b..03106988 100644 --- a/app/api/show-api/src/test/java/show/fixture/dto/ShowDtoFixture.java +++ b/app/api/show-api/src/test/java/show/fixture/dto/ShowRequestDtoFixture.java @@ -3,6 +3,7 @@ import com.example.show.controller.dto.response.SeatInfoApiResponse; import com.example.show.controller.vo.TicketingApiType; import com.example.show.service.dto.request.ShowCreateServiceRequest; +import com.example.show.service.dto.request.ShowSearchPaginationServiceRequest; import com.example.show.service.dto.request.ShowUpdateServiceRequest; import java.time.LocalDate; import java.util.List; @@ -10,7 +11,7 @@ import java.util.UUID; import org.springframework.mock.web.MockMultipartFile; -public class ShowDtoFixture { +public class ShowRequestDtoFixture { private static final MockMultipartFile post = new MockMultipartFile( "image", @@ -27,9 +28,9 @@ public static ShowCreateServiceRequest showCreateServiceRequest() { .endDate(LocalDate.EPOCH) .location("test_location") .post(post) - .seatInfoApiResponse(getSeatInfoApiResponse()) + .priceInformation(getSeatInfoApiResponse()) .showTicketingSites(getTicketingSites()) - .showTicketingDates(getShowTicketingDates()) + .ticketingTimes(getShowTicketingDates()) .artistIds(List.of(UUID.randomUUID())) .genreIds(List.of(UUID.randomUUID())) .build(); @@ -43,14 +44,34 @@ public static ShowUpdateServiceRequest showUpdateServiceRequest() { .endDate(LocalDate.EPOCH) .location("test_location") .post(post) - .seatInfoApiResponse(getSeatInfoApiResponse()) + .seatInfoApiResponse(getSeatInfoApiResponses()) .showTicketingSiteInfos(getTicketingSites()) .artistIds(List.of(UUID.randomUUID())) .genreIds(List.of(UUID.randomUUID())) .build(); } - private static SeatInfoApiResponse getSeatInfoApiResponse() { + public static ShowSearchPaginationServiceRequest showSearchPaginationServiceRequest( + int size, + String search + ) { + return ShowSearchPaginationServiceRequest.builder() + .cursor(UUID.randomUUID()) + .size(size) + .search(search) + .build(); + } + + private static Map getSeatInfoApiResponse() { + return Map.of( + "VIP", 150000, + "R", 120000, + "S", 100000, + "A", 80000 + ); + } + + private static SeatInfoApiResponse getSeatInfoApiResponses() { return new SeatInfoApiResponse( Map.of( "VIP", 150000, diff --git a/app/api/show-api/src/test/java/show/fixture/dto/ShowResponseDtoFixture.java b/app/api/show-api/src/test/java/show/fixture/dto/ShowResponseDtoFixture.java new file mode 100644 index 00000000..e1caab37 --- /dev/null +++ b/app/api/show-api/src/test/java/show/fixture/dto/ShowResponseDtoFixture.java @@ -0,0 +1,42 @@ +package show.fixture.dto; + +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; +import java.util.stream.IntStream; +import org.example.dto.show.response.ShowSearchDomainResponse; +import org.example.dto.show.response.ShowSearchPaginationDomainResponse; + +public class ShowResponseDtoFixture { + + public static ShowSearchPaginationDomainResponse showSearchPaginationDomainResponse( + int size, + boolean hasNext + ) { + return ShowSearchPaginationDomainResponse.builder() + .data(showSearchDomainResponses(size)) + .hasNext(hasNext) + .build(); + } + + public static ShowSearchPaginationDomainResponse emptyDataShowSearchPaginationDomainResponse() { + return ShowSearchPaginationDomainResponse.builder() + .data(List.of()) + .hasNext(false) + .build(); + } + + public static List showSearchDomainResponses(int size) { + return IntStream.range(0, size) + .mapToObj(i -> ShowSearchDomainResponse.builder() + .id(UUID.randomUUID()) + .title("testTitle") + .startDate(LocalDate.EPOCH) + .endDate(LocalDate.EPOCH) + .location("testLocation") + .image("testImage") + .build() + ) + .toList(); + } +} diff --git a/app/api/show-api/src/test/java/show/service/ShowAdminServiceTest.java b/app/api/show-api/src/test/java/show/service/ShowAdminServiceTest.java index cd96a21e..8f183ab8 100644 --- a/app/api/show-api/src/test/java/show/service/ShowAdminServiceTest.java +++ b/app/api/show-api/src/test/java/show/service/ShowAdminServiceTest.java @@ -18,7 +18,7 @@ import org.example.usecase.show.ShowUseCase; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import show.fixture.dto.ShowDtoFixture; +import show.fixture.dto.ShowRequestDtoFixture; class ShowAdminServiceTest { @@ -32,7 +32,7 @@ class ShowAdminServiceTest { @DisplayName("공연은 업로드된 이미지 URL과 함께 생성된다.") void showCreateWithUploadedImageUrl() { //given - ShowCreateServiceRequest showCreateServiceRequest = ShowDtoFixture.showCreateServiceRequest(); + ShowCreateServiceRequest showCreateServiceRequest = ShowRequestDtoFixture.showCreateServiceRequest(); given( fileUploadComponent.uploadFile( "artist", @@ -52,7 +52,7 @@ void showCreateWithUploadedImageUrl() { @DisplayName("공연은 업로드된 이미지 URL과 함께 업데이트 된다.") void showUpdateWithUploadedImageUrl() { //given - ShowUpdateServiceRequest showUpdateServiceRequest = ShowDtoFixture.showUpdateServiceRequest(); + ShowUpdateServiceRequest showUpdateServiceRequest = ShowRequestDtoFixture.showUpdateServiceRequest(); UUID showId = UUID.randomUUID(); given( fileUploadComponent.uploadFile( diff --git a/app/api/show-api/src/test/java/show/service/ShowServiceTest.java b/app/api/show-api/src/test/java/show/service/ShowServiceTest.java new file mode 100644 index 00000000..41bcaf50 --- /dev/null +++ b/app/api/show-api/src/test/java/show/service/ShowServiceTest.java @@ -0,0 +1,70 @@ +package show.service; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import com.example.show.service.ShowService; +import org.assertj.core.api.SoftAssertions; +import org.example.usecase.show.ShowUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import show.fixture.dto.ShowRequestDtoFixture; +import show.fixture.dto.ShowResponseDtoFixture; + +class ShowServiceTest { + + private final ShowUseCase showUseCase = mock(ShowUseCase.class); + private final ShowService showService = new ShowService(showUseCase); + + @Test + @DisplayName("페이지네이션을 이용해 공연을 검색 할 수 있다.") + void showSearchWithPagination() { + //given + int size = 2; + boolean hasNext = true; + String search = "testShowName"; + var request = ShowRequestDtoFixture.showSearchPaginationServiceRequest(size, search); + given( + showUseCase.searchShow(request.toDomainRequest()) + ).willReturn( + ShowResponseDtoFixture.showSearchPaginationDomainResponse(size, hasNext) + ); + + //when + var result = showService.searchShow(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data().size()).isEqualTo(size); + soft.assertThat(result.hasNext()).isEqualTo(hasNext); + } + ); + } + + @Test + @DisplayName("공연 검색 결과가 없으면 빈 리스트를 반환한다.") + void showSearchEmptyResultWithPagination() { + //given + int size = 2; + String search = "testShowName"; + var request = ShowRequestDtoFixture.showSearchPaginationServiceRequest(size, search); + given( + showUseCase.searchShow(request.toDomainRequest()) + ).willReturn( + ShowResponseDtoFixture.emptyDataShowSearchPaginationDomainResponse() + ); + + //when + var result = showService.searchShow(request); + + //then + SoftAssertions.assertSoftly( + soft -> { + soft.assertThat(result.data()).isEmpty(); + soft.assertThat(result.hasNext()).isFalse(); + } + ); + } + +} diff --git a/app/domain/show-domain/build.gradle b/app/domain/show-domain/build.gradle index 7d25aab2..b7e5d2e6 100644 --- a/app/domain/show-domain/build.gradle +++ b/app/domain/show-domain/build.gradle @@ -1,6 +1,16 @@ +plugins { + id "java-library" + id "java-test-fixtures" + id "maven-publish" +} + dependencies { implementation project(":app:domain:common-domain") // hypersistence utils implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.6' -} + //testFixtures + testFixturesImplementation 'org.springframework.boot:spring-boot-starter-test' + testFixturesImplementation project(":app:domain:common-domain") + testFixturesImplementation 'org.springframework.data:spring-data-jpa' +} \ No newline at end of file diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistFilterDomain.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistFilterDomain.java new file mode 100644 index 00000000..938e30fc --- /dev/null +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistFilterDomain.java @@ -0,0 +1,23 @@ +package org.example.dto.artist.request; + +import java.util.List; +import java.util.UUID; +import lombok.Builder; +import org.example.vo.ArtistGender; +import org.example.vo.ArtistType; + +@Builder +public record ArtistFilterDomain( + List artistGenders, + List artistTypes, + List genreIds +) { + + public static ArtistFilterDomain defaultArtistFilterDomain() { + return ArtistFilterDomain.builder() + .artistGenders(List.of()) + .artistTypes(List.of()) + .genreIds(List.of()) + .build(); + } +} diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistFilterTotalCountDomainRequest.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistFilterTotalCountDomainRequest.java new file mode 100644 index 00000000..ba54a951 --- /dev/null +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistFilterTotalCountDomainRequest.java @@ -0,0 +1,17 @@ +package org.example.dto.artist.request; + +import java.util.List; +import java.util.UUID; +import lombok.Builder; +import org.example.vo.ArtistGender; +import org.example.vo.ArtistType; + +@Builder +public record ArtistFilterTotalCountDomainRequest( + List artistGenders, + List artistTypes, + List genreIds, + List artistIds +) { + +} diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistPaginationDomainRequest.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistPaginationDomainRequest.java index 48687900..ebcc6e94 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistPaginationDomainRequest.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/request/ArtistPaginationDomainRequest.java @@ -4,13 +4,16 @@ import java.util.UUID; import lombok.Builder; import org.example.vo.ArtistSortStandardDomainType; +import org.example.vo.SubscriptionStatus; @Builder public record ArtistPaginationDomainRequest( + SubscriptionStatus subscriptionStatus, int size, ArtistSortStandardDomainType sortStandard, UUID cursor, - List artistIds + List artistIds, + ArtistFilterDomain artistFilterDomain ) { } diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailDomainResponse.java similarity index 89% rename from app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailResponse.java rename to app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailDomainResponse.java index f13cee67..5606312c 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailDomainResponse.java @@ -5,7 +5,7 @@ import org.example.vo.ArtistGender; import org.example.vo.ArtistType; -public record ArtistDetailResponse( +public record ArtistDetailDomainResponse( UUID id, String koreanName, String englishName, diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistFilterTotalCountDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistFilterTotalCountDomainResponse.java new file mode 100644 index 00000000..d91690cf --- /dev/null +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistFilterTotalCountDomainResponse.java @@ -0,0 +1,7 @@ +package org.example.dto.artist.response; + +public record ArtistFilterTotalCountDomainResponse( + int totalCount +) { + +} diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistKoreanNameResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistKoreanNameDomainResponse.java similarity index 70% rename from app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistKoreanNameResponse.java rename to app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistKoreanNameDomainResponse.java index 4f8aa242..376c8c8a 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistKoreanNameResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistKoreanNameDomainResponse.java @@ -2,7 +2,7 @@ import java.util.UUID; -public record ArtistKoreanNameResponse( +public record ArtistKoreanNameDomainResponse( UUID id, String koreanName ) { diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailPaginationResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistPaginationDomainResponse.java similarity index 58% rename from app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailPaginationResponse.java rename to app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistPaginationDomainResponse.java index 0e14406e..3c96cdab 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistDetailPaginationResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistPaginationDomainResponse.java @@ -4,8 +4,8 @@ import lombok.Builder; @Builder -public record ArtistDetailPaginationResponse( - List data, +public record ArtistPaginationDomainResponse( + List data, boolean hasNext ) { diff --git a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/SimpleArtistResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistSimpleDomainResponse.java similarity index 66% rename from app/domain/show-domain/src/main/java/org/example/dto/artist/response/SimpleArtistResponse.java rename to app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistSimpleDomainResponse.java index 1eb25a7a..9512d5c6 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/artist/response/SimpleArtistResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/artist/response/ArtistSimpleDomainResponse.java @@ -1,8 +1,10 @@ package org.example.dto.artist.response; import java.util.UUID; +import lombok.Builder; -public record SimpleArtistResponse( +@Builder +public record ArtistSimpleDomainResponse( UUID id, String koreanName, String englishName, diff --git a/app/domain/show-domain/src/main/java/org/example/dto/genre/request/GenreSubscriptionPaginationDomainRequest.java b/app/domain/show-domain/src/main/java/org/example/dto/genre/request/GenrePaginationDomainRequest.java similarity index 58% rename from app/domain/show-domain/src/main/java/org/example/dto/genre/request/GenreSubscriptionPaginationDomainRequest.java rename to app/domain/show-domain/src/main/java/org/example/dto/genre/request/GenrePaginationDomainRequest.java index dcf563a8..71861f16 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/genre/request/GenreSubscriptionPaginationDomainRequest.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/genre/request/GenrePaginationDomainRequest.java @@ -3,12 +3,13 @@ import java.util.List; import java.util.UUID; import lombok.Builder; +import org.example.vo.SubscriptionStatus; @Builder -public record GenreSubscriptionPaginationDomainRequest( +public record GenrePaginationDomainRequest( + SubscriptionStatus subscriptionStatus, UUID cursor, int size, - UUID userId, List genreIds ) { diff --git a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreDomainResponse.java index 87410345..d74ed045 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreDomainResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreDomainResponse.java @@ -1,10 +1,13 @@ package org.example.dto.genre.response; import java.util.UUID; +import lombok.Builder; -/** +/* * 엔티티 조회 객체 */ + +@Builder public record GenreDomainResponse( UUID id, String name diff --git a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreNameResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreNameDomainResponse.java similarity index 72% rename from app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreNameResponse.java rename to app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreNameDomainResponse.java index bf478f61..2a5b7e0e 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreNameResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreNameDomainResponse.java @@ -2,7 +2,7 @@ import java.util.UUID; -public record GenreNameResponse( +public record GenreNameDomainResponse( UUID id, String name ) { diff --git a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreSubscriptionPaginationDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenrePaginationDomainResponse.java similarity index 54% rename from app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreSubscriptionPaginationDomainResponse.java rename to app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenrePaginationDomainResponse.java index 8257116c..630cd09e 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreSubscriptionPaginationDomainResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenrePaginationDomainResponse.java @@ -4,9 +4,9 @@ import lombok.Builder; @Builder -public record GenreSubscriptionPaginationDomainResponse( +public record GenrePaginationDomainResponse( boolean hasNext, - List data + List data ) { } diff --git a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreSubscriptionDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreSubscriptionDomainResponse.java deleted file mode 100644 index 5ddeccba..00000000 --- a/app/domain/show-domain/src/main/java/org/example/dto/genre/response/GenreSubscriptionDomainResponse.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.example.dto.genre.response; - -import java.util.UUID; - -public record GenreSubscriptionDomainResponse( - UUID id, - String name -) { - -} diff --git a/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowCreationDomainRequest.java b/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowCreationDomainRequest.java index 42e5450e..572810ef 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowCreationDomainRequest.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowCreationDomainRequest.java @@ -2,17 +2,12 @@ import java.time.LocalDate; import java.util.List; -import java.util.Map; import java.util.UUID; import lombok.Builder; import org.example.entity.show.Show; -import org.example.entity.show.ShowArtist; -import org.example.entity.show.ShowGenre; -import org.example.entity.show.ShowSearch; -import org.example.entity.show.ShowTicketingTime; import org.example.entity.show.info.SeatPrices; +import org.example.entity.show.info.ShowTicketingTimes; import org.example.entity.show.info.TicketingSites; -import org.example.vo.TicketingType; @Builder public record ShowCreationDomainRequest( @@ -24,7 +19,7 @@ public record ShowCreationDomainRequest( String posterImageURL, SeatPrices showSeats, TicketingSites showTicketingSites, - Map showTicketingDates, + ShowTicketingTimes showTicketingTimes, List artistIds, List genreIds ) { @@ -41,42 +36,4 @@ public Show toShow() { .ticketingSites(showTicketingSites) .build(); } - - public ShowSearch toShowSearch(Show show) { - return ShowSearch.builder() - .name(title) - .show(show) - .build(); - } - - public List toShowArtist(Show show) { - return artistIds.stream() - .map(artistId -> ShowArtist.builder() - .artistId(artistId) - .showId(show.getId()) - .build() - ) - .toList(); - } - - public List toShowGenre(Show show) { - return genreIds.stream() - .map(genreId -> ShowGenre.builder() - .genreId(genreId) - .showId(show.getId()) - .build() - ) - .toList(); - } - - public List toShowTicketing(Show show) { - return showTicketingDates.entrySet().stream() - .map(entry -> ShowTicketingTime.builder() - .ticketingType(entry.getKey()) - .ticketingAt(entry.getValue()) - .show(show) - .build() - ) - .toList(); - } } diff --git a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowInfoDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowInfoDomainResponse.java index edd40520..c9e857af 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowInfoDomainResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowInfoDomainResponse.java @@ -3,8 +3,8 @@ import java.time.LocalDate; import java.util.Set; import java.util.UUID; -import org.example.dto.artist.response.ArtistKoreanNameResponse; -import org.example.dto.genre.response.GenreNameResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; +import org.example.dto.genre.response.GenreNameDomainResponse; import org.example.entity.show.info.SeatPrices; import org.example.entity.show.info.TicketingSites; @@ -18,8 +18,8 @@ public record ShowInfoDomainResponse( String image, SeatPrices seatPrices, TicketingSites ticketingSites, - Set artistKoreanNameResponses, - Set genreNameResponses + Set artistKoreanNameResponses, + Set genreNameResponses ) { } diff --git a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowSearchDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowSearchDomainResponse.java index 9ff873a8..ef61082d 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowSearchDomainResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowSearchDomainResponse.java @@ -2,7 +2,9 @@ import java.time.LocalDate; import java.util.UUID; +import lombok.Builder; +@Builder public record ShowSearchDomainResponse( UUID id, String title, diff --git a/app/domain/show-domain/src/main/java/org/example/entity/show/Show.java b/app/domain/show-domain/src/main/java/org/example/entity/show/Show.java index 6023cea4..adf3f534 100644 --- a/app/domain/show-domain/src/main/java/org/example/entity/show/Show.java +++ b/app/domain/show-domain/src/main/java/org/example/entity/show/Show.java @@ -13,6 +13,7 @@ import lombok.NoArgsConstructor; import org.example.entity.BaseEntity; import org.example.entity.show.info.SeatPrices; +import org.example.entity.show.info.ShowTicketingTimes; import org.example.entity.show.info.TicketingSites; import org.example.util.StringNormalizer; @@ -85,6 +86,19 @@ public List toShowGenre(List genreIds) { .toList(); } + public List toShowTicketingTime(ShowTicketingTimes showTicketingTimes) { + return showTicketingTimes.getTicketingTimeByType() + .entrySet() + .stream() + .map(entry -> ShowTicketingTime.builder() + .ticketingType(entry.getKey()) + .ticketingAt(entry.getValue()) + .show(this) + .build() + ) + .toList(); + } + public ShowSearch toShowSearch() { return ShowSearch.builder() .name(StringNormalizer.removeWhitespaceAndLowerCase(title)) @@ -92,6 +106,7 @@ public ShowSearch toShowSearch() { .build(); } + public void changeShowInfo(Show newShow) { this.title = newShow.title; this.content = newShow.content; diff --git a/app/domain/show-domain/src/main/java/org/example/entity/show/info/ShowTicketingTimes.java b/app/domain/show-domain/src/main/java/org/example/entity/show/info/ShowTicketingTimes.java new file mode 100644 index 00000000..19412766 --- /dev/null +++ b/app/domain/show-domain/src/main/java/org/example/entity/show/info/ShowTicketingTimes.java @@ -0,0 +1,19 @@ +package org.example.entity.show.info; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; +import org.example.vo.TicketingType; + +public class ShowTicketingTimes { + + private final Map ticketingTimeByType = new HashMap<>(); + + public void saveTicketingTimes(TicketingType ticketingType, LocalDate date) { + ticketingTimeByType.put(ticketingType, date); + } + + public Map getTicketingTimeByType() { + return new HashMap<>(ticketingTimeByType); + } +} diff --git a/app/domain/show-domain/src/main/java/org/example/error/ArtistError.java b/app/domain/show-domain/src/main/java/org/example/error/ArtistError.java deleted file mode 100644 index ecc8ceb3..00000000 --- a/app/domain/show-domain/src/main/java/org/example/error/ArtistError.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.example.error; - -import org.example.exception.BusinessError; - -public enum ArtistError implements BusinessError { - ENTITY_NOT_FOUND_ERROR { - @Override - public int getHttpStatus() { - return 404; - } - - @Override - public String getErrorCode() { - return "ART-001"; - } - - @Override - public String getClientMessage() { - return "존재하지 않은 아티스트입니다."; - } - - @Override - public String getLogMessage() { - return "요청 값이 잘못 처리되었습니다."; - } - }, - - SEARCH_NOT_FOUND_ERROR { - @Override - public int getHttpStatus() { - return 400; - } - - @Override - public String getErrorCode() { - return "ART-002"; - } - - @Override - public String getClientMessage() { - return "검색 조건이 일치하지 않습니다."; - } - - @Override - public String getLogMessage() { - return "검색 요청 값이 잘못 처리되었습니다."; - } - } - -} diff --git a/app/domain/show-domain/src/main/java/org/example/error/ShowError.java b/app/domain/show-domain/src/main/java/org/example/error/ShowError.java deleted file mode 100644 index 8666b550..00000000 --- a/app/domain/show-domain/src/main/java/org/example/error/ShowError.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.example.error; - -import org.example.exception.BusinessError; - -public enum ShowError implements BusinessError { - ENTITY_NOT_FOUND_ERROR { - @Override - public int getHttpStatus() { - return 404; - } - - @Override - public String getErrorCode() { - return "SHW-001"; - } - - @Override - public String getClientMessage() { - return "존재하지 않은 공연입니다."; - } - - @Override - public String getLogMessage() { - return "요청 값이 잘못 처리되었습니다."; - } - }, - - SEARCH_NOT_FOUND_ERROR { - @Override - public int getHttpStatus() { - return 400; - } - - @Override - public String getErrorCode() { - return "SHW-002"; - } - - @Override - public String getClientMessage() { - return "검색 조건이 일치하지 않습니다."; - } - - @Override - public String getLogMessage() { - return "검색 요청 값이 잘못 처리되었습니다."; - } - } -} diff --git a/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepository.java b/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepository.java index 77c7aca8..51ea6b14 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepository.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepository.java @@ -3,21 +3,29 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import org.example.dto.artist.request.ArtistFilterTotalCountDomainRequest; import org.example.dto.artist.request.ArtistPaginationDomainRequest; -import org.example.dto.artist.response.ArtistDetailPaginationResponse; -import org.example.dto.artist.response.ArtistDetailResponse; -import org.example.dto.artist.response.ArtistKoreanNameResponse; +import org.example.dto.artist.response.ArtistDetailDomainResponse; +import org.example.dto.artist.response.ArtistFilterTotalCountDomainResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; +import org.example.dto.artist.response.ArtistPaginationDomainResponse; import org.example.entity.artist.Artist; public interface ArtistQuerydslRepository { - List findAllWithGenreNames(); + List findAllWithGenreNames(); - Optional findArtistWithGenreNamesById(UUID id); + Optional findArtistWithGenreNamesById(UUID id); - List findAllArtistKoreanName(); + List findAllArtistKoreanName(); List findAllInIds(List ids); - ArtistDetailPaginationResponse findAllWithCursorPagination(ArtistPaginationDomainRequest request); + ArtistPaginationDomainResponse findAllWithCursorPagination( + ArtistPaginationDomainRequest request + ); + + Optional findFilterArtistTotalCount( + ArtistFilterTotalCountDomainRequest request + ); } diff --git a/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepositoryImpl.java b/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepositoryImpl.java index 1e9f8820..60cbbb0e 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepositoryImpl.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/artist/ArtistQuerydslRepositoryImpl.java @@ -13,17 +13,25 @@ import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.function.Function; import lombok.RequiredArgsConstructor; +import org.example.dto.artist.request.ArtistFilterDomain; +import org.example.dto.artist.request.ArtistFilterTotalCountDomainRequest; import org.example.dto.artist.request.ArtistPaginationDomainRequest; -import org.example.dto.artist.response.ArtistDetailPaginationResponse; -import org.example.dto.artist.response.ArtistDetailResponse; -import org.example.dto.artist.response.ArtistKoreanNameResponse; -import org.example.dto.artist.response.SimpleArtistResponse; +import org.example.dto.artist.response.ArtistDetailDomainResponse; +import org.example.dto.artist.response.ArtistFilterTotalCountDomainResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; +import org.example.dto.artist.response.ArtistPaginationDomainResponse; +import org.example.dto.artist.response.ArtistSimpleDomainResponse; import org.example.entity.artist.Artist; +import org.example.util.SliceUtil; import org.example.vo.ArtistSortStandardDomainType; +import org.example.vo.SubscriptionStatus; +import org.springframework.data.domain.Slice; import org.springframework.stereotype.Repository; @Repository @@ -33,12 +41,12 @@ public class ArtistQuerydslRepositoryImpl implements ArtistQuerydslRepository { private final JPAQueryFactory jpaQueryFactory; @Override - public List findAllWithGenreNames() { + public List findAllWithGenreNames() { return createArtistJoinArtistGenreAndGenreQuery() .transform( groupBy(artist.id).list( Projections.constructor( - ArtistDetailResponse.class, + ArtistDetailDomainResponse.class, artist.id, artist.koreanName, artist.englishName, @@ -53,14 +61,14 @@ public List findAllWithGenreNames() { } @Override - public Optional findArtistWithGenreNamesById(UUID id) { + public Optional findArtistWithGenreNamesById(UUID id) { return Optional.ofNullable( createArtistJoinArtistGenreAndGenreQuery() .where(artist.id.eq(id)) .transform( groupBy(artist.id).as( Projections.constructor( - ArtistDetailResponse.class, + ArtistDetailDomainResponse.class, artist.id, artist.koreanName, artist.englishName, @@ -77,11 +85,11 @@ public Optional findArtistWithGenreNamesById(UUID id) { } @Override - public List findAllArtistKoreanName() { + public List findAllArtistKoreanName() { return jpaQueryFactory .select( Projections.constructor( - ArtistKoreanNameResponse.class, + ArtistKoreanNameDomainResponse.class, artist.id, artist.koreanName ) @@ -100,30 +108,56 @@ public List findAllInIds(List ids) { } @Override - public ArtistDetailPaginationResponse findAllWithCursorPagination( + public ArtistPaginationDomainResponse findAllWithCursorPagination( ArtistPaginationDomainRequest request ) { - List result = jpaQueryFactory.select( + List result = jpaQueryFactory.select( Projections.constructor( - SimpleArtistResponse.class, + ArtistSimpleDomainResponse.class, artist.id, artist.koreanName, artist.englishName, artist.image ) ).from(artist) - .where(getWhereClauseInCursorPagination(request.cursor(), request.artistIds())) + .where( + getWhereClauseInCursorPagination( + request.subscriptionStatus(), + request.cursor(), + request.artistIds(), + request.artistFilterDomain() + ) + ) .orderBy(getOrderSpecifier(request.sortStandard())) .limit(request.size() + 1) .fetch(); - boolean hasNext = result.size() == request.size() + 1; - return ArtistDetailPaginationResponse.builder() - .data(hasNext ? result.subList(0, request.size()) : result) - .hasNext(hasNext) + Slice responses = SliceUtil.makeSlice( + request.size(), + result + ); + + return ArtistPaginationDomainResponse.builder() + .data(responses.getContent()) + .hasNext(responses.hasNext()) .build(); } + @Override + public Optional findFilterArtistTotalCount( + ArtistFilterTotalCountDomainRequest request + ) { + List totalCount = jpaQueryFactory + .selectDistinct(artist.id) + .from(artist) + .join(artistGenre).on(isArtistGenreEqualArtistIdAndIsDeletedFalse()) + .join(genre).on(isArtistGenreEqualGenreIdAndIsDeletedFalse()) + .where(getWhereClauseWithFilter(request)) + .fetch(); + + return Optional.of(new ArtistFilterTotalCountDomainResponse(totalCount.size())); + } + private JPAQuery createArtistJoinArtistGenreAndGenreQuery() { return jpaQueryFactory .selectFrom(artist) @@ -141,17 +175,40 @@ private BooleanExpression isArtistGenreEqualGenreIdAndIsDeletedFalse() { } private BooleanBuilder getWhereClauseInCursorPagination( + SubscriptionStatus status, UUID cursor, - List artistIds + List artistIds, + ArtistFilterDomain artistFilterDomain ) { BooleanBuilder whereClause = new BooleanBuilder(); whereClause.and(getDefaultPredicateInCursorPagination(cursor)); - if (artistIds.isEmpty()) { - return whereClause; + if (status.equals(SubscriptionStatus.SUBSCRIBED)) { + return whereClause.and(artist.id.in(artistIds)); } - return whereClause.and(artist.id.in(artistIds)); + if (status.equals(SubscriptionStatus.UNSUBSCRIBED)) { + addConditionIfNotEmpty(whereClause, artist.id::notIn, artistIds); + addConditionIfNotEmpty(whereClause, genre.id::in, artistFilterDomain.genreIds()); + addConditionIfNotEmpty(whereClause, artist.artistGender::in, artistFilterDomain.artistGenders()); + addConditionIfNotEmpty(whereClause, artist.artistType::in, artistFilterDomain.artistTypes()); + } + + return whereClause; + } + + private BooleanBuilder getWhereClauseWithFilter( + ArtistFilterTotalCountDomainRequest request + ) { + BooleanBuilder whereClause = new BooleanBuilder(); + whereClause.and(artist.isDeleted.isFalse()); + + addConditionIfNotEmpty(whereClause, artist.id::notIn, request.artistIds()); + addConditionIfNotEmpty(whereClause, genre.id::in, request.genreIds()); + addConditionIfNotEmpty(whereClause, artist.artistGender::in, request.artistGenders()); + addConditionIfNotEmpty(whereClause, artist.artistType::in, request.artistTypes()); + + return whereClause; } private Predicate getDefaultPredicateInCursorPagination(UUID cursor) { @@ -160,6 +217,16 @@ private Predicate getDefaultPredicateInCursorPagination(UUID cursor) { return cursor == null ? defaultPredicate : artist.id.gt(cursor).and(defaultPredicate); } + private void addConditionIfNotEmpty( + BooleanBuilder whereClause, + Function, BooleanExpression> conditionFunction, + Collection values + ) { + if (!values.isEmpty()) { + whereClause.and(conditionFunction.apply(values)); + } + } + private OrderSpecifier getOrderSpecifier(ArtistSortStandardDomainType type) { return switch (type) { case KOREAN_NAME_ASC -> artist.koreanName.asc(); diff --git a/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepository.java b/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepository.java index ce677ede..a3435a7b 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepository.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepository.java @@ -1,9 +1,9 @@ package org.example.repository.artist.artistsearch; import org.example.dto.artist.request.ArtistSearchPaginationDomainRequest; -import org.example.dto.artist.response.ArtistDetailPaginationResponse; +import org.example.dto.artist.response.ArtistPaginationDomainResponse; public interface ArtistSearchQuerydslRepository { - ArtistDetailPaginationResponse searchArtist(ArtistSearchPaginationDomainRequest request); + ArtistPaginationDomainResponse searchArtist(ArtistSearchPaginationDomainRequest request); } diff --git a/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepositoryImpl.java b/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepositoryImpl.java index 01220b7d..053f983a 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepositoryImpl.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/artist/artistsearch/ArtistSearchQuerydslRepositoryImpl.java @@ -12,8 +12,8 @@ import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.artist.request.ArtistSearchPaginationDomainRequest; -import org.example.dto.artist.response.ArtistDetailPaginationResponse; -import org.example.dto.artist.response.SimpleArtistResponse; +import org.example.dto.artist.response.ArtistPaginationDomainResponse; +import org.example.dto.artist.response.ArtistSimpleDomainResponse; import org.example.util.SliceUtil; import org.example.vo.ArtistSortStandardDomainType; import org.springframework.data.domain.Slice; @@ -25,11 +25,11 @@ public class ArtistSearchQuerydslRepositoryImpl implements ArtistSearchQuerydslR private final JPAQueryFactory jpaQueryFactory; - public ArtistDetailPaginationResponse searchArtist( + public ArtistPaginationDomainResponse searchArtist( ArtistSearchPaginationDomainRequest request) { - List result = jpaQueryFactory.select( + List result = jpaQueryFactory.select( Projections.constructor( - SimpleArtistResponse.class, + ArtistSimpleDomainResponse.class, artist.id, artist.koreanName, artist.englishName, @@ -43,12 +43,12 @@ public ArtistDetailPaginationResponse searchArtist( .orderBy(getOrderSpecifier(request.sortStandard())) .fetch(); - Slice simpleArtistSlices = SliceUtil.makeSlice( + Slice simpleArtistSlices = SliceUtil.makeSlice( request.size(), result ); - return ArtistDetailPaginationResponse.builder() + return ArtistPaginationDomainResponse.builder() .data(simpleArtistSlices.getContent()) .hasNext(simpleArtistSlices.hasNext()) .build(); diff --git a/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepository.java b/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepository.java index 9b99dcc6..c516bcdc 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepository.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepository.java @@ -2,15 +2,15 @@ import java.util.List; import java.util.UUID; -import org.example.dto.genre.request.GenreSubscriptionPaginationDomainRequest; -import org.example.dto.genre.response.GenreSubscriptionPaginationDomainResponse; +import org.example.dto.genre.request.GenrePaginationDomainRequest; +import org.example.dto.genre.response.GenrePaginationDomainResponse; import org.example.entity.genre.Genre; public interface GenreQuerydslRepository { List findAllInId(List ids); - GenreSubscriptionPaginationDomainResponse findAllWithCursorPagination( - GenreSubscriptionPaginationDomainRequest request + GenrePaginationDomainResponse findAllWithCursorPagination( + GenrePaginationDomainRequest request ); } diff --git a/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepositoryImpl.java b/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepositoryImpl.java index 3e772365..c850495d 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepositoryImpl.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/genre/GenreQuerydslRepositoryImpl.java @@ -10,11 +10,12 @@ import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.example.dto.genre.request.GenreSubscriptionPaginationDomainRequest; -import org.example.dto.genre.response.GenreSubscriptionDomainResponse; -import org.example.dto.genre.response.GenreSubscriptionPaginationDomainResponse; +import org.example.dto.genre.request.GenrePaginationDomainRequest; +import org.example.dto.genre.response.GenreDomainResponse; +import org.example.dto.genre.response.GenrePaginationDomainResponse; import org.example.entity.genre.Genre; import org.example.util.SliceUtil; +import org.example.vo.SubscriptionStatus; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Repository; @@ -33,41 +34,50 @@ public List findAllInId(List ids) { } @Override - public GenreSubscriptionPaginationDomainResponse findAllWithCursorPagination( - GenreSubscriptionPaginationDomainRequest request + public GenrePaginationDomainResponse findAllWithCursorPagination( + GenrePaginationDomainRequest request ) { - List genreSubscribeResponses = jpaQueryFactory.select( + List genreUnsubscriptionResponses = jpaQueryFactory.select( Projections.constructor( - GenreSubscriptionDomainResponse.class, + GenreDomainResponse.class, genre.id, genre.name ) - ).from(genre) - .where(getWhereClauseInCursorPagination(request.cursor(), request.genreIds())) + ) + .from(genre) + .where( + getWhereClauseInCursorPagination( + request.subscriptionStatus(), + request.cursor(), + request.genreIds() + ) + ) .limit(request.size() + 1) .fetch(); - Slice genreSubscribeSlices = SliceUtil.makeSlice(request.size(), - genreSubscribeResponses); + Slice genreUnSubscribeSlices = SliceUtil.makeSlice( + request.size(), + genreUnsubscriptionResponses + ); - return GenreSubscriptionPaginationDomainResponse.builder() - .data(genreSubscribeSlices.getContent()) - .hasNext(genreSubscribeSlices.hasNext()) + return GenrePaginationDomainResponse.builder() + .data(genreUnSubscribeSlices.getContent()) + .hasNext(genreUnSubscribeSlices.hasNext()) .build(); } private BooleanBuilder getWhereClauseInCursorPagination( + SubscriptionStatus status, UUID cursor, List genreIds ) { BooleanBuilder whereClause = new BooleanBuilder(); whereClause.and(getDefaultPredicateInCursorPagination(cursor)); - if (genreIds.isEmpty()) { - return whereClause; + if (status.equals(SubscriptionStatus.SUBSCRIBED)) { + return whereClause.and(genre.id.in(genreIds)); } - - return whereClause.and(genre.id.in(genreIds)); + return whereClause.and(genre.id.notIn(genreIds)); } private Predicate getDefaultPredicateInCursorPagination(UUID cursor) { diff --git a/app/domain/show-domain/src/main/java/org/example/repository/show/ShowQuerydslRepositoryImpl.java b/app/domain/show-domain/src/main/java/org/example/repository/show/ShowQuerydslRepositoryImpl.java index fd1c4c96..1c12f5f9 100644 --- a/app/domain/show-domain/src/main/java/org/example/repository/show/ShowQuerydslRepositoryImpl.java +++ b/app/domain/show-domain/src/main/java/org/example/repository/show/ShowQuerydslRepositoryImpl.java @@ -17,9 +17,9 @@ import java.util.UUID; import lombok.RequiredArgsConstructor; import org.example.dto.artist.response.ArtistDomainResponse; -import org.example.dto.artist.response.ArtistKoreanNameResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; import org.example.dto.genre.response.GenreDomainResponse; -import org.example.dto.genre.response.GenreNameResponse; +import org.example.dto.genre.response.GenreNameDomainResponse; import org.example.dto.show.response.ShowDetailDomainResponse; import org.example.dto.show.response.ShowDomainResponse; import org.example.dto.show.response.ShowInfoDomainResponse; @@ -97,14 +97,14 @@ public List findAllShowInfos() { show.ticketingSites, set( Projections.constructor( - ArtistKoreanNameResponse.class, + ArtistKoreanNameDomainResponse.class, artist.id, artist.koreanName ) ), set( Projections.constructor( - GenreNameResponse.class, + GenreNameDomainResponse.class, genre.id, genre.name ) @@ -134,14 +134,14 @@ public Optional findShowInfoById(UUID id) { show.ticketingSites, set( Projections.constructor( - ArtistKoreanNameResponse.class, + ArtistKoreanNameDomainResponse.class, artist.id, artist.koreanName ) ), set( Projections.constructor( - GenreNameResponse.class, + GenreNameDomainResponse.class, genre.id, genre.name ) diff --git a/app/domain/show-domain/src/main/java/org/example/usecase/artist/ArtistUseCase.java b/app/domain/show-domain/src/main/java/org/example/usecase/artist/ArtistUseCase.java index 30df0ef2..44cbb67a 100644 --- a/app/domain/show-domain/src/main/java/org/example/usecase/artist/ArtistUseCase.java +++ b/app/domain/show-domain/src/main/java/org/example/usecase/artist/ArtistUseCase.java @@ -4,18 +4,18 @@ import java.util.NoSuchElementException; import java.util.UUID; import lombok.RequiredArgsConstructor; +import org.example.dto.artist.request.ArtistFilterTotalCountDomainRequest; import org.example.dto.artist.request.ArtistPaginationDomainRequest; import org.example.dto.artist.request.ArtistSearchPaginationDomainRequest; -import org.example.dto.artist.response.ArtistDetailPaginationResponse; -import org.example.dto.artist.response.ArtistDetailResponse; -import org.example.dto.artist.response.ArtistKoreanNameResponse; +import org.example.dto.artist.response.ArtistDetailDomainResponse; +import org.example.dto.artist.response.ArtistFilterTotalCountDomainResponse; +import org.example.dto.artist.response.ArtistKoreanNameDomainResponse; +import org.example.dto.artist.response.ArtistPaginationDomainResponse; import org.example.entity.BaseEntity; import org.example.entity.artist.Artist; import org.example.entity.artist.ArtistGenre; import org.example.entity.artist.ArtistSearch; import org.example.entity.show.ShowArtist; -import org.example.error.ArtistError; -import org.example.exception.BusinessException; import org.example.repository.artist.ArtistRepository; import org.example.repository.artist.artistgenre.ArtistGenreRepository; import org.example.repository.artist.artistsearch.ArtistSearchRepository; @@ -43,34 +43,43 @@ public void save(Artist artist, List genreIds) { artistGenreRepository.saveAll(artistGenres); } - public List findAllWithGenreNames() { + public List findAllWithGenreNames() { return artistRepository.findAllWithGenreNames(); } - public List findAllArtistKoreanName() { + public List findAllArtistKoreanName() { return artistRepository.findAllArtistKoreanName(); } - public ArtistDetailResponse findArtistDetailById(UUID id) { + public ArtistDetailDomainResponse findArtistDetailById(UUID id) { return artistRepository.findArtistWithGenreNamesById(id) - .orElseThrow(() -> new BusinessException(ArtistError.ENTITY_NOT_FOUND_ERROR)); + .orElseThrow(NoSuchElementException::new); } public List findAllArtistInIds(List ids) { return artistRepository.findAllInIds(ids); } - public ArtistDetailPaginationResponse findAllArtistInCursorPagination( - ArtistPaginationDomainRequest request) { + public ArtistPaginationDomainResponse findAllArtistInCursorPagination( + ArtistPaginationDomainRequest request + ) { return artistRepository.findAllWithCursorPagination(request); } + public ArtistFilterTotalCountDomainResponse findFilterArtistTotalCount( + ArtistFilterTotalCountDomainRequest request + ) { + return artistRepository.findFilterArtistTotalCount(request) + .orElseThrow(NoSuchElementException::new); + } + @Transactional public void updateArtist(UUID id, Artist newArtist, List newGenreIds) { Artist artist = findArtistById(id); artist.changeArtistInfo(newArtist); - List currentGenres = artistGenreRepository.findAllByArtistIdAndIsDeletedFalse(artist.getId()); + List currentGenres = artistGenreRepository.findAllByArtistIdAndIsDeletedFalse( + artist.getId()); List currentGenreIds = currentGenres.stream() .map(ArtistGenre::getGenreId) @@ -93,18 +102,22 @@ public void deleteArtist(UUID id) { Artist artist = findArtistById(id); artist.softDelete(); - List artistGenres = artistGenreRepository.findAllByArtistIdAndIsDeletedFalse(artist.getId()); + List artistGenres = artistGenreRepository.findAllByArtistIdAndIsDeletedFalse( + artist.getId()); artistGenres.forEach(BaseEntity::softDelete); - List showArtists = showArtistRepository.findAllByArtistIdAndIsDeletedFalse(artist.getId()); + List showArtists = showArtistRepository.findAllByArtistIdAndIsDeletedFalse( + artist.getId()); showArtists.forEach(BaseEntity::softDelete); - List artistSearches = artistSearchRepository.findAllByArtistIdAndIsDeletedFalse(artist.getId()); + List artistSearches = artistSearchRepository.findAllByArtistIdAndIsDeletedFalse( + artist.getId()); artistSearches.forEach(BaseEntity::softDelete); } - public ArtistDetailPaginationResponse searchArtist( - ArtistSearchPaginationDomainRequest request) { + public ArtistPaginationDomainResponse searchArtist( + ArtistSearchPaginationDomainRequest request + ) { return artistSearchRepository.searchArtist(request); } diff --git a/app/domain/show-domain/src/main/java/org/example/usecase/genre/GenreUseCase.java b/app/domain/show-domain/src/main/java/org/example/usecase/genre/GenreUseCase.java index 0746e90a..1b27e9c8 100644 --- a/app/domain/show-domain/src/main/java/org/example/usecase/genre/GenreUseCase.java +++ b/app/domain/show-domain/src/main/java/org/example/usecase/genre/GenreUseCase.java @@ -1,16 +1,15 @@ package org.example.usecase.genre; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.example.dto.genre.request.GenreSubscriptionPaginationDomainRequest; -import org.example.dto.genre.response.GenreSubscriptionPaginationDomainResponse; +import org.example.dto.genre.request.GenrePaginationDomainRequest; +import org.example.dto.genre.response.GenrePaginationDomainResponse; import org.example.entity.BaseEntity; import org.example.entity.artist.ArtistGenre; import org.example.entity.genre.Genre; import org.example.entity.show.ShowGenre; -import org.example.error.GenreError; -import org.example.exception.BusinessException; import org.example.repository.artist.artistgenre.ArtistGenreRepository; import org.example.repository.genre.GenreRepository; import org.example.repository.show.showgenre.ShowGenreRepository; @@ -45,24 +44,25 @@ public void deleteGenre(UUID id) { Genre genre = findGenreById(id); genre.softDelete(); - List artistGenres = artistGenreRepository.findAllByGenreIdAndIsDeletedFalse(genre.getId()); + List artistGenres = artistGenreRepository.findAllByGenreIdAndIsDeletedFalse( + genre.getId()); artistGenres.forEach(BaseEntity::softDelete); - List showGenres = showGenreRepository.findAllByGenreIdAndIsDeletedFalse(genre.getId()); + List showGenres = showGenreRepository.findAllByGenreIdAndIsDeletedFalse( + genre.getId()); showGenres.forEach(BaseEntity::softDelete); } public Genre findGenreById(UUID id) { - return genreRepository.findById(id) - .orElseThrow(() -> new BusinessException(GenreError.ENTITY_NOT_FOUND_ERROR)); + return genreRepository.findById(id).orElseThrow(NoSuchElementException::new); } public List findAllGenresInIds(List genreIds) { return genreRepository.findAllInId(genreIds); } - public GenreSubscriptionPaginationDomainResponse findGenreSubscriptionsWithCursorPagination( - GenreSubscriptionPaginationDomainRequest request + public GenrePaginationDomainResponse findGenreWithCursorPagination( + GenrePaginationDomainRequest request ) { return genreRepository.findAllWithCursorPagination(request); } diff --git a/app/domain/show-domain/src/main/java/org/example/usecase/show/ShowUseCase.java b/app/domain/show-domain/src/main/java/org/example/usecase/show/ShowUseCase.java index 87a45a2b..1918c2aa 100644 --- a/app/domain/show-domain/src/main/java/org/example/usecase/show/ShowUseCase.java +++ b/app/domain/show-domain/src/main/java/org/example/usecase/show/ShowUseCase.java @@ -15,8 +15,6 @@ import org.example.entity.show.ShowGenre; import org.example.entity.show.ShowSearch; import org.example.entity.show.ShowTicketingTime; -import org.example.error.ShowError; -import org.example.exception.BusinessException; import org.example.repository.show.ShowRepository; import org.example.repository.show.ShowTicketingTimeRepository; import org.example.repository.show.showartist.ShowArtistRepository; @@ -40,16 +38,16 @@ public void save( ShowCreationDomainRequest request ) { Show show = request.toShow(); - showRepository.save(request.toShow()); - showSearchRepository.save(request.toShowSearch(show)); + showRepository.save(show); + showSearchRepository.save(show.toShowSearch()); - List showArtists = request.toShowArtist(show); + List showArtists = show.toShowArtist(request.artistIds()); showArtistRepository.saveAll(showArtists); - List showGenres = request.toShowGenre(show); + List showGenres = show.toShowGenre(request.genreIds()); showGenreRepository.saveAll(showGenres); - List showTicketingTimes = request.toShowTicketing(show); + List showTicketingTimes = show.toShowTicketingTime(request.showTicketingTimes()); showTicketingTimeRepository.saveAll(showTicketingTimes); } @@ -58,18 +56,16 @@ public List findAllShowInfos() { } public ShowDetailDomainResponse findShowDetail(UUID id) { - return showRepository.findShowDetailById(id) - .orElseThrow(NoSuchElementException::new); + return showRepository.findShowDetailById(id).orElseThrow(NoSuchElementException::new); } public ShowInfoDomainResponse findShowInfo(UUID id) { - return showRepository.findShowInfoById(id) - .orElseThrow(() -> new BusinessException(ShowError.ENTITY_NOT_FOUND_ERROR)); + return showRepository.findShowInfoById(id).orElseThrow(NoSuchElementException::new); } @Transactional public void updateShow(UUID id, Show newShow, List newArtistIds, List newGenreIds) { - Show show = findShowOrThrowNoSuchElementException(id); + Show show = findShowById(id); show.changeShowInfo(newShow); updateShowArtist(newArtistIds, show); @@ -116,16 +112,19 @@ private void updateShowGenre(List newGenreIds, Show show) { @Transactional public void deleteShow(UUID id) { - Show show = findShowOrThrowNoSuchElementException(id); + Show show = findShowById(id); show.softDelete(); - List showArtists = showArtistRepository.findAllByShowIdAndIsDeletedFalse(show.getId()); + List showArtists = showArtistRepository.findAllByShowIdAndIsDeletedFalse( + show.getId()); showArtists.forEach(BaseEntity::softDelete); - List showGenres = showGenreRepository.findAllByShowIdAndIsDeletedFalse(show.getId()); + List showGenres = showGenreRepository.findAllByShowIdAndIsDeletedFalse( + show.getId()); showGenres.forEach(BaseEntity::softDelete); - List showSearches = showSearchRepository.findAllByShowIdAndIsDeletedFalse(show.getId()); + List showSearches = showSearchRepository.findAllByShowIdAndIsDeletedFalse( + show.getId()); showSearches.forEach(BaseEntity::softDelete); } @@ -135,7 +134,7 @@ public ShowSearchPaginationDomainResponse searchShow( } - private Show findShowOrThrowNoSuchElementException(UUID id) { + private Show findShowById(UUID id) { return showRepository.findById(id).orElseThrow(NoSuchElementException::new); } } diff --git a/app/domain/show-domain/src/main/java/org/example/vo/SubscriptionStatus.java b/app/domain/show-domain/src/main/java/org/example/vo/SubscriptionStatus.java new file mode 100644 index 00000000..3d7fae63 --- /dev/null +++ b/app/domain/show-domain/src/main/java/org/example/vo/SubscriptionStatus.java @@ -0,0 +1,6 @@ +package org.example.vo; + +public enum SubscriptionStatus { + SUBSCRIBED, + UNSUBSCRIBED +} diff --git a/app/domain/show-domain/src/test/java/org/example/domain/ShowTest.java b/app/domain/show-domain/src/test/java/org/example/domain/ShowTest.java index 55105594..fb5b6ff2 100644 --- a/app/domain/show-domain/src/test/java/org/example/domain/ShowTest.java +++ b/app/domain/show-domain/src/test/java/org/example/domain/ShowTest.java @@ -7,7 +7,7 @@ import org.example.entity.show.Show; import org.example.entity.show.ShowArtist; import org.example.entity.show.ShowGenre; -import org.example.fixture.ShowFixture; +import org.example.fixture.domain.ShowFixture; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/app/domain/show-domain/src/test/java/org/example/fixture/ArtistFixture.java b/app/domain/show-domain/src/test/java/org/example/fixture/ArtistFixture.java deleted file mode 100644 index 5d6a7e5e..00000000 --- a/app/domain/show-domain/src/test/java/org/example/fixture/ArtistFixture.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.example.fixture; - -import org.example.entity.artist.Artist; -import org.example.vo.ArtistGender; -import org.example.vo.ArtistType; - -public class ArtistFixture { - - public static Artist womanGroup() { - return Artist.builder() - .koreanName("아이브") - .englishName("IVE") - .image("abc") - .country("KOREA") - .artistGender(ArtistGender.WOMAN) - .artistType(ArtistType.GROUP) - .build(); - } -} diff --git a/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistRepositoryTest.java b/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistRepositoryTest.java index 793d4365..556f978d 100644 --- a/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistRepositoryTest.java +++ b/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistRepositoryTest.java @@ -6,8 +6,18 @@ import java.util.List; import java.util.UUID; import org.example.QueryTest; +import org.example.dto.artist.request.ArtistFilterDomain; import org.example.entity.artist.Artist; -import org.example.fixture.ArtistFixture; +import org.example.fixture.domain.ArtistFixture; +import org.example.fixture.domain.ArtistGenreFixture; +import org.example.fixture.domain.GenreFixture; +import org.example.fixture.dto.ArtistRequestDtoFixture; +import org.example.repository.artist.artistgenre.ArtistGenreRepository; +import org.example.repository.genre.GenreRepository; +import org.example.vo.ArtistGender; +import org.example.vo.ArtistSortStandardDomainType; +import org.example.vo.ArtistType; +import org.example.vo.SubscriptionStatus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -19,6 +29,12 @@ class ArtistRepositoryTest extends QueryTest { @Autowired private ArtistRepository artistRepository; + @Autowired + private ArtistGenreRepository artistGenreRepository; + + @Autowired + private GenreRepository genreRepository; + @Test @DisplayName("findAllInIds 메서드가 의도대로 동작하는지 확인한다.") void find_all_in() { @@ -52,4 +68,183 @@ void find_all_in_no_match() { assertThat(findArtists).hasSize(0); } + + @Test + @DisplayName("구독한 아티스트 중 ID가 존재하는 아티스트 목록을 반환한다.") + void findArtistSubscriptionExistArtistIds() { + //given + int artistSize = 3; + var artists = ArtistFixture.manSoloArtists(artistSize); + artistRepository.saveAll(artists); + + int size = 5; + var request = ArtistRequestDtoFixture.artistPaginationDomainRequest( + SubscriptionStatus.SUBSCRIBED, + size, + ArtistSortStandardDomainType.ENGLISH_NAME_ASC, + null, + artists.stream().map(Artist::getId).toList(), + ArtistFilterDomain.defaultArtistFilterDomain() + ); + + //when + var result = artistRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data().size()).isEqualTo(artistSize); + } + + @Test + @DisplayName("구독한 아티스트 ID를 제외하고 구독하지 않은 아티스트 목록을 반환한다.") + void findArtistUnsubscriptionExceptSubscribedArtistIds() { + //given + int artistSize = 3; + var artists = ArtistFixture.manSoloArtists(artistSize); + artistRepository.saveAll(artists); + + int size = 5; + var request = ArtistRequestDtoFixture.artistPaginationDomainRequest( + SubscriptionStatus.UNSUBSCRIBED, + size, + ArtistSortStandardDomainType.ENGLISH_NAME_ASC, + null, + artists.stream().map(Artist::getId).toList(), + ArtistFilterDomain.defaultArtistFilterDomain() + ); + + //when + var result = artistRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data()).isEmpty(); + } + + @Test + @DisplayName("구독한 아티스트가 없다면 저장되어 있는 아티스트 목록을 반환한다.") + void findArtistUnsubscriptionWithNoneSubscriptionArtistIds() { + //given + int artistSize = 3; + var artists = ArtistFixture.manSoloArtists(artistSize); + artistRepository.saveAll(artists); + + int size = 5; + var request = ArtistRequestDtoFixture.artistPaginationDomainRequest( + SubscriptionStatus.UNSUBSCRIBED, + size, + ArtistSortStandardDomainType.ENGLISH_NAME_ASC, + null, + List.of(), + ArtistFilterDomain.defaultArtistFilterDomain() + ); + + //when + var result = artistRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data().size()).isEqualTo(artistSize); + } + + @Test + @DisplayName("구독한 아티스트가 없다면 필터링 후 구독하지 않은 아티스트 목록을 반환한다.") + void findArtisNoneSubscribedArtistIdsWithFilter() { + int artistSize = 3; + var artists = ArtistFixture.manSoloArtists(artistSize); + artistRepository.saveAll(artists); + + var genres = GenreFixture.genres(1); + genreRepository.saveAll(genres); + + artists.forEach( + artist -> artistGenreRepository.save( + ArtistGenreFixture.artistGenre(artist.getId(), genres.get(0).getId()) + ) + ); + + int size = 5; + var request = ArtistRequestDtoFixture.artistPaginationDomainRequest( + SubscriptionStatus.UNSUBSCRIBED, + size, + ArtistSortStandardDomainType.ENGLISH_NAME_ASC, + null, + List.of(), + ArtistFilterDomain.builder() + .artistGenders(List.of(ArtistGender.MAN)) + .artistTypes(List.of(ArtistType.SOLO)) + .genreIds(List.of()) + .build() + ); + + //when + var result = artistRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data().size()).isEqualTo(artistSize); + } + + @Test + @DisplayName("구독한 아티스트 ID를 제외하고 구독하지 않은 아티스트 목록을 필터링한다.") + void findArtistExceptSubscribedArtistIdsWithFilter() { + int artistSize = 3; + var artists = ArtistFixture.manSoloArtists(artistSize); + artistRepository.saveAll(artists); + + var genres = GenreFixture.genres(1); + genreRepository.saveAll(genres); + + artists.forEach( + artist -> artistGenreRepository.save( + ArtistGenreFixture.artistGenre(artist.getId(), genres.get(0).getId()) + ) + ); + + int size = 5; + var request = ArtistRequestDtoFixture.artistPaginationDomainRequest( + SubscriptionStatus.UNSUBSCRIBED, + size, + ArtistSortStandardDomainType.ENGLISH_NAME_ASC, + null, + artists.stream().map(Artist::getId).toList(), + ArtistFilterDomain.builder() + .artistGenders(List.of(ArtistGender.MAN, ArtistGender.WOMAN)) + .artistTypes(List.of(ArtistType.SOLO, ArtistType.GROUP)) + .genreIds(List.of()) + .build() + ); + + //when + var result = artistRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data()).isEmpty(); + } + + @Test + @DisplayName("구독한 아티스트가 없다면 필터링 후 구독하지 않은 아티스트 총 개수를 반환한다.") + void findArtisTotalCountNoneSubscribedArtistIds() { + int artistSize = 3; + var artists = ArtistFixture.manSoloArtists(artistSize); + artistRepository.saveAll(artists); + + var genres = GenreFixture.genres(1); + genreRepository.saveAll(genres); + + artists.forEach( + artist -> artistGenreRepository.save( + ArtistGenreFixture.artistGenre(artist.getId(), genres.get(0).getId()) + ) + ); + + var request = ArtistRequestDtoFixture.artistFilterTotalCountDomainRequest( + List.of(ArtistGender.MAN, ArtistGender.WOMAN), + List.of(ArtistType.SOLO, ArtistType.GROUP), + List.of(), + List.of() + ); + + //when + var result = artistRepository.findFilterArtistTotalCount(request).orElseThrow(); + + //then + assertThat(result.totalCount()).isEqualTo(artistSize); + } } \ No newline at end of file diff --git a/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistSearchRepositoryTest.java b/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistSearchRepositoryTest.java index e17afd19..817d99ee 100644 --- a/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistSearchRepositoryTest.java +++ b/app/domain/show-domain/src/test/java/org/example/repository/artist/ArtistSearchRepositoryTest.java @@ -5,10 +5,9 @@ import java.util.List; import org.example.QueryTest; import org.example.dto.artist.request.ArtistSearchPaginationDomainRequest; -import org.example.dto.artist.response.ArtistDetailPaginationResponse; import org.example.entity.artist.Artist; import org.example.entity.artist.ArtistSearch; -import org.example.fixture.ArtistFixture; +import org.example.fixture.domain.ArtistFixture; import org.example.repository.artist.artistsearch.ArtistSearchRepository; import org.example.util.StringNormalizer; import org.example.vo.ArtistSortStandardDomainType; @@ -44,7 +43,7 @@ void searchArtistByKoreanNameAndEnglishName(String search) { .build(); //when - ArtistDetailPaginationResponse result = artistSearchRepository.searchArtist(request); + var result = artistSearchRepository.searchArtist(request); assertThat(result.data()).isNotEmpty(); } diff --git a/app/domain/show-domain/src/test/java/org/example/repository/genre/GenreRepositoryTest.java b/app/domain/show-domain/src/test/java/org/example/repository/genre/GenreRepositoryTest.java index cc12c3bb..4788a192 100644 --- a/app/domain/show-domain/src/test/java/org/example/repository/genre/GenreRepositoryTest.java +++ b/app/domain/show-domain/src/test/java/org/example/repository/genre/GenreRepositoryTest.java @@ -7,7 +7,9 @@ import java.util.UUID; import org.example.QueryTest; import org.example.entity.genre.Genre; -import org.example.fixture.GenreFixture; +import org.example.fixture.domain.GenreFixture; +import org.example.fixture.dto.GenreRequestDtoFixture; +import org.example.vo.SubscriptionStatus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -39,7 +41,6 @@ void find_all_in() { @Test @DisplayName("findAllInIds 메서드가 매칭되는 ID가 없을 때 빈 리스트를 반환한다.") void find_all_in_no_match() { - ArrayList ids = new ArrayList<>(); ids.add(UUID.randomUUID()); @@ -49,4 +50,72 @@ void find_all_in_no_match() { assertThat(findGenres).hasSize(0); } + + @Test + @DisplayName("구독한 장르 중 ID가 존재하는 장르 목록을 반환한다.") + void findGenreSubscriptionExistGenreIds() { + //given + int genreSize = 2; + var genres = GenreFixture.genres(genreSize); + genreRepository.saveAll(genres); + + var request = GenreRequestDtoFixture.genrePaginationDomainRequest( + SubscriptionStatus.SUBSCRIBED, + null, + 3, + genres.stream().map(Genre::getId).toList() + ); + + //when + var result = genreRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data().size()).isEqualTo(genreSize); + } + + @Test + @DisplayName("구독한 장르가 없다면 저장되어 있는 장르 목록을 반환한다.") + void findGenreUnsubscriptionWithNoneSubscriptionGenreIds() { + //given + int genreSize = 2; + var genres = GenreFixture.genres(genreSize); + genreRepository.saveAll(genres); + + var request = GenreRequestDtoFixture.genrePaginationDomainRequest( + SubscriptionStatus.UNSUBSCRIBED, + null, + 3, + List.of() + ); + + //when + var result = genreRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data().size()).isEqualTo(genreSize); + } + + @Test + @DisplayName("구독한 장르가 있다면 제외하고 저장되어 있는 장르 목록을 반환한다.") + void findGenreUnsubscriptionExceptSubscriptionGenreIds() { + //given + int genreSize = 3; + var genres = GenreFixture.genres(genreSize); + genreRepository.saveAll(genres); + + var genreSubscriptionIds = genres.stream().map(Genre::getId).toList(); + var request = GenreRequestDtoFixture.genrePaginationDomainRequest( + SubscriptionStatus.UNSUBSCRIBED, + null, + 3, + genreSubscriptionIds + ); + + //when + var result = genreRepository.findAllWithCursorPagination(request); + + //then + assertThat(result.data()).isEmpty(); + } + } \ No newline at end of file diff --git a/app/domain/show-domain/src/test/java/org/example/repository/artist/ShowSearchRepositoryTest.java b/app/domain/show-domain/src/test/java/org/example/repository/show/ShowSearchRepositoryTest.java similarity index 92% rename from app/domain/show-domain/src/test/java/org/example/repository/artist/ShowSearchRepositoryTest.java rename to app/domain/show-domain/src/test/java/org/example/repository/show/ShowSearchRepositoryTest.java index fc873f21..d0c48561 100644 --- a/app/domain/show-domain/src/test/java/org/example/repository/artist/ShowSearchRepositoryTest.java +++ b/app/domain/show-domain/src/test/java/org/example/repository/show/ShowSearchRepositoryTest.java @@ -1,4 +1,4 @@ -package org.example.repository.artist; +package org.example.repository.show; import static org.assertj.core.api.Assertions.assertThat; @@ -7,8 +7,7 @@ import org.example.dto.show.response.ShowSearchPaginationDomainResponse; import org.example.entity.show.Show; import org.example.entity.show.ShowSearch; -import org.example.fixture.ShowFixture; -import org.example.repository.show.ShowRepository; +import org.example.fixture.domain.ShowFixture; import org.example.repository.show.showsearch.ShowSearchRepository; import org.example.util.StringNormalizer; import org.junit.jupiter.api.DisplayName; diff --git a/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ArtistFixture.java b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ArtistFixture.java new file mode 100644 index 00000000..137e737d --- /dev/null +++ b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ArtistFixture.java @@ -0,0 +1,35 @@ +package org.example.fixture.domain; + +import java.util.List; +import java.util.stream.IntStream; +import org.example.entity.artist.Artist; +import org.example.vo.ArtistGender; +import org.example.vo.ArtistType; + +public class ArtistFixture { + + public static Artist womanGroup() { + return Artist.builder() + .koreanName("아이브") + .englishName("IVE") + .image("abc") + .country("KOREA") + .artistGender(ArtistGender.WOMAN) + .artistType(ArtistType.GROUP) + .build(); + } + + public static List manSoloArtists(int size) { + return IntStream.range(0, size) + .mapToObj(i -> Artist.builder() + .koreanName("testKoreanName" + i) + .englishName("testEnglishName" + i) + .image("testImage" + i) + .country("testCountry" + i) + .artistGender(ArtistGender.MAN) + .artistType(ArtistType.SOLO) + .build() + ) + .toList(); + } +} diff --git a/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ArtistGenreFixture.java b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ArtistGenreFixture.java new file mode 100644 index 00000000..fefaec3e --- /dev/null +++ b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ArtistGenreFixture.java @@ -0,0 +1,18 @@ +package org.example.fixture.domain; + +import java.util.UUID; +import org.example.entity.artist.ArtistGenre; + +public class ArtistGenreFixture { + + public static ArtistGenre artistGenre( + UUID artistId, + UUID genreId + ) { + return ArtistGenre.builder() + .artistId(artistId) + .genreId(genreId) + .build(); + } + +} diff --git a/app/domain/show-domain/src/test/java/org/example/fixture/GenreFixture.java b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/GenreFixture.java similarity index 90% rename from app/domain/show-domain/src/test/java/org/example/fixture/GenreFixture.java rename to app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/GenreFixture.java index 301bafc2..7e9ff08a 100644 --- a/app/domain/show-domain/src/test/java/org/example/fixture/GenreFixture.java +++ b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/GenreFixture.java @@ -1,4 +1,4 @@ -package org.example.fixture; +package org.example.fixture.domain; import java.util.List; import java.util.stream.IntStream; diff --git a/app/domain/show-domain/src/test/java/org/example/fixture/ShowFixture.java b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ShowFixture.java similarity index 96% rename from app/domain/show-domain/src/test/java/org/example/fixture/ShowFixture.java rename to app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ShowFixture.java index 0e00e446..d6c1961c 100644 --- a/app/domain/show-domain/src/test/java/org/example/fixture/ShowFixture.java +++ b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/domain/ShowFixture.java @@ -1,4 +1,4 @@ -package org.example.fixture; +package org.example.fixture.domain; import java.time.LocalDate; import java.util.Map; diff --git a/app/domain/show-domain/src/testFixtures/java/org/example/fixture/dto/ArtistRequestDtoFixture.java b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/dto/ArtistRequestDtoFixture.java new file mode 100644 index 00000000..566cf059 --- /dev/null +++ b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/dto/ArtistRequestDtoFixture.java @@ -0,0 +1,47 @@ +package org.example.fixture.dto; + +import java.util.List; +import java.util.UUID; +import org.example.dto.artist.request.ArtistFilterDomain; +import org.example.dto.artist.request.ArtistFilterTotalCountDomainRequest; +import org.example.dto.artist.request.ArtistPaginationDomainRequest; +import org.example.vo.ArtistGender; +import org.example.vo.ArtistSortStandardDomainType; +import org.example.vo.ArtistType; +import org.example.vo.SubscriptionStatus; + +public class ArtistRequestDtoFixture { + + public static ArtistPaginationDomainRequest artistPaginationDomainRequest( + SubscriptionStatus subscriptionStatus, + int size, + ArtistSortStandardDomainType artistSortStandardDomainType, + UUID cursor, + List artistIds, + ArtistFilterDomain artistFilterDomain + ) { + return ArtistPaginationDomainRequest.builder() + .subscriptionStatus(subscriptionStatus) + .size(size) + .sortStandard(artistSortStandardDomainType) + .cursor(cursor) + .artistIds(artistIds) + .artistFilterDomain(artistFilterDomain) + .build(); + } + + public static ArtistFilterTotalCountDomainRequest artistFilterTotalCountDomainRequest( + List artistGenders, + List artistTypes, + List genreIds, + List artistIds + ) { + return ArtistFilterTotalCountDomainRequest.builder() + .artistGenders(artistGenders) + .artistTypes(artistTypes) + .genreIds(genreIds) + .artistIds(artistIds) + .build(); + } + +} diff --git a/app/domain/show-domain/src/testFixtures/java/org/example/fixture/dto/GenreRequestDtoFixture.java b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/dto/GenreRequestDtoFixture.java new file mode 100644 index 00000000..c6871d60 --- /dev/null +++ b/app/domain/show-domain/src/testFixtures/java/org/example/fixture/dto/GenreRequestDtoFixture.java @@ -0,0 +1,23 @@ +package org.example.fixture.dto; + +import java.util.List; +import java.util.UUID; +import org.example.dto.genre.request.GenrePaginationDomainRequest; +import org.example.vo.SubscriptionStatus; + +public class GenreRequestDtoFixture { + + public static GenrePaginationDomainRequest genrePaginationDomainRequest( + SubscriptionStatus status, + UUID cursor, + int size, + List genreIds + ) { + return GenrePaginationDomainRequest.builder() + .subscriptionStatus(status) + .cursor(cursor) + .size(size) + .genreIds(genreIds) + .build(); + } +} diff --git a/app/domain/user-domain/build.gradle b/app/domain/user-domain/build.gradle index 7d25aab2..77df5cdd 100644 --- a/app/domain/user-domain/build.gradle +++ b/app/domain/user-domain/build.gradle @@ -1,6 +1,16 @@ +plugins { + id "java-library" + id "java-test-fixtures" + id "maven-publish" +} + dependencies { implementation project(":app:domain:common-domain") // hypersistence utils implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.6' + //testFixtures + testFixturesImplementation 'org.springframework.boot:spring-boot-starter-test' + testFixturesImplementation project(":app:domain:common-domain") + testFixturesImplementation 'org.springframework.data:spring-data-jpa' } diff --git a/app/domain/user-domain/src/testFixtures/java/org/example/fixture/ArtistSubscriptionFixture.java b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/ArtistSubscriptionFixture.java new file mode 100644 index 00000000..2d09b369 --- /dev/null +++ b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/ArtistSubscriptionFixture.java @@ -0,0 +1,18 @@ +package org.example.fixture; + +import java.util.List; +import java.util.UUID; +import java.util.stream.IntStream; +import org.example.entity.ArtistSubscription; + +public class ArtistSubscriptionFixture { + + public static List artistSubscriptions(int count) { + return IntStream.range(0, count) + .mapToObj(i -> ArtistSubscription.builder() + .artistId(UUID.randomUUID()) + .userId(UUID.randomUUID()) + .build()) + .toList(); + } +} diff --git a/app/domain/user-domain/src/testFixtures/java/org/example/fixture/GenreSubscriptionFixture.java b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/GenreSubscriptionFixture.java new file mode 100644 index 00000000..2ba5fed4 --- /dev/null +++ b/app/domain/user-domain/src/testFixtures/java/org/example/fixture/GenreSubscriptionFixture.java @@ -0,0 +1,18 @@ +package org.example.fixture; + +import java.util.List; +import java.util.UUID; +import java.util.stream.IntStream; +import org.example.entity.GenreSubscription; + +public class GenreSubscriptionFixture { + + public static List genreSubscriptions(int count) { + return IntStream.range(0, count) + .mapToObj(i -> GenreSubscription.builder() + .genreId(UUID.randomUUID()) + .userId(UUID.randomUUID()) + .build()) + .toList(); + } +} diff --git a/common/src/main/java/org/example/util/ValidateStatus.java b/common/src/main/java/org/example/util/ValidateStatus.java new file mode 100644 index 00000000..e99197cc --- /dev/null +++ b/common/src/main/java/org/example/util/ValidateStatus.java @@ -0,0 +1,11 @@ +package org.example.util; + +import java.util.List; + +public class ValidateStatus { + + public static List checkNullOrEmpty(List list) { + return (list == null || list.isEmpty()) ? List.of() : list; + } + +}