From dc0082bfbf706d94de4509db2338b32012f1aa3e Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期日, 04 十月 2026 10:59:28 +0800
Subject: [PATCH] fix(holiday): 快报累计值与道路同比按Excel显示精度处理

---
 HANDOFF.md                                                                                          |   27 +++++++++++++
 AGENTS.md                                                                                           |    4 +
 traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayBulletinServiceTest.java |   18 +++++++++
 traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java     |   26 ++++++++++--
 4 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md
index 03ec2eb..eb42587 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -33,9 +33,11 @@
 ## 楠岃瘉娉ㄦ剰
 - 娌欑鍐� `Get-NetTCPConnection` 浼氬洜鏉冮檺鈥滄嫆缁濊闂�濊�岄潤榛樺け璐ワ紝**涓嶈**鎹鍒ゅ畾绔彛鏈洃鍚紱鍒ゆ柇鏈嶅姟鐘舵�佷互 backend.log / frontend.log 涓哄噯銆�
 - 鐢ㄦ埛瑕佹眰锛氫复鏃惰剼鏈�/鏂囦欢鏈粡鍚屾剰涓嶅垹锛涘鍏ュけ璐ヨ鍦ㄩ〉闈㈡樉绀鸿鍙凤紱AI 鍒嗘瀽缁撴灉瑕佺粰缁撴瀯鍖栨帓鐗� + 閫愬瓧杈撳嚭鍔ㄧ敾銆�
+- **鏁版嵁瀵煎叆鏉冮檺锛�2026-10-04 鐢ㄦ埛鏄庣‘锛�**锛氶櫎闈炵敤鎴锋槑纭姹傚姪鎵嬪鍏ワ紝鍚﹀垯涓嶈鎵ц浠讳綍鏁版嵁瀵煎叆銆佽鐩栨垨閲嶅鎿嶄綔锛涚敤鎴疯嚜琛屽鍏ョ殑鏁版嵁榛樿瑙嗕负宸插畬鎴愶紝鍔╂墜鍙仛鍙鏍稿銆�
 
 ## 閲嶈鑺傚亣鏃ュ揩鎶ヤ笌鍒嗘瀽鎶ュ憡鍙e緞锛�2026-10-04 璧凤級
