143 Commits

Author SHA1 Message Date
疯狂的狮子Li
fd5d028e95 fix 修复 有某些无聊人士 对一个demo案例提漏洞 CVE-2025-6925
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2025-07-04 01:10:49 +00:00
疯狂的狮子Li
64100cf1ff !712 发布 5.4.1 小步迭代修复问题
Merge pull request !712 from 疯狂的狮子Li/dev
2025-07-01 01:12:39 +00:00
疯狂的狮子Li
d501e82541 🐳🐳🐳发布 5.4.1 小步迭代修复问题 2025-07-01 09:11:10 +08:00
疯狂的狮子Li
3002585e63 fix 修复 修改数据权限漏改语句 2025-06-30 16:31:58 +08:00
AprilWind
60aca2eef3 !710 update 使用新版数据权限
* update 使用新版数据权限
2025-06-30 01:42:51 +00:00
AprilWind
5baf342478 !708 update 优化代码小改动
* update 优化代码小改动的逻辑性错误
* update 优化代码小改动
2025-06-27 05:34:43 +00:00
疯狂的狮子Li
3f9919fbee update 优化 Redis缓存监控接口 手动归还连接给连接池 提高效率 2025-06-27 10:19:26 +08:00
AprilWind
682d8b0099 !705 update 优化分页写法
* update 优化分页写法
2025-06-27 01:32:55 +00:00
AprilWind
bbabffe191 !702 update 优化参数配置
* update 优化参数配置
2025-06-27 01:10:26 +00:00
AprilWind
6722f2eeed !703 update 优化客户端管理
* update 优化客户端管理
2025-06-27 01:07:11 +00:00
AprilWind
5a9728c868 update 优化流程查询以及多根节点构建树结构 2025-06-26 17:20:04 +08:00
AprilWind
eea96e87d9 update 优化构建多根节点的树结构(支持多个顶级节点) 2025-06-26 15:10:17 +08:00
疯狂的狮子Li
e659740cb8 Revert "update 升级warm-flow1.7.4->1.7.5-m2 优化流程图悬浮窗"
This reverts commit 8f5d60f543.
2025-06-26 07:07:03 +00:00
疯狂的狮子Li
314909a536 fix 修复 excel 备注与必填注解指定下标位置问题 去除下标跟随主要注解顺序 2025-06-26 14:04:36 +08:00
AprilWind
8f5d60f543 update 升级warm-flow1.7.4->1.7.5-m2 优化流程图悬浮窗 2025-06-26 11:26:17 +08:00
疯狂的狮子Li
1ad0d5387b fix 修复 单元格样式覆盖问题 2025-06-26 10:41:06 +08:00
疯狂的狮子Li
770c3bd03e fix 修复 删除错误的注解导致前端时间不显示问题 2025-06-26 09:53:40 +08:00
AprilWind
4b04a4bf09 update 优化全局日期格式转换配置,提升日期参数解析兼容性 2025-06-24 16:10:26 +08:00
疯狂的狮子Li
1598447f6b Revert "update 优化SSE连接"
This reverts commit a8a1db4463.
2025-06-24 06:51:28 +00:00
AprilWind
a8a1db4463 update 优化SSE连接 2025-06-24 14:37:02 +08:00
疯狂的狮子Li
4b47053dcf fix 修复 超时时间单位设置错误 应该是毫秒 2025-06-24 11:42:22 +08:00
疯狂的狮子Li
03054fc1e8 reset 回滚aws-s3版本 有未知问题 2025-06-24 11:24:28 +08:00
疯狂的狮子Li
9dce540a09 fix 修复 sqlserver 字段长度错误 2025-06-23 18:17:41 +08:00
红藕香残玉簟秋
534182deff !698 fix 修复 办理任务时未传参数,导致执行任务无法获取到任务参数的问题
* fix 修复 办理任务时未传参数,导致执行任务无法获取到任务参数的问题
2025-06-23 08:57:30 +00:00
疯狂的狮子Li
4577c45110 fix 修复 升级anyline返回值类型变更导致问题 2025-06-23 14:55:51 +08:00
疯狂的狮子Li
0796791ec9 fix 修复 升级anyline返回值类型变更导致问题 2025-06-23 14:42:06 +08:00
疯狂的狮子Li
84baac0a4f update 优化 sse 超时时间设置为一天 避免连接之后直接关闭浏览器导致连接停滞 2025-06-22 16:39:05 +08:00
疯狂的狮子Li
74d257a610 update 更新工作流sql(小改动) 2025-06-20 11:01:17 +08:00
疯狂的狮子Li
cb8fa6ff9a update QueueUtils 与相关代码标记过期(redisson 新版本已经将队列功能标记删除 一些技术问题无法解决 建议搭建MQ使用) 2025-06-20 10:25:43 +08:00
疯狂的狮子Li
c157012807 update spring-boot 3.4.6 => 3.4.7
update satoken 1.42.0 => 1.44.0
update hutool 5.8.35 => 5.8.38
update redisson 3.45.1 => 3.50.0
update aws-s3 2.28.22 => 2.31.67
update anyline 8.7.2-20250101 => 8.7.2-20250603
update maven-jar-plugin 3.2.2 => 3.4.2
update maven-war-plugin 3.2.2 => 3.4.0
update maven-compiler-plugin 3.11.0 => 3.14.0
update maven-surefire-plugin 3.1.2 => 3.5.3
2025-06-20 10:25:42 +08:00
AprilWind
ffa01bdb3a update 优化类型转换逻辑、删除冗余代码 2025-06-20 10:25:02 +08:00
疯狂的狮子Li
3fa572f0a8 update 优化 去除自动注入日志警告改为默认值 避免一大堆人去定时任务搞什么登录 2025-06-20 09:27:49 +08:00
AprilWind
f868de1b7b fix 临时修复流程图提示信息,后续版本将通过defjson获取流程实例ID 2025-06-19 19:02:22 +08:00
AprilWind
fb785dc17f update 优化工作流的流程图提示信息查询 2025-06-19 18:31:58 +08:00
AprilWind
f3d475438f update 优化工作流的流程图提示信息 2025-06-19 18:13:34 +08:00
AprilWind
d7af327248 update 升级warm-flow1.7.3->1.7.4 支持流程图悬浮窗 2025-06-19 17:16:18 +08:00
疯狂的狮子Li
bd88e27c82 update 优化 加密模块 解密拦截器 将参数一起解密了 防止参数被多次加密不正常 2025-06-17 11:03:20 +08:00
AprilWind
83b6addbba update 优化工作流设计器获取任务执行人默认正常状态 2025-06-17 09:49:31 +08:00
AprilWind
d51f3b9f4e update 优化工作流内置变量表达式解析日志级别 2025-06-17 09:17:49 +08:00
AprilWind
9256432532 update 优化工作流,跳过以 $ 或 # 开头的内置变量表达式解析 2025-06-16 19:55:34 +08:00
疯狂的狮子Li
97d3a31aba fix 修复 snailjob的oracle.sql书写错误 2025-06-16 11:27:49 +08:00
疯狂的狮子Li
d9cc85187a update 删除无用功能 2025-06-13 17:30:01 +08:00
疯狂的狮子Li
2ff2d89b2d update 删除无用功能 2025-06-13 14:24:27 +08:00
疯狂的狮子Li
be2e5059fd update 优化 去除snailjob的jvm参数 默认不限制 2025-06-12 17:39:34 +08:00
疯狂的狮子Li
fad91f01ff update 优化 去除正则校验 无用配置导致问题 2025-06-09 14:54:03 +08:00
AprilWind
8f95374cef update 优化默认部门,不允许删除 2025-06-06 10:06:23 +08:00
AprilWind
7471fa7ee0 update 优化根部门不允许删除以及办理人权限名称回显 2025-06-06 09:45:06 +08:00
疯狂的狮子Li
529f1e5dbb update 优化 租户套餐菜单查询过滤掉 租户管理相关菜单 2025-06-05 18:27:44 +08:00
疯狂的狮子Li
eff131a1ed update 优化 忽略租户表判断改为精确匹配 2025-06-05 16:47:34 +08:00
疯狂的狮子Li
60b0faa3c6 update 优化 将debian换为更新更契合的rockylinux(centos作者写的稳定) 升级jdk版本避免漏洞 2025-06-03 17:11:39 +08:00
疯狂的狮子Li
b2d694b90b reset 修复 satoken异步调用需要手动传递上下文 (跟satoken无关的场景不用处理) 2025-06-03 16:13:05 +08:00
疯狂的狮子Li
fecc564099 fix 修复 部分数据库转移符解析问题导致路由不生效 统一改为使用单斜杠处理 2025-06-03 11:17:17 +08:00
疯狂的狮子Li
297e920179 fix 修复 satoken异步调用需要手动传递上下文 2025-06-03 10:14:13 +08:00
疯狂的狮子Li
ea9379a52f fix 修复 justauth 官方代码bug 2025-05-30 23:40:39 +08:00
dhb52
0b0f2ee8ea !692 fix: 需要传递 version 字段才能启用乐观锁
* fix: 需要传递 version 字段才能启用乐观锁
2025-05-30 08:07:24 +00:00
秋辞未寒
6d2f104a43 fixbug 修复地址解析工具类报错#ICBHUQ 2025-05-29 19:50:50 +08:00
疯狂的狮子Li
7e7d857ba5 Merge remote-tracking branch 'origin/dev' into 5.X 2025-05-29 18:18:20 +08:00
疯狂的狮子Li
2e50e30778 fix 修复 流程数据重复更新 状态被覆盖 无法完成流程问题 2025-05-29 18:15:47 +08:00
疯狂的狮子Li
daf79683b3 update 优化 给测试用户增加菜单权限(可不更新) 2025-05-29 17:22:14 +08:00
疯狂的狮子Li
5849ddc160 update 优化 给测试用户增加菜单权限(可不更新) 2025-05-29 17:05:29 +08:00
疯狂的狮子Li
d22b2a10df update 优化 PermissionService 无实现类也可以启动服务 2025-05-29 16:28:56 +08:00
疯狂的狮子Li
957a4d1fcd fix 修复 监听器 flowParams 为null报错问题 2025-05-29 16:28:56 +08:00
疯狂的狮子Li
c88367939c update 优化 PermissionService 无实现类也可以启动服务 2025-05-29 16:28:09 +08:00
疯狂的狮子Li
a748d0d62c fix 修复 监听器 flowParams 为null报错问题 2025-05-29 15:38:29 +08:00
疯狂的狮子Li
49ef8378fe !691 发布 5.4.0 正式版
Merge pull request !691 from 疯狂的狮子Li/dev
2025-05-29 03:14:59 +00:00
疯狂的狮子Li
cd531f1d39 🐳🐳🐳发布 5.4.0 正式版
fix
2025-05-29 11:13:39 +08:00
疯狂的狮子Li
92f73a4a72 update warmflow 升级到正式版 1.7.3 2025-05-29 10:41:06 +08:00
songgaoshuai
a4e3f7ea5e update 用户查询添加用户昵称条件 2025-05-29 10:39:56 +08:00
疯狂的狮子Li
26b4561a71 update minio 更新到最新 RELEASE.2025-04-22T22-12-26Z minio 最后一个未阉割版本 不能再进行升级 在往上的版本功能被阉割 2025-05-29 10:13:33 +08:00
songgaoshuai
dbe276a33b update 调整任务监听名称 2025-05-29 09:05:36 +08:00
gssong
4ab4e1685c update 优化事件发布 2025-05-28 20:56:27 +08:00
疯狂的狮子Li
aab87d322c update 优化 删除重复执行的代码 2025-05-28 16:56:09 +08:00
AprilWind
79ee168293 update 优化禁止删除默认流程分类 2025-05-28 14:51:43 +08:00
疯狂的狮子Li
10e4b0618c update 优化 调整菜单顺序 相关菜单放到一起 2025-05-28 11:07:23 +08:00
gssong
7c3316e116 update 调整查询流程任务记录 2025-05-27 22:21:10 +08:00
gssong
8460316632 update 调整工作流放行uri 2025-05-27 22:05:55 +08:00
疯狂的狮子Li
5d356aa6c4 update 优化 工作流代码 2025-05-27 17:29:07 +08:00
疯狂的狮子Li
a776d28294 update 优化 删除工作流字体文件(不需要了 改成前端渲染了) 2025-05-27 17:13:32 +08:00
晓华
a002a4e7a1 !690 新增通过前端显示流程图方式和新增办理人转换接口
* feat 新增通过前端显示流程图方式
2025-05-27 09:02:05 +00:00
晓华
79ec850eca !689 update 删除退回任务bo关于驳回的节点的非空校验
* update 删除退回任务bo关于驳回的节点的非空校验
2025-05-26 12:50:03 +00:00
疯狂的狮子Li
d1889c42a3 update 优化 权限获取 增加用户登录了但是查询的loginId是别人的场景 2025-05-26 16:31:10 +08:00
疯狂的狮子Li
a7ea096319 fix 修复 解决通过loginId查询角色和菜单权限 而非当前用户时 报错问题 2025-05-26 15:59:04 +08:00
疯狂的狮子Li
4e3fc7002d update minio 更新到最新 RELEASE.2025-05-24T17-08-30Z 2025-05-26 14:43:37 +08:00
疯狂的狮子Li
1752695751 update mapstruct-plus 1.4.6 => 1.4.8 2025-05-26 14:43:05 +08:00
疯狂的狮子Li
2b89c3f8d0 update 优化 表格增加border 2025-05-26 12:17:33 +08:00
疯狂的狮子Li
6b387b2456 update springboot 3.4.5 => 3.4.6
update springdoc 2.8.5 => 2.8.8
update mybatis-plus 3.5.11 => 3.5.12
update springboot-admin 3.4.5 => 3.4.7
2025-05-26 11:56:05 +08:00
疯狂的狮子Li
ffc971cf92 fix 修复 flowParams 为null导致的报错 2025-05-26 10:07:08 +08:00
gssong
887d5e85d0 add 增加logicflow流程图预览 2025-05-25 11:48:01 +08:00
gssong
8c603ff8d7 update 调整优化监听 2025-05-24 00:16:27 +08:00
疯狂的狮子Li
a0831dda45 fix 修复 请假表单菜单sql 展示状态错误问题
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2025-05-23 10:07:25 +00:00
马铃薯头
336b2e8cc3 !686 feat 新增批量级联删除菜单接口
* feat 新增批量级联删除菜单接口
2025-05-23 10:04:44 +00:00
AprilWind
cea4855f57 update 代码生成ServiceImpl层增加日志注解 2025-05-23 11:51:09 +08:00
不寻俗
9fc043b105 !684 fix: sql补全分号
* fix: sql补全分号
2025-05-22 13:39:05 +00:00
gssong
d729c8ecde update 调整流程监听 2025-05-22 20:34:25 +08:00
AprilWind
b726a91cdb update 新增发号器工具类方法 2025-05-22 17:52:23 +08:00
qxy
05d5d9be2c !683 update 优化 nginx代理snail-job websocket参数, 解决部署到服务器后,查看日志会显示ws连接失败
* update snail-job websocket配置, 解决部署到服务器后,查看日志会显示ws连接失败
2025-05-22 09:49:27 +00:00
疯狂的狮子Li
c40a8b2f0b update 优化 动态路由迁移到菜单管理 2025-05-22 17:41:35 +08:00
AprilWind
8232908b3f update 优化统一请假日期字段格式处理 2025-05-22 17:07:05 +08:00
疯狂的狮子Li
1db0bc83b2 update 优化 工作流创建事件 将状态交给业务方处理 2025-05-21 15:42:42 +08:00
疯狂的狮子Li
74a0ec1ec3 update 优化 字典项校验注解增加分隔符属性 2025-05-20 14:19:21 +08:00
AprilWind
1228e8f3ea update 统一校验器名称风格 2025-05-20 14:03:11 +08:00
AprilWind
737838d92f add 新增自定义字典值校验器 2025-05-20 12:31:49 +08:00
AprilWind
c054029cfc update snailjob 1.4.0 => 1.5.0 2025-05-19 21:23:45 +08:00
疯狂的狮子Li
62bbd78033 add 增加 成员项目地址 2025-05-19 17:09:38 +08:00
疯狂的狮子Li
82a5ed632f add 增加 成员项目地址 2025-05-19 16:06:17 +08:00
AprilWind
52ddccba3e update 升级JustAuth的钉钉和微信第三方登录 2025-05-19 15:13:12 +08:00
AprilWind
1a12aecd49 docs 优化工作流自定义条件注解注释 2025-05-19 12:27:56 +08:00
songgaoshuai
777ae645c5 update 手动合并冲突 优化 工作流模块下一个节点指定办理人、角色和部门转具体用户、抄送人和消息推送,改到通过全局分派监听器和完成监听器处理
update 修复退回申请人无法发送消息问题
2025-05-19 11:31:01 +08:00
gssong
21c87eee9a update 升级warm-flow1.7.0->1.7.2 2025-05-16 20:29:30 +08:00
疯狂的狮子Li
0c8ac12e4d fix 修复 类名书写错误 2025-05-15 17:48:33 +08:00
疯狂的狮子Li
cf871d9387 update 优化 mysql建议版本升级到8.0.42 2025-05-14 18:08:58 +08:00
疯狂的狮子Li
90fb26fbf1 update 优化 redis建议版本升级到7.2.8 2025-05-14 18:01:46 +08:00
疯狂的狮子Li
fdfca0b33a update 优化 代码写法 2025-05-12 18:28:24 +08:00
疯狂的狮子Li
facd3e351f fix 修复 重构导致的问题 2025-05-12 18:22:11 +08:00
AprilWind
a4ad56f0eb fix 继续修复查询办理人错误使用 2025-05-12 18:18:10 +08:00
AprilWind
b9e5914bab fix 修复查询办理人错误使用 2025-05-12 18:11:32 +08:00
AprilWind
553fca28a2 update 优化假分页方法 2025-05-12 16:51:58 +08:00
疯狂的狮子Li
97caabe0a2 update 优化 update sql 书写错误 2025-05-12 13:01:55 +08:00
疯狂的狮子Li
122f2770b2 update 优化 !pr678 代码结构 2025-05-12 12:56:24 +08:00
疯狂的狮子Li
748c95b30f update 优化 增加 其他数据库的升级sql 2025-05-12 10:59:12 +08:00
疯狂的狮子Li
e0672fc753 update 优化 增加对接gitea对接前端的pr地址 2025-05-12 10:12:20 +08:00
lcry
5a1523564b !677 add 新增 对接 gitea 三方单点登录
* add 新增 对接 gitea 三方单点登录
2025-05-12 02:07:46 +00:00
AprilWind
0c2fe34d92 !678 add 新增自定义 Date 类型反序列化处理器(支持多种格式)
* add 新增自定义 Date 类型反序列化处理器(支持多种格式)
2025-05-12 02:04:43 +00:00
疯狂的狮子Li
2dde42168f update 更新 readme 增加新成员项目 2025-05-12 09:33:32 +08:00
AprilWind
a5c2093c76 docs 修正验证码注释 2025-05-11 18:12:49 +08:00
AprilWind
ea74803ccc add 新增请求体读取异常处理 2025-05-11 17:57:42 +08:00
疯狂的狮子Li
57dd6831d3 !664 发布 5.3.1 正式版
Merge pull request !664 from 疯狂的狮子Li/dev
2025-03-27 02:54:00 +00:00
疯狂的狮子Li
8aa60abb1f !663 回退 'Pull Request !662 : 发布 5.3.1 正式版'
* 回退 'Pull Request !662 : 发布 5.3.1 正式版'
2025-03-27 02:53:23 +00:00
疯狂的狮子Li
7a9f51fc7a !662 发布 5.3.1 正式版
* 🐳发布 5.3.1 正式版
* update 优化 删除无用配置
* fix 修复 excel模板导出数据被覆盖的问题
* update 优化 统一用户密码校验长度
* update mybatis-plus 3.5.10.1 => 3.5.11
* fix 修复 跨域未设置请求头问题(cloud版本不需要 vue版本需要)
2025-03-27 02:51:57 +00:00
疯狂的狮子Li
159e30c982 !661 发布 5.3.1-BETA2 公测版本
Merge pull request !661 from 疯狂的狮子Li/dev
2025-03-21 07:25:25 +00:00
疯狂的狮子Li
7334d91d6b !652 发布 5.3.1-BETA 公测版本
Merge pull request !652 from 疯狂的狮子Li/dev
2025-03-13 05:27:36 +00:00
疯狂的狮子Li
95c01301f6 !644 同步修复一些问题
Merge pull request !644 from 疯狂的狮子Li/dev
2025-02-07 06:19:28 +00:00
疯狂的狮子Li
296466fa13 !640 发布 5.3.0 新春版 祝大家新年快乐
Merge pull request !640 from 疯狂的狮子Li/dev
2025-01-24 05:08:28 +00:00
疯狂的狮子Li
3c8d864b5f !639 发布 5.3.0-BETA 公测版本
Merge pull request !639 from 疯狂的狮子Li/dev
2025-01-20 03:35:45 +00:00
疯狂的狮子Li
ea50a57602 update 优化 xss包装器 Parameter 处理 兼容某些容器不允许改参数的情况 2024-11-21 10:17:34 +08:00
疯狂的狮子Li
7e14b98676 reset 回滚错误修改
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2024-10-28 09:46:28 +00:00
疯狂的狮子Li
015b406001 !591 发布 5.2.3 正式版
Merge pull request !591 from 疯狂的狮子Li/dev
2024-10-25 03:09:23 +00:00
疯狂的狮子Li
098d3347a0 !577 发布 5.2.2 正式版 安全性提升
Merge pull request !577 from 疯狂的狮子Li/dev
2024-08-26 03:43:59 +00:00
疯狂的狮子Li
08d4493994 update 优化 bug 模板 2024-07-15 15:19:22 +08:00
疯狂的狮子Li
367d739e2d Merge remote-tracking branch 'origin/5.X' into 5.X 2024-07-09 16:38:43 +08:00
疯狂的狮子Li
d6688a367d !562 ♥️发布 5.2.1 正式版本
Merge pull request !562 from 疯狂的狮子Li/dev
2024-07-09 02:42:40 +00:00
疯狂的狮子Li
0b331796e2 !551 ♥️发布 5.2.0 正式版本
Merge pull request !551 from 疯狂的狮子Li/dev
2024-06-20 02:10:15 +00:00
疯狂的狮子Li
456620b638 !549 ♥️发布 5.2.0-BETA2 公测版本
Merge pull request !549 from 疯狂的狮子Li/dev
2024-06-06 03:13:46 +00:00
131 changed files with 2108 additions and 1057 deletions

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.1" /> <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.4.1" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings> </settings>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-server:5.3.1" /> <option name="imageTag" value="ruoyi/ruoyi-server:5.4.1" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings> </settings>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.1" /> <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.4.1" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
</settings> </settings>

