From 5e2982df8aca712c3156711c1917fcedb6cfb087 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期三, 09 九月 2026 10:30:23 +0800
Subject: [PATCH] fix: 09-09上午整改——RULE_001分项运政对比(6段)+阈值说明联动通用化+汇总大表列宽回归母版+导入结果弹窗 + 测试报告与工作日结

---
 traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java |   68 ++++++----
 traffic-audit-web/src/views/DataImport.vue                                                          |   15 ++
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java   |   54 ---------
 docs/工作日结/工作日结_2026-09-09.md                                                                        |   24 ++++
 docs/功能测试报告/功能测试报告_RULE001分项运政与阈值联动及列宽回归_2026-09-09.md                                              |   58 +++++++++
 traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java     |   86 ++++++++++++++
 6 files changed, 224 insertions(+), 81 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_RULE001\345\210\206\351\241\271\350\277\220\346\224\277\344\270\216\351\230\210\345\200\274\350\201\224\345\212\250\345\217\212\345\210\227\345\256\275\345\233\236\345\275\222_2026-09-09.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_RULE001\345\210\206\351\241\271\350\277\220\346\224\277\344\270\216\351\230\210\345\200\274\350\201\224\345\212\250\345\217\212\345\210\227\345\256\275\345\233\236\345\275\222_2026-09-09.md"
