星云erp整合activiti--阶段一

前言

鉴于交流学习时候很多同学吐槽 开源的星云erp很多功能不完整特别重要审批流程管理没有问题现在决定基于开源星云erp 最新版整合activiti

星云erp整合activiti 源码

配套星云erp移动版源码

开发目标

最新版 星云erp整合activiti ,完成 创建流程部署流程启动任务查询代办处理任务查询历史任务相关测试

准备工作

  1. 基于版本

目前 giteemaster分支下的 星云erp同学自行下载也可以下载已经整合版本

注意: 这里只是做技术交流分享,同学们自觉遵守原作的相关协议)

  1. 相关技术栈

Springboot 2.2.2.RELEASE

MyBatis-plus 3.4.2

Spring-session-data-redis 2.2.0.RELEASE

HuTool 5.7.17

Lombok 1.18.10

EasyExcel 2.2.10

JDK 1.8

Mysql 5.7.18

Redis 4.0.8

Activiti 7.1.0.M4 (用7.1.0.M6 会强制需要SpringSecurity认证)

以上技术栈要求版本进攻参考,大家可以根据自己开发环境调整)

  1. 项目架构分析

思维导图插件

(为了方便直奔主题,决定直接在 xingyun-api下整合)

整合Activiti

1引入Activiti相关依赖

后端工程 xingyun-api模块的 pom.xml文件添加依赖

    <!--activity工作流依赖-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.1.0.M4</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-image-generator</artifactId>
            <version>7.1.0.M4</version>
        </dependency>
        <!--结束activity工作流依赖-->

  1. 配置application.yml文件

spring标签添加activiti相关配置

spring:
  activiti:
    db-history-used: true  #进行相关activiti操作时,要生成history相关的表
    history-level: full  #history相关表中的历史记录都要被记录

至此没有问题可以启动 项目自动生成 activiti 25(为什么不是28张,请自行百度)张数据

运行结果如下

通过Navicat查看数据库新增以下相关证明 activiti整合成功

创建测试流程

1idea中安装 BPMN-Activiti-Diagram 插件

  • file---setting --plugins 搜索组件 BPMN-Activiti-Diagram

  • 安装完成

  1. 创建流程

阶段一、流程的设计直接使用插件来创建,后期会整合到PC版)

  • resources文件 创建审批流程xml

  • 这里已经创建了一个 借支流程测试 工作流程自行到gitee下下载)

