88 Commits

Author SHA1 Message Date
b30ffa952f !766 发布 5.5.0 喜迎国庆
Merge pull request !766 from 疯狂的狮子Li/dev
2025-09-22 03:17:27 +00:00
f616c6931c 发布 5.5.0 喜迎国庆🧨🧨🧨 2025-09-22 11:13:32 +08:00
26e10293f5 update snailjob 1.7.2 => 1.8.0 2025-09-22 11:11:06 +08:00
60e578f763 Revert "update 更新工作流sql"
This reverts commit 8909b8a7d4.
2025-09-22 11:08:23 +08:00
5cd4d8ca11 Revert "update warm-flow 升级 1.8.2-m2"
This reverts commit 8ae9bde731.
2025-09-22 11:08:18 +08:00
41a6230b6e update 优化 去除不应该加压缩的日志文件 2025-09-22 11:05:13 +08:00
Lau
effda4f6e8 !765 update 历史日志文件增加压缩
* update 历史日志文件增加压缩
2025-09-22 03:00:08 +00:00
af4c38e439 update 优化 更新ip2region.xdb文件 2025-09-19 17:44:32 +08:00
fafa8cd573 update springboot 3.5.4 => 3.5.6 2025-09-19 14:12:47 +08:00
8909b8a7d4 update 更新工作流sql 2025-09-18 18:17:33 +08:00
8ae9bde731 update warm-flow 升级 1.8.2-m2 2025-09-16 21:21:08 +08:00
a703cb2ad1 update 增加 加密头用来判断数据是否已经被加密了 防止重复加密 2025-09-15 17:14:47 +08:00
a918b880d6 update 增加 加密头用来判断数据是否已经被加密了 防止重复加密 2025-09-15 17:10:31 +08:00
e795e315eb update 生成模板前端增加fixed 2025-09-15 15:46:01 +08:00
Lau
81869cfeb3 !764 update 生成模板前端增加fixed
* update 生成模板前端增加fixed
2025-09-15 07:39:34 +00:00
fc6f61bc95 update springboot-admin 3.5.1 => 3.5.3
update springdoc 2.8.11 => 2.8.13
update mybatis-plus 3.5.12 => 3.5.14
update mapstruct-plus 1.4.8 => 1.5.0
update sms4j 3.3.4 => 3.3.5
2025-09-15 11:52:05 +08:00
d44e45ad3b update 添加节点悬浮提示配置开关 2025-09-04 17:30:15 +08:00
00ed9ddd10 update 优化 SysMenu 的 selectObjs 查询 2025-09-04 16:04:36 +08:00
341fc144a1 fix 修复 自定义sql在pg数据库类型异常问题 2025-09-04 15:42:30 +08:00
b6b1b2de18 update 优化全局日期格式转换逻辑 2025-09-04 15:30:09 +08:00
c19f2b9e4e update 优化 岗位页面查询权限问题 2025-09-03 14:14:35 +08:00
3a11f18656 fix 修复 StreamUtils 返回不可变类型报错问题 2025-09-02 15:58:11 +08:00
5a43212ccc fix 修复 StreamUtils 返回不可变类型报错问题 2025-09-02 15:51:42 +08:00
f4cfd1c913 !759 [fix] 解决工作流通知messageType参数判空逻辑错误的问题
* [fix] 解决工作流通知messageType参数判空逻辑错误的问题
2025-09-02 04:57:56 +00:00
26ce8f30c9 update 优化 支持子菜单配置默认激活的父菜单activeMenu 2025-09-02 10:45:54 +08:00
2258962770 Revert "!734 update 重写selectOne方法"
This reverts commit f2e0361fb6.
2025-09-01 14:25:47 +08:00
655e84012c Revert "update 优化 增加selectOne使用注意事项"
This reverts commit bf10a13088.
2025-09-01 14:25:40 +08:00
f683ef00b8 fix 修复 json模块配置 默认覆盖了spring module 配置问题 改为让spring自动加载注册 2025-09-01 11:46:57 +08:00
424b2ea164 update Excel写出包装器添加泛型用于限定write入参类型 2025-08-31 13:24:12 +08:00
7bb4838132 feat add Excel工具类支持更灵活的自定义导出方式,以便用户分批处理导出数据 2025-08-30 18:09:01 +08:00
20516758ea upadte 优化Stream流工具类 2025-08-30 16:53:13 +08:00
2d5f84ebc2 upadte 优化Stream流工具类 2025-08-30 16:46:04 +08:00
6bc28e41de update 优化 工作流代码 2025-08-29 10:06:22 +08:00
a4fb3fadaf update 优化 将返回值从bo改为vo 2025-08-29 09:52:21 +08:00
cfa67fcd8c Revert "update 添加 FlowCopyVo 类,优化抄送对象处理逻辑"
This reverts commit e5e8d305d2.
2025-08-29 01:35:27 +00:00
e5e8d305d2 update 添加 FlowCopyVo 类,优化抄送对象处理逻辑 2025-08-29 09:29:12 +08:00
9d0084409e update 优化 支持后端监听器解析节点扩展数据到流程变量(按钮权限 抄送人 扩展变量) 2025-08-28 17:56:10 +08:00
ee02f46dfd update 优化 支持前端返回节点扩展数据(按钮权限 抄送人 扩展变量) 2025-08-28 17:55:04 +08:00
25de0b3530 update 解析扩展属性 JSON,构建 Node 扩展属性对象,增强代码可读性 2025-08-28 16:25:33 +08:00
aa76859a05 update 添加抄送设置和变量枚举,优化扩展节点配置逻辑 2025-08-28 15:00:32 +08:00
71b70a59fe fix 修复 判断错误导致新增报错问题 2025-08-28 10:59:00 +08:00
05c9528549 !752 update 优化流程实例业务扩展的保存和删除逻辑,增强代码可读性
* update 优化流程实例业务扩展的保存和删除逻辑,增强代码可读性
2025-08-27 11:10:03 +00:00
1feb2a3861 fix 修复 菜单与部门 未做角色状态判断 2025-08-27 17:54:05 +08:00
237e78e80c update hutool 5.8.38 => 5.8.40 默认支持了验证码不生成负数 2025-08-27 11:58:07 +08:00
ffc3dcaec9 upadte 优化Excel单元格合并处理器代码逻辑分支 2025-08-26 17:13:35 +08:00
a94e474069 fix 修复 时间解析类异常问题 2025-08-26 16:10:17 +08:00
40a0e57870 fix 修复 时间解析类异常问题 2025-08-26 16:02:11 +08:00
c01ed34602 update fastexcel 1.2.0 => 1.3.0 2025-08-25 13:50:09 +08:00
26a99003d2 update springdoc 2.8.10 => 2.8.11
update redisson 3.50.0 => 3.51.0
2025-08-25 09:58:46 +08:00
93c886d3ed update 优化 对三方授权 redirectUri 回调地址进行url编码 2025-08-25 09:58:06 +08:00
9e1027690b update 更新 warm-flow 版本至 1.8.1 2025-08-22 10:23:37 +08:00
cc120c06fd !746 update 优化代码生成模板空格对齐
* update 优化代码生成模板空格对齐
2025-08-22 02:16:51 +00:00
3827da078a update 移除不必要的流程状态颜色配置 2025-08-22 09:54:40 +08:00
70d3505b94 update springdoc 2.8.9 => 2.8.10 2025-08-21 10:07:15 +08:00
a39a69cac5 reset 回滚错误提交 2025-08-21 09:25:56 +08:00
1dbce3ab7c fix 修复 校验租户账号余额 查询语句错误 2025-08-21 09:20:35 +08:00
9742b1b596 fix 修复 流程重新提交报错问题 2025-08-19 17:59:29 +08:00
d98d11ae2d fix 修复 流程重新提交报错问题 2025-08-19 17:48:30 +08:00
6742dcb33e fix 修复 sql书写错误 2025-08-19 17:18:37 +08:00
09a51478a5 add 新增 请假表 申请编号字段sql 2025-08-18 09:44:22 +08:00
f02601ab2c update 优化 表sql书写格式 2025-08-16 11:31:45 +08:00
ac56ca0e81 add 补充流程扩展sql 2025-08-15 21:35:42 +08:00
0fcf77e2ed add 增加流程业务扩展 2025-08-15 21:18:25 +08:00
0f0a3a181e !743 update 注册功能同步优化 验证码校验逻辑
* update 注册功能同步优化 验证码校验逻辑
2025-08-15 08:29:49 +00:00
e24e2c51e4 update 优化 验证码校验逻辑 2025-08-15 14:04:17 +08:00
fd5d028e95 fix 修复 有某些无聊人士 对一个demo案例提漏洞 CVE-2025-6925
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2025-07-04 01:10:49 +00:00
64100cf1ff !712 发布 5.4.1 小步迭代修复问题
Merge pull request !712 from 疯狂的狮子Li/dev
2025-07-01 01:12:39 +00:00
7e7d857ba5 Merge remote-tracking branch 'origin/dev' into 5.X 2025-05-29 18:18:20 +08:00
d22b2a10df update 优化 PermissionService 无实现类也可以启动服务 2025-05-29 16:28:56 +08:00
957a4d1fcd fix 修复 监听器 flowParams 为null报错问题 2025-05-29 16:28:56 +08:00
49ef8378fe !691 发布 5.4.0 正式版
Merge pull request !691 from 疯狂的狮子Li/dev
2025-05-29 03:14:59 +00:00
57dd6831d3 !664 发布 5.3.1 正式版
Merge pull request !664 from 疯狂的狮子Li/dev
2025-03-27 02:54:00 +00:00
8aa60abb1f !663 回退 'Pull Request !662 : 发布 5.3.1 正式版'
* 回退 'Pull Request !662 : 发布 5.3.1 正式版'
2025-03-27 02:53:23 +00:00
7a9f51fc7a !662 发布 5.3.1 正式版
* 🐳发布 5.3.1 正式版
* update 优化 删除无用配置
* fix 修复 excel模板导出数据被覆盖的问题
* update 优化 统一用户密码校验长度
* update mybatis-plus 3.5.10.1 => 3.5.11
* fix 修复 跨域未设置请求头问题(cloud版本不需要 vue版本需要)
2025-03-27 02:51:57 +00:00
159e30c982 !661 发布 5.3.1-BETA2 公测版本
Merge pull request !661 from 疯狂的狮子Li/dev
2025-03-21 07:25:25 +00:00
7334d91d6b !652 发布 5.3.1-BETA 公测版本
Merge pull request !652 from 疯狂的狮子Li/dev
2025-03-13 05:27:36 +00:00
95c01301f6 !644 同步修复一些问题
Merge pull request !644 from 疯狂的狮子Li/dev
2025-02-07 06:19:28 +00:00
296466fa13 !640 发布 5.3.0 新春版 祝大家新年快乐
Merge pull request !640 from 疯狂的狮子Li/dev
2025-01-24 05:08:28 +00:00
3c8d864b5f !639 发布 5.3.0-BETA 公测版本
Merge pull request !639 from 疯狂的狮子Li/dev
2025-01-20 03:35:45 +00:00
ea50a57602 update 优化 xss包装器 Parameter 处理 兼容某些容器不允许改参数的情况 2024-11-21 10:17:34 +08:00
7e14b98676 reset 回滚错误修改
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2024-10-28 09:46:28 +00:00
015b406001 !591 发布 5.2.3 正式版
Merge pull request !591 from 疯狂的狮子Li/dev
2024-10-25 03:09:23 +00:00
098d3347a0 !577 发布 5.2.2 正式版 安全性提升
Merge pull request !577 from 疯狂的狮子Li/dev
2024-08-26 03:43:59 +00:00
08d4493994 update 优化 bug 模板 2024-07-15 15:19:22 +08:00
367d739e2d Merge remote-tracking branch 'origin/5.X' into 5.X 2024-07-09 16:38:43 +08:00
d6688a367d !562 ♥️发布 5.2.1 正式版本
Merge pull request !562 from 疯狂的狮子Li/dev
2024-07-09 02:42:40 +00:00
0b331796e2 !551 ♥️发布 5.2.0 正式版本
Merge pull request !551 from 疯狂的狮子Li/dev
2024-06-20 02:10:15 +00:00
456620b638 !549 ♥️发布 5.2.0-BETA2 公测版本
Merge pull request !549 from 疯狂的狮子Li/dev
2024-06-06 03:13:46 +00:00
81 changed files with 1764 additions and 500 deletions

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.4.1" />
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.5.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings>

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.4.1" />
<option name="imageTag" value="ruoyi/ruoyi-server:5.5.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings>

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.4.1" />
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.5.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
</settings>

View File

@ -10,7 +10,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.4.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()

26
pom.xml
View File

@ -13,28 +13,28 @@
<description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
<properties>
<revision>5.4.1</revision>
<spring-boot.version>3.5.4</spring-boot.version>
<revision>5.5.0</revision>
<spring-boot.version>3.5.6</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.16</mybatis.version>
<springdoc.version>2.8.9</springdoc.version>
<springdoc.version>2.8.13</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fastexcel.version>1.2.0</fastexcel.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version>
<satoken.version>1.44.0</satoken.version>
<mybatis-plus.version>3.5.12</mybatis-plus.version>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.38</hutool.version>
<spring-boot-admin.version>3.5.1</spring-boot-admin.version>
<redisson.version>3.50.0</redisson.version>
<hutool.version>5.8.40</hutool.version>
<spring-boot-admin.version>3.5.3</spring-boot-admin.version>
<redisson.version>3.51.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.7.2</snailjob.version>
<mapstruct-plus.version>1.4.8</mapstruct-plus.version>
<snailjob.version>1.8.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.38</lombok.version>
<lombok.version>1.18.40</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
@ -42,13 +42,13 @@
<!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 -->
<sms4j.version>3.3.4</sms4j.version>
<sms4j.version>3.3.5</sms4j.version>
<!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250603</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.8.0</warm-flow.version>
<warm-flow.version>1.8.1</warm-flow.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>

View File

@ -131,15 +131,18 @@ public class CaptchaController {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码
CaptchaType captchaType = captchaProperties.getType();
boolean isMath = CaptchaType.MATH == captchaType;
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
CodeGenerator codeGenerator;
if (CaptchaType.MATH == captchaType) {
codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getNumberLength(), false);
} else {
codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getCharLength());
}
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
// 如果是数学验证码使用SpEL表达式处理验证码结果
String code = captcha.getCode();
if (isMath) {
if (CaptchaType.MATH == captchaType) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);

