feat(character): 최근 등록된 캐릭터 전체보기 API

- 반환 값에 전체 개수 추가
This commit is contained in:
Klaus 2025-09-12 19:00:45 +09:00
parent 88e287067b
commit 3dc9dd1f35
3 changed files with 31 additions and 10 deletions

View File

@ -69,10 +69,10 @@ class ChatCharacterController(
val popularCharacters = service.getPopularCharacters() val popularCharacters = service.getPopularCharacters()
// 최근 등록된 캐릭터 리스트 조회 // 최근 등록된 캐릭터 리스트 조회
val newCharacters = service.getRecentCharacters( val newCharacters = service.getRecentCharactersPage(
page = 0, page = 0,
size = 50 size = 50
) ).content
// 큐레이션 섹션 (활성화된 큐레이션 + 캐릭터) // 큐레이션 섹션 (활성화된 큐레이션 + 캐릭터)
val curationSections = curationQueryService.getActiveCurationsWithCharacters() val curationSections = curationQueryService.getActiveCurationsWithCharacters()
@ -187,7 +187,7 @@ class ChatCharacterController(
@GetMapping("/recent") @GetMapping("/recent")
fun getRecentCharacters(@RequestParam("page", required = false) page: Int?) = run { fun getRecentCharacters(@RequestParam("page", required = false) page: Int?) = run {
ApiResponse.ok( ApiResponse.ok(
service.getRecentCharacters( service.getRecentCharactersPage(
page = page ?: 0, page = page ?: 0,
size = 20 size = 20
) )

View File

@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.chat.character.dto
/**
* 최근 등록된 캐릭터 전체보기 페이지 응답 DTO
*/
data class RecentCharactersResponse(
val totalCount: Long,
val content: List<Character>
)

View File

@ -12,6 +12,7 @@ import kr.co.vividnext.sodalive.chat.character.ChatCharacterHobby
import kr.co.vividnext.sodalive.chat.character.ChatCharacterTag import kr.co.vividnext.sodalive.chat.character.ChatCharacterTag
import kr.co.vividnext.sodalive.chat.character.ChatCharacterValue import kr.co.vividnext.sodalive.chat.character.ChatCharacterValue
import kr.co.vividnext.sodalive.chat.character.dto.Character import kr.co.vividnext.sodalive.chat.character.dto.Character
import kr.co.vividnext.sodalive.chat.character.dto.RecentCharactersResponse
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterGoalRepository import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterGoalRepository
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterHobbyRepository import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterHobbyRepository
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository
@ -68,12 +69,12 @@ class ChatCharacterService(
} }
/** /**
* 최근 등록된 캐릭터 전체보기 (페이징) * 최근 등록된 캐릭터 전체보기 (페이징) - 전체 개수 포함
* - 기준: 현재 시각 기준 2 이내 생성된 활성 캐릭터 * - 기준: 현재 시각 기준 2 이내 생성된 활성 캐릭터
* - 2 이내 캐릭터가 0개라면: 최근 등록한 캐릭터 20 반환(페이지 무시) * - 2 이내 캐릭터가 0개라면: totalCount=20, 페이지는 최근 등록 활성 캐릭터 20, 페이지는 리스트
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
fun getRecentCharacters(page: Int = 0, size: Int = 20): List<Character> { fun getRecentCharactersPage(page: Int = 0, size: Int = 20): RecentCharactersResponse {
val safePage = if (page < 0) 0 else page val safePage = if (page < 0) 0 else page
val safeSize = when { val safeSize = when {
size <= 0 -> 20 size <= 0 -> 20
@ -85,13 +86,15 @@ class ChatCharacterService(
val totalRecent = chatCharacterRepository.countByIsActiveTrueAndCreatedAtGreaterThanEqual(since) val totalRecent = chatCharacterRepository.countByIsActiveTrueAndCreatedAtGreaterThanEqual(since)
if (totalRecent == 0L) { if (totalRecent == 0L) {
if (safePage > 0) { if (safePage > 0) {
return emptyList() return RecentCharactersResponse(
totalCount = 20,
content = emptyList()
)
} }
val fallback = chatCharacterRepository.findByIsActiveTrue( val fallback = chatCharacterRepository.findByIsActiveTrue(
PageRequest.of(0, 20, Sort.by("createdAt").descending()) PageRequest.of(0, 20, Sort.by("createdAt").descending())
) )
return fallback.content.map { val content = fallback.content.map {
Character( Character(
characterId = it.id!!, characterId = it.id!!,
name = it.name, name = it.name,
@ -99,13 +102,17 @@ class ChatCharacterService(
imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}" imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}"
) )
} }
return RecentCharactersResponse(
totalCount = 20,
content = content
)
} }
val pageResult = chatCharacterRepository.findRecentSince( val pageResult = chatCharacterRepository.findRecentSince(
since, since,
PageRequest.of(safePage, safeSize) PageRequest.of(safePage, safeSize)
) )
return pageResult.content.map { val content = pageResult.content.map {
Character( Character(
characterId = it.id!!, characterId = it.id!!,
name = it.name, name = it.name,
@ -113,6 +120,11 @@ class ChatCharacterService(
imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}" imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}"
) )
} }
return RecentCharactersResponse(
totalCount = totalRecent,
content = content
)
} }
/** /**