| @@ -62,11 +62,18 @@ interface AudioContentQueryRepository { | |||||||
|         cloudfrontHost: String, |         cloudfrontHost: String, | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
|         theme: String = "", |         theme: String = "", | ||||||
|  |         sortType: SortType = SortType.NEWEST, | ||||||
|         isAdult: Boolean = false, |         isAdult: Boolean = false, | ||||||
|         offset: Long = 0, |         offset: Long = 0, | ||||||
|         limit: Long = 20 |         limit: Long = 20 | ||||||
|     ): List<GetAudioContentMainItem> |     ): List<GetAudioContentMainItem> | ||||||
|  |  | ||||||
|  |     fun totalCountByTheme( | ||||||
|  |         memberId: Long, | ||||||
|  |         theme: String = "", | ||||||
|  |         isAdult: Boolean = false | ||||||
|  |     ): Int | ||||||
|  |  | ||||||
|     fun findByThemeFor2Weeks( |     fun findByThemeFor2Weeks( | ||||||
|         cloudfrontHost: String, |         cloudfrontHost: String, | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
| @@ -317,10 +324,17 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|         cloudfrontHost: String, |         cloudfrontHost: String, | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
|         theme: String, |         theme: String, | ||||||
|  |         sortType: SortType, | ||||||
|         isAdult: Boolean, |         isAdult: Boolean, | ||||||
|         offset: Long, |         offset: Long, | ||||||
|         limit: Long |         limit: Long | ||||||
|     ): List<GetAudioContentMainItem> { |     ): List<GetAudioContentMainItem> { | ||||||
|  |         val orderBy = when (sortType) { | ||||||
|  |             SortType.NEWEST -> audioContent.releaseDate.desc() | ||||||
|  |             SortType.PRICE_HIGH -> audioContent.price.desc() | ||||||
|  |             SortType.PRICE_LOW -> audioContent.price.asc() | ||||||
|  |         } | ||||||
|  |  | ||||||
|         var where = audioContent.isActive.isTrue |         var where = audioContent.isActive.isTrue | ||||||
|             .and( |             .and( | ||||||
|                 audioContent.releaseDate.isNull |                 audioContent.releaseDate.isNull | ||||||
| @@ -344,7 +358,9 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|                     audioContent.title, |                     audioContent.title, | ||||||
|                     member.id, |                     member.id, | ||||||
|                     member.profileImage.prepend("/").prepend(cloudfrontHost), |                     member.profileImage.prepend("/").prepend(cloudfrontHost), | ||||||
|                     member.nickname |                     member.nickname, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(audioContent) |             .from(audioContent) | ||||||
| @@ -353,10 +369,36 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|             .where(where) |             .where(where) | ||||||
|             .offset(offset) |             .offset(offset) | ||||||
|             .limit(limit) |             .limit(limit) | ||||||
|             .orderBy(audioContent.releaseDate.desc()) |             .orderBy(orderBy) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun totalCountByTheme(memberId: Long, theme: String, isAdult: Boolean): Int { | ||||||
|  |         var where = audioContent.isActive.isTrue | ||||||
|  |             .and( | ||||||
|  |                 audioContent.releaseDate.isNull | ||||||
|  |                     .or(audioContent.releaseDate.loe(LocalDateTime.now())) | ||||||
|  |                     .or(audioContent.member.id.eq(memberId)) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         if (!isAdult) { | ||||||
|  |             where = where.and(audioContent.isAdult.isFalse) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (theme.isNotBlank()) { | ||||||
|  |             where = where.and(audioContentTheme.theme.eq(theme)) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select(audioContent.id) | ||||||
|  |             .from(audioContent) | ||||||
|  |             .innerJoin(audioContent.member, member) | ||||||
|  |             .innerJoin(audioContent.theme, audioContentTheme) | ||||||
|  |             .where(where) | ||||||
|  |             .fetch() | ||||||
|  |             .size | ||||||
|  |     } | ||||||
|  |  | ||||||
|     override fun totalCountNewContentFor2Weeks(theme: String, memberId: Long, isAdult: Boolean): Int { |     override fun totalCountNewContentFor2Weeks(theme: String, memberId: Long, isAdult: Boolean): Int { | ||||||
|         var where = audioContent.isActive.isTrue |         var where = audioContent.isActive.isTrue | ||||||
|             .and(audioContent.releaseDate.goe(LocalDateTime.now().minusWeeks(2))) |             .and(audioContent.releaseDate.goe(LocalDateTime.now().minusWeeks(2))) | ||||||
| @@ -416,7 +458,9 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|                     audioContent.title, |                     audioContent.title, | ||||||
|                     member.id, |                     member.id, | ||||||
|                     member.profileImage.prepend("/").prepend(cloudfrontHost), |                     member.profileImage.prepend("/").prepend(cloudfrontHost), | ||||||
|                     member.nickname |                     member.nickname, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(audioContent) |             .from(audioContent) | ||||||
| @@ -519,7 +563,9 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|                     member.profileImage |                     member.profileImage | ||||||
|                         .prepend("/") |                         .prepend("/") | ||||||
|                         .prepend(cloudfrontHost), |                         .prepend(cloudfrontHost), | ||||||
|                     member.nickname |                     member.nickname, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(audioContent) |             .from(audioContent) | ||||||
|   | |||||||
| @@ -9,5 +9,7 @@ data class GetAudioContentMainItem @QueryProjection constructor( | |||||||
|     @JsonProperty("title") val title: String, |     @JsonProperty("title") val title: String, | ||||||
|     @JsonProperty("creatorId") val creatorId: Long, |     @JsonProperty("creatorId") val creatorId: Long, | ||||||
|     @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String, |     @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String, | ||||||
|     @JsonProperty("creatorNickname") val creatorNickname: String |     @JsonProperty("creatorNickname") val creatorNickname: String, | ||||||
|  |     @JsonProperty("price") val price: Int, | ||||||
|  |     @JsonProperty("duration") val duration: String | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -1,16 +0,0 @@ | |||||||
| package kr.co.vividnext.sodalive.content.main |  | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.annotation.JsonProperty |  | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse |  | ||||||
| import kr.co.vividnext.sodalive.content.main.curation.GetAudioContentCurationResponse |  | ||||||
|  |  | ||||||
| data class GetAudioContentMainResponse( |  | ||||||
|     @JsonProperty("newContentUploadCreatorList") val newContentUploadCreatorList: List<GetNewContentUploadCreator>, |  | ||||||
|     @JsonProperty("bannerList") val bannerList: List<GetAudioContentBannerResponse>, |  | ||||||
|     @JsonProperty("orderList") val orderList: List<GetAudioContentMainItem>, |  | ||||||
|     @JsonProperty("themeList") val themeList: List<String>, |  | ||||||
|     @JsonProperty("newContentList") val newContentList: List<GetAudioContentMainItem>, |  | ||||||
|     @JsonProperty("curationList") val curationList: List<GetAudioContentCurationResponse>, |  | ||||||
|     @JsonProperty("contentRankingSortTypeList") val contentRankingSortTypeList: List<String>, |  | ||||||
|     @JsonProperty("contentRanking") val contentRanking: GetAudioContentRanking |  | ||||||
| ) |  | ||||||
| @@ -59,7 +59,9 @@ class AudioContentCurationQueryRepository(private val queryFactory: JPAQueryFact | |||||||
|                     audioContent.title, |                     audioContent.title, | ||||||
|                     member.id, |                     member.id, | ||||||
|                     member.profileImage.prepend("/").prepend(cloudfrontHost), |                     member.profileImage.prepend("/").prepend(cloudfrontHost), | ||||||
|                     member.nickname |                     member.nickname, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(audioContent) |             .from(audioContent) | ||||||
|   | |||||||
| @@ -192,7 +192,9 @@ class OrderQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Orde | |||||||
|                     member.profileImage |                     member.profileImage | ||||||
|                         .prepend("/") |                         .prepend("/") | ||||||
|                         .prepend(coverImageHost), |                         .prepend(coverImageHost), | ||||||
|                     member.nickname |                     member.nickname, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(order) |             .from(order) | ||||||
|   | |||||||
| @@ -2,11 +2,15 @@ package kr.co.vividnext.sodalive.content.theme | |||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.common.ApiResponse | import kr.co.vividnext.sodalive.common.ApiResponse | ||||||
| import kr.co.vividnext.sodalive.common.SodaException | import kr.co.vividnext.sodalive.common.SodaException | ||||||
|  | import kr.co.vividnext.sodalive.content.SortType | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
|  | import org.springframework.data.domain.Pageable | ||||||
| import org.springframework.security.access.prepost.PreAuthorize | import org.springframework.security.access.prepost.PreAuthorize | ||||||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | import org.springframework.security.core.annotation.AuthenticationPrincipal | ||||||
| import org.springframework.web.bind.annotation.GetMapping | import org.springframework.web.bind.annotation.GetMapping | ||||||
|  | import org.springframework.web.bind.annotation.PathVariable | ||||||
| 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 | ||||||
| @@ -21,4 +25,24 @@ class AudioContentThemeController(private val service: AudioContentThemeService) | |||||||
|  |  | ||||||
|         ApiResponse.ok(service.getThemes()) |         ApiResponse.ok(service.getThemes()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @GetMapping("/{id}/content") | ||||||
|  |     fun getContentByTheme( | ||||||
|  |         @PathVariable id: Long, | ||||||
|  |         @RequestParam("sort-type", required = false) sortType: SortType = SortType.NEWEST, | ||||||
|  |         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, | ||||||
|  |         pageable: Pageable | ||||||
|  |     ) = run { | ||||||
|  |         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |  | ||||||
|  |         ApiResponse.ok( | ||||||
|  |             service.getContentByTheme( | ||||||
|  |                 themeId = id, | ||||||
|  |                 sortType = sortType, | ||||||
|  |                 member = member, | ||||||
|  |                 offset = pageable.offset, | ||||||
|  |                 limit = pageable.pageSize.toLong() | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,10 +1,63 @@ | |||||||
| package kr.co.vividnext.sodalive.content.theme | package kr.co.vividnext.sodalive.content.theme | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.common.SodaException | ||||||
|  | import kr.co.vividnext.sodalive.content.AudioContentRepository | ||||||
|  | import kr.co.vividnext.sodalive.content.SortType | ||||||
|  | import kr.co.vividnext.sodalive.content.theme.content.GetContentByThemeResponse | ||||||
|  | import kr.co.vividnext.sodalive.member.Member | ||||||
|  | import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||||
|  | import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
|  | import org.springframework.transaction.annotation.Transactional | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| class AudioContentThemeService(private val queryRepository: AudioContentThemeQueryRepository) { | class AudioContentThemeService( | ||||||
|  |     private val queryRepository: AudioContentThemeQueryRepository, | ||||||
|  |     private val blockMemberRepository: BlockMemberRepository, | ||||||
|  |     private val contentRepository: AudioContentRepository, | ||||||
|  |  | ||||||
|  |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|  |     private val imageHost: String | ||||||
|  | ) { | ||||||
|  |     @Transactional(readOnly = true) | ||||||
|     fun getThemes(): List<GetAudioContentThemeResponse> { |     fun getThemes(): List<GetAudioContentThemeResponse> { | ||||||
|         return queryRepository.getActiveThemes() |         return queryRepository.getActiveThemes() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Transactional(readOnly = true) | ||||||
|  |     fun getContentByTheme( | ||||||
|  |         themeId: Long, | ||||||
|  |         sortType: SortType, | ||||||
|  |         member: Member, | ||||||
|  |         offset: Long, | ||||||
|  |         limit: Long | ||||||
|  |     ): GetContentByThemeResponse { | ||||||
|  |         val theme = queryRepository.findThemeByIdAndActive(themeId) | ||||||
|  |             ?: throw SodaException("잘못된 요청입니다.") | ||||||
|  |  | ||||||
|  |         val totalCount = contentRepository.totalCountByTheme( | ||||||
|  |             memberId = member.id!!, | ||||||
|  |             theme = theme.theme, | ||||||
|  |             isAdult = member.auth != null | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         val items = contentRepository.findByTheme( | ||||||
|  |             cloudfrontHost = imageHost, | ||||||
|  |             memberId = member.id!!, | ||||||
|  |             theme = theme.theme, | ||||||
|  |             sortType = sortType, | ||||||
|  |             isAdult = member.auth != null, | ||||||
|  |             offset = offset, | ||||||
|  |             limit = limit | ||||||
|  |         ) | ||||||
|  |             .asSequence() | ||||||
|  |             .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) } | ||||||
|  |             .toList() | ||||||
|  |  | ||||||
|  |         return GetContentByThemeResponse( | ||||||
|  |             theme = theme.theme, | ||||||
|  |             totalCount = totalCount, | ||||||
|  |             items = items | ||||||
|  |         ) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | package kr.co.vividnext.sodalive.content.theme.content | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||||
|  |  | ||||||
|  | data class GetContentByThemeResponse( | ||||||
|  |     val theme: String, | ||||||
|  |     val totalCount: Int, | ||||||
|  |     val items: List<GetAudioContentMainItem> | ||||||
|  | ) | ||||||
| @@ -442,7 +442,7 @@ class ExplorerService( | |||||||
|             FcmEvent( |             FcmEvent( | ||||||
|                 type = FcmEventType.CHANGE_NOTICE, |                 type = FcmEventType.CHANGE_NOTICE, | ||||||
|                 title = member.nickname, |                 title = member.nickname, | ||||||
|                 message = "공지를 등록했습니다.", |                 message = "새 글이 등록되었습니다.", | ||||||
|                 creatorId = member.id!! |                 creatorId = member.id!! | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|   | |||||||
| @@ -77,7 +77,7 @@ class CreatorCommunityService( | |||||||
|             FcmEvent( |             FcmEvent( | ||||||
|                 type = FcmEventType.CHANGE_NOTICE, |                 type = FcmEventType.CHANGE_NOTICE, | ||||||
|                 title = member.nickname, |                 title = member.nickname, | ||||||
|                 message = "공지를 등록했습니다.", |                 message = "새 글이 등록되었습니다.", | ||||||
|                 creatorId = member.id!! |                 creatorId = member.id!! | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user