From 2f3c2d2851f7693357feb9e62a56a64670755c6a Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期二, 06 十月 2026 14:25:27 +0800
Subject: [PATCH] fix(holiday): 拥挤度标签恢复两位小数

---
 traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayBulletinService.java |  171 ++++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 114 insertions(+), 57 deletions(-)

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 a80ca4f..77f85f9 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
@@ -42,6 +42,10 @@
 
     private static final String WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
     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;
@@ -61,7 +65,7 @@
             throw new IllegalArgumentException("鎵�閫夋棩鏈熶笉鍦ㄥ凡閰嶇疆鐨勮妭鍋囨棩鑼冨洿鍐�");
         }
         DailyMetrics previousDay = current.getDayIndex() > 1
-                ? statService.dailyMetrics(current.getYear(), current.getHolidayType(), current.getDayIndex() - 1) : null;
+                ? statService.ringRatioBaseMetrics(current.getYear(), current.getHolidayType(), current.getDayIndex()) : null;
         HolidayCalendar previousDayConfig = configService.findPreviousDay(current.getYear(), current.getHolidayType());
         LocalDate previousDayDate = previousDayConfig == null || previousDayConfig.getHolidayDate() == null
                 ? current.getDate().minusDays(1) : previousDayConfig.getHolidayDate();
@@ -78,21 +82,14 @@
         }
 
         String currentTotal = formatNumber(current.getTotalPersonFlow());