new file mode 100644
index 0000000..7f9a89e
--- /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_RULE001\345\210\206\351\241\271\350\277\220\346\224\277\344\270\216\351\230\210\345\200\274\350\201\224\345\212\250\345\217\212\345\210\227\345\256\275\345\233\236\345\275\222_2026-09-09.md"
@@ -0,0 +1,58 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡锛歊ULE_001 鍒嗛」杩愭斂瀵规瘮 / 闃堝�艰鏄庤仈鍔ㄩ�氱敤鍖� / 姹囨�诲ぇ琛ㄥ垪瀹藉洖褰� / 瀵煎叆缁撴灉寮圭獥锛�2026-09-09锛�
+
+## 1. 鍔熻兘璇存槑
+鎸夌敤鎴� 2026-09-09 涓婂崍鍙嶉鏁存敼鍥涢」锛�
+1. **璐ц繍鏈堟姤 RULE_001 鏀瑰垎椤硅繍鏀垮姣�**锛氱壍寮曡溅鏁�/鎸傝溅鏁�/鏁磋溅鏁帮紙杞︾被宸墹5 鍙帮級銆佹寕杞﹀惃浣�/鏁磋溅鍚ㄤ綅锛堝惃浣嶅樊鈮�50 鍚級銆佷笂鎶ユ�诲惃浣� vs 杩愭斂鎬诲惃浣嶏紙宸墹50 鍚級锛屼换涓�鍒嗛」瓒呴檺鍗虫彁閱掞紱鍘� RULE_002锛堟�婚噺鍚ㄤ綅瀵规瘮锛夊苟鍏� RULE_001 鍚庡仠鐢ㄩ槻閲嶅銆�
+2. **鎵�鏈夊鏍歌鍒欓槇鍊煎彉鍔� 鈫� 瑙勫垯鍚嶇О/璇存槑鑱斿姩**锛氱敱鍘熸潵鍙敼鈥�% 鍓嶆暟瀛椻�濇帹骞垮埌浠绘剰鏁板瓧鐗囨锛堝彴/鍚�/鍏噷绛夊崟浣嶅墠鐨勯槇鍊兼暟瀛楋級锛屽苟鏀寔鈥�5,5,50,5,50,50鈥濊繖绫诲娈甸槇鍊奸�愭鑱斿姩銆�
+3. **姹囨�诲ぇ琛紙閬撹矾杩愯緭閲忔眹鎬昏〃鏁存湰锛夊垪瀹藉洖褰掓瘝鐗�**锛氬垹闄ゅ鍑烘椂 `autoFitContentColumns`锛堝唴瀹硅嚜閫傚簲鍔犲锛夛紝鍒楀涓ユ牸閲囩敤姣嶇増鍘熷鍒楀銆�
+4. **鏁版嵁瀵煎叆鐣岄潰瀵煎叆缁撴灉寮圭獥**锛氬崟鏂囦欢瀵煎叆鎴愬姛寮光�滄垚鍔熸秷鎭紙鍚В鏋愭潯鏁帮級鈥濄�佸け璐ュ脊閿欒璇︽儏锛屾浛浠e師鏉ュ彧鍦ㄥ簳閮ㄥ垪琛ㄦ樉绀恒��
+
+## 2. 娴嬭瘯鐜
+- 鍚庣 Spring Boot 8090锛坢vn 澧為噺缂栬瘧閫氳繃鍚庨噸鍚紝`Started TrafficAuditApplication` 纭锛�
+- 鍓嶇 Vue dev server 8080锛坄Compiled successfully` 浜� 10:24:53锛�
+- MySQL `traffic_audit`锛坙ocalhost:3308锛夛紝瑙勫垯琛� `audit_rule`銆佸鏍哥粨鏋� `audit_result`
+- 鏁版嵁鏈燂細2026-08锛堣揣杩愪紒涓氭湀鎶� 636 鎴枫�佽繍鏀胯溅杈� 635 鎴峰潎鍦ㄥ簱锛�
+
+## 3. 鐢ㄤ緥琛紙杈撳叆/棰勬湡/瀹為檯/缁撹锛�
+
+### 3.1 RULE_001 鍒嗛」杩愭斂瀵规瘮
+| 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|
+| 姝e悜-瑙勫垯閰嶇疆 | SQL 鏇存柊 RULE_001 涓� DIFF_FIELDS + 鍏 check_field/threshold | API /rules/list 鏄剧ず鏂板悕绉�/璇存槑/闃堝�� | rule_name=鈥滆溅杈嗘暟涓庡惃浣嶈繍鏀垮垎椤瑰姣斺�濓紝check_field=vehicleTractor,vehicleTrailer,tonsTrailer,vehicleWhole,tonsWhole,tonsTotal锛宼hreshold=5,5,50,5,50,50 | 閫氳繃 |
+| 姝e悜-瀹℃牳鎵ц | POST /api/audit/execute?period=2026-08&reportType=H2032 | 姝e父杩斿洖鍛戒腑鏄庣粏 | 4s 杩斿洖 487 鏉$粨鏋滐紝鍏朵腑 RULE_001 鍛戒腑 115 鏉� | 閫氳繃 |
+| 姝e悜-鍛戒腑娑堟伅 | 鎶芥煡 rule_id=1 鐨� actual_value | 閫愰」鍒楀嚭 涓婃姤/杩愭斂/宸�/闄愶紝鈮�200 瀛楃 | 渚嬶細鈥滅壍寮曡溅鏁帮細涓婃姤=120 鍙帮紝杩愭斂=114 鍙帮紝宸�6 鍙帮紙闄�5 鍙帮級锛涙寕杞﹀惃浣嶏細涓婃姤=26032.8 鍚紝杩愭斂=25874.78 鍚紝宸�158.02 鍚紙闄�50 鍚級鈥︹�� | 閫氳繃 |
+| 璐熷悜-娑堟伅瓒呴暱 | 澶氬垎椤瑰悓鏃惰秴闄� | 涓嶅洜 actual_value 200 瀛楃鎶ラ敊鎴柇 | 鍗曟潯娑堟伅鈮�180 瀛楃鎷兼帴锛岃秴鍑鸿拷鍔犫�滅瓑N椤硅秴闄愨�濓紱棣栬疆鏃犱繚鎶ゆ椂鏇炬姤 Data truncation锛屽凡淇鍚庢墽琛屾垚鍔� | 閫氳繃 |
+| 鍘婚噸 | RULE_002 鍋滅敤 | 2026-08 涓嶅啀浜х敓 rule_id=2 缁撴灉 | 缁撴灉鍒嗗竷浠� rule_id 1/3/4/5/6锛屾棤 2 | 閫氳繃 |
+
+### 3.2 闃堝�煎彉鍔ㄨ鏄庤仈鍔紙鍘熷�尖啋鏂板�尖啋澶嶅師鍙屽悜锛�
+| 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|
+| 鍙版暟鍨� DIFF | RULE_P001 threshold 3鈫�5 | 璇存槑鈥滃樊璺�>3鍙扳�濃啋鈥滃樊璺�>5鍙扳�� | 鏇存柊鍚庤鏄庝负鈥滃樊璺�>5鍙伴渶鏍稿疄鈥� | 閫氳繃 |
+| 鐧惧垎鏁板瀷 | RULE_P005 threshold 20鈫�30 | 鍚嶇О/璇存槑 卤20%鈫捖�30% | 鍚嶇О鈥滃钩鍧囪繍璺濈幆姣旇秴卤30%鏍稿疄鈥濓紝璇存槑鍚屾 | 閫氳繃 |
+| 姣斿�煎瀷 | RULE_003 threshold 1.4鈫�1.5 | 鈥滈珮鍑�40%鈥濃啋鈥滈珮鍑�50%鈥� | 璇存槑鏇存柊涓衡�滈珮鍑�50%闇�鏍稿疄鈥� | 閫氳繃 |
+| 骞傜瓑/澶嶅師 | 涓夋潯鍏ㄩ儴杩樺師 3/20/1.4 | 鍚嶇О涓庤鏄庡洖鍒板師鍊� | 澶嶆牳鍏ㄩ儴澶嶅師锛屾湭姹℃煋璇存槑涓棤鍏虫暟瀛� | 閫氳繃 |
+
+### 3.3 姹囨�诲ぇ琛ㄥ垪瀹藉洖褰掓瘝鐗�
+| 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|
+| 姝e悜-瀵煎嚭 | GET /api/report/export/summaryWorkbook?period=2026-08 | 姝e父杈撳嚭鏁存湰 | 3s 瀵煎嚭 938,586 瀛楄妭锛�19 涓〉绛句笌姣嶇増涓�鑷� | 閫氳繃 |
+| 鍒楀瀵规瘮 | openpyxl 閫愰〉閫愬垪姣斿鍒楀 | 涓庢瘝鐗堝垪瀹戒竴鑷� | 19 椤电鍏� 12 澶勫樊寮傦紝鍏ㄩ儴涓衡�滄瘝鐗堟湭鏄惧紡瀹氫箟銆丳OI 琛ラ粯璁ゅ鈥濓紙鍊�=姣嶇増榛樿瀹� 8.8867/14.0 绛夛級锛岃瑙夊垪瀹戒竴鑷� | 閫氳繃锛堟棤鍐呭鑷�傚簲鍔犲锛� |
+| 鏍峰紡淇濈暀 | 瀵煎嚭鏂囦欢鎶芥煡 | 鏁板�间袱浣嶅皬鏁般�佸彇娑堢瓫閫夈�佽繕鍘熼殣钘忚銆佹墦寮�閲嶇畻淇濈暀 | 淇濈暀锛堟棦鏈夐�昏緫鏈姩锛� | 閫氳繃 |
+
+### 3.4 瀵煎叆缁撴灉寮圭獥
+| 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|
+| 姝e悜-浠g爜 | importNow 鎴愬姛鍥炶皟澧炲姞 showImportResult(row) | 鎴愬姛寮� `$message.success`锛堝惈鍚庣杩斿洖鈥溾�﹀鍏ユ垚鍔燂紝鍏� N 鏉♀�濓級锛屽け璐ュ脊 `$alert` 閿欒鏄庣粏 | 浠g爜宸茶惤鍦帮紝鍓嶇缂栬瘧鎴愬姛锛涘悗绔崟鏂囦欢鎺ュ彛鍧囪繑鍥� Result<String> 涓枃娑堟伅 | 閫氳繃锛堢晫闈㈡墜宸ョ偣楠屽緟鐢ㄦ埛纭锛� |
+| 鍏煎-鎶曡祫/鍩庡競鐩綍 | 鍘熸湁 checkInvest/鐩綍鎵归噺娴佺▼ | 涓嶉噸澶嶅脊绐� | 鐩綍鎵归噺浠嶈蛋鍘� $alert 姹囨�伙紝涓嶅彈褰卞搷 | 閫氳繃 |
+
+## 4. 鍏ュ簱鎶芥煡锛堜笌婧愭枃浠堕�愰」瀵归綈锛�
+- `audit_rule`锛歊ULE_001锛坕d=1锛夊凡鍏ュ簱鏂伴厤缃紱RULE_002锛坕d=2锛塦is_enabled=0`銆乼hreshold 鏍℃涓� 50 涓庤鏄庝竴鑷淬��
+- `audit_result`锛�2026-08 RULE_001 鍛戒腑 115 鏉★紝鎶芥煡 8 瀹朵紒涓氱殑 actual_value 鍧囦负鈥滃垎椤癸細涓婃姤/杩愭斂/宸�/闄愨�濈粨鏋勶紝鏁版嵁涓� h2032_enterprise_monthly / transport_auth_vehicle 鍚屽彛寰勩��
+
+## 5. 鍙戠幇鐨勯棶棰樹笌澶勭疆
+1. 鏂板垎椤规秷鎭秴杩� `audit_result.actual_value varchar(200)` 鈫� 宸插姞 180 瀛楃鎴柇淇濇姢锛堜繚鐣欓娈�+鈥滅瓑N椤硅秴闄愨�濇眹鎬伙級銆�
+2. RULE_002 闃堝�� 80 涓庡叾璇存槑鈥滃樊璺�>50鈥濋暱鏈熶笉涓�鑷� 鈫� 骞跺叆 RULE_001 骞跺仠鐢紝闃堝�肩粺涓� 50銆�
+3. 杩愭斂渚у瓧娈典粎鏈� 鐗靛紩/鎸傝溅/鍏朵粬 涓夊垎绫伙紝H2032 鐨勨�滄暣杞︹�濇寜杩愭斂鈥滃叾浠栬溅鈥濆彛寰勬瘮瀵癸紝宸插湪鍛戒腑娑堟伅涓庤鏄庝腑浣撶幇锛涘闇�涓庘�滄暣杞︼紙鍚泦瑁呯锛夆�濆崟鐙瘮瀵归渶杩愭斂鏁版嵁渚у鍔犲垎绫汇��
+
+## 6. 缁撹
+鍥涢」鏁存敼鍔熻兘楠岃瘉閫氳繃锛涙眹鎬诲ぇ琛ㄤ粛涓衡�滃鍒舵瘝鐗�+澶囦唤杩樺師鈥濇満鍒讹紝8 鏈堟暟鎹~鍏咃紙M2/M3锛夋湭鍦ㄦ湰杞惤鍦帮紝瑙佸伐浣滄棩缁撻仐鐣欎簨椤广��
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-09.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-09.md"
new file mode 100644
index 0000000..63d249e
--- /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-09.md"
@@ -0,0 +1,24 @@
+# 宸ヤ綔鏃ョ粨 2026-09-09
+
+## 浠婃棩瀹屾垚
+- 姹囨�诲ぇ琛細鍒犻櫎瀵煎嚭鏁存湰鏃剁殑鈥滃唴瀹硅嚜閫傚簲鍒楀鈥濓紝鍒楀鍥炲綊姣嶇増鍘熷瀹藉害锛堢粡 openpyxl 閫愰〉閫愬垪姣斿浠呭墿 POI 琛ラ粯璁ゅ锛屾棤瀹炶川宸紓锛夛紱鍙栨秷绛涢��/杩樺師闅愯棌琛�/涓や綅灏忔暟/鎵撳紑閲嶇畻绛夋棦鏈夊鐞嗕繚鐣欍��
+- 瀹℃牳瑙勫垯锛�
+  - RULE_001 鏀逛负鈥滆溅杈嗘暟涓庡惃浣嶈繍鏀垮垎椤瑰姣斺�濓紙DIFF_FIELDS锛夛細鐗靛紩杞�/鎸傝溅/鏁磋溅杞︾被宸墹5 鍙帮紝鎸傝溅鍚ㄤ綅/鏁磋溅鍚ㄤ綅宸墹50 鍚紝鎬诲惃浣嶅樊鈮�50 鍚紝鍛戒腑娑堟伅甯� 涓婃姤/杩愭斂/宸�/闄� 鏄庣粏骞堕槻 200 瀛楃婧㈠嚭锛�
+  - RULE_002 骞跺叆 RULE_001 鎬诲惃浣嶆鍚庡仠鐢紙闃堝�间笌璇存槑缁熶竴涓� 50锛夛紱
+  - 瑙勫垯闃堝�尖啋鍚嶇О/璇存槑鑱斿姩鎺ㄥ箍鍒板叏閮ㄨ鍒欙細鏀寔鍙�/鍚�/鍏噷绛夐潪鐧惧垎姣旀暟瀛椾笌澶氭闃堝�硷紙濡� 5,5,50,5,50,50锛夛紝DIFF/姣斿��/鐧惧垎鏁颁笁绫诲疄娴嬪弻鍚戣仈鍔ㄥ苟澶嶅師鏃犳薄鏌撱��
+- 鏁版嵁瀵煎叆锛氬崟鏂囦欢瀵煎叆鎴愬姛/澶辫触澧炲姞寮圭獥鎻愮ず锛堟垚鍔熷惈瑙f瀽鏉℃暟锛屽け璐ュ垪閿欒鏄庣粏锛夈��
+- 娴嬭瘯鎶ュ憡锛歞ocs/鍔熻兘娴嬭瘯鎶ュ憡/鍔熻兘娴嬭瘯鎶ュ憡_RULE001鍒嗛」杩愭斂涓庨槇鍊艰仈鍔ㄥ強鍒楀鍥炲綊_2026-09-09.md銆�
+
+## 娴嬭瘯缁撴灉鎽樿
+- 2026-08 璐ц繍瀹℃牳鏁存湡鎵ц 4s 杩斿洖 487 鏉★紝RULE_001 鍛戒腑 115 鏉★紝娑堟伅缁撴瀯姝g‘锛汻ULE_002 涓嶅啀鍑虹粨鏋溿��
+- 姹囨�绘暣鏈鍑� 2026-08 鎴愬姛锛�938KB/19 椤电锛夛紝鍒楀涓庢瘝鐗堜竴鑷淬��
+- 鍓嶇 dev 缂栬瘧鎴愬姛锛涘鍏ュ脊绐楀缓璁敤鎴峰湪鐣岄潰鐐归獙涓�娆°��
+
+## 閬楃暀闂涓庡緟鍔�
+- **姹囨�诲ぇ琛� 8 鏈堝~鍊硷紙鏁版嵁椹卞姩 M2/M3锛夊皻鏈惤鍦�**锛氬綋鍓嶆暣鏈粛鏄�滃鍒舵瘝鐗�+澶囦唤杩樺師鈥濓紝瀵煎叆鐨� 8 鏈堟暟鎹笉浼氳嚜鍔ㄨ繘鏁存湰锛涗笅涓�姝ュ疄鐜� SummaryFiller 璇诲簱鍥炲~锛堝綋鏈�/绱/鍚屾瘮/鎺掑悕锛夈��
+- 8 鏈堢己鏁帮紙鑷姩濉�煎墠缃級锛歠reight_turnover_import銆乻cale_split_transport銆乸assenger_individual_monthly銆亀yc_total/order 鍧囨棤 2026-08 琛岋紙璐ц繍浼佷笟鏈堟姤銆佸叕璺梾瀹€�佸叕浜ゃ�佸贰娓稿嚭绉熷凡鏈� 08锛夈��
+- 杩愭斂鈥滄暣杞�/鍏朵粬杞︹�濆垎绫荤矑搴︼細RULE_001 鏁磋溅娈电幇鎸夎繍鏀� other锛堢壍寮�/鎸傝溅浠ュ锛夊彛寰勬瘮瀵癸紝鑻ヤ笟鍔¤姹傗�滄暣杞�=涓嶅惈闆嗚绠扁�濋渶杩愭斂渚ф暟鎹厤鍚堛��
+- 寰呯敤鎴风偣楠岋細瀵煎叆寮圭獥 UI 鏁堟灉锛汻ULE_001 鍛戒腑闃堝�硷紙5 鍙�/50 鍚級鏄惁绗﹀悎涓氬姟棰勬湡銆�
+
+## 鏄庢棩璁″垝
+- 鍚姩 M2 SummaryFiller锛堝厛 7 鏈堝洖褰� diff=0锛屽啀 8 鏈堝疄寮癸級锛岃ˉ榻� 8 鏈堣揣杩愰噺鍛ㄨ浆閲�/瑙勪笂瑙勪笅/涓綋瀹㈣繍绛夌己鏁板悗鏁撮摼楠岃瘉銆�
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java b/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
index 799c2fc..8269c27 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
@@ -1569,6 +1569,8 @@
             switch (rule.getCompareType()) {
                 case "DIFF":
                     return checkDiff(rule, report, authMap);
+                case "DIFF_FIELDS":
+                    return checkDiffFields(rule, report, authMap);
                 case "RATIO":
                     return checkRatio(rule, report, authMap, trackMap);
                 case "MOM":
@@ -1609,6 +1611,86 @@
                 "宸窛鈮�" + fmt(threshold), "宸窛=" + fmt(diff));
         }
         return null;
