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

news2024/11/25 20:18:40

在实际项目开发过程中,流程相关的业务实现采用工作流会异常清晰明了,但是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);
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

今日分享丨浅谈RAG的技术特点及优势

一、引言 随着AI技术的发展&#xff0c;我们见证了人工智能在理解和处理自然语言方面取得的巨大进步。特别是结合了检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;和大语言模型&#xff08;Large Language Model, LLM&#xff09;的系统&#xff0…

华企网安技术博弈:白帽子团队如何破解网赌网站

在数字化时代&#xff0c;網賭作为一种新型犯罪形式&#xff0c;其隐蔽性和跨国性给执法机关带来了前所未有的挑战。一批专业的网络安全团队——白帽子&#xff0c;正利用他们的专业技能与犯罪分子进行技术博弈&#xff0c;有效地破解網賭网站&#xff0c;为打击網賭犯罪贡献力…

银河麒麟V10安装docker和docker-compose

1. 说明 系统镜像使用的是Kylin-Server-V10-SP3-2403-Release-20240426-x86_64.iso如果是在VMware中安装这个系统&#xff0c;需选择Ubuntu&#xff0c;如果选Centos会有问题。 尝试使用在线方式安装docker&#xff0c;报了很多错误&#xff0c;比较麻烦&#xff0c;建议使用离…

Bilibili开源发布轻量级 Index 系列语言模型:2.8T 训练数据,支持角色扮演

Bilibili首次发布 Index 系列模型中的轻量版本&#xff1a;Index-1.9B 系列 本次开源的 Index-1.9B系列包含以下模型&#xff1a; Index-1.9Bbase:基座模型&#xff0c;具有 19 亿 非词嵌入参数量&#xff0c;在 2.8T 中英文为主的语料上预训练&#xff0c;多个评测基准上与…

Marin说PCB之电源完整性PDN--Murata三端子电容--01

不知道诸位老铁们有没有忘记我们常用的电容的一些特性&#xff0c;例如在其谐振频率左边&#xff0c;电容主要呈容性&#xff0c;在谐振频率右边&#xff0c;电容主要呈感性。我随便找了一个电容&#xff1a;GRT188C80J226ME13&#xff0c;其频率特性图如下所示&#xff1a; 我…

Qt creator day5练习

Qt 中实现TCP 聊天服务器 大致流程 创建套接字服务器QTcpServer对象 通过QTcpServer对象设置监听&#xff0c;即QTcpServer&#xff1a;&#xff1a;listen&#xff08;&#xff09; 基于QTcpServer&#xff1a;&#xff1a;newConnection&#xff08;&#xff09;信号检测…

大模型面试题目精选分享

前言 掌握大模型面试关键&#xff0c;我精选了一系列面试题目。从模型理解到应用实践&#xff0c;全方位考察大模型知识。无论你是初学者还是资深专家&#xff0c;这里都有适合你的题目。快来挑战自己&#xff0c;提升面试技能吧&#xff01; 面试题笔记分享 为了助力朋友们…

日常工作中常用的管理工具

日常工作中常用的管理工具 SWOT分析法&#xff1a; 帮你清晰地把我全局&#xff0c;分析自己在资源方面的优势域劣势&#xff0c;把握环境提供的机会&#xff0c;防范可能存在的风险与威胁&#xff0c;对我们的成功有非常重要的意义 PDCA循环规则&#xff1a; 每一项工作&#…

RPM 打包入门(基于openEuler)

主要参考内容&#xff08;均为官方文档&#xff09;&#xff1a; https://rpm-packaging-guide.github.io/#building-rpms https://docs.redhat.com/zh_hans/documentation/red_hat_enterprise_linux/8/html/packaging_and_distributing_software/introduction-to-rpm_packagin…

用Python设置Excel工作表网格线的隐藏与显示

Excel表格界面的直观性很大程度上得益于表格中的网格线设计&#xff0c;这些线条帮助用户精确对齐数据&#xff0c;清晰划分单元格。网格线是Excel界面中默认显示的辅助线&#xff0c;用于辅助定位&#xff0c;与单元格边框不痛&#xff0c;不影响打印输出。然而&#xff0c;在…

