From ced20d8410d47258d8ecdca40e894c7466866f77 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期五, 04 九月 2026 17:13:01 +0800
Subject: [PATCH] docs: 拍板记录(1-6月累计/同比口径、7月列留空)与 09-04 工作日结(扩列改造列入明早待办)

---
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java |  157 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 155 insertions(+), 2 deletions(-)

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 b52289d..9fe6cae 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
@@ -456,7 +456,7 @@
         Map<String, Double> h2032Cum = loadH2032FreightCumulative(period);
         ScaleSplitTransport provCum = getProvinceCumulative(period);
         Double total = freightCum(ft.get("婀栧寳鐪�"), month);
-        Double totalWan = total == null ? null : round(total / 10000.0, 4);
+        Double totalWan = total == null ? null : round(total, 4); // 妯℃澘鍒楀凡鏄竾鍚紝涓嶅啀 梅10000
         double above = 0.0;
         for (Double v : h2032Cum.values()) if (v != null) above += v;
         double aboveWan = round(above / 10000.0, 4);
@@ -3207,7 +3207,19 @@
         }
         try (InputStream in = new FileInputStream(template);
              XSSFWorkbook wb = new XSSFWorkbook(in)) {
+            File donor = resolveSummaryDonor(year, month);
+            if (donor != null) {
+                try (InputStream din = new FileInputStream(donor); XSSFWorkbook dwb = new XSSFWorkbook(din)) {
+                    int restored = restoreSummaryMonthlyBlocks(wb, dwb);
+                    log.info("姹囨�诲伐浣滅翱鏈堝害鍧楄繕鍘熷畬鎴愶紝琛ラ綈/瑕嗙洊鍗曞厓鏍兼暟={}锛堟瘝鐗堬細{}锛屽浠芥瘝鐗堬細{}锛�", restored, template.getName(), donor.getName());
+                }
+            } else {
+                log.warn("姹囨�诲伐浣滅翱鏈壘鍒板悓鏈熷浠芥瘝鐗堬紙_澶囦唤_{}骞磠}鏈堚�︼級锛岃緭鍑哄皢淇濇寔姣嶇増鐜扮姸锛堝彲鑳戒负 1-4 鏈堥鏋讹級", year, month);
+            }
             dynamicSummaryYear(wb, year);
+            applyTwoDecimalFormat(wb); // 姹囨�诲ぇ琛ㄤ繚鐣欎汉宸ユ瘝鐗堝垪瀹�/鐗堝紡锛氫笉鍋� autoFit锛堥暱鍏紡浼氭妸鍒楀椤剁垎锛�
+            if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr();
+            wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // 杩樺師/鏀瑰啓鍏紡鍚庢墦寮�鍗抽噸绠�
             return toBytes(wb);
         }
     }
@@ -3474,6 +3486,86 @@
         return map;
     }
 
