445 Commits

Author SHA1 Message Date
5e6c917ab0 若依 3.8.9 2024-12-30 08:49:55 +08:00
9a51563144 代码生成新增配置是否允许文件覆盖到本地 2024-12-25 15:48:16 +08:00
3b2704c181 优化导入带标题文件关闭清理 2024-12-25 15:47:32 +08:00
7232217061 update sqlkeyword 2024-12-25 00:05:16 +08:00
25fd29c5ea 优化特殊字符密码修改失败问题 2024-12-17 14:27:18 +08:00
2d6a6a162f 优化TopNav内链菜单点击没有高亮(IB8WHJ) 2024-12-17 11:56:51 +08:00
164c62743f 优化菜单管理切换Mini布局错乱问题 2024-12-17 11:24:10 +08:00
4ee169b0c8 update README 2024-12-13 20:07:48 +08:00
d487ffc92f 用户管理过滤掉已禁用部门(IB5H7F) 2024-12-11 11:20:09 +08:00
5a1e7bae2c 修改主题样式本地读取 2024-12-07 17:06:50 +08:00
1810f30491 白名单支持对通配符路径匹配 2024-12-04 08:51:17 +08:00
6efceac460 Excel注解支持wrapText是否允许内容换行 2024-12-03 08:58:44 +08:00
77a6350460 修复导出子列表对象只能在最后的问题 2024-12-02 20:36:53 +08:00
58a21ff9d7 !961 修复默认关闭Tags-Views时,内链页面打不开
Merge pull request !961 from Lyb刘同学/master
2024-11-27 09:28:06 +00:00
7f507f5dfa 修复默认关闭Tags-Views时,内链页面打不开 2024-11-27 17:24:53 +08:00
a1a45ef7ac !958 修复TopNav无法正确获取active的问题
Merge pull request !958 from Lyb刘同学/N/A
2024-11-27 00:55:53 +00:00
b343308a97 修复TopNav无法正确获取active的问题
Signed-off-by: Lyb刘同学 <1553592282@qq.com>
2024-11-26 09:24:21 +00:00
0bf7457eb7 优化代码 2024-11-25 22:27:10 +08:00
0f77f524d0 面板兼容移动端显示 2024-11-25 15:39:49 +08:00
747d816be2 参数键值更换为多行文本 2024-11-25 12:15:21 +08:00
262d9e1ff0 菜单面包屑导航支持多层级显示 2024-11-22 20:44:39 +08:00
ab37956874 分栏参数微调 2024-11-22 14:45:58 +08:00
86ab3bf600 用户管理支持分栏拖动 2024-11-22 12:19:56 +08:00
f76908912e 用户头像http(s)链接支持 2024-11-20 10:42:41 +08:00
8df4c72ad1 update .env.staging 2024-11-20 10:41:20 +08:00
6bdcbabc09 update pom.xml 2024-11-08 16:31:54 +08:00
58fca720a9 升级pom依赖到安全版本 2024-11-08 16:24:14 +08:00
e4ccbc6601 支持自定义显示Excel属性列 2024-11-07 22:15:27 +08:00
430e6d4dea 升级spring-framework到安全版本 2024-11-07 22:14:51 +08:00
a0e6295693 升级oshi到最新版本6.6.5 2024-11-05 16:23:52 +08:00
52ba823328 优化无用户编号不校验数据权限 2024-11-05 16:23:42 +08:00
91ae9a164c 校检文件名是否包含特殊字符 2024-11-05 12:49:40 +08:00
d3326987a4 优化身份证脱敏正则 2024-10-21 16:19:17 +08:00
4de087b1ad !937 update ruoyi-ui/src/components/ImageUpload/index.vue.
Merge pull request !937 from AZP/N/A
2024-10-21 08:05:26 +00:00
AZP
5b959b32d7 update ruoyi-ui/src/components/ImageUpload/index.vue.
【fix】修复后台前端上传图片如果图片路径已经携带域名就无需增加前缀域名

Signed-off-by: AZP <2198774759@qq.com>
2024-10-21 03:39:18 +00:00
4358621473 优化权限更新后同步缓存 2024-10-21 10:24:45 +08:00
adb8d51932 操作日志记录DELETE请求参数(IAMV6F) 2024-10-17 12:42:40 +08:00
08a5deb285 升级fastjson到最新版2.0.53 2024-10-17 12:42:24 +08:00
dc9f3ee722 升级quill到最新版本2.0.2 2024-10-15 16:18:02 +08:00
78bb30bb5f 修复码生成上级菜单显示问题(I9CTIJ) 2024-09-27 16:15:17 +08:00
5fad997d38 修复角色禁用权限不失效问题(IAA8ZX) 2024-09-21 11:28:52 +08:00
22a795d041 优化代码 2024-09-08 10:29:41 +08:00
8a0a3a03fe 升级oshi到最新版本6.6.3 2024-08-30 21:46:03 +08:00
ad86486285 update sqlkeyword 2024-08-30 21:45:16 +08:00
3ef6000794 修改时间范围日期格式 2024-07-08 16:45:36 +08:00
f812e99a0d remove sub resultType 2024-07-08 16:38:34 +08:00
2feae7619f avatar add headers 2024-07-02 16:08:30 +08:00
212e3b4977 升级axios到最新版本0.28.1 2024-07-02 12:58:28 +08:00
99e66bf11c 若依 3.8.8 2024-06-30 08:02:22 +08:00
a96d4bf2ed 菜单管理新增路由名称 2024-06-29 19:08:09 +08:00
8264b8fb31 删除多余的依赖 2024-06-27 11:08:31 +08:00
4ec32367fd 升级core-js到最新版本3.37.1 2024-06-27 10:22:55 +08:00
9e8aa14348 优化查表特殊字符使用反斜杠进行转义 2024-06-27 10:22:38 +08:00
10f68b97af 升级spring-security到安全版本,防止漏洞风险 2024-06-26 17:43:14 +08:00
8eff83e2b4 优化代码 2024-06-26 17:40:01 +08:00
7b064d84bb 升级druid到最新版本1.2.23 2024-06-25 12:29:13 +08:00
88560a7aa5 升级oshi到最新版本6.6.1 2024-06-25 12:28:50 +08:00
e14f40670a 优化代码 2024-06-25 12:27:21 +08:00
5b98495067 cron生成的表达式hour优化 2024-06-25 12:02:23 +08:00
259dc67728 优化数据权限代码 2024-06-05 12:30:43 +08:00
bc7a607033 Excel注解新增属性comboReadDict 2024-06-02 19:29:11 +08:00
161cd2b1ea 优化代码生成主子表关联查询方式 2024-06-02 19:28:40 +08:00
7480fb4020 优化导入Excel时设置dictType属性重复查缓存问题 2024-05-30 13:35:43 +08:00
906c3a68b8 添加新群号:151450850 2024-05-29 14:48:56 +08:00
084bab3494 update sql 2024-05-29 14:48:40 +08:00
cc0efa3330 优化代码 2024-05-29 14:48:23 +08:00
f46b1bbebd 限制用户操作数据权限范围 2024-05-29 14:48:03 +08:00
e5f30b1a19 升级spring-framework到安全版本,防止漏洞风险 2024-04-11 16:43:48 +08:00
1140a6c333 新增数据脱敏过滤注解 2024-04-08 13:16:27 +08:00
86ca404dbf 设置表格头单元格文本形式 2024-03-22 16:44:54 +08:00
11320b2e13 Excel注解ColumnType类型新增文本 2024-03-22 16:23:19 +08:00
905c08fb2c 升级oshi到最新版本6.5.0 2024-03-19 16:38:37 +08:00
9386645150 定义Locale默认国际化配置 2024-03-19 16:38:03 +08:00
bf3e2115e3 update vue.config.js 2024-03-18 14:28:28 +08:00
61eb54e4a1 更新compressionPlugin到6.1.2以兼容node18+ 2024-03-18 14:11:26 +08:00
d93e2b9df0 定时任务白名单配置范围缩小 2024-03-11 11:07:29 +08:00
50339c6f73 update copyright 2024 2024-03-11 10:47:55 +08:00
b83f2ff60b 添加新群号:138988063 2024-03-11 10:47:40 +08:00
66128f140f joblog order by 2024-03-11 09:42:15 +08:00
8c990ae9fc 用户密码新增非法字符验证 2024-03-01 21:53:57 +08:00
8836d31d77 升级oshi到最新版本6.4.13 2024-03-01 14:33:56 +08:00
2f624ab5f4 代码生成新增创建表结构功能 2024-03-01 14:33:09 +08:00
80f96b4915 升级oshi到最新版本6.4.11 2024-01-25 11:41:57 +08:00
7e9d050432 update http user-agent 2024-01-25 11:41:20 +08:00
649cfe8652 优化匹配方式 2024-01-25 11:34:25 +08:00
e9ae7ae5f3 !825 update: 修改退出处理类的日志记录和返回内容
Merge pull request !825 from 致远/master
2024-01-05 05:01:24 +00:00
3cc6fb5535 update: 修改退出处理类的日志记录和返回内容 2024-01-04 21:11:13 +08:00
a7bfd3b2d6 !822 删除未生效代码
Merge pull request !822 from mrzxc/fixbug/unuseCodeDelete
2024-01-02 02:20:43 +00:00
08d0326718 fix: delete unuse code 2023-12-25 10:22:05 +08:00
3f4ac65a31 remove packages 2023-12-13 11:51:17 +08:00
94d5c174aa 添加新群号:161281055 2023-12-13 11:47:35 +08:00
e719ac8cff !817 密码输入错误时,登录日志重复
Merge pull request !817 from 也曾为你像超人/N/A
2023-12-13 03:46:20 +00:00
a9bcfc66c3 密码输入错误时,登录日志重复
Signed-off-by: 也曾为你像超人 <1553592282@qq.com>
2023-12-10 14:18:35 +00:00
36b900cef8 若依 3.8.7 2023-12-08 09:03:30 +08:00
ac9302e2a2 升级element-ui到最新版本2.15.14 2023-12-07 11:08:03 +08:00
0f7e3a744e 删除无用的代码 2023-12-07 11:07:30 +08:00
45656b271a 升级oshi到最新版本6.4.8 2023-12-05 11:28:42 +08:00
323e3b7371 升级pagehelper到最新版1.4.7 2023-12-05 11:28:18 +08:00
bfbaa9e7b5 升级druid到最新版本1.2.20 2023-12-05 11:28:05 +08:00
2253a146b3 update fastjson2 2023-12-05 10:48:22 +08:00
2070a9252a 操作日志记录部门名称 2023-12-05 10:47:39 +08:00
e231d78469 修复代码生成导入后必填项与数据库不匹配问题 2023-12-05 10:45:54 +08:00
f74454b61a 删除无用的实例演示开关配置 2023-12-05 10:44:50 +08:00
d71ee5dba1 显隐列组件支持复选框弹出类型 2023-12-01 11:20:12 +08:00
78b1ac4a60 代码生成支持选择前端模板类型 2023-11-30 09:38:07 +08:00
966a17123f 优化代码 2023-11-30 09:37:36 +08:00
42bb8f6445 优化头像上传参数新增文件名称 2023-11-29 12:41:04 +08:00
72e4cd9fb3 优化字典标签支持自定义分隔符 2023-11-29 12:40:47 +08:00
1525bd8b54 优化下载zip方法新增遮罩层 2023-11-29 12:40:01 +08:00
b8e2eeaaf8 优化缓存监控图表支持跟随屏幕大小自适应调整 2023-11-29 12:39:22 +08:00
cbcfabee2a 优化代码 2023-11-29 12:38:45 +08:00
e6d0599b25 优化个人中心/基本资料修改时数据显示问题 2023-11-28 12:36:30 +08:00
b224cebab7 防止高频率定时任务不执行问题 2023-11-28 12:35:04 +08:00
f880dee7a4 !804 update ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java.
Merge pull request !804 from 刚刚好/N/A
2023-11-28 04:07:51 +00:00
f16875c9af !799 update ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java.
Merge pull request !799 from 张利/N/A
2023-11-28 04:04:36 +00:00
a90355eb5e !791 优化白名单页面放行逻辑
Merge pull request !791 from 也曾为你像超人/N/A
2023-11-28 03:54:05 +00:00
386f32a3b7 update ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java.
提交错别字

Signed-off-by: 刚刚好 <380862139@qq.com>
2023-11-12 02:38:46 +00:00
4ca30f08d6 修改权限字符匹配方式 2023-11-10 15:46:27 +08:00
73f881c7d3 修复五级路由缓存无效问题 2023-11-10 15:31:30 +08:00
b357aedaa3 修复内链iframe没有传递参数问题(I8DUOJ) 2023-11-10 11:13:16 +08:00
8cf8c8acd0 修复外链带端口出现的异常(I86J4B) 2023-11-07 11:38:19 +08:00
fbab383bd7 update ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java.
此处新密码加密了两次,多余的操作,且会导致新生成的数据库密码与缓存中的密码不同,如果修改的不对还请讲解回复下,谢谢。

Signed-off-by: 张利 <zhangli_wei555@163.com>
2023-11-02 02:57:04 +00:00
d8255edf84 新增编程式判断资源访问权限 2023-11-01 16:02:53 +08:00
eff42d8b0f !797 修复字典表详情页面搜索bug
Merge pull request !797 from 也曾为你像超人/N/A
2023-11-01 01:57:49 +00:00
1f753e3d84 修复字典表详情页面搜索bug
Signed-off-by: 也曾为你像超人 <1553592282@qq.com>
2023-10-30 03:50:19 +00:00
72d4069537 优化数字金额大写转换精度丢失问题(I81IJA) 2023-10-27 12:25:54 +08:00
76205588f0 update ruoyi-ui/src/permission.js.
Signed-off-by: 也曾为你像超人 <1553592282@qq.com>
2023-10-24 07:45:03 +00:00
7b4ba0146b 升级fastjson到最新版2.0.41 2023-10-21 14:44:02 +08:00
3963e86537 升级oshi到最新版本6.4.6 2023-10-21 14:34:05 +08:00
7098acc968 登录不做数据重复提交验证 2023-10-21 14:31:12 +08:00
079ac841f3 添加新群号:174951577 2023-10-09 21:27:00 +08:00
0434b4ca7a 去掉多余的参数 2023-10-09 21:26:40 +08:00
8873dc9b64 富文本Editor组件检验图片格式 2023-10-02 12:45:27 +08:00
078a3aad5a 修复HeaderSearch组件跳转query参数丢失问题 2023-09-28 22:24:25 +08:00
207a9ce855 操作日志列表新增IP地址查询 2023-09-27 15:21:59 +08:00
9ced1e9766 全局数据存储用户编号 2023-09-27 15:21:37 +08:00
1926840204 优化菜单管理类型为按钮状态可选 2023-09-18 15:04:34 +08:00
006d46ad07 修复自定义字典样式不生效的问题(I81F03) 2023-09-14 16:55:07 +08:00
f5a1b0c550 删除无用的传参 2023-09-01 09:37:16 +08:00
4a78fe116d 优化TopNav菜单没有图标svg不显示 2023-08-31 10:18:25 +08:00
3e95dd21f2 !772 修改未登录访问需要登录的资源,在登录后重定向丢失请求参数问题
Merge pull request !772 from who's hu/pr
2023-08-31 02:17:32 +00:00
491b0f3db8 修复字典缓存删除方法参数错误问题(I7UDIR) 2023-08-23 14:54:20 +08:00
16d8b71e21 update ruoyi-ui/src/permission.js.
由于重定向url存在 http://xxx.xx.xxx/{id}?param={a}&name={b} 的场景, 当未登录访问时, 通过改js封装登录后重定向参数, 会丢失?后的query params
如:
访问 http://localhost:1024/core/doc/doc?id=1683734914907807745&version=31
期望 http://localhost:1024/login?redirect=%2Fcore%2Fdoc%2Fdoc%3Fid%3D1683734914907807745%26version%3D31
实际通过 to.fullPath 封装后 获得 http://localhost:1024/login?redirect=%2Fcore%2Fdoc%2Fdoc%3Fid%3D1683734914907807745&version=31

登录成功跳转到重定向参数url后, 导致version参数丢失.
需要对 to.fullPath 进行一次编码, 以保证重定向前 to.fullPath 的完整性.
通过 ${encodeURIComponent(to.fullPath)} 获得 http://localhost:1024/login?redirect=%2Fcore%2Fdoc%2Fdoc%3Fid%3D1683734914907807745%26version%3D31 完整url



Signed-off-by: who's hu <hup_dev@outlook.com>
2023-08-22 09:25:19 +00:00
90260ce2f9 修复Excels导入时无法获取到dictType字典值问题(I7M4PW) 2023-08-21 15:52:30 +08:00
d58942c506 防重复提交数据大小限制(I7KZDA) 2023-08-21 11:57:14 +08:00
6a742e1d1b Excel导入数据临时文件无法删除问题(I7KIXX) 2023-08-19 15:43:57 +08:00
5b61aea064 修复树模板父级编码变量错误(I7JZ0L) 2023-08-19 14:34:30 +08:00
45ef542687 升级fastjson到最新版2.0.39 2023-08-15 12:17:27 +08:00
4ac7a1aa1f 升级commons.io到最新版本2.13.0 2023-08-15 11:31:38 +08:00
c5e4459bb8 优化代码 2023-08-15 11:30:49 +08:00
8f67bf416b 升级oshi到最新版本6.4.4 2023-08-14 19:11:46 +08:00
ab99a72b65 优化代码 2023-08-14 19:11:13 +08:00
7c9423657e Excel自定义数据处理器增加单元格/工作簿对象 2023-08-14 17:42:44 +08:00
128b186b8e 优化定时任务状态页面显示 2023-08-14 17:42:24 +08:00
68ac40eda9 update maven-plugin 2023-08-14 17:41:52 +08:00
5557433235 添加新群号:143961921 2023-07-28 11:12:09 +08:00
2517e9dddb 优化登录提示信息(I6ADCR) 2023-07-24 15:16:52 +08:00
a0595711ca 优化页签在Firefox浏览器被遮挡的问题 2023-07-06 22:09:16 +08:00
1ffb6379f7 排序属性orderBy参数限制长度 2023-07-06 22:09:02 +08:00
4d5c204b9a 优化代码 2023-07-06 22:08:47 +08:00
8ee740ef49 update sql 2023-07-06 22:07:00 +08:00
6a811d9824 若依 3.8.6 2023-06-30 08:43:54 +08:00
1c9c076280 升级oshi到最新版本6.4.3 2023-06-29 08:50:27 +08:00
918f94d8da 升级fastjson到最新版2.0.34 2023-06-29 08:38:33 +08:00
5db610d16f optimized code 2023-06-28 21:31:25 +08:00
cc6f983ee3 升级spring-boot到最新版本2.5.15 2023-06-24 14:49:03 +08:00
afe2852bbb update banner.txt 2023-06-24 14:48:54 +08:00
9c7d302b94 升级element-ui到最新版本2.15.13 2023-06-24 10:57:40 +08:00
9e66ada9c1 优化代码 2023-06-24 10:57:05 +08:00
a63eec3be4 !714 修改侧边栏的平台标题内容与process.env.VUE_APP_TITLE保持同步
Merge pull request !714 from Yakov/N/A
2023-06-24 02:16:00 +00:00
51990695f5 !729 update ruoyi-admin/src/main/resources/application.yml.
Merge pull request !729 from WhiskyZulu/N/A
2023-06-24 02:15:34 +00:00
a7b8f2ee90 !722 update ruoyi-admin/src/main/resources/banner.txt.
Merge pull request !722 from 万河/N/A
2023-06-24 02:13:44 +00:00
67ba621db6 update ruoyi-admin/src/main/resources/application.yml.
注释不太对,“数组计算”改为“数字计算”

