首页
/ 飞龙工作流FlowLong:自定义节点开发深度解析

飞龙工作流FlowLong:自定义节点开发深度解析

2026-02-04 04:16:18作者:伍霜盼Ellen

引言:为什么需要自定义节点?

在企业级工作流应用中,标准审批节点往往无法满足复杂的业务需求。飞龙工作流FlowLong作为国产工作流引擎的佼佼者,提供了强大的自定义节点扩展能力,让开发者能够根据具体业务场景定制专属的流程处理逻辑。

本文将深入探讨FlowLong的自定义节点开发机制,通过实际代码示例和架构分析,帮助您掌握这一核心扩展能力。

一、FlowLong节点处理架构概览

1.1 核心处理器接口体系

FlowLong通过一套精心设计的处理器接口体系来实现节点的扩展性:

classDiagram
    class ConditionNodeHandler {
        +getConditionNode(FlowLongContext, Execution, NodeModel) Optional~ConditionNode~
        +getRouteNode(FlowLongContext, Execution, NodeModel) Optional~ConditionNode~
        +getInclusiveNodes(FlowLongContext, Execution, NodeModel) Optional~List~ConditionNode~~
    }
    
    class CreateTaskHandler {
        +handle(FlowLongContext, Execution, NodeModel) boolean
    }
    
    class FlowJsonHandler {
        +toJson(Object) String
        +fromJson(String, Class~T~) T
    }
    
    class FlowLongContext {
        -createTaskHandler: CreateTaskHandler
        -conditionNodeHandler: ConditionNodeHandler
        -flowJsonHandler: FlowJsonHandler
        +getCreateTaskHandler() CreateTaskHandler
        +getFlowConditionHandler() ConditionNodeHandler
    }
    
    FlowLongContext --> ConditionNodeHandler
    FlowLongContext --> CreateTaskHandler
    FlowLongContext --> FlowJsonHandler

1.2 默认实现与扩展机制

FlowLong提供了默认的简单实现,同时也允许开发者完全覆盖这些实现:

处理器类型 默认实现类 扩展方式
条件节点处理器 SimpleConditionNodeHandler 实现ConditionNodeHandler接口
任务创建处理器 SimpleCreateTaskHandler 实现CreateTaskHandler接口
JSON处理器 FlowJacksonHandler 实现FlowJsonHandler接口

二、自定义条件节点开发实战

2.1 ConditionNodeHandler接口详解

条件节点处理器是流程分支决策的核心,负责根据业务条件选择执行路径:

/**
 * 自定义条件节点处理器示例
 * 实现复杂的业务条件判断逻辑
 */
public class BusinessConditionNodeHandler implements ConditionNodeHandler {
    
    @Override
    public Optional<ConditionNode> getConditionNode(FlowLongContext context, 
                                                   Execution execution, 
                                                   NodeModel nodeModel) {
        // 获取流程变量
        Map<String, Object> variables = execution.getVariables();
        BigDecimal amount = (BigDecimal) variables.get("approvalAmount");
        String department = (String) variables.get("applyDepartment");
        
        // 复杂的业务条件判断逻辑
        if (amount.compareTo(new BigDecimal("10000")) > 0) {
            // 大额审批需要财务总监审批
            return findConditionNode(nodeModel, "financeDirectorApproval");
        } else if ("技术部".equals(department) && amount.compareTo(new BigDecimal("5000")) > 0) {
            // 技术部中等金额需要CTO审批
            return findConditionNode(nodeModel, "ctoApproval");
        } else {
            // 其他情况走默认审批流程
            return findConditionNode(nodeModel, "defaultApproval");
        }
    }
    
    @Override
    public Optional<ConditionNode> getRouteNode(FlowLongContext context, 
                                               Execution execution, 
                                               NodeModel nodeModel) {
        // 路由节点处理逻辑
        String routeType = (String) execution.getVariable("routeType");
        if ("emergency".equals(routeType)) {
            return findConditionNode(nodeModel, "emergencyRoute");
        }
        return Optional.empty();
    }
    
