캐릭터 챗봇 #338
| @@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.common.SodaException | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | ||||
| import org.springframework.web.bind.annotation.GetMapping | ||||
| import org.springframework.web.bind.annotation.PathVariable | ||||
| import org.springframework.web.bind.annotation.PostMapping | ||||
| import org.springframework.web.bind.annotation.RequestBody | ||||
| import org.springframework.web.bind.annotation.RequestMapping | ||||
| @@ -60,4 +61,21 @@ class ChatRoomController( | ||||
|             ApiResponse.ok(response) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 세션 상태 조회 API | ||||
|      * - 채팅방 참여 여부 검증 | ||||
|      * - 외부 API로 세션 상태 조회 후 active면 true, 아니면 false 반환 | ||||
|      */ | ||||
|     @GetMapping("/{chatRoomId}/session") | ||||
|     fun getChatSessionStatus( | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, | ||||
|         @PathVariable chatRoomId: Long | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|         if (member.auth == null) throw SodaException("본인인증을 하셔야 합니다.") | ||||
|  | ||||
|         val isActive = chatRoomService.isMyRoomSessionActive(member, chatRoomId) | ||||
|         ApiResponse.ok(isActive) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -34,18 +34,20 @@ data class ChatRoomListQueryDto( | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * 외부 API 채팅 세션 응답 DTO | ||||
|  * 외부 API 채팅 세션 생성 응답 DTO | ||||
|  */ | ||||
| data class ExternalChatSessionResponse( | ||||
| data class ExternalChatSessionCreateResponse( | ||||
|     val success: Boolean, | ||||
|     val message: String?, | ||||
|     val data: ExternalChatSessionData? | ||||
|     val data: ExternalChatSessionCreateData? | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * 외부 API 채팅 세션 데이터 DTO | ||||
|  * 외부 API 채팅 세션 생성 데이터 DTO | ||||
|  * 공통: sessionId, status | ||||
|  * 생성 전용: userId, characterId, character, createdAt | ||||
|  */ | ||||
| data class ExternalChatSessionData( | ||||
| data class ExternalChatSessionCreateData( | ||||
|     val sessionId: String, | ||||
|     val userId: String, | ||||
|     val characterId: String, | ||||
| @@ -54,6 +56,24 @@ data class ExternalChatSessionData( | ||||
|     val createdAt: String | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * 외부 API 채팅 세션 조회 응답 DTO | ||||
|  */ | ||||
| data class ExternalChatSessionGetResponse( | ||||
|     val success: Boolean, | ||||
|     val message: String?, | ||||
|     val data: ExternalChatSessionGetData? | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * 외부 API 채팅 세션 조회 데이터 DTO | ||||
|  * 세션 조회에서 사용하는 공통 필드만 포함 | ||||
|  */ | ||||
| data class ExternalChatSessionGetData( | ||||
|     val sessionId: String, | ||||
|     val status: String | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * 외부 API 캐릭터 데이터 DTO | ||||
|  */ | ||||
|   | ||||
| @@ -8,7 +8,8 @@ import kr.co.vividnext.sodalive.chat.room.ParticipantType | ||||
| import kr.co.vividnext.sodalive.chat.room.dto.ChatRoomListItemDto | ||||
| import kr.co.vividnext.sodalive.chat.room.dto.ChatRoomListQueryDto | ||||
| import kr.co.vividnext.sodalive.chat.room.dto.CreateChatRoomResponse | ||||
| import kr.co.vividnext.sodalive.chat.room.dto.ExternalChatSessionResponse | ||||
| import kr.co.vividnext.sodalive.chat.room.dto.ExternalChatSessionCreateResponse | ||||
| import kr.co.vividnext.sodalive.chat.room.dto.ExternalChatSessionGetResponse | ||||
| import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatMessageRepository | ||||
| import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatParticipantRepository | ||||
| import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatRoomRepository | ||||
| @@ -150,7 +151,10 @@ class ChatRoomService( | ||||
|  | ||||
|             // 응답 파싱 | ||||
|             val objectMapper = ObjectMapper() | ||||
|             val apiResponse = objectMapper.readValue(response.body, ExternalChatSessionResponse::class.java) | ||||
|             val apiResponse = objectMapper.readValue( | ||||
|                 response.body, | ||||
|                 ExternalChatSessionCreateResponse::class.java | ||||
|             ) | ||||
|  | ||||
|             // success가 false이면 throw | ||||
|             if (!apiResponse.success) { | ||||
| @@ -195,4 +199,55 @@ class ChatRoomService( | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Transactional(readOnly = true) | ||||
|     fun isMyRoomSessionActive(member: Member, chatRoomId: Long): Boolean { | ||||
|         val room = chatRoomRepository.findById(chatRoomId).orElseThrow { | ||||
|             SodaException("채팅방을 찾을 수 없습니다.") | ||||
|         } | ||||
|         val participant = participantRepository.findByChatRoomAndMemberAndIsActiveTrue(room, member) | ||||
|         if (participant == null) { | ||||
|             throw SodaException("잘못된 접근입니다") | ||||
|         } | ||||
|         return fetchSessionActive(room.sessionId) | ||||
|     } | ||||
|  | ||||
|     private fun fetchSessionActive(sessionId: String): Boolean { | ||||
|         try { | ||||
|             val factory = SimpleClientHttpRequestFactory() | ||||
|             factory.setConnectTimeout(20000) // 20초 | ||||
|             factory.setReadTimeout(20000) // 20초 | ||||
|  | ||||
|             val restTemplate = RestTemplate(factory) | ||||
|  | ||||
|             val headers = HttpHeaders() | ||||
|             headers.set("x-api-key", apiKey) | ||||
|  | ||||
|             val httpEntity = HttpEntity(null, headers) | ||||
|  | ||||
|             val response = restTemplate.exchange( | ||||
|                 "$apiUrl/api/session/$sessionId", | ||||
|                 HttpMethod.GET, | ||||
|                 httpEntity, | ||||
|                 String::class.java | ||||
|             ) | ||||
|  | ||||
|             val objectMapper = ObjectMapper() | ||||
|             val apiResponse = objectMapper.readValue( | ||||
|                 response.body, | ||||
|                 ExternalChatSessionGetResponse::class.java | ||||
|             ) | ||||
|  | ||||
|             // success가 false이면 throw | ||||
|             if (!apiResponse.success) { | ||||
|                 throw SodaException("오류가 발생했습니다. 다시 시도해 주세요.") | ||||
|             } | ||||
|  | ||||
|             val status = apiResponse.data?.status | ||||
|             return status == "active" | ||||
|         } catch (e: Exception) { | ||||
|             e.printStackTrace() | ||||
|             throw SodaException("오류가 발생했습니다. 다시 시도해 주세요.") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user