From b0728aba5c01842e24da3cff04e44be06c6bb655 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期四, 19 三月 2026 13:38:38 +0800
Subject: [PATCH] #AI.去除多余mcp

---
 rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiMcpMountServiceImpl.java |  223 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 188 insertions(+), 35 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 822d55c..e358929 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
@@ -1,10 +1,12 @@
 package com.vincent.rsf.server.ai.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.ai.config.AiDefaults;
+import com.vincent.rsf.server.ai.dto.AiMcpConnectivityTestDto;
 import com.vincent.rsf.server.ai.dto.AiMcpToolPreviewDto;
 import com.vincent.rsf.server.ai.dto.AiMcpToolTestDto;
 import com.vincent.rsf.server.ai.dto.AiMcpToolTestRequest;
@@ -22,6 +24,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -34,45 +37,128 @@
     private final ObjectMapper objectMapper;
 
     @Override
-    public List<AiMcpMount> listActiveMounts() {
+    public List<AiMcpMount> listActiveMounts(Long tenantId) {
+        ensureTenantId(tenantId);
         return this.list(new LambdaQueryWrapper<AiMcpMount>()
+                .eq(AiMcpMount::getTenantId, tenantId)
                 .eq(AiMcpMount::getStatus, StatusType.ENABLE.val)
+                .eq(AiMcpMount::getDeleted, 0)
                 .orderByAsc(AiMcpMount::getSort)
                 .orderByAsc(AiMcpMount::getId));
     }
 
     @Override
-    public void validateBeforeSave(AiMcpMount aiMcpMount) {
+    public void validateBeforeSave(AiMcpMount aiMcpMount, Long tenantId) {
+        ensureTenantId(tenantId);
+        aiMcpMount.setTenantId(tenantId);
         fillDefaults(aiMcpMount);
-        ensureRequiredFields(aiMcpMount);
+        ensureRequiredFields(aiMcpMount, tenantId);
     }
 
     @Override
-    public void validateBeforeUpdate(AiMcpMount aiMcpMount) {
+    public void validateBeforeUpdate(AiMcpMount aiMcpMount, Long tenantId) {
+        ensureTenantId(tenantId);
         fillDefaults(aiMcpMount);
         if (aiMcpMount.getId() == null) {
             throw new CoolException("MCP 鎸傝浇 ID 涓嶈兘涓虹┖");
         }
-        ensureRequiredFields(aiMcpMount);
+        AiMcpMount current = requireMount(aiMcpMount.getId(), tenantId);
+        aiMcpMount.setTenantId(current.getTenantId());
+        ensureRequiredFields(aiMcpMount, tenantId);
     }
 
     @Override
     public List<AiMcpToolPreviewDto> previewTools(Long mountId, Long userId, Long tenantId) {
-        AiMcpMount mount = requireMount(mountId);
+        AiMcpMount mount = requireMount(mountId, tenantId);
+        long startedAt = System.currentTimeMillis();
         try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) {
-            List<AiMcpToolPreviewDto> tools = new ArrayList<>();
-            for (ToolCallback callback : runtime.getToolCallbacks()) {
-                if (callback == null || callback.getToolDefinition() == null) {
-                    continue;
-                }
-                tools.add(AiMcpToolPreviewDto.builder()
-                        .name(callback.getToolDefinition().name())
-                        .description(callback.getToolDefinition().description())
-                        .inputSchema(callback.getToolDefinition().inputSchema())
-                        .returnDirect(callback.getToolMetadata() == null ? null : callback.getToolMetadata().returnDirect())
-                        .build());
+            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);
+                throw new CoolException(message);
             }
+            updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_HEALTHY,
+                    "宸ュ叿瑙f瀽鎴愬姛锛屽叡 " + tools.size() + " 涓伐鍏�", System.currentTimeMillis() - startedAt);
             return tools;
+        } catch (CoolException e) {
+            throw e;
+        } catch (Exception e) {
+            updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY,
+                    "宸ュ叿瑙f瀽澶辫触: " + e.getMessage(), System.currentTimeMillis() - startedAt);
+            throw new CoolException("鑾峰彇宸ュ叿鍒楄〃澶辫触: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public AiMcpConnectivityTestDto testConnectivity(Long mountId, Long userId, Long tenantId) {
+        AiMcpMount mount = requireMount(mountId, tenantId);
+        long startedAt = System.currentTimeMillis();
+        try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) {
+            long elapsedMs = System.currentTimeMillis() - startedAt;
+            if (!runtime.getErrors().isEmpty()) {
+                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);
+            }
+            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);
+        } catch (CoolException e) {
+            throw e;
+        } catch (Exception e) {
+            long elapsedMs = System.currentTimeMillis() - startedAt;
+            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);
+        }
+    }
+
+    @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());
         }
     }
 
@@ -98,7 +184,8 @@
         } catch (Exception e) {
             throw new CoolException("宸ュ叿杈撳叆 JSON 鏍煎紡閿欒: " + e.getMessage());
         }
-        AiMcpMount mount = requireMount(mountId);
+        AiMcpMount mount = requireMount(mountId, tenantId);
+        long startedAt = System.currentTimeMillis();
         try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) {
             ToolCallback callback = Arrays.stream(runtime.getToolCallbacks())
                     .filter(item -> item != null && item.getToolDefinition() != null)
@@ -109,11 +196,21 @@
                     request.getInputJson(),
                     new ToolContext(Map.of("userId", userId, "tenantId", tenantId, "mountId", mountId))
             );
+            updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_HEALTHY,
+                    "宸ュ叿娴嬭瘯鎴愬姛: " + request.getToolName(), System.currentTimeMillis() - startedAt);
             return AiMcpToolTestDto.builder()
                     .toolName(request.getToolName())
                     .inputJson(request.getInputJson())
                     .output(output)
                     .build();
