refactor(flow): 重构流程引擎拦截器并优化巡检任务执行

- 在 BaseEntity 中添加 Lombok 注解支持
- 重构 FlowMsgPreInterceptor 接口,增加默认拦截方法和扩展钩子
- 创建 AbstractFlowMsgPreInterceptor 抽象类统一拦截器基础功能
- 实现流程执行事件发布机制,支持节点和任务级别事件
- 集成事件监听器模式用于外部模块监听流程状态变化
- 优化巡检任务实例查询 DTO,支持多状态查询条件
- 完善巡检任务执行状态管理,修复终端锁定检查逻辑
- 实现巡检流程执行监听器,同步任务状态变更
- 优化任务实例创建和执行流程,完善参数传递机制
- 添加流程恢复状态同步功能,确保状态一致性
This commit is contained in:
lixiaolong 2026-06-03 17:08:21 +08:00
parent 68b2698614
commit 4229b07f63
22 changed files with 484 additions and 58 deletions

View File

@ -2,6 +2,8 @@ package com.cmvr.web.controller.inspection;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
@ -44,10 +46,10 @@ public class InspectionTaskInstanceController extends BaseController
@ApiOperation("查询巡检任务执行实例列表")
@PreAuthorize("@ss.hasPermi('inspection:taskInstance:list')")
@GetMapping("/list")
public TableDataInfo list(InspectionTaskInstance inspectionTaskInstance)
public TableDataInfo list(InspectionTaskInstanceQuery inspectionTaskInstanceQuery)
{
startPage();
List<InspectionTaskInstanceVo> list = inspectionTaskInstanceService.selectInspectionTaskInstanceList(inspectionTaskInstance);
List<InspectionTaskInstanceVo> list = inspectionTaskInstanceService.selectInspectionTaskInstanceList(inspectionTaskInstanceQuery);
return getDataTable(list);
}
@ -58,9 +60,9 @@ public class InspectionTaskInstanceController extends BaseController
@PreAuthorize("@ss.hasPermi('inspection:taskInstance:export')")
@Log(title = "巡检任务执行实例", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, InspectionTaskInstance inspectionTaskInstance)
public void export(HttpServletResponse response, InspectionTaskInstanceQuery inspectionTaskInstanceQuery)
{
List<InspectionTaskInstanceVo> list = inspectionTaskInstanceService.selectInspectionTaskInstanceList(inspectionTaskInstance);
List<InspectionTaskInstanceVo> list = inspectionTaskInstanceService.selectInspectionTaskInstanceList(inspectionTaskInstanceQuery);
ExcelUtil<InspectionTaskInstanceVo> util = new ExcelUtil<>(InspectionTaskInstanceVo.class);
util.exportExcel(response, list, "巡检任务执行实例数据");
}

View File

