From 82c5020e71b9d1a63a656cecb1f708bcd5da0ebd Mon Sep 17 00:00:00 2001 From: Echo <1711788888@qq.com> Date: Fri, 10 Apr 2026 17:57:48 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E7=B2=BE=E7=AE=80=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 215 + README.md | 65 +- .../server/gva-server-configmap.yaml | 8 +- .../server/gva-server-deployment.yaml | 12 +- .../kubernetes/server/gva-server-service.yaml | 8 +- server/README.md | 3 +- server/api/v1/system/enter.go | 3 - server/api/v1/system/sys_export_template.go | 456 -- server/api/v1/system/sys_version.go | 486 --- server/config.docker.yaml | 10 +- server/core/server.go | 2 +- server/docs/docs.go | 3820 ++++++----------- server/docs/swagger.json | 3810 ++++++---------- server/docs/swagger.yaml | 2574 ++++------- server/global/version.go | 4 +- server/initialize/ensure_tables.go | 8 +- server/initialize/gorm.go | 4 - server/initialize/router.go | 38 +- server/main.go | 4 +- server/mcp/client/client_test.go | 8 +- server/mcp/menu_creator.go | 4 +- .../system/request/sys_export_template.go | 14 - server/model/system/request/sys_menu.go | 2 +- server/model/system/request/sys_version.go | 40 - server/model/system/response/sys_version.go | 14 - server/model/system/sys_export_template.go | 46 - server/model/system/sys_version.go | 20 - server/router/system/enter.go | 4 - server/router/system/sys_export_template.go | 35 - server/router/system/sys_version.go | 25 - server/service/system/enter.go | 2 - server/service/system/sys_export_template.go | 724 ---- server/service/system/sys_initdb_pgsql.go | 13 +- server/service/system/sys_version.go | 230 - server/source/system/api.go | 23 +- server/source/system/casbin.go | 19 - server/source/system/excel_template.go | 75 - server/source/system/menu.go | 49 +- web-admin/docs/system-inventory.md | 8 +- web-admin/src/App.tsx | 2 + .../src/features/apis/ApiManagementPage.tsx | 648 ++- web-admin/src/features/auth/InitPage.tsx | 305 ++ web-admin/src/features/auth/LoginPage.tsx | 131 +- .../src/features/discovery/systemInventory.ts | 45 +- .../src/features/menus/MenuManagementPage.tsx | 626 ++- .../features/menus/menuComponentCatalog.ts | 16 +- .../src/features/roles/RoleManagementPage.tsx | 652 ++- web-admin/src/index.css | 51 +- web-admin/src/lib/api.ts | 43 + web-admin/src/lib/menu.ts | 15 +- web-admin/src/lib/tree.ts | 10 + .../router/pages/admin/sysVersion/page.tsx | 7 - .../pages/systemTools/exportTemplate/page.tsx | 7 - .../pages/systemTools/formCreate/page.tsx | 7 - web-admin/src/types/system.ts | 47 +- 55 files changed, 5785 insertions(+), 9712 deletions(-) create mode 100644 AGENTS.md delete mode 100644 server/api/v1/system/sys_export_template.go delete mode 100644 server/api/v1/system/sys_version.go delete mode 100644 server/model/system/request/sys_export_template.go delete mode 100644 server/model/system/request/sys_version.go delete mode 100644 server/model/system/response/sys_version.go delete mode 100644 server/model/system/sys_export_template.go delete mode 100644 server/model/system/sys_version.go delete mode 100644 server/router/system/sys_export_template.go delete mode 100644 server/router/system/sys_version.go delete mode 100644 server/service/system/sys_export_template.go delete mode 100644 server/service/system/sys_version.go delete mode 100644 server/source/system/excel_template.go create mode 100644 web-admin/src/features/auth/InitPage.tsx delete mode 100644 web-admin/src/router/pages/admin/sysVersion/page.tsx delete mode 100644 web-admin/src/router/pages/systemTools/exportTemplate/page.tsx delete mode 100644 web-admin/src/router/pages/systemTools/formCreate/page.tsx diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3acfac2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,215 @@ +# AGENTS.md + +## 1. 文件作用 + +本文件定义本仓库的协作约束,供人类开发者和代码代理统一遵循。 + +目标只有三个: + +- 先分清模块边界,再动代码。 +- 新功能默认落在 `server` 和 `web-admin`。 +- `web` 只作为旧版参考实现,不参与当前交付。 + +## 2. 项目总览 + +本仓库是一个前后端分离项目,当前有效开发面如下: + +| 目录 | 角色 | 技术栈 | 是否允许修改 | +| --- | --- | --- | --- | +| `server` | 后端服务 | Go 1.24、Gin、Gorm、Viper、Zap | 允许 | +| `web-admin` | 新管理后台前端 | React 19、TypeScript、Vite、Ant Design、Zustand、React Query | 允许 | +| `web` | 原版 Vue 管理后台 | Vue | 不允许 | + +当前默认管理后台目录已经在根目录 `Makefile` 中指定为 `WEB_DIR ?= web-admin`,后续构建与打包应以 `web-admin` 为准。 + +## 3. 工作边界 + +### 3.1 允许修改的目录 + +- `server/**` +- `web-admin/**` +- 根目录与部署目录中和当前任务直接相关的文档、脚本、配置文件 + +### 3.2 禁止修改的目录 + +- `web/**` + +`web` 是旧版 Vue 后台,仅用于界面、接口命名、交互流程和功能覆盖范围参考。不得在该目录内新增、删除或修改任何文件。 + +### 3.3 修改原则 + +- 如果 `web-admin` 缺少某个页面或交互,可以参考 `web` 的实现思路,但要用 React 方式重写。 +- 如果 `server` 的接口能力不足,应优先补齐后端,再让 `web-admin` 接入。 +- 不为了兼容旧实现而把新代码继续写回 `web`。 + +## 4. 目录说明 + +### 4.1 `server` + +后端采用典型分层结构,开发时按职责落位: + +| 目录 | 职责 | +| --- | --- | +| `server/api` | HTTP 接口入口。负责参数接收、调用 service、返回响应。 | +| `server/service` | 业务逻辑层。负责业务编排、事务边界、数据写入策略。 | +| `server/router` | 路由注册。负责把接口暴露给 Gin。 | +| `server/model` | 数据模型、请求结构、响应结构。 | +| `server/middleware` | 中间件。负责鉴权、日志、限流、错误处理等横切逻辑。 | +| `server/initialize` | 初始化流程。负责数据库、Redis、路由、校验器、定时器等装配。 | +| `server/config` | 配置结构定义。与 `config.yaml` / `config.docker.yaml` 对应。 | +| `server/utils` | 通用工具,不承载具体业务语义。 | +| `server/mcp` | MCP 相关能力。新增此类能力时优先放在这里。 | +| `server/docs` | Swagger 产物目录。只有接口变更需要更新文档时才修改。 | + +后端变更约束: + +- 不要把业务逻辑堆进 `api` 层。 +- 不要把页面专属拼装逻辑散落到 `utils`。 +- 新接口需要同时检查路由注册、权限、请求结构和返回结构是否完整。 +- 涉及配置项变更时,必须同步检查 `server/config.yaml`、`server/config.docker.yaml` 和对应结构体。 + +### 4.2 `web-admin` + +新后台是当前主开发前端,目录组织以功能模块为中心: + +| 目录 | 职责 | +| --- | --- | +| `web-admin/src/features` | 业务页面与模块实现。页面优先落在这里。 | +| `web-admin/src/router` | 路由定义与页面装配。 | +| `web-admin/src/lib` | 请求封装、日期、树结构、存储等通用前端基础设施。 | +| `web-admin/src/store` | 全局状态。当前主要用于鉴权与会话信息。 | +| `web-admin/src/types` | 共享类型定义。 | +| `web-admin/public` | 静态资源。 | + +前端变更约束: + +- 新页面优先沿用现有 `features/*Page.tsx` 的组织方式。 +- 接口请求优先收敛到 `web-admin/src/lib` 或对应 feature 内部的请求层,不要在组件里散写 HTTP 细节。 +- 类型优先显式声明,不要用大量 `any`。 +- 页面以管理后台场景为前提,优先保证信息密度、操作链路和可维护性。 + +### 4.3 `web` + +该目录只读。允许查看,不允许编辑。 + +适用场景: + +- 查旧版菜单结构 +- 查既有接口命名 +- 查页面字段和交互流程 +- 查某个能力在旧后台中的展示方式 + +不适用场景: + +- 直接在这里修 bug +- 在这里补新功能 +- 为了图省事复制构建配置并改回旧目录 + +## 5. 本地开发 + +### 5.1 环境基线 + +| 项目 | 要求 | +| --- | --- | +| Go | 1.24.x | +| Node.js | >= 18.16,建议 20+ | +| 包管理器 | `web-admin` 使用 `npm` | + +### 5.2 常用命令 + +#### 后端 + +```bash +cd /Users/lee/GolandProjects/Go-Web-Template/server +go run . +``` + +#### 新后台前端 + +```bash +cd /Users/lee/GolandProjects/Go-Web-Template/web-admin +npm install +npm run dev +``` + +默认约定: + +- `web-admin` 开发地址:`http://localhost:8081` +- 后端默认代理目标:`http://127.0.0.1:8888` + +#### Swagger 文档 + +```bash +cd /Users/lee/GolandProjects/Go-Web-Template/server +swag init +``` + +#### 根目录构建 + +```bash +cd /Users/lee/GolandProjects/Go-Web-Template +make build +``` + +## 6. 变更落地规则 + +### 6.1 后端任务 + +遇到后端需求时,优先按下面顺序检查: + +1. 是否已有对应 `model`、`service`、`api`、`router`。 +2. 是否需要补权限、菜单、字典、参数或日志记录。 +3. 是否影响 Swagger 文档、配置文件或初始化逻辑。 +4. 是否需要补测试,至少覆盖纯工具函数和稳定业务逻辑。 + +### 6.2 前端任务 + +遇到管理后台需求时,优先按下面顺序处理: + +1. 先确认目标页面属于 `web-admin` 哪个 feature。 +2. 需要参考旧实现时,只读取 `web`,不在 `web` 动手。 +3. 先整理接口契约,再组织页面状态和交互。 +4. 提交前至少完成构建或 lint 校验。 + +### 6.3 跨端联动任务 + +如果需求同时涉及前后端,推荐顺序如下: + +1. 先定义或确认接口契约。 +2. 先补 `server`,保证接口可运行。 +3. 再接 `web-admin` 页面和交互。 +4. 最后联调并确认代理、鉴权、菜单和权限表现一致。 + +## 7. 验证要求 + +只要改动了对应模块,至少执行一项最低验证: + +| 改动范围 | 最低验证 | +| --- | --- | +| `server` Go 代码 | `go test ./...` 或最小相关包测试 | +| `web-admin` 前端代码 | `npm run build` 或 `npm run lint` | +| 接口定义变更 | 补充或更新 Swagger 文档,并做一次接口联调 | +| 构建脚本或部署配置 | 执行对应构建命令,确认没有明显路径错误 | + +如果因为外部依赖无法完成验证,需要明确说明阻塞点,不得省略。 + +## 8. 代码风格补充 + +- 文档、注释、提交说明优先使用清晰中文。 +- 注释只说明职责、边界、约束和必要语义,不复述代码表面行为。 +- 新增后端代码遵循现有分层,不主动引入跨层调用。 +- 新增前端代码遵循现有 React + TypeScript + Ant Design 方案,不回退到旧 Vue 写法。 +- 非必要不新增大型依赖。新增依赖前先确认当前栈无法直接解决问题。 + +## 9. 决策优先级 + +出现冲突时,按下面顺序决策: + +1. 不修改 `web` +2. 以 `web-admin` 作为当前管理后台主实现 +3. 以 `server` 的真实接口和数据结构为准 +4. 参考旧版 `web` 的业务行为,而不是复制其实现细节 + +## 10. 一句话约束 + +这个仓库的当前主线是:维护 `server`,建设 `web-admin`,只参考 `web`,绝不修改 `web`。 diff --git a/README.md b/README.md index cfae15a..b68193f 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@
- - + + - +
@@ -20,7 +20,7 @@ ## 支持claw生态 -[🦞GvaClaw](https://plugin.gin-vue-admin.com/details/159) +[🦞GvaClaw](https://plugin.gin-react-admin.com/details/159) ## ✨一分钟生成前后端基础代码 @@ -43,29 +43,29 @@ # 项目文档 -[在线文档](https://www.gin-vue-admin.com) : https://www.gin-vue-admin.com +[在线文档](https://www.gin-react-admin.com) : https://www.gin-react-admin.com -[初始化](https://www.gin-vue-admin.com/guide/start-quickly/initialization.html) +[初始化](https://www.gin-react-admin.com/guide/start-quickly/initialization.html) [从环境到部署教学视频](https://www.bilibili.com/video/BV1Rg411u7xH) -[开发教学](https://www.gin-vue-admin.com/guide/start-quickly/env.html) (贡献者: LLemonGreen And Fann) +[开发教学](https://www.gin-react-admin.com/guide/start-quickly/env.html) (贡献者: LLemonGreen And Fann) [交流社区](https://support.qq.com/products/371961) -[插件市场](https://plugin.gin-vue-admin.com/) +[插件市场](https://plugin.gin-react-admin.com/) -[软件著作权证书](https://www.gin-vue-admin.com/copyright.pdf) +[软件著作权证书](https://www.gin-react-admin.com/copyright.pdf) # 重要提示 1.本项目从起步到开发到部署均有文档和详细视频教程 -2.本项目需要您有一定的golang和vue基础 +2.本项目需要您有一定的golang和react基础 -3.您完全可以通过我们的教程和文档完成一切操作,因此我们不再提供免费的技术服务,如需服务请进行[付费支持](https://www.gin-vue-admin.com/coffee/payment.html) +3.您完全可以通过我们的教程和文档完成一切操作,因此我们不再提供免费的技术服务,如需服务请进行[付费支持](https://www.gin-react-admin.com/coffee/payment.html) -4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,以及日志和代码中所包含的版权声明信息。所需保留信息均为文案性质,不会影响任何业务内容,如决定商用【产生收益的商业行为均在商用行列】或者必须剔除请[购买授权](https://plugin.gin-vue-admin.com/licenseindex.html) +4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,以及日志和代码中所包含的版权声明信息。所需保留信息均为文案性质,不会影响任何业务内容,如决定商用【产生收益的商业行为均在商用行列】或者必须剔除请[购买授权](https://plugin.gin-react-admin.com/licenseindex.html) \ @@ -75,20 +75,20 @@ ### 1.1 项目介绍 -> Gin-vue-admin是一个基于 [vue](https://vuejs.org) 和 [gin](https://gin-gonic.com) 开发的全栈前后端分离的开发基础平台,集成jwt鉴权,动态路由,动态菜单,casbin鉴权,表单生成器,代码生成器等功能,提供多种示例文件,让您把更多时间专注在业务开发上。 +> Gin-React-Admin 是一个基于 [react](https://react.dev) 和 [gin](https://gin-gonic.com) 开发的全栈前后端分离开发基础平台,集成 JWT 鉴权、动态路由、动态菜单、Casbin 鉴权与多种后台治理能力,让您把更多时间专注在业务开发上。 -[在线预览](http://demo.gin-vue-admin.com): http://demo.gin-vue-admin.com +[在线预览](http://demo.gin-react-admin.com): http://demo.gin-react-admin.com 测试用户名:admin 测试密码:123456 ### 1.2 贡献指南 -Hi! 首先感谢你使用 gin-vue-admin。 +Hi! 首先感谢你使用 Gin-React-Admin。 -Gin-vue-admin 是一套为快速研发准备的一整套前后端分离架构式的开源框架,旨在快速搭建中小型项目。 +Gin-React-Admin 是一套为快速研发准备的一整套前后端分离架构式的开源框架,旨在快速搭建中小型项目。 -Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-admin 贡献代码或提供建议,请阅读以下内容。 +Gin-React-Admin 的成长离不开大家的支持,如果你愿意为 Gin-React-Admin 贡献代码或提供建议,请阅读以下内容。 #### 1.2.1 Issue 规范 - issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。 @@ -114,12 +114,12 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm ### 2.1 server项目 -使用 `Goland` 等编辑工具,打开server目录,不可以打开 gin-vue-admin 根目录 +使用 `Goland` 等编辑工具,打开server目录,不可以打开 gin-react-admin 根目录 ```bash # 克隆项目 -git clone https://github.com/flipped-aurora/gin-vue-admin.git +git clone https://github.com/flipped-aurora/gin-react-admin.git # 进入server文件夹 cd server @@ -165,7 +165,7 @@ swag init #### 2.4.1 开发 -使用`VSCode`打开根目录下的工作区文件`gin-vue-admin.code-workspace`,在边栏可以看到三个虚拟目录:`backend`、`frontend`、`root`。 +使用`VSCode`打开根目录下的工作区文件`gin-react-admin.code-workspace`,在边栏可以看到三个虚拟目录:`backend`、`frontend`、`root`。 #### 2.4.2 运行/调试 @@ -194,7 +194,7 @@ swag init ### 4.1 系统架构图 -![系统架构图](http://qmplusimg.henrongyi.top/gva/gin-vue-admin.png) +![系统架构图](http://qmplusimg.henrongyi.top/gva/gin-react-admin.png) ### 4.2 前端详细设计图 (提供者:baobeisuper) @@ -219,7 +219,7 @@ swag init ├── packfile (静态文件打包) ├── resource (静态资源文件夹) │ ├── excel (excel导入导出默认路径) - │ ├── page (表单生成器) + │ ├── page (页面静态资源目录) │ └── template (模板) ├── router (路由层) ├── service (service层) @@ -242,7 +242,7 @@ swag init │ ├── components -- 全局组件 │ ├── core -- gva 组件包 │ │ ├── config.js -- gva网站配置文件 - │ │ ├── gin-vue-admin.js -- 注册欢迎文件 + │ │ ├── gin-react-admin.js -- 注册欢迎文件 │ │ └── global.js -- 统一导入文件 │ ├── directive -- v-auth 注册文件 │ ├── main.js -- 主文件 @@ -313,11 +313,10 @@ swag init - 配置管理:配置文件可前台修改(在线体验站点不开放此功能)。 - 条件搜索:增加条件搜索示例。 - restful示例:可以参考用户管理模块中的示例API。 - - 前端文件参考: [web/src/view/superAdmin/api/api.vue](https://github.com/flipped-aurora/gin-vue-admin/blob/master/web/src/view/superAdmin/api/api.vue) - - 后台文件参考: [server/router/sys_api.go](https://github.com/flipped-aurora/gin-vue-admin/blob/master/server/router/sys_api.go) + - 前端文件参考: [web/src/view/superAdmin/api/api.vue](https://github.com/flipped-aurora/gin-react-admin/blob/master/web/src/view/superAdmin/api/api.vue) + - 后台文件参考: [server/router/sys_api.go](https://github.com/flipped-aurora/gin-react-admin/blob/master/server/router/sys_api.go) - 多点登录限制:需要在`config.yaml`中把`system`中的`use-multipoint`修改为true(需要自行配置Redis和Config中的Redis参数,测试阶段,有bug请及时反馈)。 - 分片上传:提供文件分片上传和大文件分片上传功能示例。 -- 表单生成器:表单生成器借助 [@Variant Form](https://github.com/vform666/variant-form) 。 - 代码生成器:后台基础逻辑以及简单curd的代码生成器。 ## 6. 知识库 @@ -346,7 +345,7 @@ swag init > bilibili:https://space.bilibili.com/322210472/channel/detail?cid=126418&ctype=0 -(5)gin-vue-admin 版本更新介绍视频 +(5)gin-react-admin 版本更新介绍视频 > bilibili:https://www.bilibili.com/video/BV1kv4y1g7nT @@ -369,21 +368,21 @@ decodeBytes, err := base64.StdEncoding.DecodeString(str) fmt.Println(decodeBytes, err) ``` -### [关于我们](https://www.gin-vue-admin.com/about/join.html) +### [关于我们](https://www.gin-react-admin.com/about/join.html) ## 8. 贡献者 -感谢您对gin-vue-admin的贡献! +感谢您对gin-react-admin的贡献! - - Contribution Leaderboard + + Contribution Leaderboard ## 9. 捐赠 -如果你觉得这个项目对你有帮助,你可以请作者喝饮料 :tropical_drink: [点我](https://www.gin-vue-admin.com/coffee/index.html) +如果你觉得这个项目对你有帮助,你可以请作者喝饮料 :tropical_drink: [点我](https://www.gin-react-admin.com/coffee/index.html) ## 10. 注意事项 -请严格遵守Apache 2.0协议并保留作品声明,去除版权信息请务必[获取授权](https://plugin.gin-vue-admin.com/license) +请严格遵守Apache 2.0协议并保留作品声明,去除版权信息请务必[获取授权](https://plugin.gin-react-admin.com/license) 未授权去除版权信息将依法追究法律责任 diff --git a/deploy/kubernetes/server/gva-server-configmap.yaml b/deploy/kubernetes/server/gva-server-configmap.yaml index e75b5c7..6808ca9 100644 --- a/deploy/kubernetes/server/gva-server-configmap.yaml +++ b/deploy/kubernetes/server/gva-server-configmap.yaml @@ -3,12 +3,12 @@ kind: ConfigMap metadata: name: config.yaml annotations: - flipped-aurora/gin-vue-admin: backend - github: "https://github.com/flipped-aurora/gin-vue-admin.git" + flipped-aurora/gin-react-admin: backend + github: "https://github.com/flipped-aurora/gin-react-admin.git" app.kubernetes.io/version: 0.0.1 labels: app: gva-server - version: gva-vue3 + version: gva-react data: config.yaml: | # git.echol.cn/loser/Go-Web-Template/server Global Configuration @@ -125,7 +125,7 @@ data: region: 'ap-shanghai' secret-id: 'xxxxxxxx' secret-key: 'xxxxxxxx' - base-url: 'https://gin.vue.admin' + base-url: 'https://gin-react-admin.com' path-prefix: 'git.echol.cn/loser/Go-Web-Template/server' # excel configuration diff --git a/deploy/kubernetes/server/gva-server-deployment.yaml b/deploy/kubernetes/server/gva-server-deployment.yaml index b0cc15c..ddfccf8 100644 --- a/deploy/kubernetes/server/gva-server-deployment.yaml +++ b/deploy/kubernetes/server/gva-server-deployment.yaml @@ -3,26 +3,26 @@ kind: Deployment metadata: name: gva-server annotations: - flipped-aurora/gin-vue-admin: backend - github: "https://github.com/flipped-aurora/gin-vue-admin.git" + flipped-aurora/gin-react-admin: backend + github: "https://github.com/flipped-aurora/gin-react-admin.git" app.kubernetes.io/version: 0.0.1 labels: app: gva-server - version: gva-vue3 + version: gva-react spec: replicas: 1 selector: matchLabels: app: gva-server - version: gva-vue3 + version: gva-react template: metadata: labels: app: gva-server - version: gva-vue3 + version: gva-react spec: containers: - - name: gin-vue-admin-container + - name: gin-react-admin-container image: registry.cn-hangzhou.aliyuncs.com/gva/server:latest imagePullPolicy: Always ports: diff --git a/deploy/kubernetes/server/gva-server-service.yaml b/deploy/kubernetes/server/gva-server-service.yaml index 17aaef2..a8b8ef6 100644 --- a/deploy/kubernetes/server/gva-server-service.yaml +++ b/deploy/kubernetes/server/gva-server-service.yaml @@ -3,16 +3,16 @@ kind: Service metadata: name: gva-server annotations: - flipped-aurora/gin-vue-admin: backend - github: "https://github.com/flipped-aurora/gin-vue-admin.git" + flipped-aurora/gin-react-admin: backend + github: "https://github.com/flipped-aurora/gin-react-admin.git" app.kubernetes.io/version: 0.0.1 labels: app: gva-server - version: gva-vue3 + version: gva-react spec: selector: app: gva-server - version: gva-vue3 + version: gva-react ports: - port: 8888 name: http diff --git a/server/README.md b/server/README.md index 9a34870..181e578 100644 --- a/server/README.md +++ b/server/README.md @@ -43,7 +43,7 @@ | `packfile` | 静态文件打包 | 静态文件打包 | | `resource` | 静态资源文件夹 | 负责存放静态文件 | | `--excel` | excel导入导出默认路径 | excel导入导出默认路径 | -| `--page` | 表单生成器 | 表单生成器 打包后的dist | +| `--page` | 页面静态资源目录 | 历史页面资源输出目录 | | `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 | | `router` | 路由层 | 路由层 | | `service` | service层 | 存放业务逻辑问题 | @@ -51,4 +51,3 @@ | `utils` | 工具包 | 工具函数封装 | | `--timer` | timer | 定时器接口封装 | | `--upload` | oss | oss接口封装 | - diff --git a/server/api/v1/system/enter.go b/server/api/v1/system/enter.go index 035a2b1..3e8defc 100644 --- a/server/api/v1/system/enter.go +++ b/server/api/v1/system/enter.go @@ -15,10 +15,8 @@ type ApiGroup struct { OperationRecordApi DictionaryDetailApi AuthorityBtnApi - SysExportTemplateApi McpApi SysParamsApi - SysVersionApi SysErrorApi LoginLogApi ApiTokenApi @@ -39,7 +37,6 @@ var ( sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService - sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService mcpService = service.ServiceGroupApp.SystemServiceGroup.McpService sysErrorService = service.ServiceGroupApp.SystemServiceGroup.SysErrorService loginLogService = service.ServiceGroupApp.SystemServiceGroup.LoginLogService diff --git a/server/api/v1/system/sys_export_template.go b/server/api/v1/system/sys_export_template.go deleted file mode 100644 index 98512c3..0000000 --- a/server/api/v1/system/sys_export_template.go +++ /dev/null @@ -1,456 +0,0 @@ -package system - -import ( - "fmt" - "net/http" - "net/url" - "sync" - "time" - - "git.echol.cn/loser/Go-Web-Template/server/global" - "git.echol.cn/loser/Go-Web-Template/server/model/common/request" - "git.echol.cn/loser/Go-Web-Template/server/model/common/response" - "git.echol.cn/loser/Go-Web-Template/server/model/system" - systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request" - "git.echol.cn/loser/Go-Web-Template/server/service" - "git.echol.cn/loser/Go-Web-Template/server/utils" - "github.com/gin-gonic/gin" - "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 { -} - -var sysExportTemplateService = service.ServiceGroupApp.SystemServiceGroup.SysExportTemplateService - -// PreviewSQL 预览最终生成的SQL -// @Tags SysExportTemplate -// @Summary 预览最终生成的SQL(不执行查询,仅返回SQL字符串) -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param templateID query string true "导出模板ID" -// @Param params query string false "查询参数编码字符串,参考 ExportExcel 组件" -// @Success 200 {object} response.Response{data=map[string]string} "获取成功" -// @Router /sysExportTemplate/previewSQL [get] -func (sysExportTemplateApi *SysExportTemplateApi) PreviewSQL(c *gin.Context) { - templateID := c.Query("templateID") - if templateID == "" { - response.FailWithMessage("模板ID不能为空", c) - return - } - - // 直接复用导出接口的参数组织方式:使用 URL Query,其中 params 为内部编码的查询字符串 - queryParams := c.Request.URL.Query() - - if sqlPreview, err := sysExportTemplateService.PreviewSQL(templateID, queryParams); err != nil { - global.GVA_LOG.Error("获取失败!", zap.Error(err)) - response.FailWithMessage("获取失败", c) - } else { - response.OkWithData(gin.H{"sql": sqlPreview}, c) - } -} - -// CreateSysExportTemplate 创建导出模板 -// @Tags SysExportTemplate -// @Summary 创建导出模板 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param data body system.SysExportTemplate true "创建导出模板" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" -// @Router /sysExportTemplate/createSysExportTemplate [post] -func (sysExportTemplateApi *SysExportTemplateApi) CreateSysExportTemplate(c *gin.Context) { - var sysExportTemplate system.SysExportTemplate - err := c.ShouldBindJSON(&sysExportTemplate) - if err != nil { - response.FailWithMessage(err.Error(), c) - return - } - verify := utils.Rules{ - "Name": {utils.NotEmpty()}, - } - if err := utils.Verify(sysExportTemplate, verify); err != nil { - response.FailWithMessage(err.Error(), c) - return - } - if err := sysExportTemplateService.CreateSysExportTemplate(&sysExportTemplate); err != nil { - global.GVA_LOG.Error("创建失败!", zap.Error(err)) - response.FailWithMessage("创建失败", c) - } else { - response.OkWithMessage("创建成功", c) - } -} - -// DeleteSysExportTemplate 删除导出模板 -// @Tags SysExportTemplate -// @Summary 删除导出模板 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param data body system.SysExportTemplate true "删除导出模板" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" -// @Router /sysExportTemplate/deleteSysExportTemplate [delete] -func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) { - var sysExportTemplate system.SysExportTemplate - err := c.ShouldBindJSON(&sysExportTemplate) - if err != nil { - response.FailWithMessage(err.Error(), c) - return - } - if err := sysExportTemplateService.DeleteSysExportTemplate(sysExportTemplate); err != nil { - global.GVA_LOG.Error("删除失败!", zap.Error(err)) - response.FailWithMessage("删除失败", c) - } else { - response.OkWithMessage("删除成功", c) - } -} - -// DeleteSysExportTemplateByIds 批量删除导出模板 -// @Tags SysExportTemplate -// @Summary 批量删除导出模板 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param data body request.IdsReq true "批量删除导出模板" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}" -// @Router /sysExportTemplate/deleteSysExportTemplateByIds [delete] -func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) { - var IDS request.IdsReq - err := c.ShouldBindJSON(&IDS) - if err != nil { - response.FailWithMessage(err.Error(), c) - return - } - if err := sysExportTemplateService.DeleteSysExportTemplateByIds(IDS); err != nil { - global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) - response.FailWithMessage("批量删除失败", c) - } else { - response.OkWithMessage("批量删除成功", c) - } -} - -// UpdateSysExportTemplate 更新导出模板 -// @Tags SysExportTemplate -// @Summary 更新导出模板 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param data body system.SysExportTemplate true "更新导出模板" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" -// @Router /sysExportTemplate/updateSysExportTemplate [put] -func (sysExportTemplateApi *SysExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) { - var sysExportTemplate system.SysExportTemplate - err := c.ShouldBindJSON(&sysExportTemplate) - if err != nil { - response.FailWithMessage(err.Error(), c) - return - } - verify := utils.Rules{ - "Name": {utils.NotEmpty()}, - } - if err := utils.Verify(sysExportTemplate, verify); err != nil { - response.FailWithMessage(err.Error(), c) - return - } - if err := sysExportTemplateService.UpdateSysExportTemplate(sysExportTemplate); err != nil { - global.GVA_LOG.Error("更新失败!", zap.Error(err)) - response.FailWithMessage("更新失败", c) - } else { - response.OkWithMessage("更新成功", c) - } -} - -// FindSysExportTemplate 用id查询导出模板 -// @Tags SysExportTemplate -// @Summary 用id查询导出模板 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param data query system.SysExportTemplate true "用id查询导出模板" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" -// @Router /sysExportTemplate/findSysExportTemplate [get] -func (sysExportTemplateApi *SysExportTemplateApi) FindSysExportTemplate(c *gin.Context) { - var sysExportTemplate system.SysExportTemplate - err := c.ShouldBindQuery(&sysExportTemplate) - if err != nil { - response.FailWithMessage(err.Error(), c) - return - } - if resysExportTemplate, err := sysExportTemplateService.GetSysExportTemplate(sysExportTemplate.ID); err != nil { - global.GVA_LOG.Error("查询失败!", zap.Error(err)) - response.FailWithMessage("查询失败", c) - } else { - response.OkWithData(gin.H{"resysExportTemplate": resysExportTemplate}, c) - } -} - -// GetSysExportTemplateList 分页获取导出模板列表 -// @Tags SysExportTemplate -// @Summary 分页获取导出模板列表 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Param data query systemReq.SysExportTemplateSearch true "分页获取导出模板列表" -// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" -// @Router /sysExportTemplate/getSysExportTemplateList [get] -func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gin.Context) { - var pageInfo systemReq.SysExportTemplateSearch - err := c.ShouldBindQuery(&pageInfo) - if err != nil { - response.FailWithMessage(err.Error(), c) - return - } - if list, total, err := sysExportTemplateService.GetSysExportTemplateInfoList(pageInfo); err != nil { - global.GVA_LOG.Error("获取失败!", zap.Error(err)) - response.FailWithMessage("获取失败", c) - } else { - response.OkWithDetailed(response.PageResult{ - List: list, - Total: total, - Page: pageInfo.Page, - PageSize: pageInfo.PageSize, - }, "获取成功", c) - } -} - -// ExportExcel 导出表格token -// @Tags SysExportTemplate -// @Summary 导出表格 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Router /sysExportTemplate/exportExcel [get] -func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) { - templateID := c.Query("templateID") - 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("success", "true") - c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes()) - } -} - -// ExportTemplate 导出表格模板 -// @Tags SysExportTemplate -// @Summary 导出表格模板 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @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("success", "true") - c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes()) - } -} - -// ImportExcel 导入表格 -// @Tags SysImportTemplate -// @Summary 导入表格 -// @Security ApiKeyAuth -// @accept application/json -// @Produce application/json -// @Router /sysExportTemplate/importExcel [post] -func (sysExportTemplateApi *SysExportTemplateApi) ImportExcel(c *gin.Context) { - templateID := c.Query("templateID") - if templateID == "" { - response.FailWithMessage("模板ID不能为空", c) - return - } - file, err := c.FormFile("file") - if err != nil { - global.GVA_LOG.Error("文件获取失败!", zap.Error(err)) - response.FailWithMessage("文件获取失败", c) - return - } - if err := sysExportTemplateService.ImportExcel(templateID, file); err != nil { - global.GVA_LOG.Error(err.Error(), zap.Error(err)) - response.FailWithMessage(err.Error(), c) - } else { - response.OkWithMessage("导入成功", c) - } -} diff --git a/server/api/v1/system/sys_version.go b/server/api/v1/system/sys_version.go deleted file mode 100644 index edbb884..0000000 --- a/server/api/v1/system/sys_version.go +++ /dev/null @@ -1,486 +0,0 @@ -package system - -import ( - "encoding/json" - "fmt" - "net/http" - "sort" - "strconv" - "time" - - "git.echol.cn/loser/Go-Web-Template/server/global" - "git.echol.cn/loser/Go-Web-Template/server/model/common/response" - "git.echol.cn/loser/Go-Web-Template/server/model/system" - systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request" - systemRes "git.echol.cn/loser/Go-Web-Template/server/model/system/response" - "git.echol.cn/loser/Go-Web-Template/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) -} diff --git a/server/config.docker.yaml b/server/config.docker.yaml index 999ee87..b80bd0d 100644 --- a/server/config.docker.yaml +++ b/server/config.docker.yaml @@ -97,7 +97,7 @@ captcha: open-captcha-timeout: 3600 # open-captcha大于0时才生效 # mysql connect configuration -# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master) +# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-react-admin.com/docs/first_master) mysql: path: "" port: "" @@ -111,7 +111,7 @@ mysql: log-zap: false # pgsql connect configuration -# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master) +# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-react-admin.com/docs/first_master) pgsql: path: "" port: "" @@ -211,7 +211,7 @@ tencent-cos: region: ap-shanghai secret-id: your-secret-id secret-key: your-secret-key - base-url: https://gin.vue.admin + base-url: https://gin-react-admin.com path-prefix: git.echol.cn/loser/Go-Web-Template/server # aws s3 configuration (minio compatible) @@ -223,13 +223,13 @@ aws-s3: disable-ssl: false secret-id: your-secret-id secret-key: your-secret-key - base-url: https://gin.vue.admin + base-url: https://gin-react-admin.com path-prefix: git.echol.cn/loser/Go-Web-Template/server # cloudflare r2 configuration cloudflare-r2: bucket: xxxx0bucket - base-url: https://gin.vue.admin.com + base-url: https://gin-react-admin.com path: uploads account-id: xxx_account_id access-key-id: xxx_key_id diff --git a/server/core/server.go b/server/core/server.go index b15db54..fd5e258 100644 --- a/server/core/server.go +++ b/server/core/server.go @@ -34,7 +34,7 @@ func RunServer() { mcpBaseURL := mcpTool.ResolveMCPServiceURL() fmt.Printf(` - 欢迎使用 gin-vue-admin + 欢迎使用 Gin-React-Admin 当前版本:%s 默认自动化文档地址:http://127.0.0.1%s/swagger/index.html MCP 独立服务请手动启动: go run ./cmd/mcp -config ./cmd/mcp/config.yaml diff --git a/server/docs/docs.go b/server/docs/docs.go index 7785372..b694c0c 100644 --- a/server/docs/docs.go +++ b/server/docs/docs.go @@ -1,11 +1,7 @@ -// Code generated by swaggo/swag. DO NOT EDIT. - +// Package docs Code generated by swaggo/swag. DO NOT EDIT package docs -import ( - "git.echol.cn/loser/Go-Web-Template/server/global" - "github.com/swaggo/swag" -) +import "github.com/swaggo/swag" const docTemplate = `{ "schemes": {{ marshal .Schemes }}, @@ -426,6 +422,65 @@ const docTemplate = `{ } } }, + "/api/getApiRoles": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "获取拥有指定API权限的角色ID列表", + "parameters": [ + { + "type": "string", + "description": "API路径", + "name": "path", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "请求方法", + "name": "method", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/api/ignoreApi": { "post": { "security": [ @@ -465,6 +520,56 @@ const docTemplate = `{ } } }, + "/api/setApiRoles": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "全量覆盖某API关联的角色列表", + "parameters": [ + { + "description": "API路径、请求方法和角色ID列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetApiAuthorities" + } + } + ], + "responses": { + "200": { + "description": "设置成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/api/syncApi": { "get": { "security": [ @@ -578,7 +683,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/example.ExaAttachmentCategory" + "$ref": "#/definitions/common.ExaAttachmentCategory" } } ], @@ -661,7 +766,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "$ref": "#/definitions/example.ExaAttachmentCategory" + "$ref": "#/definitions/common.ExaAttachmentCategory" }, "msg": { "type": "string" @@ -883,6 +988,60 @@ const docTemplate = `{ } } }, + "/authority/getUsersByAuthority": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "获取拥有指定角色的用户ID列表", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "authorityId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "integer" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/authority/setDataAuthority": { "post": { "security": [ @@ -933,6 +1092,56 @@ const docTemplate = `{ } } }, + "/authority/setRoleUsers": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "全量覆盖某角色关联的用户列表", + "parameters": [ + { + "description": "角色ID和用户ID列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetRoleUsers" + } + } + ], + "responses": { + "200": { + "description": "设置成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/authority/updateAuthority": { "put": { "security": [ @@ -1128,858 +1337,6 @@ const docTemplate = `{ } } }, - "/autoCode/addFunc": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AddFunc" - ], - "summary": "增加方法", - "parameters": [ - { - "description": "增加方法", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.AutoCode" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/autoCode/createPackage": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePackage" - ], - "summary": "创建package", - "parameters": [ - { - "description": "创建package", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.SysAutoCodePackageCreate" - } - } - ], - "responses": { - "200": { - "description": "创建package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/createTemp": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodeTemplate" - ], - "summary": "自动代码模板", - "parameters": [ - { - "description": "创建自动代码", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.AutoCode" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/autoCode/delPackage": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "删除package", - "parameters": [ - { - "description": "创建package", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.GetById" - } - } - ], - "responses": { - "200": { - "description": "删除package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/delSysHistory": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "删除回滚记录", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.GetById" - } - } - ], - "responses": { - "200": { - "description": "删除回滚记录", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getColumn": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取当前表所有字段", - "responses": { - "200": { - "description": "获取当前表所有字段", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getDB": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取当前所有数据库", - "responses": { - "200": { - "description": "获取当前所有数据库", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getMeta": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取meta信息", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.GetById" - } - } - ], - "responses": { - "200": { - "description": "获取meta信息", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getPackage": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePackage" - ], - "summary": "获取package", - "responses": { - "200": { - "description": "创建package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getSysHistory": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "查询回滚记录", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.PageInfo" - } - } - ], - "responses": { - "200": { - "description": "查询回滚记录,返回包括列表,总数,页码,每页数量", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.PageResult" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getTables": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取当前数据库所有表", - "responses": { - "200": { - "description": "获取当前数据库所有表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getTemplates": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePackage" - ], - "summary": "获取package", - "responses": { - "200": { - "description": "创建package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/initAPI": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "打包插件", - "responses": { - "200": { - "description": "打包插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/initMenu": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "打包插件", - "responses": { - "200": { - "description": "打包插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/installPlugin": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "安装插件", - "parameters": [ - { - "type": "file", - "description": "this is a test file", - "name": "plug", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "安装插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object" - } - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/preview": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodeTemplate" - ], - "summary": "预览创建后的代码", - "parameters": [ - { - "description": "预览创建代码", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.AutoCode" - } - } - ], - "responses": { - "200": { - "description": "预览创建后的代码", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/pubPlug": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "打包插件", - "parameters": [ - { - "type": "string", - "description": "插件名称", - "name": "plugName", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "打包插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/rollback": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "回滚自动生成代码", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.SysAutoHistoryRollBack" - } - } - ], - "responses": { - "200": { - "description": "回滚自动生成代码", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, "/base/captcha": { "post": { "security": [ @@ -2170,357 +1527,6 @@ const docTemplate = `{ } } }, - "/customer/customer": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "获取单一客户信息", - "parameters": [ - { - "type": "integer", - "description": "主键ID", - "name": "ID", - "in": "query" - }, - { - "type": "string", - "description": "创建时间", - "name": "createdAt", - "in": "query" - }, - { - "type": "string", - "description": "客户名", - "name": "customerName", - "in": "query" - }, - { - "type": "string", - "description": "客户手机号", - "name": "customerPhoneData", - "in": "query" - }, - { - "type": "integer", - "description": "管理角色ID", - "name": "sysUserAuthorityID", - "in": "query" - }, - { - "type": "integer", - "description": "管理ID", - "name": "sysUserId", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" - } - ], - "responses": { - "200": { - "description": "获取单一客户信息,返回包括客户详情", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.ExaCustomerResponse" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - }, - "put": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "更新客户信息", - "parameters": [ - { - "description": "客户ID, 客户信息", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - ], - "responses": { - "200": { - "description": "更新客户信息", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - }, - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "创建客户", - "parameters": [ - { - "description": "客户用户名, 客户手机号码", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - ], - "responses": { - "200": { - "description": "创建客户", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - }, - "delete": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "删除客户", - "parameters": [ - { - "description": "客户ID", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - ], - "responses": { - "200": { - "description": "删除客户", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/customer/customerList": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "分页获取权限客户列表", - "parameters": [ - { - "type": "string", - "description": "关键字", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页大小", - "name": "pageSize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "分页获取权限客户列表,返回包括列表,总数,页码,每页数量", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.PageResult" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/email/emailTest": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "System" - ], - "summary": "发送测试邮件", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/email/sendEmail": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "System" - ], - "summary": "发送邮件", - "parameters": [ - { - "description": "发送邮件必须的参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/response.Email" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, "/fileUploadAndDownload/breakpointContinue": { "post": { "security": [ @@ -2590,7 +1596,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/example.ExaFileUploadAndDownload" + "$ref": "#/definitions/common.ExaFileUploadAndDownload" } } ], @@ -2790,7 +1796,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/example.ExaFileUploadAndDownload" + "$ref": "#/definitions/common.ExaFileUploadAndDownload" } } ], @@ -2915,451 +1921,6 @@ const docTemplate = `{ } } }, - "/info/createInfo": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "创建公告", - "parameters": [ - { - "description": "创建公告", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Info" - } - } - ], - "responses": { - "200": { - "description": "创建成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/deleteInfo": { - "delete": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "删除公告", - "parameters": [ - { - "description": "删除公告", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Info" - } - } - ], - "responses": { - "200": { - "description": "删除成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/deleteInfoByIds": { - "delete": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "批量删除公告", - "responses": { - "200": { - "description": "批量删除成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/findInfo": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "用id查询公告", - "parameters": [ - { - "type": "integer", - "description": "主键ID", - "name": "ID", - "in": "query" - }, - { - "type": "string", - "description": "内容", - "name": "content", - "in": "query" - }, - { - "type": "string", - "description": "创建时间", - "name": "createdAt", - "in": "query" - }, - { - "type": "string", - "description": "标题", - "name": "title", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" - }, - { - "type": "integer", - "description": "作者", - "name": "userID", - "in": "query" - } - ], - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/model.Info" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/getInfoDataSource": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "获取Info的数据源", - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/getInfoList": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "分页获取公告列表", - "parameters": [ - { - "type": "string", - "name": "endCreatedAt", - "in": "query" - }, - { - "type": "string", - "description": "关键字", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页大小", - "name": "pageSize", - "in": "query" - }, - { - "type": "string", - "name": "startCreatedAt", - "in": "query" - } - ], - "responses": { - "200": { - "description": "获取成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.PageResult" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/getInfoPublic": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "不需要鉴权的公告接口", - "parameters": [ - { - "type": "string", - "name": "endCreatedAt", - "in": "query" - }, - { - "type": "string", - "description": "关键字", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页大小", - "name": "pageSize", - "in": "query" - }, - { - "type": "string", - "name": "startCreatedAt", - "in": "query" - } - ], - "responses": { - "200": { - "description": "获取成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/updateInfo": { - "put": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "更新公告", - "parameters": [ - { - "description": "更新公告", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Info" - } - } - ], - "responses": { - "200": { - "description": "更新成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, "/init/checkdb": { "post": { "produces": [ @@ -3886,6 +2447,108 @@ const docTemplate = `{ } } }, + "/menu/getMenuRoles": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取拥有指定菜单的角色ID列表", + "parameters": [ + { + "type": "integer", + "description": "菜单ID", + "name": "menuId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/setMenuRoles": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "全量覆盖某菜单关联的角色列表", + "parameters": [ + { + "description": "菜单ID和角色ID列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetMenuAuthorities" + } + } + ], + "responses": { + "200": { + "description": "设置成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/menu/updateBaseMenu": { "post": { "security": [ @@ -4036,6 +2699,99 @@ const docTemplate = `{ } } }, + "/sysDictionary/exportSysDictionary": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "导出字典JSON(包含字典详情)", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "描述", + "name": "desc", + "in": "query" + }, + { + "type": "string", + "description": "字典名(中)", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "description": "父级字典ID", + "name": "parentID", + "in": "query" + }, + { + "type": "boolean", + "description": "状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "字典名(英)", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "导出字典JSON", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/sysDictionary/findSysDictionary": { "get": { "security": [ @@ -4078,6 +2834,12 @@ const docTemplate = `{ "name": "name", "in": "query" }, + { + "type": "integer", + "description": "父级字典ID", + "name": "parentID", + "in": "query" + }, { "type": "boolean", "description": "状态", @@ -4140,6 +2902,14 @@ const docTemplate = `{ "SysDictionary" ], "summary": "分页获取SysDictionary列表", + "parameters": [ + { + "type": "string", + "description": "字典名(中)", + "name": "name", + "in": "query" + } + ], "responses": { "200": { "description": "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量", @@ -4165,6 +2935,56 @@ const docTemplate = `{ } } }, + "/sysDictionary/importSysDictionary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "导入字典JSON(包含字典详情)", + "parameters": [ + { + "description": "字典JSON数据", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ImportSysDictionaryRequest" + } + } + ], + "responses": { + "200": { + "description": "导入字典", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/sysDictionary/updateSysDictionary": { "put": { "security": [ @@ -4345,6 +3165,12 @@ const docTemplate = `{ "name": "createdAt", "in": "query" }, + { + "type": "boolean", + "description": "禁用状态,根据status字段动态计算", + "name": "disabled", + "in": "query" + }, { "type": "string", "description": "扩展值", @@ -4357,6 +3183,24 @@ const docTemplate = `{ "name": "label", "in": "query" }, + { + "type": "integer", + "description": "层级深度,从0开始", + "name": "level", + "in": "query" + }, + { + "type": "integer", + "description": "父级字典详情ID", + "name": "parentID", + "in": "query" + }, + { + "type": "string", + "description": "层级路径,如 \"1,2,3\"", + "name": "path", + "in": "query" + }, { "type": "integer", "description": "排序标记", @@ -4414,6 +3258,234 @@ const docTemplate = `{ } } }, + "/sysDictionaryDetail/getDictionaryDetailsByParent": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "根据父级ID获取字典详情", + "parameters": [ + { + "type": "boolean", + "description": "是否包含子级数据", + "name": "includeChildren", + "in": "query" + }, + { + "type": "integer", + "description": "父级字典详情ID,为空时获取顶级", + "name": "parentID", + "in": "query" + }, + { + "type": "integer", + "description": "字典ID", + "name": "sysDictionaryID", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getDictionaryPath": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "获取字典详情的完整路径", + "parameters": [ + { + "type": "integer", + "description": "字典详情ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情路径", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getDictionaryTreeList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "获取字典详情树形结构", + "parameters": [ + { + "type": "integer", + "description": "字典ID", + "name": "sysDictionaryID", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情树形结构", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getDictionaryTreeListByType": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "根据字典类型获取字典详情树形结构", + "parameters": [ + { + "type": "string", + "description": "字典类型", + "name": "type", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情树形结构", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/sysDictionaryDetail/getSysDictionaryDetailList": { "get": { "security": [ @@ -4444,6 +3516,12 @@ const docTemplate = `{ "name": "createdAt", "in": "query" }, + { + "type": "boolean", + "description": "禁用状态,根据status字段动态计算", + "name": "disabled", + "in": "query" + }, { "type": "string", "description": "扩展值", @@ -4462,6 +3540,12 @@ const docTemplate = `{ "name": "label", "in": "query" }, + { + "type": "integer", + "description": "层级深度,用于查询指定层级的数据", + "name": "level", + "in": "query" + }, { "type": "integer", "description": "页码", @@ -4474,6 +3558,18 @@ const docTemplate = `{ "name": "pageSize", "in": "query" }, + { + "type": "integer", + "description": "父级字典详情ID,用于查询指定父级下的子项", + "name": "parentID", + "in": "query" + }, + { + "type": "string", + "description": "层级路径,如 \"1,2,3\"", + "name": "path", + "in": "query" + }, { "type": "integer", "description": "排序标记", @@ -4580,27 +3676,7 @@ const docTemplate = `{ } } }, - "/sysExportTemplate/ExportTemplate": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SysExportTemplate" - ], - "summary": "导出表格模板", - "responses": {} - } - }, - "/sysExportTemplate/createSysExportTemplate": { + "/sysError/createSysError": { "post": { "security": [ { @@ -4614,31 +3690,43 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "创建导出模板", + "summary": "创建错误日志", "parameters": [ { - "description": "创建导出模板", + "description": "创建错误日志", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/system.SysExportTemplate" + "$ref": "#/definitions/system.SysError" } } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "description": "创建成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/deleteSysExportTemplate": { + "/sysError/deleteSysError": { "delete": { "security": [ { @@ -4652,31 +3740,43 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "删除导出模板", + "summary": "删除错误日志", "parameters": [ { - "description": "删除导出模板", + "description": "删除错误日志", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/system.SysExportTemplate" + "$ref": "#/definitions/system.SysError" } } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "description": "删除成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/deleteSysExportTemplateByIds": { + "/sysError/deleteSysErrorByIds": { "delete": { "security": [ { @@ -4690,31 +3790,32 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysExportTemplate" - ], - "summary": "批量删除导出模板", - "parameters": [ - { - "description": "批量删除导出模板", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.IdsReq" - } - } + "SysError" ], + "summary": "批量删除错误日志", "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"批量删除成功\"}", + "description": "批量删除成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/exportExcel": { + "/sysError/findSysError": { "get": { "security": [ { @@ -4728,100 +3829,44 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "导出表格", - "responses": {} - } - }, - "/sysExportTemplate/findSysExportTemplate": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SysExportTemplate" - ], - "summary": "用id查询导出模板", + "summary": "用id查询错误日志", "parameters": [ { "type": "integer", - "description": "主键ID", + "description": "用id查询错误日志", "name": "ID", - "in": "query" - }, - { - "type": "string", - "description": "创建时间", - "name": "createdAt", - "in": "query" - }, - { - "type": "string", - "description": "数据库名称", - "name": "dbName", - "in": "query" - }, - { - "type": "integer", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "模板名称", - "name": "name", - "in": "query" - }, - { - "type": "string", - "name": "order", - "in": "query" - }, - { - "type": "string", - "description": "表名称", - "name": "tableName", - "in": "query" - }, - { - "type": "string", - "description": "模板标识", - "name": "templateID", - "in": "query" - }, - { - "type": "string", - "description": "模板信息", - "name": "templateInfo", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" + "in": "query", + "required": true } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", + "description": "查询成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.SysError" + }, + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/getSysExportTemplateList": { + "/sysError/getSysErrorList": { "get": { "security": [ { @@ -4835,31 +3880,27 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "分页获取导出模板列表", + "summary": "分页获取错误日志列表", "parameters": [ { - "type": "integer", - "description": "主键ID", - "name": "ID", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "createdAtRange", "in": "query" }, { "type": "string", - "description": "创建时间", - "name": "createdAt", + "name": "form", "in": "query" }, { "type": "string", - "description": "数据库名称", - "name": "dbName", - "in": "query" - }, - { - "type": "string", - "name": "endCreatedAt", + "name": "info", "in": "query" }, { @@ -4868,22 +3909,6 @@ const docTemplate = `{ "name": "keyword", "in": "query" }, - { - "type": "integer", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "模板名称", - "name": "name", - "in": "query" - }, - { - "type": "string", - "name": "order", - "in": "query" - }, { "type": "integer", "description": "页码", @@ -4895,49 +3920,35 @@ const docTemplate = `{ "description": "每页大小", "name": "pageSize", "in": "query" - }, - { - "type": "string", - "name": "startCreatedAt", - "in": "query" - }, - { - "type": "string", - "description": "表名称", - "name": "tableName", - "in": "query" - }, - { - "type": "string", - "description": "模板标识", - "name": "templateID", - "in": "query" - }, - { - "type": "string", - "description": "模板信息", - "name": "templateInfo", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "description": "获取成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/importExcel": { - "post": { + "/sysError/getSysErrorSolution": { + "get": { "security": [ { "ApiKeyAuth": [] @@ -4950,13 +3961,41 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysImportTemplate" + "SysError" ], - "summary": "导入表格", - "responses": {} + "summary": "根据ID触发处理:标记为处理中,1分钟后自动改为处理完成", + "parameters": [ + { + "type": "string", + "description": "错误日志ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "处理已提交", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } } }, - "/sysExportTemplate/updateSysExportTemplate": { + "/sysError/updateSysError": { "put": { "security": [ { @@ -4970,61 +4009,23 @@ const docTemplate = `{ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "更新导出模板", + "summary": "更新错误日志", "parameters": [ { - "description": "更新导出模板", + "description": "更新错误日志", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/system.SysExportTemplate" + "$ref": "#/definitions/system.SysError" } } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/sysOperationRecord/createSysOperationRecord": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SysOperationRecord" - ], - "summary": "创建SysOperationRecord", - "parameters": [ - { - "description": "创建SysOperationRecord", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/system.SysOperationRecord" - } - } - ], - "responses": { - "200": { - "description": "创建SysOperationRecord", + "description": "更新成功", "schema": { "allOf": [ { @@ -5910,10 +4911,10 @@ const docTemplate = `{ "tags": [ "System" ], - "summary": "重启系统", + "summary": "重载系统", "responses": { "200": { - "description": "重启系统", + "description": "重载系统", "schema": { "allOf": [ { @@ -6530,6 +5531,136 @@ const docTemplate = `{ } }, "definitions": { + "common.ExaAttachmentCategory": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/common.ExaAttachmentCategory" + } + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "name": { + "type": "string" + }, + "pid": { + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "common.ExaFile": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "chunkTotal": { + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileChunk": { + "type": "array", + "items": { + "$ref": "#/definitions/common.ExaFileChunk" + } + }, + "fileMd5": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "filePath": { + "type": "string" + }, + "isFinish": { + "type": "boolean" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "common.ExaFileChunk": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileID": { + "type": "integer" + }, + "fileChunkNumber": { + "type": "integer" + }, + "fileChunkPath": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "common.ExaFileUploadAndDownload": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "classId": { + "description": "分类id", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "key": { + "description": "编号", + "type": "string" + }, + "name": { + "description": "文件名", + "type": "string" + }, + "tag": { + "description": "文件标签", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "url": { + "description": "文件地址", + "type": "string" + } + } + }, "common.JSONMap": { "type": "object", "additionalProperties": true @@ -6557,26 +5688,6 @@ const docTemplate = `{ } } }, - "config.Autocode": { - "type": "object", - "properties": { - "ai-path": { - "type": "string" - }, - "module": { - "type": "string" - }, - "root": { - "type": "string" - }, - "server": { - "type": "string" - }, - "web": { - "type": "string" - } - } - }, "config.AwsS3": { "type": "object", "properties": { @@ -6659,7 +5770,7 @@ const docTemplate = `{ "type": "integer" }, "open-captcha": { - "description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码", + "description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码次数,如3代表错误三次后出现验证码", "type": "integer" }, "open-captcha-timeout": { @@ -6699,6 +5810,43 @@ const docTemplate = `{ } } }, + "config.Email": { + "type": "object", + "properties": { + "from": { + "description": "发件人 你自己要发邮件的邮箱", + "type": "string" + }, + "host": { + "description": "服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议", + "type": "string" + }, + "is-loginauth": { + "description": "是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等)", + "type": "boolean" + }, + "is-ssl": { + "description": "是否SSL 是否开启SSL", + "type": "boolean" + }, + "nickname": { + "description": "昵称 发件人昵称 通常为自己的邮箱", + "type": "string" + }, + "port": { + "description": "端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465", + "type": "integer" + }, + "secret": { + "description": "密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥", + "type": "string" + }, + "to": { + "description": "收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用", + "type": "string" + } + } + }, "config.Excel": { "type": "object", "properties": { @@ -6761,6 +5909,48 @@ const docTemplate = `{ } } }, + "config.MCP": { + "type": "object", + "properties": { + "addr": { + "type": "integer" + }, + "auth_header": { + "type": "string" + }, + "base_url": { + "type": "string" + }, + "message_path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "request_timeout": { + "type": "integer" + }, + "separate": { + "type": "boolean" + }, + "sse_path": { + "description": "Deprecated fields kept for backward compatibility with older configs.", + "type": "string" + }, + "upstream_base_url": { + "type": "string" + }, + "url_prefix": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "config.Minio": { "type": "object", "properties": { @@ -7159,14 +6349,6 @@ const docTemplate = `{ "aliyun-oss": { "$ref": "#/definitions/config.AliyunOSS" }, - "autocode": { - "description": "auto", - "allOf": [ - { - "$ref": "#/definitions/config.Autocode" - } - ] - }, "aws-s3": { "$ref": "#/definitions/config.AwsS3" }, @@ -7197,7 +6379,7 @@ const docTemplate = `{ } }, "email": { - "$ref": "#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email" + "$ref": "#/definitions/config.Email" }, "excel": { "$ref": "#/definitions/config.Excel" @@ -7216,6 +6398,14 @@ const docTemplate = `{ } ] }, + "mcp": { + "description": "MCP配置", + "allOf": [ + { + "$ref": "#/definitions/config.MCP" + } + ] + }, "minio": { "$ref": "#/definitions/config.Minio" }, @@ -7401,6 +6591,10 @@ const docTemplate = `{ "description": "数据库类型:mysql(默认)|sqlite|sqlserver|postgresql", "type": "string" }, + "disable-auto-migrate": { + "description": "自动迁移数据库表结构,生产环境建议设为false,手动迁移", + "type": "boolean" + }, "iplimit-count": { "type": "integer" }, @@ -7496,246 +6690,6 @@ const docTemplate = `{ } } }, - "example.ExaAttachmentCategory": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "children": { - "type": "array", - "items": { - "$ref": "#/definitions/example.ExaAttachmentCategory" - } - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "name": { - "type": "string" - }, - "pid": { - "type": "integer" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaCustomer": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "customerName": { - "description": "客户名", - "type": "string" - }, - "customerPhoneData": { - "description": "客户手机号", - "type": "string" - }, - "sysUser": { - "description": "管理详情", - "allOf": [ - { - "$ref": "#/definitions/system.SysUser" - } - ] - }, - "sysUserAuthorityID": { - "description": "管理角色ID", - "type": "integer" - }, - "sysUserId": { - "description": "管理ID", - "type": "integer" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaFile": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "chunkTotal": { - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "exaFileChunk": { - "type": "array", - "items": { - "$ref": "#/definitions/example.ExaFileChunk" - } - }, - "fileMd5": { - "type": "string" - }, - "fileName": { - "type": "string" - }, - "filePath": { - "type": "string" - }, - "isFinish": { - "type": "boolean" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaFileChunk": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "exaFileID": { - "type": "integer" - }, - "fileChunkNumber": { - "type": "integer" - }, - "fileChunkPath": { - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaFileUploadAndDownload": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "classId": { - "description": "分类id", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "key": { - "description": "编号", - "type": "string" - }, - "name": { - "description": "文件名", - "type": "string" - }, - "tag": { - "description": "文件标签", - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - }, - "url": { - "description": "文件地址", - "type": "string" - } - } - }, - "github_com_flipped-aurora_gin-vue-admin_server_config.Email": { - "type": "object", - "properties": { - "from": { - "description": "发件人 你自己要发邮件的邮箱", - "type": "string" - }, - "host": { - "description": "服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议", - "type": "string" - }, - "is-ssl": { - "description": "是否SSL 是否开启SSL", - "type": "boolean" - }, - "nickname": { - "description": "昵称 发件人昵称 通常为自己的邮箱", - "type": "string" - }, - "port": { - "description": "端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465", - "type": "integer" - }, - "secret": { - "description": "密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥", - "type": "string" - }, - "to": { - "description": "收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用", - "type": "string" - } - } - }, - "model.Info": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "attachments": { - "description": "附件", - "type": "array", - "items": { - "type": "object" - } - }, - "content": { - "description": "内容", - "type": "string" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "title": { - "description": "标题", - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - }, - "userID": { - "description": "作者", - "type": "integer" - } - } - }, "request.AddMenuAuthorityInfo": { "type": "object", "properties": { @@ -7751,219 +6705,6 @@ const docTemplate = `{ } } }, - "request.AutoCode": { - "type": "object", - "properties": { - "abbreviation": { - "description": "Struct简称", - "type": "string", - "example": "Struct简称" - }, - "autoCreateApiToSql": { - "description": "是否自动创建api", - "type": "boolean", - "example": false - }, - "autoCreateBtnAuth": { - "description": "是否自动创建按钮权限", - "type": "boolean", - "example": false - }, - "autoCreateMenuToSql": { - "description": "是否自动创建menu", - "type": "boolean", - "example": false - }, - "autoCreateResource": { - "description": "是否自动创建资源标识", - "type": "boolean", - "example": false - }, - "autoMigrate": { - "description": "是否自动迁移表结构", - "type": "boolean", - "example": false - }, - "businessDB": { - "description": "业务数据库", - "type": "string", - "example": "业务数据库" - }, - "description": { - "description": "Struct中文名称", - "type": "string", - "example": "Struct中文名称" - }, - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/request.AutoCodeField" - } - }, - "generateServer": { - "description": "是否生成server", - "type": "boolean", - "example": true - }, - "generateWeb": { - "description": "是否生成web", - "type": "boolean", - "example": true - }, - "gvaModel": { - "description": "是否使用gva默认Model", - "type": "boolean", - "example": false - }, - "humpPackageName": { - "description": "go文件名称", - "type": "string", - "example": "go文件名称" - }, - "isAdd": { - "description": "是否新增", - "type": "boolean", - "example": false - }, - "isTree": { - "description": "是否树形结构", - "type": "boolean", - "example": false - }, - "onlyTemplate": { - "description": "是否只生成模板", - "type": "boolean", - "example": false - }, - "package": { - "type": "string" - }, - "packageName": { - "description": "文件名称", - "type": "string", - "example": "文件名称" - }, - "primaryField": { - "$ref": "#/definitions/request.AutoCodeField" - }, - "structName": { - "description": "Struct名称", - "type": "string", - "example": "Struct名称" - }, - "tableName": { - "description": "表名", - "type": "string", - "example": "表名" - }, - "treeJson": { - "description": "展示的树json字段", - "type": "string", - "example": "展示的树json字段" - } - } - }, - "request.AutoCodeField": { - "type": "object", - "properties": { - "checkDataSource": { - "description": "是否检查数据源", - "type": "boolean" - }, - "clearable": { - "description": "是否可清空", - "type": "boolean" - }, - "columnName": { - "description": "数据库字段", - "type": "string" - }, - "comment": { - "description": "数据库字段描述", - "type": "string" - }, - "dataSource": { - "description": "数据源", - "allOf": [ - { - "$ref": "#/definitions/request.DataSource" - } - ] - }, - "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" - } - } - }, "request.CasbinInReceive": { "type": "object", "properties": { @@ -8005,30 +6746,6 @@ const docTemplate = `{ } } }, - "request.DataSource": { - "type": "object", - "properties": { - "association": { - "description": "关联关系 1 一对一 2 一对多", - "type": "integer" - }, - "dbName": { - "type": "string" - }, - "hasDeletedAt": { - "type": "boolean" - }, - "label": { - "type": "string" - }, - "table": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, "request.Empty": { "type": "object" }, @@ -8073,6 +6790,10 @@ const docTemplate = `{ "request.GetUserList": { "type": "object", "properties": { + "desc": { + "description": "排序方式:升序false(默认)|降序true", + "type": "boolean" + }, "email": { "type": "string" }, @@ -8083,6 +6804,10 @@ const docTemplate = `{ "nickName": { "type": "string" }, + "orderKey": { + "description": "排序", + "type": "string" + }, "page": { "description": "页码", "type": "integer" @@ -8110,6 +6835,18 @@ const docTemplate = `{ } } }, + "request.ImportSysDictionaryRequest": { + "type": "object", + "required": [ + "json" + ], + "properties": { + "json": { + "description": "JSON字符串", + "type": "string" + } + } + }, "request.InitDB": { "type": "object", "required": [ @@ -8286,6 +7023,58 @@ const docTemplate = `{ } } }, + "request.SetApiAuthorities": { + "type": "object", + "properties": { + "authorityIds": { + "description": "角色ID列表", + "type": "array", + "items": { + "type": "integer" + } + }, + "method": { + "description": "请求方法", + "type": "string" + }, + "path": { + "description": "API路径", + "type": "string" + } + } + }, + "request.SetMenuAuthorities": { + "type": "object", + "properties": { + "authorityIds": { + "description": "角色ID列表", + "type": "array", + "items": { + "type": "integer" + } + }, + "menuId": { + "description": "菜单ID", + "type": "integer" + } + } + }, + "request.SetRoleUsers": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + }, + "userIds": { + "description": "用户ID列表", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "request.SetUserAuth": { "type": "object", "properties": { @@ -8327,78 +7116,11 @@ const docTemplate = `{ } } }, - "request.SysAutoCodePackageCreate": { - "type": "object", - "properties": { - "desc": { - "type": "string", - "example": "描述" - }, - "label": { - "type": "string", - "example": "展示名" - }, - "packageName": { - "type": "string", - "example": "包名" - }, - "template": { - "type": "string", - "example": "模版" - } - } - }, - "request.SysAutoHistoryRollBack": { - "type": "object", - "properties": { - "deleteApi": { - "description": "是否删除接口", - "type": "boolean" - }, - "deleteMenu": { - "description": "是否删除菜单", - "type": "boolean" - }, - "deleteTable": { - "description": "是否删除表", - "type": "boolean" - }, - "id": { - "description": "主键ID", - "type": "integer" - } - } - }, - "response.Email": { - "type": "object", - "properties": { - "body": { - "description": "邮件内容", - "type": "string" - }, - "subject": { - "description": "邮件标题", - "type": "string" - }, - "to": { - "description": "邮件发送给谁", - "type": "string" - } - } - }, - "response.ExaCustomerResponse": { - "type": "object", - "properties": { - "customer": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - }, "response.ExaFileResponse": { "type": "object", "properties": { "file": { - "$ref": "#/definitions/example.ExaFileUploadAndDownload" + "$ref": "#/definitions/common.ExaFileUploadAndDownload" } } }, @@ -8414,7 +7136,7 @@ const docTemplate = `{ "type": "object", "properties": { "file": { - "$ref": "#/definitions/example.ExaFile" + "$ref": "#/definitions/common.ExaFile" } } }, @@ -8583,64 +7305,6 @@ const docTemplate = `{ } } }, - "system.Condition": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "column": { - "type": "string" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "from": { - "type": "string" - }, - "operator": { - "type": "string" - }, - "templateID": { - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "system.JoinTemplate": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "joins": { - "type": "string" - }, - "on": { - "type": "string" - }, - "table": { - "type": "string" - }, - "templateID": { - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, "system.Meta": { "type": "object", "properties": { @@ -8666,6 +7330,10 @@ const docTemplate = `{ "title": { "description": "菜单名", "type": "string" + }, + "transitionType": { + "description": "路由切换动画", + "type": "string" } } }, @@ -8890,6 +7558,13 @@ const docTemplate = `{ "description": "主键ID", "type": "integer" }, + "children": { + "description": "子字典", + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionary" + } + }, "createdAt": { "description": "创建时间", "type": "string" @@ -8902,6 +7577,10 @@ const docTemplate = `{ "description": "字典名(中)", "type": "string" }, + "parentID": { + "description": "父级字典ID", + "type": "integer" + }, "status": { "description": "状态", "type": "boolean" @@ -8929,10 +7608,21 @@ const docTemplate = `{ "description": "主键ID", "type": "integer" }, + "children": { + "description": "子字典详情", + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, "createdAt": { "description": "创建时间", "type": "string" }, + "disabled": { + "description": "禁用状态,根据status字段动态计算", + "type": "boolean" + }, "extend": { "description": "扩展值", "type": "string" @@ -8941,6 +7631,18 @@ const docTemplate = `{ "description": "展示值", "type": "string" }, + "level": { + "description": "层级深度,从0开始", + "type": "integer" + }, + "parentID": { + "description": "父级字典详情ID", + "type": "integer" + }, + "path": { + "description": "层级路径,如 \"1,2,3\"", + "type": "string" + }, "sort": { "description": "排序标记", "type": "integer" @@ -8963,53 +7665,37 @@ const docTemplate = `{ } } }, - "system.SysExportTemplate": { + "system.SysError": { "type": "object", + "required": [ + "form" + ], "properties": { "ID": { "description": "主键ID", "type": "integer" }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/definitions/system.Condition" - } - }, "createdAt": { "description": "创建时间", "type": "string" }, - "dbName": { - "description": "数据库名称", + "form": { + "description": "错误来源", "type": "string" }, - "joinTemplate": { - "type": "array", - "items": { - "$ref": "#/definitions/system.JoinTemplate" - } - }, - "limit": { - "type": "integer" - }, - "name": { - "description": "模板名称", + "info": { + "description": "错误内容", "type": "string" }, - "order": { + "level": { "type": "string" }, - "tableName": { - "description": "表名称", + "solution": { + "description": "解决方案", "type": "string" }, - "templateID": { - "description": "模板标识", - "type": "string" - }, - "templateInfo": { - "description": "模板信息", + "status": { + "description": "处理状态:未处理/处理中/处理完成", "type": "string" }, "updatedAt": { @@ -9299,14 +7985,16 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: global.Version, + Version: "v2.9.1", Host: "", BasePath: "", Schemes: []string{}, - Title: "Gin-Vue-Admin Swagger API接口文档", - Description: "使用gin+vue进行极速开发的全栈开发基础平台", + Title: "Gin-React-Admin Swagger API接口文档", + Description: "使用gin+react进行极速开发的全栈开发基础平台", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", } func init() { diff --git a/server/docs/swagger.json b/server/docs/swagger.json index 9eb3da4..16c9cdf 100644 --- a/server/docs/swagger.json +++ b/server/docs/swagger.json @@ -1,10 +1,10 @@ { "swagger": "2.0", "info": { - "description": "使用gin+vue进行极速开发的全栈开发基础平台", - "title": "Gin-Vue-Admin Swagger API接口文档", + "description": "使用gin+react进行极速开发的全栈开发基础平台", + "title": "Gin-React-Admin Swagger API接口文档", "contact": {}, - "version": "v2.7.9-beta" + "version": "v2.9.1" }, "paths": { "/api/createApi": { @@ -414,6 +414,65 @@ } } }, + "/api/getApiRoles": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "获取拥有指定API权限的角色ID列表", + "parameters": [ + { + "type": "string", + "description": "API路径", + "name": "path", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "请求方法", + "name": "method", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/api/ignoreApi": { "post": { "security": [ @@ -453,6 +512,56 @@ } } }, + "/api/setApiRoles": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysApi" + ], + "summary": "全量覆盖某API关联的角色列表", + "parameters": [ + { + "description": "API路径、请求方法和角色ID列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetApiAuthorities" + } + } + ], + "responses": { + "200": { + "description": "设置成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/api/syncApi": { "get": { "security": [ @@ -566,7 +675,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/example.ExaAttachmentCategory" + "$ref": "#/definitions/common.ExaAttachmentCategory" } } ], @@ -649,7 +758,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/example.ExaAttachmentCategory" + "$ref": "#/definitions/common.ExaAttachmentCategory" }, "msg": { "type": "string" @@ -871,6 +980,60 @@ } } }, + "/authority/getUsersByAuthority": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "获取拥有指定角色的用户ID列表", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "authorityId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "integer" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/authority/setDataAuthority": { "post": { "security": [ @@ -921,6 +1084,56 @@ } } }, + "/authority/setRoleUsers": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authority" + ], + "summary": "全量覆盖某角色关联的用户列表", + "parameters": [ + { + "description": "角色ID和用户ID列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetRoleUsers" + } + } + ], + "responses": { + "200": { + "description": "设置成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/authority/updateAuthority": { "put": { "security": [ @@ -1116,858 +1329,6 @@ } } }, - "/autoCode/addFunc": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AddFunc" - ], - "summary": "增加方法", - "parameters": [ - { - "description": "增加方法", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.AutoCode" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/autoCode/createPackage": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePackage" - ], - "summary": "创建package", - "parameters": [ - { - "description": "创建package", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.SysAutoCodePackageCreate" - } - } - ], - "responses": { - "200": { - "description": "创建package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/createTemp": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodeTemplate" - ], - "summary": "自动代码模板", - "parameters": [ - { - "description": "创建自动代码", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.AutoCode" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/autoCode/delPackage": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "删除package", - "parameters": [ - { - "description": "创建package", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.GetById" - } - } - ], - "responses": { - "200": { - "description": "删除package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/delSysHistory": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "删除回滚记录", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.GetById" - } - } - ], - "responses": { - "200": { - "description": "删除回滚记录", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getColumn": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取当前表所有字段", - "responses": { - "200": { - "description": "获取当前表所有字段", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getDB": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取当前所有数据库", - "responses": { - "200": { - "description": "获取当前所有数据库", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getMeta": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取meta信息", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.GetById" - } - } - ], - "responses": { - "200": { - "description": "获取meta信息", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getPackage": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePackage" - ], - "summary": "获取package", - "responses": { - "200": { - "description": "创建package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getSysHistory": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "查询回滚记录", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.PageInfo" - } - } - ], - "responses": { - "200": { - "description": "查询回滚记录,返回包括列表,总数,页码,每页数量", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.PageResult" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getTables": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "获取当前数据库所有表", - "responses": { - "200": { - "description": "获取当前数据库所有表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/getTemplates": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePackage" - ], - "summary": "获取package", - "responses": { - "200": { - "description": "创建package成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/initAPI": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "打包插件", - "responses": { - "200": { - "description": "打包插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/initMenu": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "打包插件", - "responses": { - "200": { - "description": "打包插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/installPlugin": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "安装插件", - "parameters": [ - { - "type": "file", - "description": "this is a test file", - "name": "plug", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "安装插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object" - } - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/preview": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodeTemplate" - ], - "summary": "预览创建后的代码", - "parameters": [ - { - "description": "预览创建代码", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.AutoCode" - } - } - ], - "responses": { - "200": { - "description": "预览创建后的代码", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/pubPlug": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCodePlugin" - ], - "summary": "打包插件", - "parameters": [ - { - "type": "string", - "description": "插件名称", - "name": "plugName", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "打包插件成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/autoCode/rollback": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "AutoCode" - ], - "summary": "回滚自动生成代码", - "parameters": [ - { - "description": "请求参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.SysAutoHistoryRollBack" - } - } - ], - "responses": { - "200": { - "description": "回滚自动生成代码", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, "/base/captcha": { "post": { "security": [ @@ -2158,357 +1519,6 @@ } } }, - "/customer/customer": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "获取单一客户信息", - "parameters": [ - { - "type": "integer", - "description": "主键ID", - "name": "ID", - "in": "query" - }, - { - "type": "string", - "description": "创建时间", - "name": "createdAt", - "in": "query" - }, - { - "type": "string", - "description": "客户名", - "name": "customerName", - "in": "query" - }, - { - "type": "string", - "description": "客户手机号", - "name": "customerPhoneData", - "in": "query" - }, - { - "type": "integer", - "description": "管理角色ID", - "name": "sysUserAuthorityID", - "in": "query" - }, - { - "type": "integer", - "description": "管理ID", - "name": "sysUserId", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" - } - ], - "responses": { - "200": { - "description": "获取单一客户信息,返回包括客户详情", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.ExaCustomerResponse" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - }, - "put": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "更新客户信息", - "parameters": [ - { - "description": "客户ID, 客户信息", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - ], - "responses": { - "200": { - "description": "更新客户信息", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - }, - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "创建客户", - "parameters": [ - { - "description": "客户用户名, 客户手机号码", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - ], - "responses": { - "200": { - "description": "创建客户", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - }, - "delete": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "删除客户", - "parameters": [ - { - "description": "客户ID", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - ], - "responses": { - "200": { - "description": "删除客户", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/customer/customerList": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ExaCustomer" - ], - "summary": "分页获取权限客户列表", - "parameters": [ - { - "type": "string", - "description": "关键字", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页大小", - "name": "pageSize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "分页获取权限客户列表,返回包括列表,总数,页码,每页数量", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.PageResult" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/email/emailTest": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "System" - ], - "summary": "发送测试邮件", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/email/sendEmail": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "System" - ], - "summary": "发送邮件", - "parameters": [ - { - "description": "发送邮件必须的参数", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/response.Email" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发送成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, "/fileUploadAndDownload/breakpointContinue": { "post": { "security": [ @@ -2578,7 +1588,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/example.ExaFileUploadAndDownload" + "$ref": "#/definitions/common.ExaFileUploadAndDownload" } } ], @@ -2778,7 +1788,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/example.ExaFileUploadAndDownload" + "$ref": "#/definitions/common.ExaFileUploadAndDownload" } } ], @@ -2903,451 +1913,6 @@ } } }, - "/info/createInfo": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "创建公告", - "parameters": [ - { - "description": "创建公告", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Info" - } - } - ], - "responses": { - "200": { - "description": "创建成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/deleteInfo": { - "delete": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "删除公告", - "parameters": [ - { - "description": "删除公告", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Info" - } - } - ], - "responses": { - "200": { - "description": "删除成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/deleteInfoByIds": { - "delete": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "批量删除公告", - "responses": { - "200": { - "description": "批量删除成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/findInfo": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "用id查询公告", - "parameters": [ - { - "type": "integer", - "description": "主键ID", - "name": "ID", - "in": "query" - }, - { - "type": "string", - "description": "内容", - "name": "content", - "in": "query" - }, - { - "type": "string", - "description": "创建时间", - "name": "createdAt", - "in": "query" - }, - { - "type": "string", - "description": "标题", - "name": "title", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" - }, - { - "type": "integer", - "description": "作者", - "name": "userID", - "in": "query" - } - ], - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/model.Info" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/getInfoDataSource": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "获取Info的数据源", - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/getInfoList": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "分页获取公告列表", - "parameters": [ - { - "type": "string", - "name": "endCreatedAt", - "in": "query" - }, - { - "type": "string", - "description": "关键字", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页大小", - "name": "pageSize", - "in": "query" - }, - { - "type": "string", - "name": "startCreatedAt", - "in": "query" - } - ], - "responses": { - "200": { - "description": "获取成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/response.PageResult" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/getInfoPublic": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "不需要鉴权的公告接口", - "parameters": [ - { - "type": "string", - "name": "endCreatedAt", - "in": "query" - }, - { - "type": "string", - "description": "关键字", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页大小", - "name": "pageSize", - "in": "query" - }, - { - "type": "string", - "name": "startCreatedAt", - "in": "query" - } - ], - "responses": { - "200": { - "description": "获取成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object" - }, - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/info/updateInfo": { - "put": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Info" - ], - "summary": "更新公告", - "parameters": [ - { - "description": "更新公告", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/model.Info" - } - } - ], - "responses": { - "200": { - "description": "更新成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, "/init/checkdb": { "post": { "produces": [ @@ -3874,6 +2439,108 @@ } } }, + "/menu/getMenuRoles": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "获取拥有指定菜单的角色ID列表", + "parameters": [ + { + "type": "integer", + "description": "菜单ID", + "name": "menuId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/setMenuRoles": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AuthorityMenu" + ], + "summary": "全量覆盖某菜单关联的角色列表", + "parameters": [ + { + "description": "菜单ID和角色ID列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SetMenuAuthorities" + } + } + ], + "responses": { + "200": { + "description": "设置成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/menu/updateBaseMenu": { "post": { "security": [ @@ -4024,6 +2691,99 @@ } } }, + "/sysDictionary/exportSysDictionary": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "导出字典JSON(包含字典详情)", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "ID", + "in": "query" + }, + { + "type": "string", + "description": "创建时间", + "name": "createdAt", + "in": "query" + }, + { + "type": "string", + "description": "描述", + "name": "desc", + "in": "query" + }, + { + "type": "string", + "description": "字典名(中)", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "description": "父级字典ID", + "name": "parentID", + "in": "query" + }, + { + "type": "boolean", + "description": "状态", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "字典名(英)", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "更新时间", + "name": "updatedAt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "导出字典JSON", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/sysDictionary/findSysDictionary": { "get": { "security": [ @@ -4066,6 +2826,12 @@ "name": "name", "in": "query" }, + { + "type": "integer", + "description": "父级字典ID", + "name": "parentID", + "in": "query" + }, { "type": "boolean", "description": "状态", @@ -4128,6 +2894,14 @@ "SysDictionary" ], "summary": "分页获取SysDictionary列表", + "parameters": [ + { + "type": "string", + "description": "字典名(中)", + "name": "name", + "in": "query" + } + ], "responses": { "200": { "description": "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量", @@ -4153,6 +2927,56 @@ } } }, + "/sysDictionary/importSysDictionary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionary" + ], + "summary": "导入字典JSON(包含字典详情)", + "parameters": [ + { + "description": "字典JSON数据", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ImportSysDictionaryRequest" + } + } + ], + "responses": { + "200": { + "description": "导入字典", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/sysDictionary/updateSysDictionary": { "put": { "security": [ @@ -4333,6 +3157,12 @@ "name": "createdAt", "in": "query" }, + { + "type": "boolean", + "description": "禁用状态,根据status字段动态计算", + "name": "disabled", + "in": "query" + }, { "type": "string", "description": "扩展值", @@ -4345,6 +3175,24 @@ "name": "label", "in": "query" }, + { + "type": "integer", + "description": "层级深度,从0开始", + "name": "level", + "in": "query" + }, + { + "type": "integer", + "description": "父级字典详情ID", + "name": "parentID", + "in": "query" + }, + { + "type": "string", + "description": "层级路径,如 \"1,2,3\"", + "name": "path", + "in": "query" + }, { "type": "integer", "description": "排序标记", @@ -4402,6 +3250,234 @@ } } }, + "/sysDictionaryDetail/getDictionaryDetailsByParent": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "根据父级ID获取字典详情", + "parameters": [ + { + "type": "boolean", + "description": "是否包含子级数据", + "name": "includeChildren", + "in": "query" + }, + { + "type": "integer", + "description": "父级字典详情ID,为空时获取顶级", + "name": "parentID", + "in": "query" + }, + { + "type": "integer", + "description": "字典ID", + "name": "sysDictionaryID", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getDictionaryPath": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "获取字典详情的完整路径", + "parameters": [ + { + "type": "integer", + "description": "字典详情ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情路径", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getDictionaryTreeList": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "获取字典详情树形结构", + "parameters": [ + { + "type": "integer", + "description": "字典ID", + "name": "sysDictionaryID", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情树形结构", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/sysDictionaryDetail/getDictionaryTreeListByType": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SysDictionaryDetail" + ], + "summary": "根据字典类型获取字典详情树形结构", + "parameters": [ + { + "type": "string", + "description": "字典类型", + "name": "type", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取字典详情树形结构", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, "/sysDictionaryDetail/getSysDictionaryDetailList": { "get": { "security": [ @@ -4432,6 +3508,12 @@ "name": "createdAt", "in": "query" }, + { + "type": "boolean", + "description": "禁用状态,根据status字段动态计算", + "name": "disabled", + "in": "query" + }, { "type": "string", "description": "扩展值", @@ -4450,6 +3532,12 @@ "name": "label", "in": "query" }, + { + "type": "integer", + "description": "层级深度,用于查询指定层级的数据", + "name": "level", + "in": "query" + }, { "type": "integer", "description": "页码", @@ -4462,6 +3550,18 @@ "name": "pageSize", "in": "query" }, + { + "type": "integer", + "description": "父级字典详情ID,用于查询指定父级下的子项", + "name": "parentID", + "in": "query" + }, + { + "type": "string", + "description": "层级路径,如 \"1,2,3\"", + "name": "path", + "in": "query" + }, { "type": "integer", "description": "排序标记", @@ -4568,27 +3668,7 @@ } } }, - "/sysExportTemplate/ExportTemplate": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SysExportTemplate" - ], - "summary": "导出表格模板", - "responses": {} - } - }, - "/sysExportTemplate/createSysExportTemplate": { + "/sysError/createSysError": { "post": { "security": [ { @@ -4602,31 +3682,43 @@ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "创建导出模板", + "summary": "创建错误日志", "parameters": [ { - "description": "创建导出模板", + "description": "创建错误日志", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/system.SysExportTemplate" + "$ref": "#/definitions/system.SysError" } } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"创建成功\"}", + "description": "创建成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/deleteSysExportTemplate": { + "/sysError/deleteSysError": { "delete": { "security": [ { @@ -4640,31 +3732,43 @@ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "删除导出模板", + "summary": "删除错误日志", "parameters": [ { - "description": "删除导出模板", + "description": "删除错误日志", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/system.SysExportTemplate" + "$ref": "#/definitions/system.SysError" } } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "description": "删除成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/deleteSysExportTemplateByIds": { + "/sysError/deleteSysErrorByIds": { "delete": { "security": [ { @@ -4678,31 +3782,32 @@ "application/json" ], "tags": [ - "SysExportTemplate" - ], - "summary": "批量删除导出模板", - "parameters": [ - { - "description": "批量删除导出模板", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.IdsReq" - } - } + "SysError" ], + "summary": "批量删除错误日志", "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"批量删除成功\"}", + "description": "批量删除成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/exportExcel": { + "/sysError/findSysError": { "get": { "security": [ { @@ -4716,100 +3821,44 @@ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "导出表格", - "responses": {} - } - }, - "/sysExportTemplate/findSysExportTemplate": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SysExportTemplate" - ], - "summary": "用id查询导出模板", + "summary": "用id查询错误日志", "parameters": [ { "type": "integer", - "description": "主键ID", + "description": "用id查询错误日志", "name": "ID", - "in": "query" - }, - { - "type": "string", - "description": "创建时间", - "name": "createdAt", - "in": "query" - }, - { - "type": "string", - "description": "数据库名称", - "name": "dbName", - "in": "query" - }, - { - "type": "integer", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "模板名称", - "name": "name", - "in": "query" - }, - { - "type": "string", - "name": "order", - "in": "query" - }, - { - "type": "string", - "description": "表名称", - "name": "tableName", - "in": "query" - }, - { - "type": "string", - "description": "模板标识", - "name": "templateID", - "in": "query" - }, - { - "type": "string", - "description": "模板信息", - "name": "templateInfo", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" + "in": "query", + "required": true } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", + "description": "查询成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.SysError" + }, + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/getSysExportTemplateList": { + "/sysError/getSysErrorList": { "get": { "security": [ { @@ -4823,31 +3872,27 @@ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "分页获取导出模板列表", + "summary": "分页获取错误日志列表", "parameters": [ { - "type": "integer", - "description": "主键ID", - "name": "ID", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "createdAtRange", "in": "query" }, { "type": "string", - "description": "创建时间", - "name": "createdAt", + "name": "form", "in": "query" }, { "type": "string", - "description": "数据库名称", - "name": "dbName", - "in": "query" - }, - { - "type": "string", - "name": "endCreatedAt", + "name": "info", "in": "query" }, { @@ -4856,22 +3901,6 @@ "name": "keyword", "in": "query" }, - { - "type": "integer", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "模板名称", - "name": "name", - "in": "query" - }, - { - "type": "string", - "name": "order", - "in": "query" - }, { "type": "integer", "description": "页码", @@ -4883,49 +3912,35 @@ "description": "每页大小", "name": "pageSize", "in": "query" - }, - { - "type": "string", - "name": "startCreatedAt", - "in": "query" - }, - { - "type": "string", - "description": "表名称", - "name": "tableName", - "in": "query" - }, - { - "type": "string", - "description": "模板标识", - "name": "templateID", - "in": "query" - }, - { - "type": "string", - "description": "模板信息", - "name": "templateInfo", - "in": "query" - }, - { - "type": "string", - "description": "更新时间", - "name": "updatedAt", - "in": "query" } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "description": "获取成功", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] } } } } }, - "/sysExportTemplate/importExcel": { - "post": { + "/sysError/getSysErrorSolution": { + "get": { "security": [ { "ApiKeyAuth": [] @@ -4938,13 +3953,41 @@ "application/json" ], "tags": [ - "SysImportTemplate" + "SysError" ], - "summary": "导入表格", - "responses": {} + "summary": "根据ID触发处理:标记为处理中,1分钟后自动改为处理完成", + "parameters": [ + { + "type": "string", + "description": "错误日志ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "处理已提交", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } } }, - "/sysExportTemplate/updateSysExportTemplate": { + "/sysError/updateSysError": { "put": { "security": [ { @@ -4958,61 +4001,23 @@ "application/json" ], "tags": [ - "SysExportTemplate" + "SysError" ], - "summary": "更新导出模板", + "summary": "更新错误日志", "parameters": [ { - "description": "更新导出模板", + "description": "更新错误日志", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/system.SysExportTemplate" + "$ref": "#/definitions/system.SysError" } } ], "responses": { "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/sysOperationRecord/createSysOperationRecord": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SysOperationRecord" - ], - "summary": "创建SysOperationRecord", - "parameters": [ - { - "description": "创建SysOperationRecord", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/system.SysOperationRecord" - } - } - ], - "responses": { - "200": { - "description": "创建SysOperationRecord", + "description": "更新成功", "schema": { "allOf": [ { @@ -5898,10 +4903,10 @@ "tags": [ "System" ], - "summary": "重启系统", + "summary": "重载系统", "responses": { "200": { - "description": "重启系统", + "description": "重载系统", "schema": { "allOf": [ { @@ -6518,6 +5523,136 @@ } }, "definitions": { + "common.ExaAttachmentCategory": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/common.ExaAttachmentCategory" + } + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "name": { + "type": "string" + }, + "pid": { + "type": "integer" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "common.ExaFile": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "chunkTotal": { + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileChunk": { + "type": "array", + "items": { + "$ref": "#/definitions/common.ExaFileChunk" + } + }, + "fileMd5": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "filePath": { + "type": "string" + }, + "isFinish": { + "type": "boolean" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "common.ExaFileChunk": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "exaFileID": { + "type": "integer" + }, + "fileChunkNumber": { + "type": "integer" + }, + "fileChunkPath": { + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + } + } + }, + "common.ExaFileUploadAndDownload": { + "type": "object", + "properties": { + "ID": { + "description": "主键ID", + "type": "integer" + }, + "classId": { + "description": "分类id", + "type": "integer" + }, + "createdAt": { + "description": "创建时间", + "type": "string" + }, + "key": { + "description": "编号", + "type": "string" + }, + "name": { + "description": "文件名", + "type": "string" + }, + "tag": { + "description": "文件标签", + "type": "string" + }, + "updatedAt": { + "description": "更新时间", + "type": "string" + }, + "url": { + "description": "文件地址", + "type": "string" + } + } + }, "common.JSONMap": { "type": "object", "additionalProperties": true @@ -6545,26 +5680,6 @@ } } }, - "config.Autocode": { - "type": "object", - "properties": { - "ai-path": { - "type": "string" - }, - "module": { - "type": "string" - }, - "root": { - "type": "string" - }, - "server": { - "type": "string" - }, - "web": { - "type": "string" - } - } - }, "config.AwsS3": { "type": "object", "properties": { @@ -6647,7 +5762,7 @@ "type": "integer" }, "open-captcha": { - "description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码", + "description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码次数,如3代表错误三次后出现验证码", "type": "integer" }, "open-captcha-timeout": { @@ -6687,6 +5802,43 @@ } } }, + "config.Email": { + "type": "object", + "properties": { + "from": { + "description": "发件人 你自己要发邮件的邮箱", + "type": "string" + }, + "host": { + "description": "服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议", + "type": "string" + }, + "is-loginauth": { + "description": "是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等)", + "type": "boolean" + }, + "is-ssl": { + "description": "是否SSL 是否开启SSL", + "type": "boolean" + }, + "nickname": { + "description": "昵称 发件人昵称 通常为自己的邮箱", + "type": "string" + }, + "port": { + "description": "端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465", + "type": "integer" + }, + "secret": { + "description": "密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥", + "type": "string" + }, + "to": { + "description": "收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用", + "type": "string" + } + } + }, "config.Excel": { "type": "object", "properties": { @@ -6749,6 +5901,48 @@ } } }, + "config.MCP": { + "type": "object", + "properties": { + "addr": { + "type": "integer" + }, + "auth_header": { + "type": "string" + }, + "base_url": { + "type": "string" + }, + "message_path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "request_timeout": { + "type": "integer" + }, + "separate": { + "type": "boolean" + }, + "sse_path": { + "description": "Deprecated fields kept for backward compatibility with older configs.", + "type": "string" + }, + "upstream_base_url": { + "type": "string" + }, + "url_prefix": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "config.Minio": { "type": "object", "properties": { @@ -7147,14 +6341,6 @@ "aliyun-oss": { "$ref": "#/definitions/config.AliyunOSS" }, - "autocode": { - "description": "auto", - "allOf": [ - { - "$ref": "#/definitions/config.Autocode" - } - ] - }, "aws-s3": { "$ref": "#/definitions/config.AwsS3" }, @@ -7185,7 +6371,7 @@ } }, "email": { - "$ref": "#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email" + "$ref": "#/definitions/config.Email" }, "excel": { "$ref": "#/definitions/config.Excel" @@ -7204,6 +6390,14 @@ } ] }, + "mcp": { + "description": "MCP配置", + "allOf": [ + { + "$ref": "#/definitions/config.MCP" + } + ] + }, "minio": { "$ref": "#/definitions/config.Minio" }, @@ -7389,6 +6583,10 @@ "description": "数据库类型:mysql(默认)|sqlite|sqlserver|postgresql", "type": "string" }, + "disable-auto-migrate": { + "description": "自动迁移数据库表结构,生产环境建议设为false,手动迁移", + "type": "boolean" + }, "iplimit-count": { "type": "integer" }, @@ -7484,246 +6682,6 @@ } } }, - "example.ExaAttachmentCategory": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "children": { - "type": "array", - "items": { - "$ref": "#/definitions/example.ExaAttachmentCategory" - } - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "name": { - "type": "string" - }, - "pid": { - "type": "integer" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaCustomer": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "customerName": { - "description": "客户名", - "type": "string" - }, - "customerPhoneData": { - "description": "客户手机号", - "type": "string" - }, - "sysUser": { - "description": "管理详情", - "allOf": [ - { - "$ref": "#/definitions/system.SysUser" - } - ] - }, - "sysUserAuthorityID": { - "description": "管理角色ID", - "type": "integer" - }, - "sysUserId": { - "description": "管理ID", - "type": "integer" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaFile": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "chunkTotal": { - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "exaFileChunk": { - "type": "array", - "items": { - "$ref": "#/definitions/example.ExaFileChunk" - } - }, - "fileMd5": { - "type": "string" - }, - "fileName": { - "type": "string" - }, - "filePath": { - "type": "string" - }, - "isFinish": { - "type": "boolean" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaFileChunk": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "exaFileID": { - "type": "integer" - }, - "fileChunkNumber": { - "type": "integer" - }, - "fileChunkPath": { - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "example.ExaFileUploadAndDownload": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "classId": { - "description": "分类id", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "key": { - "description": "编号", - "type": "string" - }, - "name": { - "description": "文件名", - "type": "string" - }, - "tag": { - "description": "文件标签", - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - }, - "url": { - "description": "文件地址", - "type": "string" - } - } - }, - "github_com_flipped-aurora_gin-vue-admin_server_config.Email": { - "type": "object", - "properties": { - "from": { - "description": "发件人 你自己要发邮件的邮箱", - "type": "string" - }, - "host": { - "description": "服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议", - "type": "string" - }, - "is-ssl": { - "description": "是否SSL 是否开启SSL", - "type": "boolean" - }, - "nickname": { - "description": "昵称 发件人昵称 通常为自己的邮箱", - "type": "string" - }, - "port": { - "description": "端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465", - "type": "integer" - }, - "secret": { - "description": "密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥", - "type": "string" - }, - "to": { - "description": "收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用", - "type": "string" - } - } - }, - "model.Info": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "attachments": { - "description": "附件", - "type": "array", - "items": { - "type": "object" - } - }, - "content": { - "description": "内容", - "type": "string" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "title": { - "description": "标题", - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - }, - "userID": { - "description": "作者", - "type": "integer" - } - } - }, "request.AddMenuAuthorityInfo": { "type": "object", "properties": { @@ -7739,219 +6697,6 @@ } } }, - "request.AutoCode": { - "type": "object", - "properties": { - "abbreviation": { - "description": "Struct简称", - "type": "string", - "example": "Struct简称" - }, - "autoCreateApiToSql": { - "description": "是否自动创建api", - "type": "boolean", - "example": false - }, - "autoCreateBtnAuth": { - "description": "是否自动创建按钮权限", - "type": "boolean", - "example": false - }, - "autoCreateMenuToSql": { - "description": "是否自动创建menu", - "type": "boolean", - "example": false - }, - "autoCreateResource": { - "description": "是否自动创建资源标识", - "type": "boolean", - "example": false - }, - "autoMigrate": { - "description": "是否自动迁移表结构", - "type": "boolean", - "example": false - }, - "businessDB": { - "description": "业务数据库", - "type": "string", - "example": "业务数据库" - }, - "description": { - "description": "Struct中文名称", - "type": "string", - "example": "Struct中文名称" - }, - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/request.AutoCodeField" - } - }, - "generateServer": { - "description": "是否生成server", - "type": "boolean", - "example": true - }, - "generateWeb": { - "description": "是否生成web", - "type": "boolean", - "example": true - }, - "gvaModel": { - "description": "是否使用gva默认Model", - "type": "boolean", - "example": false - }, - "humpPackageName": { - "description": "go文件名称", - "type": "string", - "example": "go文件名称" - }, - "isAdd": { - "description": "是否新增", - "type": "boolean", - "example": false - }, - "isTree": { - "description": "是否树形结构", - "type": "boolean", - "example": false - }, - "onlyTemplate": { - "description": "是否只生成模板", - "type": "boolean", - "example": false - }, - "package": { - "type": "string" - }, - "packageName": { - "description": "文件名称", - "type": "string", - "example": "文件名称" - }, - "primaryField": { - "$ref": "#/definitions/request.AutoCodeField" - }, - "structName": { - "description": "Struct名称", - "type": "string", - "example": "Struct名称" - }, - "tableName": { - "description": "表名", - "type": "string", - "example": "表名" - }, - "treeJson": { - "description": "展示的树json字段", - "type": "string", - "example": "展示的树json字段" - } - } - }, - "request.AutoCodeField": { - "type": "object", - "properties": { - "checkDataSource": { - "description": "是否检查数据源", - "type": "boolean" - }, - "clearable": { - "description": "是否可清空", - "type": "boolean" - }, - "columnName": { - "description": "数据库字段", - "type": "string" - }, - "comment": { - "description": "数据库字段描述", - "type": "string" - }, - "dataSource": { - "description": "数据源", - "allOf": [ - { - "$ref": "#/definitions/request.DataSource" - } - ] - }, - "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" - } - } - }, "request.CasbinInReceive": { "type": "object", "properties": { @@ -7993,30 +6738,6 @@ } } }, - "request.DataSource": { - "type": "object", - "properties": { - "association": { - "description": "关联关系 1 一对一 2 一对多", - "type": "integer" - }, - "dbName": { - "type": "string" - }, - "hasDeletedAt": { - "type": "boolean" - }, - "label": { - "type": "string" - }, - "table": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, "request.Empty": { "type": "object" }, @@ -8061,6 +6782,10 @@ "request.GetUserList": { "type": "object", "properties": { + "desc": { + "description": "排序方式:升序false(默认)|降序true", + "type": "boolean" + }, "email": { "type": "string" }, @@ -8071,6 +6796,10 @@ "nickName": { "type": "string" }, + "orderKey": { + "description": "排序", + "type": "string" + }, "page": { "description": "页码", "type": "integer" @@ -8098,6 +6827,18 @@ } } }, + "request.ImportSysDictionaryRequest": { + "type": "object", + "required": [ + "json" + ], + "properties": { + "json": { + "description": "JSON字符串", + "type": "string" + } + } + }, "request.InitDB": { "type": "object", "required": [ @@ -8274,6 +7015,58 @@ } } }, + "request.SetApiAuthorities": { + "type": "object", + "properties": { + "authorityIds": { + "description": "角色ID列表", + "type": "array", + "items": { + "type": "integer" + } + }, + "method": { + "description": "请求方法", + "type": "string" + }, + "path": { + "description": "API路径", + "type": "string" + } + } + }, + "request.SetMenuAuthorities": { + "type": "object", + "properties": { + "authorityIds": { + "description": "角色ID列表", + "type": "array", + "items": { + "type": "integer" + } + }, + "menuId": { + "description": "菜单ID", + "type": "integer" + } + } + }, + "request.SetRoleUsers": { + "type": "object", + "properties": { + "authorityId": { + "description": "角色ID", + "type": "integer" + }, + "userIds": { + "description": "用户ID列表", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "request.SetUserAuth": { "type": "object", "properties": { @@ -8315,78 +7108,11 @@ } } }, - "request.SysAutoCodePackageCreate": { - "type": "object", - "properties": { - "desc": { - "type": "string", - "example": "描述" - }, - "label": { - "type": "string", - "example": "展示名" - }, - "packageName": { - "type": "string", - "example": "包名" - }, - "template": { - "type": "string", - "example": "模版" - } - } - }, - "request.SysAutoHistoryRollBack": { - "type": "object", - "properties": { - "deleteApi": { - "description": "是否删除接口", - "type": "boolean" - }, - "deleteMenu": { - "description": "是否删除菜单", - "type": "boolean" - }, - "deleteTable": { - "description": "是否删除表", - "type": "boolean" - }, - "id": { - "description": "主键ID", - "type": "integer" - } - } - }, - "response.Email": { - "type": "object", - "properties": { - "body": { - "description": "邮件内容", - "type": "string" - }, - "subject": { - "description": "邮件标题", - "type": "string" - }, - "to": { - "description": "邮件发送给谁", - "type": "string" - } - } - }, - "response.ExaCustomerResponse": { - "type": "object", - "properties": { - "customer": { - "$ref": "#/definitions/example.ExaCustomer" - } - } - }, "response.ExaFileResponse": { "type": "object", "properties": { "file": { - "$ref": "#/definitions/example.ExaFileUploadAndDownload" + "$ref": "#/definitions/common.ExaFileUploadAndDownload" } } }, @@ -8402,7 +7128,7 @@ "type": "object", "properties": { "file": { - "$ref": "#/definitions/example.ExaFile" + "$ref": "#/definitions/common.ExaFile" } } }, @@ -8571,64 +7297,6 @@ } } }, - "system.Condition": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "column": { - "type": "string" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "from": { - "type": "string" - }, - "operator": { - "type": "string" - }, - "templateID": { - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, - "system.JoinTemplate": { - "type": "object", - "properties": { - "ID": { - "description": "主键ID", - "type": "integer" - }, - "createdAt": { - "description": "创建时间", - "type": "string" - }, - "joins": { - "type": "string" - }, - "on": { - "type": "string" - }, - "table": { - "type": "string" - }, - "templateID": { - "type": "string" - }, - "updatedAt": { - "description": "更新时间", - "type": "string" - } - } - }, "system.Meta": { "type": "object", "properties": { @@ -8654,6 +7322,10 @@ "title": { "description": "菜单名", "type": "string" + }, + "transitionType": { + "description": "路由切换动画", + "type": "string" } } }, @@ -8878,6 +7550,13 @@ "description": "主键ID", "type": "integer" }, + "children": { + "description": "子字典", + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionary" + } + }, "createdAt": { "description": "创建时间", "type": "string" @@ -8890,6 +7569,10 @@ "description": "字典名(中)", "type": "string" }, + "parentID": { + "description": "父级字典ID", + "type": "integer" + }, "status": { "description": "状态", "type": "boolean" @@ -8917,10 +7600,21 @@ "description": "主键ID", "type": "integer" }, + "children": { + "description": "子字典详情", + "type": "array", + "items": { + "$ref": "#/definitions/system.SysDictionaryDetail" + } + }, "createdAt": { "description": "创建时间", "type": "string" }, + "disabled": { + "description": "禁用状态,根据status字段动态计算", + "type": "boolean" + }, "extend": { "description": "扩展值", "type": "string" @@ -8929,6 +7623,18 @@ "description": "展示值", "type": "string" }, + "level": { + "description": "层级深度,从0开始", + "type": "integer" + }, + "parentID": { + "description": "父级字典详情ID", + "type": "integer" + }, + "path": { + "description": "层级路径,如 \"1,2,3\"", + "type": "string" + }, "sort": { "description": "排序标记", "type": "integer" @@ -8951,53 +7657,37 @@ } } }, - "system.SysExportTemplate": { + "system.SysError": { "type": "object", + "required": [ + "form" + ], "properties": { "ID": { "description": "主键ID", "type": "integer" }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/definitions/system.Condition" - } - }, "createdAt": { "description": "创建时间", "type": "string" }, - "dbName": { - "description": "数据库名称", + "form": { + "description": "错误来源", "type": "string" }, - "joinTemplate": { - "type": "array", - "items": { - "$ref": "#/definitions/system.JoinTemplate" - } - }, - "limit": { - "type": "integer" - }, - "name": { - "description": "模板名称", + "info": { + "description": "错误内容", "type": "string" }, - "order": { + "level": { "type": "string" }, - "tableName": { - "description": "表名称", + "solution": { + "description": "解决方案", "type": "string" }, - "templateID": { - "description": "模板标识", - "type": "string" - }, - "templateInfo": { - "description": "模板信息", + "status": { + "description": "处理状态:未处理/处理中/处理完成", "type": "string" }, "updatedAt": { diff --git a/server/docs/swagger.yaml b/server/docs/swagger.yaml index 1a1f8cc..8531bbf 100644 --- a/server/docs/swagger.yaml +++ b/server/docs/swagger.yaml @@ -1,4 +1,95 @@ definitions: + common.ExaAttachmentCategory: + properties: + ID: + description: 主键ID + type: integer + children: + items: + $ref: '#/definitions/common.ExaAttachmentCategory' + type: array + createdAt: + description: 创建时间 + type: string + name: + type: string + pid: + type: integer + updatedAt: + description: 更新时间 + type: string + type: object + common.ExaFile: + properties: + ID: + description: 主键ID + type: integer + chunkTotal: + type: integer + createdAt: + description: 创建时间 + type: string + exaFileChunk: + items: + $ref: '#/definitions/common.ExaFileChunk' + type: array + fileMd5: + type: string + fileName: + type: string + filePath: + type: string + isFinish: + type: boolean + updatedAt: + description: 更新时间 + type: string + type: object + common.ExaFileChunk: + properties: + ID: + description: 主键ID + type: integer + createdAt: + description: 创建时间 + type: string + exaFileID: + type: integer + fileChunkNumber: + type: integer + fileChunkPath: + type: string + updatedAt: + description: 更新时间 + type: string + type: object + common.ExaFileUploadAndDownload: + properties: + ID: + description: 主键ID + type: integer + classId: + description: 分类id + type: integer + createdAt: + description: 创建时间 + type: string + key: + description: 编号 + type: string + name: + description: 文件名 + type: string + tag: + description: 文件标签 + type: string + updatedAt: + description: 更新时间 + type: string + url: + description: 文件地址 + type: string + type: object common.JSONMap: additionalProperties: true type: object @@ -17,19 +108,6 @@ definitions: endpoint: type: string type: object - config.Autocode: - properties: - ai-path: - type: string - module: - type: string - root: - type: string - server: - type: string - web: - type: string - type: object config.AwsS3: properties: base-url: @@ -85,7 +163,7 @@ definitions: description: 验证码长度 type: integer open-captcha: - description: 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码 + description: 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码次数,如3代表错误三次后出现验证码 type: integer open-captcha-timeout: description: 防爆破验证码超时时间,单位:s(秒) @@ -111,6 +189,33 @@ definitions: mount-point: type: string type: object + config.Email: + properties: + from: + description: 发件人 你自己要发邮件的邮箱 + type: string + host: + description: 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 + type: string + is-loginauth: + description: 是否LoginAuth 是否使用LoginAuth认证方式(适用于IBM、微软邮箱服务器等) + type: boolean + is-ssl: + description: 是否SSL 是否开启SSL + type: boolean + nickname: + description: 昵称 发件人昵称 通常为自己的邮箱 + type: string + port: + description: 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 + type: integer + secret: + description: 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 + type: string + to: + description: 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 + type: string + type: object config.Excel: properties: dir: @@ -153,6 +258,35 @@ definitions: description: 本地文件存储路径 type: string type: object + config.MCP: + properties: + addr: + type: integer + auth_header: + type: string + base_url: + type: string + message_path: + type: string + name: + type: string + path: + type: string + request_timeout: + type: integer + separate: + type: boolean + sse_path: + description: Deprecated fields kept for backward compatibility with older + configs. + type: string + upstream_base_url: + type: string + url_prefix: + type: string + version: + type: string + type: object config.Minio: properties: access-key-id: @@ -443,10 +577,6 @@ definitions: properties: aliyun-oss: $ref: '#/definitions/config.AliyunOSS' - autocode: - allOf: - - $ref: '#/definitions/config.Autocode' - description: auto aws-s3: $ref: '#/definitions/config.AwsS3' captcha: @@ -466,7 +596,7 @@ definitions: $ref: '#/definitions/config.DiskList' type: array email: - $ref: '#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email' + $ref: '#/definitions/config.Email' excel: $ref: '#/definitions/config.Excel' hua-wei-obs: @@ -477,6 +607,10 @@ definitions: allOf: - $ref: '#/definitions/config.Local' description: oss + mcp: + allOf: + - $ref: '#/definitions/config.MCP' + description: MCP配置 minio: $ref: '#/definitions/config.Minio' mongo: @@ -608,6 +742,9 @@ definitions: db-type: description: 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql type: string + disable-auto-migrate: + description: 自动迁移数据库表结构,生产环境建议设为false,手动迁移 + type: boolean iplimit-count: type: integer iplimit-time: @@ -675,175 +812,6 @@ 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: - description: 主键ID - type: integer - createdAt: - description: 创建时间 - type: string - customerName: - description: 客户名 - type: string - customerPhoneData: - description: 客户手机号 - type: string - sysUser: - allOf: - - $ref: '#/definitions/system.SysUser' - description: 管理详情 - sysUserAuthorityID: - description: 管理角色ID - type: integer - sysUserId: - description: 管理ID - type: integer - updatedAt: - description: 更新时间 - type: string - type: object - example.ExaFile: - properties: - ID: - description: 主键ID - type: integer - chunkTotal: - type: integer - createdAt: - description: 创建时间 - type: string - exaFileChunk: - items: - $ref: '#/definitions/example.ExaFileChunk' - type: array - fileMd5: - type: string - fileName: - type: string - filePath: - type: string - isFinish: - type: boolean - updatedAt: - description: 更新时间 - type: string - type: object - example.ExaFileChunk: - properties: - ID: - description: 主键ID - type: integer - createdAt: - description: 创建时间 - type: string - exaFileID: - type: integer - fileChunkNumber: - type: integer - fileChunkPath: - type: string - updatedAt: - description: 更新时间 - type: string - type: object - example.ExaFileUploadAndDownload: - properties: - ID: - description: 主键ID - type: integer - classId: - description: 分类id - type: integer - createdAt: - description: 创建时间 - type: string - key: - description: 编号 - type: string - name: - description: 文件名 - type: string - tag: - description: 文件标签 - type: string - updatedAt: - description: 更新时间 - type: string - url: - description: 文件地址 - type: string - type: object - github_com_flipped-aurora_gin-vue-admin_server_config.Email: - properties: - from: - description: 发件人 你自己要发邮件的邮箱 - type: string - host: - description: 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 - type: string - is-ssl: - description: 是否SSL 是否开启SSL - type: boolean - nickname: - description: 昵称 发件人昵称 通常为自己的邮箱 - type: string - port: - description: 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 - type: integer - secret: - description: 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 - type: string - to: - description: 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 - type: string - type: object - model.Info: - properties: - ID: - description: 主键ID - type: integer - attachments: - description: 附件 - items: - type: object - type: array - content: - description: 内容 - type: string - createdAt: - description: 创建时间 - type: string - title: - description: 标题 - type: string - updatedAt: - description: 更新时间 - type: string - userID: - description: 作者 - type: integer - type: object request.AddMenuAuthorityInfo: properties: authorityId: @@ -854,166 +822,6 @@ definitions: $ref: '#/definitions/system.SysBaseMenu' 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: authorityId: @@ -1042,22 +850,6 @@ 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: @@ -1088,6 +880,9 @@ definitions: type: object request.GetUserList: properties: + desc: + description: 排序方式:升序false(默认)|降序true + type: boolean email: type: string keyword: @@ -1095,6 +890,9 @@ definitions: type: string nickName: type: string + orderKey: + description: 排序 + type: string page: description: 页码 type: integer @@ -1113,6 +911,14 @@ definitions: type: integer type: array type: object + request.ImportSysDictionaryRequest: + properties: + json: + description: JSON字符串 + type: string + required: + - json + type: object request.InitDB: properties: adminPassword: @@ -1241,6 +1047,42 @@ definitions: description: 更新时间 type: string type: object + request.SetApiAuthorities: + properties: + authorityIds: + description: 角色ID列表 + items: + type: integer + type: array + method: + description: 请求方法 + type: string + path: + description: API路径 + type: string + type: object + request.SetMenuAuthorities: + properties: + authorityIds: + description: 角色ID列表 + items: + type: integer + type: array + menuId: + description: 菜单ID + type: integer + type: object + request.SetRoleUsers: + properties: + authorityId: + description: 角色ID + type: integer + userIds: + description: 用户ID列表 + items: + type: integer + type: array + type: object request.SetUserAuth: properties: authorityId: @@ -1268,57 +1110,10 @@ definitions: type: integer type: array type: object - request.SysAutoCodePackageCreate: - properties: - desc: - example: 描述 - type: string - label: - example: 展示名 - type: string - packageName: - example: 包名 - type: string - template: - example: 模版 - type: string - type: object - request.SysAutoHistoryRollBack: - properties: - deleteApi: - description: 是否删除接口 - type: boolean - deleteMenu: - description: 是否删除菜单 - type: boolean - deleteTable: - description: 是否删除表 - type: boolean - id: - description: 主键ID - type: integer - type: object - response.Email: - properties: - body: - description: 邮件内容 - type: string - subject: - description: 邮件标题 - type: string - to: - description: 邮件发送给谁 - type: string - type: object - response.ExaCustomerResponse: - properties: - customer: - $ref: '#/definitions/example.ExaCustomer' - type: object response.ExaFileResponse: properties: file: - $ref: '#/definitions/example.ExaFileUploadAndDownload' + $ref: '#/definitions/common.ExaFileUploadAndDownload' type: object response.FilePathResponse: properties: @@ -1328,7 +1123,7 @@ definitions: response.FileResponse: properties: file: - $ref: '#/definitions/example.ExaFile' + $ref: '#/definitions/common.ExaFile' type: object response.LoginResponse: properties: @@ -1436,46 +1231,6 @@ definitions: user: $ref: '#/definitions/system.SysUser' type: object - system.Condition: - properties: - ID: - description: 主键ID - type: integer - column: - type: string - createdAt: - description: 创建时间 - type: string - from: - type: string - operator: - type: string - templateID: - type: string - updatedAt: - description: 更新时间 - type: string - type: object - system.JoinTemplate: - properties: - ID: - description: 主键ID - type: integer - createdAt: - description: 创建时间 - type: string - joins: - type: string - "on": - type: string - table: - type: string - templateID: - type: string - updatedAt: - description: 更新时间 - type: string - type: object system.Meta: properties: activeName: @@ -1495,6 +1250,9 @@ definitions: title: description: 菜单名 type: string + transitionType: + description: 路由切换动画 + type: string type: object system.SysApi: properties: @@ -1651,6 +1409,11 @@ definitions: ID: description: 主键ID type: integer + children: + description: 子字典 + items: + $ref: '#/definitions/system.SysDictionary' + type: array createdAt: description: 创建时间 type: string @@ -1660,6 +1423,9 @@ definitions: name: description: 字典名(中) type: string + parentID: + description: 父级字典ID + type: integer status: description: 状态 type: boolean @@ -1679,15 +1445,32 @@ definitions: ID: description: 主键ID type: integer + children: + description: 子字典详情 + items: + $ref: '#/definitions/system.SysDictionaryDetail' + type: array createdAt: description: 创建时间 type: string + disabled: + description: 禁用状态,根据status字段动态计算 + type: boolean extend: description: 扩展值 type: string label: description: 展示值 type: string + level: + description: 层级深度,从0开始 + type: integer + parentID: + description: 父级字典详情ID + type: integer + path: + description: 层级路径,如 "1,2,3" + type: string sort: description: 排序标记 type: integer @@ -1704,44 +1487,33 @@ definitions: description: 字典值 type: string type: object - system.SysExportTemplate: + system.SysError: properties: ID: description: 主键ID type: integer - conditions: - items: - $ref: '#/definitions/system.Condition' - type: array createdAt: description: 创建时间 type: string - dbName: - description: 数据库名称 + form: + description: 错误来源 type: string - joinTemplate: - items: - $ref: '#/definitions/system.JoinTemplate' - type: array - limit: - type: integer - name: - description: 模板名称 + info: + description: 错误内容 type: string - order: + level: type: string - tableName: - description: 表名称 + solution: + description: 解决方案 type: string - templateID: - description: 模板标识 - type: string - templateInfo: - description: 模板信息 + status: + description: 处理状态:未处理/处理中/处理完成 type: string updatedAt: description: 更新时间 type: string + required: + - form type: object system.SysMenu: properties: @@ -1927,9 +1699,9 @@ definitions: type: object info: contact: {} - description: 使用gin+vue进行极速开发的全栈开发基础平台 - title: Gin-Vue-Admin Swagger API接口文档 - version: v2.7.9-beta + description: 使用gin+react进行极速开发的全栈开发基础平台 + title: Gin-React-Admin Swagger API接口文档 + version: v2.9.1 paths: /api/createApi: post: @@ -2157,6 +1929,41 @@ paths: summary: 分页获取API列表 tags: - SysApi + /api/getApiRoles: + get: + consumes: + - application/json + parameters: + - description: API路径 + in: query + name: path + required: true + type: string + - description: 请求方法 + in: query + name: method + required: true + type: string + 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: 获取拥有指定API权限的角色ID列表 + tags: + - SysApi /api/ignoreApi: post: consumes: @@ -2178,6 +1985,34 @@ paths: summary: 忽略API tags: - IgnoreApi + /api/setApiRoles: + post: + consumes: + - application/json + parameters: + - description: API路径、请求方法和角色ID列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SetApiAuthorities' + produces: + - application/json + responses: + "200": + description: 设置成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 全量覆盖某API关联的角色列表 + tags: + - SysApi /api/syncApi: get: consumes: @@ -2237,7 +2072,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/example.ExaAttachmentCategory' + $ref: '#/definitions/common.ExaAttachmentCategory' produces: - application/json responses: {} @@ -2286,7 +2121,7 @@ paths: - $ref: '#/definitions/response.Response' - properties: data: - $ref: '#/definitions/example.ExaAttachmentCategory' + $ref: '#/definitions/common.ExaAttachmentCategory' msg: type: string type: object @@ -2413,6 +2248,37 @@ paths: summary: 分页获取角色列表 tags: - Authority + /authority/getUsersByAuthority: + get: + consumes: + - application/json + parameters: + - description: 角色ID + in: query + name: authorityId + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 获取成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + type: integer + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取拥有指定角色的用户ID列表 + tags: + - Authority /authority/setDataAuthority: post: consumes: @@ -2441,6 +2307,34 @@ paths: summary: 设置角色资源权限 tags: - Authority + /authority/setRoleUsers: + post: + consumes: + - application/json + parameters: + - description: 角色ID和用户ID列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SetRoleUsers' + produces: + - application/json + responses: + "200": + description: 设置成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 全量覆盖某角色关联的用户列表 + tags: + - Authority /authority/updateAuthority: put: consumes: @@ -2550,491 +2444,6 @@ paths: summary: 设置权限按钮 tags: - AuthorityBtn - /autoCode/addFunc: - post: - consumes: - - application/json - parameters: - - description: 增加方法 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.AutoCode' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"创建成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 增加方法 - tags: - - AddFunc - /autoCode/createPackage: - post: - consumes: - - application/json - parameters: - - description: 创建package - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.SysAutoCodePackageCreate' - produces: - - application/json - responses: - "200": - description: 创建package成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - additionalProperties: true - type: object - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 创建package - tags: - - AutoCodePackage - /autoCode/createTemp: - post: - consumes: - - application/json - parameters: - - description: 创建自动代码 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.AutoCode' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"创建成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 自动代码模板 - tags: - - AutoCodeTemplate - /autoCode/delPackage: - post: - consumes: - - application/json - parameters: - - description: 创建package - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.GetById' - produces: - - application/json - responses: - "200": - description: 删除package成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - additionalProperties: true - type: object - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 删除package - tags: - - AutoCode - /autoCode/delSysHistory: - post: - consumes: - - application/json - parameters: - - description: 请求参数 - 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: - - ApiKeyAuth: [] - summary: 删除回滚记录 - tags: - - AutoCode - /autoCode/getColumn: - get: - 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: - - AutoCode - /autoCode/getDB: - get: - 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: - - AutoCode - /autoCode/getMeta: - post: - consumes: - - application/json - parameters: - - description: 请求参数 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.GetById' - produces: - - application/json - responses: - "200": - description: 获取meta信息 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - additionalProperties: true - type: object - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 获取meta信息 - tags: - - AutoCode - /autoCode/getPackage: - post: - consumes: - - application/json - produces: - - application/json - responses: - "200": - description: 创建package成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - additionalProperties: true - type: object - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 获取package - tags: - - AutoCodePackage - /autoCode/getSysHistory: - post: - consumes: - - application/json - parameters: - - description: 请求参数 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.PageInfo' - 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: - - AutoCode - /autoCode/getTables: - get: - 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: - - AutoCode - /autoCode/getTemplates: - get: - consumes: - - application/json - produces: - - application/json - responses: - "200": - description: 创建package成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - additionalProperties: true - type: object - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - 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: - - multipart/form-data - parameters: - - description: this is a test file - in: formData - name: plug - required: true - type: file - produces: - - application/json - responses: - "200": - description: 安装插件成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - items: - type: object - type: array - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 安装插件 - tags: - - AutoCodePlugin - /autoCode/preview: - post: - consumes: - - application/json - parameters: - - description: 预览创建代码 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.AutoCode' - 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: - - AutoCodeTemplate - /autoCode/pubPlug: - post: - consumes: - - application/json - parameters: - - description: 插件名称 - in: query - name: plugName - required: true - type: string - 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/rollback: - post: - consumes: - - application/json - parameters: - - description: 请求参数 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.SysAutoHistoryRollBack' - produces: - - application/json - responses: - "200": - description: 回滚自动生成代码 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 回滚自动生成代码 - tags: - - AutoCode /base/captcha: post: consumes: @@ -3142,210 +2551,6 @@ paths: summary: 获取权限列表 tags: - Casbin - /customer/customer: - delete: - consumes: - - application/json - parameters: - - description: 客户ID - in: body - name: data - required: true - schema: - $ref: '#/definitions/example.ExaCustomer' - produces: - - application/json - responses: - "200": - description: 删除客户 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 删除客户 - tags: - - ExaCustomer - get: - consumes: - - application/json - parameters: - - description: 主键ID - in: query - name: ID - type: integer - - description: 创建时间 - in: query - name: createdAt - type: string - - description: 客户名 - in: query - name: customerName - type: string - - description: 客户手机号 - in: query - name: customerPhoneData - type: string - - description: 管理角色ID - in: query - name: sysUserAuthorityID - type: integer - - description: 管理ID - in: query - name: sysUserId - type: integer - - description: 更新时间 - in: query - name: updatedAt - type: string - produces: - - application/json - responses: - "200": - description: 获取单一客户信息,返回包括客户详情 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - $ref: '#/definitions/response.ExaCustomerResponse' - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 获取单一客户信息 - tags: - - ExaCustomer - post: - consumes: - - application/json - parameters: - - description: 客户用户名, 客户手机号码 - in: body - name: data - required: true - schema: - $ref: '#/definitions/example.ExaCustomer' - produces: - - application/json - responses: - "200": - description: 创建客户 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 创建客户 - tags: - - ExaCustomer - put: - consumes: - - application/json - parameters: - - description: 客户ID, 客户信息 - in: body - name: data - required: true - schema: - $ref: '#/definitions/example.ExaCustomer' - produces: - - application/json - responses: - "200": - description: 更新客户信息 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 更新客户信息 - tags: - - ExaCustomer - /customer/customerList: - get: - consumes: - - application/json - parameters: - - description: 关键字 - in: query - name: keyword - type: string - - description: 页码 - in: query - name: page - type: integer - - description: 每页大小 - in: query - name: pageSize - type: integer - 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: - - ExaCustomer - /email/emailTest: - post: - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发送成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 发送测试邮件 - tags: - - System - /email/sendEmail: - post: - parameters: - - description: 发送邮件必须的参数 - in: body - name: data - required: true - schema: - $ref: '#/definitions/response.Email' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发送成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 发送邮件 - tags: - - System /fileUploadAndDownload/breakpointContinue: post: consumes: @@ -3381,7 +2586,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/example.ExaFileUploadAndDownload' + $ref: '#/definitions/common.ExaFileUploadAndDownload' produces: - application/json responses: @@ -3494,7 +2699,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/example.ExaFileUploadAndDownload' + $ref: '#/definitions/common.ExaFileUploadAndDownload' produces: - application/json responses: @@ -3568,262 +2773,6 @@ paths: summary: 上传文件示例 tags: - ExaFileUploadAndDownload - /info/createInfo: - post: - consumes: - - application/json - parameters: - - description: 创建公告 - in: body - name: data - required: true - schema: - $ref: '#/definitions/model.Info' - produces: - - application/json - responses: - "200": - description: 创建成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 创建公告 - tags: - - Info - /info/deleteInfo: - delete: - consumes: - - application/json - parameters: - - description: 删除公告 - in: body - name: data - required: true - schema: - $ref: '#/definitions/model.Info' - produces: - - application/json - responses: - "200": - description: 删除成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 删除公告 - tags: - - Info - /info/deleteInfoByIds: - 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: - - Info - /info/findInfo: - get: - consumes: - - application/json - parameters: - - description: 主键ID - in: query - name: ID - type: integer - - description: 内容 - in: query - name: content - type: string - - description: 创建时间 - in: query - name: createdAt - type: string - - description: 标题 - in: query - name: title - type: string - - description: 更新时间 - in: query - name: updatedAt - type: string - - description: 作者 - in: query - name: userID - type: integer - produces: - - application/json - responses: - "200": - description: 查询成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - $ref: '#/definitions/model.Info' - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 用id查询公告 - tags: - - Info - /info/getInfoDataSource: - get: - consumes: - - application/json - produces: - - application/json - responses: - "200": - description: 查询成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - type: object - msg: - type: string - type: object - summary: 获取Info的数据源 - tags: - - Info - /info/getInfoList: - get: - consumes: - - application/json - parameters: - - in: query - name: endCreatedAt - type: string - - description: 关键字 - in: query - name: keyword - 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: - - Info - /info/getInfoPublic: - get: - consumes: - - application/json - parameters: - - in: query - name: endCreatedAt - type: string - - description: 关键字 - in: query - name: keyword - 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: - type: object - msg: - type: string - type: object - summary: 不需要鉴权的公告接口 - tags: - - Info - /info/updateInfo: - put: - consumes: - - application/json - parameters: - - description: 更新公告 - in: body - name: data - required: true - schema: - $ref: '#/definitions/model.Info' - produces: - - application/json - responses: - "200": - description: 更新成功 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 更新公告 - tags: - - Info /init/checkdb: post: produces: @@ -4120,6 +3069,64 @@ paths: summary: 分页获取基础menu列表 tags: - Menu + /menu/getMenuRoles: + get: + consumes: + - application/json + parameters: + - description: 菜单ID + in: query + name: menuId + required: true + type: integer + 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: 获取拥有指定菜单的角色ID列表 + tags: + - AuthorityMenu + /menu/setMenuRoles: + post: + consumes: + - application/json + parameters: + - description: 菜单ID和角色ID列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.SetMenuAuthorities' + produces: + - application/json + responses: + "200": + description: 设置成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 全量覆盖某菜单关联的角色列表 + tags: + - AuthorityMenu /menu/updateBaseMenu: post: consumes: @@ -4204,6 +3211,63 @@ paths: summary: 删除SysDictionary tags: - SysDictionary + /sysDictionary/exportSysDictionary: + 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: name + type: string + - description: 父级字典ID + in: query + name: parentID + type: integer + - description: 状态 + in: query + name: status + type: boolean + - description: 字典名(英) + in: query + name: type + type: string + - description: 更新时间 + in: query + name: updatedAt + type: string + produces: + - application/json + responses: + "200": + description: 导出字典JSON + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + additionalProperties: true + type: object + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 导出字典JSON(包含字典详情) + tags: + - SysDictionary /sysDictionary/findSysDictionary: get: consumes: @@ -4225,6 +3289,10 @@ paths: in: query name: name type: string + - description: 父级字典ID + in: query + name: parentID + type: integer - description: 状态 in: query name: status @@ -4261,6 +3329,11 @@ paths: get: consumes: - application/json + parameters: + - description: 字典名(中) + in: query + name: name + type: string produces: - application/json responses: @@ -4280,6 +3353,34 @@ paths: summary: 分页获取SysDictionary列表 tags: - SysDictionary + /sysDictionary/importSysDictionary: + post: + consumes: + - application/json + parameters: + - description: 字典JSON数据 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.ImportSysDictionaryRequest' + produces: + - application/json + responses: + "200": + description: 导入字典 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 导入字典JSON(包含字典详情) + tags: + - SysDictionary /sysDictionary/updateSysDictionary: put: consumes: @@ -4377,6 +3478,10 @@ paths: in: query name: createdAt type: string + - description: 禁用状态,根据status字段动态计算 + in: query + name: disabled + type: boolean - description: 扩展值 in: query name: extend @@ -4385,6 +3490,18 @@ paths: in: query name: label type: string + - description: 层级深度,从0开始 + in: query + name: level + type: integer + - description: 父级字典详情ID + in: query + name: parentID + type: integer + - description: 层级路径,如 "1,2,3" + in: query + name: path + type: string - description: 排序标记 in: query name: sort @@ -4425,6 +3542,138 @@ paths: summary: 用id查询SysDictionaryDetail tags: - SysDictionaryDetail + /sysDictionaryDetail/getDictionaryDetailsByParent: + get: + consumes: + - application/json + parameters: + - description: 是否包含子级数据 + in: query + name: includeChildren + type: boolean + - description: 父级字典详情ID,为空时获取顶级 + in: query + name: parentID + type: integer + - description: 字典ID + in: query + name: sysDictionaryID + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 获取字典详情列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.SysDictionaryDetail' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 根据父级ID获取字典详情 + tags: + - SysDictionaryDetail + /sysDictionaryDetail/getDictionaryPath: + get: + consumes: + - application/json + parameters: + - description: 字典详情ID + in: query + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 获取字典详情路径 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.SysDictionaryDetail' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取字典详情的完整路径 + tags: + - SysDictionaryDetail + /sysDictionaryDetail/getDictionaryTreeList: + get: + consumes: + - application/json + parameters: + - description: 字典ID + in: query + name: sysDictionaryID + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 获取字典详情树形结构 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.SysDictionaryDetail' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取字典详情树形结构 + tags: + - SysDictionaryDetail + /sysDictionaryDetail/getDictionaryTreeListByType: + get: + consumes: + - application/json + parameters: + - description: 字典类型 + in: query + name: type + required: true + type: string + produces: + - application/json + responses: + "200": + description: 获取字典详情树形结构 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.SysDictionaryDetail' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 根据字典类型获取字典详情树形结构 + tags: + - SysDictionaryDetail /sysDictionaryDetail/getSysDictionaryDetailList: get: consumes: @@ -4438,6 +3687,10 @@ paths: in: query name: createdAt type: string + - description: 禁用状态,根据status字段动态计算 + in: query + name: disabled + type: boolean - description: 扩展值 in: query name: extend @@ -4450,6 +3703,10 @@ paths: in: query name: label type: string + - description: 层级深度,用于查询指定层级的数据 + in: query + name: level + type: integer - description: 页码 in: query name: page @@ -4458,6 +3715,14 @@ paths: in: query name: pageSize type: integer + - description: 父级字典详情ID,用于查询指定父级下的子项 + in: query + name: parentID + type: integer + - description: 层级路径,如 "1,2,3" + in: query + name: path + type: string - description: 排序标记 in: query name: sort @@ -4525,278 +3790,22 @@ paths: summary: 更新SysDictionaryDetail tags: - SysDictionaryDetail - /sysExportTemplate/ExportTemplate: - get: - consumes: - - application/json - produces: - - application/json - responses: {} - security: - - ApiKeyAuth: [] - summary: 导出表格模板 - tags: - - SysExportTemplate - /sysExportTemplate/createSysExportTemplate: + /sysError/createSysError: post: consumes: - application/json parameters: - - description: 创建导出模板 + - description: 创建错误日志 in: body name: data required: true schema: - $ref: '#/definitions/system.SysExportTemplate' + $ref: '#/definitions/system.SysError' produces: - application/json responses: "200": - description: '{"success":true,"data":{},"msg":"创建成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 创建导出模板 - tags: - - SysExportTemplate - /sysExportTemplate/deleteSysExportTemplate: - delete: - consumes: - - application/json - parameters: - - description: 删除导出模板 - in: body - name: data - required: true - schema: - $ref: '#/definitions/system.SysExportTemplate' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"删除成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 删除导出模板 - tags: - - SysExportTemplate - /sysExportTemplate/deleteSysExportTemplateByIds: - delete: - consumes: - - application/json - parameters: - - description: 批量删除导出模板 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.IdsReq' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"批量删除成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 批量删除导出模板 - tags: - - SysExportTemplate - /sysExportTemplate/exportExcel: - get: - consumes: - - application/json - produces: - - application/json - responses: {} - security: - - ApiKeyAuth: [] - summary: 导出表格 - tags: - - SysExportTemplate - /sysExportTemplate/findSysExportTemplate: - get: - consumes: - - application/json - parameters: - - description: 主键ID - in: query - name: ID - type: integer - - description: 创建时间 - in: query - name: createdAt - type: string - - description: 数据库名称 - in: query - name: dbName - type: string - - in: query - name: limit - type: integer - - description: 模板名称 - in: query - name: name - type: string - - in: query - name: order - type: string - - description: 表名称 - in: query - name: tableName - type: string - - description: 模板标识 - in: query - name: templateID - type: string - - description: 模板信息 - in: query - name: templateInfo - type: string - - description: 更新时间 - in: query - name: updatedAt - type: string - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"查询成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 用id查询导出模板 - tags: - - SysExportTemplate - /sysExportTemplate/getSysExportTemplateList: - get: - consumes: - - application/json - parameters: - - description: 主键ID - in: query - name: ID - type: integer - - description: 创建时间 - in: query - name: createdAt - type: string - - description: 数据库名称 - in: query - name: dbName - type: string - - in: query - name: endCreatedAt - type: string - - description: 关键字 - in: query - name: keyword - type: string - - in: query - name: limit - type: integer - - description: 模板名称 - in: query - name: name - type: string - - in: query - name: order - type: string - - description: 页码 - in: query - name: page - type: integer - - description: 每页大小 - in: query - name: pageSize - type: integer - - in: query - name: startCreatedAt - type: string - - description: 表名称 - in: query - name: tableName - type: string - - description: 模板标识 - in: query - name: templateID - type: string - - description: 模板信息 - in: query - name: templateInfo - type: string - - description: 更新时间 - in: query - name: updatedAt - type: string - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"获取成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 分页获取导出模板列表 - tags: - - SysExportTemplate - /sysExportTemplate/importExcel: - post: - consumes: - - application/json - produces: - - application/json - responses: {} - security: - - ApiKeyAuth: [] - summary: 导入表格 - tags: - - SysImportTemplate - /sysExportTemplate/updateSysExportTemplate: - put: - consumes: - - application/json - parameters: - - description: 更新导出模板 - in: body - name: data - required: true - schema: - $ref: '#/definitions/system.SysExportTemplate' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"更新成功"}' - schema: - type: string - security: - - ApiKeyAuth: [] - summary: 更新导出模板 - tags: - - SysExportTemplate - /sysOperationRecord/createSysOperationRecord: - post: - consumes: - - application/json - parameters: - - description: 创建SysOperationRecord - in: body - name: data - required: true - schema: - $ref: '#/definitions/system.SysOperationRecord' - produces: - - application/json - responses: - "200": - description: 创建SysOperationRecord + description: 创建成功 schema: allOf: - $ref: '#/definitions/response.Response' @@ -4806,9 +3815,190 @@ paths: type: object security: - ApiKeyAuth: [] - summary: 创建SysOperationRecord + summary: 创建错误日志 tags: - - SysOperationRecord + - SysError + /sysError/deleteSysError: + delete: + consumes: + - application/json + parameters: + - description: 删除错误日志 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysError' + produces: + - application/json + responses: + "200": + description: 删除成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除错误日志 + tags: + - SysError + /sysError/deleteSysErrorByIds: + 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: + - SysError + /sysError/findSysError: + get: + consumes: + - application/json + parameters: + - description: 用id查询错误日志 + in: query + name: ID + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 查询成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/system.SysError' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用id查询错误日志 + tags: + - SysError + /sysError/getSysErrorList: + get: + consumes: + - application/json + parameters: + - collectionFormat: csv + in: query + items: + type: string + name: createdAtRange + type: array + - in: query + name: form + type: string + - in: query + name: info + type: string + - description: 关键字 + in: query + name: keyword + type: string + - description: 页码 + in: query + name: page + type: integer + - description: 每页大小 + in: query + name: pageSize + type: integer + 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: + - SysError + /sysError/getSysErrorSolution: + get: + consumes: + - application/json + parameters: + - description: 错误日志ID + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 处理已提交 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 根据ID触发处理:标记为处理中,1分钟后自动改为处理完成 + tags: + - SysError + /sysError/updateSysError: + put: + consumes: + - application/json + parameters: + - description: 更新错误日志 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.SysError' + produces: + - application/json + responses: + "200": + description: 更新成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新错误日志 + tags: + - SysError /sysOperationRecord/deleteSysOperationRecord: delete: consumes: @@ -5316,7 +4506,7 @@ paths: - application/json responses: "200": - description: 重启系统 + description: 重载系统 schema: allOf: - $ref: '#/definitions/response.Response' @@ -5326,7 +4516,7 @@ paths: type: object security: - ApiKeyAuth: [] - summary: 重启系统 + summary: 重载系统 tags: - System /system/setSystemConfig: diff --git a/server/global/version.go b/server/global/version.go index 2c61af2..093f9d1 100644 --- a/server/global/version.go +++ b/server/global/version.go @@ -6,7 +6,7 @@ const ( // Version 当前版本号 Version = "v2.9.1" // AppName 应用名称 - AppName = "Gin-Vue-Admin" + AppName = "Gin-React-Admin" // Description 应用描述 - Description = "使用gin+vue进行极速开发的全栈开发基础平台" + Description = "使用gin+react进行极速开发的全栈开发基础平台" ) diff --git a/server/initialize/ensure_tables.go b/server/initialize/ensure_tables.go index cfc8bde..4ee67d1 100644 --- a/server/initialize/ensure_tables.go +++ b/server/initialize/ensure_tables.go @@ -2,6 +2,7 @@ package initialize import ( "context" + commonModel "git.echol.cn/loser/Go-Web-Template/server/model/common" sysModel "git.echol.cn/loser/Go-Web-Template/server/model/system" "git.echol.cn/loser/Go-Web-Template/server/service/system" @@ -46,11 +47,7 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error sysModel.SysBaseMenuParameter{}, sysModel.SysBaseMenuBtn{}, sysModel.SysAuthorityBtn{}, - sysModel.SysExportTemplate{}, - sysModel.Condition{}, - sysModel.JoinTemplate{}, sysModel.SysParams{}, - sysModel.SysVersion{}, sysModel.SysError{}, sysModel.SysLoginLog{}, sysModel.SysApiToken{}, @@ -86,9 +83,6 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool { sysModel.SysBaseMenuParameter{}, sysModel.SysBaseMenuBtn{}, sysModel.SysAuthorityBtn{}, - sysModel.SysExportTemplate{}, - sysModel.Condition{}, - sysModel.JoinTemplate{}, adapter.CasbinRule{}, diff --git a/server/initialize/gorm.go b/server/initialize/gorm.go index ba76f03..91c73cf 100644 --- a/server/initialize/gorm.go +++ b/server/initialize/gorm.go @@ -55,11 +55,7 @@ func RegisterTables() { system.SysBaseMenuParameter{}, system.SysBaseMenuBtn{}, system.SysAuthorityBtn{}, - system.SysExportTemplate{}, - system.Condition{}, - system.JoinTemplate{}, system.SysParams{}, - system.SysVersion{}, system.SysError{}, system.SysApiToken{}, system.SysLoginLog{}, diff --git a/server/initialize/router.go b/server/initialize/router.go index 7e66477..24bf1d5 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -79,26 +79,24 @@ 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.InitSysVersionRouter(PrivateGroup) // 发版相关路由 - systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由 - systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由 - systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理 - systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录 - systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理 - systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理 - systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板 - systemRouter.InitMcpRouter(PrivateGroup) // MCP 管理 - systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理 - systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志 - systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志 - systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发 - commonRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由 - commonRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类 + systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由 + systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由 + systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由 + systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由 + systemRouter.InitSystemRouter(PrivateGroup) // system相关路由 + systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由 + systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由 + systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理 + systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录 + systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理 + systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理 + systemRouter.InitMcpRouter(PrivateGroup) // MCP 管理 + systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理 + systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志 + systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志 + systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发 + commonRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由 + commonRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类 } // 注册业务路由 diff --git a/server/main.go b/server/main.go index b0aa241..3ede3bd 100644 --- a/server/main.go +++ b/server/main.go @@ -20,9 +20,9 @@ import ( // @Tag.Name SysUser // @Tag.Description 用户 -// @title Gin-Vue-Admin Swagger API接口文档 +// @title Gin-React-Admin Swagger API接口文档 // @version v2.9.1 -// @description 使用gin+vue进行极速开发的全栈开发基础平台 +// @description 使用gin+react进行极速开发的全栈开发基础平台 // @securityDefinitions.apikey ApiKeyAuth // @in header // @name x-token diff --git a/server/mcp/client/client_test.go b/server/mcp/client/client_test.go index 07ce70b..580de1e 100644 --- a/server/mcp/client/client_test.go +++ b/server/mcp/client/client_test.go @@ -18,7 +18,7 @@ func requireMCPIntegration(t *testing.T) { // 测试 MCP 客户端连接 func TestMcpClientConnection(t *testing.T) { requireMCPIntegration(t) - c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务") + c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务") defer c.Close() if err != nil { t.Fatalf("create client failed: %v", err) @@ -28,7 +28,7 @@ func TestMcpClientConnection(t *testing.T) { func TestTools(t *testing.T) { requireMCPIntegration(t) t.Run("currentTime", func(t *testing.T) { - c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务") + c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务") defer c.Close() if err != nil { t.Fatalf("Failed to create client: %v", err) @@ -58,7 +58,7 @@ func TestTools(t *testing.T) { t.Run("getNickname", func(t *testing.T) { - c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务") + c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务") defer c.Close() if err != nil { t.Fatalf("Failed to create client: %v", err) @@ -102,7 +102,7 @@ func TestTools(t *testing.T) { func TestGetTools(t *testing.T) { requireMCPIntegration(t) - c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务") + c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务") defer c.Close() if err != nil { t.Fatalf("Failed to create client: %v", err) diff --git a/server/mcp/menu_creator.go b/server/mcp/menu_creator.go index 55171d2..8b6679c 100644 --- a/server/mcp/menu_creator.go +++ b/server/mcp/menu_creator.go @@ -69,14 +69,14 @@ func (m *MenuCreator) New() mcp.Tool { ), mcp.WithString("name", mcp.Required(), - mcp.Description("路由name,用于Vue Router,如:userList"), + mcp.Description("路由name,用于前端路由标识,如:userList"), ), mcp.WithBoolean("hidden", mcp.Description("是否在菜单列表中隐藏"), ), mcp.WithString("component", mcp.Required(), - mcp.Description("对应的前端Vue组件路径,如:view/user/list.vue"), + mcp.Description("对应的前端React组件路径,如:features/users/UserManagementPage"), ), mcp.WithNumber("sort", mcp.Description("菜单排序号,数字越小越靠前"), diff --git a/server/model/system/request/sys_export_template.go b/server/model/system/request/sys_export_template.go deleted file mode 100644 index 69d895d..0000000 --- a/server/model/system/request/sys_export_template.go +++ /dev/null @@ -1,14 +0,0 @@ -package request - -import ( - "git.echol.cn/loser/Go-Web-Template/server/model/common/request" - "git.echol.cn/loser/Go-Web-Template/server/model/system" - "time" -) - -type SysExportTemplateSearch struct { - system.SysExportTemplate - StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"` - EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"` - request.PageInfo -} diff --git a/server/model/system/request/sys_menu.go b/server/model/system/request/sys_menu.go index b60ab38..7f6f06f 100644 --- a/server/model/system/request/sys_menu.go +++ b/server/model/system/request/sys_menu.go @@ -23,7 +23,7 @@ func DefaultMenu() []system.SysBaseMenu { ParentId: 0, Path: "dashboard", Name: "dashboard", - Component: "view/dashboard/index.vue", + Component: "features/dashboard/DashboardPage", Sort: 1, Meta: system.Meta{ Title: "仪表盘", diff --git a/server/model/system/request/sys_version.go b/server/model/system/request/sys_version.go deleted file mode 100644 index 3994357..0000000 --- a/server/model/system/request/sys_version.go +++ /dev/null @@ -1,40 +0,0 @@ -package request - -import ( - "git.echol.cn/loser/Go-Web-Template/server/model/common/request" - "git.echol.cn/loser/Go-Web-Template/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"` // 导出时间 -} diff --git a/server/model/system/response/sys_version.go b/server/model/system/response/sys_version.go deleted file mode 100644 index 29cb9ba..0000000 --- a/server/model/system/response/sys_version.go +++ /dev/null @@ -1,14 +0,0 @@ -package response - -import ( - "git.echol.cn/loser/Go-Web-Template/server/model/system" - "git.echol.cn/loser/Go-Web-Template/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 -} diff --git a/server/model/system/sys_export_template.go b/server/model/system/sys_export_template.go deleted file mode 100644 index ca80b4f..0000000 --- a/server/model/system/sys_export_template.go +++ /dev/null @@ -1,46 +0,0 @@ -// 自动生成模板SysExportTemplate -package system - -import ( - "git.echol.cn/loser/Go-Web-Template/server/global" -) - -// 导出模板 结构体 SysExportTemplate -type SysExportTemplate struct { - global.GVA_MODEL - DBName string `json:"dbName" form:"dbName" gorm:"column:db_name;comment:数据库名称;"` //数据库名称 - Name string `json:"name" form:"name" gorm:"column:name;comment:模板名称;"` //模板名称 - TableName string `json:"tableName" form:"tableName" gorm:"column:table_name;comment:表名称;"` //表名称 - TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识;"` //模板标识 - TemplateInfo string `json:"templateInfo" form:"templateInfo" gorm:"column:template_info;type:text;"` //模板信息 - SQL string `json:"sql" form:"sql" gorm:"column:sql;type:text;comment:自定义导出SQL;"` //自定义导出SQL - ImportSQL string `json:"importSql" form:"importSql" gorm:"column:import_sql;type:text;comment:自定义导入SQL;"` //自定义导入SQL - Limit *int `json:"limit" form:"limit" gorm:"column:limit;comment:导出限制"` - Order string `json:"order" form:"order" gorm:"column:order;comment:排序"` - Conditions []Condition `json:"conditions" form:"conditions" gorm:"foreignKey:TemplateID;references:TemplateID;comment:条件"` - JoinTemplate []JoinTemplate `json:"joinTemplate" form:"joinTemplate" gorm:"foreignKey:TemplateID;references:TemplateID;comment:关联"` -} - -type JoinTemplate struct { - global.GVA_MODEL - TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"` - JOINS string `json:"joins" form:"joins" gorm:"column:joins;comment:关联"` - Table string `json:"table" form:"table" gorm:"column:table;comment:关联表"` - ON string `json:"on" form:"on" gorm:"column:on;comment:关联条件"` -} - -func (JoinTemplate) TableName() string { - return "sys_export_template_join" -} - -type Condition struct { - global.GVA_MODEL - TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"` - From string `json:"from" form:"from" gorm:"column:from;comment:条件取的key"` - Column string `json:"column" form:"column" gorm:"column:column;comment:作为查询条件的字段"` - Operator string `json:"operator" form:"operator" gorm:"column:operator;comment:操作符"` -} - -func (Condition) TableName() string { - return "sys_export_template_condition" -} diff --git a/server/model/system/sys_version.go b/server/model/system/sys_version.go deleted file mode 100644 index cb44e22..0000000 --- a/server/model/system/sys_version.go +++ /dev/null @@ -1,20 +0,0 @@ -// 自动生成模板SysVersion -package system - -import ( - "git.echol.cn/loser/Go-Web-Template/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:text;"` //版本数据 -} - -// TableName 版本管理 SysVersion自定义表名 sys_versions -func (SysVersion) TableName() string { - return "sys_versions" -} diff --git a/server/router/system/enter.go b/server/router/system/enter.go index 4d28b0a..05798f6 100644 --- a/server/router/system/enter.go +++ b/server/router/system/enter.go @@ -16,10 +16,8 @@ type RouterGroup struct { OperationRecordRouter DictionaryDetailRouter AuthorityBtnRouter - SysExportTemplateRouter McpRouter SysParamsRouter - SysVersionRouter SysErrorRouter LoginLogRouter ApiTokenRouter @@ -39,8 +37,6 @@ var ( authorityMenuApi = api.ApiGroupApp.SystemApiGroup.AuthorityMenuApi operationRecordApi = api.ApiGroupApp.SystemApiGroup.OperationRecordApi dictionaryDetailApi = api.ApiGroupApp.SystemApiGroup.DictionaryDetailApi - exportTemplateApi = api.ApiGroupApp.SystemApiGroup.SysExportTemplateApi mcpApi = api.ApiGroupApp.SystemApiGroup.McpApi - sysVersionApi = api.ApiGroupApp.SystemApiGroup.SysVersionApi sysErrorApi = api.ApiGroupApp.SystemApiGroup.SysErrorApi ) diff --git a/server/router/system/sys_export_template.go b/server/router/system/sys_export_template.go deleted file mode 100644 index 3cd55a5..0000000 --- a/server/router/system/sys_export_template.go +++ /dev/null @@ -1,35 +0,0 @@ -package system - -import ( - "git.echol.cn/loser/Go-Web-Template/server/middleware" - "github.com/gin-gonic/gin" -) - -type SysExportTemplateRouter struct { -} - -// InitSysExportTemplateRouter 初始化 导出模板 路由信息 -func (s *SysExportTemplateRouter) InitSysExportTemplateRouter(Router *gin.RouterGroup, pubRouter *gin.RouterGroup) { - sysExportTemplateRouter := Router.Group("sysExportTemplate").Use(middleware.OperationRecord()) - sysExportTemplateRouterWithoutRecord := Router.Group("sysExportTemplate") - sysExportTemplateRouterWithoutAuth := pubRouter.Group("sysExportTemplate") - - { - sysExportTemplateRouter.POST("createSysExportTemplate", exportTemplateApi.CreateSysExportTemplate) // 新建导出模板 - sysExportTemplateRouter.DELETE("deleteSysExportTemplate", exportTemplateApi.DeleteSysExportTemplate) // 删除导出模板 - sysExportTemplateRouter.DELETE("deleteSysExportTemplateByIds", exportTemplateApi.DeleteSysExportTemplateByIds) // 批量删除导出模板 - sysExportTemplateRouter.PUT("updateSysExportTemplate", exportTemplateApi.UpdateSysExportTemplate) // 更新导出模板 - sysExportTemplateRouter.POST("importExcel", exportTemplateApi.ImportExcel) // 导入excel模板数据 - } - { - sysExportTemplateRouterWithoutRecord.GET("findSysExportTemplate", exportTemplateApi.FindSysExportTemplate) // 根据ID获取导出模板 - sysExportTemplateRouterWithoutRecord.GET("getSysExportTemplateList", exportTemplateApi.GetSysExportTemplateList) // 获取导出模板列表 - sysExportTemplateRouterWithoutRecord.GET("exportExcel", exportTemplateApi.ExportExcel) // 获取导出token - sysExportTemplateRouterWithoutRecord.GET("exportTemplate", exportTemplateApi.ExportTemplate) // 导出表格模板 - sysExportTemplateRouterWithoutRecord.GET("previewSQL", exportTemplateApi.PreviewSQL) // 预览SQL - } - { - sysExportTemplateRouterWithoutAuth.GET("exportExcelByToken", exportTemplateApi.ExportExcelByToken) // 通过token导出表格 - sysExportTemplateRouterWithoutAuth.GET("exportTemplateByToken", exportTemplateApi.ExportTemplateByToken) // 通过token导出模板 - } -} diff --git a/server/router/system/sys_version.go b/server/router/system/sys_version.go deleted file mode 100644 index 8277c80..0000000 --- a/server/router/system/sys_version.go +++ /dev/null @@ -1,25 +0,0 @@ -package system - -import ( - "git.echol.cn/loser/Go-Web-Template/server/middleware" - "github.com/gin-gonic/gin" -) - -type SysVersionRouter struct{} - -// InitSysVersionRouter 初始化 版本管理 路由信息 -func (s *SysVersionRouter) InitSysVersionRouter(Router *gin.RouterGroup) { - sysVersionRouter := Router.Group("sysVersion").Use(middleware.OperationRecord()) - sysVersionRouterWithoutRecord := Router.Group("sysVersion") - { - sysVersionRouter.DELETE("deleteSysVersion", sysVersionApi.DeleteSysVersion) // 删除版本管理 - sysVersionRouter.DELETE("deleteSysVersionByIds", sysVersionApi.DeleteSysVersionByIds) // 批量删除版本管理 - sysVersionRouter.POST("exportVersion", sysVersionApi.ExportVersion) // 导出版本数据 - sysVersionRouter.POST("importVersion", sysVersionApi.ImportVersion) // 导入版本数据 - } - { - sysVersionRouterWithoutRecord.GET("findSysVersion", sysVersionApi.FindSysVersion) // 根据ID获取版本管理 - sysVersionRouterWithoutRecord.GET("getSysVersionList", sysVersionApi.GetSysVersionList) // 获取版本管理列表 - sysVersionRouterWithoutRecord.GET("downloadVersionJson", sysVersionApi.DownloadVersionJson) // 下载版本JSON数据 - } -} diff --git a/server/service/system/enter.go b/server/service/system/enter.go index 91a6935..240d22e 100644 --- a/server/service/system/enter.go +++ b/server/service/system/enter.go @@ -14,9 +14,7 @@ type ServiceGroup struct { OperationRecordService DictionaryDetailService AuthorityBtnService - SysExportTemplateService SysParamsService - SysVersionService McpService SysErrorService LoginLogService diff --git a/server/service/system/sys_export_template.go b/server/service/system/sys_export_template.go deleted file mode 100644 index 24639ff..0000000 --- a/server/service/system/sys_export_template.go +++ /dev/null @@ -1,724 +0,0 @@ -package system - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "mime/multipart" - "net/url" - "strconv" - "strings" - "time" - - "git.echol.cn/loser/Go-Web-Template/server/global" - "git.echol.cn/loser/Go-Web-Template/server/model/common/request" - "git.echol.cn/loser/Go-Web-Template/server/model/system" - systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request" - "git.echol.cn/loser/Go-Web-Template/server/utils" - "github.com/xuri/excelize/v2" - "gorm.io/gorm" -) - -type SysExportTemplateService struct { -} - -var SysExportTemplateServiceApp = new(SysExportTemplateService) - -// CreateSysExportTemplate 创建导出模板记录 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) CreateSysExportTemplate(sysExportTemplate *system.SysExportTemplate) (err error) { - err = global.GVA_DB.Create(sysExportTemplate).Error - return err -} - -// DeleteSysExportTemplate 删除导出模板记录 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) { - err = global.GVA_DB.Delete(&sysExportTemplate).Error - return err -} - -// DeleteSysExportTemplateByIds 批量删除导出模板记录 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplateByIds(ids request.IdsReq) (err error) { - err = global.GVA_DB.Delete(&[]system.SysExportTemplate{}, "id in ?", ids.Ids).Error - return err -} - -// UpdateSysExportTemplate 更新导出模板记录 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) { - return global.GVA_DB.Transaction(func(tx *gorm.DB) error { - conditions := sysExportTemplate.Conditions - e := tx.Delete(&[]system.Condition{}, "template_id = ?", sysExportTemplate.TemplateID).Error - if e != nil { - return e - } - sysExportTemplate.Conditions = nil - - joins := sysExportTemplate.JoinTemplate - e = tx.Delete(&[]system.JoinTemplate{}, "template_id = ?", sysExportTemplate.TemplateID).Error - if e != nil { - return e - } - sysExportTemplate.JoinTemplate = nil - - e = tx.Updates(&sysExportTemplate).Error - if e != nil { - return e - } - if len(conditions) > 0 { - for i := range conditions { - conditions[i].ID = 0 - } - e = tx.Create(&conditions).Error - } - if len(joins) > 0 { - for i := range joins { - joins[i].ID = 0 - } - e = tx.Create(&joins).Error - } - return e - }) -} - -// GetSysExportTemplate 根据id获取导出模板记录 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplate(id uint) (sysExportTemplate system.SysExportTemplate, err error) { - err = global.GVA_DB.Where("id = ?", id).Preload("JoinTemplate").Preload("Conditions").First(&sysExportTemplate).Error - return -} - -// GetSysExportTemplateInfoList 分页获取导出模板记录 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateInfoList(info systemReq.SysExportTemplateSearch) (list []system.SysExportTemplate, total int64, err error) { - limit := info.PageSize - offset := info.PageSize * (info.Page - 1) - // 创建db - db := global.GVA_DB.Model(&system.SysExportTemplate{}) - var sysExportTemplates []system.SysExportTemplate - // 如果有条件搜索 下方会自动创建搜索语句 - if info.StartCreatedAt != nil && info.EndCreatedAt != nil { - db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) - } - if info.Name != "" { - db = db.Where("name LIKE ?", "%"+info.Name+"%") - } - if info.TableName != "" { - db = db.Where("table_name = ?", info.TableName) - } - if info.TemplateID != "" { - db = db.Where("template_id = ?", info.TemplateID) - } - err = db.Count(&total).Error - if err != nil { - return - } - - if limit != 0 { - db = db.Limit(limit).Offset(offset) - } - - err = db.Find(&sysExportTemplates).Error - return sysExportTemplates, total, err -} - -// ExportExcel 导出Excel -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID string, values url.Values) (file *bytes.Buffer, name string, err error) { - var params = values.Get("params") - paramsValues, err := url.ParseQuery(params) - if err != nil { - return nil, "", fmt.Errorf("解析 params 参数失败: %v", err) - } - var template system.SysExportTemplate - err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error - if err != nil { - return nil, "", err - } - f := excelize.NewFile() - defer func() { - if err := f.Close(); err != nil { - fmt.Println(err) - } - }() - // Create a new sheet. - index, err := f.NewSheet("Sheet1") - if err != nil { - fmt.Println(err) - return - } - var templateInfoMap = make(map[string]string) - columns, err := utils.GetJSONKeys(template.TemplateInfo) - if err != nil { - return nil, "", err - } - err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) - if err != nil { - return nil, "", err - } - var tableTitle []string - var selectKeyFmt []string - for _, key := range columns { - selectKeyFmt = append(selectKeyFmt, key) - tableTitle = append(tableTitle, templateInfoMap[key]) - } - - selects := strings.Join(selectKeyFmt, ", ") - var tableMap []map[string]interface{} - db := global.GVA_DB - if template.DBName != "" { - db = global.MustGetGlobalDBByDBName(template.DBName) - } - - // 如果有自定义SQL,则优先使用自定义SQL - if template.SQL != "" { - // 将 url.Values 转换为 map[string]interface{} 以支持 GORM 的命名参数 - sqlParams := make(map[string]interface{}) - for k, v := range paramsValues { - if len(v) > 0 { - sqlParams[k] = v[0] - } - } - - // 执行原生 SQL,支持 @key 命名参数 - err = db.Raw(template.SQL, sqlParams).Scan(&tableMap).Error - if err != nil { - return nil, "", err - } - } else { - if len(template.JoinTemplate) > 0 { - for _, join := range template.JoinTemplate { - db = db.Joins(join.JOINS + " " + join.Table + " ON " + join.ON) - } - } - - db = db.Select(selects).Table(template.TableName) - - filterDeleted := false - - filterParam := paramsValues.Get("filterDeleted") - if filterParam == "true" { - filterDeleted = true - } - - if filterDeleted { - // 自动过滤主表的软删除 - db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", template.TableName)) - - // 过滤关联表的软删除(如果有) - if len(template.JoinTemplate) > 0 { - for _, join := range template.JoinTemplate { - // 检查关联表是否有deleted_at字段 - hasDeletedAt := sysExportTemplateService.hasDeletedAtColumn(join.Table) - if hasDeletedAt { - db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", join.Table)) - } - } - } - } - - if len(template.Conditions) > 0 { - for _, condition := range template.Conditions { - sql := fmt.Sprintf("%s %s ?", condition.Column, condition.Operator) - value := paramsValues.Get(condition.From) - - if condition.Operator == "IN" || condition.Operator == "NOT IN" { - sql = fmt.Sprintf("%s %s (?)", condition.Column, condition.Operator) - } - - if condition.Operator == "BETWEEN" { - sql = fmt.Sprintf("%s BETWEEN ? AND ?", condition.Column) - startValue := paramsValues.Get("start" + condition.From) - endValue := paramsValues.Get("end" + condition.From) - if startValue != "" && endValue != "" { - db = db.Where(sql, startValue, endValue) - } - continue - } - - if value != "" { - if condition.Operator == "LIKE" { - value = "%" + value + "%" - } - db = db.Where(sql, value) - } - } - } - // 通过参数传入limit - limit := paramsValues.Get("limit") - if limit != "" { - l, e := strconv.Atoi(limit) - if e == nil { - db = db.Limit(l) - } - } - // 模板的默认limit - if limit == "" && template.Limit != nil && *template.Limit != 0 { - db = db.Limit(*template.Limit) - } - - // 通过参数传入offset - offset := paramsValues.Get("offset") - if offset != "" { - o, e := strconv.Atoi(offset) - if e == nil { - db = db.Offset(o) - } - } - - // 获取当前表的所有字段 - table := template.TableName - orderColumns, err := db.Migrator().ColumnTypes(table) - if err != nil { - return nil, "", err - } - - // 创建一个 map 来存储字段名 - fields := make(map[string]bool) - - for _, column := range orderColumns { - fields[column.Name()] = true - } - - // 通过参数传入order - order := paramsValues.Get("order") - - if order == "" && template.Order != "" { - // 如果没有order入参,这里会使用模板的默认排序 - order = template.Order - } - - if order != "" { - checkOrderArr := strings.Split(order, " ") - orderStr := "" - // 检查请求的排序字段是否在字段列表中 - if _, ok := fields[checkOrderArr[0]]; !ok { - return nil, "", fmt.Errorf("order by %s is not in the fields", order) - } - orderStr = checkOrderArr[0] - if len(checkOrderArr) > 1 { - if checkOrderArr[1] != "asc" && checkOrderArr[1] != "desc" { - return nil, "", fmt.Errorf("order by %s is not secure", order) - } - orderStr = orderStr + " " + checkOrderArr[1] - } - db = db.Order(orderStr) - } - - err = db.Debug().Find(&tableMap).Error - if err != nil { - return nil, "", err - } - } - - var rows [][]string - rows = append(rows, tableTitle) - for _, exTable := range tableMap { - var row []string - for _, column := range columns { - column = strings.ReplaceAll(column, "\"", "") - column = strings.ReplaceAll(column, "`", "") - if len(template.JoinTemplate) > 0 { - columnAs := strings.Split(column, " as ") - if len(columnAs) > 1 { - column = strings.TrimSpace(strings.Split(column, " as ")[1]) - } else { - columnArr := strings.Split(column, ".") - if len(columnArr) > 1 { - column = strings.Split(column, ".")[1] - } - } - } - // 需要对时间类型特殊处理 - if t, ok := exTable[column].(time.Time); ok { - row = append(row, t.Format("2006-01-02 15:04:05")) - } else { - row = append(row, fmt.Sprintf("%v", exTable[column])) - } - } - rows = append(rows, row) - } - for i, row := range rows { - for j, colCell := range row { - cell := fmt.Sprintf("%s%d", getColumnName(j+1), i+1) - - var sErr error - if v, err := strconv.ParseFloat(colCell, 64); err == nil { - sErr = f.SetCellValue("Sheet1", cell, v) - } else if v, err := strconv.ParseInt(colCell, 10, 64); err == nil { - sErr = f.SetCellValue("Sheet1", cell, v) - } else { - sErr = f.SetCellValue("Sheet1", cell, colCell) - } - - if sErr != nil { - return nil, "", sErr - } - } - } - f.SetActiveSheet(index) - file, err = f.WriteToBuffer() - if err != nil { - return nil, "", err - } - - return file, template.Name, nil -} - -// PreviewSQL 预览最终生成的 SQL(不执行查询,仅返回 SQL 字符串) -// Author [piexlmax](https://github.com/piexlmax) & [trae-ai] -func (sysExportTemplateService *SysExportTemplateService) PreviewSQL(templateID string, values url.Values) (sqlPreview string, err error) { - // 解析 params(与导出逻辑保持一致) - var params = values.Get("params") - paramsValues, _ := url.ParseQuery(params) - - // 加载模板 - var template system.SysExportTemplate - err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error - if err != nil { - return "", err - } - - // 解析模板列 - var templateInfoMap = make(map[string]string) - columns, err := utils.GetJSONKeys(template.TemplateInfo) - if err != nil { - return "", err - } - err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) - if err != nil { - return "", err - } - var selectKeyFmt []string - for _, key := range columns { - selectKeyFmt = append(selectKeyFmt, key) - } - selects := strings.Join(selectKeyFmt, ", ") - - // 生成 FROM 与 JOIN 片段 - var sb strings.Builder - sb.WriteString("SELECT ") - sb.WriteString(selects) - sb.WriteString(" FROM ") - sb.WriteString(template.TableName) - - if len(template.JoinTemplate) > 0 { - for _, join := range template.JoinTemplate { - sb.WriteString(" ") - sb.WriteString(join.JOINS) - sb.WriteString(" ") - sb.WriteString(join.Table) - sb.WriteString(" ON ") - sb.WriteString(join.ON) - } - } - - // WHERE 条件 - var wheres []string - - // 软删除过滤 - filterDeleted := false - if paramsValues != nil { - filterParam := paramsValues.Get("filterDeleted") - if filterParam == "true" { - filterDeleted = true - } - } - if filterDeleted { - wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", template.TableName)) - if len(template.JoinTemplate) > 0 { - for _, join := range template.JoinTemplate { - if sysExportTemplateService.hasDeletedAtColumn(join.Table) { - wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", join.Table)) - } - } - } - } - - // 模板条件(保留与 ExportExcel 同步的解析规则) - if len(template.Conditions) > 0 { - for _, condition := range template.Conditions { - op := strings.ToUpper(strings.TrimSpace(condition.Operator)) - col := strings.TrimSpace(condition.Column) - - // 预览优先展示传入值,没有则展示占位符 - val := "" - if paramsValues != nil { - val = paramsValues.Get(condition.From) - } - - switch op { - case "BETWEEN": - startValue := "" - endValue := "" - if paramsValues != nil { - startValue = paramsValues.Get("start" + condition.From) - endValue = paramsValues.Get("end" + condition.From) - } - if startValue != "" && endValue != "" { - wheres = append(wheres, fmt.Sprintf("%s BETWEEN '%s' AND '%s'", col, startValue, endValue)) - } else { - wheres = append(wheres, fmt.Sprintf("%s BETWEEN {start%s} AND {end%s}", col, condition.From, condition.From)) - } - case "IN", "NOT IN": - if val != "" { - // 逗号分隔值做简单展示 - parts := strings.Split(val, ",") - for i := range parts { - parts[i] = strings.TrimSpace(parts[i]) - } - wheres = append(wheres, fmt.Sprintf("%s %s ('%s')", col, op, strings.Join(parts, "','"))) - } else { - wheres = append(wheres, fmt.Sprintf("%s %s ({%s})", col, op, condition.From)) - } - case "LIKE": - if val != "" { - wheres = append(wheres, fmt.Sprintf("%s LIKE '%%%s%%'", col, val)) - } else { - wheres = append(wheres, fmt.Sprintf("%s LIKE {%%%s%%}", col, condition.From)) - } - default: - if val != "" { - wheres = append(wheres, fmt.Sprintf("%s %s '%s'", col, op, val)) - } else { - wheres = append(wheres, fmt.Sprintf("%s %s {%s}", col, op, condition.From)) - } - } - } - } - - if len(wheres) > 0 { - sb.WriteString(" WHERE ") - sb.WriteString(strings.Join(wheres, " AND ")) - } - - // 排序 - order := "" - if paramsValues != nil { - order = paramsValues.Get("order") - } - if order == "" && template.Order != "" { - order = template.Order - } - if order != "" { - sb.WriteString(" ORDER BY ") - sb.WriteString(order) - } - - // limit/offset(如果传入或默认值为0,则不生成) - limitStr := "" - offsetStr := "" - if paramsValues != nil { - limitStr = paramsValues.Get("limit") - offsetStr = paramsValues.Get("offset") - } - - // 处理模板默认limit(仅当非0时) - if limitStr == "" && template.Limit != nil && *template.Limit != 0 { - limitStr = strconv.Itoa(*template.Limit) - } - - // 解析为数值,用于判断是否生成 - limitInt := 0 - offsetInt := 0 - if limitStr != "" { - if v, e := strconv.Atoi(limitStr); e == nil { - limitInt = v - } - } - if offsetStr != "" { - if v, e := strconv.Atoi(offsetStr); e == nil { - offsetInt = v - } - } - - if limitInt > 0 { - sb.WriteString(" LIMIT ") - sb.WriteString(strconv.Itoa(limitInt)) - if offsetInt > 0 { - sb.WriteString(" OFFSET ") - sb.WriteString(strconv.Itoa(offsetInt)) - } - } else { - // 当limit未设置或为0时,仅当offset>0才生成OFFSET - if offsetInt > 0 { - sb.WriteString(" OFFSET ") - sb.WriteString(strconv.Itoa(offsetInt)) - } - } - - return sb.String(), nil -} - -// ExportTemplate 导出Excel模板 -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templateID string) (file *bytes.Buffer, name string, err error) { - var template system.SysExportTemplate - err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error - if err != nil { - return nil, "", err - } - f := excelize.NewFile() - defer func() { - if err := f.Close(); err != nil { - fmt.Println(err) - } - }() - // Create a new sheet. - index, err := f.NewSheet("Sheet1") - if err != nil { - fmt.Println(err) - return - } - var templateInfoMap = make(map[string]string) - - columns, err := utils.GetJSONKeys(template.TemplateInfo) - - err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) - if err != nil { - return nil, "", err - } - var tableTitle []string - for _, key := range columns { - tableTitle = append(tableTitle, templateInfoMap[key]) - } - - for i := range tableTitle { - fErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(i+1), 1), tableTitle[i]) - if fErr != nil { - return nil, "", fErr - } - } - f.SetActiveSheet(index) - file, err = f.WriteToBuffer() - if err != nil { - return nil, "", err - } - - return file, template.Name, nil -} - -// 辅助函数:检查表是否有deleted_at列 -func (s *SysExportTemplateService) hasDeletedAtColumn(tableName string) bool { - var count int64 - global.GVA_DB.Raw("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = 'deleted_at'", tableName).Count(&count) - return count > 0 -} - -// ImportExcel 导入Excel -// Author [piexlmax](https://github.com/piexlmax) -func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID string, file *multipart.FileHeader) (err error) { - var template system.SysExportTemplate - err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error - if err != nil { - return err - } - - src, err := file.Open() - if err != nil { - return err - } - defer src.Close() - - f, err := excelize.OpenReader(src) - if err != nil { - return err - } - - rows, err := f.GetRows("Sheet1") - if err != nil { - return err - } - if len(rows) < 2 { - return errors.New("Excel data is not enough.\nIt should contain title row and data") - } - - var templateInfoMap = make(map[string]string) - err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap) - if err != nil { - return err - } - - db := global.GVA_DB - if template.DBName != "" { - db = global.MustGetGlobalDBByDBName(template.DBName) - } - - items, err := sysExportTemplateService.parseExcelToMap(rows, templateInfoMap) - if err != nil { - return err - } - - return db.Transaction(func(tx *gorm.DB) error { - if template.ImportSQL != "" { - return sysExportTemplateService.importBySQL(tx, template.ImportSQL, items) - } - return sysExportTemplateService.importByGORM(tx, template.TableName, items) - }) -} - -func (sysExportTemplateService *SysExportTemplateService) parseExcelToMap(rows [][]string, templateInfoMap map[string]string) ([]map[string]interface{}, error) { - var titleKeyMap = make(map[string]string) - for key, title := range templateInfoMap { - titleKeyMap[title] = key - } - - excelTitle := rows[0] - for i, str := range excelTitle { - excelTitle[i] = strings.TrimSpace(str) - } - values := rows[1:] - items := make([]map[string]interface{}, 0, len(values)) - for _, row := range values { - var item = make(map[string]interface{}) - for ii, value := range row { - if ii >= len(excelTitle) { - continue - } - if _, ok := titleKeyMap[excelTitle[ii]]; !ok { - continue // excel中多余的标题,在模板信息中没有对应的字段,因此key为空,必须跳过 - } - key := titleKeyMap[excelTitle[ii]] - item[key] = value - } - items = append(items, item) - } - return items, nil -} - -func (sysExportTemplateService *SysExportTemplateService) importBySQL(tx *gorm.DB, sql string, items []map[string]interface{}) error { - for _, item := range items { - if err := tx.Exec(sql, item).Error; err != nil { - return err - } - } - return nil -} - -func (sysExportTemplateService *SysExportTemplateService) importByGORM(tx *gorm.DB, tableName string, items []map[string]interface{}) error { - needCreated := tx.Migrator().HasColumn(tableName, "created_at") - needUpdated := tx.Migrator().HasColumn(tableName, "updated_at") - - for _, item := range items { - if item["created_at"] == nil && needCreated { - item["created_at"] = time.Now() - } - if item["updated_at"] == nil && needUpdated { - item["updated_at"] = time.Now() - } - } - return tx.Table(tableName).CreateInBatches(&items, 1000).Error -} - -func getColumnName(n int) string { - columnName := "" - for n > 0 { - n-- - columnName = string(rune('A'+n%26)) + columnName - n /= 26 - } - return columnName -} diff --git a/server/service/system/sys_initdb_pgsql.go b/server/service/system/sys_initdb_pgsql.go index 2b3ef37..161db6a 100644 --- a/server/service/system/sys_initdb_pgsql.go +++ b/server/service/system/sys_initdb_pgsql.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strings" "git.echol.cn/loser/Go-Web-Template/server/config" "github.com/gookit/color" @@ -19,6 +20,10 @@ import ( type PgsqlInitHandler struct{} +func quotePgIdentifier(name string) string { + return `"` + strings.ReplaceAll(name, `"`, `""`) + `"` +} + func NewPgsqlInitHandler() *PgsqlInitHandler { return &PgsqlInitHandler{} } @@ -55,9 +60,13 @@ func (h PgsqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (n dsn := conf.PgsqlEmptyDsn() var createSql string if conf.Template != "" { - createSql = fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s;", c.Dbname, conf.Template) + createSql = fmt.Sprintf( + "CREATE DATABASE %s WITH TEMPLATE %s;", + quotePgIdentifier(c.Dbname), + quotePgIdentifier(conf.Template), + ) } else { - createSql = fmt.Sprintf("CREATE DATABASE %s;", c.Dbname) + createSql = fmt.Sprintf("CREATE DATABASE %s;", quotePgIdentifier(c.Dbname)) } if err = createDatabase(dsn, "pgx", createSql); err != nil { return nil, err diff --git a/server/service/system/sys_version.go b/server/service/system/sys_version.go deleted file mode 100644 index f6fde51..0000000 --- a/server/service/system/sys_version.go +++ /dev/null @@ -1,230 +0,0 @@ -package system - -import ( - "context" - "git.echol.cn/loser/Go-Web-Template/server/global" - "git.echol.cn/loser/Go-Web-Template/server/model/system" - systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request" - "gorm.io/gorm" -) - -type SysVersionService struct{} - -// CreateSysVersion 创建版本管理记录 -// Author [yourname](https://github.com/yourname) -func (sysVersionService *SysVersionService) CreateSysVersion(ctx context.Context, sysVersion *system.SysVersion) (err error) { - err = global.GVA_DB.Create(sysVersion).Error - return err -} - -// DeleteSysVersion 删除版本管理记录 -// Author [yourname](https://github.com/yourname) -func (sysVersionService *SysVersionService) DeleteSysVersion(ctx context.Context, ID string) (err error) { - err = global.GVA_DB.Delete(&system.SysVersion{}, "id = ?", ID).Error - return err -} - -// DeleteSysVersionByIds 批量删除版本管理记录 -// Author [yourname](https://github.com/yourname) -func (sysVersionService *SysVersionService) DeleteSysVersionByIds(ctx context.Context, IDs []string) (err error) { - err = global.GVA_DB.Where("id in ?", IDs).Delete(&system.SysVersion{}).Error - return err -} - -// GetSysVersion 根据ID获取版本管理记录 -// Author [yourname](https://github.com/yourname) -func (sysVersionService *SysVersionService) GetSysVersion(ctx context.Context, ID string) (sysVersion system.SysVersion, err error) { - err = global.GVA_DB.Where("id = ?", ID).First(&sysVersion).Error - return -} - -// GetSysVersionInfoList 分页获取版本管理记录 -// Author [yourname](https://github.com/yourname) -func (sysVersionService *SysVersionService) GetSysVersionInfoList(ctx context.Context, info systemReq.SysVersionSearch) (list []system.SysVersion, total int64, err error) { - limit := info.PageSize - offset := info.PageSize * (info.Page - 1) - // 创建db - db := global.GVA_DB.Model(&system.SysVersion{}) - var sysVersions []system.SysVersion - // 如果有条件搜索 下方会自动创建搜索语句 - if len(info.CreatedAtRange) == 2 { - db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1]) - } - - if info.VersionName != nil && *info.VersionName != "" { - db = db.Where("version_name LIKE ?", "%"+*info.VersionName+"%") - } - if info.VersionCode != nil && *info.VersionCode != "" { - db = db.Where("version_code = ?", *info.VersionCode) - } - err = db.Count(&total).Error - if err != nil { - return - } - - if limit != 0 { - db = db.Limit(limit).Offset(offset) - } - - err = db.Find(&sysVersions).Error - return sysVersions, total, err -} -func (sysVersionService *SysVersionService) GetSysVersionPublic(ctx context.Context) { - // 此方法为获取数据源定义的数据 - // 请自行实现 -} - -// GetMenusByIds 根据ID列表获取菜单数据 -func (sysVersionService *SysVersionService) GetMenusByIds(ctx context.Context, ids []uint) (menus []system.SysBaseMenu, err error) { - err = global.GVA_DB.Where("id in ?", ids).Preload("Parameters").Preload("MenuBtn").Find(&menus).Error - return -} - -// GetApisByIds 根据ID列表获取API数据 -func (sysVersionService *SysVersionService) GetApisByIds(ctx context.Context, ids []uint) (apis []system.SysApi, err error) { - err = global.GVA_DB.Where("id in ?", ids).Find(&apis).Error - return -} - -// GetDictionariesByIds 根据ID列表获取字典数据 -func (sysVersionService *SysVersionService) GetDictionariesByIds(ctx context.Context, ids []uint) (dictionaries []system.SysDictionary, err error) { - err = global.GVA_DB.Where("id in ?", ids).Preload("SysDictionaryDetails").Find(&dictionaries).Error - return -} - -// ImportMenus 导入菜单数据 -func (sysVersionService *SysVersionService) ImportMenus(ctx context.Context, menus []system.SysBaseMenu) error { - return global.GVA_DB.Transaction(func(tx *gorm.DB) error { - // 递归创建菜单 - return sysVersionService.createMenusRecursively(tx, menus, 0) - }) -} - -// createMenusRecursively 递归创建菜单 -func (sysVersionService *SysVersionService) createMenusRecursively(tx *gorm.DB, menus []system.SysBaseMenu, parentId uint) error { - for _, menu := range menus { - // 检查菜单是否已存在 - var existingMenu system.SysBaseMenu - if err := tx.Where("name = ? AND path = ?", menu.Name, menu.Path).First(&existingMenu).Error; err == nil { - // 菜单已存在,使用现有菜单ID继续处理子菜单 - if len(menu.Children) > 0 { - if err := sysVersionService.createMenusRecursively(tx, menu.Children, existingMenu.ID); err != nil { - return err - } - } - continue - } - - // 保存参数和按钮数据,稍后处理 - parameters := menu.Parameters - menuBtns := menu.MenuBtn - children := menu.Children - - // 创建新菜单(不包含关联数据) - newMenu := system.SysBaseMenu{ - ParentId: parentId, - Path: menu.Path, - Name: menu.Name, - Hidden: menu.Hidden, - Component: menu.Component, - Sort: menu.Sort, - Meta: menu.Meta, - } - - if err := tx.Create(&newMenu).Error; err != nil { - return err - } - - // 创建参数 - if len(parameters) > 0 { - for _, param := range parameters { - newParam := system.SysBaseMenuParameter{ - SysBaseMenuID: newMenu.ID, - Type: param.Type, - Key: param.Key, - Value: param.Value, - } - if err := tx.Create(&newParam).Error; err != nil { - return err - } - } - } - - // 创建菜单按钮 - if len(menuBtns) > 0 { - for _, btn := range menuBtns { - newBtn := system.SysBaseMenuBtn{ - SysBaseMenuID: newMenu.ID, - Name: btn.Name, - Desc: btn.Desc, - } - if err := tx.Create(&newBtn).Error; err != nil { - return err - } - } - } - - // 递归处理子菜单 - if len(children) > 0 { - if err := sysVersionService.createMenusRecursively(tx, children, newMenu.ID); err != nil { - return err - } - } - } - return nil -} - -// ImportApis 导入API数据 -func (sysVersionService *SysVersionService) ImportApis(apis []system.SysApi) error { - return global.GVA_DB.Transaction(func(tx *gorm.DB) error { - for _, api := range apis { - // 检查API是否已存在 - var existingApi system.SysApi - if err := tx.Where("path = ? AND method = ?", api.Path, api.Method).First(&existingApi).Error; err == nil { - // API已存在,跳过 - continue - } - - // 创建新API - newApi := system.SysApi{ - Path: api.Path, - Description: api.Description, - ApiGroup: api.ApiGroup, - Method: api.Method, - } - - if err := tx.Create(&newApi).Error; err != nil { - return err - } - } - return nil - }) -} - -// ImportDictionaries 导入字典数据 -func (sysVersionService *SysVersionService) ImportDictionaries(dictionaries []system.SysDictionary) error { - return global.GVA_DB.Transaction(func(tx *gorm.DB) error { - for _, dict := range dictionaries { - // 检查字典是否已存在 - var existingDict system.SysDictionary - if err := tx.Where("type = ?", dict.Type).First(&existingDict).Error; err == nil { - // 字典已存在,跳过 - continue - } - - // 创建新字典 - newDict := system.SysDictionary{ - Name: dict.Name, - Type: dict.Type, - Status: dict.Status, - Desc: dict.Desc, - SysDictionaryDetails: dict.SysDictionaryDetails, - } - - if err := tx.Create(&newDict).Error; err != nil { - return err - } - } - return nil - }) -} diff --git a/server/source/system/api.go b/server/source/system/api.go index 4237e91..f8cd3e4 100644 --- a/server/source/system/api.go +++ b/server/source/system/api.go @@ -152,24 +152,13 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { {ApiGroup: "断点续传(插件版)", Method: "GET", Path: "/simpleUploader/checkFileMd5", Description: "文件完整度验证"}, {ApiGroup: "断点续传(插件版)", Method: "GET", Path: "/simpleUploader/mergeFileMd5", Description: "上传完成合并文件"}, - {ApiGroup: "email", Method: "POST", Path: "/email/emailTest", Description: "发送测试邮件"}, - {ApiGroup: "email", Method: "POST", Path: "/email/sendEmail", Description: "发送邮件"}, + //{ApiGroup: "email", Method: "POST", Path: "/email/emailTest", Description: "发送测试邮件"}, + //{ApiGroup: "email", Method: "POST", Path: "/email/sendEmail", Description: "发送邮件"}, {ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/setAuthorityBtn", Description: "设置按钮权限"}, {ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/getAuthorityBtn", Description: "获取已有按钮权限"}, {ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/canRemoveAuthorityBtn", Description: "删除按钮"}, - {ApiGroup: "导出模板", Method: "POST", Path: "/sysExportTemplate/createSysExportTemplate", Description: "新增导出模板"}, - {ApiGroup: "导出模板", Method: "DELETE", Path: "/sysExportTemplate/deleteSysExportTemplate", Description: "删除导出模板"}, - {ApiGroup: "导出模板", Method: "DELETE", Path: "/sysExportTemplate/deleteSysExportTemplateByIds", Description: "批量删除导出模板"}, - {ApiGroup: "导出模板", Method: "PUT", Path: "/sysExportTemplate/updateSysExportTemplate", Description: "更新导出模板"}, - {ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/findSysExportTemplate", Description: "根据ID获取导出模板"}, - {ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/getSysExportTemplateList", Description: "获取导出模板列表"}, - {ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/exportExcel", Description: "导出Excel"}, - {ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/exportTemplate", Description: "下载模板"}, - {ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/previewSQL", Description: "预览SQL"}, - {ApiGroup: "导出模板", Method: "POST", Path: "/sysExportTemplate/importExcel", Description: "导入Excel"}, - {ApiGroup: "错误日志", Method: "POST", Path: "/sysError/createSysError", Description: "新建错误日志"}, {ApiGroup: "错误日志", Method: "DELETE", Path: "/sysError/deleteSysError", Description: "删除错误日志"}, {ApiGroup: "错误日志", Method: "DELETE", Path: "/sysError/deleteSysErrorByIds", Description: "批量删除错误日志"}, @@ -188,14 +177,6 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { {ApiGroup: "媒体库分类", Method: "GET", Path: "/attachmentCategory/getCategoryList", Description: "分类列表"}, {ApiGroup: "媒体库分类", Method: "POST", Path: "/attachmentCategory/addCategory", Description: "添加/编辑分类"}, {ApiGroup: "媒体库分类", Method: "POST", Path: "/attachmentCategory/deleteCategory", Description: "删除分类"}, - - {ApiGroup: "版本控制", Method: "GET", Path: "/sysVersion/findSysVersion", Description: "获取单一版本"}, - {ApiGroup: "版本控制", Method: "GET", Path: "/sysVersion/getSysVersionList", Description: "获取版本列表"}, - {ApiGroup: "版本控制", Method: "GET", Path: "/sysVersion/downloadVersionJson", Description: "下载版本json"}, - {ApiGroup: "版本控制", Method: "POST", Path: "/sysVersion/exportVersion", Description: "创建版本"}, - {ApiGroup: "版本控制", Method: "POST", Path: "/sysVersion/importVersion", Description: "同步版本"}, - {ApiGroup: "版本控制", Method: "DELETE", Path: "/sysVersion/deleteSysVersion", Description: "删除版本"}, - {ApiGroup: "版本控制", Method: "DELETE", Path: "/sysVersion/deleteSysVersionByIds", Description: "批量删除版本"}, } if err := db.Create(&entities).Error; err != nil { return ctx, errors.Wrap(err, sysModel.SysApi{}.TableName()+"表数据初始化失败!") diff --git a/server/source/system/casbin.go b/server/source/system/casbin.go index a4541da..2c7315f 100644 --- a/server/source/system/casbin.go +++ b/server/source/system/casbin.go @@ -161,17 +161,6 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "888", V1: "/authorityBtn/getAuthorityBtn", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/authorityBtn/canRemoveAuthorityBtn", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/createSysExportTemplate", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/deleteSysExportTemplate", V2: "DELETE"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/deleteSysExportTemplateByIds", V2: "DELETE"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/updateSysExportTemplate", V2: "PUT"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/findSysExportTemplate", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/getSysExportTemplateList", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/exportExcel", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/exportTemplate", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/previewSQL", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysExportTemplate/importExcel", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/sysError/createSysError", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/sysError/deleteSysError", V2: "DELETE"}, {Ptype: "p", V0: "888", V1: "/sysError/deleteSysErrorByIds", V2: "DELETE"}, @@ -191,14 +180,6 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "888", V1: "/attachmentCategory/addCategory", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/attachmentCategory/deleteCategory", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/findSysVersion", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/getSysVersionList", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/downloadVersionJson", V2: "GET"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/exportVersion", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/importVersion", V2: "POST"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/deleteSysVersion", V2: "DELETE"}, - {Ptype: "p", V0: "888", V1: "/sysVersion/deleteSysVersionByIds", V2: "DELETE"}, - {Ptype: "p", V0: "8881", V1: "/user/admin_register", V2: "POST"}, {Ptype: "p", V0: "8881", V1: "/api/createApi", V2: "POST"}, {Ptype: "p", V0: "8881", V1: "/api/getApiList", V2: "POST"}, diff --git a/server/source/system/excel_template.go b/server/source/system/excel_template.go deleted file mode 100644 index 5a05a24..0000000 --- a/server/source/system/excel_template.go +++ /dev/null @@ -1,75 +0,0 @@ -package system - -import ( - "context" - sysModel "git.echol.cn/loser/Go-Web-Template/server/model/system" - "git.echol.cn/loser/Go-Web-Template/server/service/system" - "github.com/pkg/errors" - "gorm.io/gorm" -) - -type initExcelTemplate struct{} - -const initOrderExcelTemplate = initOrderDictDetail + 1 - -// auto run -func init() { - system.RegisterInit(initOrderExcelTemplate, &initExcelTemplate{}) -} - -func (i *initExcelTemplate) InitializerName() string { - return "sys_export_templates" -} - -func (i *initExcelTemplate) MigrateTable(ctx context.Context) (context.Context, error) { - db, ok := ctx.Value("db").(*gorm.DB) - if !ok { - return ctx, system.ErrMissingDBContext - } - return ctx, db.AutoMigrate(&sysModel.SysExportTemplate{}) -} - -func (i *initExcelTemplate) TableCreated(ctx context.Context) bool { - db, ok := ctx.Value("db").(*gorm.DB) - if !ok { - return false - } - return db.Migrator().HasTable(&sysModel.SysExportTemplate{}) -} - -func (i *initExcelTemplate) InitializeData(ctx context.Context) (context.Context, error) { - db, ok := ctx.Value("db").(*gorm.DB) - if !ok { - return ctx, system.ErrMissingDBContext - } - - entities := []sysModel.SysExportTemplate{ - { - Name: "api", - TableName: "sys_apis", - TemplateID: "api", - TemplateInfo: `{ -"path":"路径", -"method":"方法(大写)", -"description":"方法介绍", -"api_group":"方法分组" -}`, - }, - } - if err := db.Create(&entities).Error; err != nil { - return ctx, errors.Wrap(err, "sys_export_templates"+"表数据初始化失败!") - } - next := context.WithValue(ctx, i.InitializerName(), entities) - return next, nil -} - -func (i *initExcelTemplate) DataInserted(ctx context.Context) bool { - db, ok := ctx.Value("db").(*gorm.DB) - if !ok { - return false - } - if errors.Is(db.First(&sysModel.SysExportTemplate{}).Error, gorm.ErrRecordNotFound) { - return false - } - return true -} diff --git a/server/source/system/menu.go b/server/source/system/menu.go index 47733ed..0b4c3ae 100644 --- a/server/source/system/menu.go +++ b/server/source/system/menu.go @@ -53,14 +53,14 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er // 定义所有菜单 allMenus := []SysBaseMenu{ - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "odometer"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 9, Meta: Meta{Title: "关于我们", Icon: "info-filled"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}}, - {MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "common", Name: "common", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "公共能力", Icon: "folder-opened"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "编程辅助", Icon: "tools"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}}, - {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "features/dashboard/DashboardPage", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "DashboardOutlined"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "features/discovery/ModuleLandingPage:about", Sort: 9, Meta: Meta{Title: "关于系统", Icon: "InfoCircleOutlined"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "features/discovery/ModuleLandingPage:superAdmin", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "LockOutlined"}}, + {MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "features/person/ProfilePage", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "UserOutlined"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "common", Name: "common", Component: "features/discovery/ModuleLandingPage:common", Sort: 6, Meta: Meta{Title: "公共能力", Icon: "AppstoreOutlined"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "features/discovery/ModuleLandingPage:systemTools", Sort: 5, Meta: Meta{Title: "编程辅助", Icon: "ToolOutlined"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://echol.cn", Name: "https://echol.cn", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "features/server/ServerStatePage", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "CloudServerOutlined"}}, } // 先创建父级菜单(ParentId = 0 的菜单) @@ -77,28 +77,25 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er // 定义子菜单,并设置正确的ParentId childMenus := []SysBaseMenu{ // superAdmin子菜单 - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysParams", Name: "sysParams", Component: "view/superAdmin/params/sysParams.vue", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "compass"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 8, Meta: Meta{Title: "系统配置", Icon: "operation"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "apiToken", Name: "apiToken", Component: "view/systemTools/apiToken/index.vue", Sort: 9, Meta: Meta{Title: "API Token", Icon: "key"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "loginLog", Name: "loginLog", Component: "view/systemTools/loginLog/index.vue", Sort: 10, Meta: Meta{Title: "登录日志", Icon: "monitor"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysVersion", Name: "sysVersion", Component: "view/systemTools/version/version.vue", Sort: 11, Meta: Meta{Title: "版本管理", Icon: "server"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysError", Name: "sysError", Component: "view/systemTools/sysError/sysError.vue", Sort: 12, Meta: Meta{Title: "错误日志", Icon: "warn"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "authority", Name: "authority", Component: "features/roles/RoleManagementPage", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "TeamOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "menu", Name: "menu", Component: "features/menus/MenuManagementPage", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "AppstoreOutlined", KeepAlive: true}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "api", Name: "api", Component: "features/apis/ApiManagementPage", Sort: 3, Meta: Meta{Title: "API管理", Icon: "ApiOutlined", KeepAlive: true}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "user", Name: "user", Component: "features/users/UserManagementPage", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "UserOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "dictionary", Name: "dictionary", Component: "features/dictionaries/DictionaryManagementPage", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "BookOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "operation", Name: "operation", Component: "features/logs/OperationLogPage", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "ProfileOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysParams", Name: "sysParams", Component: "features/params/ParamsManagementPage", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "SettingOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "system", Name: "system", Component: "features/system/SystemConfigPage", Sort: 8, Meta: Meta{Title: "系统配置", Icon: "SettingOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "apiToken", Name: "apiToken", Component: "features/tokens/ApiTokenPage", Sort: 9, Meta: Meta{Title: "API Token", Icon: "LockOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "loginLog", Name: "loginLog", Component: "features/logs/LoginLogPage", Sort: 10, Meta: Meta{Title: "登录日志", Icon: "FileTextOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysError", Name: "sysError", Component: "features/errors/ErrorLogPage", Sort: 11, Meta: Meta{Title: "错误日志", Icon: "BugOutlined"}}, // common子菜单 - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 1, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 2, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "upload", Name: "upload", Component: "features/media/MediaLibraryPage", Sort: 1, Meta: Meta{Title: "媒体库(上传下载)", Icon: "UploadOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "breakpoint", Name: "breakpoint", Component: "features/breakpoint/BreakpointPage", Sort: 2, Meta: Meta{Title: "断点续传", Icon: "DeploymentUnitOutlined"}}, // systemTools子菜单 - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 1, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 2, Meta: Meta{Title: "导出模板", Icon: "reading"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "view/systemTools/mcpTest/index.vue", Sort: 3, Meta: Meta{Title: "MCP Tools管理", Icon: "partly-cloudy"}}, - {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "view/systemTools/mcpTool/index.vue", Sort: 4, Meta: Meta{Title: "MCP Tools模板", Icon: "magnet"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "features/mcp/McpTestPage", Sort: 1, Meta: Meta{Title: "MCP Tools管理", Icon: "ToolOutlined"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "features/mcp/McpToolPage", Sort: 2, Meta: Meta{Title: "MCP Tools模板", Icon: "ToolOutlined"}}, } // 创建子菜单 diff --git a/web-admin/docs/system-inventory.md b/web-admin/docs/system-inventory.md index a3adf03..1cdbdce 100644 --- a/web-admin/docs/system-inventory.md +++ b/web-admin/docs/system-inventory.md @@ -23,7 +23,6 @@ - `system` 系统配置 - `apiToken` API Token - `loginLog` 登录日志 -- `sysVersion` 版本管理 - `sysError` 错误日志 ### 公共模块 @@ -33,8 +32,6 @@ ### 编程辅助 -- `formCreate` 表单生成器 -- `exportTemplate` 导出模板 - `mcpTest` MCP Tools 管理 - `mcpTool` MCP Tools 模板 @@ -154,15 +151,12 @@ ### 已做成模块入口页 - 关于系统 -- 版本管理 - 媒体库 - 断点续传 -- 表单生成器 -- 导出模板 - MCP Tools 管理 - MCP Tools 模板 ## 说明 - 新后台没有修改后端协议,仍然复用原有 token、菜单、权限和接口格式。 -- 当前仍有部分研发辅助模块保留为入口页,原因不是无法实现,而是这些模块交互面较大,适合单独拆阶段继续重构。 +- 当前仍有部分模块保留为入口页,原因不是无法实现,而是这些模块交互面较大,适合单独拆阶段继续重构。 diff --git a/web-admin/src/App.tsx b/web-admin/src/App.tsx index fc24f02..8094037 100644 --- a/web-admin/src/App.tsx +++ b/web-admin/src/App.tsx @@ -4,6 +4,7 @@ import { Alert, Result, Spin } from 'antd' import { authApi, menuApi } from '@/lib/api' import { buildFullMenus, findDefaultRoute, flattenMenus, isExternalMenu } from '@/lib/menu' import { useAuthStore } from '@/store/auth' +import { InitPage } from '@/features/auth/InitPage' import { LoginPage } from '@/features/auth/LoginPage' import { AdminShell } from '@/features/layout/AdminShell' import { appRoutes } from '@/router/fsRoutes' @@ -133,6 +134,7 @@ export default function App() { return ( + } /> } /> }> } /> diff --git a/web-admin/src/features/apis/ApiManagementPage.tsx b/web-admin/src/features/apis/ApiManagementPage.tsx index af04fc1..2497e07 100644 --- a/web-admin/src/features/apis/ApiManagementPage.tsx +++ b/web-admin/src/features/apis/ApiManagementPage.tsx @@ -1,13 +1,12 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { + Alert, Button, Card, - Col, Drawer, Form, Input, Modal, - Row, Select, Space, Table, @@ -16,84 +15,121 @@ import { message, } from 'antd' import type { ColumnsType } from 'antd/es/table' -import { apiRegistryApi, authorityApi } from '@/lib/api' -import { flattenAuthorities } from '@/lib/tree' -import type { ApiRecord, Authority } from '@/types/system' +import { apiRegistryApi } from '@/lib/api' +import type { ApiRecord, SyncApiPayload } from '@/types/system' -const methodOptions = ['GET', 'POST', 'PUT', 'DELETE'] +const methodOptions = [ + { value: 'POST', label: '创建', color: 'green' }, + { value: 'GET', label: '查看', color: 'blue' }, + { value: 'PUT', label: '更新', color: 'orange' }, + { value: 'DELETE', label: '删除', color: 'red' }, +] + +type SearchValues = { + path?: string + description?: string + apiGroup?: string + method?: string +} + +type ApiFormValues = { + path: string + description: string + apiGroup: string + method: string +} + +function methodMeta(method: string) { + return methodOptions.find((item) => item.value === method) || { label: method, color: 'default' } +} + +function normalizeSyncPayload(payload?: SyncApiPayload): SyncApiPayload { + return { + newApis: payload?.newApis || [], + deleteApis: payload?.deleteApis || [], + ignoreApis: payload?.ignoreApis || [], + } +} export function ApiManagementPage() { - const [searchForm] = Form.useForm() - const [editForm] = Form.useForm() + const [searchForm] = Form.useForm() + const [editForm] = Form.useForm() const [apis, setApis] = useState([]) - const [roles, setRoles] = useState([]) + const [selectedRows, setSelectedRows] = useState([]) const [loading, setLoading] = useState(false) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(10) const [total, setTotal] = useState(0) - const [modalOpen, setModalOpen] = useState(false) + const [orderKey, setOrderKey] = useState() + const [desc, setDesc] = useState(true) + const [drawerOpen, setDrawerOpen] = useState(false) const [saving, setSaving] = useState(false) const [editingApi, setEditingApi] = useState(null) - const [drawerOpen, setDrawerOpen] = useState(false) - const [activeApi, setActiveApi] = useState(null) - const [selectedRoles, setSelectedRoles] = useState([]) - const [savingRoles, setSavingRoles] = useState(false) + const [syncDrawerOpen, setSyncDrawerOpen] = useState(false) + const [syncPayload, setSyncPayload] = useState(normalizeSyncPayload()) + const [syncing, setSyncing] = useState(false) + const [groupOptions, setGroupOptions] = useState>([]) + const [groupMap, setGroupMap] = useState>({}) + const [editGroupSearch, setEditGroupSearch] = useState('') + const [syncGroupSearch, setSyncGroupSearch] = useState('') - const roleOptions = useMemo( - () => - flattenAuthorities(roles).map((item) => ({ - label: `${' '.repeat(item.depth)}${item.authorityName} (${item.authorityId})`, - value: item.authorityId, - })), - [roles], - ) + const ensureGroupOption = useCallback((rawValue: string) => { + const value = rawValue.trim() + if (!value) { + return '' + } + setGroupOptions((current) => { + if (current.some((item) => item.value === value)) { + return current + } + return [...current, { label: value, value }] + }) + return value + }, []) - const apiGroupOptions = useMemo( - () => - Array.from(new Set(apis.map((item) => item.apiGroup))) - .filter(Boolean) - .map((group) => ({ - label: group, - value: group, - })), - [apis], - ) + const loadGroupOptions = useCallback(async () => { + const response = await apiRegistryApi.getApiGroups() + setGroupOptions(response.data.groups.map((item) => ({ label: item, value: item }))) + setGroupMap(response.data.apiGroupMap) + }, []) const reloadApis = useCallback(async () => { setLoading(true) try { - const [apiRes, roleRes] = await Promise.all([ - apiRegistryApi.getApiList({ - page, - pageSize, - ...searchForm.getFieldsValue(), - }), - authorityApi.getAuthorityList(), - ]) + const apiRes = await apiRegistryApi.getApiList({ + page, + pageSize, + orderKey, + desc, + ...searchForm.getFieldsValue(), + }) setApis(apiRes.data.list) setTotal(apiRes.data.total) - setRoles(roleRes.data) } finally { setLoading(false) } - }, [page, pageSize, searchForm]) + }, [desc, orderKey, page, pageSize, searchForm]) useEffect(() => { - reloadApis() + void reloadApis() }, [reloadApis]) + useEffect(() => { + void loadGroupOptions() + }, [loadGroupOptions]) + const openCreate = () => { setEditingApi(null) editForm.resetFields() - editForm.setFieldsValue({ method: 'POST' }) - setModalOpen(true) + editForm.setFieldsValue({ method: 'POST', path: '', apiGroup: '', description: '' }) + setDrawerOpen(true) } const openEdit = async (record: ApiRecord) => { const response = await apiRegistryApi.getApiById(record.ID) setEditingApi(response.data.api) editForm.setFieldsValue(response.data.api) - setModalOpen(true) + setDrawerOpen(true) } const saveApi = async () => { @@ -101,17 +137,15 @@ export function ApiManagementPage() { setSaving(true) try { if (editingApi) { - await apiRegistryApi.updateApi({ - ID: editingApi.ID, - ...values, - }) + await apiRegistryApi.updateApi({ ID: editingApi.ID, ...values }) message.success('接口已更新') } else { await apiRegistryApi.createApi(values) - message.success('接口已创建') + message.success('接口已创建,请到角色管理页分配权限') } - setModalOpen(false) - reloadApis() + setDrawerOpen(false) + await reloadApis() + await loadGroupOptions() } finally { setSaving(false) } @@ -120,64 +154,143 @@ export function ApiManagementPage() { const deleteApi = (record: ApiRecord) => { Modal.confirm({ title: `删除接口 ${record.path}`, + content: '此操作会删除当前 API 在所有角色下的权限关系。', okButtonProps: { danger: true }, onOk: async () => { await apiRegistryApi.deleteApi({ ID: record.ID }) message.success('接口已删除') - reloadApis() + if (apis.length === 1 && page > 1) { + setPage((current) => current - 1) + } else { + await reloadApis() + } + await loadGroupOptions() }, }) } - const openRoleDrawer = async (record: ApiRecord) => { - const response = await apiRegistryApi.getApiRoles(record.path, record.method) - setActiveApi(record) - setSelectedRoles(response.data) - setDrawerOpen(true) + const batchDeleteApis = () => { + Modal.confirm({ + title: '批量删除接口', + content: `当前将删除 ${selectedRows.length} 条接口记录,是否继续?`, + okButtonProps: { danger: true }, + onOk: async () => { + await apiRegistryApi.deleteApisByIds(selectedRows.map((item) => item.ID)) + message.success('批量删除成功') + setSelectedRows([]) + if (apis.length === selectedRows.length && page > 1) { + setPage((current) => current - 1) + } else { + await reloadApis() + } + await loadGroupOptions() + }, + }) } - const saveRoles = async () => { - if (!activeApi) { + const refreshCasbin = () => { + Modal.confirm({ + title: '刷新 Casbin 缓存', + content: '确定立即刷新权限缓存吗?', + onOk: async () => { + await apiRegistryApi.freshCasbin() + message.success('Casbin 缓存已刷新') + }, + }) + } + + const openSyncDrawer = async () => { + const response = await apiRegistryApi.syncApi() + const nextPayload = normalizeSyncPayload(response.data) + nextPayload.newApis = nextPayload.newApis.map((item) => ({ + ...item, + apiGroup: item.apiGroup || groupMap[item.path.split('/')[1]] || '', + })) + setSyncPayload(nextPayload) + setSyncDrawerOpen(true) + } + + const ignoreSyncApi = async (record: ApiRecord, flag: boolean) => { + await apiRegistryApi.ignoreApi({ path: record.path, method: record.method, flag }) + message.success(flag ? '已加入忽略列表' : '已取消忽略') + setSyncPayload((current) => { + if (flag) { + return { + ...current, + newApis: current.newApis.filter((item) => !(item.path === record.path && item.method === record.method)), + ignoreApis: [...current.ignoreApis, record], + } + } + + return { + ...current, + ignoreApis: current.ignoreApis.filter((item) => !(item.path === record.path && item.method === record.method)), + newApis: [...current.newApis, record], + } + }) + } + + const createSingleSyncApi = async (record: ApiRecord) => { + if (!record.apiGroup) { + message.error('请先填写 API 分组') return } - setSavingRoles(true) + if (!record.description) { + message.error('请先填写 API 描述') + return + } + await apiRegistryApi.createApi(record) + message.success('接口已新增') + setSyncPayload((current) => ({ + ...current, + newApis: current.newApis.filter((item) => !(item.path === record.path && item.method === record.method)), + })) + await reloadApis() + await loadGroupOptions() + } + + const submitSyncPayload = async () => { + if (syncPayload.newApis.some((item) => !item.apiGroup || !item.description)) { + message.error('存在未填写分组或描述的 API,无法确认同步') + return + } + setSyncing(true) try { - await apiRegistryApi.setApiRoles({ - path: activeApi.path, - method: activeApi.method, - authorityIds: selectedRoles, - }) - message.success('接口角色关系已更新') - setDrawerOpen(false) + await apiRegistryApi.enterSyncApi(syncPayload) + message.success('API 同步完成') + setSyncDrawerOpen(false) + await reloadApis() + await loadGroupOptions() } finally { - setSavingRoles(false) + setSyncing(false) } } const columns: ColumnsType = [ - { title: 'ID', dataIndex: 'ID', width: 80 }, - { title: '路径', dataIndex: 'path', width: 260 }, - { title: '分组', dataIndex: 'apiGroup', width: 160 }, - { title: '描述', dataIndex: 'description', width: 240 }, + { title: 'ID', dataIndex: 'ID', width: 80, sorter: true }, + { title: 'API 路径', dataIndex: 'path', width: 260, sorter: true }, + { title: 'API 分组', dataIndex: 'apiGroup', width: 180, sorter: true }, + { title: 'API 简介', dataIndex: 'description', width: 220, sorter: true }, { - title: '方法', + title: '请求', dataIndex: 'method', - width: 100, - render: (value: string) => {value}, + width: 120, + sorter: true, + render: (value: string) => { + const meta = methodMeta(value) + return {`${value} / ${meta.label}`} + }, }, { title: '操作', key: 'actions', - width: 220, + width: 160, fixed: 'right', render: (_, record) => ( - @@ -189,75 +302,89 @@ export function ApiManagementPage() { return (
-
-
- - API 管理 - - - 当前页覆盖接口目录、接口元数据和接口角色关系。 - + +
+
+ + API 管理 + + + 这里仅维护 API 目录和分组。菜单权限与 API 权限统一在角色管理中分配。 + +
+ + + + + +
- - - - -
-
{ setPage(1); reloadApis() }}> - - - - - - - - - - - - - - ({ label: item, value: item }))} /> - - - - - - - -
+
{ + setPage(1) + void reloadApis() + }} + > + + + + + + + + ({ label: `${item.label}(${item.value})`, value: item.value }))} + placeholder="请选择" + /> + + + + + + + +
+ + setSelectedRows(rows), + }} pagination={{ current: page, pageSize, @@ -268,50 +395,231 @@ export function ApiManagementPage() { setPageSize(nextPageSize) }, }} + onChange={(_, __, sorter) => { + if (Array.isArray(sorter)) { + return + } + if (!sorter.order || !sorter.field) { + setOrderKey(undefined) + setDesc(true) + return + } + setOrderKey(sorter.field === 'ID' ? 'id' : String(sorter.field)) + setDesc(sorter.order === 'descend') + }} /> - setModalOpen(false)} - onOk={saveApi} - confirmLoading={saving} + setDrawerOpen(false)} + width={520} + extra={ + + + + + } > +
- - + + + + ({ label: item, value: item }))} /> + + -
+ setDrawerOpen(false)} - title={activeApi ? `接口角色分配 · ${activeApi.path}` : '接口角色分配'} - width={520} + open={syncDrawerOpen} + onClose={() => setSyncDrawerOpen(false)} + width={1080} + title="同步路由" extra={ - + + + + } > -
`${record.path}::${record.method}`} + pagination={false} + dataSource={syncPayload.newApis} + columns={[ + { title: 'API 路径', dataIndex: 'path', width: 220 }, + { + title: 'API 分组', + width: 220, + render: (_, record) => ( + { + const value = event.target.value + setSyncPayload((current) => ({ + ...current, + newApis: current.newApis.map((item) => + item.path === record.path && item.method === record.method ? { ...item, description: value } : item, + ), + })) + }} + /> + ), + }, + { + title: '请求', + width: 140, + render: (_, record) => { + const meta = methodMeta(record.method) + return {`${record.method} / ${meta.label}`} + }, + }, + { + title: '操作', + width: 180, + render: (_, record) => ( + + + + + ), + }, + ]} + /> + + 已删除路由 +
`${record.path}::${record.method}`} + pagination={false} + dataSource={syncPayload.deleteApis} + columns={[ + { title: 'API 路径', dataIndex: 'path' }, + { title: 'API 分组', dataIndex: 'apiGroup' }, + { title: 'API 简介', dataIndex: 'description' }, + { + title: '请求', + render: (_, record) => { + const meta = methodMeta(record.method) + return {`${record.method} / ${meta.label}`} + }, + }, + ]} + /> + + 忽略路由 +
`${record.path}::${record.method}`} + pagination={false} + dataSource={syncPayload.ignoreApis} + columns={[ + { title: 'API 路径', dataIndex: 'path' }, + { title: 'API 分组', dataIndex: 'apiGroup' }, + { title: 'API 简介', dataIndex: 'description' }, + { + title: '请求', + render: (_, record) => { + const meta = methodMeta(record.method) + return {`${record.method} / ${meta.label}`} + }, + }, + { + title: '操作', + render: (_, record) => ( + + ), + }, + ]} + /> + ) diff --git a/web-admin/src/features/auth/InitPage.tsx b/web-admin/src/features/auth/InitPage.tsx new file mode 100644 index 0000000..30f3847 --- /dev/null +++ b/web-admin/src/features/auth/InitPage.tsx @@ -0,0 +1,305 @@ +import { useEffect, useMemo, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { Alert, Button, Card, Form, Input, Result, Select, Space, Spin, Typography, message } from 'antd' +import { initApi } from '@/lib/api' +import { useAuthStore } from '@/store/auth' +import type { InitCheckResult, InitDBPayload } from '@/types/system' + +const dbTypeOptions: Array<{ label: string; value: InitDBPayload['dbType'] }> = [ + { label: 'MySQL', value: 'mysql' }, + { label: 'PostgreSQL', value: 'pgsql' }, + { label: 'SQLite', value: 'sqlite' }, + { label: 'MSSQL', value: 'mssql' }, +] + +type InitState = 'checking' | 'required' | 'ready' + +const dbDefaults: Record> = { + mysql: { + host: '127.0.0.1', + port: '3306', + }, + pgsql: { + host: '127.0.0.1', + port: '5432', + template: 'template1', + }, + sqlite: { + dbPath: 'db', + }, + mssql: { + host: '127.0.0.1', + port: '1433', + }, +} + +function applyDbDefaults(currentValues: InitDBPayload, nextDbType: InitDBPayload['dbType']): InitDBPayload { + return { + ...currentValues, + ...dbDefaults[nextDbType], + dbType: nextDbType, + } +} + +export function InitPage() { + const navigate = useNavigate() + const token = useAuthStore((state) => state.token) + const [form] = Form.useForm() + const [state, setState] = useState('checking') + const [checkingError, setCheckingError] = useState(null) + const [submitting, setSubmitting] = useState(false) + const dbType = Form.useWatch('dbType', form) ?? 'mysql' + + const checkInitialization = async () => { + try { + const response = await initApi.checkDB() + const data: InitCheckResult = response.data + + if (data.needInit) { + setState('required') + } else { + setState('ready') + } + setCheckingError(null) + } catch (error) { + const messageText = error instanceof Error ? error.message : '初始化状态检测失败' + setCheckingError(messageText) + } + } + + useEffect(() => { + void checkInitialization() + }, []) + + const helperText = useMemo(() => { + switch (dbType) { + case 'mysql': + return '将创建数据库并写回 MySQL 连接配置。' + case 'pgsql': + return '默认使用 PostgreSQL 公共库建库,可按需指定 template。' + case 'sqlite': + return '将创建本地 SQLite 数据库文件,并写回 sqlite 配置。' + case 'mssql': + return '将按当前连接信息接入 MSSQL,并写回 mssql 配置。' + default: + return '' + } + }, [dbType]) + + const onDbTypeChange = (nextDbType: InitDBPayload['dbType']) => { + const currentValues = form.getFieldsValue() + form.setFieldsValue(applyDbDefaults(currentValues, nextDbType)) + } + + const submit = async () => { + const values = await form.validateFields() + setSubmitting(true) + try { + await initApi.initDB(values) + message.success('初始化完成,请使用 admin 账户登录') + navigate('/login', { replace: true }) + } finally { + setSubmitting(false) + } + } + + if (state === 'checking') { + return ( +
+
+ Project Bootstrap +
+ + 正在检测 +
+ 项目初始化状态 +
+ + 初始化页只在数据库尚未建立、配置尚未回写时使用。检测完成后会自动切换到对应状态。 + +
+
+
+ +
+ + 正在检测初始化状态... + {checkingError ? ( + void checkInitialization()}> + 重试 + + } + /> + ) : null} +
+
+
+
+ ) + } + + if (state === 'ready') { + return ( +
+
+ Project Bootstrap +
+ + 项目已经完成 +
+ 首次初始化 +
+ + 当前服务已经具备数据库连接和基础管理员账号,不需要再次执行初始化。 + +
+
+
+ + navigate(token ? '/' : '/login', { replace: true })}> + {token ? '进入后台' : '前往登录'} + , + , + ]} + /> + +
+
+ ) + } + + return ( +
+
+ Project Bootstrap +
+ + 首次启动先完成 +
+ 项目初始化 +
+ + 这里负责创建主业务数据库、写回当前服务配置,并生成默认管理员账号。初始化完成后,再进入后台登录。 + +
+
+
+ + + 初始化项目 + + + 默认管理员账号固定为 `admin`,密码使用下面填写的管理员密码。 + + {checkingError ? ( + + ) : null} +
+ + + + ) : ( + <> + + + + + + + + + + + + + + )} + + + + {dbType === 'pgsql' ? ( + + + + ) : ( +
+ )} + + + +
+ + + + + + + +
+
+
+ ) +} diff --git a/web-admin/src/features/auth/LoginPage.tsx b/web-admin/src/features/auth/LoginPage.tsx index ceb6fd9..e0b9113 100644 --- a/web-admin/src/features/auth/LoginPage.tsx +++ b/web-admin/src/features/auth/LoginPage.tsx @@ -1,10 +1,10 @@ import { useEffect, useMemo, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' -import { Alert, Button, Card, Form, Input, Typography, message } from 'antd' -import { authApi, menuApi } from '@/lib/api' +import { Alert, Button, Card, Form, Input, Result, Spin, Typography, message } from 'antd' +import { authApi, initApi, menuApi } from '@/lib/api' import { buildFullMenus, findDefaultRoute } from '@/lib/menu' import { useAuthStore } from '@/store/auth' -import type { CaptchaInfo } from '@/types/system' +import type { CaptchaInfo, InitCheckResult } from '@/types/system' type LoginForm = { username: string @@ -34,6 +34,9 @@ export function LoginPage() { const [captcha, setCaptcha] = useState(null) const [submitting, setSubmitting] = useState(false) const [loadCaptchaError, setLoadCaptchaError] = useState(null) + const [checkingInit, setCheckingInit] = useState(true) + const [needInit, setNeedInit] = useState(false) + const [loadInitError, setLoadInitError] = useState(null) const redirectTarget = useMemo(() => { const state = location.state as { redirectTo?: string } | null @@ -51,8 +54,38 @@ export function LoginPage() { } } + const checkInitialization = async () => { + try { + setCheckingInit(true) + const response = await initApi.checkDB() + const data: InitCheckResult = response.data + setNeedInit(data.needInit) + setLoadInitError(null) + return data.needInit + } catch (error) { + const messageText = error instanceof Error ? error.message : '初始化状态检测失败' + setLoadInitError(messageText) + return false + } finally { + setCheckingInit(false) + } + } + useEffect(() => { - fetchCaptcha() + let ignore = false + + const prepare = async () => { + const required = await checkInitialization() + if (!required && !ignore) { + await fetchCaptcha() + } + } + + void prepare() + + return () => { + ignore = true + } }, []) const submit = async () => { @@ -87,10 +120,84 @@ export function LoginPage() { } } + if (checkingInit) { + return ( +
+
+ Gin-React-Admin · React 重设计版 +
+ + 登录前先检查 +
+ 项目初始化状态 +
+ + 如果当前服务还没有建立数据库连接,登录页会直接给出初始化入口,避免出现无法登录但原因不清楚的状态。 + +
+
+
+ +
+ + 正在检测初始化状态... +
+
+
+
+ ) + } + + if (needInit) { + return ( +
+
+ Gin-React-Admin · React 重设计版 +
+ + 当前项目还没有 +
+ 完成首次初始化 +
+ + 后端已经暴露公开初始化接口。先创建数据库并写回配置,再使用管理员账号登录后台。 + +
+
+
+ + navigate('/init', { replace: true })}> + 前往初始化 + , + , + ]} + /> + {loadInitError ? ( + + ) : null} + +
+
+ ) + } + return (
- Gin-Vue-Admin · React 重设计版 + Gin-React-Admin · React 重设计版
把现有后端协议 @@ -110,6 +217,20 @@ export function LoginPage() { 首版默认沿用原系统认证协议,避免后端改造。 + {loadInitError ? ( + void checkInitialization()}> + 重试 + + } + style={{ marginBottom: 16 }} + /> + ) : null} {loadCaptchaError ? ( = { title: '菜单管理', group: '平台治理', status: 'ready', - summary: '维护后台菜单树、路由属性、按钮定义和角色关联。', - features: ['菜单树编辑', '路由路径维护', '角色分配', '按钮权限入口'], - endpoints: ['/menu/getBaseMenuTree', '/menu/addBaseMenu', '/menu/updateBaseMenu', '/menu/setMenuRoles'], + summary: '维护后台菜单树、路由属性、参数和按钮定义。', + features: ['菜单树编辑', '路由路径维护', '参数配置', '按钮定义'], + endpoints: ['/menu/getMenuList', '/menu/addBaseMenu', '/menu/updateBaseMenu'], }, api: { name: 'api', title: 'API 管理', group: '平台治理', status: 'ready', - summary: '管理接口目录、接口角色授权和 Casbin 刷新。', - features: ['接口列表', '接口增删改', '角色授权', '接口分组'], - endpoints: ['/api/getApiList', '/api/createApi', '/api/updateApi', '/api/setApiRoles'], + summary: '管理接口目录、同步结果、分组和 Casbin 刷新。', + features: ['接口列表', '接口增删改', '接口同步', '接口分组'], + endpoints: ['/api/getApiList', '/api/createApi', '/api/updateApi', '/api/syncApi'], }, user: { name: 'user', @@ -118,15 +118,6 @@ export const moduleCatalog: Record = { features: ['登录结果', '失败原因', '设备信息'], endpoints: ['/sysLoginLog/getLoginLogList', '/sysLoginLog/deleteLoginLogByIds'], }, - sysVersion: { - name: 'sysVersion', - title: '版本管理', - group: '运维', - status: 'partial', - summary: '围绕版本导出、导入和回滚的发布管理模块。', - features: ['版本列表', '版本导出', '版本同步'], - endpoints: ['/sysVersion/getSysVersionList', '/sysVersion/exportVersion', '/sysVersion/importVersion'], - }, sysError: { name: 'sysError', title: '错误日志', @@ -168,27 +159,9 @@ export const moduleCatalog: Record = { title: '编程辅助', group: '研发辅助', status: 'partial', - summary: '聚合表单设计、导出模板和 MCP 管理等研发辅助能力。', - features: ['表单设计', '导出模板', 'MCP 管理'], - endpoints: ['/sysExportTemplate/getSysExportTemplateList', '/mcp/status'], - }, - formCreate: { - name: 'formCreate', - title: '表单生成器', - group: '研发辅助', - status: 'partial', - summary: '拖拽式表单构建入口。', - features: ['表单设计'], - endpoints: [], - }, - exportTemplate: { - name: 'exportTemplate', - title: '导出模板', - group: '研发辅助', - status: 'partial', - summary: '维护 Excel 导入导出模板。', - features: ['模板管理', 'SQL 预览'], - endpoints: ['/sysExportTemplate/getSysExportTemplateList', '/sysExportTemplate/previewSQL'], + summary: '聚合 MCP 管理等研发辅助能力。', + features: ['MCP 管理', 'MCP 模板'], + endpoints: ['/mcp/status', '/mcp/tools', '/mcp/createTool'], }, mcpTest: { name: 'mcpTest', diff --git a/web-admin/src/features/menus/MenuManagementPage.tsx b/web-admin/src/features/menus/MenuManagementPage.tsx index 3e051fe..2eacf97 100644 --- a/web-admin/src/features/menus/MenuManagementPage.tsx +++ b/web-admin/src/features/menus/MenuManagementPage.tsx @@ -2,13 +2,16 @@ import { useEffect, useMemo, useState } from 'react' import { Alert, Button, + Cascader, Card, + Divider, Drawer, Form, Input, InputNumber, Modal, Select, + Segmented, Space, Switch, Table, @@ -16,33 +19,136 @@ import { Typography, message, } from 'antd' +import { + AppstoreOutlined, + ApiOutlined, + BugOutlined, + BookOutlined, + CloudServerOutlined, + DashboardOutlined, + DatabaseOutlined, + DeleteOutlined, + DeploymentUnitOutlined, + FileTextOutlined, + HomeOutlined, + InfoCircleOutlined, + LockOutlined, + PlusOutlined, + ProfileOutlined, + SettingOutlined, + TeamOutlined, + ToolOutlined, + UploadOutlined, + UserOutlined, +} from '@ant-design/icons' import type { ColumnsType } from 'antd/es/table' -import { authorityApi, menuApi } from '@/lib/api' -import { flattenAuthorities, flattenMenusForOptions } from '@/lib/tree' -import type { Authority, MenuNode } from '@/types/system' +import { authorityBtnApi, menuApi } from '@/lib/api' +import { + buildMenuComponentOptions, + getComponentLabelByRouteName, + getComponentSuggestedRouteName, + getComponentValueByRouteName, +} from '@/features/menus/menuComponentCatalog' +import { flattenMenusForOptions } from '@/lib/tree' +import { filterRemovedMenus } from '@/lib/menu' +import type { MenuButton, MenuNode, MenuParameter } from '@/types/system' + +type MenuFormValues = { + parentId: number + path: string + name: string + hidden: boolean + component: string + sort: number + meta: { + title: string + icon?: string + activeName?: string + defaultMenu?: boolean + closeTab?: boolean + keepAlive?: boolean + transitionType?: string + } + parameters: MenuParameter[] + menuBtn: MenuButton[] +} + +type ComponentInputMode = 'selector' | 'manual' + +const componentOptions = buildMenuComponentOptions() + +const iconRegistry = { + AppstoreOutlined, + ApiOutlined, + BugOutlined, + BookOutlined, + CloudServerOutlined, + DashboardOutlined, + DatabaseOutlined, + DeploymentUnitOutlined, + FileTextOutlined, + HomeOutlined, + InfoCircleOutlined, + LockOutlined, + ProfileOutlined, + SettingOutlined, + TeamOutlined, + ToolOutlined, + UploadOutlined, + UserOutlined, +} + +const iconOptions = Object.entries(iconRegistry).map(([name, IconComponent]) => ({ + value: name, + searchText: name.toLowerCase(), + label: ( + + + {name} + + ), +})) + +function createEmptyMenuForm(parentId = 0): MenuFormValues { + return { + parentId, + path: '', + name: '', + hidden: false, + component: '', + sort: 1, + meta: { + title: '', + icon: '', + activeName: '', + defaultMenu: false, + closeTab: false, + keepAlive: false, + transitionType: '', + }, + parameters: [], + menuBtn: [], + } +} + +function resolveMenuComponentValue(menu: Pick) { + return getComponentValueByRouteName(menu.name) || menu.component +} + +function resolveMenuComponentDisplay(menu: Pick) { + return getComponentLabelByRouteName(menu.name) || resolveMenuComponentValue(menu) +} export function MenuManagementPage() { - const [form] = Form.useForm() + const [form] = Form.useForm() const [menus, setMenus] = useState([]) - const [roles, setRoles] = useState([]) const [loading, setLoading] = useState(false) - const [modalOpen, setModalOpen] = useState(false) + const [editorOpen, setEditorOpen] = useState(false) const [saving, setSaving] = useState(false) const [editingMenu, setEditingMenu] = useState(null) - const [drawerOpen, setDrawerOpen] = useState(false) - const [activeMenu, setActiveMenu] = useState(null) - const [selectedRoles, setSelectedRoles] = useState([]) - const [defaultRouterRoles, setDefaultRouterRoles] = useState([]) - const [savingRoles, setSavingRoles] = useState(false) - - const roleOptions = useMemo( - () => - flattenAuthorities(roles).map((item) => ({ - label: `${' '.repeat(item.depth)}${item.authorityName} (${item.authorityId})`, - value: item.authorityId, - })), - [roles], - ) + const [componentInputMode, setComponentInputMode] = useState('selector') + const selectedComponent = Form.useWatch('component', form) + const selectedIconName = Form.useWatch(['meta', 'icon'], form) const menuOptions = useMemo( () => @@ -56,48 +162,51 @@ export function MenuManagementPage() { const reloadMenus = async () => { setLoading(true) try { - const [menuRes, roleRes] = await Promise.all([menuApi.getBaseMenuTree(), authorityApi.getAuthorityList()]) - setMenus(menuRes.data.menus) - setRoles(roleRes.data) + const menuRes = await menuApi.getMenuList() + setMenus(filterRemovedMenus(menuRes.data)) } finally { setLoading(false) } } useEffect(() => { - reloadMenus() + void reloadMenus() }, []) const openCreate = (parentId = 0) => { setEditingMenu(null) + setComponentInputMode('selector') form.resetFields() - form.setFieldsValue({ - parentId, - sort: 1, - hidden: false, - keepAlive: false, - closeTab: false, - }) - setModalOpen(true) + form.setFieldsValue(createEmptyMenuForm(parentId)) + setEditorOpen(true) } const openEdit = async (record: MenuNode) => { const response = await menuApi.getBaseMenuById(record.ID) const menu = response.data.menu + const resolvedComponent = resolveMenuComponentValue(menu) setEditingMenu(menu) + setComponentInputMode(getComponentValueByRouteName(menu.name) || getComponentSuggestedRouteName(menu.component) ? 'selector' : 'manual') form.setFieldsValue({ parentId: menu.parentId, - title: menu.meta.title, - name: menu.name, path: menu.path, - component: menu.component, - icon: menu.meta.icon, + name: menu.name, + hidden: Boolean(menu.hidden), + component: resolvedComponent, sort: menu.sort, - hidden: menu.hidden, - keepAlive: menu.meta.keepAlive, - closeTab: menu.meta.closeTab, + meta: { + title: menu.meta.title, + icon: menu.meta.icon, + activeName: menu.meta.activeName, + defaultMenu: Boolean(menu.meta.defaultMenu), + closeTab: Boolean(menu.meta.closeTab), + keepAlive: Boolean(menu.meta.keepAlive), + transitionType: menu.meta.transitionType, + }, + parameters: menu.parameters || [], + menuBtn: menu.menuBtn || [], }) - setModalOpen(true) + setEditorOpen(true) } const saveMenu = async () => { @@ -105,17 +214,31 @@ export function MenuManagementPage() { const payload = { ID: editingMenu?.ID, parentId: values.parentId || 0, - name: values.name, path: values.path, + name: values.name, + hidden: Boolean(values.hidden), component: values.component, sort: values.sort, - hidden: Boolean(values.hidden), meta: { - title: values.title, - icon: values.icon, - keepAlive: Boolean(values.keepAlive), - closeTab: Boolean(values.closeTab), + title: values.meta.title, + icon: values.meta.icon, + activeName: values.meta.activeName, + defaultMenu: Boolean(values.meta.defaultMenu), + closeTab: Boolean(values.meta.closeTab), + keepAlive: Boolean(values.meta.keepAlive), + transitionType: values.meta.transitionType, }, + parameters: (values.parameters || []).map((item) => ({ + ID: item.ID, + type: item.type, + key: item.key, + value: item.value, + })), + menuBtn: (values.menuBtn || []).map((item) => ({ + ID: item.ID, + name: item.name, + desc: item.desc, + })), } setSaving(true) @@ -125,10 +248,10 @@ export function MenuManagementPage() { message.success('菜单已更新') } else { await menuApi.addBaseMenu(payload) - message.success('菜单已创建') + message.success('菜单已创建,请到角色管理分配菜单权限') } - setModalOpen(false) - reloadMenus() + setEditorOpen(false) + await reloadMenus() } finally { setSaving(false) } @@ -137,39 +260,29 @@ export function MenuManagementPage() { const deleteMenu = (record: MenuNode) => { Modal.confirm({ title: `删除菜单 ${record.meta.title}`, - content: '如果该菜单已被角色使用,删除前请先调整菜单授权。', + content: '此操作会删除所有角色下的当前菜单关系,请确认后继续。', okButtonProps: { danger: true }, onOk: async () => { await menuApi.deleteBaseMenu(record.ID) message.success('菜单已删除') - reloadMenus() + await reloadMenus() }, }) } - const openRoleDrawer = async (record: MenuNode) => { - const response = await menuApi.getMenuRoles(record.ID) - setActiveMenu(record) - setSelectedRoles(response.data.authorityIds) - setDefaultRouterRoles(response.data.defaultRouterAuthorityIds) - setDrawerOpen(true) - } - - const saveRoles = async () => { - if (!activeMenu) { + const removeMenuButton = async (index: number) => { + const currentButtons = form.getFieldValue('menuBtn') || [] + const target = currentButtons[index] + if (!target) { return } - setSavingRoles(true) - try { - await menuApi.setMenuRoles({ - menuId: activeMenu.ID, - authorityIds: selectedRoles, - }) - message.success('菜单角色关系已更新') - setDrawerOpen(false) - } finally { - setSavingRoles(false) + if (target.ID) { + await authorityBtnApi.canRemoveAuthorityBtn(target.ID) } + form.setFieldValue( + 'menuBtn', + currentButtons.filter((_: MenuButton, currentIndex: number) => currentIndex !== index), + ) } const columns: ColumnsType = [ @@ -179,23 +292,36 @@ export function MenuManagementPage() { width: 180, render: (_, record) => record.meta.title, }, - { title: '路由 Name', dataIndex: 'name', width: 140 }, - { title: '路由 Path', dataIndex: 'path', width: 180 }, - { title: '组件路径', dataIndex: 'component', width: 280 }, { - title: '状态', - width: 180, + title: '图标', + width: 140, + render: (_, record) => record.meta.icon || '-', + }, + { title: '路由 Name', dataIndex: 'name', width: 160 }, + { title: '路由 Path', dataIndex: 'path', width: 180 }, + { title: '父节点', dataIndex: 'parentId', width: 100 }, + { title: '排序', dataIndex: 'sort', width: 90 }, + { + title: '组件路径', + width: 320, + render: (_, record) => resolveMenuComponentDisplay(record), + }, + { + title: '显示配置', + width: 280, render: (_, record) => ( {record.hidden ? '隐藏' : '显示'} {record.meta.keepAlive ? KeepAlive : null} + {record.meta.closeTab ? CloseTab : null} + {record.meta.defaultMenu ? 基础页 : null} ), }, { title: '操作', key: 'actions', - width: 260, + width: 200, fixed: 'right', render: (_, record) => ( @@ -205,9 +331,6 @@ export function MenuManagementPage() { - @@ -219,118 +342,277 @@ export function MenuManagementPage() { return (
-
-
- - 菜单管理 - - - 当前页直接维护后端基础菜单树,保证新 React 后台与原权限模型保持一致。 - + + +
+
+ + 菜单管理 + + + 对齐原版工作流,直接维护基础菜单树、菜单参数和可控按钮定义。 + +
+
- -
-
+
+ - setModalOpen(false)} - onOk={saveMenu} - confirmLoading={saving} - width={760} + setEditorOpen(false)} + width={920} + extra={ + + + + + } > +
- - - - + 基础信息 + + 文件路径模式 + + value={componentInputMode} + options={[ + { label: '组件选择器', value: 'selector' }, + { label: '手动输入', value: 'manual' }, + ]} + onChange={(value) => setComponentInputMode(value)} + /> + + {componentInputMode === 'selector' ? ( + + { + const componentValue = value.join('/') + form.setFieldValue('component', componentValue) + if (!editingMenu) { + const suggestedRouteName = getComponentSuggestedRouteName(componentValue) + if (suggestedRouteName && !form.getFieldValue('name')) { + form.setFieldValue('name', suggestedRouteName) + form.setFieldValue('path', suggestedRouteName) + } + } + }} + placeholder="请选择页面组件" + /> + + + ) : ( + + + + )} +
- + - - + + { + const currentPath = form.getFieldValue('path') + if (!editingMenu || currentPath === form.getFieldValue('name')) { + form.setFieldValue('path', event.target.value) + } + }} + /> - - - - - - - + + - +
+ + + 路由配置 +
+ + String((option as { searchText?: string })?.searchText || '').includes(input.toLowerCase())} + /> + + - +
+ {selectedIconName && selectedIconName in iconRegistry ? ( +
+ { + const PreviewIcon = iconRegistry[selectedIconName as keyof typeof iconRegistry] + return + })()}> + {selectedIconName} + +
+ ) : null} + + + 高级配置 +
+ + + + + + +
+ + 菜单参数配置 + +
+ + {(fields, { add, remove }) => ( + + {fields.map((field) => ( + +
+ + + + + + +
+ +
+ ))} + +
+ )} +
+ + +
+ + 可控按钮配置 + +
+ + {(fields, { add }) => ( + + {fields.map((field, index) => ( + +
+ + + + + + +
+ +
+ ))} + +
+ )} +
+
) diff --git a/web-admin/src/features/menus/menuComponentCatalog.ts b/web-admin/src/features/menus/menuComponentCatalog.ts index 46a622d..3c15801 100644 --- a/web-admin/src/features/menus/menuComponentCatalog.ts +++ b/web-admin/src/features/menus/menuComponentCatalog.ts @@ -12,6 +12,7 @@ type ComponentTreeNode = { const componentOptions: ComponentOption[] = [ { value: 'features/dashboard/DashboardPage', label: 'DashboardPage · 仪表盘', routeName: 'dashboard' }, + { value: 'features/discovery/ModuleLandingPage:about', label: 'ModuleLandingPage · 关于系统', routeName: 'about' }, { value: 'features/roles/RoleManagementPage', label: 'RoleManagementPage · 角色管理', routeName: 'authority' }, { value: 'features/menus/MenuManagementPage', label: 'MenuManagementPage · 菜单管理', routeName: 'menu' }, { value: 'features/apis/ApiManagementPage', label: 'ApiManagementPage · API 管理', routeName: 'api' }, @@ -54,17 +55,14 @@ const componentOptions: ComponentOption[] = [ label: 'ModuleLandingPage · 编程辅助', routeName: 'systemTools', }, - { - value: 'features/discovery/ModuleLandingPage:exportTemplate', - label: 'ModuleLandingPage · 导出模板', - routeName: 'exportTemplate', - }, { value: 'features/mcp/McpTestPage', label: 'McpTestPage · MCP Tools 管理', routeName: 'mcpTest' }, { value: 'features/mcp/McpToolPage', label: 'McpToolPage · MCP Tools 模板', routeName: 'mcpTool' }, { value: 'features/media/MediaLibraryPage', label: 'MediaLibraryPage · 媒体库', routeName: 'upload' }, ] const componentRouteNameMap = new Map(componentOptions.map((item) => [item.value, item.routeName])) +const routeNameComponentMap = new Map(componentOptions.map((item) => [item.routeName, item.value])) +const routeNameLabelMap = new Map(componentOptions.map((item) => [item.routeName, item.label])) export function buildMenuComponentOptions(): ComponentTreeNode[] { const result: ComponentTreeNode[] = [] @@ -105,3 +103,11 @@ export function isKnownMenuComponent(componentValue: string) { export function getComponentSuggestedRouteName(componentValue: string) { return componentRouteNameMap.get(componentValue) } + +export function getComponentValueByRouteName(routeName: string) { + return routeNameComponentMap.get(routeName) +} + +export function getComponentLabelByRouteName(routeName: string) { + return routeNameLabelMap.get(routeName) +} diff --git a/web-admin/src/features/roles/RoleManagementPage.tsx b/web-admin/src/features/roles/RoleManagementPage.tsx index 1e75690..9a2e3c7 100644 --- a/web-admin/src/features/roles/RoleManagementPage.tsx +++ b/web-admin/src/features/roles/RoleManagementPage.tsx @@ -1,5 +1,6 @@ -import { useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { + Alert, Button, Card, Drawer, @@ -17,58 +18,150 @@ import { } from 'antd' import type { ColumnsType } from 'antd/es/table' import type { DataNode } from 'antd/es/tree' -import { apiRegistryApi, authorityApi, casbinApi, menuApi, userApi } from '@/lib/api' -import { collectCheckedLeafMenus, flattenAuthorities } from '@/lib/tree' -import type { ApiRecord, Authority, MenuNode, UserInfo } from '@/types/system' +import { authorityBtnApi, apiRegistryApi, authorityApi, casbinApi, menuApi, userApi } from '@/lib/api' +import { filterRemovedMenus } from '@/lib/menu' +import { collectMenusByIds, flattenAuthorities } from '@/lib/tree' +import type { ApiRecord, Authority, MenuButton, MenuNode, UserInfo } from '@/types/system' -type PermissionTab = 'menus' | 'apis' | 'users' +type PermissionTab = 'menus' | 'apis' -function mapMenusToTree(menus: MenuNode[]): DataNode[] { - return menus.map((menu) => ({ - key: menu.ID, - title: menu.meta.title, - children: mapMenusToTree(menu.children || []), - })) +type RoleFormValues = { + authorityId: number + authorityName: string + parentId: number } -function mapApisToTree(apis: ApiRecord[]): DataNode[] { - const grouped = apis.reduce>((accumulator, api) => { - if (!accumulator[api.apiGroup]) { - accumulator[api.apiGroup] = [] +type UserSearchValues = { + username: string + nickName: string +} + +function filterMenuTree(nodes: MenuNode[], keyword: string): MenuNode[] { + const normalized = keyword.trim() + if (!normalized) { + return nodes + } + + return nodes.flatMap((node) => { + const children = filterMenuTree(node.children || [], normalized) + if (node.meta.title.includes(normalized) || node.name.includes(normalized) || children.length > 0) { + return [{ ...node, children }] } - accumulator[api.apiGroup].push(api) + return [] + }) +} + +function buildApiTree(apis: ApiRecord[], nameKeyword: string, pathKeyword: string): DataNode[] { + const filteredApis = apis.filter((api) => { + const matchedName = !nameKeyword || api.description.includes(nameKeyword) + const matchedPath = !pathKeyword || api.path.includes(pathKeyword) + return matchedName && matchedPath + }) + + const grouped = filteredApis.reduce>((accumulator, api) => { + const group = api.apiGroup || '未分组' + if (!accumulator[group]) { + accumulator[group] = [] + } + accumulator[group].push(api) return accumulator }, {}) return Object.entries(grouped).map(([group, items]) => ({ key: `group:${group}`, - title: `${group} 组`, + title: `${group}组`, children: items.map((item) => ({ key: `${item.path}::${item.method}`, - title: `${item.method} ${item.path} · ${item.description}`, + title: ( +
+ {item.description || `${item.method} ${item.path}`} + + {item.path} + +
+ ), })), })) } +function buildMenuTree( + menus: MenuNode[], + defaultRouter: string | undefined, + onOpenButtons: (menu: MenuNode) => void, + onSetHomepage: (menu: MenuNode) => void, +): DataNode[] { + return menus.map((menu) => ({ + key: menu.ID, + title: ( +
+ {menu.meta.title} + {defaultRouter === menu.name ? 默认首页 : null} + {menu.name && !(menu.children || []).length ? ( + + ) : null} + {menu.menuBtn?.length ? ( + + ) : null} +
+ ), + children: buildMenuTree(menu.children || [], defaultRouter, onOpenButtons, onSetHomepage), + })) +} + export function RoleManagementPage() { - const [form] = Form.useForm() + const [form] = Form.useForm() + const [userSearchForm] = Form.useForm() const [roles, setRoles] = useState([]) const [menuTree, setMenuTree] = useState([]) const [apis, setApis] = useState([]) - const [users, setUsers] = useState([]) - const [roleModalOpen, setRoleModalOpen] = useState(false) - const [drawerOpen, setDrawerOpen] = useState(false) - const [savingRole, setSavingRole] = useState(false) const [loading, setLoading] = useState(false) + const [roleDrawerOpen, setRoleDrawerOpen] = useState(false) + const [permissionDrawerOpen, setPermissionDrawerOpen] = useState(false) + const [userDrawerOpen, setUserDrawerOpen] = useState(false) + const [savingRole, setSavingRole] = useState(false) + const [loadingUsers, setLoadingUsers] = useState(false) + const [savingPermission, setSavingPermission] = useState(false) + const [savingUsers, setSavingUsers] = useState(false) const [activeTab, setActiveTab] = useState('menus') const [editingRole, setEditingRole] = useState(null) const [copySource, setCopySource] = useState(null) const [activeRole, setActiveRole] = useState(null) const [menuChecked, setMenuChecked] = useState([]) + const [menuHalfChecked, setMenuHalfChecked] = useState([]) const [defaultRouter, setDefaultRouter] = useState() const [apiChecked, setApiChecked] = useState([]) - const [userIds, setUserIds] = useState([]) - const [savingPermission, setSavingPermission] = useState(false) + const [menuFilter, setMenuFilter] = useState('') + const [apiNameFilter, setApiNameFilter] = useState('') + const [apiPathFilter, setApiPathFilter] = useState('') + const [users, setUsers] = useState([]) + const [selectedUserIds, setSelectedUserIds] = useState>(new Set()) + const [userPage, setUserPage] = useState(1) + const [userPageSize, setUserPageSize] = useState(10) + const [userTotal, setUserTotal] = useState(0) + const [buttonModalOpen, setButtonModalOpen] = useState(false) + const [buttonMenu, setButtonMenu] = useState(null) + const [selectedButtonIds, setSelectedButtonIds] = useState([]) + const [savingButtons, setSavingButtons] = useState(false) const roleOptions = useMemo( () => @@ -79,6 +172,29 @@ export function RoleManagementPage() { [roles], ) + const defaultRouterOptions = useMemo( + () => + collectMenusByIds(menuTree, new Set([...menuChecked, ...menuHalfChecked])) + .filter((menu) => menu.name && !menu.name.startsWith('http://') && !menu.name.startsWith('https://')) + .filter((menu) => !(menu.children || []).length) + .map((menu) => ({ + label: menu.meta.title, + value: menu.name, + })), + [menuChecked, menuHalfChecked, menuTree], + ) + + useEffect(() => { + if (!permissionDrawerOpen || !defaultRouter) { + return + } + if (defaultRouterOptions.some((item) => item.value === defaultRouter)) { + return + } + setDefaultRouter(undefined) + message.warning('当前默认首页已不在授权菜单内,已自动清空,请重新选择') + }, [defaultRouter, defaultRouterOptions, permissionDrawerOpen]) + const reloadRoles = async () => { setLoading(true) try { @@ -90,15 +206,77 @@ export function RoleManagementPage() { } useEffect(() => { - reloadRoles() + void reloadRoles() }, []) - const openCreate = (parentId?: number) => { + const loadUserList = useCallback(async (roleId: number) => { + setLoadingUsers(true) + try { + const [userRes, roleUserRes] = await Promise.all([ + userApi.getUserList({ + page: userPage, + pageSize: userPageSize, + username: userSearchForm.getFieldValue('username') || '', + nickName: userSearchForm.getFieldValue('nickName') || '', + orderKey: 'id', + desc: true, + }), + authorityApi.getUsersByAuthorityId(roleId), + ]) + + setUsers(userRes.data.list) + setUserTotal(userRes.data.total) + setSelectedUserIds(new Set(roleUserRes.data)) + } finally { + setLoadingUsers(false) + } + }, [userPage, userPageSize, userSearchForm]) + + const openButtonModal = useCallback(async (menu: MenuNode) => { + if (!activeRole) { + return + } + const response = await authorityBtnApi.getAuthorityBtn({ + menuID: menu.ID, + authorityId: activeRole.authorityId, + }) + setButtonMenu(menu) + setSelectedButtonIds(response.data.selected) + setButtonModalOpen(true) + }, [activeRole]) + + const filteredMenus = useMemo(() => filterMenuTree(menuTree, menuFilter), [menuFilter, menuTree]) + const menuTreeData = useMemo( + () => + buildMenuTree( + filteredMenus, + defaultRouter, + (menu) => void openButtonModal(menu), + (menu) => { + const checkedSet = new Set([...menuChecked, ...menuHalfChecked]) + if (!checkedSet.has(menu.ID)) { + message.warning('请先勾选菜单,再将其设为首页') + return + } + setDefaultRouter(menu.name) + }, + ), + [defaultRouter, filteredMenus, menuChecked, menuHalfChecked, openButtonModal], + ) + const apiTreeData = useMemo(() => buildApiTree(apis, apiNameFilter, apiPathFilter), [apiNameFilter, apiPathFilter, apis]) + + useEffect(() => { + if (userDrawerOpen && activeRole) { + void loadUserList(activeRole.authorityId) + } + }, [activeRole, loadUserList, userDrawerOpen]) + + const openCreate = (parentId = 0) => { setEditingRole(null) setCopySource(null) form.resetFields() - form.setFieldsValue({ parentId: parentId ?? 0 }) - setRoleModalOpen(true) + form.setFieldsValue({ parentId, authorityId: undefined as never, authorityName: '' }) + setRoleDrawerOpen(true) } const openEdit = (record: Authority) => { @@ -109,18 +287,18 @@ export function RoleManagementPage() { authorityName: record.authorityName, parentId: record.parentId ?? 0, }) - setRoleModalOpen(true) + setRoleDrawerOpen(true) } const openCopy = (record: Authority) => { setEditingRole(null) setCopySource(record) form.setFieldsValue({ - authorityId: undefined, + authorityId: undefined as never, authorityName: `${record.authorityName}-副本`, parentId: record.parentId ?? 0, }) - setRoleModalOpen(true) + setRoleDrawerOpen(true) } const saveRole = async () => { @@ -131,7 +309,7 @@ export function RoleManagementPage() { await authorityApi.copyAuthority({ oldAuthorityId: copySource.authorityId, authority: { - authorityId: values.authorityId, + authorityId: Number(values.authorityId), authorityName: values.authorityName, parentId: values.parentId, }, @@ -147,15 +325,15 @@ export function RoleManagementPage() { message.success('角色已更新') } else { await authorityApi.createAuthority({ - authorityId: values.authorityId, + authorityId: Number(values.authorityId), authorityName: values.authorityName, parentId: values.parentId, }) message.success('角色已创建') } - setRoleModalOpen(false) - reloadRoles() + setRoleDrawerOpen(false) + await reloadRoles() } finally { setSavingRole(false) } @@ -164,37 +342,49 @@ export function RoleManagementPage() { const deleteRole = (record: Authority) => { Modal.confirm({ title: `删除角色 ${record.authorityName}`, - content: '删除前请确认没有用户正在依赖该角色。', + content: '删除前请确认没有用户仍依赖当前角色。', okButtonProps: { danger: true }, onOk: async () => { await authorityApi.deleteAuthority(record.authorityId) message.success('角色已删除') - reloadRoles() + await reloadRoles() }, }) } const openPermissionDrawer = async (record: Authority) => { setActiveRole(record) - setDrawerOpen(true) - const [menuRes, checkedMenusRes, apiRes, policyRes, userRes, roleUserRes] = await Promise.all([ + setPermissionDrawerOpen(true) + setActiveTab('menus') + setMenuFilter('') + setApiNameFilter('') + setApiPathFilter('') + const [menuRes, checkedMenusRes, apiRes, policyRes] = await Promise.all([ menuApi.getBaseMenuTree(), menuApi.getMenuAuthority(record.authorityId), apiRegistryApi.getAllApis(), casbinApi.getPolicyPathByAuthorityId(record.authorityId), - userApi.getUserList({ page: 1, pageSize: 999 }), - authorityApi.getUsersByAuthorityId(record.authorityId), ]) - setMenuTree(menuRes.data.menus) + setMenuTree(filterRemovedMenus(menuRes.data.menus)) setMenuChecked( - checkedMenusRes.data.menus.map((item) => Number((item as unknown as { menuId?: number; ID: number }).menuId ?? item.ID)), + checkedMenusRes.data.menus.map((item) => + Number((item as unknown as { menuId?: number; ID: number }).menuId ?? item.ID), + ), ) + setMenuHalfChecked([]) setDefaultRouter(record.defaultRouter) setApis(apiRes.data.apis) setApiChecked(policyRes.data.paths.map((item) => `${item.path}::${item.method}`)) - setUsers(userRes.data.list) - setUserIds(roleUserRes.data) + } + + const openUserDrawer = async (record: Authority) => { + setActiveRole(record) + setUserDrawerOpen(true) + userSearchForm.setFieldsValue({ username: '', nickName: '' }) + setUserPage(1) + setUserPageSize(10) + await loadUserList(record.authorityId) } const saveCurrentPermission = async () => { @@ -204,7 +394,7 @@ export function RoleManagementPage() { setSavingPermission(true) try { if (activeTab === 'menus') { - const checkedMenus = collectCheckedLeafMenus(menuTree, new Set(menuChecked)) + const checkedMenus = collectMenusByIds(menuTree, new Set([...menuChecked, ...menuHalfChecked])) await menuApi.addMenuAuthority({ authorityId: activeRole.authorityId, menus: checkedMenus, @@ -216,7 +406,7 @@ export function RoleManagementPage() { defaultRouter, }) message.success('菜单权限已保存') - } else if (activeTab === 'apis') { + } else { const selectedApis = apis .filter((item) => apiChecked.includes(`${item.path}::${item.method}`)) .map((item) => ({ path: item.path, method: item.method })) @@ -225,59 +415,82 @@ export function RoleManagementPage() { casbinInfos: selectedApis, }) message.success('API 权限已保存') - } else { - await authorityApi.setRoleUsers({ - authorityId: activeRole.authorityId, - userIds, - }) - message.success('角色用户关系已保存') } - reloadRoles() + await reloadRoles() } finally { setSavingPermission(false) } } - const menuOptions = useMemo( - () => - collectCheckedLeafMenus(menuTree, new Set(menuChecked)).map((menu) => ({ - label: menu.meta.title, - value: menu.name, - })), - [menuChecked, menuTree], - ) + const saveRoleUsers = async () => { + if (!activeRole) { + return + } + setSavingUsers(true) + try { + await authorityApi.setRoleUsers({ + authorityId: activeRole.authorityId, + userIds: Array.from(selectedUserIds), + }) + message.success('角色用户关系已保存') + setUserDrawerOpen(false) + } finally { + setSavingUsers(false) + } + } + + const saveButtons = async () => { + if (!activeRole || !buttonMenu) { + return + } + setSavingButtons(true) + try { + await authorityBtnApi.setAuthorityBtn({ + menuID: buttonMenu.ID, + authorityId: activeRole.authorityId, + selected: selectedButtonIds, + }) + message.success('按钮权限已保存') + setButtonModalOpen(false) + } finally { + setSavingButtons(false) + } + } const columns: ColumnsType = [ { title: '角色 ID', dataIndex: 'authorityId', - width: 120, + width: 140, }, { title: '角色名称', dataIndex: 'authorityName', - width: 200, + width: 220, }, { title: '默认首页', dataIndex: 'defaultRouter', - width: 140, + width: 180, render: (value: string | undefined) => value || '-', }, { title: '操作', key: 'actions', - width: 320, + width: 420, render: (_, record) => ( + - - -
+
+ - setRoleModalOpen(false)} - onOk={saveRole} - confirmLoading={savingRole} + setRoleDrawerOpen(false)} + width={520} + extra={ + + + + + } >
- + + + String(value || '').match(/^[1-9]\d*$/) ? Promise.resolve() : Promise.reject(new Error('请输入正整数')), + }, + ]} + > + -
+ setDrawerOpen(false)} - width={900} - title={activeRole ? `角色权限 · ${activeRole.authorityName}` : '角色权限'} + open={permissionDrawerOpen} + onClose={() => setPermissionDrawerOpen(false)} + width={960} + title={activeRole ? `角色配置 · ${activeRole.authorityName}` : '角色配置'} extra={ + + + } + > + + +
{ + setUserPage(1) + if (activeRole) { + void loadUserList(activeRole.authorityId) + } + }} + > + + + + + + + + + + + + + +
{ + setUserPage(page) + setUserPageSize(pageSize) + }, + }} + rowSelection={{ + preserveSelectedRowKeys: true, + selectedRowKeys: Array.from(selectedUserIds), + onSelect: (record, selected) => { + setSelectedUserIds((previous) => { + const next = new Set(previous) + if (selected) { + next.add(record.ID) + } else { + next.delete(record.ID) + } + return next + }) + }, + onSelectAll: (selected, _, changeRows) => { + setSelectedUserIds((previous) => { + const next = new Set(previous) + changeRows.forEach((user) => { + if (selected) { + next.add(user.ID) + } else { + next.delete(user.ID) + } + }) + return next + }) + }, + }} + columns={[ + { title: 'ID', dataIndex: 'ID', width: 80 }, + { title: '用户名', dataIndex: 'userName', width: 160 }, + { title: '昵称', dataIndex: 'nickName', width: 160 }, + { + title: '主角色', + width: 160, + render: (_, user) => user.authority?.authorityName || '-', + }, + ]} + /> + + + + setButtonModalOpen(false)} + onOk={() => void saveButtons()} + confirmLoading={savingButtons} + > + + rowKey="ID" + pagination={false} + dataSource={buttonMenu?.menuBtn || []} + rowSelection={{ + selectedRowKeys: selectedButtonIds, + onChange: (keys) => setSelectedButtonIds(keys.map((item) => Number(item))), + }} + columns={[ + { title: '按钮名称', dataIndex: 'name' }, + { title: '按钮备注', dataIndex: 'desc' }, + ]} + /> + ) } diff --git a/web-admin/src/index.css b/web-admin/src/index.css index a03b457..da24a66 100644 --- a/web-admin/src/index.css +++ b/web-admin/src/index.css @@ -78,8 +78,9 @@ img { background: linear-gradient(180deg, rgba(16, 37, 66, 0.96) 0%, rgba(19, 51, 84, 0.94) 100%); color: var(--text-light); - overflow-y: auto; + overflow-y: scroll; overflow-x: hidden; + scrollbar-gutter: stable; } .admin-brand { @@ -117,6 +118,8 @@ img { } .admin-nav-menu .ant-menu-sub.ant-menu-inline { + width: 100%; + box-sizing: border-box; margin: 8px 0 12px; padding: 8px; border-radius: 20px; @@ -124,6 +127,11 @@ img { border: 1px solid rgba(255, 255, 255, 0.08); } +.admin-nav-menu .ant-menu-submenu, +.admin-nav-menu .ant-menu-submenu-inline { + width: 100%; +} + .admin-nav-menu.ant-menu-dark .ant-menu-item, .admin-nav-menu.ant-menu-dark .ant-menu-submenu-title, .admin-nav-menu.ant-menu-dark .ant-menu-item a, @@ -303,6 +311,12 @@ img { padding: 28px; } +.init-form-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 16px; +} + .capsule { display: inline-flex; align-items: center; @@ -319,6 +333,41 @@ img { gap: 16px; } +@media (max-width: 960px) { + .admin-shell, + .login-shell, + .hero-grid, + .metric-grid, + .config-form-grid, + .server-meter-grid, + .server-runtime-grid, + .server-disk-grid, + .catalog-grid, + .init-form-grid { + grid-template-columns: 1fr; + } + + .admin-shell { + display: block; + } + + .admin-sidebar { + position: relative; + width: 100%; + min-width: 0; + height: auto; + } + + .login-hero { + min-height: 320px; + padding: 40px 24px; + } + + .login-card { + padding: 20px; + } +} + .catalog-card { border-radius: 22px; padding: 18px; diff --git a/web-admin/src/lib/api.ts b/web-admin/src/lib/api.ts index 6a3b433..9cdef8b 100644 --- a/web-admin/src/lib/api.ts +++ b/web-admin/src/lib/api.ts @@ -1,12 +1,16 @@ import { http } from './http' import type { ApiRecord, + ApiGroupsPayload, ApiTokenRecord, AttachmentCategory, + AuthorityButtonSelection, Authority, CaptchaInfo, Dictionary, DictionaryDetail, + InitCheckResult, + InitDBPayload, LoginLog, LoginResult, McpContent, @@ -19,6 +23,7 @@ import type { OperationRecord, PagePayload, ServerState, + SyncApiPayload, SysErrorRecord, SysParam, UserInfo, @@ -48,6 +53,15 @@ export const authApi = { }, } +export const initApi = { + checkDB() { + return http.post('/init/checkdb') + }, + initDB(payload: InitDBPayload) { + return http.post>('/init/initdb', payload) + }, +} + export const menuApi = { getMenu() { return http.post<{ menus: MenuNode[] }>('/menu/getMenu') @@ -167,12 +181,27 @@ export const apiRegistryApi = { deleteApi(payload: { ID: number }) { return http.post>('/api/deleteApi', payload) }, + deleteApisByIds(ids: number[]) { + return http.delete>('/api/deleteApisByIds', { data: { ids } }) + }, getApiById(id: number) { return http.post<{ api: ApiRecord }>('/api/getApiById', { ID: id }) }, freshCasbin() { return http.get>('/api/freshCasbin') }, + syncApi() { + return http.get('/api/syncApi') + }, + getApiGroups() { + return http.get('/api/getApiGroups') + }, + ignoreApi(payload: { path: string; method: string; flag: boolean }) { + return http.post>('/api/ignoreApi', payload) + }, + enterSyncApi(payload: SyncApiPayload) { + return http.post>('/api/enterSyncApi', payload) + }, getApiRoles(path: string, method: string) { return http.get('/api/getApiRoles', { params: { path, method } }) }, @@ -181,6 +210,20 @@ export const apiRegistryApi = { }, } +export const authorityBtnApi = { + getAuthorityBtn(payload: { menuID: number; authorityId: number }) { + return http.post('/authorityBtn/getAuthorityBtn', payload) + }, + setAuthorityBtn(payload: { menuID: number; authorityId: number; selected: number[] }) { + return http.post>('/authorityBtn/setAuthorityBtn', payload) + }, + canRemoveAuthorityBtn(id: number) { + return http.post>('/authorityBtn/canRemoveAuthorityBtn', undefined, { + params: { id }, + }) + }, +} + export const dictionaryApi = { getDictionaryList(params?: Record) { return http.get('/sysDictionary/getSysDictionaryList', { params }) diff --git a/web-admin/src/lib/menu.ts b/web-admin/src/lib/menu.ts index d649a78..68371e5 100644 --- a/web-admin/src/lib/menu.ts +++ b/web-admin/src/lib/menu.ts @@ -1,5 +1,7 @@ import type { AppMenu, MenuNode } from '@/types/system' +const removedFrontendMenuNames = new Set(['sysVersion', 'exportTemplate', 'formCreate']) + export function isExternalMenu(menu: Pick) { return ( menu.path.startsWith('http://') || @@ -9,6 +11,17 @@ export function isExternalMenu(menu: Pick) { ) } +export function filterRemovedMenus(menus: T[]): T[] { + return menus.flatMap((menu) => { + if (removedFrontendMenuNames.has(menu.name)) { + return [] + } + + const children = filterRemovedMenus((menu.children || []) as T[]) + return [{ ...menu, children } as T] + }) +} + function normalizePathSegment(path: string) { if (!path) { return '' @@ -22,7 +35,7 @@ export function buildFullMenus( parentPath = '', parentName?: string, ): AppMenu[] { - return [...menus] + return [...filterRemovedMenus(menus)] .sort((left, right) => left.sort - right.sort) .map((menu) => { const normalized = normalizePathSegment(menu.path) diff --git a/web-admin/src/lib/tree.ts b/web-admin/src/lib/tree.ts index a3d1270..56a813f 100644 --- a/web-admin/src/lib/tree.ts +++ b/web-admin/src/lib/tree.ts @@ -24,3 +24,13 @@ export function collectCheckedLeafMenus(menus: MenuNode[], checkedKeys: Set): MenuNode[] { + return menus.flatMap((menu) => { + const matchedChildren = collectMenusByIds(menu.children || [], checkedKeys) + if (checkedKeys.has(menu.ID)) { + return [{ ...menu, children: undefined }, ...matchedChildren] + } + return matchedChildren + }) +} diff --git a/web-admin/src/router/pages/admin/sysVersion/page.tsx b/web-admin/src/router/pages/admin/sysVersion/page.tsx deleted file mode 100644 index fd59bff..0000000 --- a/web-admin/src/router/pages/admin/sysVersion/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'sysVersion', -} - -export default createModulePage('sysVersion') diff --git a/web-admin/src/router/pages/systemTools/exportTemplate/page.tsx b/web-admin/src/router/pages/systemTools/exportTemplate/page.tsx deleted file mode 100644 index 2310de0..0000000 --- a/web-admin/src/router/pages/systemTools/exportTemplate/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'exportTemplate', -} - -export default createModulePage('exportTemplate') diff --git a/web-admin/src/router/pages/systemTools/formCreate/page.tsx b/web-admin/src/router/pages/systemTools/formCreate/page.tsx deleted file mode 100644 index f44691a..0000000 --- a/web-admin/src/router/pages/systemTools/formCreate/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createModulePage } from '@/router/createModulePage' - -export const routeMeta = { - menuName: 'formCreate', -} - -export default createModulePage('formCreate') diff --git a/web-admin/src/types/system.ts b/web-admin/src/types/system.ts index 383bcc3..39383b4 100644 --- a/web-admin/src/types/system.ts +++ b/web-admin/src/types/system.ts @@ -42,13 +42,24 @@ export type UserInfo = BaseEntity & { export type MenuMeta = { title: string icon?: string + activeName?: string keepAlive?: boolean closeTab?: boolean defaultMenu?: boolean - activeName?: string transitionType?: string } +export type MenuParameter = BaseEntity & { + type: 'query' | 'params' + key: string + value: string +} + +export type MenuButton = BaseEntity & { + name: string + desc?: string +} + export type MenuNode = BaseEntity & { parentId: number path: string @@ -57,7 +68,8 @@ export type MenuNode = BaseEntity & { component: string sort: number meta: MenuMeta - menuBtn?: Array<{ ID: number; name: string; desc?: string }> + parameters?: MenuParameter[] + menuBtn?: MenuButton[] btns?: Record children?: MenuNode[] } @@ -82,6 +94,22 @@ export type CaptchaInfo = { openCaptcha: boolean } +export type InitCheckResult = { + needInit: boolean +} + +export type InitDBPayload = { + adminPassword: string + dbType: 'mysql' | 'pgsql' | 'sqlite' | 'mssql' + host?: string + port?: string + userName?: string + password?: string + dbName: string + dbPath?: string + template?: string +} + export type ApiRecord = BaseEntity & { path: string description: string @@ -89,6 +117,21 @@ export type ApiRecord = BaseEntity & { method: string } +export type SyncApiPayload = { + newApis: ApiRecord[] + deleteApis: ApiRecord[] + ignoreApis: ApiRecord[] +} + +export type ApiGroupsPayload = { + groups: string[] + apiGroupMap: Record +} + +export type AuthorityButtonSelection = { + selected: number[] +} + export type Dictionary = BaseEntity & { name: string type: string