+    }
+
+    /** RULE_001 鍒嗛」杩愭斂瀵规瘮锛氱壍寮曡溅/鎸傝溅/鏁磋溅杞﹁締鏁颁笌鎸傝溅鍚ㄤ綅/鏁磋溅鍚ㄤ綅/鎬诲惃浣嶅垎鍒笌杩愭斂瀵规瘮锛屼换涓�鍒嗛」瓒呴檺鍗虫彁閱� */
+    private AuditResult checkDiffFields(AuditRule rule, H2032EnterpriseMonthly report,
+                                        Map<String, TransportAuthVehicle> authMap) {
+        TransportAuthVehicle auth = findAuth(authMap, report.getEnterpriseName());
+        if (auth == null) return null;
+        String[] fields = rule.getCheckField() == null ? new String[0] : rule.getCheckField().split(",");
+        String[] limits = rule.getThreshold() == null ? new String[0] : rule.getThreshold().split("[,;|]");
+        if (fields.length == 0 || limits.length != fields.length) return null;
+        java.util.List<String> hits = new java.util.ArrayList<>();
+        double maxAbs = 0;
+        for (int i = 0; i < fields.length; i++) {
+            String field = fields[i].trim();
+            double limit;
+            try {
+                limit = Double.parseDouble(limits[i].trim());
+            } catch (Exception e) {
+                return null;
+            }
+            double actual;
+            double reference;
+            String unit;
+            switch (field) {
+                case "vehicleTractor":
+                    actual = num(report.getVehicleTractor());
+                    reference = num(auth.getTractorCount());
+                    unit = "鍙�";
+                    break;
+                case "vehicleTrailer":
+                    actual = num(report.getVehicleTrailer());
+                    reference = num(auth.getTrailerCount());
+                    unit = "鍙�";
+                    break;
+                case "tonsTrailer":
+                    actual = nvl(report.getTonsTrailer());
+                    reference = nvl(auth.getTrailerTons());
+                    unit = "鍚�";
+                    break;
+                case "vehicleWhole":
+                    actual = num(report.getVehicleWhole());
+                    reference = num(auth.getOtherCount());
+                    unit = "鍙�";
+                    break;
+                case "tonsWhole":
+                    actual = nvl(report.getTonsWhole());
+                    reference = nvl(auth.getOtherTons());
+                    unit = "鍚�";
+                    break;
+                case "tonsTotal":
+                    actual = nvl(report.getTonsTotal());
+                    reference = nvl(auth.getTrailerTons()) + nvl(auth.getOtherTons());
+                    unit = "鍚�";
+                    break;
+                default:
+                    return null;
+            }
+            double diff = Math.abs(actual - reference);
+            maxAbs = Math.max(maxAbs, diff);
+            if (diff > limit) {
+                hits.add(h2032FieldLabel(field) + "锛氫笂鎶�=" + fmt(actual) + " " + unit + "锛岃繍鏀�=" + fmt(reference) + " " + unit
+                    + "锛屽樊" + fmt(diff) + " " + unit + "锛堥檺" + fmt(limit) + " " + unit + "锛�");
+            }
+        }
+        if (hits.isEmpty()) return null;
+        StringBuilder detail = new StringBuilder();
+        for (int i = 0; i < hits.size(); i++) {
+            String one = hits.get(i);
+            if (detail.length() == 0) {
+                detail.append(one);
+            } else if (detail.length() + one.length() + 1 <= 180) {
+                detail.append("锛�").append(one);
+            } else {
+                detail.append("锛涚瓑").append(hits.size() - i).append("椤硅秴闄�");
+                break;
+            }
+        }
+        return buildResult(rule, report, detail.toString(),
+            "鐗靛紩杞�/鎸傝溅/鏁磋溅杞﹁締宸墹5鍙帮紱鎸傝溅鍚ㄤ綅/鏁磋溅鍚ㄤ綅宸墹50鍚紱鎬诲惃浣嶅樊鈮�50鍚�",
+            "瓒呭嚭椤规暟=" + hits.size() + "锛屾渶澶у樊=" + fmt(maxAbs));
     }
 
     /** 涓婃姤鍛ㄨ浆閲忎笌杞ㄨ抗娴嬬畻鍛ㄨ浆閲忓姣旓紝楂樺嚭闃堝�兼瘮渚嬫牳瀹� */
