feat(notice): 예약 관리와 공개 시점 기반 조회를 구현한다
This commit is contained in:
@@ -2,32 +2,52 @@ package kr.co.vividnext.sodalive.notice
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.DateTimeParseException
|
||||
import java.time.format.ResolverStyle
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
class ServiceNoticeService(private val repository: ServiceServiceNoticeRepository) {
|
||||
class ServiceNoticeService(
|
||||
private val repository: ServiceServiceNoticeRepository,
|
||||
private val nowProvider: () -> LocalDateTime = { LocalDateTime.now(ZoneOffset.UTC) }
|
||||
) {
|
||||
@Transactional
|
||||
fun save(request: CreateNoticeRequest): Long {
|
||||
if (request.title.isBlank()) throw SodaException(messageKey = "notice.error.title_required")
|
||||
if (request.content.isBlank()) throw SodaException(messageKey = "notice.error.content_required")
|
||||
|
||||
val notice = request.toEntity()
|
||||
val publishAt = request.publishAt?.let { parsePublishAt(it, nowProvider()) }
|
||||
val notice = request.toEntity(publishAt)
|
||||
return repository.save(notice).id!!
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun update(request: UpdateNoticeRequest) {
|
||||
fun update(request: UpdateNoticeRequest, publishAtPresent: Boolean = false) {
|
||||
if (request.id <= 0) throw SodaException(messageKey = "common.error.invalid_request")
|
||||
if (request.title.isNullOrBlank() && request.content.isNullOrBlank()) {
|
||||
if (request.title.isNullOrBlank() && request.content.isNullOrBlank() && !publishAtPresent) {
|
||||
throw SodaException(messageKey = "notice.error.update_required")
|
||||
}
|
||||
|
||||
val notice = repository.findByIdOrNull(request.id)
|
||||
val notice = repository.findByIdForUpdate(request.id)
|
||||
?: throw SodaException(messageKey = "common.error.invalid_request")
|
||||
|
||||
if (publishAtPresent) {
|
||||
val now = nowProvider()
|
||||
val currentPublishAt = notice.publishAt
|
||||
if (!notice.isActive || !notice.isScheduled || currentPublishAt == null || currentPublishAt <= now) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
notice.publishAt = request.publishAt?.let { parsePublishAt(it, now) } ?: now.truncatedTo(ChronoUnit.SECONDS)
|
||||
notice.isScheduled = request.publishAt != null
|
||||
}
|
||||
|
||||
if (!request.title.isNullOrBlank()) notice.title = request.title
|
||||
if (!request.content.isNullOrBlank()) notice.content = request.content
|
||||
}
|
||||
@@ -35,20 +55,45 @@ class ServiceNoticeService(private val repository: ServiceServiceNoticeRepositor
|
||||
@Transactional
|
||||
fun delete(id: Long) {
|
||||
if (id <= 0) throw SodaException(messageKey = "common.error.invalid_request")
|
||||
val notice = repository.findByIdOrNull(id)
|
||||
val notice = repository.findByIdForUpdate(id)
|
||||
?: throw SodaException(messageKey = "common.error.invalid_request")
|
||||
|
||||
notice.isActive = false
|
||||
}
|
||||
|
||||
fun getNoticeList(pageable: Pageable, timezone: String): GetNoticeResponse {
|
||||
val totalCount = repository.getNoticeTotalCount()
|
||||
val noticeList = repository.getNoticeList(pageable)
|
||||
val now = nowProvider()
|
||||
val totalCount = repository.getNoticeTotalCount(now)
|
||||
val noticeList = repository.getNoticeList(pageable, now)
|
||||
|
||||
return GetNoticeResponse(totalCount, noticeList)
|
||||
}
|
||||
|
||||
fun getLatestNotice(): NoticeItem? {
|
||||
return repository.getLatestNotice()
|
||||
val now = nowProvider()
|
||||
return repository.getLatestNotice(now)
|
||||
}
|
||||
|
||||
private fun parsePublishAt(value: String, now: LocalDateTime): LocalDateTime {
|
||||
if (value.length != 19) throw SodaException(messageKey = "common.error.invalid_request")
|
||||
val utc = try {
|
||||
LocalDateTime.parse(value, PUBLISH_AT_FORMAT)
|
||||
.atZone(ZoneId.of("Asia/Seoul"))
|
||||
.withZoneSameInstant(ZoneOffset.UTC)
|
||||
.toLocalDateTime()
|
||||
} catch (_: DateTimeParseException) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
if (utc < MIN_TIMESTAMP || utc > MAX_TIMESTAMP || utc <= now) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
return utc
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val PUBLISH_AT_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")
|
||||
.withResolverStyle(ResolverStyle.STRICT)
|
||||
private val MIN_TIMESTAMP = LocalDateTime.of(1970, 1, 1, 0, 0, 1)
|
||||
private val MAX_TIMESTAMP = LocalDateTime.of(2038, 1, 19, 3, 14, 7)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user