From 0cfd6e093e08566b4199836a54d827067580007e Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期三, 16 九月 2026 11:58:23 +0800
Subject: [PATCH] feat(import): 货运量周转量支持多年度工作簿(21-26年)一次性入库;fix(report): 汇总大表预览删除中口径之后的明细表
---
traffic-audit-web/src/views/ReportExport.vue | 2
docs/功能测试报告/功能测试报告_货运量周转量多年度导入_2026-09-16.md | 50 ++++++++
traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java | 170 +++++++++++++++++++++++++---
docs/工作日结/工作日结_2026-09-16.md | 35 +++++
docs/功能测试报告/功能测试报告_汇总大表预览删除中口径后明细表_2026-09-16.md | 47 +++++++
5 files changed, 285 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\250\351\242\204\350\247\210\345\210\240\351\231\244\344\270\255\345\217\243\345\276\204\345\220\216\346\230\216\347\273\206\350\241\250_2026-09-16.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\250\351\242\204\350\247\210\345\210\240\351\231\244\344\270\255\345\217\243\345\276\204\345\220\216\346\230\216\347\273\206\350\241\250_2026-09-16.md"
new file mode 100644
index 0000000..11bf852
--- /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\250\351\242\204\350\247\210\345\210\240\351\231\244\344\270\255\345\217\243\345\276\204\345\220\216\346\230\216\347\273\206\350\241\250_2026-09-16.md"
@@ -0,0 +1,47 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡锛氭姤琛ㄧ敓鎴�-姹囨�诲ぇ琛ㄩ瑙堝脊绐楀垹闄ゃ�屼腑鍙e緞銆嶄箣鍚庣殑鏄庣粏琛� 2026-09-16
+
+## 鍔熻兘璇存槑
+- 浣嶇疆锛氭姤琛ㄧ敓鎴愰〉 鈫� 鎶ヨ〃绫诲瀷銆屾眹鎬诲ぇ琛ㄣ�嶁啋 閫夋湀浠� 鈫� 鍒楄〃琛屻�岄瑙堛�嶁啋 寮圭獥銆岄瑙堟暟鎹� - 姹囨�诲ぇ琛紙YYYY-MM锛夈�嶃��
+- 鐜拌薄锛堟敼鍔ㄥ墠锛夛細寮圭獥閲屽嚭鐜�**涓ゅ紶琛�**鈥斺�旂涓�寮犳槸銆屾寚鏍� / 鏁版嵁 / 鍚屾瘮銆嶄笁鍒楃殑姹囨�昏〃锛堝惈閬撹矾璐ц繍銆佸煄甯傚杩愩�佷腑鍙e緞涓夋锛夛紝绗簩寮犳槸涓彛寰勪箣鍚庨噸澶嶆覆鏌撶殑涓�寮犳槑缁嗚〃锛屼俊鎭啑浣欍��
+- 闇�姹傦細鎶婃眹鎬诲ぇ琛ㄩ瑙堥噷**涓彛寰勫悗闈㈢殑琛ㄦ牸鍏ㄩ儴鍒犳帀**锛屽彧淇濈暀涓婇潰閭e紶姹囨�昏〃銆�
+- 鏀瑰姩鍚庯細寮圭獥鍐呭彧鍓╃涓�寮犺〃锛坄class="preview-summary wb-table"`锛夛紝琛ㄦ牸涓嬫柟鏄袱鏉″悓姣旂己澶辨彁绀轰笌銆屽叧闂�嶆寜閽��
+
+## 娑夊強鏀瑰姩
+- 鍓嶇 `traffic-audit-web/src/views/ReportExport.vue`锛�**鍗曡鏀瑰姩**锛堢 133 琛岋級锛�
+ - 鍘燂細`<table class="preview-summary" v-else-if="it.summary && it.summary.length">`
+ - 鐜帮細`<table class="preview-summary" v-if="it.key !== 'summaryWorkbook' && it.summary && it.summary.length">`
+- 鍘熷洜璇存槑锛歚previewItems` 閲� `summaryWorkbook` 杩欎竴椤瑰悓鏃跺甫鏈� `summary` 鏁版嵁锛岃�屼笂闈㈢殑姹囨�昏〃锛堢 115 琛� `v-if="it.key === 'summaryWorkbook' && it.summary && it.summary.length"`锛夊凡缁忔槸 `v-if`锛涘師鏉ョ殑绗簩绉嶈〃鐢ㄧ殑鏄� `v-else-if`锛屼細璺熷湪鍓嶄竴涓� `v-if` 涔嬪悗锛屼簬鏄 `summaryWorkbook` 涔熺収鏍锋覆鏌撳嚭绗簩寮犺〃銆傛敼鎴愬甫 `it.key !== 'summaryWorkbook'` 鏉′欢鐨勭嫭绔� `v-if` 鍚庯紝姹囨�诲ぇ琛ㄤ笉鍐嶆覆鏌撶浜屽紶琛紝鑰屽叾瀹冩姤琛ㄧ被鍨嬶紙濡傛湁 `summary` 鐨勶級琛屼负淇濇寔涓嶅彉銆�
+- 绗� 115鈥�132 琛岀殑姹囨�昏〃锛堝惈銆岄儴鍒嗘寚鏍囩己灏戜笂骞村悓鏈熲�︺�嶆彁绀猴級**鏈敼鍔�**銆�
+- 鏈敼鍚庣銆佹湭鏀规帴鍙c�佹湭鏀瑰鍑恒��
+
+## 娴嬭瘯鐜
+- 鍓嶇锛歵raffic-audit-web 8080 dev锛堟敼鍔ㄥ悗 `Compiled successfully`锛夈��
+- 鍚庣锛歵raffic-audit-server 8090锛堟湰娆′笉娑夊強鍚庣锛夈��
+- 娴忚鍣細Codex 搴旂敤鍐呮祻瑙堝櫒锛宍admin` 鐧诲綍锛屽湴鍧� `http://localhost:8080/#/report`銆�
+- 琚祴鏁版嵁锛氭姤琛ㄧ被鍨嬨�屾眹鎬诲ぇ琛ㄣ�嶃�佹湀浠� `2026骞�8鏈坄锛堟簮鏁版嵁宸茬敱鏈澶氬勾搴﹀鍏ヤ繚璇侊級銆�
+
+## 娴嬭瘯鏂规硶锛堥噸瑕佽鏄庯級
+- **涓婁竴杞殑鎴浘涓嶈兘浣滀负璇佹嵁**锛氭棭鏈熸埅鍥� `_qa_out/preview_summaryWorkbook_2026-08_after_fix.png` 鏄湪寮圭獥灏氭湭娓叉煋/鍙栨櫙涓嶅綋鏃舵媿鐨勶紝鐢婚潰閲屾病鏈夊脊绐楀唴瀹癸紝灞炰簬鏃犳晥璇佹嵁锛涘悓鏃朵緷璧栧彲璁块棶鎬ф爲锛圓X锛夎鍙栦篃涓嶅彲闈狅紙鏈満 `getAXState()` 杩斿洖缂撳瓨锛屼細璇诲埌杩囨湡鍐呭锛夈��
+- 鏈疆鏀圭敤**鐩存帴璇� DOM**浣滀负鍞竴鍒ゆ嵁锛歚tab.playwright.evaluate(() => {...})` 缁熻寮圭獥鍐� `table` 鍏冪礌鐨勬暟閲忋�佺被鍚嶄笌琛屾暟銆�
+- 鍏抽敭鎿嶄綔鍧戯紙宸查伩寮�锛夛細`el-select` 涓嬫媺椤� `isVisible()` 鎭掍负 false锛屾棤娉曠敤鏂囨湰鐐瑰嚮閫夋湀浠斤紝鏀圭敤閿洏 `ArrowDown`/`Enter`锛涜鍐呫�岄瑙堛�嶆寜閽繀椤荤敤 `locator('.el-table__row button').nth(1)`锛坄has-text` 浼氬懡涓鐢ㄧ殑銆屾壒閲忛瑙堛�嶏級锛涢〉闈笂甯搁┗ 3 涓� `.el-dialog__wrapper`锛岄渶鎸� `getComputedStyle(w).display !== 'none'` 鎵惧綋鍓嶆墦寮�鐨勯偅涓紱鏀� `.vue` 鍚庡繀椤� `tab.reload()` 鎵嶈兘鎷垮埌鏈�鏂伴〉闈€��
+- 鏈夋晥鎴浘锛歚_qa_out/preview_summaryWorkbook_2026-08_verified_20260916.png`锛�54006 瀛楄妭锛変笌 `_qa_out/preview_summaryWorkbook_2026-08_verified2_20260916.png`锛�54006 瀛楄妭锛屾湰杞鏍告椂閲嶆媿锛夈��
+
+## 鐢ㄤ緥琛�
+| # | 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|---|
+| 1 | 姝e悜-寮圭獥鍙墦寮� | 姹囨�诲ぇ琛� / 2026骞�8鏈� / 鐐硅鍐呫�岄瑙堛�� | 寮瑰嚭銆岄瑙堟暟鎹� - 姹囨�诲ぇ琛紙2026-08锛夈�� | 寮圭獥姝e父鎵撳紑锛屾爣棰樹笌棰勬湡涓�鑷� | 閫氳繃 |
+| 2 | 姝e悜-琛ㄦ牸鏁伴噺 | DOM 缁熻褰撳墠鍙寮圭獥鍐� `table` 鏁伴噺 | 鍙墿 1 寮狅紙涓彛寰勪箣鍚庣殑琛ㄥ凡鍒犻櫎锛� | `visible` 寮圭獥浠� 1 涓紝鍏朵腑 `body.querySelectorAll('table').length === 1` | 閫氳繃 |
+| 3 | 姝e悜-琛ㄦ牸鏍囪瘑 | 璇诲彇璇ヨ〃 class 涓庤鏁� | 涓烘眹鎬昏〃锛坄preview-summary wb-table`锛夛紝12 琛屾寚鏍� | `cls="preview-summary wb-table"`锛宍tbody tr` 璁� 12 琛� | 閫氳繃 |
+| 4 | 姝e悜-鍐呭瀹屾暣 | 璇诲彇 12 琛屾枃鏈� | 閬撹矾璐ц繍/鍩庡競瀹㈣繍/涓彛寰勫悇 4 椤癸紝鏁板�间笌鍚屾瘮榻愬叏 | 閬撹矾璐ц繍锛氬綋鏈堣揣杩愰噺 16888.76銆佸綋鏈堣揣鐗╁懆杞噺 2270010.6锛堝悓姣� 4.8%锛夈�佺疮璁¤揣杩愰噺 130311.89銆佺疮璁¤揣鐗╁懆杞噺 17492129.05锛堝悓姣� 0%锛夛紱鍩庡競瀹㈣繍锛�29575.09 / 212391.4 / 254440.17 / 1806719.08锛涗腑鍙e緞锛�1628.65 / 115847.35 / 12691.45 / 900177.92 | 閫氳繃 |
+| 5 | 姝e悜-琛ㄦ牸涓嬫柟鍐呭 | 璇诲彇琛ㄦ牸涔嬪悗鐨勮妭鐐� | 鍙墿鎻愮ず涓庡叧闂寜閽紝鏃犵浜屽紶琛� | 渚濇涓恒�屄� 2025-08 鍚屾湡鍩庡競瀹㈣繍鏁版嵁鏈鍏ュ簱锛屽煄甯傚杩愬悓姣旀殏涓嶆樉绀衡�︺�嶃�屄� 2025-08 鍚屾湡鍏矾鏃呭鏁版嵁鏈鍏ュ簱锛屼腑鍙e緞鍚屾瘮鏆備笉鏄剧ず鈥︺�嶄笌銆屽叧闂�嶆寜閽� | 閫氳繃 |
+| 6 | 鍥炲綊-鍏跺畠鏉′欢涓嶅洖褰� | 妫�鏌ョ 115鈥�132 琛屾眹鎬昏〃涓� `wbMissing` 鎻愮ず | 鏈鏀瑰姩 | 涓庢敼鍔ㄥ墠涓�鑷达紙`git diff` 浠呯 133 琛� 1 琛屽彉鏇达級 | 閫氳繃 |
+| 7 | 鍥炲綊-鍏跺畠鎶ヨ〃绫诲瀷 | 妫�鏌� `v-if` 鏉′欢瀵归潪 `summaryWorkbook` 椤� | 琛屼负涓嶅彉锛堜粛鎸� `summary` 娓叉煋锛� | 鏉′欢涓� `it.key !== 'summaryWorkbook' && it.summary && it.summary.length`锛岄潪姹囨�诲ぇ琛ㄩ」琛屼负涓嶅彉 | 閫氳繃 |
+
+## 鍙戠幇鐨勯棶棰樹笌澶勭疆
+1. **涓婁竴杞彇璇佹棤鏁堬紙宸蹭慨姝o級**锛氭棭鏈熸埅鍥炬湭鎷嶅埌寮圭獥銆丄X 鏍戜负缂撳瓨锛屽凡鍦ㄦ湰鎶ュ憡涓綔搴燂紱鏈疆浠� DOM 瀹炴祴 + 鏈夋晥鎴浘閲嶆柊鍙栬瘉銆�
+2. **寮圭獥鍐呫�屽叡 0 琛屻�嶄笌涓婃柟銆屾湀搴﹁鐩栵細1~8鏈堝潎鏈夋暟鎹�嶅彛寰勪笉鍚�**锛歚rowCount` 琛ㄧず鐨勬槸鏄庣粏琛屾暟锛堟眹鎬诲ぇ琛ㄤ笉鍚槑缁嗚锛屾晠涓� 0锛夛紝杩欐槸鏃㈡湁灞曠ず鍙e緞锛岄潪鏈鏀瑰姩寮曞叆锛屾湰娆℃湭鏀癸紱濡傜敤鎴疯寰楁槗璇В锛屽彲鍙﹁璋冩暣鏂囨銆�
+3. 涓ゆ潯鍚屾瘮缂哄け鎻愮ず锛�2025-08 鍩庡競瀹㈣繍 / 鍏矾鏃呭鍚屾湡鏁版嵁鏈叆搴擄級涓烘暟鎹晶鐜扮姸锛岄潪鏈鏀瑰姩寮曡捣锛屽緟 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩娑堝け銆�
+
+## 缁撹
+- 閫氳繃銆傛眹鎬诲ぇ琛ㄩ瑙堝脊绐椾腑銆屼腑鍙e緞銆嶄箣鍚庣殑閭e紶琛ㄥ凡褰诲簳绉婚櫎锛孌OM 瀹炴祴寮圭獥鍐呮湁涓斾粎鏈� 1 寮犺〃锛坄preview-summary wb-table`锛�12 琛屾眹鎬绘寚鏍囷級锛岃〃鏍煎唴瀹逛笌鏀瑰姩鍓嶄竴鑷淬�佹棤淇℃伅涓㈠け锛涘叾瀹冩姤琛ㄧ被鍨嬬殑棰勮琛屼负涓嶅彈褰卞搷銆�
+- 寰呯敤鎴峰湪鑷繁鐨勬祻瑙堝櫒涓婃墦寮�銆屾姤琛ㄧ敓鎴� 鈫� 姹囨�诲ぇ琛� 鈫� 2026骞�8鏈� 鈫� 棰勮銆嶅仛涓�娆$洰瑙嗛獙鏀躲��
\ No newline at end of file
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_\350\264\247\350\277\220\351\207\217\345\221\250\350\275\254\351\207\217\345\244\232\345\271\264\345\272\246\345\257\274\345\205\245_2026-09-16.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_\350\264\247\350\277\220\351\207\217\345\221\250\350\275\254\351\207\217\345\244\232\345\271\264\345\272\246\345\257\274\345\205\245_2026-09-16.md"
new file mode 100644
index 0000000..c54758e
--- /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_\350\264\247\350\277\220\351\207\217\345\221\250\350\275\254\351\207\217\345\244\232\345\271\264\345\272\246\345\257\274\345\205\245_2026-09-16.md"
@@ -0,0 +1,50 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡锛氳揣杩愰噺鍛ㄨ浆閲忚〃澶氬勾搴﹀伐浣滅翱锛�2021鈥�2026锛変竴娆℃�у叆搴� 2026-09-16
+
+## 鍔熻兘璇存槑
+- 闇�姹傦細8 鏈堝鍏ョ殑銆屽畾绋�-婀栧寳鐪佸悇甯傚窞杩愯緭閲忔暟鎹紙2026骞�1-8鏈堬級.xlsx銆嶄竴涓伐浣滅翱鍐呭惈 21鈥�26 骞达紙婧愭枃浠剁己 2023 骞达級鍏ㄩ儴骞村害鏁版嵁锛岃姹傛妸杩欎簺鏁版嵁鍏ㄩ儴璇诲叆鏁版嵁搴撱��
+- 鏃ц涓猴細`importFreightTurnover` 鍙绗� 1 涓� Sheet锛屽苟鎸夈�屾ā鏉縚璐ц繍閲忓懆杞噺.xlsx銆嶇殑宸﹀彸涓ゅ崐锛堝乏鍗婁粖骞� 1鈥揘 鏈堛�佸彸鍗婂幓骞� 1鈥�12 鏈堬級瑙f瀽锛屽鑷村叾浣欏勾搴� Sheet 鐨勬暟鎹鐩存帴涓㈠純銆�
+- 鏂拌涓猴細瀵煎叆鏃跺厛鍒ゆ柇鏄惁涓恒�屽骞村害宸ヤ綔绨裤�嶏紙Sheet 鏁� 鈮� 2銆佹瘡涓� Sheet 鍚嶅舰濡� `2021骞碻銆佷笖棣栬 A 鍒楀惈銆岃揣杩愰噺銆嶃�丅 鍒楀惈銆屾湀銆嶏級銆�
+ - 鍛戒腑锛氶�� Sheet 瑙f瀽鏁村勾 1鈥�12 鏈堢殑璐ц繍閲忓潡涓庡懆杞噺鍧楋紝**姣忎釜骞村害钀戒竴涓姤琛ㄦ湡**锛堟姤琛ㄦ湡 = 璇ュ勾鏈�鍚庝竴涓湁璐ц繍閲忔暟鎹殑鏈堜唤锛屾湰娆′负 `YYYY-12`锛夛紝涓婂勾鍚屾湡鍙栦笂涓�骞村害 Sheet銆�
+ - 鏈懡涓細瀹屽叏鍥為��鍘熸湁鏈堝害瑙f瀽璺緞锛岃涓轰笌鏀瑰姩鍓嶄竴鑷淬��
+- 骞傜瓑锛氫粛鎸夋姤琛ㄦ湡銆屽厛鍒犲悗鎻掋�嶏紱姣忎釜骞村害鍚勫啓涓�鏉� `import_batch` 璁板綍銆�
+
+## 娑夊強鏀瑰姩
+- 鍚庣 `traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java`锛�+153 / 鈭�19锛夛細
+ - `importFreightTurnover` 鏀逛负鍒嗘祦鍏ュ彛锛涘師閫昏緫鏁翠綋鎶戒负 `importFreightTurnoverByMonth`锛堥�昏緫涓庢敼鍔ㄥ墠閫愯涓�鑷达級銆�
+ - 鏂板 `detectYearSheets`锛堝骞村害璇嗗埆锛夈�乣importFreightTurnoverByYear`锛堟寜骞磋В鏋� + 涓婂勾鍚屾湡锛夈�乣parseWholeYearSheet`锛堟暣骞� Sheet 瑙f瀽锛夈�乣persistFreightTurnover`锛堟寜鎶ヨ〃鏈熷厛鍒犲悗鎻� + 璁版壒娆★級銆�
+ - 椤堕儴鏂板 `import java.util.TreeMap;`銆�
+- 鏈敼鏁版嵁搴撶粨鏋勩�佹湭鏀瑰墠绔�佹湭鏀瑰鍏ユ帴鍙g鍚嶏紙浠嶄负 `POST /api/import/freightTurnover?period=yyyy-MM`锛夈��
+
+## 娴嬭瘯鐜
+- 鍚庣锛歵raffic-audit-server 8090锛堝惈鏈鏀瑰姩锛宍start-dev.ps1` 鍚姩鎴愬姛锛宐ackend.log 鍑虹幇 `Tomcat started on port(s): 8090` / `Started TrafficAuditApplication`锛夈��
+- 鍓嶇锛歵raffic-audit-web 8080锛堟湰娆′笉娑夊強鍓嶇锛夈��
+- 鏁版嵁搴擄細鏈満 MySQL `127.0.0.1:3308/traffic_audit`锛岀敤 mysql CLI 鐩存煡鏍稿銆�
+- 婧愭枃浠讹細`D:\05_鏁版嵁鍒嗙被\楂橀�焅瀹氱-婀栧寳鐪佸悇甯傚窞杩愯緭閲忔暟鎹紙2026骞�1-8鏈堬級.xlsx`锛孲heet 涓� `['2026骞�','2025骞�','2024骞�','2022骞�','2021骞�']`锛�2026/2025 鍚� 38 琛屆�15 鍒楋紝2024 48 琛屆�13 鍒楋紝2022 40 琛屆�15 鍒楋紝2021 41 琛屆�14 鍒楋級銆�
+- 璐熷悜/杈圭晫鐢ㄤ緥涓�寰嬪湪涓�娆℃�ф祴璇曟湡 `2099-01` ~ `2099-04` 鍐呰繘琛岋紝鍏ㄧ▼涓嶈Е纰扮湡瀹炴湀浠斤紱娴嬭瘯鍚庡凡閫愪釜娓呯┖銆�
+
+## 鐢ㄤ緥琛�
+| # | 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|---|
+| 1 | 姝e悜-澶氬勾搴﹀鍏� | 涓婁紶婧愭枃浠讹紝`period=2026-08` | 21/22/24/25/26 浜斾釜骞村害鍏ㄩ儴鍏ュ簱 | 杩斿洖銆屽叡 90 鏉¤褰曘�嶏紙5 涓姤琛ㄦ湡 脳 18 琛岋級锛涘簱鍐呮柊澧�/瑕嗙洊 `2021-12`銆乣2022-12`銆乣2024-12`銆乣2025-12`銆乣2026-08` | 閫氳繃 |
+| 2 | 姝e悜-鍏ュ簱鎶芥煡锛堥�愰」瀵归綈锛� | 婧愭枃浠堕�愬勾閫愬競宸為�愭湀涓庡簱鍐呴�愰」姣斿锛堜粖骞磋揣杩愰噺/浠婂勾鍛ㄨ浆閲�/涓婂勾璐ц繍閲�/涓婂勾鍛ㄨ浆閲忥級 | 瀹屽叏涓�鑷� | 姣斿 **1080 涓崟鍏冩牸锛屽樊寮� 0**锛堣剼鏈� `_verify_ft_multi.py`锛� | 閫氳繃 |
+| 3 | 姝e悜-鎶ヨ〃鏈熻涔� | 鏌ョ湅鍚勬湡 `report_period` | 姣忓勾搴︿竴涓姤琛ㄦ湡 | `2021-12 / 2022-12 / 2024-12 / 2025-12 / 2026-08`锛涙簮鏂囦欢鏃� 2023 骞达紝鏁呮湭鐢熸垚 `2023-12` | 閫氳繃 |
+| 4 | 姝e悜-涓婂勾鍚屾湡 | 鍚勬湡 `last_*` 鏄惁鍙栦笂涓�骞村害 Sheet | 鏈変笂涓�骞村害鐨勬湁鍊笺�佹棤鐨勪负绌� | `2022-12`銆乣2025-12`銆乣2026-08` 鍚� 18 琛� `last_*` 鍏ㄩ儴鏈夊�硷紱`2021-12`锛堟棤涓婂勾锛夈�乣2024-12`锛堢己 2023锛変负绌� | 閫氳繃 |
+| 5 | 姝e悜-瀵煎叆鎵规 | 鏌ヨ `import_batch` | 姣忎釜鎶ヨ〃鏈熶竴鏉¤褰� | 棣栨瀵煎叆鍐欏叆 id 324鈥�328锛�2021-12/2022-12/2024-12/2025-12/2026-08锛夛紝5 鏉″潎 total=success=18銆乫ail=0锛宖ile_name 涓烘簮鏂囦欢鍚� | 閫氳繃 |
+| 6 | 璐熷悜-鍗� Sheet 鏈堝害妯℃澘鍥為�� | 鍗� Sheet 鏈堝害妯℃澘鏂囦欢 + `period=2099-01` | 璇嗗埆涓嶅懡涓紝鍥為��鏈堝害璺緞 | 杩斿洖銆屽叡 18 鏉¤褰曘�嶏紝涓庢敼鍔ㄥ墠涓�鑷� | 閫氳繃 |
+| 7 | 璐熷悜-闈炲勾搴﹀懡鍚嶅 Sheet 鍥為�� | 涓や釜闈炲勾搴﹀懡鍚� Sheet锛圫heet1/Sheet2锛�+ `period=2099-02` | 璇嗗埆涓嶅懡涓紝鍥為��鏈堝害璺緞 | `code=200`锛�18 鏉� | 閫氳繃 |
+| 8 | 璐熷悜-娣峰悎鍛藉悕鍥為�� | 骞村害鍚� + 闈炲勾搴﹀悕娣峰悎锛�2021骞�/2022骞�/澶囨敞璇存槑锛�+ `period=2099-03` | 璇嗗埆涓嶅懡涓紝鍥為��鏈堝害璺緞 | `code=200`锛�18 鏉� | 閫氳繃 |
+| 9 | 璐熷悜-闈� Excel 鏂囦欢 | 鏂囨湰鏂囦欢褰� xlsx 涓婁紶 + `period=2099-04` | 鎶ラ敊涓斾笉钀藉簱 | `{"code":500,"message":"瀵煎叆澶辫触: Can't open workbook - unsupported file type: UNKNOWN"}` | 閫氳繃 |
+| 10 | 骞傜瓑-閲嶅瀵煎叆 | 鍚屼竴婧愭枃浠跺啀瀵间竴娆� | 涓嶄骇鐢熼噸澶嶈銆佸�间笉鍙� | 浠嶈繑鍥炪�屽叡 90 鏉¤褰曘�嶏紱瀵煎叆鍚庡啀鍐� id 332鈥�336锛�**鍏ㄨ〃 216 琛屽墠鍚� md5 涓�鑷�**锛坄a2a733b2c5321a9d5efa9be2e0c5dacb`锛� | 閫氳繃 |
+| 11 | 鍥炲綊-娴嬭瘯鏈熸竻鐞� | 閫愪釜 `POST /api/import/clear?period=2099-0x` | 娴嬭瘯鏈熸暟鎹笌鎵规鍏ㄩ儴鍒犻櫎 | 鍚勬湡 dataRows=18銆乥atchRows=1 鍒犻櫎鎴愬姛锛涘綋鍓嶅簱 **0 鏉� 2099 鏁版嵁銆�0 鏉� 2099 鎵规** | 閫氳繃 |
+| 12 | 鍥炲綊-鐪熷疄鏈堜唤鎬婚噺 | 缁熻 `freight_turnover_import` | 涓庡鍏ュ墠鍙e緞涓�鑷� | 12 涓姤琛ㄦ湡 脳 18 琛� = **216 琛�**锛�2021-12 / 2022-12 / 2024-12 / 2025-12 / 2026-01鈥�2026-08锛� | 閫氳繃 |
+
+## 鍙戠幇鐨勯棶棰樹笌澶勭疆
+1. **婧愭枃浠剁己 2023 骞� Sheet**锛氬鑷翠笉鐢熸垚 `2023-12` 鎶ヨ〃鏈燂紝`2024-12` 鏃犱笂骞村悓鏈燂紙`last_*` 涓虹┖锛夈�傚睘婧愭暟鎹己鍙o紝闈炰唬鐮佺己闄凤紱宸插瀹炲弽鏄犲湪搴撳唴銆�
+2. **鎶ヨ〃鏈熻涔変负銆屾暣骞淬��**锛歚2021-12` 绛夋湡瀹為檯鎵胯浇璇ュ勾 1鈥�12 鏈堢殑鍏ㄥ勾鏁版嵁锛堣�岄潪 12 鏈堝崟鏈堬級銆傝嫢鍚庣画鎶ヨ〃/瀹℃牳鎸夈�屽崟鏈堛�嶅彛寰勫彇杩欎簺鏈燂紝闇�瑕佺敤鎴风‘璁ゅ彛寰勶紱鏈鏈仛鏀瑰姩銆�
+3. **閲嶅瀵煎叆浼氳鐩栧悓鎶ヨ〃鏈�**锛氭柊閫昏緫寤剁画銆屽厛鍒犲悗鎻掋�嶇殑骞傜瓑绛栫暐锛岄噸瀵� `2026-08` 浼氳鐩栬鏈熷師鏈夊�笺�傛湰娆″凡閫愰」鏍稿婧愭枃浠朵笌搴撳唴涓�鑷达紙宸紓 0锛夛紝鏃犳暟鎹崯澶便��
+4. **澶囦唤涓嶅畬鏁达紙宸茶褰曪級**锛氭敼鍔ㄥ墠鐨勫浠� `_backup_freight_turnover_before_multiyear_20260916.sql` 鍙湁 59642 瀛楄妭涓斾粎瑙f瀽鍑� `2026-04` 涓�澶� INSERT锛屼笉鑳戒綔涓哄畬鏁村洖婊氫緷鎹��**涓嶈鎶婅鏂囦欢褰撲綔瀹屾暣澶囦唤浣跨敤銆�**
+5. **2026-01鈥�2026-07 鐨� `last_*` 涓虹┖**锛氱粡鏍稿澶囦唤锛坄2026-04`锛夌‘璁よ繖鍑犳湡鍦ㄦ湰娆℃敼鍔ㄥ墠 `last_*` 鍗充负绌猴紝涓旀湰娆″鍏ユ湭瑙︾杩欎簺鎶ヨ〃鏈燂紙鎵规璁板綍鍙鐩� 5 涓湡锛夛紝鏁呴潪鏈鏀瑰姩寮曞叆銆�
+
+## 缁撹
+- 閫氳繃銆傚骞村害宸ヤ綔绨匡紙21鈥�26 骞达級宸插叏閮ㄥ叆搴擄紝閫愬勾涓�涓姤琛ㄦ湡銆侀�愬勾涓婂勾鍚屾湡锛�1080 涓崟鍏冩牸閫愰」姣斿闆跺樊寮傦紱闈炲骞村害鏂囦欢鑷姩鍥為��鍘熸湁鏈堝害璺緞锛岃礋鍚戜笌杈圭晫鐢ㄤ緥鍧囩鍚堥鏈燂紱閲嶅瀵煎叆骞傜瓑锛屾祴璇曟湡宸叉竻鐞嗗共鍑�锛岀湡瀹炴湀浠芥暟鎹笌鏀瑰姩鍓嶅彛寰勪竴鑷达紙216 琛� / 12 鏈燂級銆�
+- 寰呯敤鎴风‘璁わ細`2021-12` 绛夈�屾暣骞淬�嶆姤琛ㄦ湡鐨勪笟鍔″彛寰勬槸鍚︾鍚堝悗缁姤琛�/瀹℃牳鍙栨暟瑕佹眰锛涘闇�銆岄�愭湀鎶ヨ〃鏈熴�嶏紝闇�瑕佸彟琛屾敼閫狅紙鏈鏈仛锛夈��
\ No newline at end of file
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-16.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-16.md"
index 368ea97..c214d53 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-16.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-16.md"
@@ -43,3 +43,38 @@
2. 鎻愪氦 `0d2a5fc`锛歚fix(cors): 鏀捐鍐呯綉缃戞鏉ユ簮锛屼慨澶嶅眬鍩熺綉 IP 璁块棶鏃剁櫥褰� 403`锛�
3. 鎺ㄤ釜浜轰粨搴� `origin`锛歚git push origin main:master` 鎴愬姛锛坄82100d6..0d2a5fc`锛夛紱
4. 鎺ㄥ叕鍙� `gitea`锛歚git fetch gitea`銆乣git push gitea main:main` 鍧囧洜 HTTP 鏃犲搷搴旇�屾寕璧凤紝鏈帹閫佹垚鍔燂紙璇﹁閬楃暀闂 5锛夛紱鏈涓哄揩杩涙帹閫侊紝涓嶆秹鍙婂己鎺ㄣ��
+
+---
+
+## 鍏�佷笅鍗堣拷鍔狅紙涓ゆ潯鐢ㄦ埛闇�姹傦紝2026-09-16锛�
+
+### 1. 鎶� 8 鏈堝鍏ョ殑銆岃揣杩愰噺鍛ㄨ浆閲忚〃銆嶄腑鐨� 21鈥�26 骞存暟鎹叏閮ㄨ鍏ユ暟鎹簱
+- **鑳屾櫙**锛氱敤鎴� 8 鏈堝鍏ョ殑 `瀹氱-婀栧寳鐪佸悇甯傚窞杩愯緭閲忔暟鎹紙2026骞�1-8鏈堬級.xlsx` 涓�涓伐浣滅翱鍐呭惈 5 涓勾搴� Sheet锛坄2026骞� / 2025骞� / 2024骞� / 2022骞� / 2021骞碻锛屾簮鏂囦欢鏃� 2023 骞达級锛岃�岃�侀�昏緫鍙绗� 1 涓� Sheet 骞舵寜銆屽乏鍗婁粖骞� 1鈥揘 鏈� / 鍙冲崐鍘诲勾 1鈥�12 鏈堛�嶇殑鏈堝害妯℃澘瑙f瀽锛屽叾浣欏勾搴︽暟鎹涓㈠純銆�
+- **鏀瑰姩锛堝悗绔� 1 鏂囦欢锛�+153/鈭�19锛�**锛歚DataImportService.java` 涓� `importFreightTurnover` 鏀逛负鍒嗘祦鍏ュ彛锛涘師閫昏緫鎶戒负 `importFreightTurnoverByMonth`锛堥�愯涓嶅彉锛夛紱鏂板 `detectYearSheets`锛堣瘑鍒骞村害宸ヤ綔绨匡級銆乣importFreightTurnoverByYear`锛堟寜骞村叆搴� + 涓婂勾鍚屾湡鍙栬嚜涓婁竴骞村害 Sheet锛夈�乣parseWholeYearSheet`锛堟暣骞� Sheet 鐨勮揣杩愰噺鍧�/鍛ㄨ浆閲忓潡瑙f瀽锛夈�乣persistFreightTurnover`锛堟寜鎶ヨ〃鏈熷厛鍒犲悗鎻掑苟璁版壒娆★級銆�
+- **缁撴灉**锛氭簮鏂囦欢 5 涓勾搴﹀叏閮ㄥ叆搴擄紝杩斿洖銆屽叡 90 鏉¤褰曘�嶏紝鎶ヨ〃鏈熶负 `2021-12 / 2022-12 / 2024-12 / 2025-12 / 2026-08`锛屾瘡鏈� 18 琛岋紱涓庢簮鏂囦欢閫愰」姣斿 **1080 涓崟鍏冩牸闆跺樊寮�**锛涢噸澶嶅鍏ュ箓绛夛紙鍏ㄨ〃 216 琛� md5 鍓嶅悗涓�鑷达級锛涢潪澶氬勾搴︽枃浠惰嚜鍔ㄥ洖閫�鍘熸湀搴﹁矾寰勩��
+- 璇﹁銆婂姛鑳芥祴璇曟姤鍛奯璐ц繍閲忓懆杞噺澶氬勾搴﹀鍏2026-09-16.md銆嬨��
+
+### 2. 鎶ヨ〃鐢熸垚-姹囨�诲ぇ琛ㄩ瑙堬細鍒犳帀銆屼腑鍙e緞銆嶄箣鍚庣殑琛ㄦ牸
+- **鏀瑰姩锛堝墠绔� 1 鏂囦欢锛屽崟琛岋級**锛歚ReportExport.vue` 绗� 133 琛岀敱 `v-else-if="it.summary && it.summary.length"` 鏀逛负 `v-if="it.key !== 'summaryWorkbook' && it.summary && it.summary.length"`銆傛牴鍥犳槸涓婇潰鐨勬眹鎬昏〃鐢� `v-if`銆佺浜屽紶琛ㄧ敤 `v-else-if`锛屽 `summaryWorkbook` 涔熺収鏍锋覆鏌撱��
+- **楠岃瘉锛堟帹缈讳笂涓�杞殑鏃犳晥鍙栬瘉锛�**锛氭棭鏈熸埅鍥炬病鎷嶅埌寮圭獥銆丄X 鏍戣鍒扮殑鏄紦瀛橈紝鍧囦负鏃犳晥璇佹嵁锛涙湰杞敼鐢� `tab.playwright.evaluate` 鐩存帴璇� DOM 鈥斺�� 寮圭獥鍐呮湁涓斾粎鏈� 1 寮犺〃锛坄preview-summary wb-table`锛�12 琛屾寚鏍囷級锛岃〃鏍间笅鏂瑰彧鏈変袱鏉″悓姣旂己澶辨彁绀轰笌銆屽叧闂�嶆寜閽紝**绗簩寮犺〃宸插交搴曟秷澶�**銆傛湁鏁堟埅鍥撅細`_qa_out/preview_summaryWorkbook_2026-08_verified_20260916.png`锛堟湰杞鏍搁噸鎷� `..._verified2_20260916.png`锛屽潎 54006 瀛楄妭锛夈��
+- 璇﹁銆婂姛鑳芥祴璇曟姤鍛奯姹囨�诲ぇ琛ㄩ瑙堝垹闄や腑鍙e緞鍚庢槑缁嗚〃_2026-09-16.md銆嬨��
+
+## 涓冦�佹祴璇曠粨鏋滄憳瑕侊紙涓嬪崍杩藉姞閮ㄥ垎锛�
+- 澶氬勾搴﹀鍏ワ細12 鏉$敤渚嬪叏閫氳繃鈥斺�旀鍚戯紙澶氬勾搴﹀叆搴撱��1080 鍗曞厓鏍奸�愰」鎶芥煡闆跺樊寮傘�佹姤琛ㄦ湡璇箟銆佷笂骞村悓鏈熴�佹壒娆¤褰曪級銆佽礋鍚戯紙鍗� Sheet 鏈堝害妯℃澘鍥為��銆侀潪骞村害鍛藉悕鍙� Sheet 鍥為��銆佹贩鍚堝懡鍚嶅洖閫�銆侀潪 Excel 鏂囦欢鎶ラ敊锛夈�佸箓绛夛紙閲嶅瀵煎叆鍏ㄨ〃 md5 涓�鑷达級銆佸洖褰掞紙娴嬭瘯鏈熸竻鐞嗗共鍑�銆佺湡瀹炴湀浠� 216 琛� / 12 鏈熷彛寰勪笉鍙橈級銆�
+- 棰勮寮圭獥锛�7 鏉$敤渚嬪叏閫氳繃鈥斺�擠OM 瀹炴祴寮圭獥鍐� `table` 鏁伴噺涓� 1銆佺被鍚嶄笌琛屾暟姝g‘銆�12 琛屾暟鍊间笌鍚屾瘮瀹屾暣銆佽〃鏍间笅鏂规棤绗簩寮犺〃銆乣git diff` 浠� 1 琛屾敼鍔ㄤ笉鍥炲綊銆�
+- 娴嬭瘯鏈熸竻鐞嗭細`2099-01` ~ `2099-04` 鍏ㄩ儴鐢� `POST /api/import/clear?period=鈥 娓呯┖锛堝悇 dataRows=18銆乥atchRows=1锛夛紝褰撳墠搴� **0 鏉� 2099 娈嬬暀**銆乣freight_turnover_import` 216 琛� / 12 涓姤琛ㄦ湡銆�
+
+## 鍏�侀仐鐣欓棶棰樹笌寰呭姙锛堟洿鏂帮級
+1. **`2021-12` 绛夋姤琛ㄦ湡鏄�屾暣骞淬�嶈涔�**锛氳繖浜涙湡鎵胯浇璇ュ勾 1鈥�12 鏈堝叏骞存暟鎹紝鑰岄潪 12 鏈堝崟鏈堬紱鍚庣画鎶ヨ〃/瀹℃牳鑻ユ寜鍗曟湀鍙e緞鍙栨暟闇�鐢ㄦ埛纭鍙e緞锛屽繀瑕佹椂鍐嶆敼閫犮��
+2. **婧愭枃浠剁己 2023 骞� Sheet**锛氬鑷翠笉鐢熸垚 `2023-12`锛宍2024-12` 鏃犱笂骞村悓鏈熴�傚睘婧愭暟鎹己鍙c��
+3. **`2026-01` ~ `2026-07` 鐨� `last_*` 涓虹┖**锛氱粡姣斿鏀瑰姩鍓嶅浠斤紙`2026-04` 鐨� INSERT锛夌‘璁よ繖鍑犳湡鏀瑰姩鍓嶅嵆涓虹┖锛屼笖鏈瀵煎叆鏈Е纰拌繖浜涙姤琛ㄦ湡锛堟壒娆″彧瑕嗙洊 5 涓湡锛夛紝**闈炴湰娆℃敼鍔ㄥ紩鍏�**锛涘闇�琛ヤ笂骞村悓鏈熼渶閲嶆柊瀵煎叆瀵瑰簲骞村害鏁版嵁銆�
+4. **鏀瑰姩鍓嶇殑澶囦唤涓嶅畬鏁�**锛歚_backup_freight_turnover_before_multiyear_20260916.sql` 浠� 59642 瀛楄妭銆佸彧瑙f瀽鍑� `2026-04` 涓�澶� INSERT锛�**涓嶈兘浣滀负瀹屾暣鍥炴粴渚濇嵁**銆�
+5. 寮圭獥鍐呫�屽叡 0 琛屻�嶄笌銆屾湀搴﹁鐩栵細1~8鏈堝潎鏈夋暟鎹�嶅彛寰勪笉鍚岋紙鍓嶈�呮槸鏄庣粏琛屾暟锛夛紝鏄撹瑙o紝鍙�冭檻鏀规枃妗堬紙鏈仛锛岀瓑鐢ㄦ埛纭锛夈��
+6. 涓婂崍閬楃暀缁х画鎸傝处锛氬煙鍚�/鍏綉璁块棶鐨勭櫧鍚嶅崟琛ュ厖銆乣Login.vue` 閿欒鎻愮ず缁嗗垎銆丄I 澶栧彂鑴辨晱琛ラ綈銆�4 寮犳槑缁嗘姤琛ㄦā鏉挎墿鍒椼�佸鏍告瘮瀵瑰熀鍑嗙己澶变繚鎶ゃ�乣sys_role` 鏉冮檺钀藉湴銆�3D 楠岃瘉鐮佺敓浜у紑鍚�乣backend.log` 鍘嗗彶鏄庢枃娓呯悊銆佷粨搴撴牴鐩綍涓存椂鏂囦欢娓呯悊銆�
+7. **鍏徃 gitea 浠嶅緟琛ユ帹**锛堢綉缁滈棶棰橈紝闈炲嚟鎹級锛岃ˉ鎺ㄥ墠鍏� `git fetch gitea` 涓庤繙绔� `main` 瀵规瘮鍚堝苟锛岀寮烘帹銆�
+
+## 涔濄�佹槑澶╄鍒�
+1. 涓庣敤鎴风‘璁� `2021-12` / `2022-12` 绛夈�屾暣骞存姤琛ㄦ湡銆嶇殑涓氬姟鍙e緞锛屼互鍙婃槸鍚﹂渶瑕佹妸骞村害鏁版嵁鎷嗘垚閫愭湀鎶ヨ〃鏈熴��
+2. 纭鏄惁闇�瑕佽ˉ 2023 骞存暟鎹�佷互鍙� `2026-01`~`2026-07` 鐨勪笂骞村悓鏈熸槸鍚﹂渶瑕佽ˉ榻愩��
+3. 瑙嗙敤鎴锋剰瑙佸鐞嗛瑙堝脊绐椼�屽叡 0 琛屻�嶆枃妗堛��
+4. 缃戠粶鎭㈠鍚庤ˉ鎺ㄥ叕鍙� gitea銆�
\ No newline at end of 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 4c372e7..9b5d0d1 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
@@ -85,6 +85,7 @@
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.TreeMap;
import java.util.stream.Collectors;
import java.text.SimpleDateFormat;
@@ -1054,11 +1055,22 @@
// ========== 璐ц繍閲忓懆杞噺锛堟ā鏉縚璐ц繍閲忓懆杞噺.xlsx锛屾瘡鏈堝鍏ワ級 ==========
public int importFreightTurnover(MultipartFile file, String period) throws Exception {
+ try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
+ // 澶氬勾搴﹀伐浣滅翱锛堜竴骞翠竴涓� Sheet锛氭暣骞� 1-12 鏈堬紝濡� 21-26 骞村畾绋挎枃浠讹級鈫� 鍏ㄩ儴骞翠唤鍏ュ簱
+ List<String> yearSheets = detectYearSheets(wb);
+ if (!yearSheets.isEmpty()) {
+ return importFreightTurnoverByYear(wb, file, yearSheets);
+ }
+ return importFreightTurnoverByMonth(wb, file, period);
+ }
+ }
+
+ /** 姣忔湀瀵煎叆锛堟ā鏉縚璐ц繍閲忓懆杞噺.xlsx锛夛細宸﹀崐浠婂勾 1-N 鏈堬紝鍙冲崐鍘诲勾 1-12 鏈� */
+ private int importFreightTurnoverByMonth(Workbook wb, MultipartFile file, String period) throws Exception {
List<FreightTurnoverImport> list = new ArrayList<>();
- int failRows = 0;
// 鎶ヨ〃鏈� N 鏈堬細宸﹀崐涓轰粖骞� 1-N 鏈堬紙鍒楁暟涓嶅浐瀹氾級锛屽彸鍗婂浐瀹氫负鍘诲勾 1-12 鏈堬紙绠楀悓姣旂敤锛�
int monthCount = Integer.parseInt(period.split("-")[1]);
- try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
+ {
Sheet sheet = wb.getSheetAt(0);
Row header = sheet.getRow(0);
String h0 = header == null ? null : getString(header, 0);
@@ -1123,27 +1135,149 @@
}
list.add(entity);
}
- } catch (Exception e) {
- throw e;
}
+ Map<String, List<FreightTurnoverImport>> byPeriod = new LinkedHashMap<>();
+ byPeriod.put(period, list);
+ return persistFreightTurnover(byPeriod, file.getOriginalFilename());
+ }
- // 骞傜瓑锛氬悓鎶ヨ〃鏈熷厛鍒犻櫎
- freightTurnoverMapper.delete(new LambdaQueryWrapper<FreightTurnoverImport>()
- .eq(FreightTurnoverImport::getReportPeriod, period));
+ /** 璇嗗埆銆屽骞村害宸ヤ綔绨裤�嶏細姣忎釜 Sheet 涓�骞达紙Sheet 鍚� 2021骞�/2022骞粹�︼級锛屼笖琛ㄥご涓恒�岃揣杩愰噺/1鏈堚��12鏈堛�嶏紱鍚﹀垯杩斿洖绌哄垪琛� */
+ private List<String> detectYearSheets(Workbook wb) {
+ List<String> names = new ArrayList<>();
+ if (wb.getNumberOfSheets() < 2) return names;
+ for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+ Sheet sheet = wb.getSheetAt(i);
+ String nm = sheet.getSheetName() == null ? "" : sheet.getSheetName().trim();
+ if (!nm.matches("\\d{4}\\s*骞�?")) { names.clear(); return names; }
+ Row header = sheet.getRow(0);
+ String h0 = header == null ? null : getString(header, 0);
+ String h1 = header == null ? null : getString(header, 1);
+ if (h0 == null || !h0.contains("璐ц繍閲�") || h1 == null || !h1.contains("鏈�")) {
+ names.clear();
+ return names;
+ }
+ names.add(nm);
+ }
+ return names;
+ }
- int success = 0;
- for (FreightTurnoverImport entity : list) {
- try {
- freightTurnoverMapper.insert(entity);
- success++;
- } catch (Exception e) {
- log.error("freight turnover insert error: {}", entity.getRegionName(), e);
- failRows++;
+ /** 澶氬勾搴﹀伐浣滅翱瀵煎叆锛氶�愬勾涓�鏉℃姤琛ㄦ湡锛堟姤琛ㄦ湡=璇ュ勾鏈�鍚庢湁鏁版嵁鐨勬湀浠斤級锛屼笂骞村悓鏈熷彇涓婁竴骞村害 Sheet */
+ private int importFreightTurnoverByYear(Workbook wb, MultipartFile file, List<String> yearSheets) {
+ Map<Integer, String> sheetByYear = new TreeMap<>();
+ for (String sheetName : yearSheets) {
+ sheetByYear.put(Integer.parseInt(sheetName.replace("骞�", "").trim()), sheetName);
+ }
+ Map<Integer, Map<String, FreightTurnoverImport>> byYear = new TreeMap<>();
+ Map<Integer, Integer> maxMonthByYear = new HashMap<>();
+ for (Map.Entry<Integer, String> entry : sheetByYear.entrySet()) {
+ Map<String, FreightTurnoverImport> parsed = new LinkedHashMap<>();
+ int maxMonth = parseWholeYearSheet(wb.getSheet(entry.getValue()), parsed);
+ if (parsed.isEmpty()) continue;
+ byYear.put(entry.getKey(), parsed);
+ maxMonthByYear.put(entry.getKey(), Math.max(1, Math.min(12, maxMonth)));
+ }
+ Map<String, List<FreightTurnoverImport>> byPeriod = new LinkedHashMap<>();
+ for (Map.Entry<Integer, Map<String, FreightTurnoverImport>> entry : byYear.entrySet()) {
+ int year = entry.getKey();
+ String yearPeriod = year + "-" + String.format("%02d", maxMonthByYear.get(year));
+ Map<String, FreightTurnoverImport> lastYear = byYear.get(year - 1);
+ List<FreightTurnoverImport> rows = new ArrayList<>();
+ for (Map.Entry<String, FreightTurnoverImport> regionEntry : entry.getValue().entrySet()) {
+ FreightTurnoverImport entity = regionEntry.getValue();
+ entity.setReportPeriod(yearPeriod);
+ // 涓婂勾鍚屾湡 = 涓婁竴骞村害 Sheet锛堝骞村害鏂囦欢娌℃湁鍙冲崐銆屽幓骞�1-12鏈堛�嶅尯鍧楋級
+ FreightTurnoverImport last = lastYear == null ? null : lastYear.get(regionEntry.getKey());
+ if (last != null) {
+ for (int m = 1; m <= 12; m++) {
+ setLastFreight(entity, m, getFreight(last, m));
+ setLastTurnover(entity, m, getTurnover(last, m));
+ }
+ }
+ rows.add(entity);
+ }
+ byPeriod.put(yearPeriod, rows);
+ }
+ return persistFreightTurnover(byPeriod, file.getOriginalFilename());
+ }
+
+ /** 瑙f瀽鏁村勾 Sheet锛�1-12 鏈堝垪锛夛細璐ц繍閲忓潡 + 鍛ㄨ浆閲忓潡锛涜繑鍥炴渶鍚庝竴涓湁璐ц繍閲忔暟鎹殑鏈堜唤 */
+ private int parseWholeYearSheet(Sheet sheet, Map<String, FreightTurnoverImport> out) {
+ Map<String, FreightTurnoverImport> freightMap = new LinkedHashMap<>();
+ Map<String, FreightTurnoverImport> turnoverMap = new LinkedHashMap<>();
+ Set<String> seen = new HashSet<>();
+ int maxMonth = 0;
+ for (int r = 1; r <= sheet.getLastRowNum(); r++) {
+ Row row = sheet.getRow(r);
+ if (row == null) continue;
+ String name = getString(row, 0);
+ if (name == null || name.trim().isEmpty()) continue;
+ String region = RegionUtil.normalizeCityName(name);
+ boolean isCity = RegionUtil.cityList().contains(region);
+ if (!isCity && !"婀栧寳鐪�".equals(region)) continue; // 琛ㄥご/璇存槑琛�
+ if (seen.contains(region)) {
+ // 璇ュ悕绉板凡鍑虹幇杩� -> 鍛ㄨ浆閲忓尯鍧�
+ FreightTurnoverImport entity = turnoverMap.get(region);
+ if (entity == null) {
+ entity = new FreightTurnoverImport();
+ entity.setRegionName(name.trim());
+ turnoverMap.put(region, entity);
+ }
+ for (int m = 1; m <= 12; m++) {
+ setTurnover(entity, m, getDecimalOrNull(row, m));
+ }
+ } else {
+ seen.add(region);
+ FreightTurnoverImport entity = freightMap.get(region);
+ if (entity == null) {
+ entity = new FreightTurnoverImport();
+ entity.setRegionName(name.trim());
+ freightMap.put(region, entity);
+ }
+ for (int m = 1; m <= 12; m++) {
+ Double v = getDecimalOrNull(row, m);
+ setFreight(entity, m, v);
+ if (v != null && m > maxMonth) maxMonth = m;
+ }
}
}
- recordBatch(file.getOriginalFilename(), "FREIGHT_TURNOVER", period, list.size(), success, failRows, null);
- log.info("Freight turnover imported: {} rows for {}", success, period);
- return success;
+ // 鍚堝苟锛氫竴涓競宸炰竴琛岋紙璐ц繍閲� + 鍛ㄨ浆閲忥級
+ for (Map.Entry<String, FreightTurnoverImport> entry : freightMap.entrySet()) {
+ FreightTurnoverImport entity = entry.getValue();
+ FreightTurnoverImport turnover = turnoverMap.get(entry.getKey());
+ if (turnover != null) {
+ for (int m = 1; m <= 12; m++) {
+ setTurnover(entity, m, getTurnover(turnover, m));
+ }
+ }
+ out.put(entry.getKey(), entity);
+ }
+ return maxMonth;
+ }
+
+ /** 钀藉簱锛氭寜鎶ヨ〃鏈熷厛鍒犲悗鎻掞紙骞傜瓑锛夛紝姣忎釜鎶ヨ〃鏈熶竴鏉″鍏ユ壒娆¤褰曪紝杩斿洖鎴愬姛鏉℃暟 */
+ private int persistFreightTurnover(Map<String, List<FreightTurnoverImport>> byPeriod, String fileName) {
+ int total = 0;
+ for (Map.Entry<String, List<FreightTurnoverImport>> entry : byPeriod.entrySet()) {
+ String p = entry.getKey();
+ List<FreightTurnoverImport> rows = entry.getValue();
+ freightTurnoverMapper.delete(new LambdaQueryWrapper<FreightTurnoverImport>()
+ .eq(FreightTurnoverImport::getReportPeriod, p));
+ int failRows = 0;
+ int success = 0;
+ for (FreightTurnoverImport entity : rows) {
+ try {
+ freightTurnoverMapper.insert(entity);
+ success++;
+ } catch (Exception e) {
+ log.error("freight turnover insert error: {}", entity.getRegionName(), e);
+ failRows++;
+ }
+ }
+ recordBatch(fileName, "FREIGHT_TURNOVER", p, rows.size(), success, failRows, null);
+ log.info("Freight turnover imported: {} rows for {}", success, p);
+ total += success;
+ }
+ return total;
}
/** 瑙f瀽璐ц繍閲忓尯鍧楄锛氬乏鍗� B 璧� N 鍒椾负浠婂勾 1-N 鏈堬紝鍙冲崐涓哄幓骞� 1-12 鏈� */
diff --git a/traffic-audit-web/src/views/ReportExport.vue b/traffic-audit-web/src/views/ReportExport.vue
index 54f5513..f313a49 100644
--- a/traffic-audit-web/src/views/ReportExport.vue
+++ b/traffic-audit-web/src/views/ReportExport.vue
@@ -130,7 +130,7 @@
<div v-if="it.key === 'summaryWorkbook' && wbMissing(it.summary)" class="wb-note">
閮ㄥ垎鎸囨爣缂哄皯涓婂勾鍚屾湡锛坽{ wbPrevYear() }}锛夊熀鏈熸暟鎹紝鍚屾瘮鏆傛湭璁$畻
</div>
- <table class="preview-summary" v-else-if="it.summary && it.summary.length">
+ <table class="preview-summary" v-if="it.key !== 'summaryWorkbook' && it.summary && it.summary.length">
<tbody>
<tr v-for="(s, i) in it.summary" :key="i">
<td>{{ s.label }}</td>
--
Gitblit v1.9.1