fix(汇总大表): 扩列时按母版期次重写 7 个标题页标题,并修正 8 月母版标题(待验收)
5个文件已修改
102 ■■■■■ 已修改文件
HANDOFF.md 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/生成汇总大表/2026年8月道路运输量汇总表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/layout/SummaryLayoutExpander.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HANDOFF.md
@@ -4,6 +4,20 @@
> **每天结束时**:把当天进展、踩过的坑、新需求、未完成事项更新到本文件,然后可以放心开新对话。
> **协作约定(2026-08-31)**:说「提交」= æœ¬åœ° commit + æŽ¨é€åˆ°è¿œç«¯ git(origin main,Gitblit),不再只做本地 commit。
## æœ€æ–°ï¼ˆ2026-10-12,zyj çº¿ï¼‰
### 10-12:扩列器补「标题按母版期次重写」+ ä¿®æ­£ 8 æœˆæ¯ç‰ˆæ ‡é¢˜ã€å¾…验收】
- **用户反馈**:9 æœˆç‰ˆå¼ä»¶ä¸Ž 8 æœˆæ¯ç‰ˆé‡Œï¼Œ7 ä¸ªé¡µç­¾çš„æ ‡é¢˜ä»æ˜¯ `1-7月 / 7月`(货运量排名、货运周转量排名、城市汇总、城市分市州、中口径排名、中口径分析、道路运输周转量)。
- **根因(两条)**:① æŒ‰å·²ç¡®è®¤å£å¾„è¿™ 7 é¡µæ˜¯ C ç±»é¡µâ€”—**标题由生成程序在出表时现写**(`ReportExportService :969 / :1011 / :2707 / :2891 / :4446 / :4498 / :4499 / :4604` å‡æŒ‰ period å†™ï¼‰ï¼Œæ¯ç‰ˆé‡Œçš„æ–‡å­—不参与出表,所以扩列器原本没动它们;② **我的实现确实漏了"标题 +1"这一步**——《扩列配置·核对版》动作 7 å†™äº†"标题:页面标题月份+1(逐页核对)",我只做了 8 ä¸ªå°è´¦é¡µçš„表头克隆。
- **修复**:扩列器新增 `applyTitles(...)`——按**母版期次**重写标题(累计=`1-M月`、当月=`M月`),只处理 7 ä¸ªæ ‡é¢˜é¡µã€**命中标题标记才改**(避免误伤 ` è´§è¿!A1` è¿™ç±»å¤‡æ³¨ï¼‰ï¼Œ4 ä½å¹´ä»½ä¸€å¹¶æŒ‰ç›®æ ‡å¹´é‡å†™ï¼›æ–°å¢žåªè¯»æŽ¥å£å‚æ•° `?titlesOnly=true`(只改标题不扩列)。
- **8 æœˆæ¯ç‰ˆå·²ä¿®æ­£**:7 é¡µæ ‡é¢˜ `1-7月/7月` â†’ **`1-8月/8月`**(修正前备份 `docs/生成汇总大表/_bak_2026å¹´8月母版_标题修正前_20261012.xlsx`);19 é¡µç­¾ä¸Žå…¬å¼æŠ½æŸ¥æ— æŸï¼ˆ`中口径明细!C5`、`公交!C5` å…¨çœè¡Œå…¬å¼ã€` è´§è¿!A1` å¤‡æ³¨å‡æ­£å¸¸ï¼‰ã€‚
- **9 æœˆç‰ˆå¼é‡è·‘**:7 é¡µæ ‡é¢˜ â†’ **`1-9月/9月`**;产出 `docs/生成汇总大表/输出/_扩列_2026å¹´9月_待确认_v2.xlsx`(1,037,071 B)。
- **与手工 9 æœˆæ¯ç‰ˆå¤æ¯”**:851 æ¡å·®å¼‚ = 8 ä¸ªå°è´¦é¡µ 836 æ¡ï¼ˆæ²¿ç”¨ 10-10 åˆ†ç±»ï¼šç´¯è®¡/分母是否扩到 9 æœˆ 548、分隔符 153、表名引号 130、浮点尾差 1)+ **标题页 15 æ¡**(手工件标题为滞后的 `1-8月/7月`)。
- **踩坑**:重跑时目标文件被 Excel/WPS å ç”¨ï¼ˆ`being used by another process`)→ å…ˆè½ `_v2` å‘½åï¼›ç”¨æˆ·å…³é—­åŽå¯ç”¨åŽŸåé‡å†™ã€‚
- **报告(本地、不入库)**:`docs/功能测试报告/功能测试报告_汇总大表扩列器标题修正_2026-10-12.md`;日结 `docs/工作日结/工作日结_2026-10-12.md`。
- **待用户验收/确认**:① æ ‡é¢˜å£å¾„按"**标题期次=母版期次**"(8月→1-8月/8月、9月→1-9月/9月)是否正确;② `_扩列_2026å¹´9月_待确认_v2.xlsx` æ˜¯å¦æ”¹åä¸ºæ­£å¼åï¼ˆåŽŸåé‚£ä»½æ˜¯æ—§å†…å®¹ã€è¢« WPS å­˜è¿‡ï¼Œæœªç»åŒæ„ä¸åŠ¨ï¼‰ï¼›â‘¢ ç´¯è®¡/累计同比"是否扩到 9 æœˆ"(产出=已扩、手工版未扩)。
- **状态**:**【待验收】**。
## æœ€æ–°ï¼ˆ2026-10-10,zyj çº¿ï¼‰
### 10-10:Step 1 æ‰©åˆ—器落地 + 8→9 æœˆç‰ˆå¼é¦–轮产出【待验收】
docs/Éú³É»ã×Ü´ó±í/2026Äê8ÔµÀ·ÔËÊäÁ¿»ã×ܱí.xlsx
Binary files differ
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java
@@ -91,7 +91,12 @@
    /** ç”Ÿæˆ_道路运输量汇总表.xlsx(汇总工作簿 P0-P1 éª¨æž¶ï¼šæ•´æœ¬å¤åˆ¶æ¯ç‰ˆï¼Œé¡µç­¾/公式/2025 ç¼“存保留) */
    /** æ±‡æ€»å¤§è¡¨æŒ‰æœˆæ‰©åˆ—(Step 1,只做版式、数据留空):返回下一月版式 xlsx。 */
    @GetMapping("/export/summaryWorkbook/expand")
    public ResponseEntity<byte[]> expandSummaryWorkbook(@RequestParam("period") String period) throws Exception {
    public ResponseEntity<byte[]> expandSummaryWorkbook(@RequestParam("period") String period,
                                                        @RequestParam(value = "titlesOnly", defaultValue = "false") boolean titlesOnly) throws Exception {
        if (titlesOnly) {
            byte[] titled = reportService.fixSummaryTitles(period);
            return buildResponse(titled, String.format("标题修正_道路运输量汇总表_%s.xlsx", period));
        }
        byte[] data = reportService.expandSummaryWorkbook(period);
        return buildResponse(data, String.format("扩列_道路运输量汇总表_%s_待确认.xlsx", period));
    }
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/layout/SummaryLayoutExpander.java
@@ -41,6 +41,19 @@
    private static final int HEADER_SCAN_ROWS = 15;
    /** æ ‡é¢˜ç”±ç¨‹åºåœ¨å‡ºè¡¨æ—¶çŽ°å†™çš„ 7 ä¸ªé¡µç­¾ï¼ˆæ¯ç‰ˆé‡Œçš„æ ‡é¢˜æ–‡å­—需按母版期次重写)。 */
    private static final List<String> TITLE_PAGES = Arrays.asList(
            "货运量排名", "货运周转量排名", "城市汇总", "城市分市州", "中口径排名", "中口径分析", "道路运输周转量");
    /** æ ‡é¢˜è¯†åˆ«æ ‡è®°ï¼ˆå‘½ä¸­å…¶ä¸€æ‰æ”¹å†™ï¼Œé¿å…è¯¯ä¼¤å¤‡æ³¨ç­‰æ–‡å­—)。 */
    private static final List<String> TITLE_MARKERS = Arrays.asList(
            "全省分市州累计完成公路货运量情况", "全省分市州完成公路货运量情况",
            "全省分市州累计完成公路货物运输周转量情况", "全省分市州完成公路货物运输周转量情况",
            "全省累计完成城市客运量情况", "全省完成城市客运量情况",
            "分市州1-", "全省分市州累计完成道路客运生产情况", "全省分市州完成道路客运生产情况",
            "中口径客运量及周转量",
            "全省累计完成交通周转量情况", "全省完成交通周转量情况");
    /** å…¬å¼é‡Œçš„ A1 å¼•用(可带工作表前缀)。 */
    private static final Pattern REF = Pattern.compile(
            "(?:('([^']+)'|([A-Za-z_\\u4e00-\\u9fff][A-Za-z0-9_.\\u4e00-\\u9fff\u3001]*))!)?(\\$?)([A-Z]{1,3})(\\$?)(\\d+)");
@@ -67,6 +80,10 @@
                if (sh == null || expanded.contains(sh.getSheetName())) continue;
                shiftQualifiedRefs(sh, insertColOf);
            }
            int titleFixed = applyTitles(wb, year, targetMonth);
            if (titleFixed > 0) {
                log.info("扩列:标题按 {}å¹´{}月 é‡å†™ {} æ ¼", year, targetMonth, titleFixed);
            }
            wb.setForceFormulaRecalculation(true);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            wb.write(out);
