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): 快报民航累计同比只与往年已覆盖日期比较

---
 traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayWorkbookService.java |  227 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 207 insertions(+), 20 deletions(-)

diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayWorkbookService.java b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayWorkbookService.java
index 3fc5d2e..9c06e00 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayWorkbookService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayWorkbookService.java
@@ -46,6 +46,7 @@
 
     private static final Map<String, String> PASSENGER_SHEET_MODE = new LinkedHashMap<>();
     private static final int FIRST_DATA_ROW_INDEX = 2;
+    private static final int DAY_ROW_SCAN_LIMIT = 30;
     private static final int[] REFERENCE_YEARS = {2024, 2025, 2026};
 
     static {
@@ -115,7 +116,7 @@
         ImportSummary summary = new ImportSummary();
         try (InputStream in = file.getInputStream(); Workbook workbook = WorkbookFactory.create(in)) {
             FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
-            importPassengerSheets(workbook, evaluator, year, dateByDay, summary);
+            importPassengerSheets(workbook, evaluator, year, holidayType, dateByDay, summary);
             importVehicleSheets(workbook, evaluator, year, dateByDay, summary);
         } catch (Exception e) {
             throw new IllegalArgumentException("宸ヤ綔绨胯В鏋愬け璐�: " + e.getMessage(), e);
@@ -161,7 +162,7 @@
             if (sheet == null) continue;
             Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7);
             if (yearColumn == null) continue;
-            Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + dayIndex - 1);
+            Row row = findDayRow(sheet, evaluator, dayIndex);
             if (row == null) continue;
             BigDecimal value = numberValue(row.getCell(yearColumn), evaluator);
             if (value != null) return true;
@@ -174,7 +175,7 @@
         if (expressway != null) {
             Integer yearColumn = findYearColumn(expressway, evaluator, year, 2, 7);
             if (yearColumn != null) {
-                Row row = expressway.getRow(FIRST_DATA_ROW_INDEX + dayIndex - 1);
+                Row row = findDayRow(expressway, evaluator, dayIndex);
                 if (row != null && numberValue(row.getCell(yearColumn), evaluator) != null) return true;
             }
         }
@@ -182,7 +183,7 @@
         if (nationalProvincial != null) {
             Integer yearColumn = findYearColumn(nationalProvincial, evaluator, year, 3, 8);
             if (yearColumn != null) {
-                Row row = nationalProvincial.getRow(FIRST_DATA_ROW_INDEX + dayIndex - 1);
+                Row row = findDayRow(nationalProvincial, evaluator, dayIndex);
                 if (row != null && numberValue(row.getCell(yearColumn), evaluator) != null) return true;
             }
         }
@@ -200,7 +201,7 @@
             if (yearColumn == null) continue;
             for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) {
                 if (day.getKey() >= latestDay) continue;
-                Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+                Row row = findDayRow(sheet, evaluator, day.getKey());
                 if (row == null) continue;
                 BigDecimal incoming = numberValue(row.getCell(yearColumn), evaluator);
                 if (incoming == null) continue;
@@ -236,7 +237,7 @@
         if (yearColumn == null) return;
         for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) {
             if (day.getKey() >= latestDay) continue;
-            Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+            Row row = findDayRow(sheet, evaluator, day.getKey());
             if (row == null) continue;
             BigDecimal incoming = numberValue(row.getCell(yearColumn), evaluator);
             if (incoming == null) continue;
@@ -261,7 +262,7 @@
             if (day.getKey() >= latestDay) continue;
             Map<String, BigDecimal> existingDay = existingVehicle.get(day.getValue());
             if (existingDay == null || existingDay.get(HolidayConstants.ROAD_NATIONAL_PROVINCIAL) == null) continue;
-            Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+            Row row = findDayRow(sheet, evaluator, day.getKey());
             if (row == null) continue;
             BigDecimal incoming;
             try {
@@ -278,8 +279,8 @@
         }
     }
 