    @Override
    public Optional<List<ConditionNode>> getInclusiveNodes(FlowLongContext context, 
                                                          Execution execution, 
                                                          NodeModel nodeModel) {
        // 包容分支处理:同时满足多个条件的分支
        List<ConditionNode> inclusiveNodes = new ArrayList<>();
        if (meetsConditionA(execution)) {
            inclusiveNodes.add(findConditionNode(nodeModel, "conditionA").get());
        }
        if (meetsConditionB(execution)) {
            inclusiveNodes.add(findConditionNode(nodeModel, "conditionB").get());
        }
        return inclusiveNodes.isEmpty() ? Optional.empty() : Optional.of(inclusiveNodes);
    }
    
    private Optional<ConditionNode> findConditionNode(NodeModel nodeModel, String nodeKey) {
        return nodeModel.getConditionNodes().stream()
                .filter(cn -> nodeKey.equals(cn.getKey()))
                .findFirst();
    }
}

2.2 条件节点配置示例

在流程定义中配置自定义条件节点:

{
  "nodes": [
    {
      "key": "conditionCheck",
      "type": "condition",
      "name": "审批金额条件判断",
      "handler": "com.yourcompany.BusinessConditionNodeHandler",
      "conditionNodes": [
        {
          "key": "financeDirectorApproval",
          "name": "财务总监审批",
          "expression": "amount > 10000"
        },
        {
          "key": "ctoApproval", 
          "name": "CTO审批",
          "expression": "department == '技术部' && amount > 5000"
        },
        {
          "key": "defaultApproval",
          "name": "默认审批",
          "expression": "true"
        }
      ]
    }
  ]
}

三、自定义任务创建处理器开发

3.1 CreateTaskHandler接口实现

任务创建处理器负责生成具体的审批任务,支持复杂的任务分配逻辑:

/**
 * 自定义任务创建处理器
 * 实现动态任务分配和特殊任务处理逻辑
 */
public class DynamicTaskCreateHandler implements CreateTaskHandler {
    
    @Override
    public boolean handle(FlowLongContext context, Execution execution, NodeModel nodeModel) {
        // 1. 前置校验
        if (!validateBusinessRules(execution)) {
            return false;
        }
        
        // 2. 动态确定审批人
        List<String> assignees = determineAssignees(execution, nodeModel);
        
        // 3. 创建审批任务
        for (String assignee : assignees) {
            FlwTask task = createTaskInstance(execution, nodeModel, assignee);
            
            // 4. 设置任务特殊属性
            setTaskSpecialProperties(task, execution);
            
            // 5. 保存任务并发送通知
            context.getTaskService().saveTask(task);
            sendTaskNotification(task, assignee);
        }
        
        return true;
    }
    
    private List<String> determineAssignees(Execution execution, NodeModel nodeModel) {
        // 复杂的审批人确定逻辑
        String projectType = (String) execution.getVariable("projectType");
        BigDecimal budget = (BigDecimal) execution.getVariable("budgetAmount");
        
        List<String> assignees = new ArrayList<>();
        
        if ("研发项目".equals(projectType)) {
            // 研发项目需要技术负责人和项目经理审批
            assignees.add(getTechLead(execution));
            assignees.add(getProjectManager(execution));
        } else if ("市场活动".equals(projectType) && budget.compareTo(new BigDecimal("50000")) > 0) {
            // 大额市场活动需要市场总监审批
            assignees.add(getMarketingDirector());
        }
        
        // 默认审批人 fallback
        if (assignees.isEmpty()) {
            assignees.add(getDefaultApprover(nodeModel));
        }
        
        return assignees;
    }
    
    private void setTaskSpecialProperties(FlwTask task, Execution execution) {
        // 设置任务优先级
        String urgency = (String) execution.getVariable("urgencyLevel");
        if ("high".equals(urgency)) {
            task.setPriority(TaskPriority.HIGH);
            task.setDueDate(calculateDueDate(2)); // 高紧急度2天内完成
        }
        
        // 设置任务标签
        task.setTags(Arrays.asList("custom", "dynamic"));
    }
}

