From 6f7b05ba35cbd9c0600fc9dbc6cc223738a4bac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:47:42 +0900 Subject: [PATCH 01/16] =?UTF-8?q?chore=20(=20#36=20)=20:=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/status/values/ApplicationStatus.kt | 34 +++++++++++---- .../grpc/client/status/StatusGrpcClient.kt | 2 +- .../grpc/dto/status/ApplicationStatus.kt | 42 ------------------- .../grpc/dto/status/InternalStatusResponse.kt | 2 + 4 files changed, 29 insertions(+), 51 deletions(-) delete mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/ApplicationStatus.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/values/ApplicationStatus.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/values/ApplicationStatus.kt index 1177929f..83b84c0f 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/values/ApplicationStatus.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/values/ApplicationStatus.kt @@ -1,24 +1,42 @@ package hs.kr.entrydsm.domain.status.values +/** + * 지원서 상태를 나타내는 열거형 클래스입니다. + * gRPC 프로토콜과 매핑되는 지원 상태를 정의합니다. + */ enum class ApplicationStatus { - /** 미지원 - 지원자가 원서를 작성하지 않은 상태 */ + /** + * 미지원 - 지원자가 원서를 작성하지 않은 상태 + */ NOT_APPLIED, - /** 원서 작성 중 - 원서가 저장되었으나 제출되지 않은 상태 */ + /** + * 원서 작성 중 - 원서가 저장되었으나 제출되지 않은 상태 + */ WRITING, - /** 지원 완료 - 온라인 원서 접수 완료 상태 */ + /** + * 지원 완료 - 온라인 원서 접수 완료 상태 + */ SUBMITTED, - /** 서류 도착 대기 - 원서 제출 후 학교에서 접수 확인 전 상태 */ + /** + * 서류 도착 대기 - 원서 제출 후 학교에서 접수 확인 전 상태 + */ WAITING_DOCUMENTS, - /** 서류 접수 완료 - 학교에서 원서 및 증빙 서류가 정상적으로 도착하여 검토 완료된 상태 */ + /** + * 서류 접수 완료 - 학교에서 원서 및 증빙 서류가 정상적으로 도착하여 검토 완료된 상태 + */ DOCUMENTS_RECEIVED, - /** 전형 진행 중 - 1차 또는 2차 전형이 진행 중인 상태 */ + /** + * 전형 진행 중 - 1차 또는 2차 전형이 진행 중인 상태 + */ SCREENING_IN_PROGRESS, - /** 합격 여부 확인 - 최종 합격자 발표 완료 상태 */ - RESULT_ANNOUNCED + /** + * 합격 여부 확인 - 최종 합격자 발표 완료 상태 + */ + RESULT_ANNOUNCED, } \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt index 473f3ebd..c4ba81e5 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt @@ -1,7 +1,7 @@ package hs.kr.entrydsm.application.global.grpc.client.status import com.google.protobuf.Empty -import hs.kr.entrydsm.application.global.grpc.dto.status.ApplicationStatus +import hs.kr.entrydsm.domain.status.values.ApplicationStatus import hs.kr.entrydsm.application.global.grpc.dto.status.InternalStatusListResponse import hs.kr.entrydsm.application.global.grpc.dto.status.InternalStatusResponse import hs.kr.entrydsm.casper.status.proto.StatusServiceGrpc diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/ApplicationStatus.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/ApplicationStatus.kt deleted file mode 100644 index 195763ba..00000000 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/ApplicationStatus.kt +++ /dev/null @@ -1,42 +0,0 @@ -package hs.kr.entrydsm.application.global.grpc.dto.status - -/** - * 지원서 상태를 나타내는 열거형 클래스입니다. - * gRPC 프로토콜과 매핑되는 지원 상태를 정의합니다. - */ -enum class ApplicationStatus { - /** - * 미지원 - 지원자가 원서를 작성하지 않은 상태 - */ - NOT_APPLIED, - - /** - * 원서 작성 중 - 원서가 저장되었으나 제출되지 않은 상태 - */ - WRITING, - - /** - * 지원 완료 - 온라인 원서 접수 완료 상태 - */ - SUBMITTED, - - /** - * 서류 도착 대기 - 원서 제출 후 학교에서 접수 확인 전 상태 - */ - WAITING_DOCUMENTS, - - /** - * 서류 접수 완료 - 학교에서 원서 및 증빙 서류가 정상적으로 도착하여 검토 완료된 상태 - */ - DOCUMENTS_RECEIVED, - - /** - * 전형 진행 중 - 1차 또는 2차 전형이 진행 중인 상태 - */ - SCREENING_IN_PROGRESS, - - /** - * 합격 여부 확인 - 최종 합격자 발표 완료 상태 - */ - RESULT_ANNOUNCED, -} diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/InternalStatusResponse.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/InternalStatusResponse.kt index 8f2977c5..aeaceb33 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/InternalStatusResponse.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/status/InternalStatusResponse.kt @@ -1,5 +1,7 @@ package hs.kr.entrydsm.application.global.grpc.dto.status +import hs.kr.entrydsm.domain.status.values.ApplicationStatus + /** * 내부 gRPC 통신용 상태 응답 DTO 클래스입니다. * 상태 서비스와의 통신에서 상태 정보를 전달받는 데 사용됩니다. From 6cf47407c39e35ee62841db3194d56aa17d2a8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:48:09 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20contract=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interfaces/ApplicationCommandStatusContract.kt | 5 +++++ .../status/interfaces/ApplicationQueryStatusContract.kt | 9 +++++++++ .../entrydsm/domain/status/interfaces/StatusContract.kt | 3 +-- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationCommandStatusContract.kt create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationQueryStatusContract.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationCommandStatusContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationCommandStatusContract.kt new file mode 100644 index 00000000..66a5ad9e --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationCommandStatusContract.kt @@ -0,0 +1,5 @@ +package hs.kr.entrydsm.domain.status.interfaces + +interface ApplicationCommandStatusContract { + fun updateExamCode(receiptCode: Long, examCode: String) +} \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationQueryStatusContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationQueryStatusContract.kt new file mode 100644 index 00000000..078fd1df --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/ApplicationQueryStatusContract.kt @@ -0,0 +1,9 @@ +package hs.kr.entrydsm.domain.status.interfaces + +import hs.kr.entrydsm.domain.status.aggregates.Status +import hs.kr.entrydsm.domain.status.aggregates.StatusCache + +interface ApplicationQueryStatusContract { + fun queryStatusByReceiptCode(receiptCode: Long): Status? + fun queryStatusByReceiptCodeInCache(receiptCode: Long): StatusCache? +} \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/StatusContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/StatusContract.kt index 63a6c47c..2998192b 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/StatusContract.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/interfaces/StatusContract.kt @@ -1,4 +1,3 @@ package hs.kr.entrydsm.domain.status.interfaces -interface StatusContract : - SaveExamCodeContract \ No newline at end of file +interface StatusContract : ApplicationQueryStatusContract, ApplicationCommandStatusContract \ No newline at end of file From 56f7716a6a2819c0c875dd4c2958f3b71041612d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:49:56 +0900 Subject: [PATCH 03/16] =?UTF-8?q?refactor=20(=20#36=20)=20:=20import=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/examcode/usecase/GrantExamCodesUseCase.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/examcode/usecase/GrantExamCodesUseCase.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/examcode/usecase/GrantExamCodesUseCase.kt index 50b35033..c2492693 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/examcode/usecase/GrantExamCodesUseCase.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/examcode/usecase/GrantExamCodesUseCase.kt @@ -3,7 +3,6 @@ package hs.kr.entrydsm.application.domain.examcode.usecase import hs.kr.entrydsm.domain.application.interfaces.ApplicationContract import hs.kr.entrydsm.domain.examcode.interfaces.GrantExamCodesContract import hs.kr.entrydsm.domain.examcode.values.ExamCodeInfo -import hs.kr.entrydsm.domain.status.interfaces.StatusContract import hs.kr.entrydsm.application.global.annotation.usecase.UseCase import hs.kr.entrydsm.domain.application.aggregates.Application import hs.kr.entrydsm.domain.application.values.ApplicationType @@ -12,13 +11,14 @@ import hs.kr.entrydsm.domain.examcode.interfaces.BaseLocationContract import hs.kr.entrydsm.domain.examcode.interfaces.KakaoGeocodeContract import hs.kr.entrydsm.domain.examcode.values.DistanceGroup import hs.kr.entrydsm.application.domain.examcode.util.DistanceUtil +import hs.kr.entrydsm.domain.status.interfaces.SaveExamCodeContract import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope @UseCase class GrantExamCodesUseCase( private val applicationContract: ApplicationContract, - private val statusContract: StatusContract, + private val saveExamCodeContract: SaveExamCodeContract, private val kakaoGeocodeContract: KakaoGeocodeContract, private val distanceUtil: DistanceUtil, private val baseLocationContract: BaseLocationContract, @@ -100,7 +100,7 @@ class GrantExamCodesUseCase( private suspend fun saveExamCodes(examCodeInfos: List) { examCodeInfos.forEach { info -> info.examCode?.let { examCode -> - statusContract.updateExamCode(info.receiptCode, examCode) + saveExamCodeContract.updateExamCode(info.receiptCode, examCode) } } } From 8342fca9bc1472e0a94a5aee1772bbaed1dda986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:50:18 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20status=20model?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/entrydsm/domain/status/aggregates/Status.kt | 4 ++-- .../entrydsm/domain/status/aggregates/StatusCache.kt | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/StatusCache.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/Status.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/Status.kt index f2d6498a..4b402407 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/Status.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/Status.kt @@ -1,13 +1,13 @@ package hs.kr.entrydsm.domain.status.aggregates +import hs.kr.entrydsm.domain.status.values.ApplicationStatus import hs.kr.entrydsm.global.annotation.aggregates.Aggregate @Aggregate(context = "status") data class Status( val id: Long? = 0, - val isPrintsArrived: Boolean = false, - val isSubmitted: Boolean = false, val examCode: String? = null, + val applicationStatus: ApplicationStatus, val isFirstRoundPass: Boolean = false, val isSecondRoundPass: Boolean = false, val receiptCode: Long, diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/StatusCache.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/StatusCache.kt new file mode 100644 index 00000000..09a8c22e --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/status/aggregates/StatusCache.kt @@ -0,0 +1,12 @@ +package hs.kr.entrydsm.domain.status.aggregates + +import hs.kr.entrydsm.domain.status.values.ApplicationStatus + +data class StatusCache( + val receiptCode: Long, + val examCode: String?, + val applicationStatus: ApplicationStatus, + val isFirstRoundPass: Boolean, + val isSecondRoundPass: Boolean, + val ttl: Long +) \ No newline at end of file From b10dea715f017e033cfa73f027a0782e3c178fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:51:12 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20RedisEntity=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../status/entity/StatusCacheRedisEntity.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt new file mode 100644 index 00000000..d3ccbbd9 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt @@ -0,0 +1,17 @@ +package hs.kr.entrydsm.application.domain.status.entity + +import org.springframework.data.annotation.Id +import org.springframework.data.redis.core.RedisHash +import org.springframework.data.redis.core.TimeToLive + +@RedisHash("status_cache") +class StatusCacheRedisEntity( + @Id + val receiptCode: Long, + val examCode: String?, + val applicationStatus: ApplicationStatus, + val isFirstRoundPass: Boolean, + val isSecondRoundPass: Boolean, + @TimeToLive + val ttl: Long +) \ No newline at end of file From 112e3e42c865c9d068edccfd4d56d72d6b73d017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:51:34 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20StatusPersistenc?= =?UTF-8?q?eAdapter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/status/StatusPersistenceAdapter.kt | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt index 9e269735..5bfc5264 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt @@ -1,17 +1,47 @@ package hs.kr.entrydsm.application.domain.status +import hs.kr.entrydsm.application.domain.status.repository.StatusCacheRepository import hs.kr.entrydsm.application.global.grpc.client.status.StatusGrpcClient +import hs.kr.entrydsm.domain.status.aggregates.Status +import hs.kr.entrydsm.domain.status.aggregates.StatusCache import hs.kr.entrydsm.domain.status.interfaces.StatusContract +import kotlinx.coroutines.runBlocking import org.springframework.stereotype.Component @Component class StatusPersistenceAdapter( private val statusGrpcClient: StatusGrpcClient, + private val statusCacheRepository: StatusCacheRepository ) : StatusContract { - override suspend fun updateExamCode( - receiptCode: Long, - examCode: String, - ) { + + override fun queryStatusByReceiptCode(receiptCode: Long): Status? = runBlocking { + statusGrpcClient.getStatusByReceiptCode(receiptCode).let { + Status( + id = it.id, + examCode = it.examCode, + applicationStatus = it.applicationStatus, + isFirstRoundPass = it.isFirstRoundPass, + isSecondRoundPass = it.isSecondRoundPass, + receiptCode = it.receiptCode + ) + } + } + + override fun queryStatusByReceiptCodeInCache(receiptCode: Long): StatusCache? { + return statusCacheRepository.findById(receiptCode) + .map { + StatusCache( + receiptCode = it.receiptCode, + applicationStatus = it.applicationStatus, + examCode = it.examCode, + isFirstRoundPass = it.isFirstRoundPass, + isSecondRoundPass = it.isSecondRoundPass, + ttl = it.ttl + ) + }.orElse(null) + } + + override fun updateExamCode(receiptCode: Long, examCode: String) = runBlocking { statusGrpcClient.updateExamCode(receiptCode, examCode) } } From f3a72d92ad2f743a50d46800934ebaf0698dbf15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 17:51:53 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20StatusCacheRepos?= =?UTF-8?q?itory=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/status/repository/StatusCacheRepository.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt new file mode 100644 index 00000000..9655e5e8 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt @@ -0,0 +1,7 @@ +package hs.kr.entrydsm.application.domain.status.repository + +import hs.kr.entrydsm.application.domain.status.entity.StatusCacheRedisEntity +import org.springframework.data.repository.CrudRepository + +interface StatusCacheRepository : CrudRepository{ +} \ No newline at end of file From 843881692be997313ba4d2520e15f90920f91e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 18:13:29 +0900 Subject: [PATCH 08/16] =?UTF-8?q?refactor=20(=20#36=20)=20:=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/hs/kr/entrydsm/domain/user/value}/UserRole.kt | 4 ++-- .../status/{ => domain}/StatusPersistenceAdapter.kt | 8 ++++---- .../status/{ => domain}/entity/StatusCacheRedisEntity.kt | 2 +- .../status/domain/repository/StatusCacheRepository.kt | 7 +++++++ .../domain/status/repository/StatusCacheRepository.kt | 7 ------- .../application/global/grpc/client/user/UserGrpcClient.kt | 2 +- .../global/grpc/dto/user/InternalUserResponse.kt | 1 + 7 files changed, 16 insertions(+), 15 deletions(-) rename {casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user => casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/value}/UserRole.kt (85%) rename casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/{ => domain}/StatusPersistenceAdapter.kt (88%) rename casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/{ => domain}/entity/StatusCacheRedisEntity.kt (87%) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/repository/StatusCacheRepository.kt delete mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/UserRole.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/value/UserRole.kt similarity index 85% rename from casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/UserRole.kt rename to casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/value/UserRole.kt index 112396d0..457a68bf 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/UserRole.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/value/UserRole.kt @@ -1,4 +1,4 @@ -package hs.kr.entrydsm.application.global.grpc.dto.user +package hs.kr.entrydsm.domain.user.value /** * 사용자 역할을 나타내는 열거형 클래스입니다. @@ -19,4 +19,4 @@ enum class UserRole { * 관리자 권한 */ ADMIN, -} +} \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/StatusPersistenceAdapter.kt similarity index 88% rename from casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt rename to casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/StatusPersistenceAdapter.kt index 5bfc5264..6e962ad1 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/StatusPersistenceAdapter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/StatusPersistenceAdapter.kt @@ -1,6 +1,6 @@ -package hs.kr.entrydsm.application.domain.status +package hs.kr.entrydsm.application.domain.status.domain -import hs.kr.entrydsm.application.domain.status.repository.StatusCacheRepository +import hs.kr.entrydsm.application.domain.status.domain.repository.StatusCacheRepository import hs.kr.entrydsm.application.global.grpc.client.status.StatusGrpcClient import hs.kr.entrydsm.domain.status.aggregates.Status import hs.kr.entrydsm.domain.status.aggregates.StatusCache @@ -15,7 +15,7 @@ class StatusPersistenceAdapter( ) : StatusContract { override fun queryStatusByReceiptCode(receiptCode: Long): Status? = runBlocking { - statusGrpcClient.getStatusByReceiptCode(receiptCode).let { + statusGrpcClient.getStatusByReceiptCode(receiptCode)?.let { Status( id = it.id, examCode = it.examCode, @@ -44,4 +44,4 @@ class StatusPersistenceAdapter( override fun updateExamCode(receiptCode: Long, examCode: String) = runBlocking { statusGrpcClient.updateExamCode(receiptCode, examCode) } -} +} \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt similarity index 87% rename from casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt rename to casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt index d3ccbbd9..f0180065 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/entity/StatusCacheRedisEntity.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt @@ -1,4 +1,4 @@ -package hs.kr.entrydsm.application.domain.status.entity +package hs.kr.entrydsm.application.domain.status.domain.entity import org.springframework.data.annotation.Id import org.springframework.data.redis.core.RedisHash diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/repository/StatusCacheRepository.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/repository/StatusCacheRepository.kt new file mode 100644 index 00000000..446301d8 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/repository/StatusCacheRepository.kt @@ -0,0 +1,7 @@ +package hs.kr.entrydsm.application.domain.status.domain.repository + +import hs.kr.entrydsm.application.domain.status.domain.entity.StatusCacheRedisEntity +import org.springframework.data.repository.CrudRepository + +interface StatusCacheRepository : CrudRepository{ +} \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt deleted file mode 100644 index 9655e5e8..00000000 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/repository/StatusCacheRepository.kt +++ /dev/null @@ -1,7 +0,0 @@ -package hs.kr.entrydsm.application.domain.status.repository - -import hs.kr.entrydsm.application.domain.status.entity.StatusCacheRedisEntity -import org.springframework.data.repository.CrudRepository - -interface StatusCacheRepository : CrudRepository{ -} \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/user/UserGrpcClient.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/user/UserGrpcClient.kt index 339476e1..6545a93d 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/user/UserGrpcClient.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/user/UserGrpcClient.kt @@ -1,7 +1,7 @@ package hs.kr.entrydsm.application.global.grpc.client.user import hs.kr.entrydsm.application.global.grpc.dto.user.InternalUserResponse -import hs.kr.entrydsm.application.global.grpc.dto.user.UserRole +import hs.kr.entrydsm.domain.user.value.UserRole import hs.kr.entrydsm.casper.user.proto.UserServiceGrpc import hs.kr.entrydsm.casper.user.proto.UserServiceProto import io.grpc.Channel diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/InternalUserResponse.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/InternalUserResponse.kt index bfa0b071..a36fc840 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/InternalUserResponse.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/dto/user/InternalUserResponse.kt @@ -1,5 +1,6 @@ package hs.kr.entrydsm.application.global.grpc.dto.user +import hs.kr.entrydsm.domain.user.value.UserRole import java.util.UUID /** From 1b73ec4961de4cb36005ba25a05c7eb0e1eb6622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 18:13:41 +0900 Subject: [PATCH 09/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20user=20aggregate?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hs/kr/entrydsm/domain/user/aggregates/User.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/aggregates/User.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/aggregates/User.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/aggregates/User.kt new file mode 100644 index 00000000..3b979853 --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/aggregates/User.kt @@ -0,0 +1,12 @@ +package hs.kr.entrydsm.domain.user.aggregates + +import hs.kr.entrydsm.global.annotation.aggregates.Aggregate +import java.util.UUID + +@Aggregate(context = "user") +data class User( + val id: UUID, + val phoneNumber: String, + val name: String, + val isParent: Boolean +) From 84e336c2c55c4b682e51cef58b29f32796a41a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 18:13:58 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20user=20contract?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/interfaces/ApplicationQueryUserContract.kt | 8 ++++++++ .../hs/kr/entrydsm/domain/user/interfaces/UserContract.kt | 4 ++++ 2 files changed, 12 insertions(+) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/ApplicationQueryUserContract.kt create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/UserContract.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/ApplicationQueryUserContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/ApplicationQueryUserContract.kt new file mode 100644 index 00000000..81b6bfd7 --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/ApplicationQueryUserContract.kt @@ -0,0 +1,8 @@ +package hs.kr.entrydsm.domain.user.interfaces + +import hs.kr.entrydsm.domain.user.aggregates.User +import java.util.UUID + +interface ApplicationQueryUserContract { + fun queryUserByUserId(userId: UUID): User +} \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/UserContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/UserContract.kt new file mode 100644 index 00000000..cdd4b742 --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/user/interfaces/UserContract.kt @@ -0,0 +1,4 @@ +package hs.kr.entrydsm.domain.user.interfaces + +interface UserContract : ApplicationQueryUserContract { +} \ No newline at end of file From e36be96ea88c5e437c5c3908bd6e93bbc30afa6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 18:14:11 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20user=20persisten?= =?UTF-8?q?ce=20adapter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/domain/UserPersistenceAdapter.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/user/domain/UserPersistenceAdapter.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/user/domain/UserPersistenceAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/user/domain/UserPersistenceAdapter.kt new file mode 100644 index 00000000..bc452caf --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/user/domain/UserPersistenceAdapter.kt @@ -0,0 +1,26 @@ +package hs.kr.entrydsm.application.domain.user.domain + +import hs.kr.entrydsm.application.global.grpc.client.user.UserGrpcClient +import hs.kr.entrydsm.domain.user.aggregates.User +import hs.kr.entrydsm.domain.user.interfaces.UserContract +import kotlinx.coroutines.runBlocking +import org.springframework.stereotype.Component +import java.util.UUID + +@Component +class UserPersistenceAdapter( + private val userGrpcClient: UserGrpcClient +) : UserContract { + + override fun queryUserByUserId(userId: UUID): User = runBlocking { + userGrpcClient.getUserInfoByUserId(userId).run { + User( + id = id, + phoneNumber = phoneNumber, + name = name, + isParent = isParent, + ) + } + + } +} \ No newline at end of file From e82a35bfa865e56cc9c68ffacab59ec123a1405f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 18:14:25 +0900 Subject: [PATCH 12/16] =?UTF-8?q?refactor=20(=20#36=20)=20:=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EC=97=90=20=EB=84=90=20=ED=97=88=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/global/grpc/client/status/StatusGrpcClient.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt index c4ba81e5..8c543d5f 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt @@ -79,7 +79,7 @@ class StatusGrpcClient { * @throws io.grpc.StatusRuntimeException gRPC 서버에서 오류가 발생한 경우 * @throws java.util.concurrent.CancellationException 코루틴이 취소된 경우 */ - suspend fun getStatusByReceiptCode(receiptCode: Long): InternalStatusResponse { + suspend fun getStatusByReceiptCode(receiptCode: Long): InternalStatusResponse? { val statusStub = StatusServiceGrpc.newStub(channel) val request = From 49bebafb350ba4abd0c97788246a4e557e907b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 20:56:26 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feat=20(=20#36=20)=20:=20pdf=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20application,user,status,school=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EA=B3=BC=20=EC=9D=BC=EB=B6=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pdf/data/IntroductionPdfConverter.kt | 42 +++-- .../document/pdf/data/PdfDataConverter.kt | 174 ++++++++++-------- .../pdf/generator/ApplicationPdfGenerator.kt | 20 +- .../pdf/generator/IntroductionPdfGenerator.kt | 42 ++++- .../pdf/presentation/PdfTestController.kt | 49 ++++- 5 files changed, 213 insertions(+), 114 deletions(-) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt index a00104cb..c1df111c 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt @@ -1,11 +1,15 @@ package hs.kr.entrydsm.application.global.document.pdf.data +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.school.interfaces.QuerySchoolContract import org.springframework.stereotype.Component import java.util.HashMap @Component -class IntroductionPdfConverter { - fun execute(application: Any): PdfData { +class IntroductionPdfConverter( + private val querySchoolContract: QuerySchoolContract +) { + fun execute(application: Application): PdfData { val values: MutableMap = HashMap() setIntroduction(application, values) setPersonalInfo(application, values) @@ -16,37 +20,38 @@ class IntroductionPdfConverter { } private fun setPersonalInfo( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: Application 도메인 모델 연동 필요 - values["userName"] = "더미사용자명" - values["address"] = "더미주소" - values["detailAddress"] = "더미상세주소" + values["userName"] = application.applicantName ?: "더미사용자명" + values["address"] = application.streetAddress ?: "더미주소" + values["detailAddress"] = application.detailAddress ?: "더미상세주소" } private fun setSchoolInfo( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: 교육상태 및 졸업정보 연동 필요 - // 현재는 더미 데이터로 설정 + // TODO: Application에 schoolCode 필드가 없어서 School 조회 불가 + // TODO: schoolCode 필드 추가되면 아래와 같이 사용 + // val school = querySchoolContract.querySchoolBySchoolCode(application.schoolCode) + // values["schoolName"] = school?.name ?: "더미중학교" + values["schoolName"] = "더미중학교" } private fun setReceiptCode( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: Application 도메인 모델 연동 필요 - values["receiptCode"] = "더미수험번호" + values["receiptCode"] = application.receiptCode.toString() } private fun setPhoneNumber( - application: Any, + application: Application, values: MutableMap, ) { - values["applicantTel"] = toFormattedPhoneNumber("01012345678") + values["applicantTel"] = toFormattedPhoneNumber(application.applicantTel ?: "01012345678") } private fun toFormattedPhoneNumber(phoneNumber: String): String { @@ -57,12 +62,13 @@ class IntroductionPdfConverter { } private fun setIntroduction( - application: Any, + application: Application, values: MutableMap, ) { - values["selfIntroduction"] = "더미 자기소개 내용" - values["studyPlan"] = "더미 학업계획 내용" + values["selfIntroduction"] = application.selfIntroduce ?: "더미 자기소개 내용" + values["studyPlan"] = application.studyPlan ?: "더미 학업계획 내용" values["newLineChar"] = "\n" + // TODO: Status 도메인에서 examCode 가져오기 필요 values["examCode"] = "더미수험번호" } } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt index 58c7949a..fcd3238d 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt @@ -1,15 +1,20 @@ package hs.kr.entrydsm.application.global.document.pdf.data +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.application.values.ApplicationType +import hs.kr.entrydsm.domain.school.interfaces.QuerySchoolContract import org.springframework.stereotype.Component import java.time.LocalDate import java.time.LocalDateTime import java.time.YearMonth @Component -class PdfDataConverter { +class PdfDataConverter( + private val querySchoolContract: QuerySchoolContract +) { fun applicationToInfo( - application: Any, - score: Any, + application: Application, + score: Any, // TODO: Score 도메인이 없어서 더미값 사용 ): PdfData { val values: MutableMap = HashMap() setReceiptCode(application, values) @@ -43,11 +48,10 @@ class PdfDataConverter { } private fun setReceiptCode( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: Application 도메인 모델 연동 필요 - values["receiptCode"] = "더미데이터" + values["receiptCode"] = application.receiptCode.toString() } private fun setEntranceYear(values: MutableMap) { @@ -56,35 +60,35 @@ class PdfDataConverter { } private fun setVeteransNumber( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: Application 도메인 모델 연동 필요 - values["veteransNumber"] = "" + values["veteransNumber"] = application.veteransNumber?.toString() ?: "" } private fun setPersonalInfo( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: Application 도메인 모델 연동 필요 - values["userName"] = setBlankIfNull("더미사용자명") + values["userName"] = setBlankIfNull(application.applicantName) + // TODO: application 도메인에서 성별 정보 가져오기 필요 - 현재 더미값 values["isMale"] = toBallotBox(true) values["isFemale"] = toBallotBox(false) - values["address"] = setBlankIfNull("더미주소") - values["detailAddress"] = setBlankIfNull("더미상세주소") + values["address"] = setBlankIfNull(application.streetAddress) + values["detailAddress"] = setBlankIfNull(application.detailAddress) + // TODO: application 도메인에서 생일 정보 가져오기 필요 - 현재 더미값 values["birthday"] = setBlankIfNull("2000.01.01") - values["region"] = "대전" - values["applicationType"] = "일반전형" + values["region"] = if (application.isDaejeon == true) "대전" else "비대전" + values["applicationType"] = application.applicationType?.name ?: "일반전형" values["applicationRemark"] = "해당없음" } private fun setAttendanceAndVolunteer( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: ApplicationCase 도메인 모델 연동 필요 + // TODO: 출석/봉사 도메인이 없어서 더미값 사용 values["absenceDayCount"] = 0 values["latenessCount"] = 0 values["earlyLeaveCount"] = 0 @@ -93,39 +97,28 @@ class PdfDataConverter { } private fun setGenderInfo( - application: Any, + application: Application, values: MutableMap, ) { + // TODO: application 도메인에서 성별 정보 가져오기 필요 - 현재 더미값 values["gender"] = setBlankIfNull("남") } - private fun setSchoolInfo( - application: Any, - values: MutableMap, - ) { - // TODO: 졸업정보 및 학교정보 연동 필요 - values["schoolCode"] = "더미학교코드" - values["schoolRegion"] = "더미지역" - values["schoolClass"] = "더미반" - values["schoolTel"] = toFormattedPhoneNumber("0421234567") - values["schoolName"] = "더미중학교" - } - private fun setPhoneNumber( - application: Any, + application: Application, values: MutableMap, ) { - values["applicantTel"] = toFormattedPhoneNumber("01012345678") - values["parentTel"] = toFormattedPhoneNumber("01087654321") + values["applicantTel"] = toFormattedPhoneNumber(application.applicantTel ?: "01012345678") + values["parentTel"] = toFormattedPhoneNumber(application.parentTel ?: "01087654321") } private fun setGraduationClassification( - application: Any, + application: Application, values: MutableMap, ) { values.putAll(emptyGraduationClassification()) - // TODO: 졸업정보 연동 필요 + // TODO: 졸업정보 도메인이 없어서 더미값 사용 val yearMonth = YearMonth.now() values["graduateYear"] = yearMonth.year.toString() values["graduateMonth"] = yearMonth.monthValue.toString() @@ -133,28 +126,31 @@ class PdfDataConverter { } private fun setUserType( - application: Any, + application: Application, values: MutableMap, ) { + val isDaejeon = application.isDaejeon ?: false + val isCommon = application.applicationType == ApplicationType.COMMON + val list = listOf( - "isQualificationExam" to false, - "isGraduate" to true, - "isProspectiveGraduate" to false, - "isDaejeon" to true, - "isNotDaejeon" to false, - "isBasicLiving" to false, - "isFromNorth" to false, - "isLowestIncome" to false, - "isMulticultural" to false, - "isOneParent" to false, - "isTeenHouseholder" to false, - "isPrivilegedAdmission" to false, - "isNationalMerit" to false, - "isProtectedChildren" to false, - "isCommon" to true, - "isMeister" to false, - "isSocialMerit" to false, + "isQualificationExam" to false, // TODO: 검정고시 정보 도메인 없어서 더미값 + "isGraduate" to true, // TODO: 졸업정보 도메인 없어서 더미값 + "isProspectiveGraduate" to false, // TODO: 졸업정보 도메인 없어서 더미값 + "isDaejeon" to isDaejeon, + "isNotDaejeon" to !isDaejeon, + "isBasicLiving" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isFromNorth" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isLowestIncome" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isMulticultural" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isOneParent" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isTeenHouseholder" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isPrivilegedAdmission" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isNationalMerit" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isProtectedChildren" to false, // TODO: 사회적배려 정보 도메인 없어서 더미값 + "isCommon" to isCommon, + "isMeister" to (application.applicationType == ApplicationType.MEISTER), + "isSocialMerit" to (application.applicationType == ApplicationType.SOCIAL), ) list.forEach { (key, value) -> @@ -163,20 +159,19 @@ class PdfDataConverter { } private fun setExtraScore( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: ApplicationCase 연동 필요 + // TODO: 상점/자격증 도메인이 없어서 더미값 사용 values["hasCompetitionPrize"] = toCircleBallotbox(false) values["hasCertificate"] = toCircleBallotbox(false) } private fun setGradeScore( - application: Any, - score: Any, + application: Application, + score: Any, // TODO: Score 도메인이 없어서 더미값 사용 values: MutableMap, ) { - // TODO: Score 도메인 모델 연동 필요 with(values) { put("conversionScore1st", "80.0") put("conversionScore2nd", "85.0") @@ -189,10 +184,10 @@ class PdfDataConverter { } private fun setAllSubjectScores( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: ApplicationCase 연동 필요 - 일반졸업 케이스로 더미 데이터 + // TODO: 성적 도메인이 없어서 더미값 사용 val subjects = listOf("국어", "사회", "역사", "수학", "과학", "영어", "기술가정") subjects.forEach { subject -> @@ -228,47 +223,51 @@ class PdfDataConverter { } private fun setIntroduction( - application: Any, + application: Application, values: MutableMap, ) { - values["selfIntroduction"] = setBlankIfNull("더미 자기소개") - values["studyPlan"] = setBlankIfNull("더미 학업계획") + values["selfIntroduction"] = setBlankIfNull(application.selfIntroduce) + values["studyPlan"] = setBlankIfNull(application.studyPlan) values["newLineChar"] = "\n" } private fun setTeacherInfo( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: 졸업정보 연동 필요 + // TODO: 교사정보 도메인이 없어서 더미값 사용 values["teacherName"] = "더미선생님" values["teacherTel"] = toFormattedPhoneNumber("0421234567") } private fun setParentInfo( - application: Any, + application: Application, values: MutableMap, ) { - values["parentName"] = "더미학부모" - values["parentRelation"] = "부" + values["parentName"] = application.parentName ?: "더미학부모" + values["parentRelation"] = application.parentRelation ?: "부" } private fun setRecommendations( - application: Any, + application: Application, values: MutableMap, ) { - values["isDaejeonAndMeister"] = markIfTrue(false) - values["isDaejeonAndSocialMerit"] = markIfTrue(false) - values["isNotDaejeonAndMeister"] = markIfTrue(false) - values["isNotDaejeonAndSocialMerit"] = markIfTrue(false) + val isDaejeon = application.isDaejeon ?: false + val isMeister = application.applicationType == ApplicationType.MEISTER + val isSocialMerit = application.applicationType == ApplicationType.SOCIAL + + values["isDaejeonAndMeister"] = markIfTrue(isDaejeon && isMeister) + values["isDaejeonAndSocialMerit"] = markIfTrue(isDaejeon && isSocialMerit) + values["isNotDaejeonAndMeister"] = markIfTrue(!isDaejeon && isMeister) + values["isNotDaejeonAndSocialMerit"] = markIfTrue(!isDaejeon && isSocialMerit) } private fun setBase64Image( - application: Any, + application: Application, values: MutableMap, ) { - // TODO: 이미지 파일 연동 필요 - values["base64Image"] = "" + // TODO: 이미지 파일 처리 로직 필요 + values["base64Image"] = application.photoPath ?: "" } private fun markIfTrue(isTrue: Boolean): String { @@ -295,6 +294,27 @@ class PdfDataConverter { ) } + private fun setSchoolInfo( + application: Application, + values: MutableMap, + ) { + // TODO: Application에 schoolCode 필드가 없어서 School 조회 불가 + // TODO: schoolCode 필드 추가되면 아래와 같이 사용 + // val school = querySchoolContract.querySchoolBySchoolCode(application.schoolCode) + // if (school != null) { + // values["schoolCode"] = school.code + // values["schoolRegion"] = school.regionName + // values["schoolTel"] = toFormattedPhoneNumber(school.tel) + // values["schoolName"] = school.name + // } + + values["schoolCode"] = "더미학교코드" + values["schoolRegion"] = "더미지역" + values["schoolClass"] = "더미반" // TODO: 학급 정보는 별도 도메인 필요 + values["schoolTel"] = toFormattedPhoneNumber("0421234567") + values["schoolName"] = "더미중학교" + } + private fun toFormattedPhoneNumber(phoneNumber: String?): String { if (phoneNumber.isNullOrBlank()) { return "" diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt index 96f42ee3..c4e67d8b 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt @@ -7,6 +7,8 @@ import com.itextpdf.layout.Document import hs.kr.entrydsm.application.global.document.pdf.data.PdfDataConverter import hs.kr.entrydsm.application.global.document.pdf.data.TemplateFileName import hs.kr.entrydsm.application.global.document.pdf.facade.PdfDocumentFacade +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.application.values.ApplicationType import org.springframework.stereotype.Component import java.io.ByteArrayOutputStream import java.util.LinkedList @@ -19,15 +21,15 @@ class ApplicationPdfGenerator( private val pdfDocumentFacade: PdfDocumentFacade, ) { fun generate( - application: Any, - score: Any, + application: Application, + score: Any, // TODO: Score 도메인이 없어서 더미값 사용 ): ByteArray { return generateApplicationPdf(application, score) } private fun generateApplicationPdf( - application: Any, - score: Any, + application: Application, + score: Any, // TODO: Score 도메인이 없어서 더미값 사용 ): ByteArray { val data = pdfDataConverter.applicationToInfo(application, score) val templates = getTemplateFileNames(application) @@ -67,7 +69,7 @@ class ApplicationPdfGenerator( } } - private fun getTemplateFileNames(application: Any): MutableList { + private fun getTemplateFileNames(application: Application): MutableList { val result = LinkedList( listOf( @@ -79,10 +81,10 @@ class ApplicationPdfGenerator( ), ) - // TODO: 조건부 추천서 추가 로직 - // if (!application.isQualificationExam() && !application.isCommonApplicationType()) { - // result.add(2, TemplateFileName.RECOMMENDATION) - // } + // TODO: Score 도메인이 없어서 더미 조건으로 처리 + if (application.applicationType != ApplicationType.COMMON) { + result.add(2, TemplateFileName.RECOMMENDATION) + } return result } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt index 3279f79b..068e13a0 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt @@ -1,23 +1,51 @@ package hs.kr.entrydsm.application.global.document.pdf.generator +import com.itextpdf.kernel.pdf.PdfDocument +import com.itextpdf.kernel.pdf.PdfWriter +import com.itextpdf.kernel.utils.PdfMerger import hs.kr.entrydsm.application.global.document.pdf.data.IntroductionPdfConverter import hs.kr.entrydsm.application.global.document.pdf.data.TemplateFileName +import hs.kr.entrydsm.application.global.document.pdf.facade.PdfDocumentFacade +import hs.kr.entrydsm.domain.application.aggregates.Application import org.springframework.stereotype.Component +import java.io.ByteArrayOutputStream +import kotlin.collections.toMap @Component class IntroductionPdfGenerator( private val pdfProcessor: PdfProcessor, private val introductionPdfConverter: IntroductionPdfConverter, private val templateProcessor: TemplateProcessor, + private val pdfDocumentFacade: PdfDocumentFacade ) { - fun generate(application: Any): ByteArray { + fun generate(applicationList: List): ByteArray { + val outputStream = ByteArrayOutputStream() + val mergedDocument = PdfDocument(PdfWriter(outputStream)) + val pdfMerger = PdfMerger(mergedDocument) + + applicationList.forEach { application -> + val pdfStream = generateIntroductionPdf(application) + val pdfDoc = pdfDocumentFacade.getPdfDocument(pdfStream) + mergeDocument(pdfMerger, pdfDoc) + } + + mergedDocument.close() + return outputStream.toByteArray() + } + + private fun generateIntroductionPdf(application: Application): ByteArrayOutputStream { val data = introductionPdfConverter.execute(application) - val html = - templateProcessor.convertTemplateIntoHtmlString( - TemplateFileName.ADMIN_INTRODUCTION, - data.toMap(), - ) + val template = TemplateFileName.ADMIN_INTRODUCTION + + val html = templateProcessor.convertTemplateIntoHtmlString(template, data.toMap()) val pdfStream = pdfProcessor.convertHtmlToPdf(html) - return pdfStream.toByteArray() + return pdfStream + } + + private fun mergeDocument(merger: PdfMerger, document: PdfDocument?) { + if (document != null) { + merger.merge(document, 1, document.numberOfPages) + document.close() + } } } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/presentation/PdfTestController.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/presentation/PdfTestController.kt index 2f87434c..f51512e1 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/presentation/PdfTestController.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/presentation/PdfTestController.kt @@ -2,11 +2,14 @@ package hs.kr.entrydsm.application.global.document.pdf.presentation import hs.kr.entrydsm.application.global.document.pdf.generator.ApplicationPdfGenerator import hs.kr.entrydsm.application.global.document.pdf.generator.IntroductionPdfGenerator +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.application.values.ApplicationType import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import java.util.UUID @RestController @RequestMapping("/pdf") @@ -16,7 +19,28 @@ class PdfTestController( ) { @GetMapping("/test") fun testPdf(): ResponseEntity { - val dummyApp = Any() + // TODO: 실제 Application 조회 로직 필요 + val dummyApp = Application( + receiptCode = 12345L, + isDaejeon = true, + isOutOfHeadcount = false, + photoPath = null, + applicantName = "테스트사용자", + applicantTel = "01012345678", + parentName = "테스트학부모", + parentTel = "01087654321", + parentRelation = "부", + streetAddress = "테스트주소", + postalCode = "12345", + detailAddress = "테스트상세주소", + applicationType = ApplicationType.COMMON, + studyPlan = "테스트 학업계획", + selfIntroduce = "테스트 자기소개", + userId = UUID.randomUUID(), + veteransNumber = null + ) + + // TODO: Score 도메인이 없어서 더미값 사용 val dummyScore = Any() val pdfBytes = applicationPdfGenerator.generate(dummyApp, dummyScore) @@ -29,9 +53,28 @@ class PdfTestController( @GetMapping("/test-introduction") fun testIntroductionPdf(): ResponseEntity { - val dummyApp = Any() + // TODO: 실제 Application 리스트 조회 로직 필요 + val dummyApp = Application( + receiptCode = 12345L, + isDaejeon = true, + isOutOfHeadcount = false, + photoPath = null, + applicantName = "테스트사용자", + applicantTel = "01012345678", + parentName = "테스트학부모", + parentTel = "01087654321", + parentRelation = "부", + streetAddress = "테스트주소", + postalCode = "12345", + detailAddress = "테스트상세주소", + applicationType = ApplicationType.COMMON, + studyPlan = "테스트 학업계획", + selfIntroduce = "테스트 자기소개", + userId = UUID.randomUUID(), + veteransNumber = null + ) - val pdfBytes = introductionPdfGenerator.generate(dummyApp) + val pdfBytes = introductionPdfGenerator.generate(listOf(dummyApp)) return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) From 31f7f8b10d4d9abd837df6ad73e1716acdfcad63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 2 Sep 2025 21:18:13 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor=20(=20#36=20)=20:=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EC=97=B0=EB=8F=99=20=EA=B0=80=EB=8A=A5=ED=95=9C=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EB=93=A4=EA=B3=BC=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PrintAdmissionTicketGenerator.kt | 95 ++++++++---- .../generator/PrintApplicantCodesGenerator.kt | 31 ++-- .../PrintApplicationCheckListGenerator.kt | 101 ++++++++----- .../PrintApplicationInfoGenerator.kt | 105 +++++++------ .../excel/presentation/ExcelTestController.kt | 141 +++++++++++++++++- 5 files changed, 328 insertions(+), 145 deletions(-) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt index f4d8fed4..88420113 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt @@ -1,5 +1,9 @@ package hs.kr.entrydsm.application.global.excel.generator +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.school.aggregate.School +import hs.kr.entrydsm.domain.status.aggregates.Status +import hs.kr.entrydsm.domain.user.aggregates.User import jakarta.servlet.http.HttpServletResponse import org.apache.poi.ss.usermodel.Cell import org.apache.poi.ss.usermodel.CellStyle @@ -26,8 +30,14 @@ class PrintAdmissionTicketGenerator { private lateinit var drawing: XSSFDrawing - fun execute(response: HttpServletResponse) { - val targetWorkbook = generate() + fun execute( + response: HttpServletResponse, + applications: List, + users: List, + schools: List, + statuses: List, + ) { + val targetWorkbook = generate(applications, users, schools, statuses) try { setResponseHeaders(response) targetWorkbook.write(response.outputStream) @@ -38,7 +48,12 @@ class PrintAdmissionTicketGenerator { } } - fun generate(): Workbook { + fun generate( + applications: List, + users: List, + schools: List, + statuses: List, + ): Workbook { val sourceWorkbook = loadSourceWorkbook() val targetWorkbook = XSSFWorkbook() @@ -50,32 +65,20 @@ class PrintAdmissionTicketGenerator { val styleMap = createStyleMap(sourceWorkbook, targetWorkbook) targetSheet.setDefaultColumnWidth(13) - // 더미 데이터 - val dummyApplications = - listOf( - mapOf( - "receiptCode" to 1001L, - "examCode" to "DUMMY001", - "applicantName" to "홍길동", - "schoolName" to "더미고등학교", - "isDaejeon" to "대전", - "applicationType" to "일반전형", - ), - mapOf( - "receiptCode" to 1002L, - "examCode" to "DUMMY002", - "applicantName" to "김철수", - "schoolName" to "테스트고등학교", - "isDaejeon" to "전국", - "applicationType" to "마이스터전형", - ), - ) + val userMap = users.associateBy { it.id } + val schoolMap = schools.associateBy { it.code } + val statusMap = statuses.associateBy { it.receiptCode } var currentRowIndex = 0 - dummyApplications.forEach { dummyApp -> - fillApplicationData(sourceSheet, 0, dummyApp, sourceWorkbook) + applications.forEach { application -> + val user = userMap[application.userId] + val status = statusMap[application.receiptCode] + // TODO: Application에 schoolCode 필드 없어서 School 조회 불가 + val school: School? = null + + fillApplicationData(sourceSheet, 0, application, user, school, status, sourceWorkbook) copyRows(sourceSheet, targetSheet, 0, 16, currentRowIndex, styleMap) - copyDummyImage(targetSheet, currentRowIndex) + copyApplicationImage(application, targetSheet, currentRowIndex) currentRowIndex += 20 } @@ -181,15 +184,41 @@ class PrintAdmissionTicketGenerator { fun fillApplicationData( sheet: Sheet, startRowIndex: Int, - dummyApp: Map, + application: Application, + user: User?, + school: School?, + status: Status?, workbook: Workbook, ) { - setValue(sheet, "E4", dummyApp["examCode"].toString()) - setValue(sheet, "E6", dummyApp["applicantName"].toString()) - setValue(sheet, "E8", dummyApp["schoolName"].toString()) - setValue(sheet, "E10", dummyApp["isDaejeon"].toString()) - setValue(sheet, "E12", dummyApp["applicationType"].toString()) - setValue(sheet, "E14", dummyApp["receiptCode"].toString()) + setValue(sheet, "E4", status?.examCode ?: "미발급") + setValue(sheet, "E6", application.applicantName ?: "") + setValue(sheet, "E8", school?.name ?: "더미중학교") + setValue(sheet, "E10", if (application.isDaejeon == true) "대전" else "전국") + setValue(sheet, "E12", translateApplicationType(application.applicationType?.name)) + setValue(sheet, "E14", application.receiptCode.toString()) + } + + fun copyApplicationImage( + application: Application, + targetSheet: Sheet, + targetRowIndex: Int, + ) { + // TODO: 이미지 파일 처리 로직 필요 + if (!application.photoPath.isNullOrBlank()) { + // TODO: 실제 이미지 로드 로직 + copyDummyImage(targetSheet, targetRowIndex) + } else { + copyDummyImage(targetSheet, targetRowIndex) + } + } + + private fun translateApplicationType(applicationType: String?): String { + return when (applicationType) { + "COMMON" -> "일반전형" + "MEISTER" -> "마이스터전형" + "SOCIAL" -> "사회통합전형" + else -> "일반전형" + } } fun copyDummyImage( diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt index 371dd289..9d292eba 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt @@ -1,6 +1,8 @@ package hs.kr.entrydsm.application.global.excel.generator import hs.kr.entrydsm.application.global.excel.model.ApplicantCode +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.status.aggregates.Status import jakarta.servlet.http.HttpServletResponse import org.apache.poi.ss.usermodel.Row import org.springframework.stereotype.Component @@ -10,22 +12,18 @@ import java.time.format.DateTimeFormatter @Component class PrintApplicantCodesGenerator { - fun execute(response: HttpServletResponse) { + fun execute(response: HttpServletResponse, applications: List, statuses: List) { val applicantCode = ApplicantCode() val sheet = applicantCode.getSheet() applicantCode.format() - // 더미 데이터로 테스트 - val dummyData = - listOf( - Triple("DUMMY001", 1001L, "홍길동"), - Triple("DUMMY002", 1002L, "김철수"), - Triple("DUMMY003", 1003L, "이영희"), - ) - - dummyData.forEachIndexed { index, (examCode, receiptCode, name) -> + // TODO: 1차 합격자만 필터링하는 로직 필요 + val statusMap = statuses.associateBy { it.receiptCode } + + applications.forEachIndexed { index, application -> + val status = statusMap[application.receiptCode] val row = sheet.createRow(index + 1) - insertCode(row, examCode, receiptCode, name) + insertCode(row, application, status) } try { @@ -43,12 +41,11 @@ class PrintApplicantCodesGenerator { private fun insertCode( row: Row, - examCode: String, - receiptCode: Long, - name: String, + application: Application, + status: Status?, ) { - row.createCell(0).setCellValue(examCode) - row.createCell(1).setCellValue(receiptCode.toString()) - row.createCell(2).setCellValue(name) + row.createCell(0).setCellValue(status?.examCode ?: "미발급") + row.createCell(1).setCellValue(application.receiptCode.toString()) + row.createCell(2).setCellValue(application.applicantName ?: "") } } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt index 55dbe3a8..8619feef 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt @@ -1,5 +1,9 @@ package hs.kr.entrydsm.application.global.excel.generator +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.school.aggregate.School +import hs.kr.entrydsm.domain.status.aggregates.Status +import hs.kr.entrydsm.domain.user.aggregates.User import jakarta.servlet.ServletOutputStream import jakarta.servlet.http.HttpServletResponse import org.apache.poi.ss.usermodel.BorderStyle @@ -20,38 +24,36 @@ class PrintApplicationCheckListGenerator { private val workbook: Workbook = XSSFWorkbook() private val sheet: Sheet = workbook.createSheet("application Check List") - fun printApplicationCheckList(httpServletResponse: HttpServletResponse) { + fun printApplicationCheckList( + applications: List, + users: List, + schools: List, + statuses: List, + httpServletResponse: HttpServletResponse, + ) { var outputStream: ServletOutputStream? = null var dh = 0 try { - // 더미 데이터 - val dummyApplications = - listOf( - createDummyApplication(1001L, "홍길동", "더미고등학교"), - createDummyApplication(1002L, "김철수", "테스트고등학교"), - createDummyApplication(1003L, "이영희", "샘플고등학교"), - ) + val userMap = users.associateBy { it.id } + val schoolMap = schools.associateBy { it.code } + val statusMap = statuses.associateBy { it.receiptCode } - dummyApplications.forEach { dummyData -> + applications.forEach { application -> + val user = userMap[application.userId] + val status = statusMap[application.receiptCode] + // TODO: Application에 schoolCode 필드 없어서 School 조회 불가 + val school: School? = null + formatSheet(dh) - insertDataIntoSheet(dummyData, dh) + insertDataIntoSheet(application, user, school, status, dh) dh += 20 } httpServletResponse.apply { - contentType = - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - + contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" val formatFilename = "attachment;filename=\"점검표" - val time = - LocalDateTime.now() - .format(DateTimeFormatter.ofPattern("yyyy년MM월dd일_HH시mm분")) - - val fileName = - String( - ("$formatFilename$time.xlsx\"").toByteArray(Charsets.UTF_8), - Charsets.ISO_8859_1, - ) + val time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy년MM월dd일_HH시mm분")) + val fileName = String(("$formatFilename$time.xlsx\"").toByteArray(Charsets.UTF_8), Charsets.ISO_8859_1) setHeader("Content-Disposition", fileName) } @@ -283,24 +285,27 @@ class PrintApplicationCheckListGenerator { } private fun insertDataIntoSheet( - dummyData: Map, + application: Application, + user: User?, + school: School?, + status: Status?, dh: Int, ) { - getCell(dh + 1, 2).setCellValue(dummyData["receiptCode"].toString()) - getCell(dh + 1, 3).setCellValue(dummyData["schoolName"].toString()) - getCell(dh + 1, 6).setCellValue(dummyData["educationalStatus"].toString()) - getCell(dh + 1, 7).setCellValue(dummyData["graduateYear"].toString()) - getCell(dh + 4, 1).setCellValue(dummyData["applicationType"].toString()) - getCell(dh + 3, 2).setCellValue(dummyData["applicantName"].toString()) - getCell(dh + 3, 6).setCellValue(dummyData["studentNumber"].toString()) - getCell(dh + 3, 1).setCellValue(dummyData["isDaejeon"].toString()) - getCell(dh + 4, 2).setCellValue(dummyData["birthDate"].toString()) - getCell(dh + 4, 6).setCellValue(dummyData["phoneNumber"].toString()) - getCell(dh + 5, 1).setCellValue(dummyData["applicationRemark"].toString()) - getCell(dh + 5, 2).setCellValue(dummyData["sex"].toString()) - getCell(dh + 5, 6).setCellValue(dummyData["parentPhoneNumber"].toString()) + getCell(dh + 1, 2).setCellValue(application.receiptCode.toString()) + getCell(dh + 1, 3).setCellValue(school?.name ?: "더미중학교") + getCell(dh + 1, 6).setCellValue("졸업예정") // TODO: 학력구분 도메인 없어서 더미값 + getCell(dh + 1, 7).setCellValue("2024") // TODO: 졸업년도 도메인 없어서 더미값 + getCell(dh + 4, 1).setCellValue(translateApplicationType(application.applicationType?.name)) + getCell(dh + 3, 2).setCellValue(application.applicantName ?: "") + getCell(dh + 3, 6).setCellValue("30315") // TODO: 학번 정보 도메인 없어서 더미값 + getCell(dh + 3, 1).setCellValue(if (application.isDaejeon == true) "대전" else "전국") + getCell(dh + 4, 2).setCellValue("2005-03-15") // TODO: User 도메인에서 생일 정보 필요 + getCell(dh + 4, 6).setCellValue(formatPhoneNumber(application.applicantTel)) + getCell(dh + 5, 1).setCellValue("해당없음") // TODO: 추가유형 도메인 없어서 더미값 + getCell(dh + 5, 2).setCellValue("남") // TODO: User 도메인에서 성별 정보 필요 + getCell(dh + 5, 6).setCellValue(formatPhoneNumber(application.parentTel)) - // 출석 관련 더미 데이터 + // TODO: 출석 관련 도메인이 없어서 더미값 사용 getCell(dh + 8, 1).setCellValue("0") getCell(dh + 8, 2).setCellValue("0") getCell(dh + 8, 3).setCellValue("0") @@ -310,7 +315,7 @@ class PrintApplicationCheckListGenerator { getCell(dh + 8, 7).setCellValue("15.0") getCell(dh + 10, 7).setCellValue("170.5") - // 성적 더미 데이터 + // TODO: 성적 도메인이 없어서 더미값 사용 val subjects = listOf("국어", "사회", "역사", "수학", "과학", "기술가정", "영어") val dummyGrades = listOf("A", "B", "A", "B", "A", "B", "A") subjects.forEachIndexed { index, subject -> @@ -322,9 +327,12 @@ class PrintApplicationCheckListGenerator { getCell(rowIndex, 5).setCellValue(dummyGrades[index]) } + // TODO: 대회/자격증 도메인이 없어서 더미값 사용 getCell(dh + 11, 7).setCellValue("O") getCell(dh + 12, 7).setCellValue("X") getCell(dh + 13, 7).setCellValue("5.0") + + // TODO: Score 도메인이 없어서 더미값 사용 getCell(dh + 18, 2).setCellValue("180.0") getCell(dh + 18, 3).setCellValue("170.0") getCell(dh + 18, 4).setCellValue("165.0") @@ -338,6 +346,23 @@ class PrintApplicationCheckListGenerator { setRowHeight(dh + 0, 71) } + private fun translateApplicationType(applicationType: String?): String { + return when (applicationType) { + "COMMON" -> "일반전형" + "MEISTER" -> "마이스터전형" + "SOCIAL" -> "사회통합전형" + else -> "일반전형" + } + } + + private fun formatPhoneNumber(phoneNumber: String?): String { + if (phoneNumber.isNullOrBlank()) return "" + if (phoneNumber.length == 8) { + return phoneNumber.replace("(\\d{4})(\\d{4})".toRegex(), "$1-$2") + } + return phoneNumber.replace("(\\d{2,3})(\\d{3,4})(\\d{4})".toRegex(), "$1-$2-$3") + } + enum class Direction { TOP, BOTTOM, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt index 54e150e3..0735c53d 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt @@ -1,6 +1,10 @@ package hs.kr.entrydsm.application.global.excel.generator import hs.kr.entrydsm.application.global.excel.model.ApplicationInfo +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.school.aggregate.School +import hs.kr.entrydsm.domain.status.aggregates.Status +import hs.kr.entrydsm.domain.user.aggregates.User import jakarta.servlet.http.HttpServletResponse import org.apache.poi.ss.usermodel.Row import org.springframework.stereotype.Component @@ -10,21 +14,29 @@ import java.time.format.DateTimeFormatter @Component class PrintApplicationInfoGenerator { - fun execute(httpServletResponse: HttpServletResponse) { + fun execute( + httpServletResponse: HttpServletResponse, + applications: List, + users: List, + schools: List, + statuses: List, + ) { val applicationInfo = ApplicationInfo() val sheet = applicationInfo.getSheet() applicationInfo.format() - // 더미 데이터로 테스트 - val dummyApplications = listOf( - createDummyApplication(1001L, "홍길동", "더미고등학교"), - createDummyApplication(1002L, "김철수", "테스트고등학교"), - createDummyApplication(1003L, "이영희", "샘플고등학교"), - ) + val userMap = users.associateBy { it.id } + val schoolMap = schools.associateBy { it.code } + val statusMap = statuses.associateBy { it.receiptCode } - dummyApplications.forEachIndexed { index, dummyData -> + applications.forEachIndexed { index, application -> + val user = userMap[application.userId] + val status = statusMap[application.receiptCode] + // TODO: Application에 schoolCode 필드 없어서 School 조회 불가 + val school: School? = null + val row = sheet.createRow(index + 1) - insertCode(row, dummyData) + insertCode(row, application, user, school, status) } try { @@ -42,64 +54,51 @@ class PrintApplicationInfoGenerator { } } - private fun createDummyApplication( - receiptCode: Long, - name: String, - schoolName: String, - ): Map { - return mapOf( - "receiptCode" to receiptCode, - "applicationType" to "일반전형", - "isDaejeon" to "대전", - "applicationRemark" to "해당없음", - "applicantName" to name, - "birthDate" to "2005-03-15", - "address" to "대전광역시 유성구 대덕대로 1234", - "applicantTel" to "010-1234-5678", - "sex" to "남", - "educationalStatus" to "졸업예정", - "graduateDate" to "2024", - "schoolName" to schoolName, - "classNumber" to "3", - "parentName" to "홍부모", - "parentTel" to "010-9876-5432", - "examCode" to "DUMMY${receiptCode.toString().takeLast(3)}", - ) - } - private fun insertCode( row: Row, - dummyData: Map, + application: Application, + user: User?, + school: School?, + status: Status?, ) { - row.createCell(0).setCellValue(dummyData["receiptCode"].toString()) - row.createCell(1).setCellValue(dummyData["applicationType"].toString()) - row.createCell(2).setCellValue(dummyData["isDaejeon"].toString()) - row.createCell(3).setCellValue(dummyData["applicationRemark"].toString()) - row.createCell(4).setCellValue(dummyData["applicantName"].toString()) - row.createCell(5).setCellValue(dummyData["birthDate"].toString()) - row.createCell(6).setCellValue(dummyData["address"].toString()) - row.createCell(7).setCellValue(dummyData["applicantTel"].toString()) - row.createCell(8).setCellValue(dummyData["sex"].toString()) - row.createCell(9).setCellValue(dummyData["educationalStatus"].toString()) - row.createCell(10).setCellValue(dummyData["graduateDate"].toString()) - row.createCell(11).setCellValue(dummyData["schoolName"].toString()) - row.createCell(12).setCellValue(dummyData["classNumber"].toString()) - row.createCell(13).setCellValue(dummyData["parentName"].toString()) - row.createCell(14).setCellValue(dummyData["parentTel"].toString()) + row.createCell(0).setCellValue(application.receiptCode.toString()) + row.createCell(1).setCellValue(translateApplicationType(application.applicationType?.name)) + row.createCell(2).setCellValue(if (application.isDaejeon == true) "대전" else "전국") + row.createCell(3).setCellValue("해당없음") // TODO: 추가유형 도메인 없어서 더미값 + row.createCell(4).setCellValue(application.applicantName ?: "") + row.createCell(5).setCellValue("2005-03-15") // TODO: User 도메인에서 생일 정보 필요 + row.createCell(6).setCellValue("${application.streetAddress ?: ""} ${application.detailAddress ?: ""}") + row.createCell(7).setCellValue(application.applicantTel ?: "") + row.createCell(8).setCellValue("남") // TODO: User 도메인에서 성별 정보 필요 + row.createCell(9).setCellValue("졸업예정") // TODO: 학력구분 도메인 없어서 더미값 + row.createCell(10).setCellValue("2024") // TODO: 졸업년도 도메인 없어서 더미값 + row.createCell(11).setCellValue(school?.name ?: "더미중학교") + row.createCell(12).setCellValue("3") // TODO: 학급 정보 도메인 없어서 더미값 + row.createCell(13).setCellValue(application.parentName ?: "") + row.createCell(14).setCellValue(application.parentTel ?: "") - // 성적 더미 데이터 + // TODO: 성적 도메인이 없어서 더미값 사용 val dummyGrades = listOf("A", "B", "A", "B", "A", "B", "A") for (i in 15..42) { row.createCell(i).setCellValue(dummyGrades[(i - 15) % dummyGrades.size]) } - // 점수 더미 데이터 + // TODO: Score 도메인이 없어서 더미값 사용 val scores = listOf( "180.0", "170.0", "165.0", "170.5", "30.0", "15.0", "0", "0", "0", "0", - "20.0", "O", "X", "5.0", "210.5", "200.0", dummyData["examCode"].toString() + "20.0", "O", "X", "5.0", "210.5", "200.0", status?.examCode ?: "미발급" ) for (i in scores.indices) { row.createCell(43 + i).setCellValue(scores[i]) } } + + private fun translateApplicationType(applicationType: String?): String { + return when (applicationType) { + "COMMON" -> "일반전형" + "MEISTER" -> "마이스터전형" + "SOCIAL" -> "사회통합전형" + else -> "일반전형" + } + } } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt index eb6b173e..05224a47 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt @@ -4,10 +4,17 @@ import hs.kr.entrydsm.application.global.excel.generator.PrintAdmissionTicketGen import hs.kr.entrydsm.application.global.excel.generator.PrintApplicantCodesGenerator import hs.kr.entrydsm.application.global.excel.generator.PrintApplicationCheckListGenerator import hs.kr.entrydsm.application.global.excel.generator.PrintApplicationInfoGenerator +import hs.kr.entrydsm.domain.application.aggregates.Application +import hs.kr.entrydsm.domain.application.values.ApplicationType +import hs.kr.entrydsm.domain.school.aggregate.School +import hs.kr.entrydsm.domain.status.aggregates.Status +import hs.kr.entrydsm.domain.status.values.ApplicationStatus +import hs.kr.entrydsm.domain.user.aggregates.User import jakarta.servlet.http.HttpServletResponse import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import java.util.UUID @RestController @RequestMapping("/api/excel/test") @@ -19,21 +26,147 @@ class ExcelTestController( ) { @GetMapping("/applicant-codes") fun downloadApplicantCodes(response: HttpServletResponse) { - printApplicantCodesGenerator.execute(response) + // TODO: 실제 Application, Status 조회 로직 필요 + val dummyApplications = createDummyApplications() + val dummyStatuses = createDummyStatuses() + + printApplicantCodesGenerator.execute(response, dummyApplications, dummyStatuses) } @GetMapping("/application-info") fun downloadApplicationInfo(response: HttpServletResponse) { - printApplicationInfoGenerator.execute(response) + // TODO: 실제 Application, User, School, Status 조회 로직 필요 + val dummyApplications = createDummyApplications() + val dummyUsers = createDummyUsers() + val dummySchools = createDummySchools() + val dummyStatuses = createDummyStatuses() + + printApplicationInfoGenerator.execute(response, dummyApplications, dummyUsers, dummySchools, dummyStatuses) } @GetMapping("/admission-ticket") fun downloadAdmissionTicket(response: HttpServletResponse) { - printAdmissionTicketGenerator.execute(response) + // TODO: 실제 Application, User, School, Status 조회 로직 필요 + val dummyApplications = createDummyApplications() + val dummyUsers = createDummyUsers() + val dummySchools = createDummySchools() + val dummyStatuses = createDummyStatuses() + + printAdmissionTicketGenerator.execute(response, dummyApplications, dummyUsers, dummySchools, dummyStatuses) } @GetMapping("/check-list") fun downloadCheckList(response: HttpServletResponse) { - printApplicationCheckListGenerator.printApplicationCheckList(response) + // TODO: 실제 Application, User, School, Status 조회 로직 필요 + val dummyApplications = createDummyApplications() + val dummyUsers = createDummyUsers() + val dummySchools = createDummySchools() + val dummyStatuses = createDummyStatuses() + + printApplicationCheckListGenerator.printApplicationCheckList( + dummyApplications, dummyUsers, dummySchools, dummyStatuses, response + ) + } + + private fun createDummyApplications(): List { + return listOf( + Application( + receiptCode = 1001L, + isDaejeon = true, + isOutOfHeadcount = false, + photoPath = null, + applicantName = "홍길동", + applicantTel = "010-1234-5678", + parentName = "홍부모", + parentTel = "010-9876-5432", + parentRelation = "부", + streetAddress = "대전광역시 유성구", + postalCode = "34144", + detailAddress = "대덕대로 1234", + applicationType = ApplicationType.COMMON, + studyPlan = "열심히 공부하겠습니다", + selfIntroduce = "안녕하세요", + userId = UUID.randomUUID(), + veteransNumber = null + ), + Application( + receiptCode = 1002L, + isDaejeon = false, + isOutOfHeadcount = false, + photoPath = null, + applicantName = "김철수", + applicantTel = "010-2345-6789", + parentName = "김부모", + parentTel = "010-8765-4321", + parentRelation = "모", + streetAddress = "서울특별시 강남구", + postalCode = "06234", + detailAddress = "테헤란로 123", + applicationType = ApplicationType.MEISTER, + studyPlan = "기술을 배우고 싶습니다", + selfIntroduce = "기술에 관심이 많습니다", + userId = UUID.randomUUID(), + veteransNumber = null + ) + ) + } + + private fun createDummyUsers(): List { + return listOf( + User( + id = UUID.randomUUID(), + phoneNumber = "010-1234-5678", + name = "홍길동", + isParent = false + ), + User( + id = UUID.randomUUID(), + phoneNumber = "010-2345-6789", + name = "김철수", + isParent = false + ) + ) + } + + private fun createDummySchools(): List { + return listOf( + School( + code = "B100000001", + name = "더미중학교", + tel = "042-123-4567", + type = "중학교", + address = "대전광역시 유성구", + regionName = "대전" + ), + School( + code = "B100000002", + name = "테스트중학교", + tel = "02-234-5678", + type = "중학교", + address = "서울특별시 강남구", + regionName = "서울" + ) + ) + } + + private fun createDummyStatuses(): List { + return listOf( + Status( + id = 1L, + examCode = "DUMMY001", + applicationStatus = ApplicationStatus.SUBMITTED, + isFirstRoundPass = true, + isSecondRoundPass = false, + receiptCode = 1001L + ), + Status( + id = 2L, + examCode = "DUMMY002", + applicationStatus = ApplicationStatus.SUBMITTED, + isFirstRoundPass = true, + isSecondRoundPass = false, + receiptCode = 1002L + ) + ) } } From af42b509a8a8b379a701eb3e04aa8d44cda89c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 3 Sep 2025 15:49:06 +0900 Subject: [PATCH 15/16] =?UTF-8?q?refactor=20(=20#36=20)=20:=20kdoc=20?= =?UTF-8?q?=EC=9D=BC=EB=B6=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pdf/config/ConverterPropertiesCreator.kt | 13 ++++++++ .../pdf/data/IntroductionPdfConverter.kt | 12 +++++++ .../global/document/pdf/data/PdfData.kt | 8 +++++ .../document/pdf/data/PdfDataConverter.kt | 14 ++++++++ .../document/pdf/data/TemplateFileName.kt | 20 +++++++++++ .../pdf/generator/ApplicationPdfGenerator.kt | 14 ++++++++ .../pdf/generator/IntroductionPdfGenerator.kt | 13 ++++++++ .../document/pdf/generator/PdfProcessor.kt | 12 +++++++ .../pdf/generator/TemplateProcessor.kt | 14 ++++++++ .../PrintAdmissionTicketGenerator.kt | 17 ++++++++++ .../generator/PrintApplicantCodesGenerator.kt | 21 ++++++++++++ .../PrintApplicationCheckListGenerator.kt | 18 ++++++++++ .../PrintApplicationInfoGenerator.kt | 33 +++++++++++++++++++ .../global/excel/model/ApplicantCode.kt | 6 ++++ .../global/excel/model/ApplicationInfo.kt | 7 ++++ .../excel/presentation/ExcelTestController.kt | 7 ++++ 16 files changed, 229 insertions(+) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/ConverterPropertiesCreator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/ConverterPropertiesCreator.kt index 2503d313..953d5cbf 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/ConverterPropertiesCreator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/ConverterPropertiesCreator.kt @@ -6,10 +6,23 @@ import com.itextpdf.io.font.FontProgramFactory import org.springframework.stereotype.Component import java.io.IOException +/** + * PDF 변환을 위한 변환 속성을 생성하는 클래스입니다. + * + * iText PDF 라이브러리에서 HTML을 PDF로 변환할 때 필요한 설정을 관리합니다. + * 특히 한글 폰트 설정을 담당하여 PDF에서 한글이 정상적으로 표시되도록 합니다. + */ @Component class ConverterPropertiesCreator { private var fontPath: String = "/fonts/" + /** + * PDF 변환을 위한 ConverterProperties를 생성합니다. + * 한글 폰트 설정을 포함하여 PDF 생성 시 필요한 모든 속성을 구성합니다. + * + * @return 설정된 ConverterProperties 객체 + * @throws IllegalStateException 폰트 파일을 찾을 수 없는 경우 + */ fun createConverterProperties(): ConverterProperties { val properties = ConverterProperties() val fontProvider = DefaultFontProvider(false, false, false) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt index c1df111c..54d1b25f 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/IntroductionPdfConverter.kt @@ -5,10 +5,22 @@ import hs.kr.entrydsm.domain.school.interfaces.QuerySchoolContract import org.springframework.stereotype.Component import java.util.HashMap +/** + * 소개서 PDF용 데이터 변환기입니다. + * + * 지원서 정보를 소개서 템플릿에서 사용할 수 있는 데이터로 변환합니다. + * 소개서에는 개인정보, 학교정보, 자기소개서, 학업계획서 등이 포함됩니다. + */ @Component class IntroductionPdfConverter( private val querySchoolContract: QuerySchoolContract ) { + /** + * 지원서 정보를 소개서 PDF 템플릿용 데이터로 변환합니다. + * + * @param application 지원서 정보 + * @return 소개서 템플릿에 사용할 PdfData 객체 + */ fun execute(application: Application): PdfData { val values: MutableMap = HashMap() setIntroduction(application, values) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfData.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfData.kt index 02c70045..baec24fb 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfData.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfData.kt @@ -1,5 +1,13 @@ package hs.kr.entrydsm.application.global.document.pdf.data +/** + * PDF 템플릿에서 사용할 데이터를 담는 클래스입니다. + * + * Key-Value 형태의 데이터를 관리하며, Thymeleaf 템플릿에서 + * 변수로 사용될 수 있도록 데이터를 구조화합니다. + * + * @property values 템플릿 변수로 사용될 데이터 맵 + */ data class PdfData( private val values: MutableMap, ) { diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt index fcd3238d..b88bb78d 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/PdfDataConverter.kt @@ -8,10 +8,24 @@ import java.time.LocalDate import java.time.LocalDateTime import java.time.YearMonth +/** + * 지원서 정보를 PDF 템플릿용 데이터로 변환하는 Converter입니다. + * + * Application, Score 등의 도메인 객체를 HTML 템플릿에서 사용할 수 있는 + * Key-Value 형태의 데이터로 변환합니다. 누락된 도메인 정보는 더미값으로 처리하며 + * 향후 도메인이 추가되면 TODO 주석을 따라 연동할 수 있습니다. + */ @Component class PdfDataConverter( private val querySchoolContract: QuerySchoolContract ) { + /** + * 지원서 정보를 PDF 템플릿용 데이터로 변환합니다. + * + * @param application 지원서 정보 + * @param score Score 도메인 (현재 더미값 사용) + * @return 템플릿에 사용할 PdfData 객체 + */ fun applicationToInfo( application: Application, score: Any, // TODO: Score 도메인이 없어서 더미값 사용 diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/TemplateFileName.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/TemplateFileName.kt index 47d86c46..7a52d11e 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/TemplateFileName.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/data/TemplateFileName.kt @@ -1,11 +1,31 @@ package hs.kr.entrydsm.application.global.document.pdf.data +/** + * PDF 생성에 사용되는 템플릿 파일명을 정의하는 객체입니다. + * + * Thymeleaf 템플릿 파일들의 이름을 상수로 관리하여 + * 템플릿 파일명 변경 시 한 곳에서 관리할 수 있도록 합니다. + * 각 상수는 resources/templates 디렉토리의 HTML 파일과 매핑됩니다. + */ object TemplateFileName { + /** 입학지원서 템플릿 */ const val APPLICATION_FOR_ADMISSION = "application_for_admission" + + /** 개인정보 수집·이용 동의서 템플릿 */ const val PRIVACY_AGREEMENT = "privacy_agreement" + + /** 자기소개서 템플릿 */ const val INTRODUCTION = "introduction" + + /** 금연서약서 템플릿 */ const val NON_SMOKING = "nonsmoking" + + /** 흡연 검사 관련 템플릿 */ const val SMOKING_EXAMINE = "smoking_examine" + + /** 추천서 템플릿 */ const val RECOMMENDATION = "recommendation" + + /** 관리자용 소개서 템플릿 */ const val ADMIN_INTRODUCTION = "admin_introduction" } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt index c4e67d8b..2b93fd18 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/ApplicationPdfGenerator.kt @@ -13,6 +13,13 @@ import org.springframework.stereotype.Component import java.io.ByteArrayOutputStream import java.util.LinkedList +/** + * 지원서 PDF를 생성하는 Generator입니다. + * + * HTML 템플릿을 기반으로 지원서, 개인정보동의서, 소개서, 금연서약서 등 + * 여러 문서를 하나의 PDF로 병합하여 완성된 지원서 PDF를 생성합니다. + * 지원유형에 따라 추천서 등 추가 문서가 포함될 수 있습니다. + */ @Component class ApplicationPdfGenerator( private val pdfProcessor: PdfProcessor, @@ -20,6 +27,13 @@ class ApplicationPdfGenerator( private val templateProcessor: TemplateProcessor, private val pdfDocumentFacade: PdfDocumentFacade, ) { + /** + * 지원서 PDF를 생성합니다. + * + * @param application 지원서 정보 + * @param score Score 도메인 (현재 더미값 사용) + * @return 생성된 PDF 바이트 배열 + */ fun generate( application: Application, score: Any, // TODO: Score 도메인이 없어서 더미값 사용 diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt index 068e13a0..f5a9951a 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/IntroductionPdfGenerator.kt @@ -11,6 +11,13 @@ import org.springframework.stereotype.Component import java.io.ByteArrayOutputStream import kotlin.collections.toMap +/** + * 소개서 PDF를 생성하는 Generator입니다. + * + * 1차 합격자들의 소개서만을 모아서 하나의 PDF로 생성합니다. + * 관리자가 면접 등에서 활용할 수 있도록 지원자별 소개서를 + * 순차적으로 배치하여 제공합니다. + */ @Component class IntroductionPdfGenerator( private val pdfProcessor: PdfProcessor, @@ -18,6 +25,12 @@ class IntroductionPdfGenerator( private val templateProcessor: TemplateProcessor, private val pdfDocumentFacade: PdfDocumentFacade ) { + /** + * 소개서 PDF를 생성합니다. + * + * @param applicationList 지원서 목록 (1차 합격자) + * @return 생성된 소개서 PDF 바이트 배열 + */ fun generate(applicationList: List): ByteArray { val outputStream = ByteArrayOutputStream() val mergedDocument = PdfDocument(PdfWriter(outputStream)) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/PdfProcessor.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/PdfProcessor.kt index 155d14df..a45aea38 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/PdfProcessor.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/PdfProcessor.kt @@ -5,10 +5,22 @@ import hs.kr.entrydsm.application.global.document.pdf.config.ConverterProperties import org.springframework.stereotype.Component import java.io.ByteArrayOutputStream +/** + * HTML을 PDF로 변환하는 핵심 처리기입니다. + * + * iText PDF 라이브러리를 사용하여 HTML 문자열을 PDF 바이트 스트림으로 변환합니다. + * 한글 폰트 설정 및 기타 PDF 생성 옵션들이 ConverterPropertiesCreator를 통해 적용됩니다. + */ @Component class PdfProcessor( private val converterPropertiesCreator: ConverterPropertiesCreator, ) { + /** + * HTML 문자열을 PDF로 변환합니다. + * + * @param html 변환할 HTML 문자열 + * @return PDF 데이터가 포함된 ByteArrayOutputStream + */ fun convertHtmlToPdf(html: String): ByteArrayOutputStream { val outputStream = ByteArrayOutputStream() HtmlConverter.convertToPdf(html, outputStream, converterPropertiesCreator.createConverterProperties()) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/TemplateProcessor.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/TemplateProcessor.kt index b32c21d3..2389500e 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/TemplateProcessor.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/generator/TemplateProcessor.kt @@ -4,10 +4,24 @@ import org.springframework.stereotype.Component import org.thymeleaf.TemplateEngine import org.thymeleaf.context.Context +/** + * Thymeleaf 템플릿을 HTML로 변환하는 처리기입니다. + * + * PDF 생성을 위해 Thymeleaf 템플릿 엔진을 사용하여 + * 템플릿 파일과 데이터를 결합해 HTML 문자열을 생성합니다. + * 생성된 HTML은 PdfProcessor에서 PDF로 변환됩니다. + */ @Component class TemplateProcessor( private val templateEngine: TemplateEngine, ) { + /** + * 템플릿 파일과 데이터를 결합하여 HTML 문자열을 생성합니다. + * + * @param template 템플릿 파일명 + * @param data 템플릿에 바인딩할 데이터 맵 + * @return 렌더링된 HTML 문자열 + */ fun convertTemplateIntoHtmlString( template: String?, data: MutableMap?, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt index 88420113..f4657ed1 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintAdmissionTicketGenerator.kt @@ -22,6 +22,13 @@ import java.io.IOException import java.time.LocalDateTime import java.time.format.DateTimeFormatter +/** + * 수험표 Excel 파일을 생성하는 Generator입니다. + * + * 템플릿 파일을 기반으로 수험표를 생성하며, 지원자 정보와 함께 + * 사진을 포함한 완성된 수험표를 제공합니다. 각 수험표는 20행씩 차지하며 + * 여러 지원자의 수험표가 하나의 파일에 연속으로 생성됩니다. + */ @Component class PrintAdmissionTicketGenerator { companion object { @@ -30,6 +37,16 @@ class PrintAdmissionTicketGenerator { private lateinit var drawing: XSSFDrawing + /** + * 수험표 Excel 파일을 생성하고 HTTP Response로 전송합니다. + * + * @param response HTTP 응답 객체 + * @param applications 지원서 목록 + * @param users 사용자 정보 목록 + * @param schools 학교 정보 목록 + * @param statuses 전형 상태 목록 + * @throws IllegalArgumentException Excel 파일 생성 중 오류 발생 시 + */ fun execute( response: HttpServletResponse, applications: List, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt index 9d292eba..c13e4989 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicantCodesGenerator.kt @@ -10,8 +10,22 @@ import java.io.IOException import java.time.LocalDateTime import java.time.format.DateTimeFormatter +/** + * 1차 합격자의 지원자번호목록 Excel 파일을 생성하는 Generator입니다. + * + * 수험번호, 접수번호, 성명으로 구성된 3개 컬럼의 Excel 파일을 생성하여 + * 관리자가 1차 합격자 목록을 확인할 수 있도록 합니다. + */ @Component class PrintApplicantCodesGenerator { + /** + * 지원자번호목록 Excel 파일을 생성하고 HTTP Response로 전송합니다. + * + * @param response HTTP 응답 객체 + * @param applications 지원서 목록 + * @param statuses 전형 상태 목록 (수험번호 포함) + * @throws IllegalArgumentException Excel 파일 생성 중 오류 발생 시 + */ fun execute(response: HttpServletResponse, applications: List, statuses: List) { val applicantCode = ApplicantCode() val sheet = applicantCode.getSheet() @@ -39,6 +53,13 @@ class PrintApplicantCodesGenerator { } } + /** + * Excel Row에 지원자 정보를 삽입합니다. + * + * @param row Excel의 Row 객체 + * @param application 지원서 정보 + * @param status 전형 상태 정보 (수험번호 포함) + */ private fun insertCode( row: Row, application: Application, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt index 8619feef..87ac6163 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationCheckListGenerator.kt @@ -19,11 +19,29 @@ import java.io.IOException import java.time.LocalDateTime import java.time.format.DateTimeFormatter +/** + * 지원서 점검표 Excel 파일을 생성하는 Generator입니다. + * + * 각 지원자마다 20행씩 차지하는 복잡한 포맷의 점검표를 생성합니다. + * 개인정보, 성적, 출석정보, 점수 등이 시각적으로 구조화된 형태로 배치되며, + * 다양한 테두리 스타일과 셀 병합을 사용하여 가독성을 높입니다. + */ @Component class PrintApplicationCheckListGenerator { private val workbook: Workbook = XSSFWorkbook() private val sheet: Sheet = workbook.createSheet("application Check List") + /** + * 지원서 점검표 Excel 파일을 생성하고 HTTP Response로 전송합니다. + * 각 지원자당 20행의 구조화된 점검표를 생성합니다. + * + * @param applications 지원서 목록 + * @param users 사용자 정보 목록 + * @param schools 학교 정보 목록 + * @param statuses 전형 상태 목록 + * @param httpServletResponse HTTP 응답 객체 + * @throws IllegalArgumentException Excel 파일 생성 중 오류 발생 시 + */ fun printApplicationCheckList( applications: List, users: List, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt index 0735c53d..660d0f11 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/generator/PrintApplicationInfoGenerator.kt @@ -12,8 +12,25 @@ import java.io.IOException import java.time.LocalDateTime import java.time.format.DateTimeFormatter +/** + * 전형자료 Excel 파일을 생성하는 Generator입니다. + * + * 60개 컬럼으로 구성된 상세한 전형자료를 포함하여 관리자가 + * 지원자의 모든 정보를 종합적으로 확인할 수 있는 Excel 파일을 생성합니다. + * 개인정보, 성적, 출석, 봉사활동, 가산점 등의 정보가 포함됩니다. + */ @Component class PrintApplicationInfoGenerator { + /** + * 전형자료 Excel 파일을 생성하고 HTTP Response로 전송합니다. + * + * @param httpServletResponse HTTP 응답 객체 + * @param applications 지원서 목록 + * @param users 사용자 정보 목록 + * @param schools 학교 정보 목록 + * @param statuses 전형 상태 목록 + * @throws IllegalArgumentException Excel 파일 생성 중 오류 발생 시 + */ fun execute( httpServletResponse: HttpServletResponse, applications: List, @@ -54,6 +71,16 @@ class PrintApplicationInfoGenerator { } } + /** + * Excel Row에 지원자의 상세 전형 정보를 삽입합니다. + * 60개 컬럼에 개인정보부터 성적, 점수까지 모든 정보를 기록합니다. + * + * @param row Excel의 Row 객체 + * @param application 지원서 정보 + * @param user 사용자 정보 + * @param school 학교 정보 + * @param status 전형 상태 정보 + */ private fun insertCode( row: Row, application: Application, @@ -93,6 +120,12 @@ class PrintApplicationInfoGenerator { } } + /** + * 지원유형을 한국어로 변환합니다. + * + * @param applicationType 지원유형 코드 + * @return 변환된 한국어 지원유형명 + */ private fun translateApplicationType(applicationType: String?): String { return when (applicationType) { "COMMON" -> "일반전형" diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicantCode.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicantCode.kt index 4943c4f3..d9bbcd19 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicantCode.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicantCode.kt @@ -5,6 +5,12 @@ import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.Workbook import org.apache.poi.xssf.usermodel.XSSFWorkbook +/** + * 지원자번호목록 Excel 파일의 구조를 정의하는 모델 클래스입니다. + * + * 수험번호, 접수번호, 성명 3개 컬럼으로 구성된 Excel 워크북을 생성하고 + * 헤더 포맷팅을 담당합니다. Apache POI 라이브러리를 사용하여 Excel 파일을 조작합니다. + */ class ApplicantCode { private val workbook: Workbook = XSSFWorkbook() private val sheet: Sheet = workbook.createSheet("지원자 목록") diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicationInfo.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicationInfo.kt index c4822fde..ca56a3f3 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicationInfo.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/model/ApplicationInfo.kt @@ -5,6 +5,13 @@ import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.Workbook import org.apache.poi.xssf.usermodel.XSSFWorkbook +/** + * 전형자료 Excel 파일의 구조를 정의하는 모델 클래스입니다. + * + * 60개 컬럼으로 구성된 상세한 전형자료 Excel 워크북을 생성하고 + * 개인정보, 성적, 출석, 봉사, 가산점 등 모든 헤더를 설정합니다. + * 각 학기별 과목 성적과 종합 점수 정보를 포함하는 복잡한 구조를 가집니다. + */ class ApplicationInfo { private val workbook: Workbook = XSSFWorkbook() private val sheet: Sheet = workbook.createSheet("전형자료") diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt index 05224a47..03dfa7dd 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/excel/presentation/ExcelTestController.kt @@ -16,6 +16,13 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import java.util.UUID +/** + * Excel 파일 생성 기능을 테스트하기 위한 Controller입니다. + * + * 개발 환경에서 Excel Generator들의 동작을 확인할 수 있도록 + * 더미 데이터를 사용하여 각종 Excel 파일을 생성하는 엔드포인트를 제공합니다. + * 실제 운영에서는 UseCase를 통해 실제 데이터가 사용됩니다. + */ @RestController @RequestMapping("/api/excel/test") class ExcelTestController( From 4b74aa9b4b868ecef097d926c0da43c5bc7c29e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 3 Sep 2025 16:18:00 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix=20(=20#36=20)=20:=20import=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/status/domain/entity/StatusCacheRedisEntity.kt | 1 + .../application/global/grpc/client/status/StatusGrpcClient.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt index f0180065..612e2894 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/status/domain/entity/StatusCacheRedisEntity.kt @@ -1,5 +1,6 @@ package hs.kr.entrydsm.application.domain.status.domain.entity +import hs.kr.entrydsm.domain.status.values.ApplicationStatus import org.springframework.data.annotation.Id import org.springframework.data.redis.core.RedisHash import org.springframework.data.redis.core.TimeToLive diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt index d90370c1..7bd4eeb9 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/grpc/client/status/StatusGrpcClient.kt @@ -2,11 +2,11 @@ package hs.kr.entrydsm.application.global.grpc.client.status import com.google.protobuf.Empty import hs.kr.entrydsm.application.global.extension.executeGrpcCallWithResilience -import hs.kr.entrydsm.application.global.grpc.dto.status.ApplicationStatus import hs.kr.entrydsm.application.global.grpc.dto.status.InternalStatusListResponse import hs.kr.entrydsm.application.global.grpc.dto.status.InternalStatusResponse import hs.kr.entrydsm.casper.status.proto.StatusServiceGrpc import hs.kr.entrydsm.casper.status.proto.StatusServiceProto +import hs.kr.entrydsm.domain.status.values.ApplicationStatus import io.github.resilience4j.circuitbreaker.CircuitBreaker import io.github.resilience4j.retry.Retry import io.grpc.Channel