update warm-flow 2.1.1

This commit is contained in:
疯狂的狮子Li 2026-11-03 12:37:54 +08:00
parent 42b5dd19fa
commit 710287533f
8 changed files with 123 additions and 9 deletions

View File

@ -62,7 +62,7 @@
<!-- SMS 配置 -->
<sms4j.version>3.3.5</sms4j.version>
<!-- 工作流配置 -->
<warm-flow.version>2.1.0</warm-flow.version>
<warm-flow.version>2.1.1</warm-flow.version>
<liteflow.version>2.16.3.1</liteflow.version>
<!-- mqtt客户端 -->
<mica-mqtt.version>2.6.12</mica-mqtt.version>

View File

@ -27,7 +27,7 @@ import java.util.Map;
/**
* 定时任务(事件等待记录)控制器。
* <p>数据来源为引擎事件等待表,默认只查定时类事件;消息类 / 错误类等待需显式放开事件类型范围。
* 写操作只有一项:对定时启动任务手动补发一次实例——补发不读取、也不推进等待记录,
* 支持定时启动任务手动补发,以及已死事件等待记录复活;补发不推进等待记录,
* 定时任务的下次触发时间不受影响。</p>
*
* @author may
@ -92,4 +92,19 @@ public class FlwEventWaitController extends BaseController {
public R<Long> trigger(@RequestBody FlowTimerTriggerBo bo) {
return R.ok(flwEventWaitService.manualTrigger(bo));
}
/**
* 复活已死的事件等待记录,定时启动任务需通过重新发布或挂起后激活恢复。
*
* @param id 事件等待记录主键
* @return 操作结果
*/
@SaCheckPermission("workflow:eventWait:trigger")
@Log(title = "事件等待记录复活", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/{id}/revive")
public R<Void> revive(@NotNull(message = "主键不能为空") @PathVariable Long id) {
flwEventWaitService.revive(id);
return R.ok();
}
}

View File

@ -47,8 +47,7 @@ public class DemoTimerInspectHandler implements EventTaskHandler {
if (isInspectTimerStart(wait)) {
// 这里应该使用业务的id demo使用自动生成id 需要用户自行替换
String businessId = String.valueOf(IdWorker.getId());
WorkflowContext context = new WorkflowContext();
context.setHandler(FlowEngine.getFlowConfig().getSystemHandler());
WorkflowContext context = WorkflowContext.create();
FlowEngine.insService().startByTimer(businessId, wait.getDefinitionId(),
wait.getNodeCode(), null, context);
log.info("定时启动示例已自动发起审批: businessId={}", businessId);

View File

@ -9,7 +9,7 @@ import java.util.Map;
/**
* 定时任务(事件等待记录)服务接口。
* <p>数据来自引擎 flow_event_wait,页面提供查询、状态统计与定时启动任务的手动补发。
* <p>数据来自引擎 flow_event_wait,提供查询、状态统计、已死记录复活与定时启动任务的手动补发。
* 手动补发只创建一次实例,不读取也不推进等待记录,定时任务的下次触发时间不受影响。</p>
*
* @author may
@ -50,4 +50,11 @@ public interface IFlwEventWaitService {
* @return 新建流程实例主键
*/
Long manualTrigger(FlowTimerTriggerBo bo);
/**
* 复活已死的事件等待记录,状态与实例有效性由引擎校验。
*
* @param id 事件等待记录主键
*/
void revive(Long id);
}

View File

@ -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());
}
}
/**
* 统计某一状态下的等待记录数量。
*

View File

@ -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<FlowEngine> engine = mockStatic(FlowEngine.class)) {
engine.when(FlowEngine::workflow).thenReturn(new WorkflowServiceImpl());
engine.when(FlowEngine::eventWaitService).thenReturn(eventService);
return assertThrows(ServiceException.class, () -> new FlwEventWaitServiceImpl().revive(12L));
}
}
}

View File

@ -387,4 +387,5 @@ INSERT INTO sys_dict_data VALUES (1761600000000000064, 9, '转办', 'transfer',
INSERT INTO sys_dict_data VALUES (1761600000000000065, 10, '委派', 'delegate', 'wf_task_status', '', 'warning', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '委派');
INSERT INTO sys_dict_data VALUES (1761600000000000066, 11, '加签', 'add_signer', 'wf_task_status', '', 'warning', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '加签');
INSERT INTO sys_dict_data VALUES (1761600000000000067, 12, '减签', 'remove_signer', 'wf_task_status', '', 'warning', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '减签');
INSERT INTO sys_dict_data VALUES (1761600000000000068, 13, '已终止', 'termination', 'wf_task_status', '', 'danger', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '终止归档留痕');

View File

@ -76,8 +76,8 @@ INSERT INTO sys_dict_data VALUES (1761600000000000066, 11, '加签', 'add_signer
INSERT INTO sys_dict_data VALUES (1761600000000000067, 12, '减签', 'remove_signer', 'wf_task_status', '', 'warning', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '减签');
-- 原来的 waiting / termination / transfer / depute / sign / sign_off / timeout 已随上面整组删除:
-- 前两个属实例维度(wf_business_status 有),其余属协作维度 cooperate_type(transfer / delegate /
-- add_signer / remove_signer),timeout 是 flow_event_wait 的状态,都不是任务状态。
-- waiting 属实例维度;termination 也是终止归档留痕使用的编码,在下方 2.1.1 适配段补回。
-- 协作动作使用 transfer / delegate / add_signer / remove_signer,timeout 不属于任务状态。
-- take_back 目前没有写入方,是留给「拿回」功能的占位,别当冗余删掉。
-- 5. 实例状态字典补 restart(引擎 FlowStatus 有,字典缺)
@ -130,3 +130,16 @@ DELETE FROM sys_menu WHERE menu_id IN (1761400000000011901, 1761400000000011902,
insert into sys_menu values (1761400000000011901, '定时任务', 1761400000000011630, 3, 'timer', 'workflow/timer/index', '', 'N', 'N', 'C', '0', '0', 'workflow:eventWait:list', 'time', '', '', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '定时任务与事件等待监控菜单');
insert into sys_menu values (1761400000000011902, '定时任务查询', 1761400000000011901, 1, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:eventWait:query', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
insert into sys_menu values (1761400000000011903, '定时任务手动补发', 1761400000000011901, 2, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:eventWait:trigger', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
-- 8. Warm-Flow 2.1.1 适配(无引擎表结构变更)
-- 已执行过本脚本 2.1.0 迁移的数据库只需执行本段,无需重跑前面的迁移。
-- 补充任务终止归档字典,执行后刷新字典缓存;恢复接口复用页面权限 workflow:eventWait:list。
INSERT INTO sys_dict_data
SELECT 1761600000000000068, 13, '已终止', 'termination', 'wf_task_status', '', 'danger', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '终止归档留痕'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'wf_task_status' AND dict_value = 'termination');
-- 存量数据检查(仅查询,不自动改写审批历史):2.1.0 可能把实例 waiting 写入待办状态。
SELECT id, instance_id, node_code, flow_status FROM flow_task WHERE flow_status = 'waiting';
-- 确认上述记录为待审批任务后,可在备份并暂停流程写入的维护窗口执行:
-- UPDATE flow_task SET flow_status = 'pending' WHERE flow_status = 'waiting';
-- 旧撤回历史 cancel 保留原值及字典,新撤回历史由引擎写 back;终止归档仍写 termination。