View File

@ -87,7 +87,7 @@ public class SysRegisterService {
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
if (!StringUtils.equalsIgnoreCase(code, captcha)) {
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
}

View File

@ -102,7 +102,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
if (!StringUtils.equalsIgnoreCase(code, captcha)) {
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
}

View File

@ -259,13 +259,7 @@ warm-flow:
ui: true
# 是否显示流程图顶部文字
top-text-show: true
# 是否渲染节点悬浮提示默认true
node-tooltip: true
# 默认Authorization如果有多个token用逗号分隔
token-name: ${sa-token.token-name},clientid
# 流程状态对应的三元色
chart-status-color:
## 未办理
- 62,62,62
## 待办理
- 255,205,23
## 已办理
- 157,255,0

View File

@ -38,7 +38,7 @@
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
@ -60,7 +60,7 @@
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>

View File

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

View File

@ -72,5 +72,10 @@ public interface Constants {
*/
Long TOP_PARENT_ID = 0L;
/**
* 加密头
*/
String ENCRYPT_HEADER = "ENC_";
}

View File

@ -30,8 +30,10 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
return collection.stream().filter(function).collect(Collectors.toList());
return collection.stream()
.filter(function)
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
.collect(Collectors.toList());
}
/**
@ -39,13 +41,26 @@ public class StreamUtils {
*
* @param collection 需要查询的集合
* @param function 过滤方法
* @return 找到符合条件的第一个元素,没有则返回null
* @return 找到符合条件的第一个元素,没有则返回 Optional.empty()
*/
public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
public static <E> Optional<E> findFirst(Collection<E> collection, Predicate<E> function) {
if (CollUtil.isEmpty(collection)) {
return null;
return Optional.empty();
}
return collection.stream().filter(function).findFirst().orElse(null);
return collection.stream()
.filter(function)
.findFirst();
}
/**
* 找到流中满足条件的第一个元素值
*
* @param collection 需要查询的集合
* @param function 过滤方法
* @return 找到符合条件的第一个元素,没有则返回 null
*/
public static <E> E findFirstValue(Collection<E> collection, Predicate<E> function) {
return findFirst(collection,function).orElse(null);
}
/**
@ -53,13 +68,26 @@ public class StreamUtils {
*
* @param collection 需要查询的集合
* @param function 过滤方法
* @return 找到符合条件的任意一个元素,没有则返回null
* @return 找到符合条件的任意一个元素,没有则返回 Optional.empty()
*/
public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
if (CollUtil.isEmpty(collection)) {
return Optional.empty();
}
return collection.stream().filter(function).findAny();
return collection.stream()
.filter(function)
.findAny();
}
/**
* 找到流中任意一个满足条件的元素值
*
* @param collection 需要查询的集合
* @param function 过滤方法
* @return 找到符合条件的任意一个元素没有则返回null
*/
public static <E> E findAnyValue(Collection<E> collection, Predicate<E> function) {
return findAny(collection,function).orElse(null);
}
/**
@ -85,7 +113,10 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return StringUtils.EMPTY;
}
return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
return collection.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.joining(delimiter));
}
/**
@ -99,8 +130,11 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
return collection.stream()
.filter(Objects::nonNull)
.sorted(comparing)
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
.collect(Collectors.toList());
}
/**
@ -117,7 +151,9 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
return collection.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
}
/**
@ -136,7 +172,25 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l));
return collection.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(key, value, (l, r) -> l));
}
/**
* 获取 map 中的数据作为新 Map 的 value key 不变
* @param map 需要处理的map
* @param take 取值函数
* @param <K> map中的key类型
* @param <E> map中的value类型
* @param <V> 新map中的value类型
* @return 新的map
*/
public static <K, E, V> Map<K, V> toMap(Map<K, E> map, BiFunction<K, E, V> take) {
if (CollUtil.isEmpty(map)) {
return MapUtil.newHashMap();
}
return toMap(map.entrySet(), Map.Entry::getKey, entry -> take.apply(entry.getKey(), entry.getValue()));
}
/**
@ -153,8 +207,8 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream().filter(Objects::nonNull)
return collection.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
}
@ -174,8 +228,8 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream().filter(Objects::nonNull)
return collection.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
}
@ -192,11 +246,11 @@ public class StreamUtils {
* @return 分类后的map
*/
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream().filter(Objects::nonNull)
return collection.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
}
@ -214,8 +268,7 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
return collection
.stream()
return collection.stream()
.map(function)
.filter(Objects::nonNull)
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
@ -233,11 +286,10 @@ public class StreamUtils {
* @return 转化后的Set
*/
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection) || function == null) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newHashSet();
}
return collection
.stream()
return collection.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
@ -257,26 +309,20 @@ public class StreamUtils {
* @return 合并后的map
*/
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
if (CollUtil.isEmpty(map1) && CollUtil.isEmpty(map2)) {
// 如果两个 map 都为空,则直接返回空的 map
return MapUtil.newHashMap();
} else if (MapUtil.isEmpty(map1)) {
map1 = MapUtil.newHashMap();
} else if (MapUtil.isEmpty(map2)) {
map2 = MapUtil.newHashMap();
} else if (CollUtil.isEmpty(map1)) {
// 如果 map1 为空,则直接处理返回 map2
return toMap(map2.entrySet(), Map.Entry::getKey, entry -> merge.apply(null, entry.getValue()));
} else if (CollUtil.isEmpty(map2)) {
// 如果 map2 为空,则直接处理返回 map1
return toMap(map1.entrySet(), Map.Entry::getKey, entry -> merge.apply(entry.getValue(), null));
}
Set<K> key = new HashSet<>();
key.addAll(map1.keySet());
key.addAll(map2.keySet());
Map<K, V> map = new HashMap<>();
for (K t : key) {
X x = map1.get(t);
Y y = map2.get(t);
V z = merge.apply(x, y);
if (z != null) {
map.put(t, z);
}
}
return map;
Set<K> keySet = new HashSet<>();
keySet.addAll(map1.keySet());
keySet.addAll(map2.keySet());
return toMap(keySet, key -> key, key -> merge.apply(map1.get(key), map2.get(key)));
}
}

View File

@ -5,6 +5,7 @@ import cn.hutool.core.util.ReflectUtil;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
@ -92,8 +93,12 @@ public class EncryptorManager {
* @param encryptContext 加密相关的配置信息
*/
public String encrypt(String value, EncryptContext encryptContext) {
if (StringUtils.startsWith(value, Constants.ENCRYPT_HEADER)) {
return value;
}
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.encrypt(value, encryptContext.getEncode());
String encrypt = encryptor.encrypt(value, encryptContext.getEncode());
return Constants.ENCRYPT_HEADER + encrypt;
}
/**
@ -103,8 +108,12 @@ public class EncryptorManager {
* @param encryptContext 加密相关的配置信息
*/
public String decrypt(String value, EncryptContext encryptContext) {
if (!StringUtils.startsWith(value, Constants.ENCRYPT_HEADER)) {
return value;
}
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.decrypt(value);
String str = StringUtils.removeStart(value, Constants.ENCRYPT_HEADER);
return encryptor.decrypt(str);
}
/**

View File

@ -31,12 +31,89 @@ public class CellMergeHandler {
}
@SneakyThrows
public List<CellRangeAddress> handle(List<?> list) {
List<CellRangeAddress> cellList = new ArrayList<>();
if (CollUtil.isEmpty(list)) {
return cellList;
public List<CellRangeAddress> handle(List<?> rows) {
// 如果入参为空集合则返回空集
if (CollUtil.isEmpty(rows)) {
return Collections.emptyList();
}
Class<?> clazz = list.get(0).getClass();
// 获取有合并注解的字段
Map<Field, FieldColumnIndex> mergeFields = getFieldColumnIndexMap(rows.get(0).getClass());
// 如果没有需要合并的字段则返回空集
if (CollUtil.isEmpty(mergeFields)) {
return Collections.emptyList();
}
// 结果集
List<CellRangeAddress> result = new ArrayList<>();
// 生成两两合并单元格
Map<Field, RepeatCell> rowRepeatCellMap = new HashMap<>();
for (Map.Entry<Field, FieldColumnIndex> item : mergeFields.entrySet()) {
Field field = item.getKey();
FieldColumnIndex itemValue = item.getValue();
int colNum = itemValue.colIndex();
CellMerge cellMerge = itemValue.cellMerge();
for (int i = 0; i < rows.size(); i++) {
// 当前行数据
Object currentRowObj = rows.get(i);
// 当前行数据字段值
Object currentRowObjFieldVal = ReflectUtils.invokeGetter(currentRowObj, field.getName());
// 空值跳过不处理
if (currentRowObjFieldVal == null || "".equals(currentRowObjFieldVal)) {
continue;
}
// 单元格合并Map是否存在数据如果不存在则添加当前行的字段值
if (!rowRepeatCellMap.containsKey(field)) {
rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i));
continue;
}
// 获取 单元格合并Map 中字段值
RepeatCell repeatCell = rowRepeatCellMap.get(field);
Object cellValue = repeatCell.value();
int current = repeatCell.current();
// 检查是否满足合并条件
// currentRowObj 当前行数据
// rows.get(i - 1) 上一行数据 注:由于 if (!rowRepeatCellMap.containsKey(field)) 条件的存在,所以该 i 必不可能小于1
// cellMerge 当前行字段合并注解
boolean merge = isMerge(currentRowObj, rows.get(i - 1), cellMerge);
// 是否添加到结果集
boolean isAddResult = false;
// 最新行
int lastRow = i + rowIndex - 1;
// 如果当前行字段值和缓存中的字段值不相等,或不满足合并条件,则替换
if (!currentRowObjFieldVal.equals(cellValue) || !merge) {
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) {
result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
}
}
}
return result;
}
/**
* 获取带有合并注解的字段列索引和合并注解信息Map集
*/
private Map<Field, FieldColumnIndex> getFieldColumnIndexMap(Class<?> clazz) {
boolean annotationPresent = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class);
Field[] fields = ReflectUtils.getFields(clazz, field -> {
if ("serialVersionUID".equals(field.getName())) {
@ -49,86 +126,57 @@ public class CellMergeHandler {
});
// 有注解的字段
List<Field> mergeFields = new ArrayList<>();
List<Integer> mergeFieldsIndex = new ArrayList<>();
Map<Field, FieldColumnIndex> mergeFields = new HashMap<>();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(CellMerge.class)) {
CellMerge cm = field.getAnnotation(CellMerge.class);
mergeFields.add(field);
mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
if (hasTitle) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
rowIndex = Math.max(rowIndex, property.value().length);
}
if (!field.isAnnotationPresent(CellMerge.class)) {
continue;
}
CellMerge cm = field.getAnnotation(CellMerge.class);
int index = cm.index() == -1 ? i : cm.index();
mergeFields.put(field, FieldColumnIndex.of(index, cm));
if (hasTitle) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
rowIndex = Math.max(rowIndex, property.value().length);
}
}
Map<Field, RepeatCell> map = new HashMap<>();
// 生成两两合并单元格
for (int i = 0; i < list.size(); i++) {
Object rowObj = list.get(i);
for (int j = 0; j < mergeFields.size(); j++) {
Field field = mergeFields.get(j);
Object val = ReflectUtils.invokeGetter(rowObj, field.getName());
int colNum = mergeFieldsIndex.get(j);
if (!map.containsKey(field)) {
map.put(field, new RepeatCell(val, i));
} else {
RepeatCell repeatCell = map.get(field);
Object cellValue = repeatCell.value();
if (cellValue == null || "".equals(cellValue)) {
// 空值跳过不合并
continue;
}
if (!cellValue.equals(val)) {
if ((i - repeatCell.current() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
} else if (i == list.size() - 1) {
if (!isMerge(list, i, field)) {
// 如果最后一行不能合并,检查之前的数据是否需要合并
if (i - repeatCell.current() > 1) {
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
} else if (i > repeatCell.current()) {
// 如果最后一行可以合并,则直接合并到最后
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex, colNum, colNum));
}
} else if (!isMerge(list, i, field)) {
if ((i - repeatCell.current() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
}
}
}
}
return cellList;
return mergeFields;
}
private boolean isMerge(List<?> list, int i, Field field) {
boolean isMerge = true;
CellMerge cm = field.getAnnotation(CellMerge.class);
final String[] mergeBy = cm.mergeBy();
private boolean isMerge(Object currentRow, Object preRow, CellMerge cellMerge) {
final String[] mergeBy = cellMerge.mergeBy();
if (StrUtil.isAllNotBlank(mergeBy)) {
//比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真
//比对当前行和上一行的各个属性值一一比对 如果全为真 则为真
for (String fieldName : mergeBy) {
final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName);
final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName);
final Object valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName);
final Object valPre = ReflectUtil.getFieldValue(preRow, fieldName);
if (!Objects.equals(valPre, valCurrent)) {
//依赖字段如有任一不等值,则标记为不可合并
isMerge = false;
return false;
}
}
}
return isMerge;
return true;
}
record RepeatCell(Object value, int current) {}
/**
* 单元格合并
*/
record RepeatCell(Object value, int current) {
static RepeatCell of(Object value, int current) {
return new RepeatCell(value, current);
}
}
/**
* 字段列索引和合并注解信息
*/
record FieldColumnIndex(int colIndex, CellMerge cellMerge) {
static FieldColumnIndex of(int colIndex, CellMerge cellMerge) {
return new FieldColumnIndex(colIndex, cellMerge);
}
}
/**
* 创建一个单元格合并处理器实例

View File

@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* Excel相关处理
@ -203,6 +204,44 @@ public class ExcelUtil {
builder.doWrite(list);
}
/**
* 导出excel
*
* @param headType 带Excel注解的类型
* @param os 输出流
* @param options Excel下拉可选项
* @param consumer 导出助手消费函数
*/
public static <T> void exportExcel(Class<T> headType, OutputStream os, List<DropDownOptions> options, Consumer<ExcelWriterWrapper<T>> consumer) {
try (ExcelWriter writer = FastExcel.write(os, headType)
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
// 批注必填项处理
.registerWriteHandler(new DataWriteHandler(headType))
// 添加下拉框操作
.registerWriteHandler(new ExcelDownHandler(options))
.build()) {
// 执行消费函数
consumer.accept(ExcelWriterWrapper.of(writer));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 导出excel
*
* @param headType 带Excel注解的类型
* @param os 输出流
* @param consumer 导出助手消费函数
*/
public static <T> void exportExcel(Class<T> headType, OutputStream os, Consumer<ExcelWriterWrapper<T>> consumer) {
exportExcel(headType, os, null, consumer);
}
/**
* 单表多数据模板导出 模板格式为 {.属性}
*

View File

@ -0,0 +1,127 @@
package org.dromara.common.excel.utils;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcel;
import cn.idev.excel.context.WriteContext;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import cn.idev.excel.write.builder.ExcelWriterTableBuilder;
import cn.idev.excel.write.metadata.WriteSheet;
import cn.idev.excel.write.metadata.WriteTable;
import cn.idev.excel.write.metadata.fill.FillConfig;
import java.util.Collection;
import java.util.function.Supplier;
/**
* ExcelWriterWrapper Excel写出包装器
* <br>
* 提供了一组与 ExcelWriter 一一对应的写出方法,避免直接提供 ExcelWriter 而导致的一些不可控问题比如提前关闭了IO流等
*
* @author 秋辞未寒
* @see ExcelWriter
*/
public record ExcelWriterWrapper<T>(ExcelWriter excelWriter) {
public void write(Collection<T> data, WriteSheet writeSheet) {
excelWriter.write(data, writeSheet);
}
public void write(Supplier<Collection<T>> supplier, WriteSheet writeSheet) {
excelWriter.write(supplier.get(), writeSheet);
}
public void write(Collection<T> data, WriteSheet writeSheet, WriteTable writeTable) {
excelWriter.write(data, writeSheet, writeTable);
}
public void write(Supplier<Collection<T>> supplier, WriteSheet writeSheet, WriteTable writeTable) {
excelWriter.write(supplier.get(), writeSheet, writeTable);
}
public void fill(Object data, WriteSheet writeSheet) {
excelWriter.fill(data, writeSheet);
}
public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) {
excelWriter.fill(data, fillConfig, writeSheet);
}
public void fill(Supplier<Object> supplier, WriteSheet writeSheet) {
excelWriter.fill(supplier, writeSheet);
}
public void fill(Supplier<Object> supplier, FillConfig fillConfig, WriteSheet writeSheet) {
excelWriter.fill(supplier, fillConfig, writeSheet);
}
public WriteContext writeContext() {
return excelWriter.writeContext();
}
/**
* 创建一个 ExcelWriterWrapper
*
* @param excelWriter ExcelWriter
* @return ExcelWriterWrapper
*/
public static <T> ExcelWriterWrapper<T> of(ExcelWriter excelWriter) {
return new ExcelWriterWrapper<>(excelWriter);
}
// -------------------------------- sheet start
public static WriteSheet buildSheet(Integer sheetNo, String sheetName) {
return sheetBuilder(sheetNo, sheetName).build();
}
public static WriteSheet buildSheet(Integer sheetNo) {
return sheetBuilder(sheetNo).build();
}
public static WriteSheet buildSheet(String sheetName) {
return sheetBuilder(sheetName).build();
}
public static WriteSheet buildSheet() {
return sheetBuilder().build();
}
public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo, String sheetName) {
return FastExcel.writerSheet(sheetNo, sheetName);
}
public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo) {
return FastExcel.writerSheet(sheetNo);
}
public static ExcelWriterSheetBuilder sheetBuilder(String sheetName) {
return FastExcel.writerSheet(sheetName);
}
public static ExcelWriterSheetBuilder sheetBuilder() {
return FastExcel.writerSheet();
}
// -------------------------------- sheet end
// -------------------------------- table start
public static WriteTable buildTable(Integer tableNo) {
return tableBuilder(tableNo).build();
}
public static WriteTable buildTable() {
return tableBuilder().build();
}
public static ExcelWriterTableBuilder tableBuilder(Integer tableNo) {
return FastExcel.writerTable(tableNo);
}
public static ExcelWriterTableBuilder tableBuilder() {
return FastExcel.writerTable();
}
// -------------------------------- table end
}