@@ -1804,6 +1886,10 @@
         labels.put("tonsWhole", "鏁磋溅鍚ㄤ綅");
         labels.put("turnoverTotal", "鍛ㄨ浆閲�");
         labels.put("vehicleWhole", "鏁磋溅杞﹁締鏁�");
+        labels.put("vehicleTractor", "鐗靛紩杞︽暟");
+        labels.put("vehicleTrailer", "鎸傝溅鏁�");
+        labels.put("tonsTrailer", "鎸傝溅鍚ㄤ綅");
+        labels.put("tonsWhole", "鏁磋溅鍚ㄤ綅");
         String v = labels.get(field);
         return v == null ? field : v;
     }
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 2f2ebe2..9c502da 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
@@ -3234,7 +3234,6 @@
             dynamicSummaryYear(wb, year);
             cleanSummarySheetPresentation(wb); // 鍙栨秷鍚勯〉绛涢�夈�佽繕鍘熼殣钘忚锛堝惁鍒欌�滅彮绾垮寘杞︹�濈瓑椤电湅璧锋潵鍍忕己鏁版嵁锛�
             applyTwoDecimalFormat(wb); // 鏁板�兼樉绀轰袱浣嶅皬鏁帮紙淇濈暀鍏ㄧ簿搴︼級锛�% / 鏃ユ湡绛夋棦鏈夋牱寮忎笉鏀瑰彉