Spring的启动扩展点机制详解

在Java的世界中&#xff0c;我们知道Spring是当下最主流的开发框架&#xff0c;没有之一。而在使用Dubbo、Mybatis等开源框架时&#xff0c;我们发现可以采用和Spring完全一样的使用方式来使用它们。 可能你在平时的使用过程中并没有意识到这一点&#xff0c;但仔细想一想&…

34 张动图:线缆,以太网与网络寻址

以太网 本地主机访服务器 以太网标准 非屏蔽双绞线&#xff08;UTP&#xff09; 电磁干扰 串扰 双绞线 真实的双绞线 线缆与引脚对齐方式 科普&#xff1a;网线每根芯的作用 引脚连接 数据传输与接收 主机直连到交换机 主机直连到主机&#xff08;或交换器&#xff09; Auto M…

C++ 矩阵的最小路径和解法

描述 给定一个 n * m 的矩阵 a&#xff0c;从左上角开始每次只能向右或者向下走&#xff0c;最后到达右下角的位置&#xff0c;路径上所有的数字累加起来就是路径和&#xff0c;输出所有的路径中最小的路径和。 数据范围: 1≤&#x1d45b;,&#x1d45a;≤5001≤n,m≤500&…

# 消息中间件 RocketMQ 高级功能和源码分析(九)

消息中间件 RocketMQ 高级功能和源码分析&#xff08;九&#xff09; 一、消息中间件 RocketMQ 源码分析&#xff1a; 同步刷盘分析 1、刷盘机制 RocketMQ 的存储是基于 JDK NIO 的内存映射机制&#xff08;MappedByteBuffer&#xff09;的&#xff0c;消息存储首先将消息追…

好用的便签是什么 电脑桌面上好用的便签

作为一名文字工作者&#xff0c;我经常需要在繁杂的思绪中捕捉灵感&#xff0c;记录下那些一闪而过的想法。在寻找一款适合电脑桌面的便签应用时&#xff0c;我偶然发现了敬业签便签软件简直是为我量身定制的&#xff0c;它不仅界面简洁&#xff0c;操作便捷&#xff0c;更重要…

前瞻展望,中国信通院即将发布“2024云计算十大关键词”

人类对于未知领域的探索欲望&#xff0c;似乎总是无穷无尽&#xff0c;而探索欲反过来推动了技术的革新与进步。今年以来&#xff0c;AI大模型成为科技领域最为确定的趋势之一。在大模型开启的AI原生时代&#xff0c;AI原生正在重构云计算的演化逻辑和发展走向&#xff0c;MaaS…

含铬废水有哪些危害?含铬废水怎么处理呢?

铬化物可以通过消化道、呼吸道、皮肤和粘膜侵人人体&#xff0c;主要积聚在肝、肾、内分泌系统和肺部。毒理作用是影响体内物质氧化、还原和水解过程&#xff0c;与核酸、核蛋白结合影响组织中的磷含量。铬化合物具有致癌作用。 铬化合物以蒸汽和粉尘的方式进入人体组织中&…

gstreamer+qt5实现简易视频播放器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装环境1.QT52.gstreamer 二、代码1.Windows实现 三、测试效果总结 前言 最近在研究mpp&#xff0c;通过gstreamer实现了硬解码&#xff0c;但是我在想我…

ip地址怎么写才是的对的?合法ip地址正确的格式

IP地址怎么写才是的对的&#xff1f;在互联网的世界里&#xff0c;IP地址就像是我们生活中的门牌号码&#xff0c;它是每个设备在网络中的唯一标识。正确的书写IP地址对于确保网络通信的顺畅至关重要。本文将带您了解合法IP地址的正确格式与书写规范&#xff0c;并深入探讨其在…

【MySQL】索引的原理及其使用

文章目录 什么叫索引减少磁盘IO次数缓存池(Buffer Pool&#xff09;MySQL的页页内目录页目录 正确理解索引结构为什么Innodb的索引是B树结构各种存储引擎支持的索引聚簇索引和非聚簇索引索引类型 关于索引的操作创建主键索引唯一索引的创建普通索引的创建查看索引删除索引 什么…