!812 发布 5.5.2 版本 2025年最后一版

Merge pull request !812 from 疯狂的狮子Li/dev
This commit is contained in:
疯狂的狮子Li
2025-12-23 01:38:19 +00:00
committed by Gitee
92 changed files with 1321 additions and 609 deletions

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br> <br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]() [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
@@ -39,6 +39,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br> Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong <br> aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong <br>
Ruoyi-Plus-Uniapp - https://ruoyi.plus <br> Ruoyi-Plus-Uniapp - https://ruoyi.plus <br>
Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/ <br>
[如何成为赞助商 加群联系作者详谈 每日PV2500-3000 IP1700-2500](https://plus-doc.dromara.org/#/common/add_group) [如何成为赞助商 加群联系作者详谈 每日PV2500-3000 IP1700-2500](https://plus-doc.dromara.org/#/common/add_group)

16
pom.xml
View File

@@ -13,13 +13,13 @@
<description>Dromara RuoYi-Vue-Plus多租户管理系统</description> <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
<properties> <properties>
<revision>5.5.1</revision> <revision>5.5.2</revision>
<spring-boot.version>3.5.7</spring-boot.version> <spring-boot.version>3.5.9</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version> <java.version>17</java.version>
<mybatis.version>3.5.16</mybatis.version> <mybatis.version>3.5.16</mybatis.version>
<springdoc.version>2.8.13</springdoc.version> <springdoc.version>2.8.14</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version> <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fastexcel.version>1.3.0</fastexcel.version> <fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
@@ -28,17 +28,17 @@
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.40</hutool.version> <hutool.version>5.8.40</hutool.version>
<spring-boot-admin.version>3.5.5</spring-boot-admin.version> <spring-boot-admin.version>3.5.5</spring-boot-admin.version>
<redisson.version>3.51.0</redisson.version> <redisson.version>3.52.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version> <lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version> <dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.8.0</snailjob.version> <snailjob.version>1.9.0</snailjob.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version> <mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.40</lombok.version> <lombok.version>1.18.40</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version> <bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version> <justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 --> <!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version> <ip2region.version>3.3.1</ip2region.version>
<!-- OSS 配置 --> <!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version> <aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 --> <!-- SMS 配置 -->
@@ -46,9 +46,9 @@
<!-- 限制框架中的fastjson版本 --> <!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version> <fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 --> <!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250603</anyline.version> <anyline.version>8.7.3-20251210</anyline.version>
<!-- 工作流配置 --> <!-- 工作流配置 -->
<warm-flow.version>1.8.2</warm-flow.version> <warm-flow.version>1.8.4</warm-flow.version>
<!-- 插件版本 --> <!-- 插件版本 -->
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>

View File

@@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.*;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -106,7 +107,7 @@ public class AuthController {
Long userId = LoginHelper.getUserId(); Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> { scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto(); SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统"); dto.setMessage(DateUtils.getTodayHour(new Date()) + "好,欢迎登录 RuoYi-Vue-Plus 后台管理系统");
dto.setUserIds(List.of(userId)); dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto); SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS); }, 5, TimeUnit.SECONDS);
@@ -147,8 +148,8 @@ public class AuthController {
StpUtil.checkLogin(); StpUtil.checkLogin();
// 获取第三方登录信息 // 获取第三方登录信息
AuthResponse<AuthUser> response = SocialUtils.loginAuth( AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(), loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties); loginBody.getSocialState(), socialProperties);
AuthUser authUserData = response.getData(); AuthUser authUserData = response.getData();
// 判断授权响应是否成功 // 判断授权响应是否成功
if (!response.ok()) { if (!response.ok()) {

View File

@@ -14,7 +14,7 @@
</description> </description>
<properties> <properties>
<revision>5.5.1</revision> <revision>5.5.2</revision>
</properties> </properties>
<dependencyManagement> <dependencyManagement>

View File

@@ -3,10 +3,8 @@ package org.dromara.common.core.config;
import jakarta.annotation.PreDestroy; import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.dromara.common.core.config.properties.ThreadPoolProperties;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.core.task.VirtualThreadTaskExecutor;
@@ -19,7 +17,6 @@ import java.util.concurrent.*;
**/ **/
@Slf4j @Slf4j
@AutoConfiguration @AutoConfiguration
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig { public class ThreadPoolConfig {
/** /**

View File

@@ -1,30 +0,0 @@
package org.dromara.common.core.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 线程池 配置属性
*
* @author Lion Li
*/
@Data
@ConfigurationProperties(prefix = "thread-pool")
public class ThreadPoolProperties {
/**
* 是否开启线程池
*/
private boolean enabled;
/**
* 队列最大长度
*/
private int queueCapacity;
/**
* 线程池维护线程所允许的空闲时间
*/
private int keepAliveSeconds;
}

View File

@@ -82,4 +82,10 @@ public interface SystemConstants {
*/ */
Long DEFAULT_DEPT_ID = 100L; Long DEFAULT_DEPT_ID = 100L;
/**
* 排除敏感属性字段
*/
String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
} }

View File

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

View File

@@ -1,5 +1,11 @@
package org.dromara.common.core.service; package org.dromara.common.core.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import java.math.BigDecimal;
import java.util.List;
/** /**
* 通用 参数配置服务 * 通用 参数配置服务
* *
@@ -15,4 +21,80 @@ public interface ConfigService {
*/ */
String getConfigValue(String configKey); String getConfigValue(String configKey);
/**
* 根据参数 key 获取布尔值
*
* @param configKey 参数 key
* @return Boolean 值
*/
default Boolean getConfigBool(String configKey) {
return Convert.toBool(getConfigValue(configKey));
}
/**
* 根据参数 key 获取整数值
*
* @param configKey 参数 key
* @return Integer 值
*/
default Integer getConfigInt(String configKey) {
return Convert.toInt(getConfigValue(configKey));
}
/**
* 根据参数 key 获取长整型值
*
* @param configKey 参数 key
* @return Long 值
*/
default Long getConfigLong(String configKey) {
return Convert.toLong(getConfigValue(configKey));
}
/**
* 根据参数 key 获取 BigDecimal 值
*
* @param configKey 参数 key
* @return BigDecimal 值
*/
default BigDecimal getConfigDecimal(String configKey) {
return Convert.toBigDecimal(getConfigValue(configKey));
}
/**
* 根据参数 key 获取 Map 类型的配置
*
* @param configKey 参数 key
* @return Dict 对象,如果配置为空或无法解析,返回空 Dict
*/
Dict getConfigMap(String configKey);
/**
* 根据参数 key 获取 Map 类型的配置列表
*
* @param configKey 参数 key
* @return Dict 列表,如果配置为空或无法解析,返回空列表
*/
List<Dict> getConfigArrayMap(String configKey);
/**
* 根据参数 key 获取指定类型的配置对象
*
* @param configKey 参数 key
* @param clazz 目标对象类型
* @param <T> 目标对象泛型
* @return 对象实例,如果配置为空或无法解析,返回 null
*/
<T> T getConfigObject(String configKey, Class<T> clazz);
/**
* 根据参数 key 获取指定类型的配置列表
*
* @param configKey 参数 key
* @param clazz 目标元素类型
* @param <T> 元素类型泛型
* @return 指定类型列表,如果配置为空或无法解析,返回空列表
*/
<T> List<T> getConfigArray(String configKey, Class<T> clazz);
} }

View File

@@ -20,7 +20,7 @@ public interface WorkflowService {
* @param businessIds 业务id * @param businessIds 业务id
* @return 结果 * @return 结果
*/ */
boolean deleteInstance(List<Long> businessIds); boolean deleteInstance(List<String> businessIds);
/** /**
* 获取当前流程状态 * 获取当前流程状态

View File

@@ -1,5 +1,7 @@
package org.dromara.common.core.utils; package org.dromara.common.core.utils;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.DateFormatUtils;
import org.dromara.common.core.enums.FormatsType; import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
@@ -297,4 +299,80 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
} }
} }
/**
* 根据指定日期时间获取时间段(凌晨 / 上午 / 中午 / 下午 / 晚上)
*
* @param date 日期时间
* @return 时间段描述
*/
public static String getTodayHour(Date date) {
int hour = DateUtil.hour(date, true);
if (hour <= 6) {
return "凌晨";
} else if (hour < 12) {
return "上午";
} else if (hour == 12) {
return "中午";
} else if (hour <= 18) {
return "下午";
} else {
return "晚上";
}
}
/**
* 将日期格式化为仿微信的友好时间
* <p>
* 规则说明:
* 1. 未来时间yyyy-MM-dd HH:mm
* 2. 今天:
* - 1 分钟内:刚刚
* - 1 小时内X 分钟前
* - 超过 1 小时:凌晨/上午/中午/下午/晚上 HH:mm
* 3. 昨天:昨天 HH:mm
* 4. 本周周X HH:mm
* 5. 今年内MM-dd HH:mm
* 6. 非今年yyyy-MM-dd HH:mm
*
* @param date 日期时间
* @return 格式化后的时间描述
*/
public static String formatFriendlyTime(Date date) {
if (date == null) {
return "";
}
Date now = DateUtil.date();
// 未来时间或非今年
if (date.after(now) || DateUtil.year(date) != DateUtil.year(now)) {
return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM, date);
}
// 今天
if (DateUtil.isSameDay(date, now)) {
long minutes = DateUtil.between(date, now, DateUnit.MINUTE);
if (minutes < 1) {
return "刚刚";
}
if (minutes < 60) {
return minutes + "分钟前";
}
return getTodayHour(date) + " " + DateUtil.format(date, "HH:mm");
}
// 昨天
if (DateUtil.isSameDay(date, DateUtil.yesterday())) {
return "昨天 " + DateUtil.format(date, "HH:mm");
}
// 本周
if (DateUtil.isSameWeek(date, now, true)) {
return DateUtil.dayOfWeekEnum(date).toChinese("")
+ " " + DateUtil.format(date, "HH:mm");
}
// 今年内其它时间
return DateUtil.format(date, "MM-dd HH:mm");
}
} }

View File

@@ -0,0 +1,87 @@
package org.dromara.common.core.utils;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* 脱敏工具类
*
* @author AprilWind
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DesensitizedUtils extends DesensitizedUtil {
/**
* 灵活脱敏方法
*
* @param value 原始字符串
* @param prefixVisible 前面可见长度
* @param suffixVisible 后面可见长度
* @param maskLength 中间掩码长度(固定显示多少 *,如果总长度不足则自动缩减)
* @return 脱敏后字符串
*/
public static String mask(String value, int prefixVisible, int suffixVisible, int maskLength) {
if (StrUtil.isBlank(value)) {
return value;
}
int len = value.length();
int prefixMaskLimit = prefixVisible + maskLength;
int fullLimit = prefixMaskLimit + suffixVisible;
// 规则 1长度 <= 中间掩码长度 → 全掩码
if (len <= maskLength) {
return StrUtil.repeat('*', len);
}
String mask = StrUtil.repeat('*', maskLength);
// 规则 2长度 <= 前缀 + 中间掩码
if (len <= prefixMaskLimit) {
return value.substring(0, len - maskLength) + mask;
}
String prefix = value.substring(0, prefixVisible);
// 规则 3长度 <= 前缀 + 中间掩码 + 后缀
if (len <= fullLimit) {
int suffixLen = len - prefixMaskLimit;
return prefix + mask + value.substring(len - suffixLen);
}
// 规则 4标准形态
return prefix + mask + value.substring(len - suffixVisible);
}
/**
* 高安全级别脱敏方法Token / 私钥)
*
* @param value 原始字符串
* @param prefixVisible 前面可见长度推荐0~4
* @param suffixVisible 后面可见长度推荐0~4
* @return 脱敏后字符串
*/
public static String maskHighSecurity(String value, int prefixVisible, int suffixVisible) {
if (StrUtil.isBlank(value)) {
return value;
}
int len = value.length();
// 规则1长度 <= 前缀可见长度 → 全部掩码
if (len <= prefixVisible) {
return StrUtil.repeat('*', len);
}
// 规则2长度 <= 前缀 + 后缀可见长度 → 优先掩码后面
if (len <= prefixVisible + suffixVisible) {
return value.substring(0, len - prefixVisible) + StrUtil.repeat('*', prefixVisible);
}
// 规则3标准形态 → 前后可见,中间全部掩码
return value.substring(0, prefixVisible)
+ StrUtil.repeat('*', len - prefixVisible - suffixVisible)
+ value.substring(len - suffixVisible);
}
}

View File

@@ -20,51 +20,24 @@ public class AddressUtils {
public static final String UNKNOWN_IP = "XX XX"; public static final String UNKNOWN_IP = "XX XX";
// 内网地址 // 内网地址
public static final String LOCAL_ADDRESS = "内网IP"; public static final String LOCAL_ADDRESS = "内网IP";
// 未知地址
public static final String UNKNOWN_ADDRESS = "未知";
public static String getRealAddressByIP(String ip) { public static String getRealAddressByIP(String ip) {
// 处理空串并过滤HTML标签 // 处理空串并过滤HTML标签
ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,""));
// 判断是否为IPv4 // 判断是否为IPv4
if (NetUtils.isIPv4(ip)) { boolean isIPv4 = NetUtils.isIPv4(ip);
return resolverIPv4Region(ip);
}
// 判断是否为IPv6 // 判断是否为IPv6
if (NetUtils.isIPv6(ip)) { boolean isIPv6 = NetUtils.isIPv6(ip);
return resolverIPv6Region(ip);
}
// 如果不是IPv4或IPv6则返回未知IP // 如果不是IPv4或IPv6则返回未知IP
return UNKNOWN_IP; if (!isIPv4 && !isIPv6) {
} return UNKNOWN_IP;
}
/**
* 根据IPv4地址查询IP归属行政区域
* @param ip ipv4地址
* @return 归属行政区域
*/
private static String resolverIPv4Region(String ip){
// 内网不查询 // 内网不查询
if (NetUtils.isInnerIP(ip)) { if ((isIPv4 && NetUtils.isInnerIP(ip)) || (isIPv6 && NetUtils.isInnerIPv6(ip))) {
return LOCAL_ADDRESS; return LOCAL_ADDRESS;
} }
return RegionUtils.getCityInfo(ip); // TipsIp2Region 提供了精简的IPv6地址库精简的IPv6地址库并不能完全支持IPv6地址的查询且准确度上可能会存在问题如需要准确的IPv6地址查询建议自行实现
} return RegionUtils.getRegion(ip);
/**
* 根据IPv6地址查询IP归属行政区域
* @param ip ipv6地址
* @return 归属行政区域
*/
private static String resolverIPv6Region(String ip){
// 内网不查询
if (NetUtils.isInnerIPv6(ip)) {
return LOCAL_ADDRESS;
}
log.warn("ip2region不支持IPV6地址解析{}", ip);
// 不支持IPv6不再进行没有必要的IP地址信息的解析直接返回
// 如有需要可自行实现IPv6地址信息解析逻辑并在这里返回
return UNKNOWN_ADDRESS;
} }
} }

View File

