diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml index 065ff01a7..7b68f1dd3 100644 --- a/.run/ruoyi-monitor-admin.run.xml +++ b/.run/ruoyi-monitor-admin.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml index 6c5031edb..80474893b 100644 --- a/.run/ruoyi-server.run.xml +++ b/.run/ruoyi-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-snailjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml index 859902ac1..343f6e1c1 100644 --- a/.run/ruoyi-snailjob-server.run.xml +++ b/.run/ruoyi-snailjob-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/README.md b/README.md index fc3131a24..5d6dc9c0a 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ [![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus) [![Star](https://gitcode.com/dromara/RuoYi-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Vue-Plus) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/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)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.3.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.4.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![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-21](https://img.shields.io/badge/JDK-21-green.svg)]() @@ -22,10 +22,12 @@ > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) -> 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
-> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) +> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
+> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
+> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
+> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)
-> 文档地址: [plus-doc](https://plus-doc.dromara.org) +> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) ## 赞助商 @@ -34,6 +36,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/**
+Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) # 本框架与RuoYi的功能差异 @@ -75,7 +78,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | | 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释
只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | | 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | -| Excel框架 | 采用 Alibaba EasyExcel 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | +| Excel框架 | 采用 FastExcel(原Alibaba EasyExcel) 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | | 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 | | 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | | 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制
实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | @@ -113,7 +116,6 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 | | 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 | | 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | -| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 | | 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | ## 参考文档 diff --git a/pom.xml b/pom.xml index f1362f2e3..8456059d8 100644 --- a/pom.xml +++ b/pom.xml @@ -13,29 +13,29 @@ Dromara RuoYi-Vue-Plus多租户管理系统 - 5.3.1 - 3.4.4 + 5.4.0 + 3.4.6 UTF-8 UTF-8 17 3.5.16 - 2.8.5 + 2.8.8 0.15.0 - 4.0.3 + 1.2.0 2.3 - 1.40.0 - 3.5.11 + 1.42.0 + 3.5.12 3.9.1 5.8.35 - 3.4.5 + 3.4.7 3.45.1 2.2.7 4.3.1 - 1.4.0 - 1.4.6 + 1.5.0 + 1.4.8 0.2.0 1.18.36 - 1.76 + 1.80 1.16.7 2.7.0 @@ -48,8 +48,8 @@ 1.2.83 8.7.2-20250101 - - 1.6.8 + + 1.7.3 3.2.2 @@ -166,9 +166,9 @@ - com.alibaba - easyexcel - ${easyexcel.version} + cn.idev.excel + fastexcel + ${fastexcel.version} @@ -321,12 +321,6 @@ ${ip2region.version} - - commons-io - commons-io - 2.15.0 - - com.alibaba fastjson diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile index b489ab57c..278d67bb4 100644 --- a/ruoyi-admin/Dockerfile +++ b/ruoyi-admin/Dockerfile @@ -11,17 +11,18 @@ RUN mkdir -p /ruoyi/server/logs \ WORKDIR /ruoyi/server -ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" +ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" EXPOSE ${SERVER_PORT} +# 暴露 snail job 客户端端口 用于定时任务调度中心通信 +EXPOSE ${SNAIL_PORT} ADD ./target/ruoyi-admin.jar ./app.jar -# 工作流字体文件 -ADD ./zhFonts/ /usr/share/fonts/zhFonts/ SHELL ["/bin/bash", "-c"] ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + -Dsnail-job.port=${SNAIL_PORT} \ # 应用名称 如果想区分集群节点监控 改成不同的名称即可 #-Dskywalking.agent.service_name=ruoyi-server \ #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index 07595e092..554c64b32 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -1,9 +1,8 @@ package org.dromara.web.listener; -import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.listener.SaTokenListener; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.convert.Convert; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; @@ -35,14 +34,13 @@ import java.time.Duration; @Slf4j public class UserActionListener implements SaTokenListener { - private final SaTokenConfig tokenConfig; private final SysLoginService loginService; /** * 每次登录时触发 */ @Override - public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { + public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); String ip = ServletUtils.getClientIP(); UserOnlineDTO dto = new UserOnlineDTO(); @@ -52,17 +50,17 @@ public class UserActionListener implements SaTokenListener { dto.setOs(userAgent.getOs().getName()); dto.setLoginTime(System.currentTimeMillis()); dto.setTokenId(tokenValue); - String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY); - String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY); + String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); + String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY); dto.setUserName(username); - dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY)); - dto.setDeviceType(loginModel.getDevice()); - dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); + dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); + dto.setDeviceType(loginParameter.getDeviceType()); + dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); TenantHelper.dynamic(tenantId, () -> { - if(tokenConfig.getTimeout() == -1) { + if(loginParameter.getTimeout() == -1) { RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); } else { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); } }); // 记录登录日志 @@ -74,7 +72,7 @@ public class UserActionListener implements SaTokenListener { logininforEvent.setRequest(ServletUtils.getRequest()); SpringUtils.context().publishEvent(logininforEvent); // 更新登录信息 - loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip); + loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java index 9ec08132e..567906e42 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java @@ -1,6 +1,6 @@ package org.dromara.web.service; -import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.Constants; diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java index 1bed4f3e5..e4315dc59 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; @@ -58,8 +58,8 @@ public class EmailAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java index e8e60e1c4..e579f9969 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -1,9 +1,9 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.secure.BCrypt; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -70,8 +70,8 @@ public class PasswordAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java index 2ffda353e..597a6013b 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; @@ -58,8 +58,8 @@ public class SmsAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java index 419dbd6ba..ffe95e0b6 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; @@ -99,8 +99,8 @@ public class SocialAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java index fa9b61819..f223dd88f 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -76,8 +76,8 @@ public class XcxAuthStrategy implements IAuthStrategy { loginUser.setDeviceType(client.getDeviceType()); loginUser.setOpenid(openid); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index d2c45eae1..5dd2e00fe 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -263,3 +263,10 @@ justauth: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e 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 diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 1c7eae428..435a9e856 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -265,3 +265,10 @@ justauth: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e 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 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index c15d89c02..1bdc75e7e 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -21,8 +21,8 @@ server: worker: 256 captcha: + # 是否启用验证码校验 enable: true - # 页面 <参数设置> 可开启关闭 验证码校验 # 验证码类型 math 数组计算 char 字符验证 type: MATH # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 @@ -110,7 +110,7 @@ security: - /error - /*/api-docs - /*/api-docs/** - - /warm-flow-ui/token-name + - /warm-flow-ui/config # 多租户配置 tenant: @@ -177,9 +177,6 @@ springdoc: api-docs: # 是否开启接口文档 enabled: true -# swagger-ui: -# # 持久化认证数据 -# persistAuthorization: true info: # 标题 title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档' diff --git a/ruoyi-admin/zhFonts/.uuid b/ruoyi-admin/zhFonts/.uuid deleted file mode 100644 index cee5cdd7c..000000000 --- a/ruoyi-admin/zhFonts/.uuid +++ /dev/null @@ -1 +0,0 @@ -3f2ee348-0303-40ca-bf03-03f48d2d2141 \ No newline at end of file diff --git a/ruoyi-admin/zhFonts/SIMSUN.TTC b/ruoyi-admin/zhFonts/SIMSUN.TTC deleted file mode 100644 index 6ca8de3da..000000000 Binary files a/ruoyi-admin/zhFonts/SIMSUN.TTC and /dev/null differ diff --git a/ruoyi-admin/zhFonts/fonts.dir b/ruoyi-admin/zhFonts/fonts.dir deleted file mode 100644 index fed95442c..000000000 --- a/ruoyi-admin/zhFonts/fonts.dir +++ /dev/null @@ -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 diff --git a/ruoyi-admin/zhFonts/fonts.scale b/ruoyi-admin/zhFonts/fonts.scale deleted file mode 100644 index fed95442c..000000000 --- a/ruoyi-admin/zhFonts/fonts.scale +++ /dev/null @@ -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 diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 62967c30d..e4f9d30dc 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -14,7 +14,7 @@ - 5.3.1 + 5.4.0 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java index 519034cf2..c38f39b47 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java @@ -3,13 +3,14 @@ package org.dromara.common.core.constant; /** * 缓存组名称常量 *

- * key 格式为 cacheNames#ttl#maxIdleTime#maxSize + * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local *

* ttl 过期时间 如果设置为0则不过期 默认为0 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + * local 默认开启本地缓存为1 关闭本地缓存为0 *

- * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 + * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0 * * @author Lion Li */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java index 6329b9c01..7b15b85ae 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -33,7 +33,22 @@ public class ProcessEvent implements Serializable { private String businessId; /** - * 状态 + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 流程状态 */ private String status; @@ -45,6 +60,6 @@ public class ProcessEvent implements Serializable { /** * 当为true时为申请人节点办理 */ - private boolean submit; + private Boolean submit; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java similarity index 57% rename from ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java rename to ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java index 05047ab84..a9bcf7e93 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java @@ -6,12 +6,12 @@ import java.io.Serial; import java.io.Serializable; /** - * 流程创建任务监听 + * 流程任务监听 * * @author may */ @Data -public class ProcessCreateTaskEvent implements Serializable { +public class ProcessTaskEvent implements Serializable { @Serial private static final long serialVersionUID = 1L; @@ -27,10 +27,20 @@ public class ProcessCreateTaskEvent implements Serializable { private String flowCode; /** - * 审批节点编码 + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 */ private String nodeCode; + /** + * 流程节点名称 + */ + private String nodeName; + /** * 任务id */ @@ -41,4 +51,9 @@ public class ProcessCreateTaskEvent implements Serializable { */ private String businessId; + /** + * 流程状态 + */ + private String status; + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java index 6ea8a764a..cced26b2b 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java @@ -18,14 +18,14 @@ public class RegisterBody extends LoginBody { * 用户名 */ @NotBlank(message = "{user.username.not.blank}") - @Length(min = 2, max = 20, message = "{user.username.length.valid}") + @Length(min = 2, max = 30, message = "{user.username.length.valid}") private String username; /** * 用户密码 */ @NotBlank(message = "{user.password.not.blank}") - @Length(min = 5, max = 20, message = "{user.password.length.valid}") + @Length(min = 5, max = 30, message = "{user.password.length.valid}") private String password; private String userType; diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java index dbadfc2de..1667ac7bb 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java @@ -5,7 +5,6 @@ import lombok.Getter; /** * 设备类型 - * 针对一套 用户体系 * * @author Lion Li */ @@ -29,9 +28,12 @@ public enum DeviceType { XCX("xcx"), /** - * social第三方端 + * 第三方社交登录平台 */ SOCIAL("social"); + /** + * 设备标识 + */ private final String device; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java index 69e4753cf..636988f2f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java @@ -1,12 +1,11 @@ package org.dromara.common.core.enums; -import org.dromara.common.core.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; /** - * 设备类型 - * 针对多套 用户体系 + * 用户类型 * * @author Lion Li */ @@ -15,15 +14,18 @@ import lombok.Getter; public enum UserType { /** - * pc端 + * 后台系统用户 */ SYS_USER("sys_user"), /** - * app端 + * 移动客户端用户 */ APP_USER("app_user"); + /** + * 用户类型标识(用于 token、权限识别等) + */ private final String userType; public static UserType getUserType(String str) { diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java new file mode 100644 index 000000000..d7db79a91 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java @@ -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 getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java index 67cd54fba..4903c3860 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java @@ -3,6 +3,7 @@ package org.dromara.common.core.service; import org.dromara.common.core.domain.dto.UserDTO; import java.util.List; +import java.util.Map; /** * 通用 用户服务 @@ -91,4 +92,36 @@ public interface UserService { */ List selectUsersByPostIds(List postIds); + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + Map selectUserNamesByIds(List userIds); + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + Map selectRoleNamesByIds(List roleIds); + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + Map selectDeptNamesByIds(List deptIds); + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + Map selectPostNamesByIds(List postIds); + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java index abbcbff19..9d1a90195 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java @@ -78,9 +78,18 @@ public interface WorkflowService { /** * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); * * @param completeTask 参数 - * @return 结果 */ boolean completeTask(CompleteTaskDTO completeTask); + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + */ + boolean completeTask(Long taskId, String message); } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java index 41d0f6c25..b52d95e16 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java @@ -175,14 +175,27 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { } /** - * 计算两个日期之间的天数差(以毫秒为单位) + * 计算两个时间之间的时间差,并以指定单位返回(绝对值) * - * @param date1 第一个日期 - * @param date2 第二个日期 - * @return 两个日期之间的天数差的绝对值 + * @param start 起始时间 + * @param end 结束时间 + * @param unit 所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS) + * @return 时间差的绝对值,以指定单位表示 */ - public static int differentDaysByMillisecond(Date date1, Date date2) { - return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + public static long difference(Date start, Date end, TimeUnit unit) { + // 计算时间差,单位为毫秒,取绝对值避免负数 + long diffInMillis = Math.abs(end.getTime() - start.getTime()); + + // 根据目标单位转换时间差 + return switch (unit) { + case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1); + case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1); + case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1); + case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1); + case MILLISECONDS -> diffInMillis; + case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis); + case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis); + }; } /** diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java new file mode 100644 index 000000000..72fdf4033 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java @@ -0,0 +1,84 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.lang.PatternPool; +import cn.hutool.core.net.NetUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.regex.RegexUtils; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 增强网络相关工具类 + * + * @author 秋辞未寒 + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NetUtils extends NetUtil { + + /** + * 判断是否为IPv6地址 + * + * @param ip IP地址 + * @return 是否为IPv6地址 + */ + public static boolean isIPv6(String ip) { + try { + // 判断是否为IPv6地址 + return InetAddress.getByName(ip) instanceof Inet6Address; + } catch (UnknownHostException e) { + return false; + } + } + + /** + * 判断IPv6地址是否为内网地址 + *

+ * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理: + *

+     * 通配符地址 0:0:0:0:0:0:0:0
+     * 链路本地地址 fe80::/10
+     * 唯一本地地址 fec0::/10
+     * 环回地址 ::1
+     * 
+ * + * @param ip IP地址 + * @return 是否为内网地址 + */ + public static boolean isInnerIPv6(String ip) { + try { + // 判断是否为IPv6地址 + if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) { + // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断 + // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断 + // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机 + // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA) + if (inet6Address.isAnyLocalAddress() + || inet6Address.isLinkLocalAddress() + || inet6Address.isLoopbackAddress() + || inet6Address.isSiteLocalAddress()) { + return true; + } + } + } catch (UnknownHostException e) { + // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。 + throw new IllegalArgumentException("Invalid IPv6 address!", e); + } + return false; + } + + /** + * 判断是否为IPv4地址 + * + * @param ip IP地址 + * @return 是否为IPv4地址 + */ + public static boolean isIPv4(String ip) { + return RegexUtils.isMatch(PatternPool.IPV4, ip); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java index 0363ad4b5..716573431 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java @@ -6,6 +6,7 @@ import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import org.springframework.util.AntPathMatcher; +import java.nio.charset.Charset; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -339,4 +340,26 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils { return false; } + /** + * 将字符串从源字符集转换为目标字符集 + * + * @param input 原始字符串 + * @param fromCharset 源字符集 + * @param toCharset 目标字符集 + * @return 转换后的字符串 + */ + public static String convert(String input, Charset fromCharset, Charset toCharset) { + if (isBlank(input)) { + return input; + } + try { + // 从源字符集获取字节 + byte[] bytes = input.getBytes(fromCharset); + // 使用目标字符集解码 + return new String(bytes, toCharset); + } catch (Exception e) { + return input; + } + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java index 3f7cd57a9..104734465 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java @@ -1,11 +1,11 @@ package org.dromara.common.core.utils.ip; -import cn.hutool.core.net.NetUtil; import cn.hutool.http.HtmlUtil; -import org.dromara.common.core.utils.StringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.NetUtils; +import org.dromara.common.core.utils.StringUtils; /** * 获取地址类 @@ -20,14 +20,24 @@ public class AddressUtils { public static final String UNKNOWN = "XX XX"; public static String getRealAddressByIP(String ip) { - if (StringUtils.isBlank(ip)) { + // 处理空串并过滤HTML标签 + ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); + boolean isIPv6 = NetUtils.isIPv6(ip); + // 判断是否为IPv4或IPv6,如果不是则返回未知地址 + if (!NetUtils.isIPv4(ip) && !isIPv6) { return UNKNOWN; } // 内网不查询 - ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); - if (NetUtil.isInnerIP(ip)) { + if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) { return "内网IP"; } + // 不支持IPv6,不再进行没有必要的IP地址信息的解析,直接返回 + if (isIPv6) { + log.warn("ip2region不支持IPV6地址解析:{}", ip); + // 如有需要,可自行实现IPv6地址信息解析逻辑,并在这里返回 + return "未知"; + } return RegionUtils.getCityInfo(ip); } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java index 6e2a44e00..c9e867899 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java @@ -1,15 +1,12 @@ package org.dromara.common.core.utils.ip; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.resource.ClassPathResource; -import cn.hutool.core.util.ObjectUtil; -import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.core.utils.file.FileUtils; +import cn.hutool.core.io.resource.NoResourceException; +import cn.hutool.core.io.resource.ResourceUtil; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; import org.lionsoul.ip2region.xdb.Searcher; -import java.io.File; - /** * 根据ip地址定位工具类,离线方式 * 参考地址:集成 ip2region 实现离线IP地址定位库 @@ -19,31 +16,19 @@ import java.io.File; @Slf4j public class RegionUtils { + // IP地址库文件名称 + public static final String IP_XDB_FILENAME = "ip2region.xdb"; + private static final Searcher SEARCHER; static { - String fileName = "/ip2region.xdb"; - File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName); - if (!FileUtils.exist(existFile)) { - ClassPathResource fileStream = new ClassPathResource(fileName); - if (ObjectUtil.isEmpty(fileStream.getStream())) { - throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); - } - FileUtils.writeFromStream(fileStream.getStream(), existFile); - } - - String dbPath = existFile.getPath(); - - // 1、从 dbPath 加载整个 xdb 到内存。 - byte[] cBuff; try { - cBuff = Searcher.loadContentFromFile(dbPath); - } catch (Exception e) { - throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage()); - } - // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 - try { - SEARCHER = Searcher.newWithBuffer(cBuff); + // 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。 + // 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象。 + SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME)); + log.info("RegionUtils初始化成功,加载IP地址库数据成功!"); + } catch (NoResourceException e) { + throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); } catch (Exception e) { throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage()); } @@ -54,9 +39,8 @@ public class RegionUtils { */ public static String getCityInfo(String ip) { try { - ip = ip.trim(); // 3、执行查询 - String region = SEARCHER.search(ip); + String region = SEARCHER.search(StringUtils.trim(ip)); return region.replace("0|", "").replace("|0", ""); } catch (Exception e) { log.error("IP地址离线获取城市异常 {}", ip); diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java new file mode 100644 index 000000000..73fc4c444 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java @@ -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[] payload() default {}; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java new file mode 100644 index 000000000..558a3438a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java @@ -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 { + + /** + * 字典类型 + */ + 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); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java index 6cfa11a33..e63f44a00 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java @@ -1,37 +1,37 @@ -package org.dromara.common.core.validate.enumd; - -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.core.utils.reflect.ReflectUtils; - -/** - * 自定义枚举校验注解实现 - * - * @author 秋辞未寒 - * @date 2024-12-09 - */ -public class EnumPatternValidator implements ConstraintValidator { - - private EnumPattern annotation;; - - @Override - public void initialize(EnumPattern annotation) { - ConstraintValidator.super.initialize(annotation); - this.annotation = annotation; - } - - @Override - public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { - if (StringUtils.isNotBlank(value)) { - String fieldName = annotation.fieldName(); - for (Object e : annotation.type().getEnumConstants()) { - if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { - return true; - } - } - } - return false; - } - -} +package org.dromara.common.core.validate.enumd; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +/** + * 自定义枚举校验注解实现 + * + * @author 秋辞未寒 + * @date 2024-12-09 + */ +public class EnumPatternValidator implements ConstraintValidator { + + private EnumPattern annotation; + + @Override + public void initialize(EnumPattern annotation) { + ConstraintValidator.super.initialize(annotation); + this.annotation = annotation; + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isNotBlank(value)) { + String fieldName = annotation.fieldName(); + for (Object e : annotation.type().getEnumConstants()) { + if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { + return true; + } + } + } + return false; + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java index 069ef9ac8..c199015c0 100644 --- a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java @@ -30,7 +30,7 @@ import java.util.Optional; import java.util.Set; /** - * Swagger 文档配置 + * 接口文档配置 * * @author Lion Li */ diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java index 2a096eea7..ff0fbc812 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java @@ -108,7 +108,7 @@ public class EncryptUtils { } /** - * sm4加密 + * SM4加密(Base64编码) * * @param data 待加密数据 * @param password 秘钥字符串 @@ -127,11 +127,11 @@ public class EncryptUtils { } /** - * sm4加密 + * SM4加密(Hex编码) * * @param data 待加密数据 * @param password 秘钥字符串 - * @return 加密后字符串, 采用Base64编码 + * @return 加密后字符串, 采用Hex编码 */ public static String encryptBySm4Hex(String data, String password) { if (StrUtil.isBlank(password)) { @@ -148,7 +148,7 @@ public class EncryptUtils { /** * sm4解密 * - * @param data 待解密数据 + * @param data 待解密数据(可以是Base64或Hex编码) * @param password 秘钥字符串 * @return 解密后字符串 */ diff --git a/ruoyi-common/ruoyi-common-excel/pom.xml b/ruoyi-common/ruoyi-common-excel/pom.xml index dd4a5eebe..47ba5284b 100644 --- a/ruoyi-common/ruoyi-common-excel/pom.xml +++ b/ruoyi-common/ruoyi-common-excel/pom.xml @@ -22,8 +22,8 @@
- com.alibaba - easyexcel + cn.idev.excel + fastexcel diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java index 07cc4c4e7..c6beb5537 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java @@ -2,12 +2,12 @@ package org.dromara.common.excel.convert; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java index 61eeabfd7..c54816f76 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java @@ -3,12 +3,12 @@ package org.dromara.common.excel.convert; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.SpringUtils; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java index b948ea7ee..5723e61e8 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java @@ -3,12 +3,12 @@ package org.dromara.common.excel.convert; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.excel.annotation.ExcelEnumFormat; import lombok.extern.slf4j.Slf4j; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java index 7c7721c60..515f68e1b 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -3,11 +3,11 @@ package org.dromara.common.excel.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.write.handler.WorkbookWriteHandler; -import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; -import com.alibaba.excel.write.merge.AbstractMergeStrategy; +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.metadata.Head; +import cn.idev.excel.write.handler.WorkbookWriteHandler; +import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext; +import cn.idev.excel.write.merge.AbstractMergeStrategy; import lombok.AllArgsConstructor; import lombok.Data; import lombok.SneakyThrows; @@ -112,7 +112,13 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook } map.put(field, new RepeatCell(val, i)); } else if (i == list.size() - 1) { - if (i > repeatCell.getCurrent() && isMerge(list, i, field)) { + if (!isMerge(list, i, field)) { + // 如果最后一行不能合并,检查之前的数据是否需要合并 + if (i - repeatCell.getCurrent() > 1) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + } else if (i > repeatCell.getCurrent()) { + // 如果最后一行可以合并,则直接合并到最后 cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); } } else if (!isMerge(list, i, field)) { diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java index b6fa0b434..e715c5fc3 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java @@ -1,10 +1,10 @@ package org.dromara.common.excel.core; import cn.hutool.core.util.StrUtil; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.exception.ExcelDataConvertException; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import cn.idev.excel.exception.ExcelAnalysisException; +import cn.idev.excel.exception.ExcelDataConvertException; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.json.utils.JsonUtils; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 32fee7a6c..f3b641545 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -5,12 +5,12 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.excel.metadata.FieldCache; -import com.alibaba.excel.metadata.FieldWrapper; -import com.alibaba.excel.util.ClassUtils; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; -import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import cn.idev.excel.metadata.FieldCache; +import cn.idev.excel.metadata.FieldWrapper; +import cn.idev.excel.util.ClassUtils; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; @@ -175,7 +175,7 @@ public class ExcelDownHandler implements SheetWriteHandler { List firstOptions = options.getOptions(); Map> secoundOptionsMap = options.getNextOptions(); - // 采用按行填充数据的方式,避免EasyExcel出现数据无法写入的问题 + // 采用按行填充数据的方式,避免出现数据无法写入的问题 // Attempting to write a row in the range that is already written to disk // 使用ArrayList记载数据,防止乱序 @@ -291,9 +291,11 @@ public class ExcelDownHandler implements SheetWriteHandler { * @param value 下拉选可选值 */ private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List value) { + //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk + String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex; // 创建下拉数据表 - Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))) - .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))); + Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))) + .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))); // 将下拉表隐藏 workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); // 完善纵向的一级选项数据表 @@ -302,9 +304,9 @@ public class ExcelDownHandler implements SheetWriteHandler { // 获取每一选项行,如果没有则创建 Row row = Optional.ofNullable(simpleDataSheet.getRow(i)) .orElseGet(() -> simpleDataSheet.createRow(finalI)); - // 获取本级选项对应的选项列,如果没有则创建 - Cell cell = Optional.ofNullable(row.getCell(currentOptionsColumnIndex)) - .orElseGet(() -> row.createCell(currentOptionsColumnIndex)); + // 获取本级选项对应的选项列,如果没有则创建。上述采用多个sheet,默认索引为1列 + Cell cell = Optional.ofNullable(row.getCell(0)) + .orElseGet(() -> row.createCell(0)); // 设置值 cell.setCellValue(value.get(i)); } @@ -312,13 +314,13 @@ public class ExcelDownHandler implements SheetWriteHandler { // 创建名称管理器 Name name = workbook.createName(); // 设置名称管理器的别名 - String nameName = String.format("%s_%d", OPTIONS_SHEET_NAME, celIndex); + String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex); name.setNameName(nameName); // 以纵向第一列创建一级下拉拼接引用位置 String function = String.format("%s!$%s$1:$%s$%d", - OPTIONS_SHEET_NAME, - getExcelColumnName(currentOptionsColumnIndex), - getExcelColumnName(currentOptionsColumnIndex), + tmpOptionsSheetName, + getExcelColumnName(0), + getExcelColumnName(0), value.size()); // 设置名称管理器的引用位置 name.setRefersToFormula(function); diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java index 2d0340f25..957b30755 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java @@ -1,6 +1,6 @@ package org.dromara.common.excel.core; -import com.alibaba.excel.read.listener.ReadListener; +import cn.idev.excel.read.listener.ReadListener; /** * Excel 导入监听 diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java index a2aa4951b..259f8225b 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java @@ -1,15 +1,15 @@ package org.dromara.common.excel.handler; import cn.hutool.core.collection.CollUtil; -import com.alibaba.excel.metadata.data.DataFormatData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.util.StyleUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; -import com.alibaba.excel.write.metadata.style.WriteCellStyle; -import com.alibaba.excel.write.metadata.style.WriteFont; +import cn.idev.excel.metadata.data.DataFormatData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.util.StyleUtil; +import cn.idev.excel.write.handler.CellWriteHandler; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.handler.context.CellWriteHandlerContext; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.style.WriteCellStyle; +import cn.idev.excel.write.metadata.style.WriteFont; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java index 0e9569677..1b35e559a 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -3,13 +3,13 @@ package org.dromara.common.excel.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.util.IdUtil; -import com.alibaba.excel.EasyExcel; -import com.alibaba.excel.ExcelWriter; -import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; -import com.alibaba.excel.write.metadata.WriteSheet; -import com.alibaba.excel.write.metadata.fill.FillConfig; -import com.alibaba.excel.write.metadata.fill.FillWrapper; -import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import cn.idev.excel.FastExcel; +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; +import cn.idev.excel.write.metadata.WriteSheet; +import cn.idev.excel.write.metadata.fill.FillConfig; +import cn.idev.excel.write.metadata.fill.FillWrapper; +import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.AccessLevel; @@ -43,7 +43,7 @@ public class ExcelUtil { * @return 转换后集合 */ public static List importExcel(InputStream is, Class clazz) { - return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); + return FastExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); } @@ -57,7 +57,7 @@ public class ExcelUtil { */ public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); - EasyExcel.read(is, clazz, listener).sheet().doRead(); + FastExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } @@ -70,7 +70,7 @@ public class ExcelUtil { * @return 转换后集合 */ public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { - EasyExcel.read(is, clazz, listener).sheet().doRead(); + FastExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } @@ -186,7 +186,7 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os, List options) { - ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) + ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) @@ -215,6 +215,9 @@ public class ExcelUtil { */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplate(data, templatePath, os); @@ -233,18 +236,15 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplate(List data, String templatePath, OutputStream os) { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(os) + ExcelWriter excelWriter = FastExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .registerWriteHandler(new DataWriteHandler(data.get(0).getClass())) .build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); // 单表多数据导出 模板格式为 {.属性} for (T d : data) { @@ -265,6 +265,9 @@ public class ExcelUtil { */ public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplateMultiList(data, templatePath, os); @@ -285,6 +288,9 @@ public class ExcelUtil { */ public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplateMultiSheet(data, templatePath, os); @@ -303,17 +309,14 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(os) + ExcelWriter excelWriter = FastExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); for (Map.Entry map : data.entrySet()) { // 设置列表后续还有数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); @@ -337,18 +340,15 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(os) + ExcelWriter excelWriter = FastExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); for (int i = 0; i < data.size(); i++) { - WriteSheet writeSheet = EasyExcel.writerSheet(i).build(); + WriteSheet writeSheet = FastExcel.writerSheet(i).build(); for (Map.Entry map : data.get(i).entrySet()) { // 设置列表后续还有数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java index 8f5a45d11..77cf83381 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java @@ -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.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; -import org.dromara.common.json.handler.BigNumberSerializer; 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.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; @@ -15,6 +16,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Date; import java.util.TimeZone; /** @@ -38,6 +40,7 @@ public class JacksonConfig { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); + javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer()); builder.modules(javaTimeModule); builder.timeZone(TimeZone.getDefault()); log.info("初始化 jackson 配置"); diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java new file mode 100644 index 000000000..069b924f0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java @@ -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 对象 + * + * @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()); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java index 370f47995..1fe2b3ef1 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java @@ -1,5 +1,6 @@ package org.dromara.common.mybatis.core.page; +import cn.hutool.core.collection.CollUtil; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.Data; @@ -88,4 +89,19 @@ public class TableDataInfo implements Serializable { return rspData; } + /** + * 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页) + * + * @param list 原始数据列表(全部数据) + * @param page 分页参数对象(包含当前页码、每页大小等) + * @return 构造好的分页结果 TableDataInfo + */ + public static TableDataInfo build(List list, IPage page) { + if (CollUtil.isEmpty(list)) { + return TableDataInfo.build(); + } + List pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list); + return new TableDataInfo<>(pageList, list.size()); + } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index 518d52d23..9a572b56b 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -1,5 +1,6 @@ package org.dromara.common.mybatis.handler; +import cn.hutool.http.HttpStatus; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; @@ -25,7 +26,7 @@ public class MybatisExceptionHandler { public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); - return R.fail("数据库中已存在该记录,请联系管理员确认"); + return R.fail(HttpStatus.HTTP_CONFLICT, "数据库中已存在该记录,请联系管理员确认"); } /** @@ -35,12 +36,12 @@ public class MybatisExceptionHandler { public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); String message = e.getMessage(); - if (StringUtils.contains("CannotFindDataSourceException", message)) { + if (StringUtils.contains(message, "CannotFindDataSourceException")) { log.error("请求地址'{}', 未找到数据源", requestURI); - return R.fail("未找到数据源,请联系管理员确认"); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认"); } log.error("请求地址'{}', Mybatis系统异常", requestURI, e); - return R.fail(message); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, message); } } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index 740e2a13b..8428ef725 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -145,18 +145,25 @@ public class PlusSpringCacheManager implements CacheManager { if (array.length > 3) { config.setMaxSize(Integer.parseInt(array[3])); } - - if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { - return createMap(name, config); + int local = 1; + if (array.length > 4) { + local = Integer.parseInt(array[4]); } - return createMapCache(name, config); + if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { + return createMap(name, config, local); + } + + return createMapCache(name, config, local); } - private Cache createMap(String name, CacheConfig config) { + private Cache createMap(String name, CacheConfig config, int local) { RMap map = RedisUtils.getClient().getMap(name); - Cache cache = new CaffeineCacheDecorator(name, new RedissonCache(map, allowNullValues)); + Cache cache = new RedissonCache(map, allowNullValues); + if (local == 1) { + cache = new CaffeineCacheDecorator(name, cache); + } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } @@ -167,10 +174,13 @@ public class PlusSpringCacheManager implements CacheManager { return cache; } - private Cache createMapCache(String name, CacheConfig config) { + private Cache createMapCache(String name, CacheConfig config, int local) { RMapCache map = RedisUtils.getClient().getMapCache(name); - Cache cache = new CaffeineCacheDecorator(name, new RedissonCache(map, config, allowNullValues)); + Cache cache = new RedissonCache(map, config, allowNullValues); + if (local == 1) { + cache = new CaffeineCacheDecorator(name, cache); + } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java index e28c84e89..657dbbc07 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java @@ -1,165 +1,180 @@ -package org.dromara.common.redis.utils; - -import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateUtil; -import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.StringUtils; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.redisson.api.RIdGenerator; -import org.redisson.api.RedissonClient; - -import java.time.Duration; - -/** - * 发号器工具类 - * - * @author 秋辞未寒 - * @date 2024-12-10 - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class SequenceUtils { - - /** - * 默认初始值 - */ - public static final Long DEFAULT_INIT_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_MINUTE = Duration.ofMinutes(1); - - /** - * 获取Redisson客户端实例 - */ - private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); - - /** - * 获取ID生成器 - * - * @param key 业务key - * @param expireTime 过期时间 - * @param initValue ID初始值 - * @param stepValue ID步长 - * @return ID生成器 - */ - private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { - if (initValue == null || initValue <= 0) { - initValue = DEFAULT_INIT_VALUE; - } - if (stepValue == null || stepValue <= 0) { - stepValue = DEFAULT_STEP_VALUE; - } - RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); - // 设置初始值和步长 - idGenerator.tryInit(initValue, stepValue); - // 设置过期时间 - idGenerator.expire(expireTime); - return idGenerator; - } - - /** - * 获取指定业务key的唯一id - * - * @param key 业务key - * @param expireTime 过期时间 - * @param initValue ID初始值 - * @param stepValue ID步长 - * @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 过期时间 - * @param initValue ID初始值 - * @param stepValue ID步长 - * @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 过期时间 - * @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 过期时间 - * @return 唯一id - */ - public static String nextIdStr(String key, Duration expireTime) { - return String.valueOf(nextId(key, expireTime)); - } - - /** - * 获取 yyyyMMdd 开头的唯一id - * - * @return 唯一id - */ - public static String nextIdDate() { - return nextIdDate(""); - } - - /** - * 获取 prefix + yyyyMMdd 开头的唯一id - * - * @param prefix 业务前缀 - * @return 唯一id - */ - public static String nextIdDate(String prefix) { - // 前缀+日期 构建 prefixKey - String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); - // 获取下一个id - long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); - // 返回完整id - return StringUtils.format("{}{}", prefixKey, nextId); - } - - /** - * 获取 yyyyMMddHHmmss 开头的唯一id - * - * @return 唯一id - */ - public static String nextIdDateTime() { - 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); - } - -} +package org.dromara.common.redis.utils; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.redisson.api.RIdGenerator; +import org.redisson.api.RedissonClient; + +import java.time.Duration; + +/** + * 发号器工具类 + * + * @author 秋辞未寒 + * @date 2024-12-10 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SequenceUtils { + + /** + * 默认初始值 + */ + public static final Long DEFAULT_INIT_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_MINUTE = Duration.ofMinutes(1); + + /** + * 获取Redisson客户端实例 + */ + private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); + + /** + * 获取ID生成器 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return ID生成器 + */ + private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { + if (initValue == null || initValue <= 0) { + initValue = DEFAULT_INIT_VALUE; + } + if (stepValue == null || stepValue <= 0) { + stepValue = DEFAULT_STEP_VALUE; + } + RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); + // 设置初始值和步长 + idGenerator.tryInit(initValue, stepValue); + // 设置过期时间 + idGenerator.expire(expireTime); + return idGenerator; + } + + /** + * 获取指定业务key的唯一id + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @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 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @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 过期时间 + * @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 过期时间 + * @return 唯一id + */ + public static String nextIdStr(String key, Duration expireTime) { + return String.valueOf(nextId(key, expireTime)); + } + + /** + * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1),不足位数自动补零 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param width 位数,不足左补0 + * @return 补零后的唯一id字符串 + */ + public static String nextPaddedIdStr(String key, Duration expireTime, Integer width) { + return StringUtils.leftPad(nextIdStr(key, expireTime), width, '0'); + } + + /** + * 获取 yyyyMMdd 开头的唯一id + * + * @return 唯一id + */ + public static String nextIdDate() { + return nextIdDate(""); + } + + /** + * 获取 prefix + yyyyMMdd 开头的唯一id + * + * @param prefix 业务前缀 + * @return 唯一id + */ + public static String nextIdDate(String prefix) { + // 前缀+日期 构建 prefixKey + String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); + // 获取下一个id + long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + // 返回完整id + return StringUtils.format("{}{}", prefixKey, nextId); + } + + /** + * 获取 yyyyMMddHHmmss 开头的唯一id + * + * @return 唯一id + */ + public static String nextIdDateTime() { + 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); + } + +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java index a2a152003..46c61c4c8 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java @@ -1,6 +1,6 @@ package org.dromara.common.satoken.core.dao; -import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.dao.auto.SaTokenDaoBySessionFollowObject; import cn.dev33.satoken.util.SaFoxUtil; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -16,10 +16,12 @@ import java.util.concurrent.TimeUnit; * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) *

* 采用 caffeine + redis 多级缓存 优化并发查询效率 + *

+ * SaTokenDaoBySessionFollowObject 是 SaTokenDao 子集简化了session方法处理 * * @author Lion Li */ -public class PlusSaTokenDao implements SaTokenDao { +public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { private static final Cache CAFFEINE = Caffeine.newBuilder() // 设置最后一次写入或访问后经过固定时间过期 @@ -85,7 +87,8 @@ public class PlusSaTokenDao implements SaTokenDao { @Override public long getTimeout(String key) { long timeout = RedisUtils.getTimeToLive(key); - return timeout < 0 ? timeout : timeout / 1000; + // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 + return timeout < 0 ? timeout : timeout / 1000 + 1; } /** @@ -106,6 +109,19 @@ public class PlusSaTokenDao implements SaTokenDao { return o; } + /** + * 获取 Object (指定反序列化类型),如无返空 + * + * @param key 键名称 + * @return object + */ + @SuppressWarnings("unchecked cast") + @Override + public T getObject(String key, Class classType) { + Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); + return (T) o; + } + /** * 写入Object,并设定存活时间 (单位: 秒) */ @@ -152,7 +168,8 @@ public class PlusSaTokenDao implements SaTokenDao { @Override public long getObjectTimeout(String key) { long timeout = RedisUtils.getTimeToLive(key); - return timeout < 0 ? timeout : timeout / 1000; + // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 + return timeout < 0 ? timeout : timeout / 1000 + 1; } /** @@ -163,7 +180,6 @@ public class PlusSaTokenDao implements SaTokenDao { RedisUtils.expire(key, Duration.ofSeconds(timeout)); } - /** * 搜索数据 */ diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java index 1cef9a773..35d16cd6e 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java @@ -1,9 +1,13 @@ package org.dromara.common.satoken.core.service; 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.enums.UserType; +import org.dromara.common.core.service.PermissionService; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; @@ -15,19 +19,25 @@ import java.util.List; */ public class SaPermissionImpl implements StpInterface { + @Autowired + private PermissionService permissionService; + /** * 获取菜单权限列表 */ @Override public List getPermissionList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) { + List list = StringUtils.splitList(loginId.toString(), ":"); + return new ArrayList<>(permissionService.getMenuPermission(Long.parseLong(list.get(1)))); + } UserType userType = UserType.getUserType(loginUser.getUserType()); - if (userType == UserType.SYS_USER) { - return new ArrayList<>(loginUser.getMenuPermission()); - } else if (userType == UserType.APP_USER) { + if (userType == UserType.APP_USER) { // 其他端 自行根据业务编写 } - return new ArrayList<>(); + // SYS_USER 默认返回权限 + return new ArrayList<>(loginUser.getMenuPermission()); } /** @@ -36,12 +46,15 @@ public class SaPermissionImpl implements StpInterface { @Override public List getRoleList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) { + List list = StringUtils.splitList(loginId.toString(), ":"); + return new ArrayList<>(permissionService.getRolePermission(Long.parseLong(list.get(1)))); + } UserType userType = UserType.getUserType(loginUser.getUserType()); - if (userType == UserType.SYS_USER) { - return new ArrayList<>(loginUser.getRolePermission()); - } else if (userType == UserType.APP_USER) { + if (userType == UserType.APP_USER) { // 其他端 自行根据业务编写 } - return new ArrayList<>(); + // SYS_USER 默认返回权限 + return new ArrayList<>(loginUser.getRolePermission()); } } diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java index a5729387d..7a2b9fcf6 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java @@ -1,8 +1,8 @@ package org.dromara.common.satoken.utils; import cn.dev33.satoken.session.SaSession; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; @@ -47,8 +47,8 @@ public class LoginHelper { * @param loginUser 登录用户信息 * @param model 配置参数 */ - public static void login(LoginUser loginUser, SaLoginModel model) { - model = ObjectUtil.defaultIfNull(model, new SaLoginModel()); + public static void login(LoginUser loginUser, SaLoginParameter model) { + model = ObjectUtil.defaultIfNull(model, new SaLoginParameter()); StpUtil.login(loginUser.getLoginId(), model.setExtra(TENANT_KEY, loginUser.getTenantId()) .setExtra(USER_KEY, loginUser.getUserId()) diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java index a4e921f23..21f2c113c 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java @@ -11,13 +11,13 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.HttpStatus; -import org.dromara.common.core.exception.SseException; import org.dromara.common.core.utils.ServletUtils; 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.security.config.properties.SecurityProperties; import org.dromara.common.security.handler.AllUrlHandler; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -37,6 +37,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; public class SecurityConfig implements WebMvcConfigurer { private final SecurityProperties securityProperties; + @Value("${sse.path}") + private String ssePath; /** * 注册sa-token的拦截器 @@ -54,15 +56,7 @@ public class SecurityConfig implements WebMvcConfigurer { .check(() -> { HttpServletRequest request = ServletUtils.getRequest(); // 检查是否登录 是否有token - try { - StpUtil.checkLogin(); - } catch (NotLoginException e) { - if (request.getRequestURI().contains("sse")) { - throw new SseException(e.getMessage(), e.getCode()); - } else { - throw e; - } - } + StpUtil.checkLogin(); // 检查 header 与 param 里的 clientid 与 token 里的是否一致 String headerCid = request.getHeader(LoginHelper.CLIENT_KEY); @@ -84,7 +78,8 @@ public class SecurityConfig implements WebMvcConfigurer { }); })).addPathPatterns("/**") // 排除不需要拦截的路径 - .excludePathPatterns(securityProperties.getExcludes()); + .excludePathPatterns(securityProperties.getExcludes()) + .excludePathPatterns(ssePath); } /** diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java new file mode 100644 index 000000000..d3fc7516f --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java @@ -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(); + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java new file mode 100644 index 000000000..201b22308 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java @@ -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 getTargetClass() { + return AuthGiteaRequest.class; + } + + } +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index db696e515..3f7924d9b 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -10,6 +10,7 @@ import me.zhyd.oauth.request.*; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.social.config.properties.SocialLoginConfigProperties; 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.topiam.AuthTopIamRequest; @@ -42,7 +43,7 @@ public class SocialUtils { .redirectUri(obj.getRedirectUri()) .scopes(obj.getScopes()); 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 "github" -> new AuthGithubRequest(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 "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).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 "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(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配置"); }; } diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java index e5331e419..412834cfb 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java @@ -33,6 +33,7 @@ public class SseController implements DisposableBean { */ @GetMapping(value = "${sse.path}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter connect() { + StpUtil.checkLogin(); String tokenValue = StpUtil.getTokenValue(); Long userId = LoginHelper.getUserId(); return sseEmitterManager.connect(userId, tokenValue); diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java index 64dfcff37..cb9442850 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java @@ -44,9 +44,24 @@ public class SseEmitterManager { emitters.put(token, emitter); // 当 emitter 完成、超时或发生错误时,从映射表中移除对应的 token - emitter.onCompletion(() -> emitters.remove(token)); - emitter.onTimeout(() -> emitters.remove(token)); - emitter.onError((e) -> emitters.remove(token)); + emitter.onCompletion(() -> { + SseEmitter remove = emitters.remove(token); + if (remove != null) { + remove.complete(); + } + }); + emitter.onTimeout(() -> { + SseEmitter remove = emitters.remove(token); + if (remove != null) { + remove.complete(); + } + }); + emitter.onError((e) -> { + SseEmitter remove = emitters.remove(token); + if (remove != null) { + remove.complete(); + } + }); try { // 向客户端发送一条连接成功的事件 @@ -106,7 +121,10 @@ public class SseEmitterManager { .name("message") .data(message)); } catch (Exception e) { - emitters.remove(entry.getKey()); + SseEmitter remove = emitters.remove(entry.getKey()); + if (remove != null) { + remove.complete(); + } } } } else { diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java index b8da28ef1..9aaa753ec 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java @@ -81,6 +81,17 @@ public class TenantSaTokenDao extends PlusSaTokenDao { return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key); } + /** + * 获取 Object (指定反序列化类型),如无返空 + * + * @param key 键名称 + * @return object + */ + @Override + public T getObject(String key, Class classType) { + return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key, classType); + } + /** * 写入Object,并设定存活时间 (单位: 秒) */ @@ -137,7 +148,6 @@ public class TenantSaTokenDao extends PlusSaTokenDao { RedisUtils.expire(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout)); } - /** * 搜索数据 */ diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java index 6c93ee581..d518becbc 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java @@ -48,7 +48,7 @@ public class PlusTenantLineHandler implements TenantLineHandler { "gen_table_column" ); tables.addAll(excludes); - return tables.contains(tableName); + return StringUtils.containsAnyIgnoreCase(tableName, tables.toArray(new String[0])); } return true; } diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java index 0a60fbcc7..28aacbcfb 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package org.dromara.common.web.handler; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpStatus; +import com.fasterxml.jackson.core.JsonParseException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; 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.json.utils.JsonUtils; import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -180,4 +182,24 @@ public class GlobalExceptionHandler { return R.fail(message); } + /** + * JSON 解析异常(Jackson 在处理 JSON 格式出错时抛出) + * 可能是请求体格式非法,也可能是服务端反序列化失败 + */ + @ExceptionHandler(JsonParseException.class) + public R 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 handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) { + log.error("请求地址'{}', 参数解析失败: {}", request.getRequestURI(), e.getMessage()); + return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误:" + e.getMostSpecificCause().getMessage()); + } + } diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java index e3a6892fa..00f6eee13 100644 --- a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java @@ -44,7 +44,7 @@ public class ActuatorAuthFilter implements Filter { return; } // 验证用户名和密码 - if (!username.equals(split[0]) && password.equals(split[1])) { + if (!username.equals(split[0]) || !password.equals(split[1])) { response.setHeader("WWW-Authenticate", "Basic realm=\"realm\""); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); return; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java index 303cf885d..2335da4cd 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java @@ -40,7 +40,7 @@ public class RedisCacheController { *

* cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 */ - @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null") + @Cacheable(cacheNames = "demo:cache#60s#10m#20#1", key = "#key", condition = "#key != null") @GetMapping("/test1") public R test1(String key, String value) { return R.ok("操作成功", value); diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java similarity index 96% rename from ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java rename to ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java index 699a5e602..65bf68e8c 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java @@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo/websocket") @Slf4j -public class WeSocketController { +public class WebSocketController { /** * 发布消息 diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java index c0661185e..dc8b35f49 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java @@ -1,6 +1,6 @@ package org.dromara.demo.domain.bo; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelProperty; import lombok.Data; import jakarta.validation.constraints.NotBlank; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java index ef9058ed5..b42ce7679 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java @@ -1,7 +1,7 @@ package org.dromara.demo.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java index e7ea8075c..c6595b0e2 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java @@ -1,7 +1,7 @@ package org.dromara.demo.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelNotation; import org.dromara.common.excel.annotation.ExcelRequired; import org.dromara.common.translation.annotation.Translation; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java index 58b4bdbc0..ee2336ac5 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java @@ -1,7 +1,7 @@ package org.dromara.demo.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.demo.domain.TestTree; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java index 7bd4e1eea..de927609f 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java @@ -1,7 +1,7 @@ package org.dromara.demo.listener; import cn.hutool.core.util.NumberUtil; -import com.alibaba.excel.context.AnalysisContext; +import cn.idev.excel.context.AnalysisContext; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java index 6b9e60617..b397f37f4 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java @@ -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.QueryWrapper; 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.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; @@ -168,12 +167,7 @@ public class GenTableServiceImpl implements IGenTableService { return gen; }).sorted(Comparator.comparing(GenTable::getCreateTime).reversed()) .toList(); - - IPage page = pageQuery.build(); - page.setTotal(tables.size()); - // 手动分页 set数据 - page.setRecords(CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables)); - return TableDataInfo.build(page); + return TableDataInfo.build(tables, pageQuery.build()); } /** diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 48cc8b15b..be6c3bf7b 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -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.toolkit.Wrappers; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.domain.vo.${ClassName}Vo; @@ -27,6 +28,7 @@ import java.util.Collection; * @author ${author} * @date ${datetime} */ +@Slf4j @RequiredArgsConstructor @Service public class ${ClassName}ServiceImpl implements I${ClassName}Service { diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm index 5591f9778..480394c72 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm @@ -4,8 +4,8 @@ package ${packageName}.domain.vo; import ${import}; #end import ${packageName}.domain.${ClassName}; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import io.github.linpeilie.annotations.AutoMapper; diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index caf3472e1..4570d46db 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -80,6 +80,7 @@ v-loading="loading" :data="${businessName}List" row-key="${treeCode}" + border :default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index a92d19adc..df51ed063 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -82,7 +82,7 @@ - + #foreach($column in $columns) #set($javaField=$column.javaField) diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java new file mode 100644 index 000000000..2661e3465 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java @@ -0,0 +1,30 @@ +package org.dromara.job.entity; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class BillDto { + + /** + * 账单ID + */ + private Long billId; + + /** + * 账单渠道 + */ + private String billChannel; + + /** + * 账单日期 + */ + private String billDate; + + /** + * 账单金额 + */ + private BigDecimal billAmount; + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java new file mode 100644 index 000000000..b8ad8cc39 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java @@ -0,0 +1,42 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟支付宝账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "alipayBillTask") +public class AlipayBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + BillDto billDto = new BillDto(); + billDto.setBillId(23456789L); + billDto.setBillChannel("alipay"); + // 设置清算日期 + String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); + if (StrUtil.equals(settlementDate, "sysdate")) { + settlementDate = DateUtil.today(); + } + billDto.setBillDate(settlementDate); + billDto.setBillAmount(new BigDecimal("2345.67")); + // 把billDto对象放入上下文进行传递 + jobArgs.appendContext("alipay", JsonUtils.toJsonString(billDto)); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(billDto); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java new file mode 100644 index 000000000..bff15f97e --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java @@ -0,0 +1,45 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟汇总账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "summaryBillTask") +public class SummaryBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + // 获得微信账单 + BigDecimal wechatAmount = BigDecimal.valueOf(0); + String wechat = (String) jobArgs.getWfContext("wechat"); + if (StrUtil.isNotBlank(wechat)) { + BillDto wechatBillDto = JsonUtils.parseObject(wechat, BillDto.class); + wechatAmount = wechatBillDto.getBillAmount(); + } + // 获得支付宝账单 + BigDecimal alipayAmount = BigDecimal.valueOf(0); + String alipay = (String) jobArgs.getWfContext("alipay"); + if (StrUtil.isNotBlank(alipay)) { + BillDto alipayBillDto = JsonUtils.parseObject(alipay, BillDto.class); + alipayAmount = alipayBillDto.getBillAmount(); + } + // 汇总账单 + BigDecimal totalAmount = wechatAmount.add(alipayAmount); + SnailJobLog.REMOTE.info("总金额: {}", totalAmount); + return ExecuteResult.success(totalAmount); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java index 5bea9daf3..e5339f5b4 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java @@ -8,8 +8,10 @@ import com.aizuda.snailjob.common.log.SnailJobLog; import org.springframework.stereotype.Component; /** - * @author opensnail - * @date 2024-05-17 + * 正常任务 + * + * + * @author 老马 */ @Component @JobExecutor(name = "testJobExecutor") diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java new file mode 100644 index 000000000..d77e72e6c --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java @@ -0,0 +1,37 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.util.RandomUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 广播任务 + * + * + * @author 老马 + */ +@Slf4j +@Component +@JobExecutor(name = "testBroadcastJob") +public class TestBroadcastJob { + + @Value("${snail-job.port}") + private int clientPort; + + public ExecuteResult jobExecute(JobArgs jobArgs) { + int randomInt = RandomUtil.randomInt(100); + log.info("随机数: {}", randomInt); + SnailJobLog.REMOTE.info("随机数: {},客户端端口:{}", randomInt, clientPort); + if (randomInt < 50) { + throw new RuntimeException("随机数小于50,收集日志任务执行失败"); + } + // 获得jobArgs 中传入的相加的两个数 + return ExecuteResult.success("随机数大于50,收集日志任务执行成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java new file mode 100644 index 000000000..6589ed1c5 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java @@ -0,0 +1,53 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.MapHandler; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; +import com.aizuda.snailjob.client.job.core.dto.MapArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Map任务 动态分配 只分片不关注结果 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testMapJobAnnotation") +public class TestMapJobAnnotation { + + @MapExecutor + public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + // 生成1~200数值并分片 + int partitionSize = 50; + List> partition = IntStream.rangeClosed(1, 200) + .boxed() + .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + .values() + .stream() + .toList(); + SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); + return mapHandler.doMap(partition, "doCalc"); + } + + @MapExecutor(taskName = "doCalc") + public ExecuteResult doCalc(MapArgs mapArgs) { + List sourceList = (List) mapArgs.getMapResult(); + // 遍历sourceList的每一个元素,计算出一个累加值partitionTotal + int partitionTotal = sourceList.stream().mapToInt(i -> i).sum(); + // 打印日志到服务器 + ThreadUtil.sleep(3, TimeUnit.SECONDS); + SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal); + return ExecuteResult.success(partitionTotal); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java new file mode 100644 index 000000000..4ae2fa80a --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java @@ -0,0 +1,60 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.MapHandler; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; +import com.aizuda.snailjob.client.job.core.annotation.ReduceExecutor; +import com.aizuda.snailjob.client.job.core.dto.MapArgs; +import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * MapReduce任务 动态分配 分片后合并结果 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testMapReduceAnnotation1") +public class TestMapReduceAnnotation1 { + + @MapExecutor + public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + int partitionSize = 50; + List> partition = IntStream.rangeClosed(1, 200) + .boxed() + .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + .values() + .stream() + .toList(); + SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); + return mapHandler.doMap(partition, "doCalc"); + } + + @MapExecutor(taskName = "doCalc") + public ExecuteResult doCalc(MapArgs mapArgs) { + List sourceList = (List) mapArgs.getMapResult(); + // 遍历sourceList的每一个元素,计算出一个累加值partitionTotal + int partitionTotal = sourceList.stream().mapToInt(i -> i).sum(); + // 打印日志到服务器 + ThreadUtil.sleep(3, TimeUnit.SECONDS); + SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal); + return ExecuteResult.success(partitionTotal); + } + + @ReduceExecutor + public ExecuteResult reduceExecute(ReduceArgs reduceArgs) { + int reduceTotal = reduceArgs.getMapResult().stream().mapToInt(i -> Integer.parseInt((String) i)).sum(); + SnailJobLog.REMOTE.info("端口:{},reduceTotal:{}", SpringUtil.getProperty("server.port"), reduceTotal); + return ExecuteResult.success(reduceTotal); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java new file mode 100644 index 000000000..07a1bc566 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java @@ -0,0 +1,36 @@ +package org.dromara.job.snailjob; + +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +/** + * 静态分片 根据服务端任务参数分片 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testStaticShardingJob") +public class TestStaticShardingJob { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + String jobParams = String.valueOf(jobArgs.getJobParams()); + SnailJobLog.LOCAL.info("开始执行分片任务,参数:{}", jobParams); + // 获得jobArgs 中传入的开始id和结束id + String[] split = jobParams.split(","); + Long fromId = Long.parseLong(split[0]); + Long toId = Long.parseLong(split[1]); + // 模拟数据库操作,对范围id,进行加密处理 + try { + SnailJobLog.REMOTE.info("开始对id范围:{}进行加密处理", fromId + "-" + toId); + Thread.sleep(3000); + SnailJobLog.REMOTE.info("对id范围:{}进行加密处理完成", fromId + "-" + toId); + } catch (InterruptedException e) { + return ExecuteResult.failure("任务执行失败"); + } + return ExecuteResult.success("执行分片任务完成"); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java new file mode 100644 index 000000000..d8caf1af0 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java @@ -0,0 +1,43 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟微信账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "wechatBillTask") +public class WechatBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + BillDto billDto = new BillDto(); + billDto.setBillId(123456789L); + billDto.setBillChannel("wechat"); + // 从上下文中获得清算日期并设置,如果上下文中清算日期 + // 是sysdate设置为当前日期;否则取管理页面设置的值 + String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); + if (StrUtil.equals(settlementDate, "sysdate")) { + settlementDate = DateUtil.today(); + } + billDto.setBillDate(settlementDate); + billDto.setBillAmount(new BigDecimal("1234.56")); + // 把billDto对象放入上下文进行传递 + jobArgs.appendContext("wechat", JsonUtils.toJsonString(billDto)); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(billDto); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java index 6b7499ace..1e9c6558b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java @@ -1,10 +1,9 @@ package org.dromara.system.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; -import org.dromara.system.domain.vo.CacheListInfoVo; -import lombok.RequiredArgsConstructor; import org.redisson.spring.data.connection.RedissonConnectionFactory; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.web.bind.annotation.GetMapping; @@ -45,11 +44,11 @@ public class CacheController { }); } - CacheListInfoVo infoVo = new CacheListInfoVo(); - infoVo.setInfo(connection.commands().info()); - infoVo.setDbSize(connection.commands().dbSize()); - infoVo.setCommandStats(pieList); - return R.ok(infoVo); + return R.ok(new CacheListInfoVo( + connection.commands().info(), + connection.commands().dbSize(), pieList)); } + public record CacheListInfoVo(Properties info, Long dbSize, List> commandStats) {} + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java index d8cd335ce..3171ec390 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java @@ -15,7 +15,6 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.SysMenu; import org.dromara.system.domain.bo.SysMenuBo; -import org.dromara.system.domain.vo.MenuTreeSelectVo; import org.dromara.system.domain.vo.RouterVo; import org.dromara.system.domain.vo.SysMenuVo; import org.dromara.system.service.ISysMenuService; @@ -52,8 +51,8 @@ public class SysMenuController extends BaseController { * 获取菜单列表 */ @SaCheckRole(value = { - TenantConstants.SUPER_ADMIN_ROLE_KEY, - TenantConstants.TENANT_ADMIN_ROLE_KEY + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY }, mode = SaMode.OR) @SaCheckPermission("system:menu:list") @GetMapping("/list") @@ -68,8 +67,8 @@ public class SysMenuController extends BaseController { * @param menuId 菜单ID */ @SaCheckRole(value = { - TenantConstants.SUPER_ADMIN_ROLE_KEY, - TenantConstants.TENANT_ADMIN_ROLE_KEY + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY }, mode = SaMode.OR) @SaCheckPermission("system:menu:query") @GetMapping(value = "/{menuId}") @@ -96,9 +95,9 @@ public class SysMenuController extends BaseController { @GetMapping(value = "/roleMenuTreeselect/{roleId}") public R roleMenuTreeselect(@PathVariable("roleId") Long roleId) { List menus = menuService.selectMenuList(LoginHelper.getUserId()); - MenuTreeSelectVo selectVo = new MenuTreeSelectVo(); - selectVo.setCheckedKeys(menuService.selectMenuListByRoleId(roleId)); - selectVo.setMenus(menuService.buildMenuTreeSelect(menus)); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo( + menuService.selectMenuListByRoleId(roleId), + menuService.buildMenuTreeSelect(menus)); return R.ok(selectVo); } @@ -112,9 +111,9 @@ public class SysMenuController extends BaseController { @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}") public R tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) { List menus = menuService.selectMenuList(LoginHelper.getUserId()); - MenuTreeSelectVo selectVo = new MenuTreeSelectVo(); - selectVo.setCheckedKeys(menuService.selectMenuListByPackageId(packageId)); - selectVo.setMenus(menuService.buildMenuTreeSelect(menus)); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo( + menuService.selectMenuListByPackageId(packageId), + menuService.buildMenuTreeSelect(menus)); return R.ok(selectVo); } @@ -171,4 +170,25 @@ public class SysMenuController extends BaseController { return toAjax(menuService.deleteMenuById(menuId)); } + public record MenuTreeSelectVo(List checkedKeys, List> menus) { + } + + /** + * 批量级联删除菜单 + * + * @param menuIds 菜单ID串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/cascade/{menuIds}") + public R remove(@PathVariable("menuIds") Long[] menuIds) { + List menuIdList = List.of(menuIds); + if (menuService.hasChildByMenuId(menuIdList)) { + return R.warn("存在子菜单,不允许删除"); + } + menuService.deleteMenuById(menuIdList); + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index 5f187cb91..8903a7406 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -1,8 +1,8 @@ package org.dromara.system.controller.system; -import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.crypto.digest.BCrypt; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; @@ -17,8 +17,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.bo.SysUserPasswordBo; import org.dromara.system.domain.bo.SysUserProfileBo; -import org.dromara.system.domain.vo.AvatarVo; -import org.dromara.system.domain.vo.ProfileVo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysOssService; @@ -50,10 +48,9 @@ public class SysProfileController extends BaseController { @GetMapping public R profile() { SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); - ProfileVo profileVo = new ProfileVo(); - profileVo.setUser(user); - profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId())); - profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId())); + String roleGroup = userService.selectUserRoleGroup(user.getUserId()); + String postGroup = userService.selectUserPostGroup(user.getUserId()); + ProfileVo profileVo = new ProfileVo(user, roleGroup, postGroup); return R.ok(profileVo); } @@ -123,11 +120,14 @@ public class SysProfileController extends BaseController { String avatar = oss.getUrl(); boolean updateSuccess = DataPermissionHelper.ignore(() -> userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())); if (updateSuccess) { - AvatarVo avatarVo = new AvatarVo(); - avatarVo.setImgUrl(avatar); - return R.ok(avatarVo); + return R.ok(new AvatarVo(avatar)); } } return R.fail("上传图片异常,请联系管理员"); } + + public record AvatarVo(String imgUrl) {} + + public record ProfileVo(SysUserVo user, String roleGroup, String postGroup) {} + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index d4a9dc8a4..e6afa3c07 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -1,6 +1,7 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.lang.tree.Tree; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; @@ -14,7 +15,6 @@ import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.bo.SysRoleBo; import org.dromara.system.domain.bo.SysUserBo; -import org.dromara.system.domain.vo.DeptTreeSelectVo; import org.dromara.system.domain.vo.SysRoleVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysDeptService; @@ -221,9 +221,12 @@ public class SysRoleController extends BaseController { @SaCheckPermission("system:role:list") @GetMapping(value = "/deptTree/{roleId}") public R roleDeptTreeselect(@PathVariable("roleId") Long roleId) { - DeptTreeSelectVo selectVo = new DeptTreeSelectVo(); - selectVo.setCheckedKeys(deptService.selectDeptListByRoleId(roleId)); - selectVo.setDepts(deptService.selectDeptTreeList(new SysDeptBo())); + DeptTreeSelectVo selectVo = new DeptTreeSelectVo( + deptService.selectDeptListByRoleId(roleId), + deptService.selectDeptTreeList(new SysDeptBo())); return R.ok(selectVo); } + + public record DeptTreeSelectVo(List checkedKeys, List> depts) {} + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index e1e868a6e..774f26a3e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -1,10 +1,10 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java deleted file mode 100644 index 46c020b7c..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.dromara.system.domain.vo; - -import lombok.Data; - -/** - * 用户头像信息 - * - * @author Michelle.Chung - */ -@Data -public class AvatarVo { - - /** - * 头像地址 - */ - private String imgUrl; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java deleted file mode 100644 index f827cba04..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.dromara.system.domain.vo; - -import lombok.Data; - -import java.util.List; -import java.util.Map; -import java.util.Properties; - -/** - * 缓存监控列表信息 - * - * @author Michelle.Chung - */ -@Data -public class CacheListInfoVo { - - private Properties info; - - private Long dbSize; - - private List> commandStats; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java deleted file mode 100644 index 6f7db2869..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.dromara.system.domain.vo; - -import cn.hutool.core.lang.tree.Tree; -import lombok.Data; - -import java.util.List; - -/** - * 角色部门列表树信息 - * - * @author Michelle.Chung - */ -@Data -public class DeptTreeSelectVo { - - /** - * 选中部门列表 - */ - private List checkedKeys; - - /** - * 下拉树结构列表 - */ - private List> depts; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java deleted file mode 100644 index 072453853..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.dromara.system.domain.vo; - -import cn.hutool.core.lang.tree.Tree; -import lombok.Data; - -import java.util.List; - -/** - * 角色菜单列表树信息 - * - * @author Michelle.Chung - */ -@Data -public class MenuTreeSelectVo { - - /** - * 选中菜单列表 - */ - private List checkedKeys; - - /** - * 菜单下拉树结构列表 - */ - private List> menus; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java index f720cd74f..b2c11485c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java @@ -25,7 +25,7 @@ public class MetaVo { /** * 设置为true,则不会被 缓存 */ - private boolean noCache; + private Boolean noCache; /** * 内链地址(http(s)://开头) @@ -37,7 +37,7 @@ public class MetaVo { this.icon = icon; } - public MetaVo(String title, String icon, boolean noCache) { + public MetaVo(String title, String icon, Boolean noCache) { this.title = title; this.icon = icon; this.noCache = noCache; @@ -49,7 +49,7 @@ public class MetaVo { this.link = link; } - public MetaVo(String title, String icon, boolean noCache, String link) { + public MetaVo(String title, String icon, Boolean noCache, String link) { this.title = title; this.icon = icon; this.noCache = noCache; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java deleted file mode 100644 index c0476519d..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.dromara.system.domain.vo; - -import lombok.Data; - -/** - * 用户个人信息 - * - * @author Michelle.Chung - */ -@Data -public class ProfileVo { - - /** - * 用户信息 - */ - private SysUserVo user; - - /** - * 用户所属角色组 - */ - private String roleGroup; - - /** - * 用户所属岗位组 - */ - private String postGroup; - - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java index 0d576ef15..d56e09da3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java @@ -27,7 +27,7 @@ public class RouterVo { /** * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 */ - private boolean hidden; + private Boolean hidden; /** * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java index 34f24eb36..82b0f46ca 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java @@ -1,8 +1,8 @@ package org.dromara.system.domain.vo; import org.dromara.system.domain.SysClient; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import io.github.linpeilie.annotations.AutoMapper; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java index f896000a3..a35e1323c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysConfig; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java index 1f182a248..c9f5a1faf 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.excel.annotation.ExcelDictFormat; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java index 83ea619af..d97c00d1b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysDictData; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java index e6a184fcf..4b62226e2 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysDictType; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java index de19aeabd..3086aa74a 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java @@ -1,8 +1,8 @@ package org.dromara.system.domain.vo; import java.util.Date; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysLogininfor; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java index d9eb71d40..00b334432 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysOperLog; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java index e7cfde4fe..a030722d5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java @@ -1,6 +1,6 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import org.dromara.system.domain.SysOssConfig; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java index 69be547a0..50140b6f1 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.excel.annotation.ExcelDictFormat; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java index 1913170ed..1a205cc74 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.core.constant.SystemConstants; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java index 070334b41..ead9229b9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysTenantPackage; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java index 6a453150e..021c66488 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java @@ -1,8 +1,8 @@ package org.dromara.system.domain.vo; import java.util.Date; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysTenant; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java index 37ec6b709..913ab2001 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java @@ -1,6 +1,6 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import io.github.linpeilie.annotations.AutoMapper; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java index c34a23cd9..d4e575c14 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java @@ -1,6 +1,6 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import lombok.Data; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java index 25b62a9d9..66db5f578 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java @@ -4,8 +4,8 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.crypto.digest.BCrypt; import cn.hutool.http.HtmlUtil; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import org.dromara.common.core.exception.ServiceException; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java index 0a657b42d..d754303d3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java @@ -1,8 +1,11 @@ package org.dromara.system.mapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysRoleMenu; +import java.util.List; + /** * 角色与菜单关联表 数据层 * @@ -10,4 +13,15 @@ import org.dromara.system.domain.SysRoleMenu; */ public interface SysRoleMenuMapper extends BaseMapperPlus { + /** + * 根据菜单ID串删除关联关系 + * + * @return 结果 + */ + default int deleteByMenuIds(List menuIds) { + LambdaUpdateWrapper lqw = new LambdaUpdateWrapper() + .in(SysRoleMenu::getMenuId, menuIds); + return this.delete(lqw); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java index 72d705e9b..f972691be 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java @@ -105,6 +105,14 @@ public interface ISysMenuService { */ boolean hasChildByMenuId(Long menuId); + /** + * 是否存在菜单子节点 + * + * @param menuIds 菜单ID串 + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(List menuIds); + /** * 查询菜单是否存在角色 * @@ -137,6 +145,14 @@ public interface ISysMenuService { */ int deleteMenuById(Long menuId); + /** + * 批量删除菜单管理信息 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + void deleteMenuById(List menuIds); + /** * 校验菜单名称是否唯一 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index 98aad216b..5e4904d05 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -39,6 +39,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * 部门管理 服务实现 @@ -92,6 +93,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { } private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { + Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(SysDept::getDelFlag, SystemConstants.NORMAL); lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); @@ -99,6 +101,8 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); lqw.like(StringUtils.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysDept::getCreateTime, params.get("beginTime"), params.get("endTime")); lqw.orderByAsc(SysDept::getAncestors); lqw.orderByAsc(SysDept::getParentId); lqw.orderByAsc(SysDept::getOrderNum); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java index 40643e13d..692b3ce00 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java @@ -28,6 +28,7 @@ import org.dromara.system.mapper.SysRoleMenuMapper; import org.dromara.system.mapper.SysTenantPackageMapper; import org.dromara.system.service.ISysMenuService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.*; @@ -71,6 +72,8 @@ public class SysMenuServiceImpl implements ISysMenuService { .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) + .eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType()) + .eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId()) .orderByAsc(SysMenu::getParentId) .orderByAsc(SysMenu::getOrderNum)); } else { @@ -79,6 +82,8 @@ public class SysMenuServiceImpl implements ISysMenuService { .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) + .eq(StringUtils.isNotBlank(menu.getMenuType()), "m.menu_type", menu.getMenuType()) + .eq(ObjectUtil.isNotNull(menu.getParentId()), "m.parent_id", menu.getParentId()) .orderByAsc("m.parent_id") .orderByAsc("m.order_num"); List list = baseMapper.selectMenuListByUserId(wrapper); @@ -169,11 +174,15 @@ public class SysMenuServiceImpl implements ISysMenuService { if (tenantPackage.getMenuCheckStrictly()) { parentIds = baseMapper.selectObjs(new LambdaQueryWrapper() .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() .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); + }); } /** @@ -274,6 +283,17 @@ public class SysMenuServiceImpl implements ISysMenuService { return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); } + /** + * 是否存在菜单子节点 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(List menuIds) { + return baseMapper.exists(new LambdaQueryWrapper().in(SysMenu::getParentId, menuIds).notIn(SysMenu::getMenuId, menuIds)); + } + /** * 查询菜单使用数量 * @@ -320,6 +340,19 @@ public class SysMenuServiceImpl implements ISysMenuService { return baseMapper.deleteById(menuId); } + /** + * 批量删除菜单管理信息 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteMenuById(List menuIds) { + baseMapper.deleteByIds(menuIds); + roleMenuMapper.deleteByMenuIds(menuIds); + } + /** * 校验菜单名称是否唯一 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java index 9852821e6..4bf697420 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java @@ -1,11 +1,12 @@ package org.dromara.system.service.impl; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.TenantConstants; +import org.dromara.common.core.service.PermissionService; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.service.ISysMenuService; import org.dromara.system.service.ISysPermissionService; import org.dromara.system.service.ISysRoleService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.HashSet; @@ -18,7 +19,7 @@ import java.util.Set; */ @RequiredArgsConstructor @Service -public class SysPermissionServiceImpl implements ISysPermissionService { +public class SysPermissionServiceImpl implements ISysPermissionService, PermissionService { private final ISysRoleService roleService; private final ISysMenuService menuService; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index 894f6acc8..122492e0c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -27,6 +27,7 @@ import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * 岗位信息 服务层处理 @@ -76,11 +77,14 @@ public class SysPostServiceImpl implements ISysPostService, PostService { * @return 构建好的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysPostBo bo) { + Map params = bo.getParams(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode()) .like(StringUtils.isNotBlank(bo.getPostCategory()), SysPost::getPostCategory, bo.getPostCategory()) .like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName()) .eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysPost::getCreateTime, params.get("beginTime"), params.get("endTime")) .orderByAsc(SysPost::getPostSort); if (ObjectUtil.isNotNull(bo.getDeptId())) { //优先单部门搜索 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java index d4f2c0d4e..31e9be902 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java @@ -49,8 +49,8 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { public TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery) { PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); SysRoleBo bo = new SysRoleBo(); - bo.setRoleName(taskQuery.getHandlerCode()); - bo.setRoleKey(taskQuery.getHandlerName()); + bo.setRoleKey(taskQuery.getHandlerCode()); + bo.setRoleName(taskQuery.getHandlerName()); Map params = bo.getParams(); params.put("beginTime", taskQuery.getBeginTime()); params.put("endTime", taskQuery.getEndTime()); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java index a73415c64..ca088607b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java @@ -1,11 +1,11 @@ package org.dromara.system.service.impl; -import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index cecd7f56f..cc2cc88a9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -35,10 +35,8 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; /** * 用户 业务层处理 @@ -81,6 +79,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { .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.getNickName()), "u.nick_name", user.getNickName()) .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) .between(params.get("beginTime") != null && params.get("endTime") != null, @@ -720,4 +719,80 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return selectListByIds(new ArrayList<>(userIds)); } + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + @Override + public Map selectUserNamesByIds(List userIds) { + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyMap(); + } + return baseMapper.selectList( + new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getNickName) + .in(SysUser::getUserId, userIds) + ).stream() + .collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName)); + } + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + @Override + public Map selectRoleNamesByIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptyMap(); + } + return roleMapper.selectList( + new LambdaQueryWrapper() + .select(SysRole::getRoleId, SysRole::getRoleName) + .in(SysRole::getRoleId, roleIds) + ).stream() + .collect(Collectors.toMap(SysRole::getRoleId, SysRole::getRoleName)); + } + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + @Override + public Map selectDeptNamesByIds(List deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return Collections.emptyMap(); + } + return deptMapper.selectList( + new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName) + .in(SysDept::getDeptId, deptIds) + ).stream() + .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName)); + } + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + @Override + public Map selectPostNamesByIds(List postIds) { + if (CollUtil.isEmpty(postIds)) { + return Collections.emptyMap(); + } + return postMapper.selectList( + new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName) + .in(SysPost::getPostId, postIds) + ).stream() + .collect(Collectors.toMap(SysPost::getPostId, SysPost::getPostName)); + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java index 5d24b3509..e844398e2 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java @@ -7,6 +7,21 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * 自定义条件注解,用于基于配置启用或禁用特定功能 + *

+ * 该注解只会在配置文件中 `warm-flow.enabled=true` 时,标注了此注解的类或方法才会被 Spring 容器加载 + *

+ * 示例配置: + *

+ * warm-flow:
+ *   enabled: true  # 设置为 true 时,启用工作流功能
+ * 
+ *

+ * 使用此注解时,可以动态控制工作流功能是否启用,而不需要修改代码逻辑 + * + * @author Lion Li + */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true") diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java index 1b10eb8ea..f3290b841 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -13,21 +13,11 @@ public interface FlowConstant { */ String INITIATOR = "initiator"; - /** - * 流程实例id - */ - String PROCESS_INSTANCE_ID = "processInstanceId"; - /** * 业务id */ String BUSINESS_ID = "businessId"; - /** - * 任务id - */ - String TASK_ID = "taskId"; - /** * 委托 */ @@ -63,4 +53,24 @@ public interface FlowConstant { */ Long FLOW_CATEGORY_ID = 100L; + /** + * 是否为申请人提交常量 + */ + String SUBMIT = "submit"; + + /** + * 抄送常量 + */ + String FLOW_COPY_LIST = "flowCopyList"; + + /** + * 消息类型常量 + */ + String MESSAGE_TYPE = "messageType"; + + /** + * 消息通知常量 + */ + String MESSAGE_NOTICE = "messageNotice"; + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java index 7a224051d..598cd05ce 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java @@ -50,7 +50,12 @@ public enum ButtonPermissionEnum implements NodeExtEnum { /** * 是否能终止 */ - TERMINATION("是否能终止", "termination", true); + TERMINATION("是否能终止", "termination", true), + + /** + * 是否能上传附件 + */ + FILE("是否能上传附件", "file", true); private final String label; private final String value; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java index 37d414fcd..3018b0848 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java @@ -14,6 +14,7 @@ import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.web.core.BaseController; 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.vo.FlowCategoryVo; import org.dromara.workflow.service.IFlwCategoryService; @@ -110,6 +111,9 @@ public class FlwCategoryController extends BaseController { @Log(title = "流程分类", businessType = BusinessType.DELETE) @DeleteMapping("/{categoryId}") public R remove(@PathVariable Long categoryId) { + if (FlowConstant.FLOW_CATEGORY_ID.equals(categoryId)) { + return R.warn("默认流程分类,不允许删除"); + } if (flwCategoryService.hasChildByCategoryId(categoryId)) { return R.warn("存在下级流程分类,不允许删除"); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java index ae99c16fa..e7ea5e4cb 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java @@ -127,9 +127,9 @@ public class FlwInstanceController extends BaseController { * * @param businessId 业务id */ - @GetMapping("/flowImage/{businessId}") - public R> flowImage(@PathVariable String businessId) { - return R.ok(flwInstanceService.flowImage(businessId)); + @GetMapping("/flowHisTaskList/{businessId}") + public R> flowHisTaskList(@PathVariable String businessId) { + return R.ok(flwInstanceService.flowHisTaskList(businessId)); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java index 3117a33f5..654bf0800 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java @@ -43,7 +43,6 @@ public class BackProcessBo implements Serializable { /** * 驳回的节点id(目前未使用,直接驳回到申请人) */ - @NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class) private String nodeCode; /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java index a1a4b5968..395f71d95 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java @@ -10,6 +10,7 @@ import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.workflow.domain.TestLeave; +import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; @@ -40,6 +41,7 @@ public class TestLeaveBo extends BaseEntity { * 开始时间 */ @NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd") private Date startDate; @@ -47,6 +49,7 @@ public class TestLeaveBo extends BaseEntity { * 结束时间 */ @NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd") private Date endDate; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermission.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java similarity index 57% rename from ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermission.java rename to ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java index 51f320d2d..7175e5e09 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermission.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java @@ -12,23 +12,32 @@ import java.io.Serializable; * @date 2025-02-28 */ @Data -public class ButtonPermission implements Serializable { +public class ButtonPermissionVo implements Serializable { @Serial private static final long serialVersionUID = 1L; /** - * 枚举路径 + * 唯一编码 */ private String code; /** - * 按钮编码 + * 选项值 */ private String value; /** * 是否显示 */ - private boolean show; + private Boolean show; + + public ButtonPermissionVo() { + } + + public ButtonPermissionVo(String code, Boolean show) { + this.code = code; + this.show = show; + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java index f1c4caad5..db236c2df 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java @@ -1,7 +1,7 @@ package org.dromara.workflow.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.workflow.domain.FlowCategory; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java index 785e866c8..07a22c4f3 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java @@ -1,20 +1,16 @@ package org.dromara.workflow.domain.vo; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.json.JSONUtil; import lombok.Data; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.constant.TransConstant; import org.dromara.warm.flow.core.entity.User; import org.dromara.workflow.common.constant.FlowConstant; -import org.dromara.workflow.common.enums.ButtonPermissionEnum; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Date; +import java.util.List; /** * 任务视图 @@ -181,35 +177,11 @@ public class FlowTaskVo implements Serializable { /** * 是否为申请人节点 */ - private boolean applyNode; + private Boolean applyNode; /** * 按钮权限 */ - private List buttonList; + private List buttonList; - public List getButtonList(String ext) { - List buttonPermissions = Arrays.stream(ButtonPermissionEnum.values()) - .map(value -> { - ButtonPermission buttonPermission = new ButtonPermission(); - buttonPermission.setCode(value.getValue()); - buttonPermission.setShow(false); - return buttonPermission; - }) - .collect(Collectors.toList()); - if (StringUtils.isNotBlank(ext)) { - List buttonCodeList = JSONUtil.toList(JSONUtil.parseArray(ext), ButtonPermission.class); - if (CollUtil.isNotEmpty(buttonCodeList)) { - Optional firstPermission = buttonCodeList.stream().findFirst(); - firstPermission.ifPresent(permission -> { - Set codeSet = Arrays.stream(permission.getValue().split(",")) - .map(String::trim) - .filter(code -> !code.isEmpty()) - .collect(Collectors.toSet()); - buttonPermissions.forEach(bp -> bp.setShow(codeSet.contains(bp.getCode()))); - }); - } - } - return buttonPermissions; - } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java deleted file mode 100644 index b4de76e93..000000000 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.dromara.workflow.domain.vo; - -import lombok.Data; - -import java.io.Serial; -import java.io.Serializable; - -/** - * 流程变量 - * - * @author may - */ -@Data -public class FlowVariableVo implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 变量key - */ - private String key; - - /** - * 变量值 - */ - private String value; -} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java index 47886d721..57777edde 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java @@ -1,7 +1,8 @@ package org.dromara.workflow.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.workflow.domain.TestLeave; @@ -41,12 +42,14 @@ public class TestLeaveVo implements Serializable { * 开始时间 */ @ExcelProperty(value = "开始时间") + @JsonFormat(pattern = "yyyy-MM-dd") private Date startDate; /** * 结束时间 */ @ExcelProperty(value = "结束时间") + @JsonFormat(pattern = "yyyy-MM-dd") private Date endDate; /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java index 3efc52da8..c465271ca 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java @@ -1,11 +1,12 @@ package org.dromara.workflow.handler; 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.ProcessEvent; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.warm.flow.core.entity.Instance; import org.dromara.workflow.common.ConditionalOnEnable; import org.springframework.stereotype.Component; @@ -26,17 +27,22 @@ public class FlowProcessEventHandler { * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) * * @param flowCode 流程定义编码 - * @param businessId 业务id - * @param status 状态 + * @param instance 实例数据 + * @param status 流程状态 + * @param params 办理参数 * @param submit 当为true时为申请人节点办理 */ - public void processHandler(String flowCode, String businessId, String status, Map params, boolean submit) { + public void processHandler(String flowCode, Instance instance, String status, Map params, boolean submit) { String tenantId = TenantHelper.getTenantId(); - log.info("发布流程事件,租户ID: {}, 流程状态: {}, 流程编码: {}, 业务ID: {}, 是否申请人节点办理: {}", tenantId, status, flowCode, businessId, submit); + log.info("【流程事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 流程状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}", + tenantId, flowCode, instance.getBusinessId(), status, instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), submit, params); ProcessEvent processEvent = new ProcessEvent(); processEvent.setTenantId(tenantId); processEvent.setFlowCode(flowCode); - processEvent.setBusinessId(businessId); + processEvent.setBusinessId(instance.getBusinessId()); + processEvent.setNodeType(instance.getNodeType()); + processEvent.setNodeCode(instance.getNodeCode()); + processEvent.setNodeName(instance.getNodeName()); processEvent.setStatus(status); processEvent.setParams(params); processEvent.setSubmit(submit); @@ -47,20 +53,23 @@ public class FlowProcessEventHandler { * 执行创建任务监听 * * @param flowCode 流程定义编码 - * @param nodeCode 审批节点编码 + * @param instance 实例数据 * @param taskId 任务id - * @param businessId 业务id */ - public void processCreateTaskHandler(String flowCode, String nodeCode, Long taskId, String businessId) { + public void processTaskHandler(String flowCode, Instance instance, Long taskId) { String tenantId = TenantHelper.getTenantId(); - log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 节点编码: {}, 任务ID: {}, 业务ID: {}", tenantId, flowCode, nodeCode, taskId, businessId); - ProcessCreateTaskEvent processCreateTaskEvent = new ProcessCreateTaskEvent(); - processCreateTaskEvent.setTenantId(tenantId); - processCreateTaskEvent.setFlowCode(flowCode); - processCreateTaskEvent.setNodeCode(nodeCode); - processCreateTaskEvent.setTaskId(taskId); - processCreateTaskEvent.setBusinessId(businessId); - SpringUtils.context().publishEvent(processCreateTaskEvent); + log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", + tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); + ProcessTaskEvent processTaskEvent = new ProcessTaskEvent(); + processTaskEvent.setTenantId(tenantId); + processTaskEvent.setFlowCode(flowCode); + processTaskEvent.setBusinessId(instance.getBusinessId()); + processTaskEvent.setNodeType(instance.getNodeType()); + processTaskEvent.setNodeCode(instance.getNodeCode()); + processTaskEvent.setNodeName(instance.getNodeName()); + processTaskEvent.setTaskId(taskId); + processTaskEvent.setStatus(instance.getFlowStatus()); + SpringUtils.context().publishEvent(processTaskEvent); } /** @@ -71,7 +80,7 @@ public class FlowProcessEventHandler { */ public void processDeleteHandler(String flowCode, String businessId) { String tenantId = TenantHelper.getTenantId(); - log.info("发布删除流程事件, 租户ID: {}, 流程编码: {}, 业务ID: {}", tenantId, flowCode, businessId); + log.info("【流程删除事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}", tenantId, flowCode, businessId); ProcessDeleteEvent processDeleteEvent = new ProcessDeleteEvent(); processDeleteEvent.setTenantId(tenantId); processDeleteEvent.setFlowCode(flowCode); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java index c18e4ed52..f9ede15ce 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java @@ -1,22 +1,17 @@ package org.dromara.workflow.handler; -import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.collection.CollUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.model.LoginUser; -import org.dromara.workflow.common.ConditionalOnEnable; -import org.dromara.workflow.common.enums.TaskAssigneeEnum; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.dto.FlowParams; 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.service.IFlwCommonService; import org.springframework.stereotype.Component; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 办理人权限处理器 @@ -29,35 +24,16 @@ import java.util.stream.Stream; @Slf4j public class WorkflowPermissionHandler implements PermissionHandler { + private final IFlwCommonService flwCommonService; + /** - * 审批前获取当前办理人,办理时会校验的该权限集合 - * 后续在{@link TaskServiceImpl#checkAuth(Task, FlowParams)} 中调用 + * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务 + * 后续在{@link FlowParams#getPermissionFlag} 中获取 * 返回当前用户权限集合 */ @Override public List permissions() { - LoginUser loginUser = LoginHelper.getLoginUser(); - if (ObjectUtil.isNull(loginUser)) { - return new ArrayList<>(); - } - // 使用一个流来构建权限列表 - return Stream.of( - // 角色权限前缀 - loginUser.getRoles().stream() - .map(role -> TaskAssigneeEnum.ROLE.getCode() + role.getRoleId()), - - // 岗位权限前缀 - Stream.ofNullable(loginUser.getPosts()) - .flatMap(Collection::stream) - .map(post -> TaskAssigneeEnum.POST.getCode() + post.getPostId()), - - // 用户和部门权限 - Stream.of(String.valueOf(loginUser.getUserId()), - TaskAssigneeEnum.DEPT.getCode() + loginUser.getDeptId() - ) - ) - .flatMap(stream -> stream) - .collect(Collectors.toList()); + return Collections.singletonList(LoginHelper.getUserIdStr()); } /** @@ -70,4 +46,14 @@ public class WorkflowPermissionHandler implements PermissionHandler { return LoginHelper.getUserIdStr(); } + /** + * 转换办理人,比如设计器中预设了能办理的人,如果其中包含角色或者部门id等,可以通过此接口进行转换成用户id + */ + @Override + public List convertPermissions(List permissions) { + if (CollUtil.isNotEmpty(permissions)) { + permissions = flwCommonService.buildUser(permissions); + } + return permissions; + } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 272f9de92..027865bc8 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -1,20 +1,27 @@ package org.dromara.workflow.listener; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.enums.BusinessStatusEnum; 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.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.listener.GlobalListener; import org.dromara.warm.flow.core.listener.ListenerVariable; +import org.dromara.warm.flow.core.service.InsService; import org.dromara.warm.flow.orm.entity.FlowTask; 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.service.IFlwCommonService; import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Component; @@ -34,9 +41,11 @@ import java.util.Map; @RequiredArgsConstructor public class WorkflowGlobalListener implements GlobalListener { - private final IFlwTaskService taskService; + private final IFlwTaskService flwTaskService; private final IFlwInstanceService instanceService; private final FlowProcessEventHandler flowProcessEventHandler; + private final IFlwCommonService flwCommonService; + private final InsService insService; /** * 创建监听器,任务创建时执行 @@ -45,15 +54,7 @@ public class WorkflowGlobalListener implements GlobalListener { */ @Override public void create(ListenerVariable listenerVariable) { - Instance instance = listenerVariable.getInstance(); - Definition definition = listenerVariable.getDefinition(); - String businessId = instance.getBusinessId(); - String flowStatus = instance.getFlowStatus(); - Task task = listenerVariable.getTask(); - if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(flowStatus)) { - // 判断流程状态(发布审批中事件) - flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), task.getNodeCode(), task.getId(), businessId); - } + } /** @@ -72,6 +73,25 @@ public class WorkflowGlobalListener implements GlobalListener { */ @Override public void assignment(ListenerVariable listenerVariable) { + Map variable = listenerVariable.getVariable(); + List 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())); + } + } } /** @@ -83,8 +103,7 @@ public class WorkflowGlobalListener implements GlobalListener { public void finish(ListenerVariable listenerVariable) { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); - String businessId = instance.getBusinessId(); - String flowStatus = instance.getFlowStatus(); + Task task = listenerVariable.getTask(); Map params = new HashMap<>(); FlowParams flowParams = listenerVariable.getFlowParams(); if (ObjectUtil.isNotNull(flowParams)) { @@ -95,27 +114,67 @@ public class WorkflowGlobalListener implements GlobalListener { // 办理意见 params.put("message", flowParams.getMessage()); } - // 判断流程状态(发布:撤销,退回,作废,终止,已完成事件) - String status = determineFlowStatus(instance, flowStatus); - if (StringUtils.isNotBlank(status)) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), businessId, status, params, false); + Map variable = flowParams.getVariable(); + //申请人提交事件 + Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); + if (submit != null && submit) { + 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 flowCopyList = (List) variable.get(FlowConstant.FLOW_COPY_LIST); + // 添加抄送人 + flwTaskService.setCopy(task, flowCopyList); + } + if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { + List messageType = (List) 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); + } + } + Map variableMap = instance.getVariableMap(); + variableMap.remove(FlowConstant.FLOW_COPY_LIST); + variableMap.remove(FlowConstant.MESSAGE_TYPE); + variableMap.remove(FlowConstant.MESSAGE_NOTICE); + variableMap.remove(FlowConstant.SUBMIT); + instance.setVariable(FlowEngine.jsonConvert.objToStr(variableMap)); + insService.updateById(instance); + } } } /** - * 根据流程实例和当前流程状态确定最终状态 + * 根据流程实例确定最终状态 * - * @param instance 流程实例 - * @param flowStatus 流程实例当前状态 + * @param instance 流程实例 * @return 流程最终状态 */ - private String determineFlowStatus(Instance instance, String flowStatus) { + private String determineFlowStatus(Instance instance) { + String flowStatus = instance.getFlowStatus(); if (StringUtils.isNotBlank(flowStatus) && BusinessStatusEnum.initialState(flowStatus)) { log.info("流程实例当前状态: {}", flowStatus); return flowStatus; } else { Long instanceId = instance.getId(); - List flowTasks = taskService.selectByInstId(instanceId); + List flowTasks = flwTaskService.selectByInstId(instanceId); if (CollUtil.isEmpty(flowTasks)) { String status = BusinessStatusEnum.FINISH.getStatus(); // 更新流程状态为已完成 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java index 73201f43f..662d599eb 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java @@ -1,10 +1,6 @@ package org.dromara.workflow.service; -import org.dromara.warm.flow.core.entity.User; -import org.dromara.warm.flow.core.service.UserService; - import java.util.List; -import java.util.Set; /** * 通用 工作流服务 @@ -13,30 +9,13 @@ import java.util.Set; */ public interface IFlwCommonService { - /** - * 获取工作流用户service - * - * @return 工作流用户service - */ - UserService getFlowUserService(); - /** * 构建工作流用户 * - * @param userList 办理用户 - * @param taskId 任务ID + * @param permissionList 办理用户 * @return 用户 */ - Set buildUser(List userList, Long taskId); - - /** - * 构建工作流用户 - * - * @param userIdList 办理用户 - * @param taskId 任务ID - * @return 用户 - */ - Set buildFlowUser(List userIdList, Long taskId); + List buildUser(List permissionList); /** * 发送消息 @@ -48,17 +27,6 @@ public interface IFlwCommonService { */ void sendMessage(String flowName, Long instId, List messageType, String message); - /** - * 驳回 - * - * @param message 审批意见 - * @param instanceId 流程实例id - * @param targetNodeCode 目标节点 - * @param flowStatus 流程状态 - * @param flowHisStatus 节点操作状态 - */ - void backTask(String message, Long instanceId, String targetNodeCode, String flowStatus, String flowHisStatus); - /** * 申请人节点编码 * @@ -66,11 +34,4 @@ public interface IFlwCommonService { * @return 申请人节点编码 */ String applyNodeCode(Long definitionId); - - /** - * 删除运行中的任务 - * - * @param taskIds 任务id - */ - void deleteRunTask(List taskIds); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java index 99729c2a2..01e5124b4 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java @@ -107,7 +107,7 @@ public interface IFlwInstanceService { * @param businessId 业务id * @return 结果 */ - Map flowImage(String businessId); + Map flowHisTaskList(String businessId); /** * 按照实例id更新状态 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java new file mode 100644 index 000000000..959516535 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java @@ -0,0 +1,22 @@ +package org.dromara.workflow.service; + +import org.dromara.workflow.domain.vo.ButtonPermissionVo; + +import java.util.List; + +/** + * 流程节点扩展属性 服务层 + * + * @author AprilWind + */ +public interface IFlwNodeExtService { + + /** + * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * + * @param ext 扩展属性 JSON 字符串 + * @return 按钮权限 VO 列表 + */ + List buildButtonPermissionsFromExt(String ext); + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java index 116cb74f3..830abaf52 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java @@ -12,11 +12,13 @@ import java.util.List; public interface IFlwTaskAssigneeService { /** - * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 * - * @param storageId 包含分配类型和ID的字符串(例如 "user:123" 或 "role:456") - * @return 与分配类型和ID匹配的用户列表,如果格式无效则返回空列表 + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 */ - List fetchUsersByStorageId(String storageId); + List fetchUsersByStorageIds(String storageIds); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java index fcb078295..e172c001a 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java @@ -5,6 +5,7 @@ import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.warm.flow.core.entity.Node; +import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.orm.entity.FlowHisTask; import org.dromara.warm.flow.orm.entity.FlowNode; import org.dromara.warm.flow.orm.entity.FlowTask; @@ -38,6 +39,14 @@ public interface IFlwTaskService { */ boolean completeTask(CompleteTaskBo completeTaskBo); + /** + * 添加抄送人 + * + * @param task 任务信息 + * @param flowCopyList 抄送人 + */ + void setCopy(Task task, List flowCopyList); + /** * 查询当前用户的待办任务 * diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index bbc5734c9..e6dc8155d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -2,7 +2,6 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.dto.UserDTO; @@ -12,34 +11,20 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; -import org.dromara.warm.flow.core.constant.ExceptionCons; -import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Node; import org.dromara.warm.flow.core.entity.Task; -import org.dromara.warm.flow.core.entity.User; -import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.service.NodeService; -import org.dromara.warm.flow.core.service.TaskService; -import org.dromara.warm.flow.core.service.UserService; -import org.dromara.warm.flow.core.utils.AssertUtil; -import org.dromara.warm.flow.orm.entity.FlowNode; import org.dromara.warm.flow.orm.entity.FlowTask; -import org.dromara.warm.flow.orm.entity.FlowUser; -import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; -import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.MessageTypeEnum; -import org.dromara.workflow.common.enums.TaskAssigneeType; import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; @@ -53,83 +38,27 @@ import java.util.stream.Collectors; @RequiredArgsConstructor @Service public class FlwCommonServiceImpl implements IFlwCommonService { - - private final FlowNodeMapper flowNodeMapper; - private final FlowTaskMapper flowTaskMapper; - private final UserService userService; - private final TaskService taskService; private final NodeService nodeService; - /** - * 获取工作流用户service - */ - @Override - public UserService getFlowUserService() { - return userService; - } - /** * 构建工作流用户 * - * @param userList 办理用户 - * @param taskId 任务ID + * @param permissionList 办理用户 * @return 用户 */ @Override - public Set buildUser(List userList, Long taskId) { - if (CollUtil.isEmpty(userList)) { - return Set.of(); + public List buildUser(List permissionList) { + if (CollUtil.isEmpty(permissionList)) { + return List.of(); } - Set list = new HashSet<>(); - Set processedBySet = new HashSet<>(); IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); - for (User user : userList) { - // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 - List users = taskAssigneeService.fetchUsersByStorageId(user.getProcessedBy()); - // 转换为 FlowUser 并添加到结果集合 - if (CollUtil.isNotEmpty(users)) { - users.forEach(dto -> { - String processedBy = String.valueOf(dto.getUserId()); - if (!processedBySet.contains(processedBy)) { - FlowUser flowUser = new FlowUser(); - flowUser.setType(user.getType()); - flowUser.setProcessedBy(processedBy); - flowUser.setAssociated(taskId); - list.add(flowUser); - processedBySet.add(processedBy); - } - }); - } - } - return list; + String processedBys = CollUtil.join(permissionList, StringUtils.SEPARATOR); + // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 + List users = taskAssigneeService.fetchUsersByStorageIds(processedBys); + + return StreamUtils.toList(users, userDTO -> String.valueOf(userDTO.getUserId())); } - /** - * 构建工作流用户 - * - * @param userIdList 办理用户 - * @param taskId 任务ID - * @return 用户 - */ - @Override - public Set buildFlowUser(List userIdList, Long taskId) { - if (CollUtil.isEmpty(userIdList)) { - return Set.of(); - } - Set list = new HashSet<>(); - Set processedBySet = new HashSet<>(); - for (String userId : userIdList) { - if (!processedBySet.contains(userId)) { - FlowUser flowUser = new FlowUser(); - flowUser.setType(TaskAssigneeType.APPROVER.getCode()); - flowUser.setProcessedBy(String.valueOf(userId)); - flowUser.setAssociated(taskId); - list.add(flowUser); - processedBySet.add(String.valueOf(userId)); - } - } - return list; - } /** * 发送消息 @@ -177,42 +106,6 @@ public class FlwCommonServiceImpl implements IFlwCommonService { } } - /** - * 驳回 - * - * @param message 审批意见 - * @param instanceId 流程实例id - * @param targetNodeCode 目标节点 - * @param flowStatus 流程状态 - * @param flowHisStatus 节点操作状态 - */ - @Override - public void backTask(String message, Long instanceId, String targetNodeCode, String flowStatus, String flowHisStatus) { - IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class); - List list = flwTaskService.selectByInstId(instanceId); - if (CollUtil.isNotEmpty(list)) { - List tasks = StreamUtils.filter(list, e -> e.getNodeCode().equals(targetNodeCode)); - if (list.size() == tasks.size()) { - return; - } - } - for (FlowTask task : list) { - List userList = flwTaskService.currentTaskAllUser(task.getId()); - FlowParams flowParams = FlowParams.build(); - flowParams.nodeCode(targetNodeCode); - flowParams.message(message); - flowParams.skipType(SkipType.PASS.getKey()); - flowParams.flowStatus(flowStatus).hisStatus(flowHisStatus); - flowParams.ignore(true); - //解决会签没权限问题 - if (CollUtil.isNotEmpty(userList)) { - flowParams.handler(userList.get(0).getUserId().toString()); - } - taskService.skip(task.getId(), flowParams); - } - //解决会签多人审批问题 - backTask(message, instanceId, targetNodeCode, flowStatus, flowHisStatus); - } /** * 申请人节点编码 @@ -222,26 +115,8 @@ public class FlwCommonServiceImpl implements IFlwCommonService { */ @Override public String applyNodeCode(Long definitionId) { - //获取已发布的流程节点 - List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().eq(FlowNode::getDefinitionId, definitionId)); - AssertUtil.isTrue(CollUtil.isEmpty(flowNodes), ExceptionCons.NOT_PUBLISH_NODE); - Node startNode = flowNodes.stream().filter(t -> NodeType.isStart(t.getNodeType())).findFirst().orElse(null); - AssertUtil.isNull(startNode, ExceptionCons.LOST_START_NODE); + Node startNode = nodeService.getStartNode(definitionId); Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); return nextNode.getNodeCode(); } - - /** - * 删除运行中的任务 - * - * @param taskIds 任务id - */ - @Override - public void deleteRunTask(List taskIds) { - if (CollUtil.isEmpty(taskIds)) { - return; - } - userService.deleteByTaskIds(taskIds); - flowTaskMapper.deleteByIds(taskIds); - } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java index 7f0f67ec6..7936210d0 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java @@ -191,7 +191,7 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { List flowDefinitions = flowDefinitionMapper.selectByIds(StreamUtils.toList(flowHisTasks, FlowHisTask::getDefinitionId)); if (CollUtil.isNotEmpty(flowDefinitions)) { String join = StreamUtils.join(flowDefinitions, FlowDefinition::getFlowCode); - log.error("流程定义【{}】已被使用不可被删除!", join); + log.info("流程定义【{}】已被使用不可被删除!", join); throw new ServiceException("流程定义【" + join + "】已被使用不可被删除!"); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 3c0b59541..848bec4eb 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -19,14 +19,12 @@ 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.satoken.utils.LoginHelper; -import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.constant.ExceptionCons; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.enums.NodeType; -import org.dromara.warm.flow.core.service.ChartService; import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.core.service.InsService; import org.dromara.warm.flow.core.service.TaskService; @@ -42,11 +40,9 @@ import org.dromara.workflow.domain.bo.FlowInstanceBo; import org.dromara.workflow.domain.bo.FlowInvalidBo; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowInstanceVo; -import org.dromara.workflow.domain.vo.FlowVariableVo; import org.dromara.workflow.handler.FlowProcessEventHandler; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.mapper.FlwInstanceMapper; -import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; @@ -68,7 +64,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { private final InsService insService; private final DefService defService; - private final ChartService chartService; private final TaskService taskService; private final FlowHisTaskMapper flowHisTaskMapper; private final FlowInstanceMapper flowInstanceMapper; @@ -76,7 +71,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { private final IFlwTaskService flwTaskService; private final FlwInstanceMapper flwInstanceMapper; private final FlwCategoryMapper flwCategoryMapper; - private final IFlwCommonService flwCommonService; /** * 分页查询正在运行的流程实例 @@ -186,7 +180,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { @Override @Transactional(rollbackFor = Exception.class) public boolean deleteByBusinessIds(List businessIds) { - List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds,Convert::toStr))); + List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds, Convert::toStr))); if (CollUtil.isEmpty(flowInstances)) { log.warn("未找到对应的流程实例信息,无法执行删除操作。"); return false; @@ -245,19 +239,15 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF); } String message = bo.getMessage(); + String userIdStr = LoginHelper.getUserIdStr(); BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus()); - String applyNodeCode = flwCommonService.applyNodeCode(definition.getId()); - //撤销 - flwCommonService.backTask(message, instance.getId(), applyNodeCode, BusinessStatusEnum.CANCEL.getStatus(), BusinessStatusEnum.CANCEL.getStatus()); - //判断或签节点是否有多个,只保留一个 - List currentTaskList = taskService.list(FlowEngine.newTask().setInstanceId(instance.getId())); - if (CollUtil.isNotEmpty(currentTaskList)) { - if (currentTaskList.size() > 1) { - currentTaskList.remove(0); - flwCommonService.deleteRunTask(StreamUtils.toList(currentTaskList, Task::getId)); - } - } - + FlowParams flowParams = FlowParams.build() + .message(message) + .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) + .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) + .handler(userIdStr) + .ignore(true); + taskService.revoke(instance.getId(), flowParams); } catch (Exception e) { log.error("撤销失败: {}", e.getMessage(), e); throw new ServiceException(e.getMessage()); @@ -285,7 +275,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { * @param businessId 业务id */ @Override - public Map flowImage(String businessId) { + public Map flowHisTaskList(String businessId) { FlowInstance flowInstance = this.selectInstByBusinessId(businessId); if (ObjectUtil.isNull(flowInstance)) { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); @@ -321,8 +311,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { if (CollUtil.isNotEmpty(flowHisTasks)) { list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); } - String flowChart = chartService.chartIns(instanceId); - return Map.of("list", list, "image", flowChart); + return Map.of("list", list,"instanceId",instanceId); } /** @@ -346,21 +335,12 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ @Override public Map instanceVariable(Long instanceId) { - Map map = new HashMap<>(); FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId); - Map variableMap = flowInstance.getVariableMap(); - List list = new ArrayList<>(); - if (CollUtil.isNotEmpty(variableMap)) { - for (Map.Entry entry : variableMap.entrySet()) { - FlowVariableVo flowVariableVo = new FlowVariableVo(); - flowVariableVo.setKey(entry.getKey()); - flowVariableVo.setValue(entry.getValue().toString()); - list.add(flowVariableVo); - } - } - map.put("variableList", list); - map.put("variable", flowInstance.getVariable()); - return map; + Map variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap()); + List> variableList = variableMap.entrySet().stream() + .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue())) + .toList(); + return Map.of("variableList", variableList, "variable", flowInstance.getVariable()); } /** @@ -435,15 +415,12 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { if (instance != null) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } - List flowTaskList = flwTaskService.selectByInstId(bo.getId()); - for (FlowTask flowTask : flowTaskList) { - FlowParams flowParams = new FlowParams(); - flowParams.message(bo.getComment()); - flowParams.flowStatus(BusinessStatusEnum.INVALID.getStatus()) - .hisStatus(TaskStatusEnum.INVALID.getStatus()); - flowParams.ignore(true); - taskService.termination(flowTask.getId(), flowParams); - } + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.INVALID.getStatus()) + .hisStatus(TaskStatusEnum.INVALID.getStatus()) + .ignore(true); + taskService.terminationByInsId(bo.getId(), flowParams); return true; } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java index 231487d04..fca21a367 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java @@ -2,18 +2,25 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.dto.DictTypeDTO; import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; import org.dromara.warm.flow.ui.service.NodeExtService; import org.dromara.warm.flow.ui.vo.NodeExt; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.ButtonPermissionEnum; import org.dromara.workflow.common.enums.NodeExtEnum; +import org.dromara.workflow.domain.vo.ButtonPermissionVo; +import org.dromara.workflow.service.IFlwNodeExtService; import org.springframework.stereotype.Service; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 流程设计器-节点扩展属性 @@ -24,36 +31,19 @@ import java.util.*; @Slf4j @RequiredArgsConstructor @Service -public class FlwNodeExtServiceImpl implements NodeExtService { - - /** - * 权限页code - */ - private static final String PERMISSION_TAB = "wf_button_tab"; - - /** - * 权限页名称 - */ - private static final String PERMISSION_TAB_NAME = "权限"; - - /** - * 基础设置 - */ - private static final int TYPE_BASE_SETTING = 1; - - /** - * 新页签 - */ - private static final int TYPE_NEW_TAB = 2; +public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService { /** * 存储不同 dictType 对应的配置信息 */ - private static final Map> CHILD_NODE_MAP = new HashMap<>(); + private static final Map CHILD_NODE_MAP = new HashMap<>(); + + record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) { + } static { CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getSimpleName(), - Map.of("label", "权限按钮", "type", 4, "must", false, "multiple", true)); + new ButtonPermission("权限按钮", 4, false, true)); } private final DictService dictService; @@ -67,8 +57,10 @@ public class FlwNodeExtServiceImpl implements NodeExtService { public List getNodeExt() { List nodeExtList = new ArrayList<>(); // 构建按钮权限页面 - nodeExtList.add(buildNodeExt(PERMISSION_TAB, PERMISSION_TAB_NAME, TYPE_NEW_TAB, + nodeExtList.add(buildNodeExt("wf_button_tab", "权限", 2, List.of(ButtonPermissionEnum.class))); + // 自定义构建 规则参考 NodeExt 与 warm-flow文档说明 + // nodeExtList.add(buildNodeExt("xxx_xxx", "xxx", 1, List); return nodeExtList; } @@ -160,16 +152,92 @@ public class FlwNodeExtServiceImpl implements NodeExtService { */ private NodeExt.ChildNode buildChildNodeMap(String key) { NodeExt.ChildNode childNode = new NodeExt.ChildNode(); - Map map = CHILD_NODE_MAP.get(key); + ButtonPermission bp = CHILD_NODE_MAP.get(key); + if (bp == null) { + childNode.setType(1); + childNode.setMust(false); + childNode.setMultiple(true); + return childNode; + } // label名称 - childNode.setLabel((String) map.get("label")); + childNode.setLabel(bp.label()); // 1:输入框 2:输入框 3:下拉框 4:选择框 - childNode.setType(Convert.toInt(map.get("type"), 1)); + childNode.setType(bp.type()); // 是否必填 - childNode.setMust(Convert.toBool(map.get("must"), false)); + childNode.setMust(bp.must()); // 是否多选 - childNode.setMultiple(Convert.toBool(map.get("multiple"), true)); + childNode.setMultiple(bp.multiple()); return childNode; } + /** + * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * + * @param ext 扩展属性 JSON 字符串 + * @return 按钮权限 VO 列表 + */ + @Override + public List buildButtonPermissionsFromExt(String ext) { + // 解析 ext 为 Map>,用于标记权限 + Map> permissionMap = JsonUtils.parseArray(ext, ButtonPermissionVo.class) + .stream() + .collect(Collectors.toMap( + ButtonPermissionVo::getCode, + item -> StringUtils.splitList(item.getValue()).stream() + .map(String::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()), + (a, b) -> b, + HashMap::new + )); + + // 构建按钮权限列表,标记哪些按钮在 permissionMap 中出现(表示已勾选) + return buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class)); + } + + /** + * 将权限映射与按钮权限来源(枚举类或字典类型)进行匹配,生成权限视图列表 + *

+ * 使用说明: + * - sources 支持传入多个来源类型,支持 NodeExtEnum 枚举类 或 字典类型字符串(dictType) + * - 若需要扩展更多按钮权限,只需在 sources 中新增对应的枚举类或字典类型 + *

+ * 示例: + * buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class, "custom_button_dict")); + * + * @param permissionMap 权限映射 + * @param sources 枚举类或字典类型列表 + * @return 按钮权限视图对象列表 + */ + @SuppressWarnings("unchecked cast") + private List buildPermissionsFromSources(Map> permissionMap, List sources) { + return sources.stream() + .flatMap(source -> { + if (source instanceof Class clazz && NodeExtEnum.class.isAssignableFrom(clazz)) { + Set selectedSet = permissionMap.getOrDefault(clazz.getSimpleName(), Collections.emptySet()); + return extractDictItems(this.buildChildNode((Class) clazz), selectedSet).stream(); + } else if (source instanceof String dictType) { + Set selectedSet = permissionMap.getOrDefault(dictType, Collections.emptySet()); + return extractDictItems(this.buildChildNode(dictType), selectedSet).stream(); + } + return Stream.empty(); + }).toList(); + } + + /** + * 从节点子项中提取字典项,并构建按钮权限视图对象列表 + * + * @param childNode 子节点 + * @param selectedSet 已选中的值集 + * @return 按钮权限视图对象列表 + */ + private List extractDictItems(NodeExt.ChildNode childNode, Set selectedSet) { + return Optional.ofNullable(childNode) + .map(NodeExt.ChildNode::getDict) + .orElse(List.of()) + .stream() + .map(dict -> new ButtonPermissionVo(dict.getValue(), selectedSet.contains(dict.getValue()))) + .toList(); + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 1e0d17278..3009e0a1d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -2,6 +2,7 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -10,7 +11,6 @@ import org.dromara.common.core.domain.dto.TaskAssigneeDTO; import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.model.TaskAssigneeBody; import org.dromara.common.core.enums.FormatsType; -import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.DeptService; import org.dromara.common.core.service.TaskAssigneeService; import org.dromara.common.core.service.UserService; @@ -20,6 +20,7 @@ import org.dromara.warm.flow.ui.dto.HandlerFunDto; import org.dromara.warm.flow.ui.dto.HandlerQuery; import org.dromara.warm.flow.ui.dto.TreeFunDto; import org.dromara.warm.flow.ui.service.HandlerSelectService; +import org.dromara.warm.flow.ui.vo.HandlerFeedBackVo; import org.dromara.warm.flow.ui.vo.HandlerSelectVo; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.TaskAssigneeEnum; @@ -74,6 +75,49 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand return getHandlerSelectVo(buildHandlerData(dto, type), buildDeptTree(depts)); } + /** + * 办理人权限名称回显 + * + * @param storageIds 入库主键集合 + * @return 结果 + */ + @Override + public List handlerFeedback(List storageIds) { + if (CollUtil.isEmpty(storageIds)) { + return Collections.emptyList(); + } + // 解析并归类 ID,同时记录原始顺序和对应解析结果 + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + Map> parsedMap = new LinkedHashMap<>(); + for (String storageId : storageIds) { + Pair parsed = this.parseStorageId(storageId); + parsedMap.put(storageId, parsed); + if (parsed != null) { + typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); + } + } + + // 查询所有类型对应的 ID 名称映射 + Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); + typeIdMap.forEach((type, ids) -> nameMap.put(type, this.getNamesByType(type, ids))); + + // 组装返回结果,保持原始顺序 + return parsedMap.entrySet().stream() + .map(entry -> { + String storageId = entry.getKey(); + Pair parsed = entry.getValue(); + String handlerName = "格式错误"; + if (parsed != null) { + Map nameMapping = nameMap.getOrDefault(parsed.getKey(), Collections.emptyMap()); + handlerName = nameMapping.getOrDefault(parsed.getValue(), "未知名称"); + } + HandlerFeedBackVo backVo = new HandlerFeedBackVo(); + backVo.setStorageId(storageId); + backVo.setHandlerName(handlerName); + return backVo; + }).toList(); + } + /** * 根据任务办理类型查询对应的数据 */ @@ -83,7 +127,6 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand case ROLE -> taskAssigneeService.selectRolesByTaskAssigneeList(taskQuery); case DEPT -> taskAssigneeService.selectDeptsByTaskAssigneeList(taskQuery); case POST -> taskAssigneeService.selectPostsByTaskAssigneeList(taskQuery); - default -> throw new ServiceException("Unsupported handler type"); }; } @@ -123,34 +166,29 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand } /** - * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 * - * @param storageId 包含分配类型和ID的字符串(例如 "user:123" 或 "role:456") - * @return 与分配类型和ID匹配的用户列表,如果格式无效则返回空列表 + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 */ @Override - public List fetchUsersByStorageId(String storageId) { - List list = new ArrayList<>(); - Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); - for (String str : storageId.split(StrUtil.COMMA)) { - String[] parts = str.split(StrUtil.COLON, 2); - TaskAssigneeEnum type; - Long id; - if (parts.length < 2) { - // 无前缀时默认是用户类型 - type = TaskAssigneeEnum.USER; - id = Long.valueOf(parts[0]); - } else { - // 根据前缀解析类型(如 "role:123" -> ROLE 类型) - type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); - id = Long.valueOf(parts[1]); - } - typeIdMap.computeIfAbsent(type, k -> new ArrayList<>()).add(id); + public List fetchUsersByStorageIds(String storageIds) { + if (StringUtils.isEmpty(storageIds)) { + return List.of(); } - typeIdMap.entrySet().stream() - .filter(entry -> CollUtil.isNotEmpty(entry.getValue())) - .forEach(entry -> list.addAll(getUsersByType(entry.getKey(), entry.getValue()))); - return list.stream().distinct().toList(); + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + for (String storageId : storageIds.split(StringUtils.SEPARATOR)) { + Pair parsed = this.parseStorageId(storageId); + if (parsed != null) { + typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); + } + } + return typeIdMap.entrySet().stream() + .flatMap(entry -> this.getUsersByType(entry.getKey(), entry.getValue()).stream()) + .distinct() + .toList(); } /** @@ -172,4 +210,41 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand }; } + /** + * 根据任务分配类型和对应 ID 列表,批量查询名称映射关系 + * + * @param type 分配类型(用户、角色、部门、岗位) + * @param ids ID 列表(如用户ID、角色ID等) + * @return 返回 Map,其中 key 为 ID,value 为对应的名称 + */ + private Map getNamesByType(TaskAssigneeEnum type, List ids) { + return switch (type) { + case USER -> userService.selectUserNamesByIds(ids); + case ROLE -> userService.selectRoleNamesByIds(ids); + case DEPT -> userService.selectDeptNamesByIds(ids); + case POST -> userService.selectPostNamesByIds(ids); + }; + } + + /** + * 解析 storageId 字符串,返回类型和ID的组合 + * + * @param storageId 例如 "user:123" 或 "456" + * @return Pair(TaskAssigneeEnum, Long),如果格式非法返回 null + */ + private Pair parseStorageId(String storageId) { + try { + String[] parts = storageId.split(StrUtil.COLON, 2); + if (parts.length < 2) { + return Pair.of(TaskAssigneeEnum.USER, Long.valueOf(parts[0])); + } else { + TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); + return Pair.of(type, Long.valueOf(parts[1])); + } + } catch (Exception e) { + log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage()); + return null; + } + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index d93ba00ab..8a2f72380 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -16,7 +16,6 @@ import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.UserService; -import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ValidatorUtils; @@ -25,6 +24,7 @@ import org.dromara.common.core.validate.EditGroup; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.*; import org.dromara.warm.flow.core.enums.NodeType; @@ -38,16 +38,16 @@ import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskAssigneeType; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.*; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo; -import org.dromara.workflow.handler.FlowProcessEventHandler; -import org.dromara.workflow.handler.WorkflowPermissionHandler; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.mapper.FlwTaskMapper; import org.dromara.workflow.service.IFlwCommonService; +import org.dromara.workflow.service.IFlwNodeExtService; import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; @@ -79,13 +79,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final FlowTaskMapper flowTaskMapper; private final FlowHisTaskMapper flowHisTaskMapper; private final IdentifierGenerator identifierGenerator; - private final FlowProcessEventHandler flowProcessEventHandler; private final UserService userService; private final FlwTaskMapper flwTaskMapper; private final FlwCategoryMapper flwCategoryMapper; private final FlowNodeMapper flowNodeMapper; private final IFlwTaskAssigneeService flwTaskAssigneeService; private final IFlwCommonService flwCommonService; + private final IFlwNodeExtService flwNodeExtService; /** * 启动任务 @@ -117,10 +117,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService { dto.setTaskId(taskList.get(0).getId()); return dto; } - FlowParams flowParams = new FlowParams(); - flowParams.flowCode(startProcessBo.getFlowCode()); - flowParams.variable(startProcessBo.getVariables()); - flowParams.flowStatus(BusinessStatusEnum.DRAFT.getStatus()); + FlowParams flowParams = FlowParams.build() + .flowCode(startProcessBo.getFlowCode()) + .variable(startProcessBo.getVariables()) + .flowStatus(BusinessStatusEnum.DRAFT.getStatus()); Instance instance; try { instance = insService.start(businessId, flowParams); @@ -153,16 +153,22 @@ public class FlwTaskServiceImpl implements IFlwTaskService { String notice = completeTaskBo.getNotice(); // 获取抄送人 List flowCopyList = completeTaskBo.getFlowCopyList(); + // 设置抄送人 + completeTaskBo.getVariables().put(FlowConstant.FLOW_COPY_LIST, flowCopyList); + // 消息类型 + completeTaskBo.getVariables().put(FlowConstant.MESSAGE_TYPE, messageType); + // 消息通知 + completeTaskBo.getVariables().put(FlowConstant.MESSAGE_NOTICE, notice); + + FlowTask flowTask = flowTaskMapper.selectById(taskId); if (ObjectUtil.isNull(flowTask)) { throw new ServiceException("流程任务不存在或任务已审批!"); } Instance ins = insService.getById(flowTask.getInstanceId()); - // 获取流程定义信息 - Definition definition = defService.getById(flowTask.getDefinitionId()); // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), ins.getBusinessId(), ins.getFlowStatus(), null, true); + completeTaskBo.getVariables().put(FlowConstant.SUBMIT, true); } // 设置弹窗处理人 Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); @@ -170,20 +176,15 @@ public class FlwTaskServiceImpl implements IFlwTaskService { completeTaskBo.getVariables().putAll(assigneeMap); } // 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息 - FlowParams flowParams = new FlowParams(); - flowParams.variable(completeTaskBo.getVariables()); - flowParams.skipType(SkipType.PASS.getKey()); - flowParams.message(completeTaskBo.getMessage()); - flowParams.flowStatus(BusinessStatusEnum.WAITING.getStatus()).hisStatus(TaskStatusEnum.PASS.getStatus()); - - flowParams.hisTaskExt(completeTaskBo.getFileId()); + FlowParams flowParams = FlowParams.build() + .variable(completeTaskBo.getVariables()) + .skipType(SkipType.PASS.getKey()) + .message(completeTaskBo.getMessage()) + .flowStatus(BusinessStatusEnum.WAITING.getStatus()) + .hisStatus(TaskStatusEnum.PASS.getStatus()) + .hisTaskExt(completeTaskBo.getFileId()); // 执行任务跳转,并根据返回的处理人设置下一步处理人 - Instance instance = taskService.skip(taskId, flowParams); - this.setHandler(instance, flowTask, flowCopyList); - // 消息通知 - flwCommonService.sendMessage(definition.getFlowName(), ins.getId(), messageType, notice); - //设置下一环节处理人 - setNextHandler(ins.getId()); + taskService.skip(taskId, flowParams); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -191,29 +192,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService { } } - /** - * 设置下一环节处理人 - * - * @param instanceId 实例ID - */ - private void setNextHandler(Long instanceId) { - Instance inst = insService.getById(instanceId); - List flowTaskList = selectByInstId(instanceId); - Map variableMap = inst.getVariableMap(); - for (FlowTask task : flowTaskList) { - if (variableMap != null && variableMap.containsKey(task.getNodeCode())) { - String userIds = variableMap.get(task.getNodeCode()).toString(); - // 批量删除现有任务的办理人记录 - flwCommonService.getFlowUserService().deleteByTaskIds(List.of(task.getId())); - // 批量新增任务办理人记录 - Set users = flwCommonService.buildFlowUser(List.of(userIds.split(StringUtils.SEPARATOR)), task.getId()); - flwCommonService.getFlowUserService().saveBatch(new ArrayList<>(users)); - variableMap.remove(task.getNodeCode()); - } - } - taskService.mergeVariable(inst, variableMap); - } - /** * 设置弹窗处理人 * @@ -245,54 +223,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return map; } - /** - * 设置办理人 - * - * @param instance 实例 - * @param task (当前任务)未办理的任务 - * @param flowCopyList 抄送人 - */ - private void setHandler(Instance instance, FlowTask task, List flowCopyList) { - if (ObjectUtil.isNull(instance)) { - return; - } - // 添加抄送人 - this.setCopy(task, flowCopyList); - // 根据流程实例ID查询所有关联的任务 - List flowTasks = this.selectByInstId(instance.getId()); - if (CollUtil.isEmpty(flowTasks)) { - return; - } - List taskIdList = StreamUtils.toList(flowTasks, FlowTask::getId); - // 获取与当前任务关联的用户列表 - List associatedUsers = flwCommonService.getFlowUserService().getByAssociateds(taskIdList); - if (CollUtil.isEmpty(associatedUsers)) { - return; - } - List userList = new ArrayList<>(); - // 遍历任务列表,处理每个任务的办理人 - for (FlowTask flowTask : flowTasks) { - List users = StreamUtils.filter(associatedUsers, user -> Objects.equals(user.getAssociated(), flowTask.getId())); - if (CollUtil.isNotEmpty(users)) { - userList.addAll(flwCommonService.buildUser(users, flowTask.getId())); - } - } - // 批量删除现有任务的办理人记录 - flwCommonService.getFlowUserService().deleteByTaskIds(taskIdList); - // 确保要保存的 userList 不为空 - if (CollUtil.isEmpty(userList)) { - return; - } - flwCommonService.getFlowUserService().saveBatch(userList); - } - /** * 添加抄送人 * * @param task 任务信息 * @param flowCopyList 抄送人 */ - public void setCopy(FlowTask task, List flowCopyList) { + @Override + public void setCopy(Task task, List flowCopyList) { if (CollUtil.isEmpty(flowCopyList)) { return; } @@ -306,10 +244,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService { task.setId(taskId); task.setNodeName("【抄送】" + task.getNodeName()); Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000); - FlowParams flowParams = FlowParams.build(); - flowParams.skipType(SkipType.NONE.getKey()); - flowParams.hisStatus(TaskStatusEnum.COPY.getStatus()); - flowParams.message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); + FlowParams flowParams = FlowParams.build() + .skipType(SkipType.NONE.getKey()) + .hisStatus(TaskStatusEnum.COPY.getStatus()) + .message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams); hisTask.setCreateTime(updateTime); hisTask.setUpdateTime(updateTime); @@ -323,7 +261,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return flowUser; }).collect(Collectors.toList()); // 批量保存抄送人员 - flwCommonService.getFlowUserService().saveBatch(userList); + FlowEngine.userService().saveBatch(userList); } /** @@ -336,7 +274,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { public TableDataInfo pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); - queryWrapper.in("t.processed_by", SpringUtils.getBean(WorkflowPermissionHandler.class).permissions()); + queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus()); Page page = this.getFlowTaskVoPage(pageQuery, queryWrapper); return TableDataInfo.build(page); @@ -450,21 +388,23 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Instance inst = insService.getById(task.getInstanceId()); BusinessStatusEnum.checkBackStatus(inst.getFlowStatus()); Long definitionId = task.getDefinitionId(); - Definition definition = defService.getById(definitionId); String applyNodeCode = flwCommonService.applyNodeCode(definitionId); - FlowParams flowParams = FlowParams.build(); - flowParams.nodeCode(bo.getNodeCode()); - flowParams.message(message); - flowParams.skipType(SkipType.REJECT.getKey()); - flowParams.flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) - .hisStatus(TaskStatusEnum.BACK.getStatus()); - flowParams.hisTaskExt(bo.getFileId()); - taskService.skip(task.getId(), flowParams); - Instance instance = insService.getById(inst.getId()); - this.setHandler(instance, task, null); + Map variable = new HashMap<>(); + // 消息类型 + variable.put("messageType", messageType); // 消息通知 - flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); + variable.put("notice", notice); + + FlowParams flowParams = FlowParams.build() + .nodeCode(bo.getNodeCode()) + .variable(variable) + .message(message) + .skipType(SkipType.REJECT.getKey()) + .flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) + .hisStatus(TaskStatusEnum.BACK.getStatus()) + .hisTaskExt(bo.getFileId()); + taskService.skip(task.getId(), flowParams); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -515,9 +455,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (ObjectUtil.isNotNull(instance)) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } - FlowParams flowParams = new FlowParams(); - flowParams.message(bo.getComment()); - flowParams.flowStatus(BusinessStatusEnum.TERMINATION.getStatus()) + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.TERMINATION.getStatus()) .hisStatus(TaskStatusEnum.TERMINATION.getStatus()); taskService.termination(taskId, flowParams); return true; @@ -557,12 +497,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService { flowTaskVo.setFlowCode(definition.getFlowCode()); flowTaskVo.setFlowName(definition.getFlowName()); flowTaskVo.setBusinessId(instance.getBusinessId()); - //设置按钮权限 - FlowNode flowNode = getByNodeCode(flowTaskVo.getNodeCode(), instance.getDefinitionId()); + FlowNode flowNode = this.getByNodeCode(flowTaskVo.getNodeCode(), instance.getDefinitionId()); if (ObjectUtil.isNull(flowNode)) { throw new NullPointerException("当前【" + flowTaskVo.getNodeCode() + "】节点编码不存在"); } - flowTaskVo.setButtonList(flowTaskVo.getButtonList(flowNode.getExt())); + //设置按钮权限 + flowTaskVo.setButtonList(flwNodeExtService.buildButtonPermissionsFromExt(flowNode.getExt())); flowTaskVo.setNodeRatio(flowNode.getNodeRatio()); flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(flwCommonService.applyNodeCode(task.getDefinitionId()))); return flowTaskVo; @@ -581,18 +521,20 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Instance instance = insService.getById(task.getInstanceId()); Definition definition = defService.getById(task.getDefinitionId()); Map mergeVariable = MapUtil.mergeAll(instance.getVariableMap(), variables); - //获取下一节点列表 + // 获取下一节点列表 List nextNodeList = nodeService.getNextNodeList(task.getDefinitionId(), task.getNodeCode(), null, SkipType.PASS.getKey(), mergeVariable); List nextFlowNodes = BeanUtil.copyToList(nextNodeList, FlowNode.class); + // 只获取中间节点 + nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType())); if (CollUtil.isNotEmpty(nextNodeList)) { - //构建以下节点数据 - List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, null)); - //办理人变量替换 + // 构建以下节点数据 + List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build())); + // 办理人变量替换 ExpressionUtil.evalVariable(buildNextTaskList, mergeVariable); for (FlowNode flowNode : nextFlowNodes) { buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { if (CollUtil.isNotEmpty(t.getPermissionList())) { - List users = flwTaskAssigneeService.fetchUsersByStorageId(String.join(StringUtils.SEPARATOR, t.getPermissionList())); + List users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList())); if (CollUtil.isNotEmpty(users)) { flowNode.setPermissionFlag(StreamUtils.join(users, e -> String.valueOf(e.getUserId()))); } @@ -658,8 +600,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override @Transactional(rollbackFor = Exception.class) public boolean taskOperation(TaskOperationBo bo, String taskOperation) { - FlowParams flowParams = new FlowParams(); - flowParams.message(bo.getMessage()); + FlowParams flowParams = FlowParams.build() + .message(bo.getMessage()); if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { flowParams.ignore(true); } @@ -737,7 +679,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { List flowTasks = this.selectByIdList(taskIdList); // 批量删除现有任务的办理人记录 if (CollUtil.isNotEmpty(flowTasks)) { - flwCommonService.getFlowUserService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); + FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); List userList = flowTasks.stream() .map(flowTask -> { FlowUser flowUser = new FlowUser(); @@ -748,7 +690,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { }) .collect(Collectors.toList()); if (CollUtil.isNotEmpty(userList)) { - flwCommonService.getFlowUserService().saveBatch(userList); + FlowEngine.userService().saveBatch(userList); } } } catch (Exception e) { @@ -767,13 +709,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { public Map> currentTaskAllUser(List taskIdList) { Map> map = new HashMap<>(); // 获取与当前任务关联的用户列表 - List associatedUsers = flwCommonService.getFlowUserService().getByAssociateds(taskIdList); + List associatedUsers = FlowEngine.userService().getByAssociateds(taskIdList); Map> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); for (Map.Entry> entry : listMap.entrySet()) { List value = entry.getValue(); if (CollUtil.isNotEmpty(value)) { - List userDTOS = userService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy()))); - map.put(entry.getKey(), userDTOS); + List userDtoList = userService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy()))); + map.put(entry.getKey(), userDtoList); } } return map; @@ -787,7 +729,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override public List currentTaskAllUser(Long taskId) { // 获取与当前任务关联的用户列表 - List userList = flwCommonService.getFlowUserService().getByAssociateds(Collections.singletonList(taskId)); + List userList = FlowEngine.userService().getByAssociateds(Collections.singletonList(taskId)); if (CollUtil.isEmpty(userList)) { return Collections.emptyList(); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java index 9ecd867ac..3a773345d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java @@ -9,7 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; 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.ProcessEvent; import org.dromara.common.core.enums.BusinessStatusEnum; @@ -157,7 +157,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { // 办理意见 String message = Convert.toStr(params.get("message")); } - if (processEvent.isSubmit()) { + if (processEvent.getSubmit()) { testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); } baseMapper.updateById(testLeave); @@ -165,20 +165,17 @@ public class TestLeaveServiceImpl implements ITestLeaveService { /** * 执行任务创建监听 - * 示例:也可通过 @EventListener(condition = "#processCreateTaskEvent.flowCode=='leave1'")进行判断 + * 示例:也可通过 @EventListener(condition = "#processTaskEvent.flowCode=='leave1'")进行判断 * 在方法中判断流程节点key - * if ("xxx".equals(processCreateTaskEvent.getNodeCode())) { + * if ("xxx".equals(processTaskEvent.getNodeCode())) { * //执行业务逻辑 * } * - * @param processCreateTaskEvent 参数 + * @param processTaskEvent 参数 */ - @EventListener(condition = "#processCreateTaskEvent.flowCode.startsWith('leave')") - public void processCreateTaskHandler(ProcessCreateTaskEvent processCreateTaskEvent) { - log.info("当前任务创建了{}", processCreateTaskEvent.toString()); - TestLeave testLeave = baseMapper.selectById(Long.valueOf(processCreateTaskEvent.getBusinessId())); - testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); - baseMapper.updateById(testLeave); + @EventListener(condition = "#processTaskEvent.flowCode.startsWith('leave')") + public void processTaskHandler(ProcessTaskEvent processTaskEvent) { + log.info("当前任务创建了{}", processTaskEvent.toString()); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java index f8a20b5b4..0c0224000 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java @@ -122,6 +122,8 @@ public class WorkflowServiceImpl implements WorkflowService { /** * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); * * @param completeTask 参数 */ @@ -129,4 +131,21 @@ public class WorkflowServiceImpl implements WorkflowService { public boolean completeTask(CompleteTaskDTO completeTask) { return flwTaskService.completeTask(BeanUtil.toBean(completeTask, CompleteTaskBo.class)); } + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + */ + @Override + public boolean completeTask(Long taskId, String message) { + CompleteTaskBo completeTask = new CompleteTaskBo(); + completeTask.setTaskId(taskId); + completeTask.setMessage(message); + // 忽略权限(系统后台发起审批 无用户信息 需要忽略权限) + completeTask.getVariables().put("ignore", true); + return flwTaskService.completeTask(completeTask); + } + } diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index fb35cae6f..20529dd6c 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -1,6 +1,6 @@ services: mysql: - image: mysql:8.0.33 + image: mysql:8.0.42 container_name: mysql environment: # 时区上海 @@ -48,7 +48,7 @@ services: network_mode: "host" redis: - image: redis:6.2.12 + image: redis:7.2.8 container_name: redis ports: - "6379:6379" @@ -65,7 +65,8 @@ services: network_mode: "host" minio: - image: minio/minio:RELEASE.2023-04-13T03-08-07Z + # minio 最后一个未阉割版本 不能再进行升级 在往上的版本功能被阉割 + image: minio/minio:RELEASE.2025-04-22T22-12-26Z container_name: minio ports: # api 端口 @@ -98,12 +99,13 @@ services: network_mode: "host" ruoyi-server1: - image: ruoyi/ruoyi-server:5.3.1 + image: ruoyi/ruoyi-server:5.4.0 container_name: ruoyi-server1 environment: # 时区上海 TZ: Asia/Shanghai SERVER_PORT: 8080 + SNAIL_PORT: 28080 volumes: # 配置文件 - /docker/server1/logs/:/ruoyi/server/logs/ @@ -113,12 +115,13 @@ services: network_mode: "host" ruoyi-server2: - image: ruoyi/ruoyi-server:5.3.1 + image: ruoyi/ruoyi-server:5.4.0 container_name: ruoyi-server2 environment: # 时区上海 TZ: Asia/Shanghai SERVER_PORT: 8081 + SNAIL_PORT: 28081 volumes: # 配置文件 - /docker/server2/logs/:/ruoyi/server/logs/ @@ -128,7 +131,7 @@ services: network_mode: "host" ruoyi-monitor-admin: - image: ruoyi/ruoyi-monitor-admin:5.3.1 + image: ruoyi/ruoyi-monitor-admin:5.4.0 container_name: ruoyi-monitor-admin environment: # 时区上海 @@ -140,7 +143,7 @@ services: network_mode: "host" ruoyi-snailjob-server: - image: ruoyi/ruoyi-snailjob-server:5.3.1 + image: ruoyi/ruoyi-snailjob-server:5.4.0 container_name: ruoyi-snailjob-server environment: # 时区上海 diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf index 22b074f1d..4b9b179ed 100644 --- a/script/docker/nginx/conf/nginx.conf +++ b/script/docker/nginx/conf/nginx.conf @@ -106,6 +106,12 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # sse 与 websocket参数 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_cache off; proxy_pass http://snailjob-server/snail-job/; } diff --git a/script/leave/leave1.json b/script/leave/leave1.json index 36fc32602..0ffdeeb89 100644 --- a/script/leave/leave1.json +++ b/script/leave/leave1.json @@ -52,7 +52,7 @@ "nodeType" : 1, "nodeCode" : "a8abf15f-b83e-428a-86cc-033555ea9bbe", "nodeName" : "部门主管", - "permissionFlag" : "role:3,role:4", + "permissionFlag" : "role:3@@role:4", "nodeRatio" : 0.000, "coordinate" : "720,200|720,200", "formCustom" : "N", @@ -72,4 +72,4 @@ "formCustom" : "N", "ext" : "[]" } ] -} \ No newline at end of file +} diff --git a/script/leave/leave2.json b/script/leave/leave2.json index bb196a314..7bbdbaab2 100644 --- a/script/leave/leave2.json +++ b/script/leave/leave2.json @@ -58,7 +58,7 @@ "nodeType" : 1, "nodeCode" : "b3528155-dcb7-4445-bbdf-3d00e3499e86", "nodeName" : "组长", - "permissionFlag" : "3,4", + "permissionFlag" : "3@@4", "nodeRatio" : 0.000, "coordinate" : "720,320|720,320", "formCustom" : "N", @@ -108,4 +108,4 @@ "coordinate" : "770,160;860,160;860,200" } ] } ] -} \ No newline at end of file +} diff --git a/script/leave/leave3.json b/script/leave/leave3.json index d289c787a..bb22d42c9 100644 --- a/script/leave/leave3.json +++ b/script/leave/leave3.json @@ -106,7 +106,7 @@ "nodeType" : 1, "nodeCode" : "762cb975-37d8-4276-b6db-79a4c3606394", "nodeName" : "综合部", - "permissionFlag" : "role:3,role:4", + "permissionFlag" : "role:3@@role:4", "nodeRatio" : 0.000, "coordinate" : "800,300|800,300", "formCustom" : "N", @@ -118,4 +118,4 @@ "coordinate" : "850,300;920,300;920,245" } ] } ] -} \ No newline at end of file +} diff --git a/script/leave/leave4.json b/script/leave/leave4.json index c085de2e5..50968f8dd 100644 --- a/script/leave/leave4.json +++ b/script/leave/leave4.json @@ -52,7 +52,7 @@ "nodeType" : 1, "nodeCode" : "2f9f2e21-9bcf-42a3-a07c-13037aad22d1", "nodeName" : "全部审批通过", - "permissionFlag" : "role:1,role:3", + "permissionFlag" : "role:1@@role:3", "nodeRatio" : 100.000, "coordinate" : "820,240|820,240", "formCustom" : "N", @@ -87,4 +87,4 @@ "formCustom" : "N", "ext" : "[]" } ] -} \ No newline at end of file +} diff --git a/script/leave/leave5.json b/script/leave/leave5.json index 76d10f180..a27b1de4e 100644 --- a/script/leave/leave5.json +++ b/script/leave/leave5.json @@ -55,7 +55,7 @@ "nodeType" : 1, "nodeCode" : "c80f273e-1f17-4bd8-9ad1-04a4a94ea862", "nodeName" : "会签", - "permissionFlag" : "role:1,role:3", + "permissionFlag" : "role:1@@role:3", "nodeRatio" : 100.000, "coordinate" : "700,320|700,320", "formCustom" : "N", @@ -118,4 +118,4 @@ "coordinate" : "750,120;860,120;860,195" } ] } ] -} \ No newline at end of file +} diff --git a/script/sql/oracle/oracle_ry_job.sql b/script/sql/oracle/oracle_ry_job.sql index 104aabbaa..ba914e5f6 100644 --- a/script/sql/oracle/oracle_ry_job.sql +++ b/script/sql/oracle/oracle_ry_job.sql @@ -3,7 +3,7 @@ SnailJob Database Transfer Tool Source Server Type : MySQL Target Server Type : Oracle - Date: 2025-02-25 22:16:28 + Date: 2025-04-26 10:01:54 */ @@ -149,7 +149,9 @@ CREATE TABLE sj_retry_dead_letter id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, group_name varchar2(64) NULL, + group_id number NOT NULL, scene_name varchar2(64) NULL, + scene_id number NOT NULL, idempotent_id varchar2(64) NULL, biz_no varchar2(64) DEFAULT '' NULL, executor_name varchar2(512) DEFAULT '' NULL, @@ -169,7 +171,9 @@ CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt); COMMENT ON COLUMN sj_retry_dead_letter.id IS '主键'; COMMENT ON COLUMN sj_retry_dead_letter.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry_dead_letter.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter.group_id IS '组Id'; COMMENT ON COLUMN sj_retry_dead_letter.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry_dead_letter.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry_dead_letter.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry_dead_letter.executor_name IS '执行器名称'; @@ -184,7 +188,9 @@ CREATE TABLE sj_retry id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, group_name varchar2(64) NULL, + group_id number NOT NULL, scene_name varchar2(64) NULL, + scene_id number NOT NULL, idempotent_id varchar2(64) NULL, biz_no varchar2(64) DEFAULT '' NULL, executor_name varchar2(512) DEFAULT '' NULL, @@ -204,19 +210,20 @@ CREATE TABLE sj_retry ALTER TABLE sj_retry ADD CONSTRAINT pk_sj_retry PRIMARY KEY (id); -CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (namespace_id, group_name, task_type, idempotent_id, deleted); +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted); -CREATE INDEX idx_sj_retry_01 ON sj_retry (namespace_id, group_name, scene_name); -CREATE INDEX idx_sj_retry_02 ON sj_retry (namespace_id, group_name, retry_status); -CREATE INDEX idx_sj_retry_03 ON sj_retry (idempotent_id); -CREATE INDEX idx_sj_retry_04 ON sj_retry (biz_no); -CREATE INDEX idx_sj_retry_05 ON sj_retry (parent_id); -CREATE INDEX idx_sj_retry_06 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no); +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index); +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id); +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id); COMMENT ON COLUMN sj_retry.id IS '主键'; COMMENT ON COLUMN sj_retry.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry.group_id IS '组Id'; COMMENT ON COLUMN sj_retry.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry.executor_name IS '执行器名称'; @@ -395,7 +402,7 @@ COMMENT ON TABLE sj_server_node IS '服务器节点'; -- sj_distributed_lock CREATE TABLE sj_distributed_lock ( - name varchar2(64) NOT NULL, + name varchar2(64) NULL, lock_until timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL, locked_at timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL, locked_by varchar2(255) NULL, @@ -404,7 +411,7 @@ CREATE TABLE sj_distributed_lock ); ALTER TABLE sj_distributed_lock - ADD CONSTRAINT pk_sj_distributed_lock PRIMARY KEY (name); + ADD CONSTRAINT pk_sj_distributed_lock PRIMARY KEY (id); COMMENT ON COLUMN sj_distributed_lock.name IS '锁名称'; COMMENT ON COLUMN sj_distributed_lock.lock_until IS '锁定时长'; diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql index 7fff0b443..dcab9cfa5 100644 --- a/script/sql/oracle/oracle_ry_vue_5.X.sql +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -13,9 +13,9 @@ create table sys_social nick_name varchar2(30) default '', email varchar2(255) default '', avatar varchar2(500) default '', - access_token varchar2(255) not null, + access_token varchar2(2000) not null, expire_in number(20) default null, - refresh_token varchar2(255) default null, + refresh_token varchar2(2000) default null, access_code varchar2(255) default null, union_id varchar2(255) default null, scope varchar2(255) default null, @@ -447,6 +447,12 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen', insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate, null, null, '租户管理菜单'); insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate, null, null, '租户套餐管理菜单'); insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate, null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); + -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate, null, null, 'Admin监控菜单'); -- oss菜单 diff --git a/script/sql/oracle/oracle_ry_workflow.sql b/script/sql/oracle/oracle_ry_workflow.sql index d982f03ed..4c6f80e74 100644 --- a/script/sql/oracle/oracle_ry_workflow.sql +++ b/script/sql/oracle/oracle_ry_workflow.sql @@ -1,3 +1,6 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/oracle/oracle-wram-flow-all.sql +-- ---------------------------- create table FLOW_DEFINITION ( ID NUMBER(20) not null, @@ -18,7 +21,8 @@ create table FLOW_DEFINITION TENANT_ID VARCHAR2(40) ); -alter table FLOW_DEFINITION add constraint PK_FLOW_DEFINITION primary key (ID); +alter table FLOW_DEFINITION + add constraint PK_FLOW_DEFINITION primary key (ID); comment on table FLOW_DEFINITION is '流程定义表'; comment on column FLOW_DEFINITION.ID is '主键id'; @@ -63,7 +67,8 @@ create table FLOW_NODE PERMISSION_FLAG VARCHAR2(200) ); -alter table FLOW_NODE add constraint PK_FLOW_NODE primary key (ID); +alter table FLOW_NODE + add constraint PK_FLOW_NODE primary key (ID); comment on table FLOW_NODE is '流程节点表'; comment on column FLOW_NODE.ID is '主键id'; @@ -106,7 +111,8 @@ create table FLOW_SKIP TENANT_ID VARCHAR2(40) ); -alter table FLOW_SKIP add constraint PK_FLOW_SKIP primary key (ID); +alter table FLOW_SKIP + add constraint PK_FLOW_SKIP primary key (ID); comment on table FLOW_SKIP is '节点跳转关联表'; comment on column FLOW_SKIP.ID is '主键id'; @@ -144,7 +150,8 @@ create table FLOW_INSTANCE TENANT_ID VARCHAR2(40) ); -alter table FLOW_INSTANCE add constraint PK_FLOW_INSTANCE primary key (ID); +alter table FLOW_INSTANCE + add constraint PK_FLOW_INSTANCE primary key (ID); comment on table FLOW_INSTANCE is '流程实例表'; comment on column FLOW_INSTANCE.ID is '主键id'; @@ -154,7 +161,7 @@ comment on column FLOW_INSTANCE.NODE_TYPE is '开始节点类型 (0开始节点 comment on column FLOW_INSTANCE.NODE_CODE is '开始节点编码'; comment on column FLOW_INSTANCE.NODE_NAME is '开始节点名称'; comment on column FLOW_INSTANCE.VARIABLE is '任务变量'; -comment on column FLOW_INSTANCE.FLOW_STATUS is '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)'; +comment on column FLOW_INSTANCE.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; comment on column FLOW_INSTANCE.ACTIVITY_STATUS is '流程激活状态(0挂起 1激活)'; comment on column FLOW_INSTANCE.DEF_JSON is '流程定义json'; comment on column FLOW_INSTANCE.CREATE_BY is '创建者'; @@ -172,6 +179,7 @@ create table FLOW_TASK NODE_CODE VARCHAR2(100), NODE_NAME VARCHAR2(100), NODE_TYPE NUMBER(1), + FLOW_STATUS VARCHAR2(20), FORM_CUSTOM VARCHAR2(1) default 'N', FORM_PATH VARCHAR2(100), CREATE_TIME DATE, @@ -180,7 +188,8 @@ create table FLOW_TASK TENANT_ID VARCHAR2(40) ); -alter table FLOW_TASK add constraint PK_FLOW_TASK primary key (ID); +alter table FLOW_TASK + add constraint PK_FLOW_TASK primary key (ID); comment on table FLOW_TASK is '待办任务表'; comment on column FLOW_TASK.ID is '主键id'; @@ -189,6 +198,7 @@ comment on column FLOW_TASK.INSTANCE_ID is '对应flow_instance表的id'; comment on column FLOW_TASK.NODE_CODE is '节点编码'; comment on column FLOW_TASK.NODE_NAME is '节点名称'; comment on column FLOW_TASK.NODE_TYPE is '节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_TASK.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; comment on column FLOW_TASK.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; comment on column FLOW_TASK.FORM_PATH is '审批表单路径'; comment on column FLOW_TASK.CREATE_TIME is '创建时间'; @@ -224,7 +234,8 @@ create table FLOW_HIS_TASK ); -alter table FLOW_HIS_TASK add constraint PK_FLOW_HIS_TASK primary key (ID); +alter table FLOW_HIS_TASK + add constraint PK_FLOW_HIS_TASK primary key (ID); comment on table FLOW_HIS_TASK is '历史任务记录表'; comment on column FLOW_HIS_TASK.ID is '主键id'; @@ -237,7 +248,7 @@ comment on column FLOW_HIS_TASK.NODE_TYPE is '开始节点类型 (0开始节点 comment on column FLOW_HIS_TASK.TARGET_NODE_CODE is '目标节点编码'; comment on column FLOW_HIS_TASK.TARGET_NODE_NAME is '目标节点名称'; comment on column FLOW_HIS_TASK.SKIP_TYPE is '流转类型(PASS通过 REJECT退回 NONE无动作)'; -comment on column FLOW_HIS_TASK.FLOW_STATUS is '流程状态(1审批中 2 审批通过 9已退回 10失效)'; +comment on column FLOW_HIS_TASK.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; comment on column FLOW_HIS_TASK.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; comment on column FLOW_HIS_TASK.FORM_PATH is '审批表单路径'; comment on column FLOW_HIS_TASK.MESSAGE is '审批意见'; @@ -264,7 +275,8 @@ create table FLOW_USER TENANT_ID VARCHAR2(40) ); -alter table FLOW_USER add constraint PK_FLOW_USER primary key (ID); +alter table FLOW_USER + add constraint PK_FLOW_USER primary key (ID); comment on table FLOW_USER is '待办任务表'; comment on column FLOW_USER.ID is '主键id'; @@ -375,6 +387,8 @@ INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/postgres/postgres_ry_job.sql b/script/sql/postgres/postgres_ry_job.sql index 8b682dfe5..10612e65c 100644 --- a/script/sql/postgres/postgres_ry_job.sql +++ b/script/sql/postgres/postgres_ry_job.sql @@ -2,7 +2,7 @@ SnailJob Database Transfer Tool Source Server Type : MySQL Target Server Type : PostgreSQL - Date: 2025-02-25 22:15:32 + Date: 2025-04-26 09:56:45 */ @@ -136,7 +136,9 @@ CREATE TABLE sj_retry_dead_letter id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name varchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name varchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id varchar(64) NOT NULL, biz_no varchar(64) NOT NULL DEFAULT '', executor_name varchar(512) NOT NULL DEFAULT '', @@ -153,7 +155,9 @@ CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt); COMMENT ON COLUMN sj_retry_dead_letter.id IS '主键'; COMMENT ON COLUMN sj_retry_dead_letter.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry_dead_letter.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter.group_id IS '组Id'; COMMENT ON COLUMN sj_retry_dead_letter.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry_dead_letter.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry_dead_letter.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry_dead_letter.executor_name IS '执行器名称'; @@ -168,7 +172,9 @@ CREATE TABLE sj_retry id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name varchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name varchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id varchar(64) NOT NULL, biz_no varchar(64) NOT NULL DEFAULT '', executor_name varchar(512) NOT NULL DEFAULT '', @@ -185,19 +191,21 @@ CREATE TABLE sj_retry update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (namespace_id, group_name, task_type, idempotent_id, deleted); +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted); + +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no); +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index); +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id); +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id); -CREATE INDEX idx_sj_retry_01 ON sj_retry (namespace_id, group_name, scene_name); -CREATE INDEX idx_sj_retry_02 ON sj_retry (namespace_id, group_name, retry_status); -CREATE INDEX idx_sj_retry_03 ON sj_retry (idempotent_id); -CREATE INDEX idx_sj_retry_04 ON sj_retry (biz_no); -CREATE INDEX idx_sj_retry_05 ON sj_retry (parent_id); -CREATE INDEX idx_sj_retry_06 ON sj_retry (create_dt); COMMENT ON COLUMN sj_retry.id IS '主键'; COMMENT ON COLUMN sj_retry.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry.group_id IS '组Id'; COMMENT ON COLUMN sj_retry.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry.executor_name IS '执行器名称'; diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql index 523cbe266..2b1d23d56 100644 --- a/script/sql/postgres/postgres_ry_vue_5.X.sql +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -13,9 +13,9 @@ create table sys_social nick_name varchar(30) default ''::varchar, email varchar(255) default ''::varchar, avatar varchar(500) default ''::varchar, - access_token varchar(255) not null, + access_token varchar(2000) not null, expire_in int8 default null, - refresh_token varchar(255) default null::varchar, + refresh_token varchar(2000) default null::varchar, access_code varchar(255) default null::varchar, union_id varchar(255) default null::varchar, scope varchar(255) default null::varchar, @@ -448,6 +448,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen', insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', '1', '0', 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, now(), null, null, '租户管理菜单'); insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', '1', '0', 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, now(), null, null, '租户套餐管理菜单'); insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', '1', '0', 'C', '0', '0', 'system:client:list', 'international', 103, 1, now(), null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, now(), null, null, 'Admin监控菜单'); diff --git a/script/sql/postgres/postgres_ry_workflow.sql b/script/sql/postgres/postgres_ry_workflow.sql index df3d41313..8db29c704 100644 --- a/script/sql/postgres/postgres_ry_workflow.sql +++ b/script/sql/postgres/postgres_ry_workflow.sql @@ -154,7 +154,7 @@ COMMENT ON COLUMN flow_instance.node_type IS '节点类型(0开始节点 1中 COMMENT ON COLUMN flow_instance.node_code IS '流程节点编码'; COMMENT ON COLUMN flow_instance.node_name IS '流程节点名称'; COMMENT ON COLUMN flow_instance.variable IS '任务变量'; -COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)'; +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_instance.activity_status IS '流程激活状态(0挂起 1激活)'; COMMENT ON COLUMN flow_instance.def_json IS '流程定义json'; COMMENT ON COLUMN flow_instance.create_by IS '创建者'; @@ -172,6 +172,7 @@ CREATE TABLE flow_task node_code varchar(100) NOT NULL, -- 节点编码 node_name varchar(100) NULL, -- 节点名称 node_type int2 NOT NULL, -- 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + flow_status varchar(20) NOT NULL, -- 流程状态(0待提交 1审批中 2 审批通过 8已完成 9已退回 10失效) form_custom bpchar(1) NULL DEFAULT 'N':: character varying, -- 审批表单是否自定义(Y是 N否) form_path varchar(100) NULL, -- 审批表单路径 create_time timestamp NULL, -- 创建时间 @@ -188,6 +189,7 @@ COMMENT ON COLUMN flow_task.instance_id IS '对应flow_instance表的id'; COMMENT ON COLUMN flow_task.node_code IS '节点编码'; COMMENT ON COLUMN flow_task.node_name IS '节点名称'; COMMENT ON COLUMN flow_task.node_type IS '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_task.form_custom IS '审批表单是否自定义(Y是 N否)'; COMMENT ON COLUMN flow_task.form_path IS '审批表单路径'; COMMENT ON COLUMN flow_task.create_time IS '创建时间'; @@ -201,8 +203,8 @@ CREATE TABLE flow_his_task definition_id int8 NOT NULL, -- 对应flow_definition表的id instance_id int8 NOT NULL, -- 对应flow_instance表的id task_id int8 NOT NULL, -- 对应flow_task表的id - node_code varchar(200) NULL, -- 开始节点编码 - node_name varchar(200) NULL, -- 开始节点名称 + node_code varchar(100) NULL, -- 开始节点编码 + node_name varchar(100) NULL, -- 开始节点名称 node_type int2 NULL, -- 开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) target_node_code varchar(200) NULL, -- 目标节点编码 target_node_name varchar(200) NULL, -- 结束节点名称 @@ -237,7 +239,7 @@ COMMENT ON COLUMN flow_his_task.approver IS '审批者'; COMMENT ON COLUMN flow_his_task.cooperate_type IS '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)'; COMMENT ON COLUMN flow_his_task.collaborator IS '协作人'; COMMENT ON COLUMN flow_his_task.skip_type IS '流转类型(PASS通过 REJECT退回 NONE无动作)'; -COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(1审批中 2 审批通过 9已退回 10失效)'; +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_his_task.form_custom IS '审批表单是否自定义(Y是 N否)'; COMMENT ON COLUMN flow_his_task.form_path IS '审批表单路径'; COMMENT ON COLUMN flow_his_task.message IS '审批意见'; @@ -368,6 +370,9 @@ INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); + INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/ry_job.sql b/script/sql/ry_job.sql index 3577d6614..dd86deb45 100644 --- a/script/sql/ry_job.sql +++ b/script/sql/ry_job.sql @@ -84,7 +84,9 @@ CREATE TABLE `sj_retry_dead_letter` `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', `group_name` varchar(64) NOT NULL COMMENT '组名称', + `group_id` bigint(20) NOT NULL COMMENT '组Id', `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `scene_id` bigint(20) NOT NULL COMMENT '场景ID', `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', @@ -105,7 +107,9 @@ CREATE TABLE `sj_retry` `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', `group_name` varchar(64) NOT NULL COMMENT '组名称', + `group_id` bigint(20) NOT NULL COMMENT '组Id', `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `scene_id` bigint(20) NOT NULL COMMENT '场景ID', `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', @@ -121,13 +125,12 @@ CREATE TABLE `sj_retry` `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), - KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), - KEY `idx_namespace_id_group_name_retry_status` (`namespace_id`, `group_name`, `retry_status`), - KEY `idx_idempotent_id` (`idempotent_id`), KEY `idx_biz_no` (`biz_no`), + KEY `idx_idempotent_id` (`idempotent_id`), + KEY `idx_retry_status_bucket_index` (`retry_status`, `bucket_index`), KEY `idx_parent_id` (`parent_id`), KEY `idx_create_dt` (`create_dt`), - UNIQUE KEY `uk_name_task_type_idempotent_id_deleted` (`namespace_id`, `group_name`, `task_type`, `idempotent_id`, `deleted`) + UNIQUE KEY `uk_scene_tasktype_idempotentid_deleted` (`scene_id`, `task_type`, `idempotent_id`, `deleted`) ) ENGINE = InnoDB AUTO_INCREMENT = 0 DEFAULT CHARSET = utf8mb4 COMMENT ='重试信息表'; diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql index c17e4a01a..42d791fe0 100644 --- a/script/sql/ry_vue_5.X.sql +++ b/script/sql/ry_vue_5.X.sql @@ -13,10 +13,10 @@ create table sys_social nick_name varchar(30) default '' comment '用户昵称', email varchar(255) default '' comment '用户邮箱', avatar varchar(500) default '' comment '头像地址', - access_token varchar(255) not null comment '用户的授权令牌', + access_token varchar(2000) not null comment '用户的授权令牌', expire_in int default null comment '用户的授权令牌的有效期,部分平台可能没有', refresh_token varchar(255) default null comment '刷新令牌,部分平台可能没有', - access_code varchar(255) default null comment '平台的授权信息,部分平台可能没有', + access_code varchar(2000) default null comment '平台的授权信息,部分平台可能没有', union_id varchar(255) default null comment '用户的 unionid', scope varchar(255) default null comment '授予的权限,部分平台可能没有', token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有', @@ -282,6 +282,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen', insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate(), null, null, '租户管理菜单'); insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate(), null, null, '租户套餐管理菜单'); insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate(), null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate(), null, null, 'Admin监控菜单'); diff --git a/script/sql/ry_workflow.sql b/script/sql/ry_workflow.sql index acc297ec5..5e055d2ed 100644 --- a/script/sql/ry_workflow.sql +++ b/script/sql/ry_workflow.sql @@ -3,7 +3,7 @@ -- ---------------------------- CREATE TABLE `flow_definition` ( - `id` bigint unsigned NOT NULL COMMENT '主键id', + `id` bigint NOT NULL COMMENT '主键id', `flow_code` varchar(40) NOT NULL COMMENT '流程编码', `flow_name` varchar(100) NOT NULL COMMENT '流程名称', `category` varchar(100) DEFAULT NULL COMMENT '流程类别', @@ -50,7 +50,7 @@ CREATE TABLE `flow_node` CREATE TABLE `flow_skip` ( - `id` bigint unsigned NOT NULL COMMENT '主键id', + `id` bigint NOT NULL COMMENT '主键id', `definition_id` bigint NOT NULL COMMENT '流程定义id', `now_node_code` varchar(100) NOT NULL COMMENT '当前流程节点的编码', `now_node_type` tinyint(1) DEFAULT NULL COMMENT '当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', @@ -76,7 +76,7 @@ CREATE TABLE `flow_instance` `node_code` varchar(40) NOT NULL COMMENT '流程节点编码', `node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称', `variable` text COMMENT '任务变量', - `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', `activity_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '流程激活状态(0挂起 1激活)', `def_json` text COMMENT '流程定义json', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', @@ -96,6 +96,7 @@ CREATE TABLE `flow_task` `node_code` varchar(100) NOT NULL COMMENT '节点编码', `node_name` varchar(100) DEFAULT NULL COMMENT '节点名称', `node_type` tinyint(1) NOT NULL COMMENT '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', `create_time` datetime DEFAULT NULL COMMENT '创建时间', @@ -107,7 +108,7 @@ CREATE TABLE `flow_task` CREATE TABLE `flow_his_task` ( - `id` bigint(20) unsigned NOT NULL COMMENT '主键id', + `id` bigint(20) NOT NULL COMMENT '主键id', `definition_id` bigint(20) NOT NULL COMMENT '对应flow_definition表的id', `instance_id` bigint(20) NOT NULL COMMENT '对应flow_instance表的id', `task_id` bigint(20) NOT NULL COMMENT '对应flow_task表的id', @@ -120,7 +121,7 @@ CREATE TABLE `flow_his_task` `cooperate_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)', `collaborator` varchar(40) DEFAULT NULL COMMENT '协作人', `skip_type` varchar(10) NOT NULL COMMENT '流转类型(PASS通过 REJECT退回 NONE无动作)', - `flow_status` varchar(20) NOT NULL COMMENT '流程状态(1审批中 2 审批通过 9已退回 10失效)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', `message` varchar(500) DEFAULT NULL COMMENT '审批意见', @@ -136,7 +137,7 @@ CREATE TABLE `flow_his_task` CREATE TABLE `flow_user` ( - `id` bigint unsigned NOT NULL COMMENT '主键id', + `id` bigint NOT NULL COMMENT '主键id', `type` char(1) NOT NULL COMMENT '人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)', `processed_by` varchar(80) DEFAULT NULL COMMENT '权限人', `associated` bigint NOT NULL COMMENT '任务表id', @@ -213,6 +214,8 @@ insert into sys_menu values ('11622', '流程分类', '11616', '1', 'category', insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate(), NULL, NULL, ''); insert into sys_menu values ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, ''); insert into sys_menu values ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); -- 流程分类管理相关按钮 insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1,sysdate(), null, null, ''); insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1,sysdate(), null, null, ''); diff --git a/script/sql/sqlserver/sqlserver_ry_job.sql b/script/sql/sqlserver/sqlserver_ry_job.sql index 5a0bbb39b..9268e9f0d 100644 --- a/script/sql/sqlserver/sqlserver_ry_job.sql +++ b/script/sql/sqlserver/sqlserver_ry_job.sql @@ -2,7 +2,7 @@ SnailJob Database Transfer Tool Source Server Type : MySQL Target Server Type : Microsoft SQL Server - Date: 2025-02-25 22:16:48 + Date: 2025-04-26 10:03:23 */ @@ -410,7 +410,9 @@ CREATE TABLE sj_retry_dead_letter id bigint NOT NULL PRIMARY KEY IDENTITY, namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name nvarchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name nvarchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id nvarchar(64) NOT NULL, biz_no nvarchar(64) NOT NULL DEFAULT '', executor_name nvarchar(512) NOT NULL DEFAULT '', @@ -450,6 +452,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'group_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'组Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'group_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'场景名称', 'SCHEMA', N'dbo', @@ -457,6 +466,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'scene_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'场景ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'scene_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'幂等id', 'SCHEMA', N'dbo', @@ -511,7 +527,9 @@ CREATE TABLE sj_retry id bigint NOT NULL PRIMARY KEY IDENTITY, namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name nvarchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name nvarchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id nvarchar(64) NOT NULL, biz_no nvarchar(64) NOT NULL DEFAULT '', executor_name nvarchar(512) NOT NULL DEFAULT '', @@ -529,20 +547,18 @@ CREATE TABLE sj_retry ) GO -CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (namespace_id, group_name, task_type, idempotent_id, deleted) +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted) GO -CREATE INDEX idx_sj_retry_01 ON sj_retry (namespace_id, group_name, scene_name) +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no) GO -CREATE INDEX idx_sj_retry_02 ON sj_retry (namespace_id, group_name, retry_status) +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index) GO -CREATE INDEX idx_sj_retry_03 ON sj_retry (idempotent_id) +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id) GO -CREATE INDEX idx_sj_retry_04 ON sj_retry (biz_no) +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt) GO -CREATE INDEX idx_sj_retry_05 ON sj_retry (parent_id) -GO -CREATE INDEX idx_sj_retry_06 ON sj_retry (create_dt) +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id) GO EXEC sp_addextendedproperty @@ -566,6 +582,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'group_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'组Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'group_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'场景名称', 'SCHEMA', N'dbo', @@ -573,6 +596,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'scene_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'场景ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'scene_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'幂等id', 'SCHEMA', N'dbo', diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index 0567f31d9..47d7c15f2 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -10,9 +10,9 @@ create table sys_social nick_name nvarchar(30) DEFAULT ('') NULL, email nvarchar(255) DEFAULT ('') NULL, avatar nvarchar(500) DEFAULT ('') NULL, - access_token nvarchar(255) NOT NULL, + access_token nvarchar(2000) NOT NULL, expire_in bigint NULL, - refresh_token nvarchar(255) NULL, + refresh_token nvarchar(2000) NULL, access_code nvarchar(255) NULL, union_id nvarchar(255) NULL, scope nvarchar(255) NULL, @@ -1239,7 +1239,7 @@ INSERT sys_dict_data VALUES (18, N'000000', 1, N'新增', N'1', N'sys_oper_type' GO INSERT sys_dict_data VALUES (19, N'000000', 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', 103, 1, getdate(), NULL, NULL, N'修改操作') GO -INSERT sys_dict_data VALUES (20, N'000000', 3, N'删除', N3, N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'删除操作') +INSERT sys_dict_data VALUES (20, N'000000', 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'删除操作') GO INSERT sys_dict_data VALUES (21, N'000000', 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', 103, 1, getdate(), NULL, NULL, N'授权操作') GO @@ -1265,15 +1265,15 @@ INSERT sys_dict_data VALUES (32, N'000000', 0, N'邮件认证', N'email', N'sys_ GO INSERT sys_dict_data VALUES (33, N'000000', 0, N'小程序认证', N'xcx', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序认证') GO -INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'`social`', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'三方登录认证') +INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'social', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'三方登录认证') GO -INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'`pc`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'PC') +INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'pc', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'PC') GO -INSERT sys_dict_data VALUES (36, N'000000', 0, N'安卓', N'`android`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'安卓') +INSERT sys_dict_data VALUES (36, N'000000', 0, N'安卓', N'android', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'安卓') GO -INSERT sys_dict_data VALUES (37, N'000000', 0, N'iOS', N'`ios`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'iOS') +INSERT sys_dict_data VALUES (37, N'000000', 0, N'iOS', N'ios', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'iOS') GO -INSERT sys_dict_data VALUES (38, N'000000', 0, N'小程序', N'`xcx`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序') +INSERT sys_dict_data VALUES (38, N'000000', 0, N'小程序', N'xcx', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序') GO CREATE TABLE sys_dict_type @@ -1690,6 +1690,17 @@ INSERT sys_menu VALUES (122, N'租户套餐管理', 6, 2, N'tenantPackage', N'sy GO INSERT sys_menu VALUES (123, N'客户端管理', 1, 11, N'client', N'system/client/index', N'', 1, 0, N'C', N'0', N'0', N'system:client:list', N'international', 103, 1, getdate(), NULL, NULL, N'客户端管理菜单') GO +INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId(\\d+)', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (130, N'分配用户', 1, 2, N'role-auth/user/:roleId(\\d+)', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (131, N'分配角色', 1, 1, N'user-auth/role/:userId(\\d+)', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (132, N'字典数据', 1, 6, N'dict-data/index/:dictId(\\d+)', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (133, N'文件配置管理', 1, 10, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N''); +GO + INSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', 103, 1, getdate(), NULL, NULL, N'Admin监控菜单'); GO INSERT sys_menu VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', 103, 1, getdate(), NULL, NULL, N'文件管理菜单'); @@ -3180,7 +3191,7 @@ INSERT INTO sys_oss_config VALUES (N'1', N'000000', N'minio', N'ruoyi', GO INSERT INTO sys_oss_config VALUES (N'2', N'000000', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N's3-cn-north-1.qiniucs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO -INSERT INTO sys_oss_config VALUES (N3, N'000000', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +INSERT INTO sys_oss_config VALUES (N'3', N'000000', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO INSERT INTO sys_oss_config VALUES (N'4', N'000000', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi-1250000000', N'', N'cos.ap-beijing.myqcloud.com', N'',N'N', N'ap-beijing', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO diff --git a/script/sql/sqlserver/sqlserver_ry_workflow.sql b/script/sql/sqlserver/sqlserver_ry_workflow.sql index 96ea62ad3..88d92fcb4 100644 --- a/script/sql/sqlserver/sqlserver_ry_workflow.sql +++ b/script/sql/sqlserver/sqlserver_ry_workflow.sql @@ -1,3 +1,6 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/sqlserver/sqlserver.sql +-- ---------------------------- CREATE TABLE flow_definition ( id bigint NOT NULL, flow_code nvarchar(40) NOT NULL, @@ -523,7 +526,7 @@ EXEC sp_addextendedproperty GO EXEC sp_addextendedproperty -'MS_Description', N'流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)', +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', 'SCHEMA', N'dbo', 'TABLE', N'flow_instance', 'COLUMN', N'flow_status' @@ -598,6 +601,7 @@ CREATE TABLE flow_task ( node_code nvarchar(100) NOT NULL, node_name nvarchar(100) NULL, node_type tinyint NOT NULL, + flow_status nvarchar(20) NOT NULL, form_custom nchar(1) DEFAULT('N') NULL, form_path nvarchar(100) NULL, create_time datetime2(7) NULL, @@ -653,6 +657,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'node_type' GO +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +GO + EXEC sp_addextendedproperty 'MS_Description', N'审批表单是否自定义(Y是 N否)', 'SCHEMA', N'dbo', @@ -824,7 +835,7 @@ EXEC sp_addextendedproperty GO EXEC sp_addextendedproperty -'MS_Description', N'流程状态(1审批中 2 审批通过 9已退回 10失效)', +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', 'SCHEMA', N'dbo', 'TABLE', N'flow_his_task', 'COLUMN', N'flow_status' @@ -1261,6 +1272,10 @@ INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'monitor', NULL, N'', GO INSERT sys_menu VALUES (11631, N'待办任务', 11630, 2, N'allTaskWaiting', N'workflow/task/allTaskWaiting', N'', 1, 1, N'C', N'0', N'0', N'', N'waiting', 103, 1, GETDATE(), NULL, NULL, N''); GO +INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO -- 流程分类管理相关按钮 INSERT sys_menu VALUES (11623, N'流程分类查询', 11622, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:query', N'#', 103, 1, GETDATE(), NULL, NULL, N''); diff --git a/script/sql/update/oracle/update_5.3.1-5.4.0.sql b/script/sql/update/oracle/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..d0d049932 --- /dev/null +++ b/script/sql/update/oracle/update_5.3.1-5.4.0.sql @@ -0,0 +1,18 @@ +ALTER TABLE flow_task ADD (flow_status VARCHAR2(20)); +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +ALTER TABLE sys_social + MODIFY (access_token VARCHAR2(2000 BYTE)) + MODIFY (refresh_token VARCHAR2(2000 BYTE)); + +INSERT INTO sys_menu VALUES ('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/update/postgres/update_5.3.1-5.4.0.sql b/script/sql/update/postgres/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..476229c84 --- /dev/null +++ b/script/sql/update/postgres/update_5.3.1-5.4.0.sql @@ -0,0 +1,18 @@ +ALTER TABLE flow_task ADD COLUMN flow_status varchar(20); +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +ALTER TABLE sys_social +ALTER COLUMN access_token TYPE varchar(2000), +ALTER COLUMN refresh_token TYPE varchar(2000); + +INSERT INTO sys_menu VALUES ('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/update/sqlserver/update_5.0-5.1.sql b/script/sql/update/sqlserver/update_5.0-5.1.sql index bde3813ac..6ea54a55a 100644 --- a/script/sql/update/sqlserver/update_5.0-5.1.sql +++ b/script/sql/update/sqlserver/update_5.0-5.1.sql @@ -363,7 +363,7 @@ INSERT sys_dict_data VALUES (32, N'000000', 0, N'邮件认证', N'email', N'sys_ GO INSERT sys_dict_data VALUES (33, N'000000', 0, N'小程序认证', N'xcx', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'小程序认证') GO -INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'`social`', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'三方登录认证') +INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'social', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'三方登录认证') GO INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'`pc`', N'sys_device_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'PC') GO diff --git a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..c5ec57ccc --- /dev/null +++ b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql @@ -0,0 +1,63 @@ +ALTER TABLE flow_task ADD flow_status nvarchar(20) NULL; + +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +GO + +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +GO + +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +GO + +-- sys_social 表修改列 +ALTER TABLE sys_social ALTER COLUMN access_token VARCHAR(2000) NOT NULL +GO +ALTER TABLE sys_social ALTER COLUMN refresh_token VARCHAR(2000) NULL +GO + +INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId(\\d+)', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (130, N'分配用户', 1, 2, N'role-auth/user/:roleId(\\d+)', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (131, N'分配角色', 1, 1, N'user-auth/role/:userId(\\d+)', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (132, N'字典数据', 1, 6, N'dict-data/index/:dictId(\\d+)', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (133, N'文件配置管理', 1, 10, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO diff --git a/script/sql/update/update_5.3.1-5.4.0.sql b/script/sql/update/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..d5062c31b --- /dev/null +++ b/script/sql/update/update_5.3.1-5.4.0.sql @@ -0,0 +1,21 @@ +ALTER TABLE `flow_task` + ADD COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `node_type`; + +ALTER TABLE `flow_instance` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `variable`; + +ALTER TABLE `flow_his_task` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `skip_type`; + +ALTER TABLE `sys_social` + MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`; +ALTER TABLE `sys_social` + MODIFY COLUMN `refresh_token` varchar(2000) DEFAULT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`; + +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, '');