springboot特殊问题处理2——springboot集成flowable实现工作流程的完整教程(一)

在实际项目开发过程中,流程相关的业务实现采用工作流会异常清晰明了,但是Activity学习成本和开发难度对追求效率的开发工作者来说异常繁琐,但是作为Activity的亲儿子之一的flowable,其轻量化的使用和对应的api会让开发者感受简单,学习成本很低,值得推荐。

本文案基于springboot2.3.12为例讲解,jdk版本要求至少1.8+,mysql为8.0以上。

一.flowable相关官方网址

官方网站(英文):https://www.flowable.com/

第三方中文用户手册(V6.3.0):https://tkjohn.github.io/flowable-userguide/

二.如何集成springboot

1.引入官方jar或者对应springboot的starter
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>${flowable.version}</version>
</dependency>

我这边根据项目需要只引入相关的flowable-engine

        <dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-engine</artifactId>
			<version>6.3.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.mybatis</groupId>
					<artifactId>mybatis</artifactId>
				</exclusion>
				<exclusion>
					<groupId>mysql</groupId>
					<artifactId>mysql-connector-java</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
2. 配置项目需要的数据
  • flowable.properties
flowable.url=jdbc:mysql://10.1.0.223:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT&nullCatalogMeansCurrent=true
flowable.username=root
flowable.password=123456
flowable.driverClassName=com.mysql.cj.jdbc.Driver
###生成数据表
flowable.initialize=true
flowable.name=flowable
###动态生成流程执行图(定义中文字体为宋体,防止生成的图片资源存在乱码)
flowable.activityFontName=\u5B8B\u4F53
flowable.labelFontName=\u5B8B\u4F53
flowable.annotationFontName=\u5B8B\u4F53
flowable.xml.encoding=UTF-8
  • 项目结构如下 

  • 测试需要的流程图 

 

三.flowable项目正确开发使用流程

1.首先正确配置flowable.properties该文件,默认在启动项目时会生成34张工作流数据表(均已ACT_开头)

2.利用tomcat启动flowable-admin.war,然后用flowable-ui创建对应的bpm文件(或者其他的bpm工具)

3.调用/deployment这个接口,部署已经写好的流程实例,参数参照后台方法说明传递即可

4.分别查看act_re_deployment,act_re_procdef和act_ge_bytearray数据表,如果生成了相关数据即代表部署成功

5.最后就可以在相关模块创建任务开始动态执行流程

四.flowable流程业务实现以及部分关键代码展示

以下关键代码,需要特意说明的是:

  • ProcessEngine是flowable提供对公开BPM和工作流操作的所有服务的访问关键对象。
  • FlowProcessDiagramGenerator是flowable生成流程实例图片的关键,继承自
    org.flowable.image.impl.DefaultProcessDiagramGenerator类
1.流程部署
    /**
     * 1.部署流程
     *
     * @return
     */
    @GetMapping("/deployment")
    public String deploymentFlowable() {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("flowable_xml/test_flowable.bpmn20.xml")
                //类别
                .category("审批类")
                .name("领导审批")
                .deploy();
        return ResponseResult.ok(deployment);
    }
2. 查询流程定义
    /**
     * 2.查询流程定义
     *
     * @return
     */
    @GetMapping("/queryDeployment")
    public String queryFlowableDeploy() {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //查询所有定义的流程
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
        //查询单个定义的流程
        /*ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId("5")
                .singleResult();*/
//        System.out.println("Found process definition : " + processDefinition.getName());
        return ResponseResult.ok(list);
    }
3.启动流程实例
    /**
     * 3.启动流程实例
     *
     * @return
     */
    @RequestMapping("/start/instance")
    public String startProcessInstance() {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //要启动流程实例,需要提供一些初始化流程变量,自定义
        Map<String, Object> variables = new HashMap<String, Object>(0);
        variables.put("employee", "工作组");
        variables.put("nrOfHolidays", 8);
        variables.put("description", "请假");
        ProcessInstance processInstance =
                runtimeService.startProcessInstanceByKey("leader_approval_key", variables);
        return ResponseResult.ok(processInstance.getName());
    }
4.通过流程执行人员查询任务和流程变量
    /**
     * 通过流程人员定义查询任务和流程变量
     *
     * @return
     */
    @RequestMapping("/query/task")
    public String queryProcessInstance() {
        TaskService taskService = processEngine.getTaskService();
        //通过组查询任务表
//        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
        //通过人查询单个任务
        Task task = taskService.createTaskQuery().taskAssignee("小王").singleResult();
        //通过任务id查询流程变量
        Map<String, Object> processVariables = taskService.getVariables(task.getId());
        return ResponseResult.ok(processVariables);
    }