View File

@@ -10,7 +10,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br> <br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.3.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.4.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]() [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
@@ -23,7 +23,9 @@
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br> > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) > 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)<br>
> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) > 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网)

33
pom.xml
View File

@@ -13,33 +13,32 @@
<description>Dromara RuoYi-Vue-Plus多租户管理系统</description> <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
<properties> <properties>
<revision>5.3.1</revision> <revision>5.4.1</revision>
<spring-boot.version>3.4.5</spring-boot.version> <spring-boot.version>3.4.7</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version> <java.version>17</java.version>
<mybatis.version>3.5.16</mybatis.version> <mybatis.version>3.5.16</mybatis.version>
<springdoc.version>2.8.5</springdoc.version> <springdoc.version>2.8.8</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version> <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fastexcel.version>1.2.0</fastexcel.version> <fastexcel.version>1.2.0</fastexcel.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
<satoken.version>1.42.0</satoken.version> <satoken.version>1.44.0</satoken.version>
<mybatis-plus.version>3.5.11</mybatis-plus.version> <mybatis-plus.version>3.5.12</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.35</hutool.version> <hutool.version>5.8.38</hutool.version>
<spring-boot-admin.version>3.4.5</spring-boot-admin.version> <spring-boot-admin.version>3.4.7</spring-boot-admin.version>
<redisson.version>3.45.1</redisson.version> <redisson.version>3.50.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version> <lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version> <dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.4.0</snailjob.version> <snailjob.version>1.5.0</snailjob.version>
<mapstruct-plus.version>1.4.6</mapstruct-plus.version> <mapstruct-plus.version>1.4.8</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.36</lombok.version> <lombok.version>1.18.36</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version> <bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version> <justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 --> <!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version> <ip2region.version>2.7.0</ip2region.version>
<!-- OSS 配置 --> <!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version> <aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 --> <!-- SMS 配置 -->
@@ -47,15 +46,15 @@
<!-- 限制框架中的fastjson版本 --> <!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version> <fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 --> <!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250101</anyline.version> <anyline.version>8.7.2-20250603</anyline.version>
<!-- 工作流配置 --> <!-- 工作流配置 -->
<warm-flow.version>1.7.0</warm-flow.version> <warm-flow.version>1.7.4</warm-flow.version>
<!-- 插件版本 --> <!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
<maven-war-plugin.version>3.2.2</maven-war-plugin.version> <maven-war-plugin.version>3.4.0</maven-war-plugin.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version> <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<!-- 打包默认跳过测试 --> <!-- 打包默认跳过测试 -->
<skipTests>true</skipTests> <skipTests>true</skipTests>

View File

@@ -1,6 +1,6 @@
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds FROM bellsoft/liberica-openjdk-rocky:17.0.15-cds
#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds #FROM bellsoft/liberica-openjdk-rocky:21.0.7-cds
#FROM findepi/graalvm:java17-native #FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li" LABEL maintainer="Lion Li"
@@ -18,8 +18,6 @@ EXPOSE ${SERVER_PORT}
EXPOSE ${SNAIL_PORT} EXPOSE ${SNAIL_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar ADD ./target/ruoyi-admin.jar ./app.jar
# 工作流字体文件
ADD ./zhFonts/ /usr/share/fonts/zhFonts/
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]

View File

@@ -158,6 +158,6 @@ public class UserActionListener implements SaTokenListener {
* 每次Token续期时触发 * 每次Token续期时触发
*/ */
@Override @Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { public void doRenewTimeout(String loginType, Object loginId, String tokenValue, long timeout) {
} }
} }

View File

@@ -3,10 +3,7 @@ package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthResponse;
@@ -68,15 +65,6 @@ public class SocialAuthStrategy implements IAuthStrategy {
throw new ServiceException(response.getMsg()); throw new ServiceException(response.getMsg());
} }
AuthUser authUserData = response.getData(); AuthUser authUserData = response.getData();
if ("GITEE".equals(authUserData.getSource())) {
// 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
.executeAsync();
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
.executeAsync();
}
List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid()); List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {

View File

@@ -263,3 +263,10 @@ justauth:
client-id: 10**********6 client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea

View File

@@ -265,3 +265,10 @@ justauth:
client-id: 10**********6 client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea

View File

@@ -21,8 +21,8 @@ server:
worker: 256 worker: 256
captcha: captcha:
# 是否启用验证码校验
enable: true enable: true
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证 # 验证码类型 math 数组计算 char 字符验证
type: MATH type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
@@ -110,7 +110,7 @@ security:
- /error - /error
- /*/api-docs - /*/api-docs
- /*/api-docs/** - /*/api-docs/**
- /warm-flow-ui/token-name - /warm-flow-ui/config
# 多租户配置 # 多租户配置
tenant: tenant:
@@ -183,7 +183,7 @@ springdoc:
# 描述 # 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本 # 版本
version: '版本号: ${ruoyi.version}' version: '版本号: ${project.version}'
# 作者信息 # 作者信息
contact: contact:
name: Lion Li name: Lion Li

View File

@@ -1 +0,0 @@
3f2ee348-0303-40ca-bf03-03f48d2d2141

Binary file not shown.

View File

@@ -1,4 +0,0 @@
3
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r

View File

@@ -1,4 +0,0 @@
3
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r

View File

@@ -14,7 +14,7 @@
</description> </description>
<properties> <properties>
<revision>5.3.1</revision> <revision>5.4.1</revision>
</properties> </properties>
<dependencyManagement> <dependencyManagement>

View File

@@ -77,4 +77,9 @@ public interface SystemConstants {
*/ */
String ROOT_DEPT_ANCESTORS = "0"; String ROOT_DEPT_ANCESTORS = "0";
/**
* 默认部门 ID
*/
Long DEFAULT_DEPT_ID = 100L;
} }

View File

@@ -6,12 +6,12 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 流程创建任务监听 * 流程任务监听
* *
* @author may * @author may
*/ */
@Data @Data
public class ProcessCreateTaskEvent implements Serializable { public class ProcessTaskEvent implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -51,4 +51,9 @@ public class ProcessCreateTaskEvent implements Serializable {
*/ */
private String businessId; private String businessId;
/**
* 流程状态
*/
private String status;
} }

View File

@@ -0,0 +1,28 @@
package org.dromara.common.core.service;
import java.util.Set;
/**
* 用户权限处理
*
* @author Lion Li
*/
public interface PermissionService {
/**
* 获取角色数据权限
*
* @param userId 用户id
* @return 角色权限信息
*/
Set<String> getRolePermission(Long userId);
/**
* 获取菜单数据权限
*
* @param userId 用户id
* @return 菜单权限信息
*/
Set<String> getMenuPermission(Long userId);
}

View File

@@ -10,6 +10,8 @@ import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.core.utils.reflect.ReflectUtils;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -60,6 +62,31 @@ public class TreeBuildUtils extends TreeUtil {
return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser); return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
} }
/**
* 构建多根节点的树结构(支持多个顶级节点)
*
* @param list 原始数据列表
* @param getId 获取节点 ID 的方法引用例如node -> node.getId()
* @param getParentId 获取节点父级 ID 的方法引用例如node -> node.getParentId()
* @param parser 树节点属性映射器,用于将原始节点 T 转为 Tree 节点
* @param <T> 原始数据类型如实体类、DTO 等)
* @param <K> 节点 ID 类型(如 Long、String
* @return 构建完成的树形结构(可能包含多个顶级根节点)
*/
public static <T, K> List<Tree<K>> buildMultiRoot(List<T> list, Function<T, K> getId, Function<T, K> getParentId, NodeParser<T, K> parser) {
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
Set<K> rootParentIds = StreamUtils.toSet(list, getParentId);
rootParentIds.removeAll(StreamUtils.toSet(list, getId));
// 构建每一个根 parentId 下的树,并合并成最终结果列表
return rootParentIds.stream()
.flatMap(rootParentId -> TreeUtil.build(list, rootParentId, parser).stream())
.collect(Collectors.toList());
}
/** /**
* 获取节点列表中所有节点的叶子节点 * 获取节点列表中所有节点的叶子节点
* *

View File

@@ -16,28 +16,55 @@ import org.dromara.common.core.utils.StringUtils;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressUtils { public class AddressUtils {
// 未知IP
public static final String UNKNOWN_IP = "XX XX";
// 内网地址
public static final String LOCAL_ADDRESS = "内网IP";
// 未知地址 // 未知地址
public static final String UNKNOWN = "XX XX"; public static final String UNKNOWN_ADDRESS = "未知";
public static String getRealAddressByIP(String ip) { public static String getRealAddressByIP(String ip) {
// 处理空串并过滤HTML标签 // 处理空串并过滤HTML标签
ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,""));
boolean isIPv6 = NetUtils.isIPv6(ip); // 判断是否为IPv4
// 判断是否为IPv4或IPv6如果不是则返回未知地址 if (NetUtils.isIPv4(ip)) {
if (!NetUtils.isIPv4(ip) && !isIPv6) { return resolverIPv4Region(ip);
return UNKNOWN;
} }
// 判断是否为IPv6
if (NetUtils.isIPv6(ip)) {
return resolverIPv6Region(ip);
}
// 如果不是IPv4或IPv6则返回未知IP
return UNKNOWN_IP;
}
/**
* 根据IPv4地址查询IP归属行政区域
* @param ip ipv4地址
* @return 归属行政区域
*/
private static String resolverIPv4Region(String ip){
// 内网不查询 // 内网不查询
if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) { if (NetUtils.isInnerIP(ip)) {
return "内网IP"; return LOCAL_ADDRESS;
}
// 不支持IPv6不再进行没有必要的IP地址信息的解析直接返回
if (isIPv6) {
log.warn("ip2region不支持IPV6地址解析{}", ip);
// 如有需要可自行实现IPv6地址信息解析逻辑并在这里返回
return "未知";
} }
return RegionUtils.getCityInfo(ip); return RegionUtils.getCityInfo(ip);
} }
/**
* 根据IPv6地址查询IP归属行政区域
* @param ip ipv6地址
* @return 归属行政区域
*/
private static String resolverIPv6Region(String ip){
// 内网不查询
if (NetUtils.isInnerIPv6(ip)) {
return LOCAL_ADDRESS;
}
log.warn("ip2region不支持IPV6地址解析{}", ip);
// 不支持IPv6不再进行没有必要的IP地址信息的解析直接返回
// 如有需要可自行实现IPv6地址信息解析逻辑并在这里返回
return UNKNOWN_ADDRESS;
}
} }

View File

