generated from pricelees/issue-pr-template
remove: 기능 변경 완료로 인한 기존 테마 코드 제거
This commit is contained in:
parent
e7f69aaee4
commit
e4b9214d75
@ -20,7 +20,7 @@ import roomescape.schedule.business.ScheduleService
|
|||||||
import roomescape.schedule.infrastructure.persistence.ScheduleStatus
|
import roomescape.schedule.infrastructure.persistence.ScheduleStatus
|
||||||
import roomescape.schedule.web.ScheduleSummaryResponse
|
import roomescape.schedule.web.ScheduleSummaryResponse
|
||||||
import roomescape.schedule.web.ScheduleUpdateRequest
|
import roomescape.schedule.web.ScheduleUpdateRequest
|
||||||
import roomescape.theme.business.ThemeServiceV2
|
import roomescape.theme.business.ThemeService
|
||||||
import roomescape.theme.web.ThemeRetrieveResponseV2
|
import roomescape.theme.web.ThemeRetrieveResponseV2
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class ReservationService(
|
|||||||
private val reservationRepository: ReservationRepository,
|
private val reservationRepository: ReservationRepository,
|
||||||
private val scheduleService: ScheduleService,
|
private val scheduleService: ScheduleService,
|
||||||
private val memberService: MemberService,
|
private val memberService: MemberService,
|
||||||
private val themeService: ThemeServiceV2,
|
private val themeService: ThemeService,
|
||||||
private val canceledReservationRepository: CanceledReservationRepository,
|
private val canceledReservationRepository: CanceledReservationRepository,
|
||||||
private val tsidFactory: TsidFactory,
|
private val tsidFactory: TsidFactory,
|
||||||
private val paymentService: PaymentService
|
private val paymentService: PaymentService
|
||||||
|
|||||||
@ -1,67 +1,141 @@
|
|||||||
package roomescape.theme.business
|
package roomescape.theme.business
|
||||||
|
|
||||||
|
import com.github.f4b6a3.tsid.TsidFactory
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import roomescape.theme.implement.ThemeFinder
|
import roomescape.common.config.next
|
||||||
import roomescape.theme.implement.ThemeWriter
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.theme.exception.ThemeErrorCode
|
||||||
|
import roomescape.theme.exception.ThemeException
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
|
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||||
import roomescape.theme.web.*
|
import roomescape.theme.web.*
|
||||||
import java.time.LocalDate
|
|
||||||
|
|
||||||
private val log = KotlinLogging.logger {}
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class ThemeService(
|
class ThemeService(
|
||||||
private val themeFinder: ThemeFinder,
|
private val themeRepository: ThemeRepository,
|
||||||
private val themeWriter: ThemeWriter,
|
private val tsidFactory: TsidFactory,
|
||||||
|
private val memberService: MemberService,
|
||||||
|
private val themeValidator: ThemeValidator
|
||||||
) {
|
) {
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findById(id: Long): ThemeEntity {
|
fun findThemesByIds(request: ThemeListRetrieveRequest): ThemeRetrieveListResponse {
|
||||||
log.debug { "[ThemeService.findById] 시작: themeId=$id" }
|
log.info { "[ThemeService.findThemesByIds] 예약 페이지에서의 테마 목록 조회 시작: themeIds=${request.themeIds}" }
|
||||||
|
|
||||||
return themeFinder.findById(id)
|
return request.themeIds
|
||||||
.also { log.info { "[ThemeService.findById] 완료: themeId=$id, name=${it.name}" } }
|
.map { findOrThrow(it) }
|
||||||
|
.toRetrieveListResponse()
|
||||||
|
.also { log.info { "[ThemeService.findThemesByIds] ${it.themes.size}개 테마 조회 완료" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findThemes(): ThemeRetrieveListResponse {
|
fun findThemesForReservation(): ThemeRetrieveListResponse {
|
||||||
log.debug { "[ThemeService.findThemes] 시작" }
|
log.info { "[ThemeService.findThemesForReservation] 예약 페이지에서의 테마 목록 조회 시작" }
|
||||||
|
|
||||||
return themeFinder.findAll()
|
return themeRepository.findOpenedThemes()
|
||||||
.toRetrieveListResponse()
|
.toRetrieveListResponse()
|
||||||
.also { log.info { "[ThemeService.findThemes] 완료. ${it.themes.size}개 반환" } }
|
.also { log.info { "[ThemeService.findThemesForReservation] ${it.themes.size}개 테마 조회 완료" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findMostReservedThemes(count: Int): ThemeRetrieveListResponse {
|
fun findAdminThemes(): AdminThemeSummaryRetrieveListResponse {
|
||||||
log.debug { "[ThemeService.findMostReservedThemes] 시작: count=$count" }
|
log.info { "[ThemeService.findAdminThemes] 관리자 페이지에서의 테마 목록 조회 시작" }
|
||||||
|
|
||||||
val today = LocalDate.now()
|
return themeRepository.findAll()
|
||||||
val startFrom = today.minusDays(7)
|
.toAdminThemeSummaryListResponse()
|
||||||
val endAt = today.minusDays(1)
|
.also { log.info { "[ThemeService.findAdminThemes] ${it.themes.size}개 테마 조회 완료" } }
|
||||||
|
}
|
||||||
|
|
||||||
return themeFinder.findMostReservedThemes(count, startFrom, endAt)
|
@Transactional(readOnly = true)
|
||||||
.toRetrieveListResponse()
|
fun findAdminThemeDetail(id: Long): AdminThemeDetailRetrieveResponse {
|
||||||
.also { log.info { "[ThemeService.findMostReservedThemes] ${it.themes.size}개 반환" } }
|
log.info { "[ThemeService.findAdminThemeDetail] 관리자 페이지에서의 테마 상세 정보 조회 시작: id=${id}" }
|
||||||
|
|
||||||
|
val theme: ThemeEntity = findOrThrow(id)
|
||||||
|
|
||||||
|
val createdBy = memberService.findSummaryById(theme.createdBy).name
|
||||||
|
val updatedBy = memberService.findSummaryById(theme.updatedBy).name
|
||||||
|
|
||||||
|
return theme.toAdminThemeDetailResponse(createdBy, updatedBy)
|
||||||
|
.also { log.info { "[ThemeService.findAdminThemeDetail] 테마 상세 조회 완료: id=$id, name=${theme.name}" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun findById(id: Long): ThemeRetrieveResponseV2 {
|
||||||
|
log.info { "[ThemeService.findById] 테마 조회 시작: id=$id" }
|
||||||
|
|
||||||
|
return findOrThrow(id).toRetrieveResponse()
|
||||||
|
.also { log.info { "[ThemeService.findById] 테마 조회 완료: id=$id" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun createTheme(request: ThemeCreateRequest): ThemeCreateResponse {
|
fun createTheme(request: ThemeCreateRequest): ThemeCreateResponseV2 {
|
||||||
log.debug { "[ThemeService.createTheme] 시작: name=${request.name}" }
|
log.info { "[ThemeService.createTheme] 테마 생성 시작: name=${request.name}" }
|
||||||
|
|
||||||
return themeWriter.create(request.name, request.description, request.thumbnail)
|
themeValidator.validateCanCreate(request)
|
||||||
.toCreateResponse()
|
|
||||||
.also { log.info { "[ThemeService.createTheme] 테마 생성 완료: name=${it.name} themeId=${it.id}" } }
|
val theme: ThemeEntity = themeRepository.save(
|
||||||
|
request.toEntity(tsidFactory.next())
|
||||||
|
)
|
||||||
|
|
||||||
|
return ThemeCreateResponseV2(theme.id).also {
|
||||||
|
log.info { "[ThemeService.createTheme] 테마 생성 완료: id=${theme.id}, name=${theme.name}" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun deleteTheme(id: Long) {
|
fun deleteTheme(id: Long) {
|
||||||
log.debug { "[ThemeService.deleteTheme] 시작: themeId=$id" }
|
log.info { "[ThemeService.deleteTheme] 테마 삭제 시작: id=${id}" }
|
||||||
|
|
||||||
val theme: ThemeEntity = themeFinder.findById(id)
|
val theme: ThemeEntity = findOrThrow(id)
|
||||||
|
|
||||||
themeWriter.delete(theme)
|
themeRepository.delete(theme).also {
|
||||||
.also { log.info { "[ThemeService.deleteTheme] 완료: themeId=$id, name=${theme.name}" } }
|
log.info { "[ThemeService.deleteTheme] 테마 삭제 완료: id=$id, name=${theme.name}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun updateTheme(id: Long, request: ThemeUpdateRequest) {
|
||||||
|
log.info { "[ThemeService.updateTheme] 테마 수정 시작: id=${id}, request=${request}" }
|
||||||
|
|
||||||
|
if (request.isAllParamsNull()) {
|
||||||
|
log.info { "[ThemeService.updateTheme] 테마 변경 사항 없음: id=${id}" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
themeValidator.validateCanUpdate(request)
|
||||||
|
|
||||||
|
val theme: ThemeEntity = findOrThrow(id)
|
||||||
|
|
||||||
|
theme.modifyIfNotNull(
|
||||||
|
request.name,
|
||||||
|
request.description,
|
||||||
|
request.thumbnailUrl,
|
||||||
|
request.difficulty,
|
||||||
|
request.price,
|
||||||
|
request.minParticipants,
|
||||||
|
request.maxParticipants,
|
||||||
|
request.availableMinutes,
|
||||||
|
request.expectedMinutesFrom,
|
||||||
|
request.expectedMinutesTo,
|
||||||
|
request.isOpen,
|
||||||
|
).also {
|
||||||
|
log.info { "[ThemeService.updateTheme] 테마 수정 완료: id=$id, request=${request}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findOrThrow(id: Long): ThemeEntity {
|
||||||
|
log.info { "[ThemeService.findOrThrow] 테마 조회 시작: id=$id" }
|
||||||
|
|
||||||
|
return themeRepository.findByIdOrNull(id)
|
||||||
|
?.also { log.info { "[ThemeService.findOrThrow] 테마 조회 완료: id=$id" } }
|
||||||
|
?: run {
|
||||||
|
log.warn { "[ThemeService.updateTheme] 테마 조회 실패: id=$id" }
|
||||||
|
throw ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,141 +0,0 @@
|
|||||||
package roomescape.theme.business
|
|
||||||
|
|
||||||
import com.github.f4b6a3.tsid.TsidFactory
|
|
||||||
import io.github.oshai.kotlinlogging.KLogger
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
|
||||||
import roomescape.common.config.next
|
|
||||||
import roomescape.member.business.MemberService
|
|
||||||
import roomescape.theme.exception.ThemeErrorCode
|
|
||||||
import roomescape.theme.exception.ThemeException
|
|
||||||
import roomescape.theme.infrastructure.persistence.v2.ThemeEntityV2
|
|
||||||
import roomescape.theme.infrastructure.persistence.v2.ThemeRepositoryV2
|
|
||||||
import roomescape.theme.web.*
|
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class ThemeServiceV2(
|
|
||||||
private val themeRepository: ThemeRepositoryV2,
|
|
||||||
private val tsidFactory: TsidFactory,
|
|
||||||
private val memberService: MemberService,
|
|
||||||
private val themeValidator: ThemeValidatorV2
|
|
||||||
) {
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun findThemesByIds(request: ThemeListRetrieveRequest): ThemeRetrieveListResponseV2 {
|
|
||||||
log.info { "[ThemeService.findThemesByIds] 예약 페이지에서의 테마 목록 조회 시작: themeIds=${request.themeIds}" }
|
|
||||||
|
|
||||||
return request.themeIds
|
|
||||||
.map { findOrThrow(it) }
|
|
||||||
.toRetrieveListResponse()
|
|
||||||
.also { log.info { "[ThemeService.findThemesByIds] ${it.themes.size}개 테마 조회 완료" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun findThemesForReservation(): ThemeRetrieveListResponseV2 {
|
|
||||||
log.info { "[ThemeService.findThemesForReservation] 예약 페이지에서의 테마 목록 조회 시작" }
|
|
||||||
|
|
||||||
return themeRepository.findOpenedThemes()
|
|
||||||
.toRetrieveListResponse()
|
|
||||||
.also { log.info { "[ThemeService.findThemesForReservation] ${it.themes.size}개 테마 조회 완료" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun findAdminThemes(): AdminThemeSummaryRetrieveListResponse {
|
|
||||||
log.info { "[ThemeService.findAdminThemes] 관리자 페이지에서의 테마 목록 조회 시작" }
|
|
||||||
|
|
||||||
return themeRepository.findAll()
|
|
||||||
.toAdminThemeSummaryListResponse()
|
|
||||||
.also { log.info { "[ThemeService.findAdminThemes] ${it.themes.size}개 테마 조회 완료" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun findAdminThemeDetail(id: Long): AdminThemeDetailRetrieveResponse {
|
|
||||||
log.info { "[ThemeService.findAdminThemeDetail] 관리자 페이지에서의 테마 상세 정보 조회 시작: id=${id}" }
|
|
||||||
|
|
||||||
val theme: ThemeEntityV2 = findOrThrow(id)
|
|
||||||
|
|
||||||
val createdBy = memberService.findSummaryById(theme.createdBy).name
|
|
||||||
val updatedBy = memberService.findSummaryById(theme.updatedBy).name
|
|
||||||
|
|
||||||
return theme.toAdminThemeDetailResponse(createdBy, updatedBy)
|
|
||||||
.also { log.info { "[ThemeService.findAdminThemeDetail] 테마 상세 조회 완료: id=$id, name=${theme.name}" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun findById(id: Long): ThemeRetrieveResponseV2 {
|
|
||||||
log.info { "[ThemeService.findById] 테마 조회 시작: id=$id" }
|
|
||||||
|
|
||||||
return findOrThrow(id).toRetrieveResponse()
|
|
||||||
.also { log.info { "[ThemeService.findById] 테마 조회 완료: id=$id" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun createTheme(request: ThemeCreateRequestV2): ThemeCreateResponseV2 {
|
|
||||||
log.info { "[ThemeService.createTheme] 테마 생성 시작: name=${request.name}" }
|
|
||||||
|
|
||||||
themeValidator.validateCanCreate(request)
|
|
||||||
|
|
||||||
val theme: ThemeEntityV2 = themeRepository.save(
|
|
||||||
request.toEntity(tsidFactory.next())
|
|
||||||
)
|
|
||||||
|
|
||||||
return ThemeCreateResponseV2(theme.id).also {
|
|
||||||
log.info { "[ThemeService.createTheme] 테마 생성 완료: id=${theme.id}, name=${theme.name}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun deleteTheme(id: Long) {
|
|
||||||
log.info { "[ThemeService.deleteTheme] 테마 삭제 시작: id=${id}" }
|
|
||||||
|
|
||||||
val theme: ThemeEntityV2 = findOrThrow(id)
|
|
||||||
|
|
||||||
themeRepository.delete(theme).also {
|
|
||||||
log.info { "[ThemeService.deleteTheme] 테마 삭제 완료: id=$id, name=${theme.name}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun updateTheme(id: Long, request: ThemeUpdateRequest) {
|
|
||||||
log.info { "[ThemeService.updateTheme] 테마 수정 시작: id=${id}, request=${request}" }
|
|
||||||
|
|
||||||
if (request.isAllParamsNull()) {
|
|
||||||
log.info { "[ThemeService.updateTheme] 테마 변경 사항 없음: id=${id}" }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
themeValidator.validateCanUpdate(request)
|
|
||||||
|
|
||||||
val theme: ThemeEntityV2 = findOrThrow(id)
|
|
||||||
|
|
||||||
theme.modifyIfNotNull(
|
|
||||||
request.name,
|
|
||||||
request.description,
|
|
||||||
request.thumbnailUrl,
|
|
||||||
request.difficulty,
|
|
||||||
request.price,
|
|
||||||
request.minParticipants,
|
|
||||||
request.maxParticipants,
|
|
||||||
request.availableMinutes,
|
|
||||||
request.expectedMinutesFrom,
|
|
||||||
request.expectedMinutesTo,
|
|
||||||
request.isOpen,
|
|
||||||
).also {
|
|
||||||
log.info { "[ThemeService.updateTheme] 테마 수정 완료: id=$id, request=${request}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findOrThrow(id: Long): ThemeEntityV2 {
|
|
||||||
log.info { "[ThemeService.findOrThrow] 테마 조회 시작: id=$id" }
|
|
||||||
|
|
||||||
return themeRepository.findByIdOrNull(id)
|
|
||||||
?.also { log.info { "[ThemeService.findOrThrow] 테마 조회 완료: id=$id" } }
|
|
||||||
?: run {
|
|
||||||
log.warn { "[ThemeService.updateTheme] 테마 조회 실패: id=$id" }
|
|
||||||
throw ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -5,8 +5,8 @@ import io.github.oshai.kotlinlogging.KotlinLogging
|
|||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import roomescape.theme.exception.ThemeErrorCode
|
import roomescape.theme.exception.ThemeErrorCode
|
||||||
import roomescape.theme.exception.ThemeException
|
import roomescape.theme.exception.ThemeException
|
||||||
import roomescape.theme.infrastructure.persistence.v2.ThemeRepositoryV2
|
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||||
import roomescape.theme.web.ThemeCreateRequestV2
|
import roomescape.theme.web.ThemeCreateRequest
|
||||||
import roomescape.theme.web.ThemeUpdateRequest
|
import roomescape.theme.web.ThemeUpdateRequest
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
@ -16,8 +16,8 @@ const val MIN_PARTICIPANTS = 1
|
|||||||
const val MIN_DURATION = 1
|
const val MIN_DURATION = 1
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class ThemeValidatorV2(
|
class ThemeValidator(
|
||||||
private val themeRepository: ThemeRepositoryV2,
|
private val themeRepository: ThemeRepository,
|
||||||
) {
|
) {
|
||||||
fun validateCanUpdate(request: ThemeUpdateRequest) {
|
fun validateCanUpdate(request: ThemeUpdateRequest) {
|
||||||
validateProperties(
|
validateProperties(
|
||||||
@ -30,7 +30,7 @@ class ThemeValidatorV2(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validateCanCreate(request: ThemeCreateRequestV2) {
|
fun validateCanCreate(request: ThemeCreateRequest) {
|
||||||
if (themeRepository.existsByName(request.name)) {
|
if (themeRepository.existsByName(request.name)) {
|
||||||
log.info { "[ThemeValidator.validateCanCreate] 이름 중복으로 인한 실패: name=${request.name}" }
|
log.info { "[ThemeValidator.validateCanCreate] 이름 중복으로 인한 실패: name=${request.name}" }
|
||||||
throw ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
|
throw ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
|
||||||
@ -1,52 +0,0 @@
|
|||||||
package roomescape.theme.docs
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter
|
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag
|
|
||||||
import jakarta.validation.Valid
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam
|
|
||||||
import roomescape.auth.web.support.Admin
|
|
||||||
import roomescape.auth.web.support.LoginRequired
|
|
||||||
import roomescape.common.dto.response.CommonApiResponse
|
|
||||||
import roomescape.theme.web.ThemeCreateRequest
|
|
||||||
import roomescape.theme.web.ThemeCreateResponse
|
|
||||||
import roomescape.theme.web.ThemeRetrieveListResponse
|
|
||||||
import roomescape.theme.web.ThemeRetrieveResponse
|
|
||||||
|
|
||||||
@Tag(name = "5. 테마 API", description = "테마를 조회 / 추가 / 삭제할 때 사용합니다.")
|
|
||||||
interface ThemeAPI {
|
|
||||||
|
|
||||||
@LoginRequired
|
|
||||||
@Operation(summary = "모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
|
||||||
fun findThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
|
||||||
|
|
||||||
@Operation(summary = "가장 많이 예약된 테마 조회")
|
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
|
||||||
fun findMostReservedThemes(
|
|
||||||
@RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int
|
|
||||||
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
|
||||||
|
|
||||||
@Admin
|
|
||||||
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
|
||||||
@ApiResponses(
|
|
||||||
ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true),
|
|
||||||
)
|
|
||||||
fun createTheme(
|
|
||||||
@Valid @RequestBody request: ThemeCreateRequest,
|
|
||||||
): ResponseEntity<CommonApiResponse<ThemeCreateResponse>>
|
|
||||||
|
|
||||||
@Admin
|
|
||||||
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
|
||||||
@ApiResponses(
|
|
||||||
ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true),
|
|
||||||
)
|
|
||||||
fun deleteTheme(
|
|
||||||
@PathVariable id: Long
|
|
||||||
): ResponseEntity<CommonApiResponse<Unit>>
|
|
||||||
}
|
|
||||||
@ -13,11 +13,11 @@ import roomescape.auth.web.support.LoginRequired
|
|||||||
import roomescape.common.dto.response.CommonApiResponse
|
import roomescape.common.dto.response.CommonApiResponse
|
||||||
import roomescape.theme.web.AdminThemeDetailRetrieveResponse
|
import roomescape.theme.web.AdminThemeDetailRetrieveResponse
|
||||||
import roomescape.theme.web.AdminThemeSummaryRetrieveListResponse
|
import roomescape.theme.web.AdminThemeSummaryRetrieveListResponse
|
||||||
import roomescape.theme.web.ThemeCreateRequestV2
|
import roomescape.theme.web.ThemeCreateRequest
|
||||||
import roomescape.theme.web.ThemeCreateResponseV2
|
import roomescape.theme.web.ThemeCreateResponseV2
|
||||||
import roomescape.theme.web.ThemeListRetrieveRequest
|
import roomescape.theme.web.ThemeListRetrieveRequest
|
||||||
import roomescape.theme.web.ThemeUpdateRequest
|
import roomescape.theme.web.ThemeUpdateRequest
|
||||||
import roomescape.theme.web.ThemeRetrieveListResponseV2
|
import roomescape.theme.web.ThemeRetrieveListResponse
|
||||||
|
|
||||||
@Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.")
|
@Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.")
|
||||||
interface ThemeAPIV2 {
|
interface ThemeAPIV2 {
|
||||||
@ -35,7 +35,7 @@ interface ThemeAPIV2 {
|
|||||||
@Admin
|
@Admin
|
||||||
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun createTheme(@Valid @RequestBody themeCreateRequestV2: ThemeCreateRequestV2): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>>
|
fun createTheme(@Valid @RequestBody themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>>
|
||||||
|
|
||||||
@Admin
|
@Admin
|
||||||
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
||||||
@ -53,10 +53,10 @@ interface ThemeAPIV2 {
|
|||||||
@LoginRequired
|
@LoginRequired
|
||||||
@Operation(summary = "예약 페이지에서 모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
@Operation(summary = "예약 페이지에서 모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponseV2>>
|
fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
||||||
|
|
||||||
@LoginRequired
|
@LoginRequired
|
||||||
@Operation(summary = "예약 페이지에서 입력한 날짜에 가능한 테마 조회", description = "입력한 날짜에 가능한 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
@Operation(summary = "예약 페이지에서 입력한 날짜에 가능한 테마 조회", description = "입력한 날짜에 가능한 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun findThemesByIds(request: ThemeListRetrieveRequest): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponseV2>>
|
fun findThemesByIds(request: ThemeListRetrieveRequest): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
||||||
}
|
}
|
||||||
@ -1,47 +0,0 @@
|
|||||||
package roomescape.theme.implement
|
|
||||||
|
|
||||||
import io.github.oshai.kotlinlogging.KLogger
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import roomescape.theme.exception.ThemeErrorCode
|
|
||||||
import roomescape.theme.exception.ThemeException
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
|
||||||
import java.time.LocalDate
|
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class ThemeFinder(
|
|
||||||
private val themeRepository: ThemeRepository
|
|
||||||
) {
|
|
||||||
fun findAll(): List<ThemeEntity> {
|
|
||||||
log.debug { "[ThemeFinder.findAll] 시작" }
|
|
||||||
|
|
||||||
return themeRepository.findAll()
|
|
||||||
.also { log.debug { "[TimeFinder.findAll] ${it.size}개 테마 조회 완료" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findById(id: Long): ThemeEntity {
|
|
||||||
log.debug { "[ThemeFinder.findById] 조회 시작: memberId=$id" }
|
|
||||||
|
|
||||||
return themeRepository.findByIdOrNull(id)
|
|
||||||
?.also { log.debug { "[ThemeFinder.findById] 조회 완료: id=$id, name=${it.name}" } }
|
|
||||||
?: run {
|
|
||||||
log.warn { "[ThemeFinder.findById] 조회 실패: id=$id" }
|
|
||||||
throw ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findMostReservedThemes(
|
|
||||||
count: Int,
|
|
||||||
startFrom: LocalDate,
|
|
||||||
endAt: LocalDate
|
|
||||||
): List<ThemeEntity> {
|
|
||||||
log.debug { "[ThemeFinder.findMostReservedThemes] 시작. count=$count, startFrom=$startFrom, endAt=$endAt" }
|
|
||||||
|
|
||||||
return themeRepository.findPopularThemes(startFrom, endAt, count)
|
|
||||||
.also { log.debug { "[ThemeFinder.findMostReservedThemes] ${it.size} / ${count}개 테마 조회 완료" } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package roomescape.theme.implement
|
|
||||||
|
|
||||||
import io.github.oshai.kotlinlogging.KLogger
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import roomescape.theme.exception.ThemeErrorCode
|
|
||||||
import roomescape.theme.exception.ThemeException
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class ThemeValidator(
|
|
||||||
private val themeRepository: ThemeRepository
|
|
||||||
) {
|
|
||||||
fun validateNameAlreadyExists(name: String) {
|
|
||||||
log.debug { "[ThemeValidator.validateNameAlreadyExists] 시작: name=$name" }
|
|
||||||
|
|
||||||
if (themeRepository.existsByName(name)) {
|
|
||||||
log.info { "[ThemeService.createTheme] 이름 중복: name=${name}" }
|
|
||||||
throw ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug { "[ThemeValidator.validateNameAlreadyExists] 완료: name=$name" }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun validateIsReserved(theme: ThemeEntity) {
|
|
||||||
val themeId: Long = theme.id ?: run {
|
|
||||||
log.warn { "[ThemeValidator.validateIsReserved] ID를 찾을 수 없음: name:${theme.name}" }
|
|
||||||
throw ThemeException(ThemeErrorCode.INVALID_REQUEST_VALUE)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug { "[ThemeValidator.validateIsReserved] 시작: themeId=${themeId}" }
|
|
||||||
|
|
||||||
if (themeRepository.isReservedTheme(themeId)) {
|
|
||||||
log.info { "[ThemeService.deleteTheme] 예약이 있는 테마: themeId=$themeId" }
|
|
||||||
throw ThemeException(ThemeErrorCode.THEME_ALREADY_RESERVED)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug { "[ThemeValidator.validateIsReserved] 완료: themeId=$themeId" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
package roomescape.theme.implement
|
|
||||||
|
|
||||||
import com.github.f4b6a3.tsid.TsidFactory
|
|
||||||
import io.github.oshai.kotlinlogging.KLogger
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import roomescape.common.config.next
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
|
||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class ThemeWriter(
|
|
||||||
private val themeValidator: ThemeValidator,
|
|
||||||
private val themeRepository: ThemeRepository,
|
|
||||||
private val tsidFactory: TsidFactory
|
|
||||||
) {
|
|
||||||
fun create(name: String, description: String, thumbnail: String): ThemeEntity {
|
|
||||||
log.debug { "[ThemeWriter.create] 시작: name=$name" }
|
|
||||||
themeValidator.validateNameAlreadyExists(name)
|
|
||||||
|
|
||||||
val theme = ThemeEntity(
|
|
||||||
_id = tsidFactory.next(),
|
|
||||||
name = name,
|
|
||||||
description = description,
|
|
||||||
thumbnail = thumbnail
|
|
||||||
)
|
|
||||||
|
|
||||||
return themeRepository.save(theme)
|
|
||||||
.also { log.debug { "[ThemeWriter.create] 완료: name=$name, id=${it.id}" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun delete(theme: ThemeEntity) {
|
|
||||||
log.debug { "[ThemeWriter.delete] 시작: id=${theme.id}" }
|
|
||||||
themeValidator.validateIsReserved(theme)
|
|
||||||
|
|
||||||
themeRepository.delete(theme)
|
|
||||||
.also { log.debug { "[ThemeWriter.delete] 완료: id=${theme.id}, name=${theme.name}" } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,25 +2,65 @@ package roomescape.theme.infrastructure.persistence
|
|||||||
|
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
import jakarta.persistence.Id
|
import jakarta.persistence.EnumType
|
||||||
|
import jakarta.persistence.Enumerated
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import roomescape.common.entity.BaseEntity
|
import roomescape.common.entity.AuditingBaseEntity
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "themes")
|
@Table(name = "theme")
|
||||||
class ThemeEntity(
|
class ThemeEntity(
|
||||||
@Id
|
id: Long,
|
||||||
@Column(name = "theme_id")
|
|
||||||
private var _id: Long?,
|
|
||||||
|
|
||||||
@Column(name = "name", nullable = false)
|
|
||||||
var name: String,
|
var name: String,
|
||||||
|
|
||||||
@Column(name = "description", nullable = false)
|
|
||||||
var description: String,
|
var description: String,
|
||||||
|
var thumbnailUrl: String,
|
||||||
|
|
||||||
@Column(name = "thumbnail", nullable = false)
|
@Enumerated(value = EnumType.STRING)
|
||||||
var thumbnail: String,
|
var difficulty: Difficulty,
|
||||||
): BaseEntity() {
|
|
||||||
override fun getId(): Long? = _id
|
var price: Int,
|
||||||
|
var minParticipants: Short,
|
||||||
|
var maxParticipants: Short,
|
||||||
|
var availableMinutes: Short,
|
||||||
|
var expectedMinutesFrom: Short,
|
||||||
|
var expectedMinutesTo: Short,
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TINYINT", length = 1)
|
||||||
|
var isOpen: Boolean
|
||||||
|
) : AuditingBaseEntity(id) {
|
||||||
|
|
||||||
|
fun modifyIfNotNull(
|
||||||
|
name: String?,
|
||||||
|
description: String?,
|
||||||
|
thumbnailUrl: String?,
|
||||||
|
difficulty: Difficulty?,
|
||||||
|
price: Int?,
|
||||||
|
minParticipants: Short?,
|
||||||
|
maxParticipants: Short?,
|
||||||
|
availableMinutes: Short?,
|
||||||
|
expectedMinutesFrom: Short?,
|
||||||
|
expectedMinutesTo: Short?,
|
||||||
|
isOpen: Boolean?
|
||||||
|
) {
|
||||||
|
name?.let { this.name = it }
|
||||||
|
description?.let { this.description = it }
|
||||||
|
thumbnailUrl?.let { this.thumbnailUrl = it }
|
||||||
|
difficulty?.let { this.difficulty = it }
|
||||||
|
price?.let { this.price = it }
|
||||||
|
minParticipants?.let { this.minParticipants = it }
|
||||||
|
maxParticipants?.let { this.maxParticipants = it }
|
||||||
|
availableMinutes?.let { this.availableMinutes = it }
|
||||||
|
expectedMinutesFrom?.let { this.expectedMinutesFrom = it }
|
||||||
|
expectedMinutesTo?.let { this.expectedMinutesTo = it }
|
||||||
|
isOpen?.let { this.isOpen = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Difficulty {
|
||||||
|
VERY_EASY,
|
||||||
|
EASY,
|
||||||
|
NORMAL,
|
||||||
|
HARD,
|
||||||
|
VERY_HARD
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,31 +2,11 @@ package roomescape.theme.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.jpa.repository.Query
|
||||||
import java.time.LocalDate
|
|
||||||
|
|
||||||
interface ThemeRepository : JpaRepository<ThemeEntity, Long> {
|
interface ThemeRepository: JpaRepository<ThemeEntity, Long> {
|
||||||
|
|
||||||
@Query(value = """
|
@Query("SELECT t FROM ThemeEntity t WHERE t.isOpen = true")
|
||||||
SELECT t
|
fun findOpenedThemes(): List<ThemeEntity>
|
||||||
FROM ThemeEntity t
|
|
||||||
RIGHT JOIN ReservationEntity r ON t._id = r.theme._id
|
|
||||||
WHERE r.date BETWEEN :startFrom AND :endAt
|
|
||||||
GROUP BY r.theme._id
|
|
||||||
ORDER BY COUNT(r.theme._id) DESC, t._id ASC
|
|
||||||
LIMIT :count
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
fun findPopularThemes(startFrom: LocalDate, endAt: LocalDate, count: Int): List<ThemeEntity>
|
|
||||||
|
|
||||||
fun existsByName(name: String): Boolean
|
fun existsByName(name: String): Boolean
|
||||||
|
|
||||||
@Query(value = """
|
|
||||||
SELECT EXISTS(
|
|
||||||
SELECT 1
|
|
||||||
FROM ReservationEntity r
|
|
||||||
WHERE r.theme._id = :id
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
fun isReservedTheme(id: Long): Boolean
|
|
||||||
}
|
}
|
||||||
@ -1,66 +0,0 @@
|
|||||||
package roomescape.theme.infrastructure.persistence.v2
|
|
||||||
|
|
||||||
import jakarta.persistence.Column
|
|
||||||
import jakarta.persistence.Entity
|
|
||||||
import jakarta.persistence.EnumType
|
|
||||||
import jakarta.persistence.Enumerated
|
|
||||||
import jakarta.persistence.Table
|
|
||||||
import roomescape.common.entity.AuditingBaseEntity
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "theme")
|
|
||||||
class ThemeEntityV2(
|
|
||||||
id: Long,
|
|
||||||
|
|
||||||
var name: String,
|
|
||||||
var description: String,
|
|
||||||
var thumbnailUrl: String,
|
|
||||||
|
|
||||||
@Enumerated(value = EnumType.STRING)
|
|
||||||
var difficulty: Difficulty,
|
|
||||||
|
|
||||||
var price: Int,
|
|
||||||
var minParticipants: Short,
|
|
||||||
var maxParticipants: Short,
|
|
||||||
var availableMinutes: Short,
|
|
||||||
var expectedMinutesFrom: Short,
|
|
||||||
var expectedMinutesTo: Short,
|
|
||||||
|
|
||||||
@Column(columnDefinition = "TINYINT", length = 1)
|
|
||||||
var isOpen: Boolean
|
|
||||||
) : AuditingBaseEntity(id) {
|
|
||||||
|
|
||||||
fun modifyIfNotNull(
|
|
||||||
name: String?,
|
|
||||||
description: String?,
|
|
||||||
thumbnailUrl: String?,
|
|
||||||
difficulty: Difficulty?,
|
|
||||||
price: Int?,
|
|
||||||
minParticipants: Short?,
|
|
||||||
maxParticipants: Short?,
|
|
||||||
availableMinutes: Short?,
|
|
||||||
expectedMinutesFrom: Short?,
|
|
||||||
expectedMinutesTo: Short?,
|
|
||||||
isOpen: Boolean?
|
|
||||||
) {
|
|
||||||
name?.let { this.name = it }
|
|
||||||
description?.let { this.description = it }
|
|
||||||
thumbnailUrl?.let { this.thumbnailUrl = it }
|
|
||||||
difficulty?.let { this.difficulty = it }
|
|
||||||
price?.let { this.price = it }
|
|
||||||
minParticipants?.let { this.minParticipants = it }
|
|
||||||
maxParticipants?.let { this.maxParticipants = it }
|
|
||||||
availableMinutes?.let { this.availableMinutes = it }
|
|
||||||
expectedMinutesFrom?.let { this.expectedMinutesFrom = it }
|
|
||||||
expectedMinutesTo?.let { this.expectedMinutesTo = it }
|
|
||||||
isOpen?.let { this.isOpen = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Difficulty {
|
|
||||||
VERY_EASY,
|
|
||||||
EASY,
|
|
||||||
NORMAL,
|
|
||||||
HARD,
|
|
||||||
VERY_HARD
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package roomescape.theme.infrastructure.persistence.v2
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
import org.springframework.data.jpa.repository.Query
|
|
||||||
|
|
||||||
interface ThemeRepositoryV2: JpaRepository<ThemeEntityV2, Long> {
|
|
||||||
|
|
||||||
@Query("SELECT t FROM ThemeEntityV2 t WHERE t.isOpen = true")
|
|
||||||
fun findOpenedThemes(): List<ThemeEntityV2>
|
|
||||||
|
|
||||||
fun existsByName(name: String): Boolean
|
|
||||||
}
|
|
||||||
@ -1,51 +1,69 @@
|
|||||||
package roomescape.theme.web
|
package roomescape.theme.web
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter
|
|
||||||
import jakarta.validation.Valid
|
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import roomescape.common.dto.response.CommonApiResponse
|
import roomescape.common.dto.response.CommonApiResponse
|
||||||
import roomescape.theme.business.ThemeService
|
import roomescape.theme.business.ThemeService
|
||||||
import roomescape.theme.docs.ThemeAPI
|
import roomescape.theme.docs.ThemeAPIV2
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
class ThemeController(
|
class ThemeController(
|
||||||
private val themeService: ThemeService
|
private val themeService: ThemeService,
|
||||||
) : ThemeAPI {
|
) : ThemeAPIV2 {
|
||||||
|
|
||||||
@GetMapping("/themes")
|
@PostMapping("/themes/retrieve")
|
||||||
override fun findThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
override fun findThemesByIds(
|
||||||
val response: ThemeRetrieveListResponse = themeService.findThemes()
|
@RequestBody request: ThemeListRetrieveRequest
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/themes/most-reserved-last-week")
|
|
||||||
override fun findMostReservedThemes(
|
|
||||||
@RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int
|
|
||||||
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
||||||
val response: ThemeRetrieveListResponse = themeService.findMostReservedThemes(count)
|
val response = themeService.findThemesByIds(request)
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/themes")
|
@GetMapping("/v2/themes")
|
||||||
override fun createTheme(
|
override fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
||||||
@RequestBody @Valid request: ThemeCreateRequest
|
val response = themeService.findThemesForReservation()
|
||||||
): ResponseEntity<CommonApiResponse<ThemeCreateResponse>> {
|
|
||||||
val themeResponse: ThemeCreateResponse = themeService.createTheme(request)
|
|
||||||
|
|
||||||
return ResponseEntity.created(URI.create("/themes/${themeResponse.id}"))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
.body(CommonApiResponse(themeResponse))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/themes/{id}")
|
@GetMapping("/admin/themes")
|
||||||
override fun deleteTheme(
|
override fun findAdminThemes(): ResponseEntity<CommonApiResponse<AdminThemeSummaryRetrieveListResponse>> {
|
||||||
@PathVariable id: Long
|
val response = themeService.findAdminThemes()
|
||||||
): ResponseEntity<CommonApiResponse<Unit>> {
|
|
||||||
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/admin/themes/{id}")
|
||||||
|
override fun findAdminThemeDetail(@PathVariable id: Long): ResponseEntity<CommonApiResponse<AdminThemeDetailRetrieveResponse>> {
|
||||||
|
val response = themeService.findAdminThemeDetail(id)
|
||||||
|
|
||||||
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/admin/themes")
|
||||||
|
override fun createTheme(themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>> {
|
||||||
|
val response = themeService.createTheme(themeCreateRequest)
|
||||||
|
|
||||||
|
return ResponseEntity.created(URI.create("/admin/themes/${response.id}"))
|
||||||
|
.body(CommonApiResponse(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/admin/themes/{id}")
|
||||||
|
override fun deleteTheme(@PathVariable id: Long): ResponseEntity<CommonApiResponse<Unit>> {
|
||||||
themeService.deleteTheme(id)
|
themeService.deleteTheme(id)
|
||||||
|
|
||||||
return ResponseEntity.noContent().build()
|
return ResponseEntity.noContent().build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/admin/themes/{id}")
|
||||||
|
override fun updateTheme(
|
||||||
|
@PathVariable id: Long,
|
||||||
|
themeUpdateRequest: ThemeUpdateRequest
|
||||||
|
): ResponseEntity<CommonApiResponse<Unit>> {
|
||||||
|
themeService.updateTheme(id, themeUpdateRequest)
|
||||||
|
|
||||||
|
return ResponseEntity.ok().build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
package roomescape.theme.web
|
|
||||||
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.web.bind.annotation.*
|
|
||||||
import roomescape.common.dto.response.CommonApiResponse
|
|
||||||
import roomescape.theme.business.ThemeServiceV2
|
|
||||||
import roomescape.theme.docs.ThemeAPIV2
|
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
class ThemeControllerV2(
|
|
||||||
private val themeService: ThemeServiceV2,
|
|
||||||
) : ThemeAPIV2 {
|
|
||||||
|
|
||||||
@PostMapping("/themes/retrieve")
|
|
||||||
override fun findThemesByIds(
|
|
||||||
@RequestBody request: ThemeListRetrieveRequest
|
|
||||||
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponseV2>> {
|
|
||||||
val response = themeService.findThemesByIds(request)
|
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/v2/themes")
|
|
||||||
override fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponseV2>> {
|
|
||||||
val response = themeService.findThemesForReservation()
|
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/admin/themes")
|
|
||||||
override fun findAdminThemes(): ResponseEntity<CommonApiResponse<AdminThemeSummaryRetrieveListResponse>> {
|
|
||||||
val response = themeService.findAdminThemes()
|
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/admin/themes/{id}")
|
|
||||||
override fun findAdminThemeDetail(@PathVariable id: Long): ResponseEntity<CommonApiResponse<AdminThemeDetailRetrieveResponse>> {
|
|
||||||
val response = themeService.findAdminThemeDetail(id)
|
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/admin/themes")
|
|
||||||
override fun createTheme(themeCreateRequestV2: ThemeCreateRequestV2): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>> {
|
|
||||||
val response = themeService.createTheme(themeCreateRequestV2)
|
|
||||||
|
|
||||||
return ResponseEntity.created(URI.create("/admin/themes/${response.id}"))
|
|
||||||
.body(CommonApiResponse(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/admin/themes/{id}")
|
|
||||||
override fun deleteTheme(@PathVariable id: Long): ResponseEntity<CommonApiResponse<Unit>> {
|
|
||||||
themeService.deleteTheme(id)
|
|
||||||
|
|
||||||
return ResponseEntity.noContent().build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@PatchMapping("/admin/themes/{id}")
|
|
||||||
override fun updateTheme(
|
|
||||||
@PathVariable id: Long,
|
|
||||||
themeUpdateRequest: ThemeUpdateRequest
|
|
||||||
): ResponseEntity<CommonApiResponse<Unit>> {
|
|
||||||
themeService.updateTheme(id, themeUpdateRequest)
|
|
||||||
|
|
||||||
return ResponseEntity.ok().build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
package roomescape.theme.web
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
|
||||||
import jakarta.validation.constraints.NotBlank
|
|
||||||
import jakarta.validation.constraints.Size
|
|
||||||
import org.hibernate.validator.constraints.URL
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
|
||||||
|
|
||||||
data class ThemeCreateRequest(
|
|
||||||
@NotBlank
|
|
||||||
@Size(max = 20)
|
|
||||||
val name: String,
|
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@Size(max = 100)
|
|
||||||
val description: String,
|
|
||||||
|
|
||||||
@URL
|
|
||||||
@NotBlank
|
|
||||||
@Schema(description = "썸네일 이미지 주소(URL).")
|
|
||||||
val thumbnail: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ThemeCreateResponse(
|
|
||||||
val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val description: String,
|
|
||||||
@Schema(description = "썸네일 이미지 주소(URL).")
|
|
||||||
val thumbnail: String
|
|
||||||
)
|
|
||||||
|
|
||||||
fun ThemeEntity.toCreateResponse(): ThemeCreateResponse = ThemeCreateResponse(
|
|
||||||
id = this.id!!,
|
|
||||||
name = this.name,
|
|
||||||
description = this.description,
|
|
||||||
thumbnail = this.thumbnail
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ThemeRetrieveResponse(
|
|
||||||
val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val description: String,
|
|
||||||
@Schema(description = "썸네일 이미지 주소(URL).")
|
|
||||||
val thumbnail: String
|
|
||||||
)
|
|
||||||
|
|
||||||
fun ThemeEntity.toRetrieveResponse(): ThemeRetrieveResponse = ThemeRetrieveResponse(
|
|
||||||
id = this.id!!,
|
|
||||||
name = this.name,
|
|
||||||
description = this.description,
|
|
||||||
thumbnail = this.thumbnail
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ThemeRetrieveListResponse(
|
|
||||||
val themes: List<ThemeRetrieveResponse>
|
|
||||||
)
|
|
||||||
|
|
||||||
fun List<ThemeEntity>.toRetrieveListResponse(): ThemeRetrieveListResponse = ThemeRetrieveListResponse(
|
|
||||||
themes = this.map { it.toRetrieveResponse() }
|
|
||||||
)
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
package roomescape.theme.web
|
package roomescape.theme.web
|
||||||
|
|
||||||
import roomescape.theme.infrastructure.persistence.v2.Difficulty
|
import roomescape.theme.infrastructure.persistence.Difficulty
|
||||||
import roomescape.theme.infrastructure.persistence.v2.ThemeEntityV2
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
data class ThemeCreateRequestV2(
|
data class ThemeCreateRequest(
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
val thumbnailUrl: String,
|
val thumbnailUrl: String,
|
||||||
@ -22,7 +22,7 @@ data class ThemeCreateResponseV2(
|
|||||||
val id: Long
|
val id: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ThemeCreateRequestV2.toEntity(id: Long) = ThemeEntityV2(
|
fun ThemeCreateRequest.toEntity(id: Long) = ThemeEntity(
|
||||||
id = id,
|
id = id,
|
||||||
name = this.name,
|
name = this.name,
|
||||||
description = this.description,
|
description = this.description,
|
||||||
@ -73,7 +73,7 @@ data class AdminThemeSummaryRetrieveResponse(
|
|||||||
val isOpen: Boolean
|
val isOpen: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ThemeEntityV2.toAdminThemeSummaryResponse() = AdminThemeSummaryRetrieveResponse(
|
fun ThemeEntity.toAdminThemeSummaryResponse() = AdminThemeSummaryRetrieveResponse(
|
||||||
id = this.id,
|
id = this.id,
|
||||||
name = this.name,
|
name = this.name,
|
||||||
difficulty = this.difficulty,
|
difficulty = this.difficulty,
|
||||||
@ -85,7 +85,7 @@ data class AdminThemeSummaryRetrieveListResponse(
|
|||||||
val themes: List<AdminThemeSummaryRetrieveResponse>
|
val themes: List<AdminThemeSummaryRetrieveResponse>
|
||||||
)
|
)
|
||||||
|
|
||||||
fun List<ThemeEntityV2>.toAdminThemeSummaryListResponse() = AdminThemeSummaryRetrieveListResponse(
|
fun List<ThemeEntity>.toAdminThemeSummaryListResponse() = AdminThemeSummaryRetrieveListResponse(
|
||||||
themes = this.map { it.toAdminThemeSummaryResponse() }
|
themes = this.map { it.toAdminThemeSummaryResponse() }
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ data class AdminThemeDetailRetrieveResponse(
|
|||||||
val updatedBy: String,
|
val updatedBy: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ThemeEntityV2.toAdminThemeDetailResponse(createUserName: String, updateUserName: String) =
|
fun ThemeEntity.toAdminThemeDetailResponse(createUserName: String, updateUserName: String) =
|
||||||
AdminThemeDetailRetrieveResponse(
|
AdminThemeDetailRetrieveResponse(
|
||||||
id = this.id,
|
id = this.id,
|
||||||
name = this.name,
|
name = this.name,
|
||||||
@ -146,7 +146,7 @@ data class ThemeRetrieveResponseV2(
|
|||||||
val expectedMinutesTo: Short
|
val expectedMinutesTo: Short
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ThemeEntityV2.toRetrieveResponse() = ThemeRetrieveResponseV2(
|
fun ThemeEntity.toRetrieveResponse() = ThemeRetrieveResponseV2(
|
||||||
id = this.id,
|
id = this.id,
|
||||||
name = this.name,
|
name = this.name,
|
||||||
thumbnailUrl = this.thumbnailUrl,
|
thumbnailUrl = this.thumbnailUrl,
|
||||||
@ -160,10 +160,10 @@ fun ThemeEntityV2.toRetrieveResponse() = ThemeRetrieveResponseV2(
|
|||||||
expectedMinutesTo = this.expectedMinutesTo
|
expectedMinutesTo = this.expectedMinutesTo
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ThemeRetrieveListResponseV2(
|
data class ThemeRetrieveListResponse(
|
||||||
val themes: List<ThemeRetrieveResponseV2>
|
val themes: List<ThemeRetrieveResponseV2>
|
||||||
)
|
)
|
||||||
|
|
||||||
fun List<ThemeEntityV2>.toRetrieveListResponse() = ThemeRetrieveListResponseV2(
|
fun List<ThemeEntity>.toRetrieveListResponse() = ThemeRetrieveListResponse(
|
||||||
themes = this.map { it.toRetrieveResponse() }
|
themes = this.map { it.toRetrieveResponse() }
|
||||||
)
|
)
|
||||||
Loading…
x
Reference in New Issue
Block a user