Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,4 @@ class ChatRoomService (val chatroomRepository: ChatRoomRepository){
count = projection.getUnreadCount(),
opponentId = projection.getOpponentId()
)

fun getByCenterWithCarer(centerId: UUID, carerId: UUID, isCarer: Boolean): ChatRoomSummaryInfo {
val projections: ChatRoomSummaryInfoProjection

if(isCarer) {
projections = chatroomRepository.carerFindSingleChatRoom(
centerId = centerId,
carerId = carerId
)
}else {
projections = chatroomRepository.centerFindSingleChatRoom(
centerId = centerId,
carerId = carerId
)
}

return mappingChatRoomSummaryInfo(projections)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.swm.idle.application.common.security.getUserAuthentication
import com.swm.idle.application.notification.domain.DeviceTokenService
import com.swm.idle.application.user.carer.domain.CarerService
import com.swm.idle.application.user.center.service.domain.CenterService
import com.swm.idle.domain.chat.event.ChatRedisPublisher
import com.swm.idle.domain.chat.event.ChatRedisTemplate
import com.swm.idle.domain.chat.vo.ChatRoomSummaryInfo
import com.swm.idle.domain.chat.vo.ReadMessage
import com.swm.idle.infrastructure.fcm.chat.ChatNotificationService
Expand All @@ -19,7 +19,7 @@ import java.util.*
@Service
@Transactional(readOnly = true)
class ChatFacadeService(
private val redisPublisher: ChatRedisPublisher,
private val chatRedisTemplate: ChatRedisTemplate,
private val messageService: ChatMessageService,
private val notificationService: ChatNotificationService,
private val deviceTokenService: DeviceTokenService,
Expand All @@ -32,8 +32,9 @@ class ChatFacadeService(
@Transactional
fun sendMessage(request: SendChatMessageRequest, userId: UUID) {
val message = messageService.save(request, userId)
redisPublisher.publish(message)
chatRedisTemplate.publish(message)

if (chatRedisTemplate.isChatting(message.receiverId)) return
val token = deviceTokenService.findByUserId(message.receiverId)
notificationService.send(message, request.senderName, token)
}
Expand All @@ -47,7 +48,7 @@ class ChatFacadeService(
receiverId = request.opponentId,
readUserId = userId
)
redisPublisher.publish(readMessage)
chatRedisTemplate.publish(readMessage)
}

@Transactional
Expand Down Expand Up @@ -89,32 +90,4 @@ class ChatFacadeService(
}
}
}

