feat(content-preference): 콘텐츠 조회 설정 서버 저장 전환을 반영한다
This commit is contained in:
@@ -7,6 +7,9 @@ import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||
import kr.co.vividnext.sodalive.marketing.AdTrackingHistoryType
|
||||
import kr.co.vividnext.sodalive.marketing.AdTrackingService
|
||||
import kr.co.vividnext.sodalive.member.block.MemberBlockRequest
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.UpdateMemberContentPreferenceRequest
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.UpdateMemberContentPreferenceResponse
|
||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowRequest
|
||||
import kr.co.vividnext.sodalive.member.login.LoginRequest
|
||||
import kr.co.vividnext.sodalive.member.login.LoginResponse
|
||||
@@ -20,6 +23,7 @@ import org.springframework.data.domain.Pageable
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PatchMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.PutMapping
|
||||
@@ -35,6 +39,7 @@ import org.springframework.web.multipart.MultipartFile
|
||||
@RequestMapping("/member")
|
||||
class MemberController(
|
||||
private val service: MemberService,
|
||||
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||
private val socialAuthServiceResolver: SocialAuthServiceResolver,
|
||||
private val trackingService: AdTrackingService,
|
||||
private val userActionService: UserActionService,
|
||||
@@ -136,6 +141,27 @@ class MemberController(
|
||||
ApiResponse.ok(service.getMemberInfo(member, container ?: "web"))
|
||||
}
|
||||
|
||||
@PatchMapping("/content-preference")
|
||||
fun updateContentPreference(
|
||||
@RequestBody request: UpdateMemberContentPreferenceRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
|
||||
|
||||
val preference = memberContentPreferenceService.updatePreference(
|
||||
member = member,
|
||||
isAdultContentVisible = request.isAdultContentVisible,
|
||||
contentType = request.contentType
|
||||
)
|
||||
|
||||
ApiResponse.ok(
|
||||
UpdateMemberContentPreferenceResponse(
|
||||
isAdultContentVisible = preference.isAdultContentVisible,
|
||||
contentType = preference.contentType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@PostMapping("/notification")
|
||||
fun updateNotificationSettings(
|
||||
@RequestBody request: UpdateNotificationSettingRequest,
|
||||
|
||||
@@ -17,7 +17,11 @@ import kr.co.vividnext.sodalive.member.notification.QMemberNotification.memberNo
|
||||
import kr.co.vividnext.sodalive.message.QMessage.message
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Lock
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.data.repository.query.Param
|
||||
import org.springframework.stereotype.Repository
|
||||
import javax.persistence.LockModeType
|
||||
|
||||
@Repository
|
||||
interface MemberRepository : JpaRepository<Member, Long>, MemberQueryRepository {
|
||||
@@ -27,6 +31,10 @@ interface MemberRepository : JpaRepository<Member, Long>, MemberQueryRepository
|
||||
fun findByKakaoId(kakaoId: Long): Member?
|
||||
fun findByAppleId(appleId: String): Member?
|
||||
fun findByLineId(lineId: String): Member?
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select m from Member m where m.id = :memberId")
|
||||
fun findByIdForUpdate(@Param("memberId") memberId: Long): Member?
|
||||
}
|
||||
|
||||
interface MemberQueryRepository {
|
||||
|
||||
@@ -17,11 +17,11 @@ import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||
import kr.co.vividnext.sodalive.jwt.TokenProvider
|
||||
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||
import kr.co.vividnext.sodalive.member.auth.AuthRepository
|
||||
import kr.co.vividnext.sodalive.member.block.BlockMember
|
||||
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
|
||||
import kr.co.vividnext.sodalive.member.block.GetBlockedMemberListResponse
|
||||
import kr.co.vividnext.sodalive.member.block.MemberBlockRequest
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowingRepository
|
||||
import kr.co.vividnext.sodalive.member.info.GetMemberInfoResponse
|
||||
@@ -82,7 +82,6 @@ class MemberService(
|
||||
private val stipulationAgreeRepository: StipulationAgreeRepository,
|
||||
private val creatorFollowingRepository: CreatorFollowingRepository,
|
||||
private val blockMemberRepository: BlockMemberRepository,
|
||||
private val authRepository: AuthRepository,
|
||||
private val signOutRepository: SignOutRepository,
|
||||
private val nicknameChangeLogRepository: NicknameChangeLogRepository,
|
||||
private val memberTagRepository: MemberTagRepository,
|
||||
@@ -106,6 +105,7 @@ class MemberService(
|
||||
private val messageSource: SodaMessageSource,
|
||||
private val langContext: LangContext,
|
||||
private val countryContext: CountryContext,
|
||||
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val cacheManager: CacheManager,
|
||||
@@ -120,6 +120,8 @@ class MemberService(
|
||||
private val tokenLocks: MutableMap<Long, ReentrantReadWriteLock> = mutableMapOf()
|
||||
|
||||
private val recommendLiveCacheKeyPrefix = "getRecommendLive:"
|
||||
private val recommendLiveCacheKeySuffixFalse = ":false"
|
||||
private val recommendLiveCacheKeySuffixTrue = ":true"
|
||||
private val latestFinishedLiveCacheKeyPrefix = "getLatestFinishedLive:"
|
||||
|
||||
@Transactional
|
||||
@@ -154,6 +156,7 @@ class MemberService(
|
||||
}
|
||||
|
||||
repository.save(member)
|
||||
memberContentPreferenceService.initializeDefaultPreference(member)
|
||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||
|
||||
if (request.pushToken != null) {
|
||||
@@ -192,6 +195,7 @@ class MemberService(
|
||||
duplicateCheckNickname(request.nickname)
|
||||
|
||||
val member = createMember(request)
|
||||
memberContentPreferenceService.initializeDefaultPreference(member)
|
||||
member.profileImage = uploadProfileImage(profileImage = profileImage, memberId = member.id!!)
|
||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||
|
||||
@@ -217,6 +221,8 @@ class MemberService(
|
||||
}
|
||||
|
||||
fun getMemberInfo(member: Member, container: String): GetMemberInfoResponse {
|
||||
val preference = memberContentPreferenceService.getStoredPreference(member)
|
||||
|
||||
val gender = if (member.auth != null) {
|
||||
if (member.auth!!.gender == 1) {
|
||||
messageSource.getMessage("member.gender.male", langContext.lang)
|
||||
@@ -250,7 +256,10 @@ class MemberService(
|
||||
messageNotice = member.notification?.message,
|
||||
followingChannelLiveNotice = member.notification?.live,
|
||||
followingChannelUploadContentNotice = member.notification?.uploadContent,
|
||||
auditionNotice = member.notification?.audition
|
||||
auditionNotice = member.notification?.audition,
|
||||
countryCode = preference.countryCode,
|
||||
isAdultContentVisible = preference.isAdultContentVisible,
|
||||
contentType = preference.contentType
|
||||
)
|
||||
}
|
||||
|
||||
@@ -840,7 +849,11 @@ class MemberService(
|
||||
}
|
||||
|
||||
private fun evictRecommendLiveCache(memberId: Long) {
|
||||
cacheManager.getCache("cache_ttl_3_hours")?.evict(recommendLiveCacheKeyPrefix + memberId)
|
||||
val cache = cacheManager.getCache("cache_ttl_3_hours") ?: return
|
||||
|
||||
cache.evict(recommendLiveCacheKeyPrefix + memberId + recommendLiveCacheKeySuffixFalse)
|
||||
cache.evict(recommendLiveCacheKeyPrefix + memberId + recommendLiveCacheKeySuffixTrue)
|
||||
cache.evict(recommendLiveCacheKeyPrefix + memberId)
|
||||
}
|
||||
|
||||
private fun evictLatestFinishedLiveCache(memberId: Long) {
|
||||
@@ -910,6 +923,7 @@ class MemberService(
|
||||
}
|
||||
|
||||
repository.save(member)
|
||||
memberContentPreferenceService.initializeDefaultPreference(member)
|
||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||
|
||||
if (pushToken != null) {
|
||||
@@ -967,6 +981,7 @@ class MemberService(
|
||||
}
|
||||
|
||||
repository.save(member)
|
||||
memberContentPreferenceService.initializeDefaultPreference(member)
|
||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||
|
||||
if (pushToken != null) {
|
||||
@@ -1024,6 +1039,7 @@ class MemberService(
|
||||
}
|
||||
|
||||
repository.save(member)
|
||||
memberContentPreferenceService.initializeDefaultPreference(member)
|
||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||
|
||||
if (pushToken != null) {
|
||||
@@ -1081,6 +1097,7 @@ class MemberService(
|
||||
}
|
||||
|
||||
repository.save(member)
|
||||
memberContentPreferenceService.initializeDefaultPreference(member)
|
||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||
|
||||
if (pushToken != null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.member.auth
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
||||
import kr.co.vividnext.sodalive.useraction.UserActionService
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
@@ -15,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController
|
||||
@RequestMapping("/auth")
|
||||
class AuthController(
|
||||
private val service: AuthService,
|
||||
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||
private val userActionService: UserActionService
|
||||
) {
|
||||
@PostMapping
|
||||
@@ -32,6 +34,7 @@ class AuthController(
|
||||
}
|
||||
|
||||
val authResponse = service.authenticate(authenticateData, member.id!!)
|
||||
memberContentPreferenceService.markAdultVisibleAfterAuthVerify(member.id!!)
|
||||
|
||||
try {
|
||||
userActionService.recordAction(
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import java.time.LocalDateTime
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.EnumType
|
||||
import javax.persistence.Enumerated
|
||||
import javax.persistence.FetchType
|
||||
import javax.persistence.JoinColumn
|
||||
import javax.persistence.OneToOne
|
||||
|
||||
@Entity
|
||||
class MemberContentPreference(
|
||||
@Column(nullable = false)
|
||||
var isAdultContentVisible: Boolean = false,
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
var contentType: ContentType = ContentType.ALL,
|
||||
|
||||
@Column(nullable = false)
|
||||
var adultContentVisibilityChangedAt: LocalDateTime = LocalDateTime.now(),
|
||||
|
||||
@Column(nullable = false)
|
||||
var contentTypeChangedAt: LocalDateTime = LocalDateTime.now()
|
||||
) : BaseEntity() {
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "member_id", nullable = false, unique = true)
|
||||
var member: Member? = null
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
|
||||
private val FORCED_KR_MEMBER_IDS = setOf(16L, 17L)
|
||||
private val FORCED_JP_MEMBER_IDS = setOf(2L, 29721L, 32050L, 40850L)
|
||||
|
||||
fun resolveCountryCodeWithForcedMapping(member: Member, requestCountryCode: String?): String {
|
||||
val memberId = member.id
|
||||
if (memberId != null && FORCED_KR_MEMBER_IDS.contains(memberId)) {
|
||||
return "KR"
|
||||
}
|
||||
|
||||
if (memberId != null && FORCED_JP_MEMBER_IDS.contains(memberId)) {
|
||||
return "JP"
|
||||
}
|
||||
|
||||
return requestCountryCode
|
||||
?.trim()
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.uppercase()
|
||||
?: "KR"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import org.springframework.web.context.request.RequestContextHolder
|
||||
import org.springframework.web.context.request.ServletRequestAttributes
|
||||
|
||||
fun resolveCountryCodeByPolicy(member: Member): String {
|
||||
val requestAttributes = RequestContextHolder.getRequestAttributes() as? ServletRequestAttributes
|
||||
val requestCountryCode = requestAttributes?.request?.getHeader("CloudFront-Viewer-Country")
|
||||
return resolveCountryCodeWithForcedMapping(member, requestCountryCode)
|
||||
}
|
||||
|
||||
fun isAdultVisibleByPolicy(member: Member, isAdultContentVisible: Boolean): Boolean {
|
||||
return if (resolveCountryCodeByPolicy(member) == "KR") {
|
||||
member.auth != null && isAdultContentVisible
|
||||
} else {
|
||||
isAdultContentVisible
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Lock
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.data.repository.query.Param
|
||||
import org.springframework.stereotype.Repository
|
||||
import javax.persistence.LockModeType
|
||||
|
||||
@Repository
|
||||
interface MemberContentPreferenceRepository : JpaRepository<MemberContentPreference, Long> {
|
||||
fun findByMemberId(memberId: Long): MemberContentPreference?
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select mcp from MemberContentPreference mcp where mcp.member.id = :memberId")
|
||||
fun findByMemberIdForUpdate(@Param("memberId") memberId: Long): MemberContentPreference?
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.common.CountryContext
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import org.springframework.cache.CacheManager
|
||||
import org.springframework.dao.DataIntegrityViolationException
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Propagation
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionSynchronization
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
class MemberContentPreferenceService(
|
||||
private val repository: MemberContentPreferenceRepository,
|
||||
private val memberRepository: MemberRepository,
|
||||
private val countryContext: CountryContext,
|
||||
private val cacheManager: CacheManager
|
||||
) {
|
||||
companion object {
|
||||
private const val RECOMMEND_LIVE_CACHE_NAME = "cache_ttl_3_hours"
|
||||
private const val RECOMMEND_LIVE_CACHE_KEY_PREFIX = "getRecommendLive:"
|
||||
private const val RECOMMEND_LIVE_CACHE_KEY_SUFFIX_FALSE = ":false"
|
||||
private const val RECOMMEND_LIVE_CACHE_KEY_SUFFIX_TRUE = ":true"
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun initializeDefaultPreference(member: Member): MemberContentPreference {
|
||||
val memberId = requireMemberId(member)
|
||||
|
||||
val existingPreference = repository.findByMemberId(memberId)
|
||||
if (existingPreference != null) {
|
||||
return existingPreference
|
||||
}
|
||||
|
||||
memberRepository.findByIdForUpdate(memberId)
|
||||
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||
|
||||
val lockedPreference = repository.findByMemberIdForUpdate(memberId)
|
||||
if (lockedPreference != null) {
|
||||
return lockedPreference
|
||||
}
|
||||
|
||||
val now = LocalDateTime.now()
|
||||
val preference = MemberContentPreference(
|
||||
isAdultContentVisible = false,
|
||||
contentType = ContentType.ALL,
|
||||
adultContentVisibilityChangedAt = now,
|
||||
contentTypeChangedAt = now
|
||||
)
|
||||
preference.member = member
|
||||
|
||||
return try {
|
||||
repository.saveAndFlush(preference)
|
||||
} catch (e: DataIntegrityViolationException) {
|
||||
repository.findByMemberIdForUpdate(memberId) ?: throw e
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
fun resolveForQuery(
|
||||
member: Member,
|
||||
isAdultContentVisible: Boolean?,
|
||||
contentType: ContentType?
|
||||
): ViewerContentPreference {
|
||||
val preference = initializeDefaultPreference(member)
|
||||
val countryCode = resolveCountryCode(member)
|
||||
|
||||
val hasChanged = if (isAdultContentVisible != null || contentType != null) {
|
||||
applyRequestValues(
|
||||
preference = preference,
|
||||
member = member,
|
||||
countryCode = countryCode,
|
||||
isAdultContentVisible = isAdultContentVisible,
|
||||
contentType = contentType
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
evictRecommendLiveCacheAfterCommit(requireMemberId(member))
|
||||
}
|
||||
|
||||
return toViewerContentPreference(
|
||||
countryCode = countryCode,
|
||||
member = member,
|
||||
preference = preference
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun updatePreference(
|
||||
member: Member,
|
||||
isAdultContentVisible: Boolean?,
|
||||
contentType: ContentType?
|
||||
): ViewerContentPreference {
|
||||
if (isAdultContentVisible == null && contentType == null) {
|
||||
throw SodaException(messageKey = "common.error.invalid_request")
|
||||
}
|
||||
|
||||
val preference = initializeDefaultPreference(member)
|
||||
val countryCode = resolveCountryCode(member)
|
||||
|
||||
val hasChanged = applyRequestValues(
|
||||
preference = preference,
|
||||
member = member,
|
||||
countryCode = countryCode,
|
||||
isAdultContentVisible = isAdultContentVisible,
|
||||
contentType = contentType
|
||||
)
|
||||
|
||||
if (hasChanged) {
|
||||
evictRecommendLiveCacheAfterCommit(requireMemberId(member))
|
||||
}
|
||||
|
||||
return toViewerContentPreference(
|
||||
countryCode = countryCode,
|
||||
member = member,
|
||||
preference = preference
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun markAdultVisibleAfterAuthVerify(memberId: Long) {
|
||||
val member = memberRepository.findByIdOrNull(memberId)
|
||||
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||
|
||||
val preference = initializeDefaultPreference(member)
|
||||
if (!preference.isAdultContentVisible) {
|
||||
preference.isAdultContentVisible = true
|
||||
preference.adultContentVisibilityChangedAt = LocalDateTime.now()
|
||||
evictRecommendLiveCacheAfterCommit(memberId)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
fun getStoredPreference(member: Member): ViewerContentPreference {
|
||||
val preference = initializeDefaultPreference(member)
|
||||
val countryCode = resolveCountryCode(member)
|
||||
|
||||
return toViewerContentPreference(
|
||||
countryCode = countryCode,
|
||||
member = member,
|
||||
preference = preference
|
||||
)
|
||||
}
|
||||
|
||||
fun resolveCountryCode(member: Member): String {
|
||||
requireMemberId(member)
|
||||
return resolveCountryCodeWithForcedMapping(member, countryContext.countryCode)
|
||||
}
|
||||
|
||||
fun calculateIsAdultForQuery(
|
||||
member: Member,
|
||||
countryCode: String,
|
||||
isAdultContentVisible: Boolean
|
||||
): Boolean {
|
||||
return if (countryCode == "KR") {
|
||||
isAdultContentVisible && member.auth != null
|
||||
} else {
|
||||
isAdultContentVisible
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyRequestValues(
|
||||
preference: MemberContentPreference,
|
||||
member: Member,
|
||||
countryCode: String,
|
||||
isAdultContentVisible: Boolean?,
|
||||
contentType: ContentType?
|
||||
): Boolean {
|
||||
val shouldApplyByCountryPolicy = countryCode != "KR" || member.auth != null
|
||||
if (!shouldApplyByCountryPolicy) {
|
||||
return false
|
||||
}
|
||||
|
||||
val now = LocalDateTime.now()
|
||||
var hasChanged = false
|
||||
if (
|
||||
isAdultContentVisible != null &&
|
||||
preference.isAdultContentVisible != isAdultContentVisible
|
||||
) {
|
||||
preference.isAdultContentVisible = isAdultContentVisible
|
||||
preference.adultContentVisibilityChangedAt = now
|
||||
hasChanged = true
|
||||
}
|
||||
|
||||
if (contentType != null && preference.contentType != contentType) {
|
||||
preference.contentType = contentType
|
||||
preference.contentTypeChangedAt = now
|
||||
hasChanged = true
|
||||
}
|
||||
|
||||
return hasChanged
|
||||
}
|
||||
|
||||
private fun evictRecommendLiveCacheAfterCommit(memberId: Long) {
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
object : TransactionSynchronization {
|
||||
override fun afterCommit() {
|
||||
evictRecommendLiveCache(memberId)
|
||||
}
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
evictRecommendLiveCache(memberId)
|
||||
}
|
||||
|
||||
private fun evictRecommendLiveCache(memberId: Long) {
|
||||
val cache = cacheManager.getCache(RECOMMEND_LIVE_CACHE_NAME) ?: return
|
||||
|
||||
cache.evict(RECOMMEND_LIVE_CACHE_KEY_PREFIX + memberId + RECOMMEND_LIVE_CACHE_KEY_SUFFIX_FALSE)
|
||||
cache.evict(RECOMMEND_LIVE_CACHE_KEY_PREFIX + memberId + RECOMMEND_LIVE_CACHE_KEY_SUFFIX_TRUE)
|
||||
cache.evict(RECOMMEND_LIVE_CACHE_KEY_PREFIX + memberId)
|
||||
}
|
||||
|
||||
private fun toViewerContentPreference(
|
||||
countryCode: String,
|
||||
member: Member,
|
||||
preference: MemberContentPreference
|
||||
): ViewerContentPreference {
|
||||
return ViewerContentPreference(
|
||||
countryCode = countryCode,
|
||||
isAdultContentVisible = preference.isAdultContentVisible,
|
||||
contentType = preference.contentType,
|
||||
isAdult = calculateIsAdultForQuery(
|
||||
member = member,
|
||||
countryCode = countryCode,
|
||||
isAdultContentVisible = preference.isAdultContentVisible
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun requireMemberId(member: Member): Long {
|
||||
return member.id ?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
|
||||
data class UpdateMemberContentPreferenceRequest(
|
||||
val isAdultContentVisible: Boolean? = null,
|
||||
val contentType: ContentType? = null
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
|
||||
data class UpdateMemberContentPreferenceResponse(
|
||||
val isAdultContentVisible: Boolean,
|
||||
val contentType: ContentType
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package kr.co.vividnext.sodalive.member.contentpreference
|
||||
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
|
||||
data class ViewerContentPreference(
|
||||
val countryCode: String,
|
||||
val isAdultContentVisible: Boolean,
|
||||
val contentType: ContentType,
|
||||
val isAdult: Boolean
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
package kr.co.vividnext.sodalive.member.info
|
||||
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
|
||||
data class GetMemberInfoResponse(
|
||||
@@ -13,5 +14,8 @@ data class GetMemberInfoResponse(
|
||||
val messageNotice: Boolean?,
|
||||
val followingChannelLiveNotice: Boolean?,
|
||||
val followingChannelUploadContentNotice: Boolean?,
|
||||
val auditionNotice: Boolean?
|
||||
val auditionNotice: Boolean?,
|
||||
val countryCode: String,
|
||||
val isAdultContentVisible: Boolean,
|
||||
val contentType: ContentType
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user