diff --git a/src/docs/asciidoc/form.adoc b/src/docs/asciidoc/form.adoc index 635ad538..8aef113d 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[] + === 접수증 발급 원서를 최종 제출한 유저는 접수증을 발급받을 수 있습니다. diff --git a/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCase.java b/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCase.java new file mode 100644 index 00000000..5184b730 --- /dev/null +++ b/src/main/java/com/bamdoliro/maru/application/form/GenerateAllAdmissionTicketUseCase.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 GenerateAllAdmissionTicketUseCase { + + 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 d501a29a..459e7b88 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 GenerateAllAdmissionTicketUseCase generateAllAdmissionTicketUseCase; @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(generateAllAdmissionTicketUseCase.execute()); + } + @GetMapping("/proof-of-application") public ResponseEntity generateProofOfApplication( @AuthenticationPrincipal(authority = Authority.USER) User user 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 00000000..9554c49b --- /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 226bd8d0..8d0824bf 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 120fc178..69db6a5d 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); }