From 8b739ce9e80fce73da0f36408aaeec85318b806b Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期二, 22 九月 2026 17:31:09 +0800
Subject: [PATCH] docs(交接): 统一 09-22 关机快照(合并同事版本,修正运行状态为实测:8090/8080 在运行)

---
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
index bbe8df3..42e5bf2 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -5781,6 +5781,8 @@
             String curCity = null;
             Set<Long> used = new HashSet<>();
             int filled = 0, kept = 0, dup = 0;
+            List<String> skipped = new ArrayList<>();
+            Map<String, Integer> skippedByCity = new LinkedHashMap<>();
             for (int r = 8; r <= sheet.getLastRowNum(); r++) {
                 Row row = sheet.getRow(r);
                 if (row == null) continue;
@@ -5792,11 +5794,16 @@
                 if (!a.matches("\\d+(\\.\\d+)?")) continue;
                 if (b.isEmpty() || curCity == null) continue;
                 InvestmentProject p = matchInvest(projects, b, curCity, "瀹㈣繍绔欏満", used);
+                p = preferMonthlyCandidate(projects, p, mByProject, "瀹㈣繍绔欏満", used);
                 InvestmentMonthly m = p == null ? null : mByProject.get(p.getId());
                 if (p == null || m == null) {
                     kept++;
                     // 鏈夊簱鍐呴」鐩絾褰撴湀鏃犳暟鎹� 鈫� 璁颁负寰呮牳瀵癸紱鏃犲尮閰嶉」鐩� 鈫� 缁撹浆涓婃湀
                     if (p != null) dup++;
+                    // 2026-09-22锛氫笉鍐嶉潤榛樼粨杞笂鏈堬紝鏈鐩栫殑琛屽繀椤荤暀鐥曪紙琛屽彿+甯傚窞+椤圭洰鍚�+鍘熷洜锛�
+                    skipped.add("绗�" + (r + 1) + "琛� " + curCity + " " + b
+                            + (p == null ? " 鈫� 搴撳唴鏃犲尮閰嶉」鐩�" : " 鈫� 搴撳唴鏃� " + period + " 鏈堝害鏁版嵁"));
+                    skippedByCity.merge(curCity, 1, Integer::sum);
                     continue;
                 }
                 used.add(p.getId());
@@ -5826,6 +5833,7 @@
             }
             log.info("瀹㈣繍绔欐姇璧勬槑缁嗚〃 {}锛氬啓鍏� {} 琛岋紝缁撹浆姣嶇増鍘熷�� {} 琛岋紙鍏朵腑搴撳唴鏈夐」鐩絾褰撴湀鏃犳暟鎹� {} 琛岋級",
                     period, filled, kept, dup);
+            logSkippedRows("瀹㈣繍绔欐姇璧勬槑缁嗚〃", period, skipped, skippedByCity);
             return toBytes(wb);
         }
     }
@@ -5866,6 +5874,8 @@
         Map<String, double[]> cityStats = new LinkedHashMap<>();  // 鍏ㄥ悕 -> {鎬绘暟, 绱, 瑙勫垝鍐呮暟, 瑙勫垝鍐呯疮璁�, 瑙勫垝澶栨暟, 瑙勫垝澶栫疮璁
         Set<Long> used = new HashSet<>();
         int filled = 0, kept = 0;
+        List<String> skipped = new ArrayList<>();
+        Map<String, Integer> skippedByCity = new LinkedHashMap<>();
         for (int i = 0; i < cityRows.size(); i++) {
             int cr = cityRows.get(i);
             String cityLabel = nvl(cellText(sheet.getRow(cr).getCell(0)));
@@ -5888,8 +5898,16 @@
                 if (!a.matches("\\d+(\\.\\d+)?")) continue;
                 if (b.isEmpty()) continue;
                 InvestmentProject p = matchInvest(projects, b, city, "鐗╂祦鍥尯", used);
+                p = preferMonthlyCandidate(projects, p, mByProject, "鐗╂祦鍥尯", used);
                 InvestmentMonthly m = p == null ? null : mByProject.get(p.getId());
-                if (p == null || m == null) { kept++; continue; }
+                if (p == null || m == null) {
+                    kept++;
+                    // 2026-09-22锛氫笉鍐嶉潤榛樼粨杞笂鏈堬紝鏈鐩栫殑琛屽繀椤荤暀鐥�
+                    skipped.add("绗�" + (r + 1) + "琛� " + city + " " + b
+                            + (p == null ? " 鈫� 搴撳唴鏃犲尮閰嶉」鐩�" : " 鈫� 搴撳唴鏃� " + period + " 鏈堝害鏁版嵁"));
+                    skippedByCity.merge(city, 1, Integer::sum);
+                    continue;
+                }
                 used.add(p.getId());
                 // 鍙e緞鍚屽杩愮珯鏄庣粏琛細鍙鐩� H 鑷紑濮嬪缓璁剧疮璁� / J 鑷勾鍒濈疮璁� / K 鏈湀瀹屾垚 / M 褰㈣薄杩涘害
                 if (m.getStartCum() != null) putNum(row, 7, m.getStartCum());
@@ -6020,6 +6038,7 @@
             log.warn("鐗╂祦绔欏満鎶曡祫鏄庣粏鍏紡姹傚�煎け璐�: {}", e.getMessage());
         }
         log.info("鐗╂祦绔欏満鎶曡祫鏄庣粏 {}锛氬啓鍏� {} 琛岋紝缁撹浆姣嶇増鍘熷�� {} 琛�", period, filled, kept);