-        Map<LocalDate, Map<String, BigDecimal>> result = new LinkedHashMap<>();
     private Map<LocalDate, Map<String, BigDecimal>> loadPassengerByDate(int year) {
+        Map<LocalDate, Map<String, BigDecimal>> result = new LinkedHashMap<>();
         List<HolidayPassengerFlow> rows = passengerFlowMapper.selectList(
                 new LambdaQueryWrapper<HolidayPassengerFlow>().eq(HolidayPassengerFlow::getYear, year));
         for (HolidayPassengerFlow row : rows) {
@@ -337,6 +338,7 @@
         try (InputStream in = new FileInputStream(reference); Workbook workbook = WorkbookFactory.create(in)) {
             FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
             seedNationalDayCalendar();
+            ensurePreviousDayCalendarIfMissing();
             for (int year : REFERENCE_YEARS) {
                 if (passengerFlowMapper.selectCount(new LambdaQueryWrapper<HolidayPassengerFlow>()
                         .eq(HolidayPassengerFlow::getYear, year)) == 0) {
@@ -351,6 +353,8 @@
                     seedCoefficientYear(workbook, evaluator, year);
                 }
             }
+            seedPreviousDayPassengerIfMissing(workbook, evaluator);
+            seedRoad2019IfMissing(workbook, evaluator);
         } catch (Exception e) {
             log.error("Failed to seed holiday reference data", e);
         }
@@ -381,7 +385,8 @@
     }
 
     private void importPassengerSheets(Workbook workbook, FormulaEvaluator evaluator, int year,
-                                       Map<Integer, LocalDate> dateByDay, ImportSummary summary) {
+                                       String holidayType, Map<Integer, LocalDate> dateByDay,
+                                       ImportSummary summary) {
         for (Map.Entry<String, String> entry : PASSENGER_SHEET_MODE.entrySet()) {
             Sheet sheet = workbook.getSheet(entry.getKey());
             if (sheet == null) {
@@ -394,7 +399,7 @@
                 continue;
             }
             for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) {
-                Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+                Row row = findDayRow(sheet, evaluator, day.getKey());
                 if (row == null) {
                     summary.addError(entry.getKey() + " 绗�" + day.getKey() + "鏃ワ細宸ヤ綔琛ㄧ己灏戣琛�");
                     continue;
@@ -418,6 +423,39 @@
                 upsertPassenger(year, day.getValue(), entry.getValue(), value);
                 summary.incrementPassenger();
             }
+        }
+        importPreviousDayPassengerRows(workbook, evaluator, year, holidayType, dateByDay, summary);
+    }
+
+    private void importPreviousDayPassengerRows(Workbook workbook, FormulaEvaluator evaluator, int year,
+                                                 String holidayType, Map<Integer, LocalDate> dateByDay,
+                                                 ImportSummary summary) {
+        LocalDate previousDate = previousDayDate(year, holidayType, dateByDay);
+        if (previousDate == null) return;
+        for (Map.Entry<String, String> entry : PASSENGER_SHEET_MODE.entrySet()) {
+            if (!HolidayConstants.MODE_RAIL.equals(entry.getValue())
+                    && !HolidayConstants.MODE_CIVIL_AVIATION.equals(entry.getValue())) {
+                continue;
+            }
+            Sheet sheet = workbook.getSheet(entry.getKey());
+            if (sheet == null) continue;
+            Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7);
+            if (yearColumn == null) continue;
+            Row row = findPreviousDayRow(sheet, evaluator);
+            if (row == null) continue;
+            BigDecimal value;
+            try {
+                value = numberValue(row.getCell(yearColumn), evaluator);
+            } catch (Exception e) {
+                summary.addError(entry.getKey() + " 鍓嶄竴鏃ヨ锛�" + e.getMessage());
+                continue;
+            }
+            if (value == null) {
+                summary.incrementSkipped();
+                continue;
+            }
+            upsertPassenger(year, previousDate, entry.getValue(), value);
+            summary.incrementPassenger();
         }
     }
 