+        } catch (CoolException e) {
+            updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY,
+                    "宸ュ叿娴嬭瘯澶辫触: " + e.getMessage(), System.currentTimeMillis() - startedAt);
+            throw e;
+        } catch (Exception e) {
+            updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY,
+                    "宸ュ叿娴嬭瘯澶辫触: " + e.getMessage(), System.currentTimeMillis() - startedAt);
+            throw new CoolException("宸ュ叿娴嬭瘯澶辫触: " + e.getMessage());
         }
     }
 
@@ -130,15 +227,18 @@
         if (aiMcpMount.getStatus() == null) {
             aiMcpMount.setStatus(StatusType.ENABLE.val);
         }
+        if (!StringUtils.hasText(aiMcpMount.getHealthStatus())) {
+            aiMcpMount.setHealthStatus(AiDefaults.MCP_HEALTH_NOT_TESTED);
+        }
     }
 
-    private void ensureRequiredFields(AiMcpMount aiMcpMount) {
+    private void ensureRequiredFields(AiMcpMount aiMcpMount, Long tenantId) {
         if (!StringUtils.hasText(aiMcpMount.getName())) {
             throw new CoolException("MCP 鎸傝浇鍚嶇О涓嶈兘涓虹┖");
         }
         if (AiDefaults.MCP_TRANSPORT_BUILTIN.equals(aiMcpMount.getTransportType())) {
             builtinMcpToolRegistry.validateBuiltinCode(aiMcpMount.getBuiltinCode());
-            ensureBuiltinConflictFree(aiMcpMount);
+            ensureBuiltinConflictFree(aiMcpMount, tenantId);
             return;
         }
         if (AiDefaults.MCP_TRANSPORT_SSE_HTTP.equals(aiMcpMount.getTransportType())) {
@@ -156,18 +256,23 @@
         throw new CoolException("涓嶆敮鎸佺殑 MCP 浼犺緭绫诲瀷: " + aiMcpMount.getTransportType());
     }
 
-    private AiMcpMount requireMount(Long mountId) {
+    private AiMcpMount requireMount(Long mountId, Long tenantId) {
+        ensureTenantId(tenantId);
         if (mountId == null) {
             throw new CoolException("MCP 鎸傝浇 ID 涓嶈兘涓虹┖");
         }
-        AiMcpMount mount = this.getById(mountId);
-        if (mount == null || (mount.getDeleted() != null && mount.getDeleted() == 1)) {
+        AiMcpMount mount = this.getOne(new LambdaQueryWrapper<AiMcpMount>()
+                .eq(AiMcpMount::getId, mountId)
+                .eq(AiMcpMount::getTenantId, tenantId)
+                .eq(AiMcpMount::getDeleted, 0)
+                .last("limit 1"));
+        if (mount == null) {
             throw new CoolException("MCP 鎸傝浇涓嶅瓨鍦�");
         }
         return mount;
     }
 
-    private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount) {
+    private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount, Long tenantId) {
         if (aiMcpMount.getStatus() == null || aiMcpMount.getStatus() != StatusType.ENABLE.val) {
             return;
         }
@@ -176,8 +281,10 @@
             return;
         }
         LambdaQueryWrapper<AiMcpMount> queryWrapper = new LambdaQueryWrapper<AiMcpMount>()
+                .eq(AiMcpMount::getTenantId, tenantId)
                 .eq(AiMcpMount::getTransportType, AiDefaults.MCP_TRANSPORT_BUILTIN)
                 .eq(AiMcpMount::getStatus, StatusType.ENABLE.val)
+                .eq(AiMcpMount::getDeleted, 0)
                 .in(AiMcpMount::getBuiltinCode, conflictCodes);
         if (aiMcpMount.getId() != null) {
             queryWrapper.ne(AiMcpMount::getId, aiMcpMount.getId());
@@ -191,18 +298,64 @@
     }
 
     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);
+        throw new CoolException("涓嶆敮鎸佺殑鍐呯疆 MCP 缂栫爜: " + builtinCode);
+    }
+
+    private void ensureTenantId(Long tenantId) {
+        if (tenantId == null) {
+            throw new CoolException("褰撳墠绉熸埛涓嶅瓨鍦�");
         }
-        return codes;
+    }
+
+    private List<AiMcpToolPreviewDto> buildToolPreviewDtos(ToolCallback[] callbacks, List<AiMcpToolPreviewDto> governedCatalog) {
+        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;
+    }
+
+    private void updateHealthStatus(Long mountId, String healthStatus, String message, Long initElapsedMs) {
+        this.update(new LambdaUpdateWrapper<AiMcpMount>()
+                .eq(AiMcpMount::getId, mountId)
+                .set(AiMcpMount::getHealthStatus, healthStatus)
+                .set(AiMcpMount::getLastTestTime, new Date())
+                .set(AiMcpMount::getLastTestMessage, message)
+                .set(AiMcpMount::getLastInitElapsedMs, initElapsedMs));
+    }
+
+    private AiMcpConnectivityTestDto buildConnectivityDto(AiMcpMount mount, String message, Long initElapsedMs, Integer toolCount) {
+        return AiMcpConnectivityTestDto.builder()
+                .mountId(mount.getId())
+                .mountName(mount.getName())
+                .healthStatus(mount.getHealthStatus())
+                .message(message)
+                .initElapsedMs(initElapsedMs)
+                .toolCount(toolCount)
+                .testedAt(mount.getLastTestTime$())
+                .build();
     }
 }

--
Gitblit v1.9.1