View File

@ -1,5 +1,6 @@
package org.dromara.common.json.config;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
@ -28,20 +29,24 @@ import java.util.TimeZone;
@AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig {
@Bean
public Module registerJavaTimeModule() {
// 全局配置序列化返回 JSON 处理
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer());
return javaTimeModule;
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
// 全局配置序列化返回 JSON 处理
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer());
builder.modules(javaTimeModule);
builder.timeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置");
};

View File

@ -1,9 +1,11 @@
package org.dromara.common.json.handler;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.dromara.common.core.utils.ObjectUtils;
import java.io.IOException;
import java.util.Date;
@ -25,7 +27,11 @@ public class CustomDateDeserializer extends JsonDeserializer<Date> {
*/
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return DateUtil.parse(p.getText());
DateTime parse = DateUtil.parse(p.getText());
if (ObjectUtils.isNull(parse)) {
return null;
}
return parse.toJdkDate();
}
}

View File

@ -6,11 +6,9 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.dromara.common.core.utils.MapstructUtils;
@ -132,7 +130,7 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的单个VO对象
*/
default V selectVoById(Serializable id) {
return this.selectVoById(id, this.currentVoClass());
return selectVoById(id, this.currentVoClass());
}
/**
@ -158,7 +156,7 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的VO对象列表
*/
default List<V> selectVoByIds(Collection<? extends Serializable> idList) {
return this.selectVoByIds(idList, this.currentVoClass());
return selectVoByIds(idList, this.currentVoClass());
}
/**
@ -184,7 +182,7 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的VO对象列表
*/
default List<V> selectVoByMap(Map<String, Object> map) {
return this.selectVoByMap(map, this.currentVoClass());
return selectVoByMap(map, this.currentVoClass());
}
/**
@ -210,7 +208,7 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的单个VO对象
*/
default V selectVoOne(Wrapper<T> wrapper) {
return this.selectVoOne(wrapper, this.currentVoClass());
return selectVoOne(wrapper, this.currentVoClass());
}
/**
@ -221,12 +219,11 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的单个VO对象
*/
default V selectVoOne(Wrapper<T> wrapper, boolean throwEx) {
return this.selectVoOne(wrapper, this.currentVoClass(), throwEx);
return selectVoOne(wrapper, this.currentVoClass(), throwEx);
}
/**
* 根据条件查询单个VO对象并指定返回的VO对象的类型(自动拼接 limit 1)
* 注意不要再自己添加 limit 1 做限制了
* 根据条件查询单个VO对象并指定返回的VO对象的类型
*
* @param wrapper 查询条件Wrapper
* @param voClass 返回的VO对象的Class对象
@ -234,12 +231,11 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的单个VO对象经过类型转换为指定的VO类后返回
*/
default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
return this.selectVoOne(wrapper, voClass, true);
return selectVoOne(wrapper, voClass, true);
}
/**
* 根据条件查询单个实体对象并将其转换为指定的VO对象(自动拼接 limit 1)
* 注意不要再自己添加 limit 1 做限制了
* 根据条件查询单个实体对象并将其转换为指定的VO对象
*
* @param wrapper 查询条件Wrapper
* @param voClass 要转换的VO类的Class对象
@ -255,33 +251,13 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
return MapstructUtils.convert(obj, voClass);
}
/**
* 根据条件查询单条记录(自动拼接 limit 1 限制返回 1 条数据,不依赖 {@code throwEx} 参数)
* 注意不要再自己添加 limit 1 做限制了
* <p>
* <strong>注意:</strong>
* 1. 使用 {@code Page<>(1, 1)} 强制分页查询,确保 SQL 自动添加 {@code LIMIT 1},因此 {@code throwEx} 参数不再生效
* 2. 原方法的 {@code throwEx} 逻辑(多条数据抛异常)已被优化掉,因为分页查询不会返回多条记录
* </p>
*
* @param queryWrapper 查询条件(可为 null
* @param throwEx <del>是否抛出异常(已弃用,此参数不再生效)</del>
* @return 单条记录或无数据时返回 null
*/
@Override
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, boolean throwEx) {
// 强制分页查询LIMIT 1确保最多返回 1 条记录
List<T> list = this.selectList(new Page<>(1, 1), queryWrapper);
return CollUtil.isEmpty(list) ? null : list.get(0);
}
/**
* 查询所有VO对象列表
*
* @return 查询到的VO对象列表
*/
default List<V> selectVoList() {
return this.selectVoList(new QueryWrapper<>(), this.currentVoClass());
return selectVoList(new QueryWrapper<>(), this.currentVoClass());
}
/**
@ -318,7 +294,7 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
* @return 查询到的VO对象分页列表
*/
default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
return this.selectVoPage(page, wrapper, this.currentVoClass());
return selectVoPage(page, wrapper, this.currentVoClass());
}
/**

View File

@ -14,6 +14,9 @@ import org.dromara.common.social.gitea.AuthGiteaRequest;
import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
import org.dromara.common.social.topiam.AuthTopIamRequest;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 认证授权工具类
*
@ -40,7 +43,7 @@ public class SocialUtils {
AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
.clientId(obj.getClientId())
.clientSecret(obj.getClientSecret())
.redirectUri(obj.getRedirectUri())
.redirectUri(URLEncoder.encode(obj.getRedirectUri(), StandardCharsets.UTF_8))
.scopes(obj.getScopes());
return switch (source.toLowerCase()) {
case "dingtalk" -> new AuthDingTalkV2Request(builder.build(), STATE_CACHE);

View File

@ -1,7 +1,8 @@
package org.dromara.common.web.config;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.web.handler.GlobalExceptionHandler;
import org.dromara.common.web.interceptor.PlusWebInvokeTimeInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@ -34,10 +35,11 @@ public class ResourcesConfig implements WebMvcConfigurer {
public void addFormatters(FormatterRegistry registry) {
// 全局日期格式转换配置
registry.addConverter(String.class, Date.class, source -> {
if (StringUtils.isBlank(source)) {
DateTime parse = DateUtil.parse(source);
if (ObjectUtils.isNull(parse)) {
return null;
}
return DateUtil.parse(source);
return parse.toJdkDate();
});
}

View File

@ -1,8 +1,8 @@
package org.dromara.common.web.enums;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import org.dromara.common.web.utils.UnsignedMathGenerator;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -18,7 +18,7 @@ public enum CaptchaType {
/**
* 数字
*/
MATH(UnsignedMathGenerator.class),
MATH(MathGenerator.class),
/**
* 字符

View File

@ -1,88 +0,0 @@
package org.dromara.common.web.utils;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.math.Calculator;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.RandomUtil;
import org.dromara.common.core.utils.StringUtils;
import java.io.Serial;
/**
* 无符号计算生成器
*
* @author Lion Li
*/
public class UnsignedMathGenerator implements CodeGenerator {
@Serial
private static final long serialVersionUID = -5514819971774091076L;
private static final String OPERATORS = "+-*";
/**
* 参与计算数字最大长度
*/
private final int numberLength;
/**
* 构造
*/
public UnsignedMathGenerator() {
this(2);
}
/**
* 构造
*
* @param numberLength 参与计算最大数字位数
*/
public UnsignedMathGenerator(int numberLength) {
this.numberLength = numberLength;
}
@Override
public String generate() {
final int limit = getLimit();
int a = RandomUtil.randomInt(limit);
int b = RandomUtil.randomInt(limit);
String max = Integer.toString(Math.max(a,b));
String min = Integer.toString(Math.min(a,b));
max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE);
min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE);
return max + RandomUtil.randomChar(OPERATORS) + min + '=';
}
@Override
public boolean verify(String code, String userInputCode) {
int result;
try {
result = Integer.parseInt(userInputCode);
} catch (NumberFormatException e) {
// 用户输入非数字
return false;
}
final int calculateResult = (int) Calculator.conversion(code);
return result == calculateResult;
}
/**
* 获取验证码长度
*
* @return 验证码长度
*/
public int getLength() {
return this.numberLength * 2 + 2;
}
/**
* 根据长度获取参与计算数字最大值
*
* @return 最大值
*/
private int getLimit() {
return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
}
}

View File

@ -18,7 +18,7 @@ spring:
snail-job:
# 服务端节点IP(默认按照`NetUtil.getLocalIpStr()`)
server-host:
# 服务端netty的端口号
# 服务端端口号
server-port: 17888
# 合并日志默认保存天数
merge-Log-days: 1

View File

@ -18,7 +18,7 @@ spring:
snail-job:
# 服务端节点IP(默认按照`NetUtil.getLocalIpStr()`)
server-host:
# 服务端netty的端口号
# 服务端端口号
server-port: 17888
# 合并日志默认保存天数
merge-Log-days: 1

View File

@ -1,5 +1,6 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.collection.CollUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
@ -14,6 +15,7 @@ import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -94,6 +96,16 @@ public class TestExcelController {
exportExcelService.exportWithOptions(response);
}
/**
* 自定义导出
*
* @param response /
*/
@GetMapping("/customExport")
public void customExport(HttpServletResponse response) throws IOException {
exportExcelService.customExport(response);
}
/**
* 多个sheet导出
*/

View File

@ -2,6 +2,8 @@ package org.dromara.demo.service;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 导出下拉框Excel示例
*
@ -15,4 +17,11 @@ public interface IExportExcelService {
* @param response /
*/
void exportWithOptions(HttpServletResponse response);
/**
* 自定义导出
*
* @param response /
*/
void customExport(HttpServletResponse response) throws IOException;
}

View File

