From 427cb62b464b3c16748890848f31b9dcf812169b Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期五, 18 九月 2026 15:06:52 +0800
Subject: [PATCH] feat(导入): 网约车订单报告支持 Word 2003 XML(WordML)+格式嗅探与友好提示
---
docs/功能测试报告/功能测试报告_网约车订单报告WordML兼容导入_2026-09-18.md | 72 ++++++++++++++
docs/工作日结/工作日结_2026-09-18.md | 30 +++++-
traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java | 175 ++++++++++++++++++++++++++++++++--
3 files changed, 258 insertions(+), 19 deletions(-)
diff --git "a/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\347\275\221\347\272\246\350\275\246\350\256\242\345\215\225\346\212\245\345\221\212WordML\345\205\274\345\256\271\345\257\274\345\205\245_2026-09-18.md" "b/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\347\275\221\347\272\246\350\275\246\350\256\242\345\215\225\346\212\245\345\221\212WordML\345\205\274\345\256\271\345\257\274\345\205\245_2026-09-18.md"
new file mode 100644
index 0000000..4d97116
--- /dev/null
+++ "b/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\347\275\221\347\272\246\350\275\246\350\256\242\345\215\225\346\212\245\345\221\212WordML\345\205\274\345\256\271\345\257\274\345\205\245_2026-09-18.md"
@@ -0,0 +1,72 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡_缃戠害杞﹁鍗曟姤鍛� WordML 鍏煎瀵煎叆_2026-09-18
+
+## 涓�銆佸姛鑳借鏄�
+
+**鑳屾櫙锛堢敤鎴锋姤闅滐級**锛氭妸銆婃箹鍖楃綉缁滈绾﹀嚭绉熸苯杞�2025骞�09鏈堝垎鏋愭姤鍛�.docx銆嬫斁鍒版闈㈠悗鐢ㄣ�岀綉绾﹁溅璁㈠崟鎶ュ憡銆嶅鍏ワ紝涓�鐩村け璐ャ��
+
+**瀹氫綅缁撹**锛氳鏂囦欢**涓嶆槸鐪熸鐨� docx**銆傚畠鏄� **Word 2003 XML锛圵ordML 骞抽摵 XML锛屽崟鏂囦欢锛�**琚敼鍚嶆垚浜� `.docx`锛�
+- 鏂囦欢澶存槸 `<?xml version="1.0" ...?> <?mso-application progid="Word.Document"?> <w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" ...>`
+- 3.1MB 绾� XML锛�**涓嶆槸 zip 鍖�**锛汸OI 鐨� `XWPFDocument` 鍙兘璇� OPC(zip) 鍖咃紝浜庢槸鎶� `File is not a zip file`銆�
+- 杩欑被鏂囦欢鏄� WPS/Word銆學ord 2003 XML 鏂囨。銆嶆牸寮忥紝缃戠害杞︾洃绠″钩鍙�/鍚屼簨涔嬮棿娴佽浆寰堝父瑙侊紝浼氬弽澶嶅嚭鐜般��
+
+**鏀瑰姩**锛歚traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java`
+
+1. `importWycOrderReport()` 鍏ュ彛鍏堟寜鏂囦欢澶村梾鎺㈡牸寮忥紝鍐嶅垎娲撅細
+ - `PK` 寮�澶� 鈫� docx锛坺ip锛夛細鍘� `XWPFDocument` 璺緞锛�
+ - `<w:wordDocument` + `wordml` 鈫� **WordML 骞抽摵 XML锛氭柊澧炶В鏋愯矾寰�**锛�
+ - `D0CF11E0` 鈫� OLE2 鑰� `.doc`锛氱粰鍑恒�岃鍙﹀瓨涓� docx銆嶇殑鏄庣‘鎻愮ず锛�
+ - 鍏跺畠 鈫� 銆屾棤娉曡瘑鍒殑鏂囦欢鏍煎紡銆嶃��
+2. 鏂板 `isWordPackage()`锛歾ip 浣嗗寘鍐呮病鏈� `word/document.xml`锛堝 xlsx 鏀瑰悕锛夋椂锛岀洿鎺ユ彁绀恒�岃繖涓嶆槸 Word 鏂囨。鈥︹�﹀鍗婃槸 Excel 鎴栧叾瀹冩牸寮忋�嶏紝涓嶅啀鎶涘嚭 XMLBeans 鐨勮嫳鏂囧爢鏍堛��
+3. 鏂板 `parseXmlSafely()`锛欴OM 瑙f瀽骞�**鍏抽棴澶栭儴瀹炰綋/DTD**锛堟姤鍛婃潵鑷閮ㄥ钩鍙帮紝闃� XXE锛夈��
+4. 琛ㄦ牸鍖归厤閫昏緫鎶芥垚涓庢牸寮忔棤鍏崇殑 `matchWycOrderTable(tables, orders, sumHolder)`锛�
+ - docx 璺緞鎶� `XWPFTable` 鏂囨湰鍖栧悗璋冪敤锛�
+ - WordML 璺緞鎶� `<w:tbl>/<w:tr>/<w:tc>` 鏂囨湰鍖栧悗璋冪敤锛�
+ - 琛ㄥご璇嗗埆銆�17 甯傚窞鍙栧�笺�佸悎璁¤璇嗗埆**瀹屽叏澶嶇敤鍚屼竴濂楅�昏緫**锛屼袱鏉¤矾寰勪笉浼氬嚭鐜板彛寰勫垎鍙夈��
+5. 鎶ヨ〃鏈熻瘑鍒娊鎴� `periodOfParagraphs(List<String>)`锛屼袱绉嶆牸寮忓叡鐢紙鏍囬 `缃戠害杞﹁涓歒YYY骞碝鏈坄 浼樺厛锛屽彇涓嶅埌鍥為��銆屽墠 10 娈典换鎰� YYYY骞碝鏈堛�嶏級銆�
+
+## 浜屻�佹祴璇曠幆澧�
+
+- 鍚庣 8090锛坄mvn -o compile` 鍚庨噸鍚紝鍔犺浇鏂扮被锛涘惎鍔ㄦ棩蹇� `Started TrafficAuditApplication`锛夛紱鏁版嵁搴� `traffic_audit`锛�127.0.0.1:3308锛夈��
+- 鎺ュ彛锛歚POST /api/import/wycOrderReport`锛坄file` + `period`锛宍period` 鍙负绌� 鈫� 鐢辨姤鍛婃爣棰樻帹鏂級銆�
+- 娴嬭瘯鏂囦欢锛歚D:\妗岄潰\婀栧寳缃戠粶棰勭害鍑虹姹借溅2025骞�09鏈堝垎鏋愭姤鍛�.docx`锛圵ordML锛�3,145,560 瀛楄妭锛夛紱`D:\05_鏁版嵁鍒嗙被\鍩庡競瀹㈣繍\8鏈圽婀栧寳缃戠粶棰勭害鍑虹姹借溅2026骞�08鏈堝垎鏋愭姤鍛�0910鏈�缁堢増.docx`锛堢湡 docx锛夈��
+- 璇佹嵁鏂囦欢锛歚_tmp/_qa_out/`锛堝懡浠よ緭鍑鸿鏈姤鍛婅〃鏍硷紱鏁版嵁鏍稿瑙� `_wyc_2025_09_row.txt`锛夈��
+
+## 涓夈�佺敤渚嬭〃
+
+| 缂栧彿 | 绫诲瀷 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|---|
+| 1 | 姝e悜 | WordML銆�2025骞�09鏈堛�嶆姤鍛婏紝`period=2025-09` | 瑙f瀽鍑� 17 甯傚窞璁㈠崟锛屽啓鍏� 2025-09 | `瀵煎叆鎴愬姛锛氭姤琛ㄦ湡 2025-09锛岃鍗曞悎璁� 17,652,159 鍗曪紝宸插啓鍏� 17 涓競宸瀈 | 閫氳繃 |
+| 2 | 姝e悜 | 鍚屼笂锛宍period=` 鐣欑┖ | 鐢辨爣棰� `缃戠害杞﹁涓�2025骞�9鏈坄 鎺ㄦ柇鍑� 2025-09 | 鎶ヨ〃鏈� 2025-09锛岀粨鏋滀笌鐢ㄤ緥 1 涓�鑷� | 閫氳繃 |
+| 3 | 姝e悜 | 鐪� docx銆�2026骞�08鏈堛�嶆姤鍛婇噸瀵� | 涓庡簱鍐呮棦鏈夊�间竴鑷达紙骞傜瓑锛屼笉鏀瑰彉缁撴灉锛� | 璁㈠崟鍚堣 30,508,652銆佹姹� 11,550,551锛屼笌瀵煎叆鍓嶅畬鍏ㄧ浉鍚� | 閫氳繃 |
+| 4 | 姝e悜 | 2026骞�06鏈堟姤鍛� | 璁㈠崟鍚堣 = 鎶ュ憡姝f枃銆屽叏鐪佽鍗曟�绘暟 2684.30 涓囧崟銆� | 搴撳唴 2026-06 = 26,843,037 | 閫氳繃 |
+| 5 | 璐熷悜 | `.doc`锛圤LE2 澶嶅悎鏂囨。锛學ord 97-2003锛� | 鏄庣‘鎻愮ず鏀圭敤 docx | `杩欐槸 Word 97-2003 鑰佹牸寮忥紙.doc锛夛紝璇风敤 WPS/Word 鎵撳紑鍚庛�屽彟瀛樹负銆峝ocx 鏍煎紡鍐嶅鍏 | 閫氳繃 |
+| 6 | 璐熷悜 | xlsx 鏀瑰悕鎴� `.docx` 涓婁紶 | 鏄庣‘鎻愮ず涓嶆槸 Word | `杩欎笉鏄� Word 鏂囨。锛堝帇缂╁寘閲屾病鏈� word/document.xml锛夛紝澶氬崐鏄� Excel 鎴栧叾瀹冩牸寮忊�︹� | 閫氳繃 |
+| 7 | 璐熷悜 | 闈炵綉绾﹁溅鎶ュ憡鐨� docx锛堛�婃姇璧勮〃涔嬮棿鐨勫叧绯�.docx銆嬶級 | 鎻愮ず鏈В鏋愬埌璁㈠崟鏁� | `鏈В鏋愬埌浠讳綍甯傚窞璁㈠崟鏁帮紝璇风‘璁や笂浼犵殑鏄�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�媊 | 閫氳繃 |
+| 8 | 杈圭晫 | WordML 涓〃鏍煎惈 16 鍒椼�佽〃澶村湪绗� 1 琛屻�佸競宸炲悕鐢ㄧ畝绉帮紙`姝︽眽`/`鏋楀尯`锛� | 绠�绉拌兘鏄犲皠鍒拌鑼冨悕锛堟姹夊競/绁炲啘鏋舵灄鍖猴級 | 17 甯傚窞鍏ㄩ儴璇嗗埆锛屾棤缂哄け | 閫氳繃 |
+
+**鍏ュ簱鎶芥煡**锛堟姤琛ㄦ湡 2025-09锛屼笌鎶ュ憡闄勪欢1閫愰」瀵归綈锛�
+
+| 椤� | 鎶ュ憡闄勪欢1 | 搴撳唴 `wyc_order_monthly` | 缁撹 |
+|---|---|---|---|
+| 璁㈠崟鍚堣 | 17,652,159 | 17,652,159 | 涓�鑷� |
+| 姝︽眽 | 8,076,610 | 8,076,610 | 涓�鑷� |
+| 榛勭煶 | 676,644 | 676,644 | 涓�鑷� |
+| 鍗佸牥 | 1,048,593 | 1,048,593 | 涓�鑷� |
+| 鎭╂柦 | 804,736 | 804,736 | 涓�鑷� |
+| 绁炲啘鏋舵灄鍖� | 1,424 | 1,424 | 涓�鑷� |
+| 鍏ㄧ渷 pin锛圔~E锛� | 鎶ュ憡鏃� | 2794.31 / 2522.54 / 16394.73 / 12998.74锛堜粠 `wyc_total_monthly` 鍚屾湡澶嶅埗锛� | 绗﹀悎璁捐 |
+
+## 鍥涖�佸彂鐜扮殑闂涓庡缃�
+
+1. **鏍煎紡鍏煎缂哄彛锛堟湰娆′慨澶嶏級**锛歐ordML 鎶ュ憡鏃犳硶瀵煎叆銆傚凡鍔犺В鏋愯矾寰勶紱涓ょ鏍煎紡鍏辩敤鍚屼竴濂楄〃鏍煎尮閰嶉�昏緫锛岄伩鍏嶅彛寰勫垎鍙夈��
+2. **閿欒淇℃伅涓嶅弸濂斤紙鏈淇锛�**锛歺lsx 鏀瑰悕鍚庢姤 `org.apache.xmlbeans.XmlException: Element workbook@... is not a valid document@...`銆傚凡鏀逛负涓枃涓氬姟鎻愮ず銆�
+3. **20XX 骞村墠鍚庢姤鍛婃爣棰樻牸寮忎笉鍚�**锛�2025 骞存姤鍛婃爣棰樻槸銆屾箹鍖�**缃戠害杞﹁涓� 2025骞�9鏈�** 杩愯鎯呭喌鍒嗘瀽鎶ュ憡銆嶏紝2026 骞存槸銆岀綉绾﹁溅琛屼笟2026骞�6鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嶃�傚悓涓�濂楁鍒欏潎鍙懡涓紝鏃犻渶鍐嶆敼銆�
+4. **鏁版嵁缂哄彛宸茬敱鐢ㄦ埛琛ラ綈**锛�2026-01~06锛�14:21 瀵煎叆 6 浠斤級銆�2025-01锛�14:47锛夈��2025-10~12锛�14:49锛夊潎宸插叆搴擄紱2025-09 鐢辨湰娆′慨澶嶅悗琛ュ叆锛�14:59 棣栨銆�15:04 澶嶆牳锛夈��
+ - 鍏ㄩ儴 2025-01~12銆�2026-01~08 鐨勮鍗曚笌 pin 鐜板凡榻愬叏銆�
+ - 褰卞搷锛氫腑鍙e緞缁村害 4锛堝煄闄呭煄涔$綉绾﹁溅锛�1~6 鏈堢敱銆屾棤婧愩�嶅彉涓恒�屽彲绠椼�嶏紝**涓彛寰勬帓鍚嶄欢涓庝腑鍙e緞鏄庣粏浠剁疮璁″彛寰勮嚜鍔ㄥ綊涓�**锛堟鍓嶅樊鍊� 2063.45 = 1~6 鏈堢綉绾﹁溅鍩庝埂锛夈��
+
+## 浜斻�佺粨璁�
+
+- WordML锛圵ord 2003 XML 鏀瑰悕 .docx锛夋姤鍛婄幇宸插彲鐩存帴瀵煎叆锛�17 甯傚窞 + 鍚堣瑙f瀽姝g‘銆佸叆搴撻�愰」瀵归綈锛涚湡 docx 璺緞鏃犲洖褰掋��
+- 3 鏉¤礋鍚戠敤渚嬪潎缁欏嚭涓枃鍙搷浣滄彁绀猴紙.doc / 闈� Word 鍖� / 闈炴湰鎶ュ憡锛夈��
+- 8 渚嬬敤渚嬪叏閮ㄩ�氳繃锛沗docs/鍔熻兘娴嬭瘯鎶ュ憡/` 涓嬪凡褰掓。鏈姤鍛娿��
diff --git "a/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md" "b/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md"
index f1a92f4..033c6b2 100644
--- "a/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md"
+++ "b/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md"
@@ -60,6 +60,23 @@
閭f槸鎺у埗鍙颁腑鏂囦贡鐮侀�犳垚鐨勮璇伙紱姣嶇増/妯℃澘鐪熷疄鍏紡鏄纭殑 `=[1]鍑虹!C13-[1]鍑虹!C15` / `=[1]缃戠害杞�!C13-[1]缃戠害杞�!C15`锛�
鍙拌处椤点�屾�婚噺鈭掑煄甯傚唴銆嵨�17 甯傚窞 = 535.83 / 333.39锛屼笌姣嶇増瀹氱鍏ㄧ渷琛屽畬鍏ㄤ竴鑷淬��**姣嶇増鍏紡涓嶉渶瑕佷慨锛屼箣鍓�"寰呬慨姣嶇増"鐨勭粨璁轰綔搴熴��**
+### 5. 缃戠害杞﹁鍗曟姤鍛婏細Word 2003 XML锛圵ordML锛夊吋瀹瑰鍏ワ紙15:00锛�
+
+**鐢ㄦ埛鎶ラ殰**锛氥�婃箹鍖楃綉缁滈绾﹀嚭绉熸苯杞�2025骞�09鏈堝垎鏋愭姤鍛�.docx銆嬫斁鍦ㄦ闈㈠涓嶈繘鍘汇��
+
+**瀹氫綅**锛氳鏂囦欢鏍规湰涓嶆槸 docx 鈥斺�� 鏄� **Word 2003 XML锛圵ordML 骞抽摵 XML锛�** 琚敼鍚嶆垚 `.docx`锛堟枃浠跺ご `<?mso-application progid="Word.Document"?><w:wordDocument xmlns:w=".../word/2003/wordml">`锛�3.1MB 绾� XML銆佷笉鏄� zip 鍖咃級锛孭OI 鐨� `XWPFDocument` 鍙兘璇� OPC(zip) 鍖� 鈫� 鎶� `File is not a zip file`銆傜洃绠″钩鍙�/鍚屼簨闂存祦杞殑鎶ュ憡甯告槸杩欑鏍煎紡锛屼細鍙嶅鍑虹幇銆�
+
+**鏀瑰姩锛坄DataImportService.java`锛�**锛�
+- `importWycOrderReport()` 鍏ュ彛鎸夋枃浠跺ご鍡呮帰鏍煎紡鍒嗘淳锛歾ip鈫抎ocx锛沗<w:wordDocument`+`wordml`鈫�**鏂板 WordML DOM 瑙f瀽璺緞**锛沗D0CF11E0`鈫掓槑纭彁绀恒��.doc 璇峰彟瀛樹负 docx銆嶏紱鍏跺畠鈫掋�屾棤娉曡瘑鍒�嶏紱
+- 鏂板 `isWordPackage()`锛歾ip 浣嗘棤 `word/document.xml`锛坸lsx 鏀瑰悕锛夆啋 涓枃鎻愮ず锛屼笉鍐嶆姏 XMLBeans 鑻辨枃鍫嗘爤锛�
+- 鏂板 `parseXmlSafely()`锛欴OM 瑙f瀽鍏抽棴澶栭儴瀹炰綋/DTD锛堥槻 XXE锛夛紱
+- 琛ㄦ牸鍖归厤鎶芥垚涓庢牸寮忔棤鍏崇殑 `matchWycOrderTable()`锛宒ocx 涓� WordML 涓ゆ潯璺緞**鍏辩敤鍚屼竴濂楄〃澶磋瘑鍒�/17 甯傚窞鍙栧��/鍚堣琛岄�昏緫**锛屼笉浼氬彛寰勫垎鍙夛紱
+- 鎶ヨ〃鏈熻瘑鍒娊鎴� `periodOfParagraphs()`锛屼袱绉嶆牸寮忓叡鐢ㄣ��
+
+**楠岃瘉**锛�8 鏉$敤渚嬪叏閮ㄩ�氳繃锛堣銆婂姛鑳芥祴璇曟姤鍛奯缃戠害杞﹁鍗曟姤鍛奧ordML鍏煎瀵煎叆_2026-09-18銆嬶級銆�2025-09 鍏ュ簱锛氳鍗曞悎璁� 17,652,159锛涙姹� 8,076,610 / 榛勭煶 676,644 / 鍗佸牥 1,048,593 / 鎭╂柦 804,736 / 鏋楀尯 1,424锛屼笌鎶ュ憡闄勪欢1 閫愰」涓�鑷达紱pin 浠� `wyc_total_monthly` 鍚屾湡澶嶅埗銆傜湡 docx锛�2026-08锛夐噸瀵煎箓绛夋棤鍙樺寲锛沗.doc`銆亁lsx 鏀瑰悕銆侀潪鏈姤鍛� docx 涓夌璐熷悜鍧囩粰鍑轰腑鏂囧彲鎿嶄綔鎻愮ず銆�
+
+**鏁版嵁缂哄彛宸茶ˉ榻愶紙閲嶈锛�**锛氱敤鎴峰湪 14:21~14:49 涔嬮棿鑷瀵煎叆 2026-01~06锛�6 浠斤級銆�2025-01銆�2025-10~12锛�2025-09 鐢辨湰娆′慨澶嶅悗琛ュ叆銆傜幇鍦� **2025-01~12 + 2026-01~08 鐨勮鍗曚笌 pin 鍏ㄩ儴榻愬叏**銆傜洿鎺ユ晥鏋滐細涓彛寰勭淮搴� 4 鐨� 1~6 鏈堜粠銆屾棤婧愩�嶅彉涓恒�屽彲绠椼�嶏紝**涓彛寰勬帓鍚嶄欢涓庝腑鍙e緞鏄庣粏浠剁殑绱鍙e緞鑷姩褰掍竴**锛堜袱鑰� 1-8 鏈堢疮璁″潎涓� 31528.0606锛屾鍓嶅樊 2063.45锛夈��
+
## 浜屻�佹祴璇曠粨鏋滄憳瑕�
瑙� `docs/鍔熻兘娴嬭瘯鎶ュ憡/鍔熻兘娴嬭瘯鎶ュ憡_姹囨�昏〃鍏紡鎵╅〉涓庨噸绠梍2026-09-18.md`锛�8 鏉℃鍚� + 4 鏉¤礋鍚�/杈圭晫 + 4 椤瑰叆搴撴娊鏌ワ紝鍏ㄩ儴閫氳繃銆�
@@ -88,15 +105,18 @@
- 鍥炲綊锛氭眹鎬诲ぇ琛� 17 椤� **0 鏍煎樊寮�**銆併�婄敓鎴恄涓彛寰勬帓鍚嶃��**0 鏍煎樊寮�**锛�
鏄庣粏浠跺樊寮備粎钀藉湪 鎬昏 / 鍑虹鍩庝埂 / 缃戠害杞﹀煄涔� 琛岀殑鏈堜唤鍒椾笌 AA 绱鍒楋紙灞為鏈燂級銆�
+### 缃戠害杞﹁鍗曟姤鍛� WordML 瀵煎叆锛�15:00锛夐獙璇�
+
+- 8 鏉$敤渚嬶紙4 姝e悜 / 3 璐熷悜 / 1 杈圭晫锛夊叏閮ㄩ�氳繃锛歐ordML 2025-09 瀵煎叆鎴愬姛銆佹姤琛ㄦ湡鐣欑┖鏃堕潬鏍囬鑷姩璇嗗埆銆佺湡 docx 骞傜瓑鏃犲彉鍖栥�乣.doc`/xlsx 鏀瑰悕/闈炴湰鎶ュ憡 docx 鍧囩粰鍑轰腑鏂囨彁绀恒��
+- 鏁版嵁琛ラ綈鍚庨噸璺� 8 鏈堝鍑哄洖褰掞細鐙珛銆婄敓鎴恄涓彛寰勬槑缁嗐�嬫姤琛ㄦ湡鍐呬笉涓�鑷� 0銆佹姤琛ㄦ湡鍚庝粛鏈夊�� 0锛涗笌姹囨�诲ぇ琛ㄣ�屼腑鍙e緞鏄庣粏銆嶉〉 **704 瀵规牸 0 宸紓**锛涘叏鐪� 1-8 鏈堟�诲杩愰噺 3733.79鈥�4003.57锛岀綉绾﹁溅鍩庝埂 333.39鈥�426.56锛岀疮璁� AA5=31528.0607銆丄A9=2911.47銆�
+- 鎺掑悕浠朵笌鏄庣粏浠剁疮璁″彛寰勪竴鑷达紙鍧� 31528.0606锛夛紝宸�� 2063.45 娑堝け銆�
+
## 涓夈�侀仐鐣欓棶棰樹笌寰呭姙
1. ~~涓彛寰勬帓鍚嶅悓姣斾粛鏄啓姝诲�紐~ 鈫� **宸叉寜鏂板彛寰勫彇鍊�**锛圡3=0.638 / Q3=0.2247锛夛紝浣嗕粛涓庝腑鍙e緞鏄庣粏鐨� 0.0581 涓嶈嚜娲斤紝宸�� 2063.45 = 缃戠害杞� 1-6 鏈堟棤婧愶紱**鏀跺彛鍔炴硶瑙佷笅鏂�"绗� 3 姝ユ敹鍙e缓璁�"**銆�
2. ~~`loadMidClassMap()` 缂哄贰娓稿嚭绉� + 缃戠害杞︿袱缁磣~ 鈫� **宸茶ˉ**锛堢淮搴� 3銆�4锛夛紝鐙珛涓彛寰勬槑缁嗗洓缁撮綈鍏ㄣ��
3. ~~**姣嶇増缂洪櫡寰呬慨**锛氫腑鍙e緞鏄庣粏鍩庡競绾у叕寮忓紩閿欓〉~~ 鈫� **璇垽锛屽凡浣滃簾**锛氱湡瀹炲叕寮忔槸姝g‘鐨勮法绨垮紩鐢紝姣嶇増鏃犻渶淇敼锛涘綋鏃剁殑"寮曢敊椤�"鏄帶鍒跺彴涓枃涔辩爜璇銆�
-4. **鏁版嵁缂哄彛锛堝凡鏍稿疄锛屽睘婧愬ご灏辨病鏈夛級**锛�2026-01~06 缃戠害杞﹁鍗曞湪**婧愭枃浠�**銆婃ā鏉縚缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx銆嬮噷灏辨槸绌虹殑锛堝彧鏈夊叏鐪� pin銆佹棤璁㈠崟鍒楋級锛�
- 涓嶆槸瀵煎叆澶辫触鈥斺�擿wyc_order_monthly` 璇� 6 鏈� `order_*` 鍏� NULL 涓庝箣鍚诲悎锛堝鍏ユ壒娆� `WYC_ORDER`锛�09-04锛夈��
- 褰卞搷闈㈠彧鏈変竴涓細`WycSplitCalc.calc()` 鍥� `orderSum<=0` 绠椾笉鍑虹綉绾﹁溅鎷嗗垎 鈫� 缁村害 4 璇� 6 涓湀鏃犳簮锛堢幇宸茬敱绗� 4 鏉℃敼鍔ㄤ繚鐣欐瘝鐗堝畾绋垮�煎厹浣忥級銆�
- **寤鸿**锛氱粰缃戠害杞﹁鍗曞鍏ュ姞涓�鏉°�岃鍗曞垪鍏ㄧ┖銆嶇殑鏄惧紡鎻愮ず锛堢幇鍦ㄤ細闈欓粯鎴愬姛锛夛紝骞惰涓氬姟纭杩� 6 涓湀璁㈠崟鏄惁鍙ˉ銆�
+4. ~~**鏁版嵁缂哄彛**锛�2026-01~06 缃戠害杞﹁鍗曟湭鍏ュ簱~~ 鈫� **宸茶ˉ榻�**锛堢敤鎴� 14:21~14:49 瀵煎叆 2026-01~06 + 2025-01 + 2025-10~12锛�2025-09 缁� WordML 淇鍚庤ˉ鍏ワ級銆傜幇鍦� 2025-01~12 + 2026-01~08 璁㈠崟榻愬叏锛岀淮搴� 4 鍏ㄦ湡鍙畻锛屾帓鍚嶄欢/鏄庣粏浠跺彛寰勫綊涓�銆備粛寤鸿琛ヤ竴鏉°�屽鍏ユ椂璁㈠崟鍒楀叏绌恒�嶇殑鏄惧紡鎻愮ず锛堛�婃ā鏉縚缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx銆嬮偅绫绘枃浠剁幇鍦ㄤ細闈欓粯鎴愬姛锛夈��
5. 搴撳唴 h2031銆屽叏鐪併�嶈 鈮� 危17 甯傚窞锛�1-8 鏈� 27524.49 vs 24481.74锛屽樊 3042.75锛夛細涓氬姟鍙e緞闂锛屽缓璁笟鍔′晶鏍稿銆�
6. 鎶曡祫锛氭ā鏉块噷娌℃湁鐨勬柊椤圭洰涓嶄細鑷姩鎻掕锛涙槑缁嗚〃璧勯噾娓犻亾鍒嗙粍琛屾湭鍏ュ簱锛堥渶鍔� `fund_channel`锛夛紱鍚屽悕涓嶅悓椤圭洰鏈脊绐椼��
7. 鎶曡祫涓ゅ紶鏄庣粏琛紙`X鏈堝杩愮珯鎶曡祫鏄庣粏琛╜ / `X鏈堢墿娴佺珯鍦烘姇璧勬槑缁哷锛夌敓鎴愬櫒鏈仛銆�
@@ -116,6 +136,6 @@
## 鍥涖�佹槑澶╄鍒�
1. 绗� 3 姝ユ敹鍙o細鎺掑悕浠朵笌鏄庣粏浠跺彛寰勫綊涓�锛堝樊鍊� 2063.45 = 2026 骞� 1-6 鏈堢綉绾﹁溅鍩庝埂锛夆啋 鎺掑悕浠舵敼鍙栨眹鎬昏〃鍚屾簮鍊� 鈫� 鍥哄寲"鐢熸垚浠� vs 姣嶇増閲嶇畻"閫愭牸鍥炲綊鑴氭湰銆�
-2. 缃戠害杞﹁鍗曞鍏ョ┖鍒楁彁绀猴紙椤甸潰鏄庣‘鎻愮ず"璇ユ湡璁㈠崟鍒椾负绌猴紝鏈啓鍏ヨ鍗曟暟"锛�+ 鍚戠敤鎴风‘璁� 2026-01~06 鏄惁琛ユ暟銆�
+2. 缃戠害杞﹁鍗曞鍏ョ┖鍒楁彁绀猴紙椤甸潰鏄庣‘鎻愮ず"璇ユ湡璁㈠崟鍒椾负绌猴紝鏈啓鍏ヨ鍗曟暟"锛夆�斺�旀暟鎹櫧宸茶ˉ榻愶紝浣嗛潤榛樻垚鍔熶粛鏄殣鎮c��
3. 鎶曡祫"鏂板椤圭洰鎻掕 + 鍒嗙粍琛屽叆搴擄紙`fund_channel`锛�"鏂规钀藉湴锛堢瓑鐢ㄦ埛瀵�"妯″潡"鍙e緞鐨勬渶鍚庣‘璁わ級銆�
4. 鎶曡祫涓ゅ紶鏄庣粏琛ㄧ敓鎴愬櫒锛坄X鏈堝杩愮珯鎶曡祫鏄庣粏琛╜ / `X鏈堢墿娴佺珯鍦烘姇璧勬槑缁哷锛夈��
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