From 4954d3978cf1967729a5a2d5b90f6baef18974da Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期一, 23 三月 2026 09:35:10 +0800
Subject: [PATCH] #ai redis+页面优化

---
 rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiMcpMountServiceImpl.java |  148 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 132 insertions(+), 16 deletions(-)

diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiMcpMountServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiMcpMountServiceImpl.java
index 5bde192..409c20f 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiMcpMountServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiMcpMountServiceImpl.java
@@ -35,7 +35,9 @@
     private final BuiltinMcpToolRegistry builtinMcpToolRegistry;
     private final McpMountRuntimeFactory mcpMountRuntimeFactory;
     private final ObjectMapper objectMapper;
+    private final AiRedisSupport aiRedisSupport;
 
+    /** 鏌ヨ鏌愪釜绉熸埛涓嬪綋鍓嶅惎鐢ㄧ殑 MCP 鎸傝浇鍒楄〃銆� */
     @Override
     public List<AiMcpMount> listActiveMounts(Long tenantId) {
         ensureTenantId(tenantId);
@@ -47,6 +49,7 @@
                 .orderByAsc(AiMcpMount::getId));
     }
 
+    /** 淇濆瓨鍓嶆牎楠� MCP 鎸傝浇鑽夌锛屽苟琛ュ叏杩愯鏃堕粯璁ゅ�笺�� */
     @Override
     public void validateBeforeSave(AiMcpMount aiMcpMount, Long tenantId) {
         ensureTenantId(tenantId);
@@ -55,6 +58,7 @@
         ensureRequiredFields(aiMcpMount, tenantId);
     }
 
+    /** 鏇存柊鍓嶆牎楠屽苟閿佸畾璁板綍鎵�灞炵鎴凤紝闃叉璺ㄧ鎴蜂慨鏀广�� */
     @Override
     public void validateBeforeUpdate(AiMcpMount aiMcpMount, Long tenantId) {
         ensureTenantId(tenantId);
@@ -67,12 +71,24 @@
         ensureRequiredFields(aiMcpMount, tenantId);
     }
 
+    /**
+     * 棰勮褰撳墠鎸傝浇鏈�缁堜細鏆撮湶缁欐ā鍨嬬殑宸ュ叿鐩綍銆�
+     * 瀵瑰唴缃� MCP 浼氶澶栧悎骞舵不鐞嗙洰褰曚俊鎭紝瀵瑰閮� MCP 鍒欎互瀹為檯瑙f瀽缁撴灉涓哄噯銆�
+     */
     @Override
     public List<AiMcpToolPreviewDto> previewTools(Long mountId, Long userId, Long tenantId) {
         AiMcpMount mount = requireMount(mountId, tenantId);
+        // 宸ュ叿鐩綍棰勮鍒濆鍖栨垚鏈珮锛屼絾鍙樺寲棰戠巼浣庯紝閫傚悎鍋氱鐞嗙鐭紦瀛樸��
+        List<AiMcpToolPreviewDto> cached = aiRedisSupport.getToolPreview(tenantId, mountId);
+        if (cached != null) {
+            return cached;
+        }
         long startedAt = System.currentTimeMillis();
         try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) {
-            List<AiMcpToolPreviewDto> tools = buildToolPreviewDtos(runtime.getToolCallbacks());
+            List<AiMcpToolPreviewDto> tools = buildToolPreviewDtos(runtime.getToolCallbacks(),
+                    AiDefaults.MCP_TRANSPORT_BUILTIN.equals(mount.getTransportType())
+                            ? builtinMcpToolRegistry.listBuiltinToolCatalog(mount.getBuiltinCode())
+                            : List.of());
             if (!runtime.getErrors().isEmpty()) {
                 String message = String.join("锛�", runtime.getErrors());
                 updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY, message, System.currentTimeMillis() - startedAt);
@@ -80,6 +96,7 @@
             }
             updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_HEALTHY,
                     "宸ュ叿瑙f瀽鎴愬姛锛屽叡 " + tools.size() + " 涓伐鍏�", System.currentTimeMillis() - startedAt);
