From 6dbb7ce5032bd5f82a6833f00c2753edb6339270 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期一, 28 九月 2026 16:35:47 +0800
Subject: [PATCH] docs(比对工具): 数字/non-numeric 文本差异单列『需处理(非数值文本)』(如手工反引号),不再混入其它
---
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java | 659 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 651 insertions(+), 8 deletions(-)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
index 435309b..5fc7b11 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java
@@ -1,10 +1,14 @@
package com.trafficaudit.reportexport.calc;
import com.trafficaudit.common.util.RegionUtil;
+import com.trafficaudit.reportexport.calc.FreightCalc.FreightMatrix;
+import com.trafficaudit.reportexport.calc.FreightCalc.FreightMetrics;
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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -32,16 +36,22 @@
@Service
public class SummaryWorkbookFiller {
+ private static final Logger log = LoggerFactory.getLogger(SummaryWorkbookFiller.class);
+
@Resource
private MidCalc midCalc;
@Resource
private PaxCalc paxCalc;
+ @Resource
+ private FreightCalc freightCalc;
+ @Resource
+ private WycSplitCalc wycSplitCalc;
/** 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) {
+ public int fillMonthlyLedger(XSSFWorkbook wb, String period, int year, int keepMonths, List<String> problems) {
if (wb == null || keepMonths <= 0) return 0;
if (problems == null) problems = new ArrayList<>();
String yearPrefix = year + "-";
@@ -50,6 +60,8 @@
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);
+ filled += fillFreightSheet(wb, period, keepMonths, problems);
+ filled += fillWycSheet(wb, period, keepMonths, problems);
return filled;
}
@@ -117,6 +129,177 @@
}
}
return filled;
+ }
+
+ /** 璐ц繍椤靛洖濉細鍏ㄧ渷鍧� + 17 甯傚窞鍧楃殑 瑙勪笂/瑙勪笅/鍚堣 脳 璐ц繍閲忋�佸懆杞噺锛堥�愭湀鐜扮畻锛涘綋鏈堝�煎垪=2+2*(m-1)锛� */
+ private int fillFreightSheet(XSSFWorkbook wb, String period, int keepMonths, List<String> problems) {
+ Sheet sh = wb.getSheet(" 璐ц繍");
+ if (sh == null) sh = wb.getSheet("璐ц繍");
+ if (sh == null) {
+ problems.add("璐ц繍椤典笉瀛樺湪锛堟瘝鐗堢己澶辫椤碉級锛岃烦杩囪嚜鍔ㄥ洖濉�");
+ return 0;
+ }
+ int cap = monthColumnCapacity(sh);
+ int mMax = Math.min(keepMonths, cap);
+ if (mMax <= 0) {
+ problems.add("璐ц繍椤垫湭璇嗗埆鍒� 2026 骞寸疮璁″垪锛岃烦杩囪嚜鍔ㄥ洖濉�");
+ return 0;
+ }
+ int year = periodYear(period);
+ // 鍧楋紙鍏ㄧ渷/甯傚窞锛夛細鎸囨爣閿� -> 琛屽彿
+ Map<String, Map<String, Integer>> blockRows = new LinkedHashMap<>();
+ String cur = null;
+ for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ String a = text(row.getCell(0));
+ if (a != null && !a.trim().isEmpty()) {
+ String norm = RegionUtil.normalizeCityName(a.trim());
+ if (FreightCalc.PROVINCE.equals(norm) || RegionUtil.cityList().contains(norm)) {
+ cur = norm;
+ blockRows.computeIfAbsent(cur, k -> new LinkedHashMap<>());
+ } else {
+ cur = null; // 鏍囬琛� / 闈炲叏鐪侀潪甯傚窞 -> 鑴辩鍧�
+ }
+ }
+ if (cur == null) continue;
+ String key = freightMetricKey(text(row.getCell(1)));
+ if (key == null) continue;
+ blockRows.get(cur).putIfAbsent(key, r);
+ }
+ if (blockRows.isEmpty()) {
+ problems.add("璐ц繍椤垫湭璇嗗埆鍒板叏鐪�/甯傚窞琛岋紝璺宠繃鑷姩鍥炲~");
+ return 0;
+ }
+ int filled = 0;
+ // 鍙洖濉洰鏍囨湡褰撴湀锛�1..鐩爣鏈�-1 鐨勫�间互姣嶇増锛堝惈鍚屾湡澶囦唤姣嶇増杩樺師锛変负鍑嗭紝閬垮厤鏀瑰啓宸茬‘璁ゅ彛寰�
+ int targetM = Math.min(periodMonth(period), mMax);
+ for (int m = targetM; m <= targetM; m++) {
+ String mp = String.format("%04d-%02d", year, m);
+ FreightMatrix mx;
+ try {
+ mx = freightCalc.calc(mp);
+ } catch (Exception e) {
+ problems.add("璐ц繍椤� " + mp + " 鍙栨暟澶辫触锛�" + e.getMessage());
+ continue;
+ }
+ int col = FIRST_MONTH_COL + 2 * (m - 1);
+ for (Map.Entry<String, Map<String, Integer>> e : blockRows.entrySet()) {
+ FreightMetrics fm = mx.getMonth().get(e.getKey());
+ if (fm == null) continue;
+ Map<String, Integer> rows = e.getValue();
+ // 1 鏈堝垪鏄叕寮忕殑鈥滄淳鐢熻鈥濓紙鍏ㄧ渷鍚堣 璐ц繍閲�/鍛ㄨ浆閲忋�佸競宸� 瑙勪笂+瑙勪笅鍛ㄨ浆閲忥級涓嶅啓鍊硷紝
+ // 鐢� ensureMonthFormulas 鎶婂叕寮忔寜鏈堝彸绉伙紝淇濇寔涓庢瘝鐗堜竴鑷�
+ filled += setFreightCell(sh, skipIfDerived(sh, rows.get("totalFreight")), col, fm.getTotalFreight());
+ filled += setFreightCell(sh, skipIfDerived(sh, rows.get("aboveFreight")), col, fm.getAboveFreight());
+ filled += setFreightCell(sh, skipIfDerived(sh, rows.get("belowFreight")), col, fm.getBelowFreight());
+ filled += setFreightCell(sh, skipIfDerived(sh, rows.get("totalTurnover")), col, fm.getTotalTurnover());
+ filled += setFreightCell(sh, skipIfDerived(sh, rows.get("aboveTurnover")), col, fm.getAboveTurnover());
+ filled += setFreightCell(sh, skipIfDerived(sh, rows.get("belowTurnover")), col, fm.getBelowTurnover());
+ }
+ }
+ return filled;
+ }
+
+ /** 璇ヨ 1 鏈堝垪锛圕锛夋槸鍏紡 -> 娲剧敓琛岋紝杩斿洖 null 琛ㄧず涓嶅洖濉暟鍊� */
+ private Integer skipIfDerived(Sheet sh, Integer r) {
+ if (r == null) return null;
+ Row row = sh.getRow(r);
+ if (row == null) return r;
+ Cell c = row.getCell(FIRST_MONTH_COL);
+ if (c != null && c.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) return null;
+ return r;
+ }
+
+ private int setFreightCell(Sheet sh, Integer r, int col, Double v) {
+ if (r == null || v == null) return 0;
+ return setNumeric(sh, r, col, v);
+ }
+
+ /** 璐ц繍椤� B 鍒楁寚鏍囨枃妗� -> 鎸囨爣閿紙瑙勪笂+瑙勪笅 / 瑙勪笂 / 瑙勪笅 脳 璐ц繍閲� / 鍛ㄨ浆閲忥級 */
+ private String freightMetricKey(String b) {
+ if (b == null) return null;
+ String s = b.replaceAll("\\s+", "");
+ if (s.isEmpty()) return null;
+ boolean turnover = s.contains("鍛ㄨ浆閲�");
+ if (s.contains("瑙勪笂+瑙勪笅")) return turnover ? "totalTurnover" : "totalFreight";
+ if (s.contains("瑙勪笂")) return turnover ? "aboveTurnover" : "aboveFreight";
+ if (s.contains("瑙勪笅")) return turnover ? "belowTurnover" : "belowFreight";
+ return turnover ? "totalTurnover" : "totalFreight"; // 鍏ㄧ渷鍧楃殑鈥滆揣杩愰噺/璐х墿鍛ㄨ浆閲忊��= 鍚堣
+ }
+
+ /** 缃戠害杞﹂〉鍥炲~锛�17 甯傚窞 鐨� 瀹㈣繍閲�/鍛ㄨ浆閲�/鍏朵腑鍩庡競鍐呭杩愰噺/鍏朵腑鍩庡競鍐呭懆杞噺锛堥�愭湀鐜扮畻锛�
+ * 鍏ㄧ渷琛岋紙=17 甯傚窞姹傚拰鍏紡锛夌敱 ensureMonthFormulas 鎸夋湀鎵╁垪锛屼笉鍦ㄦ澶勫啓鍊笺�� */
+ private int fillWycSheet(XSSFWorkbook wb, String period, int keepMonths, List<String> problems) {
+ Sheet sh = wb.getSheet("缃戠害杞�");
+ if (sh == null) {
+ problems.add("缃戠害杞﹂〉涓嶅瓨鍦紙姣嶇増缂哄け璇ラ〉锛夛紝璺宠繃鑷姩鍥炲~");
+ return 0;
+ }
+ int cap = monthColumnCapacity(sh);
+ int mMax = Math.min(keepMonths, cap);
+ if (mMax <= 0) {
+ problems.add("缃戠害杞﹂〉鏈瘑鍒埌 2026 骞寸疮璁″垪锛岃烦杩囪嚜鍔ㄥ洖濉�");
+ return 0;
+ }
+ int year = periodYear(period);
+ Map<String, Map<String, Integer>> cityMetricRows = new LinkedHashMap<>();
+ String cur = null;
+ for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ String a = text(row.getCell(0));
+ if (a != null && !a.trim().isEmpty()) {
+ String norm = RegionUtil.normalizeCityName(a.trim());
+ cur = RegionUtil.cityList().contains(norm) ? norm : null;
+ if (cur != null) cityMetricRows.computeIfAbsent(cur, k -> new LinkedHashMap<>());
+ }
+ if (cur == null) continue;
+ String key = wycMetricKey(text(row.getCell(1)));
+ if (key == null) continue;
+ cityMetricRows.get(cur).putIfAbsent(key, r);
+ }
+ if (cityMetricRows.isEmpty()) {
+ problems.add("缃戠害杞﹂〉鏈瘑鍒埌甯傚窞琛岋紝璺宠繃鑷姩鍥炲~");
+ return 0;
+ }
+ int filled = 0;
+ // 鍙洖濉洰鏍囨湡褰撴湀锛堝巻鍙叉湀浠ユ瘝鐗�/鍘嗗彶鐢熸垚浠朵负鍑嗭級
+ int targetM = Math.min(periodMonth(period), mMax);
+ for (int m = targetM; m <= targetM; m++) {
+ String mp = String.format("%04d-%02d", year, m);
+ WycSplitCalc.WycResult wr;
+ try {
+ wr = wycSplitCalc.calc(mp);
+ } catch (Exception e) {
+ if (m == mMax) {
+ problems.add("缃戠害杞﹂〉 " + mp + " 婧愭暟鎹己澶憋紝褰撴湀鏈洖濉細" + e.getMessage());
+ }
+ continue;
+ }
+ int col = FIRST_MONTH_COL + 2 * (m - 1);
+ for (Map.Entry<String, Map<String, Integer>> e : cityMetricRows.entrySet()) {
+ WycSplitCalc.WycMetrics wm = wr.getByCity().get(e.getKey());
+ if (wm == null) continue;
+ Map<String, Integer> rows = e.getValue();
+ filled += setFreightCell(sh, rows.get("pax"), col, wm.getTotalPax());
+ filled += setFreightCell(sh, rows.get("turnover"), col, wm.getTotalTurnover());
+ filled += setFreightCell(sh, rows.get("cityPax"), col, wm.getCityPax());
+ filled += setFreightCell(sh, rows.get("cityTurnover"), col, wm.getCityTurnover());
+ }
+ }
+ return filled;
+ }
+
+ /** 缃戠害杞﹂〉 B 鍒楁寚鏍囨枃妗� -> 鎸囨爣閿� */
+ private String wycMetricKey(String b) {
+ if (b == null) return null;
+ String s = b.replaceAll("\\s+", "");
+ if (s.isEmpty()) return null;
+ boolean city = s.contains("鍩庡競鍐�");
+ boolean turnover = s.contains("鍛ㄨ浆閲�");
+ if (city) return turnover ? "cityTurnover" : "cityPax";
+ return turnover ? "turnover" : "pax";
}
/** 杞ㄩ亾杞浮椤碉紙杞ㄩ亾=姝︽眽/榛勭煶锛涜疆娓′粎姝︽眽锛変笓鐢ㄥ洖濉� */
@@ -192,15 +375,27 @@
return -1;
}
- /** 鍐欐暟鍊硷細鏍间笉瀛樺湪鍒欏缓鏍煎苟澶嶅埗鍚岃 1 鏈堝垪鏍峰紡锛涜繑鍥� 1 */
+ /** 鍐欐暟鍊硷細鏍间笉瀛樺湪鍒欏缓鏍硷紱鏍煎甫鏃ユ湡鏍峰紡锛堝鍏氦/鍑虹 8 鏈堝垪缁ф壙浜嗚〃澶粹�測yyy骞磎鏈堚�濓級鏃舵敼鍥炲悓琛� 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.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) {
+ return 0; // 姣嶇増鏈潵灏遍潬鍏紡绠楃殑琛岋紙鍚堣/鍚岄〉姹傚拰锛変繚鎸佸叕寮忥紝浜� Excel 閲嶇畻
+ }
+ Cell ref = row.getCell(FIRST_MONTH_COL);
+ boolean needStyle = false;
if (c == null) {
c = row.createCell(col);
- Cell ref = row.getCell(FIRST_MONTH_COL);
- if (ref != null && ref.getCellStyle() != null) c.setCellStyle(ref.getCellStyle());
+ needStyle = true;
+ } else if (c.getCellType() == org.apache.poi.ss.usermodel.CellType.BLANK
+ || (c.getCellType() == org.apache.poi.ss.usermodel.CellType.NUMERIC
+ && org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(c))) {
+ // 姣嶇増鎶� 8 鏈堝垪鐨勮〃澶存棩鏈熸牸寮忓甫鍒颁簡鏁版嵁鏍硷紙鎴栨湁鏍煎紡鏃犲�硷級锛屽啓鍊煎墠鏀瑰洖鍚岃 1 鏈堝垪鐨勬暟瀛楁牸寮�
+ needStyle = true;
+ }
+ if (needStyle && ref != null && ref.getCellStyle() != null) {
+ c.setCellStyle(ref.getCellStyle());
}
c.setCellValue(val);
return 1;
@@ -229,20 +424,468 @@
return row == null ? null : cityOf(row.getCell(0));
}
- /** 鏈堜唤瀹归噺锛氬湪琛ㄥご锛堝墠 5 琛岋級鎵锯��2026鈥︾疮璁♀�濆垪锛屽閲�=(绱鍒�-1 鏈堝垪)/2 */
+ /**
+ * 涓虹 month 鏈堣ˉ鈥滃綋鏈堝�尖�濆垪鍏紡锛氬彇鍚屼竴琛屾渶杩戜竴涓湀浠嶄负鍏紡鐨勫�煎垪锛屾暣鍒楀彸绉昏繃鏉�
+ * 锛堝鍏氦 7 鏈堝垪 O 鐨� =O9+O13+... -> 8 鏈堝垪 Q 鐨� =Q9+Q13+...锛夈��
+ * 鍙ˉ绌哄崟鍏冩牸锛涘凡鏈夋暟鍊硷紙璐ц繍鍏ㄧ渷鍧�/甯傚窞鍧楃敱鏁版嵁鍥炲~锛夋垨宸叉湁鍏紡鐨勬牸涓嶅姩銆�
+ */
+ public int ensureMonthFormulas(XSSFWorkbook wb, int year, int month) {
+ 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 || !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;
+ if (hasContent(row.getCell(targetCol))) continue;
+ Cell srcCell = null;
+ int srcMonth = -1;
+ for (int s = month - 1; s >= 1; s--) {
+ Cell c = row.getCell(FIRST_MONTH_COL + 2 * (s - 1));
+ if (c != null && c.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) {
+ srcCell = c;
+ srcMonth = s;
+ break;
+ }
+ }
+ 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);
+ if (target == null) target = row.createCell(targetCol);
+ if (srcCell.getCellStyle() != null) target.setCellStyle(srcCell.getCellStyle());
+ target.setCellFormula(shiftFormulaColumns(f, 2 * (month - srcMonth)));
+ 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);
+ }
+
+ /** 璇ユ牸鏄惁宸叉湁鍐呭锛堟暟鍊�/鏂囨湰/鍏紡锛夛紱浠呭甫鏍峰紡鐨勭┖澹筹紙濡傛瘝鐗堥噷缁ф壙琛ㄥご鏃ユ湡鏍煎紡鐨勬牸瀛愶級瑙嗕负绌� */
+ private boolean hasContent(Cell c) {
+ if (c == null) return false;
+ switch (c.getCellType()) {
+ case BLANK: return false;
+ case STRING: return c.getStringCellValue() != null && !c.getStringCellValue().trim().isEmpty();
+ default: return true;
+ }
+ }
+
+ /** 0 鍩哄垪鍙� + 1 鍩鸿鍙� -> "Q5" */
+ private String cellRef(int col0, int row1) {
+ StringBuilder sb = new StringBuilder();
+ int n = col0 + 1;
+ while (n > 0) {
+ int rem = (n - 1) % 26;
+ sb.insert(0, (char) ('A' + rem));
+ n = (n - 1) / 26;
+ }
+ return sb.toString() + row1;
+ }
+
+ /** 浠� "2026-08" 鍙栨湀浠斤紙瑙f瀽澶辫触杩斿洖 1锛� */
+ private int periodMonth(String period) {
+ if (period != null && period.length() >= 7) {
+ try { return Integer.parseInt(period.substring(5, 7)); } catch (Exception ignore) { }
+ }
+ return 1;
+ }
+
+ /** 浠� "2026-08" 鍙栧勾浠� */
+ private int periodYear(String period) {
+ if (period != null && period.length() >= 4) {
+ try { return Integer.parseInt(period.substring(0, 4)); } catch (Exception ignore) { }
+ }
+ return 0;
+ }
+
+ /**
+ * 涓虹 m 鏈堣ˉ鈥滀笌鍘诲勾鍚屾瘮鈥濆叕寮忥細鎶婄 m-1 鏈堝悓姣斿垪鐨勫叕寮忔暣浣撳彸绉� 2 鍒�
+ * 锛堝鍏氦 7 鏈� P5 鍏紡 =O5/AH5-1 -> 8 鏈� R5 =Q5/AJ5-1锛夛紝
+ * 鍥犳鍚屾瘮鍒嗘瘝鍙栫殑灏辨槸姹囨�昏〃 2025 骞村悓鏈堝垪锛堢敤鎴峰彛寰勶級銆�
+ */
+ public int ensureMonthYoyFormulas(XSSFWorkbook wb, int year, int month) {
+ if (wb == null || month <= 1) return 0;
+ int valueCol = FIRST_MONTH_COL + 2 * (month - 1);
+ int yoyCol = valueCol + 1;
+ int prevYoyCol = yoyCol - 2;
+ int written = 0;
+ for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+ Sheet sh = wb.getSheetAt(i);
+ if (sh == null) continue;
+ if (!isMonthLedgerSheet(sh.getSheetName())) continue;
+ for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ Cell prev = row.getCell(prevYoyCol);
+ if (prev == null || prev.getCellType() != org.apache.poi.ss.usermodel.CellType.FORMULA) continue;
+ Cell target = row.getCell(yoyCol);
+ if (target != null && target.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) continue;
+ String f = prev.getCellFormula();
+ if (f == null || f.isEmpty()) continue;
+ String shifted = shiftFormulaToNextMonth(sh, f, year, month);
+ if (target == null) target = row.createCell(yoyCol);
+ // 姣嶇増鎶婃柊鏈堜唤鐨勫悓姣斿垪鍋氭垚浜嗘櫘閫氭暟鍊兼牸寮忥紙鏄剧ず鎴愬皬鏁帮級锛岀粺涓�娌跨敤涓婃湀鍚屾瘮鍒楃殑鏍峰紡锛堢櫨鍒嗘暟锛�
+ if (prev.getCellStyle() != null) target.setCellStyle(prev.getCellStyle());
+ try {
+ target.setCellFormula(shifted);
+ written++;
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format(
+ "鍚屾瘮鍏紡琛ュ啓澶辫触锛歴heet=%s cell=%s 鍘熷紡=%s 鐩爣寮�=%s",
+ sh.getSheetName(), cellRef(yoyCol, r + 1), f, shifted), e);
+ }
+ }
+ }
+ return written;
+ }
+
+ /** 闅忔湀鎵╁垪鐨勯暱琛ㄩ〉锛堣〃澶村湪 1..3 琛屻�佹湀浠界粍涓衡�滃綋鏈堝��+鍚屾瘮鈥濅氦鏇匡級 */
+ private boolean isMonthLedgerSheet(String name) {
+ if (name == null) return false;
+ String n = name.trim();
+ return "璐ц繍".equals(n) || "鍏矾鎬诲杩�".equals(n) || "鐝嚎鍖呰溅".equals(n) || "鍩庡競瀹㈣繍".equals(n)
+ || "鍏氦".equals(n) || "鍑虹".equals(n) || "缃戠害杞�".equals(n)
+ || "杞ㄩ亾銆佽疆娓�".equals(n) || "杞ㄩ亾杞浮".equals(n)
+ || "涓彛寰勬槑缁�".equals(n) || "涓彛寰勫杩愰噺".equals(n);
+ }
+
+ /** 鍏紡鍐呮墍鏈� A1 褰㈠紡鐨勫垪寮曠敤鍙崇Щ delta 鍒楋紙SUM 绛夊嚱鏁板悕鍚庢棤鏁板瓧锛屼笉浼氳鍖归厤锛� */
+ private String shiftFormulaColumns(String formula, int delta) {
+ java.util.regex.Matcher m = java.util.regex.Pattern
+ .compile("(?<![A-Za-z0-9_$])([$]?)([A-Z]{1,3})([$]?)([0-9]+)")
+ .matcher(formula);
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ String col = m.group(2);
+ int idx = 0;
+ for (int i = 0; i < col.length(); i++) idx = idx * 26 + (col.charAt(i) - 'A' + 1);
+ int nidx = idx + delta;
+ StringBuilder nc = new StringBuilder();
+ while (nidx > 0) {
+ int rem = (nidx - 1) % 26;
+ nc.insert(0, (char) ('A' + rem));
+ nidx = (nidx - 1) / 26;
+ }
+ m.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(m.group(1) + nc + m.group(3) + m.group(4)));
+ }
+ m.appendTail(sb);
+ return sb.toString();
+ }
+
+ /** 鍏紡鍐� A1 鍒楀紩鐢ㄥ尮閰嶏紙SUM 绛夊嚱鏁板悕鍚庢棤鏁板瓧锛屼笉浼氳鍖归厤锛� */
+ private static final java.util.regex.Pattern A1_REF =
+ java.util.regex.Pattern.compile("(?<![A-Za-z0-9_$])([$]?)([A-Z]{1,3})([$]?)([0-9]+)");
+ /** 鈥�2025骞�8鏈堚�� 杩欑鍗曟湀琛ㄥご */
+ private static final java.util.regex.Pattern YM_HEADER =
+ java.util.regex.Pattern.compile("^(\\d{4})骞�(\\d{1,2})鏈�$");
+ /** 鈥�2025骞�1-7鏈堢疮璁♀�� 杩欑鍖洪棿绱杈呭姪鍒楋紙涓嶅睘浜庝换浣曞崟鏈堬級 */
+ private static final java.util.regex.Pattern YM_RANGE_CUM =
+ java.util.regex.Pattern.compile("^\\d{4}骞碶\d{1,2}-\\d{1,2}鏈堢疮璁�$");
+
+ /** A1 鍒楀瓧姣� -> 0 鍩哄垪鍙凤紙涓� POI 鐨� Cell.getColumnIndex() 瀵归綈锛孉=0銆丄J=35锛� */
+ private int colIndexOf(String letters) {
+ int idx = 0;
+ for (int i = 0; i < letters.length(); i++) idx = idx * 26 + (letters.charAt(i) - 'A' + 1);
+ return idx - 1;
+ }
+
+ /** 0 鍩哄垪鍙� -> "Q" */
+ private String colLettersOf(int idx0) {
+ StringBuilder sb = new StringBuilder();
+ int n = idx0 + 1;
+ while (n > 0) {
+ int rem = (n - 1) % 26;
+ sb.insert(0, (char) ('A' + rem));
+ n = (n - 1) / 26;
+ }
+ return sb.toString();
+ }
+
+ /** 琛ㄥご锛堝墠 5 琛岋級寤虹珛 (骞�*100+鏈�) -> 鍒楀彿锛�0 鍩猴級锛氳瘑鍒� 鈥�2025骞�8鏈堚�� 鏂囨湰鏍间笌 2025-08-01 杩欑被鏃ユ湡鏍硷紱
+ * 鍚屼竴 (骞�,鏈�) 鍙栨渶宸︿竴鍒楋紱鈥�2025骞�1-7鏈堢疮璁♀�� 杩欑被鍖洪棿绱杈呭姪鍒椾笉灞炰簬浠讳綍鍗曟湀锛屼笉鍙備笌鏄犲皠 */
+ private Map<Integer, Integer> yearMonthCols(Sheet sh, int year) {
+ Map<Integer, Integer> m = new LinkedHashMap<>();
+ if (sh == null) return m;
+ int last = Math.min(sh.getLastRowNum(), sh.getFirstRowNum() + 4);
+ for (int r = sh.getFirstRowNum(); r <= last; r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ for (int c = row.getFirstCellNum(); c >= 0 && c < row.getLastCellNum(); c++) {
+ Integer ym = yearMonthOf(row.getCell(c), year);
+ if (ym != null && !m.containsKey(ym)) m.put(ym, c);
+ }
+ }
+ return m;
+ }
+
+ /**
+ * 鍗曚釜鏈堜唤琛ㄥご -> 骞�*100+鏈堬紱鍙浠婂勾/鍘诲勾锛屽叾浣欎竴寰嬩笉璁わ紙閬垮厤鎶� 2024/2023 瀵规瘮鍧椼�佹潅鏍煎綋鏈堜唤鍒楋級銆�
+ * 涓ょ鍐欐硶锛氭枃鏈� 鈥�2026骞�8鏈堚�濓紱鏁板�兼棩鏈熷簭鍒楋紙姣嶇増鍩庡競瀹㈣繍椤� 3..8 鏈堢殑琛ㄥご鏄� 46235 杩欑瑁稿簭鍒椼��
+ * 鏁板瓧鏍煎紡杩樻槸 General锛屽彧鎸夆�滃綋鏈� 1 鍙封�濆垽鏂紝涓嶈兘渚濊禆 isCellDateFormatted锛夈��
+ */
+ private Integer yearMonthOf(Cell cell, int year) {
+ if (cell == null) return null;
+ org.apache.poi.ss.usermodel.CellType t = cell.getCellType();
+ if (t == org.apache.poi.ss.usermodel.CellType.STRING) {
+ String v = cell.getStringCellValue();
+ if (v == null || v.isEmpty()) return null;
+ v = v.trim();
+ if (v.contains("绱") || v.contains("鍚屾瘮")) return null;
+ java.util.regex.Matcher m = YM_HEADER.matcher(v);
+ if (!m.matches()) return null;
+ int y = Integer.parseInt(m.group(1));
+ if (y != year && y != year - 1) return null;
+ return y * 100 + Integer.parseInt(m.group(2));
+ }
+ if (t == org.apache.poi.ss.usermodel.CellType.NUMERIC) {
+ double v = cell.getNumericCellValue();
+ if (v < 1 || v > 200000) return null;
+ try {
+ java.util.Calendar cal = java.util.Calendar.getInstance();
+ cal.setTime(org.apache.poi.ss.usermodel.DateUtil.getJavaDate(v));
+ if (cal.get(java.util.Calendar.DAY_OF_MONTH) != 1) return null;
+ int y = cal.get(java.util.Calendar.YEAR);
+ if (y != year && y != year - 1) return null;
+ return y * 100 + (cal.get(java.util.Calendar.MONTH) + 1);
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /** 琛ㄥご閲� 鈥�20XX骞碼-b鏈堢疮璁♀�� 杩欑被鍖洪棿绱杈呭姪鍒楃殑 0 鍩哄垪鍙烽泦鍚� */
+ private java.util.Set<Integer> cumulativeHelperCols(Sheet sh) {
+ java.util.Set<Integer> set = new java.util.HashSet<>();
+ if (sh == null) return set;
+ int last = Math.min(sh.getLastRowNum(), sh.getFirstRowNum() + 4);
+ for (int r = sh.getFirstRowNum(); r <= last; r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ for (int c = row.getFirstCellNum(); c >= 0 && c < row.getLastCellNum(); c++) {
+ Cell cell = row.getCell(c);
+ if (cell == null || cell.getCellType() != org.apache.poi.ss.usermodel.CellType.STRING) continue;
+ String v = cell.getStringCellValue();
+ if (v != null && YM_RANGE_CUM.matcher(v.trim()).matches()) set.add(c);
+ }
+ }
+ return set;
+ }
+
+ /** 鏈堜唤鏄犲皠鐨勫彲鐢ㄦ�у畧鍗細鍘诲勾涓庝粖骞� 1..month 鍚勬湀鍒楀繀椤婚綈鍏ㄤ笖鍒楀彿涓ユ牸閫掑锛�
+ * 浠讳竴涓嶆弧瓒筹紙琛ㄥご娌″啓鏈堜唤銆佹垨鎶撳埌鏉傛牸锛夊氨杩斿洖绌鸿〃锛岃皟鐢ㄦ柟閫�鍥炪�屾暣浣撳彸绉� 2 鍒椼�嶇殑鏃ц涓� */
+ private Map<Integer, Integer> usableMonthCols(Sheet sh, int year, int month) {
+ Map<Integer, Integer> usable = new LinkedHashMap<>();
+ Map<Integer, Integer> all = yearMonthCols(sh, year);
+ int prevLy = -1, prevCy = -1;
+ for (int m = 1; m <= month; m++) {
+ Integer ly = all.get((year - 1) * 100 + m);
+ Integer cy = all.get(year * 100 + m);
+ if (ly == null || cy == null || ly <= prevLy || cy <= prevCy) return new LinkedHashMap<>();
+ prevLy = ly;
+ prevCy = cy;
+ usable.put((year - 1) * 100 + m, ly);
+ usable.put(year * 100 + m, cy);
+ }
+ return usable;
+ }
+
+ /**
+ * 銆屼笂鏈堝悓姣斻�嶅叕寮� 鈫� 銆屾湰鏈堝悓姣斻�嶅叕寮忥細鍏紡閲屽紩鐢ㄥ埌鏈堜唤鍒楃殑寮曠敤鎸夎〃澶� (骞�,鏈�) 椤哄欢涓�涓湀锛�
+ * 鍏朵綑寮曠敤閫�鍥炪�屾暣浣撳彸绉� 2 鍒椼�嶃��
+ *
+ * 涓轰粈涔堜笉鑳戒竴寰嬪彸绉� 2 鍒楋細姣嶇増銆婂煄甯傚杩愩�嬮〉鍦� 2025 骞存鎻掍簡涓�鍒椼��2025骞�1-7鏈堢疮璁°�嶈緟鍔╁垪锛�
+ * 2025 鍚勬湀鍒椾粠 8 鏈堣捣涓嶅啀绛夎窛锛�2025骞�8鏈堝湪 AK銆佷笉鍦� AJ锛夛紝鍙崇Щ 2 鍒椾細钀藉埌杈呭姪鍒椾笂锛�
+ * 浜庢槸 8 鏈堝悓姣旇绠楁垚銆�8鏈堝�� 梅 2025骞�1-7鏈堢疮璁� 鈭� 1銆嶏紙2026-09-20 鐢ㄦ埛涓婃姤鐨勯棶棰橈級銆�
+ */
+ private String shiftFormulaToNextMonth(Sheet sh, String formula, int year, int month) {
+ if (formula == null || formula.isEmpty()) return formula;
+ Map<Integer, Integer> cols = usableMonthCols(sh, year, month);
+ if (cols.isEmpty()) return shiftFormulaColumns(formula, 2);
+ Map<Integer, Integer> colToYm = new LinkedHashMap<>();
+ for (Map.Entry<Integer, Integer> e : cols.entrySet()) colToYm.put(e.getValue(), e.getKey());
+ java.util.regex.Matcher m = A1_REF.matcher(formula);
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ int idx = colIndexOf(m.group(2));
+ Integer ym = colToYm.get(idx);
+ Integer next = null;
+ if (ym != null) {
+ next = cols.get((ym / 100) * 100 + (ym % 100) + 1);
+ }
+ m.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(
+ m.group(1) + colLettersOf(next != null ? next : idx + 2) + m.group(3) + m.group(4)));
+ }
+ m.appendTail(sb);
+ return sb.toString();
+ }
+
+ /**
+ * 淇銆岀疮璁″悓姣斻�嶅垎姣嶃�傛瘝鐗堛�婂煄甯傚杩愩�嬮〉鎶� 2025 骞� 1..M 鏈堝悇鏈堝垪**鍜�**瀹冧滑鐨�
+ * 銆�2025骞�1-M鏈堢疮璁°�嶈緟鍔╁垪涓�璧峰啓杩涘垎姣嶏紙1..M-1 鏈堥噸澶嶈涓�娆°�佸綋鏈堟紡璁★級锛�
+ * 鍙︽湁鍦ㄦ瘝鐗堥噷鍒犳帀杈呭姪鍒楀悗娈嬬暀 #REF! 鐨勬儏褰紱涓ょ閮戒細璁╃疮璁″悓姣斾弗閲嶅け鐪�
+ * 锛�2026-09-20 鐢ㄦ埛涓婃姤锛氬煄甯傚杩� 8 鏈堝悓姣� 鈭�86.5%銆佺疮璁″悓姣� 鈭�43.5%锛夈��
+ * 缁熶竴鎸夎〃澶存妸鍒嗘瘝閲嶅啓涓恒�屽幓骞� 1..褰撴湀 鍚勬湀鍊煎垪涔嬪拰銆嶃��
+ *
+ * 2026-09-20 宸插湪銆�2026骞�8鏈堥亾璺繍杈撻噺姹囨�昏〃.xlsx銆嬫瘝鐗堥噷鍒犳帀璇ヨ緟鍔╁垪锛堝浠借
+ * docs/鐢熸垚姹囨�诲ぇ琛�/_bak_202608姣嶇増鍒犻櫎AJ鍒楀墠_20260920.xlsx锛夛紝姝e父鎯呭喌鏈柟娉曞懡涓� 0 鏍硷紱
+ * 淇濈暀瀹冩槸涓轰簡鍏滀綇銆屽悓鏈熷浠芥瘝鐗� / 鍘嗗彶閮ㄧ讲鍖� / 涔嬪悗鍙堟彃浜嗚緟鍔╁垪鐨勬瘝鐗堛�嶈繖绫绘儏鍐点��
+ */
+ public int repairCumulativeYoyFormulas(XSSFWorkbook wb, int year, int month) {
+ if (wb == null || month < 1) return 0;
+ int fixed = 0;
+ for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+ Sheet sh = wb.getSheetAt(i);
+ if (sh == null || !isMonthLedgerSheet(sh.getSheetName())) continue;
+ int[] cum = cumulativeCols(sh, year);
+ if (cum == null) continue;
+ java.util.Set<Integer> helperCols = cumulativeHelperCols(sh);
+ Map<Integer, Integer> all = yearMonthCols(sh, year);
+ List<Integer> lastYearValueCols = new ArrayList<>();
+ int prev = -1;
+ for (int m = 1; m <= month; m++) {
+ Integer c = all.get((year - 1) * 100 + m);
+ if (c == null || c <= prev) {
+ lastYearValueCols.clear();
+ break;
+ }
+ prev = c;
+ lastYearValueCols.add(c);
+ }
+ if (lastYearValueCols.isEmpty()) continue;
+ for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ Cell cc = row.getCell(cum[1]);
+ if (cc == null || cc.getCellType() != org.apache.poi.ss.usermodel.CellType.FORMULA) continue;
+ String f = cc.getCellFormula();
+ if (f == null || !cumulativeYoyBroken(f, helperCols)) continue;
+ // 娉ㄦ剰锛歅OI 鐨� setCellFormula 涓嶆帴鍙椾互 "=" 寮�澶寸殑鍏紡涓诧紙浼氭姏 FormulaParseException锛�
+ StringBuilder sb = new StringBuilder();
+ sb.append(cellRef(cum[0], r + 1)).append("/(");
+ for (int k = 0; k < lastYearValueCols.size(); k++) {
+ if (k > 0) sb.append('+');
+ sb.append(cellRef(lastYearValueCols.get(k), r + 1));
+ }
+ sb.append(")-1");
+ try {
+ cc.setCellFormula(sb.toString());
+ fixed++;
+ } catch (Exception ex) {
+ // 涓埆鍏紡鏀瑰啓澶辫触涓嶉樆濉炲鍑猴紙鎵撳紑鏃朵粛鎸夊師鍏紡閲嶇畻锛�
+ if (log != null) log.warn("绱鍚屾瘮鍏紡鏀瑰啓澶辫触锛歴heet={} cell={} 鍘熷紡={} 鏂板紡={}锛坽}锛�",
+ sh.getSheetName(), cellRef(cum[1], r + 1), f, sb, ex.toString());
+ }
+ }
+ }
+ if (fixed > 0 && log != null) {
+ log.info("姹囨�诲伐浣滅翱锛氱疮璁″悓姣斿垎姣嶄慨姝� {} 鏍硷紙绗� {} 鏈堬紝鍒嗘瘝鍙栧幓骞� 1..{} 鏈堝悇鏈堝�煎垪锛�", fixed, month, month);
+ }
+ return fixed;
+ }
+
+ /** 绱鍚屾瘮鍏紡鏄惁鈥滃潖浜嗏�濓細鍚� #REF!锛屾垨鍒嗘瘝寮曠敤浜嗗尯闂寸疮璁¤緟鍔╁垪锛堜笌鍚勬湀鍒楅噸澶嶈鍏ワ級 */
+ private boolean cumulativeYoyBroken(String formula, java.util.Set<Integer> helperCols) {
+ if (formula.contains("#REF!")) return true;
+ java.util.regex.Matcher m = A1_REF.matcher(formula);
+ while (m.find()) {
+ if (helperCols.contains(colIndexOf(m.group(2)))) return true;
+ }
+ return false;
+ }
+
+ /** 璇ラ〉銆岀疮璁� / 绱鍚屾瘮銆嶄袱鍒楋紙0 鍩猴級锛屾寜琛ㄥご鍓� 5 琛屾壘 鈥滅疮璁♀�� 鎴� 鈥渰year}骞寸疮璁♀�濓紝涓斿叾鍙冲垪鍚� 鈥滃悓姣斺�� */
+ private int[] cumulativeCols(Sheet sh, int year) {
+ int last = Math.min(sh.getLastRowNum(), sh.getFirstRowNum() + 4);
+ for (int r = sh.getFirstRowNum(); r <= last; r++) {
+ Row row = sh.getRow(r);
+ if (row == null) continue;
+ for (int c = row.getFirstCellNum(); c >= 0 && c < row.getLastCellNum(); c++) {
+ Cell cell = row.getCell(c);
+ if (cell == null || cell.getCellType() != org.apache.poi.ss.usermodel.CellType.STRING) continue;
+ String v = cell.getStringCellValue();
+ if (v == null) continue;
+ v = v.trim();
+ if (!"绱".equals(v) && !(year + "骞寸疮璁�").equals(v)) continue;
+ Cell nxt = row.getCell(c + 1);
+ if (nxt == null || nxt.getCellType() != org.apache.poi.ss.usermodel.CellType.STRING) continue;
+ String nt = nxt.getStringCellValue();
+ if (nt != null && nt.contains("鍚屾瘮")) return new int[]{c, c + 1};
+ }
+ }
+ return null;
+ }
+
+ /** 鏈堜唤瀹归噺锛氫紭鍏堝彇琛ㄥご锛堝墠 5 琛岋級鈥�2026鈥︾疮璁♀�濆垪鍙嶆帹锛涜揣杩愰〉鏃犺鏂囨锛�
+ * 鍐嶇敤琛ㄥご閲屾渶鍚庝竴涓��2026骞碝鏈堚��/鏃ユ湡鏍煎紡鐨勬湀鍒楀厹搴曪紙濡傝揣杩� Q3=2026-08-01 -> 8锛� */
private int monthColumnCapacity(Sheet sh) {
+ int byCumulative = 0;
+ int byMonthHeader = 0;
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;
+ int idx = c.getColumnIndex();
+ if (idx <= FIRST_MONTH_COL || (idx - FIRST_MONTH_COL) % 2 != 0) 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;
+ if (byCumulative == 0 && t != null && t.contains("2026") && t.contains("绱")) {
+ byCumulative = (idx - FIRST_MONTH_COL) / 2;
+ }
+ if (t != null && t.contains("2026") && t.contains("鏈�") && !t.contains("绱")
+ && !t.contains("鍚屾瘮")) {
+ byMonthHeader = Math.max(byMonthHeader, (idx - FIRST_MONTH_COL) / 2 + 1);
+ } else if (c.getCellType() == org.apache.poi.ss.usermodel.CellType.NUMERIC
+ && org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(c)) {
+ byMonthHeader = Math.max(byMonthHeader, (idx - FIRST_MONTH_COL) / 2 + 1);
}
}
}
- return 7;
+ int cap = Math.max(byCumulative, byMonthHeader);
+ return cap > 0 ? cap : 7;
}
private String text(Cell c) {
--
Gitblit v1.9.1