@@ -1,50 +1,142 @@
package org.dromara.common.core.utils.ip; package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.resource.NoResourceException;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher; import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.lionsoul.ip2region.xdb.Util;
import java.io.InputStream;
import java.time.Duration;
/** /**
* 根据ip地址定位工具类离线方式 * IP地址行政区域工具类
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a> * 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">ip2region xdb java 查询客户端实现</a>
* xdb数据库文件下载<a href="https://gitee.com/lionsoul/ip2region/tree/master/data">ip2region data</a>
* *
* @author lishuyan * @author 秋辞未寒
*/ */
@Slf4j @Slf4j
public class RegionUtils { public class RegionUtils {
// IP地址库文件名称 // 默认IPv4地址库文件路径
public static final String IP_XDB_FILENAME = "ip2region.xdb"; // 下载地址https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v4.xdb
public static final String DEFAULT_IPV4_XDB_PATH = "ip2region_v4.xdb";
private static final Searcher SEARCHER; // 默认IPv6地址库文件路径
// 下载地址https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb
public static final String DEFAULT_IPV6_XDB_PATH = "ip2region_v6.xdb";
// 未知地址
public static final String UNKNOWN_ADDRESS = "未知";
// Ip2Region服务实例
private static Ip2Region ip2Region;
// 初始化Ip2Region服务实例
static { static {
try { try {
// 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。 // 注意Ip2Region 的xdb文件加载策略 CachePolicy 有三种分别是BufferCache全量读取xdb到内存中、VIndexCache默认策略按需读取并缓存、NoCache实时读取
// 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象 // 本项目工具使用的 CachePolicy 为 BufferCacheBufferCache会加载整个xdb文件到内存中setXdbInputStream 仅支持 BufferCache 策略
SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME)); // 因为加载整个xdb文件会耗费非常大的内存如果你不希望加载整个xdb到内存中更推荐使用 VIndexCache 或 NoCache即实时读取文件策略和 setXdbPath/setXdbFile 加载方法需要注意的一点setXdbPath 和 setXdbFile 不支持读取ClassPath即源码和resource目录中的文件
log.info("RegionUtils初始化成功加载IP地址库数据成功"); // 一般而言更建议把xdb数据库放到一个指定的文件目录中即不打包进jar包中然后使用 NoCache + 配合SearcherPool的并发池读取数据更方便随时更新xdb数据库
} catch (NoResourceException e) {
throw new ServiceException("RegionUtils初始化失败原因IP地址库数据不存在"); // IPv4配置
Config v4Config = Config.custom()
.setCachePolicy(Config.BufferCache)
.setXdbInputStream(ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH))
.asV4();
// IPv6配置
Config v6Config = null;
InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH);
if (v6XdbInputStream == null) {
log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6请将 ip2region_v6.xdb 放置到 resources 目录", DEFAULT_IPV6_XDB_PATH);
} else {
v6Config = Config.custom()
.setCachePolicy(Config.BufferCache)
.setXdbInputStream(v6XdbInputStream)
.asV6();
}
// 初始化Ip2Region实例
RegionUtils.ip2Region = Ip2Region.create(v4Config, v6Config);
log.debug("IP工具初始化成功加载IP地址库数据成功");
} catch (Exception e) { } catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败原因" + e.getMessage()); throw new ServiceException("RegionUtils初始化失败原因{}", e.getMessage());
} }
} }
/** /**
* 根据IP地址离线获取城市 * 根据IP地址离线获取城市
*
* @param ipString ip地址字符串
*/ */
public static String getCityInfo(String ip) { public static String getRegion(String ipString) {
try { try {
// 3、执行查询 String region = ip2Region.search(ipString);
String region = SEARCHER.search(StringUtils.trim(ip)); if (StringUtils.isBlank(region)) {
return region.replace("0|", "").replace("|0", ""); region = UNKNOWN_ADDRESS;
}
return region;
} catch (Exception e) { } catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", ip); log.error("IP地址离线获取城市异常 {}", ipString);
return "未知"; return UNKNOWN_ADDRESS;
}
}
/**
* 根据IP地址离线获取城市
*
* @param ipBytes ip地址字节数组
*/
public static String getRegion(byte[] ipBytes) {
try {
String region = ip2Region.search(ipBytes);
if (StringUtils.isBlank(region)) {
region = UNKNOWN_ADDRESS;
}
return region;
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
return UNKNOWN_ADDRESS;
}
}
/**
* 关闭Ip2Region服务
*/
public static void close() {
if (ip2Region == null) {
return;
}
try {
ip2Region.close(10000);
} catch (Exception e) {
log.error("Ip2Region服务关闭异常", e);
}
}
/**
* 关闭Ip2Region服务
*
* @param timeout 关闭超时时间
*/
public static void close(final Duration timeout) {
if (ip2Region == null) {
return;
}
if (timeout == null) {
close();
return;
}
try {
ip2Region.close(timeout.toMillis());
} catch (Exception e) {
log.error("Ip2Region服务关闭异常", e);
} }
} }

View File

@@ -0,0 +1,23 @@
package org.dromara.common.excel.annotation;
import org.dromara.common.excel.core.ExcelOptionsProvider;
import java.lang.annotation.*;
/**
* Excel动态下拉选项注解
*
* @author Angus
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDynamicOptions {
/**
* 提供者类全限定名
* <p>
* {@link org.dromara.common.excel.core.ExcelOptionsProvider} 接口实现类 class
*/
Class<? extends ExcelOptionsProvider> providerClass();
}

View File

@@ -29,7 +29,10 @@ public class CellMergeHandler {
// 行合并开始下标 // 行合并开始下标
this.rowIndex = hasTitle ? 1 : 0; this.rowIndex = hasTitle ? 1 : 0;
} }
private CellMergeHandler(final boolean hasTitle, final int rowIndex) {
this.hasTitle = hasTitle;
this.rowIndex = hasTitle ? rowIndex : 0;
}
@SneakyThrows @SneakyThrows
public List<CellRangeAddress> handle(List<?> rows) { public List<CellRangeAddress> handle(List<?> rows) {
// 如果入参为空集合则返回空集 // 如果入参为空集合则返回空集
@@ -103,6 +106,10 @@ public class CellMergeHandler {
} }
if (isAddResult && i > current) { if (isAddResult && i > current) {
//如果是同一行,则跳过合并
if (current + rowIndex == lastRow) {
continue;
}
result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum)); result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
} }
} }
@@ -147,12 +154,12 @@ public class CellMergeHandler {
private boolean isMerge(Object currentRow, Object preRow, CellMerge cellMerge) { private boolean isMerge(Object currentRow, Object preRow, CellMerge cellMerge) {
final String[] mergeBy = cellMerge.mergeBy(); final String[] mergeBy = cellMerge.mergeBy();
if (StrUtil.isAllNotBlank(mergeBy)) { if (StrUtil.isAllNotBlank(mergeBy)) {
//比对当前行和上一行的各个属性值一一比对 如果全为真 则为真 // 比对当前行和上一行的各个属性值一一比对 如果全为真 则为真
for (String fieldName : mergeBy) { for (String fieldName : mergeBy) {
final Object valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName); final Object valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName);
final Object valPre = ReflectUtil.getFieldValue(preRow, fieldName); final Object valPre = ReflectUtil.getFieldValue(preRow, fieldName);
if (!Objects.equals(valPre, valCurrent)) { if (!Objects.equals(valPre, valCurrent)) {
//依赖字段如有任一不等值,则标记为不可合并 // 依赖字段如有任一不等值,则标记为不可合并
return false; return false;
} }
} }
@@ -177,6 +184,16 @@ public class CellMergeHandler {
return new FieldColumnIndex(colIndex, cellMerge); return new FieldColumnIndex(colIndex, cellMerge);
} }
} }
/**
* 创建一个单元格合并处理器实例
*
* @param hasTitle 是否合并标题
* @param rowIndex 行索引
* @return 单元格合并处理器
*/
public static CellMergeHandler of(final boolean hasTitle, final int rowIndex) {
return new CellMergeHandler(hasTitle, rowIndex);
}
/** /**
* 创建一个单元格合并处理器实例 * 创建一个单元格合并处理器实例

View File

@@ -2,15 +2,16 @@ package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.idev.excel.metadata.Head; import cn.idev.excel.metadata.Head;
import cn.idev.excel.write.handler.WorkbookWriteHandler; import cn.idev.excel.write.handler.SheetWriteHandler;
import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext;
import cn.idev.excel.write.merge.AbstractMergeStrategy; import cn.idev.excel.write.merge.AbstractMergeStrategy;
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import java.util.*; import java.util.List;
/** /**
* 列值重复合并策略 * 列值重复合并策略
@@ -18,7 +19,7 @@ import java.util.*;
* @author Lion Li * @author Lion Li
*/ */
@Slf4j @Slf4j
public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler { public class CellMergeStrategy extends AbstractMergeStrategy implements SheetWriteHandler {
private final List<CellRangeAddress> cellList; private final List<CellRangeAddress> cellList;
@@ -30,29 +31,34 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook
this.cellList = CellMergeHandler.of(hasTitle).handle(list); this.cellList = CellMergeHandler.of(hasTitle).handle(list);
} }
public CellMergeStrategy(List<?> list, boolean hasTitle, int rowIndex) {
this.cellList = CellMergeHandler.of(hasTitle, rowIndex).handle(list);
}
@Override @Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (CollUtil.isEmpty(cellList)){ if (CollUtil.isEmpty(cellList)) {
return; return;
} }
//单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空 // 单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空
final int rowIndex = cell.getRowIndex(); final int rowIndex = cell.getRowIndex();
for (CellRangeAddress cellAddresses : cellList) { for (CellRangeAddress cellAddresses : cellList) {
final int firstRow = cellAddresses.getFirstRow(); final int firstRow = cellAddresses.getFirstRow();
if (cellAddresses.isInRange(cell) && rowIndex != firstRow){ if (cellAddresses.isInRange(cell) && rowIndex != firstRow) {
cell.setBlank(); cell.setBlank();
} }
} }
} }
@Override @Override
public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) { public void afterSheetCreate(final WriteWorkbookHolder writeWorkbookHolder, final WriteSheetHolder writeSheetHolder) {
if (CollUtil.isEmpty(cellList)){ if (CollUtil.isEmpty(cellList)) {
return; return;
} }
//当前表格写完后,统一写入 // 在 Sheet 创建时提前写入合并区域;后续写入只会影响首格,不会移除合并
final Sheet sheet = writeSheetHolder.getSheet();
for (CellRangeAddress item : cellList) { for (CellRangeAddress item : cellList) {
context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item); sheet.addMergedRegion(item);
} }
} }

View File

@@ -23,6 +23,7 @@ import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.annotation.ExcelDynamicOptions;
import org.dromara.common.excel.annotation.ExcelEnumFormat; import org.dromara.common.excel.annotation.ExcelEnumFormat;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -117,6 +118,15 @@ public class ExcelDownHandler implements SheetWriteHandler {
ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class); ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class);
List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField()); List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField());
options = StreamUtils.toList(values, Convert::toStr); options = StreamUtils.toList(values, Convert::toStr);
} else if (field.isAnnotationPresent(ExcelDynamicOptions.class)) {
// 处理动态下拉选项
ExcelDynamicOptions dynamicOptions = field.getDeclaredAnnotation(ExcelDynamicOptions.class);
// 获取提供者实例
ExcelOptionsProvider provider = SpringUtils.getBean(dynamicOptions.providerClass());
Set<String> providerOptions = provider.getOptions();
if (CollUtil.isNotEmpty(providerOptions)) {
options = new ArrayList<>(providerOptions);
}
} }
if (ObjectUtil.isNotEmpty(options)) { if (ObjectUtil.isNotEmpty(options)) {
// 仅当下拉可选项不为空时执行 // 仅当下拉可选项不为空时执行

View File

@@ -0,0 +1,19 @@
package org.dromara.common.excel.core;
import java.util.Set;
/**
* Excel下拉选项数据提供接口
*
* @author Angus
*/
public interface ExcelOptionsProvider {
/**
* 获取下拉选项数据
*
* @return 下拉选项列表
*/
Set<String> getOptions();
}

View File

@@ -13,6 +13,7 @@ import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
@@ -39,12 +40,6 @@ import java.util.*;
@AutoConfiguration @AutoConfiguration
public class LogAspect { public class LogAspect {
/**
* 排除敏感属性字段
*/
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/** /**
* 计时 key * 计时 key
*/ */
@@ -160,7 +155,7 @@ public class LogAspect {
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 3800)); operLog.setOperParam(StringUtils.substring(params, 0, 3800));
} else { } else {
MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); MapUtil.removeAny(paramsMap, SystemConstants.EXCLUDE_PROPERTIES);
MapUtil.removeAny(paramsMap, excludeParamNames); MapUtil.removeAny(paramsMap, excludeParamNames);
operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800)); operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800));
} }
@@ -174,7 +169,7 @@ public class LogAspect {
if (ArrayUtil.isEmpty(paramsArray)) { if (ArrayUtil.isEmpty(paramsArray)) {
return params.toString(); return params.toString();
} }
String[] exclude = ArrayUtil.addAll(excludeParamNames, EXCLUDE_PROPERTIES); String[] exclude = ArrayUtil.addAll(excludeParamNames, SystemConstants.EXCLUDE_PROPERTIES);
for (Object o : paramsArray) { for (Object o : paramsArray) {
if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
String str = ""; String str = "";

View File

@@ -42,7 +42,7 @@ public class DataBaseHelper {
String databaseProductName = metaData.getDatabaseProductName(); String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName); return DataBaseType.find(databaseProductName);
} catch (SQLException e) { } catch (SQLException e) {
throw new ServiceException(e.getMessage()); throw new RuntimeException("获取数据库类型失败", e);
} }
} }

View File