+    /** 瀹氫綅鍚屾湡澶囦唤姣嶇増锛堟竻鐞嗗墠鐨勫畬鏁存湀姣嶇増锛夛紝鐢ㄤ簬鎶婇鏋舵瘝鐗堢己澶辩殑 5-7 鏈堟湀搴﹀潡鎸夊浠借繕鍘燂紱
+     *  涓� resolveAnyTemplate 鐩稿悓绛栫暐锛氬湪閰嶇疆鐩綍銆乽ser.dir 鐩稿鐩綍銆佷笂绾х洰褰曚笁涓�欓�夋牴涓壂鎻� */
+    private File resolveSummaryDonor(int year, int month) {
+        String rel = summaryTemplateDir;
+        while (rel.startsWith("./")) rel = rel.substring(2);
+        String[] roots = {
+            summaryTemplateDir,
+            System.getProperty("user.dir") + "/" + rel,
+            System.getProperty("user.dir") + "/../" + rel
+        };
+        for (String root : roots) {
+            if (root == null || root.trim().isEmpty()) continue;
+            File dir = new File(root);
+            File[] files = dir.listFiles((d, name) ->
+                name.startsWith("_澶囦唤_") && name.contains(year + "骞�" + month + "鏈�") && name.contains("閬撹矾杩愯緭閲忔眹鎬昏〃"));
+            if (files != null && files.length > 0) return files[0];
+        }
+        return null;
+    }
+
+    /** 鎸夊悓鏈熷浠芥瘝鐗堣繕鍘熸湀搴﹀潡锛氬嚒澶囦唤鏈夊唴瀹硅�屾瘝鐗堝悓鏍肩己澶�/鍐呭涓嶅悓锛堟竻鐞嗘椂琚鎺夌殑 5-7 鏈堝�间笌绱鍏紡锛夌殑鍗曞厓鏍硷紝鏁存牸鍥炲~骞惰鐩� */
+    private int restoreSummaryMonthlyBlocks(XSSFWorkbook wb, XSSFWorkbook donor) {
+        int restored = 0;
+        for (int i = 0; i < donor.getNumberOfSheets(); i++) {
+            Sheet ds = donor.getSheetAt(i);
+            if (ds == null) continue;
+            Sheet bs = wb.getSheet(ds.getSheetName());
+            if (bs == null) continue;
+            for (int r = ds.getFirstRowNum(); r <= ds.getLastRowNum(); r++) {
+                Row dr = ds.getRow(r);
+                if (dr == null) continue;
+                Row br = bs.getRow(r);
+                for (Cell dc : dr) {
+                    if (dc == null) continue;
+                    CellType t = dc.getCellType();
+                    if (t == CellType.BLANK) continue;
+                    String dtx = summaryCellText(dc);
+                    if (dtx == null || dtx.isEmpty()) continue;
+                    Cell bc = (br == null) ? null : br.getCell(dc.getColumnIndex());
+                    if (bc != null && dtx.equals(summaryCellText(bc))) continue;
+                    Row tr = (bc != null) ? br : bs.getRow(r);
+                    if (tr == null) tr = bs.createRow(r);
+                    Cell tc = (bc != null) ? bc : tr.getCell(dc.getColumnIndex());
+                    if (tc == null) tc = tr.createCell(dc.getColumnIndex());
+                    tc.setBlank();
+                    if (t == CellType.FORMULA) {
+                        String f = dc.getCellFormula();
+                        if (f != null) tc.setCellFormula(f);
+                    } else if (t == CellType.STRING) {
+                        tc.setCellValue(dc.getStringCellValue());
+                    } else if (t == CellType.NUMERIC) {
+                        tc.setCellValue(dc.getNumericCellValue());
+                    } else if (t == CellType.BOOLEAN) {
+                        tc.setCellValue(dc.getBooleanCellValue());
+                    }
+                    restored++;
+                }
+            }
+        }
+        return restored;
+    }
+
+    /** 鍗曞厓鏍煎唴瀹规枃鏈寲锛堝叕寮忓甫 = 鍓嶇紑锛夛紝鐢ㄤ簬璺ㄧ翱姣斿 */
+    private String summaryCellText(Cell c) {
+        if (c == null) return null;
+        switch (c.getCellType()) {
+            case FORMULA: {
+                String f = c.getCellFormula();
+                return f == null ? "" : "=" + f;
+            }
+            case STRING:
+                return c.getStringCellValue();
+            case NUMERIC:
+                return Double.toString(c.getNumericCellValue());
+            case BOOLEAN:
+                return Boolean.toString(c.getBooleanCellValue());
+            default:
+                return "";
+        }
+    }
     /** 姹囨�诲伐浣滅翱骞翠唤鍔ㄦ�佸寲锛氫粎鎶� 2026骞� 鍓嶇紑鏇挎崲涓虹洰鏍囧勾锛�2025/2024 鍙傝�冨垪涓嶅姩锛� */
     private void dynamicSummaryYear(XSSFWorkbook wb, int year) {
         String oldPrefix = "2026骞�";
@@ -4574,7 +4666,13 @@
                         CellType ct = cell.getCellType();
                         if (ct != CellType.NUMERIC && ct != CellType.FORMULA) continue;
                         if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) continue;
-                        if (ct == CellType.NUMERIC && Math.rint(cell.getNumericCellValue()) == cell.getNumericCellValue()) continue;
+                        boolean integral = false;
+                        if (ct == CellType.NUMERIC) {
+                            integral = Math.rint(cell.getNumericCellValue()) == cell.getNumericCellValue();
+                        } else if (cell.getCachedFormulaResultType() == CellType.NUMERIC) {
+                            integral = Math.rint(cell.getNumericCellValue()) == cell.getNumericCellValue();
+                        }
+                        if (integral) continue; // 鏁存暟涓嶆樉绀� .00锛堝惈鍏紡缁撴灉涓烘暣鏁扮殑鏍硷級
                         org.apache.poi.ss.usermodel.CellStyle cs = cell.getCellStyle();
                         if (cs == null) continue;
                         String f = cs.getDataFormatString();
@@ -4595,6 +4693,61 @@
         }
     }
 
+    /** 姹囨�昏〃瀵煎嚭鍚庢寜鍐呭鑷姩鍔犲鍒楀锛堝彧鍔犲涓嶇缉绐勶紱璺宠繃鍚堝苟鍗曞厓鏍兼爣棰橈紝闃查暱鏍囬鎶婂崟鍒楁拺鐖嗭級 */
+    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);
         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {

--
Gitblit v1.9.1