feat(channel-donation): 비밀 후원을 목록과 건수에 포함하고 익명화한다

This commit is contained in:
2026-11-04 22:02:19 +09:00
parent 6947675322
commit e48ea1b505
11 changed files with 393 additions and 71 deletions
@@ -52,7 +52,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 {
@@ -61,7 +63,8 @@ data class CreatorChannelDonationResponse(
profileImageUrl = donation.profileImageUrl,
can = donation.can,
message = donation.message,
createdAtUtc = donation.createdAt.toUtcIso()
createdAtUtc = donation.createdAt.toUtcIso(),
isSecret = donation.isSecret
)
}
}
@@ -68,7 +68,7 @@ class DefaultCreatorChannelDonationQueryRepository(
return queryFactory
.select(channelDonationMessage.id.count())
.from(channelDonationMessage)
.where(channelDonationCondition(creatorId, viewerId, now))
.where(channelDonationCondition(creatorId, now))
.fetchOne()
?.toInt()
?: 0
@@ -93,13 +93,14 @@ class DefaultCreatorChannelDonationQueryRepository(
channelDonationMessage.can,
channelDonationMessage.additionalMessage,
channelDonationMessage.createdAt,
channelDonationMessage.isSecret,
donorName.name.id.isNotNull
)
)
.from(channelDonationMessage)
.join(channelDonationMessage.member, member)
.leftJoin(donorName.name).on(donorName.condition)
.where(channelDonationCondition(creatorId, viewerId, now))
.where(channelDonationCondition(creatorId, now))
.orderBy(channelDonationMessage.createdAt.desc(), channelDonationMessage.id.desc())
.offset(offset)
.limit(limit.toLong())
@@ -108,22 +109,11 @@ class DefaultCreatorChannelDonationQueryRepository(
private fun channelDonationCondition(
creatorId: Long,
viewerId: Long,
now: LocalDateTime
): BooleanExpression {
val monthRange = queryPolicy.currentKstMonthRange(now)
return channelDonationMessage.creator.id.eq(creatorId)
.and(channelDonationMessage.createdAt.goe(monthRange.startInclusiveUtc))
.and(channelDonationMessage.createdAt.lt(monthRange.endExclusiveUtc))
.and(donationVisibilityCondition(creatorId, viewerId))
}
private fun donationVisibilityCondition(creatorId: Long, viewerId: Long): BooleanExpression {
return if (creatorId == viewerId) {
channelDonationMessage.id.isNotNull
} else {
channelDonationMessage.isSecret.isFalse
.or(channelDonationMessage.member.id.eq(viewerId))
}
}
}
@@ -68,7 +68,7 @@ class CreatorChannelDonationQueryService(
return CreatorChannelDonationTab(
donationCount = queryPort.countChannelDonations(creatorId, viewerId, now),
rankings = findRankings(creator, viewerId),
donations = queryPolicy.limitItems(fetchedDonations, donationPage).map { it.toDomain() },
donations = queryPolicy.limitItems(fetchedDonations, donationPage).map { it.toDomain(creatorId, viewerId, lang) },
page = donationPage,
hasNext = queryPolicy.hasNext(fetchedDonations, donationPage)
)
@@ -95,14 +95,30 @@ class CreatorChannelDonationQueryService(
).map { it.toDomain() }
}
private fun CreatorChannelDonationRecord.toDomain() = CreatorChannelDonation(
memberId = memberId,
nickname = if (isLocalizedName) nickname else nickname.removeDeletedNicknamePrefix(),
profileImageUrl = profileImagePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
can = can,
message = message.orEmpty(),
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.orEmpty()
},
createdAt = createdAt,
isSecret = isSecret
)
}
private fun CreatorChannelDonationRankingRecord.toDomain() = CreatorChannelDonationRanking(
userId = userId,
@@ -24,5 +24,6 @@ data class CreatorChannelDonation(
val profileImageUrl: String,
val can: Int,
val message: String,
val createdAt: LocalDateTime
val createdAt: LocalDateTime,
val isSecret: Boolean
)
@@ -41,5 +41,6 @@ data class CreatorChannelDonationRecord(
val can: Int,
val message: String?,
val createdAt: LocalDateTime,
val isSecret: Boolean,
val isLocalizedName: Boolean = false
)