Signed-off-by: WhiskyZulu <a913681304@qq.com>
2023-06-05 01:44:12 +00:00
05feef34c7 update ruoyi-admin/src/main/resources/banner.txt.
线条填歪了,看着难受

Signed-off-by: 万河 <12894283+science-01@user.noreply.gitee.com>
2023-05-18 08:53:14 +00:00
be0b36f6b9 update ruoyi-ui/src/layout/components/Sidebar/Logo.vue.
修改了第38行的/* title: '若依后台管理系统',  */ 为/* title: process.env.VUE_APP_TITLE, */,使得侧边栏的平台标题内容可以和vue.config.js里面的process.env.VUE_APP_TITLE保持同步。

Signed-off-by: yangfanao <2364917935@qq.com>
2023-04-25 09:35:36 +00:00
69bbccbd76 添加新群号:136919097 2023-04-23 15:46:53 +08:00
1eb7b3a03f !713 缓存列表:多次清除操作,提示不变的问题
Merge pull request !713 from 刘立伟/master
2023-04-23 06:57:17 +00:00
4661edf7f0 !712 修复路由跳转被阻止时vue-router内部产生报错信息问题
Merge pull request !712 from 爱吃猫的鱼/master
2023-04-23 06:55:46 +00:00
8485605145 !710 修复代码生成表字段注释不全问题
Merge pull request !710 from zouhuu/dev
2023-04-23 06:54:34 +00:00
a4fe88ca61 !707 恢复翻页/切换路由滚动功能
Merge pull request !707 from 也曾为你像超人/master
2023-04-23 06:53:45 +00:00
af15a3b274 !704 Vue的DictTag组件,当value没有匹配的值时,展示value
Merge pull request !704 from Aurora/master
2023-04-23 06:53:11 +00:00
571393c32c 缓存列表:多次清除操作,提示不变的问题; 2023-04-20 15:18:17 +08:00
eff06c110f 修复路由跳转被阻止时vue-router内部产生报错信息问题 2023-04-20 15:02:38 +08:00
6a18e06339 去除element滚动条 2023-04-17 18:52:46 +08:00
f04ca57f7a update ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml.
修复生成列字段注释显示不全问题

Signed-off-by: zouhuu <zouhugz@163.com>
2023-04-17 08:08:44 +00:00
b4f2a4f7dd 恢复翻页/切换路由滚动功能 2023-04-15 17:01:18 +08:00
de0a43285f update pom.xml.
去除多余代码

Signed-off-by: zouhuu <zouhugz@163.com>
2023-04-14 07:31:50 +00:00
4952ac0a3d 修改DictTag组件,当value没有匹配的值时,展示value 2023-04-12 15:14:09 +08:00
6ad345331d 修复开启TopNav后一级菜单路由参数设置无效问题(I6T1DK) 2023-04-11 16:51:55 +08:00
5a634a4ecd 修复导入用户时无法更新存在用户数据的问题 2023-04-10 18:03:34 +08:00
f5b865a2e1 优化用户导入更新时需获取用户编号问题 2023-04-10 17:58:03 +08:00
f7595e4998 !700 newInstance() 已弃用,使用clazz.getDeclaredConstructor().newInstance()
Merge pull request !700 from Nymph2333/N/A
2023-04-10 09:32:01 +00:00
64e71302e4 !699 修改注释中不存在的参数 set
Merge pull request !699 from bell/N/A
2023-04-10 09:26:01 +00:00
2e99c68ed0 !695 下拉图标选择组件优化:1.已选择图标高亮回显 2.滚动条采用el-scrollbar
Merge pull request !695 from 绿色心情/icon-select
2023-04-10 09:09:43 +00:00
af0e0a110e newInstance() 已弃用,使用clazz.getDeclaredConstructor().newInstance()
This method propagates any exception thrown by the nullary constructor, including a checked exception. Use of this method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler. The Constructor.newInstance method avoids this problem by wrapping any exception thrown by the constructor in a (checked) InvocationTargetException.
The call
 clazz.newInstance()
can be replaced by
 clazz.getDeclaredConstructor().newInstance()
The latter sequence of calls is inferred to be able to throw the additional exception types InvocationTargetException and NoSuchMethodException. Both of these exception types are subclasses of ReflectiveOperationException.

Signed-off-by: Nymph2333 <498092988@qq.com>
2023-04-10 06:27:40 +00:00
bef86e041f 修改注释中不存在的参数 set
Signed-off-by: bell <bellaconly@qq.com>
2023-04-10 03:20:19 +00:00
1067567f1c 下拉图标选择组件优化:1.已选择图标高亮回显 2.滚动条采用el-scrollbar 2023-04-09 13:20:59 +08:00
e
0a670fdfd7 将el-scrollbar移动到main-container下,避免鼠标移出时无法隐藏的问题 2023-04-08 04:47:34 +08:00
a33090c90e 添加新群号:101046199 2023-04-05 17:52:27 +08:00
5061558e94 优化固定头部页签滚动条被隐藏的问题 2023-04-05 17:50:32 +08:00
e7f088552f !686 导出Excel,提高导出效率
Merge pull request !686 from wzy1024/wzy1024
2023-04-05 09:36:45 +00:00
5c4682e060 !683 修复tab栏“关闭其他”异常的问题
Merge pull request !683 from 也曾为你像超人/N/A
2023-04-05 09:35:24 +00:00
5d5ebbec1a !682 解决表字段comment过长问题
Merge pull request !682 from baozhigang/column-comment
2023-04-05 09:35:04 +00:00
23544bab5e !681 移除vue-multiselect样式
Merge pull request !681 from Jimi/master
2023-04-05 09:34:17 +00:00
c5ef0336a4 !676 优化选择图标组件
Merge pull request !676 from 也曾为你像超人/master
2023-04-05 09:30:43 +00:00
a907f8485c 导出Excel,@Excel注解使用dictType属性时,如果有大量的字典数据,就会有大量的查询redis(打开、关闭),导致特别慢。于是使用map存储字典数据,相同的key就不需要再次去查询redis,大大提高了导出效率。 2023-04-04 11:58:26 +08:00
66200c4203 修复tab栏”关闭其他“异常的问题
Signed-off-by: 也曾为你像超人 <1553592282@qq.com>
2023-04-01 03:17:47 +00:00
5a25212509 解决表字段comment过长问题 2023-03-30 20:10:22 +08:00
95742bf5bd style:移除vue-multiselect样式(项目中并未安装vue-multiselect plugin) 2023-03-30 14:58:06 +08:00
4eea8cdbb0 优化选择图标组件 2023-03-18 10:59:05 +08:00
cfce89be7d 升级fastjson到最新版2.0.25 2023-03-18 10:30:34 +08:00
ce7e12ec1d delete build style 2023-03-18 09:31:26 +08:00
4f02f3c6f7 支持自定义隐藏属性列过滤子对象(I6GKPE) 2023-03-17 14:13:39 +08:00
5ca9bd6876 !673 $tab.closePage后存在非首页页签时不应该跳转首页
Merge pull request !673 from Giovanni/master
2023-03-17 06:11:24 +00:00
020a2d4670 !671 优化弹窗后导航栏偏移的问题
Merge pull request !671 from 也曾为你像超人/master
2023-03-17 06:08:47 +00:00
635d621b7b !670 修复页面切换时布局错乱的问题
Merge pull request !670 from 也曾为你像超人/N/A
2023-03-17 05:53:51 +00:00
4cbd56cbd7 !669 用户多角色,数据权限切面处理时可能出现权限抬升的情况。
Merge pull request !669 from 0慕容雪0/master
2023-03-17 05:50:26 +00:00
dcb9cb3d13 优化弹窗后导航栏偏移的问题 2023-03-11 14:42:02 +08:00
628bc94a9a update ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java.
Signed-off-by: 0慕容雪0 <ytu.mxh@163.com>
2023-03-11 04:31:55 +00:00
38ddefe2e6 修复页面切换时布局错乱的问题
Signed-off-by: 也曾为你像超人 <1553592282@qq.com>
2023-03-11 02:19:57 +00:00
7a090bda1e 关闭当前tab页应跳转最右侧tab页而非首页 2023-03-10 18:04:56 +08:00
4e8dd706d5 update ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java.
DataScopeAspect,数据权限切面处理类中,用户多角色情况下,若所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,会导致用户拥有全部数据权限,所以要限制一下, 可以根据conditions集合是否为空,来判断循环时所有角色是否都是在判断权限字符时continue了。
复现方法: 在使用@DataScope注解时permission定义了值,这个值所有角色不包含。

Signed-off-by: 0慕容雪0 <ytu.mxh@163.com>
2023-03-10 08:22:35 +00:00
641e550d7f 优化修改密码日志存储明文问题(I6ESO9) 2023-03-05 12:06:27 +08:00
81a01a1d9d 优化文件下载出现的异常(I6DLNU) 2023-02-28 13:33:12 +08:00
6523fe59a2 日志管理使用索引提升查询性能 2023-02-23 10:01:16 +08:00
90970eb9fe 修复isMatchedIp的参数判断产生空指针的问题 2023-02-22 10:29:28 +08:00
3402b69556 移除apache/commons-fileupload依赖 2023-02-21 18:06:28 +08:00
2c5e3e429f 升级druid到最新版本1.2.16 2023-02-21 18:05:22 +08:00
96ba768f50 优化代码 2023-02-21 18:02:00 +08:00
1268637e58 支持登录IP黑名单限制 2023-02-21 09:00:44 +08:00
61caa7966b 日志注解支持排除指定的请求参数 2023-02-20 16:25:40 +08:00
a5f95eddab 新增监控页面图标显示 2023-02-17 08:55:22 +08:00
ade70583e9 操作日志新增消耗时间属性 2023-02-16 10:22:39 +08:00
5676cf9ad4 修复匿名注解Anonymous空指针问题(I683DT) 2023-02-06 11:20:12 +08:00
c3d0cd5f8c update copyright 2023 2023-02-04 22:26:02 +08:00
eb96afee64 连接池Druid支持新的配置connectTimeout和socketTimeout 2023-02-04 22:25:49 +08:00
5873da87ae 屏蔽定时任务bean违规的字符 2023-02-04 22:25:33 +08:00
4f1933e2e4 !656 tagsView右键选择框,只存在首页时,不应该存在关闭左侧选项
Merge pull request !656 from Giovanni/master
2023-02-04 14:21:28 +00:00
9926f73cd0 tagsView右选框,首页不应该存在关闭左侧选项 2023-02-01 15:58:40 +08:00
492919d4af 升级element-ui到最新版本2.15.12 2023-01-19 12:05:01 +08:00
a7ff50e695 升级fastjson到最新版2.0.23 2023-01-19 12:04:11 +08:00
71e7e1d6dd 字符未使用下划线不进行驼峰式处理 2023-01-19 12:02:48 +08:00
5073f95ccd 添加新群号:108482800 2023-01-11 12:55:29 +08:00
c3a727b2fd 若依 3.8.5 2023-01-01 09:09:25 +08:00
19eaad0129 v3最新版本不需要render-after-expand 2022-12-23 16:08:23 +08:00
565cbb8c7a 升级pagehelper到最新版1.4.6 2022-12-13 19:42:39 +08:00
b2cf949956 修改参数键名时移除前缓存配置 2022-12-13 19:40:48 +08:00
3fec133c69 升级oshi到最新版本6.4.0 2022-12-13 19:39:36 +08:00
c9de6fcd82 优化代码 2022-12-13 19:39:19 +08:00
22ee2c2e94 删除fuse无效选项maxPatternLength 2022-12-08 10:21:48 +08:00
f11db02ff8 修复代码生成图片/文件/单选时选择必填无法校验问题(I64IO2) 2022-12-08 10:19:38 +08:00
d5f5c5d066 升级fastjson到最新版2.0.20 2022-12-08 10:13:39 +08:00
4536906b21 修复Vue3树形下拉不能默认选中(I64ESN) 2022-12-07 20:24:08 +08:00
9748e10339 升级druid到最新版本1.2.15 2022-12-07 11:41:44 +08:00
b08a6ce3f6 升级kaptcha到最新版2.3.3 2022-12-07 10:45:50 +08:00
3039b745a9 升级echarts到最新版本5.4.0 2022-12-07 10:12:15 +08:00
39298d803c 添加新群号:170801498 2022-12-03 12:50:26 +08:00
09bb3e15c6 定时任务违规的字符 2022-12-03 12:48:51 +08:00
3b8a68c4cf 升级oshi到最新版本6.3.2 2022-12-03 12:43:37 +08:00
9aae863ce3 优化弹窗内容过多展示不全问题(I645RU) 2022-12-03 12:42:50 +08:00
963247df43 !627 update ruoyi-ui/src/plugins/download.js.
Merge pull request !627 from Zeno/N/A
2022-12-01 08:21:29 +00:00
7d874e31cf update ruoyi-ui/src/plugins/download.js.
修复文件名包含特殊字符(+、-、*...)的文件无法下载问题

Signed-off-by: Zeno <15270656234@163.com>
2022-11-28 10:29:38 +00:00
1bb6342bcb 修复Log注解GET请求记录不到参数问题 2022-11-22 10:41:40 +08:00
27acbe5b73 修复某些特性的环境生成代码变乱码TXT文件问题 2022-11-22 09:23:17 +08:00
6474a17100 消除Vue3控制台出现的警告信息 2022-11-21 19:10:25 +08:00
142f6ad6a0 兼容Excel下拉框内容过多无法显示的问题(I5XB6I) 2022-11-21 12:20:36 +08:00
b4bdd4f306 开启TopNav没有子菜单隐藏侧边栏 2022-11-17 14:27:30 +08:00
74ba681fee 修复回显数据字典数组异常问题(I60UYQ) 2022-11-15 14:11:52 +08:00
8f2b3ac465 !611 修复调度日志点击多次数据不变化的问题
Merge pull request !611 from 也曾为你像超人/N/A
2022-11-15 06:09:02 +00:00
7eee3b9f02 升级druid到最新版本1.2.14 2022-11-14 11:17:04 +08:00
27e34c2f0a 忽略不必要的属性数据返回 2022-11-12 11:26:48 +08:00
836180fe0e 优化导出对象的子列表为空会出现[]问题 2022-11-11 11:31:27 +08:00
06fbda5324 修复sheet超出最大行数异常问题 2022-11-07 11:20:02 +08:00
bf313b17da !612 根据调度编号获取详细信息参数名改正
Merge pull request !612 from Rain/N/A
2022-10-31 05:51:48 +00:00
a8b9485a29 根据调度编号获取详细信息参数名改正
Signed-off-by: Rain <938448486@qq.com>
2022-10-31 05:51:08 +00:00
101e15d83f 修复调度日志点击多次数据不变化的问题
Signed-off-by: 也曾为你像超人 <1553592282@qq.com>
2022-10-31 05:45:21 +00:00
ebb9f15a75 新增返回警告消息提示 2022-10-30 12:02:06 +08:00
cd137bd9fc 升级fastjson到最新版2.0.16 2022-10-30 09:58:12 +08:00
07bde5f88a !610 pagehelper-boot更新1.4.5
Merge pull request !610 from abbfun/N/A
2022-10-30 01:57:27 +00:00
6fffa02acf pagehelper-boot更新1.4.5
Signed-off-by: abbfun <819589789@qq.com>
2022-10-29 14:45:55 +00:00
dc48f9858b 修复table中更多按钮切换主题色未生效修复问题 2022-10-28 20:59:42 +08:00
a6b2ac5dcd 升级oshi到最新版本6.3.0 2022-10-28 19:57:35 +08:00
c0685b7f7f !608 编辑头像时生成为透明png图片
Merge pull request !608 from BlossomWave/N/A
2022-10-28 11:48:22 +00:00
fb2d616c57 !609 优化代码
Merge pull request !609 from lihy2021/N/A
2022-10-28 11:44:27 +00:00
6ff6853082 !606 重置时取消部门选中
Merge pull request !606 from 也曾为你像超人/N/A
2022-10-28 11:42:26 +00:00
f8014ae969 !602 去除某些svg图标的fill="#bfbfbf"属性,避免菜单激活无法修改其填充颜色
Merge pull request !602 from 清溪先生/master
2022-10-28 11:39:13 +00:00
1de2b7a57e 优化代码 2022-10-27 01:25:39 +00:00
f01aa37394 update ruoyi-ui/src/views/system/user/profile/userAvatar.vue.
默认修改头像时如果上传的图片为png透明图片,生成的头像透明部分会变成黑色,修改了生成头像为png格式。可正常显示图片透明部分。