@@ -112,7 +112,7 @@ public class DataPermissionHelper {
/** /**
* 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
*/ */
public static void enableIgnore() { private static void enableIgnore() {
IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); IgnoreStrategy ignoreStrategy = getIgnoreStrategy();
if (ObjectUtil.isNull(ignoreStrategy)) { if (ObjectUtil.isNull(ignoreStrategy)) {
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
@@ -126,7 +126,7 @@ public class DataPermissionHelper {
/** /**
* 关闭忽略数据权限 * 关闭忽略数据权限
*/ */
public static void disableIgnore() { private static void disableIgnore() {
IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); IgnoreStrategy ignoreStrategy = getIgnoreStrategy();
if (ObjectUtil.isNotNull(ignoreStrategy)) { if (ObjectUtil.isNotNull(ignoreStrategy)) {
boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName())

View File

@@ -0,0 +1,129 @@
package org.dromara.common.mybatis.utils;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
/**
* ID 生成工具类
*
* @author AprilWind
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class IdGeneratorUtil {
private static final IdentifierGenerator GENERATOR = SpringUtils.getBean(IdentifierGenerator.class);
/**
* 生成字符串类型主键 ID
* <p>
* 调用 {@link IdentifierGenerator#nextId(Object)},返回 String 格式 ID。
* </p>
*
* @return 字符串格式主键 ID
*/
public static String nextId() {
return GENERATOR.nextId(null).toString();
}
/**
* 生成 Long 类型主键 ID
* <p>
* 自动将生成的数字型主键转换为 Long 类型
* </p>
*
* @return Long 类型主键 ID
*/
public static Long nextLongId() {
return GENERATOR.nextId(null).longValue();
}
/**
* 生成 Number 类型主键 ID
* <p>
* 推荐在需要保留原始 Number 类型时使用
* </p>
*
* @return Number 类型主键 ID
*/
public static Number nextNumberId() {
return GENERATOR.nextId(null);
}
/**
* 根据实体生成数字型主键 ID
* <p>
* 若自定义的 {@link IdentifierGenerator} 根据实体内容生成 ID则可以使用本方法
* </p>
*
* @param entity 实体对象
* @return Number 类型主键 ID
*/
public static Number nextId(Object entity) {
return GENERATOR.nextId(entity);
}
/**
* 根据实体生成字符串主键 ID
* <p>
* 与 {@link #nextId(Object)} 类似,但返回 String 类型
* </p>
*
* @param entity 实体对象
* @return 字符串格式主键 ID
*/
public static String nextStringId(Object entity) {
return GENERATOR.nextId(entity).toString();
}
/**
* 生成 32 位 UUID
* <p>
* 底层使用 {@link IdWorker#get32UUID()}
* </p>
*
* @return 32 位 UUID 字符串
*/
public static String nextUUID() {
return IdWorker.get32UUID();
}
/**
* 根据实体生成 32 位 UUID
* <p>
* 默认 {@link IdentifierGenerator#nextUUID(Object)} 实现忽略实体,但保留该方法便于扩展。
* </p>
*
* @param entity 实体对象
* @return 32 位 UUID 字符串
*/
public static String nextUUID(Object entity) {
return GENERATOR.nextUUID(entity);
}
/**
* 生成带指定前缀的字符串主键 ID
* <p>
* 示例prefix = "ORD",生成结果形如:{@code ORD20251211000123}
* </p>
*
* @param prefix 自定义前缀
* @return 带前缀的字符串主键 ID
*/
public static String nextIdWithPrefix(String prefix) {
return prefix + nextId();
}
/**
* 生成带前缀的 UUID
*
* @param prefix 前缀
* @return prefix + UUID
*/
public static String nextUUIDWithPrefix(String prefix) {
return prefix + nextUUID();
}
}

View File

@@ -14,7 +14,9 @@ import org.dromara.common.oss.exception.OssException;
import org.dromara.common.oss.properties.OssProperties; import org.dromara.common.oss.properties.OssProperties;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.async.*; import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody;
import software.amazon.awssdk.core.async.ResponsePublisher;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3AsyncClient;
@@ -33,6 +35,7 @@ import java.nio.channels.WritableByteChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Duration; import java.time.Duration;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -94,7 +97,11 @@ public class OssClient {
.region(of()) .region(of())
.forcePathStyle(isStyle) .forcePathStyle(isStyle)
.httpClient(NettyNioAsyncHttpClient.builder() .httpClient(NettyNioAsyncHttpClient.builder()
.connectionTimeout(Duration.ofSeconds(60)).build()) .connectionTimeout(Duration.ofSeconds(60))
.connectionAcquisitionTimeout(Duration.ofSeconds(30))
.maxConcurrency(100)
.maxPendingConnectionAcquires(1000)
.build())
.build(); .build();
//AWS基于 CRT 的 S3 AsyncClient 实例用作 S3 传输管理器的底层客户端 //AWS基于 CRT 的 S3 AsyncClient 实例用作 S3 传输管理器的底层客户端
@@ -317,13 +324,13 @@ public class OssClient {
} }
/** /**
* 获取私有URL链接 * 创建下载请求的预签名URL
* *
* @param objectKey 对象KEY * @param objectKey 对象KEY
* @param expiredTime 链接授权到期时间 * @param expiredTime 链接授权到期时间
*/ */
public String getPrivateUrl(String objectKey, Duration expiredTime) { public String createPresignedGetUrl(String objectKey, Duration expiredTime) {
// 使用 AWS S3 预签名 URL 的生成器 获取对象的预签名 URL // 使用 AWS S3 预签名 URL 的生成器 获取下载对象的预签名 URL
URL url = presigner.presignGetObject( URL url = presigner.presignGetObject(
x -> x.signatureDuration(expiredTime) x -> x.signatureDuration(expiredTime)
.getObjectRequest( .getObjectRequest(
@@ -332,7 +339,28 @@ public class OssClient {
.build()) .build())
.build()) .build())
.url(); .url();
return url.toString(); return url.toExternalForm();
}
/**
* 创建上传请求的预签名URL
*
* @param objectKey 对象KEY
* @param expiredTime 链接授权到期时间
* @param metadata 元数据
*/
public String createPresignedPutUrl(String objectKey, Duration expiredTime, Map<String, String> metadata) {
// 使用 AWS S3 预签名 URL 的生成器 获取上传文件对象的预签名 URL
URL url = presigner.presignPutObject(
x -> x.signatureDuration(expiredTime)
.putObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(objectKey)
.metadata(metadata)
.build())
.build())
.url();
return url.toExternalForm();
} }
/** /**

View File

@@ -43,16 +43,12 @@
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
</dependency> </dependency>
<!-- &lt;!&ndash; redis序列化替代方案 比json快无数的跨语言二进制序列化 &ndash;&gt;--> <!-- redis序列化替代方案 比json快无数的跨语言二进制序列化 -->
<!-- <dependency>--> <dependency>
<!-- <groupId>org.apache.fury</groupId>--> <groupId>org.apache.fory</groupId>
<!-- <artifactId>fury-core</artifactId>--> <artifactId>fory-core</artifactId>
<!-- <version>0.9.0</version>--> <version>0.13.1</version>
<!-- </dependency>--> </dependency>
<!-- <dependency>-->
<!-- <groupId>org.slf4j</groupId>-->
<!-- <artifactId>slf4j-api</artifactId>-->
<!-- </dependency>-->
</dependencies> </dependencies>

View File

@@ -53,9 +53,10 @@ public class RedisConfig {
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型类必须是非final修饰的。序列化时将对象全类名一起保存下来 // 指定序列化输入的类型类必须是非final修饰的。序列化时将对象全类名一起保存下来
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// LoggerFactory.useSlf4jLogging(true); // org.apache.fory.logging.LoggerFactory 包别引入错了
// FuryCodec furyCodec = new FuryCodec(); // LoggerFactory.useSlf4jLogging(true);
// CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, furyCodec, furyCodec); // ForyCodec foryCodec = new ForyCodec();
// CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, foryCodec, foryCodec);
TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om); TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
// 组合序列化 key 使用 String 内容使用通用 json 格式 // 组合序列化 key 使用 String 内容使用通用 json 格式
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec); CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);

View File

@@ -3,6 +3,7 @@ package org.dromara.common.sensitive.core;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.DesensitizedUtil; import cn.hutool.core.util.DesensitizedUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.dromara.common.core.utils.DesensitizedUtils;
import java.util.function.Function; import java.util.function.Function;
@@ -80,6 +81,18 @@ public enum SensitiveStrategy {
*/ */
FIRST_MASK(DesensitizedUtil::firstMask), FIRST_MASK(DesensitizedUtil::firstMask),
/**
* 通用字符串脱敏
* 可配置前后可见长度和中间掩码长度
* 默认示例前4位可见后4位可见中间固定4个*
*/
STRING_MASK(s -> DesensitizedUtils.mask(s, 4, 4, 4)),
/**
* 高安全级别脱敏Token / 私钥前2位可见后2位可见中间全部掩码
*/
MASK_HIGH_SECURITY(s -> DesensitizedUtils.maskHighSecurity(s, 2, 2)),
/** /**
* 清空为"" * 清空为""
*/ */

View File

@@ -28,9 +28,14 @@ public class SocialLoginConfigProperties {
private String redirectUri; private String redirectUri;
/** /**
* 是否获取unionId * 是否需要申请unionid目前只针对qq登录
*/ */
private boolean unionId; private Boolean unionId;
/**
* Microsoft Entra ID原微软 AAD中的租户 ID
*/
private String tenantId;
/** /**
* Coding 企业名称 * Coding 企业名称

View File

@@ -57,7 +57,7 @@ public class SocialUtils {
case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE); case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE);
case "douyin" -> new AuthDouyinRequest(builder.build(), STATE_CACHE); case "douyin" -> new AuthDouyinRequest(builder.build(), STATE_CACHE);
case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE); case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE);
case "microsoft" -> new AuthMicrosoftRequest(builder.build(), STATE_CACHE); case "microsoft" -> new AuthMicrosoftRequest(builder.tenantId(obj.getTenantId()).build(), STATE_CACHE);
case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE); case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE);
case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE); case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE);
case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE); case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE);

View File

@@ -55,7 +55,7 @@ public class TenantHelper {
/** /**
* 开启忽略租户(开启后需手动调用 {@link #disableIgnore()} 关闭) * 开启忽略租户(开启后需手动调用 {@link #disableIgnore()} 关闭)
*/ */
public static void enableIgnore() { private static void enableIgnore() {
IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); IgnoreStrategy ignoreStrategy = getIgnoreStrategy();
if (ObjectUtil.isNull(ignoreStrategy)) { if (ObjectUtil.isNull(ignoreStrategy)) {
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build()); InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
@@ -69,7 +69,7 @@ public class TenantHelper {
/** /**
* 关闭忽略租户 * 关闭忽略租户
*/ */
public static void disableIgnore() { private static void disableIgnore() {
IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); IgnoreStrategy ignoreStrategy = getIgnoreStrategy();
if (ObjectUtil.isNotNull(ignoreStrategy)) { if (ObjectUtil.isNotNull(ignoreStrategy)) {
boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName())

View File

@@ -15,6 +15,7 @@ import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.expression.ExpressionException;
import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException;
@@ -43,7 +44,7 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(HttpRequestMethodNotSupportedException.class) @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request) { HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return R.fail(HttpStatus.HTTP_BAD_METHOD, e.getMessage()); return R.fail(HttpStatus.HTTP_BAD_METHOD, e.getMessage());
@@ -210,4 +211,13 @@ public class GlobalExceptionHandler {
return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误:" + e.getMostSpecificCause().getMessage()); return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误:" + e.getMostSpecificCause().getMessage());
} }
/**
* SpEL 表达式相关异常
*/
@ExceptionHandler(ExpressionException.class)
public R<Void> handleSpelException(ExpressionException e, HttpServletRequest request) {
log.error("请求地址'{}'SpEL解析异常: {}", request.getRequestURI(), e.getMessage());
return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "SpEL解析失败" + e.getMessage());
}
} }

View File

@@ -2,11 +2,17 @@ package org.dromara.common.web.interceptor;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch; import org.apache.commons.lang3.time.StopWatch;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.web.filter.RepeatedlyRequestWrapper; import org.dromara.common.web.filter.RepeatedlyRequestWrapper;
@@ -14,8 +20,10 @@ import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import java.io.BufferedReader; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* web的调用时间统计拦截器 * web的调用时间统计拦截器
@@ -31,19 +39,25 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String url = request.getMethod() + " " + request.getRequestURI(); String url = request.getMethod() + " " + request.getRequestURI();
// 打印请求参数 // 打印请求参数
if (isJsonRequest(request)) { if (isJsonRequest(request)) {
String jsonParam = ""; String jsonParam = "";
if (request instanceof RepeatedlyRequestWrapper) { if (request instanceof RepeatedlyRequestWrapper) {
BufferedReader reader = request.getReader(); jsonParam = IoUtil.read(request.getReader());
jsonParam = IoUtil.read(reader); if (StringUtils.isNotBlank(jsonParam)) {
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
JsonNode rootNode = objectMapper.readTree(jsonParam);
removeSensitiveFields(rootNode, SystemConstants.EXCLUDE_PROPERTIES);
jsonParam = rootNode.toString();
}
} }
log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
} else { } else {
Map<String, String[]> parameterMap = request.getParameterMap(); Map<String, String[]> parameterMap = request.getParameterMap();
if (MapUtil.isNotEmpty(parameterMap)) { if (MapUtil.isNotEmpty(parameterMap)) {
String parameters = JsonUtils.toJsonString(parameterMap); Map<String, String[]> map = new LinkedHashMap<>(parameterMap);
MapUtil.removeAny(map, SystemConstants.EXCLUDE_PROPERTIES);
String parameters = JsonUtils.toJsonString(map);
log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
} else { } else {
log.info("[PLUS]开始请求 => URL[{}],无参数", url); log.info("[PLUS]开始请求 => URL[{}],无参数", url);
@@ -57,6 +71,30 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
return true; return true;
} }
private void removeSensitiveFields(JsonNode node, String[] excludeProperties) {
if (node == null) {
return;
}
if (node.isObject()) {
ObjectNode objectNode = (ObjectNode) node;
// 收集要删除的字段名(避免 ConcurrentModification
Set<String> fieldsToRemove = new HashSet<>();
objectNode.fieldNames().forEachRemaining(fieldName -> {
if (ArrayUtil.contains(excludeProperties, fieldName)) {
fieldsToRemove.add(fieldName);
}
});
fieldsToRemove.forEach(objectNode::remove);
// 递归处理子节点
objectNode.elements().forEachRemaining(child -> removeSensitiveFields(child, excludeProperties));
} else if (node.isArray()) {
ArrayNode arrayNode = (ArrayNode) node;
for (JsonNode child : arrayNode) {
removeSensitiveFields(child, excludeProperties);
}
}
}
@Override @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

View File

@@ -62,6 +62,8 @@ public class TestDemoServiceImpl implements ITestDemoService {
private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) { private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getDeptId() != null, TestDemo::getDeptId, bo.getDeptId());
lqw.eq(bo.getUserId() != null, TestDemo::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey()); lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue()); lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,

View File

@@ -2,6 +2,7 @@ package org.dromara.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.demo.domain.TestTree; import org.dromara.demo.domain.TestTree;
@@ -9,7 +10,6 @@ import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo; import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.demo.mapper.TestTreeMapper; import org.dromara.demo.mapper.TestTreeMapper;
import org.dromara.demo.service.ITestTreeService; import org.dromara.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Collection; import java.util.Collection;
@@ -44,6 +44,8 @@ public class TestTreeServiceImpl implements ITestTreeService {
private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) { private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getDeptId() != null, TestTree::getDeptId, bo.getDeptId());
lqw.eq(bo.getUserId() != null, TestTree::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName()); lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));

View File

@@ -90,10 +90,12 @@ public class GenController extends BaseController {
/** /**
* 导入表结构(保存) * 导入表结构(保存)
* *
* @param tables 表名串 * @param tables 表名串
* @param dataName 数据源名称
*/ */
@SaCheckPermission("tool:gen:import") @SaCheckPermission("tool:gen:import")
@Log(title = "代码生成", businessType = BusinessType.IMPORT) @Log(title = "代码生成", businessType = BusinessType.IMPORT)
@Lock4j(keys = {"#dataName"}, acquireTimeout = 10000)
@RepeatSubmit() @RepeatSubmit()
@PostMapping("/importTable") @PostMapping("/importTable")
public R<Void> importTableSave(String tables, String dataName) { public R<Void> importTableSave(String tables, String dataName) {
@@ -175,7 +177,7 @@ public class GenController extends BaseController {
*/ */
@SaCheckPermission("tool:gen:edit") @SaCheckPermission("tool:gen:edit")
@Log(title = "代码生成", businessType = BusinessType.UPDATE) @Log(title = "代码生成", businessType = BusinessType.UPDATE)
@Lock4j @Lock4j(keys = {"#tableId"}, acquireTimeout = 5000)
@GetMapping("/synchDb/{tableId}") @GetMapping("/synchDb/{tableId}")
public R<Void> synchDb(@PathVariable("tableId") Long tableId) { public R<Void> synchDb(@PathVariable("tableId") Long tableId) {
genTableService.synchDb(tableId); genTableService.synchDb(tableId);
@@ -214,7 +216,7 @@ public class GenController extends BaseController {
*/ */
@SaCheckPermission("tool:gen:list") @SaCheckPermission("tool:gen:list")
@GetMapping(value = "/getDataNames") @GetMapping(value = "/getDataNames")
public R<Object> getCurrentDataSourceNameList(){ public R<Object> getCurrentDataSourceNameList() {
return R.ok(DataBaseHelper.getDataSourceNameList()); return R.ok(DataBaseHelper.getDataSourceNameList());
} }
} }

View File

@@ -4,13 +4,12 @@ import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.core.utils.StringUtils; import jakarta.validation.constraints.NotBlank;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.JdbcType;
import org.dromara.common.core.utils.StringUtils;
import jakarta.validation.constraints.NotBlank; import org.dromara.common.mybatis.core.domain.BaseEntity;
/** /**
* 代码生成业务字段表 gen_table_column * 代码生成业务字段表 gen_table_column
@@ -115,6 +114,7 @@ public class GenTableColumn extends BaseEntity {
/** /**
* 字典类型 * 字典类型
*/ */
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
private String dictType; private String dictType;
/** /**

View File

@@ -8,7 +8,6 @@ import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -28,6 +27,7 @@ import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.utils.IdGeneratorUtil;
import org.dromara.generator.constant.GenConstants; import org.dromara.generator.constant.GenConstants;
import org.dromara.generator.domain.GenTable; import org.dromara.generator.domain.GenTable;
import org.dromara.generator.domain.GenTableColumn; import org.dromara.generator.domain.GenTableColumn;
@@ -60,7 +60,6 @@ public class GenTableServiceImpl implements IGenTableService {
private final GenTableMapper baseMapper; private final GenTableMapper baseMapper;
private final GenTableColumnMapper genTableColumnMapper; private final GenTableColumnMapper genTableColumnMapper;
private final IdentifierGenerator identifierGenerator;
private static final String[] TABLE_IGNORE = new String[]{"sj_", "flow_", "gen_"}; private static final String[] TABLE_IGNORE = new String[]{"sj_", "flow_", "gen_"};
@@ -322,7 +321,7 @@ public class GenTableServiceImpl implements IGenTableService {
GenTable table = baseMapper.selectGenTableById(tableId); GenTable table = baseMapper.selectGenTableById(tableId);
List<Long> menuIds = new ArrayList<>(); List<Long> menuIds = new ArrayList<>();
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
menuIds.add(identifierGenerator.nextId(null).longValue()); menuIds.add(IdGeneratorUtil.nextLongId());
} }
table.setMenuIds(menuIds); table.setMenuIds(menuIds);
// 设置主键列信息 // 设置主键列信息
@@ -468,7 +467,7 @@ public class GenTableServiceImpl implements IGenTableService {
GenTable table = baseMapper.selectGenTableById(tableId); GenTable table = baseMapper.selectGenTableById(tableId);
List<Long> menuIds = new ArrayList<>(); List<Long> menuIds = new ArrayList<>();
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
menuIds.add(identifierGenerator.nextId(null).longValue()); menuIds.add(IdGeneratorUtil.nextLongId());
} }
table.setMenuIds(menuIds); table.setMenuIds(menuIds);
// 设置主键列信息 // 设置主键列信息

View File

@@ -2,21 +2,20 @@ package org.dromara.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil; import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.QueryGroup; import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.bo.SysOssBo;
import org.dromara.system.domain.vo.SysOssUploadVo; import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysOssService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -70,9 +69,6 @@ public class SysOssController extends BaseController {
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT) @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysOssUploadVo> upload(@RequestPart("file") MultipartFile file) { public R<SysOssUploadVo> upload(@RequestPart("file") MultipartFile file) {
if (ObjectUtil.isNull(file)) {
return R.fail("上传文件不能为空");
}
SysOssVo oss = ossService.upload(file); SysOssVo oss = ossService.upload(file);
SysOssUploadVo uploadVo = new SysOssUploadVo(); SysOssUploadVo uploadVo = new SysOssUploadVo();
uploadVo.setUrl(oss.getUrl()); uploadVo.setUrl(oss.getUrl());

View File

@@ -2,6 +2,7 @@ package org.dromara.system.controller.system;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt; import cn.hutool.crypto.digest.BCrypt;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
@@ -114,7 +115,7 @@ public class SysProfileController extends BaseController {
@Log(title = "用户头像", businessType = BusinessType.UPDATE) @Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<AvatarVo> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) { public R<AvatarVo> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) {
if (!avatarfile.isEmpty()) { if (ObjectUtil.isNotNull(avatarfile) && !avatarfile.isEmpty()) {
String extension = FileUtil.extName(avatarfile.getOriginalFilename()); String extension = FileUtil.extName(avatarfile.getOriginalFilename());
if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");

View File

@@ -1,6 +1,7 @@
package org.dromara.system.service.impl; package org.dromara.system.service.impl;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -14,6 +15,7 @@ import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.redis.utils.CacheUtils;
@@ -82,6 +84,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
/** /**
* 获取注册开关 * 获取注册开关
*
* @param tenantId 租户id * @param tenantId 租户id
* @return true开启false关闭 * @return true开启false关闭
*/ */
@@ -212,4 +215,54 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
return SpringUtils.getAopProxy(this).selectConfigByKey(configKey); return SpringUtils.getAopProxy(this).selectConfigByKey(configKey);
} }
/**
* 根据参数 key 获取 Map 类型的配置
*
* @param configKey 参数 key
* @return Dict 对象,如果配置为空或无法解析,返回空 Dict
*/
@Override
public Dict getConfigMap(String configKey) {
String configValue = getConfigValue(configKey);
return JsonUtils.parseMap(configValue);
}
/**
* 根据参数 key 获取 Map 类型的配置列表
*
* @param configKey 参数 key
* @return Dict 列表,如果配置为空或无法解析,返回空列表
*/
@Override
public List<Dict> getConfigArrayMap(String configKey) {
String configValue = getConfigValue(configKey);
return JsonUtils.parseArrayMap(configValue);
}
/**
* 根据参数 key 获取指定类型的配置对象
*
* @param configKey 参数 key
* @param clazz 目标对象类型
* @return 对象实例,如果配置为空或无法解析,返回 null
*/
@Override
public <T> T getConfigObject(String configKey, Class<T> clazz) {
String configValue = getConfigValue(configKey);
return JsonUtils.parseObject(configValue, clazz);
}
/**
* 根据参数 key 获取指定类型的配置列表=
*
* @param configKey 参数 key
* @param clazz 目标元素类型
* @return 指定类型列表,如果配置为空或无法解析,返回空列表
*/
@Override
public <T> List<T> getConfigArray(String configKey, Class<T> clazz) {
String configValue = getConfigValue(configKey);
return JsonUtils.parseArray(configValue, clazz);
}
} }

View File

@@ -192,6 +192,9 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
*/ */
@Override @Override
public SysOssVo upload(MultipartFile file) { public SysOssVo upload(MultipartFile file) {
if (ObjectUtil.isNull(file) || file.isEmpty()) {
throw new ServiceException("上传文件不能为空");
}
String originalfileName = file.getOriginalFilename(); String originalfileName = file.getOriginalFilename();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
OssClient storage = OssFactory.instance(); OssClient storage = OssFactory.instance();
@@ -216,12 +219,16 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
*/ */
@Override @Override
public SysOssVo upload(File file) { public SysOssVo upload(File file) {
if (ObjectUtil.isNull(file) || !file.isFile() || file.length() <= 0) {
throw new ServiceException("上传文件不能为空");
}
String originalfileName = file.getName(); String originalfileName = file.getName();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
OssClient storage = OssFactory.instance(); OssClient storage = OssFactory.instance();
long length = file.length();
UploadResult uploadResult = storage.uploadSuffix(file, suffix); UploadResult uploadResult = storage.uploadSuffix(file, suffix);
SysOssExt ext1 = new SysOssExt(); SysOssExt ext1 = new SysOssExt();
ext1.setFileSize(file.length()); ext1.setFileSize(length);
// 保存文件信息 // 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult, ext1); return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult, ext1);
} }
@@ -270,7 +277,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
OssClient storage = OssFactory.instance(oss.getService()); OssClient storage = OssFactory.instance(oss.getService());
// 仅修改桶类型为 private 的URL临时URL时长为120s // 仅修改桶类型为 private 的URL临时URL时长为120s
if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
oss.setUrl(storage.getPrivateUrl(oss.getFileName(), Duration.ofSeconds(120))); oss.setUrl(storage.createPresignedGetUrl(oss.getFileName(), Duration.ofSeconds(120)));
} }
return oss; return oss;
} }

View File

@@ -92,4 +92,20 @@ public interface FlowConstant {
* 业务编码 * 业务编码
*/ */
String BUSINESS_CODE = "businessCode"; String BUSINESS_CODE = "businessCode";
/**
* 忽略-办理权限校验true忽略false不忽略
*/
String VAR_IGNORE = "ignore";
/**
* 忽略-委派处理true忽略false不忽略
*/
String VAR_IGNORE_DEPUTE = "ignoreDepute";
/**
* 忽略-会签票签处理true忽略false不忽略
*/
String VAR_IGNORE_COOPERATE = "ignoreCooperate";
} }

View File

@@ -187,6 +187,7 @@ public class FlwDefinitionController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/active/{id}") @PutMapping("/active/{id}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Log(title = "流程定义", businessType = BusinessType.UPDATE)
public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) { public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
return R.ok(active ? defService.active(id) : defService.unActive(id)); return R.ok(active ? defService.active(id) : defService.unActive(id));
} }

View File

@@ -1,7 +1,9 @@
package org.dromara.workflow.controller; package org.dromara.workflow.controller;
import cn.hutool.core.convert.Convert;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
@@ -75,8 +77,9 @@ public class FlwInstanceController extends BaseController {
* @param businessIds 业务id * @param businessIds 业务id
*/ */
@DeleteMapping("/deleteByBusinessIds/{businessIds}") @DeleteMapping("/deleteByBusinessIds/{businessIds}")
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
public R<Void> deleteByBusinessIds(@PathVariable List<Long> businessIds) { public R<Void> deleteByBusinessIds(@PathVariable List<Long> businessIds) {
return toAjax(flwInstanceService.deleteByBusinessIds(businessIds)); return toAjax(flwInstanceService.deleteByBusinessIds(StreamUtils.toList(businessIds, Convert::toStr)));
} }
/** /**
@@ -85,6 +88,7 @@ public class FlwInstanceController extends BaseController {
* @param instanceIds 实例id * @param instanceIds 实例id
*/ */
@DeleteMapping("/deleteByInstanceIds/{instanceIds}") @DeleteMapping("/deleteByInstanceIds/{instanceIds}")
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
public R<Void> deleteByInstanceIds(@PathVariable List<Long> instanceIds) { public R<Void> deleteByInstanceIds(@PathVariable List<Long> instanceIds) {
return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds)); return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds));
} }
@@ -95,6 +99,7 @@ public class FlwInstanceController extends BaseController {
* @param instanceIds 实例id * @param instanceIds 实例id
*/ */
@DeleteMapping("/deleteHisByInstanceIds/{instanceIds}") @DeleteMapping("/deleteHisByInstanceIds/{instanceIds}")
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
public R<Void> deleteHisByInstanceIds(@PathVariable List<Long> instanceIds) { public R<Void> deleteHisByInstanceIds(@PathVariable List<Long> instanceIds) {
return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds)); return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds));
} }
@@ -106,6 +111,7 @@ public class FlwInstanceController extends BaseController {
*/ */
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/cancelProcessApply") @PutMapping("/cancelProcessApply")
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
public R<Void> cancelProcessApply(@RequestBody FlowCancelBo bo) { public R<Void> cancelProcessApply(@RequestBody FlowCancelBo bo) {
return toAjax(flwInstanceService.cancelProcessApply(bo)); return toAjax(flwInstanceService.cancelProcessApply(bo));
} }
@@ -118,6 +124,7 @@ public class FlwInstanceController extends BaseController {
*/ */
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/active/{id}") @PutMapping("/active/{id}")
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) { public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
return R.ok(active ? insService.active(id) : insService.unActive(id)); return R.ok(active ? insService.active(id) : insService.unActive(id));
} }
@@ -160,6 +167,7 @@ public class FlwInstanceController extends BaseController {
*/ */
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/updateVariable") @PutMapping("/updateVariable")
@Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
public R<Void> updateVariable(@Validated @RequestBody FlowVariableBo bo) { public R<Void> updateVariable(@Validated @RequestBody FlowVariableBo bo) {
return toAjax(flwInstanceService.updateVariable(bo)); return toAjax(flwInstanceService.updateVariable(bo));
} }

View File

@@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
/** /**
* 流程spel达式定义 * 流程spel达式定义
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04
@@ -38,7 +38,7 @@ public class FlwSpelController extends BaseController {
private final IFlwSpelService flwSpelService; private final IFlwSpelService flwSpelService;
/** /**
* 查询流程spel达式定义列表 * 查询流程spel达式定义列表
*/ */
@SaCheckPermission("workflow:spel:list") @SaCheckPermission("workflow:spel:list")
@GetMapping("/list") @GetMapping("/list")
@@ -47,7 +47,7 @@ public class FlwSpelController extends BaseController {
} }
/** /**
* 获取流程spel达式定义详细信息 * 获取流程spel达式定义详细信息
* *
* @param id 主键 * @param id 主键
*/ */
@@ -58,10 +58,10 @@ public class FlwSpelController extends BaseController {
} }
/** /**
* 新增流程spel达式定义 * 新增流程spel达式定义
*/ */
@SaCheckPermission("workflow:spel:add") @SaCheckPermission("workflow:spel:add")
@Log(title = "流程spel达式定义", businessType = BusinessType.INSERT) @Log(title = "流程spel达式定义", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping() @PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody FlowSpelBo bo) { public R<Void> add(@Validated(AddGroup.class) @RequestBody FlowSpelBo bo) {
@@ -69,10 +69,10 @@ public class FlwSpelController extends BaseController {
} }
/** /**
* 修改流程spel达式定义 * 修改流程spel达式定义
*/ */
@SaCheckPermission("workflow:spel:edit") @SaCheckPermission("workflow:spel:edit")
@Log(title = "流程spel达式定义", businessType = BusinessType.UPDATE) @Log(title = "流程spel达式定义", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@PutMapping() @PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlowSpelBo bo) { public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlowSpelBo bo) {
@@ -80,12 +80,12 @@ public class FlwSpelController extends BaseController {
} }
/** /**
* 删除流程spel达式定义 * 删除流程spel达式定义
* *
* @param ids 主键串 * @param ids 主键串
*/ */
@SaCheckPermission("workflow:spel:remove") @SaCheckPermission("workflow:spel:remove")
@Log(title = "流程spel达式定义", businessType = BusinessType.DELETE) @Log(title = "流程spel达式定义", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}") @DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(flwSpelService.deleteWithValidByIds(List.of(ids), true)); return toAjax(flwSpelService.deleteWithValidByIds(List.of(ids), true));

View File

@@ -216,6 +216,7 @@ public class FlwTaskController extends BaseController {
* @return 结果 * @return 结果
*/ */
@PostMapping("/urgeTask") @PostMapping("/urgeTask")
@Log(title = "任务管理", businessType = BusinessType.INSERT)
public R<Void> urgeTask(@RequestBody FlowUrgeTaskBo bo) { public R<Void> urgeTask(@RequestBody FlowUrgeTaskBo bo) {
return toAjax(flwTaskService.urgeTask(bo)); return toAjax(flwTaskService.urgeTask(bo));
} }

View File

@@ -10,7 +10,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial; import java.io.Serial;
/** /**
* 流程spel达式定义对象 flow_spel * 流程spel达式定义对象 flow_spel
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04

View File

@@ -1,6 +1,5 @@
package org.dromara.workflow.domain.bo; package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;

View File

@@ -50,6 +50,6 @@ public class FlowInstanceBo implements Serializable {
/** /**
* 申请人Ids * 申请人Ids
*/ */
private List<Long> createByIds; private List<String> createByIds;
} }