@@ -0,0 +1,40 @@
package org.dromara.common.core.validate.dicts;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典项校验注解
*
* @author AprilWind
*/
@Constraint(validatedBy = DictPatternValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictPattern {
/**
* 字典类型,如 "sys_user_sex"
*/
String dictType();
/**
* 分隔符
*/
String separator();
/**
* 默认校验失败提示信息
*/
String message() default "字典值无效";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,55 @@
package org.dromara.common.core.validate.dicts;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
/**
* 自定义字典值校验器
*
* @author AprilWind
*/
public class DictPatternValidator implements ConstraintValidator<DictPattern, String> {
/**
* 字典类型
*/
private String dictType;
/**
* 分隔符
*/
private String separator = ",";
/**
* 初始化校验器,提取注解上的字典类型
*
* @param annotation 注解实例
*/
@Override
public void initialize(DictPattern annotation) {
this.dictType = annotation.dictType();
if (StringUtils.isNotBlank(annotation.separator())) {
this.separator = annotation.separator();
}
}
/**
* 校验字段值是否为指定字典类型中的合法值
*
* @param value 被校验的字段值
* @param context 校验上下文(可用于构建错误信息)
* @return true 表示校验通过合法字典值false 表示不通过
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isBlank(dictType) || StringUtils.isBlank(value)) {
return false;
}
String dictLabel = SpringUtils.getBean(DictService.class).getDictLabel(dictType, value, separator);
return StringUtils.isNotBlank(dictLabel);
}
}

View File

@@ -1,37 +1,37 @@
package org.dromara.common.core.validate.enumd; package org.dromara.common.core.validate.enumd;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.core.utils.reflect.ReflectUtils;
/** /**
* 自定义枚举校验注解实现 * 自定义枚举校验注解实现
* *
* @author 秋辞未寒 * @author 秋辞未寒
* @date 2024-12-09 * @date 2024-12-09
*/ */
public class EnumPatternValidator implements ConstraintValidator<EnumPattern, String> { public class EnumPatternValidator implements ConstraintValidator<EnumPattern, String> {
private EnumPattern annotation;; private EnumPattern annotation;
@Override @Override
public void initialize(EnumPattern annotation) { public void initialize(EnumPattern annotation) {
ConstraintValidator.super.initialize(annotation); ConstraintValidator.super.initialize(annotation);
this.annotation = annotation; this.annotation = annotation;
} }
@Override @Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (StringUtils.isNotBlank(value)) { if (StringUtils.isNotBlank(value)) {
String fieldName = annotation.fieldName(); String fieldName = annotation.fieldName();
for (Object e : annotation.type().getEnumConstants()) { for (Object e : annotation.type().getEnumConstants()) {
if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) {
return true; return true;
} }
} }
} }
return false; return false;
} }
} }

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*; import org.apache.ibatis.plugin.*;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
@@ -39,12 +40,23 @@ public class MybatisDecryptInterceptor implements Interceptor {
@Override @Override
public Object intercept(Invocation invocation) throws Throwable { public Object intercept(Invocation invocation) throws Throwable {
// 开始进行参数解密
ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
Field parameterHandlerField = resultSetHandler.getClass().getDeclaredField("parameterHandler");
parameterHandlerField.setAccessible(true);
Object target = parameterHandlerField.get(resultSetHandler);
if (target instanceof ParameterHandler parameterHandler) {
Object parameterObject = parameterHandler.getParameterObject();
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
this.decryptHandler(parameterObject);
}
}
// 获取执行mysql执行结果 // 获取执行mysql执行结果
Object result = invocation.proceed(); Object result = invocation.proceed();
if (result == null) { if (result == null) {
return null; return null;
} }
decryptHandler(result); this.decryptHandler(result);
return result; return result;
} }

View File

@@ -6,17 +6,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* 批注 * 批注 此注解仅用于单表头 不支持多层级表头
* @author guzhouyanyu * @author guzhouyanyu
*/ */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface ExcelNotation { public @interface ExcelNotation {
/**
* col index
*/
int index() default -1;
/** /**
* 批注内容 * 批注内容
*/ */

View File

@@ -8,17 +8,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* 是否必填 * 是否必填 此注解仅用于单表头 不支持多层级表头
* @author guzhouyanyu * @author guzhouyanyu
*/ */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface ExcelRequired { public @interface ExcelRequired {
/**
* col index
*/
int index() default -1;
/** /**
* 字体颜色 * 字体颜色
*/ */

View File

@@ -1,6 +1,7 @@
package org.dromara.common.excel.handler; package org.dromara.common.excel.handler;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.metadata.data.DataFormatData; import cn.idev.excel.metadata.data.DataFormatData;
import cn.idev.excel.metadata.data.WriteCellData; import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.util.StyleUtil; import cn.idev.excel.util.StyleUtil;
@@ -13,7 +14,6 @@ import cn.idev.excel.write.metadata.style.WriteFont;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.ExcelNotation; import org.dromara.common.excel.annotation.ExcelNotation;
import org.dromara.common.excel.annotation.ExcelRequired; import org.dromara.common.excel.annotation.ExcelRequired;
@@ -31,12 +31,12 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
/** /**
* 批注 * 批注
*/ */
private final Map<Integer, String> notationMap; private final Map<String, String> notationMap;
/** /**
* 头列字体颜色 * 头列字体颜色
*/ */
private final Map<Integer, Short> headColumnMap; private final Map<String, Short> headColumnMap;
public DataWriteHandler(Class<?> clazz) { public DataWriteHandler(Class<?> clazz) {
@@ -49,15 +49,16 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) { if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) {
return; return;
} }
// 第一行
WriteCellData<?> cellData = context.getFirstCellData(); WriteCellData<?> cellData = context.getFirstCellData();
// 第一个格子
WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
DataFormatData dataFormatData = new DataFormatData();
// 单元格设置为文本格式
dataFormatData.setIndex((short) 49);
writeCellStyle.setDataFormatData(dataFormatData);
if (context.getHead()) { if (context.getHead()) {
DataFormatData dataFormatData = new DataFormatData();
// 单元格设置为文本格式
dataFormatData.setIndex((short) 49);
writeCellStyle.setDataFormatData(dataFormatData);
Cell cell = context.getCell(); Cell cell = context.getCell();
WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder(); WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
Sheet sheet = writeSheetHolder.getSheet(); Sheet sheet = writeSheetHolder.getSheet();
@@ -67,17 +68,17 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
WriteFont headWriteFont = new WriteFont(); WriteFont headWriteFont = new WriteFont();
// 加粗 // 加粗
headWriteFont.setBold(true); headWriteFont.setBold(true);
if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getColumnIndex())) { if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getStringCellValue())) {
// 设置字体颜色 // 设置字体颜色
headWriteFont.setColor(headColumnMap.get(cell.getColumnIndex())); headWriteFont.setColor(headColumnMap.get(cell.getStringCellValue()));
} }
writeCellStyle.setWriteFont(headWriteFont); writeCellStyle.setWriteFont(headWriteFont);
CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle); CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle);
cell.setCellStyle(cellStyle); cell.setCellStyle(cellStyle);
if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getColumnIndex())) { if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getStringCellValue())) {
// 批注内容 // 批注内容
String notationContext = notationMap.get(cell.getColumnIndex()); String notationContext = notationMap.get(cell.getStringCellValue());
// 创建绘图对象 // 创建绘图对象
Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5)); Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5));
comment.setString(new XSSFRichTextString(notationContext)); comment.setString(new XSSFRichTextString(notationContext));
@@ -89,23 +90,16 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
/** /**
* 获取必填列 * 获取必填列
*/ */
private static Map<Integer, Short> getRequiredMap(Class<?> clazz) { private static Map<String, Short> getRequiredMap(Class<?> clazz) {
Map<Integer, Short> requiredMap = new HashMap<>(); Map<String, Short> requiredMap = new HashMap<>();
Field[] fields = clazz.getDeclaredFields(); Field[] fields = clazz.getDeclaredFields();
// 检查 fields 数组是否为空 for (Field field : fields) {
if (fields.length == 0) {
return requiredMap;
}
Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName()));
for (int i = 0; i < filteredFields.length; i++) {
Field field = filteredFields[i];
if (!field.isAnnotationPresent(ExcelRequired.class)) { if (!field.isAnnotationPresent(ExcelRequired.class)) {
continue; continue;
} }
ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class); ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class);
int columnIndex = excelRequired.index() == -1 ? i : excelRequired.index(); ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
requiredMap.put(columnIndex, excelRequired.fontColor().getIndex()); requiredMap.put(excelProperty.value()[0], excelRequired.fontColor().getIndex());
} }
return requiredMap; return requiredMap;
} }
@@ -113,22 +107,16 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
/** /**
* 获取批注 * 获取批注
*/ */
private static Map<Integer, String> getNotationMap(Class<?> clazz) { private static Map<String, String> getNotationMap(Class<?> clazz) {
Map<Integer, String> notationMap = new HashMap<>(); Map<String, String> notationMap = new HashMap<>();
Field[] fields = clazz.getDeclaredFields(); Field[] fields = clazz.getDeclaredFields();
// 检查 fields 数组是否为空 for (Field field : fields) {
if (fields.length == 0) {
return notationMap;
}
Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName()));
for (int i = 0; i < filteredFields.length; i++) {
Field field = filteredFields[i];
if (!field.isAnnotationPresent(ExcelNotation.class)) { if (!field.isAnnotationPresent(ExcelNotation.class)) {
continue; continue;
} }
ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class); ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class);
int columnIndex = excelNotation.index() == -1 ? i : excelNotation.index(); ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
notationMap.put(columnIndex, excelNotation.value()); notationMap.put(excelProperty.value()[0], excelNotation.value());
} }
return notationMap; return notationMap;
} }

View File

@@ -4,8 +4,9 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.dromara.common.json.handler.BigNumberSerializer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.handler.BigNumberSerializer;
import org.dromara.common.json.handler.CustomDateDeserializer;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
@@ -15,6 +16,7 @@ import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
@@ -38,6 +40,7 @@ public class JacksonConfig {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer());
builder.modules(javaTimeModule); builder.modules(javaTimeModule);
builder.timeZone(TimeZone.getDefault()); builder.timeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置"); log.info("初始化 jackson 配置");

View File

@@ -0,0 +1,31 @@
package org.dromara.common.json.handler;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.util.Date;
/**
* 自定义 Date 类型反序列化处理器(支持多种格式)
*
* @author AprilWind
*/
public class CustomDateDeserializer extends JsonDeserializer<Date> {
/**
* 反序列化逻辑:将字符串转换为 Date 对象
*
* @param p JSON 解析器,用于获取字符串值
* @param ctxt 上下文环境(可用于获取更多配置)
* @return 转换后的 Date 对象,若为空字符串返回 null
* @throws IOException 当字符串格式非法或转换失败时抛出
*/
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return DateUtil.parse(p.getText());
}
}

View File

@@ -1,5 +1,6 @@
package org.dromara.common.mybatis.core.page; package org.dromara.common.mybatis.core.page;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.HttpStatus; import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data; import lombok.Data;
@@ -88,4 +89,19 @@ public class TableDataInfo<T> implements Serializable {
return rspData; return rspData;
} }
/**
* 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页)
*
* @param list 原始数据列表(全部数据)
* @param page 分页参数对象(包含当前页码、每页大小等)
* @return 构造好的分页结果 TableDataInfo<T>
*/
public static <T> TableDataInfo<T> build(List<T> list, IPage<T> page) {
if (CollUtil.isEmpty(list)) {
return TableDataInfo.build();
}
List<T> pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list);
return new TableDataInfo<>(pageList, list.size());
}
} }

View File

@@ -22,6 +22,11 @@ import java.util.Date;
@Slf4j @Slf4j
public class InjectionMetaObjectHandler implements MetaObjectHandler { public class InjectionMetaObjectHandler implements MetaObjectHandler {
/**
* 如果用户不存在默认注入-1代表无用户
*/
private static final Long DEFAULT_USER_ID = -1L;
/** /**
* 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息 * 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息
* *
@@ -45,6 +50,11 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
baseEntity.setCreateBy(userId); baseEntity.setCreateBy(userId);
baseEntity.setUpdateBy(userId); baseEntity.setUpdateBy(userId);
baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId())); baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId()));
} else {
// 填充创建人、更新人和创建部门信息
baseEntity.setCreateBy(DEFAULT_USER_ID);
baseEntity.setUpdateBy(DEFAULT_USER_ID);
baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), DEFAULT_USER_ID));
} }
} }
} else { } else {
@@ -74,6 +84,8 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
Long userId = LoginHelper.getUserId(); Long userId = LoginHelper.getUserId();
if (ObjectUtil.isNotNull(userId)) { if (ObjectUtil.isNotNull(userId)) {
baseEntity.setUpdateBy(userId); baseEntity.setUpdateBy(userId);
} else {
baseEntity.setUpdateBy(DEFAULT_USER_ID);
} }
} else { } else {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
@@ -93,7 +105,6 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
try { try {
loginUser = LoginHelper.getLoginUser(); loginUser = LoginHelper.getLoginUser();
} catch (Exception e) { } catch (Exception e) {
log.warn("自动注入警告 => 用户未登录");
return null; return null;
} }
return loginUser; return loginUser;

View File

@@ -16,7 +16,9 @@ import java.util.function.Function;
* *
* @author Lion Li * @author Lion Li
* @version 3.6.0 新增 * @version 3.6.0 新增
* @deprecated redisson 新版本已经将队列功能标记删除 一些技术问题无法解决 建议搭建MQ使用
*/ */
@Deprecated
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils { public class QueueUtils {

View File

@@ -1,165 +1,180 @@
package org.dromara.common.redis.utils; package org.dromara.common.redis.utils;
import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import org.dromara.common.core.utils.SpringUtils; import lombok.AccessLevel;
import org.dromara.common.core.utils.StringUtils; import lombok.NoArgsConstructor;
import lombok.AccessLevel; import org.dromara.common.core.utils.SpringUtils;
import lombok.NoArgsConstructor; import org.dromara.common.core.utils.StringUtils;
import org.redisson.api.RIdGenerator; import org.redisson.api.RIdGenerator;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import java.time.Duration; import java.time.Duration;
/** /**
* 发号器工具类 * 发号器工具类
* *
* @author 秋辞未寒 * @author 秋辞未寒
* @date 2024-12-10 * @date 2024-12-10
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SequenceUtils { public class SequenceUtils {
/** /**
* 默认初始值 * 默认初始值
*/ */
public static final Long DEFAULT_INIT_VALUE = 1L; public static final Long DEFAULT_INIT_VALUE = 1L;
/**
* 默认步长 /**
*/ * 默认步长
public static final Long DEFAULT_STEP_VALUE = 1L; */
/** public static final Long DEFAULT_STEP_VALUE = 1L;
* 默认过期时间-天
*/ /**
public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1); * 默认过期时间-天
/** */
* 默认过期时间-分钟 public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1);
*/
public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1); /**
* 默认过期时间-分钟
/** */
* 获取Redisson客户端实例 public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1);
*/
private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); /**
* 获取Redisson客户端实例
/** */
* 获取ID生成器 private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class);
*
* @param key 业务key /**
* @param expireTime 过期时间 * 获取ID生成器
* @param initValue ID初始值 *
* @param stepValue ID步长 * @param key 业务key
* @return ID生成器 * @param expireTime 过期时间
*/ * @param initValue ID初始值
private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { * @param stepValue ID步长
if (initValue == null || initValue <= 0) { * @return ID生成器
initValue = DEFAULT_INIT_VALUE; */
} private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) {
if (stepValue == null || stepValue <= 0) { if (initValue == null || initValue <= 0) {
stepValue = DEFAULT_STEP_VALUE; initValue = DEFAULT_INIT_VALUE;
} }
RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); if (stepValue == null || stepValue <= 0) {
// 设置初始值和步长 stepValue = DEFAULT_STEP_VALUE;
idGenerator.tryInit(initValue, stepValue); }
// 设置过期时间 RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key);
idGenerator.expire(expireTime); // 设置初始值和步长
return idGenerator; idGenerator.tryInit(initValue, stepValue);
} // 设置过期时间
idGenerator.expire(expireTime);
/** return idGenerator;
* 获取指定业务key的唯一id }
*
* @param key 业务key /**
* @param expireTime 过期时间 * 获取指定业务key的唯一id
* @param initValue ID初始值 *
* @param stepValue ID步长 * @param key 业务key
* @return 唯一id * @param expireTime 过期时间
*/ * @param initValue ID初始值
public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) { * @param stepValue ID步长
return getIdGenerator(key, expireTime, initValue, stepValue).nextId(); * @return 唯一id
} */
public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) {
/** return getIdGenerator(key, expireTime, initValue, stepValue).nextId();
* 获取指定业务key的唯一id字符串 }
*
* @param key 业务key /**
* @param expireTime 过期时间 * 获取指定业务key的唯一id字符串
* @param initValue ID初始值 *
* @param stepValue ID步长 * @param key 业务key
* @return 唯一id * @param expireTime 过期时间
*/ * @param initValue ID初始值
public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) { * @param stepValue ID步长
return String.valueOf(nextId(key, expireTime, initValue, stepValue)); * @return 唯一id
} */
public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) {
/** return String.valueOf(nextId(key, expireTime, initValue, stepValue));
* 获取指定业务key的唯一id (ID初始值=1,ID步长=1) }
*
* @param key 业务key /**
* @param expireTime 过期时间 * 获取指定业务key的唯一id (ID初始值=1,ID步长=1)
* @return 唯一id *
*/ * @param key 业务key
public static long nextId(String key, Duration expireTime) { * @param expireTime 过期时间
return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); * @return 唯一id
} */
public static long nextId(String key, Duration expireTime) {
/** return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId();
* 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1) }
*
* @param key 业务key /**
* @param expireTime 过期时间 * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1)
* @return 唯一id *
*/ * @param key 业务key
public static String nextIdStr(String key, Duration expireTime) { * @param expireTime 过期时间
return String.valueOf(nextId(key, expireTime)); * @return 唯一id
} */
public static String nextIdStr(String key, Duration expireTime) {
/** return String.valueOf(nextId(key, expireTime));
* 获取 yyyyMMdd 开头的唯一id }
*
* @return 唯一id /**
*/ * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1),不足位数自动补零
public static String nextIdDate() { *
return nextIdDate(""); * @param key 业务key
} * @param expireTime 过期时间
* @param width 位数不足左补0
/** * @return 补零后的唯一id字符串
* 获取 prefix + yyyyMMdd 开头的唯一id */
* public static String nextPaddedIdStr(String key, Duration expireTime, Integer width) {
* @param prefix 业务前缀 return StringUtils.leftPad(nextIdStr(key, expireTime), width, '0');
* @return 唯一id }
*/
public static String nextIdDate(String prefix) { /**
// 前缀+日期 构建 prefixKey * 获取 yyyyMMdd 开头的唯一id
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); *
// 获取下一个id * @return 唯一id
long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); */
// 返回完整id public static String nextIdDate() {
return StringUtils.format("{}{}", prefixKey, nextId); return nextIdDate("");
} }
/** /**
* 获取 yyyyMMddHHmmss 开头的唯一id * 获取 prefix + yyyyMMdd 开头的唯一id
* *
* @return 唯一id * @param prefix 业务前缀
*/ * @return 唯一id
public static String nextIdDateTime() { */
return nextIdDateTime(""); public static String nextIdDate(String prefix) {
} // 前缀+日期 构建 prefixKey
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER));
/** // 获取下一个id
* 获取 prefix + yyyyMMddHHmmss 开头的唯一id long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId();
* // 返回完整id
* @param prefix 业务前缀 return StringUtils.format("{}{}", prefixKey, nextId);
* @return 唯一id }
*/
public static String nextIdDateTime(String prefix) { /**
// 前缀+日期时间 构建 prefixKey * 获取 yyyyMMddHHmmss 开头的唯一id
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER)); *
// 获取下一个id * @return 唯一id
long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); */
// 返回完整id public static String nextIdDateTime() {
return StringUtils.format("{}{}", prefixKey, nextId); return nextIdDateTime("");
} }
} /**
* 获取 prefix + yyyyMMddHHmmss 开头的唯一id
*
* @param prefix 业务前缀
* @return 唯一id
*/
public static String nextIdDateTime(String prefix) {
// 前缀+日期时间 构建 prefixKey
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER));
// 获取下一个id
long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId();
// 返回完整id
return StringUtils.format("{}{}", prefixKey, nextId);
}
}