5.通过任务id完成任务
    /**
     * 通过任务id完成任务
     *
     * @return
     */
    @RequestMapping("/complete/task")
    public String completeTask(String taskId) {
        TaskService taskService = processEngine.getTaskService();
        //领导审批提交的表达信息
        Map<String, Object> variables = new HashMap<String, Object>(0);
        taskService.complete(taskId, variables);
        return ResponseResult.ok();
    }
 6.通过流程执行人或者审批人查询审批历史记录
    /**
     * 通过审批人获取历史任务数据
     *
     * @param name
     * @return
     */
    @RequestMapping("/history/task")
    public String getHistoryTask(@RequestParam("name") String name) {
        HistoryService historyService = processEngine.getHistoryService();
        //历史任务流程——流程id
        List<HistoricActivityInstance> activities =
                historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId("2501")
                        .finished()
                        .orderByHistoricActivityInstanceEndTime().asc()
                        .list();
        //历史任务
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(name).list();
        return ResponseResult.ok(list.toString());
    }
 7.通过流程id查询流程执行图(多种获取方式)
/**
     * 通过流程id获取流程资源
     *
     * @return
     */
    @RequestMapping("/process/resource")
    public void getProcessResource(HttpServletResponse response) throws IOException {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        /*ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId("5085")
                .processDefinitionId("defect_report_flowable:1:5088")
//                .processDefinitionKey("leader_approval_key")
//                .deploymentId("5")
                .singleResult();*/
        BpmnModel bpmnModel = repositoryService.getBpmnModel("defect_report_flowable:1:4");
        InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel);
        /*String diagramResourceName = processDefinition.getDiagramResourceName();
        InputStream imageStream = repositoryService.getResourceAsStream(
                processDefinition.getDeploymentId(), diagramResourceName);*/
        FileOutputStream fos = new FileOutputStream("D:\\data\\22222.png");
        byte[] b = new byte[1024];
        int leng = -1;
        while ((leng = imageStream.read(b)) != -1) {
            fos.write(b, 0, leng);
        }
        fos.flush();
        imageStream.close();
        fos.close();
        /*
        //文件流直接写出
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OutputStream os = response.getOutputStream();
        int ch = 0;
        while (-1 != (ch = imageStream.read())) {
            baos.write(ch);
        }
        os.write(baos.toByteArray());
        imageStream.close();
        baos.close();
        os.close();*/
    }

五.其他相关的功能和问题持续更新,有问题私信 

1.生成流程实例图片的关键代码
@Service
public class FlowProcessDiagramGenerator extends DefaultProcessDiagramGenerator {

    private static final String IMAGE_TYPE = "png";

    @Value("${flowable.activityFontName}")
    private String activityFontName;
    @Value("${flowable.labelFontName}")
    private String labelFontName;
    @Value("${flowable.annotationFontName}")
    private String annotationFontName;
    @Value("${flowable.xml.encoding}")
    private String encoding;

    @Autowired
    private ProcessEngine processEngine;

