1.请假流程图
下图是 一个请假申请的简单流程图
(1)申请人通过发起流程进行请假申请,给经理发送一个待审批事项;
(2)经理在待办列表选择事项,进行审批,approved同意或者rejected驳回操作,并触发不同的事件;
(3)如果经理approved,则触发Enter holidays in external system事件,并给流程发起人发送一个待办事项;
(4)如果经理rejected,则触发Send out rejection email事件,给申请人发送一个请假不批的邮件,流程结束;
(5)如果是(3),申请人需要处理待办任务进行休假,流程结束。
2.工作流文件
holiday-request.bpmn20.xml
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"xmlns:flowable="http://flowable.org/bpmn"typeLanguage="http://www.w3.org/2001/XMLSchema"expressionLanguage="http://www.w3.org/1999/XPath"targetNamespace="http://www.flowable.org/processdef"><process id="holidayRequest" name="Holiday Request" isExecutable="true"><startEvent id="startEvent"/><sequenceFlow sourceRef="startEvent" targetRef="approveTask"/><userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/><sequenceFlow sourceRef="approveTask" targetRef="decision"/><exclusiveGateway id="decision"/><sequenceFlow sourceRef="decision" targetRef="externalSystemCall"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression></sequenceFlow><sequenceFlow sourceRef="decision" targetRef="sendRejectionMail"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression></sequenceFlow><serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="com.example.flowable.holiday.CallExternalSystemDelegate"/><sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/><userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/><sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/><serviceTask id="sendRejectionMail" name="Send out rejection email" flowable:class="com.example.flowable.holiday.SendRejectionMail"/><sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/><endEvent id="approveEnd"/><endEvent id="rejectEnd"/></process></definitions>
3.pom文件
依赖的jar包如下:
<dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</artifactId><version>6.4.2</version>
</dependency>
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.21</version>
</dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.21</version>
</dependency>
2.源码
(1)HolidayRequest类为测试主类
package com.example.flowable.holiday;import org.flowable.engine.*;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;/**1. 此类描述的是:测试demo2. 参考flowable手册https://tkjohn.github.io/flowable-userguide/3. @author juge4. @version 2021/9/9 9:54
*/
public class HolidayRequest {public static void main(String[] args) {//1.创建一个独立(standalone)配置ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()/*.setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1").setJdbcUsername("sa").setJdbcPassword("").setJdbcDriver("org.h2.Driver")*/.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/flowable?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("123456").setJdbcDriver("com.mysql.jdbc.Driver")//如果数据表不存在的时候,自动创建数据表.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);//2.创建流程引擎ProcessEngine processEngine = cfg.buildProcessEngine();// 使用BPMN 2.0定义process。存储为XML,同时也是可以可视化的。NPMN 2.0标准可以让技术人员与业务人员都参与讨论业务流程中来//3.利用流程引擎部署流程RepositoryService repositoryService = processEngine.getRepositoryService();Deployment deployment = repositoryService.createDeployment().addClasspathResource("holiday-request.bpmn20.xml").deploy();//4.根据流程部署实例id获取流程定义实例ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();System.out.println("Found process definition : " + processDefinition.getName());//5.启动process实例//5.1需要一些初始化的变量,这里我们简单的从Scanner中获取,一般在线上会通过接口传递过来(发起流程的表单)//5.1.1 scanner输入类似于web前端表单输入Scanner scanner= new Scanner(System.in);System.out.println("Who are you?");String employee = scanner.nextLine();System.out.println("How many holidays do you want to request?");Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());System.out.println("Why do you need them?");String description = scanner.nextLine();//5.1.2 此处代码类似web后端获取前端表单传来的字段Map<String, Object> variables = new HashMap<String, Object>();variables.put("employee", employee);variables.put("nrOfHolidays", nrOfHolidays);variables.put("description", description);//6.发起流程RuntimeService runtimeService = processEngine.getRuntimeService();ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables);//7.经理查询待办任务TaskService taskService = processEngine.getTaskService();/*将第一个任务指派给"经理(managers)"组,而第二个用户任务指派给请假申请的提交人。因此需要为第一个任务添加candidateGroups属性:<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>这里的candidateGroups,中文一般成为候选组,实际是一组用户的带号,在实际使用中可以写用户的角色id,或者组织机构(岗位、职位)id等当用户查看待办列表时,后台根据用户的id查询角色id,然后用角色id替换managers即可查询到对应的任务列表*/List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();//8.经理处理审批任务//实际一般为用户在web前端页面的待办任务列表,此处演示为控制台输入输出人机交互System.out.println("You have " + tasks.size() + " tasks:");for (int i=0; i<tasks.size(); i++) {System.out.println((i+1) + ") " + tasks.get(i).getName());}System.out.println("Which task would you like to complete?");//8.1 选择要处理的任务//此处一般为web前端页面,点击“同意”或“驳回”的按钮,同意为"y",驳回为"n"int taskIndex = Integer.valueOf(scanner.nextLine());Task task = tasks.get(taskIndex - 1);//使用任务Id获取特定流程实例的变量Map<String, Object> processVariables = taskService.getVariables(task.getId());System.out.println(processVariables.get("employee") + " wants " +processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?");//8.2 编写任务处理意见,并进行approved or rejected的操作//此处一般为web前端页面,点击“同意”或“驳回”的按钮,同意为"y",驳回为"n"boolean approved = scanner.nextLine().toLowerCase().equals("y");//8.3 完成任务//此处代码一般写在web后端,接收到前端的同意或驳回的参数,完成任务variables = new HashMap<String, Object>();variables.put("approved", approved);//注意先保存意见,再完成任务,否则任务完成后找不到if (approved){Comment comment = taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), "同意休假");comment.setUserId("manager");taskService.saveComment(comment);}else{Comment comment = taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), "不允许休假");comment.setUserId("manager");taskService.saveComment(comment);}taskService.complete(task.getId(), variables);//9.申请人查询待办事务/*并如下所示为第二个任务添加assignee属性。请注意我们没有像上面的’managers’一样使用静态值,而是使用一个流程变量动态指派。这个流程变量是在流程实例启动时传递的:<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>*///注意发起流程时的employee,最好为申请人或者审批人的id或其他唯一字段tasks = taskService.createTaskQuery().taskAssignee(employee).list();//10.申请人处理休假任务System.out.println("You have " + tasks.size() + " tasks:");for (int i=0; i<tasks.size(); i++) {System.out.println((i+1) + ") " + tasks.get(i).getName());}if (tasks.size() > 0){System.out.println("Which task would you like to complete?");//10.1 选择要处理的任务taskIndex = Integer.valueOf(scanner.nextLine());task = tasks.get(taskIndex - 1);//使用任务Id获取特定流程实例的变量processVariables = taskService.getVariables(task.getId());System.out.println("Hi " + processVariables.get("employee") + "please use your holidays !");//10.2 完成任务variables = new HashMap<String, Object>();Comment comment1 = taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), processVariables.get("employee") + "休假中");comment1.setUserId("employe");taskService.saveComment(comment1);taskService.complete(task.getId(), variables);}//展示审批意见List<Comment> taskComments = taskService.getProcessInstanceComments(processInstance.getProcessInstanceId());for (Comment comment:taskComments){System.out.println("审批人:"+comment.getUserId()+ ",审批意见:" + comment.getFullMessage() + ",审批时间:" + comment.getTime());}//展示流程流转的历史记录/*HistoryService historyService = processEngine.getHistoryService();List<HistoricActivityInstance> activities =historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstance.getId()).finished().orderByHistoricActivityInstanceEndTime().asc().list();for (HistoricActivityInstance activity : activities) {System.out.println(activity.getActivityId() + " took "+ activity.getDurationInMillis() + " milliseconds");}*/}
}
(2)CallExternalSystemDelegate类为Enter holidays in external system事件的执行类
package com.example.flowable.holiday;import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;public class CallExternalSystemDelegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution delegateExecution) {System.out.println("Calling the external system for employee "+ delegateExecution.getVariable("employee"));}
}
(3)SendRejectionMail类为Send out rejection email事件的执行类
ackage com.example.flowable.holiday;import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;public class SendRejectionMail implements JavaDelegate {@Overridepublic void execute(DelegateExecution delegateExecution) {System.out.println("send e-mail to employee "+ delegateExecution.getVariable("employee"));}
}
源码下载地址:
https://download.csdn.net/download/juligang320/22333758?spm=1001.2014.3001.5501
3.测试
启动HolidayRequest类进行测试,控制台日志如下:
Found process definition : Holiday Request
Who are you?
john
How many holidays do you want to request?
2
Why do you need them?
go to ...You have 3 tasks:
1) Approve or reject request
2) Approve or reject request
3) Approve or reject request
Which task would you like to complete?
3john wants 2 of holidays. Do you approve this?
yCalling the external system for employee johnYou have 1 tasks:
1) Holiday approved
Which task would you like to complete?
1Hi johnplease use your holidays !审批人:employe,审批意见:john休假中,审批时间:Thu Sep 09 15:13:51 CST 2021
审批人:manager,审批意见:同意休假,审批时间:Thu Sep 09 15:13:47 CST 2021
4 说明
一般工作流使用的是web方式,web前端页面输入,后端进行逻辑处理并输出到前端页面展示。HolidayRequest类中的demo,输入采用scanner,控制台代替web前端页面。
以上列子主要参考一下文档:
flowable官方手册:https://tkjohn.github.io/flowable-userguide/
欢迎交流!