Springboot整合Flowable并进行一个通用审批流程应用实践

Springboot整合Flowable并进行一个通用审批流程应用实践,第1张

Springboot整合Flowable并进行一个通用审批流程应用实践
  • 一、Flowable介绍
  • 二、springboot整合Flowable6.7.2
    • 1.springboot和flowable的版本
    • 2.pom.xml引入
    • 3.application.yml中配置flowable的参数
    • 4.建好数据库flowabletest,然后启动
    • 5.定义流程文件
    • 6.代理类准备
    • 6.启动springboot工程让springboot帮我们部署好流程
    • 7.接下来写controller类进行测试
    • 8.接下来可以进行演示了
  • 三、Spring Boot 整合 Flowable-ui-modeler 6.7.2
    • 1.下载前端文件
    • 2.添加Flowable-ui-modeler依赖
    • 3.先启动测试一波
    • 4.配置idm
    • 5.绕过Flowable授权
    • 6.配置账号信息
    • 7. Flowable Modeler匿名账号的处理
    • 8.重启测试
    • 9.在线流程进行发布
  • 四、通用审批流程的应用实践
    • 1.通用审批流程设计
    • 2.业务扩展数据库设计
    • 3.流转说明图
    • 4. 测试说明
  • 五、flowable和mybatis同时存在时问题解决
  • 六、源码下载及说明


一、Flowable介绍
##官方文档
https://www.flowable.org/docs/userguide/index.html

Flowable是一个使用Java编写的轻量级业务流程引擎
Flowable是Activiti的fork

二、springboot整合Flowable6.7.2

基本的整合是参考这篇文章的,CSDN博主「水中加点糖」这位大神的采用springboot+flowable快速实现工作流,阅读量达20万以上,太厉害了,按照这篇文章做下来,结合我的实际情况,很顺利完成基本的整合

1.springboot和flowable的版本

springboot的2.6.2版本和的flowable的6.7.2比较搭配,因此使用这2个版本配对

springBoot版本:2.6.2
flowable版本:6.7.2
2.pom.xml引入

引入flowable-spring-boot-starter和mybatis相关

	<properties>
		<flowable.version>6.7.2</flowable.version>
	</properties>
		<!--flowable工作流依赖 start -->
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-spring-boot-starter</artifactId>
			<version>${flowable.version}</version>
		</dependency>
		<!--flowable工作流依赖 end -->

		<!-- mybatis start -->
		<!--pagehelper分页 -->
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper-spring-boot-starter</artifactId>
			<version>${pagehelper.starter.version}</version>
		</dependency>
		<!-- druid -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.10</version>
		</dependency>
		<!-- mysql jdbc -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<!-- <version>5.1.45</version> -->
		</dependency>
		<!-- mybatis end -->
3.application.yml中配置flowable的参数

关闭定时任务JOB及启动检查数据库创建参数database-schema-update

flowable:
#关闭定时任务JOB
  async-executor-activate: false
  database-schema-update: true
4.建好数据库flowabletest,然后启动

启动时有个注意的:我的springboot和flowable版本的搭配如果配的是mysql8.0的驱动,会报表不存在的错误,因此先改成5.1.45的驱动,会成功创建表

CREATE DATABASE `flowabletest` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */

我这边测试时用mysql8.0驱动时,会报表不存在,然后将以下注解开放,即mysql驱动改成5.1.45,application.yml中也相应修改驱动名为com.mysql.jdbc.Driver,然后重启,可以新建表成功。如果你没有这个问题,可以不用管这一个,建表成功后,可以将flowable.database-schema-update参数改成false,这个生产环境下建议还是改成false,另外建表成功后,可以把mysql的驱动恢复成8.0的(用8.0驱动建表不成功原因不明)。

<!-- mysql jdbc -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<!-- <version>5.1.45</version> -->
</dependency>

5.定义流程文件

