diff --git a/.gitignore b/.gitignore index 5a979af..ea09a5f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ out/ ### Kotlin ### .kotlin + +### claude ### +CLAUDE.md diff --git a/Application-Module/src/main/kotlin/com/asap/application/letter/port/in/SendLetterUsecase.kt b/Application-Module/src/main/kotlin/com/asap/application/letter/port/in/SendLetterUsecase.kt index ede1b7d..ef444e6 100644 --- a/Application-Module/src/main/kotlin/com/asap/application/letter/port/in/SendLetterUsecase.kt +++ b/Application-Module/src/main/kotlin/com/asap/application/letter/port/in/SendLetterUsecase.kt @@ -15,6 +15,7 @@ interface SendLetterUsecase { ) data class AnonymousCommand( + val senderName: String? = null, val receiverName: String, val content: String, val images: List, diff --git a/Application-Module/src/main/kotlin/com/asap/application/letter/service/LetterCommandService.kt b/Application-Module/src/main/kotlin/com/asap/application/letter/service/LetterCommandService.kt index 16646c2..61a912f 100644 --- a/Application-Module/src/main/kotlin/com/asap/application/letter/service/LetterCommandService.kt +++ b/Application-Module/src/main/kotlin/com/asap/application/letter/service/LetterCommandService.kt @@ -70,6 +70,7 @@ class LetterCommandService( letterCodeGenerator.generateCode( content = command.content, ), + senderName = command.senderName ?: ANONYMOUS_SENDER_NAME, ) sendLetterManagementPort.save(sendLetter) @@ -111,9 +112,10 @@ class LetterCommandService( SenderInfo( senderId = sendLetter.senderId, senderName = - sendLetter.senderId - ?.let { userManagementPort.getUserNotNull(it).username } - .orEmpty(), + sendLetter.senderName + ?: sendLetter.senderId + ?.let { userManagementPort.getUserNotNull(it).username } + .orEmpty(), ), receiver = ReceiverInfo( @@ -273,4 +275,8 @@ class LetterCommandService( ) spaceLetterManagementPort.save(spaceLetter) } + + companion object { + private const val ANONYMOUS_SENDER_NAME = "Anonymous" + } } diff --git a/Application-Module/src/test/kotlin/com/asap/application/letter/service/LetterCommandServiceTest.kt b/Application-Module/src/test/kotlin/com/asap/application/letter/service/LetterCommandServiceTest.kt index 0ba460f..91aae3d 100644 --- a/Application-Module/src/test/kotlin/com/asap/application/letter/service/LetterCommandServiceTest.kt +++ b/Application-Module/src/test/kotlin/com/asap/application/letter/service/LetterCommandServiceTest.kt @@ -75,6 +75,52 @@ class LetterCommandServiceTest : verify { mockSendLetterManagementPort.save(any()) } } } + + val commandWithSenderName = + SendLetterUsecase.AnonymousCommand( + senderName = "Test Sender", + receiverName = "receiver-name", + content = "content", + images = emptyList(), + templateType = 1, + ) + `when`("발송자 이름이 제공된 익명 편지 전송 요청을 처리하면") { + val response = letterCommandService.sendAnonymous(commandWithSenderName) + then("편지 코드가 생성되고, 제공된 발송자 이름으로 편지가 저장되어야 한다") { + response.letterCode shouldNotBeNull { + this.isNotBlank() + this.isNotEmpty() + } + verify { + mockSendLetterManagementPort.save(match { sendLetter -> + sendLetter.senderName == "Test Sender" + }) + } + } + } + + val commandWithNullSenderName = + SendLetterUsecase.AnonymousCommand( + senderName = null, + receiverName = "receiver-name", + content = "content", + images = emptyList(), + templateType = 1, + ) + `when`("발송자 이름이 null인 익명 편지 전송 요청을 처리하면") { + val response = letterCommandService.sendAnonymous(commandWithNullSenderName) + then("편지 코드가 생성되고, Anonymous로 편지가 저장되어야 한다") { + response.letterCode shouldNotBeNull { + this.isNotBlank() + this.isNotEmpty() + } + verify { + mockSendLetterManagementPort.save(match { sendLetter -> + sendLetter.senderName == "Anonymous" + }) + } + } + } } given("편지 검증 시에") { @@ -381,6 +427,7 @@ class LetterCommandServiceTest : content = content, receiverName = "receiverName", letterCode = letterCode, + senderName = "Anonymous", ) every { diff --git a/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/controller/LetterController.kt b/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/controller/LetterController.kt index 3496ba7..7b47e77 100644 --- a/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/controller/LetterController.kt +++ b/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/controller/LetterController.kt @@ -270,6 +270,7 @@ class LetterController( val response = sendLetterUsecase.sendAnonymous( SendLetterUsecase.AnonymousCommand( + senderName = request.senderName, receiverName = request.receiverName, content = request.content, images = request.images, diff --git a/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/dto/AnonymousSendLetterRequest.kt b/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/dto/AnonymousSendLetterRequest.kt index 147bf26..180f69b 100644 --- a/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/dto/AnonymousSendLetterRequest.kt +++ b/Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/dto/AnonymousSendLetterRequest.kt @@ -1,6 +1,7 @@ package com.asap.bootstrap.web.letter.dto data class AnonymousSendLetterRequest( + val senderName: String? = null, val receiverName: String, val content: String, val images: List, diff --git a/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/acceptance/letter/controller/LetterControllerTest.kt b/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/acceptance/letter/controller/LetterControllerTest.kt index 6a6ec16..93bd6a1 100644 --- a/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/acceptance/letter/controller/LetterControllerTest.kt +++ b/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/acceptance/letter/controller/LetterControllerTest.kt @@ -626,4 +626,84 @@ class LetterControllerTest : LetterAcceptanceSupporter() { } } } + + @Test + fun sendAnonymousLetterWithSenderName() { + // given + val request = + AnonymousSendLetterRequest( + senderName = "Test Sender", + receiverName = "receiverName", + content = "content", + images = listOf("images"), + templateType = 1, + ) + BDDMockito + .given( + sendLetterUsecase.sendAnonymous( + SendLetterUsecase.AnonymousCommand( + senderName = request.senderName, + receiverName = request.receiverName, + content = request.content, + images = request.images, + templateType = request.templateType, + ), + ), + ).willReturn(SendLetterUsecase.Response("letterCode")) + // when + val response = + mockMvc.post("/api/v1/letters/anonymous/send") { + contentType = MediaType.APPLICATION_JSON + content = objectMapper.writeValueAsString(request) + } + // then + response.andExpect { + status { isOk() } + jsonPath("$.letterCode") { + exists() + isString() + isNotEmpty() + } + } + } + + @Test + fun sendAnonymousLetterWithNullSenderName() { + // given + val request = + AnonymousSendLetterRequest( + senderName = null, + receiverName = "receiverName", + content = "content", + images = listOf("images"), + templateType = 1, + ) + BDDMockito + .given( + sendLetterUsecase.sendAnonymous( + SendLetterUsecase.AnonymousCommand( + senderName = null, + receiverName = request.receiverName, + content = request.content, + images = request.images, + templateType = request.templateType, + ), + ), + ).willReturn(SendLetterUsecase.Response("letterCode")) + // when + val response = + mockMvc.post("/api/v1/letters/anonymous/send") { + contentType = MediaType.APPLICATION_JSON + content = objectMapper.writeValueAsString(request) + } + // then + response.andExpect { + status { isOk() } + jsonPath("$.letterCode") { + exists() + isString() + isNotEmpty() + } + } + } } diff --git a/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/letter/LetterApiIntegrationTest.kt b/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/letter/LetterApiIntegrationTest.kt index 901324c..58e903f 100644 --- a/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/letter/LetterApiIntegrationTest.kt +++ b/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/letter/LetterApiIntegrationTest.kt @@ -3,7 +3,13 @@ package com.asap.bootstrap.integration.letter import com.asap.application.letter.LetterMockManager import com.asap.application.space.SpaceMockManager import com.asap.bootstrap.IntegrationSupporter -import com.asap.bootstrap.web.letter.dto.* +import com.asap.bootstrap.web.letter.dto.AddPhysicalLetterRequest +import com.asap.bootstrap.web.letter.dto.AddVerifiedLetterRequest +import com.asap.bootstrap.web.letter.dto.AnonymousSendLetterRequest +import com.asap.bootstrap.web.letter.dto.DeleteSendLettersRequest +import com.asap.bootstrap.web.letter.dto.LetterVerifyRequest +import com.asap.bootstrap.web.letter.dto.ModifyLetterRequest +import com.asap.bootstrap.web.letter.dto.SendLetterRequest import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -1082,30 +1088,92 @@ class LetterApiIntegrationTest : IntegrationSupporter() { } } - @Test - @DisplayName("비회원 편지 쓰기") - fun sendAnonymousLetter() { - // given - val request = - AnonymousSendLetterRequest( - receiverName = "receiverName", - content = "content", - images = listOf("images"), - templateType = 1, - ) - // when - val response = - mockMvc.post("/api/v1/letters/anonymous/send") { - contentType = MediaType.APPLICATION_JSON - content = objectMapper.writeValueAsString(request) + @Nested + @DisplayName("익명 편지 전송") + inner class SendAnonymousLetter { + @Test + @DisplayName("발송자 이름 없이 익명 편지 전송") + fun sendAnonymousLetter() { + // given + val request = + AnonymousSendLetterRequest( + receiverName = "receiverName", + content = "content", + images = listOf("images"), + templateType = 1, + ) + // when + val response = + mockMvc.post("/api/v1/letters/anonymous/send") { + contentType = MediaType.APPLICATION_JSON + content = objectMapper.writeValueAsString(request) + } + // then + response.andExpect { + status { isOk() } + jsonPath("$.letterCode") { + exists() + isString() + isNotEmpty() + } } - // then - response.andExpect { - status { isOk() } - jsonPath("$.letterCode") { - exists() - isString() - isNotEmpty() + } + + @Test + @DisplayName("발송자 이름과 함께 익명 편지 전송") + fun sendAnonymousLetterWithSenderName() { + // given + val request = + AnonymousSendLetterRequest( + senderName = "Test Sender", + receiverName = "receiverName", + content = "content", + images = listOf("images"), + templateType = 1, + ) + // when + val response = + mockMvc.post("/api/v1/letters/anonymous/send") { + contentType = MediaType.APPLICATION_JSON + content = objectMapper.writeValueAsString(request) + } + // then + response.andExpect { + status { isOk() } + jsonPath("$.letterCode") { + exists() + isString() + isNotEmpty() + } + } + } + + @Test + @DisplayName("발송자 이름이 null인 익명 편지 전송") + fun sendAnonymousLetterWithNullSenderName() { + // given + val request = + AnonymousSendLetterRequest( + senderName = null, + receiverName = "receiverName", + content = "content", + images = listOf("images"), + templateType = 1, + ) + // when + val response = + mockMvc.post("/api/v1/letters/anonymous/send") { + contentType = MediaType.APPLICATION_JSON + content = objectMapper.writeValueAsString(request) + } + // then + response.andExpect { + status { isOk() } + jsonPath("$.letterCode") { + exists() + isString() + isNotEmpty() + } } } } diff --git a/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/user/UserApiIntegrationTest.kt b/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/user/UserApiIntegrationTest.kt index 6c32702..fdf5df2 100644 --- a/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/user/UserApiIntegrationTest.kt +++ b/Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/integration/user/UserApiIntegrationTest.kt @@ -332,7 +332,7 @@ class UserApiIntegrationTest( content = letterContent, receiverName = receiverName, letterCode = "test-letter-code", - status = LetterStatus.SENDING, + senderName = "Anonymous", ) sendLetterManagementPort.save(anonymousSendLetter) diff --git a/Domain-Module/src/main/kotlin/com/asap/domain/letter/entity/SendLetter.kt b/Domain-Module/src/main/kotlin/com/asap/domain/letter/entity/SendLetter.kt index 0fdf191..4851949 100644 --- a/Domain-Module/src/main/kotlin/com/asap/domain/letter/entity/SendLetter.kt +++ b/Domain-Module/src/main/kotlin/com/asap/domain/letter/entity/SendLetter.kt @@ -13,6 +13,7 @@ class SendLetter( id: DomainId, val content: LetterContent, var senderId: DomainId?, + var senderName: String?, var receiverName: String, var letterCode: String?, var status: LetterStatus, @@ -37,6 +38,7 @@ class SendLetter( id = DomainId.generate(), content = content, senderId = senderId, + senderName = null, receiverName = receiverName, letterCode = letterCode, status = status, @@ -51,6 +53,7 @@ class SendLetter( content: LetterContent, receiverName: String, letterCode: String?, + senderName: String, status: LetterStatus = LetterStatus.SENDING, receiverId: DomainId? = null, createdAt: LocalDateTime = LocalDateTime.now(), @@ -60,6 +63,7 @@ class SendLetter( id = DomainId.generate(), content = content, senderId = null, + senderName = senderName, receiverName = receiverName, letterCode = letterCode, status = status, diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/SendLetterMapper.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/SendLetterMapper.kt index 0fa655b..06adc52 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/SendLetterMapper.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/SendLetterMapper.kt @@ -18,6 +18,7 @@ object SendLetterMapper { receiverName = sendLetterEntity.receiverName, letterCode = sendLetterEntity.letterCode ?: "", senderId = sendLetterEntity.senderId?.let { DomainId(it) }, + senderName = sendLetterEntity.senderName, receiverId = sendLetterEntity.receiverId?.let { DomainId(it) }, status = sendLetterEntity.letterStatus, createdAt = sendLetterEntity.createdAt, @@ -32,6 +33,7 @@ object SendLetterMapper { templateType = sendLetter.content.templateType, receiverName = sendLetter.receiverName, senderId = sendLetter.senderId?.value, + senderName = sendLetter.senderName, letterCode = sendLetter.letterCode, receiverId = sendLetter.receiverId?.value, letterStatus = sendLetter.status, diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/SendLetterEntity.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/SendLetterEntity.kt index 8fe740e..7a70ee5 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/SendLetterEntity.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/SendLetterEntity.kt @@ -27,6 +27,7 @@ class SendLetterEntity( images: List, templateType: Int, senderId: String?, + senderName: String?, letterCode: String?, receiverId: String?, letterStatus: LetterStatus, @@ -52,6 +53,9 @@ class SendLetterEntity( @Column(name = "sender_id") var senderId: String? = senderId + @Column(name = "sender_name") + var senderName: String? = senderName + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn( name = "sender_id", diff --git a/Infrastructure-Module/Persistence/src/main/resources/db/V1_21__add_sender_name_to_send_letter.sql b/Infrastructure-Module/Persistence/src/main/resources/db/V1_21__add_sender_name_to_send_letter.sql new file mode 100644 index 0000000..9afea5f --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/resources/db/V1_21__add_sender_name_to_send_letter.sql @@ -0,0 +1 @@ +ALTER TABLE send_letter ADD COLUMN sender_name VARCHAR(255) NULL; \ No newline at end of file