View File

@@ -1,8 +1,13 @@
package org.dromara.common.satoken.core.service; package org.dromara.common.satoken.core.service;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.UserType; import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.PermissionService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import java.util.ArrayList; import java.util.ArrayList;
@@ -21,13 +26,21 @@ public class SaPermissionImpl implements StpInterface {
@Override @Override
public List<String> getPermissionList(Object loginId, String loginType) { public List<String> getPermissionList(Object loginId, String loginType) {
LoginUser loginUser = LoginHelper.getLoginUser(); LoginUser loginUser = LoginHelper.getLoginUser();
if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) {
PermissionService permissionService = getPermissionService();
if (ObjectUtil.isNotNull(permissionService)) {
List<String> list = StringUtils.splitList(loginId.toString(), ":");
return new ArrayList<>(permissionService.getMenuPermission(Long.parseLong(list.get(1))));
} else {
throw new ServiceException("PermissionService 实现类不存在");
}
}
UserType userType = UserType.getUserType(loginUser.getUserType()); UserType userType = UserType.getUserType(loginUser.getUserType());
if (userType == UserType.SYS_USER) { if (userType == UserType.APP_USER) {
return new ArrayList<>(loginUser.getMenuPermission());
} else if (userType == UserType.APP_USER) {
// 其他端 自行根据业务编写 // 其他端 自行根据业务编写
} }
return new ArrayList<>(); // SYS_USER 默认返回权限
return new ArrayList<>(loginUser.getMenuPermission());
} }
/** /**
@@ -36,12 +49,29 @@ public class SaPermissionImpl implements StpInterface {
@Override @Override
public List<String> getRoleList(Object loginId, String loginType) { public List<String> getRoleList(Object loginId, String loginType) {
LoginUser loginUser = LoginHelper.getLoginUser(); LoginUser loginUser = LoginHelper.getLoginUser();
if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) {
PermissionService permissionService = getPermissionService();
if (ObjectUtil.isNotNull(permissionService)) {
List<String> list = StringUtils.splitList(loginId.toString(), ":");
return new ArrayList<>(permissionService.getRolePermission(Long.parseLong(list.get(1))));
} else {
throw new ServiceException("PermissionService 实现类不存在");
}
}
UserType userType = UserType.getUserType(loginUser.getUserType()); UserType userType = UserType.getUserType(loginUser.getUserType());
if (userType == UserType.SYS_USER) { if (userType == UserType.APP_USER) {
return new ArrayList<>(loginUser.getRolePermission());
} else if (userType == UserType.APP_USER) {
// 其他端 自行根据业务编写 // 其他端 自行根据业务编写
} }
return new ArrayList<>(); // SYS_USER 默认返回权限
return new ArrayList<>(loginUser.getRolePermission());
} }
private PermissionService getPermissionService() {
try {
return SpringUtils.getBean(PermissionService.class);
} catch (Exception e) {
return null;
}
}
} }

View File

@@ -0,0 +1,154 @@
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* <p>
* 企业微信登录父类
* </p>
*
* @author liguanhua (347826496(a)qq.com)
* @since 1.15.9
*/
public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultRequest {
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source) {
super(config,source);
}
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(accessTokenUrl(null));
JSONObject object = this.checkResponse(response);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.code(authCallback.getCode())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = this.checkResponse(response);
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
// https://github.com/justauth/JustAuth/issues/227 修复bug
if (!object.containsKey("userid")) {
throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source);
}
String userId = object.getString("userid");
String userTicket = object.getString("user_ticket");
JSONObject userDetail = getUserDetail(authToken.getAccessToken(), userId, userTicket);
return AuthUser.builder()
.rawUserInfo(userDetail)
.username(userDetail.getString("name"))
.nickname(userDetail.getString("alias"))
.avatar(userDetail.getString("avatar"))
.location(userDetail.getString("address"))
.email(userDetail.getString("email"))
.uuid(userId)
.gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender")))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回JSONObject
*/
private JSONObject checkResponse(String response) {
JSONObject object = JSONObject.parseObject(response);
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"), source);
}
return object;
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("corpid", config.getClientId())
.queryParam("corpsecret", config.getClientSecret())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("code", authToken.getCode())
.build();
}
/**
* 用户详情
*
* @param accessToken accessToken
* @param userId 企业内用户id
* @param userTicket 成员票据,用于获取用户信息或敏感信息
* @return 用户详情
*/
private JSONObject getUserDetail(String accessToken, String userId, String userTicket) {
// 用户基础信息
String userInfoUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
.queryParam("access_token", accessToken)
.queryParam("userid", userId)
.build();
String userInfoResponse = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody();
JSONObject userInfo = checkResponse(userInfoResponse);
// 用户敏感信息
if (StringUtils.isNotEmpty(userTicket)) {
String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail")
.queryParam("access_token", accessToken)
.build();
JSONObject param = new JSONObject();
param.put("user_ticket", userTicket);
String userDetailResponse = new HttpUtils(config.getHttpConfig()).post(userDetailUrl, param.toJSONString()).getBody();
JSONObject userDetail = checkResponse(userDetailResponse);
userInfo.putAll(userDetail);
}
return userInfo;
}
}

View File

@@ -0,0 +1,92 @@
package org.dromara.common.social.gitea;
import cn.hutool.core.lang.Dict;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.json.utils.JsonUtils;
/**
* @author lcry
*/
@Slf4j
public class AuthGiteaRequest extends AuthDefaultRequest {
public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.gitea.server-url");
/**
* 设定归属域
*/
public AuthGiteaRequest(AuthConfig config) {
super(config, AuthGiteaSource.GITEA);
}
public AuthGiteaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthGiteaSource.GITEA, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String body = doPostAuthorizationCode(authCallback.getCode());
Dict object = JsonUtils.parseMap(body);
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getStr("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getStr("message"));
}
return AuthToken.builder()
.accessToken(object.getStr("access_token"))
.refreshToken(object.getStr("refresh_token"))
.idToken(object.getStr("id_token"))
.tokenType(object.getStr("token_type"))
.scope(object.getStr("scope"))
.build();
}
@Override
protected String doPostAuthorizationCode(String code) {
HttpRequest request = HttpRequest.post(source.accessToken())
.form("client_id", config.getClientId())
.form("client_secret", config.getClientSecret())
.form("grant_type", "authorization_code")
.form("code", code)
.form("redirect_uri", config.getRedirectUri());
HttpResponse response = request.execute();
return response.body();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String body = doGetUserInfo(authToken);
Dict object = JsonUtils.parseMap(body);
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getStr("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getStr("message"));
}
return AuthUser.builder()
.uuid(object.getStr("sub"))
.username(object.getStr("name"))
.nickname(object.getStr("preferred_username"))
.avatar(object.getStr("picture"))
.email(object.getStr("email"))
.token(authToken)
.source(source.toString())
.build();
}
}

View File

@@ -0,0 +1,50 @@
package org.dromara.common.social.gitea;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthDefaultRequest;
/**
* gitea Oauth2 默认接口说明
*
* @author lcry
*/
public enum AuthGiteaSource implements AuthSource {
/**
* 自己搭建的 gitea 私服
*/
GITEA {
/**
* 授权的api
*/
@Override
public String authorize() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/authorize";
}
/**
* 获取accessToken的api
*/
@Override
public String accessToken() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/access_token";
}
/**
* 获取用户信息的api
*/
@Override
public String userInfo() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/userinfo";
}
/**
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
*/
@Override
public Class<? extends AuthDefaultRequest> getTargetClass() {
return AuthGiteaRequest.class;
}
}
}

View File

@@ -10,6 +10,7 @@ import me.zhyd.oauth.request.*;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.gitea.AuthGiteaRequest;
import org.dromara.common.social.maxkey.AuthMaxKeyRequest; import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
import org.dromara.common.social.topiam.AuthTopIamRequest; import org.dromara.common.social.topiam.AuthTopIamRequest;
@@ -42,7 +43,7 @@ public class SocialUtils {
.redirectUri(obj.getRedirectUri()) .redirectUri(obj.getRedirectUri())
.scopes(obj.getScopes()); .scopes(obj.getScopes());
return switch (source.toLowerCase()) { return switch (source.toLowerCase()) {
case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE); case "dingtalk" -> new AuthDingTalkV2Request(builder.build(), STATE_CACHE);
case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE); case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
case "github" -> new AuthGithubRequest(builder.build(), STATE_CACHE); case "github" -> new AuthGithubRequest(builder.build(), STATE_CACHE);
case "gitee" -> new AuthGiteeRequest(builder.build(), STATE_CACHE); case "gitee" -> new AuthGiteeRequest(builder.build(), STATE_CACHE);
@@ -60,12 +61,13 @@ public class SocialUtils {
case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE); case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE);
case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE); case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE);
case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE); case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE);
case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(builder.agentId(obj.getAgentId()).build(), STATE_CACHE); case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeV2Request(builder.agentId(obj.getAgentId()).build(), STATE_CACHE);
case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE); case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE);
case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE); case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
case "gitea" -> new AuthGiteaRequest(builder.build(), STATE_CACHE);
default -> throw new AuthException("未获取到有效的Auth配置"); default -> throw new AuthException("未获取到有效的Auth配置");
}; };
} }

View File

@@ -38,8 +38,8 @@ public class SseEmitterManager {
// 每个用户可以有多个 SSE 连接,通过 token 进行区分 // 每个用户可以有多个 SSE 连接,通过 token 进行区分
Map<String, SseEmitter> emitters = USER_TOKEN_EMITTERS.computeIfAbsent(userId, k -> new ConcurrentHashMap<>()); Map<String, SseEmitter> emitters = USER_TOKEN_EMITTERS.computeIfAbsent(userId, k -> new ConcurrentHashMap<>());
// 创建一个新的 SseEmitter 实例,超时时间设置为 0 表示无限制 // 创建一个新的 SseEmitter 实例,超时时间设置为一天 避免连接之后直接关闭浏览器导致连接停滞
SseEmitter emitter = new SseEmitter(0L); SseEmitter emitter = new SseEmitter(86400000L);
emitters.put(token, emitter); emitters.put(token, emitter);

View File

@@ -48,7 +48,7 @@ public class PlusTenantLineHandler implements TenantLineHandler {
"gen_table_column" "gen_table_column"
); );
tables.addAll(excludes); tables.addAll(excludes);
return StringUtils.containsAnyIgnoreCase(tableName, tables.toArray(new String[0])); return StringUtils.equalsAnyIgnoreCase(tableName, tables.toArray(new String[0]));
} }
return true; return true;
} }

View File

@@ -1,9 +1,12 @@
package org.dromara.common.web.config; package org.dromara.common.web.config;
import cn.hutool.core.date.DateUtil;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.web.handler.GlobalExceptionHandler; import org.dromara.common.web.handler.GlobalExceptionHandler;
import org.dromara.common.web.interceptor.PlusWebInvokeTimeInterceptor; import org.dromara.common.web.interceptor.PlusWebInvokeTimeInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.CorsFilter;
@@ -11,6 +14,8 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Date;
/** /**
* 通用配置 * 通用配置
* *
@@ -25,6 +30,17 @@ public class ResourcesConfig implements WebMvcConfigurer {
registry.addInterceptor(new PlusWebInvokeTimeInterceptor()); registry.addInterceptor(new PlusWebInvokeTimeInterceptor());
} }
@Override
public void addFormatters(FormatterRegistry registry) {
// 全局日期格式转换配置
registry.addConverter(String.class, Date.class, source -> {
if (StringUtils.isBlank(source)) {
return null;
}
return DateUtil.parse(source);
});
}
@Override @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
} }

View File

@@ -2,6 +2,7 @@ package org.dromara.common.web.handler;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus; import cn.hutool.http.HttpStatus;
import com.fasterxml.jackson.core.JsonParseException;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolation;
@@ -14,6 +15,7 @@ import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -180,4 +182,24 @@ public class GlobalExceptionHandler {
return R.fail(message); return R.fail(message);
} }
/**
* JSON 解析异常Jackson 在处理 JSON 格式出错时抛出)
* 可能是请求体格式非法,也可能是服务端反序列化失败
*/
@ExceptionHandler(JsonParseException.class)
public R<Void> handleJsonParseException(JsonParseException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}' 发生 JSON 解析异常: {}", requestURI, e.getMessage());
return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求数据格式错误JSON 解析失败):" + e.getMessage());
}
/**
* 请求体读取异常(通常是请求参数格式非法、字段类型不匹配等)
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public R<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
log.error("请求地址'{}', 参数解析失败: {}", request.getRequestURI(), e.getMessage());
return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误:" + e.getMostSpecificCause().getMessage());
}
} }

View File

@@ -1,6 +1,6 @@
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds FROM bellsoft/liberica-openjdk-rocky:17.0.15-cds
#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds #FROM bellsoft/liberica-openjdk-rocky:21.0.7-cds
#FROM findepi/graalvm:java17-native #FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li" LABEL maintainer="Lion Li"

View File

@@ -1,6 +1,6 @@
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds FROM bellsoft/liberica-openjdk-rocky:17.0.15-cds
#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds #FROM bellsoft/liberica-openjdk-rocky:21.0.7-cds
#FROM findepi/graalvm:java17-native #FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li" LABEL maintainer="Lion Li"
@@ -9,7 +9,7 @@ RUN mkdir -p /ruoyi/snailjob/logs
WORKDIR /ruoyi/snailjob WORKDIR /ruoyi/snailjob
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m" ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE 8800 EXPOSE 8800
EXPOSE 17888 EXPOSE 17888

View File

@@ -1,6 +1,5 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.mail.utils.MailUtils;
@@ -18,7 +17,6 @@ import java.util.Arrays;
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@SaIgnore
@Validated @Validated
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
@@ -44,11 +42,11 @@ public class MailController {
* @param to 接收人 * @param to 接收人
* @param subject 标题 * @param subject 标题
* @param text 内容 * @param text 内容
* @param filePath 附件路径
*/ */
@GetMapping("/sendMessageWithAttachment") @GetMapping("/sendMessageWithAttachment")
public R<Void> sendMessageWithAttachment(String to, String subject, String text, String filePath) { public R<Void> sendMessageWithAttachment(String to, String subject, String text) {
MailUtils.sendText(to, subject, text, new File(filePath)); // 附件路径 禁止前端传递 有任意读取系统文件风险
MailUtils.sendText(to, subject, text, new File("/xxx/xxx"));
return R.ok(); return R.ok();
} }
@@ -58,10 +56,11 @@ public class MailController {
* @param to 接收人 * @param to 接收人
* @param subject 标题 * @param subject 标题
* @param text 内容 * @param text 内容
* @param paths 附件路径
*/ */
@GetMapping("/sendMessageWithAttachments") @GetMapping("/sendMessageWithAttachments")
public R<Void> sendMessageWithAttachments(String to, String subject, String text, String[] paths) { public R<Void> sendMessageWithAttachments(String to, String subject, String text) {
// 附件路径 禁止前端传递 有任意读取系统文件风险
String[] paths = new String[]{"/xxx/xxx", "/xxx/xxx"};
File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new); File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new);
MailUtils.sendText(to, subject, text, array); MailUtils.sendText(to, subject, text, array);
return R.ok(); return R.ok();

View File

@@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/demo/websocket") @RequestMapping("/demo/websocket")
@Slf4j @Slf4j
public class WeSocketController { public class WebSocketController {
/** /**
* 发布消息 * 发布消息

View File

@@ -1,11 +1,9 @@
package org.dromara.demo.controller.queue; package org.dromara.demo.controller.queue;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBoundedBlockingQueue; import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -20,7 +18,9 @@ import org.springframework.web.bind.annotation.RestController;
* *
* @author Lion Li * @author Lion Li
* @version 3.6.0 * @version 3.6.0
* @deprecated redisson 新版本已经将队列功能标记删除 一些技术问题无法解决 建议搭建MQ使用
*/ */
@Deprecated
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController

View File

@@ -1,6 +1,5 @@
package org.dromara.demo.controller.queue; package org.dromara.demo.controller.queue;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
@@ -23,8 +22,9 @@ import java.util.concurrent.TimeUnit;
* *
* @author Lion Li * @author Lion Li
* @version 3.6.0 * @version 3.6.0
* @deprecated redisson 新版本已经将队列功能标记删除 一些技术问题无法解决 建议搭建MQ使用
*/ */
@SaIgnore @Deprecated
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController

View File

@@ -8,7 +8,9 @@ import lombok.NoArgsConstructor;
* *
* @author Lion Li * @author Lion Li
* @version 3.6.0 * @version 3.6.0
* @deprecated redisson 新版本已经将队列功能标记删除 一些技术问题无法解决 建议搭建MQ使用
*/ */
@Deprecated
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class PriorityDemo implements Comparable<PriorityDemo> { public class PriorityDemo implements Comparable<PriorityDemo> {

View File

@@ -19,7 +19,9 @@ import org.springframework.web.bind.annotation.RestController;
* *
* @author Lion Li * @author Lion Li
* @version 3.6.0 * @version 3.6.0
* @deprecated redisson 新版本已经将队列功能标记删除 一些技术问题无法解决 建议搭建MQ使用
*/ */
@Deprecated
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController

View File

@@ -59,4 +59,9 @@ public class TestDemoBo extends BaseEntity {
@NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class}) @NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class})
private String value; private String value;
/**
* 版本
*/
private Long version;
} }

