Springboot整合Flowable

工作流引擎目前开源上可以选的就只有activitiflowablecamunda三种,当然除了activiti它们都有商用版本,flowable在6之后搞商用版了。所以暂时选用了比较稳定的开源版6.5.0。最近也是利用空闲时间研究了一下flowable的具体使用流程,总体来说还是比较简单的。

参考文档:

官网:https://tkjohn.github.io/flowable-userguide/#_introduction

Flowable介绍

Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable可以十分灵活地加入你的应用/服务/构架。可以将JAR形式发布的Flowable库加入应用或服务,来嵌入引擎。 以JAR形式发布使Flowable可以轻易加入任何Java环境:Java SE;Tomcat、Jetty或Spring之类的servlet容器;JBoss或WebSphere之类的Java EE服务器,等等。 另外,也可以使用Flowable REST API进行HTTP调用。也有许多Flowable应用(Flowable Modeler, Flowable Admin, Flowable IDM 与 Flowable Task),提供了直接可用的UI示例,可以使用流程与任务。

项目集成

在pom文件引入:


  org.flowable
  flowable-spring-boot-starter
  6.5.0

配置文件加入:

#flowable流程引擎测试
flowable:
  async-executor-activate: false
  database-schema-update: true #开启数据库结构自动同步,启动项目后会自动往数据库添加表大概七八十张

 这个方式是独立部署,逻辑是一个流程设计器->设计流程->导出流程文件xxx.bpmn2.0.xml文件->工程项目加载流程文件->调用流程相关接口(开始流程、提交、结束流程、查询流程流转、历史查询等等)->实现自己的业务逻辑。然后通过接口实现流程导入、授权、部署和更新等操作实现新流程加入。 最后要基于数据库使用配置mysql数据库连接池这些就不做说明了。

流程设计器

为了得到我们的流程部署文件(xxx.bpmn2.0.xml),我们可以安装流程设计工具或者使用一些第三方插件来做这个事情,不过我们可以利用官方提供的docker镜像来直接运行Flowable UI来实现流程设计。镜像地址:flowable/all-in-one:6.5.0,至于持久化映射就自行查阅吧。启动之访问http://localhost:28080/flowable-modeler即可,默认的用户名是:admin,密码:test,进去了之后就是编辑页面了编辑完成之后导出为xml文件就可以放在项目里面使用了,算是比较简单。

docker run -p 28080:8080 flowable/all-in-one:6.5.0

Flowable ui提供了几个web应用,用于演示及介绍Flowable项目提供的功能:

  • Flowable IDM: 身份管理应用。为所有Flowable UI应用提供单点登录认证功能,并且为拥有IDM管理员权限的用户提供了管理用户、组与权限的功能。

  • Flowable Modeler: 让具有建模权限的用户可以创建流程模型、表单、选择表与应用定义。

  • Flowable Task: 运行时任务应用。提供了启动流程实例、编辑任务表单、完成任务,以及查询流程实例与任务的功能。

  • Flowable Admin: 管理应用。让具有管理员权限的用户可以查询BPMN、DMN、Form及Content引擎,并提供了许多选项用于修改流程实例、任务、作业等。管理应用通过REST API连接至引擎,并与Flowable Task应用及Flowable REST应用一同部署。

Springboot整合Flowable_第1张图片

 demo.bpmn2.0.xml:



  
    这个流程是一个测试流程
    
    
      人员提出请假申请
    
    
    
    
      由部门经理审核请假申请
    
    
      由项目经理审核请假申请
    
    
    
    
    
    
    
    
      
    
    
      
    
    
      = 3}]]>
    
    
      
    
    
    
      
    
    
      
    
  
  
    
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
        
        
      
      
        
        
      
      
        
        
        
      
      
        
        
      
      
        
        
      
      
        
        
      
      
        
        
        
        
      
      
        
        
      
      
        
        
      
      
        
        
      
    
  

具体使用:

我这里简单创建了一个服务和写了一些前端文件来测试:

后端代码:

package com.example.springboottest.FlowableTest.service;

import com.example.springboottest.FlowableTest.Entity.ProcessInstanceObject;
import com.example.springboottest.FlowableTest.Entity.TaskObject;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

/**
 * @Author: liangpeng
 * @name: FlowableService  流程引擎服务
 * @Date: 2023/5/30 11:49
 */
public interface FlowableService {
    /**
     * 启动流程
     * @param processKey  流程定义key(流程图ID)
     * @param businessKey 业务key(可以为空)
     * @param map         参数键值对
     * @return 流程实例ID
     */
    ProcessInstance start(String processKey, String businessKey, Map map);