@@ -445,7 +483,7 @@
             return;
         }
         for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) {
-            Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+            Row row = findDayRow(sheet, evaluator, day.getKey());
             if (row == null) {
                 summary.addError(sheetName + " 绗�" + day.getKey() + "鏃ワ細宸ヤ綔琛ㄧ己灏戣琛�");
                 continue;
@@ -491,7 +529,17 @@
                 Map<LocalDate, BigDecimal> values = loadPassengerValues(year, entry.getValue());
                 for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) {
                     if (!values.containsKey(day.getValue())) continue;
-                    setNumeric(sheet, FIRST_DATA_ROW_INDEX + day.getKey() - 1, yearColumn, values.get(day.getValue()));
+                    Row row = findDayRow(sheet, evaluator, day.getKey());
+                    if (row == null) continue;
+                    setNumeric(sheet, row.getRowNum(), yearColumn, values.get(day.getValue()));
+                }
+                LocalDate previousDate = previousDayDate(year, holidayType, dayDates);
+                Row previousRow = findPreviousDayRow(sheet, evaluator);
+                if (previousDate != null && previousRow != null) {
+                    BigDecimal previousValue = values.get(previousDate);
+                    if (previousValue != null) {
+                        setNumeric(sheet, previousRow.getRowNum(), yearColumn, previousValue);
+                    }
                 }
             }
         }
@@ -515,17 +563,23 @@
             Map<LocalDate, BigDecimal> values = loadVehicleValues(year, roadCategory);
             for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) {
                 if (!values.containsKey(day.getValue())) continue;
-                setNumeric(sheet, FIRST_DATA_ROW_INDEX + day.getKey() - 1, yearColumn, values.get(day.getValue()));
+                Row row = findDayRow(sheet, evaluator, day.getKey());
+                if (row == null) continue;
+                setNumeric(sheet, row.getRowNum(), yearColumn, values.get(day.getValue()));
             }
             if (comparisonBaseColumn != null && HolidayConstants.supportsComparisonBase(year)) {
                 Map<LocalDate, BigDecimal> bases = loadComparisonBaseValues(year);
                 for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) {
                     if (!bases.containsKey(day.getValue())) continue;
-                    setNumeric(sheet, FIRST_DATA_ROW_INDEX + day.getKey() - 1, comparisonBaseColumn, bases.get(day.getValue()));
+                    Row row = findDayRow(sheet, evaluator, day.getKey());
+                    if (row == null) continue;
+                    setNumeric(sheet, row.getRowNum(), comparisonBaseColumn, bases.get(day.getValue()));
                 }
                 for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) {
                     if (!bases.containsKey(day.getValue())) continue;
-                    int rowIndex = FIRST_DATA_ROW_INDEX + day.getKey() - 1;
+                    Row row = findDayRow(sheet, evaluator, day.getKey());
+                    if (row == null) continue;
+                    int rowIndex = row.getRowNum();
                     int excelRow = rowIndex + 1;
                     setFormula(sheet, rowIndex, 4, "=(D" + excelRow + "-C" + excelRow + ")/C" + excelRow);
                 }
@@ -560,9 +614,10 @@
         if (!configService.listHolidays(null, null).isEmpty()) {
             return;
         }