这里我也就直接复制上面引用文章中的流程文件,有不少的人是直接复制这个流程文件的,但自己建的包名又不一样,因此会报包不存在的问题,因此,我们拿到这个流程文件内容后,将里面的类全名路径,可以改成你自己的包名里面去,然后将其命令为ExpenseProcess.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: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="Expense" name="ExpenseProcess" isExecutable="true">
        <documentation>报销流程</documentation>
        <startEvent id="start" name="开始"></startEvent>
        <userTask id="fillTask" name="出差报销" flowable:assignee="${taskUser}">
            <extensionElements>
                <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler">
                    <![CDATA[false]]></modeler:initiator-can-complete>
            </extensionElements>
        </userTask>
        <exclusiveGateway id="judgeTask"></exclusiveGateway>
        <userTask id="directorTak" name="经理审批">
            <extensionElements>
                <flowable:taskListener event="create"
                                       class="cn.gzsendi.flowable.listeners.ManagerTaskHandler"></flowable:taskListener>
            </extensionElements>
        </userTask>
        <userTask id="bossTask" name="老板审批">
            <extensionElements>
                <flowable:taskListener event="create"
                                       class="cn.gzsendi.flowable.listeners.BossTaskHandler"></flowable:taskListener>
            </extensionElements>
        </userTask>
        <endEvent id="end" name="结束"></endEvent>
        <sequenceFlow id="directorNotPassFlow" name="驳回" sourceRef="directorTak" targetRef="fillTask">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="bossNotPassFlow" name="驳回" sourceRef="bossTask" targetRef="fillTask">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow1" sourceRef="start" targetRef="fillTask"></sequenceFlow>
        <sequenceFlow id="flow2" sourceRef="fillTask" targetRef="judgeTask"></sequenceFlow>
        <sequenceFlow id="judgeMore" name="大于500元" sourceRef="judgeTask" targetRef="bossTask">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money > 500}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="bossPassFlow" name="通过" sourceRef="bossTask" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="directorPassFlow" name="通过" sourceRef="directorTak" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="judgeLess" name="小于500元" sourceRef="judgeTask" targetRef="directorTak">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money <= 500}]]></conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_Expense">
        <bpmndi:BPMNPlane bpmnElement="Expense" id="BPMNPlane_Expense">
            <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
                <omgdc:Bounds height="30.0" width="30.0" x="285.0" y="135.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="fillTask" id="BPMNShape_fillTask">
                <omgdc:Bounds height="80.0" width="100.0" x="405.0" y="110.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
                <omgdc:Bounds height="40.0" width="40.0" x="585.0" y="130.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="directorTak" id="BPMNShape_directorTak">
                <omgdc:Bounds height="80.0" width="100.0" x="735.0" y="110.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="bossTask" id="BPMNShape_bossTask">
                <omgdc:Bounds height="80.0" width="100.0" x="555.0" y="255.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
                <omgdc:Bounds height="28.0" width="28.0" x="771.0" y="281.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
                <omgdi:waypoint x="315.0" y="150.0"></omgdi:waypoint>
                <omgdi:waypoint x="405.0" y="150.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
                <omgdi:waypoint x="505.0" y="150.16611295681062"></omgdi:waypoint>
                <omgdi:waypoint x="585.4333333333333" y="150.43333333333334"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess">
                <omgdi:waypoint x="624.5530726256983" y="150.44692737430168"></omgdi:waypoint>
                <omgdi:waypoint x="735.0" y="150.1392757660167"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="directorNotPassFlow" id="BPMNEdge_directorNotPassFlow">
                <omgdi:waypoint x="785.0" y="110.0"></omgdi:waypoint>
                <omgdi:waypoint x="785.0" y="37.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="37.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="110.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="bossPassFlow" id="BPMNEdge_bossPassFlow">
                <omgdi:waypoint x="655.0" y="295.0"></omgdi:waypoint>
                <omgdi:waypoint x="771.0" y="295.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore">
                <omgdi:waypoint x="605.4340277777778" y="169.56597222222223"></omgdi:waypoint>
                <omgdi:waypoint x="605.1384083044983" y="255.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="directorPassFlow" id="BPMNEdge_directorPassFlow">
                <omgdi:waypoint x="785.0" y="190.0"></omgdi:waypoint>
                <omgdi:waypoint x="785.0" y="281.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="bossNotPassFlow" id="BPMNEdge_bossNotPassFlow">
                <omgdi:waypoint x="555.0" y="295.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="295.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="190.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>
6.代理类准备

ManagerTaskHandler

package cn.gzsendi.flowable.listeners;

import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
 
public class ManagerTaskHandler implements TaskListener {
	private static final long serialVersionUID = -326245510542194886L;
	@Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("经理");
    }
}

BossTaskHandler

package cn.gzsendi.flowable.listeners;

import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;

public class BossTaskHandler implements TaskListener {
	private static final long serialVersionUID = 1L;

	@Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("老板");
    }
 
}
6.启动springboot工程让springboot帮我们部署好流程

到这直接启动springboot工程,如果不报错就会将processes下的我们刚准备的流程部署至数据库中,也可以通过检查数据库的表中是否存在部署记录,比如

7.接下来写controller类进行测试

这里我还是直接引用文章开头的引用文章里面的内容,复制出来定义成我的。
写一个TestController类