3.2 任务处理器配置集成

在Spring Boot中配置自定义处理器:

@Configuration
public class FlowLongCustomConfig {
    
    @Bean
    public CreateTaskHandler dynamicTaskCreateHandler() {
        return new DynamicTaskCreateHandler();
    }
    
    @Bean
    public ConditionNodeHandler businessConditionNodeHandler() {
        return new BusinessConditionNodeHandler();
    }
    
    @Bean
    public FlowLongContext flowLongContext(FlowCache flowCache) {
        FlowLongContext context = new FlowLongContext(flowCache, null);
        context.setCreateTaskHandler(dynamicTaskCreateHandler());
        context.setConditionNodeHandler(businessConditionNodeHandler());
        return context;
    }
}

四、高级自定义场景实践

4.1 会签节点的自定义实现

/**
 * 自定义会签处理器
 * 支持复杂的会签逻辑和动态会签人确定
 */
public class CustomCountersignHandler implements CreateTaskHandler {
    
    @Override
    public boolean handle(FlowLongContext context, Execution execution, NodeModel nodeModel) {
        CountersignConfig config = parseCountersignConfig(nodeModel);
        
        // 动态确定会签参与者
        List<CountersignParticipant> participants = determineParticipants(execution, config);
        
        // 根据会签类型创建任务
        if (config.getType() == CountersignType.SEQUENTIAL) {
            createSequentialTasks(context, execution, nodeModel, participants);
        } else {
            createParallelTasks(context, execution, nodeModel, participants);
        }
        
        return true;
    }
    
    private List<CountersignParticipant> determineParticipants(Execution execution, 
                                                             CountersignConfig config) {
        List<CountersignParticipant> participants = new ArrayList<>();
        
        // 根据部门、角色、技能等维度确定会签人
        if (config.isIncludeDepartmentHeads()) {
            participants.addAll(getDepartmentHeads(execution));
        }
        
        if (config.isIncludeTechnicalExperts()) {
            participants.addAll(getTechnicalExperts(execution));
        }
        
        // 去重和排序
        return participants.stream()
                .distinct()
                .sorted(Comparator.comparing(CountersignParticipant::getPriority))
                .collect(Collectors.toList());
    }
}

4.2 条件表达式扩展

/**
 * 自定义表达式处理器
 * 扩展FlowLong的表达式解析能力
 */
public class CustomExpressionHandler extends SpelFlowLongExpression {
    
    @Override
    public Object execute(String expression, Map<String, Object> variables) {
        // 支持自定义函数调用
        if (expression.startsWith("custom:")) {
            return handleCustomFunction(expression.substring(7), variables);
        }
        
        // 支持数据库查询表达式
        if (expression.startsWith("dbQuery:")) {
            return executeDatabaseQuery(expression.substring(8), variables);
        }
        
        // 默认使用SPEL表达式
        return super.execute(expression, variables);
    }
    
    private Object handleCustomFunction(String functionExpression, Map<String, Object> variables) {
        String[] parts = functionExpression.split("\\(");
        String functionName = parts[0];
        String params = parts[1].replace(")", "");
        
        switch (functionName) {
            case "calculateRiskScore":
                return calculateRiskScore(params, variables);
            case "getApprovalPath":
                return getApprovalPath(params, variables);
            default:
                throw new IllegalArgumentException("Unknown custom function: " + functionName);
        }
    }
}

五、最佳实践与性能优化

5.1 自定义节点开发规范

实践要点 说明 示例
接口实现完整性 实现所有接口方法,提供合理的默认实现 所有Handler接口方法都要实现
异常处理 使用统一的异常处理机制 抛出FlowLongException
日志记录 记录关键操作和决策过程 使用SLF4J记录调试信息
性能考虑 避免在处理器中进行耗时操作 使用缓存、异步处理

