From 735f1e26df9bb875d6132789ca64e9623bc358e8 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 11 Aug 2025 11:33:35 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-character):=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EB=8C=80=ED=99=94=ED=95=9C=20=EC=BA=90=EB=A6=AD=ED=84=B0=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 왜: 기존에는 채팅방 미구현으로 최근 대화 리스트를 빈 배열로 응답했음. 채팅방/메시지 기능이 준비됨에 따라 실제 최근 대화 캐릭터를 노출해야 함. 무엇: - repository: findRecentCharactersByMember JPA 쿼리 추가 (채팅방/참여자/메시지 조인, 최신 메시지 기준 정렬) - service: getRecentCharacters(member, limit) 구현 (member null 처리 및 페이징 적용) - controller: /api/chat/character/main에서 인증 사용자 기준 최근 캐릭터 최대 10개 반환 --- .../controller/ChatCharacterController.kt | 4 +-- .../repository/ChatCharacterRepository.kt | 26 +++++++++++++++++++ .../character/service/ChatCharacterService.kt | 8 +++--- .../sodalive/configs/SecurityConfig.kt | 2 +- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt index 5bee186..20d1e75 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt @@ -45,8 +45,8 @@ class ChatCharacterController( ) } - // 최근 대화한 캐릭터 조회 (현재는 빈 리스트) - val recentCharacters = service.getRecentCharacters() + // 최근 대화한 캐릭터 조회 (회원별 최근 순으로 최대 10개) + val recentCharacters = service.getRecentCharacters(member, 10) .map { RecentCharacter( characterId = it.id!!, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt index 59a617c..ee760aa 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.chat.character.repository import kr.co.vividnext.sodalive.chat.character.ChatCharacter +import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository @@ -40,4 +41,29 @@ interface ChatCharacterRepository : JpaRepository { @Param("searchTerm") searchTerm: String, pageable: Pageable ): Page + + /** + * 멤버가 최근에 대화한 캐릭터 목록을 반환 (최신 메시지 시간 기준 내림차순) + */ + @Query( + value = """ + SELECT c FROM ChatRoom r + JOIN r.participants pu + JOIN r.participants pc + JOIN pc.character c + LEFT JOIN r.messages m + WHERE pu.member = :member + AND pu.participantType = kr.co.vividnext.sodalive.chat.room.ParticipantType.USER + AND pu.isActive = true + AND pc.participantType = kr.co.vividnext.sodalive.chat.room.ParticipantType.CHARACTER + AND pc.isActive = true + AND r.isActive = true + GROUP BY c.id + ORDER BY COALESCE(MAX(m.createdAt), r.createdAt) DESC + """ + ) + fun findRecentCharactersByMember( + @Param("member") member: Member, + pageable: Pageable + ): List } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterService.kt index 0f9f626..8ddce61 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/service/ChatCharacterService.kt @@ -11,6 +11,7 @@ import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterHobbyRepo import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterTagRepository import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterValueRepository +import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.PageRequest import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -26,12 +27,11 @@ class ChatCharacterService( /** * 최근에 대화한 캐릭터 목록 조회 - * 현재는 채팅방 구현 전이므로 빈 리스트 반환 */ @Transactional(readOnly = true) - fun getRecentCharacters(): List { - // 채팅방 구현 전이므로 빈 리스트 반환 - return emptyList() + fun getRecentCharacters(member: Member?, limit: Int = 10): List { + if (member == null) return emptyList() + return chatCharacterRepository.findRecentCharactersByMember(member, PageRequest.of(0, limit)) } /** diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt index de3f665..39142aa 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt @@ -94,7 +94,7 @@ class SecurityConfig( .antMatchers("/ad-tracking/app-launch").permitAll() .antMatchers(HttpMethod.GET, "/notice/latest").permitAll() .antMatchers(HttpMethod.GET, "/api/chat/character/main").permitAll() - .antMatchers(HttpMethod.GET, "/api/chat/list").permitAll() + .antMatchers(HttpMethod.GET, "/api/chat/room/list").permitAll() .anyRequest().authenticated() .and() .build()