| @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.admin.content.banner | |||||||
| import com.querydsl.jpa.impl.JPAQueryFactory | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.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.event.QEvent.event | ||||||
| import kr.co.vividnext.sodalive.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
| @@ -32,6 +33,8 @@ class AdminContentBannerQueryRepositoryImpl( | |||||||
|                     audioContentBanner.event.thumbnailImage, |                     audioContentBanner.event.thumbnailImage, | ||||||
|                     audioContentBanner.creator.id, |                     audioContentBanner.creator.id, | ||||||
|                     audioContentBanner.creator.nickname, |                     audioContentBanner.creator.nickname, | ||||||
|  |                     audioContentBanner.series.id, | ||||||
|  |                     audioContentBanner.series.title, | ||||||
|                     audioContentBanner.link, |                     audioContentBanner.link, | ||||||
|                     audioContentBanner.isAdult |                     audioContentBanner.isAdult | ||||||
|                 ) |                 ) | ||||||
| @@ -39,6 +42,7 @@ class AdminContentBannerQueryRepositoryImpl( | |||||||
|             .from(audioContentBanner) |             .from(audioContentBanner) | ||||||
|             .leftJoin(audioContentBanner.event, event) |             .leftJoin(audioContentBanner.event, event) | ||||||
|             .leftJoin(audioContentBanner.creator, member) |             .leftJoin(audioContentBanner.creator, member) | ||||||
|  |             .leftJoin(audioContentBanner.series, series) | ||||||
|             .where(audioContentBanner.isActive.isTrue) |             .where(audioContentBanner.isActive.isTrue) | ||||||
|             .orderBy(audioContentBanner.orders.asc()) |             .orderBy(audioContentBanner.orders.asc()) | ||||||
|             .fetch() |             .fetch() | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package kr.co.vividnext.sodalive.admin.content.banner | package kr.co.vividnext.sodalive.admin.content.banner | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper | 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.aws.s3.S3Uploader | ||||||
| import kr.co.vividnext.sodalive.common.SodaException | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner | ||||||
| @@ -19,6 +20,7 @@ class AdminContentBannerService( | |||||||
|     private val s3Uploader: S3Uploader, |     private val s3Uploader: S3Uploader, | ||||||
|     private val repository: AdminContentBannerRepository, |     private val repository: AdminContentBannerRepository, | ||||||
|     private val memberRepository: MemberRepository, |     private val memberRepository: MemberRepository, | ||||||
|  |     private val seriesRepository: AdminContentSeriesRepository, | ||||||
|     private val eventRepository: EventRepository, |     private val eventRepository: EventRepository, | ||||||
|     private val objectMapper: ObjectMapper, |     private val objectMapper: ObjectMapper, | ||||||
|  |  | ||||||
| @@ -32,6 +34,10 @@ class AdminContentBannerService( | |||||||
|             throw SodaException("크리에이터를 선택하세요.") |             throw SodaException("크리에이터를 선택하세요.") | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (request.type == AudioContentBannerType.SERIES && request.seriesId == null) { | ||||||
|  |             throw SodaException("시리즈를 선택하세요.") | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (request.type == AudioContentBannerType.LINK && request.link == null) { |         if (request.type == AudioContentBannerType.LINK && request.link == null) { | ||||||
|             throw SodaException("링크 url을 입력하세요.") |             throw SodaException("링크 url을 입력하세요.") | ||||||
|         } |         } | ||||||
| @@ -52,11 +58,18 @@ class AdminContentBannerService( | |||||||
|             null |             null | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         val series = if (request.seriesId != null && request.seriesId > 0) { | ||||||
|  |             seriesRepository.findByIdOrNull(request.seriesId) | ||||||
|  |         } else { | ||||||
|  |             null | ||||||
|  |         } | ||||||
|  |  | ||||||
|         val audioContentBanner = AudioContentBanner(type = request.type) |         val audioContentBanner = AudioContentBanner(type = request.type) | ||||||
|         audioContentBanner.link = request.link |         audioContentBanner.link = request.link | ||||||
|         audioContentBanner.isAdult = request.isAdult |         audioContentBanner.isAdult = request.isAdult | ||||||
|         audioContentBanner.event = event |         audioContentBanner.event = event | ||||||
|         audioContentBanner.creator = creator |         audioContentBanner.creator = creator | ||||||
|  |         audioContentBanner.series = series | ||||||
|         repository.save(audioContentBanner) |         repository.save(audioContentBanner) | ||||||
|  |  | ||||||
|         val fileName = generateFileName() |         val fileName = generateFileName() | ||||||
| @@ -96,30 +109,48 @@ class AdminContentBannerService( | |||||||
|             audioContentBanner.creator = null |             audioContentBanner.creator = null | ||||||
|             audioContentBanner.event = null |             audioContentBanner.event = null | ||||||
|             audioContentBanner.link = null |             audioContentBanner.link = null | ||||||
|  |             audioContentBanner.series = null | ||||||
|  |  | ||||||
|             if (request.type == AudioContentBannerType.CREATOR) { |             when (request.type) { | ||||||
|                 if (request.creatorId != null) { |                 AudioContentBannerType.EVENT -> { | ||||||
|                     val creator = memberRepository.findByIdOrNull(request.creatorId) |                     if (request.eventId != null) { | ||||||
|                         ?: throw SodaException("크리에이터를 선택하세요.") |                         val event = eventRepository.findByIdOrNull(request.eventId) | ||||||
|  |                             ?: throw SodaException("이벤트를 선택하세요.") | ||||||
|  |  | ||||||
|                     audioContentBanner.creator = creator |                         audioContentBanner.event = event | ||||||
|                 } else { |                     } else { | ||||||
|                     throw SodaException("크리에이터를 선택하세요.") |                         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 |                 AudioContentBannerType.CREATOR -> { | ||||||
|                 } else { |                     if (request.creatorId != null) { | ||||||
|                     throw SodaException("이벤트를 선택하세요.") |                         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("시리즈를 선택하세요.") | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ data class CreateContentBannerRequest( | |||||||
|     val type: AudioContentBannerType, |     val type: AudioContentBannerType, | ||||||
|     val eventId: Long?, |     val eventId: Long?, | ||||||
|     val creatorId: Long?, |     val creatorId: Long?, | ||||||
|  |     val seriesId: Long?, | ||||||
|     val link: String?, |     val link: String?, | ||||||
|     val isAdult: Boolean |     val isAdult: Boolean | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -11,6 +11,8 @@ data class GetAdminContentBannerResponse @QueryProjection constructor( | |||||||
|     val eventThumbnailImage: String?, |     val eventThumbnailImage: String?, | ||||||
|     val creatorId: Long?, |     val creatorId: Long?, | ||||||
|     val creatorNickname: String?, |     val creatorNickname: String?, | ||||||
|  |     val seriesId: Long?, | ||||||
|  |     val seriesTitle: String?, | ||||||
|     val link: String?, |     val link: String?, | ||||||
|     val isAdult: Boolean |     val isAdult: Boolean | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ data class UpdateContentBannerRequest( | |||||||
|     val type: AudioContentBannerType?, |     val type: AudioContentBannerType?, | ||||||
|     val eventId: Long?, |     val eventId: Long?, | ||||||
|     val creatorId: Long?, |     val creatorId: Long?, | ||||||
|  |     val seriesId: Long?, | ||||||
|     val link: String?, |     val link: String?, | ||||||
|     val isAdult: Boolean?, |     val isAdult: Boolean?, | ||||||
|     val isActive: Boolean? |     val isActive: Boolean? | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import org.springframework.data.domain.Pageable | |||||||
| import org.springframework.security.access.prepost.PreAuthorize | import org.springframework.security.access.prepost.PreAuthorize | ||||||
| import org.springframework.web.bind.annotation.GetMapping | import org.springframework.web.bind.annotation.GetMapping | ||||||
| import org.springframework.web.bind.annotation.RequestMapping | 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.bind.annotation.RestController | ||||||
|  |  | ||||||
| @RestController | @RestController | ||||||
| @@ -13,4 +14,9 @@ import org.springframework.web.bind.annotation.RestController | |||||||
| class AdminContentSeriesController(private val service: AdminContentSeriesService) { | class AdminContentSeriesController(private val service: AdminContentSeriesService) { | ||||||
|     @GetMapping |     @GetMapping | ||||||
|     fun getSeriesList(pageable: Pageable) = ApiResponse.ok(service.getSeriesList(pageable)) |     fun getSeriesList(pageable: Pageable) = ApiResponse.ok(service.getSeriesList(pageable)) | ||||||
|  |  | ||||||
|  |     @GetMapping("/search") | ||||||
|  |     fun searchSeriesList( | ||||||
|  |         @RequestParam(value = "search_word") searchWord: String | ||||||
|  |     ) = ApiResponse.ok(service.searchSeriesList(searchWord)) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,6 +20,8 @@ interface AdminContentSeriesQueryRepository { | |||||||
|         offset: Long, |         offset: Long, | ||||||
|         limit: Long |         limit: Long | ||||||
|     ): List<GetAdminSeriesListItem> |     ): List<GetAdminSeriesListItem> | ||||||
|  |  | ||||||
|  |     fun searchSeriesList(searchWord: String): List<GetAdminSearchSeriesListItem> | ||||||
| } | } | ||||||
|  |  | ||||||
| class AdminContentSeriesQueryRepositoryImpl( | class AdminContentSeriesQueryRepositoryImpl( | ||||||
| @@ -31,6 +33,7 @@ class AdminContentSeriesQueryRepositoryImpl( | |||||||
|     override fun getSeriesTotalCount(): Int { |     override fun getSeriesTotalCount(): Int { | ||||||
|         val where = series.isActive.isTrue |         val where = series.isActive.isTrue | ||||||
|             .and(series.member.isNotNull) |             .and(series.member.isNotNull) | ||||||
|  |             .and(series.member.isActive.isTrue) | ||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .select(series.id) |             .select(series.id) | ||||||
| @@ -43,6 +46,7 @@ class AdminContentSeriesQueryRepositoryImpl( | |||||||
|     override fun getSeriesList(offset: Long, limit: Long): List<GetAdminSeriesListItem> { |     override fun getSeriesList(offset: Long, limit: Long): List<GetAdminSeriesListItem> { | ||||||
|         val where = series.isActive.isTrue |         val where = series.isActive.isTrue | ||||||
|             .and(series.member.isNotNull) |             .and(series.member.isNotNull) | ||||||
|  |             .and(series.member.isActive.isTrue) | ||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .select( |             .select( | ||||||
| @@ -73,4 +77,24 @@ class AdminContentSeriesQueryRepositoryImpl( | |||||||
|             .orderBy(series.member.id.asc(), series.orders.asc()) |             .orderBy(series.member.id.asc(), series.orders.asc()) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun searchSeriesList(searchWord: String): List<GetAdminSearchSeriesListItem> { | ||||||
|  |         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() | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,4 +14,8 @@ class AdminContentSeriesService(private val repository: AdminContentSeriesReposi | |||||||
|  |  | ||||||
|         return GetAdminSeriesListResponse(totalCount, items) |         return GetAdminSeriesListResponse(totalCount, items) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fun searchSeriesList(searchWord: String): List<GetAdminSearchSeriesListItem> { | ||||||
|  |         return repository.searchSeriesList(searchWord) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,3 +18,8 @@ data class GetAdminSeriesListItem @QueryProjection constructor( | |||||||
|     val state: String, |     val state: String, | ||||||
|     val isAdult: Boolean |     val isAdult: Boolean | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | data class GetAdminSearchSeriesListItem @QueryProjection constructor( | ||||||
|  |     val id: Long, | ||||||
|  |     val title: String | ||||||
|  | ) | ||||||
|   | |||||||
| @@ -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()) | ||||||
|  | } | ||||||
| @@ -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<Event, Long>, AdminEventBannerQueryRepository | ||||||
|  |  | ||||||
|  | interface AdminEventBannerQueryRepository { | ||||||
|  |     fun getEventList(): List<GetAdminEventResponse> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class AdminEventBannerQueryRepositoryImpl( | ||||||
|  |     private val queryFactory: JPAQueryFactory | ||||||
|  | ) : AdminEventBannerQueryRepository { | ||||||
|  |     override fun getEventList(): List<GetAdminEventResponse> { | ||||||
|  |         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<LocalDateTime>): 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" | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -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<GetAdminEventResponse> { | ||||||
|  |         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() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -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 | ||||||
|  | ) | ||||||
| @@ -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 kr.co.vividnext.sodalive.common.ApiResponse | ||||||
| import org.springframework.security.access.prepost.PreAuthorize | import org.springframework.security.access.prepost.PreAuthorize | ||||||
| @@ -11,22 +11,20 @@ import org.springframework.web.bind.annotation.RestController | |||||||
| 
 | 
 | ||||||
| @RestController | @RestController | ||||||
| @RequestMapping("/event/charge") | @RequestMapping("/event/charge") | ||||||
|  | @PreAuthorize("hasRole('ADMIN')") | ||||||
| class AdminChargeEventController(private val service: AdminChargeEventService) { | class AdminChargeEventController(private val service: AdminChargeEventService) { | ||||||
|     @PostMapping |     @PostMapping | ||||||
|     @PreAuthorize("hasRole('ADMIN')") |  | ||||||
|     fun createChargeEvent(@RequestBody request: CreateChargeEventRequest): ApiResponse<Any> { |     fun createChargeEvent(@RequestBody request: CreateChargeEventRequest): ApiResponse<Any> { | ||||||
|         service.createChargeEvent(request) |         service.createChargeEvent(request) | ||||||
|         return ApiResponse.ok(null, "등록되었습니다.") |         return ApiResponse.ok(null, "등록되었습니다.") | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @PutMapping |     @PutMapping | ||||||
|     @PreAuthorize("hasRole('ADMIN')") |  | ||||||
|     fun modifyChargeEvent(@RequestBody request: ModifyChargeEventRequest) = ApiResponse.ok( |     fun modifyChargeEvent(@RequestBody request: ModifyChargeEventRequest) = ApiResponse.ok( | ||||||
|         service.modifyChargeEvent(request), |         service.modifyChargeEvent(request), | ||||||
|         "수정되었습니다." |         "수정되었습니다." | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     @GetMapping("/list") |     @GetMapping("/list") | ||||||
|     @PreAuthorize("hasRole('ADMIN')") |  | ||||||
|     fun getChargeEventList() = ApiResponse.ok(service.getChargeEventList()) |     fun getChargeEventList() = ApiResponse.ok(service.getChargeEventList()) | ||||||
| } | } | ||||||
| @@ -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 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.data.jpa.repository.JpaRepository | ||||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||||
| 
 | 
 | ||||||
| @@ -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 kr.co.vividnext.sodalive.common.SodaException | ||||||
| import org.springframework.data.repository.findByIdOrNull | import org.springframework.data.repository.findByIdOrNull | ||||||
| @@ -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 kr.co.vividnext.sodalive.common.BaseEntity | ||||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package kr.co.vividnext.sodalive.admin.event | package kr.co.vividnext.sodalive.admin.event.charge | ||||||
| 
 | 
 | ||||||
| data class CreateChargeEventRequest( | data class CreateChargeEventRequest( | ||||||
|     val title: String, |     val title: String, | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package kr.co.vividnext.sodalive.admin.event | package kr.co.vividnext.sodalive.admin.event.charge | ||||||
| 
 | 
 | ||||||
| data class GetChargeEventListResponse( | data class GetChargeEventListResponse( | ||||||
|     val id: Long, |     val id: Long, | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package kr.co.vividnext.sodalive.admin.event | package kr.co.vividnext.sodalive.admin.event.charge | ||||||
| 
 | 
 | ||||||
| data class ModifyChargeEventRequest( | data class ModifyChargeEventRequest( | ||||||
|     val id: Long, |     val id: Long, | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| package kr.co.vividnext.sodalive.can.charge.event | package kr.co.vividnext.sodalive.can.charge.event | ||||||
|  |  | ||||||
| import com.querydsl.jpa.impl.JPAQueryFactory | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
| import kr.co.vividnext.sodalive.admin.event.ChargeEvent | import kr.co.vividnext.sodalive.admin.event.charge.ChargeEvent | ||||||
| import kr.co.vividnext.sodalive.admin.event.QChargeEvent.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.charge.QCharge.charge | ||||||
| import kr.co.vividnext.sodalive.can.payment.PaymentStatus | import kr.co.vividnext.sodalive.can.payment.PaymentStatus | ||||||
| import kr.co.vividnext.sodalive.can.payment.QPayment.payment | import kr.co.vividnext.sodalive.can.payment.QPayment.payment | ||||||
|   | |||||||
| @@ -14,8 +14,6 @@ import org.springframework.cache.annotation.Cacheable | |||||||
| import org.springframework.data.domain.Pageable | import org.springframework.data.domain.Pageable | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||||
| import java.time.ZoneId |  | ||||||
| import java.time.format.DateTimeFormatter |  | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| class AudioContentMainService( | class AudioContentMainService( | ||||||
| @@ -93,13 +91,14 @@ class AudioContentMainService( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional(readOnly = true) |     @Transactional(readOnly = true) | ||||||
|     @Cacheable(cacheNames = ["default"], key = "'contentMainBannerList:' + #memberId + ':' + #isAdult") |  | ||||||
|     fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) = |     fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) = | ||||||
|         repository.getAudioContentMainBannerList(isAdult = isAdult) |         repository.getAudioContentMainBannerList(isAdult = isAdult) | ||||||
|             .asSequence() |             .asSequence() | ||||||
|             .filter { |             .filter { | ||||||
|                 if (it.type == AudioContentBannerType.CREATOR && it.creator != null) { |                 if (it.type == AudioContentBannerType.CREATOR && it.creator != null) { | ||||||
|                     !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creator!!.id!!) |                     !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 { |                 } else { | ||||||
|                     true |                     true | ||||||
|                 } |                 } | ||||||
| @@ -109,17 +108,6 @@ class AudioContentMainService( | |||||||
|                     type = it.type, |                     type = it.type, | ||||||
|                     thumbnailImageUrl = "$imageHost/${it.thumbnailImage}", |                     thumbnailImageUrl = "$imageHost/${it.thumbnailImage}", | ||||||
|                     eventItem = if (it.type == AudioContentBannerType.EVENT && it.event != null) { |                     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( |                         EventItem( | ||||||
|                             id = it.event!!.id!!, |                             id = it.event!!.id!!, | ||||||
|                             thumbnailImageUrl = if (!it.event!!.thumbnailImage.startsWith("https://")) { |                             thumbnailImageUrl = if (!it.event!!.thumbnailImage.startsWith("https://")) { | ||||||
| @@ -136,11 +124,7 @@ class AudioContentMainService( | |||||||
|                                 it.event!!.detailImage |                                 it.event!!.detailImage | ||||||
|                             }, |                             }, | ||||||
|                             popupImageUrl = null, |                             popupImageUrl = null, | ||||||
|                             startDate = startDate, |                             link = it.event!!.link | ||||||
|                             endDate = endDate, |  | ||||||
|                             link = it.event!!.link, |  | ||||||
|                             title = it.event!!.title, |  | ||||||
|                             isPopup = false |  | ||||||
|                         ) |                         ) | ||||||
|                     } else { |                     } else { | ||||||
|                         null |                         null | ||||||
| @@ -150,6 +134,11 @@ class AudioContentMainService( | |||||||
|                     } else { |                     } else { | ||||||
|                         null |                         null | ||||||
|                     }, |                     }, | ||||||
|  |                     seriesId = if (it.type == AudioContentBannerType.SERIES && it.series != null) { | ||||||
|  |                         it.series!!.id | ||||||
|  |                     } else { | ||||||
|  |                         null | ||||||
|  |                     }, | ||||||
|                     link = it.link |                     link = it.link | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package kr.co.vividnext.sodalive.content.main.banner | package kr.co.vividnext.sodalive.content.main.banner | ||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.common.BaseEntity | 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.event.Event | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import javax.persistence.Column | import javax.persistence.Column | ||||||
| @@ -34,10 +36,18 @@ data class AudioContentBanner( | |||||||
|     @JoinColumn(name = "creator_id", nullable = true) |     @JoinColumn(name = "creator_id", nullable = true) | ||||||
|     var creator: Member? = null |     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) |     @Column(nullable = true) | ||||||
|     var link: String? = null |     var link: String? = null | ||||||
| } | } | ||||||
|  |  | ||||||
| enum class AudioContentBannerType { | enum class AudioContentBannerType { | ||||||
|     EVENT, CREATOR, LINK |     EVENT, CREATOR, LINK, SERIES | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,5 +8,6 @@ data class GetAudioContentBannerResponse( | |||||||
|     @JsonProperty("thumbnailImageUrl") val thumbnailImageUrl: String, |     @JsonProperty("thumbnailImageUrl") val thumbnailImageUrl: String, | ||||||
|     @JsonProperty("eventItem") val eventItem: EventItem?, |     @JsonProperty("eventItem") val eventItem: EventItem?, | ||||||
|     @JsonProperty("creatorId") val creatorId: Long?, |     @JsonProperty("creatorId") val creatorId: Long?, | ||||||
|  |     @JsonProperty("seriesId") val seriesId: Long?, | ||||||
|     @JsonProperty("link") val link: String? |     @JsonProperty("link") val link: String? | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -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() | ||||||
| @@ -1,8 +1,5 @@ | |||||||
| package kr.co.vividnext.sodalive.event | 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 com.querydsl.jpa.impl.JPAQueryFactory | ||||||
| import kr.co.vividnext.sodalive.event.QEvent.event | import kr.co.vividnext.sodalive.event.QEvent.event | ||||||
| import org.springframework.data.jpa.repository.JpaRepository | import org.springframework.data.jpa.repository.JpaRepository | ||||||
| @@ -43,15 +40,10 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even | |||||||
|             .select( |             .select( | ||||||
|                 QEventItem( |                 QEventItem( | ||||||
|                     event.id, |                     event.id, | ||||||
|                     event.title, |  | ||||||
|                     event.thumbnailImage, |                     event.thumbnailImage, | ||||||
|                     event.detailImage, |                     event.detailImage, | ||||||
|                     event.popupImage, |                     event.popupImage, | ||||||
|                     getFormattedDate(event.startDate), |                     event.link | ||||||
|                     getFormattedDate(event.endDate), |  | ||||||
|                     event.link, |  | ||||||
|                     event.isAdult, |  | ||||||
|                     event.isPopup |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(event) |             .from(event) | ||||||
| @@ -84,15 +76,10 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even | |||||||
|             .select( |             .select( | ||||||
|                 QEventItem( |                 QEventItem( | ||||||
|                     event.id, |                     event.id, | ||||||
|                     event.title, |  | ||||||
|                     event.thumbnailImage, |                     event.thumbnailImage, | ||||||
|                     event.detailImage, |                     event.detailImage, | ||||||
|                     event.popupImage, |                     event.popupImage, | ||||||
|                     getFormattedDate(event.startDate), |                     event.link | ||||||
|                     getFormattedDate(event.endDate), |  | ||||||
|                     event.link, |  | ||||||
|                     event.isAdult, |  | ||||||
|                     event.isPopup |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(event) |             .from(event) | ||||||
| @@ -100,18 +87,4 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even | |||||||
|             .orderBy(event.id.desc()) |             .orderBy(event.id.desc()) | ||||||
|             .fetchFirst() |             .fetchFirst() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private fun getFormattedDate(dateTimePath: DateTimePath<LocalDateTime>): 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" |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,13 +12,8 @@ data class GetEventResponse( | |||||||
| @JsonIgnoreProperties(ignoreUnknown = true) | @JsonIgnoreProperties(ignoreUnknown = true) | ||||||
| data class EventItem @QueryProjection constructor( | data class EventItem @QueryProjection constructor( | ||||||
|     @JsonProperty("id") val id: Long, |     @JsonProperty("id") val id: Long, | ||||||
|     @JsonProperty("title") val title: String? = null, |  | ||||||
|     @JsonProperty("thumbnailImageUrl") var thumbnailImageUrl: String, |     @JsonProperty("thumbnailImageUrl") var thumbnailImageUrl: String, | ||||||
|     @JsonProperty("detailImageUrl") var detailImageUrl: String? = null, |     @JsonProperty("detailImageUrl") var detailImageUrl: String? = null, | ||||||
|     @JsonProperty("popupImageUrl") var popupImageUrl: String? = null, |     @JsonProperty("popupImageUrl") var popupImageUrl: String? = null, | ||||||
|     @JsonProperty("startDate") var startDate: String, |     @JsonProperty("link") val link: String? = null | ||||||
|     @JsonProperty("endDate") var endDate: String, |  | ||||||
|     @JsonProperty("link") val link: String? = null, |  | ||||||
|     @JsonProperty("isAdult") val isAdult: Boolean? = null, |  | ||||||
|     @JsonProperty("isPopup") val isPopup: Boolean |  | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -2,10 +2,8 @@ package kr.co.vividnext.sodalive.member.notification | |||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| import org.springframework.transaction.annotation.Transactional |  | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| @Transactional(readOnly = true) |  | ||||||
| class MemberNotificationService(private val repository: MemberNotificationRepository) { | class MemberNotificationService(private val repository: MemberNotificationRepository) { | ||||||
|     fun updateNotification( |     fun updateNotification( | ||||||
|         live: Boolean? = null, |         live: Boolean? = null, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user