From 770c58aef84bd66432d1f98ce2d0c7a0883dbc2d Mon Sep 17 00:00:00 2001 From: kusuri12 Date: Sat, 17 Oct 2026 22:48:10 +0900 Subject: [PATCH] =?UTF-8?q?fix(application):=20=EC=96=B4=EB=93=9C=EB=AF=BC?= =?UTF-8?q?=20=EC=9E=90=EA=B8=B0=EC=86=8C=EA=B0=9C=EC=84=9C=EC=99=80=20?= =?UTF-8?q?=ED=95=99=EC=97=85=EA=B3=84=ED=9A=8D=EC=84=9C=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=EC=B6=9C=EB=A0=A5=20#399?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../document/ApplicationFormPdfAdapter.kt | 10 ++- .../document/LocalExportDocumentAdapter.kt | 16 ++--- .../document/ApplicationFormPdfTest.kt | 67 +++++++++---------- .../application/EssayZipService.kt | 2 +- .../application/FileDocumentService.kt | 7 +- .../application/EssayZipServiceTest.kt | 19 +++--- .../application/FileDocumentServiceTest.kt | 26 +++++++ .../domain/port/out/ApplicationEssayPort.kt | 12 ++-- .../document/port/in/ApplicantFileUseCase.kt | 3 +- .../port/out/ApplicationFormPdfPort.kt | 3 +- 10 files changed, 92 insertions(+), 73 deletions(-) diff --git a/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfAdapter.kt b/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfAdapter.kt index 54f05372..c265f14d 100644 --- a/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfAdapter.kt +++ b/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfAdapter.kt @@ -126,17 +126,15 @@ class ApplicationFormPdfAdapter : ApplicationFormPdfPort { ByteArrayOutputStream().also { document.save(it) }.toByteArray() } - override fun renderEssay(form: ApplicationForm, introduction: Boolean): ByteArray = + // 전체 원서와 같은 본문 배치를 사용하고 서식 3만 남긴다. + override fun renderEssay(form: ApplicationForm): ByteArray = Loader.loadPDF(templateOf(form)).use { document -> val page = document.pages[2] document.pages.filterNot { it == page }.forEach(document::removePage) document.essays( page, PDType0Font.load(document, fontFile.inputStream()), - form.copy( - introduction = form.introduction.takeIf { introduction }, - studyPlan = form.studyPlan.takeUnless { introduction }, - ), + form, ReceiptNumber.of(form.applicantId), ) ByteArrayOutputStream().also { document.save(it) }.toByteArray() @@ -297,7 +295,7 @@ private fun PDDocument.essays(page: PDPage, font: PDFont, form: ApplicationForm, // 원본 칸 높이만큼 자리가 남으면 여기서 시작해 다음 장에 잇는다. // 0.01pt 는 자기소개서 칸이 원본 그대로일 때 부동소수 오차로 학업계획서가 넘어가지 않게 봐준다. if (STUDY_PLAN_BOTTOM - y - header + 0.01f < minOf(height(studyPlan), STUDY_PLAN_BOTTOM - STUDY_PLAN_TOP)) { - // 빈 학업계획서 칸 하나 때문에 장을 늘리지 않는다. 자기소개서만 뽑을 때(renderEssay)와 작성 중인 원서다. + // 빈 학업계획서 칸 하나 때문에 장을 늘리지 않는다. if (studyPlan.isEmpty()) return nextPage() } diff --git a/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/LocalExportDocumentAdapter.kt b/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/LocalExportDocumentAdapter.kt index c233de7b..98110f8b 100644 --- a/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/LocalExportDocumentAdapter.kt +++ b/systems/application/application-administration-adapter-out/src/main/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/LocalExportDocumentAdapter.kt @@ -3,7 +3,6 @@ package hs.kr.entrydsm.application.administration.adapterout.document import hs.kr.entrydsm.admin.domain.enum.ErrorCode import hs.kr.entrydsm.admin.domain.exception.AdminDomainException import hs.kr.entrydsm.admin.domain.port.out.AdmissionTicketPort -import hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs import hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPort import hs.kr.entrydsm.admin.domain.port.out.EssayPdfMergePort import hs.kr.entrydsm.application.administration.domain.document.exception.ApplicantNotFoundException @@ -29,8 +28,9 @@ class LocalAdmissionTicketAdapter(private val documents: ApplicantFileUseCase) : @Component @ConditionalOnProperty(name = ["application.administration.enabled"], havingValue = "true") class LocalApplicationEssayAdapter(private val documents: ApplicantFileUseCase) : ApplicationEssayPort { - override fun render(applicantId: Long, examineeNumber: String): ApplicationEssayPdfs = try { - documents.renderApplicationEssay(applicantId).let { ApplicationEssayPdfs(it.first, it.second) } + // 지원자별 통합 서식의 오류를 기존 관리자 오류 코드로 변환한다. + override fun render(applicantId: Long, examineeNumber: String): ByteArray? = try { + documents.renderApplicationEssay(applicantId) } catch (error: Exception) { throw renderFailure(error, ErrorCode.ESSAY_GENERATION_FAILED, listOf(applicantId)) } @@ -39,14 +39,12 @@ class LocalApplicationEssayAdapter(private val documents: ApplicantFileUseCase) @Component @ConditionalOnProperty(name = ["application.administration.enabled"], havingValue = "true") class PdfBoxEssayMergeAdapter : EssayPdfMergePort { - // 원본을 닫아도 글꼴과 리소스가 유지되도록 두 문서 전체를 복사한다. - override fun writeTo(documents: List, output: OutputStream) { + // 원본을 닫아도 글꼴과 리소스가 유지되도록 지원자별 문서 전체를 복사한다. + override fun writeTo(documents: List, output: OutputStream) { PDDocument().use { merged -> val merger = PDFMergerUtility() - documents.forEach { pdfs -> - listOfNotNull(pdfs.introduction, pdfs.studyPlan).forEach { pdf -> - Loader.loadPDF(pdf).use { source -> merger.appendDocument(merged, source) } - } + documents.filterNotNull().forEach { pdf -> + Loader.loadPDF(pdf).use { source -> merger.appendDocument(merged, source) } } merged.save(output) } diff --git a/systems/application/application-administration-adapter-out/src/test/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfTest.kt b/systems/application/application-administration-adapter-out/src/test/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfTest.kt index 9adac78b..cfc7dec2 100644 --- a/systems/application/application-administration-adapter-out/src/test/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfTest.kt +++ b/systems/application/application-administration-adapter-out/src/test/kotlin/hs/kr/entrydsm/application/administration/adapterout/document/ApplicationFormPdfTest.kt @@ -66,19 +66,16 @@ class ApplicationFormPdfTest { val bytes = output.toByteArray() System.getenv("TEST_UNDECLARED_OUTPUTS_DIR")?.let { File(it, "unissued-essays.pdf").writeBytes(bytes) } Loader.loadPDF(bytes).use { document -> - assertEquals(2, document.numberOfPages) - val introduction = pageText(bytes, 1) - val plan = pageText(bytes, 2) - assertTrue(introduction, introduction.contains("저는 어려서부터")) - assertFalse(introduction.contains("입학 후에는")) - assertTrue(plan, plan.contains("입학 후에는")) - assertFalse(plan.contains("저는 어려서부터")) + assertEquals(1, document.numberOfPages) + val text = pageText(bytes, 1) + assertTrue(text, text.contains("저는 어려서부터")) + assertTrue(text, text.contains("입학 후에는")) assertFalse(PDFTextStripper().getText(document).contains("미발급")) } } @Test - fun `PDF 병합은 두 문서와 단독 학업계획서를 포함하고 빈 목록도 처리한다`() { + fun `PDF 병합은 지원자별 통합 문서를 포함하고 빈 목록도 처리한다`() { fun pdf(pages: Int) = PDDocument().use { document -> repeat(pages) { document.addPage(PDPage()) } ByteArrayOutputStream().also { document.save(it) }.toByteArray() @@ -86,20 +83,20 @@ class ApplicationFormPdfTest { val merger = PdfBoxEssayMergeAdapter() val output = ByteArrayOutputStream() merger.writeTo(listOf( - hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs(pdf(1), pdf(2)), - hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs(null, pdf(4)), - hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs(null, null), + pdf(3), + pdf(4), + null, ), output) Loader.loadPDF(output.toByteArray()).use { assertEquals(7, it.numberOfPages) } val empty = ByteArrayOutputStream() merger.writeTo(emptyList(), empty) Loader.loadPDF(empty.toByteArray()).use { assertEquals(0, it.numberOfPages) } org.junit.Assert.assertThrows(Exception::class.java) { - merger.writeTo(listOf(hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs("broken pdf".toByteArray(), null)), + merger.writeTo(listOf("broken pdf".toByteArray()), ByteArrayOutputStream()) } org.junit.Assert.assertThrows(IllegalStateException::class.java) { - merger.writeTo(listOf(hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs(pdf(1), null)), + merger.writeTo(listOf(pdf(1)), object : java.io.OutputStream() { override fun write(value: Int) { error("storage failed") } }) } } @@ -326,9 +323,12 @@ class ApplicationFormPdfTest { val shortStudyPlan = adapter.render(form().copy(introduction = essay.take(1200)), photo = null) assertA4Pages(shortStudyPlan, FORM_COUNT) assertTrue(pageText(shortStudyPlan, 3).contains("입학 후에는")) - // 자기소개서만 뽑을 때는 빈 학업계획서 칸 때문에 장을 늘리지 않는다. 학업계획서는 원본 칸 자리에서 다음 장으로 잇는다. - assertA4Pages(adapter.renderEssay(form().copy(introduction = essay), introduction = true), 1) - assertA4Pages(adapter.renderEssay(form().copy(studyPlan = essay), introduction = false), 2) + val combined = adapter.renderEssay(form().copy(introduction = essay, studyPlan = essay)) + assertA4Pages(combined, 2) + val combinedBody = stamped(combined, 1).filter { it.yDirAdj > 278.16f } + stamped(combined, 2) + assertEquals(2 * essay.count { !it.isWhitespace() }, combinedBody.size) + combinedBody.forEach { assertEquals(10f, it.fontSizeInPt, 0.01f) } + } @Test @@ -389,29 +389,24 @@ class ApplicationFormPdfTest { } @Test - fun `자기소개서와 학업계획서를 서식 3 한 장씩 분리한다`() { - val introduction = adapter.renderEssay(form(), introduction = true) - val studyPlan = adapter.renderEssay(form(), introduction = false) - - assertA4Pages(introduction, 1) - assertTrue(pageText(introduction, 1).contains("저는 어려서부터")) - assertTrue(!pageText(introduction, 1).contains("입학 후에는")) - assertA4Pages(studyPlan, 1) - assertTrue(pageText(studyPlan, 1).contains("입학 후에는")) - assertTrue(!pageText(studyPlan, 1).contains("저는 어려서부터")) + fun `자기소개서와 학업계획서를 지원자 원서와 같은 서식 3 한 장에 함께 출력한다`() { + val combined = adapter.renderEssay(form()) + assertA4Pages(combined, 1) + assertTrue(pageText(combined, 1).contains("저는 어려서부터")) + assertTrue(pageText(combined, 1).contains("입학 후에는")) + val full = adapter.render(form(), photo = null) + assertEquals(stamped(full, 3).map { Triple(it.unicode, it.xDirAdj, it.yDirAdj) }, + stamped(combined, 1).map { Triple(it.unicode, it.xDirAdj, it.yDirAdj) }) } @Test - fun `검정고시 원서의 자기소개서와 학업계획서는 검정고시 서식 3 에 찍고 출신 중학교는 찍지 않는다`() { - val introduction = adapter.renderEssay(gedForm(), introduction = true) - val studyPlan = adapter.renderEssay(gedForm(), introduction = false) - - assertA4Pages(introduction, 1) - assertTrue(pageText(introduction, 1).contains("저는 어려서부터")) - assertFalse(pageText(introduction, 1).contains("서귀포")) - assertA4Pages(studyPlan, 1) - assertTrue(pageText(studyPlan, 1).contains("입학 후에는")) - assertFalse(pageText(studyPlan, 1).contains("서귀포")) + fun `검정고시 통합 문서는 검정고시 서식 3 에 두 본문을 찍고 출신 중학교는 찍지 않는다`() { + val combined = adapter.renderEssay(gedForm()) + assertA4Pages(combined, 1) + val text = pageText(combined, 1) + assertTrue(text.contains("저는 어려서부터")) + assertTrue(text.contains("입학 후에는")) + assertFalse(text.contains("서귀포")) } /** 칸 안에 기준선이 있는 원서 글자가 [count] 개이고, 모두 칸 좌우 안에 있다. 칸 아래로 넘친 글자는 수에서 빠진다. */ diff --git a/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipService.kt b/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipService.kt index 30d35f0f..89c5df67 100644 --- a/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipService.kt +++ b/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipService.kt @@ -17,7 +17,7 @@ class EssayPdfService( private val applicationEssayPort: ApplicationEssayPort, private val essayPdfMergePort: EssayPdfMergePort, ) : DownloadEssaysUseCase { - // 실제 1차 합격자의 두 서류를 수험번호 발급 여부와 무관하게 합친다. + // 실제 1차 합격자의 통합 서식을 수험번호 발급 여부와 무관하게 합친다. override fun writeTo(output: OutputStream): Int { val targets = applicantRepository.findAll(ApplicantFilter(statuses = setOf(ApplicantStatus.FIRST_PASS))) .sortedWith(compareBy { it.examineeNumber.isNullOrBlank() }.thenBy { it.id }) diff --git a/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentService.kt b/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentService.kt index 5a44a88e..5fc43f59 100644 --- a/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentService.kt +++ b/systems/application/application-administration-application/src/main/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentService.kt @@ -89,11 +89,12 @@ class FileDocumentService( return ticketStage("sheet_render", null) { admissionTicketSheetPort.render(prepared) } } - override fun renderApplicationEssay(applicantId: Long): Pair { + // 지원자 원서와 같은 서식에 두 본문을 함께 출력한다. + override fun renderApplicationEssay(applicantId: Long): ByteArray? { val applicant = applicantPort.findById(applicantId) ?: throw ApplicantNotFoundException(applicantId) val form = applicantPort.findApplicationForm(applicant.userId) ?: throw ApplicantNotFoundException(applicantId) - return form.introduction?.takeIf(String::isNotBlank)?.let { applicationFormPdfPort.renderEssay(form, true) } to - form.studyPlan?.takeIf(String::isNotBlank)?.let { applicationFormPdfPort.renderEssay(form, false) } + if (form.introduction.isNullOrBlank() && form.studyPlan.isNullOrBlank()) return null + return applicationFormPdfPort.renderEssay(form) } private fun ticket(applicantId: Long, applicant: Applicant, examineeNumber: String?): AdmissionTicket { diff --git a/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipServiceTest.kt b/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipServiceTest.kt index 29e0e059..6f9a4c90 100644 --- a/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipServiceTest.kt +++ b/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/EssayZipServiceTest.kt @@ -4,7 +4,6 @@ import hs.kr.entrydsm.admin.domain.enum.ApplicantStatus import hs.kr.entrydsm.admin.domain.model.Applicant import hs.kr.entrydsm.admin.domain.model.ApplicantFilter import hs.kr.entrydsm.admin.domain.port.out.ApplicantRepository -import hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPdfs import hs.kr.entrydsm.admin.domain.port.out.ApplicationEssayPort import hs.kr.entrydsm.admin.domain.port.out.EssayPdfMergePort import java.io.ByteArrayOutputStream @@ -15,7 +14,7 @@ import org.junit.Test class EssayZipServiceTest { @Test - fun `합격자의 두 문서와 단독 학업계획서를 순서대로 병합기에 전달한다`() { + fun `합격자의 통합 문서를 순서대로 병합기에 전달한다`() { val applicants = listOf( Applicant(id = 1, examineeNumber = "11001", status = ApplicantStatus.FIRST_PASS), Applicant(id = 2, examineeNumber = "11002", status = ApplicantStatus.FIRST_PASS), @@ -25,17 +24,17 @@ class EssayZipServiceTest { Applicant(id = 6, status = ApplicantStatus.PENDING), ) val documents = listOf( - ApplicationEssayPdfs(byteArrayOf(1), null), - ApplicationEssayPdfs(byteArrayOf(2), byteArrayOf(3)), - ApplicationEssayPdfs(null, byteArrayOf(4)), - ApplicationEssayPdfs(null, null), + byteArrayOf(1), + byteArrayOf(2, 3), + byteArrayOf(4), + null, ) val requested = mutableListOf>() val output = ByteArrayOutputStream() var merged = false val service = EssayPdfService(repository(applicants), object : ApplicationEssayPort { override fun render(applicantId: Long, examineeNumber: String) = error("batch 호출 필요") - override fun renderBatch(targets: List>): List { + override fun renderBatch(targets: List>): List { requested += targets return documents } @@ -54,7 +53,7 @@ class EssayZipServiceTest { fun `대상이 없으면 빈 문서 목록을 병합기에 전달한다`() { var merged = false val service = EssayPdfService(repository(emptyList()), ApplicationEssayPort { _, _ -> error("호출되면 안 됨") }, - EssayPdfMergePort { documents, _ -> assertEquals(emptyList(), documents); merged = true }) + EssayPdfMergePort { documents, _ -> assertEquals(emptyList(), documents); merged = true }) assertEquals(0, service.writeTo(ByteArrayOutputStream())) assertEquals(true, merged) } @@ -72,7 +71,7 @@ class EssayZipServiceTest { ) val service = EssayPdfService(repository(applicants), ApplicationEssayPort { id, value -> requested += id to value - ApplicationEssayPdfs(null, null) + null }, EssayPdfMergePort { documents, _ -> assertEquals(4, documents.size) }) assertEquals(4, service.writeTo(ByteArrayOutputStream())) assertEquals(if (issued) listOf(4L to " 11002 ", 9L to "11001", 3L to "", 7L to "") @@ -84,7 +83,7 @@ class EssayZipServiceTest { fun `병합 실패는 성공 처리하지 않고 원래 예외를 전달한다`() { val failure = IllegalStateException("merge failed") val service = EssayPdfService(repository(listOf(Applicant(id = 1, status = ApplicantStatus.FIRST_PASS))), - ApplicationEssayPort { _, _ -> ApplicationEssayPdfs(null, null) }, EssayPdfMergePort { _, _ -> throw failure }) + ApplicationEssayPort { _, _ -> null }, EssayPdfMergePort { _, _ -> throw failure }) assertSame(failure, assertThrows(IllegalStateException::class.java) { service.writeTo(ByteArrayOutputStream()) }) } diff --git a/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentServiceTest.kt b/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentServiceTest.kt index 8935a743..73e55543 100644 --- a/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentServiceTest.kt +++ b/systems/application/application-administration-application/src/test/kotlin/hs/kr/entrydsm/application/administration/application/FileDocumentServiceTest.kt @@ -41,6 +41,27 @@ import javax.imageio.ImageIO class FileDocumentServiceTest { + @Test + fun `어드민 본문 출력은 두 본문을 함께 전달하고 한쪽만 있어도 출력하며 모두 공백이면 생략한다`() { + for ((introduction, studyPlan) in listOf("자기소개" to "학업계획", "자기소개" to null, null to "학업계획")) { + val form = applicationForm().copy(introduction = introduction, studyPlan = studyPlan) + forms[STUDENT_ID] = form + assertArrayEquals("%PDF-".toByteArray(), service.renderApplicationEssay(APPLICANT_ID)) + assertEquals(form, formPdf.lastForm) + } + forms[STUDENT_ID] = applicationForm().copy(introduction = " \t", studyPlan = "\n") + formPdf.lastForm = null + assertNull(service.renderApplicationEssay(APPLICANT_ID)) + assertNull(formPdf.lastForm) + } + + @Test + fun `어드민 본문 출력은 지원자나 원서가 없으면 기존 조회 오류를 전달한다`() { + assertThrows(ApplicantNotFoundException::class.java) { service.renderApplicationEssay(999L) } + forms.clear() + assertThrows(ApplicantNotFoundException::class.java) { service.renderApplicationEssay(APPLICANT_ID) } + } + @Test fun `수험표 단계 로그는 성공과 실패 위치를 기록하고 사진 키와 예외 메시지를 숨긴다`() { val logger = org.slf4j.LoggerFactory.getLogger(FileDocumentService::class.java) as ch.qos.logback.classic.Logger @@ -625,6 +646,11 @@ class FileDocumentServiceTest { return "%PDF-".toByteArray() } + override fun renderEssay(form: ApplicationForm): ByteArray { + lastForm = form + return "%PDF-".toByteArray() + } + override fun isRegistrationTemplate(template: ByteArray) = template.decodeToString().startsWith("%PDF-") override fun renderRegistrationDocument(form: ApplicationForm, template: ByteArray): ByteArray { diff --git a/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/admin/domain/port/out/ApplicationEssayPort.kt b/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/admin/domain/port/out/ApplicationEssayPort.kt index c80c24ad..473db75e 100644 --- a/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/admin/domain/port/out/ApplicationEssayPort.kt +++ b/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/admin/domain/port/out/ApplicationEssayPort.kt @@ -2,16 +2,16 @@ package hs.kr.entrydsm.admin.domain.port.out import java.io.OutputStream -data class ApplicationEssayPdfs(val introduction: ByteArray?, val studyPlan: ByteArray?) - fun interface ApplicationEssayPort { - fun render(applicantId: Long, examineeNumber: String): ApplicationEssayPdfs + // 두 본문이 모두 비어 있으면 null을 반환한다. + fun render(applicantId: Long, examineeNumber: String): ByteArray? - fun renderBatch(targets: List>): List = + // 입력한 지원자 순서대로 통합 서식을 만든다. + fun renderBatch(targets: List>): List = targets.map { (applicantId, examineeNumber) -> render(applicantId, examineeNumber) } } fun interface EssayPdfMergePort { - // 지원자별 자기소개서와 학업계획서를 받은 순서대로 합친다. - fun writeTo(documents: List, output: OutputStream) + // 두 본문을 함께 담은 지원자별 서식 3을 받은 순서대로 합친다. + fun writeTo(documents: List, output: OutputStream) } diff --git a/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/in/ApplicantFileUseCase.kt b/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/in/ApplicantFileUseCase.kt index 4d7b9b28..719d5590 100644 --- a/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/in/ApplicantFileUseCase.kt +++ b/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/in/ApplicantFileUseCase.kt @@ -33,6 +33,7 @@ interface ApplicantFileUseCase { */ fun renderAdmissionTickets(tickets: List>): ByteArray - fun renderApplicationEssay(applicantId: Long): Pair = + /** 두 본문을 함께 담은 서식 3을 반환한다. 모두 비어 있으면 출력하지 않는다. */ + fun renderApplicationEssay(applicantId: Long): ByteArray? = throw UnsupportedOperationException() } diff --git a/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/out/ApplicationFormPdfPort.kt b/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/out/ApplicationFormPdfPort.kt index c052b64b..09296c07 100644 --- a/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/out/ApplicationFormPdfPort.kt +++ b/systems/application/application-administration-domain/src/main/kotlin/hs/kr/entrydsm/application/administration/domain/document/port/out/ApplicationFormPdfPort.kt @@ -10,7 +10,8 @@ interface ApplicationFormPdfPort { /** @param photo 원서 주인이 올린 증명사진 원본. 없거나 읽지 못하는 형식이면 사진 칸을 비운다. */ fun render(form: ApplicationForm, photo: ByteArray?): ByteArray - fun renderEssay(form: ApplicationForm, introduction: Boolean): ByteArray = + /** 자기소개서와 학업계획서를 원서와 같은 서식 3에 함께 찍는다. */ + fun renderEssay(form: ApplicationForm): ByteArray = throw UnsupportedOperationException() /** 최종 합격자 등록 서류 원본([template]) 첫 장 입학 동의서에 지원자 정보를 찍는다. 나머지 장은 그대로다. */