Skip to content
This repository was archived by the owner on Jul 7, 2025. It is now read-only.

Commit 4ce6ceb

Browse files
authored
Merge pull request #112 from ASAP-Lettering/ASAP-394
ASAP-394 실물 편지 임시저장 조회 api 추가
2 parents 6c437f4 + df3bd1b commit 4ce6ceb

File tree

13 files changed

+418
-12
lines changed

13 files changed

+418
-12
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.asap.application.letter.port.`in`
2+
3+
import java.time.LocalDateTime
4+
5+
interface GetPhysicalDraftLetterUsecase {
6+
7+
fun getAll(query: Query.All): Response.All
8+
9+
fun getByKey(query: Query.ByKey): Response.ByKey
10+
11+
sealed class Query {
12+
data class All(
13+
val userId: String,
14+
) : Query()
15+
16+
data class ByKey(
17+
val draftKey: String,
18+
val userId: String,
19+
) : Query()
20+
}
21+
22+
sealed class Response {
23+
data class All(
24+
val drafts: List<ByKey>,
25+
) : Response()
26+
27+
data class ByKey(
28+
val draftKey: String,
29+
val senderName: String,
30+
val content: String,
31+
val images: List<String>,
32+
val lastUpdated: LocalDateTime,
33+
) : Response()
34+
}
35+
}

Application-Module/src/main/kotlin/com/asap/application/letter/port/out/ReceiveDraftLetterManagementPort.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ interface ReceiveDraftLetterManagementPort {
77
fun save(receiveDraftLetter: ReceiveDraftLetter): ReceiveDraftLetter
88

99
fun getDraftLetterNotNull(draftId: DomainId, ownerId: DomainId): ReceiveDraftLetter
10+
11+
fun getAllDrafts(ownerId: DomainId): List<ReceiveDraftLetter>
1012
}

Application-Module/src/main/kotlin/com/asap/application/letter/service/DraftLetterQueryService.kt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.asap.application.letter.service
22

