traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
@@ -7,6 +7,8 @@
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -33,6 +35,8 @@
 */
@Service
public class SummaryWorkbookFiller {
    private static final Logger log = LoggerFactory.getLogger(SummaryWorkbookFiller.class);
    @Resource
    private MidCalc midCalc;
@@ -429,9 +433,11 @@
        if (wb == null || month <= 1) return 0;
        int targetCol = FIRST_MONTH_COL + 2 * (month - 1);
        int written = 0;
        int carried = 0;
        for (int i = 0; i < wb.getNumberOfSheets(); i++) {
            Sheet sh = wb.getSheetAt(i);
            if (sh == null || !isFilledLedgerSheet(sh.getSheetName())) continue;
            if (sh == null || !isMonthLedgerSheet(sh.getSheetName())) continue;
            boolean derivedSheet = isDerivedFormulaSheet(sh.getSheetName());
            for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
                Row row = sh.getRow(r);
                if (row == null) continue;
@@ -446,7 +452,21 @@
                        break;
                    }
                }
                if (srcCell == null) continue;
                if (srcCell == null) {
                    if (!derivedSheet) continue;
                    // 派生页(城市客运/中口径明细/公路总客运)里「各月都是同一个常量」的结构性常量行
                    // (如中口径明细 武汉 城际城乡巡游出租=0),按月沿用该常量,避免当月值列空着导致累计缺项
                    double struct = structuralConstant(row, month);
                    if (Double.isNaN(struct)) continue;
                    Cell target = row.getCell(targetCol);
                    if (target == null) target = row.createCell(targetCol);
                    Cell prev = row.getCell(FIRST_MONTH_COL + 2 * (month - 2));
                    if (prev != null && prev.getCellStyle() != null) target.setCellStyle(prev.getCellStyle());
                    target.setCellValue(struct);
                    carried++;
                    written++;
                    continue;
                }
                String f = srcCell.getCellFormula();
                if (f == null || f.isEmpty()) continue;
                Cell target = row.getCell(targetCol);
@@ -456,7 +476,31 @@
                written++;
            }
        }
        if (carried > 0 && log != null) {
            log.info("汇总工作簿:派生页结构性常量行按月沿用 {} 格(第 {} 月)", carried, month);
        }
        return written;
    }
    /** 该行 1..month-1 月的当月值列是否全为同一个数值常量(结构性常量行,如各月恒为 0) */
    private double structuralConstant(Row row, int month) {
        if (row == null || month <= 2) return Double.NaN;
        Double v = null;
        for (int s = 1; s < month; s++) {
            Cell c = row.getCell(FIRST_MONTH_COL + 2 * (s - 1));
            if (c == null || c.getCellType() != org.apache.poi.ss.usermodel.CellType.NUMERIC) return Double.NaN;
            double d = c.getNumericCellValue();
            if (v == null) v = d;
            else if (Math.abs(d - v) > 1e-9) return Double.NaN;
        }
        return v == null ? Double.NaN : v;
    }
    /** 派生页(当月值全部来自其他页/常量,纯公式驱动):城市客运 / 中口径明细 / 公路总客运 / 中口径客运量 */
    private boolean isDerivedFormulaSheet(String name) {
        if (name == null) return false;
        String n = name.trim();
        return "城市客运".equals(n) || "中口径明细".equals(n) || "公路总客运".equals(n) || "中口径客运量".equals(n);
    }
    /** 该格是否已有内容(数值/文本/公式);仅带样式的空壳(如母版里继承表头日期格式的格子)视为空 */
@@ -467,14 +511,6 @@
            case STRING: return c.getStringCellValue() != null && !c.getStringCellValue().trim().isEmpty();
            default: return true;
        }
    }
    /** 参与逐月数据回填的台账页(货运/班线包车/公交/出租/网约车/轨道轮渡) */
    private boolean isFilledLedgerSheet(String name) {
        if (name == null) return false;
        String n = name.trim();
        return "货运".equals(n) || "班线包车".equals(n) || "公交".equals(n)
                || "出租".equals(n) || "网约车".equals(n) || "轨道、轮渡".equals(n);
    }
    /** 0 基列号 + 1 基行号 -> "Q5" */
@@ -530,10 +566,9 @@
                String f = prev.getCellFormula();
                if (f == null || f.isEmpty()) continue;
                String shifted = shiftFormulaColumns(f, 2);
                if (target == null) {
                    target = row.createCell(yoyCol);
                    if (prev.getCellStyle() != null) target.setCellStyle(prev.getCellStyle());
                }
                if (target == null) target = row.createCell(yoyCol);
                // 母版把新月份的同比列做成了普通数值格式(显示成小数),统一沿用上月同比列的样式(百分数)
                if (prev.getCellStyle() != null) target.setCellStyle(prev.getCellStyle());
                try {
                    target.setCellFormula(shifted);
                    written++;