Skip to content
This repository was archived by the owner on Jul 7, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.asap.application.letter.port.`in`

interface GenerateDraftKeyUsecase {
fun command(command: Command): Response
fun command(command: Command.Send): Response

data class Command(
val userId: String,
)
fun command(command: Command.Physical): Response

sealed class Command {
data class Send(
val userId: String,
): Command()

data class Physical(
val userId: String,
): Command()
}

data class Response(
val draftId: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.asap.application.letter.port.out

import com.asap.domain.letter.entity.ReceiveDraftLetter

interface ReceiveDraftLetterManagementPort {
fun save(receiveDraftLetter: ReceiveDraftLetter): ReceiveDraftLetter
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,33 @@ import com.asap.application.letter.port.`in`.GenerateDraftKeyUsecase
import com.asap.application.letter.port.`in`.RemoveDraftLetterUsecase
import com.asap.application.letter.port.`in`.UpdateDraftLetterUsecase
import com.asap.application.letter.port.out.DraftLetterManagementPort
import com.asap.application.letter.port.out.ReceiveDraftLetterManagementPort
import com.asap.domain.common.DomainId
import com.asap.domain.letter.entity.DraftLetter
import com.asap.domain.letter.entity.ReceiveDraftLetter
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class DraftLetterCommandService(
private val draftLetterManagementPort: DraftLetterManagementPort,
private val receiveDraftLetterManagementPort: ReceiveDraftLetterManagementPort,
) : GenerateDraftKeyUsecase,
UpdateDraftLetterUsecase,
RemoveDraftLetterUsecase {
override fun command(command: GenerateDraftKeyUsecase.Command): GenerateDraftKeyUsecase.Response {
override fun command(command: GenerateDraftKeyUsecase.Command.Send): GenerateDraftKeyUsecase.Response {
val draftLetter = DraftLetter.default(DomainId(command.userId))
draftLetterManagementPort.save(draftLetter)
return GenerateDraftKeyUsecase.Response(draftLetter.id.value)
}

override fun command(command: GenerateDraftKeyUsecase.Command.Physical): GenerateDraftKeyUsecase.Response {
val receiveDraftLetter = ReceiveDraftLetter.default(DomainId(command.userId))
receiveDraftLetterManagementPort.save(receiveDraftLetter)
return GenerateDraftKeyUsecase.Response(receiveDraftLetter.id.value)
}

override fun command(command: UpdateDraftLetterUsecase.Command) {
val draftLetter =
draftLetterManagementPort.getDraftLetterNotNull(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import com.asap.application.letter.port.`in`.GenerateDraftKeyUsecase
import com.asap.application.letter.port.`in`.RemoveDraftLetterUsecase
import com.asap.application.letter.port.`in`.UpdateDraftLetterUsecase
import com.asap.application.letter.port.out.DraftLetterManagementPort
import com.asap.application.letter.port.out.ReceiveDraftLetterManagementPort
import com.asap.domain.common.DomainId
import com.asap.domain.letter.entity.DraftLetter
import com.asap.domain.letter.entity.ReceiveDraftLetter
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.nulls.shouldNotBeNull
import io.mockk.every
Expand All @@ -16,14 +18,16 @@ class DraftLetterCommandServiceTest :
BehaviorSpec({

val mockGenerateDraftKeyUsecase = mockk<DraftLetterManagementPort>(relaxed = true)
val draftLetterCommandService = DraftLetterCommandService(mockGenerateDraftKeyUsecase)
val mockReceiveDraftLetterManagementPort = mockk<ReceiveDraftLetterManagementPort>(relaxed = true)
val draftLetterCommandService =
DraftLetterCommandService(mockGenerateDraftKeyUsecase, mockReceiveDraftLetterManagementPort)

given("임시 저장 키를 발급할 때") {
val userId = "userId"
val draftLetter = DraftLetter.default(DomainId(userId))
every { mockGenerateDraftKeyUsecase.save(any()) } returns draftLetter
`when`("사용자 아이디를 입력하면") {
val response = draftLetterCommandService.command(GenerateDraftKeyUsecase.Command(userId))
val response = draftLetterCommandService.command(GenerateDraftKeyUsecase.Command.Send(userId))
then("임시 저장 키를 발급한다") {
response.draftId.shouldNotBeNull()
}
Expand Down Expand Up @@ -70,4 +74,16 @@ class DraftLetterCommandServiceTest :
}
}
}

given("받은 편지를 임시저장하려 할때"){
val command = GenerateDraftKeyUsecase.Command.Physical("userId")
val receiveDraftLetter = ReceiveDraftLetter.default(DomainId(command.userId))
every { mockReceiveDraftLetterManagementPort.save(any()) } returns receiveDraftLetter
`when`("사용자 아이디를 입력하면"){
val response = draftLetterCommandService.command(command)
then("받은 편지를 임시저장한다"){
response.draftId.shouldNotBeNull()
}
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ interface DraftLetterApi {
@AccessUser userId: String,
): GenerateDraftKeyResponse

@Operation(summary = "실물 편지 임시 저장 키 발급")
@PostMapping("/physical/key")
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "임시 저장 키 발급 성공",
content = [
Content(
schema =
Schema(implementation = GenerateDraftKeyResponse::class),
),
],
),
],
)
fun getPhysicalDraftKey(
@AccessUser userId: String,
): GenerateDraftKeyResponse

@Operation(summary = "임시 저장하기")
@PostMapping("/{draftId}")
@ApiResponses(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ class DraftLetterController(
private val removeDraftLetterUsecase: RemoveDraftLetterUsecase,
) : DraftLetterApi {
override fun getDraftKey(userId: String): GenerateDraftKeyResponse {
val response = generateDraftKeyUsecase.command(GenerateDraftKeyUsecase.Command(userId))
val response = generateDraftKeyUsecase.command(GenerateDraftKeyUsecase.Command.Send(userId))
return GenerateDraftKeyResponse(response.draftId)
}

override fun getPhysicalDraftKey(userId: String): GenerateDraftKeyResponse {
val response = generateDraftKeyUsecase.command(GenerateDraftKeyUsecase.Command.Physical(userId))
return GenerateDraftKeyResponse(response.draftId)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class DraftLetterControllerTest : LetterAcceptanceSupporter() {
val accessToken = jwtMockManager.generateAccessToken(userId)

BDDMockito
.given(generateDraftKeyUsecase.command(GenerateDraftKeyUsecase.Command(userId)))
.given(generateDraftKeyUsecase.command(GenerateDraftKeyUsecase.Command.Send(userId)))
.willReturn(GenerateDraftKeyUsecase.Response("draftId"))

// when
Expand Down Expand Up @@ -180,4 +180,28 @@ class DraftLetterControllerTest : LetterAcceptanceSupporter() {
status { isOk() }
}
}


@Test
fun `get physical draft key`() {
// given
val userId = userMockManager.settingUser()
val accessToken = jwtMockManager.generateAccessToken(userId)

BDDMockito
.given(generateDraftKeyUsecase.command(GenerateDraftKeyUsecase.Command.Physical(userId)))
.willReturn(GenerateDraftKeyUsecase.Response("draftId"))

// when
val response =
mockMvc.post("/api/v1/letters/drafts/physical/key") {
header("Authorization", "Bearer $accessToken")
}

// then
response.andExpect {
status { isOk() }
jsonPath("$.draftId") { isString() }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,22 @@ class DraftLetterApiIntegrationTest : IntegrationSupporter() {
status { isOk() }
}
}

@Test
fun `get physical draft key`() {
// given
val userId = userMockManager.settingUser()
val accessToken = jwtMockManager.generateAccessToken(userId)
// when
val response =
mockMvc.post("/api/v1/letters/drafts/physical/key") {
header("Authorization", "Bearer $accessToken")
}

// then
response.andExpect {
status { isOk() }
jsonPath("$.draftId") { isString() }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.asap.domain.letter.entity

import com.asap.domain.common.BaseEntity
import com.asap.domain.common.DomainId
import java.time.LocalDateTime

class ReceiveDraftLetter(
id: DomainId,
var content: String,
var senderName: String,
val ownerId: DomainId,
var images: List<String>,
var lastUpdated: LocalDateTime = LocalDateTime.now(),
val type: ReceiveDraftLetterType,
) : BaseEntity() {
companion object {
fun default(ownerId: DomainId) =
ReceiveDraftLetter(
id = DomainId.generate(),
ownerId = ownerId,
content = "",
senderName = "",
images = emptyList(),
type = ReceiveDraftLetterType.PHYSICAL,
)
}

fun update(
content: String,
senderName: String,
images: List<String>,
) {
this.content = content
this.senderName = senderName
this.images = images
this.lastUpdated = LocalDateTime.now()
}
}

enum class ReceiveDraftLetterType{
PHYSICAL,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.asap.persistence.jpa.letter

import com.asap.domain.common.DomainId
import com.asap.domain.letter.entity.ReceiveDraftLetter
import com.asap.persistence.jpa.letter.entity.ReceiveDraftLetterEntity

object ReceiveDraftLetterMapper {
fun toEntity(receiveDraftLetter: ReceiveDraftLetter): ReceiveDraftLetterEntity =
ReceiveDraftLetterEntity(
id = receiveDraftLetter.id.value,
content = receiveDraftLetter.content,
senderName = receiveDraftLetter.senderName,
ownerId = receiveDraftLetter.ownerId.value,
images = receiveDraftLetter.images,
updatedAt = receiveDraftLetter.lastUpdated,
type = receiveDraftLetter.type,
)

fun toDomain(receiveDraftLetterEntity: ReceiveDraftLetterEntity): ReceiveDraftLetter =
ReceiveDraftLetter(
id = DomainId(receiveDraftLetterEntity.id),
content = receiveDraftLetterEntity.content,
senderName = receiveDraftLetterEntity.senderName,
ownerId = DomainId(receiveDraftLetterEntity.ownerId),
images = receiveDraftLetterEntity.images,
lastUpdated = receiveDraftLetterEntity.updatedAt,
type = receiveDraftLetterEntity.type,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.asap.persistence.jpa.letter.adapter

import com.asap.application.letter.port.out.ReceiveDraftLetterManagementPort
import com.asap.domain.letter.entity.ReceiveDraftLetter
import com.asap.persistence.jpa.letter.ReceiveDraftLetterMapper
import com.asap.persistence.jpa.letter.repository.ReceiveDraftLetterJpaRepository
import org.springframework.stereotype.Repository

@Repository
class ReceiveDraftLetterManagementJpaAdapter(
private val receiveDraftLetterJpaRepository: ReceiveDraftLetterJpaRepository,
) : ReceiveDraftLetterManagementPort {
override fun save(receiveDraftLetter: ReceiveDraftLetter): ReceiveDraftLetter {
val receiveDraftLetterEntity = ReceiveDraftLetterMapper.toEntity(receiveDraftLetter)
return receiveDraftLetterJpaRepository.save(receiveDraftLetterEntity)
.let { ReceiveDraftLetterMapper.toDomain(it) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.asap.persistence.jpa.letter.entity

import com.asap.domain.letter.entity.ReceiveDraftLetterType
import com.asap.persistence.jpa.common.BaseEntity
import com.asap.persistence.jpa.user.entity.UserEntity
import jakarta.persistence.*
import org.hibernate.annotations.JdbcTypeCode
import org.hibernate.type.SqlTypes
import java.time.LocalDateTime

@Entity
@Table(
name = "receive_draft_letters"
)
class ReceiveDraftLetterEntity(
id: String,
content: String,
senderName: String,
ownerId: String,
images: List<String>,
updatedAt: LocalDateTime,
type: ReceiveDraftLetterType
) : BaseEntity(id) {

var content: String = content
var senderName: String = senderName

@Column(
name = "owner_id",
nullable = false,
)
var ownerId: String = ownerId

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "owner_id",
insertable = false,
updatable = false,
)
lateinit var owner: UserEntity

@JdbcTypeCode(SqlTypes.JSON)
@Column(
name = "images",
nullable = false,
columnDefinition = "text",
)
var images: List<String> = images

override var updatedAt: LocalDateTime = updatedAt

@Enumerated(EnumType.STRING)
@Column(
name = "type",
nullable = false,
columnDefinition = "VARCHAR(20)",
)
var type: ReceiveDraftLetterType = type

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.asap.persistence.jpa.letter.repository

import com.asap.persistence.jpa.letter.entity.ReceiveDraftLetterEntity
import org.springframework.data.jpa.repository.JpaRepository

interface ReceiveDraftLetterJpaRepository : JpaRepository<ReceiveDraftLetterEntity, String>{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE receive_draft_letters
(
id VARCHAR(255) NOT NULL,
created_at datetime NULL,
updated_at datetime NULL,
content VARCHAR(255) NULL,
sender_name VARCHAR(255) NULL,
owner_id VARCHAR(255) NOT NULL,
images TEXT NOT NULL,
type VARCHAR(20) NOT NULL,
CONSTRAINT pk_receive_draft_letters PRIMARY KEY (id)
);

ALTER TABLE receive_draft_letters
ADD CONSTRAINT FK_RECEIVE_DRAFT_LETTERS_ON_OWNER FOREIGN KEY (owner_id) REFERENCES user (id);
Loading