mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
feat(pigx-flow-api): 新增延时器节点支持
- 新增 TimerConfig 和 TimerDuration DTO 类,用于配置延时器参数 - 在 Node 类中增加 timerConfig 字段,支持延时器配置 - 新增 TIMER 节点类型枚举值 feat(pigx-flow-biz): 实现延时器节点功能 - 在 ModelUtil 中新增 buildTimerNode 方法,支持构建延时器节点 - 新增 TimerHandler 组件,处理表单字段日期格式转换 - 支持三种延时模式:固定时长、指定日期时间、表单字段日期 - 优化连线名称生成逻辑,避免重复设置
This commit is contained in:
1 parent
ecdbfaca68
commit
b28d86cb59
6 files changed
+208
-6
No files matched your search
+6
-1
@@ -46,7 +46,12 @@ public enum NodeTypeEnum {
|
||||
* 并行分支 - 同时执行多条分支,所有分支都完成后才能继续后续流程
|
||||
*/
|
||||
PARALLEL_GATEWAY("并行分支", 5, true),
|
||||
|
||||
|
||||
/**
|
||||
* 延时器节点 - 流程暂停等待指定时间后自动继续
|
||||
*/
|
||||
TIMER("延时器", 10, false),
|
||||
|
||||
/**
|
||||
* 空节点 - 占位节点,用于流程设计时的过渡
|
||||
*/
|
||||
|
||||
@@ -150,4 +150,14 @@ public class Node {
|
||||
/** 规则名称 */
|
||||
private String ruleName;
|
||||
|
||||
/**
|
||||
* 延时器配置
|
||||
* <p>
|
||||
* 包含延时类型(timerType)和对应参数:
|
||||
* - DURATION: 固定时长延时,包含value和unit
|
||||
* - DATETIME: 指定日期时间延时
|
||||
* - FORM_FIELD: 表单字段日期延时
|
||||
*/
|
||||
private TimerConfig timerConfig;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.pig4cloud.pigx.flow.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 延时器配置
|
||||
*
|
||||
* @author pigx
|
||||
*/
|
||||
@Data
|
||||
public class TimerConfig {
|
||||
|
||||
/**
|
||||
* 延时类型: DURATION-固定时长, DATETIME-指定日期时间, FORM_FIELD-表单字段日期
|
||||
*/
|
||||
private String timerType;
|
||||
|
||||
/**
|
||||
* 固定时长配置 当timerType=DURATION时使用
|
||||
*/
|
||||
private TimerDuration duration;
|
||||
|
||||
/**
|
||||
* 指定日期时间 当timerType=DATETIME时使用,ISO 8601格式
|
||||
*/
|
||||
private String dateTime;
|
||||
|
||||
/**
|
||||
* 表单字段ID 当timerType=FORM_FIELD时使用
|
||||
*/
|
||||
private String formFieldId;
|
||||
|
||||
/**
|
||||
* 表单字段名称 用于显示
|
||||
*/
|
||||
private String formFieldName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.pig4cloud.pigx.flow.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 延时器固定时长配置
|
||||
*
|
||||
* @author pigx
|
||||
*/
|
||||
@Data
|
||||
public class TimerDuration {
|
||||
|
||||
/**
|
||||
* 时长数值
|
||||
*/
|
||||
private Integer value;
|
||||
|
||||
/**
|
||||
* 时长单位: MINUTE-分钟, HOUR-小时, DAY-天, WEEK-周
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.pig4cloud.pigx.flow.support.node;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 延时器日期处理器
|
||||
* <p>
|
||||
* 将表单字段中的日期值转换为Flowable可解析的ISO 8601格式。
|
||||
* 表单字段通常存储为 "yyyy-MM-dd HH:mm:ss" 格式,
|
||||
* 而Flowable的timeDate要求ISO 8601格式 "yyyy-MM-ddTHH:mm:ss"。
|
||||
* </p>
|
||||
* <p>
|
||||
* 使用方式:
|
||||
* 在TimerEventDefinition的timeDate中配置:${timerHandler.resolveDate(execution, 'formFieldId')}
|
||||
* </p>
|
||||
*
|
||||
* @author pigx
|
||||
*/
|
||||
@Component("timerHandler")
|
||||
@Slf4j
|
||||
public class TimerHandler {
|
||||
|
||||
/**
|
||||
* 从流程变量中获取日期字段值并转换为ISO 8601格式
|
||||
*
|
||||
* @param execution 流程执行上下文
|
||||
* @param fieldId 表单字段ID
|
||||
* @return ISO 8601格式日期字符串
|
||||
*/
|
||||
public String resolveDate(DelegateExecution execution, String fieldId) {
|
||||
Object value = execution.getVariable(fieldId);
|
||||
if (value == null) {
|
||||
log.warn("延时器表单字段值为空,fieldId: {},流程实例ID: {}", fieldId, execution.getProcessInstanceId());
|
||||
return null;
|
||||
}
|
||||
|
||||
String dateStr = value.toString().trim();
|
||||
if (StrUtil.isBlank(dateStr)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 已经是ISO 8601格式(包含T),直接返回
|
||||
if (dateStr.contains("T")) {
|
||||
return dateStr;
|
||||
}
|
||||
|
||||
// "yyyy-MM-dd HH:mm:ss" → "yyyy-MM-ddTHH:mm:ss"
|
||||
try {
|
||||
return LocalDateTimeUtil.parse(dateStr, DatePattern.NORM_DATETIME_FORMATTER).toString();
|
||||
} catch (Exception e) {
|
||||
log.error("延时器日期格式转换失败,原始值: {},fieldId: {}", dateStr, fieldId, e);
|
||||
return dateStr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+70
-5
@@ -9,6 +9,8 @@ import com.pig4cloud.pigx.flow.constant.NodeTypeEnum;
|
||||
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.dto.ProcessNodeDataDto;
|
||||
import com.pig4cloud.pigx.flow.dto.TimerConfig;
|
||||
import com.pig4cloud.pigx.flow.dto.TimerDuration;
|
||||
import com.pig4cloud.pigx.flow.service.IProcessNodeDataService;
|
||||
import com.pig4cloud.pigx.flow.support.expression.condition.NodeExpressionStrategyFactory;
|
||||
import com.pig4cloud.pigx.flow.support.listeners.ApprovalCreateListener;
|
||||
@@ -346,6 +348,10 @@ public class ModelUtil {
|
||||
if (node.getType() == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) {
|
||||
flowElementList.addAll(buildParallelGatewayNode(node));
|
||||
}
|
||||
// 延时器
|
||||
if (node.getType() == NodeTypeEnum.TIMER.getValue().intValue()) {
|
||||
flowElementList.add(buildTimerNode(node));
|
||||
}
|
||||
|
||||
return flowElementList;
|
||||
}
|
||||
@@ -684,6 +690,69 @@ public class ModelUtil {
|
||||
return serviceTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建延时器节点
|
||||
* <p>
|
||||
* 创建Flowable中间捕获事件(Timer Intermediate Catch Event),
|
||||
* 用于在流程中暂停执行,等待指定时间后自动继续。
|
||||
* <p>
|
||||
* 支持三种延时模式:
|
||||
* <ul>
|
||||
* <li>DURATION - 固定时长(ISO 8601格式,如PT30M、PT2H、P1D、P1W)</li>
|
||||
* <li>DATETIME - 指定日期时间(ISO 8601格式)</li>
|
||||
* <li>FORM_FIELD - 表单字段日期(使用流程变量表达式)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node 延时器节点数据
|
||||
* @return 中间捕获事件元素
|
||||
*/
|
||||
private static FlowElement buildTimerNode(Node node) {
|
||||
IntermediateCatchEvent catchEvent = new IntermediateCatchEvent();
|
||||
catchEvent.setId(node.getId());
|
||||
catchEvent.setName(node.getName());
|
||||
|
||||
TimerEventDefinition timerDef = new TimerEventDefinition();
|
||||
|
||||
TimerConfig config = node.getTimerConfig();
|
||||
if (config != null && StrUtil.isNotBlank(config.getTimerType())) {
|
||||
String timerType = config.getTimerType();
|
||||
|
||||
if ("DURATION".equals(timerType)) {
|
||||
TimerDuration duration = config.getDuration();
|
||||
if (duration != null && duration.getValue() != null) {
|
||||
int value = duration.getValue();
|
||||
String unit = duration.getUnit() != null ? duration.getUnit() : "MINUTE";
|
||||
String iso = switch (unit) {
|
||||
case "HOUR" -> StrUtil.format("PT{}H", value);
|
||||
case "DAY" -> StrUtil.format("P{}D", value);
|
||||
case "WEEK" -> StrUtil.format("P{}W", value);
|
||||
default -> StrUtil.format("PT{}M", value);
|
||||
};
|
||||
timerDef.setTimeDuration(iso);
|
||||
}
|
||||
} else if ("DATETIME".equals(timerType)) {
|
||||
String dateTime = config.getDateTime();
|
||||
if (StrUtil.isNotBlank(dateTime)) {
|
||||
timerDef.setTimeDate(dateTime);
|
||||
}
|
||||
} else if ("FORM_FIELD".equals(timerType)) {
|
||||
String formFieldId = config.getFormFieldId();
|
||||
if (StrUtil.isNotBlank(formFieldId)) {
|
||||
timerDef.setTimeDate("${timerHandler.resolveDate(execution, '" + formFieldId + "')}");
|
||||
}
|
||||
} else {
|
||||
log.warn("未知的延时器类型: {},使用默认30分钟", timerType);
|
||||
timerDef.setTimeDuration("PT30M");
|
||||
}
|
||||
} else {
|
||||
timerDef.setTimeDuration("PT30M");
|
||||
}
|
||||
|
||||
catchEvent.setEventDefinitions(List.of(timerDef));
|
||||
|
||||
return catchEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建节点内部连接线
|
||||
* <p>
|
||||
@@ -748,11 +817,7 @@ public class ModelUtil {
|
||||
}
|
||||
SequenceFlow sequenceFlow = new SequenceFlow(pId, childId);
|
||||
sequenceFlow.setConditionExpression(expression);
|
||||
sequenceFlow.setName(StrUtil.format("{}|{}", pId, childId));
|
||||
sequenceFlow.setName(StrUtil.format("连线[{}]", RandomUtil.randomString(5)));
|
||||
if (StrUtil.isNotBlank(name)) {
|
||||
sequenceFlow.setName(name);
|
||||
}
|
||||
sequenceFlow.setName(StrUtil.isNotBlank(name) ? name : StrUtil.format("连线[{}]", RandomUtil.randomString(5)));
|
||||
sequenceFlow.setId(StrUtil.format("sq-id-{}-{}", IdUtil.fastSimpleUUID(), RandomUtil.randomInt(1, 10000000)));
|
||||
return sequenceFlow;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user