Signed-off-by: BlossomWave <316975215@qq.com>
2022-10-24 08:25:33 +00:00
4517dea98d 重置时取消部门选中
Signed-off-by: 也曾为你、像超人 <1553592282@qq.com>
2022-10-21 23:24:35 +00:00
e21396870f 修正菜单状态注释信息 2022-10-21 11:56:15 +08:00
b67f6a0fec !604 修复小屏幕上修改头像界面布局错位的问题
Merge pull request !604 from 也曾为你、像超人/master
2022-10-21 03:50:16 +00:00
42d8104505 !603 update ruoyi-ui/src/views/system/user/index.vue.
Merge pull request !603 from kknd97/N/A
2022-10-21 03:46:49 +00:00
f40a0eab23 !601 swagger-ui静态资源缓存
Merge pull request !601 from abbfun/N/A
2022-10-21 03:41:22 +00:00
b6153d1aef 修正选择按钮宽度 2022-10-21 11:21:59 +08:00
a2c585daa4 !605 升级fastjson到最新版2.0.15
Merge pull request !605 from Rain/N/A
2022-10-21 03:11:58 +00:00
5a60bf0b0a 升级fastjson到最新版2.0.15
Signed-off-by: Rain <938448486@qq.com>
2022-10-21 03:01:54 +00:00
9dcddc7876 修复小屏幕上修改头像界面布局错位的问题 2022-10-20 19:18:10 +08:00
b970185536 update ruoyi-ui/src/views/system/user/index.vue.
handleUpdate(row)方法中:this.form = response.data;语句
会导致【this.form.postIds = response.postIds;】和【this.form.roleIds = response.roleIds;】失效。
导致用户编辑对话框中,角色和部门多选框无法正常修改。
建议使用以下语句修改:
this.$set(this.form, "postIds", response.postIds);
this.$set(this.form, "roleIds", response.roleIds);

Signed-off-by: kknd97 <liujingwei@ln.chinamobile.com>
2022-10-20 06:27:02 +00:00
a64a029323 去除某些svg图标的fill="#bfbfbf"属性,避免菜单激活无法修改其填充颜色。
Signed-off-by: 清溪先生 <usfree2021@163.com>
2022-10-19 22:11:14 +08:00
78f4d1c85b swagger-ui静态资源缓存
Signed-off-by: abbfun <819589789@qq.com>
2022-10-19 09:07:35 +00:00
bf83fe568b 修复主题颜色在Drawer组件不会加载问题(I5VCF0) 2022-10-19 10:54:01 +08:00
6505432bf4 修复文件上传组件格式验证问题(I5V32H) 2022-10-12 19:33:58 +08:00
a32a931d24 升级core-js到最新版本3.25.3 2022-10-10 09:22:47 +08:00
4d72fb4289 R isError and isSuccess static 2022-10-10 09:22:37 +08:00
186c04d90a !595 解决导出时包含空子列表数据异常的问题
Merge pull request !595 from 也曾为你、像超人/N/A
2022-10-10 01:20:41 +00:00
c542b7ac32 解决导出时包含空子列表数据异常的问题
Signed-off-by: 也曾为你、像超人 <1553592282@qq.com>
2022-09-30 23:06:10 +00:00
3607e008a3 优化限流打印日志KEY(I5SQ09) 2022-09-28 19:32:55 +08:00
5b8edbf381 修复代码生成勾选属性无效问题 2022-09-28 19:31:52 +08:00
03f7bc0f3f 导入更新用户数据前校验数据权限 2022-09-28 19:26:16 +08:00
e95a1245c5 若依 3.8.4 2022-09-26 08:29:03 +08:00
d7441edae7 升级core-js到最新版本3.25.2 2022-09-22 11:13:49 +08:00
5053361a7f 升级fastjson到最新版2.0.14 2022-09-22 11:13:32 +08:00
acbdd6365a !581 优化日志操作中重置按钮时重复查询的问题
Merge pull request !581 from 也曾为你、像超人/N/A
2022-09-19 06:41:36 +00:00
e781f88eca 优化日志操作中重置按钮时重复查询的问题
Signed-off-by: 也曾为你、像超人 <1553592282@qq.com>
2022-09-19 06:30:47 +00:00
86d16d070d 通用下载方法新增config配置选项(I5PNXE) 2022-09-19 13:09:21 +08:00
854b405d89 修改用户登录账号重复验证 2022-09-18 11:26:03 +08:00
c3faa1a925 升级element-ui到最新版本2.15.10 2022-09-15 08:48:43 +08:00
37d6cc8146 优化代码生成同步后值NULL问题(I5OJDW) 2022-09-13 08:23:30 +08:00
2293822dc2 优化没有权限的用户编辑部门缺少数据(I5OF3O) 2022-09-12 17:22:23 +08:00
f5bec70911 R add isError and isSuccess method 2022-09-12 17:22:07 +08:00
87c53a049b !574 修复 issue#I5Q2ZO
Merge pull request !574 from 捏造的信仰/fix-I5Q2ZO
2022-09-09 01:45:12 +00:00
5bcd2825b6 !568 style重复赋值
Merge pull request !568 from coverme/N/A
2022-09-09 01:25:22 +00:00
036144b9ea 插入 SysOperLog 时,限制 operUrl 属性的长度
Signed-off-by: 捏造的信仰 <yiding.he@gmail.com>
2022-09-07 03:00:51 +00:00
42d602b7a8 添加新群号:160110482 2022-08-28 20:09:25 +08:00
d4b800036c style重复赋值
Signed-off-by: 胡亚飞 <huyafeily@aliyun.com>
2022-08-26 16:26:09 +00:00
2f380f0c41 !564 修复执行任务时,若方法入口在任务的父类,则无法执行的问题
Merge pull request !564 from 捏造的信仰/fix-issue-I5NNXI
2022-08-26 00:42:03 +00:00
39efed1710 修复多文件上传报错出现的异常问题 2022-08-24 11:18:52 +08:00
9a895763d5 优化页面内嵌iframe切换tab不刷新数据 2022-08-23 20:59:26 +08:00
7347cbaedf 优化页面内嵌iframe切换tab不刷新数据 2022-08-23 17:29:09 +08:00
a78b5b7b2a 修复执行任务时,若方法入口在任务的父类,则无法执行的问题
实际项目开发中,可能会为所有定时任务类建一个共同的父类,任务的执行入口在父类定义,以便管理。此时使用 `getDeclaredMethod()` 是无法从子类找到要执行的方法的,而是要换用 `getMethod()` 方法。

Signed-off-by: 捏造的信仰 <yiding.he@gmail.com>
2022-08-23 05:36:29 +00:00
99d1760b98 优化代码 2022-08-22 15:27:58 +08:00
4d71cfa90a !562 修复菜单管理已知问题
Merge pull request !562 from 稚屿/N/A
2022-08-22 06:20:18 +00:00
e02f692359 修复菜单管理已知问题
问题描述:在菜单管理下,类型为菜单或者按钮的条目下点击修改按钮。
情况1,如果是类型为菜单,第一次点击修改按钮正常,则第二次点击另一个条目后面的修改按钮时报错!
情况2,如果是类型为按钮,第一次点击修改时正常,当点击取消按钮关闭弹窗时,浏览器报错!

报错代码:this.resetForm("form")

修复方案:添加遗漏的表单 prop 属性

Signed-off-by: 稚屿 <1491182878@qq.com>
2022-08-22 06:19:08 +00:00
77ec8d1c9a 修复代码生成权限父编号错误(I5NATP) 2022-08-22 12:21:07 +08:00
66ce21ec29 升级fastjson到最新版2.0.12 2022-08-22 12:04:01 +08:00
27e0937235 支持多权限字符匹配角色数据权限 2022-08-22 12:03:51 +08:00
aadb7a41cb 优化Context信息,防止泄漏问题 2022-08-22 10:24:20 +08:00
9b3767a954 优化多角色数据权限匹配规则 2022-08-21 22:53:57 +08:00
851dc54b49 修复图片预览组件src属性为null值控制台报错问题(I5KBAS) 2022-08-15 12:07:24 +08:00
cf2c4e02c6 字典管理操作类型新增其他 2022-08-14 09:40:36 +08:00
88b5715eae 升级oshi到最新版本6.2.2 2022-08-12 12:17:48 +08:00
5b05d4a123 个人中心修改密码去除多余的user传递 2022-08-12 12:17:28 +08:00
893ef39401 防止用户个人信息修改部门 2022-08-12 12:16:30 +08:00
890ad682d7 优化修改资料头像被覆盖的问题(I5LK04) 2022-08-11 13:22:55 +08:00
b9f45057b8 操作日志记录支持排除敏感属性字段 2022-08-10 18:01:02 +08:00
89008c28df 升级fastjson到最新版2.0.11 2022-08-10 14:03:35 +08:00
af7d0a3409 优化导出对象的子列表判断条件 2022-08-10 13:59:57 +08:00
1cd2eef899 优化excel/scale属性导出单元格数值类型 2022-08-09 08:02:24 +08:00
8c7b93ec4f !547 fix: 账户解锁接口去掉多余的 /
Merge pull request !547 from iacker/N/A
2022-08-08 23:44:57 +00:00
3f7bf545b5 fix: 账户解锁接口去掉多余的 / 2022-08-08 13:55:08 +00:00
cbedec7ca6 登录日志新增解锁账户功能 2022-08-08 09:23:52 +08:00
fb1bac2114 新增删除Hash中的某条数据 2022-08-07 19:31:04 +08:00
d0f399a66a 优化xss过滤后格式出现的异常 2022-08-07 19:30:54 +08:00
e73dbd470a Excel注解支持导出对象的子列表方法 2022-08-07 18:33:04 +08:00
e0cd5381e2 数据逻辑删除不进行唯一验证 2022-08-03 16:48:55 +08:00
aee5d417ed 支持配置密码最大错误次数/锁定时间 2022-07-30 14:01:38 +08:00
250c5ba226 优化任务过期不执行调度 2022-07-29 20:28:59 +08:00
b91c848962 !538 优化表格上右侧工具条(搜索按钮显隐&右侧样式凸出)
Merge pull request !538 from abbfun/N/A
2022-07-29 12:28:38 +00:00
d4475d0e8d 优化表格上右侧工具条(搜索按钮显隐&右侧样式凸出)
无搜索条件时可通过search隐藏搜索按钮,工具条组右侧样式超出5px(相对于底部表格),其父节点gutter代码生成默认10,此处也默认10,使工具组样式左右一致
2022-07-27 09:05:31 +00:00
aff54ab5fe 自定义数据权限不排除重复 2022-07-26 20:02:17 +08:00
06177addf5 添加新群号:104748341 2022-07-25 18:45:51 +08:00
9f3b91fe57 !535 所有的覆写方法,必须加@Override注解
Merge pull request !535 from 天才的逻辑/master
2022-07-25 10:32:48 +00:00
80d25863db !536 防止主键字段名与'row'或'ids'一致导致报错的问题。
Merge pull request !536 from 张政/N/A
2022-07-25 10:32:42 +00:00
cf8a7f8678 防止主键字段名与'row'或'ids'一致导致报错的问题。 2022-07-23 08:29:52 +00:00
86fc709b03 升级pagehelper到最新版1.4.3 2022-07-22 14:54:38 +08:00
b8b58cb202 所有的覆写方法,必须加@Override注解 2022-07-22 10:57:00 +08:00
96970ff951 所有的覆写方法,必须加@Override注解 2022-07-22 10:35:31 +08:00
7cc9d17424 支持自定义隐藏Excel属性列 2022-07-21 13:52:05 +08:00
bc8b5f1079 优化布局设置使用el-drawer抽屉显示 2022-07-21 08:50:48 +08:00
5ec5e1a65d 优化字典数据使用store存取 2022-07-20 19:38:16 +08:00
1f0e742710 Excel注解支持backgroundColor属性设置背景色 2022-07-20 09:39:58 +08:00
28b9fbb4d2 优化多个相同角色数据导致权限SQL重复问题 2022-07-19 15:55:03 +08:00
2c79dc906e 升级oshi到最新版本6.2.1 2022-07-14 19:49:32 +08:00
1852017ecc 修改验证码开关变量名 2022-07-14 19:49:00 +08:00
424d11896a !531 去除生成验证码多余的逻辑判断
Merge pull request !531 from 稚屿/N/A
2022-07-14 09:28:46 +00:00
edefee46b2 去除生成验证码多余的逻辑判断 2022-07-13 01:00:44 +00:00
c7c3da2038 升级fastjson到最新版2.0.9 2022-07-12 18:07:05 +08:00
eb9f3d3772 修改验证码开关变量名 2022-07-12 18:04:49 +08:00
1a61790407 !523 修改错误的变量名
Merge pull request !523 from SG/N/A
2022-07-12 10:02:50 +00:00
fe98fba5b5 !527 调增数组格式声明,random随机转换修正
Merge pull request !527 from 靖少毅/jsy20220705
2022-07-12 10:01:56 +00:00
4eb8809a8a 调增数组格式声明,random随机转换修正 2022-07-05 16:43:55 +08:00
0a893d196e update 优化魔法值 2022-07-05 02:06:31 +00:00
SG
ec45cf04af 修改错误的变量名 2022-07-01 13:56:44 +00:00
6f48fc3c58 若依 3.8.3 2022-06-27 08:23:47 +08:00
8558954da7 !516 修复缓存列表页面变量名错误导致错误
Merge pull request !516 from zbk/N/A
2022-06-27 00:10:17 +00:00
zbk
2ae41df23b 修复缓存列表页面变量名错误导致错误 2022-06-26 12:51:55 +00:00
47b8daf69c 字典类型删除多余的mapper注解 2022-06-26 13:10:44 +08:00
ef31a0de42 升级fastjson到最新版2.0.8 2022-06-26 10:37:45 +08:00
8c956d681b 默认不启用压缩文件缓存防止node_modules过大 2022-06-26 08:53:56 +08:00
89eb44afbc 优化字典数据回显样式下拉框显示值 2022-06-26 08:53:15 +08:00
9d7e32fb07 新增缓存列表菜单功能 2022-06-25 09:39:39 +08:00
abb88d622a 调整响应成功状态码与全局保持一致 2022-06-24 09:22:05 +08:00
1ccdd75019 !515 调整响应成功状态码为200,与全局保持一致
Merge pull request !515 from 武玉航/master
2022-06-24 01:16:59 +00:00
aaedafff52 调整响应成功状态码为200,与全局保持一致 2022-06-23 14:42:10 +08:00
3ccf5c0e50 新增内容编码/解码方便插件集成使用 2022-06-22 17:57:33 +08:00
db5fea922f 升级fastjson到最新版2.0.7 2022-06-15 17:08:29 +08:00
0214d93299 删除多余的salt字段 2022-06-15 17:08:06 +08:00
1124a203cc 升级druid到最新版本1.2.11 2022-06-14 21:29:06 +08:00
e8fbf5fdbd reset babel 2022-06-14 21:26:14 +08:00
8fd4ae1282 优化druid开启wall过滤器出现的异常问题 2022-06-13 21:43:31 +08:00
eab1b450d5 优化代码 2022-06-13 21:43:11 +08:00
98fc30786e !504 update 优化新增用户与角色信息、用户与岗位信息逻辑
Merge pull request !504 from 疯狂的狮子Li/N/A
2022-06-13 13:26:04 +00:00
a46c4bf04e update 优化新增用户与角色信息、用户与岗位信息逻辑 2022-06-08 13:53:08 +00:00
b97a662f2e 新增Anonymous匿名访问不鉴权注解 2022-05-28 09:34:05 +08:00
9639c096ba 用户列表查询不显示密码字段 2022-05-27 17:50:49 +08:00
e4af41e89a !497 增加对空字符串参数的过滤
Merge pull request !497 from root/master
2022-05-27 08:53:42 +00:00
52e1717014 增加对空字符串参数的过滤 2022-05-27 12:06:27 +08:00
8a2e3d13f2 表单构建按钮不显示正则校验 2022-05-27 09:18:26 +08:00
a29a0648ad 升级spring-boot到最新版本2.5.14 2022-05-27 08:37:34 +08:00
63d471ec94 修复字典导出序列化报错问题 2022-05-27 08:37:14 +08:00
9fa3eac3aa 升级fastjson到最新版2.0.4 2022-05-26 09:02:32 +08:00
e320c50e49 !492 fastjson 版本升级
Merge pull request !492 from abbfun/N/A
2022-05-24 01:54:24 +00:00
524ad4e6dd fastjson 版本升级
fastjson <= 1.2.80 存在反序列化任意代码执行漏洞
2022-05-23 07:36:18 +00:00
77fffda10e 添加新群号:139821253 2022-05-23 09:38:52 +08:00
da0309eb09 用户头像上传限制只能为图片格式 2022-05-22 18:21:26 +08:00
6805a96e53 接口使用泛型使其看到响应属性字段 2022-05-20 16:25:51 +08:00
a92667c000 新增获取配置文件中的属性值方法 2022-05-20 16:23:52 +08:00
4a0619cadb 优化switch case条件 2022-05-20 16:23:36 +08:00
a7e16d582d !488 修复操作日志列表查询业务类型时的bug
Merge pull request !488 from 也曾为你、像超人/N/A
2022-05-20 07:56:27 +00:00
b5b8d941f3 update ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml.
问题:查业务类型为其他(businessType = 0)的数据时,会查到所有数据

原因:当参数为 Integer 类型 0 时,MyBatis 会把 0 当做空字符串处理
2022-05-17 02:08:37 +00:00
9c3b53b701 用户管理左侧树型组件增加选中高亮保持 2022-05-13 15:02:16 +08:00
5693bbe1f5 升级oshi到最新版本6.1.6 2022-05-11 09:58:25 +08:00
0055f479cb 修复字典数据显示不全问题(I55MR3) 2022-05-09 20:27:09 +08:00
1c41b701dd 优化excel创建表格样式 2022-05-09 17:37:33 +08:00
9476f7f616 升级spring-boot到最新版本2.5.13 2022-05-01 20:22:35 +08:00
cbe405f6ea ui code format 2022-05-01 20:22:25 +08:00
7414bc492e 修改代码生成树选择组件 2022-04-25 10:27:06 +08:00
6f14087a16 修改显示顺序orderNum类型为整型 2022-04-25 10:23:47 +08:00
328d4b916d Excel注解支持color字体颜色 2022-04-23 20:53:16 +08:00
b42a8176ca 设置分页参数默认值 2022-04-17 10:29:12 +08:00
2a9beec31d 检查定时任务bean所在包名是否为白名单配置 2022-04-16 22:06:25 +08:00
910fe6261b 新增获取不带后缀文件名称方法 2022-04-16 22:06:15 +08:00
0c820b96d8 !471 新增一个取文件名而不带后缀的整合方法,干净又卫生啊兄弟们
Merge pull request !471 from XCSDN/N/A
2022-04-16 13:53:10 +00:00
c3c49a05c2 新增一个取文件名而不带后缀的整合方法,干净又卫生啊兄弟们 2022-04-16 06:02:07 +00:00
68a616d7c7 升级element-ui到最新版本2.15.8 2022-04-15 09:32:21 +08:00
a0447de061 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) 2022-04-15 09:31:51 +08:00
46db06a22b !465 update ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java.
Merge pull request !465 from XCSDN/N/A
2022-04-15 01:17:21 +00:00
de5ae4a05e 添加页签openPage支持传递参数 2022-04-08 15:42:10 +08:00
c0d430f4e5 update ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java.
字典类型必须以字母开头,且字典类型只能由小写字母或加下划线还有数字组成
防止由于字典类型不符合前端开发规范而报错
2022-04-07 04:57:56 +00:00
d910888181 代码生成树表新增(展开/折叠) 2022-04-07 10:56:40 +08:00
db2dfee6ae 用户缓存信息添加部门ancestors祖级列表 2022-04-07 09:50:42 +08:00
d311d56bbf 修复Excel注解prompt/combo同时使用不生效问题 2022-04-03 18:28:30 +08:00
3bc5ef38d3 升级spring-boot到最新版本2.5.12 防止RCE漏洞 2022-04-02 10:16:09 +08:00
bac3b3a0dc 降级jsencrypt版本兼容IE浏览器 2022-04-02 10:15:25 +08:00
07fded4da9 update readme 2022-04-02 10:13:58 +08:00
281 changed files with 6892 additions and 3507 deletions

