Compare commits

...
154 Commits
Author SHA1 Message Date
奇淼(piexlmaxandkrank666 33cb2ed43d Tailwind css personel (#1509)
* css

* 修改persion页面的样式

* 优化persion样式

---------

Co-authored-by: krank666 <emosick@qq.com>
2023-08-11 01:12:51 +08:00
Felixs b56e521c5f 修复组件名称和icon名称重复导致的打包后富文本框被渲染到了不该出现的位置的bug. 2023-08-11 00:35:15 +08:00
SliverHorn 639b930ac9 Merge pull request #1508 from zdreamx/main-2
bugfix: fixed redis.go Set function
2023-08-10 16:59:48 +08:00
zdreamx 607235eb01 bugfix: fixed redis.go Set function 2023-08-10 16:51:58 +08:00
piexlMax dbfeb4b0fa 提升tailwindcss优先级 2023-08-09 17:19:05 +08:00
药要吃 432725b80d 更新 timed_task.go 增加了两个方法 (#1504)
Add:Timer工具增加AddTaskByFuncWithSeconds与AddTaskByJobWithSeconds方法
Fix:解决添加定时任务没有按时运行问题
2023-08-09 16:17:34 +08:00
taskandtask 91c2909c4a README gin版本标记错误 (#1505)
Co-authored-by: task <121913992@qq.com>
2023-08-09 16:16:28 +08:00
piexlMax 699a2f7956 [fixed]:issues #1501 2023-08-09 16:09:46 +08:00
piexlMax 7c2751ad37 提交v2.6.7版本,修复Sort使用的字段bug。 2023-08-09 15:59:58 +08:00
piexlMax 50b524fa7a 通用css 替换为tailwindcss模式,剔除无用css。 2023-08-09 15:51:18 +08:00
piexlMax aee26bc9b2 css 优化 2023-08-08 18:25:23 +08:00
piexlMax 78de254230 替换部分css为tailwindcss 删除某些无用css 2023-08-08 16:05:41 +08:00
piexlMax 22fe4053e9 增加多图片自动生成功能 2023-08-08 13:00:00 +08:00
piexlMax e73f9ac8f2 增加自动化富文本组件 2023-08-08 12:54:36 +08:00
piexlMax 19987234e5 增加部分功能视频教程快速链接 2023-08-06 15:48:02 +08:00
Tscuite 33239f28f8 feat: update ci add CGO_ENABLED=0 (#1497) 2023-08-04 17:30:41 +08:00
350f4572ed 集成tailwindcss 替换新的登陆页面和版权信息组件 (#1499)
* 集成tailwindcss

* fix: 修改登录,init 页面

* Update package.json

* 升级可升级的第三方库到新版本

* 细节调整

---------

Co-authored-by: bypanghu <bypanghu@163.com>
Co-authored-by: task <121913992@qq.com>
2023-08-04 17:03:21 +08:00
YinYongYou ab29267d64 fix: macos和linux下代码定位命令不对 #1495 (#1496) 2023-08-02 19:20:59 +08:00
xiaotianlu b410820868 Rename outer.go to other.go (#1490) 2023-08-02 10:51:58 +08:00
YinYongYou b1268e0001 feat: use description to name apiGroup (#1494) 2023-08-01 19:07:41 +08:00
奇淼(piexlmax 9fe661269d Merge pull request #1483 from ChengDaqi2023/oscs_fix_cis9at8au51vj78hfkt0
fix(sec): upgrade golang.org/x/image to 0.5.0
2023-07-21 16:15:49 +08:00
ChengDaqi2023 a715a77e76 update golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb to 0.5.0 2023-07-20 10:01:32 +08:00
奇淼(piexlmax 20933f77f5 修复marked问题 2023-07-19 15:05:56 +08:00
piexlMax c618fcafaa 增加多图片自动生成功能 2023-07-18 22:19:12 +08:00
piexlMax 350ce2ad35 修复element2.3.8以上i18n的问题 2023-07-18 00:00:26 +08:00
zhengtianbao eef8580e89 use filepath join instead of hardcode separator (#1479) 2023-07-14 21:23:12 +08:00
task 406fc05970 升级highlight.js marked (#1473)
* 升级highlight.js marked

* 升级highlight.js marked到新版本的兼容
2023-07-14 21:22:36 +08:00
Di Sheng 2b6cb775d8 FIX: globally fix typo 'Field' to 'failed' (#1471) 2023-07-14 21:22:10 +08:00
Rhon Joe fe27fd4c86 Fix #1365 (#1469)
* fix(web): 修复默认不存在时跳转问题

* feature(web): 图片组件增加预览
2023-07-14 21:21:25 +08:00
piexlMax 784dca2baa 路由嵌套情况下保证无边框功能可用 2023-07-11 22:24:47 +08:00
piexlMax 6cff6414c2 修复递归级别下无法将外层layout收起的bug 2023-07-11 22:05:58 +08:00
奇淼(piexlmax b84b6c3464 Update README.md 2023-07-02 16:54:51 +08:00
bd3931d62d [feature]:支持Sqlite数据库 (#1467)
* add sqlite3 support

* 修改gorm.io/driver/sqlite 为 github.com/glebarez/sqlite 适配windows用户无cgo环境

---------

Co-authored-by: Kafumio <linzehong1_2020@qq.com>
Co-authored-by: Kafumio <73083337+Kafumio@users.noreply.github.com>
Co-authored-by: sliverhorn <sliver_horn@qq.com>
2023-06-30 19:18:20 +08:00
奇淼(piexlmax 7ababb626b Merge pull request #1463 from zhanghaixin246/feature_modify_mysql_log_zh
feat:解决mysql配置日志等级不生效问题
2023-06-30 19:17:12 +08:00
zhangheng 62b2833c2f feat:解决mysql配置日志等级不生效问题 2023-06-28 19:14:19 +08:00
ipanghu 190c82bbe6 Merge pull request #1459 from bypanghu/main
fix:修复web在vscode打开的时候识别不了@符号的bug
2023-06-26 16:10:34 +08:00
ipanghu b8055d45e7 fix:修复web在vscode打开的时候识别不了@符号 2023-06-26 16:09:18 +08:00
piexlMax fe09bb3c91 修复插件打包丢失标准路径的bug 2023-06-24 18:04:47 +08:00
piexlMax 84d0a91c44 表单校验除string外不再添加whitespace判断 2023-06-23 13:49:57 +08:00
piexlMax 9d95f660e3 修复个人中心头像不居中的问题 2023-06-11 19:21:44 +08:00
piexlMax 2d76d1f50c 自动化代码搜索条件修正 2023-06-11 13:49:44 +08:00
piexlMax df3b921654 前端自动化页面选项修复 2023-06-10 22:31:18 +08:00
piexlMax e98fccdcfd 处理 图片文件同时存在时table下产生的bug 2023-06-10 22:20:03 +08:00
piexlMax 7b9b940b6a 处理 图片文件同时存在时table下产生的bug 2023-06-10 21:50:07 +08:00
piexlMax 3a4b750d93 调整版本号为2.5.6 2023-06-10 17:31:25 +08:00
小马and奇淼(piexlmax 40e3555d13 优化前端页面 (#1439)
* fix: 点击重置按钮时表格未刷新;
修复点击重置按钮时,筛选项被清空,但是下方表格未刷新的问题;

* fix: 未限制输入范围导致的报错;
后端使用了int类型,未限制输入会导致输入过大的数字时报错;

* fix: 未限制输入类型;
使用文本框会导致输入内容的类型未被限制,后端使用了int类型,输入非数字字符时会导致json绑定错误;

* feat: 添加创建时间验证规则;
后端仅在开始时间和结束时间同时有值时才进行搜索,所以前端会出现只选择一项内容时,搜索结果不符合预期的问题。添加验证规则使搜索结果符合预期。

* feat: 优化创建时间搜索项;
选择开始时间后,自动禁用结束时间中早于开始时间的选项;选择结束时间后,自动禁用开始时间中晚于结束时间的选项;增加校验规则,校验开始时间是否早于结束时间;点击搜索按钮时如果未通过校验则不进行搜索;

* fix: 角色id输入非数字字符时点击提交按钮后变为NaN;

* fix: 角色ID输入超大数字时报错;
后端使用int,输入的数字溢出会导致报错,所以限制输入长度;

* feat: 时间类型的选项增加验证规则;
选择开始时间后,自动禁用结束时间中早于开始时间的选项;选择结束时间后,自动禁用开始时间中晚于结束时间的选项;增加校验规则,校验开始时间是否早于结束时间;点击搜索按钮时如果未通过校验则不进行搜索;

* fix: 补全搜索form的ref引用;

* feat: 添加时间类型的搜索范围提示;
时间类型的搜索范围是开始时间(包含)至结束时间(不包含),为避免歧义,增加相关提示;

* feat: 将"时间"修改为"日期"确保字段名称一致性;

* feat: 将列名称从"按钮组"修改为更友好的"操作";

* feat: 将dialog标题修改为更友好的内容;

* fix: 使用了计算值的属性没有进行绑定;

* fix: 必填字段可以只填写空格;

---------

Co-authored-by: 奇淼(piexlmax <303176530@qq.com>
2023-06-10 17:27:52 +08:00
piexlMax 4b6adb3919 增加自动打包插件功能 2023-06-10 17:16:26 +08:00
piexlMax 66aece580f 增加自动化代码文件组件 bugFixed 2023-06-10 14:51:00 +08:00
qk winter 09fc200d55 Update clamis.go (#1450)
添加了GetUserName方法
2023-06-09 22:11:25 +08:00
piexlMax b17b56dba0 增加自动化代码文件组件 2023-06-09 22:10:18 +08:00
piexlMax a9d0531a56 调整代码自动定位无法识别windows的bug 2023-06-09 19:47:50 +08:00
piexlMax a7a2c85f82 增加自动化代码图片功能 修复 2023-06-09 17:51:16 +08:00
piexlMax 83a06fcd2b 增加自动化代码图片功能 2023-06-08 23:17:46 +08:00
piexlMax d2d7b22a8f 增加自动化代码图片功能 2023-06-08 23:17:46 +08:00
奇淼(piexlmax 92b78ab3c8 Update README.md 2023-06-07 14:28:29 +08:00
piexlMax 0bed3bab98 清理掉无用的casbin缓存刷新,增加重新loadPolicy接口。 2023-06-03 15:09:25 +08:00
piexlMax a60637fb79 修复自动引入代码element过于靠前的问题 2023-06-03 13:46:17 +08:00
piexlMax f1218264be 修复自动引入代码element过于靠前的问题 2023-06-03 13:46:17 +08:00
奇淼(piexlmax 18cf63facc Update README.md 2023-05-31 13:20:44 +08:00
奇淼(piexlmax 9f8e1775af AD 2023-05-31 13:15:38 +08:00
piexlMax 0e39f3ad05 调整swag为最新包 2023-05-23 00:08:53 +08:00
WWater 4fc90c0168 Fix go mod (#1421) 2023-05-22 23:50:19 +08:00
SliverHorn 57b4f539f9 去掉base/login 这个没有鉴权的api (#1427) 2023-05-22 16:07:31 +08:00
SliverHorn 8667ce5fb4 Merge pull request #1434 from wZuiShuai/main
修复接口返回未 return 问题
2023-05-18 20:25:13 +08:00
~ 0ed922bb29 修复接口返回未 return 问题 2023-05-18 17:25:17 +08:00
ipanghu 80eff97be3 Update README.md
fix:modify typos "分片长传"to "分片上传"
2023-04-25 16:32:38 +08:00
奇淼(piexlmax e5cfe5cd99 New fixed (#1418)
* fixed:#1416
* vite版本升级
2023-04-24 18:51:24 +08:00
奇淼(piexlmax 1d8b952a6d fixed:#1416 (#1417) 2023-04-24 17:22:10 +08:00
Albort-zand李展旗 c204f7a514 修复并发请求API情况下的读写冲突问题 (#1415)
Co-authored-by: 李展旗 <lizhanqi@inke.cn>
2023-04-24 17:21:38 +08:00
奇淼(piexlmaxandRowen 6e2ef1e29c Fixed (#1412)
* 菜单项如果有参数则携带参数跳转

* 剔除search冗余代码

---------

Co-authored-by: Rowen <wucenglou@qq.com>
2023-04-08 16:07:22 +08:00
奇淼(piexlmaxandkrank 5f0402043a feat: 指令菜单功能 (#1407)
* feat: 指令菜单功能

* 增加快捷指令功能

* 废弃jwt结构StandardClaims替换为RegisteredClaims #1409

* 调整快捷指令登出操作

---------

Co-authored-by: krank <emosick@qq.com>
2023-04-08 15:37:22 +08:00
piexlMax 6ad7ccdd6e 增加chatTable提示 2023-04-01 21:43:57 +08:00
FelixJiang 90a2fd7f19 优化万用表格Prompt 2023-04-01 21:32:28 +08:00
FelixJiang 58c8b11f85 优化万用表格Prompt 2023-04-01 20:16:37 +08:00
FelixJiang 4d700be9ae 优化万用表格Prompt 2023-04-01 20:16:15 +08:00
奇淼(piexlmax da0d02b990 Revert "修复并发请求API情况下的读写冲突问题 (#1395)" (#1404)
This reverts commit 69aa64f800.
2023-04-01 19:59:15 +08:00
小马 854cb59f6b doc: 避免引用行号变更带来的歧义 (#1400) 2023-04-01 19:58:28 +08:00
Albort-zand李展旗 33cbee23d4 修复并发请求API情况下的读写冲突问题 (#1395)
Co-authored-by: 李展旗 <lizhanqi@inke.cn>
2023-03-30 00:49:02 +08:00
奇淼(piexlmax 0497cf384d Revert "[feat]: config for TLS connection in init MySQL (#1388)" (#1392)
This reverts commit a4a240eace.
2023-03-27 11:09:19 +08:00
AONE acd8fa3810 fix: api layer can not get autofill primary key in automatically generated code (#1384) 2023-03-25 20:04:40 +08:00
TPEngineer 9d024bca3b Update sys_authority_btn.go (#1386)
增加 err 错误处理
2023-03-25 20:04:21 +08:00
Albort-z 7434c9db55 修复字典按ID排序而非sort排序问题 (#1387)
直接使用Preload加载的字典值是按ID排序的,在前端展示时并非希望的sort排序
2023-03-25 20:04:05 +08:00
conbanwa a3ab0b3243 [feat]: config for TLS connection in init MySQL (#1388) 2023-03-25 20:02:59 +08:00
64608f6b05 Gpt (#1389)
* fix:优化自动化代码搜索区域 (#1382)

* 增加gpt功能(万用表格)

* feat: chatTable页面
---------

Co-authored-by: ipanghu <bypanghu@163.com>
Co-authored-by: 陶正虎 <zhenghu.tao@jutze.com.cn>
Co-authored-by: krank <emosick@qq.com>
2023-03-25 20:02:14 +08:00
piexlmax a207c982b7 [fixed]: 点击菜单项跳转无效 #1378 2023-03-18 14:21:34 +08:00
piexlmax 072acac399 [fixed]:自动生成代码pkg和结构简称相同导致编译不通过 #1379 2023-03-18 14:21:34 +08:00
piexlmax 8049466dff 清理刷新路由时出现的warning 2023-03-18 14:21:34 +08:00
SliverHorn 80077ba94d Merge pull request #1376 from SliverHorn/fix_middleware_jwt
fixed: middleware jwt
2023-03-13 16:17:27 +08:00
sliverhorn bd4744557f fixed: middleware jwt err == utils.TokenExpired 修改为 errors.Is(err, utils.TokenExpired) 2023-03-13 15:56:40 +08:00
piexlmax d8072634ef 自动化回滚可以回滚gorm.go 和 router.go 2023-03-11 18:26:55 +08:00
piexlmax 27158f7075 路由跳转后自动页面回归顶部 2023-03-09 23:52:54 +08:00
xin053 f06f1fb7f1 fixbug: 修复代码生成器无法自动导入 time 包问题 (#1368)
原本 for 循环下 if 判断只有 HasTimer 判断,加了 break 可以提前跳出循环,但另外新增了 NeedValid 和 NeedSort 判断后,直接跳出循环会导致无法正常设置 HasTimer ,NeedValid 和 NeedSort
2023-03-07 18:22:26 +08:00
奇淼(piexlmax 8bb7b75f05 Update README.md
修复github 表单生成器使用规则
2023-03-06 20:47:42 +08:00
Jankin 0c9e92abb0 华为obs,file.Open后缺少close,uploadFile返回值顺序不对 (#1367) 2023-03-06 20:45:56 +08:00
= 7a8787b1a0 修复分配角色权限底部超出原始应有范围的问题 2023-03-06 10:27:22 +08:00
SliverHorn 78f71c8b13 fixed: #1352 (#1364) 2023-03-02 16:28:42 +08:00
piexlmax a0a35e8253 修复自动化package驼峰问题 2023-03-01 22:05:04 +08:00
a1140314368and奇淼(piexlmax e892e3f9e1 新增权限初始化时适配数据库的出错日志 (#1359)
* 新增权限初始化时适配数据库的出错日志

* 修改报错详情

修改报错详情

---------

Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2023-02-28 14:43:48 +08:00
= 2dadc98485 调整按需加载下element的国际化为中文 2023-02-27 16:05:56 +08:00
SliverHorn 1bd2b4629e Merge pull request #1357 from Macadamianuts/main
本地文件服务 gin路由报错
2023-02-27 12:52:19 +08:00
WWater ce52cbf6ac Update router.go 2023-02-27 12:44:55 +08:00
奇淼(piexlmax 0abcb5aba1 重构自动化代码,修复皮肤bug (#1355)
* 增加读取ast方法和AddImport 方法来增加引用包功能

* 增加在指定方法中添加头部声明global.DB业务库的方法

* 增加了自动添加AutoMigrate方法。

* 将自动化产生自动迁移功能替换为新ast

* 增加了自动注册router块的功能

* 废除旧的对router和gorm的处理方法,替换为新的自动化方法

* 修复开发模式下皮肤失效的问题
2023-02-25 14:46:02 +08:00
奇淼(piexlmax ca7bdc913e 补充按需加载遗漏的指令加载方式的css (#1351)
Co-authored-by: piexlmax(奇淼 <303176530@qq.com>
2023-02-20 14:48:48 +08:00
piexlmax 2de7d34b4a 区分环境进行按需加载,提升本地开发加载速度。
更改侧边栏活跃色,让其和element-ui皮肤统一。
修复issue https://github.com/flipped-aurora/gin-vue-admin/issues/1347
2023-02-18 14:08:42 +08:00
奇淼(piexlmax 0eea78f796 调整为按需加载,element样式调整为皮肤模式,不再使用全局变量覆盖。 (#1349)
* 调整element为自动按需引入,减少打包体积。

* 调整element样式变更为变量模式不再使用全局css覆盖

* 修改element自动化模板适配element皮肤变量模式

---------

Co-authored-by: piexlmax<303176530@qq.com>
2023-02-16 19:03:51 +08:00
Ackerman 95fa7e8130 fix(operation): 修复操作日志中间件,record.Resp写错的问题 (#1342) 2023-02-11 14:32:56 +08:00
ccfish fd78876dd3 Update system.vue (#1341) 2023-02-08 21:05:00 +08:00
piexlmax eaa3075516 初始化引导着重提示mysql需要使用innoDB 2023-02-08 00:24:15 +08:00
XiaoK29 92338fd219 fix:修复用户管理手机号邮箱无法置空bug (#1339) 2023-02-08 00:20:28 +08:00
ipanghuand陶正虎 26d32c02ea fix: 修复上传图片返回的错误信息,修复登录跳转的转跳函数 (#1340)
* fix : 修改登录转跳是replace

* fix:修复上传图片组件的 报错

---------

Co-authored-by: 陶正虎 <zhenghu.tao@jutze.com.cn>
2023-02-08 00:20:07 +08:00
奇淼(piexlmax e03efc33d7 调整资源管理提示 2023-01-31 15:58:07 +08:00
jerjjj 2d1188af11 update nhooyr.io/websocket v1.8.6 to 1.8.7 (#1338) 2023-01-28 14:53:08 +08:00
奇淼(piexlmax 66666ed5fd 调整eslint,优化oc判断 2023-01-14 10:52:15 +08:00
奇淼(piexlmax dfcb283899 fixed:#1336 (#1337) 2023-01-14 10:27:07 +08:00
znonymous 24215f8b28 feat:backend add router global prefix (#1333)
* feat:backend add router global prefix

* feat: global prefix in plugin router

* feat: remove plugin router prefix and add web router setting
2023-01-14 10:22:46 +08:00
Yexk_M ddc2b4c2ad feat: 监听搜索框部分提供回车键提交搜索 (#1335)
* feat: #1334 按回车键搜索
2023-01-14 10:20:13 +08:00
piexlmax 10cef483f4 Merge branch 'main' of github.com:flipped-aurora/gin-vue-admin 2023-01-07 16:26:51 +08:00
piexlmax 51dde2f71e fixed:issue#1321 2023-01-07 16:26:29 +08:00
5e0117e7c6 Captcha (#1329)
* feat: #807 自定义验证码开关,可以自行配置

* 添加数据库列表支持:SQL SERVER

* [user.vue]: 新增邮箱手机合法性校验,邮箱手机非必填

* [menu.js]: 修复错误的API注释

* 调整配置文件 是其可以支持oracle和mssql

Co-authored-by: Yexk <yexk@yexk.cn>
Co-authored-by: 逆光飞翔 <191180776@qq.com>
Co-authored-by: chenteng <cc17854330572@163.com>
2023-01-01 01:26:57 +08:00
奇淼(piexlmax 3b3535faec 2.5.4升级为2.5.5 2022-12-14 15:39:34 +08:00
mengtiantangandxdefei 3e1067faba web/src/view/layout/aside/historyComponent: fix 浏览器自带返回按钮点击多次才能返回上一页, 多次往路由栈push当前页面路由记录的bug (#1311)
Co-authored-by: xdefei <13523376917@163.com>
2022-12-14 15:31:28 +08:00
奇淼(piexlmax 722382f60e 自动化代码列表可直接进行简单编辑。 2022-12-14 15:23:05 +08:00
奇淼(piexlmax e47a512fc8 取消表格导出功能,修复回滚(删表)功能的painc (#1310)
* [feature]:取消表格导出功能

* 修复回滚(删表)painc的问题

* [feature]:修改初始化
2022-12-12 14:42:47 +08:00
奇淼(piexlmax 0627359b6e [fixed]:解决路径判断的错误 #1304 2022-12-08 19:13:31 +08:00
coke 01d1e4c7f5 fix:service\system\sys_authority.go: (#1295)
DeleteAuthority方法修改没有正确处理err导致返回的err信息不正确
GetAuthorityInfoList方法取消多余的if判断,减少阅读疑惑
2022-12-08 18:15:31 +08:00
TonghuaRoot(童话) 3f564e8179 router.go: 将状态码由 200 调整为 http.StatusOK (#1299) 2022-12-08 18:14:10 +08:00
andywu1998and奇淼(piexlmax b6f3038a73 bugfix: 通过修改配置文件使得跨域白名单正常生效 (#1296)
* bugfix: 通过修改配置文件使得跨域白名单正常生效

Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2022-11-30 16:52:00 +08:00
Carrot2 e67354f34c fix: go version error (#1297) 2022-11-30 16:49:14 +08:00
Jary a04982425f closeLoading()的时候先判断接口是否开启了loading,解决showLoading()失效问题 (#1287) 2022-11-25 18:34:25 +08:00
cokeandwangxd01 2e6f99edf0 修改获取角色列表没有检查err,优化查询逻辑 (#1238)
Co-authored-by: wangxd01 <wangxd01@mingyuanyun.com>
2022-11-22 11:26:31 +08:00
奇淼(piexlmax 9307d08bb6 增加字典方法 showDictLabel(字典,字典value)返回字典 label 2022-11-18 14:40:53 +08:00
飞龙Test 2cdcd98e08 修复手机端顶部标题栏溢出、分页溢出的问题 (#1284) 2022-11-17 17:51:19 +08:00
奇淼(piexlmax 7540ee942f [fixed]:解决无法深层校验的bug #1283 2022-11-16 11:17:03 +08:00
spartacus 7bfad03b2e k8s部署的配置文件修改为config.docker.yaml (#1277) 2022-11-10 12:11:29 +08:00
奇淼(piexlmaxand吴璟 9dacd7056b Develop init (#1280)
* 修改数据库初始化方式,增加引擎配置、统一表前缀、是否关闭复数表名

* 修改拦截器逻辑,develop模式不走数据库权限验证

* 增加表前缀,复数表,数据库引擎配置。

Co-authored-by: 吴璟 <hypf2008@126.com>
2022-11-10 12:08:13 +08:00
Sakishum 4ebe66a29c @sakishum 去除 json tag 多余的双引号 (#1279)
去除结构体 SysCaptchaResponse 字段CaptchaLength 的 json tag 多余的双引号
2022-11-09 12:03:00 +08:00
Yexk_M 6ebed9eff6 logger.go: fix nil pointer dereference (#1271) 2022-11-04 13:45:44 +08:00
piexlmax cc13685062 修复丢失的key 2022-10-30 21:31:18 +08:00
奇淼(piexlmax 6b28490d7b Update ci.yaml 2022-10-30 21:14:57 +08:00
zyw0605688and奇淼(piexlmax 27ba592434 feat:'左侧菜单item修改左内边距' (#1272)
* feat:'左侧菜单修改边距'

Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2022-10-30 21:06:50 +08:00
piexlmax cf5daffe69 根据tab决定是否keepalive (动态keepalive) 2022-10-30 21:00:57 +08:00
piexlmax 192c0e529d 增加自动化排序功能 2022-10-30 20:27:22 +08:00
piexlmax c300e16de9 修复无法将子角色重置修改为根角色的bug 2022-10-29 20:26:45 +08:00
奇淼(piexlmax e46eecfc8e Merge pull request #1269 from longzhang83/master
Update email.go
2022-10-27 17:25:22 +08:00
longzhang83 1e2add4b97 Update email.go
EmailTest  修改From 为 To
2022-10-26 17:50:39 +08:00
奇淼(piexlmaxandbypanghu 975f515691 前端细节调整 (#1268)
* fix: 修改 autoCode 引用文件报错,fixed issue 1266

Co-authored-by: bypanghu <bypanghu@163.com>
2022-10-25 17:30:08 +08:00
05f8ae2aae 增加自动化代码选择业务库功能,增加无边框页面功能。 (#1264)
* feat:增加无边框页面路由

* 更改基础页面文字说明

* 修复安装插件路径穿越漏洞

* 添加业务库数据库选择功能

Co-authored-by: dzwvip <dzwvip@qq.com>
Co-authored-by: znonymous <fly1029@yeah.net>
2022-10-23 21:04:17 +08:00
Tscuite 525b3a0da9 feat: CI (#1255)
* feat: 发布release前,自动更新版本号文件
2022-10-23 18:32:47 +08:00
03265e03fc 增加自动化代码匹配进入业务库功能 (#1249)
* Update api.go.tpl

修复不创建资源标识时 批量删除 方法内 deletedBy 未声明的问题

* 可自动创建业务库

* 自动化业务库回滚适配

* 修复断点续传路径穿越漏洞

* 增加指定菜单亮起功能

* 增加菜单高亮功能,调整版本号

* fixed: 用户后台注册没接收手机号和邮箱

Co-authored-by: dzwvip <dzwvip@qq.com>
Co-authored-by: SliverHorn <503551462@qq.com>
2022-10-15 13:55:47 +08:00
奇淼(piexlmaxanddzwvip 28c928890a 修改自动化代码不携带资源标识的created_by错误 (#1247)
* Update api.go.tpl

修复不创建资源标识时 批量删除 方法内 deletedBy 未声明的问题

* 调整配置文件结构

Co-authored-by: dzwvip <dzwvip@qq.com>
2022-10-13 12:15:35 +08:00
tscuite 6f3e8683a0 fix: 数据库默认用户设置为普通用户gva (#1245) 2022-10-13 11:47:09 +08:00
Bin ba7a16250b fix: (#1242)
* feat:perfect

* fix:buffer time

* fix:zap freifx
2022-10-06 21:48:19 +08:00
piexlmax 1cbbce741a 调整字典管理路由监听模式 2022-10-01 17:07:58 +08:00
奇淼(piexlmaxandzyw0605688 9f9f6b7a08 修改前端日志配置页面对应key (#1237)
* Update web/src/view/systemTools/system/system.vue

去掉 系统工具/系统配置/jwt签名有效期缓冲器后面的提示语(秒),现在已改为天,不加为好。
系统工具/系统配置/数据库配置 日志模式字段对应为log-zap

* 调整配置文件结构

Co-authored-by: zyw0605688 <40220158+zyw0605688@users.noreply.github.com>
2022-09-29 12:26:28 +08:00
235 changed files with 7241 additions and 5398 deletions
-64
View File
@@ -1,64 +0,0 @@
name: gin-vue-admin build test
on:
push:
branches:
- "*"
paths-ignore:
- "./db/**"
- "**.md"
pull_request:
branches:
- "*"
paths-ignore:
- "./db/**"
- "**.md"
jobs:
frontend:
name: Frontend build
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.8.0]
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Build test
run: |
npm install
npm run build
working-directory: ./web
backend:
name: Backend build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.6
uses: actions/setup-go@v1
with:
go-version: 1.16
id: go
- name: Check out branch
uses: actions/checkout@v2
- name: Download dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
working-directory: ./server
- name: Test and Build
run: |
go build -v -race
working-directory: ./server
+269
View File
@@ -0,0 +1,269 @@
name: CI
on:
push:
branches: [ '*' ]
pull_request:
release:
types: [ created, edited ]
workflow_dispatch:
inputs:
gva_version:
required: true
type: string
jobs:
init:
if: github.repository_owner == 'flipped-aurora'
runs-on: ubuntu-latest
steps:
- name: init
run: |
echo "flipped-aurora"
frontend:
if: github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'release'
name: Frontend node ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.8.0]
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Build test
run: |
npm install
npm run build
working-directory: ./web
backend:
if: github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'release'
name: Backend go
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.18]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
id: go
- name: Check out branch
uses: actions/checkout@v2
- name: Download dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
working-directory: ./server
- name: Test and Build
run: |
go build -v -race
working-directory: ./server
devops-test:
if: github.ref == 'refs/heads/test'
name: devops-test
needs:
- init
- backend
- frontend
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.8.0]
go-version: [1.18]
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Sed Config
env:
PROD: ${{ secrets.PROD }}
TESTING: ${{ secrets.TESTING }}
shell: bash
run: |
git branch
ls -l
sed -i "s/${PROD}/${TESTING}/g" web/.env.production
sed -i 's/${basePath}:${basePort}/${basePath}/g' web/src/view/systemTools/formCreate/index.vue
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2.1.2
with:
node-version: ${{ matrix.node-version }}
- name: Build-Node
run: |
cd web/ && yarn install && yarn run build
- name: Use Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Build-go
run: |
cd server/ && go mod tidy && CGO_ENABLED=0 go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
- name: restart
env:
KEY: ${{ secrets.KEY }}
HOST: ${{ secrets.HOST }}
USER: ${{ secrets.USER }}
PROT: ${{ secrets.PROT }}
MKDIRTEST: ${{ secrets.MKDIRTEST }}
run: |
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIRTEST}dist/
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIRTEST}
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIRTEST}resource/ && rm -rf ${MKDIRTEST}resource/*"
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIRTEST}resource/
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIRTEST} && bash restart.sh > /dev/null 2>&1 &"
release-pr:
if: ${{ github.event_name == 'workflow_dispatch' && github.repository_owner == 'flipped-aurora'}}
runs-on: ubuntu-latest
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Sed Config
env:
GVA_VERSION: ${{ inputs.gva_version }}
shell: bash
run: |
sed -i 's/当前版本.*`$/当前版本:v'${GVA_VERSION##v}'`/' web/src/core/config.js
sed -i 's/当前版本.*$/当前版本:v'${GVA_VERSION##v}'/' server/core/server.go
sed -i 's/当前版本.*$/当前版本:v'${GVA_VERSION##v}'/' web/src/core/gin-vue-admin.js
sed -i 's/"version": ".*",$/"version": "'${GVA_VERSION##v}'",/' web/package.json
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add . && git commit -m "release: v${GVA_VERSION##v}"
- name: Push
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
- uses: google-github-actions/release-please-action@v3
with:
command: release-pr
release-type: simple
changelog-path: docs/CHANGELOG.md
release-as: ${{ inputs.gva_version }}
package-name: gin-vue-admin
changelog-types: '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false}]'
release-please:
if: github.ref == 'refs/heads/main' || github.event_name == 'release'
runs-on: ubuntu-latest
needs:
- init
- backend
- frontend
outputs:
release_created: ${{ steps.release_please.outputs.release_created }}
tag_name: ${{ steps.release_please.outputs.tag_name }}
steps:
- uses: google-github-actions/release-please-action@v3
id: release_please
with:
#token: ${{ secrets.GAV_TOKEN }}
command: github-release
#signoff: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
release-type: simple
changelog-path: docs/CHANGELOG.md
#release-as: ${{ inputs.deploy_target }}
package-name: gin-vue-admin
#extra-files: |
# x-release-please-version.json
changelog-types: '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false}]'
devops-prod:
if: needs.release-please.outputs.release_created || github.event_name == 'release'
runs-on: ubuntu-latest
needs:
- init
- release-please
name: devops-prod
strategy:
matrix:
node-version: ['16.x']
go-version: ['1.18']
steps:
- uses: actions/checkout@v2
- name: tag major and minor versions
run: |
echo " ${{ needs.release-please.outputs.tag_name }}"
- name: Sed Config
shell: bash
run: |
git branch
ls -l
sed -i 's/${basePath}:${basePort}/${basePath}/g' web/src/view/systemTools/formCreate/index.vue
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2.1.2
with:
node-version: ${{ matrix.node-version }}
- name: Build-Node
run: |
cd web/ && yarn install && yarn run build
- name: Use Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Build-go
run: |
cd server/ && go mod tidy && CGO_ENABLED=0 go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
- name: restart
env:
KEY: ${{ secrets.KEY }}
HOST: ${{ secrets.HOST }}
USER: ${{ secrets.USER }}
PROT: ${{ secrets.PROT }}
MKDIR: ${{ secrets.MKDIR }}
run: |
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIR}dist/
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIR}
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR}resource/ && rm -rf ${MKDIR}resource/*"
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIR}resource/
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR} && bash restart.sh > /dev/null 2>&1 &"
docker:
name: docker
if: github.ref == 'refs/heads/stop-stop-stop'
runs-on: ubuntu-latest
needs:
- init
- release-please
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Login to Aliyun Registry
uses: docker/login-action@v1
with:
registry: ${{ secrets.ALIYUN_REGISTRY }}
username: ${{ secrets.ALIYUN_DOCKERHUB_USER }}
password: ${{ secrets.ALIYUN_DOCKERHUB_PASSWORD }}
- name: Sed Config
shell: bash
run: |
sed -i 56c"\ && yarn install && yarn build" Makefile
make image TAGS_OPT="latest"
sed -i 's#./entrypoint.sh"#./entrypoint.sh","actions"#g' deploy/docker/Dockerfile
sed -i "s#COPY build/ /usr/share/nginx/html/#COPY . /opt/gva#g" deploy/docker/Dockerfile
sed -i 16c"\ && cd /opt/gva/server/ && go mod tidy && cd /opt/gva/web/ && yarn" deploy/docker/Dockerfile
sed -i "s#open: true#open: false#g" web/vite.config.js
make images TAGS_OPT="latest"
docker push registry.cn-hangzhou.aliyuncs.com/gva/gin-vue-admin:latest
docker push registry.cn-hangzhou.aliyuncs.com/gva/web:latest
docker push registry.cn-hangzhou.aliyuncs.com/gva/server:latest
docker push registry.cn-hangzhou.aliyuncs.com/gva/all:latest
-55
View File
@@ -1,55 +0,0 @@
name: devops-test
on:
push:
branches: [test]
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['16.x']
go-version: ['1.16']
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Sed Config
env:
PROD: ${{ secrets.PROD }}
TESTING: ${{ secrets.TESTING }}
shell: bash
run: |
git branch
ls -l
sed -i "s/${PROD}/${TESTING}/g" web/.env.production
sed -i 's/${basePath}:${basePort}/${basePath}/g' web/src/view/systemTools/formCreate/index.vue
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2.1.2
with:
node-version: ${{ matrix.node-version }}
- name: Build-Node
run: |
cd web/ && yarn install && yarn run build
- name: Use Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Build-go
run: |
cd server/ && go mod tidy && go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
- name: restart
env:
KEY: ${{ secrets.KEY }}
HOST: ${{ secrets.HOST }}
USER: ${{ secrets.USER }}
PROT: ${{ secrets.PROT }}
MKDIRTEST: ${{ secrets.MKDIRTEST }}
run: |
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIRTEST}dist/
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIRTEST}
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIRTEST}resource/ && rm -rf ${MKDIRTEST}resource/*"
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIRTEST}resource/
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIRTEST} && bash restart.sh > /dev/null 2>&1 &"
-52
View File
@@ -1,52 +0,0 @@
name: devops
on:
push:
tags:
- v*
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['16.x']
go-version: ['1.16']
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Sed Config
shell: bash
run: |
git branch
ls -l
sed -i 's/${basePath}:${basePort}/${basePath}/g' web/src/view/systemTools/formCreate/index.vue
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2.1.2
with:
node-version: ${{ matrix.node-version }}
- name: Build-Node
run: |
cd web/ && yarn install && yarn run build
- name: Use Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Build-go
run: |
cd server/ && go mod tidy && go build && mkdir ../web/ser && mv server ../web/ser/ && cd ../web/ser/ && ls -s
- name: restart
env:
KEY: ${{ secrets.KEY }}
HOST: ${{ secrets.HOST }}
USER: ${{ secrets.USER }}
PROT: ${{ secrets.PROT }}
MKDIR: ${{ secrets.MKDIR }}
run: |
mkdir -p ~/.ssh/ && echo "$KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/dist/* ${USER}@${HOST}:${MKDIR}dist/
scp -P ${PROT} -o StrictHostKeyChecking=no -r web/ser/* ${USER}@${HOST}:${MKDIR}
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR}resource/ && rm -rf ${MKDIR}resource/*"
scp -P ${PROT} -o StrictHostKeyChecking=no -r server/resource/* ${USER}@${HOST}:${MKDIR}resource/
ssh -p ${PROT} -o StrictHostKeyChecking=no ${USER}@${HOST} "cd ${MKDIR} && bash restart.sh > /dev/null 2>&1 &"
-32
View File
@@ -1,32 +0,0 @@
name: Docker-CICD
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out branch
uses: actions/checkout@v2
- name: Login to Aliyun Registry
uses: docker/login-action@v1
with:
registry: ${{ secrets.ALIYUN_REGISTRY }}
username: ${{ secrets.ALIYUN_DOCKERHUB_USER }}
password: ${{ secrets.ALIYUN_DOCKERHUB_PASSWORD }}
- name: Sed Config
shell: bash
run: |
sed -i 56c"\ && yarn install && yarn build" Makefile
make image TAGS_OPT="latest"
sed -i 's#./entrypoint.sh"#./entrypoint.sh","actions"#g' deploy/docker/Dockerfile
sed -i "s#COPY build/ /usr/share/nginx/html/#COPY . /opt/gva#g" deploy/docker/Dockerfile
sed -i 16c"\ && cd /opt/gva/server/ && go mod tidy && cd /opt/gva/web/ && yarn" deploy/docker/Dockerfile
sed -i "s#open: true#open: false#g" web/vite.config.js
make images TAGS_OPT="latest"
docker push registry.cn-hangzhou.aliyuncs.com/gva/gin-vue-admin:latest
docker push registry.cn-hangzhou.aliyuncs.com/gva/web:latest
docker push registry.cn-hangzhou.aliyuncs.com/gva/server:latest
docker push registry.cn-hangzhou.aliyuncs.com/gva/all:latest
-114
View File
@@ -1,114 +0,0 @@
name: GIN-VUE-ADMIN Docker Build
on:
push:
branches: [stopstopstop]
#workflow_dispatch:
# inputs:
# webtag:
# description: 'Set web tag, Default latest'
# required: true
# default: "latest"
# servertag:
# description: 'Set server tag, Default latest'
# required: true
# default: "latest"
jobs:
GIN_VUE_ADMIN_WEB:
name: GIN VUE ADMIN WEB
runs-on: ubuntu-latest
steps:
- name: Private Actions Checkout
uses: actions/checkout@v2.3.4
- name: Docker Setup QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v1.6.0
- name: Docker Login
uses: docker/login-action@v1.10.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Login in Aliyun
uses: docker/login-action@v1.10.0
with:
registry: ${{ secrets.DOCKERHUB_REGISTRY_ALIYUN }}
username: ${{ secrets.DOCKERHUB_ACCOUNT_ALIYUN }}
password: ${{ secrets.DOCKERHUB_PASSWORD_ALIYUN }}
- name: Build and push Docker images
uses: docker/build-push-action@v2.7.0
with:
context: .
# 这里是适配 arm64 ,主要看你们的程序是否支持 arm64
platforms: linux/amd64,linux/arm64
file: web/Dockerfile
push: true
# 这里是给docker 打标签,默认不上传 latest
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/web:${{ github.event.inputs.webtag }}
${{ secrets.DOCKERHUB_REGISTRY_ALIYUN }}/${{ secrets.DOCKERHUB_USERNAME_ALIYUN }}web:${{ github.event.inputs.webtag }}
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/web:${{ github.event.inputs.webtag }}.cache
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/web:${{ github.event.inputs.webtag }}.cache,mode=max
# 漏洞扫描
- name: Scan for vulnerabilities
uses: crazy-max/ghaction-container-scan@v1
with:
image: ${{ secrets.DOCKERHUB_USERNAME }}/web:${{ github.event.inputs.webtag }}
dockerfile: web/Dockerfile
# 更新 docker 描述页面
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: ${{ secrets.DOCKERHUB_USERNAME }}/web
GIN_VUE_ADMIN_SERVER:
name: GIN VUE ADMIN SERVER
runs-on: ubuntu-latest
steps:
- name: Private Actions Checkout
uses: actions/checkout@v2.3.4
- name: Docker Setup QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v1.6.0
- name: Docker Login
uses: docker/login-action@v1.10.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Login in Aliyun
uses: docker/login-action@v1.10.0
with:
registry: ${{ secrets.DOCKERHUB_REGISTRY_ALIYUN }}
username: ${{ secrets.DOCKERHUB_ACCOUNT_ALIYUN }}
password: ${{ secrets.DOCKERHUB_PASSWORD_ALIYUN }}
- name: Build and push Docker images
uses: docker/build-push-action@v2.7.0
with:
context: .
# 这里是适配 arm64 ,主要看你们的程序是否支持 arm64
platforms: linux/amd64,linux/arm64
file: server/Dockerfile
push: true
# 这里是给docker 打标签,默认不上传 latest
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/server:${{ github.event.inputs.servertag }}
${{ secrets.DOCKERHUB_REGISTRY_ALIYUN }}/${{ secrets.DOCKERHUB_USERNAME_ALIYUN }}server:${{ github.event.inputs.servertag }}
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/server:${{ github.event.inputs.servertag }}.cache
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/server:${{ github.event.inputs.servertag }}.cache,mode=max
# 漏洞扫描
- name: Scan for vulnerabilities
uses: crazy-max/ghaction-container-scan@v1
with:
image: ${{ secrets.DOCKERHUB_USERNAME }}/server:${{ github.event.inputs.servertag }}
dockerfile: server/Dockerfile
# 更新 docker 描述页面
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: ${{ secrets.DOCKERHUB_USERNAME }}/server
+1 -1
View File
@@ -2,7 +2,7 @@ SHELL = /bin/bash
#SCRIPT_DIR = $(shell pwd)/etc/script
#请选择golang版本
BUILD_IMAGE_SERVER = golang:1.16
BUILD_IMAGE_SERVER = golang:1.18
#请选择node版本
BUILD_IMAGE_WEB = node:16
#项目名称
+5 -5
View File
@@ -3,11 +3,11 @@
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width=300" height="300" />
</div>
<div align=center>
<img src="https://img.shields.io/badge/golang-1.16-blue"/>
<img src="https://img.shields.io/badge/gin-1.7.0-lightBlue"/>
<img src="https://img.shields.io/badge/vue-3.2.25-brightgreen"/>
<img src="https://img.shields.io/badge/element--plus-2.0.1-green"/>
<img src="https://img.shields.io/badge/gorm-1.22.5-red"/>
<img src="https://img.shields.io/badge/golang-1.18-blue"/>
<img src="https://img.shields.io/badge/gin-1.9.1-lightBlue"/>
<img src="https://img.shields.io/badge/vue-3.3.4-brightgreen"/>
<img src="https://img.shields.io/badge/element--plus-2.3.8-green"/>
<img src="https://img.shields.io/badge/gorm-1.25.2-red"/>
</div>
English | [简体中文](./README.md)
+9 -9
View File
@@ -3,11 +3,11 @@
<img src="http://qmplusimg.henrongyi.top/gvalogo.jpg" width=300" height="300" />
</div>
<div align=center>
<img src="https://img.shields.io/badge/golang-1.16-blue"/>
<img src="https://img.shields.io/badge/gin-1.7.0-lightBlue"/>
<img src="https://img.shields.io/badge/vue-3.2.25-brightgreen"/>
<img src="https://img.shields.io/badge/element--plus-2.0.1-green"/>
<img src="https://img.shields.io/badge/gorm-1.22.5-red"/>
<img src="https://img.shields.io/badge/golang-1.18-blue"/>
<img src="https://img.shields.io/badge/gin-1.9.1-lightBlue"/>
<img src="https://img.shields.io/badge/vue-3.3.4-brightgreen"/>
<img src="https://img.shields.io/badge/element--plus-2.3.8-green"/>
<img src="https://img.shields.io/badge/gorm-1.25.2-red"/>
</div>
[English](./README-en.md) | 简体中文
@@ -66,8 +66,8 @@ Gin-vue-admin 是一套为快速研发准备的一整套前后端分离架构式
Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-admin 贡献代码或提供建议,请阅读以下内容。
#### 1.2.1 Issue 规范
- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。如果你在使用时产生了疑问,请到 Slack 或 [Gitter](https://gitter.im/ElemeFE/element) 里咨询。
- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。
- 在提交 issue 之前,请搜索相关内容是否已被提出。
#### 1.2.2 Pull Request 规范
@@ -314,8 +314,8 @@ swag init
- 前端文件参考: [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)
- 多点登录限制:需要在`config.yaml`中把`system`中的`use-multipoint`修改为true(需要自行配置Redis和Config中的Redis参数,测试阶段,有bug请及时反馈)。
- 分片长传:提供文件分片上传和大文件分片上传功能示例。
- 表单生成器:表单生成器借助 [@form-generator](https://github.com/JakHuang/form-generator) 。
- 分片上传:提供文件分片上传和大文件分片上传功能示例。
- 表单生成器:表单生成器借助 [@Variant Form](https://github.com/vform666/variant-form) 。
- 代码生成器:后台基础逻辑以及简单curd的代码生成器。
## 6. 知识库
@@ -34,7 +34,7 @@ services:
ipv4_address: 177.7.0.11
server:
image: golang:1.16
image: golang:1.18
container_name: gva-server
hostname: gva-server
restart: always
@@ -63,8 +63,10 @@ services:
ports:
- "13306:3306" # host物理直接映射端口为13306
environment:
#MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
volumes:
- mysql:/var/lib/mysql
networks:
+3 -1
View File
@@ -55,8 +55,10 @@ services:
ports:
- "13306:3306" # host物理直接映射端口为13306
environment:
#MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
volumes:
- mysql:/var/lib/mysql
networks:
@@ -29,7 +29,7 @@ spec:
- containerPort: 8888
name: http
volumeMounts:
- mountPath: /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.yaml
- mountPath: /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.docker.yaml
name: config
subPath: config.yaml
- mountPath: /etc/localtime
-2
View File
@@ -3,13 +3,11 @@ package example
import "github.com/flipped-aurora/gin-vue-admin/server/service"
type ApiGroup struct {
ExcelApi
CustomerApi
FileUploadAndDownloadApi
}
var (
excelService = service.ServiceGroupApp.ExampleServiceGroup.ExcelService
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
)
-120
View File
@@ -1,120 +0,0 @@
package example
import (
"os"
"strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type ExcelApi struct{}
// /excel/importExcel 接口,与upload接口作用类似,只是把文件存到resource/excel目录下,用于导入Excel时存放Excel文件(ExcelImport.xlsx)
// /excel/loadExcel接口,用于读取resource/excel目录下的文件((ExcelImport.xlsx)并加载为[]model.SysBaseMenu类型的示例数据
// /excel/exportExcel 接口,用于读取前端传来的tableData,生成Excel文件并返回
// /excel/downloadTemplate 接口,用于下载resource/excel目录下的 ExcelTemplate.xlsx 文件,作为导入的模板
// ExportExcel
// @Tags excel
// @Summary 导出Excel
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/octet-stream
// @Param data body example.ExcelInfo true "导出Excel文件信息"
// @Success 200
// @Router /excel/exportExcel [post]
func (e *ExcelApi) ExportExcel(c *gin.Context) {
var excelInfo example.ExcelInfo
err := c.ShouldBindJSON(&excelInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if strings.Index(excelInfo.FileName, "..") > -1 {
response.FailWithMessage("包含非法字符", c)
return
}
filePath := global.GVA_CONFIG.Excel.Dir + excelInfo.FileName
err = excelService.ParseInfoList2Excel(excelInfo.InfoList, filePath)
if err != nil {
global.GVA_LOG.Error("转换Excel失败!", zap.Error(err))
response.FailWithMessage("转换Excel失败", c)
return
}
c.Writer.Header().Add("success", "true")
c.File(filePath)
}
// ImportExcel
// @Tags excel
// @Summary 导入Excel文件
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "导入Excel文件"
// @Success 200 {object} response.Response{msg=string} "导入Excel文件"
// @Router /excel/importExcel [post]
func (e *ExcelApi) ImportExcel(c *gin.Context) {
_, header, err := c.Request.FormFile("file")
if err != nil {
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
response.FailWithMessage("接收文件失败", c)
return
}
_ = c.SaveUploadedFile(header, global.GVA_CONFIG.Excel.Dir+"ExcelImport.xlsx")
response.OkWithMessage("导入成功", c)
}
// LoadExcel
// @Tags excel
// @Summary 加载Excel数据
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "加载Excel数据,返回包括列表,总数,页码,每页数量"
// @Router /excel/loadExcel [get]
func (e *ExcelApi) LoadExcel(c *gin.Context) {
menus, err := excelService.ParseExcel2InfoList()
if err != nil {
global.GVA_LOG.Error("加载数据失败!", zap.Error(err))
response.FailWithMessage("加载数据失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: menus,
Total: int64(len(menus)),
Page: 1,
PageSize: 999,
}, "加载数据成功", c)
}
// DownloadTemplate
// @Tags excel
// @Summary 下载模板
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param fileName query string true "模板名称"
// @Success 200
// @Router /excel/downloadTemplate [get]
func (e *ExcelApi) DownloadTemplate(c *gin.Context) {
fileName := c.Query("fileName")
filePath := global.GVA_CONFIG.Excel.Dir + fileName
fi, err := os.Stat(filePath)
if err != nil {
global.GVA_LOG.Error("文件不存在!", zap.Error(err))
response.FailWithMessage("文件不存在", c)
return
}
if fi.IsDir() {
global.GVA_LOG.Error("不支持下载文件夹!", zap.Error(err))
response.FailWithMessage("不支持下载文件夹", c)
return
}
c.Writer.Header().Add("success", "true")
c.File(filePath)
}
+2
View File
@@ -17,6 +17,7 @@ type ApiGroup struct {
AutoCodeHistoryApi
DictionaryDetailApi
AuthorityBtnApi
ChatGptApi
}
var (
@@ -35,4 +36,5 @@ var (
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistoryService
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
chatGptService = service.ServiceGroupApp.SystemServiceGroup.ChatGptService
)
+17
View File
@@ -212,3 +212,20 @@ func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
}
response.OkWithMessage("删除成功", c)
}
// FreshCasbin
// @Tags SysApi
// @Summary 刷新casbin缓存
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "刷新成功"
// @Router /api/freshCasbin [get]
func (s *SystemApiApi) FreshCasbin(c *gin.Context) {
err := apiService.FreshCasbin()
if err != nil {
global.GVA_LOG.Error("刷新失败!", zap.Error(err))
response.FailWithMessage("刷新失败", c)
return
}
response.OkWithMessage("刷新成功", c)
}
@@ -22,6 +22,10 @@ type AuthorityBtnApi struct{}
func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) {
var req request.SysAuthorityBtnReq
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
res, err := authorityBtnService.GetAuthorityBtn(req)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
+41 -11
View File
@@ -11,8 +11,6 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
@@ -20,8 +18,6 @@ import (
type AutoCodeApi struct{}
var caser = cases.Title(language.English)
// PreviewTemp
// @Tags AutoCode
// @Summary 预览创建后的代码
@@ -39,7 +35,7 @@ func (autoApi *AutoCodeApi) PreviewTemp(c *gin.Context) {
return
}
a.Pretreatment() // 处理go关键字
a.PackageT = caser.String(a.Package)
a.PackageT = utils.FirstUpper(a.Package)
autoCode, err := autoCodeService.PreviewTemp(a)
if err != nil {
global.GVA_LOG.Error("预览失败!", zap.Error(err))
@@ -77,7 +73,7 @@ func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
apiIds = ids
}
}
a.PackageT = caser.String(a.Package)
a.PackageT = utils.FirstUpper(a.Package)
err := autoCodeService.CreateTemp(a, apiIds...)
if err != nil {
if errors.Is(err, system.ErrAutoMove) {
@@ -106,12 +102,22 @@ func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前所有数据库"
// @Router /autoCode/getDatabase [get]
func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
dbs, err := autoCodeService.Database().GetDB()
businessDB := c.Query("businessDB")
dbs, err := autoCodeService.Database(businessDB).GetDB(businessDB)
var dbList []map[string]interface{}
for _, db := range global.GVA_CONFIG.DBList {
var item = make(map[string]interface{})
item["aliasName"] = db.AliasName
item["dbName"] = db.Dbname
item["disable"] = db.Disable
item["dbtype"] = db.Type
dbList = append(dbList, item)
}
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
response.OkWithDetailed(gin.H{"dbs": dbs}, "获取成功", c)
response.OkWithDetailed(gin.H{"dbs": dbs, "dbList": dbList}, "获取成功", c)
}
}
@@ -125,7 +131,8 @@ func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
// @Router /autoCode/getTables [get]
func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
tables, err := autoCodeService.Database().GetTables(dbName)
businessDB := c.Query("businessDB")
tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName)
if err != nil {
global.GVA_LOG.Error("查询table失败!", zap.Error(err))
response.FailWithMessage("查询table失败", c)
@@ -143,9 +150,10 @@ func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前表所有字段"
// @Router /autoCode/getColumn [get]
func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
businessDB := c.Query("businessDB")
dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
tableName := c.Query("tableName")
columns, err := autoCodeService.Database().GetColumn(tableName, dbName)
columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
@@ -172,6 +180,7 @@ func (autoApi *AutoCodeApi) CreatePackage(c *gin.Context) {
}
err := autoCodeService.CreateAutoCode(&a)
if err != nil {
global.GVA_LOG.Error("创建成功!", zap.Error(err))
response.FailWithMessage("创建失败", c)
} else {
@@ -253,7 +262,7 @@ func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
// @Produce application/json
// @Param plug formData file true "this is a test file"
// @Success 200 {object} response.Response{data=[]interface{},msg=string} "安装插件成功"
// @Router /autoCode/createPlug [post]
// @Router /autoCode/installPlugin [post]
func (autoApi *AutoCodeApi) InstallPlugin(c *gin.Context) {
header, err := c.FormFile("plug")
if err != nil {
@@ -283,3 +292,24 @@ func (autoApi *AutoCodeApi) InstallPlugin(c *gin.Context) {
"msg": serverStr,
}}, c)
}
// PubPlug
// @Tags AutoCode
// @Summary 打包插件
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAutoCode true "打包插件"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
// @Router /autoCode/pubPlug [get]
func (autoApi *AutoCodeApi) PubPlug(c *gin.Context) {
plugName := c.Query("plugName")
snake := strings.ToLower(plugName)
zipPath, err := autoCodeService.PubPlug(snake)
if err != nil {
global.GVA_LOG.Error("打包失败!", zap.Error(err))
response.FailWithMessage("打包失败"+err.Error(), c)
return
}
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
}
+28 -1
View File
@@ -1,6 +1,8 @@
package system
import (
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
@@ -21,9 +23,22 @@ type BaseApi struct{}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度"
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码"
// @Router /base/captcha [post]
func (b *BaseApi) Captcha(c *gin.Context) {
// 判断验证码是否开启
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
key := c.ClientIP()
v, ok := global.BlackCache.Get(key)
if !ok {
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
}
var oc bool
if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
oc = true
}
// 字符,公式,验证码配置
// 生成默认数字的driver
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
@@ -39,5 +54,17 @@ func (b *BaseApi) Captcha(c *gin.Context) {
CaptchaId: id,
PicPath: b64s,
CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
OpenCaptcha: oc,
}, "验证码获取成功", c)
}
// 类型转换
func interfaceToInt(v interface{}) (i int) {
switch v := v.(type) {
case int:
i = v
default:
i = 0
}
return
}
+71
View File
@@ -0,0 +1,71 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type ChatGptApi struct{}
func (chat *ChatGptApi) CreateSK(c *gin.Context) {
var option sysModel.SysChatGptOption
c.ShouldBindJSON(&option)
err := chatGptService.CreateSK(option)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败"+err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
}
func (chat *ChatGptApi) GetSK(c *gin.Context) {
var option sysModel.SysChatGptOption
c.ShouldBindJSON(&option)
_, err := chatGptService.GetSK()
if err != nil {
response.OkWithDetailed(gin.H{
"ok": false,
}, "无sk或获取失败", c)
return
}
response.OkWithDetailed(gin.H{
"ok": true,
}, "获取成功", c)
}
func (chat *ChatGptApi) DeleteSK(c *gin.Context) {
err := chatGptService.DeleteSK()
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
func (chat *ChatGptApi) GetTable(c *gin.Context) {
var req request.ChatGptRequest
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
sql, results, err := chatGptService.GetTable(req)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithDetailed(gin.H{
"sql": sql,
"results": results,
}, "生成失败"+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{
"sql": sql,
"results": results,
}, "ChatGpt生成完成", c)
}
+1
View File
@@ -28,6 +28,7 @@ func (a *AuthorityMenuApi) GetMenu(c *gin.Context) {
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
if menus == nil {
menus = []system.SysMenu{}
+28 -8
View File
@@ -2,6 +2,7 @@ package system
import (
"strconv"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
@@ -12,7 +13,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
@@ -26,6 +27,8 @@ import (
func (b *BaseApi) Login(c *gin.Context) {
var l systemReq.Login
err := c.ShouldBindJSON(&l)
key := c.ClientIP()
if err != nil {
response.FailWithMessage(err.Error(), c)
return
@@ -35,22 +38,39 @@ func (b *BaseApi) Login(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
if store.Verify(l.CaptchaId, l.Captcha, true) {
// 判断验证码是否开启
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
v, ok := global.BlackCache.Get(key)
if !ok {
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
}
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
if !oc || store.Verify(l.CaptchaId, l.Captcha, true) {
u := &system.SysUser{Username: l.Username, Password: l.Password}
user, err := userService.Login(u)
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户名不存在或者密码错误", c)
return
}
if user.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
return
}
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("验证码错误", c)
}
@@ -74,7 +94,7 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
return
}
@@ -88,7 +108,7 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
} else if err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
@@ -107,7 +127,7 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
}
}
@@ -137,7 +157,7 @@ func (b *BaseApi) Register(c *gin.Context) {
AuthorityId: v,
})
}
user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable}
user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable, Phone: r.Phone, Email: r.Email}
userReturn, err := userService.Register(*user)
if err != nil {
global.GVA_LOG.Error("注册失败!", zap.Error(err))
@@ -248,7 +268,7 @@ func (b *BaseApi) SetUserAuthority(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
} else {
c.Header("new-token", token)
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt, 10))
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
response.OkWithMessage("修改成功", c)
}
}
@@ -379,7 +399,7 @@ func (b *BaseApi) SetSelfInfo(c *gin.Context) {
return
}
user.ID = utils.GetUserID(c)
err = userService.SetUserInfo(system.SysUser{
err = userService.SetSelfInfo(system.SysUser{
GVA_MODEL: global.GVA_MODEL{
ID: user.ID,
},
+77 -77
View File
@@ -2,45 +2,43 @@
# jwt configuration
jwt:
signing-key: 'qmPlus'
expires-time: 604800
buffer-time: 86400
issuer: 'qmPlus'
signing-key: qmPlus
expires-time: 7d
buffer-time: 1d
issuer: qmPlus
# zap logger configuration
zap:
level: 'info'
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'
format: 'console'
director: 'log'
encode-level: 'LowercaseColorLevelEncoder'
stacktrace-key: 'stacktrace'
max-age: 30 # 默认日志留存默认以天为单位
level: info
format: console
prefix: "[github.com/flipped-aurora/gin-vue-admin/server]"
director: log
show-line: true
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
log-in-console: true
# redis configuration
redis:
db: 0
addr: '177.7.0.14:6379'
password: ''
addr: 177.7.0.14:6379
password: ""
# email configuration
email:
to: 'xxx@qq.com'
to: xxx@qq.com
port: 465
from: 'xxx@163.com'
host: 'smtp.163.com'
from: xxx@163.com
host: smtp.163.com
is-ssl: true
secret: 'xxx'
nickname: 'test'
secret: xxx
nickname: test
# system configuration
system:
env: 'public' # Change to "develop" to skip authentication for development mode
env: public # Change to "develop" to skip authentication for development mode
addr: 8888
db-type: 'mysql'
oss-type: 'local' # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-multipoint: false
# IP限制次数 一个小时15000次
@@ -53,16 +51,18 @@ captcha:
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
open-captcha-timeout: 3600 # open-captcha大于0时才生效
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
mysql:
path: ''
port: ''
config: ''
db-name: ''
username: ''
password: ''
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
@@ -71,27 +71,27 @@ mysql:
# pgsql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
pgsql:
path: ''
port: ''
config: ''
db-name: ''
username: ''
password: ''
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
db-list:
- disabled: true # 是否启用
- disable: true # 是否禁用
type: "" # 数据库的类型,目前支持mysql、pgsql
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
path: ''
port: ''
config: ''
db-name: ''
username: ''
password: ''
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
@@ -100,8 +100,8 @@ db-list:
# local configuration
local:
path: 'uploads/file' # 访问路径
store-path: 'uploads/file' # 存储路径
path: uploads/file
store-path: uploads/file
# autocode configuration
autocode:
@@ -124,70 +124,70 @@ autocode:
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
qiniu:
zone: 'ZoneHuaDong'
bucket: ''
img-path: ''
zone: ZoneHuaDong
bucket: ""
img-path: ""
use-https: false
access-key: ''
secret-key: ''
access-key: ""
secret-key: ""
use-cdn-domains: false
# aliyun oss configuration
aliyun-oss:
endpoint: 'yourEndpoint'
access-key-id: 'yourAccessKeyId'
access-key-secret: 'yourAccessKeySecret'
bucket-name: 'yourBucketName'
bucket-url: 'yourBucketUrl'
base-path: 'yourBasePath'
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
bucket-url: yourBucketUrl
base-path: yourBasePath
# tencent cos configuration
tencent-cos:
bucket: 'xxxxx-10005608'
region: 'ap-shanghai'
secret-id: 'xxxxxxxx'
secret-key: 'xxxxxxxx'
base-url: 'https://gin.vue.admin'
path-prefix: 'github.com/flipped-aurora/gin-vue-admin/server'
bucket: xxxxx-10005608
region: ap-shanghai
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# aws s3 configuration (minio compatible)
aws-s3:
bucket: xxxxx-10005608
region: ap-shanghai
endpoint: ''
endpoint: ""
s3-force-path-style: false
disable-ssl: false
secret-id: xxxxxxxx
secret-key: xxxxxxxx
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# huawei obs configuration
hua-wei-obs:
path: 'you-path'
bucket: 'you-bucket'
endpoint: 'you-endpoint'
access-key: 'you-access-key'
secret-key: 'you-secret-key'
path: you-path
bucket: you-bucket
endpoint: you-endpoint
access-key: you-access-key
secret-key: you-secret-key
# excel configuration
excel:
dir: './resource/excel/'
dir: ./resource/excel/
# timer task db clear table
Timer:
start: true
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
detail:
- tableName: "sys_operation_records"
compareField: "created_at"
interval: "2160h"
- tableName: "jwt_blacklists"
compareField: "created_at"
interval: "168h"
- tableName: sys_operation_records
compareField: created_at
interval: 2160h
- tableName: jwt_blacklists
compareField: created_at
interval: 168h
# 跨域配置
# 需要配合 server/initialize/router.go#L32 使用
# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用
cors:
mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
@@ -200,4 +200,4 @@ cors:
allow-headers: content-type
allow-methods: GET, POST
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true # 布尔值
allow-credentials: true # 布尔值
+49 -13
View File
@@ -10,7 +10,7 @@ jwt:
zap:
level: info
format: console
prefix: [github.com/flipped-aurora/gin-vue-admin/server]
prefix: "[github.com/flipped-aurora/gin-vue-admin/server]"
director: log
show-line: true
encode-level: LowercaseColorLevelEncoder
@@ -35,22 +35,26 @@ email:
# system configuration
system:
env: public # Change to "develop" to skip authentication for development mode
env: public # Change to "develop" to skip authentication for development mode
addr: 8888
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-multipoint: false
# IP限制次数 一个小时15000次
iplimit-count: 15000
# IP限制一个小时
iplimit-time: 3600
# 路由全局前缀
router-prefix: ""
# captcha configuration
captcha:
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
open-captcha-timeout: 3600 # open-captcha大于0时才生效
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
@@ -79,10 +83,42 @@ pgsql:
max-open-conns: 100
log-mode: ""
log-zap: false
oracle:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
mssql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
sqlite:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
db-list:
- disabled: true # 是否启用
type: "" # 数据库的类型,目前支持mysql、pgsql
- disable: true # 是否禁用
type: "" # 数据库的类型,目前支持mysql、pgsql、mssql、oracle
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
path: ""
port: ""
@@ -95,7 +131,6 @@ db-list:
log-mode: ""
log-zap: false
# local configuration
local:
path: uploads/file
@@ -175,7 +210,7 @@ excel:
# timer task db clear table
Timer:
start: true
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
detail:
- tableName: sys_operation_records
compareField: created_at
@@ -185,14 +220,15 @@ Timer:
interval: 168h
# 跨域配置
# 需要配合 server/initialize/router.go#L32 使用
# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用
cors:
mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
mode: strict-whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
- allow-origin: example1.com
allow-headers: content-type
allow-methods: GET, POST
allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id
allow-methods: POST, GET
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true # 布尔值
- allow-origin: example2.com
allow-headers: content-type
+5 -3
View File
@@ -1,7 +1,9 @@
package config
type Captcha struct {
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
OpenCaptchaTimeOut int `mapstructure:"open-captcha-timeout" json:"open-captcha-timeout" yaml:"open-captcha-timeout"` // 防爆破验证码超时时间,单位:s(秒)
}
+3
View File
@@ -11,7 +11,10 @@ type Server struct {
AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"`
// gorm
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
Mssql Mssql `mapstructure:"mssql" json:"mssql" yaml:"mssql"`
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
Oracle Oracle `mapstructure:"oracle" json:"oracle" yaml:"oracle"`
Sqlite Sqlite `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"`
DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
// oss
Local Local `mapstructure:"local" json:"local" yaml:"local"`
+3
View File
@@ -15,6 +15,9 @@ type GeneralDB struct {
Dbname string `mapstructure:"db-name" json:"db-name" yaml:"db-name"` // 数据库名
Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` //全局表前缀,单独定义TableName则不生效
Singular bool `mapstructure:"singular" json:"singular" yaml:"singular"` //是否开启全局禁用复数,true表示开启
Engine string `mapstructure:"engine" json:"engine" yaml:"engine" default:"InnoDB"` //数据库引擎,默认InnoDB
MaxIdleConns int `mapstructure:"max-idle-conns" json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
MaxOpenConns int `mapstructure:"max-open-conns" json:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
LogMode string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"` // 是否开启Gorm全局日志
+13
View File
@@ -0,0 +1,13 @@
package config
type Mssql struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
//dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
func (m *Mssql) Dsn() string {
return "sqlserver://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "?database=" + m.Dbname + "&encrypt=disable"
}
func (m *Mssql) GetLogMode() string {
return m.LogMode
}
+14
View File
@@ -0,0 +1,14 @@
package config
type Oracle struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
func (m *Oracle) Dsn() string {
return "oracle://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "/" + m.Dbname + "?" + m.Config
}
func (m *Oracle) GetLogMode() string {
return m.LogMode
}
+17
View File
@@ -0,0 +1,17 @@
package config
import (
"path/filepath"
)
type Sqlite struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
func (s *Sqlite) Dsn() string {
return filepath.Join(s.Path, s.Dbname+".db")
}
func (s *Sqlite) GetLogMode() string {
return s.LogMode
}
+1
View File
@@ -9,4 +9,5 @@ type System struct {
UseRedis bool `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"` // 使用redis
LimitCountIP int `mapstructure:"iplimit-count" json:"iplimit-count" yaml:"iplimit-count"`
LimitTimeIP int `mapstructure:"iplimit-time" json:"iplimit-time" yaml:"iplimit-time"`
RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"`
}
+1 -1
View File
@@ -37,7 +37,7 @@ func RunWindowsServer() {
fmt.Printf(`
欢迎使用 gin-vue-admin
当前版本:v2.5.4
当前版本:v2.5.7
加群方式:微信号:shouzi_1994 QQ群:622360840
插件市场:https://plugin.gin-vue-admin.com
GVA讨论社区:https://support.qq.com/products/371961
+355 -313
View File
@@ -1,22 +1,14 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag
// Code generated by swaggo/swag. DO NOT EDIT.
package docs
import (
"bytes"
"encoding/json"
"strings"
import "github.com/swaggo/swag"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
)
var doc = `{
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{.Description}}",
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
@@ -174,6 +166,40 @@ var doc = `{
}
}
},
"/api/freshCasbin": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SysApi"
],
"summary": "刷新casbin缓存",
"responses": {
"200": {
"description": "刷新成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"msg": {
"type": "string"
}
}
}
]
}
}
}
}
},
"/api/getAllApis": {
"post": {
"security": [
@@ -1475,7 +1501,7 @@ var doc = `{
"summary": "生成验证码",
"responses": {
"200": {
"description": "生成验证码,返回包括随机数id,base64,验证码长度",
"description": "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码",
"schema": {
"allOf": [
{
@@ -1997,161 +2023,6 @@ var doc = `{
}
}
},
"/excel/downloadTemplate": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"excel"
],
"summary": "下载模板",
"parameters": [
{
"type": "string",
"description": "模板名称",
"name": "fileName",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/excel/exportExcel": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/octet-stream"
],
"tags": [
"excel"
],
"summary": "导出Excel",
"parameters": [
{
"description": "导出Excel文件信息",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/example.ExcelInfo"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/excel/importExcel": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"excel"
],
"summary": "导入Excel文件",
"parameters": [
{
"type": "file",
"description": "导入Excel文件",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "导入Excel文件",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"msg": {
"type": "string"
}
}
}
]
}
}
}
}
},
"/excel/loadExcel": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"produces": [
"application/json"
],
"tags": [
"excel"
],
"summary": "加载Excel数据",
"responses": {
"200": {
"description": "加载Excel数据,返回包括列表,总数,页码,每页数量",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/response.PageResult"
},
"msg": {
"type": "string"
}
}
}
]
}
}
}
}
},
"/fileUploadAndDownload/breakpointContinue": {
"post": {
"security": [
@@ -4940,6 +4811,14 @@ var doc = `{
"key-long": {
"description": "验证码长度",
"type": "integer"
},
"open-captcha": {
"description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码",
"type": "integer"
},
"open-captcha-timeout": {
"description": "防爆破验证码超时时间,单位:s(秒)",
"type": "integer"
}
}
},
@@ -4960,39 +4839,6 @@ var doc = `{
}
}
},
"config.Email": {
"type": "object",
"properties": {
"from": {
"description": "收件人",
"type": "string"
},
"host": {
"description": "服务器地址",
"type": "string"
},
"is-ssl": {
"description": "是否SSL",
"type": "boolean"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"port": {
"description": "端口",
"type": "integer"
},
"secret": {
"description": "密钥",
"type": "string"
},
"to": {
"description": "收件人:多个以英文逗号分隔",
"type": "string"
}
}
},
"config.Excel": {
"type": "object",
"properties": {
@@ -5055,7 +4901,7 @@ var doc = `{
}
}
},
"config.Mysql": {
"config.Mssql": {
"type": "object",
"properties": {
"config": {
@@ -5066,6 +4912,11 @@ var doc = `{
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
@@ -5094,6 +4945,130 @@ var doc = `{
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
}
}
},
"config.Mysql": {
"type": "object",
"properties": {
"config": {
"description": "高级配置",
"type": "string"
},
"db-name": {
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
},
"log-zap": {
"description": "是否通过zap写入日志文件",
"type": "boolean"
},
"max-idle-conns": {
"description": "空闲中的最大连接数",
"type": "integer"
},
"max-open-conns": {
"description": "打开到数据库的最大连接数",
"type": "integer"
},
"password": {
"description": "数据库密码",
"type": "string"
},
"path": {
"description": "服务器地址:端口",
"type": "string"
},
"port": {
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
}
}
},
"config.Oracle": {
"type": "object",
"properties": {
"config": {
"description": "高级配置",
"type": "string"
},
"db-name": {
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
},
"log-zap": {
"description": "是否通过zap写入日志文件",
"type": "boolean"
},
"max-idle-conns": {
"description": "空闲中的最大连接数",
"type": "integer"
},
"max-open-conns": {
"description": "打开到数据库的最大连接数",
"type": "integer"
},
"password": {
"description": "数据库密码",
"type": "string"
},
"path": {
"description": "服务器地址:端口",
"type": "string"
},
"port": {
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
@@ -5111,6 +5086,11 @@ var doc = `{
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
@@ -5139,6 +5119,14 @@ var doc = `{
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
@@ -5203,7 +5191,11 @@ var doc = `{
},
"autocode": {
"description": "auto",
"$ref": "#/definitions/config.Autocode"
"allOf": [
{
"$ref": "#/definitions/config.Autocode"
}
]
},
"aws-s3": {
"$ref": "#/definitions/config.AwsS3"
@@ -5213,7 +5205,11 @@ var doc = `{
},
"cors": {
"description": "跨域配置",
"$ref": "#/definitions/config.CORS"
"allOf": [
{
"$ref": "#/definitions/config.CORS"
}
]
},
"db-list": {
"type": "array",
@@ -5222,7 +5218,7 @@ var doc = `{
}
},
"email": {
"$ref": "#/definitions/config.Email"
"$ref": "#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email"
},
"excel": {
"$ref": "#/definitions/config.Excel"
@@ -5235,11 +5231,25 @@ var doc = `{
},
"local": {
"description": "oss",
"$ref": "#/definitions/config.Local"
"allOf": [
{
"$ref": "#/definitions/config.Local"
}
]
},
"mssql": {
"$ref": "#/definitions/config.Mssql"
},
"mysql": {
"description": "gorm",
"$ref": "#/definitions/config.Mysql"
"allOf": [
{
"$ref": "#/definitions/config.Mysql"
}
]
},
"oracle": {
"$ref": "#/definitions/config.Oracle"
},
"pgsql": {
"$ref": "#/definitions/config.Pgsql"
@@ -5281,6 +5291,11 @@ var doc = `{
"disable": {
"type": "boolean"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
@@ -5309,6 +5324,14 @@ var doc = `{
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"type": {
"type": "string"
},
@@ -5343,6 +5366,9 @@ var doc = `{
"description": "Oss类型",
"type": "string"
},
"router-prefix": {
"type": "string"
},
"use-multipoint": {
"description": "多点登录拦截",
"type": "boolean"
@@ -5461,7 +5487,11 @@ var doc = `{
},
"sysUser": {
"description": "管理详情",
"$ref": "#/definitions/system.SysUser"
"allOf": [
{
"$ref": "#/definitions/system.SysUser"
}
]
},
"sysUserAuthorityID": {
"description": "管理角色ID",
@@ -5574,18 +5604,36 @@ var doc = `{
}
}
},
"example.ExcelInfo": {
"github_com_flipped-aurora_gin-vue-admin_server_config.Email": {
"type": "object",
"properties": {
"fileName": {
"description": "文件名",
"from": {
"description": "收件人",
"type": "string"
},
"infoList": {
"type": "array",
"items": {
"$ref": "#/definitions/system.SysBaseMenu"
}
"host": {
"description": "服务器地址",
"type": "string"
},
"is-ssl": {
"description": "是否SSL",
"type": "boolean"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"port": {
"description": "端口",
"type": "integer"
},
"secret": {
"description": "密钥",
"type": "string"
},
"to": {
"description": "收件人:多个以英文逗号分隔",
"type": "string"
}
}
},
@@ -5752,28 +5800,40 @@ var doc = `{
"type": "object",
"properties": {
"authorityId": {
"type": "integer"
"type": "string",
"example": "int 角色id"
},
"authorityIds": {
"type": "array",
"items": {
"type": "integer"
}
"type": "string",
"example": "[]uint 角色id"
},
"email": {
"type": "string",
"example": "电子邮箱"
},
"enable": {
"type": "integer"
"type": "string",
"example": "int 是否启用"
},
"headerImg": {
"type": "string"
"type": "string",
"example": "头像链接"
},
"nickName": {
"type": "string"
"type": "string",
"example": "昵称"
},
"passWord": {
"type": "string"
"type": "string",
"example": "密码"
},
"phone": {
"type": "string",
"example": "电话号码"
},
"userName": {
"type": "string"
"type": "string",
"example": "用户名"
}
}
},
@@ -5967,9 +6027,7 @@ var doc = `{
"response.PageResult": {
"type": "object",
"properties": {
"list": {
"type": "object"
},
"list": {},
"page": {
"type": "integer"
},
@@ -5998,9 +6056,7 @@ var doc = `{
"code": {
"type": "integer"
},
"data": {
"type": "object"
},
"data": {},
"msg": {
"type": "string"
}
@@ -6084,6 +6140,9 @@ var doc = `{
"captchaLength": {
"type": "integer"
},
"openCaptcha": {
"type": "boolean"
},
"picPath": {
"type": "string"
}
@@ -6135,6 +6194,10 @@ var doc = `{
"description": "是否自动移动文件",
"type": "boolean"
},
"businessDB": {
"description": "业务数据库",
"type": "string"
},
"description": {
"description": "Struct中文名称",
"type": "string"
@@ -6219,6 +6282,38 @@ var doc = `{
"require": {
"description": "是否必填",
"type": "boolean"
},
"sort": {
"description": "是否增加排序",
"type": "boolean"
}
}
},
"system.Meta": {
"type": "object",
"properties": {
"activeName": {
"type": "string"
},
"closeTab": {
"description": "自动关闭tab",
"type": "boolean"
},
"defaultMenu": {
"description": "是否是基础路由(开发中)",
"type": "boolean"
},
"icon": {
"description": "菜单图标",
"type": "string"
},
"keepAlive": {
"description": "是否缓存",
"type": "boolean"
},
"title": {
"description": "菜单名",
"type": "string"
}
}
},
@@ -6346,10 +6441,6 @@ var doc = `{
"$ref": "#/definitions/system.SysBaseMenu"
}
},
"closeTab": {
"description": "自动关闭tab",
"type": "boolean"
},
"component": {
"description": "对应前端文件路径",
"type": "string"
@@ -6358,32 +6449,28 @@ var doc = `{
"description": "创建时间",
"type": "string"
},
"defaultMenu": {
"description": "是否是基础路由(开发中)",
"type": "boolean"
},
"hidden": {
"description": "是否在列表隐藏",
"type": "boolean"
},
"icon": {
"description": "菜单图标",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"keepAlive": {
"description": "是否缓存",
"type": "boolean"
},
"menuBtn": {
"type": "array",
"items": {
"$ref": "#/definitions/system.SysBaseMenuBtn"
}
},
"meta": {
"description": "附加属性",
"allOf": [
{
"$ref": "#/definitions/system.Meta"
}
]
},
"name": {
"description": "路由name",
"type": "string"
@@ -6406,10 +6493,6 @@ var doc = `{
"description": "排序标记",
"type": "integer"
},
"title": {
"description": "菜单名",
"type": "string"
},
"updatedAt": {
"description": "更新时间",
"type": "string"
@@ -6571,10 +6654,6 @@ var doc = `{
"$ref": "#/definitions/system.SysMenu"
}
},
"closeTab": {
"description": "自动关闭tab",
"type": "boolean"
},
"component": {
"description": "对应前端文件路径",
"type": "string"
@@ -6583,26 +6662,14 @@ var doc = `{
"description": "创建时间",
"type": "string"
},
"defaultMenu": {
"description": "是否是基础路由(开发中)",
"type": "boolean"
},
"hidden": {
"description": "是否在列表隐藏",
"type": "boolean"
},
"icon": {
"description": "菜单图标",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"keepAlive": {
"description": "是否缓存",
"type": "boolean"
},
"menuBtn": {
"type": "array",
"items": {
@@ -6612,6 +6679,14 @@ var doc = `{
"menuId": {
"type": "string"
},
"meta": {
"description": "附加属性",
"allOf": [
{
"$ref": "#/definitions/system.Meta"
}
]
},
"name": {
"description": "路由name",
"type": "string"
@@ -6634,10 +6709,6 @@ var doc = `{
"description": "排序标记",
"type": "integer"
},
"title": {
"description": "菜单名",
"type": "string"
},
"updatedAt": {
"description": "更新时间",
"type": "string"
@@ -6792,49 +6863,20 @@ var doc = `{
}
}`
type swaggerInfo struct {
Version string
Host string
BasePath string
Schemes []string
Title string
Description string
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{
Version: "0.0.1",
Host: "",
BasePath: "/",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample Server pets",
}
type s struct{}
func (s *s) ReadDoc() string {
sInfo := SwaggerInfo
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}).Parse(doc)
if err != nil {
return doc
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, sInfo); err != nil {
return doc
}
return tpl.String()
var SwaggerInfo = &swag.Spec{
Version: "0.0.1",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample Server pets",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(swag.Name, &s{})
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}
+400 -261
View File
@@ -6,7 +6,6 @@
"contact": {},
"version": "0.0.1"
},
"basePath": "/",
"paths": {
"/api/createApi": {
"post": {
@@ -158,6 +157,40 @@
}
}
},
"/api/freshCasbin": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"SysApi"
],
"summary": "刷新casbin缓存",
"responses": {
"200": {
"description": "刷新成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"msg": {
"type": "string"
}
}
}
]
}
}
}
}
},
"/api/getAllApis": {
"post": {
"security": [
@@ -1459,7 +1492,7 @@
"summary": "生成验证码",
"responses": {
"200": {
"description": "生成验证码,返回包括随机数id,base64,验证码长度",
"description": "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码",
"schema": {
"allOf": [
{
@@ -1981,161 +2014,6 @@
}
}
},
"/excel/downloadTemplate": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"excel"
],
"summary": "下载模板",
"parameters": [
{
"type": "string",
"description": "模板名称",
"name": "fileName",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/excel/exportExcel": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/octet-stream"
],
"tags": [
"excel"
],
"summary": "导出Excel",
"parameters": [
{
"description": "导出Excel文件信息",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/example.ExcelInfo"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/excel/importExcel": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"excel"
],
"summary": "导入Excel文件",
"parameters": [
{
"type": "file",
"description": "导入Excel文件",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "导入Excel文件",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"msg": {
"type": "string"
}
}
}
]
}
}
}
}
},
"/excel/loadExcel": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"produces": [
"application/json"
],
"tags": [
"excel"
],
"summary": "加载Excel数据",
"responses": {
"200": {
"description": "加载Excel数据,返回包括列表,总数,页码,每页数量",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/response.PageResult"
},
"msg": {
"type": "string"
}
}
}
]
}
}
}
}
},
"/fileUploadAndDownload/breakpointContinue": {
"post": {
"security": [
@@ -4924,6 +4802,14 @@
"key-long": {
"description": "验证码长度",
"type": "integer"
},
"open-captcha": {
"description": "防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码",
"type": "integer"
},
"open-captcha-timeout": {
"description": "防爆破验证码超时时间,单位:s(秒)",
"type": "integer"
}
}
},
@@ -4944,39 +4830,6 @@
}
}
},
"config.Email": {
"type": "object",
"properties": {
"from": {
"description": "收件人",
"type": "string"
},
"host": {
"description": "服务器地址",
"type": "string"
},
"is-ssl": {
"description": "是否SSL",
"type": "boolean"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"port": {
"description": "端口",
"type": "integer"
},
"secret": {
"description": "密钥",
"type": "string"
},
"to": {
"description": "收件人:多个以英文逗号分隔",
"type": "string"
}
}
},
"config.Excel": {
"type": "object",
"properties": {
@@ -5039,7 +4892,7 @@
}
}
},
"config.Mysql": {
"config.Mssql": {
"type": "object",
"properties": {
"config": {
@@ -5050,6 +4903,11 @@
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
@@ -5078,6 +4936,130 @@
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
}
}
},
"config.Mysql": {
"type": "object",
"properties": {
"config": {
"description": "高级配置",
"type": "string"
},
"db-name": {
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
},
"log-zap": {
"description": "是否通过zap写入日志文件",
"type": "boolean"
},
"max-idle-conns": {
"description": "空闲中的最大连接数",
"type": "integer"
},
"max-open-conns": {
"description": "打开到数据库的最大连接数",
"type": "integer"
},
"password": {
"description": "数据库密码",
"type": "string"
},
"path": {
"description": "服务器地址:端口",
"type": "string"
},
"port": {
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
}
}
},
"config.Oracle": {
"type": "object",
"properties": {
"config": {
"description": "高级配置",
"type": "string"
},
"db-name": {
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
},
"log-zap": {
"description": "是否通过zap写入日志文件",
"type": "boolean"
},
"max-idle-conns": {
"description": "空闲中的最大连接数",
"type": "integer"
},
"max-open-conns": {
"description": "打开到数据库的最大连接数",
"type": "integer"
},
"password": {
"description": "数据库密码",
"type": "string"
},
"path": {
"description": "服务器地址:端口",
"type": "string"
},
"port": {
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
@@ -5095,6 +5077,11 @@
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
@@ -5123,6 +5110,72 @@
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
}
}
},
"config.Sqlite": {
"type": "object",
"properties": {
"config": {
"description": "高级配置",
"type": "string"
},
"db-name": {
"description": "数据库名",
"type": "string"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
},
"log-zap": {
"description": "是否通过zap写入日志文件",
"type": "boolean"
},
"max-idle-conns": {
"description": "空闲中的最大连接数",
"type": "integer"
},
"max-open-conns": {
"description": "打开到数据库的最大连接数",
"type": "integer"
},
"password": {
"description": "数据库密码",
"type": "string"
},
"path": {
"description": "服务器地址:端口",
"type": "string"
},
"port": {
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"username": {
"description": "数据库用户名",
"type": "string"
@@ -5187,7 +5240,11 @@
},
"autocode": {
"description": "auto",
"$ref": "#/definitions/config.Autocode"
"allOf": [
{
"$ref": "#/definitions/config.Autocode"
}
]
},
"aws-s3": {
"$ref": "#/definitions/config.AwsS3"
@@ -5197,7 +5254,11 @@
},
"cors": {
"description": "跨域配置",
"$ref": "#/definitions/config.CORS"
"allOf": [
{
"$ref": "#/definitions/config.CORS"
}
]
},
"db-list": {
"type": "array",
@@ -5206,7 +5267,7 @@
}
},
"email": {
"$ref": "#/definitions/config.Email"
"$ref": "#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email"
},
"excel": {
"$ref": "#/definitions/config.Excel"
@@ -5219,15 +5280,32 @@
},
"local": {
"description": "oss",
"$ref": "#/definitions/config.Local"
"allOf": [
{
"$ref": "#/definitions/config.Local"
}
]
},
"mssql": {
"$ref": "#/definitions/config.Mssql"
},
"mysql": {
"description": "gorm",
"$ref": "#/definitions/config.Mysql"
"allOf": [
{
"$ref": "#/definitions/config.Mysql"
}
]
},
"oracle": {
"$ref": "#/definitions/config.Oracle"
},
"pgsql": {
"$ref": "#/definitions/config.Pgsql"
},
"sqlite": {
"$ref": "#/definitions/config.Sqlite"
},
"qiniu": {
"$ref": "#/definitions/config.Qiniu"
},
@@ -5265,6 +5343,11 @@
"disable": {
"type": "boolean"
},
"engine": {
"description": "数据库引擎,默认InnoDB",
"type": "string",
"default": "InnoDB"
},
"log-mode": {
"description": "是否开启Gorm全局日志",
"type": "string"
@@ -5293,6 +5376,14 @@
"description": ":端口",
"type": "string"
},
"prefix": {
"description": "全局表前缀,单独定义TableName则不生效",
"type": "string"
},
"singular": {
"description": "是否开启全局禁用复数,true表示开启",
"type": "boolean"
},
"type": {
"type": "string"
},
@@ -5327,6 +5418,9 @@
"description": "Oss类型",
"type": "string"
},
"router-prefix": {
"type": "string"
},
"use-multipoint": {
"description": "多点登录拦截",
"type": "boolean"
@@ -5445,7 +5539,11 @@
},
"sysUser": {
"description": "管理详情",
"$ref": "#/definitions/system.SysUser"
"allOf": [
{
"$ref": "#/definitions/system.SysUser"
}
]
},
"sysUserAuthorityID": {
"description": "管理角色ID",
@@ -5558,18 +5656,36 @@
}
}
},
"example.ExcelInfo": {
"github_com_flipped-aurora_gin-vue-admin_server_config.Email": {
"type": "object",
"properties": {
"fileName": {
"description": "文件名",
"from": {
"description": "收件人",
"type": "string"
},
"infoList": {
"type": "array",
"items": {
"$ref": "#/definitions/system.SysBaseMenu"
}
"host": {
"description": "服务器地址",
"type": "string"
},
"is-ssl": {
"description": "是否SSL",
"type": "boolean"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"port": {
"description": "端口",
"type": "integer"
},
"secret": {
"description": "密钥",
"type": "string"
},
"to": {
"description": "收件人:多个以英文逗号分隔",
"type": "string"
}
}
},
@@ -5736,28 +5852,40 @@
"type": "object",
"properties": {
"authorityId": {
"type": "integer"
"type": "string",
"example": "int 角色id"
},
"authorityIds": {
"type": "array",
"items": {
"type": "integer"
}
"type": "string",
"example": "[]uint 角色id"
},
"email": {
"type": "string",
"example": "电子邮箱"
},
"enable": {
"type": "integer"
"type": "string",
"example": "int 是否启用"
},
"headerImg": {
"type": "string"
"type": "string",
"example": "头像链接"
},
"nickName": {
"type": "string"
"type": "string",
"example": "昵称"
},
"passWord": {
"type": "string"
"type": "string",
"example": "密码"
},
"phone": {
"type": "string",
"example": "电话号码"
},
"userName": {
"type": "string"
"type": "string",
"example": "用户名"
}
}
},
@@ -5951,9 +6079,7 @@
"response.PageResult": {
"type": "object",
"properties": {
"list": {
"type": "object"
},
"list": {},
"page": {
"type": "integer"
},
@@ -5982,9 +6108,7 @@
"code": {
"type": "integer"
},
"data": {
"type": "object"
},
"data": {},
"msg": {
"type": "string"
}
@@ -6068,6 +6192,9 @@
"captchaLength": {
"type": "integer"
},
"openCaptcha": {
"type": "boolean"
},
"picPath": {
"type": "string"
}
@@ -6119,6 +6246,10 @@
"description": "是否自动移动文件",
"type": "boolean"
},
"businessDB": {
"description": "业务数据库",
"type": "string"
},
"description": {
"description": "Struct中文名称",
"type": "string"
@@ -6203,6 +6334,38 @@
"require": {
"description": "是否必填",
"type": "boolean"
},
"sort": {
"description": "是否增加排序",
"type": "boolean"
}
}
},
"system.Meta": {
"type": "object",
"properties": {
"activeName": {
"type": "string"
},
"closeTab": {
"description": "自动关闭tab",
"type": "boolean"
},
"defaultMenu": {
"description": "是否是基础路由(开发中)",
"type": "boolean"
},
"icon": {
"description": "菜单图标",
"type": "string"
},
"keepAlive": {
"description": "是否缓存",
"type": "boolean"
},
"title": {
"description": "菜单名",
"type": "string"
}
}
},
@@ -6330,10 +6493,6 @@
"$ref": "#/definitions/system.SysBaseMenu"
}
},
"closeTab": {
"description": "自动关闭tab",
"type": "boolean"
},
"component": {
"description": "对应前端文件路径",
"type": "string"
@@ -6342,32 +6501,28 @@
"description": "创建时间",
"type": "string"
},
"defaultMenu": {
"description": "是否是基础路由(开发中)",
"type": "boolean"
},
"hidden": {
"description": "是否在列表隐藏",
"type": "boolean"
},
"icon": {
"description": "菜单图标",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"keepAlive": {
"description": "是否缓存",
"type": "boolean"
},
"menuBtn": {
"type": "array",
"items": {
"$ref": "#/definitions/system.SysBaseMenuBtn"
}
},
"meta": {
"description": "附加属性",
"allOf": [
{
"$ref": "#/definitions/system.Meta"
}
]
},
"name": {
"description": "路由name",
"type": "string"
@@ -6390,10 +6545,6 @@
"description": "排序标记",
"type": "integer"
},
"title": {
"description": "菜单名",
"type": "string"
},
"updatedAt": {
"description": "更新时间",
"type": "string"
@@ -6555,10 +6706,6 @@
"$ref": "#/definitions/system.SysMenu"
}
},
"closeTab": {
"description": "自动关闭tab",
"type": "boolean"
},
"component": {
"description": "对应前端文件路径",
"type": "string"
@@ -6567,26 +6714,14 @@
"description": "创建时间",
"type": "string"
},
"defaultMenu": {
"description": "是否是基础路由(开发中)",
"type": "boolean"
},
"hidden": {
"description": "是否在列表隐藏",
"type": "boolean"
},
"icon": {
"description": "菜单图标",
"type": "string"
},
"id": {
"description": "主键ID",
"type": "integer"
},
"keepAlive": {
"description": "是否缓存",
"type": "boolean"
},
"menuBtn": {
"type": "array",
"items": {
@@ -6596,6 +6731,14 @@
"menuId": {
"type": "string"
},
"meta": {
"description": "附加属性",
"allOf": [
{
"$ref": "#/definitions/system.Meta"
}
]
},
"name": {
"description": "路由name",
"type": "string"
@@ -6618,10 +6761,6 @@
"description": "排序标记",
"type": "integer"
},
"title": {
"description": "菜单名",
"type": "string"
},
"updatedAt": {
"description": "更新时间",
"type": "string"
+235 -168
View File
@@ -1,4 +1,3 @@
basePath: /
definitions:
config.AliyunOSS:
properties:
@@ -100,6 +99,12 @@ definitions:
key-long:
description: 验证码长度
type: integer
open-captcha:
description: 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
type: integer
open-captcha-timeout:
description: 防爆破验证码超时时间,单位:s(秒)
type: integer
type: object
config.Detail:
properties:
@@ -113,30 +118,6 @@ definitions:
description: 需要清理的表名
type: string
type: object
config.Email:
properties:
from:
description: 收件人
type: string
host:
description: 服务器地址
type: string
is-ssl:
description: 是否SSL
type: boolean
nickname:
description: 昵称
type: string
port:
description: 端口
type: integer
secret:
description: 密钥
type: string
to:
description: 收件人:多个以英文逗号分隔
type: string
type: object
config.Excel:
properties:
dir:
@@ -179,7 +160,7 @@ definitions:
description: 本地文件存储路径
type: string
type: object
config.Mysql:
config.Mssql:
properties:
config:
description: 高级配置
@@ -187,6 +168,10 @@ definitions:
db-name:
description: 数据库名
type: string
engine:
default: InnoDB
description: 数据库引擎,默认InnoDB
type: string
log-mode:
description: 是否开启Gorm全局日志
type: string
@@ -208,6 +193,98 @@ definitions:
port:
description: :端口
type: string
prefix:
description: 全局表前缀,单独定义TableName则不生效
type: string
singular:
description: 是否开启全局禁用复数,true表示开启
type: boolean
username:
description: 数据库用户名
type: string
type: object
config.Mysql:
properties:
config:
description: 高级配置
type: string
db-name:
description: 数据库名
type: string
engine:
default: InnoDB
description: 数据库引擎,默认InnoDB
type: string
log-mode:
description: 是否开启Gorm全局日志
type: string
log-zap:
description: 是否通过zap写入日志文件
type: boolean
max-idle-conns:
description: 空闲中的最大连接数
type: integer
max-open-conns:
description: 打开到数据库的最大连接数
type: integer
password:
description: 数据库密码
type: string
path:
description: 服务器地址:端口
type: string
port:
description: :端口
type: string
prefix:
description: 全局表前缀,单独定义TableName则不生效
type: string
singular:
description: 是否开启全局禁用复数,true表示开启
type: boolean
username:
description: 数据库用户名
type: string
type: object
config.Oracle:
properties:
config:
description: 高级配置
type: string
db-name:
description: 数据库名
type: string
engine:
default: InnoDB
description: 数据库引擎,默认InnoDB
type: string
log-mode:
description: 是否开启Gorm全局日志
type: string
log-zap:
description: 是否通过zap写入日志文件
type: boolean
max-idle-conns:
description: 空闲中的最大连接数
type: integer
max-open-conns:
description: 打开到数据库的最大连接数
type: integer
password:
description: 数据库密码
type: string
path:
description: 服务器地址:端口
type: string
port:
description: :端口
type: string
prefix:
description: 全局表前缀,单独定义TableName则不生效
type: string
singular:
description: 是否开启全局禁用复数,true表示开启
type: boolean
username:
description: 数据库用户名
type: string
@@ -220,6 +297,10 @@ definitions:
db-name:
description: 数据库名
type: string
engine:
default: InnoDB
description: 数据库引擎,默认InnoDB
type: string
log-mode:
description: 是否开启Gorm全局日志
type: string
@@ -241,6 +322,12 @@ definitions:
port:
description: :端口
type: string
prefix:
description: 全局表前缀,单独定义TableName则不生效
type: string
singular:
description: 是否开启全局禁用复数,true表示开启
type: boolean
username:
description: 数据库用户名
type: string
@@ -286,21 +373,23 @@ definitions:
aliyun-oss:
$ref: '#/definitions/config.AliyunOSS'
autocode:
$ref: '#/definitions/config.Autocode'
allOf:
- $ref: '#/definitions/config.Autocode'
description: auto
aws-s3:
$ref: '#/definitions/config.AwsS3'
captcha:
$ref: '#/definitions/config.Captcha'
cors:
$ref: '#/definitions/config.CORS'
allOf:
- $ref: '#/definitions/config.CORS'
description: 跨域配置
db-list:
items:
$ref: '#/definitions/config.SpecializedDB'
type: array
email:
$ref: '#/definitions/config.Email'
$ref: '#/definitions/github_com_flipped-aurora_gin-vue-admin_server_config.Email'
excel:
$ref: '#/definitions/config.Excel'
hua-wei-obs:
@@ -308,11 +397,17 @@ definitions:
jwt:
$ref: '#/definitions/config.JWT'
local:
$ref: '#/definitions/config.Local'
allOf:
- $ref: '#/definitions/config.Local'
description: oss
mssql:
$ref: '#/definitions/config.Mssql'
mysql:
$ref: '#/definitions/config.Mysql'
allOf:
- $ref: '#/definitions/config.Mysql'
description: gorm
oracle:
$ref: '#/definitions/config.Oracle'
pgsql:
$ref: '#/definitions/config.Pgsql'
qiniu:
@@ -340,6 +435,10 @@ definitions:
type: string
disable:
type: boolean
engine:
default: InnoDB
description: 数据库引擎,默认InnoDB
type: string
log-mode:
description: 是否开启Gorm全局日志
type: string
@@ -361,6 +460,12 @@ definitions:
port:
description: :端口
type: string
prefix:
description: 全局表前缀,单独定义TableName则不生效
type: string
singular:
description: 是否开启全局禁用复数,true表示开启
type: boolean
type:
type: string
username:
@@ -385,6 +490,8 @@ definitions:
oss-type:
description: Oss类型
type: string
router-prefix:
type: string
use-multipoint:
description: 多点登录拦截
type: boolean
@@ -468,7 +575,8 @@ definitions:
description: 主键ID
type: integer
sysUser:
$ref: '#/definitions/system.SysUser'
allOf:
- $ref: '#/definitions/system.SysUser'
description: 管理详情
sysUserAuthorityID:
description: 管理角色ID
@@ -548,15 +656,29 @@ definitions:
description: 文件地址
type: string
type: object
example.ExcelInfo:
github_com_flipped-aurora_gin-vue-admin_server_config.Email:
properties:
fileName:
description: 文件名
from:
description: 收件人
type: string
host:
description: 服务器地址
type: string
is-ssl:
description: 是否SSL
type: boolean
nickname:
description: 昵称
type: string
port:
description: 端口
type: integer
secret:
description: 密钥
type: string
to:
description: 收件人:多个以英文逗号分隔
type: string
infoList:
items:
$ref: '#/definitions/system.SysBaseMenu'
type: array
type: object
request.AddMenuAuthorityInfo:
properties:
@@ -671,20 +793,31 @@ definitions:
request.Register:
properties:
authorityId:
type: integer
example: int 角色id
type: string
authorityIds:
items:
type: integer
type: array
example: '[]uint 角色id'
type: string
email:
example: 电子邮箱
type: string
enable:
type: integer
example: int 是否启用
type: string
headerImg:
example: 头像链接
type: string
nickName:
example: 昵称
type: string
passWord:
example: 密码
type: string
phone:
example: 电话号码
type: string
userName:
example: 用户名
type: string
type: object
request.RollBack:
@@ -817,8 +950,7 @@ definitions:
type: object
response.PageResult:
properties:
list:
type: object
list: {}
page:
type: integer
pageSize:
@@ -837,8 +969,7 @@ definitions:
properties:
code:
type: integer
data:
type: object
data: {}
msg:
type: string
type: object
@@ -892,6 +1023,8 @@ definitions:
type: string
captchaLength:
type: integer
openCaptcha:
type: boolean
picPath:
type: string
type: object
@@ -926,6 +1059,9 @@ definitions:
autoMoveFile:
description: 是否自动移动文件
type: boolean
businessDB:
description: 业务数据库
type: string
description:
description: Struct中文名称
type: string
@@ -988,6 +1124,29 @@ definitions:
require:
description: 是否必填
type: boolean
sort:
description: 是否增加排序
type: boolean
type: object
system.Meta:
properties:
activeName:
type: string
closeTab:
description: 自动关闭tab
type: boolean
defaultMenu:
description: 是否是基础路由(开发中)
type: boolean
icon:
description: 菜单图标
type: string
keepAlive:
description: 是否缓存
type: boolean
title:
description: 菜单名
type: string
type: object
system.SysApi:
properties:
@@ -1076,34 +1235,26 @@ definitions:
items:
$ref: '#/definitions/system.SysBaseMenu'
type: array
closeTab:
description: 自动关闭tab
type: boolean
component:
description: 对应前端文件路径
type: string
createdAt:
description: 创建时间
type: string
defaultMenu:
description: 是否是基础路由(开发中)
type: boolean
hidden:
description: 是否在列表隐藏
type: boolean
icon:
description: 菜单图标
type: string
id:
description: 主键ID
type: integer
keepAlive:
description: 是否缓存
type: boolean
menuBtn:
items:
$ref: '#/definitions/system.SysBaseMenuBtn'
type: array
meta:
allOf:
- $ref: '#/definitions/system.Meta'
description: 附加属性
name:
description: 路由name
type: string
@@ -1120,9 +1271,6 @@ definitions:
sort:
description: 排序标记
type: integer
title:
description: 菜单名
type: string
updatedAt:
description: 更新时间
type: string
@@ -1237,36 +1385,28 @@ definitions:
items:
$ref: '#/definitions/system.SysMenu'
type: array
closeTab:
description: 自动关闭tab
type: boolean
component:
description: 对应前端文件路径
type: string
createdAt:
description: 创建时间
type: string
defaultMenu:
description: 是否是基础路由(开发中)
type: boolean
hidden:
description: 是否在列表隐藏
type: boolean
icon:
description: 菜单图标
type: string
id:
description: 主键ID
type: integer
keepAlive:
description: 是否缓存
type: boolean
menuBtn:
items:
$ref: '#/definitions/system.SysBaseMenuBtn'
type: array
menuId:
type: string
meta:
allOf:
- $ref: '#/definitions/system.Meta'
description: 附加属性
name:
description: 路由name
type: string
@@ -1283,9 +1423,6 @@ definitions:
sort:
description: 排序标记
type: integer
title:
description: 菜单名
type: string
updatedAt:
description: 更新时间
type: string
@@ -1480,6 +1617,25 @@ paths:
summary: 删除选中Api
tags:
- SysApi
/api/freshCasbin:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 刷新成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
summary: 刷新casbin缓存
tags:
- SysApi
/api/getAllApis:
post:
consumes:
@@ -2212,7 +2368,7 @@ paths:
- application/json
responses:
"200":
description: 生成验证码,返回包括随机数id,base64,验证码长度
description: 生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码
schema:
allOf:
- $ref: '#/definitions/response.Response'
@@ -2515,95 +2671,6 @@ paths:
summary: 发送邮件
tags:
- System
/excel/downloadTemplate:
get:
consumes:
- multipart/form-data
parameters:
- description: 模板名称
in: query
name: fileName
required: true
type: string
produces:
- application/json
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: 下载模板
tags:
- excel
/excel/exportExcel:
post:
consumes:
- application/json
parameters:
- description: 导出Excel文件信息
in: body
name: data
required: true
schema:
$ref: '#/definitions/example.ExcelInfo'
produces:
- application/octet-stream
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: 导出Excel
tags:
- excel
/excel/importExcel:
post:
consumes:
- multipart/form-data
parameters:
- description: 导入Excel文件
in: formData
name: file
required: true
type: file
produces:
- application/json
responses:
"200":
description: 导入Excel文件
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 导入Excel文件
tags:
- excel
/excel/loadExcel:
get:
produces:
- application/json
responses:
"200":
description: 加载Excel数据,返回包括列表,总数,页码,每页数量
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.PageResult'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 加载Excel数据
tags:
- excel
/fileUploadAndDownload/breakpointContinue:
post:
consumes:
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/go-redis/redis/v8"
"github.com/redis/go-redis/v9"
"github.com/spf13/viper"
"gorm.io/gorm"
)
+115 -43
View File
@@ -1,59 +1,131 @@
module github.com/flipped-aurora/gin-vue-admin/server
go 1.16
go 1.18
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible
github.com/aws/aws-sdk-go v1.42.27
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/casbin/casbin/v2 v2.51.0
github.com/casbin/gorm-adapter/v3 v3.7.3
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
github.com/aws/aws-sdk-go v1.44.307
github.com/casbin/casbin/v2 v2.71.1
github.com/casbin/gorm-adapter/v3 v3.18.0
github.com/flipped-aurora/ws v1.0.2
github.com/fsnotify/fsnotify v1.4.9
github.com/fsnotify/fsnotify v1.6.0
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
github.com/gin-gonic/gin v1.7.0
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.3 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-redis/redis/v8 v8.11.4
github.com/go-sql-driver/mysql v1.6.0
github.com/golang-jwt/jwt/v4 v4.3.0
github.com/gookit/color v1.3.1
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.8.0
github.com/go-sql-driver/mysql v1.7.1
github.com/gofrs/uuid/v5 v5.0.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/gookit/color v1.5.4
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible
github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/lestrrat-go/strftime v1.0.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.2.2 // indirect
github.com/mojocn/base64Captcha v1.3.1
github.com/mojocn/base64Captcha v1.3.5
github.com/otiai10/copy v1.7.0
github.com/pelletier/go-toml v1.6.0 // indirect
github.com/pkg/errors v0.9.1
github.com/qiniu/api.v7/v7 v7.4.1
github.com/redis/go-redis/v9 v9.0.5
github.com/robfig/cron/v3 v3.0.1
github.com/satori/go.uuid v1.2.0
github.com/shirou/gopsutil/v3 v3.22.5
github.com/songzhibin97/gkit v1.2.7
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/sashabaranov/go-openai v1.14.1
github.com/shirou/gopsutil/v3 v3.23.6
github.com/songzhibin97/gkit v1.2.11
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.16.1
github.com/tencentyun/cos-go-sdk-v5 v0.7.42
github.com/unrolled/secure v1.13.0
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.10.0
golang.org/x/sync v0.3.0
golang.org/x/text v0.11.0
gorm.io/driver/mysql v1.5.1
gorm.io/driver/postgres v1.5.2
gorm.io/driver/sqlserver v1.5.1
gorm.io/gorm v1.25.2
nhooyr.io/websocket v1.8.7
)
require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.3.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lestrrat-go/strftime v1.0.6 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/microsoft/go-mssqldb v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.7.1
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.7.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.19
github.com/unrolled/secure v1.0.7
github.com/xuri/excelize/v2 v2.4.1
go.uber.org/zap v1.16.0
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.7
golang.org/x/tools v0.1.5 // indirect
gopkg.in/ini.v1 v1.55.0 // indirect
gorm.io/driver/mysql v1.3.3
gorm.io/driver/postgres v1.3.4
gorm.io/gorm v1.23.4
nhooyr.io/websocket v1.8.6
github.com/subosito/gotenv v1.4.2 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/image v0.5.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.9.1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/plugin/dbresolver v1.4.1 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/sqlite v1.23.0 // indirect
)
+477 -663
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -17,8 +17,12 @@ func DBList() {
switch info.Type {
case "mysql":
dbMap[info.AliasName] = GormMysqlByConfig(config.Mysql{GeneralDB: info.GeneralDB})
case "mssql":
dbMap[info.AliasName] = GormMssqlByConfig(config.Mssql{GeneralDB: info.GeneralDB})
case "pgsql":
dbMap[info.AliasName] = GormPgSqlByConfig(config.Pgsql{GeneralDB: info.GeneralDB})
case "oracle":
dbMap[info.AliasName] = GormOracleByConfig(config.Oracle{GeneralDB: info.GeneralDB})
default:
continue
}
+2 -1
View File
@@ -48,6 +48,7 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
sysModel.SysBaseMenuBtn{},
sysModel.SysAuthorityBtn{},
sysModel.SysAutoCode{},
sysModel.SysChatGptOption{},
adapter.CasbinRule{},
@@ -83,7 +84,7 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
sysModel.SysBaseMenuBtn{},
sysModel.SysAuthorityBtn{},
sysModel.SysAutoCode{},
sysModel.SysChatGptOption{},
adapter.CasbinRule{},
example.ExaFile{},
+9 -7
View File
@@ -19,6 +19,12 @@ func Gorm() *gorm.DB {
return GormMysql()
case "pgsql":
return GormPgSql()
case "oracle":
return GormOracle()
case "mssql":
return GormMssql()
case "sqlite":
return GormSqlite()
default:
return GormMysql()
}
@@ -26,7 +32,8 @@ func Gorm() *gorm.DB {
// RegisterTables 注册数据库表专用
// Author SliverHorn
func RegisterTables(db *gorm.DB) {
func RegisterTables() {
db := global.GVA_DB
err := db.AutoMigrate(
// 系统模块表
system.SysApi{},
@@ -42,17 +49,12 @@ func RegisterTables(db *gorm.DB) {
system.SysBaseMenuBtn{},
system.SysAuthorityBtn{},
system.SysAutoCode{},
system.SysChatGptOption{},
// 示例模块表
example.ExaFile{},
example.ExaCustomer{},
example.ExaFileChunk{},
example.ExaFileUploadAndDownload{},
// 自动化模块表
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
)
if err != nil {
global.GVA_LOG.Error("register table failed", zap.Error(err))
+59
View File
@@ -0,0 +1,59 @@
/*
* @Author: 逆光飞翔 191180776@qq.com
* @Date: 2022-12-08 17:25:49
* @LastEditors: 逆光飞翔 191180776@qq.com
* @LastEditTime: 2022-12-08 18:00:00
* @FilePath: \server\initialize\gorm_mssql.go
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
package initialize
import (
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
"gorm.io/driver/sqlserver"
"gorm.io/gorm"
)
// GormMssql 初始化Mssql数据库
// Author [LouisZhang](191180776@qq.com)
func GormMssql() *gorm.DB {
m := global.GVA_CONFIG.Mssql
if m.Dbname == "" {
return nil
}
mssqlConfig := sqlserver.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
return nil
} else {
db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine)
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
return db
}
}
// GormMssqlByConfig 初始化Mysql数据库用过传入配置
func GormMssqlByConfig(m config.Mssql) *gorm.DB {
if m.Dbname == "" {
return nil
}
mssqlConfig := sqlserver.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
panic(err)
} else {
db.InstanceSet("gorm:table_options", "ENGINE=InnoDB")
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
return db
}
}
+5 -2
View File
@@ -4,6 +4,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
@@ -21,9 +22,10 @@ func GormMysql() *gorm.DB {
DefaultStringSize: 191, // string 类型字段的默认长度
SkipInitializeWithVersion: false, // 根据版本自动配置
}
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config()); err != nil {
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
return nil
} else {
db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine)
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
@@ -41,9 +43,10 @@ func GormMysqlByConfig(m config.Mysql) *gorm.DB {
DefaultStringSize: 191, // string 类型字段的默认长度
SkipInitializeWithVersion: false, // 根据版本自动配置
}
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config()); err != nil {
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
panic(err)
} else {
db.InstanceSet("gorm:table_options", "ENGINE=InnoDB")
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
+52
View File
@@ -0,0 +1,52 @@
package initialize
import (
//"github.com/dzwvip/oracle"
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
//_ "github.com/godror/godror"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// GormOracle 初始化oracle数据库
// 如果需要Oracle库 放开import里的注释 把下方 mysql.Config 改为 oracle.Config ; mysql.New 改为 oracle.New
func GormOracle() *gorm.DB {
m := global.GVA_CONFIG.Oracle
if m.Dbname == "" {
return nil
}
oracleConfig := mysql.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(mysql.New(oracleConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
return db
}
}
// GormOracleByConfig 初始化Oracle数据库用过传入配置
func GormOracleByConfig(m config.Oracle) *gorm.DB {
if m.Dbname == "" {
return nil
}
oracleConfig := mysql.Config{
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
}
if db, err := gorm.Open(mysql.New(oracleConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
return db
}
}
+2 -2
View File
@@ -20,7 +20,7 @@ func GormPgSql() *gorm.DB {
DSN: p.Dsn(), // DSN data source name
PreferSimpleProtocol: false,
}
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config()); err != nil {
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil {
return nil
} else {
sqlDB, _ := db.DB()
@@ -39,7 +39,7 @@ func GormPgSqlByConfig(p config.Pgsql) *gorm.DB {
DSN: p.Dsn(), // DSN data source name
PreferSimpleProtocol: false,
}
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config()); err != nil {
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
+42
View File
@@ -0,0 +1,42 @@
package initialize
import (
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
// GormSqlite 初始化Sqlite数据库
func GormSqlite() *gorm.DB {
s := global.GVA_CONFIG.Sqlite
if s.Dbname == "" {
return nil
}
if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(s.MaxIdleConns)
sqlDB.SetMaxOpenConns(s.MaxOpenConns)
return db
}
}
// GormSqliteByConfig 初始化Sqlite数据库用过传入配置
func GormSqliteByConfig(s config.Sqlite) *gorm.DB {
if s.Dbname == "" {
return nil
}
if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
panic(err)
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(s.MaxIdleConns)
sqlDB.SetMaxOpenConns(s.MaxOpenConns)
return db
}
}
+12 -3
View File
@@ -1,6 +1,7 @@
package internal
import (
"gorm.io/gorm/schema"
"log"
"os"
"time"
@@ -20,9 +21,15 @@ type _gorm struct{}
// Config gorm 自定义配置
// Author [SliverHorn](https://github.com/SliverHorn)
func (g *_gorm) Config() *gorm.Config {
config := &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}
_default := logger.New(NewWriter(log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{
func (g *_gorm) Config(prefix string, singular bool) *gorm.Config {
config := &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: prefix,
SingularTable: singular,
},
DisableForeignKeyConstraintWhenMigrating: true,
}
_default := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: logger.Warn,
Colorful: true,
@@ -33,6 +40,8 @@ func (g *_gorm) Config() *gorm.Config {
logMode = &global.GVA_CONFIG.Mysql
case "pgsql":
logMode = &global.GVA_CONFIG.Pgsql
case "oracle":
logMode = &global.GVA_CONFIG.Oracle
default:
logMode = &global.GVA_CONFIG.Mysql
}
+1
View File
@@ -2,6 +2,7 @@ package initialize
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email"
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/go-redis/redis/v8"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
+12 -15
View File
@@ -3,12 +3,12 @@ package initialize
import (
"net/http"
_ "github.com/flipped-aurora/gin-vue-admin/server/docs"
"github.com/flipped-aurora/gin-vue-admin/server/docs"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/flipped-aurora/gin-vue-admin/server/router"
"github.com/gin-gonic/gin"
"github.com/swaggo/gin-swagger"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
)
@@ -16,6 +16,7 @@ import (
func Routers() *gin.Engine {
Router := gin.Default()
InstallPlugin(Router) // 安装插件
systemRouter := router.RouterGroupApp.System
exampleRouter := router.RouterGroupApp.Example
// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
@@ -27,31 +28,32 @@ func Routers() *gin.Engine {
// Router.Static("/static", "./dist/assets") // dist里面的静态资源
// Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
Router.StaticFS(global.GVA_CONFIG.Local.Path, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址
// Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
// 跨域,如需跨域可以打开下面的注释
// Router.Use(middleware.Cors()) // 直接放行全部跨域请求
//Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
// Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
//global.GVA_LOG.Info("use middleware cors")
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix
Router.GET(global.GVA_CONFIG.System.RouterPrefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
global.GVA_LOG.Info("register swagger handler")
// 方便统一添加路由组前缀 多服务器上线使用
PublicGroup := Router.Group("")
PublicGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
{
// 健康监测
PublicGroup.GET("/health", func(c *gin.Context) {
c.JSON(200, "ok")
c.JSON(http.StatusOK, "ok")
})
}
{
systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关
}
PrivateGroup := Router.Group("")
PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
{
systemRouter.InitApiRouter(PrivateGroup) // 注册功能api路由
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
@@ -64,18 +66,13 @@ func Routers() *gin.Engine {
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 字典详情管理
systemRouter.InitChatGptRouter(PrivateGroup) // chatGpt接口
exampleRouter.InitExcelRouter(PrivateGroup) // 表格导入导出
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
}
InstallPlugin(Router) // 安装插件
global.GVA_LOG.Info("router register success")
return Router
}
+1 -1
View File
@@ -29,7 +29,7 @@ func main() {
initialize.Timer()
initialize.DBList()
if global.GVA_DB != nil {
initialize.RegisterTables(global.GVA_DB) // 初始化表
initialize.RegisterTables() // 初始化表
// 程序结束前关闭数据库链接
db, _ := global.GVA_DB.DB()
defer db.Close()
+21 -17
View File
@@ -1,34 +1,38 @@
package middleware
import (
"strconv"
"strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"strconv"
)
var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
// 拦截器
// CasbinHandler 拦截器
func CasbinHandler() gin.HandlerFunc {
return func(c *gin.Context) {
waitUse, _ := utils.GetClaims(c)
// 获取请求的PATH
obj := c.Request.URL.Path
// 获取请求方法
act := c.Request.Method
// 获取用户的角色
sub := strconv.Itoa(int(waitUse.AuthorityId))
e := casbinService.Casbin() // 判断策略中是否存在
success, _ := e.Enforce(sub, obj, act)
if global.GVA_CONFIG.System.Env == "develop" || success {
c.Next()
} else {
response.FailWithDetailed(gin.H{}, "权限不足", c)
c.Abort()
return
if global.GVA_CONFIG.System.Env != "develop" {
waitUse, _ := utils.GetClaims(c)
//获取请求的PATH
path := c.Request.URL.Path
obj := strings.TrimPrefix(path, global.GVA_CONFIG.System.RouterPrefix)
// 获取请求方法
act := c.Request.Method
// 获取用户的角色
sub := strconv.Itoa(int(waitUse.AuthorityId))
e := casbinService.Casbin() // 判断策略中是否存在
success, _ := e.Enforce(sub, obj, act)
if !success {
response.FailWithDetailed(gin.H{}, "权限不足", c)
c.Abort()
return
}
}
c.Next()
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ func CorsByRules() gin.HandlerFunc {
c.AbortWithStatus(http.StatusForbidden)
} else {
// 非严格白名单模式,无论是否通过检查均放行所有 OPTIONS 方法
if c.Request.Method == "OPTIONS" {
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
}
}
+6 -4
View File
@@ -1,6 +1,8 @@
package middleware
import (
"errors"
"github.com/golang-jwt/jwt/v4"
"strconv"
"time"
@@ -35,7 +37,7 @@ func JWTAuth() gin.HandlerFunc {
// parseToken 解析token包含的信息
claims, err := j.ParseToken(token)
if err != nil {
if err == utils.TokenExpired {
if errors.Is(err, utils.TokenExpired) {
response.FailWithDetailed(gin.H{"reload": true}, "授权已过期", c)
c.Abort()
return
@@ -53,13 +55,13 @@ func JWTAuth() gin.HandlerFunc {
// response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
// c.Abort()
//}
if claims.ExpiresAt-time.Now().Unix() < claims.BufferTime {
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
dr, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
claims.ExpiresAt = time.Now().Add(dr).Unix()
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr))
newToken, _ := j.CreateTokenByOldToken(token, *claims)
newClaims, _ := j.ParseToken(newToken)
c.Header("new-token", newToken)
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt, 10))
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
if global.GVA_CONFIG.System.UseMultipoint {
RedisJwtToken, err := jwtService.GetRedisJWT(newClaims.Username)
if err != nil {
+4 -2
View File
@@ -64,8 +64,10 @@ func (l Logger) SetLoggerMiddleware() gin.HandlerFunc {
if l.Filter != nil && !l.Filter(c) {
layout.Body = string(body)
}
// 处理鉴权需要的信息
l.AuthProcess(c, &layout)
if l.AuthProcess != nil {
// 处理鉴权需要的信息
l.AuthProcess(c, &layout)
}
if l.FilterKeyword != nil {
// 自行判断key/value 脱敏等
l.FilterKeyword(&layout)
+11 -11
View File
@@ -56,8 +56,8 @@ func OperationRecord() gin.HandlerFunc {
body, _ = json.Marshal(&m)
}
claims, _ := utils.GetClaims(c)
if claims.ID != 0 {
userId = int(claims.ID)
if claims.BaseClaims.ID != 0 {
userId = int(claims.BaseClaims.ID)
} else {
id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
if err != nil {
@@ -75,7 +75,7 @@ func OperationRecord() gin.HandlerFunc {
}
// 上传文件时候 中间件日志进行裁断操作
if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
if len(record.Body) > 1024 {
// 截断
newBody := respPool.Get().([]byte)
@@ -100,20 +100,20 @@ func OperationRecord() gin.HandlerFunc {
record.Latency = latency
record.Resp = writer.body.String()
if strings.Contains(c.Writer.Header().Get("Pragma"), "public") ||
strings.Contains(c.Writer.Header().Get("Expires"), "0") ||
if strings.Contains(c.Writer.Header().Get("Pragma"), "public") ||
strings.Contains(c.Writer.Header().Get("Expires"), "0") ||
strings.Contains(c.Writer.Header().Get("Cache-Control"), "must-revalidate, post-check=0, pre-check=0") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/force-download") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/octet-stream") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/vnd.ms-excel") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/download") ||
strings.Contains(c.Writer.Header().Get("Content-Disposition"), "attachment") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/force-download") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/octet-stream") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/vnd.ms-excel") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/download") ||
strings.Contains(c.Writer.Header().Get("Content-Disposition"), "attachment") ||
strings.Contains(c.Writer.Header().Get("Content-Transfer-Encoding"), "binary") {
if len(record.Resp) > 1024 {
// 截断
newBody := respPool.Get().([]byte)
copy(newBody, record.Resp)
record.Body = string(newBody)
record.Resp = string(newBody)
defer respPool.Put(newBody[:0])
}
}
-8
View File
@@ -1,8 +0,0 @@
package example
import "github.com/flipped-aurora/gin-vue-admin/server/model/system"
type ExcelInfo struct {
FileName string `json:"fileName"` // 文件名
InfoList []system.SysBaseMenu `json:"infoList"`
}
+2 -2
View File
@@ -1,15 +1,15 @@
package request
import (
"github.com/gofrs/uuid/v5"
jwt "github.com/golang-jwt/jwt/v4"
uuid "github.com/satori/go.uuid"
)
// Custom claims structure
type CustomClaims struct {
BaseClaims
BufferTime int64
jwt.StandardClaims
jwt.RegisteredClaims
}
type BaseClaims struct {
@@ -0,0 +1,11 @@
package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
)
type ChatGptRequest struct {
system.ChatGpt
request.PageInfo
}
+31 -6
View File
@@ -7,12 +7,13 @@ import (
)
type InitDB struct {
DBType string `json:"dbType"` // 数据库类型
Host string `json:"host"` // 服务器地址
Port string `json:"port"` // 数据库连接端口
UserName string `json:"userName" binding:"required"` // 数据库用户名
Password string `json:"password"` // 数据库密码
DBName string `json:"dbName" binding:"required"` // 数据库名
DBType string `json:"dbType"` // 数据库类型
Host string `json:"host"` // 服务器地址
Port string `json:"port"` // 数据库连接端口
UserName string `json:"userName"` // 数据库用户名
Password string `json:"password"` // 数据库密码
DBName string `json:"dbName" binding:"required"` // 数据库名
DBPath string `json:"dbPath"` // sqlite数据库文件路径
}
// MysqlEmptyDsn msyql 空数据库 建库链接
@@ -39,6 +40,12 @@ func (i *InitDB) PgsqlEmptyDsn() string {
return "host=" + i.Host + " user=" + i.UserName + " password=" + i.Password + " port=" + i.Port + " dbname=" + "postgres" + " " + "sslmode=disable TimeZone=Asia/Shanghai"
}
// SqliteEmptyDsn sqlite 空数据库 建库链接
// Author Kafumio
func (i *InitDB) SqliteEmptyDsn() string {
return i.DBPath + "\\" + i.DBName + ".db"
}
// ToMysqlConfig 转换 config.Mysql
// Author [SliverHorn](https://github.com/SliverHorn)
func (i *InitDB) ToMysqlConfig() config.Mysql {
@@ -74,3 +81,21 @@ func (i *InitDB) ToPgsqlConfig() config.Pgsql {
},
}
}
// ToSqliteConfig 转换 config.Sqlite
// Author [Kafumio](https://github.com/Kafumio)
func (i *InitDB) ToSqliteConfig() config.Sqlite {
return config.Sqlite{
GeneralDB: config.GeneralDB{
Path: i.DBPath,
Port: i.Port,
Dbname: i.DBName,
Username: i.UserName,
Password: i.Password,
MaxIdleConns: 10,
MaxOpenConns: 100,
LogMode: "error",
Config: "",
},
}
}
+22 -18
View File
@@ -1,16 +1,20 @@
package request
import model "github.com/flipped-aurora/gin-vue-admin/server/model/system"
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
)
// User register structure
// Register User register structure
type Register struct {
Username string `json:"userName"`
Password string `json:"passWord"`
NickName string `json:"nickName" gorm:"default:'QMPlusUser'"`
HeaderImg string `json:"headerImg" gorm:"default:'https://qmplusimg.henrongyi.top/gva_header.jpg'"`
AuthorityId uint `json:"authorityId" gorm:"default:888"`
Enable int `json:"enable"`
AuthorityIds []uint `json:"authorityIds"`
Username string `json:"userName" example:"用户名"`
Password string `json:"passWord" example:"密码"`
NickName string `json:"nickName" example:"昵称"`
HeaderImg string `json:"headerImg" example:"头像链接"`
AuthorityId uint `json:"authorityId" swaggertype:"string" example:"int 角色id"`
Enable int `json:"enable" swaggertype:"string" example:"int 是否启用"`
AuthorityIds []uint `json:"authorityIds" swaggertype:"string" example:"[]uint 角色id"`
Phone string `json:"phone" example:"电话号码"`
Email string `json:"email" example:"电子邮箱"`
}
// User login structure
@@ -40,13 +44,13 @@ type SetUserAuthorities struct {
}
type ChangeUserInfo struct {
ID uint `gorm:"primarykey"` // 主键ID
NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称
Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户手机号
AuthorityIds []uint `json:"authorityIds" gorm:"-"` // 角色ID
Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
SideMode string `json:"sideMode" gorm:"comment:用户侧边主题"` // 用户侧边主题
Enable int `json:"enable" gorm:"comment:冻结用户"` //冻结用户
Authorities []model.SysAuthority `json:"-" gorm:"many2many:sys_user_authority;"`
ID uint `gorm:"primarykey"` // 主键ID
NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称
Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户手机号
AuthorityIds []uint `json:"authorityIds" gorm:"-"` // 角色ID
Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
SideMode string `json:"sideMode" gorm:"comment:用户侧边主题"` // 用户侧边主题
Enable int `json:"enable" gorm:"comment:冻结用户"` //冻结用户
Authorities []system.SysAuthority `json:"-" gorm:"many2many:sys_user_authority;"`
}
@@ -6,6 +6,7 @@ type AutoCodeHistory struct {
ID uint `json:"ID" gorm:"column:id"`
CreatedAt time.Time `json:"CreatedAt" gorm:"column:created_at"`
UpdatedAt time.Time `json:"UpdatedAt" gorm:"column:updated_at"`
BusinessDB string `json:"businessDB" gorm:"column:business_db"`
TableName string `json:"tableName" gorm:"column:table_name"`
StructName string `json:"structName" gorm:"column:struct_name"`
StructCNName string `json:"structCNName" gorm:"column:struct_cn_name"`
+2 -1
View File
@@ -3,5 +3,6 @@ package response
type SysCaptchaResponse struct {
CaptchaId string `json:"captchaId"`
PicPath string `json:"picPath"`
CaptchaLength int `json:"captchaLength""`
CaptchaLength int `json:"captchaLength"`
OpenCaptcha bool `json:"openCaptcha"`
}
@@ -0,0 +1,4 @@
package response
type ChatGptResponse struct {
}
+1 -1
View File
@@ -10,7 +10,7 @@ type SysAuthority struct {
DeletedAt *time.Time `sql:"index"`
AuthorityId uint `json:"authorityId" gorm:"not null;unique;primary_key;comment:角色ID;size:90"` // 角色ID
AuthorityName string `json:"authorityName" gorm:"comment:角色名"` // 角色名
ParentId uint `json:"parentId" gorm:"comment:父角色ID"` // 父角色ID
ParentId *uint `json:"parentId" gorm:"comment:父角色ID"` // 父角色ID
DataAuthorityId []*SysAuthority `json:"dataAuthorityId" gorm:"many2many:sys_data_authority_id;"`
Children []SysAuthority `json:"children" gorm:"-"`
SysBaseMenus []SysBaseMenu `json:"menus" gorm:"many2many:sys_authority_menus;"`
+7
View File
@@ -19,12 +19,18 @@ type AutoCodeStruct struct {
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建api
AutoCreateResource bool `json:"autoCreateResource"` // 是否自动创建资源标识
AutoMoveFile bool `json:"autoMoveFile"` // 是否自动移动文件
BusinessDB string `json:"businessDB"` // 业务数据库
Fields []*Field `json:"fields,omitempty"`
HasTimer bool
DictTypes []string `json:"-"`
Package string `json:"package"`
PackageT string `json:"-"`
NeedValid bool `json:"-"`
NeedSort bool `json:"-"`
HasPic bool `json:"-"`
HasRichText bool `json:"-"`
HasFile bool `json:"-"`
NeedJSON bool `json:"-"`
}
func (a *AutoCodeStruct) Pretreatment() {
@@ -61,6 +67,7 @@ type Field struct {
Require bool `json:"require"` // 是否必填
ErrorText string `json:"errorText"` // 校验失败文字
Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否增加排序
}
var ErrAutoMove error = errors.New("创建代码成功并移动文件成功")
@@ -12,6 +12,7 @@ import (
type SysAutoCodeHistory struct {
global.GVA_MODEL
Package string `json:"package"`
BusinessDB string `json:"businessDB"`
TableName string `json:"tableName"`
RequestMeta string `gorm:"type:text" json:"requestMeta,omitempty"` // 前端传入的结构化信息
AutoCodePath string `gorm:"type:text" json:"autoCodePath,omitempty"` // 其他meta信息 path;path
+18 -17
View File
@@ -6,33 +6,34 @@ import (
type SysBaseMenu struct {
global.GVA_MODEL
MenuLevel uint `json:"-"`
ParentId string `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
MenuLevel uint `json:"-"`
ParentId string `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
}
type Meta struct {
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
ActiveName string `json:"activeName" gorm:"comment:高亮菜单"`
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
DefaultMenu bool `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中)
Title string `json:"title" gorm:"comment:菜单名"` // 菜单名
Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标
CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab
Title string `json:"title" gorm:"comment:菜单名"` // 菜单名
Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标
CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab
}
type SysBaseMenuParameter struct {
global.GVA_MODEL
SysBaseMenuID uint
Type string `json:"type" gorm:"comment:地址栏携带参数为params还是query"` // 地址栏携带参数为params还是query
Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key
Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key
Value string `json:"value" gorm:"comment:地址栏携带参数的值"` // 地址栏携带参数的值
}
+22
View File
@@ -0,0 +1,22 @@
package system
type ChatGpt struct {
DBName string `json:"dbname,omitempty"`
Chat string `json:"chat,omitempty"`
ChatID string `json:"chatID,omitempty"`
}
type SysChatGptOption struct {
SK string `json:"sk"`
}
type ChatField struct {
TABLE_NAME string
COLUMN_NAME string
COLUMN_COMMENT string
}
type ChatFieldNoTable struct {
COLUMN_NAME string
COLUMN_COMMENT string
}
+1 -1
View File
@@ -2,7 +2,7 @@ package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/satori/go.uuid"
"github.com/gofrs/uuid/v5"
)
type SysUser struct {
+1 -1
View File
@@ -43,7 +43,7 @@ func ErrorToEmail(subject string, body string) error {
//@return: error
func EmailTest(subject string, body string) error {
to := []string{global.GlobalConfig.From}
to := []string{global.GlobalConfig.To}
return send(to, subject, body)
}
@@ -54,7 +54,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Create{{.StructName}}(c *gin.Con
return
}
{{- end }}
if err := {{.Abbreviation}}Service.Create{{.StructName}}({{.Abbreviation}}); err != nil {
if err := {{.Abbreviation}}Service.Create{{.StructName}}(&{{.Abbreviation}}); err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
} else {
@@ -108,7 +108,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}ByIds(c *gi
{{- if .AutoCreateResource }}
deletedBy := utils.GetUserID(c)
{{- end }}
if err := {{.Abbreviation}}Service.Delete{{.StructName}}ByIds(IDS,deletedBy); err != nil {
if err := {{.Abbreviation}}Service.Delete{{.StructName}}ByIds(IDS{{- if .AutoCreateResource }},deletedBy{{- end }}); err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
} else {
@@ -4,6 +4,7 @@ package {{.Package}}
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
{{ if .HasTimer }}"time"{{ end }}
{{ if .NeedJSON }}"gorm.io/datatypes"{{ end }}
)
// {{.StructName}} 结构体
@@ -11,6 +12,14 @@ type {{.StructName}} struct {
global.GVA_MODEL {{- range .Fields}}
{{- if eq .FieldType "enum" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};"`
{{- else if eq .FieldType "picture" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"`
{{- else if eq .FieldType "file" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"`
{{- else if eq .FieldType "pictures" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"`
{{- else if eq .FieldType "richtext" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;"`
{{- else if ne .FieldType "string" }}
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"`
{{- else }}
@@ -17,4 +17,8 @@ type {{.StructName}}Search struct{
{{- end }}
{{- end }}
request.PageInfo
{{- if .NeedSort}}
Sort string `json:"sort" form:"sort"`
Order string `json:"order" form:"order"`
{{- end}}
}
@@ -13,10 +13,17 @@ import (
type {{.StructName}}Service struct {
}
{{- $db := "" }}
{{- if eq .BusinessDB "" }}
{{- $db = "global.GVA_DB" }}
{{- else}}
{{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }}
{{- end}}
// Create{{.StructName}} 创建{{.StructName}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
err = global.GVA_DB.Create(&{{.Abbreviation}}).Error
func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{.Abbreviation}} *{{.Package}}.{{.StructName}}) (err error) {
err = {{$db}}.Create({{.Abbreviation}}).Error
return err
}
@@ -24,7 +31,7 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
{{- if .AutoCreateResource }}
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = {{$db}}.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("id = ?", {{.Abbreviation}}.ID).Update("deleted_by", {{.Abbreviation}}.DeletedBy).Error; err != nil {
return err
}
@@ -34,16 +41,16 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}({{.
return nil
})
{{- else }}
err = global.GVA_DB.Delete(&{{.Abbreviation}}).Error
err = {{$db}}.Delete(&{{.Abbreviation}}).Error
{{- end }}
return err
}
// Delete{{.StructName}}ByIds 批量删除{{.StructName}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ByIds(ids request.IdsReq,deleted_by uint) (err error) {
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ByIds(ids request.IdsReq{{- if .AutoCreateResource }},deleted_by uint{{- end}}) (err error) {
{{- if .AutoCreateResource }}
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = {{$db}}.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("id in ?", ids.Ids).Update("deleted_by", deleted_by).Error; err != nil {
return err
}
@@ -53,7 +60,7 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ById
return nil
})
{{- else}}
err = global.GVA_DB.Delete(&[]{{.Package}}.{{.StructName}}{},"id in ?",ids.Ids).Error
err = {{$db}}.Delete(&[]{{.Package}}.{{.StructName}}{},"id in ?",ids.Ids).Error
{{- end}}
return err
}
@@ -61,14 +68,14 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ById
// Update{{.StructName}} 更新{{.StructName}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Update{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
err = global.GVA_DB.Save(&{{.Abbreviation}}).Error
err = {{$db}}.Save(&{{.Abbreviation}}).Error
return err
}
// Get{{.StructName}} 根据id获取{{.StructName}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}(id uint) ({{.Abbreviation}} {{.Package}}.{{.StructName}}, err error) {
err = global.GVA_DB.Where("id = ?", id).First(&{{.Abbreviation}}).Error
err = {{$db}}.Where("id = ?", id).First(&{{.Abbreviation}}).Error
return
}
@@ -78,7 +85,7 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
// 创建db
db := global.GVA_DB.Model(&{{.Package}}.{{.StructName}}{})
db := {{$db}}.Model(&{{.Package}}.{{.StructName}}{})
var {{.Abbreviation}}s []{{.Package}}.{{.StructName}}
// 如果有条件搜索 下方会自动创建搜索语句
if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil {
@@ -105,6 +112,23 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
if err!=nil {
return
}
{{- if .NeedSort}}
var OrderStr string
orderMap := make(map[string]bool)
{{- range .Fields}}
{{- if .Sort}}
orderMap["{{.ColumnName}}"] = true
{{- end}}
{{- end}}
if orderMap[info.Sort] {
OrderStr = info.Sort
if info.Order == "descending" {
OrderStr = OrderStr + " desc"
}
db = db.Order(OrderStr)
}
{{- end}}
err = db.Limit(limit).Offset(offset).Find(&{{.Abbreviation}}s).Error
return {{.Abbreviation}}s, total, err
}
@@ -1,7 +1,4 @@
package {{ .PackageName }}
type ApiGroup struct {
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
}
@@ -1,7 +1,4 @@
package {{ .PackageName }}
type RouterGroup struct {
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
}
@@ -2,8 +2,5 @@ package {{ .PackageName }}
type ServiceGroup struct {
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
}
@@ -10,6 +10,9 @@
{{- if eq .FieldType "string" }}
<el-input v-model="formData.{{.FieldJson}}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- end }}
{{- if eq .FieldType "richtext" }}
<RichEdit v-model="formData.{{.FieldJson}}"/>
{{- end }}
{{- if eq .FieldType "int" }}
{{- if .DictType }}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择" :clearable="{{.Clearable}}">
@@ -30,11 +33,17 @@
<el-option v-for="item in [{{ .DataTypeLong }}]" :key="item" :label="item" :value="item" />
</el-select>
{{- end }}
</el-form-item>
{{- if eq .FieldType "picture" }}
<SelectImage v-model="formData.{{ .FieldJson }}" />
{{- end }}
{{- if eq .FieldType "pictures" }}
<SelectImage v-model="formData.{{ .FieldJson }}" multiple />
{{- end }}
</el-form-item>
{{- end }}
<el-form-item>
<el-button size="small" type="primary" @click="save">保存</el-button>
<el-button size="small" type="primary" @click="back">返回</el-button>
<el-button type="primary" @click="save">保存</el-button>
<el-button type="primary" @click="back">返回</el-button>
</el-form-item>
</el-form>
</div>
@@ -59,6 +68,18 @@ import { getDictFunc } from '@/utils/format'
import { useRoute, useRouter } from "vue-router"
import { ElMessage } from 'element-plus'
import { ref, reactive } from 'vue'
{{- if .HasPic }}
import SelectImage from '@/components/selectImage/selectImage.vue'
{{- end }}
{{- if .HasFile }}
import SelectFile from '@/components/selectFile/selectFile.vue'
{{- end }}
{{- if .HasRichText }}
// 富文本组件
import RichEdit from '@/components/richtext/rich-edit.vue'
{{- end }}
const route = useRoute()
const router = useRouter()
@@ -83,6 +104,15 @@ const formData = ref({
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- if eq .FieldType "picture" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "pictures" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "file" }}
{{.FieldJson}}: [],
{{- end }}
{{- end }}
})
// 验证规则
@@ -1,11 +1,19 @@
<template>
<div>
<div class="gva-search-box">
<el-form :inline="true" :model="searchInfo" class="demo-form-inline">
<el-form-item label="创建时间">
<el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始时间"></el-date-picker>
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline" :rules="searchRule" @keyup.enter="onSubmit">
<el-form-item label="创建日期" prop="createdAt">
<template #label>
<span>
创建日期
<el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
<el-icon><QuestionFilled /></el-icon>
</el-tooltip>
</span>
</template>
<el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始日期" :disabled-date="time=> searchInfo.endCreatedAt ? time.getTime() > searchInfo.endCreatedAt.getTime() : false"></el-date-picker>
—
<el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束时间"></el-date-picker>
<el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束日期" :disabled-date="time=> searchInfo.startCreatedAt ? time.getTime() < searchInfo.startCreatedAt.getTime() : false"></el-date-picker>
</el-form-item>
{{- range .Fields}} {{- if .FieldSearchType}} {{- if eq .FieldType "bool" }}
<el-form-item label="{{.FieldDesc}}" prop="{{.FieldJson}}">
@@ -29,7 +37,7 @@
</el-select>
</el-form-item>
{{- else}}
<el-form-item label="{{.FieldDesc}}">
<el-form-item label="{{.FieldDesc}}" prop="{{.FieldJson}}">
{{- if eq .FieldType "float64" "int"}}
@@ -48,9 +56,17 @@
{{- end}}
{{- else if eq .FieldType "time.Time"}}
{{if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
<el-date-picker v-model="searchInfo.start{{.FieldName}}" type="datetime" placeholder="搜索条件(起)"></el-date-picker>
<template #label>
<span>
{{.FieldDesc}}
<el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
<el-icon><QuestionFilled /></el-icon>
</el-tooltip>
</span>
</template>
<el-date-picker v-model="searchInfo.start{{.FieldName}}" type="datetime" placeholder="开始日期" :disabled-date="time=> searchInfo.end{{.FieldName}} ? time.getTime() > searchInfo.end{{.FieldName}}.getTime() : false"></el-date-picker>
—
<el-date-picker v-model="searchInfo.end{{.FieldName}}" type="datetime" placeholder="搜索条件(止)"></el-date-picker>
<el-date-picker v-model="searchInfo.end{{.FieldName}}" type="datetime" placeholder="结束日期" :disabled-date="time=> searchInfo.start{{.FieldName}} ? time.getTime() < searchInfo.start{{.FieldName}}.getTime() : false"></el-date-picker>
{{- else}}
<el-date-picker v-model="searchInfo.{{.FieldJson}}" type="datetime" placeholder="搜索条件"></el-date-picker>
{{- end}}
@@ -60,22 +76,22 @@
</el-form-item>{{ end }}{{ end }}{{ end }}
<el-form-item>
<el-button size="small" type="primary" icon="search" @click="onSubmit">查询</el-button>
<el-button size="small" icon="refresh" @click="onReset">重置</el-button>
<el-button type="primary" icon="search" @click="onSubmit">查询</el-button>
<el-button icon="refresh" @click="onReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="gva-table-box">
<div class="gva-btn-list">
<el-button size="small" type="primary" icon="plus" @click="openDialog">新增</el-button>
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
<el-popover v-model:visible="deleteVisible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin-top: 8px;">
<el-button size="small" type="primary" link @click="deleteVisible = false">取消</el-button>
<el-button size="small" type="primary" @click="onDelete">确定</el-button>
<el-button type="primary" link @click="deleteVisible = false">取消</el-button>
<el-button type="primary" @click="onDelete">确定</el-button>
</div>
<template #reference>
<el-button icon="delete" size="small" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="deleteVisible = true">删除</el-button>
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="deleteVisible = true">删除</el-button>
</template>
</el-popover>
</div>
@@ -86,6 +102,9 @@
:data="tableData"
row-key="ID"
@selection-change="handleSelectionChange"
{{- if .NeedSort}}
@sort-change="sortChange"
{{- end}}
>
<el-table-column type="selection" width="55" />
<el-table-column align="left" label="日期" width="180">
@@ -93,27 +112,55 @@
</el-table-column>
{{- range .Fields}}
{{- if .DictType}}
<el-table-column align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
{{"{{"}} filterDict(scope.row.{{.FieldJson}},{{.DictType}}Options) {{"}}"}}
</template>
</el-table-column>
{{- else if eq .FieldType "bool" }}
<el-table-column align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">{{"{{"}} formatBoolean(scope.row.{{.FieldJson}}) {{"}}"}}</template>
</el-table-column>
{{- else if eq .FieldType "time.Time" }}
<el-table-column align="left" label="{{.FieldDesc}}" width="180">
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" width="180">
<template #default="scope">{{"{{"}} formatDate(scope.row.{{.FieldJson}}) {{"}}"}}</template>
</el-table-column>
{{- else if eq .FieldType "picture" }}
<el-table-column label="{{.FieldDesc}}" width="200">
<template #default="scope">
<el-image style="width: 100px; height: 100px" :src="getUrl(scope.row.{{.FieldJson}})" fit="cover"/>
</template>
</el-table-column>
{{- else if eq .FieldType "pictures" }}
<el-table-column label="{{.FieldDesc}}" width="200">
<template #default="scope">
<div class="multiple-img-box">
<el-image v-for="(item,index) in scope.row.{{.FieldJson}}" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
</div>
</template>
</el-table-column>
{{- else if eq .FieldType "richtext" }}
<el-table-column label="{{.FieldDesc}}" width="200">
<template #default="scope">
[富文本内容]
</template>
</el-table-column>
{{- else if eq .FieldType "file" }}
<el-table-column label="{{.FieldDesc}}" width="200">
<template #default="scope">
<div class="file-list">
<el-tag v-for="file in scope.row.{{.FieldJson}}" :key="file.uid">{{"{{"}}file.name{{"}}"}}</el-tag>
</div>
</template>
</el-table-column>
{{- else }}
<el-table-column align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- end }}
{{- end }}
<el-table-column align="left" label="按钮组">
<el-table-column align="left" label="操作">
<template #default="scope">
<el-button type="primary" link icon="edit" size="small" class="table-button" @click="update{{.StructName}}Func(scope.row)">变更</el-button>
<el-button type="primary" link icon="delete" size="small" @click="deleteRow(scope.row)">删除</el-button>
<el-button type="primary" link icon="edit" class="table-button" @click="update{{.StructName}}Func(scope.row)">变更</el-button>
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -129,7 +176,7 @@
/>
</div>
</div>
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="type==='create'?'添加':'修改'" destroy-on-close>
<el-form :model="formData" label-position="right" ref="elFormRef" :rules="rule" label-width="80px">
{{- range .Fields}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
@@ -139,6 +186,9 @@
{{- if eq .FieldType "string" }}
<el-input v-model="formData.{{.FieldJson}}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- end }}
{{- if eq .FieldType "richtext" }}
<RichEdit v-model="formData.{{.FieldJson}}"/>
{{- end }}
{{- if eq .FieldType "int" }}
{{- if .DictType}}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择" style="width:100%" :clearable="{{.Clearable}}" >
@@ -158,14 +208,23 @@
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="item in [{{.DataTypeLong}}]" :key="item" :label="item" :value="item" />
</el-select>
{{- end }}
{{- if eq .FieldType "picture" }}
<SelectImage v-model="formData.{{ .FieldJson }}" />
{{- end }}
{{- if eq .FieldType "pictures" }}
<SelectImage v-model="formData.{{ .FieldJson }}" multiple />
{{- end }}
{{- if eq .FieldType "file" }}
<SelectFile v-model="formData.{{ .FieldJson }}" />
{{- end }}
</el-form-item>
{{- end }}
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button size="small" @click="closeDialog">取 消</el-button>
<el-button size="small" type="primary" @click="enterDialog">确 定</el-button>
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
@@ -188,6 +247,25 @@ import {
get{{.StructName}}List
} from '@/api/{{.PackageName}}'
{{- if or .HasPic .HasFile}}
import { getUrl } from '@/utils/image'
{{- end }}
{{- if .HasPic }}
// 图片选择组件
import SelectImage from '@/components/selectImage/selectImage.vue'
{{- end }}
{{- if .HasRichText }}
// 富文本组件
import RichEdit from '@/components/richtext/rich-edit.vue'
{{- end }}
{{- if .HasFile }}
// 文件选择组件
import SelectFile from '@/components/selectFile/selectFile.vue'
{{- end }}
// 全量引入格式化工具 请按需保留
import { getDictFunc, formatDate, formatBoolean, filterDict } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
@@ -214,6 +292,15 @@ const formData = ref({
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- if eq .FieldType "picture" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "pictures" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "file" }}
{{.FieldJson}}: [],
{{- end }}
{{- end }}
})
@@ -225,13 +312,54 @@ const rule = reactive({
required: true,
message: '{{ .ErrorText }}',
trigger: ['input','blur'],
}],
},
{{- if eq .FieldType "string" }}
{
whitespace: true,
message: '不能只输入空格',
trigger: ['input', 'blur'],
}
{{- end }}
],
{{- end }}
{{- end }}
})
const elFormRef = ref()
const searchRule = reactive({
createdAt: [
{ validator: (rule, value, callback) => {
if (searchInfo.value.startCreatedAt && !searchInfo.value.endCreatedAt) {
callback(new Error('请填写结束日期'))
} else if (!searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt) {
callback(new Error('请填写开始日期'))
} else if (searchInfo.value.startCreatedAt && searchInfo.value.endCreatedAt && (searchInfo.value.startCreatedAt.getTime() === searchInfo.value.endCreatedAt.getTime() || searchInfo.value.startCreatedAt.getTime() > searchInfo.value.endCreatedAt.getTime())) {
callback(new Error('开始日期应当早于结束日期'))
} else {
callback()
}
}, trigger: 'change' }
],
{{- range .Fields }}
{{- if .FieldSearchType}}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson }} : [{ validator: (rule, value, callback) => {
if (searchInfo.value.start{{.FieldName}} && !searchInfo.value.end{{.FieldName}}) {
callback(new Error('请填写结束日期'))
} else if (!searchInfo.value.start{{.FieldName}} && searchInfo.value.end{{.FieldName}}) {
callback(new Error('请填写开始日期'))
} else if (searchInfo.value.start{{.FieldName}} && searchInfo.value.end{{.FieldName}} && (searchInfo.value.start{{.FieldName}}.getTime() === searchInfo.value.end{{.FieldName}}.getTime() || searchInfo.value.start{{.FieldName}}.getTime() > searchInfo.value.end{{.FieldName}}.getTime())) {
callback(new Error('开始日期应当早于结束日期'))
} else {
callback()
}
}, trigger: 'change' }],
{{- end }}
{{- end }}
{{- end }}
})
const elFormRef = ref()
const elSearchFormRef = ref()
// =========== 表格控制部分 ===========
const page = ref(1)
@@ -240,20 +368,33 @@ const pageSize = ref(10)
const tableData = ref([])
const searchInfo = ref({})
{{- if .NeedSort}}
// 排序
const sortChange = ({ prop, order }) => {
searchInfo.value.sort = prop
searchInfo.value.order = order
getTableData()
}
{{- end}}
// 重置
const onReset = () => {
searchInfo.value = {}
getTableData()
}
// 搜索
const onSubmit = () => {
page.value = 1
pageSize.value = 10
{{- range .Fields}}{{- if eq .FieldType "bool" }}
if (searchInfo.value.{{.FieldJson}} === ""){
searchInfo.value.{{.FieldJson}}=null
}{{ end }}{{ end }}
getTableData()
elSearchFormRef.value?.validate(async(valid) => {
if (!valid) return
page.value = 1
pageSize.value = 10
{{- range .Fields}}{{- if eq .FieldType "bool" }}
if (searchInfo.value.{{.FieldJson}} === ""){
searchInfo.value.{{.FieldJson}}=null
}{{ end }}{{ end }}
getTableData()
})
}
// 分页
@@ -431,7 +572,19 @@ const enterDialog = async () => {
}
})
}
{{if .HasFile }}
const downloadFile = (url) => {
window.open(getUrl(url), '_blank')
}
{{end}}
</script>
<style>
{{if .HasFile }}
.file-list{
display: flex;
flex-wrap: wrap;
gap: 4px;
}
{{end}}
</style>
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1,7 +1,6 @@
package example
type RouterGroup struct {
ExcelRouter
CustomerRouter
FileUploadAndDownloadRouter
}
-19
View File
@@ -1,19 +0,0 @@
package example
import (
v1 "github.com/flipped-aurora/gin-vue-admin/server/api/v1"
"github.com/gin-gonic/gin"
)
type ExcelRouter struct{}
func (e *ExcelRouter) InitExcelRouter(Router *gin.RouterGroup) {
excelRouter := Router.Group("excel")
exaExcelApi := v1.ApiGroupApp.ExampleApiGroup.ExcelApi
{
excelRouter.POST("importExcel", exaExcelApi.ImportExcel) // 导入Excel
excelRouter.GET("loadExcel", exaExcelApi.LoadExcel) // 加载Excel数据
excelRouter.POST("exportExcel", exaExcelApi.ExportExcel) // 导出Excel
excelRouter.GET("downloadTemplate", exaExcelApi.DownloadTemplate) // 下载模板文件
}
}
+1
View File
@@ -15,4 +15,5 @@ type RouterGroup struct {
OperationRecordRouter
DictionaryDetailRouter
AuthorityBtnRouter
ChatGptRouter
}
+6 -1
View File
@@ -8,9 +8,11 @@ import (
type ApiRouter struct{}
func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup) {
func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup, RouterPub *gin.RouterGroup) {
apiRouter := Router.Group("api").Use(middleware.OperationRecord())
apiRouterWithoutRecord := Router.Group("api")
apiPublicRouterWithoutRecord := RouterPub.Group("api")
apiRouterApi := v1.ApiGroupApp.SystemApiGroup.SystemApiApi
{
apiRouter.POST("createApi", apiRouterApi.CreateApi) // 创建Api
@@ -23,4 +25,7 @@ func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup) {
apiRouterWithoutRecord.POST("getAllApis", apiRouterApi.GetAllApis) // 获取所有api
apiRouterWithoutRecord.POST("getApiList", apiRouterApi.GetApiList) // 获取Api列表
}
{
apiPublicRouterWithoutRecord.GET("freshCasbin", apiRouterApi.FreshCasbin) // 刷新casbin权限
}
}
+1
View File
@@ -21,5 +21,6 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup) {
autoCodeRouter.POST("delPackage", autoCodeApi.DelPackage) // 删除package包
autoCodeRouter.POST("createPlug", autoCodeApi.AutoPlug) // 自动插件包模板
autoCodeRouter.POST("installPlugin", autoCodeApi.InstallPlugin) // 自动安装插件
autoCodeRouter.POST("pubPlug", autoCodeApi.PubPlug) // 打包插件
}
}
+20
View File
@@ -0,0 +1,20 @@
package system
import (
v1 "github.com/flipped-aurora/gin-vue-admin/server/api/v1"
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/gin-gonic/gin"
)
type ChatGptRouter struct{}
func (s *ChatGptRouter) InitChatGptRouter(Router *gin.RouterGroup) {
chatGptRouter := Router.Group("chatGpt").Use(middleware.OperationRecord())
chatGptApi := v1.ApiGroupApp.SystemApiGroup.ChatGptApi
{
chatGptRouter.POST("createSK", chatGptApi.CreateSK)
chatGptRouter.GET("getSK", chatGptApi.GetSK)
chatGptRouter.DELETE("deleteSK", chatGptApi.DeleteSK)
chatGptRouter.POST("getTable", chatGptApi.GetTable)
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ func (s *InitRouter) InitInitRouter(Router *gin.RouterGroup) {
initRouter := Router.Group("init")
dbApi := v1.ApiGroupApp.SystemApiGroup.DBApi
{
initRouter.POST("initdb", dbApi.InitDB) // 创建Api
initRouter.POST("checkdb", dbApi.CheckDB) // 创建Api
initRouter.POST("initdb", dbApi.InitDB) // 初始化数据库
initRouter.POST("checkdb", dbApi.CheckDB) // 检测是否需要初始化数据库
}
}
-1
View File
@@ -1,7 +1,6 @@
package example
type ServiceGroup struct {
ExcelService
CustomerService
FileUploadAndDownloadService
}
-94
View File
@@ -1,94 +0,0 @@
package example
import (
"errors"
"fmt"
"strconv"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/xuri/excelize/v2"
)
type ExcelService struct{}
func (exa *ExcelService) ParseInfoList2Excel(infoList []system.SysBaseMenu, filePath string) error {
excel := excelize.NewFile()
excel.SetSheetRow("Sheet1", "A1", &[]string{"ID", "路由Name", "路由Path", "是否隐藏", "父节点", "排序", "文件名称"})
for i, menu := range infoList {
axis := fmt.Sprintf("A%d", i+2)
excel.SetSheetRow("Sheet1", axis, &[]interface{}{
menu.ID,
menu.Name,
menu.Path,
menu.Hidden,
menu.ParentId,
menu.Sort,
menu.Component,
})
}
err := excel.SaveAs(filePath)
return err
}
func (exa *ExcelService) ParseExcel2InfoList() ([]system.SysBaseMenu, error) {
skipHeader := true
fixedHeader := []string{"ID", "路由Name", "路由Path", "是否隐藏", "父节点", "排序", "文件名称"}
file, err := excelize.OpenFile(global.GVA_CONFIG.Excel.Dir + "ExcelImport.xlsx")
if err != nil {
return nil, err
}
menus := make([]system.SysBaseMenu, 0)
rows, err := file.Rows("Sheet1")
if err != nil {
return nil, err
}
for rows.Next() {
row, err := rows.Columns()
if err != nil {
return nil, err
}
if skipHeader {
if exa.compareStrSlice(row, fixedHeader) {
skipHeader = false
continue
} else {
return nil, errors.New("Excel格式错误")
}
}
if len(row) != len(fixedHeader) {
continue
}
id, _ := strconv.Atoi(row[0])
hidden, _ := strconv.ParseBool(row[3])
sort, _ := strconv.Atoi(row[5])
menu := system.SysBaseMenu{
GVA_MODEL: global.GVA_MODEL{
ID: uint(id),
},
Name: row[1],
Path: row[2],
Hidden: hidden,
ParentId: row[4],
Sort: sort,
Component: row[6],
}
menus = append(menus, menu)
}
return menus, nil
}
func (exa *ExcelService) compareStrSlice(a, b []string) bool {
if len(a) != len(b) {
return false
}
if (b == nil) != (a == nil) {
return false
}
for key, value := range a {
if value != b[key] {
return false
}
}
return true
}
@@ -94,15 +94,15 @@ func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader,
if uploadErr != nil {
panic(err)
}
s := strings.Split(header.Filename, ".")
f := example.ExaFileUploadAndDownload{
Url: filePath,
Name: header.Filename,
Tag: s[len(s)-1],
Key: key,
}
if noSave == "0" {
s := strings.Split(header.Filename, ".")
f := example.ExaFileUploadAndDownload{
Url: filePath,
Name: header.Filename,
Tag: s[len(s)-1],
Key: key,
}
return f, e.Upload(f)
}
return
return f, nil
}
+1
View File
@@ -16,4 +16,5 @@ type ServiceGroup struct {
OperationRecordService
DictionaryDetailService
AuthorityBtnService
ChatGptService
}
+15 -13
View File
@@ -43,12 +43,7 @@ func (apiService *ApiService) DeleteApi(api system.SysApi) (err error) {
if err != nil {
return err
}
success := CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)
if !success {
return errors.New(entity.Path + ":" + entity.Method + "casbin同步清理失败")
}
e := CasbinServiceApp.Casbin()
err = e.InvalidateCache()
CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)
if err != nil {
return err
}
@@ -105,7 +100,7 @@ func (apiService *ApiService) GetAPIInfoList(api system.SysApi, info request.Pag
} else {
OrderStr = order
}
} else { // didn't matched any order key in `orderMap`
} else { // didn't match any order key in `orderMap`
err = fmt.Errorf("非法的排序字段: %v", order)
return apiList, total, err
}
@@ -179,16 +174,23 @@ func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) {
return err
} else {
for _, sysApi := range apis {
success := CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
if !success {
return errors.New(sysApi.Path + ":" + sysApi.Method + "casbin同步清理失败")
}
CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
}
e := CasbinServiceApp.Casbin()
err = e.InvalidateCache()
if err != nil {
return err
}
}
return err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteApis
//@description: 删除选中API
//@param: apis []model.SysApi
//@return: err error
func (apiService *ApiService) FreshCasbin() (err error) {
e := CasbinServiceApp.Casbin()
err = e.LoadPolicy()
return err
}
+15 -8
View File
@@ -114,7 +114,7 @@ func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthor
if !errors.Is(global.GVA_DB.Where("parent_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).Error, gorm.ErrRecordNotFound) {
return errors.New("此角色存在子角色不允许删除")
}
db := global.GVA_DB.Preload("SysBaseMenus").Where("authority_id = ?", auth.AuthorityId).First(auth)
db := global.GVA_DB.Preload("SysBaseMenus").Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(auth)
err = db.Unscoped().Delete(auth).Error
if err != nil {
return
@@ -125,14 +125,21 @@ func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthor
return
}
// err = db.Association("SysBaseMenus").Delete(&auth)
} else {
err = db.Error
}
if len(auth.DataAuthorityId) > 0 {
err = global.GVA_DB.Model(auth).Association("DataAuthorityId").Delete(auth.DataAuthorityId)
if err != nil {
return
}
}
err = global.GVA_DB.Delete(&[]system.SysUserAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error
if err != nil {
return
}
err = global.GVA_DB.Delete(&[]system.SysAuthorityBtn{}, "authority_id = ?", auth.AuthorityId).Error
if err != nil {
return
}
authorityId := strconv.Itoa(int(auth.AuthorityId))
CasbinServiceApp.ClearCasbin(0, authorityId)
return err
@@ -148,13 +155,13 @@ func (authorityService *AuthorityService) GetAuthorityInfoList(info request.Page
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
db := global.GVA_DB.Model(&system.SysAuthority{})
err = db.Where("parent_id = ?", "0").Count(&total).Error
if err = db.Where("parent_id = ?", "0").Count(&total).Error; total == 0 || err != nil {
return
}
var authority []system.SysAuthority
err = db.Limit(limit).Offset(offset).Preload("DataAuthorityId").Where("parent_id = ?", "0").Find(&authority).Error
if len(authority) > 0 {
for k := range authority {
err = authorityService.findChildrenAuthority(&authority[k])
}
for k := range authority {
err = authorityService.findChildrenAuthority(&authority[k])
}
return authority, total, err
}
+251 -247
View File
@@ -1,16 +1,11 @@
package system
import (
"bytes"
"archive/zip"
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"io"
"log"
"mime/multipart"
"os"
"path/filepath"
@@ -18,12 +13,11 @@ import (
"strings"
"text/template"
cp "github.com/otiai10/copy"
"go.uber.org/zap"
"golang.org/x/text/cases"
"golang.org/x/text/language"
ast2 "github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
"github.com/flipped-aurora/gin-vue-admin/server/resource/autocode_template/subcontract"
cp "github.com/otiai10/copy"
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
@@ -53,23 +47,10 @@ type autoPackage struct {
var (
packageInjectionMap map[string]astInjectionMeta
injectionPaths []injectionMeta
caser = cases.Title(language.English)
)
func Init(Package string) {
injectionPaths = []injectionMeta{
{
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go"),
funcName: "MysqlTables",
structNameF: Package + ".%s{},",
},
{
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go"),
funcName: "Routers",
structNameF: Package + "Router.Init%sRouter(PrivateGroup)",
},
{
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, Package), "enter.go"),
@@ -144,22 +125,37 @@ type AutoCodeService struct{}
var AutoCodeServiceApp = new(AutoCodeService)
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: PreviewTemp
//@description: 预览创建代码
//@param: model.AutoCodeStruct
//@return: map[string]string, error
// @author: [songzhibin97](https://github.com/songzhibin97)
// @function: PreviewTemp
// @description: 预览创建代码
// @param: model.AutoCodeStruct
// @return: map[string]string, error
func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStruct) (map[string]string, error) {
makeDictTypes(&autoCode)
for i := range autoCode.Fields {
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
break
}
if autoCode.Fields[i].Require {
autoCode.NeedValid = true
break
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.HasPic = true
autoCode.NeedJSON = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.HasFile = true
autoCode.NeedJSON = true
}
}
dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
@@ -233,26 +229,41 @@ func makeDictTypes(autoCode *system.AutoCodeStruct) {
}
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: CreateTemp
//@description: 创建代码
//@param: model.AutoCodeStruct
//@return: err error
// @author: [piexlmax](https://github.com/piexlmax)
// @function: CreateTemp
// @description: 创建代码
// @param: model.AutoCodeStruct
// @return: err error
func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, ids ...uint) (err error) {
makeDictTypes(&autoCode)
for i := range autoCode.Fields {
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
break
}
if autoCode.Fields[i].Require {
autoCode.NeedValid = true
break
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.NeedJSON = true
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.NeedJSON = true
autoCode.HasFile = true
}
}
// 增加判断: 重复创建struct
if autoCode.AutoMoveFile && AutoCodeHistoryServiceApp.Repeat(autoCode.StructName, autoCode.Package) {
if autoCode.AutoMoveFile && AutoCodeHistoryServiceApp.Repeat(autoCode.BusinessDB, autoCode.StructName, autoCode.Package) {
return RepeatErr
}
dataList, fileList, needMkdir, err := autoCodeService.getNeedList(&autoCode)
@@ -305,6 +316,22 @@ func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruc
return err
}
}
{
// 在gorm.go 注入 自动迁移
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go")
autoCode.BusinessDB = utils.MaheHump(autoCode.BusinessDB) // 这里将 数据库中间存在 - 的转换为驼峰
ast2.AddRegisterTablesAst(path, "RegisterTables", autoCode.Package, autoCode.BusinessDB, autoCode.StructName)
}
{
// router.go 注入 自动迁移
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go")
ast2.AddRouterCode(path, "Routers", autoCode.Package, autoCode.StructName)
}
// 给各个enter进行注入
err = injectionCode(autoCode.StructName, &injectionCodeMeta)
if err != nil {
return
@@ -316,15 +343,6 @@ func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruc
bf.WriteString(";")
}
}
var gormPath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go")
var routePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go")
var imporStr = fmt.Sprintf("github.com/flipped-aurora/gin-vue-admin/server/model/%s", autoCode.Package)
_ = ImportReference(routePath, "", "", autoCode.Package, "")
_ = ImportReference(gormPath, imporStr, "", "", "")
} else { // 打包
if err = utils.ZipFiles("./ginvueadmin.zip", fileList, ".", "."); err != nil {
return err
@@ -364,11 +382,11 @@ func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruc
return nil
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetAllTplFile
//@description: 获取 pathName 文件夹下所有 tpl 文件
//@param: pathName string, fileList []string
//@return: []string, error
// @author: [piexlmax](https://github.com/piexlmax)
// @function: GetAllTplFile
// @description: 获取 pathName 文件夹下所有 tpl 文件
// @param: pathName string, fileList []string
// @return: []string, error
func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, fileList []string) ([]string, error) {
files, err := os.ReadDir(pathName)
@@ -387,22 +405,26 @@ func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, fileList
return fileList, err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetDB
//@description: 获取指定数据库和指定数据表的所有字段名,类型值等
//@param: tableName string, dbName string
//@return: err error, Columns []request.ColumnReq
// @author: [piexlmax](https://github.com/piexlmax)
// @function: GetDB
// @description: 获取指定数据库和指定数据表的所有字段名,类型值等
// @param: tableName string, dbName string
// @return: err error, Columns []request.ColumnReq
func (autoCodeService *AutoCodeService) DropTable(tableName string) error {
return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
func (autoCodeService *AutoCodeService) DropTable(BusinessDb, tableName string) error {
if BusinessDb != "" {
return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error
} else {
return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
}
}
//@author: [SliverHorn](https://github.com/SliverHorn)
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: addAutoMoveFile
//@description: 生成对应的迁移文件路径
//@param: *tplData
//@return: null
// @author: [SliverHorn](https://github.com/SliverHorn)
// @author: [songzhibin97](https://github.com/songzhibin97)
// @function: addAutoMoveFile
// @description: 生成对应的迁移文件路径
// @param: *tplData
// @return: null
func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
base := filepath.Base(data.autoCodePath)
@@ -442,49 +464,49 @@ func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
}
}
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: CreateApi
//@description: 自动创建api数据,
//@param: a *model.AutoCodeStruct
//@return: err error
// @author: [piexlmax](https://github.com/piexlmax)
// @author: [SliverHorn](https://github.com/SliverHorn)
// @function: CreateApi
// @description: 自动创建api数据,
// @param: a *model.AutoCodeStruct
// @return: err error
func (autoCodeService *AutoCodeService) AutoCreateApi(a *system.AutoCodeStruct) (ids []uint, err error) {
apiList := []system.SysApi{
{
Path: "/" + a.Abbreviation + "/" + "create" + a.StructName,
Description: "新增" + a.Description,
ApiGroup: a.Abbreviation,
ApiGroup: a.Description,
Method: "POST",
},
{
Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName,
Description: "删除" + a.Description,
ApiGroup: a.Abbreviation,
ApiGroup: a.Description,
Method: "DELETE",
},
{
Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName + "ByIds",
Description: "批量删除" + a.Description,
ApiGroup: a.Abbreviation,
ApiGroup: a.Description,
Method: "DELETE",
},
{
Path: "/" + a.Abbreviation + "/" + "update" + a.StructName,
Description: "更新" + a.Description,
ApiGroup: a.Abbreviation,
ApiGroup: a.Description,
Method: "PUT",
},
{
Path: "/" + a.Abbreviation + "/" + "find" + a.StructName,
Description: "根据ID获取" + a.Description,
ApiGroup: a.Abbreviation,
ApiGroup: a.Description,
Method: "GET",
},
{
Path: "/" + a.Abbreviation + "/" + "get" + a.StructName + "List",
Description: "获取" + a.Description + "列表",
ApiGroup: a.Abbreviation,
ApiGroup: a.Description,
Method: "GET",
},
}
@@ -569,9 +591,7 @@ func (autoCodeService *AutoCodeService) getNeedList(autoCode *system.AutoCodeStr
func injectionCode(structName string, bf *strings.Builder) error {
for _, meta := range injectionPaths {
code := fmt.Sprintf(meta.structNameF, structName)
if err := utils.AutoInjectionCode(meta.path, meta.funcName, code); err != nil {
return err
}
ast2.ImportForAutoEnter(meta.path, meta.funcName, code)
bf.WriteString(fmt.Sprintf("%s@%s@%s;", meta.path, meta.funcName, code))
}
return nil
@@ -645,179 +665,13 @@ func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) er
// 创建完成后在对应的位置插入结构代码
for _, v := range pendingTemp {
meta := packageInjectionMap[v.name]
if err := ImportReference(meta.path, fmt.Sprintf(meta.importCodeF, v.name, packageName), fmt.Sprintf(meta.structNameF, caser.String(packageName)), fmt.Sprintf(meta.packageNameF, packageName), meta.groupName); err != nil {
if err := ast2.ImportReference(meta.path, fmt.Sprintf(meta.importCodeF, v.name, packageName), fmt.Sprintf(meta.structNameF, utils.FirstUpper(packageName)), fmt.Sprintf(meta.packageNameF, packageName), meta.groupName); err != nil {
return err
}
}
return nil
}
type Visitor struct {
ImportCode string
StructName string
PackageName string
GroupName string
}
func (vi *Visitor) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.GenDecl:
// 查找有没有import context包
// Notice:没有考虑没有import任何包的情况
if n.Tok == token.IMPORT && vi.ImportCode != "" {
vi.addImport(n)
// 不需要再遍历子树
return nil
}
if n.Tok == token.TYPE && vi.StructName != "" && vi.PackageName != "" && vi.GroupName != "" {
vi.addStruct(n)
return nil
}
case *ast.FuncDecl:
if n.Name.Name == "Routers" {
vi.addFuncBodyVar(n)
return nil
}
}
return vi
}
func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor {
for i := range genDecl.Specs {
switch n := genDecl.Specs[i].(type) {
case *ast.TypeSpec:
if strings.Index(n.Name.Name, "Group") > -1 {
switch t := n.Type.(type) {
case *ast.StructType:
f := &ast.Field{
Names: []*ast.Ident{
{
Name: vi.StructName,
Obj: &ast.Object{
Kind: ast.Var,
Name: vi.StructName,
},
},
},
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: vi.PackageName,
},
Sel: &ast.Ident{
Name: vi.GroupName,
},
},
}
t.Fields.List = append(t.Fields.List, f)
}
}
}
}
return vi
}
func (vi *Visitor) addImport(genDecl *ast.GenDecl) ast.Visitor {
// 是否已经import
hasImported := false
for _, v := range genDecl.Specs {
importSpec := v.(*ast.ImportSpec)
// 如果已经包含
if importSpec.Path.Value == strconv.Quote(vi.ImportCode) {
hasImported = true
}
}
if !hasImported {
genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: strconv.Quote(vi.ImportCode),
},
})
}
return vi
}
func (vi *Visitor) addFuncBodyVar(funDecl *ast.FuncDecl) ast.Visitor {
hasVar := false
for _, v := range funDecl.Body.List {
switch varSpec := v.(type) {
case *ast.AssignStmt:
for i := range varSpec.Lhs {
switch nn := varSpec.Lhs[i].(type) {
case *ast.Ident:
if nn.Name == vi.PackageName+"Router" {
hasVar = true
}
}
}
}
}
if !hasVar {
assignStmt := &ast.AssignStmt{
Lhs: []ast.Expr{
&ast.Ident{
Name: vi.PackageName + "Router",
Obj: &ast.Object{
Kind: ast.Var,
Name: vi.PackageName + "Router",
},
},
},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.SelectorExpr{
X: &ast.SelectorExpr{
X: &ast.Ident{
Name: "router",
},
Sel: &ast.Ident{
Name: "RouterGroupApp",
},
},
Sel: &ast.Ident{
Name: caser.String(vi.PackageName),
},
},
},
}
funDecl.Body.List = append(funDecl.Body.List, funDecl.Body.List[1])
index := 1
copy(funDecl.Body.List[index+1:], funDecl.Body.List[index:])
funDecl.Body.List[index] = assignStmt
}
return vi
}
func ImportReference(filepath, importCode, structName, packageName, groupName string) error {
fSet := token.NewFileSet()
fParser, err := parser.ParseFile(fSet, filepath, nil, parser.ParseComments)
if err != nil {
return err
}
importCode = strings.TrimSpace(importCode)
v := &Visitor{
ImportCode: importCode,
StructName: structName,
PackageName: packageName,
GroupName: groupName,
}
if importCode == "" {
ast.Print(fSet, fParser)
}
ast.Walk(v, fParser)
var output []byte
buffer := bytes.NewBuffer(output)
err = format.Node(buffer, fSet, fParser)
if err != nil {
log.Fatal(err)
}
// 写回数据
return os.WriteFile(filepath, buffer.Bytes(), 0o600)
}
// CreatePlug 自动创建插件模板
func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) error {
// 检查列表参数是否有效
@@ -944,3 +798,153 @@ func skipMacSpecialDocument(src string) (bool, error) {
}
return false, nil
}
func (autoCodeService *AutoCodeService) PubPlug(plugName string) (zipPath string, err error) {
if plugName == "" {
return "", errors.New("插件名称不能为空")
}
// 防止路径穿越
plugName = filepath.Clean(plugName)
webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName)
serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName)
// 创建一个新的zip文件
// 判断目录是否存在
webInfo, err := os.Stat(webPath)
if err != nil {
return "", errors.New("web路径不存在")
}
serverInfo, err := os.Stat(serverPath)
if err != nil {
return "", errors.New("server路径不存在")
}
fileName := plugName + ".zip"
// 创建一个新的zip文件
zipFile, err := os.Create(fileName)
if err != nil {
fmt.Println(err)
return
}
defer zipFile.Close()
// 创建一个zip写入器
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
// 创建一个新的文件头
webHeader, err := zip.FileInfoHeader(webInfo)
if err != nil {
return
}
// 创建一个新的文件头
serverHeader, err := zip.FileInfoHeader(serverInfo)
if err != nil {
return
}
webHeader.Name = filepath.Join(plugName, "web", "plugin")
serverHeader.Name = filepath.Join(plugName, "server", "plugin")
// 将文件添加到zip归档中
_, err = zipWriter.CreateHeader(serverHeader)
_, err = zipWriter.CreateHeader(webHeader)
// 遍历webPath目录并将所有非隐藏文件添加到zip归档中
err = filepath.Walk(webPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过隐藏文件
if strings.HasPrefix(info.Name(), ".") {
return nil
}
// 创建一个新的文件头
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// 将文件头的名称设置为文件的相对路径
rel, _ := filepath.Rel(webPath, path)
header.Name = filepath.Join(plugName, "web", "plugin", plugName, rel)
// 将文件添加到zip归档中
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// 打开文件并将其内容复制到zip归档中
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
return nil
})
if err != nil {
return
}
// 遍历serverPath目录并将所有非隐藏文件添加到zip归档中
err = filepath.Walk(serverPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过隐藏文件和目录
if strings.HasPrefix(info.Name(), ".") {
return nil
}
// 创建一个新的文件头
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// 将文件头的名称设置为文件的相对路径
rel, _ := filepath.Rel(serverPath, path)
header.Name = filepath.Join(plugName, "server", "plugin", plugName, rel)
// 将文件添加到zip归档中
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// 打开文件并将其内容复制到zip归档中
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
return nil
})
if err != nil {
return
}
return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil
}
@@ -6,18 +6,44 @@ import (
)
type Database interface {
GetDB() (data []response.Db, err error)
GetTables(dbName string) (data []response.Table, err error)
GetColumn(tableName string, dbName string) (data []response.Column, err error)
GetDB(businessDB string) (data []response.Db, err error)
GetTables(businessDB string, dbName string) (data []response.Table, err error)
GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error)
}
func (autoCodeService *AutoCodeService) Database() Database {
switch global.GVA_CONFIG.System.DbType {
case "mysql":
return AutoCodeMysql
case "pgsql":
return AutoCodePgsql
default:
func (autoCodeService *AutoCodeService) Database(businessDB string) Database {
if businessDB == "" {
switch global.GVA_CONFIG.System.DbType {
case "mysql":
return AutoCodeMysql
case "pgsql":
return AutoCodePgsql
case "sqlite":
return AutoCodeSqlite
default:
return AutoCodeMysql
}
} else {
for _, info := range global.GVA_CONFIG.DBList {
if info.AliasName == businessDB {
switch info.Type {
case "mysql":
return AutoCodeMysql
case "mssql":
return AutoCodeMssql
case "pgsql":
return AutoCodePgsql
case "oracle":
return AutoCodeOracle
case "sqlite":
return AutoCodeSqlite
default:
return AutoCodeMysql
}
}
}
return AutoCodeMysql
}
}
@@ -0,0 +1,58 @@
package system
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
)
var AutoCodeMssql = new(autoCodeMssql)
type autoCodeMssql struct{}
// GetDB 获取数据库的所有数据库名
// Author [piexlmax](https://github.com/piexlmax)
// Author [SliverHorn](https://github.com/SliverHorn)
func (s *autoCodeMssql) GetDB(businessDB string) (data []response.Db, err error) {
var entities []response.Db
sql := "select name AS 'database' from sysdatabases;"
if businessDB == "" {
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
} else {
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
}
return entities, err
}
// GetTables 获取数据库的所有表名
// Author [piexlmax](https://github.com/piexlmax)
// Author [SliverHorn](https://github.com/SliverHorn)
func (s *autoCodeMssql) GetTables(businessDB string, dbName string) (data []response.Table, err error) {
var entities []response.Table
sql := fmt.Sprintf(`select name as 'table_name' from %s.DBO.sysobjects where xtype='U'`, dbName)
if businessDB == "" {
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
} else {
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
}
return entities, err
}
// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等
// Author [piexlmax](https://github.com/piexlmax)
// Author [SliverHorn](https://github.com/SliverHorn)
func (s *autoCodeMssql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
var entities []response.Column
sql := fmt.Sprintf(`select sc.name as column_name,st.name as data_type, sc.length as data_type_long
from %s.DBO.syscolumns sc,systypes st where sc.xtype=st.xtype and st.usertype=0 and sc.id in (select id from %s.DBO.sysobjects where xtype='U' and name='%s');`, dbName, dbName, tableName)
if businessDB == "" {
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
} else {
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
}
return entities, err
}

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