View File

@@ -2,6 +2,7 @@ package org.dromara.demo.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty; import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.format.DateTimeFormat;
import org.dromara.common.excel.annotation.ExcelNotation; import org.dromara.common.excel.annotation.ExcelNotation;
import org.dromara.common.excel.annotation.ExcelRequired; import org.dromara.common.excel.annotation.ExcelRequired;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
@@ -46,7 +47,7 @@ public class TestDemoVo implements Serializable {
* 用户id * 用户id
*/ */
@ExcelRequired @ExcelRequired
@ExcelProperty(value = "用户id") @ExcelProperty(value = "用户id", index = 5)
private Long userId; private Long userId;
/** /**
@@ -73,6 +74,8 @@ public class TestDemoVo implements Serializable {
/** /**
* 创建时间 * 创建时间
*/ */
@ExcelRequired
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "创建时间") @ExcelProperty(value = "创建时间")
private Date createTime; private Date createTime;
@@ -108,4 +111,9 @@ public class TestDemoVo implements Serializable {
@ExcelProperty(value = "更新人账号") @ExcelProperty(value = "更新人账号")
private String updateByName; private String updateByName;
/**
* 版本
*/
private Long version;
} }

View File

@@ -3,21 +3,21 @@ package org.dromara.generator.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.generator.domain.GenTable; import org.dromara.generator.domain.GenTable;
import org.dromara.generator.domain.GenTableColumn; import org.dromara.generator.domain.GenTableColumn;
import org.dromara.generator.service.IGenTableService; import org.dromara.generator.service.IGenTableService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -80,11 +80,8 @@ public class GenController extends BaseController {
@SaCheckPermission("tool:gen:list") @SaCheckPermission("tool:gen:list")
@GetMapping(value = "/column/{tableId}") @GetMapping(value = "/column/{tableId}")
public TableDataInfo<GenTableColumn> columnList(@PathVariable("tableId") Long tableId) { public TableDataInfo<GenTableColumn> columnList(@PathVariable("tableId") Long tableId) {
TableDataInfo<GenTableColumn> dataInfo = new TableDataInfo<>();
List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId); List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);
dataInfo.setRows(list); return TableDataInfo.build(list);
dataInfo.setTotal(list.size());
return dataInfo;
} }
/** /**

View File

@@ -9,7 +9,6 @@ import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -168,12 +167,7 @@ public class GenTableServiceImpl implements IGenTableService {
return gen; return gen;
}).sorted(Comparator.comparing(GenTable::getCreateTime).reversed()) }).sorted(Comparator.comparing(GenTable::getCreateTime).reversed())
.toList(); .toList();
return TableDataInfo.build(tables, pageQuery.build());
IPage<GenTable> page = pageQuery.build();
page.setTotal(tables.size());
// 手动分页 set数据
page.setRecords(CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables));
return TableDataInfo.build(page);
} }
/** /**
@@ -303,13 +297,13 @@ public class GenTableServiceImpl implements IGenTableService {
List<GenTableColumn> tableColumns = new ArrayList<>(); List<GenTableColumn> tableColumns = new ArrayList<>();
columns.forEach((columnName, column) -> { columns.forEach((columnName, column) -> {
GenTableColumn tableColumn = new GenTableColumn(); GenTableColumn tableColumn = new GenTableColumn();
tableColumn.setIsPk(String.valueOf(column.isPrimaryKey())); tableColumn.setIsPk(column.isPrimaryKey() ? "1" : "0");
tableColumn.setColumnName(column.getName()); tableColumn.setColumnName(column.getName());
tableColumn.setColumnComment(column.getComment()); tableColumn.setColumnComment(column.getComment());
tableColumn.setColumnType(column.getOriginType().toLowerCase()); tableColumn.setColumnType(column.getOriginType().toLowerCase());
tableColumn.setSort(column.getPosition()); tableColumn.setSort(column.getPosition());
tableColumn.setIsRequired(column.isNullable() == 0 ? "1" : "0"); tableColumn.setIsRequired(column.isNullable() ? "1" : "0");
tableColumn.setIsIncrement(column.isAutoIncrement() == -1 ? "0" : "1"); tableColumn.setIsIncrement(column.isAutoIncrement() ? "1" : "0");
tableColumns.add(tableColumn); tableColumns.add(tableColumn);
}); });
return tableColumns; return tableColumns;

View File

@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.domain.bo.${ClassName}Bo;
import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.vo.${ClassName}Vo;
@@ -27,6 +28,7 @@ import java.util.Collection;
* @author ${author} * @author ${author}
* @date ${datetime} * @date ${datetime}
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class ${ClassName}ServiceImpl implements I${ClassName}Service { public class ${ClassName}ServiceImpl implements I${ClassName}Service {

View File

@@ -80,6 +80,7 @@
v-loading="loading" v-loading="loading"
:data="${businessName}List" :data="${businessName}List"
row-key="${treeCode}" row-key="${treeCode}"
border
:default-expand-all="isExpandAll" :default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
> >

View File

@@ -82,7 +82,7 @@
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange"> <el-table v-loading="loading" border :data="${businessName}List" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
#foreach($column in $columns) #foreach($column in $columns)
#set($javaField=$column.javaField) #set($javaField=$column.javaField)

View File

@@ -6,6 +6,7 @@ import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.redisson.spring.data.connection.RedissonConnectionFactory; import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -31,9 +32,9 @@ public class CacheController {
@GetMapping() @GetMapping()
public R<CacheListInfoVo> getInfo() throws Exception { public R<CacheListInfoVo> getInfo() throws Exception {
RedisConnection connection = connectionFactory.getConnection(); RedisConnection connection = connectionFactory.getConnection();
Properties commandStats = connection.commands().info("commandstats"); try {
Properties commandStats = connection.commands().info("commandstats");
List<Map<String, String>> pieList = new ArrayList<>(); List<Map<String, String>> pieList = new ArrayList<>();
if (commandStats != null) { if (commandStats != null) {
commandStats.stringPropertyNames().forEach(key -> { commandStats.stringPropertyNames().forEach(key -> {
Map<String, String> data = new HashMap<>(2); Map<String, String> data = new HashMap<>(2);
@@ -43,10 +44,13 @@ public class CacheController {
pieList.add(data); pieList.add(data);
}); });
} }
return R.ok(new CacheListInfoVo( return R.ok(new CacheListInfoVo(
connection.commands().info(), connection.commands().info(),
connection.commands().dbSize(), pieList)); connection.commands().dbSize(), pieList));
} finally {
// 归还连接给连接池
RedisConnectionUtils.releaseConnection(connection, connectionFactory);
}
} }
public record CacheListInfoVo(Properties info, Long dbSize, List<Map<String, String>> commandStats) {} public record CacheListInfoVo(Properties info, Long dbSize, List<Map<String, String>> commandStats) {}

View File

@@ -1,6 +1,8 @@
package org.dromara.system.controller.system; package org.dromara.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
@@ -11,11 +13,10 @@ import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysConfigBo; import org.dromara.system.domain.bo.SysConfigBo;
import org.dromara.system.domain.vo.SysConfigVo; import org.dromara.system.domain.vo.SysConfigVo;
import org.dromara.system.service.ISysConfigService; import org.dromara.system.service.ISysConfigService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -120,7 +121,7 @@ public class SysConfigController extends BaseController {
@Log(title = "参数管理", businessType = BusinessType.DELETE) @Log(title = "参数管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}") @DeleteMapping("/{configIds}")
public R<Void> remove(@PathVariable Long[] configIds) { public R<Void> remove(@PathVariable Long[] configIds) {
configService.deleteConfigByIds(configIds); configService.deleteConfigByIds(Arrays.asList(configIds));
return R.ok(); return R.ok();
} }

View File

@@ -113,6 +113,9 @@ public class SysDeptController extends BaseController {
@Log(title = "部门管理", businessType = BusinessType.DELETE) @Log(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}") @DeleteMapping("/{deptId}")
public R<Void> remove(@PathVariable Long deptId) { public R<Void> remove(@PathVariable Long deptId) {
if (SystemConstants.DEFAULT_DEPT_ID.equals(deptId)) {
return R.warn("默认部门,不允许删除");
}
if (deptService.hasChildByDeptId(deptId)) { if (deptService.hasChildByDeptId(deptId)) {
return R.warn("存在下级部门,不允许删除"); return R.warn("存在下级部门,不允许删除");
} }

View File

@@ -2,23 +2,24 @@ package org.dromara.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.log.annotation.Log; import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.web.core.BaseController; import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysDictDataBo; import org.dromara.system.domain.bo.SysDictDataBo;
import org.dromara.system.domain.vo.SysDictDataVo; import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.service.ISysDictDataService; import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysDictTypeService; import org.dromara.system.service.ISysDictTypeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -117,7 +118,7 @@ public class SysDictDataController extends BaseController {
@Log(title = "字典类型", businessType = BusinessType.DELETE) @Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}") @DeleteMapping("/{dictCodes}")
public R<Void> remove(@PathVariable Long[] dictCodes) { public R<Void> remove(@PathVariable Long[] dictCodes) {
dictDataService.deleteDictDataByIds(dictCodes); dictDataService.deleteDictDataByIds(Arrays.asList(dictCodes));
return R.ok(); return R.ok();
} }
} }

View File

@@ -1,6 +1,8 @@
package org.dromara.system.controller.system; package org.dromara.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
@@ -11,11 +13,10 @@ import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysDictTypeBo; import org.dromara.system.domain.bo.SysDictTypeBo;
import org.dromara.system.domain.vo.SysDictTypeVo; import org.dromara.system.domain.vo.SysDictTypeVo;
import org.dromara.system.service.ISysDictTypeService; import org.dromara.system.service.ISysDictTypeService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -99,7 +100,7 @@ public class SysDictTypeController extends BaseController {
@Log(title = "字典类型", businessType = BusinessType.DELETE) @Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictIds}") @DeleteMapping("/{dictIds}")
public R<Void> remove(@PathVariable Long[] dictIds) { public R<Void> remove(@PathVariable Long[] dictIds) {
dictTypeService.deleteDictTypeByIds(dictIds); dictTypeService.deleteDictTypeByIds(Arrays.asList(dictIds));
return R.ok(); return R.ok();
} }

View File

@@ -21,6 +21,7 @@ import org.dromara.system.service.ISysMenuService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@@ -51,8 +52,8 @@ public class SysMenuController extends BaseController {
* 获取菜单列表 * 获取菜单列表
*/ */
@SaCheckRole(value = { @SaCheckRole(value = {
TenantConstants.SUPER_ADMIN_ROLE_KEY, TenantConstants.SUPER_ADMIN_ROLE_KEY,
TenantConstants.TENANT_ADMIN_ROLE_KEY TenantConstants.TENANT_ADMIN_ROLE_KEY
}, mode = SaMode.OR) }, mode = SaMode.OR)
@SaCheckPermission("system:menu:list") @SaCheckPermission("system:menu:list")
@GetMapping("/list") @GetMapping("/list")
@@ -67,8 +68,8 @@ public class SysMenuController extends BaseController {
* @param menuId 菜单ID * @param menuId 菜单ID
*/ */
@SaCheckRole(value = { @SaCheckRole(value = {
TenantConstants.SUPER_ADMIN_ROLE_KEY, TenantConstants.SUPER_ADMIN_ROLE_KEY,
TenantConstants.TENANT_ADMIN_ROLE_KEY TenantConstants.TENANT_ADMIN_ROLE_KEY
}, mode = SaMode.OR) }, mode = SaMode.OR)
@SaCheckPermission("system:menu:query") @SaCheckPermission("system:menu:query")
@GetMapping(value = "/{menuId}") @GetMapping(value = "/{menuId}")
@@ -111,9 +112,14 @@ public class SysMenuController extends BaseController {
@GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}") @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}")
public R<MenuTreeSelectVo> tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) { public R<MenuTreeSelectVo> tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) {
List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId()); List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId());
MenuTreeSelectVo selectVo = new MenuTreeSelectVo( List<Tree<Long>> list = menuService.buildMenuTreeSelect(menus);
menuService.selectMenuListByPackageId(packageId), // 删除租户管理菜单
menuService.buildMenuTreeSelect(menus)); list.removeIf(menu -> menu.getId() == 6L);
List<Long> ids = new ArrayList<>();
if (packageId > 0L) {
ids = menuService.selectMenuListByPackageId(packageId);
}
MenuTreeSelectVo selectVo = new MenuTreeSelectVo(ids, list);
return R.ok(selectVo); return R.ok(selectVo);
} }
@@ -173,4 +179,22 @@ public class SysMenuController extends BaseController {
public record MenuTreeSelectVo(List<Long> checkedKeys, List<Tree<Long>> menus) { public record MenuTreeSelectVo(List<Long> checkedKeys, List<Tree<Long>> menus) {
} }
/**
* 批量级联删除菜单
*
* @param menuIds 菜单ID串
*/
@SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
@SaCheckPermission("system:menu:remove")
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/cascade/{menuIds}")
public R<Void> remove(@PathVariable("menuIds") Long[] menuIds) {
List<Long> menuIdList = List.of(menuIds);
if (menuService.hasChildByMenuId(menuIdList)) {
return R.warn("存在子菜单,不允许删除");
}
menuService.deleteMenuById(menuIdList);
return R.ok();
}
} }

View File

@@ -2,7 +2,6 @@ package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataColumn;
@@ -30,18 +29,23 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id") @DataColumn(key = "deptName", value = "dept_id")
}) })
List<SysDeptVo> selectDeptList(@Param(Constants.WRAPPER) Wrapper<SysDept> queryWrapper); default List<SysDeptVo> selectDeptList(Wrapper<SysDept> queryWrapper) {
return this.selectVoList(queryWrapper);
}
/** /**
* 分页查询部门管理数据 * 分页查询部门管理数据
* *
* @param page 分页信息
* @param queryWrapper 查询条件 * @param queryWrapper 查询条件
* @return 部门信息集合 * @return 部门信息集合
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
}) })
Page<SysDeptVo> selectPageDeptList(@Param("page") Page<SysDeptVo> page, @Param(Constants.WRAPPER) Wrapper<SysDept> queryWrapper); default Page<SysDeptVo> selectPageDeptList(Page<SysDept> page, Wrapper<SysDept> queryWrapper) {
return this.selectVoPage(page, queryWrapper);
}
/** /**
* 统计指定部门ID的部门数量 * 统计指定部门ID的部门数量
@@ -52,7 +56,9 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id") @DataColumn(key = "deptName", value = "dept_id")
}) })
long countDeptById(Long deptId); default long countDeptById(Long deptId) {
return this.selectCount(new LambdaQueryWrapper<SysDept>().eq(SysDept::getDeptId, deptId));
}
/** /**
* 根据父部门ID查询其所有子部门的列表 * 根据父部门ID查询其所有子部门的列表

View File

@@ -1,9 +1,7 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@@ -30,7 +28,9 @@ public interface SysPostMapper extends BaseMapperPlus<SysPost, SysPostVo> {
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "create_by") @DataColumn(key = "userName", value = "create_by")
}) })
Page<SysPostVo> selectPagePostList(@Param("page") Page<SysPostVo> page, @Param(Constants.WRAPPER) Wrapper<SysPost> queryWrapper); default Page<SysPostVo> selectPagePostList(Page<SysPost> page, Wrapper<SysPost> queryWrapper) {
return this.selectVoPage(page, queryWrapper);
}
/** /**
* 查询用户所属岗位组 * 查询用户所属岗位组

View File

@@ -1,8 +1,11 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.system.domain.SysRoleMenu; import org.dromara.system.domain.SysRoleMenu;
import java.util.List;
/** /**
* 角色与菜单关联表 数据层 * 角色与菜单关联表 数据层
* *
@@ -10,4 +13,14 @@ import org.dromara.system.domain.SysRoleMenu;
*/ */
public interface SysRoleMenuMapper extends BaseMapperPlus<SysRoleMenu, SysRoleMenu> { public interface SysRoleMenuMapper extends BaseMapperPlus<SysRoleMenu, SysRoleMenu> {
/**
* 根据菜单ID串删除关联关系
*
* @param menuIds 菜单ID串
* @return 结果
*/
default int deleteByMenuIds(List<Long> menuIds) {
return this.delete(new LambdaUpdateWrapper<SysRoleMenu>().in(SysRoleMenu::getMenuId, menuIds));
}
} }

