package kr.co.vividnext.sodalive.event import com.amazonaws.services.s3.model.ObjectMetadata import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.auth.AuthRepository import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService import kr.co.vividnext.sodalive.utils.generateFileName import org.springframework.beans.factory.annotation.Value 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.LocalDate import java.time.ZoneId import java.time.format.DateTimeFormatter @Service class EventService( private val repository: EventRepository, private val s3Uploader: S3Uploader, private val authRepository: AuthRepository, private val memberContentPreferenceService: MemberContentPreferenceService, @Value("\${cloud.aws.s3.bucket}") private val bucket: String, @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { @Transactional(readOnly = true) fun getEventList(isAdult: Boolean? = null, lang: Lang? = null): GetEventResponse { val eventList = repository.getEventList(isAdult, lang) .map { if (!it.thumbnailImageUrl.startsWith("https://")) { it.thumbnailImageUrl = "$cloudFrontHost/${it.thumbnailImageUrl}" } if (it.detailImageUrl != null && !it.detailImageUrl!!.startsWith("https://")) { it.detailImageUrl = "$cloudFrontHost/${it.detailImageUrl}" } if (it.popupImageUrl != null && !it.popupImageUrl!!.startsWith("https://")) { it.popupImageUrl = "$cloudFrontHost/${it.popupImageUrl}" } it } return GetEventResponse(0, eventList) } @Transactional(readOnly = true) fun getEventList(member: Member?): GetEventResponse { val isAdult = if (member?.role == MemberRole.ADMIN) { null } else { member?.id?.let { authRepository.getAuthIdByMemberId(it) != null } ?: false } return getEventList(isAdult = isAdult, lang = resolveEventLang(member)) } @Transactional(readOnly = true) fun getEventPopup(isAdult: Boolean, lang: Lang? = null): EventItem? { val eventPopup = repository.getMainEventPopup(isAdult, lang) if (eventPopup != null) { if (!eventPopup.thumbnailImageUrl.startsWith("https://")) { eventPopup.thumbnailImageUrl = "$cloudFrontHost/${eventPopup.thumbnailImageUrl}" } if (eventPopup.detailImageUrl != null && !eventPopup.detailImageUrl!!.startsWith("https://")) { eventPopup.detailImageUrl = "$cloudFrontHost/${eventPopup.detailImageUrl}" } if (eventPopup.popupImageUrl != null && !eventPopup.popupImageUrl!!.startsWith("https://")) { eventPopup.popupImageUrl = "$cloudFrontHost/${eventPopup.popupImageUrl}" } } return eventPopup } @Transactional(readOnly = true) fun getEventPopup(member: Member?): EventItem? { val isAdult = member?.id?.let { authRepository.getAuthIdByMemberId(it) != null } ?: false return getEventPopup(isAdult = isAdult, lang = resolveEventLang(member)) } private fun resolveEventLang(member: Member?): Lang = if (memberContentPreferenceService.resolveCountryCode(member) == "JP") Lang.JA else Lang.KO @Transactional fun save( thumbnail: MultipartFile, detail: MultipartFile? = null, popup: MultipartFile? = null, link: String? = null, title: String? = null, isAdult: Boolean? = null, isPopup: Boolean, startDateString: String, endDateString: String ): Long { if (detail == null && link.isNullOrBlank()) { throw SodaException(messageKey = "event.detail_or_link_required") } val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val startDate = LocalDate.parse(startDateString, dateTimeFormatter).atTime(0, 0) .atZone(ZoneId.of("Asia/Seoul")) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() val endDate = LocalDate.parse(endDateString, dateTimeFormatter).atTime(23, 59, 59) .atZone(ZoneId.of("Asia/Seoul")) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() val event = repository.save( Event( thumbnailImage = "", detailImage = null, popupImage = null, link = link, title = title, isAdult = isAdult, isPopup = isPopup, startDate = startDate, endDate = endDate ) ) var metadata = ObjectMetadata() metadata.contentLength = thumbnail.size val thumbnailImagePath = s3Uploader.upload( inputStream = thumbnail.inputStream, bucket = bucket, filePath = "event/${event.id}/${generateFileName()}", metadata = metadata ) val detailImagePath = if (detail != null) { metadata = ObjectMetadata() metadata.contentLength = detail.size s3Uploader.upload( inputStream = detail.inputStream, bucket = bucket, filePath = "event/${event.id}/${generateFileName()}", metadata = metadata ) } else { null } val popupImagePath = if (popup != null) { metadata = ObjectMetadata() metadata.contentLength = popup.size s3Uploader.upload( inputStream = popup.inputStream, bucket = bucket, filePath = "event/${event.id}/${generateFileName()}", metadata = metadata ) } else { null } event.thumbnailImage = thumbnailImagePath event.detailImage = detailImagePath event.popupImage = popupImagePath return event.id ?: throw SodaException(messageKey = "event.save_failed") } @Transactional fun update( id: Long, thumbnail: MultipartFile? = null, detail: MultipartFile? = null, popup: MultipartFile? = null, link: String? = null, title: String? = null, isAdult: Boolean? = null, isPopup: Boolean? = null, startDateString: String? = null, endDateString: String? = null ) { if (id <= 0) throw SodaException(messageKey = "common.error.invalid_request") val event = repository.findByIdOrNull(id) ?: throw SodaException(messageKey = "common.error.invalid_request") if (thumbnail != null) { val metadata = ObjectMetadata() metadata.contentLength = thumbnail.size event.thumbnailImage = s3Uploader.upload( inputStream = thumbnail.inputStream, bucket = bucket, filePath = "event/${event.id}/${generateFileName()}" ) } if (detail != null) { val metadata = ObjectMetadata() metadata.contentLength = detail.size event.detailImage = s3Uploader.upload( inputStream = detail.inputStream, bucket = bucket, filePath = "event/${event.id}/${generateFileName()}" ) } if (popup != null) { val metadata = ObjectMetadata() metadata.contentLength = popup.size event.popupImage = s3Uploader.upload( inputStream = popup.inputStream, bucket = bucket, filePath = "event/${event.id}/${generateFileName()}" ) } if (event.link != link) { event.link = link } if (!title.isNullOrBlank() && event.title != title) { event.title = title } if (isPopup != null) { event.isPopup = isPopup } if (isAdult != event.isAdult) { event.isAdult = isAdult } val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") if (startDateString != null) { event.startDate = LocalDate.parse(startDateString, dateTimeFormatter).atTime(0, 0) .atZone(ZoneId.of("Asia/Seoul")) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() } if (endDateString != null) { event.endDate = LocalDate.parse(endDateString, dateTimeFormatter).atTime(23, 59, 59) .atZone(ZoneId.of("Asia/Seoul")) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() } } @Transactional fun delete(id: Long) { if (id <= 0) throw SodaException(messageKey = "common.error.invalid_request") val event = repository.findByIdOrNull(id) ?: throw SodaException(messageKey = "common.error.invalid_request") event.isActive = false } }