5.2 性能优化策略

/**
 * 高性能条件节点处理器
 * 使用缓存和预编译优化性能
 */
public class OptimizedConditionHandler implements ConditionNodeHandler {
    
    private final Cache<String, CompiledCondition> conditionCache;
    
    public OptimizedConditionHandler() {
        this.conditionCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
    }
    
    @Override
    public Optional<ConditionNode> getConditionNode(FlowLongContext context, 
                                                   Execution execution, 
                                                   NodeModel nodeModel) {
        // 使用缓存加速条件判断
        String cacheKey = buildCacheKey(nodeModel, execution);
        CompiledCondition compiledCondition = conditionCache.get(cacheKey, 
            key -> compileConditions(nodeModel.getConditionNodes()));
        
        return compiledCondition.evaluate(execution.getVariables());
    }
    
    private CompiledCondition compileConditions(List<ConditionNode> conditionNodes) {
        // 预编译条件表达式,提高执行性能
        return new CompiledCondition(conditionNodes);
    }
}

六、测试与调试

6.1 单元测试示例

@SpringBootTest
public class CustomNodeHandlerTest {
    
    @Autowired
    private CreateTaskHandler customTaskHandler;
    
    @Test
    public void testDynamicAssigneeDetermination() {
        // 准备测试数据
        Execution execution = createTestExecution();
        NodeModel nodeModel = createTestNodeModel();
        FlowLongContext context = createMockContext();
        
        // 执行测试
        boolean result = customTaskHandler.handle(context, execution, nodeModel);
        
        // 验证结果
        assertTrue(result);
        verifyTaskCreation(execution, 2); // 应该创建2个任务
    }
    
    @Test
    public void testConditionNodeRouting() {
        // 测试条件节点路由逻辑
        Execution execution = createExecutionWithVariables(
            Map.of("approvalAmount", new BigDecimal("15000"),
                   "applyDepartment", "技术部")
        );
        
        Optional<ConditionNode> result = conditionHandler.getConditionNode(
            context, execution, nodeModel
        );
        
        assertTrue(result.isPresent());
        assertEquals("financeDirectorApproval", result.get().getKey());
    }
}

6.2 调试技巧

/**
 * 可调试的自定义处理器
 * 提供详细的调试信息和日志输出
 */
public class DebuggableTaskHandler implements CreateTaskHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(DebuggableTaskHandler.class);
    
    @Override
    public boolean handle(FlowLongContext context, Execution execution, NodeModel nodeModel) {
        logger.debug("开始处理任务创建,实例ID: {}, 节点: {}", 
                    execution.getInstanceId(), nodeModel.getName());
        
        try {
            // 详细的调试日志
            logExecutionDetails(execution);
            logNodeModelDetails(nodeModel);
            
            boolean result = doHandle(context, execution, nodeModel);
            
            logger.debug("任务创建处理完成,结果: {}", result);
            return result;
            
        } catch (Exception e) {
            logger.error("任务创建处理失败", e);
            throw new FlowLongException("自定义任务处理失败", e);
        }
    }
}

七、总结与展望

通过本文的深入探讨,我们全面了解了飞龙工作流FlowLong的自定义节点开发机制。自定义节点开发是FlowLong强大扩展能力的体现,让开发者能够:

  1. 灵活适应业务需求:根据具体业务场景定制审批逻辑
  2. 实现复杂流程控制:支持条件分支、会签、动态分配等高级功能
  3. 提升系统集成度:与企业现有系统深度集成
  4. 优化用户体验:提供更智能、更高效的审批流程

未来,随着FlowLong的持续发展,自定义节点开发将支持更多高级特性,如AI智能审批、区块链存证、多租户隔离等,为企业数字化转型提供更强大的工作流引擎支持。

掌握自定义节点开发技能,将使您能够在FlowLong基础上构建出真正符合企业需求的智能化工作流系统。

登录后查看全文
热门项目推荐
相关项目推荐