-        int[] lengths = {7, 8, 8};
-        for (int i = 0; i < REFERENCE_YEARS.length; i++) {
-            int year = REFERENCE_YEARS[i];
+        int[] calendarYears = {2019, 2024, 2025, 2026};
+        int[] lengths = {8, 7, 8, 8};
+        for (int i = 0; i < calendarYears.length; i++) {
+            int year = calendarYears[i];
             List<HolidayCalendar> existing = configService.findHolidayDays(year, HolidayConstants.TYPE_NATIONAL_DAY);
             Map<Integer, HolidayCalendar> byDay = new LinkedHashMap<>();
             for (HolidayCalendar row : existing) {
@@ -579,6 +634,87 @@
                 calendar.setUpdatedAt(LocalDateTime.now());
                 configService.saveHoliday(toMap(calendar));
             }
+            if (year == 2019) {
+                continue;
+            }
+            HolidayCalendar previousDay = new HolidayCalendar();
+            previousDay.setYear(year);
+            previousDay.setHolidayType(HolidayConstants.TYPE_NATIONAL_DAY);
+            previousDay.setHolidayDate(LocalDate.of(year, 10, 1).minusDays(1));
+            previousDay.setDayIndex(0);
+            previousDay.setCreatedAt(LocalDateTime.now());
+            previousDay.setUpdatedAt(LocalDateTime.now());
+            configService.saveHoliday(toMap(previousDay));
+        }
+    }
+
+    private void ensurePreviousDayCalendarIfMissing() {
+        for (int year : REFERENCE_YEARS) {
+            if (configService.findPreviousDay(year, HolidayConstants.TYPE_NATIONAL_DAY) != null) {
+                continue;
+            }
+            LocalDate firstHolidayDate = firstDate(dayDates(year, HolidayConstants.TYPE_NATIONAL_DAY));
+            if (firstHolidayDate == null) {
+                continue;
+            }
+            HolidayCalendar previousDay = new HolidayCalendar();
+            previousDay.setYear(year);
+            previousDay.setHolidayType(HolidayConstants.TYPE_NATIONAL_DAY);
+            previousDay.setHolidayDate(firstHolidayDate.minusDays(1));
+            previousDay.setDayIndex(0);
+            previousDay.setCreatedAt(LocalDateTime.now());
+            previousDay.setUpdatedAt(LocalDateTime.now());
+            configService.saveHoliday(toMap(previousDay));
+        }
+    }
+
+    private void seedPreviousDayPassengerIfMissing(Workbook workbook, FormulaEvaluator evaluator) {
+        for (int year : REFERENCE_YEARS) {
+            Map<Integer, LocalDate> dayDates = dayDates(year, HolidayConstants.TYPE_NATIONAL_DAY);
+            LocalDate previousDate = previousDayDate(year, HolidayConstants.TYPE_NATIONAL_DAY, dayDates);
+            if (previousDate == null) continue;
+            for (Map.Entry<String, String> entry : PASSENGER_SHEET_MODE.entrySet()) {
+                if (!HolidayConstants.MODE_RAIL.equals(entry.getValue())
+                        && !HolidayConstants.MODE_CIVIL_AVIATION.equals(entry.getValue())) {
+                    continue;
+                }
+                if (passengerFlowMapper.selectCount(new LambdaQueryWrapper<HolidayPassengerFlow>()
+                        .eq(HolidayPassengerFlow::getYear, year)
+                        .eq(HolidayPassengerFlow::getFlowDate, previousDate)
+                        .eq(HolidayPassengerFlow::getTransportMode, entry.getValue())) > 0) {
+                    continue;
+                }
+                Sheet sheet = workbook.getSheet(entry.getKey());
+                if (sheet == null) continue;
+                Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7);
+                if (yearColumn == null) continue;
+                Row row = findPreviousDayRow(sheet, evaluator);
+                if (row == null) continue;
+                BigDecimal value = numberValue(row.getCell(yearColumn), evaluator);
+                if (value != null) upsertPassenger(year, previousDate, entry.getValue(), value);
+            }
+        }
+    }
+
+    private void seedRoad2019IfMissing(Workbook workbook, FormulaEvaluator evaluator) {
+        if (passengerFlowMapper.selectCount(new LambdaQueryWrapper<HolidayPassengerFlow>()
+                .eq(HolidayPassengerFlow::getYear, 2019)
+                .eq(HolidayPassengerFlow::getTransportMode, HolidayConstants.MODE_ROAD)) > 0) {
+            return;
+        }
+        Sheet sheet = workbook.getSheet("閬撹矾");
+        if (sheet == null) return;
+        Integer yearColumn = findYearColumn(sheet, evaluator, 2019, 2, 20);
+        if (yearColumn == null) return;
+        for (int rowIndex = 0; rowIndex <= Math.min(sheet.getLastRowNum(), DAY_ROW_SCAN_LIMIT); rowIndex++) {
+            Row row = sheet.getRow(rowIndex);
+            if (row == null) continue;
+            Integer dayIndex = parseDayIndex(row.getCell(0), evaluator);
+            if (dayIndex == null) continue;
+            BigDecimal value = numberValue(row.getCell(yearColumn), evaluator);
+            if (value != null) {
+                upsertPassenger(2019, LocalDate.of(2019, 10, dayIndex), HolidayConstants.MODE_ROAD, value);
+            }
         }
     }
 
