캐릭터 챗봇 #338
| @@ -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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user