当前位置: 首页 > news >正文

别再死记硬背API了!用请假审批实战,带你玩转Activiti 7的RuntimeService

别再死记硬背API了!用请假审批实战,带你玩转Activiti 7的RuntimeService

作为Java开发者,你是否曾经面对Activiti的API文档感到无从下手?那些抽象的概念和冰冷的方法名,就像一堵高墙挡在面前。今天,让我们换个方式学习——通过一个真实的请假审批流程,来探索RuntimeService的核心功能。

1. 从零开始构建请假流程

在开始编码之前,我们需要先设计一个简单的请假审批流程。这个流程将包含以下节点:

  • 员工提交请假申请
  • 根据请假天数决定审批路径
  • 部门经理审批
  • 人力资源审批(仅当请假天数≥5天时)
  • 流程结束
<bpmn:process id="leaveRequest" name="员工请假流程"> <bpmn:startEvent id="startEvent" /> <bpmn:sequenceFlow id="flow1" sourceRef="startEvent" targetRef="applyLeaveTask" /> <bpmn:userTask id="applyLeaveTask" name="提交请假申请"> <bpmn:extensionElements> <activiti:formProperty id="leaveDays" name="请假天数" type="long" required="true" /> <activiti:formProperty id="reason" name="请假原因" type="string" /> </bpmn:extensionElements> </bpmn:userTask> <bpmn:sequenceFlow id="flow2" sourceRef="applyLeaveTask" targetRef="gateway1" /> <bpmn:exclusiveGateway id="gateway1" name="天数判断网关" /> <bpmn:sequenceFlow id="flow3" sourceRef="gateway1" targetRef="deptManagerTask"> <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"> ${leaveDays < 5} </bpmn:conditionExpression> </bpmn:sequenceFlow> <bpmn:sequenceFlow id="flow4" sourceRef="gateway1" targetRef="hrManagerTask"> <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"> ${leaveDays >= 5} </bpmn:conditionExpression> </bpmn:sequenceFlow> <bpmn:userTask id="deptManagerTask" name="部门经理审批" /> <bpmn:userTask id="hrManagerTask" name="人力资源审批" /> <bpmn:sequenceFlow id="flow5" sourceRef="deptManagerTask" targetRef="endEvent" /> <bpmn:sequenceFlow id="flow6" sourceRef="hrManagerTask" targetRef="endEvent" /> <bpmn:endEvent id="endEvent" /> </bpmn:process>

这个BPMN流程定义清晰地展现了我们的业务逻辑。接下来,让我们看看如何通过RuntimeService来操作这个流程。

2. RuntimeService核心操作实战

2.1 启动流程实例

启动流程是RuntimeService最基础的功能。Activiti提供了多种启动方式,我们来看最常用的两种:

// 方式1:通过流程定义Key启动(推荐) ProcessInstance instance1 = runtimeService.startProcessInstanceByKey( "leaveRequest", "LEAVE-2023-001", // 业务主键 variables // 流程变量 ); // 方式2:通过流程定义ID启动 ProcessInstance instance2 = runtimeService.startProcessInstanceById( processDefinition.getId(), "LEAVE-2023-002", variables );

两种方式的对比:

启动方式优点缺点
startProcessInstanceByKey不需要查询流程定义,直接使用流程Key如果流程有多个版本,会启动最新版本
startProcessInstanceById精确控制启动的流程定义版本需要先获取流程定义ID

提示:业务主键(Business Key)是关联业务数据的桥梁,建议使用有意义的标识符,如"LEAVE-2023-001"。

2.2 流程变量管理

流程变量是流程运转的核心数据载体。RuntimeService提供了丰富的变量操作方法:

// 设置全局变量(整个流程可见) runtimeService.setVariable(executionId, "approvalComment", "同意请假"); // 设置局部变量(仅当前执行流可见) runtimeService.setVariableLocal(executionId, "tempData", "临时数据"); // 批量设置变量 Map<String, Object> variables = new HashMap<>(); variables.put("days", 3); variables.put("reason", "年假"); runtimeService.setVariables(executionId, variables); // 获取变量 Object days = runtimeService.getVariable(executionId, "days"); Object tempData = runtimeService.getVariableLocal(executionId, "tempData");

变量作用域对比:

  • 全局变量:整个流程实例生命周期内有效
  • 局部变量:仅在当前执行流中有效,分支合并后失效

2.3 流程控制与事件触发

RuntimeService提供了多种流程控制方法,让流程能够灵活运转:

// 触发接收任务(Receive Task) runtimeService.trigger(executionId); // 触发信号事件 runtimeService.signalEventReceived("approvalSignal", executionId); // 触发消息事件 runtimeService.messageEventReceived("msgName", executionId, variables); // 挂起流程 runtimeService.suspendProcessInstanceById(processInstanceId); // 激活流程 runtimeService.activateProcessInstanceById(processInstanceId);

这些方法在复杂流程中特别有用。例如,当审批人通过邮件审批时,可以通过信号事件来驱动流程前进。

3. 流程实例查询技巧

RuntimeService提供了强大的查询API,帮助开发者快速定位流程实例:

// 基础查询 List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery() .processDefinitionKey("leaveRequest") .active() // 只查询活跃实例 .list(); // 带变量的高级查询 List<ProcessInstance> longLeaveInstances = runtimeService.createProcessInstanceQuery() .variableValueGreaterThan("days", 5) .orderByProcessInstanceId().desc() .listPage(0, 10); // 执行流查询 List<Execution> executions = runtimeService.createExecutionQuery() .activityId("deptManagerTask") // 查询特定节点的执行流 .list();

常用查询条件:

  • processInstanceBusinessKey()- 按业务主键查询
  • variableValueEquals()- 按变量值精确查询
  • active()/suspended()- 按状态查询
  • startedBefore()/startedAfter()- 按时间范围查询

4. 实战:完整请假审批实现

现在,让我们把这些知识点串联起来,实现一个完整的请假审批流程。

4.1 初始化流程引擎

// 创建流程引擎配置 ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration() .setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useSSL=false") .setJdbcUsername("root") .setJdbcPassword("password") .setJdbcDriver("com.mysql.jdbc.Driver") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); // 构建流程引擎 ProcessEngine processEngine = cfg.buildProcessEngine(); // 获取服务 RepositoryService repositoryService = processEngine.getRepositoryService(); RuntimeService runtimeService = processEngine.getRuntimeService(); TaskService taskService = processEngine.getTaskService();

4.2 部署流程定义

// 部署流程定义 Deployment deployment = repositoryService.createDeployment() .addClasspathResource("leave-request.bpmn20.xml") .name("员工请假流程") .deploy(); // 验证部署 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .deploymentId(deployment.getId()) .singleResult(); System.out.println("流程定义部署成功:" + processDefinition.getKey());

4.3 员工提交请假申请

// 准备请假数据 Map<String, Object> variables = new HashMap<>(); variables.put("applicant", "张三"); variables.put("leaveDays", 6); variables.put("reason", "回家探亲"); // 启动流程实例 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey( "leaveRequest", "LEAVE-2023-1001", variables ); System.out.println("流程实例ID:" + processInstance.getId());

4.4 处理审批任务

// 查询部门经理的待办任务 Task deptManagerTask = taskService.createTaskQuery() .processDefinitionKey("leaveRequest") .taskAssignee("deptManager") .singleResult(); if (deptManagerTask != null) { // 审批通过 Map<String, Object> taskVariables = new HashMap<>(); taskVariables.put("deptApproved", true); taskVariables.put("deptComment", "同意,注意工作交接"); taskService.complete(deptManagerTask.getId(), taskVariables); System.out.println("部门经理审批完成"); } // 查询HR的待办任务(当请假天数≥5天时) Task hrTask = taskService.createTaskQuery() .processDefinitionKey("leaveRequest") .taskAssignee("hrManager") .singleResult(); if (hrTask != null) { // HR审批 Map<String, Object> hrVariables = new HashMap<>(); hrVariables.put("hrApproved", true); hrVariables.put("hrComment", "已记录考勤系统"); taskService.complete(hrTask.getId(), hrVariables); System.out.println("HR审批完成"); }

4.5 流程监控与管理

// 查询所有进行中的请假流程 List<ProcessInstance> activeInstances = runtimeService.createProcessInstanceQuery() .processDefinitionKey("leaveRequest") .active() .list(); System.out.println("当前活跃的请假流程数量:" + activeInstances.size()); // 查询特定流程的当前节点 List<Execution> executions = runtimeService.createExecutionQuery() .processInstanceId(processInstance.getId()) .list(); for (Execution execution : executions) { System.out.println("执行流ID:" + execution.getId() + ", 当前节点:" + execution.getActivityId()); }

5. 高级技巧与最佳实践

5.1 使用信号事件实现跨流程通信

