diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/controller/ChatRoomController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/controller/ChatRoomController.kt index f5bd481..aa0f1f1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/controller/ChatRoomController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/controller/ChatRoomController.kt @@ -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) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt index b43d8a7..b09f230 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt @@ -182,7 +182,8 @@ data class ChatRoomEnterResponse( val messages: List, val hasMoreMessages: Boolean, val totalRemaining: Int, - val nextRechargeAtEpoch: Long? + val nextRechargeAtEpoch: Long?, + val bgImageUrl: String? = null ) /** diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt index 34d852e..50bd17b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt @@ -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 ) }