feat: 09-09晚 M2台账层数据驱动回填——班线包车/公交/出租/轨道轮渡读库现算写整本(7月回归1323格0差异) + 测试报告与工作日结
2个文件已修改
2个文件已添加
311 ■■■■■ 已修改文件
docs/功能测试报告/功能测试报告_汇总大表数据驱动回填M2台账层_2026-09-09.md 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/工作日结/工作日结_2026-09-09.md 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_»ã×Ü´ó±íÊý¾ÝÇý¶¯»ØÌîM2̨Õ˲ã_2026-09-09.md
New file
@@ -0,0 +1,27 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Š_汇总大表数据驱动回填(M2 å°è´¦å±‚)_2026-09-09
## åŠŸèƒ½è¯´æ˜Ž
汇总表整本(道路运输量汇总表)导出此前是“复制当月人工母版+备份还原”,当月没有人工母版就报错,用户每月需手工制作母版。
本轮落地 M2 ç¬¬ä¸€æ­¥ï¼šæ–°å¢ž `SummaryWorkbookFiller`,生成整本时把 2026 å¹´ 1..M æœˆâ€œå°è´¦é¡µå¸‚州明细行”的当月值从库现算写入母版对应格
(m æœˆå€¼åˆ— = ç¬¬ 2+2*(m-1) åˆ—,C èµ·ï¼‰ï¼Œå…¨çœè¡Œä¸Žè·¨é¡µåˆæˆä¸ºå…¬å¼ã€æ‰“开自动重算,2025 å‚照列保留母版缓存。
本轮覆盖 4 å¼ å°è´¦é¡µï¼šç­çº¿åŒ…车(含个体)、公交、出租、轨道轮渡;网约车(依赖拆分输入)、货运页(M3)及 9 æœˆä»¥åŽæ‰©åˆ—待下一轮。
## æµ‹è¯•环境
- åŽç«¯ï¼šSpring Boot 8090(mvn spring-boot:run,2026-09-09 16:28 é‡å¯å«æ–°ä»£ç ï¼‰ï¼›æ•°æ®åº“为本地业务库。
- åŸºå‡†ï¼šäººå·¥å¤‡ä»½æ¯ç‰ˆ `docs/生成汇总大表/_备份_2026å¹´7月道路运输量汇总表_清理前_20260904.xlsx`(2026-07 å£å¾„ 0 å·®å¼‚基准)。
## ç”¨ä¾‹è¡¨
| ç”¨ä¾‹ | è¾“å…¥ | é¢„期 | å®žé™… | ç»“论 |
|---|---|---|---|---|
| 7 æœˆæ•´æœ¬æ­£å‘回归 | GET /api/report/export/summaryWorkbook?period=2026-07 | ç­çº¿åŒ…车/公交/出租/轨道轮渡 4 é¡µæœˆåº¦å€¼åˆ— C..Q ä¸Žå¤‡ä»½æ¯ç‰ˆé€æ ¼ä¸€è‡´ | æ¯”较 1323 æ ¼ï¼Œå·®å¼‚ 0,maxAbs=0 | é€šè¿‡ |
| 7 æœˆå¤‡ä»½æ¯ç‰ˆä¸ªåˆ«æ ¼ä¸ºæ–‡æœ¬æ•°å­— | å…¬äº¤ 3/6 æœˆç­‰æ ¼å­˜ä¸º '4970.63' æ–‡æœ¬ | æ•°å€¼/文本同值视为一致 | æ–‡æœ¬è½¬æ•°å€¼åŽ 0 å·®å¼‚ | é€šè¿‡ |
| 8 æœˆæ•´æœ¬ï¼ˆæ— å¤‡ä»½æ¯ç‰ˆã€æ¯ç‰ˆç‰ˆå¼å« 8 æœˆåˆ—但数据只到 7 æœˆï¼‰ | period=2026-08 | 8 æœˆå½“月值(Q åˆ—)由库现算填入,已有 1-7 æœˆå€¼ä¸å›žé€€ | å…¬äº¤/出租 476+476 æ ¼ 0 å·®å¼‚;班线包车/轨道轮渡多出 Q åˆ— 8 æœˆåº“值(武汉班线客运量 329.906 ç­‰ï¼‰ï¼Œå±žé¢„期“读库填新月份” | é€šè¿‡ï¼ˆ8 æœˆçœŸå€¼æ— äººå·¥å¯¹ç…§ï¼Œä»¥ MidCalc 7 æœˆ 0 å·®å¼‚口径同源推算) |
| ç¼–译 | mvn -o compile | æ— é”™è¯¯ | é€šè¿‡ | é€šè¿‡ |
## å‘现的问题与处置
1. å¤‡ä»½æ¯ç‰ˆï¼ˆæ¸…理前)里公交部分月份格是“文本型数字”(如 '4970.63'),Excel å…¬å¼å¯æ­£å¸¸å‚与运算;生成文件一律写数值,属优化而非差异,比对脚本已按“文本数字→数值”处理。
2. 8 æœˆè¾“出目录中多本“8 æœˆæ•´æœ¬â€å…¶å®žåªå¡«åˆ° 7 æœˆï¼ˆQ åˆ—空),印证用户“每月手工填数/母版”痛点;本引擎已能把 8 æœˆå°è´¦é¡µæ–°æœˆä»½æ•°æ®ä»Žåº“算入。
3. é—留:9 æœˆåŠä»¥ä¸Šæ— å¯¹åº”版式列,需要“自动扩列”(在累计列前插入当月值+同比两列并同步公式/样式);网约车/货运/城市客运等其余页取数待 M2/M3 åŽç»­æŽ¥å…¥ï¼›ç¼ºæ•°æç¤ºå½“前进日志,页面提示接入在整链完成后补。
## ç»“论
M2 å°è´¦å±‚(班线包车/公交/出租/轨道轮渡)数据驱动回填可用:2026-07 ä¸Žäººå·¥å¤‡ä»½æ¯ç‰ˆé€æ ¼ 0 å·®å¼‚,2026-08 å¯æŠŠ 8 æœˆæ–°æœˆä»½æ•°æ®ä»Žåº“填入,未破坏既有 1-7 æœˆå€¼ã€‚可进入下一轮(自动扩列 + ç½‘约车/货运/合成页 M3)开发。
docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-09.md
@@ -59,3 +59,22 @@
### é—留问题与待办(与上午一致)
- æ±‡æ€»å¤§è¡¨ 8 æœˆè‡ªåŠ¨å¡«å€¼ï¼ˆM2/M3 SummaryFiller)未启动;8 æœˆç¼º freight_turnover_import、scale_split_transport、passenger_individual_monthly、wyc_total/order æºæ•°æ®ã€‚
- å¾…业务终验:问题范围/视图/状态控件、行内数据弹窗、导出 Excel ä¸Žæ¨¡æ¿/四条例文逐条核对;导入弹窗 UI、RULE_001 é˜ˆå€¼ï¼ˆ5 å°/50 å¨ï¼‰æ˜¯å¦ç¬¦åˆä¸šåŠ¡é¢„æœŸã€‚
---
## ä¸‹åˆç¬¬å››è½®ï¼ˆ2026-09-09 17:xx)M2 æ•°æ®é©±åŠ¨å›žå¡«å¯åŠ¨ï¼šå°è´¦å±‚è¯»åº“çŽ°ç®—å†™æ•´æœ¬
### å®Œæˆå†…容
- æ–°å¢ž `SummaryWorkbookFiller`(reportexport/calc):生成整本时把 2026 å¹´ 1..M æœˆå°è´¦é¡µå¸‚州明细行当月值从库现算写入(m æœˆå€¼åˆ—=C+2*(m-1)),行定位按“列A市州名+列B指标文案”扫描,不写死行号;全省行/跨页合成保持公式,打开自动重算;2025 å‚照列沿用母版缓存。
- è¦†ç›–页:班线包车(含个体)、公交、出租、轨道轮渡;网约车(依赖拆分输入)与货运页待 M3。
- ReportExportService.exportSummaryWorkbook æŽ¥å…¥å›žå¡«ï¼ˆdonor è¿˜åŽŸåŽè¦†ç›–å½“æœˆå€¼ï¼‰ï¼Œç¼ºæ•°æç¤ºè¿›æ—¥å¿—ã€‚
### æµ‹è¯•结果摘要
- mvn -o compile é€šè¿‡ï¼›åŽç«¯ 16:28 é‡å¯å°±ç»ªã€‚
- 2026-07 æ•´æœ¬å›žå½’:四张台账页月度值列(C..Q)与备份母版共 1323 æ ¼é€æ ¼ 0 å·®å¼‚(备份母版部分文本数字按数值比较)。
- 2026-08 æ•´æœ¬ï¼šå…¬äº¤/出租 0 å·®å¼‚;班线包车/轨道轮渡把库中 8 æœˆï¼ˆQ åˆ—)数据现算填入(此前的“8月整本”其实只填到 7 æœˆï¼‰ï¼Œç¬¦åˆæ•°æ®é©±åŠ¨ç›®æ ‡ã€‚
- æµ‹è¯•报告:docs/功能测试报告/功能测试报告_汇总大表数据驱动回填M2台账层_2026-09-09.md。
### é—留问题与待办
- 9 æœˆåŠä»¥ä¸Šæ— å½“月母版且母版无对应月份列:需“自动扩列”(累计列前插当月值+同比两列、公式/样式/2025 å‚照组右移同步),本轮未做。
- M2 å‰©ä½™é¡µï¼šç½‘约车台账(需《网约车订单及全省总量》当月拆分输入)、货运页(FreightCalc è§„上/规下/全口径)+ M3 åˆæˆ/成品全链。
- ç¼ºæ•°æç¤ºç›®å‰è¿›åŽç«¯æ—¥å¿—,页面提示接入待整链完成。
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
New file
@@ -0,0 +1,256 @@
package com.trafficaudit.reportexport.calc;
import com.trafficaudit.common.util.RegionUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
 * æ±‡æ€»æ•´æœ¬â€œæ•°æ®é©±åŠ¨å›žå¡«â€å¼•æ“Žï¼ˆM2 å°è´¦å±‚,2026-09-09 æŽ¥å…¥ï¼‰ã€‚
 *
 * ç›®æ ‡ï¼šç”Ÿæˆä»»æ„æœˆä»½ã€Šé“路运输量汇总表(整本)》时不再依赖当月人工母版;
 * æ¯ç‰ˆåªå½“版式底稿,当月值按已验证口径从库现算写入(班线包车 2026-07 ä¸Žäººå·¥æ¯ç‰ˆ
 * 350 æ ¼ 0 å·®å¼‚、公交/出租/轨道轮渡同批 0 å·®å¼‚)。
 *
 * æœ¬ç‰ˆè¦†ç›– 4 å¼ å°è´¦é¡µï¼šç­çº¿åŒ…车、公交、出租、轨道轮渡;
 * ç½‘约车依赖《网约车订单及全省总量》拆分输入、货运页在 M3 æŽ¥å…¥ï¼ŒåŽç»­ç‰ˆæœ¬è¡¥å……。
 *
 * å†™å…¥è§„则:
 * 1) åªå†™â€œå¸‚州明细行”的 2026 å¹´ m æœˆå½“月值列(m æœˆå€¼åˆ— = ç¬¬ 2+2*(m-1) åˆ—,0 åŸºï¼Œå³ C èµ·ï¼‰ï¼›
 * 2) å…¨çœè¡Œï¼ˆ=17 å¸‚州求和公式)与跨页合成页均为公式,不动,Excel æ‰“开自动重算;
 * 3) 2025 åŒæœŸå‚照列保留母版缓存值(同比公式自动引用);
 * 4) æºæ•°æ®æ•´æœˆç¼ºå¤±æ—¶æ”¶é›†æç¤ºã€ä¸å›žå¡«ï¼›å¸‚州当月全为 0 æ—¶ä¿æŒæ¯ç‰ˆç©ºå•元格。
 */
