Flowable 自定义表达式

适用版本:PIGX 5.10 及以上
流程引擎:Flowable
核心模块:flow-biz(业务处理模块)
最后更新:2025 年 10 月

第一部分:表达式基础

1.1 表达式系统概述

表达式 是 Flowable 流程定义中用于动态处理业务逻辑的核心机制,通过表达式可以:

  • 动态确定任务审批人
  • 实现复杂的分支条件判断
  • 访问和修改流程变量
  • 调用 Spring Bean 方法
  • 整合业务系统数据

1.2 表达式执行架构

流程定义配置 ↓ 表达式解析(Expression Parser) ↓ Spring Bean 定位 ↓ 方法调用(使用 DelegateExecution) ↓ 返回结果(集合 / 布尔值 / 对象) ↓ 流程引擎处理 ↓ 流程节点分发 / 分支判断

1.3 核心概念

概念定义示例
Spring Bean流程引擎可调用的 Spring 容器托管对象demoBean
Expression表达式字符串demoBean.getUserList(execution)
DelegateExecutionFlowable 执行上下文,携带流程信息方法参数
变量域流程级别、任务级别、局部级别变量存储全局变量池

第二部分:表达式语法详解

2.1 表达式语法规范

基本语法结构

springBeanName.methodName(execution[, otherParams])
组成部分类型必需说明
springBeanName标识符Spring 容器中 Bean 的名称(@Component 注解中定义)
.操作符对象成员访问符
methodName标识符Bean 中的公开方法名称
(execution)参数必须传入 DelegateExecution 对象
[, otherParams]参数可选的额外参数(PIGX 版本需支持)

语法示例

场景表达式返回类型
获取审批人demoBean.getUserList(execution)List<Long>
条件判断demoBean.switchUser(execution)boolean
参数传递demoBean.getApprover(execution, "manager")List<Long>
复杂逻辑demoBean.validateAndGetUsers(execution)List<Long>

2.2 保留字和关键字

关键字用途示例
execution固定参数,代表流程执行上下文必须使用
${...}变量表达式(某些版本支持)${processInstanceId}
#{...}SpEL 表达式(某些版本支持)#{T(System).currentTimeMillis()}
安全建议

PIGX 主要使用 Spring Bean 方法调用,避免在表达式中直接使用 SpEL,以保持系统安全性和可维护性。

2.3 表达式验证规则

规则说明违反后果
Bean 必须存在表达式中的 Bean 名称必须在 Spring 容器中注册流程执行报错
方法必须公开调用的方法必须是 public 访问级别运行时异常
参数类型必须匹配传入的参数类型必须与方法签名一致类型转换错误
返回类型受限审批人表达式返回 List<Long>,条件表达式返回 boolean流程节点处理失败

第三部分:表达式类型详解

3.1 审批人表达式 (Assignee Expression)

定义与用途

审批人表达式 用于在流程执行时动态确定任务的处理人(审批人、执行人)。

属性
返回类型List<Long>List<String>(用户 ID 列表)
执行时机流程到达用户任务节点时
作用范围单个任务节点
使用场景根据业务规则动态分配审批人

表达式配置

流程定义配置方式

在 Flowable 流程设计器中,为用户任务节点配置审批人表达式:

节点类型: User Task 表达式字段: Assignee Expression 或 Assignee 表达式内容: demoBean.getUserList(execution)

多人审批配置 (根据 PIGX 实现)

表达式: demoBean.getMultipleApprovers(execution) 返回值: [1, 2, 3] (多个用户 ID) 处理方式: 并行 / 串联审批

核心实现

标准审批人 Bean
@Component("demoBean")
public class DemoBean {
    
    private final RemoteUserService remoteUserService;
    
    public DemoBean(RemoteUserService remoteUserService) {
        this.remoteUserService = remoteUserService;
    }
    
