feat(explorer): 크리에이터 상세정보 조회 API를 추가한다

This commit is contained in:
2026-02-23 16:25:57 +09:00
parent cc74628107
commit 10e1c1eed0
5 changed files with 126 additions and 0 deletions

View File

@@ -73,6 +73,15 @@ class ExplorerController(
)
}
@GetMapping("/profile/{id}/detail")
fun getCreatorDetail(
@PathVariable("id") creatorId: Long,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getCreatorDetail(creatorId = creatorId, memberId = member.id!!))
}
@GetMapping("/profile/{id}/donation-rank")
fun getCreatorProfileDonationRanking(
@PathVariable("id") creatorId: Long,

View File

@@ -565,6 +565,30 @@ class ExplorerQueryRepository(
.fetchFirst()
}
fun getFirstLiveBeginDateTime(creatorId: Long): LocalDateTime? {
return queryFactory
.select(liveRoom.beginDateTime.min())
.from(liveRoom)
.where(
liveRoom.member.id.eq(creatorId)
.and(liveRoom.channelName.isNotNull)
)
.fetchFirst()
}
fun getFirstContentReleaseDate(creatorId: Long): LocalDateTime? {
return queryFactory
.select(audioContent.releaseDate.min())
.from(audioContent)
.where(
audioContent.member.id.eq(creatorId)
.and(audioContent.isActive.isTrue)
.and(audioContent.releaseDate.isNotNull)
.and(audioContent.releaseDate.loe(LocalDateTime.now()))
)
.fetchFirst()
}
fun getLiveTime(creatorId: Long): Long {
val diffs = queryFactory
.select(
@@ -708,6 +732,8 @@ class ExplorerQueryRepository(
.where(
audioContent.isActive.isTrue
.and(audioContent.member.id.eq(creatorId))
.and(audioContent.releaseDate.isNotNull)
.and(audioContent.releaseDate.loe(LocalDateTime.now()))
)
.fetchFirst()
}

View File

@@ -37,6 +37,7 @@ import java.time.DayOfWeek
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
import kotlin.random.Random
@@ -192,6 +193,63 @@ class ExplorerService(
.toList()
}
fun getCreatorDetail(creatorId: Long, memberId: Long): GetCreatorDetailResponse {
val creatorAccount = queryRepository.getMember(creatorId)
?: throw SodaException(messageKey = "member.validation.user_not_found")
if (isBlockedBetweenMembers(memberId = memberId, otherMemberId = creatorId)) {
val messageTemplate = messageSource
.getMessage("explorer.creator.blocked_access", langContext.lang)
.orEmpty()
throw SodaException(message = String.format(messageTemplate, creatorAccount.nickname))
}
if (creatorAccount.role != MemberRole.CREATOR) {
throw SodaException(messageKey = "member.validation.creator_not_found")
}
val liveCount = queryRepository.getLiveCount(creatorId) ?: 0
val liveTime = queryRepository.getLiveTime(creatorId)
val liveContributorCount = queryRepository.getLiveContributorCount(creatorId) ?: 0
val contentCount = queryRepository.getContentCount(creatorId) ?: 0
val activitySummary = GetCreatorActivitySummary(
liveCount = liveCount,
liveTime = liveTime,
liveContributorCount = liveContributorCount,
contentCount = contentCount
)
val debutDateTime = listOfNotNull(
queryRepository.getFirstLiveBeginDateTime(creatorId),
queryRepository.getFirstContentReleaseDate(creatorId)
).minOrNull()
val debutDate = debutDateTime?.toLocalDate()
val dDay = if (debutDate != null) {
"D+${ChronoUnit.DAYS.between(debutDate, LocalDate.now())}"
} else {
""
}
return GetCreatorDetailResponse(
nickname = creatorAccount.nickname,
profileImageUrl = if (creatorAccount.profileImage != null) {
"$cloudFrontHost/${creatorAccount.profileImage}"
} else {
"$cloudFrontHost/profile/default-profile.png"
},
debutDate = debutDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: "",
dDay = dDay,
activitySummary = activitySummary,
instagramUrl = creatorAccount.instagramUrl,
fancimmUrl = creatorAccount.fancimmUrl,
xUrl = creatorAccount.xUrl,
youtubeUrl = creatorAccount.youtubeUrl,
websiteUrl = creatorAccount.websiteUrl,
blogUrl = creatorAccount.blogUrl
)
}
fun getCreatorProfile(
creatorId: Long,
timezone: String,

View File

@@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.explorer
data class GetCreatorDetailResponse(
val nickname: String,
val profileImageUrl: String,
val debutDate: String,
val dDay: String,
val activitySummary: GetCreatorActivitySummary,
val instagramUrl: String,
val fancimmUrl: String,
val xUrl: String,
val youtubeUrl: String,
val websiteUrl: String,
val blogUrl: String
)