244 Commits

Author SHA1 Message Date
4a5b0e6079 升级oshi到最新版本6.8.3 2025-08-28 13:33:23 +08:00
08637e31e5 优化代码 2025-08-28 13:32:57 +08:00
512b157801 优化代码 2025-08-27 15:34:24 +08:00
5e8efaa94a !1082 修复每次登录把部门id更新为null
Merge pull request !1082 from afterglow/master
2025-08-27 06:48:16 +00:00
5f11fed41b 用户导入添加验证提示 2025-08-23 11:13:51 +08:00
b60b5de750 优化布局设置显示 2025-08-23 11:12:42 +08:00
lcs
41ff3843e6 修复每次登录把部门id更新为null 2025-08-22 16:01:01 +08:00
6a2e8a35e9 修复用户归属部门无法修改为空问题 2025-08-21 14:47:48 +08:00
769165575f columns default value 2025-08-09 16:11:36 +08:00
18c8d4ec9c 显示列信息支持对象格式 2025-08-09 13:21:54 +08:00
191fd29301 自动识别json对象白名单配置范围缩小 2025-08-09 10:57:26 +08:00
47510fe2de 升级tomcat到最新版本9.0.108 2025-08-07 15:24:27 +08:00
725c7dcea2 添加新群号:191164766 2025-06-20 11:39:20 +08:00
158ccaebe0 优化定时任务包名白名单匹配方式 2025-06-20 11:33:25 +08:00
7b9060af26 优化Excel统计行数值的单元格样式显示 2025-06-19 14:47:49 +08:00
1a2f20e859 升级oshi到最新版本6.8.2 2025-06-18 13:51:41 +08:00
09faecb5d3 升级tomcat到最新版本9.0.106 2025-06-18 13:40:59 +08:00
d46e62a21a 用户头像更换后移除旧头像文件 2025-06-06 14:58:01 +08:00
fa88922637 若依 3.9.0 2025-05-28 09:04:45 +08:00
65159934ab 注册账号设置默认密码最后更新时间 2025-05-26 10:57:49 +08:00
1642bba612 升级fastjson到最新版2.0.57 2025-05-26 08:59:40 +08:00
a7a61fee8d update vue.config.js 2025-05-24 14:31:02 +08:00
db6d5d34e6 添加底部版权信息及开关 2025-05-24 14:24:23 +08:00
9ceca3a68e 添加页签图标显示开关功能 2025-05-23 14:56:38 +08:00
cf2579612c update pwdUpdateDate 2025-05-23 10:44:51 +08:00
c0355a0f5a 账号密码支持自定义更新周期 2025-05-23 09:04:50 +08:00
8ff013552a 初始密码支持自定义修改策略 2025-05-22 23:03:30 +08:00
673249d373 升级tomcat到最新版本9.0.105 2025-05-15 10:54:38 +08:00
fe3a92a812 升级oshi到最新版本6.8.1 2025-05-15 09:05:21 +08:00
67b6a0e11b 升级commons.io到最新版本2.19.0 2025-05-15 09:04:36 +08:00
bc70351e34 delete vue-meta 2025-05-15 08:52:28 +08:00
fe0c1fcb5b delete eslint 2025-05-15 08:13:34 +08:00
9f39dfd0c1 优化导航栏显示昵称&设置 2025-05-09 13:45:39 +08:00
131abe876d 菜单搜索支持键盘选择&悬浮主题背景 2025-05-07 13:22:43 +08:00
46708ceee4 图片上传组件新增disabled属性 2025-05-06 19:13:32 +08:00
ecd201550f add columnName Drag 2025-05-06 14:52:36 +08:00
ff3f3f2631 !1013 修复图片上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Merge pull request !1013 from 稚屿/N/A
2025-05-06 05:03:41 +00:00
d3cc8f0fb7 !1012 修复文件上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Merge pull request !1012 from 稚屿/N/A
2025-05-06 05:03:36 +00:00
6cafa3373e 修复图片上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Signed-off-by: 稚屿 <1491182878@qq.com>
2025-05-06 04:53:32 +00:00
42fbf09dde 修复图片上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Signed-off-by: 稚屿 <1491182878@qq.com>
2025-05-06 04:48:46 +00:00
88b0f5bcb2 update icon 2025-05-05 11:20:27 +08:00
e852fdb687 上传组件新增拖动排序属性 2025-04-30 10:28:59 +08:00
baf2f6f46b 优化Excel匹配数值型.0结尾 2025-04-28 11:24:24 +08:00
e19f1abfeb update editor index 2025-04-27 14:02:36 +08:00
38ed092de7 remove all semicolons 2025-04-27 10:05:51 +08:00
27a037ed3d Excel导入导出支持多图片 2025-04-25 10:09:21 +08:00
87173cbe75 富文本复制粘贴图片上传至url 2025-04-24 14:23:29 +08:00
29a5b6da53 update vue.config.js 2025-04-24 11:08:10 +08:00
b1d2139559 update package.json 2025-04-24 11:07:54 +08:00
43d78c2cf5 优化低版本node无法启动的问题 2025-04-22 12:05:43 +08:00
8f4eb24bf2 优化代码 2025-04-22 12:03:31 +08:00
a9f9133e31 显隐列组件支持全选/全不选 2025-04-21 15:21:51 +08:00
09810ccf1d 优化菜单搜索查询页 2025-04-21 13:22:00 +08:00
0d9fb8b5c0 支持文件&图片组件自定义地址&参数 2025-04-18 12:55:58 +08:00
c6b0efcdc2 优化角色禁用不允许分配 2025-04-17 15:08:10 +08:00
84fef1f675 update status name 2025-04-17 15:07:38 +08:00
11fed08b56 添加新群号:287842588 2025-04-01 19:15:21 +08:00
f83b6fbfa2 remove dev runjs 2025-03-18 15:49:01 +08:00
eef81e6ca9 !997 登录页和注册页表头使用VUE_APP_TITLE配置值
Merge pull request !997 from myifengs/master
2025-03-18 07:48:23 +00:00
5a03a754e8 登录页肯注册页表头使用VUE_APP_TITLE配置值 2025-03-18 14:53:46 +08:00
245dea7215 升级tomcat到最新版本9.0.102 2025-03-14 16:09:22 +08:00
51632f8e60 优化代码 2025-03-14 16:09:01 +08:00
525ebf92d2 菜单管理新增路由名称 2025-03-06 11:02:21 +08:00
d3b23a831e 优化代码 2025-03-04 20:03:11 +08:00
89ab3bd058 !990 优化服务监控和缓存监控页面,页边距保持一致
Merge pull request !990 from NewYoung208/master
2025-03-04 11:18:04 +00:00
9e16beb48f !989 update ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java.
Merge pull request !989 from 程子/N/A
2025-03-04 11:16:45 +00:00
8d5ecc7ff4 优化服务监控和缓存监控页面,页边距保持一致 2025-03-04 16:58:16 +08:00
6e314dd3e8 update ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java.
添加String转换Boolean值时对(是、否)的支持

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

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

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

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

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



Signed-off-by: who's hu <hup_dev@outlook.com>
2023-08-22 09:25:19 +00:00
90260ce2f9 修复Excels导入时无法获取到dictType字典值问题(I7M4PW) 2023-08-21 15:52:30 +08:00
d58942c506 防重复提交数据大小限制(I7KZDA) 2023-08-21 11:57:14 +08:00
6a742e1d1b Excel导入数据临时文件无法删除问题(I7KIXX) 2023-08-19 15:43:57 +08:00
5b61aea064 修复树模板父级编码变量错误(I7JZ0L) 2023-08-19 14:34:30 +08:00
45ef542687 升级fastjson到最新版2.0.39 2023-08-15 12:17:27 +08:00
4ac7a1aa1f 升级commons.io到最新版本2.13.0 2023-08-15 11:31:38 +08:00
c5e4459bb8 优化代码 2023-08-15 11:30:49 +08:00
8f67bf416b 升级oshi到最新版本6.4.4 2023-08-14 19:11:46 +08:00
ab99a72b65 优化代码 2023-08-14 19:11:13 +08:00
7c9423657e Excel自定义数据处理器增加单元格/工作簿对象 2023-08-14 17:42:44 +08:00
128b186b8e 优化定时任务状态页面显示 2023-08-14 17:42:24 +08:00
68ac40eda9 update maven-plugin 2023-08-14 17:41:52 +08:00
5557433235 添加新群号:143961921 2023-07-28 11:12:09 +08:00
2517e9dddb 优化登录提示信息(I6ADCR) 2023-07-24 15:16:52 +08:00
a0595711ca 优化页签在Firefox浏览器被遮挡的问题 2023-07-06 22:09:16 +08:00
1ffb6379f7 排序属性orderBy参数限制长度 2023-07-06 22:09:02 +08:00
4d5c204b9a 优化代码 2023-07-06 22:08:47 +08:00
8ee740ef49 update sql 2023-07-06 22:07:00 +08:00
226 changed files with 6660 additions and 5320 deletions

View File

