| | |
| | | } |
| | | if (name == null || name.trim().isEmpty()) continue; |
| | | String nt = name.trim().replaceAll("[\r\n]+", ""); |
| | | if (nt.contains("合计") || nt.contains("总计")) continue; |
| | | String ntFlat = nt.replaceAll("\\s+", ""); |
| | | if (ntFlat.contains("合计") || ntFlat.contains("总计") || ntFlat.contains("小计")) continue; |
| | | if (nt.matches("^[一二三四五六七八九十]+、.*")) continue; |
| | | if (nt.matches("^([一二三四五六七八九十]+).*")) continue; |
| | | if (nt.replace(" ", "").matches(".*小\\s*计.*")) continue; |
| | | if (nt.startsWith("填报") || nt.startsWith("单位负责人") || nt.matches("^\\d+、.*")) continue; |
| | | if (hasSeq) { |
| | | if (seq == null || seq.trim().isEmpty()) { |
| | |
| | | } |
| | | if (!hasName) continue; |
| | | Map<Integer, String> colTexts = new HashMap<>(); |
| | | for (int rr = r; rr <= Math.min(r + 3, sh.getLastRowNum()); rr++) { |
| | | int scanEnd = investHeaderScanEnd(sh, r, row); |
| | | for (int rr = r; rr <= scanEnd; rr++) { |
| | | Row rr2 = sh.getRow(rr); |
| | | if (rr2 == null) continue; |
| | | for (int c = 0; c <= rr2.getLastCellNum(); c++) { |
| | |
| | | else if (v.contains("自年初累计")) col.putIfAbsent("yearcum", c); |
| | | else if (v.contains("本月完成")) col.putIfAbsent("monthdone", c); |
| | | else if (v.contains("建设阶段")) col.putIfAbsent("stage", c); |
| | | else if (v.contains("形象进度")) col.putIfAbsent("desc", c); |
| | | else if (v.contains("形象进度") || v.contains("建设进展")) col.putIfAbsent("desc", c); |
| | | else if (v.contains("工可") && !v.contains("项目名称")) col.putIfAbsent("gk", c); |
| | | else if (v.contains("初设") && !v.contains("项目名称")) col.putIfAbsent("cs", c); |
| | | else if (v.contains("建筑面积") && !v.contains("新增")) col.putIfAbsent("area", c); |
| | |
| | | int md = col.get("yearcum") + 1; |
| | | if (md <= row.getLastCellNum()) col.put("monthdone", md); |
| | | } |
| | | // desc 缺失时按 stage+1 兜底:省标「项目建设阶段、进度情况」是 L/M 合并表头, |
| | | // 孝感/恩施/林区/随州等市州只在 L 写「建设阶段」、M 直接写进度文字而不写标题 |
| | | if (!col.containsKey("desc") && col.containsKey("stage") && col.get("stage") >= 0) { |
| | | int dc = col.get("stage") + 1; |
| | | if (dc <= row.getLastCellNum()) col.put("desc", dc); |
| | | } |
| | | for (String k : new String[]{"builder", "nature", "start", "end", "total", "startcum", |
| | | "yearplan", "yearcum", "monthdone", "stage", "desc", "gk", "cs", "area"}) { |
| | | col.putIfAbsent(k, -1); |
| | |
| | | return col; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** 表头文本扫描下界:默认表头行 +3;若「项目名称」列首个数据行更靠下,则扩展到数据行前一行(上限 +12)。 |
| | | * 背景:咸宁/十堰等市州把「建设阶段/形象进度」标签写在表头下第 4~8 行,固定 +3 会漏列(形象进度读不到)。 */ |
| | | private int investHeaderScanEnd(Sheet sh, int headerRow, Row headerRowObj) { |
| | | int hardEnd = Math.min(headerRow + 12, sh.getLastRowNum()); |
| | | int nameCol = -1; |
| | | if (headerRowObj != null) { |
| | | for (int c = 0; c <= headerRowObj.getLastCellNum(); c++) { |
| | | String v = cellStr(headerRowObj, c); |
| | | if (v != null && v.contains("项目名称")) { nameCol = c; break; } |
| | | } |
| | | } |
| | | int firstData = findFirstInvestDataRow(sh, headerRow, nameCol); |
| | | int scanEnd = (firstData > headerRow) |
| | | ? Math.min(hardEnd, firstData - 1) |
| | | : Math.min(headerRow + 3, sh.getLastRowNum()); |
| | | if (scanEnd < headerRow) scanEnd = headerRow; |
| | | return scanEnd; |
| | | } |
| | | |
| | | /** 「项目名称」列首个数据行下标(跳过表头续行与空行);找不到返回 -1 */ |
| | | private int findFirstInvestDataRow(Sheet sh, int headerRow, int nameCol) { |
| | | if (nameCol < 0) return -1; |
| | | for (int r = headerRow + 1; r <= sh.getLastRowNum(); r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | String name = cellStr(row, nameCol); |
| | | if (name == null || name.trim().isEmpty()) continue; |
| | | String nt = name.trim().replaceAll("[\r\n]+", ""); |
| | | if (nt.contains("项目名称")) continue; |
| | | String flat = nt.replaceAll("\\s+", ""); |
| | | if (flat.contains("合计") || flat.contains("总计") || flat.contains("小计")) continue; |
| | | if (nt.matches("^[一二三四五六七八九十]+、.*")) continue; |
| | | if (nt.matches("^([一二三四五六七八九十]+).*")) continue; |
| | | if (nt.startsWith("填报") || nt.startsWith("单位负责人") || nt.matches("^\\d+、.*")) continue; |
| | | if (investmentCity(nt) != null) continue; |
| | | return r; |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | /** 统计 sheet 内项目数据行数(用于多 sheet 历史文件选当月 sheet) */ |
| | |
| | | String name = cellStr(row, nameCol); |
| | | if (name == null || name.trim().isEmpty()) continue; |
| | | String nt = name.trim().replaceAll("[\r\n]+", ""); |
| | | if (nt.contains("合计") || nt.contains("总计")) continue; |
| | | String ntFlat = nt.replaceAll("\\s+", ""); |
| | | if (ntFlat.contains("合计") || ntFlat.contains("总计") || ntFlat.contains("小计")) continue; |
| | | if (nt.matches("^[一二三四五六七八九十]+、.*")) continue; |
| | | if (nt.replace(" ", "").matches(".*小\\s*计.*")) continue; |
| | | if (nt.startsWith("填报") || nt.startsWith("单位负责人") || nt.matches("^\\d+、.*")) continue; |
| | | if (!hasSeq) { |
| | | if (nt.length() > 4) count++; |