发布dev2.8.2版本 (#2025)

* refactor(server): 重构服务器启动和重载逻辑

将服务器启动和重载逻辑进行重构,提取初始化系统为单独函数,优化代码结构。删除冗余的服务器初始化文件,统一使用新的 `server_run.go` 实现优雅关闭和重载功能。同时,将“重启服务”改为“重载服务”以更准确地描述功能。

* refactor: 重构系统事件处理、JWT和Casbin相关逻辑

- 将系统重载逻辑提取到独立的`system_events.go`文件中,并引入全局事件管理器
- 将JWT相关操作从`service`层移动到`utils`层,减少服务层依赖
- 将Casbin实例管理逻辑提取到`utils`层,统一管理Casbin实例的初始化和获取
- 删除冗余的`CreateSysOperationRecord`方法,优化操作记录中间件逻辑

* refactor(server): 重构服务初始化和关闭逻辑

将 `RunServer` 函数重命名为 `initServer`,并调整其调用方式以简化代码。同时,在系统初始化时添加 `SetupHandlers` 函数以注册全局处理函数,提升代码可维护性。

* fix: 修复自动化代码enum查询条件的bug

* fix: 修复组合模式下,顶部菜单重复bug

* refactor: 修改名称 RunWindowsServer ==> RunServer

* 新增mcp

* feat: 支持mcp服务

* feat:调整mcp结构,增加客户端和测试用例

* feat:更换mcp基础包和结构

* feat:提交客户端工具测试用例

* feat: 增加自动创建 mcp Tool模板 功能

* fix: 增加默认值属性

* feat: 调整初始化menu的逻辑

* feat: 调整初始config.yaml

* feat: 增加全局GVA_MCP_SERVER属性,方便灵活化开发。

* feat: 优化自动化mcp逻辑和成功展示

* feat: 优化mcp tool nickname工具

* feat: 发布2.8.2 Beta版本

---------

Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
Co-authored-by: Gor-c <creup@outlook.com>
Co-authored-by: QIN xiansheng <sjjlnaps@163.com>
This commit is contained in:
PiexlMax(奇淼
2025-05-13 19:24:54 +08:00
committed by GitHub
parent 7a7c811311
commit 6323688fca
55 changed files with 1124 additions and 276 deletions

View File

@@ -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
}

View File

@@ -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())
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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