<?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:activiti="http://activiti.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.activiti.org/processdef">
  <process id="jz" name="借支申请" isExecutable="true">
    <startEvent id="sid-2f65ca73-6ae5-411f-80d6-61b87da2868c"/>
    <userTask id="sid-bac0db30-99bf-4c15-b563-f8d90229359d" name="借支申请" activiti:assignee="${name}"/>
    <userTask id="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa" name="部门主管审批" activiti:assignee="张三"/>
    <userTask id="sid-7a04db13-b607-4b56-a008-82bf49e87b5b" name="财务审批" activiti:assignee="李四"/>
    <userTask id="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" name="老板审批" activiti:assignee="王五"/>
    <endEvent id="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112"/>
    <sequenceFlow id="sid-ceda75ac-fedb-4509-96c1-22b4b39d1bdf" sourceRef="sid-2f65ca73-6ae5-411f-80d6-61b87da2868c" targetRef="sid-bac0db30-99bf-4c15-b563-f8d90229359d"/>
    <sequenceFlow id="sid-315bb768-b704-4d6a-9b58-e22afecfe575" sourceRef="sid-bac0db30-99bf-4c15-b563-f8d90229359d" targetRef="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa"/>
    <exclusiveGateway id="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75"/>
    <sequenceFlow id="sid-ed8a6b21-c4b2-48f9-902a-b6724070c6f5" sourceRef="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa" targetRef="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75"/>
    <sequenceFlow id="sid-db35498f-3561-418c-a181-804aa14ecbc9" sourceRef="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75" targetRef="sid-7a04db13-b607-4b56-a008-82bf49e87b5b" name="同意">
      <conditionExpression xsi:type="tFormalExpression">${sp==1}</conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35"/>
    <sequenceFlow id="sid-40d5bb77-d68d-42bd-a8e2-e437566c1c76" sourceRef="sid-7a04db13-b607-4b56-a008-82bf49e87b5b" targetRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35"/>
    <sequenceFlow id="sid-2c8957f3-4ed2-45a6-bf5d-f4ef07c3ce5e" sourceRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35" targetRef="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" name="借支金额大于3000">
      <conditionExpression xsi:type="tFormalExpression">${sp1==1&amp;&amp;money&gt;3000}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-f42a08d5-2f62-4079-89d6-d9a031727976" sourceRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35" targetRef="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112" name="借支金额小于或等于3000">
      <conditionExpression xsi:type="tFormalExpression">${sp1==1&amp;&amp;money==3000}</conditionExpression>
    </sequenceFlow>
    <endEvent id="sid-de1dac9b-ac4f-44af-9431-e8c41724040d"/>
    <sequenceFlow id="sid-91a7bfa6-8e6c-4040-ba3c-3b4f0b2936f9" sourceRef="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75" targetRef="sid-de1dac9b-ac4f-44af-9431-e8c41724040d" name="驳回">
      <conditionExpression xsi:type="tFormalExpression">${sp==0}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-e52aacd6-dfd8-4dd0-8b2d-55005519fc18" sourceRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35" targetRef="sid-de1dac9b-ac4f-44af-9431-e8c41724040d" name="驳回">
      <conditionExpression xsi:type="tFormalExpression">${sp1==0}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-457e06a6-1bb9-48f1-93a6-8905c36daaa7" sourceRef="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" targetRef="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112">
      <conditionExpression>${sp2==1}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-d44d1aa5-63a2-4091-9854-dec3d31648e5" sourceRef="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" targetRef="sid-de1dac9b-ac4f-44af-9431-e8c41724040d" name="驳回">
      <conditionExpression>${sp2==0}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_借支申请">
    <bpmndi:BPMNPlane bpmnElement="jz" id="BPMNPlane_借支申请">
      <bpmndi:BPMNShape id="shape-e58741b4-6e31-4074-935f-374c6b28948d" bpmnElement="sid-2f65ca73-6ae5-411f-80d6-61b87da2868c">
        <omgdc:Bounds x="-185.0" y="-65.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-4e82b3f5-9b68-4e6d-9979-e1797046a8dc" bpmnElement="sid-bac0db30-99bf-4c15-b563-f8d90229359d">
        <omgdc:Bounds x="-105.0" y="-90.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-8738aadc-c9b0-4425-8aa6-72bbc112810f" bpmnElement="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa">
        <omgdc:Bounds x="40.0" y="-90.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-5c376243-ed67-4880-9409-7bf2a5b8dae7" bpmnElement="sid-7a04db13-b607-4b56-a008-82bf49e87b5b">
        <omgdc:Bounds x="280.0" y="-90.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-f4d77dfe-e759-4a2c-b999-4d38e883c3f3" bpmnElement="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b">
        <omgdc:Bounds x="589.99994" y="-200.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-95f6c6a9-cf28-410a-9a3b-793f64127a58" bpmnElement="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112">
        <omgdc:Bounds x="625.0" y="-65.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-76f787ac-1d25-41c9-bbe9-b764552a3efb" bpmnElement="sid-ceda75ac-fedb-4509-96c1-22b4b39d1bdf">
        <omgdi:waypoint x="-154.99998" y="-50.000004"/>
        <omgdi:waypoint x="-105.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-f1dfb186-d63f-4ab1-9919-0916d2a30398" bpmnElement="sid-315bb768-b704-4d6a-9b58-e22afecfe575">
        <omgdi:waypoint x="-5.0" y="-50.0"/>
        <omgdi:waypoint x="40.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-19cca33f-c048-4e9d-a1f0-97f8f8ec8ca4" bpmnElement="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75">
        <omgdc:Bounds x="175.0" y="-70.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-149b8ca9-3916-44d3-ad2f-474eef6c9f94" bpmnElement="sid-ed8a6b21-c4b2-48f9-902a-b6724070c6f5">
        <omgdi:waypoint x="140.0" y="-50.0"/>
        <omgdi:waypoint x="175.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-28443c4d-dc3b-4da7-8d62-5a54400f301f" bpmnElement="sid-db35498f-3561-418c-a181-804aa14ecbc9">
        <omgdi:waypoint x="215.0" y="-50.0"/>
        <omgdi:waypoint x="280.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-f7b93b8f-6ce5-4350-a070-08fe7e6fecd3" bpmnElement="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35">
        <omgdc:Bounds x="400.0" y="-70.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-60e6cdc2-cec6-4d15-b526-d89ccd6a93c7" bpmnElement="sid-40d5bb77-d68d-42bd-a8e2-e437566c1c76">
        <omgdi:waypoint x="380.0" y="-50.0"/>
        <omgdi:waypoint x="400.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-abb25af8-44c2-4235-b38a-e30bcc433ae4" bpmnElement="sid-2c8957f3-4ed2-45a6-bf5d-f4ef07c3ce5e">
        <omgdi:waypoint x="420.0" y="-70.0"/>
        <omgdi:waypoint x="589.99994" y="-160.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-754f9d94-a77b-4eef-a481-88a754263399" bpmnElement="sid-f42a08d5-2f62-4079-89d6-d9a031727976">
        <omgdi:waypoint x="440.0" y="-50.0"/>
        <omgdi:waypoint x="625.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-54161de0-db42-4b4e-81b0-ff591997ba36" bpmnElement="sid-de1dac9b-ac4f-44af-9431-e8c41724040d">
        <omgdc:Bounds x="180.0" y="-180.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-9a7697b0-b324-4edd-b1ac-632a35774be7" bpmnElement="sid-91a7bfa6-8e6c-4040-ba3c-3b4f0b2936f9">
        <omgdi:waypoint x="195.0" y="-70.0"/>
        <omgdi:waypoint x="195.0" y="-150.00002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-f51508e0-aa41-42c3-abb6-fe0c0fd190fc" bpmnElement="sid-e52aacd6-dfd8-4dd0-8b2d-55005519fc18">
        <omgdi:waypoint x="420.0" y="-65.0"/>
        <omgdi:waypoint x="420.00003" y="-167.5"/>
        <omgdi:waypoint x="210.00002" y="-165.00002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-0da791b8-acbc-4ff0-acb2-4226aa9a42b5" bpmnElement="sid-457e06a6-1bb9-48f1-93a6-8905c36daaa7">
        <omgdi:waypoint x="639.99994" y="-120.0"/>
        <omgdi:waypoint x="640.0" y="-65.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-778941f6-726b-464c-b439-c3d3695e6428" bpmnElement="sid-d44d1aa5-63a2-4091-9854-dec3d31648e5">
        <omgdi:waypoint x="639.99994" y="-199.99998"/>
        <omgdi:waypoint x="639.9999" y="-215.0"/>
        <omgdi:waypoint x="420.00003" y="-215.0"/>
        <omgdi:waypoint x="195.0" y="-215.0"/>
        <omgdi:waypoint x="195.0" y="-200.00002"/>
        <omgdi:waypoint x="195.0" y="-172.50002"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

  • 流程视图