    /**
     * 获取单个审批人
     * @param execution 委托执行对象
     * @return 审批人 ID 列表
     */
    public List<Long> getUserList(DelegateExecution execution) {
        // 获取流程变量
        Object userId = execution.getVariable("userId");
        
        // 业务逻辑处理
        Long approver = determineApprover(userId);
        
        return Arrays.asList(approver);
    }
    
    /**
     * 获取多个审批人(并行审批)
     * @param execution 委托执行对象
     * @return 审批人 ID 列表
     */
    public List<Long> getMultipleApprovers(DelegateExecution execution) {
        // 获取当前用户部门
        Long deptId = (Long) execution.getVariable("deptId");
        
        // 查询部门经理和总经理
        List<Long> managers = remoteUserService.getManagersByDept(deptId);
        
        return managers;
    }
    
    /**
     * 获取具有特定角色的用户
     * @param execution 委托执行对象
     * @return 用户 ID 列表
     */
    public List<Long> getUsersByRole(DelegateExecution execution) {
        String role = (String) execution.getVariable("requiredRole");
        List<Long> users = remoteUserService.getUsersByRole(role);
        return users;
    }
    
    private Long determineApprover(Object userId) {
        // 实现具体的审批人确定逻辑
        return 1L;
    }
}
基于业务数据的审批人
@Component("businessBean")
public class BusinessBean {
    
    @Autowired
    private BpmOaLeaveService leaveService;
    
    /**
     * 根据请假天数确定审批人
     * @param execution 委托执行对象
     * @return 审批人 ID
     */
    public List<Long> getApproverByLeaveDay(DelegateExecution execution) {
        String processInstanceId = execution.getProcessInstanceId();

        // 查询业务数据
        BpmOaLeaveEntity leave = leaveService.getOne(Wrappers.<BpmOaLeaveEntity>lambdaQuery()
                .eq(BpmOaLeaveEntity::getProcessInstanceId, processInstanceId));
                
        // 根据请假天数返回不同的审批人
        if (leave.getLeaveDay() <= 3) {
            // 直属主管审批
            return Arrays.asList(leave.getUserDeptManager());
        } else if (leave.getLeaveDay() <= 7) {
            // 部门总经理审批
            return Arrays.asList(leave.getDeptDirector());
        } else {
            // 需要 HR 和总经理联合审批
            return Arrays.asList(
                leave.getDeptDirector(),
                leave.getHrManager()
            );
        }
    }
}

DelegateExecution 对象 API

方法返回类型说明示例
getVariable(name)Object获取流程变量execution.getVariable("userId")
setVariable(name, value)void设置流程变量execution.setVariable("status", "approved")
getProcessInstanceId()String获取流程实例 IDString pid = execution.getProcessInstanceId()
getProcessDefinitionId()String获取流程定义 IDString defId = execution.getProcessDefinitionId()
getVariables()Map<String, Object>获取所有变量Map vars = execution.getVariables()

3.2 条件判断表达式 (Condition Expression)

定义与用途

条件判断表达式 用于在流程分支中实现条件路由,根据条件返回值决定流程走向。

属性
返回类型boolean(true / false)
执行时机流程经过排他网关(Exclusive Gateway)或条件边时
作用范围单条流程分支
使用场景流程分支判断、业务规则路由

表达式配置

流程定义配置方式

在排他网关或条件边上配置条件表达式:

元素类型: Exclusive Gateway / Sequence Flow 表达式字段: Condition Expression 表达式内容: demoBean.switchUser(execution)

多分支配置示例

网关分支 1: demoBean.isApproved(execution) → 流向通过 网关分支 2: demoBean.isRejected(execution) → 流向拒绝 网关默认路: (无条件) → 默认流向

核心实现

标准条件判断
@Component("conditionBean")
public class ConditionBean {
    
    @Autowired
    private BpmOaLeaveService leaveService;
    
