Activiti7工作流+SpringBoot

Activiti7工作流+SpringBoot一 Activiti 介绍 Activiti 是基于 Apache 许可的开源 BPM 平台 创始人 TomBaeyens 原是 JBPM 架构师 可以理解为与 JBPM 出自同一祖师爷 它提供了 Eclipse 插件 开发可以通过插件直接绘制业务流程图 基于 Spring ibatis 等框架 并在此之上构建了非常清晰的开发框架 是由 Alfresco 软件发布的业务流程管理 BPM 框架 它是覆盖了业务流程管理 工作流 服务

一. Activiti相关概念

1. Activiti介绍

    Activiti是基于Apache许可的开源BPM平台,创始人Tom Baeyens原是JBPM架构师,可以理解为与JBPM出自同一祖师爷。它提供了Eclipse插件,开发可以通过插件直接绘制业务流程图。基于Spring,ibatis等框架,并在此之上构建了非常清晰的开发框架。是由Alfresco软件发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架。 本文基于Activiti7的Activiti Core,基于Spring Boot做简单学习总结。(Activiti最新版本向微服务这边靠齐了,并分Activiti Core与Activiti Cloud两块,Activiti Cloud还没研究)

2. 核心类

2.1 ProcessEngine

    流程引擎的抽象,可以通过此类获取需要的所有服务。

2.2 服务(Service)类

    通过ProcessEngine获取,Activiti将不同生命周期的服务封装在不同Service中,包括定义、部署、运行。通过服务类可获取相关生命周期中的服务信息。

2.2.1 TaskService

    流程运行过程中,每个任务节点的相关操作接口,如complete,delete,delegate等。

2.2.2 RepositoryService

    流程定义和部署相关的存储服务。

2.2.3 RuntimeService

    流程运行时相关的服务,如根据流程好启动流程实例startProcessInstanceByKey。

2.2.3 HistoryService

    历史记录相关服务接口。

2.3 CommandContextIntercepter或CommandExecutor

    Activiti使用命令模式作为基础开发模式,如Service中调用的各个方法都对应相应的命令对象。Service将请求委托给命令对象,命令对象来命令接受者,接受者接收后执行并返回结果。而CommandContextIntercepter的作用是拦截所有命令,并在命令前后执行一些公共方法。

2.4 核心业务对象

    org.activiti.engine.impl.persistence.entity包下的类,包括Task,ProcessInstance,Execution等。会根据不同职责实现相应接口的方法(如需要持久化则继承PersistentObject接口),与传统的实体类不同。

3. 上下文组件(Context)

    用来保存生命周期比较长,全局性的信息,类似Application,主要包括如下三类。

3.1 CommandContext

    命令上下文,保存每个命令必要的资源,如持久化需要的session。

3.2 ProcessEngineConfigurationImpl

    流程引擎相关配置信息,整个引擎全局的配置信息,如数据源DataSource等。该对象为单例,在流程引擎创建的时候初始化。

3.3 ExecutionContext

    持有ExecutionEntity对象。

4. 持久化组件

    Activiti使用ibatis作OR映射,并在此基础上增加设计了自己的持久化框架。在流程引擎创建时初始化。顶层接口Session、SessionFactory。Session有两个实现类:DbSqlSession,负责sql表达式的执行。AbstractManager负责对象的持久化操作。SessionFactory有两个实现类:DbSqlSessionFactory负责DbSqlSession相关操作,GenericManagerFactory负责AbstractManager相关操作。

5. Event-Listener组件

    Activiti允许客户代码介入流程执行,提供了事件监听组件。监听的事件类型可以分为TaskListener、JavaDelegate、Expression、ExecutionListener。ProcessEngineConfigurationImpl持有DelegateInterceptor的某个实例,方便调用handleInvocation。

6. Cache组件

    DbSqlSession中有cache的实现,Activiti基于List和Map来做缓存。如查询时先查缓存,没有则直接查询并放入缓存。

