
* fix(style): 修复 border 额外的 reset 导致 tailwind border 属性生效异常的问题 * feat: 添加错误预览组件并优化请求错误处理逻辑 * optimize: select and update necessary fields in `ChangePassword` method - Simplify `ChangePassword` method signature by removing unnecessary return type. - Use `Select()` to fetch only the necessary fields (`id` and `password`) from the database. - Replace `Save()` with `Update()` for more efficient password update operation. Note: use `Save(&user)` to update the whole user record, which will cover other unchanged fields as well, causing data inconsistency when data race conditions. * feat(menu): 版本更新为2.8.4,给菜单增加按钮和参数的预制打包 * feat(menu): 恢复空白的配置文件 * Remove unused `SideMode` field from `ChangeUserInfo` struct Remove unused and deprecated `SideMode` field from user request model. * feat(automation): 增加可以自动生成CURD和续写方法的MCP * fix(mcp): 确保始终返回目录结构信息 * fix(mcp): 当不需要创建模块时提前返回目录结构信息 * feat(automation): 增加可以自动生成CURD和续写方法的MCP * feat(mcp): 添加GAG工具用户确认流程和自动字典创建功能 实现三步工作流程:分析、确认、执行 新增自动字典创建功能,当字段使用字典类型时自动检查并创建字典 添加用户确认机制,确保创建操作前获得用户明确确认 * feat(version): 新增版本管理功能,支持创建、导入、导出和下载版本数据 新增版本管理模块,包含以下功能: 1. 版本数据的增删改查 2. 版本创建功能,可选择关联菜单和API 3. 版本导入导出功能 4. 版本JSON数据下载 5. 相关前端页面和接口实现 * refactor(version): 简化版本管理删除逻辑并移除无用字段 移除版本管理中的状态、创建者、更新者和删除者字段 简化删除和批量删除方法的实现,去除事务和用户ID参数 更新自动生成配置的默认值说明 * feat(版本管理): 新增版本管理功能模块 * fix(menu): 修复递归创建菜单时关联数据未正确处理的问题 * feat(mcp): 添加预设计模块扫描功能以支持代码自动生成 在自动化模块分析器中添加对预设计模块的扫描功能,包括: - 新增PredesignedModuleInfo结构体存储模块信息 - 实现scanPredesignedModules方法扫描plugin和model目录 - 在分析响应中添加predesignedModules字段 - 更新帮助文档说明预设计模块的使用方式 这些修改使系统能够识别并利用现有的预设计模块,提高代码生成效率并减少重复工作。 * feat(mcp): 新增API、菜单和字典生成工具并优化自动生成模块 * docs(mcp): 更新菜单和API创建工具的描述信息 * feat(mcp): 添加字典查询工具用于AI生成逻辑时了解可用字典选项 * feat: 在创建菜单/API/模块结果中添加权限分配提醒 为菜单创建、API创建和模块创建的结果消息添加权限分配提醒,帮助用户了解后续需要进行的权限配置步骤 * refactor(mcp): 统一使用WithBoolean替换WithBool并优化错误处理 * docs(mcp): 更新API创建工具的说明和错误处理日志 * feat(mcp): 添加插件意图检测功能并增强验证逻辑 --------- Co-authored-by: Azir <2075125282@qq.com> Co-authored-by: Feng.YJ <jxfengyijie@gmail.com> Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
284 lines
8.0 KiB
Go
284 lines
8.0 KiB
Go
package mcpTool
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
|
||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||
"github.com/mark3labs/mcp-go/mcp"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// 注册工具
|
||
func init() {
|
||
RegisterTool(&MenuCreator{})
|
||
}
|
||
|
||
// MenuCreateRequest 菜单创建请求结构
|
||
type MenuCreateRequest struct {
|
||
ParentId uint `json:"parentId"` // 父菜单ID,0表示根菜单
|
||
Path string `json:"path"` // 路由path
|
||
Name string `json:"name"` // 路由name
|
||
Hidden bool `json:"hidden"` // 是否在列表隐藏
|
||
Component string `json:"component"` // 对应前端文件路径
|
||
Sort int `json:"sort"` // 排序标记
|
||
Title string `json:"title"` // 菜单名
|
||
Icon string `json:"icon"` // 菜单图标
|
||
KeepAlive bool `json:"keepAlive"` // 是否缓存
|
||
DefaultMenu bool `json:"defaultMenu"` // 是否是基础路由
|
||
CloseTab bool `json:"closeTab"` // 自动关闭tab
|
||
ActiveName string `json:"activeName"` // 高亮菜单
|
||
Parameters []MenuParameterRequest `json:"parameters"` // 路由参数
|
||
MenuBtn []MenuButtonRequest `json:"menuBtn"` // 菜单按钮
|
||
}
|
||
|
||
// MenuParameterRequest 菜单参数请求结构
|
||
type MenuParameterRequest struct {
|
||
Type string `json:"type"` // 参数类型:params或query
|
||
Key string `json:"key"` // 参数key
|
||
Value string `json:"value"` // 参数值
|
||
}
|
||
|
||
// MenuButtonRequest 菜单按钮请求结构
|
||
type MenuButtonRequest struct {
|
||
Name string `json:"name"` // 按钮名称
|
||
Desc string `json:"desc"` // 按钮描述
|
||
}
|
||
|
||
// MenuCreateResponse 菜单创建响应结构
|
||
type MenuCreateResponse struct {
|
||
Success bool `json:"success"`
|
||
Message string `json:"message"`
|
||
MenuID uint `json:"menuId"`
|
||
Name string `json:"name"`
|
||
Path string `json:"path"`
|
||
}
|
||
|
||
// MenuCreator 菜单创建工具
|
||
type MenuCreator struct{}
|
||
|
||
// New 创建菜单创建工具
|
||
func (m *MenuCreator) New() mcp.Tool {
|
||
return mcp.NewTool("create_menu",
|
||
mcp.WithDescription("创建前端菜单记录,用于在生成前端页面时自动创建对应的菜单项,只要前端有页面生成,都需要调用此mcp。"),
|
||
mcp.WithNumber("parentId",
|
||
mcp.Description("父菜单ID,0表示根菜单"),
|
||
mcp.DefaultNumber(0),
|
||
),
|
||
mcp.WithString("path",
|
||
mcp.Required(),
|
||
mcp.Description("路由path,如:userList"),
|
||
),
|
||
mcp.WithString("name",
|
||
mcp.Required(),
|
||
mcp.Description("路由name,用于Vue Router,如:userList"),
|
||
),
|
||
mcp.WithBoolean("hidden",
|
||
mcp.Description("是否在菜单列表中隐藏"),
|
||
),
|
||
mcp.WithString("component",
|
||
mcp.Required(),
|
||
mcp.Description("对应的前端Vue组件路径,如:view/user/list.vue"),
|
||
),
|
||
mcp.WithNumber("sort",
|
||
mcp.Description("菜单排序号,数字越小越靠前"),
|
||
mcp.DefaultNumber(1),
|
||
),
|
||
mcp.WithString("title",
|
||
mcp.Required(),
|
||
mcp.Description("菜单显示标题"),
|
||
),
|
||
mcp.WithString("icon",
|
||
mcp.Description("菜单图标名称"),
|
||
mcp.DefaultString("menu"),
|
||
),
|
||
mcp.WithBoolean("keepAlive",
|
||
mcp.Description("是否缓存页面"),
|
||
),
|
||
mcp.WithBoolean("defaultMenu",
|
||
mcp.Description("是否是基础路由"),
|
||
),
|
||
mcp.WithBoolean("closeTab",
|
||
mcp.Description("是否自动关闭tab"),
|
||
),
|
||
mcp.WithString("activeName",
|
||
mcp.Description("高亮菜单名称"),
|
||
),
|
||
mcp.WithString("parameters",
|
||
mcp.Description("路由参数JSON字符串,格式:[{\"type\":\"params\",\"key\":\"id\",\"value\":\"1\"}]"),
|
||
),
|
||
mcp.WithString("menuBtn",
|
||
mcp.Description("菜单按钮JSON字符串,格式:[{\"name\":\"add\",\"desc\":\"新增\"}]"),
|
||
),
|
||
)
|
||
}
|
||
|
||
// Handle 处理菜单创建请求
|
||
func (m *MenuCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||
// 解析请求参数
|
||
args := request.GetArguments()
|
||
|
||
// 必需参数
|
||
path, ok := args["path"].(string)
|
||
if !ok || path == "" {
|
||
return nil, errors.New("path 参数是必需的")
|
||
}
|
||
|
||
name, ok := args["name"].(string)
|
||
if !ok || name == "" {
|
||
return nil, errors.New("name 参数是必需的")
|
||
}
|
||
|
||
component, ok := args["component"].(string)
|
||
if !ok || component == "" {
|
||
return nil, errors.New("component 参数是必需的")
|
||
}
|
||
|
||
title, ok := args["title"].(string)
|
||
if !ok || title == "" {
|
||
return nil, errors.New("title 参数是必需的")
|
||
}
|
||
|
||
// 可选参数
|
||
parentId := uint(0)
|
||
if val, ok := args["parentId"].(float64); ok {
|
||
parentId = uint(val)
|
||
}
|
||
|
||
hidden := false
|
||
if val, ok := args["hidden"].(bool); ok {
|
||
hidden = val
|
||
}
|
||
|
||
sort := 1
|
||
if val, ok := args["sort"].(float64); ok {
|
||
sort = int(val)
|
||
}
|
||
|
||
icon := "menu"
|
||
if val, ok := args["icon"].(string); ok && val != "" {
|
||
icon = val
|
||
}
|
||
|
||
keepAlive := false
|
||
if val, ok := args["keepAlive"].(bool); ok {
|
||
keepAlive = val
|
||
}
|
||
|
||
defaultMenu := false
|
||
if val, ok := args["defaultMenu"].(bool); ok {
|
||
defaultMenu = val
|
||
}
|
||
|
||
closeTab := false
|
||
if val, ok := args["closeTab"].(bool); ok {
|
||
closeTab = val
|
||
}
|
||
|
||
activeName := ""
|
||
if val, ok := args["activeName"].(string); ok {
|
||
activeName = val
|
||
}
|
||
|
||
// 解析参数和按钮
|
||
var parameters []system.SysBaseMenuParameter
|
||
if parametersStr, ok := args["parameters"].(string); ok && parametersStr != "" {
|
||
var paramReqs []MenuParameterRequest
|
||
if err := json.Unmarshal([]byte(parametersStr), ¶mReqs); err != nil {
|
||
return nil, fmt.Errorf("parameters 参数格式错误: %v", err)
|
||
}
|
||
for _, param := range paramReqs {
|
||
parameters = append(parameters, system.SysBaseMenuParameter{
|
||
Type: param.Type,
|
||
Key: param.Key,
|
||
Value: param.Value,
|
||
})
|
||
}
|
||
}
|
||
|
||
var menuBtn []system.SysBaseMenuBtn
|
||
if menuBtnStr, ok := args["menuBtn"].(string); ok && menuBtnStr != "" {
|
||
var btnReqs []MenuButtonRequest
|
||
if err := json.Unmarshal([]byte(menuBtnStr), &btnReqs); err != nil {
|
||
return nil, fmt.Errorf("menuBtn 参数格式错误: %v", err)
|
||
}
|
||
for _, btn := range btnReqs {
|
||
menuBtn = append(menuBtn, system.SysBaseMenuBtn{
|
||
Name: btn.Name,
|
||
Desc: btn.Desc,
|
||
})
|
||
}
|
||
}
|
||
|
||
// 构建菜单对象
|
||
menu := system.SysBaseMenu{
|
||
ParentId: parentId,
|
||
Path: path,
|
||
Name: name,
|
||
Hidden: hidden,
|
||
Component: component,
|
||
Sort: sort,
|
||
Meta: system.Meta{
|
||
Title: title,
|
||
Icon: icon,
|
||
KeepAlive: keepAlive,
|
||
DefaultMenu: defaultMenu,
|
||
CloseTab: closeTab,
|
||
ActiveName: activeName,
|
||
},
|
||
Parameters: parameters,
|
||
MenuBtn: menuBtn,
|
||
}
|
||
|
||
// 创建菜单
|
||
menuService := service.ServiceGroupApp.SystemServiceGroup.MenuService
|
||
err := menuService.AddBaseMenu(menu)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建菜单失败: %v", err)
|
||
}
|
||
|
||
// 获取创建的菜单ID
|
||
var createdMenu system.SysBaseMenu
|
||
err = global.GVA_DB.Where("name = ? AND path = ?", name, path).First(&createdMenu).Error
|
||
if err != nil {
|
||
global.GVA_LOG.Warn("获取创建的菜单ID失败", zap.Error(err))
|
||
}
|
||
|
||
// 构建响应
|
||
response := &MenuCreateResponse{
|
||
Success: true,
|
||
Message: fmt.Sprintf("成功创建菜单 %s", title),
|
||
MenuID: createdMenu.ID,
|
||
Name: name,
|
||
Path: path,
|
||
}
|
||
|
||
resultJSON, err := json.MarshalIndent(response, "", " ")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||
}
|
||
|
||
// 添加权限分配提醒
|
||
permissionReminder := "\n\n⚠️ 重要提醒:\n" +
|
||
"菜单创建完成后,请前往【系统管理】->【角色管理】中为相关角色分配新创建的菜单权限," +
|
||
"以确保用户能够正常访问新菜单。\n" +
|
||
"具体步骤:\n" +
|
||
"1. 进入角色管理页面\n" +
|
||
"2. 选择需要授权的角色\n" +
|
||
"3. 在菜单权限中勾选新创建的菜单项\n" +
|
||
"4. 保存权限配置"
|
||
|
||
return &mcp.CallToolResult{
|
||
Content: []mcp.Content{
|
||
mcp.TextContent{
|
||
Type: "text",
|
||
Text: fmt.Sprintf("菜单创建结果:\n\n%s%s", string(resultJSON), permissionReminder),
|
||
},
|
||
},
|
||
}, nil
|
||
}
|