캐릭터 챗봇 #338
| @@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.common.SodaException | |||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | import org.springframework.security.core.annotation.AuthenticationPrincipal | ||||||
| import org.springframework.web.bind.annotation.GetMapping | 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.PostMapping | ||||||
| import org.springframework.web.bind.annotation.RequestBody | import org.springframework.web.bind.annotation.RequestBody | ||||||
| import org.springframework.web.bind.annotation.RequestMapping | import org.springframework.web.bind.annotation.RequestMapping | ||||||
| @@ -60,4 +61,21 @@ class ChatRoomController( | |||||||
|             ApiResponse.ok(response) |             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 success: Boolean, | ||||||
|     val message: String?, |     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 sessionId: String, | ||||||
|     val userId: String, |     val userId: String, | ||||||
|     val characterId: String, |     val characterId: String, | ||||||
| @@ -54,6 +56,24 @@ data class ExternalChatSessionData( | |||||||
|     val createdAt: String |     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 |  * 외부 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.ChatRoomListItemDto | ||||||
| import kr.co.vividnext.sodalive.chat.room.dto.ChatRoomListQueryDto | 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.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.CharacterChatMessageRepository | ||||||
| import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatParticipantRepository | import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatParticipantRepository | ||||||
| import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatRoomRepository | import kr.co.vividnext.sodalive.chat.room.repository.CharacterChatRoomRepository | ||||||
| @@ -150,7 +151,10 @@ class ChatRoomService( | |||||||
|  |  | ||||||
|             // 응답 파싱 |             // 응답 파싱 | ||||||
|             val objectMapper = ObjectMapper() |             val objectMapper = ObjectMapper() | ||||||
|             val apiResponse = objectMapper.readValue(response.body, ExternalChatSessionResponse::class.java) |             val apiResponse = objectMapper.readValue( | ||||||
|  |                 response.body, | ||||||
|  |                 ExternalChatSessionCreateResponse::class.java | ||||||
|  |             ) | ||||||
|  |  | ||||||
|             // success가 false이면 throw |             // success가 false이면 throw | ||||||
|             if (!apiResponse.success) { |             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