| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.CellType; |
| | | import org.apache.poi.ss.usermodel.FormulaEvaluator; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.ss.util.CellRangeAddress; |
| | |
| | | if (sh == null || expanded.contains(sh.getSheetName())) continue; |
| | | shiftQualifiedRefs(sh, insertColOf); |
| | | } |
| | | recalcAndCache(wb); |
| | | int titleFixed = applyTitles(wb, year, targetMonth); |
| | | if (titleFixed > 0) { |
| | | log.info("扩列:标题按 {}年{}月 重写 {} 格", year, targetMonth, titleFixed); |
| | |
| | | /** 只把 7 个标题页的标题按给定期次重写(用于修正母版里滞后的标题)。 */ |
| | | public byte[] fixTitles(byte[] source, int year, int month) { |
| | | try (XSSFWorkbook wb = new XSSFWorkbook(new ByteArrayInputStream(source))) { |
| | | recalcAndCache(wb); |
| | | int n = applyTitles(wb, year, month); |
| | | wb.setForceFormulaRecalculation(true); |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | |
| | | return out.toByteArray(); |
| | | } catch (Exception e) { |
| | | throw new IllegalStateException("汇总大表标题修正失败: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** 整本重算并把结果写入缓存值:否则母版里只有公式没有值,后续读取会把空值当 0(2026-10-12 修) */ |
| | | private void recalcAndCache(XSSFWorkbook wb) { |
| | | try { |
| | | FormulaEvaluator ev = wb.getCreationHelper().createFormulaEvaluator(); |
| | | ev.clearAllCachedResultValues(); |
| | | ev.evaluateAll(); |
| | | } catch (Exception e) { |
| | | log.warn("扩列:整本重算未完成(公式仍在,Excel 打开会再算):{}", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | |
| | | if (headerRows.contains(r)) continue; |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | cloneDataCell(row, srcVal, insertCol, shiftable, insertCol); |
| | | cloneDataCell(row, srcYoy, insertCol + 1, shiftable, insertCol); |
| | | cloneDataCell(row, srcVal, insertCol, shiftable); |
| | | cloneYoyCell(row, srcYoy, insertCol + 1, insertCol, lastYearSameMonth + 2); |
| | | } |
| | | // 5) 累计列补当月项;累计同比分母补去年同月列 |
| | | int cumCol = insertCol + 2, cumYoyCol = insertCol + 3; |
| | |
| | | } |
| | | } |
| | | |
| | | private void cloneDataCell(Row row, int srcCol, int dstCol, Set<String> shiftable, int insertCol) { |
| | | private void cloneDataCell(Row row, int srcCol, int dstCol, Set<String> shiftable) { |
| | | Cell s = row.getCell(srcCol); |
| | | if (s == null) return; |
| | | Cell d = getOrCreate(row, dstCol); |
| | | d.setBlank(); |
| | | d.setCellStyle(s.getCellStyle()); |
| | | if (s.getCellType() == CellType.FORMULA) { |
| | | d.setCellFormula(shiftRefs(s.getCellFormula(), insertCol, shiftable)); |
| | | d.setCellFormula(shiftRefsAll(s.getCellFormula(), shiftable)); |
| | | } |
| | | } |
| | | |
| | | /** 当月同比列:不克隆上月公式,直接按「本月值 / 去年同月 − 1」重写(去年同月列已按表头定位) */ |
| | | private void cloneYoyCell(Row row, int srcCol, int dstCol, int valCol0, int denCol0) { |
| | | Cell s = row.getCell(srcCol); |
| | | if (s == null) return; |
| | | Cell d = getOrCreate(row, dstCol); |
| | | d.setBlank(); |
| | | d.setCellStyle(s.getCellStyle()); |
| | | if (s.getCellType() == CellType.FORMULA) { |
| | | int excelRow = row.getRowNum() + 1; |
| | | d.setCellFormula(colLetter(valCol0) + excelRow + "/" + colLetter(denCol0) + excelRow + "-1"); |
| | | } |
| | | } |
| | | |
| | | /** 相对复制用的平移:把本页/已扩列页的引用整体 +2(不做列号阈值判断) */ |
| | | private String shiftRefsAll(String formula, Set<String> shiftable) { |
| | | Matcher m = REF.matcher(formula); |
| | | StringBuffer sb = new StringBuffer(); |
| | | while (m.find()) { |
| | | String sheet = m.group(2) != null ? m.group(2) : m.group(3); |
| | | if ((sheet != null && !shiftable.contains(sheet)) || isFunctionName(formula, m)) { |
| | | m.appendReplacement(sb, Matcher.quoteReplacement(m.group(0))); |
| | | continue; |
| | | } |
| | | int colIdx = colIndex(m.group(5)); |
| | | m.appendReplacement(sb, Matcher.quoteReplacement(rebuild(m, sheet, colIdx + 2))); |
| | | } |
| | | m.appendTail(sb); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | private void shiftAllRefs(Sheet sh, int insertCol, Set<String> shiftable) { |
| | | for (int r = 0; r <= sh.getLastRowNum(); r++) { |
| | | Row row = sh.getRow(r); |