@Service
public class SummaryWorkbookFiller {
    @Resource
    private MidCalc midCalc;
    @Resource
    private PaxCalc paxCalc;
    /** 2026 å¹´ 1 æœˆå½“月值列(0 åŸºï¼Œå³ C åˆ—) */
    private static final int FIRST_MONTH_COL = 2;
    /** å›žå¡« 1..keepMonths æœˆå°è´¦é¡µï¼Œè¿”回写入格数;缺数提示写入 problems */
    public int fillMonthlyLedger(XSSFWorkbook wb, int year, int keepMonths, List<String> problems) {
        if (wb == null || keepMonths <= 0) return 0;
        if (problems == null) problems = new ArrayList<>();
        String yearPrefix = year + "-";
        int filled = 0;
        filled += fillCityRows(wb, "班线包车", midCalc.banxianMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false);
        filled += fillCityRows(wb, "公交", paxCalc.busMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false);
        filled += fillCityRows(wb, "出租", paxCalc.taxiMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false);
        filled += fillTrackFerry(wb, paxCalc.railFerryMonthly(yearPrefix, keepMonths), year, keepMonths, problems);
        return filled;
    }
    /** é€šç”¨å°è´¦é¡µï¼ˆç­çº¿åŒ…车/公交/出租)回填:按“列 A å¸‚州名 + åˆ— B æŒ‡æ ‡æ–‡æ¡ˆâ€å®šä½è¡Œï¼Œä¸å†™æ­»è¡Œå· */
    private int fillCityRows(XSSFWorkbook wb, String sheetName,
                             Map<Integer, Map<String, double[]>> monthly,
                             int year, int keepMonths, List<String> problems, boolean wyc) {
        Sheet sh = wb.getSheet(sheetName);
        if (sh == null) {
            problems.add(sheetName + "页不存在(母版缺失该页),跳过自动回填");
            return 0;
        }
        int cap = monthColumnCapacity(sh);
        int mMax = Math.min(keepMonths, cap);
        if (mMax <= 0) {
            problems.add(sheetName + "页未识别到 2026 å¹´ç´¯è®¡åˆ—,跳过自动回填");
            return 0;
        }
        // 1) å¸‚州起始行(列 A ä¸ºè§„范市州名)
        Map<String, Integer> cityStart = new LinkedHashMap<>();
        for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
            Row row = sh.getRow(r);
            if (row == null) continue;
            String city = cityOf(row.getCell(0));
            if (city != null && !cityStart.containsKey(city)) cityStart.put(city, r);
        }
        if (cityStart.isEmpty()) {
            problems.add(sheetName + "页未识别到市州行,跳过自动回填");
            return 0;
        }
        List<Integer> starts = new ArrayList<>(cityStart.values());
        int filled = 0;
        List<String> missingMonths = new ArrayList<>();
        for (int m = 1; m <= mMax; m++) {
            if (!monthly.containsKey(m)) missingMonths.add(m + "月");
        }
        if (!missingMonths.isEmpty()) {
            problems.add(sheetName + "缺 " + year + " å¹´ " + String.join("、", missingMonths) + " æºæ•°æ®ï¼ˆæœªå›žå¡«ï¼‰");
        }
        for (int i = 0; i < starts.size(); i++) {
            String city = findCityByStart(sh, starts.get(i));
            if (city == null) continue;
            int end = (i + 1 < starts.size()) ? starts.get(i + 1) - 1 : sh.getLastRowNum();
            // 2) è¡Œå†…各指标所在行(B åˆ—文案 â†’ æŒ‡æ ‡ä½ 0..3)
            int[] metricRow = new int[]{-1, -1, -1, -1};
            for (int r = starts.get(i); r <= end; r++) {
                Row row = sh.getRow(r);
                if (row == null) continue;
                int idx = metricIndexOf(text(row.getCell(1)));
                if (idx >= 0 && metricRow[idx] < 0) metricRow[idx] = r;
            }
            if (metricRow[0] < 0 && metricRow[1] < 0) continue;
            for (int m = 1; m <= mMax; m++) {
                Map<String, double[]> mm = monthly.get(m);
                if (mm == null) continue;
                double[] arr = mm.get(city);
                if (arr == null || (arr[0] == 0.0 && arr[1] == 0.0)) continue;
                int col = FIRST_MONTH_COL + 2 * (m - 1);
                for (int idx = 0; idx < 4; idx++) {
                    if (metricRow[idx] < 0) continue;
                    double val = arr[idx];
                    if (val == 0.0) continue;
                    filled += setNumeric(sh, metricRow[idx], col, val);
                }
            }
        }
        return filled;
    }
    /** è½¨é“轮渡页(轨道=武汉/黄石;轮渡仅武汉)专用回填 */
    private int fillTrackFerry(XSSFWorkbook wb, Map<Integer, Map<String, double[]>> monthly,
                               int year, int keepMonths, List<String> problems) {
        String sheetName = "轨道、轮渡";
        Sheet sh = wb.getSheet(sheetName);
        if (sh == null) return 0;
        int cap = monthColumnCapacity(sh);
        int mMax = Math.min(keepMonths, cap);
        if (mMax <= 0) return 0;
        // è½¨é“块与轮渡块标题行(列 A æ–‡æ¡ˆï¼‰
        int trackTitle = -1, ferryTitle = -1;
        for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
            String a = text(sh.getRow(r) == null ? null : sh.getRow(r).getCell(0));
            if (a == null) continue;
            if (trackTitle < 0 && a.startsWith("轨道客运量")) trackTitle = r;
            else if (ferryTitle < 0 && a.startsWith("轮渡客运量")) ferryTitle = r;
        }
        if (trackTitle < 0 || ferryTitle < 0) {
            problems.add(sheetName + "页未识别到轨道/轮渡块标题,跳过自动回填");
            return 0;
        }
        int filled = 0;
        filled += fillFerryBlock(sh, monthly, trackTitle + 3, ferryTitle - 1, 0, mMax, problems, sheetName);
        filled += fillFerryBlock(sh, monthly, ferryTitle + 3, sh.getLastRowNum(), 2, mMax, problems, sheetName);
        return filled;
    }
    /** å¡«å……一段“标题行后数据区”:base ä¸ºè¯¥å—指标位基(轨道 0 / è½®æ¸¡ 2);block å†…
     *  æ¯å¸‚å·ž 2 è¡Œï¼šå®¢è¿é‡è¡Œï¼ˆåˆ— A=市州名)、旅客周转量行(A ç©ºï¼ŒB å«â€œå‘¨è½¬é‡â€ï¼‰ */
    private int fillFerryBlock(Sheet sh, Map<Integer, Map<String, double[]>> monthly,
                               int start, int end, int base, int mMax,
                               List<String> problems, String sheetName) {
        int filled = 0;
        // æ”¶é›†å¸‚州客运量行
        Map<String, Integer> paxRow = new LinkedHashMap<>();
        for (int r = start; r <= end; r++) {
            Row row = sh.getRow(r);
            if (row == null) continue;
            String city = cityOf(row.getCell(0));
            if (city != null) paxRow.put(city, r);
        }
        for (Map.Entry<String, Integer> e : paxRow.entrySet()) {
            String city = e.getKey();
            int rp = e.getValue();
            int rt = findTurnoverRow(sh, rp + 1, Math.min(end, rp + 6));
            if (rt < 0) continue;
            for (int m = 1; m <= mMax; m++) {
                Map<String, double[]> mm = monthly.get(m);
                if (mm == null) continue;
                double[] arr = mm.get(city);
                if (arr == null) continue;
                int col = FIRST_MONTH_COL + 2 * (m - 1);
                double pax = arr[base];
                double turn = arr[base + 1];
                if (pax != 0.0) filled += setNumeric(sh, rp, col, pax);
                if (turn != 0.0) filled += setNumeric(sh, rt, col, turn);
            }
        }
        return filled;
    }
    private int findTurnoverRow(Sheet sh, int from, int to) {
        for (int r = from; r <= to; r++) {
            Row row = sh.getRow(r);
            if (row == null) continue;
            String a = text(row.getCell(0));
            if (a != null && !a.trim().isEmpty()) return -1; // è¿›å…¥ä¸‹ä¸€å¸‚州块
            String b = text(row.getCell(1));
            if (b != null && b.contains("周转量")) return r;
        }
        return -1;
    }
    /** å†™æ•°å€¼ï¼šæ ¼ä¸å­˜åœ¨åˆ™å»ºæ ¼å¹¶å¤åˆ¶åŒè¡Œ 1 æœˆåˆ—样式;返回 1 */
    private int setNumeric(Sheet sh, int r, int col, double val) {
        Row row = sh.getRow(r);
        if (row == null) row = sh.createRow(r);
        Cell c = row.getCell(col);
        if (c == null) {
            c = row.createCell(col);
            Cell ref = row.getCell(FIRST_MONTH_COL);
            if (ref != null && ref.getCellStyle() != null) c.setCellStyle(ref.getCellStyle());
        }
        c.setCellValue(val);
        return 1;
    }
    /** è¡Œ B åˆ—文案 â†’ æŒ‡æ ‡ä½ï¼š0 å®¢è¿é‡ / 1 æ—…客周转量 / 2 å…¶ä¸­åŸŽå¸‚内客运量或其中个体客运量 / 3 å¯¹åº”周转量 */
    private int metricIndexOf(String b) {
        if (b == null) return -1;
        if (b.contains("其中个体旅客周转量") || b.contains("其中城市内旅客周转量")) return 3;
        if (b.contains("其中个体客运量") || b.contains("其中城市内客运量")) return 2;
        if (b.contains("旅客周转量")) return 1;
        if (b.contains("客运量")) return 0;
        return -1;
    }
    /** åˆ— A æ–‡æ¡ˆ â†’ è§„范市州名(湖北省/全省、非市州行返回 null) */
    private String cityOf(Cell a) {
        String t = text(a);
        if (t == null || t.trim().isEmpty()) return null;
        String norm = RegionUtil.normalizeCityName(t.trim());
        return RegionUtil.cityList().contains(norm) ? norm : null;
    }
    private String findCityByStart(Sheet sh, int startRow) {
        Row row = sh.getRow(startRow);
        return row == null ? null : cityOf(row.getCell(0));
    }
    /** æœˆä»½å®¹é‡ï¼šåœ¨è¡¨å¤´ï¼ˆå‰ 5 è¡Œï¼‰æ‰¾â€œ2026…累计”列,容量=(累计列-1 æœˆåˆ—)/2 */
    private int monthColumnCapacity(Sheet sh) {
        for (int r = 0; r <= 4 && r <= sh.getLastRowNum(); r++) {
            Row row = sh.getRow(r);
            if (row == null) continue;
            for (Cell c : row) {
                if (c == null) continue;
                String t = text(c);
                if (t != null && t.contains("2026") && t.contains("累计") && c.getColumnIndex() > FIRST_MONTH_COL) {
                    return (c.getColumnIndex() - FIRST_MONTH_COL) / 2;
                }
            }
        }
        return 7;
    }
    private String text(Cell c) {
        if (c == null) return null;
        switch (c.getCellType()) {
            case STRING: return c.getStringCellValue();
            case NUMERIC: return Double.toString(c.getNumericCellValue());
            default: return null;
        }
    }
}
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -29,6 +29,9 @@
import com.trafficaudit.dataimport.mapper.PassengerEnterpriseMonthlyMapper;
import com.trafficaudit.dataimport.mapper.PassengerIndividualMonthlyMapper;
import com.trafficaudit.dataimport.mapper.ScaleSplitTransportMapper;
import com.trafficaudit.reportexport.calc.MidCalc;
import com.trafficaudit.reportexport.calc.PaxCalc;
import com.trafficaudit.reportexport.calc.SummaryWorkbookFiller;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
@@ -92,6 +95,8 @@
    private CityBusMonthlyMapper cityBusMapper;
    @Resource
    private CityTaxiMonthlyMapper cityTaxiMapper;
    @Resource
    private SummaryWorkbookFiller summaryWorkbookFiller;
    @Resource
    private AuditResultMapper auditResultMapper;
    @Resource
@@ -3340,6 +3345,10 @@
            }
            dynamicSummaryYear(wb, year);
            cleanSummarySheetPresentation(wb); // å–消各页筛选、还原隐藏行(否则“班线包车”等页看起来像缺数据)
            int effMonth = (toMonth != null && toMonth > 0 && toMonth < month) ? toMonth : month;
            List<String> dbProblems = new ArrayList<>();
            int dbFilled = summaryWorkbookFiller.fillMonthlyLedger(wb, year, effMonth, dbProblems);
            log.info("汇总工作簿数据驱动回填完成:写入台账页当月值格数={},缺数提示={}", dbFilled, dbProblems);
            applyTwoDecimalFormat(wb); // æ•°å€¼æ˜¾ç¤ºä¸¤ä½å°æ•°ï¼ˆä¿ç•™å…¨ç²¾åº¦ï¼‰ï¼›% / æ—¥æœŸç­‰æ—¢æœ‰æ ·å¼ä¸æ”¹å˜
            if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr();
            wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // è¿˜åŽŸ/改写公式后打开即重算