@ -1,11 +1,11 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.6</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.0</h1>
<h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.6-brightgreen.svg"></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/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
@ -18,11 +18,10 @@
* 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
* 提供了单应用版本[RuoYi-Vue-fast](https://github.com/yangzongzhuan/RuoYi-Vue-fast)Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
* 提供了单应用版本[RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast)Oracle版本[RuoYi-Vue-Oracle](https://gitcode.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
## 内置功能
@ -93,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群 [![加入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) 点击按钮入群。

Binary file not shown.

80
pom.xml
View File

@ -6,45 +6,100 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>3.8.6</version>
<version>3.9.0</version>
<name>ruoyi</name>
<url>http://www.ruoyi.vip</url>
<description>若依管理系统</description>
<properties>
<ruoyi.version>3.8.6</ruoyi.version>
<ruoyi.version>3.9.0</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<druid.version>1.2.16</druid.version>
<spring-boot.version>2.5.15</spring-boot.version>
<druid.version>1.2.23</druid.version>
<bitwalker.version>1.21</bitwalker.version>
<swagger.version>3.0.0</swagger.version>
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>1.4.6</pagehelper.boot.version>
<fastjson.version>2.0.34</fastjson.version>
<oshi.version>6.4.3</oshi.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.collections.version>3.2.2</commons.collections.version>
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
<fastjson.version>2.0.57</fastjson.version>
<oshi.version>6.8.3</oshi.version>
<commons.io.version>2.19.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.108</tomcat.version>
<logback.version>1.2.13</logback.version>
<spring-security.version>5.7.12</spring-security.version>
<spring-framework.version>5.3.39</spring-framework.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- 覆盖SpringFramework的依赖配置-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring-framework.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖SpringSecurity的依赖配置-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring-security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.15</version>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖logback的依赖配置-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 覆盖tomcat的依赖配置-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
@ -107,13 +162,6 @@
<version>${velocity.version}</version>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>

View File

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

View File

@ -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;
@ -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

@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
@ -80,7 +81,7 @@ public class CacheController
public AjaxResult getCacheKeys(@PathVariable String cacheName)
{
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
return AjaxResult.success(cacheKeys);
return AjaxResult.success(new TreeSet<>(cacheKeys));
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")

View File

@ -1,5 +1,6 @@
package com.ruoyi.web.controller.system;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
@ -12,9 +13,15 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysMenuService;
/**
@ -34,6 +41,12 @@ public class SysLoginController
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
@Autowired
private ISysConfigService configService;
/**
* 登录方法
*
@ -59,15 +72,23 @@ public class SysLoginController
@GetMapping("getInfo")
public AjaxResult getInfo()
{
SysUser user = SecurityUtils.getLoginUser().getUser();
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions))
{
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
return ajax;
}
@ -83,4 +104,28 @@ public class SysLoginController
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
// 检查初始密码是否提醒修改
public boolean initPasswordIsModify(Date pwdUpdateDate)
{
Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
}
// 检查密码是否过期
public boolean passwordIsExpiration(Date pwdUpdateDate)
{
Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
if (passwordValidateDays != null && passwordValidateDays > 0)
{
if (StringUtils.isNull(pwdUpdateDate))
{
// 如果从未修改过初始密码,直接提醒过期
return true;
}
Date nowDate = DateUtils.getNowDate();
return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
}
return false;
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.web.controller.system;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -16,9 +17,11 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
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;
@ -60,27 +63,22 @@ public class SysProfileController extends BaseController
public AjaxResult updateProfile(@RequestBody SysUser user)
{
LoginUser loginUser = getLoginUser();
SysUser sysUser = loginUser.getUser();
user.setUserName(sysUser.getUserName());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
SysUser currentUser = loginUser.getUser();
currentUser.setNickName(user.getNickName());
currentUser.setEmail(user.getEmail());
currentUser.setPhonenumber(user.getPhonenumber());
currentUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
}
user.setUserId(sysUser.getUserId());
user.setPassword(null);
user.setAvatar(null);
user.setDeptId(null);
if (userService.updateUserProfile(user) > 0)
if (userService.updateUserProfile(currentUser) > 0)
{
// 更新缓存用户信息
sysUser.setNickName(user.getNickName());
sysUser.setPhonenumber(user.getPhonenumber());
sysUser.setEmail(user.getEmail());
sysUser.setSex(user.getSex());
tokenService.setLoginUser(loginUser);
return success();
}
@ -92,10 +90,12 @@ public class SysProfileController extends BaseController
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword)
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
{
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
Long userId = loginUser.getUserId();
String password = loginUser.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
@ -105,10 +105,12 @@ public class SysProfileController extends BaseController
{
return error("新密码不能与旧密码相同");
}
if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userId, newPassword) > 0)
{
// 更新缓存用户密码
loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
// 更新缓存用户密码&密码最后更新时间
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}
@ -125,9 +127,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

@ -132,8 +132,8 @@ public class SysRoleController extends BaseController
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
{
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();

View File

@ -101,18 +101,18 @@ public class SysUserController extends BaseController
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
userService.checkUserDataScope(userId);
AjaxResult ajax = AjaxResult.success();
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
if (StringUtils.isNotNull(userId))
{
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
@ -124,6 +124,8 @@ public class SysUserController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
@ -151,6 +153,8 @@ public class SysUserController extends BaseController
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
@ -235,6 +239,7 @@ public class SysUserController extends BaseController
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
return success();
}

View File

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

View File

@ -3,11 +3,9 @@ ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.8.6
version: 3.9.0
# 版权年份
copyrightYear: 2023
# 实例演示开关
demoEnabled: true
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: D:/ruoyi/uploadPath
# 获取ip地址开关

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.6</version>
<version>3.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -59,13 +59,6 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>

View File

@ -83,11 +83,21 @@ public @interface Excel
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
@ -171,7 +181,7 @@ public @interface Excel
public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2);
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
private final int value;
ColumnType(int value)

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.constant;
import java.util.Locale;
import io.jsonwebtoken.Claims;
/**
@ -19,6 +20,11 @@ public class Constants
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* www主域
*/
@ -64,6 +70,26 @@ public class Constants
*/
public static final String LOGIN_FAIL = "Error";
/**
* 所有权限标识
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
public static final String SUPER_ADMIN = "admin";
/**
* 角色权限分隔符
*/
public static final String ROLE_DELIMETER = ",";
/**
* 权限标识分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
/**
* 验证码有效期(分钟)
*/
@ -129,14 +155,19 @@ public class Constants
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "com.ruoyi" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}

View File

@ -21,6 +21,9 @@ public class UserConstants
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";

View File

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

View File

@ -42,6 +42,9 @@ public class SysMenu extends BaseEntity
/** 路由参数 */
private String query;
/** 路由名称默认和路由地址相同的驼峰格式注意因为vue3版本的router会删除名称相同路由为避免名字的冲突特殊情况可以自定义 */
private String routeName;
/** 是否为外链0是 1否 */
private String isFrame;
@ -151,6 +154,16 @@ public class SysMenu extends BaseEntity
this.query = query;
}
public String getRouteName()
{
return routeName;
}
public void setRouteName(String routeName)
{
this.routeName = routeName;
}
public String getIsFrame()
{
return isFrame;
@ -242,6 +255,8 @@ public class SysMenu extends BaseEntity
.append("orderNum", getOrderNum())
.append("path", getPath())
.append("component", getComponent())
.append("query", getQuery())
.append("routeName", getRouteName())
.append("isFrame", getIsFrame())
.append("IsCache", getIsCache())
.append("menuType", getMenuType())

View File

@ -22,7 +22,7 @@ public class SysUser extends BaseEntity
private static final long serialVersionUID = 1L;
/** 用户ID */
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
/** 部门ID */
@ -42,7 +42,7 @@ public class SysUser extends BaseEntity
private String email;
/** 手机号码 */
@Excel(name = "手机号码")
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
private String phonenumber;
/** 用户性别 */
@ -55,8 +55,8 @@ public class SysUser extends BaseEntity
/** 密码 */
private String password;
/** 号状态0正常 1停用 */
@Excel(name = "号状态", readConverterExp = "0=正常,1=停用")
/** 号状态0正常 1停用 */
@Excel(name = "号状态", readConverterExp = "0=正常,1=停用")
private String status;
/** 删除标志0代表存在 2代表删除 */
@ -70,6 +70,9 @@ public class SysUser extends BaseEntity
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
private Date loginDate;
/** 密码最后更新时间 */
private Date pwdUpdateDate;
/** 部门对象 */
@Excels({
@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
@ -247,6 +250,16 @@ public class SysUser extends BaseEntity
this.loginDate = loginDate;
}
public Date getPwdUpdateDate()
{
return pwdUpdateDate;
}
public void setPwdUpdateDate(Date pwdUpdateDate)
{
this.pwdUpdateDate = pwdUpdateDate;
}
public SysDept getDept()
{
return dept;
@ -313,6 +326,7 @@ public class SysUser extends BaseEntity
.append("delFlag", getDelFlag())
.append("loginIp", getLoginIp())
.append("loginDate", getLoginDate())
.append("pwdUpdateDate", getPwdUpdateDate())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())

View File

@ -37,7 +37,7 @@ public class TableDataInfo implements Serializable
* @param list 列表数据
* @param total 总记录数
*/
public TableDataInfo(List<?> list, int total)
public TableDataInfo(List<?> list, long total)
{
this.rows = list;
this.total = total;

View File

@ -2,12 +2,12 @@ package com.ruoyi.common.core.text;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.ruoyi.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
/**
* 类型转换器
@ -364,6 +364,10 @@ public class Convert
*/
public static String[] toStrArray(String str)
{
if (StringUtils.isEmpty(str))
{
return new String[] {};
}
return toStrArray(",", str);
}
@ -536,7 +540,7 @@ public class Convert
/**
* 转换为boolean<br>
* String支持的值为true、false、yes、ok、no1,0 如果给定的值为空,或者转换失败,返回默认值<br>
* String支持的值为true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
* @param value 被转换的值
@ -565,10 +569,12 @@ public class Convert
case "yes":
case "ok":
case "1":
case "":
return true;
case "false":
case "no":
case "0":
case "":
return false;
default:
return defaultValue;
@ -791,14 +797,23 @@ public class Convert
{
return (String) obj;
}
else if (obj instanceof byte[])
else if (obj instanceof byte[] || obj instanceof Byte[])
{
if (obj instanceof byte[])
{
return str((byte[]) obj, charset);
}
else if (obj instanceof Byte[])
else
{
byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
return str(bytes, charset);
Byte[] bytes = (Byte[]) obj;
int length = bytes.length;
byte[] dest = new byte[length];
for (int i = 0; i < length; i++)
{
dest[i] = bytes[i];
}
return str(dest, charset);
}
}
else if (obj instanceof ByteBuffer)
{
@ -954,9 +969,7 @@ public class Convert
c[i] = (char) (c[i] - 65248);
}
}
String returnString = new String(c);
return returnString;
return new String(c);
}
/**
@ -977,7 +990,12 @@ public class Convert
String s = "";
for (int i = 0; i < fraction.length; i++)
{
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
// 优化double计算精度丢失问题
BigDecimal nNum = new BigDecimal(n);
BigDecimal decimal = new BigDecimal(10);
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
double d = scale.doubleValue();
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
}
if (s.length() < 1)
{

View File

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

View File

@ -32,10 +32,10 @@ public class XssFilter implements Filter
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url[i]);
excludes.add(url);
}
}
}

View File

@ -108,7 +108,6 @@ public class Arith
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = BigDecimal.ONE;
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
return b.divide(BigDecimal.ONE, scale, RoundingMode.HALF_UP).doubleValue();
}
}

View File

@ -147,7 +147,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/**
* 计算时间差
*
* @param endTime 最后时间
* @param endDate 最后时间
* @param startTime 开始时间
* @return 时间差(天/小时/分钟)
*/

View File

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

View File

@ -56,6 +56,10 @@ public class DictUtils
*/
public static String getDictLabel(String dictType, String dictValue)
{
if (StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
return getDictLabel(dictType, dictValue, SEPARATOR);
}
@ -68,6 +72,10 @@ public class DictUtils
*/
public static String getDictValue(String dictType, String dictLabel)
{
if (StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
return getDictValue(dictType, dictLabel, SEPARATOR);
}
@ -83,9 +91,10 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNotNull(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictValue))
{
for (SysDictData dict : datas)
@ -110,7 +119,6 @@ public class DictUtils
}
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
@ -126,8 +134,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictLabel))
{
for (SysDictData dict : datas)
{
@ -154,6 +165,48 @@ public class DictUtils
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型获取字典所有值
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictValues(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictValue()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 根据字典类型获取字典所有标签
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictLabels(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictLabel()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 删除指定字典缓存
*

View File

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

View File

@ -23,6 +23,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
/** 下划线 */
private static final char SEPARATOR = '_';
/** 星号 */
private static final char ASTERISK = '*';
/**
* 获取参数不为空值
*
@ -163,6 +166,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return (str == null ? "" : str.trim());
}
/**
* 替换指定字符串的指定区间内字符为"*"
*
* @param str 字符串
* @param startInclude 开始位置(包含)
* @param endExclude 结束位置(不包含)
* @return 替换后的字符串
*/
public static String hide(CharSequence str, int startInclude, int endExclude)
{
if (isEmpty(str))
{
return NULLSTR;
}
final int strLength = str.length();
if (startInclude > strLength)
{
return NULLSTR;
}
if (endExclude > strLength)
{
endExclude = strLength;
}
if (startInclude > endExclude)
{
// 如果起始位置大于结束位置,不替换
return NULLSTR;
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++)
{
if (i >= startInclude && i < endExclude)
{
chars[i] = ASTERISK;
}
else
{
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
/**
* 截取字符串
*
@ -240,6 +286,56 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return str.substring(start, end);
}
/**
* 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串
*
* @param str 要截取的字符串
* @param open 起始字符串
* @param close 结束字符串
* @return 截取结果
*/
public static String substringBetweenLast(final String str, final String open, final String close)
{
if (isEmpty(str) || isEmpty(open) || isEmpty(close))
{
return NULLSTR;
}
final int start = str.indexOf(open);
if (start != INDEX_NOT_FOUND)
{
final int end = str.lastIndexOf(close);
if (end != INDEX_NOT_FOUND)
{
return str.substring(start + open.length(), end);
}
}
return NULLSTR;
}
/**
* 判断是否为空,并且不是空白字符
*
* @param str 要判断的value
* @return 结果
*/
public static boolean hasText(String str)
{
return (str != null && !str.isEmpty() && containsText(str));
}
private static boolean containsText(CharSequence str)
{
int strLen = str.length();
for (int i = 0; i < strLen; i++)
{
if (!Character.isWhitespace(str.charAt(i)))
{
return true;
}
}
return false;
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
@ -285,6 +381,18 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return new HashSet<String>(str2List(str, sep, true, false));
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @return list集合
*/
public static final List<String> str2List(String str, String sep)
{
return str2List(str, sep, true, false);
}
/**
* 字符串转list
*

View File

@ -13,6 +13,7 @@ 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;
/**
@ -25,7 +26,7 @@ public class FileUploadUtils
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
/**
* 默认的文件名最大长度 100
@ -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

View File

@ -11,13 +11,14 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.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

@ -21,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.http.MediaType;
/**
* 通用http发送方法
@ -74,7 +75,7 @@ public class HttpUtils
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
@ -125,6 +126,19 @@ public class HttpUtils
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数
* @param contentType 内容类型
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param, String contentType)
{
PrintWriter out = null;
BufferedReader in = null;
@ -136,9 +150,9 @@ public class HttpUtils
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
@ -190,6 +204,11 @@ public class HttpUtils
}
public static String sendSSLPost(String url, String param)
{
return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
public static String sendSSLPost(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
@ -202,9 +221,9 @@ public class HttpUtils
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);

View File

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

View File

@ -39,6 +39,7 @@ import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
@ -94,6 +95,8 @@ public class ExcelUtil<T>
{
private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
public static final String SEPARATOR = ",";
public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
@ -183,16 +186,16 @@ public class ExcelUtil<T>
*/
private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
/**
* 数字格式
*/
private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
/**
* 实体对象
*/
public Class<T> clazz;
/**
* 需要显示列属性
*/
public String[] includeFields;
/**
* 需要排除列属性
*/
@ -203,11 +206,20 @@ public class ExcelUtil<T>
this.clazz = clazz;
}
/**
* 仅在Excel中显示列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
*/
public void showColumn(String... fields)
{
this.includeFields = fields;
}
/**
* 隐藏Excel中列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
* @throws Exception
*/
public void hideColumn(String... fields)
{
@ -237,8 +249,6 @@ public class ExcelUtil<T>
{
if (StringUtils.isNotEmpty(title))
{
subMergedFirstRowNum++;
subMergedLastRowNum++;
int titleLastCol = this.fields.size() - 1;
if (isSubList())
{
@ -249,7 +259,7 @@ public class ExcelUtil<T>
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));
}
}
@ -260,23 +270,31 @@ public class ExcelUtil<T>
{
if (isSubList())
{
subMergedFirstRowNum++;
subMergedLastRowNum++;
Row subRow = sheet.createRow(rownum);
int excelNum = 0;
int column = 0;
int subFieldSize = subFields != null ? subFields.size() : 0;
for (Object[] objects : fields)
{
Field field = (Field) objects[0];
Excel attr = (Excel) objects[1];
Cell headCell1 = subRow.createCell(excelNum);
headCell1.setCellValue(attr.name());
headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
excelNum++;
}
int headFirstRow = excelNum - 1;
int headLastRow = headFirstRow + subFields.size() - 1;
if (headLastRow > headFirstRow)
if (Collection.class.isAssignableFrom(field.getType()))
{
sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));
Cell cell = subRow.createCell(column);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
if (subFieldSize > 1)
{
CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
sheet.addMergedRegion(cellAddress);
}
column += subFieldSize;
}
else
{
Cell cell = subRow.createCell(column++);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
}
}
rownum++;
}
@ -288,7 +306,7 @@ public class ExcelUtil<T>
* @param is 输入流
* @return 转换后集合
*/
public List<T> importExcel(InputStream is) throws Exception
public List<T> importExcel(InputStream is)
{
return importExcel(is, 0);
}
@ -300,9 +318,23 @@ public class ExcelUtil<T>
* @param titleNum 标题占用行数
* @return 转换后集合
*/
public List<T> importExcel(InputStream is, int titleNum) throws Exception
public List<T> importExcel(InputStream is, int titleNum)
{
return importExcel(StringUtils.EMPTY, is, titleNum);
List<T> list = null;
try
{
list = importExcel(StringUtils.EMPTY, is, titleNum);
}
catch (Exception e)
{
log.error("导入Excel异常{}", e.getMessage());
throw new UtilException(e.getMessage());
}
finally
{
IOUtils.closeQuietly(is);
}
return list;
}
/**
@ -325,7 +357,7 @@ public class ExcelUtil<T>
throw new IOException("文件sheet不存在");
}
boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
Map<String, PictureData> pictures;
Map<String, List<PictureData>> pictures = null;
if (isXSSFWorkbook)
{
pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
@ -336,7 +368,6 @@ public class ExcelUtil<T>
}
// 获取最后一个非空行的行下标比如总行数为n则返回的为n-1
int rows = sheet.getLastRowNum();
if (rows > 0)
{
// 定义一个map用于存放excel列的序号和field.
@ -392,7 +423,7 @@ public class ExcelUtil<T>
if (String.class == fieldType)
{
String s = Convert.toStr(val);
if (StringUtils.endsWith(s, ".0"))
if (s.matches("^\\d+\\.0$"))
{
val = StringUtils.substringBefore(s, ".0");
}
@ -451,30 +482,34 @@ public class ExcelUtil<T>
{
propertyName = field.getName() + "." + attr.targetAttr();
}
else if (StringUtils.isNotEmpty(attr.readConverterExp()))
if (StringUtils.isNotEmpty(attr.readConverterExp()))
{
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
}
else if (StringUtils.isNotEmpty(attr.dictType()))
{
val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
if (!sysDictMap.containsKey(attr.dictType() + val))
{
String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
sysDictMap.put(attr.dictType() + val, dictValue);
}
val = sysDictMap.get(attr.dictType() + val);
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
val = dataFormatHandlerAdapter(val, attr);
val = dataFormatHandlerAdapter(val, attr, null);
}
else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
{
PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
if (image == null)
StringBuilder propertyString = new StringBuilder();
List<PictureData> images = pictures.get(row.getRowNum() + "_" + entry.getKey());
for (PictureData picture : images)
{
val = "";
}
else
{
byte[] data = image.getData();
val = FileUtils.writeImportBytes(data);
byte[] data = picture.getData();
String fileName = FileUtils.writeImportBytes(data);
propertyString.append(fileName).append(SEPARATOR);
}
val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
ReflectUtils.invokeSetter(entity, propertyName, val);
}
@ -692,64 +727,91 @@ public class ExcelUtil<T>
{
int startNo = index * sheetSize;
int endNo = Math.min(startNo + sheetSize, list.size());
int rowNo = (1 + rownum) - startNo;
int currentRowNum = rownum + 1; // 从标题行后开始
for (int i = startNo; i < endNo; i++)
{
rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo;
row = sheet.createRow(rowNo);
// 得到导出对象.
row = sheet.createRow(currentRowNum);
T vo = (T) list.get(i);
Collection<?> subList = null;
if (isSubList())
{
if (isSubListValue(vo))
{
subList = getListCellValue(vo);
subMergedLastRowNum = subMergedLastRowNum + subList.size();
}
else
{
subMergedFirstRowNum++;
subMergedLastRowNum++;
}
}
int column = 0;
int maxSubListSize = getCurrentMaxSubListSize(vo);
for (Object[] os : fields)
{
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList))
if (Collection.class.isAssignableFrom(field.getType()))
{
boolean subFirst = false;
for (Object obj : subList)
try
{
if (subFirst)
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
if (subList != null && !subList.isEmpty())
{
rowNo++;
row = sheet.createRow(rowNo);
}
List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);
int subIndex = 0;
for (Object subVo : subList)
{
Row subRow = sheet.getRow(currentRowNum + subIndex);
if (subRow == null)
{
subRow = sheet.createRow(currentRowNum + subIndex);
}
int subColumn = column;
for (Field subField : subFields)
{
if (subField.isAnnotationPresent(Excel.class))
{
subField.setAccessible(true);
Excel attr = subField.getAnnotation(Excel.class);
this.addCell(attr, row, (T) obj, subField, column + subIndex);
Excel subExcel = subField.getAnnotation(Excel.class);
addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
}
subIndex++;
}
subFirst = true;
column += subFields.size();
}
}
catch (Exception e)
{
log.error("填充集合数据失败", e);
}
this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();
}
else
{
this.addCell(excel, row, vo, field, column++);
// 创建单元格并设置值
addCell(excel, row, vo, field, column);
if (maxSubListSize > 1 && excel.needMerge())
{
sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column));
}
column++;
}
}
currentRowNum += maxSubListSize;
}
}
/**
* 获取子列表最大数
*/
private int getCurrentMaxSubListSize(T vo)
{
int maxSubListSize = 1;
for (Object[] os : fields)
{
Field field = (Field) os[0];
if (Collection.class.isAssignableFrom(field.getType()))
{
try
{
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, (Excel) os[1]);
if (subList != null && !subList.isEmpty())
{
maxSubListSize = Math.max(maxSubListSize, subList.size());
}
}
catch (Exception e)
{
log.error("获取集合大小失败", e);
}
}
}
return maxSubListSize;
}
/**
@ -770,6 +832,8 @@ public class ExcelUtil<T>
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBold(true);
style.setFont(titleFont);
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
styles.put("title", style);
style = wb.createCellStyle();
@ -792,6 +856,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);
@ -832,6 +897,9 @@ public class ExcelUtil<T>
headerFont.setBold(true);
headerFont.setColor(excel.headerColor().index);
style.setFont(headerFont);
// 设置表格头单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
headerStyles.put(key, style);
}
}
@ -849,8 +917,37 @@ public class ExcelUtil<T>
Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
for (Object[] os : fields)
{
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor());
if (Collection.class.isAssignableFrom(field.getType()))
{
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
for (Field subField : subFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
annotationDataStyles(styles, subField, subExcel);
}
}
else
{
annotationDataStyles(styles, field, excel);
}
}
return styles;
}
/**
* 根据Excel注解创建表格列样式
*
* @param styles 自定义样式列表
* @param field 属性列信息
* @param excel 注解信息
*/
public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel)
{
String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText());
if (!styles.containsKey(key))
{
CellStyle style = wb.createCellStyle();
@ -866,16 +963,20 @@ public class ExcelUtil<T>
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillForegroundColor(excel.backgroundColor().getIndex());
style.setWrapText(excel.wrapText());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
dataFont.setColor(excel.color().index);
style.setFont(dataFont);
if (ColumnType.TEXT == excel.cellType())
{
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
}
styles.put(key, style);
}
}
return styles;
}
/**
* 创建单元格
@ -891,7 +992,7 @@ public class ExcelUtil<T>
if (isSubList())
{
// 填充默认样式,防止合并单元格样式失效
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
if (attr.needMerge())
{
sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
@ -909,7 +1010,7 @@ public class ExcelUtil<T>
*/
public void setCellVo(Object value, Excel attr, Cell cell)
{
if (ColumnType.STRING == attr.cellType())
if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType())
{
String cellValue = Convert.toStr(value);
// 对于任何以表达式触发字符 =-+@开头的单元格直接使用tab字符作为前缀防止CSV注入。
@ -933,12 +1034,15 @@ public class ExcelUtil<T>
else if (ColumnType.IMAGE == attr.cellType())
{
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
String imagePath = Convert.toStr(value);
if (StringUtils.isNotEmpty(imagePath))
String propertyValue = Convert.toStr(value);
if (StringUtils.isNotEmpty(propertyValue))
{
List<String> imagePaths = StringUtils.str2List(propertyValue, SEPARATOR);
for (String imagePath : imagePaths)
{
byte[] data = ImageUtils.getImage(imagePath);
getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
}
}
}
}
@ -986,17 +1090,28 @@ public class ExcelUtil<T>
// 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
}
if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0)
if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict())
{
if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255)
String[] comboArray = attr.combo();
if (attr.comboReadDict())
{
if (!sysDictMap.containsKey("combo_" + attr.dictType()))
{
String labels = DictUtils.getDictLabels(attr.dictType());
sysDictMap.put("combo_" + attr.dictType(), labels);
}
String val = sysDictMap.get("combo_" + attr.dictType());
comboArray = StringUtils.split(val, DictUtils.SEPARATOR);
}
if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255)
{
// 如果下拉数大于15或字符串长度大于255则使用一个新sheet存储避免生成的模板下拉值获取不到
setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column);
}
else
{
// 提示信息或只能选择不能输入的列内容.
setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column);
}
}
}
@ -1018,10 +1133,12 @@ public class ExcelUtil<T>
cell = row.createCell(column);
if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
{
CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
sheet.addMergedRegion(cellAddress);
if (subMergedLastRowNum >= subMergedFirstRowNum)
{
sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column));
}
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
}
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
// 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr);
@ -1031,6 +1148,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.setCellValue(parseDateToStr(dateFormat, value));
}
else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
@ -1052,7 +1170,7 @@ public class ExcelUtil<T>
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
cell.setCellValue(dataFormatHandlerAdapter(value, attr));
cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));
}
else
{
@ -1169,7 +1287,7 @@ public class ExcelUtil<T>
public static String convertByExp(String propertyValue, String converterExp, String separator)
{
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(",");
String[] convertSource = converterExp.split(SEPARATOR);
for (String item : convertSource)
{
String[] itemArray = item.split("=");
@ -1206,7 +1324,7 @@ public class ExcelUtil<T>
public static String reverseByExp(String propertyValue, String converterExp, String separator)
{
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(",");
String[] convertSource = converterExp.split(SEPARATOR);
for (String item : convertSource)
{
String[] itemArray = item.split("=");
@ -1265,13 +1383,13 @@ public class ExcelUtil<T>
* @param excel 数据注解
* @return
*/
public String dataFormatHandlerAdapter(Object value, Excel excel)
public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell)
{
try
{
Object instance = excel.handler().newInstance();
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class });
value = formatMethod.invoke(instance, value, excel.args());
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
}
catch (Exception e)
{
@ -1320,7 +1438,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();
}
@ -1331,8 +1449,7 @@ public class ExcelUtil<T>
*/
public String encodingFilename(String filename)
{
filename = UUID.randomUUID() + "_" + filename + ".xlsx";
return filename;
return UUID.randomUUID() + "_" + filename + ".xlsx";
}
/**
@ -1362,6 +1479,7 @@ public class ExcelUtil<T>
*/
private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
{
field.setAccessible(true);
Object o = field.get(vo);
if (StringUtils.isNotEmpty(excel.targetAttr()))
{
@ -1421,9 +1539,40 @@ public class ExcelUtil<T>
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
if (StringUtils.isNotEmpty(includeFields))
{
for (Field field : tempFields)
{
if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class))
{
addField(fields, field);
}
}
}
else if (StringUtils.isNotEmpty(excludeFields))
{
for (Field field : tempFields)
{
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
{
addField(fields, field);
}
}
}
else
{
for (Field field : tempFields)
{
addField(fields, field);
}
}
return fields;
}
/**
* 添加字段信息
*/
public void addField(List<Object[]> fields, Field field)
{
// 单注解
if (field.isAnnotationPresent(Excel.class))
@ -1431,7 +1580,6 @@ public class ExcelUtil<T>
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
if (Collection.class.isAssignableFrom(field.getType()))
@ -1449,19 +1597,26 @@ public class ExcelUtil<T>
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels)
{
if (StringUtils.isNotEmpty(includeFields))
{
if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
fields.add(new Object[] { field, attr });
}
}
else
{
if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
}
}
}
}
return fields;
}
/**
* 根据注解获取最大行高
@ -1595,31 +1750,25 @@ public class ExcelUtil<T>
* @param workbook 工作簿对象
* @return Map key:图片单元格索引1_1Stringvalue:图片流PictureData
*/
public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook)
public static Map<String, List<PictureData>> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook)
{
Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
Map<String, List<PictureData>> sheetIndexPicMap = new HashMap<>();
List<HSSFPictureData> pictures = workbook.getAllPictures();
if (!pictures.isEmpty())
if (!pictures.isEmpty() && sheet.getDrawingPatriarch() != null)
{
for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren())
{
HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
if (shape instanceof HSSFPicture)
{
HSSFPicture pic = (HSSFPicture) shape;
int pictureIndex = pic.getPictureIndex() - 1;
HSSFPictureData picData = pictures.get(pictureIndex);
HSSFClientAnchor anchor = (HSSFClientAnchor) pic.getAnchor();
String picIndex = anchor.getRow1() + "_" + anchor.getCol1();
sheetIndexPicMap.put(picIndex, picData);
sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData());
}
}
}
return sheetIndexPicMap;
}
else
{
return sheetIndexPicMap;
}
}
/**
* 获取Excel2007图片
@ -1628,16 +1777,15 @@ public class ExcelUtil<T>
* @param workbook 工作簿对象
* @return Map key:图片单元格索引1_1Stringvalue:图片流PictureData
*/
public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook)
public static Map<String, List<PictureData>> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook)
{
Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
Map<String, List<PictureData>> sheetIndexPicMap = new HashMap<>();
for (POIXMLDocumentPart dr : sheet.getRelations())
{
if (dr instanceof XSSFDrawing)
{
XSSFDrawing drawing = (XSSFDrawing) dr;
List<XSSFShape> shapes = drawing.getShapes();
for (XSSFShape shape : shapes)
for (XSSFShape shape : drawing.getShapes())
{
if (shape instanceof XSSFPicture)
{
@ -1645,7 +1793,7 @@ public class ExcelUtil<T>
XSSFClientAnchor anchor = pic.getPreferredSize();
CTMarker ctMarker = anchor.getFrom();
String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
sheetIndexPicMap.put(picIndex, pic.getPictureData());
sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData());
}
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.utils.spring;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@ -120,7 +121,12 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
Object proxy = AopContext.currentProxy();
if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass())
{
return (T) proxy;
}
return invoker;
}
/**

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -73,8 +74,7 @@ public class DataScopeAspect
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission);
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
}
}
}
@ -92,16 +92,22 @@ public class DataScopeAspect
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
List<String> scopeCustomIds = new ArrayList<String>();
user.getRoles().forEach(role -> {
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
}
});
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
@ -113,9 +119,15 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
if (scopeCustomIds.size() > 1)
{
// 多个自定数据权限使用in查询避免多次拼接。
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
}
else
{
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
}
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
@ -123,9 +135,7 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
@ -142,7 +152,7 @@ public class DataScopeAspect
conditions.add(dataScope);
}
// 多角色情况下,所有角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
// 角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));

View File

@ -18,10 +18,13 @@ import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.filter.PropertyPreExcludeFilter;
import com.ruoyi.common.utils.ExceptionUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
@ -51,7 +54,7 @@ public class LogAspect
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog)
public void doBefore(JoinPoint joinPoint, Log controllerLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
@ -96,12 +99,17 @@ public class LogAspect
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
SysUser currentUser = loginUser.getUser();
if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept()))
{
operLog.setDeptName(currentUser.getDept().getDeptName());
}
}
if (e != null)
{
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
@ -166,8 +174,7 @@ public class LogAspect
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
if (StringUtils.isEmpty(paramsMap)
&& (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
{
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));

View File

@ -6,6 +6,8 @@ import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import com.ruoyi.common.constant.Constants;
/**
* Redis使用FastJson序列化
@ -16,6 +18,8 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz)
@ -43,6 +47,6 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);
return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER);
}
}

View File

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

View File

@ -36,7 +36,7 @@ public class ResourcesConfig implements WebMvcConfigurer
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
/**
@ -55,7 +55,6 @@ public class ResourcesConfig implements WebMvcConfigurer
public CorsFilter corsFilter()
{
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头

View File

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

View File

@ -47,8 +47,9 @@ public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @param request 请求信息
* @param annotation 防重复注解参数
* @return 结果
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);

View File

@ -12,6 +12,7 @@ import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
@ -45,8 +46,8 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success"))));
}
}

View File

@ -7,13 +7,17 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.html.EscapeUtil;
/**
* 全局异常处理器
@ -59,6 +63,33 @@ public class GlobalExceptionHandler
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
/**
* 请求路径中缺少必需的路径变量
*/
@ExceptionHandler(MissingPathVariableException.class)
public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
}
/**
* 请求参数类型不匹配
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
String value = Convert.toStr(e.getValue());
if (StringUtils.isNotEmpty(value))
{
value = EscapeUtil.clean(value);
}
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value));
}
/**
* 拦截未知的运行时异常
*/

