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ÔµÀ·ÔËÊäÁ¿»ã×ܱí.xlsxBinary 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 ç彿å¼/忝忏 空ãä¿ç表头ï¼