优化
This commit is contained in:
@@ -3,7 +3,9 @@ package com.baiyue.print.controller;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.common.web.GlobalExceptionHandler;
|
||||
import com.baiyue.print.dto.FreightA4PdfDtos;
|
||||
import com.baiyue.print.dto.InboundMarkPdfDtos;
|
||||
import com.baiyue.print.service.FreightA4PdfService;
|
||||
import com.baiyue.print.service.InboundMarkPdfService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -38,6 +40,8 @@ class InternalPdfControllerTest {
|
||||
private static final String TOKEN = "0123456789abcdef0123456789abcdef";
|
||||
@MockBean
|
||||
private InboundMarkPdfService service;
|
||||
@MockBean
|
||||
private FreightA4PdfService freightA4PdfService;
|
||||
@Resource
|
||||
private InternalPdfController controller;
|
||||
private MockMvc mockMvc;
|
||||
@@ -127,6 +131,28 @@ class InternalPdfControllerTest {
|
||||
assertThat(captor.getValue().downloadFileName()).isEqualTo("inbound-marks-request-123.pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsFreightA4PdfWithDownloadHeaders() throws Exception {
|
||||
byte[] pdf = "%PDF-a4-test".getBytes(StandardCharsets.US_ASCII);
|
||||
when(freightA4PdfService.generate(any())).thenReturn(new FreightA4PdfService.GeneratedPdf(
|
||||
pdf, "BYSE2607044-托运书.pdf", 1));
|
||||
|
||||
mockMvc.perform(post("/internal/print/v1/pdfs/freight-a4-documents")
|
||||
.header(InternalPdfController.INTERNAL_TOKEN_HEADER, TOKEN)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_PDF, MediaType.APPLICATION_JSON)
|
||||
.content(validFreightA4RequestJson()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_PDF))
|
||||
.andExpect(content().bytes(pdf))
|
||||
.andExpect(header().string("Cache-Control", "no-store"))
|
||||
.andExpect(header().string("X-Print-Page-Count", "1"))
|
||||
.andExpect(header().string("Content-Disposition",
|
||||
org.hamcrest.Matchers.containsString("filename*=")));
|
||||
|
||||
verify(freightA4PdfService).generate(any(FreightA4PdfDtos.Request.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointDoesNotPinProducedMediaTypeToPdf() throws Exception {
|
||||
PostMapping mapping = InternalPdfController.class
|
||||
@@ -142,6 +168,7 @@ class InternalPdfControllerTest {
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertThat(contract)
|
||||
.contains("/internal/print/v1/pdfs/inbound-marks:")
|
||||
.contains("/internal/print/v1/pdfs/freight-a4-documents:")
|
||||
.contains("application/pdf:")
|
||||
.contains("application/json:")
|
||||
.contains("name: X-Internal-Service-Token")
|
||||
@@ -165,4 +192,29 @@ class InternalPdfControllerTest {
|
||||
""";
|
||||
}
|
||||
|
||||
private String validFreightA4RequestJson() {
|
||||
return """
|
||||
{
|
||||
"templateCode": "SEA_FREIGHT_SHIPPING_INSTRUCTION_A4_V1",
|
||||
"pageSpec": {"widthMm": 210, "heightMm": 297},
|
||||
"downloadFileName": "BYSE2607044-托运书.pdf",
|
||||
"documents": [{
|
||||
"fields": {
|
||||
"toCompany": "宁波胜泽远航国际物流有限公司",
|
||||
"shipper": "ZHEJIANG BAIYUE TRADE CO.,LIMITED",
|
||||
"jobNumber": "BYSE2607044"
|
||||
},
|
||||
"cargoRows": [{
|
||||
"marksAndNos": "N/M",
|
||||
"quantity": "700",
|
||||
"packaging": "PACKAGES",
|
||||
"descriptionOfGoods": "CAR ACCESSORIES",
|
||||
"grossWeight": "28000.00 KGS",
|
||||
"measurement": "28.000 CBM"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,19 @@ class PrintRequestSizeFilterTest {
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void alsoCapsFreightA4DocumentRequests() throws Exception {
|
||||
MockHttpServletRequest request = request("12345678901", true,
|
||||
"/internal/print/v1/pdfs/freight-a4-documents");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
AtomicReference<Boolean> called = new AtomicReference<>(false);
|
||||
|
||||
filter.doFilter(request, response, (ignoredRequest, ignoredResponse) -> called.set(true));
|
||||
|
||||
assertThat(called).hasValue(false);
|
||||
assertTooLarge(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsOversizedDeclaredContentLengthWithoutCallingDownstream() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest() {
|
||||
@@ -68,17 +81,25 @@ class PrintRequestSizeFilterTest {
|
||||
}
|
||||
|
||||
private MockHttpServletRequest request(String content, boolean unknownLength) {
|
||||
return request(content, unknownLength, "/internal/print/v1/pdfs/inbound-marks");
|
||||
}
|
||||
|
||||
private MockHttpServletRequest request(String content, boolean unknownLength, String path) {
|
||||
MockHttpServletRequest request = unknownLength ? new MockHttpServletRequest() {
|
||||
@Override public int getContentLength() { return -1; }
|
||||
@Override public long getContentLengthLong() { return -1; }
|
||||
} : new MockHttpServletRequest();
|
||||
initialize(request, content);
|
||||
initialize(request, content, path);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void initialize(MockHttpServletRequest request, String content) {
|
||||
initialize(request, content, "/internal/print/v1/pdfs/inbound-marks");
|
||||
}
|
||||
|
||||
private void initialize(MockHttpServletRequest request, String content, String path) {
|
||||
request.setMethod("POST");
|
||||
request.setRequestURI("/internal/print/v1/pdfs/inbound-marks");
|
||||
request.setRequestURI(path);
|
||||
request.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
request.setAttribute("requestId", "request-123");
|
||||
request.setAttribute("traceId", "trace-123");
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.baiyue.print.service;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.baiyue.print.config.PrintPdfProperties;
|
||||
import com.baiyue.print.dto.FreightA4PdfDtos;
|
||||
import com.baiyue.print.renderer.FreightA4Renderer;
|
||||
import com.baiyue.print.renderer.PdfUnits;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
@SpringJUnitConfig(classes = {FreightA4PdfService.class, PrintPdfProperties.class,
|
||||
PdfGenerationLimiter.class, FreightA4Renderer.class})
|
||||
class FreightA4PdfServiceGenerationTest {
|
||||
@Resource
|
||||
private FreightA4PdfService service;
|
||||
|
||||
@Test
|
||||
void generatesShippingInstructionAsStrictA4Pdf() throws IOException {
|
||||
FreightA4PdfService.GeneratedPdf generated = service.generate(request(
|
||||
FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
"BYSE2607044-shipping-instruction.pdf",
|
||||
shippingDocument()));
|
||||
|
||||
assertThat(generated.fileName()).isEqualTo("BYSE2607044-shipping-instruction.pdf");
|
||||
assertThat(generated.pageCount()).isEqualTo(1);
|
||||
assertThat(generated.content()).startsWith("%PDF-".getBytes(java.nio.charset.StandardCharsets.US_ASCII));
|
||||
try (PDDocument document = Loader.loadPDF(generated.content())) {
|
||||
assertA4(document);
|
||||
String text = new PDFTextStripper().getText(document);
|
||||
assertThat(text).contains("Sea Freight Shipping Instructions", "Job Nbr",
|
||||
"BYSE2607044", "CAR ACCESSORIES", "SAY SIX HUNDRED AND FIFTY NINE PACKAGES ONLY.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesBillConfirmationAsStrictA4Pdf() throws IOException {
|
||||
FreightA4PdfService.GeneratedPdf generated = service.generate(request(
|
||||
FreightA4PdfDtos.BILL_CONFIRMATION_TEMPLATE,
|
||||
"BYSE2607044-bill-confirmation.pdf",
|
||||
billConfirmationDocument()));
|
||||
|
||||
assertThat(generated.pageCount()).isEqualTo(1);
|
||||
try (PDDocument document = Loader.loadPDF(generated.content())) {
|
||||
assertA4(document);
|
||||
String text = new PDFTextStripper().getText(document);
|
||||
assertThat(text).contains("WHY074G504054", "D R A F T", "SAID TO CONTAIN(S)",
|
||||
"WHSU2884669/WHLX730243/20GP/1028PACKAGES/28000KGS/ 28CBM");
|
||||
}
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Request request(String templateCode, String fileName,
|
||||
FreightA4PdfDtos.Document document) {
|
||||
return new FreightA4PdfDtos.Request(templateCode,
|
||||
new FreightA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297.0")),
|
||||
fileName, List.of(document));
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Document shippingDocument() {
|
||||
return new FreightA4PdfDtos.Document(Map.ofEntries(
|
||||
Map.entry("toCompany", "宁波胜泽远航国际物流有限公司"),
|
||||
Map.entry("issuerNameCn", "浙江柏悦贸易有限公司(香港)"),
|
||||
Map.entry("issuerNameEn", "ZHEJIANG BAIYUE TRADE CO.,LIMITED"),
|
||||
Map.entry("issuerAddress", "地址:浙江省义乌市稠州北路 1399 号新光国际 8613 室"),
|
||||
Map.entry("issuerPostalCode", "322000"),
|
||||
Map.entry("issuerTel", "0579-85875557"),
|
||||
Map.entry("issuerFax", "0579-85601277"),
|
||||
Map.entry("shipper", "ZHEJIANG BAIYUE TRADE CO.,LIMITED\nROOM 8613,NO.1399,CHOUZHOU NORTH ROAD,FUTIAN STREET,YIWU CITY,ZHEJIANG PROVINCE\nTEL:0579-85695557"),
|
||||
Map.entry("consignee", "SANATRK FOR IMPORT AND TRADE OF AUTO SPARE PARTS\n3 ATIA ELZEKRY ST- OMAR EBN ELKHTAB CITY SHOBRA\nELKHIMA QALYUBIA SECOND-OWNER/ MENA MATTA GABALLA\nMATTA\nTEL:01225672354"),
|
||||
Map.entry("notifyParty", "SAME AS CONSIGNEE"),
|
||||
Map.entry("picName", "苏天翔"),
|
||||
Map.entry("jobNumber", "BYSE2607044"),
|
||||
Map.entry("etdDate", "2026/07/24"),
|
||||
Map.entry("serviceTerm", "CY-CY"),
|
||||
Map.entry("freightTerm", "FREIGHT PREPAID"),
|
||||
Map.entry("cargoQuantity", "1*20GP"),
|
||||
Map.entry("portOfLoading", "NINGBO"),
|
||||
Map.entry("portOfDischarge", "EL DEKHEILA"),
|
||||
Map.entry("placeOfDelivery", "EL DEKHEILA"),
|
||||
Map.entry("totalPackagesInWords", "SAY SIX HUNDRED AND FIFTY NINE PACKAGES ONLY."),
|
||||
Map.entry("carrier", "万海航运(香港)股份有限公司"),
|
||||
Map.entry("bookingAgent", "宁波胜泽远航国际物流有限公司"),
|
||||
Map.entry("printDate", "2026 年 07 月 16 日")
|
||||
), List.of(new FreightA4PdfDtos.CargoRow("N/M", "700", "PACKAGES",
|
||||
"CAR ACCESSORIES\nACID: 4120661061019510011\n申请目的港 21 天免用箱",
|
||||
"28000.00 KGS", "28.000 CBM")));
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Document billConfirmationDocument() {
|
||||
return new FreightA4PdfDtos.Document(Map.ofEntries(
|
||||
Map.entry("shipper", "ZHEJIANG BAIYUE TRADE CO.,LIMITED\nROOM 8613,NO.1399,CHOUZHOU NORTH ROAD,FUTIAN\nSTREET,YIWU CITY,ZHEJIANG PROVINCE\nTEL:0579-85695557"),
|
||||
Map.entry("consignee", "SANATRK FOR IMPORT AND TRADE OF AUTO SPARE PARTS\n3 ATIA ELZEKRY ST- OMAR EBN ELKHTAB CITY SHOBRA\nELKHIMA QALYUBIA SECOND-OWNER/ MENA MATTA GABALLA\nMATTA\nTEL:01225672354"),
|
||||
Map.entry("notifyParty", "SAME AS CONSIGNEE"),
|
||||
Map.entry("bookingNumber", "WHY074G504054"),
|
||||
Map.entry("ensAmsCutoffDate", "2026/07/20"),
|
||||
Map.entry("expectedDepartureDate", "2026/07/24"),
|
||||
Map.entry("oceanVesselVoyage", "WAN HAI 622 W027"),
|
||||
Map.entry("portOfLoading", "NINGBO"),
|
||||
Map.entry("portOfDischarge", "EL DEKHEILA"),
|
||||
Map.entry("placeOfDelivery", "EL DEKHEILA"),
|
||||
Map.entry("finalDestination", "EL DEKHEILA"),
|
||||
Map.entry("serviceTerm", "CY-CY"),
|
||||
Map.entry("freightTerm", "FREIGHT PREPAID"),
|
||||
Map.entry("shipperLoadCountSeal", "SHPR'S LOAD,COUNT AND SEAL"),
|
||||
Map.entry("containerSpec", "1*20GP"),
|
||||
Map.entry("shippedOnBoard", "24-JUL-2026"),
|
||||
Map.entry("draftText", "D R A F T"),
|
||||
Map.entry("totalPackagesInWords", "SAY ONE THOUSAND AND TWENTY EIGHT PACKAGES ONLY."),
|
||||
Map.entry("containerSummary", "WHSU2884669/WHLX730243/20GP/1028PACKAGES/28000KGS/ 28CBM"),
|
||||
Map.entry("notes", "备注:1.多个箱子请分别提供分箱数,分箱品名,若未指出默认品名混装.\n2.请在 ENS/AMS 规定时间内确认好提单内容,否则有上了不船的风险."),
|
||||
Map.entry("issuePlace", "NINGBO"),
|
||||
Map.entry("issueDate", "24-JUL-2026")
|
||||
), List.of(new FreightA4PdfDtos.CargoRow("N/M", "1028", "PACKAGES",
|
||||
"AUTO SPARE PARTS\nHS CODE 870899\nACID: 4120661061019510011\nEGYPTIAN IMPORTER VAT NUMBER:412066106\nEXPORTER REGISTRATION\nNUMBER:CN-02-91330782MA2E62HK3Y\n申请目的港 14 天免用箱",
|
||||
"28000.00 KGS", "28.000 CBM")));
|
||||
}
|
||||
|
||||
private void assertA4(PDDocument document) {
|
||||
assertThat(document.getNumberOfPages()).isEqualTo(1);
|
||||
assertThat(document.getPage(0).getMediaBox().getWidth()).isCloseTo(PdfUnits.mmToPt(210),
|
||||
org.assertj.core.data.Offset.offset(0.002f));
|
||||
assertThat(document.getPage(0).getMediaBox().getHeight()).isCloseTo(PdfUnits.mmToPt(297),
|
||||
org.assertj.core.data.Offset.offset(0.002f));
|
||||
assertThat(document.getPage(0).getRotation()).isZero();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.baiyue.print.service;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.print.config.PrintPdfProperties;
|
||||
import com.baiyue.print.dto.FreightA4PdfDtos;
|
||||
import com.baiyue.print.renderer.FreightA4Renderer;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
@SpringJUnitConfig(classes = {FreightA4PdfService.class, PrintPdfProperties.class})
|
||||
class FreightA4PdfServiceValidationTest {
|
||||
@MockBean private PdfGenerationLimiter limiter;
|
||||
@MockBean private FreightA4Renderer renderer;
|
||||
@jakarta.annotation.Resource private FreightA4PdfService service;
|
||||
|
||||
@Test
|
||||
void rejectsUnsupportedTemplateCode() {
|
||||
assertValidationFailure(new FreightA4PdfDtos.Request("UNKNOWN",
|
||||
pageSpec(), "a4.pdf", List.of(document())), "templateCode");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNonA4PageSize() {
|
||||
assertValidationFailure(new FreightA4PdfDtos.Request(
|
||||
FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
new FreightA4PdfDtos.PageSpec(new BigDecimal("100"), new BigDecimal("100")),
|
||||
"a4.pdf", List.of(document())), "pageSpec.widthMm");
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsMultilineDocumentFields() {
|
||||
FreightA4PdfDtos.Request request = new FreightA4PdfDtos.Request(
|
||||
FreightA4PdfDtos.SHIPPING_INSTRUCTION_TEMPLATE,
|
||||
pageSpec(), "a4.pdf", List.of(document()));
|
||||
|
||||
assertThatCode(() -> service.generate(request)).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
private void assertValidationFailure(FreightA4PdfDtos.Request request, String messagePart) {
|
||||
assertThatThrownBy(() -> service.generate(request))
|
||||
.isInstanceOfSatisfying(BizException.class, exception -> {
|
||||
assertThat(exception.getResultCode()).isEqualTo(ResultCode.VALIDATION_FAILED);
|
||||
assertThat(exception.getMessage()).contains(messagePart);
|
||||
});
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.PageSpec pageSpec() {
|
||||
return new FreightA4PdfDtos.PageSpec(new BigDecimal("210"), new BigDecimal("297"));
|
||||
}
|
||||
|
||||
private FreightA4PdfDtos.Document document() {
|
||||
return new FreightA4PdfDtos.Document(Map.of("shipper", "A\nB"),
|
||||
List.of(new FreightA4PdfDtos.CargoRow("N/M", "1", "PACKAGES", "GOODS", "1 KGS", "1 CBM")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user