From 2e6830bcf59c01f29f040652b3e5209a7f21238e Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期一, 21 九月 2026 10:23:50 +0800
Subject: [PATCH] feat(投资): 投资系统导出新模板按表头解析 + 审核基准缺失保护 + 十堰源文件清理

---
 traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java |  127 +++++++++++++++++++++++++++++++-----------
 1 files changed, 93 insertions(+), 34 deletions(-)

diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
index 049d707..446043d 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
@@ -1808,6 +1808,35 @@
         }
     }
 
+    /** 鎸夎〃澶村叧閿瓧瀹氫綅鍒楀彿锛氫粠绗� 1 鍒楀悜鍙虫壂锛屽懡涓换涓�鍏抽敭瀛楀嵆杩斿洖璇ュ垪锛涙湭鍛戒腑杩斿洖 -1 */
+    private int findColByKeywords(Row header, String... keywords) {
+        if (header == null) return -1;
+        for (int c = 0; c <= header.getLastCellNum(); c++) {
+            Cell cell = header.getCell(c);
+            if (cell == null) continue;
+            String h = FORMATTER.formatCellValue(cell).trim();
+            if (h.isEmpty()) continue;
+            for (String k : keywords) {
+                if (h.contains(k)) return c;
+            }
+        }
+        return -1;
+    }
+
+    /** 琛ㄥご琛屾枃鏈紙鎶ラ敊鎻愮ず鐢級锛屾渶澶氬彇 12 鍒� */
+    private String headerText(Row header) {
+        if (header == null) return "绌�";
+        StringBuilder sb = new StringBuilder();
+        for (int c = 0; c < Math.min(header.getLastCellNum(), 12); c++) {
+            Cell cell = header.getCell(c);
+            String h = cell == null ? "" : FORMATTER.formatCellValue(cell).trim();
+            if (h.isEmpty()) continue;
+            if (sb.length() > 0) sb.append(" | ");
+            sb.append(h);
+        }
+        return sb.length() == 0 ? "绌�" : sb.toString();
+    }
+
     /** 鎸夎〃澶村悕绉板缓绔嬪垪鍙锋槧灏勶紝鍏煎鏂版棫妯℃澘鍒椾綅宸紓 */
     private Map<String, Integer> buildHeaderMap(Row header) {
         Map<String, Integer> colMap = new HashMap<>();
@@ -2489,6 +2518,8 @@
             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"))));
@@ -2524,11 +2555,19 @@
             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++) {
@@ -2542,23 +2581,8 @@
             }
             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("绔e伐") || 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++) {
@@ -2582,12 +2606,37 @@
                 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 col;
         }
+        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("绔e伐") || 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;
     }
 
@@ -2911,32 +2960,42 @@
             this.monthly = monthly;
         }
     }