33
import com.asap.application.letter.port.`in`.GetDraftLetterUsecase
4+
import com.asap.application.letter.port.`in`.GetPhysicalDraftLetterUsecase
45
import com.asap.application.letter.port.out.DraftLetterManagementPort
6+
import com.asap.application.letter.port.out.ReceiveDraftLetterManagementPort
57
import com.asap.domain.common.DomainId
68
import org.springframework.stereotype.Service
79
import org.springframework.transaction.annotation.Transactional
@@ -10,7 +12,8 @@ import org.springframework.transaction.annotation.Transactional
1012
@Transactional(readOnly = true)
1113
class DraftLetterQueryService(
1214
private val draftLetterManagementPort: DraftLetterManagementPort,
13-
) : GetDraftLetterUsecase {
15+
private val receiveDraftLetterManagementPort: ReceiveDraftLetterManagementPort,
16+
) : GetDraftLetterUsecase, GetPhysicalDraftLetterUsecase {
1417
override fun getAll(query: GetDraftLetterUsecase.Query.All): GetDraftLetterUsecase.Response.All =
1518
GetDraftLetterUsecase.Response.All(
1619
drafts =
@@ -46,4 +49,33 @@ class DraftLetterQueryService(
4649
val count = draftLetterManagementPort.countDrafts(DomainId(query.userId))
4750
return GetDraftLetterUsecase.Response.Count(count)
4851
}
52+
53+
override fun getAll(query: GetPhysicalDraftLetterUsecase.Query.All): GetPhysicalDraftLetterUsecase.Response.All {
54+
val drafts = receiveDraftLetterManagementPort.getAllDrafts(DomainId(query.userId))
55+
return GetPhysicalDraftLetterUsecase.Response.All(
56+
drafts = drafts.map {
57+
GetPhysicalDraftLetterUsecase.Response.ByKey(
58+
draftKey = it.id.value,
59+
content = it.content,
60+
images = it.images,
61+
senderName = it.senderName,
62+
lastUpdated = it.lastUpdated
63+
)
64+
}
65+
)
66+
}
67+
68+
override fun getByKey(query: GetPhysicalDraftLetterUsecase.Query.ByKey): GetPhysicalDraftLetterUsecase.Response.ByKey {
69+
val draft = receiveDraftLetterManagementPort.getDraftLetterNotNull(
70+
ownerId = DomainId(query.userId),
71+
draftId = DomainId(query.draftKey),
72+
)
73+
return GetPhysicalDraftLetterUsecase.Response.ByKey(
74+
draftKey = draft.id.value,
75+
senderName = draft.senderName,
76+
content = draft.content,
77+
images = draft.images,
78+
lastUpdated = draft.lastUpdated
79+
)
80+
}
4981
}

Application-Module/src/test/kotlin/com/asap/application/letter/service/DraftLetterQueryServiceTest.kt

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package com.asap.application.letter.service
22

33
import com.asap.application.letter.exception.LetterException
44
import com.asap.application.letter.port.`in`.GetDraftLetterUsecase
5+
import com.asap.application.letter.port.`in`.GetPhysicalDraftLetterUsecase
56
import com.asap.application.letter.port.out.DraftLetterManagementPort
7+
import com.asap.application.letter.port.out.ReceiveDraftLetterManagementPort
68
import com.asap.domain.common.DomainId
79
import com.asap.domain.letter.entity.DraftLetter
10+
import com.asap.domain.letter.entity.ReceiveDraftLetter
811
import io.kotest.assertions.throwables.shouldThrow
912
import io.kotest.core.spec.style.BehaviorSpec
1013
import io.kotest.matchers.nulls.shouldNotBeNull
@@ -16,8 +19,10 @@ class DraftLetterQueryServiceTest :
1619
BehaviorSpec({
1720

1821
val mockDraftLetterManagementPort = mockk<DraftLetterManagementPort>()
22+
val mockReceiveLetterManagementPort = mockk<ReceiveDraftLetterManagementPort>()
1923

20-
val draftLetterQueryService = DraftLetterQueryService(mockDraftLetterManagementPort)
24+
val draftLetterQueryService =
25+
DraftLetterQueryService(mockDraftLetterManagementPort, mockReceiveLetterManagementPort)
2126

2227
given("임시 저장 편지를 조회할 떄") {
2328
val userId = "userId"
@@ -44,7 +49,8 @@ class DraftLetterQueryServiceTest :
4449
)
4550
} returns draftLetter
4651
`when`("사용자 아이디와 임시 저장 편지 아이디를 입력하면") {
47-
val response = draftLetterQueryService.getByKey(GetDraftLetterUsecase.Query.ByKey(draftLetter.id.value, userId))
52+
val response =
53+
draftLetterQueryService.getByKey(GetDraftLetterUsecase.Query.ByKey(draftLetter.id.value, userId))
4854
then("임시 저장 편지를 반환한다") {
4955
response.shouldNotBeNull {
5056
this.draftKey shouldBe draftLetter.id.value
@@ -61,7 +67,12 @@ class DraftLetterQueryServiceTest :
6167
} throws LetterException.DraftLetterNotFoundException()
6268
then("임시 저장 편지를 찾을 수 없다는 예외를 반환한다") {
6369
shouldThrow<LetterException.DraftLetterNotFoundException> {
64-
draftLetterQueryService.getByKey(GetDraftLetterUsecase.Query.ByKey(draftLetter.id.value, userId))
70+
draftLetterQueryService.getByKey(
71+
GetDraftLetterUsecase.Query.ByKey(
72+
draftLetter.id.value,
73+
userId
74+
)
75+
)
6576
}
6677
}
6778
}
@@ -79,4 +90,58 @@ class DraftLetterQueryServiceTest :
7990
}
8091
}
8192
}
93+
94+
given("임시 저장한 실물 편지를 조회할 때"){
95+
val userId = "userId"
96+
val query = GetPhysicalDraftLetterUsecase.Query.All(userId)
97+
val draftLetters = listOf(ReceiveDraftLetter.default(DomainId(userId)))
98+
every { mockReceiveLetterManagementPort.getAllDrafts(DomainId(userId)) } returns draftLetters
99+
`when`("사용자 아이디를 입력하면") {
100+
val response = draftLetterQueryService.getAll(query)
101+
then("임시 저장한 실물 편지 목록을 반환한다") {
102+
response.shouldNotBeNull {
103+
this.drafts.size shouldBe draftLetters.size
104+
this.drafts.forEachIndexed { index, draftLetter ->
105+
draftLetter.draftKey shouldBe draftLetters[index].id.value
106+
}
107+
}
108+
}
109+
}
110+
111+
val draftLetter = ReceiveDraftLetter.default(DomainId(userId))
112+
every {
113+
mockReceiveLetterManagementPort.getDraftLetterNotNull(
114+
draftId = draftLetter.id,
115+
ownerId = DomainId(userId),
116+
)
117+
} returns draftLetter
118+
`when`("사용자 아이디와 임시 저장한 실물 편지 아이디를 입력하면") {
119+
val response =
120+
draftLetterQueryService.getByKey(GetPhysicalDraftLetterUsecase.Query.ByKey(draftLetter.id.value, userId))
121+
then("임시 저장한 실물 편지를 반환한다") {
122+
response.shouldNotBeNull {
123+
this.draftKey shouldBe draftLetter.id.value
124+
}
125+
}
126+
}
127+
128+
`when`("임시 저장한 실물 편지가 없다면") {
129+
every {
130+
mockReceiveLetterManagementPort.getDraftLetterNotNull(
131+
draftId = draftLetter.id,
132+
ownerId = DomainId(userId),
133+
)
134+
} throws LetterException.DraftLetterNotFoundException()
135+
then("임시 저장한 실물 편지를 찾을 수 없다는 예외를 반환한다") {
136+
shouldThrow<LetterException.DraftLetterNotFoundException> {
137+
draftLetterQueryService.getByKey(
138+
GetPhysicalDraftLetterUsecase.Query.ByKey(
139+
draftLetter.id.value,
140+
userId
141+
)
142+
)
143+
}
144+
}
145+
}
146+
}
82147
})

Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/api/DraftLetterApi.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,25 @@ interface DraftLetterApi {
9696
@AccessUser userId: String,
9797
): GetAllDraftLetterResponse
9898