View File

@ -3,6 +3,7 @@ package com.ruoyi.framework.web.service;
import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
@ -17,16 +18,6 @@ import com.ruoyi.framework.security.context.PermissionContextHolder;
@Service("ss")
public class PermissionService
{
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ",";
/**
* 验证用户是否具备某权限
*
@ -78,7 +69,7 @@ public class PermissionService
}
PermissionContextHolder.setContext(permissions);
Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(PERMISSION_DELIMETER))
for (String permission : permissions.split(Constants.PERMISSION_DELIMETER))
{
if (permission != null && hasPermissions(authorities, permission))
{
@ -108,7 +99,7 @@ public class PermissionService
for (SysRole sysRole : loginUser.getUser().getRoles())
{
String roleKey = sysRole.getRoleKey();
if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
{
return true;
}
@ -144,7 +135,7 @@ public class PermissionService
{
return false;
}
for (String role : roles.split(ROLE_DELIMETER))
for (String role : roles.split(Constants.ROLE_DELIMETER))
{
if (hasRole(role))
{
@ -163,6 +154,6 @@ public class PermissionService
*/
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
}

View File

@ -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;
@ -115,12 +114,12 @@ public class SysLoginService
{
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
}
redisCache.deleteObject(verifyKey);
if (!code.equalsIgnoreCase(captcha))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
@ -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,15 +6,11 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
/**
@ -60,16 +56,12 @@ public class SysPasswordService
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)));
throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
}
if (!matches(user, password))
{
retryCount = retryCount + 1;
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.count", retryCount)));
redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
throw new UserPasswordNotMatchException();
}

View File

@ -6,8 +6,10 @@ 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.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysRoleService;
@ -67,12 +69,15 @@ public class SysPermissionService
{
// 多角色设置permissions属性以便数据权限匹配权限
for (SysRole role : roles)
{
if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin())
{
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
perms.addAll(rolePerms);
}
}
}
else
{
perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));

View File

@ -10,6 +10,7 @@ import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
@ -76,6 +77,7 @@ public class SysRegisterService
else
{
sysUser.setNickName(username);
sysUser.setPwdUpdateDate(DateUtils.getNowDate());
sysUser.setPassword(SecurityUtils.encryptPassword(password));
boolean regFlag = userService.registerUser(sysUser);
if (!regFlag)

View File

@ -4,6 +4,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -29,6 +31,8 @@ import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class TokenService
{
private static final Logger log = LoggerFactory.getLogger(TokenService.class);
// 令牌自定义标识
@Value("${token.header}")
private String header;
@ -45,7 +49,7 @@ public class TokenService
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
private static final Long MILLIS_MINUTE_TWENTY = 20 * 60 * 1000L;
@Autowired
private RedisCache redisCache;
@ -72,6 +76,7 @@ public class TokenService
}
catch (Exception e)
{
log.error("获取用户信息异常'{}'", e.getMessage());
}
}
return null;
@ -115,20 +120,21 @@ public class TokenService
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
claims.put(Constants.JWT_USERNAME, loginUser.getUsername());
return createToken(claims);
}
/**
* 验证令牌有效期相差不足20分钟自动刷新缓存
*
* @param loginUser
* @param loginUser 登录信息
* @return 令牌
*/
public void verifyToken(LoginUser loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
if (expireTime - currentTime <= MILLIS_MINUTE_TWENTY)
{
refreshToken(loginUser);
}

View File

@ -11,6 +11,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
@ -40,17 +41,17 @@ public class UserDetailsServiceImpl implements UserDetailsService
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", username);
throw new ServiceException("登录用户:" + username + " 不存在");
throw new ServiceException(MessageUtils.message("user.not.exists"));
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
log.info("登录用户:{} 已被删除.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
throw new ServiceException(MessageUtils.message("user.password.delete"));
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
log.info("登录用户:{} 已被停用.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
throw new ServiceException(MessageUtils.message("user.blocked"));
}
passwordService.validate(user);

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.6</version>
<version>3.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -23,18 +23,18 @@
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -21,12 +21,15 @@ public class GenConfig
/** 生成包路径 */
public static String packageName;
/** 自动去除表前缀默认是false */
/** 自动去除表前缀 */
public static boolean autoRemovePre;
/** 表前缀(类名不会包含表前缀) */
/** 表前缀 */
public static String tablePrefix;
/** 是否允许生成文件覆盖到本地(自定义路径) */
public static boolean allowOverwrite;
public static String getAuthor()
{
return author;
@ -70,4 +73,15 @@ public class GenConfig
{
GenConfig.tablePrefix = tablePrefix;
}
public static boolean isAllowOverwrite()
{
return allowOverwrite;
}
@Value("${allowOverwrite}")
public void setAllowOverwrite(boolean allowOverwrite)
{
GenConfig.allowOverwrite = allowOverwrite;
}
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.generator.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -17,12 +18,19 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
import com.ruoyi.generator.config.GenConfig;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.service.IGenTableColumnService;
@ -56,7 +64,7 @@ public class GenController extends BaseController
}
/**
* 修改代码生成业务
* 获取代码生成信息
*/
@PreAuthorize("@ss.hasPermi('tool:gen:query')")
@GetMapping(value = "/{tableId}")
@ -109,10 +117,47 @@ public class GenController extends BaseController
String[] tableNames = Convert.toStrArray(tables);
// 查询表信息
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
genTableService.importGenTable(tableList);
genTableService.importGenTable(tableList, SecurityUtils.getUsername());
return success();
}
/**
* 创建表结构(保存)
*/
@PreAuthorize("@ss.hasRole('admin')")
@Log(title = "创建表", businessType = BusinessType.OTHER)
@PostMapping("/createTable")
public AjaxResult createTableSave(String sql)
{
try
{
SqlUtil.filterKeyword(sql);
List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
List<String> tableNames = new ArrayList<>();
for (SQLStatement sqlStatement : sqlStatements)
{
if (sqlStatement instanceof MySqlCreateTableStatement)
{
MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
if (genTableService.createTable(createTableStatement.toString()))
{
String tableName = createTableStatement.getTableName().replaceAll("`", "");
tableNames.add(tableName);
}
}
}
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()]));
String operName = SecurityUtils.getUsername();
genTableService.importGenTable(tableList, operName);
return AjaxResult.success();
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
return AjaxResult.error("创建表结构异常");
}
}
/**
* 修改保存代码生成业务
*/
@ -169,6 +214,10 @@ public class GenController extends BaseController
@GetMapping("/genCode/{tableName}")
public AjaxResult genCode(@PathVariable("tableName") String tableName)
{
if (!GenConfig.isAllowOverwrite())
{
return AjaxResult.error("【系统预设】不允许生成文件覆盖到本地");
}
genTableService.generatorCode(tableName);
return success();
}

