From 23303d31e0ed78aa77a96981de80339f2f8f5b19 Mon Sep 17 00:00:00 2001 From: Sim-km Date: Thu, 3 Oct 2024 16:17:20 +0900 Subject: [PATCH 1/4] =?UTF-8?q?ASAP-143=20chore:=20task=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .deploy/task/task-definition.json | 34 ++----------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/.deploy/task/task-definition.json b/.deploy/task/task-definition.json index 64430ef..ac07ba9 100644 --- a/.deploy/task/task-definition.json +++ b/.deploy/task/task-definition.json @@ -29,40 +29,10 @@ "valueFrom": "arn:aws:ssm:ap-northeast-2:396608783702:parameter/jwt/secret", "name": "JWT_SECRET" } - ], - "logConfiguration": { - "logDriver": "awsfirelens", - "options": { - "Labels": "job=firelens", - "Name": "loki", - "host": "dev.simproject.kr", - "port": "3100", - "line_format": "key_value" - } - } - }, - { - "essential": true, - "image": "grafana/fluent-bit-plugin-loki:2.9.3-amd64", - "name": "log_router", - "firelensConfiguration": { - "type": "fluentbit", - "options": { - "enable-ecs-log-metadata": "true" - } - }, - "logConfiguration": { - "logDriver": "awslogs", - "options": { - "awslogs-group": "/ecs/lettering-task", - "awslogs-region": "ap-northeast-2", - "awslogs-stream-prefix": "firelens", - "awslogs-create-group": "true" - } - } + ] } ], - "cpu": "768", + "cpu": "512", "memory": "768", "family": "lettering-task" } \ No newline at end of file From 51f11e286234603bc8c74e5314daebccd64a183a Mon Sep 17 00:00:00 2001 From: Sim-km Date: Thu, 3 Oct 2024 17:07:23 +0900 Subject: [PATCH 2/4] =?UTF-8?q?ASAP-143=20feat:=20sendLetter=20jpa=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MemorySendLetterManagementAdapter.kt | 200 +++++++------ .../letter/service/LetterCommandService.kt | 12 +- .../space/port/out/SpaceManagementPort.kt | 27 +- .../space/service/SpaceCommandService.kt | 14 +- .../space/service/SpaceCommandServiceTest.kt | 280 +++++++++--------- .../application/letter/LetterMockManager.kt | 19 +- .../application/space/SpaceMockManager.kt | 26 +- .../letter/LetterApiIntegrationTest.kt | 18 +- .../asap/domain/letter/entity/SendLetter.kt | 22 +- .../com/asap/domain/space/entity/Space.kt | 20 +- .../persistence/jpa/common/AggregateRoot.kt | 4 +- .../jpa/letter/SendLetterMapper.kt | 38 +++ .../adapter/SendLetterManagementJpaAdapter.kt | 64 ++++ .../jpa/letter/entity/ReceiveLetterEntity.kt | 11 + .../jpa/letter/entity/SendLetterEntity.kt | 91 ++++++ .../repository/SendLetterJpaRepository.kt | 91 ++++++ .../asap/persistence/jpa/space/SpaceMapper.kt | 11 + .../adapter/SpaceManagementJpaAdapter.kt | 25 +- .../jpa/space/entity/SpaceEntity.kt | 17 +- 19 files changed, 650 insertions(+), 340 deletions(-) create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/SendLetterMapper.kt create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/SendLetterManagementJpaAdapter.kt create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/SendLetterEntity.kt create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/SendLetterJpaRepository.kt diff --git a/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemorySendLetterManagementAdapter.kt b/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemorySendLetterManagementAdapter.kt index d1fad76..9819fcf 100644 --- a/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemorySendLetterManagementAdapter.kt +++ b/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemorySendLetterManagementAdapter.kt @@ -1,101 +1,99 @@ -package com.asap.application.letter.port.out.memory - -import com.asap.application.letter.exception.LetterException -import com.asap.application.letter.port.out.SendLetterManagementPort -import com.asap.domain.common.DomainId -import com.asap.domain.letter.entity.SendLetter -import com.asap.domain.letter.vo.LetterContent -import org.springframework.context.annotation.Primary -import org.springframework.stereotype.Component -import java.time.LocalDateTime - -@Component -@Primary -class MemorySendLetterManagementAdapter : SendLetterManagementPort { - private val sendLetters = mutableListOf() - - override fun save(sendLetter: SendLetter) { - sendLetters.add(SendLetterEntity.fromSendLetter(sendLetter)) - } - - override fun getLetterNotNull(letterId: DomainId): SendLetter = - matchingNotExpired { this.id == letterId.value }?.toSendLetter() - ?: throw LetterException.SendLetterNotFoundException() - - override fun getLetterByCodeNotNull(letterCode: String): SendLetter = - matchingNotExpired { this.letterCode == letterCode }?.toSendLetter() - ?: throw LetterException.SendLetterNotFoundException() - - override fun getReadLetterNotNull( - receiverId: DomainId, - letterCode: String, - ): SendLetter { - sendLetters.find { it.letterCode == letterCode && it.receiverId == receiverId.value }?.let { - return it.toSendLetter() - } ?: throw LetterException.SendLetterNotFoundException() - } - - override fun getReadLetterNotNull( - receiverId: DomainId, - letterId: DomainId, - ): SendLetter { - sendLetters.find { it.id == letterId.value && it.receiverId == receiverId.value }?.let { - return it.toSendLetter() - } ?: throw LetterException.SendLetterNotFoundException() - } - - override fun verifiedLetter( - receiverId: DomainId, - letterCode: String, - ): Boolean = matching { this.letterCode == letterCode }?.receiverId.isNullOrBlank().not() - - private fun matchingNotExpired(query: SendLetterEntity.() -> Boolean): SendLetterEntity? = matching { this.isExpired.not() and query() } - - private fun matchingExpired(query: SendLetterEntity.() -> Boolean): SendLetterEntity? = matching { this.isExpired and query() } - - private fun matching(query: SendLetterEntity.() -> Boolean): SendLetterEntity? = sendLetters.find { query(it) } - - data class SendLetterEntity( - val id: String, - val receiverName: String, - val content: String, - val images: List, - val templateType: Int, - val senderId: String, - val letterCode: String, - var isExpired: Boolean, - var receiverId: String?, - val createdAt: LocalDateTime, - ) { - fun toSendLetter(): SendLetter = - SendLetter( - id = DomainId(id), - receiverName = receiverName, - content = - LetterContent( - content = content, - images = images, - templateType = templateType, - ), - senderId = DomainId(senderId), - letterCode = letterCode, - createdAt = createdAt, - ) - - companion object { - fun fromSendLetter(sendLetter: SendLetter): SendLetterEntity = - SendLetterEntity( - id = sendLetter.id.value, - receiverName = sendLetter.receiverName, - content = sendLetter.content.content, - images = sendLetter.content.images, - templateType = sendLetter.content.templateType, - senderId = sendLetter.senderId.value, - letterCode = sendLetter.letterCode, - isExpired = false, - receiverId = sendLetter.receiverId?.value, - createdAt = sendLetter.createdAt, - ) - } - } -} +// package com.asap.application.letter.port.out.memory +// +// import com.asap.application.letter.exception.LetterException +// import com.asap.application.letter.port.out.SendLetterManagementPort +// import com.asap.domain.common.DomainId +// import com.asap.domain.letter.entity.SendLetter +// import com.asap.domain.letter.vo.LetterContent +// import java.time.LocalDateTime +// +// // @Component +// // @Primary +// class MemorySendLetterManagementAdapter : SendLetterManagementPort { +// private val sendLetters = mutableListOf() +// +// override fun save(sendLetter: SendLetter) { +// sendLetters.add(SendLetterEntity.fromSendLetter(sendLetter)) +// } +// +// override fun getLetterNotNull(letterId: DomainId): SendLetter = +// matchingNotExpired { this.id == letterId.value }?.toSendLetter() +// ?: throw LetterException.SendLetterNotFoundException() +// +// override fun getLetterByCodeNotNull(letterCode: String): SendLetter = +// matchingNotExpired { this.letterCode == letterCode }?.toSendLetter() +// ?: throw LetterException.SendLetterNotFoundException() +// +// override fun getReadLetterNotNull( +// receiverId: DomainId, +// letterCode: String, +// ): SendLetter { +// sendLetters.find { it.letterCode == letterCode && it.receiverId == receiverId.value }?.let { +// return it.toSendLetter() +// } ?: throw LetterException.SendLetterNotFoundException() +// } +// +// override fun getReadLetterNotNull( +// receiverId: DomainId, +// letterId: DomainId, +// ): SendLetter { +// sendLetters.find { it.id == letterId.value && it.receiverId == receiverId.value }?.let { +// return it.toSendLetter() +// } ?: throw LetterException.SendLetterNotFoundException() +// } +// +// override fun verifiedLetter( +// receiverId: DomainId, +// letterCode: String, +// ): Boolean = matching { this.letterCode == letterCode }?.receiverId.isNullOrBlank().not() +// +// private fun matchingNotExpired(query: SendLetterEntity.() -> Boolean): SendLetterEntity? = matching { this.isExpired.not() and query() } +// +// private fun matchingExpired(query: SendLetterEntity.() -> Boolean): SendLetterEntity? = matching { this.isExpired and query() } +// +// private fun matching(query: SendLetterEntity.() -> Boolean): SendLetterEntity? = sendLetters.find { query(it) } +// +// data class SendLetterEntity( +// val id: String, +// val receiverName: String, +// val content: String, +// val images: List, +// val templateType: Int, +// val senderId: String, +// val letterCode: String, +// var isExpired: Boolean, +// var receiverId: String?, +// val createdAt: LocalDateTime, +// ) { +// fun toSendLetter(): SendLetter = +// SendLetter( +// id = DomainId(id), +// receiverName = receiverName, +// content = +// LetterContent( +// content = content, +// images = images, +// templateType = templateType, +// ), +// senderId = DomainId(senderId), +// letterCode = letterCode, +// createdAt = createdAt, +// ) +// +// companion object { +// fun fromSendLetter(sendLetter: SendLetter): SendLetterEntity = +// SendLetterEntity( +// id = sendLetter.id.value, +// receiverName = sendLetter.receiverName, +// content = sendLetter.content.content, +// images = sendLetter.content.images, +// templateType = sendLetter.content.templateType, +// senderId = sendLetter.senderId.value, +// letterCode = sendLetter.letterCode, +// isExpired = false, +// receiverId = sendLetter.receiverId?.value, +// createdAt = sendLetter.createdAt, +// ) +// } +// } +// } 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 454076d..997c6b8 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 @@ -54,7 +54,7 @@ class LetterCommandService( // event } - return SendLetterUsecase.Response(letterCode = sendLetter.letterCode) + return SendLetterUsecase.Response(letterCode = sendLetter.letterCode!!) } override fun verify(command: VerifyLetterAccessibleUsecase.Command): VerifyLetterAccessibleUsecase.Response { @@ -70,11 +70,11 @@ class LetterCommandService( val sendLetter = sendLetterManagementPort.getLetterByCodeNotNull(command.letterCode) sendLetter .isSameReceiver { - userManagementPort.getUserNotNull(DomainId(command.userId)).username + userManagementPort.getUserNotNull(DomainId(command.userId)) }.takeIf { it } ?.let { - val readLetter = sendLetter.readLetter(DomainId(command.userId)) - sendLetterManagementPort.save(readLetter) + sendLetter.readLetter(DomainId(command.userId)) + sendLetterManagementPort.save(sendLetter) return VerifyLetterAccessibleUsecase.Response(letterId = sendLetter.id.value) } ?: throw LetterException.InvalidLetterAccessException() } @@ -99,9 +99,9 @@ class LetterCommandService( content = sendLetter.content, receiveDate = sendLetter.createdDate, ) - val receivedLetter = sendLetter.receiveLetter() + sendLetter.receiveLetter() - sendLetterManagementPort.save(receivedLetter) + sendLetterManagementPort.save(sendLetter) independentLetterManagementPort.save(independentLetter) } diff --git a/Application-Module/src/main/kotlin/com/asap/application/space/port/out/SpaceManagementPort.kt b/Application-Module/src/main/kotlin/com/asap/application/space/port/out/SpaceManagementPort.kt index 3ecaae5..114e331 100644 --- a/Application-Module/src/main/kotlin/com/asap/application/space/port/out/SpaceManagementPort.kt +++ b/Application-Module/src/main/kotlin/com/asap/application/space/port/out/SpaceManagementPort.kt @@ -6,42 +6,31 @@ import com.asap.domain.space.entity.MainSpace import com.asap.domain.space.entity.Space interface SpaceManagementPort { - - fun getMainSpace( - userId: DomainId - ): MainSpace + fun getMainSpace(userId: DomainId): MainSpace fun getSpaceNotNull( userId: DomainId, - spaceId: DomainId + spaceId: DomainId, ): Space - fun getAllIndexedSpace( - userId: DomainId, - ): List + fun getAllIndexedSpace(userId: DomainId): List - fun createSpace( - userId: DomainId, - spaceName: String, - templateType: Int - ): Space + fun save(space: Space): Space fun update(space: Space): Space fun updateIndexes( userId: DomainId, - orders: List + orders: List, ) - fun deleteById( userId: DomainId, - spaceId: DomainId + spaceId: DomainId, ) fun deleteAllBySpaceIds( userId: DomainId, - spaceIds: List + spaceIds: List, ) - -} \ No newline at end of file +} diff --git a/Application-Module/src/main/kotlin/com/asap/application/space/service/SpaceCommandService.kt b/Application-Module/src/main/kotlin/com/asap/application/space/service/SpaceCommandService.kt index 5650eac..5426999 100644 --- a/Application-Module/src/main/kotlin/com/asap/application/space/service/SpaceCommandService.kt +++ b/Application-Module/src/main/kotlin/com/asap/application/space/service/SpaceCommandService.kt @@ -8,6 +8,7 @@ import com.asap.application.space.port.`in`.SpaceUpdateNameUsecase import com.asap.application.space.port.out.SpaceManagementPort import com.asap.common.exception.DefaultException import com.asap.domain.common.DomainId +import com.asap.domain.space.entity.Space import com.asap.domain.space.service.SpaceIndexValidator import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -23,11 +24,14 @@ class SpaceCommandService( private val spaceIndexValidator: SpaceIndexValidator = SpaceIndexValidator() override fun create(command: SpaceCreateUsecase.Command) { - spaceManagementPort.createSpace( - userId = DomainId(command.userId), - spaceName = command.spaceName, - templateType = command.templateType, - ) + Space + .create( + userId = DomainId(command.userId), + name = command.spaceName, + templateType = command.templateType, + ).let { + spaceManagementPort.save(it) + } } override fun update(command: SpaceUpdateNameUsecase.Command) { diff --git a/Application-Module/src/test/kotlin/com/asap/application/space/service/SpaceCommandServiceTest.kt b/Application-Module/src/test/kotlin/com/asap/application/space/service/SpaceCommandServiceTest.kt index d4fa626..a138661 100644 --- a/Application-Module/src/test/kotlin/com/asap/application/space/service/SpaceCommandServiceTest.kt +++ b/Application-Module/src/test/kotlin/com/asap/application/space/service/SpaceCommandServiceTest.kt @@ -15,166 +15,170 @@ import io.mockk.every import io.mockk.mockk import io.mockk.verify -class SpaceCommandServiceTest : BehaviorSpec({ +class SpaceCommandServiceTest : + BehaviorSpec({ - val spaceManagementPort = mockk(relaxed = true) - - val spaceCommandService = SpaceCommandService( - spaceManagementPort, - ) + val spaceManagementPort = mockk(relaxed = true) + val spaceCommandService = + SpaceCommandService( + spaceManagementPort, + ) - given("스페이스 생성 요청이 들어왔을 때") { - val spaceCreateCommand = SpaceCreateUsecase.Command( - userId = "userId", - spaceName = "spaceName", - templateType = 1 - ) - `when`("유저 아이디, 스페이스 이름, 템플릿 타입이 주어진다면") { - spaceCommandService.create(spaceCreateCommand) - then("스페이스를 생성한다") { - verify { - spaceManagementPort.createSpace( - userId = DomainId(spaceCreateCommand.userId), - spaceName = spaceCreateCommand.spaceName, - templateType = spaceCreateCommand.templateType - ) + given("스페이스 생성 요청이 들어왔을 때") { + val spaceCreateCommand = + SpaceCreateUsecase.Command( + userId = "userId", + spaceName = "spaceName", + templateType = 1, + ) + `when`("유저 아이디, 스페이스 이름, 템플릿 타입이 주어진다면") { + spaceCommandService.create(spaceCreateCommand) + then("스페이스를 생성한다") { + verify { + spaceManagementPort.save(any(Space::class)) + } } } } - } - given("스페이스 이름 변경 요청이 들어왔을 때") { - val spaceUpdateNameCommand = SpaceUpdateNameUsecase.Command( - userId = "userId", - spaceId = "spaceId", - name = "newName" - ) - val mockSpace = Space( - id = DomainId(spaceUpdateNameCommand.spaceId), - userId = DomainId(spaceUpdateNameCommand.userId), - name = "oldName", - templateType = 1 - ) - every { - spaceManagementPort.getSpaceNotNull( - userId = DomainId(spaceUpdateNameCommand.userId), - spaceId = DomainId(spaceUpdateNameCommand.spaceId) - ) - } returns mockSpace - `when`("유저 아이디, 스페이스 아이디, 새로운 이름이 주어진다면") { - spaceCommandService.update(spaceUpdateNameCommand) - then("스페이스 이름을 변경한다") { - verify { - spaceManagementPort.getSpaceNotNull( - userId = DomainId(spaceUpdateNameCommand.userId), - spaceId = DomainId(spaceUpdateNameCommand.spaceId) - ) - } - verify { - spaceManagementPort.update(mockSpace.updateName(spaceUpdateNameCommand.name)) + given("스페이스 이름 변경 요청이 들어왔을 때") { + val spaceUpdateNameCommand = + SpaceUpdateNameUsecase.Command( + userId = "userId", + spaceId = "spaceId", + name = "newName", + ) + val mockSpace = + Space( + id = DomainId(spaceUpdateNameCommand.spaceId), + userId = DomainId(spaceUpdateNameCommand.userId), + name = "oldName", + templateType = 1, + ) + every { + spaceManagementPort.getSpaceNotNull( + userId = DomainId(spaceUpdateNameCommand.userId), + spaceId = DomainId(spaceUpdateNameCommand.spaceId), + ) + } returns mockSpace + `when`("유저 아이디, 스페이스 아이디, 새로운 이름이 주어진다면") { + spaceCommandService.update(spaceUpdateNameCommand) + then("스페이스 이름을 변경한다") { + verify { + spaceManagementPort.getSpaceNotNull( + userId = DomainId(spaceUpdateNameCommand.userId), + spaceId = DomainId(spaceUpdateNameCommand.spaceId), + ) + } + verify { + spaceManagementPort.update(mockSpace.updateName(spaceUpdateNameCommand.name)) + } } } } - } - given("스페이스 삭제 요청이 들어왔을 때") { - val spaceDeleteOneCommand = SpaceDeleteUsecase.DeleteOneCommand( - userId = "userId", - spaceId = "spaceId" - ) - `when`("유저 아이디, 스페이스 아이디가 주어진다면") { - spaceCommandService.deleteOne(spaceDeleteOneCommand) - then("스페이스를 삭제한다") { - verify { - spaceManagementPort.deleteById( - userId = DomainId(spaceDeleteOneCommand.userId), - spaceId = DomainId(spaceDeleteOneCommand.spaceId) - ) + given("스페이스 삭제 요청이 들어왔을 때") { + val spaceDeleteOneCommand = + SpaceDeleteUsecase.DeleteOneCommand( + userId = "userId", + spaceId = "spaceId", + ) + `when`("유저 아이디, 스페이스 아이디가 주어진다면") { + spaceCommandService.deleteOne(spaceDeleteOneCommand) + then("스페이스를 삭제한다") { + verify { + spaceManagementPort.deleteById( + userId = DomainId(spaceDeleteOneCommand.userId), + spaceId = DomainId(spaceDeleteOneCommand.spaceId), + ) + } } } - } - val spaceDeleteAllCommand = SpaceDeleteUsecase.DeleteAllCommand( - userId = "userId", - spaceIds = listOf("spaceId1", "spaceId2") - ) - `when`("여러 스페이스 아이디가 주어진다면") { - spaceCommandService.deleteAll(spaceDeleteAllCommand) - then("여러 스페이스를 삭제한다") { - verify { - spaceManagementPort.deleteAllBySpaceIds( - userId = DomainId(spaceDeleteAllCommand.userId), - spaceIds = spaceDeleteAllCommand.spaceIds.map { DomainId(it) } - ) + val spaceDeleteAllCommand = + SpaceDeleteUsecase.DeleteAllCommand( + userId = "userId", + spaceIds = listOf("spaceId1", "spaceId2"), + ) + `when`("여러 스페이스 아이디가 주어진다면") { + spaceCommandService.deleteAll(spaceDeleteAllCommand) + then("여러 스페이스를 삭제한다") { + verify { + spaceManagementPort.deleteAllBySpaceIds( + userId = DomainId(spaceDeleteAllCommand.userId), + spaceIds = spaceDeleteAllCommand.spaceIds.map { DomainId(it) }, + ) + } } } } - } - given("스페이스 인덱스 수정 요청이 들어올 때") { - val spaceUpdateIndexCommand = SpaceUpdateIndexUsecase.Command( - userId = "userId", - orders = listOf( - SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId1", 1), - SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId2", 0) - ) - ) - val indexedSpaces = listOf( - IndexedSpace( - id = DomainId("spaceId1"), - userId = DomainId("userId"), - name = "space1", - index = 0, - templateType = 1 - ), - IndexedSpace( - id = DomainId("spaceId2"), - userId = DomainId("userId"), - name = "space2", - index = 1, - templateType = 1 - ) - ) - every { spaceManagementPort.getAllIndexedSpace(DomainId(spaceUpdateIndexCommand.userId)) } returns indexedSpaces - `when`("유저 아이디, 스페이스 순서가 주어진다면") { - spaceCommandService.update(spaceUpdateIndexCommand) - then("스페이스 순서를 수정한다") { - verify { - spaceManagementPort.updateIndexes( - userId = DomainId(spaceUpdateIndexCommand.userId), - orders = listOf( - indexedSpaces[0].updateIndex(1), - indexedSpaces[1].updateIndex(0) + given("스페이스 인덱스 수정 요청이 들어올 때") { + val spaceUpdateIndexCommand = + SpaceUpdateIndexUsecase.Command( + userId = "userId", + orders = + listOf( + SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId1", 1), + SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId2", 0), + ), + ) + val indexedSpaces = + listOf( + IndexedSpace( + id = DomainId("spaceId1"), + userId = DomainId("userId"), + name = "space1", + index = 0, + templateType = 1, + ), + IndexedSpace( + id = DomainId("spaceId2"), + userId = DomainId("userId"), + name = "space2", + index = 1, + templateType = 1, + ), + ) + every { spaceManagementPort.getAllIndexedSpace(DomainId(spaceUpdateIndexCommand.userId)) } returns indexedSpaces + `when`("유저 아이디, 스페이스 순서가 주어진다면") { + spaceCommandService.update(spaceUpdateIndexCommand) + then("스페이스 순서를 수정한다") { + verify { + spaceManagementPort.updateIndexes( + userId = DomainId(spaceUpdateIndexCommand.userId), + orders = + listOf( + indexedSpaces[0].updateIndex(1), + indexedSpaces[1].updateIndex(0), + ), ) - ) + } } } - } - - val invalidCommand = SpaceUpdateIndexUsecase.Command( - userId = "userId", - orders = listOf( - SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId1", 1), - SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId2", 2), - SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId3", 3) - ) - ) - every { - spaceManagementPort.getAllIndexedSpace(DomainId(invalidCommand.userId)) - } returns indexedSpaces - `when`("인덱스 검증과정에서 예외가 발생한다면") { - then("스페이스 순서를 수정하지 않는다") { - shouldThrow { - spaceCommandService.update( - invalidCommand - ) + val invalidCommand = + SpaceUpdateIndexUsecase.Command( + userId = "userId", + orders = + listOf( + SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId1", 1), + SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId2", 2), + SpaceUpdateIndexUsecase.Command.SpaceOrder("spaceId3", 3), + ), + ) + every { + spaceManagementPort.getAllIndexedSpace(DomainId(invalidCommand.userId)) + } returns indexedSpaces + `when`("인덱스 검증과정에서 예외가 발생한다면") { + then("스페이스 순서를 수정하지 않는다") { + shouldThrow { + spaceCommandService.update( + invalidCommand, + ) + } } } } - } - - -}) { -} \ No newline at end of file + }) diff --git a/Application-Module/src/testFixtures/kotlin/com/asap/application/letter/LetterMockManager.kt b/Application-Module/src/testFixtures/kotlin/com/asap/application/letter/LetterMockManager.kt index 8bc78f6..7a1ab79 100644 --- a/Application-Module/src/testFixtures/kotlin/com/asap/application/letter/LetterMockManager.kt +++ b/Application-Module/src/testFixtures/kotlin/com/asap/application/letter/LetterMockManager.kt @@ -20,7 +20,10 @@ class LetterMockManager( ) { private val letterCodeGenerator = LetterCodeGenerator() - fun generateMockSendLetter(receiverName: String): String { + fun generateMockSendLetter( + receiverName: String, + senderId: String = DomainId.generate().value, + ): String { val sendLetter = SendLetter( receiverName = receiverName, @@ -30,15 +33,15 @@ class LetterMockManager( templateType = 1, images = listOf("image1", "image2"), ), - senderId = DomainId.generate(), + senderId = DomainId(senderId), letterCode = letterCodeGenerator.generateCode( content = "content", - ownerId = DomainId.generate().value, + ownerId = senderId, ), ) sendLetterManagementPort.save(sendLetter) - return sendLetter.letterCode + return sendLetter.letterCode!! } fun generateMockReadLetter( @@ -59,13 +62,13 @@ class LetterMockManager( letterCode = letterCodeGenerator.generateCode( content = "content", - ownerId = DomainId(senderId).value, + ownerId = senderId, ), ) - val readLetter = sendLetter.readLetter(DomainId(receiverId)) - sendLetterManagementPort.save(readLetter) + sendLetter.readLetter(DomainId(receiverId)) + sendLetterManagementPort.save(sendLetter) return mapOf( - "letterCode" to sendLetter.letterCode, + "letterCode" to sendLetter.letterCode!!, "letterId" to sendLetter.id.value, ) } diff --git a/Application-Module/src/testFixtures/kotlin/com/asap/application/space/SpaceMockManager.kt b/Application-Module/src/testFixtures/kotlin/com/asap/application/space/SpaceMockManager.kt index f5af5b3..70e75bf 100644 --- a/Application-Module/src/testFixtures/kotlin/com/asap/application/space/SpaceMockManager.kt +++ b/Application-Module/src/testFixtures/kotlin/com/asap/application/space/SpaceMockManager.kt @@ -2,23 +2,25 @@ package com.asap.application.space import com.asap.application.space.port.out.SpaceManagementPort import com.asap.domain.common.DomainId - +import com.asap.domain.space.entity.Space class SpaceMockManager( - private val spaceManagementPort: SpaceManagementPort + private val spaceManagementPort: SpaceManagementPort, ) { - - fun settingSpace(userId: String): String{ - return spaceManagementPort.createSpace(DomainId(userId), "spaceName", 0).id.value + fun settingSpace(userId: String): String { + val space = + Space.create( + userId = DomainId(userId), + name = "test", + templateType = 0, + ) + return spaceManagementPort.save(space).id.value } - fun getSpaceCount(userId: String): Int{ - return spaceManagementPort.getAllIndexedSpace(DomainId(userId)).size - } + fun getSpaceCount(userId: String): Int = spaceManagementPort.getAllIndexedSpace(DomainId(userId)).size - fun getSpaceIndexes(userId: String): List>{ - return spaceManagementPort.getAllIndexedSpace(DomainId(userId)).map{ + fun getSpaceIndexes(userId: String): List> = + spaceManagementPort.getAllIndexedSpace(DomainId(userId)).map { it.id.value to it.index } - } -} \ No newline at end of file +} 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 a30323e..b16ef23 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 @@ -29,9 +29,10 @@ class LetterApiIntegrationTest : IntegrationSupporter() { @DisplayName("편지 열람 가능 검증 성공") fun verifyLetter() { // given + val senderId = userMockManager.settingUser(username = "senderUsername") val userId = userMockManager.settingUser(username = "username") val accessToken = testJwtDataGenerator.generateAccessToken(userId) - val letterCode = letterMockManager.generateMockSendLetter("username") + val letterCode = letterMockManager.generateMockSendLetter("username", senderId = senderId) val request = LetterVerifyRequest(letterCode) // when val response = @@ -78,9 +79,10 @@ class LetterApiIntegrationTest : IntegrationSupporter() { @DisplayName("해당 사용자는 편지 열람 권한이 없음") fun verifyLetter_With_InvalidUser() { // given + val senderId = userMockManager.settingUser(username = "senderUsername") val userId = userMockManager.settingUser(username = "username") val accessToken = testJwtDataGenerator.generateAccessToken(userId) - val letterCode = letterMockManager.generateMockSendLetter("otherUsername") + val letterCode = letterMockManager.generateMockSendLetter("otherUsername_invalidUser", senderId) val request = LetterVerifyRequest(letterCode) // when val response = @@ -102,10 +104,12 @@ class LetterApiIntegrationTest : IntegrationSupporter() { @DisplayName("다른 사용자가 이미 연람함 편지면 열람 불가") fun verifyLetter_With_ExpiredLetter() { // given + val senderId = userMockManager.settingUser(username = "senderUsername") val userId = userMockManager.settingUser(username = "username") + val otherUserId = userMockManager.settingUser(username = "otherUser") val accessToken = testJwtDataGenerator.generateAccessToken(userId) val letterCode = - letterMockManager.generateMockReadLetter("username", "otherUserId")["letterCode"] as String + letterMockManager.generateMockReadLetter("username", otherUserId, senderId)["letterCode"] as String val request = LetterVerifyRequest(letterCode) // when val response = @@ -116,9 +120,9 @@ class LetterApiIntegrationTest : IntegrationSupporter() { } // then response.andExpect { - status { isBadRequest() } + status { isForbidden() } jsonPath("$.code") { - value("LETTER-001") + value("LETTER-002") } } } @@ -128,8 +132,10 @@ class LetterApiIntegrationTest : IntegrationSupporter() { fun verifyLetter_With_ExpiredLetter_ReAccessible() { // given val userId = userMockManager.settingUser(username = "username") + val sender = userMockManager.settingUser(username = "senderUsername") val accessToken = testJwtDataGenerator.generateAccessToken(userId) - val letterCode = letterMockManager.generateMockReadLetter("username", userId)["letterCode"] as String + val letterCode = + letterMockManager.generateMockReadLetter("username", userId, senderId = sender)["letterCode"] as String val request = LetterVerifyRequest(letterCode) // when val response = 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 7f2649f..1990371 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 @@ -3,6 +3,7 @@ package com.asap.domain.letter.entity import com.asap.domain.common.DomainId import com.asap.domain.letter.enums.LetterStatus import com.asap.domain.letter.vo.LetterContent +import com.asap.domain.user.entity.User import java.time.LocalDate import java.time.LocalDateTime @@ -11,16 +12,25 @@ data class SendLetter( val content: LetterContent, val senderId: DomainId, val receiverName: String, - val letterCode: String, - val status: LetterStatus = LetterStatus.SENDING, + var letterCode: String?, + var status: LetterStatus = LetterStatus.SENDING, val createdAt: LocalDateTime = LocalDateTime.now(), - val receiverId: DomainId? = null, + var receiverId: DomainId? = null, ) { val createdDate: LocalDate = createdAt.toLocalDate() - fun isSameReceiver(receiverName: () -> String): Boolean = this.receiverName == receiverName() + fun isSameReceiver(receiver: () -> User): Boolean { + val receiverUser = receiver() + return receiverName == receiverUser.username && (receiverId == null || receiverId == receiverUser.id) + } - fun readLetter(receiverId: DomainId): SendLetter = copy(status = LetterStatus.READ, receiverId = receiverId) + fun readLetter(receiverId: DomainId) { + this.receiverId = receiverId + this.status = LetterStatus.READ + } - fun receiveLetter(): SendLetter = copy(status = LetterStatus.RECEIVED, letterCode = "") + fun receiveLetter() { + status = LetterStatus.RECEIVED + letterCode = null + } } diff --git a/Domain-Module/src/main/kotlin/com/asap/domain/space/entity/Space.kt b/Domain-Module/src/main/kotlin/com/asap/domain/space/entity/Space.kt index 4f78b2e..7f76cc4 100644 --- a/Domain-Module/src/main/kotlin/com/asap/domain/space/entity/Space.kt +++ b/Domain-Module/src/main/kotlin/com/asap/domain/space/entity/Space.kt @@ -6,10 +6,20 @@ data class Space( val id: DomainId = DomainId.generate(), val userId: DomainId, val name: String, - val templateType: Int + val templateType: Int, ) { - - fun updateName(name: String): Space { - return this.copy(name = name) + companion object { + fun create( + userId: DomainId, + name: String, + templateType: Int, + ): Space = + Space( + userId = userId, + name = name, + templateType = templateType, + ) } -} \ No newline at end of file + + fun updateName(name: String): Space = this.copy(name = name) +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/common/AggregateRoot.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/common/AggregateRoot.kt index 5183513..26441a0 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/common/AggregateRoot.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/common/AggregateRoot.kt @@ -1,7 +1,5 @@ package com.asap.persistence.jpa.common -import com.asap.domain.common.DomainId - abstract class AggregateRoot>( - override val id: String = DomainId.generate().value, + override val id: String, ) : BaseEntity(id) 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 new file mode 100644 index 0000000..0a97b50 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/SendLetterMapper.kt @@ -0,0 +1,38 @@ +package com.asap.persistence.jpa.letter + +import com.asap.domain.common.DomainId +import com.asap.domain.letter.entity.SendLetter +import com.asap.domain.letter.vo.LetterContent +import com.asap.persistence.jpa.letter.entity.SendLetterEntity + +object SendLetterMapper { + fun toSendLetter(sendLetterEntity: SendLetterEntity): SendLetter = + SendLetter( + id = DomainId(sendLetterEntity.id), + content = + LetterContent( + content = sendLetterEntity.content, + images = sendLetterEntity.images, + templateType = sendLetterEntity.templateType, + ), + receiverName = sendLetterEntity.receiverName, + letterCode = sendLetterEntity.letterCode ?: "", + senderId = DomainId(sendLetterEntity.senderId), + receiverId = sendLetterEntity.receiverId?.let { DomainId(it) }, + status = sendLetterEntity.letterStatus, + createdAt = sendLetterEntity.createdAt, + ) + + fun toSendLetterEntity(sendLetter: SendLetter): SendLetterEntity = + SendLetterEntity( + id = sendLetter.id.value, + content = sendLetter.content.content, + images = sendLetter.content.images, + templateType = sendLetter.content.templateType, + receiverName = sendLetter.receiverName, + senderId = sendLetter.senderId.value, + letterCode = sendLetter.letterCode, + receiverId = sendLetter.receiverId?.value, + letterStatus = sendLetter.status, + ) +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/SendLetterManagementJpaAdapter.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/SendLetterManagementJpaAdapter.kt new file mode 100644 index 0000000..60d7962 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/SendLetterManagementJpaAdapter.kt @@ -0,0 +1,64 @@ +package com.asap.persistence.jpa.letter.adapter + +import com.asap.application.letter.exception.LetterException +import com.asap.application.letter.port.out.SendLetterManagementPort +import com.asap.domain.common.DomainId +import com.asap.domain.letter.entity.SendLetter +import com.asap.domain.letter.enums.LetterStatus +import com.asap.persistence.jpa.letter.SendLetterMapper +import com.asap.persistence.jpa.letter.repository.* +import org.springframework.stereotype.Repository + +@Repository +class SendLetterManagementJpaAdapter( + private val sendLetterJpaRepository: SendLetterJpaRepository, +) : SendLetterManagementPort { + override fun save(sendLetter: SendLetter) { + sendLetterJpaRepository.save(SendLetterMapper.toSendLetterEntity(sendLetter)) + } + + override fun getLetterNotNull(letterId: DomainId): SendLetter = + sendLetterJpaRepository.findActiveSendLetterById(letterId.value)?.let { + SendLetterMapper.toSendLetter(it) + } ?: throw LetterException.SendLetterNotFoundException() + + override fun getLetterByCodeNotNull(letterCode: String): SendLetter = + sendLetterJpaRepository.findActiveSendLetterByCode(letterCode)?.let { + SendLetterMapper.toSendLetter(it) + } ?: throw LetterException.SendLetterNotFoundException() + + override fun getReadLetterNotNull( + receiverId: DomainId, + letterCode: String, + ): SendLetter = + sendLetterJpaRepository + .findActiveSendLetterByCodeAndReceiverIdAndLetterStatus( + code = letterCode, + receiverId = receiverId.value, + letterStatus = LetterStatus.READ, + )?.let { + SendLetterMapper.toSendLetter(it) + } ?: throw LetterException.SendLetterNotFoundException() + + override fun getReadLetterNotNull( + receiverId: DomainId, + letterId: DomainId, + ): SendLetter = + sendLetterJpaRepository + .findActiveSendLetterByIdAndReceiverIdAndLetterStatus( + id = letterId.value, + receiverId = receiverId.value, + letterStatus = LetterStatus.READ, + )?.let { + SendLetterMapper.toSendLetter(it) + } ?: throw LetterException.SendLetterNotFoundException() + + override fun verifiedLetter( + receiverId: DomainId, + letterCode: String, + ): Boolean = + sendLetterJpaRepository.existsByLetterCodeAndReceiverId( + letterCode = letterCode, + receiverId = receiverId.value, + ) +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt new file mode 100644 index 0000000..fddb363 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt @@ -0,0 +1,11 @@ +package com.asap.persistence.jpa.letter.entity + +import com.asap.persistence.jpa.common.AggregateRoot +import jakarta.persistence.Entity +import jakarta.persistence.Table + +@Entity +@Table(name = "receive_letter") +class ReceiveLetterEntity( + id: String, +) : AggregateRoot(id) 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 new file mode 100644 index 0000000..aaa8437 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/SendLetterEntity.kt @@ -0,0 +1,91 @@ +package com.asap.persistence.jpa.letter.entity + +import com.asap.domain.letter.enums.LetterStatus +import com.asap.persistence.jpa.common.AggregateRoot +import com.asap.persistence.jpa.common.EntityStatus +import com.asap.persistence.jpa.user.entity.UserEntity +import jakarta.persistence.* +import org.hibernate.annotations.JdbcTypeCode +import org.hibernate.type.SqlTypes + +@Entity +@Table( + name = "send_letter", + indexes = [ + Index( + name = "idx_letter_code", + columnList = "letter_code", + unique = true, + ), + ], +) +class SendLetterEntity( + id: String, + receiverName: String, + content: String, + images: List, + templateType: Int, + senderId: String, + letterCode: String?, + receiverId: String?, + letterStatus: LetterStatus, +) : AggregateRoot(id) { + @Column( + name = "content", + nullable = false, + columnDefinition = "text", + ) + var content: String = content + + @JdbcTypeCode(SqlTypes.JSON) + @Column( + name = "images", + columnDefinition = "json", + nullable = false, + ) + var images: List = images + var templateType: Int = templateType + + @Column(name = "sender_id", nullable = false) + var senderId: String = senderId + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "sender_id", + nullable = false, + insertable = false, + updatable = false, + ) + lateinit var sender: UserEntity + + @Column(name = "receiver_id") + var receiverId: String? = receiverId + + var receiverName: String = receiverName + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "receiver_id", + insertable = false, + updatable = false, + ) + var receiver: UserEntity? = null + + var letterCode: String? = letterCode + + @Enumerated(EnumType.STRING) + @Column( + name = "letter_status", + nullable = false, + columnDefinition = "varchar(20)", + ) + var letterStatus: LetterStatus = letterStatus + + @Enumerated(EnumType.STRING) + @Column( + name = "entity_status", + nullable = false, + columnDefinition = "varchar(20)", + ) + var entityStatus: EntityStatus = EntityStatus.ACTIVE +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/SendLetterJpaRepository.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/SendLetterJpaRepository.kt new file mode 100644 index 0000000..d77d908 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/SendLetterJpaRepository.kt @@ -0,0 +1,91 @@ +package com.asap.persistence.jpa.letter.repository + +import com.asap.domain.letter.enums.LetterStatus +import com.asap.persistence.jpa.common.EntityStatus +import com.asap.persistence.jpa.letter.entity.SendLetterEntity +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query + +interface SendLetterJpaRepository : JpaRepository { + @Query( + """ + SELECT s + FROM SendLetterEntity s + WHERE s.id = :id + AND s.entityStatus = :entityStatus + """, + ) + fun findBy( + id: String, + entityStatus: EntityStatus, + ): SendLetterEntity? + + @Query( + """ + SELECT s + FROM SendLetterEntity s + WHERE s.letterCode = :letterCode + AND s.entityStatus = :entityStatus + """, + ) + fun findByLetterCode( + letterCode: String, + entityStatus: EntityStatus, + ): SendLetterEntity? + + @Query( + """ + SELECT s + FROM SendLetterEntity s + WHERE s.id = :id + AND s.receiverId = :receiverId + AND s.letterStatus = :letterStatus + AND s.entityStatus = :entityStatus + """, + ) + fun findByIdAndReceiverIdAndLetterStatus( + id: String, + receiverId: String, + letterStatus: LetterStatus, + entityStatus: EntityStatus, + ): SendLetterEntity? + + @Query( + """ + SELECT s + FROM SendLetterEntity s + WHERE s.letterCode = :letterCode + AND s.receiverId = :receiverId + AND s.letterStatus = :letterStatus + AND s.entityStatus = :entityStatus + """, + ) + fun findByCodeAndReceiverIdAndLetterStatus( + letterCode: String, + receiverId: String, + letterStatus: LetterStatus, + entityStatus: EntityStatus, + ): SendLetterEntity? + + fun existsByLetterCodeAndReceiverId( + letterCode: String, + receiverId: String, + ): Boolean +} + +fun SendLetterJpaRepository.findActiveSendLetterById(id: String): SendLetterEntity? = findBy(id, EntityStatus.ACTIVE) + +fun SendLetterJpaRepository.findActiveSendLetterByCode(letterCode: String): SendLetterEntity? = + findByLetterCode(letterCode, EntityStatus.ACTIVE) + +fun SendLetterJpaRepository.findActiveSendLetterByCodeAndReceiverIdAndLetterStatus( + code: String, + receiverId: String, + letterStatus: LetterStatus, +): SendLetterEntity? = findByCodeAndReceiverIdAndLetterStatus(code, receiverId, letterStatus, EntityStatus.ACTIVE) + +fun SendLetterJpaRepository.findActiveSendLetterByIdAndReceiverIdAndLetterStatus( + id: String, + receiverId: String, + letterStatus: LetterStatus, +): SendLetterEntity? = findByIdAndReceiverIdAndLetterStatus(id, receiverId, letterStatus, EntityStatus.ACTIVE) diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/SpaceMapper.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/SpaceMapper.kt index 1c7cc45..d38fe55 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/SpaceMapper.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/SpaceMapper.kt @@ -28,4 +28,15 @@ object SpaceMapper { templateType = spaceEntity.templateType, index = spaceEntity.index, ) + + fun toSpaceEntity( + space: Space, + index: Int, + ) = SpaceEntity( + id = space.id.value, + userId = space.userId.value, + name = space.name, + templateType = space.templateType, + index = index, + ) } diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/adapter/SpaceManagementJpaAdapter.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/adapter/SpaceManagementJpaAdapter.kt index f80feba..6607176 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/adapter/SpaceManagementJpaAdapter.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/adapter/SpaceManagementJpaAdapter.kt @@ -7,7 +7,6 @@ import com.asap.domain.space.entity.IndexedSpace import com.asap.domain.space.entity.MainSpace import com.asap.domain.space.entity.Space import com.asap.persistence.jpa.space.SpaceMapper -import com.asap.persistence.jpa.space.entity.SpaceEntity import com.asap.persistence.jpa.space.repository.* import org.springframework.stereotype.Repository @@ -35,21 +34,15 @@ class SpaceManagementJpaAdapter( SpaceMapper.toIndexedSpace(it) }.sortedBy { it.index } - override fun createSpace( - userId: DomainId, - spaceName: String, - templateType: Int, - ): Space { - SpaceEntity - .default( - userId = userId.value, - name = spaceName, - templateType = templateType, - index = spaceJpaRepository.countActiveSpaceByUserId(userId.value), - ).let { - spaceJpaRepository.save(it) - return SpaceMapper.toSpace(it) - } + override fun save(space: Space): Space { + val entity = + SpaceMapper.toSpaceEntity( + space = space, + index = spaceJpaRepository.countActiveSpaceByUserId(space.userId.value), + ) + return spaceJpaRepository.save(entity).let { + SpaceMapper.toSpace(it) + } } override fun update(space: Space): Space = diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/entity/SpaceEntity.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/entity/SpaceEntity.kt index 75fffcb..9ef1d8f 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/entity/SpaceEntity.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/space/entity/SpaceEntity.kt @@ -9,11 +9,12 @@ import jakarta.persistence.* @Entity @Table(name = "space") class SpaceEntity( + id: String, userId: String, name: String, templateType: Int, index: Int, -) : AggregateRoot() { +) : AggregateRoot(id) { @Column(name = "user_id", nullable = false) var userId: String = userId @@ -33,20 +34,6 @@ class SpaceEntity( ) var spaceStatus: EntityStatus = EntityStatus.ACTIVE - companion object { - fun default( - userId: String, - name: String, - templateType: Int, - index: Int, - ) = SpaceEntity( - userId = userId, - name = name, - templateType = templateType, - index = index, - ) - } - fun update(space: Space) { this.userId = space.userId.value this.name = space.name From 7ef51c2d628bf9f26aa02e897d3566fbcd83d784 Mon Sep 17 00:00:00 2001 From: Sim-km Date: Thu, 3 Oct 2024 17:51:25 +0900 Subject: [PATCH 3/4] =?UTF-8?q?ASAP-143=20feat:=20receiveLetter=20jpa=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MemoryReceiveLetterManagementAdapter.kt | 488 +++++++++--------- .../jpa/letter/ReceiverLetterMapper.kt | 54 ++ .../ReceiveLetterManagementJpaAdapter.kt | 140 +++++ .../jpa/letter/entity/ReceiveLetterEntity.kt | 71 ++- .../repository/ReceiveLetterJpaRepository.kt | 118 +++++ 5 files changed, 628 insertions(+), 243 deletions(-) create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/ReceiverLetterMapper.kt create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/ReceiveLetterManagementJpaAdapter.kt create mode 100644 Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/ReceiveLetterJpaRepository.kt diff --git a/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemoryReceiveLetterManagementAdapter.kt b/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemoryReceiveLetterManagementAdapter.kt index 0ee3694..1f51644 100644 --- a/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemoryReceiveLetterManagementAdapter.kt +++ b/Application-Module/src/main/kotlin/com/asap/application/letter/port/out/memory/MemoryReceiveLetterManagementAdapter.kt @@ -1,240 +1,248 @@ -package com.asap.application.letter.port.out.memory - -import com.asap.application.letter.exception.LetterException -import com.asap.application.letter.port.out.IndependentLetterManagementPort -import com.asap.application.letter.port.out.SpaceLetterManagementPort -import com.asap.common.exception.DefaultException -import com.asap.common.page.Page -import com.asap.common.page.PageRequest -import com.asap.domain.common.DomainId -import com.asap.domain.letter.entity.IndependentLetter -import com.asap.domain.letter.entity.SpaceLetter -import com.asap.domain.letter.vo.LetterContent -import com.asap.domain.letter.vo.ReceiverInfo -import com.asap.domain.letter.vo.SenderInfo -import org.springframework.stereotype.Component -import java.time.LocalDate -import java.time.LocalDateTime - -@Component -class MemoryReceiveLetterManagementAdapter : IndependentLetterManagementPort, SpaceLetterManagementPort { - - private val receiveLetters = mutableMapOf() - - - override fun save(letter: IndependentLetter) { - receiveLetters[letter.id.value] = fromDomain(letter) - } - - override fun getAllByReceiverId(receiverId: DomainId): List { - return receiveLetters - .filter { it.value.receiverId == receiverId.value } - .filter { it.value.spaceId == null } - .map { - toIndependentLetterEntity(it.value) - } - } - - override fun getIndependentLetterByIdNotNull(id: DomainId): IndependentLetter { - return receiveLetters[id.value]?.let { - toIndependentLetterEntity(it) - } ?: throw LetterException.ReceiveLetterNotFoundException() - } - - override fun saveBySpaceLetter(letter: SpaceLetter, userId: DomainId): IndependentLetter { - val receiveLetter = receiveLetters[letter.id.value] ?: throw LetterException.ReceiveLetterNotFoundException() - if (receiveLetter.receiverId != userId.value) { - throw DefaultException.InvalidStateException() - } - receiveLetter.spaceId = null - receiveLetter.movedAt = LocalDateTime.now() - return toIndependentLetterEntity(receiveLetter) - } - - override fun save(letter: SpaceLetter) { - receiveLetters[letter.id.value] = fromDomain(letter) - } - - override fun saveByIndependentLetter( - letter: IndependentLetter, - spaceId: DomainId, - userId: DomainId - ): SpaceLetter { - val receiveLetter = receiveLetters[letter.id.value] ?: throw LetterException.ReceiveLetterNotFoundException() - if (receiveLetter.receiverId != userId.value) { - throw DefaultException.InvalidStateException() - } - receiveLetter.spaceId = spaceId.value - receiveLetter.movedAt = LocalDateTime.now() - return toSpaceLetterEntity(receiveLetter) - } - - override fun getSpaceLetterNotNull(id: DomainId, userId: DomainId): SpaceLetter { - return receiveLetters[id.value]?.let { - if (it.receiverId != userId.value) { - throw DefaultException.InvalidStateException() - } - toSpaceLetterEntity(it) - } ?: throw LetterException.ReceiveLetterNotFoundException() - } - - override fun getNearbyLetter( - spaceId: DomainId, - userId: DomainId, - letterId: DomainId - ): Pair { - val letters = receiveLetters - .filter { it.value.spaceId == spaceId.value } - .filter { it.value.receiverId == userId.value } - .keys - .sortedBy { receiveLetters[it]!!.createdAt } - val index = letters.indexOf(letterId.value) - val prev = if (index > 0) { - receiveLetters[letters[index - 1]]?.let { - toSpaceLetterEntity(it) - } - } else { - null - } - val next = if (index < letters.size - 1) { - receiveLetters[letters[index + 1]]?.let { - toSpaceLetterEntity(it) - } - } else { - null - } - return Pair(prev, next) - } - - override fun countLetterBySpaceId(spaceId: DomainId): Long { - return receiveLetters - .filter { it.value.spaceId == spaceId.value } - .count() - .toLong() - } - - override fun getAllBySpaceId(spaceId: DomainId, userId: DomainId, pageRequest: PageRequest): Page { - val letters = receiveLetters - .filter { it.value.spaceId == spaceId.value } - .filter { it.value.receiverId == userId.value } - .keys - .sortedBy { receiveLetters[it]!!.createdAt } - .subList( - pageRequest.page * pageRequest.size, - (pageRequest.page + 1) * pageRequest.size - ) - .map { - toSpaceLetterEntity(receiveLetters[it]!!) - } - val total = receiveLetters - .filter { it.value.spaceId == spaceId.value } - .filter { it.value.receiverId == userId.value } - .count() - return Page.of( - content = letters, - totalElements = total.toLong(), - totalPages = total / pageRequest.size + 1, - size = letters.size, - page = pageRequest.page - ) - } - - override fun delete(letter: SpaceLetter) { - receiveLetters.filter { it.value.spaceId == letter.spaceId.value } - .filter { it.value.receiverId == letter.receiver.receiverId.value } - .filter { it.value.id == letter.id.value } - .keys - .forEach { - receiveLetters.remove(it) - } - } - - - data class ReceiveLetter( - val id: String, - val content: String, - val images: List, - val templateType: Int, - val senderId: String?, - val senderName: String, - val receiverId: String, - val receiveDate: LocalDate, - var spaceId: String?, - val createdAt: LocalDateTime = LocalDateTime.now(), - val updatedAt: LocalDateTime = LocalDateTime.now(), - var movedAt: LocalDateTime = LocalDateTime.now() - ) - - - companion object { - fun fromDomain(letter: IndependentLetter): ReceiveLetter { - return ReceiveLetter( - id = letter.id.value, - content = letter.content.content, - images = letter.content.images, - templateType = letter.content.templateType, - senderId = letter.sender.senderId?.value, - senderName = letter.sender.senderName, - receiverId = letter.receiver.receiverId.value, - receiveDate = letter.receiveDate, - spaceId = null - ) - } - - fun fromDomain(letter: SpaceLetter): ReceiveLetter { - return ReceiveLetter( - id = letter.id.value, - content = letter.content.content, - images = letter.content.images, - templateType = letter.content.templateType, - senderId = letter.sender.senderId?.value, - senderName = letter.sender.senderName, - receiverId = letter.receiver.receiverId.value, - receiveDate = letter.receiveDate, - spaceId = letter.spaceId.value - ) - } - - fun toIndependentLetterEntity(receiveLetter: ReceiveLetter): IndependentLetter { - return IndependentLetter( - id = DomainId(receiveLetter.id), - content = LetterContent( - content = receiveLetter.content, - images = receiveLetter.images, - templateType = receiveLetter.templateType - ), - sender = SenderInfo( - senderId = receiveLetter.senderId?.let { DomainId(it) }, - senderName = receiveLetter.senderName - ), - receiver = ReceiverInfo( - receiverId = DomainId(receiveLetter.receiverId) - ), - receiveDate = receiveLetter.receiveDate, - isNew = receiveLetter.movedAt.isAfter(LocalDateTime.now().minusDays(1)) - ) - } - - fun toSpaceLetterEntity(receiveLetter: ReceiveLetter): SpaceLetter { - return SpaceLetter( - id = DomainId(receiveLetter.id), - content = LetterContent( - content = receiveLetter.content, - images = receiveLetter.images, - templateType = receiveLetter.templateType - ), - sender = SenderInfo( - senderId = receiveLetter.senderId?.let { DomainId(it) }, - senderName = receiveLetter.senderName - ), - receiver = ReceiverInfo( - receiverId = DomainId(receiveLetter.receiverId) - ), - receiveDate = receiveLetter.receiveDate, - spaceId = receiveLetter.spaceId?.let { DomainId(it) } ?: throw DefaultException.InvalidStateException() - ) - } - } - - -} \ No newline at end of file +// package com.asap.application.letter.port.out.memory +// +// import com.asap.application.letter.exception.LetterException +// import com.asap.application.letter.port.out.IndependentLetterManagementPort +// import com.asap.application.letter.port.out.SpaceLetterManagementPort +// import com.asap.common.exception.DefaultException +// import com.asap.common.page.Page +// import com.asap.common.page.PageRequest +// import com.asap.domain.common.DomainId +// import com.asap.domain.letter.entity.IndependentLetter +// import com.asap.domain.letter.entity.SpaceLetter +// import com.asap.domain.letter.vo.LetterContent +// import com.asap.domain.letter.vo.ReceiverInfo +// import com.asap.domain.letter.vo.SenderInfo +// import java.time.LocalDate +// import java.time.LocalDateTime +// +// // @Component +// class MemoryReceiveLetterManagementAdapter : +// IndependentLetterManagementPort, +// SpaceLetterManagementPort { +// private val receiveLetters = mutableMapOf() +// +// override fun save(letter: IndependentLetter) { +// receiveLetters[letter.id.value] = fromDomain(letter) +// } +// +// override fun getAllByReceiverId(receiverId: DomainId): List = +// receiveLetters +// .filter { it.value.receiverId == receiverId.value } +// .filter { it.value.spaceId == null } +// .map { +// toIndependentLetterEntity(it.value) +// } +// +// override fun getIndependentLetterByIdNotNull(id: DomainId): IndependentLetter = +// receiveLetters[id.value]?.let { +// toIndependentLetterEntity(it) +// } ?: throw LetterException.ReceiveLetterNotFoundException() +// +// override fun saveBySpaceLetter( +// letter: SpaceLetter, +// userId: DomainId, +// ): IndependentLetter { +// val receiveLetter = receiveLetters[letter.id.value] ?: throw LetterException.ReceiveLetterNotFoundException() +// if (receiveLetter.receiverId != userId.value) { +// throw DefaultException.InvalidStateException() +// } +// receiveLetter.spaceId = null +// receiveLetter.movedAt = LocalDateTime.now() +// return toIndependentLetterEntity(receiveLetter) +// } +// +// override fun save(letter: SpaceLetter) { +// receiveLetters[letter.id.value] = fromDomain(letter) +// } +// +// override fun saveByIndependentLetter( +// letter: IndependentLetter, +// spaceId: DomainId, +// userId: DomainId, +// ): SpaceLetter { +// val receiveLetter = receiveLetters[letter.id.value] ?: throw LetterException.ReceiveLetterNotFoundException() +// if (receiveLetter.receiverId != userId.value) { +// throw DefaultException.InvalidStateException() +// } +// receiveLetter.spaceId = spaceId.value +// receiveLetter.movedAt = LocalDateTime.now() +// return toSpaceLetterEntity(receiveLetter) +// } +// +// override fun getSpaceLetterNotNull( +// id: DomainId, +// userId: DomainId, +// ): SpaceLetter = +// receiveLetters[id.value]?.let { +// if (it.receiverId != userId.value) { +// throw DefaultException.InvalidStateException() +// } +// toSpaceLetterEntity(it) +// } ?: throw LetterException.ReceiveLetterNotFoundException() +// +// override fun getNearbyLetter( +// spaceId: DomainId, +// userId: DomainId, +// letterId: DomainId, +// ): Pair { +// val letters = +// receiveLetters +// .filter { it.value.spaceId == spaceId.value } +// .filter { it.value.receiverId == userId.value } +// .keys +// .sortedBy { receiveLetters[it]!!.createdAt } +// val index = letters.indexOf(letterId.value) +// val prev = +// if (index > 0) { +// receiveLetters[letters[index - 1]]?.let { +// toSpaceLetterEntity(it) +// } +// } else { +// null +// } +// val next = +// if (index < letters.size - 1) { +// receiveLetters[letters[index + 1]]?.let { +// toSpaceLetterEntity(it) +// } +// } else { +// null +// } +// return Pair(prev, next) +// } +// +// override fun countLetterBySpaceId(spaceId: DomainId): Long = +// receiveLetters +// .filter { it.value.spaceId == spaceId.value } +// .count() +// .toLong() +// +// override fun getAllBySpaceId( +// spaceId: DomainId, +// userId: DomainId, +// pageRequest: PageRequest, +// ): Page { +// val letters = +// receiveLetters +// .filter { it.value.spaceId == spaceId.value } +// .filter { it.value.receiverId == userId.value } +// .keys +// .sortedBy { receiveLetters[it]!!.createdAt } +// .subList( +// pageRequest.page * pageRequest.size, +// (pageRequest.page + 1) * pageRequest.size, +// ).map { +// toSpaceLetterEntity(receiveLetters[it]!!) +// } +// val total = +// receiveLetters +// .filter { it.value.spaceId == spaceId.value } +// .filter { it.value.receiverId == userId.value } +// .count() +// return Page.of( +// content = letters, +// totalElements = total.toLong(), +// totalPages = total / pageRequest.size + 1, +// size = letters.size, +// page = pageRequest.page, +// ) +// } +// +// override fun delete(letter: SpaceLetter) { +// receiveLetters +// .filter { it.value.spaceId == letter.spaceId.value } +// .filter { it.value.receiverId == letter.receiver.receiverId.value } +// .filter { it.value.id == letter.id.value } +// .keys +// .forEach { +// receiveLetters.remove(it) +// } +// } +// +// data class ReceiveLetter( +// val id: String, +// val content: String, +// val images: List, +// val templateType: Int, +// val senderId: String?, +// val senderName: String, +// val receiverId: String, +// val receiveDate: LocalDate, +// var spaceId: String?, +// val createdAt: LocalDateTime = LocalDateTime.now(), +// val updatedAt: LocalDateTime = LocalDateTime.now(), +// var movedAt: LocalDateTime = LocalDateTime.now(), +// ) +// +// companion object { +// fun fromDomain(letter: IndependentLetter): ReceiveLetter = +// ReceiveLetter( +// id = letter.id.value, +// content = letter.content.content, +// images = letter.content.images, +// templateType = letter.content.templateType, +// senderId = letter.sender.senderId?.value, +// senderName = letter.sender.senderName, +// receiverId = letter.receiver.receiverId.value, +// receiveDate = letter.receiveDate, +// spaceId = null, +// ) +// +// fun fromDomain(letter: SpaceLetter): ReceiveLetter = +// ReceiveLetter( +// id = letter.id.value, +// content = letter.content.content, +// images = letter.content.images, +// templateType = letter.content.templateType, +// senderId = letter.sender.senderId?.value, +// senderName = letter.sender.senderName, +// receiverId = letter.receiver.receiverId.value, +// receiveDate = letter.receiveDate, +// spaceId = letter.spaceId.value, +// ) +// +// fun toIndependentLetterEntity(receiveLetter: ReceiveLetter): IndependentLetter = +// IndependentLetter( +// id = DomainId(receiveLetter.id), +// content = +// LetterContent( +// content = receiveLetter.content, +// images = receiveLetter.images, +// templateType = receiveLetter.templateType, +// ), +// sender = +// SenderInfo( +// senderId = receiveLetter.senderId?.let { DomainId(it) }, +// senderName = receiveLetter.senderName, +// ), +// receiver = +// ReceiverInfo( +// receiverId = DomainId(receiveLetter.receiverId), +// ), +// receiveDate = receiveLetter.receiveDate, +// isNew = receiveLetter.movedAt.isAfter(LocalDateTime.now().minusDays(1)), +// ) +// +// fun toSpaceLetterEntity(receiveLetter: ReceiveLetter): SpaceLetter = +// SpaceLetter( +// id = DomainId(receiveLetter.id), +// content = +// LetterContent( +// content = receiveLetter.content, +// images = receiveLetter.images, +// templateType = receiveLetter.templateType, +// ), +// sender = +// SenderInfo( +// senderId = receiveLetter.senderId?.let { DomainId(it) }, +// senderName = receiveLetter.senderName, +// ), +// receiver = +// ReceiverInfo( +// receiverId = DomainId(receiveLetter.receiverId), +// ), +// receiveDate = receiveLetter.receiveDate, +// spaceId = receiveLetter.spaceId?.let { DomainId(it) } ?: throw DefaultException.InvalidStateException(), +// ) +// } +// } diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/ReceiverLetterMapper.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/ReceiverLetterMapper.kt new file mode 100644 index 0000000..b0c731e --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/ReceiverLetterMapper.kt @@ -0,0 +1,54 @@ +package com.asap.persistence.jpa.letter + +import com.asap.domain.common.DomainId +import com.asap.domain.letter.entity.IndependentLetter +import com.asap.domain.letter.entity.SpaceLetter +import com.asap.domain.letter.vo.LetterContent +import com.asap.domain.letter.vo.ReceiverInfo +import com.asap.domain.letter.vo.SenderInfo +import com.asap.persistence.jpa.letter.entity.ReceiveLetterEntity + +object ReceiverLetterMapper { + fun toIndependentLetter(receiveLetterEntity: ReceiveLetterEntity): IndependentLetter = + IndependentLetter( + id = DomainId(receiveLetterEntity.id), + content = + LetterContent( + content = receiveLetterEntity.content, + templateType = receiveLetterEntity.templateType, + images = receiveLetterEntity.images, + ), + sender = + SenderInfo( + senderId = receiveLetterEntity.senderId?.let { DomainId(it) }, + senderName = receiveLetterEntity.senderName, + ), + receiver = + ReceiverInfo( + receiverId = DomainId(receiveLetterEntity.receiverId), + ), + receiveDate = receiveLetterEntity.receiveDate, + ) + + fun toSpaceLetter(receiveLetterEntity: ReceiveLetterEntity): SpaceLetter = + SpaceLetter( + id = DomainId(receiveLetterEntity.id), + content = + LetterContent( + content = receiveLetterEntity.content, + templateType = receiveLetterEntity.templateType, + images = receiveLetterEntity.images, + ), + sender = + SenderInfo( + senderId = receiveLetterEntity.senderId?.let { DomainId(it) }, + senderName = receiveLetterEntity.senderName, + ), + receiver = + ReceiverInfo( + receiverId = DomainId(receiveLetterEntity.receiverId), + ), + receiveDate = receiveLetterEntity.receiveDate, + spaceId = receiveLetterEntity.spaceId!!.let { DomainId(it) }, + ) +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/ReceiveLetterManagementJpaAdapter.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/ReceiveLetterManagementJpaAdapter.kt new file mode 100644 index 0000000..46345fb --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/adapter/ReceiveLetterManagementJpaAdapter.kt @@ -0,0 +1,140 @@ +package com.asap.persistence.jpa.letter.adapter + +import com.asap.application.letter.exception.LetterException +import com.asap.application.letter.port.out.IndependentLetterManagementPort +import com.asap.application.letter.port.out.SpaceLetterManagementPort +import com.asap.common.page.Page +import com.asap.common.page.PageRequest +import com.asap.domain.common.DomainId +import com.asap.domain.letter.entity.IndependentLetter +import com.asap.domain.letter.entity.SpaceLetter +import com.asap.persistence.jpa.letter.ReceiverLetterMapper +import com.asap.persistence.jpa.letter.entity.ReceiveLetterEntity +import com.asap.persistence.jpa.letter.repository.* +import org.springframework.stereotype.Repository + +@Repository +class ReceiveLetterManagementJpaAdapter( + private val receiveLetterJpaRepository: ReceiveLetterJpaRepository, +) : IndependentLetterManagementPort, + SpaceLetterManagementPort { + override fun save(letter: IndependentLetter) { + ReceiveLetterEntity( + id = letter.id.value, + content = letter.content.content, + templateType = letter.content.templateType, + images = letter.content.images, + senderId = letter.sender.senderId?.value, + senderName = letter.sender.senderName, + receiverId = letter.receiver.receiverId.value, + receiveDate = letter.receiveDate, + ).apply { + receiveLetterJpaRepository.save(this) + } + } + + override fun getAllByReceiverId(receiverId: DomainId): List = + receiveLetterJpaRepository + .findAllIndependentByReceiverId(receiverId.value) + .map { ReceiverLetterMapper.toIndependentLetter(it) } + + override fun getIndependentLetterByIdNotNull(id: DomainId): IndependentLetter = + receiveLetterJpaRepository + .findIndependentById(id.value) + ?.let { ReceiverLetterMapper.toIndependentLetter(it) } + ?: throw LetterException.ReceiveLetterNotFoundException() + + override fun saveBySpaceLetter( + letter: SpaceLetter, + userId: DomainId, + ): IndependentLetter { + val receiveLetter = + receiveLetterJpaRepository.findSpaceByIdAndReceiverId(letter.id.value, userId.value) + ?: throw LetterException.ReceiveLetterNotFoundException() + receiveLetter.spaceId = null + return ReceiverLetterMapper.toIndependentLetter(receiveLetter) + } + + override fun save(letter: SpaceLetter) { + ReceiveLetterEntity( + id = letter.id.value, + content = letter.content.content, + templateType = letter.content.templateType, + images = letter.content.images, + senderId = letter.sender.senderId?.value, + senderName = letter.sender.senderName, + receiverId = letter.receiver.receiverId.value, + receiveDate = letter.receiveDate, + spaceId = letter.spaceId.value, + ).apply { + receiveLetterJpaRepository.save(this) + } + } + + override fun saveByIndependentLetter( + letter: IndependentLetter, + spaceId: DomainId, + userId: DomainId, + ): SpaceLetter { + val receiveLetter = + receiveLetterJpaRepository.findIndependentByIdAndReceiverId(letter.id.value, userId.value) + ?: throw LetterException.ReceiveLetterNotFoundException() + receiveLetter.spaceId = spaceId.value + return ReceiverLetterMapper.toSpaceLetter(receiveLetter) + } + + override fun getSpaceLetterNotNull( + id: DomainId, + userId: DomainId, + ): SpaceLetter = + receiveLetterJpaRepository + .findSpaceByIdAndReceiverId(id.value, userId.value) + ?.let { ReceiverLetterMapper.toSpaceLetter(it) } + ?: throw LetterException.ReceiveLetterNotFoundException() + + override fun getNearbyLetter( + spaceId: DomainId, + userId: DomainId, + letterId: DomainId, + ): Pair { + val letters = + receiveLetterJpaRepository + .findAllSpaceBySpaceIdAndReceiverId(spaceId.value, userId.value) + .sortedBy { it.receiveDate } + val index = letters.indexOfFirst { it.id == letterId.value } + return Pair( + letters.getOrNull(index - 1)?.let { ReceiverLetterMapper.toSpaceLetter(it) }, + letters.getOrNull(index + 1)?.let { ReceiverLetterMapper.toSpaceLetter(it) }, + ) + } + + override fun countLetterBySpaceId(spaceId: DomainId): Long = receiveLetterJpaRepository.countBySpaceId(spaceId.value).toLong() + + override fun getAllBySpaceId( + spaceId: DomainId, + userId: DomainId, + pageRequest: PageRequest, + ): Page { + val letters = + receiveLetterJpaRepository + .findAllBySpaceIdAndReceiverId( + spaceId.value, + userId.value, + org.springframework.data.domain.PageRequest.of( + pageRequest.page, + pageRequest.size, + ), + ).map { ReceiverLetterMapper.toSpaceLetter(it) } + return Page.of( + content = letters.content, + totalElements = letters.totalElements, + totalPages = letters.totalPages, + size = letters.size, + page = pageRequest.page, + ) + } + + override fun delete(letter: SpaceLetter) { + receiveLetterJpaRepository.deleteByLetterId(letter.id.value) + } +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt index fddb363..b4bc517 100644 --- a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/entity/ReceiveLetterEntity.kt @@ -1,11 +1,76 @@ package com.asap.persistence.jpa.letter.entity import com.asap.persistence.jpa.common.AggregateRoot -import jakarta.persistence.Entity -import jakarta.persistence.Table +import com.asap.persistence.jpa.common.EntityStatus +import com.asap.persistence.jpa.space.entity.SpaceEntity +import com.asap.persistence.jpa.user.entity.UserEntity +import jakarta.persistence.* +import org.hibernate.annotations.JdbcTypeCode +import org.hibernate.type.SqlTypes +import java.time.LocalDate @Entity @Table(name = "receive_letter") class ReceiveLetterEntity( id: String, -) : AggregateRoot(id) + content: String, + images: List, + templateType: Int, + senderId: String? = null, + senderName: String, + receiverId: String, + receiveDate: LocalDate, + spaceId: String? = null, +) : AggregateRoot(id) { + @Column( + name = "content", + nullable = false, + columnDefinition = "TEXT", + ) + var content: String = content + + @Column( + name = "images", + nullable = false, + columnDefinition = "json", + ) + @JdbcTypeCode(SqlTypes.JSON) + var images: List = images + + var templateType: Int = templateType + + @Column( + name = "sender_id", + nullable = true, + ) + var senderId: String? = senderId + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "sender_id", insertable = false, updatable = false) + var sender: UserEntity? = null + var senderName: String = senderName + + @Column( + name = "receiver_id", + nullable = false, + ) + var receiverId: String = receiverId + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "receiver_id", insertable = false, updatable = false) + lateinit var receiver: UserEntity + + var receiveDate: LocalDate = receiveDate + + @Column( + name = "space_id", + nullable = true, + ) + var spaceId: String? = spaceId + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "space_id", insertable = false, updatable = false) + var space: SpaceEntity? = null + + var entityStatus: EntityStatus = EntityStatus.ACTIVE +} diff --git a/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/ReceiveLetterJpaRepository.kt b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/ReceiveLetterJpaRepository.kt new file mode 100644 index 0000000..eb1b144 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/kotlin/com/asap/persistence/jpa/letter/repository/ReceiveLetterJpaRepository.kt @@ -0,0 +1,118 @@ +package com.asap.persistence.jpa.letter.repository + +import com.asap.persistence.jpa.common.EntityStatus +import com.asap.persistence.jpa.letter.entity.ReceiveLetterEntity +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Modifying +import org.springframework.data.jpa.repository.Query + +interface ReceiveLetterJpaRepository : JpaRepository { + @Query( + """ + SELECT r + FROM ReceiveLetterEntity r + WHERE r.receiverId = :receiverId + AND ((r.spaceId is not null and :isSpaceLetter = true) or (r.spaceId is null and :isSpaceLetter = false)) + AND r.entityStatus = :entityStatus + """, + ) + fun findAllByReceiverId( + receiverId: String, + isSpaceLetter: Boolean, + entityStatus: EntityStatus, + ): List + + @Query( + """ + SELECT r + FROM ReceiveLetterEntity r + WHERE r.spaceId = :spaceId + AND r.receiverId = :receiverId + AND r.entityStatus = :entityStatus + """, + ) + fun findAllBySpaceIdAndReceiverId( + spaceId: String, + receiverId: String, + entityStatus: EntityStatus, + ): List + + @Query( + """ + SELECT r + FROM ReceiveLetterEntity r + WHERE r.id = :letterId + AND ((r.spaceId is not null and :isSpaceLetter = true) or (r.spaceId is null and :isSpaceLetter = false)) + AND r.entityStatus = :entityStatus + """, + ) + fun findBy( + letterId: String, + isSpaceLetter: Boolean, + entityStatus: EntityStatus, + ): ReceiveLetterEntity? + + @Query( + """ + SELECT r + FROM ReceiveLetterEntity r + WHERE r.id = :letterId + AND r.receiverId = :receiverId + AND ((r.spaceId is not null and :isSpaceLetter = true) or (r.spaceId is null and :isSpaceLetter = false)) + AND r.entityStatus = :entityStatus + """, + ) + fun findBy( + letterId: String, + receiverId: String, + isSpaceLetter: Boolean, + entityStatus: EntityStatus, + ): ReceiveLetterEntity? + + fun countBySpaceId(spaceId: String): Int + + fun findAllBySpaceIdAndReceiverId( + spaceId: String, + receiverId: String, + pageable: Pageable, + ): Page + + @Modifying + @Query( + """ + UPDATE ReceiveLetterEntity r + SET r.entityStatus = :entityStatus + WHERE r.id = :id + """, + ) + fun updateEntityStatusById( + id: String, + entityStatus: EntityStatus, + ) +} + +fun ReceiveLetterJpaRepository.findAllIndependentByReceiverId(receiverId: String): List = + findAllByReceiverId(receiverId, false, EntityStatus.ACTIVE) + +fun ReceiveLetterJpaRepository.findAllSpaceBySpaceIdAndReceiverId( + spaceId: String, + receiverId: String, +): List = findAllBySpaceIdAndReceiverId(spaceId, receiverId, EntityStatus.ACTIVE) + +fun ReceiveLetterJpaRepository.findIndependentById(letterId: String): ReceiveLetterEntity? = findBy(letterId, false, EntityStatus.ACTIVE) + +fun ReceiveLetterJpaRepository.findSpaceByIdAndReceiverId( + letterId: String, + receiverId: String, +): ReceiveLetterEntity? = findBy(letterId, receiverId, true, EntityStatus.ACTIVE) + +fun ReceiveLetterJpaRepository.findIndependentByIdAndReceiverId( + letterId: String, + receiverId: String, +): ReceiveLetterEntity? = findBy(letterId, receiverId, false, EntityStatus.ACTIVE) + +fun ReceiveLetterJpaRepository.deleteByLetterId(id: String) { + updateEntityStatusById(id, EntityStatus.DELETED) +} From c69415905538f072173fb542acd8722545d09103 Mon Sep 17 00:00:00 2001 From: Sim-km Date: Thu, 3 Oct 2024 17:54:29 +0900 Subject: [PATCH 4/4] =?UTF-8?q?ASAP-143=20chore:=20flyway=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/db/V1_5__create_letter.sql | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Infrastructure-Module/Persistence/src/main/resources/db/V1_5__create_letter.sql diff --git a/Infrastructure-Module/Persistence/src/main/resources/db/V1_5__create_letter.sql b/Infrastructure-Module/Persistence/src/main/resources/db/V1_5__create_letter.sql new file mode 100644 index 0000000..69930e9 --- /dev/null +++ b/Infrastructure-Module/Persistence/src/main/resources/db/V1_5__create_letter.sql @@ -0,0 +1,53 @@ +CREATE TABLE receive_letter +( + id VARCHAR(255) NOT NULL, + created_at datetime NULL, + updated_at datetime NULL, + content TEXT NOT NULL, + images JSON NOT NULL, + template_type INT NOT NULL, + sender_id VARCHAR(255) NULL, + sender_name VARCHAR(255) NULL, + receiver_id VARCHAR(255) NOT NULL, + receive_date date NULL, + space_id VARCHAR(255) NULL, + entity_status SMALLINT NULL, + CONSTRAINT pk_receive_letter PRIMARY KEY (id) +); + + + +ALTER TABLE receive_letter + ADD CONSTRAINT FK_RECEIVE_LETTER_ON_RECEIVER FOREIGN KEY (receiver_id) REFERENCES user (id); + +ALTER TABLE receive_letter + ADD CONSTRAINT FK_RECEIVE_LETTER_ON_SENDER FOREIGN KEY (sender_id) REFERENCES user (id); + +ALTER TABLE receive_letter + ADD CONSTRAINT FK_RECEIVE_LETTER_ON_SPACE FOREIGN KEY (space_id) REFERENCES space (id); + + +CREATE TABLE send_letter +( + id VARCHAR(255) NOT NULL, + created_at datetime NULL, + updated_at datetime NULL, + content TEXT NOT NULL, + images JSON NOT NULL, + template_type INT NOT NULL, + sender_id VARCHAR(255) NOT NULL, + receiver_id VARCHAR(255) NULL, + receiver_name VARCHAR(255) NULL, + letter_code VARCHAR(255) NULL, + letter_status VARCHAR(20) NOT NULL, + entity_status VARCHAR(20) NOT NULL, + CONSTRAINT pk_send_letter PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX idx_letter_code ON send_letter (letter_code); + +ALTER TABLE send_letter + ADD CONSTRAINT FK_SEND_LETTER_ON_RECEIVER FOREIGN KEY (receiver_id) REFERENCES user (id); + +ALTER TABLE send_letter + ADD CONSTRAINT FK_SEND_LETTER_ON_SENDER FOREIGN KEY (sender_id) REFERENCES user (id); \ No newline at end of file