    /**
     * 审批通过条件
     * @param execution 委托执行对象
     * @return true 表示通过,false 表示未通过
     */
    public boolean isApproved(DelegateExecution execution) {
        // 获取审批意见
        String opinion = (String) execution.getVariable("opinion");
        
        // 返回条件判断结果
        return "approved".equals(opinion);
    }
    
    /**
     * 审批拒绝条件
     * @param execution 委托执行对象
     * @return true 表示拒绝
     */
    public boolean isRejected(DelegateExecution execution) {
        String opinion = (String) execution.getVariable("opinion");
        return "rejected".equals(opinion);
    }
    
    /**
     * 复杂业务条件:根据请假天数判断是否需要总经理审批
     * @param execution 委托执行对象
     * @return true 需要二级审批
     */
    public boolean needsSecondaryApproval(DelegateExecution execution) {
        String processInstanceId = execution.getProcessInstanceId();

        // 查询业务数据
        BpmOaLeaveEntity leave = leaveService.getOne(Wrappers.<BpmOaLeaveEntity>lambdaQuery()
                .eq(BpmOaLeaveEntity::getProcessInstanceId, processInstanceId));
                
        // 超过 7 天需要总经理审批
        return leave.getLeaveDay() > 7;
    }
    
    /**
     * 金额判断条件
     * @param execution 委托执行对象
     * @return true 金额超出预算
     */
    public boolean isAmountExceeded(DelegateExecution execution) {
        Double amount = (Double) execution.getVariable("amount");
        Double budget = (Double) execution.getVariable("budget");
        
        return amount != null && budget != null && amount > budget;
    }
}
基于流程变量的条件
@Component("variableCondition")
public class VariableCondition {
    
    /**
     * 根据申请人等级判断审批流程
     * @param execution 委托执行对象
     * @return true 高管流程
     */
    public boolean isExecutiveApprovalRequired(DelegateExecution execution) {
        String userLevel = (String) execution.getVariable("userLevel");
        return "executive".equals(userLevel);
    }
    
    /**
     * 根据部门判断审批权限
     * @param execution 委托执行对象
     * @return true 需要财务部审批
     */
    public boolean needsFinanceApproval(DelegateExecution execution) {
        String dept = (String) execution.getVariable("department");
        return "sales".equals(dept) || "purchase".equals(dept);
    }
    
    /**
     * 时间区间判断
     * @param execution 委托执行对象
     * @return true 工作时间外
     */
    public boolean isOutsideWorkingHours(DelegateExecution execution) {
        LocalDateTime now = LocalDateTime.now();
        int hour = now.getHour();
        
        // 判断是否在工作时间外(18:00 - 09:00)
        return hour < 9 || hour >= 18;
    }
}
链式条件判断
@Component("chainCondition")
public class ChainCondition {
    
    @Autowired
    private BpmOaLeaveService leaveService;
    
    /**
     * 综合条件判断链
     * @param execution 委托执行对象
     * @return true 满足所有条件
     */
    public boolean validateAll(DelegateExecution execution) {
        // 条件 1: 检查是否有重复申请
        if (hasDuplicateRequest(execution)) {
            return false;
        }
        
        // 条件 2: 检查余额
        if (!hasEnoughBalance(execution)) {
            return false;
        }
        
        // 条件 3: 检查时间冲突
        if (hasTimeConflict(execution)) {
            return false;
        }
        
        return true;
    }
    
    private boolean hasDuplicateRequest(DelegateExecution execution) {
        // 实现检查逻辑
        return false;
    }
    
    private boolean hasEnoughBalance(DelegateExecution execution) {
        // 实现余额检查逻辑
        return true;
    }
    
    private boolean hasTimeConflict(DelegateExecution execution) {
        // 实现时间冲突检查逻辑
        return false;
    }
}

条件返回值说明

返回值含义流程行为
true条件成立沿当前分支继续执行
false条件不成立尝试其他分支或默认分支
异常条件判断错误流程中断,记录异常
null无返回值作为 false 处理

第四部分:流程数据交互

4.1 流程变量系统

变量类型

