캐릭터 챗봇 #338

Merged
klaus merged 119 commits from test into main 2025-09-10 06:08:47 +00:00
3 changed files with 39 additions and 5 deletions
Showing only changes of commit 258943535c - Show all commits

View File

@ -91,12 +91,13 @@ class ChatRoomController(
@GetMapping("/{chatRoomId}/enter") @GetMapping("/{chatRoomId}/enter")
fun enterChatRoom( fun enterChatRoom(
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
@PathVariable chatRoomId: Long @PathVariable chatRoomId: Long,
@RequestParam(required = false) characterImageId: Long?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
if (member.auth == 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) ApiResponse.ok(response)
} }

View File

@ -182,7 +182,8 @@ data class ChatRoomEnterResponse(
val messages: List<ChatMessageItemDto>, val messages: List<ChatMessageItemDto>,
val hasMoreMessages: Boolean, val hasMoreMessages: Boolean,
val totalRemaining: Int, val totalRemaining: Int,
val nextRechargeAtEpoch: Long? val nextRechargeAtEpoch: Long?,
val bgImageUrl: String? = null
) )
/** /**

View File

@ -298,7 +298,7 @@ class ChatRoomService(
} }
@Transactional @Transactional
fun enterChatRoom(member: Member, chatRoomId: Long): ChatRoomEnterResponse { fun enterChatRoom(member: Member, chatRoomId: Long, characterImageId: Long? = null): ChatRoomEnterResponse {
val room = chatRoomRepository.findById(chatRoomId).orElseThrow { val room = chatRoomRepository.findById(chatRoomId).orElseThrow {
SodaException("채팅방을 찾을 수 없습니다.") SodaException("채팅방을 찾을 수 없습니다.")
} }
@ -340,13 +340,45 @@ class ChatRoomService(
// 입장 시 Lazy refill 적용 후 상태 반환 // 입장 시 Lazy refill 적용 후 상태 반환
val quotaStatus = chatQuotaService.applyRefillOnEnterAndGetStatus(member.id!!) 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( return ChatRoomEnterResponse(
roomId = room.id!!, roomId = room.id!!,
character = characterDto, character = characterDto,
messages = items, messages = items,
hasMoreMessages = hasMore, hasMoreMessages = hasMore,
totalRemaining = quotaStatus.totalRemaining, totalRemaining = quotaStatus.totalRemaining,
nextRechargeAtEpoch = quotaStatus.nextRechargeAtEpochMillis nextRechargeAtEpoch = quotaStatus.nextRechargeAtEpochMillis,
bgImageUrl = signedUrl
) )
} }