From efb0ef54e0db028bca527753128e3d5e3e5dad09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Tue, 26 Aug 2025 19:05:14 +0900 Subject: [PATCH 01/21] refactor ( #28 ) : ktlint --- .../pdf/config/ConverterPropertiesCreator.kt | 1 - .../global/document/pdf/config/Font.kt | 13 +- .../global/document/pdf/config/PdfConfig.kt | 16 +- .../pdf/data/IntroductionPdfConverter.kt | 28 +- .../global/document/pdf/data/PdfData.kt | 11 +- .../document/pdf/data/PdfDataConverter.kt | 159 ++++++---- .../document/pdf/facade/PdfDocumentFacade.kt | 1 - .../pdf/generator/ApplicationPdfGenerator.kt | 58 ++-- .../pdf/generator/IntroductionPdfGenerator.kt | 12 +- .../document/pdf/generator/PdfProcessor.kt | 1 - .../pdf/generator/TemplateProcessor.kt | 8 +- .../pdf/presentation/PdfTestController.kt | 4 +- .../PrintAdmissionTicketGenerator.kt | 116 +++++--- .../generator/PrintApplicantCodesGenerator.kt | 22 +- .../PrintApplicationCheckListGenerator.kt | 279 ++++++++++-------- .../PrintApplicationInfoGenerator.kt | 39 ++- .../global/excel/model/ApplicationInfo.kt | 1 - .../excel/presentation/ExcelTestController.kt | 5 +- 18 files changed, 472 insertions(+), 302 deletions(-) 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 baff0308..2503d313 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 @@ -8,7 +8,6 @@ import java.io.IOException @Component class ConverterPropertiesCreator { - private var fontPath: String = "/fonts/" fun createConverterProperties(): ConverterProperties { diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/Font.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/Font.kt index 85c5dde7..3362a036 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/Font.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/Font.kt @@ -1,10 +1,11 @@ package hs.kr.entrydsm.application.global.document.pdf.config object Font { - val fonts = listOf( - "KoPubWorld Dotum Light.ttf", - "KoPubWorld Dotum Bold.ttf", - "KoPubWorld Dotum Medium.ttf", - "DejaVuSans.ttf" - ) + val fonts = + listOf( + "KoPubWorld Dotum Light.ttf", + "KoPubWorld Dotum Bold.ttf", + "KoPubWorld Dotum Medium.ttf", + "DejaVuSans.ttf", + ) } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt index 236965cb..96561abf 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt @@ -8,16 +8,16 @@ import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver @Configuration class PdfConfig { - @Bean fun templateEngine(): TemplateEngine { - val templateResolver = ClassLoaderTemplateResolver().apply { - prefix = "classpath:/templates/" - suffix = ".html" - templateMode = TemplateMode.HTML - } - return TemplateEngine().apply { - this.setTemplateResolver(templateResolver) + val templateResolver = + ClassLoaderTemplateResolver().apply { + prefix = "classpath:/templates/" + suffix = ".html" + templateMode = TemplateMode.HTML + } + return TemplateEngine().apply { + this.setTemplateResolver(templateResolver) } } } 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 ab9e0a77..a00104cb 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,7 +5,6 @@ import java.util.HashMap @Component class IntroductionPdfConverter { - fun execute(application: Any): PdfData { val values: MutableMap = HashMap() setIntroduction(application, values) @@ -16,25 +15,37 @@ class IntroductionPdfConverter { return PdfData(values) } - private fun setPersonalInfo(application: Any, values: MutableMap) { + private fun setPersonalInfo( + application: Any, + values: MutableMap, + ) { // TODO: Application 도메인 모델 연동 필요 values["userName"] = "더미사용자명" values["address"] = "더미주소" values["detailAddress"] = "더미상세주소" } - - private fun setSchoolInfo(application: Any, values: MutableMap) { + + private fun setSchoolInfo( + application: Any, + values: MutableMap, + ) { // TODO: 교육상태 및 졸업정보 연동 필요 // 현재는 더미 데이터로 설정 values["schoolName"] = "더미중학교" } - private fun setReceiptCode(application: Any, values: MutableMap) { + private fun setReceiptCode( + application: Any, + values: MutableMap, + ) { // TODO: Application 도메인 모델 연동 필요 values["receiptCode"] = "더미수험번호" } - private fun setPhoneNumber(application: Any, values: MutableMap) { + private fun setPhoneNumber( + application: Any, + values: MutableMap, + ) { values["applicantTel"] = toFormattedPhoneNumber("01012345678") } @@ -45,7 +56,10 @@ class IntroductionPdfConverter { return phoneNumber.replace("(\\d{2,3})(\\d{3,4})(\\d{4})".toRegex(), "$1-$2-$3") } - private fun setIntroduction(application: Any, values: MutableMap) { + private fun setIntroduction( + application: Any, + values: MutableMap, + ) { values["selfIntroduction"] = "더미 자기소개 내용" values["studyPlan"] = "더미 학업계획 내용" values["newLineChar"] = "\n" 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 bd9fd6f8..02c70045 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,13 +1,16 @@ package hs.kr.entrydsm.application.global.document.pdf.data data class PdfData( - private val values: MutableMap + private val values: MutableMap, ) { fun toMap(): MutableMap = values - + fun getValue(key: String): Any? = values[key] - - fun setValue(key: String, value: Any) { + + fun setValue( + key: String, + value: Any, + ) { values[key] = value } } 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 c23316c0..58c7949a 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 @@ -4,12 +4,13 @@ import org.springframework.stereotype.Component import java.time.LocalDate import java.time.LocalDateTime import java.time.YearMonth -import java.util.* @Component class PdfDataConverter { - - fun applicationToInfo(application: Any, score: Any): PdfData { + fun applicationToInfo( + application: Any, + score: Any, + ): PdfData { val values: MutableMap = HashMap() setReceiptCode(application, values) setEntranceYear(values) @@ -41,7 +42,10 @@ class PdfDataConverter { return PdfData(values) } - private fun setReceiptCode(application: Any, values: MutableMap) { + private fun setReceiptCode( + application: Any, + values: MutableMap, + ) { // TODO: Application 도메인 모델 연동 필요 values["receiptCode"] = "더미데이터" } @@ -51,12 +55,18 @@ class PdfDataConverter { values["entranceYear"] = entranceYear.toString() } - private fun setVeteransNumber(application: Any, values: MutableMap) { + private fun setVeteransNumber( + application: Any, + values: MutableMap, + ) { // TODO: Application 도메인 모델 연동 필요 values["veteransNumber"] = "" } - private fun setPersonalInfo(application: Any, values: MutableMap) { + private fun setPersonalInfo( + application: Any, + values: MutableMap, + ) { // TODO: Application 도메인 모델 연동 필요 values["userName"] = setBlankIfNull("더미사용자명") values["isMale"] = toBallotBox(true) @@ -70,7 +80,10 @@ class PdfDataConverter { values["applicationRemark"] = "해당없음" } - private fun setAttendanceAndVolunteer(application: Any, values: MutableMap) { + private fun setAttendanceAndVolunteer( + application: Any, + values: MutableMap, + ) { // TODO: ApplicationCase 도메인 모델 연동 필요 values["absenceDayCount"] = 0 values["latenessCount"] = 0 @@ -79,11 +92,17 @@ class PdfDataConverter { values["volunteerTime"] = 0 } - private fun setGenderInfo(application: Any, values: MutableMap) { + private fun setGenderInfo( + application: Any, + values: MutableMap, + ) { values["gender"] = setBlankIfNull("남") } - private fun setSchoolInfo(application: Any, values: MutableMap) { + private fun setSchoolInfo( + application: Any, + values: MutableMap, + ) { // TODO: 졸업정보 및 학교정보 연동 필요 values["schoolCode"] = "더미학교코드" values["schoolRegion"] = "더미지역" @@ -92,14 +111,20 @@ class PdfDataConverter { values["schoolName"] = "더미중학교" } - private fun setPhoneNumber(application: Any, values: MutableMap) { + private fun setPhoneNumber( + application: Any, + values: MutableMap, + ) { values["applicantTel"] = toFormattedPhoneNumber("01012345678") values["parentTel"] = toFormattedPhoneNumber("01087654321") } - private fun setGraduationClassification(application: Any, values: MutableMap) { + private fun setGraduationClassification( + application: Any, + values: MutableMap, + ) { values.putAll(emptyGraduationClassification()) - + // TODO: 졸업정보 연동 필요 val yearMonth = YearMonth.now() values["graduateYear"] = yearMonth.year.toString() @@ -107,39 +132,50 @@ class PdfDataConverter { values["educationalStatus"] = "${yearMonth.year}년 ${yearMonth.monthValue}월 중학교 졸업" } - private fun setUserType(application: Any, values: MutableMap) { - 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 - ) + private fun setUserType( + application: Any, + values: MutableMap, + ) { + 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, + ) list.forEach { (key, value) -> values[key] = toBallotBox(value) } } - private fun setExtraScore(application: Any, values: MutableMap) { + private fun setExtraScore( + application: Any, + values: MutableMap, + ) { // TODO: ApplicationCase 연동 필요 values["hasCompetitionPrize"] = toCircleBallotbox(false) values["hasCertificate"] = toCircleBallotbox(false) } - private fun setGradeScore(application: Any, score: Any, values: MutableMap) { + private fun setGradeScore( + application: Any, + score: Any, + values: MutableMap, + ) { // TODO: Score 도메인 모델 연동 필요 with(values) { put("conversionScore1st", "80.0") @@ -152,21 +188,25 @@ class PdfDataConverter { } } - private fun setAllSubjectScores(application: Any, values: MutableMap) { + private fun setAllSubjectScores( + application: Any, + values: MutableMap, + ) { // TODO: ApplicationCase 연동 필요 - 일반졸업 케이스로 더미 데이터 val subjects = listOf("국어", "사회", "역사", "수학", "과학", "영어", "기술가정") subjects.forEach { subject -> - val subjectPrefix = when (subject) { - "국어" -> "korean" - "사회" -> "social" - "역사" -> "history" - "수학" -> "math" - "과학" -> "science" - "영어" -> "english" - "기술가정" -> "techAndHome" - else -> subject.lowercase() - } + val subjectPrefix = + when (subject) { + "국어" -> "korean" + "사회" -> "social" + "역사" -> "history" + "수학" -> "math" + "과학" -> "science" + "영어" -> "english" + "기술가정" -> "techAndHome" + else -> subject.lowercase() + } with(values) { put("applicationCase", "기술∙가정") @@ -187,31 +227,46 @@ class PdfDataConverter { } } - private fun setIntroduction(application: Any, values: MutableMap) { + private fun setIntroduction( + application: Any, + values: MutableMap, + ) { values["selfIntroduction"] = setBlankIfNull("더미 자기소개") values["studyPlan"] = setBlankIfNull("더미 학업계획") values["newLineChar"] = "\n" } - private fun setTeacherInfo(application: Any, values: MutableMap) { + private fun setTeacherInfo( + application: Any, + values: MutableMap, + ) { // TODO: 졸업정보 연동 필요 values["teacherName"] = "더미선생님" values["teacherTel"] = toFormattedPhoneNumber("0421234567") } - private fun setParentInfo(application: Any, values: MutableMap) { + private fun setParentInfo( + application: Any, + values: MutableMap, + ) { values["parentName"] = "더미학부모" values["parentRelation"] = "부" } - private fun setRecommendations(application: Any, values: MutableMap) { + private fun setRecommendations( + application: Any, + values: MutableMap, + ) { values["isDaejeonAndMeister"] = markIfTrue(false) values["isDaejeonAndSocialMerit"] = markIfTrue(false) values["isNotDaejeonAndMeister"] = markIfTrue(false) values["isNotDaejeonAndSocialMerit"] = markIfTrue(false) } - private fun setBase64Image(application: Any, values: MutableMap) { + private fun setBase64Image( + application: Any, + values: MutableMap, + ) { // TODO: 이미지 파일 연동 필요 values["base64Image"] = "" } @@ -225,7 +280,7 @@ class PdfDataConverter { "schoolCode" to "", "schoolClass" to "", "schoolTel" to "", - "schoolName" to "" + "schoolName" to "", ) } @@ -236,7 +291,7 @@ class PdfDataConverter { "graduateYear" to "20__", "graduateMonth" to "__", "prospectiveGraduateYear" to "20__", - "prospectiveGraduateMonth" to "__" + "prospectiveGraduateMonth" to "__", ) } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/facade/PdfDocumentFacade.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/facade/PdfDocumentFacade.kt index 048c7cf4..d260c02c 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/facade/PdfDocumentFacade.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/facade/PdfDocumentFacade.kt @@ -8,7 +8,6 @@ import java.io.ByteArrayOutputStream @Component class PdfDocumentFacade { - fun getPdfDocument(pdfStream: ByteArrayOutputStream): PdfDocument? { return try { val inputStream = ByteArrayInputStream(pdfStream.toByteArray()) 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 bc72993c..96f42ee3 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 @@ -1,7 +1,6 @@ package hs.kr.entrydsm.application.global.document.pdf.generator import com.itextpdf.kernel.pdf.PdfDocument -import com.itextpdf.kernel.pdf.PdfReader import com.itextpdf.kernel.pdf.PdfWriter import com.itextpdf.kernel.utils.PdfMerger import com.itextpdf.layout.Document @@ -9,36 +8,39 @@ 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 org.springframework.stereotype.Component -import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.io.IOException -import java.io.InputStream -import java.util.* +import java.util.LinkedList @Component class ApplicationPdfGenerator( private val pdfProcessor: PdfProcessor, private val pdfDataConverter: PdfDataConverter, private val templateProcessor: TemplateProcessor, - private val pdfDocumentFacade: PdfDocumentFacade + private val pdfDocumentFacade: PdfDocumentFacade, ) { - - fun generate(application: Any, score: Any): ByteArray { + fun generate( + application: Any, + score: Any, + ): ByteArray { return generateApplicationPdf(application, score) } - private fun generateApplicationPdf(application: Any, score: Any): ByteArray { + private fun generateApplicationPdf( + application: Any, + score: Any, + ): ByteArray { val data = pdfDataConverter.applicationToInfo(application, score) val templates = getTemplateFileNames(application) - val outStream = templates.stream() - .map { template -> - templateProcessor.convertTemplateIntoHtmlString(template, data.toMap()) - } - .map { html -> - pdfProcessor.convertHtmlToPdf(html) - } - .toArray { size -> arrayOfNulls(size) } + val outStream = + templates.stream() + .map { template -> + templateProcessor.convertTemplateIntoHtmlString(template, data.toMap()) + } + .map { html -> + pdfProcessor.convertHtmlToPdf(html) + } + .toArray { size -> arrayOfNulls(size) } val outputStream = ByteArrayOutputStream() val mergedDocument = PdfDocument(PdfWriter(outputStream)) @@ -55,7 +57,10 @@ class ApplicationPdfGenerator( return outputStream.toByteArray() } - private fun mergeDocument(merger: PdfMerger, document: PdfDocument?) { + private fun mergeDocument( + merger: PdfMerger, + document: PdfDocument?, + ) { if (document != null) { merger.merge(document, 1, document.numberOfPages) document.close() @@ -63,15 +68,16 @@ class ApplicationPdfGenerator( } private fun getTemplateFileNames(application: Any): MutableList { - val result = LinkedList( - listOf( - TemplateFileName.APPLICATION_FOR_ADMISSION, - TemplateFileName.PRIVACY_AGREEMENT, - TemplateFileName.INTRODUCTION, - TemplateFileName.NON_SMOKING, - TemplateFileName.SMOKING_EXAMINE + val result = + LinkedList( + listOf( + TemplateFileName.APPLICATION_FOR_ADMISSION, + TemplateFileName.PRIVACY_AGREEMENT, + TemplateFileName.INTRODUCTION, + TemplateFileName.NON_SMOKING, + TemplateFileName.SMOKING_EXAMINE, + ), ) - ) // TODO: 조건부 추천서 추가 로직 // if (!application.isQualificationExam() && !application.isCommonApplicationType()) { 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 73604cd9..3279f79b 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 @@ -8,15 +8,15 @@ import org.springframework.stereotype.Component class IntroductionPdfGenerator( private val pdfProcessor: PdfProcessor, private val introductionPdfConverter: IntroductionPdfConverter, - private val templateProcessor: TemplateProcessor + private val templateProcessor: TemplateProcessor, ) { - fun generate(application: Any): ByteArray { val data = introductionPdfConverter.execute(application) - val html = templateProcessor.convertTemplateIntoHtmlString( - TemplateFileName.ADMIN_INTRODUCTION, - data.toMap() - ) + val html = + templateProcessor.convertTemplateIntoHtmlString( + TemplateFileName.ADMIN_INTRODUCTION, + data.toMap(), + ) val pdfStream = pdfProcessor.convertHtmlToPdf(html) return pdfStream.toByteArray() } 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 dac284d0..155d14df 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 @@ -9,7 +9,6 @@ import java.io.ByteArrayOutputStream class PdfProcessor( private val converterPropertiesCreator: ConverterPropertiesCreator, ) { - 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 c30e2cbb..b32c21d3 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 @@ -6,10 +6,12 @@ import org.thymeleaf.context.Context @Component class TemplateProcessor( - private val templateEngine: TemplateEngine + private val templateEngine: TemplateEngine, ) { - - fun convertTemplateIntoHtmlString(template: String?, data: MutableMap?): String { + fun convertTemplateIntoHtmlString( + template: String?, + data: MutableMap?, + ): String { val context = Context() context.setVariables(data) return templateEngine.process(template, context) 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 3199fd80..2f87434c 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 @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/pdf") class PdfTestController( private val applicationPdfGenerator: ApplicationPdfGenerator, - private val introductionPdfGenerator: IntroductionPdfGenerator + private val introductionPdfGenerator: IntroductionPdfGenerator, ) { @GetMapping("/test") fun testPdf(): ResponseEntity { @@ -38,4 +38,4 @@ class PdfTestController( .header("Content-Disposition", "inline; filename=test-introduction.pdf") .body(pdfBytes) } -} \ No newline at end of file +} 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 3a633ff5..f4d8fed4 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,6 +1,12 @@ package hs.kr.entrydsm.application.global.excel.generator -import org.apache.poi.ss.usermodel.* +import jakarta.servlet.http.HttpServletResponse +import org.apache.poi.ss.usermodel.Cell +import org.apache.poi.ss.usermodel.CellStyle +import org.apache.poi.ss.usermodel.CellType +import org.apache.poi.ss.usermodel.Row +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.Workbook import org.apache.poi.ss.util.CellRangeAddress import org.apache.poi.ss.util.CellReference import org.apache.poi.xssf.usermodel.XSSFClientAnchor @@ -11,7 +17,6 @@ import org.springframework.stereotype.Component import java.io.IOException import java.time.LocalDateTime import java.time.format.DateTimeFormatter -import jakarta.servlet.http.HttpServletResponse @Component class PrintAdmissionTicketGenerator { @@ -46,24 +51,25 @@ class PrintAdmissionTicketGenerator { 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 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 "마이스터전형", + ), ) - ) var currentRowIndex = 0 dummyApplications.forEach { dummyApp -> @@ -82,20 +88,31 @@ class PrintAdmissionTicketGenerator { return resource.inputStream.use { XSSFWorkbook(it) } } - fun createStyleMap(sourceWorkbook: Workbook, targetWorkbook: Workbook): Map { + fun createStyleMap( + sourceWorkbook: Workbook, + targetWorkbook: Workbook, + ): Map { val styleCache = mutableMapOf() return (0 until sourceWorkbook.numCellStyles).associate { i -> val sourceStyle = sourceWorkbook.getCellStyleAt(i) - val targetStyle = styleCache.getOrPut(sourceStyle.index) { - val newStyle = targetWorkbook.createCellStyle() - newStyle.cloneStyleFrom(sourceStyle) - newStyle - } + val targetStyle = + styleCache.getOrPut(sourceStyle.index) { + val newStyle = targetWorkbook.createCellStyle() + newStyle.cloneStyleFrom(sourceStyle) + newStyle + } sourceStyle to targetStyle } } - fun copyRows(sourceSheet: Sheet, targetSheet: Sheet, sourceStartRow: Int, sourceEndRow: Int, targetStartRow: Int, styleMap: Map) { + fun copyRows( + sourceSheet: Sheet, + targetSheet: Sheet, + sourceStartRow: Int, + sourceEndRow: Int, + targetStartRow: Int, + styleMap: Map, + ) { for (i in sourceStartRow..sourceEndRow) { val sourceRow = sourceSheet.getRow(i) val targetRow = targetSheet.createRow(targetStartRow + i - sourceStartRow) @@ -105,7 +122,13 @@ class PrintAdmissionTicketGenerator { } } - fun copyRow(sourceSheet: Sheet, targetSheet: Sheet, sourceRow: Row, targetRow: Row, styleMap: Map) { + fun copyRow( + sourceSheet: Sheet, + targetSheet: Sheet, + sourceRow: Row, + targetRow: Row, + styleMap: Map, + ) { targetRow.height = sourceRow.height for (i in 0 until sourceRow.lastCellNum) { @@ -122,18 +145,23 @@ class PrintAdmissionTicketGenerator { for (i in 0 until sourceSheet.numMergedRegions) { val mergedRegion = sourceSheet.getMergedRegion(i) if (mergedRegion.firstRow == sourceRow.rowNum) { - val newMergedRegion = CellRangeAddress( - targetRow.rowNum, - targetRow.rowNum + (mergedRegion.lastRow - mergedRegion.firstRow), - mergedRegion.firstColumn, - mergedRegion.lastColumn - ) + val newMergedRegion = + CellRangeAddress( + targetRow.rowNum, + targetRow.rowNum + (mergedRegion.lastRow - mergedRegion.firstRow), + mergedRegion.firstColumn, + mergedRegion.lastColumn, + ) targetSheet.addMergedRegion(newMergedRegion) } } } - fun copyCell(oldCell: Cell, newCell: Cell, styleMap: Map) { + fun copyCell( + oldCell: Cell, + newCell: Cell, + styleMap: Map, + ) { val newStyle = styleMap[oldCell.cellStyle] if (newStyle != null) { newCell.cellStyle = newStyle @@ -150,7 +178,12 @@ class PrintAdmissionTicketGenerator { } } - fun fillApplicationData(sheet: Sheet, startRowIndex: Int, dummyApp: Map, workbook: Workbook) { + fun fillApplicationData( + sheet: Sheet, + startRowIndex: Int, + dummyApp: Map, + workbook: Workbook, + ) { setValue(sheet, "E4", dummyApp["examCode"].toString()) setValue(sheet, "E6", dummyApp["applicantName"].toString()) setValue(sheet, "E8", dummyApp["schoolName"].toString()) @@ -159,10 +192,13 @@ class PrintAdmissionTicketGenerator { setValue(sheet, "E14", dummyApp["receiptCode"].toString()) } - fun copyDummyImage(targetSheet: Sheet, targetRowIndex: Int) { + fun copyDummyImage( + targetSheet: Sheet, + targetRowIndex: Int, + ) { // 더미 이미지 데이터 (빈 바이트 배열) val dummyImageBytes = ByteArray(100) { 0 } - + try { val workbook = targetSheet.workbook val pictureId = workbook.addPicture(dummyImageBytes, Workbook.PICTURE_TYPE_PNG) @@ -179,7 +215,11 @@ class PrintAdmissionTicketGenerator { } } - fun setValue(sheet: Sheet, position: String, value: String) { + fun setValue( + sheet: Sheet, + position: String, + value: String, + ) { val ref = CellReference(position) val r = sheet.getRow(ref.row) if (r != null) { 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 4b638deb..371dd289 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 @@ -14,14 +14,15 @@ class PrintApplicantCodesGenerator { val applicantCode = ApplicantCode() val sheet = applicantCode.getSheet() applicantCode.format() - + // 더미 데이터로 테스트 - val dummyData = listOf( - Triple("DUMMY001", 1001L, "홍길동"), - Triple("DUMMY002", 1002L, "김철수"), - Triple("DUMMY003", 1003L, "이영희") - ) - + val dummyData = + listOf( + Triple("DUMMY001", 1001L, "홍길동"), + Triple("DUMMY002", 1002L, "김철수"), + Triple("DUMMY003", 1003L, "이영희"), + ) + dummyData.forEachIndexed { index, (examCode, receiptCode, name) -> val row = sheet.createRow(index + 1) insertCode(row, examCode, receiptCode, name) @@ -40,7 +41,12 @@ class PrintApplicantCodesGenerator { } } - private fun insertCode(row: Row, examCode: String, receiptCode: Long, name: String) { + private fun insertCode( + row: Row, + examCode: String, + receiptCode: Long, + name: String, + ) { row.createCell(0).setCellValue(examCode) row.createCell(1).setCellValue(receiptCode.toString()) row.createCell(2).setCellValue(name) 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 2530180e..55dbe3a8 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 @@ -2,7 +2,11 @@ package hs.kr.entrydsm.application.global.excel.generator import jakarta.servlet.ServletOutputStream import jakarta.servlet.http.HttpServletResponse -import org.apache.poi.ss.usermodel.* +import org.apache.poi.ss.usermodel.BorderStyle +import org.apache.poi.ss.usermodel.Cell +import org.apache.poi.ss.usermodel.Row +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.Workbook import org.apache.poi.ss.util.CellRangeAddress import org.apache.poi.ss.util.RegionUtil import org.apache.poi.xssf.usermodel.XSSFWorkbook @@ -13,7 +17,6 @@ import java.time.format.DateTimeFormatter @Component class PrintApplicationCheckListGenerator { - private val workbook: Workbook = XSSFWorkbook() private val sheet: Sheet = workbook.createSheet("application Check List") @@ -22,12 +25,13 @@ class PrintApplicationCheckListGenerator { var dh = 0 try { // 더미 데이터 - val dummyApplications = listOf( - createDummyApplication(1001L, "홍길동", "더미고등학교"), - createDummyApplication(1002L, "김철수", "테스트고등학교"), - createDummyApplication(1003L, "이영희", "샘플고등학교") - ) - + val dummyApplications = + listOf( + createDummyApplication(1001L, "홍길동", "더미고등학교"), + createDummyApplication(1002L, "김철수", "테스트고등학교"), + createDummyApplication(1003L, "이영희", "샘플고등학교"), + ) + dummyApplications.forEach { dummyData -> formatSheet(dh) insertDataIntoSheet(dummyData, dh) @@ -35,10 +39,19 @@ class PrintApplicationCheckListGenerator { } 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) } @@ -60,7 +73,11 @@ class PrintApplicationCheckListGenerator { } } - private fun createDummyApplication(receiptCode: Long, name: String, schoolName: String): Map { + private fun createDummyApplication( + receiptCode: Long, + name: String, + schoolName: String, + ): Map { return mapOf( "receiptCode" to receiptCode, "applicationType" to "일반전형", @@ -76,7 +93,7 @@ class PrintApplicationCheckListGenerator { "graduateYear" to "2024", "studentNumber" to "30315", "phoneNumber" to "010-1234-5678", - "parentPhoneNumber" to "010-9876-5432" + "parentPhoneNumber" to "010-9876-5432", ) } @@ -89,15 +106,16 @@ class PrintApplicationCheckListGenerator { } private fun Sheet.mergeRegions(rowOffset: Int) { - val mergedRegions = arrayOf( - CellRangeAddress(1 + rowOffset, 1 + rowOffset, 3, 5), - CellRangeAddress(3 + rowOffset, 3 + rowOffset, 2, 3), - CellRangeAddress(4 + rowOffset, 4 + rowOffset, 2, 3), - CellRangeAddress(5 + rowOffset, 5 + rowOffset, 2, 3), - CellRangeAddress(4 + rowOffset, 4 + rowOffset, 6, 7), - CellRangeAddress(5 + rowOffset, 5 + rowOffset, 6, 7), - CellRangeAddress(3 + rowOffset, 3 + rowOffset, 6, 7) - ) + val mergedRegions = + arrayOf( + CellRangeAddress(1 + rowOffset, 1 + rowOffset, 3, 5), + CellRangeAddress(3 + rowOffset, 3 + rowOffset, 2, 3), + CellRangeAddress(4 + rowOffset, 4 + rowOffset, 2, 3), + CellRangeAddress(5 + rowOffset, 5 + rowOffset, 2, 3), + CellRangeAddress(4 + rowOffset, 4 + rowOffset, 6, 7), + CellRangeAddress(5 + rowOffset, 5 + rowOffset, 6, 7), + CellRangeAddress(3 + rowOffset, 3 + rowOffset, 6, 7), + ) mergedRegions.forEach { if (!isRegionMerged(it)) { addMergedRegion(it) @@ -106,116 +124,131 @@ class PrintApplicationCheckListGenerator { } private fun Sheet.isRegionMerged(region: CellRangeAddress): Boolean { - return mergedRegions.any { it.firstRow == region.firstRow && it.lastRow == region.lastRow && it.firstColumn == region.firstColumn && it.lastColumn == region.lastColumn } + return mergedRegions.any { + it.firstRow == region.firstRow && + it.lastRow == region.lastRow && + it.firstColumn == region.firstColumn && + it.lastColumn == region.lastColumn + } } private fun Sheet.applyBorderStyles(dh: Int) { - val borderRegionsDashedBottom = arrayOf( - intArrayOf(3 + dh, 3 + dh, 1, 1), - intArrayOf(4 + dh, 4 + dh, 1, 3), - intArrayOf(5 + dh, 5 + dh, 1, 3), - intArrayOf(3 + dh, 3 + dh, 5, 7), - intArrayOf(4 + dh, 4 + dh, 5, 7), - intArrayOf(5 + dh, 5 + dh, 5, 7), - intArrayOf(7 + dh, 7 + dh, 1, 7), - intArrayOf(11 + dh, 11 + dh, 1, 7), - intArrayOf(12 + dh, 12 + dh, 1, 7), - intArrayOf(13 + dh, 13 + dh, 1, 7), - intArrayOf(14 + dh, 14 + dh, 1, 7), - intArrayOf(15 + dh, 15 + dh, 1, 7), - intArrayOf(16 + dh, 16 + dh, 1, 7), - ) + val borderRegionsDashedBottom = + arrayOf( + intArrayOf(3 + dh, 3 + dh, 1, 1), + intArrayOf(4 + dh, 4 + dh, 1, 3), + intArrayOf(5 + dh, 5 + dh, 1, 3), + intArrayOf(3 + dh, 3 + dh, 5, 7), + intArrayOf(4 + dh, 4 + dh, 5, 7), + intArrayOf(5 + dh, 5 + dh, 5, 7), + intArrayOf(7 + dh, 7 + dh, 1, 7), + intArrayOf(11 + dh, 11 + dh, 1, 7), + intArrayOf(12 + dh, 12 + dh, 1, 7), + intArrayOf(13 + dh, 13 + dh, 1, 7), + intArrayOf(14 + dh, 14 + dh, 1, 7), + intArrayOf(15 + dh, 15 + dh, 1, 7), + intArrayOf(16 + dh, 16 + dh, 1, 7), + ) setBorderStyle(borderRegionsDashedBottom, BorderStyle.DASHED, Direction.BOTTOM) - val borderRegionsThin = arrayOf( - intArrayOf(1 + dh, 1 + dh, 1, 7), - intArrayOf(3 + dh, 3 + dh, 1, 1), - intArrayOf(4 + dh, 5 + dh, 1, 3), - intArrayOf(3 + dh, 5 + dh, 5, 7), - intArrayOf(7 + dh, 8 + dh, 1, 7), - intArrayOf(10 + dh, 17 + dh, 1, 7), - intArrayOf(10 + dh, 10 + dh, 1, 5), - intArrayOf(18 + dh, 18 + dh, 1, 5), - ) + val borderRegionsThin = + arrayOf( + intArrayOf(1 + dh, 1 + dh, 1, 7), + intArrayOf(3 + dh, 3 + dh, 1, 1), + intArrayOf(4 + dh, 5 + dh, 1, 3), + intArrayOf(3 + dh, 5 + dh, 5, 7), + intArrayOf(7 + dh, 8 + dh, 1, 7), + intArrayOf(10 + dh, 17 + dh, 1, 7), + intArrayOf(10 + dh, 10 + dh, 1, 5), + intArrayOf(18 + dh, 18 + dh, 1, 5), + ) setBorderStyle(borderRegionsThin, BorderStyle.THIN, Direction.ALL) - val borderRegionsThick = arrayOf( - intArrayOf(18 + dh, 18 + dh, 6, 7), - intArrayOf(10 + dh, 10 + dh, 6, 7), - intArrayOf(1 + dh, 1 + dh, 2, 2), - intArrayOf(3 + dh, 3 + dh, 2, 2), - intArrayOf(18 + dh, 18 + dh, 6, 7), - intArrayOf(19 + dh, 19 + dh, 6, 7) - ) + val borderRegionsThick = + arrayOf( + intArrayOf(18 + dh, 18 + dh, 6, 7), + intArrayOf(10 + dh, 10 + dh, 6, 7), + intArrayOf(1 + dh, 1 + dh, 2, 2), + intArrayOf(3 + dh, 3 + dh, 2, 2), + intArrayOf(18 + dh, 18 + dh, 6, 7), + intArrayOf(19 + dh, 19 + dh, 6, 7), + ) setBorderStyle(borderRegionsThick, BorderStyle.THICK, Direction.ALL) - val borderRegionsDashedRight = arrayOf( - intArrayOf(18 + dh, 18 + dh, 2, 3), - intArrayOf(1 + dh, 1 + dh, 4, 5), - intArrayOf(1 + dh, 1 + dh, 5, 6), - intArrayOf(4 + dh, 5 + dh, 1, 2), - intArrayOf(7 + dh, 8 + dh, 1, 1), - intArrayOf(7 + dh, 8 + dh, 2, 2), - intArrayOf(7 + dh, 8 + dh, 3, 3), - intArrayOf(7 + dh, 8 + dh, 4, 4), - intArrayOf(7 + dh, 8 + dh, 5, 5), - intArrayOf(7 + dh, 8 + dh, 6, 6), - intArrayOf(10 + dh, 18 + dh, 1, 1), - intArrayOf(10 + dh, 18 + dh, 2, 2), - intArrayOf(10 + dh, 18 + dh, 3, 3), - intArrayOf(10 + dh, 18 + dh, 4, 4), - intArrayOf(10 + dh, 18 + dh, 6, 6), - intArrayOf(3 + dh, 5 + dh, 1, 1), - intArrayOf(19 + dh, 19 + dh, 6, 6) - ) + val borderRegionsDashedRight = + arrayOf( + intArrayOf(18 + dh, 18 + dh, 2, 3), + intArrayOf(1 + dh, 1 + dh, 4, 5), + intArrayOf(1 + dh, 1 + dh, 5, 6), + intArrayOf(4 + dh, 5 + dh, 1, 2), + intArrayOf(7 + dh, 8 + dh, 1, 1), + intArrayOf(7 + dh, 8 + dh, 2, 2), + intArrayOf(7 + dh, 8 + dh, 3, 3), + intArrayOf(7 + dh, 8 + dh, 4, 4), + intArrayOf(7 + dh, 8 + dh, 5, 5), + intArrayOf(7 + dh, 8 + dh, 6, 6), + intArrayOf(10 + dh, 18 + dh, 1, 1), + intArrayOf(10 + dh, 18 + dh, 2, 2), + intArrayOf(10 + dh, 18 + dh, 3, 3), + intArrayOf(10 + dh, 18 + dh, 4, 4), + intArrayOf(10 + dh, 18 + dh, 6, 6), + intArrayOf(3 + dh, 5 + dh, 1, 1), + intArrayOf(19 + dh, 19 + dh, 6, 6), + ) setBorderStyle(borderRegionsDashedRight, BorderStyle.DASHED, Direction.RIGHT) - val borderRegionsThinRight = arrayOf( - intArrayOf(11 + dh, 17 + dh, 5, 5), - intArrayOf(3 + dh, 5 + dh, 5, 5) - ) + val borderRegionsThinRight = + arrayOf( + intArrayOf(11 + dh, 17 + dh, 5, 5), + intArrayOf(3 + dh, 5 + dh, 5, 5), + ) setBorderStyle(borderRegionsThinRight, BorderStyle.THIN, Direction.RIGHT) } private fun Sheet.setCellValues(dh: Int) { - val cellValues = mapOf( - Pair(1 + dh, 1) to "접수번호", - Pair(3 + dh, 5) to "학번", - Pair(4 + dh, 5) to "학생", - Pair(5 + dh, 5) to "보호자", - Pair(7 + dh, 1) to "결석", - Pair(7 + dh, 2) to "지각", - Pair(7 + dh, 3) to "조퇴", - Pair(7 + dh, 4) to "결과", - Pair(7 + dh, 5) to "출석점수", - Pair(7 + dh, 6) to "봉사시간", - Pair(7 + dh, 7) to "봉사점수", - Pair(10 + dh, 1) to "과목", - Pair(10 + dh, 2) to "3_2학기", - Pair(10 + dh, 3) to "3_1학기", - Pair(10 + dh, 4) to "직전", - Pair(10 + dh, 5) to "직전전", - Pair(10 + dh, 6) to "교과성적", - Pair(11 + dh, 6) to "대회", - Pair(12 + dh, 6) to "기능사", - Pair(13 + dh, 6) to "가산점", - Pair(19 + dh, 6) to "총점", - Pair(18 + dh, 6) to "환산점수", - Pair(11 + dh, 1) to "국어", - Pair(12 + dh, 1) to "사회", - Pair(13 + dh, 1) to "역사", - Pair(14 + dh, 1) to "수학", - Pair(15 + dh, 1) to "과학", - Pair(16 + dh, 1) to "기술가정", - Pair(17 + dh, 1) to "영어", - Pair(18 + dh, 1) to "점수" - ) + val cellValues = + mapOf( + Pair(1 + dh, 1) to "접수번호", + Pair(3 + dh, 5) to "학번", + Pair(4 + dh, 5) to "학생", + Pair(5 + dh, 5) to "보호자", + Pair(7 + dh, 1) to "결석", + Pair(7 + dh, 2) to "지각", + Pair(7 + dh, 3) to "조퇴", + Pair(7 + dh, 4) to "결과", + Pair(7 + dh, 5) to "출석점수", + Pair(7 + dh, 6) to "봉사시간", + Pair(7 + dh, 7) to "봉사점수", + Pair(10 + dh, 1) to "과목", + Pair(10 + dh, 2) to "3_2학기", + Pair(10 + dh, 3) to "3_1학기", + Pair(10 + dh, 4) to "직전", + Pair(10 + dh, 5) to "직전전", + Pair(10 + dh, 6) to "교과성적", + Pair(11 + dh, 6) to "대회", + Pair(12 + dh, 6) to "기능사", + Pair(13 + dh, 6) to "가산점", + Pair(19 + dh, 6) to "총점", + Pair(18 + dh, 6) to "환산점수", + Pair(11 + dh, 1) to "국어", + Pair(12 + dh, 1) to "사회", + Pair(13 + dh, 1) to "역사", + Pair(14 + dh, 1) to "수학", + Pair(15 + dh, 1) to "과학", + Pair(16 + dh, 1) to "기술가정", + Pair(17 + dh, 1) to "영어", + Pair(18 + dh, 1) to "점수", + ) cellValues.forEach { (cell, value) -> getCell(cell.first, cell.second).setCellValue(value) } } - private fun setBorderStyle(regions: Array, borderStyle: BorderStyle, direction: Direction) { + private fun setBorderStyle( + regions: Array, + borderStyle: BorderStyle, + direction: Direction, + ) { regions.forEach { region -> val address = CellRangeAddress(region[0], region[1], region[2], region[3]) when (direction) { @@ -233,17 +266,26 @@ class PrintApplicationCheckListGenerator { } } - private fun getCell(rowNum: Int, cellNum: Int): Cell { - val row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum) + private fun getCell( + rowNum: Int, + cellNum: Int, + ): Cell { + val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum) return row.getCell(cellNum) ?: row.createCell(cellNum) } - private fun setRowHeight(rowIndex: Int, height: Int) { - val row = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex) + private fun setRowHeight( + rowIndex: Int, + height: Int, + ) { + val row: Row = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex) row.heightInPoints = height.toFloat() } - private fun insertDataIntoSheet(dummyData: Map, dh: Int) { + private fun insertDataIntoSheet( + dummyData: Map, + 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()) @@ -271,7 +313,6 @@ class PrintApplicationCheckListGenerator { // 성적 더미 데이터 val subjects = listOf("국어", "사회", "역사", "수학", "과학", "기술가정", "영어") val dummyGrades = listOf("A", "B", "A", "B", "A", "B", "A") - subjects.forEachIndexed { index, subject -> val rowIndex = dh + 11 + index getCell(rowIndex, 1).setCellValue(subject) @@ -302,6 +343,6 @@ class PrintApplicationCheckListGenerator { BOTTOM, LEFT, RIGHT, - ALL + ALL, } } 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 15b4bed9..edd1132d 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,28 +1,28 @@ package hs.kr.entrydsm.application.global.excel.generator import hs.kr.entrydsm.application.global.excel.model.ApplicationInfo +import jakarta.servlet.http.HttpServletResponse import org.apache.poi.ss.usermodel.Row import org.springframework.stereotype.Component import java.io.IOException import java.time.LocalDateTime import java.time.format.DateTimeFormatter -import jakarta.servlet.http.HttpServletResponse @Component class PrintApplicationInfoGenerator { - fun execute(httpServletResponse: HttpServletResponse) { val applicationInfo = ApplicationInfo() val sheet = applicationInfo.getSheet() applicationInfo.format() - + // 더미 데이터로 테스트 - val dummyApplications = listOf( - createDummyApplication(1001L, "홍길동", "더미고등학교"), - createDummyApplication(1002L, "김철수", "테스트고등학교"), - createDummyApplication(1003L, "이영희", "샘플고등학교") - ) - + val dummyApplications = + listOf( + createDummyApplication(1001L, "홍길동", "더미고등학교"), + createDummyApplication(1002L, "김철수", "테스트고등학교"), + createDummyApplication(1003L, "이영희", "샘플고등학교"), + ) + dummyApplications.forEachIndexed { index, dummyData -> val row = sheet.createRow(index + 1) insertCode(row, dummyData) @@ -42,8 +42,12 @@ class PrintApplicationInfoGenerator { throw IllegalArgumentException("Excel 파일 생성 중 오류가 발생했습니다.", e) } } - - private fun createDummyApplication(receiptCode: Long, name: String, schoolName: String): Map { + + private fun createDummyApplication( + receiptCode: Long, + name: String, + schoolName: String, + ): Map { return mapOf( "receiptCode" to receiptCode, "applicationType" to "일반전형", @@ -60,11 +64,14 @@ class PrintApplicationInfoGenerator { "classNumber" to "3", "parentName" to "홍부모", "parentTel" to "010-9876-5432", - "examCode" to "DUMMY${receiptCode.toString().takeLast(3)}" + "examCode" to "DUMMY${receiptCode.toString().takeLast(3)}", ) } - - private fun insertCode(row: Row, dummyData: Map) { + + private fun insertCode( + row: Row, + dummyData: Map, + ) { row.createCell(0).setCellValue(dummyData["receiptCode"].toString()) row.createCell(1).setCellValue(dummyData["applicationType"].toString()) row.createCell(2).setCellValue(dummyData["isDaejeon"].toString()) @@ -80,7 +87,7 @@ class PrintApplicationInfoGenerator { row.createCell(12).setCellValue(dummyData["classNumber"].toString()) row.createCell(13).setCellValue(dummyData["parentName"].toString()) row.createCell(14).setCellValue(dummyData["parentTel"].toString()) - + // 성적 더미 데이터 val dummyGrades = listOf("A", "B", "A", "B", "A", "B", "A") for (i in 15..21) { @@ -95,7 +102,7 @@ class PrintApplicationInfoGenerator { for (i in 36..42) { row.createCell(i).setCellValue(dummyGrades[i - 36]) } - + // 점수 더미 데이터 row.createCell(43).setCellValue("180.0") row.createCell(44).setCellValue("170.0") 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 dae722ec..c4822fde 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 @@ -90,5 +90,4 @@ class ApplicationInfo { row.createCell(58).setCellValue("일반전형(170)") row.createCell(59).setCellValue("수험번호") } - } 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 c199a9ad..eb6b173e 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 @@ -15,9 +15,8 @@ class ExcelTestController( private val printApplicantCodesGenerator: PrintApplicantCodesGenerator, private val printApplicationInfoGenerator: PrintApplicationInfoGenerator, private val printAdmissionTicketGenerator: PrintAdmissionTicketGenerator, - private val printApplicationCheckListGenerator: PrintApplicationCheckListGenerator + private val printApplicationCheckListGenerator: PrintApplicationCheckListGenerator, ) { - @GetMapping("/applicant-codes") fun downloadApplicantCodes(response: HttpServletResponse) { printApplicantCodesGenerator.execute(response) @@ -37,4 +36,4 @@ class ExcelTestController( fun downloadCheckList(response: HttpServletResponse) { printApplicationCheckListGenerator.printApplicationCheckList(response) } -} \ No newline at end of file +} From a28cca4bd78f911774ba467192b2111e3ab0995a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:14:26 +0900 Subject: [PATCH 02/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20school=20aggrega?= =?UTF-8?q?te=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entrydsm/domain/school/aggregate/School.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt new file mode 100644 index 00000000..80daec4d --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt @@ -0,0 +1,18 @@ +package hs.kr.entrydsm.domain.school.aggregate + +import hs.kr.entrydsm.domain.school.exception.SchoolException + +data class School( + val code: String, + val name: String, + val tel: String, + val type: String, + val address: String, + val regionName: String +) { + init { + check(type == "중학교") { + throw SchoolException.InvalidSchoolTypeException(type) + } + } +} From e8dd3c08f1d2e7aded234d65ca54ae5149b91d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:16:24 +0900 Subject: [PATCH 03/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20exception=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 --- .../domain/school/exception/SchoolException.kt | 15 +++++++++++++++ .../global/feign/exception/FeignException.kt | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt new file mode 100644 index 00000000..a0a61c66 --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.domain.school.exception + +import hs.kr.entrydsm.global.exception.DomainException +import hs.kr.entrydsm.global.exception.ErrorCode + +sealed class SchoolException( + errorCode: ErrorCode, // override 제거 + message: String +) : DomainException(errorCode, message) { + + class InvalidSchoolTypeException(schoolType: String) : SchoolException( + errorCode = ErrorCode.SCHOOL_INVALID_TYPE, + message = "Invalid school type: $schoolType" + ) +} \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt new file mode 100644 index 00000000..89d458f3 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt @@ -0,0 +1,14 @@ +package hs.kr.entrydsm.application.global.feign.exception + +import hs.kr.entrydsm.global.exception.DomainException +import hs.kr.entrydsm.global.exception.ErrorCode + +sealed class FeignException( + errorCode: ErrorCode, + message: String, +) : DomainException(errorCode, message) { + class FeignServerErrorException(statusCode: Int, methodKey: String?) : FeignException( + errorCode = ErrorCode.FEIGN_SERVER_ERROR, + message = "Feign server error: $statusCode for method: $methodKey", + ) +} From cbe8dd3396838a2ca5ea9aee736bc921a135f6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:18:29 +0900 Subject: [PATCH 04/21] =?UTF-8?q?feat=20(=20#28=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 --- .../domain/school/interfaces/QuerySchoolContract.kt | 8 ++++++++ .../school/interfaces/QuerySchoolUseCaseContract.kt | 7 +++++++ .../entrydsm/domain/school/interfaces/SchoolContract.kt | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt new file mode 100644 index 00000000..cd1240f4 --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt @@ -0,0 +1,8 @@ +package hs.kr.entrydsm.domain.school.interfaces + +import hs.kr.entrydsm.domain.school.aggregate.School + +interface QuerySchoolContract { + fun querySchoolListBySchoolName(schoolName: String): List + fun querySchoolBySchoolCode(schoolCode: String): School? +} \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt new file mode 100644 index 00000000..1c69a6fb --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt @@ -0,0 +1,7 @@ +package hs.kr.entrydsm.domain.school.interfaces + +import hs.kr.entrydsm.domain.school.dto.QuerySchoolResponse + +interface QuerySchoolUseCaseContract { + fun querySchool(name: String): QuerySchoolResponse +} \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt new file mode 100644 index 00000000..68a39dcd --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt @@ -0,0 +1,4 @@ +package hs.kr.entrydsm.domain.school.interfaces + +interface SchoolContract : QuerySchoolContract { +} \ No newline at end of file From 197882e9451d3b482e9209fea8464c2021de9ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:18:45 +0900 Subject: [PATCH 05/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20web=20dto=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 --- .../domain/presentation/dto/QuerySchoolWebResponse.kt | 5 +++++ .../school/domain/presentation/dto/SchoolWebResponse.kt | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt new file mode 100644 index 00000000..d59a4c3b --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt @@ -0,0 +1,5 @@ +package hs.kr.entrydsm.application.domain.school.domain.presentation.dto + +data class QuerySchoolWebResponse( + val content: List? = null, +) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt new file mode 100644 index 00000000..474603f3 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt @@ -0,0 +1,8 @@ +package hs.kr.entrydsm.application.domain.school.domain.presentation.dto + +data class SchoolWebResponse( + val code: String? = null, + val name: String? = null, + val information: String? = null, + val address: String? = null, +) From 1bb0d04524145d2fe303e5a9b23bc1859d7e47e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:19:02 +0900 Subject: [PATCH 06/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20dto=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/school/dto/QuerySchoolResponse.kt | 5 +++++ .../hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt create mode 100644 casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt new file mode 100644 index 00000000..fce94944 --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt @@ -0,0 +1,5 @@ +package hs.kr.entrydsm.domain.school.dto + +data class QuerySchoolResponse( + val content: List +) diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt new file mode 100644 index 00000000..bfcc53dd --- /dev/null +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt @@ -0,0 +1,8 @@ +package hs.kr.entrydsm.domain.school.dto + +data class SchoolResponse( + val code: String, + val name: String, + val information: String, + val address: String +) From 3dfbb19369a02da4bbac0f56ea728ca509f7a704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:19:25 +0900 Subject: [PATCH 07/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20persistenceAdapt?= =?UTF-8?q?er=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../school/domain/SchoolPersistenceAdapter.kt | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt new file mode 100644 index 00000000..d7ed03ca --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt @@ -0,0 +1,87 @@ +package hs.kr.entrydsm.application.domain.school.domain + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import hs.kr.entrydsm.application.domain.school.domain.entity.SchoolCacheRedisEntity +import hs.kr.entrydsm.application.domain.school.domain.repository.SchoolCacheRepository +import hs.kr.entrydsm.application.global.feign.client.SchoolClient +import hs.kr.entrydsm.application.global.feign.client.dto.SchoolInfoElement +import hs.kr.entrydsm.domain.school.aggregate.School +import hs.kr.entrydsm.domain.school.interfaces.SchoolContract +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component + +@Component +class SchoolPersistenceAdapter( + private val schoolClient: SchoolClient, + private val schoolCacheRepository: SchoolCacheRepository, +) : SchoolContract { + @Value("\${neis.key}") + lateinit var apiKey: String + + override fun querySchoolBySchoolCode(school: String): School? { + if (schoolCacheRepository.existsById(school)) { + val schoolCache = schoolCacheRepository.findById(school).get() + schoolCache.run { + return School( + code = code, + name = name, + tel = tel, + type = type, + address = address, + regionName = regionName, + ) + } + } + + val school = + schoolClient.getSchoolBySchoolCode(schoolCode = school, key = apiKey)?.let { response -> + val mapper = ObjectMapper().registerKotlinModule() + val responseObject = mapper.readValue(response) + responseObject.schoolInfo?.getOrNull(1)?.row?.map { + School( + code = it.sdSchulCode, + name = it.schulNm, + tel = it.orgTelno, + type = it.schulKndScNm, + address = it.orgRdnma, + regionName = it.lctnScNm, + ) + }?.firstOrNull() + } + return school?.let { saveInCache(it) } + } + + override fun querySchoolListBySchoolName(schoolName: String): List { + return schoolClient.getSchoolListBySchoolName(schoolName = schoolName, key = apiKey)?.let { response -> + val mapper = ObjectMapper().registerKotlinModule() + val responseObject = mapper.readValue(response) + responseObject.schoolInfo?.getOrNull(1)?.row?.map { + School( + code = it.sdSchulCode, + name = it.schulNm, + tel = it.orgTelno, + type = it.schulKndScNm, + address = it.orgRdnma, + regionName = it.lctnScNm, + ) + } + } ?: emptyList() + } + + private fun saveInCache(school: School): School { + val schoolCache = + SchoolCacheRedisEntity( + code = school.code, + name = school.name, + tel = school.tel, + type = school.type, + address = school.address, + regionName = school.regionName, + ) + + schoolCacheRepository.save(schoolCache) + return school + } +} From 05b4f2c983a76a2f3fddc9666a8fd11616a6dccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:19:47 +0900 Subject: [PATCH 08/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20redis=20Reposito?= =?UTF-8?q?ry=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../school/domain/repository/SchoolCacheRepository.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt new file mode 100644 index 00000000..ce76f0c7 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt @@ -0,0 +1,6 @@ +package hs.kr.entrydsm.application.domain.school.domain.repository + +import hs.kr.entrydsm.application.domain.school.domain.entity.SchoolCacheRedisEntity +import org.springframework.data.repository.CrudRepository + +interface SchoolCacheRepository : CrudRepository From 0e5f68516daafd34c02e24efbd2edffd80109ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:20:02 +0900 Subject: [PATCH 09/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20UseCase=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 --- .../domain/usecase/QuerySchoolUseCase.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt new file mode 100644 index 00000000..cbd5a916 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt @@ -0,0 +1,27 @@ +package hs.kr.entrydsm.application.domain.school.domain.usecase + +import hs.kr.entrydsm.domain.school.interfaces.QuerySchoolContract +import hs.kr.entrydsm.domain.school.dto.QuerySchoolResponse +import hs.kr.entrydsm.domain.school.dto.SchoolResponse +import hs.kr.entrydsm.application.global.annotation.usecase.ReadOnlyUseCase +import hs.kr.entrydsm.domain.school.interfaces.QuerySchoolUseCaseContract + +@ReadOnlyUseCase +class QuerySchoolUseCase( + private val querySchoolContract: QuerySchoolContract, +): QuerySchoolUseCaseContract { + override fun querySchool(name: String): QuerySchoolResponse { + val schoolList = querySchoolContract.querySchoolListBySchoolName(name) + return QuerySchoolResponse( + content = + schoolList.map { + SchoolResponse( + code = it.code, + name = it.name, + information = it.tel, + address = it.address, + ) + }, + ) + } +} From 61cc9c23fab8ebc9dda04ca7fe8dedb64131e0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:20:15 +0900 Subject: [PATCH 10/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/annotation/usecase/ReadOnlyUseCase.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/annotation/usecase/ReadOnlyUseCase.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/annotation/usecase/ReadOnlyUseCase.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/annotation/usecase/ReadOnlyUseCase.kt new file mode 100644 index 00000000..d508a90f --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/annotation/usecase/ReadOnlyUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.application.global.annotation.usecase + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * + * 조회 기능을 담당하는 사용자 UseCase를 나타내는 어노테이션 + * + * @author 박주원 + **/ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +@Transactional(readOnly = true) +@Service +annotation class ReadOnlyUseCase() From b23f286a4fc1c8cedc9274dc3ee78291cf12d8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:20:27 +0900 Subject: [PATCH 11/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20feign=20client?= =?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 --- .../global/feign/client/SchoolClient.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt new file mode 100644 index 00000000..1725a527 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt @@ -0,0 +1,29 @@ +package hs.kr.entrydsm.application.global.feign.client + +import hs.kr.entrydsm.application.global.feign.FeignConfig +import org.springframework.cloud.openfeign.FeignClient +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam + +@FeignClient(name = "SchoolClient", url = "\${url.school}", configuration = [FeignConfig::class]) +interface SchoolClient { + @GetMapping + fun getSchoolBySchoolCode( + @RequestParam("SD_SCHUL_CODE") schoolCode: String, + @RequestParam("KEY") key: String, + @RequestParam("Type") type: String = "json", + @RequestParam("pIndex") pageIndex: Int = 1, + @RequestParam("pSize") pageSize: Int = 100, + @RequestParam("SCHUL_KND_SC_NM") schoolKind: String = "중학교", + ): String? + + @GetMapping + fun getSchoolListBySchoolName( + @RequestParam("SCHUL_NM") schoolName: String, + @RequestParam("KEY") key: String, + @RequestParam("Type") type: String = "json", + @RequestParam("pIndex") pageIndex: Int = 1, + @RequestParam("pSize") pageSize: Int = 100, + @RequestParam("SCHUL_KND_SC_NM") schoolKind: String = "중학교", + ): String? +} From e7d0c7cc6dc94d78522e18896ee854569d51ad29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:20:38 +0900 Subject: [PATCH 12/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20feign=20dto=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 --- .../feign/client/dto/SchoolInfoElement.kt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt new file mode 100644 index 00000000..e5497f61 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt @@ -0,0 +1,81 @@ +package hs.kr.entrydsm.application.global.feign.client.dto + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty + +@JsonIgnoreProperties(ignoreUnknown = true) +data class SchoolInfoElement( + val schoolInfo: List?, +) + +data class SchoolInfo( + val head: List?, + val row: List?, +) + +data class Head( + @JsonProperty("list_total_count") + val listTotalCount: Long?, + @JsonProperty("RESULT") + val result: Result?, +) + +data class Result( + @JsonProperty("CODE") + val code: String, + @JsonProperty("MESSAGE") + val message: String, +) + +data class Row( + @JsonProperty("ATPT_OFCDC_SC_CODE") + val atptOfcdcScCode: String, + @JsonProperty("ATPT_OFCDC_SC_NM") + val atptOfcdcScNm: String, + @JsonProperty("SD_SCHUL_CODE") + val sdSchulCode: String, + @JsonProperty("SCHUL_NM") + val schulNm: String, + @JsonProperty("ENG_SCHUL_NM") + val engSchulNm: String? = null, + @JsonProperty("SCHUL_KND_SC_NM") + val schulKndScNm: String, + @JsonProperty("LCTN_SC_NM") + val lctnScNm: String, + @JsonProperty("JU_ORG_NM") + val juOrgNm: String, + @JsonProperty("FOND_SC_NM") + val fondScNm: String, + @JsonProperty("ORG_RDNZC") + val orgRdnzc: String, + @JsonProperty("ORG_RDNMA") + val orgRdnma: String, + @JsonProperty("ORG_RDNDA") + val orgRdnda: String, + @JsonProperty("ORG_TELNO") + val orgTelno: String, + @JsonProperty("HMPG_ADRES") + val hmpgAdres: String, + @JsonProperty("COEDU_SC_NM") + val coeduScNm: String, + @JsonProperty("ORG_FAXNO") + val orgFaxno: String? = null, + @JsonProperty("HS_SC_NM") + val hsScNm: Any?, + @JsonProperty("INDST_SPECL_CCCCL_EXST_YN") + val indstSpeclCccclExstYn: String, + @JsonProperty("HS_GNRL_BUSNS_SC_NM") + val hsGnrlBusnsScNm: Any?, + @JsonProperty("SPCLY_PURPS_HS_ORD_NM") + val spclyPurpsHsOrdNm: Any?, + @JsonProperty("ENE_BFE_SEHF_SC_NM") + val eneBfeSehfScNm: String, + @JsonProperty("DGHT_SC_NM") + val dghtScNm: String, + @JsonProperty("FOND_YMD") + val fondYmd: String, + @JsonProperty("FOAS_MEMRD") + val foasMemrd: String, + @JsonProperty("LOAD_DTM") + val loadDtm: String, +) From 9c51b2b204f631d03eccc87fb1c9fca690c530c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:20:56 +0900 Subject: [PATCH 13/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20webAdapter=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20+=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/presentation/SchoolWebAdapter.kt | 34 +++++++++++++++++++ .../presentation/TestSchoolController.kt | 31 +++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt new file mode 100644 index 00000000..05e9dc13 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt @@ -0,0 +1,34 @@ +package hs.kr.entrydsm.application.domain.school.domain.presentation + +import hs.kr.entrydsm.application.domain.school.domain.presentation.dto.QuerySchoolWebResponse +import hs.kr.entrydsm.application.domain.school.domain.presentation.dto.SchoolWebResponse +import hs.kr.entrydsm.application.domain.school.domain.usecase.QuerySchoolUseCase +import org.springframework.cache.annotation.Cacheable +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/schools") +class SchoolWebAdapter( + private val querySchoolUseCase: QuerySchoolUseCase, +) { + @Cacheable(value = ["school_info"], key = "#name") + @GetMapping + fun querySchool( + @RequestParam(value = "school_name") name: String, + ): QuerySchoolWebResponse { + return QuerySchoolWebResponse( + content = + querySchoolUseCase.querySchool(name).content.map { + SchoolWebResponse( + code = it.code, + name = it.name, + information = it.information, + address = it.address, + ) + }, + ) + } +} diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt new file mode 100644 index 00000000..d3a556a0 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt @@ -0,0 +1,31 @@ +package hs.kr.entrydsm.application.domain.school.domain.presentation + +import hs.kr.entrydsm.application.global.feign.client.SchoolClient +import org.springframework.beans.factory.annotation.Value +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/test/schools") +class TestSchoolController( + private val schoolClient: SchoolClient, +) { + @Value("\${neis.key}") + lateinit var apiKey: String + + @GetMapping("/code") + fun getSchoolByCode( + @RequestParam("school_code") schoolCode: String, + ): String? { + return schoolClient.getSchoolBySchoolCode(schoolCode = schoolCode, key = apiKey) + } + + @GetMapping("/name") + fun getSchoolByName( + @RequestParam("school_name") schoolName: String, + ): String? { + return schoolClient.getSchoolListBySchoolName(schoolName = schoolName, key = apiKey) + } +} From 4eab313f58d3a98cb29b9a937a21e6bb93faeb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:21:07 +0900 Subject: [PATCH 14/21] =?UTF-8?q?feat=20(=20#28=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 --- .../domain/entity/SchoolCacheRedisEntity.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt new file mode 100644 index 00000000..daa4e1d3 --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.application.domain.school.domain.entity + +import org.springframework.data.annotation.Id +import org.springframework.data.redis.core.RedisHash + +@RedisHash("school_cache") +data class SchoolCacheRedisEntity( + @Id + val code: String, + val name: String, + val tel: String, + val type: String, + val address: String, + val regionName: String, +) From 6300d13658adf3f50b3d8c1be0e2e97079f267b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:21:20 +0900 Subject: [PATCH 15/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20feign=20Config?= =?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 --- .../application/global/feign/FeignConfig.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt new file mode 100644 index 00000000..954b526d --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt @@ -0,0 +1,22 @@ +package hs.kr.entrydsm.application.global.feign + +import feign.Logger +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.cloud.openfeign.EnableFeignClients +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@EnableFeignClients +@Configuration +class FeignConfig { + @Bean + @ConditionalOnMissingBean(value = [FeignClientErrorDecoder::class]) + fun commonFeignErrorDecoder(): FeignClientErrorDecoder? { + return FeignClientErrorDecoder() + } + + @Bean + fun feignLoggerLevel(): Logger.Level? { + return Logger.Level.FULL + } +} From 0582fd68594b151547695ec690eacdaa527126f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:21:33 +0900 Subject: [PATCH 16/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt index c93eca8d..c76106f0 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt @@ -390,7 +390,13 @@ enum class ErrorCode(val code: String, val description: String) { // Annotation 도메인 오류 (ANT) MISSING_ENTITY_ANNOTATION("ANT001", "엔티티 어노테이션 누락"), ENTITY_CONTRACT_NOT_IMPLEMENTED("ANT002", "EntityCont,ract 미구현"), - INVALID_AGGREGATE_ROOT("ANT003", "유효하지 않은 Aggregate Root"); + INVALID_AGGREGATE_ROOT("ANT003", "유효하지 않은 Aggregate Root"), + + // School 도메인 오류 (SCH) + SCHOOL_INVALID_TYPE("SCH001", "유효하지 않은 학교 유형입니다"), + + //feign error + FEIGN_SERVER_ERROR("FGN001", "외부 API 서버 오류가 발생했습니다"); /** * 오류 코드의 도메인 접두사를 반환합니다. From 5e0bb3cbaa7a16b6913c1cad911c20ddd109bb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:22:02 +0900 Subject: [PATCH 17/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20gradle=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/Dependencies.kt | 10 ++++++++ .../src/main/kotlin/DependencyVersions.kt | 4 ++++ buildSrc/src/main/kotlin/Plugins.kt | 1 + casper-application-domain/build.gradle.kts | 2 ++ .../build.gradle.kts | 24 +++++++++++++++++-- 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index fb052b8b..a177307d 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -8,6 +8,7 @@ object Dependencies { const val SPRING_BOOT_STARTER_WEB = "org.springframework.boot:spring-boot-starter-web" const val SPRING_BOOT_STARTER_TEST = "org.springframework.boot:spring-boot-starter-test" const val SPRING_BOOT_STARTER_ACTUATOR = "org.springframework.boot:spring-boot-starter-actuator" + const val SPRING_CONTEXT = "org.springframework:spring-context" //jexl const val APACHE_COMMONS_JEXL = "org.apache.commons:commons-jexl3:${DependencyVersions.APACHE_COMMONS_JEXL_VERSION}" @@ -34,4 +35,13 @@ object Dependencies { //commons io const val COMMONS_IO = "commons-io:commons-io:${DependencyVersions.COMMONS_IO}" + + // Feign Client + const val OPEN_FEIGN = "org.springframework.cloud:spring-cloud-starter-openfeign:${DependencyVersions.OPEN_FEIGN_VERSION}" + + // Spring Cloud BOM + const val SPRING_CLOUD = "org.springframework.cloud:spring-cloud-dependencies:${DependencyVersions.SPRING_CLOUD_VERSION}" + + // transaction + const val SPRING_TRANSACTION = "org.springframework:spring-tx:${DependencyVersions.SPRING_TRANSACTION}" } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/DependencyVersions.kt b/buildSrc/src/main/kotlin/DependencyVersions.kt index e4f780cf..6c278f1b 100644 --- a/buildSrc/src/main/kotlin/DependencyVersions.kt +++ b/buildSrc/src/main/kotlin/DependencyVersions.kt @@ -12,4 +12,8 @@ object DependencyVersions { const val PDF_HTML = "3.0.3" const val POI_VERSION = "5.2.3" const val COMMONS_IO = "2.11.0" + + const val SPRING_CLOUD_VERSION = "2024.0.0" + const val OPEN_FEIGN_VERSION = "3.1.4" + const val SPRING_TRANSACTION = "5.3.22" } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Plugins.kt b/buildSrc/src/main/kotlin/Plugins.kt index 3532b5ef..f88d9209 100644 --- a/buildSrc/src/main/kotlin/Plugins.kt +++ b/buildSrc/src/main/kotlin/Plugins.kt @@ -8,4 +8,5 @@ object Plugins { const val SPRING_DEPENDENCY_MANAGEMENT = "io.spring.dependency-management" const val KTLINT = "org.jlleitschuh.gradle.ktlint" const val CASPER_CONVENTION = "casper.documentation-convention" + const val KOTLIN_ALL_OPEN = "org.jetbrains.kotlin.plugin.spring" } \ No newline at end of file diff --git a/casper-application-domain/build.gradle.kts b/casper-application-domain/build.gradle.kts index 87c58d05..17b47a55 100644 --- a/casper-application-domain/build.gradle.kts +++ b/casper-application-domain/build.gradle.kts @@ -1,11 +1,13 @@ plugins { kotlin(Plugins.KOTLIN_JVM) version PluginVersions.KOTLIN_VERSION kotlin(Plugins.KOTLIN_SERIALIZATION) version PluginVersions.KOTLIN_VERSION + id(Plugins.KOTLIN_ALL_OPEN) version PluginVersions.KOTLIN_VERSION } version = Projects.APPLICATION_DOMAIN_VERSION dependencies { + implementation(Dependencies.KOTLINX_SERIALIZATION_JSON) implementation(Dependencies.KOTLINX_COROUTINES_CORE) diff --git a/casper-application-infrastructure/build.gradle.kts b/casper-application-infrastructure/build.gradle.kts index 1f56128a..6929a784 100644 --- a/casper-application-infrastructure/build.gradle.kts +++ b/casper-application-infrastructure/build.gradle.kts @@ -12,7 +12,15 @@ repositories { mavenCentral() } +dependencyManagement { + imports { + mavenBom(Dependencies.SPRING_CLOUD) + } +} + dependencies { + implementation(project(":casper-application-domain")) + implementation(Dependencies.SPRING_BOOT_STARTER) implementation(Dependencies.SPRING_BOOT_STARTER_WEB) implementation(Dependencies.SPRING_BOOT_STARTER_TEST) @@ -25,10 +33,22 @@ dependencies { // itext implementation(Dependencies.PDF_HTML) - implementation (Dependencies.THYMELEAF) + implementation(Dependencies.THYMELEAF) - //read-file + // read-file implementation(Dependencies.COMMONS_IO) implementation(Dependencies.POI) implementation(Dependencies.POI_OOXML) + + // Feign Client + implementation(Dependencies.OPEN_FEIGN) + + // Jackson (JSON 처리) + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + // Redis (캐시) + implementation("org.springframework.boot:spring-boot-starter-data-redis") + + // Cache (스프링 캐시) + implementation("org.springframework.boot:spring-boot-starter-cache") } From 98b6c73a3609407149fa0256aa5a7260eda4f790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:22:15 +0900 Subject: [PATCH 18/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20feign=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20decoder=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/feign/FeignClientErrorDecoder.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt new file mode 100644 index 00000000..b334023f --- /dev/null +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt @@ -0,0 +1,17 @@ +package hs.kr.entrydsm.application.global.feign + +import feign.Response +import feign.codec.ErrorDecoder +import hs.kr.entrydsm.application.global.feign.exception.FeignException + +class FeignClientErrorDecoder : ErrorDecoder { + override fun decode( + methodKey: String?, + response: Response, + ): Exception? { + if (response.status() >= 400) { + throw FeignException.FeignServerErrorException(response.status(), methodKey) + } + return feign.FeignException.errorStatus(methodKey, response) + } +} From 5b5e4b70dd07a6eada43598e7fc421ab4088a091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 17:40:52 +0900 Subject: [PATCH 19/21] refactor ( #28 ) : solve conflict --- buildSrc/src/main/kotlin/Dependencies.kt | 48 ++++++- .../src/main/kotlin/DependencyVersions.kt | 37 +++++- buildSrc/src/main/kotlin/Plugins.kt | 9 +- .../kr/entrydsm/global/exception/ErrorCode.kt | 5 +- .../build.gradle.kts | 125 ++++++++++++++++-- .../global/document/pdf/config/PdfConfig.kt | 13 +- .../PrintApplicationInfoGenerator.kt | 48 ++----- 7 files changed, 226 insertions(+), 59 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index a177307d..444a9964 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -42,6 +42,52 @@ object Dependencies { // Spring Cloud BOM const val SPRING_CLOUD = "org.springframework.cloud:spring-cloud-dependencies:${DependencyVersions.SPRING_CLOUD_VERSION}" + // WebFlux + const val WEB_FLUX = "org.springframework.boot:spring-boot-starter-webflux" + + // gRPC + const val GRPC_NETTY_SHADED = "io.grpc:grpc-netty-shaded:${DependencyVersions.GRPC}" + const val GRPC_PROTOBUF = "io.grpc:grpc-protobuf:${DependencyVersions.GRPC}" + const val GRPC_STUB = "io.grpc:grpc-stub:${DependencyVersions.GRPC}" + const val GRPC_KOTLIN_STUB = "io.grpc:grpc-kotlin-stub:${DependencyVersions.GRPC_KOTLIN}" + const val PROTOBUF_KOTLIN = "com.google.protobuf:protobuf-kotlin:${DependencyVersions.PROTOBUF}" + const val GRPC_TESTING = "io.grpc:grpc-testing:${DependencyVersions.GRPC}" + const val GRPC_CLIENT = "net.devh:grpc-client-spring-boot-starter:${DependencyVersions.GRPC_CLIENT}" + const val GOOGLE_PROTOBUF = "com.google.protobuf:protobuf-java:${DependencyVersions.GOOGLE_PROTOBUF}" + + // Coroutines + const val COROUTINES = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${DependencyVersions.COROUTINES}" + + // MapStruct + const val MAPSTRUCT = "org.mapstruct:mapstruct:${DependencyVersions.MAPSTRUCT}" + const val MAPSTRUCT_PROCESSOR = "org.mapstruct:mapstruct-processor:${DependencyVersions.MAPSTRUCT}" + + // MySQL + const val MYSQL_CONNECTOR = "com.mysql:mysql-connector-j" + + // QueryDSL + const val QUERYDSL_JPA = "com.querydsl:querydsl-jpa:${DependencyVersions.QUERYDSL}:jakarta" + const val QUERYDSL_APT = "com.querydsl:querydsl-apt:${DependencyVersions.QUERYDSL}:jakarta" + + // Jakarta APIs for kapt + const val JAKARTA_PERSISTENCE_API = "jakarta.persistence:jakarta.persistence-api:${DependencyVersions.JAKARTA_PERSISTENCE}" + const val JAKARTA_ANNOTATION_API = "jakarta.annotation:jakarta.annotation-api:${DependencyVersions.JAKARTA_ANNOTATION}" + + // Caffeine + const val CAFFEINE = "com.github.ben-manes.caffeine:caffeine:${DependencyVersions.CAFFEINE}" + + // Jackson Kotlin module + const val JACKSON_MODULE_KOTLIN = "com.fasterxml.jackson.module:jackson-module-kotlin" + + // Coroutines Reactor bridge + const val COROUTINES_REACTOR = "org.jetbrains.kotlinx:kotlinx-coroutines-reactor" + + // Reactor Netty + const val REACTOR_NETTY = "io.projectreactor.netty:reactor-netty" + + // JPA + const val SPRING_BOOT_STARTER_DATA_JPA = "org.springframework.boot:spring-boot-starter-data-jpa" + // transaction const val SPRING_TRANSACTION = "org.springframework:spring-tx:${DependencyVersions.SPRING_TRANSACTION}" -} \ No newline at end of file +} diff --git a/buildSrc/src/main/kotlin/DependencyVersions.kt b/buildSrc/src/main/kotlin/DependencyVersions.kt index 6c278f1b..01489eba 100644 --- a/buildSrc/src/main/kotlin/DependencyVersions.kt +++ b/buildSrc/src/main/kotlin/DependencyVersions.kt @@ -1,19 +1,50 @@ object DependencyVersions { // JEXL const val APACHE_COMMONS_JEXL_VERSION = "3.5.0" - + // Kotlinx Serialization const val KOTLINX_SERIALIZATION_VERSION = "1.6.3" - + // Kotlinx Coroutines const val KOTLINX_COROUTINES_VERSION = "1.8.1" + // PDF const val PDF_ITEXT = "7.2.0" const val PDF_HTML = "3.0.3" + + // Apache POI const val POI_VERSION = "5.2.3" + + // Commons IO const val COMMONS_IO = "2.11.0" + // Spring Cloud (Feign 포함) const val SPRING_CLOUD_VERSION = "2024.0.0" const val OPEN_FEIGN_VERSION = "3.1.4" + + // gRPC + const val GRPC = "1.61.1" + const val GRPC_KOTLIN = "1.4.1" + const val PROTOBUF = "3.25.3" + const val GRPC_CLIENT = "2.15.0.RELEASE" + const val GOOGLE_PROTOBUF = "3.25.3" + + // Coroutines (main 쪽 버전, KOTLINX_COROUTINES_VERSION 과 구분됨) + const val COROUTINES = "1.8.0" + + // MapStruct + const val MAPSTRUCT = "1.6.0" + + // QueryDSL + const val QUERYDSL = "5.0.0" + + // Jakarta + const val JAKARTA_PERSISTENCE = "3.1.0" + const val JAKARTA_ANNOTATION = "2.1.1" + + // Caffeine + const val CAFFEINE = "3.1.8" + + // Spring Transaction const val SPRING_TRANSACTION = "5.3.22" -} \ No newline at end of file +} diff --git a/buildSrc/src/main/kotlin/Plugins.kt b/buildSrc/src/main/kotlin/Plugins.kt index f88d9209..0bf2e97d 100644 --- a/buildSrc/src/main/kotlin/Plugins.kt +++ b/buildSrc/src/main/kotlin/Plugins.kt @@ -8,5 +8,12 @@ object Plugins { const val SPRING_DEPENDENCY_MANAGEMENT = "io.spring.dependency-management" const val KTLINT = "org.jlleitschuh.gradle.ktlint" const val CASPER_CONVENTION = "casper.documentation-convention" + + // feature/28-school-info const val KOTLIN_ALL_OPEN = "org.jetbrains.kotlin.plugin.spring" -} \ No newline at end of file + + // main 브랜치 + const val KAPT = "kapt" + const val PROTOBUF = "com.google.protobuf" + const val ALL_OPEN = "plugin.allopen" +} diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt index c76106f0..c804abde 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/global/exception/ErrorCode.kt @@ -396,7 +396,10 @@ enum class ErrorCode(val code: String, val description: String) { SCHOOL_INVALID_TYPE("SCH001", "유효하지 않은 학교 유형입니다"), //feign error - FEIGN_SERVER_ERROR("FGN001", "외부 API 서버 오류가 발생했습니다"); + FEIGN_SERVER_ERROR("FGN001", "외부 API 서버 오류가 발생했습니다"), + + // ExamCode 오류 (EXA) + FAILED_GEOCODE_CONVERSION("EXA001", "주소를 변환할 수 없습니다"); /** * 오류 코드의 도메인 접두사를 반환합니다. diff --git a/casper-application-infrastructure/build.gradle.kts b/casper-application-infrastructure/build.gradle.kts index 6929a784..82c41baa 100644 --- a/casper-application-infrastructure/build.gradle.kts +++ b/casper-application-infrastructure/build.gradle.kts @@ -1,9 +1,11 @@ plugins { kotlin(Plugins.KOTLIN_JVM) version PluginVersions.KOTLIN_VERSION kotlin(Plugins.KOTLIN_SPRING) version PluginVersions.KOTLIN_VERSION + kotlin(Plugins.KAPT) id(Plugins.KTLINT) version PluginVersions.KTLINT_VERSION id(Plugins.SPRING_BOOT) version PluginVersions.SPRING_BOOT_VERSION id(Plugins.SPRING_DEPENDENCY_MANAGEMENT) version PluginVersions.SPRING_DEPENDENCY_MANAGEMENT_VERSION + id(Plugins.PROTOBUF) version PluginVersions.PROTOBUF_VERSION } version = Projects.APPLICATION_INFRASTRUCTURE_VERSION @@ -21,34 +23,133 @@ dependencyManagement { dependencies { implementation(project(":casper-application-domain")) + // Spring Boot implementation(Dependencies.SPRING_BOOT_STARTER) implementation(Dependencies.SPRING_BOOT_STARTER_WEB) - implementation(Dependencies.SPRING_BOOT_STARTER_TEST) implementation(Dependencies.SPRING_BOOT_STARTER_ACTUATOR) + implementation(Dependencies.SPRING_BOOT_STARTER_DATA_JPA) + implementation(Dependencies.SPRING_TRANSACTION) + implementation(Dependencies.SPRING_BOOT_STARTER_CACHE) + implementation("org.springframework.boot:spring-boot-starter-data-redis") - implementation(Dependencies.APACHE_COMMONS_JEXL) - + // Kotlin implementation(Dependencies.KOTLIN_REFLECT) testImplementation(Dependencies.KOTLIN_TEST) - // itext + // Utilities + implementation(Dependencies.APACHE_COMMONS_JEXL) + implementation(Dependencies.COMMONS_IO) + + // PDF / Template implementation(Dependencies.PDF_HTML) implementation(Dependencies.THYMELEAF) - // read-file - implementation(Dependencies.COMMONS_IO) + // Excel implementation(Dependencies.POI) implementation(Dependencies.POI_OOXML) - // Feign Client + // Feign implementation(Dependencies.OPEN_FEIGN) - // Jackson (JSON 처리) + // Jackson + implementation(Dependencies.JACKSON_MODULE_KOTLIN) implementation("com.fasterxml.jackson.module:jackson-module-kotlin") - // Redis (캐시) - implementation("org.springframework.boot:spring-boot-starter-data-redis") + // gRPC + implementation(Dependencies.GRPC_NETTY_SHADED) + implementation(Dependencies.GRPC_PROTOBUF) + implementation(Dependencies.GRPC_STUB) + implementation(Dependencies.GRPC_KOTLIN_STUB) + implementation(Dependencies.PROTOBUF_KOTLIN) + implementation(Dependencies.GRPC_CLIENT) + testImplementation(Dependencies.GRPC_TESTING) + implementation(Dependencies.GOOGLE_PROTOBUF) + + // Coroutines + implementation(Dependencies.COROUTINES) + implementation(Dependencies.COROUTINES_REACTOR) + + // MapStruct + implementation(Dependencies.MAPSTRUCT) + kapt(Dependencies.MAPSTRUCT_PROCESSOR) - // Cache (스프링 캐시) - implementation("org.springframework.boot:spring-boot-starter-cache") + // QueryDSL + implementation(Dependencies.QUERYDSL_JPA) + kapt(Dependencies.QUERYDSL_APT) + kapt(Dependencies.JAKARTA_PERSISTENCE_API) + kapt(Dependencies.JAKARTA_ANNOTATION_API) + + // WebFlux + implementation(Dependencies.WEB_FLUX) + + // Cache + implementation(Dependencies.CAFFEINE) + + // MySQL + runtimeOnly(Dependencies.MYSQL_CONNECTOR) +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:${DependencyVersions.PROTOBUF}" + } + plugins { + create("grpc") { + artifact = "io.grpc:protoc-gen-grpc-java:${DependencyVersions.GRPC}" + } + create("grpckt") { + artifact = "io.grpc:protoc-gen-grpc-kotlin:${DependencyVersions.GRPC_KOTLIN}:jdk8@jar" + } + } + generateProtoTasks { + all().forEach { + it.plugins { + create("grpc") + create("grpckt") + } + } + } +} + +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xjsr305=strict") + } +} + +tasks.withType { + kotlinOptions { jvmTarget = "17" } +} + +tasks.test { + useJUnitPlatform() +} + +sourceSets { + named("main") { + java.srcDir("build/generated/source/kapt/main") + } +} + +kapt { + correctErrorTypes = true + arguments { + arg("querydsl.entityAccessors", "true") + arg("querydsl.generatedAnnotationClass", "jakarta.annotation.Generated") + } +} + +ktlint { + ignoreFailures.set(true) + filter { + exclude("**/build/**") + exclude("**/generated/**") + } +} + +tasks.matching { it.name == "runKtlintCheckOverMainSourceSet" }.configureEach { + dependsOn("kaptKotlin") +} +tasks.matching { it.name == "runKtlintFormatOverMainSourceSet" }.configureEach { + dependsOn("kaptKotlin") } diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt index 96561abf..11eb3f78 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/document/pdf/config/PdfConfig.kt @@ -10,14 +10,13 @@ import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver class PdfConfig { @Bean fun templateEngine(): TemplateEngine { - val templateResolver = - ClassLoaderTemplateResolver().apply { - prefix = "classpath:/templates/" - suffix = ".html" - templateMode = TemplateMode.HTML - } + val templateResolver = ClassLoaderTemplateResolver().apply { + prefix = "classpath:/templates/" + suffix = ".html" + templateMode = TemplateMode.HTML + } return TemplateEngine().apply { - this.setTemplateResolver(templateResolver) + setTemplateResolver(templateResolver) } } } 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 edd1132d..54e150e3 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 @@ -16,12 +16,11 @@ class PrintApplicationInfoGenerator { applicationInfo.format() // 더미 데이터로 테스트 - val dummyApplications = - listOf( - createDummyApplication(1001L, "홍길동", "더미고등학교"), - createDummyApplication(1002L, "김철수", "테스트고등학교"), - createDummyApplication(1003L, "이영희", "샘플고등학교"), - ) + val dummyApplications = listOf( + createDummyApplication(1001L, "홍길동", "더미고등학교"), + createDummyApplication(1002L, "김철수", "테스트고등학교"), + createDummyApplication(1003L, "이영희", "샘플고등학교"), + ) dummyApplications.forEachIndexed { index, dummyData -> val row = sheet.createRow(index + 1) @@ -90,36 +89,17 @@ class PrintApplicationInfoGenerator { // 성적 더미 데이터 val dummyGrades = listOf("A", "B", "A", "B", "A", "B", "A") - for (i in 15..21) { - row.createCell(i).setCellValue(dummyGrades[i - 15]) - } - for (i in 22..28) { - row.createCell(i).setCellValue(dummyGrades[i - 22]) - } - for (i in 29..35) { - row.createCell(i).setCellValue(dummyGrades[i - 29]) - } - for (i in 36..42) { - row.createCell(i).setCellValue(dummyGrades[i - 36]) + for (i in 15..42) { + row.createCell(i).setCellValue(dummyGrades[(i - 15) % dummyGrades.size]) } // 점수 더미 데이터 - row.createCell(43).setCellValue("180.0") - row.createCell(44).setCellValue("170.0") - row.createCell(45).setCellValue("165.0") - row.createCell(46).setCellValue("170.5") - row.createCell(47).setCellValue("30.0") - row.createCell(48).setCellValue("15.0") - row.createCell(49).setCellValue("0") - row.createCell(50).setCellValue("0") - row.createCell(51).setCellValue("0") - row.createCell(52).setCellValue("0") - row.createCell(53).setCellValue("20.0") - row.createCell(54).setCellValue("O") - row.createCell(55).setCellValue("X") - row.createCell(56).setCellValue("5.0") - row.createCell(57).setCellValue("210.5") - row.createCell(58).setCellValue("200.0") - row.createCell(59).setCellValue(dummyData["examCode"].toString()) + 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() + ) + for (i in scores.indices) { + row.createCell(43 + i).setCellValue(scores[i]) + } } } From ec8108e554010675c0f322090d4955e294ad025f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 18:00:59 +0900 Subject: [PATCH 20/21] =?UTF-8?q?feat=20(=20#28=20)=20:=20kdoc=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/school/aggregate/School.kt | 10 ++++ .../domain/school/dto/QuerySchoolResponse.kt | 5 ++ .../domain/school/dto/SchoolResponse.kt | 8 +++ .../school/exception/SchoolException.kt | 7 +++ .../school/interfaces/QuerySchoolContract.kt | 16 ++++++ .../interfaces/QuerySchoolUseCaseContract.kt | 9 ++++ .../school/interfaces/SchoolContract.kt | 3 ++ .../school/domain/SchoolPersistenceAdapter.kt | 29 +++++++++-- .../domain/entity/SchoolCacheRedisEntity.kt | 10 ++++ .../domain/presentation/SchoolWebAdapter.kt | 9 ++++ .../presentation/TestSchoolController.kt | 15 ++++++ .../dto/QuerySchoolWebResponse.kt | 5 ++ .../presentation/dto/SchoolWebResponse.kt | 8 +++ .../repository/SchoolCacheRepository.kt | 3 ++ .../domain/usecase/QuerySchoolUseCase.kt | 10 ++++ .../global/feign/FeignClientErrorDecoder.kt | 10 ++++ .../application/global/feign/FeignConfig.kt | 13 +++++ .../global/feign/client/SchoolClient.kt | 25 ++++++++++ .../feign/client/dto/SchoolInfoElement.kt | 50 +++++++++++++++++++ .../global/feign/exception/FeignException.kt | 7 +++ 20 files changed, 248 insertions(+), 4 deletions(-) diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt index 80daec4d..6d9c6605 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/aggregate/School.kt @@ -2,6 +2,16 @@ package hs.kr.entrydsm.domain.school.aggregate import hs.kr.entrydsm.domain.school.exception.SchoolException +/** + * 학교 정보를 담는 데이터 클래스 입니다. + * + * @property code 학교 코드 + * @property name 학교 이름 + * @property tel 학교 전화번호 + * @property type 학교 타입 + * @property address 학교 주소 + * @property regionName 지역 이름 + */ data class School( val code: String, val name: String, diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt index fce94944..53acb6a8 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/QuerySchoolResponse.kt @@ -1,5 +1,10 @@ package hs.kr.entrydsm.domain.school.dto +/** + * 학교 검색 결과를 담는 데이터 클래스 입니다. + * + * @property content 학교 검색 결과 리스트 + */ data class QuerySchoolResponse( val content: List ) diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt index bfcc53dd..0bcdf429 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/dto/SchoolResponse.kt @@ -1,5 +1,13 @@ package hs.kr.entrydsm.domain.school.dto +/** + * 학교 정보를 담는 데이터 클래스 입니다. + * + * @property code 학교 코드 + * @property name 학교 이름 + * @property information 학교 정보 + * @property address 학교 주소 + */ data class SchoolResponse( val code: String, val name: String, diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt index a0a61c66..d523fdf2 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/exception/SchoolException.kt @@ -3,11 +3,18 @@ package hs.kr.entrydsm.domain.school.exception import hs.kr.entrydsm.global.exception.DomainException import hs.kr.entrydsm.global.exception.ErrorCode +/** + * 학교 관련 최상위 예외 클래스 입니다. + */ sealed class SchoolException( errorCode: ErrorCode, // override 제거 message: String ) : DomainException(errorCode, message) { + + /** + * 유효하지 않은 학교 타입일 경우 발생하는 예외입니다. + */ class InvalidSchoolTypeException(schoolType: String) : SchoolException( errorCode = ErrorCode.SCHOOL_INVALID_TYPE, message = "Invalid school type: $schoolType" diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt index cd1240f4..231517db 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolContract.kt @@ -2,7 +2,23 @@ package hs.kr.entrydsm.domain.school.interfaces import hs.kr.entrydsm.domain.school.aggregate.School +/** + * 학교 정보를 조회하는 Contract 입니다. + */ interface QuerySchoolContract { + /** + * 학교 이름으로 학교 리스트를 조회합니다. + * + * @param schoolName 학교 이름 + * @return 학교 리스트 + */ fun querySchoolListBySchoolName(schoolName: String): List + + /** + * 학교 코드로 학교를 조회합니다. + * + * @param schoolCode 학교 코드 + * @return 학교 정보 + */ fun querySchoolBySchoolCode(schoolCode: String): School? } \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt index 1c69a6fb..d4fd4415 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/QuerySchoolUseCaseContract.kt @@ -2,6 +2,15 @@ package hs.kr.entrydsm.domain.school.interfaces import hs.kr.entrydsm.domain.school.dto.QuerySchoolResponse +/** + * 학교 정보를 조회하는 UseCase Contract 입니다. + */ interface QuerySchoolUseCaseContract { + /** + * 학교 이름으로 학교를 조회합니다. + * + * @param name 학교 이름 + * @return 학교 검색 결과 + */ fun querySchool(name: String): QuerySchoolResponse } \ No newline at end of file diff --git a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt index 68a39dcd..2db96bb6 100644 --- a/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt +++ b/casper-application-domain/src/main/kotlin/hs/kr/entrydsm/domain/school/interfaces/SchoolContract.kt @@ -1,4 +1,7 @@ package hs.kr.entrydsm.domain.school.interfaces +/** + * School 도메인의 Persistence를 관리하는 Contract 입니다. + */ interface SchoolContract : QuerySchoolContract { } \ No newline at end of file diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt index d7ed03ca..1e96c521 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/SchoolPersistenceAdapter.kt @@ -12,6 +12,9 @@ import hs.kr.entrydsm.domain.school.interfaces.SchoolContract import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component +/** + * School 도메인의 영속성을 관리하는 Adapter 입니다. + */ @Component class SchoolPersistenceAdapter( private val schoolClient: SchoolClient, @@ -20,9 +23,15 @@ class SchoolPersistenceAdapter( @Value("\${neis.key}") lateinit var apiKey: String - override fun querySchoolBySchoolCode(school: String): School? { - if (schoolCacheRepository.existsById(school)) { - val schoolCache = schoolCacheRepository.findById(school).get() + /** + * 학교 코드로 학교를 조회합니다. + * + * @param schoolCode 학교 코드 + * @return 학교 정보 + */ + override fun querySchoolBySchoolCode(schoolCode: String): School? { + if (schoolCacheRepository.existsById(schoolCode)) { + val schoolCache = schoolCacheRepository.findById(schoolCode).get() schoolCache.run { return School( code = code, @@ -36,7 +45,7 @@ class SchoolPersistenceAdapter( } val school = - schoolClient.getSchoolBySchoolCode(schoolCode = school, key = apiKey)?.let { response -> + schoolClient.getSchoolBySchoolCode(schoolCode = schoolCode, key = apiKey)?.let { response -> val mapper = ObjectMapper().registerKotlinModule() val responseObject = mapper.readValue(response) responseObject.schoolInfo?.getOrNull(1)?.row?.map { @@ -53,6 +62,12 @@ class SchoolPersistenceAdapter( return school?.let { saveInCache(it) } } + /** + * 학교 이름으로 학교 리스트를 조회합니다. + * + * @param schoolName 학교 이름 + * @return 학교 리스트 + */ override fun querySchoolListBySchoolName(schoolName: String): List { return schoolClient.getSchoolListBySchoolName(schoolName = schoolName, key = apiKey)?.let { response -> val mapper = ObjectMapper().registerKotlinModule() @@ -70,6 +85,12 @@ class SchoolPersistenceAdapter( } ?: emptyList() } + /** + * 학교 정보를 캐시에 저장합니다. + * + * @param school 학교 정보 + * @return 저장된 학교 정보 + */ private fun saveInCache(school: School): School { val schoolCache = SchoolCacheRedisEntity( diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt index daa4e1d3..baed10de 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/entity/SchoolCacheRedisEntity.kt @@ -3,6 +3,16 @@ package hs.kr.entrydsm.application.domain.school.domain.entity import org.springframework.data.annotation.Id import org.springframework.data.redis.core.RedisHash +/** + * 학교 정보를 캐시하는 Redis Entity 입니다. + * + * @property code 학교 코드 + * @property name 학교 이름 + * @property tel 학교 전화번호 + * @property type 학교 타입 + * @property address 학교 주소 + * @property regionName 지역 이름 + */ @RedisHash("school_cache") data class SchoolCacheRedisEntity( @Id diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt index 05e9dc13..4de4659c 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/SchoolWebAdapter.kt @@ -11,9 +11,18 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/schools") +/** + * 학교 정보 API를 제공하는 WebAdapter 입니다. + */ class SchoolWebAdapter( private val querySchoolUseCase: QuerySchoolUseCase, ) { + /** + * 학교 이름으로 학교를 검색합니다. + * + * @param name 학교 이름 + * @return 학교 검색 결과 + */ @Cacheable(value = ["school_info"], key = "#name") @GetMapping fun querySchool( diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt index d3a556a0..13ce9274 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/TestSchoolController.kt @@ -7,6 +7,9 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +/** + * School API 테스트를 위한 컨트롤러입니다. + */ @RestController @RequestMapping("/test/schools") class TestSchoolController( @@ -15,6 +18,12 @@ class TestSchoolController( @Value("\${neis.key}") lateinit var apiKey: String + /** + * 학교 코드로 학교 정보를 조회합니다. + * + * @param schoolCode 학교 코드 + * @return 학교 정보 + */ @GetMapping("/code") fun getSchoolByCode( @RequestParam("school_code") schoolCode: String, @@ -22,6 +31,12 @@ class TestSchoolController( return schoolClient.getSchoolBySchoolCode(schoolCode = schoolCode, key = apiKey) } + /** + * 학교 이름으로 학교 리스트를 조회합니다. + * + * @param schoolName 학교 이름 + * @return 학교 리스트 + */ @GetMapping("/name") fun getSchoolByName( @RequestParam("school_name") schoolName: String, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt index d59a4c3b..0c17ad14 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/QuerySchoolWebResponse.kt @@ -1,5 +1,10 @@ package hs.kr.entrydsm.application.domain.school.domain.presentation.dto +/** + * 학교 정보 검색 응답을 위한 데이터 클래스 입니다. + * + * @property content 학교 정보 리스트 + */ data class QuerySchoolWebResponse( val content: List? = null, ) diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt index 474603f3..35e85cc4 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/presentation/dto/SchoolWebResponse.kt @@ -1,5 +1,13 @@ package hs.kr.entrydsm.application.domain.school.domain.presentation.dto +/** + * 학교 정보 응답을 위한 데이터 클래스 입니다. + * + * @property code 학교 코드 + * @property name 학교 이름 + * @property information 학교 정보 + * @property address 학교 주소 + */ data class SchoolWebResponse( val code: String? = null, val name: String? = null, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt index ce76f0c7..646db8a5 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/repository/SchoolCacheRepository.kt @@ -3,4 +3,7 @@ package hs.kr.entrydsm.application.domain.school.domain.repository import hs.kr.entrydsm.application.domain.school.domain.entity.SchoolCacheRedisEntity import org.springframework.data.repository.CrudRepository +/** + * 학교 정보를 캐시하는 Repository 입니다. + */ interface SchoolCacheRepository : CrudRepository diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt index cbd5a916..feab2b3c 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/domain/school/domain/usecase/QuerySchoolUseCase.kt @@ -6,10 +6,20 @@ import hs.kr.entrydsm.domain.school.dto.SchoolResponse import hs.kr.entrydsm.application.global.annotation.usecase.ReadOnlyUseCase import hs.kr.entrydsm.domain.school.interfaces.QuerySchoolUseCaseContract +/** + * 학교 정보를 조회하는 UseCase 입니다. + */ @ReadOnlyUseCase class QuerySchoolUseCase( private val querySchoolContract: QuerySchoolContract, ): QuerySchoolUseCaseContract { + + /** + * 학교 이름으로 학교를 조회합니다. + * + * @param name 학교 이름 + * @return 학교 검색 결과 + */ override fun querySchool(name: String): QuerySchoolResponse { val schoolList = querySchoolContract.querySchoolListBySchoolName(name) return QuerySchoolResponse( diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt index b334023f..19a00580 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignClientErrorDecoder.kt @@ -4,7 +4,17 @@ import feign.Response import feign.codec.ErrorDecoder import hs.kr.entrydsm.application.global.feign.exception.FeignException +/** + * Feign Client의 에러를 디코딩하는 클래스입니다. + */ class FeignClientErrorDecoder : ErrorDecoder { + /** + * Feign 요청 중 발생한 에러를 디코딩합니다. + * + * @param methodKey 요청한 메소드 키 + * @param response 응답 + * @return 디코딩된 예외 + */ override fun decode( methodKey: String?, response: Response, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt index 954b526d..bdd026ea 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/FeignConfig.kt @@ -6,15 +6,28 @@ import org.springframework.cloud.openfeign.EnableFeignClients import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +/** + * Feign Client 설정을 위한 클래스입니다. + */ @EnableFeignClients @Configuration class FeignConfig { + /** + * Feign Client의 에러 디코더를 설정합니다. + * + * @return FeignClientErrorDecoder + */ @Bean @ConditionalOnMissingBean(value = [FeignClientErrorDecoder::class]) fun commonFeignErrorDecoder(): FeignClientErrorDecoder? { return FeignClientErrorDecoder() } + /** + * Feign Client의 로거 레벨을 설정합니다. + * + * @return Logger.Level + */ @Bean fun feignLoggerLevel(): Logger.Level? { return Logger.Level.FULL diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt index 1725a527..9606299f 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/SchoolClient.kt @@ -5,8 +5,22 @@ import org.springframework.cloud.openfeign.FeignClient import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam +/** + * 나이스 교육정보 개방 포털 API SchoolClient 입니다. + */ @FeignClient(name = "SchoolClient", url = "\${url.school}", configuration = [FeignConfig::class]) interface SchoolClient { + /** + * 학교 코드로 학교 정보를 조회합니다. + * + * @param schoolCode 학교 코드 + * @param key API KEY + * @param type 응답 타입 + * @param pageIndex 페이지 인덱스 + * @param pageSize 페이지 사이즈 + * @param schoolKind 학교 종류 + * @return 학교 정보 + */ @GetMapping fun getSchoolBySchoolCode( @RequestParam("SD_SCHUL_CODE") schoolCode: String, @@ -17,6 +31,17 @@ interface SchoolClient { @RequestParam("SCHUL_KND_SC_NM") schoolKind: String = "중학교", ): String? + /** + * 학교 이름으로 학교 리스트를 조회합니다. + * + * @param schoolName 학교 이름 + * @param key API KEY + * @param type 응답 타입 + * @param pageIndex 페이지 인덱스 + * @param pageSize 페이지 사이즈 + * @param schoolKind 학교 종류 + * @return 학교 리스트 + */ @GetMapping fun getSchoolListBySchoolName( @RequestParam("SCHUL_NM") schoolName: String, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt index e5497f61..775475bb 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/client/dto/SchoolInfoElement.kt @@ -3,16 +3,31 @@ package hs.kr.entrydsm.application.global.feign.client.dto import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty +/** + * 나이스 API 학교 정보 응답을 위한 데이터 클래스 입니다. + */ @JsonIgnoreProperties(ignoreUnknown = true) data class SchoolInfoElement( val schoolInfo: List?, ) +/** + * 학교 정보를 담는 데이터 클래스 입니다. + * + * @property head 헤더 정보 + * @property row 학교 정보 리스트 + */ data class SchoolInfo( val head: List?, val row: List?, ) +/** + * 나이스 API 응답의 헤더 정보를 담는 데이터 클래스 입니다. + * + * @property listTotalCount 총 아이템 수 + * @property result 응답 결과 + */ data class Head( @JsonProperty("list_total_count") val listTotalCount: Long?, @@ -20,6 +35,12 @@ data class Head( val result: Result?, ) +/** + * 나이스 API 응답 결과를 담는 데이터 클래스 입니다. + * + * @property code 응답 코드 + * @property message 응답 메시지 + */ data class Result( @JsonProperty("CODE") val code: String, @@ -27,6 +48,35 @@ data class Result( val message: String, ) +/** + * 나이스 API 학교 정보 상세를 담는 데이터 클래스 입니다. + * + * @property atptOfcdcScCode 시도교육청코드 + * @property atptOfcdcScNm 시도교육청명 + * @property sdSchulCode 표준학교코드 + * @property schulNm 학교명 + * @property engSchulNm 영문학교명 + * @property schulKndScNm 학교종류명 + * @property lctnScNm 소재지명 + * @property juOrgNm 관할조직명 + * @property fondScNm 설립명 + * @property orgRdnzc 도로명우편번호 + * @property orgRdnma 도로명주소 + * @property orgRdnda 도로명상세주소 + * @property orgTelno 전화번호 + * @property hmpgAdres 홈페이지주소 + * @property coeduScNm 남녀공학구분명 + * @property orgFaxno 팩с번호 + * @property hsScNm 고등학교구분명 + * @property indstSpeclCccclExstYn 산업체특별학급존재여부 + * @property hsGnrlBusnsScNm 고등학교일반실업구분명 + * @property spclyPurpsHsOrdNm 특수목적고등학교계열명 + * @property eneBfeSehfScNm 입학전형구분명 + * @property dghtScNm 주야구분명 + * @property fondYmd 설립일자 + * @property foasMemrd 개교기념일 + * @property loadDtm 수정일 + */ data class Row( @JsonProperty("ATPT_OFCDC_SC_CODE") val atptOfcdcScCode: String, diff --git a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt index 89d458f3..ccb0ead7 100644 --- a/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt +++ b/casper-application-infrastructure/src/main/kotlin/hs/kr/entrydsm/application/global/feign/exception/FeignException.kt @@ -3,10 +3,17 @@ package hs.kr.entrydsm.application.global.feign.exception import hs.kr.entrydsm.global.exception.DomainException import hs.kr.entrydsm.global.exception.ErrorCode +/** + * Feign 관련 최상위 예외 클래스 입니다. + */ sealed class FeignException( errorCode: ErrorCode, message: String, ) : DomainException(errorCode, message) { + + /** + * Feign 서버 오류시 발생하는 예외입니다. + */ class FeignServerErrorException(statusCode: Int, methodKey: String?) : FeignException( errorCode = ErrorCode.FEIGN_SERVER_ERROR, message = "Feign server error: $statusCode for method: $methodKey", From a70acea1aae59d5b48131993b1ea406d40e2f07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A3=BC=EC=9B=90?= Date: Wed, 27 Aug 2025 19:26:42 +0900 Subject: [PATCH 21/21] =?UTF-8?q?fix=20(=20#28=20)=20:=20conflict=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/Dependencies.kt | 10 +++++++++- buildSrc/src/main/kotlin/DependencyVersions.kt | 13 +++++-------- casper-application-infrastructure/build.gradle.kts | 9 +++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 444a9964..2239f9e3 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -89,5 +89,13 @@ object Dependencies { const val SPRING_BOOT_STARTER_DATA_JPA = "org.springframework.boot:spring-boot-starter-data-jpa" // transaction - const val SPRING_TRANSACTION = "org.springframework:spring-tx:${DependencyVersions.SPRING_TRANSACTION}" + const val SPRING_TRANSACTION = "org.springframework:spring-tx" + + //spring cache + // Redis (캐시) + const val REDIS = "org.springframework.boot:spring-boot-starter-data-redis" + + // Cache (스프링 캐시) + const val SPRING_CACHE = "org.springframework.boot:spring-boot-starter-cache" + } diff --git a/buildSrc/src/main/kotlin/DependencyVersions.kt b/buildSrc/src/main/kotlin/DependencyVersions.kt index 01489eba..0218932e 100644 --- a/buildSrc/src/main/kotlin/DependencyVersions.kt +++ b/buildSrc/src/main/kotlin/DependencyVersions.kt @@ -23,11 +23,11 @@ object DependencyVersions { const val OPEN_FEIGN_VERSION = "3.1.4" // gRPC - const val GRPC = "1.61.1" - const val GRPC_KOTLIN = "1.4.1" - const val PROTOBUF = "3.25.3" - const val GRPC_CLIENT = "2.15.0.RELEASE" - const val GOOGLE_PROTOBUF = "3.25.3" + const val GRPC = "1.58.0" // 1.61.1에서 변경 + const val GRPC_KOTLIN = "1.4.0" // 1.4.1에서 변경 + const val GRPC_CLIENT = "2.15.0.RELEASE" // 유지 + const val PROTOBUF = "3.24.0" // 3.25.3에서 변경 + const val GOOGLE_PROTOBUF = "3.24.0" // 3.25.3에서 변경 // Coroutines (main 쪽 버전, KOTLINX_COROUTINES_VERSION 과 구분됨) const val COROUTINES = "1.8.0" @@ -44,7 +44,4 @@ object DependencyVersions { // Caffeine const val CAFFEINE = "3.1.8" - - // Spring Transaction - const val SPRING_TRANSACTION = "5.3.22" } diff --git a/casper-application-infrastructure/build.gradle.kts b/casper-application-infrastructure/build.gradle.kts index 82c41baa..ad2be732 100644 --- a/casper-application-infrastructure/build.gradle.kts +++ b/casper-application-infrastructure/build.gradle.kts @@ -27,10 +27,12 @@ dependencies { implementation(Dependencies.SPRING_BOOT_STARTER) implementation(Dependencies.SPRING_BOOT_STARTER_WEB) implementation(Dependencies.SPRING_BOOT_STARTER_ACTUATOR) + implementation(Dependencies.SPRING_BOOT_STARTER_TEST) implementation(Dependencies.SPRING_BOOT_STARTER_DATA_JPA) - implementation(Dependencies.SPRING_TRANSACTION) - implementation(Dependencies.SPRING_BOOT_STARTER_CACHE) - implementation("org.springframework.boot:spring-boot-starter-data-redis") + implementation(Dependencies.SPRING_CACHE) + + //redis + implementation(Dependencies.REDIS) // Kotlin implementation(Dependencies.KOTLIN_REFLECT) @@ -53,7 +55,6 @@ dependencies { // Jackson implementation(Dependencies.JACKSON_MODULE_KOTLIN) - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") // gRPC implementation(Dependencies.GRPC_NETTY_SHADED)