-        String cumulativeTotalYi = formatNumber(currentCumulative.getTotalPersonFlow() == null
-                ? null : HolidayNumberUtils.divide(currentCumulative.getTotalPersonFlow(), BigDecimal.valueOf(10000)));
         String ringRatioClause = previousDay == null ? "" : "锛岀幆姣�" + ratioPhrase(
-                current.getTotalPersonFlow(), previousDay.getTotalPersonFlow());
+                current.getTotalPersonFlow(), previousDay.getTotalPersonFlow(), EXCEL_TWO_DECIMAL_PERCENT);
         String paragraph1 = String.format("%d鏈�%d鏃ワ紙鍋囨湡绗�%d澶╋級锛屽叏鐪佸叏绀句細璺ㄥ尯鍩熶汉鍛樻祦鍔ㄩ噺%s涓囦汉娆�%s锛屾瘮2025骞村悓鏈�%s锛屾瘮2024骞村悓鏈�%s銆�",
                 date.getMonthValue(), date.getDayOfMonth(), current.getDayIndex(), currentTotal,
                 ringRatioClause,
-                ratioPhrase(current.getTotalPersonFlow(), previousYear == null ? null : previousYear.getTotalPersonFlow()),
-                ratioPhrase(current.getTotalPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow()));
-        String paragraph2 = String.format("鍋囨湡浠ユ潵锛屽叏鐪佸叏绀句細璺ㄥ尯鍩熶汉鍛樻祦鍔ㄩ噺绱%s浜夸汉娆★紝姣�2025骞村悓鏈�%s锛屾瘮2024骞村悓鏈�%s銆�",
-                cumulativeTotalYi,
-                ratioPhrase(currentCumulative.getTotalPersonFlow(),
-                        previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow()),
-                ratioPhrase(currentCumulative.getTotalPersonFlow(),
-                        twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow()));
+                ratioPhrase(current.getTotalPersonFlow(), previousYear == null ? null : previousYear.getTotalPersonFlow(), EXCEL_ONE_DECIMAL_PERCENT),
+                ratioPhrase(current.getTotalPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow(), EXCEL_ONE_DECIMAL_PERCENT));
+        String paragraph2 = cumulativeParagraph(currentCumulative, previousYearCumulative, twoYearsAgoCumulative);
 
         File template = resolveTemplateFile();
         if (!template.exists() || !template.isFile()) {
@@ -138,34 +135,46 @@
                 current, twoYearsAgo, DailyMetrics::getCivilAviation);
         writeTableRow(table, 2, new String[]{
                 number(current.getNonBusinessPersonFlow()),
-                ratio(current.getNonBusinessPersonFlow(), previousDay == null ? null : previousDay.getNonBusinessPersonFlow()),
-                ratio(current.getNonBusinessPersonFlow(), previousYear == null ? null : previousYear.getNonBusinessPersonFlow()),
-                ratio(current.getNonBusinessPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getNonBusinessPersonFlow()),
-                number(currentCumulative.getNonBusinessPersonFlow()),
+                ratio(current.getNonBusinessPersonFlow(), previousDay == null ? null : previousDay.getNonBusinessPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
+                ratio(current.getNonBusinessPersonFlow(), previousYear == null ? null : previousYear.getNonBusinessPersonFlow(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(current.getNonBusinessPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getNonBusinessPersonFlow(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                numberCumulative(currentCumulative.getNonBusinessPersonFlow()),
                 ratio(currentCumulative.getNonBusinessPersonFlow(),
-                        previousYearCumulative == null ? null : previousYearCumulative.getNonBusinessPersonFlow()),
+                        previousYearCumulative == null ? null : previousYearCumulative.getNonBusinessPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
                 ratio(currentCumulative.getNonBusinessPersonFlow(),
-                        twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getNonBusinessPersonFlow())
+                        twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getNonBusinessPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT)
         });
         writeTableRow(table, 3, new String[]{
                 number(current.getRoad()),
-                ratio(current.getRoad(), previousDay == null ? null : previousDay.getRoad()),
-                ratio(current.getRoad(), previousYear == null ? null : previousYear.getRoad()),
-                ratio(current.getRoad(), twoYearsAgo == null ? null : twoYearsAgo.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_TWO_DECIMAL_PERCENT),
                 number(currentCumulative.getRoad()),
-                ratio(currentCumulative.getRoad(), previousYearCumulative == null ? null : previousYearCumulative.getRoad()),
-                ratio(currentCumulative.getRoad(), twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRoad())
+                ratio(currentCumulative.getRoad(), previousYearCumulative == null ? null : previousYearCumulative.getRoad(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(currentCumulative.getRoad(), twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRoad(),
+                        EXCEL_ONE_DECIMAL_PERCENT)
         });
         writeTableRow(table, 4, new String[]{
                 number(current.getRoadPersonFlow()),
-                ratio(current.getRoadPersonFlow(), previousDay == null ? null : previousDay.getRoadPersonFlow()),
-                ratio(current.getRoadPersonFlow(), previousYear == null ? null : previousYear.getRoadPersonFlow()),
-                ratio(current.getRoadPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getRoadPersonFlow()),
-                number(currentCumulative.getRoadPersonFlow()),
+                ratio(current.getRoadPersonFlow(), previousDay == null ? null : previousDay.getRoadPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
+                ratio(current.getRoadPersonFlow(), previousYear == null ? null : previousYear.getRoadPersonFlow(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(current.getRoadPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getRoadPersonFlow(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                numberCumulative(currentCumulative.getRoadPersonFlow()),
                 ratio(currentCumulative.getRoadPersonFlow(),
-                        previousYearCumulative == null ? null : previousYearCumulative.getRoadPersonFlow()),
+                        previousYearCumulative == null ? null : previousYearCumulative.getRoadPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
                 ratio(currentCumulative.getRoadPersonFlow(),
-                        twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRoadPersonFlow())
+                        twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRoadPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT)
         });
         writeTableRow(table, 5, transportRow(current.getRail(),
                 firstDayRailBase == null ? null : firstDayRailBase.getRail(),
@@ -173,41 +182,54 @@
                 twoYearsAgo == null ? null : twoYearsAgo.getRail(),
                 currentCumulative.getRail(),
                 previousYearCumulative == null ? null : previousYearCumulative.getRail(),
-                twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRail()));
+                twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRail(),
+                EXCEL_ONE_DECIMAL_PERCENT));
         writeTableRow(table, 6, transportRow(current.getCivilAviation(),
                 firstDayCivilAviationBase == null ? null : firstDayCivilAviationBase.getCivilAviation(),
                 previousYear == null ? null : previousYear.getCivilAviation(),
                 twoYearsAgo == null ? null : twoYearsAgo.getCivilAviation(),
                 currentCumulative.getCivilAviation(),
                 previousYearCivilCumulative == null ? null : previousYearCivilCumulative.getCivilAviation(),
-                twoYearsAgoCivilCumulative == null ? null : twoYearsAgoCivilCumulative.getCivilAviation()));
+                twoYearsAgoCivilCumulative == null ? null : twoYearsAgoCivilCumulative.getCivilAviation(),
+                EXCEL_ONE_DECIMAL_PERCENT));
         writeTableRow(table, 7, transportRow(current.getWater(),
                 previousDay == null ? null : previousDay.getWater(),
                 previousYear == null ? null : previousYear.getWater(),
                 twoYearsAgo == null ? null : twoYearsAgo.getWater(),
                 currentCumulative.getWater(),
                 previousYearCumulative == null ? null : previousYearCumulative.getWater(),
-                twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getWater()));
-        writeTableRow(table, 8, transportRow(current.getTotalPersonFlow(),
-                previousDay == null ? null : previousDay.getTotalPersonFlow(),
-                previousYear == null ? null : previousYear.getTotalPersonFlow(),
-                twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow(),
-                currentCumulative.getTotalPersonFlow(),
-                previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow(),
-                twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow()));
+                twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getWater(),
+                EXCEL_TWO_DECIMAL_PERCENT));
+        writeTableRow(table, 8, new String[]{
+                number(current.getTotalPersonFlow()),
+                ratio(current.getTotalPersonFlow(), previousDay == null ? null : previousDay.getTotalPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
+                ratio(current.getTotalPersonFlow(), previousYear == null ? null : previousYear.getTotalPersonFlow(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(current.getTotalPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow(),
+                        EXCEL_ONE_DECIMAL_PERCENT),
+                numberCumulative(currentCumulative.getTotalPersonFlow()),
+                ratio(currentCumulative.getTotalPersonFlow(),
+                        previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
+                ratio(currentCumulative.getTotalPersonFlow(),
+                        twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT)
+        });
     }
 
     private String[] transportRow(BigDecimal current, BigDecimal previousDay, BigDecimal previousYear,
                                   BigDecimal twoYearsAgo, BigDecimal currentCumulative,
-                                  BigDecimal previousYearCumulative, BigDecimal twoYearsAgoCumulative) {
+                                  BigDecimal previousYearCumulative, BigDecimal twoYearsAgoCumulative,
+                                  int ringRatioExcelScale) {
         return new String[]{
                 number(current),
-                ratio(current, previousDay),
-                ratio(current, previousYear),
-                ratio(current, twoYearsAgo),
+                ratio(current, previousDay, ringRatioExcelScale),
+                ratio(current, previousYear, EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(current, twoYearsAgo, EXCEL_ONE_DECIMAL_PERCENT),
                 number(currentCumulative),
-                ratio(currentCumulative, previousYearCumulative),
-                ratio(currentCumulative, twoYearsAgoCumulative)
+                ratio(currentCumulative, previousYearCumulative, EXCEL_ONE_DECIMAL_PERCENT),
+                ratio(currentCumulative, twoYearsAgoCumulative, EXCEL_ONE_DECIMAL_PERCENT)
         };
     }
 
@@ -329,11 +351,10 @@
         return builder.toString();
     }
 
-    private String ratioPhrase(BigDecimal current, BigDecimal base) {
-        if (current == null || base == null || base.abs().compareTo(EPSILON) < 0) return "鏃犲彲姣旀暟鎹�";
-        BigDecimal ratio = HolidayNumberUtils.divide(current, base);
-        BigDecimal percent = HolidayNumberUtils.multiply(ratio.subtract(BigDecimal.ONE), BigDecimal.valueOf(100));
-        if (percent == null || percent.abs().compareTo(EPSILON) < 0) return "鎸佸钩";
+    private String ratioPhrase(BigDecimal current, BigDecimal base, int excelPercentScale) {
+        BigDecimal percent = bulletinPercent(current, base, excelPercentScale);
+        if (percent == null) return "鏃犲彲姣旀暟鎹�";
+        if (percent.signum() == 0) return "鎸佸钩";
         return (percent.signum() > 0 ? "澧為暱" : "涓嬮檷") + HolidayNumberUtils.format1(percent.abs()) + "%";
     }
 
@@ -352,20 +373,56 @@
         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);
-        if (ratio == null) return "鈥�";
-        BigDecimal percent = HolidayNumberUtils.multiply(ratio.subtract(BigDecimal.ONE), BigDecimal.valueOf(100));
+    private String cumulativeParagraph(DailyMetrics currentCumulative,
+                                       DailyMetrics previousYearCumulative,
+                                       DailyMetrics twoYearsAgoCumulative) {
+        BigDecimal total = currentCumulative == null ? null : currentCumulative.getTotalPersonFlow();
+        return String.format("鍋囨湡浠ユ潵锛屽叏鐪佸叏绀句細璺ㄥ尯鍩熶汉鍛樻祦鍔ㄩ噺绱%s涓囦汉娆★紝姣�2025骞村悓鏈�%s锛屾瘮2024骞村悓鏈�%s銆�",
+                formatNumber(displayedCumulativeWan(total)),
+                ratioPhrase(total, previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT),
+                ratioPhrase(total, twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow(),
+                        EXCEL_TWO_DECIMAL_PERCENT));
+    }
+
+    private String ratio(BigDecimal current, BigDecimal base, int excelPercentScale) {
+        BigDecimal percent = bulletinPercent(current, base, excelPercentScale);
         if (percent == null) return "鈥�";
-        BigDecimal rounded = percent.setScale(1, RoundingMode.HALF_UP);
-        return rounded.signum() == 0 ? "0%" : rounded.toPlainString() + "%";
+        return percent.signum() == 0 ? "0%" : percent.toPlainString() + "%";
+    }
+
+    /**
+     * Mirrors the source Excel percentage cell format, then applies the bulletin's one-decimal rule.
+     * Excel stores full precision but displays 0.0% or 0.00%; reviewers compare against that display.
+     */
+    private BigDecimal bulletinPercent(BigDecimal current, BigDecimal base, int excelPercentScale) {
+        if (current == null || base == null || base.abs().compareTo(EPSILON) < 0) return null;
+        BigDecimal ratio = HolidayNumberUtils.divide(current, base);
+        if (ratio == null) return null;
+        BigDecimal percent = HolidayNumberUtils.multiply(ratio.subtract(BigDecimal.ONE), BigDecimal.valueOf(100));
+        if (percent == null) return null;
+        BigDecimal displayed = percent.setScale(excelPercentScale, RoundingMode.HALF_UP);
+        return displayed.setScale(1, RoundingMode.HALF_UP);
     }
 
     private String number(BigDecimal value) {
         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);

--
Gitblit v1.9.1