    /**
     * 停止流程
     * @param processInstanceId 流程实例ID
     * @param reason            终止理由
     */
    void stop(String processInstanceId, String reason);
    /**
     * 完成指定任务(推动流程运行)
     * @param taskId 任务ID
     * @param map    变量键值对
     * @throws RuntimeException 任务不存在
     */
    void complete(String taskId, Map map);

    /**
     * 获取指定用户的待办任务列表(创建时间倒序)
     * @param userId 用户ID
     * @return 任务列表
     */
    List getTasksByUserId(String userId);
    /**
     * 获取指定用户组的待办任务列表
     * @param group 用户组
     * @return 任务列表
     */
    List getTasksByGroup(String group);

    /**
     * 获取指定实例的任务列表
     * @param processInstanceId 流程实例id
     * @return 任务列表
     */
    List getTasksByProcessInstanceId(String processInstanceId);

    /**
     * 获取指定任务列表中的特定任务
     * @param list        任务列表
     * @param businessKey 业务key
     * @return 任务
     */
    Task getOneByBusinessKey(List list, String businessKey);
    /**
     * 创建流程并完成第一个任务
     * @param processKey  流程定义key(流程图ID)
     * @param businessKey 业务key
     * @param map         变量键值对
     */
    Map startAndComplete(String processKey, String businessKey, Map map);

    /**
     * 退回到指定任务节点
     * @param currentTaskId 当前任务ID
     * @param targetTaskKey 目标任务节点key
     * @throws Exception 当前任务节点不存在
     */
    void backToStep(String currentTaskId, String targetTaskKey) throws Exception;

    /**
     * 返回尚未结束的流程实例
     * @param processKey 流程的名称(传入空返回所有)
     * @return 流程列表
     */
    List getProcessListByKey(String processKey);
    /**
     * 返回所有的流程实例
     *
     * @param processKey 流程的名称(传入空返回所有)
     * @return 流程列表
     */
    List getHistoryProcessList(String processKey);

    /**
     * 获取尚未结束的流程图
     * @param outputStream 流程图的输出流(传入空返回所有)
     * @param processId 流程的ID
     * @throws IOException 流无法读取或写入
     */
    Boolean getProcessChart(OutputStream outputStream, String processId) throws IOException;
}
package com.example.springboottest.FlowableTest.service.impl;
import com.example.springboottest.FlowableTest.Entity.ProcessInstanceObject;
import com.example.springboottest.FlowableTest.Entity.TaskObject;
import com.example.springboottest.FlowableTest.service.FlowableService;
import liquibase.pro.packaged.B;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: liangpeng
 * @name: FlowableServiceImpl
 * @Date: 2023/5/30 11:49
 * @description: 流程引擎实现
 */
@Service
public class FlowableServiceImpl implements FlowableService {
    private static final Logger logger = LoggerFactory.getLogger("flowable-service-log");
    @Resource
    RuntimeService runtimeService;
    @Resource
    TaskService taskService;
    @Resource
    HistoryService historyService;
    @Resource
    ProcessEngine processEngine;
    @Resource
    RepositoryService repositoryService;


