From 933a7e00ac092590f02bc9571698e98c8a8b9c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AF=BC=EC=B0=AC=EA=B8=B0?= Date: Mon, 12 Aug 2024 07:55:25 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B3=B5=EC=97=B0=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/example/config/SecurityConfig.java | 2 + .../show/controller/ShowController.java | 13 +- .../dto/request/ShowPaginationApiRequest.java | 8 +- .../dto/response/ShowInfoApiResponse.java | 2 + .../dto/response/ShowPaginationApiParam.java | 6 +- .../com/example/show/service/ShowService.java | 4 + .../request/ShowPaginationServiceRequest.java | 6 +- .../dto/response/ShowInfoServiceResponse.java | 3 + .../ShowPaginationServiceResponse.java | 2 + .../main/java/org/example/util/SliceUtil.java | 5 +- .../common-domain/src/main/resources/data.sql | 3 +- .../src/main/resources/schema.sql | 22 ++- .../request/ShowPaginationDomainRequest.java | 3 +- .../response/ShowDetailDomainResponse.java | 2 + .../dto/show/response/ShowDomainResponse.java | 1 + .../java/org/example/entity/show/Show.java | 8 ++ .../show/ShowQuerydslRepositoryImpl.java | 128 ++++++++++++------ .../org/example/usecase/show/ShowUseCase.java | 6 + .../java/org/example/util/DateTimeUtil.java | 4 + 19 files changed, 167 insertions(+), 61 deletions(-) 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 c7761eae..45c2fbf6 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 @@ -62,6 +62,7 @@ private CorsConfigurationSource corsConfigurationSource() { private RequestMatcher getMatcherForAnyone() { return RequestMatchers.anyOf( + antMatcher("/health"), antMatcher("/swagger-ui/**"), antMatcher("/v3/api-docs/**"), antMatcher("/admin/**"), @@ -75,6 +76,7 @@ private RequestMatcher getMatcherForAnyone() { antMatcher(HttpMethod.GET, "/api/v1/genres"), antMatcher(HttpMethod.GET, "/api/v1/shows"), antMatcher(HttpMethod.GET, "/api/v1/shows/{showId}"), + antMatcher(HttpMethod.PATCH, "/api/v1/shows/{showId}/view"), antMatcher(HttpMethod.GET, "/api/v1/artists/search/**"), antMatcher(HttpMethod.GET, "/api/v1/shows/search/**"), antMatcher(HttpMethod.GET, "/api/v1/artists/filter"), diff --git a/app/api/show-api/src/main/java/com/example/show/controller/ShowController.java b/app/api/show-api/src/main/java/com/example/show/controller/ShowController.java index edda691e..7e7f376e 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/ShowController.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/ShowController.java @@ -25,6 +25,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -57,8 +58,8 @@ public ResponseEntity> getShows( return ResponseEntity.ok( PaginationApiResponse.builder() - .data(data) - .hasNext(response.hasNext()) + .data(data) + .hasNext(response.hasNext()) .build() ); @@ -135,4 +136,12 @@ public ResponseEntity> searc .build() ); } + + @PatchMapping("/{showId}/view") + @Operation(summary = "공연 조회수 증가") + public void view( + @PathVariable("showId") UUID showId + ) { + showService.view(showId); + } } diff --git a/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowPaginationApiRequest.java b/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowPaginationApiRequest.java index 790a2bd0..ae8cf6b8 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowPaginationApiRequest.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/dto/request/ShowPaginationApiRequest.java @@ -17,7 +17,10 @@ public record ShowPaginationApiRequest( boolean onlyOpenSchedule, @Parameter(description = "이전 페이지네이션 마지막 데이터의 ID / 최초 조회라면 null") - UUID cursor, + UUID cursorId, + + @Parameter(description = "이전 페이지네이션 마지막 데이터의 정렬 필드 값 (RECENT: reservationAt, POPULAR: viewCount) / 최초 조회라면 null") + UUID cursorValue, @Parameter(required = true, description = "조회하려는 데이터 개수") int size @@ -26,7 +29,8 @@ public record ShowPaginationApiRequest( public ShowPaginationServiceRequest toServiceRequest(LocalDateTime now) { return ShowPaginationServiceRequest.builder() .sort(sort) - .cursor(cursor) + .cursorId(cursorId) + .cursorValue(cursorValue) .size(size) .now(now) .build(); diff --git a/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowInfoApiResponse.java b/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowInfoApiResponse.java index d0405e5d..3309ede8 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowInfoApiResponse.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowInfoApiResponse.java @@ -16,6 +16,7 @@ public record ShowInfoApiResponse( LocalDate endDate, String location, String image, + int viewCount, ShowSeatApiResponse seatInfoApiResponse, ShowTicketingSiteApiResponse ticketingSiteApiResponse, List ticketingTimes, @@ -32,6 +33,7 @@ public ShowInfoApiResponse(ShowInfoServiceResponse showInfoServiceResponse) { showInfoServiceResponse.startDate(), showInfoServiceResponse.location(), showInfoServiceResponse.image(), + showInfoServiceResponse.viewCount(), ShowSeatApiResponse.from(showInfoServiceResponse.seats()), ShowTicketingSiteApiResponse.from(showInfoServiceResponse.ticketingSiteInfos()), showInfoServiceResponse.ticketingSites().stream() diff --git a/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowPaginationApiParam.java b/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowPaginationApiParam.java index b7c0ca80..d38d0f64 100644 --- a/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowPaginationApiParam.java +++ b/app/api/show-api/src/main/java/com/example/show/controller/dto/response/ShowPaginationApiParam.java @@ -21,12 +21,15 @@ public record ShowPaginationApiParam( @Schema(description = "공연 포스터 이미지 주소") String posterImageURL, - @Schema(description = "예매일") + @Schema(description = "가장 근접한 예매 시간") String reservationAt, @Schema(description = "오픈 예정인 티켓팅 일정이 있는지 여부") boolean hasTicketingOpenSchedule, + @Schema(description = "조회수") + int viewCount, + @Schema(description = "아티스트 정보") List artists, @@ -45,6 +48,7 @@ public static ShowPaginationApiParam from(ShowPaginationServiceResponse response .posterImageURL(response.posterImageURL()) .reservationAt(response.reservationAt()) .hasTicketingOpenSchedule(response.hasTicketingOpenSchedule()) + .viewCount(response.viewCount()) .artists( response.artists().stream() .map(ShowArtistPaginationApiParam::from) 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 074d0b21..66f14fbb 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 @@ -56,4 +56,8 @@ public PaginationServiceResponse findShows(ShowPa response.hasNext() ); } + + public void view(UUID showId) { + showUseCase.view(showId); + } } diff --git a/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowPaginationServiceRequest.java b/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowPaginationServiceRequest.java index 231a4384..f5b3937e 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowPaginationServiceRequest.java +++ b/app/api/show-api/src/main/java/com/example/show/service/dto/request/ShowPaginationServiceRequest.java @@ -10,7 +10,8 @@ public record ShowPaginationServiceRequest( ShowSortApiType sort, boolean onlyOpenSchedule, - UUID cursor, + UUID cursorId, + Object cursorValue, int size, LocalDateTime now ) { @@ -19,7 +20,8 @@ public ShowPaginationDomainRequest toDomainRequest() { return ShowPaginationDomainRequest.builder() .sort(sort.toDomainType()) .onlyOpenSchedule(onlyOpenSchedule) - .cursor(cursor) + .cursorId(cursorId) + .cursorValue(cursorValue) .size(size) .now(now) .build(); 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 b609cbbe..c786bc30 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 @@ -24,6 +24,7 @@ public record ShowInfoServiceResponse( LocalDate endDate, String location, String image, + int viewCount, ShowSeatServiceResponse seats, ShowTicketingSiteServiceResponse ticketingSiteInfos, List ticketingSites, @@ -40,6 +41,7 @@ public ShowInfoServiceResponse(ShowInfoDomainResponse showInfo) { showInfo.show().endDate(), showInfo.show().location(), showInfo.show().image(), + showInfo.show().viewCount(), ShowSeatServiceResponse.from(showInfo.show().seatPrices()), ShowTicketingSiteServiceResponse.from(showInfo.show().ticketingSites()), toShowTicketingTimeServiceResponses(showInfo.ticketingTimes()), @@ -72,6 +74,7 @@ public static List as( showWithTicketingTime.show().endDate(), showWithTicketingTime.show().location(), showWithTicketingTime.show().image(), + showWithTicketingTime.show().viewCount(), ShowSeatServiceResponse.from(showWithTicketingTime.show().seatPrices()), ShowTicketingSiteServiceResponse.from( showWithTicketingTime.show().ticketingSites()), diff --git a/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowPaginationServiceResponse.java b/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowPaginationServiceResponse.java index 8f617296..9dba63a7 100644 --- a/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowPaginationServiceResponse.java +++ b/app/api/show-api/src/main/java/com/example/show/service/dto/response/ShowPaginationServiceResponse.java @@ -16,6 +16,7 @@ public record ShowPaginationServiceResponse( String posterImageURL, String reservationAt, boolean hasTicketingOpenSchedule, + int viewCount, List artists, List genres, List showTicketingTimes @@ -41,6 +42,7 @@ public static ShowPaginationServiceResponse from(ShowDetailDomainResponse respon .posterImageURL(response.show().image()) .reservationAt(reservationAt) .hasTicketingOpenSchedule(now.isBefore(response.show().lastTicketingAt())) + .viewCount(response.show().viewCount()) .artists( response.artists().stream() .map(ShowArtistSimpleServiceResponse::from) diff --git a/app/domain/common-domain/src/main/java/org/example/util/SliceUtil.java b/app/domain/common-domain/src/main/java/org/example/util/SliceUtil.java index f340f37f..2eba9513 100644 --- a/app/domain/common-domain/src/main/java/org/example/util/SliceUtil.java +++ b/app/domain/common-domain/src/main/java/org/example/util/SliceUtil.java @@ -13,10 +13,11 @@ private SliceUtil() { public static Slice makeSlice(final int size, final List dtos) { final boolean hasNext = dtos.size() > size; + if (hasNext) { - dtos.remove(size); + return new SliceImpl<>(dtos.subList(0, size), Pageable.ofSize(size), true); } - return new SliceImpl<>(dtos, Pageable.ofSize(size), hasNext); + return new SliceImpl<>(dtos, Pageable.ofSize(size), false); } } diff --git a/app/domain/common-domain/src/main/resources/data.sql b/app/domain/common-domain/src/main/resources/data.sql index 62110f89..898f0265 100644 --- a/app/domain/common-domain/src/main/resources/data.sql +++ b/app/domain/common-domain/src/main/resources/data.sql @@ -852,7 +852,7 @@ VALUES (gen_random_uuid(), -- Show INSERT INTO show(id, created_at, updated_at, is_deleted, title, content, start_date, end_date, - location, image, last_ticketing_at, seat_prices, ticketing_sites) + location, image, last_ticketing_at, view_count, seat_prices, ticketing_sites) VALUES ('eca21e50-1392-4059-b380-061a2323c6d2', now(), now(), @@ -864,6 +864,7 @@ VALUES ('eca21e50-1392-4059-b380-061a2323c6d2', '공연장 위치', 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg', now(), + 0, '{"b": 20000, "c": 30000}', '{"티켓링크": "https://naver.com"}'); diff --git a/app/domain/common-domain/src/main/resources/schema.sql b/app/domain/common-domain/src/main/resources/schema.sql index c4c14cce..f9e9c0cb 100644 --- a/app/domain/common-domain/src/main/resources/schema.sql +++ b/app/domain/common-domain/src/main/resources/schema.sql @@ -1,14 +1,11 @@ alter table if exists artist_search -drop -constraint if exists fk_artist_artist_search; + drop constraint if exists fk_artist_artist_search; alter table if exists show_search -drop -constraint if exists fk_show_show_search; + drop constraint if exists fk_show_show_search; alter table if exists show_ticketing_time -drop -constraint if exists fk_show_show_ticketing_time; + drop constraint if exists fk_show_show_ticketing_time; drop table if exists admin cascade; drop table if exists artist cascade; @@ -131,6 +128,7 @@ create table show location varchar(255) not null, image varchar(255) not null, last_ticketing_at timestamp(6) not null, + view_count int not null, seat_prices jsonb not null, ticketing_sites jsonb not null, primary key (id) @@ -223,15 +221,15 @@ create table users alter table if exists artist_search add constraint fk_artist_artist_search - foreign key (artist_id) - references artist; + foreign key (artist_id) + references artist; alter table if exists show_search add constraint fk_show_show_search - foreign key (show_id) - references show; + foreign key (show_id) + references show; alter table if exists show_ticketing_time add constraint fk_show_show_ticketing_time - foreign key (show_id) - references show; \ No newline at end of file + foreign key (show_id) + references show; \ No newline at end of file diff --git a/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowPaginationDomainRequest.java b/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowPaginationDomainRequest.java index fe24b004..6309aa6c 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowPaginationDomainRequest.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/show/request/ShowPaginationDomainRequest.java @@ -9,7 +9,8 @@ public record ShowPaginationDomainRequest( ShowSortType sort, boolean onlyOpenSchedule, - UUID cursor, + UUID cursorId, + Object cursorValue, int size, LocalDateTime now ) { diff --git a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDetailDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDetailDomainResponse.java index 6fd4df85..b9c6dbfc 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDetailDomainResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDetailDomainResponse.java @@ -1,9 +1,11 @@ package org.example.dto.show.response; import java.util.Set; +import lombok.Builder; import org.example.dto.artist.response.ArtistDomainResponse; import org.example.dto.genre.response.GenreDomainResponse; +@Builder public record ShowDetailDomainResponse( ShowDomainResponse show, Set artists, diff --git a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDomainResponse.java b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDomainResponse.java index 61692659..6ad83d3e 100644 --- a/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDomainResponse.java +++ b/app/domain/show-domain/src/main/java/org/example/dto/show/response/ShowDomainResponse.java @@ -18,6 +18,7 @@ public record ShowDomainResponse( String location, String image, LocalDateTime lastTicketingAt, + int viewCount, SeatPrices seatPrices, TicketingSites ticketingSites ) { 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 5f887682..6edeabf3 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 @@ -45,6 +45,9 @@ public class Show extends BaseEntity { @Column(name = "last_ticketing_at", nullable = false) private LocalDateTime lastTicketingAt; + @Column(name = "view_count", nullable = false) + private int viewCount; + @Enumerated private SeatPrices seatPrices; @@ -70,6 +73,7 @@ private Show( this.location = location; this.image = image; this.lastTicketingAt = lastTicketingAt; + this.viewCount = 0; this.seatPrices = seatPrices; this.ticketingSites = ticketingSites; } @@ -120,4 +124,8 @@ public void changeShowInfo(Show newShow) { this.image = newShow.image; this.seatPrices = newShow.seatPrices; } + + public void view() { + this.viewCount++; + } } \ No newline at end of file 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 3930ea19..441b0aa5 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 @@ -11,11 +11,14 @@ import static org.example.entity.show.QShowTicketingTime.showTicketingTime; import com.querydsl.core.types.ConstructorExpression; +import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.OrderSpecifier.NullHandling; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -32,8 +35,8 @@ import org.example.dto.show.response.ShowTicketingTimeDomainResponse; import org.example.dto.show.response.ShowWithTicketingTimesDomainResponse; import org.example.entity.show.Show; +import org.example.util.DateTimeUtil; import org.example.util.SliceUtil; -import org.example.vo.ShowSortType; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Repository; @@ -74,6 +77,7 @@ public List findShowDetailWithTicketingTim show.location, show.image, show.lastTicketingAt, + show.viewCount, show.seatPrices, show.ticketingSites ), @@ -103,28 +107,98 @@ public Optional findShowInfoById(UUID id) { @Override public ShowPaginationDomainResponse findShows(ShowPaginationDomainRequest request) { - List data = jpaQueryFactory + List result; + switch (request.sort()) { + case POPULAR -> result = findShowsByPopularity(request); + default -> result = findShowsByClosestTicketingAt(request); + } + + Slice slice = SliceUtil.makeSlice(request.size(), result); + + return ShowPaginationDomainResponse.builder() + .data(slice.getContent()) + .hasNext(slice.hasNext()) + .build(); + } + + private List findShowsByPopularity(ShowPaginationDomainRequest request) { + BooleanExpression whereExpression = show.isDeleted.isFalse(); + + if (request.cursorId() != null) { + whereExpression.and( + show.viewCount.gt(Integer.parseInt(request.cursorValue().toString())) + .or(show.viewCount.eq(Integer.parseInt(request.cursorValue().toString())) + .and(show.id.gt(request.cursorId())) + ) + ); + } + + if (request.onlyOpenSchedule()) { + whereExpression.and(show.lastTicketingAt.after(request.now())); + } + + return jpaQueryFactory .selectFrom(show) - .join(showArtist).on(isShowArtistEqualShowIdAndIsDeletedFalse()) - .join(artist).on(isArtistIdEqualShowArtistAndIsDeletedFalse()) - .join(showGenre).on(isShowGenreEqualShowIdAndIsDeletedFalse()) - .join(genre).on(isGenreIdEqualShowGenreAndIsDeletedFalse()) - .join(showTicketingTime) + .leftJoin(showArtist).on(isShowArtistEqualShowIdAndIsDeletedFalse()) + .leftJoin(artist).on(isArtistIdEqualShowArtistAndIsDeletedFalse()) + .leftJoin(showGenre).on(isShowGenreEqualShowIdAndIsDeletedFalse()) + .leftJoin(genre).on(isGenreIdEqualShowGenreAndIsDeletedFalse()) + .leftJoin(showTicketingTime) .on(showTicketingTime.show.id.eq(show.id).and(showTicketingTime.isDeleted.isFalse())) - .where(getShowPaginationWhereCondition(request)) + .where(whereExpression) .limit(request.size() + 1) - .orderBy(getShowOrderSpecifier(request.sort())) + .orderBy( + show.viewCount.desc(), + show.id.asc() + ) .transform( groupBy(show.id).as(getShowDetailConstructor()) ).values().stream() .toList(); + } - Slice slice = SliceUtil.makeSlice(request.size(), data); + private List findShowsByClosestTicketingAt(ShowPaginationDomainRequest request) { + JPAQuery closestTicketingTimeQuery = jpaQueryFactory + .select(showTicketingTime.ticketingAt.min()) + .from(showTicketingTime) + .where(showTicketingTime.show.id.eq(show.id) + .and(showTicketingTime.ticketingAt.gt(request.now()))); - return ShowPaginationDomainResponse.builder() - .data(slice.getContent()) - .hasNext(slice.hasNext()) - .build(); + BooleanExpression whereExpression = show.isDeleted.isFalse() + .and(closestTicketingTimeQuery.isNotNull()); + + if (request.cursorId() != null) { + whereExpression.and( + closestTicketingTimeQuery.gt(DateTimeUtil.parse(request.cursorValue().toString())) + .or(closestTicketingTimeQuery.eq(DateTimeUtil.parse(request.cursorValue().toString())) + .and(show.id.gt(request.cursorId())) + ) + ); + } + + if (request.onlyOpenSchedule()) { + whereExpression.and(show.lastTicketingAt.after(request.now())); + } + + List response = jpaQueryFactory + .selectFrom(show) + .leftJoin(showGenre).on(show.id.eq(showGenre.showId).and(showGenre.isDeleted.isFalse())) + .leftJoin(genre).on(showGenre.genreId.eq(genre.id).and(genre.isDeleted.isFalse())) + .leftJoin(showArtist).on(show.id.eq(showArtist.showId).and(showArtist.isDeleted.isFalse())) + .leftJoin(artist).on(showArtist.artistId.eq(artist.id).and(artist.isDeleted.isFalse())) + .leftJoin(showTicketingTime) + .on(show.id.eq(showTicketingTime.show.id).and(showTicketingTime.isDeleted.isFalse())) + .where(whereExpression) + .limit(request.size() + 1) + .orderBy( + new OrderSpecifier<>(Order.ASC, closestTicketingTimeQuery, NullHandling.NullsLast), + show.id.asc() + ) + .transform( + groupBy(show.id).as(getShowDetailConstructor()) + ).values().stream().toList(); + + return response; } private ConstructorExpression getShowDetailConstructor() { @@ -140,6 +214,7 @@ private ConstructorExpression getShowDetailConstructor show.location, show.image, show.lastTicketingAt, + show.viewCount, show.seatPrices, show.ticketingSites ), @@ -185,6 +260,7 @@ private ConstructorExpression getShowInfoConstructor() { show.location, show.image, show.lastTicketingAt, + show.viewCount, show.seatPrices, show.ticketingSites ), @@ -242,28 +318,4 @@ private BooleanExpression isGenreIdEqualShowGenreAndIsDeletedFalse() { private BooleanExpression isShowTicketingEqualShowAndIsDeletedFalse() { return showTicketingTime.show.id.eq(show.id).and(showTicketingTime.isDeleted.isFalse()); } - - private BooleanExpression getShowPaginationWhereCondition(ShowPaginationDomainRequest request) { - BooleanExpression defaultCondition = show.isDeleted.isFalse(); - - if (request.cursor() != null) { - defaultCondition.and(show.id.gt(request.cursor())); - } - - // 티켓팅 오픈 예정 스케줄이 있는 경우 - // TODO: 이름 변경이 필요해 보임 - if (request.onlyOpenSchedule()) { - defaultCondition.and(show.lastTicketingAt.after(request.now())); - } - - return defaultCondition; - } - - // TODO: Show 조회수 컬럼 추가 후 수정 - private OrderSpecifier getShowOrderSpecifier(ShowSortType sortType) { - return switch (sortType) { - case RECENT -> show.lastTicketingAt.desc(); - case POPULAR -> show.id.desc(); - }; - } } 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 b4d33d5d..f7bb9e12 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 @@ -164,6 +164,12 @@ public void updateShowTicketingTimes(ShowTicketingTimes ticketingTimes, Show sho showGenresToRemove.forEach(BaseEntity::softDelete); } + // TODO: 동시성 이슈 고려 안 함 + @Transactional + public void view(UUID id) { + findShowOrThrowNoSuchElementException(id).view(); + } + @Transactional public void deleteShow(UUID id) { Show show = findShowOrThrowNoSuchElementException(id); diff --git a/common/src/main/java/org/example/util/DateTimeUtil.java b/common/src/main/java/org/example/util/DateTimeUtil.java index 840b484e..1bf76bb5 100644 --- a/common/src/main/java/org/example/util/DateTimeUtil.java +++ b/common/src/main/java/org/example/util/DateTimeUtil.java @@ -20,4 +20,8 @@ public static String formatTime(LocalTime time) { DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); return time.format(timeFormatter); } + + public static LocalDateTime parse(String origin) { + return LocalDateTime.parse(origin, DateTimeFormatter.ofPattern("yyyy-MM-dd H:m")); + } }