@ -2,17 +2,21 @@ package org.dromara.demo.service.impl;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.write.metadata.WriteSheet;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.excel.core.DropDownOptions;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.excel.utils.ExcelWriterWrapper;
import org.dromara.demo.domain.vo.ExportDemoVo;
import org.dromara.demo.service.IExportExcelService;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -233,4 +237,61 @@ public class ExportExcelServiceImpl implements IExportExcelService {
this.name = name;
}
}
@Override
public void customExport(HttpServletResponse response) throws IOException {
String filename = ExcelUtil.encodingFilename("自定义导出");
FileUtils.setAttachmentResponseHeader(response, filename);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
ExcelUtil.exportExcel(ExportDemoVo.class, response.getOutputStream(), wrapper -> {
// 创建表格数据,业务中一般通过数据库查询
List<ExportDemoVo> excelDataList = new ArrayList<>();
for (int i = 0; i < 30; i++) {
// 模拟数据库中的一条数据
ExportDemoVo everyRowData = new ExportDemoVo();
everyRowData.setNickName("用户-" + i);
everyRowData.setUserStatus(SystemConstants.NORMAL);
everyRowData.setGender("1");
everyRowData.setPhoneNumber(String.format("175%08d", i));
everyRowData.setEmail(String.format("175%08d", i) + "@163.com");
everyRowData.setProvinceId(i);
everyRowData.setCityId(i);
everyRowData.setAreaId(i);
excelDataList.add(everyRowData);
}
// 创建表格
WriteSheet sheet = ExcelWriterWrapper.sheetBuilder("自定义导出demo")
// 合并单元格
// .registerWriteHandler(new CellMergeStrategy(excelDataList, true))
.build();
wrapper.write(excelDataList, sheet);
List<ExportDemoVo> excelDataList2 = new ArrayList<>();
for (int i = 0; i < 20; i++) {
int index = 1000 + i;
// 模拟数据库中的一条数据
ExportDemoVo everyRowData = new ExportDemoVo();
everyRowData.setNickName("用户-" + index);
everyRowData.setUserStatus(SystemConstants.NORMAL);
everyRowData.setGender("1");
everyRowData.setPhoneNumber(String.format("175%08d", index));
everyRowData.setEmail(String.format("175%08d", index) + "@163.com");
everyRowData.setProvinceId(index);
everyRowData.setCityId(index);
everyRowData.setAreaId(index);
excelDataList2.add(everyRowData);
}
wrapper.write(excelDataList2, sheet);
// 或者在同一个excel中创建多个表格
// WriteSheet sheet2 = ExcelWriterWrapper.sheetBuilder("自定义导出demo2").build();
// wrapper.write(excelDataList2, sheet2);
});
}
}

View File

@ -54,11 +54,8 @@ export interface ${BusinessName}Query #if(!${treeCode})extends PageQuery #end{
#end
#end
#end
/**
* 日期范围参数
*/
params?: any;
/**
* 日期范围参数
*/
params?: any;
}

View File

@ -123,7 +123,7 @@
#end
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']" />

View File

@ -120,7 +120,7 @@
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']"></el-button>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${packageName}.mapper.${ClassName}Mapper">
</mapper>

View File

@ -53,6 +53,13 @@ public class CacheController {
}
}
/**
* 缓存监控列表信息
*
* @param info 信息
* @param dbSize 数据库
* @param commandStats 命令统计
*/
public record CacheListInfoVo(Properties info, Long dbSize, List<Map<String, String>> commandStats) {}
}

View File

@ -179,6 +179,12 @@ public class SysMenuController extends BaseController {
return toAjax(menuService.deleteMenuById(menuId));
}
/**
* 角色菜单列表树信息
*
* @param checkedKeys 选中菜单列表
* @param menus 菜单下拉树结构列表
*/
public record MenuTreeSelectVo(List<Long> checkedKeys, List<Tree<Long>> menus) {
}

View File

@ -1,6 +1,7 @@
package org.dromara.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@ -13,8 +14,10 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.domain.bo.SysPostBo;
import org.dromara.system.domain.vo.SysPostVo;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.service.ISysPostService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -35,6 +38,7 @@ import java.util.List;
public class SysPostController extends BaseController {
private final ISysPostService postService;
private final ISysDeptService deptService;
/**
* 获取岗位列表
@ -134,4 +138,14 @@ public class SysPostController extends BaseController {
return R.ok(list);
}
/**
* 获取部门树列表
*/
@SaCheckPermission("system:post:list")
@GetMapping("/deptTree")
public R<List<Tree<Long>>> deptTree(SysDeptBo dept) {
return R.ok(deptService.selectDeptTreeList(dept));
}
}

View File

@ -129,8 +129,20 @@ public class SysProfileController extends BaseController {
return R.fail("上传图片异常,请联系管理员");
}
/**
* 用户头像信息
*
* @param imgUrl 头像地址
*/
public record AvatarVo(String imgUrl) {}
/**
* 用户个人信息
*
* @param user 用户信息
* @param roleGroup 用户所属角色组
* @param postGroup 用户所属岗位组
*/
public record ProfileVo(ProfileUserVo user, String roleGroup, String postGroup) {}
}

View File

@ -235,6 +235,12 @@ public class SysRoleController extends BaseController {
return R.ok(selectVo);
}
/**
* 角色部门列表树信息
*
* @param checkedKeys 选中部门列表
* @param depts 下拉树结构列表
*/
public record DeptTreeSelectVo(List<Long> checkedKeys, List<Tree<Long>> depts) {}
}

View File

@ -32,6 +32,11 @@ public class MetaVo {
*/
private String link;
/**
* 激活菜单
*/
private String activeMenu;
public MetaVo(String title, String icon) {
this.title = title;
this.icon = icon;
@ -58,4 +63,16 @@ public class MetaVo {
}
}
public MetaVo(String title, String icon, Boolean noCache, String link, String activeMenu) {
this.title = title;
this.icon = icon;
this.noCache = noCache;
if (StringUtils.ishttp(link)) {
this.link = link;
}
if (StringUtils.startWithAnyIgnoreCase(activeMenu, "/")) {
this.activeMenu = activeMenu;
}
}
}

View File

@ -30,7 +30,9 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
*/
default String buildDeptByRoleSql(Long roleId) {
return """
select dept_id from sys_role_dept where role_id = %d
select srd.dept_id from sys_role_dept srd
left join sys_role sr on sr.role_id = srd.role_id
where srd.role_id = %d and sr.status = '0'
""".formatted(roleId);
}
@ -47,7 +49,9 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
default String buildParentDeptByRoleSql(Long roleId) {
return """
select parent_id from sys_dept where dept_id in (
select dept_id from sys_role_dept where role_id = %d
select srd.dept_id from sys_role_dept srd
left join sys_role sr on sr.role_id = srd.role_id
where srd.role_id = %d and sr.status = '0'
)
""".formatted(roleId);
}

View File

@ -32,7 +32,9 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
default String buildMenuByUserSql(Long userId) {
return """
select menu_id from sys_role_menu where role_id in (
select role_id from sys_user_role where user_id = %d
select sur.role_id from sys_user_role sur
left join sys_role sr on sr.role_id = sur.role_id
where sur.user_id = %d and sr.status = '0'
)
""".formatted(userId);
}
@ -50,7 +52,9 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
*/
default String buildMenuByRoleSql(Long roleId) {
return """
select menu_id from sys_role_menu where role_id = %d
select srm.menu_id from sys_role_menu srm
left join sys_role sr on sr.role_id = srm.role_id
where srm.role_id = %d and sr.status = '0'
""".formatted(roleId);
}
@ -68,7 +72,9 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
default String buildParentMenuByRoleSql(Long roleId) {
return """
select parent_id from sys_menu where menu_id in (
select menu_id from sys_role_menu where role_id = %d
select srm.menu_id from sys_role_menu srm
left join sys_role sr on sr.role_id = srm.role_id
where srm.role_id = %d and sr.status = '0'
)
""".formatted(roleId);
}

View File

@ -161,6 +161,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
});
}
return baseMapper.selectObjs(new LambdaQueryWrapper<SysMenu>()
.select(SysMenu::getMenuId)
.in(SysMenu::getMenuId, menuIds)
.notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), x -> {
return Convert.toLong(x);
@ -185,7 +186,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
router.setPath(menu.getRouterPath());
router.setComponent(menu.getComponentInfo());
router.setQuery(menu.getQueryParam());
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath(), menu.getRemark()));
List<SysMenu> cMenus = menu.getChildren();
if (CollUtil.isNotEmpty(cMenus) && SystemConstants.TYPE_DIR.equals(menu.getMenuType())) {
router.setAlwaysShow(true);
@ -199,7 +200,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
children.setPath(menu.getPath());
children.setComponent(menu.getComponent());
children.setName(frameName);
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath(), menu.getRemark()));
children.setQuery(menu.getQueryParam());
childrenList.add(children);
router.setChildren(childrenList);

View File

@ -88,4 +88,8 @@ public interface FlowConstant {
*/
String AUTO_PASS = "autoPass";
/**
* 业务编码
*/
String BUSINESS_CODE = "businessCode";
}

View File

@ -0,0 +1,20 @@
package org.dromara.workflow.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 抄送设置枚举
*
* @author AprilWind
*/
@Getter
@AllArgsConstructor
public enum CopySettingEnum implements NodeExtEnum {
;
private final String label;
private final String value;
private final boolean selected;
}

View File

@ -0,0 +1,20 @@
package org.dromara.workflow.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 变量枚举
*
* @author AprilWind
*/
@Getter
@AllArgsConstructor
public enum VariablesEnum implements NodeExtEnum {
;
private final String label;
private final String value;
private final boolean selected;
}

View File

@ -0,0 +1,59 @@
package org.dromara.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 流程实例业务扩展对象 flow_instance_biz_ext
*
* @author may
* @date 2025-08-05
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("flow_instance_biz_ext")
public class FlowInstanceBizExt extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id")
private Long id;
/**
* 流程实例ID
*/
private Long instanceId;
/**
* 业务ID
*/
private String businessId;
/**
* 业务编码
*/
private String businessCode;
/**
* 业务标题
*/
private String businessTitle;
/**
* 删除标志0代表存在 1代表删除
*/
@TableLogic
private String delFlag;
}

View File