测试流程

这里为了测试理解activiti使用暂时使用 Test测试

  1. 创建测试类

这里为了更直观理解,测试类 违反 命名规则 中文命名 可忽略)



import com.lframework.xingyun.api.XingYunApiApplication;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.CollectionUtils;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 借支流程测试
 *
 */
@SpringBootTest(classes = XingYunApiApplication.class)
@Slf4j
public class 借支测试 {
	
	@Autowired
	RepositoryService ps;
	
	@Test
	public void 部署流程() {
		Deployment deploy = ps.createDeployment().addClasspathResource("借支申请.bpmn20.xml").deploy();
		System.out.println(deploy.getId());
	}
	
	
	@Autowired
	RuntimeService runtimeService;
	@Test
	public void 申请借支() {
		Map<String,Object> map = new HashMap<>();
		map.put("name","刚哥");
		// 根据流程定义的ID启动流程
		ProcessInstance instance = runtimeService.startProcessInstanceByKey("jz","jz1",map);
		// 查询此任务并继续
		Task task = taskService.createTaskQuery().processInstanceBusinessKey("jz1").singleResult();
		taskService.complete(task.getId());
		
	}
	
	
	@Autowired
	TaskService taskService;
	
	@Test
	public void 查询待审核的任务() {
		TaskQuery query = taskService.createTaskQuery();
		List<Task> taskList = query.
			processInstanceBusinessKey("jz1"). // 以业务id作为条件
//                processDefinitionKey("qj"). // 以流程id作为条件
//                taskAssignee("张三"). // 以任务办理人作为条件
	list();
		// 3.输出任务信息
		for (Task task : taskList) {
			System.out.println("任务ID:"+task.getId());
			System.out.println("任务名称:"+task.getName());
			System.out.println("任务的创建时间:"+task.getCreateTime());
			System.out.println("任务的办理人:"+task.getAssignee());
			System.out.println("流程实例ID:"+task.getProcessInstanceId());
			System.out.println("执行对象ID:"+task.getExecutionId());
			System.out.println("流程定义ID:"+task.getProcessDefinitionId());
		}
	}
	
	
	//办理任务(主要操作ACT_RU_EXECUTIONACT_RU_TASK)
	@Test
	public void 办理任务() {
		// processInstanceBusinessKey 业务id
		// taskAssignee 任务处理人
		Task task = taskService.createTaskQuery().
			processInstanceBusinessKey("jz1")
//                .taskAssignee("刚哥")
			.singleResult();
		// 设置审批意见
		 taskService.addComment(task.getId(),task.getProcessInstanceId(),"同意");
		// 任务id 完成任务进入下一步
		Map<String, Object> map = new HashMap<>();
		map.put("sp", 1);
		map.put("money", 3500);
		taskService.complete(task.getId(), map);

		System.out.println("任务ID:"+task.getId());
		System.out.println("任务名称:"+task.getName());
		System.out.println("任务审批完成");

	}
	
	
	
