天天看點

SpringBoot - 工作流Activiti開發

1.工作流

1.1 開發前奏

介紹

目前系統activit開發以springboot+mybatis開發為準,

1.1.1 IDEA安裝actiBPM

通過File -> Settings -> Plugins 找到actiBPM插件進行安裝

SpringBoot - 工作流Activiti開發
1.1.2 核心API介紹
SpringBoot - 工作流Activiti開發

ProcessEngineConfiguration:流程引擎配置。
ProcessEngine:流程引擎
核心七大接口
RepositoryService:提供一系列管理流程部署和流程定義的API。
RuntimeService:在流程運作時對流程執行個體進行管理與控制。
TaskService:對流程任務進行管理,例如任務提醒、任務完成和建立任務等。
IdentityService:提供對流程角色資料進行管理的API,這些角色資料包括使用者組、使用者及它們之間的關系。
ManagementService:提供對流程引擎進行管理和維護的服務,提供對activiti資料庫的直接通路【一般不用】
HistoryService:對流程的曆史資料進行操作,包括查詢、删除這些曆史資料。FormService:表單服務
           

1.1.3 添加依賴 pom.xml

<dependency>
   <groupId>org.activiti</groupId>
   <artifactId>activiti-spring-boot-starter-basic</artifactId>
   <version>6.0.0</version>
</dependency>
           

1.1.4 yml配置

spring:
  activiti:
    check-process-definitions: true #自動檢查、部署流程定義檔案
    database-schema-update: true #自動更新資料庫結構
    history-level: full #儲存曆史資料級别設定為full最進階别,便于曆史資料的追溯
    # process-definition-location-prefix: classpath:/processes/ #流程定義檔案存放目錄
    #process-definition-location-suffixes: #流程檔案格式
    #  - **.bpmn20.xml
    #  - **.bpmn
    
           

springboot環境下不再以activiti.cfg.xml檔案的形式配置,activiti使用starter配置後屬于spring下,是以在yml裡配置。

  • check-process-definitions【檢查Activiti資料表是否存在及版本号是否比對】預設為true,自動建立好表之後設為false。設為false會取消自動部署功能。
  • database-schema-update【在流程引擎啟動和關閉時處理資料庫模式】如下四個值:
    • false (預設值):在建立流程引擎時檢查庫模式的版本,如果版本不比對則抛出異常。
    • true:在建立流程引擎時,執行檢查并在必要時對資料庫中所有的表進行更新,如果表不存在,則自動建立。 、
    • create-drop:在建立流程引擎時,會建立資料庫的表,并在關閉流程引擎時删除資料庫的表。
    • drop-create:Activiti啟動時,執行資料庫表的删除操作,在Activiti關閉時,會執行資料庫表的建立操作。
  • history-level 【曆史資料儲存級别】
    • none:不儲存任何的曆史資料,是以,在流程執行過程中,這是最高效的。
    • activity:級别高于none,儲存流程執行個體與流程行為,其他資料不儲存。
    • audit:除activity級别會儲存的資料外,還會儲存全部的流程任務及其屬性。audit為history的預設值。
    • full:儲存曆史資料的最進階别,除了會儲存audit級别的資料外,還會儲存其他全部流程相關的細節資料,包括一些流程參數等。

1.1.5 添加processes目錄

SpringBoot內建activiti預設會從classpath下的processes目錄下讀取流程定義檔案,是以需要在src/main/resources目錄下添加processes目錄,并在目錄中建立流程檔案.

如果沒有processes目錄,則需要修改配置spring.activiti.process-definition-location-prefix,指定流程檔案存放目錄。

SpringBoot - 工作流Activiti開發

1.1.6 其他相關配置

  • 現在系統的啟動類排除org.activiti.spring.boot.SecurityAutoConfiguration即可

    @SpringBootApplication(exclude = SecurityAutoConfiguration.class,scanBasePackages="com.poly")

    public class AvtivityApplication {

    public static void main(String[] args) {
        SpringApplication.run(AvtivityApplication.class, args);
    }
               
    }
  • 官方文檔給出的啟動類配置

    @Configuration

    @ComponentScan

    @EnableAutoConfiguration

    public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
               

1.1.7 配置bpmn

  • 通過插件配置
  • 官方文檔測試bpmn檔案 命名為one-task-process.bpmn20.xml 放入processes目錄
    <?xml version="1.0" encoding="UTF-8"?>
    <definitions
            xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
            xmlns:activiti="http://activiti.org/bpmn"
            targetNamespace="Examples">
    
        <process id="oneTaskProcess" name="The One Task Process">
            <startEvent id="theStart" />
            <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
            <userTask id="theTask" name="my task" />
            <sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
            <endEvent id="theEnd" />
        </process>
    
    </definitions>
               

1.1.8 配置資料源

注意mysql連接配接池版本

