From da614c0b85465f92453405928534d5fa2adc3c8c Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期五, 11 九月 2026 16:43:08 +0800
Subject: [PATCH] feat: 网约车订单运行监测报告(docx)导入 + 汇总大表8月数据驱动回填与版式对齐

---
 traffic-audit-web/src/views/DataImport.vue                                                          |   21 
 traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java       |  212 +++++++++++++
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java             |   38 ++
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java   |  104 ++++++
 docs/功能测试报告/功能测试报告_汇总大表8月数据回填与版式对齐_2026-09-11.md                                                    |   70 ++++
 docs/城市客运/模板_网约车订单.docx                                                                             |    0 
 docs/工作日结/工作日结_2026-09-11.md                                                                        |   40 ++
 traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java |   18 +
 docs/功能测试报告/功能测试报告_网约车订单报告导入_2026-09-11.md                                                          |   41 ++
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java    |  380 +++++++++++++++++++++++
 10 files changed, 905 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_\346\261\207\346\200\273\345\244\247\350\241\2508\346\234\210\346\225\260\346\215\256\345\233\236\345\241\253\344\270\216\347\211\210\345\274\217\345\257\271\351\275\220_2026-09-11.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_\346\261\207\346\200\273\345\244\247\350\241\2508\346\234\210\346\225\260\346\215\256\345\233\236\345\241\253\344\270\216\347\211\210\345\274\217\345\257\271\351\275\220_2026-09-11.md"