View File

@@ -1,6 +1,7 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -28,10 +29,12 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
* @return 分页的用户信息 * @return 分页的用户信息
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "u.dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "u.user_id") @DataColumn(key = "userName", value = "user_id")
}) })
Page<SysUserVo> selectPageUserList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper); default Page<SysUserVo> selectPageUserList(Page<SysUser> page, Wrapper<SysUser> queryWrapper) {
return this.selectVoPage(page, queryWrapper);
}
/** /**
* 查询用户列表,并进行数据权限控制 * 查询用户列表,并进行数据权限控制
@@ -43,7 +46,9 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "user_id")
}) })
List<SysUserVo> selectUserList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper); default List<SysUserVo> selectUserList(Wrapper<SysUser> queryWrapper) {
return this.selectVoList(queryWrapper);
}
/** /**
* 根据条件分页查询用户列表 * 根据条件分页查询用户列表
@@ -60,6 +65,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
/** /**
* 根据条件分页查询已配用户角色列表 * 根据条件分页查询已配用户角色列表
* *
* @param page 分页信息
* @param queryWrapper 查询条件 * @param queryWrapper 查询条件
* @return 用户信息集合信息 * @return 用户信息集合信息
*/ */
@@ -91,7 +97,9 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "user_id")
}) })
long countUserById(Long userId); default long countUserById(Long userId) {
return this.selectCount(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId, userId));
}
/** /**
* 根据条件更新用户数据 * 根据条件更新用户数据

View File

@@ -69,7 +69,7 @@ public interface ISysConfigService {
* *
* @param configIds 需要删除的参数ID * @param configIds 需要删除的参数ID
*/ */
void deleteConfigByIds(Long[] configIds); void deleteConfigByIds(List<Long> configIds);
/** /**
* 重置参数缓存数据 * 重置参数缓存数据

View File

@@ -47,7 +47,7 @@ public interface ISysDictDataService {
* *
* @param dictCodes 需要删除的字典数据ID * @param dictCodes 需要删除的字典数据ID
*/ */
void deleteDictDataByIds(Long[] dictCodes); void deleteDictDataByIds(List<Long> dictCodes);
/** /**
* 新增保存字典数据信息 * 新增保存字典数据信息

View File

@@ -62,7 +62,7 @@ public interface ISysDictTypeService {
* *
* @param dictIds 需要删除的字典ID * @param dictIds 需要删除的字典ID
*/ */
void deleteDictTypeByIds(Long[] dictIds); void deleteDictTypeByIds(List<Long> dictIds);
/** /**
* 重置字典缓存数据 * 重置字典缓存数据

View File

@@ -105,6 +105,14 @@ public interface ISysMenuService {
*/ */
boolean hasChildByMenuId(Long menuId); boolean hasChildByMenuId(Long menuId);
/**
* 是否存在菜单子节点
*
* @param menuIds 菜单ID串
* @return 结果 true 存在 false 不存在
*/
boolean hasChildByMenuId(List<Long> menuIds);
/** /**
* 查询菜单是否存在角色 * 查询菜单是否存在角色
* *
@@ -137,6 +145,14 @@ public interface ISysMenuService {
*/ */
int deleteMenuById(Long menuId); int deleteMenuById(Long menuId);
/**
* 批量删除菜单管理信息
*
* @param menuIds 菜单ID串
* @return 结果
*/
void deleteMenuById(List<Long> menuIds);
/** /**
* 校验菜单名称是否唯一 * 校验菜单名称是否唯一
* *

View File

@@ -1,5 +1,6 @@
package org.dromara.system.service.impl; package org.dromara.system.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -43,11 +44,10 @@ public class SysClientServiceImpl implements ISysClientService {
@Override @Override
public SysClientVo queryById(Long id) { public SysClientVo queryById(Long id) {
SysClientVo vo = baseMapper.selectVoById(id); SysClientVo vo = baseMapper.selectVoById(id);
vo.setGrantTypeList(List.of(vo.getGrantType().split(","))); vo.setGrantTypeList(StringUtils.splitList(vo.getGrantType()));
return vo; return vo;
} }
/** /**
* 查询客户端管理 * 查询客户端管理
*/ */
@@ -64,7 +64,7 @@ public class SysClientServiceImpl implements ISysClientService {
public TableDataInfo<SysClientVo> queryPageList(SysClientBo bo, PageQuery pageQuery) { public TableDataInfo<SysClientVo> queryPageList(SysClientBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<SysClient> lqw = buildQueryWrapper(bo); LambdaQueryWrapper<SysClient> lqw = buildQueryWrapper(bo);
Page<SysClientVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); Page<SysClientVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
result.getRecords().forEach(r -> r.setGrantTypeList(List.of(r.getGrantType().split(",")))); result.getRecords().forEach(r -> r.setGrantTypeList(StringUtils.splitList(r.getGrantType())));
return TableDataInfo.build(result); return TableDataInfo.build(result);
} }
@@ -93,8 +93,7 @@ public class SysClientServiceImpl implements ISysClientService {
@Override @Override
public Boolean insertByBo(SysClientBo bo) { public Boolean insertByBo(SysClientBo bo) {
SysClient add = MapstructUtils.convert(bo, SysClient.class); SysClient add = MapstructUtils.convert(bo, SysClient.class);
validEntityBeforeSave(add); add.setGrantType(CollUtil.join(bo.getGrantTypeList(), StringUtils.SEPARATOR));
add.setGrantType(String.join(",", bo.getGrantTypeList()));
// 生成clientid // 生成clientid
String clientKey = bo.getClientKey(); String clientKey = bo.getClientKey();
String clientSecret = bo.getClientSecret(); String clientSecret = bo.getClientSecret();
@@ -113,7 +112,6 @@ public class SysClientServiceImpl implements ISysClientService {
@Override @Override
public Boolean updateByBo(SysClientBo bo) { public Boolean updateByBo(SysClientBo bo) {
SysClient update = MapstructUtils.convert(bo, SysClient.class); SysClient update = MapstructUtils.convert(bo, SysClient.class);
validEntityBeforeSave(update);
update.setGrantType(String.join(",", bo.getGrantTypeList())); update.setGrantType(String.join(",", bo.getGrantTypeList()));
return baseMapper.updateById(update) > 0; return baseMapper.updateById(update) > 0;
} }
@@ -130,22 +128,12 @@ public class SysClientServiceImpl implements ISysClientService {
.eq(SysClient::getClientId, clientId)); .eq(SysClient::getClientId, clientId));
} }
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(SysClient entity) {
//TODO 做一些数据校验,如唯一约束
}
/** /**
* 批量删除客户端管理 * 批量删除客户端管理
*/ */
@CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true)
@Override @Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }
} }

View File

@@ -2,10 +2,10 @@ package org.dromara.system.service.impl;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
@@ -23,12 +23,10 @@ import org.dromara.system.domain.bo.SysConfigBo;
import org.dromara.system.domain.vo.SysConfigVo; import org.dromara.system.domain.vo.SysConfigVo;
import org.dromara.system.mapper.SysConfigMapper; import org.dromara.system.mapper.SysConfigMapper;
import org.dromara.system.service.ISysConfigService; import org.dromara.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -57,7 +55,6 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
* @return 参数配置信息 * @return 参数配置信息
*/ */
@Override @Override
@DS("master")
public SysConfigVo selectConfigById(Long configId) { public SysConfigVo selectConfigById(Long configId) {
return baseMapper.selectVoById(configId); return baseMapper.selectVoById(configId);
} }
@@ -83,14 +80,10 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
*/ */
@Override @Override
public boolean selectRegisterEnabled(String tenantId) { public boolean selectRegisterEnabled(String tenantId) {
SysConfig retConfig = TenantHelper.dynamic(tenantId, () -> { String configValue = TenantHelper.dynamic(tenantId, () ->
return baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>() this.selectConfigByKey("sys.account.registerUser")
.eq(SysConfig::getConfigKey, "sys.account.registerUser")); );
}); return Convert.toBool(configValue);
if (ObjectUtil.isNull(retConfig)) {
return false;
}
return Convert.toBool(retConfig.getConfigValue());
} }
/** /**
@@ -168,15 +161,15 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
* @param configIds 需要删除的参数ID * @param configIds 需要删除的参数ID
*/ */
@Override @Override
public void deleteConfigByIds(Long[] configIds) { public void deleteConfigByIds(List<Long> configIds) {
for (Long configId : configIds) { List<SysConfig> list = baseMapper.selectByIds(configIds);
SysConfig config = baseMapper.selectById(configId); list.forEach(config -> {
if (StringUtils.equals(SystemConstants.YES, config.getConfigType())) { if (StringUtils.equals(SystemConstants.YES, config.getConfigType())) {
throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); throw new ServiceException(String.format("内置参数【%s】不能删除", config.getConfigKey()));
} }
CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey());
} });
baseMapper.deleteByIds(Arrays.asList(configIds)); baseMapper.deleteByIds(configIds);
} }
/** /**
@@ -195,12 +188,10 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
*/ */
@Override @Override
public boolean checkConfigKeyUnique(SysConfigBo config) { public boolean checkConfigKeyUnique(SysConfigBo config) {
long configId = ObjectUtils.notNull(config.getConfigId(), -1L); boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysConfig>()
SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, config.getConfigKey())); .eq(SysConfig::getConfigKey, config.getConfigKey())
if (ObjectUtil.isNotNull(info) && info.getConfigId() != configId) { .ne(ObjectUtil.isNotNull(config.getConfigId()), SysConfig::getConfigId, config.getConfigId()));
return false; return !exist;
}
return true;
} }
/** /**

View File

@@ -131,23 +131,17 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
if (CollUtil.isEmpty(depts)) { if (CollUtil.isEmpty(depts)) {
return CollUtil.newArrayList(); return CollUtil.newArrayList();
} }
// 获取当前列表中每一个节点的parentId然后在列表中查找是否有id与其parentId对应若无对应则表明此时节点列表中该节点在当前列表中属于顶级节点 return TreeBuildUtils.buildMultiRoot(
List<Tree<Long>> treeList = CollUtil.newArrayList(); depts,
for (SysDeptVo d : depts) { SysDeptVo::getDeptId,
Long parentId = d.getParentId(); SysDeptVo::getParentId,
SysDeptVo sysDeptVo = StreamUtils.findFirst(depts, it -> it.getDeptId().longValue() == parentId); (node, treeNode) -> treeNode
if (ObjectUtil.isNull(sysDeptVo)) { .setId(node.getDeptId())
List<Tree<Long>> trees = TreeBuildUtils.build(depts, parentId, (dept, tree) -> .setParentId(node.getParentId())
tree.setId(dept.getDeptId()) .setName(node.getDeptName())
.setParentId(dept.getParentId()) .setWeight(node.getOrderNum())
.setName(dept.getDeptName()) .putExtra("disabled", SystemConstants.DISABLE.equals(node.getStatus()))
.setWeight(dept.getOrderNum()) );
.putExtra("disabled", SystemConstants.DISABLE.equals(dept.getStatus())));
Tree<Long> tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId());
treeList.add(tree);
}
}
return treeList;
} }
/** /**

View File

@@ -4,20 +4,19 @@ import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.system.domain.SysDictData;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.system.domain.SysDictData;
import org.dromara.system.domain.bo.SysDictDataBo; import org.dromara.system.domain.bo.SysDictDataBo;
import org.dromara.system.domain.vo.SysDictDataVo; import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.mapper.SysDictDataMapper; import org.dromara.system.mapper.SysDictDataMapper;
import org.dromara.system.service.ISysDictDataService; import org.dromara.system.service.ISysDictDataService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -58,7 +57,7 @@ public class SysDictDataServiceImpl implements ISysDictDataService {
lqw.eq(bo.getDictSort() != null, SysDictData::getDictSort, bo.getDictSort()); lqw.eq(bo.getDictSort() != null, SysDictData::getDictSort, bo.getDictSort());
lqw.like(StringUtils.isNotBlank(bo.getDictLabel()), SysDictData::getDictLabel, bo.getDictLabel()); lqw.like(StringUtils.isNotBlank(bo.getDictLabel()), SysDictData::getDictLabel, bo.getDictLabel());
lqw.eq(StringUtils.isNotBlank(bo.getDictType()), SysDictData::getDictType, bo.getDictType()); lqw.eq(StringUtils.isNotBlank(bo.getDictType()), SysDictData::getDictType, bo.getDictType());
lqw.orderByAsc(SysDictData::getDictSort); lqw.orderByAsc(SysDictData::getDictSort, SysDictData::getDictCode);
return lqw; return lqw;
} }
@@ -95,12 +94,10 @@ public class SysDictDataServiceImpl implements ISysDictDataService {
* @param dictCodes 需要删除的字典数据ID * @param dictCodes 需要删除的字典数据ID
*/ */
@Override @Override
public void deleteDictDataByIds(Long[] dictCodes) { public void deleteDictDataByIds(List<Long> dictCodes) {
for (Long dictCode : dictCodes) { List<SysDictData> list = baseMapper.selectByIds(dictCodes);
SysDictData data = baseMapper.selectById(dictCode); baseMapper.deleteByIds(dictCodes);
baseMapper.deleteById(dictCode); list.forEach(x -> CacheUtils.evict(CacheNames.SYS_DICT, x.getDictType()));
CacheUtils.evict(CacheNames.SYS_DICT, data.getDictType());
}
} }
/** /**
@@ -145,13 +142,11 @@ public class SysDictDataServiceImpl implements ISysDictDataService {
*/ */
@Override @Override
public boolean checkDictDataUnique(SysDictDataBo dict) { public boolean checkDictDataUnique(SysDictDataBo dict) {
Long dictCode = ObjectUtils.notNull(dict.getDictCode(), -1L); boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDictData>()
SysDictData entity = baseMapper.selectOne(new LambdaQueryWrapper<SysDictData>() .eq(SysDictData::getDictType, dict.getDictType())
.eq(SysDictData::getDictType, dict.getDictType()).eq(SysDictData::getDictValue, dict.getDictValue())); .eq(SysDictData::getDictValue, dict.getDictValue())
if (ObjectUtil.isNotNull(entity) && !dictCode.equals(entity.getDictCode())) { .ne(ObjectUtil.isNotNull(dict.getDictCode()), SysDictData::getDictCode, dict.getDictCode()));
return false; return !exist;
}
return true;
} }
} }

View File

