From a06459858859d8a5ca0092c79a05b70208ada2f6 Mon Sep 17 00:00:00 2001 From: Jeongwan Noh Date: Tue, 30 Dec 2025 12:51:17 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=A1=B0=ED=9A=8C=20API=20=EB=B0=8F=20DTO?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ApplicationController.kt | 18 ++++++++++- .../dto/ApplicationStatusResponse.kt | 32 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt diff --git a/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt b/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt index 24ecde5..cc3b4cb 100644 --- a/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt +++ b/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt @@ -10,6 +10,7 @@ import land.leets.domain.application.domain.Application import land.leets.domain.application.presentation.dto.ApplicationDetailsResponse import land.leets.domain.application.presentation.dto.ApplicationRequest import land.leets.domain.application.presentation.dto.ApplicationResponse +import land.leets.domain.application.presentation.dto.ApplicationStatusResponse import land.leets.domain.application.presentation.dto.StatusRequest import land.leets.domain.application.usecase.* import land.leets.domain.auth.AuthDetails @@ -25,7 +26,8 @@ class ApplicationController( private val updateApplication: UpdateApplication, private val getApplication: GetAllApplication, private val getApplicationDetails: GetApplicationDetails, - private val updateResult: UpdateResult + private val updateResult: UpdateResult, + private val getApplicationStatus: GetApplicationStatus, ) { @Operation(summary = "(유저) 지원서 작성", description = "지원서를 작성합니다.") @@ -116,4 +118,18 @@ class ApplicationController( val uid = authDetails.uid return getApplicationDetails.execute(uid) } + + @Operation(summary = "(유저) 지원서 상태 불러오기", description = "작성한 지원서 상태를 불러옵니다.") + @ApiResponses( + ApiResponse(responseCode = "200"), + ApiResponse(responseCode = "400", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "403", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "404", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "500", content = [Content(schema = Schema(implementation = ErrorResponse::class))]) + ) + @GetMapping("/status") + fun getStatus(@AuthenticationPrincipal authDetails: AuthDetails): ApplicationStatusResponse { + val uid = authDetails.uid + return getApplicationStatus.execute(uid) + } } diff --git a/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt b/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt new file mode 100644 index 0000000..ca28c47 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt @@ -0,0 +1,32 @@ +package land.leets.domain.application.presentation.dto + +import land.leets.domain.application.domain.Application +import land.leets.domain.application.type.ApplicationStatus + +data class ApplicationStatusResponse( + val id: Long, + val status: ApplicationStatus, + val interviewDay: String?, + val interviewTime: String?, +) { + companion object { + fun from( + application: Application, + ): ApplicationStatusResponse { + if (application.applicationStatus != ApplicationStatus.PASS_PAPER) { + return ApplicationStatusResponse( + id = application.id!!, + status = application.applicationStatus, + interviewDay = null, + interviewTime = null, + ) + } + return ApplicationStatusResponse( + id = application.id!!, + status = application.applicationStatus, + interviewDay = application.interviewDay, + interviewTime = application.interviewTime, + ) + } + } +} From 58ab9e48037a7ddd951e42ed33b2065b643e471e Mon Sep 17 00:00:00 2001 From: Jeongwan Noh Date: Tue, 30 Dec 2025 12:51:27 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/GetApplicationStatus.kt | 8 +++++++ .../usecase/GetApplicationStatusImpl.kt | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt create mode 100644 src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt diff --git a/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt new file mode 100644 index 0000000..2298bcb --- /dev/null +++ b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt @@ -0,0 +1,8 @@ +package land.leets.domain.application.usecase + +import land.leets.domain.application.presentation.dto.ApplicationStatusResponse +import java.util.UUID + +interface GetApplicationStatus { + fun execute(uid: UUID): ApplicationStatusResponse +} \ No newline at end of file diff --git a/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt new file mode 100644 index 0000000..f567d1f --- /dev/null +++ b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt @@ -0,0 +1,21 @@ +package land.leets.domain.application.usecase + +import land.leets.domain.application.domain.repository.ApplicationRepository +import land.leets.domain.application.exception.ApplicationNotFoundException +import land.leets.domain.application.presentation.dto.ApplicationStatusResponse +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.util.UUID + +@Service +@Transactional(readOnly = true) +class GetApplicationStatusImpl( + private val applicationRepository: ApplicationRepository, +) : GetApplicationStatus { + override fun execute(uid: UUID): ApplicationStatusResponse { + val application = applicationRepository.findByUser_Id(uid) + ?: throw ApplicationNotFoundException() + + return ApplicationStatusResponse.from(application) + } +} \ No newline at end of file From abb344a52b8b53556e8ca9129303e8f9d556abff Mon Sep 17 00:00:00 2001 From: Jeongwan Noh Date: Tue, 30 Dec 2025 12:51:48 +0900 Subject: [PATCH 3/4] =?UTF-8?q?test:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/GetApplicationStatusImplTest.kt | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt diff --git a/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt b/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt new file mode 100644 index 0000000..7b676d3 --- /dev/null +++ b/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt @@ -0,0 +1,82 @@ +package land.leets.domain.application.usecase + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import land.leets.domain.application.domain.Application +import land.leets.domain.application.domain.repository.ApplicationRepository +import land.leets.domain.application.exception.ApplicationNotFoundException +import land.leets.domain.application.type.ApplicationStatus +import java.util.UUID + +class GetApplicationStatusImplTest : DescribeSpec({ + + val applicationRepository = mockk() + val getApplicationStatus = GetApplicationStatusImpl(applicationRepository) + + val uid = UUID.randomUUID() + + val interviewDay = "2025년 3월 3일 (토)" + val interviewTime = "15:00" + + fun mockApplication( + id: Long = 1L, + status: ApplicationStatus, + day: String = interviewDay, + time: String = interviewTime + ): Application = mockk().also { application -> + every { application.id } returns id + every { application.applicationStatus } returns status + every { application.interviewDay } returns day + every { application.interviewTime } returns time + } + + describe("GetApplicationStatusImpl 유스케이스는") { + + context("지원서 상태 조회를 요청할 때") { + + it("지원서 상태가 PASS_PAPER이면 인터뷰 정보를 포함하여 반환한다") { + val application = mockApplication(status = ApplicationStatus.PASS_PAPER) + every { applicationRepository.findByUser_Id(uid) } returns application + + val result = getApplicationStatus.execute(uid) + + result.id shouldBe 1L + result.status shouldBe ApplicationStatus.PASS_PAPER + result.interviewDay shouldBe interviewDay + result.interviewTime shouldBe interviewTime + } + + it("PASS_PAPER이 아닌 상태들은 인터뷰 정보가 null이어야 한다") { + val nonInterviewStatuses = listOf( + ApplicationStatus.PENDING, + ApplicationStatus.FAIL_PAPER, + ApplicationStatus.PASS, + ApplicationStatus.FAIL, + ) + + nonInterviewStatuses.forEach { status -> + val application = mockApplication(status = status) + every { applicationRepository.findByUser_Id(uid) } returns application + + val result = getApplicationStatus.execute(uid) + + result.id shouldBe 1L + result.status shouldBe status + result.interviewDay shouldBe null + result.interviewTime shouldBe null + } + } + + it("지원서가 존재하지 않으면 ApplicationNotFoundException을 던진다") { + every { applicationRepository.findByUser_Id(uid) } returns null + + shouldThrow { + getApplicationStatus.execute(uid) + } + } + } + } +}) From 2d48f46f516edc9824625eb01c5398b69ebb441e Mon Sep 17 00:00:00 2001 From: Jeongwan Noh Date: Wed, 31 Dec 2025 18:11:35 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=EB=B9=84=EC=A6=88=EB=8B=88=EC=8A=A4?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=EB=B7=B0=20=EC=9D=BC=EC=A0=95=20=EB=B0=8F=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=A0=84=EB=8B=AC=20=EB=B0=A9=EC=8B=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ApplicationController.kt | 1 - .../dto/ApplicationStatusResponse.kt | 21 ++++++---- .../usecase/GetApplicationStatus.kt | 2 +- .../usecase/GetApplicationStatusImpl.kt | 7 +++- .../usecase/GetApplicationStatusImplTest.kt | 41 +++++++++++++------ 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt b/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt index cc3b4cb..d7d8c71 100644 --- a/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt +++ b/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt @@ -17,7 +17,6 @@ import land.leets.domain.auth.AuthDetails import land.leets.global.error.ErrorResponse import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* -import java.util.* @RestController @RequestMapping("/application") diff --git a/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt b/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt index ca28c47..ad3b426 100644 --- a/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt +++ b/src/main/kotlin/land/leets/domain/application/presentation/dto/ApplicationStatusResponse.kt @@ -2,30 +2,37 @@ package land.leets.domain.application.presentation.dto import land.leets.domain.application.domain.Application import land.leets.domain.application.type.ApplicationStatus +import land.leets.domain.interview.domain.Interview +import land.leets.domain.interview.type.HasInterview +import java.time.LocalDateTime data class ApplicationStatusResponse( val id: Long, val status: ApplicationStatus, - val interviewDay: String?, - val interviewTime: String?, + val hasInterview: HasInterview?, + val interviewDate: LocalDateTime?, + val interviewPlace: String?, ) { companion object { - fun from( + fun of( application: Application, + interview: Interview?, ): ApplicationStatusResponse { if (application.applicationStatus != ApplicationStatus.PASS_PAPER) { return ApplicationStatusResponse( id = application.id!!, status = application.applicationStatus, - interviewDay = null, - interviewTime = null, + hasInterview = null, + interviewDate = null, + interviewPlace = null, ) } return ApplicationStatusResponse( id = application.id!!, status = application.applicationStatus, - interviewDay = application.interviewDay, - interviewTime = application.interviewTime, + hasInterview = interview!!.hasInterview, + interviewDate = interview.fixedInterviewDate, + interviewPlace = interview.place, ) } } diff --git a/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt index 2298bcb..488d57c 100644 --- a/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt +++ b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatus.kt @@ -5,4 +5,4 @@ import java.util.UUID interface GetApplicationStatus { fun execute(uid: UUID): ApplicationStatusResponse -} \ No newline at end of file +} diff --git a/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt index f567d1f..b4788be 100644 --- a/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt +++ b/src/main/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImpl.kt @@ -3,6 +3,7 @@ package land.leets.domain.application.usecase import land.leets.domain.application.domain.repository.ApplicationRepository import land.leets.domain.application.exception.ApplicationNotFoundException import land.leets.domain.application.presentation.dto.ApplicationStatusResponse +import land.leets.domain.interview.domain.repository.InterviewRepository import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.util.UUID @@ -11,11 +12,13 @@ import java.util.UUID @Transactional(readOnly = true) class GetApplicationStatusImpl( private val applicationRepository: ApplicationRepository, + private val interviewRepository: InterviewRepository, ) : GetApplicationStatus { override fun execute(uid: UUID): ApplicationStatusResponse { val application = applicationRepository.findByUser_Id(uid) ?: throw ApplicationNotFoundException() + val interview = interviewRepository.findByApplication(application) - return ApplicationStatusResponse.from(application) + return ApplicationStatusResponse.of(application, interview) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt b/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt index 7b676d3..7afbe7f 100644 --- a/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt +++ b/src/test/kotlin/land/leets/domain/application/usecase/GetApplicationStatusImplTest.kt @@ -9,28 +9,37 @@ import land.leets.domain.application.domain.Application import land.leets.domain.application.domain.repository.ApplicationRepository import land.leets.domain.application.exception.ApplicationNotFoundException import land.leets.domain.application.type.ApplicationStatus +import land.leets.domain.interview.domain.Interview +import land.leets.domain.interview.domain.repository.InterviewRepository +import land.leets.domain.interview.type.HasInterview +import java.time.LocalDateTime import java.util.UUID class GetApplicationStatusImplTest : DescribeSpec({ val applicationRepository = mockk() - val getApplicationStatus = GetApplicationStatusImpl(applicationRepository) + val interviewRepository = mockk() + val getApplicationStatus = GetApplicationStatusImpl(applicationRepository, interviewRepository) val uid = UUID.randomUUID() - val interviewDay = "2025년 3월 3일 (토)" - val interviewTime = "15:00" + val interviewDate: LocalDateTime = LocalDateTime.of(2026, 3, 14, 14, 0) + val interviewPlace = "전자정보도서관 1층 스터디룸 A" + val applicationId = 1L fun mockApplication( - id: Long = 1L, status: ApplicationStatus, - day: String = interviewDay, - time: String = interviewTime ): Application = mockk().also { application -> - every { application.id } returns id + every { application.id } returns applicationId every { application.applicationStatus } returns status - every { application.interviewDay } returns day - every { application.interviewTime } returns time + } + + fun mockInterview( + hasInterview: HasInterview = HasInterview.PENDING, + ): Interview = mockk().also { interview -> + every { interview.hasInterview } returns hasInterview + every { interview.fixedInterviewDate } returns interviewDate + every { interview.place } returns interviewPlace } describe("GetApplicationStatusImpl 유스케이스는") { @@ -39,14 +48,17 @@ class GetApplicationStatusImplTest : DescribeSpec({ it("지원서 상태가 PASS_PAPER이면 인터뷰 정보를 포함하여 반환한다") { val application = mockApplication(status = ApplicationStatus.PASS_PAPER) + val interview = mockInterview() every { applicationRepository.findByUser_Id(uid) } returns application + every { interviewRepository.findByApplication(application) } returns interview val result = getApplicationStatus.execute(uid) result.id shouldBe 1L result.status shouldBe ApplicationStatus.PASS_PAPER - result.interviewDay shouldBe interviewDay - result.interviewTime shouldBe interviewTime + result.hasInterview shouldBe HasInterview.PENDING + result.interviewDate shouldBe interviewDate + result.interviewPlace shouldBe interviewPlace } it("PASS_PAPER이 아닌 상태들은 인터뷰 정보가 null이어야 한다") { @@ -59,14 +71,17 @@ class GetApplicationStatusImplTest : DescribeSpec({ nonInterviewStatuses.forEach { status -> val application = mockApplication(status = status) + val interview = mockInterview() every { applicationRepository.findByUser_Id(uid) } returns application + every { interviewRepository.findByApplication(application) } returns interview val result = getApplicationStatus.execute(uid) result.id shouldBe 1L result.status shouldBe status - result.interviewDay shouldBe null - result.interviewTime shouldBe null + result.hasInterview shouldBe null + result.interviewDate shouldBe null + result.interviewPlace shouldBe null } }