变量类型存储位置生命周期访问范围
流程级变量流程实例中流程启动 → 流程完成全流程可见
任务级变量任务实例中任务创建 → 任务完成当前任务 + 委托
局部变量执行分支中分支创建 → 分支完成当前分支

变量访问方式

// 获取变量
Object value = execution.getVariable("variableName");

// 设置变量
execution.setVariable("variableName", "value");

// 获取所有变量
Map<String, Object> allVariables = execution.getVariables();

// 删除变量
execution.removeVariable("variableName");

// 检查变量是否存在
boolean exists = execution.hasVariable("variableName");

4.2 流程实例数据获取

通过 processInstanceId 查询数据

获取当前状态

信息API / 方法返回类型
实例状态execution.getProcessInstanceId()String
当前活动节点execution.getCurrentActivityId()String
流程变量execution.getVariable(name)Object
所有变量execution.getVariables()Map

查询历史记录

@Component("historyBean")
public class HistoryBean {
    
    @Autowired
    private HistoryService historyService;
    
    /**
     * 获取流程完整历史
     * @param processInstanceId 流程实例 ID
     * @return 历史实例列表
     */
    public List<HistoricProcessInstance> getProcessHistory(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .list();
    }
    
    /**
     * 获取任务历史
     * @param processInstanceId 流程实例 ID
     * @return 历史任务列表
     */
    public List<HistoricTaskInstance> getTaskHistory(String processInstanceId) {
        return historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(processInstanceId)
            .orderByHistoricTaskInstanceEndTime()
            .asc()
            .list();
    }
    
    /**
     * 获取流程变量历史
     * @param processInstanceId 流程实例 ID
     * @return 变量历史列表
     */
    public List<HistoricVariableInstance> getVariableHistory(String processInstanceId) {
        return historyService.createHistoricVariableInstanceQuery()
            .processInstanceId(processInstanceId)
            .list();
    }
}

4.3 业务数据与流程数据关联

关联模式

业务表 (bpm_oa_leave) ↓ process_instance_id 字段(唯一关键) ↓ 流程实例 (Flowable ProcessInstance) ↓ 流程变量 (Process Variables) ↓ 任务与审批数据

数据查询示例

@Service
public class LeaveProcessService {
    
    @Autowired
    private BpmOaLeaveService leaveService;
    
    @Autowired
    private RuntimeService runtimeService;
    
    /**
     * 根据流程实例 ID 查询完整数据
     * @param processInstanceId 流程实例 ID
     * @return 完整的业务和流程数据
     */
    public Map<String, Object> getCompleteData(String processInstanceId) {
        Map<String, Object> result = new HashMap<>();
        
        // 1. 获取业务数据
        BpmOaLeaveEntity leave = leaveService.getByProcessInstanceId(processInstanceId);
        result.put("business", leave);
        
        // 2. 获取流程实例
        ProcessInstance instance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
        result.put("instance", instance);
        
        // 3. 获取流程变量
        Map<String, Object> variables = runtimeService.getVariables(processInstanceId);
        result.put("variables", variables);
        
        return result;
    }
    
    /**
     * 从流程变量获取业务数据
     * @param execution 委托执行对象
     * @return 业务实体
     */
    public BpmOaLeaveEntity getBusinessDataFromExecution(DelegateExecution execution) {
        String processInstanceId = execution.getProcessInstanceId();
        return leaveService.getByProcessInstanceId(processInstanceId);
    }
}

4.4 表单数据流转

数据流转周期

前端表单 (create.vue) ↓ POST /api/bpmOaLeave (表单数据) ↓ Controller.save() ↓ Service.saveAndStartProcess() ├─ 1. 保存业务数据 → bpm_oa_leave 表 ├─ 2. 设置 process_instance_id └─ 3. 启动流程 → 流程变量初始化 ↓ 流程运行 ├─ 表达式调用 ├─ 审批人确定 ├─ 任务分配 └─ 流程变量修改 ↓ 审批操作 (detail.vue) ├─ 获取流程数据 ├─ 展示业务信息 ├─ 提交审批意见 └─ 更新 leave_status ↓ 流程完成 ├─ 触发状态回调 ├─ 更新业务表数据 └─ 归档流程数据