+            aiRedisSupport.cacheToolPreview(tenantId, mountId, tools);
             return tools;
         } catch (CoolException e) {
             throw e;
@@ -90,6 +107,7 @@
         }
     }
 
+    /** 瀵瑰凡淇濆瓨鐨勬寕杞藉仛鐪熷疄杩為�氭�ф祴璇曪紝骞舵妸缁撴灉鍥炲啓鍒拌繍琛屾�佸瓧娈点�� */
     @Override
     public AiMcpConnectivityTestDto testConnectivity(Long mountId, Long userId, Long tenantId) {
         AiMcpMount mount = requireMount(mountId, tenantId);
@@ -100,12 +118,16 @@
                 String message = String.join("锛�", runtime.getErrors());
                 updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY, message, elapsedMs);
                 AiMcpMount latest = requireMount(mount.getId(), tenantId);
-                return buildConnectivityDto(latest, message, elapsedMs, runtime.getToolCallbacks().length);
+                AiMcpConnectivityTestDto connectivity = buildConnectivityDto(latest, message, elapsedMs, runtime.getToolCallbacks().length);
+                aiRedisSupport.cacheConnectivity(tenantId, mountId, connectivity);
+                return connectivity;
             }
             String message = "杩為�氭�ф祴璇曟垚鍔燂紝瑙f瀽鍑� " + runtime.getToolCallbacks().length + " 涓伐鍏�";
             updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_HEALTHY, message, elapsedMs);
             AiMcpMount latest = requireMount(mount.getId(), tenantId);
-            return buildConnectivityDto(latest, message, elapsedMs, runtime.getToolCallbacks().length);
+            AiMcpConnectivityTestDto connectivity = buildConnectivityDto(latest, message, elapsedMs, runtime.getToolCallbacks().length);
+            aiRedisSupport.cacheConnectivity(tenantId, mountId, connectivity);
+            return connectivity;
         } catch (CoolException e) {
             throw e;
         } catch (Exception e) {
@@ -113,10 +135,59 @@
             String message = "杩為�氭�ф祴璇曞け璐�: " + e.getMessage();
             updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY, message, elapsedMs);
             AiMcpMount latest = requireMount(mount.getId(), tenantId);
-            return buildConnectivityDto(latest, message, elapsedMs, 0);
+            AiMcpConnectivityTestDto connectivity = buildConnectivityDto(latest, message, elapsedMs, 0);
+            aiRedisSupport.cacheConnectivity(tenantId, mountId, connectivity);
+            return connectivity;
         }
     }
 
