diff --git a/src/main/java/muit/backend/config/WebConfig.java b/src/main/java/muit/backend/config/WebConfig.java new file mode 100644 index 0000000..f88823b --- /dev/null +++ b/src/main/java/muit/backend/config/WebConfig.java @@ -0,0 +1,19 @@ +package muit.backend.config; + + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("http://localhost:5173") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH") + .allowedHeaders("*") + .allowCredentials(true) + .maxAge(3600); + } +} \ No newline at end of file diff --git a/src/main/java/muit/backend/controller/ManageReservController.java b/src/main/java/muit/backend/controller/ManageReservController.java new file mode 100644 index 0000000..7b4087c --- /dev/null +++ b/src/main/java/muit/backend/controller/ManageReservController.java @@ -0,0 +1,36 @@ +package muit.backend.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import muit.backend.apiPayLoad.ApiResponse; +import muit.backend.dto.manageReservDTO.ManageReservResponseDTO; +import muit.backend.service.ManageReservService; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Set; + +@Tag(name = "어드민이 공연 예약 관리") +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/amateur-reservations") +public class ManageReservController { + + private final ManageReservService manageReservService; + + @Operation(summary = "전체 공연 예약 조회") + @GetMapping + public ApiResponse> getAllReservations(@ParameterObject Pageable pageable, + @RequestParam(required = false) String keyword, + @RequestParam(required = false) Set selectedFields) { + Page reservations = manageReservService.getAllReservations(pageable, keyword, selectedFields); + return ApiResponse.onSuccess(reservations); + } + +} diff --git a/src/main/java/muit/backend/converter/ManageReservConverter.java b/src/main/java/muit/backend/converter/ManageReservConverter.java new file mode 100644 index 0000000..a55a910 --- /dev/null +++ b/src/main/java/muit/backend/converter/ManageReservConverter.java @@ -0,0 +1,53 @@ +package muit.backend.converter; + +import muit.backend.domain.entity.amateur.AmateurShow; +import muit.backend.domain.entity.member.MemberTicket; +import muit.backend.domain.entity.member.Reservation; +import muit.backend.dto.manageReservDTO.ManageReservResponseDTO; + +import java.util.Set; + +public class ManageReservConverter { + + // reservation에서 amateurshow 가져오기 + private static AmateurShow getAmateurShowFromReservation(Reservation reservation) { + return reservation.getMemberTicketList().stream() + .findFirst() // 아무 티켓이나 봐도 같은 공연이니 첫 번째 것 사용 + .map(memberTicket -> memberTicket.getAmateurTicket().getAmateurShow()) + .orElse(null); + } + + public static ManageReservResponseDTO.ManageReservResultListDTO toManageReservResultListDTO(Reservation reservation, Set selectedFields, boolean isKeywordSearch) { + + // 키워드 검색이거나 전체 조회인 경우 모든 필드 포함 + if (isKeywordSearch || selectedFields == null || selectedFields.isEmpty()) { + AmateurShow amateurShow = getAmateurShowFromReservation(reservation); + + return ManageReservResponseDTO.ManageReservResultListDTO.builder() + .reservationId(reservation.getId()) + .memberName(reservation.getMember().getName()) + .amateurShowName(amateurShow.getName()) + .schedule(amateurShow.getSchedule()) + .place(amateurShow.getPlace()) + .quantity(reservation.getMemberTicketList().stream() + .findFirst() + .map(MemberTicket::getQuantity) + .orElse(null)) + .reservationStatus(reservation.getStatus()) + .build(); + } + + // selectedFields로 특정 필드만 선택한 경우 + AmateurShow amateurShow = getAmateurShowFromReservation(reservation); + return ManageReservResponseDTO.ManageReservResultListDTO.builder() + .reservationId(reservation.getId()) // ID는 항상 포함 + .amateurShowName(selectedFields.contains("amateurShowName") ? + (amateurShow != null ? amateurShow.getName() : null) : null) + .schedule(selectedFields.contains("schedule") ? + (amateurShow != null ? amateurShow.getSchedule() : null) : null) + .reservationStatus(selectedFields.contains("reservationStatus") ? + reservation.getStatus() : null) + .build(); + } + +} diff --git a/src/main/java/muit/backend/dto/manageReservDTO/ManageReservRequestDTO.java b/src/main/java/muit/backend/dto/manageReservDTO/ManageReservRequestDTO.java new file mode 100644 index 0000000..db1251e --- /dev/null +++ b/src/main/java/muit/backend/dto/manageReservDTO/ManageReservRequestDTO.java @@ -0,0 +1,4 @@ +package muit.backend.dto.manageReservDTO; + +public class ManageReservRequestDTO { +} diff --git a/src/main/java/muit/backend/dto/manageReservDTO/ManageReservResponseDTO.java b/src/main/java/muit/backend/dto/manageReservDTO/ManageReservResponseDTO.java new file mode 100644 index 0000000..91e6ff9 --- /dev/null +++ b/src/main/java/muit/backend/dto/manageReservDTO/ManageReservResponseDTO.java @@ -0,0 +1,26 @@ +package muit.backend.dto.manageReservDTO; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import muit.backend.domain.enums.ReservationStatus; + +public class ManageReservResponseDTO { + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @JsonInclude(JsonInclude.Include.NON_NULL) // null 값을 가진 필드는 JSON 응답에서 제외 + public static class ManageReservResultListDTO { // 소극장 공연 관리 내역 전체 조회 + private Long reservationId; // 소극장 예약 id + private String memberName; // 예약자(사용자)명 + private String amateurShowName; // 소극장 공연명 + private String schedule; // 소극장 공연 날짜/시간 + private String place; // 소극장 공연 장소 + private Integer quantity; + private ReservationStatus reservationStatus; // 예약 상태 (예약 중, 예약 완료, 사용 완료, 취소 중, 취소 완료) + } +} diff --git a/src/main/java/muit/backend/repository/ReservationRepository.java b/src/main/java/muit/backend/repository/ReservationRepository.java new file mode 100644 index 0000000..5221060 --- /dev/null +++ b/src/main/java/muit/backend/repository/ReservationRepository.java @@ -0,0 +1,23 @@ +package muit.backend.repository; + +import muit.backend.domain.entity.member.Reservation; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface ReservationRepository extends JpaRepository { + + // 검색어가 있을 때 + @Query("SELECT r FROM Reservation r " + + "JOIN FETCH r.member m " + + "JOIN FETCH r.memberTicketList mt " + + "JOIN FETCH mt.amateurTicket at " + + "JOIN FETCH at.amateurShow ams " + + "WHERE m.name LIKE CONCAT('%', :keyword, '%') " + + "OR ams.name LIKE CONCAT('%', :keyword, '%') " + + "OR ams.place LIKE CONCAT('%', :keyword, '%') " + + "OR ams.schedule LIKE CONCAT('%', :keyword, '%')") + Page findByKeyword(Pageable pageable, @Param("keyword") String keyword); +} \ No newline at end of file diff --git a/src/main/java/muit/backend/service/ManageReservService.java b/src/main/java/muit/backend/service/ManageReservService.java new file mode 100644 index 0000000..ddbce51 --- /dev/null +++ b/src/main/java/muit/backend/service/ManageReservService.java @@ -0,0 +1,14 @@ +package muit.backend.service; + +import muit.backend.dto.manageReservDTO.ManageReservResponseDTO; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.Set; + +@Service +public interface ManageReservService { + + public Page getAllReservations(Pageable pageable, String keyword, Set selectedFields); +} diff --git a/src/main/java/muit/backend/service/ManageReservServiceImpl.java b/src/main/java/muit/backend/service/ManageReservServiceImpl.java new file mode 100644 index 0000000..f2a2cc2 --- /dev/null +++ b/src/main/java/muit/backend/service/ManageReservServiceImpl.java @@ -0,0 +1,46 @@ +package muit.backend.service; + +import lombok.RequiredArgsConstructor; +import muit.backend.converter.AmateurTicketConverter; +import muit.backend.converter.ManageReservConverter; +import muit.backend.domain.entity.amateur.AmateurShow; +import muit.backend.domain.entity.member.Reservation; +import muit.backend.dto.manageReservDTO.ManageReservResponseDTO; +import muit.backend.repository.ReservationRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Set; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ManageReservServiceImpl implements ManageReservService { + + private final ReservationRepository reservationRepository; + + @Override + public Page getAllReservations(Pageable pageable, String keyword, Set selectedFields) { + + // 검색어가 있는지 확인 + boolean isKeywordSearch = keyword != null && !keyword.trim().isEmpty(); // 그냥 빈 검색어도 없다고 침 + + Page reservations; + + // 검색어가 있으면 해당 키워드로 검색 + if (isKeywordSearch) { + reservations = reservationRepository.findByKeyword(pageable, keyword); + if(reservations.isEmpty()) { + return Page.empty(pageable); + } + } else { // 검색어가 없으면 모든 소극장 공연 정보 조회 + reservations = reservationRepository.findAll(pageable); + } + + return reservations.map(reservation -> + ManageReservConverter.toManageReservResultListDTO(reservation, selectedFields, isKeywordSearch) + ); + } +}