View File

@@ -10,7 +10,7 @@ import jakarta.validation.constraints.*;
import org.dromara.workflow.domain.FlowSpel; import org.dromara.workflow.domain.FlowSpel;
/** /**
* 流程spel达式定义业务对象 flow_spel * 流程spel达式定义业务对象 flow_spel
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04

View File

@@ -1,10 +1,13 @@
package org.dromara.workflow.domain.bo; package org.dromara.workflow.domain.bo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 任务请求对象 * 任务请求对象
@@ -42,6 +45,11 @@ public class FlowTaskBo implements Serializable {
*/ */
private Long instanceId; private Long instanceId;
/**
* 流程状态
*/
private String flowStatus;
/** /**
* 权限列表 * 权限列表
*/ */
@@ -52,4 +60,10 @@ public class FlowTaskBo implements Serializable {
*/ */
private List<Long> createByIds; private List<Long> createByIds;
/**
* 请求参数
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params = new HashMap<>();
} }

View File

@@ -253,4 +253,8 @@ public class FlowHisTaskVo implements Serializable {
this.cooperateTypeName = CooperateType.getValueByKey(cooperateType); this.cooperateTypeName = CooperateType.getValueByKey(cooperateType);
} }
public String getCreateTime() {
return DateUtils.formatFriendlyTime(createTime);
}
} }

View File

@@ -78,7 +78,7 @@ public class FlowInstanceVo {
private String variable; private String variable;
/** /**
* 流程状态0待提交 1审批中 2 审批通过 3自动通过 8已完成 9已退回 10失效 * 流程状态
*/ */
private String flowStatus; private String flowStatus;

View File

@@ -14,7 +14,7 @@ import java.util.Date;
/** /**
* 流程spel达式定义视图对象 flow_spel * 流程spel达式定义视图对象 flow_spel
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04

View File

@@ -1,6 +1,7 @@
package org.dromara.workflow.domain.vo; package org.dromara.workflow.domain.vo;
import lombok.Data; import lombok.Data;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant; import org.dromara.common.translation.constant.TransConstant;
import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.core.entity.User;
@@ -8,7 +9,6 @@ import org.dromara.workflow.common.constant.FlowConstant;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -163,7 +163,7 @@ public class FlowTaskVo implements Serializable {
/** /**
* 流程签署比例值 大于0为票签会签 * 流程签署比例值 大于0为票签会签
*/ */
private BigDecimal nodeRatio; private String nodeRatio;
/** /**
* 申请人id * 申请人id
@@ -212,4 +212,8 @@ public class FlowTaskVo implements Serializable {
private String businessTitle; private String businessTitle;
//业务扩展信息结束 //业务扩展信息结束
public String getCreateTime() {
return DateUtils.formatFriendlyTime(createTime);
}
} }

View File

@@ -7,6 +7,7 @@ import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.Task;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -55,22 +56,22 @@ public class FlowProcessEventHandler {
* *
* @param flowCode 流程定义编码 * @param flowCode 流程定义编码
* @param instance 实例数据 * @param instance 实例数据
* @param taskId 任务id * @param nextTask 任务
* @param params 上一个任务的办理参数 * @param params 上一个任务的办理参数
*/ */
public void processTaskHandler(String flowCode, Instance instance, Long taskId, Map<String, Object> params) { public void processTaskHandler(String flowCode, Instance instance, Task nextTask, Map<String, Object> params) {
String tenantId = TenantHelper.getTenantId(); String tenantId = TenantHelper.getTenantId();
log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}",
tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); tenantId, flowCode, instance.getBusinessId(), nextTask.getNodeType(), nextTask.getNodeCode(), nextTask.getNodeName(), nextTask.getId());
ProcessTaskEvent processTaskEvent = new ProcessTaskEvent(); ProcessTaskEvent processTaskEvent = new ProcessTaskEvent();
processTaskEvent.setTenantId(tenantId); processTaskEvent.setTenantId(tenantId);
processTaskEvent.setFlowCode(flowCode); processTaskEvent.setFlowCode(flowCode);
processTaskEvent.setInstanceId(instance.getId()); processTaskEvent.setInstanceId(instance.getId());
processTaskEvent.setBusinessId(instance.getBusinessId()); processTaskEvent.setBusinessId(instance.getBusinessId());
processTaskEvent.setNodeType(instance.getNodeType()); processTaskEvent.setNodeType(nextTask.getNodeType());
processTaskEvent.setNodeCode(instance.getNodeCode()); processTaskEvent.setNodeCode(nextTask.getNodeCode());
processTaskEvent.setNodeName(instance.getNodeName()); processTaskEvent.setNodeName(nextTask.getNodeName());
processTaskEvent.setTaskId(taskId); processTaskEvent.setTaskId(nextTask.getId());
processTaskEvent.setStatus(instance.getFlowStatus()); processTaskEvent.setStatus(instance.getFlowStatus());
processTaskEvent.setParams(params); processTaskEvent.setParams(params);
SpringUtils.context().publishEvent(processTaskEvent); SpringUtils.context().publishEvent(processTaskEvent);

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference; import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.enums.BusinessStatusEnum;
@@ -73,6 +74,9 @@ public class WorkflowGlobalListener implements GlobalListener {
String ext = listenerVariable.getNode().getExt(); String ext = listenerVariable.getNode().getExt();
if (StringUtils.isNotBlank(ext)) { if (StringUtils.isNotBlank(ext)) {
Map<String, Object> variable = listenerVariable.getVariable(); Map<String, Object> variable = listenerVariable.getVariable();
if (CollUtil.isNotEmpty(variable)) {
variable = new HashMap<>();
}
NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable); NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable);
Set<String> copyList = nodeExt.getCopySettings(); Set<String> copyList = nodeExt.getCopySettings();
if (CollUtil.isNotEmpty(copyList)) { if (CollUtil.isNotEmpty(copyList)) {
@@ -104,20 +108,61 @@ public class WorkflowGlobalListener implements GlobalListener {
Definition definition = listenerVariable.getDefinition(); Definition definition = listenerVariable.getDefinition();
Instance instance = listenerVariable.getInstance(); Instance instance = listenerVariable.getInstance();
String applyNodeCode = flwCommonService.applyNodeCode(definition.getId()); String applyNodeCode = flwCommonService.applyNodeCode(definition.getId());
String hisStatus = flowParams != null ? flowParams.getHisStatus() : null;
for (Task flowTask : nextTasks) { for (Task flowTask : nextTasks) {
// 如果办理或者退回并行存在需要指定办理人,则直接覆盖办理人 String nodeCode = flowTask.getNodeCode();
if (variable.containsKey(flowTask.getNodeCode()) && TaskStatusEnum.isPassOrBack(flowParams.getHisStatus())) {
String userIds = variable.get(flowTask.getNodeCode()).toString(); // 处理办理或退回时指定办理人的情况
flowTask.setPermissionList(List.of(userIds.split(StringUtils.SEPARATOR))); if (TaskStatusEnum.PASS.getStatus().equals(hisStatus)) {
variable.remove(flowTask.getNodeCode()); processTaskPermission(variable, flowTask, hisStatus);
} else if (TaskStatusEnum.BACK.getStatus().equals(hisStatus)) {
processTaskPermission(variable, flowTask, hisStatus);
} }
// 如果是申请节点,则把启动人添加到办理人 // 如果是申请节点,则把启动人添加到办理人
if (flowTask.getNodeCode().equals(applyNodeCode)) { if (nodeCode.equals(applyNodeCode) && StringUtils.isNotBlank(instance.getCreateBy())) {
flowTask.setPermissionList(List.of(instance.getCreateBy())); flowTask.setPermissionList(List.of(instance.getCreateBy()));
} }
} }
} }
/**
* 处理任务权限设置
*
* @param variable 变量集合
* @param flowTask 流程任务
* @param taskStatus 任务状态
*/
private void processTaskPermission(Map<String, Object> variable, Task flowTask, String taskStatus) {
String nodeKey = taskStatus + StrUtil.COLON + flowTask.getNodeCode();
// 检查是否存在状态相关的变量
if (!variable.containsKey(nodeKey)) {
return;
}
// 获取用户ID字符串
Object userIdsObj = variable.get(nodeKey);
if (userIdsObj == null) {
return;
}
String userIds = userIdsObj.toString();
if (StringUtils.isBlank(userIds)) {
return;
}
// 分割用户ID并设置权限列表
String[] userIdArray = userIds.split(StringUtils.SEPARATOR);
if (userIdArray.length > 0) {
flowTask.setPermissionList(List.of(userIdArray));
// 移除已处理的状态变量
variable.remove(nodeKey);
FlowEngine.insService().removeVariables(flowTask.getInstanceId(),nodeKey);
}
}
/** /**
* 完成监听器,当前任务完成后执行 * 完成监听器,当前任务完成后执行
* *
@@ -144,7 +189,8 @@ public class WorkflowGlobalListener implements GlobalListener {
//申请人提交事件 //申请人提交事件
Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT);
if (submit != null && submit) { if (submit != null && submit) {
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true); String status = determineFlowStatus(instance);
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, variable, true);
} else { } else {
// 判断流程状态(发布:撤销,退回,作废,终止,已完成事件) // 判断流程状态(发布:撤销,退回,作废,终止,已完成事件)
String status = determineFlowStatus(instance); String status = determineFlowStatus(instance);
@@ -165,7 +211,7 @@ public class WorkflowGlobalListener implements GlobalListener {
//发布任务事件 //发布任务事件
if (CollUtil.isNotEmpty(nextTasks)) { if (CollUtil.isNotEmpty(nextTasks)) {
for (Task nextTask : nextTasks) { for (Task nextTask : nextTasks) {
flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, nextTask.getId(), params); flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, nextTask, params);
} }
} }
if (ObjectUtil.isNull(flowParams)) { if (ObjectUtil.isNull(flowParams)) {

View File

@@ -5,7 +5,7 @@ import org.dromara.workflow.domain.vo.FlowSpelVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/** /**
* 流程spel达式定义Mapper接口 * 流程spel达式定义Mapper接口
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04

View File

@@ -75,7 +75,7 @@ public interface IFlwInstanceService {
* @param businessIds 业务id * @param businessIds 业务id
* @return 结果 * @return 结果
*/ */
boolean deleteByBusinessIds(List<Long> businessIds); boolean deleteByBusinessIds(List<String> businessIds);
/** /**
* 按照实例id删除流程实例 * 按照实例id删除流程实例

View File

@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 流程spel达式定义Service接口 * 流程spel达式定义Service接口
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04
@@ -20,48 +20,48 @@ import java.util.Map;
public interface IFlwSpelService { public interface IFlwSpelService {
/** /**
* 查询流程spel达式定义 * 查询流程spel达式定义
* *
* @param id 主键 * @param id 主键
* @return 流程spel达式定义 * @return 流程spel达式定义
*/ */
FlowSpelVo queryById(Long id); FlowSpelVo queryById(Long id);
/** /**
* 分页查询流程spel达式定义列表 * 分页查询流程spel达式定义列表
* *
* @param bo 查询条件 * @param bo 查询条件
* @param pageQuery 分页参数 * @param pageQuery 分页参数
* @return 流程spel达式定义分页列表 * @return 流程spel达式定义分页列表
*/ */
TableDataInfo<FlowSpelVo> queryPageList(FlowSpelBo bo, PageQuery pageQuery); TableDataInfo<FlowSpelVo> queryPageList(FlowSpelBo bo, PageQuery pageQuery);
/** /**
* 查询符合条件的流程spel达式定义列表 * 查询符合条件的流程spel达式定义列表
* *
* @param bo 查询条件 * @param bo 查询条件
* @return 流程spel达式定义列表 * @return 流程spel达式定义列表
*/ */
List<FlowSpelVo> queryList(FlowSpelBo bo); List<FlowSpelVo> queryList(FlowSpelBo bo);
/** /**
* 新增流程spel达式定义 * 新增流程spel达式定义
* *
* @param bo 流程spel达式定义 * @param bo 流程spel达式定义
* @return 是否新增成功 * @return 是否新增成功
*/ */
Boolean insertByBo(FlowSpelBo bo); Boolean insertByBo(FlowSpelBo bo);
/** /**
* 修改流程spel达式定义 * 修改流程spel达式定义
* *
* @param bo 流程spel达式定义 * @param bo 流程spel达式定义
* @return 是否修改成功 * @return 是否修改成功
*/ */
Boolean updateByBo(FlowSpelBo bo); Boolean updateByBo(FlowSpelBo bo);
/** /**
* 校验并批量删除流程spel达式定义信息 * 校验并批量删除流程spel达式定义信息
* *
* @param ids 待删除的主键集合 * @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验 * @param isValid 是否进行有效性校验

View File

@@ -206,6 +206,9 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ
if (ObjectUtil.isNull(oldCategory)) { if (ObjectUtil.isNull(oldCategory)) {
throw new ServiceException("流程分类不存在,无法修改"); throw new ServiceException("流程分类不存在,无法修改");
} }
if (oldCategory.getParentId() == 0L && category.getParentId() != 0L) {
throw new ServiceException("不允许修改顶级分类的父级节点");
}
if (!oldCategory.getParentId().equals(category.getParentId())) { if (!oldCategory.getParentId().equals(category.getParentId())) {
FlowCategory newParentCategory = baseMapper.selectById(category.getParentId()); FlowCategory newParentCategory = baseMapper.selectById(category.getParentId());
if (ObjectUtil.isNotNull(newParentCategory)) { if (ObjectUtil.isNotNull(newParentCategory)) {

View File

@@ -88,23 +88,28 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
if (ObjectUtil.isEmpty(messageTypeEnum)) { if (ObjectUtil.isEmpty(messageTypeEnum)) {
continue; continue;
} }
switch (messageTypeEnum) { try {
case SYSTEM_MESSAGE -> { switch (messageTypeEnum) {
SseMessageDto dto = new SseMessageDto(); case SYSTEM_MESSAGE -> {
dto.setUserIds(userIds); SseMessageDto dto = new SseMessageDto();
dto.setMessage(message); dto.setUserIds(userIds);
SseMessageUtils.publishMessage(dto); dto.setMessage(message);
SseMessageUtils.publishMessage(dto);
}
case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message);
case SMS_MESSAGE -> {
// TODO: 补充短信发送逻辑
log.info("【短信发送 - TODO】用户数量={} 内容={}", userList.size(), message);
}
default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum);
} }
case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message); } catch (Exception ex) {
case SMS_MESSAGE -> { // 记录错误但不抛出,确保主逻辑不受影响
//todo 短信发送 log.error("【消息发送失败】类型={},原因={}", messageTypeEnum, ex.getMessage(), ex);
}
default -> throw new IllegalStateException("Unexpected value: " + messageTypeEnum);
} }
} }
} }
/** /**
* 申请人节点编码 * 申请人节点编码
* *

View File

@@ -208,10 +208,6 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void syncDef(String tenantId) { public void syncDef(String tenantId) {
List<FlowDefinition> flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper<FlowDefinition>().eq(FlowDefinition::getTenantId, DEFAULT_TENANT_ID));
if (CollUtil.isEmpty(flowDefinitions)) {
return;
}
FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper<FlowCategory>() FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper<FlowCategory>()
.eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID) .eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID)
.eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID)); .eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID));
@@ -223,6 +219,11 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
flowCategory.setUpdateBy(null); flowCategory.setUpdateBy(null);
flowCategory.setUpdateTime(null); flowCategory.setUpdateTime(null);
flwCategoryMapper.insert(flowCategory); flwCategoryMapper.insert(flowCategory);
List<FlowDefinition> flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper<FlowDefinition>().eq(FlowDefinition::getTenantId, DEFAULT_TENANT_ID));
if (CollUtil.isEmpty(flowDefinitions)) {
return;
}
List<Long> defIds = StreamUtils.toList(flowDefinitions, FlowDefinition::getId); List<Long> defIds = StreamUtils.toList(flowDefinitions, FlowDefinition::getId);
List<FlowNode> flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper<FlowNode>().in(FlowNode::getDefinitionId, defIds)); List<FlowNode> flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper<FlowNode>().in(FlowNode::getDefinitionId, defIds));
List<FlowSkip> flowSkips = flowSkipMapper.selectList(new LambdaQueryWrapper<FlowSkip>().in(FlowSkip::getDefinitionId, defIds)); List<FlowSkip> flowSkips = flowSkipMapper.selectList(new LambdaQueryWrapper<FlowSkip>().in(FlowSkip::getDefinitionId, defIds));

View File

@@ -181,8 +181,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteByBusinessIds(List<Long> businessIds) { public boolean deleteByBusinessIds(List<String> businessIds) {
List<FlowInstance> flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper<FlowInstance>().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds, Convert::toStr))); List<FlowInstance> flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper<FlowInstance>().in(FlowInstance::getBusinessId, businessIds));
if (CollUtil.isEmpty(flowInstances)) { if (CollUtil.isEmpty(flowInstances)) {
log.warn("未找到对应的流程实例信息,无法执行删除操作。"); log.warn("未找到对应的流程实例信息,无法执行删除操作。");
return false; return false;
@@ -211,27 +211,17 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
Function.identity() Function.identity()
); );
try { // 逐一触发删除事件
// 逐一触发删除事件 instances.forEach(instance -> {
instances.forEach(instance -> { Definition definition = definitionMap.get(instance.getDefinitionId());
Definition definition = definitionMap.get(instance.getDefinitionId()); if (ObjectUtil.isNull(definition)) {
if (ObjectUtil.isNull(definition)) { log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); return;
return;
}
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
});
// 删除实例
boolean remove = insService.remove(instanceIds);
if (!remove) {
log.warn("删除流程实例失败!");
throw new ServiceException("删除流程实例失败");
} }
} catch (Exception e) { flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
log.warn("操作失败!{}", e.getMessage()); });
throw new ServiceException(e.getMessage()); // 删除实例
} return insService.remove(instanceIds);
return true;
} }
/** /**
@@ -254,27 +244,22 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
Definition::getId, Definition::getId,
Function.identity() Function.identity()
); );
try { // 逐一触发删除事件
// 逐一触发删除事件 instances.forEach(instance -> {
instances.forEach(instance -> { Definition definition = definitionMap.get(instance.getDefinitionId());
Definition definition = definitionMap.get(instance.getDefinitionId()); if (ObjectUtil.isNull(definition)) {
if (ObjectUtil.isNull(definition)) { log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); return;
return;
}
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
});
List<FlowTask> flowTaskList = flwTaskService.selectByInstIds(instanceIds);
if (CollUtil.isNotEmpty(flowTaskList)) {
FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId));
} }
FlowEngine.taskService().deleteByInsIds(instanceIds); flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
FlowEngine.hisTaskService().deleteByInsIds(instanceIds); });
FlowEngine.insService().removeByIds(instanceIds); List<FlowTask> flowTaskList = flwTaskService.selectByInstIds(instanceIds);
} catch (Exception e) { if (CollUtil.isNotEmpty(flowTaskList)) {
log.warn("操作失败!{}", e.getMessage()); FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId));
throw new ServiceException(e.getMessage());
} }
FlowEngine.taskService().deleteByInsIds(instanceIds);
FlowEngine.hisTaskService().deleteByInsIds(instanceIds);
FlowEngine.insService().removeByIds(instanceIds);
return true; return true;
} }
@@ -286,29 +271,24 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean cancelProcessApply(FlowCancelBo bo) { public boolean cancelProcessApply(FlowCancelBo bo) {
try { Instance instance = selectInstByBusinessId(bo.getBusinessId());
Instance instance = selectInstByBusinessId(bo.getBusinessId()); if (instance == null) {
if (instance == null) { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
}
Definition definition = defService.getById(instance.getDefinitionId());
if (definition == null) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
}
String message = bo.getMessage();
String userIdStr = LoginHelper.getUserIdStr();
BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus());
FlowParams flowParams = FlowParams.build()
.message(message)
.flowStatus(BusinessStatusEnum.CANCEL.getStatus())
.hisStatus(BusinessStatusEnum.CANCEL.getStatus())
.handler(userIdStr)
.ignore(true);
taskService.revoke(instance.getId(), flowParams);
} catch (Exception e) {
log.error("撤销失败: {}", e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
Definition definition = defService.getById(instance.getDefinitionId());
if (definition == null) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
}
String message = bo.getMessage();
String userIdStr = LoginHelper.getUserIdStr();
BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus());
FlowParams flowParams = FlowParams.build()
.message(message)
.flowStatus(BusinessStatusEnum.CANCEL.getStatus())
.hisStatus(BusinessStatusEnum.CANCEL.getStatus())
.handler(userIdStr)
.ignore(true);
taskService.revoke(instance.getId(), flowParams);
return true; return true;
} }
@@ -422,20 +402,14 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
if (flowInstance == null) { if (flowInstance == null) {
throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
} }
try { Map<String, Object> variableMap = new HashMap<>(Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap()));
Map<String, Object> variableMap = new HashMap<>(Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap())); if (!variableMap.containsKey(bo.getKey())) {
if (!variableMap.containsKey(bo.getKey())) { log.error("变量不存在: {}", bo.getKey());
log.error("变量不存在: {}", bo.getKey()); return false;
return false;
}
variableMap.put(bo.getKey(), bo.getValue());
flowInstance.setVariable(FlowEngine.jsonConvert.objToStr(variableMap));
flowInstanceMapper.updateById(flowInstance);
} catch (Exception e) {
log.error("设置流程变量失败: {}", e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
return true; variableMap.put(bo.getKey(), bo.getValue());
flowInstance.setVariable(FlowEngine.jsonConvert.objToStr(variableMap));
return flowInstanceMapper.updateById(flowInstance) > 0;
} }
/** /**
@@ -480,21 +454,16 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean processInvalid(FlowInvalidBo bo) { public boolean processInvalid(FlowInvalidBo bo) {
try { Instance instance = insService.getById(bo.getId());
Instance instance = insService.getById(bo.getId()); if (instance != null) {
if (instance != null) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus());
BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus());
}
FlowParams flowParams = FlowParams.build()
.message(bo.getComment())
.flowStatus(BusinessStatusEnum.INVALID.getStatus())
.hisStatus(TaskStatusEnum.INVALID.getStatus())
.ignore(true);
taskService.terminationByInsId(bo.getId(), flowParams);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
FlowParams flowParams = FlowParams.build()
.message(bo.getComment())
.flowStatus(BusinessStatusEnum.INVALID.getStatus())
.hisStatus(TaskStatusEnum.INVALID.getStatus())
.ignore(true);
taskService.terminationByInsId(bo.getId(), flowParams);
return true;
} }
} }

View File

@@ -28,7 +28,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 流程spel达式定义Service业务层处理 * 流程spel达式定义Service业务层处理
* *
* @author Michelle.Chung * @author Michelle.Chung
* @date 2025-07-04 * @date 2025-07-04
@@ -42,10 +42,10 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
private final FlwSpelMapper baseMapper; private final FlwSpelMapper baseMapper;
/** /**
* 查询流程spel达式定义 * 查询流程spel达式定义
* *
* @param id 主键 * @param id 主键
* @return 流程spel达式定义 * @return 流程spel达式定义
*/ */
@Override @Override
public FlowSpelVo queryById(Long id){ public FlowSpelVo queryById(Long id){
@@ -53,11 +53,11 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
} }
/** /**
* 分页查询流程spel达式定义列表 * 分页查询流程spel达式定义列表
* *
* @param bo 查询条件 * @param bo 查询条件
* @param pageQuery 分页参数 * @param pageQuery 分页参数
* @return 流程spel达式定义分页列表 * @return 流程spel达式定义分页列表
*/ */
@Override @Override
public TableDataInfo<FlowSpelVo> queryPageList(FlowSpelBo bo, PageQuery pageQuery) { public TableDataInfo<FlowSpelVo> queryPageList(FlowSpelBo bo, PageQuery pageQuery) {
@@ -67,10 +67,10 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
} }
/** /**
* 查询符合条件的流程spel达式定义列表 * 查询符合条件的流程spel达式定义列表
* *
* @param bo 查询条件 * @param bo 查询条件
* @return 流程spel达式定义列表 * @return 流程spel达式定义列表
*/ */
@Override @Override
public List<FlowSpelVo> queryList(FlowSpelBo bo) { public List<FlowSpelVo> queryList(FlowSpelBo bo) {
@@ -92,9 +92,9 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
} }
/** /**
* 新增流程spel达式定义 * 新增流程spel达式定义
* *
* @param bo 流程spel达式定义 * @param bo 流程spel达式定义
* @return 是否新增成功 * @return 是否新增成功
*/ */
@Override @Override
@@ -109,9 +109,9 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
} }
/** /**
* 修改流程spel达式定义 * 修改流程spel达式定义
* *
* @param bo 流程spel达式定义 * @param bo 流程spel达式定义
* @return 是否修改成功 * @return 是否修改成功
*/ */
@Override @Override
@@ -129,7 +129,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
} }
/** /**
* 校验并批量删除流程spel达式定义信息 * 校验并批量删除流程spel达式定义信息
* *
* @param ids 待删除的主键集合 * @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验 * @param isValid 是否进行有效性校验

View File

@@ -5,9 +5,10 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict; import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -25,10 +26,12 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.utils.IdGeneratorUtil;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.*; import org.dromara.warm.flow.core.entity.*;
import org.dromara.warm.flow.core.enums.CooperateType;
import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.enums.SkipType;
import org.dromara.warm.flow.core.enums.UserType; import org.dromara.warm.flow.core.enums.UserType;
@@ -60,7 +63,6 @@ import org.dromara.workflow.service.IFlwTaskService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.*; import java.util.*;
import static org.dromara.workflow.common.constant.FlowConstant.*; import static org.dromara.workflow.common.constant.FlowConstant.*;
@@ -84,7 +86,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
private final FlowInstanceMapper flowInstanceMapper; private final FlowInstanceMapper flowInstanceMapper;
private final FlowTaskMapper flowTaskMapper; private final FlowTaskMapper flowTaskMapper;
private final FlowHisTaskMapper flowHisTaskMapper; private final FlowHisTaskMapper flowHisTaskMapper;
private final IdentifierGenerator identifierGenerator;
private final UserService userService; private final UserService userService;
private final FlwTaskMapper flwTaskMapper; private final FlwTaskMapper flwTaskMapper;
private final FlwCategoryMapper flwCategoryMapper; private final FlwCategoryMapper flwCategoryMapper;
@@ -101,6 +102,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Lock4j(keys = {"#startProcessBo.flowCode + #startProcessBo.businessId"})
public StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo) { public StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo) {
String businessId = startProcessBo.getBusinessId(); String businessId = startProcessBo.getBusinessId();
if (StringUtils.isBlank(businessId)) { if (StringUtils.isBlank(businessId)) {
@@ -137,6 +139,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
// 将流程定义内的扩展参数设置到变量中 // 将流程定义内的扩展参数设置到变量中
Definition definition = FlowEngine.defService().getPublishByFlowCode(startProcessBo.getFlowCode()); Definition definition = FlowEngine.defService().getPublishByFlowCode(startProcessBo.getFlowCode());
if (ObjectUtil.isNull(definition)) {
throw new ServiceException("流程【" + startProcessBo.getFlowCode() + "】未发布,请先在流程设计器中发布流程定义");
}
Dict dict = JsonUtils.parseMap(definition.getExt()); Dict dict = JsonUtils.parseMap(definition.getExt());
boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(FlowConstant.AUTO_PASS); boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(FlowConstant.AUTO_PASS);
variables.put(FlowConstant.AUTO_PASS, autoPass); variables.put(FlowConstant.AUTO_PASS, autoPass);
@@ -146,12 +151,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
.flowCode(startProcessBo.getFlowCode()) .flowCode(startProcessBo.getFlowCode())
.variable(startProcessBo.getVariables()) .variable(startProcessBo.getVariables())
.flowStatus(BusinessStatusEnum.DRAFT.getStatus()); .flowStatus(BusinessStatusEnum.DRAFT.getStatus());
Instance instance; Instance instance = insService.start(businessId, flowParams);
try {
instance = insService.start(businessId, flowParams);
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
// 保存流程实例业务信息 // 保存流程实例业务信息
this.buildFlowInstanceBizExt(instance, bizExt); this.buildFlowInstanceBizExt(instance, bizExt);
// 申请人执行流程 // 申请人执行流程
@@ -197,53 +197,52 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Lock4j(keys = {"#completeTaskBo.taskId"})
public boolean completeTask(CompleteTaskBo completeTaskBo) { public boolean completeTask(CompleteTaskBo completeTaskBo) {
try { // 获取任务ID并查询对应的流程任务和实例信息
// 获取任务ID并查询对应的流程任务和实例信息 Long taskId = completeTaskBo.getTaskId();
Long taskId = completeTaskBo.getTaskId(); List<String> messageType = completeTaskBo.getMessageType();
List<String> messageType = completeTaskBo.getMessageType(); String notice = completeTaskBo.getNotice();
String notice = completeTaskBo.getNotice(); // 获取抄送人
// 获取抄送人 List<FlowCopyBo> flowCopyList = completeTaskBo.getFlowCopyList();
List<FlowCopyBo> flowCopyList = completeTaskBo.getFlowCopyList(); // 设置抄送人
// 设置抄送人 Map<String, Object> variables = completeTaskBo.getVariables();
Map<String, Object> variables = completeTaskBo.getVariables(); variables.put(FlowConstant.FLOW_COPY_LIST, flowCopyList);
variables.put(FlowConstant.FLOW_COPY_LIST, flowCopyList); // 消息类型
// 消息类型 variables.put(FlowConstant.MESSAGE_TYPE, messageType);
variables.put(FlowConstant.MESSAGE_TYPE, messageType); // 消息通知
// 消息通知 variables.put(FlowConstant.MESSAGE_NOTICE, notice);
variables.put(FlowConstant.MESSAGE_NOTICE, notice);
FlowTask flowTask = flowTaskMapper.selectById(taskId); FlowTask flowTask = flowTaskMapper.selectById(taskId);
if (ObjectUtil.isNull(flowTask)) { if (ObjectUtil.isNull(flowTask)) {
throw new ServiceException("流程任务不存在或任务已审批!"); throw new ServiceException("流程任务不存在或任务已审批!");
}
Instance ins = insService.getById(flowTask.getInstanceId());
// 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听
if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) {
variables.put(FlowConstant.SUBMIT, true);
}
// 设置弹窗处理人
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap());
if (CollUtil.isNotEmpty(assigneeMap)) {
variables.putAll(assigneeMap);
}
// 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息
FlowParams flowParams = FlowParams.build()
.handler(completeTaskBo.getHandler())
.variable(variables)
.skipType(SkipType.PASS.getKey())
.message(completeTaskBo.getMessage())
.flowStatus(BusinessStatusEnum.WAITING.getStatus())
.hisStatus(TaskStatusEnum.PASS.getStatus())
.hisTaskExt(completeTaskBo.getFileId());
Boolean autoPass = Convert.toBool(variables.getOrDefault(AUTO_PASS, false));
skipTask(taskId, flowParams, flowTask.getInstanceId(), autoPass);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
Instance ins = insService.getById(flowTask.getInstanceId());
// 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听
if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) {
variables.put(FlowConstant.SUBMIT, true);
}
// 设置弹窗处理人
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap());
if (CollUtil.isNotEmpty(assigneeMap)) {
variables.putAll(assigneeMap);
}
// 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息
FlowParams flowParams = FlowParams.build()
.handler(completeTaskBo.getHandler())
.variable(variables)
.ignore(Convert.toBool(variables.getOrDefault(VAR_IGNORE, false)))
.ignoreDepute(Convert.toBool(variables.getOrDefault(VAR_IGNORE_DEPUTE, false)))
.ignoreCooperate(Convert.toBool(variables.getOrDefault(VAR_IGNORE_COOPERATE, false)))
.skipType(SkipType.PASS.getKey())
.message(completeTaskBo.getMessage())
.flowStatus(BusinessStatusEnum.WAITING.getStatus())
.hisStatus(TaskStatusEnum.PASS.getStatus())
.hisTaskExt(completeTaskBo.getFileId());
Boolean autoPass = Convert.toBool(variables.getOrDefault(AUTO_PASS, false));
skipTask(taskId, flowParams, flowTask.getInstanceId(), autoPass);
return true;
} }
/** /**
@@ -305,10 +304,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
List<String> variableUserIds = Arrays.asList(userIds.split(StringUtils.SEPARATOR)); List<String> variableUserIds = Arrays.asList(userIds.split(StringUtils.SEPARATOR));
hashSet.addAll(popUserIds); hashSet.addAll(popUserIds);
hashSet.addAll(variableUserIds); hashSet.addAll(variableUserIds);
map.put(entry.getKey(), StringUtils.joinComma(hashSet)); map.put(TaskStatusEnum.PASS.getStatus() + StrUtil.COLON + entry.getKey(), StringUtils.joinComma(hashSet));
map.put(TaskStatusEnum.BACK.getStatus() + StrUtil.COLON + entry.getKey(), StringUtils.joinComma(hashSet));
} }
} else { } else {
map.put(entry.getKey(), entry.getValue()); map.put(TaskStatusEnum.PASS.getStatus() + StrUtil.COLON + entry.getKey(), entry.getValue());
map.put(TaskStatusEnum.BACK.getStatus() + StrUtil.COLON + entry.getKey(), entry.getValue());
} }
} }
return map; return map;
@@ -333,7 +334,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
flowNode.setNodeCode(flowHisTask.getTargetNodeCode()); flowNode.setNodeCode(flowHisTask.getTargetNodeCode());
flowNode.setNodeName(flowHisTask.getTargetNodeName()); flowNode.setNodeName(flowHisTask.getTargetNodeName());
//生成新的任务id //生成新的任务id
long taskId = identifierGenerator.nextId(null).longValue(); long taskId = IdGeneratorUtil.nextLongId();
task.setId(taskId); task.setId(taskId);
task.setNodeName("【抄送】" + task.getNodeName()); task.setNodeName("【抄送】" + task.getNodeName());
Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000); Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000);
@@ -447,15 +448,19 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
} }
private QueryWrapper<FlowTaskBo> buildQueryWrapper(FlowTaskBo flowTaskBo) { private QueryWrapper<FlowTaskBo> buildQueryWrapper(FlowTaskBo flowTaskBo) {
Map<String, Object> params = flowTaskBo.getParams();
QueryWrapper<FlowTaskBo> wrapper = Wrappers.query(); QueryWrapper<FlowTaskBo> wrapper = Wrappers.query();
wrapper.like(StringUtils.isNotBlank(flowTaskBo.getNodeName()), "t.node_name", flowTaskBo.getNodeName()); wrapper.like(StringUtils.isNotBlank(flowTaskBo.getNodeName()), "t.node_name", flowTaskBo.getNodeName());
wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowName()), "t.flow_name", flowTaskBo.getFlowName()); wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowName()), "t.flow_name", flowTaskBo.getFlowName());
wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowCode()), "t.flow_code", flowTaskBo.getFlowCode()); wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowCode()), "t.flow_code", flowTaskBo.getFlowCode());
wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowStatus()), "t.flow_status", flowTaskBo.getFlowStatus());
wrapper.in(CollUtil.isNotEmpty(flowTaskBo.getCreateByIds()), "t.create_by", flowTaskBo.getCreateByIds()); wrapper.in(CollUtil.isNotEmpty(flowTaskBo.getCreateByIds()), "t.create_by", flowTaskBo.getCreateByIds());
if (StringUtils.isNotBlank(flowTaskBo.getCategory())) { if (StringUtils.isNotBlank(flowTaskBo.getCategory())) {
List<Long> categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowTaskBo.getCategory())); List<Long> categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowTaskBo.getCategory()));
wrapper.in("t.category", StreamUtils.toList(categoryIds, Convert::toStr)); wrapper.in("t.category", StreamUtils.toList(categoryIds, Convert::toStr));
} }
wrapper.between(params.get("beginTime") != null && params.get("endTime") != null,
"t.create_time", params.get("beginTime"), params.get("endTime"));
wrapper.orderByDesc("t.create_time").orderByDesc("t.update_time"); wrapper.orderByDesc("t.create_time").orderByDesc("t.update_time");
return wrapper; return wrapper;
} }
@@ -468,40 +473,35 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean backProcess(BackProcessBo bo) { public boolean backProcess(BackProcessBo bo) {
try { Long taskId = bo.getTaskId();
Long taskId = bo.getTaskId(); String notice = bo.getNotice();
String notice = bo.getNotice(); List<String> messageType = bo.getMessageType();
List<String> messageType = bo.getMessageType(); String message = bo.getMessage();
String message = bo.getMessage(); FlowTask task = flowTaskMapper.selectById(taskId);
FlowTask task = flowTaskMapper.selectById(taskId); if (ObjectUtil.isNull(task)) {
if (ObjectUtil.isNull(task)) { throw new ServiceException("任务不存在!");
throw new ServiceException("任务不存在!");
}
Instance inst = insService.getById(task.getInstanceId());
BusinessStatusEnum.checkBackStatus(inst.getFlowStatus());
Long definitionId = task.getDefinitionId();
String applyNodeCode = flwCommonService.applyNodeCode(definitionId);
Map<String, Object> variable = new HashMap<>();
// 消息类型
variable.put(FlowConstant.MESSAGE_TYPE, messageType);
// 消息通知
variable.put(FlowConstant.MESSAGE_NOTICE, notice);
FlowParams flowParams = FlowParams.build()
.nodeCode(bo.getNodeCode())
.variable(variable)
.message(message)
.skipType(SkipType.REJECT.getKey())
.flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus())
.hisStatus(TaskStatusEnum.BACK.getStatus())
.hisTaskExt(bo.getFileId());
taskService.skip(task.getId(), flowParams);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
Instance inst = insService.getById(task.getInstanceId());
BusinessStatusEnum.checkBackStatus(inst.getFlowStatus());
Long definitionId = task.getDefinitionId();
String applyNodeCode = flwCommonService.applyNodeCode(definitionId);
Map<String, Object> variable = new HashMap<>();
// 消息类型
variable.put(FlowConstant.MESSAGE_TYPE, messageType);
// 消息通知
variable.put(FlowConstant.MESSAGE_NOTICE, notice);
FlowParams flowParams = FlowParams.build()
.nodeCode(bo.getNodeCode())
.variable(variable)
.message(message)
.skipType(SkipType.REJECT.getKey())
.flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus())
.hisStatus(TaskStatusEnum.BACK.getStatus())
.hisTaskExt(bo.getFileId());
taskService.skip(task.getId(), flowParams);
return true;
} }
/** /**
@@ -529,8 +529,22 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
} }
//获取可驳回的前置节点 //获取可驳回的前置节点
List<Node> nodes = nodeService.previousNodeList(task.getDefinitionId(), nowNodeCode); List<Node> nodes = nodeService.previousNodeList(task.getDefinitionId(), nowNodeCode);
if (CollUtil.isNotEmpty(nodes)) { List<HisTask> hisTaskList = hisTaskService.getByInsId(task.getInstanceId());
return StreamUtils.filter(nodes, e -> NodeType.BETWEEN.getKey().equals(e.getNodeType()));
Map<String, Node> nodeMap = StreamUtils.toIdentityMap(nodes, Node::getNodeCode);
Set<String> added = new HashSet<>();
List<Node> backNodeList = new ArrayList<>();
for (HisTask hisTask : hisTaskList) {
Node nodeValue = nodeMap.get(hisTask.getNodeCode());
if (nodeValue != null
&& NodeType.BETWEEN.getKey().equals(nodeValue.getNodeType())
&& added.add(nodeValue.getNodeCode())) {
backNodeList.add(nodeValue);
}
}
if (CollUtil.isNotEmpty(backNodeList)) {
Collections.reverse(backNodeList);
return backNodeList;
} }
return nodes; return nodes;
} }
@@ -543,26 +557,21 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean terminationTask(FlowTerminationBo bo) { public boolean terminationTask(FlowTerminationBo bo) {
try { Long taskId = bo.getTaskId();
Long taskId = bo.getTaskId(); Task task = taskService.getById(taskId);
Task task = taskService.getById(taskId); if (task == null) {
if (task == null) { throw new ServiceException("任务不存在!");
throw new ServiceException("任务不存在!");
}
Instance instance = insService.getById(task.getInstanceId());
if (ObjectUtil.isNotNull(instance)) {
BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus());
}
FlowParams flowParams = FlowParams.build()
.message(bo.getComment())
.flowStatus(BusinessStatusEnum.TERMINATION.getStatus())
.hisStatus(TaskStatusEnum.TERMINATION.getStatus());
taskService.termination(taskId, flowParams);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
Instance instance = insService.getById(task.getInstanceId());
if (ObjectUtil.isNotNull(instance)) {
BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus());
}
FlowParams flowParams = FlowParams.build()
.message(bo.getComment())
.flowStatus(BusinessStatusEnum.TERMINATION.getStatus())
.hisStatus(TaskStatusEnum.TERMINATION.getStatus());
taskService.termination(taskId, flowParams);
return true;
} }
/** /**
@@ -739,8 +748,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
Task task = taskService.getById(taskId); Task task = taskService.getById(taskId);
FlowNode flowNode = getByNodeCode(task.getNodeCode(), task.getDefinitionId()); FlowNode flowNode = getByNodeCode(task.getNodeCode(), task.getDefinitionId());
if (ADD_SIGNATURE.equals(taskOperation) || REDUCTION_SIGNATURE.equals(taskOperation)) { if (ADD_SIGNATURE.equals(taskOperation) || REDUCTION_SIGNATURE.equals(taskOperation)) {
if (flowNode.getNodeRatio().compareTo(BigDecimal.ZERO) == 0) { if (CooperateType.isOrSign(flowNode.getNodeRatio())) {
throw new ServiceException(task.getNodeName() + "不是会签节点!"); throw new ServiceException(task.getNodeName() + "不是会签或票签节点!");
} }
} }
// 设置任务状态并执行对应的任务操作 // 设置任务状态并执行对应的任务操作
@@ -784,23 +793,18 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
if (CollUtil.isEmpty(taskIdList)) { if (CollUtil.isEmpty(taskIdList)) {
return false; return false;
} }
try { List<FlowTask> flowTasks = this.selectByIdList(taskIdList);
List<FlowTask> flowTasks = this.selectByIdList(taskIdList); // 批量删除现有任务的办理人记录
// 批量删除现有任务的办理人记录 if (CollUtil.isNotEmpty(flowTasks)) {
if (CollUtil.isNotEmpty(flowTasks)) { FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId));
FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); List<User> userList = StreamUtils.toList(flowTasks, flowTask ->
List<User> userList = StreamUtils.toList(flowTasks, flowTask -> new FlowUser()
new FlowUser() .setType(TaskAssigneeType.APPROVER.getCode())
.setType(TaskAssigneeType.APPROVER.getCode()) .setProcessedBy(userId)
.setProcessedBy(userId) .setAssociated(flowTask.getId()));
.setAssociated(flowTask.getId())); if (CollUtil.isNotEmpty(userList)) {
if (CollUtil.isNotEmpty(userList)) { FlowEngine.userService().saveBatch(userList);
FlowEngine.userService().saveBatch(userList);
}
} }
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
return true; return true;
} }
@@ -840,21 +844,16 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
*/ */
@Override @Override
public boolean urgeTask(FlowUrgeTaskBo bo) { public boolean urgeTask(FlowUrgeTaskBo bo) {
try { if (CollUtil.isEmpty(bo.getTaskIdList())) {
if (CollUtil.isEmpty(bo.getTaskIdList())) { return false;
return false;
}
List<UserDTO> userList = this.currentTaskAllUser(bo.getTaskIdList());
if (CollUtil.isEmpty(userList)) {
return false;
}
List<String> messageType = bo.getMessageType();
String message = bo.getMessage();
flwCommonService.sendMessage(messageType, message, "单据审批提醒", userList);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
} }
List<UserDTO> userList = this.currentTaskAllUser(bo.getTaskIdList());
if (CollUtil.isEmpty(userList)) {
return false;
}
List<String> messageType = bo.getMessageType();
String message = bo.getMessage();
flwCommonService.sendMessage(messageType, message, "单据审批提醒", userList);
return true; return true;
} }
} }

