diff --git a/server/api/v1/system/auto_code_mcp.go b/server/api/v1/system/auto_code_mcp.go
new file mode 100644
index 0000000000..14dc315719
--- /dev/null
+++ b/server/api/v1/system/auto_code_mcp.go
@@ -0,0 +1,34 @@
+package system
+
+import (
+ "github.com/flipped-aurora/gin-vue-admin/server/global"
+ "github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
+ "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
+ "github.com/gin-gonic/gin"
+)
+
+// Create
+// @Tags mcp
+// @Summary 自动McpTool
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.AutoMcpTool true "创建自动代码"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
+// @Router /autoCode/mcp [post]
+func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
+ var info request.AutoMcpTool
+ err := c.ShouldBindJSON(&info)
+ if err != nil {
+ response.FailWithMessage(err.Error(), c)
+ return
+ }
+
+ toolFilePath, err := autoCodeTemplateService.CreateMcp(c.Request.Context(), info)
+ if err != nil {
+ response.FailWithMessage("创建失败", c)
+ global.GVA_LOG.Error(err.Error())
+ return
+ }
+ response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c)
+}
diff --git a/server/api/v1/system/sys_operation_record.go b/server/api/v1/system/sys_operation_record.go
index 40daeb98db..c88511f765 100644
--- a/server/api/v1/system/sys_operation_record.go
+++ b/server/api/v1/system/sys_operation_record.go
@@ -13,31 +13,6 @@ import (
type OperationRecordApi struct{}
-// CreateSysOperationRecord
-// @Tags SysOperationRecord
-// @Summary 创建SysOperationRecord
-// @Security ApiKeyAuth
-// @accept application/json
-// @Produce application/json
-// @Param data body system.SysOperationRecord true "创建SysOperationRecord"
-// @Success 200 {object} response.Response{msg=string} "创建SysOperationRecord"
-// @Router /sysOperationRecord/createSysOperationRecord [post]
-func (s *OperationRecordApi) CreateSysOperationRecord(c *gin.Context) {
- var sysOperationRecord system.SysOperationRecord
- err := c.ShouldBindJSON(&sysOperationRecord)
- if err != nil {
- response.FailWithMessage(err.Error(), c)
- return
- }
- err = operationRecordService.CreateSysOperationRecord(sysOperationRecord)
- if err != nil {
- global.GVA_LOG.Error("创建失败!", zap.Error(err))
- response.FailWithMessage("创建失败", c)
- return
- }
- response.OkWithMessage("创建成功", c)
-}
-
// DeleteSysOperationRecord
// @Tags SysOperationRecord
// @Summary 删除SysOperationRecord
diff --git a/server/api/v1/system/sys_system.go b/server/api/v1/system/sys_system.go
index aa41c2f469..6dabb0abed 100644
--- a/server/api/v1/system/sys_system.go
+++ b/server/api/v1/system/sys_system.go
@@ -55,19 +55,20 @@ func (s *SystemApi) SetSystemConfig(c *gin.Context) {
// ReloadSystem
// @Tags System
-// @Summary 重启系统
+// @Summary 重载系统
// @Security ApiKeyAuth
// @Produce application/json
-// @Success 200 {object} response.Response{msg=string} "重启系统"
+// @Success 200 {object} response.Response{msg=string} "重载系统"
// @Router /system/reloadSystem [post]
func (s *SystemApi) ReloadSystem(c *gin.Context) {
- err := utils.Reload()
+ // 触发系统重载事件
+ err := utils.GlobalSystemEvents.TriggerReload()
if err != nil {
- global.GVA_LOG.Error("重启系统失败!", zap.Error(err))
- response.FailWithMessage("重启系统失败", c)
+ global.GVA_LOG.Error("重载系统失败!", zap.Error(err))
+ response.FailWithMessage("重载系统失败:"+err.Error(), c)
return
}
- response.OkWithMessage("重启系统成功", c)
+ response.OkWithMessage("重载系统成功", c)
}
// GetServerInfo
diff --git a/server/api/v1/system/sys_user.go b/server/api/v1/system/sys_user.go
index e531bf3fd1..ae763f8bb5 100644
--- a/server/api/v1/system/sys_user.go
+++ b/server/api/v1/system/sys_user.go
@@ -93,7 +93,7 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
}
if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
- if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
+ if err := utils.SetRedisJWT(token, user.Username); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
response.FailWithMessage("设置登录状态失败", c)
return
@@ -114,7 +114,7 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
response.FailWithMessage("jwt作废失败", c)
return
}
- if err := jwtService.SetRedisJWT(token, user.GetUsername()); err != nil {
+ if err := utils.SetRedisJWT(token, user.GetUsername()); err != nil {
response.FailWithMessage("设置登录状态失败", c)
return
}
diff --git a/server/config.yaml b/server/config.yaml
index c3ef828809..758d6dcb2f 100644
--- a/server/config.yaml
+++ b/server/config.yaml
@@ -274,4 +274,10 @@ cors:
allow-headers: content-type
allow-methods: GET, POST
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
- allow-credentials: true # 布尔值
\ No newline at end of file
+ allow-credentials: true # 布尔值
+mcp:
+ name: GVA_MCP
+ version: v1.0.0
+ sse_path: /sse
+ message_path: /message
+ url_prefix: ''
diff --git a/server/config/config.go b/server/config/config.go
index 15da9c7db4..3abac5ac03 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -34,4 +34,7 @@ type Server struct {
// 跨域配置
Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
+
+ // MCP配置
+ MCP MCP `mapstructure:"mcp" json:"mcp" yaml:"mcp"`
}
diff --git a/server/config/mcp.go b/server/config/mcp.go
new file mode 100644
index 0000000000..81f4bff307
--- /dev/null
+++ b/server/config/mcp.go
@@ -0,0 +1,9 @@
+package config
+
+type MCP struct {
+ Name string `mapstructure:"name" json:"name" yaml:"name"` // MCP名称
+ Version string `mapstructure:"version" json:"version" yaml:"version"` // MCP版本
+ SSEPath string `mapstructure:"sse_path" json:"sse_path" yaml:"sse_path"` // SSE路径
+ MessagePath string `mapstructure:"message_path" json:"message_path" yaml:"message_path"` // 消息路径
+ UrlPrefix string `mapstructure:"url_prefix" json:"url_prefix" yaml:"url_prefix"` // URL前缀
+}
diff --git a/server/core/server.go b/server/core/server.go
index 64c5ff04e5..3867a15ab0 100644
--- a/server/core/server.go
+++ b/server/core/server.go
@@ -6,13 +6,10 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/initialize"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"go.uber.org/zap"
+ "time"
)
-type server interface {
- ListenAndServe() error
-}
-
-func RunWindowsServer() {
+func RunServer() {
if global.GVA_CONFIG.System.UseRedis {
// 初始化redis服务
initialize.Redis()
@@ -35,23 +32,22 @@ func RunWindowsServer() {
Router := initialize.Routers()
address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
- s := initServer(address, Router)
-
- global.GVA_LOG.Info("server run success on ", zap.String("address", address))
fmt.Printf(`
欢迎使用 gin-vue-admin
- 当前版本:v2.8.1
+ 当前版本:v2.8.2
加群方式:微信号:shouzi_1994 QQ群:470239250
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
GVA讨论社区:https://support.qq.com/products/371961
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
+ 默认MCP SSE地址:http://127.0.0.1%s%s
+ 默认MCP Message地址:http://127.0.0.1%s%s
默认前端文件运行地址:http://127.0.0.1:8080
--------------------------------------版权声明--------------------------------------
** 版权所有方:flipped-aurora开源团队 **
** 版权持有公司:北京翻转极光科技有限责任公司 **
** 剔除授权标识需购买商用授权:https://gin-vue-admin.com/empower/index.html **
-`, address)
- global.GVA_LOG.Error(s.ListenAndServe().Error())
+`, address, address, global.GVA_CONFIG.MCP.SSEPath, address, global.GVA_CONFIG.MCP.MessagePath)
+ initServer(address, Router, 10*time.Minute, 10*time.Minute)
}
diff --git a/server/core/server_other.go b/server/core/server_other.go
deleted file mode 100644
index 83645fced0..0000000000
--- a/server/core/server_other.go
+++ /dev/null
@@ -1,19 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package core
-
-import (
- "time"
-
- "github.com/fvbock/endless"
- "github.com/gin-gonic/gin"
-)
-
-func initServer(address string, router *gin.Engine) server {
- s := endless.NewServer(address, router)
- s.ReadHeaderTimeout = 10 * time.Minute
- s.WriteTimeout = 10 * time.Minute
- s.MaxHeaderBytes = 1 << 20
- return s
-}
diff --git a/server/core/server_run.go b/server/core/server_run.go
new file mode 100644
index 0000000000..067ce6b6e6
--- /dev/null
+++ b/server/core/server_run.go
@@ -0,0 +1,60 @@
+package core
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ "go.uber.org/zap"
+)
+
+type server interface {
+ ListenAndServe() error
+ Shutdown(context.Context) error
+}
+
+// initServer 启动服务并实现优雅关闭
+func initServer(address string, router *gin.Engine, readTimeout, writeTimeout time.Duration) {
+ // 创建服务
+ srv := &http.Server{
+ Addr: address,
+ Handler: router,
+ ReadTimeout: readTimeout,
+ WriteTimeout: writeTimeout,
+ MaxHeaderBytes: 1 << 20,
+ }
+
+ // 在goroutine中启动服务
+ go func() {
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ fmt.Printf("listen: %s\n", err)
+ zap.L().Error("server启动失败", zap.Error(err))
+ os.Exit(1)
+ }
+ }()
+
+ // 等待中断信号以优雅地关闭服务器
+ quit := make(chan os.Signal, 1)
+ // kill (无参数) 默认发送 syscall.SIGTERM
+ // kill -2 发送 syscall.SIGINT
+ // kill -9 发送 syscall.SIGKILL,但是无法被捕获,所以不需要添加
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+ zap.L().Info("关闭WEB服务...")
+
+ // 设置5秒的超时时间
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+
+ defer cancel()
+
+ if err := srv.Shutdown(ctx); err != nil {
+ zap.L().Fatal("WEB服务关闭异常", zap.Error(err))
+ }
+
+ zap.L().Info("WEB服务已关闭")
+}
diff --git a/server/core/server_win.go b/server/core/server_win.go
deleted file mode 100644
index 20cf44b9f2..0000000000
--- a/server/core/server_win.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build windows
-// +build windows
-
-package core
-
-import (
- "net/http"
- "time"
-
- "github.com/gin-gonic/gin"
-)
-
-func initServer(address string, router *gin.Engine) server {
- return &http.Server{
- Addr: address,
- Handler: router,
- ReadTimeout: 10 * time.Minute,
- WriteTimeout: 10 * time.Minute,
- MaxHeaderBytes: 1 << 20,
- }
-}
diff --git a/server/docs/docs.go b/server/docs/docs.go
index f553400a5e..6f0eb2a0da 100644
--- a/server/docs/docs.go
+++ b/server/docs/docs.go
@@ -9296,7 +9296,7 @@ const docTemplate = `{
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
- Version: "v2.8.1",
+ Version: "v2.8.2",
Host: "",
BasePath: "",
Schemes: []string{},
diff --git a/server/global/global.go b/server/global/global.go
index 7291d2a307..97b57c4a6b 100644
--- a/server/global/global.go
+++ b/server/global/global.go
@@ -2,6 +2,7 @@ package global
import (
"fmt"
+ "github.com/mark3labs/mcp-go/server"
"sync"
"github.com/gin-gonic/gin"
@@ -35,6 +36,7 @@ var (
GVA_Concurrency_Control = &singleflight.Group{}
GVA_ROUTERS gin.RoutesInfo
GVA_ACTIVE_DBNAME *string
+ GVA_MCP_SERVER *server.MCPServer
BlackCache local_cache.Cache
lock sync.RWMutex
)
diff --git a/server/go.mod b/server/go.mod
index 9422baa881..b70c6f5661 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -1,14 +1,16 @@
module github.com/flipped-aurora/gin-vue-admin/server
-go 1.22.2
+go 1.23
+
+toolchain go1.23.9
require (
+ github.com/ThinkInAIXYZ/go-mcp v0.2.2
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/aws/aws-sdk-go v1.55.6
github.com/casbin/casbin/v2 v2.103.0
github.com/casbin/gorm-adapter/v3 v3.32.0
github.com/fsnotify/fsnotify v1.8.0
- github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
github.com/go-sql-driver/mysql v1.8.1
@@ -55,7 +57,7 @@ require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
- github.com/STARRY-S/zip v0.1.0 // indirect
+ github.com/STARRY-S/zip v0.2.1 // indirect
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect
@@ -111,9 +113,11 @@ require (
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
+ github.com/mark3labs/mcp-go v0.26.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/microsoft/go-mssqldb v1.8.0 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
+ github.com/minio/minlz v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -121,7 +125,8 @@ require (
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/mozillazg/go-httpheader v0.4.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
- github.com/nwaples/rardecode/v2 v2.0.1 // indirect
+ github.com/nwaples/rardecode/v2 v2.1.0 // indirect
+ github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
@@ -141,6 +146,9 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
+ github.com/tidwall/gjson v1.18.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
@@ -152,6 +160,7 @@ require (
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 // indirect
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 // indirect
+ github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
diff --git a/server/go.sum b/server/go.sum
index 0c82192c12..dba8883f00 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -46,8 +46,10 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
-github.com/STARRY-S/zip v0.1.0 h1:eUER3jKmHKXjv+iy3BekLa+QnNSo1Lqz4eTzYBcGDqo=
-github.com/STARRY-S/zip v0.1.0/go.mod h1:qj/mTZkvb3AvfGQ2e775/3AODRvB4peSw8KNMvrM8/I=
+github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
+github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
+github.com/ThinkInAIXYZ/go-mcp v0.2.2 h1:zlm4Xo8pxGzmfTvN16hM0YP75Q7QZ713g3mZSbO3JAs=
+github.com/ThinkInAIXYZ/go-mcp v0.2.2/go.mod h1:KnUWUymko7rmOgzvIjxwX0uB9oiJeLF/Q3W9cRt8fVg=
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4=
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
@@ -113,8 +115,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
-github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=
@@ -312,6 +312,8 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+github.com/mark3labs/mcp-go v0.26.0 h1:xz/Kv1cHLYovF8txv6btBM39/88q3YOjnxqhi51jB0w=
+github.com/mark3labs/mcp-go v0.26.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
@@ -326,6 +328,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E=
github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
+github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
+github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -347,8 +351,10 @@ github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiY
github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
-github.com/nwaples/rardecode/v2 v2.0.1 h1:3MN6/R+Y4c7e+21U3yhWuUcf72sYmcmr6jtiuAVSH1A=
-github.com/nwaples/rardecode/v2 v2.0.1/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
+github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
+github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
+github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
+github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
@@ -449,6 +455,12 @@ github.com/tencentyun/cos-go-sdk-v5 v0.7.60 h1:/e/tmvRmfKexr/QQIBzWhOkZWsmY3EK72
github.com/tencentyun/cos-go-sdk-v5 v0.7.60/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
@@ -478,6 +490,8 @@ github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 h1:hOh7aVDrvGJRxzXrQbDY8E
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
+github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
+github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
diff --git a/server/initialize/init.go b/server/initialize/init.go
new file mode 100644
index 0000000000..4dc48f3160
--- /dev/null
+++ b/server/initialize/init.go
@@ -0,0 +1,15 @@
+// 假设这是初始化逻辑的一部分
+
+package initialize
+
+import (
+ "github.com/flipped-aurora/gin-vue-admin/server/utils"
+)
+
+// 初始化全局函数
+func SetupHandlers() {
+ // 注册系统重载处理函数
+ utils.GlobalSystemEvents.RegisterReloadHandler(func() error {
+ return Reload()
+ })
+}
diff --git a/server/initialize/mcp.go b/server/initialize/mcp.go
new file mode 100644
index 0000000000..5e03f2940f
--- /dev/null
+++ b/server/initialize/mcp.go
@@ -0,0 +1,25 @@
+package initialize
+
+import (
+ "github.com/flipped-aurora/gin-vue-admin/server/global"
+ mcpTool "github.com/flipped-aurora/gin-vue-admin/server/mcp"
+ "github.com/mark3labs/mcp-go/server"
+)
+
+func McpRun() *server.SSEServer {
+ config := global.GVA_CONFIG.MCP
+
+ s := server.NewMCPServer(
+ config.Name,
+ config.Version,
+ )
+
+ global.GVA_MCP_SERVER = s
+
+ mcpTool.RegisterAllTools(s)
+
+ return server.NewSSEServer(s,
+ server.WithSSEEndpoint(config.SSEPath),
+ server.WithMessageEndpoint(config.MessagePath),
+ server.WithBaseURL(config.UrlPrefix))
+}
diff --git a/server/initialize/reload.go b/server/initialize/reload.go
new file mode 100644
index 0000000000..8fd27e6913
--- /dev/null
+++ b/server/initialize/reload.go
@@ -0,0 +1,45 @@
+package initialize
+
+import (
+ "github.com/flipped-aurora/gin-vue-admin/server/global"
+ "go.uber.org/zap"
+)
+
+// Reload 优雅地重新加载系统配置
+func Reload() error {
+ global.GVA_LOG.Info("正在重新加载系统配置...")
+
+ // 重新加载配置文件
+ if err := global.GVA_VP.ReadInConfig(); err != nil {
+ global.GVA_LOG.Error("重新读取配置文件失败!", zap.Error(err))
+ return err
+ }
+
+ // 重新初始化数据库连接
+ if global.GVA_DB != nil {
+ db, _ := global.GVA_DB.DB()
+ err := db.Close()
+ if err != nil {
+ global.GVA_LOG.Error("关闭原数据库连接失败!", zap.Error(err))
+ return err
+ }
+ }
+
+ // 重新建立数据库连接
+ global.GVA_DB = Gorm()
+
+ // 重新初始化其他配置
+ OtherInit()
+ DBList()
+
+ if global.GVA_DB != nil {
+ // 确保数据库表结构是最新的
+ RegisterTables()
+ }
+
+ // 重新初始化定时任务
+ Timer()
+
+ global.GVA_LOG.Info("系统配置重新加载完成")
+ return nil
+}
diff --git a/server/initialize/router.go b/server/initialize/router.go
index c3dfe1ebb3..4e6fd083e4 100644
--- a/server/initialize/router.go
+++ b/server/initialize/router.go
@@ -40,6 +40,17 @@ func Routers() *gin.Engine {
Router.Use(gin.Logger())
}
+ sseServer := McpRun()
+
+ // 注册mcp服务
+ Router.GET(global.GVA_CONFIG.MCP.SSEPath, func(c *gin.Context) {
+ sseServer.SSEHandler().ServeHTTP(c.Writer, c.Request)
+ })
+
+ Router.POST(global.GVA_CONFIG.MCP.MessagePath, func(c *gin.Context) {
+ sseServer.MessageHandler().ServeHTTP(c.Writer, c.Request)
+ })
+
systemRouter := router.RouterGroupApp.System
exampleRouter := router.RouterGroupApp.Example
// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
diff --git a/server/main.go b/server/main.go
index deaac4bad4..48ff6d91eb 100644
--- a/server/main.go
+++ b/server/main.go
@@ -21,13 +21,22 @@ import (
// @Tag.Description 用户
// @title Gin-Vue-Admin Swagger API接口文档
-// @version v2.8.1
+// @version v2.8.2
// @description 使用gin+vue进行极速开发的全栈开发基础平台
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name x-token
// @BasePath /
func main() {
+ // 初始化系统
+ initializeSystem()
+ // 运行服务器
+ core.RunServer()
+}
+
+// initializeSystem 初始化系统所有组件
+// 提取为单独函数以便于系统重载时调用
+func initializeSystem() {
global.GVA_VP = core.Viper() // 初始化Viper
initialize.OtherInit()
global.GVA_LOG = core.Zap() // 初始化zap日志库
@@ -35,11 +44,9 @@ func main() {
global.GVA_DB = initialize.Gorm() // gorm连接数据库
initialize.Timer()
initialize.DBList()
+ initialize.SetupHandlers() // 注册全局函数
+ initialize.McpRun()
if global.GVA_DB != nil {
initialize.RegisterTables() // 初始化表
- // 程序结束前关闭数据库链接
- db, _ := global.GVA_DB.DB()
- defer db.Close()
}
- core.RunWindowsServer()
}
diff --git a/server/mcp/client/client.go b/server/mcp/client/client.go
new file mode 100644
index 0000000000..7e5db1ef6d
--- /dev/null
+++ b/server/mcp/client/client.go
@@ -0,0 +1,39 @@
+package client
+
+import (
+ "context"
+ "errors"
+ mcpClient "github.com/mark3labs/mcp-go/client"
+ "github.com/mark3labs/mcp-go/mcp"
+)
+
+func NewClient(baseUrl, name, version, serverName string) (*mcpClient.Client, error) {
+ client, err := mcpClient.NewSSEMCPClient(baseUrl)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx := context.Background()
+
+ // 启动client
+ if err := client.Start(ctx); err != nil {
+ return nil, err
+ }
+
+ // 初始化
+ initRequest := mcp.InitializeRequest{}
+ initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
+ initRequest.Params.ClientInfo = mcp.Implementation{
+ Name: name,
+ Version: version,
+ }
+
+ result, err := client.Initialize(ctx, initRequest)
+ if err != nil {
+ return nil, err
+ }
+ if result.ServerInfo.Name != serverName {
+ return nil, errors.New("server name mismatch")
+ }
+ return client, nil
+}
diff --git a/server/mcp/client/client_test.go b/server/mcp/client/client_test.go
new file mode 100644
index 0000000000..917d22d397
--- /dev/null
+++ b/server/mcp/client/client_test.go
@@ -0,0 +1,132 @@
+package client
+
+import (
+ "context"
+ "fmt"
+ "github.com/mark3labs/mcp-go/mcp"
+ "testing"
+)
+
+// 测试 MCP 客户端连接
+func TestMcpClientConnection(t *testing.T) {
+ c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
+ defer c.Close()
+ if err != nil {
+ t.Fatalf(err.Error())
+ }
+}
+
+func TestTools(t *testing.T) {
+ t.Run("currentTime", func(t *testing.T) {
+ c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
+ defer c.Close()
+ if err != nil {
+ t.Fatalf("Failed to create client: %v", err)
+ }
+ ctx := context.Background()
+
+ request := mcp.CallToolRequest{}
+ request.Params.Name = "currentTime"
+ request.Params.Arguments = map[string]interface{}{
+ "timezone": "UTC+8",
+ }
+
+ result, err := c.CallTool(ctx, request)
+ if err != nil {
+ t.Fatalf("方法调用错误: %v", err)
+ }
+
+ if len(result.Content) != 1 {
+ t.Errorf("应该有且仅返回1条信息,但是现在有 %d", len(result.Content))
+ }
+ if content, ok := result.Content[0].(mcp.TextContent); ok {
+ t.Logf("成功返回信息%s", content.Text)
+ } else {
+ t.Logf("返回为止类型信息%+v", content)
+ }
+ })
+
+ t.Run("getNickname", func(t *testing.T) {
+
+ c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
+ defer c.Close()
+ if err != nil {
+ t.Fatalf("Failed to create client: %v", err)
+ }
+ ctx := context.Background()
+
+ // Initialize
+ initRequest := mcp.InitializeRequest{}
+ initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
+ initRequest.Params.ClientInfo = mcp.Implementation{
+ Name: "test-client",
+ Version: "1.0.0",
+ }
+
+ _, err = c.Initialize(ctx, initRequest)
+ if err != nil {
+ t.Fatalf("初始化失败: %v", err)
+ }
+
+ request := mcp.CallToolRequest{}
+ request.Params.Name = "getNickname"
+ request.Params.Arguments = map[string]interface{}{
+ "username": "admin",
+ }
+
+ result, err := c.CallTool(ctx, request)
+ if err != nil {
+ t.Fatalf("方法调用错误: %v", err)
+ }
+
+ if len(result.Content) != 1 {
+ t.Errorf("应该有且仅返回1条信息,但是现在有 %d", len(result.Content))
+ }
+ if content, ok := result.Content[0].(mcp.TextContent); ok {
+ t.Logf("成功返回信息%s", content.Text)
+ } else {
+ t.Logf("返回为止类型信息%+v", content)
+ }
+ })
+}
+
+func TestGetTools(t *testing.T) {
+ c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
+ defer c.Close()
+ if err != nil {
+ t.Fatalf("Failed to create client: %v", err)
+ }
+ ctx := context.Background()
+
+ toolsRequest := mcp.ListToolsRequest{}
+
+ toolListResult, err := c.ListTools(ctx, toolsRequest)
+ if err != nil {
+ t.Fatalf("获取工具列表失败: %v", err)
+ }
+ for i := range toolListResult.Tools {
+ tool := toolListResult.Tools[i]
+ fmt.Printf("工具名称: %s\n", tool.Name)
+ fmt.Printf("工具描述: %s\n", tool.Description)
+
+ // 打印参数信息
+ if tool.InputSchema.Properties != nil {
+ fmt.Println("参数列表:")
+ for paramName, prop := range tool.InputSchema.Properties {
+ required := "否"
+ // 检查参数是否在必填列表中
+ for _, reqField := range tool.InputSchema.Required {
+ if reqField == paramName {
+ required = "是"
+ break
+ }
+ }
+ fmt.Printf(" - %s (类型: %s, 描述: %s, 必填: %s)\n",
+ paramName, prop.(map[string]any)["type"], prop.(map[string]any)["description"], required)
+ }
+ } else {
+ fmt.Println("该工具没有参数")
+ }
+ fmt.Println("-------------------")
+ }
+}
diff --git a/server/mcp/current_time.go b/server/mcp/current_time.go
new file mode 100644
index 0000000000..d6f0fc7a8a
--- /dev/null
+++ b/server/mcp/current_time.go
@@ -0,0 +1,39 @@
+package mcpTool
+
+import (
+ "context"
+ "github.com/mark3labs/mcp-go/mcp"
+ "time"
+)
+
+func init() {
+ RegisterTool(&CurrentTime{})
+}
+
+type CurrentTime struct {
+}
+
+// 获取当前系统时间
+func (t *CurrentTime) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+ // 获取当前系统时间
+ currentTime := time.Now().Format("2006-01-02 15:04:05")
+ //返回
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: currentTime,
+ },
+ },
+ }, nil
+}
+
+func (t *CurrentTime) New() mcp.Tool {
+ return mcp.NewTool("currentTime",
+ mcp.WithDescription("获取当前系统时间"),
+ mcp.WithString("timezone",
+ mcp.Required(),
+ mcp.Description("时区"),
+ mcp.Enum("UTC", "CST", "PST", "EST", "GMT", "CET", "JST", "MST", "IST", "AST", "HST"),
+ ))
+}
diff --git a/server/mcp/enter.go b/server/mcp/enter.go
new file mode 100644
index 0000000000..ca19f54c0e
--- /dev/null
+++ b/server/mcp/enter.go
@@ -0,0 +1,31 @@
+package mcpTool
+
+import (
+ "context"
+ "github.com/mark3labs/mcp-go/mcp"
+ "github.com/mark3labs/mcp-go/server"
+)
+
+// McpTool 定义了MCP工具必须实现的接口
+type McpTool interface {
+ // Handle 返回工具调用信息
+ Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
+ // New 返回工具注册信息
+ New() mcp.Tool
+}
+
+// 工具注册表
+var toolRegister = make(map[string]McpTool)
+
+// RegisterTool 供工具在init时调用,将自己注册到工具注册表中
+func RegisterTool(tool McpTool) {
+ mcpTool := tool.New()
+ toolRegister[mcpTool.Name] = tool
+}
+
+// RegisterAllTools 将所有注册的工具注册到MCP服务中
+func RegisterAllTools(mcpServer *server.MCPServer) {
+ for _, tool := range toolRegister {
+ mcpServer.AddTool(tool.New(), tool.Handle)
+ }
+}
diff --git a/server/mcp/get_nickname.go b/server/mcp/get_nickname.go
new file mode 100644
index 0000000000..029a49a109
--- /dev/null
+++ b/server/mcp/get_nickname.go
@@ -0,0 +1,71 @@
+package mcpTool
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/flipped-aurora/gin-vue-admin/server/global"
+ "github.com/flipped-aurora/gin-vue-admin/server/model/system"
+ "github.com/mark3labs/mcp-go/mcp"
+)
+
+func init() {
+ RegisterTool(&GetNickname{})
+}
+
+type GetNickname struct{}
+
+// 根据用户username获取nickname
+func (t *GetNickname) New() mcp.Tool {
+ return mcp.NewTool("getNickname",
+ mcp.WithDescription("根据用户username获取nickname"),
+ mcp.WithString("username",
+ mcp.Required(),
+ mcp.Description("用户的username"),
+ ))
+}
+
+// Handle 处理获取昵称的请求
+func (t *GetNickname) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+ // 1. 参数验证
+ username, ok := request.Params.Arguments["username"].(string)
+ if !ok {
+ return nil, errors.New("参数错误:username必须是字符串类型")
+ }
+
+ if username == "" {
+ return nil, errors.New("参数错误:username不能为空")
+ }
+
+ // 2. 记录操作日志
+ global.GVA_LOG.Info("getNickname工具被调用", "username", username)
+
+ // 3. 优化查询,只选择需要的字段
+ var user struct {
+ NickName string
+ }
+
+ err := global.GVA_DB.Model(&system.SysUser{}).
+ Select("nick_name").
+ Where("username = ?", username).
+ First(&user).Error
+
+ // 4. 优化错误处理
+ if err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, errors.New("用户不存在")
+ }
+ global.GVA_LOG.Error("数据库查询错误", "error", err)
+ return nil, errors.New("系统错误,请稍后再试")
+ }
+
+ // 构造回复信息
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: fmt.Sprintf("用户 %s 的昵称是 %s", username, user.NickName),
+ },
+ },
+ }, nil
+}
diff --git a/server/middleware/casbin_rbac.go b/server/middleware/casbin_rbac.go
index a1ca4c2b7b..6744c758b5 100644
--- a/server/middleware/casbin_rbac.go
+++ b/server/middleware/casbin_rbac.go
@@ -6,13 +6,10 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
- "github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
)
-var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
-
// CasbinHandler 拦截器
func CasbinHandler() gin.HandlerFunc {
return func(c *gin.Context) {
@@ -24,7 +21,7 @@ func CasbinHandler() gin.HandlerFunc {
act := c.Request.Method
// 获取用户的角色
sub := strconv.Itoa(int(waitUse.AuthorityId))
- e := casbinService.Casbin() // 判断策略中是否存在
+ e := utils.GetCasbin() // 判断策略中是否存在
success, _ := e.Enforce(sub, obj, act)
if !success {
response.FailWithDetailed(gin.H{}, "权限不足", c)
diff --git a/server/middleware/email.go b/server/middleware/email.go
index 4a07561c98..1bc976cfbf 100644
--- a/server/middleware/email.go
+++ b/server/middleware/email.go
@@ -11,13 +11,10 @@ import (
"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/gin-gonic/gin"
"go.uber.org/zap"
)
-var userService = service.ServiceGroupApp.SystemServiceGroup.UserService
-
func ErrorToEmail() gin.HandlerFunc {
return func(c *gin.Context) {
var username string
@@ -26,11 +23,12 @@ func ErrorToEmail() gin.HandlerFunc {
username = claims.Username
} else {
id, _ := strconv.Atoi(c.Request.Header.Get("x-user-id"))
- user, err := userService.FindUserById(id)
+ var u system.SysUser
+ err := global.GVA_DB.Where("id = ?", id).First(&u).Error
if err != nil {
username = "Unknown"
}
- username = user.Username
+ username = u.Username
}
body, _ := io.ReadAll(c.Request.Body)
// 再重新写回请求体body中,ioutil.ReadAll会清空c.Request.Body中的数据
diff --git a/server/middleware/jwt.go b/server/middleware/jwt.go
index 291d5e3d7c..db71ed6b97 100644
--- a/server/middleware/jwt.go
+++ b/server/middleware/jwt.go
@@ -9,12 +9,9 @@ import (
"time"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
- "github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/gin-gonic/gin"
)
-var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
-
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
@@ -24,7 +21,7 @@ func JWTAuth() gin.HandlerFunc {
c.Abort()
return
}
- if jwtService.IsBlacklist(token) {
+ if isBlacklist(token) {
response.NoAuth("您的帐户异地登陆或令牌失效", c)
utils.ClearToken(c)
c.Abort()
@@ -65,7 +62,7 @@ func JWTAuth() gin.HandlerFunc {
utils.SetToken(c, newToken, int(dr.Seconds()))
if global.GVA_CONFIG.System.UseMultipoint {
// 记录新的活跃jwt
- _ = jwtService.SetRedisJWT(newToken, newClaims.Username)
+ _ = utils.SetRedisJWT(newToken, newClaims.Username)
}
}
c.Next()
@@ -78,3 +75,14 @@ func JWTAuth() gin.HandlerFunc {
}
}
}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: IsBlacklist
+//@description: 判断JWT是否在黑名单内部
+//@param: jwt string
+//@return: bool
+
+func isBlacklist(jwt string) bool {
+ _, ok := global.BlackCache.Get(jwt)
+ return ok
+}
diff --git a/server/middleware/operation.go b/server/middleware/operation.go
index f34cf68ee2..dd545f56fc 100644
--- a/server/middleware/operation.go
+++ b/server/middleware/operation.go
@@ -15,13 +15,10 @@ import (
"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/gin-gonic/gin"
"go.uber.org/zap"
)
-var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
-
var respPool sync.Pool
var bufferSize = 1024
@@ -115,8 +112,7 @@ func OperationRecord() gin.HandlerFunc {
record.Body = "超出记录长度"
}
}
-
- if err := operationRecordService.CreateSysOperationRecord(record); err != nil {
+ if err := global.GVA_DB.Create(&record).Error; err != nil {
global.GVA_LOG.Error("create operation record error:", zap.Error(err))
}
}
diff --git a/server/model/system/request/sys_auto_code_mcp.go b/server/model/system/request/sys_auto_code_mcp.go
new file mode 100644
index 0000000000..a52ec7c500
--- /dev/null
+++ b/server/model/system/request/sys_auto_code_mcp.go
@@ -0,0 +1,16 @@
+package request
+
+type AutoMcpTool struct {
+ Name string `json:"name" form:"name" binding:"required"`
+ Description string `json:"description" form:"description" binding:"required"`
+ Params []struct {
+ Name string `json:"name" form:"name" binding:"required"`
+ Description string `json:"description" form:"description" binding:"required"`
+ Type string `json:"type" form:"type" binding:"required"` // string, number, boolean, object, array
+ Required bool `json:"required" form:"required"`
+ Default string `json:"default" form:"default"`
+ } `json:"params" form:"params"`
+ Response []struct {
+ Type string `json:"type" form:"type" binding:"required"` // text, image
+ } `json:"response" form:"response"`
+}
diff --git a/server/resource/mcp/tools.tpl b/server/resource/mcp/tools.tpl
new file mode 100644
index 0000000000..1c6903a22b
--- /dev/null
+++ b/server/resource/mcp/tools.tpl
@@ -0,0 +1,56 @@
+package mcpTool
+
+import (
+ "context"
+ "github.com/mark3labs/mcp-go/mcp"
+)
+
+func init() {
+ RegisterTool(&{{.Name | title}}{})
+}
+
+type {{.Name | title}} struct {
+}
+
+// {{.Description}}
+func (t *{{.Name | title}}) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+ // TODO: 实现工具逻辑
+ // 参数示例:
+ // {{- range .Params}}
+ // {{.Name}} := request.Params["{{.Name}}"]
+ // {{- end}}
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ {{- range .Response}}
+ mcp.{{.Type | title}}Content{
+ Type: "{{.Type}}",
+ // TODO: 填充{{.Type}}内容
+ },
+ {{- end}}
+ },
+ }, nil
+}
+
+func (t *{{.Name | title}}) New() mcp.Tool {
+ return mcp.NewTool("{{.Name}}",
+ mcp.WithDescription("{{.Description}}"),
+ {{- range .Params}}
+ mcp.With{{.Type | title}}("{{.Name}}",
+ {{- if .Required}}mcp.Required(),{{end}}
+ mcp.Description("{{.Description}}"),
+ {{- if .Default}}
+ {{- if eq .Type "string"}}
+ mcp.DefaultString("{{.Default}}"),
+ {{- else if eq .Type "number"}}
+ mcp.DefaultNumber({{.Default}}),
+ {{- else if eq .Type "boolean"}}
+ mcp.DefaultBoolean({{if or (eq .Default "true") (eq .Default "True")}}true{{else}}false{{end}}),
+ {{- else if eq .Type "array"}}
+ // 注意:数组默认值需要在后端代码中预处理为正确的格式
+ // mcp.DefaultArray({{.Default}}),
+ {{- end}}
+ {{- end}}
+ ),
+ {{- end}}
+ )
+}
diff --git a/server/router/system/sys_auto_code.go b/server/router/system/sys_auto_code.go
index e25e1cef8e..68fb8494e2 100644
--- a/server/router/system/sys_auto_code.go
+++ b/server/router/system/sys_auto_code.go
@@ -19,6 +19,9 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup, RouterPubli
autoCodeRouter.POST("createTemp", autoCodeTemplateApi.Create) // 创建自动化代码
autoCodeRouter.POST("addFunc", autoCodeTemplateApi.AddFunc) // 为代码插入方法
}
+ {
+ autoCodeRouter.POST("mcp", autoCodeTemplateApi.MCP) // 自动创建Mcp Tool模板
+ }
{
autoCodeRouter.POST("getPackage", autoCodePackageApi.All) // 获取package包
autoCodeRouter.POST("delPackage", autoCodePackageApi.Delete) // 删除package包
diff --git a/server/router/system/sys_operation_record.go b/server/router/system/sys_operation_record.go
index 11b841db70..d158d5e704 100644
--- a/server/router/system/sys_operation_record.go
+++ b/server/router/system/sys_operation_record.go
@@ -9,7 +9,6 @@ type OperationRecordRouter struct{}
func (s *OperationRecordRouter) InitSysOperationRecordRouter(Router *gin.RouterGroup) {
operationRecordRouter := Router.Group("sysOperationRecord")
{
- operationRecordRouter.POST("createSysOperationRecord", operationRecordApi.CreateSysOperationRecord) // 新建SysOperationRecord
operationRecordRouter.DELETE("deleteSysOperationRecord", operationRecordApi.DeleteSysOperationRecord) // 删除SysOperationRecord
operationRecordRouter.DELETE("deleteSysOperationRecordByIds", operationRecordApi.DeleteSysOperationRecordByIds) // 批量删除SysOperationRecord
operationRecordRouter.GET("findSysOperationRecord", operationRecordApi.FindSysOperationRecord) // 根据ID获取SysOperationRecord
diff --git a/server/service/system/auto_code_mcp.go b/server/service/system/auto_code_mcp.go
new file mode 100644
index 0000000000..3b6eb84181
--- /dev/null
+++ b/server/service/system/auto_code_mcp.go
@@ -0,0 +1,45 @@
+package system
+
+import (
+ "context"
+ "github.com/flipped-aurora/gin-vue-admin/server/global"
+ "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
+ "github.com/flipped-aurora/gin-vue-admin/server/utils"
+ "github.com/flipped-aurora/gin-vue-admin/server/utils/autocode"
+ "os"
+ "path/filepath"
+ "text/template"
+)
+
+func (s *autoCodeTemplate) CreateMcp(ctx context.Context, info request.AutoMcpTool) (toolFilePath string, err error) {
+ mcpTemplatePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "mcp", "tools.tpl")
+ mcpToolPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "mcp")
+
+ var files *template.Template
+
+ templateName := filepath.Base(mcpTemplatePath)
+
+ files, err = template.New(templateName).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(mcpTemplatePath)
+ if err != nil {
+ return
+ }
+
+ fileName := utils.HumpToUnderscore(info.Name)
+
+ toolFilePath = filepath.Join(mcpToolPath, fileName+".go")
+
+ f, err := os.Create(toolFilePath)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ // 执行模板,将内容写入文件
+ err = files.Execute(f, info)
+ if err != nil {
+ return
+ }
+
+ return
+
+}
diff --git a/server/service/system/auto_code_package.go b/server/service/system/auto_code_package.go
index 6c3f4eee43..4e184b1cc7 100644
--- a/server/service/system/auto_code_package.go
+++ b/server/service/system/auto_code_package.go
@@ -233,6 +233,9 @@ func (s *autoCodePackage) Templates(ctx context.Context) ([]string, error) {
if entries[i].Name() == "preview" {
continue
} // preview 为预览代码生成器的代码
+ if entries[i].Name() == "mcp" {
+ continue
+ } // preview 为mcp生成器的代码
templates = append(templates, entries[i].Name())
}
}
diff --git a/server/service/system/jwt_black_list.go b/server/service/system/jwt_black_list.go
index 78ae38a7ea..6c34bbbac1 100644
--- a/server/service/system/jwt_black_list.go
+++ b/server/service/system/jwt_black_list.go
@@ -7,7 +7,6 @@ import (
"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/utils"
)
type JwtService struct{}
@@ -29,20 +28,6 @@ func (jwtService *JwtService) JsonInBlacklist(jwtList system.JwtBlacklist) (err
return
}
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: IsBlacklist
-//@description: 判断JWT是否在黑名单内部
-//@param: jwt string
-//@return: bool
-
-func (jwtService *JwtService) IsBlacklist(jwt string) bool {
- _, ok := global.BlackCache.Get(jwt)
- return ok
- // err := global.GVA_DB.Where("jwt = ?", jwt).First(&system.JwtBlacklist{}).Error
- // isNotFound := errors.Is(err, gorm.ErrRecordNotFound)
- // return !isNotFound
-}
-
//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetRedisJWT
//@description: 从redis取jwt
@@ -54,23 +39,6 @@ func (jwtService *JwtService) GetRedisJWT(userName string) (redisJWT string, err
return redisJWT, err
}
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: SetRedisJWT
-//@description: jwt存入redis并设置过期时间
-//@param: jwt string, userName string
-//@return: err error
-
-func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) {
- // 此处过期时间等于jwt过期时间
- dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
- if err != nil {
- return err
- }
- timer := dr
- err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err()
- return err
-}
-
func LoadAll() {
var data []string
err := global.GVA_DB.Model(&system.JwtBlacklist{}).Select("jwt").Find(&data).Error
diff --git a/server/service/system/sys_casbin.go b/server/service/system/sys_casbin.go
index 6b54904c0e..264c36aff6 100644
--- a/server/service/system/sys_casbin.go
+++ b/server/service/system/sys_casbin.go
@@ -3,17 +3,14 @@ package system
import (
"errors"
"strconv"
- "sync"
"gorm.io/gorm"
- "github.com/casbin/casbin/v2"
- "github.com/casbin/casbin/v2/model"
gormadapter "github.com/casbin/gorm-adapter/v3"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
+ "github.com/flipped-aurora/gin-vue-admin/server/utils"
_ "github.com/go-sql-driver/mysql"
- "go.uber.org/zap"
)
//@author: [piexlmax](https://github.com/piexlmax)
@@ -68,7 +65,7 @@ func (casbinService *CasbinService) UpdateCasbin(adminAuthorityID, AuthorityID u
if len(rules) == 0 {
return nil
} // 设置空权限无需调用 AddPolicies 方法
- e := casbinService.Casbin()
+ e := utils.GetCasbin()
success, _ := e.AddPolicies(rules)
if !success {
return errors.New("存在相同api,添加失败,请联系管理员")
@@ -91,7 +88,7 @@ func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath stri
return err
}
- e := casbinService.Casbin()
+ e := utils.GetCasbin()
return e.LoadPolicy()
}
@@ -102,7 +99,7 @@ func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath stri
//@return: pathMaps []request.CasbinInfo
func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []request.CasbinInfo) {
- e := casbinService.Casbin()
+ e := utils.GetCasbin()
authorityId := strconv.Itoa(int(AuthorityID))
list, _ := e.GetFilteredPolicy(0, authorityId)
for _, v := range list {
@@ -121,7 +118,7 @@ func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint)
//@return: bool
func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool {
- e := casbinService.Casbin()
+ e := utils.GetCasbin()
success, _ := e.RemoveFilteredPolicy(v, p...)
return success
}
@@ -170,52 +167,7 @@ func (casbinService *CasbinService) AddPolicies(db *gorm.DB, rules [][]string) e
}
func (casbinService *CasbinService) FreshCasbin() (err error) {
- e := casbinService.Casbin()
+ e := utils.GetCasbin()
err = e.LoadPolicy()
return err
}
-
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: Casbin
-//@description: 持久化到数据库 引入自定义规则
-//@return: *casbin.Enforcer
-
-var (
- syncedCachedEnforcer *casbin.SyncedCachedEnforcer
- once sync.Once
-)
-
-func (casbinService *CasbinService) Casbin() *casbin.SyncedCachedEnforcer {
- once.Do(func() {
- a, err := gormadapter.NewAdapterByDB(global.GVA_DB)
- if err != nil {
- zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err))
- return
- }
- text := `
- [request_definition]
- r = sub, obj, act
-
- [policy_definition]
- p = sub, obj, act
-
- [role_definition]
- g = _, _
-
- [policy_effect]
- e = some(where (p.eft == allow))
-
- [matchers]
- m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
- `
- m, err := model.NewModelFromString(text)
- if err != nil {
- zap.L().Error("字符串加载模型失败!", zap.Error(err))
- return
- }
- syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a)
- syncedCachedEnforcer.SetExpireTime(60 * 60)
- _ = syncedCachedEnforcer.LoadPolicy()
- })
- return syncedCachedEnforcer
-}
diff --git a/server/service/system/sys_operation_record.go b/server/service/system/sys_operation_record.go
index adfc25efd1..ef131db9ac 100644
--- a/server/service/system/sys_operation_record.go
+++ b/server/service/system/sys_operation_record.go
@@ -17,11 +17,6 @@ type OperationRecordService struct{}
var OperationRecordServiceApp = new(OperationRecordService)
-func (operationRecordService *OperationRecordService) CreateSysOperationRecord(sysOperationRecord system.SysOperationRecord) (err error) {
- err = global.GVA_DB.Create(&sysOperationRecord).Error
- return err
-}
-
//@author: [granty1](https://github.com/granty1)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteSysOperationRecordByIds
diff --git a/server/source/system/api.go b/server/source/system/api.go
index fddb1d77bb..a026518696 100644
--- a/server/source/system/api.go
+++ b/server/source/system/api.go
@@ -117,6 +117,7 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
{ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getColumn", Description: "获取所选table的所有字段"},
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/installPlugin", Description: "安装插件"},
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/pubPlug", Description: "打包插件"},
+ {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcp", Description: "自动生成 MCP Tool 模板"},
{ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/createPackage", Description: "配置模板"},
{ApiGroup: "模板配置", Method: "GET", Path: "/autoCode/getTemplates", Description: "获取模板文件"},
diff --git a/server/source/system/authorities_menus.go b/server/source/system/authorities_menus.go
index e6799bc33e..0bf72fd683 100644
--- a/server/source/system/authorities_menus.go
+++ b/server/source/system/authorities_menus.go
@@ -35,35 +35,72 @@ func (i *initMenuAuthority) InitializeData(ctx context.Context) (next context.Co
if !ok {
return ctx, system.ErrMissingDBContext
}
+
initAuth := &initAuthority{}
authorities, ok := ctx.Value(initAuth.InitializerName()).([]sysModel.SysAuthority)
if !ok {
return ctx, errors.Wrap(system.ErrMissingDependentContext, "创建 [菜单-权限] 关联失败, 未找到权限表初始化数据")
}
- menus, ok := ctx.Value(new(initMenu).InitializerName()).([]sysModel.SysBaseMenu)
+
+ allMenus, ok := ctx.Value(new(initMenu).InitializerName()).([]sysModel.SysBaseMenu)
if !ok {
return next, errors.Wrap(errors.New(""), "创建 [菜单-权限] 关联失败, 未找到菜单表初始化数据")
}
next = ctx
- // 888
- if err = db.Model(&authorities[0]).Association("SysBaseMenus").Replace(menus); err != nil {
- return next, err
+
+ // 构建菜单ID映射,方便快速查找
+ menuMap := make(map[uint]sysModel.SysBaseMenu)
+ for _, menu := range allMenus {
+ menuMap[menu.ID] = menu
+ }
+
+ // 为不同角色分配不同权限
+ // 1. 超级管理员角色(888) - 拥有所有菜单权限
+ if err = db.Model(&authorities[0]).Association("SysBaseMenus").Replace(allMenus); err != nil {
+ return next, errors.Wrap(err, "为超级管理员分配菜单失败")
+ }
+
+ // 2. 普通用户角色(8881) - 仅拥有基础功能菜单
+ // 仅选择部分父级菜单及其子菜单
+ var menu8881 []sysModel.SysBaseMenu
+
+ // 添加仪表盘、关于我们和个人信息菜单
+ for _, menu := range allMenus {
+ if menu.ParentId == 0 && (menu.Name == "dashboard" || menu.Name == "about" || menu.Name == "person" || menu.Name == "state") {
+ menu8881 = append(menu8881, menu)
+ }
}
- // 8881
- menu8881 := menus[:2]
- menu8881 = append(menu8881, menus[7])
if err = db.Model(&authorities[1]).Association("SysBaseMenus").Replace(menu8881); err != nil {
- return next, err
+ return next, errors.Wrap(err, "为普通用户分配菜单失败")
}
- // 9528
- if err = db.Model(&authorities[2]).Association("SysBaseMenus").Replace(menus[:11]); err != nil {
- return next, err
+ // 3. 测试角色(9528) - 拥有部分菜单权限
+ var menu9528 []sysModel.SysBaseMenu
+
+ // 添加所有父级菜单
+ for _, menu := range allMenus {
+ if menu.ParentId == 0 {
+ menu9528 = append(menu9528, menu)
+ }
}
- if err = db.Model(&authorities[2]).Association("SysBaseMenus").Append(menus[12:17]); err != nil {
- return next, err
+
+ // 添加部分子菜单 - 系统工具、示例文件等模块的子菜单
+ for _, menu := range allMenus {
+ parentName := ""
+ if menu.ParentId > 0 && menuMap[menu.ParentId].Name != "" {
+ parentName = menuMap[menu.ParentId].Name
+ }
+
+ if menu.ParentId > 0 && (parentName == "systemTools" || parentName == "example") {
+ menu9528 = append(menu9528, menu)
+ }
+ }
+
+ if err = db.Model(&authorities[2]).Association("SysBaseMenus").Replace(menu9528); err != nil {
+ return next, errors.Wrap(err, "为测试角色分配菜单失败")
}
+
return next, nil
}
diff --git a/server/source/system/casbin.go b/server/source/system/casbin.go
index eda525d100..e9da7551a3 100644
--- a/server/source/system/casbin.go
+++ b/server/source/system/casbin.go
@@ -130,6 +130,7 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error
{Ptype: "p", V0: "888", V1: "/autoCode/installPlugin", V2: "POST"},
{Ptype: "p", V0: "888", V1: "/autoCode/pubPlug", V2: "POST"},
{Ptype: "p", V0: "888", V1: "/autoCode/addFunc", V2: "POST"},
+ {Ptype: "p", V0: "888", V1: "/autoCode/mcp", V2: "POST"},
{Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"},
{Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"},
diff --git a/server/source/system/menu.go b/server/source/system/menu.go
index 100a2cfd2a..ba64f6a04e 100644
--- a/server/source/system/menu.go
+++ b/server/source/system/menu.go
@@ -50,44 +50,73 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
if !ok {
return ctx, system.ErrMissingDBContext
}
- entities := []SysBaseMenu{
+
+ // 定义所有菜单
+ allMenus := []SysBaseMenu{
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "odometer"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 9, Meta: Meta{Title: "关于我们", Icon: "info-filled"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}},
{MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "example", Name: "example", Component: "view/example/index.vue", Sort: 7, Meta: Meta{Title: "示例文件", Icon: "management"}},
- {MenuLevel: 0, Hidden: false, ParentId: 11, Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 5, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}},
- {MenuLevel: 0, Hidden: false, ParentId: 11, Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}},
- {MenuLevel: 0, Hidden: false, ParentId: 11, Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: Meta{Title: "客户列表(资源示例)", Icon: "avatar"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "系统工具", Icon: "tools"}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoCode", Name: "autoCode", Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: Meta{Title: "代码生成器", Icon: "cpu", KeepAlive: true}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 3, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 4, Meta: Meta{Title: "系统配置", Icon: "operation"}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 2, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}},
- {MenuLevel: 0, Hidden: true, ParentId: 15, Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "模板配置", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}},
- {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}},
- {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}},
- {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "pubPlug", Name: "pubPlug", Component: "view/systemTools/pubPlug/pubPlug.vue", Sort: 3, Meta: Meta{Title: "打包插件", Icon: "files"}},
- {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 5, Meta: Meta{Title: "导出模板", Icon: "reading"}},
- {MenuLevel: 0, Hidden: false, ParentId: 24, Path: "anInfo", Name: "anInfo", Component: "plugin/announcement/view/info.vue", Sort: 5, Meta: Meta{Title: "公告管理[示例]", Icon: "scaleToOriginal"}},
- {MenuLevel: 0, Hidden: false, ParentId: 3, Path: "sysParams", Name: "sysParams", Component: "view/superAdmin/params/sysParams.vue", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "compass"}},
- {MenuLevel: 0, Hidden: false, ParentId: 15, Path: "picture", Name: "picture", Component: "view/systemTools/autoCode/picture.vue", Sort: 6, Meta: Meta{Title: "AI页面绘制", Icon: "picture-filled"}},
}
- if err = db.Create(&entities).Error; err != nil {
- return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"表数据初始化失败!")
+
+ // 先创建父级菜单(ParentId = 0 的菜单)
+ if err = db.Create(&allMenus).Error; err != nil {
+ return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"父级菜单初始化失败!")
+ }
+
+ // 建立菜单映射 - 通过Name查找已创建的菜单及其ID
+ menuNameMap := make(map[string]uint)
+ for _, menu := range allMenus {
+ menuNameMap[menu.Name] = menu.ID
+ }
+
+ // 定义子菜单,并设置正确的ParentId
+ childMenus := []SysBaseMenu{
+ // superAdmin子菜单
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysParams", Name: "sysParams", Component: "view/superAdmin/params/sysParams.vue", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "compass"}},
+
+ // example子菜单
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["example"], Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 5, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["example"], Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["example"], Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: Meta{Title: "客户列表(资源示例)", Icon: "avatar"}},
+
+ // systemTools子菜单
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "autoCode", Name: "autoCode", Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: Meta{Title: "代码生成器", Icon: "cpu", KeepAlive: true}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 3, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 4, Meta: Meta{Title: "系统配置", Icon: "operation"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 2, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}},
+ {MenuLevel: 1, Hidden: true, ParentId: menuNameMap["systemTools"], Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "模板配置", Icon: "folder"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 5, Meta: Meta{Title: "导出模板", Icon: "reading"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "picture", Name: "picture", Component: "view/systemTools/autoCode/picture.vue", Sort: 6, Meta: Meta{Title: "AI页面绘制", Icon: "picture-filled"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "view/systemTools/autoCode/mcp.vue", Sort: 7, Meta: Meta{Title: "Mcp Tools模板", Icon: "magnet"}},
+
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "pubPlug", Name: "pubPlug", Component: "view/systemTools/pubPlug/pubPlug.vue", Sort: 3, Meta: Meta{Title: "打包插件", Icon: "files"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}},
+ {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "anInfo", Name: "anInfo", Component: "plugin/announcement/view/info.vue", Sort: 5, Meta: Meta{Title: "公告管理[示例]", Icon: "scaleToOriginal"}},
+ }
+
+ // 创建子菜单
+ if err = db.Create(&childMenus).Error; err != nil {
+ return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"子菜单初始化失败!")
}
- next = context.WithValue(ctx, i.InitializerName(), entities)
+
+ // 组合所有菜单作为返回结果
+ allEntities := append(allMenus, childMenus...)
+ next = context.WithValue(ctx, i.InitializerName(), allEntities)
return next, nil
}
diff --git a/server/utils/autocode/template_funcs.go b/server/utils/autocode/template_funcs.go
index fd03e20108..3aab00332b 100644
--- a/server/utils/autocode/template_funcs.go
+++ b/server/utils/autocode/template_funcs.go
@@ -11,6 +11,7 @@ import (
// GetTemplateFuncMap 返回模板函数映射,用于在模板中使用
func GetTemplateFuncMap() template.FuncMap {
return template.FuncMap{
+ "title": strings.Title,
"GenerateField": GenerateField,
"GenerateSearchField": GenerateSearchField,
"GenerateSearchConditions": GenerateSearchConditions,
diff --git a/server/utils/casbin_util.go b/server/utils/casbin_util.go
new file mode 100644
index 0000000000..62d44fcfc0
--- /dev/null
+++ b/server/utils/casbin_util.go
@@ -0,0 +1,52 @@
+package utils
+
+import (
+ "sync"
+
+ "github.com/casbin/casbin/v2"
+ "github.com/casbin/casbin/v2/model"
+ gormadapter "github.com/casbin/gorm-adapter/v3"
+ "github.com/flipped-aurora/gin-vue-admin/server/global"
+ "go.uber.org/zap"
+)
+
+var (
+ syncedCachedEnforcer *casbin.SyncedCachedEnforcer
+ once sync.Once
+)
+
+// GetCasbin 获取casbin实例
+func GetCasbin() *casbin.SyncedCachedEnforcer {
+ once.Do(func() {
+ a, err := gormadapter.NewAdapterByDB(global.GVA_DB)
+ if err != nil {
+ zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err))
+ return
+ }
+ text := `
+ [request_definition]
+ r = sub, obj, act
+
+ [policy_definition]
+ p = sub, obj, act
+
+ [role_definition]
+ g = _, _
+
+ [policy_effect]
+ e = some(where (p.eft == allow))
+
+ [matchers]
+ m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
+ `
+ m, err := model.NewModelFromString(text)
+ if err != nil {
+ zap.L().Error("字符串加载模型失败!", zap.Error(err))
+ return
+ }
+ syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a)
+ syncedCachedEnforcer.SetExpireTime(60 * 60)
+ _ = syncedCachedEnforcer.LoadPolicy()
+ })
+ return syncedCachedEnforcer
+}
diff --git a/server/utils/fmt_plus.go b/server/utils/fmt_plus.go
index 6f34cb47c4..1257a65995 100644
--- a/server/utils/fmt_plus.go
+++ b/server/utils/fmt_plus.go
@@ -68,6 +68,23 @@ func MaheHump(s string) string {
return strings.Join(words, "")
}
+// HumpToUnderscore 将驼峰命名转换为下划线分割模式
+func HumpToUnderscore(s string) string {
+ var result strings.Builder
+
+ for i, char := range s {
+ if i > 0 && char >= 'A' && char <= 'Z' {
+ // 在大写字母前添加下划线
+ result.WriteRune('_')
+ result.WriteRune(char - 'A' + 'a') // 转小写
+ } else {
+ result.WriteRune(char)
+ }
+ }
+
+ return strings.ToLower(result.String())
+}
+
// RandomString 随机字符串
func RandomString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
diff --git a/server/utils/jwt.go b/server/utils/jwt.go
index f34b792fa4..b4e6b3b2bb 100644
--- a/server/utils/jwt.go
+++ b/server/utils/jwt.go
@@ -1,6 +1,7 @@
package utils
import (
+ "context"
"errors"
"time"
@@ -85,3 +86,20 @@ func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) {
}
return nil, TokenValid
}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetRedisJWT
+//@description: jwt存入redis并设置过期时间
+//@param: jwt string, userName string
+//@return: err error
+
+func SetRedisJWT(jwt string, userName string) (err error) {
+ // 此处过期时间等于jwt过期时间
+ dr, err := ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
+ if err != nil {
+ return err
+ }
+ timer := dr
+ err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err()
+ return err
+}
diff --git a/server/utils/reload.go b/server/utils/reload.go
deleted file mode 100644
index de5499bf33..0000000000
--- a/server/utils/reload.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package utils
-
-import (
- "errors"
- "os"
- "os/exec"
- "runtime"
- "strconv"
-)
-
-func Reload() error {
- if runtime.GOOS == "windows" {
- return errors.New("系统不支持")
- }
- pid := os.Getpid()
- cmd := exec.Command("kill", "-1", strconv.Itoa(pid))
- return cmd.Run()
-}
diff --git a/server/utils/system_events.go b/server/utils/system_events.go
new file mode 100644
index 0000000000..736ea747f3
--- /dev/null
+++ b/server/utils/system_events.go
@@ -0,0 +1,34 @@
+package utils
+
+import (
+ "sync"
+)
+
+// SystemEvents 定义系统级事件处理
+type SystemEvents struct {
+ reloadHandlers []func() error
+ mu sync.RWMutex
+}
+
+// 全局事件管理器
+var GlobalSystemEvents = &SystemEvents{}
+
+// RegisterReloadHandler 注册系统重载处理函数
+func (e *SystemEvents) RegisterReloadHandler(handler func() error) {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ e.reloadHandlers = append(e.reloadHandlers, handler)
+}
+
+// TriggerReload 触发所有注册的重载处理函数
+func (e *SystemEvents) TriggerReload() error {
+ e.mu.RLock()
+ defer e.mu.RUnlock()
+
+ for _, handler := range e.reloadHandlers {
+ if err := handler(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/web/src/api/autoCode.js b/web/src/api/autoCode.js
index 7fe1af98cb..a6a1118bd5 100644
--- a/web/src/api/autoCode.js
+++ b/web/src/api/autoCode.js
@@ -207,3 +207,11 @@ export const initAPI = (data) => {
data
})
}
+
+export const mcp = (data) => {
+ return service({
+ url: '/autoCode/mcp',
+ method: 'post',
+ data
+ })
+}
diff --git a/web/src/api/system.js b/web/src/api/system.js
index 9395519a3a..ff41abfad6 100644
--- a/web/src/api/system.js
+++ b/web/src/api/system.js
@@ -42,7 +42,7 @@ export const getSystemState = () => {
}
/**
- * 重启服务
+ * 重载服务
* @param data
* @returns {*}
*/
diff --git a/web/src/core/config.js b/web/src/core/config.js
index 709b495a25..bb44caffb3 100644
--- a/web/src/core/config.js
+++ b/web/src/core/config.js
@@ -17,7 +17,7 @@ export const viteLogo = (env) => {
`> 欢迎使用Gin-Vue-Admin,开源地址:https://github.com/flipped-aurora/gin-vue-admin`
)
)
- console.log(greenText(`> 当前版本:v2.8.1`))
+ console.log(greenText(`> 当前版本:v2.8.2`))
console.log(greenText(`> 加群方式:微信:shouzi_1994 QQ群:470239250`))
console.log(
greenText(`> 项目地址:https://github.com/flipped-aurora/gin-vue-admin`)
diff --git a/web/src/core/gin-vue-admin.js b/web/src/core/gin-vue-admin.js
index 034b36b885..0823d950b9 100644
--- a/web/src/core/gin-vue-admin.js
+++ b/web/src/core/gin-vue-admin.js
@@ -10,7 +10,7 @@ export default {
register(app)
console.log(`
欢迎使用 Gin-Vue-Admin
- 当前版本:v2.8.1
+ 当前版本:v2.8.2
加群方式:微信:shouzi_1994 QQ群:622360840
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
diff --git a/web/src/pathInfo.json b/web/src/pathInfo.json
index 05c02a6cde..76707c4291 100644
--- a/web/src/pathInfo.json
+++ b/web/src/pathInfo.json
@@ -26,6 +26,7 @@
"/src/view/layout/aside/headMode.vue": "GvaAside",
"/src/view/layout/aside/index.vue": "Index",
"/src/view/layout/aside/normalMode.vue": "GvaAside",
+ "/src/view/layout/aside/sidebarMode.vue": "SidebarMode",
"/src/view/layout/header/index.vue": "Index",
"/src/view/layout/header/tools.vue": "Tools",
"/src/view/layout/iframe.vue": "GvaLayoutIframe",
@@ -54,9 +55,9 @@
"/src/view/superAdmin/user/user.vue": "User",
"/src/view/system/state.vue": "State",
"/src/view/systemTools/autoCode/component/fieldDialog.vue": "FieldDialog",
- "/src/view/systemTools/autoCode/component/iframeRenderer.vue": "IframeRenderer",
"/src/view/systemTools/autoCode/component/previewCodeDialog.vue": "PreviewCodeDialog",
"/src/view/systemTools/autoCode/index.vue": "AutoCode",
+ "/src/view/systemTools/autoCode/mcp.vue": "Mcp",
"/src/view/systemTools/autoCode/picture.vue": "Picture",
"/src/view/systemTools/autoCodeAdmin/index.vue": "AutoCodeAdmin",
"/src/view/systemTools/autoPkg/autoPkg.vue": "AutoPkg",
diff --git a/web/src/view/systemTools/autoCode/mcp.vue b/web/src/view/systemTools/autoCode/mcp.vue
new file mode 100644
index 0000000000..16f8bc0b4e
--- /dev/null
+++ b/web/src/view/systemTools/autoCode/mcp.vue
@@ -0,0 +1,147 @@
+
+