@ -4,10 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.*;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
@ -23,6 +20,7 @@ import java.util.Date;
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Builder
@ApiModel("巡检任务执行实例")
public class InspectionTaskInstance extends BaseEntity
{

View File

@ -4,10 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.*;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
@ -21,6 +18,7 @@ import com.cmvr.common.core.domain.BaseEntity;
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Builder
@ApiModel("巡检点位")
public class InspectionWaypoint extends BaseEntity
{

View File

@ -0,0 +1,28 @@
package com.cmvr.inspection.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 巡检任务实例查询入参DTO
*/
@Data
@ApiModel("巡检任务实例查询条件")
public class InspectionTaskInstanceQuery {
@ApiModelProperty("任务ID")
private String taskId;
@ApiModelProperty("机器人ID")
private String robotId;
@ApiModelProperty("单个状态(兼容原有精确查询)")
private Integer status;
@ApiModelProperty("多状态集合in查询用0待执行 1执行中 2已完成 3已取消 4执行失败 5已暂停")
private List<Integer> statusList;
}

View File

@ -0,0 +1,58 @@
package com.cmvr.inspection.listener;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.inspection.domain.InspectionTaskInstance;
import com.cmvr.inspection.enums.TaskStatusEnum;
import com.cmvr.inspection.service.IInspectionTaskInstanceService;
import com.cmvr.test.flow.runtime.event.FlowExecutionEvent;
import com.cmvr.test.flow.runtime.event.FlowExecutionListener;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class InspectionFlowExecutionListener implements FlowExecutionListener {
@Autowired
private IInspectionTaskInstanceService inspectionTaskInstanceService;
@Override
public void onEvent(FlowExecutionEvent event) {
// 通过 instId 找到巡检任务实例并修改其状态
// 1.构造更新条件
LambdaQueryChainWrapper<InspectionTaskInstance> wrapper = inspectionTaskInstanceService.lambdaQuery()
.eq(InspectionTaskInstance::getTaskInsId, event.getInstId());
// 2.要更新的字段
InspectionTaskInstance updateEntity = getInspectionTaskInstance();
switch (event.getEventType()) {
case TASK_COMPLETED:
// 设置状态为成功
updateEntity.setStatus(TaskStatusEnum.SUCCESS.getCode());
// 正确APIupdate(更新实体, 条件Wrapper)
inspectionTaskInstanceService.update(updateEntity, wrapper.getWrapper());
break;
case TASK_FAILED:
// 设置状态为失败
updateEntity.setStatus(TaskStatusEnum.FAILED.getCode());
// 正确APIupdate(更新实体, 条件Wrapper)
inspectionTaskInstanceService.update(updateEntity, wrapper.getWrapper());
break;
case NODE_COMPLETED:
// 判断当前任务所有节点是否执行完毕
break;
}
}
@NotNull
private static InspectionTaskInstance getInspectionTaskInstance() {
InspectionTaskInstance updateEntity = InspectionTaskInstance.builder()
.endTime(DateUtils.getNowDate())
.build();
updateEntity.setUpdateTime(DateUtils.getNowDate());
return updateEntity;
}
}

View File

@ -2,6 +2,7 @@ package com.cmvr.inspection.mapper;
import java.util.List;
import com.cmvr.inspection.domain.InspectionTaskInstance;
import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery;
import com.cmvr.inspection.domain.vo.InspectionTaskInstanceVo;
import com.github.yulichang.base.MPJBaseMapper;
@ -56,8 +57,8 @@ public interface InspectionTaskInstanceMapper extends MPJBaseMapper<InspectionT
/**
* 查询巡检任务执行实例视图列表包含关联信息
*
* @param inspectionTaskInstance 巡检任务执行实例
* @param inspectionTaskInstanceQuery 巡检任务执行查询对象
* @return 巡检任务执行实例视图集合
*/
List<InspectionTaskInstanceVo> selectInspectionTaskInstanceVoList(InspectionTaskInstance inspectionTaskInstance);
List<InspectionTaskInstanceVo> selectInspectionTaskInstanceVoList(InspectionTaskInstanceQuery inspectionTaskInstanceQuery);
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmvr.inspection.domain.InspectionTaskInstance;
import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery;
import com.cmvr.inspection.domain.vo.InspectionTaskInstanceVo;
/**
* 巡检任务执行实例Service接口
@ -28,7 +29,7 @@ public interface IInspectionTaskInstanceService extends IService<InspectionTaskI
* @param inspectionTaskInstance 巡检任务执行实例
* @return 巡检任务执行实例集合
*/
List<InspectionTaskInstanceVo> selectInspectionTaskInstanceList(InspectionTaskInstance inspectionTaskInstance);
List<InspectionTaskInstanceVo> selectInspectionTaskInstanceList(InspectionTaskInstanceQuery inspectionTaskInstance);
/**
* 新增巡检任务执行实例

View File

@ -3,15 +3,21 @@ package com.cmvr.inspection.service.impl;
import java.util.List;
import java.util.UUID;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmvr.common.exception.ServiceException;
import com.cmvr.common.utils.SecurityUtils;
import com.cmvr.inspection.domain.InspectionTask;
import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery;
import com.cmvr.inspection.domain.vo.InspectionTaskInstanceVo;
import com.cmvr.inspection.enums.TaskStatusEnum;
import com.cmvr.inspection.service.IInspectionTaskService;
import com.cmvr.test.flow.control.FlowControlService;
import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService;
import com.cmvr.test.model.vo.TeTaskExecuteNormalVO;
import com.cmvr.test.service.ITeTaskOrchestrationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.inspection.mapper.InspectionTaskInstanceMapper;
@ -27,12 +33,19 @@ import com.cmvr.inspection.service.IInspectionTaskInstanceService;
@Service
public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTaskInstanceMapper, InspectionTaskInstance> implements IInspectionTaskInstanceService
{
@Autowired
private IInspectionTaskService inspectionTaskService;
@Autowired
private InspectionTaskInstanceMapper inspectionTaskInstanceMapper;
@Autowired
private ITeTaskOrchestrationService teTaskOrchestrationService;
@Autowired
@Lazy
private FlowTaskRuntimeService flowTaskRuntimeService;
@Autowired
@Lazy
private FlowControlService flowControlService;
/**
@ -50,13 +63,13 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
/**
* 查询巡检任务执行实例列表
*
* @param inspectionTaskInstance 巡检任务执行实例
* @param inspectionTaskInstanceQuery 巡检任务执行实例
* @return 巡检任务执行实例
*/
@Override
public List<InspectionTaskInstanceVo> selectInspectionTaskInstanceList(InspectionTaskInstance inspectionTaskInstance)
public List<InspectionTaskInstanceVo> selectInspectionTaskInstanceList(InspectionTaskInstanceQuery inspectionTaskInstanceQuery)
{
return inspectionTaskInstanceMapper.selectInspectionTaskInstanceVoList(inspectionTaskInstance);
return inspectionTaskInstanceMapper.selectInspectionTaskInstanceVoList(inspectionTaskInstanceQuery);
}
/**
@ -72,6 +85,7 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
if (inspectionTaskInstance.getId() == null || inspectionTaskInstance.getId().isEmpty()) {
inspectionTaskInstance.setId(UUID.randomUUID().toString().replace("-", ""));
}
inspectionTaskInstance.setStatus(TaskStatusEnum.NOT_STARTED.getCode());
inspectionTaskInstance.setCreateTime(DateUtils.getNowDate());
inspectionTaskInstance.setCreateBy(SecurityUtils.getUsername());
return inspectionTaskInstanceMapper.insertInspectionTaskInstance(inspectionTaskInstance);
@ -133,8 +147,13 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
update.setStartTime(DateUtils.getNowDate());
update.setUpdateTime(DateUtils.getNowDate());
update.setUpdateBy(SecurityUtils.getUsername());
InspectionTask inspectionTask = inspectionTaskService.selectInspectionTaskById(instance.getTaskId());
JSONObject runParams = new JSONObject();
// 获取所有的检测项id
teTaskOrchestrationService.queryByTaskId(inspectionTask.getTaskConfigId()).forEach(item -> runParams.put(item.getItemId(), new JSONObject()));
// 调用真正的执行
flowTaskRuntimeService.executeTask(TeTaskExecuteNormalVO.builder().taskId(instance.getTaskInsId()).build());
String insId = flowTaskRuntimeService.executeTask(TeTaskExecuteNormalVO.builder().taskId(inspectionTask.getTaskConfigId()).runParams(runParams).build());
update.setTaskInsId(insId);
return inspectionTaskInstanceMapper.updateInspectionTaskInstance(update);
}

View File

@ -161,6 +161,7 @@ public class InspectionTaskServiceImpl extends ServiceImpl<InspectionTaskMapper,
tw.setSortOrder(i + 1);
list.add(tw);
}
inspectionTaskWaypointMapper.batchInsertTaskWaypoint(list);
// 查询巡检任务
InspectionTask inspectionTask = inspectionTaskMapper.selectInspectionTaskById(taskId);
// 查询点位
@ -168,7 +169,7 @@ public class InspectionTaskServiceImpl extends ServiceImpl<InspectionTaskMapper,
// 同步任务编排
TeTaskOrchestraVO teTaskOrchestraVO = TeTaskOrchestraVO.builder().taskId(inspectionTask.getTaskConfigId()).itemIds(waypoints.stream().map(InspectionWaypoint::getDetectItemId).collect(Collectors.toList())).build();
teTaskOrchestrationService.insertTeTaskOrchestration(teTaskOrchestraVO);
return inspectionTaskWaypointMapper.batchInsertTaskWaypoint(list);
return 1;
}
/**

View File

@ -122,7 +122,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</delete>
<select id="selectInspectionTaskInstanceVoList" parameterType="InspectionTaskInstance" resultMap="InspectionTaskInstanceVoResult">
<select id="selectInspectionTaskInstanceVoList" parameterType="InspectionTaskInstanceQuery" resultMap="InspectionTaskInstanceVoResult">
select ti.id, ti.create_by, ti.create_time, ti.update_by, ti.update_time, ti.remark,
ti.task_id, t.task_name, t.task_code,
ti.robot_id, r.robot_name,
@ -133,12 +133,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join inspection_task t on ti.task_id = t.id
left join inspection_robot r on ti.robot_id = r.id
left join inspection_map m on r.current_map_id = m.id
left join sys_user u1 on ti.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on ti.update_by = u2.user_name <!-- 更新人关联用户 -->
left join sys_user u1 on ti.create_by = u1.user_name
left join sys_user u2 on ti.update_by = u2.user_name
<where>
<if test="taskId != null and taskId != ''"> and ti.task_id = #{taskId}</if>
<if test="robotId != null and robotId != ''"> and ti.robot_id = #{robotId}</if>
<if test="status != null and status != ''"> and ti.status = #{status}</if>
<if test="taskId != null and taskId != ''">
and ti.task_id = #{taskId}
</if>
<if test="robotId != null and robotId != ''">
and ti.robot_id = #{robotId}
</if>
<!-- 优先多状态IN查询 -->
<if test="statusList != null and statusList.size() > 0">
and ti.status in
<foreach collection="statusList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<!-- statusList为空再走单个状态精确匹配 -->
<if test="(statusList == null or statusList.size() == 0) and status != null">
and ti.status = #{status}
</if>
</where>
order by ti.create_time desc
</select>

View File

@ -4,6 +4,8 @@ import com.alibaba.fastjson2.JSONObject;
import com.cmvr.test.enums.NodeTypeEnum;
import com.cmvr.test.enums.TaskStatusEnum;
import com.cmvr.test.enums.TerminalStatusEnum;
import com.cmvr.test.flow.runtime.event.FlowExecutionEvent;
import com.cmvr.test.flow.runtime.event.FlowExecutionEventPublisher;
import com.cmvr.test.service.ITeNodeInstService;
import com.cmvr.test.service.ITeTaskInstService;
import lombok.RequiredArgsConstructor;
@ -23,6 +25,7 @@ public class TaskInstHolder {
private final ITeNodeInstService nodeInstService;
private final TaskContextManager taskContextManager;
private final ITeTaskInstService taskInstService;
private final FlowExecutionEventPublisher eventPublisher;
public void register(TaskContext context) {
taskContextManager.register(context);
@ -52,7 +55,16 @@ public class TaskInstHolder {
syncStatus(instId, TaskStatusEnum.SUCCESS);
// 如果没有下一个检测项要执行 且是end节点 注销任务上下文
if (pendingItemCount == 0 && nodeType.equalsIgnoreCase(NodeTypeEnum.END.getCode())) {
TaskContext ctx = taskContextManager.get(instId);
taskContextManager.unregister(instId);
// 发布任务完成事件
eventPublisher.publishEvent(FlowExecutionEvent.builder()
.eventType(FlowExecutionEvent.EventType.TASK_COMPLETED)
.instId(instId)
.taskId(ctx != null ? ctx.getTaskId() : null)
.itemId(itemId)
.status(TaskStatusEnum.SUCCESS)
.build());
}
}
@ -63,6 +75,15 @@ public class TaskInstHolder {
syncStatus(instId, TaskStatusEnum.FAILED);
taskContextManager.unregister(instId);
// 发布任务失败事件
eventPublisher.publishEvent(FlowExecutionEvent.builder()
.eventType(FlowExecutionEvent.EventType.TASK_FAILED)
.instId(instId)
.taskId(taskId)
.itemId(itemId)
.status(TaskStatusEnum.FAILED)
.errorMessage(message)
.build());
}
public void syncStatus(String instId, TaskStatusEnum status) {
@ -83,6 +104,35 @@ public class TaskInstHolder {
ctx.updateTimestamp();
taskInstService.updateStatus(instId, status);
// 发布任务状态变更事件暂停/终止/恢复
switch (status) {
case PAUSED:
eventPublisher.publishEvent(FlowExecutionEvent.builder()
.eventType(FlowExecutionEvent.EventType.TASK_PAUSED)
.instId(instId).taskId(ctx.getTaskId()).itemId(ctx.getItemId())
.status(status).build());
break;
case STOPPED:
eventPublisher.publishEvent(FlowExecutionEvent.builder()
.eventType(FlowExecutionEvent.EventType.TASK_STOPPED)
.instId(instId).taskId(ctx.getTaskId()).itemId(ctx.getItemId())
.status(status).build());
break;
default:
break;
}
}
/**
* 同步状态并标记为恢复操作发布 TASK_RESUMED 事件
*/
public void syncStatusAsResumed(String instId) {
TaskContext ctx = taskContextManager.get(instId);
if (ctx == null) return;
eventPublisher.publishEvent(FlowExecutionEvent.builder()
.eventType(FlowExecutionEvent.EventType.TASK_RESUMED)
.instId(instId).taskId(ctx.getTaskId()).itemId(ctx.getItemId())
.status(TaskStatusEnum.RUNNING).build());
}
/**

View File

@ -55,9 +55,7 @@ public class FlowControlService {
throw new GlobalException("任务已终止,无需重复操作");
}
// 异步终止
executor.execute(() -> {
edgeSystemService.stopAll(ctx.getTerminalId());
});
executor.execute(() -> edgeSystemService.stopAll(ctx.getTerminalId()));
// 统一记录日志 + 设置上下文状态 + 数据库状态
taskInstHolder.syncStatus(instId, TaskStatusEnum.STOPPED);
@ -82,9 +80,7 @@ public class FlowControlService {
throw new GlobalException("任务已处于暂停状态");
}
// 异步停止终端
executor.execute(() -> {
edgeSystemService.stopAll(ctx.getTerminalId());
});
executor.execute(() -> edgeSystemService.stopAll(ctx.getTerminalId()));
ctx.setPaused(true);
ctx.setTerminalStatus(TerminalStatusEnum.RUNNING);
ctx.setStatus(TaskStatusEnum.PAUSED);
@ -197,5 +193,7 @@ public class FlowControlService {
ctx.setStatus(TaskStatusEnum.RUNNING);
ctx.updateTimestamp();
taskInstHolder.syncStatus(instId, TaskStatusEnum.RUNNING);
// 发布任务恢复事件
taskInstHolder.syncStatusAsResumed(instId);
}
}

View File

@ -123,11 +123,11 @@ public class FlowTaskRuntimeEntry implements FlowTaskRuntimeService {
public String executeTaskInternal(String terminalId, String taskId,
RunModeEnum runMode, JSONObject runParams, Object originalVO) {
if (StrUtil.isEmpty(terminalId)) {
throw new GlobalException("终端ID不能为空");
// throw new GlobalException("终端ID不能为空");
}
// 检查终端状态
if (taskInstHolder.isTerminalLocked(terminalId)) {
throw new GlobalException("当前终端正在执行其他任务,请稍后再试");
if (terminalId != null && taskInstHolder.isTerminalLocked(terminalId)) {
// throw new GlobalException("当前终端正在执行其他任务,请稍后再试");
}
// 查询任务详情检测项信息
List<TeQueryTaskDetailDTO> details = taskOrchestrationService.queryTaskDetail(taskId);

View File

@ -0,0 +1,81 @@
package com.cmvr.test.flow.runtime.event;
import com.cmvr.test.enums.TaskStatusEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 流程执行事件
* <p>
* 携带流程执行过程中的关键信息供外部模块监听和处理
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FlowExecutionEvent {
/**
* 事件类型
*/
private EventType eventType;
/**
* 任务实例 ID
*/
private String instId;
/**
* 任务 ID
*/
private String taskId;
/**
* 当前检测项 ID
*/
private String itemId;
/**
* 节点 ID节点级事件时有值
*/
private String nodeId;
/**
* 节点类型节点级事件时有值
*/
private String nodeType;
/**
* 任务状态
*/
private TaskStatusEnum status;
/**
* 错误信息失败事件时有值
*/
private String errorMessage;
/**
* 事件类型枚举
*/
public enum EventType {
/** 节点执行完成 */
NODE_COMPLETED,
/** 节点执行失败 */
NODE_FAILED,
/** 任务全部执行完成 */
TASK_COMPLETED,
/** 任务执行失败 */
TASK_FAILED,
/** 任务被暂停 */
TASK_PAUSED,
/** 任务被终止 */
TASK_STOPPED,
/** 任务恢复执行 */
TASK_RESUMED,
/** 节点开始执行 */
NODE_STARTED,
}
}

View File

@ -0,0 +1,47 @@
package com.cmvr.test.flow.runtime.event;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
/**
* 流程执行事件发布器
* <p>
* 自动收集所有 Spring 容器中实现了 {@link FlowExecutionListener} Bean
* 在流程执行的关键节点发布事件通知
*/
@Slf4j
@Component
public class FlowExecutionEventPublisher {
private final List<FlowExecutionListener> listeners;
public FlowExecutionEventPublisher(List<FlowExecutionListener> listeners) {
this.listeners = listeners != null ? listeners : Collections.emptyList();
if (!this.listeners.isEmpty()) {
log.info("已注册 {} 个流程执行监听器: {}", this.listeners.size(),
this.listeners.stream().map(l -> l.getClass().getSimpleName()).toArray());
}
}
/**
* 发布流程执行事件通知所有监听器
*
* @param event 事件对象
*/
public void publishEvent(FlowExecutionEvent event) {
if (listeners.isEmpty()) {
return;
}
for (FlowExecutionListener listener : listeners) {
try {
listener.onEvent(event);
} catch (Exception e) {
log.error("流程执行监听器 [{}] 处理事件 [{}] 异常: {}",
listener.getClass().getSimpleName(), event.getEventType(), e.getMessage(), e);
}
}
}
}

View File

@ -0,0 +1,38 @@
package com.cmvr.test.flow.runtime.event;
/**
* 流程执行事件监听器
* <p>
* 其他模块可以通过实现此接口来监听流程执行过程中的事件
* 例如节点执行完成任务完成任务失败任务暂停/终止/恢复等
* <p>
* 使用方式 Spring 容器中注册一个实现了此接口的 Bean 即可自动生效
* <pre>
* &#64;Component
* public class InspectionFlowExecutionListener implements FlowExecutionListener {
* &#64;Override
* public void onEvent(FlowExecutionEvent event) {
* switch (event.getEventType()) {
* case TASK_COMPLETED:
* // 处理任务完成逻辑
* break;
* case TASK_FAILED:
* // 处理任务失败逻辑
* break;
* }
* }
* }
* </pre>
*/
public interface FlowExecutionListener {
/**
* 接收流程执行事件
* <p>
* 注意此方法在流程执行线程中同步调用请勿执行耗时操作
* 如需耗时处理建议在内部异步执行
*
* @param event 流程执行事件
*/
void onEvent(FlowExecutionEvent event);
}

View File

@ -0,0 +1,58 @@
package com.cmvr.test.flow.runtime.interceptor;
import com.cmvr.test.flow.runtime.event.FlowExecutionEvent;
import com.cmvr.test.flow.runtime.event.FlowExecutionEventPublisher;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
/**
* 流程消息前置处理
*/
public abstract class AbstractFlowMsgPreInterceptor implements FlowMsgPreInterceptor {
protected final FlowExecutionEventPublisher eventPublisher;
// 构造注入子类实例化时由spring注入
public AbstractFlowMsgPreInterceptor(FlowExecutionEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Override
public void beforeIntercept(TaskNodeExecuteMessage message) {
FlowExecutionEvent event = FlowExecutionEvent.builder()
.eventType(FlowExecutionEvent.EventType.NODE_STARTED)
.instId(message.getInstId())
.taskId(message.getTaskId())
.itemId(message.getItemId())
.nodeId(message.getNodeId())
.nodeType(message.getNodeType())
.build();
eventPublisher.publishEvent(event);
}
@Override
public void afterIntercept(TaskNodeExecuteMessage message, TaskNodeExecuteResult result) {
FlowExecutionEvent.EventType eventType;
String errorMessage = null;
if (result != null && result.isSuccess()) {
eventType = FlowExecutionEvent.EventType.NODE_COMPLETED;
} else {
eventType = FlowExecutionEvent.EventType.NODE_FAILED;
errorMessage = result != null ? result.getErrorMsg() : "执行结果为空";
}
FlowExecutionEvent event = FlowExecutionEvent.builder()
.eventType(eventType)
.instId(message.getInstId())
.taskId(message.getTaskId())
.itemId(message.getItemId())
.nodeId(message.getNodeId())
.nodeType(message.getNodeType())
.errorMessage(errorMessage)
.build();
eventPublisher.publishEvent(event);
}
}

View File

@ -5,9 +5,9 @@ import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.test.flow.context.TaskInstHolder;
import com.cmvr.test.flow.runtime.event.FlowExecutionEventPublisher;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -17,13 +17,17 @@ import java.util.function.Function;
@Slf4j
@Component
@Order(1)
@RequiredArgsConstructor
public class FlowExceptionInterceptor implements FlowMsgPreInterceptor {
public class FlowExceptionInterceptor extends AbstractFlowMsgPreInterceptor {
private final TaskInstHolder taskInstHolder;
public FlowExceptionInterceptor(FlowExecutionEventPublisher eventPublisher, TaskInstHolder taskInstHolder) {
super(eventPublisher);
this.taskInstHolder = taskInstHolder;
}
@Override
public TaskNodeExecuteResult intercept(TaskNodeExecuteMessage message,
public TaskNodeExecuteResult doIntercept(TaskNodeExecuteMessage message,
Function<TaskNodeExecuteMessage, TaskNodeExecuteResult> next) {
try {
return next.apply(message);

View File

@ -4,9 +4,9 @@ import com.alibaba.fastjson2.JSONObject;
import com.cmvr.test.flow.builder.FlowGraph;
import com.cmvr.test.flow.builder.FlowNodeWrapper;
import com.cmvr.test.flow.runtime.engine.support.FlowNodeParamPreparer;
import com.cmvr.test.flow.runtime.event.FlowExecutionEventPublisher;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -20,13 +20,18 @@ import java.util.function.Function;
@Slf4j
@Component
@Order(2)
@RequiredArgsConstructor
public class FlowInputPrepareInterceptor implements FlowMsgPreInterceptor {
public class FlowInputPrepareInterceptor extends AbstractFlowMsgPreInterceptor {
private final FlowNodeParamPreparer paramPreparer;
public FlowInputPrepareInterceptor(FlowExecutionEventPublisher eventPublisher, FlowNodeParamPreparer paramPreparer) {
super(eventPublisher);
this.paramPreparer = paramPreparer;
}
@Override
public TaskNodeExecuteResult intercept(TaskNodeExecuteMessage message,
public TaskNodeExecuteResult doIntercept(TaskNodeExecuteMessage message,
Function<TaskNodeExecuteMessage, TaskNodeExecuteResult> next) {
FlowGraph graph = message.getGraph();
FlowNodeWrapper node = graph.getNode(message.getNodeId());

View File

@ -4,9 +4,9 @@ import cn.hutool.core.util.StrUtil;
import com.cmvr.test.enums.NodeTypeEnum;
import com.cmvr.test.flow.context.TaskContext;
import com.cmvr.test.flow.context.TaskInstHolder;
import com.cmvr.test.flow.runtime.event.FlowExecutionEventPublisher;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -16,13 +16,17 @@ import java.util.function.Function;
@Slf4j
@Component
@Order(3)
@RequiredArgsConstructor
public class FlowLoggingInterceptor implements FlowMsgPreInterceptor {
public class FlowLoggingInterceptor extends AbstractFlowMsgPreInterceptor {
private final TaskInstHolder taskInstHolder;
public FlowLoggingInterceptor(FlowExecutionEventPublisher eventPublisher, TaskInstHolder taskInstHolder) {
super(eventPublisher);
this.taskInstHolder = taskInstHolder;
}
@Override
public TaskNodeExecuteResult intercept(TaskNodeExecuteMessage message,
public TaskNodeExecuteResult doIntercept(TaskNodeExecuteMessage message,
Function<TaskNodeExecuteMessage, TaskNodeExecuteResult> next) {
long start = System.currentTimeMillis();
log.info("[ {} ]节点开始执行, 参数[ {} ], 循环次数[ {} ]", message.getAction(), message.getInputParams(), message.getLoopNum());

View File

@ -16,6 +16,24 @@ public interface FlowMsgPreInterceptor {
* @param next 下一个处理函数
* @return 返回最终执行结果
*/
TaskNodeExecuteResult intercept(TaskNodeExecuteMessage message,
default TaskNodeExecuteResult intercept(TaskNodeExecuteMessage message,
Function<TaskNodeExecuteMessage, TaskNodeExecuteResult> next) {
beforeIntercept(message);
TaskNodeExecuteResult taskNodeExecuteResult = doIntercept(message, next);
afterIntercept(message, taskNodeExecuteResult);
return taskNodeExecuteResult;
}
/**
* 执行拦截逻辑子类可重写此方法实现自定义拦截
* @param message 执行消息
* @param next 下一个处理函数
* @return 执行结果
*/
TaskNodeExecuteResult doIntercept(TaskNodeExecuteMessage message,
Function<TaskNodeExecuteMessage, TaskNodeExecuteResult> next);
void beforeIntercept(TaskNodeExecuteMessage message);
void afterIntercept(TaskNodeExecuteMessage message, TaskNodeExecuteResult result);
}

View File

@ -3,9 +3,9 @@ package com.cmvr.test.flow.runtime.interceptor;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.test.flow.context.TaskContext;
import com.cmvr.test.flow.context.TaskInstHolder;
import com.cmvr.test.flow.runtime.event.FlowExecutionEventPublisher;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -19,13 +19,16 @@ import java.util.function.Function;
@Slf4j
@Component
@Order(4)
@RequiredArgsConstructor
public class FlowOutputStoreInterceptor implements FlowMsgPreInterceptor {
public class FlowOutputStoreInterceptor extends AbstractFlowMsgPreInterceptor {
private final TaskInstHolder taskInstHolder;
public FlowOutputStoreInterceptor(FlowExecutionEventPublisher eventPublisher, TaskInstHolder taskInstHolder) {
super(eventPublisher);
this.taskInstHolder = taskInstHolder;
}
@Override
public TaskNodeExecuteResult intercept(TaskNodeExecuteMessage message,
public TaskNodeExecuteResult doIntercept(TaskNodeExecuteMessage message,
Function<TaskNodeExecuteMessage, TaskNodeExecuteResult> next) {
TaskNodeExecuteResult result = next.apply(message);