| | |
| | | project.setProjectName(nt); |
| | | project.setCategory(category); |
| | | project.setCity(currentCity); |
| | | String countyCell = cellStr(row, col.get("county")); |
| | | if (countyCell != null && !countyCell.trim().isEmpty()) project.setCounty(countyCell.trim()); |
| | | project.setBuilderName(cellStr(row, col.get("builder"))); |
| | | project.setConstructNature(cellStr(row, col.get("nature"))); |
| | | project.setStartTime(investTime(cellStr(row, col.get("start")))); |
| | |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | boolean hasName = false; |
| | | int headerCells = 0; |
| | | for (int c = 0; c <= row.getLastCellNum(); c++) { |
| | | String v = cellStr(row, c); |
| | | if (v != null && v.contains("项目名称")) { hasName = true; break; } |
| | | if (v == null) continue; |
| | | String t = v.trim(); |
| | | if (t.isEmpty() || t.length() > 20) continue; // 说明性长句不算表头字段 |
| | | String k = investHeaderKey(t); |
| | | if (k == null) continue; |
| | | headerCells++; |
| | | if ("name".equals(k)) hasName = true; |
| | | } |
| | | if (!hasName) continue; |
| | | // 真表头行至少有 2 个短表头字段;只有一格长句含「项目名称」的说明页会被排除 |
| | | if (!hasName || headerCells < 2) continue; |
| | | Map<Integer, String> colTexts = new HashMap<>(); |
| | | int scanEnd = investHeaderScanEnd(sh, r, row); |
| | | for (int rr = r; rr <= scanEnd; rr++) { |
| | |
| | | } |
| | | Map<String, Integer> col = new HashMap<>(); |
| | | for (Map.Entry<Integer, String> e : colTexts.entrySet()) { |
| | | int c = e.getKey(); |
| | | String v = e.getValue(); |
| | | if (v.contains("项目名称")) col.putIfAbsent("name", c); |
| | | else if (v.contains("投资建设") || v.contains("建设单位") || v.contains("项目业主")) col.putIfAbsent("builder", c); |
| | | else if (v.contains("建设性质")) col.putIfAbsent("nature", c); |
| | | else if (v.contains("开工")) col.putIfAbsent("start", c); |
| | | else if (v.contains("竣工") || v.contains("建成")) col.putIfAbsent("end", c); |
| | | else if ((v.contains("计划总投资") || v.contains("总投资")) && !v.contains("累计")) col.putIfAbsent("total", c); |
| | | else if (v.contains("自开始建设")) col.putIfAbsent("startcum", c); |
| | | else if (v.contains("本年计划")) col.putIfAbsent("yearplan", 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("形象进度") || 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); |
| | | String k = investHeaderKey(e.getValue()); |
| | | if (k != null) col.putIfAbsent(k, e.getKey()); |
| | | } |
| | | if (!col.containsKey("name")) continue; |
| | | for (int c = 0; c <= row.getLastCellNum(); c++) { |
| | |
| | | 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"}) { |
| | | "yearplan", "yearcum", "monthdone", "stage", "desc", "gk", "cs", "area", "county"}) { |
| | | col.putIfAbsent(k, -1); |
| | | } |
| | | col.put("headerRow", r); |
| | |
| | | return null; |
| | | } |
| | | |
| | | /** 单个表头单元格 -> 字段键(null 表示不是表头字段);供表头行判定与列映射共用 */ |
| | | private String investHeaderKey(String v) { |
| | | if (v == null) return null; |
| | | String flat = v.replace(" ", ""); |
| | | if (v.contains("项目名称")) return "name"; |
| | | // 「县(市、区)」的各种写法:半/全角括号、顿号/逗号、有无括号,统一归一化后再判定 |
| | | String flatCounty = flat.replace("(", "(").replace(")", ")").replace("、", "").replace(",", "").replace(",", ""); |
| | | if (flatCounty.contains("县(市区)") || flatCounty.contains("县市区") || flatCounty.contains("区县")) return "county"; |
| | | if (v.contains("投资建设") || v.contains("建设单位") || v.contains("项目业主")) return "builder"; |
| | | if (v.contains("建设性质")) return "nature"; |
| | | if (v.contains("开工")) return "start"; |
| | | if (v.contains("竣工") || v.contains("建成")) return "end"; |
| | | if ((v.contains("计划总投资") || v.contains("总投资")) && !v.contains("累计")) return "total"; |
| | | if (v.contains("自开始建设")) return "startcum"; |
| | | if (v.contains("本年计划")) return "yearplan"; |
| | | if (v.contains("自年初累计")) return "yearcum"; |
| | | if (v.contains("本月完成")) return "monthdone"; |
| | | if (v.contains("建设阶段")) return "stage"; |
| | | if (v.contains("形象进度") || v.contains("建设进展")) return "desc"; |
| | | if (v.contains("工可") && !v.contains("项目名称")) return "gk"; |
| | | if (v.contains("初设") && !v.contains("项目名称")) return "cs"; |
| | | if (v.contains("建筑面积") && !v.contains("新增")) return "area"; |
| | | return null; |
| | | } |
| | | |
| | | /** 表头文本扫描下界:默认表头行 +3;若「项目名称」列首个数据行更靠下,则扩展到数据行前一行(上限 +12)。 |
| | | * 背景:咸宁/十堰等市州把「建设阶段/形象进度」标签写在表头下第 4~8 行,固定 +3 会漏列(形象进度读不到)。 */ |
| | | private int investHeaderScanEnd(Sheet sh, int headerRow, Row headerRowObj) { |