关键数据转换点

阶段数据来源数据转换数据目标
启动create.vue 表单序列化为 Java 对象bpm_oa_leave 表
初始化业务表数据转换为流程变量ProcessInstance
审批任务实例表达式调用业务数据审批人确定
完成流程状态状态值映射leave_status 更新

第五部分:表达式最佳实践

5.1 表达式设计原则

必须遵循的原则

原则说明示例
单一职责一个表达式只处理一个业务逻辑❌ 不要在审批人表达式中做条件判断
幂等性相同输入必须产生相同输出✅ 表达式不应依赖随机数或时间
快速执行避免耗时操作(数据库查询应有缓存)❌ 不要在表达式中执行复杂计算
异常处理表达式应优雅处理异常✅ 使用 try-catch 和日志记录
可维护性表达式代码应清晰易懂,有适当注释✅ 方法名应表达业务含义

5.2 表达式编写规范

命名规范

// 审批人表达式方法命名规范
public List<Long> get[业务名称]Approvers(DelegateExecution execution)
public List<Long> get[级别]Users(DelegateExecution execution)
public List<Long> getUsersBy[条件](DelegateExecution execution)

// 示例
public List<Long> getLeaveApprovers(DelegateExecution execution)
public List<Long> getManagerUsers(DelegateExecution execution)
public List<Long> getUsersByDepartment(DelegateExecution execution)

// 条件判断方法命名规范
public boolean is[条件状态](DelegateExecution execution)
public boolean needs[动作](DelegateExecution execution)
public boolean can[操作](DelegateExecution execution)

// 示例
public boolean isApproved(DelegateExecution execution)
public boolean needsSecondaryApproval(DelegateExecution execution)
public boolean canApprove(DelegateExecution execution)

异常处理

@Component("safeBean")
public class SafeBean {
    
    private static final Logger log = LoggerFactory.getLogger(SafeBean.class);
    
    /**
     * 安全的审批人获取方法
     * @param execution 委托执行对象
     * @return 审批人列表(默认空列表)
     */
    public List<Long> getSafeApprovers(DelegateExecution execution) {
        try {
            // 获取数据和处理逻辑
            String processInstanceId = execution.getProcessInstanceId();
            // ... 业务逻辑
            return Arrays.asList(1L, 2L);
        } catch (NullPointerException e) {
            log.error("NPE in getSafeApprovers: {}", e.getMessage());
            return Collections.emptyList();
        } catch (Exception e) {
            log.error("Error in getSafeApprovers", e);
            // 返回默认审批人或抛出有意义的异常
            return Arrays.asList(0L);  // 0L 表示系统管理员
        }
    }
    
    /**
     * 安全的条件判断
     * @param execution 委托执行对象
     * @return 条件结果(异常时默认 false)
     */
    public boolean safeCondition(DelegateExecution execution) {
        try {
            // 条件判断逻辑
            return true;
        } catch (Exception e) {
            log.error("Error in safeCondition", e);
            return false;  // 异常时安全地返回 false
        }
    }
}

5.3 表达式测试

单元测试示例

@SpringBootTest
public class ExpressionBeanTest {
    
    @Autowired
    private DemoBean demoBean;
    
    private DelegateExecution execution;
    
    @Before
    public void setUp() {
        execution = Mockito.mock(DelegateExecution.class);
    }
    
    @Test
    public void testGetUserList() {
        // 安排
        Mockito.when(execution.getVariable("userId")).thenReturn(123L);
        
        // 执行
        List<Long> result = demoBean.getUserList(execution);
        
        // 断言
        Assert.assertNotNull(result);
        Assert.assertFalse(result.isEmpty());
    }
    
