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