Compare commits

..

64 Commits

Author SHA1 Message Date
疯狂的狮子Li 8136a0191a !860 发布 5.6.2 版本 依赖升级
* 🦁🦁🦁发布 5.6.2 版本 依赖升级
* update springboot 3.5.14 => 3.5.15(springboot 3.X最后一个版本)
* update 优化 防止工作流保存被xss过滤
* update README.md
* add 新增 赞助商
* fix 修复 多次驳回无法锁定审批人问题
* update 优化 findInSet 方法 增加参数校验防止注入
* fix 修复 前端输入性CVE漏洞 禁止使用代码生成到本地路径
* !848 fix: 用户管理导入用户时校验账号最小长度
2026-06-17 05:00:28 +00:00
疯狂的狮子Li 6bfdcae06e !847 发布 5.6.1 版本 依赖升级漏洞修复
Merge pull request !847 from 疯狂的狮子Li/dev
2026-04-24 01:47:21 +00:00
疯狂的狮子Li ba70f9bb2d 🦁🦁🦁发布 5.6.1 版本 依赖升级漏洞修复 2026-04-24 09:44:18 +08:00
疯狂的狮子Li b48fc54151 fix 修复 springboot 升级后 依赖缺失 2026-04-24 09:39:53 +08:00
疯狂的狮子Li 2779c02953 update springboot 3.5.12 => 3.5.14
update springdoc 2.8.15 => 2.8.17
update satoken 1.44.0 => 1.45.0
update springboot-admin 3.5.6 => 3.5.8
update lombok 1.18.42 => 1.18.44
update warmflow 1.8.4 => 1.8.5
update anyline 20251210 => 20260319
2026-04-24 09:21:25 +08:00
疯狂的狮子Li 3965e47d8a update ip2region 3.3.4 => 3.3.7 2026-04-24 09:19:44 +08:00
疯狂的狮子Li 91f7fab613 update snailjob 1.9.0 => 1.10.0 2026-04-24 09:19:20 +08:00
疯狂的狮子Li d259cac6a0 fix 修复 netty 对虚拟线程适配有问题 导致长时间使用 redisson 卡死问题 2026-04-21 10:59:46 +08:00
疯狂的狮子Li cf006a47da fix 修复 AUTO_PASS 变量取值错误 2026-04-10 17:49:59 +08:00
疯狂的狮子Li 14df8e0d9a fix 修复 代码生成 主库生成其他不同数据源sql模板错误问题 2026-04-10 14:50:31 +08:00
疯狂的狮子Li ddff4ae38d update 优化 全局异常拦截器 不返回具体的异常内容到前端页面 避免信息泄漏问题 2026-04-09 14:21:46 +08:00
疯狂的狮子Li a2be9bd3dc update 优化 截断token 避免日志输出具体token内容 防止盗用隐患 2026-04-09 10:58:25 +08:00
疯狂的狮子Li 3904d80329 update 优化 RepeatSubmitAspect.KEY_CACHE 清理不彻底 2026-04-09 10:55:54 +08:00
疯狂的狮子Li acab0637bf fix 修复 ExcelBigNumberConvert 导入没法转换数据问题 2026-04-08 16:27:38 +08:00
疯狂的狮子Li ac374b36b2 fix 修复 部门vo子部门属性使用错误 2026-04-07 09:16:44 +08:00
疯狂的狮子Li 631b6be33c fix 修复 下拉数据字符大于255报错问题 2026-04-03 11:29:50 +08:00
疯狂的狮子Li fe637c4479 update 优化 已办任务列表去除抄送任务 2026-04-02 11:29:16 +08:00
RealXin cb301e214b !840 update 更新gitignore文件
* update 更新gitignore文件
2026-03-30 12:07:31 +00:00
疯狂的狮子Li 6afb100ba3 !842 fix 修复 更新sql字段内容错误
Merge pull request !842 from 疯狂的狮子Li/dev
2026-03-27 09:35:36 +00:00
疯狂的狮子Li 5d1aaa62d6 fix 修复 更新sql字段内容错误 2026-03-24 13:45:58 +08:00
疯狂的狮子Li b629c82d0f !839 发布 5.6.0 版本 新年第一版
Merge pull request !839 from 疯狂的狮子Li/dev
2026-03-24 03:48:49 +00:00
疯狂的狮子Li c601164ba3 🦁🦁🦁发布 5.6.0 版本 新年第一版 2026-03-24 11:38:49 +08:00
疯狂的狮子Li 867a5d084d update bcpkix-jdk18on 1.80 => 1.83 2026-03-24 11:17:54 +08:00
疯狂的狮子Li 78aab8f8cd update springboot 3.5.11 => 3.5.12 2026-03-24 10:20:49 +08:00
疯狂的狮子Li c919572eb2 update 增加更新sql 2026-03-24 10:15:48 +08:00
gssong 12bcc68b45 update 调整流程撤销如果非发起人或管理员不可以撤销 2026-03-19 10:51:05 +08:00
gssong f551cd938c add 增加流程实例权限脚本 2026-03-19 10:34:26 +08:00
gssong e1a8eea3a1 add 增加流程实例权限 2026-03-19 10:28:43 +08:00
gssong 269de03d0b add 增加流程实例权限 2026-03-19 10:26:28 +08:00
gssong 0bc4961694 add 增加流程定义权限 2026-03-18 17:25:55 +08:00
gssong a71541a227 fix 修复 CVE-2026-2819 工作流接口通过业务id可以越级删除问题 2026-03-18 11:40:35 +08:00
疯狂的狮子Li 334c85ed61 fix 修复 CVE-2026-2819 工作流接口通过业务id可以越级删除问题 2026-03-18 11:35:51 +08:00
疯狂的狮子Li 0dd3b8dc51 fix 修复 excel结尾出现空白格导致合并策略异常问题 2026-03-16 18:00:04 +08:00
Lau 0530a9d307 update 修改vben5前端仓库地址 2026-03-13 14:20:35 +00:00
疯狂的狮子Li 2d7195c61d update 优化 代码增加空判断与其他性能优化 2026-03-10 17:20:37 +08:00
AprilWind aaede419bc update 优化统一用户昵称 2026-03-10 14:25:29 +08:00
AprilWind 75d8d374bc update 优化统一用户昵称 2026-03-10 14:08:44 +08:00
gssong 07b29e06cf update 调整转办等消息提示 2026-03-09 10:12:02 +08:00
gssong 3cbbd0698d add 增加调整转办等消息提示 2026-03-06 18:35:47 +08:00
疯狂的狮子Li 5312131635 update 优化代码 2026-03-06 17:57:05 +08:00
秋辞未寒 91f505539e Merge remote-tracking branch 'plus/dev' into dev 2026-03-06 17:32:35 +08:00
秋辞未寒 c9774e78c4 update 通过类加载的方式优化 Javadoc 解析器,支持执行多个 Javadoc 解析器 2026-03-06 17:32:28 +08:00
疯狂的狮子Li 337c2f7170 fix 修复 jackson createContextual 用法不标准导致可能出现的并发问题(https://gitee.com/dromara/RuoYi-Cloud-Plus/issues/IFAM5Z) 2026-03-06 16:54:50 +08:00
疯狂的狮子Li f773818642 fix 修复 jackson createContextual 用法不标准导致可能出现的并发问题(https://gitee.com/dromara/RuoYi-Cloud-Plus/issues/IFAM5Z) 2026-03-06 16:49:36 +08:00
疯狂的狮子Li 9bf8ae5583 fix 修复 移除超级管理员角色后新增角色分配校验,避免无角色分配时报错 2026-03-06 13:08:05 +08:00
疯狂的狮子Li d190b89681 update README.md 2026-03-06 11:28:27 +08:00
疯狂的狮子Li d89e09b94e update 优化 !pr835 相关代码 2026-03-05 17:32:51 +08:00
AprilWind 1452ae9685 !835 update Sa-Token 权限码展示,接口详情显示权限码,感谢nextdoc4j
* update 增强接口描述,合并JavaDoc权限信息到操作描述中
* update 优化satoken依赖引用,减少耦合性
* update 优化接口描述文本展示
* update Sa-Token 权限码展示,接口详情显示权限码,感谢nextdoc4j
2026-03-05 09:06:58 +00:00
疯狂的狮子Li 28772b8b30 fix 修正 SysDictDataController 接口注释及日志注解中的错误描述 2026-03-05 17:05:58 +08:00
AprilWind ab201f9d9e Revert "update 优化redis 工具类使用方法"
This reverts commit 1f5f969d20.
2026-03-04 16:34:46 +08:00
AprilWind 1f5f969d20 update 优化redis 工具类使用方法 2026-03-03 17:53:38 +08:00
疯狂的狮子Li a89a124a0e update readme github stars label 2026-03-03 17:01:08 +08:00
疯狂的狮子Li 536b6db527 update minio 升级到开源社区维护的最新版本 RELEASE.2026-02-14T12-00-00Z 2026-03-02 16:19:55 +08:00
疯狂的狮子Li f8e3cff83d update 优化 区分系统监控与流程监控菜单地址 避免冲突 2026-03-02 14:16:23 +08:00
疯狂的狮子Li 6b3ed18c33 update 优化 消除编译相关提醒 2026-03-02 13:27:53 +08:00
疯狂的狮子Li e669fa3210 fix 修复 undertow 新版本无法上传大文件问题 2026-02-26 13:42:56 +08:00
疯狂的狮子Li 2d116146ce fix 修复 undertow 新版本无法上传大文件问题 2026-02-26 13:41:10 +08:00
疯狂的狮子Li ed9de38c6f fix 修复 TestDemoImportVo 包放置错误 2026-01-29 15:21:53 +08:00
清酒 28d4c88387 !828 fix: 修复演示案例导入 VO 缺失 AutoMapper 注解导致导入数据功能转换失败的问题
* fix: 修复演示案例导入 VO 缺失 AutoMapper 注解导致导入数据功能转换失败的问题
2026-01-29 07:20:57 +00:00
疯狂的狮子Li c656f3340d update 优化 增加工作流短信发送案例 2026-01-28 15:29:07 +08:00
ColorDreams cd0ee3f016 update 更新ip2region版本,优化IP未知地区占位符为0的情况 2026-01-28 13:14:35 +08:00
ColorDreams 48ea66cb1a update 使用release指令代替source和target指令进行编译构建 2026-01-28 13:12:56 +08:00
疯狂的狮子Li ab6409ea28 !827 fix 修复 springboot升级到3.5.10之后大文件上传请求无响应问题(不清楚原因等spring修复)
Merge pull request !827 from 疯狂的狮子Li/dev
2026-01-27 08:13:42 +00:00
疯狂的狮子Li 34c3b81190 fix 修复 springboot升级到3.5.10之后大文件上传请求无响应问题(不清楚原因等spring修复) 2026-01-27 16:10:02 +08:00
93 changed files with 1585 additions and 368 deletions
+3
View File
@@ -25,6 +25,9 @@ target/
*.iml
*.ipr
### VS CODE ###
.vscode
### JRebel ###
rebel.xml
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.5.3" />
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.6.2" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-server:5.5.3" />
<option name="imageTag" value="ruoyi/ruoyi-server:5.6.2" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.5.3" />
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.6.2" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
</settings>
+5 -5
View File
@@ -5,11 +5,11 @@
## 平台简介
[![码云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)
[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus?style=social&label=Github%20Stars)](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/5.X/LICENSE)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.3-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.6.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5-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,7 +22,7 @@
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)<br>
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://github.com/imdap/ruoyi-plus-vben5)<br>
> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
@@ -33,11 +33,11 @@
MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
数舵科技 软件定制开发APP小程序等 - https://www.shuduokeji.com/ <br>
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong <br>
Ruoyi-Plus-Uniapp - https://ruoyi.plus <br>
Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/ <br>
稳定低价的大模型中转站 - https://aicodelink.top/register?aff=pHeG <br>
[如何成为赞助商 加群联系作者详谈 每日PV2500-3000 IP1700-2500](https://plus-doc.dromara.org/#/common/add_group)
@@ -53,7 +53,7 @@ Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/ <br>
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR``权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
| 三方鉴权 | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证 | 无 |
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
| 缓存数据库 | 支持 Redis >= 6 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题 |
| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 |
| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL |
+13 -14
View File
@@ -13,32 +13,32 @@
<description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
<properties>
<revision>5.5.3</revision>
<spring-boot.version>3.5.10</spring-boot.version>
<revision>5.6.2</revision>
<spring-boot.version>3.5.15</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<mybatis.version>3.5.19</mybatis.version>
<springdoc.version>2.8.15</springdoc.version>
<springdoc.version>2.8.17</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version>
<satoken.version>1.44.0</satoken.version>
<satoken.version>1.45.0</satoken.version>
<mybatis-plus.version>3.5.16</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.43</hutool.version>
<spring-boot-admin.version>3.5.6</spring-boot-admin.version>
<spring-boot-admin.version>3.5.8</spring-boot-admin.version>
<redisson.version>3.52.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.9.0</snailjob.version>
<snailjob.version>1.10.0</snailjob.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.42</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<lombok.version>1.18.44</lombok.version>
<bouncycastle.version>1.83</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>3.3.2</ip2region.version>
<ip2region.version>3.3.7</ip2region.version>
<!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 -->
@@ -46,9 +46,9 @@
<!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.3-20251210</anyline.version>
<anyline.version>8.7.3-20260319</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.8.4</warm-flow.version>
<warm-flow.version>1.8.5</warm-flow.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
@@ -284,7 +284,7 @@
<!-- 加密包引入 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
@@ -375,8 +375,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
@@ -14,6 +14,7 @@ import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.utils.MessageUtils;
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.core.utils.ip.AddressUtils;
import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils;
@@ -73,7 +74,7 @@ public class UserActionListener implements SaTokenListener {
SpringUtils.context().publishEvent(logininforEvent);
// 更新登录信息
loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
log.info("user doLogin, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
}
/**
@@ -85,7 +86,7 @@ public class UserActionListener implements SaTokenListener {
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
log.info("user doLogout, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
}
/**
@@ -97,7 +98,7 @@ public class UserActionListener implements SaTokenListener {
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
log.info("user doKickout, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
}
/**
@@ -109,7 +110,7 @@ public class UserActionListener implements SaTokenListener {
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
log.info("user doReplaced, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
}
/**
@@ -8,7 +8,7 @@ server:
# undertow 配置
undertow:
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
max-http-post-size: -1
max-http-post-size: 1GB
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 512
@@ -215,6 +215,7 @@ xss:
# 排除链接
excludeUrls:
- /system/notice
- /warm-flow/save-json
--- # 分布式锁 lock4j 全局配置
lock4j:
Binary file not shown.
+1 -1
View File
@@ -14,7 +14,7 @@
</description>
<properties>
<revision>5.5.3</revision>
<revision>5.6.2</revision>
</properties>
<dependencyManagement>
@@ -52,7 +52,7 @@ public interface CacheNames {
String SYS_USER_NAME = "sys_user_name#30d";
/**
* 用户
* 用户
*/
String SYS_NICKNAME = "sys_nickname#30d";
@@ -23,8 +23,8 @@ public class FlowCopyDTO implements Serializable {
private Long userId;
/**
* 用户
* 用户
*/
private String userName;
private String nickName;
}
@@ -48,7 +48,8 @@ public class StartProcessDTO implements Serializable {
public Map<String, Object> getVariables() {
if (variables == null) {
return new HashMap<>(16);
variables = new HashMap<>(16);
return variables;
}
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables;
@@ -30,7 +30,7 @@ public class UserOnlineDTO implements Serializable {
private String deptName;
/**
* 用户名称
* 用户账号
*/
private String userName;
@@ -21,18 +21,18 @@ public interface UserService {
String selectUserNameById(Long userId);
/**
* 通过用户ID查询用户账户
* 通过用户ID查询用户昵称
*
* @param userId 用户ID
* @return 用户
* @return 用户
*/
String selectNicknameById(Long userId);
/**
* 通过用户ID查询用户账户
* 通过用户ID查询用户昵称
*
* @param userIds 用户ID 多个用逗号隔开
* @return 用户
* @return 用户
*/
String selectNicknameByIds(String userIds);
@@ -93,11 +93,11 @@ public interface UserService {
List<UserDTO> selectUsersByPostIds(List<Long> postIds);
/**
* 根据用户 ID 列表查询用户称映射关系
* 根据用户 ID 列表查询用户称映射关系
*
* @param userIds 用户 ID 列表
* @return Map,其中 key 为用户 IDvalue 为对应的用户
* @return Map,其中 key 为用户 IDvalue 为对应的用户
*/
Map<Long, String> selectUserNamesByIds(List<Long> userIds);
Map<Long, String> selectUserNicksByIds(List<Long> userIds);
}
@@ -90,9 +90,9 @@ public class RegionUtils {
try {
String region = ip2Region.search(ipString);
if (StringUtils.isBlank(region)) {
region = UNKNOWN_ADDRESS;
return UNKNOWN_ADDRESS;
}
return region;
return StringUtils.replace(region, "0", UNKNOWN_ADDRESS);
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", ipString);
return UNKNOWN_ADDRESS;
@@ -109,9 +109,9 @@ public class RegionUtils {
try {
String region = ip2Region.search(ipBytes);
if (StringUtils.isBlank(region)) {
region = UNKNOWN_ADDRESS;
return UNKNOWN_ADDRESS;
}
return region;
return StringUtils.replace(region, "0", UNKNOWN_ADDRESS);
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
return UNKNOWN_ADDRESS;
@@ -1,5 +1,6 @@
package org.dromara.common.core.utils.sql;
import cn.hutool.core.exceptions.UtilException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
@@ -7,7 +8,7 @@ import org.dromara.common.core.utils.StringUtils;
/**
* sql操作工具类
*
* @author ruoyi
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SqlUtil {
@@ -15,7 +16,7 @@ public class SqlUtil {
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
public static final String SQL_REGEX = "\u000B|%0A|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
@@ -46,11 +47,21 @@ public class SqlUtil {
if (StringUtils.isEmpty(value)) {
return;
}
// ==================== 核心增强:自动转义单引号 ====================
// 不抛异常、不破坏业务、不改变原方法行为、自动防注入
if (value.contains("'")) {
throw new UtilException("请求参数包含非法字符【'】,已禁止执行");
}
// ==================== 原有逻辑不变 ====================
String normalizedValue = value.replaceAll("\\p{Z}|\\s", "");
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords) {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
throw new IllegalArgumentException("参数存在SQL注入风险");
if (StringUtils.indexOf(normalizedValue, sqlKeyword) > -1) {
throw new UtilException("请求参数包含敏感关键词'" + sqlKeyword + "',可能存在安全风险");
}
}
}
}
@@ -7,6 +7,8 @@ import io.swagger.v3.oas.models.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.doc.config.properties.SpringDocProperties;
import org.dromara.common.doc.core.resolver.JavadocResolver;
import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver;
import org.dromara.common.doc.handler.OpenApiHandler;
import org.springdoc.core.configuration.SpringDocConfiguration;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
@@ -84,8 +86,9 @@ public class SpringDocConfig {
SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider,
List<JavadocResolver> javadocResolvers) {
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, javadocResolvers);
}
/**
@@ -112,6 +115,14 @@ public class SpringDocConfig {
};
}
/**
* 注册SaToken JavaDoc权限注解解析器
*/
@Bean
public JavadocResolver saTokenAnnotationJavadocResolver() {
return new SaTokenAnnotationMetadataJavadocResolver();
}
/**
* 单独使用一个类便于判断 解决springdoc路径拼接重复问题
*
@@ -0,0 +1,175 @@
package org.dromara.common.doc.core.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.Data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 存储权限框架注解解析后的权限和角色信息
*
* @author AprilWind
*/
@Data
@JsonInclude(Include.NON_EMPTY)
public class SaTokenSecurityMetadata {
/**
* 权限校验信息列表(对应 @SaCheckPermission 注解)
*/
private List<AuthInfo> permissions = new ArrayList<>();
/**
* 角色校验信息列表(对应 @SaCheckRole 注解)
*/
private List<AuthInfo> roles = new ArrayList<>();
/**
* 是否忽略校验(对应 @SaIgnore 注解)
*/
private boolean ignore = false;
/**
* 添加权限信息
*
* @param values 权限值数组
* @param mode 校验模式(AND/OR
* @param type 权限类型
* @param orRoles 或角色数组
*/
public void addPermission(String[] values, String mode, String type, String[] orRoles) {
if (values != null && values.length > 0) {
AuthInfo authInfo = new AuthInfo();
authInfo.setValues(values);
authInfo.setMode(mode);
authInfo.setType(type);
if (orRoles != null && orRoles.length > 0) {
authInfo.setOrValues(orRoles);
authInfo.setOrType("role");
}
this.permissions.add(authInfo);
}
}
/**
* 添加角色信息
*
* @param values 角色值数组
* @param mode 校验模式(AND/OR
* @param type 角色类型
*/
public void addRole(String[] values, String mode, String type) {
if (values != null && values.length > 0) {
AuthInfo authInfo = new AuthInfo();
authInfo.setValues(values);
authInfo.setMode(mode);
authInfo.setType(type);
this.roles.add(authInfo);
}
}
/**
* 生成 Markdown 结构的权限说明
*
* @return Markdown 文本
*/
public String toMarkdownString() {
StringBuilder sb = new StringBuilder();
sb.append("<br><h3>访问权限</h3><br>");
if (ignore) {
sb.append("> **权限策略**:忽略权限检查<br>");
return sb.toString();
}
if (!ignore && permissions.isEmpty() && roles.isEmpty()){
sb.append("> **权限策略**:需要登录<br><br>");
return sb.toString();
}
if (!permissions.isEmpty()) {
sb.append("**权限校验:**<br><br>");
permissions.forEach(p -> {
String permTags = Arrays.stream(p.getValues())
.map(v -> "`" + v + "`")
.collect(Collectors.joining(p.getModeSymbol()));
sb.append("- ").append(permTags).append("<br>");
if (p.getOrValues() != null && p.getOrValues().length > 0) {
String orTags = Arrays.stream(p.getOrValues())
.map(v -> "`" + v + "`")
.collect(Collectors.joining(p.getModeSymbol()));
sb.append(" - 或角色:").append(orTags).append("<br>");
}
});
sb.append("<br>");
}
if (!roles.isEmpty()) {
sb.append("**角色校验:**<br><br>");
roles.forEach(r -> {
String roleTags = Arrays.stream(r.getValues())
.map(v -> "`" + v + "`")
.collect(Collectors.joining(r.getModeSymbol()));
sb.append("- ").append(roleTags).append("<br>");
});
}
return sb.toString().trim();
}
/**
* 认证信息
*/
@Data
@JsonInclude(Include.NON_EMPTY)
public static class AuthInfo {
/**
* 权限或角色值数组
*/
private String[] values;
/**
* 校验模式(AND/OR
*/
private String mode;
/**
* 类型说明
*/
private String type;
/**
* 或权限/角色值数组(用于权限校验时的或角色校验)
*/
private String[] orValues;
/**
* 或值的类型(role/permission
*/
private String orType;
/**
* 重写mode的获取方法,返回符号而非文字
* @return AND→&OR→|,默认→&
*/
public String getModeSymbol() {
if (mode == null) {
return " & "; // 默认AND,返回&
}
return "AND".equalsIgnoreCase(mode) ? " & " : " | ";
}
}
}
@@ -0,0 +1,163 @@
package org.dromara.common.doc.core.resolver;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ClassLoaderUtil;
import io.swagger.v3.oas.models.Operation;
import org.springframework.web.method.HandlerMethod;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Map;
import java.util.function.Supplier;
/**
* 抽象元数据 Javadoc 解析器
*
* @param <M> 元数据类型
* @author 秋辞未寒
*/
public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResolver {
public static final int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
public static final int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
private final Supplier<M> metadataProvider;
private final int order;
public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider) {
this(metadataProvider, LOWEST_PRECEDENCE);
}
public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider, int order) {
this.metadataProvider = metadataProvider;
this.order = order;
}
@Override
public int getOrder() {
return order;
}
@Override
public String resolve(HandlerMethod handlerMethod, Operation operation) {
return resolve(handlerMethod, operation, metadataProvider.get());
}
/**
* 执行解析并返回解析到的 Javadoc 内容
* @param handlerMethod 处理器方法
* @param operation Swagger Operation实例
* @param metadata 元信息
* @return 解析到的 Javadoc 内容
*/
public abstract String resolve(HandlerMethod handlerMethod, Operation operation, M metadata);
/**
* 检查处理器方法所属的类上是否存在注解
* @param handlerMethod 处理器方法
* @param annotationClass 注解类
* @return 是否存在注解
*/
public boolean hasClassAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationClass);
}
/**
* 检查处理器方法所属的类上是否存在注解
* @param handlerMethod 处理器方法
* @param annotationTypeName 注解类名称
* @return 是否存在注解
*/
public boolean hasClassAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationTypeName);
}
/**
* 检查处理器方法上是否存在注解
* @param handlerMethod 处理器方法
* @param annotationClass 注解类
* @return 是否存在注解
*/
public boolean hasMethodAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationClass);
}
/**
* 检查处理器方法上是否存在注解
* @param handlerMethod 处理器方法
* @param annotationTypeName 注解类名称
* @return 是否存在注解
*/
public boolean hasMethodAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationTypeName);
}
/**
* 检查处理器方法上是否存在注解
* @param handlerMethod 处理器方法
* @param annotationClass 注解类
* @return 是否存在注解
*/
public boolean hasAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
return this.hasClassAnnotation(handlerMethod, annotationClass) || this.hasMethodAnnotation(handlerMethod, annotationClass);
}
/**
* 检查处理器方法上是否存在注解
* @param handlerMethod 处理器方法
* @param annotationTypeName 注解类名称
* @return 是否存在注解
*/
public boolean hasAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
return this.hasClassAnnotation(handlerMethod, annotationTypeName) || this.hasMethodAnnotation(handlerMethod, annotationTypeName);
}
/**
* 获取处理器方法所属类上的注解的值
* @param handlerMethod 处理器方法
* @param annotationClass 注解类
* @return 注解的值
*/
public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
}
/**
* 获取处理器方法所属类上的注解的值
* @param handlerMethod 处理器方法
* @param annotationClassName 注解类名称
* @return 注解的值
*/
@SuppressWarnings("unchecked")
public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
}
/**
* 获取处理器方法上的注解的值
* @param handlerMethod 处理器方法
* @param annotationClass 注解类
* @return 注解的值
*/
public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
}
/**
* 获取处理器方法所属类上的注解的值
* @param handlerMethod 处理器方法
* @param annotationClassName 注解类名称
* @return 注解的值
*/
@SuppressWarnings("unchecked")
public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
}
private Map<String, Object> getAnnotationValueMap(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationClass) {
return AnnotationUtil.getAnnotationValueMap(annotatedElement, annotationClass);
}
}
@@ -0,0 +1,51 @@
package org.dromara.common.doc.core.resolver;
import io.swagger.v3.oas.models.Operation;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.Ordered;
import org.springframework.web.method.HandlerMethod;
/**
* Javadoc解析器接口
*
* @author echo
* @author 秋辞未寒
*/
public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
/**
* 检查解析器是否支持解析 HandlerMethod
* @param handlerMethod 处理器方法
* @return 是否支持解析
*/
boolean supports(HandlerMethod handlerMethod);
/**
* 执行解析并返回解析到的 Javadoc 内容
* @param handlerMethod 处理器方法
* @param operation Swagger Operation实例
* @return 解析到的 Javadoc 内容
*/
String resolve(HandlerMethod handlerMethod, Operation operation);
/**
* 获取解析器优先级
*/
default int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
/**
* 获取解析器的名称
*
* @return 解析器名称
*/
default String getName() {
return this.getClass().getSimpleName();
}
@Override
default int compareTo(@NotNull JavadocResolver o) {
return Integer.compare(getOrder(), o.getOrder());
}
}
@@ -0,0 +1,164 @@
package org.dromara.common.doc.core.resolver;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassLoaderUtil;
import io.swagger.v3.oas.models.Operation;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
import org.springframework.web.method.HandlerMethod;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* 基于JavaDoc的SaToken权限解析器
*
* @author echo
* @author 秋辞未寒
*/
@SuppressWarnings("unchecked")
@Slf4j
public class SaTokenAnnotationMetadataJavadocResolver extends AbstractMetadataJavadocResolver<SaTokenSecurityMetadata> {
/**
* 默认元数据提供者,每次解析都会创建一个新的元数据对象
*/
public static final Supplier<SaTokenSecurityMetadata> DEFAULT_METADATA_PROVIDER = SaTokenSecurityMetadata::new;
private static final String BASE_CLASS_NAME = "cn.dev33.satoken.annotation";
private static final String SA_CHECK_ROLE_CLASS_NAME = BASE_CLASS_NAME + ".SaCheckRole";
private static final String SA_CHECK_PERMISSION_CLASS_NAME = BASE_CLASS_NAME + ".SaCheckPermission";
private static final String SA_IGNORE_CLASS_NAME = BASE_CLASS_NAME + ".SaIgnore";
private static final String SA_CHECK_LOGIN_NAME = BASE_CLASS_NAME + ".SaCheckLogin";
private static final Class<? extends Annotation> SA_CHECK_ROLE_CLASS;
private static final Class<? extends Annotation> SA_CHECK_PERMISSION_CLASS;
private static final Class<? extends Annotation> SA_IGNORE_CLASS;
private static final Class<? extends Annotation> SA_CHECK_LOGIN_CLASS;
static {
// 通过类加载器去加载注解类Class实例
SA_CHECK_ROLE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_ROLE_CLASS_NAME, false);
SA_CHECK_PERMISSION_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_PERMISSION_CLASS_NAME, false);
SA_IGNORE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_IGNORE_CLASS_NAME, false);
SA_CHECK_LOGIN_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_LOGIN_NAME, false);
if (log.isDebugEnabled()) {
log.debug("SaTokenAnnotationJavadocResolver init success, load annotation class: {}", List.of(SA_CHECK_ROLE_CLASS, SA_CHECK_PERMISSION_CLASS, SA_IGNORE_CLASS, SA_CHECK_LOGIN_CLASS));
}
}
public SaTokenAnnotationMetadataJavadocResolver() {
this(DEFAULT_METADATA_PROVIDER);
}
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider) {
super(metadataProvider);
}
public SaTokenAnnotationMetadataJavadocResolver(int order) {
this(DEFAULT_METADATA_PROVIDER,order);
}
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider, int order) {
super(metadataProvider,order);
}
@Override
public boolean supports(HandlerMethod handlerMethod) {
return hasAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS) || hasAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS) || hasAnnotation(handlerMethod, SA_IGNORE_CLASS);
}
@Override
public String resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) {
// 检查是否忽略校验
if(hasAnnotation(handlerMethod, SA_IGNORE_CLASS_NAME)){
metadata.setIgnore(true);
return metadata.toMarkdownString();
}
// 解析权限校验
resolvePermissionCheck(handlerMethod, metadata);
// 解析角色校验
resolveRoleCheck(handlerMethod, metadata);
return metadata.toMarkdownString();
}
/**
* 解析权限校验
*/
private void resolvePermissionCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
// 解析获取方法上的注解角色信息
if (hasMethodAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
resolvePermissionAnnotation(metadata, annotationValueMap);
}
// 解析获取类上的注解角色信息
if (hasClassAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
resolvePermissionAnnotation(metadata, annotationValueMap);
}
}
/**
* 解析权限注解
*/
private void resolvePermissionAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
try {
// 反射获取注解属性
Object value = annotationValueMap.get( "value");
Object mode = annotationValueMap.get( "mode");
Object type = annotationValueMap.get( "type");
Object orRole = annotationValueMap.get( "orRole");
String[] values = Convert.toStrArray(value);
String modeStr = mode != null ? mode.toString() : "AND";
String typeStr = type != null ? type.toString() : "";
String[] orRoles = Convert.toStrArray(orRole);
metadata.addPermission(values, modeStr, typeStr, orRoles);
} catch (Exception ignore) {
// 忽略解析错误
}
}
/**
* 解析角色校验
*/
private void resolveRoleCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
// 解析获取方法上的注解角色信息
if (hasMethodAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
resolveRoleAnnotation(metadata, annotationValueMap);
}
// 解析获取类上的注解角色信息
if (hasClassAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
resolveRoleAnnotation(metadata, annotationValueMap);
}
}
/**
* 解析角色注解
*/
private void resolveRoleAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
try {
// 反射获取注解属性
Object value = annotationValueMap.get("value");
Object mode = annotationValueMap.get("mode");
Object type = annotationValueMap.get("type");
String[] values = Convert.toStrArray(value);
String modeStr = mode != null ? mode.toString() : "AND";
String typeStr = type != null ? type.toString() : "";
metadata.addRole(values, modeStr, typeStr);
} catch (Exception ignore) {
// 忽略解析错误
}
}
}
@@ -12,6 +12,7 @@ import io.swagger.v3.oas.models.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.doc.core.resolver.JavadocResolver;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
@@ -83,6 +84,11 @@ public class OpenApiHandler extends OpenAPIService {
*/
private final PropertyResolverUtils propertyResolverUtils;
/**
* Javadoc解析器接口
*/
private final List<JavadocResolver> javadocResolvers;
/**
* The javadoc provider.
*/
@@ -123,7 +129,8 @@ public class OpenApiHandler extends OpenAPIService {
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
Optional<JavadocProvider> javadocProvider) {
Optional<JavadocProvider> javadocProvider,
List<JavadocResolver> javadocResolvers) {
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
if (openAPI.isPresent()) {
this.openAPI = openAPI.get();
@@ -140,6 +147,7 @@ public class OpenApiHandler extends OpenAPIService {
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
this.javadocProvider = javadocProvider;
this.javadocResolvers = javadocResolvers == null ? new ArrayList<>() : javadocResolvers;
if (springDocConfigProperties.isUseFqn())
TypeNameResolver.std.setUseFqn(true);
}
@@ -220,6 +228,22 @@ public class OpenApiHandler extends OpenAPIService {
securityParser.buildSecurityRequirement(securityRequirements, operation);
}
if (javadocProvider.isPresent()) {
String description = javadocProvider.get().getMethodJavadocDescription(handlerMethod.getMethod());
String summary = javadocProvider.get().getFirstSentence(description);
if (StringUtils.isNotBlank(description)){
operation.setSummary(summary);
}
// 调用解析器提取JavaDoc中的权限信息
if (javadocResolvers != null && !javadocResolvers.isEmpty()) {
for (JavadocResolver resolver : javadocResolvers) {
String desc = resolver.resolve(handlerMethod, operation);
description = description + desc;
}
operation.setDescription(description);
}
}
return operation;
}
+1 -1
View File
@@ -24,7 +24,7 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
<dependency>
@@ -6,10 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.plugin.*;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.core.EncryptContext;
@@ -42,19 +39,19 @@ public class MybatisEncryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation;
}
@Override
public Object plugin(Object target) {
Object target = invocation.getTarget();
if (target instanceof ParameterHandler parameterHandler) {
// 进行加密操作
Object parameterObject = parameterHandler.getParameterObject();
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
this.encryptHandler(parameterObject);
}
}
return target;
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
@@ -33,7 +33,7 @@ public class ExcelBigNumberConvert implements Converter<Long> {
@Override
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
return Convert.toLong(cellData.getData());
return Convert.toLong(cellData.getStringValue());
}
@Override
@@ -64,8 +64,10 @@ public class CellMergeHandler {
// 当前行数据字段值
Object currentRowObjFieldVal = ReflectUtils.invokeGetter(currentRowObj, field.getName());
// 空值跳过不处理
if (currentRowObjFieldVal == null || "".equals(currentRowObjFieldVal)) {
// 空值视为合并中断,需要先收口上一段合并区间
if (isBlankCell(currentRowObjFieldVal)) {
appendMergeResult(result, rowRepeatCellMap.get(field), i - 1, colNum);
rowRepeatCellMap.remove(field);
continue;
}
@@ -78,7 +80,6 @@ public class CellMergeHandler {
// 获取 单元格合并Map 中字段值
RepeatCell repeatCell = rowRepeatCellMap.get(field);
Object cellValue = repeatCell.value();
int current = repeatCell.current();
// 检查是否满足合并条件
// currentRowObj 当前行数据
@@ -86,33 +87,14 @@ public class CellMergeHandler {
// cellMerge 当前行字段合并注解
boolean merge = isMerge(currentRowObj, rows.get(i - 1), cellMerge);
// 是否添加到结果集
boolean isAddResult = false;
// 最新行
int lastRow = i + rowIndex - 1;
// 如果当前行字段值和缓存中的字段值不相等,或不满足合并条件,则替换
if (!currentRowObjFieldVal.equals(cellValue) || !merge) {
appendMergeResult(result, repeatCell, i - 1, colNum);
rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i));
isAddResult = true;
}
// 如果最后一行不能合并,检查之前的数据是否需要合并;如果最后一行可以合并,则直接合并到最后
if (i == rows.size() - 1) {
isAddResult = true;
if (i > current) {
lastRow = i + rowIndex;
}
}
if (isAddResult && i > current) {
//如果是同一行,则跳过合并
if (current + rowIndex == lastRow) {
continue;
}
result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
}
}
appendMergeResult(result, rowRepeatCellMap.get(field), rows.size() - 1, colNum);
rowRepeatCellMap.remove(field);
}
return result;
}
@@ -167,6 +149,17 @@ public class CellMergeHandler {
return true;
}
private boolean isBlankCell(Object value) {
return value == null || StrUtil.isBlankIfStr(value);
}
private void appendMergeResult(List<CellRangeAddress> result, RepeatCell repeatCell, int endIndex, int colNum) {
if (repeatCell == null || endIndex <= repeatCell.current()) {
return;
}
result.add(new CellRangeAddress(repeatCell.current() + rowIndex, endIndex + rowIndex, colNum, colNum));
}
/**
* 单元格合并
*/
@@ -130,8 +130,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
}
if (ObjectUtil.isNotEmpty(options)) {
// 仅当下拉可选项不为空时执行
if (options.size() > 20) {
// 这里限制如果可选项大于20,则使用额外表形式
int totalCharacter = options.stream().mapToInt(String::length).sum() + options.size();
if (options.size() > 20 || totalCharacter > 255) {
// 这里限制如果可选项大于20 或 总字符数超过255,则使用额外表形式
dropDownWithSheet(helper, workbook, sheet, index, options);
} else {
// 否则使用固定值形式
@@ -12,6 +12,7 @@ import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MessageUtils;
@@ -76,16 +77,16 @@ public class RepeatSubmitAspect {
*/
@AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {
if (jsonResult instanceof R<?> r) {
try {
try {
if (jsonResult instanceof R<?> r) {
// 成功则不删除redis数据 保证在有效时间内无法重复提交
if (r.getCode() == R.SUCCESS) {
if (r.getCode() == HttpStatus.SUCCESS) {
return;
}
RedisUtils.deleteObject(KEY_CACHE.get());
} finally {
KEY_CACHE.remove();
}
} finally {
KEY_CACHE.remove();
}
}
@@ -2,10 +2,12 @@ package org.dromara.common.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.sql.SqlUtil;
import org.dromara.common.mybatis.enums.DataBaseType;
import javax.sql.DataSource;
@@ -14,6 +16,8 @@ import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 数据库助手
@@ -24,6 +28,7 @@ import java.util.List;
public class DataBaseHelper {
private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
private static final Map<String, DataBaseType> DB_TYPE_CACHE = new ConcurrentHashMap<>();
/**
* 获取当前数据源对应的数据库类型
@@ -37,13 +42,37 @@ public class DataBaseHelper {
*/
public static DataBaseType getDataBaseType() {
DataSource dataSource = DS.determineDataSource();
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new RuntimeException("获取数据库类型失败", e);
}
String dsKey = DynamicDataSourceContextHolder.peek();
final String key = dsKey != null ? dsKey : "primary";
return DB_TYPE_CACHE.computeIfAbsent(key, k -> {
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new RuntimeException("获取数据库类型失败", e);
}
});
}
/**
* 获取指定数据源对应的数据库类型
*
* @param dsName 数据源名称
* @return 指定数据库对应的 DataBaseType 枚举,找不到时默认返回 MY_SQL
* @throws ServiceException 当获取数据库连接或元数据出现异常时抛出业务异常
*/
public static DataBaseType getDataBaseType(String dsName) {
DataSource dataSource = DS.getDataSource(dsName);
return DB_TYPE_CACHE.computeIfAbsent(dsName, k -> {
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new RuntimeException("获取数据库类型失败", e);
}
});
}
/**
@@ -61,6 +90,8 @@ public class DataBaseHelper {
*/
public static String findInSet(Object var1, String var2) {
String var = Convert.toStr(var1);
SqlUtil.filterKeyword(var);
SqlUtil.filterKeyword(var2);
return switch (getDataBaseType()) {
// instr(',0,100,101,' , ',100,') <> 0
case ORACLE -> "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var);
@@ -74,7 +105,9 @@ public class DataBaseHelper {
}
/**
* 获取当前加载的数据库名
* 获取当前注册的数据源名称列表。
*
* @return 数据源名称列表
*/
public static List<String> getDataSourceNameList() {
return new ArrayList<>(DS.getDataSources().keySet());
@@ -23,7 +23,7 @@ import java.util.function.Supplier;
* @version 3.5.0
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
public class DataPermissionHelper {
private static final String DATA_PERMISSION_KEY = "data:permission";
@@ -9,7 +9,6 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.redis.config.properties.RedissonProperties;
import org.dromara.common.redis.handler.KeyPrefixHandler;
import org.dromara.common.redis.handler.RedisExceptionHandler;
@@ -21,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.VirtualThreadTaskExecutor;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@@ -65,9 +63,10 @@ public class RedisConfig {
// 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
.setUseScriptCache(true)
.setCodec(codec);
if (SpringUtils.isVirtual()) {
config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-"));
}
// netty 对虚拟线程适配有问题 暂时禁止使用
//if (SpringUtils.isVirtual()) {
// config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-"));
//}
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用单机模式
@@ -41,6 +41,11 @@
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
</project>
@@ -113,7 +113,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject {
* @param key 键名称
* @return object
*/
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
@Override
public <T> T getObject(String key, Class<T> classType) {
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
@@ -63,7 +63,7 @@ public class LoginHelper {
/**
* 获取用户(多级缓存)
*/
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
public static <T extends LoginUser> T getLoginUser() {
SaSession session = StpUtil.getTokenSession();
if (ObjectUtil.isNull(session)) {
@@ -75,7 +75,7 @@ public class LoginHelper {
/**
* 获取用户基于token
*/
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
public static <T extends LoginUser> T getLoginUser(String token) {
SaSession session = StpUtil.getTokenSessionByToken(token);
if (ObjectUtil.isNull(session)) {
@@ -25,9 +25,24 @@ import java.util.Objects;
@Slf4j
public class SensitiveHandler extends JsonSerializer<String> implements ContextualSerializer {
private SensitiveStrategy strategy;
private String[] roleKey;
private String[] perms;
private final SensitiveStrategy strategy;
private final String[] roleKey;
private final String[] perms;
/**
* 提供给 jackson 创建上下文序列化器时使用 不然会报错
*/
public SensitiveHandler() {
this.strategy = null;
this.roleKey = null;
this.perms = null;
}
public SensitiveHandler(SensitiveStrategy strategy, String[] strings, String[] perms) {
this.strategy = strategy;
this.roleKey = strings;
this.perms = perms;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
@@ -48,10 +63,7 @@ public class SensitiveHandler extends JsonSerializer<String> implements Contextu
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
this.strategy = annotation.strategy();
this.roleKey = annotation.roleKey();
this.perms = annotation.perms();
return this;
return new SensitiveHandler(annotation.strategy(), annotation.roleKey(), annotation.perms());
}
return prov.findValueSerializer(property.getType(), property);
}
@@ -13,7 +13,7 @@ public interface TransConstant {
String USER_ID_TO_NAME = "user_id_to_name";
/**
* 用户id转用户
* 用户id转用户
*/
String USER_ID_TO_NICKNAME = "user_id_to_nickname";
@@ -31,7 +31,18 @@ public class TranslationHandler extends JsonSerializer<Object> implements Contex
*/
public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();
private Translation translation;
private final Translation translation;
/**
* 提供给 jackson 创建上下文序列化器时使用 不然会报错
*/
public TranslationHandler() {
this.translation = null;
}
public TranslationHandler(Translation translation) {
this.translation = translation;
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
@@ -63,8 +74,7 @@ public class TranslationHandler extends JsonSerializer<Object> implements Contex
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Translation translation = property.getAnnotation(Translation.class);
if (Objects.nonNull(translation)) {
this.translation = translation;
return this;
return new TranslationHandler(translation);
}
return prov.findValueSerializer(property.getType(), property);
}
@@ -7,7 +7,7 @@ import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
/**
* 用户称翻译实现
* 用户称翻译实现
*
* @author may
*/
@@ -1,11 +1,14 @@
package org.dromara.common.web.config;
import io.undertow.UndertowOptions;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.server.handlers.DisallowedMethodsHandler;
import io.undertow.util.HttpString;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.core.task.VirtualThreadTaskExecutor;
@@ -18,6 +21,9 @@ import org.springframework.core.task.VirtualThreadTaskExecutor;
@AutoConfiguration
public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
@Autowired
private ServerProperties serverProperties;
/**
* 自定义 Undertow 配置
* <p>
@@ -31,6 +37,11 @@ public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServle
*/
@Override
public void customize(UndertowServletWebServerFactory factory) {
long bytes = serverProperties.getUndertow().getMaxHttpPostSize().toBytes();
factory.addBuilderCustomizers(builder -> {
builder.setServerOption(UndertowOptions.MULTIPART_MAX_ENTITY_SIZE, bytes);
});
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
// 配置 WebSocket 部署信息,设置 WebSocket 使用的缓冲区池
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
@@ -2,7 +2,6 @@ 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 +13,7 @@ import org.dromara.common.core.exception.SseException;
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.boot.json.JsonParseException;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.expression.ExpressionException;
@@ -80,7 +80,7 @@ public class GlobalExceptionHandler {
public R<Void> handleServletException(ServletException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return R.fail(e.getMessage());
return R.fail("发生未知异常,请联系管理员");
}
/**
@@ -119,7 +119,7 @@ public class GlobalExceptionHandler {
public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}'不存在.", requestURI);
return R.fail(HttpStatus.HTTP_NOT_FOUND, e.getMessage());
return R.fail(HttpStatus.HTTP_NOT_FOUND, "请求地址不存在");
}
/**
@@ -150,7 +150,7 @@ public class GlobalExceptionHandler {
public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return R.fail(e.getMessage());
return R.fail("发生未知异常,请联系管理员");
}
/**
@@ -160,7 +160,7 @@ public class GlobalExceptionHandler {
public R<Void> handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return R.fail(e.getMessage());
return R.fail("发生系统异常,请联系管理员");
}
/**
@@ -0,0 +1,198 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* SaToken 权限测试 接口文档输出测试
*
* @author AprilWind
*/
@Slf4j
@RestController
@RequestMapping("/demo/saTokenDoc")
public class SaTokenTestController {
// ====================== 基础场景:单一校验规则 ======================
/**
* 场景1:仅登录校验(无角色/权限限制,只需登录态)
*/
@SaCheckLogin
@GetMapping("/basic/loginOnly")
public R<Void> loginOnly() {
log.info("【场景1】仅登录校验通过");
return R.ok("仅登录校验通过,无需角色/权限");
}
/**
* 场景2:单一角色校验(AND模式,默认)
*/
@SaCheckRole("admin")
@GetMapping("/basic/singleRole")
public R<Void> singleRole() {
log.info("【场景2】单一角色(admin)校验通过");
return R.ok("拥有admin角色,校验通过");
}
/**
* 场景3:单一权限校验(AND模式,默认)
*/
@SaCheckPermission("system:user:view")
@GetMapping("/basic/singlePermission")
public R<Void> singlePermission() {
log.info("【场景3】单一权限(system:user:view)校验通过");
return R.ok("拥有system:user:view权限,校验通过");
}
/**
* 场景4:忽略所有权限校验(SaIgnore优先级最高)
*/
@SaIgnore
@SaCheckRole("none_exist") // 该注解会被忽略
@GetMapping("/basic/ignoreAll")
public R<Void> ignoreAll() {
log.info("【场景4】SaIgnore忽略所有权限校验");
return R.ok("SaIgnore生效,所有权限校验被忽略");
}
// ====================== 进阶场景:多条件组合(AND/OR ======================
/**
* 场景5:多角色AND模式(必须同时拥有所有角色)
*/
@SaCheckRole(value = {"admin", "operator"}, mode = SaMode.AND)
@GetMapping("/advance/multiRoleAnd")
public R<Void> multiRoleAnd() {
log.info("【场景5】多角色AND模式(admin+operator)校验通过");
return R.ok("同时拥有admin和operator角色,校验通过");
}
/**
* 场景6:多角色OR模式(拥有任一角色即可)
*/
@SaCheckRole(value = {"admin", "test"}, mode = SaMode.OR)
@GetMapping("/advance/multiRoleOr")
public R<Void> multiRoleOr() {
log.info("【场景6】多角色OR模式(admin|test)校验通过");
return R.ok("拥有admin或test角色,校验通过");
}
/**
* 场景7:多权限AND模式(必须同时拥有所有权限)
*/
@SaCheckPermission(value = {"system:user:edit", "system:log:view"}, mode = SaMode.AND)
@GetMapping("/advance/multiPermAnd")
public R<Void> multiPermAnd() {
log.info("【场景7】多权限AND模式(system:user:edit+system:log:view)校验通过");
return R.ok("同时拥有system:user:edit和system:log:view权限,校验通过");
}
/**
* 场景8:多权限OR模式(拥有任一权限即可)
*/
@SaCheckPermission(value = {"system:user:add", "system:user:delete"}, mode = SaMode.OR)
@GetMapping("/advance/multiPermOr")
public R<Void> multiPermOr() {
log.info("【场景8】多权限OR模式(system:user:add|system:user:delete)校验通过");
return R.ok("拥有system:user:add或system:user:delete权限,校验通过");
}
// ====================== 高级场景:通配符/混合组合 ======================
/**
* 场景9:权限通配符匹配(前缀匹配)
* 拥有system:user:* 即可匹配所有用户模块权限
*/
@SaCheckPermission("system:user:*")
@GetMapping("/advanced/permWildcardPrefix")
public R<Void> permWildcardPrefix() {
log.info("【场景9】权限通配符(system:user:*)校验通过");
return R.ok("拥有system:user:*前缀权限,校验通过");
}
/**
* 场景10:角色通配符匹配(前缀匹配)
* 拥有admin_* 即可匹配所有admin开头的角色
*/
@SaCheckRole("admin_*")
@GetMapping("/advanced/roleWildcardPrefix")
public R<Void> roleWildcardPrefix() {
log.info("【场景10】角色通配符(admin_*)校验通过");
return R.ok("拥有admin_*前缀角色,校验通过");
}
/**
* 场景11:权限+角色混合AND模式(所有条件必须满足)
* 需同时满足:拥有admin角色 + 拥有system:user:all权限
*/
@SaCheckRole("admin")
@SaCheckPermission("system:user:all")
@GetMapping("/advanced/mixRolePermAnd")
public R<Void> mixRolePermAnd() {
log.info("【场景11】角色+权限混合AND(admin+system:user:all)校验通过");
return R.ok("拥有admin角色且拥有system:user:all权限,校验通过");
}
/**
* 场景12:权限+角色混合OR模式(任一条件满足即可)
* 满足任一:拥有super_admin角色 | 拥有system:manage权限
*/
@SaCheckRole(value = {"super_admin"}, mode = SaMode.OR)
@SaCheckPermission(value = {"system:manage"}, mode = SaMode.OR)
@GetMapping("/advanced/mixRolePermOr")
public R<Void> mixRolePermOr() {
log.info("【场景12】角色+权限混合OR(super_admin|system:manage)校验通过");
return R.ok("拥有super_admin角色或system:manage权限,校验通过");
}
/**
* 场景13:orRole参数(权限校验失败时,兜底角色校验)
* 核心逻辑:无system:user:export权限时,检查是否有admin/operator角色
*/
@SaCheckPermission(value = "system:user:export", orRole = {"admin", "operator"})
@GetMapping("/advanced/permWithOrRole")
public R<Void> permWithOrRole() {
log.info("【场景13】权限+orRole兜底校验通过");
return R.ok("拥有system:user:export权限,或拥有admin/operator角色,校验通过");
}
// ====================== 特殊场景:临时权限/注解覆盖 ======================
/**
* 场景14:SaIgnore局部覆盖(方法注解覆盖类注解,若有)
* 假设类上有@SaCheckLogin,方法上@SaIgnore会覆盖
*/
@SaIgnore
@GetMapping("/special/ignoreOverride")
public R<Void> ignoreOverride() {
log.info("【场景14】SaIgnore覆盖类级别权限注解");
return R.ok("方法级SaIgnore覆盖类级别权限校验");
}
/**
* 场景15:临时权限校验(SaCheckPermission逻辑:临时权限>永久权限)
* 注:临时权限需通过SaToken API手动设置,如 SaHolder.getStpLogic().setTempPermission("system:temp:test")
*/
@SaCheckPermission("system:temp:test")
@GetMapping("/special/tempPermission")
public R<Void> tempPermission() {
log.info("【场景15】临时权限(system:temp:test)校验通过");
return R.ok("临时权限校验通过(需先通过API设置临时权限)");
}
/**
* 场景16:登录类型指定(多端登录场景,如PC/APP/小程序)
* 注:需配合SaToken多账号体系配置
*/
@SaCheckLogin(type = "PC") // 仅校验PC端的登录态
@GetMapping("/special/loginTypeSpecify")
public R<Void> loginTypeSpecify() {
log.info("【场景16】指定登录类型(PC)校验通过");
return R.ok("仅PC端登录态校验通过");
}
}
@@ -17,7 +17,7 @@ import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.bo.TestDemoImportVo;
import org.dromara.demo.domain.vo.TestDemoImportVo;
import org.dromara.demo.domain.vo.TestDemoVo;
import org.dromara.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
@@ -35,8 +35,8 @@ public class ExportDemoVo implements Serializable {
/**
* 用户昵称
*/
@ExcelProperty(value = "用户", index = 0)
@NotEmpty(message = "用户不能为空", groups = AddGroup.class)
@ExcelProperty(value = "用户昵称", index = 0)
@NotEmpty(message = "用户昵称不能为空", groups = AddGroup.class)
private String nickName;
/**
@@ -1,10 +1,12 @@
package org.dromara.demo.domain.bo;
package org.dromara.demo.domain.vo;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.dromara.demo.domain.TestDemo;
/**
* 测试单表业务对象 test_demo
@@ -13,6 +15,7 @@ import jakarta.validation.constraints.NotNull;
* @date 2021-07-26
*/
@Data
@AutoMapper(target = TestDemo.class)
public class TestDemoImportVo {
/**
@@ -157,19 +157,6 @@ public class GenController extends BaseController {
genCode(response, data);
}
/**
* 生成代码(自定义路径)
*
* @param tableId 表ID
*/
@SaCheckPermission("tool:gen:code")
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
@GetMapping("/genCode/{tableId}")
public R<Void> genCode(@PathVariable("tableId") Long tableId) {
genTableService.generatorCode(tableId);
return R.ok();
}
/**
* 同步数据库
*
@@ -101,12 +101,12 @@ public class GenTable extends BaseEntity {
private String functionAuthor;
/**
* 生成代码方式(0zip压缩包 1自定义路径
* 生成代码方式(固定为0zip压缩包)
*/
private String genType;
/**
* 生成路径(不填默认项目路径
* 生成路径(兼容历史字段
*/
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
private String genPath;
@@ -23,7 +23,6 @@ import org.dromara.common.core.exception.ServiceException;
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.file.FileUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -40,7 +39,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
@@ -222,6 +220,8 @@ public class GenTableServiceImpl implements IGenTableService {
@Transactional(rollbackFor = Exception.class)
@Override
public void updateGenTable(GenTable genTable) {
genTable.setGenType("0");
genTable.setGenPath("/");
String options = JsonUtils.toJsonString(genTable.getParams());
genTable.setOptions(options);
int row = baseMapper.updateById(genTable);
@@ -331,7 +331,7 @@ public class GenTableServiceImpl implements IGenTableService {
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName());
for (String template : templates) {
// 渲染模板
StringWriter sw = new StringWriter();
@@ -357,40 +357,6 @@ public class GenTableServiceImpl implements IGenTableService {
return outputStream.toByteArray();
}
/**
* 生成代码(自定义路径)
*
* @param tableId 表名称
*/
@Override
public void generatorCode(Long tableId) {
// 查询表信息
GenTable table = baseMapper.selectGenTableById(tableId);
// 设置主键列信息
setPkColumn(table);
VelocityInitializer.initVelocity();
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
for (String template : templates) {
if (!StringUtils.containsAny(template, "sql.vm", "api.ts.vm", "types.ts.vm", "index.vue.vm", "index-tree.vue.vm")) {
// 渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
tpl.merge(context, sw);
try {
String path = getGenPath(table, template);
FileUtils.writeUtf8String(sw.toString(), path);
} catch (Exception e) {
throw new ServiceException("渲染模板失败,表名:" + table.getTableName());
}
}
}
}
/**
* 同步数据库
*
@@ -478,7 +444,7 @@ public class GenTableServiceImpl implements IGenTableService {
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName());
for (String template : templates) {
// 渲染模板
StringWriter sw = new StringWriter();
@@ -523,6 +489,9 @@ public class GenTableServiceImpl implements IGenTableService {
* @param table 业务表信息
*/
public void setPkColumn(GenTable table) {
if (CollUtil.isEmpty(table.getColumns())) {
throw new ServiceException("表【" + table.getTableName() + "】字段为空,请检查表结构");
}
for (GenTableColumn column : table.getColumns()) {
if (column.isPk()) {
table.setPkColumn(column);
@@ -557,19 +526,5 @@ public class GenTableServiceImpl implements IGenTableService {
}
}
/**
* 获取代码生成地址
*
* @param table 业务表信息
* @param template 模板文件路径
* @return 生成地址
*/
public static String getGenPath(GenTable table, String template) {
String genPath = table.getGenPath();
if (StringUtils.equals(genPath, "/")) {
return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table);
}
return genPath + File.separator + VelocityUtils.getFileName(template, table);
}
}
@@ -110,13 +110,6 @@ public interface IGenTableService {
*/
byte[] downloadCode(Long tableId);
/**
* 生成代码(自定义路径)
*
* @param tableId 表名称
*/
void generatorCode(Long tableId);
/**
* 同步数据库
*
@@ -109,7 +109,7 @@ public class VelocityUtils {
*
* @return 模板列表
*/
public static List<String> getTemplateList(String tplCategory) {
public static List<String> getTemplateList(String tplCategory, String dsName) {
List<String> templates = new ArrayList<>();
templates.add("vm/java/domain.java.vm");
templates.add("vm/java/vo.java.vm");
@@ -119,7 +119,7 @@ public class VelocityUtils {
templates.add("vm/java/serviceImpl.java.vm");
templates.add("vm/java/controller.java.vm");
templates.add("vm/xml/mapper.xml.vm");
DataBaseType dataBaseType = DataBaseHelper.getDataBaseType();
DataBaseType dataBaseType = DataBaseHelper.getDataBaseType(dsName);
if (dataBaseType.isOracle()) {
templates.add("vm/sql/oracle/sql.vm");
} else if (dataBaseType.isPostgreSql()) {
@@ -21,6 +21,7 @@ import java.util.stream.IntStream;
*
* @author 老马
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@Component
@JobExecutor(name = "testMapJobAnnotation")
public class TestMapJobAnnotation {
@@ -23,6 +23,7 @@ import java.util.stream.IntStream;
*
* @author 老马
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@Component
@JobExecutor(name = "testMapReduceAnnotation1")
public class TestMapReduceAnnotation1 {
@@ -83,7 +83,7 @@ public class SysDictDataController extends BaseController {
}
/**
* 新增字典类型
* 新增字典数据
*/
@SaCheckPermission("system:dict:add")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@@ -98,7 +98,7 @@ public class SysDictDataController extends BaseController {
}
/**
* 修改保存字典类型
* 修改保存字典数据
*/
@SaCheckPermission("system:dict:edit")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@@ -113,12 +113,12 @@ public class SysDictDataController extends BaseController {
}
/**
* 删除字典类型
* 删除字典数据
*
* @param dictCodes 字典code串
*/
@SaCheckPermission("system:dict:remove")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@Log(title = "字典数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public R<Void> remove(@PathVariable Long[] dictCodes) {
dictDataService.deleteDictDataByIds(Arrays.asList(dictCodes));
@@ -21,7 +21,7 @@ public class SysUserOnline {
private String deptName;
/**
* 用户名称
* 用户账号
*/
private String userName;
@@ -39,7 +39,7 @@ public class SysUserBo extends BaseEntity {
*/
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
@Size(min = 2, max = 30, message = "用户账号长度必须在{min}到{max}个字符之间")
private String userName;
/**
@@ -104,6 +104,6 @@ public class SysDeptVo implements Serializable {
/**
* 子部门
*/
private List<SysDept> children = new ArrayList<>();
private List<SysDeptVo> children = new ArrayList<>();
}
@@ -34,13 +34,13 @@ public class SysUserExportVo implements Serializable {
/**
* 用户账号
*/
@ExcelProperty(value = "登录名称")
@ExcelProperty(value = "用户账号")
private String userName;
/**
* 用户昵称
*/
@ExcelProperty(value = "用户")
@ExcelProperty(value = "用户")
private String nickName;
/**
@@ -38,13 +38,13 @@ public class SysUserImportVo implements Serializable {
/**
* 用户账号
*/
@ExcelProperty(value = "登录名称")
@ExcelProperty(value = "用户账号")
private String userName;
/**
* 用户昵称
*/
@ExcelProperty(value = "用户")
@ExcelProperty(value = "用户")
private String nickName;
/**
@@ -101,7 +101,7 @@ public interface ISysUserService {
String selectUserPostGroup(Long userId);
/**
* 校验用户名称是否唯一
* 校验用户账号是否唯一
*
* @param user 用户信息
* @return 结果
@@ -229,6 +229,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public String getDictLabel(String dictType, String dictValue, String separator) {
List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
if (CollUtil.isEmpty(datas)) {
return StringUtils.EMPTY;
}
Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
if (StringUtils.containsAny(dictValue, separator)) {
return Arrays.stream(dictValue.split(separator))
@@ -250,6 +253,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public String getDictValue(String dictType, String dictLabel, String separator) {
List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
if (CollUtil.isEmpty(datas)) {
return StringUtils.EMPTY;
}
Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue);
if (StringUtils.containsAny(dictLabel, separator)) {
return Arrays.stream(dictLabel.split(separator))
@@ -269,6 +275,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public Map<String, String> getAllDictByDictType(String dictType) {
List<SysDictDataVo> list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
if (CollUtil.isEmpty(list)) {
return new HashMap<>();
}
// 保证顺序
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (SysDictDataVo vo : list) {
@@ -286,6 +295,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public DictTypeDTO getDictType(String dictType) {
SysDictTypeVo vo = SpringUtils.getAopProxy(this).selectDictTypeByType(dictType);
if (ObjectUtil.isNull(vo)) {
return null;
}
return BeanUtil.toBean(vo, DictTypeDTO.class);
}
@@ -298,6 +310,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public List<DictDataDTO> getDictData(String dictType) {
List<SysDictDataVo> list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
}
return BeanUtil.copyToList(list, DictDataDTO.class);
}
@@ -112,7 +112,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
/**
* 根据用户ID查询菜单
*
* @param userId 用户名称
* @param userId 用户ID
* @return 菜单列表
*/
@Override
@@ -232,7 +232,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
* 校验用户名称是否唯一
* 校验用户账号是否唯一
*
* @param user 用户信息
* @return 结果
@@ -497,6 +497,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
roleList.remove(SystemConstants.SUPER_ADMIN_ID);
}
// 移除超管角色后若无剩余角色说明仅选了超管角色且不允许分配显式报错
if (roleList.isEmpty()) {
throw new ServiceException("不允许为普通用户分配超级管理员角色,请至少选择一个其他角色");
}
// 校验是否有权限访问这些角色含数据权限控制
if (roleMapper.selectRoleCount(roleList) != roleList.size()) {
throw new ServiceException("没有权限访问角色的数据");
@@ -594,10 +599,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
* 通过用户ID查询用户账户
* 通过用户ID查询用户昵称
*
* @param userId 用户ID
* @return 用户账户
* @return 用户昵称
*/
@Override
@Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId")
@@ -608,10 +613,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
* 通过用户ID查询用户账户
* 通过用户ID查询用户昵称
*
* @param userIds 用户ID 多个用逗号隔开
* @return 用户账户
* @return 用户昵称
*/
@Override
public String selectNicknameByIds(String userIds) {
@@ -751,13 +756,13 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
* 根据用户 ID 列表查询用户称映射关系
* 根据用户 ID 列表查询用户称映射关系
*
* @param userIds 用户 ID 列表
* @return Map其中 key 为用户 IDvalue 为对应的用户
* @return Map其中 key 为用户 IDvalue 为对应的用户
*/
@Override
public Map<Long, String> selectUserNamesByIds(List<Long> userIds) {
public Map<Long, String> selectUserNicksByIds(List<Long> userIds) {
if (CollUtil.isEmpty(userIds)) {
return Collections.emptyMap();
}
@@ -23,26 +23,6 @@ public interface FlowConstant {
*/
String INITIATOR_DEPT_ID = "initiatorDeptId";
/**
* 委托
*/
String DELEGATE_TASK = "delegateTask";
/**
* 转办
*/
String TRANSFER_TASK = "transferTask";
/**
* 加签
*/
String ADD_SIGNATURE = "addSignature";
/**
* 减签
*/
String REDUCTION_SIGNATURE = "reductionSignature";
/**
* 流程分类Id转名称
*/
@@ -0,0 +1,53 @@
package org.dromara.workflow.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 任务操作类型枚举
*
* @author may
*/
@Getter
@AllArgsConstructor
public enum TaskOperationEnum {
/**
* 委派
*/
DELEGATE_TASK("delegateTask", "委派"),
/**
* 转办
*/
TRANSFER_TASK("transferTask", "转办"),
/**
* 加签
*/
ADD_SIGNATURE("addSignature", "加签"),
/**
* 减签
*/
REDUCTION_SIGNATURE("reductionSignature", "减签");
private final String code;
private final String desc;
private static final Map<String, TaskOperationEnum> CODE_MAP = Arrays.stream(values())
.collect(Collectors.toConcurrentMap(TaskOperationEnum::getCode, Function.identity()));
/**
* 根据 code 获取枚举
*/
public static TaskOperationEnum getByCode(String code) {
return CODE_MAP.get(code);
}
}
@@ -1,5 +1,6 @@
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
@@ -45,6 +46,7 @@ public class FlwDefinitionController extends BaseController {
* @param pageQuery 分页
*/
@GetMapping("/list")
@SaCheckPermission("workflow:definition:list")
public TableDataInfo<FlowDefinitionVo> list(FlowDefinition flowDefinition, PageQuery pageQuery) {
return flwDefinitionService.queryList(flowDefinition, pageQuery);
}
@@ -56,6 +58,7 @@ public class FlwDefinitionController extends BaseController {
* @param pageQuery 分页
*/
@GetMapping("/unPublishList")
@SaCheckPermission("workflow:definition:list")
public TableDataInfo<FlowDefinitionVo> unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) {
return flwDefinitionService.unPublishList(flowDefinition, pageQuery);
}
@@ -66,6 +69,7 @@ public class FlwDefinitionController extends BaseController {
* @param id 流程定义id
*/
@GetMapping(value = "/{id}")
@SaCheckPermission("workflow:definition:query")
public R<Definition> getInfo(@PathVariable Long id) {
return R.ok(defService.getById(id));
}
@@ -79,6 +83,7 @@ public class FlwDefinitionController extends BaseController {
@PostMapping
@RepeatSubmit()
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("workflow:definition:add")
public R<Boolean> add(@RequestBody FlowDefinition flowDefinition) {
return R.ok(defService.checkAndSave(flowDefinition));
}
@@ -92,6 +97,7 @@ public class FlwDefinitionController extends BaseController {
@PutMapping
@RepeatSubmit()
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("workflow:definition:edit")
public R<Boolean> edit(@RequestBody FlowDefinition flowDefinition) {
return R.ok(defService.updateById(flowDefinition));
}
@@ -104,6 +110,7 @@ public class FlwDefinitionController extends BaseController {
@Log(title = "流程定义", businessType = BusinessType.INSERT)
@PutMapping("/publish/{id}")
@RepeatSubmit()
@SaCheckPermission("workflow:definition:publish")
public R<Boolean> publish(@PathVariable Long id) {
return R.ok(flwDefinitionService.publish(id));
}
@@ -117,6 +124,7 @@ public class FlwDefinitionController extends BaseController {
@PutMapping("/unPublish/{id}")
@RepeatSubmit()
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("workflow:definition:publish")
public R<Boolean> unPublish(@PathVariable Long id) {
return R.ok(defService.unPublish(id));
}
@@ -126,6 +134,7 @@ public class FlwDefinitionController extends BaseController {
*/
@Log(title = "流程定义", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@SaCheckPermission("workflow:definition:remove")
public R<Void> remove(@PathVariable List<Long> ids) {
return toAjax(flwDefinitionService.removeDef(ids));
}
@@ -139,6 +148,7 @@ public class FlwDefinitionController extends BaseController {
@PostMapping("/copy/{id}")
@RepeatSubmit()
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("workflow:definition:copy")
public R<Boolean> copy(@PathVariable Long id) {
return R.ok(defService.copyDef(id));
}
@@ -151,6 +161,7 @@ public class FlwDefinitionController extends BaseController {
*/
@Log(title = "流程定义", businessType = BusinessType.IMPORT)
@PostMapping("/importDef")
@SaCheckPermission("workflow:definition:import")
public R<Boolean> importDef(MultipartFile file, String category) {
return R.ok(flwDefinitionService.importJson(file, category));
}
@@ -164,6 +175,7 @@ public class FlwDefinitionController extends BaseController {
*/
@Log(title = "流程定义", businessType = BusinessType.EXPORT)
@PostMapping("/exportDef/{id}")
@SaCheckPermission("workflow:definition:export")
public void exportDef(@PathVariable Long id, HttpServletResponse response) throws IOException {
flwDefinitionService.exportDef(id, response);
}
@@ -174,6 +186,7 @@ public class FlwDefinitionController extends BaseController {
* @param id 流程定义id
*/
@GetMapping("/xmlString/{id}")
@SaCheckPermission("workflow:definition:query")
public R<String> xmlString(@PathVariable Long id) {
return R.ok("操作成功", defService.exportJson(id));
}
@@ -188,6 +201,7 @@ public class FlwDefinitionController extends BaseController {
@PutMapping("/active/{id}")
@Transactional(rollbackFor = Exception.class)
@Log(title = "流程定义", businessType = BusinessType.UPDATE)
@SaCheckPermission("workflow:definition:active")
public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
return R.ok(active ? defService.active(id) : defService.unActive(id));
}
@@ -1,5 +1,6 @@
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.convert.Convert;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
@@ -46,6 +47,7 @@ public class FlwInstanceController extends BaseController {
* @param pageQuery 分页
*/
@GetMapping("/pageByRunning")
@SaCheckPermission("workflow:instance:list")
public TableDataInfo<FlowInstanceVo> selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
return flwInstanceService.selectRunningInstanceList(flowInstanceBo, pageQuery);
}
@@ -57,6 +59,7 @@ public class FlwInstanceController extends BaseController {
* @param pageQuery 分页
*/
@GetMapping("/pageByFinish")
@SaCheckPermission("workflow:instance:list")
public TableDataInfo<FlowInstanceVo> selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
return flwInstanceService.selectFinishInstanceList(flowInstanceBo, pageQuery);
}
@@ -67,6 +70,7 @@ public class FlwInstanceController extends BaseController {
* @param businessId 业务id
*/
@GetMapping("/getInfo/{businessId}")
@SaCheckPermission("workflow:instance:query")
public R<FlowInstanceVo> getInfo(@PathVariable Long businessId) {
return R.ok(flwInstanceService.queryByBusinessId(businessId));
}
@@ -78,6 +82,7 @@ public class FlwInstanceController extends BaseController {
*/
@DeleteMapping("/deleteByBusinessIds/{businessIds}")
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
@SaCheckPermission("workflow:instance:remove")
public R<Void> deleteByBusinessIds(@PathVariable List<Long> businessIds) {
return toAjax(flwInstanceService.deleteByBusinessIds(StreamUtils.toList(businessIds, Convert::toStr)));
}
@@ -89,6 +94,7 @@ public class FlwInstanceController extends BaseController {
*/
@DeleteMapping("/deleteByInstanceIds/{instanceIds}")
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
@SaCheckPermission("workflow:instance:remove")
public R<Void> deleteByInstanceIds(@PathVariable List<Long> instanceIds) {
return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds));
}
@@ -100,6 +106,7 @@ public class FlwInstanceController extends BaseController {
*/
@DeleteMapping("/deleteHisByInstanceIds/{instanceIds}")
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
@SaCheckPermission("workflow:instance:remove")
public R<Void> deleteHisByInstanceIds(@PathVariable List<Long> instanceIds) {
return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds));
}
@@ -112,6 +119,7 @@ public class FlwInstanceController extends BaseController {
@RepeatSubmit()
@PutMapping("/cancelProcessApply")
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
@SaCheckPermission("workflow:instance:cancel")
public R<Void> cancelProcessApply(@RequestBody FlowCancelBo bo) {
return toAjax(flwInstanceService.cancelProcessApply(bo));
}
@@ -125,6 +133,7 @@ public class FlwInstanceController extends BaseController {
@RepeatSubmit()
@PutMapping("/active/{id}")
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
@SaCheckPermission("workflow:instance:active")
public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
return R.ok(active ? insService.active(id) : insService.unActive(id));
}
@@ -136,6 +145,7 @@ public class FlwInstanceController extends BaseController {
* @param pageQuery 分页
*/
@GetMapping("/pageByCurrent")
@SaCheckPermission("workflow:instance:currentList")
public TableDataInfo<FlowInstanceVo> selectCurrentInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
return flwInstanceService.selectCurrentInstanceList(flowInstanceBo, pageQuery);
}
@@ -146,6 +156,7 @@ public class FlwInstanceController extends BaseController {
* @param businessId 业务id
*/
@GetMapping("/flowHisTaskList/{businessId}")
@SaCheckPermission("workflow:instance:query")
public R<Map<String, Object>> flowHisTaskList(@PathVariable String businessId) {
return R.ok(flwInstanceService.flowHisTaskList(businessId));
}
@@ -156,6 +167,7 @@ public class FlwInstanceController extends BaseController {
* @param instanceId 流程实例id
*/
@GetMapping("/instanceVariable/{instanceId}")
@SaCheckPermission("workflow:instance:variableQuery")
public R<Map<String, Object>> instanceVariable(@PathVariable Long instanceId) {
return R.ok(flwInstanceService.instanceVariable(instanceId));
}
@@ -168,6 +180,7 @@ public class FlwInstanceController extends BaseController {
@RepeatSubmit()
@PutMapping("/updateVariable")
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
@SaCheckPermission("workflow:instance:variable")
public R<Void> updateVariable(@Validated @RequestBody FlowVariableBo bo) {
return toAjax(flwInstanceService.updateVariable(bo));
}
@@ -180,6 +193,7 @@ public class FlwInstanceController extends BaseController {
@Log(title = "流程实例管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/invalid")
@SaCheckPermission("workflow:instance:invalid")
public R<Boolean> invalid(@Validated @RequestBody FlowInvalidBo bo) {
return R.ok(flwInstanceService.processInvalid(bo));
}
@@ -61,7 +61,8 @@ public class BackProcessBo implements Serializable {
public Map<String, Object> getVariables() {
if (variables == null) {
return new HashMap<>(16);
variables = new HashMap<>(16);
return variables;
}
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables;
@@ -23,8 +23,8 @@ public class FlowCopyBo implements Serializable {
private Long userId;
/**
* 用户
* 用户
*/
private String userName;
private String nickName;
}
@@ -30,7 +30,8 @@ public class FlowNextNodeBo implements Serializable {
public Map<String, Object> getVariables() {
if (variables == null) {
return new HashMap<>(16);
variables = new HashMap<>(16);
return variables;
}
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables;
@@ -53,7 +53,8 @@ public class StartProcessBo implements Serializable {
public Map<String, Object> getVariables() {
if (variables == null) {
return new HashMap<>(16);
variables = new HashMap<>(16);
return variables;
}
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables;
@@ -40,6 +40,11 @@ public class TaskOperationBo implements Serializable {
@NotNull(message = "任务id不能为空")
private Long taskId;
/**
* 消息类型
*/
private List<String> messageType;
/**
* 意见或备注信息可选
*/
@@ -24,10 +24,10 @@ public class FlowCopyVo implements Serializable {
private Long userId;
/**
* 用户
* 用户
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "userId")
private String userName;
private String nickName;
public FlowCopyVo(Long userId) {
this.userId = userId;
@@ -80,11 +80,13 @@ public class WorkflowGlobalListener implements GlobalListener {
NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable);
Set<String> copyList = nodeExt.getCopySettings();
if (CollUtil.isNotEmpty(copyList)) {
List<Long> userIds = StreamUtils.toList(copyList, Convert::toLong);
Map<Long, String> nickNameMap = userService.selectUserNicksByIds(userIds);
List<FlowCopyBo> list = StreamUtils.toList(copyList, x -> {
FlowCopyBo bo = new FlowCopyBo();
Long id = Convert.toLong(x);
bo.setUserId(id);
bo.setUserName(userService.selectUserNameById(id));
bo.setNickName(nickNameMap.getOrDefault(id, StringUtils.EMPTY));
return bo;
});
variable.put(FlowConstant.FLOW_COPY_LIST, list);
@@ -157,9 +159,11 @@ public class WorkflowGlobalListener implements GlobalListener {
String[] userIdArray = userIds.split(StringUtils.SEPARATOR);
if (userIdArray.length > 0) {
flowTask.setPermissionList(List.of(userIdArray));
// 移除已处理的状态变量
variable.remove(nodeKey);
FlowEngine.insService().removeVariables(flowTask.getInstanceId(),nodeKey);
if (TaskStatusEnum.PASS.getStatus().equals(taskStatus)) {
// 办理指定人变量只消费一次驳回指定人变量需要保留给后续重复驳回
variable.remove(nodeKey);
FlowEngine.insService().removeVariables(flowTask.getInstanceId(), nodeKey);
}
}
}
@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -98,7 +99,14 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
}
case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message);
case SMS_MESSAGE -> {
// TODO: 补充短信发送逻辑
// LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
// // 根据具体短信服务商参数用法传参
// map.put("code", "1234");
// // 自动获取一个短信服务商
// SmsBlend smsBlend = SmsFactory.getSmsBlend();
// // 指定获取一个短信服务商 configKey
// SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
// SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
log.info("【短信发送 - TODO】用户数量={} 内容={}", userList.size(), message);
}
default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum);
@@ -119,6 +127,9 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
@Override
public String applyNodeCode(Long definitionId) {
List<Node> firstBetweenNode = FlowEngine.nodeService().getFirstBetweenNode(definitionId, new HashMap<>());
if (CollUtil.isEmpty(firstBetweenNode)) {
throw new ServiceException("流程定义缺少申请人节点,请检查流程定义配置");
}
return firstBetweenNode.get(0).getNodeCode();
}
}
@@ -111,8 +111,14 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
@Override
public FlowInstanceVo queryByBusinessId(Long businessId) {
FlowInstance instance = this.selectInstByBusinessId(Convert.toStr(businessId));
if (ObjectUtil.isNull(instance)) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
}
FlowInstanceVo instanceVo = BeanUtil.toBean(instance, FlowInstanceVo.class);
Definition definition = defService.getById(instanceVo.getDefinitionId());
if (ObjectUtil.isNull(definition)) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
}
instanceVo.setFlowName(definition.getFlowName());
instanceVo.setFlowCode(definition.getFlowCode());
instanceVo.setVersion(definition.getVersion());
@@ -187,6 +193,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
log.warn("未找到对应的流程实例信息,无法执行删除操作。");
return false;
}
// 发送事件
processDeleteHandler(flowInstances);
return insService.remove(StreamUtils.toList(flowInstances, FlowInstance::getId));
}
@@ -199,27 +207,13 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
@Transactional(rollbackFor = Exception.class)
public boolean deleteByInstanceIds(List<Long> instanceIds) {
// 获取实例信息
List<Instance> instances = insService.getByIds(instanceIds);
if (CollUtil.isEmpty(instances)) {
List<FlowInstance> flowInstances = flowInstanceMapper.selectByIds(instanceIds);
if (CollUtil.isEmpty(flowInstances)) {
log.warn("未找到对应的流程实例信息,无法执行删除操作。");
return false;
}
// 获取定义信息
Map<Long, Definition> definitionMap = StreamUtils.toMap(
defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)),
Definition::getId,
Function.identity()
);
// 逐一触发删除事件
instances.forEach(instance -> {
Definition definition = definitionMap.get(instance.getDefinitionId());
if (ObjectUtil.isNull(definition)) {
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
return;
}
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
});
// 发送事件
processDeleteHandler(flowInstances);
// 删除实例
return insService.remove(instanceIds);
}
@@ -233,26 +227,13 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
@Transactional(rollbackFor = Exception.class)
public boolean deleteHisByInstanceIds(List<Long> instanceIds) {
// 获取实例信息
List<Instance> instances = insService.getByIds(instanceIds);
if (CollUtil.isEmpty(instances)) {
List<FlowInstance> flowInstances = flowInstanceMapper.selectByIds(instanceIds);
if (CollUtil.isEmpty(flowInstances)) {
log.warn("未找到对应的流程实例信息,无法执行删除操作。");
return false;
}
// 获取定义信息
Map<Long, Definition> definitionMap = StreamUtils.toMap(
defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)),
Definition::getId,
Function.identity()
);
// 逐一触发删除事件
instances.forEach(instance -> {
Definition definition = definitionMap.get(instance.getDefinitionId());
if (ObjectUtil.isNull(definition)) {
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
return;
}
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
});
// 发送事件
processDeleteHandler(flowInstances);
List<FlowTask> flowTaskList = flwTaskService.selectByInstIds(instanceIds);
if (CollUtil.isNotEmpty(flowTaskList)) {
FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId));
@@ -263,6 +244,35 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
return true;
}
private void processDeleteHandler(List<FlowInstance> flowInstances) {
String userId = LoginHelper.getUserIdStr();
for (FlowInstance flowInstance : flowInstances) {
//如果创建人与当前登陆人一致或者当前登陆人为管理员才能删除
if (LoginHelper.isSuperAdmin() || flowInstance.getCreateBy().equals(userId)) {
continue;
}
throw new ServiceException("权限不足,无法删除流程实例信息!");
}
// 获取定义信息
Map<Long, Definition> definitionMap = StreamUtils.toMap(
defService.getByIds(StreamUtils.toList(flowInstances, Instance::getDefinitionId)),
Definition::getId,
Function.identity()
);
// 逐一触发删除事件
flowInstances.forEach(instance -> {
Definition definition = definitionMap.get(instance.getDefinitionId());
if (ObjectUtil.isNull(definition)) {
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
return;
}
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
});
}
/**
* 撤销流程
*
@@ -279,8 +289,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
if (definition == null) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
}
String message = bo.getMessage();
String userIdStr = LoginHelper.getUserIdStr();
if (!LoginHelper.isSuperAdmin() && !instance.getCreateBy().equals(userIdStr)) {
throw new ServiceException("权限不足,无法撤销流程!");
}
String message = bo.getMessage();
BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus());
FlowParams flowParams = FlowParams.build()
.message(message)
@@ -383,6 +396,9 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
@Override
public Map<String, Object> instanceVariable(Long instanceId) {
FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId);
if (ObjectUtil.isNull(flowInstance)) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
}
Map<String, Object> variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap());
List<Map<String, Object>> variableList = variableMap.entrySet().stream()
.map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue()))
@@ -102,7 +102,7 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
* @param sources 数据来源枚举类或字典类型
* @return 构建的 `NodeExt` 对象
*/
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
private NodeExt buildNodeExt(String code, String name, int type, List<Object> sources) {
NodeExt nodeExt = new NodeExt();
nodeExt.setCode(code);
@@ -1,6 +1,7 @@
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 com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -9,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
import org.dromara.common.core.domain.model.TaskAssigneeBody;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -125,7 +127,14 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(FlowSpel entity){
//TODO 做一些数据校验,如唯一约束
if (StringUtils.isNotBlank(entity.getViewSpel())) {
boolean exists = baseMapper.exists(new LambdaQueryWrapper<FlowSpel>()
.eq(FlowSpel::getViewSpel, entity.getViewSpel())
.ne(ObjectUtil.isNotNull(entity.getId()), FlowSpel::getId, entity.getId()));
if (exists) {
throw new ServiceException("SpEL表达式已存在,请勿重复添加");
}
}
}
/**
@@ -137,7 +146,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
if (isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
@@ -244,7 +244,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
List<Long> longIds = StreamUtils.toList(ids, Convert::toLong);
Map<Long, String> rawMap = switch (type) {
case USER -> userService.selectUserNamesByIds(longIds);
case USER -> userService.selectUserNicksByIds(longIds);
case ROLE -> roleService.selectRoleNamesByIds(longIds);
case DEPT -> deptService.selectDeptNamesByIds(longIds);
case POST -> postService.selectPostNamesByIds(longIds);
@@ -44,8 +44,8 @@ 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.TaskOperationEnum;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.FlowInstanceBizExt;
import org.dromara.workflow.domain.bo.*;
@@ -127,6 +127,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
// 已存在流程
BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus());
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId()));
if (CollUtil.isEmpty(taskList)) {
throw new ServiceException("流程实例缺少任务,请检查流程定义配置");
}
taskService.mergeVariable(flowInstance, variables);
insService.updateById(flowInstance);
StartProcessReturnDTO dto = new StartProcessReturnDTO();
@@ -143,9 +146,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
throw new ServiceException("流程【" + startProcessBo.getFlowCode() + "】未发布,请先在流程设计器中发布流程定义");
}
Dict dict = JsonUtils.parseMap(definition.getExt());
boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(FlowConstant.AUTO_PASS);
variables.put(FlowConstant.AUTO_PASS, autoPass);
variables.put(FlowConstant.BUSINESS_CODE, this.generateBusinessCode(bizExt));
boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(AUTO_PASS);
variables.put(AUTO_PASS, autoPass);
variables.put(BUSINESS_CODE, this.generateBusinessCode(bizExt));
FlowParams flowParams = FlowParams.build()
.handler(startProcessBo.getHandler())
.flowCode(startProcessBo.getFlowCode())
@@ -156,6 +159,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
this.buildFlowInstanceBizExt(instance, bizExt);
// 申请人执行流程
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
if (CollUtil.isEmpty(taskList)) {
throw new ServiceException("流程启动失败,未生成任务");
}
if (taskList.size() > 1) {
throw new ServiceException("请检查流程第一个环节是否为申请人!");
}
@@ -207,11 +213,11 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
List<FlowCopyBo> flowCopyList = completeTaskBo.getFlowCopyList();
// 设置抄送人
Map<String, Object> variables = completeTaskBo.getVariables();
variables.put(FlowConstant.FLOW_COPY_LIST, flowCopyList);
variables.put(FLOW_COPY_LIST, flowCopyList);
// 消息类型
variables.put(FlowConstant.MESSAGE_TYPE, messageType);
variables.put(MESSAGE_TYPE, messageType);
// 消息通知
variables.put(FlowConstant.MESSAGE_NOTICE, notice);
variables.put(MESSAGE_NOTICE, notice);
FlowTask flowTask = flowTaskMapper.selectById(taskId);
@@ -219,12 +225,16 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
throw new ServiceException("流程任务不存在或任务已审批!");
}
Instance ins = insService.getById(flowTask.getInstanceId());
if (ObjectUtil.isNull(ins)) {
throw new ServiceException("流程实例不存在");
}
// 检查流程状态是否为草稿已撤销或已退回状态若是则执行流程提交监听
if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) {
variables.put(FlowConstant.SUBMIT, true);
variables.put(SUBMIT, true);
}
Map<String, Object> insVariableMap = ins.getVariableMap();
// 设置弹窗处理人
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap());
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), insVariableMap);
if (CollUtil.isNotEmpty(assigneeMap)) {
variables.putAll(assigneeMap);
}
@@ -240,7 +250,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
.flowStatus(BusinessStatusEnum.WAITING.getStatus())
.hisStatus(TaskStatusEnum.PASS.getStatus())
.hisTaskExt(completeTaskBo.getFileId());
Boolean autoPass = Convert.toBool(variables.getOrDefault(AUTO_PASS, false));
Boolean autoPass = Convert.toBool(insVariableMap.getOrDefault(AUTO_PASS, false));
skipTask(taskId, flowParams, flowTask.getInstanceId(), autoPass);
return true;
}
@@ -274,9 +284,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
flowParams.
message("流程引擎自动审批!").
variable(Map.of(
FlowConstant.SUBMIT, false,
FlowConstant.FLOW_COPY_LIST, Collections.emptyList(),
FlowConstant.MESSAGE_NOTICE, StringUtils.EMPTY));
SUBMIT, false,
FLOW_COPY_LIST, Collections.emptyList(),
MESSAGE_NOTICE, StringUtils.EMPTY));
skipTask(task.getId(), flowParams, instanceId, true);
}
}
@@ -341,7 +351,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
FlowParams flowParams = FlowParams.build()
.skipType(SkipType.NONE.getKey())
.hisStatus(TaskStatusEnum.COPY.getStatus())
.message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName));
.message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getNickName));
HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams);
hisTask.setCreateTime(updateTime);
hisTask.setUpdateTime(updateTime);
@@ -482,15 +492,18 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
throw new ServiceException("任务不存在!");
}
Instance inst = insService.getById(task.getInstanceId());
if (ObjectUtil.isNull(inst)) {
throw new ServiceException("流程实例不存在");
}
BusinessStatusEnum.checkBackStatus(inst.getFlowStatus());
Long definitionId = task.getDefinitionId();
String applyNodeCode = flwCommonService.applyNodeCode(definitionId);
Map<String, Object> variable = new HashMap<>();
// 消息类型
variable.put(FlowConstant.MESSAGE_TYPE, messageType);
variable.put(MESSAGE_TYPE, messageType);
// 消息通知
variable.put(FlowConstant.MESSAGE_NOTICE, notice);
variable.put(MESSAGE_NOTICE, notice);
FlowParams flowParams = FlowParams.build()
.nodeCode(bo.getNodeCode())
@@ -513,6 +526,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
@Override
public List<Node> getBackTaskNode(Long taskId, String nowNodeCode) {
FlowTask task = flowTaskMapper.selectById(taskId);
if (ObjectUtil.isNull(task)) {
throw new ServiceException("任务不存在!");
}
List<Node> nodeCodes = nodeService.getByNodeCodes(Collections.singletonList(nowNodeCode), task.getDefinitionId());
if (!CollUtil.isNotEmpty(nodeCodes)) {
return nodeCodes;
@@ -597,7 +613,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
}
FlowTaskVo flowTaskVo = BeanUtil.toBean(task, FlowTaskVo.class);
Instance instance = insService.getById(task.getInstanceId());
if (ObjectUtil.isNull(instance)) {
throw new ServiceException("流程实例不存在");
}
Definition definition = defService.getById(task.getDefinitionId());
if (ObjectUtil.isNull(definition)) {
throw new ServiceException("流程定义不存在");
}
flowTaskVo.setFlowStatus(instance.getFlowStatus());
flowTaskVo.setVersion(definition.getVersion());
flowTaskVo.setFlowCode(definition.getFlowCode());
@@ -640,11 +662,23 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
Long taskId = bo.getTaskId();
Map<String, Object> variables = bo.getVariables();
Task task = taskService.getById(taskId);
if (ObjectUtil.isNull(task)) {
throw new ServiceException("任务不存在!");
}
Instance instance = insService.getById(task.getInstanceId());
if (ObjectUtil.isNull(instance)) {
throw new ServiceException("流程实例不存在");
}
Definition definition = defService.getById(task.getDefinitionId());
if (ObjectUtil.isNull(definition)) {
throw new ServiceException("流程定义不存在");
}
Map<String, Object> mergeVariable = MapUtil.mergeAll(instance.getVariableMap(), variables);
// 获取下一节点列表
List<Node> nextNodeList = nodeService.getNextNodeList(task.getDefinitionId(), task.getNodeCode(), null, SkipType.PASS.getKey(), mergeVariable);
if (CollUtil.isEmpty(nextNodeList)) {
return new ArrayList<>();
}
List<FlowNode> nextFlowNodes = BeanUtil.copyToList(nextNodeList, FlowNode.class);
// 只获取中间节点
nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType()));
@@ -719,13 +753,19 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean taskOperation(TaskOperationBo bo, String taskOperation) {
TaskOperationEnum op = TaskOperationEnum.getByCode(taskOperation);
if (op == null) {
log.error("Invalid operation type:{} ", taskOperation);
throw new ServiceException("Invalid operation type " + taskOperation);
}
FlowParams flowParams = FlowParams.build().message(bo.getMessage());
if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
flowParams.ignore(true);
}
// 根据操作类型构建 FlowParams
switch (taskOperation) {
switch (op) {
case DELEGATE_TASK, TRANSFER_TASK -> {
ValidatorUtils.validate(bo, AddGroup.class);
flowParams.addHandlers(Collections.singletonList(bo.getUserId()));
@@ -738,47 +778,63 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
ValidatorUtils.validate(bo, EditGroup.class);
flowParams.reductionHandlers(bo.getUserIds());
}
default -> {
log.error("Invalid operation type:{} ", taskOperation);
throw new ServiceException("Invalid operation type " + taskOperation);
}
}
Long taskId = bo.getTaskId();
Task task = taskService.getById(taskId);
if (ObjectUtil.isNull(task)) {
throw new ServiceException("任务不存在!");
}
FlowNode flowNode = getByNodeCode(task.getNodeCode(), task.getDefinitionId());
if (ADD_SIGNATURE.equals(taskOperation) || REDUCTION_SIGNATURE.equals(taskOperation)) {
if (ObjectUtil.isNull(flowNode)) {
throw new ServiceException("流程节点不存在");
}
if (op == TaskOperationEnum.ADD_SIGNATURE || op == TaskOperationEnum.REDUCTION_SIGNATURE) {
if (CooperateType.isOrSign(flowNode.getNodeRatio())) {
throw new ServiceException(task.getNodeName() + "不是会签或票签节点!");
}
}
// 设置任务状态并执行对应的任务操作
switch (taskOperation) {
//委派任务
boolean result = false;
switch (op) {
case DELEGATE_TASK -> {
flowParams.hisStatus(TaskStatusEnum.DEPUTE.getStatus());
return taskService.depute(taskId, flowParams);
result = taskService.depute(taskId, flowParams);
}
//转办任务
case TRANSFER_TASK -> {
flowParams.hisStatus(TaskStatusEnum.TRANSFER.getStatus());
return taskService.transfer(taskId, flowParams);
result = taskService.transfer(taskId, flowParams);
}
//加签增加办理人
case ADD_SIGNATURE -> {
flowParams.hisStatus(TaskStatusEnum.SIGN.getStatus());
return taskService.addSignature(taskId, flowParams);
result = taskService.addSignature(taskId, flowParams);
}
//减签减少办理人
case REDUCTION_SIGNATURE -> {
flowParams.hisStatus(TaskStatusEnum.SIGN_OFF.getStatus());
return taskService.reductionSignature(taskId, flowParams);
}
default -> {
log.error("Invalid operation type:{} ", taskOperation);
throw new ServiceException("Invalid operation type " + taskOperation);
result = taskService.reductionSignature(taskId, flowParams);
}
}
// 操作执行成功后再发送消息
if (result && CollUtil.isNotEmpty(bo.getMessageType())) {
List<Long> userIdList = new ArrayList<>();
if (StrUtil.isNotBlank(bo.getUserId())) {
userIdList.add(Convert.toLong(bo.getUserId()));
}
if (CollUtil.isNotEmpty(bo.getUserIds())) {
userIdList.addAll(StreamUtils.toList(bo.getUserIds(), Convert::toLong));
}
if (CollUtil.isNotEmpty(userIdList)) {
flwCommonService.sendMessage(
bo.getMessageType(),
StringUtils.isNotBlank(bo.getMessage()) ? bo.getMessage() : "单据「" + op.getDesc() + "」通知",
"单据「" + op.getDesc() + "」提醒",
userService.selectListByIds(userIdList)
);
}
}
return result;
}
/**
@@ -88,6 +88,7 @@
and b.del_flag = '0'
and c.del_flag = '0'
and a.node_type in ('1','3','4')
and a.flow_status <![CDATA[ <> ]]> 'copy'
) t
${ew.getCustomSqlSegment}
</select>
+6 -6
View File
@@ -65,8 +65,8 @@ services:
network_mode: "host"
minio:
# minio 最后一个未阉割版本 不能再进行升级 在往上的版本功能被阉割
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
# pgsty 开源社区 fork 重新维护的最新版 minio
image: pgsty/minio:RELEASE.2026-02-14T12-00-00Z
container_name: minio
ports:
# api 端口
@@ -99,7 +99,7 @@ services:
network_mode: "host"
ruoyi-server1:
image: ruoyi/ruoyi-server:5.5.3
image: ruoyi/ruoyi-server:5.6.2
container_name: ruoyi-server1
environment:
# 时区上海
@@ -115,7 +115,7 @@ services:
network_mode: "host"
ruoyi-server2:
image: ruoyi/ruoyi-server:5.5.3
image: ruoyi/ruoyi-server:5.6.2
container_name: ruoyi-server2
environment:
# 时区上海
@@ -131,7 +131,7 @@ services:
network_mode: "host"
ruoyi-monitor-admin:
image: ruoyi/ruoyi-monitor-admin:5.5.3
image: ruoyi/ruoyi-monitor-admin:5.6.2
container_name: ruoyi-monitor-admin
environment:
# 时区上海
@@ -143,7 +143,7 @@ services:
network_mode: "host"
ruoyi-snailjob-server:
image: ruoyi/ruoyi-snailjob-server:5.5.3
image: ruoyi/ruoyi-snailjob-server:5.6.2
container_name: ruoyi-snailjob-server
environment:
# 时区上海
+8 -2
View File
@@ -484,6 +484,7 @@ CREATE TABLE sj_job
(
id number GENERATED ALWAYS AS IDENTITY,
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
biz_id varchar2(64) NOT NULL,
group_name varchar2(64) NULL,
job_name varchar2(64) NULL,
args_str clob DEFAULT NULL NULL,
@@ -519,9 +520,11 @@ ALTER TABLE sj_job
CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name);
CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index);
CREATE INDEX idx_sj_job_03 ON sj_job (create_dt);
CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id);
COMMENT ON COLUMN sj_job.id IS '主键';
COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id';
COMMENT ON COLUMN sj_job.biz_id IS '业务ID';
COMMENT ON COLUMN sj_job.group_name IS '组名称';
COMMENT ON COLUMN sj_job.job_name IS '名称';
COMMENT ON COLUMN sj_job.args_str IS '执行方法参数';
@@ -551,7 +554,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间';
COMMENT ON COLUMN sj_job.update_dt IS '修改时间';
COMMENT ON TABLE sj_job IS '任务信息';
INSERT INTO sj_job(namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate);
INSERT INTO sj_job(namespace_id, biz_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'demo-job', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate);
-- sj_job_log_message
CREATE TABLE sj_job_log_message
@@ -738,7 +741,7 @@ CREATE TABLE sj_retry_summary
id number GENERATED ALWAYS AS IDENTITY,
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
group_name varchar2(64) DEFAULT '' NULL,
scene_name varchar2(64) DEFAULT '' NULL,
scene_name varchar2(50) DEFAULT '' NULL,
trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL,
running_num number DEFAULT 0 NOT NULL,
finish_num number DEFAULT 0 NOT NULL,
@@ -774,6 +777,7 @@ CREATE TABLE sj_workflow
id number GENERATED ALWAYS AS IDENTITY,
workflow_name varchar2(64) NULL,
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
biz_id varchar2(64) NOT NULL,
group_name varchar2(64) NULL,
workflow_status smallint DEFAULT 1 NOT NULL,
trigger_type smallint NOT NULL,
@@ -799,10 +803,12 @@ ALTER TABLE sj_workflow
CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt);
CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name);
CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id);
COMMENT ON COLUMN sj_workflow.id IS '主键';
COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称';
COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id';
COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID';
COMMENT ON COLUMN sj_workflow.group_name IS '组名称';
COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启';
COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间';
+24 -4
View File
@@ -473,11 +473,11 @@ INSERT INTO sys_menu VALUES ('11618', '我的任务', '0', '7', 'task', '', '',
INSERT INTO sys_menu VALUES ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', 'workflow:definition:list', 'process-definition', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', 'workflow:instance:list', 'tree-table', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, SYSDATE, NULL, NULL, '');
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 ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', 'workflow:instance:currentList', 'guide', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'processMonitor', '', '', '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, '/workflow/processDefinition');
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, '');
@@ -488,6 +488,26 @@ INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '
INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
-- 流程实例管理相关按钮
INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, SYSDATE, NULL, NULL, '');
-- 流程定义管理相关按钮
INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, SYSDATE, 1, SYSDATE, '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel表达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel表达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
+8 -2
View File
@@ -445,6 +445,7 @@ CREATE TABLE sj_job
(
id bigserial PRIMARY KEY,
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
biz_id varchar(64) NOT NULL,
group_name varchar(64) NOT NULL,
job_name varchar(64) NOT NULL,
args_str text NULL DEFAULT NULL,
@@ -477,9 +478,11 @@ CREATE TABLE sj_job
CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name);
CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index);
CREATE INDEX idx_sj_job_03 ON sj_job (create_dt);
CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id);
COMMENT ON COLUMN sj_job.id IS '主键';
COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id';
COMMENT ON COLUMN sj_job.biz_id IS '业务ID';
COMMENT ON COLUMN sj_job.group_name IS '组名称';
COMMENT ON COLUMN sj_job.job_name IS '名称';
COMMENT ON COLUMN sj_job.args_str IS '执行方法参数';
@@ -509,7 +512,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间';
COMMENT ON COLUMN sj_job.update_dt IS '修改时间';
COMMENT ON TABLE sj_job IS '任务信息';
INSERT INTO sj_job VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now());
INSERT INTO sj_job VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now());
-- sj_job_log_message
CREATE TABLE sj_job_log_message
@@ -684,7 +687,7 @@ CREATE TABLE sj_retry_summary
id bigserial PRIMARY KEY,
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
group_name varchar(64) NOT NULL DEFAULT '',
scene_name varchar(64) NOT NULL DEFAULT '',
scene_name varchar(50) NOT NULL DEFAULT '',
trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
running_num int NOT NULL DEFAULT 0,
finish_num int NOT NULL DEFAULT 0,
@@ -717,6 +720,7 @@ CREATE TABLE sj_workflow
id bigserial PRIMARY KEY,
workflow_name varchar(64) NOT NULL,
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
biz_id varchar(64) NOT NULL,
group_name varchar(64) NOT NULL,
workflow_status smallint NOT NULL DEFAULT 1,
trigger_type smallint NOT NULL,
@@ -739,10 +743,12 @@ CREATE TABLE sj_workflow
CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt);
CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name);
CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id);
COMMENT ON COLUMN sj_workflow.id IS '主键';
COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称';
COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id';
COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID';
COMMENT ON COLUMN sj_workflow.group_name IS '组名称';
COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启';
COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间';
+24 -4
View File
@@ -451,11 +451,11 @@ INSERT INTO sys_menu VALUES ('11618', '我的任务', '0', '7', 'task', '', '',
INSERT INTO sys_menu VALUES ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', 'workflow:definition:list', 'process-definition', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', 'workflow:instance:list', 'tree-table', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, now(), NULL, NULL, '');
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 ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', 'workflow:instance:currentList', 'guide', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'processMonitor', '', '', '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, '/workflow/processDefinition');
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, '');
@@ -466,6 +466,26 @@ INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '
INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, now(), NULL, NULL, '');
-- 流程实例管理相关按钮
INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, now(), NULL, NULL, '');
-- 流程定义管理相关按钮
INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, now(), 1, now(), '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel表达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel表达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, now(), NULL, NULL, '');
+7 -3
View File
@@ -280,6 +280,7 @@ CREATE TABLE `sj_job`
(
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id',
`biz_id` varchar(64) NOT NULL COMMENT '业务ID',
`group_name` varchar(64) NOT NULL COMMENT '组名称',
`job_name` varchar(64) NOT NULL COMMENT '名称',
`args_str` text DEFAULT NULL COMMENT '执行方法参数',
@@ -310,12 +311,13 @@ CREATE TABLE `sj_job`
PRIMARY KEY (`id`),
KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`),
KEY `idx_create_dt` (`create_dt`)
KEY `idx_create_dt` (`create_dt`),
UNIQUE KEY `uk_sj_job_01` (`namespace_id`, `biz_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4 COMMENT ='任务信息';
INSERT INTO `sj_job` VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now());
INSERT INTO `sj_job` VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now());
CREATE TABLE `sj_job_log_message`
(
@@ -443,6 +445,7 @@ CREATE TABLE `sj_workflow`
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`workflow_name` varchar(64) NOT NULL COMMENT '工作流名称',
`namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id',
`biz_id` varchar(64) NOT NULL COMMENT '业务ID',
`group_name` varchar(64) NOT NULL COMMENT '组名称',
`workflow_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流状态 0、关闭、1、开启',
`trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间',
@@ -463,7 +466,8 @@ CREATE TABLE `sj_workflow`
`update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_create_dt` (`create_dt`),
KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
UNIQUE KEY `uk_sj_workflow_01` (`namespace_id`, `biz_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4 COMMENT ='工作流';
+24 -4
View File
@@ -263,12 +263,12 @@ insert into sys_menu values ('11618', '我的任务', '0', '7', 'task', '', '',
insert into sys_menu values ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', 'workflow:definition:list', 'process-definition', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', 'workflow:instance:list', 'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', '2', 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, sysdate(), 1, sysdate(), '流程达式定义菜单');
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 ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', 'workflow:instance:currentList', 'guide', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11630', '流程监控', '11616', '4', 'processMonitor', '', '', '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, '/workflow/processDefinition');
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, '');
@@ -278,6 +278,26 @@ insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '
insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit', '#', 103, 1,sysdate(), null, null, '');
insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove', '#', 103,1, sysdate(), null, null, '');
insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export', '#', 103,1, sysdate(), null, null, '');
-- 流程实例管理相关按钮
insert into sys_menu values ('11653', '流程实例查询', '11621', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11654', '流程变量查询', '11621', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11655', '流程变量修改', '11621', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11657', '流程实例删除', '11621', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11658', '流程实例作废', '11621', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11659', '流程实例撤销', '11621', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, sysdate(), null, null, '');
-- 流程定义管理相关按钮
insert into sys_menu values ('11644', '流程定义查询', '11620', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11645', '流程定义新增', '11620', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11646', '流程定义修改', '11620', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11647', '流程定义删除', '11620', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11648', '流程定义导出', '11620', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11649', '流程定义导入', '11620', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11651', '流程定义复制', '11620', '8', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, sysdate(), null, null, '');
-- 流程表达式管理相关按钮
INSERT INTO sys_menu VALUES ('11802', '流程达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, sysdate(), NULL, NULL, '');
+22 -2
View File
@@ -1428,6 +1428,7 @@ CREATE TABLE sj_job
(
id bigint NOT NULL PRIMARY KEY IDENTITY,
namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
biz_id nvarchar(64) NOT NULL,
group_name nvarchar(64) NOT NULL,
job_name nvarchar(64) NOT NULL,
args_str nvarchar(max) NULL DEFAULT NULL,
@@ -1464,6 +1465,8 @@ CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index)
GO
CREATE INDEX idx_sj_job_03 ON sj_job (create_dt)
GO
CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id)
GO
EXEC sp_addextendedproperty
'MS_Description', N'主键',
@@ -1479,6 +1482,13 @@ EXEC sp_addextendedproperty
'COLUMN', N'namespace_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务ID',
'SCHEMA', N'dbo',
'TABLE', N'sj_job',
'COLUMN', N'biz_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'组名称',
'SCHEMA', N'dbo',
@@ -1674,7 +1684,7 @@ EXEC sp_addextendedproperty
'TABLE', N'sj_job'
GO
INSERT INTO sj_job (namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES (N'dev', N'ruoyi_group', N'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, N'testJobExecutor', 2, N'60', 1, 60, 3, 1, 1, 116, 0, N'', 1, N'', N'', N'', 0, getdate(), getdate())
INSERT INTO sj_job (namespace_id, biz_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES (N'dev', N'demo-job', N'ruoyi_group', N'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, N'testJobExecutor', 2, N'60', 1, 60, 3, 1, 1, 116, 0, N'', 1, N'', N'', N'', 0, getdate(), getdate())
GO
-- sj_job_log_message
@@ -2248,7 +2258,7 @@ CREATE TABLE sj_retry_summary
id bigint NOT NULL PRIMARY KEY IDENTITY,
namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
group_name nvarchar(64) NOT NULL DEFAULT '',
scene_name nvarchar(64) NOT NULL DEFAULT '',
scene_name nvarchar(50) NOT NULL DEFAULT '',
trigger_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP,
running_num int NOT NULL DEFAULT 0,
finish_num int NOT NULL DEFAULT 0,
@@ -2354,6 +2364,7 @@ CREATE TABLE sj_workflow
id bigint NOT NULL PRIMARY KEY IDENTITY,
workflow_name nvarchar(64) NOT NULL,
namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
biz_id nvarchar(64) NOT NULL,
group_name nvarchar(64) NOT NULL,
workflow_status tinyint NOT NULL DEFAULT 1,
trigger_type tinyint NOT NULL,
@@ -2379,6 +2390,8 @@ CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt)
GO
CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name)
GO
CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id)
GO
EXEC sp_addextendedproperty
'MS_Description', N'主键',
@@ -2401,6 +2414,13 @@ EXEC sp_addextendedproperty
'COLUMN', N'namespace_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务ID',
'SCHEMA', N'dbo',
'TABLE', N'sj_workflow',
'COLUMN', N'biz_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'组名称',
'SCHEMA', N'dbo',
+19 -3
View File
@@ -1573,13 +1573,13 @@ INSERT sys_menu VALUES (11633, N'我的抄送', 11618, 4, N'taskCopyList', N'wor
GO
INSERT sys_menu VALUES (11620, N'流程定义', 11616, 3, N'processDefinition', N'workflow/processDefinition/index', N'', 1, 1, N'C', N'0', N'0', N'', N'process-definition', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11621, N'流程实例', 11630, 1, N'processInstance', N'workflow/processInstance/index', N'', 1, 1, N'C', N'0', N'0', N'', N'tree-table', 103, 1, GETDATE(), NULL, NULL, N'');
INSERT sys_menu VALUES (11621, N'流程实例', 11630, 1, N'processInstance', N'workflow/processInstance/index', N'', 1, 1, N'C', N'0', N'0', N'workflow:instance:list', N'tree-table', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11622, N'流程分类', 11616, 1, N'category', N'workflow/category/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:category:list', N'category', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11629, N'我发起的', 11618, 1, N'myDocument', N'workflow/task/myDocument', N'', 1, 1, N'C', N'0', N'0', N'', N'guide', 103, 1, GETDATE(), NULL, NULL, N'');
INSERT sys_menu VALUES (11629, N'我发起的', 11618, 1, N'myDocument', N'workflow/task/myDocument', N'', 1, 1, N'C', N'0', N'0', N'workflow:instance:currentList', N'guide', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, GETDATE(), NULL, NULL, N'');
INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'processMonitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, GETDATE(), NULL, 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
@@ -1600,6 +1600,22 @@ GO
INSERT sys_menu VALUES (11627, N'流程分类导出', 11622, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:export', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
-- 流程实例管理相关按钮
INSERT sys_menu VALUES (11653, N'流程实例查询', 11621, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11654, N'流程变量查询', 11621, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:variableQuery', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11655, N'流程变量修改', 11621, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:variable', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11656, N'流程实例激活/挂起', 11621, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:active', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11657, N'流程实例删除', 11621, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11658, N'流程实例作废', 11621, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:invalid', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11659, N'流程实例撤销', 11621, 7, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:cancel', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11801, N'流程表达式', N'11616', 2, N'spel', N'workflow/spel/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:spel:list', N'input', 103, 1, GETDATE(), 1, GETDATE(), N'流程达式定义菜单');
GO
INSERT sys_menu VALUES (11802, N'流程spel表达式定义查询', N'11801', 1, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
@@ -0,0 +1,24 @@
-- 修正已有菜单权限标识
update sys_menu set perms = 'workflow:definition:list' where menu_id = '11620';
update sys_menu set perms = 'workflow:instance:list' where menu_id = '11621';
update sys_menu set perms = 'workflow:instance:currentList' where menu_id = '11629';
-- 流程定义管理相关按钮
INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
-- 流程实例管理相关按钮
INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, SYSDATE, NULL, NULL, '');
@@ -0,0 +1,24 @@
-- 修正已有菜单权限标识
update sys_menu set perms = 'workflow:definition:list' where menu_id = '11620';
update sys_menu set perms = 'workflow:instance:list' where menu_id = '11621';
update sys_menu set perms = 'workflow:instance:currentList' where menu_id = '11629';
-- 流程定义管理相关按钮
INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, now(), NULL, NULL, '');
-- 流程实例管理相关按钮
INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, now(), NULL, NULL, '');
@@ -0,0 +1,43 @@
-- 修正已有菜单权限标识
update sys_menu set perms = N'workflow:definition:list' where menu_id = 11620;
GO
update sys_menu set perms = N'workflow:instance:list' where menu_id = 11621;
GO
update sys_menu set perms = N'workflow:instance:currentList' where menu_id = 11629;
GO
-- 流程定义管理相关按钮
INSERT sys_menu VALUES (11644, N'流程定义查询', N'11620', 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11645, N'流程定义新增', N'11620', 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:add', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11646, N'流程定义修改', N'11620', 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11647, N'流程定义删除', N'11620', 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11648, N'流程定义导出', N'11620', 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:export', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11649, N'流程定义导入', N'11620', 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:import', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11650, N'流程定义发布/取消发布', N'11620', 7, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:publish', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11651, N'流程定义复制', N'11620', 8, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:copy', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11652, N'流程定义激活/挂起', N'11620', 9, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:definition:active', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
-- 流程实例管理相关按钮
INSERT sys_menu VALUES (11653, N'流程实例查询', N'11621', 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11654, N'流程变量查询', N'11621', 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:variableQuery', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11655, N'流程变量修改', N'11621', 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:variable', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11656, N'流程实例激活/挂起', N'11621', 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:active', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11657, N'流程实例删除', N'11621', 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11658, N'流程实例作废', N'11621', 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:invalid', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11659, N'流程实例撤销', N'11621', 7, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:cancel', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO
+24
View File
@@ -0,0 +1,24 @@
-- 修正已有菜单权限标识
update sys_menu set perms = 'workflow:definition:list' where menu_id = '11620';
update sys_menu set perms = 'workflow:instance:list' where menu_id = '11621';
update sys_menu set perms = 'workflow:instance:currentList' where menu_id = '11629';
-- 流程定义管理相关按钮
INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, sysdate(), null, null, '');
-- 流程实例管理相关按钮
INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, sysdate(), null, null, '');
INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, sysdate(), null, null, '');