Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[feat] #278 - 티켓 조회 api 변경 #281

Merged
merged 5 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/main/java/com/beat/domain/booking/api/TicketApi.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.beat.domain.booking.api;

import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -52,8 +54,8 @@ public interface TicketApi {
ResponseEntity<SuccessResponse<TicketRetrieveResponse>> getTickets(
@CurrentMember Long memberId,
@PathVariable Long performanceId,
@RequestParam(required = false) ScheduleNumber scheduleNumber,
@RequestParam(required = false) BookingStatus bookingStatus
@RequestParam(required = false) List<ScheduleNumber> scheduleNumbers,
@RequestParam(required = false) List<BookingStatus> bookingStatuses
);

@Operation(summary = "예매자 목록 검색 API", description = "메이커가 자신의 공연에 대한 예매자 목록을 검색하는 GET API입니다.")
Expand Down Expand Up @@ -84,8 +86,8 @@ ResponseEntity<SuccessResponse<TicketRetrieveResponse>> searchTickets(
@CurrentMember Long memberId,
@PathVariable Long performanceId,
@RequestParam String searchWord,
@RequestParam(required = false) ScheduleNumber scheduleNumber,
@RequestParam(required = false) BookingStatus bookingStatus
@RequestParam(required = false) List<ScheduleNumber> scheduleNumbers,
@RequestParam(required = false) List<BookingStatus> bookingStatuses
);

@Operation(summary = "예매자 입금여부 수정 및 웹발신 API", description = "메이커가 자신의 공연에 대한 예매자의 입금여부 정보를 수정한 뒤 예매확정 웹발신을 보내는 PUT API입니다.")
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/com/beat/domain/booking/api/TicketController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.beat.domain.booking.api;

import java.util.List;

import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -37,14 +39,13 @@ public class TicketController implements TicketApi {
public ResponseEntity<SuccessResponse<TicketRetrieveResponse>> getTickets(
@CurrentMember Long memberId,
@PathVariable Long performanceId,
@RequestParam(required = false) ScheduleNumber scheduleNumber,
@RequestParam(required = false) BookingStatus bookingStatus) {
if (bookingStatus == BookingStatus.BOOKING_DELETED) {
@RequestParam(required = false) List<ScheduleNumber> scheduleNumbers,
@RequestParam(required = false) List<BookingStatus> bookingStatuses) {
if (bookingStatuses != null && bookingStatuses.contains(BookingStatus.BOOKING_DELETED)) {
throw new BadRequestException(TicketErrorCode.DELETED_TICKET_RETRIEVE_NOT_ALLOWED);
}
TicketRetrieveResponse response = ticketService.findAllTicketsByConditions(memberId, performanceId,
scheduleNumber,
bookingStatus);
scheduleNumbers, bookingStatuses);
return ResponseEntity.ok(SuccessResponse.of(TicketSuccessCode.TICKET_RETRIEVE_SUCCESS, response));
}

Expand All @@ -54,18 +55,17 @@ public ResponseEntity<SuccessResponse<TicketRetrieveResponse>> searchTickets(
@CurrentMember Long memberId,
@PathVariable Long performanceId,
@RequestParam String searchWord,
@RequestParam(required = false) ScheduleNumber scheduleNumber,
@RequestParam(required = false) BookingStatus bookingStatus) {
@RequestParam(required = false) List<ScheduleNumber> scheduleNumbers,
@RequestParam(required = false) List<BookingStatus> bookingStatuses) {
if (searchWord.length() < 2) {
throw new BadRequestException(TicketErrorCode.SEARCH_WORD_TOO_SHORT);
}
if (bookingStatus == BookingStatus.BOOKING_DELETED) {
if (bookingStatuses != null && bookingStatuses.contains(BookingStatus.BOOKING_DELETED)) {
throw new BadRequestException(TicketErrorCode.DELETED_TICKET_RETRIEVE_NOT_ALLOWED);
}

TicketRetrieveResponse response = ticketService.searchAllTicketsByConditions(memberId, performanceId,
searchWord,
scheduleNumber, bookingStatus);
searchWord, scheduleNumbers, bookingStatuses);
return ResponseEntity.ok()
.cacheControl(CacheControl.noCache())
.body(SuccessResponse.of(TicketSuccessCode.TICKET_SEARCH_SUCCESS, response));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.beat.domain.booking.application;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -56,49 +57,61 @@ public class TicketService {
private final CoolSmsService coolSmsService;

public TicketRetrieveResponse findAllTicketsByConditions(Long memberId, Long performanceId,
ScheduleNumber scheduleNumber,
BookingStatus bookingStatus) {
List<ScheduleNumber> scheduleNumbers, List<BookingStatus> bookingStatuses) {
Member member = findMember(memberId);
Users user = findUser(member);
Performance performance = findPerformance(performanceId);
performance.validatePerformanceOwnership(user.getId());
List<Schedule> schedules = scheduleRepository.findAllByPerformanceId(performanceId);

List<Schedule> schedules = scheduleRepository.findAllByPerformanceId(performanceId);
int totalPerformanceTicketCount = calculateTotalTicketCount(schedules);
int totalPerformanceSoldTicketCount = calculateTotalSoldTicketCount(schedules);

List<Booking> bookings = ticketRepository.findBookings(performanceId, scheduleNumber, bookingStatus);
List<Booking> bookings = ticketRepository.findBookings(performanceId, scheduleNumbers, bookingStatuses);

return findTicketRetrieveResponse(performance, totalPerformanceTicketCount, totalPerformanceSoldTicketCount,
bookings);
}

public TicketRetrieveResponse searchAllTicketsByConditions(Long memberId, Long performanceId, String searchWord,
ScheduleNumber scheduleNumber, BookingStatus bookingStatus) {
List<ScheduleNumber> scheduleNumbers, List<BookingStatus> bookingStatuses) {
Member member = findMember(memberId);
Users user = findUser(member);
Performance performance = findPerformance(performanceId);
performance.validatePerformanceOwnership(user.getId());
List<Schedule> schedules = scheduleRepository.findAllByPerformanceId(performanceId);

List<Schedule> schedules = scheduleRepository.findAllByPerformanceId(performanceId);
int totalPerformanceTicketCount = calculateTotalTicketCount(schedules);
int totalPerformanceSoldTicketCount = calculateTotalSoldTicketCount(schedules);

String scheduleNumberValue = null;
if (scheduleNumber != null) {
scheduleNumberValue = scheduleNumber.name();
List<String> selectedScheduleNumbers = schedules.stream()
.map(schedule -> schedule.getScheduleNumber().name())
.toList();

List<String> selectedBookingStatuses = Arrays.asList(
BookingStatus.CHECKING_PAYMENT.name(),
BookingStatus.BOOKING_CONFIRMED.name(),
BookingStatus.BOOKING_CANCELLED.name(),
BookingStatus.REFUND_REQUESTED.name()
);

if (scheduleNumbers != null && !scheduleNumbers.isEmpty()) {
selectedScheduleNumbers = scheduleNumbers.stream()
.map(Enum::name)
.toList();
}

String bookingStatusValue = null;
if (bookingStatus != null) {
bookingStatusValue = bookingStatus.name();
if (bookingStatuses != null && !bookingStatuses.isEmpty()) {
selectedBookingStatuses = bookingStatuses.stream()
.map(Enum::name)
.toList();
}

List<Booking> bookings = ticketRepository.searchBookings(
performanceId,
searchWord,
scheduleNumberValue,
bookingStatusValue
selectedScheduleNumbers,
selectedBookingStatuses
);

log.info("searchTickets result: {}", bookings);
Expand Down
27 changes: 14 additions & 13 deletions src/main/java/com/beat/domain/booking/dao/TicketRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public interface TicketRepository extends JpaRepository<Booking, Long> {
@Query("SELECT b FROM Booking b " +
"WHERE b.schedule.performance.id = :performanceId " +
"AND b.bookingStatus != com.beat.domain.booking.domain.BookingStatus.BOOKING_DELETED " +
"AND (:scheduleNumber IS NULL OR b.schedule.scheduleNumber = :scheduleNumber) " +
"AND (:bookingStatus IS NULL OR b.bookingStatus = :bookingStatus) " +
"AND (:scheduleNumbers IS NULL OR b.schedule.scheduleNumber IN :scheduleNumbers) " +
"AND (:bookingStatuses IS NULL OR b.bookingStatus IN :bookingStatuses) " +
"ORDER BY CASE b.bookingStatus " +
" WHEN com.beat.domain.booking.domain.BookingStatus.REFUND_REQUESTED THEN 1 " +
" WHEN com.beat.domain.booking.domain.BookingStatus.CHECKING_PAYMENT THEN 2 " +
Expand All @@ -26,8 +26,8 @@ public interface TicketRepository extends JpaRepository<Booking, Long> {
" b.createdAt DESC")
List<Booking> findBookings(
@Param("performanceId") Long performanceId,
@Param("scheduleNumber") ScheduleNumber scheduleNumber,
@Param("bookingStatus") BookingStatus bookingStatus);
@Param("scheduleNumbers") List<ScheduleNumber> scheduleNumbers,
@Param("bookingStatuses") List<BookingStatus> bookingStatuses);

List<Booking> findByBookingStatusAndCancellationDateBefore(BookingStatus bookingStatus,
LocalDateTime cancellationDate);
Expand All @@ -37,23 +37,24 @@ List<Booking> findByBookingStatusAndCancellationDateBefore(BookingStatus booking
FROM booking b
JOIN schedule s ON b.schedule_id = s.id
WHERE s.performance_id = :performanceId
AND b.booking_status != 'BOOKING_DELETED'
AND (:scheduleNumber IS NULL OR s.schedule_number = :scheduleNumber)
AND(:bookingStatus IS NULL OR b.booking_status = :bookingStatus)
AND MATCH(b.booker_name) AGAINST(:searchWord IN BOOLEAN MODE)
AND b.booking_status != 'BOOKING_DELETED'
AND (s.schedule_number IN (:selectedScheduleNumbers))
AND (b.booking_status IN (:selectedBookingStatuses))
AND MATCH(b.booker_name) AGAINST(:searchWord IN BOOLEAN MODE)
ORDER BY
CASE b.booking_status
CASE b.booking_status
WHEN 'REFUND_REQUESTED' THEN 1
WHEN 'CHECKING_PAYMENT' THEN 2
WHEN 'BOOKING_CONFIRMED' THEN 3
WHEN 'BOOKING_CANCELLED' THEN 4
END ASC,
b.created_at DESC
END ASC,
b.created_at DESC
""", nativeQuery = true)
List<Booking> searchBookings(
@Param("performanceId") Long performanceId,
@Param("searchWord") String searchWord,
@Param("scheduleNumber") String scheduleNumber,
@Param("bookingStatus") String bookingStatus);
@Param("selectedScheduleNumbers") List<String> selectedScheduleNumbers,
@Param("selectedBookingStatuses") List<String> selectedBookingStatuses
);
Copy link
Member

Choose a reason for hiding this comment

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

@Query(value = """
    SELECT b.*
    FROM booking b
    JOIN schedule s ON b.schedule_id = s.id
    WHERE s.performance_id = :performanceId
      AND b.booking_status != 'BOOKING_DELETED'
      AND (:scheduleNumberStrings IS NULL OR s.schedule_number IN (:scheduleNumberStrings))
      AND (:bookingStatusStrings IS NULL OR b.booking_status IN (:bookingStatusStrings))
      AND (:searchWord IS NULL OR MATCH(b.booker_name) AGAINST(:searchWord IN BOOLEAN MODE))
    ORDER BY 
        FIELD(b.booking_status, 'REFUND_REQUESTED', 'CHECKING_PAYMENT', 'BOOKING_CONFIRMED', 'BOOKING_CANCELLED'),
        b.created_at DESC
    """, nativeQuery = true)
List<Booking> searchBookings(
    @Param("performanceId") Long performanceId,
    @Param("searchWord") String searchWord,
    @Param("scheduleNumberStrings") List<String> scheduleNumberStrings,
    @Param("bookingStatusStrings") List<String> bookingStatusStrings
);

해당 방식으로 바꾸면 좀 더 FIELD 함수를 사용해 동일한 정렬 우선순위를 더 간결하게 작성할 수 있을 것 같은데 어떻게 생각하시나요?

Copy link
Collaborator Author

@hyerinhwang-sailin hyerinhwang-sailin Dec 3, 2024

Choose a reason for hiding this comment

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

우선 is null or 적용 시 여러개의 enum 값으로 같이 조회하는 것이 불가능해서 conversation에 적었던 것처럼 service에 default 값을 설정하고 is null or은 적용하지 않는 방식으로 해결해뒀습니다!
case 함수는 대부분의 dbms에서 사용 가능한 방면 field 함수는 mysql 전용함수이고, 성능상에서는 차이가 거의 없고 데이터셋이 클 때는 case가 조금 더 낫다고 하는데 동훈님은 가독성을 위해 FIELD 함수 사용을 추천하신걸까요?

Copy link
Member

Choose a reason for hiding this comment

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

DB 변경 가능성이 없을 것 같아서 FIELD로 추천드렸었는데, 추후 QueryDSL을 사용하려면 FIELD와 같은 MySQL 전용 함수는 지원이 제한적일 수도 있을 것 같네요!!

혜린님 방식으로 유지해도 좋을 것 같습니다!


}