feat(content-preference): 콘텐츠 조회 설정 서버 저장 전환을 반영한다

This commit is contained in:
2026-03-27 13:33:51 +09:00
parent 1ba3cb8a40
commit a87bd147dc
75 changed files with 3593 additions and 301 deletions

View File

@@ -67,7 +67,7 @@ class ExplorerController(
service.getCreatorProfile(
creatorId = creatorId,
timezone = timezone,
isAdultContentVisible = isAdultContentVisible ?: true,
isAdultContentVisible = isAdultContentVisible,
member = member
)
)

View File

@@ -339,6 +339,7 @@ class ExplorerQueryRepository(
fun getLiveRoomList(
creatorId: Long,
userMember: Member,
isAdult: Boolean,
timezone: String,
offset: Long = 0
): List<LiveRoomResponse> {
@@ -361,7 +362,8 @@ class ExplorerQueryRepository(
where = where.and(genderCondition.or(liveRoom.member.id.eq(userMember.id)))
}
if (userMember.auth == null) {
// 라이브 목록 노출은 호출부에서 계산한 정책 결과(isAdult)만 신뢰해 필터링한다.
if (!isAdult) {
where = where.and(liveRoom.isAdult.isFalse)
}

View File

@@ -2,7 +2,6 @@ package kr.co.vividnext.sodalive.explorer
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.AudioContentService
import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.LanguageDetectEvent
import kr.co.vividnext.sodalive.content.LanguageDetectTargetType
import kr.co.vividnext.sodalive.content.SortType
@@ -31,6 +30,7 @@ import kr.co.vividnext.sodalive.member.DonationRankingPeriod
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.member.MemberService
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.domain.Pageable
@@ -48,6 +48,7 @@ import kotlin.random.Random
@Transactional(readOnly = true)
class ExplorerService(
private val memberService: MemberService,
private val memberContentPreferenceService: MemberContentPreferenceService,
private val audioContentService: AudioContentService,
private val donationRankingService: CreatorDonationRankingService,
@@ -257,9 +258,15 @@ class ExplorerService(
fun getCreatorProfile(
creatorId: Long,
timezone: String,
isAdultContentVisible: Boolean,
isAdultContentVisible: Boolean?,
member: Member
): GetCreatorProfileResponse {
val preference = memberContentPreferenceService.resolveForQuery(
member = member,
isAdultContentVisible = isAdultContentVisible,
contentType = null
)
// 크리에이터(유저) 정보
val creatorAccount = queryRepository.getMember(creatorId)
?: throw SodaException(messageKey = "member.validation.user_not_found")
@@ -307,6 +314,7 @@ class ExplorerService(
queryRepository.getLiveRoomList(
creatorId,
userMember = member,
isAdult = preference.isAdult,
timezone = timezone
)
} else {
@@ -318,8 +326,8 @@ class ExplorerService(
audioContentService.getAudioContentList(
creatorId = creatorId,
sortType = SortType.NEWEST,
isAdultContentVisible = isAdultContentVisible,
contentType = ContentType.ALL,
isAdultContentVisible = preference.isAdultContentVisible,
contentType = preference.contentType,
member = member,
offset = 0,
limit = 3
@@ -348,7 +356,11 @@ class ExplorerService(
// 크리에이터의 최신 오디오 콘텐츠 1개
val latestContent = if (isCreator && !isBlock) {
audioContentService.getLatestCreatorAudioContent(creatorId, member, isAdultContentVisible)
audioContentService.getLatestCreatorAudioContent(
creatorId = creatorId,
member = member,
isAdultContentVisible = preference.isAdultContentVisible
)
} else {
null
}
@@ -382,7 +394,7 @@ class ExplorerService(
timezone = timezone,
offset = 0,
limit = 3,
isAdult = member.auth != null
isAdult = preference.isAdult
)
} else {
listOf()
@@ -412,8 +424,8 @@ class ExplorerService(
seriesService
.getSeriesList(
creatorId = creatorId,
isAdultContentVisible = isAdultContentVisible,
contentType = ContentType.ALL,
isAdultContentVisible = preference.isAdultContentVisible,
contentType = preference.contentType,
member = member
)
.items

View File

@@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.Create
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.ModifyCommunityPostCommentRequest
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeRequest
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
import org.springframework.data.domain.Pageable
import org.springframework.lang.Nullable
import org.springframework.security.access.prepost.PreAuthorize
@@ -23,7 +24,10 @@ import org.springframework.web.multipart.MultipartFile
@RestController
@RequestMapping("/creator-community")
class CreatorCommunityController(private val service: CreatorCommunityService) {
class CreatorCommunityController(
private val service: CreatorCommunityService,
private val memberContentPreferenceService: MemberContentPreferenceService
) {
@PostMapping
@PreAuthorize("hasRole('CREATOR')")
fun createCommunityPost(
@@ -92,6 +96,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
pageable: Pageable
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.getCommunityPostList(
@@ -100,7 +105,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
timezone = timezone,
offset = pageable.offset,
limit = pageable.pageSize.toLong(),
isAdult = member.auth != null
isAdult = isAdult
)
)
}
@@ -112,13 +117,14 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.getCommunityPostDetail(
postId = postId,
memberId = member.id!!,
timezone = timezone,
isAdult = member.auth != null
isAdult = isAdult
)
)
}
@@ -129,8 +135,10 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
// 좋아요 대상 게시글 조회도 저장된 성인 노출 정책을 동일하게 적용한다.
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(service.communityPostLike(request, member))
ApiResponse.ok(service.communityPostLike(request, member, isAdult))
}
@PostMapping("/comment")
@@ -139,6 +147,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.createCommunityPostComment(
@@ -146,7 +155,8 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
postId = request.postId,
parentId = request.parentId,
isSecret = request.isSecret,
member = member
member = member,
isAdult = isAdult
)
)
}
@@ -171,6 +181,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
pageable: Pageable
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.getCommunityPostCommentList(
@@ -178,7 +189,8 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
memberId = member.id!!,
timezone = timezone,
offset = pageable.offset,
limit = pageable.pageSize.toLong()
limit = pageable.pageSize.toLong(),
isAdult = isAdult
)
)
}
@@ -191,6 +203,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
pageable: Pageable
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.getCommentReplyList(
@@ -198,7 +211,8 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
memberId = member.id!!,
timezone = timezone,
offset = pageable.offset,
limit = pageable.pageSize.toLong()
limit = pageable.pageSize.toLong(),
isAdult = isAdult
)
)
}
@@ -209,12 +223,13 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.getLatestPostListFromCreatorsYouFollow(
timezone = timezone,
memberId = member.id!!,
isAdult = member.auth != null
isAdult = isAdult
)
)
}
@@ -225,13 +240,14 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
val isAdult = memberContentPreferenceService.getStoredPreference(member).isAdult
ApiResponse.ok(
service.purchasePost(
postId = request.postId,
memberId = member.id!!,
timezone = request.timezone,
isAdult = member.auth != null,
isAdult = isAdult,
container = request.container
)
)

View File

@@ -380,14 +380,18 @@ class CreatorCommunityService(
}
@Transactional
fun communityPostLike(request: PostCommunityPostLikeRequest, member: Member): PostCommunityPostLikeResponse {
fun communityPostLike(
request: PostCommunityPostLikeRequest,
member: Member,
isAdult: Boolean
): PostCommunityPostLikeResponse {
var postLike = likeRepository.findByPostIdAndMemberId(postId = request.postId, memberId = member.id!!)
if (postLike == null) {
postLike = CreatorCommunityLike()
postLike.member = member
val post = repository.findByIdAndActive(request.postId, isAdult = member.auth != null)
val post = repository.findByIdAndActive(request.postId, isAdult = isAdult)
?: throw SodaException(messageKey = "creator.community.invalid_request_retry")
postLike.creatorCommunity = post
@@ -405,10 +409,11 @@ class CreatorCommunityService(
comment: String,
postId: Long,
parentId: Long? = null,
isSecret: Boolean = false
isSecret: Boolean = false,
isAdult: Boolean
) {
val post = repository.findByIdOrNull(id = postId)
?: throw SodaException(messageKey = "creator.community.invalid_post_retry")
val post = repository.findByIdAndActive(postId = postId, isAdult = isAdult)
?: throw SodaException(messageKey = "creator.community.invalid_request_retry")
val creatorId = post.member!!.id!!
@@ -480,10 +485,13 @@ class CreatorCommunityService(
memberId: Long,
timezone: String,
offset: Long,
limit: Long
limit: Long,
isAdult: Boolean
): GetCommunityPostCommentListResponse {
val post = repository.findByIdOrNull(id = postId)
if (post != null && isBlockedBetweenMembers(memberId = memberId, creatorId = post.member!!.id!!)) {
val post = repository.findByIdAndActive(postId = postId, isAdult = isAdult)
?: throw SodaException(messageKey = "creator.community.invalid_request_retry")
if (isBlockedBetweenMembers(memberId = memberId, creatorId = post.member!!.id!!)) {
return GetCommunityPostCommentListResponse(totalCount = 0, items = listOf())
}
@@ -509,9 +517,14 @@ class CreatorCommunityService(
memberId: Long,
timezone: String,
offset: Long,
limit: Long
limit: Long,
isAdult: Boolean
): GetCommunityPostCommentListResponse {
val parentComment = commentRepository.findByIdOrNull(id = commentId)
if (parentComment != null && !isAdult && parentComment.creatorCommunity!!.isAdult) {
throw SodaException(messageKey = "creator.community.invalid_request_retry")
}
if (
parentComment != null &&
isBlockedBetweenMembers(memberId = memberId, creatorId = parentComment.creatorCommunity!!.member!!.id!!)