feat(chat): 이미지 메시지 조회 시 CloudFront 서명 URL 적용 및 DTO 변환 로직 공통화
- 조회 가능한(보유/무료/결제완료) 이미지 메시지의 이미지 URL을 ImageContentCloudFront.generateSignedURL(만료 5분)로 생성 - 접근 불가(미보유, 유료 미구매) 이미지 메시지는 기존 공개 호스트 URL(블러/스냅샷 경로) 유지 - ChatRoomService에 ImageContentCloudFront를 주입하고, toChatMessageItemDto에서 이미지 URL/hasAccess 결정 로직 단일화 - enterChatRoom, getChatMessages, sendMessage 경로의 중복된 DTO 매핑 로직 제거 - purchaseMessage 결제 완료 시 forceHasAccess=true로 접근 가능 DTO 반환
This commit is contained in:
		| @@ -48,6 +48,7 @@ class ChatRoomService( | ||||
|     private val characterService: ChatCharacterService, | ||||
|     private val characterImageService: CharacterImageService, | ||||
|     private val canPaymentService: kr.co.vividnext.sodalive.can.payment.CanPaymentService, | ||||
|     private val imageCloudFront: kr.co.vividnext.sodalive.aws.cloudfront.ImageContentCloudFront, | ||||
|  | ||||
|     @Value("\${weraser.api-key}") | ||||
|     private val apiKey: String, | ||||
| @@ -332,40 +333,7 @@ class ChatRoomService( | ||||
|         } | ||||
|  | ||||
|         val messagesAsc = fetched.sortedBy { it.createdAt } | ||||
|         val items = messagesAsc.map { msg -> | ||||
|             val sender = msg.participant | ||||
|             val profilePath = when (sender.participantType) { | ||||
|                 ParticipantType.USER -> sender.member?.profileImage | ||||
|                 ParticipantType.CHARACTER -> sender.character?.imagePath | ||||
|             } | ||||
|             val senderImageUrl = "$imageHost/${profilePath ?: "profile/default-profile.png"}" | ||||
|             val createdAtMillis = msg.createdAt?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() | ||||
|                 ?: 0L | ||||
|             ChatMessageItemDto( | ||||
|                 messageId = msg.id!!, | ||||
|                 message = msg.message, | ||||
|                 profileImageUrl = senderImageUrl, | ||||
|                 mine = sender.member?.id == member.id, | ||||
|                 createdAt = createdAtMillis, | ||||
|                 messageType = msg.messageType.name, | ||||
|                 imageUrl = msg.imagePath?.let { "$imageHost/$it" }, | ||||
|                 price = msg.price, | ||||
|                 hasAccess = if (msg.messageType == ChatMessageType.IMAGE) { | ||||
|                     if (msg.price == null) { | ||||
|                         true | ||||
|                     } else { | ||||
|                         msg.characterImage?.id?.let { | ||||
|                             characterImageService.isOwnedImageByMember( | ||||
|                                 it, | ||||
|                                 member.id!! | ||||
|                             ) | ||||
|                         } ?: true | ||||
|                     } | ||||
|                 } else { | ||||
|                     true | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|         val items = messagesAsc.map { toChatMessageItemDto(it, member) } | ||||
|  | ||||
|         return ChatRoomEnterResponse( | ||||
|             roomId = room.id!!, | ||||
| @@ -512,40 +480,7 @@ class ChatRoomService( | ||||
|         // createdAt 오름차순으로 정렬하여 반환 | ||||
|         val messagesAsc = fetched.sortedBy { it.createdAt } | ||||
|  | ||||
|         val items = messagesAsc.map { msg -> | ||||
|             val sender = msg.participant | ||||
|             val profilePath = when (sender.participantType) { | ||||
|                 ParticipantType.USER -> sender.member?.profileImage | ||||
|                 ParticipantType.CHARACTER -> sender.character?.imagePath | ||||
|             } | ||||
|             val senderImageUrl = "$imageHost/${profilePath ?: "profile/default-profile.png"}" | ||||
|             val createdAtMillis = msg.createdAt?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() | ||||
|                 ?: 0L | ||||
|             ChatMessageItemDto( | ||||
|                 messageId = msg.id!!, | ||||
|                 message = msg.message, | ||||
|                 profileImageUrl = senderImageUrl, | ||||
|                 mine = sender.member?.id == member.id, | ||||
|                 createdAt = createdAtMillis, | ||||
|                 messageType = msg.messageType.name, | ||||
|                 imageUrl = msg.imagePath?.let { "$imageHost/$it" }, | ||||
|                 price = msg.price, | ||||
|                 hasAccess = if (msg.messageType == ChatMessageType.IMAGE) { | ||||
|                     if (msg.price == null) { | ||||
|                         true | ||||
|                     } else { | ||||
|                         msg.characterImage?.id?.let { | ||||
|                             characterImageService.isOwnedImageByMember( | ||||
|                                 it, | ||||
|                                 member.id!! | ||||
|                             ) | ||||
|                         } ?: true | ||||
|                     } | ||||
|                 } else { | ||||
|                     true | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|         val items = messagesAsc.map { toChatMessageItemDto(it, member) } | ||||
|  | ||||
|         return ChatMessagesPageResponse( | ||||
|             messages = items, | ||||
| @@ -643,19 +578,7 @@ class ChatRoomService( | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|             val imageDto = ChatMessageItemDto( | ||||
|                 messageId = imageMsg.id!!, | ||||
|                 message = imageMsg.message, | ||||
|                 profileImageUrl = senderImageUrl, | ||||
|                 mine = false, | ||||
|                 createdAt = imageMsg.createdAt?.atZone(ZoneId.systemDefault())?.toInstant() | ||||
|                     ?.toEpochMilli() | ||||
|                     ?: 0L, | ||||
|                 messageType = ChatMessageType.IMAGE.name, | ||||
|                 imageUrl = imageMsg.imagePath?.let { "$imageHost/$it" }, | ||||
|                 price = imageMsg.price, | ||||
|                 hasAccess = owned || imageMsg.price == null | ||||
|             ) | ||||
|             val imageDto = toChatMessageItemDto(imageMsg, member) | ||||
|             return listOf(textDto, imageDto) | ||||
|         } | ||||
|  | ||||
| @@ -687,6 +610,23 @@ class ChatRoomService( | ||||
|         } else { | ||||
|             true | ||||
|         } | ||||
|         val expirationMs = 5L * 60L * 1000L | ||||
|         val resolvedImageUrl: String? = if (msg.messageType == ChatMessageType.IMAGE) { | ||||
|             val path = if (hasAccess) { | ||||
|                 msg.characterImage?.imagePath ?: msg.imagePath | ||||
|             } else { | ||||
|                 msg.imagePath | ||||
|             } | ||||
|             path?.let { p -> | ||||
|                 if (hasAccess) { | ||||
|                     imageCloudFront.generateSignedURL(p, expirationMs) | ||||
|                 } else { | ||||
|                     "$imageHost/$p" | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             null | ||||
|         } | ||||
|         return ChatMessageItemDto( | ||||
|             messageId = msg.id!!, | ||||
|             message = msg.message, | ||||
| @@ -694,7 +634,7 @@ class ChatRoomService( | ||||
|             mine = sender.member?.id == member.id, | ||||
|             createdAt = createdAtMillis, | ||||
|             messageType = msg.messageType.name, | ||||
|             imageUrl = msg.imagePath?.let { "$imageHost/$it" }, | ||||
|             imageUrl = resolvedImageUrl, | ||||
|             price = msg.price, | ||||
|             hasAccess = hasAccess | ||||
|         ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user