	@Autowired
	HistoryService historyService;
	/**
	 * 历史活动实例查询
	 */
	@Test
	public void queryHistoryTask() {
		List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery() // 创建历史活动实例查询
//                .processInstanceId("b6d83887-ad4b-11ee-b76f-005056c00008") // 执行流程实例id
			.processInstanceBusinessKey("jz1")  //根据业务id,查询当前业务下创建的流程实例
			.orderByTaskCreateTime()
			.asc()
			.list();
		for (HistoricTaskInstance hai : list) {
			System.out.println("活动ID:" + hai.getId());
			System.out.println("流程实例ID:" + hai.getProcessInstanceId());
			System.out.println("活动名称:" + hai.getName());
			System.out.println("办理人:" + hai.getAssignee());
			System.out.println("开始时间:" + hai.getStartTime());
			System.out.println("结束时间:" + hai.getEndTime());
			
			
			List<Comment> taskComments = taskService.getTaskComments(hai.getId());
			for (Comment taskComment : taskComments) {
				System.out.println("---"+taskComment.toString());
				System.out.println("审批意见:" + taskComment.getFullMessage());
			}
		}
	}
	
	
	@Test
	public void 根据业务id判断流程是否结束(){
		ProcessInstance pi = runtimeService.createProcessInstanceQuery().
			processInstanceBusinessKey("qj:10086").singleResult();
		System.out.println(pi==null?"结束":"未结束");
	}
	
	
	/**
	 * 查询历史流程变量
	 */
	@Test
	public void queryHisProVariable(){
		// 先根据流程id查询出历史任务
		HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().processInstanceBusinessKey("jz5")
			.list().get(0);
		
		// 查询出该任务执行中所有的变量
		List<HistoricVariableInstance> historicVariableInstanceList = historyService
			.createHistoricVariableInstanceQuery().
			processInstanceId(task.getProcessInstanceId()).list();
		
		boolean b = true;
		
		for(HistoricVariableInstance v: historicVariableInstanceList){
			if(v.getVariableName().endsWith("sp")){
				int sp = (int) v.getValue();
				if (sp == 0) {
					b = false;
					break;
				}
			}
		}
		
		System.out.println("(b?:\"\") = " + (b?"流程审批通过":"流程审批不通过"));
	}
	
	
	
	@Autowired
	RepositoryService repositoryService;
	
