diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt index fb410b13..2232613e 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt @@ -7,7 +7,7 @@ import com.sangdol.roomescape.reservation.exception.ReservationException import com.sangdol.roomescape.reservation.web.PendingReservationCreateRequest import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus import com.sangdol.roomescape.schedule.web.ScheduleSummaryResponse -import com.sangdol.roomescape.theme.web.ThemeInfoResponse +import com.sangdol.roomescape.theme.dto.ThemeInfoResponse import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/AdminThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/AdminThemeService.kt new file mode 100644 index 00000000..43c04df7 --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/AdminThemeService.kt @@ -0,0 +1,136 @@ +package com.sangdol.roomescape.theme.business + +import com.sangdol.common.persistence.IDGenerator +import com.sangdol.roomescape.admin.business.AdminService +import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.theme.dto.ThemeDetailResponse +import com.sangdol.roomescape.theme.dto.ThemeSummaryListResponse +import com.sangdol.roomescape.theme.dto.ThemeNameListResponse +import com.sangdol.roomescape.theme.dto.ThemeCreateRequest +import com.sangdol.roomescape.theme.dto.ThemeCreateResponse +import com.sangdol.roomescape.theme.dto.ThemeUpdateRequest +import com.sangdol.roomescape.theme.exception.ThemeErrorCode +import com.sangdol.roomescape.theme.exception.ThemeException +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.theme.mapper.toDetailResponse +import com.sangdol.roomescape.theme.mapper.toSummaryListResponse +import com.sangdol.roomescape.theme.mapper.toEntity +import com.sangdol.roomescape.theme.mapper.toNameListResponse +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 + +private val log: KLogger = KotlinLogging.logger {} + +@Service +class AdminThemeService( + private val themeRepository: ThemeRepository, + private val themeValidator: ThemeValidator, + private val idGenerator: IDGenerator, + private val adminService: AdminService +) { + @Transactional(readOnly = true) + fun findThemeSummaries(): ThemeSummaryListResponse { + log.info { "[findAdminThemes] 관리자 페이지에서의 테마 목록 조회 시작" } + + return themeRepository.findAll() + .toSummaryListResponse() + .also { log.info { "[findAdminThemes] ${it.themes.size}개 테마 조회 완료" } } + } + + @Transactional(readOnly = true) + fun findThemeDetail(id: Long): ThemeDetailResponse { + log.info { "[findAdminThemeDetail] 관리자 페이지에서의 테마 상세 정보 조회 시작: id=${id}" } + + val theme: ThemeEntity = findOrThrow(id) + + val createdBy = adminService.findOperatorOrUnknown(theme.createdBy) + val updatedBy = adminService.findOperatorOrUnknown(theme.updatedBy) + val audit = AuditingInfo(theme.createdAt, createdBy, theme.updatedAt, updatedBy) + + return theme.toDetailResponse(audit) + .also { log.info { "[findAdminThemeDetail] 테마 상세 조회 완료: id=$id, name=${theme.name}" } } + } + + @Transactional(readOnly = true) + fun findActiveThemes(): ThemeNameListResponse { + log.info { "[findActiveThemes] open 상태인 모든 테마 조회 시작" } + + return themeRepository.findActiveThemes() + .toNameListResponse() + .also { + log.info { "[findActiveThemes] ${it.themes.size}개 테마 조회 완료" } + } + } + + + @Transactional + fun createTheme(request: ThemeCreateRequest): ThemeCreateResponse { + log.info { "[createTheme] 테마 생성 시작: name=${request.name}" } + + themeValidator.validateCanCreate(request) + + val theme: ThemeEntity = request.toEntity(id = idGenerator.create()) + .also { themeRepository.save(it) } + + return ThemeCreateResponse(theme.id).also { + log.info { "[createTheme] 테마 생성 완료: id=${theme.id}, name=${theme.name}" } + } + } + + + @Transactional + fun deleteTheme(id: Long) { + log.info { "[deleteTheme] 테마 삭제 시작: id=${id}" } + + val theme: ThemeEntity = findOrThrow(id) + + themeRepository.delete(theme).also { + log.info { "[deleteTheme] 테마 삭제 완료: id=$id, name=${theme.name}" } + } + } + + @Transactional + fun updateTheme(id: Long, request: ThemeUpdateRequest) { + log.info { "[updateTheme] 테마 수정 시작: id=${id}, request=${request}" } + + if (request.isAllParamsNull()) { + log.info { "[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.isActive, + ).also { + log.info { "[updateTheme] 테마 수정 완료: id=$id, request=${request}" } + } + } + + private fun findOrThrow(id: Long): ThemeEntity { + log.info { "[findOrThrow] 테마 조회 시작: id=$id" } + + return themeRepository.findByIdOrNull(id) + ?.also { log.info { "[findOrThrow] 테마 조회 완료: id=$id" } } + ?: run { + log.warn { "[findOrThrow] 테마 조회 실패: id=$id" } + throw ThemeException(ThemeErrorCode.THEME_NOT_FOUND) + } + } +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/DateUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/DateUtils.kt deleted file mode 100644 index 1a0ba300..00000000 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/DateUtils.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.sangdol.roomescape.theme.business - -import java.time.DayOfWeek -import java.time.LocalDate -import java.time.temporal.TemporalAdjusters - -object DateUtils { - fun getSundayOfPreviousWeek(date: LocalDate): LocalDate = date - .minusWeeks(1) - .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)) -} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt index 78788a17..50e38130 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt @@ -1,44 +1,38 @@ package com.sangdol.roomescape.theme.business -import com.sangdol.common.persistence.IDGenerator import com.sangdol.common.utils.KoreaDate -import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.theme.dto.ThemeInfoListResponse +import com.sangdol.roomescape.theme.dto.ThemeInfoResponse import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.exception.ThemeException -import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository -import com.sangdol.roomescape.theme.web.* +import com.sangdol.roomescape.theme.mapper.toInfoResponse +import com.sangdol.roomescape.theme.mapper.toListResponse 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 java.time.DayOfWeek +import java.time.LocalDate +import java.time.temporal.TemporalAdjusters private val log: KLogger = KotlinLogging.logger {} -/** - * Structure: - * - Public: 모두가 접근 가능한 메서드 - * - Store Admin: 매장 관리자가 사용하는 메서드 - * - HQ Admin: 본사 관리자가 사용하는 메서드 - * - Common: 공통 메서드 - */ @Service class ThemeService( - private val themeRepository: ThemeRepository, - private val themeValidator: ThemeValidator, - private val idGenerator: IDGenerator, - private val adminService: AdminService + private val themeRepository: ThemeRepository ) { - // ======================================== - // Public (인증 불필요) - // ======================================== @Transactional(readOnly = true) fun findInfoById(id: Long): ThemeInfoResponse { log.info { "[findInfoById] 테마 조회 시작: id=$id" } - return findOrThrow(id).toInfoResponse() + val theme = themeRepository.findByIdOrNull(id) ?: run { + log.warn { "[updateTheme] 테마 조회 실패: id=$id" } + throw ThemeException(ThemeErrorCode.THEME_NOT_FOUND) + } + + return theme.toInfoResponse() .also { log.info { "[findInfoById] 테마 조회 완료: id=$id" } } } @@ -54,115 +48,11 @@ class ThemeService( .also { log.info { "[findMostReservedThemeLastWeek] ${it.themes.size} / $count 개의 인기 테마 조회 완료" } } - - } - - // ======================================== - // HQ Admin (본사) - // ======================================== - @Transactional(readOnly = true) - fun findAdminThemes(): AdminThemeSummaryListResponse { - log.info { "[findAdminThemes] 관리자 페이지에서의 테마 목록 조회 시작" } - - return themeRepository.findAll() - .toAdminThemeSummaryListResponse() - .also { log.info { "[findAdminThemes] ${it.themes.size}개 테마 조회 완료" } } - } - - @Transactional(readOnly = true) - fun findAdminThemeDetail(id: Long): AdminThemeDetailResponse { - log.info { "[findAdminThemeDetail] 관리자 페이지에서의 테마 상세 정보 조회 시작: id=${id}" } - - val theme: ThemeEntity = findOrThrow(id) - - val createdBy = adminService.findOperatorOrUnknown(theme.createdBy) - val updatedBy = adminService.findOperatorOrUnknown(theme.updatedBy) - val audit = AuditingInfo(theme.createdAt, createdBy, theme.updatedAt, updatedBy) - - return theme.toAdminThemeDetailResponse(audit) - .also { log.info { "[findAdminThemeDetail] 테마 상세 조회 완료: id=$id, name=${theme.name}" } } - } - - @Transactional - fun createTheme(request: ThemeCreateRequest): ThemeCreateResponse { - log.info { "[createTheme] 테마 생성 시작: name=${request.name}" } - - themeValidator.validateCanCreate(request) - - val theme: ThemeEntity = request.toEntity(id = idGenerator.create()) - .also { themeRepository.save(it) } - - return ThemeCreateResponse(theme.id).also { - log.info { "[createTheme] 테마 생성 완료: id=${theme.id}, name=${theme.name}" } - } - } - - @Transactional - fun deleteTheme(id: Long) { - log.info { "[deleteTheme] 테마 삭제 시작: id=${id}" } - - val theme: ThemeEntity = findOrThrow(id) - - themeRepository.delete(theme).also { - log.info { "[deleteTheme] 테마 삭제 완료: id=$id, name=${theme.name}" } - } - } - - @Transactional - fun updateTheme(id: Long, request: ThemeUpdateRequest) { - log.info { "[updateTheme] 테마 수정 시작: id=${id}, request=${request}" } - - if (request.isAllParamsNull()) { - log.info { "[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.isActive, - ).also { - log.info { "[updateTheme] 테마 수정 완료: id=$id, request=${request}" } - } - } - - // ======================================== - // Store Admin (매장) - // ======================================== - @Transactional(readOnly = true) - fun findActiveThemes(): SimpleActiveThemeListResponse { - log.info { "[findActiveThemes] open 상태인 모든 테마 조회 시작" } - - return themeRepository.findActiveThemes() - .toSimpleActiveThemeResponse() - .also { - log.info { "[findActiveThemes] ${it.themes.size}개 테마 조회 완료" } - } - } - - // ======================================== - // Common (공통 메서드) - // ======================================== - private fun findOrThrow(id: Long): ThemeEntity { - log.info { "[findOrThrow] 테마 조회 시작: id=$id" } - - return themeRepository.findByIdOrNull(id) - ?.also { log.info { "[findOrThrow] 테마 조회 완료: id=$id" } } - ?: run { - log.warn { "[updateTheme] 테마 조회 실패: id=$id" } - throw ThemeException(ThemeErrorCode.THEME_NOT_FOUND) - } } } + +object DateUtils { + fun getSundayOfPreviousWeek(date: LocalDate): LocalDate = date + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)) +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt index e8a362f4..8be522f6 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt @@ -3,8 +3,8 @@ package com.sangdol.roomescape.theme.business import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.exception.ThemeException import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository -import com.sangdol.roomescape.theme.web.ThemeCreateRequest -import com.sangdol.roomescape.theme.web.ThemeUpdateRequest +import com.sangdol.roomescape.theme.dto.ThemeCreateRequest +import com.sangdol.roomescape.theme.dto.ThemeUpdateRequest import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt index 14acfd0c..40abab51 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt @@ -5,7 +5,14 @@ import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege import com.sangdol.roomescape.auth.web.support.AdminOnly import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.theme.web.* +import com.sangdol.roomescape.theme.dto.ThemeDetailResponse +import com.sangdol.roomescape.theme.dto.ThemeSummaryListResponse +import com.sangdol.roomescape.theme.dto.ThemeNameListResponse +import com.sangdol.roomescape.theme.dto.ThemeCreateRequest +import com.sangdol.roomescape.theme.dto.ThemeCreateResponse +import com.sangdol.roomescape.theme.dto.ThemeInfoListResponse +import com.sangdol.roomescape.theme.dto.ThemeInfoResponse +import com.sangdol.roomescape.theme.dto.ThemeUpdateRequest import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -20,12 +27,12 @@ interface AdminThemeAPI { @AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_SUMMARY) @Operation(summary = "모든 테마 조회") @ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true)) - fun getAdminThemeSummaries(): ResponseEntity> + fun getAdminThemeSummaries(): ResponseEntity> @AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_DETAIL) @Operation(summary = "테마 상세 조회") @ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true)) - fun findAdminThemeDetail(@PathVariable("id") id: Long): ResponseEntity> + fun findAdminThemeDetail(@PathVariable("id") id: Long): ResponseEntity> @AdminOnly(type = AdminType.HQ, privilege = Privilege.CREATE) @Operation(summary = "테마 추가") @@ -48,7 +55,7 @@ interface AdminThemeAPI { @AdminOnly(privilege = Privilege.READ_SUMMARY) @Operation(summary = "현재 활성화 상태인 테마 ID + 이름 목록 조회") @ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true)) - fun getActiveThemes(): ResponseEntity> + fun getActiveThemes(): ResponseEntity> } interface PublicThemeAPI { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/AdminThemeFindDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/AdminThemeFindDTO.kt new file mode 100644 index 00000000..641ee5ba --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/AdminThemeFindDTO.kt @@ -0,0 +1,31 @@ +package com.sangdol.roomescape.theme.dto + +import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty + +data class ThemeSummaryResponse( + val id: Long, + val name: String, + val difficulty: Difficulty, + val price: Int, + val isActive: Boolean +) + +data class ThemeSummaryListResponse( + val themes: List +) + +data class ThemeDetailResponse( + val theme: ThemeInfoResponse, + val isActive: Boolean, + val audit: AuditingInfo +) + +data class ThemeNameResponse( + val id: Long, + val name: String +) + +data class ThemeNameListResponse( + val themes: List +) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/AdminThemeWriteDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/AdminThemeWriteDTO.kt new file mode 100644 index 00000000..8adc3c31 --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/AdminThemeWriteDTO.kt @@ -0,0 +1,49 @@ +package com.sangdol.roomescape.theme.dto + +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty + +data class ThemeCreateRequest( + val name: String, + val description: String, + val thumbnailUrl: String, + val difficulty: Difficulty, + val price: Int, + val minParticipants: Short, + val maxParticipants: Short, + val availableMinutes: Short, + val expectedMinutesFrom: Short, + val expectedMinutesTo: Short, + val isActive: Boolean +) + +data class ThemeCreateResponse( + val id: Long +) + +data class ThemeUpdateRequest( + val name: String? = null, + val description: String? = null, + val thumbnailUrl: String? = null, + val difficulty: Difficulty? = null, + val price: Int? = null, + val minParticipants: Short? = null, + val maxParticipants: Short? = null, + val availableMinutes: Short? = null, + val expectedMinutesFrom: Short? = null, + val expectedMinutesTo: Short? = null, + val isActive: Boolean? = null, +) { + fun isAllParamsNull(): Boolean { + return name == null && + description == null && + thumbnailUrl == null && + difficulty == null && + price == null && + minParticipants == null && + maxParticipants == null && + availableMinutes == null && + expectedMinutesFrom == null && + expectedMinutesTo == null && + isActive == null + } +} \ No newline at end of file diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/UserThemeFindDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/UserThemeFindDTO.kt new file mode 100644 index 00000000..40a36ae1 --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/dto/UserThemeFindDTO.kt @@ -0,0 +1,19 @@ +package com.sangdol.roomescape.theme.dto + +data class ThemeInfoResponse( + val id: Long, + val name: String, + val thumbnailUrl: String, + val description: String, + val difficulty: String, + val price: Int, + val minParticipants: Short, + val maxParticipants: Short, + val availableMinutes: Short, + val expectedMinutesFrom: Short, + val expectedMinutesTo: Short +) + +data class ThemeInfoListResponse( + val themes: List +) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/mapper/AdminThemeMappingExtensions.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/mapper/AdminThemeMappingExtensions.kt new file mode 100644 index 00000000..dfe91ccc --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/mapper/AdminThemeMappingExtensions.kt @@ -0,0 +1,48 @@ +package com.sangdol.roomescape.theme.mapper + +import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.theme.dto.* +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity + +fun ThemeCreateRequest.toEntity(id: Long) = ThemeEntity( + id = id, + name = this.name, + description = this.description, + thumbnailUrl = this.thumbnailUrl, + difficulty = this.difficulty, + price = this.price, + minParticipants = this.minParticipants, + maxParticipants = this.maxParticipants, + availableMinutes = this.availableMinutes, + expectedMinutesFrom = this.expectedMinutesFrom, + expectedMinutesTo = this.expectedMinutesTo, + isActive = this.isActive +) + +fun ThemeEntity.toSummaryResponse() = ThemeSummaryResponse( + id = this.id, + name = this.name, + difficulty = this.difficulty, + price = this.price, + isActive = this.isActive +) + +fun ThemeEntity.toDetailResponse(audit: AuditingInfo) = + ThemeDetailResponse( + theme = this.toInfoResponse(), + isActive = this.isActive, + audit = audit + ) + +fun ThemeEntity.toNameResponse() = ThemeNameResponse( + id = this.id, + name = this.name +) + +fun List.toSummaryListResponse() = ThemeSummaryListResponse( + themes = this.map { it.toSummaryResponse() } +) + +fun List.toNameListResponse() = ThemeNameListResponse( + themes = this.map { it.toNameResponse() } +) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/mapper/UserThemeMappingExtensions.kt similarity index 65% rename from service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/mapper/UserThemeMappingExtensions.kt index 46463d38..9c0a346b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/mapper/UserThemeMappingExtensions.kt @@ -1,23 +1,11 @@ -package com.sangdol.roomescape.theme.web +package com.sangdol.roomescape.theme.mapper import com.sangdol.roomescape.theme.business.domain.ThemeInfo +import com.sangdol.roomescape.theme.dto.ThemeInfoListResponse +import com.sangdol.roomescape.theme.dto.ThemeInfoResponse import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity -data class ThemeInfoResponse( - val id: Long, - val name: String, - val thumbnailUrl: String, - val description: String, - val difficulty: String, - val price: Int, - val minParticipants: Short, - val maxParticipants: Short, - val availableMinutes: Short, - val expectedMinutesFrom: Short, - val expectedMinutesTo: Short -) - -fun ThemeInfo.toInfoResponse() = ThemeInfoResponse( +fun ThemeInfo.toResponse() = ThemeInfoResponse( id = this.id, name = this.name, thumbnailUrl = this.thumbnailUrl, @@ -45,10 +33,8 @@ fun ThemeEntity.toInfoResponse() = ThemeInfoResponse( expectedMinutesTo = this.expectedMinutesTo ) -data class ThemeInfoListResponse( - val themes: List +fun List.toListResponse() = ThemeInfoListResponse( + themes = this.map { it.toResponse() } ) -fun List.toListResponse() = ThemeInfoListResponse( - themes = this.map { it.toInfoResponse() } -) + diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt index 3a5f6375..e0d86291 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt @@ -1,8 +1,9 @@ package com.sangdol.roomescape.theme.web import com.sangdol.common.types.web.CommonApiResponse -import com.sangdol.roomescape.theme.business.ThemeService +import com.sangdol.roomescape.theme.business.AdminThemeService import com.sangdol.roomescape.theme.docs.AdminThemeAPI +import com.sangdol.roomescape.theme.dto.* import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* @@ -10,19 +11,19 @@ import java.net.URI @RestController class AdminThemeController( - private val themeService: ThemeService, + private val adminThemeService: AdminThemeService, ) : AdminThemeAPI { @GetMapping("/admin/themes") - override fun getAdminThemeSummaries(): ResponseEntity> { - val response = themeService.findAdminThemes() + override fun getAdminThemeSummaries(): ResponseEntity> { + val response = adminThemeService.findThemeSummaries() return ResponseEntity.ok(CommonApiResponse(response)) } @GetMapping("/admin/themes/{id}") - override fun findAdminThemeDetail(@PathVariable id: Long): ResponseEntity> { - val response = themeService.findAdminThemeDetail(id) + override fun findAdminThemeDetail(@PathVariable id: Long): ResponseEntity> { + val response = adminThemeService.findThemeDetail(id) return ResponseEntity.ok(CommonApiResponse(response)) } @@ -31,7 +32,7 @@ class AdminThemeController( override fun createTheme( @Valid @RequestBody themeCreateRequest: ThemeCreateRequest ): ResponseEntity> { - val response = themeService.createTheme(themeCreateRequest) + val response = adminThemeService.createTheme(themeCreateRequest) return ResponseEntity.created(URI.create("/admin/themes/${response.id}")) .body(CommonApiResponse(response)) @@ -39,7 +40,7 @@ class AdminThemeController( @DeleteMapping("/admin/themes/{id}") override fun deleteTheme(@PathVariable id: Long): ResponseEntity> { - themeService.deleteTheme(id) + adminThemeService.deleteTheme(id) return ResponseEntity.noContent().build() } @@ -49,14 +50,14 @@ class AdminThemeController( @PathVariable id: Long, @Valid @RequestBody request: ThemeUpdateRequest ): ResponseEntity> { - themeService.updateTheme(id, request) + adminThemeService.updateTheme(id, request) return ResponseEntity.ok().build() } @GetMapping("/admin/themes/active") - override fun getActiveThemes(): ResponseEntity> { - val response = themeService.findActiveThemes() + override fun getActiveThemes(): ResponseEntity> { + val response = adminThemeService.findActiveThemes() return ResponseEntity.ok(CommonApiResponse(response)) } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt deleted file mode 100644 index 94c5a195..00000000 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.sangdol.roomescape.theme.web - -import com.sangdol.roomescape.common.types.AuditingInfo -import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty -import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity - -// ======================================== -// HQ Admin DTO (본사) -// ======================================== -data class ThemeCreateRequest( - val name: String, - val description: String, - val thumbnailUrl: String, - val difficulty: Difficulty, - val price: Int, - val minParticipants: Short, - val maxParticipants: Short, - val availableMinutes: Short, - val expectedMinutesFrom: Short, - val expectedMinutesTo: Short, - val isActive: Boolean -) - -data class ThemeCreateResponse( - val id: Long -) - -fun ThemeCreateRequest.toEntity(id: Long) = ThemeEntity( - id = id, - name = this.name, - description = this.description, - thumbnailUrl = this.thumbnailUrl, - difficulty = this.difficulty, - price = this.price, - minParticipants = this.minParticipants, - maxParticipants = this.maxParticipants, - availableMinutes = this.availableMinutes, - expectedMinutesFrom = this.expectedMinutesFrom, - expectedMinutesTo = this.expectedMinutesTo, - isActive = this.isActive -) - -data class ThemeUpdateRequest( - val name: String? = null, - val description: String? = null, - val thumbnailUrl: String? = null, - val difficulty: Difficulty? = null, - val price: Int? = null, - val minParticipants: Short? = null, - val maxParticipants: Short? = null, - val availableMinutes: Short? = null, - val expectedMinutesFrom: Short? = null, - val expectedMinutesTo: Short? = null, - val isActive: Boolean? = null, -) { - fun isAllParamsNull(): Boolean { - return name == null && - description == null && - thumbnailUrl == null && - difficulty == null && - price == null && - minParticipants == null && - maxParticipants == null && - availableMinutes == null && - expectedMinutesFrom == null && - expectedMinutesTo == null && - isActive == null - } -} - -data class AdminThemeSummaryResponse( - val id: Long, - val name: String, - val difficulty: Difficulty, - val price: Int, - val isActive: Boolean -) - -fun ThemeEntity.toAdminThemeSummaryResponse() = AdminThemeSummaryResponse( - id = this.id, - name = this.name, - difficulty = this.difficulty, - price = this.price, - isActive = this.isActive -) - -data class AdminThemeSummaryListResponse( - val themes: List -) - -fun List.toAdminThemeSummaryListResponse() = AdminThemeSummaryListResponse( - themes = this.map { it.toAdminThemeSummaryResponse() } -) - -data class AdminThemeDetailResponse( - val theme: ThemeInfoResponse, - val isActive: Boolean, - val audit: AuditingInfo -) - -fun ThemeEntity.toAdminThemeDetailResponse(audit: AuditingInfo) = - AdminThemeDetailResponse( - theme = this.toInfoResponse(), - isActive = this.isActive, - audit = audit - ) - -// ======================================== -// Store Admin DTO -// ======================================== -data class SimpleActiveThemeResponse( - val id: Long, - val name: String -) - -fun ThemeEntity.toSimpleActiveThemeResponse() = SimpleActiveThemeResponse( - id = this.id, - name = this.name -) - -data class SimpleActiveThemeListResponse( - val themes: List -) - -fun List.toSimpleActiveThemeResponse() = SimpleActiveThemeListResponse( - themes = this.map { it.toSimpleActiveThemeResponse() } -) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt index f68e341c..f15d3fc7 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt @@ -3,6 +3,8 @@ package com.sangdol.roomescape.theme.web import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.theme.business.ThemeService import com.sangdol.roomescape.theme.docs.PublicThemeAPI +import com.sangdol.roomescape.theme.dto.ThemeInfoListResponse +import com.sangdol.roomescape.theme.dto.ThemeInfoResponse import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt index d4422ae6..04119355 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt @@ -26,8 +26,8 @@ import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository -import com.sangdol.roomescape.theme.web.ThemeCreateRequest -import com.sangdol.roomescape.theme.web.toEntity +import com.sangdol.roomescape.theme.dto.ThemeCreateRequest +import com.sangdol.roomescape.theme.mapper.toEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import org.springframework.data.repository.findByIdOrNull import java.time.Instant diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt index 2d0db551..50145d77 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt @@ -19,7 +19,7 @@ import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus import com.sangdol.roomescape.store.web.StoreRegisterRequest import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity -import com.sangdol.roomescape.theme.web.ThemeCreateRequest +import com.sangdol.roomescape.theme.dto.ThemeCreateRequest import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus import com.sangdol.roomescape.user.dto.MIN_PASSWORD_LENGTH diff --git a/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt index f70cbecf..d65b60e6 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt @@ -12,7 +12,7 @@ import com.sangdol.roomescape.theme.business.MIN_PRICE import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository -import com.sangdol.roomescape.theme.web.ThemeUpdateRequest +import com.sangdol.roomescape.theme.dto.ThemeUpdateRequest import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import org.hamcrest.CoreMatchers.equalTo diff --git a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt index b99987d4..30e51c65 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt @@ -7,8 +7,8 @@ import com.sangdol.roomescape.theme.business.DateUtils import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository -import com.sangdol.roomescape.theme.web.ThemeInfoResponse -import com.sangdol.roomescape.theme.web.toEntity +import com.sangdol.roomescape.theme.dto.ThemeInfoResponse +import com.sangdol.roomescape.theme.mapper.toEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldHaveSize