From 3bde121db1c2a5fd0bc656c71e8fe6bdfde31074 Mon Sep 17 00:00:00 2001
From: yxh <172933527@qq.com>
Date: 星期二, 27 十月 2026 13:56:32 +0800
Subject: [PATCH] 修改了统计

---
 ruoyi-project/src/main/java/com/ruoyi/project/domain/SpStatCase.java              |   32 ++----
 ruoyi-project/src/main/java/com/ruoyi/project/common/AttachmentNameValidator.java |  246 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 257 insertions(+), 21 deletions(-)

diff --git a/ruoyi-project/src/main/java/com/ruoyi/project/common/AttachmentNameValidator.java b/ruoyi-project/src/main/java/com/ruoyi/project/common/AttachmentNameValidator.java
new file mode 100644
index 0000000..9b7aea9
--- /dev/null
+++ b/ruoyi-project/src/main/java/com/ruoyi/project/common/AttachmentNameValidator.java
@@ -0,0 +1,246 @@
+package com.ruoyi.project.common;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * 鎶ラ攢鍗曢檮浠舵枃浠跺悕瑙勮寖鏍¢獙锛�2026-10-15锛夈��
+ *
+ * <p>涓氬姟鍙e緞锛堜笟鍔℃柟纭锛夛細
+ * <ol>
+ *   <li>蹇呴』鍖呭惈涓枃锛堣嚦灏� 1 涓眽瀛楋級鈥斺��"涓烘柟渚挎煡楠岄檮浠讹紝璇风敤涓枃瑙勮寖鍛藉悕闄勪欢锛�"</li>
+ *   <li>鍘绘帀鎵╁睍鍚嶅悗鐨勪富骞诧紝鎸夊瓧绗︽暟璁$畻涓嶈兘瓒呰繃 20 涓瓧鈥斺��"涓烘柟渚挎煡楠岄檮浠讹紝闄勪欢鍚嶅瓧闀垮害涓嶈兘瓒呰繃20涓瓧锛�"</li>
+ *   <li>涓嶈兘鍖呭惈绌烘牸鍙� {@code / \ : * ? " < > | # %} 绛夌壒娈婂瓧绗�</li>
+ *   <li>鍚屼竴鏄庣粏琛屽唴涓嶈兘閲嶅悕锛堟瘮杈冩椂蹇界暐鎵╁睍鍚嶅ぇ灏忓啓涓庨灏剧┖鏍硷級</li>
+ * </ol>
+ *
+ * <p>涓庡墠绔� {@code opo-web/src/utils/fileNameCheck.js} <b>鍚屽彛寰�</b>锛�
+ * 鍓嶇璐熻矗"涓婁紶鍗虫彁閱� + 淇濆瓨鍓嶆嫤鎴�"锛屾湰绫昏礋璐e悗绔繚瀛樻椂鐨�<b>浜屾鏍¢獙</b>锛堥槻姝㈢粫杩囧墠绔級銆�
+ *
+ * <p>鐢ㄦ硶锛堝湪淇濆瓨鍏ュ彛閲岋級锛�
+ * <pre>
+ *   String err = AttachmentNameValidator.checkJsonList(detail.getInvoicefiles(), detail.getAnnexfiles());
+ *   if (err != null) { return AjaxResult.error("闄勪欢鏂囦欢鍚嶄笉绗﹀悎瑕佹眰锛屾棤娉曚繚瀛橈細" + err); }
+ * </pre>
+ *
+ * @author ruoyi
+ */
+public final class AttachmentNameValidator {
+
+    /** 涓诲共锛堝幓鎵╁睍鍚嶏級鏈�澶у瓧绗︽暟 */
+    public static final int MAX_MAIN_LENGTH = 20;
+
+    public static final String MSG_NO_CHINESE = "涓烘柟渚挎煡楠岄檮浠讹紝璇风敤涓枃瑙勮寖鍛藉悕闄勪欢锛�";
+    public static final String MSG_TOO_LONG = "涓烘柟渚挎煡楠岄檮浠讹紝闄勪欢鍚嶅瓧闀垮害涓嶈兘瓒呰繃20涓瓧锛�";
+    public static final String MSG_ILLEGAL = "涓烘柟渚挎煡楠岄檮浠讹紝闄勪欢鍚嶄笉鑳藉寘鍚┖鏍煎強 / \\ : * ? \" < > | # % 绛夌壒娈婂瓧绗︼紒";
+    public static final String MSG_DUPLICATE = "涓烘柟渚挎煡楠岄檮浠讹紝鍚屼竴琛屽唴涓嶈兘鏈夐噸鍚嶇殑闄勪欢锛�";
+    /** 闄勪欢椤归噷鍙栦笉鍒颁换浣曟枃浠跺悕锛堟棦娌℃湁 name 涔熸病鏈� fileName锛夋椂鐨勬彁绀� */
+    public static final String MSG_NO_NAME = "涓烘柟渚挎煡楠岄檮浠讹紝闄勪欢蹇呴』甯︽枃浠跺悕锛岃閲嶆柊涓婁紶锛�";
+
+    /** 姹夊瓧 */
+    private static final Pattern CHINESE = Pattern.compile("[\\u4e00-\\u9fa5]");
+
+    /** 闈炴硶瀛楃锛氱┖鏍硷紙鍚悇绫荤┖鐧斤級+ / \ : * ? " < > | # % */
+    private static final Pattern ILLEGAL = Pattern.compile("[\\s/\\\\:*?\"<>|#%]");
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private AttachmentNameValidator() {
+    }
+
+    /**
+     * 鍙栦富骞诧紙鍘绘帀鏈�鍚庝竴涓墿灞曞悕锛夈��
+     */
+    public static String mainName(String fileName) {
+        String s = fileName == null ? "" : fileName.trim();
+        int index = s.lastIndexOf('.');
+        return index > 0 ? s.substring(0, index) : s;
+    }
+
+    /**
+     * 鏍¢獙鍗曚釜鏂囦欢鍚嶃��
+     *
+     * @param fileName 鍘熷鏂囦欢鍚�
+     * @return {@code null}=閫氳繃锛涘惁鍒欒繑鍥炰笉鍚堟牸鍘熷洜
+     */
+    public static String checkName(String fileName) {
+        String name = fileName == null ? "" : fileName.trim();
+        if (name.isEmpty()) {
+            return MSG_NO_CHINESE;
+        }
+        if (ILLEGAL.matcher(name).find()) {
+            return MSG_ILLEGAL;
+        }
+        String main = mainName(name);
+        if (!CHINESE.matcher(main).find()) {
+            return MSG_NO_CHINESE;
+        }
+        // 鎸夊瓧绗︽暟璁★紙鐮佺偣璁℃暟锛氫腑鏂�/瀛楁瘝/鏁板瓧/鍏ㄨ鍚勮 1锛�
+        if (main.codePointCount(0, main.length()) > MAX_MAIN_LENGTH) {
+            return MSG_TOO_LONG;
+        }
+        return null;
+    }
+
+    /**
+     * 鏍¢獙涓�涓檮浠跺垪琛� JSON锛堝舰濡� {@code [{"name":"寮犱笁鍙戠エ.pdf","url":"/profile/upload/..."}]}锛夈��
+     *
+     * @param json 闄勪欢瀛楁鍐呭锛坅nnexfiles / invoicefiles锛夛紝鍙负绌�
+     * @return {@code null}=閫氳繃锛涘惁鍒欒繑鍥炰笉鍚堟牸鍘熷洜锛堝惈鏂囦欢鍚嶏級
+     */
+    public static String checkJson(String json) {
+        return checkJson(json, null, true);
+    }
+
+    /**
+     * 淇敼淇濆瓨鏃朵娇鐢細蹇界暐"淇濆瓨鍓嶅凡瀛樺湪"鐨勫巻鍙查檮浠跺悕銆�
+     *
+     * <p>涓氬姟鍙e緞锛�2026-10-15 纭锛夛細**鍙牎楠屾湰娆″彉鏇寸殑闄勪欢**鈥斺�斿巻鍙插崟鎹噷宸插瓨鐨勪笉鍚堣闄勪欢鍚嶏紝
+     * 鍦ㄧ紪杈戣鍗曟嵁鏃朵笉搴旀妸淇濆瓨鍗℃锛屽惁鍒欒�佸崟鎹敼涓�涓噾棰濋兘瀛樹笉杩涘幓銆�
+     *
+     * @param existingNames 淇濆瓨鍓嶈鏄庣粏宸插瓨鍦ㄧ殑闄勪欢鍚嶏紙鐢� {@link #collectNames(String)} 鏀堕泦锛�
+     *                      闇�涓哄皬鍐欏幓绌虹櫧褰㈠紡锛涗负绌�/涓� null 琛ㄧず鎸夊叏鏂板崟鎹鐞嗭級
+     */
+    public static String checkJsonForUpdate(String json, Set<String> existingNames) {
+        return checkJson(json, existingNames, false);
+    }
+
+    /**
+     * 鏀堕泦闄勪欢 JSON 閲屽凡鏈夌殑鏂囦欢鍚嶏紙灏忓啓銆佸幓绌虹櫧锛夛紝鐢ㄤ簬璇嗗埆"鍘嗗彶宸插瓨鍦�"鐨勯檮浠躲��
+     */
+    public static Set<String> collectNames(String json) {
+        Set<String> names = new HashSet<>();
+        if (json == null || json.trim().isEmpty()) {
+            return names;
+        }
+        try {
+            JsonNode root = MAPPER.readTree(json);
+            if (root != null && root.isArray()) {
+                for (JsonNode node : root) {
+                    String raw = fieldText(node, "name");
+                    if (raw == null) {
+                        raw = fieldText(node, "fileName");
+                    }
+                    if (raw != null && !raw.trim().isEmpty()) {
+                        names.add(raw.trim().toLowerCase());
+                    }
+                }
+            }
+        } catch (Exception ignored) {
+            // 鍘嗗彶瀛楁瑙f瀽澶辫触灏卞綋浣�"娌℃湁鍘嗗彶闄勪欢"锛屼笉鍥犺В鏋愰棶棰樺奖鍝嶄繚瀛�
+        }
+        return names;
+    }
+
+    /**
+     * @param existingNames 鍘嗗彶闄勪欢鍚嶏紙灏忓啓锛夛紝鍛戒腑鍒欒烦杩囨牎楠岋紱{@code null} 琛ㄧず涓嶅拷鐣ヤ换浣曞悕瀛�
+     * @param strictNoName  {@code true}=鍙栦笉鍒版枃浠跺悕鏃舵嫤鎴紙鍏ㄦ柊淇濆瓨锛夛紱{@code false}=鏀捐锛堜慨鏀瑰巻鍙插崟鎹級
+     */
+    private static String checkJson(String json, Set<String> existingNames, boolean strictNoName) {
+        if (json == null || json.trim().isEmpty()) {
+            return null;
+        }
+        JsonNode root;
+        try {
+            root = MAPPER.readTree(json);
+        } catch (Exception e) {
+            // 瑙f瀽涓嶄簡灏变笉鍦ㄨ繖閲屾嫤锛堜繚瀛樻祦绋嬭嚜韬細澶勭悊鏍煎紡闂锛夛紝閬垮厤璇嫤
+            return null;
+        }
+        if (root == null || !root.isArray()) {
+            return null;
+        }
+        Set<String> seen = new HashSet<>();
+        for (JsonNode node : root) {
+            String raw = fieldText(node, "name");
+            if (raw == null) {
+                raw = fieldText(node, "fileName");
+            }
+            if (raw == null || raw.trim().isEmpty()) {
+                // 鏃㈡病鏈� name 涔熸病鏈� fileName锛氭棤娉曟牳瀵归檮浠跺悕銆�
+                // 鍏ㄦ柊淇濆瓨锛坅dd锛夋寜"涓嶈兘淇濆瓨"澶勭悊锛涗慨鏀瑰巻鍙插崟鎹椂鏀捐锛岄伩鍏嶅崱浣忚�佹暟鎹�
+                if (strictNoName) {
+                    return MSG_NO_NAME;
+                }
+                continue;
+            }
+            String name = raw.trim();
+            if (isUploadPath(name)) {
+                // 璇ラ檮浠堕」瀛樼殑鏄�"涓婁紶鍚庣殑瀛樺偍璺緞"锛堝舰濡� /profile/upload/2026/10/15/xxx.pdf锛夛紝
+                // 涓嶆槸鎿嶄綔浜哄懡鍚嶇殑鏂囦欢鍚嶏紝鏃犳硶鎹鍒ゆ柇鍛藉悕鏄惁瑙勮寖 鈫� 璺宠繃鏍¢獙锛岄伩鍏嶈鎷﹀悎娉曞崟鎹��
+                // 寰呭墠绔敼涓轰笂浼犳椂璁板綍鍘熷鏂囦欢鍚嶏紙file.raw.name锛夊悗锛岃繖浜涢」浼氳嚜鍔ㄧ撼鍏ユ牎楠屻��
+                continue;
+            }
+            if (existingNames != null && existingNames.contains(name.toLowerCase())) {
+                // 鍘嗗彶闄勪欢锛堜繚瀛樺墠宸插瓨鍦級涓嶅弬涓庢牎楠岋細涓氬姟纭"鍙牎楠屾湰娆″彉鏇寸殑闄勪欢"锛岄伩鍏嶅崱浣忚�佸崟鎹�
+                continue;
+            }
+            String reason = checkName(name);
+            if (reason != null) {
+                return "銆�" + name + "銆�" + reason;
+            }
+            String key = name.toLowerCase();
+            if (!seen.add(key)) {
+                return "銆�" + name + "銆�" + MSG_DUPLICATE;
+            }
+        }
+        return null;
+    }
+
+    /** 璇诲彇闄勪欢椤归噷鐨勫瓧绗︿覆瀛楁锛屽彇涓嶅埌锛堢己澶�/涓虹┖/null锛夎繑鍥� {@code null} */
+    private static String fieldText(JsonNode node, String field) {
+        if (node == null) {
+            return null;
+        }
+        JsonNode value = node.get(field);
+        if (value == null || value.isNull()) {
+            return null;
+        }
+        String text = value.asText();
+        return text == null || text.trim().isEmpty() ? null : text;
+    }
+
+    /** 鍒ゆ柇闄勪欢椤归噷鐨� name 鏄惁涓�"涓婁紶鍚庣殑瀛樺偍璺緞 / URL"锛堣�屼笉鏄搷浣滀汉鍛藉悕鐨勬枃浠跺悕锛� */
+    private static boolean isUploadPath(String name) {
+        String lower = name.toLowerCase();
+        return name.startsWith("/") || name.startsWith("http") || name.contains("/") || name.contains("\\")
+                || lower.contains("profile/upload");
+    }
+
+    /**
+     * 鏍¢獙鍚屼竴鏄庣粏琛屼笅鐨勫涓檮浠跺垪琛紙濡傚彂绁� + 鍏跺畠闄勪欢锛夛紝閫愪釜鍒楄〃鐙珛鏍¢獙锛�
+     * 姣忎釜鍒楄〃鍐呴儴鏌ラ噸鍚嶏紙涓庡墠绔� checkDetails 鐨勫彛寰勪竴鑷达級銆�
+     *
+     * @param jsons 闄勪欢瀛楁鍐呭锛屽彲涓� null/绌�
+     * @return {@code null}=閫氳繃锛涘惁鍒欒繑鍥炰笉鍚堟牸鍘熷洜
+     */
+    public static String checkJsonList(String... jsons) {        if (jsons == null) {
+            return null;
+        }
+        for (String json : jsons) {
+            String reason = checkJson(json);
+            if (reason != null) {
+                return reason;
+            }
+        }
+        return null;
+    }
+
+    /** 淇敼淇濆瓨鏃朵娇鐢細蹇界暐鍘嗗彶闄勪欢鍚嶏紝閫愪釜鍒楄〃鏍¢獙锛堜緵"澶氫釜瀛楁涓�璧锋牎楠�"鐨勮皟鐢ㄧ偣锛� */
+    public static String checkJsonListForUpdate(Set<String> existingNames, String... jsons) {
+        if (jsons == null) {
+            return null;
+        }
+        for (String json : jsons) {
+            String reason = checkJsonForUpdate(json, existingNames);
+            if (reason != null) {
+                return reason;
+            }
+        }
+        return null;
+    }
+}
diff --git a/ruoyi-project/src/main/java/com/ruoyi/project/domain/SpStatCase.java b/ruoyi-project/src/main/java/com/ruoyi/project/domain/SpStatCase.java
index b9d1c03..476b9df 100644
--- a/ruoyi-project/src/main/java/com/ruoyi/project/domain/SpStatCase.java
+++ b/ruoyi-project/src/main/java/com/ruoyi/project/domain/SpStatCase.java
@@ -24,13 +24,6 @@
     private static final long serialVersionUID = 1L;
 
     /**
-     * donationmonth
-     */
-    @ApiModelProperty("donationmonth")
-    @Excel(name = "缁熻鏈堜唤", sort = 1)
-    private String donationmonth;
-
-    /**
      * deptid
      */
     @ApiModelProperty("deptid")
@@ -40,7 +33,7 @@
      * deptname
      */
     @ApiModelProperty("deptname")
-    @Excel(name = "涓氬姟缁�", sort = 2)
+    @Excel(name = "涓氬姟缁�", sort = 1)
     private String deptname;
 
     /**
@@ -53,90 +46,87 @@
      * reportername
      */
     @ApiModelProperty("reportername")
-    @Excel(name = "涓撹亴浜哄憳", sort = 3)
+    @Excel(name = "涓撹亴浜哄憳", sort = 2)
     private String reportername;
 
     /**
      * casesum
      */
     @ApiModelProperty("casesum")
-    @Excel(name = "妗堜緥绱", sort = 4, cellType = Excel.ColumnType.NUMERIC)
     private String casesum;
 
     /**
      * 鐜妭 0锛氭綔鍦ㄦ崘鐚�
      */
     @ApiModelProperty("娼滃湪鎹愮尞")
-    @Excel(name = "娼滃湪鎹愮尞", sort = 5, cellType = Excel.ColumnType.NUMERIC)
     private String workflow0;
 
     /**
      * 鐜妭 1锛氬尰瀛﹁瘎浼�
      */
     @ApiModelProperty("鍖诲璇勪及")
-    @Excel(name = "鍖诲璇勪及", sort = 6, cellType = Excel.ColumnType.NUMERIC)
     private String workflow1;
 
     /**
      * 鐜妭 2锛氬灞炵‘璁�
      */
     @ApiModelProperty("瀹跺睘纭")
-    @Excel(name = "瀹跺睘纭", sort = 7, cellType = Excel.ColumnType.NUMERIC)
     private String workflow2;
 
     /**
      * 鐜妭 3锛氫鸡鐞嗗鏌�
      */
     @ApiModelProperty("浼︾悊瀹℃煡")
-    @Excel(name = "浼︾悊瀹℃煡", sort = 8, cellType = Excel.ColumnType.NUMERIC)
     private String workflow3;
 
     /**
      * 鐜妭 4锛氬櫒瀹樺垎閰�
      */
     @ApiModelProperty("鍣ㄥ畼鍒嗛厤")
-    @Excel(name = "鍣ㄥ畼鍒嗛厤", sort = 9, cellType = Excel.ColumnType.NUMERIC)
     private String workflow4;
 
     /**
      * 鐜妭 5锛氳幏鍙栬璇�
      */
     @ApiModelProperty("鑾峰彇瑙佽瘉")
-    @Excel(name = "鑾峰彇瑙佽瘉", sort = 10, cellType = Excel.ColumnType.NUMERIC)
     private String workflow5;
 
     /**
      * 鐜妭 6锛氭崘鐚畬鎴�
      */
     @ApiModelProperty("鎹愮尞瀹屾垚")
-    @Excel(name = "鎹愮尞瀹屾垚", sort = 11, cellType = Excel.ColumnType.NUMERIC)
     private String workflow6;
 
     /**
      * 鐜妭 7锛氬綊妗f挙閿�
      */
     @ApiModelProperty("褰掓。鎾ら攢")
-    @Excel(name = "褰掓。鎾ら攢", sort = 12, cellType = Excel.ColumnType.NUMERIC)
     private String workflow7;
 
     /**
      * 鐜妭 8锛氭渚嬪綊妗�
      */
     @ApiModelProperty("妗堜緥褰掓。")
-    @Excel(name = "妗堜緥褰掓。", sort = 13, cellType = Excel.ColumnType.NUMERIC)
     private String workflow8;
 
     /**
-     * 鐜妭 9锛氭渚嬬粓姝紙椤甸潰宸查殣钘忚鍒楋紝瀵煎嚭涔熶笉杈撳嚭锛�
+     * 鐜妭 9锛氭渚嬬粓姝�
      */
     @ApiModelProperty("妗堜緥缁堟")
     private String workflow9;
 
     /**
+     * 妗堜緥绱锛坵orkflow 7 鎴� 8锛�
+     */
+    @ApiModelProperty("妗堜緥绱")
+    @Excel(name = "妗堜緥绱", sort = 3, cellType = Excel.ColumnType.NUMERIC)
+    private String workflowsum;
+
+    /**
      * 缁堟妗堜緥鏁帮紙termination_case=1锛�
      */
     @ApiModelProperty("缁堟妗堜緥鏁�")
-    @Excel(name = "缁堟妗堜緥", sort = 14, cellType = Excel.ColumnType.NUMERIC)
+    @Excel(name = "缁堟妗堜緥", sort = 4, cellType = Excel.ColumnType.NUMERIC)
     private String terminationcase;
 
 }

--
Gitblit v1.9.3