View File

@ -1,11 +1,11 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.1</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.9</h1>
<h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.1-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.9-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
@ -18,12 +18,10 @@
* 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
* 提供了单应用版本[RuoYi-Vue-fast](https://github.com/yangzongzhuan/RuoYi-Vue-fast)Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
* 提供了单应用版本[RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast)Oracle版本[RuoYi-Vue-Oracle](https://gitcode.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
* 特别鸣谢:[element](https://github.com/ElemeFE/element)[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://github.com/elunez/eladmin-web)。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
## 内置功能
@ -94,4 +92,4 @@
## 若依前后端分离交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) 点击按钮入群。
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) 点击按钮入群。

124
pom.xml
View File

@ -6,48 +6,100 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>3.8.2</version>
<version>3.8.9</version>
<name>ruoyi</name>
<url>http://www.ruoyi.vip</url>
<description>若依管理系统</description>
<properties>
<ruoyi.version>3.8.2</ruoyi.version>
<ruoyi.version>3.8.9</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<druid.version>1.2.8</druid.version>
<spring-boot.version>2.5.15</spring-boot.version>
<druid.version>1.2.23</druid.version>
<bitwalker.version>1.21</bitwalker.version>
<swagger.version>3.0.0</swagger.version>
<kaptcha.version>2.3.2</kaptcha.version>
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>
<pagehelper.boot.version>1.4.1</pagehelper.boot.version>
<fastjson.version>1.2.80</fastjson.version>
<oshi.version>6.1.2</oshi.version>
<jna.version>5.10.0</jna.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.fileupload.version>1.4</commons.fileupload.version>
<commons.collections.version>3.2.2</commons.collections.version>
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
<fastjson.version>2.0.53</fastjson.version>
<oshi.version>6.6.5</oshi.version>
<commons.io.version>2.13.0</commons.io.version>
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<!-- override dependency version -->
<tomcat.version>9.0.96</tomcat.version>
<logback.version>1.2.13</logback.version>
<spring-security.version>5.7.12</spring-security.version>
<spring-framework.version>5.3.39</spring-framework.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- 覆盖SpringFramework的依赖配置-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring-framework.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖SpringSecurity的依赖配置-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring-security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.11</version>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖logback的依赖配置-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 覆盖tomcat的依赖配置-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
@ -62,13 +114,6 @@
<version>${bitwalker.version}</version>
</dependency>
<!-- SpringBoot集成mybatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
@ -83,18 +128,6 @@
<version>${oshi.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>${jna.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>${jna.version}</version>
</dependency>
<!-- Swagger3依赖 -->
<dependency>
<groupId>io.springfox</groupId>
@ -115,13 +148,6 @@
<version>${commons.io.version}</version>
</dependency>
<!-- 文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
@ -136,17 +162,10 @@
<version>${velocity.version}</version>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson.version}</version>
</dependency>
@ -159,7 +178,7 @@
<!-- 验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
@ -212,11 +231,6 @@
</modules>
<packaging>pom</packaging>
<dependencies>
</dependencies>
<build>
<plugins>
<plugin>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.2</version>
<version>3.8.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
@ -68,7 +68,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>

View File

@ -6,12 +6,13 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.config.RuoYiConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
@ -45,16 +46,16 @@ public class CaptchaController
public AjaxResult getCode(HttpServletResponse response) throws IOException
{
AjaxResult ajax = AjaxResult.success();
boolean captchaOnOff = configService.selectCaptchaOnOff();
ajax.put("captchaOnOff", captchaOnOff);
if (!captchaOnOff)
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;

View File

@ -1,19 +1,26 @@
package com.ruoyi.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysCache;
/**
* 缓存监控
@ -27,6 +34,17 @@ public class CacheController
@Autowired
private RedisTemplate<String, String> redisTemplate;
private final static List<SysCache> caches = new ArrayList<SysCache>();
{
caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
@ -50,4 +68,54 @@ public class CacheController
result.put("commandStats", pieList);
return AjaxResult.success(result);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getNames")
public AjaxResult cache()
{
return AjaxResult.success(caches);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getKeys/{cacheName}")
public AjaxResult getCacheKeys(@PathVariable String cacheName)
{
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
return AjaxResult.success(new TreeSet<>(cacheKeys));
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getValue/{cacheName}/{cacheKey}")
public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey)
{
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
return AjaxResult.success(sysCache);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheName/{cacheName}")
public AjaxResult clearCacheName(@PathVariable String cacheName)
{
Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*");
redisTemplate.delete(cacheKeys);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheKey/{cacheKey}")
public AjaxResult clearCacheKey(@PathVariable String cacheKey)
{
redisTemplate.delete(cacheKey);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheAll")
public AjaxResult clearCacheAll()
{
Collection<String> cacheKeys = redisTemplate.keys("*");
redisTemplate.delete(cacheKeys);
return AjaxResult.success();
}
}

View File

@ -16,6 +16,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPasswordService;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.service.ISysLogininforService;
@ -31,6 +32,9 @@ public class SysLogininforController extends BaseController
@Autowired
private ISysLogininforService logininforService;
@Autowired
private SysPasswordService passwordService;
@PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
@GetMapping("/list")
public TableDataInfo list(SysLogininfor logininfor)
@ -64,6 +68,15 @@ public class SysLogininforController extends BaseController
public AjaxResult clean()
{
logininforService.cleanLogininfor();
return AjaxResult.success();
return success();
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
@Log(title = "账户解锁", businessType = BusinessType.OTHER)
@GetMapping("/unlock/{userName}")
public AjaxResult unlock(@PathVariable("userName") String userName)
{
passwordService.clearLoginRecordCache(userName);
return success();
}
}

View File

@ -64,6 +64,6 @@ public class SysOperlogController extends BaseController
public AjaxResult clean()
{
operLogService.cleanOperLog();
return AjaxResult.success();
return success();
}
}

View File

@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
@ -42,31 +42,22 @@ public class SysUserOnlineController extends BaseController
@GetMapping("/list")
public TableDataInfo list(String ipaddr, String userName)
{
Collection<String> keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*");
Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
for (String key : keys)
{
LoginUser user = redisCache.getCacheObject(key);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
{
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
else if (StringUtils.isNotEmpty(ipaddr))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()))
{
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
{
if (StringUtils.equals(userName, user.getUsername()))
{
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
else
{
@ -86,7 +77,7 @@ public class SysUserOnlineController extends BaseController
@DeleteMapping("/{tokenId}")
public AjaxResult forceLogout(@PathVariable String tokenId)
{
redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + tokenId);
return AjaxResult.success();
redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
return success();
}
}

View File

@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
@ -64,7 +63,7 @@ public class SysConfigController extends BaseController
@GetMapping(value = "/{configId}")
public AjaxResult getInfo(@PathVariable Long configId)
{
return AjaxResult.success(configService.selectConfigById(configId));
return success(configService.selectConfigById(configId));
}
/**
@ -73,7 +72,7 @@ public class SysConfigController extends BaseController
@GetMapping(value = "/configKey/{configKey}")
public AjaxResult getConfigKey(@PathVariable String configKey)
{
return AjaxResult.success(configService.selectConfigByKey(configKey));
return success(configService.selectConfigByKey(configKey));
}
/**
@ -84,9 +83,9 @@ public class SysConfigController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config)
{
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
if (!configService.checkConfigKeyUnique(config))
{
return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setCreateBy(getUsername());
return toAjax(configService.insertConfig(config));
@ -100,9 +99,9 @@ public class SysConfigController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config)
{
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
if (!configService.checkConfigKeyUnique(config))
{
return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setUpdateBy(getUsername());
return toAjax(configService.updateConfig(config));
@ -129,6 +128,6 @@ public class SysConfigController extends BaseController
public AjaxResult refreshCache()
{
configService.resetConfigCache();
return AjaxResult.success();
return success();
}
}

View File

@ -1,6 +1,5 @@
package com.ruoyi.web.controller.system;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -43,7 +42,7 @@ public class SysDeptController extends BaseController
public AjaxResult list(SysDept dept)
{
List<SysDept> depts = deptService.selectDeptList(dept);
return AjaxResult.success(depts);
return success(depts);
}
/**
@ -54,17 +53,8 @@ public class SysDeptController extends BaseController
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
{
List<SysDept> depts = deptService.selectDeptList(new SysDept());
Iterator<SysDept> it = depts.iterator();
while (it.hasNext())
{
SysDept d = (SysDept) it.next();
if (d.getDeptId().intValue() == deptId
|| ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
{
it.remove();
}
}
return AjaxResult.success(depts);
depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
return success(depts);
}
/**
@ -75,30 +65,7 @@ public class SysDeptController extends BaseController
public AjaxResult getInfo(@PathVariable Long deptId)
{
deptService.checkDeptDataScope(deptId);
return AjaxResult.success(deptService.selectDeptById(deptId));
}
/**
* 获取部门下拉树列表
*/
@GetMapping("/treeselect")
public AjaxResult treeselect(SysDept dept)
{
List<SysDept> depts = deptService.selectDeptList(dept);
return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
}
/**
* 加载对应角色部门列表树
*/
@GetMapping(value = "/roleDeptTreeselect/{roleId}")
public AjaxResult roleDeptTreeselect(@PathVariable("roleId") Long roleId)
{
List<SysDept> depts = deptService.selectDeptList(new SysDept());
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.buildDeptTreeSelect(depts));
return ajax;
return success(deptService.selectDeptById(deptId));
}
/**
@ -109,9 +76,9 @@ public class SysDeptController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept)
{
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
if (!deptService.checkDeptNameUnique(dept))
{
return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
dept.setCreateBy(getUsername());
return toAjax(deptService.insertDept(dept));
@ -127,17 +94,17 @@ public class SysDeptController extends BaseController
{
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
if (!deptService.checkDeptNameUnique(dept))
{
return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
else if (dept.getParentId().equals(deptId))
{
return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
}
else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
{
return AjaxResult.error("该部门包含未停用的子部门!");
return error("该部门包含未停用的子部门!");
}
dept.setUpdateBy(getUsername());
return toAjax(deptService.updateDept(dept));
@ -153,11 +120,11 @@ public class SysDeptController extends BaseController
{
if (deptService.hasChildByDeptId(deptId))
{
return AjaxResult.error("存在下级部门,不允许删除");
return warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId))
{
return AjaxResult.error("部门存在用户,不允许删除");
return warn("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));

View File

@ -66,7 +66,7 @@ public class SysDictDataController extends BaseController
@GetMapping(value = "/{dictCode}")
public AjaxResult getInfo(@PathVariable Long dictCode)
{
return AjaxResult.success(dictDataService.selectDictDataById(dictCode));
return success(dictDataService.selectDictDataById(dictCode));
}
/**
@ -80,7 +80,7 @@ public class SysDictDataController extends BaseController
{
data = new ArrayList<SysDictData>();
}
return AjaxResult.success(data);
return success(data);
}
/**

View File

@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictType;
@ -61,7 +60,7 @@ public class SysDictTypeController extends BaseController
@GetMapping(value = "/{dictId}")
public AjaxResult getInfo(@PathVariable Long dictId)
{
return AjaxResult.success(dictTypeService.selectDictTypeById(dictId));
return success(dictTypeService.selectDictTypeById(dictId));
}
/**
@ -72,9 +71,9 @@ public class SysDictTypeController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict)
{
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
if (!dictTypeService.checkDictTypeUnique(dict))
{
return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setCreateBy(getUsername());
return toAjax(dictTypeService.insertDictType(dict));
@ -88,9 +87,9 @@ public class SysDictTypeController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict)
{
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
if (!dictTypeService.checkDictTypeUnique(dict))
{
return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setUpdateBy(getUsername());
return toAjax(dictTypeService.updateDictType(dict));
@ -117,7 +116,7 @@ public class SysDictTypeController extends BaseController
public AjaxResult refreshCache()
{
dictTypeService.resetDictCache();
return AjaxResult.success();
return success();
}
/**
@ -127,6 +126,6 @@ public class SysDictTypeController extends BaseController
public AjaxResult optionselect()
{
List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
return AjaxResult.success(dictTypes);
return success(dictTypes);
}
}

View File

@ -12,9 +12,11 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysMenuService;
/**
@ -34,6 +36,9 @@ public class SysLoginController
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
/**
* 登录方法
*
@ -59,11 +64,17 @@ public class SysLoginController
@GetMapping("getInfo")
public AjaxResult getInfo()
{
SysUser user = SecurityUtils.getLoginUser().getUser();
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions))
{
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);

View File

@ -41,7 +41,7 @@ public class SysMenuController extends BaseController
public AjaxResult list(SysMenu menu)
{
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return AjaxResult.success(menus);
return success(menus);
}
/**
@ -51,7 +51,7 @@ public class SysMenuController extends BaseController
@GetMapping(value = "/{menuId}")
public AjaxResult getInfo(@PathVariable Long menuId)
{
return AjaxResult.success(menuService.selectMenuById(menuId));
return success(menuService.selectMenuById(menuId));
}
/**
@ -61,7 +61,7 @@ public class SysMenuController extends BaseController
public AjaxResult treeselect(SysMenu menu)
{
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
return success(menuService.buildMenuTreeSelect(menus));
}
/**
@ -85,13 +85,13 @@ public class SysMenuController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu)
{
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
if (!menuService.checkMenuNameUnique(menu))
{
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
return error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
menu.setCreateBy(getUsername());
return toAjax(menuService.insertMenu(menu));
@ -105,17 +105,17 @@ public class SysMenuController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu)
{
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
if (!menuService.checkMenuNameUnique(menu))
{
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
return error("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
else if (menu.getMenuId().equals(menu.getParentId()))
{
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
menu.setUpdateBy(getUsername());
return toAjax(menuService.updateMenu(menu));
@ -131,11 +131,11 @@ public class SysMenuController extends BaseController
{
if (menuService.hasChildByMenuId(menuId))
{
return AjaxResult.error("存在子菜单,不允许删除");
return warn("存在子菜单,不允许删除");
}
if (menuService.checkMenuExistRole(menuId))
{
return AjaxResult.error("菜单已分配,不允许删除");
return warn("菜单已分配,不允许删除");
}
return toAjax(menuService.deleteMenuById(menuId));
}

View File

@ -51,7 +51,7 @@ public class SysNoticeController extends BaseController
@GetMapping(value = "/{noticeId}")
public AjaxResult getInfo(@PathVariable Long noticeId)
{
return AjaxResult.success(noticeService.selectNoticeById(noticeId));
return success(noticeService.selectNoticeById(noticeId));
}
/**

View File

@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
@ -64,7 +63,7 @@ public class SysPostController extends BaseController
@GetMapping(value = "/{postId}")
public AjaxResult getInfo(@PathVariable Long postId)
{
return AjaxResult.success(postService.selectPostById(postId));
return success(postService.selectPostById(postId));
}
/**
@ -75,13 +74,13 @@ public class SysPostController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post)
{
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
if (!postService.checkPostNameUnique(post))
{
return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
else if (!postService.checkPostCodeUnique(post))
{
return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setCreateBy(getUsername());
return toAjax(postService.insertPost(post));
@ -95,13 +94,13 @@ public class SysPostController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post)
{
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
if (!postService.checkPostNameUnique(post))
{
return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
else if (!postService.checkPostCodeUnique(post))
{
return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setUpdateBy(getUsername());
return toAjax(postService.updatePost(post));
@ -125,6 +124,6 @@ public class SysPostController extends BaseController
public AjaxResult optionselect()
{
List<SysPost> posts = postService.selectPostAll();
return AjaxResult.success(posts);
return success(posts);
}
}

View File

@ -1,6 +1,6 @@
package com.ruoyi.web.controller.system;
import java.io.IOException;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -21,6 +20,7 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
@ -61,31 +61,26 @@ public class SysProfileController extends BaseController
public AjaxResult updateProfile(@RequestBody SysUser user)
{
LoginUser loginUser = getLoginUser();
SysUser sysUser = loginUser.getUser();
user.setUserName(sysUser.getUserName());
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
SysUser currentUser = loginUser.getUser();
currentUser.setNickName(user.getNickName());
currentUser.setEmail(user.getEmail());
currentUser.setPhonenumber(user.getPhonenumber());
currentUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
}
user.setUserId(sysUser.getUserId());
user.setPassword(null);
if (userService.updateUserProfile(user) > 0)
if (userService.updateUserProfile(currentUser) > 0)
{
// 更新缓存用户信息
sysUser.setNickName(user.getNickName());
sysUser.setPhonenumber(user.getPhonenumber());
sysUser.setEmail(user.getEmail());
sysUser.setSex(user.getSex());
tokenService.setLoginUser(loginUser);
return AjaxResult.success();
return success();
}
return AjaxResult.error("修改个人信息异常,请联系管理员");
return error("修改个人信息异常,请联系管理员");
}
/**
@ -93,27 +88,30 @@ public class SysProfileController extends BaseController
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword)
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
{
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
return AjaxResult.error("修改密码失败,旧密码错误");
return error("修改密码失败,旧密码错误");
}
if (SecurityUtils.matchesPassword(newPassword, password))
{
return AjaxResult.error("新密码不能与旧密码相同");
return error("新密码不能与旧密码相同");
}
if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0)
{
// 更新缓存用户密码
loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return AjaxResult.success();
return success();
}
return AjaxResult.error("修改密码异常,请联系管理员");
return error("修改密码异常,请联系管理员");
}
/**
@ -121,12 +119,12 @@ public class SysProfileController extends BaseController
*/
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
{
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file);
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
{
AjaxResult ajax = AjaxResult.success();
@ -137,6 +135,6 @@ public class SysProfileController extends BaseController
return ajax;
}
}
return AjaxResult.error("上传图片异常,请联系管理员");
return error("上传图片异常,请联系管理员");
}
}