// 在流程定义中添加信号事件 <bpmn:signal id="emergencySignal" name="emergencySignal" /> // 在代码中触发信号 runtimeService.signalEventReceived("emergencySignal"); // 所有监听该信号的流程都会收到通知

5.2 流程版本控制策略

// 查询所有版本的流程定义 List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery() .processDefinitionKey("leaveRequest") .orderByProcessDefinitionVersion().desc() .list(); // 启动特定版本的流程 ProcessDefinition oldVersion = definitions.get(1); // 获取第二个版本 runtimeService.startProcessInstanceById(oldVersion.getId());

5.3 性能优化建议

  1. 批量操作:当需要处理大量流程实例时,使用批量操作方法
  2. 变量精简:避免在流程变量中存储大对象
  3. 查询优化:合理使用索引字段进行查询
  4. 缓存利用:Activiti内置缓存机制,合理配置可提升性能
// 批量挂起流程实例 runtimeService.suspendProcessInstanceByProcessDefinitionKey("leaveRequest"); // 批量删除流程实例 runtimeService.deleteProcessInstances( processInstanceIds, "系统清理", true, // 是否跳过自定义监听器 true // 是否外部系统触发 );

通过这个请假审批的实战案例,我们不仅学会了RuntimeService的核心API,更重要的是掌握了如何将这些API应用到真实业务场景中。记住,学习工作流引擎的关键不在于死记硬背API,而在于理解其设计思想和工作原理。

http://www.cnnetsun.cn/news/2906200.html

相关文章:

  • 3分钟学会Blender建筑建模:Building Tools终极指南
  • 2026百色市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • 学术不端检测技术与科研诚信体系建设实践
  • 别再只看CVSS分数了!手把手教你解读CVE漏洞报告里的那些‘潜台词’
  • Simulink仿真避坑指南:调试BASK/BFSK/BPSK系统时,你的带通滤波器和比较器阈值设对了吗?
  • 分治算法的递归深度控制与栈空间优化的技术8
  • Cursor Pro破解技术深度解析:从机器ID重置到多平台兼容的开源解决方案
  • IronyModManager:终极Paradox游戏模组冲突解决方案指南
  • WechatBakTool:微信聊天记录备份解密全攻略
  • Windows Cleaner终极指南:三步告别C盘爆红,免费开源工具助你重获流畅体验
  • 如何彻底掌控Mac睡眠模式?SleeperX让你的Mac按需休眠
  • 联想刃7000k BIOS隐藏选项解锁:三步实现高级配置权限提升
  • Typora自动编号插件:告别手动编号,实现文档结构化自动化
  • 别再死记硬背了!用Wireshark抓包实战,带你彻底搞懂TCP拥塞控制(慢开始/快恢复)
  • 如何免费获取Grammarly Premium高级版:autosearch-grammarly-premium-cookie完整指南
  • M68HC11指令集深度解析:从寻址模式到条件码的嵌入式编程实践
  • 深入微指令:拆解HUST单总线CPU的ControlBus,看懂32位控制信号如何驱动排序
  • Dropbear和OpenSSH混用指南:跨平台SCP免密传文件,这些细节别踩坑
  • 你的序列Logo图颜色选对了吗?深入解读WebLogo的Chemistry、Hydrophobicity等配色方案与应用场景
  • 无人机山地灾害巡检数据集 | 滑坡多区域实例分割 遥感影像解译 地质灾害预警深度学习数据10296期
  • 鸣潮工具箱:5分钟解锁120帧极致游戏体验的完整指南
  • 从LSTM到Mamba:为什么说双向状态空间模型是处理视觉序列的“潜力股”?
  • 3分钟实现优雅Markdown阅读体验:为什么你需要这款Chrome扩展?
  • PyQt6图表进阶:手把手教你实现图表缩放、平移与自定义交互(QChartView实战)
  • Cursor Free VIP 技术解析与应用指南:跨平台AI编程助手功能扩展方案
  • 避开这些坑!IEEE TII/TITS/IoTJ投稿全流程保姆级解析(含时间线预测)
  • 2026年10款论文降AIGC工具亲测:从90%降至10%的硬核之选
  • d2s-editor:如何用可视化工具高效编辑暗黑破坏神2存档
  • 鸿蒙原生应用实战(五):数据统计与个人中心——柱状图实现、统计计算与设置面板
  • 碧蓝航线自动化脚本终极指南:5分钟上手全自动游戏管家