    @Override
    public ProcessInstance start(String processKey, String businessKey, Map map) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, businessKey, map);
        return processInstance;
    }
    @Override
    public void stop(String processInstanceId, String reason) {
        runtimeService.deleteProcessInstance(processInstanceId, reason);
    }
    @Override
    public void complete(String taskId, Map map) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            logger.error(taskId + ":指定的任务不存在");
            throw new RuntimeException("任务不存在");
        }
        taskService.complete(taskId, map);
    }

    @Override
    public List getTasksByUserId(String userId) {
        List tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
        return taskToTaskObject(tasks);
    }
    @Override
    public List getTasksByGroup(String group) {
        List tasks = taskService.createTaskQuery().taskCandidateGroup(group).orderByTaskCreateTime().desc().list();
        return taskToTaskObject(tasks);
    }
    @Override
    public List getTasksByProcessInstanceId(String processInstanceId) {
        List tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).orderByTaskCreateTime().desc().list();
        return taskToTaskObject(tasks);
    }


    private List taskToTaskObject(List tasks) {
        List taskObjects = new ArrayList<>();
        for (Task task : tasks) {
            taskObjects.add(TaskObject.fromTask(task));
        }
        return taskObjects;
    }

    @Override
    public Task getOneByBusinessKey(List list, String businessKey) {
        Task task = null;
        for (Task t : list) {
            // 通过任务对象获取流程实例
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(t.getProcessInstanceId()).singleResult();
            if (businessKey.equals(pi.getBusinessKey())) {
                task = t;
            }
        }
        return task;
    }

    @Override
    public Map startAndComplete(String processKey, String businessKey, Map map) {
        Map out=new HashMap<>();
        ProcessInstance processInstance = start(processKey, businessKey, map);//启动流程,返回流程id
        if(ObjectUtils.isEmpty(processInstance)) return out;
        out.put("processInstance",ProcessInstanceObject.fromProcessInstance(processInstance));
        Task task = processEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        taskService.complete(task.getId(), map);
        out.put("task",TaskObject.fromTask(task));
        return out;
    }

    @Override
    public void backToStep(String currentTaskId, String targetTaskKey) throws Exception {
        Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
        if (currentTask == null) {
            logger.error(currentTaskId + ":指定的任务不存在");
            throw new Exception("当前任务节点不存在");
        }
        List currentTaskKeys = new ArrayList<>();
        currentTaskKeys.add(currentTask.getTaskDefinitionKey());
        runtimeService.createChangeActivityStateBuilder().processInstanceId(currentTask.getProcessInstanceId()).moveActivityIdsToSingleActivityId(currentTaskKeys, targetTaskKey);
    }
    @Override
    public List getProcessListByKey(String processKey) {
        if (processKey == null) {
            return processToProcessObject(runtimeService.createProcessInstanceQuery().orderByStartTime().desc().list());
        }
        return processToProcessObject(runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).orderByStartTime().desc().list());
    }
    @Override
    public List getHistoryProcessList(String processKey) {
        if (processKey == null) {
            return historyProcessToProcessObject(historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().list());
        }
        return historyProcessToProcessObject(historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().processDefinitionKey(processKey).list());
    }
    private List processToProcessObject(List instances) {
        List instanceObjects = new ArrayList<>();
        for (ProcessInstance instance : instances) {
            instanceObjects.add(ProcessInstanceObject.fromProcessInstance(instance));
        }
        return instanceObjects;
    }
    private List historyProcessToProcessObject(List instances) {
        List instanceObjects = new ArrayList<>();
        for (HistoricProcessInstance instance : instances) {
            instanceObjects.add(ProcessInstanceObject.fromHistoryProcessInstance(instance));
        }
        return instanceObjects;
    }

    @Override
    public Boolean getProcessChart(OutputStream out, String processId) throws IOException {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        //流程走完的不显示图
        if (pi == null) {
            return false;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();
        //得到正在执行的Activity的Id
        List activityIds = new ArrayList<>();
        List flows = new ArrayList<>();
        for (Execution exe : executions) {
            List ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png",
                activityIds, flows, "宋体", "宋体", "宋体", null, 1.0, true);
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
        return true;
    }

}

package com.example.springboottest.FlowableTest.Controller;

import com.example.springboottest.FlowableTest.Entity.ProcessInstanceObject;
import com.example.springboottest.FlowableTest.service.FlowableService;
import com.example.springboottest.common.vo.result.R;
import liquibase.util.StringUtil;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping(value = "${application.admin-path}/flowable")
public class FlowableController {
    final private String PROCESS_KEY = "demo";
    @Resource
    FlowableService flowableService;


    //TODO:动态部署一个流程(xml文件)


    @GetMapping("/demo/stopProcessById")
    //@ApiOperation("停止流程实例")
    public R stopProcessById(String processInstanceId) {
        if(StringUtil.isEmpty(processInstanceId))
            throw new RuntimeException("参数不全请指定流程实例id(processInstanceId)字段");
        flowableService.stop(processInstanceId, "测试手动停止!");
        return R.buildOk("停止流程实例["+processInstanceId+"]成功!");
    }

    @GetMapping("/demo/startAndComplete")
    //@ApiOperation("开始请假流程,并提交")
    public R startAndComplete(Integer days,String processKey) {
        if(ObjectUtils.isEmpty(days)||StringUtil.isEmpty(processKey))
            throw new RuntimeException("参数不全请指定请假天数(days)和流程key(processKey)字段");
        Map map = new HashMap<>();
        map.put("days", days);
        Map objectMap = flowableService.startAndComplete(processKey, "请假", map);
        return R.buildOkData(objectMap);
    }