99+
@Operation(summary = "실물 편지 임시 저장 목록 조회")
100+
@GetMapping("/physical")
101+
@ApiResponses(
102+
value = [
103+
ApiResponse(
104+
responseCode = "200",
105+
description = "임시 저장 목록 조회 성공",
106+
content = [
107+
Content(
108+
schema = Schema(implementation = GetAllPhysicalDraftLetterResponse::class),
109+
),
110+
],
111+
),
112+
],
113+
)
114+
fun getAllPhysicalDrafts(
115+
@AccessUser userId: String,
116+
): GetAllPhysicalDraftLetterResponse
117+
99118
@Operation(summary = "임시 저장 조회")
100119
@GetMapping("/{draftKey}")
101120
@ApiResponses(
@@ -116,6 +135,26 @@ interface DraftLetterApi {
116135
@AccessUser userId: String,
117136
): GetDraftLetterResponse
118137

138+
@Operation(summary = "실물 편지 임시 저장 조회")
139+
@GetMapping("/physical/{draftKey}")
140+
@ApiResponses(
141+
value = [
142+
ApiResponse(
143+
responseCode = "200",
144+
description = "임시 저장 조회 성공",
145+
content = [
146+
Content(
147+
schema = Schema(implementation = GetPhysicalDraftLetterResponse::class),
148+
),
149+
],
150+
),
151+
],
152+
)
153+
fun getPhysicalDraftLetter(
154+
@PathVariable draftKey: String,
155+
@AccessUser userId: String,
156+
): GetPhysicalDraftLetterResponse
157+
119158
@Operation(summary = "임시 저장 개수 조회")
120159
@GetMapping("/count")
121160
@ApiResponses(

Bootstrap-Module/src/main/kotlin/com/asap/bootstrap/web/letter/controller/DraftLetterController.kt

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.asap.bootstrap.web.letter.controller
22

3-
import com.asap.application.letter.port.`in`.GenerateDraftKeyUsecase
4-
import com.asap.application.letter.port.`in`.GetDraftLetterUsecase
5-
import com.asap.application.letter.port.`in`.RemoveDraftLetterUsecase
6-
import com.asap.application.letter.port.`in`.UpdateDraftLetterUsecase
3+
import com.asap.application.letter.port.`in`.*
74
import com.asap.bootstrap.web.letter.api.DraftLetterApi
85
import com.asap.bootstrap.web.letter.dto.*
96
import org.springframework.web.bind.annotation.RestController
@@ -13,6 +10,7 @@ class DraftLetterController(
1310
private val generateDraftKeyUsecase: GenerateDraftKeyUsecase,
1411
private val updateDraftLetterUsecase: UpdateDraftLetterUsecase,
1512
private val getDraftLetterUsecase: GetDraftLetterUsecase,
13+
private val getPhysicalDraftLetterUsecase: GetPhysicalDraftLetterUsecase,
1614
private val removeDraftLetterUsecase: RemoveDraftLetterUsecase,
1715
) : DraftLetterApi {
1816
override fun getDraftKey(userId: String): GenerateDraftKeyResponse {
@@ -74,6 +72,20 @@ class DraftLetterController(
7472
)
7573
}
7674

75+
override fun getAllPhysicalDrafts(userId: String): GetAllPhysicalDraftLetterResponse {
76+
val response = getPhysicalDraftLetterUsecase.getAll(GetPhysicalDraftLetterUsecase.Query.All(userId))
77+
return GetAllPhysicalDraftLetterResponse(
78+
drafts = response.drafts.map {
79+
PhysicalDraftLetterInfo(
80+
draftKey = it.draftKey,
81+
senderName = it.senderName,
82+
content = it.content,
83+
lastUpdated = it.lastUpdated,
84+
)
85+
}
86+
)
87+
}
88+
7789
override fun getDraftLetter(
7890
draftKey: String,
7991
userId: String,
@@ -93,6 +105,19 @@ class DraftLetterController(
93105
)
94106
}
95107

108+
override fun getPhysicalDraftLetter(
109+
draftKey: String,
110+
userId: String
111+
): GetPhysicalDraftLetterResponse {
112+
val response = getPhysicalDraftLetterUsecase.getByKey(GetPhysicalDraftLetterUsecase.Query.ByKey(draftKey, userId))
113+
return GetPhysicalDraftLetterResponse(
114+
draftKey = response.draftKey,
115+
senderName = response.senderName,
116+
content = response.content,
117+
images = response.images,
118+
)
119+
}
120+
96121
override fun getDraftCount(userId: String): GetDraftLetterCountResponse {
97122
val response = getDraftLetterUsecase.count(GetDraftLetterUsecase.Query.All(userId))
98123
return GetDraftLetterCountResponse(response.count)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.asap.bootstrap.web.letter.dto
2+
3+
import java.time.LocalDateTime
4+
5+
data class GetAllPhysicalDraftLetterResponse(
6+
val drafts: List<PhysicalDraftLetterInfo>,
7+
) {
8+
}
9+
10+
data class PhysicalDraftLetterInfo(
11+
val draftKey: String,
12+
val senderName: String,
13+
val content: String,
14+
val lastUpdated: LocalDateTime,
15+
) {
16+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.asap.bootstrap.web.letter.dto
2+
3+
data class GetPhysicalDraftLetterResponse(
4+
val draftKey: String,
5+
val senderName: String,
6+
val content: String,
7+
val images: List<String>,
8+
) {
9+
}

Bootstrap-Module/src/test/kotlin/com/asap/bootstrap/acceptance/letter/LetterAcceptanceSupporter.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,7 @@ abstract class LetterAcceptanceSupporter : AcceptanceSupporter() {
5252

5353
@MockBean
5454
lateinit var getSendLetterUsecase: GetSendLetterUsecase
55+
56+
@MockBean
57+
lateinit var getPhysicalDraftLetterUsecase: GetPhysicalDraftLetterUsecase
5558
}

0 commit comments

Comments
 (0)