fun getSingleChatRoomInfo(chatRoomId: UUID, opponentId: UUID,isCarer: Boolean): ChatRoomSummaryInfo {
val (carerId, centerId) = if (isCarer) {
getUserAuthentication().userId to opponentId
} else {
opponentId to getUserAuthentication().userId
}

val chatRoomSummaryInfo = chatroomService.getByCenterWithCarer(
centerId = centerId,
carerId =carerId,
isCarer)

return if (isCarer) {
val center = centerService.getById(centerId)
chatRoomSummaryInfo.also {
it.opponentName = center.centerName
it.opponentProfileImageUrl = center.profileImageUrl
}

}else {
val carer = carerService.getById(carerId)
chatRoomSummaryInfo.also {
it.opponentName = carer.name
it.opponentProfileImageUrl = carer.profileImageUrl
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ class ChatRedisSubscriber(
val actualJson = objectMapper.readTree(rawJson).asText()
val jsonNode = objectMapper.readTree(actualJson)

when (jsonNode.get(ChatRedisPublisher.TYPE).asText()) {
ChatRedisPublisher.SEND_MESSAGE -> {
when (jsonNode.get(ChatRedisTemplate.TYPE).asText()) {
ChatRedisTemplate.SEND_MESSAGE -> {
val chatMessage: ChatMessage = objectMapper.treeToValue(
jsonNode.get(ChatRedisPublisher.DATA), ChatMessage::class.java
jsonNode.get(ChatRedisTemplate.DATA), ChatMessage::class.java
)

eventPublisher.publishEvent(chatMessage)
}
ChatRedisPublisher.READ_MESSAGE -> {
ChatRedisTemplate.READ_MESSAGE -> {
val readMessage: ReadMessage = objectMapper.treeToValue(
jsonNode.get(ChatRedisPublisher.DATA), ReadMessage::class.java
jsonNode.get(ChatRedisTemplate.DATA), ReadMessage::class.java
)

eventPublisher.publishEvent(readMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ import com.swm.idle.domain.chat.entity.jpa.ChatMessage
import com.swm.idle.domain.chat.vo.ReadMessage
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Component
import java.time.Duration
import java.util.*

@Component
class ChatRedisPublisher(
class ChatRedisTemplate(
private val redisTemplate: RedisTemplate<String, Any>,
private val objectMapper: ObjectMapper
) {
fun isChatting(userId: UUID): Boolean {
return redisTemplate.hasKey(userId.toString())
}

fun delete(userId: String) {
redisTemplate.delete(userId)
}

fun setSession(userId: String, duration: Duration) {
redisTemplate.opsForValue().set(userId,"active",duration)
}

fun publish(chatMessage: ChatMessage) {
val message = objectMapper.writeValueAsString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.swm.idle.domain.chat.repository

import com.swm.idle.domain.chat.entity.jpa.ChatMessage
import io.lettuce.core.dynamic.annotation.Param
import jakarta.transaction.Transactional
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
Expand All @@ -10,7 +11,8 @@ import java.util.*

@Repository
interface ChatMessageRepository : JpaRepository<ChatMessage, UUID> {
@Modifying
@Transactional
@Modifying(clearAutomatically = true)
@Query("""
UPDATE ChatMessage cm
SET cm.isRead = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ interface ChatRoomRepository : JpaRepository<ChatRoom, UUID> {

fun findByCarerIdAndCenterId(carerId: UUID, centerId: UUID): ChatRoom?

@Query("""
@Query(
"""
WITH FilteredChatRooms AS (
SELECT
cr.id AS chat_room_id,
Expand All @@ -29,6 +30,7 @@ interface ChatRoomRepository : JpaRepository<ChatRoom, UUID> {
FROM chat_message cm
WHERE cm.chat_room_id IN (SELECT chat_room_id FROM FilteredChatRooms)
AND cm.is_read = false
AND cm.receiverId = :userId
GROUP BY cm.chat_room_id
)

Expand All @@ -47,10 +49,12 @@ interface ChatRoomRepository : JpaRepository<ChatRoom, UUID> {
ORDER BY id DESC
LIMIT 1
) cm;
""", nativeQuery = true)
""", nativeQuery = true
)
fun carerFindChatRooms(@Param("userId") userId: UUID): List<ChatRoomSummaryInfoProjection>

@Query("""
@Query(
"""
WITH FilteredChatRooms AS (
SELECT
cr.id AS chat_room_id,
Expand All @@ -66,6 +70,7 @@ interface ChatRoomRepository : JpaRepository<ChatRoom, UUID> {
FROM chat_message cm
WHERE cm.chat_room_id IN (SELECT chat_room_id FROM FilteredChatRooms)
AND cm.is_read = false
AND cm.receiverId = :userId
GROUP BY cm.chat_room_id
)

Expand All @@ -84,85 +89,7 @@ interface ChatRoomRepository : JpaRepository<ChatRoom, UUID> {
ORDER BY id DESC
LIMIT 1
) cm;
""", nativeQuery = true)
fun centerFindChatRooms(@Param("userId") userId: UUID): List<ChatRoomSummaryInfoProjection>


@Query("""
WITH FilteredChatRooms AS (
SELECT
cr.id AS chat_room_id,
cr.center_id
FROM chat_room cr
WHERE cr.carer_id = :carerId
AND cr.center_id =:centerId
),

UnreadMessageCounts AS (
SELECT
cm.chat_room_id,
COUNT(*) AS unread_count
FROM chat_message cm
WHERE cm.chat_room_id IN (SELECT chat_room_id FROM FilteredChatRooms)
AND cm.is_read = false
GROUP BY cm.chat_room_id
""", nativeQuery = true
)

SELECT
fcr.chat_room_id AS chatRoomId,
fcr.center_id AS opponentId,
umc.unread_count AS unreadCount,
cm.content AS lastMessage,
cm.created_at AS lastMessageTime
FROM FilteredChatRooms fcr
JOIN UnreadMessageCounts umc ON fcr.chat_room_id = umc.chat_room_id
JOIN LATERAL (
SELECT content, created_at
FROM chat_message
WHERE chat_room_id = fcr.chat_room_id AND is_read = false
ORDER BY id DESC
LIMIT 1
) cm;
""", nativeQuery = true)
fun carerFindSingleChatRoom(@Param("centerId") centerId: UUID,
@Param("carerId") carerId: UUID): ChatRoomSummaryInfoProjection

@Query("""
WITH FilteredChatRooms AS (
SELECT
cr.id AS chat_room_id,
cr.carer_id
FROM chat_room cr
WHERE cr.carer_id = :carerId
AND cr.center_id =:centerId
),

UnreadMessageCounts AS (
SELECT
cm.chat_room_id,
COUNT(*) AS unread_count
FROM chat_message cm
WHERE cm.chat_room_id IN (SELECT chat_room_id FROM FilteredChatRooms)
AND cm.is_read = false
GROUP BY cm.chat_room_id
)

SELECT
fcr.chat_room_id AS chatRoomId,
fcr.carer_id AS opponentId,
umc.unread_count AS unreadCount,
cm.content AS lastMessage,
cm.created_at AS lastMessageTime
FROM FilteredChatRooms fcr
JOIN UnreadMessageCounts umc ON fcr.chat_room_id = umc.chat_room_id
JOIN LATERAL (
SELECT content, created_at
FROM chat_message
WHERE chat_room_id = fcr.chat_room_id AND is_read = false
ORDER BY id DESC
LIMIT 1
) cm;
""", nativeQuery = true)
fun centerFindSingleChatRoom(@Param("centerId") centerId: UUID,
@Param("carerId") carerId: UUID): ChatRoomSummaryInfoProjection
}
fun centerFindChatRooms(@Param("userId") userId: UUID): List<ChatRoomSummaryInfoProjection>
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,4 @@ interface ChatCarerApi {
@GetMapping("/chatrooms")
@ResponseStatus(HttpStatus.OK)
fun carerChatroomSummary(): List<ChatRoomSummaryInfo>

@Secured
@Operation(summary = "보호사의 단일 채팅방 정보 조회 API")
@GetMapping("/chatrooms/{chatroom-id}/opponent/{opponent-id}")
@ResponseStatus(HttpStatus.OK)
fun carerSingleChatroomSummary(@PathVariable(value = "chatroom-id") chatroomId: UUID,
@PathVariable(value = "opponent-id") opponentId: UUID,): ChatRoomSummaryInfo
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,4 @@ interface ChatCenterApi {
@GetMapping("/chatrooms")
@ResponseStatus(HttpStatus.OK)
fun centerChatroomSummary(): List<ChatRoomSummaryInfo>

@Secured
@Operation(summary = "센터장의 단일 채팅방 정보 조회 API")
@GetMapping("/chatrooms/{chatroom-id}/opponent/{opponent-id}")
@ResponseStatus(HttpStatus.OK)
fun carerSingleChatroomSummary(@PathVariable(value = "chatroom-id") chatroomId: UUID,
@PathVariable(value = "opponent-id") opponentId: UUID): ChatRoomSummaryInfo
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package com.swm.idle.presentation.chat.config

import com.swm.idle.application.common.properties.JwtTokenProperties
import com.swm.idle.domain.chat.event.ChatRedisTemplate
import com.swm.idle.support.security.util.JwtTokenProvider
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.http.server.ServletServerHttpRequest
import org.springframework.stereotype.Component
import org.springframework.web.socket.WebSocketHandler
import org.springframework.web.socket.server.HandshakeInterceptor
import java.time.Duration

@Component
class ChatHandshakeInterceptor(
private val jwtTokenProperties: JwtTokenProperties,
private val redisTemplate : ChatRedisTemplate,
): HandshakeInterceptor {

override fun beforeHandshake(
Expand All @@ -29,7 +32,11 @@ class ChatHandshakeInterceptor(
return false
}

attributes["userId"] = claims.customClaims["userId"] as String
val userId = claims.customClaims["userId"] as String
attributes["userId"] = userId

redisTemplate.setSession(userId, Duration.ofHours(24))

return true
}
return false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.swm.idle.presentation.chat.config

import com.swm.idle.domain.chat.event.ChatRedisTemplate
import org.springframework.context.event.EventListener
import org.springframework.messaging.simp.stomp.StompHeaderAccessor
import org.springframework.stereotype.Component
import org.springframework.web.socket.messaging.SessionDisconnectEvent

@Component
class WebSocketDisconnectListener(
private val redisTemplate: ChatRedisTemplate
) {

@EventListener
fun handleDisconnect(event: SessionDisconnectEvent) {
val headers = StompHeaderAccessor.wrap(event.message)
val userId = headers.sessionAttributes?.get("userId") as? String

if (userId != null) {
redisTemplate.delete(userId)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,4 @@ class ChatCarerController(
override fun recentMessages(chatroomId: UUID, messageId: UUID?): List<ChatMessageResponse> {
return chatMessageService.getRecentMessages(chatroomId, messageId)
}

override fun carerSingleChatroomSummary(chatroomId: UUID, opponentId: UUID): ChatRoomSummaryInfo {
return chatMessageService.getSingleChatRoomInfo(chatroomId,opponentId, true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,4 @@ class ChatCenterController(
override fun recentMessages(chatroomId: UUID, messageId: UUID?): List<ChatMessageResponse> {
return chatMessageService.getRecentMessages(chatroomId, messageId)
}

override fun carerSingleChatroomSummary(chatroomId: UUID, opponentId: UUID): ChatRoomSummaryInfo {
return chatMessageService.getSingleChatRoomInfo(chatroomId, opponentId, false)
}
}