    @GetMapping("/demo/reComplete")
    //@ApiOperation("重新提交申请流程")
    public R reComplete(Integer days,String taskId){
        if(ObjectUtils.isEmpty(days)||StringUtil.isEmpty(taskId))
            throw new RuntimeException("参数不全请指定请假天数(days)和任务id(taskId)字段");
        Map map = new HashMap<>();
        map.put("project_pass", false);
        flowableService.complete(taskId, map);
        return R.buildOk("重新提交申请流程成功,项目经理审核!");
    }



    @GetMapping("/demo/project/pass")
    //@ApiOperation("项目经理审核通过")
    public R projectPass(String taskId) throws Exception {
        Map map = new HashMap<>();
        map.put("project_pass", true);
        flowableService.complete(taskId, map);
        return R.buildOk("项目经理审核通过成功,自动判定请假天数决定是否部门经理审核!");
    }

    @GetMapping("/demo/project/not_pass")
    //@ApiOperation("项目经理审核驳回")
    public R projectNotPass(String taskId)  {
        Map map = new HashMap<>();
        map.put("project_pass", false);
        flowableService.complete(taskId, map);
        return R.buildOk("项目经理审核驳回成功,请修改后重新提交申请!");
    }

    @GetMapping("/demo/dept/pass")
    //@ApiOperation("部门经理审核通过")
    public R deptPass(String taskId)  {
        Map map = new HashMap<>();
        map.put("dept_pass", true);
        flowableService.complete(taskId, map);
        return R.buildOk("部门经理审核通过,流程结束!");
    }

    @GetMapping("/demo/dept/not_pass")
    //@ApiOperation("部门经理审核驳回")
    public R deptNotPass(String taskId)  {
        Map map = new HashMap<>();
        map.put("dept_pass", false);
        flowableService.complete(taskId, map);
        return R.buildOk("部门经理审核驳回成功,请修改后重新提交申请!");
    }

    @GetMapping("/demo/process/list")
    //@ApiOperation("通过流程key获取未完成的流程实例列表")
    public R getProcessList(String processKey)  {
        if(StringUtil.isEmpty(processKey))
            throw new RuntimeException("参数不全请指定流程key(processKey)字段");
        return R.buildOkData(flowableService.getProcessListByKey(processKey));
    }
    @GetMapping("/demo/getTasksByGroup")
    //@ApiOperation("获取任务列表-指定用户组,倒叙")
    public R getTaskList(String groupId) {
        return R.buildOkData(flowableService.getTasksByGroup(groupId));
    }
    @GetMapping("/demo/getTasksByProcessInstanceId")
    //@ApiOperation("获取任务列表-指定流程实例id,倒叙")
    public R getTasksByProcessInstanceId(String processInstanceId) {
        return R.buildOkData(flowableService.getTasksByProcessInstanceId(processInstanceId));
    }

    @GetMapping("/demo/process/history/list")
   //@ApiOperation("获取已经完成的流程列表")
    public Object getHistoryProcessList() throws Exception {
        return flowableService.getHistoryProcessList(PROCESS_KEY);
    }

    @RequestMapping("/demo/process/chart/{processId}")
   // @ApiOperation("获取某个流程实例的进度状态")
    public void getProcessChart(HttpServletResponse httpServletResponse, @PathVariable("processId") String processId) throws IOException {
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        httpServletResponse.setContentType("image/png");
        Boolean processChart = flowableService.getProcessChart(outputStream, processId);
        //return R.buildOkData(processChart);
    }
}
package com.example.springboottest.FlowableTest.Entity;

import lombok.Data;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.BeanUtils;

import java.util.Date;
import java.util.Map;

/**
 *
 * 流程实体表--转换
 */

@Data
public class ProcessInstanceObject {

	String processDefinitionId;

	String processDefinitionName;

	String processDefinitionKey;

	Integer processDefinitionVersion;

	String deploymentId;

	String businessKey;

	boolean isSuspended;

	Map processVariables;

	String tenantId;

	String name;

	String description;

	String localizedName;

	String localizedDescription;

	Date startTime;

	Date endTime;

	Long durationInMillis;

	String endActivityId;

	String startUserId;

	String callbackId;

	String callbackType;

	String id;

	boolean suspended;

	boolean ended;

	String activityId;

	String processInstanceId;

	String parentId;

	String superExecutionId;

	String rootProcessInstanceId;

	String referenceId;

	String referenceType;

	String propagatedStageInstanceId;

