diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt index aa654dd..14c1f25 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.admin.content.banner import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.audioContentBanner +import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series import kr.co.vividnext.sodalive.event.QEvent.event import kr.co.vividnext.sodalive.member.QMember.member import org.springframework.beans.factory.annotation.Value @@ -32,6 +33,8 @@ class AdminContentBannerQueryRepositoryImpl( audioContentBanner.event.thumbnailImage, audioContentBanner.creator.id, audioContentBanner.creator.nickname, + audioContentBanner.series.id, + audioContentBanner.series.title, audioContentBanner.link, audioContentBanner.isAdult ) @@ -39,6 +42,7 @@ class AdminContentBannerQueryRepositoryImpl( .from(audioContentBanner) .leftJoin(audioContentBanner.event, event) .leftJoin(audioContentBanner.creator, member) + .leftJoin(audioContentBanner.series, series) .where(audioContentBanner.isActive.isTrue) .orderBy(audioContentBanner.orders.asc()) .fetch() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt index b7e4837..4fdddae 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.content.banner import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner @@ -19,6 +20,7 @@ class AdminContentBannerService( private val s3Uploader: S3Uploader, private val repository: AdminContentBannerRepository, private val memberRepository: MemberRepository, + private val seriesRepository: AdminContentSeriesRepository, private val eventRepository: EventRepository, private val objectMapper: ObjectMapper, @@ -32,6 +34,10 @@ class AdminContentBannerService( throw SodaException("크리에이터를 선택하세요.") } + if (request.type == AudioContentBannerType.SERIES && request.seriesId == null) { + throw SodaException("시리즈를 선택하세요.") + } + if (request.type == AudioContentBannerType.LINK && request.link == null) { throw SodaException("링크 url을 입력하세요.") } @@ -52,11 +58,18 @@ class AdminContentBannerService( null } + val series = if (request.seriesId != null && request.seriesId > 0) { + seriesRepository.findByIdOrNull(request.seriesId) + } else { + null + } + val audioContentBanner = AudioContentBanner(type = request.type) audioContentBanner.link = request.link audioContentBanner.isAdult = request.isAdult audioContentBanner.event = event audioContentBanner.creator = creator + audioContentBanner.series = series repository.save(audioContentBanner) val fileName = generateFileName() @@ -96,30 +109,48 @@ class AdminContentBannerService( audioContentBanner.creator = null audioContentBanner.event = null audioContentBanner.link = null + audioContentBanner.series = null - if (request.type == AudioContentBannerType.CREATOR) { - if (request.creatorId != null) { - val creator = memberRepository.findByIdOrNull(request.creatorId) - ?: throw SodaException("크리에이터를 선택하세요.") + when (request.type) { + AudioContentBannerType.EVENT -> { + if (request.eventId != null) { + val event = eventRepository.findByIdOrNull(request.eventId) + ?: throw SodaException("이벤트를 선택하세요.") - audioContentBanner.creator = creator - } else { - throw SodaException("크리에이터를 선택하세요.") + audioContentBanner.event = event + } else { + throw SodaException("이벤트를 선택하세요.") + } } - } else if (request.type == AudioContentBannerType.LINK) { - if (request.link != null) { - audioContentBanner.link = request.link - } else { - throw SodaException("링크 url을 입력하세요.") - } - } else if (request.type == AudioContentBannerType.EVENT) { - if (request.eventId != null) { - val event = eventRepository.findByIdOrNull(request.eventId) - ?: throw SodaException("이벤트를 선택하세요.") - audioContentBanner.event = event - } else { - throw SodaException("이벤트를 선택하세요.") + AudioContentBannerType.CREATOR -> { + if (request.creatorId != null) { + val creator = memberRepository.findByIdOrNull(request.creatorId) + ?: throw SodaException("크리에이터를 선택하세요.") + + audioContentBanner.creator = creator + } else { + throw SodaException("크리에이터를 선택하세요.") + } + } + + AudioContentBannerType.LINK -> { + if (request.link != null) { + audioContentBanner.link = request.link + } else { + throw SodaException("링크 url을 입력하세요.") + } + } + + AudioContentBannerType.SERIES -> { + if (request.seriesId != null) { + val series = seriesRepository.findByIdOrNull(request.seriesId) + ?: throw SodaException("시리즈를 선택하세요.") + + audioContentBanner.series = series + } else { + throw SodaException("시리즈를 선택하세요.") + } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt index 3203366..ca9cb5e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt @@ -6,6 +6,7 @@ data class CreateContentBannerRequest( val type: AudioContentBannerType, val eventId: Long?, val creatorId: Long?, + val seriesId: Long?, val link: String?, val isAdult: Boolean ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt index 6618f2b..bfd21d1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt @@ -11,6 +11,8 @@ data class GetAdminContentBannerResponse @QueryProjection constructor( val eventThumbnailImage: String?, val creatorId: Long?, val creatorNickname: String?, + val seriesId: Long?, + val seriesTitle: String?, val link: String?, val isAdult: Boolean ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt index fe97928..fcbad72 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt @@ -7,6 +7,7 @@ data class UpdateContentBannerRequest( val type: AudioContentBannerType?, val eventId: Long?, val creatorId: Long?, + val seriesId: Long?, val link: String?, val isAdult: Boolean?, val isActive: Boolean? diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesController.kt index 99987e2..bc2867c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesController.kt @@ -5,6 +5,7 @@ import org.springframework.data.domain.Pageable import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @@ -13,4 +14,9 @@ import org.springframework.web.bind.annotation.RestController class AdminContentSeriesController(private val service: AdminContentSeriesService) { @GetMapping fun getSeriesList(pageable: Pageable) = ApiResponse.ok(service.getSeriesList(pageable)) + + @GetMapping("/search") + fun searchSeriesList( + @RequestParam(value = "search_word") searchWord: String + ) = ApiResponse.ok(service.searchSeriesList(searchWord)) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt index 0bd1833..c251e5b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt @@ -20,6 +20,8 @@ interface AdminContentSeriesQueryRepository { offset: Long, limit: Long ): List + + fun searchSeriesList(searchWord: String): List } class AdminContentSeriesQueryRepositoryImpl( @@ -31,6 +33,7 @@ class AdminContentSeriesQueryRepositoryImpl( override fun getSeriesTotalCount(): Int { val where = series.isActive.isTrue .and(series.member.isNotNull) + .and(series.member.isActive.isTrue) return queryFactory .select(series.id) @@ -43,6 +46,7 @@ class AdminContentSeriesQueryRepositoryImpl( override fun getSeriesList(offset: Long, limit: Long): List { val where = series.isActive.isTrue .and(series.member.isNotNull) + .and(series.member.isActive.isTrue) return queryFactory .select( @@ -73,4 +77,24 @@ class AdminContentSeriesQueryRepositoryImpl( .orderBy(series.member.id.asc(), series.orders.asc()) .fetch() } + + override fun searchSeriesList(searchWord: String): List { + val where = series.isActive.isTrue + .and(series.title.contains(searchWord)) + .and(series.member.isNotNull) + .and(series.member.isActive.isTrue) + + return queryFactory + .select( + QGetAdminSearchSeriesListItem( + series.id, + series.title + ) + ) + .from(series) + .innerJoin(series.member, member) + .where(where) + .orderBy(series.id.desc()) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt index 6cb2df0..1a949d2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt @@ -14,4 +14,8 @@ class AdminContentSeriesService(private val repository: AdminContentSeriesReposi return GetAdminSeriesListResponse(totalCount, items) } + + fun searchSeriesList(searchWord: String): List { + return repository.searchSeriesList(searchWord) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/GetAdminSeriesListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/GetAdminSeriesListResponse.kt index 3682943..ec03b71 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/GetAdminSeriesListResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/GetAdminSeriesListResponse.kt @@ -18,3 +18,8 @@ data class GetAdminSeriesListItem @QueryProjection constructor( val state: String, val isAdult: Boolean ) + +data class GetAdminSearchSeriesListItem @QueryProjection constructor( + val id: Long, + val title: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerController.kt new file mode 100644 index 0000000..5c45ef2 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerController.kt @@ -0,0 +1,57 @@ +package kr.co.vividnext.sodalive.admin.event.banner + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile + +@RestController +@RequestMapping("/admin/event/banner") +@PreAuthorize("hasRole('ADMIN')") +class AdminEventBannerController(private val service: AdminEventBannerService) { + @PostMapping + fun createEventBanner( + @RequestParam("thumbnail") thumbnail: MultipartFile, + @RequestParam(value = "detail", required = false) detail: MultipartFile? = null, + @RequestParam(value = "popup", required = false) popup: MultipartFile? = null, + @RequestParam(value = "link", required = false) link: String? = null, + @RequestParam(value = "title", required = false) title: String? = null, + @RequestParam(value = "isAdult", required = false) isAdult: Boolean? = null, + @RequestParam(value = "isPopup") isPopup: Boolean, + @RequestParam(value = "startDate") startDate: String, + @RequestParam(value = "endDate") endDate: String + ) = ApiResponse.ok( + service.save(thumbnail, detail, popup, link, title, isAdult, isPopup, startDate, endDate), + "등록되었습니다." + ) + + @PutMapping + fun updateEvent( + @RequestParam(value = "id") id: Long, + @RequestParam(value = "thumbnail", required = false) thumbnail: MultipartFile? = null, + @RequestParam(value = "detail", required = false) detail: MultipartFile? = null, + @RequestParam(value = "popup", required = false) popup: MultipartFile? = null, + @RequestParam(value = "link", required = false) link: String? = null, + @RequestParam(value = "title", required = false) title: String? = null, + @RequestParam(value = "isAdult", required = false) isAdult: Boolean? = null, + @RequestParam(value = "isPopup", required = false) isPopup: Boolean? = null, + @RequestParam(value = "startDate", required = false) startDate: String? = null, + @RequestParam(value = "endDate", required = false) endDate: String? = null + ) = ApiResponse.ok( + service.update(id, thumbnail, detail, popup, link, title, isAdult, isPopup, startDate, endDate), + "수정되었습니다." + ) + + @DeleteMapping("/{id}") + fun deleteEvent(@PathVariable id: Long) = ApiResponse.ok(service.delete(id), "삭제되었습니다.") + + @GetMapping + fun getEventList() = ApiResponse.ok(service.getEventList()) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerRepository.kt new file mode 100644 index 0000000..547715b --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerRepository.kt @@ -0,0 +1,61 @@ +package kr.co.vividnext.sodalive.admin.event.banner + +import com.querydsl.core.types.dsl.DateTimePath +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.core.types.dsl.StringTemplate +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.event.Event +import kr.co.vividnext.sodalive.event.QEvent.event +import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDateTime + +interface AdminEventBannerRepository : JpaRepository, AdminEventBannerQueryRepository + +interface AdminEventBannerQueryRepository { + fun getEventList(): List +} + +class AdminEventBannerQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : AdminEventBannerQueryRepository { + override fun getEventList(): List { + val now = LocalDateTime.now() + val where = event.isActive.isTrue + .and(event.startDate.loe(now)) + .and(event.endDate.goe(now)) + + return queryFactory + .select( + QGetAdminEventResponse( + event.id, + event.title, + event.thumbnailImage, + event.detailImage, + event.popupImage, + getFormattedDate(event.startDate), + getFormattedDate(event.endDate), + event.link, + event.isAdult, + event.isPopup + ) + ) + .from(event) + .where(where) + .orderBy(event.id.desc()) + .fetch() + } + + private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { + return Expressions.stringTemplate( + "DATE_FORMAT({0}, {1})", + Expressions.dateTimeTemplate( + LocalDateTime::class.java, + "CONVERT_TZ({0},{1},{2})", + dateTimePath, + "UTC", + "Asia/Seoul" + ), + "%Y-%m-%d" + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerService.kt new file mode 100644 index 0000000..158dc5e --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/AdminEventBannerService.kt @@ -0,0 +1,222 @@ +package kr.co.vividnext.sodalive.admin.event.banner + +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.event.Event +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 AdminEventBannerService( + private val repository: AdminEventBannerRepository, + private val s3Uploader: S3Uploader, + + @Value("\${cloud.aws.s3.bucket}") + private val bucket: String, + @Value("\${cloud.aws.cloud-front.host}") + private val cloudFrontHost: String +) { + @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("상세이미지 혹은 링크를 등록하세요") + + 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("이벤트 등록을 하지 못했습니다.") + } + + @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("잘못된 요청입니다.") + + val event = repository.findByIdOrNull(id) + ?: throw SodaException("잘못된 요청입니다.") + + 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 (!link.isNullOrBlank() && 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("잘못된 요청입니다.") + val event = repository.findByIdOrNull(id) + ?: throw SodaException("잘못된 요청입니다.") + + event.isActive = false + } + + fun getEventList(): List { + return repository.getEventList() + .asSequence() + .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 + } + .toList() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/GetAdminEventResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/GetAdminEventResponse.kt new file mode 100644 index 0000000..82e9e26 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/banner/GetAdminEventResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.admin.event.banner + +import com.querydsl.core.annotations.QueryProjection + +data class GetAdminEventResponse @QueryProjection constructor( + val id: Long, + val title: String? = null, + var thumbnailImageUrl: String, + var detailImageUrl: String? = null, + var popupImageUrl: String? = null, + var startDate: String, + var endDate: String, + val link: String? = null, + val isAdult: Boolean? = null, + val isPopup: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventController.kt similarity index 87% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventController.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventController.kt index 0502923..22452b7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventController.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge import kr.co.vividnext.sodalive.common.ApiResponse import org.springframework.security.access.prepost.PreAuthorize @@ -11,22 +11,20 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/event/charge") +@PreAuthorize("hasRole('ADMIN')") class AdminChargeEventController(private val service: AdminChargeEventService) { @PostMapping - @PreAuthorize("hasRole('ADMIN')") fun createChargeEvent(@RequestBody request: CreateChargeEventRequest): ApiResponse { service.createChargeEvent(request) return ApiResponse.ok(null, "등록되었습니다.") } @PutMapping - @PreAuthorize("hasRole('ADMIN')") fun modifyChargeEvent(@RequestBody request: ModifyChargeEventRequest) = ApiResponse.ok( service.modifyChargeEvent(request), "수정되었습니다." ) @GetMapping("/list") - @PreAuthorize("hasRole('ADMIN')") fun getChargeEventList() = ApiResponse.ok(service.getChargeEventList()) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventRepository.kt similarity index 84% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventRepository.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventRepository.kt index 4cd02e4..d8dfa4e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventRepository.kt @@ -1,7 +1,7 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge import com.querydsl.jpa.impl.JPAQueryFactory -import kr.co.vividnext.sodalive.admin.event.QChargeEvent.chargeEvent +import kr.co.vividnext.sodalive.admin.event.charge.QChargeEvent.chargeEvent import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventService.kt similarity index 98% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventService.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventService.kt index fac0957..27aad89 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/AdminChargeEventService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/AdminChargeEventService.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge import kr.co.vividnext.sodalive.common.SodaException import org.springframework.data.repository.findByIdOrNull diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/ChargeEvent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/ChargeEvent.kt similarity index 86% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/ChargeEvent.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/ChargeEvent.kt index 2d93162..3c20aed 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/ChargeEvent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/ChargeEvent.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge import kr.co.vividnext.sodalive.common.BaseEntity import java.time.LocalDateTime diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/CreateChargeEventRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/CreateChargeEventRequest.kt similarity index 77% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/CreateChargeEventRequest.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/CreateChargeEventRequest.kt index 7e81f4e..e105ef8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/CreateChargeEventRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/CreateChargeEventRequest.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge data class CreateChargeEventRequest( val title: String, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/GetChargeEventListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/GetChargeEventListResponse.kt similarity index 80% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/GetChargeEventListResponse.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/GetChargeEventListResponse.kt index fbac78b..ee6ae16 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/GetChargeEventListResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/GetChargeEventListResponse.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge data class GetChargeEventListResponse( val id: Long, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/ModifyChargeEventRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/ModifyChargeEventRequest.kt similarity index 84% rename from src/main/kotlin/kr/co/vividnext/sodalive/admin/event/ModifyChargeEventRequest.kt rename to src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/ModifyChargeEventRequest.kt index 1086ae2..6d491f0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/ModifyChargeEventRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/event/charge/ModifyChargeEventRequest.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.admin.event +package kr.co.vividnext.sodalive.admin.event.charge data class ModifyChargeEventRequest( val id: Long, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt index 88a047b..c7b84fb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt @@ -1,8 +1,8 @@ package kr.co.vividnext.sodalive.can.charge.event import com.querydsl.jpa.impl.JPAQueryFactory -import kr.co.vividnext.sodalive.admin.event.ChargeEvent -import kr.co.vividnext.sodalive.admin.event.QChargeEvent.chargeEvent +import kr.co.vividnext.sodalive.admin.event.charge.ChargeEvent +import kr.co.vividnext.sodalive.admin.event.charge.QChargeEvent.chargeEvent import kr.co.vividnext.sodalive.can.charge.QCharge.charge import kr.co.vividnext.sodalive.can.payment.PaymentStatus import kr.co.vividnext.sodalive.can.payment.QPayment.payment diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt index 598b8b2..3e32d6d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt @@ -14,8 +14,6 @@ import org.springframework.cache.annotation.Cacheable import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import java.time.ZoneId -import java.time.format.DateTimeFormatter @Service class AudioContentMainService( @@ -93,13 +91,14 @@ class AudioContentMainService( } @Transactional(readOnly = true) - @Cacheable(cacheNames = ["default"], key = "'contentMainBannerList:' + #memberId + ':' + #isAdult") fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) = repository.getAudioContentMainBannerList(isAdult = isAdult) .asSequence() .filter { if (it.type == AudioContentBannerType.CREATOR && it.creator != null) { !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creator!!.id!!) + } else if (it.type == AudioContentBannerType.SERIES && it.series != null) { + !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.series!!.member!!.id!!) } else { true } @@ -109,17 +108,6 @@ class AudioContentMainService( type = it.type, thumbnailImageUrl = "$imageHost/${it.thumbnailImage}", eventItem = if (it.type == AudioContentBannerType.EVENT && it.event != null) { - val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") - val startDate = it.event!!.startDate - .atZone(ZoneId.of("UTC")) - .withZoneSameInstant(ZoneId.of("Asia/Seoul")) - .format(dateTimeFormatter) - - val endDate = it.event!!.endDate - .atZone(ZoneId.of("UTC")) - .withZoneSameInstant(ZoneId.of("Asia/Seoul")) - .format(dateTimeFormatter) - EventItem( id = it.event!!.id!!, thumbnailImageUrl = if (!it.event!!.thumbnailImage.startsWith("https://")) { @@ -136,11 +124,7 @@ class AudioContentMainService( it.event!!.detailImage }, popupImageUrl = null, - startDate = startDate, - endDate = endDate, - link = it.event!!.link, - title = it.event!!.title, - isPopup = false + link = it.event!!.link ) } else { null @@ -150,6 +134,11 @@ class AudioContentMainService( } else { null }, + seriesId = if (it.type == AudioContentBannerType.SERIES && it.series != null) { + it.series!!.id + } else { + null + }, link = it.link ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt index 3caea5f..c79f17e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt @@ -1,6 +1,8 @@ package kr.co.vividnext.sodalive.content.main.banner import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTab +import kr.co.vividnext.sodalive.creator.admin.content.series.Series import kr.co.vividnext.sodalive.event.Event import kr.co.vividnext.sodalive.member.Member import javax.persistence.Column @@ -34,10 +36,18 @@ data class AudioContentBanner( @JoinColumn(name = "creator_id", nullable = true) var creator: Member? = null + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "series_id", nullable = true) + var series: Series? = null + + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "tab_id", nullable = true) + var tab: AudioContentMainTab? = null + @Column(nullable = true) var link: String? = null } enum class AudioContentBannerType { - EVENT, CREATOR, LINK + EVENT, CREATOR, LINK, SERIES } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/GetAudioContentBannerResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/GetAudioContentBannerResponse.kt index 14210e9..6ba7ddb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/GetAudioContentBannerResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/GetAudioContentBannerResponse.kt @@ -8,5 +8,6 @@ data class GetAudioContentBannerResponse( @JsonProperty("thumbnailImageUrl") val thumbnailImageUrl: String, @JsonProperty("eventItem") val eventItem: EventItem?, @JsonProperty("creatorId") val creatorId: Long?, + @JsonProperty("seriesId") val seriesId: Long?, @JsonProperty("link") val link: String? ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/AudioContentMainTab.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/AudioContentMainTab.kt new file mode 100644 index 0000000..95fc3fa --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/AudioContentMainTab.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.content.main.tab + +import kr.co.vividnext.sodalive.common.BaseEntity +import javax.persistence.Entity + +@Entity +data class AudioContentMainTab( + val title: String, + val isActive: Boolean +) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt index 8f40b49..2a0df4b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt @@ -1,8 +1,5 @@ package kr.co.vividnext.sodalive.event -import com.querydsl.core.types.dsl.DateTimePath -import com.querydsl.core.types.dsl.Expressions -import com.querydsl.core.types.dsl.StringTemplate import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.event.QEvent.event import org.springframework.data.jpa.repository.JpaRepository @@ -43,15 +40,10 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even .select( QEventItem( event.id, - event.title, event.thumbnailImage, event.detailImage, event.popupImage, - getFormattedDate(event.startDate), - getFormattedDate(event.endDate), - event.link, - event.isAdult, - event.isPopup + event.link ) ) .from(event) @@ -84,15 +76,10 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even .select( QEventItem( event.id, - event.title, event.thumbnailImage, event.detailImage, event.popupImage, - getFormattedDate(event.startDate), - getFormattedDate(event.endDate), - event.link, - event.isAdult, - event.isPopup + event.link ) ) .from(event) @@ -100,18 +87,4 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even .orderBy(event.id.desc()) .fetchFirst() } - - private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { - return Expressions.stringTemplate( - "DATE_FORMAT({0}, {1})", - Expressions.dateTimeTemplate( - LocalDateTime::class.java, - "CONVERT_TZ({0},{1},{2})", - dateTimePath, - "UTC", - "Asia/Seoul" - ), - "%Y-%m-%d" - ) - } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt index 80c4ef8..318491c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt @@ -12,13 +12,8 @@ data class GetEventResponse( @JsonIgnoreProperties(ignoreUnknown = true) data class EventItem @QueryProjection constructor( @JsonProperty("id") val id: Long, - @JsonProperty("title") val title: String? = null, @JsonProperty("thumbnailImageUrl") var thumbnailImageUrl: String, @JsonProperty("detailImageUrl") var detailImageUrl: String? = null, @JsonProperty("popupImageUrl") var popupImageUrl: String? = null, - @JsonProperty("startDate") var startDate: String, - @JsonProperty("endDate") var endDate: String, - @JsonProperty("link") val link: String? = null, - @JsonProperty("isAdult") val isAdult: Boolean? = null, - @JsonProperty("isPopup") val isPopup: Boolean + @JsonProperty("link") val link: String? = null ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/notification/MemberNotificationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/notification/MemberNotificationService.kt index 5feee56..db023e8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/notification/MemberNotificationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/notification/MemberNotificationService.kt @@ -2,10 +2,8 @@ package kr.co.vividnext.sodalive.member.notification import kr.co.vividnext.sodalive.member.Member import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional(readOnly = true) class MemberNotificationService(private val repository: MemberNotificationRepository) { fun updateNotification( live: Boolean? = null,