    /**
     * 生成执行动态图片流
     *
     * @param processDefinitionId 流程定义的id——xml文件规固定的key
     * @param businessKey
     * @return
     */
    public InputStream generateActiveDiagram(String processDefinitionId, String businessKey) {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        HistoryService historyService = processEngine.getHistoryService();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //1.获取当前的流程定义
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processDefinitionId(processDefinitionId)
//                .processInstanceId(processInstanceId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        //流程实例执行的实例id
        String processId = null;
        List<String> activeActivityIds = new ArrayList<>();
        List<String> highLightedFlows = new ArrayList<>();
        //3. 获取流程定义id和高亮的节点id
        if (processInstance != null) {
            //3.1. 正在运行的流程实例
            processId = processInstance.getProcessInstanceId();
            //2.获取所有的历史轨迹线对象
            List<HistoricActivityInstance> historicSquenceFlows = historyService.createHistoricActivityInstanceQuery()
//                .processDefinitionId(processInstanceId)
                    .processInstanceId(processId)
                    .activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)
                    .list();
            historicSquenceFlows.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
            activeActivityIds = runtimeService.getActiveActivityIds(processId);
        } else {
            //3.2. 已经结束的流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processDefinitionId(processDefinitionId)
//                    .processInstanceId(processId)
                    .processInstanceBusinessKey(businessKey)
                    .singleResult();
            if(historicProcessInstance == null){
                throw new MessageCodeException(MessageCode.FLOWABLE_PROCESS_IS_RELEASE_SUCCESS);
            }
            processId = historicProcessInstance.getId();
            //3.3. 获取结束节点列表
            List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processId)
                    .activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
            List<String> finalActiveActivityIds = activeActivityIds;
            historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
        }
        //4. 获取bpmnModel对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        //模型 活动节点 高亮线
        return generateDiagram(bpmnModel, IMAGE_TYPE, activeActivityIds,
                highLightedFlows, activityFontName, labelFontName, annotationFontName,
                null, 1.0);
    }

    /**
     * 生成工作流程图
     *
     * @param bpmnModel 模型
     * @return
     */
    public InputStream generateDiagram(BpmnModel bpmnModel) {
        return generateDiagram(bpmnModel, IMAGE_TYPE, activityFontName,
                labelFontName, annotationFontName,
                null, 1.0);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/731542.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

毕业答辩制作PPT【攻略】

毕业答辩制作PPT【攻略】 前言版权毕业答辩制作PPT【攻略】一、WPS AI 15天免费会员二、AI文档生成PPT三、修改完善PPT 最后 前言 2024-06-14 23:43:05 以下内容源自《【攻略】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN…

快捷方式(lnk)--加载HTA-CS上线

免责声明:本文仅做技术交流与学习... 目录 CS: HTA文档 文件托管 借助mshta.exe突破 本地生成lnk快捷方式: 非系统图标路径不同问题: 关于lnk的上线问题: CS: HTA文档 配置监听器 有效载荷---->HTA文档--->选择监听器--->选择powershell模式----> 默认生成一…

记某大学的一次EduSRC的挖掘

0x1 前言 漏洞由来简述 首先讲下这个漏洞的由来吧&#xff0c;这类漏洞叫做OSS储存桶漏洞&#xff0c;是阿里云OSS存储云安全的漏洞&#xff0c;也是一个相对来讲比较新鲜的安全漏洞。我是在进行对某大学的小程序进行文件上传测试的时候发现返回包的URL里面有“OSS”字段&…

Wilcom PE 威尔克姆绣花软件下载安装;Wilcom PE 广大绣花制版师必备软件!

Wilcom PE这款软件不仅具备强大的设计功能&#xff0c;更在用户体验上下足了功夫&#xff0c;使得刺绣图案的创作变得更为简单、高效。 在Wilcom PE的丰富工具箱中&#xff0c;用户可以发现各种精心设计的工具&#xff0c;它们如同刺绣师手中的魔法棒&#xff0c;将创意变为现…

深度解析RocketMq源码-持久化组件(一) MappedFile

1. 绪论 rocketmq之所以能够有如此大的吞吐量&#xff0c;离不开两个组件&#xff0c;一个是利用netty实现的高性能网络通信组件&#xff1b;另一个就是利用mmap技术实现的存储组件。而在rocketmq的存储组件中主要有三个组件&#xff0c;分别是持久化文件commitLog&#xff0c…

【尚庭公寓SpringBoot + Vue 项目实战】移动端浏览历史(二十二)

【尚庭公寓SpringBoot Vue 项目实战】移动端浏览历史&#xff08;二十二&#xff09; 文章目录 【尚庭公寓SpringBoot Vue 项目实战】移动端浏览历史&#xff08;二十二&#xff09;1、业务介绍2.接口开发2.1.分页查询浏览历史列表2.2.保存浏览历史 1、业务介绍 浏览历史指的…

基于STM32和人工智能的智能小车系统

目录 引言环境准备智能小车系统基础代码实现&#xff1a;实现智能小车系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景&#xff1a;智能小车管理与优化问题解决方案与优化收尾与总结 1. 引言 随着机器人技术的发展&#xff0c;智能小…

【Golang】Steam 创意工坊 Mod 文件夹批量重命名

本文将介绍一个使用Go语言编写的脚本&#xff0c;其主要功能是解析XML文件并基于解析结果重命名文件夹。这个脚本适用于需要对文件夹进行批量重命名&#xff0c;并且重命名规则依赖于XML文件内容的情况。 脚本功能概述 Steam创意工坊下载的Mod文件夹批量重命名为id名称 运行前…

【docker入门】

在软件开发过程中&#xff0c;环境配置是一个至关重要的步骤&#xff0c;它不仅影响开发效率&#xff0c;也直接关联到软件的最终质量。正确的环境配置可以极大地减少开发中的潜在问题&#xff0c;提升软件发布的流畅度和稳定性。以下是几个关键方面&#xff0c;以及如何优化环…

网上预约就医取号系统

摘 要 近年来&#xff0c;随着信息技术的发展和普及&#xff0c;我国医疗信息产业快速发展&#xff0c;各大医院陆续推出自己的信息系统来实现医疗服务的现代化转型。不可否认&#xff0c;对一些大型三级医院来说&#xff0c;其信息服务质量还是广泛被大众所认可的。这就更需要…

如何制定新版FMEA培训后的知识应用考核机制?

随着新版FMEA的推出&#xff0c;如何确保培训后的知识能够得到有效应用&#xff0c;并转化为实际工作中的能力&#xff0c;成为了企业关注的焦点。本文&#xff0c;深圳天行健企业管理咨询公司将分享如何制定一套科学、实用的新版FMEA培训后知识应用考核机制&#xff0c;以助力…

四、SpringMVC实战:构建高效表述层框架(二)

二、SpringMVC接收数据 2.1 访问路径设置 RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式&#xff08;handler方法&#xff09;关联起来&#xff0c;建立映射关系。 SpringMVC 接收到指定的请求&#xff0c;就会来找到在映射关系中对应的方法来处理这个请求…

第六十六天打卡 | 卡码网101 孤岛的总面积、卡码网102 沉没孤岛、卡码网103 水流问题、卡码网104 建造最大岛屿

卡码网101 孤岛的总面积 这一题在昨天的基础上&#xff0c;将比较得出最大孤岛面积的逻辑改为统计所有孤岛面积之和的逻辑即可。 最近做项目的时候也发现&#xff0c;很多时候代码逻辑能够复用最好就不要再自己写&#xff0c;防止出错&#xff0c;当然刷代码题的时候不…

环境配置04:Pytorch下载安装

说明&#xff1a; 显存大于4G的建议使用GPU版本的pytorch&#xff0c;低于4G建议使用CPU版本pytorch&#xff0c;直接使用命令安装对应版本即可 GPU版本的pytorch的使用需要显卡支持&#xff0c;需要先安装CUDA&#xff0c;即需要完成以下安装 1.查看已安装CUDA版本 GPU对应…

基于DPU的云原生裸金属网络解决方案

1. 方案背景和挑战 裸金属服务器是云上资源的重要部分&#xff0c;其网络需要与云上的虚拟机和容器互在同一个VPC下&#xff0c;并且能够像容器和虚拟机一样使用云的网络功能和能力。 传统的裸金属服务器使用开源的 OpenStack Ironic 组件&#xff0c;配合 OpenStack Neutron…

Uncaught TypeError: Cannot read properties of null (reading ‘isCE‘)

问题描述 使用 view-ui-plus 加 vue3 开发项目&#xff0c;本地启动项目正常&#xff0c;但其他人将代码拉下来&#xff0c;启动项目时报错 Uncaught TypeError: Cannot read properties of null (reading isCE)&#xff1a; 原因分析&#xff1a; 尝试将 mode_nodules 文件删…

量子计算:1 从薛定谔的猫开始

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则…

C++ 编程技巧分享

侯捷 C 学习路径&#xff1a;面向对象的高级编程 -> STL库 -> C11新特性 -> cmake 1.1. C 与 C的区别 在C语言中&#xff0c;主要存在两大类内容&#xff0c;数据和处理数据的函数&#xff0c;二者彼此分离&#xff0c;是多对多的关系。不同的函数可以调用同一个数据…

【Linux进程】手把手教你如何调整----进程优先级(什么是优先级?为什么要有优先级?)

目录 一、前言 二、优先级的基本概念 &#x1f95d; 什么是优先级&#xff1f; &#x1f34d; 为什么要有优先级&#xff1f; 三、如何查看并修改 --- 进程优先级 &#x1f347; PRI and NI &#x1f525;PRI&#x1f525; &#x1f525;NI&#x1f525; &#x1f3…

亿发开启极速开单新纪元,解锁业务新速度,提升企业竞争力

我们不断追求卓越&#xff0c;致力于通过技术革新&#xff0c;为客户带来更快捷、更智能、更全面的进销存管理体验。立即更新&#xff0c;享受更高效的业务处理流程。