캐릭터 챗봇 #338
| @@ -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) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -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 | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user