View File

@@ -18,6 +18,7 @@ import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.WorkflowService; import org.dromara.common.core.service.WorkflowService;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
@@ -167,7 +168,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(List<Long> ids) { public Boolean deleteWithValidByIds(List<Long> ids) {
workflowService.deleteInstance(ids); workflowService.deleteInstance(StreamUtils.toList(ids, Convert::toStr));
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }
@@ -199,7 +200,10 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
testLeave.setApplyCode(businessCode); testLeave.setApplyCode(businessCode);
} }
testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus());
log.info("申请人提交");
} }
String status = BusinessStatusEnum.findByStatus(processEvent.getStatus());
log.info("当前流程状态为{}", status);
baseMapper.updateById(testLeave); baseMapper.updateById(testLeave);
} }

View File

@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.dto.CompleteTaskDTO; import org.dromara.common.core.domain.dto.CompleteTaskDTO;
import org.dromara.common.core.domain.dto.StartProcessDTO; import org.dromara.common.core.domain.dto.StartProcessDTO;
import org.dromara.common.core.domain.dto.StartProcessReturnDTO; import org.dromara.common.core.domain.dto.StartProcessReturnDTO;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.WorkflowService; import org.dromara.common.core.service.WorkflowService;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.warm.flow.orm.entity.FlowInstance; import org.dromara.warm.flow.orm.entity.FlowInstance;
@@ -46,7 +45,7 @@ public class WorkflowServiceImpl implements WorkflowService {
* @return 结果 * @return 结果
*/ */
@Override @Override
public boolean deleteInstance(List<Long> businessIds) { public boolean deleteInstance(List<String> businessIds) {
return flwInstanceService.deleteByBusinessIds(businessIds); return flwInstanceService.deleteByBusinessIds(businessIds);
} }
@@ -161,28 +160,19 @@ public class WorkflowServiceImpl implements WorkflowService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean startCompleteTask(StartProcessDTO startProcess) { public boolean startCompleteTask(StartProcessDTO startProcess) {
try { StartProcessBo processBo = new StartProcessBo();
StartProcessBo processBo = new StartProcessBo(); processBo.setBusinessId(startProcess.getBusinessId());
processBo.setBusinessId(startProcess.getBusinessId()); processBo.setFlowCode(startProcess.getFlowCode());
processBo.setFlowCode(startProcess.getFlowCode()); processBo.setVariables(startProcess.getVariables());
processBo.setVariables(startProcess.getVariables()); processBo.setHandler(startProcess.getHandler());
processBo.setHandler(startProcess.getHandler()); processBo.setBizExt(BeanUtil.toBean(startProcess.getBizExt(), FlowInstanceBizExt.class));
processBo.setBizExt(BeanUtil.toBean(startProcess.getBizExt(), FlowInstanceBizExt.class));
StartProcessReturnDTO result = flwTaskService.startWorkFlow(processBo); StartProcessReturnDTO result = flwTaskService.startWorkFlow(processBo);
CompleteTaskBo taskBo = new CompleteTaskBo(); CompleteTaskBo taskBo = new CompleteTaskBo();
taskBo.setTaskId(result.getTaskId()); taskBo.setTaskId(result.getTaskId());
taskBo.setMessageType(Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode())); taskBo.setMessageType(Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode()));
taskBo.setVariables(startProcess.getVariables()); taskBo.setVariables(startProcess.getVariables());
taskBo.setHandler(startProcess.getHandler()); taskBo.setHandler(startProcess.getHandler());
return flwTaskService.completeTask(taskBo);
boolean flag = flwTaskService.completeTask(taskBo);
if (!flag) {
throw new ServiceException("流程发起异常");
}
return true;
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
} }
} }