-            autoFitContentColumns(wb); // 鎸夋暟鎹嚜鍔ㄥ姞瀹藉垪瀹斤紙鍙姞瀹戒笉缂╃獎锛岃烦杩囧悎骞舵爣棰樻牸锛岄槻闀挎枃鏈妸鍗曞垪鎾戠垎锛�
             if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr();
             wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // 杩樺師/鏀瑰啓鍏紡鍚庢墦寮�鍗抽噸绠�
             return toBytes(wb);
@@ -4822,59 +4821,6 @@
         }
     }
 
-    private void autoFitContentColumns(org.apache.poi.ss.usermodel.Workbook wb) {
-        if (wb == null) return;
-        org.apache.poi.ss.usermodel.DataFormatter dfmt = new org.apache.poi.ss.usermodel.DataFormatter();
-        for (int s = 0; s < wb.getNumberOfSheets(); s++) {
-            org.apache.poi.ss.usermodel.Sheet sh = wb.getSheetAt(s);
-            if (sh == null) continue;
-            java.util.Set<String> merged = new java.util.HashSet<>();
-            for (org.apache.poi.ss.util.CellRangeAddress ra : sh.getMergedRegions()) {
-                for (int r = ra.getFirstRow(); r <= ra.getLastRow(); r++) {
-                    for (int c = ra.getFirstColumn(); c <= ra.getLastColumn(); c++) {
-                        merged.add(r + ":" + c);
-                    }
-                }
-            }
-            int maxCol = -1;
-            for (org.apache.poi.ss.usermodel.Row row : sh) {
-                if (row == null) continue;
-                maxCol = Math.max(maxCol, (int) row.getLastCellNum() - 1);
-            }
-            if (maxCol < 0) continue;
-            int[] need = new int[maxCol + 1];
-            for (org.apache.poi.ss.usermodel.Row row : sh) {
-                if (row == null) continue;
-                for (org.apache.poi.ss.usermodel.Cell cell : row) {
-                    int c = cell.getColumnIndex();
-                    if (c > maxCol || merged.contains(row.getRowNum() + ":" + c)) continue;
-                    String txt;
-                    try {
-                        txt = dfmt.formatCellValue(cell);
-                    } catch (Exception ignore) {
-                        continue;
-                    }
-                    if (txt == null || txt.isEmpty()) continue;
-                    int units = 0;
-                    for (int i = 0; i < txt.length(); i++) {
-                        units += isWideChar(txt.charAt(i)) ? 2 : 1;
-                    }
-                    if (units > need[c]) need[c] = Math.min(units, 34); // 闀挎枃鏈笉鎶婂崟鍒楁拺鐖�
-                }
-            }
-            for (int c = 0; c <= maxCol; c++) {
-                if (need[c] <= 0) continue;
-                int target = Math.min(need[c] + 2, 46) * 256;
-                if (target > sh.getColumnWidth(c)) sh.setColumnWidth(c, target);
-            }
-        }
-    }
-
-    /** CJK / 鍏ㄨ瀛楃鎸変袱鍊嶅搴﹁锛堣繎浼� Excel 鏄剧ず鍒楀锛� */
-    private boolean isWideChar(char ch) {
-        return (ch >= 0x2E80 && ch <= 0x9FFF) || (ch >= 0xF900 && ch <= 0xFAFF)
-            || (ch >= 0xFF00 && ch <= 0xFFEF);
-    }
 
     private byte[] toBytes(XSSFWorkbook wb) throws Exception {
         applyTwoDecimalFormat(wb);
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java b/traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java
index e4087de..a3dbab1 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java
@@ -64,19 +64,22 @@
         return Result.ok("宸蹭繚瀛�");
     }
 
