feat(character): 캐릭터 메인 API 추가
This commit is contained in:
parent
b0a6fc6498
commit
a1533c8e98
|
@ -0,0 +1,88 @@
|
|||
package kr.co.vividnext.sodalive.chat.character.controller
|
||||
|
||||
import kr.co.vividnext.sodalive.chat.character.dto.Character
|
||||
import kr.co.vividnext.sodalive.chat.character.dto.CharacterBannerResponse
|
||||
import kr.co.vividnext.sodalive.chat.character.dto.CharacterMainResponse
|
||||
import kr.co.vividnext.sodalive.chat.character.dto.CurationSection
|
||||
import kr.co.vividnext.sodalive.chat.character.dto.RecentCharacter
|
||||
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterBannerService
|
||||
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/chat/character")
|
||||
class ChatCharacterController(
|
||||
private val service: ChatCharacterService,
|
||||
private val bannerService: ChatCharacterBannerService,
|
||||
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
private val imageHost: String
|
||||
) {
|
||||
@GetMapping("/main")
|
||||
fun getCharacterMain(
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
): ApiResponse<CharacterMainResponse> = run {
|
||||
// 배너 조회 (최대 10개)
|
||||
val banners = bannerService.getActiveBanners(PageRequest.of(0, 10))
|
||||
.content
|
||||
.map {
|
||||
CharacterBannerResponse(
|
||||
characterId = it.chatCharacter.id!!,
|
||||
imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}"
|
||||
)
|
||||
}
|
||||
|
||||
// 최근 대화한 캐릭터 조회 (현재는 빈 리스트)
|
||||
val recentCharacters = service.getRecentCharacters()
|
||||
.map {
|
||||
RecentCharacter(
|
||||
characterId = it.id!!,
|
||||
name = it.name,
|
||||
imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}"
|
||||
)
|
||||
}
|
||||
|
||||
// 인기 캐릭터 조회 (현재는 빈 리스트)
|
||||
val popularCharacters = service.getPopularCharacters()
|
||||
.map {
|
||||
Character(
|
||||
characterId = it.id!!,
|
||||
name = it.name,
|
||||
description = it.description,
|
||||
imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}"
|
||||
)
|
||||
}
|
||||
|
||||
// 최신 캐릭터 조회 (최대 10개)
|
||||
val newCharacters = service.getNewCharacters(10)
|
||||
.map {
|
||||
Character(
|
||||
characterId = it.id!!,
|
||||
name = it.name,
|
||||
description = it.description,
|
||||
imageUrl = "$imageHost/${it.imagePath ?: "profile/default-profile.png"}"
|
||||
)
|
||||
}
|
||||
|
||||
// 큐레이션 섹션 (현재는 빈 리스트)
|
||||
val curationSections = emptyList<CurationSection>()
|
||||
|
||||
// 응답 생성
|
||||
ApiResponse.ok(
|
||||
CharacterMainResponse(
|
||||
banners = banners,
|
||||
recentCharacters = recentCharacters,
|
||||
popularCharacters = popularCharacters,
|
||||
newCharacters = newCharacters,
|
||||
curationSections = curationSections
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package kr.co.vividnext.sodalive.chat.character.dto
|
||||
|
||||
data class CharacterMainResponse(
|
||||
val banners: List<CharacterBannerResponse>,
|
||||
val recentCharacters: List<RecentCharacter>,
|
||||
val popularCharacters: List<Character>,
|
||||
val newCharacters: List<Character>,
|
||||
val curationSections: List<CurationSection>
|
||||
)
|
||||
|
||||
data class CurationSection(
|
||||
val characterCurationId: Long,
|
||||
val title: String,
|
||||
val characters: List<Character>
|
||||
)
|
||||
|
||||
data class Character(
|
||||
val characterId: Long,
|
||||
val name: String,
|
||||
val description: String,
|
||||
val imageUrl: String
|
||||
)
|
||||
|
||||
data class RecentCharacter(
|
||||
val characterId: Long,
|
||||
val name: String,
|
||||
val imageUrl: String
|
||||
)
|
||||
|
||||
data class CharacterBannerResponse(
|
||||
val characterId: Long,
|
||||
val imageUrl: String
|
||||
)
|
|
@ -10,7 +10,7 @@ import org.springframework.stereotype.Repository
|
|||
@Repository
|
||||
interface ChatCharacterBannerRepository : JpaRepository<ChatCharacterBanner, Long> {
|
||||
// 활성화된 배너 목록 조회 (정렬 순서대로)
|
||||
fun findByActiveTrueOrderBySortOrderAsc(pageable: Pageable): Page<ChatCharacterBanner>
|
||||
fun findByIsActiveTrueOrderBySortOrderAsc(pageable: Pageable): Page<ChatCharacterBanner>
|
||||
|
||||
// 활성화된 배너 중 최대 정렬 순서 값 조회
|
||||
@Query("SELECT MAX(b.sortOrder) FROM ChatCharacterBanner b WHERE b.isActive = true")
|
||||
|
|
|
@ -18,7 +18,7 @@ class ChatCharacterBannerService(
|
|||
* 활성화된 모든 배너 조회 (정렬 순서대로)
|
||||
*/
|
||||
fun getActiveBanners(pageable: Pageable): Page<ChatCharacterBanner> {
|
||||
return bannerRepository.findByActiveTrueOrderBySortOrderAsc(pageable)
|
||||
return bannerRepository.findByIsActiveTrueOrderBySortOrderAsc(pageable)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,37 @@ class ChatCharacterService(
|
|||
private val goalRepository: ChatCharacterGoalRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* 최근에 대화한 캐릭터 목록 조회
|
||||
* 현재는 채팅방 구현 전이므로 빈 리스트 반환
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
fun getRecentCharacters(): List<ChatCharacter> {
|
||||
// 채팅방 구현 전이므로 빈 리스트 반환
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 일주일간 대화가 가장 많은 인기 캐릭터 목록 조회
|
||||
* 현재는 채팅방 구현 전이므로 빈 리스트 반환
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
fun getPopularCharacters(): List<ChatCharacter> {
|
||||
// 채팅방 구현 전이므로 빈 리스트 반환
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 최근 등록된 캐릭터 목록 조회 (최대 10개)
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
fun getNewCharacters(limit: Int = 10): List<ChatCharacter> {
|
||||
return chatCharacterRepository.findAll()
|
||||
.filter { it.isActive }
|
||||
.sortedByDescending { it.createdAt }
|
||||
.take(limit)
|
||||
}
|
||||
|
||||
/**
|
||||
* 태그를 찾거나 생성하여 캐릭터에 연결
|
||||
*/
|
||||
|
|
|
@ -93,6 +93,7 @@ class SecurityConfig(
|
|||
.antMatchers(HttpMethod.GET, "/live/recommend").permitAll()
|
||||
.antMatchers("/ad-tracking/app-launch").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/notice/latest").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/api/chat/character/main").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.build()
|
||||
|
|
Loading…
Reference in New Issue