캐릭터 챗봇 #338
| @@ -91,12 +91,13 @@ class ChatRoomController( | ||||
|     @GetMapping("/{chatRoomId}/enter") | ||||
|     fun enterChatRoom( | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, | ||||
|         @PathVariable chatRoomId: Long | ||||
|         @PathVariable chatRoomId: Long, | ||||
|         @RequestParam(required = false) characterImageId: Long? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|         if (member.auth == null) throw SodaException("본인인증을 하셔야 합니다.") | ||||
|  | ||||
|         val response = chatRoomService.enterChatRoom(member, chatRoomId) | ||||
|         val response = chatRoomService.enterChatRoom(member, chatRoomId, characterImageId) | ||||
|         ApiResponse.ok(response) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -182,7 +182,8 @@ data class ChatRoomEnterResponse( | ||||
|     val messages: List<ChatMessageItemDto>, | ||||
|     val hasMoreMessages: Boolean, | ||||
|     val totalRemaining: Int, | ||||
|     val nextRechargeAtEpoch: Long? | ||||
|     val nextRechargeAtEpoch: Long?, | ||||
|     val bgImageUrl: String? = null | ||||
| ) | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -298,7 +298,7 @@ class ChatRoomService( | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun enterChatRoom(member: Member, chatRoomId: Long): ChatRoomEnterResponse { | ||||
|     fun enterChatRoom(member: Member, chatRoomId: Long, characterImageId: Long? = null): ChatRoomEnterResponse { | ||||
|         val room = chatRoomRepository.findById(chatRoomId).orElseThrow { | ||||
|             SodaException("채팅방을 찾을 수 없습니다.") | ||||
|         } | ||||
| @@ -340,13 +340,45 @@ class ChatRoomService( | ||||
|         // 입장 시 Lazy refill 적용 후 상태 반환 | ||||
|         val quotaStatus = chatQuotaService.applyRefillOnEnterAndGetStatus(member.id!!) | ||||
|  | ||||
|         // 선택적 캐릭터 이미지 서명 URL 생성 처리 | ||||
|         val signedUrl: String? = try { | ||||
|             if (characterImageId != null) { | ||||
|                 val img = characterImageService.getById(characterImageId) | ||||
|                 // 동일 캐릭터 소속 및 활성 검증 | ||||
|                 if (img.chatCharacter.id == character.id && img.isActive) { | ||||
|                     val owned = | ||||
|                         (img.imagePriceCan == 0L) || characterImageService.isOwnedImageByMember(img.id!!, member.id!!) | ||||
|                     if (owned) { | ||||
|                         val expiration = 5L * 60L * 1000L // 5분 | ||||
|                         imageCloudFront.generateSignedURL(img.imagePath, expiration) | ||||
|                     } else { | ||||
|                         null | ||||
|                     } | ||||
|                 } else { | ||||
|                     null | ||||
|                 } | ||||
|             } else { | ||||
|                 null | ||||
|             } | ||||
|         } catch (e: Exception) { | ||||
|             // 문제가 있어도 입장 자체는 가능해야 하므로 로그만 남기고 null 반환 | ||||
|             log.warn( | ||||
|                 "[chat] enter: signed url generation failed. roomId={}, imageId={}, reason={}", | ||||
|                 room.id, | ||||
|                 characterImageId, | ||||
|                 e.message | ||||
|             ) | ||||
|             null | ||||
|         } | ||||
|  | ||||
|         return ChatRoomEnterResponse( | ||||
|             roomId = room.id!!, | ||||
|             character = characterDto, | ||||
|             messages = items, | ||||
|             hasMoreMessages = hasMore, | ||||
|             totalRemaining = quotaStatus.totalRemaining, | ||||
|             nextRechargeAtEpoch = quotaStatus.nextRechargeAtEpochMillis | ||||
|             nextRechargeAtEpoch = quotaStatus.nextRechargeAtEpochMillis, | ||||
|             bgImageUrl = signedUrl | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user