From 8b8971e94db61fb346598a2402d65c8ddb196d6b Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期六, 03 十月 2026 20:23:30 +0800
Subject: [PATCH] fix(holiday): 快报民航累计同比只与往年已覆盖日期比较

---
 HANDOFF.md                                                                                          |    7 +++
 traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayBulletinServiceTest.java |   65 ++++++++++++++++++++++++++++++++
 traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java     |   24 +++++++++++-
 3 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/HANDOFF.md b/HANDOFF.md
index 8ab0814..189008f 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -23,6 +23,13 @@
 - **楠岃瘉**锛歁aven 缂栬瘧鍜屾祴璇曢�氳繃锛涢噸鏂扮敓鎴� 2026-10-01 蹇姤锛屾鏂囦粛涓衡�滃闀�2.3%鈥濅笖涓嶅惈鈥滃闀夸簡鈥濓紱Word 瀵煎嚭鐢� 2 椤垫仮澶嶄负 1 椤碉紝鏁板瓧涓嶅啀鎷嗚銆傛渶缁堟牱鎶ヨ鏈湴 `_tmp_holiday_verify/bulletin_final_signed_styled_2026-10-01.docx`銆�
 - **鐘舵��**锛�2026-10-03 鐢ㄦ埛楠屾敹閫氳繃锛涙湰娆℃彁浜ゅ彧鍖呭惈浠g爜銆佸洖褰掓祴璇曞拰鏈氦鎺ヨ褰曪紝涓嶅寘鍚� `_tmp_*` 杩囩▼浜х墿鍙婄敤鎴锋湭鎻愪氦鐨勬暟鎹枃浠堕噸鍛藉悕銆�
 