@ -29,6 +29,11 @@ public class TestLeave extends BaseEntity {
@TableId(value = "id")
private Long id;
/**
* 申请编号
*/
private String applyCode;
/**
* 请假类型
*/

View File

@ -1,9 +1,11 @@
package org.dromara.workflow.domain.bo;
import cn.hutool.core.util.ObjectUtil;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.workflow.domain.FlowInstanceBizExt;
import java.io.Serial;
import java.io.Serializable;
@ -44,6 +46,11 @@ public class StartProcessBo implements Serializable {
*/
private Map<String, Object> variables;
/**
* 流程业务扩展信息
*/
private FlowInstanceBizExt bizExt;
public Map<String, Object> getVariables() {
if (variables == null) {
return new HashMap<>(16);
@ -51,4 +58,11 @@ public class StartProcessBo implements Serializable {
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables;
}
public FlowInstanceBizExt getBizExt() {
if (ObjectUtil.isNull(bizExt)) {
bizExt = new FlowInstanceBizExt();
}
return bizExt;
}
}

View File

@ -36,6 +36,11 @@ public class TestLeaveBo extends BaseEntity {
*/
private String flowCode;
/**
* 申请编号
*/
private String applyCode;
/**
* 请假类型
*/

View File

@ -0,0 +1,36 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import java.io.Serial;
import java.io.Serializable;
/**
* 抄送对象
*
* @author AprilWind
*/
@Data
public class FlowCopyVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户id
*/
private Long userId;
/**
* 用户名称
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "userId")
private String userName;
public FlowCopyVo(Long userId) {
this.userId = userId;
}
}

View File

@ -203,6 +203,18 @@ public class FlowHisTaskVo implements Serializable {
*/
private String runDuration;
//业务扩展信息开始
/**
* 业务编码
*/
private String businessCode;
/**
* 业务标题
*/
private String businessTitle;
//业务扩展信息结束
/**
* 设置创建时间并计算任务运行时长
*

View File

@ -134,4 +134,16 @@ public class FlowInstanceVo {
@Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category")
private String categoryName;
//业务扩展信息开始
/**
* 业务编码
*/
private String businessCode;
/**
* 业务标题
*/
private String businessTitle;
//业务扩展信息结束
}

View File

@ -11,6 +11,7 @@ import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 任务视图
@ -185,4 +186,30 @@ public class FlowTaskVo implements Serializable {
*/
private List<ButtonPermissionVo> buttonList;
/**
* 抄送对象 ID 集合
* <p>
* 根据扩展属性中 CopySettingEnum 类型的数据生成,存储需要抄送的对象 ID
*/
private List<FlowCopyVo> copyList;
/**
* 自定义参数 Map
* <p>
* 根据扩展属性中 VariablesEnum 类型的数据生成,存储 key=value 格式的自定义参数
*/
private Map<String, String> varList;
//业务扩展信息开始
/**
* 业务编码
*/
private String businessCode;
/**
* 业务标题
*/
private String businessTitle;
//业务扩展信息结束
}

View File

@ -0,0 +1,45 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Node 扩展属性解析结果 VO
* <p>
* 用于封装从扩展属性 JSON 中解析出的各类信息,包括按钮权限、抄送对象和自定义参数。
*
* @author AprilWind
*/
@Data
public class NodeExtVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 按钮权限列表
* <p>
* 根据扩展属性中 ButtonPermissionEnum 类型的数据生成,每个元素表示一个按钮及其是否勾选。
*/
private List<ButtonPermissionVo> buttonPermissions;
/**
* 抄送对象 ID 集合
* <p>
* 根据扩展属性中 CopySettingEnum 类型的数据生成,存储需要抄送的对象 ID
*/
private Set<String> copySettings;
/**
* 自定义参数 Map
* <p>
* 根据扩展属性中 VariablesEnum 类型的数据生成,存储 key=value 格式的自定义参数
*/
private Map<String, String> variables;
}

View File

@ -32,6 +32,12 @@ public class TestLeaveVo implements Serializable {
@ExcelProperty(value = "主键")
private Long id;
/**
* 申请编号
*/
@ExcelProperty(value = "申请编号")
private String applyCode;
/**
* 请假类型
*/

View File

@ -24,7 +24,7 @@ import java.util.Map;
public class FlowProcessEventHandler {
/**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成等)
*
* @param flowCode 流程定义编码
* @param instance 实例数据

View File

@ -1,33 +1,39 @@
package org.dromara.workflow.listener;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.Task;
import org.dromara.warm.flow.core.listener.GlobalListener;
import org.dromara.warm.flow.core.listener.ListenerVariable;
import org.dromara.warm.flow.core.service.InsService;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.bo.FlowCopyBo;
import org.dromara.workflow.domain.vo.NodeExtVo;
import org.dromara.workflow.handler.FlowProcessEventHandler;
import org.dromara.workflow.service.IFlwCommonService;
import org.dromara.workflow.service.IFlwInstanceService;
import org.dromara.workflow.service.IFlwNodeExtService;
import org.dromara.workflow.service.IFlwTaskService;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 全局任务办理监听
@ -41,10 +47,11 @@ import java.util.Map;
public class WorkflowGlobalListener implements GlobalListener {
private final IFlwTaskService flwTaskService;
private final IFlwInstanceService instanceService;
private final IFlwInstanceService flwInstanceService;
private final FlowProcessEventHandler flowProcessEventHandler;
private final IFlwCommonService flwCommonService;
private final InsService insService;
private final IFlwNodeExtService nodeExtService;
private final UserService userService;
/**
* 创建监听器,任务创建时执行
@ -63,6 +70,25 @@ public class WorkflowGlobalListener implements GlobalListener {
*/
@Override
public void start(ListenerVariable listenerVariable) {
String ext = listenerVariable.getNode().getExt();
if (StringUtils.isNotBlank(ext)) {
NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext);
Map<String, Object> variable = listenerVariable.getVariable();
Set<String> copyList = nodeExt.getCopySettings();
if (CollUtil.isNotEmpty(copyList)) {
List<FlowCopyBo> list = StreamUtils.toList(copyList, x -> {
FlowCopyBo bo = new FlowCopyBo();
Long id = Convert.toLong(x);
bo.setUserId(id);
bo.setUserName(userService.selectUserNameById(id));
return bo;
});
variable.put(FlowConstant.FLOW_COPY_LIST, list);
}
if (CollUtil.isNotEmpty(nodeExt.getVariables())) {
variable.putAll(nodeExt.getVariables());
}
}
}
/**
@ -132,7 +158,7 @@ public class WorkflowGlobalListener implements GlobalListener {
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, BusinessStatusEnum.BACK.getStatus(), params, false);
// 修改流程实例状态
instance.setFlowStatus(BusinessStatusEnum.BACK.getStatus());
insService.updateById(instance);
FlowEngine.insService().updateById(instance);
}
}
}
@ -161,12 +187,9 @@ public class WorkflowGlobalListener implements GlobalListener {
if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) {
List<String> messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() {});
String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE);
// 消息通知
if (CollUtil.isNotEmpty(messageType)) {
flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice);
}
flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice);
}
insService.removeVariables(instance.getId(),
FlowEngine.insService().removeVariables(instance.getId(),
FlowConstant.FLOW_COPY_LIST,
FlowConstant.MESSAGE_TYPE,
FlowConstant.MESSAGE_NOTICE,
@ -190,7 +213,7 @@ public class WorkflowGlobalListener implements GlobalListener {
if (flwTaskService.isTaskEnd(instanceId)) {
String status = BusinessStatusEnum.FINISH.getStatus();
// 更新流程状态为已完成
instanceService.updateStatus(instanceId, status);
flwInstanceService.updateStatus(instanceId, status);
log.info("流程已结束,状态更新为: {}", status);
return status;
}

View File

@ -0,0 +1,61 @@
package org.dromara.workflow.mapper;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.workflow.domain.FlowInstanceBizExt;
import java.util.List;
/**
* 流程实例业务扩展Mapper接口
*
* @author may
* @date 2025-08-05
*/
public interface FlwInstanceBizExtMapper extends BaseMapperPlus<FlowInstanceBizExt, FlowInstanceBizExt> {
/**
* 根据 instanceId 保存或更新流程实例业务扩展
*
* @param entity 流程实例业务扩展实体
* @return 操作是否成功
*/
default int saveOrUpdateByInstanceId(FlowInstanceBizExt entity) {
// 查询是否存在
FlowInstanceBizExt exist = this.selectOne(new LambdaQueryWrapper<FlowInstanceBizExt>()
.eq(FlowInstanceBizExt::getInstanceId, entity.getInstanceId()));
if (ObjectUtil.isNotNull(exist)) {
// 存在就带上主键更新
entity.setId(exist.getId());
return updateById(entity);
} else {
// 不存在就插入
return insert(entity);
}
}
/**
* 按照流程实例ID删除单个流程实例业务扩展
*
* @param instanceId 流程实例ID
* @return 删除的行数
*/
default int deleteByInstId(Long instanceId) {
return this.delete(new LambdaQueryWrapper<FlowInstanceBizExt>()
.eq(FlowInstanceBizExt::getInstanceId, instanceId));
}
/**
* 按照流程实例ID批量删除流程实例业务扩展
*
* @param instanceIds 流程实例ID列表
* @return 删除的行数
*/
default int deleteByInstIds(List<Long> instanceIds) {
return this.delete(new LambdaQueryWrapper<FlowInstanceBizExt>()
.in(FlowInstanceBizExt::getInstanceId, instanceIds));
}
}

View File

@ -1,8 +1,6 @@
package org.dromara.workflow.service;
import org.dromara.workflow.domain.vo.ButtonPermissionVo;
import java.util.List;
import org.dromara.workflow.domain.vo.NodeExtVo;
/**
* 流程节点扩展属性 服务层
@ -12,11 +10,23 @@ import java.util.List;
public interface IFlwNodeExtService {
/**
* 扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选
* 解析扩展属性 JSON 并构建 Node 扩展属性对象
* <p>
* 根据传入的 JSON 字符串,将扩展属性分为三类:
* 1. ButtonPermissionEnum解析为按钮权限列表标记每个按钮是否勾选
* 2. CopySettingEnum解析为抄送对象 ID 集合
* 3. VariablesEnum解析为自定义参数 Map
*
* <p>示例 JSON
* [
* {"code": "ButtonPermissionEnum", "value": "back,termination"},
* {"code": "CopySettingEnum", "value": "1"},
* {"code": "VariablesEnum", "value": "key1=value1,key2=value2"}
* ]
*
* @param ext 扩展属性 JSON 字符串
* @return 按钮权限 VO 列表
* @return NodeExtVo 对象,封装按钮权限列表、抄送对象集合和自定义参数 Map
*/
List<ButtonPermissionVo> buildButtonPermissionsFromExt(String ext);
NodeExtVo parseNodeExt(String ext);
}

View File

@ -24,6 +24,7 @@ import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
import org.dromara.warm.flow.ui.service.ChartExtService;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@ -48,6 +49,8 @@ public class FlwChartExtServiceImpl implements ChartExtService {
private final DeptService deptService;
private final FlowHisTaskMapper flowHisTaskMapper;
private final DictService dictService;
@Value("${warm-flow.node-tooltip:true}")
private boolean nodeTooltip;
/**
* 设置流程图提示信息
@ -56,6 +59,11 @@ public class FlwChartExtServiceImpl implements ChartExtService {
*/
@Override
public void execute(DefJson defJson) {
// 配置关闭,直接返回,不渲染悬浮窗
if (!nodeTooltip) {
return;
}
// 根据流程实例ID查询所有相关的历史任务列表
List<FlowHisTask> flowHisTasks = this.getHisTaskGroupedByNode(defJson.getInstance().getId());
if (CollUtil.isEmpty(flowHisTasks)) {
@ -103,6 +111,11 @@ public class FlwChartExtServiceImpl implements ChartExtService {
*/
@Override
public void initPromptContent(DefJson defJson) {
// 配置关闭,直接返回,不渲染悬浮窗
if (!nodeTooltip) {
return;
}
defJson.setTopText("流程名称: " + defJson.getFlowName());
defJson.getNodeList().forEach(nodeJson -> {
nodeJson.setPromptContent(
@ -152,8 +165,10 @@ public class FlwChartExtServiceImpl implements ChartExtService {
/**
* 处理节点的扩展信息,构建用于流程图悬浮提示的内容
*
* @param nodeJson 当前节点对象
* @param taskList 当前节点对应的历史审批任务列表
* @param nodeJson 当前流程节点对象,包含节点基础信息和提示内容容器
* @param taskList 当前节点关联的历史审批任务列表,用于生成提示信息
* @param userMap 用户信息映射表key 为用户IDvalue 为用户DTO对象用于获取审批人信息
* @param dictType 数据字典映射表key 为字典项编码value 为对应显示值,用于翻译审批状态等
*/
private void processNodeExtInfo(NodeJson nodeJson, List<FlowHisTask> taskList, Map<Long, UserDTO> userMap, Map<String, String> dictType) {

View File

@ -11,9 +11,8 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.entity.Node;
import org.dromara.warm.flow.core.enums.SkipType;
import org.dromara.warm.flow.core.service.NodeService;
import org.dromara.warm.flow.orm.entity.FlowTask;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.enums.MessageTypeEnum;
@ -21,8 +20,10 @@ import org.dromara.workflow.service.IFlwCommonService;
import org.dromara.workflow.service.IFlwTaskService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Set;
/**
@ -35,19 +36,27 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
@Service
public class FlwCommonServiceImpl implements IFlwCommonService {
private final NodeService nodeService;
private static final String DEFAULT_SUBJECT = "单据审批提醒";
/**
* 发送消息
* 根据流程实例发送消息给当前处理人
*
* @param flowName 流程定义名称
* @param messageType 消息类型
* @param message 消息内容,为空则发送默认配置的消息内容
* @param instId 流程实例ID
* @param messageType 消息类型列表
* @param message 消息内容,为空则使用默认消息
*/
@Override
public void sendMessage(String flowName, Long instId, List<String> messageType, String message) {
if (CollUtil.isEmpty(messageType)) {
return;
}
IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class);
List<FlowTask> list = flwTaskService.selectByInstId(instId);
if (CollUtil.isEmpty(list)) {
return;
}
if (StringUtils.isBlank(message)) {
message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。";
}
@ -55,19 +64,25 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
if (CollUtil.isEmpty(userList)) {
return;
}
sendMessage(messageType, message, "单据审批提醒", userList);
sendMessage(messageType, message, DEFAULT_SUBJECT, userList);
}
/**
* 发送消息
* 发送消息给指定用户列表
*
* @param messageType 消息类型
* @param messageType 消息类型列表
* @param message 消息内容
* @param subject 邮件标题
* @param userList 接收用户
* @param userList 接收用户列表
*/
@Override
public void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList) {
if (CollUtil.isEmpty(messageType) || CollUtil.isEmpty(userList)) {
return;
}
List<Long> userIds = new ArrayList<>(StreamUtils.toSet(userList, UserDTO::getUserId));
Set<String> emails = StreamUtils.toSet(userList, UserDTO::getEmail);
for (String code : messageType) {
MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code);
if (ObjectUtil.isEmpty(messageTypeEnum)) {
@ -76,13 +91,11 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
switch (messageTypeEnum) {
case SYSTEM_MESSAGE -> {
SseMessageDto dto = new SseMessageDto();
dto.setUserIds(StreamUtils.toList(userList, UserDTO::getUserId).stream().distinct().collect(Collectors.toList()));
dto.setUserIds(userIds);
dto.setMessage(message);
SseMessageUtils.publishMessage(dto);
}
case EMAIL_MESSAGE -> {
MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), subject, message);
}
case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message);
case SMS_MESSAGE -> {
//todo 短信发送
}
@ -100,8 +113,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
*/
@Override
public String applyNodeCode(Long definitionId) {
Node startNode = nodeService.getStartNode(definitionId);
Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey());
return nextNode.getNodeCode();
List<Node> firstBetweenNode = FlowEngine.nodeService().getFirstBetweenNode(definitionId, new HashMap<>());
return firstBetweenNode.get(0).getNodeCode();
}
}

View File

@ -1,8 +1,8 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.DictTypeDTO;
@ -13,14 +13,16 @@ import org.dromara.warm.flow.ui.service.NodeExtService;
import org.dromara.warm.flow.ui.vo.NodeExt;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.enums.ButtonPermissionEnum;
import org.dromara.workflow.common.enums.CopySettingEnum;
import org.dromara.workflow.common.enums.NodeExtEnum;
import org.dromara.workflow.common.enums.VariablesEnum;
import org.dromara.workflow.domain.vo.ButtonPermissionVo;
import org.dromara.workflow.domain.vo.NodeExtVo;
import org.dromara.workflow.service.IFlwNodeExtService;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 流程设计器-节点扩展属性
@ -36,14 +38,35 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
/**
* 存储不同 dictType 对应的配置信息
*/
private static final Map<String, ButtonPermission> CHILD_NODE_MAP = new HashMap<>();
record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) {
}
private static final Map<String, Map<String, Object>> CHILD_NODE_MAP;
static {
CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getSimpleName(),
new ButtonPermission("权限按钮", 4, false, true));
CHILD_NODE_MAP = Map.of(
CopySettingEnum.class.getSimpleName(),
Map.of(
"label", "抄送对象",
"type", 2,
"must", false,
"multiple", false,
"desc", "设置该节点的抄送办理人"
),
VariablesEnum.class.getSimpleName(),
Map.of(
"label", "自定义参数",
"type", 2,
"must", false,
"multiple", false,
"desc", "节点执行时可以使用的自定义参数"
),
ButtonPermissionEnum.class.getSimpleName(),
Map.of(
"label", "权限按钮",
"type", 4,
"must", false,
"multiple", true,
"desc", "控制该节点的按钮权限"
)
);
}
private final DictService dictService;
@ -56,6 +79,9 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
@Override
public List<NodeExt> getNodeExt() {
List<NodeExt> nodeExtList = new ArrayList<>();
// 构建基础设置页面
nodeExtList.add(buildNodeExt("wf_basic_tab", "基础设置", 1,
List.of(CopySettingEnum.class, VariablesEnum.class)));
// 构建按钮权限页面
nodeExtList.add(buildNodeExt("wf_button_tab", "权限", 2,
List.of(ButtonPermissionEnum.class)));
@ -105,9 +131,20 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
return null;
}
String simpleName = enumClass.getSimpleName();
NodeExt.ChildNode childNode = buildChildNodeMap(simpleName);
NodeExt.ChildNode childNode = new NodeExt.ChildNode();
Map<String, Object> map = CHILD_NODE_MAP.get(simpleName);
// 编码此json中唯
childNode.setCode(simpleName);
// label名称
childNode.setLabel(Convert.toStr(map.get("label")));
// 1输入框 2文本域 3下拉框 4选择框
childNode.setType(Convert.toInt(map.get("type"), 1));
// 是否必填
childNode.setMust(Convert.toBool(map.get("must"), false));
// 是否多选
childNode.setMultiple(Convert.toBool(map.get("multiple"), true));
// 描述
childNode.setDesc(Convert.toStr(map.get("desc"), null));
// 字典,下拉框和复选框时用到
childNode.setDict(Arrays.stream(enumClass.getEnumConstants())
.map(NodeExtEnum.class::cast)
@ -128,12 +165,18 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
if (ObjectUtil.isNull(dictTypeDTO)) {
return null;
}
NodeExt.ChildNode childNode = buildChildNodeMap(dictType);
NodeExt.ChildNode childNode = new NodeExt.ChildNode();
// 编码此json中唯一
childNode.setCode(dictType);
// label名称
childNode.setLabel(dictTypeDTO.getDictName());
// 描述
// 1输入框 2文本域 3下拉框 4选择框
childNode.setType(3);
// 是否必填
childNode.setMust(false);
// 是否多选
childNode.setMultiple(true);
// 描述 (可根据描述参数解析更多配置如typemustmultiple等)
childNode.setDesc(dictTypeDTO.getRemark());
// 字典,下拉框和复选框时用到
childNode.setDict(dictService.getDictData(dictType)
@ -144,100 +187,71 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
}
/**
* 根据 CHILD_NODE_MAP 中的配置信息,构建一个基本的 ChildNode 对象
* 该方法用于设置 ChildNode 的常规属性,例如 label、type、是否必填、是否多选等
* 解析扩展属性 JSON 并构建 Node 扩展属性对象
* <p>
* 根据传入的 JSON 字符串,将扩展属性分为三类:
* 1. ButtonPermissionEnum解析为按钮权限列表标记每个按钮是否勾选
* 2. CopySettingEnum解析为抄送对象 ID 集合
* 3. VariablesEnum解析为自定义参数 Map
*
* @param key CHILD_NODE_MAP 的 key
* @return 返回构建好的 ChildNode 对象
*/
private NodeExt.ChildNode buildChildNodeMap(String key) {
NodeExt.ChildNode childNode = new NodeExt.ChildNode();
ButtonPermission bp = CHILD_NODE_MAP.get(key);
if (bp == null) {
childNode.setType(1);
childNode.setMust(false);
childNode.setMultiple(true);
return childNode;
}
// label名称
childNode.setLabel(bp.label());
// 1输入框 2输入框 3下拉框 4选择框
childNode.setType(bp.type());
// 是否必填
childNode.setMust(bp.must());
// 是否多选
childNode.setMultiple(bp.multiple());
return childNode;
}
/**
* 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选
* <p>示例 JSON
* [
* {"code": "ButtonPermissionEnum", "value": "back,termination"},
* {"code": "CopySettingEnum", "value": "1"},
* {"code": "VariablesEnum", "value": "key1=value1,key2=value2"}
* ]
*
* @param ext 扩展属性 JSON 字符串
* @return 按钮权限 VO 列表
* @return NodeExtVo 对象,封装按钮权限列表、抄送对象集合和自定义参数 Map
*/
@Override
public List<ButtonPermissionVo> buildButtonPermissionsFromExt(String ext) {
// 解析 ext 为 Map<code, Set<value>>,用于标记权限
Map<String, Set<String>> permissionMap = JsonUtils.parseArray(ext, ButtonPermissionVo.class)
.stream()
.collect(Collectors.toMap(
ButtonPermissionVo::getCode,
item -> StringUtils.splitList(item.getValue()).stream()
.map(String::trim)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet()),
(a, b) -> b,
HashMap::new
));
public NodeExtVo parseNodeExt(String ext) {
NodeExtVo nodeExtVo = new NodeExtVo();
// 构建按钮权限列表,标记哪些按钮在 permissionMap 中出现(表示已勾选)
return buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class));
}
// 解析 JSON 为 Dict 列表
List<Dict> nodeExtMap = JsonUtils.parseArrayMap(ext);
/**
* 将权限映射与按钮权限来源(枚举类或字典类型)进行匹配,生成权限视图列表
* <p>
* 使用说明:
* - sources 支持传入多个来源类型,支持 NodeExtEnum 枚举类 或 字典类型字符串dictType
* - 若需要扩展更多按钮权限,只需在 sources 中新增对应的枚举类或字典类型
* <p>
* 示例:
* buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class, "custom_button_dict"));
*
* @param permissionMap 权限映射
* @param sources 枚举类或字典类型列表
* @return 按钮权限视图对象列表
*/
@SuppressWarnings("unchecked cast")
private List<ButtonPermissionVo> buildPermissionsFromSources(Map<String, Set<String>> permissionMap, List<Object> sources) {
return sources.stream()
.flatMap(source -> {
if (source instanceof Class<?> clazz && NodeExtEnum.class.isAssignableFrom(clazz)) {
Set<String> selectedSet = permissionMap.getOrDefault(clazz.getSimpleName(), Collections.emptySet());
return extractDictItems(this.buildChildNode((Class<? extends NodeExtEnum>) clazz), selectedSet).stream();
} else if (source instanceof String dictType) {
Set<String> selectedSet = permissionMap.getOrDefault(dictType, Collections.emptySet());
return extractDictItems(this.buildChildNode(dictType), selectedSet).stream();
}
return Stream.empty();
}).toList();
}
for (Dict nodeExt : nodeExtMap) {
String code = nodeExt.getStr("code");
String value = nodeExt.getStr("value");
/**
* 从节点子项中提取字典项,并构建按钮权限视图对象列表
*
* @param childNode 子节点
* @param selectedSet 已选中的值集
* @return 按钮权限视图对象列表
*/
private List<ButtonPermissionVo> extractDictItems(NodeExt.ChildNode childNode, Set<String> selectedSet) {
return Optional.ofNullable(childNode)
.map(NodeExt.ChildNode::getDict)
.orElse(List.of())
.stream()
.map(dict -> new ButtonPermissionVo(dict.getValue(), selectedSet.contains(dict.getValue())))
.toList();
if (ButtonPermissionEnum.class.getSimpleName().equals(code)) {
// 解析按钮权限
// 将 value 拆分为 Set<String>,便于精确匹配
Set<String> buttonSet = StringUtils.str2Set(value, StringUtils.SEPARATOR);
// 获取按钮字典配置
NodeExt.ChildNode childNode = buildChildNode(ButtonPermissionEnum.class);
// 构建 ButtonPermissionVo 列表
List<ButtonPermissionVo> buttonList = Optional.ofNullable(childNode)
.map(NodeExt.ChildNode::getDict)
.orElse(List.of())
.stream()
.map(dict -> new ButtonPermissionVo(dict.getValue(), buttonSet.contains(dict.getValue())))
.toList();
nodeExtVo.setButtonPermissions(buttonList);
} else if (CopySettingEnum.class.getSimpleName().equals(code)) {
// 解析抄送对象 ID 集合
nodeExtVo.setCopySettings(StringUtils.str2Set(value, StringUtils.SEPARATOR));
} else if (VariablesEnum.class.getSimpleName().equals(code)) {
// 解析自定义参数
// 将 key=value 字符串拆分为 Map
Map<String, String> variables = Arrays.stream(StringUtils.split(value, StringUtils.SEPARATOR))
.map(s -> StringUtils.split(s, "="))
.filter(arr -> arr.length == 2)
.collect(Collectors.toMap(arr -> arr[0], arr -> arr[1]));
nodeExtVo.setVariables(variables);
} else {
// 未知扩展类型,记录日志
log.warn("未知扩展类型code={}, value={}", code, value);
}
}
return nodeExtVo;
}
}

View File

@ -36,15 +36,22 @@ import org.dromara.warm.flow.core.service.*;
import org.dromara.warm.flow.core.utils.ExpressionUtil;
import org.dromara.warm.flow.core.utils.MapUtil;
import org.dromara.warm.flow.orm.entity.*;
import org.dromara.warm.flow.orm.mapper.*;
import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
import org.dromara.warm.flow.orm.mapper.FlowNodeMapper;
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.TaskAssigneeType;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.FlowInstanceBizExt;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.FlowCopyVo;
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo;
import org.dromara.workflow.domain.vo.NodeExtVo;
import org.dromara.workflow.mapper.FlwCategoryMapper;
import org.dromara.workflow.mapper.FlwInstanceBizExtMapper;
import org.dromara.workflow.mapper.FlwTaskMapper;
import org.dromara.workflow.service.IFlwCommonService;
import org.dromara.workflow.service.IFlwNodeExtService;
@ -85,7 +92,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
private final IFlwTaskAssigneeService flwTaskAssigneeService;
private final IFlwCommonService flwCommonService;
private final IFlwNodeExtService flwNodeExtService;
private final FlowDefinitionMapper flowDefinitionMapper;
private final FlwInstanceBizExtMapper flwInstanceBizExtMapper;
/**
* 启动任务
@ -99,6 +106,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
if (StringUtils.isBlank(businessId)) {
throw new ServiceException("启动工作流时必须包含业务ID");
}
// 启动流程实例(提交申请)
Map<String, Object> variables = startProcessBo.getVariables();
// 流程发起人
@ -107,9 +115,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
variables.put(INITIATOR_DEPT_ID, LoginHelper.getDeptId());
// 业务id
variables.put(BUSINESS_ID, businessId);
FlowInstanceBizExt bizExt = startProcessBo.getBizExt();
// 获取已有流程实例
FlowInstance flowInstance = flowInstanceMapper.selectOne(new LambdaQueryWrapper<>(FlowInstance.class)
.eq(FlowInstance::getBusinessId, businessId));
if (ObjectUtil.isNotNull(flowInstance)) {
// 已存在流程
BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus());
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId()));
taskService.mergeVariable(flowInstance, variables);
@ -117,14 +130,17 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
StartProcessReturnDTO dto = new StartProcessReturnDTO();
dto.setProcessInstanceId(taskList.get(0).getInstanceId());
dto.setTaskId(taskList.get(0).getId());
// 保存流程实例业务信息
this.buildFlowInstanceBizExt(flowInstance, bizExt);
return dto;
}
// 将流程定义内的扩展参数设置到变量中
Definition definition = FlowEngine.defService().getPublishByFlowCode(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));
FlowParams flowParams = FlowParams.build()
.handler(startProcessBo.getHandler())
.flowCode(startProcessBo.getFlowCode())
@ -136,6 +152,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
// 保存流程实例业务信息
this.buildFlowInstanceBizExt(instance, bizExt);
// 申请人执行流程
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
if (taskList.size() > 1) {
@ -147,6 +165,31 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
return dto;
}
/**
* 生成业务编号,如果已有则直接返回已有值
*/
private String generateBusinessCode(FlowInstanceBizExt bizExt) {
if (StringUtils.isBlank(bizExt.getBusinessCode())) {
// TODO: 按照自己业务规则生成编号
String businessCode = Convert.toStr(System.currentTimeMillis());
bizExt.setBusinessCode(businessCode);
return businessCode;
}
return bizExt.getBusinessCode();
}
/**
* 构建流程实例业务信息
*
* @param instance 流程实例
* @param bizExt 流程业务扩展信息
*/
private void buildFlowInstanceBizExt(Instance instance, FlowInstanceBizExt bizExt) {
bizExt.setInstanceId(instance.getId());
bizExt.setBusinessId(instance.getBusinessId());
flwInstanceBizExtMapper.saveOrUpdateByInstanceId(bizExt);
}
/**
* 办理任务
*
@ -206,10 +249,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
/**
* 流程办理
*
* @param taskId 任务ID
* @param flowParams 参数
* @param instanceId 实例ID
* @param autoPass 自动审批
* @param taskId 任务ID
* @param flowParams 参数
* @param instanceId 实例ID
* @param autoPass 自动审批
*/
private void skipTask(Long taskId, FlowParams flowParams, Long instanceId, Boolean autoPass) {
// 执行任务跳转,并根据返回的处理人设置下一步处理人
@ -465,8 +508,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
/**
* 获取可驳回的前置节点
*
* @param taskId 任务id
* @param nowNodeCode 当前节点
* @param taskId 任务id
* @param nowNodeCode 当前节点
*/
@Override
public List<Node> getBackTaskNode(Long taskId, String nowNodeCode) {
@ -556,8 +599,16 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
if (ObjectUtil.isNull(flowNode)) {
throw new NullPointerException("当前【" + flowTaskVo.getNodeCode() + "】节点编码不存在");
}
NodeExtVo nodeExtVo = flwNodeExtService.parseNodeExt(flowNode.getExt());
//设置按钮权限
flowTaskVo.setButtonList(flwNodeExtService.buildButtonPermissionsFromExt(flowNode.getExt()));
flowTaskVo.setButtonList(nodeExtVo.getButtonPermissions());
if (CollUtil.isNotEmpty(nodeExtVo.getCopySettings())) {
List<FlowCopyVo> list = StreamUtils.toList(nodeExtVo.getCopySettings(), x -> new FlowCopyVo(Convert.toLong(x)));
flowTaskVo.setCopyList(list);
} else {
flowTaskVo.setCopyList(new ArrayList<>());
}
flowTaskVo.setVarList(nodeExtVo.getVariables());
flowTaskVo.setNodeRatio(flowNode.getNodeRatio());
flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(flwCommonService.applyNodeCode(task.getDefinitionId())));
return flowTaskVo;
@ -587,14 +638,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
//办理人变量替换
ExpressionUtil.evalVariable(buildNextTaskList, FlowParams.build().variable(mergeVariable));
for (FlowNode flowNode : nextFlowNodes) {
Task first = StreamUtils.findFirst(buildNextTaskList, t -> t.getNodeCode().equals(flowNode.getNodeCode()));
if (ObjectUtil.isNotNull(first) && CollUtil.isNotEmpty(first.getPermissionList())) {
List<UserDTO> users = flwTaskAssigneeService.fetchUsersByStorageIds(StringUtils.joinComma(first.getPermissionList()));
if (CollUtil.isNotEmpty(users)) {
flowNode.setPermissionFlag(StreamUtils.join(users, e -> Convert.toStr(e.getUserId())));
}
}
StreamUtils.findFirst(buildNextTaskList, t -> t.getNodeCode().equals(flowNode.getNodeCode()))
.ifPresent(first -> {
List<UserDTO> users;
if (CollUtil.isNotEmpty(first.getPermissionList())
&& CollUtil.isNotEmpty(users = flwTaskAssigneeService.fetchUsersByStorageIds(StringUtils.joinComma(first.getPermissionList())))) {
flowNode.setPermissionFlag(StreamUtils.join(users, e -> Convert.toStr(e.getUserId())));
}
});
}
}
return nextFlowNodes;

View File

@ -4,6 +4,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -106,6 +107,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true);
// 截止日期也算一天
bo.setLeaveDays((int) day + 1);
bo.setApplyCode(System.currentTimeMillis() + StrUtil.EMPTY);
TestLeave add = MapstructUtils.convert(bo, TestLeave.class);
if (StringUtils.isBlank(add.getStatus())) {
add.setStatus(BusinessStatusEnum.DRAFT.getStatus());
@ -123,6 +125,9 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true);
// 截止日期也算一天
bo.setLeaveDays((int) day + 1);
if (ObjectUtil.isNull(bo.getId())) {
bo.setApplyCode(System.currentTimeMillis() + StrUtil.EMPTY);
}
TestLeave leave = MapstructUtils.convert(bo, TestLeave.class);
boolean flag = baseMapper.insertOrUpdate(leave);
if (flag) {
@ -188,6 +193,10 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
String message = Convert.toStr(params.get("message"));
}
if (processEvent.getSubmit()) {
if(StringUtils.isBlank(testLeave.getApplyCode())){
String businessCode = MapUtil.getStr(params, "businessCode",StrUtil.EMPTY);
testLeave.setApplyCode(businessCode);
}
testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus());
}
baseMapper.updateById(testLeave);

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.workflow.mapper.FlwInstanceBizExtMapper">
</mapper>

View File

@ -27,9 +27,12 @@
fd.version,
fd.form_custom,
fd.form_path,
fd.category
fd.category,
biz.business_code,
biz.business_title
from flow_instance fi
left join flow_definition fd on fi.definition_id = fd.id
left join flow_instance_biz_ext biz on biz.instance_id = fi.id
${ew.getCustomSqlSegment}
</select>

View File

@ -30,11 +30,14 @@
COALESCE(t.form_path, d.form_path) as form_path,
d.version,
uu.processed_by,
uu.type
uu.type,
biz.business_code,
biz.business_title
from flow_task t
left join flow_user uu on uu.associated = t.id
left join flow_definition d on t.definition_id = d.id
left join flow_instance i on t.instance_id = i.id
left join flow_instance_biz_ext biz on biz.instance_id = i.id
where t.node_type = 1
and t.del_flag = '0'
and uu.del_flag = '0'
@ -71,10 +74,13 @@
c.flow_name,
c.flow_code,
c.category,
c.version
c.version,
biz.business_code,
biz.business_title
from flow_his_task a
left join flow_instance b on a.instance_id = b.id
left join flow_definition c on a.definition_id = c.id
left join flow_instance_biz_ext biz on biz.instance_id = b.id
where a.del_flag ='0'
and b.del_flag = '0'
and c.del_flag = '0'
@ -100,11 +106,14 @@
d.flow_name,
d.flow_code,
d.category,
d.version
d.version,
biz.business_code,
biz.business_title
from flow_user a
left join flow_his_task b on a.associated = b.task_id
left join flow_instance c on b.instance_id = c.id
left join flow_definition d on c.definition_id=d.id
left join flow_instance_biz_ext biz on biz.instance_id = c.id
where a.type = '4'
and a.del_flag = '0'
and b.del_flag = '0'

View File

@ -99,7 +99,7 @@ services:
network_mode: "host"
ruoyi-server1:
image: ruoyi/ruoyi-server:5.4.1
image: ruoyi/ruoyi-server:5.5.0
container_name: ruoyi-server1
environment:
# 时区上海
@ -115,7 +115,7 @@ services:
network_mode: "host"
ruoyi-server2:
image: ruoyi/ruoyi-server:5.4.1
image: ruoyi/ruoyi-server:5.5.0
container_name: ruoyi-server2
environment:
# 时区上海
@ -131,7 +131,7 @@ services:
network_mode: "host"
ruoyi-monitor-admin:
image: ruoyi/ruoyi-monitor-admin:5.4.1
image: ruoyi/ruoyi-monitor-admin:5.5.0
container_name: ruoyi-monitor-admin
environment:
# 时区上海
@ -143,7 +143,7 @@ services:
network_mode: "host"
ruoyi-snailjob-server:
image: ruoyi/ruoyi-snailjob-server:5.4.1
image: ruoyi/ruoyi-snailjob-server:5.5.0
container_name: ruoyi-snailjob-server
environment:
# 时区上海

View File

@ -447,11 +447,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen',
insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate, null, null, '租户管理菜单');
insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate, null, null, '租户套餐管理菜单');
insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate, null, null, '客户端管理菜单');
insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, '/tool/gen');
insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, '/system/role');
insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, '/system/user');
insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, '/system/dict');
insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, '/system/oss');
-- springboot-admin监控
insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate, null, null, 'Admin监控菜单');