-- 蹇姤鐧惧垎姣斿繀椤诲厛鎸夊鍏ョ殑 `2026骞村崄涓�鏁版嵁.xlsx` 瀵瑰簲鍗曞厓鏍肩殑鏁板瓧鏍煎紡鐢熸垚鏄剧ず鍊硷紝鍐嶆寜蹇姤缁熶竴 1 浣嶅皬鏁板彇鏁淬�傚綋鍓嶆簮琛ㄤ笉鏄叏灞�鍚屼竴绮惧害锛氬鏁板崟鍏冩牸鏄� `0.0%`锛沗浜哄憳娴佸姩閲忔眹鎬籤 鐨勫綋鏃ョ幆姣� D/P/J銆佺疮璁″悓姣� V/W/Y/Z/AB/AC锛屼互鍙婃按璺綋鏃ョ幆姣� D 鏄� `0.00%`銆�
+- 蹇姤鐧惧垎姣斿繀椤诲厛鎸夊鍏ョ殑 `2026骞村崄涓�鏁版嵁.xlsx` 瀵瑰簲鍗曞厓鏍肩殑鏁板瓧鏍煎紡鐢熸垚鏄剧ず鍊硷紝鍐嶆寜蹇姤缁熶竴 1 浣嶅皬鏁板彇鏁淬�傚綋鍓嶆簮琛ㄤ笉鏄叏灞�鍚屼竴绮惧害锛氬鏁板崟鍏冩牸鏄� `0.0%`锛沗浜哄憳娴佸姩閲忔眹鎬籤 鐨勫綋鏃ョ幆姣� D/P/J銆佺疮璁″悓姣� V/W/Y/Z/AB/AC锛岄亾璺� `H`锛堝悓姣�2024骞达級锛屼互鍙婃按璺綋鏃ョ幆姣� D 鏄� `0.00%`銆�
+- `浜哄憳娴佸姩閲忔眹鎬籤 鐨勭疮璁″�� U/X/AA 瀛樼殑鏄嚎鍗曚綅锛屽崟鍏冩牸鏍煎紡涓� `0.0000000_`锛涘揩鎶ュ繀椤诲湪鎹㈢畻涓囦汉娆″墠鍏堟寜浜垮崟浣� 7 浣嶅皬鏁扮敓鎴愭樉绀哄�硷紝鍐嶄箻 10000 骞舵寜蹇姤 2 浣嶈緭鍑恒�傚搴旀�昏銆佸叕璺悎璁°�侀潪钀ヤ笟鎬у皬瀹㈣溅涓夐」銆�
 - 鏍稿棰嗗鍒嗘瀽鎶ュ憡鏃讹紝鎸夎鍒嗘瀽鎶ュ憡鑷韩鏄剧ず鐨勫皬鏁颁綅鏍稿锛屼笉濂楃敤蹇姤鐨� 1 浣嶅皬鏁板彛寰勶紱鐩爣鏄鐜颁汉宸ュ湪鏂囨。/Excel 涓婄湅鍒扮殑鍊笺��
 
 ## 宸ヤ綔瑙勮寖锛�2026-09-02 璧凤紱2026-09-27 鎸夊悓浜嬫彁浜ゆ柟寮忚皟鏁达級
diff --git a/HANDOFF.md b/HANDOFF.md
index ee3c765..6d85a97 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -5,6 +5,33 @@
 > **鍗忎綔绾﹀畾锛�2026-08-31锛�**锛氳銆屾彁浜ゃ��= 鏈湴 commit + 鎺ㄩ�佸埌杩滅 git锛坥rigin main锛孏itblit锛夛紝涓嶅啀鍙仛鏈湴 commit銆�
 
 ## 鏈�鏂帮紙2026-10-04锛�
+### 10-04 涓婂崍锛�10 鏈� 3 鏃ユ暟鎹鍏ュ苟鐢熸垚蹇姤
+
+- **婧愭枃浠�**锛歚D:\03_鍏徃\02_椤圭洰\27_婀栧寳鐪侀亾璺繍杈撲簨涓氬彂灞曚腑蹇僜10_涓氬姟\99_涓撻」缁熻\01_閲嶈鑺傚亣鏃2026\鍗佷竴\10鏈�3鏃2026骞村崄涓�鏁版嵁10鏈�3鏃�.xlsx`锛�2026-10-04 10:11 鏇存柊锛夛紱瑙傛祴绔欏師濮嬫暟鎹负鍚岀洰褰� `瑙傛祴绔欏垎鏃ヨ皟鏌ユ暟鎹�2026.10.3.xls`銆�
+- **瀵煎叆杩囩▼锛堜慨姝o級**锛氱敤鎴峰厛浜� 10:13:34 瀵煎叆涓绘暟鎹紱鍔╂墜闅忓悗鍦� 10:17:41 鍙堢敤鍚屼竴鏂囦欢閲嶅瀵煎叆浜嗕竴娆°�傛帴鍙h繑鍥� `passengerRows=14, vehicleRows=6, skippedRows=24, successRows=20, failRows=0`锛屾湭妫�娴嬪埌 10 鏈� 1銆�2 鏃ュ巻鍙叉暟鎹彉鍖栥�傚敮涓�閿懡涓悗浠呮洿鏂� `updated_at`锛屾病鏈夋柊澧為噸澶嶈銆�
+- **10 鏈� 3 鏃ュ叆搴撳��**锛氶搧璺� 72.4銆佹皯鑸� 5.7571銆侀亾璺� 73.12銆佹按璺� 5.5668 涓囦汉娆★紱楂橀�� 193.3634銆佹櫘閫氬浗鐪侀亾 323.7837 涓囪締娆★紱鍚堣浜哄憳娴佸姩閲� 1397.99694 涓囦汉娆°��
+- **瑙傛祴绔欐暟鎹紙淇锛�**锛氱敤鎴峰厛瀵煎叆锛涘姪鎵嬮噸澶嶅鍏ユ椂鎸夊悓鏃ヨ鐩栭噸鎻掞紝褰撳墠 2026-10-03 涓� 691 琛� / 691 涓敮涓�閿紝鏃犻噸澶嶏紱涓皬瀹㈡祦閲忓悎璁� 3,237,837 杈嗘锛�323.7837 涓囪締娆★級锛屼笌鏅�氬浗鐪侀亾鍏ュ簱鍊间竴鑷淬��
+- **蹇姤**锛氬凡鐢熸垚 `C:\Users\jcxiong\Downloads\婀栧寳鐪佸浗搴嗗亣鏈熺患鍚堣繍杈撳娴佸揩鎶ワ紙10鏈�3鏃ワ級.docx`銆傛鏂囷細褰撴棩 1398.00 涓囦汉娆★紝鐜瘮涓嬮檷 3.3%锛屽悓姣� 2025 涓嬮檷 10.1%锛屽悓姣� 2024 涓嬮檷 5.0%锛涚疮璁� 4486.63 涓囦汉娆★紝鍚屾瘮 2025 涓嬮檷 6.3%锛屽悓姣� 2024 涓嬮檷 2.7%銆�
+- **浜哄伐绋挎瘮瀵癸紙10-04 鏇存柊锛�**锛氭墜宸ョ涓庣郴缁熶慨姝g増鎻愬彇鏂囨湰閫愯涓�鑷达紱绯荤粺鍘熺増浠呮湁涓ゅ绱鏁板樊寮傦紝宸查殢鏈浠g爜淇娑堥櫎銆備粖鏃ヨ蛋鍔垮浘鍔熻兘鏆傚仠銆�
+
+### 10-04 涓婂崍锛�2锛夛細蹇姤绱鏁版寜 Excel 浜垮崟浣嶆樉绀虹簿搴﹀悗鍐嶆崲绠�
+
+- **闂**锛氱郴缁熷師鐗堜笌鎵嬪伐绋夸粎涓ゅ宸紓锛氶潪钀ヤ笟鎬у皬瀹㈣溅绱 `3961.76` 搴斾负 `3961.77`锛涘叕璺悎璁$疮璁� `4189.31` 搴斾负 `4189.32`銆�
+- **鏍瑰洜**锛氭簮琛� `浜哄憳娴佸姩閲忔眹鎬�!X5/AA5`锛堟�昏瀵瑰簲 `U5`锛夊瓨鐨勬槸浜垮崟浣嶏紝鍗曞厓鏍兼牸寮忎负 `0.0000000_`銆備汉鍏堢湅鍒� 7 浣嶅皬鏁扮殑鏄剧ず鍊硷紝鍐嶆崲绠楁垚涓囦汉娆″苟淇濈暀 2 浣嶏紱鏃т唬鐮佺洿鎺ユ嬁鍘熷绱鍊兼崲绠楀悗鍙� 2 浣嶏紝婕忔帀浜� Excel 鏄剧ず杩欎竴姝ャ��
+- **淇**锛歚HolidayBulletinService` 鏂板 `displayedCumulativeWan(...)`锛屽厛鎸夋簮鍗曞厓鏍� 7 浣嶅皬鏁板彇鏁达紝鍐嶄箻 10000锛屾渶鍚庢寜蹇姤 2 浣嶈緭鍑猴紱搴旂敤浜庢�昏銆侀潪钀ヤ笟鎬у皬瀹㈣溅銆佸叕璺悎璁′笁澶勭疮璁℃暟銆�
+- **楠岃瘉**锛氭柊澧炲崟娴嬭鐩� `3961.76472 -> 3961.77`銆乣4189.31472 -> 4189.32`銆乣4486.62652 -> 4486.63`锛沗mvn test` 鍏ㄩ�氳繃銆傞噸鍚悗鍩轰簬鐜版湁搴撻噸鏂扮敓鎴� 10 鏈� 3 鏃ュ揩鎶ワ紝涓庢墜宸ョ鎻愬彇鏂囨湰閫愯姣斿缁撴灉涓� `NO DIFFERENCES`銆傛湰娆℃湭鎵ц浠讳綍鏁版嵁瀵煎叆銆�
+- **浜х墿**锛歚_tmp_holiday_verify/bulletin_cumulative_display_2026-10-03.docx`銆�
+- **鐘舵��**锛氬緟鐢ㄦ埛楠屾敹/鎻愪氦锛涜蛋鍔垮浘鍔熻兘浠嶆殏鍋溿��
+
+### 10-04 涓婂崍锛�3锛夛細蹇姤鍏朵綑 Excel 鏄剧ず绮惧害璺緞瀹¤
+
+- **瀹¤鑼冨洿**锛氶�愰」鏄犲皠蹇姤姝f枃鍜岃〃鏍肩殑鎵�鏈夋暟鍊兼潵婧愶紝鏍稿婧愬伐浣滅翱鐨勫疄闄呭崟鍏冩牸鏍煎紡銆丒xcel 鏄剧ず鍊煎拰浠g爜浼犲叆鐨勬樉绀虹簿搴︺��
+- **缁撹**锛氶櫎宸蹭慨澶嶇殑绱鍊� `U/X/AA` 澶栵紝娌℃湁鍙戠幇鍏朵粬鏈鐞嗙殑蹇姤鍙栧�艰矾寰勩�傛簮琛ㄧ洿鎺ュ弬涓庡揩鎶ョ殑 `0.00%` 鍗曞厓鏍煎叡 11 澶勶細`浜哄憳娴佸姩閲忔眹鎬籤 D/P/J銆乂/W/Y/Z/AB/AC锛岄亾璺� H锛堝悓姣�2024骞达級锛屾按璺� D锛堝綋鏃ョ幆姣旓級锛涗唬鐮佸潎鍏堟寜婧愭牸寮忓彇 2 浣嶏紝鍐嶆寜蹇姤 1 浣嶅鐞嗐��
+- **琛ヤ慨杈圭晫**锛氶亾璺� H 鍘熸寜 1 浣嶆簮鏍煎紡澶勭悊锛�10 鏈� 3 鏃ョ宸т粛涓� `6.6%`锛涙敼涓哄厛鎸� `0.00%` 寰楀埌 `6.62%` 鍚庡啀鍙� 1 浣嶏紝杈圭晫鍊间笉浼氬樊 0.1 涓櫨鍒嗙偣銆�
+- **鏈撼鍏ョ洿鎺ュ揩鎶ヨ矾寰勭殑鏍煎紡**锛歚鏅�氬浗鐪侀亾` E/N 铏戒负 `0.00%`锛屽揩鎶ヤ娇鐢ㄧ殑鏄� `浜哄憳娴佸姩閲忔眹鎬籤 姹囨�诲悗鐨� P/R/T锛屼笉浣跨敤璇ヤ袱鍒楋紱楂橀�熷叕璺�/鏅�氬浗鐪侀亾鍐呴儴 4 浣嶅皬鏁颁汉鍛樻祦鍔ㄩ噺鍚岀悊鍙綔涓烘簮鍏紡涓棿鍒楋紝涓嶇洿鎺ヨ緭鍑恒��
+- **楠岃瘉**锛歚mvn test` 鍏ㄩ�氳繃锛涢噸鍚悗绔悗閲嶆柊鐢熸垚 10 鏈� 3 鏃ュ揩鎶ワ紝涓庢墜宸ョ閫愬潡銆侀�愬崟鍏冩牸姣旇緝鏃犱换浣曞樊寮傘��10 鏈� 2 鏃ヤ笌鎵嬪伐绋跨殑鍐呭鍜岃〃鏍兼枃鏈竴鑷达紝浠呮湁 Word run 鍒嗘鏍煎紡宸紓銆俙_tmp_holiday_verify/bulletin_after_road_format_2026-10-01/02/03.docx` 涓烘湰杞牳瀵逛骇鐗┿��
+- **鏁版嵁鎿嶄綔**锛氬彧璇绘牳瀵癸紝鏈鍏ャ�佽鐩栨垨閲嶅浠讳綍鏁版嵁銆�
+- **鐘舵��**锛氬緟鐢ㄦ埛楠屾敹/鎻愪氦銆�
 
 ### 10-04锛氬揩鎶ョ櫨鍒嗘瘮鎸� Excel 鍗曞厓鏍兼樉绀虹簿搴﹀悗鍐嶅彇 1 浣�
 
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java
index 312d18f..ebd3ca9 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java
@@ -44,6 +44,8 @@
     private static final BigDecimal EPSILON = new BigDecimal("0.0000001");
     private static final int EXCEL_ONE_DECIMAL_PERCENT = 1;
     private static final int EXCEL_TWO_DECIMAL_PERCENT = 2;
