feat(banner): 관리자 예약 기간 관리와 입력 검증을 추가한다
This commit is contained in:
+3
-1
@@ -46,7 +46,9 @@ class AdminContentBannerQueryRepositoryImpl(
|
||||
audioContentBanner.series.id,
|
||||
audioContentBanner.series.title,
|
||||
audioContentBanner.link,
|
||||
audioContentBanner.isAdult
|
||||
audioContentBanner.isAdult,
|
||||
audioContentBanner.startDate,
|
||||
audioContentBanner.endDate
|
||||
)
|
||||
)
|
||||
.from(audioContentBanner)
|
||||
|
||||
+56
-2
@@ -1,5 +1,6 @@
|
||||
package kr.co.vividnext.sodalive.admin.content.banner
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository
|
||||
import kr.co.vividnext.sodalive.admin.content.tab.AdminContentMainTabRepository
|
||||
@@ -15,6 +16,11 @@ import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.DateTimeParseException
|
||||
import java.time.format.ResolverStyle
|
||||
|
||||
@Service
|
||||
class AdminContentBannerService(
|
||||
@@ -31,7 +37,12 @@ class AdminContentBannerService(
|
||||
) {
|
||||
@Transactional
|
||||
fun createAudioContentMainBanner(image: MultipartFile, requestString: String) {
|
||||
val request = objectMapper.readValue(requestString, CreateContentBannerRequest::class.java)
|
||||
val fields = objectMapper.readTree(requestString)
|
||||
validateReservationTypes(fields)
|
||||
val request = objectMapper.treeToValue(fields, CreateContentBannerRequest::class.java)
|
||||
val startDate = parseReservationDate(request.startDate)
|
||||
val endDate = parseReservationDate(request.endDate)?.withSecond(59)
|
||||
validateReservationRange(startDate, endDate)
|
||||
if (request.type == AudioContentBannerType.CREATOR && request.creatorId == null) {
|
||||
throw SodaException(messageKey = "admin.content.banner.creator_required")
|
||||
}
|
||||
@@ -79,6 +90,8 @@ class AdminContentBannerService(
|
||||
audioContentBanner.creator = creator
|
||||
audioContentBanner.series = series
|
||||
audioContentBanner.tab = tab
|
||||
audioContentBanner.startDate = startDate
|
||||
audioContentBanner.endDate = endDate
|
||||
repository.save(audioContentBanner)
|
||||
|
||||
val fileName = generateFileName()
|
||||
@@ -92,10 +105,20 @@ class AdminContentBannerService(
|
||||
|
||||
@Transactional
|
||||
fun updateAudioContentMainBanner(image: MultipartFile?, requestString: String) {
|
||||
val request = objectMapper.readValue(requestString, UpdateContentBannerRequest::class.java)
|
||||
val fields = objectMapper.readTree(requestString)
|
||||
validateReservationTypes(fields)
|
||||
val request = objectMapper.treeToValue(fields, UpdateContentBannerRequest::class.java)
|
||||
val audioContentBanner = repository.findByIdOrNull(request.id)
|
||||
?: throw SodaException(messageKey = "common.error.invalid_request")
|
||||
|
||||
val startDate = if (fields.has("startDate")) parseReservationDate(request.startDate) else audioContentBanner.startDate
|
||||
val endDate = if (fields.has("endDate")) {
|
||||
parseReservationDate(request.endDate)?.withSecond(59)
|
||||
} else {
|
||||
audioContentBanner.endDate
|
||||
}
|
||||
validateReservationRange(startDate, endDate)
|
||||
|
||||
if (image != null) {
|
||||
val fileName = generateFileName()
|
||||
val imagePath = s3Uploader.upload(
|
||||
@@ -106,6 +129,9 @@ class AdminContentBannerService(
|
||||
audioContentBanner.thumbnailImage = imagePath
|
||||
}
|
||||
|
||||
audioContentBanner.startDate = startDate
|
||||
audioContentBanner.endDate = endDate
|
||||
|
||||
if (request.isAdult != null) {
|
||||
audioContentBanner.isAdult = request.isAdult
|
||||
}
|
||||
@@ -185,4 +211,32 @@ class AdminContentBannerService(
|
||||
fun getAudioContentMainBannerList(tabId: Long?): List<GetAdminContentBannerResponse> {
|
||||
return repository.getAudioContentMainBannerList(tabId = tabId ?: 1)
|
||||
}
|
||||
|
||||
private fun validateReservationTypes(fields: JsonNode) {
|
||||
if (listOf(fields["startDate"], fields["endDate"]).any { it != null && !it.isNull && !it.isTextual }) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseReservationDate(value: String?): LocalDateTime? {
|
||||
if (value == null) return null
|
||||
if (value.length != 16) throw SodaException(messageKey = "common.error.invalid_request")
|
||||
return try {
|
||||
LocalDateTime.parse(
|
||||
value,
|
||||
DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm").withResolverStyle(ResolverStyle.STRICT)
|
||||
)
|
||||
.atZone(ZoneId.of("Asia/Seoul"))
|
||||
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||
.toLocalDateTime()
|
||||
} catch (_: DateTimeParseException) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateReservationRange(startDate: LocalDateTime?, endDate: LocalDateTime?) {
|
||||
if (startDate != null && endDate != null && startDate > endDate) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -11,5 +11,7 @@ data class CreateContentBannerRequest(
|
||||
val creatorId: Long?,
|
||||
val seriesId: Long?,
|
||||
val link: String?,
|
||||
val isAdult: Boolean
|
||||
val isAdult: Boolean,
|
||||
val startDate: String? = null,
|
||||
val endDate: String? = null
|
||||
)
|
||||
|
||||
+36
-3
@@ -2,8 +2,11 @@ package kr.co.vividnext.sodalive.admin.content.banner
|
||||
|
||||
import com.querydsl.core.annotations.QueryProjection
|
||||
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
data class GetAdminContentBannerResponse @QueryProjection constructor(
|
||||
data class GetAdminContentBannerResponse(
|
||||
val id: Long,
|
||||
val tabId: Long?,
|
||||
val type: AudioContentBannerType,
|
||||
@@ -15,5 +18,35 @@ data class GetAdminContentBannerResponse @QueryProjection constructor(
|
||||
val seriesId: Long?,
|
||||
val seriesTitle: String?,
|
||||
val link: String?,
|
||||
val isAdult: Boolean
|
||||
)
|
||||
val isAdult: Boolean,
|
||||
val startDate: String? = null,
|
||||
val endDate: String? = null
|
||||
) {
|
||||
@QueryProjection
|
||||
constructor(
|
||||
id: Long,
|
||||
tabId: Long?,
|
||||
type: AudioContentBannerType,
|
||||
thumbnailImageUrl: String,
|
||||
eventId: Long?,
|
||||
eventThumbnailImage: String?,
|
||||
creatorId: Long?,
|
||||
creatorNickname: String?,
|
||||
seriesId: Long?,
|
||||
seriesTitle: String?,
|
||||
link: String?,
|
||||
isAdult: Boolean,
|
||||
startDate: LocalDateTime?,
|
||||
endDate: LocalDateTime?
|
||||
) : this(
|
||||
id, tabId, type, thumbnailImageUrl, eventId, eventThumbnailImage, creatorId, creatorNickname,
|
||||
seriesId, seriesTitle, link, isAdult, formatDate(startDate), formatDate(endDate)
|
||||
)
|
||||
|
||||
companion object {
|
||||
private fun formatDate(value: LocalDateTime?): String? = value
|
||||
?.atZone(ZoneId.of("UTC"))
|
||||
?.withZoneSameInstant(ZoneId.of("Asia/Seoul"))
|
||||
?.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -11,5 +11,7 @@ data class UpdateContentBannerRequest(
|
||||
val seriesId: Long?,
|
||||
val link: String?,
|
||||
val isAdult: Boolean?,
|
||||
val isActive: Boolean?
|
||||
val isActive: Boolean?,
|
||||
val startDate: String? = null,
|
||||
val endDate: String? = null
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user