View File

@ -379,6 +379,40 @@ COMMENT ON COLUMN flow_spel.update_time IS '更新时间';
INSERT INTO flow_spel VALUES (1, 'spelRuleComponent', 'selectDeptLeaderById', 'initiatorDeptId', '#{@spelRuleComponent.selectDeptLeaderById(#initiatorDeptId)}', '根据部门id获取部门负责人', '0', '0', 103, 1, SYSDATE, 1, SYSDATE);
INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, SYSDATE, 1, SYSDATE);
-- ----------------------------
-- 流程实例业务扩展表
-- ----------------------------
CREATE TABLE flow_instance_biz_ext (
id NUMBER(20),
tenant_id VARCHAR2(20) DEFAULT '000000',
create_dept NUMBER(20),
create_by NUMBER(20),
create_time TIMESTAMP,
update_by NUMBER(20),
update_time TIMESTAMP,
business_code VARCHAR2(255),
business_title VARCHAR2(1000),
del_flag CHAR(1) DEFAULT '0',
instance_id NUMBER(20),
business_id VARCHAR2(255)
);
alter table flow_instance_biz_ext add constraint pk_fi_biz_ext primary key (id);
COMMENT ON TABLE flow_instance_biz_ext IS '流程实例业务扩展表';
COMMENT ON COLUMN flow_instance_biz_ext.id IS '主键id';
COMMENT ON COLUMN flow_instance_biz_ext.tenant_id IS '租户编号';
COMMENT ON COLUMN flow_instance_biz_ext.create_dept IS '创建部门';
COMMENT ON COLUMN flow_instance_biz_ext.create_by IS '创建者';
COMMENT ON COLUMN flow_instance_biz_ext.create_time IS '创建时间';
COMMENT ON COLUMN flow_instance_biz_ext.update_by IS '更新者';
COMMENT ON COLUMN flow_instance_biz_ext.update_time IS '更新时间';
COMMENT ON COLUMN flow_instance_biz_ext.business_code IS '业务编码';
COMMENT ON COLUMN flow_instance_biz_ext.business_title IS '业务标题';
COMMENT ON COLUMN flow_instance_biz_ext.del_flag IS '删除标志0代表存在 1代表删除';
COMMENT ON COLUMN flow_instance_biz_ext.instance_id IS '流程实例Id';
COMMENT ON COLUMN flow_instance_biz_ext.business_id IS '业务Id';
-- ----------------------------
-- 请假单信息
-- ----------------------------
@ -386,6 +420,7 @@ CREATE TABLE test_leave
(
id NUMBER (20) NOT NULL,
tenant_id VARCHAR2 (20) DEFAULT '000000',
apply_code VARCHAR2 (50) NOT NULL,
leave_type VARCHAR2 (255) NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
@ -404,6 +439,7 @@ alter table test_leave add constraint pk_test_leave primary key (id);
COMMENT ON TABLE test_leave IS '请假申请表';
COMMENT ON COLUMN test_leave.id IS 'ID';
COMMENT ON COLUMN test_leave.tenant_id IS '租户编号';
COMMENT ON COLUMN test_leave.apply_code IS '申请编号';
COMMENT ON COLUMN test_leave.leave_type IS '请假类型';
COMMENT ON COLUMN test_leave.start_date IS '开始时间';
COMMENT ON COLUMN test_leave.end_date IS '结束时间';
@ -427,7 +463,7 @@ INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category',
INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('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, '');
INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, SYSDATE, NULL, NULL, '');

View File

@ -448,11 +448,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen',
insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', '1', '0', 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, now(), null, null, '租户管理菜单');
insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', '1', '0', 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, now(), null, null, '租户套餐管理菜单');
insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', '1', '0', 'C', '0', '0', 'system:client:list', 'international', 103, 1, now(), null, null, '客户端管理菜单');
insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, '/tool/gen');
insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, '/system/role');
insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, '/system/user');
insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, '/system/dict');
insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, '/system/oss');
-- springboot-admin监控
insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, now(), null, null, 'Admin监控菜单');