View File

@ -14,9 +14,9 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
@ -27,6 +27,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
@ -44,13 +45,16 @@ public class SysRoleController extends BaseController
@Autowired
private TokenService tokenService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private ISysUserService userService;
@Autowired
private ISysDeptService deptService;
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/list")
public TableDataInfo list(SysRole role)
@ -78,7 +82,7 @@ public class SysRoleController extends BaseController
public AjaxResult getInfo(@PathVariable Long roleId)
{
roleService.checkRoleDataScope(roleId);
return AjaxResult.success(roleService.selectRoleById(roleId));
return success(roleService.selectRoleById(roleId));
}
/**
@ -89,13 +93,13 @@ public class SysRoleController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role)
{
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
if (!roleService.checkRoleNameUnique(role))
{
return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
else if (!roleService.checkRoleKeyUnique(role))
{
return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setCreateBy(getUsername());
return toAjax(roleService.insertRole(role));
@ -112,13 +116,13 @@ public class SysRoleController extends BaseController
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
if (!roleService.checkRoleNameUnique(role))
{
return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
else if (!roleService.checkRoleKeyUnique(role))
{
return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
@ -128,13 +132,13 @@ public class SysRoleController extends BaseController
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
{
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return AjaxResult.success();
return success();
}
return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
}
/**
@ -182,7 +186,7 @@ public class SysRoleController extends BaseController
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
return AjaxResult.success(roleService.selectRoleAll());
return success(roleService.selectRoleAll());
}
/**
@ -242,4 +246,17 @@ public class SysRoleController extends BaseController
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
* 获取对应角色部门树列表
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/deptTree/{roleId}")
public AjaxResult deptTree(@PathVariable("roleId") Long roleId)
{
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
return ajax;
}
}

View File

@ -17,9 +17,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
@ -27,6 +27,7 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
@ -46,6 +47,9 @@ public class SysUserController extends BaseController
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysDeptService deptService;
@Autowired
private ISysPostService postService;
@ -80,7 +84,7 @@ public class SysUserController extends BaseController
List<SysUser> userList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = userService.importUser(userList, updateSupport, operName);
return AjaxResult.success(message);
return success(message);
}
@PostMapping("/importTemplate")
@ -97,18 +101,18 @@ public class SysUserController extends BaseController
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
userService.checkUserDataScope(userId);
AjaxResult ajax = AjaxResult.success();
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
if (StringUtils.isNotNull(userId))
{
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
@ -120,19 +124,19 @@ public class SysUserController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName())))
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
@ -149,15 +153,19 @@ public class SysUserController extends BaseController
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUpdateBy(getUsername());
return toAjax(userService.updateUser(user));
@ -231,7 +239,18 @@ public class SysUserController extends BaseController
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
return success();
}
/**
* 获取部门树列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept)
{
return success(deptService.selectDeptTreeList(dept));
}
}

View File

@ -1,24 +0,0 @@
package com.ruoyi.web.controller.tool;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ruoyi.common.core.controller.BaseController;
/**
* swagger 接口
*
* @author ruoyi
*/
@Controller
@RequestMapping("/tool/swagger")
public class SwaggerController extends BaseController
{
@PreAuthorize("@ss.hasPermi('tool:swagger:view')")
@GetMapping()
public String index()
{
return redirect("/swagger-ui.html");
}
}

View File

@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@ -40,24 +40,24 @@ public class TestController extends BaseController
@ApiOperation("获取用户列表")
@GetMapping("/list")
public AjaxResult userList()
public R<List<UserEntity>> userList()
{
List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
return AjaxResult.success(userList);
return R.ok(userList);
}
@ApiOperation("获取用户详细")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@GetMapping("/{userId}")
public AjaxResult getUser(@PathVariable Integer userId)
public R<UserEntity> getUser(@PathVariable Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
return AjaxResult.success(users.get(userId));
return R.ok(users.get(userId));
}
else
{
return error("用户不存在");
return R.fail("用户不存在");
}
}
@ -69,44 +69,46 @@ public class TestController extends BaseController
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
})
@PostMapping("/save")
public AjaxResult save(UserEntity user)
public R<String> save(UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
return error("用户ID不能为空");
return R.fail("用户ID不能为空");
}
return AjaxResult.success(users.put(user.getUserId(), user));
users.put(user.getUserId(), user);
return R.ok();
}
@ApiOperation("更新用户")
@PutMapping("/update")
public AjaxResult update(@RequestBody UserEntity user)
public R<String> update(@RequestBody UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
return error("用户ID不能为空");
return R.fail("用户ID不能为空");
}
if (users.isEmpty() || !users.containsKey(user.getUserId()))
{
return error("用户不存在");
return R.fail("用户不存在");
}
users.remove(user.getUserId());
return AjaxResult.success(users.put(user.getUserId(), user));
users.put(user.getUserId(), user);
return R.ok();
}
@ApiOperation("删除用户信息")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@DeleteMapping("/{userId}")
public AjaxResult delete(@PathVariable Integer userId)
public R<String> delete(@PathVariable Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
users.remove(userId);
return success();
return R.ok();
}
else
{
return error("用户不存在");
return R.fail("用户不存在");
}
}
}

View File

@ -1 +1 @@
restart.include.json=/com.alibaba.fastjson.*.jar
restart.include.json=/com.alibaba.fastjson2.*.jar

View File

@ -24,6 +24,10 @@ spring:
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒

View File

@ -3,16 +3,14 @@ ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.8.2
version: 3.8.9
# 版权年份
copyrightYear: 2022
# 实例演示开关
demoEnabled: true
copyrightYear: 2024
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: D:/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数计算 char 字符验证
# 验证码类型 math 数计算 char 字符验证
captchaType: math
# 开发环境配置
@ -39,21 +37,29 @@ logging:
com.ruoyi: debug
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
profiles:
active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
@ -68,7 +74,7 @@ spring:
# 数据库索引
database: 0
# 密码
password:
password:
# 连接超时时间
timeout: 10s
lettuce:
@ -84,27 +90,27 @@ spring:
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
params: count=countSql
# Swagger配置
swagger:
@ -114,7 +120,7 @@ swagger:
pathMapping: /dev-api
# 防止XSS攻击
xss:
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)

View File

@ -5,10 +5,11 @@ user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.2</version>
<version>3.8.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -58,11 +58,11 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- io常用工具类 -->
@ -71,12 +71,6 @@
<artifactId>commons-io</artifactId>
</dependency>
<!-- 文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>

View File

@ -0,0 +1,19 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 匿名访问不鉴权注解
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

View File

@ -25,4 +25,9 @@ public @interface DataScope
* 用户表的别名
*/
public String userAlias() default "";
/**
* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取多个权限用逗号分隔开来
*/
public String permission() default "";
}

View File

@ -5,6 +5,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.ruoyi.common.utils.poi.ExcelHandlerAdapter;
/**
@ -57,17 +59,12 @@ public @interface Excel
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
* 导出类型0数字 1字符串
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出时在excel中每个列的高度 单位为字符
* 导出时在excel中每个列的高度
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽 单位为字符
* 导出时在excel中每个列的宽
*/
public double width() default 16;
@ -86,11 +83,26 @@ public @interface Excel
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
public boolean needMerge() default false;
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
@ -107,9 +119,34 @@ public @interface Excel
public boolean isStatistics() default false;
/**
* 导出字段对齐方式0默认1靠左2居中3靠右
* 导出类型0数字 1字符串 2图片
*/
public Align align() default Align.AUTO;
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景颜色
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
/**
* 导出列头字体颜色
*/
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景颜色
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
/**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器
@ -121,22 +158,6 @@ public @interface Excel
*/
public String[] args() default {};
public enum Align
{
AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
private final int value;
Align(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
/**
* 字段类型0导出导入1仅导出2仅导入
*/
@ -160,7 +181,7 @@ public @interface Excel
public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2);
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
private final int value;
ColumnType(int value)

View File

@ -20,7 +20,7 @@ import com.ruoyi.common.enums.OperatorType;
public @interface Log
{
/**
* 模块
* 模块
*/
public String title() default "";
@ -43,4 +43,9 @@ public @interface Log
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}

View File

@ -5,7 +5,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.enums.LimitType;
/**
@ -21,7 +21,7 @@ public @interface RateLimiter
/**
* 限流key
*/
public String key() default Constants.RATE_LIMIT_KEY;
public String key() default CacheConstants.RATE_LIMIT_KEY;
/**
* 限流时间,单位秒

View File

@ -0,0 +1,24 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.config.serializer.SensitiveJsonSerializer;
import com.ruoyi.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

@ -21,9 +21,6 @@ public class RuoYiConfig
/** 版权年份 */
private String copyrightYear;
/** 实例演示开关 */
private boolean demoEnabled;
/** 上传路径 */
private static String profile;
@ -63,16 +60,6 @@ public class RuoYiConfig
this.copyrightYear = copyrightYear;
}
public boolean isDemoEnabled()
{
return demoEnabled;
}
public void setDemoEnabled(boolean demoEnabled)
{
this.demoEnabled = demoEnabled;
}
public static String getProfile()
{
return profile;

View File

@ -0,0 +1,67 @@
package com.ruoyi.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DesensitizedType;
import com.ruoyi.common.utils.SecurityUtils;
/**
* 数据脱敏序列化过滤
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
else
{
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
this.desensitizedType = annotation.desensitizedType();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
/**
* 是否需要脱敏处理
*/
private boolean desensitization()
{
try
{
LoginUser securityUser = SecurityUtils.getLoginUser();
// 管理员不脱敏
return !securityUser.getUser().isAdmin();
}
catch (Exception e)
{
return true;
}
}
}

View File

@ -0,0 +1,44 @@
package com.ruoyi.common.constant;
/**
* 缓存的key 常量
*
* @author ruoyi
*/
public class CacheConstants
{
/**
* 登录用户 redis key
*/
public static final String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
/**
* 限流 redis key
*/
public static final String RATE_LIMIT_KEY = "rate_limit:";
/**
* 登录账户密码错误次数 redis key
*/
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.constant;
import java.util.Locale;
import io.jsonwebtoken.Claims;
/**
@ -19,6 +20,16 @@ public class Constants
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* www主域
*/
public static final String WWW = "www.";
/**
* http请求
*/
@ -60,24 +71,24 @@ public class Constants
public static final String LOGIN_FAIL = "Error";
/**
* 验证码 redis key
* 所有权限标识
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
public static final String ALL_PERMISSION = "*:*:*";
/**
* 登录用户 redis key
* 管理员角色权限标识
*/
public static final String LOGIN_TOKEN_KEY = "login_tokens:";
public static final String SUPER_ADMIN = "admin";
/**
* 防重提交 redis key
* 角色权限分隔符
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
public static final String ROLE_DELIMETER = ",";
/**
* 限流 redis key
* 权限标识分隔符
*/
public static final String RATE_LIMIT_KEY = "rate_limit:";
public static final String PERMISSION_DELIMETER = ",";
/**
* 验证码有效期(分钟)
@ -124,16 +135,6 @@ public class Constants
*/
public static final String JWT_AUTHORITIES = "authorities";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* 资源映射路径 前缀
*/
@ -154,14 +155,19 @@ public class Constants
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.ruoyi.common.utils.file" };
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}

View File

@ -86,4 +86,9 @@ public class HttpStatus
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
/**
* 系统警告消息
*/
public static final int WARN = 601;
}

View File