# com.mysql.cj.jdbc.Driver
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/activiti_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 12345678
           

1.2 表介紹

在1.1.2~1.1.8完成後 啟動項目會生成如下表

SpringBoot - 工作流Activiti開發
  1. act_ge_ 通用資料表,ge是general的縮寫
  2. act_hi_ 曆史資料表,hi是history的縮寫,對應HistoryService接口
  3. act_id_ 身份資料表,id是identity的縮寫,對應IdentityService接口
  4. act_re_ 流程存儲表,re是repository的縮寫,對應RepositoryService接口,存儲流程部署和流程定義等靜态資料
  5. act_ru_ 運作時資料表,ru是runtime的縮寫,對應RuntimeService接口和TaskService接口,存儲流程執行個體和使用者任務等動态資料
    資源庫流程規則表
    
    1) act_re_deployment 部署資訊表
    
    2) act_re_model  流程設計模型部署表
    
    3) act_re_procdef  流程定義資料表
    
    運作時資料庫表
    
    1) act_ru_execution運作時流程執行執行個體表
    
    2) act_ru_identitylink運作時流程人員表,主要存儲任務節點與參與者的相關資訊
    
    3) act_ru_task運作時任務節點表
    
    4) act_ru_variable運作時流程變量資料表
    
    曆史資料庫表
    
    1) act_hi_actinst 曆史節點表
    
    2) act_hi_attachment曆史附件表
    
    3) act_hi_comment曆史意見表
    
    4) act_hi_identitylink曆史流程人員表
    
    5) act_hi_detail曆史詳情表,提供曆史變量的查詢
    
    6) act_hi_procinst曆史流程執行個體表
    
    7) act_hi_taskinst曆史任務執行個體表
    
    8) act_hi_varinst曆史變量表
    
    組織機構表
    
    1) act_id_group使用者組資訊表
    
    2) act_id_info使用者擴充資訊表
    
    3) act_id_membership使用者與使用者組對應資訊表
    
    4) act_id_user使用者資訊表
    
               
SpringBoot - 工作流Activiti開發

1.3 開發執行個體

1.3.1 項目開發案例

  1. 通過actiBPM設定流程

    在processes下建立bpmn流程,之後更改為bpmn20.xml格式(注:具體xml内容請看項目中processes檔案)

1.3.2 個人執行個體

思路

  1. 員工請假,先建立請假流程
  2. 員工填寫請假申請,也可以不填寫,直接結束流程
  3. 送出給直接主管審批,如果直接主管拒絕,則重新填寫,如果直接主管同意,再到部門主管審批,
  4. 部門主管審批部門主管同意,則請假流程結束,請假成功,如果部門主管不同意,傳回員工重新送出申請。
  • 建立流程檔案
    SpringBoot - 工作流Activiti開發

代碼

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1539766523202" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1539766523202" name="" targetNamespace="http://www.activiti.org/testm1539766523202" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="leave1" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" name="start"/>
    <userTask activiti:assignee="${leave.userId}" activiti:exclusive="true" id="_3" name="submit"/>
    <exclusiveGateway gatewayDirection="Unspecified" id="_4" name="result"/>
    <userTask activiti:assignee="${leave.approver1}" activiti:exclusive="true" id="_5" name="approve1"/>
    <exclusiveGateway gatewayDirection="Unspecified" id="_6" name="result"/>
    <userTask activiti:assignee="${leave.approver2}" activiti:exclusive="true" id="_7" name="approve2"/>
    <exclusiveGateway gatewayDirection="Unspecified" id="_8" name="result"/>
    <endEvent id="_9" name="end"/>
    <sequenceFlow id="_10" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_11" sourceRef="_3" targetRef="_4"/>
    <sequenceFlow id="_12" name="y" sourceRef="_4" targetRef="_5">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.submit==true}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_13" name="n" sourceRef="_4" targetRef="_9">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.submit==false}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_14" sourceRef="_5" targetRef="_6"/>
    <sequenceFlow id="_15" name="n" sourceRef="_6" targetRef="_7">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.agree1==true}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_16" sourceRef="_7" targetRef="_8"/>
    <sequenceFlow id="_17" name="y" sourceRef="_6" targetRef="_3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.agree1==false}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_18" name="n" sourceRef="_8" targetRef="_3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.agree2==false}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_19" name="y" sourceRef="_8" targetRef="_9">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.agree2==true}]]></conditionExpression>
    </sequenceFlow>
  </process>