+### 10-03 娣卞锛�2锛夛細缂哄綋鏃ユ皯鑸暟鎹椂鐨勭疮璁″悓姣斿姣旀湡闂翠慨姝�
+- **蹇姤姘戣埅绱鍚屾瘮瀵规瘮鏈熼棿淇锛�2026-10-03锛岀己褰撴棩姘戣埅鏁版嵁鍦烘櫙锛�**锛氬綋鏌愯繍杈撴柟寮忓綋骞寸疮璁¤鐩栫殑鏃ユ湡鐭簬鎵�閫夋棩鏈燂紙鍏稿瀷涓� 10 鏈� 2 鏃ユ皯鑸暟鎹湭鍑恒�佹皯鑸疮璁″彧鏈� 10-01锛夛紝绱鍚屾瘮鏀逛负銆屽彧鍜屽線骞村凡缁忚鐩栧埌鐨勬棩鏈熸瘮杈冦�嶁�斺�斿厛鎵惧綋骞存渶鍚庝竴涓湁璇ユ寚鏍囨暟鎹殑鏃ユ湡锛屽啀鍙栧線骞村悓鏃ョ疮璁°�備慨澶嶅悗缂哄け姘戣埅鐨� 10 鏈� 2 鏃ュ揩鎶ユ皯鑸疮璁″悓姣旂敱 `-50.5%/-49.9%` 鍙樹负 `-10.0%/-0.9%`锛堜笌鎵嬪伐绋� -10.03%/-0.87% 鐨� 1 浣嶅皬鏁板�间竴鑷达級銆傚疄鐜拌 `HolidayBulletinService.comparableCumulative`锛堜粎浣滅敤浜庢皯鑸锛夛紝鍏朵綑琛屼笌鎬讳汉鍛樻祦鍔ㄩ噺鍙e緞涓嶅彉銆�
+- **銆岀疮璁″悓姣� -4.65% vs 蹇姤 -4.6%銆嶇粨璁猴紙2026-10-03锛屾棤闇�鏀逛唬鐮侊級**锛欵xcel 鏄剧ず鐨� -4.65% 鏄�**涓や綅灏忔暟**缁撴灉锛涘簳灞傜簿纭瘮鍊兼槸 `-4.64633%`锛�2026 绱 3082.23818銆�2025 绱 3232.42742锛夛紝**涓�娆″洓鑸嶄簲鍏ュ埌 1 浣嶅皬鏁板氨鏄� -4.6%**銆傛妸宸茬粡鍙栧埌涓や綅灏忔暟鐨� -4.65 鍐嶅彇涓�浣嶆墠寰楀埌 -4.7锛屽睘浜屾鍙栨暣锛屼笉閲囩敤銆傚悓鐞� 2026 瀵� 2024 涓� `-1.7710%` 鈫� `-1.8%`銆傚揩鎶ョ櫨鍒嗘瘮缁熶竴 1 浣嶅皬鏁板彛寰勪笉鍙樸��
+- **銆屾殏缂烘皯鑸�嶆彁绀�**锛氱敤鎴风‘璁ょ敱浜哄伐鍦ㄧ敓鎴愮殑蹇姤绋夸笂淇敼锛岀郴缁熶笉鐢熸垚璇ユ彁绀鸿锛�**涓嶅仛浠g爜鏀瑰姩**銆�
+- **楠岃瘉**锛歚HolidayBulletinServiceTest` 3 涓敤渚嬮�氳繃锛堟柊澧� 2 涓鐩栨皯鑸疮璁″姣旀湡闂翠笌鏃犳暟鎹繑鍥� null锛夛紱绔埌绔敤搴撳唴鐪熷疄 2026-10-02 鏁版嵁锛堥搧璺�/閬撹矾/姘磋矾/杞︽祦榻愬叏銆佺己姘戣埅锛夐噸鏂扮敓鎴愬揩鎶ワ紝姘戣埅绱鍚屾瘮 `-10.0%/-0.9%`銆佹皯鑸疮璁� `6.75`銆佹�昏绱 `3082.24`銆佹�昏绱鍚屾瘮 `-4.6%/-1.8%`锛屼笌鎵嬪伐绋垮彛寰勪竴鑷淬��
+- **Git**锛氭湰娆℃敼鍔ㄥ緟鐢ㄦ埛銆岄獙鏀堕�氳繃銆嶅悗鍐嶆彁浜�/鎺ㄩ�併��
+
 ---
 
 ### 琛ュ厖锛�10-03 涓嬪崍锛夛細鏈嶅姟鍣ㄩ儴缃叉姤 鈥淎ccess denied ... (using password: NO)鈥� 鈥斺�� 鏍瑰洜涓庝慨澶�
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 1ef5884..a80ca4f 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
@@ -31,6 +31,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
@@ -131,6 +132,10 @@
         if (table == null) {
             throw new IllegalArgumentException("蹇姤妯℃澘涓壘涓嶅埌鏁版嵁琛�");
         }
+        DailyMetrics previousYearCivilCumulative = comparableCumulative(
+                current, previousYear, DailyMetrics::getCivilAviation);
+        DailyMetrics twoYearsAgoCivilCumulative = comparableCumulative(
+                current, twoYearsAgo, DailyMetrics::getCivilAviation);
         writeTableRow(table, 2, new String[]{
                 number(current.getNonBusinessPersonFlow()),
                 ratio(current.getNonBusinessPersonFlow(), previousDay == null ? null : previousDay.getNonBusinessPersonFlow()),
@@ -174,8 +179,8 @@
                 previousYear == null ? null : previousYear.getCivilAviation(),
                 twoYearsAgo == null ? null : twoYearsAgo.getCivilAviation(),
                 currentCumulative.getCivilAviation(),
-                previousYearCumulative == null ? null : previousYearCumulative.getCivilAviation(),
-                twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getCivilAviation()));
+                previousYearCivilCumulative == null ? null : previousYearCivilCumulative.getCivilAviation(),
+                twoYearsAgoCivilCumulative == null ? null : twoYearsAgoCivilCumulative.getCivilAviation()));
         writeTableRow(table, 7, transportRow(current.getWater(),
                 previousDay == null ? null : previousDay.getWater(),
                 previousYear == null ? null : previousYear.getWater(),
@@ -332,6 +337,21 @@
         return (percent.signum() > 0 ? "澧為暱" : "涓嬮檷") + HolidayNumberUtils.format1(percent.abs()) + "%";
     }
 
+    private DailyMetrics comparableCumulative(DailyMetrics current, DailyMetrics comparisonYear,
+                                              Function<DailyMetrics, BigDecimal> selector) {
+        if (current == null || comparisonYear == null) return null;
+        int coveredDay = 0;
+        for (int day = current.getDayIndex(); day >= 1; day--) {
+            DailyMetrics metrics = statService.dailyMetrics(current.getYear(), current.getHolidayType(), day);
+            if (metrics != null && selector.apply(metrics) != null) {
+                coveredDay = day;
+                break;
+            }
+        }
+        if (coveredDay == 0) return null;
+        return statService.cumulativeMetrics(comparisonYear.getYear(), current.getHolidayType(), coveredDay);
+    }
+
     private String ratio(BigDecimal current, BigDecimal base) {
         if (current == null || base == null || base.abs().compareTo(EPSILON) < 0) return "鈥�";
         BigDecimal ratio = HolidayNumberUtils.divide(current, base);
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 4830de8..7f585b3 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
@@ -1,11 +1,19 @@
 package com.trafficaudit.holiday.service;
 
+import com.trafficaudit.holiday.service.HolidayStatService.DailyMetrics;
 import org.junit.jupiter.api.Test;
 
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
+import java.util.function.Function;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 class HolidayBulletinServiceTest {
 
@@ -21,6 +29,63 @@
         assertEquals("鈥�", ratio(service, "100", "0"));
     }
 
+    @Test
+    void comparesCivilAviationOnlyThroughCoveredDay() throws Exception {
+        HolidayStatService stat = mock(HolidayStatService.class);
+        HolidayBulletinService service = new HolidayBulletinService(stat);
+
+        DailyMetrics current = new DailyMetrics();
+        current.setYear(2026);
+        current.setHolidayType("NATIONAL_DAY");
+        current.setDayIndex(2);
+
+        DailyMetrics coveredDay = new DailyMetrics();
+        coveredDay.setCivilAviation(new BigDecimal("6.7482"));
+        DailyMetrics missingDay = new DailyMetrics();
+
+        when(stat.dailyMetrics(2026, "NATIONAL_DAY", 2)).thenReturn(missingDay);
+        when(stat.dailyMetrics(2026, "NATIONAL_DAY", 1)).thenReturn(coveredDay);
+
+        DailyMetrics previousYear = new DailyMetrics();
+        previousYear.setYear(2025);
+        DailyMetrics previousYearThroughDay1 = new DailyMetrics();
+        previousYearThroughDay1.setCivilAviation(new BigDecimal("7.5003"));
+        when(stat.cumulativeMetrics(2025, "NATIONAL_DAY", 1)).thenReturn(previousYearThroughDay1);
+
+        Object result = comparableCumulative(service, current, previousYear, DailyMetrics::getCivilAviation);
+
+        assertSame(previousYearThroughDay1, result);
+        verify(stat).cumulativeMetrics(2025, "NATIONAL_DAY", 1);
+        verify(stat, never()).cumulativeMetrics(2025, "NATIONAL_DAY", 2);
+    }
+
+    @Test
+    void comparableCumulativeIsNullWhenCurrentYearHasNoMetricData() throws Exception {
+        HolidayStatService stat = mock(HolidayStatService.class);
+        HolidayBulletinService service = new HolidayBulletinService(stat);
+
+        DailyMetrics current = new DailyMetrics();
+        current.setYear(2026);
+        current.setHolidayType("NATIONAL_DAY");
+        current.setDayIndex(2);
+        when(stat.dailyMetrics(2026, "NATIONAL_DAY", 2)).thenReturn(new DailyMetrics());
+        when(stat.dailyMetrics(2026, "NATIONAL_DAY", 1)).thenReturn(new DailyMetrics());
+
+        Object result = comparableCumulative(service, current, new DailyMetrics(), DailyMetrics::getCivilAviation);
+
+        assertNull(result);
+        verify(stat, never()).cumulativeMetrics(0, "NATIONAL_DAY", 1);
+    }
+
+    private Object comparableCumulative(HolidayBulletinService service, DailyMetrics current,
+                                        DailyMetrics comparisonYear,
+                                        Function<DailyMetrics, BigDecimal> selector) throws Exception {
+        Method method = HolidayBulletinService.class.getDeclaredMethod(
+                "comparableCumulative", DailyMetrics.class, DailyMetrics.class, Function.class);
+        method.setAccessible(true);
+        return method.invoke(service, current, comparisonYear, selector);
+    }
+
     private String ratio(HolidayBulletinService service, String current, String base) throws Exception {
         Method method = HolidayBulletinService.class.getDeclaredMethod("ratio", BigDecimal.class, BigDecimal.class);
         method.setAccessible(true);

--
Gitblit v1.9.1