7. 异步执行组件

    Activiti可以执行任务,JobExecutor为启核心类,JobExecutor包含三个主要属性:JobAcquisitionThread,BlockingQueue,ThreadPoolExecutor。方法ProcessEngines在引擎启动时调用JobExecutor.start,JobAcquisitionThread 线程即开始工作,其run方法不断循环执行AcquiredJobs中的job,执行一次后线程等待一定时间直到超时或者JobExecutor.jobWasAdded方法,因为有新任务而被调用。

8. PVM:Process Virtal Machine

二. Eclipse插件安装:

三. 项目搭建

1. 新建Spring Boot工程

2. 引入Activiti相关依赖

    在pom属性中定义版本号,并添加Activiti相关依赖:

 
  
    7.0.56 
   
 
   
    
     
     
       org.activiti.dependencies 
      
     
       activiti-dependencies 
      
     
       ${activiti-dependencies.version} 
      
     
       import 
      
     
       pom 
      
     
    
   
 
   
   
     org.activiti 
    
   
     activiti-spring-boot-starter 
    
   

    由于Activiti默认使用H2数据库,所以需添加H2数据库支持(这里使用此SpringBoot版本默认1.4.197):

 
   
   
     com.h2database 
    
   
     h2 
    
   

    出现错误:Missing artifact org.activiti:activiti-spring-boot-starter:jar:7.0.56

在这里插入图片描述
    添加私服仓库地址:

 
   
    
    
      alfresco 
     
    
      Activiti Releases 
     
    
      https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/ 
     
     
     
       true 
      
     
    
   

在这里插入图片描述
    错误消失。

3. 创建流程图

4. 启动工程

5. 修改配置

    在正常使用中,一般系统会有自己的数据库,而不会采用默认内存的H2数据库,这里以MySQL为例。修改application.yml。

5.1 添加MySQL依赖

 
   
   
   
     mysql 
    
   
     mysql-connector-java 
    
   

5.2 修改数据库

spring: 数据库连接信息 datasource: # 数据源配置 url: jdbc:mysql://127.0.0.1:3306/activity?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver # SQLException: XAER_INVAL: Invalid arguments (or unsupported command)问题 xa: properties: pinGlobalTxToPhysicalConnection: true useServerPrepStmts: true 

5.3 Activiti相关配置

  # 参考配置https://www.cnblogs.com/liaojie970/p/8857710.html
  activiti:
    # 自动建表
    database-schema: ACTIVITI
    database-schema-update: true
    history-level: full
    db-history-used: true

5.4 附上application.yml完整配置:

# 服务配置
server:
  display-name: actdemo
  port: 8085
  
# Spring相关配置
spring:
  数据库连接信息
  datasource:
    # 数据源配置
    url: jdbc:mysql://127.0.0.1:3306/activity?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 888
    driver-class-name: com.mysql.jdbc.Driver
    
    # SQLException: XAER_INVAL: Invalid arguments (or unsupported command)问题
    xa:
      properties:
        pinGlobalTxToPhysicalConnection: true
        useServerPrepStmts: true

  thymeleaf:
    mode: HTML
    encoding: utf-8
    # 禁用缓存
    cache: false
  application:
    # 注册应用名
    name: actdemo
  mvc:
    # 静态资源路径
    static-path-pattern: /static/
  # 参考配置https://www.cnblogs.com/liaojie970/p/8857710.html
  activiti:
    # 自动建表
    database-schema: ACTIVITI
    database-schema-update: true
    history-level: full
    db-history-used: true

5.5 启动工程

6. 编写实例

    本例子使用Thymeleaf做前端页面展示(SpringBoot推荐使用),并创建控制器Controller调用工作流接口与前端交互。

6.1 引入Thymeleaf依赖,(前端使用Thymeleaf的配置已经在application.yml中)

 
   
   
   
     org.springframework.boot 
    
   
     spring-boot-starter-thymeleaf 
    
   