View File

@@ -27,7 +27,10 @@
d.flow_code, d.flow_code,
d.form_custom, d.form_custom,
d.category, d.category,
COALESCE(t.form_path, d.form_path) as form_path, COALESCE(
NULLIF(TRIM(t.form_path), ''),
NULLIF(TRIM(d.form_path), '')
) AS form_path,
d.version, d.version,
uu.processed_by, uu.processed_by,
uu.type, uu.type,

View File

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

View File

@@ -106,11 +106,6 @@ http {
proxy_buffering off; proxy_buffering off;
# 禁用代理缓存 # 禁用代理缓存
proxy_cache off; proxy_cache off;
# 按 IP 限制连接数(防 CC 攻击) 小型站10~20 就够 中型站50~100
limit_conn perip 20;
# 按 Server 限制总并发连接数 根据服务器的最大并发处理能力来定 太小会限制合法用户访问,太大会占满服务器资源
limit_conn perserver 500;
proxy_pass http://server/; proxy_pass http://server/;
} }

View File

@@ -738,7 +738,7 @@ CREATE TABLE sj_retry_summary
id number GENERATED ALWAYS AS IDENTITY, id number GENERATED ALWAYS AS IDENTITY,
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
group_name varchar2(64) DEFAULT '' NULL, group_name varchar2(64) DEFAULT '' NULL,
scene_name varchar2(50) DEFAULT '' NULL, scene_name varchar2(64) DEFAULT '' NULL,
trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL, trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL,
running_num number DEFAULT 0 NOT NULL, running_num number DEFAULT 0 NOT NULL,
finish_num number DEFAULT 0 NOT NULL, finish_num number DEFAULT 0 NOT NULL,

