1. flowable-ui
目前flowable-ui主要用于画流程图,流程图画完之后,再对XML做适当的修改
docker安装使用flowable
docker run -d -p 8080:8080 flowable/all-in-one
进入flowable-ui界面
http://127.0.0.1:8080/flowable-modeler
2. springboot 使用
2.1 准备流程文件
Holiday_Request.bpmn20.xml,将文件放在resource/processes目录下,启动是会自动找流程文件部署
<?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:flowable="http://flowable.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
<process id="holiday-request" name="Holiday Request" isExecutable="true">
<startEvent id="startEvent" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sequenceFlow-df79157f-2c4c-467c-8f27-50e3e1c65795" sourceRef="startEvent" targetRef="approveTask"></sequenceFlow>
<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="dept-managers" flowable:formFieldValidation="true"></userTask>
<sequenceFlow id="sequenceFlow-e262d0a2-be78-462e-a382-58a507462e77" sourceRef="approveTask" targetRef="decision"></sequenceFlow>
<exclusiveGateway id="decision"></exclusiveGateway>
<sequenceFlow id="sequenceFlow-4395dd85-dfe3-41e1-9160-5f47ac0755a7" sourceRef="decision" targetRef="externalSystemCall">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="sequenceFlow-17d5b350-554f-40ad-a475-b38196d562d7" sourceRef="decision" targetRef="sendRejectionMail">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
</sequenceFlow>
<serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="com.hsm.flow.callback.HolidayCallback"></serviceTask>
<sequenceFlow id="sequenceFlow-a0666bbf-eccf-4c2f-8567-6131445fa9b6" sourceRef="externalSystemCall" targetRef="holidayApprovedTask"></sequenceFlow>
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="managers" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sequenceFlow-e7697e72-a60f-4b97-9cde-7af165951805" sourceRef="holidayApprovedTask" targetRef="approveEnd"></sequenceFlow>
<serviceTask id="sendRejectionMail" name="Send out rejection email" flowable:class="com.hsm.flow.callback.SendRejectionMailCallback"></serviceTask>
<sequenceFlow id="sequenceFlow-fbd52a13-630e-452b-9136-5939252f8c9f" sourceRef="sendRejectionMail" targetRef="rejectEnd"></sequenceFlow>
<endEvent id="approveEnd"></endEvent>
<endEvent id="rejectEnd"></endEvent>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_holidayRequest">
<bpmndi:BPMNPlane bpmnElement="holiday-request" id="BPMNPlane_holidayRequest">
<bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
<omgdc:Bounds height="30.0" width="30.0" x="240.0" y="255.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="approveTask" id="BPMNShape_approveTask">
<omgdc:Bounds height="60.0" width="100.0" x="320.0" y="240.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="decision" id="BPMNShape_decision">
<omgdc:Bounds height="40.0" width="40.0" x="470.0" y="250.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="externalSystemCall" id="BPMNShape_externalSystemCall">
<omgdc:Bounds height="60.0" width="100.0" x="560.0" y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="holidayApprovedTask" id="BPMNShape_holidayApprovedTask">
<omgdc:Bounds height="60.0" width="100.0" x="710.0" y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sendRejectionMail" id="BPMNShape_sendRejectionMail">
<omgdc:Bounds height="60.0" width="100.0" x="560.0" y="320.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="approveEnd" id="BPMNShape_approveEnd">
<omgdc:Bounds height="28.0" width="28.0" x="860.0" y="176.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="rejectEnd" id="BPMNShape_rejectEnd">
<omgdc:Bounds height="28.0" width="28.0" x="745.0" y="335.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-e7697e72-a60f-4b97-9cde-7af165951805" id="BPMNEdge_sequenceFlow-e7697e72-a60f-4b97-9cde-7af165951805">
<omgdi:waypoint x="809.9499999999999" y="190.0"></omgdi:waypoint>
<omgdi:waypoint x="860.0" y="190.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-fbd52a13-630e-452b-9136-5939252f8c9f" id="BPMNEdge_sequenceFlow-fbd52a13-630e-452b-9136-5939252f8c9f">
<omgdi:waypoint x="659.949999999997" y="349.66442953020135"></omgdi:waypoint>
<omgdi:waypoint x="745.0003059524752" y="349.09362216470777"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-e262d0a2-be78-462e-a382-58a507462e77" id="BPMNEdge_sequenceFlow-e262d0a2-be78-462e-a382-58a507462e77">
<omgdi:waypoint x="419.9499999999756" y="270.0"></omgdi:waypoint>
<omgdi:waypoint x="470.0" y="270.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-4395dd85-dfe3-41e1-9160-5f47ac0755a7" id="BPMNEdge_sequenceFlow-4395dd85-dfe3-41e1-9160-5f47ac0755a7">
<omgdi:waypoint x="509.9189252336448" y="270.0"></omgdi:waypoint>
<omgdi:waypoint x="522.0" y="270.0"></omgdi:waypoint>
<omgdi:waypoint x="522.0" y="190.0"></omgdi:waypoint>
<omgdi:waypoint x="560.0" y="190.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-17d5b350-554f-40ad-a475-b38196d562d7" id="BPMNEdge_sequenceFlow-17d5b350-554f-40ad-a475-b38196d562d7">
<omgdi:waypoint x="509.9189252336448" y="270.0"></omgdi:waypoint>
<omgdi:waypoint x="522.0" y="270.0"></omgdi:waypoint>
<omgdi:waypoint x="522.0" y="350.0"></omgdi:waypoint>
<omgdi:waypoint x="559.9999999999769" y="350.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-df79157f-2c4c-467c-8f27-50e3e1c65795" id="BPMNEdge_sequenceFlow-df79157f-2c4c-467c-8f27-50e3e1c65795">
<omgdi:waypoint x="269.9499986183554" y="270.0"></omgdi:waypoint>
<omgdi:waypoint x="319.9999999999394" y="270.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-a0666bbf-eccf-4c2f-8567-6131445fa9b6" id="BPMNEdge_sequenceFlow-a0666bbf-eccf-4c2f-8567-6131445fa9b6">
<omgdi:waypoint x="659.9499999999999" y="190.0"></omgdi:waypoint>
<omgdi:waypoint x="710.0" y="190.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
2.2 准备pom文件
<dependencies>
<!-- knife4j,swagger增强版 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.7</version>
</dependency>
<!-- 工作流 -->
<!-- Flowable spring-boot 版套餐 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.6.0</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
<scope>runtime</scope>
</dependency>
</dependencies>
2.3 配置数据源
server:
port: 9099
spring:
application:
name: spring-flowable
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://ip:3306/flowable?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
2.4 配置流程引擎文件
ProcessEngineConfig.java
@Configuration
@Slf4j
public class ProcessEngineConfig {
/**
* 初始化流程引擎
* @return
*/
@Primary
@Bean(name = "processEngine")
public ProcessEngine initProcessEngine(DataSource dataSource) {
log.info("=============================ProcessEngineBegin=============================");
// 流程引擎配置
ProcessEngineConfiguration cfg = null;
try {
cfg = new StandaloneProcessEngineConfiguration()
.setDataSource(dataSource)
// 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
} catch (Exception e) {
e.printStackTrace();
}
// 初始化流程引擎对象
ProcessEngine processEngine = cfg.buildProcessEngine();
log.info("=============================ProcessEngineEnd=============================");
return processEngine;
}
}
2.5 开始写代码
实体类TaskResp.java
@Data
public class TaskResp {
private String taskId;
private String taskName;
}
控制器 HolidayController.java
@RestController
@Api(tags = "请求流程控制器", value = "请求流程控制器")
@RequestMapping("/holiday")
@Slf4j
public class HolidayController {
@Autowired
private IHolidayService holidayService;
@PostMapping("create")
@ApiOperation(value = "创建请假申请", notes = "创建请假申请")
public boolean createRequest(@RequestParam("userName") String userName,
@RequestParam("reason") String reason,
@RequestParam("days") Integer days){
return holidayService.createRequest(userName,reason,days);
}
@GetMapping("list/task")
@ApiOperation(value = "任务列表查询", notes = "任务列表查询")
public List<TaskResp> listTask(@RequestParam(value = "userGroup",required = false) String userGroup,
@RequestParam(value = "username",required = false) String username,
@RequestParam(value = "taskName",required = false) String taskName,
@RequestParam(value = "businessKey",required = false) String businessKey){
return holidayService.listTask(userGroup,username,taskName,businessKey);
}
@GetMapping("complete/task")
@ApiOperation(value = "审核任务", notes = "审核任务")
public boolean completeTask(@RequestParam(value = "taskId",required = true) String taskId,
@RequestParam(value = "approved",required = true) boolean approved){
return holidayService.completeTask(taskId,approved);
}
}
实现类HolidayService.java
@Service
public class HolidayService implements IHolidayService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
private static final String processDefinitionId = "holiday-request";
@Override
public boolean createRequest(String userName, String reason, Integer days) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId)
.singleResult();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("userName", userName);
variables.put("reason", reason);
variables.put("days", days);
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey(processDefinitionId, variables);
return true;
}
@Override
public List<TaskResp> listTask(String userGroup, String username, String taskName, String businessKey) {
List<TaskResp> dataList = new ArrayList<>();
TaskQuery taskQuery = taskService.createTaskQuery();
if(StringUtils.isNotEmpty(username)){
taskQuery.taskAssignee(username);
}
if(StringUtils.isNotEmpty(userGroup)){
taskQuery.taskCandidateGroup(userGroup);
}
List<Task> list = taskQuery.list();
for (Task task : list) {
TaskResp taskResp = new TaskResp();
taskResp.setTaskId(task.getId());
taskResp.setTaskName(task.getName());
dataList.add(taskResp);
}
return dataList;
}
@Override
public boolean completeTask(String taskId, boolean approved) {
Map<String, Object> variables = new HashMap<>();
variables.put("approved",approved);
variables.put("var",123);
taskService.complete(taskId,variables);
return true;
}
}
回调类
public class HolidayCallback implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("审核通过了,保存假期数据入库");
}
}
public class SendRejectionMailCallback implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("拒绝了申请,给申请人发送邮件");
}
}
3. 流程说明
看图说话,这里需要仔细看xml文件和流程扭转了
github地址
https://github.com/Steven-hsm/java-interview
用户手册地址
https://tkjohn.github.io/flowable-userguide/#_getting_started