</definitions>
           
  • 編寫接口

    @RestController

    @RequestMapping("/level/v1")

    public class LeaveController {

    public static final Logger log = LoggerFactory.getLogger(LeaveController.class);
    
    @Autowired
    private RuntimeService runtimeService;
    
    @Autowired
    private TaskService taskService;
    
    @Autowired
    private ProcessEngine processEngine;
    
    /**
     * 啟動流程
     * @param userId
     * @return
     */
    @RequestMapping(value = "/start", method = RequestMethod.GET)
    public Map<String, Object> start(@RequestParam String userId){
        Map<String, Object> vars = new HashMap<>();
        Leave leave = new Leave();
        leave.setUserId(userId);
        vars.put("leave",leave);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave1",vars);
        Map<String, Object> resultMap = new HashMap<>();
        return resultMap;
    }
    
    /**
     * 填寫請假單
     * @param leave
     * @return
     */
    @RequestMapping(value="/apply", method = RequestMethod.POST)
    public Map<String, Object> apply(@RequestBody Leave leave){
        Task task = taskService.createTaskQuery().taskId(leave.getTaskId()).singleResult();
        Map<String, Object> vars = new HashMap<>();
        Leave origin = (Leave) taskService.getVariable(leave.getTaskId(), "leave");
        origin.setDesc(leave.getDesc());
        origin.setStartDate(leave.getStartDate());
        origin.setEndDate(leave.getEndDate());
        origin.setTotalDay(leave.getTotalDay());
        origin.setApprover1(leave.getApprover1());
        origin.setApprover2(leave.getApprover2());
        origin.setSubmit(leave.getSubmit());
        vars.put("leave", origin);
        taskService.complete(leave.getTaskId(), vars);
        Map<String, Object> resultMap = ResultMapHelper.getSuccessMap();
        return resultMap;
    }
    
    /**
     * 查詢使用者流程
     * @param userId
     * @return
     */
    @RequestMapping(value = "/find", method = RequestMethod.GET)
    public Map<String, Object> find(@RequestParam("userId")String userId){
        List<Task> taskList = taskService.createTaskQuery().taskAssignee(userId).list();
        List<Leave> resultList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(taskList)){
            for(Task task : taskList){
                Leave leave = (Leave) taskService.getVariable(task.getId(),"leave");
                leave.setTaskId(task.getId());
                leave.setTaskName(task.getName());
                resultList.add(leave);
            }
        }
        Map<String, Object> resultMap = ResultMapHelper.getSuccessMap();
        resultMap.put("datas", resultList);
        return resultMap;
    }
    
    /**
     * 直接主管審批
     * @param leave
     * @return
     */
    @RequestMapping(value = "/approve1", method = RequestMethod.POST)
    public Map<String, Object> approve1(@RequestBody Leave leave){
        Task task = taskService.createTaskQuery().taskId(leave.getTaskId()).singleResult();
        Map<String, Object> vars = new HashMap<>();
        Leave origin = (Leave) taskService.getVariable(leave.getTaskId(), "leave");
        origin.setApproveDesc1(leave.getApproveDesc1());
        origin.setAgree1(leave.getAgree1());
        vars.put("leave", origin);
        taskService.complete(leave.getTaskId(),vars);
        Map<String, Object> resultMap = ResultMapHelper.getSuccessMap();
        return resultMap;
    }
    
    /**
     * 部門主管審批
     * @param leave
     * @return
     */
    @RequestMapping(value = "/approve2", method = RequestMethod.POST)
    public Map<String, Object> approve2(@RequestBody Leave leave){
        Task task = taskService.createTaskQuery().taskId(leave.getTaskId()).singleResult();
        Map<String, Object> vars = new HashMap<>();
        Leave origin = (Leave) taskService.getVariable(leave.getTaskId(), "leave");
        origin.setApproveDesc2(leave.getApproveDesc2());
        origin.setAgree2(leave.getAgree2());
        vars.put("leave", origin);
        taskService.complete(leave.getTaskId(),vars);
        Map<String, Object> resultMap = ResultMapHelper.getSuccessMap();
        return resultMap;
    }
    
    /**
     * 檢視曆史記錄
     * @param userId
     * @return
     */
    @RequestMapping(value="/findClosed", method = RequestMethod.GET)
    public Map<String, Object> findClosed(String userId){
        HistoryService historyService = processEngine.getHistoryService();
    
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processDefinitionKey("leave1").variableValueEquals("leave.userId",userId).list();
        List<Leave> leaves = new ArrayList<>();
        for(HistoricProcessInstance pi : list){
            leaves.add((Leave) pi.getProcessVariables().get("leave"));
        }
        Map<String, Object> resultMap = ResultMapHelper.getSuccessMap();
        resultMap.put("datas", leaves);
        return resultMap;
    }
               
public class Leave implements Serializable {
    private static final long serialVersionUID = 2248469053125414262L;
 
    private String userId;
 
    private Boolean submit;
 
    private Date startDate;
 
    private Date endDate;
 
    private float totalDay;
 
    private String desc;
 
    private String taskId;
 
    private String taskName;
 
    private String approver1;
 
    private Boolean agree1;
 
    private String approveDesc1;
 
    private String approver2;
 
    private Boolean agree2;
 
    private String approveDesc2;
}