+    private static final int EXCEL_SEVEN_DECIMAL_YI = 7;
+    private static final BigDecimal TEN_THOUSAND = new BigDecimal("10000");
 
     @Value("${holiday.bulletin-template:docs/涓撻」缁熻/閲嶈鑺傚亣鏃�/婀栧寳鐪佸浗搴嗗亣鏈熺患鍚堣繍杈撳娴佸揩鎶ユā鏉�.docx}")
     private String bulletinTemplatePath;
@@ -139,7 +141,7 @@
                         EXCEL_ONE_DECIMAL_PERCENT),
                 ratio(current.getNonBusinessPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getNonBusinessPersonFlow(),
                         EXCEL_ONE_DECIMAL_PERCENT),
-                number(currentCumulative.getNonBusinessPersonFlow()),
+                numberCumulative(currentCumulative.getNonBusinessPersonFlow()),
                 ratio(currentCumulative.getNonBusinessPersonFlow(),
                         previousYearCumulative == null ? null : previousYearCumulative.getNonBusinessPersonFlow(),
                         EXCEL_TWO_DECIMAL_PERCENT),
@@ -151,7 +153,7 @@
                 number(current.getRoad()),
                 ratio(current.getRoad(), previousDay == null ? null : previousDay.getRoad(), EXCEL_ONE_DECIMAL_PERCENT),
                 ratio(current.getRoad(), previousYear == null ? null : previousYear.getRoad(), EXCEL_ONE_DECIMAL_PERCENT),