@@ -98,10 +98,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override @Override
public List<SysDictDataVo> selectDictDataByType(String dictType) { public List<SysDictDataVo> selectDictDataByType(String dictType) {
List<SysDictDataVo> dictDatas = dictDataMapper.selectDictDataByType(dictType); List<SysDictDataVo> dictDatas = dictDataMapper.selectDictDataByType(dictType);
if (CollUtil.isNotEmpty(dictDatas)) { return CollUtil.isNotEmpty(dictDatas) ? dictDatas : null;
return dictDatas;
}
return null;
} }
/** /**
@@ -133,17 +130,20 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
* @param dictIds 需要删除的字典ID * @param dictIds 需要删除的字典ID
*/ */
@Override @Override
public void deleteDictTypeByIds(Long[] dictIds) { public void deleteDictTypeByIds(List<Long> dictIds) {
for (Long dictId : dictIds) { List<SysDictType> list = baseMapper.selectByIds(dictIds);
SysDictType dictType = baseMapper.selectById(dictId); list.forEach(x -> {
if (dictDataMapper.exists(new LambdaQueryWrapper<SysDictData>() boolean assigned = dictDataMapper.exists(new LambdaQueryWrapper<SysDictData>()
.eq(SysDictData::getDictType, dictType.getDictType()))) { .eq(SysDictData::getDictType, x.getDictType()));
throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); if (assigned) {
throw new ServiceException(String.format("%1$s已分配,不能删除", x.getDictName()));
} }
CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); });
CacheUtils.evict(CacheNames.SYS_DICT_TYPE, dictType.getDictType()); baseMapper.deleteByIds(dictIds);
} list.forEach(x -> {
baseMapper.deleteByIds(Arrays.asList(dictIds)); CacheUtils.evict(CacheNames.SYS_DICT, x.getDictType());
CacheUtils.evict(CacheNames.SYS_DICT_TYPE, x.getDictType());
});
} }
/** /**

View File

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
@@ -28,6 +29,7 @@ import org.dromara.system.mapper.SysRoleMenuMapper;
import org.dromara.system.mapper.SysTenantPackageMapper; import org.dromara.system.mapper.SysTenantPackageMapper;
import org.dromara.system.service.ISysMenuService; import org.dromara.system.service.ISysMenuService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.*;
@@ -141,7 +143,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
} else { } else {
menus = baseMapper.selectMenuTreeByUserId(userId); menus = baseMapper.selectMenuTreeByUserId(userId);
} }
return getChildPerms(menus, 0); return getChildPerms(menus, Constants.TOP_PARENT_ID);
} }
/** /**
@@ -173,11 +175,15 @@ public class SysMenuServiceImpl implements ISysMenuService {
if (tenantPackage.getMenuCheckStrictly()) { if (tenantPackage.getMenuCheckStrictly()) {
parentIds = baseMapper.selectObjs(new LambdaQueryWrapper<SysMenu>() parentIds = baseMapper.selectObjs(new LambdaQueryWrapper<SysMenu>()
.select(SysMenu::getParentId) .select(SysMenu::getParentId)
.in(SysMenu::getMenuId, menuIds), x -> {return Convert.toLong(x);}); .in(SysMenu::getMenuId, menuIds), x -> {
return Convert.toLong(x);
});
} }
return baseMapper.selectObjs(new LambdaQueryWrapper<SysMenu>() return baseMapper.selectObjs(new LambdaQueryWrapper<SysMenu>()
.in(SysMenu::getMenuId, menuIds) .in(SysMenu::getMenuId, menuIds)
.notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), x -> {return Convert.toLong(x);}); .notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), x -> {
return Convert.toLong(x);
});
} }
/** /**
@@ -216,7 +222,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
children.setQuery(menu.getQueryParam()); children.setQuery(menu.getQueryParam());
childrenList.add(children); childrenList.add(children);
router.setChildren(childrenList); router.setChildren(childrenList);
} else if (menu.getParentId().intValue() == 0 && menu.isInnerLink()) { } else if (menu.getParentId().equals(Constants.TOP_PARENT_ID) && menu.isInnerLink()) {
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
router.setPath("/"); router.setPath("/");
List<RouterVo> childrenList = new ArrayList<>(); List<RouterVo> childrenList = new ArrayList<>();
@@ -278,6 +284,17 @@ public class SysMenuServiceImpl implements ISysMenuService {
return baseMapper.exists(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, menuId)); return baseMapper.exists(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, menuId));
} }
/**
* 是否存在菜单子节点
*
* @param menuIds 菜单ID串
* @return 结果
*/
@Override
public boolean hasChildByMenuId(List<Long> menuIds) {
return baseMapper.exists(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getParentId, menuIds).notIn(SysMenu::getMenuId, menuIds));
}
/** /**
* 查询菜单使用数量 * 查询菜单使用数量
* *
@@ -324,6 +341,19 @@ public class SysMenuServiceImpl implements ISysMenuService {
return baseMapper.deleteById(menuId); return baseMapper.deleteById(menuId);
} }
/**
* 批量删除菜单管理信息
*
* @param menuIds 菜单ID串
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteMenuById(List<Long> menuIds) {
baseMapper.deleteByIds(menuIds);
roleMenuMapper.deleteByMenuIds(menuIds);
}
/** /**
* 校验菜单名称是否唯一 * 校验菜单名称是否唯一
* *
@@ -346,11 +376,11 @@ public class SysMenuServiceImpl implements ISysMenuService {
* @param parentId 传入的父节点ID * @param parentId 传入的父节点ID
* @return String * @return String
*/ */
private List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) { private List<SysMenu> getChildPerms(List<SysMenu> list, Long parentId) {
List<SysMenu> returnList = new ArrayList<>(); List<SysMenu> returnList = new ArrayList<>();
for (SysMenu t : list) { for (SysMenu t : list) {
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getParentId() == parentId) { if (t.getParentId().equals(parentId)) {
recursionFn(list, t); recursionFn(list, t);
returnList.add(t); returnList.add(t);
} }

View File

@@ -1,11 +1,12 @@
package org.dromara.system.service.impl; package org.dromara.system.service.impl;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.TenantConstants; import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.service.PermissionService;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.service.ISysMenuService; import org.dromara.system.service.ISysMenuService;
import org.dromara.system.service.ISysPermissionService; import org.dromara.system.service.ISysPermissionService;
import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysRoleService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.HashSet; import java.util.HashSet;
@@ -18,7 +19,7 @@ import java.util.Set;
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class SysPermissionServiceImpl implements ISysPermissionService { public class SysPermissionServiceImpl implements ISysPermissionService, PermissionService {
private final ISysRoleService roleService; private final ISysRoleService roleService;
private final ISysMenuService menuService; private final ISysMenuService menuService;

View File

@@ -2,6 +2,7 @@ package org.dromara.system.service.impl;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.dto.TaskAssigneeDTO; import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
import org.dromara.common.core.domain.model.TaskAssigneeBody; import org.dromara.common.core.domain.model.TaskAssigneeBody;
import org.dromara.common.core.service.TaskAssigneeService; import org.dromara.common.core.service.TaskAssigneeService;
@@ -51,6 +52,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
SysRoleBo bo = new SysRoleBo(); SysRoleBo bo = new SysRoleBo();
bo.setRoleKey(taskQuery.getHandlerCode()); bo.setRoleKey(taskQuery.getHandlerCode());
bo.setRoleName(taskQuery.getHandlerName()); bo.setRoleName(taskQuery.getHandlerName());
bo.setStatus(SystemConstants.NORMAL);
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
params.put("beginTime", taskQuery.getBeginTime()); params.put("beginTime", taskQuery.getBeginTime());
params.put("endTime", taskQuery.getEndTime()); params.put("endTime", taskQuery.getEndTime());
@@ -73,6 +75,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
SysPostBo bo = new SysPostBo(); SysPostBo bo = new SysPostBo();
bo.setPostCategory(taskQuery.getHandlerCode()); bo.setPostCategory(taskQuery.getHandlerCode());
bo.setPostName(taskQuery.getHandlerName()); bo.setPostName(taskQuery.getHandlerName());
bo.setStatus(SystemConstants.NORMAL);
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
params.put("beginTime", taskQuery.getBeginTime()); params.put("beginTime", taskQuery.getBeginTime());
params.put("endTime", taskQuery.getEndTime()); params.put("endTime", taskQuery.getEndTime());
@@ -96,6 +99,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
SysDeptBo bo = new SysDeptBo(); SysDeptBo bo = new SysDeptBo();
bo.setDeptCategory(taskQuery.getHandlerCode()); bo.setDeptCategory(taskQuery.getHandlerCode());
bo.setDeptName(taskQuery.getHandlerName()); bo.setDeptName(taskQuery.getHandlerName());
bo.setStatus(SystemConstants.NORMAL);
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
params.put("beginTime", taskQuery.getBeginTime()); params.put("beginTime", taskQuery.getBeginTime());
params.put("endTime", taskQuery.getEndTime()); params.put("endTime", taskQuery.getEndTime());
@@ -107,7 +111,6 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
return new TaskAssigneeDTO(page.getTotal(), handlers); return new TaskAssigneeDTO(page.getTotal(), handlers);
} }
/** /**
* 查询用户并返回任务指派的列表,支持分页 * 查询用户并返回任务指派的列表,支持分页
* *
@@ -120,6 +123,7 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService {
SysUserBo bo = new SysUserBo(); SysUserBo bo = new SysUserBo();
bo.setUserName(taskQuery.getHandlerCode()); bo.setUserName(taskQuery.getHandlerCode());
bo.setNickName(taskQuery.getHandlerName()); bo.setNickName(taskQuery.getHandlerName());
bo.setStatus(SystemConstants.NORMAL);
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
params.put("beginTime", taskQuery.getBeginTime()); params.put("beginTime", taskQuery.getBeginTime());
params.put("endTime", taskQuery.getEndTime()); params.put("endTime", taskQuery.getEndTime());

View File

@@ -69,16 +69,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
*/ */
@Override @Override
public List<SysUserExportVo> selectUserExportList(SysUserBo user) { public List<SysUserExportVo> selectUserExportList(SysUserBo user) {
return baseMapper.selectUserExportList(this.buildQueryWrapper(user));
}
private Wrapper<SysUser> buildQueryWrapper(SysUserBo user) {
Map<String, Object> params = user.getParams(); Map<String, Object> params = user.getParams();
QueryWrapper<SysUser> wrapper = Wrappers.query(); QueryWrapper<SysUser> wrapper = Wrappers.query();
wrapper.eq("u.del_flag", SystemConstants.NORMAL) wrapper.eq("u.del_flag", SystemConstants.NORMAL)
.eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId())
.in(StringUtils.isNotBlank(user.getUserIds()), "u.user_id", StringUtils.splitTo(user.getUserIds(), Convert::toLong))
.like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
.like(StringUtils.isNotBlank(user.getNickName()), "u.nick_name", user.getNickName())
.eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus())
.like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
.between(params.get("beginTime") != null && params.get("endTime") != null, .between(params.get("beginTime") != null && params.get("endTime") != null,
@@ -89,8 +84,29 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
ids.add(user.getDeptId()); ids.add(user.getDeptId());
w.in("u.dept_id", ids); w.in("u.dept_id", ids);
}).orderByAsc("u.user_id"); }).orderByAsc("u.user_id");
return baseMapper.selectUserExportList(wrapper);
}
private Wrapper<SysUser> buildQueryWrapper(SysUserBo user) {
Map<String, Object> params = user.getParams();
LambdaQueryWrapper<SysUser> wrapper = Wrappers.lambdaQuery();
wrapper.eq(SysUser::getDelFlag, SystemConstants.NORMAL)
.eq(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())
.in(StringUtils.isNotBlank(user.getUserIds()), SysUser::getUserId, StringUtils.splitTo(user.getUserIds(), Convert::toLong))
.like(StringUtils.isNotBlank(user.getUserName()), SysUser::getUserName, user.getUserName())
.like(StringUtils.isNotBlank(user.getNickName()), SysUser::getNickName, user.getNickName())
.eq(StringUtils.isNotBlank(user.getStatus()), SysUser::getStatus, user.getStatus())
.like(StringUtils.isNotBlank(user.getPhonenumber()), SysUser::getPhonenumber, user.getPhonenumber())
.between(params.get("beginTime") != null && params.get("endTime") != null,
SysUser::getCreateTime, params.get("beginTime"), params.get("endTime"))
.and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
List<SysDept> deptList = deptMapper.selectListByParentId(user.getDeptId());
List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
ids.add(user.getDeptId());
w.in(SysUser::getDeptId, ids);
}).orderByAsc(SysUser::getUserId);
if (StringUtils.isNotBlank(user.getExcludeUserIds())) { if (StringUtils.isNotBlank(user.getExcludeUserIds())) {
wrapper.notIn("u.user_id", StringUtils.splitTo(user.getExcludeUserIds(), Convert::toLong)); wrapper.notIn(SysUser::getUserId, StringUtils.splitList(user.getExcludeUserIds()));
} }
return wrapper; return wrapper;
} }
@@ -634,7 +650,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return List.of(); return List.of();
} }
List<SysUserVo> list = baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>() List<SysUserVo> list = baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber) .select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName,
SysUser::getNickName, SysUser::getUserType, SysUser::getEmail,
SysUser::getPhonenumber, SysUser::getSex, SysUser::getStatus,
SysUser::getCreateTime)
.eq(SysUser::getStatus, SystemConstants.NORMAL) .eq(SysUser::getStatus, SystemConstants.NORMAL)
.in(SysUser::getUserId, userIds)); .in(SysUser::getUserId, userIds));
return BeanUtil.copyToList(list, UserDTO.class); return BeanUtil.copyToList(list, UserDTO.class);
@@ -675,7 +694,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
// 获取用户ID列表 // 获取用户ID列表
Set<Long> userIds = StreamUtils.toSet(userRoles, SysUserRole::getUserId); Set<Long> userIds = StreamUtils.toSet(userRoles, SysUserRole::getUserId);
return selectListByIds(new ArrayList<>(userIds)); return this.selectListByIds(new ArrayList<>(userIds));
} }
/** /**
@@ -715,7 +734,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
// 获取用户ID列表 // 获取用户ID列表
Set<Long> userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId); Set<Long> userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId);
return selectListByIds(new ArrayList<>(userIds)); return this.selectListByIds(new ArrayList<>(userIds));
} }
/** /**

View File

@@ -7,32 +7,6 @@
<resultMap type="org.dromara.system.domain.vo.SysDeptVo" id="SysDeptResult"> <resultMap type="org.dromara.system.domain.vo.SysDeptVo" id="SysDeptResult">
</resultMap> </resultMap>
<select id="selectDeptList" resultMap="SysDeptResult">
select
<if test="ew.getSqlSelect != null">
${ew.getSqlSelect}
</if>
<if test="ew.getSqlSelect == null">
*
</if>
from sys_dept ${ew.getCustomSqlSegment}
</select>
<select id="selectPageDeptList" resultMap="SysDeptResult">
select
<if test="ew.getSqlSelect != null">
${ew.getSqlSelect}
</if>
<if test="ew.getSqlSelect == null">
*
</if>
from sys_dept ${ew.getCustomSqlSegment}
</select>
<select id="countDeptById" resultType="Long">
select count(*) from sys_dept where del_flag = '0' and dept_id = #{deptId}
</select>
<select id="selectDeptListByRoleId" resultType="Long"> <select id="selectDeptListByRoleId" resultType="Long">
select d.dept_id select d.dept_id
from sys_dept d from sys_dept d

View File

@@ -7,17 +7,6 @@
<resultMap type="org.dromara.system.domain.vo.SysPostVo" id="SysPostResult"> <resultMap type="org.dromara.system.domain.vo.SysPostVo" id="SysPostResult">
</resultMap> </resultMap>
<select id="selectPagePostList" resultMap="SysPostResult">
select
<if test="ew.getSqlSelect != null">
${ew.getSqlSelect}
</if>
<if test="ew.getSqlSelect == null">
*
</if>
from sys_post ${ew.getCustomSqlSegment}
</select>
<select id="selectPostsByUserId" parameterType="Long" resultMap="SysPostResult"> <select id="selectPostsByUserId" parameterType="Long" resultMap="SysPostResult">
select p.post_id, p.dept_id, p.post_name, p.post_code, p.post_category select p.post_id, p.dept_id, p.post_name, p.post_code, p.post_category
from sys_post p from sys_post p

View File

@@ -11,32 +11,6 @@
<id property="userId" column="user_id"/> <id property="userId" column="user_id"/>
</resultMap> </resultMap>
<select id="selectPageUserList" resultMap="SysUserResult">
select
<if test="ew.getSqlSelect != null">
${ew.getSqlSelect}
</if>
<if test="ew.getSqlSelect == null">
u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
</if>
from sys_user u
${ew.getCustomSqlSegment}
</select>
<select id="selectUserList" resultMap="SysUserResult">
select
<if test="ew.getSqlSelect != null">
${ew.getSqlSelect}
</if>
<if test="ew.getSqlSelect == null">
u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
</if>
from sys_user u
${ew.getCustomSqlSegment}
</select>
<select id="selectUserExportList" resultMap="SysUserExportResult"> <select id="selectUserExportList" resultMap="SysUserExportResult">
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
@@ -65,9 +39,4 @@
${ew.getCustomSqlSegment} ${ew.getCustomSqlSegment}
</select> </select>
<select id="countUserById" resultType="Long">
select count(*) from sys_user where del_flag = '0' and user_id = #{userId}
</select>
</mapper> </mapper>

View File

@@ -7,6 +7,21 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/**
* 自定义条件注解,用于基于配置启用或禁用特定功能
* <p>
* 该注解只会在配置文件中 `warm-flow.enabled=true` 时,标注了此注解的类或方法才会被 Spring 容器加载
* <p>
* 示例配置:
* <pre>
* warm-flow:
* enabled: true # 设置为 true 时,启用工作流功能
* </pre>
* <p>
* 使用此注解时,可以动态控制工作流功能是否启用,而不需要修改代码逻辑
*
* @author Lion Li
*/
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD }) @Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true") @ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true")

View File

@@ -13,21 +13,11 @@ public interface FlowConstant {
*/ */
String INITIATOR = "initiator"; String INITIATOR = "initiator";
/**
* 流程实例id
*/
String PROCESS_INSTANCE_ID = "processInstanceId";
/** /**
* 业务id * 业务id
*/ */
String BUSINESS_ID = "businessId"; String BUSINESS_ID = "businessId";
/**
* 任务id
*/
String TASK_ID = "taskId";
/** /**
* 委托 * 委托
*/ */
@@ -63,4 +53,29 @@ public interface FlowConstant {
*/ */
Long FLOW_CATEGORY_ID = 100L; Long FLOW_CATEGORY_ID = 100L;
/**
* 是否为申请人提交常量
*/
String SUBMIT = "submit";
/**
* 抄送常量
*/
String FLOW_COPY_LIST = "flowCopyList";
/**
* 消息类型常量
*/
String MESSAGE_TYPE = "messageType";
/**
* 消息通知常量
*/
String MESSAGE_NOTICE = "messageNotice";
/**
* 任务状态
*/
String WF_TASK_STATUS = "wf_task_status";
} }

