mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-11-02 13:31:49 +08:00
feat(workflow): 新增定时任务(事件等待)管理接口与菜单
- 新增 FlwEventWaitController:/workflow/eventWait 分页列表、状态汇总、详情、 手动补发,权限分别为 workflow:eventWait:list / query / trigger - FlowEventWaitBo 支持按流程编码、节点编码、业务主键、事件类型、状态、 下次触发时间区间筛选;多值条件用逗号串传递 - FlowEventWaitVo 装配流程名称、业务主键,并透出 timerEnabled, 供前端在定时引擎未开启时给出提示 - 手动补发复用引擎 InsService#startByTimer,不读写等待记录, 下次触发时间不受影响;业务主键留空时生成 long 型雪花 ID - 四方言全量脚本补充「流程监控 → 定时任务」菜单与查询、手动补发按钮 - 升级脚本补充 flow_event_wait 建表(6.0.0 存量库没有该表)与定时任务菜单
This commit is contained in:
parent
d0a35db6c5
commit
1ee1289c4d
@ -0,0 +1,95 @@
|
||||
package org.dromara.workflow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.FlowEventWaitBo;
|
||||
import org.dromara.workflow.domain.bo.FlowTimerTriggerBo;
|
||||
import org.dromara.workflow.domain.vo.FlowEventWaitVo;
|
||||
import org.dromara.workflow.service.IFlwEventWaitService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 定时任务(事件等待记录)控制器。
|
||||
* <p>数据来源为引擎事件等待表,默认只查定时类事件;消息类 / 错误类等待需显式放开事件类型范围。
|
||||
* 写操作只有一项:对定时启动任务手动补发一次实例——补发不读取、也不推进等待记录,
|
||||
* 定时任务的下次触发时间不受影响。</p>
|
||||
*
|
||||
* @author may
|
||||
* @since 2026/10/21
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/workflow/eventWait")
|
||||
public class FlwEventWaitController extends BaseController {
|
||||
|
||||
private final IFlwEventWaitService flwEventWaitService;
|
||||
|
||||
/**
|
||||
* 分页查询定时任务(事件等待记录)列表。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 等待记录分页数据
|
||||
*/
|
||||
@SaCheckPermission("workflow:eventWait:list")
|
||||
@GetMapping("/list")
|
||||
public R<PageResult<FlowEventWaitVo>> list(FlowEventWaitBo bo) {
|
||||
return R.ok(flwEventWaitService.queryPageList(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按同一组筛选条件统计各状态等待记录数量。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 各状态计数
|
||||
*/
|
||||
@SaCheckPermission("workflow:eventWait:list")
|
||||
@GetMapping("/summary")
|
||||
public R<Map<String, Long>> summary(FlowEventWaitBo bo) {
|
||||
return R.ok(flwEventWaitService.summary(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询定时任务详情。
|
||||
*
|
||||
* @param id 事件等待记录主键
|
||||
* @return 等待记录详情
|
||||
*/
|
||||
@SaCheckPermission("workflow:eventWait:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<FlowEventWaitVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(flwEventWaitService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动补发一次定时启动任务(运维应急)。
|
||||
* <p>不推进等待记录,下一次定时触发照常进行;实例发起人记为当前登录用户。</p>
|
||||
*
|
||||
* @param bo 补发参数
|
||||
* @return 新建实例主键
|
||||
*/
|
||||
@SaCheckPermission("workflow:eventWait:trigger")
|
||||
@Log(title = "定时任务手动补发", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/trigger")
|
||||
public R<Long> trigger(@RequestBody FlowTimerTriggerBo bo) {
|
||||
return R.ok(flwEventWaitService.manualTrigger(bo));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.warm.flow.core.dto.EventWaitQuery;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定时任务(事件等待记录)查询业务对象。
|
||||
* <p>过滤维度与引擎 {@link EventWaitQuery} 一一对应:多值条件取「或」、不同条件之间取「且」;
|
||||
* 事件类型留空时按 {@link #timerOnly} 决定是否只查定时类事件。数据来自引擎 flow_event_wait,
|
||||
* 本对象只读,不含写操作字段。</p>
|
||||
*
|
||||
* @author may
|
||||
* @since 2026/10/21
|
||||
*/
|
||||
@Data
|
||||
public class FlowEventWaitBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 页码
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页条数
|
||||
*/
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 排序字段(引擎白名单:id / nextTriggerAt / retryAt / createTime / updateTime / status)
|
||||
*/
|
||||
private String orderByColumn;
|
||||
|
||||
/**
|
||||
* 排序方向 asc / desc
|
||||
*/
|
||||
private String isAsc;
|
||||
|
||||
/**
|
||||
* 等待状态多选(WAITING / RETRY / RUNNING / DEAD / CANCELLED),逗号分隔,留空表示不限
|
||||
*/
|
||||
private String statuses;
|
||||
|
||||
/**
|
||||
* 事件类型多选(TIMER_START / TIMER_CATCH / TIMER_BOUNDARY / …),逗号分隔,
|
||||
* 留空时按 timerOnly 取默认范围
|
||||
*/
|
||||
private String eventTypes;
|
||||
|
||||
/**
|
||||
* 是否只查定时类事件,默认 true
|
||||
*/
|
||||
private Boolean timerOnly = true;
|
||||
|
||||
/**
|
||||
* 流程定义编码
|
||||
*/
|
||||
private String flowCode;
|
||||
|
||||
/**
|
||||
* 业务流程主键
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 事件节点编码
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 事件名称(消息名 / 错误码)
|
||||
*/
|
||||
private String eventName;
|
||||
|
||||
/**
|
||||
* 下次触发时间下界
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date nextTriggerAtFrom;
|
||||
|
||||
/**
|
||||
* 下次触发时间上界
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date nextTriggerAtTo;
|
||||
|
||||
/**
|
||||
* 转换为引擎查询条件。
|
||||
*
|
||||
* @return 引擎只读查询条件
|
||||
*/
|
||||
public EventWaitQuery toQuery() {
|
||||
EventWaitQuery query = new EventWaitQuery();
|
||||
query.setPageNum(pageNum == null || pageNum < 1 ? 1 : pageNum);
|
||||
query.setPageSize(pageSize == null || pageSize < 1 ? 10 : pageSize);
|
||||
query.setStatuses(splitCsv(statuses));
|
||||
query.setEventTypes(splitCsv(eventTypes));
|
||||
query.setTimerOnly(!Boolean.FALSE.equals(timerOnly));
|
||||
query.setFlowCode(flowCode);
|
||||
query.setBusinessId(businessId);
|
||||
query.setNodeCode(nodeCode);
|
||||
query.setEventName(eventName);
|
||||
query.setNextTriggerAtFrom(nextTriggerAtFrom);
|
||||
query.setNextTriggerAtTo(nextTriggerAtTo);
|
||||
if (orderByColumn != null && !orderByColumn.isBlank()) {
|
||||
query.setOrderBy(orderByColumn);
|
||||
query.setIsAsc(isAsc);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 逗号分隔串拆成引擎多值条件;空串返回 {@code null} 表示不限。
|
||||
*
|
||||
* @param csv 逗号分隔串
|
||||
* @return 多值列表或 null
|
||||
*/
|
||||
private static List<String> splitCsv(String csv) {
|
||||
if (csv == null || csv.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(csv.split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package org.dromara.workflow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 定时启动任务手动补发业务对象。
|
||||
* <p>只对 TIMER_START 事件的等待记录生效;补发只创建一次实例,不读取也不推进等待记录,
|
||||
* 定时任务的下次触发时间不受影响。</p>
|
||||
*
|
||||
* @author may
|
||||
* @since 2026/10/21
|
||||
*/
|
||||
@Data
|
||||
public class FlowTimerTriggerBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 事件等待记录主键
|
||||
*/
|
||||
private Long waitId;
|
||||
|
||||
/**
|
||||
* 业务主键,留空时按 M{waitId}_{时间戳} 自动生成
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 额外流程变量,合并到节点默认变量之后
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
package org.dromara.workflow.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 定时任务(事件等待记录)视图对象 flow_event_wait。
|
||||
* <p>字段来自引擎 flow_event_wait;flowName / businessId 由服务层按定义与实例主键装配,
|
||||
* eventTypeName / statusName 为展示文案。</p>
|
||||
*
|
||||
* @author may
|
||||
* @since 2026/10/21
|
||||
*/
|
||||
@Data
|
||||
public class FlowEventWaitVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 流程定义id
|
||||
*/
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
private String flowName;
|
||||
|
||||
/**
|
||||
* 流程实例id(定时启动事件为空)
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 业务流程主键
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 事件节点编码
|
||||
*/
|
||||
private String nodeCode;
|
||||
|
||||
/**
|
||||
* 事件类型(TIMER_START / TIMER_CATCH / TIMER_BOUNDARY / …)
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 事件类型文案
|
||||
*/
|
||||
private String eventTypeName;
|
||||
|
||||
/**
|
||||
* 事件名称(消息名 / 错误码)
|
||||
*/
|
||||
private String eventName;
|
||||
|
||||
/**
|
||||
* 边界事件关联的待办任务id
|
||||
*/
|
||||
private Long attachedTaskId;
|
||||
|
||||
/**
|
||||
* 定时触发策略(AFTER / FIXED / POINT_IN_TIME)
|
||||
*/
|
||||
private String triggerType;
|
||||
|
||||
/**
|
||||
* 触发间隔表达式
|
||||
*/
|
||||
private String triggerInterval;
|
||||
|
||||
/**
|
||||
* 下次触发时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date nextTriggerAt;
|
||||
|
||||
/**
|
||||
* 时区
|
||||
*/
|
||||
private String timezone;
|
||||
|
||||
/**
|
||||
* 已触发次数
|
||||
*/
|
||||
private Integer repeatCount;
|
||||
|
||||
/**
|
||||
* 最大触发次数
|
||||
*/
|
||||
private Integer maxRepeatCount;
|
||||
|
||||
/**
|
||||
* 等待状态(WAITING / RETRY / RUNNING / CANCELLED / DEAD / SUCCESS)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 等待状态文案
|
||||
*/
|
||||
private String statusName;
|
||||
|
||||
/**
|
||||
* 已重试次数
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private Integer maxRetryCount;
|
||||
|
||||
/**
|
||||
* 下次重试时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date retryAt;
|
||||
|
||||
/**
|
||||
* 载荷
|
||||
*/
|
||||
private String payload;
|
||||
|
||||
/**
|
||||
* 最近一次错误
|
||||
*/
|
||||
private String lastError;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 定时引擎扫描开关是否开启(warm-flow.timer-enabled)。
|
||||
* 关闭时等待记录仍会生成但不会被自动触发,页面据此给出提醒。
|
||||
*/
|
||||
private Boolean timerEnabled;
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package org.dromara.workflow.service;
|
||||
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.workflow.domain.bo.FlowEventWaitBo;
|
||||
import org.dromara.workflow.domain.bo.FlowTimerTriggerBo;
|
||||
import org.dromara.workflow.domain.vo.FlowEventWaitVo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 定时任务(事件等待记录)服务接口。
|
||||
* <p>数据来自引擎 flow_event_wait,页面提供查询、状态统计与定时启动任务的手动补发。
|
||||
* 手动补发只创建一次实例,不读取也不推进等待记录,定时任务的下次触发时间不受影响。</p>
|
||||
*
|
||||
* @author may
|
||||
* @since 2026/10/21
|
||||
*/
|
||||
public interface IFlwEventWaitService {
|
||||
|
||||
/**
|
||||
* 分页查询事件等待记录,默认只查定时类事件、按下次触发时间升序。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 等待记录分页数据
|
||||
*/
|
||||
PageResult<FlowEventWaitVo> queryPageList(FlowEventWaitBo bo);
|
||||
|
||||
/**
|
||||
* 按同一组筛选条件统计各状态等待记录数量。
|
||||
*
|
||||
* @param bo 查询条件(其中状态条件被逐一分组覆盖)
|
||||
* @return 各状态计数,键为 waiting / retry / running / dead / cancelled
|
||||
*/
|
||||
Map<String, Long> summary(FlowEventWaitBo bo);
|
||||
|
||||
/**
|
||||
* 查询单条等待记录详情。
|
||||
*
|
||||
* @param id 事件等待记录主键
|
||||
* @return 等待记录详情,不存在时返回 {@code null}
|
||||
*/
|
||||
FlowEventWaitVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 手动补发一次定时启动任务(运维应急)。
|
||||
* <p>实例发起人记为当前登录用户;等待记录保持不变,「每天 9 点的任务今天提前补发一次」
|
||||
* 不会吞掉当天 9 点的正常触发。</p>
|
||||
*
|
||||
* @param bo 补发参数(等待记录主键、可选业务主键与变量)
|
||||
* @return 新建流程实例主键
|
||||
*/
|
||||
Long manualTrigger(FlowTimerTriggerBo bo);
|
||||
}
|
||||
@ -0,0 +1,225 @@
|
||||
package org.dromara.workflow.service.impl;
|
||||
|
||||
import com.baomidou.lock.annotation.Lock4j;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.warm.flow.core.FlowEngine;
|
||||
import org.dromara.warm.flow.core.constant.EventCons;
|
||||
import org.dromara.warm.flow.core.dto.EventWaitQuery;
|
||||
import org.dromara.warm.flow.core.entity.EventWait;
|
||||
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.context.WorkflowContext;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.dromara.workflow.domain.bo.FlowEventWaitBo;
|
||||
import org.dromara.workflow.domain.bo.FlowTimerTriggerBo;
|
||||
import org.dromara.workflow.domain.vo.FlowEventWaitVo;
|
||||
import org.dromara.workflow.service.IFlwEventWaitService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 定时任务(事件等待记录)服务实现。
|
||||
* <p>条件解析与合法性校验由引擎 {@link EventWaitQuery} 与门面层完成,本类只负责参数透传、
|
||||
* 引擎实体到视图对象的装配(流程名 / 业务主键)与状态文案。手动补发复用引擎
|
||||
* {@code InsService#startByTimer},不读写等待记录,定时任务下次触发时间不受影响。</p>
|
||||
*
|
||||
* @author may
|
||||
* @since 2026/10/21
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FlwEventWaitServiceImpl implements IFlwEventWaitService {
|
||||
|
||||
/**
|
||||
* 事件类型持久化值 → 页面文案。
|
||||
*/
|
||||
private static final Map<String, String> EVENT_TYPE_NAMES = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* 等待状态持久化值 → 页面文案。
|
||||
*/
|
||||
private static final Map<String, String> STATUS_NAMES = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
EVENT_TYPE_NAMES.put(EventType.MESSAGE_START.getValue(), "消息启动");
|
||||
EVENT_TYPE_NAMES.put(EventType.TIMER_START.getValue(), "定时启动");
|
||||
EVENT_TYPE_NAMES.put(EventType.TIMER_CATCH.getValue(), "定时捕获");
|
||||
EVENT_TYPE_NAMES.put(EventType.MESSAGE_CATCH.getValue(), "消息捕获");
|
||||
EVENT_TYPE_NAMES.put(EventType.MESSAGE_THROW.getValue(), "消息抛出");
|
||||
EVENT_TYPE_NAMES.put(EventType.TIMER_BOUNDARY.getValue(), "定时边界");
|
||||
EVENT_TYPE_NAMES.put(EventType.MESSAGE_BOUNDARY.getValue(), "消息边界");
|
||||
EVENT_TYPE_NAMES.put(EventType.TERMINATE_END.getValue(), "终止结束");
|
||||
EVENT_TYPE_NAMES.put(EventType.MESSAGE_END.getValue(), "消息结束");
|
||||
EVENT_TYPE_NAMES.put(EventType.ERROR_CATCH.getValue(), "错误捕获");
|
||||
EVENT_TYPE_NAMES.put(EventType.ERROR_BOUNDARY.getValue(), "错误边界");
|
||||
|
||||
STATUS_NAMES.put(EventCons.STATUS_WAITING, "待触发");
|
||||
STATUS_NAMES.put(EventCons.STATUS_RETRY, "重试中");
|
||||
STATUS_NAMES.put(EventCons.STATUS_RUNNING, "执行中");
|
||||
STATUS_NAMES.put(EventCons.STATUS_CANCELLED, "已取消");
|
||||
STATUS_NAMES.put(EventCons.STATUS_DEAD, "已死");
|
||||
STATUS_NAMES.put(EventCons.STATUS_SUCCESS, "成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询事件等待记录。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 分页等待记录
|
||||
*/
|
||||
@Override
|
||||
public PageResult<FlowEventWaitVo> queryPageList(FlowEventWaitBo bo) {
|
||||
Page<EventWait> page = FlowEngine.workflow().pageEventWaits(bo.toQuery());
|
||||
List<EventWait> items = page.getList() == null ? List.of() : page.getList();
|
||||
boolean timerEnabled = FlowEngine.getFlowConfig().isTimerEnabled();
|
||||
List<FlowEventWaitVo> rows = new ArrayList<>(items.size());
|
||||
for (EventWait wait : items) {
|
||||
FlowEventWaitVo vo = toVo(wait);
|
||||
vo.setTimerEnabled(timerEnabled);
|
||||
rows.add(vo);
|
||||
}
|
||||
return PageResult.build(rows, page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按同一组筛选条件统计各状态等待记录数量。
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 各状态计数
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Long> summary(FlowEventWaitBo bo) {
|
||||
Map<String, Long> summary = new LinkedHashMap<>();
|
||||
summary.put("waiting", countByStatus(bo, EventCons.STATUS_WAITING));
|
||||
summary.put("retry", countByStatus(bo, EventCons.STATUS_RETRY));
|
||||
summary.put("running", countByStatus(bo, EventCons.STATUS_RUNNING));
|
||||
summary.put("dead", countByStatus(bo, EventCons.STATUS_DEAD));
|
||||
summary.put("cancelled", countByStatus(bo, EventCons.STATUS_CANCELLED));
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单条等待记录详情。
|
||||
*
|
||||
* @param id 事件等待记录主键
|
||||
* @return 等待记录详情,不存在时返回 null
|
||||
*/
|
||||
@Override
|
||||
public FlowEventWaitVo queryById(Long id) {
|
||||
EventWait wait = FlowEngine.eventWaitService().getById(id);
|
||||
if (wait == null) {
|
||||
return null;
|
||||
}
|
||||
FlowEventWaitVo vo = toVo(wait);
|
||||
vo.setTimerEnabled(FlowEngine.getFlowConfig().isTimerEnabled());
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动补发一次定时启动任务(运维应急)。
|
||||
* <p>
|
||||
* 只做「按定义 + 事件节点直接发起」,<b>不读写等待记录</b>:定时任务的下次触发时间保持原样,
|
||||
* 提前补发不会吞掉当天的正常触发。业务主键优先取调用方传入值,留空时用雪花 long 型 id
|
||||
* (与宿主其它业务表主键同型,便于下游按 long 解析),天然不会与定时触发的
|
||||
* {@code T{waitId}_{计划时间}} 撞号。加分布式锁防止同机/多机双击连发。
|
||||
*
|
||||
* @param bo 补发参数
|
||||
* @return 新建实例主键
|
||||
*/
|
||||
@Override
|
||||
@Lock4j(keys = {"#bo.waitId"})
|
||||
public Long manualTrigger(FlowTimerTriggerBo bo) {
|
||||
if (bo == null || bo.getWaitId() == null) {
|
||||
throw new ServiceException("事件等待记录主键不能为空");
|
||||
}
|
||||
EventWait wait = FlowEngine.eventWaitService().getById(bo.getWaitId());
|
||||
if (wait == null) {
|
||||
throw new ServiceException("事件等待记录不存在: " + bo.getWaitId());
|
||||
}
|
||||
if (!EventType.TIMER_START.getValue().equals(wait.getEventType())) {
|
||||
throw new ServiceException("只有定时启动任务支持手动触发,当前事件类型为 " + wait.getEventType());
|
||||
}
|
||||
String finalBusinessId = StringUtils.isNotBlank(bo.getBusinessId())
|
||||
? bo.getBusinessId().trim()
|
||||
: String.valueOf(IdWorker.getId());
|
||||
WorkflowContext context = new WorkflowContext();
|
||||
context.setHandler(LoginHelper.getUserIdStr());
|
||||
try {
|
||||
Instance instance = FlowEngine.insService().startByTimer(finalBusinessId, wait.getDefinitionId()
|
||||
, wait.getNodeCode(), bo.getVariables(), context);
|
||||
return instance.getId();
|
||||
} catch (FlowException e) {
|
||||
log.warn("定时启动任务手动补发失败 waitId={} 原因={}", bo.getWaitId(), e.getMessage());
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计某一状态下的等待记录数量。
|
||||
*
|
||||
* @param bo 查询参数(其中状态条件被覆盖)
|
||||
* @param status 等待状态
|
||||
* @return 记录数量
|
||||
*/
|
||||
private long countByStatus(FlowEventWaitBo bo, String status) {
|
||||
EventWaitQuery query = bo.toQuery();
|
||||
query.setStatuses(List.of(status));
|
||||
return FlowEngine.workflow().countEventWaits(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 引擎等待记录 → 视图对象。
|
||||
*
|
||||
* @param wait 引擎等待记录
|
||||
* @return 视图对象
|
||||
*/
|
||||
private FlowEventWaitVo toVo(EventWait wait) {
|
||||
FlowEventWaitVo vo = new FlowEventWaitVo();
|
||||
vo.setId(wait.getId());
|
||||
vo.setDefinitionId(wait.getDefinitionId());
|
||||
vo.setInstanceId(wait.getInstanceId());
|
||||
vo.setNodeCode(wait.getNodeCode());
|
||||
vo.setEventType(wait.getEventType());
|
||||
vo.setEventName(wait.getEventName());
|
||||
vo.setAttachedTaskId(wait.getAttachedTaskId());
|
||||
vo.setTriggerType(wait.getTriggerType());
|
||||
vo.setTriggerInterval(wait.getTriggerInterval());
|
||||
vo.setNextTriggerAt(wait.getNextTriggerAt());
|
||||
vo.setTimezone(wait.getTimezone());
|
||||
vo.setRepeatCount(wait.getRepeatCount());
|
||||
vo.setMaxRepeatCount(wait.getMaxRepeatCount());
|
||||
vo.setStatus(wait.getStatus());
|
||||
vo.setRetryCount(wait.getRetryCount());
|
||||
vo.setMaxRetryCount(wait.getMaxRetryCount());
|
||||
vo.setRetryAt(wait.getRetryAt());
|
||||
vo.setPayload(wait.getPayload());
|
||||
vo.setLastError(wait.getLastError());
|
||||
vo.setCreateTime(wait.getCreateTime());
|
||||
vo.setUpdateTime(wait.getUpdateTime());
|
||||
vo.setEventTypeName(EVENT_TYPE_NAMES.getOrDefault(wait.getEventType(), wait.getEventType()));
|
||||
vo.setStatusName(STATUS_NAMES.getOrDefault(wait.getStatus(), wait.getStatus()));
|
||||
if (wait.getDefinitionId() != null) {
|
||||
var definition = FlowEngine.defService().getById(wait.getDefinitionId());
|
||||
vo.setFlowName(definition == null ? null : definition.getFlowName());
|
||||
}
|
||||
if (wait.getInstanceId() != null) {
|
||||
var instance = FlowEngine.insService().getById(wait.getInstanceId());
|
||||
vo.setBusinessId(instance == null ? null : instance.getBusinessId());
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@ -601,6 +601,11 @@ INSERT INTO sys_menu VALUES (1761400000000011804, '流程spel表达式定义修
|
||||
INSERT INTO sys_menu VALUES (1761400000000011805, '流程spel表达式定义删除', 1761400000000011801, 4, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:remove', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011806, '流程spel表达式定义导出', 1761400000000011801, 5, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:export', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '');
|
||||
|
||||
-- 定时任务(事件等待记录)菜单与按钮
|
||||
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, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:eventWait:query', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011903, '定时任务手动补发', 1761400000000011901, 2, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:eventWait:trigger', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '');
|
||||
|
||||
INSERT INTO sys_menu VALUES (1761400000000011638, '请假申请', 1761400000000000005, 1, 'leave', 'workflow/leave/index', '', 'N', 'Y', 'C', '0', '0', 'workflow:leave:list', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '请假申请菜单');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011639, '请假申请查询', 1761400000000011638, 1, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:leave:query', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011640, '请假申请新增', 1761400000000011638, 2, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:leave:add', '#', '', '', 1761000000000000103, 1761100000000000001, SYSDATE, NULL, NULL, '');
|
||||
|
||||
@ -569,6 +569,11 @@ INSERT INTO sys_menu VALUES (1761400000000011804, '流程spel表达式定义修
|
||||
INSERT INTO sys_menu VALUES (1761400000000011805, '流程spel表达式定义删除', 1761400000000011801, 4, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:remove', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011806, '流程spel表达式定义导出', 1761400000000011801, 5, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:export', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '');
|
||||
|
||||
-- 定时任务(事件等待记录)菜单与按钮
|
||||
INSERT INTO sys_menu VALUES (1761400000000011901, '定时任务', 1761400000000011630, 3, 'timer', 'workflow/timer/index', '', 'N', 'N', 'C', '0', '0', 'workflow:eventWait:list', 'time', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '定时任务与事件等待监控菜单');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011902, '定时任务查询', 1761400000000011901, 1, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:eventWait:query', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011903, '定时任务手动补发', 1761400000000011901, 2, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:eventWait:trigger', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '');
|
||||
|
||||
INSERT INTO sys_menu VALUES (1761400000000011638, '请假申请', 1761400000000000005, 1, 'leave', 'workflow/leave/index', '', 'N', 'Y', 'C', '0', '0', 'workflow:leave:list', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '请假申请菜单');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011639, '请假申请查询', 1761400000000011638, 1, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:leave:query', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011640, '请假申请新增', 1761400000000011638, 2, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:leave:add', '#', '', '', 1761000000000000103, 1761100000000000001, now(), NULL, NULL, '');
|
||||
|
||||
@ -349,6 +349,11 @@ INSERT INTO sys_menu VALUES (1761400000000011803, '流程达式定义新增', 17
|
||||
INSERT INTO sys_menu VALUES (1761400000000011804, '流程达式定义修改', 1761400000000011801, 3, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:edit', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011805, '流程达式定义删除', 1761400000000011801, 4, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:remove', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '');
|
||||
INSERT INTO sys_menu VALUES (1761400000000011806, '流程达式定义导出', 1761400000000011801, 5, '#', '', NULL, 'N', 'Y', 'F', '0', '0', 'workflow:spel:export', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '');
|
||||
|
||||
-- 定时任务(事件等待记录)菜单与按钮
|
||||
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, '');
|
||||
-- 请假测试相关按钮
|
||||
insert into sys_menu VALUES (1761400000000011638, '请假申请', 1761400000000000005, 1, 'leave', 'workflow/leave/index', '', 'N', 'Y', 'C', '0', '0', 'workflow:leave:list', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '请假申请菜单');
|
||||
insert into sys_menu VALUES (1761400000000011639, '请假申请查询', 1761400000000011638, 1, '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:leave:query', '#', '', '', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '');
|
||||
|
||||
@ -1913,6 +1913,14 @@ GO
|
||||
INSERT sys_menu VALUES (1761400000000011806, N'流程spel表达式定义导出', 1761400000000011801, 5, N'#', N'', NULL, N'N', N'Y', N'F', N'0', N'0', N'workflow:spel:export', N'#', N'', N'', 1761000000000000103, 1761100000000000001, GETDATE(), NULL, NULL, N'');
|
||||
GO
|
||||
|
||||
-- 定时任务(事件等待记录)菜单与按钮
|
||||
INSERT sys_menu VALUES (1761400000000011901, N'定时任务', 1761400000000011630, 3, N'timer', N'workflow/timer/index', N'', N'N', N'N', N'C', N'0', N'0', N'workflow:eventWait:list', N'time', N'', N'', 1761000000000000103, 1761100000000000001, GETDATE(), NULL, NULL, N'定时任务与事件等待监控菜单');
|
||||
GO
|
||||
INSERT sys_menu VALUES (1761400000000011902, N'定时任务查询', 1761400000000011901, 1, N'#', N'', NULL, N'N', N'Y', N'F', N'0', N'0', N'workflow:eventWait:query', N'#', N'', N'', 1761000000000000103, 1761100000000000001, GETDATE(), NULL, NULL, N'');
|
||||
GO
|
||||
INSERT sys_menu VALUES (1761400000000011903, N'定时任务手动补发', 1761400000000011901, 2, N'#', N'', NULL, N'N', N'Y', N'F', N'0', N'0', N'workflow:eventWait:trigger', N'#', N'', N'', 1761000000000000103, 1761100000000000001, GETDATE(), NULL, NULL, N'');
|
||||
GO
|
||||
|
||||
-- 请假测试相关按钮
|
||||
INSERT sys_menu VALUES (1761400000000011638, N'请假申请', 1761400000000000005, 1, N'leave', N'workflow/leave/index', N'', N'N', N'Y', N'C', N'0', N'0', N'workflow:leave:list', N'#', N'', N'', 1761000000000000103, 1761100000000000001, GETDATE(), NULL, NULL, N'请假申请菜单');
|
||||
GO
|
||||
|
||||
@ -31,3 +31,51 @@ INSERT INTO sys_dict_data VALUES (1761600000000000067, 12, '减签', 'remove_sig
|
||||
|
||||
-- 3. 实例状态字典补 restart(引擎 FlowStatus 有,字典缺)
|
||||
INSERT INTO sys_dict_data VALUES (1761600000000000063, 8, '重启', 'restart', 'wf_business_status', '', 'info', 'N', 1761000000000000103, 1761100000000000001, sysdate(), NULL, NULL, '重启');
|
||||
|
||||
-- 4. 事件等待表:定时 / 消息等需要「等待」的事件统一落在此表(与全量脚本一致)。
|
||||
-- 6.0.0 存量库没有这张表,不建表则定时器与定时任务页面全部不可用
|
||||
-- 注意:建表后还需在 application.yml 打开 warm-flow.timer-enabled: true,引擎才会扫描本表
|
||||
CREATE TABLE IF NOT EXISTS `flow_event_wait`
|
||||
(
|
||||
`id` bigint NOT NULL COMMENT '主键id',
|
||||
`tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id',
|
||||
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志',
|
||||
`definition_id` bigint DEFAULT NULL COMMENT '流程定义id(定时启动事件必须有)',
|
||||
`instance_id` bigint DEFAULT NULL COMMENT '流程实例id(实例等待事件必须有)',
|
||||
`node_code` varchar(100) DEFAULT NULL COMMENT '事件节点编码(定位沿用 definition_id + node_code)',
|
||||
`event_type` varchar(40) NOT NULL COMMENT '事件类型(MESSAGE_START TIMER_START TIMER_CATCH MESSAGE_CATCH MESSAGE_THROW TIMER_BOUNDARY MESSAGE_BOUNDARY ERROR_CATCH ERROR_BOUNDARY TERMINATE_END MESSAGE_END)',
|
||||
`event_name` varchar(100) DEFAULT NULL COMMENT '事件名称(消息事件使用)',
|
||||
`attached_task_id` bigint DEFAULT NULL COMMENT '边界事件关联的待办任务id',
|
||||
`trigger_type` varchar(40) DEFAULT NULL COMMENT '触发策略类型(AFTER FIXED POINT_IN_TIME)',
|
||||
`trigger_interval` varchar(100) DEFAULT NULL COMMENT '触发策略原始配置',
|
||||
`next_trigger_at` datetime DEFAULT NULL COMMENT '下次触发时间',
|
||||
`timezone` varchar(40) DEFAULT NULL COMMENT '定时触发使用的时区',
|
||||
`repeat_count` int DEFAULT NULL COMMENT '已触发次数',
|
||||
`max_repeat_count` int DEFAULT NULL COMMENT '最大触发次数',
|
||||
`status` varchar(20) NOT NULL COMMENT '等待状态(WAITING RUNNING CANCELLED RETRY DEAD)',
|
||||
`retry_count` int DEFAULT NULL COMMENT '已重试次数',
|
||||
`max_retry_count` int DEFAULT NULL COMMENT '最大重试次数',
|
||||
`retry_at` datetime DEFAULT NULL COMMENT '下次重试时间',
|
||||
`lock_owner` varchar(64) DEFAULT NULL COMMENT '抢占持有者标识',
|
||||
`lock_until` datetime DEFAULT NULL COMMENT '抢占租约到期时间',
|
||||
`version` int DEFAULT NULL COMMENT '乐观锁版本号',
|
||||
`payload` text COMMENT '消息事件接收的业务载荷',
|
||||
`last_error` varchar(500) DEFAULT NULL COMMENT '最近一次失败原因摘要',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `event_wait_status_next` (`status`, `next_trigger_at`, `id`) USING BTREE,
|
||||
KEY `event_wait_status_retry` (`status`, `retry_at`, `id`) USING BTREE,
|
||||
KEY `event_wait_tenant_status_next` (`tenant_id`, `status`, `next_trigger_at`, `id`) USING BTREE,
|
||||
KEY `event_wait_tenant_status_retry` (`tenant_id`, `status`, `retry_at`, `id`) USING BTREE,
|
||||
KEY `event_wait_instance_task` (`instance_id`, `attached_task_id`) USING BTREE,
|
||||
KEY `event_wait_name_instance_status` (`event_name`, `instance_id`, `status`) USING BTREE,
|
||||
KEY `event_wait_def_node_status` (`definition_id`, `node_code`, `status`) USING BTREE
|
||||
) ENGINE = InnoDB COMMENT ='事件等待表';
|
||||
|
||||
-- 5. 「流程监控」下补「定时任务」菜单 + 查询/手动补发按钮(与 script/sql/ry_workflow.sql 全量脚本一致)
|
||||
-- 先删后插保证可重复执行;重新执行后记得给角色重新勾选按钮权限
|
||||
DELETE FROM sys_menu WHERE menu_id IN (1761400000000011901, 1761400000000011902, 1761400000000011903);
|
||||
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, '');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user