View File

@ -41,6 +41,9 @@ public class GenTable extends BaseEntity
/** 使用的模板crud单表操作 tree树表操作 sub主子表操作 */
private String tplCategory;
/** 前端类型element-ui模版 element-plus模版 */
private String tplWebType;
/** 生成包路径 */
@NotBlank(message = "生成包路径不能为空")
private String packageName;
@ -90,7 +93,7 @@ public class GenTable extends BaseEntity
private String treeName;
/** 上级菜单ID字段 */
private String parentMenuId;
private Long parentMenuId;
/** 上级菜单名称字段 */
private String parentMenuName;
@ -165,6 +168,16 @@ public class GenTable extends BaseEntity
this.tplCategory = tplCategory;
}
public String getTplWebType()
{
return tplWebType;
}
public void setTplWebType(String tplWebType)
{
this.tplWebType = tplWebType;
}
public String getPackageName()
{
return packageName;
@ -305,12 +318,12 @@ public class GenTable extends BaseEntity
this.treeName = treeName;
}
public String getParentMenuId()
public Long getParentMenuId()
{
return parentMenuId;
}
public void setParentMenuId(String parentMenuId)
public void setParentMenuId(Long parentMenuId)
{
this.parentMenuId = parentMenuId;
}

View File

@ -80,4 +80,12 @@ public interface GenTableMapper
* @return 结果
*/
public int deleteGenTableByIds(Long[] ids);
/**
* 创建表
*
* @param sql 表结构
* @return 结果
*/
public int createTable(String sql);
}

