Compare commits

...
215 Commits
Author SHA1 Message Date
pixelmaxQM 78cdc287c5 docs: 更新项目规则文档中的角色描述为全栈专家 2025-09-07 12:46:59 +08:00
piexlMax(奇淼 fb199aeedc fix: 修复多出来的一个/ 2025-09-04 15:16:31 +08:00
piexlMax(奇淼 364dddb70a Merge branch 'main' of github.com:flipped-aurora/gin-vue-admin 2025-09-03 16:32:46 +08:00
piexlMax(奇淼 6bf848d53f style: 统一使用!前缀替代方括号的宽度类名
修改模板文件中的宽度类名,从w-[380px]和w-40统一改为!w-380px和!w-40
修复模板中条件判断的语法格式问题
2025-09-03 16:29:52 +08:00
青菜白玉汤 3e429f4091 feat: 增加依赖,提高 unocss 相关兼容性 (#2088)
* fix: 补充部分运行时依赖

* optimize: 优化class mark.
2025-09-02 15:03:48 +08:00
piexlMax(奇淼 daabca4768 Merge branch 'main' of github.com:flipped-aurora/gin-vue-admin 2025-09-01 15:19:22 +08:00
piexlMax(奇淼 9e688c65d3 fix(richtext): 为富文本编辑器上传图片添加token认证头
添加用户token到图片上传请求头中,确保有权限的用户才能上传文件
2025-09-01 15:17:58 +08:00
pixelmaxQM 2e8f5870d8 refactor(model): 调整版本数据字段的数据库类型注释
为VersionData字段添加postgres和sqlite数据库特定的类型注释,提高跨数据库兼容性
2025-08-31 00:16:32 +08:00
Qing Liang 8b0a00b98a Update project_rules.md (#2083) 2025-08-29 11:44:59 +08:00
piexlMax(奇淼 1ce3e11d52 fix(selectFile): 修复未初始化时fileList为undefined的问题 2025-08-28 15:34:35 +08:00
piexlMax(奇淼 11aa8c5cc2 feat(version): 添加字典数据支持到版本管理功能 2025-08-28 15:11:52 +08:00
piexlMax(奇淼 d8e6f2f5f4 fix: 同步 go.mod 2025-08-28 13:58:29 +08:00
piexlMax(奇淼 9d5c93b4c3 refactor(version): 统一版本号管理并更新至v2.8.5
将版本号从硬编码改为统一管理,使用package.json和全局变量维护版本信息
2025-08-28 13:51:54 +08:00
Qing Liang 2f7f3e5ac6 other:注释 重置用户密码 (#2081) 2025-08-28 13:37:04 +08:00
PiexlMax(奇淼 cb97d3832d Merge pull request #2080 from flipped-aurora/v2.8.5
发布v2.8.5 beta 版本
2025-08-28 11:45:01 +08:00
piexlMax(奇淼 dd34d6e3be fix: mcp字段统一样式限制 2025-08-26 18:43:55 +08:00
PiexlMax(奇淼 1d700ad5b6 Update README.md 2025-08-19 18:26:17 +08:00
PiexlMax(奇淼 3648df9522 Merge pull request #2070 from dzwvip/main
修改gorm-oracle的驱动包为go-ora,不再依赖cgo和oracle instant  client
2025-08-14 15:33:51 +08:00
dzwvip eb2925829f 修改gorm-oracle的驱动包为go-ora,不再依赖cgo和oracle client 2025-08-12 15:05:38 +08:00
piexlMax(奇淼 1e3c968277 refactor(mcp): 更新默认值描述并移除无用工具 2025-08-11 10:52:15 +08:00
piexlMax(奇淼 339db778c3 chore: 优化引入svg方式 2025-08-08 11:46:08 +08:00
piexlMax(奇淼 8b8ae7b692 refactor(auth): 简化权限指令中的类型检查逻辑 2025-08-07 16:18:04 +08:00
piexlMax(奇淼 52be26eb43 fix: 修正未配置默认路由时的错误提示信息 2025-08-06 15:55:38 +08:00
piexlMax(奇淼 9f958addc8 fix(verify): 移除登录验证中的Captcha字段校验
refactor(validation): 强制字段名首字母大写并更新文档
2025-08-06 15:18:03 +08:00
piexlMax(奇淼 7a69b88885 refactor(config): 更新配置文件格式并添加新配置项 2025-08-06 13:47:09 +08:00
pixelmaxQM 76fe71a8dd docs(mcp): 完善工具描述并添加重要限制说明 2025-08-05 20:35:12 +08:00
piexlMax(奇淼 3f21d7f8dd docs(mcp): 更新字段类型的支持说明文档 2025-08-05 18:56:57 +08:00
PiexlMax(奇淼 1fce169616 Merge pull request #2065 from flipped-aurora/2.8.5
支持多模块mcp,精细化api和menu的创建规则。
2025-08-05 18:43:21 +08:00
piexlMax(奇淼 0d900293eb docs: 更新文档中字段类型描述并优化工具说明
更新多个文档和工具描述文件,统一字段类型的详细说明格式
优化菜单创建工具和API创建工具的描述,明确使用场景
补充执行计划schema中字段类型的详细说明

(cherry picked from commit d9448aa25cca717e01a00aec2c38a24da7b28f6f)
2025-08-05 18:39:13 +08:00
piexlMax(奇淼 b71ad7b6f4 docs(mcp): 完善模块关联配置的文档和示例
(cherry picked from commit e042d0464c47ab8441f363df1a1242e26a9c13f7)
2025-08-05 18:39:13 +08:00
PiexlMax(奇淼 2139e38147 refactor: optimize and simplify login captcha validation flow (#2063)
* Optimize captcha related fields verification

- Add `Captcha` field validation to `LoginVerify` rules, ensuring that the captcha isn't empty while prechecking the data.
- Remove redundant checks for empty values for `Captcha` and `CaptchaId`, as they are already validated in the `LoginVerify` struct.
- Move client IP key retrieval after input validation for better flow.

* refactor: simplify login captcha validation flow for cleaner code
2025-08-04 18:35:28 +08:00
Feng.YJ a033ab9abe refactor: simplify login captcha validation flow for cleaner code 2025-08-04 13:43:03 +08:00
Feng.YJ 311e1ddb9f Optimize captcha related fields verification
- Add `Captcha` field validation to `LoginVerify` rules, ensuring that the captcha isn't empty while prechecking the data.
- Remove redundant checks for empty values for `Captcha` and `CaptchaId`, as they are already validated in the `LoginVerify` struct.
- Move client IP key retrieval after input validation for better flow.
2025-08-04 13:39:51 +08:00
pixelmaxQM 31e6316916 feat(mcp): 实现需求分析工具并支持批量创建模块
1. 新增requirement_analyzer工具,用于将用户需求转换为结构化提示词
2. 修改gva_auto_generate工具支持批量创建多个模块
3. 更新文档示例和schema以反映批量创建功能
4. 优化执行计划验证逻辑,支持模块数组校验
5. 改进字典创建流程,避免重复创建相同字典类型
2025-08-03 23:58:47 +08:00
PiexlMax(奇淼 9260111312 Update README.md 2025-08-01 13:31:50 +08:00
PiexlMax(奇淼andpiexlMax(奇淼 877f8f4fcf feat(autoCode): 添加空包检测与自动清理功能 (#2061)
refactor(analyzer): 重构模块分析逻辑,增加空包检测

Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
2025-08-01 13:22:40 +08:00
273d7bd50e V2.8.4Beta 极致融合AI编辑器 让开发速度更进一步 (#2060)
* 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>
2025-07-31 21:21:04 +08:00
PiexlMax(奇淼 39abc7b632 Update README.md 2025-07-16 14:46:50 +08:00
piexlMax(奇淼 e19ddf6600 public: 发布2.8.3版本 2025-07-15 18:22:04 +08:00
piexlMax(奇淼 9db3bcfac5 Merge branch 'main' of github.com:flipped-aurora/gin-vue-admin 2025-07-15 10:48:53 +08:00
piexlMax(奇淼 a6b6b5d4aa fix: 修正变量名拼写错误,将detailFrom统一改为detailForm 2025-07-15 10:45:58 +08:00
pixelmaxQM 23f9227da1 fix(table): 修复日期列的模板语法错误,确保正确渲染创建日期 2025-07-11 00:20:29 +08:00
pixelmaxQM 857ba194e5 fix(menu): 更新成功消息提示,添加角色管理页面权限分配说明;优化菜单添加逻辑,增加父菜单存在性和权限检查 2025-07-11 00:14:14 +08:00
pixelmaxQM 6e27e3a114 fix(menus): 在设置首页菜单以后保存当前菜单结构,防止把无权限菜单设置为首页导致系统死锁 2025-07-10 23:36:27 +08:00
piexlMax(奇淼 d5b4bbfa84 fix(autoCode): 删除不必要的路由参数监听 2025-07-10 16:11:51 +08:00
piexlMax(奇淼 72ee8fb6fc fix(router): 修复路由keep-alive逻辑并添加组件名称 2025-07-10 14:52:59 +08:00
gorsandPiexlMax(奇淼 2ec2e9a66e Update service.go.tpl (#2053)
fix 排序时错误获取失败:错误: 字段 "createdat" 不存在 (SQLSTATE 42703)

---------

Co-authored-by: PiexlMax(奇淼 <165128580+pixelmaxQm@users.noreply.github.com>
2025-07-03 13:19:44 +08:00
piexlMax(奇淼 bf095c38e9 fix(service): 表格排序ID变为小写 2025-07-03 11:50:14 +08:00
piexlMax(奇淼 cee3b54b4c fix(loading): 修复loading状态管理问题并添加超时处理 2025-07-03 11:42:39 +08:00
piexlMax(奇淼 ff7e002049 fix: 修复richtext的情况下gorm标签多一个"的bug 2025-07-02 10:36:50 +08:00
zayn a1c8f6bc55 逻辑错误调整 (#2052) 2025-06-30 16:04:18 +08:00
青菜白玉汤andPiexlMax(奇淼 ca24945af6 feat: 重构设置界面,添加主题模式、颜色选择和布局配置模块,移除冗余组件 (#2050)
* feat: 重构设置界面,添加主题模式、颜色选择和布局配置模块,移除冗余组件

---------

Co-authored-by: PiexlMax(奇淼 <165128580+pixelmaxQm@users.noreply.github.com>
2025-06-29 13:27:12 +08:00
我看谁不卷 ea7455e092 fix:修复查询条件无法添加问题 (#2047) 2025-06-28 22:30:45 +08:00
Joy a5e1170af0 web/src/utils/request.js: Fix Server 返回非 JSON 响应数据时导致错误弹窗 (#2049) 2025-06-28 22:29:02 +08:00
pixelmaxQM 44751357b4 feat: 添加超时中间件以处理请求超时 2025-06-28 17:45:53 +08:00
zayn e58d40ab83 头部导航栏里的菜单无法正常渲染出来的bug (#2046)
菜单循环中没有子菜单就直接返回,没有添加到arr列表中导致arr始终是空的
2025-06-20 13:36:43 +08:00
piexlMax(奇淼 f7c3d76d37 feat(菜单管理): 重构菜单表单界面并更新element-plus依赖 2025-06-19 15:33:05 +08:00
hzbnb~ 55bf69c810 fix: 修复不小心选择业务库后想删掉的问题 (#2042) 2025-06-12 13:13:02 +08:00
Shan Chenyu b4232a8f27 更新 captcha.go 注释拼写错误 (#2043) 2025-06-12 13:12:28 +08:00
Hu Haolong 8d6dcc6b07 sys_authority.go: Fix a potential nil pointer dereference in GetParentAuthorityID (#2044) 2025-06-12 13:12:08 +08:00
piexlMax(奇淼 e3a57bdc69 update: 同步go.sum 2025-06-11 15:42:16 +08:00
piexlMax(奇淼 0eee6a3cc2 update: 升级mcp-go依赖至v0.31.0 2025-06-10 10:54:06 +08:00
计乐心 174721bc55 新增支持LoginAuth认证方式适用于IBM、微软等部分邮箱服务 (#2041) 2025-06-05 11:31:25 +08:00
OPSTime ba63bf2371 修正导出execl中数字为文本类型问题 (#2039) 2025-06-05 11:29:35 +08:00
hzbnb~ 2f07e9271f fix: 修复在多库模式下生成代码时候用默认库用错的情况 (#2038) 2025-06-05 11:19:30 +08:00
shabby2333 3e9602ff7e fix: #2030 富文本编辑器全屏时部分内容会被其他表单项遮挡 (#2040) 2025-06-05 11:08:28 +08:00
K8sCat 30763b48de db_list.go: 移除不必要的case判断 (#2036) 2025-06-05 11:06:17 +08:00
bossa c3b75933b8 系统生成文件,当需要手动实现增加TODO标识 (#2037) 2025-06-05 11:05:29 +08:00
PiexlMax(奇淼 ff08d45f25 Revert "修正导出数字为文本格式问题 (#2033)" (#2034)
This reverts commit b916c7951b.
2025-05-28 17:15:39 +08:00
hzbnb~ be0e4e97f7 feat: 添加暂存成功提醒 (#2031) 2025-05-28 15:49:45 +08:00
OPSTimeandCai Meng 蔡猛 b916c7951b 修正导出数字为文本格式问题 (#2033)
Co-authored-by: Cai Meng 蔡猛 <caimeng@wisers.com>
2025-05-28 15:49:28 +08:00
piexlMax(奇淼 0ebb3d4195 update: 更新MCP为0.29.0,并且调节对应的方法。 2025-05-22 16:10:47 +08:00
piexlMax(奇淼 e7680c43ac fix: 处理SQL条件中IN和NOT IN操作符的格式化问题
在生成SQL查询条件时,当操作符为IN或NOT IN时,未正确格式化SQL语句。现已修复此问题,确保生成的SQL语句符合预期格式。
2025-05-21 17:41:54 +08:00
piexlMax(奇淼 2f80eaaf9c fix(exportTemplate): 添加IN和NOT IN操作符并修复参数解析问题
在导出模板功能中添加IN和NOT IN操作符,以支持更复杂的查询条件。同时修复了参数解析逻辑,确保从params参数中正确提取过滤条件、分页和排序等参数,避免直接使用values导致的解析错误。
2025-05-21 17:11:23 +08:00
hang666 2dfe9d7f4b fix: 修复代码生成中时间和富文本的搜索错误 (#2028)
* fix: 修复代码生成中时间和富文本的搜索错误
2025-05-19 15:30:27 +08:00
piexlMax(奇淼 cbc504cc0b fix(用户模块): 修正清理存储逻辑以正确删除cookie和localStorage项 2025-05-19 14:43:05 +08:00
piexlMax(奇淼 99550cbc37 feat: 图片选择功能增加拖拽 2025-05-19 13:58:20 +08:00
pixelmaxQM 4a53864738 fix:调整自动化MCP Tools模板 2025-05-18 11:45:36 +08:00
PiexlMax(奇淼andhang666 b731b9df78 fix: 修正搜索时间范围传入ISO8601字符串导致MySQL查询出错,并优化nil切片len检查(S1009) (#2027)
* fix: 修正搜索时间范围传入ISO8601字符串导致MySQL查询出错,并优化nil切片len检查(S1009) (#2026)

* fix:时间范围修改接受参数

---------

Co-authored-by: hang666 <44329474+hang666@users.noreply.github.com>
2025-05-17 15:41:37 +08:00
piexlMax(奇淼 1f3a8ea2eb feat: 不再对查询条件时间范围进行检测 2025-05-15 18:42:58 +08:00
piexlMax(奇淼 2bd82ead97 refactor(搜索功能): 优化时间范围搜索逻辑,使用createdAtRange替代startCreatedAt和endCreatedAt
将时间范围搜索逻辑从`startCreatedAt`和`endCreatedAt`改为`createdAtRange`,简化代码并提高可维护性。同时更新了相关模板文件和验证规则,确保前后端一致性和用户体验的连贯性。
2025-05-15 18:40:57 +08:00
piexlMax(奇淼 9f1f72fce5 update: 修正前端版本号 2025-05-14 16:25:57 +08:00
piexlMax(奇淼 38ed8bb399 feat: 删除多余的服务初始化 2025-05-14 14:36:43 +08:00
piexlMax(奇淼 2d98fdf321 feat: tool卡片页面优化 2025-05-14 12:47:59 +08:00
piexlMax(奇淼 214e108591 feat(mcpTest): 添加复制MCP配置功能 2025-05-14 12:41:52 +08:00
piexlMax(奇淼 b197a84ef7 feat:提交并优化mcp配置 2025-05-14 12:39:20 +08:00
piexlMax(奇淼 09603c436e feat: 增加 mcp Tools 自动测试工具 2025-05-14 10:51:52 +08:00
pixelmaxQM d4c883535b fix:mcp tools 编译不通过的bug 2025-05-13 21:58:21 +08:00
6323688fca 发布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>
2025-05-13 19:24:54 +08:00
PiexlMax(奇淼 7a7c811311 Update README.md 2025-05-13 19:22:50 +08:00
Shinji-Li 81e178b267 docs: update a new contributions charts (#2024) 2025-05-13 19:21:58 +08:00
PiexlMax(奇淼 70c9a715d4 Update README.md 2025-05-07 16:03:32 +08:00
qSeisCloud 28bd6fef7e auto_code_plugin.go: update deprecated lib mholt/archiver/v4 to mholt/archives (#2023)
* update deprecated lib mholt/archiver/v4 to mholt/archives
2025-05-07 09:59:37 +08:00
pixelmaxQM 35e3f820c0 fix: 搜索条件增加array的特殊处理 2025-05-05 14:21:23 +08:00
piexlMax(奇淼 e847aa9e75 fix: 修复顶栏菜单重复出现的bug 2025-04-30 09:56:36 +08:00
pixelmaxQM fcfa55b39a feat: 修复自动化代码插件form引用模板错误的问题 2025-04-29 22:33:09 +08:00
qSeisCloud 8be390b2cf 修复表格标题行首尾空不匹配或多余的标题导致的SQL语句错误问题
修复表格标题行首尾空不匹配或多余的标题导致的SQL语句错误问题
2025-04-29 13:21:22 +08:00
Gor-c 3ad83cbcd6 fix: 修复组合模式下,顶部菜单重复bug 2025-04-29 11:16:17 +08:00
piexlMax(奇淼 41826f5e5c fix: 优化Windows服务器启动时Redis初始化逻辑 2025-04-28 15:38:36 +08:00
pixelmaxQM 0ccd6dc750 fix: 修复顶栏多出一个...的问题 2025-04-24 21:47:47 +08:00
pixelmaxQM 8c2ce8587d feat: 修复自动化代码的bug 2025-04-24 21:18:31 +08:00
pixelmaxQM 5e35fcea58 fix: 修复菜单无ICON的展示问题 2025-04-21 23:49:38 +08:00
pixelmaxQM 54595d46e5 fix: 修复顶栏模式bug和侧栏无图标的bug 2025-04-21 23:15:08 +08:00
piexlMax(奇淼 09cb29da17 feat: VITE_EDITOR 默认值 调整为 code 2025-04-21 18:19:03 +08:00
piexlMax(奇淼 ecf1b5bc56 fix: 修复自动化代码enum查询条件的bug 2025-04-21 15:08:05 +08:00
piexlMax(奇淼 da71231be9 Merge branch 'main' of github.com:flipped-aurora/gin-vue-admin 2025-04-18 11:43:19 +08:00
piexlMax(奇淼 63feecf4aa update: 发布2.8.1版本 2025-04-18 11:41:57 +08:00
a6255557ee 发布2.8.1 beta版本 (#2014)
AI: 增加了AI前端绘制功能,可以根据描述生成客户端页面【授权用户专属】

自动化: 自动化模板采用了function模式,更加方便用户二次开发和自定义改动

自动化: 默认携带ID和CreatedAt排序

自动化: 所有自动化Select模板默认支持select搜索

优化:http交互报错信息增加防止多次弹出错误遮罩机制

ICON: 优化ICON逻辑,防止多次加载svg

布局:增加侧边分栏模式
布局: 顶栏模式样式优化和高亮逻辑调整
优化: 个人配置不再需要手动点击保存,会根据变化自动保存

BUG: 修复了菜单点击设为主页勾选被取消的bug
安全: 更新了jwt版本,修复CVE-2025-30204
导出: 默认支持软删除过滤
代码: 优化了部分代码逻辑
本次更新需要重新执行 npm i

---------

Signed-off-by: joohwan <zhouhuan.chen@yunqutech.com >
Co-authored-by: huiyifyj <jxfengyijie@gmail.com>
Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
Co-authored-by: okppop <okppop@protonmail.com>
Co-authored-by: joohwan <zhouhuan.chen@yunqutech.com >
Co-authored-by: xuedinge <781408517@qq.com>
2025-04-18 11:40:03 +08:00
piexlMax(奇淼 84a3c3ba7c fix: 修复redirect跳转的bug 2025-04-15 18:01:01 +08:00
piexlMax(奇淼 90c8a240a1 Merge branch 'main' of github.com:flipped-aurora/gin-vue-admin 2025-04-15 16:46:09 +08:00
piexlMax(奇淼 07c694e24d fix: 修复点击分配按钮,勾选的菜单消失的bug 2025-04-15 16:43:22 +08:00
pixelmaxQM 7c1c392710 feat: 固定svg导入版本为1.1.0 2025-04-12 19:12:30 +08:00
piexlMax(奇淼 379f5fe841 Merge remote-tracking branch 'refs/remotes/origin/dev-281' 2025-04-09 09:51:04 +08:00
piexlMax(奇淼 0ab15cc5e2 feat: 模板方法抽离 不在模板中做逻辑 2025-04-08 18:48:39 +08:00
PiexlMax(奇淼 8f7a8a9e6d Update bug_report.yaml 2025-04-08 17:53:27 +08:00
PiexlMax(奇淼 d5dc42c71e Update feature_request.yaml 2025-04-08 17:53:09 +08:00
pixelmaxQM 6ae5b2384e feat: 增加默认AI绘制功能页面 2025-04-07 17:34:59 +08:00
pixelmaxQM 07c9e97bf5 feat: 增加复制功能 2025-04-07 17:31:31 +08:00
pixelmaxQM 5a53da62e3 feat: 调整AI基础路径 2025-04-07 17:25:09 +08:00
pixelmaxQM 11748da7a9 feat: 调整提示语 2025-04-07 17:20:53 +08:00
pixelmaxQM b0e15b86f5 Merge remote-tracking branch 'origin/main' into dev-281 2025-04-07 17:11:32 +08:00
pixelmaxQM 1d1ad23bc0 feat: 页面可以拖动选择 2025-04-07 17:08:20 +08:00
pixelmaxQM ac54446769 feat: 增加AI生成页面功能。 2025-04-07 17:03:24 +08:00
pixelmaxQM 5fe5caaf2b feat: 增加AI生成页面功能。 2025-04-07 17:03:18 +08:00
pixelmaxQM e111b01f16 feat: 调整重置配置属性,增加自动出页面功能 2025-04-07 13:15:53 +08:00
pixelmaxQM fd2ac3f7f1 Merge remote-tracking branch 'refs/remotes/origin/autoCode-picture' into dev-281 2025-04-07 13:12:50 +08:00
pixelmaxQM 4e57b75640 Merge remote-tracking branch 'origin/dev-281' 2025-04-07 13:03:50 +08:00
pixelmaxQM e439bff2bc feat: 调整个人配置功能,每次改变自动保存,右上角保存修改为重置。 2025-04-07 13:00:14 +08:00
PiexlMax(奇淼 e210002bff Merge pull request #2003 from huiyifyj/ctx-nil-and-others
fix: check for nil and return explicit type for `UseWithCtx` method
2025-04-07 12:46:03 +08:00
pixelmaxQM 4e735beb2f fix: 组合模式下 获取左侧菜单栏 2025-04-07 12:38:26 +08:00
pixelmaxQM d747edc1f3 fix: 禁止浏览器的默认游标行为 2025-04-07 12:17:38 +08:00
huiyifyj 8bec94336b test: fix missing isPackage argument called method 2025-04-01 16:46:36 +08:00
huiyifyj 83eb193f1b fix UseWithCtx method to check for nil context
- Add nil check for context parameter.
- UseWithCtx method return explicit type rather than interface.

Replace base64Captcha.Store return type with *RedisStore for better type safety.
2025-04-01 16:26:31 +08:00
piexlMax(奇淼 9806f72ca6 fix: 设为首页按钮防止事件穿透 2025-04-01 14:23:46 +08:00
pixelmaxQM 7e7ec52290 feat: 防止npm i 报错 2025-03-27 22:24:20 +08:00
pixelmaxQM 98746fbca5 feat: 更新前端依赖 2025-03-26 20:35:10 +08:00
PiexlMax(奇淼 c0f65386bd Merge pull request #2000 from ByteZhou-2018/m_20250318_gorm_logic
GORM初始化数据库优化,通过抽取重复的逻辑避免代码冗余。
2025-03-26 13:31:23 +08:00
pixelmaxQM cef6fce2c9 feat: 菜单增加单独的动画选择机制 2025-03-20 22:32:20 +08:00
ByteZhou-2018 4964518486 fix pgsql return error to panic 2025-03-18 21:57:51 +08:00
ByteZhou-2018 06ec433b4e Optimize GORM database initialization by extracting duplicate logic 2025-03-18 21:35:10 +08:00
pixelmaxQM 85681af8b8 feat: 默认注册表增加 info表和图库分类表 2025-03-16 12:09:14 +08:00
Qing Liang 4b12ecdf4b bugfix:mongodb没有密码无法连接 (#1999) 2025-03-16 12:00:22 +08:00
pixelmaxQM e8eacb02d6 feat: 未登录情况下防止进入路由死循环 2025-03-16 11:53:44 +08:00
pixelmaxQM e9dd55f4f9 feat: 修复未登录情况下死循环的bug 2025-03-15 16:42:36 +08:00
pixelmaxQM a79df727c9 feat: 给扫码上传添加客户端标记 2025-03-15 14:06:17 +08:00
pixelmaxQM ca47b1c4f5 feat: 在登录状态下进入白名单页面强制拉取到当前角色的默认页面 2025-03-15 14:05:58 +08:00
ab75bc7f59 public: 发布2.8.0版本 (#1998)
* feat:修改token获取优先级,优先从header获取

* feat: 增加业务ctx的传递

* fix文件选择问题,
有公共函数onDownloadFile,去掉downloadFile

* 没问题

* feat: 增加context引入

* fix: 修复图片多选的情况下点击确定抽屉无法收起的bug

* feat: 提高header的token优先级,优化导出表格逻辑,不再依赖于cookie鉴权

---------

Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
Co-authored-by: task <121913992@qq.com>
2025-03-15 13:31:33 +08:00
PiexlMax(奇淼 f776a061ce Update README.md 2025-03-13 11:05:33 +08:00
krank 1456f95b2c Update README.md 2025-03-08 13:34:23 +08:00
krank c5767f6ad5 Update README.md 2025-03-08 13:32:41 +08:00
haimait 3f8dedba80 fix: 修改导入excel模板数据注释 (#1995) 2025-03-05 10:15:49 +08:00
pixelmaxQM 121d62f20f fix: 修复图片多选的情况下点击确定抽屉无法收起的bug 2025-03-03 20:05:48 +08:00
pixelmaxQM 2c08265210 fix: 修复个人样式配置情况下false无法生效的问题 2025-02-25 20:43:58 +08:00
PiexlMax(奇淼 82e397f136 fix:修复mssql中getDB失败的bug 2025-02-20 15:29:46 +08:00
taskandtask 730ee0205e 媒体库增加裁剪上传,扫码上传 (#1994)
Co-authored-by: task <121913992@qq.com>
2025-02-19 18:08:31 +08:00
pixelmaxQM a4716d5db0 fix: 修复iframe url传入不识别的bug 2025-02-16 12:28:34 +08:00
pixelmaxQM 06f166b70a feat: 发布2.7.9版本 2025-02-16 11:52:41 +08:00
pixelmaxQM f2ea36ac0d feat: 调整初始化数据 2025-02-15 16:58:33 +08:00
piexlMax(奇淼 a8e706a395 feat: 调整自动化代码富文本模式下的可选查询条件 2025-02-13 16:20:52 +08:00
bypanghu f608bfa191 fix:设置添加页面切换动画 2025-02-13 15:39:09 +08:00
piexlMax(奇淼 e494e22c07 feat: 调整版本为v2.7.9-beta 2025-02-13 15:33:01 +08:00
d40c815760 2.7.9beta (#1993)
* 媒体库增加分类,图库多选择时优化。

* 重构 JWT token 生成,使用 `New()` 函数代替直接创建实例

* 上传组件支持查看大图 (#1982)

* 将参数缓存,媒体库增加分类,图库多选择时优化。 (#1978)

* 媒体库增加分类,图库多选择时优化。

*修复文件上传进度显示bug&按钮样式优化 (#1986)

* fix:添加内部 iframe 展示网页,优化 permission 代码

* 俩个uuid库合并一个,更新库到当前版本。

* 优化关于我们界面

* feat: 个人中心头像调整,媒体库兼容性调整。

* feat: 自动化代码前端页面美化,多余按钮收入专家模式

* feat: 增加单独生成server功能

* feat: 限制单独生成前后端的情况下的细节配置

* feat: 修复全选失败报错的问题

---------

Co-authored-by: task <121913992@qq.com>
Co-authored-by: Feng.YJ <32027253+huiyifyj@users.noreply.github.com>
Co-authored-by: will0523 <dygsunshine@163.com>
Co-authored-by: task <ms.yangdan@gmail.com>
Co-authored-by: sslee <57312216+GIS142857@users.noreply.github.com>
Co-authored-by: bypanghu <bypanghu@163.com>
Co-authored-by: Azir <2075125282@qq.com>
Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
Co-authored-by: krank <emosick@qq.com>
2025-02-13 15:25:10 +08:00
PiexlMax(奇淼 ea39d83907 Update README.md 2025-02-10 16:44:35 +08:00
pixelmaxQM dae38d9b18 fix:菜单复合模式下,点击一级菜单会自动选择首位子菜单,不含子菜单则定位为顶栏本体。 2025-02-09 19:56:51 +08:00
krank 1e7cf9e65d feat: AI识图前端页面 2025-01-10 16:33:59 +08:00
药要吃 831bd61e02 更新个人信息页样式 这个头 得开 (#1977)
* 更新个人信息页样式
2025-01-09 17:14:39 +08:00
pixelmaxQM fe2b846dbc feat: 提交发布2.7.8版本 2025-01-01 14:49:27 +08:00
pixelmaxQM 8c47d2b179 feat: 提交发布2.7.8版本 2025-01-01 14:49:27 +08:00
Leslie afe6b234cf fixed: 修复递归获取角色列表数据缺失问题 (#1974) 2024-12-31 11:38:04 +08:00
piexlMax(奇淼 1743c763d2 feat: gorm的debug日志无论是否开启了zap都需要输出到控制台 2024-12-31 10:51:33 +08:00
Feng.YJ 21237f8be1 模板文件文档注释修改 (#1973)
* 模板文件文档注释修改

* 补充模板相关修改
2024-12-30 17:08:17 +08:00
piexlMax(奇淼 d19e446416 fixed: 修复无法创建新内容的bug 2024-12-26 15:16:58 +08:00
piexlMax(奇淼 8b93ede959 fixed: 修复sql日志无法加入统一zap输出的bug 2024-12-25 13:51:02 +08:00
PiexlMax(奇淼 cb23254152 给自动化代码增加视觉功能 (#1970)
* update:增加视觉能力。

* feat: 自动化代码增加视觉识别功能
2024-12-22 00:14:03 +08:00
Feng.YJ 601aa6e245 代码优化及错误返回改进 (#1969)
* Makefile: 使用 '?=' 替代 if-else 的判断

* 移除多余的类型转换

* 移除多余的nil判断

* map 使用简化以及错误处理优化
2024-12-22 00:13:22 +08:00
Feng.YJ 532d282509 修复生成的结构体 tag 字段错误以及其它注释和尾部空格问题 (#1968)
* 修复生成的结构体 tag 字段错误

* 移除字段注释多余尾部空格

* 文件模板注释 `@accept` 保持规范
2024-12-20 16:50:45 +08:00
Feng.YJ 93c0525ea5 生成模板中注释文档 query 修改 (#1967)
* 生成模板中注释文档 query 修改
2024-12-19 17:21:22 +08:00
Feng.YJ 2bf423b42f 修复可能的 context 泄露以及遗漏的错误处理 (#1965)
* 修复结构体 tag 字段的错误使用
2024-12-17 16:59:06 +08:00
Brandon-lz f6c2ce45c1 修复限流不返回报错信息的bug (#1966) 2024-12-17 16:57:28 +08:00
Feng.YJ d24eeefc86 添加 swagger 文档接口排序的 @Tag 设置说明 (#1964)
* fix: 修复欢迎信息中的 tab 缩进格式

* 添加 swagger 文档生成

* 添加 swagger 文档接口排序的 `@Tag` 设置说明
2024-12-16 14:25:38 +08:00
pixelmaxQM 876d4187d9 fixed: 过滤条件添加富文本 2024-12-15 13:34:14 +08:00
pixelmaxQM 2412e7151f fixed: 修复富文本组件 查看详情没有正确展示的bug 2024-12-15 13:31:33 +08:00
Isaacedvr1003andzsc 07c21caf8d fixed:修复表名是大写字母开头时,自动化代码异常问题。 (#1961)
Co-authored-by: zsc <zhangshuncheng@dhcc.com.cn>
2024-12-13 17:51:00 +08:00
青菜白玉汤 69ddb2ddff fix: 修复自动化代码管理侧边栏自动展开的问题 (#1959) 2024-12-11 17:51:10 +08:00
283143e1bf 发布v2.7.8Beta1 (#1957)
* beta:2.7.8-a 增加自动化创建树形结构 (#1941)

* feat: 支持创建树形结构

---------

Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>

* 优化user store部分写法

* Update user.js

* feat: 升级版本号

* Dev 278 beta2 (#1954)

* 在关闭详情弹窗后 detailFrom为空对象,arr为undefined 使用slice控制台会报错

* 查询不重置pageSize

* 优化主题模式相关内容

* 优化弹窗手机端显示

* bugfix:PostgreSQL initdb (#1953)

* bugfix:postgresql增加显示指定template

---------

Co-authored-by: PiexlMax(奇淼 <165128580+pixelmaxQm@users.noreply.github.com>

---------

Co-authored-by: zayn <972858472@qq.com>
Co-authored-by: Azir <2075125282@qq.com>
Co-authored-by: Qing Liang <106448173+xue-ding-e@users.noreply.github.com>

* docs:调整部分代码注释以及代码格式

* feat: 自动化代码字典支持多选

* fix:调整值接收器和指针接收器

* feat: 支持导出表格复制,优化增加方法页面。

* chore:初始化代码规范化。

---------

Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
Co-authored-by: Azir <2075125282@qq.com>
Co-authored-by: zayn <972858472@qq.com>
Co-authored-by: Qing Liang <106448173+xue-ding-e@users.noreply.github.com>
Co-authored-by: cjb <75364055@qq.com>
2024-12-08 21:17:21 +08:00
PiexlMax(奇淼 80c54ad890 Merge pull request #1946 from neo-6/neo-6-patch-1
解决Invalid vnode type when creating vnode的警告问题
2024-12-04 20:29:35 +08:00
天梦 3272f3c8cc 修复长度验证中数字验证 2024-12-02 18:47:28 +08:00
天梦 6bfbfc4762 解决传参时提示错误警告 2024-12-02 18:29:44 +08:00
天梦 8d2735f6e9 解决Invalid vnode type when creating vnode的警告问题 2024-12-02 15:59:25 +08:00
ipanghu d72984be7e 修复 dockerfile 添加缓存层 (#1943) 2024-11-25 15:41:08 +08:00
pixelmaxQM 39212666c5 feat: 修复导出在baseApi配置为/时候的路径拼接错误 2024-11-21 20:00:27 +08:00
PiexlMax(奇淼 c909d32c56 fix: 无法自动化新增字段的bug 2024-11-19 16:31:43 +08:00
zayn 8073e989f5 查询不重置pageSize (#1940)
* 查询不重置pageSize
2024-11-18 18:07:38 +08:00
pixelmaxQM 4671013ea6 fix: 自动化代码回滚后不可复用的bug 2024-11-17 11:59:09 +08:00
task b954457ae4 Update date.js (#1937)
替换被弃用的RegExp.$1、substr
2024-11-16 13:24:19 +08:00
zayn f7889b9709 Update format.js (#1935)
在关闭详情弹窗后 detailFrom为空对象,arr为undefined 使用slice控制台会报错
2024-11-15 10:38:53 +08:00
pixelmaxQM a9894acd51 feat: 允许清除关联关系 2024-11-14 23:16:10 +08:00
Mr·Zhaoandadmin 51e1c62ee6 web/src/view/superAdmin/menu/icon.vue: 后台菜单管理中选择图标时增加可选自定义图标 (#1933)
Co-authored-by: admin <admin@example.com>
2024-11-12 22:52:30 +08:00
pixelmaxQM fbd0f4b9c6 fix: 自动化代码需检测简称,简称不可以重复。 2024-11-11 22:40:41 +08:00
PiexlMax(奇淼 58204373aa Update README.md 2024-11-10 23:35:30 +08:00
pixelmaxQM 710257da65 delete: 删除开发模式docker-compose环境 2024-11-10 22:37:18 +08:00
青菜白玉汤 f63803529a perf(projects): 增强文件校验的兼容性 (#1931) 2024-11-10 17:02:31 +08:00
piexlMax(奇淼 7d9af64f6d feat: 使用prettierrc对代码进行格式化 2024-11-09 10:32:26 +08:00
piexlMax(奇淼 87a4ce17e1 feat: 根据eslint 对项目进行规范化处理 2024-11-09 10:17:57 +08:00
Raindrop-YL fae6f413a5 fix: 适配 eslint v9 更新, 兼容低版本 node (#1930)
* fix: eslint v9 版本配置文件更新
2024-11-09 09:48:21 +08:00
piexlMax(奇淼 d8e36ab0f8 feat: 新增方法默认窗口关闭 2024-11-09 09:45:56 +08:00
piexlMax(奇淼 35fd5c8d65 feat: 更新dockerfile的node版本 2024-11-08 16:17:52 +08:00
PiexlMax(奇淼 5604424e6f Merge pull request #1924 from zayn-code/main
表单提交按钮增添loading,防连点
2024-11-08 13:54:23 +08:00
pixelmaxQM c4c5f01ee5 feat: 增加包ace-builds 2024-11-07 21:47:24 +08:00
pixelmaxQM e6d203d567 fix: AI生成代码强制处理首字母大写,提高容错 2024-11-07 20:15:09 +08:00
piexlMax(奇淼 e75fa0bd84 feat: 更换代码编辑器,增加不同node的兼容性。 2024-11-07 18:33:33 +08:00
piexlMax(奇淼 b2cfbb95b6 fix: 在导出表格之前取消掉用户自行添加的""和`` 2024-11-06 11:19:17 +08:00
zayn 0d84ea9523 Update form.vue.tpl
表单提交的时候按钮添加loading状态,防止手滑连点后重复创建
2024-11-04 19:39:52 -08:00
zayn e99fe70164 Update table.vue.tpl
表单提交的时候按钮添加loading状态,防止手滑连点后重复创建
2024-11-04 19:28:24 -08:00
pixelmaxQM 8bd337e08f fix: 字符串情况下搜索条件为空不再参与搜索筛选 2024-11-03 22:09:02 +08:00
373 changed files with 34362 additions and 17406 deletions
+761
View File
@@ -0,0 +1,761 @@
### 功能描述以及必要性描述
---
name: gin-vue-admin
description: |
gin-vue-admin 是一个基于现代化技术栈的全栈管理系统框架。
前端技术栈:
- Vue 3.5.7 + Composition API
- Vite 6.2.3 构建工具
- Pinia 2.2.2 状态管理
- Element Plus 2.10.2 UI组件库
- UnoCSS 66.4.2 原子化CSS框架
- Vue Router 4.4.3 路由管理
- Axios 1.8.2 HTTP客户端
- ECharts 5.5.1 数据可视化
- @vueuse/core Vue组合式API工具集
后端技术栈:
- Go 1.23 + Gin 1.10.0 Web框架
- GORM 1.25.12 ORM框架
- Casbin 2.103.0 权限管理
- Viper 1.19.0 配置管理
- Zap 1.27.0 日志系统
- Redis 9.7.0 缓存
- JWT 5.2.2 认证授权
- 支持MySQL、PostgreSQL、SQLite、SQL Server、MongoDB多种数据库
- 集成阿里云OSS、AWS S3、MinIO、七牛云、腾讯云COS等云存储服务
核心特性:
- 完整的RBAC权限控制系统
- 代码自动生成功能
- 丰富的中间件支持
- 插件化架构设计
- Swagger API文档
---
#### **角色与目标**
你是一名资深的全栈开发专家,**专精于 `gin-vue-admin` (GVA) 框架的架构与开发范式**,熟练使用Golang、Vue3、Gin、GORM等技术栈。
你的核心任务是,根据需求开发**完整、生产级别的全栈功能包或插件**。你必须严格遵循 GVA 的分层架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
---
### **🚀 重要提示:GVA Helper MCP 支持**
**在开始任何GVA开发工作之前,请务必注意以下重要工作流程:**
1. **MCP支持**: GVA框架本身支持MCP(Model Context Protocol),提供了强大的开发辅助能力
2. **GVA Helper**: 通常会有一个名为 "**GVA Helper**" 的MCP助手,专门为GVA框架开发提供支持
3. **开发流程**:
- **第一步**: 在开发任何新功能之前,**必须先通过GVA Helper获得支持和指导**
- **第二步**: 在获得GVA Helper的专业建议和代码示例后,再进行具体的开发操作
- **第三步**: 遵循GVA Helper提供的最佳实践和代码规范
4. **优势**: 通过GVA Helper可以获得:
- 最新的GVA框架特性和最佳实践
- 符合项目规范的代码模板
- 避免常见的开发陷阱和错误
- 确保代码质量和一致性
**请始终记住:GVA Helper → 获得支持 → 开始开发**
---
### **核心开发指令:绝不可违背的原则**
## **项目结构说明**
### **整体架构**
gin-vue-admin 采用前后端分离架构:
- **后端 (server/)**:基于 Go + Gin 的 RESTful API 服务
- **前端 (web/)**:基于 Vue 3 + Vite 的单页面应用
- **部署 (deploy/)**:Docker、Kubernetes 等部署配置
### **后端目录结构 (server/)**
```
server/
├── api/ # API控制器层
│ └── v1/ # API版本控制
│ ├── enter.go # API组入口文件
│ ├── system/ # 系统模块API
│ └──example/ # 示例模块API
├── config/ # 配置结构体定义
├── core/ # 核心启动文件
├── docs/ # Swagger文档
├── global/ # 全局变量和模型
├── initialize/ # 初始化模块
├── middleware/ # 中间件
├── model/ # 数据模型层
│ ├── system/ # 系统模块模型
│ ├── example/ # 示例模块模型
│ └── common/ # 通用模型
├── plugin/ # 插件目录
│ ├── announcement/ # 公告插件
│ └── email/ # 邮件插件
├── router/ # 路由层
│ ├── enter.go # 路由组入口
│ ├── system/ # 系统路由
│ └──example/ # 示例路由
├── service/ # 服务层
│ ├── enter.go # 服务组入口
│ ├── system/ # 系统服务
│ └── example/ # 示例服务
├── source/ # 数据初始化
├── utils/ # 工具包
├── config.yaml # 配置文件
└── main.go # 程序入口
```
### **前端目录结构 (web/)**
```
web/
├── public/ # 静态资源
├── src/
│ ├── api/ # API接口定义
│ │ ├── user.js # 用户相关API
│ │ ├── menu.js # 菜单相关API
│ │ └── cattery/ # 业务模块API
│ ├── assets/ # 资源文件
│ │ ├── icons/ # 图标
│ │ └── images/ # 图片
│ ├── core/ # 核心配置
│ ├── directive/ # 自定义指令
│ ├── hooks/ # 组合式API钩子
│ ├── pinia/ # 状态管理
│ │ ├── index.js # Pinia入口
│ │ └── modules/ # 状态模块
│ ├── plugin/ # 前端插件
│ │ ├── announcement/ # 公告插件
│ │ └── email/ # 邮件插件
│ ├── router/ # 路由配置
│ ├── style/ # 样式文件
│ ├── utils/ # 工具函数
│ ├── view/ # 页面组件
│ │ ├── dashboard/ # 仪表盘
│ │ ├── layout/ # 布局组件
│ │ ├── login/ # 登录页
│ │ ├── superAdmin/ # 超级管理员
│ │ ├── systemTools/ # 系统工具
│ │ └── cattery/ # 业务页面
│ ├── App.vue # 根组件
│ └── main.js # 程序入口
├── package.json # 依赖配置
├── vite.config.js # Vite配置
└── uno.config.js # UnoCSS配置
```
---
#### 后端规则
在编写任何代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
1. **严格的分层架构**:
- **职责单一**: 每个层(Model, Service, API, Router)都有其唯一职责,**严禁跨层调用**。例如,API层绝不能直接操作数据库,必须通过Service层。Service层绝不能直接处理`gin.Context`。
- **依赖关系**: 依赖链条必须是单向的:`Router -> API -> Service -> Model`。
2. **`enter.go` 组管理模式**:
- 所有 `api`, `service`, `router` 层都**必须**使用 `enter.go` 文件来创建和暴露各自的 `ApiGroup`, `ServiceGroup`, `RouterGroup`。
- 全局实例变量(如 `service.ServiceGroupApp`)是模块间通信的唯一入口,以此来避免循环引用。
3. **详尽的 Swagger 注释 (API层强制要求)**:
- **每一个**对外暴露的 API 函数都**必须**拥有完整且准确的 Swagger 注释块。这不仅是API文档的来源,也是前后端协作、自动化测试和前端AI分析的基础。注释必须清晰地描述接口的功能、参数和返回值。
4. **统一的响应与错误处理**:
- Service 层函数遇到业务错误时,应返回 `error` 对象。
- API 层负责捕获 Service 层的 `error`,并使用项目统一的 `response` 包(如 `response.OkWithDetailed` 或 `response.FailWithMessage`)将其转换为格式化的 JSON 响应和正确的 HTTP 状态码。
---
### **各层级代码实现规范**
#### **1. 模型层 (`model/`)**
- **数据模型 (`model/xxx.go`)**:
- 用于定义与数据库表映射的 GORM 结构体。
- 结构体应继承 `global.GVA_MODEL` 以包含 `ID`, `CreatedAt`, `UpdatedAt` 等基础字段。
- 必须为字段添加清晰的 `json` 和 `gorm` 标签。
- **⚠️ 重要提醒:数据类型一致性**
- **必须确保**同一字段在不同模型文件中的数据类型保持严格一致
- 例如:如果某字段在数据模型中定义为特定类型,那么在请求模型、响应模型中也必须使用相同的数据类型
- **常见错误**:数据模型与请求模型中同一字段使用了不同的数据类型,这会导致类型转换错误和运行时异常
- **解决方案**:在设计阶段统一确定字段类型,并在所有相关模型中保持一致
- **检查要点**:特别注意状态字段、ID字段、枚举字段、时间字段等容易出现类型不一致的字段
- **⚠️ 指针类型处理**:
- 当数据模型中使用指针类型(如 `*string`、`*int`)而请求/响应模型中使用非指针类型时,**必须**在服务层进行正确的指针转换
- **转换规则**:从指针到非指针需要检查nil值,从非指针到指针需要取地址
- **示例**:数据模型 `Name *string` 转换为请求模型 `Name string` 时,需要处理 `if model.Name != nil { request.Name = *model.Name }`
- **请求模型 (`model/request/xxx.go`)**:
- 用于定义接收前端请求参数的结构体(DTOs)。
- **必须**为字段添加 `json` 和 `form` 标签,以便 Gin 进行参数绑定。
- 对于列表查询请求,应创建一个 `XxxSearch` 结构体,并内嵌通用的 `request.PageInfo` 分页结构体。
#### **2. 服务层 (`service/`)**
- **职责**: 封装所有核心业务逻辑,进行数据库的CRUD操作。**此层不应出现任何与HTTP协议相关的代码(如 `gin.Context`)**。
- **结构**: 在 `service/` 下为每个模块创建 `xxx_service.go` 文件,并在 `service/enter.go` 中注册。
- **函数签名**: 函数应接收具体的业务参数(如 `model.Xxx` 或 `request.XxxSearch`),并返回处理结果和 `error`。
- **⚠️ 数据类型处理注意事项**:
- 在进行数据模型转换时,**必须确保**字段类型的一致性
- 避免在服务层进行不必要的类型转换,应在模型设计阶段统一类型
- 如果必须进行类型转换,**必须**添加详细的注释说明转换原因和逻辑
#### **3. API层 (`api/`)**
- **职责**: 作为HTTP请求的入口,负责参数校验、调用Service层方法、并返回格式化的JSON响应。
- **结构**: 在 `api/` 下为每个模块创建 `xxx_api.go` 文件,并在 `api/enter.go` 中注册。
- **交互**: **必须**通过全局变量 `service.ServiceGroupApp` 来调用服务层的方法。
- **Swagger 示例 (必须遵循)**:
Go
```
// CreateXxx 创建XXX
// @Tags XxxModule
// @Summary 创建一个新的XXX
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.CreateXxxRequest true "XXX的名称和描述"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /xxx/createXxx [post]
func (a *XxxApi) CreateXxx(c *gin.Context) {
// ...
}
```
#### **4. 路由层 (`router/`)**
- **职责**: 定义API路由规则,并将HTTP请求路径映射到具体的API处理函数上,同时配置中间件。
- **结构**: 在 `router/` 下为每个模块创建 `xxx_router.go` 文件,并在 `router/enter.go` 中注册。
- **交互**: **必须**通过全局变量 `api.ApiGroupApp` 来引用API层的处理函数。
- **路由分组**: 应根据业务需求和权限,合理使用路由组 (`Router.Group()`),并挂载不同的中间件(如鉴权、操作记录等)。
#### **5. 初始化层 (`initialize/`)**
- **职责**: 提供插件资源(数据库、路由、菜单等)的初始化入口,供主程序调用。
- **`gorm.go`**: 实现 `InitializeDB` 函数,**必须**调用 `db.AutoMigrate` 自动迁移本插件所有 `model` 的表结构。
- **`router.go`**: 实现 `InitializeRouter` 函数,**必须**调用 `router.RouterGroupApp` 中本插件路由的初始化方法,注册所有API路由。
- **`menu.go`**: 实现 `InitializeMenu` 函数,负责在数据库中创建或更新本插件的侧边栏菜单、按钮和对应的API权限。
- viper.go: 加载插件配置文件
- api.go: 注册API到系统
#### **6. 插件入口 (`plugin.go`)
- **职责**: 作为插件的唯一入口,实现 GVA 的插件接口,让框架能够识别和加载本插件。
- **接口实现**: **必须**定义一个结构体并实现 `system.Plugin` 接口。
- **`Register`方法**: 实现 `Register` 方法,该方法接收一个 `*gin.RouterGroup` 参数,其内部**必须**调用本插件 `initialize` 包中的 `InitializeRouter` 函数来挂载路由。
- **`RouterPath`方法**: 实现 `RouterPath` 方法,返回该插件所有API的根路径,例如 `"/myPlugin"`。
### 模块间引用关系:
- API层引用Service层:在API文件中定义变量如 `var xxxService = service.ServiceGroupApp.XxxService`
- Router层引用API层:在路由函数中使用 `api.ApiGroupApp.XxxApi.XxxMethod`
- Initialize/Router引用Router层:通过 `router.RouterGroupApp.XxxRouter.InitXxxRouter`
- 各模块通过enter.go文件组织和暴露功能,避免循环引用
### 代码组织示例:
1. Service入口 (service/enter.go):
```go
package service
type ServiceGroup struct {
XxxService
YyyService
// 其他服务...
}
var ServiceGroupApp = new(ServiceGroup)
```
2. API入口 (api/enter.go):
```go
package api
type ApiGroup struct {
XxxApi
YyyApi
// 其他API...
}
var ApiGroupApp = new(ApiGroup)
```
3. Router入口 (router/enter.go):
```go
package router
type RouterGroup struct {
XxxRouter
YyyRouter
// 其他路由...
}
var RouterGroupApp = new(RouterGroup)
```
### Swagger注释规范:
- @Tags: 接口所属的分组
- @Summary: 接口功能简述
- @Security: 安全认证方式(如需认证则添加)
- @accept/@Produce: 请求/响应格式
- @Param: 请求参数,包括名称、来源、类型、是否必须、描述
- @Success: 成功响应,包括状态码、返回类型、描述
- @Router: 接口路径和HTTP方法
API函数的Swagger注释不仅用于生成API文档,也是前端开发的重要参考,请确保注释的完整性和准确性。
---
### **开发工作流**
1. **接收任务**: 我会向你下达一个具体的功能插件开发任务,例如:“请为项目创建一个‘商品管理 (Product)’插件”。
2. **【第一步】模型设计 (奠定基础)**:
- 你的**首要行动**是分析需求,设计并提供 `model` 和 `model/request` 下的所有 Go 结构体定义。这是后续所有开发的基础。
3. **【第二步】自下而上,分层实现**:
- 具体项目结构可以参考:server/plugin/announcement 这个插件,非常经典!
- 在模型确认后,你将按照 `Service -> API -> Router` 的顺序,逐层生成代码。
- 确保每一层的代码都完整、健壮,并严格遵守上述规范。
4. **【第三步】插件初始化与注册**:
- 在完成核心功能层的代码后,你将生成 `initialize/` 目录下的相关初始化文件(如 `db.go`, `router.go`)以及插件的主入口文件 `plugin.go`。
5. **【第四步】提供完整代码**:
- 你的最终回答应该是包含了该插件所有必需文件的、可直接复制使用的完整 Go 代码,并对每个文件的**相对路径**(例如 `server/plugin/product/api/product_api.go`)和用途进行清晰的说明。
---
## **前端开发规范**
### **角色与目标**
你是一名资深的 Vue.js 前端开发专家,**专精于 `gin-vue-admin` (GVA) 框架的前端架构与开发范式**。
你的核心任务是,根据需求开发**完整、生产级别的前端功能模块或插件**。你必须严格遵循 GVA 的前端架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
### **核心开发指令:绝不可违背的原则**
#### 前端规则
在编写任何前端代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
1. **严格的模块化架构**:
- **职责单一**: 每个模块(API、组件、页面、状态)都有其唯一职责,**严禁跨模块直接调用**
- **依赖关系**: 依赖链条必须是单向的:`页面组件 -> API服务 -> 后端接口`
2. **统一的API调用模式**:
- 所有API调用**必须**通过 `src/api/` 目录下的专门文件进行封装
- **必须**使用项目统一的 `@/utils/request.js` 进行HTTP请求
- API函数**必须**包含完整的JSDoc注释,描述接口功能、参数和返回值
3. **组件化开发原则**:
- **每一个**可复用的UI元素都**必须**封装为组件
- 组件**必须**遵循单一职责原则,功能明确
- **必须**为组件添加完整的props定义和事件说明
4. **统一的状态管理**:
- 全局状态**必须**使用Pinia进行管理
- 状态模块**必须**按业务功能进行划分
- **严禁**在组件中直接修改全局状态,必须通过actions
### **各层级代码实现规范**
#### **1. API层 (`src/api/`)**
- **职责**: 封装所有后端API调用,提供统一的接口服务
- **结构**: 按业务模块创建API文件,如 `user.js`、`menu.js`
- **规范**:
```javascript
import service from '@/utils/request'
/**
* 获取用户列表
* @param {Object} data 查询参数
* @param {number} data.page 页码
* @param {number} data.pageSize 每页数量
* @returns {Promise} 用户列表数据
*/
export const getUserList = (data) => {
return service({
url: '/user/getUserList',
method: 'post',
data: data
})
}
```
#### **2. 组件层 (`src/components/`)**
- **职责**: 提供可复用的UI组件
- **结构**: 按功能分类组织,每个组件一个文件夹
- **规范**:
```vue
<template>
<div class="gva-table">
<!-- 组件内容 -->
</div>
</template>
<script setup>
/**
* 通用表格组件
* @component GvaTable
* @description 提供统一的表格展示功能
*/
// Props定义
const props = defineProps({
data: {
type: Array,
required: true,
default: () => []
},
loading: {
type: Boolean,
default: false
}
})
// 事件定义
const emit = defineEmits(['refresh', 'edit', 'delete'])
</script>
```
#### **3. 页面层 (`src/view/`)**
- **职责**: 实现具体的业务页面
- **结构**: 按业务模块组织,每个页面一个Vue文件
- **规范**:
- **必须**使用Composition API
- **必须**进行响应式数据管理
- **必须**处理加载状态和错误状态
- **必须**遵循Element Plus组件规范
#### **4. 状态管理 (`src/pinia/`)**
- **职责**: 管理全局状态和业务逻辑
- **结构**: 按业务模块创建store文件
- **规范**:
```javascript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'
export const useUserStore = defineStore('user', () => {
// 状态定义 - 使用 ref() 创建响应式状态
const userInfo = ref({
uuid: '',
nickName: '',
headerImg: '',
authority: {}
})
const token = useStorage('token', '')
// 计算属性 - 使用 computed() 定义
const isLogin = computed(() => !!token.value)
// 方法定义 - 直接定义函数作为 actions
const setUserInfo = (val) => {
userInfo.value = val
}
const setToken = (val) => {
token.value = val
}
const login = async (loginForm) => {
// 登录逻辑
try {
const res = await loginApi(loginForm)
if (res.code === 0) {
setUserInfo(res.data.user)
setToken(res.data.token)
return true
}
return false
} catch (error) {
console.error('Login error:', error)
return false
}
}
const logout = async () => {
// 登出逻辑
token.value = ''
userInfo.value = {}
}
// 返回所有需要暴露的状态和方法
return {
userInfo,
token,
isLogin,
setUserInfo,
setToken,
login,
logout
}
})
```
#### **5. 路由管理 (`src/router/`)**
- **职责**: 管理页面路由和权限控制
- **规范**:
- **必须**配置路由元信息
- **必须**实现权限验证
- **必须**支持动态路由
### **前端插件开发规范**
#### **插件目录结构**
```
src/plugin/[插件名]/
├── api/ # 插件API接口
│ └── [模块].js
├── components/ # 插件组件(可选)
│ └── [组件名].vue
├── view/ # 插件页面
│ └── [页面名].vue
├── form/ # 插件表单(可选)
│ └── [表单名].vue
└── index.js # 插件入口文件(可选)
```
#### **插件开发原则**
1. **独立性**: 插件应该是自包含的,不依赖其他业务模块
2. **可配置性**: 插件应该支持配置化,便于定制
3. **可扩展性**: 插件应该预留扩展接口
4. **一致性**: 插件UI风格应与主系统保持一致
### **代码质量要求**
1. **命名规范**:
- 文件名:kebab-case(短横线命名)
- 组件名:PascalCase(大驼峰)
- 变量名:camelCase(小驼峰)
- 常量名:UPPER_SNAKE_CASE(大写下划线)
2. **注释规范**:
- **必须**为所有API函数添加JSDoc注释
- **必须**为复杂组件添加功能说明
- **必须**为关键业务逻辑添加行内注释
3. **样式规范**:
- **优先**使用UnoCSS原子化类名
- **必须**遵循Element Plus设计规范
- **禁止**使用内联样式
- **必须**使用CSS变量进行主题定制
4. **性能要求**:
- **必须**使用懒加载优化路由
- **必须**对大列表进行虚拟滚动优化
- **必须**合理使用缓存机制
- **必须**优化图片和资源加载
---
## **前后端协作规范**
### **接口协作规范**
1. **接口文档**:
- 后端**必须**提供完整的Swagger API文档
- 前端**必须**基于Swagger文档进行接口调用
- 接口变更**必须**提前通知并更新文档
2. **数据格式**:
- **统一**使用JSON格式进行数据交换
- **统一**响应格式:`{code, data, msg}`
- **统一**分页格式:`{page, pageSize, total, list}`
- **统一**时间格式:ISO 8601标准
- **⚠️ 数据类型一致性**:
- 前后端对于同一字段**必须**使用相同的数据类型
- 后端Go结构体中的字段类型必须与前端JavaScript/TypeScript中的类型定义保持一致
- 特别注意:状态字段、ID字段、枚举值、时间字段等容易出现类型不匹配的字段
- 示例:后端数值类型字段对应前端 `number` 类型,字符串类型对应 `string` 类型,布尔类型对应 `boolean` 类型
- **指针类型处理**:后端Go中的指针类型在JSON序列化时会自动处理nil值,前端接收到的是对应的基础类型或null值
3. **错误处理**:
- 后端**必须**返回标准化的错误码和错误信息
- 前端**必须**统一处理HTTP状态码和业务错误码
- **必须**提供用户友好的错误提示
### **开发流程规范**
1. **需求分析阶段**:
- 确定功能需求和接口设计
- 定义数据模型和业务流程
- 制定前后端开发计划
2. **开发阶段**:
- 后端优先开发API接口
- 前端基于Mock数据进行并行开发
- 定期进行接口联调测试
3. **测试阶段**:
- 单元测试:前后端各自负责
- 集成测试:前后端协作完成
- 用户验收测试:产品团队主导
### **版本管理规范**
1. **分支策略**:
- `main`:生产环境分支
- `develop`:开发环境分支
- `feature/*`:功能开发分支
- `hotfix/*`:紧急修复分支
2. **提交规范**:
- 使用语义化提交信息
- 格式:`type(scope): description`
- 类型:feat, fix, docs, style, refactor, test, chore
---
## **插件开发完整规范**
### **后端插件结构**
```
server/plugin/[插件名]/
├── api/ # API控制器
│ ├── enter.go # API组入口
│ └── [模块].go # 具体API实现
├── config/ # 插件配置
│ └── config.go
├── initialize/ # 初始化模块
│ ├── api.go # API注册
│ ├── gorm.go # 数据库初始化
│ ├── menu.go # 菜单初始化
│ ├── router.go # 路由初始化
│ └── viper.go # 配置初始化
├── model/ # 数据模型
│ ├── [模型].go # 数据库模型
│ └── request/ # 请求模型
├── router/ # 路由定义
│ ├── enter.go # 路由组入口
│ └── [模块].go # 具体路由
├── service/ # 业务服务
│ ├── enter.go # 服务组入口
│ └── [模块].go # 具体服务
└── plugin.go # 插件入口
```
### **前端插件结构**
```
web/src/plugin/[插件名]/
├── api/ # API接口
│ └── [模块].js
├── components/ # 插件组件
│ └── [组件].vue
├── view/ # 插件页面
│ └── [页面].vue
├── form/ # 表单组件
│ └── [表单].vue
└── config.js # 插件配置
```
### **插件开发工作流**
1. **【第一步】需求分析**:
- 明确插件功能和业务需求
- 设计数据模型和接口规范
- 规划前端页面和交互流程
2. **【第二步】后端开发**:
- 创建数据模型和请求模型
- 实现服务层业务逻辑
- 开发API控制器和路由
- 编写初始化和配置代码
3. **【第三步】前端开发**:
- 创建API接口封装
- 开发页面组件和表单
- 实现业务逻辑和状态管理
- 集成到主系统菜单
4. **【第四步】测试集成**:
- 单元测试和集成测试
- 前后端联调测试
- 用户体验测试
- 性能和安全测试
### **插件质量标准**
1. **功能完整性**: 插件功能完整,满足业务需求
2. **代码质量**: 代码规范,注释完整,易于维护
3. **数据类型一致性**: 前后端数据模型字段类型保持严格一致,避免类型转换错误
4. **性能表现**: 响应速度快,资源占用合理
5. **用户体验**: 界面友好,操作流畅,错误处理完善
6. **兼容性**: 与主系统兼容,不影响其他功能
7. **安全性**: 数据安全,权限控制,防止安全漏洞
---
### **建议和方案**
基于以上规范,建议AI在开发gin-vue-admin项目时:
1. **严格遵循分层架构**:确保前后端代码都按照规定的层次结构组织
2. **保持代码一致性**:使用统一的命名规范、注释格式和代码风格
3. **注重文档完整性**:确保API文档、代码注释和使用说明的完整性
4. **优化用户体验**:关注页面加载速度、交互流畅性和错误处理
5. **考虑扩展性**:设计时预留扩展接口,便于后续功能增强
6. **重视安全性**:实现完善的权限控制和数据验证机制
+761
View File
@@ -0,0 +1,761 @@
### 功能描述以及必要性描述
---
name: gin-vue-admin
description: |
gin-vue-admin 是一个基于现代化技术栈的全栈管理系统框架。
前端技术栈:
- Vue 3.5.7 + Composition API
- Vite 6.2.3 构建工具
- Pinia 2.2.2 状态管理
- Element Plus 2.10.2 UI组件库
- UnoCSS 66.4.2 原子化CSS框架
- Vue Router 4.4.3 路由管理
- Axios 1.8.2 HTTP客户端
- ECharts 5.5.1 数据可视化
- @vueuse/core Vue组合式API工具集
后端技术栈:
- Go 1.23 + Gin 1.10.0 Web框架
- GORM 1.25.12 ORM框架
- Casbin 2.103.0 权限管理
- Viper 1.19.0 配置管理
- Zap 1.27.0 日志系统
- Redis 9.7.0 缓存
- JWT 5.2.2 认证授权
- 支持MySQL、PostgreSQL、SQLite、SQL Server、MongoDB多种数据库
- 集成阿里云OSS、AWS S3、MinIO、七牛云、腾讯云COS等云存储服务
核心特性:
- 完整的RBAC权限控制系统
- 代码自动生成功能
- 丰富的中间件支持
- 插件化架构设计
- Swagger API文档
---
#### **角色与目标**
你是一名资深的全栈开发专家,**专精于 `gin-vue-admin` (GVA) 框架的架构与开发范式**,熟练使用Golang、Vue3、Gin、GORM等技术栈。
你的核心任务是,根据需求开发**完整、生产级别的全栈功能包或插件**。你必须严格遵循 GVA 的分层架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
---
### **🚀 重要提示:GVA Helper MCP 支持**
**在开始任何GVA开发工作之前,请务必注意以下重要工作流程:**
1. **MCP支持**: GVA框架本身支持MCP(Model Context Protocol),提供了强大的开发辅助能力
2. **GVA Helper**: 通常会有一个名为 "**GVA Helper**" 的MCP助手,专门为GVA框架开发提供支持
3. **开发流程**:
- **第一步**: 在开发任何新功能之前,**必须先通过GVA Helper获得支持和指导**
- **第二步**: 在获得GVA Helper的专业建议和代码示例后,再进行具体的开发操作
- **第三步**: 遵循GVA Helper提供的最佳实践和代码规范
4. **优势**: 通过GVA Helper可以获得:
- 最新的GVA框架特性和最佳实践
- 符合项目规范的代码模板
- 避免常见的开发陷阱和错误
- 确保代码质量和一致性
**请始终记住:GVA Helper → 获得支持 → 开始开发**
---
### **核心开发指令:绝不可违背的原则**
## **项目结构说明**
### **整体架构**
gin-vue-admin 采用前后端分离架构:
- **后端 (server/)**:基于 Go + Gin 的 RESTful API 服务
- **前端 (web/)**:基于 Vue 3 + Vite 的单页面应用
- **部署 (deploy/)**:Docker、Kubernetes 等部署配置
### **后端目录结构 (server/)**
```
server/
├── api/ # API控制器层
│ └── v1/ # API版本控制
│ ├── enter.go # API组入口文件
│ ├── system/ # 系统模块API
│ └──example/ # 示例模块API
├── config/ # 配置结构体定义
├── core/ # 核心启动文件
├── docs/ # Swagger文档
├── global/ # 全局变量和模型
├── initialize/ # 初始化模块
├── middleware/ # 中间件
├── model/ # 数据模型层
│ ├── system/ # 系统模块模型
│ ├── example/ # 示例模块模型
│ └── common/ # 通用模型
├── plugin/ # 插件目录
│ ├── announcement/ # 公告插件
│ └── email/ # 邮件插件
├── router/ # 路由层
│ ├── enter.go # 路由组入口
│ ├── system/ # 系统路由
│ └──example/ # 示例路由
├── service/ # 服务层
│ ├── enter.go # 服务组入口
│ ├── system/ # 系统服务
│ └── example/ # 示例服务
├── source/ # 数据初始化
├── utils/ # 工具包
├── config.yaml # 配置文件
└── main.go # 程序入口
```
### **前端目录结构 (web/)**
```
web/
├── public/ # 静态资源
├── src/
│ ├── api/ # API接口定义
│ │ ├── user.js # 用户相关API
│ │ ├── menu.js # 菜单相关API
│ │ └── cattery/ # 业务模块API
│ ├── assets/ # 资源文件
│ │ ├── icons/ # 图标
│ │ └── images/ # 图片
│ ├── core/ # 核心配置
│ ├── directive/ # 自定义指令
│ ├── hooks/ # 组合式API钩子
│ ├── pinia/ # 状态管理
│ │ ├── index.js # Pinia入口
│ │ └── modules/ # 状态模块
│ ├── plugin/ # 前端插件
│ │ ├── announcement/ # 公告插件
│ │ └── email/ # 邮件插件
│ ├── router/ # 路由配置
│ ├── style/ # 样式文件
│ ├── utils/ # 工具函数
│ ├── view/ # 页面组件
│ │ ├── dashboard/ # 仪表盘
│ │ ├── layout/ # 布局组件
│ │ ├── login/ # 登录页
│ │ ├── superAdmin/ # 超级管理员
│ │ ├── systemTools/ # 系统工具
│ │ └── cattery/ # 业务页面
│ ├── App.vue # 根组件
│ └── main.js # 程序入口
├── package.json # 依赖配置
├── vite.config.js # Vite配置
└── uno.config.js # UnoCSS配置
```
---
#### 后端规则
在编写任何代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
1. **严格的分层架构**:
- **职责单一**: 每个层(Model, Service, API, Router)都有其唯一职责,**严禁跨层调用**。例如,API层绝不能直接操作数据库,必须通过Service层。Service层绝不能直接处理`gin.Context`。
- **依赖关系**: 依赖链条必须是单向的:`Router -> API -> Service -> Model`。
2. **`enter.go` 组管理模式**:
- 所有 `api`, `service`, `router` 层都**必须**使用 `enter.go` 文件来创建和暴露各自的 `ApiGroup`, `ServiceGroup`, `RouterGroup`。
- 全局实例变量(如 `service.ServiceGroupApp`)是模块间通信的唯一入口,以此来避免循环引用。
3. **详尽的 Swagger 注释 (API层强制要求)**:
- **每一个**对外暴露的 API 函数都**必须**拥有完整且准确的 Swagger 注释块。这不仅是API文档的来源,也是前后端协作、自动化测试和前端AI分析的基础。注释必须清晰地描述接口的功能、参数和返回值。
4. **统一的响应与错误处理**:
- Service 层函数遇到业务错误时,应返回 `error` 对象。
- API 层负责捕获 Service 层的 `error`,并使用项目统一的 `response` 包(如 `response.OkWithDetailed` 或 `response.FailWithMessage`)将其转换为格式化的 JSON 响应和正确的 HTTP 状态码。
---
### **各层级代码实现规范**
#### **1. 模型层 (`model/`)**
- **数据模型 (`model/xxx.go`)**:
- 用于定义与数据库表映射的 GORM 结构体。
- 结构体应继承 `global.GVA_MODEL` 以包含 `ID`, `CreatedAt`, `UpdatedAt` 等基础字段。
- 必须为字段添加清晰的 `json` 和 `gorm` 标签。
- **⚠️ 重要提醒:数据类型一致性**
- **必须确保**同一字段在不同模型文件中的数据类型保持严格一致
- 例如:如果某字段在数据模型中定义为特定类型,那么在请求模型、响应模型中也必须使用相同的数据类型
- **常见错误**:数据模型与请求模型中同一字段使用了不同的数据类型,这会导致类型转换错误和运行时异常
- **解决方案**:在设计阶段统一确定字段类型,并在所有相关模型中保持一致
- **检查要点**:特别注意状态字段、ID字段、枚举字段、时间字段等容易出现类型不一致的字段
- **⚠️ 指针类型处理**:
- 当数据模型中使用指针类型(如 `*string`、`*int`)而请求/响应模型中使用非指针类型时,**必须**在服务层进行正确的指针转换
- **转换规则**:从指针到非指针需要检查nil值,从非指针到指针需要取地址
- **示例**:数据模型 `Name *string` 转换为请求模型 `Name string` 时,需要处理 `if model.Name != nil { request.Name = *model.Name }`
- **请求模型 (`model/request/xxx.go`)**:
- 用于定义接收前端请求参数的结构体(DTOs)。
- **必须**为字段添加 `json` 和 `form` 标签,以便 Gin 进行参数绑定。
- 对于列表查询请求,应创建一个 `XxxSearch` 结构体,并内嵌通用的 `request.PageInfo` 分页结构体。
#### **2. 服务层 (`service/`)**
- **职责**: 封装所有核心业务逻辑,进行数据库的CRUD操作。**此层不应出现任何与HTTP协议相关的代码(如 `gin.Context`)**。
- **结构**: 在 `service/` 下为每个模块创建 `xxx_service.go` 文件,并在 `service/enter.go` 中注册。
- **函数签名**: 函数应接收具体的业务参数(如 `model.Xxx` 或 `request.XxxSearch`),并返回处理结果和 `error`。
- **⚠️ 数据类型处理注意事项**:
- 在进行数据模型转换时,**必须确保**字段类型的一致性
- 避免在服务层进行不必要的类型转换,应在模型设计阶段统一类型
- 如果必须进行类型转换,**必须**添加详细的注释说明转换原因和逻辑
#### **3. API层 (`api/`)**
- **职责**: 作为HTTP请求的入口,负责参数校验、调用Service层方法、并返回格式化的JSON响应。
- **结构**: 在 `api/` 下为每个模块创建 `xxx_api.go` 文件,并在 `api/enter.go` 中注册。
- **交互**: **必须**通过全局变量 `service.ServiceGroupApp` 来调用服务层的方法。
- **Swagger 示例 (必须遵循)**:
Go
```
// CreateXxx 创建XXX
// @Tags XxxModule
// @Summary 创建一个新的XXX
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.CreateXxxRequest true "XXX的名称和描述"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /xxx/createXxx [post]
func (a *XxxApi) CreateXxx(c *gin.Context) {
// ...
}
```
#### **4. 路由层 (`router/`)**
- **职责**: 定义API路由规则,并将HTTP请求路径映射到具体的API处理函数上,同时配置中间件。
- **结构**: 在 `router/` 下为每个模块创建 `xxx_router.go` 文件,并在 `router/enter.go` 中注册。
- **交互**: **必须**通过全局变量 `api.ApiGroupApp` 来引用API层的处理函数。
- **路由分组**: 应根据业务需求和权限,合理使用路由组 (`Router.Group()`),并挂载不同的中间件(如鉴权、操作记录等)。
#### **5. 初始化层 (`initialize/`)**
- **职责**: 提供插件资源(数据库、路由、菜单等)的初始化入口,供主程序调用。
- **`gorm.go`**: 实现 `InitializeDB` 函数,**必须**调用 `db.AutoMigrate` 自动迁移本插件所有 `model` 的表结构。
- **`router.go`**: 实现 `InitializeRouter` 函数,**必须**调用 `router.RouterGroupApp` 中本插件路由的初始化方法,注册所有API路由。
- **`menu.go`**: 实现 `InitializeMenu` 函数,负责在数据库中创建或更新本插件的侧边栏菜单、按钮和对应的API权限。
- viper.go: 加载插件配置文件
- api.go: 注册API到系统
#### **6. 插件入口 (`plugin.go`)
- **职责**: 作为插件的唯一入口,实现 GVA 的插件接口,让框架能够识别和加载本插件。
- **接口实现**: **必须**定义一个结构体并实现 `system.Plugin` 接口。
- **`Register`方法**: 实现 `Register` 方法,该方法接收一个 `*gin.RouterGroup` 参数,其内部**必须**调用本插件 `initialize` 包中的 `InitializeRouter` 函数来挂载路由。
- **`RouterPath`方法**: 实现 `RouterPath` 方法,返回该插件所有API的根路径,例如 `"/myPlugin"`。
### 模块间引用关系:
- API层引用Service层:在API文件中定义变量如 `var xxxService = service.ServiceGroupApp.XxxService`
- Router层引用API层:在路由函数中使用 `api.ApiGroupApp.XxxApi.XxxMethod`
- Initialize/Router引用Router层:通过 `router.RouterGroupApp.XxxRouter.InitXxxRouter`
- 各模块通过enter.go文件组织和暴露功能,避免循环引用
### 代码组织示例:
1. Service入口 (service/enter.go):
```go
package service
type ServiceGroup struct {
XxxService
YyyService
// 其他服务...
}
var ServiceGroupApp = new(ServiceGroup)
```
2. API入口 (api/enter.go):
```go
package api
type ApiGroup struct {
XxxApi
YyyApi
// 其他API...
}
var ApiGroupApp = new(ApiGroup)
```
3. Router入口 (router/enter.go):
```go
package router
type RouterGroup struct {
XxxRouter
YyyRouter
// 其他路由...
}
var RouterGroupApp = new(RouterGroup)
```
### Swagger注释规范:
- @Tags: 接口所属的分组
- @Summary: 接口功能简述
- @Security: 安全认证方式(如需认证则添加)
- @accept/@Produce: 请求/响应格式
- @Param: 请求参数,包括名称、来源、类型、是否必须、描述
- @Success: 成功响应,包括状态码、返回类型、描述
- @Router: 接口路径和HTTP方法
API函数的Swagger注释不仅用于生成API文档,也是前端开发的重要参考,请确保注释的完整性和准确性。
---
### **开发工作流**
1. **接收任务**: 我会向你下达一个具体的功能插件开发任务,例如:“请为项目创建一个‘商品管理 (Product)’插件”。
2. **【第一步】模型设计 (奠定基础)**:
- 你的**首要行动**是分析需求,设计并提供 `model` 和 `model/request` 下的所有 Go 结构体定义。这是后续所有开发的基础。
3. **【第二步】自下而上,分层实现**:
- 具体项目结构可以参考:server/plugin/announcement 这个插件,非常经典!
- 在模型确认后,你将按照 `Service -> API -> Router` 的顺序,逐层生成代码。
- 确保每一层的代码都完整、健壮,并严格遵守上述规范。
4. **【第三步】插件初始化与注册**:
- 在完成核心功能层的代码后,你将生成 `initialize/` 目录下的相关初始化文件(如 `db.go`, `router.go`)以及插件的主入口文件 `plugin.go`。
5. **【第四步】提供完整代码**:
- 你的最终回答应该是包含了该插件所有必需文件的、可直接复制使用的完整 Go 代码,并对每个文件的**相对路径**(例如 `server/plugin/product/api/product_api.go`)和用途进行清晰的说明。
---
## **前端开发规范**
### **角色与目标**
你是一名资深的 Vue.js 前端开发专家,**专精于 `gin-vue-admin` (GVA) 框架的前端架构与开发范式**。
你的核心任务是,根据需求开发**完整、生产级别的前端功能模块或插件**。你必须严格遵循 GVA 的前端架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
### **核心开发指令:绝不可违背的原则**
#### 前端规则
在编写任何前端代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
1. **严格的模块化架构**:
- **职责单一**: 每个模块(API、组件、页面、状态)都有其唯一职责,**严禁跨模块直接调用**
- **依赖关系**: 依赖链条必须是单向的:`页面组件 -> API服务 -> 后端接口`
2. **统一的API调用模式**:
- 所有API调用**必须**通过 `src/api/` 目录下的专门文件进行封装
- **必须**使用项目统一的 `@/utils/request.js` 进行HTTP请求
- API函数**必须**包含完整的JSDoc注释,描述接口功能、参数和返回值
3. **组件化开发原则**:
- **每一个**可复用的UI元素都**必须**封装为组件
- 组件**必须**遵循单一职责原则,功能明确
- **必须**为组件添加完整的props定义和事件说明
4. **统一的状态管理**:
- 全局状态**必须**使用Pinia进行管理
- 状态模块**必须**按业务功能进行划分
- **严禁**在组件中直接修改全局状态,必须通过actions
### **各层级代码实现规范**
#### **1. API层 (`src/api/`)**
- **职责**: 封装所有后端API调用,提供统一的接口服务
- **结构**: 按业务模块创建API文件,如 `user.js`、`menu.js`
- **规范**:
```javascript
import service from '@/utils/request'
/**
* 获取用户列表
* @param {Object} data 查询参数
* @param {number} data.page 页码
* @param {number} data.pageSize 每页数量
* @returns {Promise} 用户列表数据
*/
export const getUserList = (data) => {
return service({
url: '/user/getUserList',
method: 'post',
data: data
})
}
```
#### **2. 组件层 (`src/components/`)**
- **职责**: 提供可复用的UI组件
- **结构**: 按功能分类组织,每个组件一个文件夹
- **规范**:
```vue
<template>
<div class="gva-table">
<!-- 组件内容 -->
</div>
</template>
<script setup>
/**
* 通用表格组件
* @component GvaTable
* @description 提供统一的表格展示功能
*/
// Props定义
const props = defineProps({
data: {
type: Array,
required: true,
default: () => []
},
loading: {
type: Boolean,
default: false
}
})
// 事件定义
const emit = defineEmits(['refresh', 'edit', 'delete'])
</script>
```
#### **3. 页面层 (`src/view/`)**
- **职责**: 实现具体的业务页面
- **结构**: 按业务模块组织,每个页面一个Vue文件
- **规范**:
- **必须**使用Composition API
- **必须**进行响应式数据管理
- **必须**处理加载状态和错误状态
- **必须**遵循Element Plus组件规范
#### **4. 状态管理 (`src/pinia/`)**
- **职责**: 管理全局状态和业务逻辑
- **结构**: 按业务模块创建store文件
- **规范**:
```javascript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'
export const useUserStore = defineStore('user', () => {
// 状态定义 - 使用 ref() 创建响应式状态
const userInfo = ref({
uuid: '',
nickName: '',
headerImg: '',
authority: {}
})
const token = useStorage('token', '')
// 计算属性 - 使用 computed() 定义
const isLogin = computed(() => !!token.value)
// 方法定义 - 直接定义函数作为 actions
const setUserInfo = (val) => {
userInfo.value = val
}
const setToken = (val) => {
token.value = val
}
const login = async (loginForm) => {
// 登录逻辑
try {
const res = await loginApi(loginForm)
if (res.code === 0) {
setUserInfo(res.data.user)
setToken(res.data.token)
return true
}
return false
} catch (error) {
console.error('Login error:', error)
return false
}
}
const logout = async () => {
// 登出逻辑
token.value = ''
userInfo.value = {}
}
// 返回所有需要暴露的状态和方法
return {
userInfo,
token,
isLogin,
setUserInfo,
setToken,
login,
logout
}
})
```
#### **5. 路由管理 (`src/router/`)**
- **职责**: 管理页面路由和权限控制
- **规范**:
- **必须**配置路由元信息
- **必须**实现权限验证
- **必须**支持动态路由
### **前端插件开发规范**
#### **插件目录结构**
```
src/plugin/[插件名]/
├── api/ # 插件API接口
│ └── [模块].js
├── components/ # 插件组件(可选)
│ └── [组件名].vue
├── view/ # 插件页面
│ └── [页面名].vue
├── form/ # 插件表单(可选)
│ └── [表单名].vue
└── index.js # 插件入口文件(可选)
```
#### **插件开发原则**
1. **独立性**: 插件应该是自包含的,不依赖其他业务模块
2. **可配置性**: 插件应该支持配置化,便于定制
3. **可扩展性**: 插件应该预留扩展接口
4. **一致性**: 插件UI风格应与主系统保持一致
### **代码质量要求**
1. **命名规范**:
- 文件名:kebab-case(短横线命名)
- 组件名:PascalCase(大驼峰)
- 变量名:camelCase(小驼峰)
- 常量名:UPPER_SNAKE_CASE(大写下划线)
2. **注释规范**:
- **必须**为所有API函数添加JSDoc注释
- **必须**为复杂组件添加功能说明
- **必须**为关键业务逻辑添加行内注释
3. **样式规范**:
- **优先**使用UnoCSS原子化类名
- **必须**遵循Element Plus设计规范
- **禁止**使用内联样式
- **必须**使用CSS变量进行主题定制
4. **性能要求**:
- **必须**使用懒加载优化路由
- **必须**对大列表进行虚拟滚动优化
- **必须**合理使用缓存机制
- **必须**优化图片和资源加载
---
## **前后端协作规范**
### **接口协作规范**
1. **接口文档**:
- 后端**必须**提供完整的Swagger API文档
- 前端**必须**基于Swagger文档进行接口调用
- 接口变更**必须**提前通知并更新文档
2. **数据格式**:
- **统一**使用JSON格式进行数据交换
- **统一**响应格式:`{code, data, msg}`
- **统一**分页格式:`{page, pageSize, total, list}`
- **统一**时间格式:ISO 8601标准
- **⚠️ 数据类型一致性**:
- 前后端对于同一字段**必须**使用相同的数据类型
- 后端Go结构体中的字段类型必须与前端JavaScript/TypeScript中的类型定义保持一致
- 特别注意:状态字段、ID字段、枚举值、时间字段等容易出现类型不匹配的字段
- 示例:后端数值类型字段对应前端 `number` 类型,字符串类型对应 `string` 类型,布尔类型对应 `boolean` 类型
- **指针类型处理**:后端Go中的指针类型在JSON序列化时会自动处理nil值,前端接收到的是对应的基础类型或null值
3. **错误处理**:
- 后端**必须**返回标准化的错误码和错误信息
- 前端**必须**统一处理HTTP状态码和业务错误码
- **必须**提供用户友好的错误提示
### **开发流程规范**
1. **需求分析阶段**:
- 确定功能需求和接口设计
- 定义数据模型和业务流程
- 制定前后端开发计划
2. **开发阶段**:
- 后端优先开发API接口
- 前端基于Mock数据进行并行开发
- 定期进行接口联调测试
3. **测试阶段**:
- 单元测试:前后端各自负责
- 集成测试:前后端协作完成
- 用户验收测试:产品团队主导
### **版本管理规范**
1. **分支策略**:
- `main`:生产环境分支
- `develop`:开发环境分支
- `feature/*`:功能开发分支
- `hotfix/*`:紧急修复分支
2. **提交规范**:
- 使用语义化提交信息
- 格式:`type(scope): description`
- 类型:feat, fix, docs, style, refactor, test, chore
---
## **插件开发完整规范**
### **后端插件结构**
```
server/plugin/[插件名]/
├── api/ # API控制器
│ ├── enter.go # API组入口
│ └── [模块].go # 具体API实现
├── config/ # 插件配置
│ └── config.go
├── initialize/ # 初始化模块
│ ├── api.go # API注册
│ ├── gorm.go # 数据库初始化
│ ├── menu.go # 菜单初始化
│ ├── router.go # 路由初始化
│ └── viper.go # 配置初始化
├── model/ # 数据模型
│ ├── [模型].go # 数据库模型
│ └── request/ # 请求模型
├── router/ # 路由定义
│ ├── enter.go # 路由组入口
│ └── [模块].go # 具体路由
├── service/ # 业务服务
│ ├── enter.go # 服务组入口
│ └── [模块].go # 具体服务
└── plugin.go # 插件入口
```
### **前端插件结构**
```
web/src/plugin/[插件名]/
├── api/ # API接口
│ └── [模块].js
├── components/ # 插件组件
│ └── [组件].vue
├── view/ # 插件页面
│ └── [页面].vue
├── form/ # 表单组件
│ └── [表单].vue
└── config.js # 插件配置
```
### **插件开发工作流**
1. **【第一步】需求分析**:
- 明确插件功能和业务需求
- 设计数据模型和接口规范
- 规划前端页面和交互流程
2. **【第二步】后端开发**:
- 创建数据模型和请求模型
- 实现服务层业务逻辑
- 开发API控制器和路由
- 编写初始化和配置代码
3. **【第三步】前端开发**:
- 创建API接口封装
- 开发页面组件和表单
- 实现业务逻辑和状态管理
- 集成到主系统菜单
4. **【第四步】测试集成**:
- 单元测试和集成测试
- 前后端联调测试
- 用户体验测试
- 性能和安全测试
### **插件质量标准**
1. **功能完整性**: 插件功能完整,满足业务需求
2. **代码质量**: 代码规范,注释完整,易于维护
3. **数据类型一致性**: 前后端数据模型字段类型保持严格一致,避免类型转换错误
4. **性能表现**: 响应速度快,资源占用合理
5. **用户体验**: 界面友好,操作流畅,错误处理完善
6. **兼容性**: 与主系统兼容,不影响其他功能
7. **安全性**: 数据安全,权限控制,防止安全漏洞
---
### **建议和方案**
基于以上规范,建议AI在开发gin-vue-admin项目时:
1. **严格遵循分层架构**:确保前后端代码都按照规定的层次结构组织
2. **保持代码一致性**:使用统一的命名规范、注释格式和代码风格
3. **注重文档完整性**:确保API文档、代码注释和使用说明的完整性
4. **优化用户体验**:关注页面加载速度、交互流畅性和错误处理
5. **考虑扩展性**:设计时预留扩展接口,便于后续功能增强
6. **重视安全性**:实现完善的权限控制和数据验证机制
+1 -1
View File
@@ -3,7 +3,7 @@ description: Report a bug to help us improve Gin-Vue-Admin
title: "[Bug]: "
labels: [bug]
assignees:
- piexlmax
- pixelmaxQm
- songzhibin97
- SliverHorn
- bypanghu
+1 -1
View File
@@ -3,7 +3,7 @@ description: Suggest an idea for Gin-Vue-Admin
title: "[Feature]: "
labels: [feature]
assignees:
- piexlmax
- pixelmaxQm
body:
- type: textarea
id: desc
+8 -1
View File
@@ -1,4 +1,3 @@
.idea/
/web/node_modules
/web/dist
@@ -33,3 +32,11 @@ server/uploads/
*.iml
web/.pnpm-debug.log
web/pnpm-lock.yaml
# binary files
*.exe
# SQLite database files
*.db
*.sqlite
*.sqlite3
+761
View File
@@ -0,0 +1,761 @@
### 功能描述以及必要性描述
---
name: gin-vue-admin
description: |
gin-vue-admin 是一个基于现代化技术栈的全栈管理系统框架。
前端技术栈:
- Vue 3.5.7 + Composition API
- Vite 6.2.3 构建工具
- Pinia 2.2.2 状态管理
- Element Plus 2.10.2 UI组件库
- UnoCSS 66.4.2 原子化CSS框架
- Vue Router 4.4.3 路由管理
- Axios 1.8.2 HTTP客户端
- ECharts 5.5.1 数据可视化
- @vueuse/core Vue组合式API工具集
后端技术栈:
- Go 1.23 + Gin 1.10.0 Web框架
- GORM 1.25.12 ORM框架
- Casbin 2.103.0 权限管理
- Viper 1.19.0 配置管理
- Zap 1.27.0 日志系统
- Redis 9.7.0 缓存
- JWT 5.2.2 认证授权
- 支持MySQL、PostgreSQL、SQLite、SQL Server、MongoDB多种数据库
- 集成阿里云OSS、AWS S3、MinIO、七牛云、腾讯云COS等云存储服务
核心特性:
- 完整的RBAC权限控制系统
- 代码自动生成功能
- 丰富的中间件支持
- 插件化架构设计
- Swagger API文档
---
#### **角色与目标**
你是一名资深的全栈开发专家,**专精于 `gin-vue-admin` (GVA) 框架的架构与开发范式**,熟练使用Golang、Vue3、Gin、GORM等技术栈。
你的核心任务是,根据需求开发**完整、生产级别的全栈功能包或插件**。你必须严格遵循 GVA 的分层架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
---
### **🚀 重要提示:GVA Helper MCP 支持**
**在开始任何GVA开发工作之前,请务必注意以下重要工作流程:**
1. **MCP支持**: GVA框架本身支持MCP(Model Context Protocol),提供了强大的开发辅助能力
2. **GVA Helper**: 通常会有一个名为 "**GVA Helper**" 的MCP助手,专门为GVA框架开发提供支持
3. **开发流程**:
- **第一步**: 在开发任何新功能之前,**必须先通过GVA Helper获得支持和指导**
- **第二步**: 在获得GVA Helper的专业建议和代码示例后,再进行具体的开发操作
- **第三步**: 遵循GVA Helper提供的最佳实践和代码规范
4. **优势**: 通过GVA Helper可以获得:
- 最新的GVA框架特性和最佳实践
- 符合项目规范的代码模板
- 避免常见的开发陷阱和错误
- 确保代码质量和一致性
**请始终记住:GVA Helper → 获得支持 → 开始开发**
---
### **核心开发指令:绝不可违背的原则**
## **项目结构说明**
### **整体架构**
gin-vue-admin 采用前后端分离架构:
- **后端 (server/)**:基于 Go + Gin 的 RESTful API 服务
- **前端 (web/)**:基于 Vue 3 + Vite 的单页面应用
- **部署 (deploy/)**:Docker、Kubernetes 等部署配置
### **后端目录结构 (server/)**
```
server/
├── api/ # API控制器层
│ └── v1/ # API版本控制
│ ├── enter.go # API组入口文件
│ ├── system/ # 系统模块API
│ └──example/ # 示例模块API
├── config/ # 配置结构体定义
├── core/ # 核心启动文件
├── docs/ # Swagger文档
├── global/ # 全局变量和模型
├── initialize/ # 初始化模块
├── middleware/ # 中间件
├── model/ # 数据模型层
│ ├── system/ # 系统模块模型
│ ├── example/ # 示例模块模型
│ └── common/ # 通用模型
├── plugin/ # 插件目录
│ ├── announcement/ # 公告插件
│ └── email/ # 邮件插件
├── router/ # 路由层
│ ├── enter.go # 路由组入口
│ ├── system/ # 系统路由
│ └──example/ # 示例路由
├── service/ # 服务层
│ ├── enter.go # 服务组入口
│ ├── system/ # 系统服务
│ └── example/ # 示例服务
├── source/ # 数据初始化
├── utils/ # 工具包
├── config.yaml # 配置文件
└── main.go # 程序入口
```
### **前端目录结构 (web/)**
```
web/
├── public/ # 静态资源
├── src/
│ ├── api/ # API接口定义
│ │ ├── user.js # 用户相关API
│ │ ├── menu.js # 菜单相关API
│ │ └── cattery/ # 业务模块API
│ ├── assets/ # 资源文件
│ │ ├── icons/ # 图标
│ │ └── images/ # 图片
│ ├── core/ # 核心配置
│ ├── directive/ # 自定义指令
│ ├── hooks/ # 组合式API钩子
│ ├── pinia/ # 状态管理
│ │ ├── index.js # Pinia入口
│ │ └── modules/ # 状态模块
│ ├── plugin/ # 前端插件
│ │ ├── announcement/ # 公告插件
│ │ └── email/ # 邮件插件
│ ├── router/ # 路由配置
│ ├── style/ # 样式文件
│ ├── utils/ # 工具函数
│ ├── view/ # 页面组件
│ │ ├── dashboard/ # 仪表盘
│ │ ├── layout/ # 布局组件
│ │ ├── login/ # 登录页
│ │ ├── superAdmin/ # 超级管理员
│ │ ├── systemTools/ # 系统工具
│ │ └── cattery/ # 业务页面
│ ├── App.vue # 根组件
│ └── main.js # 程序入口
├── package.json # 依赖配置
├── vite.config.js # Vite配置
└── uno.config.js # UnoCSS配置
```
---
#### 后端规则
在编写任何代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
1. **严格的分层架构**:
- **职责单一**: 每个层(Model, Service, API, Router)都有其唯一职责,**严禁跨层调用**。例如,API层绝不能直接操作数据库,必须通过Service层。Service层绝不能直接处理`gin.Context`。
- **依赖关系**: 依赖链条必须是单向的:`Router -> API -> Service -> Model`。
2. **`enter.go` 组管理模式**:
- 所有 `api`, `service`, `router` 层都**必须**使用 `enter.go` 文件来创建和暴露各自的 `ApiGroup`, `ServiceGroup`, `RouterGroup`。
- 全局实例变量(如 `service.ServiceGroupApp`)是模块间通信的唯一入口,以此来避免循环引用。
3. **详尽的 Swagger 注释 (API层强制要求)**:
- **每一个**对外暴露的 API 函数都**必须**拥有完整且准确的 Swagger 注释块。这不仅是API文档的来源,也是前后端协作、自动化测试和前端AI分析的基础。注释必须清晰地描述接口的功能、参数和返回值。
4. **统一的响应与错误处理**:
- Service 层函数遇到业务错误时,应返回 `error` 对象。
- API 层负责捕获 Service 层的 `error`,并使用项目统一的 `response` 包(如 `response.OkWithDetailed` 或 `response.FailWithMessage`)将其转换为格式化的 JSON 响应和正确的 HTTP 状态码。
---
### **各层级代码实现规范**
#### **1. 模型层 (`model/`)**
- **数据模型 (`model/xxx.go`)**:
- 用于定义与数据库表映射的 GORM 结构体。
- 结构体应继承 `global.GVA_MODEL` 以包含 `ID`, `CreatedAt`, `UpdatedAt` 等基础字段。
- 必须为字段添加清晰的 `json` 和 `gorm` 标签。
- **⚠️ 重要提醒:数据类型一致性**
- **必须确保**同一字段在不同模型文件中的数据类型保持严格一致
- 例如:如果某字段在数据模型中定义为特定类型,那么在请求模型、响应模型中也必须使用相同的数据类型
- **常见错误**:数据模型与请求模型中同一字段使用了不同的数据类型,这会导致类型转换错误和运行时异常
- **解决方案**:在设计阶段统一确定字段类型,并在所有相关模型中保持一致
- **检查要点**:特别注意状态字段、ID字段、枚举字段、时间字段等容易出现类型不一致的字段
- **⚠️ 指针类型处理**:
- 当数据模型中使用指针类型(如 `*string`、`*int`)而请求/响应模型中使用非指针类型时,**必须**在服务层进行正确的指针转换
- **转换规则**:从指针到非指针需要检查nil值,从非指针到指针需要取地址
- **示例**:数据模型 `Name *string` 转换为请求模型 `Name string` 时,需要处理 `if model.Name != nil { request.Name = *model.Name }`
- **请求模型 (`model/request/xxx.go`)**:
- 用于定义接收前端请求参数的结构体(DTOs)。
- **必须**为字段添加 `json` 和 `form` 标签,以便 Gin 进行参数绑定。
- 对于列表查询请求,应创建一个 `XxxSearch` 结构体,并内嵌通用的 `request.PageInfo` 分页结构体。
#### **2. 服务层 (`service/`)**
- **职责**: 封装所有核心业务逻辑,进行数据库的CRUD操作。**此层不应出现任何与HTTP协议相关的代码(如 `gin.Context`)**。
- **结构**: 在 `service/` 下为每个模块创建 `xxx_service.go` 文件,并在 `service/enter.go` 中注册。
- **函数签名**: 函数应接收具体的业务参数(如 `model.Xxx` 或 `request.XxxSearch`),并返回处理结果和 `error`。
- **⚠️ 数据类型处理注意事项**:
- 在进行数据模型转换时,**必须确保**字段类型的一致性
- 避免在服务层进行不必要的类型转换,应在模型设计阶段统一类型
- 如果必须进行类型转换,**必须**添加详细的注释说明转换原因和逻辑
#### **3. API层 (`api/`)**
- **职责**: 作为HTTP请求的入口,负责参数校验、调用Service层方法、并返回格式化的JSON响应。
- **结构**: 在 `api/` 下为每个模块创建 `xxx_api.go` 文件,并在 `api/enter.go` 中注册。
- **交互**: **必须**通过全局变量 `service.ServiceGroupApp` 来调用服务层的方法。
- **Swagger 示例 (必须遵循)**:
Go
```
// CreateXxx 创建XXX
// @Tags XxxModule
// @Summary 创建一个新的XXX
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.CreateXxxRequest true "XXX的名称和描述"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /xxx/createXxx [post]
func (a *XxxApi) CreateXxx(c *gin.Context) {
// ...
}
```
#### **4. 路由层 (`router/`)**
- **职责**: 定义API路由规则,并将HTTP请求路径映射到具体的API处理函数上,同时配置中间件。
- **结构**: 在 `router/` 下为每个模块创建 `xxx_router.go` 文件,并在 `router/enter.go` 中注册。
- **交互**: **必须**通过全局变量 `api.ApiGroupApp` 来引用API层的处理函数。
- **路由分组**: 应根据业务需求和权限,合理使用路由组 (`Router.Group()`),并挂载不同的中间件(如鉴权、操作记录等)。
#### **5. 初始化层 (`initialize/`)**
- **职责**: 提供插件资源(数据库、路由、菜单等)的初始化入口,供主程序调用。
- **`gorm.go`**: 实现 `InitializeDB` 函数,**必须**调用 `db.AutoMigrate` 自动迁移本插件所有 `model` 的表结构。
- **`router.go`**: 实现 `InitializeRouter` 函数,**必须**调用 `router.RouterGroupApp` 中本插件路由的初始化方法,注册所有API路由。
- **`menu.go`**: 实现 `InitializeMenu` 函数,负责在数据库中创建或更新本插件的侧边栏菜单、按钮和对应的API权限。
- viper.go: 加载插件配置文件
- api.go: 注册API到系统
#### **6. 插件入口 (`plugin.go`)
- **职责**: 作为插件的唯一入口,实现 GVA 的插件接口,让框架能够识别和加载本插件。
- **接口实现**: **必须**定义一个结构体并实现 `system.Plugin` 接口。
- **`Register`方法**: 实现 `Register` 方法,该方法接收一个 `*gin.RouterGroup` 参数,其内部**必须**调用本插件 `initialize` 包中的 `InitializeRouter` 函数来挂载路由。
- **`RouterPath`方法**: 实现 `RouterPath` 方法,返回该插件所有API的根路径,例如 `"/myPlugin"`。
### 模块间引用关系:
- API层引用Service层:在API文件中定义变量如 `var xxxService = service.ServiceGroupApp.XxxService`
- Router层引用API层:在路由函数中使用 `api.ApiGroupApp.XxxApi.XxxMethod`
- Initialize/Router引用Router层:通过 `router.RouterGroupApp.XxxRouter.InitXxxRouter`
- 各模块通过enter.go文件组织和暴露功能,避免循环引用
### 代码组织示例:
1. Service入口 (service/enter.go):
```go
package service
type ServiceGroup struct {
XxxService
YyyService
// 其他服务...
}
var ServiceGroupApp = new(ServiceGroup)
```
2. API入口 (api/enter.go):
```go
package api
type ApiGroup struct {
XxxApi
YyyApi
// 其他API...
}
var ApiGroupApp = new(ApiGroup)
```
3. Router入口 (router/enter.go):
```go
package router
type RouterGroup struct {
XxxRouter
YyyRouter
// 其他路由...
}
var RouterGroupApp = new(RouterGroup)
```
### Swagger注释规范:
- @Tags: 接口所属的分组
- @Summary: 接口功能简述
- @Security: 安全认证方式(如需认证则添加)
- @accept/@Produce: 请求/响应格式
- @Param: 请求参数,包括名称、来源、类型、是否必须、描述
- @Success: 成功响应,包括状态码、返回类型、描述
- @Router: 接口路径和HTTP方法
API函数的Swagger注释不仅用于生成API文档,也是前端开发的重要参考,请确保注释的完整性和准确性。
---
### **开发工作流**
1. **接收任务**: 我会向你下达一个具体的功能插件开发任务,例如:“请为项目创建一个‘商品管理 (Product)’插件”。
2. **【第一步】模型设计 (奠定基础)**:
- 你的**首要行动**是分析需求,设计并提供 `model` 和 `model/request` 下的所有 Go 结构体定义。这是后续所有开发的基础。
3. **【第二步】自下而上,分层实现**:
- 具体项目结构可以参考:server/plugin/announcement 这个插件,非常经典!
- 在模型确认后,你将按照 `Service -> API -> Router` 的顺序,逐层生成代码。
- 确保每一层的代码都完整、健壮,并严格遵守上述规范。
4. **【第三步】插件初始化与注册**:
- 在完成核心功能层的代码后,你将生成 `initialize/` 目录下的相关初始化文件(如 `db.go`, `router.go`)以及插件的主入口文件 `plugin.go`。
5. **【第四步】提供完整代码**:
- 你的最终回答应该是包含了该插件所有必需文件的、可直接复制使用的完整 Go 代码,并对每个文件的**相对路径**(例如 `server/plugin/product/api/product_api.go`)和用途进行清晰的说明。
---
## **前端开发规范**
### **角色与目标**
你是一名资深的 Vue.js 前端开发专家,**专精于 `gin-vue-admin` (GVA) 框架的前端架构与开发范式**。
你的核心任务是,根据需求开发**完整、生产级别的前端功能模块或插件**。你必须严格遵循 GVA 的前端架构、代码规范和核心设计模式,确保你生成的每一部分代码都能无缝集成到现有项目中。
### **核心开发指令:绝不可违背的原则**
#### 前端规则
在编写任何前端代码之前,你必须将以下 GVA 的核心设计原则作为最高行为准则:
1. **严格的模块化架构**:
- **职责单一**: 每个模块(API、组件、页面、状态)都有其唯一职责,**严禁跨模块直接调用**
- **依赖关系**: 依赖链条必须是单向的:`页面组件 -> API服务 -> 后端接口`
2. **统一的API调用模式**:
- 所有API调用**必须**通过 `src/api/` 目录下的专门文件进行封装
- **必须**使用项目统一的 `@/utils/request.js` 进行HTTP请求
- API函数**必须**包含完整的JSDoc注释,描述接口功能、参数和返回值
3. **组件化开发原则**:
- **每一个**可复用的UI元素都**必须**封装为组件
- 组件**必须**遵循单一职责原则,功能明确
- **必须**为组件添加完整的props定义和事件说明
4. **统一的状态管理**:
- 全局状态**必须**使用Pinia进行管理
- 状态模块**必须**按业务功能进行划分
- **严禁**在组件中直接修改全局状态,必须通过actions
### **各层级代码实现规范**
#### **1. API层 (`src/api/`)**
- **职责**: 封装所有后端API调用,提供统一的接口服务
- **结构**: 按业务模块创建API文件,如 `user.js`、`menu.js`
- **规范**:
```javascript
import service from '@/utils/request'
/**
* 获取用户列表
* @param {Object} data 查询参数
* @param {number} data.page 页码
* @param {number} data.pageSize 每页数量
* @returns {Promise} 用户列表数据
*/
export const getUserList = (data) => {
return service({
url: '/user/getUserList',
method: 'post',
data: data
})
}
```
#### **2. 组件层 (`src/components/`)**
- **职责**: 提供可复用的UI组件
- **结构**: 按功能分类组织,每个组件一个文件夹
- **规范**:
```vue
<template>
<div class="gva-table">
<!-- 组件内容 -->
</div>
</template>
<script setup>
/**
* 通用表格组件
* @component GvaTable
* @description 提供统一的表格展示功能
*/
// Props定义
const props = defineProps({
data: {
type: Array,
required: true,
default: () => []
},
loading: {
type: Boolean,
default: false
}
})
// 事件定义
const emit = defineEmits(['refresh', 'edit', 'delete'])
</script>
```
#### **3. 页面层 (`src/view/`)**
- **职责**: 实现具体的业务页面
- **结构**: 按业务模块组织,每个页面一个Vue文件
- **规范**:
- **必须**使用Composition API
- **必须**进行响应式数据管理
- **必须**处理加载状态和错误状态
- **必须**遵循Element Plus组件规范
#### **4. 状态管理 (`src/pinia/`)**
- **职责**: 管理全局状态和业务逻辑
- **结构**: 按业务模块创建store文件
- **规范**:
```javascript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'
export const useUserStore = defineStore('user', () => {
// 状态定义 - 使用 ref() 创建响应式状态
const userInfo = ref({
uuid: '',
nickName: '',
headerImg: '',
authority: {}
})
const token = useStorage('token', '')
// 计算属性 - 使用 computed() 定义
const isLogin = computed(() => !!token.value)
// 方法定义 - 直接定义函数作为 actions
const setUserInfo = (val) => {
userInfo.value = val
}
const setToken = (val) => {
token.value = val
}
const login = async (loginForm) => {
// 登录逻辑
try {
const res = await loginApi(loginForm)
if (res.code === 0) {
setUserInfo(res.data.user)
setToken(res.data.token)
return true
}
return false
} catch (error) {
console.error('Login error:', error)
return false
}
}
const logout = async () => {
// 登出逻辑
token.value = ''
userInfo.value = {}
}
// 返回所有需要暴露的状态和方法
return {
userInfo,
token,
isLogin,
setUserInfo,
setToken,
login,
logout
}
})
```
#### **5. 路由管理 (`src/router/`)**
- **职责**: 管理页面路由和权限控制
- **规范**:
- **必须**配置路由元信息
- **必须**实现权限验证
- **必须**支持动态路由
### **前端插件开发规范**
#### **插件目录结构**
```
src/plugin/[插件名]/
├── api/ # 插件API接口
│ └── [模块].js
├── components/ # 插件组件(可选)
│ └── [组件名].vue
├── view/ # 插件页面
│ └── [页面名].vue
├── form/ # 插件表单(可选)
│ └── [表单名].vue
└── index.js # 插件入口文件(可选)
```
#### **插件开发原则**
1. **独立性**: 插件应该是自包含的,不依赖其他业务模块
2. **可配置性**: 插件应该支持配置化,便于定制
3. **可扩展性**: 插件应该预留扩展接口
4. **一致性**: 插件UI风格应与主系统保持一致
### **代码质量要求**
1. **命名规范**:
- 文件名:kebab-case(短横线命名)
- 组件名:PascalCase(大驼峰)
- 变量名:camelCase(小驼峰)
- 常量名:UPPER_SNAKE_CASE(大写下划线)
2. **注释规范**:
- **必须**为所有API函数添加JSDoc注释
- **必须**为复杂组件添加功能说明
- **必须**为关键业务逻辑添加行内注释
3. **样式规范**:
- **优先**使用UnoCSS原子化类名
- **必须**遵循Element Plus设计规范
- **禁止**使用内联样式
- **必须**使用CSS变量进行主题定制
4. **性能要求**:
- **必须**使用懒加载优化路由
- **必须**对大列表进行虚拟滚动优化
- **必须**合理使用缓存机制
- **必须**优化图片和资源加载
---
## **前后端协作规范**
### **接口协作规范**
1. **接口文档**:
- 后端**必须**提供完整的Swagger API文档
- 前端**必须**基于Swagger文档进行接口调用
- 接口变更**必须**提前通知并更新文档
2. **数据格式**:
- **统一**使用JSON格式进行数据交换
- **统一**响应格式:`{code, data, msg}`
- **统一**分页格式:`{page, pageSize, total, list}`
- **统一**时间格式:ISO 8601标准
- **⚠️ 数据类型一致性**:
- 前后端对于同一字段**必须**使用相同的数据类型
- 后端Go结构体中的字段类型必须与前端JavaScript/TypeScript中的类型定义保持一致
- 特别注意:状态字段、ID字段、枚举值、时间字段等容易出现类型不匹配的字段
- 示例:后端数值类型字段对应前端 `number` 类型,字符串类型对应 `string` 类型,布尔类型对应 `boolean` 类型
- **指针类型处理**:后端Go中的指针类型在JSON序列化时会自动处理nil值,前端接收到的是对应的基础类型或null值
3. **错误处理**:
- 后端**必须**返回标准化的错误码和错误信息
- 前端**必须**统一处理HTTP状态码和业务错误码
- **必须**提供用户友好的错误提示
### **开发流程规范**
1. **需求分析阶段**:
- 确定功能需求和接口设计
- 定义数据模型和业务流程
- 制定前后端开发计划
2. **开发阶段**:
- 后端优先开发API接口
- 前端基于Mock数据进行并行开发
- 定期进行接口联调测试
3. **测试阶段**:
- 单元测试:前后端各自负责
- 集成测试:前后端协作完成
- 用户验收测试:产品团队主导
### **版本管理规范**
1. **分支策略**:
- `main`:生产环境分支
- `develop`:开发环境分支
- `feature/*`:功能开发分支
- `hotfix/*`:紧急修复分支
2. **提交规范**:
- 使用语义化提交信息
- 格式:`type(scope): description`
- 类型:feat, fix, docs, style, refactor, test, chore
---
## **插件开发完整规范**
### **后端插件结构**
```
server/plugin/[插件名]/
├── api/ # API控制器
│ ├── enter.go # API组入口
│ └── [模块].go # 具体API实现
├── config/ # 插件配置
│ └── config.go
├── initialize/ # 初始化模块
│ ├── api.go # API注册
│ ├── gorm.go # 数据库初始化
│ ├── menu.go # 菜单初始化
│ ├── router.go # 路由初始化
│ └── viper.go # 配置初始化
├── model/ # 数据模型
│ ├── [模型].go # 数据库模型
│ └── request/ # 请求模型
├── router/ # 路由定义
│ ├── enter.go # 路由组入口
│ └── [模块].go # 具体路由
├── service/ # 业务服务
│ ├── enter.go # 服务组入口
│ └── [模块].go # 具体服务
└── plugin.go # 插件入口
```
### **前端插件结构**
```
web/src/plugin/[插件名]/
├── api/ # API接口
│ └── [模块].js
├── components/ # 插件组件
│ └── [组件].vue
├── view/ # 插件页面
│ └── [页面].vue
├── form/ # 表单组件
│ └── [表单].vue
└── config.js # 插件配置
```
### **插件开发工作流**
1. **【第一步】需求分析**:
- 明确插件功能和业务需求
- 设计数据模型和接口规范
- 规划前端页面和交互流程
2. **【第二步】后端开发**:
- 创建数据模型和请求模型
- 实现服务层业务逻辑
- 开发API控制器和路由
- 编写初始化和配置代码
3. **【第三步】前端开发**:
- 创建API接口封装
- 开发页面组件和表单
- 实现业务逻辑和状态管理
- 集成到主系统菜单
4. **【第四步】测试集成**:
- 单元测试和集成测试
- 前后端联调测试
- 用户体验测试
- 性能和安全测试
### **插件质量标准**
1. **功能完整性**: 插件功能完整,满足业务需求
2. **代码质量**: 代码规范,注释完整,易于维护
3. **数据类型一致性**: 前后端数据模型字段类型保持严格一致,避免类型转换错误
4. **性能表现**: 响应速度快,资源占用合理
5. **用户体验**: 界面友好,操作流畅,错误处理完善
6. **兼容性**: 与主系统兼容,不影响其他功能
7. **安全性**: 数据安全,权限控制,防止安全漏洞
---
### **建议和方案**
基于以上规范,建议AI在开发gin-vue-admin项目时:
1. **严格遵循分层架构**:确保前后端代码都按照规定的层次结构组织
2. **保持代码一致性**:使用统一的命名规范、注释格式和代码风格
3. **注重文档完整性**:确保API文档、代码注释和使用说明的完整性
4. **优化用户体验**:关注页面加载速度、交互流畅性和错误处理
5. **考虑扩展性**:设计时预留扩展接口,便于后续功能增强
6. **重视安全性**:实现完善的权限控制和数据验证机制
+10 -13
View File
@@ -13,16 +13,9 @@ CONFIG_FILE = config.yaml
IMAGE_NAME = gva
#镜像地址
REPOSITORY = registry.cn-hangzhou.aliyuncs.com/${IMAGE_NAME}
ifeq ($(TAGS_OPT),)
TAGS_OPT = latest
else
endif
ifeq ($(PLUGIN),)
PLUGIN = email
else
endif
#镜像版本
TAGS_OPT ?= latest
PLUGIN ?= email
#容器环境前后端共同打包
build: build-web build-server
@@ -48,7 +41,7 @@ build-image-server:
build-local:
if [ -d "build" ];then rm -rf build; else echo "OK!"; fi \
&& if [ -f "/.dockerenv" ];then echo "OK!"; else make build-web-local && make build-server-local; fi \
&& mkdir build && cp -r web/dist build/ && cp server/server build/ && cp -r server/resource build/resource
&& mkdir build && cp -r web/dist build/ && cp server/server build/ && cp -r server/resource build/resource
#本地环境打包前端
build-web-local:
@@ -63,13 +56,17 @@ build-server-local:
&& go build -ldflags "-B 0x$(shell head -c20 /dev/urandom|od -An -tx1|tr -d ' \n') -X main.Version=${TAGS_OPT}" -v
#打包前后端二合一镜像
image: build
image: build
docker build -t ${REPOSITORY}/gin-vue-admin:${TAGS_OPT} -f deploy/docker/Dockerfile .
#尝鲜版
images: build build-image-web build-image-server
docker build -t ${REPOSITORY}/all:${TAGS_OPT} -f deploy/docker/Dockerfile .
#swagger 文档生成
doc:
@cd server && swag init
#插件快捷打包: make plugin PLUGIN="这里是插件文件夹名称,默认为email"
plugin:
if [ -d ".plugin" ];then rm -rf .plugin ; else echo "OK!"; fi && mkdir -p .plugin/${PLUGIN}/{server/plugin,web/plugin} \
+4
View File
@@ -299,6 +299,10 @@ swag init
Thank you for considering your contribution to gin-vue-admin!
<a href="https://openomy.app/github/flipped-aurora/gin-vue-admin" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.app/svg?repo=flipped-aurora/gin-vue-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
<a href="https://github.com/flipped-aurora/gin-vue-admin/graphs/contributors">
<img src="https://contrib.rocks/image?repo=flipped-aurora/gin-vue-admin" />
</a>
+36 -7
View File
@@ -2,16 +2,42 @@
<div align=center>
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width="300" height="300" />
</div>
<div align=center>
<img src="https://img.shields.io/badge/golang-1.20-blue"/>
<img src="https://img.shields.io/badge/gin-1.9.1-lightBlue"/>
<img src="https://img.shields.io/badge/vue-3.3.4-brightgreen"/>
<img src="https://img.shields.io/badge/element--plus-2.3.8-green"/>
<img src="https://img.shields.io/badge/gorm-1.25.2-red"/>
<img src="https://gitcode.com/flipped-aurora/gin-vue-admin/star/badge.svg"/>
</div>
<div align=center>
<a href="https://trendshift.io/repositories/3250" target="_blank"><img src="https://trendshift.io/api/badge/repositories/3250" alt="Calcium-Ion%2Fnew-api | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</div>
[English](./README-en.md) | 简体中文
## ✨一分钟生成前后端基础代码
<table>
<tr>
<td width="250">
<p>⭐️ <a href="https://www.bilibili.com/video/BV1B3htzqEf1/?spm_id_from=333.1387.homepage.video_card.click" target="__blank"> 高度适配AI编辑器的MCP </a></p>
<p>📄 创建基础模板</p>
<p>🤖 AI生成结构</p>
<p>⏰ 生成代码</p>
<p>🏷️ 分配权限</p>
<p>🎉 基础CURD生成完成</p>
</td>
<td>
<video src="https://private-user-images.githubusercontent.com/165128580/384700666-4d039215-af29-4f86-bb4f-60dbab38f58e.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzEyNTIxNDYsIm5iZiI6MTczMTI1MTg0NiwicGF0aCI6Ii8xNjUxMjg1ODAvMzg0NzAwNjY2LTRkMDM5MjE1LWFmMjktNGY4Ni1iYjRmLTYwZGJhYjM4ZjU4ZS5tcDQ_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQxMTEwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MTExMFQxNTE3MjZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT00NjJkMDcwZjJkMjAyMmU1N2I2MzQxY2RhODFlNzgzNGRiMDFhMmY2NTYyM2ZmODdhNDVmMWE1NzlhMDdlOTI5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.ZJbswpLzF2RHjemcGirKOP0L1fvpl3FUqIiQ_-yjeUo" data-canonical-src="https://private-user-images.githubusercontent.com/165128580/384700666-4d039215-af29-4f86-bb4f-60dbab38f58e.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzEyNTIxNDYsIm5iZiI6MTczMTI1MTg0NiwicGF0aCI6Ii8xNjUxMjg1ODAvMzg0NzAwNjY2LTRkMDM5MjE1LWFmMjktNGY4Ni1iYjRmLTYwZGJhYjM4ZjU4ZS5tcDQ_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQxMTEwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MTExMFQxNTE3MjZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT00NjJkMDcwZjJkMjAyMmU1N2I2MzQxY2RhODFlNzgzNGRiMDFhMmY2NTYyM2ZmODdhNDVmMWE1NzlhMDdlOTI5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.ZJbswpLzF2RHjemcGirKOP0L1fvpl3FUqIiQ_-yjeUo" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px">
</video>
</td>
</tr>
</table>
# 项目文档
[在线文档](https://www.gin-vue-admin.com) : https://www.gin-vue-admin.com
@@ -25,6 +51,8 @@
[插件市场](https://plugin.gin-vue-admin.com/)
[软件著作权证书](https://www.gin-vue-admin.com/copyright.pdf)
# 重要提示
1.本项目从起步到开发到部署均有文档和详细视频教程
@@ -75,7 +103,7 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm
## 2. 使用说明
```
- node版本 > v16.8.3
- node版本 > v18.16.0
- golang版本 >= v1.22
- IDE推荐:Goland
```
@@ -322,7 +350,7 @@ swag init
### 7.1 技术群
### QQ交流群:470239250
### QQ交流群:971857775
### 微信交流群
| 微信 |
@@ -343,14 +371,15 @@ fmt.Println(decodeBytes, err)
感谢您对gin-vue-admin的贡献!
<a href="https://github.com/flipped-aurora/gin-vue-admin/graphs/contributors">
<img src="https://contrib.rocks/image?repo=flipped-aurora/gin-vue-admin" />
</a>
<a href="https://openomy.app/github/flipped-aurora/gin-vue-admin" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.app/svg?repo=flipped-aurora/gin-vue-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
## 9. 捐赠
如果你觉得这个项目对你有帮助,你可以请作者喝饮料 :tropical_drink: [点我](https://www.gin-vue-admin.com/coffee/index.html)
## 10. 商用注意事项
## 10. 注意事项
如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。
请严格遵守Apache 2.0协议并保留作品声明,去除版权信息请务必[获取授权](https://www.gin-vue-admin.com/empower/)
未授权去除版权信息将依法追究法律责任
@@ -1,99 +0,0 @@
version: "3"
# 声明一个名为network的networks,subnet为network的子网地址,默认网关是177.7.0.1
networks:
network:
ipam:
driver: default
config:
- subnet: '177.7.0.0/16'
# 设置mysql,redis持久化保存
volumes:
mysql:
redis:
services:
web:
image: node:20
container_name: gva-web
hostname: gva-web #可以通过容器名访问
restart: always
ports:
- '8080:8080'
depends_on:
- server
working_dir: /web # 如果docker 设置了workdir 则此处不需要设置
#若网络不太好,请自行换源,如下
#command: bash -c "yarn config set registry https://registry.npmmirror.com --global && yarn install && yarn serve"
command: bash -c "yarn install && yarn serve"
volumes:
- ../../web:/web
networks:
network:
ipv4_address: 177.7.0.11
server:
image: golang:1.22
container_name: gva-server
hostname: gva-server
restart: always
ports:
- '8888:8888'
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ../../server:/server
working_dir: /server # 如果docker 设置了workdir 则此处不需要设置
command: bash -c "go env -w GOPROXY=https://goproxy.cn,direct && go mod tidy && go run main.go"
links:
- mysql
- redis
networks:
network:
ipv4_address: 177.7.0.12
mysql:
image: mysql:8.0.21 # 如果您是 arm64 架构:如 MacOS 的 M1,请修改镜像为 image: mysql/mysql-server:8.0.21
container_name: gva-mysql
hostname: gva-mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci #设置utf8字符集
restart: always
ports:
- "13306:3306" # host物理直接映射端口为13306
environment:
#MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "gva", "-pAa@6447985"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- mysql:/var/lib/mysql
networks:
network:
ipv4_address: 177.7.0.13
redis:
image: redis:6.0.6
container_name: gva-redis # 容器名
hostname: gva-redis
restart: always
ports:
- '16379:6379'
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG || exit 1"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- redis:/data
networks:
network:
ipv4_address: 177.7.0.14
+2
View File
@@ -5,9 +5,11 @@ import "github.com/flipped-aurora/gin-vue-admin/server/service"
type ApiGroup struct {
CustomerApi
FileUploadAndDownloadApi
AttachmentCategoryApi
}
var (
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
attachmentCategoryService = service.ServiceGroupApp.ExampleServiceGroup.AttachmentCategoryService
)
@@ -0,0 +1,82 @@
package example
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AttachmentCategoryApi struct{}
// GetCategoryList
// @Tags GetCategoryList
// @Summary 媒体库分类列表
// @Security AttachmentCategory
// @Produce application/json
// @Success 200 {object} response.Response{data=example.ExaAttachmentCategory,msg=string} "媒体库分类列表"
// @Router /attachmentCategory/getCategoryList [get]
func (a *AttachmentCategoryApi) GetCategoryList(c *gin.Context) {
res, err := attachmentCategoryService.GetCategoryList()
if err != nil {
global.GVA_LOG.Error("获取分类列表失败!", zap.Error(err))
response.FailWithMessage("获取分类列表失败", c)
return
}
response.OkWithData(res, c)
}
// AddCategory
// @Tags AddCategory
// @Summary 添加媒体库分类
// @Security AttachmentCategory
// @accept application/json
// @Produce application/json
// @Param data body example.ExaAttachmentCategory true "媒体库分类数据"// @Success 200 {object} response.Response{msg=string} "添加媒体库分类"
// @Router /attachmentCategory/addCategory [post]
func (a *AttachmentCategoryApi) AddCategory(c *gin.Context) {
var req example.ExaAttachmentCategory
if err := c.ShouldBindJSON(&req); err != nil {
global.GVA_LOG.Error("参数错误!", zap.Error(err))
response.FailWithMessage("参数错误", c)
return
}
if err := attachmentCategoryService.AddCategory(&req); err != nil {
global.GVA_LOG.Error("创建/更新失败!", zap.Error(err))
response.FailWithMessage("创建/更新失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建/更新成功", c)
}
// DeleteCategory
// @Tags DeleteCategory
// @Summary 删除分类
// @Security AttachmentCategory
// @accept application/json
// @Produce application/json
// @Param data body common.GetById true "分类id"
// @Success 200 {object} response.Response{msg=string} "删除分类"
// @Router /attachmentCategory/deleteCategory [post]
func (a *AttachmentCategoryApi) DeleteCategory(c *gin.Context) {
var req common.GetById
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("参数错误", c)
return
}
if req.ID == 0 {
response.FailWithMessage("参数错误", c)
return
}
if err := attachmentCategoryService.DeleteCategory(&req.ID); err != nil {
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
@@ -2,12 +2,13 @@ package example
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
"github.com/flipped-aurora/gin-vue-admin/server/model/example/request"
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"strconv"
)
type FileUploadAndDownloadApi struct{}
@@ -25,12 +26,13 @@ func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
var file example.ExaFileUploadAndDownload
noSave := c.DefaultQuery("noSave", "0")
_, header, err := c.Request.FormFile("file")
classId, _ := strconv.Atoi(c.DefaultPostForm("classId", "0"))
if err != nil {
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
response.FailWithMessage("接收文件失败", c)
return
}
file, err = fileUploadAndDownloadService.UploadFile(header, noSave) // 文件上传后拿到文件路径
file, err = fileUploadAndDownloadService.UploadFile(header, noSave, classId) // 文件上传后拿到文件路径
if err != nil {
global.GVA_LOG.Error("上传文件失败!", zap.Error(err))
response.FailWithMessage("上传文件失败", c)
@@ -85,11 +87,11 @@ func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "页码, 每页大小"
// @Param data body request.ExaAttachmentCategorySearch true "页码, 每页大小, 分类id"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量"
// @Router /fileUploadAndDownload/getFileList [post]
func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
var pageInfo request.PageInfo
var pageInfo request.ExaAttachmentCategorySearch
err := c.ShouldBindJSON(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
+144
View File
@@ -0,0 +1,144 @@
package system
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/mcp/client"
"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"
"github.com/mark3labs/mcp-go/mcp"
)
// 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)
}
// 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/mcpList [post]
func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) {
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
defer testClient.Close()
toolsRequest := mcp.ListToolsRequest{}
list, err := testClient.ListTools(c.Request.Context(), toolsRequest)
if err != nil {
response.FailWithMessage("创建失败", c)
global.GVA_LOG.Error(err.Error())
return
}
mcpServerConfig := map[string]interface{}{
"mcpServers": map[string]interface{}{
global.GVA_CONFIG.MCP.Name: map[string]string{
"url": baseUrl,
},
},
}
response.OkWithData(gin.H{
"mcpServerConfig": mcpServerConfig,
"list": list,
}, c)
}
// Create
// @Tags mcp
// @Summary 测试McpTool
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body object true "调用MCP Tool的参数"
// @Success 200 {object} response.Response "{"success":true,"data":{},"msg":"测试成功"}"
// @Router /autoCode/mcpTest [post]
func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
// 定义接口请求结构
var testRequest struct {
Name string `json:"name" binding:"required"` // 工具名称
Arguments map[string]interface{} `json:"arguments" binding:"required"` // 工具参数
}
// 绑定JSON请求体
if err := c.ShouldBindJSON(&testRequest); err != nil {
response.FailWithMessage("参数解析失败:"+err.Error(), c)
return
}
// 创建MCP客户端
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
if err != nil {
response.FailWithMessage("创建MCP客户端失败:"+err.Error(), c)
return
}
defer testClient.Close()
ctx := c.Request.Context()
// 初始化MCP连接
initRequest := mcp.InitializeRequest{}
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
initRequest.Params.ClientInfo = mcp.Implementation{
Name: "testClient",
Version: "v1.0.0",
}
_, err = testClient.Initialize(ctx, initRequest)
if err != nil {
response.FailWithMessage("初始化MCP连接失败:"+err.Error(), c)
return
}
// 构建工具调用请求
request := mcp.CallToolRequest{}
request.Params.Name = testRequest.Name
request.Params.Arguments = testRequest.Arguments
// 调用工具
result, err := testClient.CallTool(ctx, request)
if err != nil {
response.FailWithMessage("工具调用失败:"+err.Error(), c)
return
}
// 处理响应结果
if len(result.Content) == 0 {
response.FailWithMessage("工具未返回任何内容", c)
return
}
// 返回结果
response.OkWithData(result.Content, c)
}
+2 -2
View File
@@ -70,7 +70,7 @@ func (a *AutoCodePluginApi) Packaged(c *gin.Context) {
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
}
// Packaged
// InitMenu
// @Tags AutoCodePlugin
// @Summary 打包插件
// @Security ApiKeyAuth
@@ -94,7 +94,7 @@ func (a *AutoCodePluginApi) InitMenu(c *gin.Context) {
response.OkWithMessage("文件变更成功", c)
}
// Packaged
// InitAPI
// @Tags AutoCodePlugin
// @Summary 打包插件
// @Security ApiKeyAuth
+3 -3
View File
@@ -40,8 +40,8 @@ func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
info.PackageT = utils.FirstUpper(info.Package)
autoCode, err := autoCodeTemplateService.Preview(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("预览失败!", zap.Error(err))
response.FailWithMessage("预览失败", c)
global.GVA_LOG.Error(err.Error(), zap.Error(err))
response.FailWithMessage("预览失败:"+err.Error(), c)
} else {
response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
}
@@ -82,7 +82,7 @@ func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
}
}
// Create
// AddFunc
// @Tags AddFunc
// @Summary 增加方法
// @Security ApiKeyAuth
+2
View File
@@ -22,6 +22,7 @@ type ApiGroup struct {
AutoCodeHistoryApi
AutoCodeTemplateApi
SysParamsApi
SysVersionApi
}
var (
@@ -44,4 +45,5 @@ var (
autoCodePackageService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistory
autoCodeTemplateService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService
)
+175 -5
View File
@@ -3,6 +3,9 @@ package system
import (
"fmt"
"net/http"
"net/url"
"sync"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
@@ -15,6 +18,33 @@ import (
"go.uber.org/zap"
)
// 用于token一次性存储
var (
exportTokenCache = make(map[string]interface{})
exportTokenExpiration = make(map[string]time.Time)
tokenMutex sync.RWMutex
)
// 五分钟检测窗口过期
func cleanupExpiredTokens() {
for {
time.Sleep(5 * time.Minute)
tokenMutex.Lock()
now := time.Now()
for token, expiry := range exportTokenExpiration {
if now.After(expiry) {
delete(exportTokenCache, token)
delete(exportTokenExpiration, token)
}
}
tokenMutex.Unlock()
}
}
func init() {
go cleanupExpiredTokens()
}
type SysExportTemplateApi struct {
}
@@ -183,7 +213,7 @@ func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gi
}
}
// ExportExcel 导出表格
// ExportExcel 导出表格token
// @Tags SysExportTemplate
// @Summary 导出表格
// @Security ApiKeyAuth
@@ -192,16 +222,83 @@ func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gi
// @Router /sysExportTemplate/exportExcel [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) {
templateID := c.Query("templateID")
queryParams := c.Request.URL.Query()
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
queryParams := c.Request.URL.Query()
//创造一次性token
token := utils.RandomString(32) // 随机32位
// 记录本次请求参数
exportParams := map[string]interface{}{
"templateID": templateID,
"queryParams": queryParams,
}
// 参数保留记录完成鉴权
tokenMutex.Lock()
exportTokenCache[token] = exportParams
exportTokenExpiration[token] = time.Now().Add(30 * time.Minute)
tokenMutex.Unlock()
// 生成一次性链接
exportUrl := fmt.Sprintf("/sysExportTemplate/exportExcelByToken?token=%s", token)
response.OkWithData(exportUrl, c)
}
// ExportExcelByToken 导出表格
// @Tags ExportExcelByToken
// @Summary 导出表格
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportExcelByToken [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcelByToken(c *gin.Context) {
token := c.Query("token")
if token == "" {
response.FailWithMessage("导出token不能为空", c)
return
}
// 获取token并且从缓存中剔除
tokenMutex.RLock()
exportParamsRaw, exists := exportTokenCache[token]
expiry, _ := exportTokenExpiration[token]
tokenMutex.RUnlock()
if !exists || time.Now().After(expiry) {
global.GVA_LOG.Error("导出token无效或已过期!")
response.FailWithMessage("导出token无效或已过期", c)
return
}
// 从token获取参数
exportParams, ok := exportParamsRaw.(map[string]interface{})
if !ok {
global.GVA_LOG.Error("解析导出参数失败!")
response.FailWithMessage("解析导出参数失败", c)
return
}
// 获取导出参数
templateID := exportParams["templateID"].(string)
queryParams := exportParams["queryParams"].(url.Values)
// 清理一次性token
tokenMutex.Lock()
delete(exportTokenCache, token)
delete(exportTokenExpiration, token)
tokenMutex.Unlock()
// 导出
if file, name, err := sysExportTemplateService.ExportExcel(templateID, queryParams); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx")) // 对下载的文件重命名
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx"))
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
}
@@ -213,18 +310,91 @@ func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportExcel [get]
// @Router /sysExportTemplate/exportTemplate [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplate(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
// 创造一次性token
token := utils.RandomString(32) // 随机32位
// 记录本次请求参数
exportParams := map[string]interface{}{
"templateID": templateID,
"isTemplate": true,
}
// 参数保留记录完成鉴权
tokenMutex.Lock()
exportTokenCache[token] = exportParams
exportTokenExpiration[token] = time.Now().Add(30 * time.Minute)
tokenMutex.Unlock()
// 生成一次性链接
exportUrl := fmt.Sprintf("/sysExportTemplate/exportTemplateByToken?token=%s", token)
response.OkWithData(exportUrl, c)
}
// ExportTemplateByToken 通过token导出表格模板
// @Tags ExportTemplateByToken
// @Summary 通过token导出表格模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportTemplateByToken [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplateByToken(c *gin.Context) {
token := c.Query("token")
if token == "" {
response.FailWithMessage("导出token不能为空", c)
return
}
// 获取token并且从缓存中剔除
tokenMutex.RLock()
exportParamsRaw, exists := exportTokenCache[token]
expiry, _ := exportTokenExpiration[token]
tokenMutex.RUnlock()
if !exists || time.Now().After(expiry) {
global.GVA_LOG.Error("导出token无效或已过期!")
response.FailWithMessage("导出token无效或已过期", c)
return
}
// 从token获取参数
exportParams, ok := exportParamsRaw.(map[string]interface{})
if !ok {
global.GVA_LOG.Error("解析导出参数失败!")
response.FailWithMessage("解析导出参数失败", c)
return
}
// 检查是否为模板导出
isTemplate, _ := exportParams["isTemplate"].(bool)
if !isTemplate {
global.GVA_LOG.Error("token类型错误!")
response.FailWithMessage("token类型错误", c)
return
}
// 获取导出参数
templateID := exportParams["templateID"].(string)
// 清理一次性token
tokenMutex.Lock()
delete(exportTokenCache, token)
delete(exportTokenExpiration, token)
tokenMutex.Unlock()
// 导出模板
if file, name, err := sysExportTemplateService.ExportTemplate(templateID); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx")) // 对下载的文件重命名
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx"))
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
}
+1 -1
View File
@@ -143,7 +143,7 @@ func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) {
err = menuService.AddBaseMenu(menu)
if err != nil {
global.GVA_LOG.Error("添加失败!", zap.Error(err))
response.FailWithMessage("添加失败", c)
response.FailWithMessage("添加失败:"+err.Error(), c)
return
}
response.OkWithMessage("添加成功", c)
@@ -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
+7 -6
View File
@@ -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
+37 -39
View File
@@ -1,18 +1,17 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
"strconv"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
@@ -28,8 +27,6 @@ import (
func (b *BaseApi) Login(c *gin.Context) {
var l systemReq.Login
err := c.ShouldBindJSON(&l)
key := c.ClientIP()
if err != nil {
response.FailWithMessage(err.Error(), c)
return
@@ -40,6 +37,7 @@ func (b *BaseApi) Login(c *gin.Context) {
return
}
key := c.ClientIP()
// 判断验证码是否开启
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
@@ -49,30 +47,30 @@ func (b *BaseApi) Login(c *gin.Context) {
}
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) {
u := &system.SysUser{Username: l.Username, Password: l.Password}
user, err := userService.Login(u)
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户名不存在或者密码错误", c)
return
}
if user.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
if oc && (l.Captcha == "" || l.CaptchaId == "" || !store.Verify(l.CaptchaId, l.Captcha, true)) {
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("验证码错误", c)
return
}
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("验证码错误", c)
u := &system.SysUser{Username: l.Username, Password: l.Password}
user, err := userService.Login(u)
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户名不存在或者密码错误", c)
return
}
if user.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
}
// TokenNext 登录以后签发jwt
@@ -94,7 +92,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
@@ -115,7 +113,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
}
@@ -185,7 +183,7 @@ func (b *BaseApi) ChangePassword(c *gin.Context) {
}
uid := utils.GetUserID(c)
u := &system.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password}
_, err = userService.ChangePassword(u, req.NewPassword)
err = userService.ChangePassword(u, req.NewPassword)
if err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage("修改失败,原密码与当前账户不符", c)
@@ -257,17 +255,17 @@ func (b *BaseApi) SetUserAuthority(c *gin.Context) {
return
}
claims := utils.GetUserInfo(c)
j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
claims.AuthorityId = sua.AuthorityId
if token, err := j.CreateToken(*claims); err != nil {
token, err := utils.NewJWT().CreateToken(*claims)
if err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
} else {
c.Header("new-token", token)
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
response.OkWithMessage("修改成功", c)
return
}
c.Header("new-token", token)
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
response.OkWithMessage("修改成功", c)
}
// SetUserAuthorities
@@ -468,13 +466,13 @@ func (b *BaseApi) GetUserInfo(c *gin.Context) {
// @Success 200 {object} response.Response{msg=string} "重置用户密码"
// @Router /user/resetPassword [post]
func (b *BaseApi) ResetPassword(c *gin.Context) {
var user system.SysUser
err := c.ShouldBindJSON(&user)
var rps systemReq.ResetPassword
err := c.ShouldBindJSON(&rps)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = userService.ResetPassword(user.ID)
err = userService.ResetPassword(rps.ID, rps.Password)
if err != nil {
global.GVA_LOG.Error("重置失败!", zap.Error(err))
response.FailWithMessage("重置失败"+err.Error(), c)
+486
View File
@@ -0,0 +1,486 @@
package system
import (
"encoding/json"
"fmt"
"net/http"
"sort"
"strconv"
"time"
"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"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type SysVersionApi struct{}
// buildMenuTree 构建菜单树结构
func buildMenuTree(menus []system.SysBaseMenu) []system.SysBaseMenu {
// 创建菜单映射
menuMap := make(map[uint]*system.SysBaseMenu)
for i := range menus {
menuMap[menus[i].ID] = &menus[i]
}
// 构建树结构
var rootMenus []system.SysBaseMenu
for _, menu := range menus {
if menu.ParentId == 0 {
// 根菜单
menuData := convertMenuToStruct(menu, menuMap)
rootMenus = append(rootMenus, menuData)
}
}
// 按sort排序根菜单
sort.Slice(rootMenus, func(i, j int) bool {
return rootMenus[i].Sort < rootMenus[j].Sort
})
return rootMenus
}
// convertMenuToStruct 将菜单转换为结构体并递归处理子菜单
func convertMenuToStruct(menu system.SysBaseMenu, menuMap map[uint]*system.SysBaseMenu) system.SysBaseMenu {
result := system.SysBaseMenu{
Path: menu.Path,
Name: menu.Name,
Hidden: menu.Hidden,
Component: menu.Component,
Sort: menu.Sort,
Meta: menu.Meta,
}
// 清理并复制参数数据
if len(menu.Parameters) > 0 {
cleanParameters := make([]system.SysBaseMenuParameter, 0, len(menu.Parameters))
for _, param := range menu.Parameters {
cleanParam := system.SysBaseMenuParameter{
Type: param.Type,
Key: param.Key,
Value: param.Value,
// 不复制 ID, CreatedAt, UpdatedAt, SysBaseMenuID
}
cleanParameters = append(cleanParameters, cleanParam)
}
result.Parameters = cleanParameters
}
// 清理并复制菜单按钮数据
if len(menu.MenuBtn) > 0 {
cleanMenuBtns := make([]system.SysBaseMenuBtn, 0, len(menu.MenuBtn))
for _, btn := range menu.MenuBtn {
cleanBtn := system.SysBaseMenuBtn{
Name: btn.Name,
Desc: btn.Desc,
// 不复制 ID, CreatedAt, UpdatedAt, SysBaseMenuID
}
cleanMenuBtns = append(cleanMenuBtns, cleanBtn)
}
result.MenuBtn = cleanMenuBtns
}
// 查找并处理子菜单
var children []system.SysBaseMenu
for _, childMenu := range menuMap {
if childMenu.ParentId == menu.ID {
childData := convertMenuToStruct(*childMenu, menuMap)
children = append(children, childData)
}
}
// 按sort排序子菜单
if len(children) > 0 {
sort.Slice(children, func(i, j int) bool {
return children[i].Sort < children[j].Sort
})
result.Children = children
}
return result
}
// DeleteSysVersion 删除版本管理
// @Tags SysVersion
// @Summary 删除版本管理
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Param data body system.SysVersion true "删除版本管理"
// @Success 200 {object} response.Response{msg=string} "删除成功"
// @Router /sysVersion/deleteSysVersion [delete]
func (sysVersionApi *SysVersionApi) DeleteSysVersion(c *gin.Context) {
// 创建业务用Context
ctx := c.Request.Context()
ID := c.Query("ID")
err := sysVersionService.DeleteSysVersion(ctx, ID)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// DeleteSysVersionByIds 批量删除版本管理
// @Tags SysVersion
// @Summary 批量删除版本管理
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
// @Router /sysVersion/deleteSysVersionByIds [delete]
func (sysVersionApi *SysVersionApi) DeleteSysVersionByIds(c *gin.Context) {
// 创建业务用Context
ctx := c.Request.Context()
IDs := c.QueryArray("IDs[]")
err := sysVersionService.DeleteSysVersionByIds(ctx, IDs)
if err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("批量删除成功", c)
}
// FindSysVersion 用id查询版本管理
// @Tags SysVersion
// @Summary 用id查询版本管理
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Param ID query uint true "用id查询版本管理"
// @Success 200 {object} response.Response{data=system.SysVersion,msg=string} "查询成功"
// @Router /sysVersion/findSysVersion [get]
func (sysVersionApi *SysVersionApi) FindSysVersion(c *gin.Context) {
// 创建业务用Context
ctx := c.Request.Context()
ID := c.Query("ID")
resysVersion, err := sysVersionService.GetSysVersion(ctx, ID)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithData(resysVersion, c)
}
// GetSysVersionList 分页获取版本管理列表
// @Tags SysVersion
// @Summary 分页获取版本管理列表
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Param data query systemReq.SysVersionSearch true "分页获取版本管理列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /sysVersion/getSysVersionList [get]
func (sysVersionApi *SysVersionApi) GetSysVersionList(c *gin.Context) {
// 创建业务用Context
ctx := c.Request.Context()
var pageInfo systemReq.SysVersionSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := sysVersionService.GetSysVersionInfoList(ctx, pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetSysVersionPublic 不需要鉴权的版本管理接口
// @Tags SysVersion
// @Summary 不需要鉴权的版本管理接口
// @Accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
// @Router /sysVersion/getSysVersionPublic [get]
func (sysVersionApi *SysVersionApi) GetSysVersionPublic(c *gin.Context) {
// 创建业务用Context
ctx := c.Request.Context()
// 此接口不需要鉴权
// 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
sysVersionService.GetSysVersionPublic(ctx)
response.OkWithDetailed(gin.H{
"info": "不需要鉴权的版本管理接口信息",
}, "获取成功", c)
}
// ExportVersion 创建发版数据
// @Tags SysVersion
// @Summary 创建发版数据
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Param data body systemReq.ExportVersionRequest true "创建发版数据"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /sysVersion/exportVersion [post]
func (sysVersionApi *SysVersionApi) ExportVersion(c *gin.Context) {
ctx := c.Request.Context()
var req systemReq.ExportVersionRequest
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 获取选中的菜单数据
var menuData []system.SysBaseMenu
if len(req.MenuIds) > 0 {
menuData, err = sysVersionService.GetMenusByIds(ctx, req.MenuIds)
if err != nil {
global.GVA_LOG.Error("获取菜单数据失败!", zap.Error(err))
response.FailWithMessage("获取菜单数据失败:"+err.Error(), c)
return
}
}
// 获取选中的API数据
var apiData []system.SysApi
if len(req.ApiIds) > 0 {
apiData, err = sysVersionService.GetApisByIds(ctx, req.ApiIds)
if err != nil {
global.GVA_LOG.Error("获取API数据失败!", zap.Error(err))
response.FailWithMessage("获取API数据失败:"+err.Error(), c)
return
}
}
// 获取选中的字典数据
var dictData []system.SysDictionary
if len(req.DictIds) > 0 {
dictData, err = sysVersionService.GetDictionariesByIds(ctx, req.DictIds)
if err != nil {
global.GVA_LOG.Error("获取字典数据失败!", zap.Error(err))
response.FailWithMessage("获取字典数据失败:"+err.Error(), c)
return
}
}
// 处理菜单数据,构建递归的children结构
processedMenus := buildMenuTree(menuData)
// 处理API数据,清除ID和时间戳字段
processedApis := make([]system.SysApi, 0, len(apiData))
for _, api := range apiData {
cleanApi := system.SysApi{
Path: api.Path,
Description: api.Description,
ApiGroup: api.ApiGroup,
Method: api.Method,
}
processedApis = append(processedApis, cleanApi)
}
// 处理字典数据,清除ID和时间戳字段,包含字典详情
processedDicts := make([]system.SysDictionary, 0, len(dictData))
for _, dict := range dictData {
cleanDict := system.SysDictionary{
Name: dict.Name,
Type: dict.Type,
Status: dict.Status,
Desc: dict.Desc,
}
// 处理字典详情数据,清除ID和时间戳字段
cleanDetails := make([]system.SysDictionaryDetail, 0, len(dict.SysDictionaryDetails))
for _, detail := range dict.SysDictionaryDetails {
cleanDetail := system.SysDictionaryDetail{
Label: detail.Label,
Value: detail.Value,
Extend: detail.Extend,
Status: detail.Status,
Sort: detail.Sort,
// 不复制 ID, CreatedAt, UpdatedAt, SysDictionaryID
}
cleanDetails = append(cleanDetails, cleanDetail)
}
cleanDict.SysDictionaryDetails = cleanDetails
processedDicts = append(processedDicts, cleanDict)
}
// 构建导出数据
exportData := systemRes.ExportVersionResponse{
Version: systemReq.VersionInfo{
Name: req.VersionName,
Code: req.VersionCode,
Description: req.Description,
ExportTime: time.Now().Format("2006-01-02 15:04:05"),
},
Menus: processedMenus,
Apis: processedApis,
Dictionaries: processedDicts,
}
// 转换为JSON
jsonData, err := json.MarshalIndent(exportData, "", " ")
if err != nil {
global.GVA_LOG.Error("JSON序列化失败!", zap.Error(err))
response.FailWithMessage("JSON序列化失败:"+err.Error(), c)
return
}
// 保存版本记录
version := system.SysVersion{
VersionName: utils.Pointer(req.VersionName),
VersionCode: utils.Pointer(req.VersionCode),
Description: utils.Pointer(req.Description),
VersionData: utils.Pointer(string(jsonData)),
}
err = sysVersionService.CreateSysVersion(ctx, &version)
if err != nil {
global.GVA_LOG.Error("保存版本记录失败!", zap.Error(err))
response.FailWithMessage("保存版本记录失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建发版成功", c)
}
// DownloadVersionJson 下载版本JSON数据
// @Tags SysVersion
// @Summary 下载版本JSON数据
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Param ID query string true "版本ID"
// @Success 200 {object} response.Response{data=object,msg=string} "下载成功"
// @Router /sysVersion/downloadVersionJson [get]
func (sysVersionApi *SysVersionApi) DownloadVersionJson(c *gin.Context) {
ctx := c.Request.Context()
ID := c.Query("ID")
if ID == "" {
response.FailWithMessage("版本ID不能为空", c)
return
}
// 获取版本记录
version, err := sysVersionService.GetSysVersion(ctx, ID)
if err != nil {
global.GVA_LOG.Error("获取版本记录失败!", zap.Error(err))
response.FailWithMessage("获取版本记录失败:"+err.Error(), c)
return
}
// 构建JSON数据
var jsonData []byte
if version.VersionData != nil && *version.VersionData != "" {
jsonData = []byte(*version.VersionData)
} else {
// 如果没有存储的JSON数据,构建一个基本的结构
basicData := systemRes.ExportVersionResponse{
Version: systemReq.VersionInfo{
Name: *version.VersionName,
Code: *version.VersionCode,
Description: *version.Description,
ExportTime: version.CreatedAt.Format("2006-01-02 15:04:05"),
},
Menus: []system.SysBaseMenu{},
Apis: []system.SysApi{},
}
jsonData, _ = json.MarshalIndent(basicData, "", " ")
}
// 设置下载响应头
filename := fmt.Sprintf("version_%s_%s.json", *version.VersionCode, time.Now().Format("20060102150405"))
c.Header("Content-Type", "application/json")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
c.Header("Content-Length", strconv.Itoa(len(jsonData)))
c.Data(http.StatusOK, "application/json", jsonData)
}
// ImportVersion 导入版本数据
// @Tags SysVersion
// @Summary 导入版本数据
// @Security ApiKeyAuth
// @Accept application/json
// @Produce application/json
// @Param data body systemReq.ImportVersionRequest true "版本JSON数据"
// @Success 200 {object} response.Response{msg=string} "导入成功"
// @Router /sysVersion/importVersion [post]
func (sysVersionApi *SysVersionApi) ImportVersion(c *gin.Context) {
ctx := c.Request.Context()
// 获取JSON数据
var importData systemReq.ImportVersionRequest
err := c.ShouldBindJSON(&importData)
if err != nil {
response.FailWithMessage("解析JSON数据失败:"+err.Error(), c)
return
}
// 验证数据格式
if importData.VersionInfo.Name == "" || importData.VersionInfo.Code == "" {
response.FailWithMessage("版本信息格式错误", c)
return
}
// 导入菜单数据
if len(importData.ExportMenu) > 0 {
if err := sysVersionService.ImportMenus(ctx, importData.ExportMenu); err != nil {
global.GVA_LOG.Error("导入菜单失败!", zap.Error(err))
response.FailWithMessage("导入菜单失败: "+err.Error(), c)
return
}
}
// 导入API数据
if len(importData.ExportApi) > 0 {
if err := sysVersionService.ImportApis(importData.ExportApi); err != nil {
global.GVA_LOG.Error("导入API失败!", zap.Error(err))
response.FailWithMessage("导入API失败: "+err.Error(), c)
return
}
}
// 导入字典数据
if len(importData.ExportDictionary) > 0 {
if err := sysVersionService.ImportDictionaries(importData.ExportDictionary); err != nil {
global.GVA_LOG.Error("导入字典失败!", zap.Error(err))
response.FailWithMessage("导入字典失败: "+err.Error(), c)
return
}
}
// 创建导入记录
jsonData, _ := json.Marshal(importData)
version := system.SysVersion{
VersionName: utils.Pointer(importData.VersionInfo.Name),
VersionCode: utils.Pointer(fmt.Sprintf("%s_imported_%s", importData.VersionInfo.Code, time.Now().Format("20060102150405"))),
Description: utils.Pointer(fmt.Sprintf("导入版本: %s", importData.VersionInfo.Description)),
VersionData: utils.Pointer(string(jsonData)),
}
err = sysVersionService.CreateSysVersion(ctx, &version)
if err != nil {
global.GVA_LOG.Error("保存导入记录失败!", zap.Error(err))
// 这里不返回错误,因为数据已经导入成功
}
response.OkWithMessage("导入成功", c)
}
+218 -155
View File
@@ -2,107 +2,103 @@
# jwt configuration
jwt:
signing-key: qmPlus
expires-time: 7d
buffer-time: 1d
issuer: qmPlus
signing-key: qmPlus
expires-time: 7d
buffer-time: 1d
issuer: qmPlus
# zap logger configuration
zap:
level: info
format: console
prefix: "[github.com/flipped-aurora/gin-vue-admin/server]"
director: log
show-line: true
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
log-in-console: true
level: info
format: console
prefix: "[github.com/flipped-aurora/gin-vue-admin/server]"
director: log
show-line: true
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
log-in-console: true
retention-day: -1
# redis configuration
redis:
db: 0
addr: 177.7.0.14:6379
password: ""
#是否使用redis集群模式
useCluster: false
#使用集群模式addr和db默认无效
addr: 177.7.0.14:6379
password: ""
db: 0
clusterAddrs:
- "177.7.0.14:7000"
- "177.7.0.15:7001"
- "177.7.0.13:7002"
# redis-list configuration
redis-list:
- name: cache # 数据库的名称,注意: name 需要在 redis-list 中唯一
useCluster: false # 是否使用redis集群模式
addr: 177.7.0.14:6379 # 使用集群模式addr和db默认无效
password: ""
db: 0
clusterAddrs:
- "177.7.0.14:7000"
- "177.7.0.15:7001"
- "177.7.0.13:7002"
# mongo configuration
mongo:
coll: ''
options: ''
database: ''
username: ''
password: ''
min-pool-size: 0
max-pool-size: 100
socket-timeout-ms: 0
connect-timeout-ms: 0
is-zap: false
hosts:
- host: ''
port: ''
coll: ''
options: ''
database: ''
username: ''
password: ''
auth-source: ''
min-pool-size: 0
max-pool-size: 100
socket-timeout-ms: 0
connect-timeout-ms: 0
is-zap: false
hosts:
- host: ''
port: ''
# email configuration
email:
to: xxx@qq.com
port: 465
from: xxx@163.com
host: smtp.163.com
is-ssl: true
secret: xxx
nickname: test
to: xxx@qq.com
port: 465
from: xxx@163.com
host: smtp.163.com
is-ssl: true
secret: xxx
nickname: test
# system configuration
system:
env: public # Change to "develop" to skip authentication for development mode
addr: 8888
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-mongo: false # 使用mongo
use-multipoint: false
# IP限制次数 一个小时15000次
iplimit-count: 15000
# IP限制一个小时
iplimit-time: 3600
env: local # 修改为public可以关闭路由日志输出
addr: 8888
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-mongo: false # 使用mongo
use-multipoint: false
# IP限制次数 一个小时15000次
iplimit-count: 15000
# IP限制一个小时
iplimit-time: 3600
# 路由全局前缀
router-prefix: ""
# 严格角色模式 打开后权限将会存在上下级关系
use-strict-auth: false
# captcha configuration
captcha:
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
open-captcha-timeout: 3600 # open-captcha大于0时才生效
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
open-captcha-timeout: 3600 # open-captcha大于0时才生效
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
mysql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
# pgsql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
pgsql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
db-list:
- disable: true # 是否禁用
type: "" # 数据库的类型,目前支持mysql、pgsql
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
path: ""
port: ""
config: ""
@@ -114,107 +110,174 @@ db-list:
log-mode: ""
log-zap: false
# pgsql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
pgsql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
oracle:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
mssql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
sqlite:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
db-list:
- disable: true # 是否禁用
type: "" # 数据库的类型,目前支持mysql、pgsql、mssql、oracle
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
# local configuration
local:
path: uploads/file
store-path: uploads/file
path: uploads/file
store-path: uploads/file
# autocode configuration
autocode:
transfer-restart: true
# root 自动适配项目根目录
# 请不要手动配置,他会在项目加载的时候识别出根路径
root: ""
server: /server
server-plug: /plugin/%s
server-api: /api/v1/%s
server-initialize: /initialize
server-model: /model/%s
server-request: /model/%s/request/
server-router: /router/%s
server-service: /service/%s
web: /web/src
web-api: /api
web-form: /view
web-table: /view
web: web/src
root: "" # root 自动适配项目根目录, 请不要手动配置,他会在项目加载的时候识别出根路径
server: server
module: 'github.com/flipped-aurora/gin-vue-admin/server'
ai-path: "" # AI服务路径
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
qiniu:
zone: ZoneHuaDong
bucket: ""
img-path: ""
use-https: false
access-key: ""
secret-key: ""
use-cdn-domains: false
zone: ZoneHuaDong
bucket: ""
img-path: ""
use-https: false
access-key: ""
secret-key: ""
use-cdn-domains: false
# minio oss configuration
minio:
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
use-ssl: false
base-path: ""
bucket-url: "http://host:9000/yourBucketName"
# aliyun oss configuration
aliyun-oss:
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
bucket-url: yourBucketUrl
base-path: yourBasePath
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
bucket-url: yourBucketUrl
base-path: yourBasePath
# tencent cos configuration
tencent-cos:
bucket: xxxxx-10005608
region: ap-shanghai
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
bucket: xxxxx-10005608
region: ap-shanghai
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# aws s3 configuration (minio compatible)
aws-s3:
bucket: xxxxx-10005608
region: ap-shanghai
endpoint: ""
s3-force-path-style: false
disable-ssl: false
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
bucket: xxxxx-10005608
region: ap-shanghai
endpoint: ""
s3-force-path-style: false
disable-ssl: false
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# cloudflare r2 configuration
cloudflare-r2:
bucket: xxxx0bucket
base-url: https://gin.vue.admin.com
path: uploads
account-id: xxx_account_id
access-key-id: xxx_key_id
secret-access-key: xxx_secret_key
# huawei obs configuration
hua-wei-obs:
path: you-path
bucket: you-bucket
endpoint: you-endpoint
access-key: you-access-key
secret-key: you-secret-key
path: you-path
bucket: you-bucket
endpoint: you-endpoint
access-key: you-access-key
secret-key: you-secret-key
# excel configuration
excel:
dir: ./resource/excel/
dir: ./resource/excel/
# timer task db clear table
Timer:
start: true
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
detail:
- tableName: sys_operation_records
compareField: created_at
interval: 2160h
- tableName: jwt_blacklists
compareField: created_at
interval: 168h
# disk usage configuration
disk-list:
- mount-point: "/"
# 跨域配置
# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用
cors:
mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
- allow-origin: example1.com
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 # 布尔值
- allow-origin: example2.com
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 # 布尔值
mode: strict-whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
- allow-origin: example1.com
allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id
allow-methods: POST, GET
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true # 布尔值
- allow-origin: example2.com
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 # 布尔值
mcp:
name: GVA_MCP
version: v1.0.0
sse_path: /sse
message_path: /message
url_prefix: ''
+14 -8
View File
@@ -196,13 +196,13 @@ qiniu:
# minio oss configuration
minio:
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
use-ssl: false
base-path: ""
bucket-url: "http://host:9000/yourBucketName"
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
use-ssl: false
base-path: ""
bucket-url: "http://host:9000/yourBucketName"
# aliyun oss configuration
aliyun-oss:
@@ -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 # 布尔值
allow-credentials: true # 布尔值
mcp:
name: GVA_MCP
version: v1.0.0
sse_path: /sse
message_path: /message
url_prefix: ''
+1 -1
View File
@@ -4,6 +4,6 @@ type Captcha struct {
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码次数,如3代表错误三次后出现验证码
OpenCaptchaTimeOut int `mapstructure:"open-captcha-timeout" json:"open-captcha-timeout" yaml:"open-captcha-timeout"` // 防爆破验证码超时时间,单位:s(秒)
}
+3
View File
@@ -34,4 +34,7 @@ type Server struct {
// 跨域配置
Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
// MCP配置
MCP MCP `mapstructure:"mcp" json:"mcp" yaml:"mcp"`
}
+6 -5
View File
@@ -1,8 +1,9 @@
package config
import (
"gorm.io/gorm/logger"
"strings"
"gorm.io/gorm/logger"
)
type DsnProvider interface {
@@ -31,13 +32,13 @@ type GeneralDB struct {
func (c GeneralDB) LogLevel() logger.LogLevel {
switch strings.ToLower(c.LogMode) {
case "silent", "Silent":
case "silent":
return logger.Silent
case "error", "Error":
case "error":
return logger.Error
case "warn", "Warn":
case "warn":
return logger.Warn
case "info", "Info":
case "info":
return logger.Info
default:
return logger.Info
+8 -7
View File
@@ -1,11 +1,12 @@
package config
type Email struct {
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL
IsLoginAuth bool `mapstructure:"is-loginauth" json:"is-loginauth" yaml:"is-loginauth"` // 是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等)
}
+9 -1
View File
@@ -1,10 +1,18 @@
package config
import (
"fmt"
"net"
"net/url"
)
type Oracle struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
func (m *Oracle) Dsn() string {
return "oracle://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "/" + m.Dbname + "?" + m.Config
dsn := fmt.Sprintf("oracle://%s:%s@%s/%s?%s", url.PathEscape(m.Username), url.PathEscape(m.Password),
net.JoinHostPort(m.Path, m.Port), url.PathEscape(m.Dbname), m.Config)
return dsn
}
+9
View File
@@ -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前缀
}
+13 -14
View File
@@ -6,17 +6,16 @@ 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() {
if global.GVA_CONFIG.System.UseMultipoint || global.GVA_CONFIG.System.UseRedis {
func RunServer() {
if global.GVA_CONFIG.System.UseRedis {
// 初始化redis服务
initialize.Redis()
initialize.RedisList()
if global.GVA_CONFIG.System.UseMultipoint {
initialize.RedisList()
}
}
if global.GVA_CONFIG.System.UseMongo {
@@ -33,23 +32,23 @@ 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.7.7
加群方式:微信号:shouzi_1994 QQ群:470239250
当前版本:%s
加群方式:微信号: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())
** 感谢您对Gin-Vue-Admin的支持与关注 合法授权使用更有利于项目的长久发展**
`, global.Version, address, address, global.GVA_CONFIG.MCP.SSEPath, address, global.GVA_CONFIG.MCP.MessagePath)
initServer(address, Router, 10*time.Minute, 10*time.Minute)
}
-19
View File
@@ -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
}
+60
View File
@@ -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服务已关闭")
}
-21
View File
@@ -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,
}
}
+43 -38
View File
@@ -3,55 +3,26 @@ package core
import (
"flag"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
"github.com/gin-gonic/gin"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/fsnotify/fsnotify"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
// Viper //
// 优先级: 命令行 > 环境变量 > 默认值
// Author [SliverHorn](https://github.com/SliverHorn)
func Viper(path ...string) *viper.Viper {
var config string
if len(path) == 0 {
flag.StringVar(&config, "c", "", "choose config file.")
flag.Parse()
if config == "" { // 判断命令行参数是否为空
if configEnv := os.Getenv(internal.ConfigEnv); configEnv == "" { // 判断 internal.ConfigEnv 常量存储的环境变量是否为空
switch gin.Mode() {
case gin.DebugMode:
config = internal.ConfigDefaultFile
case gin.ReleaseMode:
config = internal.ConfigReleaseFile
case gin.TestMode:
config = internal.ConfigTestFile
}
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), config)
} else { // internal.ConfigEnv 常量存储的环境变量不为空 将值赋值于config
config = configEnv
fmt.Printf("您正在使用%s环境变量,config的路径为%s\n", internal.ConfigEnv, config)
}
} else { // 命令行参数不为空 将值赋值于config
fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%s\n", config)
}
} else { // 函数传递的可变参数的第一个值赋值于config
config = path[0]
fmt.Printf("您正在使用func Viper()传递的值,config的路径为%s\n", config)
}
// Viper 配置
func Viper() *viper.Viper {
config := getConfigPath()
v := viper.New()
v.SetConfigFile(config)
v.SetConfigType("yaml")
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
panic(fmt.Errorf("fatal error config file: %w", err))
}
v.WatchConfig()
@@ -62,10 +33,44 @@ func Viper(path ...string) *viper.Viper {
}
})
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
panic(err)
panic(fmt.Errorf("fatal error unmarshal config: %w", err))
}
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
return v
}
// getConfigPath 获取配置文件路径, 优先级: 命令行 > 环境变量 > 默认值
func getConfigPath() (config string) {
// `-c` flag parse
flag.StringVar(&config, "c", "", "choose config file.")
flag.Parse()
if config != "" { // 命令行参数不为空 将值赋值于config
fmt.Printf("您正在使用命令行的 '-c' 参数传递的值, config 的路径为 %s\n", config)
return
}
if env := os.Getenv(internal.ConfigEnv); env != "" { // 判断环境变量 GVA_CONFIG
config = env
fmt.Printf("您正在使用 %s 环境变量, config 的路径为 %s\n", internal.ConfigEnv, config)
return
}
switch gin.Mode() { // 根据 gin 模式文件名
case gin.DebugMode:
config = internal.ConfigDebugFile
case gin.ReleaseMode:
config = internal.ConfigReleaseFile
case gin.TestMode:
config = internal.ConfigTestFile
}
fmt.Printf("您正在使用 gin 的 %s 模式运行, config 的路径为 %s\n", gin.Mode(), config)
_, err := os.Stat(config)
if err != nil || os.IsNotExist(err) {
config = internal.ConfigDefaultFile
fmt.Printf("配置文件路径不存在, 使用默认配置文件路径: %s\n", config)
}
return
}
+1234 -24
View File
File diff suppressed because it is too large Load Diff
+1228 -20
View File
File diff suppressed because it is too large Load Diff
+766 -16
View File
@@ -1,4 +1,7 @@
definitions:
common.JSONMap:
additionalProperties: true
type: object
config.AliyunOSS:
properties:
access-key-id:
@@ -150,6 +153,23 @@ definitions:
description: 本地文件存储路径
type: string
type: object
config.Minio:
properties:
access-key-id:
type: string
access-key-secret:
type: string
base-path:
type: string
bucket-name:
type: string
bucket-url:
type: string
endpoint:
type: string
use-ssl:
type: boolean
type: object
config.Mongo:
properties:
auth-source:
@@ -409,6 +429,9 @@ definitions:
db:
description: 单实例模式下redis的哪个数据库
type: integer
name:
description: 代表当前实例的名字
type: string
password:
description: 密码
type: string
@@ -454,6 +477,8 @@ definitions:
allOf:
- $ref: '#/definitions/config.Local'
description: oss
minio:
$ref: '#/definitions/config.Minio'
mongo:
$ref: '#/definitions/config.Mongo'
mssql:
@@ -470,6 +495,10 @@ definitions:
$ref: '#/definitions/config.Qiniu'
redis:
$ref: '#/definitions/config.Redis'
redis-list:
items:
$ref: '#/definitions/config.Redis'
type: array
sqlite:
$ref: '#/definitions/config.Sqlite'
system:
@@ -597,6 +626,9 @@ definitions:
use-redis:
description: 使用redis
type: boolean
use-strict-auth:
description: 使用树形角色分配模式
type: boolean
type: object
config.TencentCOS:
properties:
@@ -643,6 +675,26 @@ definitions:
description: 栈名
type: string
type: object
example.ExaAttachmentCategory:
properties:
ID:
description: 主键ID
type: integer
children:
items:
$ref: '#/definitions/example.ExaAttachmentCategory'
type: array
createdAt:
description: 创建时间
type: string
name:
type: string
pid:
type: integer
updatedAt:
description: 更新时间
type: string
type: object
example.ExaCustomer:
properties:
ID:
@@ -720,6 +772,9 @@ definitions:
ID:
description: 主键ID
type: integer
classId:
description: 分类id
type: integer
createdAt:
description: 创建时间
type: string
@@ -800,6 +855,164 @@ definitions:
type: array
type: object
request.AutoCode:
properties:
abbreviation:
description: Struct简称
example: Struct简称
type: string
autoCreateApiToSql:
description: 是否自动创建api
example: false
type: boolean
autoCreateBtnAuth:
description: 是否自动创建按钮权限
example: false
type: boolean
autoCreateMenuToSql:
description: 是否自动创建menu
example: false
type: boolean
autoCreateResource:
description: 是否自动创建资源标识
example: false
type: boolean
autoMigrate:
description: 是否自动迁移表结构
example: false
type: boolean
businessDB:
description: 业务数据库
example: 业务数据库
type: string
description:
description: Struct中文名称
example: Struct中文名称
type: string
fields:
items:
$ref: '#/definitions/request.AutoCodeField'
type: array
generateServer:
description: 是否生成server
example: true
type: boolean
generateWeb:
description: 是否生成web
example: true
type: boolean
gvaModel:
description: 是否使用gva默认Model
example: false
type: boolean
humpPackageName:
description: go文件名称
example: go文件名称
type: string
isAdd:
description: 是否新增
example: false
type: boolean
isTree:
description: 是否树形结构
example: false
type: boolean
onlyTemplate:
description: 是否只生成模板
example: false
type: boolean
package:
type: string
packageName:
description: 文件名称
example: 文件名称
type: string
primaryField:
$ref: '#/definitions/request.AutoCodeField'
structName:
description: Struct名称
example: Struct名称
type: string
tableName:
description: 表名
example: 表名
type: string
treeJson:
description: 展示的树json字段
example: 展示的树json字段
type: string
type: object
request.AutoCodeField:
properties:
checkDataSource:
description: 是否检查数据源
type: boolean
clearable:
description: 是否可清空
type: boolean
columnName:
description: 数据库字段
type: string
comment:
description: 数据库字段描述
type: string
dataSource:
allOf:
- $ref: '#/definitions/request.DataSource'
description: 数据源
dataTypeLong:
description: 数据库字段长度
type: string
defaultValue:
description: 是否必填
type: string
desc:
description: 是否前端详情
type: boolean
dictType:
description: 字典
type: string
errorText:
description: 校验失败文字
type: string
excel:
description: 是否导入/导出
type: boolean
fieldDesc:
description: 中文名
type: string
fieldIndexType:
description: 索引类型
type: string
fieldJson:
description: FieldJson
type: string
fieldName:
description: Field名
type: string
fieldSearchHide:
description: 是否隐藏查询条件
type: boolean
fieldSearchType:
description: 搜索条件
type: string
fieldType:
description: Field数据类型
type: string
form:
description: Front bool `json:"front"` // 是否前端可见
type: boolean
primaryKey:
description: 是否主键
type: boolean
require:
description: 是否必填
type: boolean
sort:
description: 是否增加排序
type: boolean
table:
description: 是否前端表格列
type: boolean
type: object
request.CasbinInReceive:
properties:
@@ -829,8 +1042,38 @@ definitions:
description: 密码
type: string
type: object
request.DataSource:
properties:
association:
description: 关联关系 1 一对一 2 一对多
type: integer
dbName:
type: string
hasDeletedAt:
type: boolean
label:
type: string
table:
type: string
value:
type: string
type: object
request.Empty:
type: object
request.ExaAttachmentCategorySearch:
properties:
classId:
type: integer
keyword:
description: 关键字
type: string
page:
description: 页码
type: integer
pageSize:
description: 每页大小
type: integer
type: object
request.GetAuthorityId:
properties:
authorityId:
@@ -843,6 +1086,26 @@ definitions:
description: 主键ID
type: integer
type: object
request.GetUserList:
properties:
email:
type: string
keyword:
description: 关键字
type: string
nickName:
type: string
page:
description: 页码
type: integer
pageSize:
description: 每页大小
type: integer
phone:
type: string
username:
type: string
type: object
request.IdsReq:
properties:
ids:
@@ -872,6 +1135,9 @@ definitions:
port:
description: 数据库连接端口
type: string
template:
description: postgresql指定template
type: string
userName:
description: 数据库用户名
type: string
@@ -1577,23 +1843,51 @@ definitions:
description: 用户id
type: integer
type: object
system.SysParams:
properties:
ID:
description: 主键ID
type: integer
createdAt:
description: 创建时间
type: string
desc:
description: 参数说明
type: string
key:
description: 参数键
type: string
name:
description: 参数名称
type: string
updatedAt:
description: 更新时间
type: string
value:
description: 参数值
type: string
required:
- key
- name
- value
type: object
system.SysUser:
properties:
ID:
description: 主键ID
type: integer
authorities:
description: 多用户角色
items:
$ref: '#/definitions/system.SysAuthority'
type: array
authority:
$ref: '#/definitions/system.SysAuthority'
allOf:
- $ref: '#/definitions/system.SysAuthority'
description: 用户角色
authorityId:
description: 用户角色ID
type: integer
baseColor:
description: 基础颜色
type: string
createdAt:
description: 创建时间
type: string
@@ -1609,12 +1903,13 @@ definitions:
nickName:
description: 用户昵称
type: string
originSetting:
allOf:
- $ref: '#/definitions/common.JSONMap'
description: 配置
phone:
description: 用户手机号
type: string
sideMode:
description: 用户侧边主题
type: string
updatedAt:
description: 更新时间
type: string
@@ -1634,7 +1929,7 @@ info:
contact: {}
description: 使用gin+vue进行极速开发的全栈开发基础平台
title: Gin-Vue-Admin Swagger API接口文档
version: v2.7.7
version: v2.7.9-beta
paths:
/api/createApi:
post:
@@ -1812,7 +2107,7 @@ paths:
tags:
- SysApi
/api/getApiGroups:
post:
get:
consumes:
- application/json
produces:
@@ -1932,6 +2227,74 @@ paths:
summary: 修改基础api
tags:
- SysApi
/attachmentCategory/addCategory:
post:
consumes:
- application/json
parameters:
- description: 媒体库分类数据
in: body
name: data
required: true
schema:
$ref: '#/definitions/example.ExaAttachmentCategory'
produces:
- application/json
responses: {}
security:
- AttachmentCategory: []
summary: 添加媒体库分类
tags:
- AddCategory
/attachmentCategory/deleteCategory:
post:
consumes:
- application/json
parameters:
- description: 分类id
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.GetById'
produces:
- application/json
responses:
"200":
description: 删除分类
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- AttachmentCategory: []
summary: 删除分类
tags:
- DeleteCategory
/attachmentCategory/getCategoryList:
get:
produces:
- application/json
responses:
"200":
description: 媒体库分类列表
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/example.ExaAttachmentCategory'
msg:
type: string
type: object
security:
- AttachmentCategory: []
summary: 媒体库分类列表
tags:
- GetCategoryList
/authority/copyAuthority:
post:
consumes:
@@ -2079,7 +2442,7 @@ paths:
tags:
- Authority
/authority/updateAuthority:
post:
put:
consumes:
- application/json
parameters:
@@ -2347,7 +2710,7 @@ paths:
summary: 获取当前表所有字段
tags:
- AutoCode
/autoCode/getDatabase:
/autoCode/getDB:
get:
consumes:
- application/json
@@ -2504,6 +2867,54 @@ paths:
summary: 获取package
tags:
- AutoCodePackage
/autoCode/initAPI:
post:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 打包插件成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
additionalProperties: true
type: object
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 打包插件
tags:
- AutoCodePlugin
/autoCode/initMenu:
post:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 打包插件成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
additionalProperties: true
type: object
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 打包插件
tags:
- AutoCodePlugin
/autoCode/installPlugin:
post:
consumes:
@@ -2567,7 +2978,7 @@ paths:
tags:
- AutoCodeTemplate
/autoCode/pubPlug:
get:
post:
consumes:
- application/json
parameters:
@@ -2989,6 +3400,34 @@ paths:
tags:
- ExaFileUploadAndDownload
/fileUploadAndDownload/findFile:
get:
consumes:
- multipart/form-data
parameters:
- description: Find the file, 查找文件
in: formData
name: file
required: true
type: file
produces:
- application/json
responses:
"200":
description: 查找文件,返回包括文件详情
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.FileResponse'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 查找文件
tags:
- ExaFileUploadAndDownload
post:
consumes:
- multipart/form-data
@@ -3022,12 +3461,12 @@ paths:
consumes:
- application/json
parameters:
- description: 页码, 每页大小
- description: 页码, 每页大小, 分类id
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.PageInfo'
$ref: '#/definitions/request.ExaAttachmentCategorySearch'
produces:
- application/json
responses:
@@ -3047,6 +3486,32 @@ paths:
summary: 分页文件列表
tags:
- ExaFileUploadAndDownload
/fileUploadAndDownload/importURL:
post:
parameters:
- description: 对象
in: body
name: data
required: true
schema:
$ref: '#/definitions/example.ExaFileUploadAndDownload'
produces:
- application/json
responses:
"200":
description: 导入URL
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 导入URL
tags:
- ExaFileUploadAndDownload
/fileUploadAndDownload/removeChunk:
post:
consumes:
@@ -4060,6 +4525,18 @@ paths:
summary: 更新SysDictionaryDetail
tags:
- SysDictionaryDetail
/sysExportTemplate/ExportTemplate:
get:
consumes:
- application/json
produces:
- application/json
responses: {}
security:
- ApiKeyAuth: []
summary: 导出表格模板
tags:
- SysExportTemplate
/sysExportTemplate/createSysExportTemplate:
post:
consumes:
@@ -4138,7 +4615,7 @@ paths:
responses: {}
security:
- ApiKeyAuth: []
summary: 导出表格模板
summary: 导出表格
tags:
- SysExportTemplate
/sysExportTemplate/findSysExportTemplate:
@@ -4553,6 +5030,243 @@ paths:
summary: 分页获取SysOperationRecord列表
tags:
- SysOperationRecord
/sysParams/createSysParams:
post:
consumes:
- application/json
parameters:
- description: 创建参数
in: body
name: data
required: true
schema:
$ref: '#/definitions/system.SysParams'
produces:
- application/json
responses:
"200":
description: 创建成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 创建参数
tags:
- SysParams
/sysParams/deleteSysParams:
delete:
consumes:
- application/json
parameters:
- description: 删除参数
in: body
name: data
required: true
schema:
$ref: '#/definitions/system.SysParams'
produces:
- application/json
responses:
"200":
description: 删除成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 删除参数
tags:
- SysParams
/sysParams/deleteSysParamsByIds:
delete:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 批量删除成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 批量删除参数
tags:
- SysParams
/sysParams/findSysParams:
get:
consumes:
- application/json
parameters:
- description: 主键ID
in: query
name: ID
type: integer
- description: 创建时间
in: query
name: createdAt
type: string
- description: 参数说明
in: query
name: desc
type: string
- description: 参数键
in: query
name: key
required: true
type: string
- description: 参数名称
in: query
name: name
required: true
type: string
- description: 更新时间
in: query
name: updatedAt
type: string
- description: 参数值
in: query
name: value
required: true
type: string
produces:
- application/json
responses:
"200":
description: 查询成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/system.SysParams'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 用id查询参数
tags:
- SysParams
/sysParams/getSysParam:
get:
consumes:
- application/json
parameters:
- description: key
in: query
name: key
required: true
type: string
produces:
- application/json
responses:
"200":
description: 获取成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/system.SysParams'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 根据key获取参数value
tags:
- SysParams
/sysParams/getSysParamsList:
get:
consumes:
- application/json
parameters:
- in: query
name: endCreatedAt
type: string
- in: query
name: key
type: string
- description: 关键字
in: query
name: keyword
type: string
- in: query
name: name
type: string
- description: 页码
in: query
name: page
type: integer
- description: 每页大小
in: query
name: pageSize
type: integer
- in: query
name: startCreatedAt
type: string
produces:
- application/json
responses:
"200":
description: 获取成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.PageResult'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 分页获取参数列表
tags:
- SysParams
/sysParams/updateSysParams:
put:
consumes:
- application/json
parameters:
- description: 更新参数
in: body
name: data
required: true
schema:
$ref: '#/definitions/system.SysParams'
produces:
- application/json
responses:
"200":
description: 更新成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 更新参数
tags:
- SysParams
/system/getServerInfo:
post:
produces:
@@ -4672,6 +5386,38 @@ paths:
summary: 设置用户信息
tags:
- SysUser
/user/SetSelfSetting:
put:
consumes:
- application/json
parameters:
- description: 用户配置数据
in: body
name: data
required: true
schema:
additionalProperties: true
type: object
produces:
- application/json
responses:
"200":
description: 设置用户配置
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
additionalProperties: true
type: object
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 设置用户配置
tags:
- SysUser
/user/admin_register:
post:
parameters:
@@ -4786,7 +5532,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/request.PageInfo'
$ref: '#/definitions/request.GetUserList'
produces:
- application/json
responses:
@@ -4925,3 +5671,7 @@ securityDefinitions:
name: x-token
type: apiKey
swagger: "2.0"
tags:
- name: Base
- description: 用户
name: SysUser
+2
View File
@@ -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
)
+12
View File
@@ -0,0 +1,12 @@
package global
// Version 版本信息
// 目前只有Version正式使用 其余为预留
const (
// Version 当前版本号
Version = "v2.8.5"
// AppName 应用名称
AppName = "Gin-Vue-Admin"
// Description 应用描述
Description = "使用gin+vue进行极速开发的全栈开发基础平台"
)
+72 -69
View File
@@ -1,54 +1,55 @@
module github.com/flipped-aurora/gin-vue-admin/server
go 1.22.0
go 1.23
toolchain go1.22.2
toolchain go1.23.9
require (
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/aws/aws-sdk-go v1.55.5
github.com/casbin/casbin/v2 v2.100.0
github.com/casbin/gorm-adapter/v3 v3.28.0
github.com/fsnotify/fsnotify v1.7.0
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
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/dzwvip/gorm-oracle v0.1.2
github.com/fsnotify/fsnotify v1.8.0
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
github.com/go-sql-driver/mysql v1.8.1
github.com/goccy/go-json v0.10.3
github.com/gofrs/uuid/v5 v5.3.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/goccy/go-json v0.10.4
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/minio/minio-go/v7 v7.0.78
github.com/mojocn/base64Captcha v1.3.6
github.com/otiai10/copy v1.14.0
github.com/mark3labs/mcp-go v0.31.0
github.com/mholt/archives v0.1.1
github.com/minio/minio-go/v7 v7.0.84
github.com/mojocn/base64Captcha v1.3.8
github.com/otiai10/copy v1.14.1
github.com/pkg/errors v0.9.1
github.com/qiniu/go-sdk/v7 v7.23.0
github.com/qiniu/qmgo v1.1.8
github.com/redis/go-redis/v9 v9.6.2
github.com/qiniu/go-sdk/v7 v7.25.2
github.com/qiniu/qmgo v1.1.9
github.com/redis/go-redis/v9 v9.7.0
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/songzhibin97/gkit v1.2.13
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
github.com/tencentyun/cos-go-sdk-v5 v0.7.55
github.com/unrolled/secure v1.16.0
github.com/swaggo/swag v1.16.4
github.com/tencentyun/cos-go-sdk-v5 v0.7.60
github.com/unrolled/secure v1.17.0
github.com/xuri/excelize/v2 v2.9.0
go.mongodb.org/mongo-driver v1.17.1
go.mongodb.org/mongo-driver v1.17.2
go.uber.org/automaxprocs v1.6.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.28.0
golang.org/x/sync v0.8.0
golang.org/x/text v0.19.0
gorm.io/datatypes v1.2.3
golang.org/x/crypto v0.32.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
gorm.io/datatypes v1.2.5
gorm.io/driver/mysql v1.5.7
gorm.io/driver/postgres v1.5.9
gorm.io/driver/sqlserver v1.5.3
gorm.io/driver/postgres v1.5.11
gorm.io/driver/sqlserver v1.5.4
gorm.io/gen v0.3.26
gorm.io/gorm v1.25.12
)
@@ -57,28 +58,27 @@ 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.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.7.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.5.2 // indirect
github.com/bodgit/sevenzip v1.6.0 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/bytedance/sonic v1.12.3 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/casbin/govaluate v1.2.0 // indirect
github.com/bytedance/sonic v1.12.7 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/casbin/govaluate v1.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elastic/go-sysinfo v1.14.2 // indirect
github.com/elastic/go-windows v1.0.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gammazero/toposort v0.1.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -88,21 +88,20 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/pgx/v5 v5.7.2 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
@@ -110,15 +109,16 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/microsoft/go-mssqldb v1.7.2 // 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
@@ -126,25 +126,28 @@ 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.0-beta.3 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // 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.21 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sijms/go-ora/v2 v2.7.17 // indirect
github.com/sorairolake/lzip-go v0.3.5 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
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/thoas/go-funk v0.7.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
@@ -154,29 +157,29 @@ require (
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // 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
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/image v0.21.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
golang.org/x/arch v0.13.0 // indirect
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
golang.org/x/image v0.23.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/protobuf v1.36.3 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/hints v1.1.2 // indirect
gorm.io/plugin/dbresolver v1.5.3 // indirect
howett.net/plist v1.0.1 // indirect
modernc.org/fileutil v1.3.0 // indirect
modernc.org/libc v1.61.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.33.1 // indirect
modernc.org/libc v1.61.9 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.34.5 // indirect
)
+215 -187
View File
@@ -17,30 +17,27 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
@@ -49,38 +46,40 @@ 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.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
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=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ=
github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.5.2 h1:acMIYRaqoHAdeu9LhEGGjL9UzBD4RNf9z7+kWDNignI=
github.com/bodgit/sevenzip v1.5.2/go.mod h1:gTGzXA67Yko6/HLSD0iK4kWaWzPlPmLfDO73jTjSRqc=
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q=
github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/casbin/casbin/v2 v2.100.0 h1:aeugSNjjHfCrgA22nHkVvw2xsscboHv5r0a13ljQKGQ=
github.com/casbin/casbin/v2 v2.100.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng=
github.com/casbin/gorm-adapter/v3 v3.28.0 h1:ORF8prF6SfaipdgT1fud+r1Tp5J0uul8QaKJHqCPY/o=
github.com/casbin/gorm-adapter/v3 v3.28.0/go.mod h1:aftWi0cla0CC1bHQVrSFzBcX/98IFK28AvuPppCQgTs=
github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYak=
github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/casbin/casbin/v2 v2.103.0 h1:dHElatNXNrr8XcseUov0ZSiWjauwmZZE6YMV3eU1yic=
github.com/casbin/casbin/v2 v2.103.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wnNUnS0edAco=
github.com/casbin/gorm-adapter/v3 v3.32.0 h1:Au+IOILBIE9clox5BJhI2nA3p9t7Ep1ePlupdGbGfus=
github.com/casbin/gorm-adapter/v3 v3.32.0/go.mod h1:Zre/H8p17mpv5U3EaWgPoxLILLdXO3gHW5aoQQpUDZI=
github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc=
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -90,9 +89,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
@@ -104,33 +102,29 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elastic/go-sysinfo v1.0.2/go.mod h1:O/D5m1VpYLwGjCYzEt63g3Z1uO3jXfwyzzjiW90t8cY=
github.com/elastic/go-sysinfo v1.14.2 h1:DeIy+pVfdRsd08Nx2Xjh+dUS+jrEEI7LGc29U/BKVWo=
github.com/elastic/go-sysinfo v1.14.2/go.mod h1:jPSuTgXG+dhhh0GKIyI2Cso+w5lPJ5PvVqKlL8LV/Hk=
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI=
github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8=
github.com/dzwvip/gorm-oracle v0.1.2 h1:811aFDY7oDfKWHc0Z0lHdXzzr89EmKBSwc/jLJ8GU5g=
github.com/dzwvip/gorm-oracle v0.1.2/go.mod h1:TbF7idnO9UgGpJ0qJpDZby1/wGquzP5GYof88ScBITE=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
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/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=
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
@@ -165,24 +159,21 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
@@ -203,7 +194,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -213,6 +203,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -228,6 +219,7 @@ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwg
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -259,8 +251,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
@@ -272,13 +264,13 @@ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJk
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -295,8 +287,8 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@@ -319,25 +311,28 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
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.31.0 h1:4UxSV8aM770OPmTvaVe/b1rA2oZAjBMhGBfUgOGut+4=
github.com/mark3labs/mcp-go v0.31.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=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/mholt/archives v0.1.1 h1:c7J3qXN1FB54y0qiUXiq9Bxk4eCUc8pdXWwOhZdRzeY=
github.com/mholt/archives v0.1.1/go.mod h1:FQVz01Q2uXKB/35CXeW/QFO23xT+hSCGZHVtha78U4I=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
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.78 h1:LqW2zy52fxnI4gg8C2oZviTaKHcBV36scS+RzJnxUFs=
github.com/minio/minio-go/v7 v7.0.78/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
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=
@@ -349,9 +344,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw=
github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg=
github.com/mojocn/base64Captcha v1.3.8/go.mod h1:QFZy927L8HVP3+VV5z2b1EAEiv1KxVJKZbAucVgLUy4=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
@@ -360,21 +354,20 @@ 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.0-beta.3 h1:evQTW0IjM2GAL5AaPHiQrT+laWohkt5zHKA3yCsGQGU=
github.com/nwaples/rardecode/v2 v2.0.0-beta.3/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
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/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=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -385,17 +378,14 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:Om
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
github.com/qiniu/go-sdk/v7 v7.23.0 h1:4wYB4EGE6MBhvjtE/FZH/mIUt/VH6WjzBucU3VfPwhg=
github.com/qiniu/go-sdk/v7 v7.23.0/go.mod h1:OXsAVU5YrLLtVi4iPFpP80jzb3SRBAczrGkcqQmWhcY=
github.com/qiniu/qmgo v1.1.8 h1:E64M+P59aqQpXKI24ClVtluYkLaJLkkeD2hTVhrdMks=
github.com/qiniu/qmgo v1.1.8/go.mod h1:QvZkzWNEv0buWPx0kdZsSs6URhESVubacxFPlITmvB8=
github.com/qiniu/go-sdk/v7 v7.25.2 h1:URwgZpxySdiwu2yQpHk93X4LXWHyFRp1x3Vmlk/YWvo=
github.com/qiniu/go-sdk/v7 v7.25.2/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o=
github.com/qiniu/qmgo v1.1.9 h1:3G3h9RLyjIUW9YSAQEPP2WqqNnboZ2Z/zO3mugjVb3E=
github.com/qiniu/qmgo v1.1.9/go.mod h1:aba4tNSlMWrwUhe7RdILfwBRIgvBujt1y10X+T1YZSI=
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk=
github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
@@ -413,8 +403,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
@@ -423,14 +413,18 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sijms/go-ora/v2 v2.7.17 h1:M/pYIqjaMUeBxyzOWp2oj4ntF6fHSBloJWGNH9vbmsU=
github.com/sijms/go-ora/v2 v2.7.17/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
github.com/songzhibin97/gkit v1.2.13 h1:paY0XJkdRuy9/8k9nTnbdrzo8pC22jIIFldUkOQv5nU=
github.com/songzhibin97/gkit v1.2.13/go.mod h1:38CreNR27eTGaG1UMGihrXqI4xc3nGfYxLVKKVx6Ngg=
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
@@ -447,25 +441,25 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.55 h1:9DfH3umWUd0I2jdqcUxrU1kLfUPOydULNy4T9qN5PF8=
github.com/tencentyun/cos-go-sdk-v5 v0.7.55/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.60 h1:/e/tmvRmfKexr/QQIBzWhOkZWsmY3EK72NrI6G/Tv0o=
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/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y=
github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
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=
@@ -474,38 +468,37 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unrolled/secure v1.16.0 h1:XgdAsS/Zl50ZfZPRJK6WpicFttfrsFYFd0+ONDBJubU=
github.com/unrolled/secure v1.16.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU=
github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY=
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 h1:8m6DWBG+dlFNbx5ynvrE7NgI+Y7OlZVMVTpayoW+rCc=
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE=
github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 h1:hOh7aVDrvGJRxzXrQbDY8E+02oaI//5cHL+97oYpEPw=
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/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
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=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -520,21 +513,28 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -543,13 +543,12 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -565,11 +564,13 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -584,17 +585,22 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -606,16 +612,18 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -625,10 +633,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -637,15 +643,34 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -657,15 +682,18 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -692,8 +720,10 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -730,8 +760,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -739,7 +769,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -749,18 +778,19 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.3 h1:95ucr9ip9dZMPhB3Tc9zbcoAi62hxYAgHicu7SLjK4g=
gorm.io/datatypes v1.2.3/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI=
gorm.io/datatypes v1.2.5 h1:9UogU3jkydFVW1bIVVeoYsTpLRgwDVW3rHfJG6/Ek9I=
gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0=
gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00=
gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g=
gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g=
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=
gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
@@ -776,31 +806,29 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00=
modernc.org/ccgo/v4 v4.23.13/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0=
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE=
modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8=
modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.9 h1:PLSBXVkifXGELtJ5BOnBUyAHr7lsatNwFU/RRo4kfJM=
modernc.org/libc v1.61.9/go.mod h1:61xrnzk/aR8gr5bR7Uj/lLFLuXu2/zMpIjcry63Eumk=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+9 -2
View File
@@ -5,6 +5,7 @@ import (
adapter "github.com/casbin/gorm-adapter/v3"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/announcement/model"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"gorm.io/gorm"
)
@@ -18,7 +19,7 @@ func init() {
system.RegisterInit(initOrderEnsureTables, &ensureTables{})
}
func (ensureTables) InitializerName() string {
func (e *ensureTables) InitializerName() string {
return "ensure_tables_created"
}
func (e *ensureTables) InitializeData(ctx context.Context) (next context.Context, err error) {
@@ -52,13 +53,16 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
sysModel.Condition{},
sysModel.JoinTemplate{},
sysModel.SysParams{},
sysModel.SysVersion{},
adapter.CasbinRule{},
example.ExaFile{},
example.ExaCustomer{},
example.ExaFileChunk{},
example.ExaFileUploadAndDownload{},
example.ExaAttachmentCategory{},
model.Info{},
}
for _, t := range tables {
_ = db.AutoMigrate(&t)
@@ -97,6 +101,9 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
example.ExaCustomer{},
example.ExaFileChunk{},
example.ExaFileUploadAndDownload{},
example.ExaAttachmentCategory{},
model.Info{},
}
yes := true
for _, t := range tables {
+2
View File
@@ -56,11 +56,13 @@ func RegisterTables() {
system.Condition{},
system.JoinTemplate{},
system.SysParams{},
system.SysVersion{},
example.ExaFile{},
example.ExaCustomer{},
example.ExaFileChunk{},
example.ExaFileUploadAndDownload{},
example.ExaAttachmentCategory{},
)
if err != nil {
global.GVA_LOG.Error("register table failed", zap.Error(err))
+10 -5
View File
@@ -1,3 +1,5 @@
package initialize
/*
* @Author: 逆光飞翔 191180776@qq.com
* @Date: 2022-12-08 17:25:49
@@ -6,7 +8,6 @@
* @FilePath: \server\initialize\gorm_mssql.go
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
package initialize
import (
"github.com/flipped-aurora/gin-vue-admin/server/config"
@@ -27,7 +28,9 @@ func GormMssql() *gorm.DB {
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
// 数据库配置
general := m.GeneralDB
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(general)); err != nil {
return nil
} else {
db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine)
@@ -44,10 +47,12 @@ func GormMssqlByConfig(m config.Mssql) *gorm.DB {
return nil
}
mssqlConfig := sqlserver.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
// 数据库配置
general := m.GeneralDB
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(general)); err != nil {
panic(err)
} else {
db.InstanceSet("gorm:table_options", "ENGINE=InnoDB")
+16 -23
View File
@@ -12,18 +12,32 @@ import (
// GormMysql 初始化Mysql数据库
// Author [piexlmax](https://github.com/piexlmax)
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [ByteZhou-2018](https://github.com/ByteZhou-2018)
func GormMysql() *gorm.DB {
m := global.GVA_CONFIG.Mysql
return initMysqlDatabase(m)
}
// GormMysqlByConfig 通过传入配置初始化Mysql数据库
func GormMysqlByConfig(m config.Mysql) *gorm.DB {
return initMysqlDatabase(m)
}
// initMysqlDatabase 初始化Mysql数据库的辅助函数
func initMysqlDatabase(m config.Mysql) *gorm.DB {
if m.Dbname == "" {
return nil
}
mysqlConfig := mysql.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
SkipInitializeWithVersion: false, // 根据版本自动配置
}
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
return nil
// 数据库配置
general := m.GeneralDB
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(general)); err != nil {
panic(err)
} else {
db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine)
sqlDB, _ := db.DB()
@@ -32,24 +46,3 @@ func GormMysql() *gorm.DB {
return db
}
}
// GormMysqlByConfig 初始化Mysql数据库用过传入配置
func GormMysqlByConfig(m config.Mysql) *gorm.DB {
if m.Dbname == "" {
return nil
}
mysqlConfig := mysql.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
SkipInitializeWithVersion: false, // 根据版本自动配置
}
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
panic(err)
} else {
db.InstanceSet("gorm:table_options", "ENGINE=InnoDB")
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
return db
}
}
+10 -25
View File
@@ -1,47 +1,32 @@
package initialize
import (
//"github.com/dzwvip/oracle"
oracle "github.com/dzwvip/gorm-oracle"
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
//_ "github.com/godror/godror"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// GormOracle 初始化oracle数据库
// 如果需要Oracle库 放开import里的注释 把下方 mysql.Config 改为 oracle.Config ; mysql.New 改为 oracle.New
func GormOracle() *gorm.DB {
m := global.GVA_CONFIG.Oracle
if m.Dbname == "" {
return nil
}
oracleConfig := mysql.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(mysql.New(oracleConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
return db
}
return initOracleDatabase(m)
}
// GormOracleByConfig 初始化Oracle数据库用过传入配置
func GormOracleByConfig(m config.Oracle) *gorm.DB {
return initOracleDatabase(m)
}
// initOracleDatabase 初始化Oracle数据库的辅助函数
func initOracleDatabase(m config.Oracle) *gorm.DB {
if m.Dbname == "" {
return nil
}
oracleConfig := mysql.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(mysql.New(oracleConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
// 数据库配置
general := m.GeneralDB
if db, err := gorm.Open(oracle.Open(m.Dsn()), internal.Gorm.Config(general)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
+10 -17
View File
@@ -13,25 +13,16 @@ import (
// Author [SliverHorn](https://github.com/SliverHorn)
func GormPgSql() *gorm.DB {
p := global.GVA_CONFIG.Pgsql
if p.Dbname == "" {
return nil
}
pgsqlConfig := postgres.Config{
DSN: p.Dsn(), // DSN data source name
PreferSimpleProtocol: false,
}
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil {
return nil
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(p.MaxIdleConns)
sqlDB.SetMaxOpenConns(p.MaxOpenConns)
return db
}
return initPgSqlDatabase(p)
}
// GormPgSqlByConfig 初始化 Postgresql 数据库 通过参数
// GormPgSqlByConfig 初始化 Postgresql 数据库 通过指定参数
func GormPgSqlByConfig(p config.Pgsql) *gorm.DB {
return initPgSqlDatabase(p)
}
// initPgSqlDatabase 初始化 Postgresql 数据库的辅助函数
func initPgSqlDatabase(p config.Pgsql) *gorm.DB {
if p.Dbname == "" {
return nil
}
@@ -39,7 +30,9 @@ func GormPgSqlByConfig(p config.Pgsql) *gorm.DB {
DSN: p.Dsn(), // DSN data source name
PreferSimpleProtocol: false,
}
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil {
// 数据库配置
general := p.GeneralDB
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(general)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
+9 -13
View File
@@ -11,27 +11,23 @@ import (
// GormSqlite 初始化Sqlite数据库
func GormSqlite() *gorm.DB {
s := global.GVA_CONFIG.Sqlite
if s.Dbname == "" {
return nil
}
if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(s.MaxIdleConns)
sqlDB.SetMaxOpenConns(s.MaxOpenConns)
return db
}
return initSqliteDatabase(s)
}
// GormSqliteByConfig 初始化Sqlite数据库用过传入配置
func GormSqliteByConfig(s config.Sqlite) *gorm.DB {
return initSqliteDatabase(s)
}
// initSqliteDatabase 初始化Sqlite数据库辅助函数
func initSqliteDatabase(s config.Sqlite) *gorm.DB {
if s.Dbname == "" {
return nil
}
if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
// 数据库配置
general := s.GeneralDB
if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(general)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
+15
View File
@@ -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()
})
}
+6 -23
View File
@@ -1,14 +1,12 @@
package internal
import (
"time"
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"log"
"os"
"time"
)
var Gorm = new(_gorm)
@@ -17,31 +15,16 @@ type _gorm struct{}
// Config gorm 自定义配置
// Author [SliverHorn](https://github.com/SliverHorn)
func (g *_gorm) Config(prefix string, singular bool) *gorm.Config {
var general config.GeneralDB
switch global.GVA_CONFIG.System.DbType {
case "mysql":
general = global.GVA_CONFIG.Mysql.GeneralDB
case "pgsql":
general = global.GVA_CONFIG.Pgsql.GeneralDB
case "oracle":
general = global.GVA_CONFIG.Oracle.GeneralDB
case "sqlite":
general = global.GVA_CONFIG.Sqlite.GeneralDB
case "mssql":
general = global.GVA_CONFIG.Mssql.GeneralDB
default:
general = global.GVA_CONFIG.Mysql.GeneralDB
}
func (g *_gorm) Config(general config.GeneralDB) *gorm.Config {
return &gorm.Config{
Logger: logger.New(NewWriter(general, log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{
Logger: logger.New(NewWriter(general), logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: general.LogLevel(),
Colorful: true,
}),
NamingStrategy: schema.NamingStrategy{
TablePrefix: prefix,
SingularTable: singular,
TablePrefix: general.Prefix,
SingularTable: general.Singular,
},
DisableForeignKeyConstraintWhenMigrating: true,
}
@@ -3,7 +3,7 @@ package internal
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/config"
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"gorm.io/gorm/logger"
)
@@ -12,26 +12,30 @@ type Writer struct {
writer logger.Writer
}
func NewWriter(config config.GeneralDB, writer logger.Writer) *Writer {
return &Writer{config: config, writer: writer}
func NewWriter(config config.GeneralDB) *Writer {
return &Writer{config: config}
}
// Printf 格式化打印日志
func (c *Writer) Printf(message string, data ...any) {
// 当有日志时候均需要输出到控制台
fmt.Printf(message, data...)
// 当开启了zap的情况,会打印到日志记录
if c.config.LogZap {
switch c.config.LogLevel() {
case logger.Silent:
zap.L().Debug(fmt.Sprintf(message, data...))
global.GVA_LOG.Debug(fmt.Sprintf(message, data...))
case logger.Error:
zap.L().Error(fmt.Sprintf(message, data...))
global.GVA_LOG.Error(fmt.Sprintf(message, data...))
case logger.Warn:
zap.L().Warn(fmt.Sprintf(message, data...))
global.GVA_LOG.Warn(fmt.Sprintf(message, data...))
case logger.Info:
zap.L().Info(fmt.Sprintf(message, data...))
global.GVA_LOG.Info(fmt.Sprintf(message, data...))
default:
zap.L().Info(fmt.Sprintf(message, data...))
global.GVA_LOG.Info(fmt.Sprintf(message, data...))
}
return
}
c.writer.Printf(message, data...)
}
+25
View File
@@ -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))
}
+8 -4
View File
@@ -45,7 +45,7 @@ func (m *mongo) Initialization() error {
opts = internal.Mongo.GetClientOptions()
}
ctx := context.Background()
client, err := qmgo.Open(ctx, &qmgo.Config{
config := &qmgo.Config{
Uri: global.GVA_CONFIG.Mongo.Uri(),
Coll: global.GVA_CONFIG.Mongo.Coll,
Database: global.GVA_CONFIG.Mongo.Database,
@@ -53,12 +53,16 @@ func (m *mongo) Initialization() error {
MaxPoolSize: &global.GVA_CONFIG.Mongo.MaxPoolSize,
SocketTimeoutMS: &global.GVA_CONFIG.Mongo.SocketTimeoutMs,
ConnectTimeoutMS: &global.GVA_CONFIG.Mongo.ConnectTimeoutMs,
Auth: &qmgo.Credential{
}
if global.GVA_CONFIG.Mongo.Username != "" && global.GVA_CONFIG.Mongo.Password != "" {
config.Auth = &qmgo.Credential{
Username: global.GVA_CONFIG.Mongo.Username,
Password: global.GVA_CONFIG.Mongo.Password,
AuthSource: global.GVA_CONFIG.Mongo.AuthSource,
},
}, opts...)
}
}
client, err := qmgo.Open(ctx, config, opts...)
if err != nil {
return errors.Wrap(err, "链接mongodb数据库失败!")
}
+2
View File
@@ -2,6 +2,7 @@ package initialize
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email"
"github.com/flipped-aurora/gin-vue-admin/server/utils/plugin"
@@ -29,6 +30,7 @@ func bizPluginV1(group ...*gin.RouterGroup) {
global.GVA_CONFIG.Email.Nickname,
global.GVA_CONFIG.Email.Port,
global.GVA_CONFIG.Email.IsSSL,
global.GVA_CONFIG.Email.IsLoginAuth,
))
holder(public, private)
}
+45
View File
@@ -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
}
+31 -18
View File
@@ -40,13 +40,24 @@ 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 下的
// VUE_APP_BASE_API = /
// VUE_APP_BASE_PATH = http://localhost
// 然后执行打包命令 npm run build。在打开下面3行注释
// Router.Static("/favicon.ico", "./dist/favicon.ico")
// Router.StaticFile("/favicon.ico", "./dist/favicon.ico")
// Router.Static("/assets", "./dist/assets") // dist里面的静态资源
// Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
@@ -77,23 +88,25 @@ func Routers() *gin.Engine {
}
{
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
systemRouter.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
systemRouter.InitSysExportTemplateRouter(PrivateGroup) // 导出模板
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
systemRouter.InitSysVersionRouter(PrivateGroup) // 发版相关路由
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
systemRouter.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
exampleRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
}
+18 -5
View File
@@ -13,14 +13,30 @@ import (
//go:generate go mod tidy
//go:generate go mod download
// 这部分 @Tag 设置用于排序, 需要排序的接口请按照下面的格式添加
// swag init 对 @Tag 只会从入口文件解析, 默认 main.go
// 也可通过 --generalInfo flag 指定其他文件
// @Tag.Name Base
// @Tag.Name SysUser
// @Tag.Description 用户
// @title Gin-Vue-Admin Swagger API接口文档
// @version v2.7.7
// @version v2.8.5
// @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日志库
@@ -28,11 +44,8 @@ func main() {
global.GVA_DB = initialize.Gorm() // gorm连接数据库
initialize.Timer()
initialize.DBList()
initialize.SetupHandlers() // 注册全局函数
if global.GVA_DB != nil {
initialize.RegisterTables() // 初始化表
// 程序结束前关闭数据库链接
db, _ := global.GVA_DB.DB()
defer db.Close()
}
core.RunWindowsServer()
}
+201
View File
@@ -0,0 +1,201 @@
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(&ApiCreator{})
}
// ApiCreateRequest API创建请求结构
type ApiCreateRequest struct {
Path string `json:"path"` // API路径
Description string `json:"description"` // API中文描述
ApiGroup string `json:"apiGroup"` // API组
Method string `json:"method"` // HTTP方法
}
// ApiCreateResponse API创建响应结构
type ApiCreateResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
ApiID uint `json:"apiId"`
Path string `json:"path"`
Method string `json:"method"`
}
// ApiCreator API创建工具
type ApiCreator struct{}
// New 创建API创建工具
func (a *ApiCreator) New() mcp.Tool {
return mcp.NewTool("create_api",
mcp.WithDescription(`创建后端API记录,用于AI编辑器自动添加API接口时自动创建对应的API权限记录。
**重要限制:**
- 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成API权限,不应调用此工具
- 仅在以下情况使用:1) 单独创建API(不涉及模块创建);2) AI编辑器自动添加API;3) router下的文件产生路径变化时`),
mcp.WithString("path",
mcp.Required(),
mcp.Description("API路径,如:/user/create"),
),
mcp.WithString("description",
mcp.Required(),
mcp.Description("API中文描述,如:创建用户"),
),
mcp.WithString("apiGroup",
mcp.Required(),
mcp.Description("API组名称,用于分类管理,如:用户管理"),
),
mcp.WithString("method",
mcp.Description("HTTP方法"),
mcp.DefaultString("POST"),
),
mcp.WithString("apis",
mcp.Description("批量创建API的JSON字符串,格式:[{\"path\":\"/user/create\",\"description\":\"创建用户\",\"apiGroup\":\"用户管理\",\"method\":\"POST\"}]"),
),
)
}
// Handle 处理API创建请求
func (a *ApiCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args := request.GetArguments()
var apis []ApiCreateRequest
// 检查是否是批量创建
if apisStr, ok := args["apis"].(string); ok && apisStr != "" {
if err := json.Unmarshal([]byte(apisStr), &apis); err != nil {
return nil, fmt.Errorf("apis 参数格式错误: %v", err)
}
} else {
// 单个API创建
path, ok := args["path"].(string)
if !ok || path == "" {
return nil, errors.New("path 参数是必需的")
}
description, ok := args["description"].(string)
if !ok || description == "" {
return nil, errors.New("description 参数是必需的")
}
apiGroup, ok := args["apiGroup"].(string)
if !ok || apiGroup == "" {
return nil, errors.New("apiGroup 参数是必需的")
}
method := "POST"
if val, ok := args["method"].(string); ok && val != "" {
method = val
}
apis = append(apis, ApiCreateRequest{
Path: path,
Description: description,
ApiGroup: apiGroup,
Method: method,
})
}
if len(apis) == 0 {
return nil, errors.New("没有要创建的API")
}
// 创建API记录
apiService := service.ServiceGroupApp.SystemServiceGroup.ApiService
var responses []ApiCreateResponse
successCount := 0
for _, apiReq := range apis {
api := system.SysApi{
Path: apiReq.Path,
Description: apiReq.Description,
ApiGroup: apiReq.ApiGroup,
Method: apiReq.Method,
}
err := apiService.CreateApi(api)
if err != nil {
global.GVA_LOG.Warn("创建API失败",
zap.String("path", apiReq.Path),
zap.String("method", apiReq.Method),
zap.Error(err))
responses = append(responses, ApiCreateResponse{
Success: false,
Message: fmt.Sprintf("创建API失败: %v", err),
Path: apiReq.Path,
Method: apiReq.Method,
})
} else {
// 获取创建的API ID
var createdApi system.SysApi
err = global.GVA_DB.Where("path = ? AND method = ?", apiReq.Path, apiReq.Method).First(&createdApi).Error
if err != nil {
global.GVA_LOG.Warn("获取创建的API ID失败", zap.Error(err))
}
responses = append(responses, ApiCreateResponse{
Success: true,
Message: fmt.Sprintf("成功创建API %s %s", apiReq.Method, apiReq.Path),
ApiID: createdApi.ID,
Path: apiReq.Path,
Method: apiReq.Method,
})
successCount++
}
}
// 构建总体响应
var resultMessage string
if len(apis) == 1 {
resultMessage = responses[0].Message
} else {
resultMessage = fmt.Sprintf("批量创建API完成,成功 %d 个,失败 %d 个", successCount, len(apis)-successCount)
}
result := map[string]interface{}{
"success": successCount > 0,
"message": resultMessage,
"totalCount": len(apis),
"successCount": successCount,
"failedCount": len(apis) - successCount,
"details": responses,
}
resultJSON, err := json.MarshalIndent(result, "", " ")
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
// 添加权限分配提醒
permissionReminder := "\n\n⚠️ 重要提醒:\n" +
"API创建完成后,请前往【系统管理】->【角色管理】中为相关角色分配新创建的API权限," +
"以确保用户能够正常访问新接口。\n" +
"具体步骤:\n" +
"1. 进入角色管理页面\n" +
"2. 选择需要授权的角色\n" +
"3. 在API权限中勾选新创建的API接口\n" +
"4. 保存权限配置"
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("API创建结果:\n\n%s%s", string(resultJSON), permissionReminder),
},
},
}, nil
}
+165
View File
@@ -0,0 +1,165 @@
package mcpTool
import (
"context"
"encoding/json"
"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"
"go.uber.org/zap"
)
// 注册工具
func init() {
// 注册工具将在enter.go中统一处理
RegisterTool(&ApiLister{})
}
// ApiInfo API信息结构
type ApiInfo struct {
ID uint `json:"id,omitempty"` // 数据库ID(仅数据库API有)
Path string `json:"path"` // API路径
Description string `json:"description,omitempty"` // API描述
ApiGroup string `json:"apiGroup,omitempty"` // API组
Method string `json:"method"` // HTTP方法
Source string `json:"source"` // 来源:database 或 gin
}
// ApiListResponse API列表响应结构
type ApiListResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
DatabaseApis []ApiInfo `json:"databaseApis"` // 数据库中的API
GinApis []ApiInfo `json:"ginApis"` // gin框架中的API
TotalCount int `json:"totalCount"` // 总数量
}
// ApiLister API列表工具
type ApiLister struct{}
// New 创建API列表工具
func (a *ApiLister) New() mcp.Tool {
return mcp.NewTool("list_all_apis",
mcp.WithDescription(`获取系统中所有的API接口,分为两组:
**功能说明:**
- 返回数据库中已注册的API列表
- 返回gin框架中实际注册的路由API列表
- 帮助前端判断是使用现有API还是需要创建新的API,如果api在前端未使用且需要前端调用的时候,请到api文件夹下对应模块的js中添加方法并暴露给当前业务调用
**返回数据结构:**
- databaseApis: 数据库中的API记录(包含ID、描述、分组等完整信息)
- ginApis: gin路由中的API(仅包含路径和方法),需要AI根据路径自行揣摩路径的业务含义,例如:/api/user/:id 表示根据用户ID获取用户信息`),
)
}
// Handle 处理API列表请求
func (a *ApiLister) Handle(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取数据库中的API
databaseApis, err := a.getDatabaseApis()
if err != nil {
global.GVA_LOG.Error("获取数据库API失败", zap.Error(err))
errorResponse := ApiListResponse{
Success: false,
Message: "获取数据库API失败: " + err.Error(),
}
resultJSON, _ := json.Marshal(errorResponse)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(resultJSON),
},
},
}, nil
}
// 获取gin路由中的API
ginApis, err := a.getGinApis()
if err != nil {
global.GVA_LOG.Error("获取gin路由API失败", zap.Error(err))
errorResponse := ApiListResponse{
Success: false,
Message: "获取gin路由API失败: " + err.Error(),
}
resultJSON, _ := json.Marshal(errorResponse)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(resultJSON),
},
},
}, nil
}
// 构建响应
response := ApiListResponse{
Success: true,
Message: "获取API列表成功",
DatabaseApis: databaseApis,
GinApis: ginApis,
TotalCount: len(databaseApis) + len(ginApis),
}
global.GVA_LOG.Info("API列表获取成功",
zap.Int("数据库API数量", len(databaseApis)),
zap.Int("gin路由API数量", len(ginApis)),
zap.Int("总数量", response.TotalCount))
resultJSON, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(resultJSON),
},
},
}, nil
}
// getDatabaseApis 获取数据库中的所有API
func (a *ApiLister) getDatabaseApis() ([]ApiInfo, error) {
var apis []system.SysApi
err := global.GVA_DB.Model(&system.SysApi{}).Order("api_group ASC, path ASC").Find(&apis).Error
if err != nil {
return nil, err
}
// 转换为ApiInfo格式
var result []ApiInfo
for _, api := range apis {
result = append(result, ApiInfo{
ID: api.ID,
Path: api.Path,
Description: api.Description,
ApiGroup: api.ApiGroup,
Method: api.Method,
Source: "database",
})
}
return result, nil
}
// getGinApis 获取gin路由中的所有API(包含被忽略的API)
func (a *ApiLister) getGinApis() ([]ApiInfo, error) {
// 从gin路由信息中获取所有API
var result []ApiInfo
for _, route := range global.GVA_ROUTERS {
result = append(result, ApiInfo{
Path: route.Path,
Method: route.Method,
Source: "gin",
})
}
return result, nil
}
+39
View File
@@ -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
}
+132
View File
@@ -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("-------------------")
}
}
+310
View File
@@ -0,0 +1,310 @@
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"
"gorm.io/gorm"
)
func init() {
RegisterTool(&DictionaryOptionsGenerator{})
}
// DictionaryOptionsGenerator 字典选项生成器
type DictionaryOptionsGenerator struct{}
// DictionaryGenerateRequest 字典生成请求
type DictionaryGenerateRequest struct {
DictType string `json:"dictType"` // 字典类型
FieldDesc string `json:"fieldDesc"` // 字段描述
Options []DictionaryOption `json:"options"` // AI生成的字典选项
DictName string `json:"dictName"` // 字典名称(可选)
Description string `json:"description"` // 字典描述(可选)
}
// DictionaryGenerateResponse 字典生成响应
type DictionaryGenerateResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
DictType string `json:"dictType"`
OptionsCount int `json:"optionsCount"`
}
// New 返回工具注册信息
func (d *DictionaryOptionsGenerator) New() mcp.Tool {
return mcp.NewTool("generate_dictionary_options",
mcp.WithDescription("智能生成字典选项并自动创建字典和字典详情"),
mcp.WithString("dictType",
mcp.Required(),
mcp.Description("字典类型,用于标识字典的唯一性"),
),
mcp.WithString("fieldDesc",
mcp.Required(),
mcp.Description("字段描述,用于AI理解字段含义"),
),
mcp.WithString("options",
mcp.Required(),
mcp.Description("字典选项JSON字符串,格式:[{\"label\":\"显示名\",\"value\":\"值\",\"sort\":1}]"),
),
mcp.WithString("dictName",
mcp.Description("字典名称,如果不提供将自动生成"),
),
mcp.WithString("description",
mcp.Description("字典描述"),
),
)
}
// Name 返回工具名称
func (d *DictionaryOptionsGenerator) Name() string {
return "generate_dictionary_options"
}
// Description 返回工具描述
func (d *DictionaryOptionsGenerator) Description() string {
return `字典选项生成工具 - 让AI生成并创建字典选项
此工具允许AI根据字典类型和字段描述生成合适的字典选项,并自动创建字典和字典详情。
参数说明:
- dictType: 字典类型(必填)
- fieldDesc: 字段描述(必填)
- options: AI生成的字典选项数组(必填)
- label: 选项标签
- value: 选项值
- sort: 排序号
- dictName: 字典名称(可选,默认根据fieldDesc生成)
- description: 字典描述(可选)
使用场景:
1. 在创建模块时,如果字段需要字典类型,AI可以根据字段描述智能生成合适的选项
2. 支持各种业务场景的字典选项生成,如状态、类型、等级等
3. 自动创建字典和字典详情,无需手动配置
示例调用:
{
"dictType": "user_status",
"fieldDesc": "用户状态",
"options": [
{"label": "正常", "value": "1", "sort": 1},
{"label": "禁用", "value": "0", "sort": 2}
],
"dictName": "用户状态字典",
"description": "用于管理用户账户状态的字典"
}`
}
// InputSchema 返回输入参数的JSON Schema
func (d *DictionaryOptionsGenerator) InputSchema() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"dictType": map[string]interface{}{
"type": "string",
"description": "字典类型,用于标识字典的唯一性",
},
"fieldDesc": map[string]interface{}{
"type": "string",
"description": "字段描述,用于生成字典名称和理解字典用途",
},
"options": map[string]interface{}{
"type": "array",
"description": "AI生成的字典选项数组",
"items": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"label": map[string]interface{}{
"type": "string",
"description": "选项标签,显示给用户的文本",
},
"value": map[string]interface{}{
"type": "string",
"description": "选项值,存储在数据库中的值",
},
"sort": map[string]interface{}{
"type": "integer",
"description": "排序号,用于控制选项显示顺序",
},
},
"required": []string{"label", "value", "sort"},
},
},
"dictName": map[string]interface{}{
"type": "string",
"description": "字典名称,可选,默认根据fieldDesc生成",
},
"description": map[string]interface{}{
"type": "string",
"description": "字典描述,可选",
},
},
"required": []string{"dictType", "fieldDesc", "options"},
}
}
// Handle 处理工具调用
func (d *DictionaryOptionsGenerator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 解析请求参数
args := request.GetArguments()
dictType, ok := args["dictType"].(string)
if !ok || dictType == "" {
return nil, errors.New("dictType 参数是必需的")
}
fieldDesc, ok := args["fieldDesc"].(string)
if !ok || fieldDesc == "" {
return nil, errors.New("fieldDesc 参数是必需的")
}
optionsStr, ok := args["options"].(string)
if !ok || optionsStr == "" {
return nil, errors.New("options 参数是必需的")
}
// 解析options JSON字符串
var options []DictionaryOption
if err := json.Unmarshal([]byte(optionsStr), &options); err != nil {
return nil, fmt.Errorf("options 参数格式错误: %v", err)
}
if len(options) == 0 {
return nil, errors.New("options 不能为空")
}
// 可选参数
dictName, _ := args["dictName"].(string)
description, _ := args["description"].(string)
// 构建请求对象
req := &DictionaryGenerateRequest{
DictType: dictType,
FieldDesc: fieldDesc,
Options: options,
DictName: dictName,
Description: description,
}
// 创建字典
response, err := d.createDictionaryWithOptions(ctx, req)
if err != nil {
return nil, err
}
// 构建响应
resultJSON, err := json.MarshalIndent(response, "", " ")
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("字典选项生成结果:\n\n%s", string(resultJSON)),
},
},
}, nil
}
// createDictionaryWithOptions 创建字典和字典选项
func (d *DictionaryOptionsGenerator) createDictionaryWithOptions(ctx context.Context, req *DictionaryGenerateRequest) (*DictionaryGenerateResponse, error) {
// 检查字典是否已存在
exists, err := d.checkDictionaryExists(req.DictType)
if err != nil {
return nil, fmt.Errorf("检查字典是否存在失败: %v", err)
}
if exists {
return &DictionaryGenerateResponse{
Success: false,
Message: fmt.Sprintf("字典 %s 已存在,跳过创建", req.DictType),
DictType: req.DictType,
OptionsCount: 0,
}, nil
}
// 生成字典名称
dictName := req.DictName
if dictName == "" {
dictName = d.generateDictionaryName(req.DictType, req.FieldDesc)
}
// 创建字典
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
dictionary := system.SysDictionary{
Name: dictName,
Type: req.DictType,
Status: &[]bool{true}[0], // 默认启用
Desc: req.Description,
}
err = dictionaryService.CreateSysDictionary(dictionary)
if err != nil {
return nil, fmt.Errorf("创建字典失败: %v", err)
}
// 获取刚创建的字典ID
var createdDict system.SysDictionary
err = global.GVA_DB.Where("type = ?", req.DictType).First(&createdDict).Error
if err != nil {
return nil, fmt.Errorf("获取创建的字典失败: %v", err)
}
// 创建字典详情项
dictionaryDetailService := service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
successCount := 0
for _, option := range req.Options {
dictionaryDetail := system.SysDictionaryDetail{
Label: option.Label,
Value: option.Value,
Status: &[]bool{true}[0], // 默认启用
Sort: option.Sort,
SysDictionaryID: int(createdDict.ID),
}
err = dictionaryDetailService.CreateSysDictionaryDetail(dictionaryDetail)
if err != nil {
global.GVA_LOG.Warn("创建字典详情项失败", zap.Error(err))
} else {
successCount++
}
}
return &DictionaryGenerateResponse{
Success: true,
Message: fmt.Sprintf("成功创建字典 %s,包含 %d 个选项", req.DictType, successCount),
DictType: req.DictType,
OptionsCount: successCount,
}, nil
}
// checkDictionaryExists 检查字典是否存在
func (d *DictionaryOptionsGenerator) checkDictionaryExists(dictType string) (bool, error) {
var dictionary system.SysDictionary
err := global.GVA_DB.Where("type = ?", dictType).First(&dictionary).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return false, nil // 字典不存在
}
return false, err // 其他错误
}
return true, nil // 字典存在
}
// generateDictionaryName 生成字典名称
func (d *DictionaryOptionsGenerator) generateDictionaryName(dictType, fieldDesc string) string {
if fieldDesc != "" {
return fmt.Sprintf("%s字典", fieldDesc)
}
return fmt.Sprintf("%s字典", dictType)
}
+234
View File
@@ -0,0 +1,234 @@
package mcpTool
import (
"context"
"encoding/json"
"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"
"gorm.io/gorm"
)
// 注册工具
func init() {
RegisterTool(&DictionaryQuery{})
}
// DictionaryInfo 字典信息结构
type DictionaryInfo struct {
ID uint `json:"id"`
Name string `json:"name"` // 字典名(中)
Type string `json:"type"` // 字典名(英)
Status *bool `json:"status"` // 状态
Desc string `json:"desc"` // 描述
Details []DictionaryDetailInfo `json:"details"` // 字典详情
}
// DictionaryDetailInfo 字典详情信息结构
type DictionaryDetailInfo struct {
ID uint `json:"id"`
Label string `json:"label"` // 展示值
Value string `json:"value"` // 字典值
Extend string `json:"extend"` // 扩展值
Status *bool `json:"status"` // 启用状态
Sort int `json:"sort"` // 排序标记
}
// DictionaryQueryResponse 字典查询响应结构
type DictionaryQueryResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Total int `json:"total"`
Dictionaries []DictionaryInfo `json:"dictionaries"`
}
// DictionaryQuery 字典查询工具
type DictionaryQuery struct{}
// New 创建字典查询工具
func (d *DictionaryQuery) New() mcp.Tool {
return mcp.NewTool("query_dictionaries",
mcp.WithDescription("查询系统中所有的字典和字典属性,用于AI生成逻辑时了解可用的字典选项"),
mcp.WithString("dictType",
mcp.Description("可选:指定字典类型进行精确查询,如果不提供则返回所有字典"),
),
mcp.WithBoolean("includeDisabled",
mcp.Description("是否包含已禁用的字典和字典项,默认为false(只返回启用的)"),
),
mcp.WithBoolean("detailsOnly",
mcp.Description("是否只返回字典详情信息(不包含字典基本信息),默认为false"),
),
)
}
// Handle 处理字典查询请求
func (d *DictionaryQuery) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args := request.GetArguments()
// 获取参数
dictType := ""
if val, ok := args["dictType"].(string); ok {
dictType = val
}
includeDisabled := false
if val, ok := args["includeDisabled"].(bool); ok {
includeDisabled = val
}
detailsOnly := false
if val, ok := args["detailsOnly"].(bool); ok {
detailsOnly = val
}
// 获取字典服务
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
var dictionaries []DictionaryInfo
var err error
if dictType != "" {
// 查询指定类型的字典
var status *bool
if !includeDisabled {
status = &[]bool{true}[0]
}
sysDictionary, err := dictionaryService.GetSysDictionary(dictType, 0, status)
if err != nil {
global.GVA_LOG.Error("查询字典失败", zap.Error(err))
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "查询字典失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
},
}, nil
}
// 转换为响应格式
dictInfo := DictionaryInfo{
ID: sysDictionary.ID,
Name: sysDictionary.Name,
Type: sysDictionary.Type,
Status: sysDictionary.Status,
Desc: sysDictionary.Desc,
}
// 获取字典详情
for _, detail := range sysDictionary.SysDictionaryDetails {
if includeDisabled || (detail.Status != nil && *detail.Status) {
dictInfo.Details = append(dictInfo.Details, DictionaryDetailInfo{
ID: detail.ID,
Label: detail.Label,
Value: detail.Value,
Extend: detail.Extend,
Status: detail.Status,
Sort: detail.Sort,
})
}
}
dictionaries = append(dictionaries, dictInfo)
} else {
// 查询所有字典
var sysDictionaries []system.SysDictionary
db := global.GVA_DB.Model(&system.SysDictionary{})
if !includeDisabled {
db = db.Where("status = ?", true)
}
err = db.Preload("SysDictionaryDetails", func(db *gorm.DB) *gorm.DB {
if includeDisabled {
return db.Order("sort")
} else {
return db.Where("status = ?", true).Order("sort")
}
}).Find(&sysDictionaries).Error
if err != nil {
global.GVA_LOG.Error("查询字典列表失败", zap.Error(err))
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "查询字典列表失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
},
}, nil
}
// 转换为响应格式
for _, dict := range sysDictionaries {
dictInfo := DictionaryInfo{
ID: dict.ID,
Name: dict.Name,
Type: dict.Type,
Status: dict.Status,
Desc: dict.Desc,
}
// 获取字典详情
for _, detail := range dict.SysDictionaryDetails {
if includeDisabled || (detail.Status != nil && *detail.Status) {
dictInfo.Details = append(dictInfo.Details, DictionaryDetailInfo{
ID: detail.ID,
Label: detail.Label,
Value: detail.Value,
Extend: detail.Extend,
Status: detail.Status,
Sort: detail.Sort,
})
}
}
dictionaries = append(dictionaries, dictInfo)
}
}
// 如果只需要详情信息,则提取所有详情
if detailsOnly {
var allDetails []DictionaryDetailInfo
for _, dict := range dictionaries {
allDetails = append(allDetails, dict.Details...)
}
response := map[string]interface{}{
"success": true,
"message": "查询字典详情成功",
"total": len(allDetails),
"details": allDetails,
}
responseJSON, _ := json.Marshal(response)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(string(responseJSON)),
},
}, nil
}
// 构建响应
response := DictionaryQueryResponse{
Success: true,
Message: "查询字典成功",
Total: len(dictionaries),
Dictionaries: dictionaries,
}
responseJSON, err := json.Marshal(response)
if err != nil {
global.GVA_LOG.Error("序列化响应失败", zap.Error(err))
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "序列化响应失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
},
}, nil
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(string(responseJSON)),
},
}, nil
}
+31
View File
@@ -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)
}
}
+529
View File
@@ -0,0 +1,529 @@
# ExecutionPlan 结构体格式说明
## 概述
ExecutionPlan 是用于自动化模块创建的执行计划结构体,包含了创建包和模块所需的所有信息。
## 完整结构体定义
```go
type ExecutionPlan struct {
PackageName string `json:"packageName"` // 包名,如:"user", "order", "product"
PackageType string `json:"packageType"` // "plugin" 或 "package"
NeedCreatedPackage bool `json:"needCreatedPackage"` // 是否需要创建包
NeedCreatedModules bool `json:"needCreatedModules"` // 是否需要创建模块
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"` // 包信息(当NeedCreatedPackage=true时必需)
ModulesInfo []*request.AutoCode `json:"modulesInfo,omitempty"` // 模块信息数组(当NeedCreatedModules=true时必需,支持批量创建)
Paths map[string]string `json:"paths,omitempty"` // 路径信息
}
```
## 子结构体详细说明
### 1. SysAutoCodePackageCreate 结构体
```go
type SysAutoCodePackageCreate struct {
Desc string `json:"desc"` // 描述,如:"用户管理模块"
Label string `json:"label"` // 展示名,如:"用户管理"
Template string `json:"template"` // 模板类型:"plugin" 或 "package"
PackageName string `json:"packageName"` // 包名,如:"user"
Module string `json:"-"` // 模块名(自动填充,无需设置)
}
```
### 2. AutoCode 结构体(核心字段)
```go
type AutoCode struct {
Package string `json:"package"` // 包名
TableName string `json:"tableName"` // 数据库表名
BusinessDB string `json:"businessDB"` // 业务数据库名
StructName string `json:"structName"` // 结构体名称
PackageName string `json:"packageName"` // 文件名称
Description string `json:"description"` // 结构体中文名称
Abbreviation string `json:"abbreviation"` // 结构体简称
HumpPackageName string `json:"humpPackageName"` // 驼峰命名的包名
GvaModel bool `json:"gvaModel"` // 是否使用GVA默认Model
AutoMigrate bool `json:"autoMigrate"` // 是否自动迁移表结构
AutoCreateResource bool `json:"autoCreateResource"` // 是否自动创建资源标识
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建API
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql"` // 是否自动创建菜单
AutoCreateBtnAuth bool `json:"autoCreateBtnAuth"` // 是否自动创建按钮权限
OnlyTemplate bool `json:"onlyTemplate"` // 是否只生成模板
IsTree bool `json:"isTree"` // 是否树形结构
TreeJson string `json:"treeJson"` // 树形结构JSON字段
IsAdd bool `json:"isAdd"` // 是否新增
Fields []*AutoCodeField `json:"fields"` // 字段列表
GenerateWeb bool `json:"generateWeb"` // 是否生成前端代码
GenerateServer bool `json:"generateServer"` // 是否生成后端代码
Module string `json:"-"` // 模块(自动填充)
DictTypes []string `json:"-"` // 字典类型(自动填充)
}
```
### 3. AutoCodeField 结构体(字段定义)
```go
type AutoCodeField struct {
FieldName string `json:"fieldName"` // 字段名
FieldDesc string `json:"fieldDesc"` // 字段中文描述
FieldType string `json:"fieldType"` // 字段类型:string, int, bool, time.Time等
FieldJson string `json:"fieldJson"` // JSON标签名
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
Comment string `json:"comment"` // 数据库字段注释
ColumnName string `json:"columnName"` // 数据库列名
FieldSearchType string `json:"fieldSearchType"` // 搜索类型:EQ, LIKE, BETWEEN等
FieldSearchHide bool `json:"fieldSearchHide"` // 是否隐藏查询条件
DictType string `json:"dictType"` // 字典类型
Form bool `json:"form"` // 是否在表单中显示
Table bool `json:"table"` // 是否在表格中显示
Desc bool `json:"desc"` // 是否在详情中显示
Excel bool `json:"excel"` // 是否支持导入导出
Require bool `json:"require"` // 是否必填
DefaultValue string `json:"defaultValue"` // 默认值
ErrorText string `json:"errorText"` // 校验失败提示
Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否支持排序
PrimaryKey bool `json:"primaryKey"` // 是否主键
DataSource *DataSource `json:"dataSource"` // 数据源配置(用于关联其他表)
CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源
FieldIndexType string `json:"fieldIndexType"` // 索引类型
}
```
### 4. DataSource 结构体(关联表配置)
```go
type DataSource struct {
DBName string `json:"dbName"` // 关联的数据库名称
Table string `json:"table"` // 关联的表名
Label string `json:"label"` // 用于显示的字段名(如name、title等)
Value string `json:"value"` // 用于存储的值字段名(通常是id)
Association int `json:"association"` // 关联关系:1=一对一,2=一对多
HasDeletedAt bool `json:"hasDeletedAt"` // 关联表是否有软删除字段
}
```
## 使用示例
### 示例1:创建新包和批量创建多个模块
```json
{
"packageName": "user",
"packageType": "package",
"needCreatedPackage": true,
"needCreatedModules": true,
"packageInfo": {
"desc": "用户管理模块",
"label": "用户管理",
"template": "package",
"packageName": "user"
},
"modulesInfo": [
{
"package": "user",
"tableName": "sys_users",
"businessDB": "",
"structName": "User",
"packageName": "user",
"description": "用户",
"abbreviation": "user",
"humpPackageName": "user",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": true,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "Username",
"fieldDesc": "用户名",
"fieldType": "string",
"fieldJson": "username",
"dataTypeLong": "50",
"comment": "用户名",
"columnName": "username",
"fieldSearchType": "LIKE",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请输入用户名",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": {
"dbName": "gva",
"table": "sys_users",
"label": "username",
"value": "id",
"association": 2,
"hasDeletedAt": true
},
"checkDataSource": true,
"fieldIndexType": ""
},
{
"fieldName": "Email",
"fieldDesc": "邮箱",
"fieldType": "string",
"fieldJson": "email",
"dataTypeLong": "100",
"comment": "邮箱地址",
"columnName": "email",
"fieldSearchType": "EQ",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请输入邮箱",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": null,
"checkDataSource": false,
"fieldIndexType": "index"
}
]
},
{
"package": "user",
"tableName": "user_profiles",
"businessDB": "",
"structName": "UserProfile",
"packageName": "user",
"description": "用户档案",
"abbreviation": "userProfile",
"humpPackageName": "user",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": true,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "UserID",
"fieldDesc": "用户ID",
"fieldType": "int",
"fieldJson": "userId",
"dataTypeLong": "",
"comment": "关联用户ID",
"columnName": "user_id",
"fieldSearchType": "EQ",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请选择用户",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": null,
"checkDataSource": false,
"fieldIndexType": "index"
},
{
"fieldName": "Avatar",
"fieldDesc": "头像",
"fieldType": "string",
"fieldJson": "avatar",
"dataTypeLong": "255",
"comment": "用户头像URL",
"columnName": "avatar",
"fieldSearchType": "",
"fieldSearchHide": true,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": false,
"require": false,
"defaultValue": "",
"errorText": "",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": null,
"checkDataSource": false,
"fieldIndexType": ""
}
]
}
]
}
```
### 示例2:仅在现有包中批量创建多个模块
```json
{
"packageName": "system",
"packageType": "package",
"needCreatedPackage": false,
"needCreatedModules": true,
"packageInfo": null,
"modulesInfo": [
{
"package": "system",
"tableName": "sys_roles",
"businessDB": "",
"structName": "Role",
"packageName": "system",
"description": "角色",
"abbreviation": "role",
"humpPackageName": "system",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "RoleName",
"fieldDesc": "角色名称",
"fieldType": "string",
"fieldJson": "roleName",
"dataTypeLong": "50",
"comment": "角色名称",
"columnName": "role_name",
"fieldSearchType": "LIKE",
"form": true,
"table": true,
"desc": true,
"require": true
}
]
},
{
"package": "system",
"tableName": "sys_permissions",
"businessDB": "",
"structName": "Permission",
"packageName": "system",
"description": "权限",
"abbreviation": "permission",
"humpPackageName": "system",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "PermissionName",
"fieldDesc": "权限名称",
"fieldType": "string",
"fieldJson": "permissionName",
"dataTypeLong": "100",
"comment": "权限名称",
"columnName": "permission_name",
"fieldSearchType": "LIKE",
"form": true,
"table": true,
"desc": true,
"require": true
},
{
"fieldName": "PermissionCode",
"fieldDesc": "权限代码",
"fieldType": "string",
"fieldJson": "permissionCode",
"dataTypeLong": "50",
"comment": "权限代码",
"columnName": "permission_code",
"fieldSearchType": "=",
"form": true,
"table": true,
"desc": true,
"require": true
}
]
}
]
}
```
### 示例3:模块关联关系配置详解
以下示例展示了如何配置不同类型的关联关系:
```json
{
"packageName": "order",
"packageType": "package",
"needCreatedPackage": true,
"needCreatedModules": true,
"packageInfo": {
"desc": "订单管理模块",
"label": "订单管理",
"template": "package",
"packageName": "order"
},
"modulesInfo": [
{
"package": "order",
"tableName": "orders",
"structName": "Order",
"packageName": "order",
"description": "订单",
"abbreviation": "order",
"humpPackageName": "order",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "UserID",
"fieldDesc": "下单用户",
"fieldType": "uint",
"fieldJson": "userId",
"columnName": "user_id",
"fieldSearchType": "EQ",
"form": true,
"table": true,
"desc": true,
"require": true,
"dataSource": {
"dbName": "gva",
"table": "sys_users",
"label": "username",
"value": "id",
"association": 2,
"hasDeletedAt": true
},
"checkDataSource": true
},
{
"fieldName": "ProductID",
"fieldDesc": "商品",
"fieldType": "uint",
"fieldJson": "productId",
"columnName": "product_id",
"fieldSearchType": "EQ",
"form": true,
"table": true,
"desc": true,
"require": true,
"dataSource": {
"dbName": "gva",
"table": "products",
"label": "name",
"value": "id",
"association": 2,
"hasDeletedAt": false
},
"checkDataSource": true
},
{
"fieldName": "Status",
"fieldDesc": "订单状态",
"fieldType": "int",
"fieldJson": "status",
"columnName": "status",
"fieldSearchType": "EQ",
"form": true,
"table": true,
"desc": true,
"require": true,
"dictType": "order_status"
}
]
}
]
}
```
## DataSource 配置说明
### 关联关系类型
- **association: 1** - 一对一关联(如用户与用户档案)
- **association: 2** - 一对多关联(如用户与订单)
### 配置要点
1. **dbName**: 通常为 "gva"(默认数据库)
2. **table**: 关联表的实际表名
3. **label**: 用于前端显示的字段(如用户名、商品名称)
4. **value**: 用于存储关联ID的字段(通常是 "id")
5. **hasDeletedAt**: 关联表是否支持软删除
6. **checkDataSource**: 建议设为true,会验证关联表是否存在
### 常见关联场景
- 用户关联:`{"table": "sys_users", "label": "username", "value": "id"}`
- 角色关联:`{"table": "sys_authorities", "label": "authorityName", "value": "authorityId"}`
- 部门关联:`{"table": "sys_departments", "label": "name", "value": "id"}`
- 分类关联:`{"table": "categories", "label": "name", "value": "id"}`
## 重要注意事项
1. **PackageType**: 只能是 "plugin" 或 "package"
2. **NeedCreatedPackage**: 当为true时,PackageInfo必须提供
3. **NeedCreatedModules**: 当为true时,ModulesInfo必须提供
4. **字段类型**: FieldType支持的类型包括:
- string(字符串)
- richtext(富文本)
- int(整型)
- bool(布尔值)
- float64(浮点型)
- time.Time(时间)
- enum(枚举)
- picture(单图片,字符串)
- pictures(多图片,json字符串)
- video(视频,字符串)
- file(文件,json字符串)
- json(JSON)
- array(数组)
5. **搜索类型**: FieldSearchType支持:EQ, NE, GT, GE, LT, LE, LIKE, BETWEEN等
6. **索引类型**: FieldIndexType支持:index, unique等
7. **GvaModel**: 设置为true时会自动包含ID、CreatedAt、UpdatedAt、DeletedAt字段
8. **关联配置**: 使用dataSource时,确保关联表已存在,建议开启checkDataSource验证
## 常见错误避免
1. 确保PackageName和ModuleName符合Go语言命名规范
2. 字段名使用大写开头的驼峰命名
3. JSON标签使用小写开头的驼峰命名
4. 数据库列名使用下划线分隔的小写命名
5. 必填字段不要遗漏
6. 字段类型要与实际需求匹配
+205
View File
@@ -0,0 +1,205 @@
# GAG工具使用示例 - 带用户确认流程
## 新的工作流程
现在GAG工具支持三步工作流程:
1. `analyze` - 分析现有模块信息
2. `confirm` - 请求用户确认创建计划
3. `execute` - 执行创建操作(需要用户确认)
## 使用示例
### 第一步:分析
```json
{
"action": "analyze",
"requirement": "创建一个图书管理功能"
}
```
### 第二步:确认(支持批量创建多个模块)
```json
{
"action": "confirm",
"executionPlan": {
"packageName": "library",
"packageType": "package",
"needCreatedPackage": true,
"needCreatedModules": true,
"packageInfo": {
"desc": "图书管理包",
"label": "图书管理",
"template": "package",
"packageName": "library"
},
"modulesInfo": [
{
"package": "library",
"tableName": "library_books",
"businessDB": "",
"structName": "Book",
"packageName": "library",
"description": "图书信息",
"abbreviation": "book",
"humpPackageName": "Library",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": false,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "title",
"fieldDesc": "书名",
"fieldType": "string",
"fieldJson": "title",
"dataTypeLong": "255",
"comment": "书名",
"columnName": "title",
"fieldSearchType": "LIKE",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请输入书名",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": {},
"checkDataSource": false,
"fieldIndexType": ""
},
{
"fieldName": "AuthorID",
"fieldDesc": "作者",
"fieldType": "uint",
"fieldJson": "authorId",
"dataTypeLong": "",
"comment": "作者ID",
"columnName": "author_id",
"fieldSearchType": "EQ",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请选择作者",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": {
"dbName": "gva",
"table": "library_authors",
"label": "name",
"value": "id",
"association": 2,
"hasDeletedAt": true
},
"checkDataSource": true,
"fieldIndexType": ""
}
]
},
{
"package": "library",
"tableName": "library_authors",
"businessDB": "",
"structName": "Author",
"packageName": "library",
"description": "作者信息",
"abbreviation": "author",
"humpPackageName": "Library",
"gvaModel": true,
"autoMigrate": true,
"autoCreateResource": true,
"autoCreateApiToSql": true,
"autoCreateMenuToSql": true,
"autoCreateBtnAuth": true,
"onlyTemplate": false,
"isTree": false,
"treeJson": "",
"isAdd": false,
"generateWeb": true,
"generateServer": true,
"fields": [
{
"fieldName": "name",
"fieldDesc": "作者姓名",
"fieldType": "string",
"fieldJson": "name",
"dataTypeLong": "100",
"comment": "作者姓名",
"columnName": "name",
"fieldSearchType": "LIKE",
"fieldSearchHide": false,
"dictType": "",
"form": true,
"table": true,
"desc": true,
"excel": true,
"require": true,
"defaultValue": "",
"errorText": "请输入作者姓名",
"clearable": true,
"sort": false,
"primaryKey": false,
"dataSource": {},
"checkDataSource": false,
"fieldIndexType": ""
}
]
}
]
}
}
```
### 第三步:执行(需要确认参数)
```json
{
"action": "execute",
"executionPlan": {
// ... 同上面的executionPlan
},
"packageConfirm": "yes", // 确认创建包
"modulesConfirm": "yes" // 确认创建模块
}
```
## 确认参数说明
- `packageConfirm`: 当`needCreatedPackage`为true时必需
- "yes": 确认创建包
- "no": 取消创建包(停止后续处理)
- `modulesConfirm`: 当`needCreatedModules`为true时必需
- "yes": 确认创建模块
- "no": 取消创建模块(停止后续处理)
## 取消操作的行为
1. 如果用户在`packageConfirm`中选择"no",系统将停止所有后续处理
2. 如果用户在`modulesConfirm`中选择"no",系统将停止模块创建
3. 任何取消操作都会返回相应的取消消息,不会执行任何创建操作
## 注意事项
1. 必须先调用`confirm`来获取确认信息
2. 在`execute`时必须提供相应的确认参数
3. 确认参数的值必须是"yes"或"no"
4. 如果不需要创建包或模块,则不需要提供对应的确认参数
5. 字段类型支持:string(字符串),richtext(富文本),int(整型),bool(布尔值),float64(浮点型),time.Time(时间),enum(枚举),picture(单图片,字符串),pictures(多图片,json字符串),video(视频,字符串),file(文件,json字符串),json(JSON),array(数组)
File diff suppressed because it is too large Load Diff
+287
View File
@@ -0,0 +1,287 @@
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(`创建前端菜单记录,用于AI编辑器自动添加前端页面时自动创建对应的菜单项。
**重要限制:**
- 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成菜单项,不应调用此工具
- 仅在以下情况使用:1) 单独创建菜单(不涉及模块创建);2) AI编辑器自动添加前端页面时`),
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), &paramReqs); 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
}
+112
View File
@@ -0,0 +1,112 @@
package mcpTool
import (
"context"
"encoding/json"
"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"
"go.uber.org/zap"
)
// 注册工具
func init() {
// 注册工具将在enter.go中统一处理
RegisterTool(&MenuLister{})
}
// MenuListResponse 菜单列表响应结构
type MenuListResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Menus []system.SysBaseMenu `json:"menus"`
TotalCount int `json:"totalCount"`
Description string `json:"description"`
}
// MenuLister 菜单列表工具
type MenuLister struct{}
// New 创建菜单列表工具
func (m *MenuLister) New() mcp.Tool {
return mcp.NewTool("list_all_menus",
mcp.WithDescription(`获取系统中所有菜单信息,包括菜单树结构、路由信息、组件路径等,用于前端编写vue-router时正确跳转
**功能说明:**
- 返回完整的菜单树形结构
- 包含路由配置信息(path、name、component)
- 包含菜单元数据(title、icon、keepAlive等)
- 包含菜单参数和按钮配置
- 支持父子菜单关系展示
**使用场景:**
- 前端路由配置:获取所有菜单信息用于配置vue-router
- 菜单权限管理:了解系统中所有可用的菜单项
- 导航组件开发:构建动态导航菜单
- 系统架构分析:了解系统的菜单结构和页面组织`),
)
}
// Handle 处理菜单列表请求
func (m *MenuLister) Handle(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取所有基础菜单
allMenus, err := m.getAllMenus()
if err != nil {
global.GVA_LOG.Error("获取菜单列表失败", zap.Error(err))
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("获取菜单列表失败: %v", err),
},
},
IsError: true,
}, nil
}
// 构建返回结果
response := MenuListResponse{
Success: true,
Message: "获取菜单列表成功",
Menus: allMenus,
TotalCount: len(allMenus),
Description: "系统中所有菜单信息的标准列表,包含路由配置和组件信息",
}
// 序列化响应
responseJSON, err := json.MarshalIndent(response, "", " ")
if err != nil {
global.GVA_LOG.Error("序列化菜单响应失败", zap.Error(err))
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("序列化响应失败: %v", err),
},
},
IsError: true,
}, nil
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJSON),
},
},
}, nil
}
// getAllMenus 获取所有基础菜单
func (m *MenuLister) getAllMenus() ([]system.SysBaseMenu, error) {
var menus []system.SysBaseMenu
err := global.GVA_DB.Order("sort").Preload("Parameters").Preload("MenuBtn").Find(&menus).Error
if err != nil {
return nil, err
}
return menus, nil
}
+139
View File
@@ -0,0 +1,139 @@
package mcpTool
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/mark3labs/mcp-go/mcp"
)
func init() {
RegisterTool(&RequirementAnalyzer{})
}
type RequirementAnalyzer struct{}
// RequirementAnalysisRequest 需求分析请求
type RequirementAnalysisRequest struct {
UserRequirement string `json:"userRequirement"`
}
// RequirementAnalysisResponse 需求分析响应
type RequirementAnalysisResponse struct {
AIPrompt string `json:"aiPrompt"` // 给AI的提示词
}
// New 返回工具注册信息
func (t *RequirementAnalyzer) New() mcp.Tool {
return mcp.NewTool("requirement_analyzer",
mcp.WithDescription(`**🚀 需求分析工具 - 首选入口工具(最高优先级)**
**⭐ 重要提示:这是所有MCP工具的首选入口,请优先使用!**
**🎯 核心职责:**
将用户的自然语言需求转换为AI可理解的结构化提示词
**📋 工作流程:**
1. 接收用户自然语言需求描述
2. 生成专业的AI提示词,要求AI将需求梳理为清晰的逻辑步骤:
- **1. 第一步功能描述**
- **2. 第二步功能描述**
- **3. 第三步功能描述**
- **...**
3. 指导后续使用 gva_auto_generate 工具进行代码生成
**✅ 适用场景:**
- 用户有新的业务需求需要开发
- 需要创建新的功能模块
- 想要快速搭建业务系统
- 需求描述比较模糊,需要AI帮助梳理
**❌ 不负责的事情:**
- 不生成具体的包名和模块名(交给 gva_auto_generate)
- 不进行代码生成(交给 gva_auto_generate)
- 不创建数据库表结构(交给 gva_auto_generate)
**🔄 推荐工作流:**
requirement_analyzer → gva_auto_generate → 其他辅助工具
`),
mcp.WithString("userRequirement",
mcp.Required(),
mcp.Description("用户的需求描述,支持自然语言,如:'我要做一个猫舍管理系统,用来录入猫的信息,并且记录每只猫每天的活动信息'"),
),
)
}
// Handle 处理工具调用
func (t *RequirementAnalyzer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
userRequirement, ok := request.GetArguments()["userRequirement"].(string)
if !ok || userRequirement == "" {
return nil, errors.New("参数错误:userRequirement 必须是非空字符串")
}
// 分析用户需求
analysisResponse, err := t.analyzeRequirement(userRequirement)
if err != nil {
return nil, fmt.Errorf("需求分析失败: %v", err)
}
// 序列化响应
responseData, err := json.Marshal(analysisResponse)
if err != nil {
return nil, fmt.Errorf("序列化响应失败: %v", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(string(responseData)),
},
}, nil
}
// analyzeRequirement 分析用户需求 - 专注于AI需求传递
func (t *RequirementAnalyzer) analyzeRequirement(userRequirement string) (*RequirementAnalysisResponse, error) {
// 生成AI提示词 - 这是唯一功能
aiPrompt := t.generateAIPrompt(userRequirement)
return &RequirementAnalysisResponse{
AIPrompt: aiPrompt,
}, nil
}
// generateAIPrompt 生成AI提示词 - 要求AI梳理逻辑为1xxx2xxx格式
func (t *RequirementAnalyzer) generateAIPrompt(userRequirement string) string {
prompt := fmt.Sprintf(`# 🤖 AI需求逻辑梳理任务
## 📝 用户原始需求
%s
## 🎯 AI任务要求
请将上述用户需求梳理成清晰的逻辑步骤,格式要求:
**1. 第一步功能描述**
**2. 第二步功能描述**
**3. 第三步功能描述**
**...**
## 📋 梳理要求
- 将需求拆解为具体的功能步骤
- 每个步骤用数字编号(1、2、3...)
- 步骤描述要清晰、具体、可执行
- 按照业务逻辑顺序排列
- 考虑数据流和用户操作流程
## 🔄 后续流程
梳理完成后,请使用 gva_auto_generate 工具进行代码生成:
- gva_auto_generate 会根据梳理的逻辑步骤自动生成包名、模块名
- gva_auto_generate 会设计数据表结构和API接口
- gva_auto_generate 会生成完整的前后端代码
现在请开始梳理用户需求:"%s"`, userRequirement, userRequirement)
return prompt
}
+1 -4
View File
@@ -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)
+3 -5
View File
@@ -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中的数据
+16 -9
View File
@@ -4,28 +4,24 @@ import (
"errors"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"strconv"
"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中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
token := utils.GetToken(c)
if token == "" {
response.NoAuth("未登录或非法访问", c)
response.NoAuth("未登录或非法访问,请登录", c)
c.Abort()
return
}
if jwtService.IsBlacklist(token) {
if isBlacklist(token) {
response.NoAuth("您的帐户异地登陆或令牌失效", c)
utils.ClearToken(c)
c.Abort()
@@ -36,7 +32,7 @@ func JWTAuth() gin.HandlerFunc {
claims, err := j.ParseToken(token)
if err != nil {
if errors.Is(err, utils.TokenExpired) {
response.NoAuth("授权已过期", c)
response.NoAuth("登录已过期,请重新登录", c)
utils.ClearToken(c)
c.Abort()
return
@@ -66,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()
@@ -79,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
}
+1 -1
View File
@@ -27,7 +27,7 @@ type LimitConfig struct {
func (l LimitConfig) LimitWithTime() gin.HandlerFunc {
return func(c *gin.Context) {
if err := l.CheckOrMark(l.GenerationKey(c), l.Expire, l.Limit); err != nil {
c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": err})
c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": err.Error()})
c.Abort()
return
} else {
+1 -5
View File
@@ -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))
}
}
+55
View File
@@ -0,0 +1,55 @@
package middleware
import (
"context"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
// TimeoutMiddleware 创建超时中间件
// 入参 timeout 设置超时时间(例如:time.Second * 5)
// 使用示例 xxx.Get("path",middleware.TimeoutMiddleware(30*time.Second),HandleFunc)
func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
defer cancel()
c.Request = c.Request.WithContext(ctx)
// 使用 buffered channel 避免 goroutine 泄漏
done := make(chan struct{}, 1)
panicChan := make(chan interface{}, 1)
go func() {
defer func() {
if p := recover(); p != nil {
select {
case panicChan <- p:
default:
}
}
select {
case done <- struct{}{}:
default:
}
}()
c.Next()
}()
select {
case p := <-panicChan:
panic(p)
case <-done:
return
case <-ctx.Done():
// 确保服务器超时设置足够长
c.Header("Connection", "close")
c.AbortWithStatusJSON(http.StatusGatewayTimeout, gin.H{
"code": 504,
"msg": "请求超时",
})
return
}
}
}
+7
View File
@@ -34,3 +34,10 @@ func (m *JSONMap) Scan(value interface{}) error {
}
return nil
}
type TreeNode[T any] interface {
GetChildren() []T
SetChildren(children T)
GetID() int
GetParentID() int
}
@@ -0,0 +1,16 @@
package example
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
)
type ExaAttachmentCategory struct {
global.GVA_MODEL
Name string `json:"name" form:"name" gorm:"default:null;type:varchar(255);column:name;comment:分类名称;"`
Pid uint `json:"pid" form:"pid" gorm:"default:0;type:int;column:pid;comment:父节点ID;"`
Children []*ExaAttachmentCategory `json:"children" gorm:"-"`
}
func (ExaAttachmentCategory) TableName() string {
return "exa_attachment_category"
}
@@ -6,10 +6,11 @@ import (
type ExaFileUploadAndDownload struct {
global.GVA_MODEL
Name string `json:"name" gorm:"comment:文件名"` // 文件名
Url string `json:"url" gorm:"comment:文件地址"` // 文件地址
Tag string `json:"tag" gorm:"comment:文件标签"` // 文件标签
Key string `json:"key" gorm:"comment:编号"` // 编号
Name string `json:"name" form:"name" gorm:"column:name;comment:文件名"` // 文件名
ClassId int `json:"classId" form:"classId" gorm:"default:0;type:int;column:class_id;comment:分类id;"` // 分类id
Url string `json:"url" form:"url" gorm:"column:url;comment:文件地址"` // 文件地址
Tag string `json:"tag" form:"tag" gorm:"column:tag;comment:文件标签"` // 文件标签
Key string `json:"key" form:"key" gorm:"column:key;comment:编号"` // 编号
}
func (ExaFileUploadAndDownload) TableName() string {
@@ -0,0 +1,10 @@
package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
)
type ExaAttachmentCategorySearch struct {
ClassId int `json:"classId" form:"classId"`
request.PageInfo
}
+3 -3
View File
@@ -1,11 +1,11 @@
package request
import (
"github.com/gofrs/uuid/v5"
jwt "github.com/golang-jwt/jwt/v4"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
// Custom claims structure
// CustomClaims structure
type CustomClaims struct {
BaseClaims
BufferTime int64
+5 -1
View File
@@ -27,8 +27,12 @@ type AutoCode struct {
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" example:"false"` // 是否自动创建menu
AutoCreateBtnAuth bool `json:"autoCreateBtnAuth" example:"false"` // 是否自动创建按钮权限
OnlyTemplate bool `json:"onlyTemplate" example:"false"` // 是否只生成模板
IsTree bool `json:"isTree" example:"false"` // 是否树形结构
TreeJson string `json:"treeJson" example:"展示的树json字段"` // 展示的树json字段
IsAdd bool `json:"isAdd" example:"false"` // 是否新增
Fields []*AutoCodeField `json:"fields"`
GenerateWeb bool `json:"generateWeb" example:"true"` // 是否生成web
GenerateServer bool `json:"generateServer" example:"true"` // 是否生成server
Module string `json:"-"`
DictTypes []string `json:"-"`
PrimaryField *AutoCodeField `json:"primaryField"`
@@ -154,7 +158,7 @@ func (r *AutoCode) Pretreatment() error {
r.NeedJSON = true
case "time.Time":
r.HasTimer = true
if r.Fields[i].FieldSearchType != "" {
if r.Fields[i].FieldSearchType != "" && r.Fields[i].FieldSearchType != "BETWEEN" && r.Fields[i].FieldSearchType != "NOT BETWEEN" {
r.HasSearchTimer = true
}
}
@@ -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"`
}
@@ -25,6 +25,7 @@ func (r *SysAutoHistoryCreate) Create() model.SysAutoCodeHistory {
Request: r.Request,
Table: r.Table,
StructName: r.StructName,
Abbreviation: r.StructName,
BusinessDB: r.BusinessDB,
Description: r.Description,
Injections: r.Injections,
+2 -2
View File
@@ -1,12 +1,12 @@
package request
// Casbin info structure
// CasbinInfo Casbin info structure
type CasbinInfo struct {
Path string `json:"path"` // 路径
Method string `json:"method"` // 方法
}
// Casbin structure for input parameters
// CasbinInReceive Casbin structure for input parameters
type CasbinInReceive struct {
AuthorityId uint `json:"authorityId"` // 权限id
CasbinInfos []CasbinInfo `json:"casbinInfos"`
+1
View File
@@ -15,6 +15,7 @@ type InitDB struct {
Password string `json:"password"` // 数据库密码
DBName string `json:"dbName" binding:"required"` // 数据库名
DBPath string `json:"dbPath"` // sqlite数据库文件路径
Template string `json:"template"` // postgresql指定template
}
// MysqlEmptyDsn msyql 空数据库 建库链接
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
)
// Add menu authority info structure
// AddMenuAuthorityInfo Add menu authority info structure
type AddMenuAuthorityInfo struct {
Menus []system.SysBaseMenu `json:"menus"`
AuthorityId uint `json:"authorityId"` // 角色ID
+9 -5
View File
@@ -18,7 +18,7 @@ type Register struct {
Email string `json:"email" example:"电子邮箱"`
}
// User login structure
// Login User login structure
type Login struct {
Username string `json:"username"` // 用户名
Password string `json:"password"` // 密码
@@ -26,19 +26,24 @@ type Login struct {
CaptchaId string `json:"captchaId"` // 验证码ID
}
// Modify password structure
// ChangePasswordReq Modify password structure
type ChangePasswordReq struct {
ID uint `json:"-"` // 从 JWT 中提取 user id,避免越权
Password string `json:"password"` // 密码
NewPassword string `json:"newPassword"` // 新密码
}
// Modify user's auth structure
type ResetPassword struct {
ID uint `json:"ID" form:"ID"`
Password string `json:"password" form:"password" gorm:"comment:用户登录密码"` // 用户登录密码
}
// SetUserAuth Modify user's auth structure
type SetUserAuth struct {
AuthorityId uint `json:"authorityId"` // 角色ID
}
// Modify user's auth structure
// SetUserAuthorities Modify user's auth structure
type SetUserAuthorities struct {
ID uint
AuthorityIds []uint `json:"authorityIds"` // 角色ID
@@ -51,7 +56,6 @@ type ChangeUserInfo struct {
AuthorityIds []uint `json:"authorityIds" gorm:"-"` // 角色ID
Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
SideMode string `json:"sideMode" gorm:"comment:用户侧边主题"` // 用户侧边主题
Enable int `json:"enable" gorm:"comment:冻结用户"` //冻结用户
Authorities []system.SysAuthority `json:"-" gorm:"many2many:sys_user_authority;"`
}
@@ -0,0 +1,40 @@
package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"time"
)
type SysVersionSearch struct {
CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"`
VersionName *string `json:"versionName" form:"versionName"`
VersionCode *string `json:"versionCode" form:"versionCode"`
request.PageInfo
}
// ExportVersionRequest 导出版本请求结构体
type ExportVersionRequest struct {
VersionName string `json:"versionName" binding:"required"` // 版本名称
VersionCode string `json:"versionCode" binding:"required"` // 版本号
Description string `json:"description"` // 版本描述
MenuIds []uint `json:"menuIds"` // 选中的菜单ID列表
ApiIds []uint `json:"apiIds"` // 选中的API ID列表
DictIds []uint `json:"dictIds"` // 选中的字典ID列表
}
// ImportVersionRequest 导入版本请求结构体
type ImportVersionRequest struct {
VersionInfo VersionInfo `json:"version" binding:"required"` // 版本信息
ExportMenu []system.SysBaseMenu `json:"menus"` // 菜单数据,直接复用SysBaseMenu
ExportApi []system.SysApi `json:"apis"` // API数据,直接复用SysApi
ExportDictionary []system.SysDictionary `json:"dictionaries"` // 字典数据,直接复用SysDictionary
}
// VersionInfo 版本信息结构体
type VersionInfo struct {
Name string `json:"name" binding:"required"` // 版本名称
Code string `json:"code" binding:"required"` // 版本号
Description string `json:"description"` // 版本描述
ExportTime string `json:"exportTime"` // 导出时间
}
@@ -0,0 +1,14 @@
package response
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
)
// ExportVersionResponse 导出版本响应结构体
type ExportVersionResponse struct {
Version request.VersionInfo `json:"version"` // 版本信息
Menus []system.SysBaseMenu `json:"menus"` // 菜单数据,直接复用SysBaseMenu
Apis []system.SysApi `json:"apis"` // API数据,直接复用SysApi
Dictionaries []system.SysDictionary `json:"dictionaries"` // 字典数据,直接复用SysDictionary
}
@@ -16,6 +16,7 @@ type SysAutoCodeHistory struct {
Package string `json:"package" gorm:"column:package;comment:模块名/插件名"`
Request string `json:"request" gorm:"type:text;column:request;comment:前端传入的结构化信息"`
StructName string `json:"structName" gorm:"column:struct_name;comment:结构体名称"`
Abbreviation string `json:"abbreviation" gorm:"column:abbreviation;comment:结构体名称缩写"`
BusinessDB string `json:"businessDb" gorm:"column:business_db;comment:业务库"`
Description string `json:"description" gorm:"column:description;comment:Struct中文名称"`
Templates map[string]string `json:"template" gorm:"serializer:json;type:text;column:templates;comment:模板信息"`
+21 -20
View File
@@ -6,35 +6,36 @@ import (
type SysBaseMenu struct {
global.GVA_MODEL
MenuLevel uint `json:"-"`
ParentId uint `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
MenuLevel uint `json:"-"`
ParentId uint `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
}
type Meta struct {
ActiveName string `json:"activeName" gorm:"comment:高亮菜单"`
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
DefaultMenu bool `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中)
Title string `json:"title" gorm:"comment:菜单名"` // 菜单名
Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标
CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab
ActiveName string `json:"activeName" gorm:"comment:高亮菜单"`
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
DefaultMenu bool `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中)
Title string `json:"title" gorm:"comment:菜单名"` // 菜单名
Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标
CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab
TransitionType string `json:"transitionType" gorm:"comment:路由切换动画"` // 路由切换动画
}
type SysBaseMenuParameter struct {
global.GVA_MODEL
SysBaseMenuID uint
Type string `json:"type" gorm:"comment:地址栏携带参数为params还是query"` // 地址栏携带参数为params还是query
Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key
Value string `json:"value" gorm:"comment:地址栏携带参数的值"` // 地址栏携带参数的值
Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key
Value string `json:"value" gorm:"comment:地址栏携带参数的值"` // 地址栏携带参数的值
}
func (SysBaseMenu) TableName() string {
+1 -1
View File
@@ -3,7 +3,7 @@ package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
"github.com/gofrs/uuid/v5"
"github.com/google/uuid"
)
type Login interface {
+20
View File
@@ -0,0 +1,20 @@
// 自动生成模板SysVersion
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
)
// 版本管理 结构体 SysVersion
type SysVersion struct {
global.GVA_MODEL
VersionName *string `json:"versionName" form:"versionName" gorm:"comment:版本名称;column:version_name;size:255;" binding:"required"` //版本名称
VersionCode *string `json:"versionCode" form:"versionCode" gorm:"comment:版本号;column:version_code;size:100;" binding:"required"` //版本号
Description *string `json:"description" form:"description" gorm:"comment:版本描述;column:description;size:500;"` //版本描述
VersionData *string `json:"versionData" form:"versionData" gorm:"comment:版本数据JSON;column:version_data;type:longtext;postgres:type:text;sqlite:type:text;"` //版本数据
}
// TableName 版本管理 SysVersion自定义表名 sys_versions
func (SysVersion) TableName() string {
return "sys_versions"
}
+4 -4
View File
@@ -8,10 +8,10 @@ import (
// Info 公告 结构体
type Info struct {
global.GVA_MODEL
Title string `json:"title" form:"title" gorm:"column:title;comment:公告标题;"` //标题
Content string `json:"content" form:"content" gorm:"column:content;comment:公告内容;type:text;"` //内容
UserID *int `json:"userID" form:"userID" gorm:"column:user_id;comment:发布者;"` //作者
Attachments datatypes.JSON `json:"attachments" form:"attachments" gorm:"column:attachments;comment:相关附件;"swaggertype:"array,object"` //附件
Title string `json:"title" form:"title" gorm:"column:title;comment:公告标题;"` //标题
Content string `json:"content" form:"content" gorm:"column:content;comment:公告内容;type:text;"` //内容
UserID *int `json:"userID" form:"userID" gorm:"column:user_id;comment:发布者;"` //作者
Attachments datatypes.JSON `json:"attachments" form:"attachments" gorm:"column:attachments;comment:相关附件;" swaggertype:"array,object"` //附件
}
// TableName 公告 Info自定义表名 gva_announcements_info
+10 -7
View File
@@ -14,6 +14,7 @@
global.GVA_CONFIG.Email.Nickname,
global.GVA_CONFIG.Email.Port,
global.GVA_CONFIG.Email.IsSSL,
global.GVA_CONFIG.Email.IsLoginAuth,
))
同样也可以再传入时写死
@@ -26,6 +27,7 @@
"登录密钥",
465,
true,
true,
))
### 2. 配置说明
@@ -34,13 +36,14 @@
//其中 Form 和 Secret 通常来说就是用户名和密码
type Email struct {
To string // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 此处配置主要用于发送错误监控邮件
From string // 发件人 你自己要发邮件的邮箱
Host string // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string // 昵称 发件人昵称 自定义即可 可以不填
Port int // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool // 是否SSL 是否开启SSL
To string // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 此处配置主要用于发送错误监控邮件
From string // 发件人 你自己要发邮件的邮箱
Host string // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string // 昵称 发件人昵称 自定义即可 可以不填
Port int // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool // 是否SSL 是否开启SSL
IsLoginAuth bool // 是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等)
}
#### 2-2 入参结构说明
//其中 Form 和 Secret 通常来说就是用户名和密码
+8 -7
View File
@@ -1,11 +1,12 @@
package config
type Email struct {
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 是否开启SSL
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 是否开启SSL
IsLoginAuth bool `mapstructure:"is-loginauth" json:"is-loginauth" yaml:"is-loginauth"` // 是否LoginAuth 是否使用LoginAuth认证
}
+2 -1
View File
@@ -8,7 +8,7 @@ import (
type emailPlugin struct{}
func CreateEmailPlug(To, From, Host, Secret, Nickname string, Port int, IsSSL bool) *emailPlugin {
func CreateEmailPlug(To, From, Host, Secret, Nickname string, Port int, IsSSL bool, IsLoginAuth bool) *emailPlugin {
global.GlobalConfig.To = To
global.GlobalConfig.From = From
global.GlobalConfig.Host = Host
@@ -16,6 +16,7 @@ func CreateEmailPlug(To, From, Host, Secret, Nickname string, Port int, IsSSL bo
global.GlobalConfig.Nickname = Nickname
global.GlobalConfig.Port = Port
global.GlobalConfig.IsSSL = IsSSL
global.GlobalConfig.IsLoginAuth = IsLoginAuth
return &emailPlugin{}
}

Some files were not shown because too many files have changed in this diff Show More