View File

@@ -14,6 +14,7 @@ import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.domain.bo.FlowCategoryBo; import org.dromara.workflow.domain.bo.FlowCategoryBo;
import org.dromara.workflow.domain.vo.FlowCategoryVo; import org.dromara.workflow.domain.vo.FlowCategoryVo;
import org.dromara.workflow.service.IFlwCategoryService; import org.dromara.workflow.service.IFlwCategoryService;
@@ -110,6 +111,9 @@ public class FlwCategoryController extends BaseController {
@Log(title = "流程分类", businessType = BusinessType.DELETE) @Log(title = "流程分类", businessType = BusinessType.DELETE)
@DeleteMapping("/{categoryId}") @DeleteMapping("/{categoryId}")
public R<Void> remove(@PathVariable Long categoryId) { public R<Void> remove(@PathVariable Long categoryId) {
if (FlowConstant.FLOW_CATEGORY_ID.equals(categoryId)) {
return R.warn("默认流程分类,不允许删除");
}
if (flwCategoryService.hasChildByCategoryId(categoryId)) { if (flwCategoryService.hasChildByCategoryId(categoryId)) {
return R.warn("存在下级流程分类,不允许删除"); return R.warn("存在下级流程分类,不允许删除");
} }

View File

@@ -127,9 +127,9 @@ public class FlwInstanceController extends BaseController {
* *
* @param businessId 业务id * @param businessId 业务id
*/ */
@GetMapping("/flowImage/{businessId}") @GetMapping("/flowHisTaskList/{businessId}")
public R<Map<String, Object>> flowImage(@PathVariable String businessId) { public R<Map<String, Object>> flowHisTaskList(@PathVariable String businessId) {
return R.ok(flwInstanceService.flowImage(businessId)); return R.ok(flwInstanceService.flowHisTaskList(businessId));
} }
/** /**

View File

@@ -43,7 +43,6 @@ public class BackProcessBo implements Serializable {
/** /**
* 驳回的节点id(目前未使用,直接驳回到申请人) * 驳回的节点id(目前未使用,直接驳回到申请人)
*/ */
@NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class)
private String nodeCode; private String nodeCode;
/** /**

View File

@@ -70,7 +70,8 @@ public class CompleteTaskBo implements Serializable {
public Map<String, Object> getVariables() { public Map<String, Object> getVariables() {
if (variables == null) { if (variables == null) {
return new HashMap<>(16); variables = new HashMap<>(16);
return variables;
} }
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables; return variables;

View File

@@ -10,6 +10,7 @@ import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup; import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.workflow.domain.TestLeave; import org.dromara.workflow.domain.TestLeave;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date; import java.util.Date;
@@ -40,6 +41,7 @@ public class TestLeaveBo extends BaseEntity {
* 开始时间 * 开始时间
*/ */
@NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class}) @NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd")
private Date startDate; private Date startDate;
@@ -47,6 +49,7 @@ public class TestLeaveBo extends BaseEntity {
* 结束时间 * 结束时间
*/ */
@NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class}) @NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd")
private Date endDate; private Date endDate;

View File

@@ -4,14 +4,13 @@ import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty; import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.domain.FlowCategory; import org.dromara.workflow.domain.FlowCategory;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* 流程分类视图对象 wf_category * 流程分类视图对象 wf_category
@@ -34,13 +33,14 @@ public class FlowCategoryVo implements Serializable {
private Long categoryId; private Long categoryId;
/** /**
* 父级id * 父级分类id
*/ */
private Long parentId; private Long parentId;
/** /**
* 父类名称 * 父级分类名称
*/ */
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "parentId")
private String parentName; private String parentName;
/** /**
@@ -66,9 +66,4 @@ public class FlowCategoryVo implements Serializable {
@ExcelProperty(value = "创建时间") @ExcelProperty(value = "创建时间")
private Date createTime; private Date createTime;
/**
* 子菜单
*/
private List<FlowCategoryVo> children = new ArrayList<>();
} }

View File

@@ -2,6 +2,7 @@ package org.dromara.workflow.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty; import cn.idev.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.workflow.domain.TestLeave; import org.dromara.workflow.domain.TestLeave;

View File

@@ -1,7 +1,7 @@
package org.dromara.workflow.handler; package org.dromara.workflow.handler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.event.ProcessCreateTaskEvent; import org.dromara.common.core.domain.event.ProcessTaskEvent;
import org.dromara.common.core.domain.event.ProcessDeleteEvent; import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent; import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
@@ -56,19 +56,20 @@ public class FlowProcessEventHandler {
* @param instance 实例数据 * @param instance 实例数据
* @param taskId 任务id * @param taskId 任务id
*/ */
public void processCreateTaskHandler(String flowCode, Instance instance, Long taskId) { public void processTaskHandler(String flowCode, Instance instance, Long taskId) {
String tenantId = TenantHelper.getTenantId(); String tenantId = TenantHelper.getTenantId();
log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}",
tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId);
ProcessCreateTaskEvent processCreateTaskEvent = new ProcessCreateTaskEvent(); ProcessTaskEvent processTaskEvent = new ProcessTaskEvent();
processCreateTaskEvent.setTenantId(tenantId); processTaskEvent.setTenantId(tenantId);
processCreateTaskEvent.setFlowCode(flowCode); processTaskEvent.setFlowCode(flowCode);
processCreateTaskEvent.setBusinessId(instance.getBusinessId()); processTaskEvent.setBusinessId(instance.getBusinessId());
processCreateTaskEvent.setNodeType(instance.getNodeType()); processTaskEvent.setNodeType(instance.getNodeType());
processCreateTaskEvent.setNodeCode(instance.getNodeCode()); processTaskEvent.setNodeCode(instance.getNodeCode());
processCreateTaskEvent.setNodeName(instance.getNodeName()); processTaskEvent.setNodeName(instance.getNodeName());
processCreateTaskEvent.setTaskId(taskId); processTaskEvent.setTaskId(taskId);
SpringUtils.context().publishEvent(processCreateTaskEvent); processTaskEvent.setStatus(instance.getFlowStatus());
SpringUtils.context().publishEvent(processTaskEvent);
} }
/** /**

View File

@@ -1,12 +1,13 @@
package org.dromara.workflow.handler; package org.dromara.workflow.handler;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.handler.PermissionHandler; import org.dromara.warm.flow.core.handler.PermissionHandler;
import org.dromara.warm.flow.core.service.impl.TaskServiceImpl;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.service.IFlwCommonService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.Collections;
@@ -23,9 +24,11 @@ import java.util.List;
@Slf4j @Slf4j
public class WorkflowPermissionHandler implements PermissionHandler { public class WorkflowPermissionHandler implements PermissionHandler {
private final IFlwCommonService flwCommonService;
/** /**
* 审批前获取当前办理人,办理时会校验的该权限集合 * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
* 后续在{@link TaskServiceImpl#checkAuth(Task, FlowParams)} 中调用 * 后续在{@link FlowParams#getPermissionFlag} 中获取
* 返回当前用户权限集合 * 返回当前用户权限集合
*/ */
@Override @Override
@@ -43,4 +46,14 @@ public class WorkflowPermissionHandler implements PermissionHandler {
return LoginHelper.getUserIdStr(); return LoginHelper.getUserIdStr();
} }
/**
* 转换办理人比如设计器中预设了能办理的人如果其中包含角色或者部门id等可以通过此接口进行转换成用户id
*/
@Override
public List<String> convertPermissions(List<String> permissions) {
if (CollUtil.isNotEmpty(permissions)) {
permissions = flwCommonService.buildUser(permissions);
}
return permissions;
}
} }

View File

@@ -1,20 +1,28 @@
package org.dromara.workflow.listener; package org.dromara.workflow.listener;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.entity.Task;
import org.dromara.warm.flow.core.listener.GlobalListener; import org.dromara.warm.flow.core.listener.GlobalListener;
import org.dromara.warm.flow.core.listener.ListenerVariable; import org.dromara.warm.flow.core.listener.ListenerVariable;
import org.dromara.warm.flow.core.service.InsService;
import org.dromara.warm.flow.orm.entity.FlowInstance;
import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.warm.flow.orm.entity.FlowTask;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.bo.FlowCopyBo;
import org.dromara.workflow.handler.FlowProcessEventHandler; import org.dromara.workflow.handler.FlowProcessEventHandler;
import org.dromara.workflow.service.IFlwCommonService;
import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwInstanceService;
import org.dromara.workflow.service.IFlwTaskService; import org.dromara.workflow.service.IFlwTaskService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -34,9 +42,11 @@ import java.util.Map;
@RequiredArgsConstructor @RequiredArgsConstructor
public class WorkflowGlobalListener implements GlobalListener { public class WorkflowGlobalListener implements GlobalListener {
private final IFlwTaskService taskService; private final IFlwTaskService flwTaskService;
private final IFlwInstanceService instanceService; private final IFlwInstanceService instanceService;
private final FlowProcessEventHandler flowProcessEventHandler; private final FlowProcessEventHandler flowProcessEventHandler;
private final IFlwCommonService flwCommonService;
private final InsService insService;
/** /**
* 创建监听器,任务创建时执行 * 创建监听器,任务创建时执行
@@ -45,13 +55,7 @@ public class WorkflowGlobalListener implements GlobalListener {
*/ */
@Override @Override
public void create(ListenerVariable listenerVariable) { public void create(ListenerVariable listenerVariable) {
Instance instance = listenerVariable.getInstance();
Definition definition = listenerVariable.getDefinition();
Task task = listenerVariable.getTask();
if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(instance.getFlowStatus())) {
// 判断流程状态(发布审批中事件)
flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), instance, task.getId());
}
} }
/** /**
@@ -70,6 +74,25 @@ public class WorkflowGlobalListener implements GlobalListener {
*/ */
@Override @Override
public void assignment(ListenerVariable listenerVariable) { public void assignment(ListenerVariable listenerVariable) {
Map<String, Object> variable = listenerVariable.getVariable();
List<Task> nextTasks = listenerVariable.getNextTasks();
FlowParams flowParams = listenerVariable.getFlowParams();
Definition definition = listenerVariable.getDefinition();
Instance instance = listenerVariable.getInstance();
String applyNodeCode = flwCommonService.applyNodeCode(definition.getId());
for (Task flowTask : nextTasks) {
// 如果办理或者退回并行存在需要指定办理人,则直接覆盖办理人
if (variable.containsKey(flowTask.getNodeCode()) && (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus())
|| TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus()))) {
String userIds = variable.get(flowTask.getNodeCode()).toString();
flowTask.setPermissionList(List.of(userIds.split(StringUtils.SEPARATOR)));
variable.remove(flowTask.getNodeCode());
}
// 如果是申请节点,则把启动人添加到办理人
if (flowTask.getNodeCode().equals(applyNodeCode)) {
flowTask.setPermissionList(List.of(instance.getCreateBy()));
}
}
} }
/** /**
@@ -81,8 +104,10 @@ public class WorkflowGlobalListener implements GlobalListener {
public void finish(ListenerVariable listenerVariable) { public void finish(ListenerVariable listenerVariable) {
Instance instance = listenerVariable.getInstance(); Instance instance = listenerVariable.getInstance();
Definition definition = listenerVariable.getDefinition(); Definition definition = listenerVariable.getDefinition();
Task task = listenerVariable.getTask();
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
FlowParams flowParams = listenerVariable.getFlowParams(); FlowParams flowParams = listenerVariable.getFlowParams();
Map<String, Object> variable = new HashMap<>();
if (ObjectUtil.isNotNull(flowParams)) { if (ObjectUtil.isNotNull(flowParams)) {
// 历史任务扩展(通常为附件) // 历史任务扩展(通常为附件)
params.put("hisTaskExt", flowParams.getHisTaskExt()); params.put("hisTaskExt", flowParams.getHisTaskExt());
@@ -90,11 +115,53 @@ public class WorkflowGlobalListener implements GlobalListener {
params.put("handler", flowParams.getHandler()); params.put("handler", flowParams.getHandler());
// 办理意见 // 办理意见
params.put("message", flowParams.getMessage()); params.put("message", flowParams.getMessage());
variable = flowParams.getVariable();
} }
// 判断流程状态(发布:撤销,退回,作废,终止,已完成事件 //申请人提交事件
String status = determineFlowStatus(instance); Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT);
if (StringUtils.isNotBlank(status)) { if (submit != null && submit) {
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true);
} else {
// 判断流程状态(发布:撤销,退回,作废,终止,已完成事件)
String status = determineFlowStatus(instance);
if (StringUtils.isNotBlank(status)) {
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false);
}
}
//发布任务事件
if (task != null) {
flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, task.getId());
}
if (ObjectUtil.isNull(flowParams)) {
return;
}
// 只有办理或者退回的时候才执行消息通知和抄送
if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus())
|| TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) {
if (variable != null) {
if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) {
List<FlowCopyBo> flowCopyList = (List<FlowCopyBo>) variable.get(FlowConstant.FLOW_COPY_LIST);
// 添加抄送人
flwTaskService.setCopy(task, flowCopyList);
}
if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) {
List<String> messageType = (List<String>) variable.get(FlowConstant.MESSAGE_TYPE);
String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE);
// 消息通知
if (CollUtil.isNotEmpty(messageType)) {
flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice);
}
}
FlowInstance ins = new FlowInstance();
Map<String, Object> variableMap = instance.getVariableMap();
variableMap.remove(FlowConstant.FLOW_COPY_LIST);
variableMap.remove(FlowConstant.MESSAGE_TYPE);
variableMap.remove(FlowConstant.MESSAGE_NOTICE);
variableMap.remove(FlowConstant.SUBMIT);
ins.setId(instance.getId());
ins.setVariable(FlowEngine.jsonConvert.objToStr(variableMap));
insService.updateById(ins);
}
} }
} }
@@ -111,7 +178,7 @@ public class WorkflowGlobalListener implements GlobalListener {
return flowStatus; return flowStatus;
} else { } else {
Long instanceId = instance.getId(); Long instanceId = instance.getId();
List<FlowTask> flowTasks = taskService.selectByInstId(instanceId); List<FlowTask> flowTasks = flwTaskService.selectByInstId(instanceId);
if (CollUtil.isEmpty(flowTasks)) { if (CollUtil.isEmpty(flowTasks)) {
String status = BusinessStatusEnum.FINISH.getStatus(); String status = BusinessStatusEnum.FINISH.getStatus();
// 更新流程状态为已完成 // 更新流程状态为已完成

View File

@@ -29,7 +29,9 @@ public interface FlwCategoryMapper extends BaseMapperPlus<FlowCategory, FlowCate
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "createDept") @DataColumn(key = "deptName", value = "createDept")
}) })
long countCategoryById(Long categoryId); default long countCategoryById(Long categoryId) {
return this.selectCount(new LambdaQueryWrapper<FlowCategory>().eq(FlowCategory::getCategoryId, categoryId));
}
/** /**
* 根据父流程分类ID查询其所有子流程分类的列表 * 根据父流程分类ID查询其所有子流程分类的列表

View File

@@ -1,12 +1,6 @@
package org.dromara.workflow.service; package org.dromara.workflow.service;
import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.User;
import org.dromara.warm.flow.core.service.UserService;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 通用 工作流服务 * 通用 工作流服务
@@ -15,30 +9,13 @@ import java.util.Set;
*/ */
public interface IFlwCommonService { public interface IFlwCommonService {
/**
* 获取工作流用户service
*
* @return 工作流用户service
*/
UserService getFlowUserService();
/** /**
* 构建工作流用户 * 构建工作流用户
* *
* @param userList 办理用户 * @param permissionList 办理用户
* @param taskId 任务ID
* @return 用户 * @return 用户
*/ */
Set<User> buildUser(List<User> userList, Long taskId); List<String> buildUser(List<String> permissionList);
/**
* 构建工作流用户
*
* @param userIdList 办理用户
* @param taskId 任务ID
* @return 用户
*/
Set<User> buildFlowUser(List<String> userIdList, Long taskId);
/** /**
* 发送消息 * 发送消息
@@ -57,12 +34,4 @@ public interface IFlwCommonService {
* @return 申请人节点编码 * @return 申请人节点编码
*/ */
String applyNodeCode(Long definitionId); String applyNodeCode(Long definitionId);
/**
* 合并变量
*
* @param instance 流程实例
* @param variable 变量
*/
void mergeVariable(Instance instance, Map<String, Object> variable);
} }

View File

@@ -35,7 +35,6 @@ public interface IFlwDefinitionService {
*/ */
TableDataInfo<FlowDefinitionVo> unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery); TableDataInfo<FlowDefinitionVo> unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery);
/** /**
* 发布流程定义 * 发布流程定义
* *

View File

@@ -107,7 +107,7 @@ public interface IFlwInstanceService {
* @param businessId 业务id * @param businessId 业务id
* @return 结果 * @return 结果
*/ */
Map<String, Object> flowImage(String businessId); Map<String, Object> flowHisTaskList(String businessId);
/** /**
* 按照实例id更新状态 * 按照实例id更新状态

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