    @Test
    public void testNeedsSecondaryApproval() {
        // 测试需要二级审批的情况
        Mockito.when(execution.getVariable("deptId")).thenReturn(1L);
        
        boolean result = demoBean.needsSecondaryApproval(execution);
        
        Assert.assertTrue(result);
    }
}

第六部分:常见表达式场景

6.1 请假审批流程

场景描述

根据请假天数和申请人等级,实现分级审批流程。

表达式实现

@Component("leaveBean")
public class LeaveBean {
    
    @Autowired
    private BpmOaLeaveService leaveService;
    
    /**
     * 根据请假天数获取审批人
     */
    public List<Long> getLeaveApprover(DelegateExecution execution) {
        BpmOaLeaveEntity leave = getLeaveData(execution);
        
        // 根据请假天数分级审批
        if (leave.getLeaveDay() <= 3) {
            return Arrays.asList(leave.getDirectManagerId());
        } else if (leave.getLeaveDay() <= 7) {
            return Arrays.asList(leave.getDeptDirectorId());
        } else {
            return Arrays.asList(
                leave.getDeptDirectorId(),
                leave.getHrManagerId()
            );
        }
    }
    
    /**
     * 判断是否需要 HR 审批
     */
    public boolean needsHrApproval(DelegateExecution execution) {
        BpmOaLeaveEntity leave = getLeaveData(execution);
        return leave.getLeaveDay() > 7 || "special".equals(leave.getLeaveType());
    }
    
    private BpmOaLeaveEntity getLeaveData(DelegateExecution execution) {
        String processInstanceId = execution.getProcessInstanceId();
        return leaveService.getByProcessInstanceId(processInstanceId);
    }
}

流程设计

开始 ↓ [请假申请] → 保存数据、启动流程 ↓ ≪分支1: 3天以内≫ → [直属主管审批] ↓ ≪分支2: 3-7天≫ → [部门总经理审批] ↓ ≪分支3: 超过7天≫ → [部门总经理审批] → [HR审批] ↓ {Approved? → 是: [流程完成] → 否: [驳回]} ↓ 结束

6.2 采购审批流程

场景描述

根据采购金额和类型,实现多级审批。

@Component("purchaseBean")
public class PurchaseBean {
    
    @Autowired
    private BpmOaPurchaseService purchaseService;
    
    /**
     * 根据金额获取审批人
     */
    public List<Long> getPurchaseApprover(DelegateExecution execution) {
        Map<String, Object> vars = execution.getVariables();
        Double amount = (Double) vars.get("amount");
        
        List<Long> approvers = new ArrayList<>();
        
        // 金额分级
        if (amount <= 1000) {
            approvers.add((Long) vars.get("deptManagerId"));
        } else if (amount <= 10000) {
            approvers.add((Long) vars.get("deptDirectorId"));
        } else if (amount <= 100000) {
            approvers.addAll(Arrays.asList(
                (Long) vars.get("financeManagerId"),
                (Long) vars.get("executiveDirectorId")
            ));
        } else {
            approvers.addAll(Arrays.asList(
                (Long) vars.get("cfoId"),
                (Long) vars.get("ceoId")
            ));
        }
        
        return approvers;
    }
    
    /**
     * 判断是否需要政府部门审批
     */
    public boolean needsGovernmentApproval(DelegateExecution execution) {
        String category = (String) execution.getVariable("category");
        return "government_contract".equals(category);
    }
}

6.3 报销流程

场景描述

员工报销,根据金额和类别实现动态审批。

@Component("expenseBean")
public class ExpenseBean {
    
    @Autowired
    private BpmExpenseService expenseService;
    