-    /** % 鍓嶇殑鏁板�硷紙鍚嶇О/璇存槑涓殢闃堝�煎姩鎬佸彉鍖栫殑鐗囨锛�% 鍓嶅彲鑳芥槸 卤銆�>銆佽秴杩囥�侀珮鍑� 绛夋弿杩拌瘝锛屼粎鏇挎崲鏁板瓧鏈韩锛� */
-    private static final Pattern PCT_NUM = Pattern.compile("\\d+(?:\\.\\d+)?(?=%)");
+    /** 鍚嶇О/璇存槑涓殢闃堝�煎姩鎬佸彉鍖栫殑鏁板瓧鐗囨锛氬尮閰嶄换鎰忔暟瀛楋紙% 鍓嶅悗銆佸彴/鍚�/鍏噷绛夊崟浣嶅墠鍧囧彲锛� */
+    private static final Pattern NUM_TOKEN = Pattern.compile("\\d+(?:\\.\\d+)?");
 
-    /** 瑙f瀽闃堝�煎瓧绗︿覆涓涓�涓暟鍊硷紱鏃犳硶瑙f瀽杩斿洖 null */
-    private Double parseThresholdNum(String threshold) {
-        if (threshold == null || threshold.trim().isEmpty()) return null;
-        java.util.regex.Matcher m = Pattern.compile("\\d+(?:\\.\\d+)?").matcher(threshold.trim());
-        if (!m.find()) return null;
-        try {
-            return Double.parseDouble(m.group());
-        } catch (Exception e) {
-            return null;
+    /** 瑙f瀽闃堝�煎瓧绗︿覆涓殑鎵�鏈夋暟鍊硷紙鏀寔鈥�5,5,50鈥濈瓑澶氭闃堝�硷紝濡� RULE_001 鍒嗛」杩愭斂瀵规瘮锛� */
+    private java.util.List<Double> parseThresholdNums(String threshold) {
+        java.util.List<Double> out = new java.util.ArrayList<>();
+        if (threshold == null || threshold.trim().isEmpty()) return out;
+        java.util.regex.Matcher m = NUM_TOKEN.matcher(threshold.trim());
+        while (m.find()) {
+            try {
+                out.add(Double.parseDouble(m.group()));
+            } catch (Exception e) {
+                // 蹇界暐鏃犳硶瑙f瀽鐨勬
+            }
         }
+        return out;
     }
 
     /** 鏁板�煎睍绀猴細鏁存暟鍘绘帀灏忔暟浣嶏紝鍏朵綑淇濈暀鍘熺簿搴� */
@@ -86,10 +89,11 @@
         return new java.math.BigDecimal(String.valueOf(v)).stripTrailingZeros().toPlainString();
     }
 
-    /** 鐢遍槇鍊兼暟鍊兼帹瀵尖�滆鏄庢枃瀛楅噷鍐欑殑鏄櫨鍒嗕箣鍑犫�濈殑鍊欓�夊彛寰勶細
+    /** 鐢遍槇鍊兼暟鍊兼帹瀵尖�滃悕绉�/璇存槑閲屽啓鐨勬槸鐧惧垎涔嬪嚑鈥濈殑鍊欓�夊彛寰勶細
      *  鈶� 闃堝�兼湰韬氨鏄櫨鍒嗘暟锛堝 20 鈫� 卤20%锛夛紱
      *  鈶� 闃堝�兼槸灏忔暟姣斾緥锛堝 0.02 鈫� 2%銆�0.5 鈫� 50%锛夛紱姝ゆ椂 <1 鎵嶅惎鐢紱
-     *  鈶� 闃堝�兼槸姣斿�硷紙濡� 1.3 鈫� 楂樺嚭30%锛夛紱姝ゆ椂 >1 鎵嶅惎鐢ㄣ�� */
+     *  鈶� 闃堝�兼槸姣斿�硷紙濡� 1.3 鈫� 楂樺嚭30%锛夛紱姝ゆ椂 >1 鎵嶅惎鐢紱
+     *  鍏朵綑鎸夊師鏁板尮閰嶏紙鍙�/鍚�/鍏噷绛夐槇鍊煎師鏍峰嚭鐜板湪鏂囧瓧涓級銆� */
     private double[] thresholdPercentCandidates(double v) {
         double[] cands;
         if (v > 0 && v < 1) {
@@ -102,16 +106,23 @@
         return cands;
     }
 
-    /** 鎸夋棫闃堝�煎尮閰嶆枃鏈腑宸叉湁鐨勭櫨鍒嗘瘮鐗囨锛屼粎鏇挎崲鈥滀笌鏃ч槇鍊煎寰椾笂鈥濈殑鏁板瓧涓烘柊闃堝�煎搴旀暟瀛楋紱瀵逛笉涓婄殑鏁板瓧锛堝璇存槑閲屼笌闃堝�兼棤鍏崇殑鏁板�硷級淇濇寔涓嶅姩 */
+    /** 鎸夆�滄棫闃堝�尖啋鏂伴槇鍊尖�濋�愭鏀瑰啓鍚嶇О/璇存槑涓殑鏁板瓧锛�
+     *  鍙浛鎹笌鏃ч槇鍊兼煇涓�娈碉紙鎴栧叾鐧惧垎姣斿彛寰勶級瀵瑰緱涓婄殑鏁板瓧锛涘娈甸槇鍊硷紙閫楀彿鍒嗛殧锛夐�愭瀵瑰簲锛�
+     *  涓庨槇鍊兼棤鍏崇殑鏁板瓧锛堟棩鏈熴�佷笌瑙勫垯鏃犲叧鐨勫浐瀹氭暟鍊肩瓑锛変繚鎸佷笉鍔ㄣ�� */
     private String decorateThresholdText(String text, String oldThreshold, String newThreshold) {
         if (text == null || text.trim().isEmpty()) return text;
-        Double oldNum = parseThresholdNum(oldThreshold);
-        Double newNum = parseThresholdNum(newThreshold);
-        if (oldNum == null || newNum == null) return text;
-        double[] oldCands = thresholdPercentCandidates(oldNum);
-        double[] newCands = thresholdPercentCandidates(newNum);
-        if (oldCands.length != newCands.length) return text;
-        java.util.regex.Matcher m = PCT_NUM.matcher(text);
+        java.util.List<Double> olds = parseThresholdNums(oldThreshold);
+        java.util.List<Double> news = parseThresholdNums(newThreshold);
+        if (olds.isEmpty() || olds.size() != news.size()) return text;
+        int n = olds.size();
+        double[][] oc = new double[n][];
+        double[][] nc = new double[n][];
+        for (int i = 0; i < n; i++) {
+            oc[i] = thresholdPercentCandidates(olds.get(i));
+            nc[i] = thresholdPercentCandidates(news.get(i));
+            if (oc[i].length != nc[i].length) return text;
+        }
+        java.util.regex.Matcher m = NUM_TOKEN.matcher(text);
         StringBuffer sb = new StringBuffer();
         while (m.find()) {
             double token;
@@ -121,10 +132,13 @@
                 continue;
             }
             String repl = null;
-            for (int i = 0; i < oldCands.length; i++) {
-                if (Math.abs(oldCands[i] - token) < 1e-9) {
-                    repl = fmtNum(newCands[i]);
-                    break;
+            outer:
+            for (int i = 0; i < n; i++) {
+                for (int j = 0; j < oc[i].length; j++) {
+                    if (Math.abs(oc[i][j] - token) < 1e-9) {
+                        repl = fmtNum(nc[i][j]);
+                        break outer;
+                    }
                 }
             }
             if (repl == null) continue;
@@ -134,7 +148,7 @@
         return sb.toString();
     }
 
-    /** 鍚嶇О/璇存槑闅忛槇鍊煎悓姝ワ紙update 鏃舵寜 鏃р啋鏂� 鏀瑰啓骞舵寔涔呭寲锛� */
+    /** 鍚嶇О/璇存槑闅忛槇鍊煎悓姝ワ紙update 鏃舵寜 鏃р啋鏂� 鏀瑰啓骞舵寔涔呭寲锛涘娈甸槇鍊煎悓鏍烽�愭鑱斿姩锛� */
     private void syncThresholdText(AuditRule rule, String oldThreshold) {
         if (rule == null || rule.getThreshold() == null) return;
         rule.setRuleName(decorateThresholdText(rule.getRuleName(), oldThreshold, rule.getThreshold()));
@@ -145,4 +159,4 @@
     private void syncThresholdText(AuditRule rule) {
         syncThresholdText(rule, rule == null ? null : rule.getThreshold());
     }
-}
\ No newline at end of file
+}
diff --git a/traffic-audit-web/src/views/DataImport.vue b/traffic-audit-web/src/views/DataImport.vue
index 3215245..2d77063 100644
--- a/traffic-audit-web/src/views/DataImport.vue
+++ b/traffic-audit-web/src/views/DataImport.vue
@@ -372,6 +372,7 @@
         message: ''
       }
       this.importOne(row).then(() => {
+        this.showImportResult(row)
         if ((row.type === 'investment' || row.type === 'investmentLogistics') && row.status === 'success') {
           this.checkInvest(this.period)
         }
@@ -466,6 +467,20 @@
         })
         .finally(() => { this.cityDirLoading = false })
     },
+    showImportResult(row) {
+      if (!row || !row.status) return
+      const msg = (row.message || '').toString()
+      if (row.status === 'success') {
+        const text = (msg && msg.indexOf('瀵煎叆') !== -1) ? msg : '鏁版嵁瀵煎叆鎴愬姛'
+        this.$message({ message: text, type: 'success', duration: 5000 })
+      } else {
+        this.$alert((msg || '鏁版嵁瀵煎叆澶辫触锛岃瑙佷笅鏂瑰け璐ヨ褰�').replace(/\n/g, '<br>'), '鏁版嵁瀵煎叆澶辫触', {
+          dangerouslyUseHTMLString: true,
+          type: 'error',
+          confirmButtonText: '鐭ラ亾浜�'
+        })
+      }
+    },
     importOne(row) {
       if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return Promise.resolve() }
       row.status = 'uploading'

--
Gitblit v1.9.1