Skip to content

Commit bf07779

Browse files
authored
[BE] fix: 생성, 수정로직 도메인으로 이동 (#127)
* [BE] fix: 생성, 수정로직 도메인으로 이동 (#124) * [BE] fix: isNew 필드 추가하여 ID 생성 후 전달시 createAt null 해결 (#124) * [BE] fix: Persistable 로직 제거, 수정시간을 생성시간으로 변경하여 해결 (#124) * [BE] fix: FileUpload, Paging route 추가 (#124) * [BE] fix: 타입 소문자로 가는 오류 해결 (#124) * [BE] fix: 잘못된 라우팅 수정 (#124) * [BE] refactor: ktlintformat (#124) * [BE] fix: RequestMissing Error Handle 추가 (#124) * [BE] feat: ChatServer LoggingAspect 추가 (#124) * [BE] fix: 잘못된 apigateway reoute 수정 (#124) * [BE] fix: DirectMessage deleted 미반영 수정 (#124) * [BE] fix: 수정시 기존id 그대로 사용하도록 변경 (#124)
2 parents 01439a0 + 4e096bd commit bf07779

File tree

13 files changed

+220
-55
lines changed

13 files changed

+220
-55
lines changed

src/backend/apigateway-server/src/main/resources/application-prod.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ spring:
105105
- Path=/chats/**
106106
filters:
107107
- RemoveRequestHeader=Cookie
108-
- RewritePath=/users/(?<segment>.*), /$\{segment}
108+
- RewritePath=/chats/(?<segment>.*), /$\{segment}
109109
- AuthorizationHeaderFilter
110110

111111
default-filters:

src/backend/chat-server/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ dependencies {
3535
implementation("org.springframework.boot:spring-boot-starter-web")
3636
implementation("org.springframework.boot:spring-boot-starter-actuator")
3737

38+
implementation("org.springframework.boot:spring-boot-starter-aop")
39+
3840
implementation("org.springframework.cloud:spring-cloud-starter")
3941
implementation("org.springframework.cloud:spring-cloud-starter-config")
4042
implementation("org.springframework.cloud:spring-cloud-starter-bootstrap")

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectController.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class DirectController(
7070
}
7171

7272
@ResponseBody
73-
@GetMapping("chat/direct")
73+
@GetMapping("/chat/direct")
7474
override fun readPaging(
7575
@RequestParam("page", defaultValue = "0") page: Int,
7676
@RequestParam("size", defaultValue = "10") size: Int,

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectRequest.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import java.io.Serializable
1212
@JsonSerialize
1313
@JsonDeserialize
1414
data class DirectMessageCreate(
15-
@Schema(description = "채널 ID, 다이렉트 채팅방 생성 후 id. DirectId와 같음", example = "channel-12345")
15+
@Schema(description = "채널 ID", example = "channel-12345")
1616
val channelId: String,
1717

1818
@Schema(description = "프로필 이미지 URL", example = "http://example.com/image.png")
@@ -24,20 +24,21 @@ data class DirectMessageCreate(
2424
@Schema(description = "메시지 내용", example = "Hello!")
2525
val content: String,
2626

27-
@Schema(description = "썸네일 URL", example = "http://example.com/thumbnail.png", nullable = true)
27+
@Schema(description = "썸네일 URL", nullable = true)
2828
val thumbnail: String? = null,
2929

30-
@Schema(description = "부모 메시지 ID", example = "msg-12345", nullable = true)
30+
@Schema(description = "부모 메시지 ID", nullable = true)
3131
val parentId: String? = null,
3232

33-
@Schema(description = "부모 메시지 작성자 이름", example = "Jane Doe", nullable = true)
33+
@Schema(description = "부모 메시지 작성자 이름", nullable = true)
3434
val parentName: String? = null,
3535

36-
@Schema(description = "부모 메시지 내용", example = "Previous message content", nullable = true)
36+
@Schema(description = "부모 메시지 내용", nullable = true)
3737
val parentContent: String? = null,
3838
) : Serializable {
39+
3940
fun toDomain(userId: String): DirectMessage {
40-
return DirectMessage(
41+
return DirectMessage.create(
4142
channelId = channelId,
4243
userId = userId,
4344
type = DirectMessageType.CREATE,
@@ -70,6 +71,7 @@ data class DirectMessageEditRequest(
7071
) : Serializable {
7172
fun toDomain(userId: String): DirectMessage {
7273
return DirectMessage(
74+
id = id,
7375
channelId = channelId,
7476
userId = userId,
7577
type = DirectMessageType.EDIT,
@@ -113,7 +115,7 @@ data class DirectMessageTypingRequest(
113115
val name: String,
114116
) : Serializable {
115117
fun toDomain(userId: String): DirectMessage {
116-
return DirectMessage(
118+
return DirectMessage.create(
117119
channelId = channelId,
118120
userId = userId,
119121
name = name,
@@ -151,7 +153,7 @@ data class FileRequest(
151153
val fileType: DirectMessageType,
152154
) : Serializable {
153155
fun toDomain(userId: String, type: DirectMessageType): DirectMessage {
154-
return DirectMessage(
156+
return DirectMessage.create(
155157
userId = userId,
156158
name = name,
157159
profileImage = profileImage,

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/domain/DirectMessage.kt

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,99 @@
11
package com.asyncgate.chat_server.domain
22

3+
import com.asyncgate.chat_server.support.utility.IdGenerator
34
import java.time.LocalDateTime
45

56
class DirectMessage(
6-
val id: String? = null,
7-
7+
val id: String,
88
val channelId: String,
99
val userId: String,
1010
val type: DirectMessageType,
11-
1211
val profileImage: String? = null,
13-
1412
val read: Map<Long, Boolean>? = null,
15-
1613
val name: String? = null,
1714
val content: String? = null,
1815
val thumbnail: String? = null,
19-
2016
val parentId: String? = null,
2117
val parentName: String? = null,
2218
val parentContent: String? = null,
23-
2419
val createdAt: LocalDateTime? = null,
25-
val updatedAt: LocalDateTime? = null,
20+
var updatedAt: LocalDateTime? = null,
21+
var isDeleted: Boolean = false,
2622
) {
23+
companion object {
24+
fun create(
25+
channelId: String,
26+
userId: String,
27+
type: DirectMessageType,
28+
profileImage: String? = null,
29+
name: String? = null,
30+
content: String? = null,
31+
thumbnail: String? = null,
32+
parentId: String? = null,
33+
parentName: String? = null,
34+
parentContent: String? = null,
35+
): DirectMessage {
36+
return DirectMessage(
37+
id = IdGenerator.generate(),
38+
channelId = channelId,
39+
userId = userId,
40+
type = type,
41+
profileImage = profileImage,
42+
name = name,
43+
content = content,
44+
thumbnail = thumbnail,
45+
parentId = parentId,
46+
parentName = parentName,
47+
parentContent = parentContent,
48+
isDeleted = false
49+
)
50+
}
51+
}
52+
53+
/**
54+
* 현재 메시지를 "삭제" 상태로 변경한 새로운 도메인 객체 반환.
55+
* (실제 삭제 플래그는 엔티티 변환 시 toEntity()에서 처리)
56+
*/
57+
fun markDeleted(): DirectMessage {
58+
return DirectMessage(
59+
id = this.id,
60+
channelId = this.channelId,
61+
userId = this.userId,
62+
type = this.type,
63+
profileImage = this.profileImage,
64+
read = this.read,
65+
name = this.name,
66+
content = this.content,
67+
thumbnail = this.thumbnail,
68+
parentId = this.parentId,
69+
parentName = this.parentName,
70+
parentContent = this.parentContent,
71+
isDeleted = true
72+
)
73+
}
74+
75+
/**
76+
* 현재 메시지를 수정하여, 새로운 메시지(수정본)를 생성.
77+
* 새로운 메시지 type은 EDIT로 변경됨.
78+
*/
79+
fun withEdit(newName: String, newContent: String): DirectMessage {
80+
return DirectMessage(
81+
id = this.id,
82+
channelId = this.channelId,
83+
userId = this.userId,
84+
type = DirectMessageType.EDIT,
85+
profileImage = this.profileImage,
86+
read = this.read,
87+
name = newName,
88+
content = newContent,
89+
thumbnail = this.thumbnail,
90+
parentId = this.parentId,
91+
parentName = this.parentName,
92+
parentContent = this.parentContent,
93+
isDeleted = false
94+
)
95+
}
96+
2797
override fun toString(): String {
2898
return "DirectMessage(" +
2999
"id=$id, " +

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/entity/DirectMessageEntity.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package com.asyncgate.chat_server.entity
22

33
import com.asyncgate.chat_server.domain.DirectMessageType
4-
import com.asyncgate.chat_server.support.utility.IdGenerator
54
import org.springframework.data.annotation.Id
65
import org.springframework.data.mongodb.core.mapping.Document
76

87
@Document(collection = "directMessage")
98
data class DirectMessageEntity(
10-
@get:Id
11-
val id: String = IdGenerator.generate(),
9+
@Id
10+
val id: String,
1211

1312
val channelId: String,
1413
val userId: String,

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/entity/MongoBaseTimeEntity.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.asyncgate.chat_server.entity
22

3-
import org.springframework.data.annotation.CreatedDate
43
import org.springframework.data.annotation.LastModifiedDate
54
import java.time.LocalDateTime
65

76
abstract class MongoBaseTimeEntity {
87

9-
@CreatedDate
8+
@LastModifiedDate
109
var createdAt: LocalDateTime? = null
1110

1211
@LastModifiedDate

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/exception/ChatServerErrorHandler.kt

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.asyncgate.chat_server.support.response.FailResponse
44
import org.slf4j.Logger
55
import org.slf4j.LoggerFactory
66
import org.springframework.http.ResponseEntity
7+
import org.springframework.web.bind.MissingServletRequestParameterException
78
import org.springframework.web.bind.annotation.ControllerAdvice
89
import org.springframework.web.bind.annotation.ExceptionHandler
910
import org.springframework.web.servlet.resource.NoResourceFoundException
@@ -23,12 +24,32 @@ class ChatServerErrorHandler {
2324
return ResponseEntity.status(errorType.status).body(response)
2425
}
2526

27+
@ExceptionHandler(NoResourceFoundException::class)
28+
fun handleResourceException(exception: NoResourceFoundException): ResponseEntity<FailResponse> {
29+
log.error("🚨 [Global Error] Resource not found: ${exception.message}", exception)
30+
val errorType = FailType.RESOURCE_NOT_FOUND
31+
val response: FailResponse = FailResponse.of(
32+
errorType.errorCode,
33+
errorType.message,
34+
errorType.status.value()
35+
)
36+
return ResponseEntity.status(errorType.status).body(response)
37+
}
38+
39+
@ExceptionHandler(MissingServletRequestParameterException::class)
40+
fun handleMissingRequestParamException(e: MissingServletRequestParameterException): ResponseEntity<FailResponse> {
41+
log.error("🚨 [Global Error] Missing request parameter: ${e.parameterName}", e)
42+
val errorType = FailType.REQUEST_PARAMETER_MISSING
43+
val response: FailResponse = FailResponse.of(
44+
errorType.errorCode,
45+
errorType.message,
46+
errorType.status.value()
47+
)
48+
return ResponseEntity.status(errorType.status).body(response)
49+
}
50+
2651
@ExceptionHandler(Exception::class)
2752
fun handleException(exception: Exception) {
2853
log.error("🚨 [Global Error] ${exception.message}", exception)
2954
}
30-
31-
@ExceptionHandler(NoResourceFoundException::class)
32-
fun handleResourceException(exception: NoResourceFoundException) {
33-
}
3455
}

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/exception/FailType.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ enum class FailType(
3030

3131
DIRECT_MESSAGE_BAD_REQUEST(HttpStatus.BAD_REQUEST, "DirectMessage_4002", "IMAGE가 필수입니다."),
3232
DIRECT_MESSAGE_CONTENT_NULL(HttpStatus.BAD_REQUEST, "DirectMessage_4002", "CODE, SNIPPET 타입은 content가 필수입니다."),
33+
REQUEST_PARAMETER_MISSING(HttpStatus.BAD_REQUEST, "Request_4001", "필수 요청 파라미터가 누락되었습니다."),
34+
RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "Resource_4040", "요청한 리소스를 찾을 수 없습니다."),
3335
;
3436
}

src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/service/DirectListener.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package com.asyncgate.chat_server.service
33
import com.asyncgate.chat_server.domain.DirectMessage
44
import com.asyncgate.chat_server.domain.DirectMessageType
55
import com.asyncgate.chat_server.domain.ReadStatus
6-
import com.asyncgate.chat_server.exception.ChatServerException
7-
import com.asyncgate.chat_server.exception.FailType
86
import com.fasterxml.jackson.databind.ObjectMapper
97
import org.slf4j.Logger
108
import org.slf4j.LoggerFactory
@@ -32,13 +30,13 @@ class DirectListener(
3230
log.info("directMessage = $directMessage")
3331

3432
val msg = HashMap<String, String>()
35-
msg["type"] = DirectMessageType.CREATE.toString().lowercase()
33+
msg["type"] = DirectMessageType.CREATE.toString()
3634
msg["userId"] = java.lang.String.valueOf(directMessage.userId)
3735
msg["name"] = directMessage.name ?: ""
3836
msg["profileImage"] = directMessage.profileImage ?: ""
3937
msg["message"] = directMessage.content ?: ""
4038
msg["time"] = java.lang.String.valueOf(directMessage.createdAt)
41-
msg["id"] = directMessage.id ?: throw ChatServerException(FailType.X_DIRECT_INTERNAL_ERROR)
39+
msg["id"] = directMessage.id
4240

4341
val serializable = objectMapper.writeValueAsString(msg)
4442
template.convertAndSend("/topic/direct-message/" + directMessage.channelId, serializable)
@@ -85,13 +83,14 @@ class DirectListener(
8583

8684
when (directMessage.type) {
8785
DirectMessageType.EDIT -> {
86+
msg["id"] = directMessage.id
8887
msg["type"] = DirectMessageType.EDIT.toString()
8988
msg["userId"] = directMessage.userId
9089
msg["channelId"] = directMessage.channelId
9190
msg["content"] = directMessage.content ?: ""
9291
}
9392
DirectMessageType.DELETE -> {
94-
msg["id"] = directMessage.id ?: throw ChatServerException(FailType.X_DIRECT_INTERNAL_ERROR)
93+
msg["id"] = directMessage.id
9594
msg["type"] = DirectMessageType.DELETE.toString()
9695
msg["userId"] = directMessage.userId
9796
msg["channelId"] = directMessage.channelId

0 commit comments

Comments
 (0)