From 1e66aec40c8b0982318d2b8756c567b52c594dd7 Mon Sep 17 00:00:00 2001 From: jyj1289 Date: Mon, 21 Oct 2024 16:48:06 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat(#163):=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=88=98=ED=97=98=ED=91=9C=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20-=20=EC=A0=84=EC=B2=B4=20=EC=88=98=ED=97=98?= =?UTF-8?q?=ED=91=9C=EB=A5=BC=20=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20API=EB=A5=BC=20=EB=A7=8C=EB=93=A4=EC=97=88?= =?UTF-8?q?=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../form/GenerateAllAdmissionUseCase.java | 70 +++++++++++++++++++ .../presentation/form/FormController.java | 10 +++ 2 files changed, 80 insertions(+) create mode 100644 src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java diff --git a/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java new file mode 100644 index 0000000..5207016 --- /dev/null +++ b/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java @@ -0,0 +1,70 @@ +package com.bamdoliro.maru.application.form; + +import com.bamdoliro.maru.domain.form.domain.Form; +import com.bamdoliro.maru.domain.form.domain.type.FormStatus; +import com.bamdoliro.maru.infrastructure.pdf.GeneratePdfService; +import com.bamdoliro.maru.infrastructure.pdf.MergePdfService; +import com.bamdoliro.maru.infrastructure.persistence.form.FormRepository; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.s3.constants.FolderConstant; +import com.bamdoliro.maru.infrastructure.thymeleaf.ProcessTemplateService; +import com.bamdoliro.maru.infrastructure.thymeleaf.Templates; +import com.bamdoliro.maru.shared.annotation.UseCase; +import com.bamdoliro.maru.shared.constants.Schedule; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.utils.PdfMerger; +import lombok.RequiredArgsConstructor; +import org.springframework.core.io.ByteArrayResource; + +import java.util.List; + +import java.io.ByteArrayOutputStream; +import java.util.Map; + +import static com.bamdoliro.maru.shared.constants.Schedule.*; + +@RequiredArgsConstructor +@UseCase +public class GenerateAllAdmissionUseCase { + + private final FormRepository formRepository; + private final ProcessTemplateService processTemplateService; + private final GeneratePdfService generatePdfService; + private final MergePdfService mergePdfService; + private final FileService fileService; + + public ByteArrayResource execute() { + List
formList = formRepository.findByStatus(FormStatus.FIRST_PASSED); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PdfDocument mergedDocument = new PdfDocument(new PdfWriter(outputStream)); + PdfMerger pdfMerger = new PdfMerger(mergedDocument); + + formList.forEach(form -> mergePdfService.execute(pdfMerger, generateAdmissionTicket(form))); + + mergedDocument.close(); + pdfMerger.close(); + + return new ByteArrayResource(outputStream.toByteArray()); + } + + private ByteArrayOutputStream generateAdmissionTicket(Form form) { + + Map formMap = Map.ofEntries( + Map.entry("form", form), + Map.entry("year", Schedule.getAdmissionYear()), + Map.entry("codingTest", Schedule.toLocaleString(CODING_TEST)), + Map.entry("ncs", Schedule.toLocaleString(NCS)), + Map.entry("depthInterview", Schedule.toLocaleString(DEPTH_INTERVIEW)), + Map.entry("physicalExamination", Schedule.toLocaleString(PHYSICAL_EXAMINATION)), + Map.entry("announcementOfSecondPass", Schedule.toLocaleString(ANNOUNCEMENT_OF_SECOND_PASS)), + Map.entry("meisterTalentEntranceTime", Schedule.toLocaleString(MEISTER_TALENT_ENTRANCE_TIME)), + Map.entry("meisterTalentExclusionEntranceTime", Schedule.toLocaleString(MEISTER_TALENT_EXCLUSION_ENTRANCE_TIME)), + Map.entry("entranceRegistrationTime", Schedule.toLocaleString(ENTRANCE_REGISTRATION_PERIOD_START, ENTRANCE_REGISTRATION_PERIOD_END)), + Map.entry("identificationPictureUri", fileService.getPresignedUrl(FolderConstant.IDENTIFICATION_PICTURE, form.getUser().getUuid().toString()).getDownloadUrl()) + ); + String html = processTemplateService.execute(Templates.ADMISSION_TICKET, formMap); + + return generatePdfService.execute(html); + } +} diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java b/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java index d501a29..9360bfd 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java @@ -72,6 +72,7 @@ public class FormController { private final QueryFormUrlUseCase queryFormUrlUseCase; private final SelectSecondPassUseCase selectSecondPassUseCase; private final UpdateOriginalTypeUseCase updateOriginalTypeUseCase; + private final GenerateAllAdmissionUseCase generateAllAdmissionUseCase; @ResponseStatus(HttpStatus.CREATED) @PostMapping @@ -225,6 +226,15 @@ public ResponseEntity generateAdmissionTicket( .body(generateAdmissionTicketUseCase.execute(user)); } + @GetMapping("/admission-ticket/all") + public ResponseEntity generateAllAdmissionTicket( + @AuthenticationPrincipal(authority = Authority.ADMIN) User user + ) { + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_PDF) + .body(generateAllAdmissionUseCase.execute()); + } + @GetMapping("/proof-of-application") public ResponseEntity generateProofOfApplication( @AuthenticationPrincipal(authority = Authority.USER) User user From 8b682198310b7efd6e23608ea07f2e581f4e6da4 Mon Sep 17 00:00:00 2001 From: jyj1289 Date: Mon, 21 Oct 2024 16:50:03 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix(#163):=20=EC=98=A4=ED=83=80=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20GenerateAllAdmissionUseCase=EB=A5=BC=20Generate?= =?UTF-8?q?AllAdmissionTicketUseCase=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=96=88=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ionUseCase.java => GenerateAllAdmissionTicketUseCase.java} | 2 +- .../com/bamdoliro/maru/presentation/form/FormController.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/com/bamdoliro/maru/application/form/{GenerateAllAdmissionUseCase.java => GenerateAllAdmissionTicketUseCase.java} (98%) diff --git a/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCase.java similarity index 98% rename from src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java rename to src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCase.java index 5207016..5184b73 100644 --- a/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionUseCase.java +++ b/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCase.java @@ -26,7 +26,7 @@ @RequiredArgsConstructor @UseCase -public class GenerateAllAdmissionUseCase { +public class GenerateAllAdmissionTicketUseCase { private final FormRepository formRepository; private final ProcessTemplateService processTemplateService; diff --git a/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java b/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java index 9360bfd..459e7b8 100644 --- a/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java +++ b/src/main/java/com/bamdoliro/maru/presentation/form/FormController.java @@ -72,7 +72,7 @@ public class FormController { private final QueryFormUrlUseCase queryFormUrlUseCase; private final SelectSecondPassUseCase selectSecondPassUseCase; private final UpdateOriginalTypeUseCase updateOriginalTypeUseCase; - private final GenerateAllAdmissionUseCase generateAllAdmissionUseCase; + private final GenerateAllAdmissionTicketUseCase generateAllAdmissionTicketUseCase; @ResponseStatus(HttpStatus.CREATED) @PostMapping @@ -232,7 +232,7 @@ public ResponseEntity generateAllAdmissionTicket( ) { return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) - .body(generateAllAdmissionUseCase.execute()); + .body(generateAllAdmissionTicketUseCase.execute()); } @GetMapping("/proof-of-application") From 9711caf3ca77356c26dd73619f6362423f84391f Mon Sep 17 00:00:00 2001 From: jyj1289 Date: Mon, 21 Oct 2024 17:00:40 +0900 Subject: [PATCH 3/5] =?UTF-8?q?test(#163):=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20-=20Controller=EC=99=80=20UseCase=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=96=88=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...GenerateAllAdmissionTicketUseCaseTest.java | 71 +++++++++++++++++++ .../presentation/form/FormControllerTest.java | 40 ++++++++++- .../maru/shared/util/ControllerTest.java | 3 + 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCaseTest.java diff --git a/src/test/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCaseTest.java b/src/test/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCaseTest.java new file mode 100644 index 0000000..9554c49 --- /dev/null +++ b/src/test/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCaseTest.java @@ -0,0 +1,71 @@ +package com.bamdoliro.maru.application.form; + +import com.bamdoliro.maru.domain.form.domain.Form; +import com.bamdoliro.maru.domain.form.domain.type.FormStatus; +import com.bamdoliro.maru.domain.form.domain.type.FormType; +import com.bamdoliro.maru.infrastructure.pdf.GeneratePdfService; +import com.bamdoliro.maru.infrastructure.pdf.MergePdfService; +import com.bamdoliro.maru.infrastructure.persistence.form.FormRepository; +import com.bamdoliro.maru.infrastructure.s3.FileService; +import com.bamdoliro.maru.infrastructure.thymeleaf.ProcessTemplateService; +import com.bamdoliro.maru.shared.fixture.FormFixture; +import com.bamdoliro.maru.shared.fixture.SharedFixture; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class GenerateAllAdmissionTicketUseCaseTest { + + @InjectMocks + private GenerateAllAdmissionTicketUseCase generateAllAdmissionTicketUseCase; + + @Mock + private ProcessTemplateService processTemplateService; + + @Mock + private GeneratePdfService generatePdfService; + + @Mock + private FileService fileService; + + @Mock + private MergePdfService mergePdfService; + + @Mock + private FormRepository formRepository; + + @Test + void 모든_1차_합격자의_수험표를_생성한다() { + // given + List formList = new ArrayList<>(); + formList.add(FormFixture.createForm(FormType.REGULAR)); + formList.add(FormFixture.createForm(FormType.MEISTER_TALENT)); + formList.forEach(Form::firstPass); + given(formRepository.findByStatus(FormStatus.FIRST_PASSED)).willReturn(formList); + given(processTemplateService.execute(any(String.class), any(Map.class))).willReturn("html"); + given(fileService.getPresignedUrl(any(String.class), any(String.class))).willReturn(SharedFixture.createFormUrlResponse()); + given(generatePdfService.execute(any(String.class))).willReturn(new ByteArrayOutputStream()); + + // when + generateAllAdmissionTicketUseCase.execute(); + + //then + verify(formRepository, times(1)).findByStatus(FormStatus.FIRST_PASSED); + verify(processTemplateService, times(2)).execute(any(String.class), any(Map.class)); + verify(generatePdfService, times(2)).execute(any(String.class)); + verify(fileService, times(2)).getPresignedUrl(any(String.class), any(String.class)); + } +} diff --git a/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java b/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java index 226bd8d..8d0824b 100644 --- a/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/presentation/form/FormControllerTest.java @@ -1438,11 +1438,45 @@ class FormControllerTest extends RestDocsTestSupport { .andExpect(status().isNotFound()) - .andDo(restDocs.document()); + .andDo(restDocs.document( + requestHeaders( + headerWithName(HttpHeaders.AUTHORIZATION) + .description("Bearer token") + ) + )); verify(generateAdmissionTicketUseCase, times(1)).execute(user); } + @Test + void 수험표_전체를_발급받는다() throws Exception { + User user = UserFixture.createAdminUser(); + MockMultipartFile file = new MockMultipartFile( + "admission-ticket-all", + "admission-ticket-all.pdf", + MediaType.APPLICATION_PDF_VALUE, + "<>".getBytes() + ); + given(authenticationArgumentResolver.supportsParameter(any(MethodParameter.class))).willReturn(true); + given(authenticationArgumentResolver.resolveArgument(any(), any(), any(), any())).willReturn(user); + given(generateAllAdmissionTicketUseCase.execute()).willReturn(new ByteArrayResource(file.getBytes())); + + mockMvc.perform(get("/form/admission-ticket/all") + .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader()) + .accept(MediaType.APPLICATION_PDF)) + + .andExpect(status().isOk()) + + .andDo(restDocs.document( + requestHeaders( + headerWithName(HttpHeaders.AUTHORIZATION) + .description("Bearer token") + ) + )); + + verify(generateAllAdmissionTicketUseCase, times(1)).execute(); + } + @Test void 접수증을_발급받는다() throws Exception { User user = UserFixture.createUser(); @@ -1868,7 +1902,7 @@ class FormControllerTest extends RestDocsTestSupport { willDoNothing().given(selectSecondPassUseCase).execute(); mockMvc.perform(patch("/form/second-round/select") - .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader())) + .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader())) .andExpect(status().isNoContent()) @@ -1890,7 +1924,7 @@ class FormControllerTest extends RestDocsTestSupport { willThrow(new MissingTotalScoreException()).given(selectSecondPassUseCase).execute(); mockMvc.perform(patch("/form/second-round/select") - .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader())) + .header(HttpHeaders.AUTHORIZATION, AuthFixture.createAuthHeader())) .andExpect(status().isConflict()) diff --git a/src/test/java/com/bamdoliro/maru/shared/util/ControllerTest.java b/src/test/java/com/bamdoliro/maru/shared/util/ControllerTest.java index 120fc17..69db6a5 100644 --- a/src/test/java/com/bamdoliro/maru/shared/util/ControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/shared/util/ControllerTest.java @@ -265,6 +265,9 @@ public abstract class ControllerTest { @MockBean protected UpdateOriginalTypeUseCase updateOriginalTypeUseCase; + @MockBean + protected GenerateAllAdmissionTicketUseCase generateAllAdmissionTicketUseCase; + protected String toJson(Object object) throws JsonProcessingException { return objectMapper.writeValueAsString(object); } From 9c65dd9fa2ea204e7766ed8b33248919b692ea72 Mon Sep 17 00:00:00 2001 From: jyj1289 Date: Mon, 21 Oct 2024 17:01:05 +0900 Subject: [PATCH 4/5] =?UTF-8?q?docs(#163):=20=EC=88=98=ED=97=98=ED=91=9C?= =?UTF-8?q?=20=EC=A0=84=EC=B2=B4=20=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?API=20=EB=AC=B8=EC=84=9C=ED=99=94=20-=20=EC=88=98=ED=97=98?= =?UTF-8?q?=ED=91=9C=20=EC=A0=84=EC=B2=B4=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20API=EB=A5=BC=20=EB=AC=B8=EC=84=9C=ED=99=94=ED=96=88?= =?UTF-8?q?=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/form.adoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/docs/asciidoc/form.adoc b/src/docs/asciidoc/form.adoc index 635ad53..51a939a 100644 --- a/src/docs/asciidoc/form.adoc +++ b/src/docs/asciidoc/form.adoc @@ -420,6 +420,22 @@ include::{snippets}/form-controller-test/수험표를_발급받을_때_원서를 ===== 불합격한 경우 include::{snippets}/form-controller-test/수험표를_발급받을_때_불합격자라면_에러가_발생한다/http-response.adoc[] +=== 수험표 전체 발급 +어드민은 1차 전형에서 합격한 전체 유저의 수험표를 발급받을 수 있습니다. + +==== 요청 형식 + +===== Request Header +include::{snippets}/form-controller-test/수험표_전체를_발급받는다/request-headers.adoc[] + +==== 요청 +include::{snippets}/form-controller-test/수험표_전체를_발급받는다/http-request.adoc[] + +==== 응답 + +===== 정상 응답 +include::{snippets}/form-controller-test/수험표_전체를_발급받는다/http-response.adoc[]ㅌ + === 접수증 발급 원서를 최종 제출한 유저는 접수증을 발급받을 수 있습니다. From b3d60ec0c95d296b51930c9bd6631b108283af1a Mon Sep 17 00:00:00 2001 From: jyj1289 Date: Mon, 21 Oct 2024 17:10:43 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix(#163):=20=EC=98=A4=ED=83=80=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/form.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/asciidoc/form.adoc b/src/docs/asciidoc/form.adoc index 51a939a..8aef113 100644 --- a/src/docs/asciidoc/form.adoc +++ b/src/docs/asciidoc/form.adoc @@ -434,7 +434,7 @@ include::{snippets}/form-controller-test/수험표_전체를_발급받는다/htt ==== 응답 ===== 정상 응답 -include::{snippets}/form-controller-test/수험표_전체를_발급받는다/http-response.adoc[]ㅌ +include::{snippets}/form-controller-test/수험표_전체를_발급받는다/http-response.adoc[] === 접수증 발급 원서를 최종 제출한 유저는 접수증을 발급받을 수 있습니다.