View File

@ -27,7 +27,6 @@ import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.text.CharsetKit;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
@ -129,9 +128,9 @@ public class GenTableServiceImpl implements IGenTableService
int row = genTableMapper.updateGenTable(genTable);
if (row > 0)
{
for (GenTableColumn cenTableColumn : genTable.getColumns())
for (GenTableColumn genTableColumn : genTable.getColumns())
{
genTableColumnMapper.updateGenTableColumn(cenTableColumn);
genTableColumnMapper.updateGenTableColumn(genTableColumn);
}
}
}
@ -150,6 +149,18 @@ public class GenTableServiceImpl implements IGenTableService
genTableColumnMapper.deleteGenTableColumnByIds(tableIds);
}
/**
* 创建表
*
* @param sql 创建表语句
* @return 结果
*/
@Override
public boolean createTable(String sql)
{
return genTableMapper.createTable(sql) == 0;
}
/**
* 导入表结构
*
@ -157,9 +168,8 @@ public class GenTableServiceImpl implements IGenTableService
*/
@Override
@Transactional
public void importGenTable(List<GenTable> tableList)
public void importGenTable(List<GenTable> tableList, String operName)
{
String operName = SecurityUtils.getUsername();
try
{
for (GenTable table : tableList)
@ -206,7 +216,7 @@ public class GenTableServiceImpl implements IGenTableService
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());
for (String template : templates)
{
// 渲染模板
@ -254,7 +264,7 @@ public class GenTableServiceImpl implements IGenTableService
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());
for (String template : templates)
{
if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm"))
@ -367,7 +377,7 @@ public class GenTableServiceImpl implements IGenTableService
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());
for (String template : templates)
{
// 渲染模板
@ -414,6 +424,7 @@ public class GenTableServiceImpl implements IGenTableService
{
throw new ServiceException("树名称字段不能为空");
}
}
else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory()))
{
if (StringUtils.isEmpty(genTable.getSubTableName()))
@ -426,7 +437,6 @@ public class GenTableServiceImpl implements IGenTableService
}
}
}
}
/**
* 设置主键列信息
@ -491,7 +501,7 @@ public class GenTableServiceImpl implements IGenTableService
String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
String treeName = paramsObj.getString(GenConstants.TREE_NAME);
String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID);
Long parentMenuId = paramsObj.getLongValue(GenConstants.PARENT_MENU_ID);
String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);
genTable.setTreeCode(treeCode);

View File

@ -66,12 +66,21 @@ public interface IGenTableService
*/
public void deleteGenTableByIds(Long[] tableIds);
/**
* 创建表
*
* @param sql 创建表语句
* @return 结果
*/
public boolean createTable(String sql);
/**
* 导入表结构
*
* @param tableList 导入表列表
* @param operName 操作人员
*/
public void importGenTable(List<GenTable> tableList);
public void importGenTable(List<GenTable> tableList, String operName);
/**
* 预览代码

View File

@ -123,11 +123,17 @@ public class VelocityUtils
/**
* 获取模板信息
*
* @param tplCategory 生成的模板
* @param tplWebType 前端类型
* @return 模板列表
*/
public static List<String> getTemplateList(String tplCategory)
public static List<String> getTemplateList(String tplCategory, String tplWebType)
{
String useWebType = "vm/vue";
if ("element-plus".equals(tplWebType))
{
useWebType = "vm/vue/v3";
}
List<String> templates = new ArrayList<String>();
templates.add("vm/java/domain.java.vm");
templates.add("vm/java/mapper.java.vm");
@ -139,15 +145,15 @@ public class VelocityUtils
templates.add("vm/js/api.js.vm");
if (GenConstants.TPL_CRUD.equals(tplCategory))
{
templates.add("vm/vue/index.vue.vm");
templates.add(useWebType + "/index.vue.vm");
}
else if (GenConstants.TPL_TREE.equals(tplCategory))
{
templates.add("vm/vue/index-tree.vue.vm");
templates.add(useWebType + "/index-tree.vue.vm");
}
else if (GenConstants.TPL_SUB.equals(tplCategory))
{
templates.add("vm/vue/index.vue.vm");
templates.add(useWebType + "/index.vue.vm");
templates.add("vm/java/sub-domain.java.vm");
}
return templates;

View File

@ -8,3 +8,5 @@ gen:
autoRemovePre: false
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_
# 是否允许生成文件覆盖到本地(自定义路径),默认不允许
allowOverwrite: false

View File

@ -40,7 +40,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
select column_name, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type
select column_name, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else '0' end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type
from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName})
order by ordinal_position
</select>

View File

