Compare commits

...
373 Commits
Author SHA1 Message Date
pixelMax(奇淼 0f9e519b0e feature:调整侧边栏暗夜模式的hover错误 2024-05-10 15:17:04 +08:00
PiexlMax(奇淼 0d17a2351f Update ci.yaml 2024-05-10 14:34:21 +08:00
pixelMax(奇淼 1b388664cb feature:水印密集度调整 2024-05-10 13:16:06 +08:00
pixelMax(奇淼 1edb0c529f feature:调整bottomInfo组件位置 2024-05-10 13:05:36 +08:00
pixelMax(奇淼 f07a58bff4 feature:readme版权相关文案调整 2024-05-10 11:18:48 +08:00
pixelMax(奇淼 c054840aec feature:上线前细节数据调整 2024-05-10 11:15:38 +08:00
PiexlMax(奇淼 e259418398 Update README.md 2024-05-09 21:42:11 +08:00
PiexlMax(奇淼andbypanghu df1d68a724 新版前端UI,预发布2.6.4版本。 (#1735)
* fix: 修改 layout 中相关 emit 和 重复声明的 theme ,采用最新的vue api

* fix:修改 eslintrc 的配置文件,删除大多数无用配置

* fix: 修复 layout 切换手机样式模糊层的 bug

* feature: 重构 layout , 添加暗黑模式 , 删除侧边栏颜色

* fix:细节调整,activeColor摘除。

* feature:右侧滚动模式调整

* feature:无用代码剔除

* fix: 修复暗黑模式相关细节

* fix: custome config layout

* feature:样式细节调整

* fix: 增加前端样式配置文件

* feature:调整基础配置,增加表格边框

* feature:buttomInfo取消底色,a标签更改为活跃色

* feature:调整阴影颜色和配置同步

* feature:版本调整2.6.3==>2.6.4

---------

Co-authored-by: bypanghu <bypanghu@163.com>
2024-05-09 21:35:39 +08:00
Wallandwall-js 9d58390e11 fix(bug): [api.go.tpl]自动化代码swag路径冲突 (#1732)
Co-authored-by: wall-js <liujun@grectronics.cn>
2024-05-09 11:48:15 +08:00
pixelMax(奇淼 db4c7a8bda fixed:修复富文本无法编辑的bug 2024-05-09 10:12:20 +08:00
PiexlMax(奇淼 ab163fc0ef Update ci.yaml 2024-05-06 21:20:20 +08:00
pixelmaxQM 0265801830 fixed:表格模板删除查看详情功能。 2024-05-06 21:15:37 +08:00
pixelmaxQM 5fa7bbbc0f fixed:导出模板顺序一致性调整 2024-05-06 21:10:01 +08:00
PiexlMax(奇淼 e15351e1c5 Update README.md 2024-04-29 17:10:22 +08:00
pixelmaxQM 5eb0133b5c feature:升级后端依赖,前端代码调优 2024-04-28 22:05:50 +08:00
pixelmaxQM ff8344d671 feature:自动化代码增加数据源配置功能 2024-04-27 21:48:24 +08:00
pixelmaxQM d9a43ea1d6 feature:自动化代码增加数据源配置功能 2024-04-27 21:35:29 +08:00
pixelmaxQM 6c62d73afb feature:v2.6.3发布 2024-04-27 17:16:42 +08:00
pixelmaxQM d23d6c342a feature:自动化增加前端可控显隐。 2024-04-25 21:13:55 +08:00
pixelmaxQM 90549a09e1 feature:自动化增加前端可控显隐和自定义class。 2024-04-25 21:02:43 +08:00
pixelmaxQM 17d3639443 feature:优化api代码逻辑,调整pgsql下 more than once的bug。 2024-04-23 21:13:09 +08:00
pixelmaxQM e8a096b8fe feature:调整Update方法,不再使用save全量更新。 2024-04-23 20:56:30 +08:00
pixelmaxQM fea136e513 fixed:修改agent的数据库存储方式,防止日志过长存储失败。 2024-04-23 20:43:50 +08:00
pixelMax(奇淼 f3a4095462 增加丢失的vue-router 2024-04-23 14:45:56 +08:00
pixelmaxQM d7d25cf69b feature:调整抽屉的title属性,消除warning 2024-04-22 22:14:25 +08:00
pixelmaxQM 3b33fe83e6 feature:代码生成器可拖拽行,修复pgsql从库创建代码的bug,增加默认值属性。 2024-04-22 22:04:29 +08:00
pixelmaxQM 56f4d223ab fixed:适配pgsql编辑菜单触发 more than once的bug 2024-04-22 20:59:15 +08:00
pixelMax(奇淼 c07317a619 调整openDocument 2024-04-17 10:39:55 +08:00
Qing Liang 4798f7688b 点击跳转代码插件修复websotrm bug (#1715) 2024-04-17 10:35:32 +08:00
pixelmaxQM 24db0eb5aa fixed:切换角色功能适配新版gorm 2024-04-14 23:43:21 +08:00
Haoran Wu 1ece13c43d sys_export_template.go: 修复导入Excel时前几条为空数据的问题 (#1713) 2024-04-14 21:03:44 +08:00
pixelmaxQM 48268309b9 fixed:回滚时候自动清理掉router下空的作用域 2024-04-14 21:02:35 +08:00
4074a52b1b 修复pgsql和数据库导入bug,增加默认mod中的datatypes (#1712)
* 多文件: fix error specified more than once in PG

* Update sys_authority.go

* Update sys_authority.go

* sys_export_template.go: 导入Excel时使用批量插入提高性能

* 添加gorm.io/datatypes

---------

Co-authored-by: crazyrunsnail <fjyuan1991@163.com>
Co-authored-by: XiMo-210 <2831802697@qq.com>
2024-04-13 18:46:58 +08:00
pixelMax(奇淼 61dd524c26 fixed:修复根目录为O的错误 2024-04-12 14:54:28 +08:00
pixelMax(奇淼 61a678a318 调整无法创建根菜单的bug 2024-04-12 14:47:16 +08:00
pixelmaxQM 8ecec6a14f Merge branch 'fixed_menu'
# Conflicts:
#	server/service/system/sys_base_menu.go
#	web/src/view/superAdmin/menu/menu.vue
2024-04-11 21:27:44 +08:00
PiexlMax(奇淼andcrazyrunsnail e3f8b8a394 fixed:菜单删除功能细节调优 (#1705)
* 多文件: 菜单parentId由string改成uint

* sys_base_menu.go: fix table name "sys_base_menus" specified more than once (SQLSTATE 42712) (#1704)

---------

Co-authored-by: crazyrunsnail <fjyuan1991@163.com>
2024-04-11 21:17:49 +08:00
pixelmaxQM eebddbafcc feature:调整菜单删除逻辑 2024-04-11 21:15:35 +08:00
PiexlMax(奇淼 50cbce0f7a Merge pull request #1702 from crazyrunsnail/fix-model-idtype
多文件: 菜单parentId由string改成uint
2024-04-11 20:56:07 +08:00
fakedavid f9974a8971 sys_base_menu.go: fix table name "sys_base_menus" specified more than once (SQLSTATE 42712) (#1704) 2024-04-11 20:55:46 +08:00
crazyrunsnail baa48a1679 多文件: 菜单parentId由string改成uint 2024-04-11 16:38:09 +08:00
pixelmaxQM 032910f87a feature:插件自动化添加自动注册菜单和api的模板供给修改 2024-04-10 21:48:20 +08:00
pixelmaxQM 773ef95b70 feature:增加插件自动化的前端功能页面 后端api生成自动添加一个开放的接口示例 2024-04-10 21:24:46 +08:00
pixelmaxQM 18c2f84771 feature:表格导出功能增加join支持多表关联导出 2024-04-09 22:05:12 +08:00
pixelmaxQM ea721a71b5 feature:防止自动化代码中的路径穿越 2024-04-09 21:09:01 +08:00
pixelMax(奇淼 b1b7427c6e fixed:修复GHSA-gv3w-m57p-3wc4 2024-04-09 18:09:09 +08:00
pixelmaxQM 7bc7c30510 fixed:#1695 对资源属性进行检测 2024-04-07 21:35:40 +08:00
pixelmaxQM d1e055430d feature:前端自动化增加分包 2024-04-07 21:20:27 +08:00
pixelmaxQM 736534379a fixed:字典限制仅字符串模式可选择 2024-04-07 20:40:02 +08:00
pixelMax(奇淼 f6b71e157c fixed:升级postgres的驱动到1.5.7 2024-04-07 10:16:51 +08:00
ShuBo6 53e9f931f2 fix: 应该调用filepath.Ext的地方调用了path.Ext (#1696)
* fix: 应该调用filepath.Ext的地方调用了path.Ext

* fix: typo
2024-04-03 16:25:09 +08:00
PiexlMax(奇淼 c226a9476f Merge pull request #1694 from xue-ding-e/main
增加redis集群设置
2024-03-31 17:51:56 +08:00
pixelmaxQM 8ca5600286 feature: 修改版本号发布v2.6.2 2024-03-31 17:50:07 +08:00
薛定饿了不养喵 9729bc00a0 删掉了不必要的增加(就是之前说的那个样式bug其实不是bug)
style="width:194px"
2024-03-31 03:58:55 +08:00
xuedinge f9c9af8261 增加redis集群连接方式 2024-03-31 01:15:20 +08:00
薛定饿了不养喵 c9e64373fb Merge branch 'flipped-aurora:main' into main 2024-03-31 00:33:51 +08:00
pixelmaxQM 262febf486 feature:增加API导入导出,方便填写录入,暂时屏蔽自动书写导入的创建时间的功能,等待gorm合并pr后恢复。 2024-03-30 20:54:34 +08:00
pixelmaxQM eca9c4d7f8 fixed:#1692 统一前端操作模式 2024-03-29 20:53:19 +08:00
薛定饿了不养喵 a20083bb9c Merge branch 'flipped-aurora:main' into main 2024-03-28 01:59:21 +08:00
piexlMax d31b01415b feature:自动化代码支持基础菜单创建 2024-03-27 21:18:34 +08:00
piexlMax 26cdb472f0 feature:自动创建基本菜单 2024-03-27 20:55:03 +08:00
pixelMax(奇淼 3c2595aaf9 feature:调整jwt令牌过期展现形式和返回错误编码形式,方便用户重新登录和知晓错误原因。 2024-03-27 17:35:24 +08:00
PiexlMax(奇淼 ad323a0dff Update config.js 2024-03-27 16:48:22 +08:00
PiexlMax(奇淼 9a0417b1a7 Update server.go 2024-03-27 16:47:59 +08:00
PiexlMax(奇淼 3324a816ee Update README.md 2024-03-27 16:47:32 +08:00
piexlmax(奇淼 cc4135a2d7 feature:禁止选中角色的子以及后代节点 2024-03-27 10:45:19 +08:00
piexlmax(奇淼 2d884c83cc feature:调整jwt续期功能,保证新的token返回和一致性 2024-03-27 10:39:19 +08:00
piexlmax(奇淼 619c5b5e1d feature:消除icon缓存 2024-03-27 10:11:19 +08:00
傅鹏 33afa9adfc feat: ignore go debug bin file (#1687)
* fix: k8s cpu num

* feat: ignore go debug bin file
2024-03-25 13:38:40 +08:00
CC11001100 0c141cc6f7 fix: 应该调用filepath.Join的地方调用了path.Join (#1688) 2024-03-25 13:38:22 +08:00
傅鹏 efdff2ab0c fix: k8s cpu num (#1685) 2024-03-21 10:16:06 +08:00
hooraymanand胡浩 ebefb0eba4 feat: docker-compose healthz check (#1680)
Co-authored-by: 胡浩 <huhao@anesec.cn>
2024-03-20 15:08:39 +08:00
奇淼(piexlmax d4b9c2c97b loading增加定制属性,可自行配置 (#1683)
* feature:增加区域loading功能

* feature:增加修改loading配置
2024-03-20 10:44:21 +08:00
piexlMax d4db16b501 增加json model模板 2024-03-18 22:32:14 +08:00
piexlMax a5cbd7387b feature:#1647 自动化代码支持纯json 2024-03-18 22:25:50 +08:00
piexlMax 99d6b4a361 feature:#1671调整字典的value属性为string 自动化模板随改 2024-03-18 21:44:14 +08:00
piexlMax 18365689e6 调整字典的value属性为string 不再限制为int 2024-03-18 21:23:53 +08:00
piexlMax 8ea827412a 同步清理前端timer配置 2024-03-17 12:52:57 +08:00
piexlMax 4d8979c4ad 调整抽屉框格式 2024-03-16 00:29:03 +08:00
piexlMax 236357baa8 调整自动化代码的新增,编辑,删除为抽屉模式 2024-03-16 00:22:36 +08:00
piexlMax 5ef4b21858 增加表格导出多数据库支持 2024-03-15 23:22:20 +08:00
piexlmax(奇淼 3b3441851c feature:保证存在GVA默认结构的表格导入自动填充created和updated 2024-03-15 14:55:20 +08:00
piexlmax(奇淼 5517987e14 feature:保证表格导出的列顺序和json录入顺序一致 2024-03-15 11:38:34 +08:00
奇淼(piexlmax 7e98f61bec Revert "fix:保证excel每次导出列顺序一致 (#1677)" (#1678)
This reverts commit e2d360e8b1.
2024-03-15 11:23:21 +08:00
e2d360e8b1 fix:保证excel每次导出列顺序一致 (#1677)
* fix:保证excel每次导出列顺序一致

* Update sys_export_template.go

---------

Co-authored-by: Li vito <vito.li@shopee.com>
Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2024-03-15 11:16:24 +08:00
piexlmax(奇淼 57cfedfd7c typo修复,正则字符串修复 2024-03-14 14:50:41 +08:00
piexlMax 50b1d21d13 调整删除方法 放弃使用el-popover 删除操作使用弹窗提示 2024-03-13 22:37:07 +08:00
Alex Lin 7b79ec6c66 fixed: api管理中的 popover 触发改为 click (#1673) 2024-03-13 20:14:27 +08:00
piexlmax(奇淼 7a5b31530f 清理错误提交的测试代码 2024-03-13 16:25:01 +08:00
piexlmax(奇淼 e9100a9542 替换v-model API 为 defineModel 2024-03-13 15:17:57 +08:00
piexlmax(奇淼 421304d4a3 提升最低VUE版本至3.4.x 2024-03-13 14:49:28 +08:00
piexlMax 10d61bbbb3 导出功能可传入limit、offset和order,优先级高于默认配置 2024-03-12 21:08:25 +08:00
piexlmax(奇淼 095709fb19 fixed:退出只清理特定的localStorage 不再进行全部清理操作 2024-03-11 11:47:59 +08:00
piexlMax 725f4150fc 统一调整element form inline元素的属性 2024-03-07 22:35:02 +08:00
andywu1998 93ac2e70d2 feat(gin.Mode()): 由环境变量决定,不再依赖于配置文件system.Env (#1668)
* feat(gin.Mode()): 由环境变量决定,不再依赖于配置文件system.Env

* feat(casbin):不需要判断是否是debug模式,都要附带参数

* feat(前端配置):去掉环境之的配置
2024-03-06 23:38:17 +08:00
andywu1998 f4c2b50ca5 feat(config init log): modify constant value GIN_MODE to gin.Mode() (#1667) 2024-03-06 22:32:42 +08:00
piexlMax 3354edbb1c 在初始化时候添加excelTemplate的关联表 2024-03-06 22:22:27 +08:00
piexlMax d3600499af fixed:剔除错误提交的测试用代码 2024-03-06 00:19:17 +08:00
奇淼(piexlmax 35e2c2cfe8 feature:表格导出支持配置limit order和where条件 (#1665)
* feature:导出功能可携带条件

* feature:组件增加导出入参

* 带条件导出Excel功能
2024-03-05 23:29:59 +08:00
piexlmax(奇淼 1e4970469f publish:调整版本号v2.6.1 2024-03-04 17:06:34 +08:00
piexlMax 4cbfd7c9b1 消除el-descriptions的warning 2024-03-03 17:35:38 +08:00
piexlmax(奇淼 deb32fa0fa fixed:#1659 调整table中sort驼峰对应不到数据库字段的问题 2024-03-01 11:04:52 +08:00
piexlmax(奇淼 f17a738780 fix:switchTab,query=>params。 2024-03-01 10:04:18 +08:00
piexlmax(奇淼 2f53d0efae feat:增加全局订阅switchTab,用户可以给tab添加参数跳转且不打开新tab。 2024-02-29 18:06:29 +08:00
piexlmax(奇淼 3e43c2e6e6 type:限=>线 2024-02-29 16:34:53 +08:00
piexlmax(奇淼 96919a9f79 fix:取消重复方法 2024-02-29 16:34:08 +08:00
piexlmax(奇淼 a23fb627cd feat:#1658 增加全局订阅 setQuery 用户可以动态变更当前页query而不产生新的tab 2024-02-29 16:32:09 +08:00
Leo Wang aae915b092 fix: 解决build时,会有浏览器缓存问题。 (#1661) 2024-02-29 16:26:45 +08:00
Leo Wangand奇淼(piexlmax 160c8e814b bugfix:修复新增菜单强制默认icon的bug;修复production环境变量容易造成困惑的问题; (#1660)
* fix: 解决每次新增菜单时,默认都会选中第一个aim图标的问题

* fix: 修订production的环境变量,去除那些在生产环境会造成困惑的项目

* Update .env.production

合理性调整

---------

Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2024-02-29 16:18:07 +08:00
piexlmax(奇淼 88f48baba1 feat:#1658 增加全局订阅 setQuery 用户可以动态变更当前页query而不产生新的tab 2024-02-29 16:08:36 +08:00
piexlMax 7835efbf37 调整自动化代码模板的格式 2024-02-26 23:09:51 +08:00
shan_lu 455fbb505e fix the word spelling error for acitveAxios (#1655) 2024-02-23 11:31:15 +08:00
ipanghuand陶正虎 df3f7090e8 fix:修改操作记录中间件,文件类型统一记录为[文件] (#1656)
Co-authored-by: 陶正虎 <zhenghu.tao@jutze.com.cn>
2024-02-23 11:30:52 +08:00
piexlmax(奇淼 632b3c200c 调整cookie写入方法 2024-02-22 13:43:53 +08:00
piexlmax(奇淼 5b203326ae 清理文本输出之前的sleep 2024-02-21 17:44:25 +08:00
piexlMax fd87aca57d 前端跟随增加cookie管理方法,防止后端无法设置cookie的情况下导致的某些功能不可用问题。 2024-02-20 16:06:08 +08:00
piexlMax b9b7e556e3 修复表格导出和模板导出的bug 2024-02-20 15:06:17 +08:00
piexlmax(奇淼 5b9a7e6a2a 调整mssql自动化 2024-02-19 10:48:38 +08:00
rightSun d278e19367 dictionary.js update 优化字典文字展示方法 (#1652)
优化字典文字展示方法
新增可配置的参数 keyCode 和valueCode 使用字典字段更加灵活
2024-02-07 17:40:01 +08:00
piexlmax(奇淼 342030ca47 Merge remote-tracking branch 'origin/main' 2024-02-04 17:47:43 +08:00
piexlmax(奇淼 f90e241cc9 调整mssql初始化 2024-02-04 17:47:30 +08:00
lenandwangshenglei ff6bcc437c [Bug]:修复使用页面内按钮权限不生效问题 (#1621)
Co-authored-by: wangshenglei <wangshenglei@zhuanzhuan.com>
2024-02-04 11:36:06 +08:00
G-XD e32dbb5125 fix: 更新api路径时使用binary区分大小写进行查询 (#1645)
* fix: 更新api路径时使用`binary`区分大小写进行查询

* fix: 修改相同api路径判断逻辑
2024-02-04 11:35:45 +08:00
Sonui 06701cc7d1 fix(Server): 静态资源目录错误 (#1648) 2024-02-04 11:33:29 +08:00
piexlMax ccb31bc1b4 调整静态文件读取 防止文件目录暴露 2024-01-30 23:33:38 +08:00
myface-wang 7e2e29e2e8 Update svgIcon.vue (#1642)
删除不必要的导入,defineProps
2024-01-30 22:56:02 +08:00
piexlMax 0c76995f6d 修复菜单select展示的bug 2024-01-30 22:43:22 +08:00
piexlMax 17cab8fcb7 更换版本号 2024-01-28 16:40:08 +08:00
Krane d64753859d 解决drawer和dialog层级冲突问题 (#1640) 2024-01-26 14:35:54 +08:00
piexlMax 48b1c24c95 [fixed]:修复超出26个英文字母后表格的导出导入bug 2024-01-25 19:41:52 +08:00
piexlmax(奇淼 7e5e12b139 前端dist代码位置变更 2024-01-25 16:14:28 +08:00
59ba4752c0 [server/config/system.go]: fix a comment error (#1638)
Co-authored-by: liweiwu.wlw <liweiwu.wlw@xiaoman.cn>
Co-authored-by: 奇淼(piexlmax <303176530@qq.com>
2024-01-24 16:01:36 +08:00
piexlmax(奇淼 189219c673 typo修复 2024-01-24 15:59:02 +08:00
piexlmax(奇淼 4b54262edd Merge remote-tracking branch 'origin/main' 2024-01-24 15:50:37 +08:00
piexlmax(奇淼 458b035a7e 增加字典方法 GetDictionaryList,GetDictionaryListByType,GetDictionaryInfoByValue, GetDictionaryInfoByTypeValue 方便后台直接获取字典属性
前端增加extend属性获取
2024-01-24 15:50:15 +08:00
piexlmax(奇淼 abe85cf6e7 增加字典方法 GetDictionaryList,GetDictionaryListByType,GetDictionaryInfoByValue, GetDictionaryInfoByTypeValue 方便后台直接获取字典属性
前端增加extend属性获取
2024-01-24 15:50:14 +08:00
奇淼(piexlmax 379b63275a 自动化代码可选不使用官方global.Model结构 (#1637)
* 自动化代码可自行选择默认结构

* 调整自动化模板

* 增加自动化模板中对timer的处理
2024-01-22 17:37:03 +08:00
yunshengand01471076 07cde689b1 mongo use auth-source (#1635)
Co-authored-by: 01471076 <yangyunsheng.it@haier.com>
2024-01-20 12:33:27 +08:00
薛定饿了不养喵 26606f138d 修改样式 系统工具 -> 代码生成器 下的 '点这里从现有数据库创建代码' 内容显示不全 (#1632) 2024-01-20 12:33:10 +08:00
haimait 24c4774f7e 代码打包的插件,从后台安装失败,认不到目录的问题 (#1634)
https://github.com/flipped-aurora/gin-vue-admin/issues/1633
2024-01-20 12:32:59 +08:00
xuedinge 6bbb4f577e 修改样式 系统工具 -> 代码生成器 下的 '点这里从现有数据库创建代码' 内容显示不全 2024-01-18 18:48:47 +08:00
piexlMax(奇淼 4890de5052 Merge remote-tracking branch 'upstream/main' 2024-01-18 13:16:52 +08:00
haimait 746af37899 解决打包插件解压报错的问题 (#1630)
https://github.com/flipped-aurora/gin-vue-admin/issues/1628
2024-01-18 13:16:39 +08:00
haimait 0136ce7034 解决打包插件解压报错的问题 (#1630)
https://github.com/flipped-aurora/gin-vue-admin/issues/1628
2024-01-18 13:11:54 +08:00
piexlMax(奇淼 5ca47a91a6 开发环境允许设置cookie 2024-01-18 13:08:14 +08:00
piexlMax(奇淼 d849df41be 代码定位功能默认关闭。 2024-01-17 16:05:32 +08:00
奇淼(piexlmax a2269bcdd5 优化自定义图标功能,修复上线后无法展示自定义图标的问题。 (#1625)
* 修改自定义图标使用方式

* 优化自定义icon功能
2024-01-16 22:07:59 +08:00
ipanghu d41394c70a fix:修复首页 dashboard echarts 页面变化导致的变性, 修复 vite.config.js 中 proxy 代理 https 时的 bug (#1623)
add : 添加 “@vueuse/core” 的依赖,添加全局 hooks (use-windows-resize)使用方法查看 dashboard 页面
2024-01-15 13:10:42 +08:00
haimait 756f73bbd2 修复代码生成器 列表中数据库字段描述回显错误 (#1622) 2024-01-15 13:10:24 +08:00
piexlMax 95eb17b6d1 [fixed]:默认注释静态资源 2024-01-13 12:38:07 +08:00
piexlMax 773fd1f221 Merge remote-tracking branch 'origin/main' 2024-01-13 12:09:49 +08:00
piexlMax c95b436680 [fixed]:静态资源基础path修复 2024-01-13 12:08:37 +08:00
piexlmax(奇淼 3f47151678 默认增加 gorm.io/datatypes 2024-01-11 15:57:20 +08:00
piexlmax(奇淼 dbf1e7136c 调整更换绑定后的自动化代码结构 2024-01-11 15:56:38 +08:00
piexlMax 84b44eef91 [fixed]:调整过期token更换new-token时间,防止多机操作业务延迟带来的token失效的bug 2024-01-08 21:50:25 +08:00
piexlMax 686ada1e49 [fixed]:调整自动化表格TemplateInfo字段类型为text防止超长 2024-01-08 19:55:21 +08:00
piexlmax(奇淼 d9f6bc058a 调整自动化验证为gin自带binding tag 2024-01-08 16:45:19 +08:00
piexlMax 6f531793f0 清理错误添加的测试代码 2024-01-06 17:26:39 +08:00
piexlMax f8794bef5f 导入导出功能性bug修复 2024-01-06 16:16:50 +08:00
piexlMax 9daf103669 增加导入成功回调 2024-01-05 23:27:59 +08:00
piexlMax 26fa047fe4 基于cookie模式下,剔除所有的上传文件的x-token头 2024-01-05 23:13:04 +08:00
piexlMax 1a04bb535d 增加表格导入,增加导入模板下载,修复日志中间件的截断bug。 2024-01-05 23:09:07 +08:00
piexlMax ad41b7b434 [fixed]:初始化添加自动导出功能表 2024-01-05 21:28:57 +08:00
piexlMax 6791b1dbdc [fixed]:修复切换角色不生效的bug 2024-01-05 20:11:16 +08:00
piexlMax dc1b84130c 完善cookie模式 2024-01-04 22:25:13 +08:00
piexlMax e70a933090 完善cookie模式 2024-01-04 22:21:37 +08:00
piexlMax 08617f2fd4 完善cookie模式 2024-01-04 22:15:54 +08:00
thy 8aefb5db8e feat: 开启recovery (#1615) 2024-01-04 17:06:52 +08:00
piexlMax(奇淼 e1d214b91a 剔除GPT相关功能,后续独立新开源项目制作。 2024-01-03 14:49:28 +08:00
奇淼(piexlmax 0285de50e5 修复#1611提及的反引号导致pgsql搜索不支持的bug (#1612) 2024-01-03 11:21:08 +08:00
piexlmax(奇淼 5f7caa0535 降低自定义button圆角权重 2024-01-02 17:23:34 +08:00
piexlMax df9d2bc472 消除误提交的测试代码 2023-12-31 20:22:47 +08:00
piexlMax a923b6beca 更新版本号2.5.9 2023-12-31 11:09:00 +08:00
piexlMax 760ef77593 消除误提交的测试代码 2023-12-30 20:49:24 +08:00
piexlMax 64f703856e 调整初始化信息 2023-12-30 20:42:24 +08:00
piexlMax 36eba36bf2 调整建表结构 2023-12-30 20:32:05 +08:00
piexlMax 332176a619 增加新功能初始化数据 2023-12-30 20:29:49 +08:00
piexlMax cd5b60c8cc 增加excel模板配置,增加前端导出工具。token迁移至cookie x-token且保留header x-token 两者兼容。 2023-12-30 20:29:47 +08:00
thy 5ea8947fc9 feat: 将placeholder替换为tooltip (#1606) 2023-12-28 09:55:32 +08:00
piexlmax(奇淼 474a5c8e42 清理RemoveTaskByName的重复锁导致系统卡死的问题 2023-12-26 09:53:26 +08:00
piexlMax 952062d5be 调整弹出气泡无法取消的bug 2023-12-25 22:04:27 +08:00
奇淼(piexlmax c071564de4 Update ci.yaml 2023-12-25 10:26:50 +08:00
huoxiao b2fa7563d3 web/src/view/routerHolder.vue:Fix Vue warn Component inside <Transition> renders non-element root node that cannot be animated (#1603) 2023-12-25 09:53:45 +08:00
piexlmax(奇淼 22f68fedcb swag文档默认属性调整 2023-12-22 10:27:51 +08:00
奇淼(piexlmax 0a4cda26cb Update README.md 2023-12-22 10:19:21 +08:00
piexlmax(奇淼 54eb84e690 将task拆分为cron与task两部分,使其呈现父子结构,切单独可控。 2023-12-22 10:08:54 +08:00
piexlmax(奇淼 5cd33814fc 将task拆分为cron与task两部分,使其呈现父子结构,切单独可控。 2023-12-22 09:55:57 +08:00
奇淼(piexlmax 90f19679e7 Update README.md 2023-12-19 17:26:43 +08:00
奇淼(piexlmax 38ade9e498 Update go.mod 2023-12-19 17:26:07 +08:00
faker-wang 4c77831001 zapcore.WarnLevel修改成zapcore.ErrorLevel (#1602) 2023-12-19 17:17:02 +08:00
piexlMax 722fe556e3 增加初始字典内容,兼容自动化使用pgsql读取表的数据格式 2023-12-17 17:40:00 +08:00
xinyan818 98b83c0078 fix: typo (#1591) 2023-12-13 15:01:35 +08:00
yang hao 85646b5ffa sys_auto_code fix: log文字提示错误 (#1592) 2023-12-13 15:01:19 +08:00
Zmzblink d3f5ed7d60 修改el-tag中type属性的默认值, type属性枚举不包含primary (#1593) 2023-12-13 15:01:02 +08:00
piexlmax(奇淼 e0a8e93704 定位功能调整为 shift+alt+左键 2023-12-12 10:50:23 +08:00
piexlmax(奇淼 3aab5e9eb7 路由增加meta.client 属性,添加本属性为true则跳过前端路由拦截器 2023-12-11 16:28:40 +08:00
piexlMax 2307ac88cb 对不合法名称icon做检测停止加入图标库 2023-12-10 23:47:30 +08:00
piexlMax 0f2018d913 增加自动icon功能,所有添加到web/assets/icons下的svg会自动被注册为icon组件使用【可用于menu和直接用标签加文件名使用】 2023-12-10 23:30:39 +08:00
piexlMax 8b9a75ba5a 清除ast中无用的测试用例 2023-12-06 23:06:43 +08:00
SliverHorn 7872e0ce38 Update: 修复mongodb index默认name太长而导致创建索引失败, 在组装keys数据进行排序以便符合bson.M的构造 (#1580) 2023-12-05 23:04:23 +08:00
xinyan818 d28ac73987 fix: use db list (#1588) 2023-12-05 23:03:54 +08:00
ning2510 2c8c7a8b51 Update ast_gorm_test.go (#1587) 2023-12-05 23:01:50 +08:00
奇淼(piexlmaxandyang hao f98fc6bf3c swag中id错误问题的修复 (#1589)
* model.go: fix swagger请求的id问题

* 验证码逻辑修回

---------

Co-authored-by: yang hao <1013055366@qq.com>
2023-12-05 23:01:27 +08:00
奇淼(piexlmax d42958734f 清理掉定时任务config配置,修改为代码内书写 (#1584) 2023-12-04 22:12:01 +08:00
piexlMax 6f2c785713 调整为新的timer模式 增加每个task内具体方法的描述方便对接后续的页面控制 2023-12-04 22:12:01 +08:00
奇淼(piexlmax 05a1ce51f0 清理掉定时任务config配置,修改为代码内书写 (#1584) 2023-12-04 22:05:19 +08:00
piexlMax(奇淼 58b063770d 调整swag 2023-12-01 14:42:54 +08:00
Sun Dadiand奇淼(piexlmax 6f9a83709d [selectImage.vue]: 增加selectImage组件,最多上传图片的功能 (#1568)
* 增加selectImage组件,最多上传图片的功能

* 增加selectImage组件,最多上传图片的功能

* Update config.yaml

* Update config.yaml

---------

Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2023-11-21 22:54:39 +08:00
奇淼(piexlmax bd2a3ddfba 清理掉定时任务config配置,修改为代码内书写 (#1578) 2023-11-20 23:38:15 +08:00
SliverHorn 4433b6623e Merge pull request #1577 from flipped-aurora/transaction
[feature]:调整角色的创建,删除操作为事务模式
2023-11-19 14:17:30 +08:00
piexlMax e1ca17637f casbin相关操作修改为事务 2023-11-19 11:50:30 +08:00
ZongQiangandzongqiang 5ef0e43312 缺少事务可能出现数据不一致风险 (#1575)
Co-authored-by: zongqiang <zongq@rp-pet.cn>
2023-11-18 22:20:26 +08:00
aa6ca95f13 初始化失败panic (#1573)
* 初始化失败panic

* Update gorm_mssql.go

* Update gorm_pgsql.go

* Update gorm_mysql.go

---------

Co-authored-by: zongqiang <zongq@rp-pet.cn>
Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2023-11-18 21:01:44 +08:00
奇淼(piexlmax 2523467c97 Update LICENSE 2023-11-15 16:31:51 +08:00
奇淼(piexlmax b38eb6709e Update README.md 2023-11-14 10:38:25 +08:00
piexlMax(奇淼 f6dacdb046 优化自动化模板,防止按钮被折叠 2023-11-13 11:03:51 +08:00
piexlMax(奇淼 cc0903f74d 调整表格的适配性 2023-11-13 10:53:30 +08:00
piexlMax(奇淼 72936fc0d1 版权信息不再和appName绑定 2023-11-07 14:48:22 +08:00
SliverHorn 4b7f552885 Merge pull request #1569 from 729343861/main
web/src/view/systemTools/system/system.vue : fix 图形验证码配置 bug
2023-11-06 11:25:17 +08:00
729343861 9b2fc60d79 web/src/view/systemTools/system/system.vue : fix 图形验证码配置 bug 2023-11-06 11:09:12 +08:00
piexlMax 3e5a26b95a 修改开发环境的中间件 2023-11-04 21:48:01 +08:00
SliverHorn a5f3139784 fixed: deploy/docker/Dockerfile #1518 (#1567) 2023-10-31 17:52:38 +08:00
piexlMax 9ca9fcdb09 SelectFile组件建议添加limit和accept属性,控制文件的上传数量和类型 2023-10-26 22:41:37 +08:00
piexlMax 913db35b23 添加分配菜单和分配api的滚动条 2023-10-26 22:26:04 +08:00
piexlMax(奇淼 8eba3ba4f7 修复字典展示bug
修复自动化代码字典类型渲染bug
2023-10-25 17:14:09 +08:00
piexlMax(奇淼 5bd0f24a84 增加package为空判断 #1564
Co-authored-by: @saikey0379
2023-10-24 18:42:50 +08:00
piexlMax e8fb3180db 字典使用模式调整 2023-10-22 20:59:47 +08:00
SliverHorn 232aaad090 feature: mongoDB 支持 (#1565)
* feature: mongoDB 支持

* update: 修改错误mongo索引示例

* update: 在system添加use-mongo 开关开启或者关闭mongo
2023-10-14 13:59:30 +08:00
piexlMax(奇淼 b3dbb8d4be 解决打包错误的bug 2023-10-10 18:57:46 +08:00
piexlMax(奇淼 3caf070056 优化打包 2023-10-10 18:52:39 +08:00
piexlMax(奇淼 1e24bbc3e2 修复UI BUG #1562 2023-10-10 16:46:08 +08:00
18694fd24a 修复富文本bug,调整日志输出,修复已知bug (#1563)
* operation.go: fix 调用respPool.Put归还对象前清空对象会造成下次调用无法正常copy
redis.go: fix NewDefaultRedisStore方法实例化的对象直接使用会造成空指针异常

* 设置为发布模式时gin不再输出路由信息log

* 调整基础配置文件

* 修复富文本不回显的bug

---------

Co-authored-by: liuyahui <liuyahui@wjacloud.com>
Co-authored-by: Alan <alan.cd@qq.com>
Co-authored-by: wuqianang <wuqianang@163.com>
Co-authored-by: task <ms.yangdan@gmail.com>
2023-10-10 16:41:55 +08:00
piexlMax(奇淼 10dc003f20 修复富文本未创建前端输入字段的bug 2023-10-10 16:04:23 +08:00
809e2496c0 增加了自动化视频以及修复了一些已知漏洞 (#1554)
* 解决bug:为角色添加权限时,如果有重复api权限,该角色所有权限均添加失败(包括登录)

* 支持视频上传和选用。视频首帧作为封面预览。

* 增加自动化代码视频选项

* 媒体库可上传视频
SelectImage组件增加参数file-type="image|video" 方便用户对媒体库可选内容做限制

* 自动化代码bug修复

---------

Co-authored-by: liuyahui <liuyahui@wjacloud.com>
Co-authored-by: Alan <alan.cd@qq.com>
2023-09-24 15:49:58 +08:00
奇淼(piexlmax c1c37901b5 Merge pull request #1550 from Suixinee/main
初步构建了文件预览组件
2023-09-24 14:50:57 +08:00
SliverHorn 44e115f211 update: 去掉 github.com/lestrrat-go/file-rotatelogs 库的依赖, 实现zap.String("business", "xx")自定义业务日志数据路径, 输出文件路径为./global.GVA_CONFIG.Zap.Director/2006-01-02/xx/level(debug, info, warn, error, dpanic, panic, fatal).log (#1551) 2023-09-24 14:35:54 +08:00
ipanghu 217cc338a8 fix:修复自动代码的详情页面的bug,选择器,输入框添加 中文名称 (#1552) 2023-09-24 14:35:34 +08:00
suixinee 81eccc177d 初步文件预览组件构建 2023-09-22 10:47:16 +08:00
碎心Eand奇淼(piexlmax 4ea00b7e7b 调整了文件上传无法异步赋值的bug (#1547)
* watch了immediate参数,防止组件异步调用获取数据失败的问题

* watch添加了immediate参数,防止组件异步调用获取不到数据

* Update selectFile.vue

---------

Co-authored-by: 奇淼(piexlmax <qimiaojiangjizhao@gmail.com>
2023-09-19 16:08:35 +08:00
奇淼(piexlmax 844699263d Update selectFile.vue 2023-09-19 16:08:14 +08:00
suixinee d72a548852 watch添加了immediate参数,防止组件异步调用获取不到数据 2023-09-19 12:05:21 +08:00
suixinee 722ed9bece watch了immediate参数,防止组件异步调用获取数据失败的问题 2023-09-19 11:57:41 +08:00
SummerBytes 8852367837 codeServer: fix 删除无用代码 (#1541) 2023-09-15 11:50:10 +08:00
piexlMax 5559f9b9af 调整模板代码中的中文解释 2023-09-15 11:44:16 +08:00
piexlMax 39526edafd 代码模板调整name挂载位置 2023-09-14 18:49:59 +08:00
piexlMax a4f86354d3 前端eslint调整 2023-09-14 18:47:47 +08:00
piexlMax 636204bbaf 修复eslint问题 2023-09-14 18:06:26 +08:00
piexlMax 08494834c2 字典增加扩展值字段,优化自动化生成搜索条件默认语句。 2023-09-13 21:33:36 +08:00
piexlMax 2247f34b89 修改富文本组件默认高度 2023-09-13 11:42:25 +08:00
tomoyo233 5c84412238 Update sys_user.go (#1540)
验证码bug修复
2023-09-10 23:03:14 +08:00
piexlMax 13112aa4d8 调整table模板让详情功能适配多种数据类型 2023-09-10 00:27:46 +08:00
zyw0605688 836a165ff2 bugfix:'升级gin-swagger到最新版,解决swagger-ui界面上的Cannot read properties of undefined (reading '1')报错' (#1538) 2023-09-09 13:17:17 +08:00
piexlMax f36e0b39ed 修复#1537 环境变量不生效的问题 2023-09-09 11:25:55 +08:00
Jamieandmazhenming 74e2ec74e0 resource/autocode_template/server/model.go.tpl: swag文档添加中文信息 (#1535)
Co-authored-by: mazhenming <zhenmingma@qq.com>
2023-09-09 11:03:04 +08:00
zyw0605688 bcb6243f3b 批量删除按钮,未选中内容时不应该弹出确定提醒 (#1536)
table的批量删除按钮,未选中内容时候,悬浮提醒不应该显示。增加以下代码
:disabled="!multipleSelection.length"
2023-09-09 11:02:26 +08:00
piexlMax c00b628fb4 [fixed]修复业务库生成代码回滚时候删表位置错误的bug。 2023-09-08 16:54:41 +08:00
piexlMax 2fbed17ebc [fixed]修复业务库生成代码gorm自动迁移数据库name错误的问题。 2023-09-08 16:37:58 +08:00
Felixs 7bfea23958 补充form.vue里遗漏的SelectFile自动化组件 2023-09-06 11:40:30 +08:00
Felixs 91c567779b 恢复 btnAuth 2023-09-05 11:00:36 +08:00
piexlMax c4aeeb419e [update] 清理了一些无用文件 2023-08-31 18:17:06 +08:00
piexlMax 7808fbb852 [fixed] 修复富文本未引入watch的问题 #1528 2023-08-31 12:27:18 +08:00
wangzmgitandwangziming 9e64bd474b fix: 修复菜单折叠后再次展开右侧出现遮罩的问题 (#1529)
Co-authored-by: wangziming <wangziming@emsys.cn>
2023-08-31 11:18:19 +08:00
lubinandlubin 25a6aef472 修复接口实现问题 (#1524)
Co-authored-by: lubin <lubin@xiaoduotech.com>
2023-08-30 14:33:52 +08:00
zyw0605688 203ba521b0 分页列表接口,兼容不分页获取全部数据 (#1526)
* feat:'分页列表接口,兼容不分页获取全部数据'
2023-08-29 20:08:22 +08:00
piexlMax 287685420a [fixed]: 枚举类型条件查询的错误 #1525 2023-08-29 16:17:56 +08:00
piexlMax a6ae0ba4b0 富文本组件增加watch防止富文本无法加载的情况发生。 2023-08-23 18:12:03 +08:00
andywu1998 208346a480 优化config字段排列,使得内存对齐 (#1521)
* 调整字段顺序,解决config包内存对齐问题

* fix(config):修复内存对齐
2023-08-21 18:14:44 +08:00
DaMingandCodeLucifer 0d5c55a3b5 修复上次提交忘记把文件类型下载按钮描述的{{}}设置为字符串 (#1520)
* 修复上次提交忘记把文件类型下载按钮描述的{{}}设置为字符串
---------

Co-authored-by: CodeLucifer <tender_zhu@foxamil.com>
2023-08-19 22:49:29 +08:00
piexlMax 4fcd2bf14c 修改静态目录打包的配置 2023-08-19 11:29:44 +08:00
SDProgrammerandShi Dapeng deea1aba7f fix:修复不同系统环境下创建db文件路径错误的问题 (#1512)
Co-authored-by: Shi Dapeng <shidapeng@cmdi.chinamobile.com>
2023-08-17 17:23:14 +08:00
zhanghaixin246 3e31666cd6 feat:抛出上传文件错误信息 (#1515) 2023-08-17 17:22:50 +08:00
DaMingandCodeLucifer 197c4c46ab 生成代码模版文件新增了查看详情、详情内图片预览、文件下载 (#1516)
* 生成代码模版文件新增了查看详情、详情内图片预览、文件下载
前端工具文件主要修改了utils>format.js

* 替换富文本组建路径

---------

Co-authored-by: CodeLucifer <tender_zhu@foxamil.com>
2023-08-17 17:22:30 +08:00
奇淼(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
306 changed files with 16049 additions and 9543 deletions
+5 -5
View File
@@ -48,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.18]
go-version: [1.22]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
@@ -84,7 +84,7 @@ jobs:
strategy:
matrix:
node-version: [16.8.0]
go-version: [1.18]
go-version: [1.22]
steps:
- name: Check out branch
uses: actions/checkout@v2
@@ -111,7 +111,7 @@ jobs:
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
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 }}
@@ -195,7 +195,7 @@ jobs:
strategy:
matrix:
node-version: ['16.x']
go-version: ['1.18']
go-version: ['1.22']
steps:
- uses: actions/checkout@v2
- name: tag major and minor versions
@@ -220,7 +220,7 @@ jobs:
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
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 }}
+1
View File
@@ -25,6 +25,7 @@ yarn-error.log*
/server/log/
/server/gva
/server/latest_log
/server/__debug_bin*
*.iml
web/.pnpm-debug.log
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 JiZhao Jiang
Copyright 2019 北京翻转极光科技有限责任公司
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+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)
+24 -40
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.20-blue"/>
<img src="https://img.shields.io/badge/gin-1.9.1-lightBlue"/>
<img src="https://img.shields.io/badge/vue-3.3.4-brightgreen"/>
<img src="https://img.shields.io/badge/element--plus-2.3.8-green"/>
<img src="https://img.shields.io/badge/gorm-1.25.2-red"/>
</div>
[English](./README-en.md) | 简体中文
@@ -42,9 +42,11 @@
3.您完全可以通过我们的教程和文档完成一切操作,因此我们不再提供免费的技术服务,如需服务请进行[付费支持](https://www.gin-vue-admin.com/coffee/payment.html)
4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,其余信息功能不做任何限制。如需剔除请[购买授权](https://www.gin-vue-admin.com/empower/index.html)
4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,以及日志和代码中所包含的版权声明信息。所需保留信息均为文案性质,不会影响任何业务内容,如决定商用且必须剔除请[购买授权](https://www.gin-vue-admin.com/empower/index.html)
<img src="https://qmplusimg.henrongyi.top/%E6%8E%88%E6%9D%83.png" width="1000">
<img src="https://qmplusimg.henrongyi.top/openSource/login.jpg" width="1000">
<img src="https://qmplusimg.henrongyi.top/openSource/dashboard.jpg" width="1000">
## 1. 基本介绍
@@ -66,8 +68,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 规范
@@ -83,7 +85,7 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm
```
- node版本 > v16.8.3
- golang版本 >= v1.16
- golang版本 >= v1.22
- IDE推荐:Goland
```
@@ -125,29 +127,8 @@ npm run serve
#### 2.3.1 安装 swagger
##### (1)可以访问外国网站
````
go get -u github.com/swaggo/swag/cmd/swag
````
##### (2)无法访问外国网站
由于国内没法安装 go.org/x 包下面的东西,推荐使用 [goproxy.cn](https://goproxy.cn) 或者 [goproxy.io](https://goproxy.io/zh/)
```bash
# 如果您使用的 Go 版本是 1.13 - 1.15 需要手动设置GO111MODULE=on, 开启方式如下命令, 如果你的 Go 版本 是 1.16 ~ 最新版 可以忽略以下步骤一
# 步骤一、启用 Go Modules 功能
go env -w GO111MODULE=on
# 步骤二、配置 GOPROXY 环境变量
go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct
# 如果嫌弃麻烦,可以使用go generate 编译前自动执行代码, 不过这个不能使用 `Goland` 或者 `Vscode` 的 命令行终端
cd server
go generate -run "go env -w .*?"
# 使用如下命令下载swag
go get -u github.com/swaggo/swag/cmd/swag
``` shell
go install github.com/swaggo/swag/cmd/swag@latest
```
#### 2.3.2 生成API文档
@@ -314,8 +295,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. 知识库
@@ -352,17 +333,20 @@ swag init
### 7.1 技术群
### QQ交流群:622360840
| QQ 群 |
| :---: |
| <img src="http://qmplusimg.henrongyi.top/qq.jpg" width="180"/> |
### QQ交流群:470239250
### 微信交流群
| 微信 |
| :---: |
| <img width="150" src="http://qmplusimg.henrongyi.top/qrjjz.png">
添加微信,备注"加入gin-vue-admin交流群"
防止广告进群,添加微信,输入以下代码执行结果(请勿转码为string)
```
str := "5Yqg5YWlR1ZB5Lqk5rWB576k"
decodeBytes, err := base64.StdEncoding.DecodeString(str)
fmt.Println(decodeBytes, err)
```
### [关于我们](https://www.gin-vue-admin.com/about/join.html)
+14 -2
View File
@@ -41,8 +41,10 @@ services:
ports:
- '8888:8888'
depends_on:
- mysql
- redis
mysql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ../../server:/server
working_dir: /server # 如果docker 设置了workdir 则此处不需要设置
@@ -67,6 +69,11 @@ services:
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "gva", "-pAa@6447985"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- mysql:/var/lib/mysql
networks:
@@ -80,6 +87,11 @@ services:
restart: always
ports:
- '16379:6379'
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG || exit 1"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- redis:/data
networks:
+14 -2
View File
@@ -38,8 +38,10 @@ services:
ports:
- '8888:8888'
depends_on:
- mysql
- redis
mysql:
condition: service_healthy
redis:
condition: service_healthy
links:
- mysql
- redis
@@ -59,6 +61,11 @@ services:
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "gva", "-pAa@6447985"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- mysql:/var/lib/mysql
networks:
@@ -71,6 +78,11 @@ services:
restart: always
ports:
- '16379:6379'
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG || exit 1"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- redis:/data
networks:
+1 -1
View File
@@ -9,7 +9,7 @@ RUN set -ex \
&& echo "LANG=en_US.utf8" > /etc/locale.conf \
&& echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf \
&& echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf \
&& yum -y install yum -y install *epel* \
&& yum -y install epel-release \
&& yum -y localinstall http://mirrors.ustc.edu.cn/mysql-repo/mysql57-community-release-el7.rpm \
&& yum -y install mysql-community-server git redis nginx go npm --nogpgcheck && chmod +x ./entrypoint.sh \
&& npm install -g yarn && go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct \
+1
View File
@@ -17,6 +17,7 @@ type ApiGroup struct {
AutoCodeHistoryApi
DictionaryDetailApi
AuthorityBtnApi
SysExportTemplateApi
}
var (
+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 := casbinService.FreshCasbin()
if err != nil {
global.GVA_LOG.Error("刷新失败!", zap.Error(err))
response.FailWithMessage("刷新失败", c)
return
}
response.OkWithMessage("刷新成功", c)
}
+21 -17
View File
@@ -5,7 +5,6 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
@@ -25,26 +24,31 @@ type AuthorityApi struct{}
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "创建角色,返回包括系统角色详情"
// @Router /authority/createAuthority [post]
func (a *AuthorityApi) CreateAuthority(c *gin.Context) {
var authority system.SysAuthority
err := c.ShouldBindJSON(&authority)
if err != nil {
var authority, authBack system.SysAuthority
var err error
if err = c.ShouldBindJSON(&authority); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(authority, utils.AuthorityVerify)
if err != nil {
if err = utils.Verify(authority, utils.AuthorityVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if authBack, err := authorityService.CreateAuthority(authority); err != nil {
if authBack, err = authorityService.CreateAuthority(authority); err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败"+err.Error(), c)
} else {
_ = menuService.AddMenuAuthority(systemReq.DefaultMenu(), authority.AuthorityId)
_ = casbinService.UpdateCasbin(authority.AuthorityId, systemReq.DefaultCasbin())
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
return
}
err = casbinService.FreshCasbin()
if err != nil {
global.GVA_LOG.Error("创建成功,权限刷新失败。", zap.Error(err))
response.FailWithMessage("创建成功,权限刷新失败。"+err.Error(), c)
return
}
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
}
// CopyAuthority
@@ -93,22 +97,22 @@ func (a *AuthorityApi) CopyAuthority(c *gin.Context) {
// @Router /authority/deleteAuthority [post]
func (a *AuthorityApi) DeleteAuthority(c *gin.Context) {
var authority system.SysAuthority
err := c.ShouldBindJSON(&authority)
if err != nil {
var err error
if err = c.ShouldBindJSON(&authority); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(authority, utils.AuthorityIdVerify)
if err != nil {
if err = utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = authorityService.DeleteAuthority(&authority)
if err != nil { // 删除角色之前需要判断是否有用户正在使用此角色
// 删除角色之前需要判断是否有用户正在使用此角色
if err = authorityService.DeleteAuthority(&authority); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败"+err.Error(), c)
return
}
_ = casbinService.FreshCasbin()
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))
+46 -9
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))
@@ -67,6 +63,7 @@ func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
}
a.Pretreatment()
var apiIds []uint
var menuId uint
if a.AutoCreateApiToSql {
if ids, err := autoCodeService.AutoCreateApi(&a); err != nil {
global.GVA_LOG.Error("自动化创建失败!请自行清空垃圾数据!", zap.Error(err))
@@ -77,8 +74,17 @@ func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
apiIds = ids
}
}
a.PackageT = caser.String(a.Package)
err := autoCodeService.CreateTemp(a, apiIds...)
if a.AutoCreateMenuToSql {
if id, err := autoCodeService.AutoCreateMenu(&a); err != nil {
global.GVA_LOG.Error("自动化创建失败!请自行清空垃圾数据!", zap.Error(err))
c.Writer.Header().Add("success", "false")
c.Writer.Header().Add("msg", url.QueryEscape("自动化创建失败!请自行清空垃圾数据!"))
} else {
menuId = id
}
}
a.PackageT = utils.FirstUpper(a.Package)
err := autoCodeService.CreateTemp(a, menuId, apiIds...)
if err != nil {
if errors.Is(err, system.ErrAutoMove) {
c.Writer.Header().Add("success", "true")
@@ -182,10 +188,16 @@ func (autoApi *AutoCodeApi) CreatePackage(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
// PackageName可能导致路径穿越的问题 / 和 \ 都要防止
if strings.Contains(a.PackageName, "\\") || strings.Contains(a.PackageName, "/") || strings.Contains(a.PackageName, "..") {
response.FailWithMessage("包名不合法", c)
return
}
err := autoCodeService.CreateAutoCode(&a)
if err != nil {
global.GVA_LOG.Error("创建成功!", zap.Error(err))
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
} else {
response.OkWithMessage("创建成功", c)
@@ -247,6 +259,11 @@ func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
if strings.Contains(a.PlugName, "\\") || strings.Contains(a.PlugName, "/") || strings.Contains(a.PlugName, "..") {
response.FailWithMessage("插件名称不合法", c)
return
}
a.Snake = strings.ToLower(a.PlugName)
a.NeedModel = a.HasRequest || a.HasResponse
err = autoCodeService.CreatePlug(a)
@@ -266,7 +283,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 {
@@ -296,3 +313,23 @@ 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")
zipPath, err := autoCodeService.PubPlug(plugName)
if err != nil {
global.GVA_LOG.Error("打包失败!", zap.Error(err))
response.FailWithMessage("打包失败"+err.Error(), c)
return
}
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
}
+1 -1
View File
@@ -44,7 +44,7 @@ func (b *BaseApi) Captcha(c *gin.Context) {
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c)) // v8下使用redis
cp := base64Captcha.NewCaptcha(driver, store)
id, b64s, err := cp.Generate()
id, b64s, _, err := cp.Generate()
if err != nil {
global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
response.FailWithMessage("验证码获取失败", c)
+2 -21
View File
@@ -4,8 +4,6 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
@@ -118,31 +116,14 @@ func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.SysDictionarySearch true "页码, 每页大小, 搜索条件"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量"
// @Router /sysDictionary/getSysDictionaryList [get]
func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
var pageInfo request.SysDictionarySearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := dictionaryService.GetSysDictionaryInfoList(pageInfo)
list, err := dictionaryService.GetSysDictionaryInfoList()
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
response.OkWithDetailed(list, "获取成功", c)
}
+257
View File
@@ -0,0 +1,257 @@
package system
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"net/http"
)
type SysExportTemplateApi struct {
}
var sysExportTemplateService = service.ServiceGroupApp.SystemServiceGroup.SysExportTemplateService
// CreateSysExportTemplate 创建导出模板
// @Tags SysExportTemplate
// @Summary 创建导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysExportTemplate true "创建导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /sysExportTemplate/createSysExportTemplate [post]
func (sysExportTemplateApi *SysExportTemplateApi) CreateSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindJSON(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
verify := utils.Rules{
"Name": {utils.NotEmpty()},
}
if err := utils.Verify(sysExportTemplate, verify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.CreateSysExportTemplate(&sysExportTemplate); err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
} else {
response.OkWithMessage("创建成功", c)
}
}
// DeleteSysExportTemplate 删除导出模板
// @Tags SysExportTemplate
// @Summary 删除导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysExportTemplate true "删除导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /sysExportTemplate/deleteSysExportTemplate [delete]
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindJSON(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.DeleteSysExportTemplate(sysExportTemplate); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
} else {
response.OkWithMessage("删除成功", c)
}
}
// DeleteSysExportTemplateByIds 批量删除导出模板
// @Tags SysExportTemplate
// @Summary 批量删除导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
// @Router /sysExportTemplate/deleteSysExportTemplateByIds [delete]
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) {
var IDS request.IdsReq
err := c.ShouldBindJSON(&IDS)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.DeleteSysExportTemplateByIds(IDS); err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
} else {
response.OkWithMessage("批量删除成功", c)
}
}
// UpdateSysExportTemplate 更新导出模板
// @Tags SysExportTemplate
// @Summary 更新导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysExportTemplate true "更新导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /sysExportTemplate/updateSysExportTemplate [put]
func (sysExportTemplateApi *SysExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindJSON(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
verify := utils.Rules{
"Name": {utils.NotEmpty()},
}
if err := utils.Verify(sysExportTemplate, verify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.UpdateSysExportTemplate(sysExportTemplate); err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
} else {
response.OkWithMessage("更新成功", c)
}
}
// FindSysExportTemplate 用id查询导出模板
// @Tags SysExportTemplate
// @Summary 用id查询导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query system.SysExportTemplate true "用id查询导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /sysExportTemplate/findSysExportTemplate [get]
func (sysExportTemplateApi *SysExportTemplateApi) FindSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindQuery(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if resysExportTemplate, err := sysExportTemplateService.GetSysExportTemplate(sysExportTemplate.ID); err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
} else {
response.OkWithData(gin.H{"resysExportTemplate": resysExportTemplate}, c)
}
}
// GetSysExportTemplateList 分页获取导出模板列表
// @Tags SysExportTemplate
// @Summary 分页获取导出模板列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query systemReq.SysExportTemplateSearch true "分页获取导出模板列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysExportTemplate/getSysExportTemplateList [get]
func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gin.Context) {
var pageInfo systemReq.SysExportTemplateSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if list, total, err := sysExportTemplateService.GetSysExportTemplateInfoList(pageInfo); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
}
// ExportExcel 导出表格
// @Tags SysExportTemplate
// @Summary 导出表格
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportExcel [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) {
templateID := c.Query("templateID")
queryParams := c.Request.URL.Query()
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
if file, name, err := sysExportTemplateService.ExportExcel(templateID, queryParams); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx")) // 对下载的文件重命名
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
}
}
// ExportExcel 导出表格模板
// @Tags SysExportTemplate
// @Summary 导出表格模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportExcel [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplate(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
if file, name, err := sysExportTemplateService.ExportTemplate(templateID); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx")) // 对下载的文件重命名
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
}
}
// ExportExcel 导入表格
// @Tags SysImportTemplate
// @Summary 导入表格
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/importExcel [post]
func (sysExportTemplateApi *SysExportTemplateApi) ImportExcel(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
file, err := c.FormFile("file")
if err != nil {
global.GVA_LOG.Error("文件获取失败!", zap.Error(err))
response.FailWithMessage("文件获取失败", c)
return
}
if err := sysExportTemplateService.ImportExcel(templateID, file); err != nil {
global.GVA_LOG.Error(err.Error(), zap.Error(err))
response.FailWithMessage(err.Error(), c)
} else {
response.OkWithMessage("导入成功", c)
}
}
+3 -1
View File
@@ -4,6 +4,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
@@ -19,7 +20,7 @@ type JwtApi struct{}
// @Success 200 {object} response.Response{msg=string} "jwt加入黑名单"
// @Router /jwt/jsonInBlacklist [post]
func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
token := c.Request.Header.Get("x-token")
token := utils.GetToken(c)
jwt := system.JwtBlacklist{Jwt: token}
err := jwtService.JsonInBlacklist(jwt)
if err != nil {
@@ -27,5 +28,6 @@ func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
response.FailWithMessage("jwt作废失败", c)
return
}
utils.ClearToken(c)
response.OkWithMessage("jwt作废成功", 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{}
-1
View File
@@ -6,7 +6,6 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
+10 -6
View File
@@ -13,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"
)
@@ -49,7 +49,7 @@ func (b *BaseApi) Login(c *gin.Context) {
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
if !oc || store.Verify(l.CaptchaId, l.Captcha, true) {
if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) {
u := &system.SysUser{Username: l.Username, Password: l.Password}
user, err := userService.Login(u)
if err != nil {
@@ -91,10 +91,11 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
return
}
if !global.GVA_CONFIG.System.UseMultipoint {
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
return
}
@@ -105,10 +106,11 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
response.FailWithMessage("设置登录状态失败", c)
return
}
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
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))
@@ -124,10 +126,11 @@ func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
response.FailWithMessage("设置登录状态失败", c)
return
}
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
}
}
@@ -268,7 +271,8 @@ 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))
utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
response.OkWithMessage("修改成功", c)
}
}
+18 -1
View File
@@ -23,6 +23,22 @@ redis:
addr: 177.7.0.14:6379
password: ""
# mongo configuration
mongo:
coll: ''
options: ''
database: ''
username: ''
password: ''
min-pool-size: 0
max-pool-size: 100
socket-timeout-ms: 0
connect-timeout-ms: 0
is-zap: false
hosts:
- host: ''
port: ''
# email configuration
email:
to: xxx@qq.com
@@ -40,6 +56,7 @@ system:
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-mongo: false # 使用mongo
use-multipoint: false
# IP限制次数 一个小时15000次
iplimit-count: 15000
@@ -187,7 +204,7 @@ 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 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
+40 -15
View File
@@ -19,9 +19,34 @@ zap:
# redis configuration
redis:
db: 0
#是否使用redis集群模式
useCluster: false
#使用集群模式addr和db默认无效
addr: 127.0.0.1:6379
password: ""
db: 0
clusterAddrs:
- "172.21.0.3:7000"
- "172.21.0.4:7001"
- "172.21.0.2:7002"
# mongo configuration
mongo:
coll: ''
options: ''
database: ''
username: ''
password: ''
auth-source: ''
min-pool-size: 0
max-pool-size: 100
socket-timeout-ms: 0
connect-timeout-ms: 0
is-zap: false
hosts:
- host: ''
port: ''
# email configuration
email:
@@ -35,11 +60,12 @@ email:
# system configuration
system:
env: public # Change to "develop" to skip authentication for development mode
env: local # 修改为public可以关闭路由日志输出
addr: 8888
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-mongo: false # 使用mongo
use-multipoint: false
# IP限制次数 一个小时15000次
iplimit-count: 15000
@@ -105,6 +131,17 @@ mssql:
max-open-conns: 100
log-mode: ""
log-zap: false
sqlite:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
db-list:
- disable: true # 是否禁用
type: "" # 数据库的类型,目前支持mysql、pgsql、mssql、oracle
@@ -196,20 +233,8 @@ hua-wei-obs:
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
# 跨域配置
# 需要配合 server/initialize/router.go#L32 使用
# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用
cors:
mode: strict-whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
+7 -7
View File
@@ -1,18 +1,18 @@
package config
type Autocode struct {
TransferRestart bool `mapstructure:"transfer-restart" json:"transfer-restart" yaml:"transfer-restart"`
Root string `mapstructure:"root" json:"root" yaml:"root"`
SModel string `mapstructure:"server-model" json:"server-model" yaml:"server-model"`
SRouter string `mapstructure:"server-router" json:"server-router" yaml:"server-router"`
Server string `mapstructure:"server" json:"server" yaml:"server"`
SApi string `mapstructure:"server-api" json:"server-api" yaml:"server-api"`
SPlug string `mapstructure:"server-plug" json:"server-plug" yaml:"server-plug"`
SInitialize string `mapstructure:"server-initialize" json:"server-initialize" yaml:"server-initialize"`
SModel string `mapstructure:"server-model" json:"server-model" yaml:"server-model"`
SRequest string `mapstructure:"server-request" json:"server-request" yaml:"server-request"`
SRouter string `mapstructure:"server-router" json:"server-router" yaml:"server-router"`
SService string `mapstructure:"server-service" json:"server-service" yaml:"server-service"`
Root string `mapstructure:"root" json:"root" yaml:"root"`
WTable string `mapstructure:"web-table" json:"web-table" yaml:"web-table"`
Web string `mapstructure:"web" json:"web" yaml:"web"`
SService string `mapstructure:"server-service" json:"server-service" yaml:"server-service"`
SRequest string `mapstructure:"server-request" json:"server-request" yaml:"server-request"`
WApi string `mapstructure:"web-api" json:"web-api" yaml:"web-api"`
WForm string `mapstructure:"web-form" json:"web-form" yaml:"web-form"`
WTable string `mapstructure:"web-table" json:"web-table" yaml:"web-table"`
TransferRestart bool `mapstructure:"transfer-restart" json:"transfer-restart" yaml:"transfer-restart"`
}
+2 -1
View File
@@ -4,6 +4,7 @@ type Server struct {
JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"`
Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`
Mongo Mongo `mapstructure:"mongo" json:"mongo" yaml:"mongo"`
Email Email `mapstructure:"email" json:"email" yaml:"email"`
System System `mapstructure:"system" json:"system" yaml:"system"`
Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
@@ -14,6 +15,7 @@ type Server struct {
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"`
@@ -24,7 +26,6 @@ type Server struct {
AwsS3 AwsS3 `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"`
Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
Timer Timer `mapstructure:"timer" json:"timer" yaml:"timer"`
// 跨域配置
Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
+10 -10
View File
@@ -9,24 +9,24 @@ type DsnProvider interface {
// GeneralDB 也被 Pgsql 和 Mysql 原样使用
type GeneralDB struct {
Path string `mapstructure:"path" json:"path" yaml:"path"` // 服务器地址:端口
Port string `mapstructure:"port" json:"port" yaml:"port"` //:端口
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
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表示开启
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"`
Port string `mapstructure:"port" json:"port" yaml:"port"`
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
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"` // 数据库密码
Path string `mapstructure:"path" json:"path" yaml:"path"`
Engine string `mapstructure:"engine" json:"engine" yaml:"engine" default:"InnoDB"` //数据库引擎,默认InnoDB
LogMode string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"` // 是否开启Gorm全局日志
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全局日志
Singular bool `mapstructure:"singular" json:"singular" yaml:"singular"` //是否开启全局禁用复数,true表示开启
LogZap bool `mapstructure:"log-zap" json:"log-zap" yaml:"log-zap"` // 是否通过zap写入日志文件
}
type SpecializedDB struct {
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
Type string `mapstructure:"type" json:"type" yaml:"type"`
AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"`
GeneralDB `yaml:",inline" mapstructure:",squash"`
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
}
+7 -7
View File
@@ -1,11 +1,11 @@
package config
type Email struct {
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口
From string `mapstructure:"from" json:"from" yaml:"from"` // 收件人
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL
}
+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
}
+41
View File
@@ -0,0 +1,41 @@
package config
import (
"fmt"
"strings"
)
type Mongo struct {
Coll string `json:"coll" yaml:"coll" mapstructure:"coll"` // collection name
Options string `json:"options" yaml:"options" mapstructure:"options"` // mongodb options
Database string `json:"database" yaml:"database" mapstructure:"database"` // database name
Username string `json:"username" yaml:"username" mapstructure:"username"` // 用户名
Password string `json:"password" yaml:"password" mapstructure:"password"` // 密码
AuthSource string `json:"auth-source" yaml:"auth-source" mapstructure:"auth-source"` // 验证数据库
MinPoolSize uint64 `json:"min-pool-size" yaml:"min-pool-size" mapstructure:"min-pool-size"` // 最小连接池
MaxPoolSize uint64 `json:"max-pool-size" yaml:"max-pool-size" mapstructure:"max-pool-size"` // 最大连接池
SocketTimeoutMs int64 `json:"socket-timeout-ms" yaml:"socket-timeout-ms" mapstructure:"socket-timeout-ms"` // socket超时时间
ConnectTimeoutMs int64 `json:"connect-timeout-ms" yaml:"connect-timeout-ms" mapstructure:"connect-timeout-ms"` // 连接超时时间
IsZap bool `json:"is-zap" yaml:"is-zap" mapstructure:"is-zap"` // 是否开启zap日志
Hosts []*MongoHost `json:"hosts" yaml:"hosts" mapstructure:"hosts"` // 主机列表
}
type MongoHost struct {
Host string `json:"host" yaml:"host" mapstructure:"host"` // ip地址
Port string `json:"port" yaml:"port" mapstructure:"port"` // 端口
}
// Uri .
func (x *Mongo) Uri() string {
length := len(x.Hosts)
hosts := make([]string, 0, length)
for i := 0; i < length; i++ {
if x.Hosts[i].Host != "" && x.Hosts[i].Port != "" {
hosts = append(hosts, x.Hosts[i].Host+":"+x.Hosts[i].Port)
}
}
if x.Options != "" {
return fmt.Sprintf("mongodb://%s/%s?%s", strings.Join(hosts, ","), x.Database, x.Options)
}
return fmt.Sprintf("mongodb://%s/%s", strings.Join(hosts, ","), x.Database)
}
+2 -2
View File
@@ -4,10 +4,10 @@ type AwsS3 struct {
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
Region string `mapstructure:"region" json:"region" yaml:"region"`
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
}
+1 -1
View File
@@ -4,8 +4,8 @@ type Qiniu struct {
Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称
ImgPath string `mapstructure:"img-path" json:"img-path" yaml:"img-path"` // CDN加速域名
UseHTTPS bool `mapstructure:"use-https" json:"use-https" yaml:"use-https"` // 是否使用https
AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"` // 秘钥AK
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` // 秘钥SK
UseHTTPS bool `mapstructure:"use-https" json:"use-https" yaml:"use-https"` // 是否使用https
UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"use-cdn-domains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速
}
+5 -3
View File
@@ -1,7 +1,9 @@
package config
type Redis struct {
DB int `mapstructure:"db" json:"db" yaml:"db"` // redis的哪个数据库
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口
Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口
Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
DB int `mapstructure:"db" json:"db" yaml:"db"` // 单实例模式下redis的哪个数据库
UseCluster bool `mapstructure:"useCluster" json:"useCluster" yaml:"useCluster"` // 是否使用集群模式
ClusterAddrs []string `mapstructure:"clusterAddrs" json:"clusterAddrs" yaml:"clusterAddrs"` // 集群模式下的节点地址列表
}
+7 -7
View File
@@ -1,13 +1,13 @@
package config
type System struct {
Env string `mapstructure:"env" json:"env" yaml:"env"` // 环境值
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // Oss类型
UseMultipoint bool `mapstructure:"use-multipoint" json:"use-multipoint" yaml:"use-multipoint"` // 多点登录拦截
UseRedis bool `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"` // 使用redis
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // Oss类型
RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"`
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值
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"`
UseMultipoint bool `mapstructure:"use-multipoint" json:"use-multipoint" yaml:"use-multipoint"` // 多点登录拦截
UseRedis bool `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"` // 使用redis
UseMongo bool `mapstructure:"use-mongo" json:"use-mongo" yaml:"use-mongo"` // 使用mongo
}
-14
View File
@@ -1,14 +0,0 @@
package config
type Timer struct {
Start bool `mapstructure:"start" json:"start" yaml:"start"` // 是否启用
Spec string `mapstructure:"spec" json:"spec" yaml:"spec"` // CRON表达式
WithSeconds bool `mapstructure:"with_seconds" json:"with_seconds" yaml:"with_seconds"` // 是否精确到秒
Detail []Detail `mapstructure:"detail" json:"detail" yaml:"detail"`
}
type Detail struct {
TableName string `mapstructure:"tableName" json:"tableName" yaml:"tableName"` // 需要清理的表名
CompareField string `mapstructure:"compareField" json:"compareField" yaml:"compareField"` // 需要比较时间的字段
Interval string `mapstructure:"interval" json:"interval" yaml:"interval"` // 时间间隔
}
+1 -1
View File
@@ -47,7 +47,7 @@ func (z *Zap) TransportLevel() zapcore.Level {
case "warn":
return zapcore.WarnLevel
case "error":
return zapcore.WarnLevel
return zapcore.ErrorLevel
case "dpanic":
return zapcore.DPanicLevel
case "panic":
+97
View File
@@ -0,0 +1,97 @@
package internal
import (
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
)
type Cutter struct {
level string // 日志级别(debug, info, warn, error, dpanic, panic, fatal)
format string // 时间格式(2006-01-02)
Director string // 日志文件夹
file *os.File // 文件句柄
mutex *sync.RWMutex // 读写锁
}
type CutterOption func(*Cutter)
// WithCutterFormat 设置时间格式
func WithCutterFormat(format string) CutterOption {
return func(c *Cutter) {
c.format = format
}
}
func NewCutter(director string, level string, options ...CutterOption) *Cutter {
rotate := &Cutter{
level: level,
Director: director,
mutex: new(sync.RWMutex),
}
for i := 0; i < len(options); i++ {
options[i](rotate)
}
return rotate
}
// Write satisfies the io.Writer interface. It writes to the
// appropriate file handle that is currently being used.
// If we have reached rotation time, the target file gets
// automatically rotated, and also purged if necessary.
func (c *Cutter) Write(bytes []byte) (n int, err error) {
c.mutex.Lock()
defer func() {
if c.file != nil {
_ = c.file.Close()
c.file = nil
}
c.mutex.Unlock()
}()
var business string
if strings.Contains(string(bytes), "business") {
var compile *regexp.Regexp
compile, err = regexp.Compile(`{"business": "([^,]+)"}`)
if err != nil {
return 0, err
}
if compile.Match(bytes) {
finds := compile.FindSubmatch(bytes)
business = string(finds[len(finds)-1])
bytes = compile.ReplaceAll(bytes, []byte(""))
}
compile, err = regexp.Compile(`"business": "([^,]+)"`)
if err != nil {
return 0, err
}
if compile.Match(bytes) {
finds := compile.FindSubmatch(bytes)
business = string(finds[len(finds)-1])
bytes = compile.ReplaceAll(bytes, []byte(""))
}
}
format := time.Now().Format(c.format)
formats := make([]string, 0, 4)
formats = append(formats, c.Director)
if format != "" {
formats = append(formats, format)
}
if business != "" {
formats = append(formats, business)
}
formats = append(formats, c.level+".log")
filename := filepath.Join(formats...)
dirname := filepath.Dir(filename)
err = os.MkdirAll(dirname, 0755)
if err != nil {
return 0, err
}
c.file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return 0, err
}
return c.file.Write(bytes)
}
+4 -12
View File
@@ -2,11 +2,8 @@ package internal
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"go.uber.org/zap/zapcore"
"os"
"path"
"time"
)
var FileRotatelogs = new(fileRotatelogs)
@@ -15,15 +12,10 @@ type fileRotatelogs struct{}
// GetWriteSyncer 获取 zapcore.WriteSyncer
// Author [SliverHorn](https://github.com/SliverHorn)
func (r *fileRotatelogs) GetWriteSyncer(level string) (zapcore.WriteSyncer, error) {
fileWriter, err := rotatelogs.New(
path.Join(global.GVA_CONFIG.Zap.Director, "%Y-%m-%d", level+".log"),
rotatelogs.WithClock(rotatelogs.Local),
rotatelogs.WithMaxAge(time.Duration(global.GVA_CONFIG.Zap.MaxAge)*24*time.Hour), // 日志留存时间
rotatelogs.WithRotationTime(time.Hour*24),
)
func (r *fileRotatelogs) GetWriteSyncer(level string) zapcore.WriteSyncer {
fileWriter := NewCutter(global.GVA_CONFIG.Zap.Director, level, WithCutterFormat("2006-01-02"))
if global.GVA_CONFIG.Zap.LogInConsole {
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter))
}
return zapcore.AddSync(fileWriter), err
return zapcore.AddSync(fileWriter)
}
+1 -7
View File
@@ -1,7 +1,6 @@
package internal
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -42,12 +41,7 @@ func (z *_zap) GetEncoderConfig() zapcore.EncoderConfig {
// GetEncoderCore 获取Encoder的 zapcore.Core
// Author [SliverHorn](https://github.com/SliverHorn)
func (z *_zap) GetEncoderCore(l zapcore.Level, level zap.LevelEnablerFunc) zapcore.Core {
writer, err := FileRotatelogs.GetWriteSyncer(l.String()) // 使用file-rotatelogs进行日志分割
if err != nil {
fmt.Printf("Get Write Syncer Failed err:%v", err.Error())
return nil
}
writer := FileRotatelogs.GetWriteSyncer(l.String()) // 日志分割
return zapcore.NewCore(z.GetEncoder(), writer, level)
}
+14 -9
View File
@@ -2,8 +2,6 @@ package core
import (
"fmt"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
@@ -19,7 +17,12 @@ func RunWindowsServer() {
// 初始化redis服务
initialize.Redis()
}
if global.GVA_CONFIG.System.UseMongo {
err := initialize.Mongo.Initialization()
if err != nil {
zap.L().Error(fmt.Sprintf("%+v", err))
}
}
// 从db加载jwt数据
if global.GVA_DB != nil {
system.LoadAll()
@@ -30,20 +33,22 @@ func RunWindowsServer() {
address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
s := initServer(address, Router)
// 保证文本顺序输出
// In order to ensure that the text order output can be deleted
time.Sleep(10 * time.Microsecond)
global.GVA_LOG.Info("server run success on ", zap.String("address", address))
fmt.Printf(`
欢迎使用 gin-vue-admin
当前版本:v2.5.5
加群方式:微信号:shouzi_1994 QQ群:622360840
当前版本:v2.6.4
加群方式:微信号:shouzi_1994 QQ群:470239250
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
GVA讨论社区:https://support.qq.com/products/371961
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:8080
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/coffee/index.html
--------------------------------------版权声明--------------------------------------
** 版权所有方:flipped-aurora开源团队 **
** 版权持有公司:北京翻转极光科技有限责任公司 **
** 剔除授权标识需购买商用授权:https://gin-vue-admin.com/empower/index.html **
`, address)
global.GVA_LOG.Error(s.ListenAndServe().Error())
}
+4 -4
View File
@@ -29,13 +29,13 @@ func Viper(path ...string) *viper.Viper {
switch gin.Mode() {
case gin.DebugMode:
config = internal.ConfigDefaultFile
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.EnvGinMode, internal.ConfigDefaultFile)
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), internal.ConfigDefaultFile)
case gin.ReleaseMode:
config = internal.ConfigReleaseFile
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.EnvGinMode, internal.ConfigReleaseFile)
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), internal.ConfigReleaseFile)
case gin.TestMode:
config = internal.ConfigTestFile
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.EnvGinMode, internal.ConfigTestFile)
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), internal.ConfigTestFile)
}
} else { // internal.ConfigEnv 常量存储的环境变量不为空 将值赋值于config
config = configEnv
@@ -65,7 +65,7 @@ func Viper(path ...string) *viper.Viper {
}
})
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
fmt.Println(err)
panic(err)
}
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
+705 -531
View File
File diff suppressed because it is too large Load Diff
+693 -482
View File
File diff suppressed because it is too large Load Diff
+478 -331
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,6 +1,7 @@
package global
import (
"github.com/qiniu/qmgo"
"sync"
"github.com/flipped-aurora/gin-vue-admin/server/utils/timer"
@@ -12,7 +13,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"
)
@@ -20,7 +21,8 @@ import (
var (
GVA_DB *gorm.DB
GVA_DBList map[string]*gorm.DB
GVA_REDIS *redis.Client
GVA_REDIS redis.UniversalClient
GVA_MONGO *qmgo.QmgoClient
GVA_CONFIG config.Server
GVA_VP *viper.Viper
// GVA_LOG *oplogging.Logger
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
type GVA_MODEL struct {
ID uint `gorm:"primarykey"` // 主键ID
ID uint `gorm:"primarykey" json:"ID"` // 主键ID
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
+99 -89
View File
@@ -1,133 +1,143 @@
module github.com/flipped-aurora/gin-vue-admin/server
go 1.18
go 1.22
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/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.87.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-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/mojocn/base64Captcha v1.3.1
github.com/mojocn/base64Captcha v1.3.6
github.com/otiai10/copy v1.7.0
github.com/pkg/errors v0.9.1
github.com/qiniu/api.v7/v7 v7.4.1
github.com/qiniu/qmgo v1.1.8
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/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
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
gorm.io/driver/mysql v1.3.3
gorm.io/driver/postgres v1.3.4
gorm.io/driver/sqlserver v1.3.2
gorm.io/gorm v1.23.4
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/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.2
github.com/tencentyun/cos-go-sdk-v5 v0.7.42
github.com/unrolled/secure v1.13.0
github.com/xuri/excelize/v2 v2.8.0
go.mongodb.org/mongo-driver v1.12.1
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.22.0
golang.org/x/sync v0.5.0
golang.org/x/text v0.14.0
gorm.io/driver/mysql v1.5.6
gorm.io/driver/postgres v1.5.7
gorm.io/driver/sqlserver v1.5.1
gorm.io/gorm v1.25.9
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/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/casbin/govaluate v1.1.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/denisenkom/go-mssqldb v0.12.0 // 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.16.0 // indirect
github.com/glebarez/sqlite v1.4.3 // 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.5 // indirect
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-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.3 // indirect
github.com/go-openapi/spec v0.20.12 // indirect
github.com/go-openapi/swag v0.22.5 // 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.0.0-20170517235910-f1bb20e5a188 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.1 // 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/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.11.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.10.0 // indirect
github.com/jackc/pgx/v4 v4.15.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.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/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/leodido/go-urn v1.2.1 // indirect
github.com/lestrrat-go/strftime v1.0.6 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.2.2 // 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/montanaflynn/stats v0.7.0 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/pelletier/go-toml v1.6.0 // 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-20200410134404-eec4a21b6bb0 // indirect
github.com/richardlehane/mscfb v1.0.3 // indirect
github.com/richardlehane/msoleps v1.0.1 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // 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/subosito/gotenv v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.uber.org/atomic v1.6.0 // indirect
go.uber.org/multierr v1.5.0 // indirect
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.org/x/tools v0.1.5 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.55.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
gorm.io/plugin/dbresolver v1.1.0 // indirect
modernc.org/libc v1.15.1 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.7 // indirect
modernc.org/sqlite v1.16.0 // indirect
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/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca // indirect
github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // 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.15.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.16.1 // indirect
google.golang.org/protobuf v1.33.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
)
+564 -645
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -48,6 +48,9 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
sysModel.SysBaseMenuBtn{},
sysModel.SysAuthorityBtn{},
sysModel.SysAutoCode{},
sysModel.SysExportTemplate{},
sysModel.Condition{},
sysModel.JoinTemplate{},
adapter.CasbinRule{},
@@ -83,6 +86,9 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
sysModel.SysBaseMenuBtn{},
sysModel.SysAuthorityBtn{},
sysModel.SysAutoCode{},
sysModel.SysExportTemplate{},
sysModel.Condition{},
sysModel.JoinTemplate{},
adapter.CasbinRule{},
+6 -5
View File
@@ -11,8 +11,6 @@ import (
"gorm.io/gorm"
)
// Gorm 初始化数据库并产生数据库全局变量
// Author SliverHorn
func Gorm() *gorm.DB {
switch global.GVA_CONFIG.System.DbType {
case "mysql":
@@ -23,17 +21,17 @@ func Gorm() *gorm.DB {
return GormOracle()
case "mssql":
return GormMssql()
case "sqlite":
return GormSqlite()
default:
return GormMysql()
}
}
// RegisterTables 注册数据库表专用
// Author SliverHorn
func RegisterTables() {
db := global.GVA_DB
err := db.AutoMigrate(
// 系统模块表
system.SysApi{},
system.SysUser{},
system.SysBaseMenu{},
@@ -47,6 +45,9 @@ func RegisterTables() {
system.SysBaseMenuBtn{},
system.SysAuthorityBtn{},
system.SysAutoCode{},
system.SysExportTemplate{},
system.Condition{},
system.JoinTemplate{},
example.ExaFile{},
example.ExaCustomer{},
+1 -1
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"
)
@@ -20,7 +21,6 @@ func GormMysql() *gorm.DB {
DSN: m.Dsn(), // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
SkipInitializeWithVersion: false, // 根据版本自动配置
}
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
return nil
-1
View File
@@ -40,7 +40,6 @@ func GormOracleByConfig(m config.Oracle) *gorm.DB {
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)
+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
}
}
+1 -1
View File
@@ -29,7 +29,7 @@ func (g *_gorm) Config(prefix string, singular bool) *gorm.Config {
},
DisableForeignKeyConstraintWhenMigrating: true,
}
_default := logger.New(NewWriter(log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{
_default := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: logger.Warn,
Colorful: true,
+29
View File
@@ -0,0 +1,29 @@
package internal
import (
"context"
"fmt"
"github.com/qiniu/qmgo/options"
"go.mongodb.org/mongo-driver/event"
opt "go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
)
var Mongo = new(mongo)
type mongo struct{}
func (m *mongo) GetClientOptions() []options.ClientOptions {
cmdMonitor := &event.CommandMonitor{
Started: func(ctx context.Context, event *event.CommandStartedEvent) {
zap.L().Info(fmt.Sprintf("[MongoDB][RequestID:%d][database:%s] %s\n", event.RequestID, event.DatabaseName, event.Command), zap.String("business", "mongo"))
},
Succeeded: func(ctx context.Context, event *event.CommandSucceededEvent) {
zap.L().Info(fmt.Sprintf("[MongoDB][RequestID:%d] [%s] %s\n", event.RequestID, event.Duration.String(), event.Reply), zap.String("business", "mongo"))
},
Failed: func(ctx context.Context, event *event.CommandFailedEvent) {
zap.L().Error(fmt.Sprintf("[MongoDB][RequestID:%d] [%s] %s\n", event.RequestID, event.Duration.String(), event.Failure), zap.String("business", "mongo"))
},
}
return []options.ClientOptions{{ClientOptions: &opt.ClientOptions{Monitor: cmdMonitor}}}
}
+151
View File
@@ -0,0 +1,151 @@
package initialize
import (
"context"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/pkg/errors"
"github.com/qiniu/qmgo"
"github.com/qiniu/qmgo/options"
"go.mongodb.org/mongo-driver/bson"
option "go.mongodb.org/mongo-driver/mongo/options"
"sort"
"strings"
)
var Mongo = new(mongo)
type (
mongo struct{}
Index struct {
V any `bson:"v"`
Ns any `bson:"ns"`
Key []bson.E `bson:"key"`
Name string `bson:"name"`
}
)
func (m *mongo) Indexes(ctx context.Context) error {
// 表名:索引列表 列: "表名": [][]string{{"index1", "index2"}}
indexMap := map[string][][]string{}
for collection, indexes := range indexMap {
err := m.CreateIndexes(ctx, collection, indexes)
if err != nil {
return err
}
}
return nil
}
func (m *mongo) Initialization() error {
var opts []options.ClientOptions
if global.GVA_CONFIG.Mongo.IsZap {
opts = internal.Mongo.GetClientOptions()
}
ctx := context.Background()
client, err := qmgo.Open(ctx, &qmgo.Config{
Uri: global.GVA_CONFIG.Mongo.Uri(),
Coll: global.GVA_CONFIG.Mongo.Coll,
Database: global.GVA_CONFIG.Mongo.Database,
MinPoolSize: &global.GVA_CONFIG.Mongo.MinPoolSize,
MaxPoolSize: &global.GVA_CONFIG.Mongo.MaxPoolSize,
SocketTimeoutMS: &global.GVA_CONFIG.Mongo.SocketTimeoutMs,
ConnectTimeoutMS: &global.GVA_CONFIG.Mongo.ConnectTimeoutMs,
Auth: &qmgo.Credential{
Username: global.GVA_CONFIG.Mongo.Username,
Password: global.GVA_CONFIG.Mongo.Password,
AuthSource: global.GVA_CONFIG.Mongo.AuthSource,
},
}, opts...)
if err != nil {
return errors.Wrap(err, "链接mongodb数据库失败!")
}
global.GVA_MONGO = client
err = m.Indexes(ctx)
if err != nil {
return err
}
return nil
}
func (m *mongo) CreateIndexes(ctx context.Context, name string, indexes [][]string) error {
collection, err := global.GVA_MONGO.Database.Collection(name).CloneCollection()
if err != nil {
return errors.Wrapf(err, "获取[%s]的表对象失败!", name)
}
list, err := collection.Indexes().List(ctx)
if err != nil {
return errors.Wrapf(err, "获取[%s]的索引对象失败!", name)
}
var entities []Index
err = list.All(ctx, &entities)
if err != nil {
return errors.Wrapf(err, "获取[%s]的索引列表失败!", name)
}
length := len(indexes)
indexMap1 := make(map[string][]string, length)
for i := 0; i < length; i++ {
sort.Strings(indexes[i]) // 对索引key进行排序, 在使用bson.M搜索时, bson会自动按照key的字母顺序进行排序
length1 := len(indexes[i])
keys := make([]string, 0, length1)
for j := 0; j < length1; j++ {
if indexes[i][i][0] == '-' {
keys = append(keys, indexes[i][j], "-1")
continue
}
keys = append(keys, indexes[i][j], "1")
}
key := strings.Join(keys, "_")
_, o1 := indexMap1[key]
if o1 {
return errors.Errorf("索引[%s]重复!", key)
}
indexMap1[key] = indexes[i]
}
length = len(entities)
indexMap2 := make(map[string]map[string]string, length)
for i := 0; i < length; i++ {
v1, o1 := indexMap2[entities[i].Name]
if !o1 {
keyLength := len(entities[i].Key)
v1 = make(map[string]string, keyLength)
for j := 0; j < keyLength; j++ {
v2, o2 := v1[entities[i].Key[j].Key]
if !o2 {
v1 = make(map[string]string)
}
v2 = entities[i].Key[j].Key
v1[entities[i].Key[j].Key] = v2
indexMap2[entities[i].Name] = v1
}
}
}
for k1, v1 := range indexMap1 {
_, o2 := indexMap2[k1]
if o2 {
continue
} // 索引存在
if len(fmt.Sprintf("%s.%s.$%s", collection.Name(), name, v1)) > 127 {
err = global.GVA_MONGO.Database.Collection(name).CreateOneIndex(ctx, options.IndexModel{
Key: v1,
IndexOptions: option.Index().SetName(utils.MD5V([]byte(k1))),
// IndexOptions: option.Index().SetName(utils.MD5V([]byte(k1))).SetExpireAfterSeconds(86400), // SetExpireAfterSeconds(86400) 设置索引过期时间, 86400 = 1天
})
if err != nil {
return errors.Wrapf(err, "创建索引[%s]失败!", k1)
}
return nil
}
err = global.GVA_MONGO.Database.Collection(name).CreateOneIndex(ctx, options.IndexModel{
Key: v1,
IndexOptions: option.Index().SetExpireAfterSeconds(86400),
// IndexOptions: option.Index().SetName(utils.MD5V([]byte(k1))).SetExpireAfterSeconds(86400), // SetExpireAfterSeconds(86400) 设置索引过期时间(秒), 86400 = 1天
})
if err != nil {
return errors.Wrapf(err, "创建索引[%s]失败!", k1)
}
}
return nil
}
+17 -6
View File
@@ -5,20 +5,31 @@ 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"
)
func Redis() {
redisCfg := global.GVA_CONFIG.Redis
client := redis.NewClient(&redis.Options{
Addr: redisCfg.Addr,
Password: redisCfg.Password, // no password set
DB: redisCfg.DB, // use default DB
})
var client redis.UniversalClient
// 使用集群模式
if redisCfg.UseCluster {
client = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: redisCfg.ClusterAddrs,
Password: redisCfg.Password,
})
} else {
// 使用单例模式
client = redis.NewClient(&redis.Options{
Addr: redisCfg.Addr,
Password: redisCfg.Password,
DB: redisCfg.DB,
})
}
pong, err := client.Ping(context.Background()).Result()
if err != nil {
global.GVA_LOG.Error("redis connect ping failed, err:", zap.Error(err))
panic(err)
} else {
global.GVA_LOG.Info("redis connect ping response:", zap.String("pong", pong))
global.GVA_REDIS = client
+46 -25
View File
@@ -1,35 +1,56 @@
package initialize
import (
"net/http"
"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"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
"net/http"
"os"
)
type justFilesFilesystem struct {
fs http.FileSystem
}
func (fs justFilesFilesystem) Open(name string) (http.File, error) {
f, err := fs.fs.Open(name)
if err != nil {
return nil, err
}
stat, err := f.Stat()
if stat.IsDir() {
return nil, os.ErrPermission
}
return f, nil
}
// 初始化总路由
func Routers() *gin.Engine {
Router := gin.Default()
Router := gin.New()
Router.Use(gin.Recovery())
if gin.Mode() == gin.DebugMode {
Router.Use(gin.Logger())
}
InstallPlugin(Router) // 安装插件
systemRouter := router.RouterGroupApp.System
exampleRouter := router.RouterGroupApp.Example
// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
// VUE_APP_BASE_API = /
// VUE_APP_BASE_PATH = http://localhost
// 然后执行打包命令 npm run build。在打开下面4行注释
// Router.LoadHTMLGlob("./dist/*.html") // npm打包成dist的路径
// Router.Static("/favicon.ico", "./dist/favicon.ico")
// Router.Static("/static", "./dist/assets") // dist里面的静态资源
// Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
// 然后执行打包命令 npm run build。在打开下面3行注释
//Router.Static("/favicon.ico", "./dist/favicon.ico")
//Router.Static("/assets", "./dist/assets") // dist里面的静态资源
//Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
Router.StaticFS(global.GVA_CONFIG.Local.Path, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址
// Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{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()) // 按照配置的规则放行跨域请求
@@ -53,20 +74,20 @@ func Routers() *gin.Engine {
PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
{
systemRouter.InitApiRouter(PrivateGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
systemRouter.InitAutoCodeRouter(PrivateGroup) // 创建自动化代码
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 字典详情管理
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
systemRouter.InitAutoCodeRouter(PrivateGroup) // 创建自动化代码
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 字典详情管理
systemRouter.InitSysExportTemplateRouter(PrivateGroup) // 导出模板
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
+24 -20
View File
@@ -2,32 +2,36 @@ package initialize
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/task"
"github.com/robfig/cron/v3"
"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/utils"
)
func Timer() {
if global.GVA_CONFIG.Timer.Start {
for i := range global.GVA_CONFIG.Timer.Detail {
go func(detail config.Detail) {
var option []cron.Option
if global.GVA_CONFIG.Timer.WithSeconds {
option = append(option, cron.WithSeconds())
}
_, err := global.GVA_Timer.AddTaskByFunc("ClearDB", global.GVA_CONFIG.Timer.Spec, func() {
err := utils.ClearTable(global.GVA_DB, detail.TableName, detail.CompareField, detail.Interval)
if err != nil {
fmt.Println("timer error:", err)
}
}, option...)
if err != nil {
fmt.Println("add timer error:", err)
}
}(global.GVA_CONFIG.Timer.Detail[i])
go func() {
var option []cron.Option
option = append(option, cron.WithSeconds())
// 清理DB定时任务
_, err := global.GVA_Timer.AddTaskByFunc("ClearDB", "@daily", func() {
err := task.ClearTable(global.GVA_DB) // 定时任务方法定在task文件包中
if err != nil {
fmt.Println("timer error:", err)
}
}, "定时清理数据库【日志,黑名单】内容", option...)
if err != nil {
fmt.Println("add timer error:", err)
}
}
// 其他定时任务定在这里 参考上方使用方法
//_, err := global.GVA_Timer.AddTaskByFunc("定时任务标识", "corn表达式", func() {
// 具体执行内容...
// ......
//}, option...)
//if err != nil {
// fmt.Println("add timer error:", err)
//}
}()
}
+4 -3
View File
@@ -1,6 +1,7 @@
package main
import (
_ "go.uber.org/automaxprocs"
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/core"
@@ -13,9 +14,9 @@ import (
//go:generate go mod tidy
//go:generate go mod download
// @title Swagger Example API
// @version 0.0.1
// @description This is a sample Server pets
// @title Gin-Vue-Admin Swagger API接口文档
// @version v2.6.4
// @description 使用gin+vue进行极速开发的全栈开发基础平台
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name x-token
+14 -16
View File
@@ -16,22 +16,20 @@ var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
// CasbinHandler 拦截器
func CasbinHandler() gin.HandlerFunc {
return func(c *gin.Context) {
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
}
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()
}
+27 -15
View File
@@ -1,18 +1,19 @@
package middleware
import (
"errors"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/golang-jwt/jwt/v4"
"go.uber.org/zap"
"strconv"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
@@ -20,14 +21,15 @@ var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
token := c.Request.Header.Get("x-token")
token := utils.GetToken(c)
if token == "" {
response.FailWithDetailed(gin.H{"reload": true}, "未登录或非法访问", c)
response.NoAuth("未登录或非法访问", c)
c.Abort()
return
}
if jwtService.IsBlacklist(token) {
response.FailWithDetailed(gin.H{"reload": true}, "您的帐户异地登陆或令牌失效", c)
response.NoAuth("您的帐户异地登陆或令牌失效", c)
utils.ClearToken(c)
c.Abort()
return
}
@@ -35,12 +37,14 @@ func JWTAuth() gin.HandlerFunc {
// parseToken 解析token包含的信息
claims, err := j.ParseToken(token)
if err != nil {
if err == utils.TokenExpired {
response.FailWithDetailed(gin.H{"reload": true}, "授权已过期", c)
if errors.Is(err, utils.TokenExpired) {
response.NoAuth("授权已过期", c)
utils.ClearToken(c)
c.Abort()
return
}
response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
response.NoAuth(err.Error(), c)
utils.ClearToken(c)
c.Abort()
return
}
@@ -53,13 +57,15 @@ func JWTAuth() gin.HandlerFunc {
// response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
// c.Abort()
//}
if claims.ExpiresAt-time.Now().Unix() < claims.BufferTime {
c.Set("claims", claims)
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))
utils.SetToken(c, newToken, int(dr.Seconds()))
if global.GVA_CONFIG.System.UseMultipoint {
RedisJwtToken, err := jwtService.GetRedisJWT(newClaims.Username)
if err != nil {
@@ -71,7 +77,13 @@ func JWTAuth() gin.HandlerFunc {
_ = jwtService.SetRedisJWT(newToken, newClaims.Username)
}
}
c.Set("claims", claims)
c.Next()
if newToken, exists := c.Get("new-token"); exists {
c.Header("new-token", newToken.(string))
}
if newExpiresAt, exists := c.Get("new-expires-at"); exists {
c.Header("new-expires-at", newExpiresAt.(string))
}
}
}
-22
View File
@@ -1,22 +0,0 @@
package middleware
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/gin-gonic/gin"
)
// 处理跨域请求,支持options访问
func NeedInit() gin.HandlerFunc {
return func(c *gin.Context) {
if global.GVA_DB == nil {
response.OkWithDetailed(gin.H{
"needInit": true,
}, "前往初始化数据库", c)
c.Abort()
} else {
c.Next()
}
// 处理请求
}
}
+21 -23
View File
@@ -23,10 +23,11 @@ import (
var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
var respPool sync.Pool
var bufferSize = 1024
func init() {
respPool.New = func() interface{} {
return make([]byte, 1024)
return make([]byte, bufferSize)
}
}
@@ -56,8 +57,8 @@ func OperationRecord() gin.HandlerFunc {
body, _ = json.Marshal(&m)
}
claims, _ := utils.GetClaims(c)
if claims.ID != 0 {
userId = int(claims.ID)
if claims != nil && claims.BaseClaims.ID != 0 {
userId = int(claims.BaseClaims.ID)
} else {
id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
if err != nil {
@@ -70,18 +71,18 @@ func OperationRecord() gin.HandlerFunc {
Method: c.Request.Method,
Path: c.Request.URL.Path,
Agent: c.Request.UserAgent(),
Body: string(body),
Body: "",
UserID: userId,
}
// 上传文件时候 中间件日志进行裁断操作
if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
if len(record.Body) > 1024 {
// 截断
newBody := respPool.Get().([]byte)
copy(newBody, record.Body)
record.Body = string(newBody)
defer respPool.Put(newBody[:0])
if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
record.Body = "[文件]"
} else {
if len(body) > bufferSize {
record.Body = "[超出记录长度]"
} else {
record.Body = string(body)
}
}
@@ -100,21 +101,18 @@ 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 {
if len(record.Resp) > bufferSize {
// 截断
newBody := respPool.Get().([]byte)
copy(newBody, record.Resp)
record.Resp = string(newBody)
defer respPool.Put(newBody[:0])
record.Body = "超出记录长度"
}
}
+7
View File
@@ -0,0 +1,7 @@
package common
type ClearDB struct {
TableName string
CompareField string
Interval string
}
+8
View File
@@ -50,6 +50,14 @@ func FailWithMessage(message string, c *gin.Context) {
Result(ERROR, map[string]interface{}{}, message, c)
}
func NoAuth(message string, c *gin.Context) {
c.JSON(http.StatusUnauthorized, Response{
7,
nil,
message,
})
}
func FailWithDetailed(data interface{}, message string, c *gin.Context) {
Result(ERROR, data, message, c)
}
+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 {
@@ -1,11 +0,0 @@
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 SysDictionarySearch struct {
system.SysDictionary
request.PageInfo
}
@@ -0,0 +1,14 @@
package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"time"
)
type SysExportTemplateSearch struct {
system.SysExportTemplate
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
request.PageInfo
}
+53 -7
View File
@@ -2,17 +2,18 @@ package request
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/config"
"os"
)
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,17 @@ 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 {
separator := string(os.PathSeparator)
return i.DBPath + separator + i.DBName + ".db"
}
func (i *InitDB) MssqlEmptyDsn() string {
return "sqlserver://" + i.UserName + ":" + i.Password + "@" + i.Host + ":" + i.Port + "?database=" + i.DBName + "&encrypt=disable"
}
// ToMysqlConfig 转换 config.Mysql
// Author [SliverHorn](https://github.com/SliverHorn)
func (i *InitDB) ToMysqlConfig() config.Mysql {
@@ -74,3 +86,37 @@ 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: "",
},
}
}
func (i *InitDB) ToMssqlConfig() config.Mssql {
return config.Mssql{
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: "",
},
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ type AddMenuAuthorityInfo struct {
func DefaultMenu() []system.SysBaseMenu {
return []system.SysBaseMenu{{
GVA_MODEL: global.GVA_MODEL{ID: 1},
ParentId: "0",
ParentId: 0,
Path: "dashboard",
Name: "dashboard",
Component: "view/dashboard/index.vue",
@@ -13,4 +13,5 @@ type Column struct {
ColumnName string `json:"columnName" gorm:"column:column_name"`
DataTypeLong string `json:"dataTypeLong" gorm:"column:data_type_long"`
ColumnComment string `json:"columnComment" gorm:"column:column_comment"`
PrimaryKey bool `json:"primaryKey" gorm:"column:primary_key"`
}
+1 -1
View File
@@ -2,7 +2,7 @@ package system
type SysMenu struct {
SysBaseMenu
MenuId string `json:"menuId" gorm:"comment:菜单ID"`
MenuId uint `json:"menuId" gorm:"comment:菜单ID"`
AuthorityId uint `json:"-" gorm:"comment:角色ID"`
Children []SysMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters" gorm:"foreignKey:SysBaseMenuID;references:MenuId"`
+51 -30
View File
@@ -10,23 +10,39 @@ import (
// AutoCodeStruct 初始版本自动化代码工具
type AutoCodeStruct struct {
StructName string `json:"structName"` // Struct名称
TableName string `json:"tableName"` // 表名
PackageName string `json:"packageName"` // 文件名称
HumpPackageName string `json:"humpPackageName"` // go文件名称
Abbreviation string `json:"abbreviation"` // Struct简称
Description string `json:"description"` // 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:"-"`
StructName string `json:"structName"` // Struct名称
TableName string `json:"tableName"` // 表名
PackageName string `json:"packageName"` // 文件名称
HumpPackageName string `json:"humpPackageName"` // go文件名称
Abbreviation string `json:"abbreviation"` // Struct简称
Description string `json:"description"` // Struct中文名称
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建api
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql"` // 是否自动创建menu
AutoCreateResource bool `json:"autoCreateResource"` // 是否自动创建资源标识
AutoMoveFile bool `json:"autoMoveFile"` // 是否自动移动文件
BusinessDB string `json:"businessDB"` // 业务数据库
GvaModel bool `json:"gvaModel"` // 是否使用gva默认Model
Fields []*Field `json:"fields"`
PrimaryField *Field `json:"primaryField"`
HasTimer bool `json:"-"`
HasSearchTimer bool `json:"-"`
DictTypes []string `json:"-"`
Package string `json:"package"`
PackageT string `json:"-"`
NeedSort bool `json:"-"`
HasPic bool `json:"-"`
HasRichText bool `json:"-"`
HasFile bool `json:"-"`
NeedJSON bool `json:"-"`
FrontFields []*Field `json:"-"`
HasDataSource bool `json:"-"`
DataSourceMap map[string]*DataSource `json:"-"`
}
type DataSource struct {
Table string `json:"table"`
Label string `json:"label"`
Value string `json:"value"`
}
func (a *AutoCodeStruct) Pretreatment() {
@@ -51,19 +67,24 @@ func (a *AutoCodeStruct) SuffixTest() {
}
type Field struct {
FieldName string `json:"fieldName"` // Field名
FieldDesc string `json:"fieldDesc"` // 中文名
FieldType string `json:"fieldType"` // Field数据类型
FieldJson string `json:"fieldJson"` // FieldJson
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
Comment string `json:"comment"` // 数据库字段描述
ColumnName string `json:"columnName"` // 数据库字段
FieldSearchType string `json:"fieldSearchType"` // 搜索条件
DictType string `json:"dictType"` // 字典
Require bool `json:"require"` // 是否必填
ErrorText string `json:"errorText"` // 校验失败文字
Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否增加排序
FieldName string `json:"fieldName"` // Field名
FieldDesc string `json:"fieldDesc"` // 中文名
FieldType string `json:"fieldType"` // Field数据类型
FieldJson string `json:"fieldJson"` // FieldJson
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
Comment string `json:"comment"` // 数据库字段描述
ColumnName string `json:"columnName"` // 数据库字段
FieldSearchType string `json:"fieldSearchType"` // 搜索条件
DictType string `json:"dictType"` // 字典
Front bool `json:"front"` // 是否前端可见
Require bool `json:"require"` // 是否必填
DefaultValue string `json:"defaultValue"` // 是否必填
ErrorText string `json:"errorText"` // 校验失败文字
Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否增加排序
PrimaryKey bool `json:"primaryKey"` // 是否主键
DataSource *DataSource `json:"dataSource"` // 数据源
CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源
}
var ErrAutoMove error = errors.New("创建代码成功并移动文件成功")
@@ -14,6 +14,7 @@ type SysAutoCodeHistory struct {
Package string `json:"package"`
BusinessDB string `json:"businessDB"`
TableName string `json:"tableName"`
MenuID uint `json:"menuID"`
RequestMeta string `gorm:"type:text" json:"requestMeta,omitempty"` // 前端传入的结构化信息
AutoCodePath string `gorm:"type:text" json:"autoCodePath,omitempty"` // 其他meta信息 path;path
InjectionMeta string `gorm:"type:text" json:"injectionMeta,omitempty"` // 注入的内容 RouterPath@functionName@RouterString;
+17 -17
View File
@@ -6,34 +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 uint `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
}
type Meta struct {
ActiveName string `json:"activeName" gorm:"comment:高亮菜单"`
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
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:地址栏携带参数的值"` // 地址栏携带参数的值
}
+2 -1
View File
@@ -9,7 +9,8 @@ import (
type SysDictionaryDetail struct {
global.GVA_MODEL
Label string `json:"label" form:"label" gorm:"column:label;comment:展示值"` // 展示值
Value int `json:"value" form:"value" gorm:"column:value;comment:字典值"` // 字典值
Value string `json:"value" form:"value" gorm:"column:value;comment:字典值"` // 字典值
Extend string `json:"extend" form:"extend" gorm:"column:extend;comment:扩展值"` // 扩展值
Status *bool `json:"status" form:"status" gorm:"column:status;comment:启用状态"` // 启用状态
Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序标记"` // 排序标记
SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" gorm:"column:sys_dictionary_id;comment:关联标记"` // 关联标记
@@ -0,0 +1,44 @@
// 自动生成模板SysExportTemplate
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
)
// 导出模板 结构体 SysExportTemplate
type SysExportTemplate struct {
global.GVA_MODEL
DBName string `json:"dbName" form:"dbName" gorm:"column:db_name;comment:数据库名称;"` //数据库名称
Name string `json:"name" form:"name" gorm:"column:name;comment:模板名称;"` //模板名称
TableName string `json:"tableName" form:"tableName" gorm:"column:table_name;comment:表名称;"` //表名称
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识;"` //模板标识
TemplateInfo string `json:"templateInfo" form:"templateInfo" gorm:"column:template_info;type:text;"` //模板信息
Limit int `json:"limit" form:"limit" gorm:"column:limit;comment:导出限制"`
Order string `json:"order" form:"order" gorm:"column:order;comment:排序"`
Conditions []Condition `json:"conditions" form:"conditions" gorm:"foreignKey:TemplateID;references:TemplateID;comment:条件"`
JoinTemplate []JoinTemplate `json:"joinTemplate" form:"joinTemplate" gorm:"foreignKey:TemplateID;references:TemplateID;comment:关联"`
}
type JoinTemplate struct {
global.GVA_MODEL
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"`
JOINS string `json:"joins" form:"joins" gorm:"column:joins;comment:关联"`
Table string `json:"table" form:"table" gorm:"column:table;comment:关联表"`
ON string `json:"on" form:"on" gorm:"column:on;comment:关联条件"`
}
func (JoinTemplate) TableName() string {
return "sys_export_template_join"
}
type Condition struct {
global.GVA_MODEL
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"`
From string `json:"from" form:"from" gorm:"column:from;comment:条件取的key"`
Column string `json:"column" form:"column" gorm:"column:column;comment:作为查询条件的字段"`
Operator string `json:"operator" form:"operator" gorm:"column:operator;comment:操作符"`
}
func (Condition) TableName() string {
return "sys_export_template_condition"
}
+1 -1
View File
@@ -15,7 +15,7 @@ type SysOperationRecord struct {
Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径
Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态
Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟
Agent string `json:"agent" form:"agent" gorm:"column:agent;comment:代理"` // 代理
Agent string `json:"agent" form:"agent" gorm:"type:text;column:agent;comment:代理"` // 代理
ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息
Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body
Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body
+1 -2
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 {
@@ -14,7 +14,6 @@ type SysUser struct {
SideMode string `json:"sideMode" gorm:"default:dark;comment:用户侧边主题"` // 用户侧边主题
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
BaseColor string `json:"baseColor" gorm:"default:#fff;comment:基础颜色"` // 基础颜色
ActiveColor string `json:"activeColor" gorm:"default:#1890ff;comment:活跃颜色"` // 活跃颜色
AuthorityId uint `json:"authorityId" gorm:"default:888;comment:用户角色ID"` // 用户角色ID
Authority SysAuthority `json:"authority" gorm:"foreignKey:AuthorityId;references:AuthorityId;comment:用户角色"`
Authorities []SysAuthority `json:"authorities" gorm:"many2many:sys_user_authority;"`
+3 -3
View File
@@ -2,9 +2,9 @@ package utils
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"strconv"
)
func RegisterApis(apis ...system.SysApi) {
@@ -37,13 +37,13 @@ func RegisterMenus(menus ...system.SysBaseMenu) {
fmt.Println("插件已安装或存在同名菜单")
return
}
parentMenu.ParentId = "0"
parentMenu.ParentId = 0
err := global.GVA_DB.Create(&parentMenu).Error
if err != nil {
fmt.Println(err)
}
for i := range otherMenus {
pid := strconv.Itoa(int(parentMenu.ID))
pid := parentMenu.ID
otherMenus[i].ParentId = pid
}
err = global.GVA_DB.Create(&otherMenus).Error
@@ -3,15 +3,12 @@ package {{.Package}}
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
{{.Package}}Req "github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
{{- if .NeedValid }}
"github.com/flipped-aurora/gin-vue-admin/server/utils"
{{- else if .AutoCreateResource}}
{{- if .AutoCreateResource}}
"github.com/flipped-aurora/gin-vue-admin/server/utils"
{{- end }}
)
@@ -22,14 +19,14 @@ type {{.StructName}}Api struct {
var {{.Abbreviation}}Service = service.ServiceGroupApp.{{.PackageT}}ServiceGroup.{{.StructName}}Service
// Create{{.StructName}} 创建{{.StructName}}
// Create{{.StructName}} 创建{{.Description}}
// @Tags {{.StructName}}
// @Summary 创建{{.StructName}}
// @Summary 创建{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {{.Package}}.{{.StructName}} true "创建{{.StructName}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Param data body {{.Package}}.{{.StructName}} true "创建{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /{{.Abbreviation}}/create{{.StructName}} [post]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Create{{.StructName}}(c *gin.Context) {
var {{.Abbreviation}} {{.Package}}.{{.StructName}}
@@ -41,20 +38,8 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Create{{.StructName}}(c *gin.Con
{{- if .AutoCreateResource }}
{{.Abbreviation}}.CreatedBy = utils.GetUserID(c)
{{- end }}
{{- if .NeedValid }}
verify := utils.Rules{
{{- range $index,$element := .Fields }}
{{- if $element.Require }}
"{{$element.FieldName}}":{utils.NotEmpty()},
{{- end }}
{{- end }}
}
if err := utils.Verify({{.Abbreviation}}, verify); err != nil {
response.FailWithMessage(err.Error(), c)
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 {
@@ -62,26 +47,21 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Create{{.StructName}}(c *gin.Con
}
}
// Delete{{.StructName}} 删除{{.StructName}}
// Delete{{.StructName}} 删除{{.Description}}
// @Tags {{.StructName}}
// @Summary 删除{{.StructName}}
// @Summary 删除{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {{.Package}}.{{.StructName}} true "删除{{.StructName}}"
// @Param data body {{.Package}}.{{.StructName}} true "删除{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}(c *gin.Context) {
var {{.Abbreviation}} {{.Package}}.{{.StructName}}
err := c.ShouldBindJSON(&{{.Abbreviation}})
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
{{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}")
{{- if .AutoCreateResource }}
{{.Abbreviation}}.DeletedBy = utils.GetUserID(c)
userID := utils.GetUserID(c)
{{- end }}
if err := {{.Abbreviation}}Service.Delete{{.StructName}}({{.Abbreviation}}); err != nil {
if err := {{.Abbreviation}}Service.Delete{{.StructName}}({{.PrimaryField.FieldJson}} {{- if .AutoCreateResource -}},userID{{- end -}}); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
} else {
@@ -89,26 +69,20 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}(c *gin.Con
}
}
// Delete{{.StructName}}ByIds 批量删除{{.StructName}}
// Delete{{.StructName}}ByIds 批量删除{{.Description}}
// @Tags {{.StructName}}
// @Summary 批量删除{{.StructName}}
// @Summary 批量删除{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除{{.StructName}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
// @Router /{{.Abbreviation}}/delete{{.StructName}}ByIds [delete]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}ByIds(c *gin.Context) {
var IDS request.IdsReq
err := c.ShouldBindJSON(&IDS)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
{{.PrimaryField.FieldJson}}s := c.QueryArray("{{.PrimaryField.FieldJson}}s[]")
{{- if .AutoCreateResource }}
deletedBy := utils.GetUserID(c)
userID := utils.GetUserID(c)
{{- end }}
if err := {{.Abbreviation}}Service.Delete{{.StructName}}ByIds(IDS{{- if .AutoCreateResource }},deletedBy{{- end }}); err != nil {
if err := {{.Abbreviation}}Service.Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s{{- if .AutoCreateResource }},userID{{- end }}); err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
} else {
@@ -116,13 +90,13 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}ByIds(c *gi
}
}
// Update{{.StructName}} 更新{{.StructName}}
// Update{{.StructName}} 更新{{.Description}}
// @Tags {{.StructName}}
// @Summary 更新{{.StructName}}
// @Summary 更新{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {{.Package}}.{{.StructName}} true "更新{{.StructName}}"
// @Param data body {{.Package}}.{{.StructName}} true "更新{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /{{.Abbreviation}}/update{{.StructName}} [put]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Update{{.StructName}}(c *gin.Context) {
@@ -135,19 +109,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Update{{.StructName}}(c *gin.Con
{{- if .AutoCreateResource }}
{{.Abbreviation}}.UpdatedBy = utils.GetUserID(c)
{{- end }}
{{- if .NeedValid }}
verify := utils.Rules{
{{- range $index,$element := .Fields }}
{{- if $element.Require }}
"{{$element.FieldName}}":{utils.NotEmpty()},
{{- end }}
{{- end }}
}
if err := utils.Verify({{.Abbreviation}}, verify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
{{- end }}
if err := {{.Abbreviation}}Service.Update{{.StructName}}({{.Abbreviation}}); err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
@@ -156,23 +118,18 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Update{{.StructName}}(c *gin.Con
}
}
// Find{{.StructName}} 用id查询{{.StructName}}
// Find{{.StructName}} 用id查询{{.Description}}
// @Tags {{.StructName}}
// @Summary 用id查询{{.StructName}}
// @Summary 用id查询{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}.{{.StructName}} true "用id查询{{.StructName}}"
// @Param data query {{.Package}}.{{.StructName}} true "用id查询{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /{{.Abbreviation}}/find{{.StructName}} [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Find{{.StructName}}(c *gin.Context) {
var {{.Abbreviation}} {{.Package}}.{{.StructName}}
err := c.ShouldBindQuery(&{{.Abbreviation}})
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if re{{.Abbreviation}}, err := {{.Abbreviation}}Service.Get{{.StructName}}({{.Abbreviation}}.ID); err != nil {
{{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}")
if re{{.Abbreviation}}, err := {{.Abbreviation}}Service.Get{{.StructName}}({{.PrimaryField.FieldJson}}); err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
} else {
@@ -180,13 +137,13 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Find{{.StructName}}(c *gin.Conte
}
}
// Get{{.StructName}}List 分页获取{{.StructName}}列表
// Get{{.StructName}}List 分页获取{{.Description}}列表
// @Tags {{.StructName}}
// @Summary 分页获取{{.StructName}}列表
// @Summary 分页获取{{.Description}}列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.StructName}}列表"
// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /{{.Abbreviation}}/get{{.StructName}}List [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Context) {
@@ -208,3 +165,38 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Co
}, "获取成功", c)
}
}
{{- if .HasDataSource }}
// Get{{.StructName}}DataSource 获取{{.StructName}}的数据源
// @Tags {{.StructName}}
// @Summary 获取{{.StructName}}的数据源
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /{{.Abbreviation}}/get{{.StructName}}DataSource [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}DataSource(c *gin.Context) {
// 此接口为获取数据源定义的数据
if dataSource, err := {{.Abbreviation}}Service.Get{{.StructName}}DataSource(); err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
} else {
response.OkWithData(dataSource, c)
}
}
{{- end }}
// Get{{.StructName}}Public 不需要鉴权的{{.Description}}接口
// @Tags {{.StructName}}
// @Summary 不需要鉴权的{{.Description}}接口
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}Public(c *gin.Context) {
// 此接口不需要鉴权
// 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
response.OkWithDetailed(gin.H{
"info": "不需要鉴权的{{.Description}}接口信息",
}, "获取成功", c)
}
@@ -2,20 +2,34 @@
package {{.Package}}
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
{{ if .HasTimer }}"time"{{ end }}
{{ if .GvaModel }}"github.com/flipped-aurora/gin-vue-admin/server/global"{{ end }}
{{ if or .HasTimer }}"time"{{ end }}
{{ if .NeedJSON }}"gorm.io/datatypes"{{ end }}
)
// {{.StructName}} 结构体
// {{.Description}} 结构体 {{.StructName}}
type {{.StructName}} struct {
global.GVA_MODEL {{- range .Fields}}
{{ if .GvaModel }} global.GVA_MODEL {{ end }}
{{- range .Fields}}
{{- if eq .FieldType "enum" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};"`
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "picture" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "video" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "file" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "pictures" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "richtext" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "json" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if ne .FieldType "string" }}
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"`
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else }}
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"`
{{- end }} {{- end }}
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- end }} {{ if .FieldDesc }}//{{.FieldDesc}} {{ end }} {{- end }}
{{- if .AutoCreateResource }}
CreatedBy uint `gorm:"column:created_by;comment:创建者"`
UpdatedBy uint `gorm:"column:updated_by;comment:更新者"`
@@ -24,7 +38,7 @@ type {{.StructName}} struct {
}
{{ if .TableName }}
// TableName {{.StructName}} 表名
// TableName {{.Description}} {{.StructName}}自定义表名 {{.TableName}}
func ({{.StructName}}) TableName() string {
return "{{.TableName}}"
}
@@ -1,20 +1,41 @@
package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"time"
{{ if or .HasSearchTimer .GvaModel}}"time"{{ end }}
{{ if .NeedJSON }}"gorm.io/datatypes"{{ end }}
)
type {{.StructName}}Search struct{
{{.Package}}.{{.StructName}}
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
{{ if .GvaModel }}
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
{{ end }}
{{- range .Fields}}
{{- if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"`
End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"`
{{- end }}
{{- if ne .FieldSearchType ""}}
{{- if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"`
End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"`
{{- else }}
{{- if eq .FieldType "enum" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}"`
{{- else if eq .FieldType "picture" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "video" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "file" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "pictures" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "richtext" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if ne .FieldType "string" }}
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else }}
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- end }}
{{- end }}
{{- end}}
{{- end }}
request.PageInfo
{{- if .NeedSort}}
@@ -9,19 +9,27 @@ import (
type {{.StructName}}Router struct {
}
// Init{{.StructName}}Router 初始化 {{.StructName}} 路由信息
func (s *{{.StructName}}Router) Init{{.StructName}}Router(Router *gin.RouterGroup) {
// Init{{.StructName}}Router 初始化 {{.Description}} 路由信息
func (s *{{.StructName}}Router) Init{{.StructName}}Router(Router *gin.RouterGroup,PublicRouter *gin.RouterGroup) {
{{.Abbreviation}}Router := Router.Group("{{.Abbreviation}}").Use(middleware.OperationRecord())
{{.Abbreviation}}RouterWithoutRecord := Router.Group("{{.Abbreviation}}")
{{.Abbreviation}}RouterWithoutAuth := PublicRouter.Group("{{.Abbreviation}}")
var {{.Abbreviation}}Api = v1.ApiGroupApp.{{.PackageT}}ApiGroup.{{.StructName}}Api
{
{{.Abbreviation}}Router.POST("create{{.StructName}}", {{.Abbreviation}}Api.Create{{.StructName}}) // 新建{{.StructName}}
{{.Abbreviation}}Router.DELETE("delete{{.StructName}}", {{.Abbreviation}}Api.Delete{{.StructName}}) // 删除{{.StructName}}
{{.Abbreviation}}Router.DELETE("delete{{.StructName}}ByIds", {{.Abbreviation}}Api.Delete{{.StructName}}ByIds) // 批量删除{{.StructName}}
{{.Abbreviation}}Router.PUT("update{{.StructName}}", {{.Abbreviation}}Api.Update{{.StructName}}) // 更新{{.StructName}}
{{.Abbreviation}}Router.POST("create{{.StructName}}", {{.Abbreviation}}Api.Create{{.StructName}}) // 新建{{.Description}}
{{.Abbreviation}}Router.DELETE("delete{{.StructName}}", {{.Abbreviation}}Api.Delete{{.StructName}}) // 删除{{.Description}}
{{.Abbreviation}}Router.DELETE("delete{{.StructName}}ByIds", {{.Abbreviation}}Api.Delete{{.StructName}}ByIds) // 批量删除{{.Description}}
{{.Abbreviation}}Router.PUT("update{{.StructName}}", {{.Abbreviation}}Api.Update{{.StructName}}) // 更新{{.Description}}
}
{
{{.Abbreviation}}RouterWithoutRecord.GET("find{{.StructName}}", {{.Abbreviation}}Api.Find{{.StructName}}) // 根据ID获取{{.StructName}}
{{.Abbreviation}}RouterWithoutRecord.GET("get{{.StructName}}List", {{.Abbreviation}}Api.Get{{.StructName}}List) // 获取{{.StructName}}列表
{{.Abbreviation}}RouterWithoutRecord.GET("find{{.StructName}}", {{.Abbreviation}}Api.Find{{.StructName}}) // 根据ID获取{{.Description}}
{{.Abbreviation}}RouterWithoutRecord.GET("get{{.StructName}}List", {{.Abbreviation}}Api.Get{{.StructName}}List) // 获取{{.Description}}列表
}
{
{{- if .HasDataSource}}
{{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}DataSource", {{.Abbreviation}}Api.Get{{.StructName}}DataSource) // 获取{{.Description}}数据源
{{- end}}
{{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}Public", {{.Abbreviation}}Api.Get{{.StructName}}Public) // 获取{{.Description}}列表
}
}
@@ -3,7 +3,6 @@ package {{.Package}}
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
{{.Package}}Req "github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}/request"
{{- if .AutoCreateResource }}
"gorm.io/gorm"
@@ -20,66 +19,66 @@ type {{.StructName}}Service struct {
{{- $db = printf "global.MustGetGlobalDBByDBName(\"%s\")" .BusinessDB }}
{{- end}}
// Create{{.StructName}} 创建{{.StructName}}记录
// Create{{.StructName}} 创建{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
err = {{$db}}.Create(&{{.Abbreviation}}).Error
func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{.Abbreviation}} *{{.Package}}.{{.StructName}}) (err error) {
err = {{$db}}.Create({{.Abbreviation}}).Error
return err
}
// Delete{{.StructName}} 删除{{.StructName}}记录
// Delete{{.StructName}} 删除{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}({{.PrimaryField.FieldJson}} string{{- if .AutoCreateResource -}},userID uint{{- end -}}) (err error) {
{{- if .AutoCreateResource }}
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 {
if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).Update("deleted_by", userID).Error; err != nil {
return err
}
if err = tx.Delete(&{{.Abbreviation}}).Error; err != nil {
if err = tx.Delete(&{{.Package}}.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error; err != nil {
return err
}
return nil
})
{{- else }}
err = {{$db}}.Delete(&{{.Abbreviation}}).Error
err = {{$db}}.Delete(&{{.Package}}.{{.StructName}}{},"{{.PrimaryField.ColumnName}} = ?",{{.PrimaryField.FieldJson}}).Error
{{- end }}
return err
}
// Delete{{.StructName}}ByIds 批量删除{{.StructName}}记录
// Delete{{.StructName}}ByIds 批量删除{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ByIds(ids request.IdsReq{{- if .AutoCreateResource }},deleted_by uint{{- end}}) (err error) {
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s []string {{- if .AutoCreateResource }},deleted_by uint{{- end}}) (err error) {
{{- if .AutoCreateResource }}
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 {
if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Update("deleted_by", deleted_by).Error; err != nil {
return err
}
if err := tx.Where("id in ?", ids.Ids).Delete(&{{.Package}}.{{.StructName}}{}).Error; err != nil {
if err := tx.Where("{{.PrimaryField.ColumnName}} in ?", {{.PrimaryField.FieldJson}}s).Delete(&{{.Package}}.{{.StructName}}{}).Error; err != nil {
return err
}
return nil
})
{{- else}}
err = {{$db}}.Delete(&[]{{.Package}}.{{.StructName}}{},"id in ?",ids.Ids).Error
err = {{$db}}.Delete(&[]{{.Package}}.{{.StructName}}{},"{{.PrimaryField.ColumnName}} in ?",{{.PrimaryField.FieldJson}}s).Error
{{- end}}
return err
}
// Update{{.StructName}} 更新{{.StructName}}记录
// Update{{.StructName}} 更新{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Update{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
err = {{$db}}.Save(&{{.Abbreviation}}).Error
err = {{$db}}.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?",{{.Abbreviation}}.{{.PrimaryField.FieldName}}).Updates(&{{.Abbreviation}}).Error
return err
}
// Get{{.StructName}} 根据id获取{{.StructName}}记录
// Get{{.StructName}} 根据{{.PrimaryField.FieldJson}}获取{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}(id uint) ({{.Abbreviation}} {{.Package}}.{{.StructName}}, err error) {
err = {{$db}}.Where("id = ?", id).First(&{{.Abbreviation}}).Error
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}({{.PrimaryField.FieldJson}} string) ({{.Abbreviation}} {{.Package}}.{{.StructName}}, err error) {
err = {{$db}}.Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).First(&{{.Abbreviation}}).Error
return
}
// Get{{.StructName}}InfoList 分页获取{{.StructName}}记录
// Get{{.StructName}}InfoList 分页获取{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoList(info {{.Package}}Req.{{.StructName}}Search) (list []{{.Package}}.{{.StructName}}, total int64, err error) {
limit := info.PageSize
@@ -88,12 +87,14 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
db := {{$db}}.Model(&{{.Package}}.{{.StructName}}{})
var {{.Abbreviation}}s []{{.Package}}.{{.StructName}}
// 如果有条件搜索 下方会自动创建搜索语句
{{- if .GvaModel }}
if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil {
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
}
{{- end }}
{{- range .Fields}}
{{- if .FieldSearchType}}
{{- if eq .FieldType "string" }}
{{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") }}
if info.{{.FieldName}} != "" {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
@@ -117,7 +118,7 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
orderMap := make(map[string]bool)
{{- range .Fields}}
{{- if .Sort}}
orderMap["{{.FieldJson}}"] = true
orderMap["{{.ColumnName}}"] = true
{{- end}}
{{- end}}
if orderMap[info.Sort] {
@@ -129,6 +130,22 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
}
{{- end}}
err = db.Limit(limit).Offset(offset).Find(&{{.Abbreviation}}s).Error
if limit != 0 {
db = db.Limit(limit).Offset(offset)
}
err = db.Find(&{{.Abbreviation}}s).Error
return {{.Abbreviation}}s, total, err
}
{{- if .HasDataSource }}
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}DataSource() (res map[string][]map[string]any, err error) {
res = make(map[string][]map[string]any)
{{range $key, $value := .DataSourceMap}}
{{$key}} := make([]map[string]any, 0)
{{$db}}.Table("{{$value.Table}}").Select("{{$value.Label}} as label,{{$value.Value}} as value").Scan(&{{$key}})
res["{{$key}}"] = {{$key}}
{{- end }}
return
}
{{- end }}
@@ -1,12 +1,12 @@
import service from '@/utils/request'
// @Tags {{.StructName}}
// @Summary 创建{{.StructName}}
// @Summary 创建{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.{{.StructName}} true "创建{{.StructName}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Param data body model.{{.StructName}} true "创建{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /{{.Abbreviation}}/create{{.StructName}} [post]
export const create{{.StructName}} = (data) => {
return service({
@@ -17,43 +17,43 @@ export const create{{.StructName}} = (data) => {
}
// @Tags {{.StructName}}
// @Summary 删除{{.StructName}}
// @Summary 删除{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.{{.StructName}} true "删除{{.StructName}}"
// @Param data body model.{{.StructName}} true "删除{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete]
export const delete{{.StructName}} = (data) => {
export const delete{{.StructName}} = (params) => {
return service({
url: '/{{.Abbreviation}}/delete{{.StructName}}',
method: 'delete',
data
params
})
}
// @Tags {{.StructName}}
// @Summary 删除{{.StructName}}
// @Summary 批量删除{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除{{.StructName}}"
// @Param data body request.IdsReq true "批量删除{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete]
export const delete{{.StructName}}ByIds = (data) => {
export const delete{{.StructName}}ByIds = (params) => {
return service({
url: '/{{.Abbreviation}}/delete{{.StructName}}ByIds',
method: 'delete',
data
params
})
}
// @Tags {{.StructName}}
// @Summary 更新{{.StructName}}
// @Summary 更新{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.{{.StructName}} true "更新{{.StructName}}"
// @Param data body model.{{.StructName}} true "更新{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /{{.Abbreviation}}/update{{.StructName}} [put]
export const update{{.StructName}} = (data) => {
@@ -65,11 +65,11 @@ export const update{{.StructName}} = (data) => {
}
// @Tags {{.StructName}}
// @Summary 用id查询{{.StructName}}
// @Summary 用id查询{{.Description}}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query model.{{.StructName}} true "用id查询{{.StructName}}"
// @Param data query model.{{.StructName}} true "用id查询{{.Description}}"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /{{.Abbreviation}}/find{{.StructName}} [get]
export const find{{.StructName}} = (params) => {
@@ -81,11 +81,11 @@ export const find{{.StructName}} = (params) => {
}
// @Tags {{.StructName}}
// @Summary 分页获取{{.StructName}}列表
// @Summary 分页获取{{.Description}}列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.PageInfo true "分页获取{{.StructName}}列表"
// @Param data query request.PageInfo true "分页获取{{.Description}}列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /{{.Abbreviation}}/get{{.StructName}}List [get]
export const get{{.StructName}}List = (params) => {
@@ -95,3 +95,19 @@ export const get{{.StructName}}List = (params) => {
params
})
}
{{- if .HasDataSource}}
// @Tags {{.StructName}}
// @Summary 获取数据源
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /{{.Abbreviation}}/find{{.StructName}}DataSource [get]
export const get{{.StructName}}DataSource = () => {
return service({
url: '/{{.Abbreviation}}/get{{.StructName}}DataSource',
method: 'get',
})
}
{{- end}}
@@ -4,21 +4,29 @@
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
{{- range .Fields}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}">
{{- if .CheckDataSource}}
<el-select v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in dataSource.{{.FieldJson}}" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
{{- if eq .FieldType "bool" }}
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
{{- if eq .FieldType "string" }}
<el-input v-model="formData.{{.FieldJson}}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- if .DictType}}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
<el-input v-model="formData.{{.FieldJson}}" :clearable="{{.Clearable}}" placeholder="请输入{{.FieldDesc}}" />
{{- end }}
{{- 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}}">
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
<el-input v-model.number="formData.{{ .FieldJson }}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- end }}
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" placeholder="选择日期" :clearable="{{.Clearable}}"></el-date-picker>
{{- end }}
@@ -30,7 +38,24 @@
<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 }}" file-type="image"/>
{{- end }}
{{- if eq .FieldType "video" }}
<SelectImage v-model="formData.{{ .FieldJson }}" file-type="video"/>
{{- end }}
{{- if eq .FieldType "pictures" }}
<SelectImage v-model="formData.{{ .FieldJson }}" multiple file-type="image"/>
{{- end }}
{{- if eq .FieldType "file" }}
<SelectFile v-model="formData.{{ .FieldJson }}" />
{{- end }}
{{- if eq .FieldType "json" }}
// 此字段为json结构,可以前端自行控制展示和数据绑定模式 需绑定json的key为 formData.{{.FieldJson}} 后端会按照json的类型进行存取
{{"{{"}} formData.{{.FieldJson}} {{"}}"}}
{{- end }}
{{- end }}
</el-form-item>
{{- end }}
<el-form-item>
<el-button type="primary" @click="save">保存</el-button>
@@ -41,24 +66,37 @@
</div>
</template>
<script>
export default {
name: '{{.StructName}}'
}
</script>
<script setup>
import {
{{- if .HasDataSource }}
get{{.StructName}}DataSource,
{{- end }}
create{{.StructName}},
update{{.StructName}},
find{{.StructName}}
} from '@/api/{{.PackageName}}'
} from '@/api/{{.Package}}/{{.PackageName}}'
defineOptions({
name: '{{.StructName}}Form'
})
// 自动获取字典
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()
@@ -74,6 +112,9 @@ const formData = ref({
{{- if eq .FieldType "string" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "richtext" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "int" }}
{{.FieldJson}}: {{- if .DictType }} undefined{{ else }} 0{{- end }},
{{- end }}
@@ -83,6 +124,21 @@ const formData = ref({
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- if eq .FieldType "picture" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "video" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "pictures" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "file" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "json" }}
{{.FieldJson}}: {},
{{- end }}
{{- end }}
})
// 验证规则
@@ -100,6 +156,17 @@ const rule = reactive({
const elFormRef = ref()
{{- if .HasDataSource }}
const dataSource = ref([])
const getDataSourceFunc = async()=>{
const res = await get{{.StructName}}DataSource()
if (res.code === 0) {
dataSource.value = res.data
}
}
getDataSourceFunc()
{{- end }}
// 初始化方法
const init = async () => {
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
@@ -1,12 +1,22 @@
<template>
<div>
<div class="gva-search-box">
<el-form :inline="true" :model="searchInfo" class="demo-form-inline" @keyup.enter="onSubmit">
<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">
{{- if .GvaModel }}
<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>
{{ end -}}
{{- range .Fields}} {{- if .FieldSearchType}} {{- if eq .FieldType "bool" }}
<el-form-item label="{{.FieldDesc}}" prop="{{.FieldJson}}">
<el-select v-model="searchInfo.{{.FieldJson}}" clearable placeholder="请选择">
@@ -29,14 +39,12 @@
</el-select>
</el-form-item>
{{- else}}
<el-form-item label="{{.FieldDesc}}">
<el-form-item label="{{.FieldDesc}}" prop="{{.FieldJson}}">
{{- if eq .FieldType "float64" "int"}}
{{if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
<el-input v-model.number="searchInfo.start{{.FieldName}}" placeholder="搜索条件(起)" />
<el-input v-model.number="searchInfo.start{{.FieldName}}" placeholder="最小值" />
—
<el-input v-model.number="searchInfo.end{{.FieldName}}" placeholder="搜索条件(止)" />
<el-input v-model.number="searchInfo.end{{.FieldName}}" placeholder="最大值" />
{{- else}}
{{- if .DictType}}
<el-select v-model="searchInfo.{{.FieldJson}}" placeholder="请选择" style="width:100%" :clearable="true" >
@@ -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}}
@@ -68,34 +84,33 @@
<div class="gva-table-box">
<div class="gva-btn-list">
<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 type="primary" link @click="deleteVisible = false">取消</el-button>
<el-button type="primary" @click="onDelete">确定</el-button>
</div>
<template #reference>
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="deleteVisible = true">删除</el-button>
</template>
</el-popover>
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">删除</el-button>
</div>
<el-table
ref="multipleTable"
style="width: 100%"
tooltip-effect="dark"
:data="tableData"
row-key="ID"
row-key="{{.PrimaryField.FieldJson}}"
@selection-change="handleSelectionChange"
{{- if .NeedSort}}
@sort-change="sortChange"
{{- end}}
>
<el-table-column type="selection" width="55" />
{{ if .GvaModel }}
<el-table-column align="left" label="日期" width="180">
<template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
</el-table-column>
{{- range .Fields}}
{{- if .DictType}}
{{ end }}
{{- range .FrontFields}}
{{- if .CheckDataSource }}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
{{"{{"}} filterDataSource(dataSource.{{.FieldJson}},scope.row.{{.FieldJson}}) {{"}}"}}
</template>
</el-table-column>
{{- else if .DictType}}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
{{"{{"}} filterDict(scope.row.{{.FieldJson}},{{.DictType}}Options) {{"}}"}}
@@ -109,11 +124,57 @@
<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}}" :key="index" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
</div>
</template>
</el-table-column>
{{- else if eq .FieldType "video" }}
<el-table-column label="{{.FieldDesc}}" width="200">
<template #default="scope">
<video
style="width: 100px; height: 100px"
muted
preload="metadata"
>
<source :src="getUrl(scope.row.{{.FieldJson}}) + '#t=1'">
</video>
</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 if eq .FieldType "json" }}
<el-table-column label="{{.FieldDesc}}" width="200">
<template #default="scope">
[JSON]
</template>
</el-table-column>
{{- else }}
<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="操作" fixed="right" min-width="240">
<template #default="scope">
<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>
@@ -132,82 +193,144 @@
/>
</div>
</div>
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
<el-form :model="formData" label-position="right" ref="elFormRef" :rules="rule" label-width="80px">
{{- range .Fields}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
{{- if eq .FieldType "bool" }}
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
{{- if eq .FieldType "string" }}
<el-input v-model="formData.{{.FieldJson}}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- end }}
{{- if eq .FieldType "int" }}
{{- if .DictType}}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
<el-input v-model.number="formData.{{ .FieldJson }}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- end }}
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" style="width:100%" placeholder="选择日期" :clearable="{{.Clearable}}" />
{{- end }}
{{- if eq .FieldType "float64" }}
<el-input-number v-model="formData.{{ .FieldJson }}" style="width:100%" :precision="2" :clearable="{{.Clearable}}" />
{{- end }}
{{- if eq .FieldType "enum" }}
<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-drawer destroy-on-close size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
<template #header>
<div class="flex justify-between items-center">
<span class="text-lg">{{"{{"}}type==='create'?'添加':'修改'{{"}}"}}</span>
<div>
<el-button type="primary" @click="enterDialog">确 定</el-button>
<el-button @click="closeDialog">取 消</el-button>
</div>
</div>
</template>
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
{{- range .FrontFields}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
{{- if .CheckDataSource}}
<el-select v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in dataSource.{{.FieldJson}}" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- end }}
</el-form-item>
{{- end }}
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
{{- else }}
{{- if eq .FieldType "bool" }}
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
{{- if eq .FieldType "string" }}
{{- if .DictType}}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
<el-input v-model="formData.{{.FieldJson}}" :clearable="{{.Clearable}}" placeholder="请输入{{.FieldDesc}}" />
{{- end }}
{{- end }}
{{- if eq .FieldType "richtext" }}
<RichEdit v-model="formData.{{.FieldJson}}"/>
{{- end }}
{{- if eq .FieldType "json" }}
// 此字段为json结构,可以前端自行控制展示和数据绑定模式 需绑定json的key为 formData.{{.FieldJson}} 后端会按照json的类型进行存取
{{"{{"}} formData.{{.FieldJson}} {{"}}"}}
{{- end }}
{{- if eq .FieldType "int" }}
<el-input v-model.number="formData.{{ .FieldJson }}" :clearable="{{.Clearable}}" placeholder="请输入{{.FieldDesc}}" />
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" style="width:100%" placeholder="选择日期" :clearable="{{.Clearable}}" />
{{- end }}
{{- if eq .FieldType "float64" }}
<el-input-number v-model="formData.{{ .FieldJson }}" style="width:100%" :precision="2" :clearable="{{.Clearable}}" />
{{- end }}
{{- if eq .FieldType "enum" }}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择{{.FieldDesc}}" 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 }}"
file-type="image"
/>
{{- end }}
{{- if eq .FieldType "pictures" }}
<SelectImage
multiple
v-model="formData.{{ .FieldJson }}"
file-type="image"
/>
{{- end }}
{{- if eq .FieldType "video" }}
<SelectImage
v-model="formData.{{ .FieldJson }}"
file-type="video"
/>
{{- end }}
{{- if eq .FieldType "file" }}
<SelectFile v-model="formData.{{ .FieldJson }}" />
{{- end }}
{{- end }}
</el-form-item>
{{- end }}
</el-form>
</el-drawer>
</div>
</template>
<script>
export default {
name: '{{.StructName}}'
}
</script>
<script setup>
import {
{{- if .HasDataSource }}
get{{.StructName}}DataSource,
{{- end }}
create{{.StructName}},
delete{{.StructName}},
delete{{.StructName}}ByIds,
update{{.StructName}},
find{{.StructName}},
get{{.StructName}}List
} from '@/api/{{.PackageName}}'
} from '@/api/{{.Package}}/{{.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 { getDictFunc, formatDate, formatBoolean, filterDict,filterDataSource, ReturnArrImg, onDownloadFile } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive } from 'vue'
defineOptions({
name: '{{.StructName}}'
})
// 自动化生成的字典(可能为空)以及字段
{{- range $index, $element := .DictTypes}}
const {{ $element }}Options = ref([])
{{- end }}
const formData = ref({
{{- range .Fields}}
{{- range .FrontFields}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
{{- if eq .FieldType "string" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "richtext" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "int" }}
{{.FieldJson}}: {{- if .DictType }} undefined{{ else }} 0{{- end }},
{{- end }}
@@ -217,24 +340,93 @@ const formData = ref({
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- if eq .FieldType "picture" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "video" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "pictures" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "file" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "json" }}
{{.FieldJson}}: {},
{{- end }}
{{- end }}
})
{{- if .HasDataSource }}
const dataSource = ref([])
const getDataSourceFunc = async()=>{
const res = await get{{.StructName}}DataSource()
if (res.code === 0) {
dataSource.value = res.data
}
}
getDataSourceFunc()
{{- end }}
// 验证规则
const rule = reactive({
{{- range .Fields }}
{{- range .FrontFields }}
{{- if eq .Require true }}
{{.FieldJson }} : [{
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 .FrontFields }}
{{- 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)
@@ -246,7 +438,22 @@ const searchInfo = ref({})
{{- if .NeedSort}}
// 排序
const sortChange = ({ prop, order }) => {
searchInfo.value.sort = prop
const sortMap = {
{{- range .FrontFields}}
{{- if and .Sort}}
{{- if not (eq .ColumnName "")}}
{{.FieldJson}}: '{{.ColumnName}}',
{{- end}}
{{- end}}
{{- end}}
}
let sort = sortMap[prop]
if(!sort){
sort = prop.replace(/[A-Z]/g, match => `_${match.toLowerCase()}`)
}
searchInfo.value.sort = sort
searchInfo.value.order = order
getTableData()
}
@@ -260,13 +467,16 @@ const onReset = () => {
// 搜索
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 .FrontFields}}{{- if eq .FieldType "bool" }}
if (searchInfo.value.{{.FieldJson}} === ""){
searchInfo.value.{{.FieldJson}}=null
}{{ end }}{{ end }}
getTableData()
})
}
// 分页
@@ -325,13 +535,14 @@ const deleteRow = (row) => {
})
}
// 批量删除控制标记
const deleteVisible = ref(false)
// 多选删除
const onDelete = async() => {
const ids = []
ElMessageBox.confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
const {{.PrimaryField.FieldJson}}s = []
if (multipleSelection.value.length === 0) {
ElMessage({
type: 'warning',
@@ -341,20 +552,20 @@ const onDelete = async() => {
}
multipleSelection.value &&
multipleSelection.value.map(item => {
ids.push(item.ID)
{{.PrimaryField.FieldJson}}s.push(item.{{.PrimaryField.FieldJson}})
})
const res = await delete{{.StructName}}ByIds({ ids })
const res = await delete{{.StructName}}ByIds({ {{.PrimaryField.FieldJson}}s })
if (res.code === 0) {
ElMessage({
type: 'success',
message: '删除成功'
})
if (tableData.value.length === ids.length && page.value > 1) {
if (tableData.value.length === {{.PrimaryField.FieldJson}}s.length && page.value > 1) {
page.value--
}
deleteVisible.value = false
getTableData()
}
})
}
// 行为控制标记(弹窗内部需要增还是改)
@@ -362,7 +573,7 @@ const type = ref('')
// 更新行
const update{{.StructName}}Func = async(row) => {
const res = await find{{.StructName}}({ ID: row.ID })
const res = await find{{.StructName}}({ {{.PrimaryField.FieldJson}}: row.{{.PrimaryField.FieldJson}} })
type.value = 'update'
if (res.code === 0) {
formData.value = res.data.re{{.Abbreviation}}
@@ -373,7 +584,7 @@ const update{{.StructName}}Func = async(row) => {
// 删除行
const delete{{.StructName}}Func = async (row) => {
const res = await delete{{.StructName}}({ ID: row.ID })
const res = await delete{{.StructName}}({ {{.PrimaryField.FieldJson}}: row.{{.PrimaryField.FieldJson}} })
if (res.code === 0) {
ElMessage({
type: 'success',
@@ -399,7 +610,7 @@ const openDialog = () => {
const closeDialog = () => {
dialogFormVisible.value = false
formData.value = {
{{- range .Fields}}
{{- range .FrontFields}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -444,7 +655,27 @@ 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;
}
.fileBtn{
margin-bottom: 10px;
}
.fileBtn:last-child{
margin-bottom: 0;
}
{{end}}
</style>
-29
View File
@@ -1,29 +0,0 @@
package {{ .Snake}}
import (
{{- if .HasGlobal }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/global"
{{- end }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/router"
"github.com/gin-gonic/gin"
)
type {{ .PlugName}}Plugin struct {
}
func Create{{ .PlugName}}Plug({{- range .Global}} {{.Key}} {{.Type}}, {{- end }})*{{ .PlugName}}Plugin {
{{- if .HasGlobal }}
{{- range .Global}}
global.GlobalConfig.{{.Key}} = {{.Key}}
{{- end }}
{{ end }}
return &{{ .PlugName}}Plugin{}
}
func (*{{ .PlugName}}Plugin) Register(group *gin.RouterGroup) {
router.RouterGroupApp.Init{{ .PlugName}}Router(group)
}
func (*{{ .PlugName}}Plugin) RouterPath() string {
return "{{ .RouterGroup}}"
}
@@ -0,0 +1,72 @@
package {{ .Snake}}
import (
gvaGlobal "github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils"
{{- if .HasGlobal }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/global"
{{- end }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/router"
"github.com/gin-gonic/gin"
)
type {{ .PlugName}}Plugin struct {
}
func Create{{ .PlugName}}Plug({{- range .Global}} {{.Key}} {{.Type}}, {{- end }})*{{ .PlugName }}Plugin {
{{- if .HasGlobal }}
{{- range .Global}}
global.GlobalConfig.{{.Key}} = {{.Key}}
{{- end }}
{{ end }}
gvaGlobal.GVA_DB.AutoMigrate() // 此处可以把插件依赖的数据库结构体自动创建表 需要填写对应的结构体
// 下方会自动注册菜单 第一个参数为菜单一级路由信息一般为定义好的组名 第二个参数为真实使用的web页面路由信息
// 具体值请根据实际情况修改
utils.RegisterMenus(
system.SysBaseMenu{
Path: "{{ .Snake }}Group",
Name: "{{ .Snake }}Group",
Hidden: false,
Component: "view/routerHolder.vue",
Sort: 1000,
Meta: system.Meta{
Title: "{{ .PlugName }}组",
Icon: "school",
},
},
system.SysBaseMenu{
Path: "{{ .Snake }}",
Name: "{{ .Snake }}",
Hidden: false,
Component: "plugin/{{ .Snake }}/view/index.vue",
Sort: 0,
Meta: system.Meta{
Title: "{{ .PlugName }}",
Icon: "school",
},
},
)
// 下方会自动注册api 以下格式为示例格式,请按照实际情况修改
utils.RegisterApis(
system.SysApi{
Path: "/{{ .RouterGroup }}/routerName",
Description: "{{ .PlugName }}接口",
ApiGroup: "{{ .RouterGroup }}",
Method: "POST",
},
)
return &{{ .PlugName}}Plugin{}
}
func (*{{ .PlugName}}Plugin) Register(group *gin.RouterGroup) {
router.RouterGroupApp.Init{{ .PlugName}}Router(group)
}
func (*{{ .PlugName}}Plugin) RouterPath() string {
return "{{ .RouterGroup}}"
}
@@ -0,0 +1,10 @@
import service from '@/utils/request'
// @Router /{{ .RouterGroup }}/routerName
export const routerName = (data) => {
return service({
url: '/{{ .RouterGroup }}/routerName',
method: 'post',
data
})
}

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