package cn.gzsendi.flowable.controller;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
	
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine processEngine;

    /**
     * 通过接收用户的一个请求传入用户的ID和金额以及描述信息来开启一个报销流程,并返回给用户这个流程的Id
     * 添加报销
     * @param userId    用户Id
     * @param money    销金额
     * @param descption 描述
     */
    @GetMapping("/add")
    public String addExpense(String userId, Integer money, String descption) {
        //启动流程
        HashMap<String, Object> map = new HashMap<>();
        map.put("taskUser", userId);
        map.put("money", money);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("Expense", map);
        return "提交成功.流程Id为:" + processInstance.getId();
    }
    
    /**
     * 查询流程列表,待办列表,通过代码获取出用户需要处理的流程
     * 获取审批管理列表
     */
    @GetMapping("/list")
    public Object list(String userId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
        for (Task task : tasks) {
            System.out.println(task.toString());
        }
        return "task size: " +tasks.size() + " , 第一个:" + tasks.get(0).toString() ;
    }
    
    /**
     * 批准,通过前端传入的任务ID来对此流程进行同意处理
     * @param taskId 任务ID
     */
    @GetMapping("/apply")
    public String apply(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("outcome", "通过");
        taskService.complete(taskId, map);
        return "processed ok!";
    }
    
    /**
     * 拒绝
     */
    @GetMapping("/reject")
    public String reject(String taskId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("outcome", "驳回");
        taskService.complete(taskId, map);
        return "reject";
    }
    
    /**
     * 生成当前流程图表
     * 生成流程图
     * @param processId 任务ID
     */
    @GetMapping("/processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
 
        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();
 
        //得到正在执行的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);
        }
        
        //图片输出要加上这个不会显示二进制数据,有些浏览器正常,有些浏览器是直接显示二进制数据,因此修改
        httpServletResponse.setContentType("image/png".concat(";charset=UTF-8"));
 
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0,true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    } 
	
}
8.接下来可以进行演示了
1.创建一个流程:
访问:http://localhost:8080/test/add?userId=testuser&money=1234
返回:提交成功.流程Id为:cd8b3025-c6c9-11ec-b88b-02004c4f4f50

2.查询待办列表:
访问:http://localhost:8080/test/list?userId=testuser
输出:task size: 1 , 第一个:Task[id=cd93949c-c6c9-11ec-b88b-02004c4f4f50, name=出差报销]

3.同意:
访问:http://localhost:8080/test/apply?taskId=cd93949c-c6c9-11ec-b88b-02004c4f4f50
返回:processed ok!

4.生成流程图:
访问:http://localhost:8080/test/processDiagram?processId=cd8b3025-c6c9-11ec-b88b-02004c4f4f50

发现流程乱码

进一步处理,在工程中加上类FlowableConfig,内容如下:

package cn.gzsendi.config;

import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;

/**
 * @author haiyangp
 * date:  2018/4/7
 * desc: flowable配置----为放置生成的流程图中中文乱码
 */
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
    @Override
    public void configure(SpringProcessEngineConfiguration engineConfiguration) {
        engineConfiguration.setActivityFontName("宋体");
        engineConfiguration.setLabelFontName("宋体");
        engineConfiguration.setAnnotationFontName("宋体");
    }
}

重新启动工程测试,至此,图片也正常了,如图

到这里基本的flowable初探过程结束

三、Spring Boot 整合 Flowable-ui-modeler 6.7.2

初探完成,接着就需要整合Flowable-ui-modeler这个在线设计流程器了,这个也同样的借鉴了另一个大神的文章,CSDN博主「wangdaoyin2010」的Spring Boot 整合 Flowable-ui-modeler 6.7.2,顺利的完成了整合modeler过程,其间也有一些自己的调整。

1.下载前端文件
  1. 下载flowable-engine-main源码文件
    https://github.com/flowable/flowable-engine/releases/tag/flowable-6.7.2
    注:下源码不是编绎后的
    wget https://github.com/flowable/flowable-engine/archive/refs/tags/flowable-6.7.2.tar.gz
  2. 找到前端文件
    解压下载后的文件,找到“flowable-engine-main\modules\flowable-ui\flowable-ui-modeler-frontend\src\main\resources\static”文件夹。将整个”static”文件夹拷贝到自己项目下“resources”目录中
  3. 启动测试下
    http://localhost:8080/modeler/index.html
2.添加Flowable-ui-modeler依赖

在前面工程的依赖基础之上继续添加依赖

		<!-- 添加flowable-ui-modeler核心依赖项 -->
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-ui-modeler-rest</artifactId>
			<version>${flowable.version}</version>
		</dependency>
		<!--添加flowable-ui-modeler配置依赖项 -->
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-ui-modeler-conf</artifactId>
			<version>${flowable.version}</version>
		</dependency>
flowable-ui-modeler-rest: 包含flowableUI对模型的增删查改等所有模型维护接口
flowable-ui-modeler-conf: 包含了一些ui-modeler的自动化配置项
3.先启动测试一波

报错,Caused by: java.lang.IllegalArgumentException: flowable.common.app.idm-url must be set

4.配置idm

继续摘录大神文章中的内容,没有进行Flowable默认使用idm授权,需要对idm授权信息进行配置,添加对应idm配置,继续在application.yml中加入配置如下,然后重启工程。

flowable:
#关闭定时任务JOB
  async-executor-activate: false
  database-schema-update: true
  common:
    app:
#     目前先设置一个正确但是不一定可用的url地址
      idm-url: http://localhost:8080/flowable-idm1
      idm-admin:
#       需要设置一个密码,目前先设置,后期不使用
        password: test
#       默认user为admin,这个地方可设置可不设置
        user: admin1
5.绕过Flowable授权

再次启动成功,然后访问http://localhost:8080/modeler
会跳转到http://localhost:8080/flowable-idm1/idm/#/login?redirectOnAuthSuccess=true&redirectUrl=http://localhost:8080/modeler/
该地址中http://localhost:8080/flowable-idm1就是前面配置idm认证授权地址。说明要访问页面需要进行授权,这里我们不进行授权,直接配置绕过Flowable中的授权。

CSDN博主「wangdaoyin2010」大神的Spring Boot 整合 Flowable-ui-modeler 6.7.2里面写得比较细,我这里就直接使用过程不纠原理了,继续。

  1. 创建一个授权配置类SecurityConfiguration,可以放在任何包下。
package cn.gzsendi.config;

import org.flowable.ui.common.security.SecurityConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfiguration {
    @Configuration(proxyBeanMethods = false)
    //Order配置说明
    // 这个地方相同会报错
    //这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用
    //所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权
    @Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    //必须要将csrf设置为disable,不然后面发送POST请求时会报403错误
                    .csrf().disable()
                    //为了简单起见,简单粗暴方式直接放行modeler下面所有请求
                    .authorizeRequests().antMatchers("/modeler/**").permitAll();
        }
    }
}
6.配置账号信息
  1. 重新启动后通过http://localhost:8080/modeler/看效果如下,基本的东西出来了,就是差菜单。

    按F12看接口情况,也发现有一个获取账号的接口报500错误

    该请求为获取当前用户信息接口

  2. 重写RemoteAccountResource这个类的代码,细节还是可以看大神的文章,这里继续 *** 作过程。
    创建一个类覆盖原来的RemoteAccountResource类(该类所在的包名、类名必须与原类相同弄才能覆盖),在该类中重新实现getAccount方法,直接返回一个用户信息既可。
    注意:因为返回的用户信息,前端需要判断角色权限,所以最好该方法中会添加一些角色权限信息返回。直接使用如下代码既可

package cn.gzsendi.flowable.controller;

import java.util.ArrayList;
import java.util.List;

import org.flowable.ui.common.model.UserRepresentation;
import org.flowable.ui.common.security.DefaultPrivileges;
import org.flowable.ui.common.service.exception.NotFoundException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping({
       "/app",
       "/"
})
public class RemoteAccountResource {
   /**
    * GET /rest/account -> get the current user.
    */
   @RequestMapping(value = "/rest/account", method = RequestMethod.GET, produces = "application/json")
   public UserRepresentation getAccount() {
       UserRepresentation userRepresentation = new UserRepresentation();
       userRepresentation.setFirstName("admin");
       userRepresentation.setLastName("admin");
       userRepresentation.setFullName("admin");
       userRepresentation.setId("admin");
       List<String> pris = new ArrayList<>();
       pris.add(DefaultPrivileges.ACCESS_MODELER);
       pris.add(DefaultPrivileges.ACCESS_IDM);
       pris.add(DefaultPrivileges.ACCESS_ADMIN);
       pris.add(DefaultPrivileges.ACCESS_TASK);
       pris.add(DefaultPrivileges.ACCESS_REST_API);
       userRepresentation.setPrivileges(pris);

       if (userRepresentation != null) {
           return userRepresentation;
       } else {
           throw new NotFoundException();
       }
   }
}
  1. 重启测试还是会报错的,路径不对,找到url-config.js配置文件中的请求地址进行修改
    共两个url-config.js,找到路径为src\main\resources\static\modeler\scripts\configuration\url-config.js的这个进行改即可。
    修改前:
    getAccountUrl: function () {
        return FLOWABLE.CONFIG.contextModelerRestRoot + '/rest/account';
    },

修改后

    getAccountUrl: function () {
        return FLOWABLE.CONFIG.contextRoot +  '/app/rest/account';
    },

刷新看效果如图:

7. Flowable Modeler匿名账号的处理

现在,基本上能进行在线流程设计了,我简单设计一下如下:


