feat: 가장 많이 예약된 테마 ID 조회 API 추가

This commit is contained in:
이상진 2025-09-13 15:40:36 +09:00
parent c3b736b81f
commit 6eecd145cc
5 changed files with 61 additions and 1 deletions

View File

@ -9,6 +9,7 @@ import org.springframework.transaction.annotation.Transactional
import roomescape.common.config.next import roomescape.common.config.next
import roomescape.common.dto.CurrentUserContext import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.PrincipalType import roomescape.common.dto.PrincipalType
import roomescape.common.util.DateUtils
import roomescape.member.business.UserService import roomescape.member.business.UserService
import roomescape.member.web.UserContactRetrieveResponse import roomescape.member.web.UserContactRetrieveResponse
import roomescape.payment.business.PaymentService import roomescape.payment.business.PaymentService
@ -23,6 +24,7 @@ import roomescape.schedule.web.ScheduleSummaryResponse
import roomescape.schedule.web.ScheduleUpdateRequest import roomescape.schedule.web.ScheduleUpdateRequest
import roomescape.theme.business.ThemeService import roomescape.theme.business.ThemeService
import roomescape.theme.web.ThemeInfoRetrieveResponse import roomescape.theme.web.ThemeInfoRetrieveResponse
import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
private val log: KLogger = KotlinLogging.logger {} private val log: KLogger = KotlinLogging.logger {}
@ -126,6 +128,23 @@ class ReservationService(
} }
} }
@Transactional(readOnly = true)
fun findMostReservedThemeIds(count: Int): MostReservedThemeIdListResponse {
log.info { "[ReservationService.findMostReservedThemeIds] 인기 테마 조회 시작: count=$count" }
val previousWeekSunday = DateUtils.getSundayOfPreviousWeek(LocalDate.now())
val previousWeekSaturday = previousWeekSunday.plusDays(6)
val themeIds: List<Long> = reservationRepository.findMostReservedThemeIds(
dateFrom = previousWeekSunday,
dateTo = previousWeekSaturday,
count = count
)
return MostReservedThemeIdListResponse(themeIds = themeIds).also {
log.info { "[ReservationService.findMostReservedThemeIds] 인기 테마 조회 완료: count=${it.themeIds.size}" }
}
}
private fun findOrThrow(id: Long): ReservationEntity { private fun findOrThrow(id: Long): ReservationEntity {
log.info { "[ReservationService.findOrThrow] 예약 조회 시작: reservationId=${id}" } log.info { "[ReservationService.findOrThrow] 예약 조회 시작: reservationId=${id}" }

View File

@ -7,8 +7,10 @@ import jakarta.validation.Valid
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import roomescape.auth.web.support.Authenticated import roomescape.auth.web.support.Authenticated
import roomescape.auth.web.support.CurrentUser import roomescape.auth.web.support.CurrentUser
import roomescape.auth.web.support.Public
import roomescape.auth.web.support.UserOnly import roomescape.auth.web.support.UserOnly
import roomescape.common.dto.CurrentUserContext import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse import roomescape.common.dto.response.CommonApiResponse
@ -16,6 +18,13 @@ import roomescape.reservation.web.*
interface ReservationAPI { interface ReservationAPI {
@Public
@Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findMostReservedThemeIds(
@RequestParam count: Int
): ResponseEntity<CommonApiResponse<MostReservedThemeIdListResponse>>
@UserOnly @UserOnly
@Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"]) @Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))

View File

@ -1,8 +1,27 @@
package roomescape.reservation.infrastructure.persistence package roomescape.reservation.infrastructure.persistence
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import java.time.LocalDate
interface ReservationRepository : JpaRepository<ReservationEntity, Long> { interface ReservationRepository : JpaRepository<ReservationEntity, Long> {
fun findAllByUserId(userId: Long): List<ReservationEntity> fun findAllByUserId(userId: Long): List<ReservationEntity>
@Query("""
SELECT s.themeId
FROM ReservationEntity r
JOIN ScheduleEntity s ON s._id = r.scheduleId
WHERE r.status = roomescape.reservation.infrastructure.persistence.ReservationStatus.CONFIRMED
AND s.date BETWEEN :dateFrom AND :dateTo
GROUP BY s.themeId
ORDER BY count(r) DESC
LIMIT :count
""")
fun findMostReservedThemeIds(
@Param("dateFrom") dateFrom: LocalDate,
@Param("dateTo") dateTo: LocalDate,
@Param("count") count: Int
): List<Long>
} }

View File

@ -14,6 +14,15 @@ class ReservationController(
private val reservationService: ReservationService private val reservationService: ReservationService
) : ReservationAPI { ) : ReservationAPI {
@GetMapping("/reservations/popular-themes")
override fun findMostReservedThemeIds(
@RequestParam count: Int
): ResponseEntity<CommonApiResponse<MostReservedThemeIdListResponse>> {
val response = reservationService.findMostReservedThemeIds(count)
return ResponseEntity.ok(CommonApiResponse(response))
}
@PostMapping("/reservations/pending") @PostMapping("/reservations/pending")
override fun createPendingReservation( override fun createPendingReservation(
@CurrentUser user: CurrentUserContext, @CurrentUser user: CurrentUserContext,

View File

@ -68,3 +68,7 @@ fun ReservationEntity.toReservationDetailRetrieveResponse(
data class ReservationCancelRequest( data class ReservationCancelRequest(
val cancelReason: String val cancelReason: String
) )
data class MostReservedThemeIdListResponse(
val themeIds: List<Long>
)