@@ -591,11 +727,22 @@
             Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7);
             if (yearColumn == null) continue;
             for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) {
-                Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+                Row row = findDayRow(sheet, evaluator, day.getKey());
                 if (row == null) continue;
                 BigDecimal value = numberValue(row.getCell(yearColumn), evaluator);
                 if (value != null) {
                     upsertPassenger(year, day.getValue(), entry.getValue(), value);
+                }
+            }
+            if (HolidayConstants.MODE_RAIL.equals(entry.getValue())
+                    || HolidayConstants.MODE_CIVIL_AVIATION.equals(entry.getValue())) {
+                Row previousRow = findPreviousDayRow(sheet, evaluator);
+                LocalDate previousDate = previousDayDate(year, HolidayConstants.TYPE_NATIONAL_DAY, dayDates);
+                if (previousRow != null && previousDate != null) {
+                    BigDecimal value = numberValue(previousRow.getCell(yearColumn), evaluator);
+                    if (value != null) {
+                        upsertPassenger(year, previousDate, entry.getValue(), value);
+                    }
                 }
             }
         }
@@ -618,7 +765,7 @@
         Integer yearColumn = findYearColumn(sheet, evaluator, year, startColumn, endColumn);
         if (yearColumn == null) return;
         for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) {
-            Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1);
+            Row row = findDayRow(sheet, evaluator, day.getKey());
             if (row == null) continue;
             BigDecimal value = numberValue(row.getCell(yearColumn), evaluator);
             if (value != null) {
@@ -659,6 +806,46 @@
         }
     }
 
+    private LocalDate previousDayDate(int year, String holidayType, Map<Integer, LocalDate> dateByDay) {
+        HolidayCalendar configured = configService.findPreviousDay(year, holidayType);
+        if (configured != null && configured.getHolidayDate() != null) {
+            return configured.getHolidayDate();
+        }
+        LocalDate firstDate = firstDate(dateByDay);
+        return firstDate == null ? null : firstDate.minusDays(1);
+    }
+
+    private LocalDate firstDate(Map<Integer, LocalDate> dateByDay) {
+        LocalDate result = null;
+        for (LocalDate date : dateByDay.values()) {
+            if (date != null && (result == null || date.isBefore(result))) result = date;
+        }
+        return result;
+    }
+
+    private Row findDayRow(Sheet sheet, FormulaEvaluator evaluator, int dayIndex) {
+        int lastRow = Math.min(sheet.getLastRowNum(), DAY_ROW_SCAN_LIMIT);
+        for (int rowIndex = 0; rowIndex <= lastRow; rowIndex++) {
+            Row row = sheet.getRow(rowIndex);
+            if (row == null) continue;
+            Integer rowDay = parseDayIndex(row.getCell(0), evaluator);
+            if (rowDay != null && rowDay.intValue() == dayIndex) return row;
+        }
+        return null;
+    }
+
+    private Row findPreviousDayRow(Sheet sheet, FormulaEvaluator evaluator) {
+        int lastRow = Math.min(sheet.getLastRowNum(), DAY_ROW_SCAN_LIMIT);
+        DataFormatter formatter = new DataFormatter();
+        for (int rowIndex = 0; rowIndex <= lastRow; rowIndex++) {
+            Row row = sheet.getRow(rowIndex);
+            if (row == null || row.getCell(0) == null) continue;
+            String text = formatter.formatCellValue(row.getCell(0), evaluator).trim();
+            if (text.contains("鍓嶄竴鏃�")) return row;
+        }
+        return null;
+    }
+
     private Map<Integer, LocalDate> dayDates(int year, String holidayType) {
         Map<Integer, LocalDate> result = new LinkedHashMap<>();
         for (HolidayCalendar row : configService.findHolidayDays(year, holidayType)) {

--
Gitblit v1.9.1