@ -21,6 +21,9 @@ public class UserConstants
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";
@ -60,9 +63,9 @@ public class UserConstants
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验返回结果码 */
public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1";
/** 校验是否唯一的返回标识 */
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
/**
* 用户名长度限制

View File

@ -113,6 +113,14 @@ public class BaseController
{
return AjaxResult.success(message);
}
/**
* 返回成功消息
*/
public AjaxResult success(Object data)
{
return AjaxResult.success(data);
}
/**
* 返回失败消息
@ -122,6 +130,14 @@ public class BaseController
return AjaxResult.error(message);
}
/**
* 返回警告消息
*/
public AjaxResult warn(String message)
{
return AjaxResult.warn(message);
}
/**
* 响应返回结果
*

View File

@ -1,6 +1,7 @@
package com.ruoyi.common.core.domain;
import java.util.HashMap;
import java.util.Objects;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;
@ -101,10 +102,33 @@ public class AjaxResult extends HashMap<String, Object>
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
* 返回错误消息
*
* @return
* @return 错误消息
*/
public static AjaxResult error()
{
@ -115,7 +139,7 @@ public class AjaxResult extends HashMap<String, Object>
* 返回错误消息
*
* @param msg 返回内容
* @return 警告消息
* @return 错误消息
*/
public static AjaxResult error(String msg)
{
@ -127,7 +151,7 @@ public class AjaxResult extends HashMap<String, Object>
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
* @return 错误消息
*/
public static AjaxResult error(String msg, Object data)
{
@ -139,13 +163,43 @@ public class AjaxResult extends HashMap<String, Object>
*
* @param code 状态码
* @param msg 返回内容
* @return 警告消息
* @return 错误消息
*/
public static AjaxResult error(int code, String msg)
{
return new AjaxResult(code, msg, null);
}
/**
* 是否为成功消息
*
* @return 结果
*/
public boolean isSuccess()
{
return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
}
/**
* 是否为警告消息
*
* @return 结果
*/
public boolean isWarn()
{
return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
}
/**
* 是否为错误消息
*
* @return 结果
*/
public boolean isError()
{
return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
}
/**
* 方便链式调用
*

View File

@ -5,6 +5,8 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Entity基类
@ -16,6 +18,7 @@ public class BaseEntity implements Serializable
private static final long serialVersionUID = 1L;
/** 搜索值 */
@JsonIgnore
private String searchValue;
/** 创建者 */
@ -36,6 +39,7 @@ public class BaseEntity implements Serializable
private String remark;
/** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public String getSearchValue()

View File

@ -0,0 +1,115 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import com.ruoyi.common.constant.HttpStatus;
/**
* 响应信息主体
*
* @author ruoyi
*/
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
/** 成功 */
public static final int SUCCESS = HttpStatus.SUCCESS;
/** 失败 */
public static final int FAIL = HttpStatus.ERROR;
private int code;
private String msg;
private T data;
public static <T> R<T> ok()
{
return restResult(null, SUCCESS, "操作成功");
}
public static <T> R<T> ok(T data)
{
return restResult(data, SUCCESS, "操作成功");
}
public static <T> R<T> ok(T data, String msg)
{
return restResult(data, SUCCESS, msg);
}
public static <T> R<T> fail()
{
return restResult(null, FAIL, "操作失败");
}
public static <T> R<T> fail(String msg)
{
return restResult(null, FAIL, msg);
}
public static <T> R<T> fail(T data)
{
return restResult(data, FAIL, "操作失败");
}
public static <T> R<T> fail(T data, String msg)
{
return restResult(data, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return restResult(null, code, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
public static <T> Boolean isError(R<T> ret)
{
return !isSuccess(ret);
}
public static <T> Boolean isSuccess(R<T> ret)
{
return R.SUCCESS == ret.getCode();
}
}

View File

@ -4,8 +4,10 @@ import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.utils.StringUtils;
/**
* Treeselect树结构实体类
@ -22,6 +24,9 @@ public class TreeSelect implements Serializable
/** 节点名称 */
private String label;
/** 节点禁用 */
private boolean disabled = false;
/** 子节点 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<TreeSelect> children;
@ -35,6 +40,7 @@ public class TreeSelect implements Serializable
{
this.id = dept.getDeptId();
this.label = dept.getDeptName();
this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus());
this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
@ -65,6 +71,16 @@ public class TreeSelect implements Serializable
this.label = label;
}
public boolean isDisabled()
{
return disabled;
}
public void setDisabled(boolean disabled)
{
this.disabled = disabled;
}
public List<TreeSelect> getChildren()
{
return children;

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -31,7 +32,7 @@ public class SysDept extends BaseEntity
private String deptName;
/** 显示顺序 */
private String orderNum;
private Integer orderNum;
/** 负责人 */
private String leader;
@ -96,13 +97,13 @@ public class SysDept extends BaseEntity
this.deptName = deptName;
}
@NotBlank(message = "显示顺序不能为空")
public String getOrderNum()
@NotNull(message = "显示顺序不能为空")
public Integer getOrderNum()
{
return orderNum;
}
public void setOrderNum(String orderNum)
public void setOrderNum(Integer orderNum)
{
this.orderNum = orderNum;
}

View File

@ -131,7 +131,7 @@ public class SysDictData extends BaseEntity
public boolean getDefault()
{
return UserConstants.YES.equals(this.isDefault) ? true : false;
return UserConstants.YES.equals(this.isDefault);
}
public String getIsDefault()

View File

@ -1,6 +1,7 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -57,6 +58,7 @@ public class SysDictType extends BaseEntity
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
public String getDictType()
{
return dictType;

View File

@ -42,6 +42,9 @@ public class SysMenu extends BaseEntity
/** 路由参数 */
private String query;
/** 路由名称默认和路由地址相同的驼峰格式注意因为vue3版本的router会删除名称相同路由为避免名字的冲突特殊情况可以自定义 */
private String routeName;
/** 是否为外链0是 1否 */
private String isFrame;
@ -53,8 +56,8 @@ public class SysMenu extends BaseEntity
/** 显示状态0显示 1隐藏 */
private String visible;
/** 菜单状态0显示 1隐藏 */
/** 菜单状态0正常 1停用 */
private String status;
/** 权限字符串 */
@ -151,6 +154,16 @@ public class SysMenu extends BaseEntity
this.query = query;
}
public String getRouteName()
{
return routeName;
}
public void setRouteName(String routeName)
{
this.routeName = routeName;
}
public String getIsFrame()
{
return isFrame;
@ -232,7 +245,7 @@ public class SysMenu extends BaseEntity
{
this.children = children;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -242,6 +255,8 @@ public class SysMenu extends BaseEntity
.append("orderNum", getOrderNum())
.append("path", getPath())
.append("component", getComponent())
.append("query", getQuery())
.append("routeName", getRouteName())
.append("isFrame", getIsFrame())
.append("IsCache", getIsCache())
.append("menuType", getMenuType())

View File

@ -1,6 +1,8 @@
package com.ruoyi.common.core.domain.entity;
import java.util.Set;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -31,7 +33,7 @@ public class SysRole extends BaseEntity
/** 角色排序 */
@Excel(name = "角色排序")
private String roleSort;
private Integer roleSort;
/** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 */
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
@ -59,6 +61,9 @@ public class SysRole extends BaseEntity
/** 部门组(数据权限) */
private Long[] deptIds;
/** 角色菜单权限 */
private Set<String> permissions;
public SysRole()
{
@ -113,13 +118,13 @@ public class SysRole extends BaseEntity
this.roleKey = roleKey;
}
@NotBlank(message = "显示顺序不能为空")
public String getRoleSort()
@NotNull(message = "显示顺序不能为空")
public Integer getRoleSort()
{
return roleSort;
}
public void setRoleSort(String roleSort)
public void setRoleSort(Integer roleSort)
{
this.roleSort = roleSort;
}
@ -203,7 +208,17 @@ public class SysRole extends BaseEntity
{
this.deptIds = deptIds;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

View File

@ -5,8 +5,6 @@ import java.util.List;
import javax.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
@ -24,7 +22,7 @@ public class SysUser extends BaseEntity
private static final long serialVersionUID = 1L;
/** 用户ID */
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
/** 部门ID */
@ -44,7 +42,7 @@ public class SysUser extends BaseEntity
private String email;
/** 手机号码 */
@Excel(name = "手机号码")
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
private String phonenumber;
/** 用户性别 */
@ -57,9 +55,6 @@ public class SysUser extends BaseEntity
/** 密码 */
private String password;
/** 盐加密 */
private String salt;
/** 帐号状态0正常 1停用 */
@Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
private String status;
@ -202,8 +197,6 @@ public class SysUser extends BaseEntity
this.avatar = avatar;
}
@JsonIgnore
@JsonProperty
public String getPassword()
{
return password;
@ -214,16 +207,6 @@ public class SysUser extends BaseEntity
this.password = password;
}
public String getSalt()
{
return salt;
}
public void setSalt(String salt)
{
this.salt = salt;
}
public String getStatus()
{
return status;
@ -326,7 +309,6 @@ public class SysUser extends BaseEntity
.append("sex", getSex())
.append("avatar", getAvatar())
.append("password", getPassword())
.append("salt", getSalt())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("loginIp", getLoginIp())

View File

@ -1,11 +1,11 @@
package com.ruoyi.common.core.domain.model;
import java.util.Collection;
import java.util.Set;
import com.alibaba.fastjson2.annotation.JSONField;
import com.ruoyi.common.core.domain.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson.annotation.JSONField;
import com.ruoyi.common.core.domain.entity.SysUser;
import java.util.Collection;
import java.util.Set;
/**
* 登录用户身份权限
@ -71,6 +71,24 @@ public class LoginUser implements UserDetails
*/
private SysUser user;
public LoginUser()
{
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
public Long getUserId()
{
return userId;
@ -101,24 +119,6 @@ public class LoginUser implements UserDetails
this.token = token;
}
public LoginUser()
{
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword()

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.core.page;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.ServletUtils;
/**
@ -40,8 +41,8 @@ public class TableSupport
public static PageDomain getPageDomain()
{
PageDomain pageDomain = new PageDomain();
pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM));
pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));

View File

@ -74,6 +74,28 @@ public class RedisCache
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获取有效时间
*
* @param key Redis键
* @return 有效时间
*/
public long getExpire(final String key)
{
return redisTemplate.getExpire(key);
}
/**
* 判断 key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key)
{
return redisTemplate.hasKey(key);
}
/**
* 获得缓存的基本对象。
*
@ -102,9 +124,9 @@ public class RedisCache
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection)
public boolean deleteObject(final Collection collection)
{
return redisTemplate.delete(collection);
return redisTemplate.delete(collection) > 0;
}
/**
@ -209,18 +231,6 @@ public class RedisCache
return opsForHash.get(key, hKey);
}
/**
* 删除Hash中的数据
*
* @param key
* @param hKey
*/
public void delCacheMapValue(final String key, final String hKey)
{
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hKey);
}
/**
* 获取多个Hash中的数据
*
@ -233,6 +243,18 @@ public class RedisCache
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 删除Hash中的某条数据
*
* @param key Redis键
* @param hKey Hash键
* @return 是否成功
*/
public boolean deleteCacheMapValue(final String key, final String hKey)
{
return redisTemplate.opsForHash().delete(key, hKey) > 0;
}
/**
* 获得缓存的基本对象列表
*

View File

@ -2,6 +2,7 @@ package com.ruoyi.common.core.text;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.NumberFormat;
@ -364,6 +365,10 @@ public class Convert
*/
public static String[] toStrArray(String str)
{
if (StringUtils.isEmpty(str))
{
return new String[] {};
}
return toStrArray(",", str);
}
@ -562,17 +567,12 @@ public class Convert
switch (valueStr)
{
case "true":
return true;
case "false":
return false;
case "yes":
return true;
case "ok":
return true;
case "no":
return false;
case "1":
return true;
case "false":
case "no":
case "0":
return false;
default:
@ -718,7 +718,7 @@ public class Convert
}
if (value instanceof Double)
{
return new BigDecimal((Double) value);
return BigDecimal.valueOf((Double) value);
}
if (value instanceof Integer)
{
@ -899,7 +899,7 @@ public class Convert
*/
public static String toSBC(String input, Set<Character> notConvertSet)
{
char c[] = input.toCharArray();
char[] c = input.toCharArray();
for (int i = 0; i < c.length; i++)
{
if (null != notConvertSet && notConvertSet.contains(c[i]))
@ -941,7 +941,7 @@ public class Convert
*/
public static String toDBC(String text, Set<Character> notConvertSet)
{
char c[] = text.toCharArray();
char[] c = text.toCharArray();
for (int i = 0; i < c.length; i++)
{
if (null != notConvertSet && notConvertSet.contains(c[i]))
@ -982,7 +982,12 @@ public class Convert
String s = "";
for (int i = 0; i < fraction.length; i++)
{
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
// 优化double计算精度丢失问题
BigDecimal nNum = new BigDecimal(n);
BigDecimal decimal = new BigDecimal(10);
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
double d = scale.doubleValue();
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
}
if (s.length() < 1)
{

View File

@ -0,0 +1,59 @@
package com.ruoyi.common.enums;
import java.util.function.Function;
import com.ruoyi.common.utils.DesensitizedUtil;
/**
* 脱敏类型
*
* @author ruoyi
*/
public enum DesensitizedType
{
/**
* 姓名第2位星号替换
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* 密码,全部字符都用*代替
*/
PASSWORD(DesensitizedUtil::password),
/**
* 身份证中间10位星号替换
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
/**
* 手机号中间4位星号替换
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
*/
EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
/**
* 银行卡号保留最后4位其他星号替换
*/
BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
/**
* 车牌号码,包含普通车辆、新能源车辆
*/
CAR_LICENSE(DesensitizedUtil::carLicense);
private final Function<String, String> desensitizer;
DesensitizedType(Function<String, String> desensitizer)
{
this.desensitizer = desensitizer;
}
public Function<String, String> desensitizer()
{
return desensitizer;
}
}

View File

@ -7,7 +7,6 @@ package com.ruoyi.common.exception;
*/
public class GlobalException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
@ -45,6 +44,7 @@ public class GlobalException extends RuntimeException
return this;
}
@Override
public String getMessage()
{
return message;

View File

@ -49,6 +49,7 @@ public final class ServiceException extends RuntimeException
return detailMessage;
}
@Override
public String getMessage()
{
return message;

View File

@ -0,0 +1,61 @@
package com.ruoyi.common.exception.file;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* 文件上传异常类
*
* @author ruoyi
*/
public class FileUploadException extends Exception
{
private static final long serialVersionUID = 1L;
private final Throwable cause;
public FileUploadException()
{
this(null, null);
}
public FileUploadException(final String msg)
{
this(msg, null);
}
public FileUploadException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
@Override
public void printStackTrace(PrintStream stream)
{
super.printStackTrace(stream);
if (cause != null)
{
stream.println("Caused by:");
cause.printStackTrace(stream);
}
}
@Override
public void printStackTrace(PrintWriter writer)
{
super.printStackTrace(writer);
if (cause != null)
{
writer.println("Caused by:");
cause.printStackTrace(writer);
}
}
@Override
public Throwable getCause()
{
return cause;
}
}

View File

@ -1,7 +1,6 @@
package com.ruoyi.common.exception.file;
import java.util.Arrays;
import org.apache.commons.fileupload.FileUploadException;
/**
* 文件上传 误异常类
@ -18,7 +17,7 @@ public class InvalidExtensionException extends FileUploadException
public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
{
super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式");
this.allowedExtension = allowedExtension;
this.extension = extension;
this.filename = filename;

View File

@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
* 黑名单IP异常类
*
* @author ruoyi
*/
public class BlackListException extends UserException
{
private static final long serialVersionUID = 1L;
public BlackListException()
{
super("login.blocked", null);
}
}

View File

@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
* 用户不存在异常类
*
* @author ruoyi
*/
public class UserNotExistsException extends UserException
{
private static final long serialVersionUID = 1L;
public UserNotExistsException()
{
super("user.not.exists", null);
}
}

View File

@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
* 用户错误最大次数异常类
*
* @author ruoyi
*/
public class UserPasswordRetryLimitExceedException extends UserException
{
private static final long serialVersionUID = 1L;
public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime)
{
super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime });
}
}

View File

@ -0,0 +1,24 @@
package com.ruoyi.common.filter;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
/**
* 排除JSON敏感属性
*
* @author ruoyi
*/
public class PropertyPreExcludeFilter extends SimplePropertyPreFilter
{
public PropertyPreExcludeFilter()
{
}
public PropertyPreExcludeFilter addExcludes(String... filters)
{
for (int i = 0; i < filters.length; i++)
{
this.getExcludes().add(filters[i]);
}
return this;
}
}

View File

@ -10,6 +10,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.constant.Constants;
/**
* 构建可重复读取inputStream的request
@ -23,10 +24,10 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
{
super(request);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding(Constants.UTF8);
response.setCharacterEncoding(Constants.UTF8);
body = HttpHelper.getBodyString(request).getBytes("UTF-8");
body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8);
}
@Override

View File

@ -12,6 +12,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.enums.HttpMethod;
/**
* 防止XSS攻击的过滤器
@ -31,10 +32,10 @@ public class XssFilter implements Filter
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url[i]);
excludes.add(url);
}
}
}
@ -59,7 +60,7 @@ public class XssFilter implements Filter
String url = request.getServletPath();
String method = request.getMethod();
// GET DELETE 不过滤
if (method == null || method.matches("GET") || method.matches("DELETE"))
if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))
{
return true;
}

View File

@ -34,13 +34,13 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
if (values != null)
{
int length = values.length;
String[] escapseValues = new String[length];
String[] escapesValues = new String[length];
for (int i = 0; i < length; i++)
{
// 防xss攻击和过滤前后空格
escapseValues[i] = EscapeUtil.clean(values[i]).trim();
escapesValues[i] = EscapeUtil.clean(values[i]).trim();
}
return escapseValues;
return escapesValues;
}
return super.getParameterValues(name);
}

View File

@ -145,16 +145,20 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
}
/**
* 计算两个时间差
* 计算时间差
*
* @param endDate 最后时间
* @param startTime 开始时间
* @return 时间差(天/小时/分钟)
*/
public static String getDatePoor(Date endDate, Date nowDate)
public static String timeDistance(Date endDate, Date startTime)
{
long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60;
long nm = 1000 * 60;
// long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - nowDate.getTime();
long diff = endDate.getTime() - startTime.getTime();
// 计算差多少天
long day = diff / nd;
// 计算差多少小时

View File

@ -0,0 +1,49 @@
package com.ruoyi.common.utils;
/**
* 脱敏工具类
*
* @author ruoyi
*/
public class DesensitizedUtil
{
/**
* 密码的全部字符都用*代替,比如:******
*
* @param password 密码
* @return 脱敏后的密码
*/
public static String password(String password)
{
if (StringUtils.isBlank(password))
{
return StringUtils.EMPTY;
}
return StringUtils.repeat('*', password.length());
}
/**
* 车牌中间用*代替,如果是错误的车牌,不处理
*
* @param carLicense 完整的车牌号
* @return 脱敏后的车牌
*/
public static String carLicense(String carLicense)
{
if (StringUtils.isBlank(carLicense))
{
return StringUtils.EMPTY;
}
// 普通车牌
if (carLicense.length() == 7)
{
carLicense = StringUtils.hide(carLicense, 3, 6);
}
else if (carLicense.length() == 8)
{
// 新能源车牌
carLicense = StringUtils.hide(carLicense, 3, 7);
}
return carLicense;
}
}

View File

@ -2,7 +2,8 @@ package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.List;
import com.ruoyi.common.constant.Constants;
import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
@ -38,10 +39,10 @@ public class DictUtils
*/
public static List<SysDictData> getDictCache(String key)
{
Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(cacheObj))
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(arrayCache))
{
return StringUtils.cast(cacheObj);
return arrayCache.toList(SysDictData.class);
}
return null;
}
@ -55,6 +56,10 @@ public class DictUtils
*/
public static String getDictLabel(String dictType, String dictValue)
{
if (StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
return getDictLabel(dictType, dictValue, SEPARATOR);
}
@ -67,6 +72,10 @@ public class DictUtils
*/
public static String getDictValue(String dictType, String dictLabel)
{
if (StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
return getDictValue(dictType, dictLabel, SEPARATOR);
}
@ -82,8 +91,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictValue) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictValue))
{
for (SysDictData dict : datas)
{
@ -122,8 +134,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictLabel))
{
for (SysDictData dict : datas)
{
@ -150,6 +165,48 @@ public class DictUtils
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型获取字典所有值
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictValues(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictValue()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 根据字典类型获取字典所有标签
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictLabels(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictLabel()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 删除指定字典缓存
*
@ -165,7 +222,7 @@ public class DictUtils
*/
public static void clearDictCache()
{
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*");
SpringUtils.getBean(RedisCache.class).deleteObject(keys);
}
@ -177,6 +234,6 @@ public class DictUtils
*/
public static String getCacheKey(String configKey)
{
return Constants.SYS_DICT_KEY + configKey;
return CacheConstants.SYS_DICT_KEY + configKey;
}
}

View File

@ -20,12 +20,9 @@ public class PageUtils extends PageHelper
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
{
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
/**

View File

@ -1,9 +1,15 @@
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.PatternMatchUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
@ -14,6 +20,7 @@ import com.ruoyi.common.exception.ServiceException;
*/
public class SecurityUtils
{
/**
* 用户ID
**/
@ -43,7 +50,7 @@ public class SecurityUtils
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取用户账户
**/
@ -117,4 +124,55 @@ public class SecurityUtils
{
return userId != null && 1L == userId;
}
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(String permission)
{
return hasPermi(getLoginUser().getPermissions(), permission);
}
/**
* 判断是否包含权限
*
* @param authorities 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(Collection<String> authorities, String permission)
{
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
}
/**
* 验证用户是否拥有某个角色
*
* @param role 角色标识
* @return 用户是否具备某角色
*/
public static boolean hasRole(String role)
{
List<SysRole> roleList = getLoginUser().getUser().getRoles();
Collection<String> roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet());
return hasRole(roles, role);
}
/**
* 判断是否包含角色
*
* @param roles 角色列表
* @param role 角色
* @return 用户是否具备某角色权限
*/
public static boolean hasRole(Collection<String> roles, String role)
{
return roles.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
}
}

View File

@ -1,12 +1,20 @@
package com.ruoyi.common.utils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.text.Convert;
/**
@ -64,6 +72,34 @@ public class ServletUtils
return Convert.toBool(getRequest().getParameter(name), defaultValue);
}
/**
* 获得所有请求参数
*
* @param request 请求对象{@link ServletRequest}
* @return Map
*/
public static Map<String, String[]> getParams(ServletRequest request)
{
final Map<String, String[]> map = request.getParameterMap();
return Collections.unmodifiableMap(map);
}
/**
* 获得所有请求参数
*
* @param request 请求对象{@link ServletRequest}
* @return Map
*/
public static Map<String, String> getParamMap(ServletRequest request)
{
Map<String, String> params = new HashMap<>();
for (Map.Entry<String, String[]> entry : getParams(request).entrySet())
{
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
}
return params;
}
/**
* 获取request
*/
@ -143,4 +179,40 @@ public class ServletUtils
String ajax = request.getParameter("__ajax");
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
}
/**
* 内容编码
*
* @param str 内容
* @return 编码后的内容
*/
public static String urlEncode(String str)
{
try
{
return URLEncoder.encode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return StringUtils.EMPTY;
}
}
/**
* 内容解码
*
* @param str 内容
* @return 解码后的内容
*/
public static String urlDecode(String str)
{
try
{
return URLDecoder.decode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return StringUtils.EMPTY;
}
}
}

View File

@ -23,6 +23,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
/** 下划线 */
private static final char SEPARATOR = '_';
/** 星号 */
private static final char ASTERISK = '*';
/**
* 获取参数不为空值
*
@ -163,6 +166,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return (str == null ? "" : str.trim());
}
/**
* 替换指定字符串的指定区间内字符为"*"
*
* @param str 字符串
* @param startInclude 开始位置(包含)
* @param endExclude 结束位置(不包含)
* @return 替换后的字符串
*/
public static String hide(CharSequence str, int startInclude, int endExclude)
{
if (isEmpty(str))
{
return NULLSTR;
}
final int strLength = str.length();
if (startInclude > strLength)
{
return NULLSTR;
}
if (endExclude > strLength)
{
endExclude = strLength;
}
if (startInclude > endExclude)
{
// 如果起始位置大于结束位置,不替换
return NULLSTR;
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++)
{
if (i >= startInclude && i < endExclude)
{
chars[i] = ASTERISK;
}
else
{
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
/**
* 截取字符串
*
@ -240,6 +286,30 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return str.substring(start, end);
}
/**
* 判断是否为空,并且不是空白字符
*
* @param str 要判断的value
* @return 结果
*/
public static boolean hasText(String str)
{
return (str != null && !str.isEmpty() && containsText(str));
}
private static boolean containsText(CharSequence str)
{
int strLen = str.length();
for (int i = 0; i < strLen; i++)
{
if (!Character.isWhitespace(str.charAt(i)))
{
return true;
}
}
return false;
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
@ -324,6 +394,32 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return list;
}
/**
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
*
* @param collection 给定的集合
* @param array 给定的数组
* @return boolean 结果
*/
public static boolean containsAny(Collection<String> collection, String... array)
{
if (isEmpty(collection) || isEmpty(array))
{
return false;
}
else
{
for (String str : array)
{
if (collection.contains(str))
{
return true;
}
}
return false;
}
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
@ -455,7 +551,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
}
/**
* 驼峰式命名法 例如user_name->userName
* 驼峰式命名法
* 例如user_name->userName
*/
public static String toCamelCase(String s)
{
@ -463,6 +560,10 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
{
return null;
}
if (s.indexOf(SEPARATOR) == -1)
{
return s;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;

View File

@ -25,7 +25,7 @@ public class FileUploadUtils
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
/**
* 默认的文件名最大长度 100
@ -193,7 +193,6 @@ public class FileUploadUtils
throw new InvalidExtensionException(allowedExtension, extension, fileName);
}
}
}
/**

View File

@ -17,6 +17,7 @@ import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import org.apache.commons.io.FilenameUtils;
/**
* 文件处理工具类
@ -115,8 +116,7 @@ public class FileUtils
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists())
{
file.delete();
flag = true;
flag = file.delete();
}
return flag;
}
@ -256,7 +256,7 @@ public class FileUtils
}
/**
* 获取名称
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
*
* @param fileName 路径名称
* @return 没有文件路径的名称
@ -272,4 +272,20 @@ public class FileUtils
int index = Math.max(lastUnixPos, lastWindowsPos);
return fileName.substring(index + 1);
}
/**
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
*
* @param fileName 路径名称
* @return 没有文件路径和后缀的名称
*/
public static String getNameNotSuffix(String fileName)
{
if (fileName == null)
{
return null;
}
String baseName = FilenameUtils.getBaseName(fileName);
return baseName;
}
}

View File

@ -387,7 +387,7 @@ public final class HTMLFilter
{
paramValue = processParamProtocol(paramValue);
}
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\"");
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
}
}

View File

@ -74,7 +74,7 @@ public class HttpUtils
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
@ -136,7 +136,7 @@ public class HttpUtils
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
@ -202,7 +202,7 @@ public class HttpUtils
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);

View File

@ -2,7 +2,8 @@ package com.ruoyi.common.utils.ip;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
@ -40,7 +41,7 @@ public class AddressUtils
log.error("获取地理位置异常 {}", ip);
return UNKNOWN;
}
JSONObject obj = JSONObject.parseObject(rspStr);
JSONObject obj = JSON.parseObject(rspStr);
String region = obj.getString("pro");
String city = obj.getString("city");
return String.format("%s %s", region, city);

View File

@ -3,6 +3,7 @@ package com.ruoyi.common.utils.ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
/**
@ -12,6 +13,23 @@ import com.ruoyi.common.utils.StringUtils;
*/
public class IpUtils
{
public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
// 匹配 ip
public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
// 匹配网段
public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
/**
* 获取客户端IP
*
* @return IP地址
*/
public static String getIpAddr()
{
return getIpAddr(ServletUtils.getRequest());
}
/**
* 获取客户端IP
*
@ -248,7 +266,7 @@ public class IpUtils
}
}
}
return ip;
return StringUtils.substring(ip, 0, 255);
}
/**
@ -261,4 +279,104 @@ public class IpUtils
{
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
/**
* 是否为IP
*/
public static boolean isIP(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
}
/**
* 是否为IP或 *为间隔的通配符地址
*/
public static boolean isIpWildCard(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
}
/**
* 检测参数是否在ip通配符里
*/
public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip)
{
String[] s1 = ipWildCard.split("\\.");
String[] s2 = ip.split("\\.");
boolean isMatchedSeg = true;
for (int i = 0; i < s1.length && !s1[i].equals("*"); i++)
{
if (!s1[i].equals(s2[i]))
{
isMatchedSeg = false;
break;
}
}
return isMatchedSeg;
}
/**
* 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
*/
public static boolean isIPSegment(String ipSeg)
{
return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
}
/**
* 判断ip是否在指定网段中
*/
public static boolean ipIsInNetNoCheck(String iparea, String ip)
{
int idx = iparea.indexOf('-');
String[] sips = iparea.substring(0, idx).split("\\.");
String[] sipe = iparea.substring(idx + 1).split("\\.");
String[] sipt = ip.split("\\.");
long ips = 0L, ipe = 0L, ipt = 0L;
for (int i = 0; i < 4; ++i)
{
ips = ips << 8 | Integer.parseInt(sips[i]);
ipe = ipe << 8 | Integer.parseInt(sipe[i]);
ipt = ipt << 8 | Integer.parseInt(sipt[i]);
}
if (ips > ipe)
{
long t = ips;
ips = ipe;
ipe = t;
}
return ips <= ipt && ipt <= ipe;
}
/**
* 校验ip是否符合过滤串规则
*
* @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
* @param ip 校验IP地址
* @return boolean 结果
*/
public static boolean isMatchedIp(String filter, String ip)
{
if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip))
{
return false;
}
String[] ips = filter.split(";");
for (String iStr : ips)
{
if (isIP(iStr) && iStr.equals(ip))
{
return true;
}
else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip))
{
return true;
}
else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip))
{
return true;
}
}
return false;
}
}

View File

@ -1,5 +1,8 @@
package com.ruoyi.common.utils.poi;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Excel数据格式处理适配器
*
@ -12,8 +15,10 @@ public interface ExcelHandlerAdapter
*
* @param value 单元格数据值
* @param args excel注解args参数组
* @param cell 单元格对象
* @param wb 工作簿对象
*
* @return 处理后的值
*/
Object format(Object value, String[] args);
Object format(Object value, String[] args, Cell cell, Workbook wb);
}

View File

@ -7,12 +7,14 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
@ -22,7 +24,9 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPicture;
import org.apache.poi.hssf.usermodel.HSSFPictureData;
@ -35,6 +39,7 @@ import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
@ -44,6 +49,7 @@ import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
@ -93,6 +99,11 @@ public class ExcelUtil<T>
public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
/**
* 用于dictType属性数据存储避免重复查缓存
*/
public Map<String, String> sysDictMap = new HashMap<String, String>();
/**
* Excel sheet最大行数默认65536
*/
@ -148,6 +159,26 @@ public class ExcelUtil<T>
*/
private short maxHeight;
/**
* 合并后最后行数
*/
private int subMergedLastRowNum = 0;
/**
* 合并后开始行数
*/
private int subMergedFirstRowNum = 1;
/**
* 对象的子列表方法
*/
private Method subMethod;
/**
* 对象的子列表属性
*/
private List<Field> subFields;
/**
* 统计列表
*/
@ -163,11 +194,41 @@ public class ExcelUtil<T>
*/
public Class<T> clazz;
/**
* 需要显示列属性
*/
public String[] includeFields;
/**
* 需要排除列属性
*/
public String[] excludeFields;
public ExcelUtil(Class<T> clazz)
{
this.clazz = clazz;
}
/**
* 仅在Excel中显示列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
*/
public void showColumn(String... fields)
{
this.includeFields = fields;
}
/**
* 隐藏Excel中列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
*/
public void hideColumn(String... fields)
{
this.excludeFields = fields;
}
public void init(List<T> list, String sheetName, String title, Type type)
{
if (list == null)
@ -181,6 +242,7 @@ public class ExcelUtil<T>
createExcelField();
createWorkbook();
createTitle();
createSubHead();
}
/**
@ -190,13 +252,54 @@ public class ExcelUtil<T>
{
if (StringUtils.isNotEmpty(title))
{
int titleLastCol = this.fields.size() - 1;
if (isSubList())
{
titleLastCol = titleLastCol + subFields.size() - 1;
}
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
titleRow.setHeightInPoints(30);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),
this.fields.size() - 1));
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));
}
}
/**
* 创建对象的子列表名称
*/
public void createSubHead()
{
if (isSubList())
{
Row subRow = sheet.createRow(rownum);
int column = 0;
int subFieldSize = subFields != null ? subFields.size() : 0;
for (Object[] objects : fields)
{
Field field = (Field) objects[0];
Excel attr = (Excel) objects[1];
if (Collection.class.isAssignableFrom(field.getType()))
{
Cell cell = subRow.createCell(column);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
if (subFieldSize > 1)
{
CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
sheet.addMergedRegion(cellAddress);
}
column += subFieldSize;
}
else
{
Cell cell = subRow.createCell(column++);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
}
}
rownum++;
}
}
@ -206,7 +309,7 @@ public class ExcelUtil<T>
* @param is 输入流
* @return 转换后集合
*/
public List<T> importExcel(InputStream is) throws Exception
public List<T> importExcel(InputStream is)
{
return importExcel(is, 0);
}
@ -218,9 +321,23 @@ public class ExcelUtil<T>
* @param titleNum 标题占用行数
* @return 转换后集合
*/
public List<T> importExcel(InputStream is, int titleNum) throws Exception
public List<T> importExcel(InputStream is, int titleNum)
{
return importExcel(StringUtils.EMPTY, is, titleNum);
List<T> list = null;
try
{
list = importExcel(StringUtils.EMPTY, is, titleNum);
}
catch (Exception e)
{
log.error("导入Excel异常{}", e.getMessage());
throw new UtilException(e.getMessage());
}
finally
{
IOUtils.closeQuietly(is);
}
return list;
}
/**
@ -254,7 +371,6 @@ public class ExcelUtil<T>
}
// 获取最后一个非空行的行下标比如总行数为n则返回的为n-1
int rows = sheet.getLastRowNum();
if (rows > 0)
{
// 定义一个map用于存放excel列的序号和field.
@ -369,17 +485,22 @@ public class ExcelUtil<T>
{
propertyName = field.getName() + "." + attr.targetAttr();
}
else if (StringUtils.isNotEmpty(attr.readConverterExp()))
if (StringUtils.isNotEmpty(attr.readConverterExp()))
{
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
}
else if (StringUtils.isNotEmpty(attr.dictType()))
{
val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
if (!sysDictMap.containsKey(attr.dictType() + val))
{
String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
sysDictMap.put(attr.dictType() + val, dictValue);
}
val = sysDictMap.get(attr.dictType() + val);
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
val = dataFormatHandlerAdapter(val, attr);
val = dataFormatHandlerAdapter(val, attr, null);
}
else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
{
@ -576,8 +697,20 @@ public class ExcelUtil<T>
// 写入各个字段的列头名称
for (Object[] os : fields)
{
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
this.createCell(excel, row, column++);
if (Collection.class.isAssignableFrom(field.getType()))
{
for (Field subField : subFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
this.createHeadCell(subExcel, row, column++);
}
}
else
{
this.createHeadCell(excel, row, column++);
}
}
if (Type.EXPORT.equals(type))
{
@ -593,23 +726,96 @@ public class ExcelUtil<T>
* @param index 序号
* @param row 单元格行
*/
@SuppressWarnings("unchecked")
public void fillExcelData(int index, Row row)
{
int startNo = index * sheetSize;
int endNo = Math.min(startNo + sheetSize, list.size());
int currentRowNum = rownum + 1; // 从标题行后开始
for (int i = startNo; i < endNo; i++)
{
row = sheet.createRow(i + 1 + rownum - startNo);
// 得到导出对象.
row = sheet.createRow(currentRowNum);
T vo = (T) list.get(i);
int column = 0;
int maxSubListSize = getCurrentMaxSubListSize(vo);
for (Object[] os : fields)
{
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
this.addCell(excel, row, vo, field, column++);
if (Collection.class.isAssignableFrom(field.getType()))
{
try
{
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
if (subList != null && !subList.isEmpty())
{
int subIndex = 0;
for (Object subVo : subList)
{
Row subRow = sheet.getRow(currentRowNum + subIndex);
if (subRow == null)
{
subRow = sheet.createRow(currentRowNum + subIndex);
}
int subColumn = column;
for (Field subField : subFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
}
subIndex++;
}
column += subFields.size();
}
}
catch (Exception e)
{
log.error("填充集合数据失败", e);
}
}
else
{
// 创建单元格并设置值
addCell(excel, row, vo, field, column);
if (maxSubListSize > 1 && excel.needMerge())
{
sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column));
}
column++;
}
}
currentRowNum += maxSubListSize;
}
}
/**
* 获取子列表最大数
*/
private int getCurrentMaxSubListSize(T vo)
{
int maxSubListSize = 1;
for (Object[] os : fields)
{
Field field = (Field) os[0];
if (Collection.class.isAssignableFrom(field.getType()))
{
try
{
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, (Excel) os[1]);
if (subList != null && !subList.isEmpty())
{
maxSubListSize = Math.max(maxSubListSize, subList.size());
}
}
catch (Exception e)
{
log.error("获取集合大小失败", e);
}
}
}
return maxSubListSize;
}
/**
@ -630,6 +836,8 @@ public class ExcelUtil<T>
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBold(true);
style.setFont(titleFont);
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
styles.put("title", style);
style = wb.createCellStyle();
@ -649,20 +857,6 @@ public class ExcelUtil<T>
style.setFont(dataFont);
styles.put("data", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font headerFont = wb.createFont();
headerFont.setFontName("Arial");
headerFont.setFontHeightInPoints((short) 10);
headerFont.setBold(true);
headerFont.setColor(IndexedColors.WHITE.getIndex());
style.setFont(headerFont);
styles.put("header", style);
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
@ -672,35 +866,141 @@ public class ExcelUtil<T>
style.setFont(totalFont);
styles.put("total", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.LEFT);
styles.put("data1", style);
styles.putAll(annotationHeaderStyles(wb, styles));
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.CENTER);
styles.put("data2", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.RIGHT);
styles.put("data3", style);
styles.putAll(annotationDataStyles(wb));
return styles;
}
/**
* 根据Excel注解创建表格头样式
*
* @param wb 工作薄对象
* @return 自定义样式列表
*/
private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles)
{
Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>();
for (Object[] os : fields)
{
Excel excel = (Excel) os[1];
String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor());
if (!headerStyles.containsKey(key))
{
CellStyle style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setFillForegroundColor(excel.headerBackgroundColor().index);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font headerFont = wb.createFont();
headerFont.setFontName("Arial");
headerFont.setFontHeightInPoints((short) 10);
headerFont.setBold(true);
headerFont.setColor(excel.headerColor().index);
style.setFont(headerFont);
// 设置表格头单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
headerStyles.put(key, style);
}
}
return headerStyles;
}
/**
* 根据Excel注解创建表格列样式
*
* @param wb 工作薄对象
* @return 自定义样式列表
*/
private Map<String, CellStyle> annotationDataStyles(Workbook wb)
{
Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
for (Object[] os : fields)
{
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
if (Collection.class.isAssignableFrom(field.getType()))
{
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
for (Field subField : subFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
annotationDataStyles(styles, subField, subExcel);
}
}
else
{
annotationDataStyles(styles, field, excel);
}
}
return styles;
}
/**
* 根据Excel注解创建表格列样式
*
* @param styles 自定义样式列表
* @param field 属性列信息
* @param excel 注解信息
*/
public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel)
{
String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText());
if (!styles.containsKey(key))
{
CellStyle style = wb.createCellStyle();
style.setAlignment(excel.align());
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillForegroundColor(excel.backgroundColor().getIndex());
style.setWrapText(excel.wrapText());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
dataFont.setColor(excel.color().index);
style.setFont(dataFont);
if (ColumnType.TEXT == excel.cellType())
{
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
}
styles.put(key, style);
}
}
/**
* 创建单元格
*/
public Cell createCell(Excel attr, Row row, int column)
public Cell createHeadCell(Excel attr, Row row, int column)
{
// 创建列
Cell cell = row.createCell(column);
// 写入列信息
cell.setCellValue(attr.name());
setDataValidation(attr, row, column);
cell.setCellStyle(styles.get("header"));
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
if (isSubList())
{
// 填充默认样式,防止合并单元格样式失效
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
if (attr.needMerge())
{
sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
}
}
return cell;
}
@ -713,7 +1013,7 @@ public class ExcelUtil<T>
*/
public void setCellVo(Object value, Excel attr, Cell cell)
{
if (ColumnType.STRING == attr.cellType())
if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType())
{
String cellValue = Convert.toStr(value);
// 对于任何以表达式触发字符 =-+@开头的单元格直接使用tab字符作为前缀防止CSV注入。
@ -721,6 +1021,10 @@ public class ExcelUtil<T>
{
cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
}
if (value instanceof Collection && StringUtils.equals("[]", cellValue))
{
cellValue = StringUtils.EMPTY;
}
cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
}
else if (ColumnType.NUMERIC == attr.cellType())
@ -786,17 +1090,29 @@ public class ExcelUtil<T>
// 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
}
// 如果设置了提示信息则鼠标放上去提示.
if (StringUtils.isNotEmpty(attr.prompt()))
if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict())
{
// 这里默认设了2-101列提示.
setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
}
// 如果设置了combo属性则本列只能选择不能输入
if (attr.combo().length > 0)
{
// 这里默认设了2-101列只能选择不能输入.
setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
String[] comboArray = attr.combo();
if (attr.comboReadDict())
{
if (!sysDictMap.containsKey("combo_" + attr.dictType()))
{
String labels = DictUtils.getDictLabels(attr.dictType());
sysDictMap.put("combo_" + attr.dictType(), labels);
}
String val = sysDictMap.get("combo_" + attr.dictType());
comboArray = StringUtils.split(val, DictUtils.SEPARATOR);
}
if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255)
{
// 如果下拉数大于15或字符串长度大于255则使用一个新sheet存储避免生成的模板下拉值获取不到
setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column);
}
else
{
// 提示信息或只能选择不能输入的列内容.
setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column);
}
}
}
@ -815,8 +1131,14 @@ public class ExcelUtil<T>
{
// 创建cell
cell = row.createCell(column);
int align = attr.align().value();
cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
{
if (subMergedLastRowNum >= subMergedFirstRowNum)
{
sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column));
}
}
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
// 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr);
@ -834,15 +1156,20 @@ public class ExcelUtil<T>
}
else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value))
{
cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));
if (!sysDictMap.containsKey(dictType + value))
{
String lable = convertDictByExp(Convert.toStr(value), dictType, separator);
sysDictMap.put(dictType + value, lable);
}
cell.setCellValue(sysDictMap.get(dictType + value));
}
else if (value instanceof BigDecimal && -1 != attr.scale())
{
cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
cell.setCellValue(dataFormatHandlerAdapter(value, attr));
cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));
}
else
{
@ -860,48 +1187,78 @@ public class ExcelUtil<T>
}
/**
* 设置 POI XSSFSheet 单元格提示
* 设置 POI XSSFSheet 单元格提示或选择框
*
* @param sheet 表单
* @param promptTitle 提示标题
* @param textlist 下拉框显示的内容
* @param promptContent 提示内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
*/
public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow,
int firstCol, int endCol)
{
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1");
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
DataValidation dataValidation = helper.createValidation(constraint, regions);
dataValidation.createPromptBox(promptTitle, promptContent);
dataValidation.setShowPromptBox(true);
if (StringUtils.isNotEmpty(promptContent))
{
// 如果设置了提示信息则鼠标放上去提示
dataValidation.createPromptBox("", promptContent);
dataValidation.setShowPromptBox(true);
}
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation)
{
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
}
else
{
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
}
/**
* 设置某些列的值只能输入预制的数据,显示下拉框.
* 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框).
*
* @param sheet 要设置的sheet.
* @param textlist 下拉框显示的内容
* @param promptContent 提示内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
* @return 设置好的sheet.
*/
public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol)
public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol)
{
String hideSheetName = "combo_" + firstCol + "_" + endCol;
Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
for (int i = 0; i < textlist.length; i++)
{
hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
}
// 创建名称,可被其他单元格引用
Name name = wb.createName();
name.setNameName(hideSheetName + "_data");
name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
DataValidationHelper helper = sheet.getDataValidationHelper();
// 加载下拉列表内容
DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
// 数据有效性对象
DataValidation dataValidation = helper.createValidation(constraint, regions);
if (StringUtils.isNotEmpty(promptContent))
{
// 如果设置了提示信息则鼠标放上去提示
dataValidation.createPromptBox("", promptContent);
dataValidation.setShowPromptBox(true);
}
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation)
{
@ -914,6 +1271,8 @@ public class ExcelUtil<T>
}
sheet.addValidationData(dataValidation);
// 设置hiddenSheet隐藏
wb.setSheetHidden(wb.getSheetIndex(hideSheet), true);
}
/**
@ -931,7 +1290,7 @@ public class ExcelUtil<T>
for (String item : convertSource)
{
String[] itemArray = item.split("=");
if (StringUtils.containsAny(separator, propertyValue))
if (StringUtils.containsAny(propertyValue, separator))
{
for (String value : propertyValue.split(separator))
{
@ -968,7 +1327,7 @@ public class ExcelUtil<T>
for (String item : convertSource)
{
String[] itemArray = item.split("=");
if (StringUtils.containsAny(separator, propertyValue))
if (StringUtils.containsAny(propertyValue, separator))
{
for (String value : propertyValue.split(separator))
{
@ -1023,13 +1382,13 @@ public class ExcelUtil<T>
* @param excel 数据注解
* @return
*/
public String dataFormatHandlerAdapter(Object value, Excel excel)
public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell)
{
try
{
Object instance = excel.handler().newInstance();
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class });
value = formatMethod.invoke(instance, value, excel.args());
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
}
catch (Exception e)
{
@ -1089,7 +1448,7 @@ public class ExcelUtil<T>
*/
public String encodingFilename(String filename)
{
filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
filename = UUID.randomUUID() + "_" + filename + ".xlsx";
return filename;
}
@ -1120,6 +1479,7 @@ public class ExcelUtil<T>
*/
private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
{
field.setAccessible(true);
Object o = field.get(vo);
if (StringUtils.isNotEmpty(excel.targetAttr()))
{
@ -1179,35 +1539,83 @@ public class ExcelUtil<T>
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
for (Field field : tempFields)
if (StringUtils.isNotEmpty(includeFields))
{
// 单注解
if (field.isAnnotationPresent(Excel.class))
for (Field field : tempFields)
{
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
addField(fields, field);
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class))
}
else if (StringUtils.isNotEmpty(excludeFields))
{
for (Field field : tempFields)
{
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels)
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
{
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
addField(fields, field);
}
}
}
else
{
for (Field field : tempFields)
{
addField(fields, field);
}
}
return fields;
}
/**
* 添加字段信息
*/
public void addField(List<Object[]> fields, Field field)
{
// 单注解
if (field.isAnnotationPresent(Excel.class))
{
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
fields.add(new Object[] { field, attr });
}
if (Collection.class.isAssignableFrom(field.getType()))
{
subMethod = getSubMethod(field.getName(), clazz);
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class))
{
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels)
{
if (StringUtils.isNotEmpty(includeFields))
{
if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
fields.add(new Object[] { field, attr });
}
}
else
{
if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
}
}
}
return fields;
}
/**
@ -1356,7 +1764,7 @@ public class ExcelUtil<T>
HSSFPicture pic = (HSSFPicture) shape;
int pictureIndex = pic.getPictureIndex() - 1;
HSSFPictureData picData = pictures.get(pictureIndex);
String picIndex = String.valueOf(anchor.getRow1()) + "_" + String.valueOf(anchor.getCol1());
String picIndex = anchor.getRow1() + "_" + anchor.getCol1();
sheetIndexPicMap.put(picIndex, picData);
}
}
@ -1432,4 +1840,61 @@ public class ExcelUtil<T>
}
return str;
}
/**
* 是否有对象的子列表
*/
public boolean isSubList()
{
return StringUtils.isNotNull(subFields) && subFields.size() > 0;
}
/**
* 是否有对象的子列表,集合不为空
*/
public boolean isSubListValue(T vo)
{
return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
}
/**
* 获取集合的值
*/
public Collection<?> getListCellValue(Object obj)
{
Object value;
try
{
value = subMethod.invoke(obj, new Object[] {});
}
catch (Exception e)
{
return new ArrayList<Object>();
}
return (Collection<?>) value;
}
/**
* 获取对象的子列表方法
*
* @param name 名称
* @param pojoClass 类对象
* @return 子列表方法
*/
public Method getSubMethod(String name, Class<?> pojoClass)
{
StringBuffer getMethodName = new StringBuffer("get");
getMethodName.append(name.substring(0, 1).toUpperCase());
getMethodName.append(name.substring(1));
Method method = null;
try
{
method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
}
catch (Exception e)
{
log.error("获取对象异常{}", e.getMessage());
}
return method;
}
}