new file mode 100644
index 0000000..caad084
--- /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_\346\261\207\346\200\273\345\244\247\350\241\2508\346\234\210\346\225\260\346\215\256\345\233\236\345\241\253\344\270\216\347\211\210\345\274\217\345\257\271\351\275\220_2026-09-11.md"
@@ -0,0 +1,70 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡_姹囨�诲ぇ琛�8鏈堟暟鎹洖濉笌鐗堝紡瀵归綈_2026-09-11
+
+## 涓�銆佸姛鑳借鏄�
+閽堝鐢ㄦ埛鍙嶉鐨� 4 涓棶棰橈紝瀵广�婇亾璺繍杈撻噺姹囨�昏〃銆嬪鍑洪摼璺仛鏁版嵁椹卞姩鍥炲~涓庣増寮忓榻愶細
+
+1. **8 鏈堟暟鎹凡鍏ュ簱浣嗘眹鎬昏〃娌℃湁 8 鏈堟暟鎹�** 鈫� 姹囨�诲伐浣滅翱濉厖鍣ㄨ瘑鍒�屽綋鏈堝�煎垪銆嶏紙8 鏈堜负 Q 鍒楋級骞跺洖濉紱
+2. **鍚屾瘮涓�寰嬩互姹囨�昏〃 2025 骞� 8 鏈堟暟鎹负鍑�** 鈫� 褰撴湀鍚屾瘮鍒楋紙R 鍒楋級鍏紡缁熶竴涓� `=Q../AJ..-1`锛孉J 鍒楀嵆 2025 骞� 8 鏈堬紱
+3. **鍏氦銆佸嚭绉熸暟鎹槸鏃ユ湡褰㈠紡** 鈫� 鍥炲~鏁板�兼椂鎸夊悓琛� 1 鏈堝垪鏍峰紡濂楃敤锛岄伩鍏嶆暟鍊艰濂椾笂鏃ユ湡鏍煎紡锛堟樉绀烘垚 1902/1910 骞达級锛�
+4. **鐝嚎鍖呰溅銆佽建閬撹疆娓″彧鏈夊垎甯傚窞娌℃湁鍏ㄧ渷鏁版嵁** 鈫� 涓哄叏鐪佽琛ラ綈鍚堣鍏紡锛堟瘝鐗堝師鏈彧瀵� 1 鏈堝啓姝诲叕寮忥級锛�
+5. **鐗堝紡瀹屽叏鎸夈�婄敓鎴恄閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx銆�** 鈫� 閫愬垪濂楃敤鍙傜収浠跺垪瀹�/闅愯棌鏍囪锛�**鍚庣画鎵╁垪锛�8 鏈� Q/R 鍒楋級涔熸寜鍙傜収浠跺搴�**銆�
+
+鏀瑰姩鑼冨洿锛�
+- `SummaryWorkbookFiller`锛歚fillFreightSheet` / `fillWycSheet` 閲嶅啓锛堝彧鍥炲~鐩爣鏈熷綋鏈堬紝娲剧敓琛屼繚鎸佸叕寮忥級锛涙柊澧� `ensureMonthFormulas`銆乣ensureMonthYoyFormulas`銆乣monthColumnCapacity`锛�
+- `ReportExportService`锛氬鍑烘椂璋冪敤 `ensureMonthFormulas` + 鏂板 `applySummaryReferenceLayout`锛堟寜鍙傜収浠跺榻愬垪瀹斤級锛�
+- `WycSplitCalc`锛氭眹鎬昏〃缃戠害杞﹀彇鏁颁紭鍏堣搴撱��
+
+鍏抽敭鍙e緞锛氬綋鏈堝�煎垪锛�0 鍩猴級= `2 + 2*(m-1)`锛屽悓姣斿垪 = 鍊煎垪 + 1锛涘悓姣斿垎姣嶅浐瀹氫负 AJ 鍒楋紙2025 骞� 8 鏈堬級銆�
+
+## 浜屻�佹祴璇曠幆澧�
+- 鍚庣 `http://localhost:8090`锛屽鍑烘帴鍙� `GET /api/report/export/summaryWorkbook?period=YYYY-MM`銆�
+- 姣嶇増锛歚docs/鐢熸垚姹囨�诲ぇ琛�/2026骞�8鏈堥亾璺繍杈撻噺姹囨�昏〃.xlsx`锛涚増寮忓弬鐓э細`docs/鐢熸垚姹囨�诲ぇ琛�/鐢熸垚_閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx`銆�
+- 娴嬭瘯鏃堕棿锛�2026-09-11銆�
+
+## 涓夈�佺敤渚嬭〃
+
+### 3.1 姝e悜锛�2026-08 鐢熸垚涓庡綋鏈堝彇鍊�
+
+| 缂栧彿 | 妫�鏌ラ」 | 棰勬湡 | 瀹為檯 | 缁撹 |
+| --- | --- | --- | --- | --- |
+| TC-1 | 瀵煎嚭 2026-08 | HTTP 200锛屾甯镐骇鍑烘枃浠� | 4.6 绉掕繑鍥烇紝948,735 瀛楄妭 | 閫氳繃 |
+| TC-2 | 鍚庣鍥炲~鏃ュ織 | 鏈夊啓鍏ユ牸鏁扮粺璁°�佹棤缂烘暟鎻愮ず | 鍐欏叆褰撴湀鍊兼牸鏁�=1659锛岃ˉ鍐欏綋鏈堝悓姣斿叕寮忔牸鏁�=600锛岃ˉ鍐欏綋鏈堝悎璁″叕寮忔牸鏁�=43锛屽榻愬垪瀹藉垪鏁�=422锛岀己鏁版彁绀�=[] | 閫氳繃 |
+| TC-3 | 璐ц繍椤靛叏鐪佸潡锛堣5-10锛塓 鍒� | 瑙勪笂/瑙勪笅璐ц繍閲忎笌鍛ㄨ浆閲忔湁鍊硷紝鍚堣琛屼负鍏紡 | 瑙勪笂璐ц繍閲� 3666.1401銆佽涓� 13222.6214銆佽涓婂懆杞� 570004.4355銆佽涓嬪懆杞� 1700006.166锛決5=`=Q7+Q9`銆丵6=`=Q8+Q10` | 閫氳繃 |
+| TC-4 | 璐ц繍椤� 危17甯傚窞 = 鍏ㄧ渷 | 鐩哥瓑 | 危17甯傚窞璐ц繍閲� = 16888.7615锛涘叏鐪� Q5 = 3666.1401 + 13222.6214 = 16888.7615 | 閫氳繃 |
+| TC-5 | 璐ц繍椤靛競宸炴淳鐢熻 | 涓嶅啓姝诲�硷紝淇濇寔鍏紡 | 姝︽眽 Q14锛堣涓�+瑙勪笅鍛ㄨ浆閲忥級=`=Q15+Q16`锛涜涓�/瑙勪笅鍒嗛」涓烘暟鍊� | 閫氳繃 |
+| TC-6 | 缃戠害杞﹂〉 | 17 甯傚窞褰撴湀鏈夊�硷紝鍏ㄧ渷琛屼负鍏紡 | 姝︽眽 Q9=1879.9049鈥︺�丵10=13407.6982鈥︼紱鍏ㄧ渷 Q5=`=Q9+Q13+鈥�+Q73`锛浳�17甯傚窞瀹㈣繍閲�=4997.74銆佸懆杞噺=29166.7 | 閫氳繃 |
+| TC-7 | 鍏氦椤� | 甯傚窞褰撴湀鏈夊�间笖**涓嶆槸鏃ユ湡鏄剧ず** | Q9=3856.83锛堟姹夊杩愰噺锛夛紝鍗曞厓鏍兼牸寮� `0.00`锛涘叏鐪� Q5 涓烘眰鍜屽叕寮� | 閫氳繃 |
+| TC-8 | 鍑虹椤� | 鍚屼笂 | Q9=1058.95锛堟姹夊杩愰噺锛夛紝鏍煎紡 `0.00`锛涘叏鐪� Q5 涓烘眰鍜屽叕寮� | 閫氳繃 |
+| TC-9 | 鐝嚎鍖呰溅椤靛叏鐪佽 | 鏈夊叏鐪佸悎璁★紙鍘熺己澶憋級 | Q4=`=Q8+Q10+Q14+鈥�+Q56`銆丵5=`=Q9+Q11+鈥�+Q57`銆丵6/Q7 涓綋鍚堣浜︿负鍏紡 | 閫氳繃 |
+| TC-10 | 杞ㄩ亾銆佽疆娓¢〉鍏ㄧ渷琛� | 鏈夊叏鐪佸悎璁★紙鍘熺己澶憋級 | 杞ㄩ亾 鍏ㄧ渷 Q4=`=SUM(Q6,Q8)`銆丵5=`=Q7+Q9`锛涜疆娓� 鍏ㄧ渷 Q13=`=Q15`銆丵14=`=Q16` | 閫氳繃 |
+| TC-11 | 鍚屾瘮鍏紡鎸囧悜 2025 骞� 8 鏈� | R 鍒楀垎姣嶄负 AJ 鍒� | 鍚勯〉琛ㄥご AJ 鍒� = `2025骞�8鏈坄锛汻 鍒楀叕寮忓舰濡� `=Q9/AJ9-1`锛汚J 鍒楃‘鏈� 2025 骞� 8 鏈堟瘝鐗堟暟鍊硷紙濡傝揣杩� AJ7=3716.1158锛� | 閫氳繃 |
+| TC-12 | 鎵撳紑鍗抽噸绠� | 鏂囦欢甯﹀叏閲忛噸绠楁爣璁� | `xl/workbook.xml` 鍐� `<calcPr calcId="191029" fullCalcOnLoad="true"/>`锛孍xcel/WPS 鎵撳紑鍗崇畻鍑哄叏鐪佸悎璁� | 閫氳繃 |
+
+### 3.2 鐗堝紡瀵归綈锛堢 5 椤癸級
+
+| 缂栧彿 | 妫�鏌ラ」 | 棰勬湡 | 瀹為檯 | 缁撹 |
+| --- | --- | --- | --- | --- |
+| TC-13 | 鍏ㄨ〃鍒楀涓庛�婄敓鎴恄閬撹矾杩愯緭閲忔眹鎬昏〃銆嬫瘮瀵癸紙灞曞紑 `<col min..max>` 鑼冨洿閫愬垪姣斿锛� | 瀹屽叏涓�鑷� | 19 涓〉绛惧叏閮ㄦ瘮瀵癸紝**鐪熷疄宸紓 0 鍒�** | 閫氳繃 |
+| TC-14 | 鎵╁垪锛�8 鏈� Q/R锛夊垪瀹� | 涓庡弬鐓т欢 8 鏈堜竴鑷� | 璐ц繍 P/Q/R = 12.67/10.55/11.67锛堝弬鐓� 12.67/10.56/11.67锛屼粎 0.01 娴偣鑸嶅叆锛夛紱缃戠害杞�/杞ㄩ亾杞浮 Q/R=13.0/13.0锛涘叕浜� Q/R=11.89/13.0锛涘嚭绉� Q/R=11.89/13.0锛涚彮绾垮寘杞� Q/R=10.22/9.78 鈥斺�� 鍧囦笌鍙傜収浠剁浉鍚� | 閫氳繃 |
+
+### 3.3 璐熷悜涓庡洖褰�
+
+| 缂栧彿 | 妫�鏌ラ」 | 棰勬湡 | 瀹為檯 | 缁撹 |
+| --- | --- | --- | --- | --- |
+| TC-15 | 鍘嗗彶鏈堬紙2026-07锛変笉寰楄褰撴湀鍥炲~姹℃煋锛氫笌 `docs/鐢熸垚姹囨�诲ぇ琛�/_澶囦唤_2026骞�7鏈堥亾璺繍杈撻噺姹囨�昏〃_娓呯悊鍓峗20260904.xlsx` 閫愭牸姣斿 | 闄ゆ棦瀹氭敼閫犲鏃犲樊寮� | 宸紓鍏变笁绫伙細鈶� 426 澶勬诞鐐规湯浣嶅樊锛堟渶澶х浉瀵瑰樊 7.26e-15锛屽睘 Excel 15 浣嶆湁鏁堟暟瀛楁埅鏂紝鏃犲疄璐ㄥ樊寮傦級锛涒憽 57 澶�"鏂囨湰 vs 绛夊�兼暟瀛�"锛涒憿 148 澶勫叏閮ㄩ泦涓湪銆屼腑鍙e緞鎺掑悕銆嶉〉 鈥斺�� 璇ラ〉宸︿晶闄堟棫鍧楀凡浜� 2026-09-08 鎸夐渶姹傛暣鍧楀垹闄わ紝灞�**棰勬湡鍙樺寲**銆�**鏃犲洖褰�** | 閫氳繃 |
+| TC-16 | 缂烘湀鎻愮ず | 缂烘暟鎹殑鏈堜唤鏈夋彁绀� | 2026-08 瀵煎嚭 `缂烘暟鎻愮ず=[]` | 閫氳繃 |
+
+## 鍥涖�佸彂鐜扮殑闂涓庡缃�
+
+1. **闂锛堝凡淇锛�**锛歚monthColumnCapacity` 鍘熷疄鐜板彧璁�"鍚� 2026+绱"鐨勮〃澶达紝鑰岃揣杩愰〉琛ㄥご鏄�"绱鍛ㄨ浆閲�"锛屽鑷村叾鎭掕繑鍥為粯璁ゅ垪鏁般��8 鏈堝垪姘镐笉鍥炲~銆�**澶勭疆**锛氭敼涓恒�岀疮璁″垪銆嶄笌銆屾渶鍚庝竴涓湀浠�/鏃ユ湡鏍煎紡鏈堝垪銆嶅彇杈冨ぇ鍊笺��
+2. **闂锛堝凡淇锛�**锛歚shiftFormulaColumns` 杩炵画缁勫紩鐢ㄨ鎷兼垚 `Q5AJ5O5`銆�**澶勭疆**锛歚sb.append(quoteReplacement(...))` 鈫� `m.appendReplacement(sb, quoteReplacement(...))`銆�
+3. **闂锛堝凡淇锛�**锛歚ensureMonthYoyFormulas` 涓� `setCellFormula` 澶辫触琚潤榛樺悶鎺夈��**澶勭疆**锛氭敼涓烘姏鍑哄惈椤电/鍗曞厓鏍�/鍘熷紡/鐩爣寮忕殑 `IllegalStateException`锛屼究浜庡畾浣嶃��
+4. **闂锛堝凡淇锛�**锛氬洖濉洰鏍囨牸鑻ヨ濂椾笂鏃ユ湡鏍煎紡锛屾暟鍊间細鏄剧ず鎴� 1902/1910 骞达紙鍗崇敤鎴峰弽棣堢殑"鏃ユ湡褰㈠紡"锛夈��**澶勭疆**锛歚setNumeric` 瀵圭┖/鏃ユ湡鏍煎紡鏍煎鐢ㄥ悓琛� 1 鏈堝垪鏍峰紡锛涘宸叉湁鍏紡鏍肩洿鎺ヨ烦杩囦笉瑕嗙洊銆�
+5. **宸茬煡鏃犲鍛婅**锛�2026-08 瀵煎嚭鏃舵棩蹇楀嚭鐜� `姹囨�诲伐浣滅翱鏈壘鍒板悓鏈熷浠芥瘝鐗堬紙_澶囦唤_2026骞�8鏈堚�︼級锛岃緭鍑哄皢淇濇寔姣嶇増鐜扮姸`銆傚師鍥犳槸 8 鏈堟病鏈�"娓呯悊鍓嶅浠芥瘝鐗�"锛岃�� 8 鏈堟瘝鐗堟湰韬凡鏄畬鏁存瘝鐗堬紝鏃犻渶浠庡浠借繕鍘燂紝鏁呬笉褰卞搷杈撳嚭锛堝凡閫愰」楠岃瘉鍙栧�兼纭級銆�7 鏈堝洜瀛樺湪 `_澶囦唤_2026骞�7鏈堚� 鏃犳鍛婅銆�
+6. **鎬ц兘娉ㄦ剰**锛歚applySummaryReferenceLayout` 鐨勫垪鏁颁笂闄愪繚鐣� `min(鍙傜収浠跺凡鐢ㄥ垪鏁�, 鐩爣浠跺凡鐢ㄥ垪鏁� + 4)` 鈥斺�� 鍘绘帀璇ヤ笂闄愪細璁� POI 鍦ㄥぇ鍒楀彿锛堣揣杩愰〉 147 鍒楋級涓婂嚭鐜版寚鏁扮骇鍗¢】銆�
+
+## 浜斻�佺粨璁�
+8 鏈堟眹鎬诲ぇ琛ㄥ凡鑳界敱搴撲腑鏁版嵁鑷姩鐢熸垚锛氳揣杩愯涓�/瑙勪笅銆佺綉绾﹁溅銆佸叕浜ゃ�佸嚭绉熴�佺彮绾垮寘杞︺�佽建閬撹疆娓$殑褰撴湀鍒楀潎宸插洖濉紝鍏ㄧ渷鍚堣涓庡垎甯傚窞鍚堣涓�鑷达紱鍚屾瘮缁熶竴鍙� 2025 骞� 8 鏈堬紱鍏氦/鍑虹涓嶅啀鏄剧ず涓烘棩鏈燂紱鐝嚎鍖呰溅涓庤建閬撹疆娓$殑鍏ㄧ渷鏁版嵁宸茶ˉ榻愶紙鍏紡锛屾墦寮�鍗崇畻锛夛紱鍏ㄨ〃鍒楀涓庣敤鎴锋寚瀹氱殑銆婄敓鎴恄閬撹矾杩愯緭閲忔眹鎬昏〃銆嬮�愬垪涓�鑷达紙鍚� 8 鏈堟墿鍒楀搴︼級銆�7 鏈堝巻鍙叉湀鍥炲綊鏃犲疄璐ㄥ樊寮傘��
+
+閬楃暀锛氳灏嗙湡瀹炵殑銆婄綉绾﹁溅琛屼笟2026骞�8鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬮噸浼犱竴娆★紝瑕嗙洊褰撳墠鐢� 7 鏈堟姤鍛婁复鏃跺壇鏈鍏ョ殑 8 鏈堝競宸炶鍗曟暟銆�
+
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\212\345\257\274\345\205\245_2026-09-11.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\212\345\257\274\345\205\245_2026-09-11.md"
new file mode 100644
index 0000000..0e01dc9
--- /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\212\345\257\274\345\205\245_2026-09-11.md"
@@ -0,0 +1,41 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡_缃戠害杞﹁鍗曟姤鍛婂鍏2026-09-11
+
+## 涓�銆佸姛鑳借鏄�
+鏀寔鐩存帴涓婁紶閮ㄧ骇銆婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶紙.docx锛夛紝绯荤粺鑷姩瑙f瀽鎶ュ憡姝f枃闄勪欢琛ㄤ腑鐨� **17 甯傚窞璁㈠崟鏁�**骞跺啓鍏ユ暟鎹簱 `wyc_order_monthly`锛團 鍒楄鍗曞悎璁� + G-W 17 甯傚窞璁㈠崟鍒楋級锛�
+姹囨�诲ぇ琛ㄣ�岀綉绾﹁溅銆嶉〉鍙栨暟鏀逛负**浼樺厛璇诲簱**锛屽疄鐜�"涓婁紶鎶ュ憡 鈫� 姹囨�昏〃褰撴湀缃戠害杞︽暟鎹嚜鍔ㄥ嚭鐜�"銆�
+
+鏀瑰姩鑼冨洿锛�
+- 鍚庣 `DataImportService.importWycOrderReport()` / `parseWycOrderReportTables()` / `periodOfDocx()`锛氳В鏋愭姤鍛娿�佸畾浣嶆姤琛ㄦ湡銆佽惤搴� `wyc_order_monthly`锛屽苟鍦ㄧ己澶辨椂浠� `wyc_total_monthly` 琛ラ綈鍏ㄧ渷 pin锛圔-E锛夈��
+- 鍚庣 `DataImportController`锛氭柊澧� `POST /api/import/wycOrderReport`锛堝弬鏁� file + period锛夈��
+- 鍚庣 `WycSplitCalc.loadMonthlyInputFromDb()`锛氭眹鎬昏〃鍙栨暟浼樺厛璇� `wyc_order_monthly`锛堝簱閲岃鏈熻鍗曞叏绌烘椂鍥為��璇� `docs/鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx`锛夈��
+- 鍓嶇 `DataImport.vue`锛氬鍏ョ被鍨嬫柊澧炪�岀綉绾﹁溅璁㈠崟鎶ュ憡銆嶏紝妯℃澘鎻愮ず涓� `鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗�.docx`锛屾帴鍙� `/import/wycOrderReport`锛屾枃浠剁被鍨嬮檺 `.docx/.doc`銆�
+- 娓呯┖鏄犲皠锛歚wycOrderReport` 鍏宠仈娓呯┖ `WYC_ORDER` 鎵规锛沗wycOrder` 涓� `wycOrderReport` 浜掍负鍏宠仈娓呴櫎銆�
+
+鎶ヨ〃鏈熷彇鍊艰鍒欙細浼樺厛鍙栨姤鍛婃爣棰樹腑鐨勩�岀綉绾﹁溅琛屼笟YYYY骞碝鏈堛�嶏紝鍙栦笉鍒版椂鐢ㄩ〉闈㈡墍閫夋姤琛ㄦ湡锛涗袱鑰呴兘鍙栦笉鍒板垯鎶ラ敊銆�
+
+## 浜屻�佹祴璇曠幆澧�
+- 鍚庣锛歋pring Boot锛宍http://localhost:8090`锛�8090 宸茬洃鍚級锛涘墠绔細`http://localhost:8080`銆�
+- 鏁版嵁搴擄細MySQL 5.7 `127.0.0.1:3308` / `traffic_audit`銆�
+- 鐧诲綍锛歛dmin锛涙祴璇曟椂闂达細2026-09-11銆�
+
+## 涓夈�佺敤渚嬭〃
+
+| 缂栧彿 | 绫诲瀷 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+| --- | --- | --- | --- | --- | --- |
+| TC-1 | 姝e悜 | 涓婁紶 `docs/鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗�.docx`锛堛�婄綉绾﹁溅琛屼笟2026骞�7鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶級锛宲eriod=2026-07 | 瑙f瀽鎴愬姛锛屾姤琛ㄦ湡鍙栨爣棰樼殑 2026-07锛�17 甯傚窞鍏ㄩ儴鍛戒腑 | 杩斿洖"鎶ヨ〃鏈� 2026-07锛岃鍗曞悎璁� 29,730,791 鍗曪紝宸插啓鍏� 17 涓競宸�" | 閫氳繃 |
+| TC-2 | 姝e悜 | 鍚屼笂锛屾鏌ュ悗绔棩蹇� | 璁板綍 period/sum/cities/missing | `WycOrderReport imported: period=2026-07 sum=2.9730791E7 cities=17 missing=[]` | 閫氳繃 |
+| TC-3 | 鍏ュ簱鎶芥煡 | docx 闄勪欢琛ㄣ�屽湴甯傚悕绉� / 璁㈠崟鏁版�绘暟銆�17 琛岋紝閫愰」涓� `wyc_order_monthly` 姣斿 | 17 甯傚窞閫愪竴鐩哥瓑锛屽悎璁$浉绛� | 姝︽眽 11,183,267锛涢粍鐭� 983,296锛涘崄鍫� 2,155,144锛涘疁鏄� 3,024,336锛涜闃� 2,390,572锛涢剛宸� 321,348锛涜崋闂� 942,124锛涘瓭鎰� 1,220,812锛涜崋宸� 1,573,514锛涢粍鍐� 1,125,829锛涘捀瀹� 928,460锛涢殢宸� 528,210锛涙仼鏂� 2,166,015锛涗粰妗� 528,557锛涙綔姹� 267,879锛涘ぉ闂� 387,894锛涙灄鍖� 3,534锛涘悎璁� 29,730,791 鈥斺�� **涓� DB 瀹屽叏涓�鑷�** | 閫氳繃 |
+| TC-4 | 璐熷悜锛堟枃浠剁被鍨嬮敊锛� | 鎶� `妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx` 浼犵粰鎶ュ憡瀵煎叆鎺ュ彛 | 缁欏嚭鍙鎻愮ず锛屼笉钀藉簱 | 杩斿洖"瀵煎叆澶辫触: 鏃犳硶璇诲彇 Word 鏂囨。锛岃涓婁紶 .docx 鏍煎紡鐨勩�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶紱鑻ヤ笂浼犵殑鍏跺疄鏄� Excel/PDF锛岃鏀圭敤瀵瑰簲妯℃澘瀵煎叆銆傦紙鍘熷閿欒锛氣�︼級" | 閫氳繃锛堝惈淇锛岃绗洓鑺傦級 |
+| TC-5 | 璐熷悜锛堝唴瀹逛笉绗︼級 | 涓婁紶 `鍩庡競瀹㈣繍鏁版嵁浼樺寲.docx`锛堥潪缃戠害杞︾洃娴嬫姤鍛婏級 | 鏄庣‘鎻愮ず鏈В鏋愬埌甯傚窞璁㈠崟鏁� | 杩斿洖"瀵煎叆澶辫触: 鏈В鏋愬埌浠讳綍甯傚窞璁㈠崟鏁帮紝璇风‘璁や笂浼犵殑鏄�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿��" | 閫氳繃 |
+| TC-6 | 姝e悜锛堥噸澶嶅鍏ュ箓绛夛級 | 鍚屼竴浠� 7 鏈堟姤鍛婅繛缁鍏ヤ袱娆� | 绗簩娆¤鐩栧悓鎶ヨ〃鏈熻锛屼笉浜х敓閲嶅琛� | 浠嶈繑鍥� 17 甯傚窞锛沗wyc_order_monthly` 涓� `2026-07` 浠� 1 琛� | 閫氳繃 |
+| TC-7 | 鑱旇皟 | 鎶ュ憡瀵煎叆鍚庨噸鏂板鍑� `period=2026-08` 姹囨�诲ぇ琛� | 缃戠害杞﹂〉褰撴湀鍒楋紙Q 鍒楋級鍑虹幇鏁板�硷紝鍏ㄧ渷琛屼负姹傚拰鍏紡 | Q 鍒� 17 甯傚窞鍏ㄩ儴鏈夊�硷紝Q5 璧� 4 涓叏鐪佽涓� `=Q9+Q13+鈥 姹傚拰鍏紡锛孯 鍒楀悓姣斿叕寮� `=Q../AJ..-1`锛圓J=2025骞�8鏈堬級 | 閫氳繃锛堣瑙併�婂姛鑳芥祴璇曟姤鍛奯姹囨�诲ぇ琛�8鏈堟暟鎹洖濉笌鐗堝紡瀵归綈_2026-09-11.md銆嬶級 |
+
+## 鍥涖�佸彂鐜扮殑闂涓庡缃�
+
+1. **闂锛堝凡淇锛�**锛氭妸 Excel/PDF 璇紶鍒般�岀綉绾﹁溅璁㈠崟鎶ュ憡銆嶇被鍨嬫椂锛屾帴鍙h繑鍥炵殑鏄� xmlbeans 鐨勮嫳鏂囨妧鏈紓甯革紙`org.apache.xmlbeans.XmlException: ... is not a valid document`锛夛紝椤甸潰鐢ㄦ埛鏃犳硶鐞嗚В銆�
+   **澶勭疆**锛歚DataImportService.importWycOrderReport()` 鐨勮В鏋愭缁熶竴鎹曡幏寮傚父锛屾敼鍐欎负鐢ㄦ埛鍙鎻愮ず锛屽苟鎶婂師濮嬮敊璇互"锛堝師濮嬮敊璇細鈥︼級"闄勫湪鏈熬渚夸簬鎺掓煡銆備慨澶嶅悗 TC-4 澶嶆祴閫氳繃銆�
+2. **闇�瑕佺敤鎴锋搷浣滅殑浜嬮」**锛氬綋鍓嶅簱涓� `2026-08` 鐨�**甯傚窞璁㈠崟鏁�**鏄敤 7 鏈堟姤鍛婃敼鏍囬鐨勪复鏃跺壇鏈紙`_t_wyc_aug.docx`锛夊鍏ョ殑锛屽叾甯傚窞鍊间笌 7 鏈堢浉鍚岋紱`pin`锛圔-E锛屽嵆瀹㈣繍閲�/鍛ㄨ浆閲忓彛寰勶級鍙栬嚜 `wyc_total_monthly` 鐨� 2026-08 琛岋紝鏄纭殑銆�**璇锋妸鐪熷疄鐨勩�婄綉绾﹁溅琛屼笟2026骞�8鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬮噸鏂颁笂浼犱竴娆�**锛堢被鍨嬮�夈�岀綉绾﹁溅璁㈠崟鎶ュ憡銆嶏紝鎶ヨ〃鏈熼�� 2026-08锛夛紝鍗冲彲瑕嗙洊涓虹湡瀹� 8 鏈堝競宸炶鍗曟暟銆�
+
+## 浜斻�佺粨璁�
+缃戠害杞﹁鍗曡繍琛岀洃娴嬫姤鍛婏紙docx锛夊鍏ュ姛鑳藉彲鐢細姝e悜瑙f瀽 17 甯傚窞璁㈠崟鏁板噯纭叆搴撳苟涓庢簮鏂囦欢閫愰」涓�鑷达紱鏂囦欢绫诲瀷閿欒銆佸唴瀹逛笉绗︿袱绫昏礋鍚戝満鏅潎鑳界粰鍑烘槑纭彁绀猴紱閲嶅瀵煎叆骞傜瓑锛涘鍏ュ悗姹囨�诲ぇ琛ㄥ彲鑷姩鍙栧埌褰撴湀鏁版嵁銆傚缓璁皢鐪熷疄 8 鏈堟姤鍛婇噸浼犲悗鍐嶆瀵煎嚭鏍稿銆�
+
diff --git "a/docs/\345\237\216\345\270\202\345\256\242\350\277\220/\346\250\241\346\235\277_\347\275\221\347\272\246\350\275\246\350\256\242\345\215\225.docx" "b/docs/\345\237\216\345\270\202\345\256\242\350\277\220/\346\250\241\346\235\277_\347\275\221\347\272\246\350\275\246\350\256\242\345\215\225.docx"
new file mode 100644
index 0000000..b71f664
--- /dev/null
+++ "b/docs/\345\237\216\345\270\202\345\256\242\350\277\220/\346\250\241\346\235\277_\347\275\221\347\272\246\350\275\246\350\256\242\345\215\225.docx"
Binary files differ
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-11.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-11.md"
new file mode 100644
index 0000000..71fb4ee
--- /dev/null
+++ "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-11.md"
@@ -0,0 +1,40 @@
+# 宸ヤ綔鏃ョ粨_2026-09-11
+
+## 涓�銆佷粖澶╁仛浜嗕粈涔�
+
+### 1. 鍩庡競瀹㈣繍锛氱綉绾﹁溅璁㈠崟杩愯鐩戞祴鎶ュ憡锛坉ocx锛夊鍏ワ紙鐢ㄦ埛绗� 1 椤癸級
+- 鍚庣鏂板 `POST /api/import/wycOrderReport` 涓� `DataImportService.importWycOrderReport()`锛氳В鏋愩�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬮檮浠惰〃銆屽湴甯傚悕绉� / 璁㈠崟鏁版�绘暟銆嶏紝鍙� 17 甯傚窞璁㈠崟鏁板啓鍏� `wyc_order_monthly`锛團 璁㈠崟鍚堣 + G-W 甯傚窞鍒楋級锛屾姤琛ㄦ湡浼樺厛鍙栨姤鍛婃爣棰樺勾鏈堛��
+- 鍏ㄧ渷 pin锛圔-E锛夊湪璇ユ湡琛岀己澶辨椂锛屼粠 `wyc_total_monthly` 鍚屾姤琛ㄦ湡琛ラ綈銆�
+- 鍓嶇瀵煎叆鐣岄潰鏂板绫诲瀷銆岀綉绾﹁溅璁㈠崟鎶ュ憡銆嶏紝缁戝畾 `鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗�.docx`锛堥檺 .docx/.doc锛夛紝骞舵帴鍏ユ寜绫诲瀷娓呯┖銆�
+- 姹囨�诲ぇ琛ㄧ綉绾﹁溅鍙栨暟鏀逛负**浼樺厛璇诲簱**锛坄WycSplitCalc.loadMonthlyInputFromDb`锛夛紝搴撻噷鏃犳暟鎹嚜鍔ㄥ洖閫�璇� xlsx 姣嶇増銆�
+- 淇锛氳浼� Excel 鏃舵姏鍑� xmlbeans 鑻辨枃寮傚父 鈫� 鏀逛负鐢ㄦ埛鍙鎻愮ず銆�
+
+### 2. 姹囨�诲ぇ琛� 8 鏈堟暟鎹洖濉笌鐗堝紡瀵归綈锛堢敤鎴风 2/3/4/5 椤癸級
+- 8 鏈堟簮鏁版嵁宸插湪搴� 鈫� 瀵煎嚭鏃惰嚜鍔ㄥ洖濉綋鏈堝�煎垪锛圦 鍒楋級锛屽巻鍙叉湀涓嶅姩銆�
+- 鍚屾瘮缁熶竴浠ユ眹鎬昏〃 **2025 骞� 8 鏈堬紙AJ 鍒楋級** 涓哄垎姣嶏紝鍏紡 `=Q../AJ..-1`銆�
+- 鍏氦/鍑虹鏁板�间笉鍐嶈鏃ユ湡鏍煎紡鍚炴帀锛堟寜鍚岃 1 鏈堝垪鏍峰紡濂楃敤锛宍0.00`锛夈��
+- 鐝嚎鍖呰溅銆佽建閬撹疆娓°�屽叏鐪併�嶈琛ラ綈鍚堣鍏紡锛堟瘝鐗堝師鍙 1 鏈堝啓姝诲叕寮忥級銆�
+- 鐗堝紡鎸夌敤鎴锋寚瀹氬弬鐓т欢 `docs/鐢熸垚姹囨�诲ぇ琛�/鐢熸垚_閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx` 閫愬垪瀵归綈鍒楀/闅愯棌锛�**8 鏈堟墿鍒� Q/R 鐨勫搴︿篃鍙栬嚜鍙傜収浠�**銆�
+
+### 3. 鏂囨。褰掓。
+- 鏄ㄥぉ宸插畬鎴愩�婃箹鍖楃渷閬撹矾璐х墿杩愯緭閲忓垎甯傚窞缁熻鏂规硶(2023)缂栧埗璇存槑銆嬪涔犵瑪璁板綊妗o紙`docs/璐ц繍/瀛︿範绗旇_鍒嗗競宸為亾璺揣鐗╄繍杈撻噺缁熻鏂规硶_2026-09-10.md`锛夛紝浠婂ぉ鏈啀鏀瑰姩銆�
+
+## 浜屻�佹祴璇曠粨鏋滄憳瑕侊紙璇﹁銆婂姛鑳芥祴璇曟姤鍛娿�嬶級
+- `docs/鍔熻兘娴嬭瘯鎶ュ憡/鍔熻兘娴嬭瘯鎶ュ憡_缃戠害杞﹁鍗曟姤鍛婂鍏2026-09-11.md`锛歍C-1~TC-7 鍏ㄩ儴閫氳繃锛�7 鏈堟姤鍛婅В鏋� 17 甯傚窞銆佸悎璁� 29,730,791锛屼笌搴撲腑閫愰」涓�鑷达紱涓ょ被璐熷悜鐢ㄤ緥鎻愮ず鏄庣‘銆�
+- `docs/鍔熻兘娴嬭瘯鎶ュ憡/鍔熻兘娴嬭瘯鎶ュ憡_姹囨�诲ぇ琛�8鏈堟暟鎹洖濉笌鐗堝紡瀵归綈_2026-09-11.md`锛歍C-1~TC-16 鍏ㄩ儴閫氳繃銆�
+  - 2026-08 瀵煎嚭鎴愬姛锛�4.6s / 948,735 瀛楄妭锛夛紱鍐欏叆褰撴湀鍊� 1659 鏍笺�佸綋鏈堝悓姣斿叕寮� 600 鏍笺�佸綋鏈堝悎璁″叕寮� 43 鏍笺�佺己鏁版彁绀轰负绌恒��
+  - 璐ц繍 危17 甯傚窞 = 鍏ㄧ渷锛�16888.7615锛夛紱缃戠害杞�/鍏氦/鍑虹/鐝嚎鍖呰溅/杞ㄩ亾杞浮鍏ㄧ渷琛屼笌甯傚窞琛屽潎姝g‘銆�
+  - 鍒楀涓庡弬鐓т欢**鐪熷疄宸紓 0 鍒�**銆�
+  - 7 鏈堝巻鍙叉湀鍥炲綊锛氭棤瀹炶川宸紓锛�148 澶勫樊寮傚叏閮ㄦ槸銆屼腑鍙e緞鎺掑悕銆嶉〉 09-08 宸叉寜闇�姹傚垹闄ょ殑鏃у乏鍧楋級銆�
+
+## 涓夈�侀仐鐣欓棶棰樹笌寰呭姙
+1. **闇�鐢ㄦ埛鎿嶄綔**锛氬簱涓� `2026-08` 鐨勫競宸炶鍗曟暟鐩墠鏄敤 7 鏈堟姤鍛婃敼鏍囬鐨勪复鏃跺壇鏈紙`_t_wyc_aug.docx`锛夊鍏ョ殑锛屽競宸炲�间笌 7 鏈堢浉鍚岋紱璇锋妸鐪熷疄鐨� 8 鏈堣繍琛岀洃娴嬫姤鍛婇噸浼犱竴娆★紙绫诲瀷銆岀綉绾﹁溅璁㈠崟鎶ュ憡銆嶏紝鎶ヨ〃鏈� 2026-08锛夎鐩栥��
+2. 2026-08 瀵煎嚭浼氭墦鍗般�屾湭鎵惧埌鍚屾湡澶囦唤姣嶇増锛坃澶囦唤_2026骞�8鏈堚�︼級銆嶅憡璀︼紝灞炴棤瀹虫彁绀猴紙8 鏈堟瘝鐗堟湰韬畬鏁达級锛屽悗缁彲鑰冭檻鎸夐渶姹傚喅瀹氭槸鍚︽秷闄ゃ��
+3. 鏍圭洰褰曞爢绉緝澶氬巻鍙蹭复鏃舵枃浠讹紙`_t_*.py`銆乣_*.sql`銆乣_gen_*.xlsx`銆乣_bak_*.java`銆乣_token.txt` 绛夛級锛屽緟鐢ㄦ埛鍚屾剰鍚庨泦涓竻鐞嗐��
+4. 鐗堝紡鍙傜収浠� `docs/鐢熸垚姹囨�诲ぇ琛�/鐢熸垚_閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx` 鐩墠涓烘湭鍏ュ簱鏂囦欢锛屾槸鍚︾撼鍏ョ増鏈鐞嗗緟纭銆�
+
+## 鍥涖�佹槑澶╄鍒�
+1. 鐢ㄦ埛閲嶄紶鐪熷疄 8 鏈堢綉绾﹁溅鎶ュ憡鍚庯紝閲嶆柊瀵煎嚭 2026-08 骞堕�愰」澶嶆牳缃戠害杞﹂〉銆�
+2. 缁х画鎸夌敤鎴峰弽棣堟牳瀵� 8 鏈堟眹鎬诲ぇ琛ㄦ垚鍝侊紙閲嶇偣锛氫腑鍙e緞鍚勯〉銆佽揣杩愭帓鍚嶉〉锛夈��
+3. 娓呯悊涓存椂鏂囦欢锛堥渶鐢ㄦ埛纭锛夈��
+
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java
index 54e09c2..46d6e61 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java
@@ -269,6 +269,24 @@
         }
     }
 
+    /** 缃戠害杞﹁鍗曡繍琛岀洃娴嬪垎鏋愭姤鍛婏紙docx锛氥�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶級 */
+    @PostMapping("/wycOrderReport")
+    public Result<String> importWycOrderReport(@RequestParam("file") MultipartFile file,
+                                               @RequestParam("period") String period) {
+        try {
+            DataImportService.WycOrderReportResult r = importService.importWycOrderReport(file, period);
+            StringBuilder msg = new StringBuilder("缃戠害杞﹁鍗曟姤鍛婂鍏ユ垚鍔燂細鎶ヨ〃鏈� " + r.getPeriod()
+                    + "锛岃鍗曞悎璁� " + String.format("%,.0f", r.getOrderSum()) + " 鍗曪紝宸插啓鍏� " + r.getCityCount() + " 涓競宸�");
+            if (!r.getMissingCities().isEmpty()) {
+                msg.append("锛涗互涓嬪競宸炴湭鍦ㄦ姤鍛婁腑瑙f瀽鍒帮紝鏈啓鍏ワ細").append(String.join("銆�", r.getMissingCities()));
+            }
+            return Result.ok(msg.toString());
+        } catch (Exception e) {
+            log.error("wycOrderReport import error", e);
+            return Result.error("瀵煎叆澶辫触: " + e.getMessage());
+        }
+    }
+
     /** 鎶曡祫鏈堟姤锛堝叏鐪佹眹鎬诲ぇ琛級锛歝ategory=瀹㈣繍绔欏満/鐗╂祦鍥尯 */
     @PostMapping("/investment")
     public Result<String> importInvestment(@RequestParam("file") MultipartFile file,
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 e34a275..4c372e7 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
@@ -56,6 +56,10 @@
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
 import cn.hutool.core.io.IoUtil;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -1345,6 +1349,7 @@
         CLEAR_TYPE_TO_OP.put("cityTaxiAuth", "CITY_TAXI_AUTH");
         CLEAR_TYPE_TO_OP.put("wycTotal", "WYC_TOTAL");
         CLEAR_TYPE_TO_OP.put("wycOrder", "WYC_ORDER");
+        CLEAR_TYPE_TO_OP.put("wycOrderReport", "WYC_ORDER");
 
         CLEAR_TYPE_TO_AUDIT.put("h2032", new String[]{"H2032"});
         CLEAR_TYPE_TO_AUDIT.put("transportAuth", new String[]{"H2032"});
@@ -1386,7 +1391,8 @@
         CLEAR_TYPE_TO_BATCH.put("cityTaxi", new String[]{"CITY_TAXI"});
         CLEAR_TYPE_TO_BATCH.put("cityTaxiAuth", new String[]{"CITY_TAXI_AUTH"});
         CLEAR_TYPE_TO_BATCH.put("wycTotal", new String[]{"WYC_TOTAL"});
-        CLEAR_TYPE_TO_BATCH.put("wycOrder", new String[]{"WYC_ORDER"});
+        CLEAR_TYPE_TO_BATCH.put("wycOrder", new String[]{"WYC_ORDER", "WYC_ORDER_REPORT"});
+        CLEAR_TYPE_TO_BATCH.put("wycOrderReport", new String[]{"WYC_ORDER_REPORT", "WYC_ORDER"});
     }
 
     /**
@@ -3144,6 +3150,209 @@
         return ok;
     }
 
+    // ========== 缃戠害杞﹁鍗曡繍琛岀洃娴嬪垎鏋愭姤鍛婏紙docx锛夊鍏� ==========
+
+    /** 缃戠害杞﹁鍗曟姤鍛婂鍏ョ粨鏋滐紙鎶ヨ〃鏈�/璁㈠崟鍚堣/瑕嗙洊甯傚窞鏁�/缂哄け甯傚窞锛� */
+    public static class WycOrderReportResult {
+        private final String period;
+        private final double orderSum;
+        private final int cityCount;
+        private final List<String> missingCities;
+
+        public WycOrderReportResult(String period, double orderSum, int cityCount, List<String> missingCities) {
+            this.period = period;
+            this.orderSum = orderSum;
+            this.cityCount = cityCount;
+            this.missingCities = missingCities;
+        }
+
+        public String getPeriod() { return period; }
+        public double getOrderSum() { return orderSum; }
+        public int getCityCount() { return cityCount; }
+        public List<String> getMissingCities() { return missingCities; }
+    }
+
+    /**
+     * 銆婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶紙docx锛夊鍏ワ細
+     * 瑙f瀽鎶ュ憡闄勪欢涓�屽湴甯傚悕绉� | 鈥� | 璁㈠崟鏁版�绘暟銆嶈〃鐨� 17 甯傚窞璁㈠崟鏁帮紝鍐欏叆 wyc_order_monthly 鐨�
+     * F 璁㈠崟鍚堣涓� G-W 17 甯傚窞璁㈠崟鍒楋紱鍏ㄧ渷 pin锛圔-E锛夋部鐢ㄨ鏈熷凡鏈夎锛岃嫢鏃犺鏈熻鍒欎粠
+     * wyc_total_monthly 鍚屾姤琛ㄦ湡澶嶅埗銆傛姤琛ㄦ湡浼樺厛鍙栨姤鍛婃爣棰樺勾鏈堬紝鍙栦笉鍒版椂鐢ㄩ〉闈㈡墍閫夋姤琛ㄦ湡銆�
+     */
+    public synchronized WycOrderReportResult importWycOrderReport(MultipartFile file, String period) throws Exception {
+        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);
+        } catch (Exception e) {
+            throw new RuntimeException("鏃犳硶璇诲彇 Word 鏂囨。锛岃涓婁紶 .docx 鏍煎紡鐨勩�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶紱"
+                    + "鑻ヤ笂浼犵殑鍏跺疄鏄� Excel/PDF锛岃鏀圭敤瀵瑰簲妯℃澘瀵煎叆銆傦紙鍘熷閿欒锛�" + e.getMessage() + "锛�");
+        }
+        if (orders.isEmpty()) {
+            throw new RuntimeException("鏈В鏋愬埌浠讳綍甯傚窞璁㈠崟鏁帮紝璇风‘璁や笂浼犵殑鏄�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿��");
+        }
+        String rp = null;
+        if (docPeriod != null && docPeriod.matches("\\d{4}-\\d{2}")) rp = docPeriod;
+        else if (period != null && period.matches("\\d{4}-\\d{2}")) rp = period;
+        if (rp == null) {
+            throw new RuntimeException("鏃犳硶纭畾鎶ヨ〃鏈燂細鎶ュ憡鏍囬鏈惈銆孻YYY骞碝鏈堛�嶏紝涓旈〉闈㈡湭閫夋嫨鎶ヨ〃鏈�");
+        }
+
+        List<String> missing = new ArrayList<>();
+        double citySum = 0.0;
+        for (String city : RegionUtil.cityList()) {
+            Double v = orders.get(city);
+            if (v == null) missing.add(city);
+            else citySum += v;
+        }
+        double sum = sumHolder[0] > 0 ? sumHolder[0] : citySum;
+
+        WycOrderMonthly it = wycOrderMapper.selectOne(new LambdaQueryWrapper<WycOrderMonthly>()
+                .eq(WycOrderMonthly::getReportPeriod, rp).last("LIMIT 1"));
+        if (it == null) it = new WycOrderMonthly();
+        it.setReportPeriod(rp);
+        it.setOrderSum(sum > 0 ? sum : null);
+        it.setOrderWuhan(orderOf(orders, "姝︽眽甯�"));
+        it.setOrderHuangshi(orderOf(orders, "榛勭煶甯�"));
+        it.setOrderShiyan(orderOf(orders, "鍗佸牥甯�"));
+        it.setOrderYichang(orderOf(orders, "瀹滄槍甯�"));
+        it.setOrderXiangyang(orderOf(orders, "瑗勯槼甯�"));
+        it.setOrderEzhou(orderOf(orders, "閯傚窞甯�"));
+        it.setOrderJingmen(orderOf(orders, "鑽嗛棬甯�"));
+        it.setOrderXiaogan(orderOf(orders, "瀛濇劅甯�"));
+        it.setOrderJingzhou(orderOf(orders, "鑽嗗窞甯�"));
+        it.setOrderHuanggang(orderOf(orders, "榛勫唸甯�"));
+        it.setOrderXianning(orderOf(orders, "鍜稿畞甯�"));
+        it.setOrderSuizhou(orderOf(orders, "闅忓窞甯�"));
+        it.setOrderEnshi(orderOf(orders, "鎭╂柦宸�"));
+        it.setOrderXiantao(orderOf(orders, "浠欐甯�"));
+        it.setOrderQianjiang(orderOf(orders, "娼滄睙甯�"));
+        it.setOrderTianmen(orderOf(orders, "澶╅棬甯�"));
+        it.setOrderShennong(orderOf(orders, "绁炲啘鏋舵灄鍖�"));
+
+        if (it.getPinK() == null || it.getPinCK() == null || it.getPinZ() == null || it.getPinCZ() == null) {
+            WycTotalMonthly tot = wycTotalMapper.selectOne(new LambdaQueryWrapper<WycTotalMonthly>()
+                    .eq(WycTotalMonthly::getReportPeriod, rp).last("LIMIT 1"));
+            if (tot != null) {
+                if (it.getPinK() == null) it.setPinK(tot.getPinK());
+                if (it.getPinCK() == null) it.setPinCK(tot.getPinCK());
+                if (it.getPinZ() == null) it.setPinZ(tot.getPinZ());
+                if (it.getPinCZ() == null) it.setPinCZ(tot.getPinCZ());
+            }
+        }
+
+        wycOrderMapper.delete(new LambdaQueryWrapper<WycOrderMonthly>().eq(WycOrderMonthly::getReportPeriod, rp));
+        wycOrderMapper.insert(it);
+        recordBatch(file.getOriginalFilename(), "WYC_ORDER_REPORT", rp, 1, 1, 0, null);
+        log.info("WycOrderReport imported: period={} sum={} cities={} missing={}", rp, sum, orders.size(), missing);
+        return new WycOrderReportResult(rp, sum, orders.size(), missing);
+    }
+
+    /** 鎶ュ憡鏍囬涓殑鎶ヨ〃鏈燂細缃戠害杞﹁涓�2026骞�7鏈� -> 2026-07锛涘彇涓嶅埌杩斿洖 null */
+    private String periodOfDocx(XWPFDocument doc) {
+        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();
+            if (text == null) continue;
+            String flat = text.replaceAll("\\s+", "");
+            if (flat.isEmpty()) continue;
+            java.util.regex.Matcher m = titlePat.matcher(flat);
+            if (m.find()) return ymd(m.group(1), m.group(2));
+            if (fallback == null && scanned < 10) {
+                java.util.regex.Matcher m2 = anyPat.matcher(flat);
+                if (m2.find()) fallback = ymd(m2.group(1), m2.group(2));
+            }
+            scanned++;
+        }
+        return fallback;
+    }
+
+    private String ymd(String y, String mo) {
+        try {
+            int month = Integer.parseInt(mo);
+            if (month < 1 || month > 12) return null;
+            return String.format("%04d-%02d", Integer.parseInt(y), month);
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    /** 瑙f瀽鎶ュ憡涓殑銆屽湴甯傚悕绉� | 鈥� | 璁㈠崟鏁版�绘暟銆嶈〃锛宱rders=甯傚窞璁㈠崟锛宻umHolder[0]=鍚堣琛岃鍗曟暟 */
+    private void parseWycOrderReportTables(XWPFDocument doc, Map<String, Double> orders, double[] sumHolder) {
+        for (XWPFTable table : doc.getTables()) {
+            List<XWPFTableRow> rows = table.getRows();
+            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();
+                for (int c = 0; c < cells.size(); c++) {
+                    String norm = flat(cells.get(c).getText());
+                    if (norm.contains("璁㈠崟鏁版�绘暟") || "璁㈠崟鏁�".equals(norm)) {
+                        headerRow = r;
+                        orderCol = c;
+                        break;
+                    }
+                }
+                if (headerRow >= 0) break;
+            }
+            if (headerRow < 0 || orderCol < 0) continue;
+
+            Map<String, Double> found = new LinkedHashMap<>();
+            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());
+                if (name.isEmpty()) continue;
+                Double val = parsePlainNumber(cells.get(orderCol).getText());
+                if (val == null) continue;
+                String city = RegionUtil.normalizeCityName(name);
+                if (city != null && RegionUtil.cityList().contains(city)) {
+                    found.put(city, val);
+                    citySum += val;
+                } else if (name.contains("鍚堣") || name.contains("鎬昏") || name.contains("鍏ㄧ渷") || name.contains("婀栧寳")) {
+                    hasTotal = true;
+                    sumHolder[0] = val;
+                }
+            }
+            if (!found.isEmpty()) {
+                orders.clear();
+                orders.putAll(found);
+                if (!hasTotal) sumHolder[0] = citySum;
+                return;
+            }
+        }
+    }
+
+    private String flat(String s) {
+        return s == null ? "" : s.replaceAll("\\s+", "").trim();
+    }
+
+    /** 銆�11,183,267 鍗曘�嶈繖绫绘枃鏈彇鏁板�� */
+    private Double parsePlainNumber(String s) {
+        if (s == null) return null;
+        String t = s.replaceAll("[,\\s]", "").trim();
+        if (t.isEmpty()) return null;
+        java.util.regex.Matcher m = java.util.regex.Pattern.compile("-?\\d+(?:\\.\\d+)?").matcher(t);
+        if (!m.find()) return null;
+        try {
+            return Double.parseDouble(m.group());
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    private Double orderOf(Map<String, Double> orders, String city) {
+        Double v = orders.get(city);
+        return (v == null || v <= 0) ? null : v;
+    }
     private String periodOfCell(Cell c) {
         if (c == null) return null;
         if (c.getCellType() == CellType.NUMERIC) {
@@ -3513,6 +3722,7 @@
             case "scaleSplit": return new ImportResult(importScaleSplit(file, period), 0, new ArrayList<>());
             case "wycTotal": return new ImportResult(importWycTotal(file, period), 0, new ArrayList<>());
             case "wycOrder": return new ImportResult(importWycOrder(file, period), 0, new ArrayList<>());
+            case "wycOrderReport": return new ImportResult(importWycOrderReport(file, period).getCityCount(), 0, new ArrayList<>());
             default: throw new RuntimeException("涓嶆敮鎸佺殑瀵煎叆绫诲瀷锛�" + type);
         }
     }
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
index 435309b..1abdfca 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
@@ -1,6 +1,8 @@
 package com.trafficaudit.reportexport.calc;
 
 import com.trafficaudit.common.util.RegionUtil;
+import com.trafficaudit.reportexport.calc.FreightCalc.FreightMatrix;
+import com.trafficaudit.reportexport.calc.FreightCalc.FreightMetrics;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -36,12 +38,16 @@
     private MidCalc midCalc;
     @Resource
     private PaxCalc paxCalc;
+    @Resource
+    private FreightCalc freightCalc;
+    @Resource
+    private WycSplitCalc wycSplitCalc;
 
     /** 2026 骞� 1 鏈堝綋鏈堝�煎垪锛�0 鍩猴紝鍗� C 鍒楋級 */
     private static final int FIRST_MONTH_COL = 2;
 
     /** 鍥炲~ 1..keepMonths 鏈堝彴璐﹂〉锛岃繑鍥炲啓鍏ユ牸鏁帮紱缂烘暟鎻愮ず鍐欏叆 problems */
-    public int fillMonthlyLedger(XSSFWorkbook wb, int year, int keepMonths, List<String> problems) {
+    public int fillMonthlyLedger(XSSFWorkbook wb, String period, int year, int keepMonths, List<String> problems) {
         if (wb == null || keepMonths <= 0) return 0;
         if (problems == null) problems = new ArrayList<>();
         String yearPrefix = year + "-";
@@ -50,6 +56,8 @@
         filled += fillCityRows(wb, "鍏氦", paxCalc.busMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false);
         filled += fillCityRows(wb, "鍑虹", paxCalc.taxiMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false);
         filled += fillTrackFerry(wb, paxCalc.railFerryMonthly(yearPrefix, keepMonths), year, keepMonths, problems);
+        filled += fillFreightSheet(wb, period, keepMonths, problems);
+        filled += fillWycSheet(wb, period, keepMonths, problems);
         return filled;
     }
 
@@ -117,6 +125,177 @@
             }
         }
         return filled;
+    }
+
+    /** 璐ц繍椤靛洖濉細鍏ㄧ渷鍧� + 17 甯傚窞鍧楃殑 瑙勪笂/瑙勪笅/鍚堣 脳 璐ц繍閲忋�佸懆杞噺锛堥�愭湀鐜扮畻锛涘綋鏈堝�煎垪=2+2*(m-1)锛� */
+    private int fillFreightSheet(XSSFWorkbook wb, String period, int keepMonths, List<String> problems) {
+        Sheet sh = wb.getSheet(" 璐ц繍");
+        if (sh == null) sh = wb.getSheet("璐ц繍");
+        if (sh == null) {
+            problems.add("璐ц繍椤典笉瀛樺湪锛堟瘝鐗堢己澶辫椤碉級锛岃烦杩囪嚜鍔ㄥ洖濉�");
+            return 0;
+        }
+        int cap = monthColumnCapacity(sh);
+        int mMax = Math.min(keepMonths, cap);
+        if (mMax <= 0) {
+            problems.add("璐ц繍椤垫湭璇嗗埆鍒� 2026 骞寸疮璁″垪锛岃烦杩囪嚜鍔ㄥ洖濉�");
+            return 0;
+        }
+        int year = periodYear(period);
+        // 鍧楋紙鍏ㄧ渷/甯傚窞锛夛細鎸囨爣閿� -> 琛屽彿
+        Map<String, Map<String, Integer>> blockRows = new LinkedHashMap<>();
+        String cur = null;
+        for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+            Row row = sh.getRow(r);
+            if (row == null) continue;
+            String a = text(row.getCell(0));
+            if (a != null && !a.trim().isEmpty()) {
+                String norm = RegionUtil.normalizeCityName(a.trim());
+                if (FreightCalc.PROVINCE.equals(norm) || RegionUtil.cityList().contains(norm)) {
+                    cur = norm;
+                    blockRows.computeIfAbsent(cur, k -> new LinkedHashMap<>());
+                } else {
+                    cur = null; // 鏍囬琛� / 闈炲叏鐪侀潪甯傚窞 -> 鑴辩鍧�
+                }
+            }
+            if (cur == null) continue;
+            String key = freightMetricKey(text(row.getCell(1)));
+            if (key == null) continue;
+            blockRows.get(cur).putIfAbsent(key, r);
+        }
+        if (blockRows.isEmpty()) {
+            problems.add("璐ц繍椤垫湭璇嗗埆鍒板叏鐪�/甯傚窞琛岋紝璺宠繃鑷姩鍥炲~");
+            return 0;
+        }
+        int filled = 0;
+        // 鍙洖濉洰鏍囨湡褰撴湀锛�1..鐩爣鏈�-1 鐨勫�间互姣嶇増锛堝惈鍚屾湡澶囦唤姣嶇増杩樺師锛変负鍑嗭紝閬垮厤鏀瑰啓宸茬‘璁ゅ彛寰�
+        int targetM = Math.min(periodMonth(period), mMax);
+        for (int m = targetM; m <= targetM; m++) {
+            String mp = String.format("%04d-%02d", year, m);
+            FreightMatrix mx;
+            try {
+                mx = freightCalc.calc(mp);
+            } catch (Exception e) {
+                problems.add("璐ц繍椤� " + mp + " 鍙栨暟澶辫触锛�" + e.getMessage());
+                continue;
+            }
+            int col = FIRST_MONTH_COL + 2 * (m - 1);
+            for (Map.Entry<String, Map<String, Integer>> e : blockRows.entrySet()) {
+                FreightMetrics fm = mx.getMonth().get(e.getKey());
+                if (fm == null) continue;
+                Map<String, Integer> rows = e.getValue();
+                // 1 鏈堝垪鏄叕寮忕殑鈥滄淳鐢熻鈥濓紙鍏ㄧ渷鍚堣 璐ц繍閲�/鍛ㄨ浆閲忋�佸競宸� 瑙勪笂+瑙勪笅鍛ㄨ浆閲忥級涓嶅啓鍊硷紝
+                // 鐢� ensureMonthFormulas 鎶婂叕寮忔寜鏈堝彸绉伙紝淇濇寔涓庢瘝鐗堜竴鑷�
+                filled += setFreightCell(sh, skipIfDerived(sh, rows.get("totalFreight")), col, fm.getTotalFreight());
+                filled += setFreightCell(sh, skipIfDerived(sh, rows.get("aboveFreight")), col, fm.getAboveFreight());
+                filled += setFreightCell(sh, skipIfDerived(sh, rows.get("belowFreight")), col, fm.getBelowFreight());
+                filled += setFreightCell(sh, skipIfDerived(sh, rows.get("totalTurnover")), col, fm.getTotalTurnover());
+                filled += setFreightCell(sh, skipIfDerived(sh, rows.get("aboveTurnover")), col, fm.getAboveTurnover());
+                filled += setFreightCell(sh, skipIfDerived(sh, rows.get("belowTurnover")), col, fm.getBelowTurnover());
+            }
+        }
+        return filled;
+    }
+
+    /** 璇ヨ 1 鏈堝垪锛圕锛夋槸鍏紡 -> 娲剧敓琛岋紝杩斿洖 null 琛ㄧず涓嶅洖濉暟鍊� */
+    private Integer skipIfDerived(Sheet sh, Integer r) {
+        if (r == null) return null;
+        Row row = sh.getRow(r);
+        if (row == null) return r;
+        Cell c = row.getCell(FIRST_MONTH_COL);
+        if (c != null && c.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) return null;
+        return r;
+    }
+
+    private int setFreightCell(Sheet sh, Integer r, int col, Double v) {
+        if (r == null || v == null) return 0;
+        return setNumeric(sh, r, col, v);
+    }
+
+    /** 璐ц繍椤� B 鍒楁寚鏍囨枃妗� -> 鎸囨爣閿紙瑙勪笂+瑙勪笅 / 瑙勪笂 / 瑙勪笅 脳 璐ц繍閲� / 鍛ㄨ浆閲忥級 */
+    private String freightMetricKey(String b) {
+        if (b == null) return null;
+        String s = b.replaceAll("\\s+", "");
+        if (s.isEmpty()) return null;
+        boolean turnover = s.contains("鍛ㄨ浆閲�");
+        if (s.contains("瑙勪笂+瑙勪笅")) return turnover ? "totalTurnover" : "totalFreight";
+        if (s.contains("瑙勪笂")) return turnover ? "aboveTurnover" : "aboveFreight";
+        if (s.contains("瑙勪笅")) return turnover ? "belowTurnover" : "belowFreight";
+        return turnover ? "totalTurnover" : "totalFreight"; // 鍏ㄧ渷鍧楃殑鈥滆揣杩愰噺/璐х墿鍛ㄨ浆閲忊��= 鍚堣
+    }
+
+    /** 缃戠害杞﹂〉鍥炲~锛�17 甯傚窞 鐨� 瀹㈣繍閲�/鍛ㄨ浆閲�/鍏朵腑鍩庡競鍐呭杩愰噺/鍏朵腑鍩庡競鍐呭懆杞噺锛堥�愭湀鐜扮畻锛�
+     *  鍏ㄧ渷琛岋紙=17 甯傚窞姹傚拰鍏紡锛夌敱 ensureMonthFormulas 鎸夋湀鎵╁垪锛屼笉鍦ㄦ澶勫啓鍊笺�� */
+    private int fillWycSheet(XSSFWorkbook wb, String period, int keepMonths, List<String> problems) {
+        Sheet sh = wb.getSheet("缃戠害杞�");
+        if (sh == null) {
+            problems.add("缃戠害杞﹂〉涓嶅瓨鍦紙姣嶇増缂哄け璇ラ〉锛夛紝璺宠繃鑷姩鍥炲~");
+            return 0;
+        }
+        int cap = monthColumnCapacity(sh);
+        int mMax = Math.min(keepMonths, cap);
+        if (mMax <= 0) {
+            problems.add("缃戠害杞﹂〉鏈瘑鍒埌 2026 骞寸疮璁″垪锛岃烦杩囪嚜鍔ㄥ洖濉�");
+            return 0;
+        }
+        int year = periodYear(period);
+        Map<String, Map<String, Integer>> cityMetricRows = new LinkedHashMap<>();
+        String cur = null;
+        for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+            Row row = sh.getRow(r);
+            if (row == null) continue;
+            String a = text(row.getCell(0));
+            if (a != null && !a.trim().isEmpty()) {
+                String norm = RegionUtil.normalizeCityName(a.trim());
+                cur = RegionUtil.cityList().contains(norm) ? norm : null;
+                if (cur != null) cityMetricRows.computeIfAbsent(cur, k -> new LinkedHashMap<>());
+            }
+            if (cur == null) continue;
+            String key = wycMetricKey(text(row.getCell(1)));
+            if (key == null) continue;
+            cityMetricRows.get(cur).putIfAbsent(key, r);
+        }
+        if (cityMetricRows.isEmpty()) {
+            problems.add("缃戠害杞﹂〉鏈瘑鍒埌甯傚窞琛岋紝璺宠繃鑷姩鍥炲~");
+            return 0;
+        }
+        int filled = 0;
+        // 鍙洖濉洰鏍囨湡褰撴湀锛堝巻鍙叉湀浠ユ瘝鐗�/鍘嗗彶鐢熸垚浠朵负鍑嗭級
+        int targetM = Math.min(periodMonth(period), mMax);
+        for (int m = targetM; m <= targetM; m++) {
+            String mp = String.format("%04d-%02d", year, m);
+            WycSplitCalc.WycResult wr;
+            try {
+                wr = wycSplitCalc.calc(mp);
+            } catch (Exception e) {
+                if (m == mMax) {
+                    problems.add("缃戠害杞﹂〉 " + mp + " 婧愭暟鎹己澶憋紝褰撴湀鏈洖濉細" + e.getMessage());
+                }
+                continue;
+            }
+            int col = FIRST_MONTH_COL + 2 * (m - 1);
+            for (Map.Entry<String, Map<String, Integer>> e : cityMetricRows.entrySet()) {
+                WycSplitCalc.WycMetrics wm = wr.getByCity().get(e.getKey());
+                if (wm == null) continue;
+                Map<String, Integer> rows = e.getValue();
+                filled += setFreightCell(sh, rows.get("pax"), col, wm.getTotalPax());
+                filled += setFreightCell(sh, rows.get("turnover"), col, wm.getTotalTurnover());
+                filled += setFreightCell(sh, rows.get("cityPax"), col, wm.getCityPax());
+                filled += setFreightCell(sh, rows.get("cityTurnover"), col, wm.getCityTurnover());
+            }
+        }
+        return filled;
+    }
+
+    /** 缃戠害杞﹂〉 B 鍒楁寚鏍囨枃妗� -> 鎸囨爣閿� */
+    private String wycMetricKey(String b) {
+        if (b == null) return null;
+        String s = b.replaceAll("\\s+", "");
+        if (s.isEmpty()) return null;
+        boolean city = s.contains("鍩庡競鍐�");
+        boolean turnover = s.contains("鍛ㄨ浆閲�");
+        if (city) return turnover ? "cityTurnover" : "cityPax";
+        return turnover ? "turnover" : "pax";
     }
 
     /** 杞ㄩ亾杞浮椤碉紙杞ㄩ亾=姝︽眽/榛勭煶锛涜疆娓′粎姝︽眽锛変笓鐢ㄥ洖濉� */
@@ -192,15 +371,27 @@
         return -1;
     }
 
-    /** 鍐欐暟鍊硷細鏍间笉瀛樺湪鍒欏缓鏍煎苟澶嶅埗鍚岃 1 鏈堝垪鏍峰紡锛涜繑鍥� 1 */
+    /** 鍐欐暟鍊硷細鏍间笉瀛樺湪鍒欏缓鏍硷紱鏍煎甫鏃ユ湡鏍峰紡锛堝鍏氦/鍑虹 8 鏈堝垪缁ф壙浜嗚〃澶粹�測yyy骞磎鏈堚�濓級鏃舵敼鍥炲悓琛� 1 鏈堝垪鐨勬暟鍊兼牱寮忥紝閬垮厤鏁板�兼樉绀烘垚鏃ユ湡 */
     private int setNumeric(Sheet sh, int r, int col, double val) {
         Row row = sh.getRow(r);
         if (row == null) row = sh.createRow(r);
         Cell c = row.getCell(col);
+        if (c != null && c.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) {
+            return 0; // 姣嶇増鏈潵灏遍潬鍏紡绠楃殑琛岋紙鍚堣/鍚岄〉姹傚拰锛変繚鎸佸叕寮忥紝浜� Excel 閲嶇畻
+        }
+        Cell ref = row.getCell(FIRST_MONTH_COL);
+        boolean needStyle = false;
         if (c == null) {
             c = row.createCell(col);
-            Cell ref = row.getCell(FIRST_MONTH_COL);
-            if (ref != null && ref.getCellStyle() != null) c.setCellStyle(ref.getCellStyle());
+            needStyle = true;
+        } else if (c.getCellType() == org.apache.poi.ss.usermodel.CellType.BLANK
+                || (c.getCellType() == org.apache.poi.ss.usermodel.CellType.NUMERIC
+                    && org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(c))) {
+            // 姣嶇増鎶� 8 鏈堝垪鐨勮〃澶存棩鏈熸牸寮忓甫鍒颁簡鏁版嵁鏍硷紙鎴栨湁鏍煎紡鏃犲�硷級锛屽啓鍊煎墠鏀瑰洖鍚岃 1 鏈堝垪鐨勬暟瀛楁牸寮�
+            needStyle = true;
+        }
+        if (needStyle && ref != null && ref.getCellStyle() != null) {
+            c.setCellStyle(ref.getCellStyle());
         }
         c.setCellValue(val);
         return 1;
@@ -229,20 +420,193 @@
         return row == null ? null : cityOf(row.getCell(0));
     }
 
-    /** 鏈堜唤瀹归噺锛氬湪琛ㄥご锛堝墠 5 琛岋級鎵锯��2026鈥︾疮璁♀�濆垪锛屽閲�=(绱鍒�-1 鏈堝垪)/2 */
+    /**
+     * 涓虹 month 鏈堣ˉ鈥滃綋鏈堝�尖�濆垪鍏紡锛氬彇鍚屼竴琛屾渶杩戜竴涓湀浠嶄负鍏紡鐨勫�煎垪锛屾暣鍒楀彸绉昏繃鏉�
+     * 锛堝鍏氦 7 鏈堝垪 O 鐨� =O9+O13+... -> 8 鏈堝垪 Q 鐨� =Q9+Q13+...锛夈��
+     * 鍙ˉ绌哄崟鍏冩牸锛涘凡鏈夋暟鍊硷紙璐ц繍鍏ㄧ渷鍧�/甯傚窞鍧楃敱鏁版嵁鍥炲~锛夋垨宸叉湁鍏紡鐨勬牸涓嶅姩銆�
+     */
+    public int ensureMonthFormulas(XSSFWorkbook wb, int year, int month) {
+        if (wb == null || month <= 1) return 0;
+        int targetCol = FIRST_MONTH_COL + 2 * (month - 1);
+        int written = 0;
+        for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+            Sheet sh = wb.getSheetAt(i);
+            if (sh == null || !isFilledLedgerSheet(sh.getSheetName())) continue;
+            for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+                Row row = sh.getRow(r);
+                if (row == null) continue;
+                if (hasContent(row.getCell(targetCol))) continue;
+                Cell srcCell = null;
+                int srcMonth = -1;
+                for (int s = month - 1; s >= 1; s--) {
+                    Cell c = row.getCell(FIRST_MONTH_COL + 2 * (s - 1));
+                    if (c != null && c.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) {
+                        srcCell = c;
+                        srcMonth = s;
+                        break;
+                    }
+                }
+                if (srcCell == null) continue;
+                String f = srcCell.getCellFormula();
+                if (f == null || f.isEmpty()) continue;
+                Cell target = row.getCell(targetCol);
+                if (target == null) target = row.createCell(targetCol);
+                if (srcCell.getCellStyle() != null) target.setCellStyle(srcCell.getCellStyle());
+                target.setCellFormula(shiftFormulaColumns(f, 2 * (month - srcMonth)));
+                written++;
+            }
+        }
+        return written;
+    }
+
+    /** 璇ユ牸鏄惁宸叉湁鍐呭锛堟暟鍊�/鏂囨湰/鍏紡锛夛紱浠呭甫鏍峰紡鐨勭┖澹筹紙濡傛瘝鐗堥噷缁ф壙琛ㄥご鏃ユ湡鏍煎紡鐨勬牸瀛愶級瑙嗕负绌� */
+    private boolean hasContent(Cell c) {
+        if (c == null) return false;
+        switch (c.getCellType()) {
+            case BLANK: return false;
+            case STRING: return c.getStringCellValue() != null && !c.getStringCellValue().trim().isEmpty();
+            default: return true;
+        }
+    }
+
+    /** 鍙備笌閫愭湀鏁版嵁鍥炲~鐨勫彴璐﹂〉锛堣揣杩�/鐝嚎鍖呰溅/鍏氦/鍑虹/缃戠害杞�/杞ㄩ亾杞浮锛� */
+    private boolean isFilledLedgerSheet(String name) {
+        if (name == null) return false;
+        String n = name.trim();
+        return "璐ц繍".equals(n) || "鐝嚎鍖呰溅".equals(n) || "鍏氦".equals(n)
+                || "鍑虹".equals(n) || "缃戠害杞�".equals(n) || "杞ㄩ亾銆佽疆娓�".equals(n);
+    }
+
+    /** 0 鍩哄垪鍙� + 1 鍩鸿鍙� -> "Q5" */
+    private String cellRef(int col0, int row1) {
+        StringBuilder sb = new StringBuilder();
+        int n = col0 + 1;
+        while (n > 0) {
+            int rem = (n - 1) % 26;
+            sb.insert(0, (char) ('A' + rem));
+            n = (n - 1) / 26;
+        }
+        return sb.toString() + row1;
+    }
+
+    /** 浠� "2026-08" 鍙栨湀浠斤紙瑙f瀽澶辫触杩斿洖 1锛� */
+    private int periodMonth(String period) {
+        if (period != null && period.length() >= 7) {
+            try { return Integer.parseInt(period.substring(5, 7)); } catch (Exception ignore) { }
+        }
+        return 1;
+    }
+
+    /** 浠� "2026-08" 鍙栧勾浠� */
+    private int periodYear(String period) {
+        if (period != null && period.length() >= 4) {
+            try { return Integer.parseInt(period.substring(0, 4)); } catch (Exception ignore) { }
+        }
+        return 0;
+    }
+
+    /**
+     * 涓虹 m 鏈堣ˉ鈥滀笌鍘诲勾鍚屾瘮鈥濆叕寮忥細鎶婄 m-1 鏈堝悓姣斿垪鐨勫叕寮忔暣浣撳彸绉� 2 鍒�
+     * 锛堝鍏氦 7 鏈� P5 鍏紡 =O5/AH5-1 -> 8 鏈� R5 =Q5/AJ5-1锛夛紝
+     * 鍥犳鍚屾瘮鍒嗘瘝鍙栫殑灏辨槸姹囨�昏〃 2025 骞村悓鏈堝垪锛堢敤鎴峰彛寰勶級銆�
+     */
+    public int ensureMonthYoyFormulas(XSSFWorkbook wb, int year, int month) {
+        if (wb == null || month <= 1) return 0;
+        int valueCol = FIRST_MONTH_COL + 2 * (month - 1);
+        int yoyCol = valueCol + 1;
+        int prevYoyCol = yoyCol - 2;
+        int written = 0;
+        for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+            Sheet sh = wb.getSheetAt(i);
+            if (sh == null) continue;
+            if (!isMonthLedgerSheet(sh.getSheetName())) continue;
+            for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+                Row row = sh.getRow(r);
+                if (row == null) continue;
+                Cell prev = row.getCell(prevYoyCol);
+                if (prev == null || prev.getCellType() != org.apache.poi.ss.usermodel.CellType.FORMULA) continue;
+                Cell target = row.getCell(yoyCol);
+                if (target != null && target.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) continue;
+                String f = prev.getCellFormula();
+                if (f == null || f.isEmpty()) continue;
+                String shifted = shiftFormulaColumns(f, 2);
+                if (target == null) {
+                    target = row.createCell(yoyCol);
+                    if (prev.getCellStyle() != null) target.setCellStyle(prev.getCellStyle());
+                }
+                try {
+                    target.setCellFormula(shifted);
+                    written++;
+                } catch (Exception e) {
+                    throw new IllegalStateException(String.format(
+                            "鍚屾瘮鍏紡琛ュ啓澶辫触锛歴heet=%s cell=%s 鍘熷紡=%s 鐩爣寮�=%s",
+                            sh.getSheetName(), cellRef(yoyCol, r + 1), f, shifted), e);
+                }
+            }
+        }
+        return written;
+    }
+
+    /** 闅忔湀鎵╁垪鐨勯暱琛ㄩ〉锛堣〃澶村湪 1..3 琛屻�佹湀浠界粍涓衡�滃綋鏈堝��+鍚屾瘮鈥濅氦鏇匡級 */
+    private boolean isMonthLedgerSheet(String name) {
+        if (name == null) return false;
+        String n = name.trim();
+        return "璐ц繍".equals(n) || "鍏矾鎬诲杩�".equals(n) || "鐝嚎鍖呰溅".equals(n) || "鍩庡競瀹㈣繍".equals(n)
+                || "鍏氦".equals(n) || "鍑虹".equals(n) || "缃戠害杞�".equals(n)
+                || "杞ㄩ亾銆佽疆娓�".equals(n) || "杞ㄩ亾杞浮".equals(n)
+                || "涓彛寰勬槑缁�".equals(n) || "涓彛寰勫杩愰噺".equals(n);
+    }
+
+    /** 鍏紡鍐呮墍鏈� A1 褰㈠紡鐨勫垪寮曠敤鍙崇Щ delta 鍒楋紙SUM 绛夊嚱鏁板悕鍚庢棤鏁板瓧锛屼笉浼氳鍖归厤锛� */
+    private String shiftFormulaColumns(String formula, int delta) {
+        java.util.regex.Matcher m = java.util.regex.Pattern
+                .compile("(?<![A-Za-z0-9_$])([$]?)([A-Z]{1,3})([$]?)([0-9]+)")
+                .matcher(formula);
+        StringBuffer sb = new StringBuffer();
+        while (m.find()) {
+            String col = m.group(2);
+            int idx = 0;
+            for (int i = 0; i < col.length(); i++) idx = idx * 26 + (col.charAt(i) - 'A' + 1);
+            int nidx = idx + delta;
+            StringBuilder nc = new StringBuilder();
+            while (nidx > 0) {
+                int rem = (nidx - 1) % 26;
+                nc.insert(0, (char) ('A' + rem));
+                nidx = (nidx - 1) / 26;
+            }
+            m.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(m.group(1) + nc + m.group(3) + m.group(4)));
+        }
+        m.appendTail(sb);
+        return sb.toString();
+    }
+
+    /** 鏈堜唤瀹归噺锛氫紭鍏堝彇琛ㄥご锛堝墠 5 琛岋級鈥�2026鈥︾疮璁♀�濆垪鍙嶆帹锛涜揣杩愰〉鏃犺鏂囨锛�
+     *  鍐嶇敤琛ㄥご閲屾渶鍚庝竴涓��2026骞碝鏈堚��/鏃ユ湡鏍煎紡鐨勬湀鍒楀厹搴曪紙濡傝揣杩� Q3=2026-08-01 -> 8锛� */
     private int monthColumnCapacity(Sheet sh) {
+        int byCumulative = 0;
+        int byMonthHeader = 0;
         for (int r = 0; r <= 4 && r <= sh.getLastRowNum(); r++) {
             Row row = sh.getRow(r);
             if (row == null) continue;
             for (Cell c : row) {
                 if (c == null) continue;
+                int idx = c.getColumnIndex();
+                if (idx <= FIRST_MONTH_COL || (idx - FIRST_MONTH_COL) % 2 != 0) continue; // 鍙湅鈥滃綋鏈堝�尖�濆垪
                 String t = text(c);
-                if (t != null && t.contains("2026") && t.contains("绱") && c.getColumnIndex() > FIRST_MONTH_COL) {
-                    return (c.getColumnIndex() - FIRST_MONTH_COL) / 2;
+                if (byCumulative == 0 && t != null && t.contains("2026") && t.contains("绱")) {
+                    byCumulative = (idx - FIRST_MONTH_COL) / 2;
+                }
+                if (t != null && t.contains("2026") && t.contains("鏈�") && !t.contains("绱")
+                        && !t.contains("鍚屾瘮")) {
+                    byMonthHeader = Math.max(byMonthHeader, (idx - FIRST_MONTH_COL) / 2 + 1);
+                } else if (c.getCellType() == org.apache.poi.ss.usermodel.CellType.NUMERIC
+                        && org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(c)) {
+                    byMonthHeader = Math.max(byMonthHeader, (idx - FIRST_MONTH_COL) / 2 + 1);
                 }
             }
         }
-        return 7;
+        int cap = Math.max(byCumulative, byMonthHeader);
+        return cap > 0 ? cap : 7;
     }
 
     private String text(Cell c) {
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java
index baf3d26..947eff3 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java
@@ -3,7 +3,9 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.trafficaudit.common.util.RegionUtil;
 import com.trafficaudit.dataimport.entity.CityTaxiMonthly;
+import com.trafficaudit.dataimport.entity.WycOrderMonthly;
 import com.trafficaudit.dataimport.mapper.CityTaxiMonthlyMapper;
+import com.trafficaudit.dataimport.mapper.WycOrderMonthlyMapper;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellType;
 import org.apache.poi.ss.usermodel.DataFormatter;
@@ -76,6 +78,8 @@
 
     @Resource
     private CityTaxiMonthlyMapper cityTaxiMapper;
+    @Resource
+    private WycOrderMonthlyMapper wycOrderMapper;
     @Value("${city-passenger.template-dir:docs/鍩庡競瀹㈣繍}")
     private String templateDir;
 
@@ -190,6 +194,8 @@
 
     /** 璇诲彇 docs/鍩庡競瀹㈣繍/銆婄綉绾﹁溅璁㈠崟鍙婂叏鐪佹�婚噺.xlsx銆嬶細鎸� period(yyyy-MM) 琛屽彇 pin(B-E) 涓� 17 甯傚窞璁㈠崟(G-W) */
     private Map<String, Object> loadMonthlyInput(String period) throws Exception {
+        Map<String, Object> db = loadMonthlyInputFromDb(period);
+        if (db != null) return db;
         File f = findInput();
         try (InputStream in = new FileInputStream(f);
              Workbook wb = WorkbookFactory.create(in)) {
@@ -226,6 +232,38 @@
         }
     }
 
+    /** 浼樺厛浠庡簱鍙栨暟锛坵yc_order_monthly锛夛紱搴撻噷鏃犺鏈熸垨璁㈠崟鍏ㄧ┖鏃惰繑鍥� null锛屽洖閫�璇绘瘝鐗堟枃浠� */
+    private Map<String, Object> loadMonthlyInputFromDb(String period) {
+        WycOrderMonthly row = wycOrderMapper.selectOne(new LambdaQueryWrapper<WycOrderMonthly>()
+                .eq(WycOrderMonthly::getReportPeriod, period).last("LIMIT 1"));
+        if (row == null) return null;
+        List<String> cities = RegionUtil.cityList();
+        Double[] vals = {
+                row.getOrderWuhan(), row.getOrderHuangshi(), row.getOrderShiyan(), row.getOrderYichang(),
+                row.getOrderXiangyang(), row.getOrderEzhou(), row.getOrderJingmen(), row.getOrderXiaogan(),
+                row.getOrderJingzhou(), row.getOrderHuanggang(), row.getOrderXianning(), row.getOrderSuizhou(),
+                row.getOrderEnshi(), row.getOrderXiantao(), row.getOrderQianjiang(), row.getOrderTianmen(),
+                row.getOrderShennong()
+        };
+        double[] orders = new double[cities.size()];
+        double sum = 0.0;
+        for (int i = 0; i < orders.length && i < vals.length; i++) {
+            orders[i] = vals[i] == null ? 0.0 : vals[i];
+            sum += orders[i];
+        }
+        if (sum <= 0) return null;
+        double orderSum = sum;
+        if (row.getOrderSum() != null && row.getOrderSum() > 0) orderSum = row.getOrderSum();
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("pinK", row.getPinK() == null ? 0.0 : row.getPinK());
+        map.put("pinCK", row.getPinCK() == null ? 0.0 : row.getPinCK());
+        map.put("pinZ", row.getPinZ() == null ? 0.0 : row.getPinZ());
+        map.put("pinCZ", row.getPinCZ() == null ? 0.0 : row.getPinCZ());
+        map.put("orders", orders);
+        map.put("orderSum", orderSum);
+        map.put("source", "db");
+        return map;
+    }
     private File findInput() throws Exception {
         String rel = templateDir;
         while (rel.startsWith("./")) rel = rel.substring(2);
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
index fb82de4..c8ae866 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -3347,8 +3347,14 @@
             cleanSummarySheetPresentation(wb); // 鍙栨秷鍚勯〉绛涢�夈�佽繕鍘熼殣钘忚锛堝惁鍒欌�滅彮绾垮寘杞︹�濈瓑椤电湅璧锋潵鍍忕己鏁版嵁锛�
             int effMonth = (toMonth != null && toMonth > 0 && toMonth < month) ? toMonth : month;
             List<String> dbProblems = new ArrayList<>();
-            int dbFilled = summaryWorkbookFiller.fillMonthlyLedger(wb, year, effMonth, dbProblems);
-            log.info("姹囨�诲伐浣滅翱鏁版嵁椹卞姩鍥炲~瀹屾垚锛氬啓鍏ュ彴璐﹂〉褰撴湀鍊兼牸鏁�={}锛岀己鏁版彁绀�={}", dbFilled, dbProblems);
+            String effPeriod = String.format("%04d-%02d", year, effMonth);
+            int dbFilled = summaryWorkbookFiller.fillMonthlyLedger(wb, effPeriod, year, effMonth, dbProblems);
+            int yoyFilled = summaryWorkbookFiller.ensureMonthYoyFormulas(wb, year, effMonth);
+            int monthFormulaFilled = summaryWorkbookFiller.ensureMonthFormulas(wb, year, effMonth);
+            int widthApplied = applySummaryReferenceLayout(wb);
+            log.info("姹囨�诲伐浣滅翱鏁版嵁椹卞姩鍥炲~瀹屾垚锛氬啓鍏ュ綋鏈堝�兼牸鏁�={}锛岃ˉ鍐欏綋鏈堝悓姣斿叕寮忔牸鏁�={}锛岃ˉ鍐欏綋鏈堝悎璁″叕寮忔牸鏁�={}锛�"
+                    + "鎸夈�婄敓鎴恄閬撹矾杩愯緭閲忔眹鎬昏〃銆嬪榻愬垪瀹藉垪鏁�={}锛岀己鏁版彁绀�={}",
+                    dbFilled, yoyFilled, monthFormulaFilled, widthApplied, dbProblems);
             applyTwoDecimalFormat(wb); // 鏁板�兼樉绀轰袱浣嶅皬鏁帮紙淇濈暀鍏ㄧ簿搴︼級锛�% / 鏃ユ湡绛夋棦鏈夋牱寮忎笉鏀瑰彉
             if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr();
             wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // 杩樺師/鏀瑰啓鍏紡鍚庢墦寮�鍗抽噸绠�
@@ -3358,6 +3364,100 @@
         }
     }
 
+    /**
+     * 鐗堝紡瀵归綈锛氭寜 docs/鐢熸垚姹囨�诲ぇ琛�/銆婄敓鎴恄閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx銆嬶紙鐢ㄦ埛鎻愪緵鐨勭増寮忓弬鐓э級閫愬垪濂楃敤鍒楀涓庨殣钘忔爣璁帮紝
+     * 浣跨敓鎴愪欢涓庡弬鐓т欢鈥滄牸寮忓畬鍏ㄤ竴鑷粹�濓紝骞惰姣嶇増娌″畾涔夊搴︾殑鈥滃悗缁墿鍒椻�濓紙濡� 8 鏈堝垪锛変篃鏈変笌 8 鏈堜竴鑷寸殑鍒楀銆�
+     * 鍙傜収浠剁己澶辨椂闈欓粯璺宠繃锛堣繑鍥� 0锛夛紝涓嶅奖鍝嶅師鏈夊鍑恒��
+     */
+    private int applySummaryReferenceLayout(XSSFWorkbook wb) {
+        File ref;
+        try {
+            ref = resolveSummaryLayoutRef();
+        } catch (Exception e) {
+            return 0;
+        }
+        if (ref == null) return 0;
+        double savedZipRatio = ZipSecureFile.getMinInflateRatio();
+        ZipSecureFile.setMinInflateRatio(0.0001);
+        int applied = 0;
+        try (InputStream in = new FileInputStream(ref); XSSFWorkbook rwb = new XSSFWorkbook(in)) {
+            for (int i = 0; i < rwb.getNumberOfSheets(); i++) {
+                org.apache.poi.xssf.usermodel.XSSFSheet rs = rwb.getSheetAt(i);
+                if (rs == null) continue;
+                org.apache.poi.xssf.usermodel.XSSFSheet ts = wb.getSheet(rs.getSheetName());
+                if (ts == null) continue;
+                int maxCol = Math.min(usedColumnCount(rs), usedColumnCount(ts) + 4);
+                if (maxCol <= 0) continue;
+                double[] width = new double[maxCol];
+                boolean[] hasWidth = new boolean[maxCol];
+                boolean[] hidden = new boolean[maxCol];
+                for (org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols cols : rs.getCTWorksheet().getColsArray()) {
+                    for (org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol col : cols.getColArray()) {
+                        long min = col.getMin();
+                        long max = col.getMax();
+                        for (long c = min; c <= max; c++) {
+                            int idx = (int) (c - 1);
+                            if (idx < 0 || idx >= maxCol) {
+                                if (idx >= maxCol) break;
+                                continue;
+                            }
+                            if (col.isSetWidth()) {
+                                width[idx] = col.getWidth();
+                                hasWidth[idx] = true;
+                            }
+                            hidden[idx] = col.isSetHidden() && col.getHidden();
+                        }
+                    }
+                }
+                for (int c = 0; c < maxCol; c++) {
+                    if (hasWidth[c]) {
+                        int w = (int) Math.round(width[c] * 256d);
+                        if (ts.getColumnWidth(c) != w) {
+                            ts.setColumnWidth(c, w);
+                            applied++;
+                        }
+                    }
+                    if (hidden[c] != ts.isColumnHidden(c)) ts.setColumnHidden(c, hidden[c]);
+                }
+            }
+            log.info("姹囨�诲伐浣滅翱鐗堝紡瀵归綈銆婄敓鎴恄閬撹矾杩愯緭閲忔眹鎬昏〃銆嬪畬鎴愶細濂楃敤鍒楀 {} 鍒楋紙鍙傜収浠讹細{}锛�", applied, ref.getName());
+        } catch (Exception e) {
+            log.warn("姹囨�诲伐浣滅翱鐗堝紡瀵归綈澶辫触锛堝拷鐣ワ紝缁х画鎸夋瘝鐗堢増寮忚緭鍑猴級锛歿}", e.getMessage());
+            return 0;
+        } finally {
+            ZipSecureFile.setMinInflateRatio(savedZipRatio);
+        }
+        return applied;
+    }
+
+    /** 涓�椤靛凡鐢ㄥ埌鐨勬渶澶у垪鏁帮紙0 鍩鸿鏁帮級 */
+    private int usedColumnCount(org.apache.poi.ss.usermodel.Sheet sh) {
+        int max = 0;
+        for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+            org.apache.poi.ss.usermodel.Row row = sh.getRow(r);
+            if (row == null) continue;
+            if (row.getLastCellNum() > max) max = row.getLastCellNum();
+        }
+        return max;
+    }
+
+    /** 瀹氫綅鐗堝紡鍙傜収浠� docs/鐢熸垚姹囨�诲ぇ琛�/鐢熸垚_閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx锛堜紭鍏堢骇鍚屾瘝鐗堬細閰嶇疆鐩綍 鈫� user.dir 鐩稿 鈫� 涓婄骇锛� */
+    private File resolveSummaryLayoutRef() {
+        String rel = summaryTemplateDir;
+        while (rel != null && rel.startsWith("./")) rel = rel.substring(2);
+        String[] roots = {
+            summaryTemplateDir,
+            System.getProperty("user.dir") + "/" + rel,
+            System.getProperty("user.dir") + "/../" + rel
+        };
+        for (String root : roots) {
+            if (root == null || root.trim().isEmpty()) continue;
+            File f = new File(root, "鐢熸垚_閬撹矾杩愯緭閲忔眹鎬昏〃.xlsx");
+            if (f.isFile()) return f;
+        }
+        return null;
+    }
+
     /** 姹囨�昏〃姣嶇増瀹氫綅锛氫粎绮剧‘鍖归厤 {year}骞磠month}鏈堚�︼紙P0-P1 楠ㄦ灦鏈熶笉鍏佽璺ㄦ湀/璺ㄥ勾闈欓粯鍥為��锛�
      *  鐩爣鏈熸棤瀵瑰簲姣嶇増鏃舵槑纭姤閿欙紝鏈堜唤鎵╁睍瑙勫垯寰� P0 姣嶇増瀹氱鍚庡啀鏀惧紑锛� */
     private File resolveSummaryMother(String period, String mode) throws Exception {
diff --git a/traffic-audit-web/src/views/DataImport.vue b/traffic-audit-web/src/views/DataImport.vue
index 7ed598b..b9c83e7 100644
--- a/traffic-audit-web/src/views/DataImport.vue
+++ b/traffic-audit-web/src/views/DataImport.vue
@@ -1,4 +1,3 @@
-SKIP/CHECK (0): TYPE_LABELS upper <template>
   <el-container direction="vertical">
     <app-header title="浜ら�氱粺璁℃姤琛ㄥ鏍哥郴缁� - 鏁版嵁瀵煎叆"></app-header>
     <el-container>
@@ -54,7 +53,7 @@
                 <div v-else-if="moduleSel[g.key] && !isDirOnlyType(moduleSel[g.key])" class="module-file">
                   <div class="file-name" :title="fileNamesText(moduleSel[g.key])">{{ fileNamesText(moduleSel[g.key]) || '鏈�夋嫨鏂囦欢' }}</div>
                   <div class="file-btns">
-                    <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple accept=".xlsx,.xls,.et"
+                    <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple :accept="acceptOf(moduleSel[g.key])"
                                :ref="'up' + g.key" :on-change="f => onFilePicked(g.key, f)" style="display:inline-block">
                       <el-button size="mini" type="primary" plain icon="el-icon-folder-opened" :disabled="importing">閫夋嫨鏂囦欢</el-button>
                     </el-upload>
@@ -165,11 +164,11 @@
   investCityPassenger: '甯傚窞鍗曡〃锛堝杩愮珯鍦猴級', investCityLogistics: '甯傚窞鍗曡〃锛堢墿娴佸洯鍖猴級',
   'INVEST_瀹㈣繍绔欏満_CITY': '甯傚窞鍗曡〃锛堝杩愮珯鍦猴級', 'INVEST_鐗╂祦鍥尯_CITY': '甯傚窞鍗曡〃锛堢墿娴佸洯鍖猴級',
   cityBus: '鍩庡競鍏氦鏈堟姤', cityTaxi: '宸℃父鍑虹鏈堟姤', cityTaxiAuth: '鍑虹杞﹁繍鏀胯溅杈�',
-  wycTotal: '缃戠害杞︽�绘暟', wycOrder: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺',
+  wycTotal: '缃戠害杞︽�绘暟', wycOrder: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺', wycOrderReport: '缃戠害杞﹁鍗曟姤鍛�',
   H2032: 'H203-2鏈堟姤', H2031: 'H203-1鏃呭鏈堟姤', H204: 'H204鑳借�楀鎶�', PASSENGER_AUTH: '鏃呭杩愭斂杞﹁締鏁�',
   PASSENGER_INDIVIDUAL: '涓綋瀹㈣繍閲�/鍛ㄨ浆閲�', ENERGY_AUTH: '鑳借�楄溅杈嗚繍鏀夸俊鎭�', TRANSPORT_AUTH: '杩愭斂杞﹁締',
   TRACK_MILEAGE: '杞ㄨ抗閲岀▼', SCALE_SPLIT: '瑙勪笂瑙勪笅鎷嗗垎', FREIGHT_TURNOVER: '璐ц繍閲忓懆杞噺',
-  INVEST_SYSTEM: '鎶曡祫绯荤粺瀵煎嚭', CITY_BUS: '鍩庡競鍏氦鏈堟姤', CITY_TAXI: '宸℃父鍑虹鏈堟姤', CITY_TAXI_AUTH: '鍑虹杞﹁繍鏀胯溅杈�', WYC_TOTAL: '缃戠害杞︽�绘暟', WYC_ORDER: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺'
+  INVEST_SYSTEM: '鎶曡祫绯荤粺瀵煎嚭', CITY_BUS: '鍩庡競鍏氦鏈堟姤', CITY_TAXI: '宸℃父鍑虹鏈堟姤', CITY_TAXI_AUTH: '鍑虹杞﹁繍鏀胯溅杈�', WYC_TOTAL: '缃戠害杞︽�绘暟', WYC_ORDER: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺', WYC_ORDER_REPORT: '缃戠害杞﹁鍗曟姤鍛�'
 }
 const BATCH_TYPE_MAP = {
   H2032: 'h2032', TRANSPORT_AUTH: 'transportAuth', TRACK_MILEAGE: 'trackMileage',
@@ -177,7 +176,7 @@
   H2031: 'passengerMonthly', PASSENGER_AUTH: 'passengerAuth', PASSENGER_INDIVIDUAL: 'passengerIndividual',
   H204: 'energyMonthly', ENERGY_AUTH: 'energyAuth',
   CITY_BUS: 'cityBus', CITY_TAXI: 'cityTaxi', CITY_TAXI_AUTH: 'cityTaxiAuth',
-  WYC_TOTAL: 'wycTotal', WYC_ORDER: 'wycOrder',
+  WYC_TOTAL: 'wycTotal', WYC_ORDER: 'wycOrder', WYC_ORDER_REPORT: 'wycOrderReport',
   INVEST_SYSTEM: 'investmentSystem'
 }
 export default {
@@ -226,7 +225,8 @@
             { value: 'cityTaxi', label: '宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤' },
             { value: 'cityTaxiAuth', label: '鍑虹杞﹁繍鏀胯溅杈�' },
             { value: 'wycTotal', label: '缃戠害杞︽�绘暟' },
-            { value: 'wycOrder', label: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺' }
+            { value: 'wycOrder', label: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺' },
+            { value: 'wycOrderReport', label: '缃戠害杞﹁鍗曟姤鍛�' }
           ]
         },
         {
@@ -299,7 +299,8 @@
         cityTaxi: '瀵煎叆_宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤锛圷YYY骞碝鏈堬級.xls锛堝競宸炵骇17琛岋級',
         cityTaxiAuth: '瀵煎叆妯℃澘_鍑虹杞︿俊鎭�(杩愭斂杞﹁締淇℃伅).xlsx锛堣溅杈嗙骇鏄庣粏锛岀敤浜庤繍钀ヨ溅鏁板姣旓級',
         wycTotal: '妯℃澘_缃戠害杞︽�绘暟.xlsx锛圓鍒�=鏈堜唤锛孊-E=鍏ㄧ渷瀹㈣繍閲�/鍩庡競鍐�/鍛ㄨ浆閲�/鍩庡競鍐呭懆杞噺锛�',
-        wycOrder: '妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx锛圓鍒�=鏈堜唤锛孊-E=鍏ㄧ渷pin锛孎=璁㈠崟鍚堣锛孏-W=17甯傚窞璁㈠崟锛�'
+        wycOrder: '妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx锛圓鍒�=鏈堜唤锛孊-E=鍏ㄧ渷pin锛孎=璁㈠崟鍚堣锛孏-W=17甯傚窞璁㈠崟锛�',
+        wycOrderReport: '妯℃澘_缃戠害杞﹁鍗�.docx锛堛�婄綉绾﹁溅琛屼笟YYYY骞碝鏈堣繍琛岀洃娴嬫儏鍐靛垎鏋愭姤鍛娿�嬶紝鑷姩瑙f瀽闄勪欢琛ㄣ�屽湴甯傚悕绉扳�﹁鍗曟暟鎬绘暟銆嶇殑 17 甯傚窞璁㈠崟锛�'
       }
       return map[this.importType] || ''
     },
@@ -320,6 +321,7 @@
         cityTaxiAuth: '鍩庡競瀹㈣繍/瀵煎叆妯℃澘_鍑虹杞︿俊鎭�(杩愭斂杞﹁締淇℃伅).xlsx',
         wycTotal: '鍩庡競瀹㈣繍/妯℃澘_缃戠害杞︽�绘暟.xlsx',
         wycOrder: '鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx',
+        wycOrderReport: '鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗�.docx',
         investment: '鎶曡祫/杈撳叆/瀹㈣繍锛堝杩愮珯锛�/鏇存柊_7鏈堝杩愮珯鎶曡祫鏄庣粏琛紙甯傚窞姹囨�诲ぇ琛紝缁欓瀵硷級.xlsx',
         investmentLogistics: '鎶曡祫/杈撳叆/鐗╂祦锛堢墿娴佸洯鍖猴級/鏇存柊_7鏈堢墿娴佺珯鍦烘姇璧勬槑缁�.xls',
         investmentSystem: '鎶曡祫/妯℃澘_鏌ヨ缁撴灉(鎶曡祫绯荤粺-瀹㈣繍+鐗╂祦 2026.7).xls'
@@ -359,7 +361,8 @@
         cityTaxi: '/import/cityTaxi',
         cityTaxiAuth: '/import/cityTaxiAuth',
         wycTotal: '/import/wycTotal',
-        wycOrder: '/import/wycOrder'
+        wycOrder: '/import/wycOrder',
+        wycOrderReport: '/import/wycOrderReport'
       }
       return map[t] || ''
     },
@@ -464,6 +467,7 @@
           this.$message.error((e.response && e.response.data && e.response.data.message) || '鎵归噺瀵煎叆澶辫触')
         })
     },
+    acceptOf(type) { return type === 'wycOrderReport' ? '.docx,.doc' : '.xlsx,.xls,.et' },
     isCityDirType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' },
     isDirOnlyType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' },
     importCityDir(type) {
@@ -550,6 +554,7 @@
               map[key] = { totalRows: r.totalRows, successRows: r.successRows, failRows: r.failRows, latestId: r.id }
             }
           })
+          if (map.wycOrderReport && !map.wycOrder) map.wycOrder = map.wycOrderReport
           this.overview = map
         })
         .catch(e => { if (!e || !e._toast) this.$message.error('瀵煎叆璁板綍鍔犺浇澶辫触锛岃鍒锋柊閲嶈瘯') })

--
Gitblit v1.9.1