    /**
     * 获取报销审批人
     */
    public List<Long> getExpenseApprover(DelegateExecution execution) {
        String expenseId = (String) execution.getVariable("expenseId");
        BpmExpenseEntity expense = expenseService.getById(expenseId);
        
        List<Long> approvers = new ArrayList<>();
        
        // 第一级:直属主管(所有金额都需要)
        approvers.add(expense.getDirectManagerId());
        
        // 第二级:部门经理(超过2000元)
        if (expense.getAmount() > 2000) {
            approvers.add(expense.getDeptManagerId());
        }
        
        // 第三级:财务负责人(超过5000元)
        if (expense.getAmount() > 5000) {
            approvers.add(expense.getFinanceManagerId());
        }
        
        return approvers;
    }
    
    /**
     * 判断是否需要特殊审批
     */
    public boolean needsSpecialApproval(DelegateExecution execution) {
        String category = (String) execution.getVariable("category");
        Double amount = (Double) execution.getVariable("amount");
        
        // 特殊项目或大金额需要特殊审批
        return "special_project".equals(category) && amount > 10000;
    }
}

第七部分:故障排查与调试

7.1 常见错误与解决方案

错误 1:Bean 名称错误

症状原因排查步骤解决方案
流程无法启动Bean 不存在或名称错误1. 检查 @Component 注解名称 2. 确认 Spring 自动扫描配置确保 Bean 名称与表达式一致
异常:NoSuchBeanDefinitionException容器中找不到 Bean查看应用启动日志检查 Spring 扫描路径配置

错误 2:方法返回类型不匹配

症状原因排查步骤解决方案
任务无审批人分配返回值类型错误1. 检查方法签名 2. 验证返回类型审批人方法必须返回 List<Long>
流程分支无法选择条件方法返回非布尔值1. 检查返回值 2. 调试表达式条件方法必须返回 boolean

错误 3:流程变量未初始化

症状原因排查步骤解决方案
表达式中 getVariable 返回 null变量未设置或命名不一致1. 检查变量名称 2. 查看流程启动代码确保在启动流程前设置所有变量
NullPointerException获取 null 变量后直接使用添加 null 检查和异常处理在表达式中加入防御性编程

7.2 调试技巧

启用详细日志

# application.yml
logging:
  level:
    org.flowable: DEBUG
    com.pigx.bpm: DEBUG
    
# logback-spring.xml
<logger name="org.flowable.engine.impl.el" level="DEBUG"/>
<logger name="org.flowable.engine.impl.bpmn.behavior" level="DEBUG"/>

表达式调试代码

@Component("debugBean")
public class DebugBean {
    
    private static final Logger log = LoggerFactory.getLogger(DebugBean.class);
    
    /**
     * 调试表达式执行
     */
    public List<Long> debugGetApprovers(DelegateExecution execution) {
        log.info("=== Expression Debug Start ===");
        log.info("ProcessInstanceId: {}", execution.getProcessInstanceId());
        log.info("Variables: {}", execution.getVariables());
        
        try {
            // 业务逻辑
            List<Long> result = Arrays.asList(1L, 2L);
            log.info("Result: {}", result);
            return result;
        } catch (Exception e) {
            log.error("Expression execution failed", e);
            log.info("=== Expression Debug End (ERROR) ===");
            throw e;
        }
    }
}

单步调试

// 在 IDE 中调试表达式执行
@Component("stepDebugBean")
public class StepDebugBean {
    
    public List<Long> stepDebugApprovers(DelegateExecution execution) {
        // 在此处设置断点
        String processInstanceId = execution.getProcessInstanceId();  // Step 1
        Object userId = execution.getVariable("userId");              // Step 2
        
        // 按 F10 逐步执行
        List<Long> approvers = determineApprovers(userId);            // Step 3
        
        return approvers;
    }
    
    private List<Long> determineApprovers(Object userId) {
        // 实现审批人确定逻辑
        return Arrays.asList(1L);
    }
}

7.3 性能监控

表达式性能测试

@Component("performanceBean")
public class PerformanceBean {
    
    private static final Logger log = LoggerFactory.getLogger(PerformanceBean.class);
    