保存后显示匿名用户创建的:
网上找了一波,要改org.flowable.ui.common.security.SecurityUtils的类,进行重写覆盖到工程里面(包名路径等不能修改

package org.flowable.ui.common.security;
 
import java.util.ArrayList;
import java.util.List;

import org.flowable.common.engine.api.FlowableIllegalStateException;
import org.flowable.idm.api.User;
import org.flowable.ui.common.model.RemoteUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
 
/**
 * 重构流程编辑器获取用户信息
 */
public class SecurityUtils {
 
  private static User assumeUser;
  
  private static SecurityScopeProvider securityScopeProvider = new FlowableSecurityScopeProvider();
 
  private SecurityUtils() {
  }
 
  /**
   * Get the login of the current user.
   */
  public static String getCurrentUserId() {
    User user = getCurrentUserObject();
    if (user != null) {
      return user.getId();
    }
    return null;
  }
 
  /**
   * @return the {@link User} object associated with the current logged in user.
   */
  public static User getCurrentUserObject() {
    if (assumeUser != null) {
      return assumeUser;
    }
 
    RemoteUser user = new RemoteUser();
    user.setId("admin");
    user.setDisplayName("admin");
    user.setFirstName("admin");
    user.setLastName("admin");
    user.setEmail("[email protected]");
    user.setPassword("123456");
    List<String> pris = new ArrayList<>();
    pris.add(DefaultPrivileges.ACCESS_MODELER);
    pris.add(DefaultPrivileges.ACCESS_IDM);
    pris.add(DefaultPrivileges.ACCESS_ADMIN);
    pris.add(DefaultPrivileges.ACCESS_TASK);
    pris.add(DefaultPrivileges.ACCESS_REST_API);
    user.setPrivileges(pris);
    return user;
  }
  
    public static void setSecurityScopeProvider(SecurityScopeProvider securityScopeProvider) {
        SecurityUtils.securityScopeProvider = securityScopeProvider;
    }
 
    public static SecurityScope getCurrentSecurityScope() {
        SecurityContext securityContext = SecurityContextHolder.getContext();
        if (securityContext != null && securityContext.getAuthentication() != null) {
            return getSecurityScope(securityContext.getAuthentication());
        }
        return null;
    }
 
    public static SecurityScope getSecurityScope(Authentication authentication) {
        return securityScopeProvider.getSecurityScope(authentication);
    }
 
    public static SecurityScope getAuthenticatedSecurityScope() {
        SecurityScope currentSecurityScope = getCurrentSecurityScope();
        if (currentSecurityScope != null) {
            return currentSecurityScope;
        }
        throw new FlowableIllegalStateException("User is not authenticated");
    }
 
  public static void assumeUser(User user) {
    assumeUser = user;
  }
 
  public static void clearAssumeUser() {
    assumeUser = null;
  }
}

如图:

8.重启测试

重启测试后,重新加一个流程,匿名账号的问题也解决了,一切正常。

9.在线流程进行发布

设计了在线流程后,通过flowable moderler怎么在线发布流程,新的flowable-ui,可通过应用程序进行发布

  1. 新加一个应用程序

    然后进行创建好的应用程序进行编辑


    应用程序进行编辑过程,可以选中想发布的流程,然后在点发布即可。

    发布后数据库就会有记录

    至此,整合Modeler完成。
四、通用审批流程的应用实践

前面的flowable整合完后,已经基本上能按业务需求进行使用了,但每个流程要设计一次,还是觉得有点麻烦,对于我们一些简单的业务,就只设计配置一个通用的流程,系统中的流程都转化成这个通用的流程来进行审批流转

1.通用审批流程设计

通过flowable modeler进行设计一个通用流程如下,系统中的流程会转化成这个流程来进行审批流转

附上对应的通用审批流程.bpmn20.xml(只是参考用,因为可以通过在线Modeler进行修改):

<?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="CommonExamineAndApproveFlow" name="通用审批流程" isExecutable="true">
    <startEvent id="startCEAAF" name="开始" flowable:formFieldValidation="false">
      <extensionElements>
        <flowable:executionListener event="start" class="cn.gzsendi.flowable.listener.CommonFlowExecListener"></flowable:executionListener>
      </extensionElements>
    </startEvent>
    <userTask id="examAndApprove" name="审批" flowable:assignee="${assignee}" flowable:formFieldValidation="true">
      <extensionElements>
        <flowable:taskListener event="create" class="cn.gzsendi.flowable.listener.CommonFlowTaskListener"></flowable:taskListener>
        <flowable:taskListener event="complete" class="cn.gzsendi.flowable.listener.CommonFlowTaskListener"></flowable:taskListener>
        <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <userTask id="modification" name="修改" flowable:assignee="${applicant}" flowable:formFieldValidation="true">
      <extensionElements>
        <flowable:taskListener event="create" class="cn.gzsendi.flowable.listener.CommonFlowTaskListener"></flowable:taskListener>
        <flowable:taskListener event="complete" class="cn.gzsendi.flowable.listener.CommonFlowTaskListener"></flowable:taskListener>
        <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <exclusiveGateway id="sid-801B443D-1EE6-4B3D-A574-6939856E31BD"></exclusiveGateway>
    <exclusiveGateway id="sid-E4B59163-B3E3-47D2-94C2-D079D90F2673"></exclusiveGateway>
    <exclusiveGateway id="checkApproveResult"></exclusiveGateway>
    <endEvent id="endCEAAF" name="结束">
      <extensionElements>
        <flowable:executionListener event="end" class="cn.gzsendi.flowable.listener.CommonFlowExecListener"></flowable:executionListener>
      </extensionElements>
    </endEvent>
    <endEvent id="cancelCEAAF" name="取消">
      <extensionElements>
        <flowable:executionListener event="end" class="cn.gzsendi.flowable.listener.CommonFlowExecListener"></flowable:executionListener>
      </extensionElements>
    </endEvent>
    <sequenceFlow id="sid-C21B362D-DC54-4507-B9C8-511450686938" name="提交申请" sourceRef="startCEAAF" targetRef="examAndApprove"></sequenceFlow>
    <sequenceFlow id="sid-94CE2A65-C720-4B6B-BCE4-363269AAB6A0" name="下一个人继续批" sourceRef="checkApproveResult" targetRef="examAndApprove">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result>0}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-54D272DB-EBF8-4944-914B-69D77C3E89D1" name="通过" sourceRef="checkApproveResult" targetRef="endCEAAF">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result==0}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-39DAAB85-AF7E-4D7A-A4F3-66226719D9AC" name="不通过" sourceRef="checkApproveResult" targetRef="sid-E4B59163-B3E3-47D2-94C2-D079D90F2673">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result<0}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-931CBA3F-5300-4C16-98BB-73AF1558B1F5" name="打回修改" sourceRef="sid-E4B59163-B3E3-47D2-94C2-D079D90F2673" targetRef="modification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result!=-999}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-A82C78B3-A7A3-434F-ABC4-1E624D6A0F25" sourceRef="modification" targetRef="sid-801B443D-1EE6-4B3D-A574-6939856E31BD"></sequenceFlow>
    <sequenceFlow id="sid-550CC53D-C3B4-44E8-9456-74167659FAC0" name="取消审批" sourceRef="sid-E4B59163-B3E3-47D2-94C2-D079D90F2673" targetRef="cancelCEAAF">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result==-999}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-1DB6E9FA-8EBD-493A-A4A8-396C1CD626AD" name="从初审开始重批" sourceRef="sid-801B443D-1EE6-4B3D-A574-6939856E31BD" targetRef="examAndApprove">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result!=-999}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-3A6B227E-0B82-4E4D-854B-39C9E081828B" name="N次循环(多个审批人/处理人、多次修改重批)" sourceRef="examAndApprove" targetRef="checkApproveResult"></sequenceFlow>
    <sequenceFlow id="sid-069CD520-EA9F-4FD9-B20B-C1A6A335861A" name="取消审批" sourceRef="sid-801B443D-1EE6-4B3D-A574-6939856E31BD" targetRef="cancelCEAAF">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result==-999}]]></conditionExpression>
    </sequenceFlow>
    <textAnnotation id="sid-9CA67D24-B4CE-4E6E-9597-E253BF9BEA43">
      <text>只要审批没有通过,不管当前流程进行到哪个任务节点,申请人都可以取消审批(撤单)</text>
    </textAnnotation>
    <association id="sid-58C0FC70-A1CF-433D-BA68-737C39E47007" sourceRef="sid-E4B59163-B3E3-47D2-94C2-D079D90F2673" targetRef="sid-9CA67D24-B4CE-4E6E-9597-E253BF9BEA43" associationDirection="None"></association>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_CommonExamineAndApproveFlow">
    <bpmndi:BPMNPlane bpmnElement="CommonExamineAndApproveFlow" id="BPMNPlane_CommonExamineAndApproveFlow">
      <bpmndi:BPMNShape bpmnElement="startCEAAF" id="BPMNShape_startCEAAF">
        <omgdc:Bounds height="30.0" width="29.999999999999986" x="119.00000000000001" y="180.00000000000026"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="examAndApprove" id="BPMNShape_examAndApprove">
        <omgdc:Bounds height="77.0" width="110.99999999999997" x="240.00000000000003" y="156.50000000000023"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="modification" id="BPMNShape_modification">
        <omgdc:Bounds height="80.0" width="106.99999999999994" x="405.00000000000006" y="326.11111140545563"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-801B443D-1EE6-4B3D-A574-6939856E31BD" id="BPMNShape_sid-801B443D-1EE6-4B3D-A574-6939856E31BD">
        <omgdc:Bounds height="40.0" width="40.0" x="275.50000000000006" y="346.11111140545563"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-E4B59163-B3E3-47D2-94C2-D079D90F2673" id="BPMNShape_sid-E4B59163-B3E3-47D2-94C2-D079D90F2673">
        <omgdc:Bounds height="40.0" width="40.0" x="645.0000000000002" y="346.11111140545563"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="checkApproveResult" id="BPMNShape_checkApproveResult">
        <omgdc:Bounds height="40.0" width="40.0" x="645.0000000000001" y="175.00000000000037"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endCEAAF" id="BPMNShape_endCEAAF">
        <omgdc:Bounds height="28.0" width="28.0" x="840.0000000000002" y="181.00000000000043"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="cancelCEAAF" id="BPMNShape_cancelCEAAF">
        <omgdc:Bounds height="28.0" width="27.999999999999986" x="120.00000000000001" y="352.1111114054557"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-9CA67D24-B4CE-4E6E-9597-E253BF9BEA43" id="BPMNShape_sid-9CA67D24-B4CE-4E6E-9597-E253BF9BEA43">
        <omgdc:Bounds height="62.0" width="142.0" x="780.0000000000001" y="335.1111114054557"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-931CBA3F-5300-4C16-98BB-73AF1558B1F5" id="BPMNEdge_sid-931CBA3F-5300-4C16-98BB-73AF1558B1F5" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="53.49999999999997" flowable:targetDockerY="40.0">
        <omgdi:waypoint x="645.0000000000002" y="366.11111140545563"></omgdi:waypoint>
        <omgdi:waypoint x="511.95000000000005" y="366.11111140545563"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-550CC53D-C3B4-44E8-9456-74167659FAC0" id="BPMNEdge_sid-550CC53D-C3B4-44E8-9456-74167659FAC0" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="13.999999999999993" flowable:targetDockerY="14.0">
        <omgdi:waypoint x="665.0000000000002" y="386.0502677044908"></omgdi:waypoint>
        <omgdi:waypoint x="665.0000000000002" y="458.0"></omgdi:waypoint>
        <omgdi:waypoint x="339.0" y="458.0"></omgdi:waypoint>
        <omgdi:waypoint x="146.72314379197059" y="371.8184580073399"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-C21B362D-DC54-4507-B9C8-511450686938" id="BPMNEdge_sid-C21B362D-DC54-4507-B9C8-511450686938" flowable:sourceDockerX="14.999999999999991" flowable:sourceDockerY="15.0" flowable:targetDockerX="55.499999999999986" flowable:targetDockerY="38.5">
        <omgdi:waypoint x="148.94999929925845" y="195.00000000000026"></omgdi:waypoint>
        <omgdi:waypoint x="240.00000000000003" y="195.00000000000023"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-54D272DB-EBF8-4944-914B-69D77C3E89D1" id="BPMNEdge_sid-54D272DB-EBF8-4944-914B-69D77C3E89D1" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
        <omgdi:waypoint x="684.9447250132206" y="195.0000000000004"></omgdi:waypoint>
        <omgdi:waypoint x="840.0000000000002" y="195.00000000000043"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-069CD520-EA9F-4FD9-B20B-C1A6A335861A" id="BPMNEdge_sid-069CD520-EA9F-4FD9-B20B-C1A6A335861A" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="13.999999999999993" flowable:targetDockerY="14.0">
        <omgdi:waypoint x="275.5000000000001" y="366.1111114054557"></omgdi:waypoint>
        <omgdi:waypoint x="147.9499267462589" y="366.1111114054557"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-3A6B227E-0B82-4E4D-854B-39C9E081828B" id="BPMNEdge_sid-3A6B227E-0B82-4E4D-854B-39C9E081828B" flowable:sourceDockerX="55.499999999999986" flowable:sourceDockerY="38.5" flowable:targetDockerX="20.0" flowable:targetDockerY="20.0">
        <omgdi:waypoint x="350.9499999999042" y="195.00000000000026"></omgdi:waypoint>
        <omgdi:waypoint x="645.0000000000001" y="195.00000000000037"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-58C0FC70-A1CF-433D-BA68-737C39E47007" id="BPMNEdge_sid-58C0FC70-A1CF-433D-BA68-737C39E47007" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="0.0" flowable:targetDockerY="31.0">
        <omgdi:waypoint x="684.5247370727237" y="366.52777807212226"></omgdi:waypoint>
        <omgdi:waypoint x="780.0000000000001" y="366.1111114054557"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-1DB6E9FA-8EBD-493A-A4A8-396C1CD626AD" id="BPMNEdge_sid-1DB6E9FA-8EBD-493A-A4A8-396C1CD626AD" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="55.499999999999986" flowable:targetDockerY="38.5">
        <omgdi:waypoint x="295.50000000000006" y="346.11111140545563"></omgdi:waypoint>
        <omgdi:waypoint x="295.5" y="233.45000000000024"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-39DAAB85-AF7E-4D7A-A4F3-66226719D9AC" id="BPMNEdge_sid-39DAAB85-AF7E-4D7A-A4F3-66226719D9AC" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="20.0" flowable:targetDockerY="20.0">
        <omgdi:waypoint x="665.0000000000001" y="214.94417385944323"></omgdi:waypoint>
        <omgdi:waypoint x="665.0000000000002" y="346.11111140545563"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-A82C78B3-A7A3-434F-ABC4-1E624D6A0F25" id="BPMNEdge_sid-A82C78B3-A7A3-434F-ABC4-1E624D6A0F25" flowable:sourceDockerX="53.49999999999997" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.0" flowable:targetDockerY="20.0">
        <omgdi:waypoint x="404.9999999998836" y="366.11111140545563"></omgdi:waypoint>
        <omgdi:waypoint x="315.40609269471895" y="366.11111140545563"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-94CE2A65-C720-4B6B-BCE4-363269AAB6A0" id="BPMNEdge_sid-94CE2A65-C720-4B6B-BCE4-363269AAB6A0" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="55.499999999999986" flowable:targetDockerY="38.5">
        <omgdi:waypoint x="665.0000000000001" y="175.00000000000037"></omgdi:waypoint>
        <omgdi:waypoint x="665.0000000000001" y="67.0"></omgdi:waypoint>
        <omgdi:waypoint x="295.5" y="67.0"></omgdi:waypoint>
        <omgdi:waypoint x="295.5" y="156.50000000000023"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
