155 Commits

Author SHA1 Message Date
4a0faad4bc 添加菜单路由地址和名称的校验规则 2026-01-09 11:14:38 +08:00
eafe812ae7 优化防重提交间隔时间可自定义 2026-01-08 13:39:32 +08:00
70d125a6f4 修复Excel自定义格式样式污染问题 2026-01-06 13:53:23 +08:00
493dd513e5 copyright 2026 2026-01-04 16:08:32 +08:00
8df58e7512 将isAdmin方法统一到SecurityUtils 2026-01-04 16:07:06 +08:00
6a35245aaf 修正UserAgent解析逻辑,正确设置浏览器和操作系统字段 2025-12-31 09:29:10 +08:00
b2cd0b51c2 修正UserAgent解析逻辑,正确设置浏览器和操作系统字段 2025-12-21 10:51:25 +08:00
a0be25948d 优化topbar顶部菜单样式 2025-12-18 14:00:02 +08:00
ab1bbf21ed 若依 3.9.1 2025-12-18 09:06:24 +08:00
2c82a847eb 默认固定头部 2025-12-18 09:05:51 +08:00
2b3b29a4a3 优化字典组件值宽松匹配 2025-12-16 16:41:56 +08:00
f57dea5332 菜单导航设置支持纯顶部 2025-12-16 13:02:05 +08:00
fab921a1d2 升级quartz到最新版本2.5.2 2025-12-12 11:04:33 +08:00
336a47660f 升级spring-boot到最新版本3.5.8 2025-12-11 14:40:16 +08:00
575b1b727f 升级spring-boot-mybatis到最新版3.0.5 2025-12-11 14:37:58 +08:00
542979e703 升级springdoc到最新版本2.8.14 2025-12-11 14:37:19 +08:00
c2202fee27 升级druid到最新版本1.2.27 2025-12-11 14:34:31 +08:00
17a726942f 升级oshi到最新版本6.9.1 2025-12-11 14:34:14 +08:00
362db4a071 升级commons.io到最新版本2.21.0 2025-12-11 14:33:38 +08:00
771b9af7c0 升级fastjson到最新版2.0.60 2025-12-11 14:33:14 +08:00
7a787a919e update sqlkeyword 2025-12-11 14:33:05 +08:00
8fdbd05ce7 使用yauaa代替bitwalker 2025-12-09 14:29:23 +08:00
970275755d 优化用户密码字段序列化配置 2025-12-05 14:59:52 +08:00
516116f125 优化数据权限控制逻辑,放开permission限制 2025-12-04 17:33:15 +08:00
d3493e94d6 支持Excel导出对象的多个子列表 2025-12-04 16:32:48 +08:00
3beb3ae3f9 优化表单构建关闭页签销毁复制插件 2025-12-04 13:53:57 +08:00
f77c0ec97d 修复优雅停机出现的冲突问题 2025-12-04 13:51:36 +08:00
55854ec195 忽略用户密码字段的JSON序列化 2025-12-03 14:38:31 +08:00
088e9afbc9 优化代码 2025-12-03 11:48:40 +08:00
456e1534ee 优化生成代码下载的zip文件名 2025-12-03 10:27:04 +08:00
352ed6eee7 网页标题设置新增SET_TITLE方法 2025-12-02 19:30:01 +08:00
0cb12f5440 支持Excel导出对象的多个子列表 2025-12-02 19:13:23 +08:00
169ca65ed3 登录/注册页面底部版权信息修改为读取配置 2025-12-02 15:29:35 +08:00
cd6c2fc5f1 修复v3时间控件between选择后清空报错问题 2025-12-02 14:57:45 +08:00
e24918b054 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-02 13:08:27 +08:00
2d36603c46 修复comboReadDict属性下多个sheet出现的报错(ICWQ8E) 2025-11-13 11:34:45 +08:00
964569f715 update ruoyi-admin pom.xml 2025-10-14 12:14:12 +08:00
e37b8ff9b8 添加新群号:174569686 2025-10-05 20:11:22 +08:00
5896895bab 升级fastjson到最新版2.0.58 2025-09-05 09:17:39 +08:00
0c8e9ad74e 修复固定头部时出现的导航栏偏移问题(ICV9OH) 2025-09-04 19:58:58 +08:00
f0346b3def 支持防盗链功能 2025-09-02 11:31:18 +08:00
d1cdbbd22c 优化代码 2025-09-02 08:53:31 +08:00
e2badb7c68 升级oshi到最新版本6.8.3 2025-08-28 13:38:19 +08:00
40f54f9700 优化代码 2025-08-28 13:37:57 +08:00
534bd7b8cd 优化代码 2025-08-27 15:34:47 +08:00
6c811c88c2 用户导入添加验证提示 2025-08-23 11:14:45 +08:00
eed3ae8f80 优化布局设置显示 2025-08-23 11:14:36 +08:00
5aa5f42042 修复用户归属部门无法修改为空问题 2025-08-21 14:48:00 +08:00
29e4eae4e2 columns default value 2025-08-09 16:12:51 +08:00
84e1f8a210 显示列信息支持对象格式 2025-08-09 13:23:50 +08:00
2eceac52cf 自动识别json对象白名单配置范围缩小 2025-08-09 10:57:40 +08:00
624c52b568 升级spring-boot到最新版本3.5.4 2025-08-07 15:12:17 +08:00
0e698bcd5b 升级pagehelper到最新版2.1.1 2025-08-07 15:11:35 +08:00
c044ec0bb8 升级oshi到最新版本6.8.2 2025-08-07 15:10:41 +08:00
c6d9a03f9f 添加新群号:191164766 2025-06-20 11:39:46 +08:00
3030c3fa11 优化定时任务包名白名单匹配方式 2025-06-20 11:34:35 +08:00
9dfea3c77f 优化Excel统计行数值的单元格样式显示 2025-06-19 14:48:04 +08:00
ada072c767 用户头像更换后移除旧头像文件 2025-06-06 14:58:29 +08:00
c8a47f8b39 若依 3.9.0 2025-05-28 09:04:44 +08:00
92f8a44f67 注册账号设置默认密码最后更新时间 2025-05-26 10:58:19 +08:00
e0bfca5bdb 升级fastjson到最新版2.0.57 2025-05-26 09:00:13 +08:00
3f753cd801 添加底部版权信息及开关 2025-05-24 14:24:46 +08:00
f3bdf8c7e8 添加页签图标显示开关功能 2025-05-23 14:57:21 +08:00
9a37444e68 update pwdUpdateDate 2025-05-23 10:45:05 +08:00
e43efd179c 账号密码支持自定义更新周期 2025-05-23 09:06:07 +08:00
99e9f21253 初始密码支持自定义修改策略 2025-05-22 23:07:18 +08:00
6ee698b1ab 升级oshi到最新版本6.8.1 2025-05-15 09:14:42 +08:00
992537f343 升级commons.io到最新版本2.19.0 2025-05-15 09:14:23 +08:00
fbd5052f99 delete eslint&vue-meta 2025-05-15 09:14:02 +08:00
f8a918c62c 优化导航栏显示昵称&设置 2025-05-09 13:53:05 +08:00
df0954d5df 菜单搜索支持键盘选择&悬浮主题背景 2025-05-07 13:23:35 +08:00
8af2aa929a 图片上传组件新增disabled属性 2025-05-06 19:13:58 +08:00
fb7743de1c add columnName Drag 2025-05-06 14:53:17 +08:00
eefe15e36b 修复上传组件被多次引用拖动仅对第一个有效的问题 2025-05-06 13:10:37 +08:00
b6874d4320 update icon 2025-05-06 11:08:15 +08:00
6d00fc8603 上传组件新增拖动排序属性 2025-04-30 10:28:30 +08:00
00b2eeb4c3 优化Excel匹配数值型.0结尾 2025-04-28 11:24:35 +08:00
838fc115ca update editor index 2025-04-27 14:03:01 +08:00
62630a5a95 remove all semicolons 2025-04-27 10:05:55 +08:00
f30e737be2 Excel导入导出支持多图片 2025-04-25 10:09:02 +08:00
5069654cb7 富文本复制粘贴图片上传至url 2025-04-24 14:24:20 +08:00
fc6b8eefae update package.json 2025-04-24 11:08:59 +08:00
d6c22b8a42 优化低版本node无法启动的问题 2025-04-22 12:06:28 +08:00
33303f13d9 优化代码 2025-04-22 12:06:12 +08:00
0154365cc0 显隐列组件支持全选/全不选 2025-04-21 15:22:19 +08:00
2cbae4126d 优化菜单搜索查询页 2025-04-21 13:22:49 +08:00
a8ba5b1b0e 支持文件&图片组件自定义地址&参数 2025-04-18 12:57:34 +08:00
cf59bb007d 添加新群号:287842588 2025-04-18 12:57:09 +08:00
fd2568b0e7 springdoc proxy 2025-04-17 15:26:23 +08:00
7ab047745f 优化角色禁用不允许分配 2025-04-17 15:24:35 +08:00
3d09ea31fc update status name 2025-04-17 15:24:08 +08:00
0188c81853 添加新群号:287842588 2025-04-01 19:14:31 +08:00
e39629472c remove dev runjs 2025-03-18 16:06:40 +08:00
df911695f0 登录页和注册页表头使用VUE_APP_TITLE配置值 2025-03-18 16:05:14 +08:00
13b2f5149b 优化代码 2025-03-14 16:11:22 +08:00
495e640888 菜单管理新增路由名称 2025-03-06 11:03:43 +08:00
3a224461d8 优化代码 2025-03-04 20:16:53 +08:00
5fdd49582b 优化顶部菜单搜索栏为多层级显示(IBESXH) 2025-03-03 12:08:36 +08:00
e763e67c61 优化导出Excel日期格式双击离开后与设定的格式不一致问题 2025-03-01 15:33:49 +08:00
9962534193 优化代码 2025-03-01 15:33:07 +08:00
dd1f6a7b9e pagination更换成flex布局 2025-03-01 15:29:58 +08:00
7eb86458cc 优化前端处理路由函数代码 2025-03-01 15:29:42 +08:00
d3de651360 优化前端树结构性能问题 2025-03-01 15:29:13 +08:00
637a09cfa9 修复代码生成主子表校验必填失效问题 2025-02-28 21:53:15 +08:00
499648c90e 代码生成列表支持按时间排序 2025-02-28 19:44:41 +08:00
896c0a23de 文件上传组件新增类型 2025-02-28 19:44:23 +08:00
50e1a727d4 优化空指针异常时无法获取错误信息问题 2025-02-28 19:44:04 +08:00
af80841751 文件上传组件新增disabled属性 2025-02-28 13:03:50 +08:00
4bbe3f3b56 优化代码 2025-02-28 13:03:33 +08:00
57293a2ecf copyright 2025 2025-01-07 10:53:33 +08:00
98cdcae01f 若依 3.8.9 2024-12-30 08:54:15 +08:00
927ad037a2 代码生成新增配置是否允许文件覆盖到本地 2024-12-25 16:00:25 +08:00
8a672152f8 优化导入带标题文件关闭清理 2024-12-25 16:00:06 +08:00
1883a40dcf update sqlkeyword 2024-12-25 00:07:36 +08:00
8b63f468b4 优化特殊字符密码修改失败问题 2024-12-17 14:35:30 +08:00
bfc6fe5ae1 优化TopNav内链菜单点击没有高亮(IB8WHJ) 2024-12-17 14:35:13 +08:00
45e8c51751 优化菜单管理切换Mini布局错乱问题 2024-12-17 14:35:02 +08:00
353476b88c update README 2024-12-17 14:34:48 +08:00
db2f438279 用户管理过滤掉已禁用部门(IB5H7F) 2024-12-11 11:48:44 +08:00
739a1c262d 修改主题样式本地读取 2024-12-10 16:41:20 +08:00
88f620c44d 白名单支持对通配符路径匹配 2024-12-07 14:41:58 +08:00
aec1a93e16 Excel注解支持wrapText是否允许内容换行 2024-12-07 14:41:35 +08:00
ed96bf80e7 修复导出子列表对象只能在最后的问题 2024-12-07 14:41:17 +08:00
4a9011a727 修复默认关闭Tags-Views时,内链页面打不开 2024-11-27 19:59:11 +08:00
8e99b826fa 修复TopNav无法正确获取active的问题 2024-11-27 19:58:58 +08:00
c5615001c8 菜单面包屑导航支持多层级显示 2024-11-25 22:35:42 +08:00
b376ab792a 优化代码 2024-11-25 22:35:19 +08:00
0ca6619a80 分栏参数微调 2024-11-22 14:51:08 +08:00
a691f28f30 用户管理支持分栏拖动 2024-11-22 14:10:36 +08:00
32f0636d46 用户头像http(s)链接支持 2024-11-22 14:10:15 +08:00
4c013a4f73 update .env.staging 2024-11-22 14:09:34 +08:00
a69a436159 支持自定义显示Excel属性列 2024-11-07 22:48:40 +08:00
8ba8524250 升级spring-boot到最新版本3.3.5 2024-11-06 21:40:02 +08:00
bb74a620ba 升级springdoc到最新版本2.6.0 2024-11-06 21:39:47 +08:00
ff8ded2704 升级oshi到最新版本6.6.5 2024-11-05 16:40:36 +08:00
fa8b909408 优化无用户编号不校验数据权限 2024-11-05 16:39:55 +08:00
433f588747 校检文件名是否包含特殊字符 2024-11-05 16:39:14 +08:00
35c1f02392 优化身份证脱敏正则 2024-10-21 16:47:53 +08:00
1a7bcc10bd 优化权限更新后同步缓存 2024-10-21 16:47:44 +08:00
4008d1f30d 优化上传图片带域名不增加前缀 2024-10-21 16:44:57 +08:00
27e07c6a80 升级quill到最新版本2.0.2 2024-10-21 16:44:20 +08:00
587858abe3 升级axios到最新版本0.28.1 2024-10-21 16:43:52 +08:00
0adc70d510 操作日志记录DELETE请求参数(IAMV6F) 2024-10-17 13:09:23 +08:00
4e1ec5e98e 升级fastjson到最新版2.0.53 2024-10-17 13:09:09 +08:00
7d0724b9bf 修复码生成上级菜单显示问题(I9CTIJ) 2024-10-17 13:08:04 +08:00
0bbb8b7c06 修复角色禁用权限不失效问题(IAA8ZX) 2024-09-21 11:30:57 +08:00
19d7e347e4 优化代码 2024-09-08 10:50:55 +08:00
9369590f57 升级oshi到最新版本6.6.3 2024-08-30 21:49:32 +08:00
16af81344c update sqlkeyword 2024-08-30 21:49:23 +08:00
6fc05bc616 修改时间范围日期格式 2024-07-08 16:55:23 +08:00
b84d5554c2 remove sub resultType 2024-07-08 16:55:06 +08:00
583f057de6 若依 3.8.8 2024-06-30 08:11:13 +08:00
232d228e52 菜单管理新增路由名称 2024-06-29 19:01:48 +08:00
92ea278242 删除多余的依赖 2024-06-27 17:34:19 +08:00
534f7d13d3 update springboot3 2024-06-27 16:03:30 +08:00
126 changed files with 2222 additions and 1376 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.9.0</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</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.9.0-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.1-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>
@ -92,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群](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) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) 点击按钮入群。
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) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/已满-228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [![加入QQ群](https://img.shields.io/badge/已满-191164766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [![加入QQ群](https://img.shields.io/badge/174569686-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) 点击按钮入群。

143
pom.xml
View File

@ -6,112 +6,62 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>3.9.0</version>
<version>3.9.1</version>
<name>ruoyi</name>
<url>http://www.ruoyi.vip</url>
<description>若依管理系统</description>
<properties>
<ruoyi.version>3.9.0</ruoyi.version>
<ruoyi.version>3.9.1</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>17</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<spring-boot.version>2.5.15</spring-boot.version>
<druid.version>1.2.23</druid.version>
<bitwalker.version>1.21</bitwalker.version>
<mybatis-spring-boot.version>3.0.5</mybatis-spring-boot.version>
<druid.version>1.2.27</druid.version>
<yauaa.version>7.32.0</yauaa.version>
<swagger.version>3.0.0</swagger.version>
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
<fastjson.version>2.0.57</fastjson.version>
<oshi.version>6.8.1</oshi.version>
<commons.io.version>2.19.0</commons.io.version>
<pagehelper.boot.version>2.1.1</pagehelper.boot.version>
<fastjson.version>2.0.60</fastjson.version>
<oshi.version>6.9.1</oshi.version>
<commons.io.version>2.21.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.105</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>
<quartz.version>2.5.2</quartz.version>
<mysql.version>8.2.0</mysql.version>
<jaxb-api.version>2.3.1</jaxb-api.version>
<jakarta.version>6.0.0</jakarta.version>
<springdoc.version>2.8.14</springdoc.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>${spring-boot.version}</version>
<version>3.5.8</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>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
<groupId>nl.basjes.parse.useragent</groupId>
<artifactId>yauaa</artifactId>
<version>${yauaa.version}</version>
</dependency>
<!-- pagehelper 分页插件 -->
@ -121,6 +71,30 @@
<version>${pagehelper.boot.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
@ -128,17 +102,11 @@
<version>${oshi.version}</version>
</dependency>
<!-- Swagger3依赖 -->
<!-- spring-doc -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- io常用工具类 -->
@ -162,6 +130,13 @@
<version>${velocity.version}</version>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
@ -236,13 +211,19 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.13.0</version>
<configuration>
<parameters>true</parameters>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
@ -24,23 +24,16 @@
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- swagger3-->
<!-- spring-doc -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 核心模块-->
@ -68,9 +61,9 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version>
<version>3.5.4</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
<addResources>true</addResources>
</configuration>
<executions>
<execution>

View File

@ -3,9 +3,9 @@ package com.ruoyi.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;

View File

@ -2,8 +2,8 @@ package com.ruoyi.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
@ -35,7 +34,7 @@ public class CommonController
@Autowired
private ServerConfig serverConfig;
private static final String FILE_DELIMETER = ",";
private static final String FILE_DELIMITER = ",";
/**
* 通用下载请求
@ -120,10 +119,10 @@ public class CommonController
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
ajax.put("urls", StringUtils.join(urls, FILE_DELIMITER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMITER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMITER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMITER));
return ajax;
}
catch (Exception e)
@ -148,7 +147,7 @@ public class CommonController
// 本地资源路径
String localPath = RuoYiConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
String downloadPath = localPath + FileUtils.stripPrefix(resource);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);

View File

@ -45,6 +45,7 @@ public class CacheController
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
}
@SuppressWarnings("deprecation")
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;

View File

@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;

View File

@ -93,6 +93,10 @@ public class SysMenuController extends BaseController
{
return error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
else if (!menuService.checkRouteConfigUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
menu.setCreateBy(getUsername());
return toAjax(menuService.insertMenu(menu));
}
@ -117,6 +121,10 @@ public class SysMenuController extends BaseController
{
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
else if (!menuService.checkRouteConfigUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
menu.setUpdateBy(getUsername());
return toAjax(menuService.updateMenu(menu));
}

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;

View File

@ -21,6 +21,7 @@ import com.ruoyi.common.utils.DateUtils;
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.FileUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
@ -94,8 +95,9 @@ public class SysProfileController extends BaseController
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
Long userId = loginUser.getUserId();
SysUser user = userService.selectUserById(userId);
String password = user.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
return error("修改密码失败,旧密码错误");
@ -105,7 +107,7 @@ public class SysProfileController extends BaseController
return error("新密码不能与旧密码相同");
}
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0)
if (userService.resetUserPwd(userId, newPassword) > 0)
{
// 更新缓存用户密码&密码最后更新时间
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
@ -126,9 +128,14 @@ public class SysProfileController extends BaseController
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
{
String oldAvatar = loginUser.getUser().getAvatar();
if (StringUtils.isNotEmpty(oldAvatar))
{
FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
}
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);
// 更新缓存用户头像

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;

View File

@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@ -111,7 +111,7 @@ public class SysUserController extends BaseController
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("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
@ -226,7 +226,7 @@ public class SysUserController extends BaseController
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
ajax.put("user", user);
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
return ajax;
}

View File

@ -15,19 +15,16 @@ import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* swagger 用户测试方法
*
*
* @author ruoyi
*/
@Api("用户信息管理")
@Tag(name = "用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
@ -37,19 +34,19 @@ public class TestController extends BaseController
users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
}
@ApiOperation("获取用户列表")
@Operation(summary = "获取用户列表")
@GetMapping("/list")
public R<List<UserEntity>> userList()
{
List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
return R.ok(userList);
}
@ApiOperation("获取用户详细")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@Operation(summary = "获取用户详细")
@GetMapping("/{userId}")
public R<UserEntity> getUser(@PathVariable Integer userId)
public R<UserEntity> getUser(@PathVariable(name = "userId")
Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
@ -60,14 +57,8 @@ public class TestController extends BaseController
return R.fail("用户不存在");
}
}
@ApiOperation("新增用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
})
@Operation(summary = "新增用户")
@PostMapping("/save")
public R<String> save(UserEntity user)
{
@ -78,10 +69,11 @@ public class TestController extends BaseController
users.put(user.getUserId(), user);
return R.ok();
}
@ApiOperation("更新用户")
@Operation(summary = "更新用户")
@PutMapping("/update")
public R<String> update(@RequestBody UserEntity user)
public R<String> update(@RequestBody
UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
@ -95,11 +87,11 @@ public class TestController extends BaseController
users.put(user.getUserId(), user);
return R.ok();
}
@ApiOperation("删除用户信息")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@Operation(summary = "删除用户信息")
@DeleteMapping("/{userId}")
public R<String> delete(@PathVariable Integer userId)
public R<String> delete(@PathVariable(name = "userId")
Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
@ -113,26 +105,26 @@ public class TestController extends BaseController
}
}
@ApiModel(value = "UserEntity", description = "用户实体")
@Schema(description = "用户实体")
class UserEntity
{
@ApiModelProperty("用户ID")
@Schema(title = "用户ID")
private Integer userId;
@ApiModelProperty("用户名称")
@Schema(title = "用户名称")
private String username;
@ApiModelProperty("用户密码")
@Schema(title = "用户密码")
private String password;
@ApiModelProperty("用户手机")
@Schema(title = "用户手机")
private String mobile;
public UserEntity()
{
}
public UserEntity(Integer userId, String username, String password, String mobile)
{
this.userId = userId;
@ -140,42 +132,42 @@ class UserEntity
this.password = password;
this.mobile = mobile;
}
public Integer getUserId()
{
return userId;
}
public void setUserId(Integer userId)
{
this.userId = userId;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getMobile()
{
return mobile;
}
public void setMobile(String mobile)
{
this.mobile = mobile;

View File

@ -1,26 +1,15 @@
package com.ruoyi.web.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
/**
* Swagger2的接口配置
@ -33,93 +22,43 @@ public class SwaggerConfig
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/**
* 创建API
* 自定义的 OpenAPI 对象
*/
@Bean
public Docket createRestApi()
public OpenAPI customOpenApi()
{
return new Docket(DocumentationType.OAS_30)
// 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
/* 设置安全模式swagger可以设置访问token */
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
return new OpenAPI().components(new Components()
// 设置认证的请求头
.addSecuritySchemes("apikey", securityScheme()))
.addSecurityItem(new SecurityRequirement().addList("apikey"))
.info(getApiInfo());
}
/**
* 安全模式这里指定token通过Authorization头请求头传递
*/
private List<SecurityScheme> securitySchemes()
@Bean
public SecurityScheme securityScheme()
{
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
return new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.name("Authorization")
.in(SecurityScheme.In.HEADER)
.scheme("Bearer");
}
/**
* 安全上下文
*/
private List<SecurityContext> securityContexts()
{
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
* 默认的安全上引用
*/
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
public Info getApiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题若依管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
return new Info()
// 设置标题
.title("标题若依管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact().name(ruoyiConfig.getName()))
// 版本
.version("版本号:" + ruoyiConfig.getVersion());
}
}

View File

@ -3,9 +3,9 @@ ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.9.0
version: 3.9.1
# 版权年份
copyrightYear: 2025
copyrightYear: 2026
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: D:/ruoyi/uploadPath
# 获取ip地址开关
@ -65,28 +65,29 @@ spring:
restart:
# 热部署开关
enabled: true
# redis 配置
redis:
# 地址
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
data:
# redis 配置
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# token配置
token:
@ -112,12 +113,26 @@ pagehelper:
supportMethodsArguments: true
params: count=countSql
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# Springdoc配置
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
tags-sorter: alpha
group-configs:
- group: 'default'
display-name: '测试模块'
paths-to-match: '/**'
packages-to-scan: com.ruoyi.web.controller.tool
# 防盗链配置
referer:
# 防盗链开关
enabled: false
# 允许的域名列表
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
# 防止XSS攻击
xss:

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -77,12 +77,6 @@
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
@ -109,14 +103,14 @@
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<groupId>nl.basjes.parse.useragent</groupId>
<artifactId>yauaa</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
</dependencies>

View File

@ -56,6 +56,7 @@ public @interface Excel
/**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
@SuppressWarnings("deprecation")
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**

View File

@ -83,12 +83,12 @@ public class Constants
/**
* 角色权限分隔符
*/
public static final String ROLE_DELIMETER = ",";
public static final String ROLE_DELIMITER = ",";
/**
* 权限标识分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
public static final String PERMISSION_DELIMITER = ",";
/**
* 验证码有效期(分钟)
@ -158,7 +158,7 @@ public class Constants
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
public static final String[] JSON_WHITELIST_STR = { "com.ruoyi" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)

View File

@ -2,10 +2,10 @@ package com.ruoyi.common.core.domain.entity;
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 jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;

View File

@ -1,8 +1,8 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;

View File

@ -2,9 +2,9 @@ package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,9 +1,9 @@
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 jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;

View File

@ -2,14 +2,16 @@ package com.ruoyi.common.core.domain.entity;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.*;
import jakarta.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
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;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.xss.Xss;
/**
@ -114,12 +116,7 @@ public class SysUser extends BaseEntity
public boolean isAdmin()
{
return isAdmin(this.userId);
}
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
return SecurityUtils.isAdmin(this.userId);
}
public Long getDeptId()
@ -200,6 +197,7 @@ public class SysUser extends BaseEntity
this.avatar = avatar;
}
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String getPassword()
{
return password;

View File

@ -3,7 +3,7 @@ package com.ruoyi.common.exception.file;
import java.util.Arrays;
/**
* 文件上传异常类
* 文件上传无效扩展名异常类
*
* @author ruoyi
*/

View File

@ -0,0 +1,77 @@
package com.ruoyi.common.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 防盗链过滤器
*
* @author ruoyi
*/
public class RefererFilter implements Filter
{
/**
* 允许的域名列表
*/
public List<String> allowedDomains;
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
String domains = filterConfig.getInitParameter("allowedDomains");
this.allowedDomains = Arrays.asList(domains.split(","));
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String referer = req.getHeader("Referer");
// 如果Referer为空拒绝访问
if (referer == null || referer.isEmpty())
{
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied: Referer header is required");
return;
}
// 检查Referer是否在允许的域名列表中
boolean allowed = false;
for (String domain : allowedDomains)
{
if (referer.contains(domain))
{
allowed = true;
break;
}
}
// 根据检查结果决定是否放行
if (allowed)
{
chain.doFilter(request, response);
}
else
{
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied: Referer '" + referer + "' is not allowed");
}
}
@Override
public void destroy()
{
}
}

View File

@ -1,13 +1,13 @@
package com.ruoyi.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import com.ruoyi.common.utils.StringUtils;

View File

@ -4,11 +4,11 @@ import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.constant.Constants;

View File

@ -3,14 +3,14 @@ package com.ruoyi.common.filter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.enums.HttpMethod;

View File

@ -2,10 +2,10 @@ package com.ruoyi.common.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

View File

@ -16,6 +16,7 @@ import org.apache.commons.lang3.time.DateFormatUtils;
*
* @author ruoyi
*/
@SuppressWarnings("deprecation")
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{
public static String YYYY = "yyyy";

View File

@ -1,7 +1,9 @@
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
@ -89,37 +91,25 @@ public class DictUtils
*/
public static String getDictLabel(String dictType, String dictValue, String separator)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictValue))
Map<String, String> dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictValue(), dict.getDictLabel()), Map::putAll);
if (!StringUtils.contains(dictValue, separator))
{
for (SysDictData dict : datas)
return dictMap.getOrDefault(dictValue, StringUtils.EMPTY);
}
StringBuilder labelBuilder = new StringBuilder();
for (String seperatedValue : dictValue.split(separator))
{
if (dictMap.containsKey(seperatedValue))
{
for (String value : dictValue.split(separator))
{
if (value.equals(dict.getDictValue()))
{
propertyString.append(dict.getDictLabel()).append(separator);
break;
}
}
labelBuilder.append(dictMap.get(seperatedValue)).append(separator);
}
}
else
{
for (SysDictData dict : datas)
{
if (dictValue.equals(dict.getDictValue()))
{
return dict.getDictLabel();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
return StringUtils.removeEnd(labelBuilder.toString(), separator);
}
/**
@ -132,37 +122,25 @@ public class DictUtils
*/
public static String getDictValue(String dictType, String dictLabel, String separator)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictLabel))
Map<String, String> dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictLabel(), dict.getDictValue()), Map::putAll);
if (!StringUtils.contains(dictLabel, separator))
{
for (SysDictData dict : datas)
return dictMap.getOrDefault(dictLabel, StringUtils.EMPTY);
}
StringBuilder valueBuilder = new StringBuilder();
for (String seperatedValue : dictLabel.split(separator))
{
if (dictMap.containsKey(seperatedValue))
{
for (String label : dictLabel.split(separator))
{
if (label.equals(dict.getDictLabel()))
{
propertyString.append(dict.getDictValue()).append(separator);
break;
}
}
valueBuilder.append(dictMap.get(seperatedValue)).append(separator);
}
}
else
{
for (SysDictData dict : datas)
{
if (dictLabel.equals(dict.getDictLabel()))
{
return dict.getDictValue();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
return StringUtils.removeEnd(valueBuilder.toString(), separator);
}
/**

View File

@ -114,6 +114,16 @@ public class SecurityUtils
return passwordEncoder.matches(rawPassword, encodedPassword);
}
/**
* 是否为管理员
*
* @return 结果
*/
public static boolean isAdmin()
{
return isAdmin(getUserId());
}
/**
* 是否为管理员
*

View File

@ -7,10 +7,10 @@ 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 jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

View File

@ -15,6 +15,7 @@ import com.ruoyi.common.core.text.StrFormatter;
*
* @author ruoyi
*/
@SuppressWarnings("deprecation")
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
/** 空字符串 */

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.utils.bean;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;
/**
* bean对象属性验证

View File

@ -13,11 +13,12 @@ import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.Seq;
/**
* 文件上传工具类
*
*
* @author ruoyi
*/
public class FileUploadUtils
@ -102,15 +103,35 @@ public class FileUploadUtils
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException
{
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
return upload(baseDir, file, allowedExtension, false);
}
/**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param useCustomNaming 系统自定义文件名
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean useCustomNaming)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException
{
int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
String fileName = useCustomNaming ? uuidFilename(file) : extractFilename(file);
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
@ -118,12 +139,19 @@ public class FileUploadUtils
}
/**
* 编码文件名
* 编码文件名(日期格式目录 + 原文件名 + 序列值 + 后缀)
*/
public static final String extractFilename(MultipartFile file)
{
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
}
/**
* 编编码文件名(日期格式目录 + UUID + 后缀)
*/
public static final String uuidFilename(MultipartFile file)
{
return StringUtils.format("{}/{}.{}", DateUtils.datePath(), IdUtils.fastSimpleUUID(), getExtension(file));
}
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
@ -216,7 +244,7 @@ public class FileUploadUtils
/**
* 获取文件名的后缀
*
*
* @param file 表单文件
* @return 后缀名
*/

View File

@ -9,15 +9,16 @@ import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
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;
/**
* 文件处理工具类
@ -103,6 +104,17 @@ public class FileUtils
return FileUploadUtils.getPathFileName(uploadDir, pathName);
}
/**
* 移除路径中的请求前缀片段
*
* @param filePath 文件路径
* @return 移除后的文件路径
*/
public static String stripPrefix(String filePath)
{
return StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
}
/**
* 删除文件
*

View File

@ -5,7 +5,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletRequest;
import jakarta.servlet.ServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,254 @@
package com.ruoyi.common.utils.http;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ruoyi.common.utils.StringUtils;
import nl.basjes.parse.useragent.UserAgent;
import nl.basjes.parse.useragent.UserAgentAnalyzer;
/**
* UserAgent解析工具类
*
* @author ruoyi
*/
public class UserAgentUtils
{
public static final String UNKNOWN = "";
// 浏览器正则表达式模式
private static final Pattern CHROME_PATTERN = Pattern.compile("Chrome/(\\d+)(?:\\.\\d+)*");
private static final Pattern FIREFOX_PATTERN = Pattern.compile("Firefox/(\\d+)(?:\\.\\d+)*");
private static final Pattern EDGE_PATTERN = Pattern.compile("Edg(?:e)?/(\\d+)(?:\\.\\d+)*");
private static final Pattern SAFARI_PATTERN = Pattern.compile("Version/(\\d+)(?:\\.\\d+)*");
private static final Pattern OPERA_PATTERN = Pattern.compile("Opera/(\\d+)(?:\\.\\d+)*");
private static final Pattern IE_PATTERN = Pattern.compile("(?:MSIE |Trident/.*rv:)(\\d+)(?:\\.\\d+)*");
private static final Pattern SAMSUNG_PATTERN = Pattern.compile("SamsungBrowser/(\\d+)(?:\\.\\d+)*");
private static final Pattern UC_PATTERN = Pattern.compile("UCBrowser/(\\d+)(?:\\.\\d+)*");
private static final Pattern QQ_PATTERN = Pattern.compile("QQBrowser/(\\d+)(?:\\.\\d+)*");
private static final Pattern WECHAT_PATTERN = Pattern.compile("MicroMessenger/(\\d+)(?:\\.\\d+)*");
private static final Pattern BAIDU_PATTERN = Pattern.compile("baidubrowser/(\\d+)(?:\\.\\d+)*");
// 操作系统正则表达式模式
private static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows NT (\\d+\\.\\d+)");
private static final Pattern MACOS_PATTERN = Pattern.compile("Mac OS X (\\d+[_\\d]*)");
private static final Pattern ANDROID_PATTERN = Pattern.compile("Android (\\d+)(?:\\.\\d+)*");
private static final Pattern IOS_PATTERN = Pattern.compile("OS[\\s_](\\d+)(?:_\\d+)*");
private static final Pattern LINUX_PATTERN = Pattern.compile("Linux");
private static final Pattern CHROMEOS_PATTERN = Pattern.compile("CrOS");
private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
.newBuilder().hideMatcherLoadStats()
.withCache(5000)
.showMinimalVersion()
.withField(UserAgent.AGENT_NAME_VERSION)
.withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION)
.build();
/**
* 获取客户端浏览器
*/
public static String getBrowser(String userAgent)
{
UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
String agentNameVersion = iua.get(UserAgent.AGENT_NAME_VERSION).getValue();
if (StringUtils.isBlank(agentNameVersion) || agentNameVersion.contains("??"))
{
return formatBrowser(userAgent);
}
return agentNameVersion;
}
/**
* 获取客户端操作系统
*/
public static String getOperatingSystem(String userAgent)
{
UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
String operatingSystemNameVersion = iua.get(UserAgent.OPERATING_SYSTEM_NAME_VERSION).getValue();
if (StringUtils.isBlank(operatingSystemNameVersion) || operatingSystemNameVersion.contains("??"))
{
return formatOperatingSystem(userAgent);
}
return operatingSystemNameVersion;
}
/**
* 全面浏览器检测
*/
private static String formatBrowser(String browser)
{
// Chrome系列浏览器
Matcher chromeMatcher = CHROME_PATTERN.matcher(browser);
if (chromeMatcher.find() && (browser.contains("Chrome") || browser.contains("CriOS")))
{
return "Chrome" + chromeMatcher.group(1);
}
// Firefox
Matcher firefoxMatcher = FIREFOX_PATTERN.matcher(browser);
if (firefoxMatcher.find())
{
return "Firefox" + firefoxMatcher.group(1);
}
// Edge浏览器
Matcher edgeMatcher = EDGE_PATTERN.matcher(browser);
if (edgeMatcher.find())
{
return "Edge" + edgeMatcher.group(1);
}
// Safari浏览器需排除Chrome
Matcher safariMatcher = SAFARI_PATTERN.matcher(browser);
if (safariMatcher.find() && !browser.contains("Chrome"))
{
return "Safari" + safariMatcher.group(1);
}
// 微信内置浏览器
Matcher wechatMatcher = WECHAT_PATTERN.matcher(browser);
if (wechatMatcher.find())
{
return "WeChat" + wechatMatcher.group(1);
}
// UC浏览器
Matcher ucMatcher = UC_PATTERN.matcher(browser);
if (ucMatcher.find())
{
return "UC Browser" + ucMatcher.group(1);
}
// QQ浏览器
Matcher qqMatcher = QQ_PATTERN.matcher(browser);
if (qqMatcher.find())
{
return "QQ Browser" + qqMatcher.group(1);
}
// 百度浏览器
Matcher baiduMatcher = BAIDU_PATTERN.matcher(browser);
if (baiduMatcher.find())
{
return "Baidu Browser" + baiduMatcher.group(1);
}
// Samsung浏览器
Matcher samsungMatcher = SAMSUNG_PATTERN.matcher(browser);
if (samsungMatcher.find())
{
return "Samsung Browser" + samsungMatcher.group(1);
}
// Opera浏览器
Matcher operaMatcher = OPERA_PATTERN.matcher(browser);
if (operaMatcher.find())
{
return "Opera" + operaMatcher.group(1);
}
// IE浏览器
Matcher ieMatcher = IE_PATTERN.matcher(browser);
if (ieMatcher.find())
{
return "Internet Explorer" + ieMatcher.group(1);
}
return UNKNOWN;
}
/**
* 检测操作系统
*/
private static String formatOperatingSystem(String operatingSystem)
{
// Windows系统
Matcher windowsMatcher = WINDOWS_PATTERN.matcher(operatingSystem);
if (windowsMatcher.find())
{
return "Windows" + getWindowsVersionDisplay(windowsMatcher.group(1));
}
// macOS系统
Matcher macMatcher = MACOS_PATTERN.matcher(operatingSystem);
if (macMatcher.find())
{
String version = macMatcher.group(1).replace("_", ".");
return "macOS" + extractMajorVersion(version);
}
// Android系统
Matcher androidMatcher = ANDROID_PATTERN.matcher(operatingSystem);
if (androidMatcher.find())
{
return "Android" + extractMajorVersion(androidMatcher.group(1));
}
// iOS系统
Matcher iosMatcher = IOS_PATTERN.matcher(operatingSystem);
if (iosMatcher.find() && (operatingSystem.contains("iPhone") || operatingSystem.contains("iPad")))
{
return "iOS" + extractMajorVersion(iosMatcher.group(1));
}
// Linux系统
if (LINUX_PATTERN.matcher(operatingSystem).find() && !operatingSystem.contains("Android"))
{
return "Linux";
}
// Chrome OS
if (CHROMEOS_PATTERN.matcher(operatingSystem).find())
{
return "Chrome OS";
}
return UNKNOWN;
}
/**
* 提取优化的主版本号
*/
private static String extractMajorVersion(String fullVersion)
{
if (StringUtils.isEmpty(fullVersion))
{
return StringUtils.EMPTY;
}
try
{
// 清理版本号中的非数字字符
String cleanVersion = fullVersion.replaceAll("[^0-9.]", "");
String[] parts = cleanVersion.split("\\.");
if (parts.length > 0)
{
String firstPart = parts[0];
if (firstPart.matches("\\d+"))
{
int version = Integer.parseInt(firstPart);
// 处理三位数版本号如142 -> 14
if (version >= 100)
{
return String.valueOf(version / 10);
}
return firstPart;
}
}
}
catch (NumberFormatException e)
{
// 版本号解析失败,返回原始值
}
return fullVersion;
}
/**
* Windows版本号显示优化
*/
private static String getWindowsVersionDisplay(String version)
{
switch (version)
{
case "10.0":
return "10";
case "6.3":
return "8.1";
case "6.2":
return "8";
case "6.1":
return "7";
case "6.0":
return "Vista";
case "5.1":
return "XP";
case "5.0":
return "2000";
default:
return extractMajorVersion(version);
}
}
}

View File

@ -2,7 +2,7 @@ package com.ruoyi.common.utils.ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;

View File

@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
@ -174,23 +174,18 @@ public class ExcelUtil<T>
/**
* 对象的子列表方法
*/
private Method subMethod;
private Map<String, Method> subMethods;
/**
* 对象的子列表属性
*/
private List<Field> subFields;
private Map<String, List<Field>> subFieldsMap;
/**
* 统计列表
*/
private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
/**
* 数字格式
*/
private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
/**
* 实体对象
*/
@ -257,7 +252,10 @@ public class ExcelUtil<T>
int titleLastCol = this.fields.size() - 1;
if (isSubList())
{
titleLastCol = titleLastCol + subFields.size() - 1;
for (List<Field> currentSubFields : subFieldsMap.values())
{
titleLastCol = titleLastCol + currentSubFields.size() - 1;
}
}
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
titleRow.setHeightInPoints(30);
@ -277,16 +275,17 @@ public class ExcelUtil<T>
{
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];
CellStyle cellStyle = styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()));
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())));
cell.setCellStyle(cellStyle);
int subFieldSize = subFieldsMap != null ? subFieldsMap.get(field.getName()).size() : 0;
if (subFieldSize > 1)
{
CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
@ -298,7 +297,7 @@ public class ExcelUtil<T>
{
Cell cell = subRow.createCell(column++);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
cell.setCellStyle(cellStyle);
}
}
rownum++;
@ -379,7 +378,11 @@ public class ExcelUtil<T>
Map<String, Integer> cellMap = new HashMap<String, Integer>();
// 获取表头
Row heard = sheet.getRow(titleNum);
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
if (heard == null)
{
throw new UtilException("文件标题行为空请检查Excel文件格式");
}
for (int i = 0; i < heard.getLastCellNum(); i++)
{
Cell cell = heard.getCell(i);
if (StringUtils.isNotNull(cell))
@ -387,10 +390,6 @@ public class ExcelUtil<T>
String value = this.getCellValue(heard, i).toString();
cellMap.put(value, i);
}
else
{
cellMap.put(null, i);
}
}
// 有数据时才处理 得到类的所有field.
List<Object[]> fields = this.getFields();
@ -419,7 +418,7 @@ public class ExcelUtil<T>
Object val = this.getCellValue(row, entry.getKey());
// 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity);
entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity);
// 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
@ -702,7 +701,8 @@ public class ExcelUtil<T>
Excel excel = (Excel) os[1];
if (Collection.class.isAssignableFrom(field.getType()))
{
for (Field subField : subFields)
List<Field> currentSubFields = subFieldsMap.get(field.getName());
for (Field subField : currentSubFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
this.createHeadCell(subExcel, row, column++);
@ -715,7 +715,7 @@ public class ExcelUtil<T>
}
if (Type.EXPORT.equals(type))
{
fillExcelData(index, row);
fillExcelData(index);
addStatisticsRow();
}
}
@ -725,10 +725,9 @@ public class ExcelUtil<T>
* 填充excel数据
*
* @param index 序号
* @param row 单元格行
*/
@SuppressWarnings("unchecked")
public void fillExcelData(int index, Row row)
public void fillExcelData(int index)
{
int startNo = index * sheetSize;
int endNo = Math.min(startNo + sheetSize, list.size());
@ -736,7 +735,7 @@ public class ExcelUtil<T>
for (int i = startNo; i < endNo; i++)
{
row = sheet.createRow(currentRowNum);
Row row = sheet.createRow(currentRowNum);
T vo = (T) list.get(i);
int column = 0;
int maxSubListSize = getCurrentMaxSubListSize(vo);
@ -749,6 +748,7 @@ public class ExcelUtil<T>
try
{
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
List<Field> currentSubFields = subFieldsMap.get(field.getName());
if (subList != null && !subList.isEmpty())
{
int subIndex = 0;
@ -761,15 +761,15 @@ public class ExcelUtil<T>
}
int subColumn = column;
for (Field subField : subFields)
for (Field subField : currentSubFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
}
subIndex++;
}
column += subFields.size();
}
column += currentSubFields.size();
}
catch (Exception e)
{
@ -861,6 +861,7 @@ public class ExcelUtil<T>
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setDataFormat(dataFormat.getFormat("######0.00"));
Font totalFont = wb.createFont();
totalFont.setFontName("Arial");
totalFont.setFontHeightInPoints((short) 10);
@ -1123,7 +1124,8 @@ public class ExcelUtil<T>
/**
* 添加单元格
*/
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
@SuppressWarnings("deprecation")
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
{
Cell cell = null;
try
@ -1135,7 +1137,7 @@ public class ExcelUtil<T>
{
// 创建cell
cell = row.createCell(column);
if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
if (isSubListValue(vo) && getListCellValue(vo) > 1 && attr.needMerge())
{
if (subMergedLastRowNum >= subMergedFirstRowNum)
{
@ -1152,7 +1154,7 @@ public class ExcelUtil<T>
String dictType = attr.dictType();
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
{
cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat));
cell.setCellStyle(createCellStyle(cell.getCellStyle(), dateFormat));
cell.setCellValue(parseDateToStr(dateFormat, value));
}
else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
@ -1191,6 +1193,21 @@ public class ExcelUtil<T>
return cell;
}
/**
* 使用自定义格式,同时避免样式污染
*
* @param cellStyle 从此样式复制
* @param format 格式匹配的字符串
* @return 格式化后CellStyle对象
*/
private CellStyle createCellStyle(CellStyle cellStyle, String format)
{
CellStyle style = wb.createCellStyle();
style.cloneStyleFrom(cellStyle);
style.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(format));
return style;
}
/**
* 设置 POI XSSFSheet 单元格提示或选择框
*
@ -1242,18 +1259,36 @@ public class ExcelUtil<T>
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++)
Sheet hideSheet = null;
String hideSheetDataName = hideSheetName + "_data";
Name name = wb.getName(hideSheetDataName);
if (name != null)
{
hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
// 名称已存在尝试从名称的引用中找到sheet名称
String refersToFormula = name.getRefersToFormula();
if (StringUtils.isNotEmpty(refersToFormula) && refersToFormula.contains("!"))
{
String sheetNameFromFormula = refersToFormula.substring(0, refersToFormula.indexOf("!"));
hideSheet = wb.getSheet(sheetNameFromFormula);
}
}
// 创建名称,可被其他单元格引用
Name name = wb.createName();
name.setNameName(hideSheetName + "_data");
name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
if (hideSheet == null)
{
hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
for (int i = 0; i < textlist.length; i++)
{
hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
}
// 创建名称,可被其他单元格引用
name = wb.createName();
name.setNameName(hideSheetDataName);
name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
}
DataValidationHelper helper = sheet.getDataValidationHelper();
// 加载下拉列表内容
DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetDataName);
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
// 数据有效性对象
@ -1391,7 +1426,7 @@ public class ExcelUtil<T>
{
try
{
Object instance = excel.handler().newInstance();
Object instance = excel.handler().getDeclaredConstructor().newInstance();
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);
}
@ -1442,7 +1477,7 @@ public class ExcelUtil<T>
{
cell = row.createCell(key);
cell.setCellStyle(styles.get("total"));
cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
cell.setCellValue(statistics.get(key));
}
statistics.clear();
}
@ -1541,6 +1576,8 @@ public class ExcelUtil<T>
{
List<Object[]> fields = new ArrayList<Object[]>();
List<Field> tempFields = new ArrayList<>();
subFieldsMap = new HashMap<>();
subMethods = new HashMap<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
if (StringUtils.isNotEmpty(includeFields))
@ -1588,10 +1625,11 @@ public class ExcelUtil<T>
}
if (Collection.class.isAssignableFrom(field.getType()))
{
subMethod = getSubMethod(field.getName(), clazz);
String fieldName = field.getName();
subMethods.put(fieldName, getSubMethod(fieldName, clazz));
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
subFieldsMap.put(fieldName, FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class));
}
}
@ -1660,7 +1698,8 @@ public class ExcelUtil<T>
{
this.sheet = wb.createSheet();
this.createTitle();
wb.setSheetName(index, sheetName + index);
int actualIndex = wb.getSheetIndex(this.sheet);
wb.setSheetName(actualIndex, sheetName + index);
}
}
@ -1843,7 +1882,7 @@ public class ExcelUtil<T>
*/
public boolean isSubList()
{
return StringUtils.isNotNull(subFields) && subFields.size() > 0;
return !StringUtils.isEmpty(subFieldsMap);
}
/**
@ -1851,24 +1890,32 @@ public class ExcelUtil<T>
*/
public boolean isSubListValue(T vo)
{
return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
return !StringUtils.isEmpty(subFieldsMap) && getListCellValue(vo) > 0;
}
/**
* 获取集合的值
*/
public Collection<?> getListCellValue(Object obj)
public int getListCellValue(Object obj)
{
Object value;
Collection<?> value;
int max = 0;
try
{
value = subMethod.invoke(obj, new Object[] {});
for (String s : subMethods.keySet())
{
value = (Collection<?>) subMethods.get(s).invoke(obj);
if (value.size() > max)
{
max = value.size();
}
}
}
catch (Exception e)
{
return new ArrayList<Object>();
return 0;
}
return (Collection<?>) value;
return max;
}
/**

View File

@ -310,6 +310,7 @@ public class ReflectUtils
/**
* 改变private/protected的方法为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨。
*/
@SuppressWarnings("deprecation")
public static void makeAccessible(Method method)
{
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
@ -322,6 +323,7 @@ public class ReflectUtils
/**
* 改变private/protected的成员变量为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨。
*/
@SuppressWarnings("deprecation")
public static void makeAccessible(Field field)
{
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())

View File

@ -13,7 +13,7 @@ public class SqlUtil
/**
* 定义常用的 sql关键字
*/
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_REGEX = "\u000B|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.xss;
import javax.validation.Constraint;
import javax.validation.Payload;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -1,8 +1,8 @@
package com.ruoyi.common.xss;
import com.ruoyi.common.utils.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -32,7 +32,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<!-- 验证码 -->

View File

@ -94,7 +94,7 @@ public class DataScopeAspect
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)))
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && (StringUtils.isEmpty(permission) || StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))))
{
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
}
@ -107,7 +107,7 @@ public class DataScopeAspect
{
continue;
}
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}