@ -12,6 +12,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="subTableFkName" column="sub_table_fk_name" />
<result property="className" column="class_name" />
<result property="tplCategory" column="tpl_category" />
<result property="tplWebType" column="tpl_web_type" />
<result property="packageName" column="package_name" />
<result property="moduleName" column="module_name" />
<result property="businessName" column="business_name" />
@ -54,7 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectGenTableVo">
select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
</sql>
<select id="selectGenTableList" parameterType="GenTable" resultMap="GenTableResult">
@ -67,10 +68,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
AND date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
AND date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
</where>
</select>
@ -78,7 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_schema = (select database())
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT LIKE 'qrtz\_%' AND table_name NOT LIKE 'gen\_%'
AND table_name NOT IN (select table_name from gen_table)
<if test="tableName != null and tableName != ''">
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
@ -87,17 +88,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
AND date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
AND date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
order by create_time desc
</select>
<select id="selectDbTableListByNames" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
where table_name NOT LIKE 'qrtz\_%' and table_name NOT LIKE 'gen\_%' and table_schema = (select database())
and table_name in
<foreach collection="array" item="name" open="(" separator="," close=")">
#{name}
@ -111,7 +112,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.tpl_web_type, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
@ -119,7 +120,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.tpl_web_type, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
@ -127,7 +128,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.tpl_web_type, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
@ -140,6 +141,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="tableComment != null and tableComment != ''">table_comment,</if>
<if test="className != null and className != ''">class_name,</if>
<if test="tplCategory != null and tplCategory != ''">tpl_category,</if>
<if test="tplWebType != null and tplWebType != ''">tpl_web_type,</if>
<if test="packageName != null and packageName != ''">package_name,</if>
<if test="moduleName != null and moduleName != ''">module_name,</if>
<if test="businessName != null and businessName != ''">business_name,</if>
@ -155,6 +157,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="tableComment != null and tableComment != ''">#{tableComment},</if>
<if test="className != null and className != ''">#{className},</if>
<if test="tplCategory != null and tplCategory != ''">#{tplCategory},</if>
<if test="tplWebType != null and tplWebType != ''">#{tplWebType},</if>
<if test="packageName != null and packageName != ''">#{packageName},</if>
<if test="moduleName != null and moduleName != ''">#{moduleName},</if>
<if test="businessName != null and businessName != ''">#{businessName},</if>
@ -168,6 +171,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
)
</insert>
<update id="createTable">
${sql}
</update>
<update id="updateGenTable" parameterType="GenTable">
update gen_table
<set>
@ -180,6 +187,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="genType != null and genType != ''">gen_type = #{genType},</if>
<if test="genPath != null and genPath != ''">gen_path = #{genPath},</if>
<if test="tplCategory != null and tplCategory != ''">tpl_category = #{tplCategory},</if>
<if test="tplWebType != null and tplWebType != ''">tpl_web_type = #{tplWebType},</if>
<if test="packageName != null and packageName != ''">package_name = #{packageName},</if>
<if test="moduleName != null and moduleName != ''">module_name = #{moduleName},</if>
<if test="businessName != null and businessName != ''">business_name = #{businessName},</if>

View File

@ -71,9 +71,9 @@ public class ${ClassName} extends ${Entity}
{
return $column.javaField;
}
#end
#end
#end
#end
#if($table.sub)
public List<${subClassName}> get${subClassName}List()
{

View File

@ -75,7 +75,7 @@
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
v-hasPermi="['${permissionPrefix}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
@ -144,21 +144,21 @@
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:edit']"
v-hasPermi="['${permissionPrefix}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-plus"
@click="handleAdd(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:add']"
v-hasPermi="['${permissionPrefix}:add']"
>新增</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:remove']"
v-hasPermi="['${permissionPrefix}:remove']"
>删除</el-button>
</template>
</el-table-column>
@ -283,9 +283,9 @@
</template>
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
export default {
name: "${BusinessName}",
@ -346,18 +346,18 @@ export default {
#end
#end
}
};
}
},
created() {
this.getList();
this.getList()
},
methods: {
/** 查询${functionName}列表 */
getList() {
this.loading = true;
this.loading = true
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
this.queryParams.params = {};
this.queryParams.params = {}
#break
#end
#end
@ -365,40 +365,40 @@ export default {
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0]
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1]
}
#end
#end
list${BusinessName}(this.queryParams).then(response => {
this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
this.loading = false;
});
this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}")
this.loading = false
})
},
/** 转换${functionName}数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
delete node.children
}
return {
id: node.${treeCode},
label: node.${treeName},
children: node.children
};
}
},
/** 查询${functionName}下拉树结构 */
getTreeselect() {
list${BusinessName}().then(response => {
this.${businessName}Options = [];
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
data.children = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
this.${businessName}Options.push(data);
});
this.${businessName}Options = []
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] }
data.children = this.handleTree(response.data, "${treeCode}", "${treeParentCode}")
this.${businessName}Options.push(data)
})
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
this.open = false
this.reset()
},
// 表单重置
reset() {
@ -410,61 +410,61 @@ export default {
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
};
this.resetForm("form");
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
this.daterange${AttrName} = [];
this.daterange${AttrName} = []
#end
#end
this.resetForm("queryForm");
this.handleQuery();
this.resetForm("queryForm")
this.handleQuery()
},
/** 新增按钮操作 */
handleAdd(row) {
this.reset();
this.getTreeselect();
this.reset()
this.getTreeselect()
if (row != null && row.${treeCode}) {
this.form.${treeParentCode} = row.${treeCode};
this.form.${treeParentCode} = row.${treeCode}
} else {
this.form.${treeParentCode} = 0;
this.form.${treeParentCode} = 0
}
this.open = true;
this.title = "添加${functionName}";
this.open = true
this.title = "添加${functionName}"
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.refreshTable = false
this.isExpandAll = !this.isExpandAll
this.$nextTick(() => {
this.refreshTable = true;
});
this.refreshTable = true
})
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
this.getTreeselect();
this.reset()
this.getTreeselect()
if (row != null) {
this.form.${treeParentCode} = row.${treeCode};
this.form.${treeParentCode} = row.${treeParentCode}
}
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
this.form = response.data;
this.form = response.data
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.split(",");
this.form.$column.javaField = this.form.${column.javaField}.split(",")
#end
#end
this.open = true;
this.title = "修改${functionName}";
});
this.open = true
this.title = "修改${functionName}"
})
},
/** 提交按钮 */
submitForm() {
@ -472,34 +472,34 @@ export default {
if (valid) {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.join(",");
this.form.$column.javaField = this.form.${column.javaField}.join(",")
#end
#end
if (this.form.${pkColumn.javaField} != null) {
update${BusinessName}(this.form).then(response => {
this.#[[$modal]]#.msgSuccess("修改成功");
this.open = false;
this.getList();
});
this.#[[$modal]]#.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
add${BusinessName}(this.form).then(response => {
this.#[[$modal]]#.msgSuccess("新增成功");
this.open = false;
this.getList();
});
this.#[[$modal]]#.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
});
})
},
/** 删除按钮操作 */
handleDelete(row) {
this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
return del${BusinessName}(row.${pkColumn.javaField});
return del${BusinessName}(row.${pkColumn.javaField})
}).then(() => {
this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
}).catch(() => {});
this.getList()
this.#[[$modal]]#.msgSuccess("删除成功")
}).catch(() => {})
}
}
}
};
</script>

View File

@ -75,7 +75,7 @@
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
v-hasPermi="['${permissionPrefix}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
@ -86,7 +86,7 @@
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['${moduleName}:${businessName}:edit']"
v-hasPermi="['${permissionPrefix}:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
@ -97,7 +97,7 @@
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['${moduleName}:${businessName}:remove']"
v-hasPermi="['${permissionPrefix}:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
@ -107,7 +107,7 @@
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['${moduleName}:${businessName}:export']"
v-hasPermi="['${permissionPrefix}:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
@ -158,14 +158,14 @@
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:edit']"
v-hasPermi="['${permissionPrefix}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:remove']"
v-hasPermi="['${permissionPrefix}:remove']"
>删除</el-button>
</template>
</el-table-column>
@ -353,7 +353,7 @@
</template>
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"
export default {
name: "${BusinessName}",
@ -423,18 +423,18 @@ export default {
#end
#end
}
};
}
},
created() {
this.getList();
this.getList()
},
methods: {
/** 查询${functionName}列表 */
getList() {
this.loading = true;
this.loading = true
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
this.queryParams.params = {};
this.queryParams.params = {}
#break
#end
#end
@ -442,21 +442,21 @@ export default {
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0]
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1]
}
#end
#end
list${BusinessName}(this.queryParams).then(response => {
this.${businessName}List = response.rows;
this.total = response.total;
this.loading = false;
});
this.${businessName}List = response.rows
this.total = response.total
this.loading = false
})
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
this.open = false
this.reset()
},
// 表单重置
reset() {
@ -468,27 +468,27 @@ export default {
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
};
}
#if($table.sub)
this.${subclassName}List = [];
this.${subclassName}List = []
#end
this.resetForm("form");
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
this.daterange${AttrName} = [];
this.daterange${AttrName} = []
#end
#end
this.resetForm("queryForm");
this.handleQuery();
this.resetForm("queryForm")
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
@ -498,27 +498,27 @@ export default {
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加${functionName}";
this.reset()
this.open = true
this.title = "添加${functionName}"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
this.reset()
const ${pkColumn.javaField} = row.${pkColumn.javaField} || this.ids
get${BusinessName}(${pkColumn.javaField}).then(response => {
this.form = response.data;
this.form = response.data
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.split(",");
this.form.$column.javaField = this.form.${column.javaField}.split(",")
#end
#end
#if($table.sub)
this.${subclassName}List = response.data.${subclassName}List;
this.${subclassName}List = response.data.${subclassName}List
#end
this.open = true;
this.title = "修改${functionName}";
});
this.open = true
this.title = "修改${functionName}"
})
},
/** 提交按钮 */
submitForm() {
@ -526,64 +526,64 @@ export default {
if (valid) {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.join(",");
this.form.$column.javaField = this.form.${column.javaField}.join(",")
#end
#end
#if($table.sub)
this.form.${subclassName}List = this.${subclassName}List;
this.form.${subclassName}List = this.${subclassName}List
#end
if (this.form.${pkColumn.javaField} != null) {
update${BusinessName}(this.form).then(response => {
this.#[[$modal]]#.msgSuccess("修改成功");
this.open = false;
this.getList();
});
this.#[[$modal]]#.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
add${BusinessName}(this.form).then(response => {
this.#[[$modal]]#.msgSuccess("新增成功");
this.open = false;
this.getList();
});
this.#[[$modal]]#.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
});
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids;
const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids
this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?').then(function() {
return del${BusinessName}(${pkColumn.javaField}s);
return del${BusinessName}(${pkColumn.javaField}s)
}).then(() => {
this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
}).catch(() => {});
this.getList()
this.#[[$modal]]#.msgSuccess("删除成功")
}).catch(() => {})
},
#if($table.sub)
/** ${subTable.functionName}序号 */
row${subClassName}Index({ row, rowIndex }) {
row.index = rowIndex + 1;
row.index = rowIndex + 1
},
/** ${subTable.functionName}添加按钮操作 */
handleAdd${subClassName}() {
let obj = {};
let obj = {}
#foreach($column in $subTable.columns)
#if($column.pk || $column.javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
obj.$column.javaField = "";
obj.$column.javaField = ""
#end
#end
this.${subclassName}List.push(obj);
this.${subclassName}List.push(obj)
},
/** ${subTable.functionName}删除按钮操作 */
handleDelete${subClassName}() {
if (this.checked${subClassName}.length == 0) {
this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据")
} else {
const ${subclassName}List = this.${subclassName}List;
const checked${subClassName} = this.checked${subClassName};
const ${subclassName}List = this.${subclassName}List
const checked${subClassName} = this.checked${subClassName}
this.${subclassName}List = ${subclassName}List.filter(function(item) {
return checked${subClassName}.indexOf(item.index) == -1
});
})
}
},
/** 复选框选中数据 */
@ -598,5 +598,5 @@ export default {
}, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
}
}
};
}
</script>

View File