View File

@ -143,4 +143,16 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
final String[] activeProfiles = getActiveProfiles();
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
/**
* 获取配置文件中的值
*
* @param key 配置文件的key
* @return 当前的配置文件的值
*
*/
public static String getRequiredProperty(String key)
{
return applicationContext.getEnvironment().getRequiredProperty(key);
}
}

View File

@ -13,13 +13,18 @@ public class SqlUtil
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
*/
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 限制orderBy最大长度
*/
private static final int ORDER_BY_MAX_LENGTH = 500;
/**
* 检查字符,防止注入绕过
*/
@ -29,6 +34,10 @@ public class SqlUtil
{
throw new UtilException("参数不符合规范,不能进行查询");
}
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
{
throw new UtilException("参数已超过最大限制,不能进行查询");
}
return value;
}

View File

@ -22,7 +22,7 @@ public class Seq
private static AtomicInteger uploadSeq = new AtomicInteger(1);
// 机器标识
private static String machineCode = "A";
private static final String machineCode = "A";
/**
* 获取通用序列号

View File

@ -66,7 +66,7 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
* 获取类型 4伪随机生成的UUID 的静态工厂。
*
* @return 随机生成的 {@code UUID}
*/

View File

@ -27,8 +27,13 @@ public class XssValidator implements ConstraintValidator<Xss, String>
public static boolean containsHtml(String value)
{
StringBuilder sHtml = new StringBuilder();
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
while (matcher.find())
{
sHtml.append(matcher.group());
}
return pattern.matcher(sHtml).matches();
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.2</version>
<version>3.8.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -37,11 +37,11 @@
<!-- 验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<exclusions>
<exclusion>
<artifactId>javax.servlet-api</artifactId>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>

View File

@ -1,16 +1,21 @@
package com.ruoyi.framework.aspectj;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.context.PermissionContextHolder;
/**
* 数据过滤处理
@ -68,8 +73,8 @@ public class DataScopeAspect
// 如果是超级管理员,则不过滤数据
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias());
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
}
}
}
@ -79,25 +84,50 @@ public class DataScopeAspect
*
* @param joinPoint 切点
* @param user 用户
* @param userAlias 别名
* @param deptAlias 部门别名
* @param userAlias 用户别名
* @param permission 权限字符
*/
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
List<String> scopeCustomIds = new ArrayList<String>();
user.getRoles().forEach(role -> {
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
}
});
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
{
continue;
}
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
conditions.add(dataScope);
break;
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
if (scopeCustomIds.size() > 1)
{
// 多个自定数据权限使用in查询避免多次拼接。
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
}
else
{
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
}
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
@ -105,9 +135,7 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
@ -118,9 +146,16 @@ public class DataScopeAspect
else
{
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(" OR 1=0 ");
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
}
conditions.add(dataScope);
}
// 角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
if (StringUtils.isNotBlank(sqlString.toString()))

