feat(creator): 채널 홈 오디오 소장 필드를 추가한다

This commit is contained in:
2026-06-17 16:06:08 +09:00
parent 7e6ac283cb
commit fe19be90f9
8 changed files with 66 additions and 17 deletions

View File

@@ -155,7 +155,8 @@ class DefaultCreatorChannelHomeQueryRepository(
override fun findLatestAudioContent( override fun findLatestAudioContent(
creatorId: Long, creatorId: Long,
now: LocalDateTime, now: LocalDateTime,
canViewAdultContent: Boolean canViewAdultContent: Boolean,
viewerId: Long?
): CreatorChannelAudioContentRecord? { ): CreatorChannelAudioContentRecord? {
val row = findAudioContentRows(creatorId, now, null, canViewAdultContent, 1).firstOrNull() ?: return null val row = findAudioContentRows(creatorId, now, null, canViewAdultContent, 1).firstOrNull() ?: return null
return row.toAudioRecord( return row.toAudioRecord(
@@ -351,6 +352,7 @@ class DefaultCreatorChannelHomeQueryRepository(
now: LocalDateTime, now: LocalDateTime,
latestAudioContentId: Long?, latestAudioContentId: Long?,
canViewAdultContent: Boolean, canViewAdultContent: Boolean,
viewerId: Long?,
limit: Int limit: Int
): List<CreatorChannelAudioContentRecord> { ): List<CreatorChannelAudioContentRecord> {
val rows = findAudioContentRows(creatorId, now, latestAudioContentId, canViewAdultContent, limit) val rows = findAudioContentRows(creatorId, now, latestAudioContentId, canViewAdultContent, limit)
@@ -564,7 +566,9 @@ class DefaultCreatorChannelHomeQueryRepository(
isFirstContent = firstContentId == audioContentId, isFirstContent = firstContentId == audioContentId,
publishedAt = get(audioContent.releaseDate)!!, publishedAt = get(audioContent.releaseDate)!!,
seriesName = seriesSummary?.title, seriesName = seriesSummary?.title,
isOriginalSeries = seriesSummary?.isOriginal isOriginalSeries = seriesSummary?.isOriginal,
isOwned = false,
isRented = false
) )
} }

View File

@@ -72,14 +72,15 @@ class CreatorChannelHomeQueryService(
val isViewerCreator = viewerId == creatorId val isViewerCreator = viewerId == creatorId
val effectiveViewerGender = viewer.effectiveGender() val effectiveViewerGender = viewer.effectiveGender()
val latestAudioContent = queryPort val latestAudioContent = queryPort
.findLatestAudioContent(creatorId, now, canViewAdultContent) .findLatestAudioContent(creatorId, now, canViewAdultContent, viewerId)
?.toDomain() ?.toDomain()
val audioContents = queryPolicy.excludeLatestAudioContent( val audioContents = queryPolicy.excludeLatestAudioContent(
queryPort.findAudioContents( queryPort.findAudioContents(
creatorId = creatorId, creatorId = creatorId,
now = now, now = now,
latestAudioContentId = latestAudioContent?.audioContentId, latestAudioContentId = latestAudioContent?.audioContentId,
canViewAdultContent = canViewAdultContent canViewAdultContent = canViewAdultContent,
viewerId = viewerId
).map { it.toDomain() }, ).map { it.toDomain() },
latestAudioContent?.audioContentId latestAudioContent?.audioContentId
) )
@@ -179,7 +180,9 @@ class CreatorChannelHomeQueryService(
isFirstContent = isFirstContent, isFirstContent = isFirstContent,
publishedAt = publishedAt, publishedAt = publishedAt,
seriesName = seriesName, seriesName = seriesName,
isOriginalSeries = isOriginalSeries isOriginalSeries = isOriginalSeries,
isOwned = isOwned,
isRented = isRented
) )
private fun CreatorChannelDonationRecord.toDomain() = CreatorChannelDonation( private fun CreatorChannelDonationRecord.toDomain() = CreatorChannelDonation(

View File

@@ -51,7 +51,9 @@ data class CreatorChannelAudioContent(
val isFirstContent: Boolean, val isFirstContent: Boolean,
val publishedAt: LocalDateTime, val publishedAt: LocalDateTime,
val seriesName: String?, val seriesName: String?,
val isOriginalSeries: Boolean? val isOriginalSeries: Boolean?,
val isOwned: Boolean,
val isRented: Boolean
) )
data class CreatorChannelDonation( data class CreatorChannelDonation(

View File

@@ -122,7 +122,11 @@ data class CreatorChannelAudioContentResponse(
val isFirstContent: Boolean, val isFirstContent: Boolean,
val seriesName: String?, val seriesName: String?,
@JsonProperty("isOriginalSeries") @JsonProperty("isOriginalSeries")
val isOriginalSeries: Boolean? val isOriginalSeries: Boolean?,
@JsonProperty("isOwned")
val isOwned: Boolean,
@JsonProperty("isRented")
val isRented: Boolean
) { ) {
companion object { companion object {
fun from(audioContent: CreatorChannelAudioContent): CreatorChannelAudioContentResponse { fun from(audioContent: CreatorChannelAudioContent): CreatorChannelAudioContentResponse {
@@ -136,7 +140,9 @@ data class CreatorChannelAudioContentResponse(
isPointAvailable = audioContent.isPointAvailable, isPointAvailable = audioContent.isPointAvailable,
isFirstContent = audioContent.isFirstContent, isFirstContent = audioContent.isFirstContent,
seriesName = audioContent.seriesName, seriesName = audioContent.seriesName,
isOriginalSeries = audioContent.isOriginalSeries isOriginalSeries = audioContent.isOriginalSeries,
isOwned = audioContent.isOwned,
isRented = audioContent.isRented
) )
} }
} }

View File

@@ -23,7 +23,8 @@ interface CreatorChannelHomeQueryPort {
fun findLatestAudioContent( fun findLatestAudioContent(
creatorId: Long, creatorId: Long,
now: LocalDateTime, now: LocalDateTime,
canViewAdultContent: Boolean canViewAdultContent: Boolean,
viewerId: Long? = null
): CreatorChannelAudioContentRecord? ): CreatorChannelAudioContentRecord?
fun findChannelDonations( fun findChannelDonations(
@@ -56,6 +57,7 @@ interface CreatorChannelHomeQueryPort {
now: LocalDateTime, now: LocalDateTime,
latestAudioContentId: Long?, latestAudioContentId: Long?,
canViewAdultContent: Boolean, canViewAdultContent: Boolean,
viewerId: Long? = null,
limit: Int = 9 limit: Int = 9
): List<CreatorChannelAudioContentRecord> ): List<CreatorChannelAudioContentRecord>
@@ -109,7 +111,9 @@ data class CreatorChannelAudioContentRecord(
val isFirstContent: Boolean, val isFirstContent: Boolean,
val publishedAt: LocalDateTime, val publishedAt: LocalDateTime,
val seriesName: String?, val seriesName: String?,
val isOriginalSeries: Boolean? val isOriginalSeries: Boolean?,
val isOwned: Boolean,
val isRented: Boolean
) )
data class CreatorChannelDonationRecord( data class CreatorChannelDonationRecord(

View File

@@ -122,6 +122,10 @@ class CreatorChannelHomeControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.latestAudioContent.isPointAvailable").value(true)) .andExpect(jsonPath("$.data.latestAudioContent.isPointAvailable").value(true))
.andExpect(jsonPath("$.data.latestAudioContent.isFirstContent").value(true)) .andExpect(jsonPath("$.data.latestAudioContent.isFirstContent").value(true))
.andExpect(jsonPath("$.data.latestAudioContent.isOriginalSeries").value(true)) .andExpect(jsonPath("$.data.latestAudioContent.isOriginalSeries").value(true))
.andExpect(jsonPath("$.data.latestAudioContent.isOwned").value(true))
.andExpect(jsonPath("$.data.latestAudioContent.isRented").value(false))
.andExpect(jsonPath("$.data.audioContents[0].isOwned").value(false))
.andExpect(jsonPath("$.data.audioContents[0].isRented").value(true))
.andExpect(jsonPath("$.data.currentLive.isAdult").value(true)) .andExpect(jsonPath("$.data.currentLive.isAdult").value(true))
.andExpect(jsonPath("$.data.schedules[0].isAdult").doesNotExist()) .andExpect(jsonPath("$.data.schedules[0].isAdult").doesNotExist())
.andExpect(jsonPath("$.data.channelDonations[0].donationId").doesNotExist()) .andExpect(jsonPath("$.data.channelDonations[0].donationId").doesNotExist())
@@ -195,7 +199,9 @@ class CreatorChannelHomeControllerTest @Autowired constructor(
isFirstContent = true, isFirstContent = true,
publishedAt = LocalDateTime.of(2026, 6, 11, 1, 0), publishedAt = LocalDateTime.of(2026, 6, 11, 1, 0),
seriesName = "series", seriesName = "series",
isOriginalSeries = true isOriginalSeries = true,
isOwned = true,
isRented = false
), ),
channelDonations = listOf( channelDonations = listOf(
CreatorChannelDonation( CreatorChannelDonation(
@@ -228,7 +234,9 @@ class CreatorChannelHomeControllerTest @Autowired constructor(
isFirstContent = false, isFirstContent = false,
publishedAt = LocalDateTime.of(2026, 6, 10, 1, 0), publishedAt = LocalDateTime.of(2026, 6, 10, 1, 0),
seriesName = null, seriesName = null,
isOriginalSeries = null isOriginalSeries = null,
isOwned = false,
isRented = true
) )
), ),
series = listOf( series = listOf(

View File

@@ -68,7 +68,11 @@ class CreatorChannelHomeQueryServiceTest {
assertEquals("https://cdn.test/profile/creator.png", home.creator.profileImageUrl) assertEquals("https://cdn.test/profile/creator.png", home.creator.profileImageUrl)
assertEquals("https://cdn.test/live.png", home.currentLive?.coverImageUrl) assertEquals("https://cdn.test/live.png", home.currentLive?.coverImageUrl)
assertEquals("https://cdn.test/audio/latest.png", home.latestAudioContent?.imageUrl) assertEquals("https://cdn.test/audio/latest.png", home.latestAudioContent?.imageUrl)
assertTrue(home.latestAudioContent?.isOwned == true)
assertFalse(home.latestAudioContent?.isRented == true)
assertEquals(listOf(203L, 202L), home.audioContents.map { it.audioContentId }) assertEquals(listOf(203L, 202L), home.audioContents.map { it.audioContentId })
assertEquals(listOf(false, true), home.audioContents.map { it.isOwned })
assertEquals(listOf(true, false), home.audioContents.map { it.isRented })
assertEquals(listOf(402L, 401L, 404L), home.schedules.map { it.targetId }) assertEquals(listOf(402L, 401L, 404L), home.schedules.map { it.targetId })
assertFalse(home.schedules.any { it.isAdult }) assertFalse(home.schedules.any { it.isAdult })
assertEquals("https://cdn.test/profile/fan.png", home.channelDonations.first().profileImageUrl) assertEquals("https://cdn.test/profile/fan.png", home.channelDonations.first().profileImageUrl)
@@ -179,6 +183,8 @@ class CreatorChannelHomeQueryServiceTest {
assertEquals(home.creator.characterId, response.creator.characterId) assertEquals(home.creator.characterId, response.creator.characterId)
assertEquals(home.currentLive?.liveId, response.currentLive?.liveId) assertEquals(home.currentLive?.liveId, response.currentLive?.liveId)
assertEquals(home.latestAudioContent?.audioContentId, response.latestAudioContent?.audioContentId) assertEquals(home.latestAudioContent?.audioContentId, response.latestAudioContent?.audioContentId)
assertEquals(home.latestAudioContent?.isOwned, response.latestAudioContent?.isOwned)
assertEquals(home.latestAudioContent?.isRented, response.latestAudioContent?.isRented)
assertEquals(home.channelDonations.first().message, response.channelDonations.first().message) assertEquals(home.channelDonations.first().message, response.channelDonations.first().message)
assertEquals(home.notices.first().postId, response.notices.first().postId) assertEquals(home.notices.first().postId, response.notices.first().postId)
assertEquals(home.schedules.first().targetId, response.schedules.first().targetId) assertEquals(home.schedules.first().targetId, response.schedules.first().targetId)
@@ -217,6 +223,8 @@ class CreatorChannelHomeQueryServiceTest {
assertTrue(response.latestAudioContent?.isPointAvailable == true) assertTrue(response.latestAudioContent?.isPointAvailable == true)
assertTrue(response.latestAudioContent?.isFirstContent == true) assertTrue(response.latestAudioContent?.isFirstContent == true)
assertTrue(response.latestAudioContent?.isAdult == true) assertTrue(response.latestAudioContent?.isAdult == true)
assertTrue(response.latestAudioContent?.isOwned == true)
assertFalse(response.latestAudioContent?.isRented == true)
assertTrue(response.series.first().isOriginal) assertTrue(response.series.first().isOriginal)
assertNotNull(response.latestAudioContent?.isOriginalSeries) assertNotNull(response.latestAudioContent?.isOriginalSeries)
} }
@@ -239,6 +247,10 @@ class CreatorChannelHomeQueryServiceTest {
assertFalse(json["latestAudioContent"].has("firstContent")) assertFalse(json["latestAudioContent"].has("firstContent"))
assertTrue(json["latestAudioContent"]["isAdult"].asBoolean()) assertTrue(json["latestAudioContent"]["isAdult"].asBoolean())
assertFalse(json["latestAudioContent"].has("adult")) assertFalse(json["latestAudioContent"].has("adult"))
assertTrue(json["latestAudioContent"]["isOwned"].asBoolean())
assertFalse(json["latestAudioContent"].has("owned"))
assertFalse(json["latestAudioContent"]["isRented"].asBoolean())
assertFalse(json["latestAudioContent"].has("rented"))
assertTrue(json["series"][0]["isOriginal"].asBoolean()) assertTrue(json["series"][0]["isOriginal"].asBoolean())
assertFalse(json["series"][0].has("original")) assertFalse(json["series"][0].has("original"))
assertFalse(json["series"][0].has("published" + "DaysOfWeek")) assertFalse(json["series"][0].has("published" + "DaysOfWeek"))
@@ -297,7 +309,9 @@ class CreatorChannelHomeQueryServiceTest {
isFirstContent = true, isFirstContent = true,
publishedAt = LocalDateTime.of(2026, 6, 11, 1, 0), publishedAt = LocalDateTime.of(2026, 6, 11, 1, 0),
seriesName = "series", seriesName = "series",
isOriginalSeries = true isOriginalSeries = true,
isOwned = true,
isRented = false
), ),
channelDonations = listOf( channelDonations = listOf(
CreatorChannelDonation( CreatorChannelDonation(
@@ -330,7 +344,9 @@ class CreatorChannelHomeQueryServiceTest {
isFirstContent = false, isFirstContent = false,
publishedAt = LocalDateTime.of(2026, 6, 10, 1, 0), publishedAt = LocalDateTime.of(2026, 6, 10, 1, 0),
seriesName = null, seriesName = null,
isOriginalSeries = null isOriginalSeries = null,
isOwned = false,
isRented = true
) )
), ),
series = listOf( series = listOf(
@@ -484,7 +500,8 @@ private class FakeCreatorChannelHomeQueryPort : CreatorChannelHomeQueryPort {
override fun findLatestAudioContent( override fun findLatestAudioContent(
creatorId: Long, creatorId: Long,
now: LocalDateTime, now: LocalDateTime,
canViewAdultContent: Boolean canViewAdultContent: Boolean,
viewerId: Long?
): CreatorChannelAudioContentRecord? = audioContentRecord(201L, "audio/latest.png") ): CreatorChannelAudioContentRecord? = audioContentRecord(201L, "audio/latest.png")
override fun findChannelDonations( override fun findChannelDonations(
@@ -553,6 +570,7 @@ private class FakeCreatorChannelHomeQueryPort : CreatorChannelHomeQueryPort {
now: LocalDateTime, now: LocalDateTime,
latestAudioContentId: Long?, latestAudioContentId: Long?,
canViewAdultContent: Boolean, canViewAdultContent: Boolean,
viewerId: Long?,
limit: Int limit: Int
): List<CreatorChannelAudioContentRecord> { ): List<CreatorChannelAudioContentRecord> {
audioContentsLatestAudioContentId = latestAudioContentId audioContentsLatestAudioContentId = latestAudioContentId
@@ -630,7 +648,9 @@ private class FakeCreatorChannelHomeQueryPort : CreatorChannelHomeQueryPort {
isFirstContent = false, isFirstContent = false,
publishedAt = LocalDateTime.of(2026, 6, 10, 0, audioContentId.toInt() % 60), publishedAt = LocalDateTime.of(2026, 6, 10, 0, audioContentId.toInt() % 60),
seriesName = null, seriesName = null,
isOriginalSeries = null isOriginalSeries = null,
isOwned = audioContentId == 201L || audioContentId == 202L,
isRented = audioContentId == 203L
) )
} }

View File

@@ -127,7 +127,9 @@ class CreatorChannelHomeQueryPolicyTest {
isFirstContent = false, isFirstContent = false,
publishedAt = publishedAt, publishedAt = publishedAt,
seriesName = null, seriesName = null,
isOriginalSeries = null isOriginalSeries = null,
isOwned = false,
isRented = false
) )
} }
} }