View File

@ -325,7 +325,7 @@ INSERT INTO flow_category VALUES (109, '000000', 102, '0,100,102', '离职', 2,
-- 流程spel表达式定义表
-- ----------------------------
CREATE TABLE flow_spel (
id BIGINT NOT NULL,
id int8 NOT NULL,
component_name VARCHAR(255),
method_name VARCHAR(255),
method_params VARCHAR(255),
@ -333,10 +333,10 @@ CREATE TABLE flow_spel (
remark VARCHAR(255),
status CHAR(1) DEFAULT '0',
del_flag CHAR(1) DEFAULT '0',
create_dept BIGINT,
create_by BIGINT,
create_dept int8,
create_by int8,
create_time TIMESTAMP,
update_by BIGINT,
update_by int8,
update_time TIMESTAMP,
PRIMARY KEY (id)
);
@ -359,6 +359,39 @@ COMMENT ON COLUMN flow_spel.update_time IS '更新时间';
INSERT INTO flow_spel VALUES (1, 'spelRuleComponent', 'selectDeptLeaderById', 'initiatorDeptId', '#{@spelRuleComponent.selectDeptLeaderById(#initiatorDeptId)}', '根据部门id获取部门负责人', '0', '0', 103, 1, now(), 1, now());
INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, now(), 1, now());
-- ----------------------------
-- 流程实例业务扩展表
-- ----------------------------
CREATE TABLE flow_instance_biz_ext (
id int8,
tenant_id VARCHAR(20) DEFAULT '000000',
create_dept int8,
create_by int8,
create_time TIMESTAMP,
update_by int8,
update_time TIMESTAMP,
business_code VARCHAR(255),
business_title VARCHAR(1000),
del_flag CHAR(1) DEFAULT '0',
instance_id int8,
business_id VARCHAR(255),
PRIMARY KEY (id)
);
COMMENT ON TABLE flow_instance_biz_ext IS '流程实例业务扩展表';
COMMENT ON COLUMN flow_instance_biz_ext.id IS '主键id';
COMMENT ON COLUMN flow_instance_biz_ext.tenant_id IS '租户编号';
COMMENT ON COLUMN flow_instance_biz_ext.create_dept IS '创建部门';
COMMENT ON COLUMN flow_instance_biz_ext.create_by IS '创建者';
COMMENT ON COLUMN flow_instance_biz_ext.create_time IS '创建时间';
COMMENT ON COLUMN flow_instance_biz_ext.update_by IS '更新者';
COMMENT ON COLUMN flow_instance_biz_ext.update_time IS '更新时间';
COMMENT ON COLUMN flow_instance_biz_ext.business_code IS '业务编码';
COMMENT ON COLUMN flow_instance_biz_ext.business_title IS '业务标题';
COMMENT ON COLUMN flow_instance_biz_ext.del_flag IS '删除标志0代表存在 1代表删除';
COMMENT ON COLUMN flow_instance_biz_ext.instance_id IS '流程实例Id';
COMMENT ON COLUMN flow_instance_biz_ext.business_id IS '业务Id';
-- ----------------------------
-- 请假单信息
-- ----------------------------
@ -366,6 +399,7 @@ CREATE TABLE test_leave
(
id int8 NOT NULL,
tenant_id VARCHAR(20) DEFAULT '000000'::varchar,
apply_code VARCHAR(50) NOT NULL,
leave_type VARCHAR(255) NOT NULL,
start_date TIMESTAMP NOT NULL,
end_date TIMESTAMP NOT NULL,
@ -383,6 +417,7 @@ CREATE TABLE test_leave
COMMENT ON TABLE test_leave IS '请假申请表';
COMMENT ON COLUMN test_leave.id IS 'id';
COMMENT ON COLUMN test_leave.tenant_id IS '租户编号';
COMMENT ON COLUMN test_leave.apply_code IS '申请编号';
COMMENT ON COLUMN test_leave.leave_type IS '请假类型';
COMMENT ON COLUMN test_leave.start_date IS '开始时间';
COMMENT ON COLUMN test_leave.end_date IS '结束时间';
@ -406,7 +441,7 @@ INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category',
INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('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, '');
INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), NULL, NULL, '');

View File

@ -282,11 +282,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen',
insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate(), null, null, '租户管理菜单');
insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate(), null, null, '租户套餐管理菜单');
insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate(), null, null, '客户端管理菜单');
insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, '/tool/gen');
insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, '/system/role');
insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, '/system/user');
insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, '/system/dict');
insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, '/system/oss');
-- springboot-admin监控
insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate(), null, null, 'Admin监控菜单');

View File

@ -207,13 +207,35 @@ CREATE TABLE flow_spel (
INSERT INTO flow_spel VALUES (1, 'spelRuleComponent', 'selectDeptLeaderById', 'initiatorDeptId', '#{@spelRuleComponent.selectDeptLeaderById(#initiatorDeptId)}', '根据部门id获取部门负责人', '0', '0', 103, 1, sysdate(), 1, sysdate());
INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, sysdate(), 1, sysdate());
-- ----------------------------
-- 流程实例业务扩展表
-- ----------------------------
create table flow_instance_biz_ext (
id bigint not null comment '主键id',
tenant_id varchar(20) default '000000' null comment '租户编号',
create_dept bigint null comment '创建部门',
create_by bigint null comment '创建者',
create_time datetime null comment '创建时间',
update_by bigint null comment '更新者',
update_time datetime null comment '更新时间',
business_code varchar(255) null comment '业务编码',
business_title varchar(1000) null comment '业务标题',
del_flag char default '0' null comment '删除标志0代表存在 1代表删除',
instance_id bigint null comment '流程实例Id',
business_id varchar(255) null comment '业务Id',
PRIMARY KEY (id)
) ENGINE = InnoDB COMMENT '流程实例业务扩展表';
-- ----------------------------
-- 请假单信息
-- ----------------------------
create table test_leave
(
id bigint(20) not null comment 'id',
tenant_id varchar(20) default '000000' comment '租户编号',
tenant_id varchar(20) default '000000' comment '租户编号',
apply_code varchar(50) not null comment '申请编号',
leave_type varchar(255) not null comment '请假类型',
start_date datetime not null comment '开始时间',
end_date datetime not null comment '结束时间',
@ -240,7 +262,7 @@ INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', '2', 'spel',
insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('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, '');
-- 流程分类管理相关按钮
insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1,sysdate(), null, null, '');

View File

@ -1690,15 +1690,15 @@ INSERT sys_menu VALUES (122, N'租户套餐管理', 6, 2, N'tenantPackage', N'sy
GO
INSERT sys_menu VALUES (123, N'客户端管理', 1, 11, N'client', N'system/client/index', N'', 1, 0, N'C', N'0', N'0', N'system:client:list', N'international', 103, 1, getdate(), NULL, NULL, N'客户端管理菜单')
GO
INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N'');
INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N'/tool/gen');
GO
INSERT sys_menu VALUES (130, N'分配用户', 1, 2, N'role-auth/user/:roleId', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N'');
INSERT sys_menu VALUES (130, N'分配用户', 1, 2, N'role-auth/user/:roleId', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N'/system/role');
GO
INSERT sys_menu VALUES (131, N'分配角色', 1, 1, N'user-auth/role/:userId', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N'');
INSERT sys_menu VALUES (131, N'分配角色', 1, 1, N'user-auth/role/:userId', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N'/system/user');
GO
INSERT sys_menu VALUES (132, N'字典数据', 1, 6, N'dict-data/index/:dictId', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N'');
INSERT sys_menu VALUES (132, N'字典数据', 1, 6, N'dict-data/index/:dictId', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N'/system/dict');
GO
INSERT sys_menu VALUES (133, N'文件配置管理', 1, 10, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N'');
INSERT sys_menu VALUES (133, N'文件配置管理', 1, 10, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N'/system/oss');
GO
INSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', 103, 1, getdate(), NULL, NULL, N'Admin监控菜单');