-    /** 鎶曡祫绯荤粺瀵煎嚭瀵煎叆锛堝鏍告瘮瀵瑰熀鍑嗭級锛屽浐瀹�8鍒楋細琛屽彿/鍗曚綅/鏃舵湡/璁″垝鎬绘姇璧�/鑷紑濮嬬疮璁�/鏈勾璁″垝/鑷勾鍒濈疮璁�/褰撴湀瀹屾垚 */
-    private static final int[] INVEST_SYSTEM_HEADER_COLS = {0, 1, 2, 3, 4, 5, 6, 7};
-    private static final String[] INVEST_SYSTEM_HEADER_KEYWORDS =
-        {"琛屽彿", "鍗曚綅", "鏃舵湡", "璁″垝鎬绘姇璧�", "鑷紑濮嬪缓璁�", "鏈勾璁″垝鎶曡祫", "鑷勾鍒�", "褰撴湀瀹屾垚鎶曡祫"};
-
+    /** 鎶曡祫绯荤粺瀵煎嚭瀵煎叆锛堝鏍告瘮瀵瑰熀鍑嗭級銆�
+     *  鍒椾綅**鎸夎〃澶村叧閿瓧瀹氫綅**锛屽吋瀹逛袱绉嶆ā鏉匡細
+     *   - 鑰佹ā鏉裤�婃ā鏉縚鏌ヨ缁撴灉(鎶曡祫绯荤粺-瀹㈣繍+鐗╂祦 2026.7).xls銆嬶細琛屽彿/鍗曚綅/鏃舵湡/璁″垝鎬绘姇璧�/鑷紑濮嬪缓璁剧疮璁�/鏈勾璁″垝鎶曡祫/鑷勾鍒濈疮璁�/褰撴湀瀹屾垚鎶曡祫
+     *   - 鏂版ā鏉裤��2026骞碭鏈堟姇璧勭郴缁熼」鐩槑缁�.xls銆嬶紙鐢ㄦ埛 2026-09-21 鏇存崲锛夛細琛屽彿/鎶曡祫椤圭洰/鏃舵湡/椤圭洰鎵�澶勯樁娈�/璁″垝鎬绘姇璧�/鏈勾璁″垝鎶曡祫/鑷紑濮嬪缓璁捐嚦鏈勾锛堟湀锛夊簳-绱瀹屾垚鎶曡祫/褰撴湀瀹屾垚鎶曡祫
+     *  鏂版ā鏉�**娌℃湁銆岃嚜骞村垵绱銆嶅垪** 鈫� year_cum 鐣欑┖锛堝鏍告椂鎸夈�屽熀鍑嗙己澶便�嶈烦杩囷紝涓嶅啀褰撴垚 0 鍙備笌姣斿锛夈�� */
     public int importInvestmentSystem(MultipartFile file, String period) throws Exception {
         List<InvestmentSystem> list = new ArrayList<>();
         int failRows = 0;
         try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
             Sheet sheet = wb.getSheetAt(0);
-            requireHeaderKeyword(sheet, "鎶曡祫绯荤粺瀵煎嚭", "椤圭洰鍚嶇О", "璁″垝鎬绘姇璧�", "鍗曚綅");
-            requireHeaderLayout(sheet, "鎶曡祫绯荤粺瀵煎嚭", "妯℃澘_鏌ヨ缁撴灉(鎶曡祫绯荤粺-瀹㈣繍+鐗╂祦 2026.7).xls",
-                INVEST_SYSTEM_HEADER_COLS, INVEST_SYSTEM_HEADER_KEYWORDS);
+            requireHeaderKeyword(sheet, "鎶曡祫绯荤粺瀵煎嚭", "鎶曡祫椤圭洰", "椤圭洰鍚嶇О", "鍗曚綅", "璁″垝鎬绘姇璧�");
+            Row header = sheet.getRow(0);
+            int cName = findColByKeywords(header, "鎶曡祫椤圭洰", "椤圭洰鍚嶇О", "鍗曚綅");
+            int cTotal = findColByKeywords(header, "璁″垝鎬绘姇璧�");
+            int cYearPlan = findColByKeywords(header, "鏈勾璁″垝鎶曡祫");
+            int cStartCum = findColByKeywords(header, "鑷紑濮嬪缓璁�");
+            int cYearCum = findColByKeywords(header, "鑷勾鍒�");
+            int cMonthDone = findColByKeywords(header, "褰撴湀瀹屾垚鎶曡祫");
+            if (cName < 0 || cTotal < 0 || cYearPlan < 0 || cStartCum < 0 || cMonthDone < 0) {
+                throw new RuntimeException("鎵�閫夋暟鎹被鍨嬨�愭姇璧勭郴缁熷鍑恒�戣〃澶村垪涓嶅畬鏁达細闇�瑕併�屾姇璧勯」鐩�(鎴栧崟浣�)銆佽鍒掓�绘姇璧勩�佹湰骞磋鍒掓姇璧勩��"
+                    + "鑷紑濮嬪缓璁锯�︾疮璁″畬鎴愭姇璧勩�佸綋鏈堝畬鎴愭姇璧勩�嶇瓑鍒楋紝瀹為檯琛ㄥご涓恒��" + headerText(header)
+                    + "銆嶃�傝浣跨敤绯荤粺妯℃澘銆�2026骞碭鏈堟姇璧勭郴缁熼」鐩槑缁嗐�嬫垨銆婃ā鏉縚鏌ヨ缁撴灉(鎶曡祫绯荤粺-瀹㈣繍+鐗╂祦)銆嬫暣鐞嗗悗鍐嶅鍏�");
+            }
             for (int r = 1; r <= sheet.getLastRowNum(); r++) {
                 Row row = sheet.getRow(r);
                 if (row == null) continue;
-                String name = getString(row, 1);
+                String name = getString(row, cName);
                 if (name == null || name.trim().isEmpty()) continue;
                 InvestmentSystem sys = new InvestmentSystem();
                 sys.setReportPeriod(period);
                 sys.setProjectName(name.trim());
-                sys.setTotalInvestment(getDecimal(row, 3));
-                sys.setStartCum(getDecimal(row, 4));
-                sys.setYearPlan(getDecimal(row, 5));
-                sys.setYearCum(getDecimal(row, 6));
-                sys.setMonthDone(getDecimal(row, 7));
+                sys.setTotalInvestment(getDecimal(row, cTotal));
+                sys.setStartCum(getDecimal(row, cStartCum));
+                sys.setYearPlan(getDecimal(row, cYearPlan));
+                sys.setYearCum(cYearCum < 0 ? null : getDecimal(row, cYearCum));
+                sys.setMonthDone(getDecimal(row, cMonthDone));
                 list.add(sys);
             }
         } catch (Exception e) {

--
Gitblit v1.9.1