From 117ecd77287794026954a5147f196bda81ddbf3b Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期五, 04 九月 2026 14:07:22 +0800
Subject: [PATCH] feat: 汇总工作簿月度块按备份母版还原(1-4月骨架→1-7月)试点 + fullCalcOnLoad + 09-04 测试报告

---
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java |   91 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 91 insertions(+), 0 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 b1b1566..87fe99d 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
@@ -3207,9 +3207,20 @@
         }
         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);
             autoFitContentColumns(wb); // 姹囨�昏〃瀵煎嚭鍚庢寜鍐呭鍔犲鍒楀锛堝彧鍔犲涓嶇缉绐勶級
+            if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr();
+            wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // 杩樺師/鏀瑰啓鍏紡鍚庢墦寮�鍗抽噸绠�
             return toBytes(wb);
         }
     }
@@ -3476,6 +3487,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骞�";

--
Gitblit v1.9.1