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. 此类描述的是:测试demo
2. 参考flowable手册https://tkjohn.github.io/flowable-userguide/
3. @author juge
4. @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 {
@Override
public 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 {
@Override
public 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?
3
john wants 2 of holidays. Do you approve this?
y
Calling the external system for employee john
You have 1 tasks:
1) Holiday approved
Which task would you like to complete?
1
Hi 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/
欢迎交流!