	@Test
	public void 流程图(){
		try {
			// 获取历史流程实例
			HistoricProcessInstance historicProcessInstance = historyService
				.createHistoricProcessInstanceQuery()
				.processInstanceBusinessKey("jz1").singleResult();
			// 获取流程中已经执行的节点,按照执行先后顺序排序
			List<HistoricActivityInstance> historicActivityInstances = historyService
				.createHistoricActivityInstanceQuery()
				.processInstanceId(historicProcessInstance.getId())
				.orderByHistoricActivityInstanceId()
				.asc().list();
			// 高亮已经执行流程节点ID集合
			List<String> highLightedActivitiIds = new ArrayList<>();
			int index = 1;
			for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
				// 用默认颜色
				highLightedActivitiIds.add(historicActivityInstance.getActivityId());
				index++;
			}
			ProcessDiagramGenerator processDiagramGenerator = null;
			// 使用默认的程序图片生成器
			processDiagramGenerator = new DefaultProcessDiagramGenerator();
			BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
			// 高亮流程已发生流转的线id集合
			List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances);
			// 使用默认配置获得流程图表生成器,并生成追踪图片字符流
			InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel,
				highLightedActivitiIds, highLightedFlowIds, "宋体","微软雅黑", "黑体");
			// 输出图片内容
			int byteSize = 1024;
			byte[] b = new byte[byteSize];
			OutputStream os = Files.newOutputStream(Paths.get("E:\\ATTPD\\activity\\src\\main\\resources\\a.svg"));
			int len;
			while ((len = imageStream.read(b, 0, byteSize)) != -1) {
				os.write(b, 0, len);
			}
			os.close();
		} catch (Exception e) {
		
		}
		
	}
	
	/**
	 * 获取已经流转的线
	 *
	 * @param bpmnModel
	 * @param historicActivityInstances
	 * @return
	 */
	private static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
		// 高亮流程已发生流转的线id集合
		List<String> highLightedFlowIds = new ArrayList<>();
		// 全部活动节点
		List<FlowNode> historicActivityNodes = new ArrayList<>();
		// 已完成的历史活动节点
		List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();
		
		for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
			FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
			historicActivityNodes.add(flowNode);
			if (historicActivityInstance.getEndTime() != null) {
				finishedActivityInstances.add(historicActivityInstance);
			}
		}
		
		FlowNode currentFlowNode = null;
		FlowNode targetFlowNode = null;
		// 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
		for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
			// 获得当前活动对应的节点信息及outgoingFlows信息
			currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
			List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
			
			/**
			 * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转: 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转
			 */
			if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
				// 遍历历史活动节点,找到匹配流程目标节点的
				for (SequenceFlow sequenceFlow : sequenceFlows) {
					targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
					if (historicActivityNodes.contains(targetFlowNode)) {
						highLightedFlowIds.add(targetFlowNode.getId());
					}
				}
			} else {
				List<Map<String, Object>> tempMapList = new ArrayList<>();
				for (SequenceFlow sequenceFlow : sequenceFlows) {
					for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
						if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
							Map<String, Object> map = new HashMap<>();
							map.put("highLightedFlowId", sequenceFlow.getId());
							map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
							tempMapList.add(map);
						}
					}
				}
				
				if (!CollectionUtils.isEmpty(tempMapList)) {
					// 遍历匹配的集合,取得开始时间最早的一个
					long earliestStamp = 0L;
					String highLightedFlowId = null;
					for (Map<String, Object> map : tempMapList) {
						long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
						if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
							highLightedFlowId = map.get("highLightedFlowId").toString();
							earliestStamp = highLightedFlowStartTime;
						}
					}
					
					highLightedFlowIds.add(highLightedFlowId);
				}
				
			}
			
		}
		return highLightedFlowIds;
	}
	

}

测试结果

  1. 部署流程

结果

也可以数据库看到

  1. 启动流程创建申请任务

结果

也可数据库查看

  1. 查看待审批任务

测试结果

  1. 处理任务

处理结果

  1. 查看历史任务

结果

至此阶段开发目标已经达到

下阶段目标

  1. 目标

任务相关管理PC通过界面管理

  1. 效果图

### 项目介绍 星云ERP基于SpringBoot框架,为中小企业提供完全开源、永久免费、用户体验好的进销存ERP系统,解决开店难、管理难、数据统计难的问题。星云ERP主要包括基础信息管理、商品中心、采购管理、销售管理、零售管理、库存管理、盘点管理、结算管理等,各业务模块均支持参数配置,满足实际遇到的各种业务场景。丰富的报表模块支持用户做各项数据分析。同时支持对部门、岗位、角色、用户、权限等进行精细化管理。最终,达到业务线上化、透明化、简易化管理的目标,实现物流、资金流、信息流的体化管控。 #### 单体架构 集成常用的SpringBoot、MybatisPlus等框架,更利于上手使用或二次开发。 #### 关于商业使用的说明 项目使用Apache 2.0 License,编写的代码无任何闭源情况,均可免费使用。我们对商业使用行为没有限制,只需要遵循Apache2.0 License即可。 ### 演示环境地址 星云ERP平台地址:http://erp.lframework.com <a href="http://erp.lframework.com" target="_blank"> 点此进入</a> ### 底层框架源码 底层框架源码:https://gitee.com/lframework/jugg <a href="https://gitee.com/lframework/jugg" target="_blank"> 点此进入</a> ### 前端项目源码 前端项目源码:https://gitee.com/lframework/xingyun-front <a href="https://gitee.com/lframework/xingyun-front" target="_blank"> 点此进入</a> ### 后端开发的些约定 * cacheName不使用{} * 主库的名称为master * 创建子线程时使用DefaultCallable或DefaultRunnable包装 ### 关于多租户 application.yml中的tenant.enabled改为true则代表开启多租户模式;false则代表关闭多租户模式。 虽然开启和关闭多租户是由配置文件控制,但是由于两种模式数据库结构不同,不支持正在运行中的系统修改租户模式。 ### 使用说明文档 ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值