From 710287533ffc5aea18422512fec6801a5485dd81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Tue, 3 Nov 2026 12:37:54 +0800
Subject: [PATCH] update warm-flow 2.1.1
---
pom.xml | 2 +-
.../controller/FlwEventWaitController.java | 17 ++++-
.../demo/DemoTimerInspectHandler.java | 3 +-
.../service/IFlwEventWaitService.java | 9 ++-
.../service/impl/FlwEventWaitServiceImpl.java | 21 ++++++-
.../impl/FlwEventWaitServiceImplTest.java | 62 +++++++++++++++++++
script/sql/ry_workflow.sql | 1 +
script/sql/update/update_6.0.0-6.1.0.sql | 17 ++++-
8 files changed, 123 insertions(+), 9 deletions(-)
create mode 100644 ruoyi-modules/ruoyi-workflow/src/test/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImplTest.java
diff --git a/pom.xml b/pom.xml
index 9185289ee..295a50e0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@
数据来源为引擎事件等待表,默认只查定时类事件;消息类 / 错误类等待需显式放开事件类型范围。 - * 写操作只有一项:对定时启动任务手动补发一次实例——补发不读取、也不推进等待记录, + * 支持定时启动任务手动补发,以及已死事件等待记录复活;补发不推进等待记录, * 定时任务的下次触发时间不受影响。
* * @author may @@ -92,4 +92,19 @@ public class FlwEventWaitController extends BaseController { public R数据来自引擎 flow_event_wait,页面提供查询、状态统计与定时启动任务的手动补发。 + *
数据来自引擎 flow_event_wait,提供查询、状态统计、已死记录复活与定时启动任务的手动补发。 * 手动补发只创建一次实例,不读取也不推进等待记录,定时任务的下次触发时间不受影响。
* * @author may @@ -50,4 +50,11 @@ public interface IFlwEventWaitService { * @return 新建流程实例主键 */ Long manualTrigger(FlowTimerTriggerBo bo); + + /** + * 复活已死的事件等待记录,状态与实例有效性由引擎校验。 + * + * @param id 事件等待记录主键 + */ + void revive(Long id); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImpl.java index d686c77a4..b5ac228ee 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImpl.java @@ -16,6 +16,7 @@ import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.enums.EventType; import org.dromara.warm.flow.core.exception.FlowException; import org.dromara.warm.flow.core.utils.page.Page; +import org.dromara.warm.flow.core.workflow.command.ReviveEventCommand; import org.dromara.warm.flow.core.workflow.context.WorkflowContext; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.domain.bo.FlowEventWaitBo; @@ -156,8 +157,7 @@ public class FlwEventWaitServiceImpl implements IFlwEventWaitService { String finalBusinessId = StringUtils.isNotBlank(bo.getBusinessId()) ? bo.getBusinessId().trim() : String.valueOf(IdWorker.getId()); - WorkflowContext context = new WorkflowContext(); - context.setHandler(LoginHelper.getUserIdStr()); + WorkflowContext context = WorkflowContext.create(LoginHelper.getUserIdStr()); try { Instance instance = FlowEngine.insService().startByTimer(finalBusinessId, wait.getDefinitionId() , wait.getNodeCode(), bo.getVariables(), context); @@ -168,6 +168,23 @@ public class FlwEventWaitServiceImpl implements IFlwEventWaitService { } } + /** + * 复活已死的事件等待记录,复用引擎的状态校验、排期重算与乐观锁更新。 + * + * @param id 事件等待记录主键 + */ + @Override + public void revive(Long id) { + ReviveEventCommand command = new ReviveEventCommand(); + command.setEventWaitId(id); + try { + FlowEngine.workflow().reviveEvent(command); + } catch (FlowException e) { + log.warn("事件等待记录复活失败 waitId={} 原因={}", id, e.getMessage()); + throw new ServiceException(e.getMessage()); + } + } + /** * 统计某一状态下的等待记录数量。 * diff --git a/ruoyi-modules/ruoyi-workflow/src/test/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImplTest.java b/ruoyi-modules/ruoyi-workflow/src/test/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImplTest.java new file mode 100644 index 000000000..939e6595b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/test/java/org/dromara/workflow/service/impl/FlwEventWaitServiceImplTest.java @@ -0,0 +1,62 @@ +package org.dromara.workflow.service.impl; + +import org.dromara.common.core.exception.ServiceException; +import org.dromara.warm.flow.core.FlowEngine; +import org.dromara.warm.flow.core.constant.EventCons; +import org.dromara.warm.flow.core.entity.EventWait; +import org.dromara.warm.flow.core.enums.EventType; +import org.dromara.warm.flow.core.service.EventWaitService; +import org.dromara.warm.flow.core.service.impl.EventWaitServiceImpl; +import org.dromara.warm.flow.core.workflow.WorkflowServiceImpl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.MockedStatic; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +class FlwEventWaitServiceImplTest { + + @ParameterizedTest + @ValueSource(strings = {"WAITING", "RETRY", "RUNNING", "CANCELLED", "SUCCESS"}) + void recoveryRejectsRecordsThatAreNotDead(String status) { + ServiceException error = recover(status, EventType.TIMER_CATCH); + assertTrue(error.getMessage().contains("DEAD"), error.getMessage()); + } + + @Test + void deadTimerStartMustBeRebuiltFromDefinition() { + ServiceException error = recover(EventCons.STATUS_DEAD, EventType.TIMER_START); + assertTrue(error.getMessage().contains("定时启动"), error.getMessage()); + } + + @Test + void deadEventCannotResumeWithoutItsInstance() { + ServiceException error = recover(EventCons.STATUS_DEAD, EventType.MESSAGE_CATCH); + assertTrue(error.getMessage().contains("实例"), error.getMessage()); + } + + /** + * 使用实际 2.1.1 门面和事件服务验证恢复约束,避免宿主绕过引擎直接改状态。 + */ + private ServiceException recover(String status, EventType type) { + EventWait wait = mock(EventWait.class); + when(wait.getStatus()).thenReturn(status); + when(wait.getEventType()).thenReturn(type.getValue()); + when(wait.getAttachedTaskId()).thenReturn(null); + when(wait.getInstanceId()).thenReturn(null); + EventWaitService eventService = spy(new EventWaitServiceImpl()); + doReturn(wait).when(eventService).getById(12L); + try (MockedStatic