@@ -76,6 +93,54 @@
        }
    }
    /** åªæŠŠ 7 ä¸ªæ ‡é¢˜é¡µçš„æ ‡é¢˜æŒ‰ç»™å®šæœŸæ¬¡é‡å†™ï¼ˆç”¨äºŽä¿®æ­£æ¯ç‰ˆé‡Œæ»žåŽçš„æ ‡é¢˜ï¼‰ã€‚ */
    public byte[] fixTitles(byte[] source, int year, int month) {
        try (XSSFWorkbook wb = new XSSFWorkbook(new ByteArrayInputStream(source))) {
            int n = applyTitles(wb, year, month);
            wb.setForceFormulaRecalculation(true);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            wb.write(out);
            log.info("汇总大表标题修正完成:{}å¹´{}月,改写 {} æ ¼", year, month, n);
            return out.toByteArray();
        } catch (Exception e) {
            throw new IllegalStateException("汇总大表标题修正失败: " + e.getMessage(), e);
        }
    }
    /** æŠŠ 7 ä¸ªé¡µç­¾çš„æ ‡é¢˜æ–‡å­—按「累计=1-M月、当月=M月」重写;命中标记才改,避免误伤备注。 */
    private int applyTitles(XSSFWorkbook wb, int year, int month) {
        int changed = 0;
        for (String page : TITLE_PAGES) {
            Sheet sh = wb.getSheet(page);
            if (sh == null) continue;
            int lastRow = Math.min(sh.getLastRowNum(), 40);
            for (int r = 0; r <= lastRow; r++) {
                Row row = sh.getRow(r);
                if (row == null) continue;
                for (int c = 0; c <= 1; c++) {
                    Cell cell = row.getCell(c);
                    if (cell == null || cell.getCellType() != CellType.STRING) continue;
                    String t = cell.getStringCellValue();
                    if (t == null || !looksLikeTitle(t)) continue;
                    String nt = t.replaceAll("(\\d{4})å¹´", year + "å¹´")
                                 .replaceAll("(\\d{1,2})月", month + "月");
                    if (!nt.equals(t)) {
                        cell.setCellValue(nt);
                        changed++;
                    }
                }
            }
        }
        return changed;
    }
    private boolean looksLikeTitle(String t) {
        for (String m : TITLE_MARKERS) {
            if (t.contains(m)) return true;
        }
        return false;
    }
    /** å¯¹å•页扩列,返回插入位置(0 åŸºåˆ—号)。 */
    private int expandSheet(Sheet sh, int year, int targetMonth) {
        int lastMonth = targetMonth - 1;
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -3649,6 +3649,22 @@
        }
    }
    /** åªä¿®æ­£å½“期母版里 7 ä¸ªæ ‡é¢˜é¡µçš„æ ‡é¢˜æ–‡å­—(按母版期次重写,用于修复滞后标题)。 */
    public byte[] fixSummaryTitles(String period) throws Exception {
        if (period == null || !period.contains("-")) {
            throw new RuntimeException("标题修正仅支持按月,请指定 period=YYYY-MM");
        }
        int year = Integer.parseInt(period.split("-")[0]);
        int month = monthOf(period, "month");
        File mother = resolveSummaryMother(period, "month");
        if (mother == null) {
            throw new RuntimeException("未找到汇总表母版(docs/生成汇总大表):" + year + "å¹´" + month + "月道路运输量汇总表.xlsx");
        }
        try (InputStream in = new FileInputStream(mother)) {
            return summaryLayoutExpander.fixTitles(org.apache.poi.util.IOUtils.toByteArray(in), year, month);
        }
    }
    /** ç”Ÿæˆ_道路运输量汇总表.xlsx(P0-P1 éª¨æž¶ï¼Œæ”¯æŒ toMonth è£å‰ªï¼‰ï¼š
     *  ä»¥ã€ŠYYYYå¹´M月道路运输量汇总表.xlsx》(M ç”± period å†³å®šï¼‰ä¸ºæ¯ç‰ˆï¼ŒæŒ‰åŒæœŸå¤‡ä»½æ¯ç‰ˆè¿˜åŽŸæœˆåº¦å—ï¼›
     *  toMonth éžç©ºä¸”小于 M æ—¶ï¼ŒæŠŠ 8 ä¸ªå°è´¦/合成页裁到 toMonth æœˆï¼ˆtoMonth+1..M çš„当月值/同比列清空、保留表头,