feat(recommend): 콘텐츠 조회 이력 서비스를 추가한다

This commit is contained in:
2026-05-31 18:19:28 +09:00
parent 2ef8e8e489
commit 43179de810
2 changed files with 92 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
package kr.co.vividnext.sodalive.v2.recommend.application
import kr.co.vividnext.sodalive.v2.recommend.port.out.CreatorContentViewHistoryPort
import kr.co.vividnext.sodalive.v2.recommend.port.out.CreatorContentViewHistoryRecord
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
@Service
class CreatorContentViewHistoryService(
private val port: CreatorContentViewHistoryPort
) {
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun recordView(memberId: Long?, contentId: Long, viewedAt: LocalDateTime = LocalDateTime.now()) {
if (memberId == null) return
val genreId = port.findGenreIdByContentId(contentId) ?: return
port.save(
CreatorContentViewHistoryRecord(
memberId = memberId,
contentId = contentId,
genreId = genreId,
viewedAt = viewedAt
)
)
}
}

View File

@@ -0,0 +1,64 @@
package kr.co.vividnext.sodalive.v2.recommend.application
import kr.co.vividnext.sodalive.v2.recommend.port.out.CreatorContentViewHistoryPort
import kr.co.vividnext.sodalive.v2.recommend.port.out.CreatorContentViewHistoryRecord
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.time.LocalDateTime
class CreatorContentViewHistoryServiceTest {
private val port = FakeCreatorContentViewHistoryPort()
private val service = CreatorContentViewHistoryService(port)
@Test
@DisplayName("인증 회원의 콘텐츠 상세 진입 시 memberId/contentId/genreId/viewedAt을 저장한다")
fun shouldRecordAuthenticatedMemberContentViewWithGenreAndViewedAt() {
val viewedAt = LocalDateTime.of(2026, 5, 31, 10, 0)
port.genreIdByContentId[20L] = 30L
service.recordView(memberId = 10L, contentId = 20L, viewedAt = viewedAt)
assertEquals(
listOf(
CreatorContentViewHistoryRecord(
memberId = 10L,
contentId = 20L,
genreId = 30L,
viewedAt = viewedAt
)
),
port.savedRecords
)
}
@Test
@DisplayName("비회원 콘텐츠 상세 진입은 조회 이력을 저장하지 않는다")
fun shouldNotRecordAnonymousContentView() {
service.recordView(memberId = null, contentId = 20L)
assertTrue(port.savedRecords.isEmpty())
}
@Test
@DisplayName("콘텐츠의 활성 장르를 찾지 못하면 조회 이력을 저장하지 않는다")
fun shouldNotRecordWhenContentGenreDoesNotExist() {
service.recordView(memberId = 10L, contentId = 20L)
assertTrue(port.savedRecords.isEmpty())
}
private class FakeCreatorContentViewHistoryPort : CreatorContentViewHistoryPort {
val genreIdByContentId = mutableMapOf<Long, Long>()
val savedRecords = mutableListOf<CreatorContentViewHistoryRecord>()
override fun findGenreIdByContentId(contentId: Long): Long? {
return genreIdByContentId[contentId]
}
override fun save(record: CreatorContentViewHistoryRecord) {
savedRecords.add(record)
}
}
}