View File

@ -2,8 +2,8 @@ package com.ruoyi.framework.aspectj;
import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
@ -50,6 +50,9 @@ public class LogAspect
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/** 参数最大长度限制 */
private static final int PARAM_MAX_LENGTH = 2000;
/**
* 处理请求前执行
*/
@ -172,16 +175,16 @@ public class LogAspect
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
{
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
operLog.setOperParam(params);
}
else
{
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, PARAM_MAX_LENGTH));
}
}
@ -190,7 +193,7 @@ public class LogAspect
*/
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
{
String params = "";
StringBuilder params = new StringBuilder();
if (paramsArray != null && paramsArray.length > 0)
{
for (Object o : paramsArray)
@ -200,15 +203,20 @@ public class LogAspect
try
{
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
params += jsonObj.toString() + " ";
params.append(jsonObj).append(" ");
if (params.length() >= PARAM_MAX_LENGTH)
{
return StringUtils.substring(params.toString(), 0, PARAM_MAX_LENGTH);
}
}
catch (Exception e)
{
log.error("请求参数拼装异常 msg:{}, 参数:{}", e.getMessage(), paramsArray, e);
}
}
}
}
return params.trim();
return params.toString();
}
/**

View File

@ -3,11 +3,6 @@ package com.ruoyi.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -16,13 +11,18 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.DruidProperties;
import com.ruoyi.framework.datasource.DynamicDataSource;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
/**
* druid 配置多数据源
@ -96,7 +96,7 @@ public class DruidConfig
Filter filter = new Filter()
{
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
public void init(jakarta.servlet.FilterConfig filterConfig) throws ServletException
{
}
@Override

View File

@ -2,12 +2,14 @@ package com.ruoyi.framework.config;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import jakarta.servlet.DispatcherType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.filter.RefererFilter;
import com.ruoyi.common.filter.RepeatableFilter;
import com.ruoyi.common.filter.XssFilter;
import com.ruoyi.common.utils.StringUtils;
@ -26,6 +28,9 @@ public class FilterConfig
@Value("${xss.urlPatterns}")
private String urlPatterns;
@Value("${referer.allowed-domains}")
private String allowedDomains;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
@ -43,6 +48,23 @@ public class FilterConfig
return registration;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
@ConditionalOnProperty(value = "referer.enabled", havingValue = "true")
public FilterRegistrationBean refererFilterRegistration()
{
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new RefererFilter());
registration.addUrlPatterns(Constants.RESOURCE_PREFIX + "/*");
registration.setName("refererFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("allowedDomains", allowedDomains);
registration.setInitParameters(initParameters);
return registration;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean someFilterRegistration()

View File

@ -14,6 +14,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
*
* @author ruoyi
*/
@SuppressWarnings("deprecation")
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport

View File

@ -5,12 +5,10 @@ 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.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
@ -30,12 +28,6 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
@Configuration
public class SecurityConfig
{
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@ -66,17 +58,14 @@ public class SecurityConfig
@Autowired
private PermitAllUrlProperties permitAllUrl;
/**
* 身份验证实现
*/
@Bean
public AuthenticationManager authenticationManager()
{
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}
/**
* 身份验证实现
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
{
return authenticationConfiguration.getAuthenticationManager();
}
/**
* anyRequest | 匹配所有请求路径
@ -109,12 +98,12 @@ public class SecurityConfig
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
requests.requestMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.config;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.ServletUtils;

View File

@ -37,7 +37,7 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
@Override
public void afterPropertiesSet()
{
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(info -> {
@ -45,12 +45,12 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) //
.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())
Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
});
}

View File

@ -1,8 +1,8 @@
package com.ruoyi.framework.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

View File

@ -3,7 +3,7 @@ package com.ruoyi.framework.interceptor.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

View File

@ -3,7 +3,7 @@ package com.ruoyi.framework.manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import jakarta.annotation.PreDestroy;
/**
* 确保应用退出时能关闭后台线程

View File

@ -7,6 +7,7 @@ import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
@ -14,7 +15,6 @@ import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService;
import eu.bitwalker.useragentutils.UserAgent;
/**
* 异步工厂(产生任务用)
@ -37,7 +37,7 @@ public class AsyncFactory
public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args)
{
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
final String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
final String ip = IpUtils.getIpAddr();
return new TimerTask()
{
@ -54,9 +54,9 @@ public class AsyncFactory
// 打印信息到日志
sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
String os = UserAgentUtils.getOperatingSystem(userAgent);
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
String browser = UserAgentUtils.getBrowser(userAgent);
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);

View File

@ -1,10 +1,10 @@
package com.ruoyi.framework.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;

View File

@ -2,8 +2,8 @@ package com.ruoyi.framework.security.handle;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

View File

@ -1,9 +1,9 @@
package com.ruoyi.framework.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.web.exception;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;

View File

@ -53,7 +53,7 @@ public class PermissionService
/**
* 验证用户是否具有以下任意一个权限
*
* @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表
* @param permissions 以 PERMISSION_DELIMITER 为分隔符的权限列表
* @return 用户是否具有以下任意一个权限
*/
public boolean hasAnyPermi(String permissions)
@ -69,7 +69,7 @@ public class PermissionService
}
PermissionContextHolder.setContext(permissions);
Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(Constants.PERMISSION_DELIMETER))
for (String permission : permissions.split(Constants.PERMISSION_DELIMITER))
{
if (permission != null && hasPermissions(authorities, permission))
{
@ -121,7 +121,7 @@ public class PermissionService
/**
* 验证用户是否具有以下任意一个角色
*
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
* @param roles 以 ROLE_DELIMITER 为分隔符的角色列表
* @return 用户是否具有以下任意一个角色
*/
public boolean hasAnyRoles(String roles)
@ -135,7 +135,7 @@ public class PermissionService
{
return false;
}
for (String role : roles.split(Constants.ROLE_DELIMETER))
for (String role : roles.split(Constants.ROLE_DELIMITER))
{
if (hasRole(role))
{

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.web.service;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
@ -10,7 +10,6 @@ import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
@ -172,10 +171,6 @@ public class SysLoginService
*/
public void recordLoginInfo(Long userId)
{
SysUser sysUser = new SysUser();
sysUser.setUserId(userId);
sysUser.setLoginIp(IpUtils.getIpAddr());
sysUser.setLoginDate(DateUtils.getNowDate());
userService.updateUserProfile(sysUser);
userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate());
}
}

View File

@ -6,6 +6,7 @@ import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -39,7 +40,7 @@ public class SysPermissionService
// 管理员拥有所有权限
if (user.isAdmin())
{
roles.add("admin");
roles.add(Constants.SUPER_ADMIN);
}
else
{
@ -60,7 +61,7 @@ public class SysPermissionService
// 管理员拥有所有权限
if (user.isAdmin())
{
perms.add("*:*:*");
perms.add(Constants.ALL_PERMISSION);
}
else
{

View File

@ -3,7 +3,7 @@ package com.ruoyi.framework.web.service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -15,10 +15,10 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@ -161,12 +161,12 @@ public class TokenService
*/
public void setUserAgent(LoginUser loginUser)
{
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
String ip = IpUtils.getIpAddr();
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());
loginUser.setOs(userAgent.getOperatingSystem().getName());
loginUser.setBrowser(UserAgentUtils.getBrowser(userAgent));
loginUser.setOs(UserAgentUtils.getOperatingSystem(userAgent));
}
/**

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -32,7 +32,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
</dependencies>

View File

@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;

View File

@ -1,8 +1,8 @@
package com.ruoyi.generator.domain;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.apache.commons.lang3.ArrayUtils;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,6 +1,6 @@
package com.ruoyi.generator.domain;
import javax.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotBlank;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.utils.StringUtils;

View File

@ -1,7 +1,7 @@
package ${packageName}.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;

View File

@ -334,7 +334,7 @@ function getList() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
if (null != daterange${AttrName}.value && '' != daterange${AttrName}.value) {
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]
}

View File

@ -415,7 +415,7 @@ function getList() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
if (null != daterange${AttrName}.value && '' != daterange${AttrName}.value) {
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -21,12 +21,6 @@
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 通用工具-->

View File

@ -1,7 +1,7 @@
package com.ruoyi.quartz.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;

View File

@ -1,7 +1,7 @@
package com.ruoyi.quartz.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,8 +1,8 @@
package com.ruoyi.quartz.domain;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonFormat;

View File

@ -1,7 +1,7 @@
package com.ruoyi.quartz.service.impl;
import java.util.List;
import javax.annotation.PostConstruct;
import jakarta.annotation.PostConstruct;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;

View File

@ -131,11 +131,11 @@ public class ScheduleUtils
int count = StringUtils.countMatches(packageName, ".");
if (count > 1)
{
return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);
return StringUtils.startsWithAny(invokeTarget, Constants.JOB_WHITELIST_STR);
}
Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
String beanPackageName = obj.getClass().getPackage().getName();
return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR)
&& !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR);
return StringUtils.startsWithAny(beanPackageName, Constants.JOB_WHITELIST_STR)
&& !StringUtils.startsWithAny(beanPackageName, Constants.JOB_ERROR_STR);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -1,7 +1,7 @@
package com.ruoyi.system.domain;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;

View File

@ -1,7 +1,7 @@
package com.ruoyi.system.domain;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,8 +1,8 @@
package com.ruoyi.system.domain;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;

View File

@ -122,4 +122,13 @@ public interface SysMenuMapper
* @return 结果
*/
public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId);
/**
* 根据路由路径或名称查询菜单信息(用于唯一性校验)
*
* @param path 路由地址
* @param routeName 路由名称
* @return 匹配的菜单列表
*/
public List<SysMenu> selectMenusByPathOrRouteName(@Param("path") String path, @Param("routeName") String routeName);
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.system.mapper;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -70,20 +71,39 @@ public interface SysUserMapper
/**
* 修改用户头像
*
* @param userName 用户
* @param userId 用户ID
* @param avatar 头像地址
* @return 结果
*/
public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar);
public int updateUserAvatar(@Param("userId") Long userId, @Param("avatar") String avatar);
/**
* 修改用户状态
*
* @param userId 用户ID
* @param status 状态
* @return 结果
*/
public int updateUserStatus(@Param("userId") Long userId, @Param("status") String status);
/**
* 更新用户登录信息IP和登录时间
*
* @param userId 用户ID
* @param loginIp 登录IP地址
* @param loginDate 登录时间
* @return 结果
*/
public int updateLoginInfo(@Param("userId") Long userId, @Param("loginIp") String loginIp, @Param("loginDate") Date loginDate);
/**
* 重置用户密码
*
* @param userName 用户
* @param userId 用户ID
* @param password 密码
* @return 结果
*/
public int resetUserPwd(@Param("userName") String userName, @Param("password") String password);
public int resetUserPwd(@Param("userId") Long userId, @Param("password") String password);
/**
* 通过用户ID删除用户

View File

@ -141,4 +141,12 @@ public interface ISysMenuService
* @return 结果
*/
public boolean checkMenuNameUnique(SysMenu menu);
/**
* 校验路由组合是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
public boolean checkRouteConfigUnique(SysMenu menu);
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.system.service;
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -155,11 +156,21 @@ public interface ISysUserService
/**
* 修改用户头像
*
* @param userName 用户
* @param userId 用户ID
* @param avatar 头像地址
* @return 结果
*/
public boolean updateUserAvatar(String userName, String avatar);
public boolean updateUserAvatar(Long userId, String avatar);
/**
* 更新用户登录信息IP和登录时间
*
* @param userId 用户ID
* @param loginIp 登录IP地址
* @param loginDate 登录时间
* @return 结果
*/
public void updateLoginInfo(Long userId, String loginIp, Date loginDate);
/**
* 重置用户密码
@ -172,11 +183,11 @@ public interface ISysUserService
/**
* 重置用户密码
*
* @param userName 用户
* @param userId 用户ID
* @param password 密码
* @return 结果
*/
public int resetUserPwd(String userName, String password);
public int resetUserPwd(Long userId, String password);
/**
* 通过用户ID删除用户

View File

@ -2,7 +2,7 @@ package com.ruoyi.system.service.impl;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.annotation.DataSource;

View File

@ -11,7 +11,6 @@ import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
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.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
@ -190,7 +189,7 @@ public class SysDeptServiceImpl implements ISysDeptService
@Override
public void checkDeptDataScope(Long deptId)
{
if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(deptId))
if (!SecurityUtils.isAdmin() && StringUtils.isNotNull(deptId))
{
SysDept dept = new SysDept();
dept.setDeptId(deptId);

View File

@ -4,7 +4,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

View File

@ -8,6 +8,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.Constants;
@ -15,7 +17,6 @@ import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.vo.MetaVo;
@ -33,8 +34,12 @@ import com.ruoyi.system.service.ISysMenuService;
@Service
public class SysMenuServiceImpl implements ISysMenuService
{
private static final Logger log = LoggerFactory.getLogger(SysMenuServiceImpl.class);
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
public static final Long MENU_ROOT_ID = 0L;
@Autowired
private SysMenuMapper menuMapper;
@ -67,7 +72,7 @@ public class SysMenuServiceImpl implements ISysMenuService
{
List<SysMenu> menuList = null;
// 管理员显示所有菜单信息
if (SysUser.isAdmin(userId))
if (SecurityUtils.isAdmin(userId))
{
menuList = menuMapper.selectMenuList(menu);
}
@ -139,7 +144,7 @@ public class SysMenuServiceImpl implements ISysMenuService
{
menus = menuMapper.selectMenuTreeByUserId(userId);
}
return getChildPerms(menus, 0);
return getChildPerms(menus, MENU_ROOT_ID);
}
/**
@ -194,7 +199,7 @@ public class SysMenuServiceImpl implements ISysMenuService
childrenList.add(children);
router.setChildren(childrenList);
}
else if (menu.getParentId().intValue() == 0 && isInnerLink(menu))
else if (menu.getParentId().intValue() == MENU_ROOT_ID && isInnerLink(menu))
{
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
router.setPath("/");
@ -346,6 +351,47 @@ public class SysMenuServiceImpl implements ISysMenuService
return UserConstants.UNIQUE;
}
/**
* 校验路由名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public boolean checkRouteConfigUnique(SysMenu menu)
{
Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
Long parentId = menu.getParentId();
String path = menu.getPath();
String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName();
List<SysMenu> sysMenuList = menuMapper.selectMenusByPathOrRouteName(path, routeName);
for (SysMenu sysMenu : sysMenuList)
{
if (sysMenu.getMenuId().longValue() != menuId.longValue())
{
Long dbParentId = sysMenu.getParentId();
String dbPath = sysMenu.getPath();
String dbRouteName = StringUtils.isEmpty(sysMenu.getRouteName()) ? dbPath : sysMenu.getRouteName();
if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue())
{
log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName());
return UserConstants.NOT_UNIQUE;
}
else if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == MENU_ROOT_ID)
{
log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName());
return UserConstants.NOT_UNIQUE;
}
else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName))
{
log.warn("[路由名称冲突] 路由名称 '{}' 需全局唯一,已被菜单 '{}' 使用", routeName, sysMenu.getMenuName());
return UserConstants.NOT_UNIQUE;
}
}
}
return UserConstants.UNIQUE;
}
/**
* 获取路由名称
*
@ -385,12 +431,12 @@ public class SysMenuServiceImpl implements ISysMenuService
{
String routerPath = menu.getPath();
// 内链打开外网方式
if (menu.getParentId().intValue() != 0 && isInnerLink(menu))
if (menu.getParentId().intValue() != MENU_ROOT_ID && isInnerLink(menu))
{
routerPath = innerLinkReplaceEach(routerPath);
}
// 非外链并且是一级目录(类型为目录)
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
if (MENU_ROOT_ID == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
&& UserConstants.NO_FRAME.equals(menu.getIsFrame()))
{
routerPath = "/" + menu.getPath();
@ -416,7 +462,7 @@ public class SysMenuServiceImpl implements ISysMenuService
{
component = menu.getComponent();
}
else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu))
else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != MENU_ROOT_ID && isInnerLink(menu))
{
component = UserConstants.INNER_LINK;
}
@ -435,10 +481,21 @@ public class SysMenuServiceImpl implements ISysMenuService
*/
public boolean isMenuFrame(SysMenu menu)
{
return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
return menu.getParentId().intValue() == MENU_ROOT_ID && UserConstants.TYPE_MENU.equals(menu.getMenuType())
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
}
/**
* 是否为parent_view组件
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isParentView(SysMenu menu)
{
return menu.getParentId().intValue() != MENU_ROOT_ID && UserConstants.TYPE_DIR.equals(menu.getMenuType());
}
/**
* 是否为内链组件
*
@ -450,17 +507,6 @@ public class SysMenuServiceImpl implements ISysMenuService
return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());
}
/**
* 是否为parent_view组件
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isParentView(SysMenu menu)
{
return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
}
/**
* 根据父节点的ID获取所有子节点
*
@ -468,7 +514,7 @@ public class SysMenuServiceImpl implements ISysMenuService
* @param parentId 传入的父节点ID
* @return String
*/
public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId)
public List<SysMenu> getChildPerms(List<SysMenu> list, long parentId)
{
List<SysMenu> returnList = new ArrayList<SysMenu>();
for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();)