2.业务扩展数据库设计
  1. 增加work_flow_def表,用来存我们自己的流程名称
  2. 增加work_flow_form_field表,用来存我们定义的表单字段(不使用flowable的)
  3. 增加work_order表,用于存放发起流程关联的工单数据(一个流程实例对应一条work_order数据记录)
  4. 增加wo_assignee表,用来存放审批人设置的信息。
wo_assignee(工单处理人表,即流程模板和流程实例数据表)**
用来将业务流程转化为"通用审批流程",
其中字段**assignee(审批人)**可能是**具体的用户ID**,也可能是**变量**
当一个流程中的某一个节点的assignee(审批人)**变量时**,
那就需要在**创建流程实例前赋值**成具体的用户ID,
创建流程实例前会生成**工单ID(work_order表的order_id)**,
我们就用**(order_id和具体的用户ID)**来再存入wo_assignee表,
得到新的工单处理人数据,流程流转时就用这些数据,因此,这些数据就是流程实例数据




3.流转说明图

4. 测试说明

因代码有点多,因此这里只是展示下效果,详见可以下载文章最后提供的源码,使用springboot工程,只需要将工程下的dbscripts\flowabletest.sql导入数据库,然后启动工程开箱即用,就可以进行测试

/**
 * 通用的流程设计查看(不要删除):http://localhost:8080/flowabletest/modeler/#/processes/c1ad50ed-c7e2-11ec-8b32-02004c4f4f50
 * @author jxlhl
 * 测试URL:先从普通人员页面开始测试即可,即:http://localhost:8080/flowabletest/main.html?userId=lix
 * 普通人员:http://localhost:8080/flowtest/main.html?userId=lix
 * 项目经理:http://localhost:8080/flowtest/main.html?userId=zhaoyl
 * 部门经理:http://localhost:8080/flowtest/main.html?userId=linjh
 * 总经理:http://localhost:8080/flowtest/main.html?userId=xuw
 */