View File

@ -1155,7 +1155,9 @@ CREATE TABLE flow_spel (
create_time DATETIME,
update_by BIGINT,
update_time DATETIME,
CONSTRAINT PK_flow_spel PRIMARY KEY (id)
CONSTRAINT PK__flow_spel__D54EE9B4AE98B9C1 PRIMARY KEY CLUSTERED (id)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
);
GO
@ -1261,9 +1263,118 @@ GO
INSERT flow_spel VALUES (2, NULL, NULL, N'initiator', N'${initiator}', N'流程发起人', N'0', N'0', 103, 1, GETDATE(), 1, GETDATE());
GO
CREATE TABLE flow_instance_biz_ext (
id BIGINT NOT NULL,
tenant_id VARCHAR(20) DEFAULT ('000000'),
create_dept BIGINT,
create_by BIGINT,
create_time DATETIME,
update_by BIGINT,
update_time DATETIME,
business_code VARCHAR(255),
business_title VARCHAR(1000),
del_flag CHAR(1) DEFAULT ('0'),
instance_id BIGINT,
business_id VARCHAR(255),
CONSTRAINT PK__fi_biz_ext__D54EE9B4AE98B9C1 PRIMARY KEY CLUSTERED (id)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
);
EXEC sp_addextendedproperty
'MS_Description', N'流程实例业务扩展表',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext'
GO
EXEC sp_addextendedproperty
'MS_Description', N'主键id',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'租户编号',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'tenant_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'创建部门',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'create_dept'
GO
EXEC sp_addextendedproperty
'MS_Description', N'创建者',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'create_by'
GO
EXEC sp_addextendedproperty
'MS_Description', N'创建时间',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'create_time'
GO
EXEC sp_addextendedproperty
'MS_Description', N'更新者',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'update_by'
GO
EXEC sp_addextendedproperty
'MS_Description', N'更新时间',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'update_time'
GO
EXEC sp_addextendedproperty
'MS_Description', N'删除标志',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'del_flag'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务编码',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'business_code'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务标题',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'business_title'
GO
EXEC sp_addextendedproperty
'MS_Description', N'流程实例Id',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'instance_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务Id',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'business_id'
GO
CREATE TABLE test_leave (
id bigint NOT NULL,
tenant_id nvarchar(20) DEFAULT('000000') NULL,
apply_code nvarchar(50) NOT NULL,
leave_type nvarchar(255) NOT NULL,
start_date datetime2(7) NOT NULL,
end_date datetime2(7) NOT NULL,
@ -1296,6 +1407,13 @@ EXEC sp_addextendedproperty
'COLUMN', N'tenant_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'申请编号',
'SCHEMA', N'dbo',
'TABLE', N'test_leave',
'COLUMN', N'apply_code'
GO
EXEC sp_addextendedproperty
'MS_Description', N'请假类型',
'SCHEMA', N'dbo',
@ -1401,7 +1519,7 @@ INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'monitor', NULL, N'',
GO
INSERT sys_menu VALUES (11631, N'待办任务', 11630, 2, N'allTaskWaiting', N'workflow/task/allTaskWaiting', N'', 1, 1, N'C', N'0', N'0', N'', N'waiting', 103, 1, GETDATE(), NULL, NULL, N'');
GO
INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'/workflow/processDefinition');
GO
INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO

View File

@ -51,3 +51,47 @@ UPDATE flow_skip SET skip_condition = REPLACE(skip_condition, 'notNike', 'notLik
ALTER TABLE flow_his_task MODIFY (collaborator VARCHAR2(500) DEFAULT NULL NULL);
COMMENT ON COLUMN flow_his_task.collaborator IS '协作人';
-- ----------------------------
-- 流程实例业务扩展表
-- ----------------------------
CREATE TABLE flow_instance_biz_ext (
id NUMBER(20),
tenant_id VARCHAR2(20) DEFAULT '000000',
create_dept NUMBER(20),
create_by NUMBER(20),
create_time TIMESTAMP,
update_by NUMBER(20),
update_time TIMESTAMP,
business_code VARCHAR2(255),
business_title VARCHAR2(1000),
del_flag CHAR(1) DEFAULT '0',
instance_id NUMBER(20),
business_id VARCHAR2(255)
);
alter table flow_instance_biz_ext add constraint pk_fi_biz_ext primary key (id);
COMMENT ON TABLE flow_instance_biz_ext IS '流程实例业务扩展表';
COMMENT ON COLUMN flow_instance_biz_ext.id IS '主键id';
COMMENT ON COLUMN flow_instance_biz_ext.tenant_id IS '租户编号';
COMMENT ON COLUMN flow_instance_biz_ext.create_dept IS '创建部门';
COMMENT ON COLUMN flow_instance_biz_ext.create_by IS '创建者';
COMMENT ON COLUMN flow_instance_biz_ext.create_time IS '创建时间';
COMMENT ON COLUMN flow_instance_biz_ext.update_by IS '更新者';
COMMENT ON COLUMN flow_instance_biz_ext.update_time IS '更新时间';
COMMENT ON COLUMN flow_instance_biz_ext.business_code IS '业务编码';
COMMENT ON COLUMN flow_instance_biz_ext.business_title IS '业务标题';
COMMENT ON COLUMN flow_instance_biz_ext.del_flag IS '删除标志0代表存在 1代表删除';
COMMENT ON COLUMN flow_instance_biz_ext.instance_id IS '流程实例Id';
COMMENT ON COLUMN flow_instance_biz_ext.business_id IS '业务Id';
ALTER TABLE test_leave ADD COLUMN apply_code VARCHAR2(50) NOT NULL;
COMMENT ON COLUMN test_leave.apply_code IS '申请编号';
update sys_menu set remark = '/tool/gen' where menu_id = 116;
update sys_menu set remark = '/system/role' where menu_id = 130;
update sys_menu set remark = '/system/user' where menu_id = 131;
update sys_menu set remark = '/system/dict' where menu_id = 132;
update sys_menu set remark = '/system/oss' where menu_id = 133;
update sys_menu set remark = '/workflow/processDefinition' where menu_id = 11700;

View File

@ -53,3 +53,47 @@ ALTER TABLE flow_his_task
ALTER COLUMN collaborator SET DEFAULT NULL,
ALTER COLUMN collaborator TYPE VARCHAR(500);
COMMENT ON COLUMN flow_his_task.collaborator IS '协作人';
-- ----------------------------
-- 流程实例业务扩展表
-- ----------------------------
CREATE TABLE flow_instance_biz_ext (
id int8,
tenant_id VARCHAR(20) DEFAULT '000000',
create_dept int8,
create_by int8,
create_time TIMESTAMP,
update_by int8,
update_time TIMESTAMP,
business_code VARCHAR(255),
business_title VARCHAR(1000),
del_flag CHAR(1) DEFAULT '0',
instance_id int8,
business_id VARCHAR(255),
PRIMARY KEY (id)
);
COMMENT ON TABLE flow_instance_biz_ext IS '流程实例业务扩展表';
COMMENT ON COLUMN flow_instance_biz_ext.id IS '主键id';
COMMENT ON COLUMN flow_instance_biz_ext.tenant_id IS '租户编号';
COMMENT ON COLUMN flow_instance_biz_ext.create_dept IS '创建部门';
COMMENT ON COLUMN flow_instance_biz_ext.create_by IS '创建者';
COMMENT ON COLUMN flow_instance_biz_ext.create_time IS '创建时间';
COMMENT ON COLUMN flow_instance_biz_ext.update_by IS '更新者';
COMMENT ON COLUMN flow_instance_biz_ext.update_time IS '更新时间';
COMMENT ON COLUMN flow_instance_biz_ext.business_code IS '业务编码';
COMMENT ON COLUMN flow_instance_biz_ext.business_title IS '业务标题';
COMMENT ON COLUMN flow_instance_biz_ext.del_flag IS '删除标志0代表存在 1代表删除';
COMMENT ON COLUMN flow_instance_biz_ext.instance_id IS '流程实例Id';
COMMENT ON COLUMN flow_instance_biz_ext.business_id IS '业务Id';
ALTER TABLE test_leave ADD COLUMN apply_code VARCHAR(50) NOT NULL;
COMMENT ON COLUMN test_leave.apply_code IS '申请编号';
update sys_menu set remark = '/tool/gen' where menu_id = 116;
update sys_menu set remark = '/system/role' where menu_id = 130;
update sys_menu set remark = '/system/user' where menu_id = 131;
update sys_menu set remark = '/system/dict' where menu_id = 132;
update sys_menu set remark = '/system/oss' where menu_id = 133;
update sys_menu set remark = '/workflow/processDefinition' where menu_id = 11700;

View File

@ -162,3 +162,133 @@ ELSE
'TABLE', N'flow_his_task',
'COLUMN', N'collaborator'
GO
CREATE TABLE flow_instance_biz_ext (
id BIGINT NOT NULL,
tenant_id VARCHAR(20) DEFAULT ('000000'),
create_dept BIGINT,
create_by BIGINT,
create_time DATETIME,
update_by BIGINT,
update_time DATETIME,
business_code VARCHAR(255),
business_title VARCHAR(1000),
del_flag CHAR(1) DEFAULT ('0'),
instance_id BIGINT,
business_id VARCHAR(255),
CONSTRAINT PK__fi_biz_ext__D54EE9B4AE98B9C1 PRIMARY KEY CLUSTERED (id)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
);
EXEC sp_addextendedproperty
'MS_Description', N'流程实例业务扩展表',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext'
GO
EXEC sp_addextendedproperty
'MS_Description', N'主键id',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'租户编号',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'tenant_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'创建部门',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'create_dept'
GO
EXEC sp_addextendedproperty
'MS_Description', N'创建者',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'create_by'
GO
EXEC sp_addextendedproperty
'MS_Description', N'创建时间',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'create_time'
GO
EXEC sp_addextendedproperty
'MS_Description', N'更新者',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'update_by'
GO
EXEC sp_addextendedproperty
'MS_Description', N'更新时间',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'update_time'
GO
EXEC sp_addextendedproperty
'MS_Description', N'删除标志',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'del_flag'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务编码',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'business_code'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务标题',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'business_title'
GO
EXEC sp_addextendedproperty
'MS_Description', N'流程实例Id',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'instance_id'
GO
EXEC sp_addextendedproperty
'MS_Description', N'业务Id',
'SCHEMA', N'dbo',
'TABLE', N'flow_instance_biz_ext',
'COLUMN', N'business_id'
GO
ALTER TABLE test_leave ADD apply_code nvarchar(50) NOT NULL;
GO
EXEC sp_addextendedproperty
'MS_Description', N'申请编号',
'SCHEMA', N'dbo',
'TABLE', N'test_leave',
'COLUMN', N'apply_code'
GO
update sys_menu set remark = N'/tool/gen' where menu_id = 116;
GO
update sys_menu set remark = N'/system/role' where menu_id = 130;
GO
update sys_menu set remark = N'/system/user' where menu_id = 131;
GO
update sys_menu set remark = N'/system/dict' where menu_id = 132;
GO
update sys_menu set remark = N'/system/oss' where menu_id = 133;
GO
update sys_menu set remark = N'/workflow/processDefinition' where menu_id = 11700;
GO

View File

@ -35,3 +35,33 @@ update flow_skip set skip_condition = REPLACE(skip_condition,'notNike','notLike'
ALTER TABLE `flow_his_task`
MODIFY COLUMN `collaborator` varchar(500) NULL DEFAULT NULL COMMENT '协作人' AFTER `cooperate_type`;
-- ----------------------------
-- 流程实例业务扩展表
-- ----------------------------
create table flow_instance_biz_ext (
id bigint not null comment '主键id',
tenant_id varchar(20) default '000000' null comment '租户编号',
create_dept bigint null comment '创建部门',
create_by bigint null comment '创建者',
create_time datetime null comment '创建时间',
update_by bigint null comment '更新者',
update_time datetime null comment '更新时间',
business_code varchar(255) null comment '业务编码',
business_title varchar(1000) null comment '业务标题',
del_flag char default '0' null comment '删除标志0代表存在 1代表删除',
instance_id bigint null comment '流程实例Id',
business_id varchar(255) null comment '业务Id',
PRIMARY KEY (id)
) ENGINE = InnoDB COMMENT '流程实例业务扩展表';
ALTER TABLE `test_leave`
ADD COLUMN `apply_code` varchar(50) NOT NULL COMMENT '申请编号' AFTER `tenant_id`;
update sys_menu set remark = '/tool/gen' where menu_id = 116;
update sys_menu set remark = '/system/role' where menu_id = 130;
update sys_menu set remark = '/system/user' where menu_id = 131;
update sys_menu set remark = '/system/dict' where menu_id = 132;
update sys_menu set remark = '/system/oss' where menu_id = 133;
update sys_menu set remark = '/workflow/processDefinition' where menu_id = 11700;