View File

@ -11,7 +11,6 @@ import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
@ -197,7 +196,7 @@ public class SysRoleServiceImpl implements ISysRoleService
@Override
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(SecurityUtils.getUserId()))
if (!SecurityUtils.isAdmin())
{
for (Long roleId : roleIds)
{

View File

@ -1,9 +1,10 @@
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.Validator;
import jakarta.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -238,7 +239,7 @@ public class SysUserServiceImpl implements ISysUserService
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(SecurityUtils.getUserId()))
if (!SecurityUtils.isAdmin())
{
SysUser user = new SysUser();
user.setUserId(userId);
@ -326,7 +327,7 @@ public class SysUserServiceImpl implements ISysUserService
@Override
public int updateUserStatus(SysUser user)
{
return userMapper.updateUser(user);
return userMapper.updateUserStatus(user.getUserId(), user.getStatus());
}
/**
@ -344,14 +345,27 @@ public class SysUserServiceImpl implements ISysUserService
/**
* 修改用户头像
*
* @param userName 用户
* @param userId 用户ID
* @param avatar 头像地址
* @return 结果
*/
@Override
public boolean updateUserAvatar(String userName, String avatar)
public boolean updateUserAvatar(Long userId, String avatar)
{
return userMapper.updateUserAvatar(userName, avatar) > 0;
return userMapper.updateUserAvatar(userId, avatar) > 0;
}
/**
* 更新用户登录信息IP和登录时间
*
* @param userId 用户ID
* @param loginIp 登录IP地址
* @param loginDate 登录时间
* @return 结果
*/
public void updateLoginInfo(Long userId, String loginIp, Date loginDate)
{
userMapper.updateLoginInfo(userId, loginIp, loginDate);
}
/**
@ -363,20 +377,20 @@ public class SysUserServiceImpl implements ISysUserService
@Override
public int resetPwd(SysUser user)
{
return userMapper.updateUser(user);
return userMapper.resetUserPwd(user.getUserId(), user.getPassword());
}
/**
* 重置用户密码
*
* @param userName 用户
* @param userId 用户ID
* @param password 密码
* @return 结果
*/
@Override
public int resetUserPwd(String userName, String password)
public int resetUserPwd(Long userId, String password)
{
return userMapper.resetUserPwd(userName, password);
return userMapper.resetUserPwd(userId, password);
}
/**
@ -517,6 +531,7 @@ public class SysUserServiceImpl implements ISysUserService
checkUserDataScope(u.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
user.setUserId(u.getUserId());
user.setDeptId(u.getDeptId());
user.setUpdateBy(operName);
userMapper.updateUser(user);
successNum++;

View File

@ -130,7 +130,12 @@
<select id="checkMenuNameUnique" parameterType="SysMenu" resultMap="SysMenuResult">
<include refid="selectMenuVo"/>
where menu_name=#{menuName} and parent_id = #{parentId} limit 1
where menu_name= #{menuName} and parent_id = #{parentId} limit 1
</select>
<select id="selectMenusByPathOrRouteName" parameterType="SysMenu" resultMap="SysMenuResult">
<include refid="selectMenuVo"/>
where menu_type in ('M', 'C') and (path = #{path} or path = #{routeName} or route_name = #{path} or route_name = #{routeName})
</select>
<update id="updateMenu" parameterType="SysMenu">

View File

@ -19,13 +19,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="loginIp" column="login_ip" />
<result property="loginDate" column="login_date" />
<result property="pwdUpdateDate" column="pwd_update_date" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
</resultMap>
<resultMap id="deptResult" type="SysDept">
@ -180,7 +180,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="updateUser" parameterType="SysUser">
update sys_user
<set>
<if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
<if test="deptId != 0">dept_id = #{deptId},</if>
<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
<if test="email != null ">email = #{email},</if>
<if test="phonenumber != null ">phonenumber = #{phonenumber},</if>
@ -198,15 +198,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</update>
<update id="updateUserStatus" parameterType="SysUser">
update sys_user set status = #{status} where user_id = #{userId}
update sys_user set status = #{status}, update_time = sysdate() where user_id = #{userId}
</update>
<update id="updateUserAvatar" parameterType="SysUser">
update sys_user set avatar = #{avatar} where user_name = #{userName}
update sys_user set avatar = #{avatar}, update_time = sysdate() where user_id = #{userId}
</update>
<update id="updateLoginInfo" parameterType="SysUser">
update sys_user set login_ip = #{loginIp}, login_date = #{loginDate} where user_id = #{userId}
</update>
<update id="resetUserPwd" parameterType="SysUser">
update sys_user set pwd_update_date = sysdate(), password = #{password} where user_name = #{userName}
update sys_user set pwd_update_date = sysdate(), password = #{password}, update_time = sysdate() where user_id = #{userId}
</update>
<delete id="deleteUserById" parameterType="Long">

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