From 9ee7acd9d16e59224ee5184c89374f06d6592ad3 Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期日, 04 十月 2026 17:47:34 +0800
Subject: [PATCH] feat(holiday): 新增节假日综合运输客流量走势图

---
 traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendWordServiceTest.java |  143 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 143 insertions(+), 0 deletions(-)

diff --git a/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendWordServiceTest.java b/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendWordServiceTest.java
new file mode 100644
index 0000000..d973be0
--- /dev/null
+++ b/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendWordServiceTest.java
@@ -0,0 +1,143 @@
+package com.trafficaudit.holiday.service;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class HolidayTrendWordServiceTest {
+
+    @Test
+    void rebuildsTemplateWithSectionsChartsAndAppendix() throws Exception {
+        Path template = resolveTemplate();
+        byte[] templateBytes = Files.readAllBytes(template);
+        HolidayTrendWordService service = new HolidayTrendWordService();
+
+        List<HolidayTrendWordService.DocumentBlock> blocks = new ArrayList<>();
+        blocks.add(new HolidayTrendWordService.SectionBlock("涓�銆佹�讳綋鎯呭喌"));
+        blocks.add(new HolidayTrendWordService.ChartBlock("瀹㈡祦鎬婚噺", png(1),
+                new HolidayTrendWordService.ImageSize(5000000L, 3000000L)));
+        blocks.add(new HolidayTrendWordService.ChartBlock("瀹㈡祦鍗犳瘮", png(2), true,
+                new HolidayTrendWordService.ImageSize(4000000L, 2500000L)));
+        blocks.add(new HolidayTrendWordService.PageBreakBlock());
+        blocks.add(new HolidayTrendWordService.SectionBlock("闄勫綍涓� 姣忔棩瀹㈡祦鏋勬垚鍙樺寲"));
+        blocks.add(new HolidayTrendWordService.ChartBlock("姣忔棩鏋勬垚", png(3), true));
+
+        byte[] result = service.generate(templateBytes, 2026, blocks);
+        List<String> entries = entries(result);
+        assertTrue(entries.contains("word/media/image1.png"));
+        assertTrue(entries.contains("word/media/image2.png"));
+        assertTrue(entries.contains("word/media/image3.png"));
+        assertEquals(3, entries.stream().filter(name -> name.endsWith(".png")).count());
+
+        String documentXml = readEntry(result, "word/document.xml");
+        assertTrue(documentXml.contains("涓�銆佹�讳綋鎯呭喌"));
+        assertTrue(documentXml.contains("闄勫綍涓� 姣忔棩瀹㈡祦鏋勬垚鍙樺寲"));
+        assertTrue(documentXml.contains("瀹㈡祦鎬婚噺"));
+        assertTrue(documentXml.contains("姣忔棩鏋勬垚"));
+        assertTrue(!paragraphHasNumbering(documentXml, "涓�銆佹�讳綋鎯呭喌"));
+        assertTrue(!paragraphHasNumbering(documentXml, "闄勫綍涓� 姣忔棩瀹㈡祦鏋勬垚鍙樺寲"));
+        assertTrue(documentXml.contains("cx=\"5000000\" cy=\"3000000\""));
+        assertTrue(documentXml.contains("cx=\"4000000\" cy=\"2500000\""));
+        assertEquals(2, pageBreakCount(documentXml));
+
+        String rels = readEntry(result, "word/_rels/document.xml.rels");
+        assertTrue(rels.contains("rId100"));
+        assertTrue(rels.contains("rId102"));
+    }
+
+    private Path resolveTemplate() {
+        Path direct = Paths.get("docs/涓撻」缁熻/閲嶈鑺傚亣鏃�/2026骞村崄涓�鍋囨湡鍏ㄧ渷缁煎悎杩愯緭瀹㈡祦閲忚蛋鍔垮浘.docx");
+        if (Files.exists(direct)) return direct;
+        return Paths.get("..", "docs", "涓撻」缁熻", "閲嶈鑺傚亣鏃�", "2026骞村崄涓�鍋囨湡鍏ㄧ渷缁煎悎杩愯緭瀹㈡祦閲忚蛋鍔垮浘.docx");
+    }
+
+    private boolean paragraphHasNumbering(String documentXml, String text) throws Exception {
+        javax.xml.parsers.DocumentBuilderFactory factory =
+                javax.xml.parsers.DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        org.w3c.dom.Document document = factory.newDocumentBuilder().parse(
+                new java.io.ByteArrayInputStream(documentXml.getBytes("UTF-8")));
+        org.w3c.dom.NodeList paragraphs = document.getElementsByTagNameNS(
+                "http://schemas.openxmlformats.org/wordprocessingml/2006/main", "p");
+        for (int i = 0; i < paragraphs.getLength(); i++) {
+            org.w3c.dom.Element paragraph = (org.w3c.dom.Element) paragraphs.item(i);
+            StringBuilder builder = new StringBuilder();
+            org.w3c.dom.NodeList texts = paragraph.getElementsByTagNameNS(
+                    "http://schemas.openxmlformats.org/wordprocessingml/2006/main", "t");
+            for (int j = 0; j < texts.getLength(); j++) {
+                builder.append(texts.item(j).getTextContent());
+            }
+            if (text.equals(builder.toString())) {
+                return paragraph.getElementsByTagNameNS(
+                        "http://schemas.openxmlformats.org/wordprocessingml/2006/main", "numPr")
+                        .getLength() > 0;
+            }
+        }
+        throw new AssertionError("Paragraph not found: " + text);
+    }
+
+    private int pageBreakCount(String documentXml) throws Exception {
+        javax.xml.parsers.DocumentBuilderFactory factory =
+                javax.xml.parsers.DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        org.w3c.dom.Document document = factory.newDocumentBuilder().parse(
+                new java.io.ByteArrayInputStream(documentXml.getBytes("UTF-8")));
+        org.w3c.dom.NodeList breaks = document.getElementsByTagNameNS(
+                "http://schemas.openxmlformats.org/wordprocessingml/2006/main", "br");
+        int count = 0;
+        for (int i = 0; i < breaks.getLength(); i++) {
+            org.w3c.dom.Element br = (org.w3c.dom.Element) breaks.item(i);
+            if ("page".equals(br.getAttributeNS(
+                    "http://schemas.openxmlformats.org/wordprocessingml/2006/main", "type"))) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private byte[] png(int seed) {
+        byte[] bytes = new byte[256];
+        Arrays.fill(bytes, (byte) seed);
+        bytes[0] = (byte) 0x89;
+        bytes[1] = 0x50;
+        bytes[2] = 0x4e;
+        bytes[3] = 0x47;
+        return bytes;
+    }
+
+    private List<String> entries(byte[] zipBytes) throws Exception {
+        List<String> result = new ArrayList<>();
+        try (ZipInputStream input = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {
+            ZipEntry entry;
+            while ((entry = input.getNextEntry()) != null) result.add(entry.getName());
+        }
+        return result;
+    }
+
+    private String readEntry(byte[] zipBytes, String name) throws Exception {
+        try (ZipInputStream input = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {
+            ZipEntry entry;
+            while ((entry = input.getNextEntry()) != null) {
+                if (name.equals(entry.getName())) {
+                    java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
+                    byte[] buffer = new byte[1024];
+                    int length;
+                    while ((length = input.read(buffer)) > 0) out.write(buffer, 0, length);
+                    return out.toString("UTF-8");
+                }
+            }
+        }
+        return "";
+    }
+}

--
Gitblit v1.9.1