From 98912594522e3438edc995896ca9324f41fe7bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=92=E4=BA=A6?= Date: Sun, 10 Aug 2025 20:50:04 +0800 Subject: [PATCH] =?UTF-8?q?=20mcp=20=E4=BF=A1=E6=81=AF=20=E5=A2=9E?= =?UTF-8?q?=E5=88=A0=E6=94=B9=E6=9F=A5=20=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/ruoyi/domain/McpInfo.java | 65 ++++++++++ .../java/org/ruoyi/domain/bo/McpInfoBo.java | 59 +++++++++ .../java/org/ruoyi/domain/vo/McpInfoVo.java | 64 ++++++++++ .../java/org/ruoyi/mapper/McpInfoMapper.java | 18 +++ .../main/resources/mapper/McpInfoMapper.xml | 7 ++ .../mcp/controller/McpInfoController.java | 106 +++++++++++++++++ .../org/ruoyi/mcp/service/McpInfoService.java | 48 ++++++++ .../mcp/service/impl/McpInfoServiceImpl.java | 112 ++++++++++++++++++ script/sql/update/mcp_info_menu.sql | 43 +++++++ 9 files changed, 522 insertions(+) create mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/McpInfo.java create mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/McpInfoBo.java create mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/McpInfoVo.java create mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/McpInfoMapper.java create mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/McpInfoMapper.xml create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/controller/McpInfoController.java create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/McpInfoService.java create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/impl/McpInfoServiceImpl.java create mode 100644 script/sql/update/mcp_info_menu.sql diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/McpInfo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/McpInfo.java new file mode 100644 index 00000000..11b3a096 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/McpInfo.java @@ -0,0 +1,65 @@ +package org.ruoyi.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.ruoyi.annotation.DataColumn; +import org.ruoyi.core.domain.BaseEntity; + +import java.util.Date; + +/** + * MCP对象 mcp_info + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("mcp_info") +public class McpInfo extends BaseEntity { + + + /** + * id + */ + @TableId(value = "mcp_id", type = IdType.AUTO) + private Integer mcpId; + + /** + * 服务器名称 + */ + private String serverName; + + /** + * 链接方式 + */ + + private String transportType; + + /** + * Command + */ + private String command; + + /** + * Args + */ + private String arguments; + + /** + * Env + */ + private String env; + + /** + * 是否启用 + */ + private Boolean status; + + + + +} diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/McpInfoBo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/McpInfoBo.java new file mode 100644 index 00000000..9aba1209 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/McpInfoBo.java @@ -0,0 +1,59 @@ +package org.ruoyi.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.ruoyi.domain.McpInfo; + + +import java.io.Serializable; + +/** + * MCP业务对象 mcp_info + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +@Data + +@AutoMapper(target = McpInfo.class, reverseConvertGenerate = false) +public class McpInfoBo implements Serializable { + + /** + * id + */ + @NotNull(message = "id不能为空" ) + private Integer mcpId; + + /** + * 服务器名称 + */ + private String serverName; + + /** + * 链接方式 + */ + private String transportType; + + /** + * Command + */ + private String command; + + /** + * Args + */ + private String arguments; + + /** + * Env + */ + private String env; + + /** + * 是否启用 + */ + private Boolean status; + + +} diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/McpInfoVo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/McpInfoVo.java new file mode 100644 index 00000000..610b0a8e --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/McpInfoVo.java @@ -0,0 +1,64 @@ +package org.ruoyi.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.ruoyi.common.excel.annotation.ExcelDictFormat; +import org.ruoyi.common.excel.convert.ExcelDictConvert; +import org.ruoyi.domain.McpInfo; + +import java.io.Serializable; + + +/** + * MCP视图对象 mcp_info + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = McpInfo.class) +public class McpInfoVo implements Serializable { + private Integer mcpId; + + /** + * 服务器名称 + */ + @ExcelProperty(value = "服务器名称") + private String serverName; + + /** + * 链接方式 + */ + @ExcelProperty(value = "链接方式", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "mcp_transport_type") + private String transportType; + + /** + * Command + */ + @ExcelProperty(value = "Command") + private String command; + + /** + * Args + */ + @ExcelProperty(value = "Args") + private String arguments; + + /** + * Env + */ + @ExcelProperty(value = "Env") + private String env; + + /** + * 是否启用 + */ + @ExcelProperty(value = "是否启用") + private Boolean status; + + +} diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/McpInfoMapper.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/McpInfoMapper.java new file mode 100644 index 00000000..0ff4ad69 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/McpInfoMapper.java @@ -0,0 +1,18 @@ +package org.ruoyi.mapper; + + +import org.ruoyi.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Mapper; +import org.ruoyi.domain.McpInfo; +import org.ruoyi.domain.vo.McpInfoVo; + +/** + * MCPMapper接口 + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +@Mapper +public interface McpInfoMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/McpInfoMapper.xml b/ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/McpInfoMapper.xml new file mode 100644 index 00000000..f2a28e2a --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/McpInfoMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/controller/McpInfoController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/controller/McpInfoController.java new file mode 100644 index 00000000..524c6f51 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/controller/McpInfoController.java @@ -0,0 +1,106 @@ +package org.ruoyi.mcp.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.ruoyi.domain.bo.McpInfoBo; +import org.ruoyi.domain.vo.McpInfoVo; +import org.ruoyi.mcp.service.McpInfoService; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.ruoyi.common.idempotent.annotation.RepeatSubmit; +import org.ruoyi.common.log.annotation.Log; +import org.ruoyi.common.web.core.BaseController; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.common.core.domain.R; +import org.ruoyi.common.core.validate.AddGroup; +import org.ruoyi.common.core.validate.EditGroup; +import org.ruoyi.common.log.enums.BusinessType; +import org.ruoyi.common.excel.utils.ExcelUtil; + +import org.ruoyi.core.page.TableDataInfo; + +/** + * MCP + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operator/mcpInfo") +public class McpInfoController extends BaseController { + + private final McpInfoService mcpInfoService; + +/** + * 查询MCP列表 + */ +@SaCheckPermission("operator:mcpInfo:list") +@GetMapping("/list") + public TableDataInfo list(McpInfoBo bo, PageQuery pageQuery) { + return mcpInfoService.queryPageList(bo, pageQuery); + } + + /** + * 导出MCP列表 + */ + @SaCheckPermission("operator:mcpInfo:export") + @Log(title = "MCP", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(McpInfoBo bo, HttpServletResponse response) { + List list = mcpInfoService.queryList(bo); + ExcelUtil.exportExcel(list, "MCP", McpInfoVo.class, response); + } + + /** + * 获取MCP详细信息 + * + * @param mcpId 主键 + */ + @SaCheckPermission("operator:mcpInfo:query") + @GetMapping("/{mcpId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Integer mcpId) { + return R.ok(mcpInfoService.queryById(mcpId)); + } + + /** + * 新增MCP + */ + @SaCheckPermission("operator:mcpInfo:add") + @Log(title = "MCP", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody McpInfoBo bo) { + return toAjax(mcpInfoService.insertByBo(bo)); + } + + /** + * 修改MCP + */ + @SaCheckPermission("operator:mcpInfo:edit") + @Log(title = "MCP", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody McpInfoBo bo) { + return toAjax(mcpInfoService.updateByBo(bo)); + } + + /** + * 删除MCP + * + * @param mcpIds 主键串 + */ + @SaCheckPermission("operator:mcpInfo:remove") + @Log(title = "MCP", businessType = BusinessType.DELETE) + @DeleteMapping("/{mcpIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Integer[] mcpIds) { + return toAjax(mcpInfoService.deleteWithValidByIds(List.of(mcpIds), true)); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/McpInfoService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/McpInfoService.java new file mode 100644 index 00000000..b2c2ad0d --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/McpInfoService.java @@ -0,0 +1,48 @@ +package org.ruoyi.mcp.service; + + import org.ruoyi.core.page.TableDataInfo; + import org.ruoyi.core.page.PageQuery; + import org.ruoyi.domain.bo.McpInfoBo; + import org.ruoyi.domain.vo.McpInfoVo; + + import java.util.Collection; +import java.util.List; + +/** + * MCPService接口 + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +public interface McpInfoService { + + /** + * 查询MCP + */ + McpInfoVo queryById(Integer mcpId); + + /** + * 查询MCP列表 + */ + TableDataInfo queryPageList(McpInfoBo bo, PageQuery pageQuery); + + /** + * 查询MCP列表 + */ + List queryList(McpInfoBo bo); + + /** + * 新增MCP + */ + Boolean insertByBo(McpInfoBo bo); + + /** + * 修改MCP + */ + Boolean updateByBo(McpInfoBo bo); + + /** + * 校验并批量删除MCP信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/impl/McpInfoServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/impl/McpInfoServiceImpl.java new file mode 100644 index 00000000..627b83ff --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/mcp/service/impl/McpInfoServiceImpl.java @@ -0,0 +1,112 @@ +package org.ruoyi.mcp.service.impl; + +import org.ruoyi.common.core.utils.MapstructUtils; + import org.ruoyi.core.page.TableDataInfo; + import org.ruoyi.core.page.PageQuery; + import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.ruoyi.domain.McpInfo; +import org.ruoyi.domain.bo.McpInfoBo; +import org.ruoyi.domain.vo.McpInfoVo; +import org.ruoyi.mapper.McpInfoMapper; +import org.ruoyi.mcp.service.McpInfoService; +import org.springframework.stereotype.Service; + +import org.ruoyi.common.core.utils.StringUtils; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * MCPService业务层处理 + * + * @author ageerle + * @date Sat Aug 09 16:50:58 CST 2025 + */ +@RequiredArgsConstructor +@Service +public class McpInfoServiceImpl implements McpInfoService { + + private final McpInfoMapper baseMapper; + + /** + * 查询MCP + */ + @Override + public McpInfoVo queryById(Integer mcpId) { + return baseMapper.selectVoById(mcpId); + } + + /** + * 查询MCP列表 + */ + @Override + public TableDataInfo queryPageList(McpInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询MCP列表 + */ + @Override + public List queryList(McpInfoBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(McpInfoBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getServerName()), McpInfo::getServerName, bo.getServerName()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportType()), McpInfo::getTransportType, bo.getTransportType()); + lqw.eq(StringUtils.isNotBlank(bo.getCommand()), McpInfo::getCommand, bo.getCommand()); + lqw.eq(bo.getStatus() != null, McpInfo::getStatus, bo.getStatus()); + return lqw; + } + + /** + * 新增MCP + */ + @Override + public Boolean insertByBo(McpInfoBo bo) { + McpInfo add = MapstructUtils.convert(bo, McpInfo. class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setMcpId(add.getMcpId()); + } + return flag; + } + + /** + * 修改MCP + */ + @Override + public Boolean updateByBo(McpInfoBo bo) { + McpInfo update = MapstructUtils.convert(bo, McpInfo. class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(McpInfo entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除MCP + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/script/sql/update/mcp_info_menu.sql b/script/sql/update/mcp_info_menu.sql new file mode 100644 index 00000000..b26de439 --- /dev/null +++ b/script/sql/update/mcp_info_menu.sql @@ -0,0 +1,43 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(1954103099019309056, 'MCP', '2000', '1', 'mcpInfo', 'operator/mcpInfo/index', 1, 0, 'C', '0', '0', 'operator:mcpInfo:list', '#', 103, 1, sysdate(), null, null, 'MCP菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(1954103099019309057, 'MCP查询', 1954103099019309056, '1', '#', '', 1, 0, 'F', '0', '0', 'operator:mcpInfo:query', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(1954103099019309058, 'MCP新增', 1954103099019309056, '2', '#', '', 1, 0, 'F', '0', '0', 'operator:mcpInfo:add', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(1954103099019309059, 'MCP修改', 1954103099019309056, '3', '#', '', 1, 0, 'F', '0', '0', 'operator:mcpInfo:edit', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(1954103099019309060, 'MCP删除', 1954103099019309056, '4', '#', '', 1, 0, 'F', '0', '0', 'operator:mcpInfo:remove', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(1954103099019309061, 'MCP导出', 1954103099019309056, '5', '#', '', 1, 0, 'F', '0', '0', 'operator:mcpInfo:export', '#', 103, 1, sysdate(), null, null, ''); + + +-- mcp_info ddl +CREATE TABLE `mcp_info` ( + `mcp_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `server_name` varchar(50) DEFAULT NULL COMMENT '服务器名称', + `transport_type` varchar(255) DEFAULT NULL COMMENT '链接方式', + `command` varchar(255) DEFAULT NULL COMMENT 'Command', + `arguments` varchar(255) DEFAULT NULL COMMENT 'Args', + `env` varchar(255) DEFAULT NULL COMMENT 'Env', + `status` tinyint(1) DEFAULT NULL COMMENT '是否启用', + `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', + `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(255) DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`mcp_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; + +INSERT INTO `ruoyi-ai`.`sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1954098808913211393, '000000', 0, 'STDIO', 'STDIO', 'mcp_transport_type', NULL, '', 'N', '0', NULL, NULL, '2025-08-09 16:33:56', 1, '2025-08-09 16:34:19', NULL); +INSERT INTO `ruoyi-ai`.`sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1954098960432443394, '000000', 1, 'SSE', 'SSE', 'mcp_transport_type', NULL, '', 'N', '0', NULL, NULL, '2025-08-09 16:34:32', NULL, '2025-08-09 16:34:32', NULL); +INSERT INTO `ruoyi-ai`.`sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1954099421436784642, '000000', 2, 'HTTP', 'HTTP', 'mcp_transport_type', NULL, '', 'N', '0', NULL, NULL, '2025-08-09 16:36:22', NULL, '2025-08-09 16:36:22', NULL); +INSERT INTO `ruoyi-ai`.`sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `status`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1954098639622713345, '000000', 'mcp链接方式', 'mcp_transport_type', '0', NULL, NULL, '2025-08-09 16:33:16', NULL, '2025-08-09 16:33:16', NULL);