+        logSkippedRows("鐗╂祦绔欏満鎶曡祫鏄庣粏", period, skipped, skippedByCity);
         return toBytesHssf(wb);
     }
 
@@ -6314,6 +6333,40 @@
         return full != null && RegionUtil.cityList().contains(full);
     }
 
+    /**
+     * 鍚屽悕锛堝綊涓�鍖栧悗涓�鑷达級澶氭。鏃讹紝浼樺厛閫夈�屽綋鏈堟湁鏁版嵁銆嶇殑閭d竴妗c��
+     * 鑳屾櫙锛�2026-09-22锛夛細鍗佸牥銆屽揩閫掔墿娴佷骇涓氬洯浜屾湡銆嶅湪搴撳唴鏈変袱妗� id=154锛堢宸� 2026锛屾湀搴﹀彧鍒� 7 鏈堬級涓�
+     * id=181锛堢宸� 2029锛屾湁 8 鏈堬級锛屾ā鏉胯鍚嶅墠缂�涓� 154 涓�鑷� 鈫� 瀵煎嚭鍙栧埌 154 鐨� 7 鏈堝�笺��
+     */
+    private InvestmentProject preferMonthlyCandidate(List<InvestmentProject> pool, InvestmentProject p,
+                                                    Map<Long, InvestmentMonthly> mByProject,
+                                                    String category, Set<Long> excludeIds) {
+        if (p == null || mByProject.containsKey(p.getId())) return p;
+        String norm = normInvestName(p.getProjectName());
+        if (norm.isEmpty()) return p;
+        InvestmentProject better = null;
+        for (InvestmentProject c : pool) {
+            if (c == null || c.getId().equals(p.getId())) continue;
+            if (excludeIds != null && excludeIds.contains(c.getId())) continue;
+            if (category != null && !category.equals(c.getCategory())) continue;
+            if (!mByProject.containsKey(c.getId())) continue;
+            if (!norm.equals(normInvestName(c.getProjectName()))) continue;
+            if (better == null || c.getId() > better.getId()) better = c;
+        }
+        return better != null ? better : p;
+    }
+
+    /** 鏈寜褰撴湀鏁版嵁瑕嗙洊鐨勮蹇呴』鐣欑棔锛氶�愯鍒楀嚭琛屽彿+甯傚窞+椤圭洰鍚嶏紝閬垮厤銆岄潤榛樼粨杞笂鏈熷�笺�嶅啀娆″彂鐢� */
+    private void logSkippedRows(String reportName, String period, List<String> skipped, Map<String, Integer> byCity) {
+        if (skipped == null || skipped.isEmpty()) return;
+        log.warn("{} {}锛歿} 琛屾湭鎸夊綋鏈堟暟鎹鐩栵紙缁撹浆姣嶇増鍘熷�硷級锛岄渶浜哄伐鏍稿婧愭枃浠�/搴撳唴鏁版嵁锛�",
+                reportName, period, skipped.size());
+        int limit = Math.min(skipped.size(), 50);
+        for (int i = 0; i < limit; i++) log.warn("  [鏈鐩朷 {}", skipped.get(i));
+        if (skipped.size() > limit) log.warn("  鈥﹀彟鏈� {} 琛屾湭閫愯鍒楀嚭", skipped.size() - limit);
+        if (byCity != null && !byCity.isEmpty()) log.warn("  鏈鐩栬鎸夊競宸炵粺璁★細{}", byCity);
+    }
+
     /** 鍦ㄥ�欓�夐」鐩睜涓寜鍚嶇О鍖归厤 DB 椤圭洰锛堝彲閫� 甯傚窞/绫诲埆 杩囨护 + 鍘垮尯鍓嶇紑娑堟锛夛紝鏈懡涓繑鍥� null */
     private InvestmentProject matchInvest(List<InvestmentProject> pool, String name, String cityFull, String category) {
         return matchInvest(pool, name, cityFull, category, null);
@@ -6362,7 +6415,8 @@
         if (name == null) return "";
         String s = name.trim();
         s = s.replaceAll("[锛�(]", "").replaceAll("[锛�)]", "");
-        s = s.replaceAll("[路鈥⑩�旓紞_\\-]", "");
+        // 2026-09-22锛氳ˉ鍏呭崐/鍏ㄨ鍙ョ偣銆侀】鍙枫�佸垎鍙风瓑锛堝簱鍐呫�屼腑鍥戒緵閿�.鍏畨鍟嗚锤鐗╂祦鍥�嶅洜鍗婅鍙ョ偣鍖归厤涓嶄笂妯℃澘琛岋級
+        s = s.replaceAll("[路鈥⑩�旓紞_\\-锛�.銆傘�侊紝,;锛�:锛歖", "");
         s = s.replaceAll("[鈪犫叀鈪⑩叄鈪も叆鈪︹収鈪ㄢ叐鈪扳叡鈪测叧鈪粹叺鈪垛叿鈪糕吂]", "I");
         s = s.replaceAll("^(婀栧寳鐪亅姝︽眽甯倈榛勭煶甯倈鍗佸牥甯倈瀹滄槍甯倈瑗勯槼甯倈閯傚窞甯倈鑽嗛棬甯倈瀛濇劅甯倈鑽嗗窞甯倈榛勫唸甯倈鍜稿畞甯倈闅忓窞甯倈鎭╂柦宸瀨浠欐甯倈娼滄睙甯倈澶╅棬甯倈绁炲啘鏋舵灄鍖�)", "");
         s = s.replaceAll("^(?:[\\u4e00-\\u9fa5]{2,4}(?:鍘縷甯倈鍖�))", "");

--
Gitblit v1.9.1