@ -73,7 +73,7 @@
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
v-hasPermi="['${permissionPrefix}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
@ -136,9 +136,9 @@
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${permissionPrefix}:edit']">修改</el-button>
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${permissionPrefix}:add']">新增</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${permissionPrefix}:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -271,26 +271,26 @@
</template>
<script setup name="${BusinessName}">
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"
const { proxy } = getCurrentInstance();
const { proxy } = getCurrentInstance()
#if(${dicts} != '')
#set($dictsNoSymbol=$dicts.replace("'", ""))
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
const { ${dictsNoSymbol} } = proxy.useDict(${dicts})
#end
const ${businessName}List = ref([]);
const ${businessName}Options = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const title = ref("");
const isExpandAll = ref(true);
const refreshTable = ref(true);
const ${businessName}List = ref([])
const ${businessName}Options = ref([])
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const title = ref("")
const isExpandAll = ref(true)
const refreshTable = ref(true)
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
const daterange${AttrName} = ref([]);
const daterange${AttrName} = ref([])
#end
#end
@ -318,16 +318,16 @@ const data = reactive({
#end
#end
}
});
})
const { queryParams, form, rules } = toRefs(data);
const { queryParams, form, rules } = toRefs(data)
/** 查询${functionName}列表 */
function getList() {
loading.value = true;
loading.value = true
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
queryParams.value.params = {};
queryParams.value.params = {}
#break
#end
#end
@ -335,31 +335,31 @@ function getList() {
#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}) {
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]
}
#end
#end
list${BusinessName}(queryParams.value).then(response => {
${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
loading.value = false;
});
${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}")
loading.value = false
})
}
/** 查询${functionName}下拉树结构 */
function getTreeselect() {
list${BusinessName}().then(response => {
${businessName}Options.value = [];
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
${businessName}Options.value.push(data);
});
${businessName}Options.value = []
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] }
data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}")
${businessName}Options.value.push(data)
})
}
// 取消按钮
function cancel() {
open.value = false;
reset();
open.value = false
reset()
}
// 表单重置
@ -372,13 +372,13 @@ function reset() {
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
};
proxy.resetForm("${businessName}Ref");
}
proxy.resetForm("${businessName}Ref")
}
/** 搜索按钮操作 */
function handleQuery() {
getList();
getList()
}
/** 重置按钮操作 */
@ -386,52 +386,52 @@ function resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
daterange${AttrName}.value = [];
daterange${AttrName}.value = []
#end
#end
proxy.resetForm("queryRef");
handleQuery();
proxy.resetForm("queryRef")
handleQuery()
}
/** 新增按钮操作 */
function handleAdd(row) {
reset();
getTreeselect();
reset()
getTreeselect()
if (row != null && row.${treeCode}) {
form.value.${treeParentCode} = row.${treeCode};
form.value.${treeParentCode} = row.${treeCode}
} else {
form.value.${treeParentCode} = 0;
form.value.${treeParentCode} = 0
}
open.value = true;
title.value = "添加${functionName}";
open.value = true
title.value = "添加${functionName}"
}
/** 展开/折叠操作 */
function toggleExpandAll() {
refreshTable.value = false;
isExpandAll.value = !isExpandAll.value;
refreshTable.value = false
isExpandAll.value = !isExpandAll.value
nextTick(() => {
refreshTable.value = true;
});
refreshTable.value = true
})
}
/** 修改按钮操作 */
async function handleUpdate(row) {
reset();
await getTreeselect();
reset()
await getTreeselect()
if (row != null) {
form.value.${treeParentCode} = row.${treeCode};
form.value.${treeParentCode} = row.${treeParentCode}
}
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
form.value = response.data;
form.value = response.data
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
form.value.$column.javaField = form.value.${column.javaField}.split(",");
form.value.$column.javaField = form.value.${column.javaField}.split(",")
#end
#end
open.value = true;
title.value = "修改${functionName}";
});
open.value = true
title.value = "修改${functionName}"
})
}
/** 提交按钮 */
@ -440,35 +440,35 @@ function submitForm() {
if (valid) {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
form.value.$column.javaField = form.value.${column.javaField}.join(",");
form.value.$column.javaField = form.value.${column.javaField}.join(",")
#end
#end
if (form.value.${pkColumn.javaField} != null) {
update${BusinessName}(form.value).then(response => {
proxy.#[[$modal]]#.msgSuccess("修改成功");
open.value = false;
getList();
});
proxy.#[[$modal]]#.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
add${BusinessName}(form.value).then(response => {
proxy.#[[$modal]]#.msgSuccess("新增成功");
open.value = false;
getList();
});
proxy.#[[$modal]]#.msgSuccess("新增成功")
open.value = false
getList()
})
}
}
});
})
}
/** 删除按钮操作 */
function handleDelete(row) {
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
return del${BusinessName}(row.${pkColumn.javaField});
return del${BusinessName}(row.${pkColumn.javaField})
}).then(() => {
getList();
proxy.#[[$modal]]#.msgSuccess("删除成功");
}).catch(() => {});
getList()
proxy.#[[$modal]]#.msgSuccess("删除成功")
}).catch(() => {})
}
getList();
getList()
</script>

View File

@ -73,7 +73,7 @@
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
v-hasPermi="['${permissionPrefix}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
@ -83,7 +83,7 @@
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['${moduleName}:${businessName}:edit']"
v-hasPermi="['${permissionPrefix}:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
@ -93,7 +93,7 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['${moduleName}:${businessName}:remove']"
v-hasPermi="['${permissionPrefix}:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
@ -102,7 +102,7 @@
plain
icon="Download"
@click="handleExport"
v-hasPermi="['${moduleName}:${businessName}:export']"
v-hasPermi="['${permissionPrefix}:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
@ -148,8 +148,8 @@
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${permissionPrefix}:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${permissionPrefix}:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -343,33 +343,33 @@
</template>
<script setup name="${BusinessName}">
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"
const { proxy } = getCurrentInstance();
const { proxy } = getCurrentInstance()
#if(${dicts} != '')
#set($dictsNoSymbol=$dicts.replace("'", ""))
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
const { ${dictsNoSymbol} } = proxy.useDict(${dicts})
#end
const ${businessName}List = ref([]);
const ${businessName}List = ref([])
#if($table.sub)
const ${subclassName}List = ref([]);
const ${subclassName}List = ref([])
#end
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
#if($table.sub)
const checked${subClassName} = ref([]);
const checked${subClassName} = ref([])
#end
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
const daterange${AttrName} = ref([]);
const daterange${AttrName} = ref([])
#end
#end
@ -399,16 +399,16 @@ const data = reactive({
#end
#end
}
});
})
const { queryParams, form, rules } = toRefs(data);
const { queryParams, form, rules } = toRefs(data)
/** 查询${functionName}列表 */
function getList() {
loading.value = true;
loading.value = true
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
queryParams.value.params = {};
queryParams.value.params = {}
#break
#end
#end
@ -416,22 +416,22 @@ function getList() {
#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}) {
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]
}
#end
#end
list${BusinessName}(queryParams.value).then(response => {
${businessName}List.value = response.rows;
total.value = response.total;
loading.value = false;
});
${businessName}List.value = response.rows
total.value = response.total
loading.value = false
})
}
// 取消按钮
function cancel() {
open.value = false;
reset();
open.value = false
reset()
}
// 表单重置
@ -444,17 +444,17 @@ function reset() {
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
};
}
#if($table.sub)
${subclassName}List.value = [];
${subclassName}List.value = []
#end
proxy.resetForm("${businessName}Ref");
proxy.resetForm("${businessName}Ref")
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
@ -462,44 +462,44 @@ function resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
daterange${AttrName}.value = [];
daterange${AttrName}.value = []
#end
#end
proxy.resetForm("queryRef");
handleQuery();
proxy.resetForm("queryRef")
handleQuery()
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.${pkColumn.javaField});
single.value = selection.length != 1;
multiple.value = !selection.length;
ids.value = selection.map(item => item.${pkColumn.javaField})
single.value = selection.length != 1
multiple.value = !selection.length
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = "添加${functionName}";
reset()
open.value = true
title.value = "添加${functionName}"
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
reset()
const _${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value
get${BusinessName}(_${pkColumn.javaField}).then(response => {
form.value = response.data;
form.value = response.data
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
form.value.$column.javaField = form.value.${column.javaField}.split(",");
form.value.$column.javaField = form.value.${column.javaField}.split(",")
#end
#end
#if($table.sub)
${subclassName}List.value = response.data.${subclassName}List;
${subclassName}List.value = response.data.${subclassName}List
#end
open.value = true;
title.value = "修改${functionName}";
});
open.value = true
title.value = "修改${functionName}"
})
}
/** 提交按钮 */
@ -508,68 +508,68 @@ function submitForm() {
if (valid) {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
form.value.$column.javaField = form.value.${column.javaField}.join(",");
form.value.$column.javaField = form.value.${column.javaField}.join(",")
#end
#end
#if($table.sub)
form.value.${subclassName}List = ${subclassName}List.value;
form.value.${subclassName}List = ${subclassName}List.value
#end
if (form.value.${pkColumn.javaField} != null) {
update${BusinessName}(form.value).then(response => {
proxy.#[[$modal]]#.msgSuccess("修改成功");
open.value = false;
getList();
});
proxy.#[[$modal]]#.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
add${BusinessName}(form.value).then(response => {
proxy.#[[$modal]]#.msgSuccess("新增成功");
open.value = false;
getList();
});
proxy.#[[$modal]]#.msgSuccess("新增成功")
open.value = false
getList()
})
}
}
});
})
}
/** 删除按钮操作 */
function handleDelete(row) {
const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value;
const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?').then(function() {
return del${BusinessName}(_${pkColumn.javaField}s);
return del${BusinessName}(_${pkColumn.javaField}s)
}).then(() => {
getList();
proxy.#[[$modal]]#.msgSuccess("删除成功");
}).catch(() => {});
getList()
proxy.#[[$modal]]#.msgSuccess("删除成功")
}).catch(() => {})
}
#if($table.sub)
/** ${subTable.functionName}序号 */
function row${subClassName}Index({ row, rowIndex }) {
row.index = rowIndex + 1;
row.index = rowIndex + 1
}
/** ${subTable.functionName}添加按钮操作 */
function handleAdd${subClassName}() {
let obj = {};
let obj = {}
#foreach($column in $subTable.columns)
#if($column.pk || $column.javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
obj.$column.javaField = "";
obj.$column.javaField = ""
#end
#end
${subclassName}List.value.push(obj);
${subclassName}List.value.push(obj)
}
/** ${subTable.functionName}删除按钮操作 */
function handleDelete${subClassName}() {
if (checked${subClassName}.value.length == 0) {
proxy.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
proxy.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据")
} else {
const ${subclassName}s = ${subclassName}List.value;
const checked${subClassName}s = checked${subClassName}.value;
const ${subclassName}s = ${subclassName}List.value
const checked${subClassName}s = checked${subClassName}.value
${subclassName}List.value = ${subclassName}s.filter(function(item) {
return checked${subClassName}s.indexOf(item.index) == -1
});
})
}
}
@ -586,5 +586,5 @@ function handleExport() {
}, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
}
getList();
getList()
</script>

View File

@ -1 +0,0 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>õ<EFBFBD><EFBFBD><EFBFBD>RuoYi-Vue3ǰ<33>ˣ<EFBFBD><CBA3><EFBFBD>ô<EFBFBD><C3B4>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>һ<EFBFBD>´<EFBFBD>Ŀ¼<C4BF><C2BC>ģ<EFBFBD><C4A3>index.vue.vm<76><6D>index-tree.vue.vm<76>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ϼ<EFBFBD>vueĿ¼<C4BF><C2BC>

View File