View File

@@ -55,13 +55,12 @@ create table FLOW_NODE
DEFINITION_ID NUMBER(20) not null, DEFINITION_ID NUMBER(20) not null,
NODE_CODE VARCHAR2(100) not null, NODE_CODE VARCHAR2(100) not null,
NODE_NAME VARCHAR2(100), NODE_NAME VARCHAR2(100),
NODE_RATIO NUMBER(6, 3), PERMISSION_FLAG VARCHAR2(200),
NODE_RATIO VARCHAR2(200),
COORDINATE VARCHAR2(100), COORDINATE VARCHAR2(100),
ANY_NODE_SKIP VARCHAR2(100), ANY_NODE_SKIP VARCHAR2(100),
LISTENER_TYPE VARCHAR2(100), LISTENER_TYPE VARCHAR2(100),
LISTENER_PATH VARCHAR2(500), LISTENER_PATH VARCHAR2(500),
HANDLER_TYPE VARCHAR2(100),
HANDLER_PATH VARCHAR2(400),
FORM_CUSTOM VARCHAR2(1) default 'N', FORM_CUSTOM VARCHAR2(1) default 'N',
FORM_PATH VARCHAR2(100), FORM_PATH VARCHAR2(100),
VERSION VARCHAR2(20), VERSION VARCHAR2(20),
@@ -71,8 +70,7 @@ create table FLOW_NODE
UPDATE_BY VARCHAR2(64) default '', UPDATE_BY VARCHAR2(64) default '',
EXT CLOB, EXT CLOB,
DEL_FLAG VARCHAR2(1) default '0', DEL_FLAG VARCHAR2(1) default '0',
TENANT_ID VARCHAR2(40), TENANT_ID VARCHAR2(40)
PERMISSION_FLAG VARCHAR2(200)
); );
alter table FLOW_NODE alter table FLOW_NODE
@@ -89,8 +87,6 @@ comment on column FLOW_NODE.COORDINATE is '坐标';
comment on column FLOW_NODE.ANY_NODE_SKIP is '任意结点跳转'; comment on column FLOW_NODE.ANY_NODE_SKIP is '任意结点跳转';
comment on column FLOW_NODE.LISTENER_TYPE is '监听器类型'; comment on column FLOW_NODE.LISTENER_TYPE is '监听器类型';
comment on column FLOW_NODE.LISTENER_PATH is '监听器路径'; comment on column FLOW_NODE.LISTENER_PATH is '监听器路径';
comment on column FLOW_NODE.HANDLER_TYPE is '处理器类型';
comment on column FLOW_NODE.HANDLER_PATH is '处理器路径';
comment on column FLOW_NODE.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; comment on column FLOW_NODE.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)';
comment on column FLOW_NODE.FORM_PATH is '审批表单路径'; comment on column FLOW_NODE.FORM_PATH is '审批表单路径';
comment on column FLOW_NODE.VERSION is '版本'; comment on column FLOW_NODE.VERSION is '版本';
@@ -493,11 +489,11 @@ INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '
INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, SYSDATE, 1, SYSDATE, '流程达式定义菜单'); INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, SYSDATE, 1, SYSDATE, '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11638', '请假申请', '5', '1', 'leave', 'workflow/leave/index', '', '1', '0', 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, SYSDATE, NULL, NULL, '请假申请菜单'); INSERT INTO sys_menu VALUES ('11638', '请假申请', '5', '1', 'leave', 'workflow/leave/index', '', '1', '0', 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, SYSDATE, NULL, NULL, '请假申请菜单');
INSERT INTO sys_menu VALUES ('11639', '请假申请查询', '11638', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11639', '请假申请查询', '11638', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, SYSDATE, NULL, NULL, '');

View File

@@ -684,7 +684,7 @@ CREATE TABLE sj_retry_summary
id bigserial PRIMARY KEY, id bigserial PRIMARY KEY,
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
group_name varchar(64) NOT NULL DEFAULT '', group_name varchar(64) NOT NULL DEFAULT '',
scene_name varchar(50) NOT NULL DEFAULT '', scene_name varchar(64) NOT NULL DEFAULT '',
trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
running_num int NOT NULL DEFAULT 0, running_num int NOT NULL DEFAULT 0,
finish_num int NOT NULL DEFAULT 0, finish_num int NOT NULL DEFAULT 0,

View File

@@ -51,13 +51,11 @@ CREATE TABLE flow_node
node_code varchar(100) NOT NULL, node_code varchar(100) NOT NULL,
node_name varchar(100) NULL, node_name varchar(100) NULL,
permission_flag varchar(200) NULL, permission_flag varchar(200) NULL,
node_ratio numeric(6, 3) NULL, node_ratio varchar(200) NULL,
coordinate varchar(100) NULL, coordinate varchar(100) NULL,
any_node_skip varchar(100) NULL, any_node_skip varchar(100) NULL,
listener_type varchar(100) NULL, listener_type varchar(100) NULL,
listener_path varchar(400) NULL, listener_path varchar(400) NULL,
handler_type varchar(100) NULL,
handler_path varchar(400) NULL,
form_custom bpchar(1) NULL DEFAULT 'N':: character varying, form_custom bpchar(1) NULL DEFAULT 'N':: character varying,
form_path varchar(100) NULL, form_path varchar(100) NULL,
"version" varchar(20) NOT NULL, "version" varchar(20) NOT NULL,
@@ -83,8 +81,6 @@ COMMENT ON COLUMN flow_node.coordinate IS '坐标';
COMMENT ON COLUMN flow_node.any_node_skip IS '任意结点跳转'; COMMENT ON COLUMN flow_node.any_node_skip IS '任意结点跳转';
COMMENT ON COLUMN flow_node.listener_type IS '监听器类型'; COMMENT ON COLUMN flow_node.listener_type IS '监听器类型';
COMMENT ON COLUMN flow_node.listener_path IS '监听器路径'; COMMENT ON COLUMN flow_node.listener_path IS '监听器路径';
COMMENT ON COLUMN flow_node.handler_type IS '处理器类型';
COMMENT ON COLUMN flow_node.handler_path IS '处理器路径';
COMMENT ON COLUMN flow_node.form_custom IS '审批表单是否自定义Y是 N否'; COMMENT ON COLUMN flow_node.form_custom IS '审批表单是否自定义Y是 N否';
COMMENT ON COLUMN flow_node.form_path IS '审批表单路径'; COMMENT ON COLUMN flow_node.form_path IS '审批表单路径';
COMMENT ON COLUMN flow_node."version" IS '版本'; COMMENT ON COLUMN flow_node."version" IS '版本';
@@ -471,11 +467,11 @@ INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '
INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, now(), 1, now(), '流程达式定义菜单'); INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, now(), 1, now(), '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11638', '请假申请', '5', '1', 'leave', 'workflow/leave/index', '', '1', '0', 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, now(), NULL, NULL, '请假申请菜单'); INSERT INTO sys_menu VALUES ('11638', '请假申请', '5', '1', 'leave', 'workflow/leave/index', '', '1', '0', 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, now(), NULL, NULL, '请假申请菜单');
INSERT INTO sys_menu VALUES ('11639', '请假申请查询', '11638', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11639', '请假申请查询', '11638', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, now(), NULL, NULL, '');

View File

@@ -423,7 +423,7 @@ CREATE TABLE `sj_retry_summary`
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', `namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id',
`group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称', `group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称',
`scene_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '场景名称', `scene_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '场景名称',
`trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间', `trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间',
`running_num` int NOT NULL DEFAULT 0 COMMENT '重试中-日志数量', `running_num` int NOT NULL DEFAULT 0 COMMENT '重试中-日志数量',
`finish_num` int NOT NULL DEFAULT 0 COMMENT '重试完成-日志数量', `finish_num` int NOT NULL DEFAULT 0 COMMENT '重试完成-日志数量',

View File

@@ -33,13 +33,11 @@ CREATE TABLE `flow_node`
`node_code` varchar(100) NOT NULL COMMENT '流程节点编码', `node_code` varchar(100) NOT NULL COMMENT '流程节点编码',
`node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称', `node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称',
`permission_flag` varchar(200) DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用@@隔开)', `permission_flag` varchar(200) DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用@@隔开)',
`node_ratio` decimal(6, 3) DEFAULT NULL COMMENT '流程签署比例值', `node_ratio` varchar(200) DEFAULT NULL COMMENT '流程签署比例值',
`coordinate` varchar(100) DEFAULT NULL COMMENT '坐标', `coordinate` varchar(100) DEFAULT NULL COMMENT '坐标',
`any_node_skip` varchar(100) DEFAULT NULL COMMENT '任意结点跳转', `any_node_skip` varchar(100) DEFAULT NULL COMMENT '任意结点跳转',
`listener_type` varchar(100) DEFAULT NULL COMMENT '监听器类型', `listener_type` varchar(100) DEFAULT NULL COMMENT '监听器类型',
`listener_path` varchar(400) DEFAULT NULL COMMENT '监听器路径', `listener_path` varchar(400) DEFAULT NULL COMMENT '监听器路径',
`handler_type` varchar(100) DEFAULT NULL COMMENT '处理器类型',
`handler_path` varchar(400) DEFAULT NULL COMMENT '处理器路径',
`form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义Y是 N否', `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义Y是 N否',
`form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径',
`version` varchar(20) NOT NULL COMMENT '版本', `version` varchar(20) NOT NULL COMMENT '版本',

View File

@@ -2248,7 +2248,7 @@ CREATE TABLE sj_retry_summary
id bigint NOT NULL PRIMARY KEY IDENTITY, id bigint NOT NULL PRIMARY KEY IDENTITY,
namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
group_name nvarchar(64) NOT NULL DEFAULT '', group_name nvarchar(64) NOT NULL DEFAULT '',
scene_name nvarchar(50) NOT NULL DEFAULT '', scene_name nvarchar(64) NOT NULL DEFAULT '',
trigger_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, trigger_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP,
running_num int NOT NULL DEFAULT 0, running_num int NOT NULL DEFAULT 0,
finish_num int NOT NULL DEFAULT 0, finish_num int NOT NULL DEFAULT 0,

View File

@@ -174,13 +174,11 @@ CREATE TABLE flow_node (
node_code nvarchar(100) NOT NULL, node_code nvarchar(100) NOT NULL,
node_name nvarchar(100) NULL, node_name nvarchar(100) NULL,
permission_flag nvarchar(200) NULL, permission_flag nvarchar(200) NULL,
node_ratio decimal(6,3) NULL, node_ratio nvarchar(200) NULL,
coordinate nvarchar(100) NULL, coordinate nvarchar(100) NULL,
any_node_skip nvarchar(100) NULL, any_node_skip nvarchar(100) NULL,
listener_type nvarchar(100) NULL, listener_type nvarchar(100) NULL,
listener_path nvarchar(400) NULL, listener_path nvarchar(400) NULL,
handler_type nvarchar(100) NULL,
handler_path nvarchar(400) NULL,
form_custom nchar(1) DEFAULT('N') NULL, form_custom nchar(1) DEFAULT('N') NULL,
form_path nvarchar(100) NULL, form_path nvarchar(100) NULL,
version nvarchar(20) NOT NULL, version nvarchar(20) NOT NULL,
@@ -275,20 +273,6 @@ EXEC sp_addextendedproperty
'COLUMN', N'listener_path' 'COLUMN', N'listener_path'
GO GO
EXEC sp_addextendedproperty
'MS_Description', N'处理器类型',
'SCHEMA', N'dbo',
'TABLE', N'flow_node',
'COLUMN', N'handler_type'
GO
EXEC sp_addextendedproperty
'MS_Description', N'处理器路径',
'SCHEMA', N'dbo',
'TABLE', N'flow_node',
'COLUMN', N'handler_path'
GO
EXEC sp_addextendedproperty EXEC sp_addextendedproperty
'MS_Description', N'审批表单是否自定义Y是 N否', 'MS_Description', N'审批表单是否自定义Y是 N否',
'SCHEMA', N'dbo', 'SCHEMA', N'dbo',
@@ -1261,7 +1245,7 @@ EXEC sp_addextendedproperty
'COLUMN', N'component_name' 'COLUMN', N'component_name'
GO GO
-- method_name 字段注释 EXEC sp_addextendedproperty
'MS_Description', N'方法名', 'MS_Description', N'方法名',
'SCHEMA', N'dbo', 'SCHEMA', N'dbo',
'TABLE', N'flow_spel', 'TABLE', N'flow_spel',
@@ -1618,15 +1602,15 @@ GO
INSERT sys_menu VALUES (11801, N'流程表达式', N'11616', 2, N'spel', N'workflow/spel/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:spel:list', N'input', 103, 1, GETDATE(), 1, GETDATE(), N'流程达式定义菜单'); INSERT sys_menu VALUES (11801, N'流程表达式', N'11616', 2, N'spel', N'workflow/spel/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:spel:list', N'input', 103, 1, GETDATE(), 1, GETDATE(), N'流程达式定义菜单');
GO GO
INSERT sys_menu VALUES (11802, N'流程spel达式定义查询', N'11801', 1, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:query', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11802, N'流程spel达式定义查询', N'11801', 1, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11803, N'流程spel达式定义新增', N'11801', 2, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:add', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11803, N'流程spel达式定义新增', N'11801', 2, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:add', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11804, N'流程spel达式定义修改', N'11801', 3, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11804, N'流程spel达式定义修改', N'11801', 3, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11805, N'流程spel达式定义删除', N'11801', 4, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11805, N'流程spel达式定义删除', N'11801', 4, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11806, N'流程spel达式定义导出', N'11801', 5, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:export', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11806, N'流程spel达式定义导出', N'11801', 5, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:export', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
-- 请假测试相关按钮 -- 请假测试相关按钮

View File

@@ -38,11 +38,11 @@ INSERT INTO flow_spel VALUES (1, 'spelRuleComponent', 'selectDeptLeaderById', 'i
INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, SYSDATE, 1, SYSDATE); INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, SYSDATE, 1, SYSDATE);
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, SYSDATE, 1, SYSDATE, '流程达式定义菜单'); INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, SYSDATE, 1, SYSDATE, '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
ALTER TABLE flow_definition ADD model_value VARCHAR2(40) DEFAULT 'CLASSICS' NOT NULL; ALTER TABLE flow_definition ADD model_value VARCHAR2(40) DEFAULT 'CLASSICS' NOT NULL;
COMMENT ON COLUMN flow_definition.model_value IS '设计器模式CLASSICS经典模式 MIMIC仿钉钉模式'; COMMENT ON COLUMN flow_definition.model_value IS '设计器模式CLASSICS经典模式 MIMIC仿钉钉模式';

View File

@@ -0,0 +1,4 @@
ALTER TABLE flow_node MODIFY (node_ratio VARCHAR2(200) DEFAULT NULL NULL);
COMMENT ON COLUMN flow_node.node_ratio IS '流程签署比例值';
ALTER TABLE flow_node DROP COLUMN handler_type;
ALTER TABLE flow_node DROP COLUMN handler_path;

View File

@@ -37,11 +37,11 @@ INSERT INTO flow_spel VALUES (1, 'spelRuleComponent', 'selectDeptLeaderById', 'i
INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, now(), 1, now()); INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, now(), 1, now());
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, now(), 1, now(), '流程达式定义菜单'); INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, now(), 1, now(), '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, now(), NULL, NULL, '');
ALTER TABLE flow_definition ADD COLUMN model_value VARCHAR(40) NOT NULL DEFAULT 'CLASSICS'; ALTER TABLE flow_definition ADD COLUMN model_value VARCHAR(40) NOT NULL DEFAULT 'CLASSICS';
COMMENT ON COLUMN flow_definition.model_value IS '设计器模式CLASSICS经典模式 MIMIC仿钉钉模式'; COMMENT ON COLUMN flow_definition.model_value IS '设计器模式CLASSICS经典模式 MIMIC仿钉钉模式';

View File

@@ -1,25 +1,25 @@
ALTER TABLE flow_definition ADD create_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_definition ADD create_by VARCHAR(64) DEFAULT '' NOT NULL;
ALTER TABLE flow_definition ADD update_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_definition ADD update_by VARCHAR(64) DEFAULT '' NOT NULL;
COMMENT ON COLUMN flow_definition.create_by IS '创建人'; COMMENT ON COLUMN flow_definition.create_by IS '创建人';
COMMENT ON COLUMN flow_definition.update_by IS '更新人'; COMMENT ON COLUMN flow_definition.update_by IS '更新人';
ALTER TABLE flow_node ADD create_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_node ADD create_by VARCHAR(64) DEFAULT '' NOT NULL;
ALTER TABLE flow_node ADD update_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_node ADD update_by VARCHAR(64) DEFAULT '' NOT NULL;
COMMENT ON COLUMN flow_node.create_by IS '创建人'; COMMENT ON COLUMN flow_node.create_by IS '创建人';
COMMENT ON COLUMN flow_node.update_by IS '更新人'; COMMENT ON COLUMN flow_node.update_by IS '更新人';
ALTER TABLE flow_skip ADD create_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_skip ADD create_by VARCHAR(64) DEFAULT '' NOT NULL;
ALTER TABLE flow_skip ADD update_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_skip ADD update_by VARCHAR(64) DEFAULT '' NOT NULL;
COMMENT ON COLUMN flow_skip.create_by IS '创建人'; COMMENT ON COLUMN flow_skip.create_by IS '创建人';
COMMENT ON COLUMN flow_skip.update_by IS '更新人'; COMMENT ON COLUMN flow_skip.update_by IS '更新人';
ALTER TABLE flow_instance ADD update_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_instance ADD update_by VARCHAR(64) DEFAULT '' NOT NULL;
COMMENT ON COLUMN flow_instance.update_by IS '更新人'; COMMENT ON COLUMN flow_instance.update_by IS '更新人';
ALTER TABLE flow_task ADD create_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_task ADD create_by VARCHAR(64) DEFAULT '' NOT NULL;
ALTER TABLE flow_task ADD update_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_task ADD update_by VARCHAR(64) DEFAULT '' NOT NULL;
COMMENT ON COLUMN flow_task.create_by IS '创建人'; COMMENT ON COLUMN flow_task.create_by IS '创建人';
COMMENT ON COLUMN flow_task.update_by IS '更新人'; COMMENT ON COLUMN flow_task.update_by IS '更新人';
ALTER TABLE flow_user ADD update_by VARCHAR2(64) DEFAULT '' NOT NULL; ALTER TABLE flow_user ADD update_by VARCHAR(64) DEFAULT '' NOT NULL;
COMMENT ON COLUMN flow_user.update_by IS '更新人'; COMMENT ON COLUMN flow_user.update_by IS '更新人';

View File

@@ -0,0 +1,6 @@
ALTER TABLE flow_node
ALTER COLUMN node_ratio TYPE VARCHAR(200),
ALTER COLUMN node_ratio DROP NOT NULL;
COMMENT ON COLUMN flow_node.node_ratio IS '流程签署比例值';
ALTER TABLE flow_node DROP COLUMN handler_type;
ALTER TABLE flow_node DROP COLUMN handler_path;

View File

@@ -123,15 +123,15 @@ GO
INSERT sys_menu VALUES (11801, N'流程表达式', N'11616', 2, N'spel', N'workflow/spel/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:spel:list', N'input', 103, 1, GETDATE(), 1, GETDATE(), N'流程达式定义菜单'); INSERT sys_menu VALUES (11801, N'流程表达式', N'11616', 2, N'spel', N'workflow/spel/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:spel:list', N'input', 103, 1, GETDATE(), 1, GETDATE(), N'流程达式定义菜单');
GO GO
INSERT sys_menu VALUES (11802, N'流程spel达式定义查询', N'11801', 1, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:query', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11802, N'流程spel达式定义查询', N'11801', 1, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11803, N'流程spel达式定义新增', N'11801', 2, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:add', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11803, N'流程spel达式定义新增', N'11801', 2, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:add', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11804, N'流程spel达式定义修改', N'11801', 3, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11804, N'流程spel达式定义修改', N'11801', 3, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11805, N'流程spel达式定义删除', N'11801', 4, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11805, N'流程spel达式定义删除', N'11801', 4, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
INSERT sys_menu VALUES (11806, N'流程spel达式定义导出', N'11801', 5, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:export', N'#', 103, 1, GETDATE(), NULL, NULL, N''); INSERT sys_menu VALUES (11806, N'流程spel达式定义导出', N'11801', 5, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:export', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
GO GO
ALTER TABLE flow_definition ADD model_value VARCHAR(40) NOT NULL CONSTRAINT DF_flow_definition_model_value DEFAULT 'CLASSICS'; ALTER TABLE flow_definition ADD model_value VARCHAR(40) NOT NULL CONSTRAINT DF_flow_definition_model_value DEFAULT 'CLASSICS';

View File

@@ -0,0 +1,22 @@
ALTER TABLE flow_node ALTER COLUMN node_ratio nvarchar(200) NULL;
GO
IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description',
'SCHEMA', N'dbo',
'TABLE', N'flow_his_task',
'COLUMN', N'collaborator')) > 0)
EXEC sp_updateextendedproperty
'MS_Description', N'流程签署比例值',
'SCHEMA', N'dbo',
'TABLE', N'flow_node',
'COLUMN', N'node_ratio'
ELSE
EXEC sp_addextendedproperty
'MS_Description', N'流程签署比例值',
'SCHEMA', N'dbo',
'TABLE', N'flow_node',
'COLUMN', N'node_ratio'
GO
ALTER TABLE flow_node DROP COLUMN handler_type;
GO
ALTER TABLE flow_node DROP COLUMN handler_path;
GO

View File

@@ -22,11 +22,11 @@ INSERT INTO flow_spel VALUES (1, 'spelRuleComponent', 'selectDeptLeaderById', 'i
INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, sysdate(), 1, sysdate()); INSERT INTO flow_spel VALUES (2, NULL, NULL, 'initiator', '${initiator}', '流程发起人', '0', '0', 103, 1, sysdate(), 1, sysdate());
INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', '2', 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, sysdate(), 1, sysdate(), '流程达式定义菜单'); INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', '2', 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, sysdate(), 1, sysdate(), '流程达式定义菜单');
INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, sysdate(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11802', '流程spel达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, sysdate(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11803', '流程spel达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, sysdate(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11804', '流程spel达式定义修改', '11801', 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:edit', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, sysdate(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11805', '流程spel达式定义删除', '11801', 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:remove', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, sysdate(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11806', '流程spel达式定义导出', '11801', 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:export', '#', 103, 1, sysdate(), NULL, NULL, '');
ALTER TABLE `flow_definition` ALTER TABLE `flow_definition`
ADD COLUMN `model_value` varchar(40) NOT NULL DEFAULT 'CLASSICS' COMMENT '设计器模式CLASSICS经典模式 MIMIC仿钉钉模式' AFTER `flow_name`; ADD COLUMN `model_value` varchar(40) NOT NULL DEFAULT 'CLASSICS' COMMENT '设计器模式CLASSICS经典模式 MIMIC仿钉钉模式' AFTER `flow_name`;

View File

@@ -0,0 +1,3 @@
ALTER TABLE flow_node MODIFY COLUMN node_ratio varchar(200) NULL COMMENT '流程签署比例值';
ALTER TABLE flow_node DROP COLUMN handler_type;
ALTER TABLE flow_node DROP COLUMN handler_path;