-                ratio(current.getRoad(), twoYearsAgo == null ? null : twoYearsAgo.getRoad(), EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(current.getRoad(), twoYearsAgo == null ? null : twoYearsAgo.getRoad(), EXCEL_TWO_DECIMAL_PERCENT),
                 number(currentCumulative.getRoad()),
                 ratio(currentCumulative.getRoad(), previousYearCumulative == null ? null : previousYearCumulative.getRoad(),
                         EXCEL_ONE_DECIMAL_PERCENT),
@@ -166,7 +168,7 @@
                         EXCEL_ONE_DECIMAL_PERCENT),
                 ratio(current.getRoadPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getRoadPersonFlow(),
                         EXCEL_ONE_DECIMAL_PERCENT),
-                number(currentCumulative.getRoadPersonFlow()),
+                numberCumulative(currentCumulative.getRoadPersonFlow()),
                 ratio(currentCumulative.getRoadPersonFlow(),
                         previousYearCumulative == null ? null : previousYearCumulative.getRoadPersonFlow(),
                         EXCEL_TWO_DECIMAL_PERCENT),
@@ -206,7 +208,7 @@
                         EXCEL_ONE_DECIMAL_PERCENT),
                 ratio(current.getTotalPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow(),
                         EXCEL_ONE_DECIMAL_PERCENT),