    /**
     * 性能监测的审批人获取
     */
    public List<Long> getApproversWithMonitoring(DelegateExecution execution) {
        long startTime = System.currentTimeMillis();
        
        try {
            List<Long> approvers = getApprovers(execution);
            return approvers;
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            log.info("Expression execution time: {} ms", duration);
            
            // 如果超过阈值,记录警告
            if (duration > 1000) {
                log.warn("Expression execution took too long: {} ms", duration);
            }
        }
    }
    
    private List<Long> getApprovers(DelegateExecution execution) {
        // 实现审批人获取逻辑
        return Arrays.asList(1L);
    }
}

第八部分:高级用法

8.1 动态表达式(若支持)

@Component("dynamicBean")
public class DynamicBean {
    
    @Autowired
    private ScriptEngineManager scriptEngineManager;
    
    /**
     * 动态执行脚本表达式(需要 PIGX 支持)
     */
    public List<Long> executeDynamicExpression(DelegateExecution execution) {
        String expression = (String) execution.getVariable("dynamicExpression");
        
        // 通过脚本引擎执行动态表达式
        // 需要 PIGX 框架支持
        return Arrays.asList(1L);
    }
}

8.2 参数化表达式

@Component("parameterizedBean")
public class ParameterizedBean {
    
    /**
     * 带参数的审批人获取(需要 PIGX 支持参数传递)
     */
    public List<Long> getApproversByLevel(DelegateExecution execution, String level) {
        if ("high".equals(level)) {
            return Arrays.asList(100L);  // CEO
        } else if ("medium".equals(level)) {
            return Arrays.asList(50L);   // Manager
        } else {
            return Arrays.asList(10L);   // Staff
        }
    }
}

8.3 缓存优化

@Component("cachedBean")
public class CachedBean {
    
    @Autowired
    private CacheManager cacheManager;
    
    /**
     * 带缓存的审批人获取
     */
    @Cacheable(value = "approvers", key = "#level")
    public List<Long> getCachedApprovers(String level) {
        // 实现审批人获取逻辑(结果会被缓存)
        return Arrays.asList(1L);
    }
    
    /**
     * 清除缓存
     */
    @CacheEvict(value = "approvers", allEntries = true)
    public void clearApproverCache() {
        // 清除所有缓存
    }
}

第九部分:完整工作流示例

9.1 请假流程完整实现

数据库表

CREATE TABLE `bpm_oa_leave` (
  `id` BIGINT PRIMARY KEY,
  `process_instance_id` VARCHAR(64),
  `username` VARCHAR(255),
  `leave_day` SMALLINT,
  `leave_status` SMALLINT,
  `start_time` DATETIME(6),
  `end_time` DATETIME(6),
  `create_time` DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)
);

Service Bean

@Component("leaveWorkflowBean")
public class LeaveWorkflowBean {
    
    @Autowired
    private BpmOaLeaveService leaveService;
    
    // 审批人表达式
    public List<Long> getLeaveApprovers(DelegateExecution execution) {
        BpmOaLeaveEntity leave = getLeaveData(execution);
        return leave.getLeaveDay() <= 3 
            ? Arrays.asList(leave.getDirectManagerId())
            : Arrays.asList(leave.getDeptDirectorId());
    }
    
    // 条件表达式:需要 HR 审批
    public boolean needsHrApproval(DelegateExecution execution) {
        BpmOaLeaveEntity leave = getLeaveData(execution);
        return leave.getLeaveDay() > 7;
    }
    
    private BpmOaLeaveEntity getLeaveData(DelegateExecution execution) {
        return leaveService.getByProcessInstanceId(
            execution.getProcessInstanceId()
        );
    }
}

流程定义配置

节点类型表达式说明
发起Start Event-开始
审批User TaskleaveWorkflowBean.getLeaveApprovers(execution)审批人分配
判断Exclusive GatewayleaveWorkflowBean.needsHrApproval(execution)是否需要 HR
HR审批User Task固定分配HR 最终审批
结束End Event-完成
本页目录