springboot整合flowable框架入門步驟
最近工作中有用到工作流的開發(fā),引入了flowable工作流框架,在此記錄一下springboot整合flowable工作流框架的過程,以便后續(xù)再次使用到時可以做一些參考使用,如果項目中有涉及到流程審批的話,可以使用該框架幫我們實現(xiàn)流程圖示化展示的功能,為了快速了解flowable工作流框架的一個使用過程,我們直接步入主題,springboot整合flowable工作流框架的步驟如下:
1、首先創(chuàng)建一個springboot工程,然后引入flowable pom依賴,代碼如下:
<dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.7.0</version> </dependency>
2、創(chuàng)建流程圖定義文檔
這里有一個使用flowable-ui可視化的工程來創(chuàng)建流程圖定義文檔,https://www.wandouip.com/t5i212543/,具體實施過程如下:
先從 https://github.com/flowable/flowable-engine/releases 上下載一個發(fā)布文檔,這里選擇Flowable 6.7.2 release;然后解壓縮文件,將里面的wars文檔下的兩個jar包(flowable-rest.war、flowable-ui.war)部署到tomcat下,放到webapps文件加下,點擊運行,運行存在一個解壓縮文件的過程,會產(chǎn)生flowable-rest、flowable-ui文件夾,瀏覽器輸入 http://localhost:8080/flowable-ui,如下圖:
用戶名密碼輸入admin/test,如下圖:
點擊建模器應(yīng)用程序,點擊右上角”創(chuàng)建流程“,填寫相關(guān)信息,進(jìn)去后就可以可視化地創(chuàng)建流程,如下圖:
上面初步展示了使用可視化制作工具制作流程圖的過程,然后導(dǎo)出BPMN2文件,文件內(nèi)容大概如下:
<?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" exporter="Flowable Open Source Modeler" exporterVersion="6.7.2"> <process id="test" name="test" isExecutable="true"> <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent> <userTask id="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB" name="初級管理員" flowable:formFieldValidation="true"></userTask> <sequenceFlow id="sid-BD25A451-66F1-4AF1-B954-5EE5BC5A5782" sourceRef="startEvent1" targetRef="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB"></sequenceFlow> <exclusiveGateway id="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541"></exclusiveGateway> <sequenceFlow id="sid-D9571C82-9071-41EA-9858-D10FF50B4396" sourceRef="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB" targetRef="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541"></sequenceFlow> <userTask id="sid-1EF0B969-8092-4EC5-9899-16D066122EC8" name="高級管理員" flowable:formFieldValidation="true"></userTask> <sequenceFlow id="sid-7E40EF52-4ED7-4785-954B-3530C3400D81" sourceRef="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541" targetRef="sid-1EF0B969-8092-4EC5-9899-16D066122EC8"></sequenceFlow> <intermediateThrowEvent id="sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F"></intermediateThrowEvent> <sequenceFlow id="sid-72B217BF-4F36-446E-B48F-741E3F38B66F" sourceRef="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541" targetRef="sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F"></sequenceFlow> <exclusiveGateway id="sid-DB335453-27CB-4992-824A-3C060312F59F"></exclusiveGateway> <sequenceFlow id="sid-60B0EF34-8DB4-421C-A265-59E8A5B7C35F" sourceRef="sid-1EF0B969-8092-4EC5-9899-16D066122EC8" targetRef="sid-DB335453-27CB-4992-824A-3C060312F59F"></sequenceFlow> <intermediateThrowEvent id="sid-D6512D07-3215-48FC-A568-227A718EC174"></intermediateThrowEvent> <sequenceFlow id="sid-0ABE9D33-F08C-4787-A827-27639909C63A" sourceRef="sid-DB335453-27CB-4992-824A-3C060312F59F" targetRef="sid-D6512D07-3215-48FC-A568-227A718EC174"></sequenceFlow> <intermediateThrowEvent id="sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2"></intermediateThrowEvent> <sequenceFlow id="sid-50C31AEE-6B3A-48A2-92D3-7D640E5FF60E" sourceRef="sid-DB335453-27CB-4992-824A-3C060312F59F" targetRef="sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_test"> <bpmndi:BPMNPlane bpmnElement="test" id="BPMNPlane_test"> <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1"> <omgdc:Bounds height="30.0" width="30.0" x="90.0" y="166.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB" id="BPMNShape_sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB"> <omgdc:Bounds height="80.0" width="100.0" x="240.0" y="141.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541" id="BPMNShape_sid-57C003F8-C0F3-4499-8B1D-0B29223DB541"> <omgdc:Bounds height="40.0" width="40.0" x="385.0" y="161.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-1EF0B969-8092-4EC5-9899-16D066122EC8" id="BPMNShape_sid-1EF0B969-8092-4EC5-9899-16D066122EC8"> <omgdc:Bounds height="80.0" width="100.0" x="470.0" y="141.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F" id="BPMNShape_sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F"> <omgdc:Bounds height="30.0" width="30.0" x="390.0" y="270.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-DB335453-27CB-4992-824A-3C060312F59F" id="BPMNShape_sid-DB335453-27CB-4992-824A-3C060312F59F"> <omgdc:Bounds height="40.0" width="40.0" x="615.0" y="161.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-D6512D07-3215-48FC-A568-227A718EC174" id="BPMNShape_sid-D6512D07-3215-48FC-A568-227A718EC174"> <omgdc:Bounds height="30.0" width="30.0" x="700.0" y="166.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2" id="BPMNShape_sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2"> <omgdc:Bounds height="30.0" width="30.0" x="620.0" y="270.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="sid-0ABE9D33-F08C-4787-A827-27639909C63A" id="BPMNEdge_sid-0ABE9D33-F08C-4787-A827-27639909C63A" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="15.0" flowable:targetDockerY="15.0"> <omgdi:waypoint x="654.557806573957" y="181.379746835443"></omgdi:waypoint> <omgdi:waypoint x="700.0002881553987" y="181.0940234142237"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-72B217BF-4F36-446E-B48F-741E3F38B66F" id="BPMNEdge_sid-72B217BF-4F36-446E-B48F-741E3F38B66F" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="15.5" flowable:targetDockerY="3.0"> <omgdi:waypoint x="405.5" y="200.43965611353715"></omgdi:waypoint> <omgdi:waypoint x="405.5" y="270.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-BD25A451-66F1-4AF1-B954-5EE5BC5A5782" id="BPMNEdge_sid-BD25A451-66F1-4AF1-B954-5EE5BC5A5782" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0"> <omgdi:waypoint x="119.94999946593475" y="181.0"></omgdi:waypoint> <omgdi:waypoint x="239.9999999999298" y="181.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-D9571C82-9071-41EA-9858-D10FF50B4396" id="BPMNEdge_sid-D9571C82-9071-41EA-9858-D10FF50B4396" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5"> <omgdi:waypoint x="339.9499999999977" y="181.21623376623376"></omgdi:waypoint> <omgdi:waypoint x="385.4130434782609" y="181.41304347826087"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-7E40EF52-4ED7-4785-954B-3530C3400D81" id="BPMNEdge_sid-7E40EF52-4ED7-4785-954B-3530C3400D81" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0"> <omgdi:waypoint x="424.52473707274277" y="181.41666666666666"></omgdi:waypoint> <omgdi:waypoint x="469.99999999999386" y="181.21812227074238"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-60B0EF34-8DB4-421C-A265-59E8A5B7C35F" id="BPMNEdge_sid-60B0EF34-8DB4-421C-A265-59E8A5B7C35F" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5"> <omgdi:waypoint x="569.9499999999981" y="181.21623376623378"></omgdi:waypoint> <omgdi:waypoint x="615.4130434782609" y="181.41304347826087"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-50C31AEE-6B3A-48A2-92D3-7D640E5FF60E" id="BPMNEdge_sid-50C31AEE-6B3A-48A2-92D3-7D640E5FF60E" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="15.0" flowable:targetDockerY="15.0"> <omgdi:waypoint x="635.4077669902913" y="200.53271096023283"></omgdi:waypoint> <omgdi:waypoint x="635.072221397189" y="270.00017140256364"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
以上初步實現(xiàn)怎么使用可視化工具來創(chuàng)建流程圖定義文件,里面更為具體的使用方法還有待探索,上面的步驟執(zhí)行完畢后,我們就可以運行我們的springboot工程了,在配置文件中配置好數(shù)據(jù)庫鏈接相關(guān)的信息后,然后配置flowable相關(guān)信息:
flowable:
aysnc-executor-activate: false
database-schema-update: true
process-definition-location-prefix: classpath*:/processes/
process-definition-location-suffixes: "**.bpmn20.xml, **.bpmn"
上面的信息配置完畢后,就可以運行我們的工程了,在運行工程之前,在工程的resources文件夾下創(chuàng)建一個processes文件夾,我們的流程定義文檔就放在這個文件夾下,創(chuàng)建一個process-test.bpmn20.xml放到processes文件夾下,具體內(nèi)容定義如下:
<?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:activiti="http://activiti.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.activiti.org/test"> <process id="processTest" name="Request Resource Approval " isExecutable="true"> <startEvent id="starter" name="Starter"></startEvent> <serviceTask id="sendJuniorRejectEmail" name="發(fā)送初級審批拒絕郵件" activiti:class="com.chinaums.web.controller.flowable2.delegate.SendJuniorRejectionMailDelegate"> </serviceTask> <endEvent id="juniorRejectEnd" name="Junior Reject End"></endEvent> <sequenceFlow id="flow5" sourceRef="sendJuniorRejectEmail" targetRef="juniorRejectEnd"></sequenceFlow> <userTask id="seniorApproval" name="高級審批" activiti:assignee="${seniorAdmin}"></userTask> <userTask id="juniorApproval" name="初級審批" activiti:assignee="${juniorAdmin}"></userTask> <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway1"></exclusiveGateway> <sequenceFlow id="juniorSuccessFlow" name="同意" sourceRef="exclusivegateway1" targetRef="seniorApproval"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${approved=='Y'}]]> </conditionExpression> </sequenceFlow> <sequenceFlow id="juniorRejectFlow" name="拒絕" sourceRef="exclusivegateway1" targetRef="sendJuniorRejectEmail"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${approved=='N'}]]> </conditionExpression> </sequenceFlow> <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway2"></exclusiveGateway> <sequenceFlow id="flow7" sourceRef="seniorApproval" targetRef="exclusivegateway2"></sequenceFlow> <endEvent id="approvalSuccessEnd" name="Approval Success End"></endEvent> <sequenceFlow id="seniorSuccessFlow" name="同意" sourceRef="exclusivegateway2" targetRef="sendApprovalSuccessEmail"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${approved=='Y'}]]> </conditionExpression> </sequenceFlow> <serviceTask id="sendSeniorRejectEmail" name="發(fā)送高級審批拒絕郵件" activiti:class="com.chinaums.web.controller.flowable2.delegate.SendSeniorRejectionMailDelegate"> </serviceTask> <endEvent id="seniorRejectEnd" name="Senior Reject End"></endEvent> <sequenceFlow id="seniorRejectFlow" name="拒絕" sourceRef="exclusivegateway2" targetRef="sendSeniorRejectEmail"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${approved=='N'}]]> </conditionExpression> </sequenceFlow> <sequenceFlow id="flow9" sourceRef="sendSeniorRejectEmail" targetRef="seniorRejectEnd"></sequenceFlow> <sequenceFlow id="flow11" sourceRef="juniorApproval" targetRef="exclusivegateway1"></sequenceFlow> <sequenceFlow id="flow12" sourceRef="starter" targetRef="juniorApproval"></sequenceFlow> <serviceTask id="sendApprovalSuccessEmail" name="發(fā)送審批通過郵件" activiti:class="com.chinaums.web.controller.flowable2.delegate.SendApprovalSuccessEmailDelegate"></serviceTask> <sequenceFlow id="flow13" sourceRef="sendApprovalSuccessEmail" targetRef="approvalSuccessEnd"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_requestResourceApprovalProcess"> <bpmndi:BPMNPlane bpmnElement="requestResourceApprovalProcess" id="BPMNPlane_requestResourceApprovalProcess"> <bpmndi:BPMNShape bpmnElement="starter" id="BPMNShape_starter"> <omgdc:Bounds height="35.0" width="35.0" x="45.0" y="118.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sendJuniorRejectEmail" id="BPMNShape_sendJuniorRejectEmail"> <omgdc:Bounds height="71.0" width="171.0" x="340.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="juniorRejectEnd" id="BPMNShape_juniorRejectEnd"> <omgdc:Bounds height="35.0" width="35.0" x="408.0" y="385.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="seniorApproval" id="BPMNShape_seniorApproval"> <omgdc:Bounds height="78.0" width="121.0" x="575.0" y="95.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="juniorApproval" id="BPMNShape_juniorApproval"> <omgdc:Bounds height="81.0" width="115.0" x="170.0" y="95.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1"> <omgdc:Bounds height="40.0" width="40.0" x="405.0" y="113.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2"> <omgdc:Bounds height="40.0" width="40.0" x="765.0" y="115.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="approvalSuccessEnd" id="BPMNShape_approvalSuccessEnd"> <omgdc:Bounds height="35.0" width="35.0" x="1140.0" y="117.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sendSeniorRejectEmail" id="BPMNShape_sendSeniorRejectEmail"> <omgdc:Bounds height="71.0" width="192.0" x="690.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="seniorRejectEnd" id="BPMNShape_seniorRejectEnd"> <omgdc:Bounds height="35.0" width="35.0" x="768.0" y="385.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sendApprovalSuccessEmail" id="BPMNShape_sendApprovalSuccessEmail"> <omgdc:Bounds height="75.0" width="141.0" x="920.0" y="96.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"> <omgdi:waypoint x="425.0" y="301.0"></omgdi:waypoint> <omgdi:waypoint x="425.0" y="385.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="juniorSuccessFlow" id="BPMNEdge_juniorSuccessFlow"> <omgdi:waypoint x="445.0" y="133.0"></omgdi:waypoint> <omgdi:waypoint x="575.0" y="134.0"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="16.0" width="32.0" x="445.0" y="133.0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="juniorRejectFlow" id="BPMNEdge_juniorRejectFlow"> <omgdi:waypoint x="425.0" y="153.0"></omgdi:waypoint> <omgdi:waypoint x="425.0" y="230.0"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="16.0" width="32.0" x="425.0" y="153.0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7"> <omgdi:waypoint x="696.0" y="134.0"></omgdi:waypoint> <omgdi:waypoint x="765.0" y="135.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="seniorSuccessFlow" id="BPMNEdge_seniorSuccessFlow"> <omgdi:waypoint x="805.0" y="135.0"></omgdi:waypoint> <omgdi:waypoint x="920.0" y="133.0"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="16.0" width="32.0" x="805.0" y="135.0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="seniorRejectFlow" id="BPMNEdge_seniorRejectFlow"> <omgdi:waypoint x="785.0" y="155.0"></omgdi:waypoint> <omgdi:waypoint x="786.0" y="230.0"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="16.0" width="32.0" x="785.0" y="155.0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9"> <omgdi:waypoint x="786.0" y="301.0"></omgdi:waypoint> <omgdi:waypoint x="785.0" y="385.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11"> <omgdi:waypoint x="285.0" y="135.0"></omgdi:waypoint> <omgdi:waypoint x="405.0" y="133.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12"> <omgdi:waypoint x="80.0" y="135.0"></omgdi:waypoint> <omgdi:waypoint x="170.0" y="135.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow13" id="BPMNEdge_flow13"> <omgdi:waypoint x="1061.0" y="133.0"></omgdi:waypoint> <omgdi:waypoint x="1140.0" y="134.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
上面文件中的process標(biāo)簽的id值是該流程定義的唯一標(biāo)識,在創(chuàng)建流程實例時需要傳入processId標(biāo)識,上面的assignee變量后面定義了一個變量${seniorAdmin},這個是由接口調(diào)用時傳入的,activiti:class后面的值是一個類,表示執(zhí)行到這個步驟時會觸發(fā)執(zhí)行某個動作,比如id為sendJuniorRectEmail的serviceTask中的class定義如下:
@Slf4j public class SendJuniorRejectionMailDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) { String requestUser = (String) execution.getVariable("requestUser"); String resourceId = (String) execution.getVariable("resourceId"); System.out.println("SendJuniorRejectionMailDelegate"); log.info("send approval success mail for user [" + requestUser + "] with apply resource [" + resourceId + "]"); } }
上面的類需要實現(xiàn)JavaDelegate這個接口,上面的內(nèi)容定義完畢后,就可以定義我們的實現(xiàn)方法了,先創(chuàng)建一個接口,定義一些方法:
public interface IProcess { /** * 創(chuàng)建一個流程實例,創(chuàng)建實例時會登記一些信息,這些信息可以通過調(diào)用 * queryProcessVariables方法獲取到,調(diào)用時需要傳遞processInstanceId * @param paramObj * @return */ ProcessInstanceEntity startProcess(ParamObj paramObj); /** * 獲取指定工作人的代辦任務(wù) * @param assignee * @return */ List<TaskInstanceEntity> taskInstance(String assignee); /** * 處理工作 * @param paramObj */ void handleTask(ParamObj paramObj); /** * 獲取某個流程實體的狀態(tài),各個審批環(huán)節(jié)所處的狀態(tài)信息 * @param processInstanceId * @return */ List<ProcessStatusEntity> queryProcessStatus(String processInstanceId); /** * 查看創(chuàng)建流程實例時登記的變量信息 * @param processInstanceId * @return */ Map<String,Object> queryProcessVariables(String processInstanceId); /** * 獲取某人的歷史審批數(shù)據(jù) * @param assignee * @return */ List<HistanceInstanceEntity> queryHistoryProcess(String assignee); /** * 生成流程的圖譜 * @param httpServletResponse * @param processInstanceId */ void genProcessDiagram(HttpServletResponse httpServletResponse, String processInstanceId) throws Exception; /** * 查詢是否存在歷史數(shù)據(jù)的流程實例 * @param processInstanceId * @return */ boolean isExistHistoricProcessInstance(String processInstanceId); /** * 查詢指定的流程是否是運行中的流程 * @param processInstanceId * @return */ boolean isExistRunningProcessInstance(String processInstanceId); /** * 將指定的流程掛起 * @param processInstanceId */ void suspendProcessInstance(String processInstanceId); /** * 終止項目流程 * @param paramObj */ void terminateProcessInstance(ParamObj paramObj); /** * 將指定的流程激活 * @param processInstanceId */ void activateProcessInstance(String processInstanceId); /** * 刪除流程實例 * @param paramObj */ void deleteProcessInstance(ParamObj paramObj); /** * 將任務(wù)返回到某一步驟 * @param taskId * @param targetTaskKey 返回到的目標(biāo)任務(wù)ID */ void rollbackTask(String taskId, String targetTaskKey); boolean isProcessFinished(String processInstanceId); }
定義好接口后,再定義一個實現(xiàn)類:
@Service public class IProcessImpl implements IProcess { @Autowired private RepositoryService repositoryService; private RuntimeService runtimeService; private TaskService taskService; private HistoryService historyService; private ProcessEngine processEngine; ManagementService managementService; @Override public ProcessInstanceEntity startProcess(ParamObj paramObj) { Map<String, Object> variables = new HashMap<>(); // 請求的資源ID variables.put("resourceId", paramObj.getResourceId()); // 請求發(fā)起用戶 variables.put("requestUser", paramObj.getRequestUser()); // 初級審批用戶 variables.put("juniorAdmin", paramObj.getJuniorAdmin()); // 高級審批用戶 variables.put("seniorAdmin", paramObj.getSeniorAdmin()); ProcessInstance processInstance=runtimeService. startProcessInstanceByKey(ConstantValues.FLOWABLE_PROCESS_TEST, variables); ProcessInstanceEntity entity=new ProcessInstanceEntity(); entity.setProcessDeploymentId(processInstance.getDeploymentId()); entity.setProcessInstanceId(processInstance.getProcessInstanceId()); entity.setActivityId(processInstance.getActivityId()); return entity; } public List<TaskInstanceEntity> taskInstance(String assignee) { List<TaskInstanceEntity> entities=new ArrayList<>(); List<Task> tasks= taskService.createTaskQuery().taskAssignee(assignee).orderByTaskCreateTime().desc().list(); if(!CollectionUtils.isEmpty(tasks)){ tasks.stream().forEach(task -> { TaskInstanceEntity entity=new TaskInstanceEntity(); String id=task.getId(); entity.setCreateTime(task.getCreateTime()); entity.setTaskName(task.getName()); entity.setProcessInstanceId(task.getProcessInstanceId()); entity.setTaskId(id); Map<String, Object> processVariables = taskService.getVariables(id); entity.setRequestUser(processVariables.get("requestUser").toString()); entity.setResourceId(processVariables.get("resourceId").toString()); entities.add(entity); }); } return entities; public void handleTask(ParamObj paramObj) { Map<String, Object> taskVariables = new HashMap<>(); String approved=paramObj.isApproved()?"Y":"N"; taskVariables.put("approved", approved); //審核結(jié)果和審核意見都封裝為JSON然后放在評論里,后續(xù)需要進(jìn)行逆操作。 ObjectMapper objectMapper = new ObjectMapper(); Map<String, String> map= new HashMap<>(); map.put("approved", approved); map.put("comment", paramObj.getComment()); try { String json = objectMapper.writeValueAsString(map); taskService.addComment(paramObj.getTaskId(), null, json); taskService.complete(paramObj.getTaskId(), taskVariables); } catch (Exception e) { throw new RuntimeException(e); public List<ProcessStatusEntity> queryProcessStatus(String processInstanceId) { List<ProcessStatusEntity> result = new ArrayList<>(); List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId).list(); if(CollectionUtils.isEmpty(historicTaskInstances)) { throw new RuntimeException("Process instance [" + processInstanceId + "] not exist"); for (HistoricTaskInstance hti : historicTaskInstances) { String taskId = hti.getId(); String taskName = hti.getName(); String assignee = hti.getAssignee(); Date createTime = hti.getCreateTime(); String comment = null; String approved=null; List<Comment> comments = taskService.getTaskComments(taskId); if (!CollectionUtils.isEmpty(comments)) { comment = comments.get(0).getFullMessage(); if(null!=comment) { //這里進(jìn)行評論的JSON數(shù)據(jù)的逆操作提取數(shù)據(jù) ObjectMapper mapper = new ObjectMapper(); try { Map<String,Object> data = mapper.readValue(comment, Map.class); approved=data.get("approved").toString(); comment=data.get("comment").toString(); } catch (Exception e) { System.out.println(e.toString()); } } } ProcessStatusEntity pd=new ProcessStatusEntity(); pd.setTaskName(taskName); pd.setAssignee(assignee); pd.setCreateTime(createTime); pd.setApproved(approved); pd.setComment(comment); pd.setTaskId(hti.getId()); pd.setProcessInstanceId(hti.getProcessInstanceId()); result.add(pd); return result; public Map<String, Object> queryProcessVariables(String processInstanceId) { List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery() if (historicVariableInstances == null) { Map<String,Object> ret= new HashMap<>(); for(HistoricVariableInstance var: historicVariableInstances) { ret.put(var.getVariableName(), var.getValue()); return ret; public List<HistanceInstanceEntity> queryHistoryProcess(String assignee) { List<HistanceInstanceEntity> result=new ArrayList<>(); List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery() .taskAssignee(assignee).finished().orderByHistoricActivityInstanceEndTime().desc().list(); for(HistoricActivityInstance h : activities) { HistanceInstanceEntity d=new HistanceInstanceEntity(); d.setProcessInstanceId(h.getProcessInstanceId()); d.setTaskId(h.getTaskId()); d.setStartTime(h.getStartTime()); d.setEndTime(h.getEndTime()); result.add(d); public void genProcessDiagram(HttpServletResponse httpServletResponse, String processInstanceId) throws Exception{ ProcessInstance pi = runtimeService.createProcessInstanceQuery(). processInstanceId(processInstanceId).singleResult(); //流程走完的不顯示圖 if (pi == null) { // System.out.println("不存在該流程或則流程已經(jīng)走完"); throw new RuntimeException("不存在該流程或則流程已經(jīng)走完"); // return; Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult(); //使用流程實例ID,查詢正在執(zhí)行的執(zhí)行對象表,返回流程實例對象 String InstanceId = task.getProcessInstanceId(); List<Execution> executions = runtimeService .createExecutionQuery() .processInstanceId(InstanceId) .list(); //得到正在執(zhí)行的Activity的Id List<String> activityIds = new ArrayList<>(); List<String> flows = new ArrayList<>(); for (Execution exe : executions) { List<String> ids = runtimeService.getActiveActivityIds(exe.getId()); activityIds.addAll(ids); //獲取流程圖 BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); ProcessEngineConfiguration engineConf = processEngine.getProcessEngineConfiguration(); ProcessDiagramGenerator diagramGenerator = engineConf.getProcessDiagramGenerator(); InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engineConf.getActivityFontName(), engineConf.getLabelFontName(), engineConf.getAnnotationFontName(), engineConf.getClassLoader(), 1.0,true); OutputStream out = null; byte[] buf = new byte[1024]; int length = 0; out = httpServletResponse.getOutputStream(); while ((length = in.read(buf)) != -1) { out.write(buf, 0, length); } finally { if (in != null) { in.close(); if (out != null) { out.close(); public boolean isExistHistoricProcessInstance(String processInstanceId) { HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery(). processInstanceId(processInstanceId).singleResult(); if (historicProcessInstance == null) { return false; return true; public boolean isExistRunningProcessInstance(String processInstanceId) { ProcessInstance processInstance = runtimeService.createProcessInstanceQuery(). if (processInstance == null) { public void suspendProcessInstance(String processInstanceId) { runtimeService.suspendProcessInstanceById(processInstanceId); public void terminateProcessInstance(ParamObj paramObj) { runtimeService.deleteProcessInstance(paramObj.getProcessInstanceId(),paramObj.getDeleteReason()); public void activateProcessInstance(String processInstanceId) { runtimeService.activateProcessInstanceById(processInstanceId); public void deleteProcessInstance(ParamObj paramObj) { //查詢是否操作 long count = runtimeService.createExecutionQuery().processInstanceId(paramObj.getProcessInstanceId()).count(); if(count>0){ DeleteFlowableProcessInstanceCmd cmd= new DeleteFlowableProcessInstanceCmd(paramObj.getProcessInstanceId(), paramObj.getDeleteReason(),true); managementService.executeCommand(cmd); //runtimeService.deleteProcessInstance(processInstanceId,deleteReason); }else{ //刪除歷史數(shù)據(jù)的流程實體 historyService.deleteHistoricProcessInstance(paramObj.getProcessInstanceId()); public void rollbackTask(String taskId, String targetTaskKey) { Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult(); if (currentTask == null) { return ; List<String> key = new ArrayList<>(); key.add(currentTask.getTaskDefinitionKey()); runtimeService.createChangeActivityStateBuilder() .processInstanceId(currentTask.getProcessInstanceId()) .moveActivityIdsToSingleActivityId(key, targetTaskKey) .changeState(); public boolean isProcessFinished(String processInstanceId) { return historyService.createHistoricProcessInstanceQuery().finished() .processInstanceId(processInstanceId).count()>0; }
實現(xiàn)類中注入的變量都是flowable框架中的變量,實現(xiàn)類中的方法的作用在接口中都有向相關(guān)注釋,其中deleteProcessInstance方法中會引用一個類來刪除流程實例,DeleteFlowableProcessInstanceCmd類的定義如下:
@Data public class DeleteProcessInstanceCmd implements Command<Void>, Serializable { String processInstanceId; String deleteReason; //是否刪除歷史 boolean cascade=true; public DeleteProcessInstanceCmd(){ } public DeleteProcessInstanceCmd(String processInstanceId,String deleteReason){ this.deleteReason=deleteReason; this.processInstanceId=processInstanceId; } public DeleteProcessInstanceCmd(String processInstanceId, String deleteReason, boolean cascade){ this.deleteReason=deleteReason; this.processInstanceId=processInstanceId; this.cascade=cascade; } @Override public Void execute(CommandContext commandContext) { ExecutionEntity entity= CommandContextUtil.getExecutionEntityManager(commandContext) .findById(processInstanceId); if(entity!=null){ if(entity.isDeleted()){ return null; } if(Flowable5Util.isFlowable5ProcessDefinitionId(commandContext,entity.getProcessDefinitionId())){ Flowable5CompatibilityHandler handler=Flowable5Util.getFlowable5CompatibilityHandler(); handler.deleteProcessInstance(processInstanceId,deleteReason); }else{ CommandContextUtil.getExecutionEntityManager(commandContext).deleteProcessInstance(entity.getProcessInstanceId(),deleteReason,cascade); } } return null; } }
上述功能了類定義完畢后,就可以創(chuàng)建我們的controller類了,我們的controller類的定義如下:
@RestController @RequestMapping("/flowableTest") public class FlowableController { @Autowired IProcess process; @PostMapping("/startProcess") public ProcessInstanceEntity startProcess(@RequestBody ParamObj paramObj){ return process.startProcess(paramObj); } @GetMapping("/getTaskInstance/{assignee}") public List<TaskInstanceEntity> taskInstance(@PathVariable("assignee") String assignee){ return process.taskInstance(assignee); } @PutMapping("/handleTask") public String handleTask(@RequestBody ParamObj paramObj){ process.handleTask(paramObj); return "success"; } @GetMapping("/queryProcessStatus") public List<ProcessStatusEntity> queryProcessStatus(String processInstanceId){ return process.queryProcessStatus(processInstanceId); } @GetMapping("/queryProcessVariables") public Map<String, Object> queryProcessVariables(String processInstanceId){ return process.queryProcessVariables(processInstanceId); } @GetMapping("/queryHistoryProcess") public List<HistanceInstanceEntity> queryHistoryProcess(String assignee){ return process.queryHistoryProcess(assignee); } @GetMapping("/genProcessDiagram") public void genProcessDiagram(HttpServletResponse httpServletResponse, String processInstanceId) throws Exception { process.genProcessDiagram(httpServletResponse,processInstanceId); } @GetMapping("/isExistHistoricProcessInstance") public boolean isExistHistoricProcessInstance(String processInstanceId){ return process.isExistHistoricProcessInstance(processInstanceId); } @GetMapping("/isProcessFinished") public boolean isProcessFinished(String processInstanceId){ return process.isProcessFinished(processInstanceId); } @GetMapping("/isExistRunningProcessInstance") public boolean isExistRunningProcessInstance(String processInstanceId){ return process.isExistRunningProcessInstance(processInstanceId); } @PutMapping("/suspendProcessInstance") public String suspendProcessInstance(String processInstanceId){ process.suspendProcessInstance(processInstanceId); return "流程 "+processInstanceId+" 已經(jīng)掛起"; } @PutMapping("/terminateProcessInstance") public String terminateProcessInstance(ParamObj paramObj){ process.terminateProcessInstance(paramObj); return "流程 "+paramObj.getProcessInstanceId()+" 已經(jīng)終止"; } @PutMapping("/activateProcessInstance") public String activateProcessInstance(String processInstanceId) { process.activateProcessInstance(processInstanceId); return "流程 "+processInstanceId+" 已經(jīng)激活"; } @PutMapping("/deleteProcessInstance") public String deleteProcessInstance(ParamObj paramObj){ process.deleteProcessInstance(paramObj); return "流程 "+paramObj.getProcessInstanceId()+" 已經(jīng)刪除"; } @PutMapping("/rollback") public String rollbackTask(String taskId, String targetTaskKey){ process.rollbackTask(taskId,targetTaskKey); return "流程回退成功"; } }
試運行工程,運行工程后,系統(tǒng)會自動生成一些表單,是一些act_和flw_開頭的表單,如下,利用postman創(chuàng)建一個流程實例:
查看流程示例圖:
調(diào)用查看任務(wù)接口,可以查看某個人有哪些任務(wù)需要處理:
調(diào)用任務(wù)處理接口:
查看處理后的結(jié)果:
以上是flowable流程框架的簡單應(yīng)用,更為詳細(xì)的使用等待后續(xù)的挖掘......
到此這篇關(guān)于springboot整合flowable-初步入門的文章就介紹到這了,更多相關(guān)springboot整合flowable內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
說一說java關(guān)鍵字final和transient
這篇文章主要和大家說一說java關(guān)鍵字final和transient,感興趣的小伙伴們可以參考一下2016-06-06Springboot引入hibernate配置自動建表并進(jìn)行增刪改查操作
這篇文章主要介紹了Springboot引入hibernate配置自動建表并進(jìn)行增刪改查,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09SSH框架網(wǎng)上商城項目第19戰(zhàn)之訂單信息級聯(lián)入庫以及頁面緩存問題
這篇文章主要介紹了SSH框架網(wǎng)上商城項目第19戰(zhàn)之訂單信息級聯(lián)入庫以及頁面緩存問題,感興趣的小伙伴們可以參考一下2016-06-06如何使用Idea中的 Deployment 實現(xiàn)打包自動部署
這篇文章主要介紹了使用Idea中的 Deployment 實現(xiàn)打包自動部署,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08