-                number(currentCumulative.getTotalPersonFlow()),
+                numberCumulative(currentCumulative.getTotalPersonFlow()),
                 ratio(currentCumulative.getTotalPersonFlow(),
                         previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow(),
                         EXCEL_TWO_DECIMAL_PERCENT),
@@ -376,7 +378,7 @@
                                        DailyMetrics twoYearsAgoCumulative) {
         BigDecimal total = currentCumulative == null ? null : currentCumulative.getTotalPersonFlow();
         return String.format("鍋囨湡浠ユ潵锛屽叏鐪佸叏绀句細璺ㄥ尯鍩熶汉鍛樻祦鍔ㄩ噺绱%s涓囦汉娆★紝姣�2025骞村悓鏈�%s锛屾瘮2024骞村悓鏈�%s銆�",
-                formatNumber(total),
+                formatNumber(displayedCumulativeWan(total)),
                 ratioPhrase(total, previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow(),
                         EXCEL_TWO_DECIMAL_PERCENT),
                 ratioPhrase(total, twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow(),
@@ -407,6 +409,20 @@
         return value == null ? "鈥�" : formatNumber(value);
     }
 
+    private String numberCumulative(BigDecimal value) {
+        return value == null ? "鈥�" : formatNumber(displayedCumulativeWan(value));
+    }
+
+    /**
+     * Source cumulative cells U5/X5/AA5 are stored in 100-million units and displayed with seven decimals.
+     * Mirror that displayed precision before the bulletin converts the value to 10-thousand units.
+     */
+    private BigDecimal displayedCumulativeWan(BigDecimal cumulativeWan) {
+        if (cumulativeWan == null) return null;
+        BigDecimal yi = cumulativeWan.divide(TEN_THOUSAND, EXCEL_SEVEN_DECIMAL_YI, RoundingMode.HALF_UP);
+        return HolidayNumberUtils.normalize(yi.multiply(TEN_THOUSAND));
+    }
+
     private String formatNumber(BigDecimal value) {
         if (value == null) return "";
         return HolidayNumberUtils.format2(value);
diff --git a/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayBulletinServiceTest.java b/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayBulletinServiceTest.java
index 846023c..cc81b4a 100644
--- a/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayBulletinServiceTest.java
+++ b/traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayBulletinServiceTest.java
@@ -26,6 +26,8 @@
         assertEquals("-4.5%", ratio(service, "95.551398", "100", 2));
         assertEquals("-0.1%", ratio(service, "99.955", "100", 2));
         assertEquals("0%", ratio(service, "99.955", "100", 1));
+        assertEquals("6.7%", ratio(service, "106.649", "100", 2));
+        assertEquals("6.6%", ratio(service, "106.649", "100", 1));
         assertEquals("1.0%", ratio(service, "101", "100", 1));
         assertEquals("0%", ratio(service, "100", "100", 1));
         assertEquals("0%", ratio(service, "10004", "10000", 1));
@@ -102,6 +104,22 @@
                 text);
     }
 
+    @Test
+    void cumulativeAmountsUseExcelYiDisplayBeforeWanRounding() throws Exception {
+        HolidayBulletinService service = new HolidayBulletinService(null);
+
+        assertEquals("3961.77", cumulativeNumber(service, "3961.76472"));
+        assertEquals("4189.32", cumulativeNumber(service, "4189.31472"));
+        assertEquals("4486.63", cumulativeNumber(service, "4486.62652"));
+    }
+
+    private String cumulativeNumber(HolidayBulletinService service, String value) throws Exception {
+        Method method = HolidayBulletinService.class.getDeclaredMethod("numberCumulative", BigDecimal.class);
+        method.setAccessible(true);
+        BigDecimal number = value == null ? null : new BigDecimal(value);
+        return (String) method.invoke(service, number);
+    }
+
     private Object comparableCumulative(HolidayBulletinService service, DailyMetrics current,
                                         DailyMetrics comparisonYear,
                                         Function<DailyMetrics, BigDecimal> selector) throws Exception {

--
Gitblit v1.9.1