feat(channel-home): 비밀 후원을 권한에 따라 익명 표시한다

This commit is contained in:
2026-11-04 22:02:00 +09:00
parent 8b3aab232f
commit 6947675322
11 changed files with 324 additions and 41 deletions
@@ -1921,6 +1921,16 @@ class SodaMessageSource {
Lang.KO to "%s캔을 비밀후원하셨습니다.",
Lang.EN to "You secretly sponsored %s cans.",
Lang.JA to "%sCANをシークレット支援しました。"
),
"explorer.channel_donation.anonymous.nickname" to mapOf(
Lang.KO to "익명",
Lang.EN to "Anonymous",
Lang.JA to "匿名"
),
"explorer.channel_donation.anonymous.message" to mapOf(
Lang.KO to "크리에이터에게만 보여요",
Lang.EN to "Only visible to the creator",
Lang.JA to "クリエイターにのみ表示されます"
)
)
@@ -113,7 +113,9 @@ data class CreatorChannelDonationResponse(
val profileImageUrl: String,
val can: Int,
val message: String,
val createdAtUtc: String
val createdAtUtc: String,
@JsonProperty("isSecret")
val isSecret: Boolean
) {
companion object {
fun from(donation: CreatorChannelDonation): CreatorChannelDonationResponse {
@@ -122,7 +124,8 @@ data class CreatorChannelDonationResponse(
profileImageUrl = donation.profileImageUrl,
can = donation.can,
message = donation.message,
createdAtUtc = donation.createdAt.toUtcIso()
createdAtUtc = donation.createdAt.toUtcIso(),
isSecret = donation.isSecret
)
}
}
@@ -201,6 +201,7 @@ class DefaultCreatorChannelHomeQueryRepository(
channelDonationMessage.can,
channelDonationMessage.additionalMessage.coalesce(""),
channelDonationMessage.createdAt,
channelDonationMessage.isSecret,
donorName.name.id.isNotNull
)
)
@@ -210,8 +211,7 @@ class DefaultCreatorChannelHomeQueryRepository(
.where(
channelDonationMessage.creator.id.eq(creatorId),
channelDonationMessage.createdAt.goe(start),
channelDonationMessage.createdAt.lt(end),
donationVisibilityCondition(creatorId, viewerId)
channelDonationMessage.createdAt.lt(end)
)
.orderBy(channelDonationMessage.createdAt.desc(), channelDonationMessage.id.desc())
.limit(limit.toLong())
@@ -737,16 +737,6 @@ class DefaultCreatorChannelHomeQueryRepository(
.not()
}
private fun donationVisibilityCondition(creatorId: Long, viewerId: Long?): BooleanExpression? {
return if (viewerId == null) {
channelDonationMessage.isSecret.isFalse
} else if (viewerId == creatorId) {
null
} else {
channelDonationMessage.isSecret.isFalse.or(channelDonationMessage.member.id.eq(viewerId))
}
}
private fun seriesContentStats(
seriesIds: List<Long>,
now: LocalDateTime,
@@ -102,7 +102,8 @@ class CreatorChannelHomeQueryService(
effectiveViewerGender = effectiveViewerGender
)?.toDomain(),
latestAudioContent = latestAudioContent,
channelDonations = queryPort.findChannelDonations(creatorId, viewerId, now, lang = lang).map { it.toDomain() },
channelDonations = queryPort.findChannelDonations(creatorId, viewerId, now, lang = lang)
.map { it.toDomain(creatorId, viewerId, lang) },
notices = communityQueryService.findHomeCommunityPosts(
creatorId = creatorId,
viewerId = viewerId,
@@ -196,14 +197,30 @@ class CreatorChannelHomeQueryService(
isRented = isRented
)
private fun CreatorChannelDonationRecord.toDomain() = CreatorChannelDonation(
memberId = memberId,
nickname = if (isLocalizedName) nickname else nickname.removeDeletedNicknamePrefix(),
profileImageUrl = profileImagePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
can = can,
message = message,
createdAt = createdAt
)
private fun CreatorChannelDonationRecord.toDomain(creatorId: Long, viewerId: Long, lang: Lang): CreatorChannelDonation {
val isMasked = isSecret && viewerId != creatorId && viewerId != memberId
return CreatorChannelDonation(
memberId = memberId,
nickname = when {
isMasked -> messageSource.getMessage("explorer.channel_donation.anonymous.nickname", lang).orEmpty()
isLocalizedName -> nickname
else -> nickname.removeDeletedNicknamePrefix()
},
profileImageUrl = if (isMasked) {
"$cloudFrontHost/profile/anonymous-profile.png"
} else {
profileImagePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl()
},
can = can,
message = if (isMasked) {
messageSource.getMessage("explorer.channel_donation.anonymous.message", lang).orEmpty()
} else {
message
},
createdAt = createdAt,
isSecret = isSecret
)
}
private fun CreatorChannelScheduleRecord.toDomain() = CreatorChannelSchedule(
scheduledAt = scheduledAt,
@@ -48,7 +48,8 @@ data class CreatorChannelDonation(
val profileImageUrl: String,
val can: Int,
val message: String,
val createdAt: LocalDateTime
val createdAt: LocalDateTime,
val isSecret: Boolean
)
data class CreatorChannelSchedule(
@@ -120,6 +120,7 @@ data class CreatorChannelDonationRecord(
val can: Int,
val message: String,
val createdAt: LocalDateTime,
val isSecret: Boolean,
val isLocalizedName: Boolean = false
)