Skip to content

#155 - 개별 쪽지 삭제 API를 구현합니다. #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
@@ -0,0 +1,23 @@
package com.wespot.message.v2

import com.wespot.message.port.`in`.DeleteMessageV2UseCase
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/v2/messages")
class DeleteMessageV2Controller(
private val deleteMessageV2UseCase: DeleteMessageV2UseCase
) {

@DeleteMapping("/{messageId}")
fun deleteMessage(@PathVariable messageId: Long): ResponseEntity<Unit> {
deleteMessageV2UseCase.deleteMessage(messageId = messageId)

return ResponseEntity.noContent()
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ data class MessageV2DetailsResponse(
name = room.receiverName(),
messageRoomId = room.id(),
isBookmarked = room.isBookmarked(),
messageDetails = room.messages.asList()
messageDetails = room.messageDetailsAsList()
.map { MessageDetailResponse.from(it) }
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.wespot.message.port.`in`

interface DeleteMessageV2UseCase {

fun deleteMessage(messageId: Long)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.wespot.message.service.v2

import com.wespot.auth.service.SecurityUtils
import com.wespot.message.port.`in`.DeleteMessageV2UseCase
import com.wespot.message.port.out.MessageV2Port
import com.wespot.message.v2.MessageRoom
import com.wespot.message.v2.MessageV2
import com.wespot.user.port.out.UserPort
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class DeleteMessageV2Service(
private val userPort: UserPort,
private val messageV2Port: MessageV2Port
) : DeleteMessageV2UseCase {

@Transactional
override fun deleteMessage(messageId: Long) {
val loginUser = SecurityUtils.getLoginUser(userPort = userPort)

val message = messageV2Port.findById(id = messageId)
val allMessagesOfRoom = getAllMessagesOfRoom(message)

val room = MessageRoom.of(
viewer = loginUser,
allMessagesOfRoom = allMessagesOfRoom,
)
val deleteMessage = room.deleteMessage(messageId = messageId)
messageV2Port.save(messageV2 = deleteMessage)
}

private fun getAllMessagesOfRoom(message: MessageV2): List<MessageV2> {
if (message.isRoom()) {
return listOf(message) + messageV2Port.findAllByMessageRoomId(message.id)
}

val messageRoom = messageV2Port.findById(id = message.messageRoomId!!)
return listOf(messageRoom) + messageV2Port.findAllByMessageRoomId(messageRoom.id)
}

}
8 changes: 8 additions & 0 deletions domain/src/main/kotlin/com/wespot/message/v2/MessageDetail.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,12 @@ data class MessageDetail(
return message.answerMessage(viewer = sender, content = content)
}

fun delete(deleter: User): MessageV2 {
return message.delete(deleter = deleter)
}

fun isNotDeleted(viewer: User): Boolean {
return message.isDeleted(viewer = viewer)
}

}
18 changes: 16 additions & 2 deletions domain/src/main/kotlin/com/wespot/message/v2/MessageDetails.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ data class MessageDetails(
return messages.map { it.chatTime() }
}

fun asList(): List<MessageDetail> {
return messages
fun asList(viewer: User): List<MessageDetail> {
return asExcludeDeleteMessageDetails(viewer = viewer)
}

fun isAbleToAnswer(): Boolean {
Expand All @@ -51,4 +51,18 @@ data class MessageDetails(
return toAnswerMessage.createAnswerMessage(sender = sender, content = content)
}

fun deleteMessage(viewer: User, messageId: Long): MessageV2 {
val toDeleteMessage = messages.find { it.message.id == messageId } ?: throw CustomException(
message = "삭제하려는 쪽지를 찾을 수 없습니다.",
status = HttpStatus.BAD_REQUEST,
view = ExceptionView.TOAST,
)

return toDeleteMessage.delete(deleter = viewer)
}

private fun asExcludeDeleteMessageDetails(viewer: User): List<MessageDetail> {
return messages.filter { it.isNotDeleted(viewer = viewer) }
}

}
48 changes: 48 additions & 0 deletions domain/src/main/kotlin/com/wespot/message/v2/MessageRoom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,33 @@ data class MessageRoom(
)
}

fun of(viewer: User, allMessagesOfRoom: List<MessageV2>): MessageRoom {
val roomMessage = allMessagesOfRoom.find { it.isRoom() } ?: throw CustomException(
message = "쪽지 방이 존재하지 않습니다.",
status = HttpStatus.BAD_REQUEST,
view = ExceptionView.TOAST,
)

if (!roomMessage.isAbleToView(viewer = viewer)) {
throw CustomException(
message = "해당 쪽지 방을 볼 수 있는 권한이 존재하지 않습니다.",
status = HttpStatus.FORBIDDEN,
view = ExceptionView.TOAST,
)
}

val messageDetails = allMessagesOfRoom.filter { !it.isRoom() }

return MessageRoom(
viewer = viewer,
roomMessage = roomMessage,
messages = MessageDetails.of(
viewer = viewer,
messages = listOf(roomMessage) + messageDetails.filter { it.isContainsOf(roomMessage) }
)
)
}

}

fun isExistsUnReadMessage(): Boolean {
Expand Down Expand Up @@ -129,4 +156,25 @@ data class MessageRoom(
return messages.answer(sender = sender, content = validatedContent)
}

fun deleteMessage(messageId: Long): MessageV2 {
val excludedDeletedMessage = messageDetailsAsList()

if (excludedDeletedMessage.size <= 1) {
throw CustomException(
message = "해당 쪽지를 삭제하면 쪽지 방에 남은 쪽지가 존재하지 않습니다.",
status = HttpStatus.BAD_REQUEST,
view = ExceptionView.TOAST,
)
}

return messages.deleteMessage(
viewer = viewer,
messageId = messageId
)
}

fun messageDetailsAsList(): List<MessageDetail> {
return messages.asList(viewer = viewer)
}

}
40 changes: 36 additions & 4 deletions domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.wespot.message.event.MessageAnswerEvent
import com.wespot.user.User
import com.wespot.user.event.UsedAnswerFeatureEvent
import com.wespot.user.message.AnonymousProfile
import jakarta.persistence.Id
import org.springframework.http.HttpStatus
import java.time.LocalDateTime

Expand All @@ -25,11 +26,11 @@ data class MessageV2(
val createdAt: LocalDateTime,
val updatedAt: LocalDateTime,

val isSenderDeleted: Boolean,
val senderDeletedAt: LocalDateTime?,
var isSenderDeleted: Boolean,
var senderDeletedAt: LocalDateTime?,

val isReceiverDeleted: Boolean,
val receiverDeletedAt: LocalDateTime?,
var isReceiverDeleted: Boolean,
var receiverDeletedAt: LocalDateTime?,

val messageRoomId: Long?,
val messageRoomOwnerId: Long,
Expand Down Expand Up @@ -363,6 +364,37 @@ data class MessageV2(
isReceiverRead = true
}

fun delete(deleter: User): MessageV2 {
val alreadyDeleted = isDeleted(viewer = deleter)

if (alreadyDeleted) {
throw CustomException(
message = "이미 삭제된 쪽지입니다.",
status = HttpStatus.BAD_REQUEST,
view = ExceptionView.TOAST,
)
}

if (deleter.isMeSender(senderId = sender.id)) {
isSenderDeleted = true
senderDeletedAt = senderDeletedAt ?: LocalDateTime.now()
return this
}

isReceiverDeleted = true
receiverDeletedAt = receiverDeletedAt ?: LocalDateTime.now()
return this
}

fun isDeleted(viewer: User): Boolean {
if (viewer.isMeSender(senderId = sender.id)) {
return isSenderDeleted
}

return isReceiverDeleted
}


override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is MessageV2) return false
Expand Down
Loading