From b14fec1599277629e10b2bc686f02fd0c2e0bec1 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期五, 18 九月 2026 16:46:30 +0800
Subject: [PATCH] fix(货运月报): 订正持续在规企业累计运距同比基期口径(v2)
---
traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 161 insertions(+), 14 deletions(-)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
index 1f6ca46..19191bd 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
@@ -60,6 +60,10 @@
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import cn.hutool.core.io.IoUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -3319,10 +3323,36 @@
Map<String, Double> orders = new LinkedHashMap<>();
double[] sumHolder = new double[]{0.0};
String docPeriod;
- try (java.io.InputStream in = file.getInputStream();
- XWPFDocument doc = new XWPFDocument(in)) {
- docPeriod = periodOfDocx(doc);
- parseWycOrderReportTables(doc, orders, sumHolder);
+ byte[] bytes;
+ try (java.io.InputStream in = file.getInputStream()) {
+ bytes = IoUtil.readBytes(in);
+ }
+ boolean isZip = bytes.length > 3 && (bytes[0] & 0xFF) == 0x50 && (bytes[1] & 0xFF) == 0x4B;
+ boolean isWordMl = !isZip && looksLikeWordMl(bytes);
+ try {
+ if (isZip && !isWordPackage(bytes)) {
+ throw new RuntimeException("杩欎笉鏄� Word 鏂囨。锛堝帇缂╁寘閲屾病鏈� word/document.xml锛夛紝"
+ + "澶氬崐鏄� Excel 鎴栧叾瀹冩牸寮忋�傝涓婁紶缃戠害杞﹁鍗曞垎鏋愭姤鍛婄殑 .docx锛堟垨 Word 2003 XML锛�");
+ }
+ if (isZip) {
+ try (XWPFDocument doc = new XWPFDocument(new java.io.ByteArrayInputStream(bytes))) {
+ docPeriod = periodOfParagraphs(paragraphsOfDocx(doc));
+ parseWycOrderReportTables(doc, orders, sumHolder);
+ }
+ } else if (isWordMl) {
+ // 缃戠害杞︾洃绠″钩鍙颁笅鍙戠殑鎶ュ憡甯歌 WPS/Word 瀛樻垚銆學ord 2003 XML锛堝钩閾� XML锛夈�嶅悗鍐嶆敼鍚� .docx锛�
+ // 杩欑鏂囦欢涓嶆槸 zip 鍖咃紝XWPFDocument 鎵撲笉寮�锛堟姤 "File is not a zip file"锛夛紝闇�鎸� XML 鐩存帴瑙f瀽銆�
+ String xml = new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
+ Document dom = parseXmlSafely(xml);
+ docPeriod = periodOfParagraphs(paragraphsOfDocxXml(dom));
+ parseWycOrderReportTablesXml(dom, orders, sumHolder);
+ } else if (isOle2(bytes)) {
+ throw new RuntimeException("杩欐槸 Word 97-2003 鑰佹牸寮忥紙.doc锛夛紝璇风敤 WPS/Word 鎵撳紑鍚庛�屽彟瀛樹负銆峝ocx 鏍煎紡鍐嶅鍏�");
+ } else {
+ throw new RuntimeException("鏃犳硶璇嗗埆鐨勬枃浠舵牸寮忥細鏃笉鏄� docx锛坺ip 鍖咃級锛屼篃涓嶆槸 Word 2003 XML 鎴� .doc");
+ }
+ } catch (RuntimeException e) {
+ throw e;
} catch (Exception e) {
throw new RuntimeException("鏃犳硶璇诲彇 Word 鏂囨。锛岃涓婁紶 .docx 鏍煎紡鐨勩�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶紱"
+ "鑻ヤ笂浼犵殑鍏跺疄鏄� Excel/PDF锛岃鏀圭敤瀵瑰簲妯℃澘瀵煎叆銆傦紙鍘熷閿欒锛�" + e.getMessage() + "锛�");
@@ -3388,13 +3418,12 @@
}
/** 鎶ュ憡鏍囬涓殑鎶ヨ〃鏈燂細缃戠害杞﹁涓�2026骞�7鏈� -> 2026-07锛涘彇涓嶅埌杩斿洖 null */
- private String periodOfDocx(XWPFDocument doc) {
+ private String periodOfParagraphs(List<String> paragraphs) {
java.util.regex.Pattern titlePat = java.util.regex.Pattern.compile("缃戠害杞﹁涓歕\s*(\\d{4})\\s*骞碶\s*(\\d{1,2})\\s*鏈�");
java.util.regex.Pattern anyPat = java.util.regex.Pattern.compile("(\\d{4})\\s*骞碶\s*(\\d{1,2})\\s*鏈�");
String fallback = null;
int scanned = 0;
- for (XWPFParagraph para : doc.getParagraphs()) {
- String text = para.getText();
+ for (String text : paragraphs) {
if (text == null) continue;
String flat = text.replaceAll("\\s+", "");
if (flat.isEmpty()) continue;
@@ -3409,6 +3438,92 @@
return fallback;
}
+ /** docx 娈佃惤鏂囨湰锛堜繚鎸佸師濮嬮『搴忥級 */
+ private List<String> paragraphsOfDocx(XWPFDocument doc) {
+ List<String> out = new ArrayList<>();
+ for (XWPFParagraph para : doc.getParagraphs()) out.add(para.getText());
+ return out;
+ }
+
+ /** Word 2003 XML锛圵ordML锛夋钀芥枃鏈細<w:p> 涓嬫墍鏈� <w:t> 鎷兼帴鍚庡帇鎺夌┖鐧� */
+ private List<String> paragraphsOfDocxXml(Document dom) {
+ List<String> out = new ArrayList<>();
+ for (Element p : elementsByLocalName(dom, "p")) out.add(textOfXmlElement(p));
+ return out;
+ }
+
+ /** 鏄惁涓� Word 2003 XML锛圵ordML 骞抽摵 XML锛夆�斺�旇繖绉嶆枃浠跺父琚敼鍚嶆垚 .docx */
+ private boolean looksLikeWordMl(byte[] bytes) {
+ int n = Math.min(bytes.length, 4096);
+ String head = new String(bytes, 0, n, java.nio.charset.StandardCharsets.UTF_8);
+ return head.contains("wordml") && head.contains("<w:wordDocument");
+ }
+
+ /** zip 鍖呭唴鏄惁鍚� word/document.xml锛堢敤浜庡尯鍒� docx 涓� xlsx/鍏跺畠 OPC 鍖咃級 */
+ private boolean isWordPackage(byte[] bytes) {
+ try (java.util.zip.ZipInputStream zin =
+ new java.util.zip.ZipInputStream(new java.io.ByteArrayInputStream(bytes))) {
+ java.util.zip.ZipEntry e;
+ while ((e = zin.getNextEntry()) != null) {
+ String n = e.getName();
+ if ("word/document.xml".equals(n)) return true;
+ }
+ } catch (Exception ignore) {
+ return false;
+ }
+ return false;
+ }
+
+ /** 鏄惁涓� OLE2 澶嶅悎鏂囨。锛圵ord 97-2003 .doc锛� */
+ private boolean isOle2(byte[] b) {
+ return b.length > 8 && (b[0] & 0xFF) == 0xD0 && (b[1] & 0xFF) == 0xCF
+ && (b[2] & 0xFF) == 0x11 && (b[3] & 0xFF) == 0xE0;
+ }
+
+ /** 瀹夊叏瑙f瀽 XML锛氱鐢ㄥ閮ㄥ疄浣�/DTD锛堟姤鍛婃潵鑷閮ㄥ钩鍙帮紝闃� XXE锛� */
+ private Document parseXmlSafely(String xml) throws Exception {
+ javax.xml.parsers.DocumentBuilderFactory f = javax.xml.parsers.DocumentBuilderFactory.newInstance();
+ f.setNamespaceAware(true);
+ try { f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); } catch (Exception ignore) { }
+ try { f.setFeature("http://xml.org/sax/features/external-general-entities", false); } catch (Exception ignore) { }
+ try { f.setFeature("http://xml.org/sax/features/external-parameter-entities", false); } catch (Exception ignore) { }
+ javax.xml.parsers.DocumentBuilder b = f.newDocumentBuilder();
+ b.setEntityResolver(new org.xml.sax.EntityResolver() {
+ @Override
+ public org.xml.sax.InputSource resolveEntity(String publicId, String systemId) {
+ return new org.xml.sax.InputSource(new java.io.StringReader(""));
+ }
+ });
+ return b.parse(new org.xml.sax.InputSource(new java.io.StringReader(xml)));
+ }
+
+ /** 鎸� localName 鍙栧叏閮ㄥ悗浠e厓绱狅紙鍛藉悕绌洪棿鏃犲叧锛屽吋瀹� WordML 涓� docx 鐨� w: 鍓嶇紑锛� */
+ private List<Element> elementsByLocalName(Document dom, String localName) {
+ List<Element> out = new ArrayList<>();
+ NodeList list = dom.getElementsByTagNameNS("*", localName);
+ for (int i = 0; i < list.getLength(); i++) out.add((Element) list.item(i));
+ return out;
+ }
+
+ /** 鍙彇鐩存帴瀛愬厓绱狅紙閬垮厤宓屽琛ㄦ牸/宓屽灞炴�у厓绱犺绠楄繘鏉ワ級 */
+ private List<Element> childElementsByLocalName(Element parent, String localName) {
+ List<Element> out = new ArrayList<>();
+ NodeList kids = parent.getChildNodes();
+ for (int i = 0; i < kids.getLength(); i++) {
+ Node n = kids.item(i);
+ if (n.getNodeType() == Node.ELEMENT_NODE && localName.equals(n.getLocalName())) out.add((Element) n);
+ }
+ return out;
+ }
+
+ /** 鍏冪礌鍐呮墍鏈� <w:t> 鏂囨湰鎷兼帴骞跺帇鎺夌┖鐧� */
+ private String textOfXmlElement(Element el) {
+ StringBuilder sb = new StringBuilder();
+ NodeList ts = el.getElementsByTagNameNS("*", "t");
+ for (int i = 0; i < ts.getLength(); i++) sb.append(ts.item(i).getTextContent());
+ return flat(sb.toString());
+ }
+
private String ymd(String y, String mo) {
try {
int month = Integer.parseInt(mo);
@@ -3421,15 +3536,47 @@
/** 瑙f瀽鎶ュ憡涓殑銆屽湴甯傚悕绉� | 鈥� | 璁㈠崟鏁版�绘暟銆嶈〃锛宱rders=甯傚窞璁㈠崟锛宻umHolder[0]=鍚堣琛岃鍗曟暟 */
private void parseWycOrderReportTables(XWPFDocument doc, Map<String, Double> orders, double[] sumHolder) {
+ List<List<List<String>>> tables = new ArrayList<>();
for (XWPFTable table : doc.getTables()) {
- List<XWPFTableRow> rows = table.getRows();
+ List<List<String>> rows = new ArrayList<>();
+ for (XWPFTableRow row : table.getRows()) {
+ List<String> cells = new ArrayList<>();
+ for (org.apache.poi.xwpf.usermodel.XWPFTableCell c : row.getTableCells()) cells.add(flat(c.getText()));
+ rows.add(cells);
+ }
+ tables.add(rows);
+ }
+ matchWycOrderTable(tables, orders, sumHolder);
+ }
+
+ /** Word 2003 XML锛圵ordML锛夊垎鏀細<w:tbl>/<w:tr>/<w:tc> 鎶芥垚鏂囨湰琛ㄥ悗璧板悓涓�濂楀尮閰嶉�昏緫 */
+ private void parseWycOrderReportTablesXml(Document dom, Map<String, Double> orders, double[] sumHolder) {
+ List<List<List<String>>> tables = new ArrayList<>();
+ for (Element tbl : elementsByLocalName(dom, "tbl")) {
+ List<List<String>> rows = new ArrayList<>();
+ for (Element tr : childElementsByLocalName(tbl, "tr")) {
+ List<String> cells = new ArrayList<>();
+ for (Element tc : childElementsByLocalName(tr, "tc")) cells.add(textOfXmlElement(tc));
+ rows.add(cells);
+ }
+ tables.add(rows);
+ }
+ matchWycOrderTable(tables, orders, sumHolder);
+ }
+
+ /**
+ * 鍦ㄣ�屾枃鏈寲琛ㄦ牸銆嶉噷鎵鹃檮浠�1锛堣〃澶村惈銆岃鍗曟暟鎬绘暟銆�/銆岃鍗曟暟銆嶏級骞跺彇 17 甯傚窞璁㈠崟鏁颁笌鍚堣銆�
+ * 鍏ュ弬 tables = 琛� -> 琛� -> 鍗曞厓鏍兼枃鏈紙宸插帇绌虹櫧锛夛紝涓庢潵婧愭牸寮忔棤鍏炽��
+ */
+ private void matchWycOrderTable(List<List<List<String>>> tables, Map<String, Double> orders, double[] sumHolder) {
+ for (List<List<String>> rows : tables) {
if (rows.size() < 3) continue;
int headerRow = -1;
int orderCol = -1;
for (int r = 0; r < Math.min(rows.size(), 3); r++) {
- List<org.apache.poi.xwpf.usermodel.XWPFTableCell> cells = rows.get(r).getTableCells();
+ List<String> cells = rows.get(r);
for (int c = 0; c < cells.size(); c++) {
- String norm = flat(cells.get(c).getText());
+ String norm = cells.get(c);
if (norm.contains("璁㈠崟鏁版�绘暟") || "璁㈠崟鏁�".equals(norm)) {
headerRow = r;
orderCol = c;
@@ -3444,11 +3591,11 @@
double citySum = 0.0;
boolean hasTotal = false;
for (int r = headerRow + 1; r < rows.size(); r++) {
- List<org.apache.poi.xwpf.usermodel.XWPFTableCell> cells = rows.get(r).getTableCells();
- if (orderCol >= cells.size()) continue;
- String name = flat(cells.get(0).getText());
+ List<String> cells = rows.get(r);
+ if (cells.isEmpty() || orderCol >= cells.size()) continue;
+ String name = cells.get(0);
if (name.isEmpty()) continue;
- Double val = parsePlainNumber(cells.get(orderCol).getText());
+ Double val = parsePlainNumber(cells.get(orderCol));
if (val == null) continue;
String city = RegionUtil.normalizeCityName(name);
if (city != null && RegionUtil.cityList().contains(city)) {
--
Gitblit v1.9.1