View File

@ -4,25 +4,29 @@ import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.filter.PropertyPreExcludeFilter;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.domain.SysOperLog;
@ -38,6 +42,21 @@ public class LogAspect
{
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/**
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
@ -72,12 +91,17 @@ public class LogAspect
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
String ip = IpUtils.getIpAddr();
operLog.setOperIp(ip);
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
SysUser currentUser = loginUser.getUser();
if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept()))
{
operLog.setDeptName(currentUser.getDept().getDeptName());
}
}
if (e != null)
@ -93,16 +117,21 @@ public class LogAspect
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
catch (Exception exp)
{
// 记录本地异常日志
log.error("==前置通知异常==");
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
finally
{
TIME_THREADLOCAL.remove();
}
}
/**
@ -124,7 +153,7 @@ public class LogAspect
if (log.isSaveRequestData())
{
// 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog);
setRequestValue(joinPoint, operLog, log.excludeParamNames());
}
// 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
@ -139,25 +168,25 @@ public class LogAspect
* @param operLog 操作日志
* @throws Exception 异常
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
{
String params = argsArrayToString(joinPoint.getArgs());
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
}
else
{
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray)
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
@ -168,7 +197,7 @@ public class LogAspect
{
try
{
Object jsonObj = JSON.toJSON(o);
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
params += jsonObj.toString() + " ";
}
catch (Exception e)
@ -180,6 +209,14 @@ public class LogAspect
return params.trim();
}
/**
* 忽略敏感属性
*/
public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
{
return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
}
/**
* 判断是否需要过滤的对象。
*

View File

@ -16,7 +16,6 @@ import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
@ -50,7 +49,6 @@ public class RateLimiterAspect
@Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
{
String key = rateLimiter.key();
int time = rateLimiter.time();
int count = rateLimiter.count();
@ -63,7 +61,7 @@ public class RateLimiterAspect
{
throw new ServiceException("访问过于频繁,请稍候再试");
}
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);
}
catch (ServiceException e)
{
@ -80,7 +78,7 @@ public class RateLimiterAspect
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
stringBuffer.append(IpUtils.getIpAddr()).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();

View File

@ -1,15 +1,13 @@
package com.ruoyi.framework.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import com.ruoyi.common.constant.Constants;
/**
* Redis使用FastJson序列化
@ -18,17 +16,11 @@ import java.nio.charset.Charset;
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
@SuppressWarnings("unused")
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);
static
{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz)
{
@ -43,7 +35,7 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
@ -55,17 +47,6 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper)
{
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz)
{
return TypeFactory.defaultInstance().constructType(clazz);
return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER);
}
}

View File

@ -0,0 +1,43 @@
package com.ruoyi.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import com.ruoyi.common.constant.Constants;
/**
* 资源文件配置加载
*
* @author ruoyi
*/
@Configuration
public class I18nConfig implements WebMvcConfigurer
{
@Bean
public LocaleResolver localeResolver()
{
SessionLocaleResolver slr = new SessionLocaleResolver();
// 默认语言
slr.setDefaultLocale(Constants.DEFAULT_LOCALE);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor()
{
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
// 参数名
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(localeChangeInterceptor());
}
}

View File

@ -5,7 +5,7 @@ import com.google.code.kaptcha.text.impl.DefaultTextCreator;
/**
* 验证码文本生成器
*
*
* @author ruoyi
*/
public class KaptchaTextCreator extends DefaultTextCreator
@ -20,7 +20,7 @@ public class KaptchaTextCreator extends DefaultTextCreator
int x = random.nextInt(10);
int y = random.nextInt(10);
StringBuilder suChinese = new StringBuilder();
int randomoperands = (int) Math.round(Math.random() * 2);
int randomoperands = random.nextInt(3);
if (randomoperands == 0)
{
result = x * y;
@ -30,7 +30,7 @@ public class KaptchaTextCreator extends DefaultTextCreator
}
else if (randomoperands == 1)
{
if (!(x == 0) && y % x == 0)
if ((x != 0) && y % x == 0)
{
result = y / x;
suChinese.append(CNUMBERS[y]);
@ -45,7 +45,7 @@ public class KaptchaTextCreator extends DefaultTextCreator
suChinese.append(CNUMBERS[y]);
}
}
else if (randomoperands == 2)
else
{
if (x >= y)
{
@ -62,13 +62,6 @@ public class KaptchaTextCreator extends DefaultTextCreator
suChinese.append(CNUMBERS[x]);
}
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
suChinese.append("=?@" + result);
return suChinese.toString();
}

View File

@ -8,11 +8,6 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
/**
* redis配置
@ -32,11 +27,6 @@ public class RedisConfig extends CachingConfigurerSupport
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);

View File

@ -1,8 +1,10 @@
package com.ruoyi.framework.config;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@ -33,7 +35,8 @@ public class ResourcesConfig implements WebMvcConfigurer
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
/**

View File

@ -2,18 +2,21 @@ package com.ruoyi.framework.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
@ -23,8 +26,9 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
*
* @author ruoyi
*/
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig
{
/**
* 自定义用户认证逻辑
@ -55,18 +59,23 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
*/
@Autowired
private CorsFilter corsFilter;
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
* 允许匿名访问的地址
*/
@Autowired
private PermitAllUrlProperties permitAllUrl;
/**
* 身份验证实现
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
public AuthenticationManager authenticationManager()
{
return super.authenticationManagerBean();
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}
/**
@ -84,44 +93,39 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
@Bean
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
{
httpSecurity
// CSRF禁用因为不使用session
.csrf().disable()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
return httpSecurity
// CSRF禁用因为不使用session
.csrf(csrf -> csrf.disable())
// 禁用HTTP响应标头
.headers((headersCustomizer) -> {
headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
})
// 认证失败处理类
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
// 基于token所以不需要session
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage").anonymous()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/profile/**"
).permitAll()
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
.antMatchers("/druid/**").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
// 添加Logout filter
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
// 添加JWT filter
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
// 添加CORS filter
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
.addFilterBefore(corsFilter, LogoutFilter.class)
.build();
}
/**
@ -132,13 +136,4 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
{
return new BCryptPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}

View File

@ -24,6 +24,12 @@ public class DruidProperties
@Value("${spring.datasource.druid.maxWait}")
private int maxWait;
@Value("${spring.datasource.druid.connectTimeout}")
private int connectTimeout;
@Value("${spring.datasource.druid.socketTimeout}")
private int socketTimeout;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@ -54,6 +60,12 @@ public class DruidProperties
/** 配置获取连接等待超时的时间 */
datasource.setMaxWait(maxWait);
/** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */
datasource.setConnectTimeout(connectTimeout);
/** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */
datasource.setSocketTimeout(socketTimeout);
/** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

View File

@ -0,0 +1,73 @@
package com.ruoyi.framework.config.properties;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import org.apache.commons.lang3.RegExUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.ruoyi.common.annotation.Anonymous;
/**
* 设置Anonymous注解允许匿名访问的url
*
* @author ruoyi
*/
@Configuration
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware
{
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
private ApplicationContext applicationContext;
private List<String> urls = new ArrayList<>();
public String ASTERISK = "*";
@Override
public void afterPropertiesSet()
{
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(info -> {
HandlerMethod handlerMethod = map.get(info);
// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
// 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
});
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException
{
this.applicationContext = context;
}
public List<String> getUrls()
{
return urls;
}
public void setUrls(List<String> urls)
{
this.urls = urls;
}
}

View File

@ -14,7 +14,7 @@ public class DynamicDataSourceContextHolder
/**
* 使用ThreadLocal维护变量ThreadLocal为每个使用该变量的线程提供独立的变量副本
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

View File

@ -6,7 +6,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ServletUtils;
@ -32,7 +32,7 @@ public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
if (this.isRepeatSubmit(request, annotation))
{
AjaxResult ajaxResult = AjaxResult.error(annotation.message());
ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
return false;
}
}
@ -47,8 +47,9 @@ public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @param request 请求信息
* @param annotation 防重复注解参数
* @return 结果
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);

View File

@ -7,9 +7,9 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
import com.ruoyi.common.utils.StringUtils;
@ -50,7 +50,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
// body参数为空获取Parameter的数据
if (StringUtils.isEmpty(nowParams))
{
nowParams = JSONObject.toJSONString(request.getParameterMap());
nowParams = JSON.toJSONString(request.getParameterMap());
}
Map<String, Object> nowDataMap = new HashMap<String, Object>();
nowDataMap.put(REPEAT_PARAMS, nowParams);
@ -63,7 +63,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
String submitKey = StringUtils.trimToEmpty(request.getHeader(header));
// 唯一标识指定key + url + 消息头)
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + url + submitKey;
String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;
Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
if (sessionObj != null)

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