zhou zhou
17 小时以前 ffbf67765d2ae447d62333eed85100a15685d781
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;
@@ -67,20 +70,53 @@
    @Override
    public List<AiMcpToolPreviewDto> previewTools(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)) {
            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,
                    "工具解析成功,共 " + tools.size() + " 个工具", System.currentTimeMillis() - startedAt);
            return tools;
        } catch (CoolException e) {
            throw e;
        } catch (Exception e) {
            updateHealthStatus(mount.getId(), AiDefaults.MCP_HEALTH_UNHEALTHY,
                    "工具解析失败: " + 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 = "连通性测试成功,解析出 " + 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);
        }
    }
@@ -107,6 +143,7 @@
            throw new CoolException("工具输入 JSON 格式错误: " + e.getMessage());
        }
        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)
@@ -117,11 +154,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());
        }
    }
@@ -137,6 +184,9 @@
        }
        if (aiMcpMount.getStatus() == null) {
            aiMcpMount.setStatus(StatusType.ENABLE.val);
        }
        if (!StringUtils.hasText(aiMcpMount.getHealthStatus())) {
            aiMcpMount.setHealthStatus(AiDefaults.MCP_HEALTH_NOT_TESTED);
        }
    }
@@ -226,4 +276,53 @@
            throw new CoolException("当前租户不存在");
        }
    }
    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();
    }
}