6.2 创建Controller控制器

    创建启动流程方法,主要代码如下

/ * 

启动请假流程

* @return String 流程实例ID * @author FRH * @time 2018年12月10日上午11:03:36 * @version 1.0 */ @RequestMapping(value="/start") @ResponseBody public String start() { // xml中定义的ID String instanceKey = "leaveProcess"; logger.info("开启请假流程..."); // 设置流程参数,开启流程 Map map = new HashMap (); map.put("jobNumber","A1001"); map.put("busData","bus data"); ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, map);//使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动 logger.info("启动流程实例成功:{}", instance); logger.info("流程实例ID:{}", instance.getId()); logger.info("流程定义ID:{}", instance.getProcessDefinitionId()); //验证是否启动成功 //通过查询正在运行的流程实例来判断 ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery(); //根据流程实例ID来查询 List runningList = processInstanceQuery.processInstanceId(instance.getProcessInstanceId()).list(); logger.info("根据流程ID查询条数:{}", runningList.size()); // 返回流程ID return instance.getId(); }

6.3 流程跟踪与流程图展示

    Activiti流程图展示,使用流程图生成器,本例生成的流程图在页面使用如下方式展示即可:

 

    引入相关工具包,版本使用默认版本7.0.65

 
   
   
   
     org.activiti 
    
   
     activiti-image-generator 
    
   

    调用输出流程图

/ * 

查看当前流程图

* @param instanceId 流程实例 * @param response void 响应 * @author FRH * @time 2018年12月10日上午11:14:12 * @version 1.0 */ @ResponseBody @RequestMapping(value="/showImg") public void showImg(String instanceId, HttpServletResponse response) { /* * 参数校验 */ logger.info("查看完整流程图!流程实例ID:{}", instanceId); if(StringUtils.isBlank(instanceId)) return; /* * 获取流程实例 */ HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult(); if(processInstance == null) { logger.error("流程实例ID:{}没查询到流程实例!", instanceId); return; } // 根据流程对象获取流程对象模型 BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); /* * 查看已执行的节点集合 * 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序 */ // 构造历史流程查询 HistoricActivityInstanceQuery historyInstanceQuery = historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId); // 查询历史节点 List historicActivityInstanceList = historyInstanceQuery.orderByHistoricActivityInstanceStartTime().asc().list(); if(historicActivityInstanceList == null || historicActivityInstanceList.size() == 0) { logger.info("流程实例ID:{}没有历史节点信息!", instanceId); outputImg(response, bpmnModel, null, null); return; } // 已执行的节点ID集合(将historicActivityInstanceList中元素的activityId字段取出封装到executedActivityIdList) List executedActivityIdList = historicActivityInstanceList.stream().map(item -> item.getActivityId()).collect(Collectors.toList()); /* * 获取流程走过的线 */ // 获取流程定义 ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processInstance.getProcessDefinitionId()); List flowIds = ActivitiUtils.getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList); /* * 输出图像,并设置高亮 */ outputImg(response, bpmnModel, flowIds, executedActivityIdList); } / *

输出图像

* @param response 响应实体 * @param bpmnModel 图像对象 * @param flowIds 已执行的线集合 * @param executedActivityIdList void 已执行的节点ID集合 * @author FRH * @time 2018年12月10日上午11:23:01 * @version 1.0 */ private void outputImg(HttpServletResponse response, BpmnModel bpmnModel, List flowIds, List executedActivityIdList) { InputStream imageStream = null; try { imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", true, "png"); // 输出资源内容到相应对象 byte[] b = new byte[1024]; int len; while ((len = imageStream.read(b, 0, 1024)) != -1) { response.getOutputStream().write(b, 0, len); } response.getOutputStream().flush(); }catch(Exception e) { logger.error("流程图输出异常!", e); } finally { // 流关闭 StreamUtils.closeInputStream(imageStream); } }

    流程图工具类ActivitiUtils

package com.mypro.activiti.utils; import java.util.ArrayList; import java.util.List; import org.activiti.bpmn.model.BpmnModel; import org.activiti.bpmn.model.FlowNode; import org.activiti.bpmn.model.SequenceFlow; import org.activiti.engine.history.HistoricActivityInstance; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; / * 

Activiti工作流工具类

* @author FRH * @time 2018年12月10日上午11:26:02 * @version 1.0 */ public class ActivitiUtils { / *

获取流程走过的线

* @param bpmnModel 流程对象模型 * @param processDefinitionEntity 流程定义对象 * @param historicActivityInstances 历史流程已经执行的节点,并已经按执行的先后顺序排序 * @return List 流程走过的线 * @author FRH * @time 2018年12月10日上午11:26:19 * @version 1.0 */ public static List getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List historicActivityInstances) { // 用以保存高亮的线flowId List highFlows = new ArrayList (); if(historicActivityInstances == null || historicActivityInstances.size() == 0) return highFlows; // 遍历历史节点 for (int i = 0; i < historicActivityInstances.size() - 1; i++) { // 取出已执行的节点 HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i); // 用以保存后续开始时间相同的节点 List sameStartTimeNodes = new ArrayList (); // 获取下一个节点(用于连线) FlowNode sameActivityImpl = getNextFlowNode(bpmnModel, historicActivityInstances, i, activityImpl_); // FlowNode sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i + 1).getActivityId()); // 将后面第一个节点放在时间相同节点的集合里 if(sameActivityImpl != null) sameStartTimeNodes.add(sameActivityImpl); // 循环后面节点,看是否有与此后继节点开始时间相同的节点,有则添加到后继节点集合 for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) { HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点 HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点 if (activityImpl1.getStartTime().getTime() != activityImpl2.getStartTime().getTime()) break; // 如果第一个节点和第二个节点开始时间相同保存 FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId()); sameStartTimeNodes.add(sameActivityImpl2); } // 得到节点定义的详细信息 FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId()); // 取出节点的所有出去的线,对所有的线进行遍历 List pvmTransitions = activityImpl.getOutgoingFlows(); for (SequenceFlow pvmTransition : pvmTransitions) { // 获取节点 FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef()); // 不是后继节点 if(!sameStartTimeNodes.contains(pvmActivityImpl)) continue; // 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示 highFlows.add(pvmTransition.getId()); } } //返回高亮的线 return highFlows; } / *

获取下一个节点信息

* @param bpmnModel 流程模型 * @param historicActivityInstances 历史节点 * @param i 当前已经遍历到的历史节点索引(找下一个节点从此节点后) * @param activityImpl_ 当前遍历到的历史节点实例 * @return FlowNode 下一个节点信息 * @author FRH * @time 2018年12月10日上午11:26:55 * @version 1.0 */ private static FlowNode getNextFlowNode(BpmnModel bpmnModel, List historicActivityInstances, int i, HistoricActivityInstance activityImpl_) { // 保存后一个节点 FlowNode sameActivityImpl = null; // 如果当前节点不是用户任务节点,则取排序的下一个节点为后续节点 if(!"userTask".equals(activityImpl_.getActivityType())) { // 是最后一个节点,没有下一个节点 if(i == historicActivityInstances.size()) return sameActivityImpl; // 不是最后一个节点,取下一个节点为后继节点 sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i + 1).getActivityId());// 找到紧跟在后面的一个节点 // 返回 return sameActivityImpl; } // 遍历后续节点,获取当前节点后续节点 for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) { // 后续节点 HistoricActivityInstance activityImp2_ = historicActivityInstances.get(k); // 都是userTask,且主节点与后续节点的开始时间相同,说明不是真实的后继节点 if("userTask".equals(activityImp2_.getActivityType()) && activityImpl_.getStartTime().getTime() == activityImp2_.getStartTime().getTime()) continue; // 找到紧跟在后面的一个节点 sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId()); break; } return sameActivityImpl; } }

6.4 附上DemoController完整代码

package com.mypro.activiti.controller; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.activiti.bpmn.model.BpmnModel; 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.HistoricActivityInstanceQuery; import org.activiti.engine.history.HistoricProcessInstance; import org.activiti.engine.impl.RepositoryServiceImpl; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.runtime.ProcessInstanceQuery; import org.activiti.engine.task.Task; import org.activiti.image.ProcessDiagramGenerator; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.mypro.activiti.utils.ActivitiUtils; import com.mypro.activiti.utils.StreamUtils; / * 

Activiti控制器

* @author FRH * @time 2018年12月10日上午9:30:18 * @version 1.0 */ @Controller @RequestMapping("/demo") public class DemoController { private static final Logger logger = LoggerFactory.getLogger(DemoController.class); / 流程定义和部署相关的存储服务 */ @Autowired private RepositoryService repositoryService; / 流程运行时相关的服务 */ @Autowired private RuntimeService runtimeService; / 节点任务相关操作接口 */ @Autowired private TaskService taskService; / 流程图生成器 */ @Autowired private ProcessDiagramGenerator processDiagramGenerator; / 历史记录相关服务接口 */ @Autowired private HistoryService historyService; / *

跳转到测试主页面

* @return String 测试主页面 * @author FRH * @time 2018年12月10日上午11:12:28 * @version 1.0 */ @RequestMapping(value="/toIndex.html") public String toTestPage() { return "/index"; } / *

跳转到上级审核页面

* @return String 上级审核页面 * @author FRH * @time 2018年12月5日下午2:31:42 * @version 1.0 */ @RequestMapping(value="/toLeave") public String employeeLeave() { return "/employeeLeave"; } / *

启动请假流程(流程key即xml中定义的ID为leaveProcess)

* @return String 启动的流程ID * @author FRH * @time 2018年12月10日上午11:12:50 * @version 1.0 */ @RequestMapping(value="/start") @ResponseBody public String start() { /* * xml中定义的ID */ String instanceKey = "leaveProcess"; logger.info("开启请假流程..."); /* * 设置流程参数,开启流程 */ Map map = new HashMap (); map.put("jobNumber","A1001"); map.put("busData","bus data"); ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, map);//使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动 logger.info("启动流程实例成功:{}", instance); logger.info("流程实例ID:{}", instance.getId()); logger.info("流程定义ID:{}", instance.getProcessDefinitionId()); /* * 验证是否启动成功 */ //通过查询正在运行的流程实例来判断 ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery(); //根据流程实例ID来查询 List runningList = processInstanceQuery.processInstanceId(instance.getProcessInstanceId()).list(); logger.info("根据流程ID查询条数:{}", runningList.size()); /* * 返回流程ID */ return instance.getId(); } / *

查看当前流程图

* @param instanceId 流程实例 * @param response void 响应 * @author FRH * @time 2018年12月10日上午11:14:12 * @version 1.0 */ @ResponseBody @RequestMapping(value="/showImg") public void showImg(String instanceId, HttpServletResponse response) { /* * 参数校验 */ logger.info("查看完整流程图!流程实例ID:{}", instanceId); if(StringUtils.isBlank(instanceId)) return; /* * 获取流程实例 */ HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult(); if(processInstance == null) { logger.error("流程实例ID:{}没查询到流程实例!", instanceId); return; } // 根据流程对象获取流程对象模型 BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); /* * 查看已执行的节点集合 * 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序 */ // 构造历史流程查询 HistoricActivityInstanceQuery historyInstanceQuery = historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId); // 查询历史节点 List historicActivityInstanceList = historyInstanceQuery.orderByHistoricActivityInstanceStartTime().asc().list(); if(historicActivityInstanceList == null || historicActivityInstanceList.size() == 0) { logger.info("流程实例ID:{}没有历史节点信息!", instanceId); outputImg(response, bpmnModel, null, null); return; } // 已执行的节点ID集合(将historicActivityInstanceList中元素的activityId字段取出封装到executedActivityIdList) List executedActivityIdList = historicActivityInstanceList.stream().map(item -> item.getActivityId()).collect(Collectors.toList()); /* * 获取流程走过的线 */ // 获取流程定义 ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processInstance.getProcessDefinitionId()); List flowIds = ActivitiUtils.getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList); /* * 输出图像,并设置高亮 */ outputImg(response, bpmnModel, flowIds, executedActivityIdList); } / *

员工提交申请

* @param request 请求 * @return String 申请受理结果 * @author FRH * @time 2018年12月10日上午11:15:09 * @version 1.0 */ @RequestMapping(value="/employeeApply") @ResponseBody public String employeeApply(HttpServletRequest request){ /* * 获取请求参数 */ String taskId = request.getParameter("taskId"); // 任务ID String jobNumber = request.getParameter("jobNumber"); // 工号 String leaveDays = request.getParameter("leaveDays"); // 请假天数 String leaveReason = request.getParameter("leaveReason"); // 请假原因 /* * 查询任务 */ Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if(task == null) { logger.info("任务ID:{}查询到任务为空!", taskId); return "fail"; } /* * 参数传递并提交申请 */ Map map = new HashMap (); map.put("days", leaveDays); map.put("date", new Date()); map.put("reason", leaveReason); map.put("jobNumber", jobNumber); taskService.complete(task.getId(), map); logger.info("执行【员工申请】环节,流程推动到【上级审核】环节"); /* * 返回成功 */ return "success"; } / *

跳转到上级审核页面

* @return String 页面 * @author FRH * @time 2018年12月5日下午2:31:42 * @version 1.0 */ @RequestMapping(value="/viewTask") public String toHigherAudit(String taskId, HttpServletRequest request) { /* * 获取参数 */ logger.info("跳转到任务详情页面,任务ID:{}", taskId); if(StringUtils.isBlank(taskId)) return "/higherAudit"; /* * 查看任务详细信息 */ Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if(task == null) { logger.info("任务ID:{}不存在!", taskId); return "/higherAudit"; } /* * 完成任务 */ Map paramMap = taskService.getVariables(taskId); request.setAttribute("task", task); request.setAttribute("paramMap", paramMap); return "higherAudit"; } / *

跳转到部门经理审核页面

* @param taskId 任务ID * @param request 请求 * @return String 响应页面 * @author FRH * @time 2018年12月6日上午9:54:34 * @version 1.0 */ @RequestMapping(value="/viewTaskManager") public String viewTaskManager(String taskId, HttpServletRequest request) { /* * 获取参数 */ logger.info("跳转到任务详情页面,任务ID:{}", taskId); if(StringUtils.isBlank(taskId)) return "/manageAudit"; /* * 查看任务详细信息 */ Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if(task == null) { logger.info("任务ID:{}不存在!", taskId); return "/manageAudit"; } /* * 完成任务 */ Map paramMap = taskService.getVariables(taskId); request.setAttribute("task", task); request.setAttribute("paramMap", paramMap); return "manageAudit"; } / *

上级审核

* @param request 请求 * @return String 受理结果 * @author FRH * @time 2018年12月10日上午11:19:44 * @version 1.0 */ @ResponseBody @RequestMapping(value="/higherLevelAudit") public String higherLevelAudit(HttpServletRequest request) { /* * 获取请求参数 */ String taskId = request.getParameter("taskId"); String higherLevelOpinion = request.getParameter("sug"); String auditStr = request.getParameter("audit"); logger.info("上级审核任务ID:{}", taskId); if(StringUtils.isBlank(taskId)) return "fail"; /* * 查找任务 */ Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if(task == null) { logger.info("审核任务ID:{}查询到任务为空!", taskId); return "fail"; } /* * 设置局部变量参数,完成任务 */ Map map = new HashMap (); map.put("audit", "1".equals(auditStr) ? false : true); map.put("higherLevelOpinion", higherLevelOpinion); taskService.complete(taskId, map); return "success"; } / *

部门经理审核

* @param request 请求 * @return String 受理结果 * @author FRH * @time 2018年12月10日上午11:20:44 * @version 1.0 */ @ResponseBody @RequestMapping(value="/divisionManagerAudit") public String divisionManagerAudit(HttpServletRequest request) { /* * 获取请求参数 */ String taskId = request.getParameter("taskId"); String opinion = request.getParameter("sug"); String auditStr = request.getParameter("audit"); logger.info("上级审核任务ID:{}", taskId); if(StringUtils.isBlank(taskId)) return "fail"; /* * 查找任务 */ Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if(task == null) { logger.info("审核任务ID:{}查询到任务为空!", taskId); return "fail"; } /* * 设置局部变量参数,完成任务 */ Map map = new HashMap (); map.put("audit", "1".equals(auditStr) ? false : true); map.put("managerOpinion", opinion); taskService.complete(taskId, map); return "success"; } / *

查看任务

* @param request 请求 * @return String 任务展示页面 * @author FRH * @time 2018年12月10日上午11:21:33 * @version 1.0 */ @RequestMapping(value="/toShowTask") public String toShowTask(HttpServletRequest request) { /* * 获取请求参数 */ List taskList = taskService.createTaskQuery().list(); if(taskList == null || taskList.size() == 0) { logger.info("查询任务列表为空!"); return "/task"; } /* * 查询所有任务,并封装 */ List > resultList = new ArrayList >(); for(Task task : taskList) { Map map = new HashMap (); map.put("taskId", task.getId()); map.put("name", task.getName()); map.put("createTime", task.getCreateTime().toString()); map.put("assignee", task.getAssignee()); map.put("instanceId", task.getProcessInstanceId()); map.put("executionId", task.getExecutionId()); map.put("definitionId", task.getProcessDefinitionId()); resultList.add(map); } /* * 返回结果 */ logger.info("返回集合:{}", resultList.toString()); request.setAttribute("resultList", resultList); return "/task"; } / *

输出图像

* @param response 响应实体 * @param bpmnModel 图像对象 * @param flowIds 已执行的线集合 * @param executedActivityIdList void 已执行的节点ID集合 * @author FRH * @time 2018年12月10日上午11:23:01 * @version 1.0 */ private void outputImg(HttpServletResponse response, BpmnModel bpmnModel, List flowIds, List executedActivityIdList) { InputStream imageStream = null; try { imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", true, "png"); // 输出资源内容到相应对象 byte[] b = new byte[1024]; int len; while ((len = imageStream.read(b, 0, 1024)) != -1) { response.getOutputStream().write(b, 0, len); } response.getOutputStream().flush(); }catch(Exception e) { logger.error("流程图输出异常!", e); } finally { // 流关闭 StreamUtils.closeInputStream(imageStream); } } / *

判断流程是否完成

* @param processInstanceId 流程实例ID * @return boolean 已完成-true,未完成-false * @author FRH * @time 2018年12月10日上午11:23:26 * @version 1.0 */ public boolean isFinished(String processInstanceId) { return historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count() > 0; } }

7. 效果

在这里插入图片描述
    看出Activiti默认使用Spring的security,添加配置,关闭安全认证,如下:

# 关闭activiti登录验证 security: basic: enabled: false 

在这里插入图片描述

    附上首页/index.html代码,其它页面相比比较简单:

 
   
    
    首页 
     
     
     
     
    
流程实例ID:








版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/178056.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午6:07
下一篇 2026年3月26日 下午6:08


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号