@ -12,12 +12,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#if($table.sub)
<resultMap id="${ClassName}${subClassName}Result" type="${ClassName}" extends="${ClassName}Result">
<collection property="${subclassName}List" notNullColumn="sub_${subTable.pkColumn.columnName}" javaType="java.util.List" resultMap="${subClassName}Result" />
<collection property="${subclassName}List" ofType="${subClassName}" column="${pkColumn.columnName}" select="select${subClassName}List" />
</resultMap>
<resultMap type="${subClassName}" id="${subClassName}Result">
#foreach ($column in $subTable.columns)
<result property="${column.javaField}" column="sub_${column.columnName}" />
<result property="${column.javaField}" column="${column.columnName}" />
#end
</resultMap>
#end
@ -63,14 +63,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="select${ClassName}Vo"/>
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
#elseif($table.sub)
select#foreach($column in $columns) a.$column.columnName#if($foreach.count != $columns.size()),#end#end,
#foreach($column in $subTable.columns) b.$column.columnName as sub_$column.columnName#if($foreach.count != $subTable.columns.size()),#end#end
from ${tableName} a
left join ${subTableName} b on b.${subTableFkName} = a.${pkColumn.columnName}
where a.${pkColumn.columnName} = #{${pkColumn.javaField}}
select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end
from ${tableName}
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
#end
</select>
#if($table.sub)
<select id="select${subClassName}List" resultMap="${subClassName}Result">
select#foreach ($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end
from ${subTableName}
where ${subTableFkName} = #{${subTableFkName}}
</select>
#end
<insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment) useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
insert into ${tableName}

View File

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

View File

@ -33,7 +33,7 @@
// // 集群配置
// prop.put("org.quartz.jobStore.isClustered", "true");
// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10");
// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
//
// // sqlserver 启用

View File

@ -177,7 +177,7 @@ public class SysJobController extends BaseController
@PreAuthorize("@ss.hasPermi('monitor:job:remove')")
@Log(title = "定时任务", businessType = BusinessType.DELETE)
@DeleteMapping("/{jobIds}")
public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException
{
jobService.deleteJobByIds(jobIds);
return success();

View File

@ -3,7 +3,6 @@ package com.ruoyi.quartz.util;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
@ -31,7 +30,7 @@ public abstract class AbstractQuartzJob implements Job
private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
public void execute(JobExecutionContext context)
{
SysJob sysJob = new SysJob();
BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));

View File

@ -105,7 +105,7 @@ public class JobInvokeUtil
*/
public static List<Object[]> getMethodParams(String invokeTarget)
{
String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
String methodStr = StringUtils.substringBetweenLast(invokeTarget, "(", ")");
if (StringUtils.isEmpty(methodStr))
{
return null;

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

@ -36,12 +36,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND invoke_target like concat('%', #{invokeTarget}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
</where>
order by create_time desc
</select>
<select id="selectJobLogAll" resultMap="SysJobLogResult">

View File

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

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

@ -37,7 +37,7 @@ public interface SysUserPostMapper
/**
* 批量新增用户岗位信息
*
* @param userPostList 用户角色列表
* @param userPostList 用户岗位列表
* @return 结果
*/
public int batchUserPost(List<SysUserPost> userPostList);

View File

@ -85,9 +85,9 @@ public interface ISysRoleService
/**
* 校验角色是否有数据权限
*
* @param roleId 角色id
* @param roleIds 角色id
*/
public void checkRoleDataScope(Long roleId);
public void checkRoleDataScope(Long... roleIds);
/**
* 通过角色ID查询角色使用数量

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

@ -190,7 +190,7 @@ public class SysDeptServiceImpl implements ISysDeptService
@Override
public void checkDeptDataScope(Long deptId)
{
if (!SysUser.isAdmin(SecurityUtils.getUserId()))
if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(deptId))
{
SysDept dept = new SysDept();
dept.setDeptId(deptId);

View File

@ -188,7 +188,7 @@ public class SysMenuServiceImpl implements ISysMenuService
RouterVo children = new RouterVo();
children.setPath(menu.getPath());
children.setComponent(menu.getComponent());
children.setName(StringUtils.capitalize(menu.getPath()));
children.setName(getRouteName(menu.getRouteName(), menu.getPath()));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
children.setQuery(menu.getQuery());
childrenList.add(children);
@ -203,7 +203,7 @@ public class SysMenuServiceImpl implements ISysMenuService
String routerPath = innerLinkReplaceEach(menu.getPath());
children.setPath(routerPath);
children.setComponent(UserConstants.INNER_LINK);
children.setName(StringUtils.capitalize(routerPath));
children.setName(getRouteName(menu.getRouteName(), routerPath));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
childrenList.add(children);
router.setChildren(childrenList);
@ -354,13 +354,25 @@ public class SysMenuServiceImpl implements ISysMenuService
*/
public String getRouteName(SysMenu menu)
{
String routerName = StringUtils.capitalize(menu.getPath());
// 非外链并且是一级目录(类型为目录)
if (isMenuFrame(menu))
{
routerName = StringUtils.EMPTY;
return StringUtils.EMPTY;
}
return routerName;
return getRouteName(menu.getRouteName(), menu.getPath());
}
/**
* 获取路由名称,如没有配置路由名称则取路由地址
*
* @param name 路由名称
* @param path 路由地址
* @return 路由名称(驼峰格式)
*/
public String getRouteName(String name, String path)
{
String routerName = StringUtils.isNotEmpty(name) ? name : path;
return StringUtils.capitalize(routerName);
}
/**
@ -525,7 +537,7 @@ public class SysMenuServiceImpl implements ISysMenuService
*/
public String innerLinkReplaceEach(String path)
{
return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, "." },
new String[] { "", "", "", "/" });
return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":" },
new String[] { "", "", "", "/", "/" });
}
}

View File

@ -192,12 +192,14 @@ public class SysRoleServiceImpl implements ISysRoleService
/**
* 校验角色是否有数据权限
*
* @param roleId 角色id
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long roleId)
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(SecurityUtils.getUserId()))
{
for (Long roleId : roleIds)
{
SysRole role = new SysRole();
role.setRoleId(roleId);
@ -208,6 +210,7 @@ public class SysRoleServiceImpl implements ISysRoleService
}
}
}
}
/**
* 通过角色ID查询角色使用数量

View File

@ -1,6 +1,7 @@
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;
@ -28,6 +29,7 @@ import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.mapper.SysUserPostMapper;
import com.ruoyi.system.mapper.SysUserRoleMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
/**
@ -58,6 +60,9 @@ public class SysUserServiceImpl implements ISysUserService
@Autowired
private ISysConfigService configService;
@Autowired
private ISysDeptService deptService;
@Autowired
protected Validator validator;
@ -322,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());
}
/**
@ -340,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);
}
/**
@ -359,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);
}
/**
@ -489,7 +507,6 @@ public class SysUserServiceImpl implements ISysUserService
int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
String password = configService.selectConfigByKey("sys.user.initPassword");
for (SysUser user : userList)
{
try
@ -499,6 +516,8 @@ public class SysUserServiceImpl implements ISysUserService
if (StringUtils.isNull(u))
{
BeanValidators.validateWithException(validator, user);
deptService.checkDeptDataScope(user.getDeptId());
String password = configService.selectConfigByKey("sys.user.initPassword");
user.setPassword(SecurityUtils.encryptPassword(password));
user.setCreateBy(operName);
userMapper.insertUser(user);
@ -510,7 +529,9 @@ public class SysUserServiceImpl implements ISysUserService
BeanValidators.validateWithException(validator, user);
checkUserAllowed(u);
checkUserDataScope(u.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
user.setUserId(u.getUserId());
user.setDeptId(u.getDeptId());
user.setUpdateBy(operName);
userMapper.updateUser(user);
successNum++;

View File

@ -51,10 +51,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND config_key like concat('%', #{configKey}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
</where>
</select>

View File

@ -41,7 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
order by dict_sort asc
</select>
<select id="selectDictDataByType" parameterType="SysDictData" resultMap="SysDictDataResult">
<select id="selectDictDataByType" parameterType="String" resultMap="SysDictDataResult">
<include refid="selectDictDataVo"/>
where status = '0' and dict_type = #{dictType} order by dict_sort asc
</select>

View File

@ -33,10 +33,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND dict_type like concat('%', #{dictType}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
</where>
</select>

View File

@ -13,6 +13,7 @@
<result property="path" column="path" />
<result property="component" column="component" />
<result property="query" column="query" />
<result property="routeName" column="route_name" />
<result property="isFrame" column="is_frame" />
<result property="isCache" column="is_cache" />
<result property="menuType" column="menu_type" />
@ -28,7 +29,7 @@
</resultMap>
<sql id="selectMenuVo">
select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
from sys_menu
</sql>
@ -49,13 +50,13 @@
</select>
<select id="selectMenuTreeAll" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
order by m.parent_id, m.order_num
</select>
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
@ -74,7 +75,7 @@
</select>
<select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
@ -141,6 +142,7 @@
<if test="path != null and path != ''">path = #{path},</if>
<if test="component != null">component = #{component},</if>
<if test="query != null">`query` = #{query},</if>
<if test="routeName != null">route_name = #{routeName},</if>
<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
@ -164,6 +166,7 @@
<if test="path != null and path != ''">path,</if>
<if test="component != null and component != ''">component,</if>
<if test="query != null and query != ''">`query`,</if>
<if test="routeName != null">route_name,</if>
<if test="isFrame != null and isFrame != ''">is_frame,</if>
<if test="isCache != null and isCache != ''">is_cache,</if>
<if test="menuType != null and menuType != ''">menu_type,</if>
@ -182,6 +185,7 @@
<if test="path != null and path != ''">#{path},</if>
<if test="component != null and component != ''">#{component},</if>
<if test="query != null and query != ''">#{query},</if>
<if test="routeName != null">#{routeName},</if>
<if test="isFrame != null and isFrame != ''">#{isFrame},</if>
<if test="isCache != null and isCache != ''">#{isCache},</if>
<if test="menuType != null and menuType != ''">#{menuType},</if>

View File

@ -37,6 +37,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult">
<include refid="selectOperLogVo"/>
<where>
<if test="operIp != null and operIp != ''">
AND oper_ip like concat('%', #{operIp}, '%')
</if>
<if test="title != null and title != ''">
AND title like concat('%', #{title}, '%')
</if>

View File

@ -46,10 +46,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND r.role_key like concat('%', #{roleKey}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(r.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
and date_format(r.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
and date_format(r.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
<!-- 数据范围过滤 -->
${params.dataScope}

View File

@ -18,12 +18,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="delFlag" column="del_flag" />
<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" column="dept_id" javaType="SysDept" resultMap="deptResult" />
<association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
</resultMap>
@ -47,7 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectUserVo">
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark,
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
from sys_user u
@ -73,10 +74,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
AND date_format(u.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
AND date_format(u.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
<if test="deptId != null and deptId != 0">
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
@ -154,6 +155,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sex != null and sex != ''">sex,</if>
<if test="password != null and password != ''">password,</if>
<if test="status != null and status != ''">status,</if>
<if test="pwdUpdateDate != null">pwd_update_date,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="remark != null and remark != ''">remark,</if>
create_time
@ -168,6 +170,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sex != null and sex != ''">#{sex},</if>
<if test="password != null and password != ''">#{password},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="pwdUpdateDate != null">#{pwdUpdateDate},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="remark != null and remark != ''">#{remark},</if>
sysdate()
@ -177,8 +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="userName != null and userName != ''">user_name = #{userName},</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>
@ -196,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 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">

View File

@ -1,6 +1,8 @@
# 页面标题
VUE_APP_TITLE = 若依管理系统
BABEL_ENV = production
NODE_ENV = production
# 测试环境配置

View File

@ -1,10 +0,0 @@
# 忽略build目录下类型为js的文件的语法检查
build/*.js
# 忽略src/assets目录下文件的语法检查
src/assets
# 忽略public目录下文件的语法检查
public
# 忽略当前目录下为js的文件的语法检查
*.js
# 忽略当前目录下为vue的文件的语法检查
*.vue

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