首页

流程表单设计和审批人设置

开启流程

审批

五、flowable和mybatis同时存在时问题解决

同时存在时不能正常启动,报存在2个session如类下以下错误

Description:

file [xxxx\com\carshow\flowable\mapper\IFlowableCommentMapper.class] required a single bean, but 2 were found:
	- sqlSessionFactory: defined by method 'sqlSessionFactory' in class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]
	- modelerSqlSessionFactory: defined by method 'modelerSqlSessionFactory' in class path resource [org/flowable/ui/modeler/conf/ModelerDatabaseConfiguration.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

有博主的文章解决方法如https://blog.csdn.net/forever_xw_/article/details/122377609,我没有试过,觉得挺麻烦的,我这边直接采用手工配置多数据源,然后在写mybatis的mapper时指定使用的数据库来解决的,比如:

为数据库指定一个mybatis相关的config,如DefaultDbConfig,指定此库相关的表的mapper使用的注解为UsingDefalutDB,然后在mapper的编写时也同时指定注解。

package cn.gzsendi.config.mybatis;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 指定Mapper使用defaultDB库
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UsingDefaultDB {
}

写mapper时指定注解,使用哪个数据库

详细可以看我的源码里面的代码

六、源码下载及说明

最后提供源码如下及说明:

总的代码地址

github: https://github.com/jxlhljh/flowabletest
gitee: https://gitee.com/jxlhljh/flowabletest.git

springboot整合Flowable6.7.2

https://github.com/jxlhljh/flowabletest/tree/master/v1/flowabletest
https://gitee.com/jxlhljh/flowabletest/tree/master/v1/flowabletest

Spring Boot 整合 Flowable-ui-modeler 6.7.2

https://github.com/jxlhljh/flowabletest/tree/master/v2/flowabletest
https://gitee.com/jxlhljh/flowabletest/tree/master/v2/flowabletest

整合完成后的通用审批流程的应用实践的代码

https://github.com/jxlhljh/flowabletest/tree/master/v3/flowabletest
https://gitee.com/jxlhljh/flowabletest/tree/master/v3/flowabletest

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/796362.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存