	public static ProcessInstanceObject fromProcessInstance(ProcessInstance instance){
		ProcessInstanceObject object = new ProcessInstanceObject();
		BeanUtils.copyProperties(instance,object);
		return object;
	}

	public static ProcessInstanceObject fromHistoryProcessInstance(HistoricProcessInstance instance){
		ProcessInstanceObject object = new ProcessInstanceObject();
		BeanUtils.copyProperties(instance,object);
		return object;
	}
}

package com.example.springboottest.FlowableTest.Entity;

import lombok.Data;
import org.flowable.task.api.Task;
import org.springframework.beans.BeanUtils;

import java.util.Date;
import java.util.Map;

/**
 * 
 * 任务实体表--转换
 */
@Data
public class TaskObject {
	String id;

	String name;

	String description;

	int priority;

	String owner;

	String assignee;

	String processInstanceId;

	String executionId;

	String taskDefinitionId;

	String processDefinitionId;

	String scopeId;

	String subScopeId;

	String scopeType;

	String scopeDefinitionId;

	String propagatedStageInstanceId;

	Date createTime;

	String taskDefinitionKey;

	Date dueDate;

	String category;

	String parentTaskId;

	String tenantId;

	String formKey;

	Map taskLocalVariables;

	Map processVariables;

	Date claimTime;

	public static TaskObject fromTask(Task task){
		TaskObject object = new TaskObject();
		BeanUtils.copyProperties(task,object);
		return object;
	}
}
package com.example.springboottest.common.vo.result;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.http.HttpStatus;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;


/**
 * 响应信息主体
 *
 * @param 
 * @author somewhere
 */
@ToString
@Accessors(chain = true)
@AllArgsConstructor
public class R implements Serializable {
    private static final long serialVersionUID = 1L;

    @Getter
    @Setter
    private int code = CommonConstants.SUCCESS;

    @Getter
    @Setter
    private HttpStatus httpStatus;


    @Getter
    @Setter
    private T data;


    private String[] messages = {};


    public R() {
        super();
    }

    public R(T data) {
        super();
        this.data = data;
    }

    public R(String... msg) {
        super();
        this.messages = msg;
    }

    public R(T data, String... msg) {
        super();
        this.data = data;
        this.messages = msg;
    }

    public R(T data, int code, String... msg) {
        super();
        this.data = data;
        this.code = code;
        this.messages = msg;
    }

    public R(Throwable e) {
        super();
        setMessage(e.getMessage());
        this.code = CommonConstants.FAIL;
    }

    public static R buildOk(String... messages) {
        return new R(messages);
    }

    public static  R buildOkData(T data, String... messages) {
        return new R(data, messages);
    }

    public static  R buildFailData(T data, String... messages) {
        return new R(data, CommonConstants.FAIL, messages);
    }

    public static  R buildFail(String... messages) {
        return new R(null, CommonConstants.FAIL, messages);
    }

    public static  R build(T data, int code, String... messages) {
        return new R(data, code, messages);
    }

    public static  R build(int code, String... messages) {
        return new R(null, code, messages);
    }

    public String getMessage() {
        return readMessages();
    }

    public void setMessage(String message) {
        addMessage(message);
    }

    public String readMessages() {
        StringBuilder sb = new StringBuilder();
        for (String message : messages) {
            sb.append(message);
        }
        return sb.toString();
    }

    public void addMessage(String message) {
        this.messages = ObjectUtils.addObjectToArray(messages, message);
    }

}

前端页面:




	
	flowable测试
	
	
	


flowable测试

info: 图片显示

-----------------------------------------基本信息区域-----------------------------------------
流程key:
当前流程实例id:
当前任务id:

-----------------------------------------操作区域-----------------------------------------

请假人操作:
请输入请假天数:

项目经理操作:

部门经理操作:

Springboot整合Flowable_第2张图片

TODO:

目前只是建的测试了一下,具体的还有很多想做的没做完的事:

  • 流程发布接口和更新接口。(目前只需要放在resources/processes里面,只要不冲突和重复就不会有问题,项目启动时会自动部署)
  • 在框架项目albedo里面集成流程设计器,实现用户可以自己编辑和定义流程。参考:Spring Boot 整合 Flowable-ui-modeler 6.7.2_springboot 整合flowableui_繁华哟的博客-CSDN博客
  • 若依流程引擎框架探索:开源项目Ruoyi-Flowable-plus逆向学习视频教程配套文档_zhaozhiqiang1981的博客-CSDN博客

你可能感兴趣的:(spring,boot,java,后端,flowable)