feat(recommend): 추천 팔로우 성공 로그를 커밋 후 기록한다
This commit is contained in:
@@ -5,16 +5,27 @@ import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowingRepository
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionSynchronization
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
|
||||
@Service
|
||||
class RecommendedCreatorFollowService(
|
||||
private val memberRepository: MemberRepository,
|
||||
private val creatorFollowingRepository: CreatorFollowingRepository
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
@Transactional
|
||||
fun followCreators(member: Member, creatorIds: List<Long>) {
|
||||
val startedAt = System.currentTimeMillis()
|
||||
var savedCount = 0
|
||||
var reactivatedCount = 0
|
||||
var skippedCount = 0
|
||||
|
||||
runCatching {
|
||||
val distinctCreatorIds = creatorIds.distinct()
|
||||
val creatorById = distinctCreatorIds
|
||||
.filter { it != member.id }
|
||||
@@ -25,6 +36,7 @@ class RecommendedCreatorFollowService(
|
||||
|
||||
distinctCreatorIds.forEach { creatorId ->
|
||||
if (creatorId == member.id) {
|
||||
skippedCount += 1
|
||||
return@forEach
|
||||
}
|
||||
|
||||
@@ -36,6 +48,9 @@ class RecommendedCreatorFollowService(
|
||||
if (!existingFollowing.isActive) {
|
||||
existingFollowing.isNotify = true
|
||||
existingFollowing.isActive = true
|
||||
reactivatedCount += 1
|
||||
} else {
|
||||
skippedCount += 1
|
||||
}
|
||||
return@forEach
|
||||
}
|
||||
@@ -46,6 +61,47 @@ class RecommendedCreatorFollowService(
|
||||
creator = creatorById.getValue(creatorId)
|
||||
}
|
||||
)
|
||||
savedCount += 1
|
||||
}
|
||||
distinctCreatorIds.size
|
||||
}.onSuccess { distinctCount ->
|
||||
afterCommit {
|
||||
log.info(
|
||||
"event=recommended_creator_follow_success " +
|
||||
"memberId={} requestedCount={} distinctCount={} savedCount={} " +
|
||||
"reactivatedCount={} skippedCount={} elapsedMs={}",
|
||||
member.id,
|
||||
creatorIds.size,
|
||||
distinctCount,
|
||||
savedCount,
|
||||
reactivatedCount,
|
||||
skippedCount,
|
||||
System.currentTimeMillis() - startedAt
|
||||
)
|
||||
}
|
||||
}.onFailure { ex ->
|
||||
log.warn(
|
||||
"event=recommended_creator_follow_failure memberId={} requestedCount={} distinctCount={} elapsedMs={} error={}",
|
||||
member.id,
|
||||
creatorIds.size,
|
||||
creatorIds.distinct().size,
|
||||
System.currentTimeMillis() - startedAt,
|
||||
ex.message,
|
||||
ex
|
||||
)
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
private fun afterCommit(action: () -> Unit) {
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
action()
|
||||
return
|
||||
}
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
object : TransactionSynchronization {
|
||||
override fun afterCommit() = action()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,21 @@ import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.system.CapturedOutput
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension
|
||||
import org.springframework.dao.DataIntegrityViolationException
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
import javax.persistence.EntityManager
|
||||
|
||||
@SpringBootTest
|
||||
@Transactional
|
||||
@ContextConfiguration(initializers = [EmbeddedRedisInitializer::class])
|
||||
@ExtendWith(OutputCaptureExtension::class)
|
||||
class RecommendedCreatorFollowServiceTest @Autowired constructor(
|
||||
private val service: RecommendedCreatorFollowService,
|
||||
private val memberRepository: MemberRepository,
|
||||
@@ -32,7 +37,7 @@ class RecommendedCreatorFollowServiceTest @Autowired constructor(
|
||||
) {
|
||||
@Test
|
||||
@DisplayName("신규 크리에이터만 팔로우 저장하고 이미 팔로우/본인 id는 서버 내부에서 제외한다")
|
||||
fun shouldFollowOnlyNewCreatorsAndSkipExistingAndSelf() {
|
||||
fun shouldFollowOnlyNewCreatorsAndSkipExistingAndSelf(output: CapturedOutput) {
|
||||
val member = saveMember("viewer", MemberRole.USER)
|
||||
val newCreator = saveMember("new-creator", MemberRole.CREATOR)
|
||||
val followedCreator = saveMember("followed-creator", MemberRole.CREATOR)
|
||||
@@ -40,16 +45,39 @@ class RecommendedCreatorFollowServiceTest @Autowired constructor(
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
|
||||
val beforeCount = TransactionSynchronizationManager.getSynchronizations().size
|
||||
service.followCreators(
|
||||
member = member,
|
||||
creatorIds = listOf(newCreator.id!!, followedCreator.id!!, member.id!!)
|
||||
)
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
TransactionSynchronizationManager.getSynchronizations().drop(beforeCount).forEach { it.afterCommit() }
|
||||
|
||||
assertNotNull(creatorFollowingRepository.findByCreatorIdAndMemberId(newCreator.id!!, member.id!!))
|
||||
assertNotNull(creatorFollowingRepository.findByCreatorIdAndMemberId(followedCreator.id!!, member.id!!))
|
||||
assertEquals(2, creatorFollowingRepository.findAll().size)
|
||||
assertTrue(output.out.contains("event=recommended_creator_follow_success"))
|
||||
assertTrue(output.out.contains("requestedCount=3"))
|
||||
assertTrue(output.out.contains("savedCount=1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("추천 크리에이터 동시 팔로우 성공 로그는 트랜잭션 커밋 후 기록한다")
|
||||
fun shouldLogFollowSuccessAfterTransactionCommit(output: CapturedOutput) {
|
||||
val member = saveMember("after-commit-viewer", MemberRole.USER)
|
||||
val creator = saveMember("after-commit-creator", MemberRole.CREATOR)
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
|
||||
val beforeCount = TransactionSynchronizationManager.getSynchronizations().size
|
||||
|
||||
service.followCreators(member = member, creatorIds = listOf(creator.id!!))
|
||||
|
||||
assertEquals(false, output.out.contains("event=recommended_creator_follow_success"))
|
||||
TransactionSynchronizationManager.getSynchronizations().drop(beforeCount).forEach { it.afterCommit() }
|
||||
|
||||
assertTrue(output.out.contains("event=recommended_creator_follow_success"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,7 +147,7 @@ class RecommendedCreatorFollowServiceTest @Autowired constructor(
|
||||
|
||||
@Test
|
||||
@DisplayName("존재하지 않는 id가 하나라도 포함되면 전체 실패하고 신규 저장하지 않는다")
|
||||
fun shouldFailAllAndSaveNothingWhenAnyCreatorIdDoesNotExist() {
|
||||
fun shouldFailAllAndSaveNothingWhenAnyCreatorIdDoesNotExist(output: CapturedOutput) {
|
||||
val member = saveMember("viewer", MemberRole.USER)
|
||||
val validCreator = saveMember("valid-creator", MemberRole.CREATOR)
|
||||
entityManager.flush()
|
||||
@@ -131,6 +159,7 @@ class RecommendedCreatorFollowServiceTest @Autowired constructor(
|
||||
|
||||
assertEquals("member.validation.creator_not_found", exception.messageKey)
|
||||
assertEquals(0, creatorFollowingRepository.findAll().size)
|
||||
assertTrue(output.out.contains("event=recommended_creator_follow_failure"))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user