+    /** 瀵硅〃鍗曢噷鐨勮崏绋块厤缃仛涓存椂杩為�氭�ф祴璇曪紝涓嶈惤搴撱�� */
+    @Override
+    public AiMcpConnectivityTestDto testDraftConnectivity(AiMcpMount mount, Long userId, Long tenantId) {
+        ensureTenantId(tenantId);
+        if (userId == null) {
+            throw new CoolException("褰撳墠鐧诲綍鐢ㄦ埛涓嶅瓨鍦�");
+        }
+        if (mount == null) {
+            throw new CoolException("MCP 鎸傝浇鍙傛暟涓嶈兘涓虹┖");
+        }
+        mount.setTenantId(tenantId);
+        fillDefaults(mount);
+        ensureRequiredFields(mount, tenantId);
+        long startedAt = System.currentTimeMillis();
+        try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) {
+            long elapsedMs = System.currentTimeMillis() - startedAt;
+            if (!runtime.getErrors().isEmpty()) {
+                return AiMcpConnectivityTestDto.builder()
+                        .mountId(mount.getId())
+                        .mountName(mount.getName())
+                        .healthStatus(AiDefaults.MCP_HEALTH_UNHEALTHY)
+                        .message(String.join("锛�", runtime.getErrors()))
+                        .initElapsedMs(elapsedMs)
+                        .toolCount(runtime.getToolCallbacks().length)
+                        .testedAt(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
+                        .build();
+            }
+            return AiMcpConnectivityTestDto.builder()
+                    .mountId(mount.getId())
+                    .mountName(mount.getName())
+                    .healthStatus(AiDefaults.MCP_HEALTH_HEALTHY)
+                    .message("鑽夌杩為�氭�ф祴璇曟垚鍔燂紝瑙f瀽鍑� " + runtime.getToolCallbacks().length + " 涓伐鍏�")
+                    .initElapsedMs(elapsedMs)
+                    .toolCount(runtime.getToolCallbacks().length)
+                    .testedAt(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
+                    .build();
+        } catch (CoolException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new CoolException("鑽夌杩為�氭�ф祴璇曞け璐�: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鐩存帴鎵ц鏌愪竴涓伐鍏风殑娴嬭瘯璋冪敤銆�
+     * 璇ユ柟娉曚富瑕佹湇鍔′簬绠$悊绔殑鈥滃伐鍏锋祴璇曗�濋潰鏉匡紝涓嶅弬涓庢寮忓璇濋摼璺��
+     */
     @Override
     public AiMcpToolTestDto testTool(Long mountId, Long userId, Long tenantId, AiMcpToolTestRequest request) {
         if (userId == null) {
@@ -169,7 +240,42 @@
         }
     }
 
+    @Override
+    public boolean save(AiMcpMount entity) {
+        boolean saved = super.save(entity);
+        if (saved && entity != null && entity.getTenantId() != null) {
+            aiRedisSupport.evictMcpMountCaches(entity.getTenantId(), entity.getId());
+        }
+        return saved;
+    }
+
+    @Override
+    public boolean updateById(AiMcpMount entity) {
+        boolean updated = super.updateById(entity);
+        if (updated && entity != null && entity.getTenantId() != null) {
+            aiRedisSupport.evictMcpMountCaches(entity.getTenantId(), entity.getId());
+        }
+        return updated;
+    }
+
+    @Override
+    public boolean removeByIds(java.util.Collection<?> list) {
+        java.util.List<java.io.Serializable> ids = list == null ? java.util.List.of() : list.stream()
+                .filter(java.util.Objects::nonNull)
+                .map(item -> (java.io.Serializable) item)
+                .toList();
+        java.util.List<AiMcpMount> records = this.listByIds(ids);
+        boolean removed = super.removeByIds(list);
+        if (removed) {
+            records.stream()
+                    .filter(java.util.Objects::nonNull)
+                    .forEach(item -> aiRedisSupport.evictMcpMountCaches(item.getTenantId(), item.getId()));
+        }
+        return removed;
+    }
+
     private void fillDefaults(AiMcpMount aiMcpMount) {
+        /** 涓烘寕杞借崏绋胯ˉ榻愮粺涓�榛樿鍊硷紝淇濊瘉鍚庣画杩愯鏃朵唬鐮佷笉闇�瑕侀噸澶嶅垽鏂┖鍊笺�� */
         if (!StringUtils.hasText(aiMcpMount.getTransportType())) {
             aiMcpMount.setTransportType(AiDefaults.MCP_TRANSPORT_SSE_HTTP);
         }
@@ -188,6 +294,10 @@
     }
 
     private void ensureRequiredFields(AiMcpMount aiMcpMount, Long tenantId) {
+        /**
+         * 鎸� transportType 鏍¢獙鎸傝浇蹇呭~椤广��
+         * 杩欓噷鎶娾�滃瓧娈靛悎娉曟�р�濆拰鈥滆法璁板綍鍐茬獊鈥濅竴璧锋敹鍙o紝閬垮厤鏍¢獙閫昏緫鍒嗘暎鍦� controller 灞傘��
+         */
         if (!StringUtils.hasText(aiMcpMount.getName())) {
             throw new CoolException("MCP 鎸傝浇鍚嶇О涓嶈兘涓虹┖");
         }
@@ -212,6 +322,7 @@
     }
 
     private AiMcpMount requireMount(Long mountId, Long tenantId) {
+        /** 鎸夌鎴峰姞杞芥寕杞借褰曪紝涓嶅瓨鍦ㄧ洿鎺ユ姏閿欍�� */
         ensureTenantId(tenantId);
         if (mountId == null) {
             throw new CoolException("MCP 鎸傝浇 ID 涓嶈兘涓虹┖");
@@ -228,6 +339,7 @@
     }
 
     private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount, Long tenantId) {
+        /** 鏍¢獙鍚岀鎴蜂笅鏄惁瀛樺湪涓庡綋鍓嶅唴缃紪鐮佷簰鏂ョ殑鍚敤鎸傝浇銆� */
         if (aiMcpMount.getStatus() == null || aiMcpMount.getStatus() != StatusType.ENABLE.val) {
             return;
         }
@@ -253,19 +365,10 @@
     }
 
     private List<String> resolveConflictCodes(String builtinCode) {
-        List<String> codes = new ArrayList<>();
         if (AiDefaults.MCP_BUILTIN_RSF_WMS.equals(builtinCode)) {
-            codes.add(AiDefaults.MCP_BUILTIN_RSF_WMS_STOCK);
-            codes.add(AiDefaults.MCP_BUILTIN_RSF_WMS_TASK);
-            codes.add(AiDefaults.MCP_BUILTIN_RSF_WMS_BASE);
-            return codes;
+            return List.of();
         }
-        if (AiDefaults.MCP_BUILTIN_RSF_WMS_STOCK.equals(builtinCode)
-                || AiDefaults.MCP_BUILTIN_RSF_WMS_TASK.equals(builtinCode)
-                || AiDefaults.MCP_BUILTIN_RSF_WMS_BASE.equals(builtinCode)) {
-            codes.add(AiDefaults.MCP_BUILTIN_RSF_WMS);
-        }
-        return codes;
+        throw new CoolException("涓嶆敮鎸佺殑鍐呯疆 MCP 缂栫爜: " + builtinCode);
     }
 
     private void ensureTenantId(Long tenantId) {
@@ -274,17 +377,30 @@
         }
     }
 
-    private List<AiMcpToolPreviewDto> buildToolPreviewDtos(ToolCallback[] callbacks) {
+    private List<AiMcpToolPreviewDto> buildToolPreviewDtos(ToolCallback[] callbacks, List<AiMcpToolPreviewDto> governedCatalog) {
+        /** 鎶婂簳灞� ToolCallback 鍜屾不鐞嗙洰褰曚俊鎭嫾鎴愬墠绔渶瑕佺殑缁撴瀯鍖栧伐鍏峰崱鐗囨暟鎹�� */
         List<AiMcpToolPreviewDto> tools = new ArrayList<>();
+        Map<String, AiMcpToolPreviewDto> catalogMap = new java.util.LinkedHashMap<>();
+        for (AiMcpToolPreviewDto item : governedCatalog) {
+            if (item == null || !StringUtils.hasText(item.getName())) {
+                continue;
+            }
+            catalogMap.put(item.getName(), item);
+        }
         for (ToolCallback callback : callbacks) {
             if (callback == null || callback.getToolDefinition() == null) {
                 continue;
             }
+            AiMcpToolPreviewDto governedItem = catalogMap.get(callback.getToolDefinition().name());
             tools.add(AiMcpToolPreviewDto.builder()
                     .name(callback.getToolDefinition().name())
                     .description(callback.getToolDefinition().description())
                     .inputSchema(callback.getToolDefinition().inputSchema())
                     .returnDirect(callback.getToolMetadata() == null ? null : callback.getToolMetadata().returnDirect())
+                    .toolGroup(governedItem == null ? null : governedItem.getToolGroup())
+                    .toolPurpose(governedItem == null ? null : governedItem.getToolPurpose())
+                    .queryBoundary(governedItem == null ? null : governedItem.getQueryBoundary())
+                    .exampleQuestions(governedItem == null ? List.of() : governedItem.getExampleQuestions())
                     .build());
         }
         return tools;

--
Gitblit v1.9.1