手把手教你如何玩转Activiti工作流

置顶 2018年01月30日 19:51:36 Cs_hnu_scw 阅读数:24023
 
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Cs_hnu_scw/article/details/79059965

一:Activiti的介绍

场景:学校

主角:阿毛  ,   班主任   ,教务处处长

问题:有一天,阿毛到学校,感觉到身体不舒服,然后想跟班主任请假,然后班主任告诉阿毛说,你想请假,那么就必须要请假条,这个上面必须要我同意,然后再拿到教务处去盖章,然后交给我,这样才可以进行请假。。阿毛,想着,怎么请个假都这么麻烦,这么多层次处理问题,能不能简便一点。。。。好烦好烦~!!~~

分析:从上面的小例子,我们可以很明显的得到一个结论,就是:

请假流程:阿毛------》提交申请-----》班主任审批-----》教务处审批-----》请假成功

也就是说,这种问题就是一种流式的控制管理,当然,这是最简单的,因为里面没有包含着回馈,相当于只是一个方向。其实,到这里,Activiti的定义就已经出来了。。。。。

Activiti定义:

Activiti5是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架。Activiti基于Apache许可的开源BPM平台,创始人Tom Baeyens是JBoss jBPM的项目架构师,它特色是提供了eclipse插件,开发人员可以通过插件直接绘画出业务流程图。

二:牛刀小试---------搭建第一个Activiti项目流程

环境:IDEA(或者Eclipse,本人这里是用IDEA进行的)

步骤:

(1)第一步:IDEA安装Activiti插件

首先,创建一个普通的Java工程即可,然后按照下面流程进行:

1:点击菜单中的File(最左上角),选择settings

2:3

3:然后点击右边的安装,等待一下就可以啦。

(2)创建Activiti流图

(3)创建一个流程图(这里就用一个学生请假实例来进行)

1:首先选择一个startEvent,也就是流程的开始,并且点击一个额外的界面地方,然后输入该处理流程的名称和ID,这里就输入为shenqing

2:在选择一个UserTask按钮,表示一个处理任务,同理命名为“请假申请”

3:在选择一个UserTask按钮,表示一个处理任务,同理命名为“班主任”

4:在选择一个UserTask按钮,表示一个处理任务,同理命名为“教务处”

5:选择一个EndEvent按钮,表示流程的结束;

6:将各个按钮进行连线。(将鼠标放到每个按钮的“正中心”,然后拖着到想要链接的另外一个按钮即可,出现线条)

7:最终的效果。描述:就是学生提交请假申请——》班主任审核——》教务处审核

(4)将第三步中创建的shenqing.bpmn文件生成一个png格式的内容。对于这个的话,在IDEA与Eclipse有一点不一样,因为,在Eclipse中,当保存了之后,就会生成一个对应的png的图片,而在IDEA中需要手动进行生成。

1:首先将shenqing.bpmn的后缀改为xml

2:当点击xml文件,我们会看到里面之前的文字都是乱码了,那么如何进行解决?

小点1:找到自己IDEA的安装目录下的bin文件

小点2:找到如图所示的内容

小点3:分别打开这两个文件,然后添加一行内容(追加到最后即可):

小点4:保存内容,然后重启IDEA,就会发现不会乱码了

3:右击xml文件,然后选择:

4:然后继续:

保存到对应的工程下面即可。就会看到有个shenqing.png的内容出现

5:将之前修改为xml的文件的后缀改回原来的bpmn.

(5)创建一个acitiviti.cfg.xml文件,放在src目录下即可。这个主要是用于存放后面acitivi部署流程中,创建的相关联的一些表。

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  4.  
  5. <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
  6. <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
  7. <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activititest?useUnicode=true&characterEncoding=utf8"></property>
  8. <property name="jdbcUsername" value="xxxxxx"></property>
  9. <property name="jdbcPassword" value="xxxxxxxx"></property>
  10. <!--
  11. 创建表的策略
  12. -->
  13. <property name="databaseSchemaUpdate" value="true"></property>
  14. </bean>
  15. </beans>

(6)导入Activiti的包--------当然也可以直接通过Maven进行依赖包的管理

(7)创建一个数据库生成的测试。(注意:要保证本地有对应名字的数据库)

  1. package com.hnu.scw.activiti;
  2. import org.activiti.engine.ProcessEngine;
  3. import org.activiti.engine.ProcessEngineConfiguration;
  4. import org.junit.Test;
  5. /**
  6. * @author scw
  7. * @create 2018-01-15 11:06
  8. * @desc 从数据源和流程图中,生成一个数据库表(这个就是Activiti流程控制的关键的数据表)
  9. **/
  10. public class ActivitiTable {
  11. /**
  12. * 创建Activiti流的相关的数据库表
  13. */
  14. @Test
  15. public void creatTable(){
  16. ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml")
  17. .buildProcessEngine();
  18. }
  19. }

如果,运行测试方法成功之后,再进入数据库,我们会看到产生了如下多张数据表(23张):

后面我会再详细讲解这每个表的作用,也是activiti的关键所在。

(8)进行流程部署的重点开发(按照下面的流程进行)------------重点的重点

  1. package com.hnu.scw.activiti;
  2. import org.activiti.engine.ProcessEngine;
  3. import org.activiti.engine.ProcessEngines;
  4. import org.activiti.engine.task.Task;
  5. import org.junit.Test;
  6. import java.util.List;
  7. /**
  8. * @author scw
  9. * @create 2018-01-15 11:04
  10. * @desc 用于进行演示Activiti的首例程序,即描述如何在代码中实现学生进行请假申请,班主任审核,教务处审核
  11. **/
  12. public class ActivitiTest {
  13.  
  14. /**
  15. * 1、部署流程
  16. * 2、启动流程实例
  17. * 3、请假人发出请假申请
  18. * 4、班主任查看任务
  19. * 5、班主任审批
  20. * 6、最终的教务处Boss审批
  21. */
  22. /**
  23. * 1:部署一个Activiti流程
  24. * 运行成功后,查看之前的数据库表,就会发现多了很多内容
  25. */
  26. @Test
  27. public void creatActivitiTask(){
  28. //加载的那两个内容就是我们之前已经弄好的基础内容哦。
  29. //得到了流程引擎
  30. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  31. processEngine.getRepositoryService()
  32. .createDeployment()
  33. .addClasspathResource("shenqing.bpmn")
  34. .addClasspathResource("shenqing.png")
  35. .deploy();
  36. }
  37. /**
  38. * 2:启动流程实例
  39. */
  40. @Test
  41. public void testStartProcessInstance(){
  42. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  43. processEngine.getRuntimeService()
  44. .startProcessInstanceById("shenqing:1:4"); //这个是查看数据库中act_re_procdef表
  45. }
  46. /**
  47. * 完成请假申请
  48. */
  49. @Test
  50. public void testQingjia(){
  51. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  52. processEngine.getTaskService()
  53. .complete("104"); //查看act_ru_task表
  54. }
  55.  
  56. /**
  57. * 小明学习的班主任小毛查询当前正在执行任务
  58. */
  59. @Test
  60. public void testQueryTask(){
  61. //下面代码中的小毛,就是我们之前设计那个流程图中添加的班主任内容
  62. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  63. List<Task> tasks = processEngine.getTaskService()
  64. .createTaskQuery()
  65. .taskAssignee("小毛")
  66. .list();
  67. for (Task task : tasks) {
  68. System.out.println(task.getName());
  69. }
  70. }
  71.  
  72. /**
  73. * 班主任小毛完成任务
  74. */
  75. @Test
  76. public void testFinishTask_manager(){
  77. ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  78. engine.getTaskService()
  79. .complete("202"); //查看act_ru_task数据表
  80. }
  81.  
  82. /**
  83. * 教务处的大毛完成的任务
  84. */
  85. @Test
  86. public void testFinishTask_Boss(){
  87. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  88. processEngine.getTaskService()
  89. .complete("302"); //查看act_ru_task数据表
  90. }
  91. }

注意:通过上面的代码,一个个@Test,过来,主要是查看act_get_bytearray,act_ru_task和act_re_procdef三张表,当每进行不同的代码的时候,记得关注一下数据库的变化哦。。。你就会发现其中的奥秘的,

(9)小试牛刀的审批流程,基本完成。。。。。。。项目结构如下所示:

三:Activiti流程部署的方法

描述:对于流程处理, 第一步就是要进行流程的部署操作,在”小试牛刀“上面的代码中,主要是采取读取bpmn和png的资源文件的方法,那么除了这种方法之外,还有其他的方法吗?

备注:首先,说一下,在接下来的操作的时候,主要看下面的三张表的改变:

  1. 涉及到的表
  2. * act_ge_bytearray:
  3. * 1、英文解释
  4. * act:activiti
  5. * ge:general
  6. * bytearray:二进制
  7. * 2、字段
  8. * name_:文件的路径加上名称
  9. * bytes_:存放内容
  10. * deployment_id_:部署ID
  11. * 3、说明:
  12. * 如果要查询文件(bpmn和png),需要知道deploymentId
  13. * act_re_deployment
  14. * 1、解析
  15. * re:repository
  16. * deployment:部署 用户描述一次部署
  17. * 2、字段
  18. * ID_:部署ID 主键
  19. * act_re_procdef
  20. * 1、解释
  21. * procdef: process definition 流程定义
  22. * 2、字段
  23. * id_:pdid:pdkey:pdversion:随机数
  24. * name:名称
  25. * key:名称
  26. * version:版本号
  27. * 如果名称不变,每次部署,版本号加1
  28. * 如果名称改变,则版本号从1开始计算
  29. * deployment_id_:部署ID
  1. 涉及到的表
  2. * act_ge_bytearray:
  3. * 1、英文解释
  4. * act:activiti
  5. * ge:general
  6. * bytearray:二进制
  7. * 2、字段
  8. * name_:文件的路径加上名称
  9. * bytes_:存放内容
  10. * deployment_id_:部署ID
  11. * 3、说明:
  12. * 如果要查询文件(bpmnpng),需要知道deploymentId
  13. * act_re_deployment
  14. * 1、解析
  15. * re:repository
  16. * deployment:部署 用户描述一次部署
  17. * 2、字段
  18. * ID_:部署ID 主键
  19. * act_re_procdef
  20. * 1、解释
  21. * procdef: process definition 流程定义
  22. * 2、字段
  23. * id_:pdid:pdkey:pdversion:随机数
  24. * name:名称
  25. * key:名称
  26. * version:版本号
  27. * 如果名称不变,每次部署,版本号加1
  28. * 如果名称改变,则版本号从1开始计算
  29. * deployment_id_:部署ID

方法一:

  1. /**
  2. * 通过bpmn和png资源进行部署
  3. */
  4. @Test
  5. public void testDeployFromClasspath(){
  6. //得到流程引擎
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. processEngine.getRepositoryService()
  9. .createDeployment()
  10. .addClasspathResource("shenqing.bpmn")
  11. .addClasspathResource("shenqing.png")
  12. .deploy();
  13. }

方法二:

  1. /**
  2. * 通过 inputstream完成部署
  3. */
  4. @Test
  5. public void testDeployFromInputStream(){
  6. InputStream bpmnStream = this.getClass().getClassLoader().getResourceAsStream("shenqing.bpmn");
  7. //得到流程引擎
  8. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  9. processEngine.getRepositoryService()
  10. .createDeployment()
  11. .addInputStream("shenqing.bpmn", bpmnStream)
  12. .deploy();
  13. }

方法三:

  1. /**
  2. * 通过zipinputstream完成部署
  3. * 注意:这个的话,需要将bpmn和png文件进行压缩成zip文件,然后放在项目src目录下即可(当然其他目录也可以)
  4. */
  5. @Test
  6. public void testDeployFromZipinputStream(){
  7. InputStream in = this.getClass().getClassLoader().getResourceAsStream("shenqing.zip");
  8. ZipInputStream zipInputStream = new ZipInputStream(in);
  9. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  10. processEngine.getRepositoryService()
  11. .createDeployment()
  12. .addZipInputStream(zipInputStream)
  13. .deploy();
  14. }

删除已经部署的Activiti的代码:

  1. /**
  2. * 删除已经部署的Activiti流程
  3. */
  4. @Test
  5. public void testDelete(){
  6. //得到流程引擎
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. //第一个参数是部署的流程的ID,第二个true表示是进行级联删除
  9. processEngine.getRepositoryService()
  10. .deleteDeployment("601",true);
  11. }

四:关于流程部署相关的其他API的示例

(1)

  1. /**
  2. * 根据名称查询流程部署
  3. */
  4. @Test
  5. public void testQueryDeploymentByName(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. List<Deployment> deployments = processEngine.getRepositoryService()
  8. .createDeploymentQuery()
  9. .orderByDeploymenTime()//按照部署时间排序
  10. .desc()//按照降序排序
  11. .deploymentName("请假流程")
  12. .list();
  13. for (Deployment deployment : deployments) {
  14. System.out.println(deployment.getId());
  15. }
  16. }

数据库情况:

执行后输出:

(2)

  1. /**
  2. * 查询所有的部署流程
  3. */
  4. @Test
  5. public void queryAllDeplyoment(){
  6. //得到流程引擎
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. List<Deployment> lists = processEngine.getRepositoryService()
  9. .createDeploymentQuery()
  10. .orderByDeploymenTime()//按照部署时间排序
  11. .desc()//按照降序排序
  12. .list();
  13. for (Deployment deployment:lists) {
  14. System.out.println(deployment.getId() +" 部署名称" + deployment.getName());
  15. }
  16. }

数据库情况:

程序执行后输出:

801    部署名称请假流程

(3)

  1. /**
  2. * 查询所有的流程定义
  3. */
  4. @Test
  5. public void testQueryAllPD(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. List<ProcessDefinition> pdList = processEngine.getRepositoryService()
  8. .createProcessDefinitionQuery()
  9. .orderByProcessDefinitionVersion()
  10. .desc()
  11. .list();
  12. for (ProcessDefinition pd : pdList) {
  13. System.out.println(pd.getName());
  14. }
  15. }

数据库情况:

程序执行后输出:

shenqing

(4)

  1. /**
  2. * 查看流程图
  3. * 根据deploymentId和name(在act_ge_bytearray数据表中)
  4. */
  5. @Test
  6. public void testShowImage() throws Exception{
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. InputStream inputStream = processEngine.getRepositoryService()
  9. /**
  10. * deploymentID
  11. * 文件的名称和路径
  12. */
  13. .getResourceAsStream("801","shenqing.png");
  14. OutputStream outputStream3 = new FileOutputStream("e:/processimg.png");
  15. int b = -1 ;
  16. while ((b=inputStream.read())!=-1){
  17. outputStream3.write(b);
  18. }
  19. inputStream.close();
  20. outputStream3.close();
  21. }

数据库情况:

(5)

  1. /**
  2. * 根据pdid查看图片(在act_re_procdef数据表中)
  3. * @throws Exception
  4. */
  5. @Test
  6. public void testShowImage2() throws Exception{
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. InputStream inputStream = processEngine.getRepositoryService()
  9. .getProcessDiagram("shenqing:1:804");
  10. OutputStream outputStream = new FileOutputStream("e:/processimg.png");
  11. int b = -1 ;
  12. while ((b=inputStream.read())!=-1){
  13. outputStream.write(b);
  14. }
  15. inputStream.close();
  16. outputStream.close();
  17. }

数据库情况:

(6)

  1. /**
  2. * 查看bpmn文件(在act_re_procdef数据表中)
  3. */
  4. @Test
  5. public void testShowBpmn() throws Exception{
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. InputStream inputStream = processEngine.getRepositoryService()
  8. .getProcessModel("shenqing:1:804");
  9. OutputStream outputStream = new FileOutputStream("e:/processimg.bpmn");
  10. int b = -1 ;
  11. while ((b=inputStream.read())!=-1){
  12. outputStream.write(b);
  13. }
  14. inputStream.close();
  15. outputStream.close();
  16. }

数据库情况:

五:关于流程实例的相关API

涉及到的表:

 *    act_hi_actinst
 *     1、说明
 *         act:activiti
 *         hi:history
 *         actinst:activity instance
 *            流程图上出现的每一个元素都称为activity
 *            流程图上正在执行的元素或者已经执行完成的元素称为activity instance
 *      2、字段
 *         proc_def_id:pdid
 *         proc_inst_id:流程实例ID
 *         execution_id_:执行ID
 *         act_id_:activity
 *         act_name
 *         act_type
 *    act_hi_procinst
 *      1、说明
 *         procinst:process instance  历史的流程实例
 *            正在执行的流程实例也在这张表中
 *         如果end_time_为null,说明正在执行,如果有值,说明该流程实例已经结束了
 *    act_hi_taskinst
 *      1、说明
 *          taskinst:task instance  历史任务
 *             正在执行的任务也在这张表中
 *             如果end_time_为null,说明该任务正在执行
 *             如果end_time不为null,说明该任务已经执行完毕了
 *    act_ru_execution
 *      1、说明
 *         ru:runtime
 *         代表正在执行的流程实例表
 *         如果当期正在执行的流程实例结束以后,该行在这张表中就被删除掉了,所以该表也是一个临时表
 *      2、字段
 *         proc_inst_id_:piid  流程实例ID,如果不存在并发的情况下,piid和executionID是一样的
 *         act_id:当前正在执行的流程实例(如果不考虑并发的情况)的正在执行的activity有一个,所以act_id就是当前正在执行的流程实例的正在执行的
 *           节点
 *    act_ru_task
 *      1、说明
 *         代表正在执行的任务表
 *         该表是一个临时表,如果当前任务被完成以后,任务在这张表中就被删除掉了
 *      2、字段
 *          id_:  主键    任务ID
 *          execution_id_:执行ID  
 *              根据该ID查询出来的任务肯定是一个
 *          proc_inst_id:piid
 *              根据该id查询出来的任务
 *                 如果没有并发,则是一个
 *                 如果有并发,则是多个
 *          name_:任务的名称
 *          assignee_:任务的执行人

(1)启动流程实例

方法一:

  1. /**
  2. * 启动流程实例,通过PID
  3. */
  4. @Test
  5. public void testStartProcessInstanceByPID(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. ProcessInstance processInstance = processEngine.getRuntimeService()
  8. .startProcessInstanceById("shenqing:1:804"); //这个就是从部署的时候生成的一个内容,如下数据库所示
  9. System.out.println(processInstance.getId());
  10. }

方法二:

  1. /**
  2. * 根据pdkey启动流程实例,默认启动最高版本的
  3. */
  4. @Test
  5. public void testStartPIByPDKEY(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. processEngine.getRuntimeService()
  8. .startProcessInstanceByKey("shenqing"); //这个字段对应上面那个数据库中的Key字段
  9. }

对应的数据库内容:

执行后:(就多出了一条流程)

(2)完成任务

  1. /**
  2. * 完成任务
  3. */
  4. @Test
  5. public void testFinishTask(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. processEngine.getTaskService()
  8. .complete("1102");
  9. }

对应的表:

执行后的情况:(注意表不同了,原来那个表的字段ID的条目信息,就没了)

(3)

  1. /**
  2. * 查询任务
  3. * 根据任务的执行人查询正在执行任务(通过act_ru_task数据表)
  4. */
  5. @Test
  6. public void testQueryTaskByAssignee(){
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. /**
  9. * 当前班主任小毛人这个人当前正在执行的所有的任务
  10. */
  11. List<Task> tasks = processEngine.getTaskService()
  12. .createTaskQuery()
  13. .orderByTaskCreateTime()
  14. .desc()
  15. .taskAssignee("小毛")
  16. .list();
  17. for (Task task : tasks) {
  18. System.out.println(task.getName());
  19. System.out.println(task.getAssignee());
  20. }
  21. }

数据库情况:

程序执行后,输出:

班主任
小毛

(4)

  1. /**
  2. * 查询所有的正在执行的任务
  3. */
  4. @Test
  5. public void testQueryTask(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. List<Task> tasks = processEngine.getTaskService()
  8. .createTaskQuery()
  9. .list();
  10. for (Task task : tasks) {
  11. System.out.println(task.getName());
  12. }
  13. }

数据库情况:

程序执行后,输出内容:

请假申请
班主任
请假申请

(5)

  1. /**
  2. * 根据piid查询任务
  3. */
  4. @Test
  5. public void testQueryTaskByPIID(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. List<Task> tasks = processEngine.getTaskService()
  8. .createTaskQuery().executionId("1001")
  9. .list();
  10. for (Task task : tasks) {//因为没有并发,所以就有一个
  11. System.out.println(task.getName());
  12. }
  13. }

数据库情况:

程序执行后输出:

班主任

(6)

  1. /**
  2. * 根据piid得到当前正在执行的流程实例的正在活动的节点
  3. */
  4. @Test
  5. public void testActivity(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. /**
  8. * 根据piid得到流程实例
  9. */
  10. ProcessInstance pi = processEngine.getRuntimeService()
  11. .createProcessInstanceQuery()
  12. .processInstanceId("1001")
  13. .singleResult();
  14. String activityId = pi.getActivityId();//当前流程实例正在执行的activityId
  15. System.out.println(activityId);
  16. }

数据库情况:

程序执行后输出:

班主任

(7)查询历史执行的任务(这个包括当前在执行的和已经执行过的任务,可以通过Delete_Reason这个字段进行区别)

  1. /**
  2. * 查看已经完成的任务和当前在执行的任务
  3. */
  4. @Test
  5. public void findHistoryTask(){
  6. ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
  7. //如果只想获取到已经执行完成的,那么就要加入completed这个过滤条件
  8. List<HistoricTaskInstance> historicTaskInstances1 = defaultProcessEngine.getHistoryService()
  9. .createHistoricTaskInstanceQuery()
  10. .taskDeleteReason("completed")
  11. .list();
  12. //如果只想获取到已经执行完成的,那么就要加入completed这个过滤条件
  13. List<HistoricTaskInstance> historicTaskInstances2 = defaultProcessEngine.getHistoryService()
  14. .createHistoricTaskInstanceQuery()
  15. .list();
  16. System.out.println("执行完成的任务:" + historicTaskInstances1.size());
  17. System.out.println("所有的总任务数(执行完和当前未执行完):" +historicTaskInstances2.size());
  18. }

执行结果:

执行完成的任务:1
所有的总任务数(执行完和当前未执行完):3

六:关于ProcessDefinitionEntity(流程定义实体)的相关内容

上面的这个图就是对于一个流程的相关内容,这部分要特别注意,是非常重要的一个内容。

  1. package com.hnu.scw.activiti.activityimpl;
  2. import java.util.List;
  3. import org.activiti.engine.ProcessEngine;
  4. import org.activiti.engine.ProcessEngines;
  5. import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
  6. import org.activiti.engine.impl.pvm.PvmTransition;
  7. import org.activiti.engine.impl.pvm.process.ActivityImpl;
  8. import org.activiti.engine.runtime.ProcessInstance;
  9. import org.junit.Test;
  10.  
  11. public class ProcessDefinitionEntityTest {
  12. /**
  13. * 根据pdid得到processDefinitionEntity
  14. */
  15. @Test
  16. public void testProcessDefinitionEntity(){
  17. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  18. /**
  19. * 根据pdid得到ProcessDefinitionEntry
  20. */
  21. ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService()
  22. .getProcessDefinition("qingjia1:1:804");
  23.  
  24. }
  25.  
  26. /**
  27. * 根据pdid得到processDefinitionEntity中的activityimpl
  28. */
  29. @Test
  30. public void testGetActivityImpl(){
  31. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  32. /**
  33. * 根据pdid得到ProcessDefinitionEntry
  34. */
  35. ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService()
  36. .getProcessDefinition("qingjia1:1:804");
  37. /**
  38. * ActivityImpl是一个对象
  39. * 一个activityImpl代表processDefinitionEntity中的一个节点
  40. */
  41. List<ActivityImpl> activityImpls = processDefinitionEntity.getActivities();
  42. for (ActivityImpl activityImpl : activityImpls) {
  43. System.out.println(activityImpl.getId());
  44. System.out.print("hegiht:"+activityImpl.getHeight());
  45. System.out.print("width:"+activityImpl.getWidth());
  46. System.out.print(" x:"+activityImpl.getX());
  47. System.out.println(" y:"+activityImpl.getY());
  48. }
  49. }
  50.  
  51. /**
  52. * 得到ProcessDefinitionEntity中的所有的ActivityImpl的所有的PvmTransition
  53. */
  54. @Test
  55. public void testSequenceFlow(){
  56. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  57. /**
  58. * 根据pdid得到ProcessDefinitionEntry
  59. */
  60. ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService()
  61. .getProcessDefinition("qingjia1:1:804");
  62.  
  63. /**
  64. * ActivityImpl是一个对象
  65. * 一个activityImpl代表processDefinitionEntity中的一个节点
  66. */
  67. List<ActivityImpl> activityImpls = processDefinitionEntity.getActivities();
  68. for (ActivityImpl activityImpl : activityImpls) {
  69. /**
  70. * 得到一个activityimpl的所有的outgoing
  71. */
  72. List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();
  73. for (PvmTransition pvmTransition : pvmTransitions) {
  74. System.out.println("sequenceFlowId:"+pvmTransition.getId());
  75. }
  76. }
  77. }
  78.  
  79. /**
  80. * 得到当前正在执行的流程实例的activityimpl-->PvmTransition
  81. */
  82. @Test
  83. public void testQueryActivityImpl_Ing(){
  84. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  85. ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService()
  86. .getProcessDefinition("qingjia:1:504");
  87. //根据piid获取到activityId
  88. ProcessInstance pi = processEngine.getRuntimeService()
  89. .createProcessInstanceQuery()
  90. .processInstanceId("1001")
  91. .singleResult();
  92. //根据流程实例得到当前正在执行的流程实例的正在执行的节点
  93. ActivityImpl activityImpl = processDefinitionEntity.findActivity(pi.getActivityId());
  94. System.out.print("流程实例ID:"+pi.getId());
  95. System.out.print(" 当前正在执行的节点:"+activityImpl.getId());
  96. System.out.print(" hegiht:"+activityImpl.getHeight());
  97. System.out.print(" width:"+activityImpl.getWidth());
  98. System.out.print(" x:"+activityImpl.getX());
  99. System.out.println(" y:"+activityImpl.getY());
  100. }
  101. }

七:通过当前系统登陆用户,获取到的相关Activiti内容

备注:上面这个流程图,一定要根据上面的知识点来进行推到学习,一定要弄清楚每个内容对应的属性和对象指的是什么,因为这个在实际的开发项目中,是非常有用,并且是经常进行使用的内容。

上面图所对应的代码如下:

  1. package com.hnu.scw.activiti.utils;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.activiti.engine.ProcessEngine;
  5. import org.activiti.engine.ProcessEngines;
  6. import org.activiti.engine.repository.ProcessDefinition;
  7. import org.activiti.engine.runtime.ProcessInstance;
  8. import org.activiti.engine.task.Task;
  9. public class ActivitiUtils {
  10. /**
  11. * 当前用户-->当前用户正在执行的任务--->当前正在执行的任务的piid-->该任务所在的流程实例
  12. * @param assignee
  13. * @return
  14. */
  15. public static List<ProcessInstance> getPIByUser(String assignee){
  16. List<ProcessInstance> pis = new ArrayList<ProcessInstance>();
  17. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  18. /**
  19. * 该用户正在执行的任务
  20. */
  21. List<Task> tasks = processEngine.getTaskService()
  22. .createTaskQuery()
  23. .taskAssignee(assignee)
  24. .list();
  25. for (Task task : tasks) {
  26. /**
  27. * 根据task-->piid-->pi
  28. */
  29. String piid = task.getProcessInstanceId();
  30. ProcessInstance pi = processEngine.getRuntimeService()
  31. .createProcessInstanceQuery()
  32. .processInstanceId(piid)
  33. .singleResult();
  34. pis.add(pi);
  35. }
  36. return pis;
  37. }
  38.  
  39. /**
  40. * 根据当前的登录人能够推导出所在的流程定义
  41. */
  42. public static void getProcessInstance(String assignee){
  43. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  44. List<Task> tasks = processEngine.getTaskService()
  45. .createTaskQuery()
  46. .taskAssignee(assignee)
  47. .list();
  48. for (Task task : tasks) {
  49. String pdid = task.getProcessDefinitionId();
  50. ProcessDefinition processDefinition = processEngine.getRepositoryService()
  51. .createProcessDefinitionQuery()
  52. .processDefinitionId(pdid)
  53. .singleResult();
  54. }
  55. }
  56. }

八:详细解析Task任务(非常重要)

任务的概念:需要有人进行审批或者申请的为任务

任务的执行人的情况类型:

情况一:当没有进入该节点之前,就可以确定任务的执行人
  实例:比如进行“请假申请”的流程时候,最开始执行的就是提交”请假申请“,那么就需要知道,谁提交的“请假”,很明显,在一个系统中,谁登陆到系统里面,谁就有提交“请假任务”的提交人,那么执行人就可以确定就是登录人。
情况二:有可能一个任务节点的执行人是固定的。

实例:比如,在“公司财务报账”的流程中,最后一个审批的人,一定是财务部的最大的BOSS,所以,这样该流程的最后一个节点执行人,是不是就已经确定是为“财务部最大BOSS”了。

情况三:一个节点任务,之前是不存在执行人(未知),只有当符合身份的人,登陆系统,进入该系统,才能确定执行人。

实例:比如,如果当前的流程实例正在执行“自荐信审批”,这个时候,自荐信审批没有任务执行人,因为审批人是可以很多个,无法确定到底是谁,只有当咨询员登录系统以后才能给该任务赋值执行人,即存在只要是咨询员登陆,那么就可以看到所有的“自荐信”。

情况四:一个任务节点有n多人能够执行该任务,但是只要有一个人执行完毕就完成该任务了:组任务

实例:比如,“进入地铁站通道”的流程,我们一般地铁都是有N个安全检查的入口,有很多个人在进行检查,那么我们要想通过检查,那么任意一个检察员只要通过即可。

详细分析:

针对情况一:

步骤:

(1)首先构建流程图:(注意区别上面的第一次画的内容)

(2)将bpmn的内容,生成一个png的图片(这个操作方法,上面都已经很详细了,不多说)

(3)代码实现步骤:

1:

  1. /**
  2. * 部署流程
  3. */
  4. @Test
  5. public void startDeployTest(){
  6. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  7. processEngine.getRepositoryService()
  8. .createDeployment()
  9. .name("请假流程:情况一")
  10. .addClasspathResource("com/hnu/scw/task/shenqing.bpmn")
  11. .deploy();
  12. }

数据库情况:

2:

  1. /**
  2. * 启动流程实例
  3. * 可以设置一个流程变量
  4. */
  5. @Test
  6. public void testStartPI(){
  7. /**
  8. * 流程变量
  9. * 给<userTask id="请假申请" name="请假申请" activiti:assignee="#{student}"></userTask>
  10. * 的student赋值
  11. */
  12. Map<String, Object> variables = new HashMap<String, Object>();
  13. variables.put("student", "小明");
  14. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  15. processEngine.getRuntimeService()
  16. .startProcessInstanceById("shenqing1:1:1304",variables);
  17. }

数据库情况:

分析:如果,我们安装下面的代码执行,那么就出出现如下的错误

  1. /**
  2. * 启动流程实例
  3. * 可以设置一个流程变量
  4. */
  5. @Test
  6. public void testStartPI(){
  7. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  8. processEngine.getRuntimeService()
  9. .startProcessInstanceById("shenqing1:1:1304");
  10. }

原因:是否还记得,我们在画流程图的时候,对该请假申请的节点,分配了一个#{student},这个变量,这个其实含义就是说,当我们进行该节点的处理的时候,就需要分配一个执行人,如果没有分配,就会发生上面的错误。然后再回头想一下,是不是就是我们的第一种情况呢?因为,在进行请假的流程的执行开始的时候,其实申请人是已经可以确定了,就是登陆的用户。

3:后面的代码如下:

  1. /**
  2. * 在完成请假申请的任务的时候,给班主任审批的节点赋值任务的执行人
  3. */
  4. @Test
  5. public void testFinishTask_Teacher(){
  6. Map<String, Object> variables = new HashMap<String, Object>();
  7. variables.put("teacher", "我是小明的班主任");
  8. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  9. processEngine.getTaskService()
  10. .complete("1405", variables); //完成任务的同时设置流程变量
  11. }
  12.  
  13. /**
  14. * 在完成班主任审批的情况下,给教务处节点赋值
  15. */
  16. @Test
  17. public void testFinishTask_Manager(){
  18. Map<String, Object> variables = new HashMap<String, Object>();
  19. variables.put("manager", "我是小明的教务处处长");
  20. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  21. processEngine.getTaskService()
  22. .complete("1603", variables); //完成任务的同时设置流程变量
  23. }
  24.  
  25. /**
  26. * 结束流程实例
  27. */
  28. @Test
  29. public void testFinishTask(){
  30. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  31. processEngine.getTaskService()
  32. .complete("1703");
  33. }

总结:针对情况一,那么我们必须要进入该节点执行前,就要分配一个执行人。

情况二:这个情况的话,这里不多介绍,因为之前的知识点中,都是在画流程图的时候就已经分配这个执行人了。可以回头去看看。
情况三:

步骤:(1)画流程图,这里不多介绍,就说一下需要修改的地方。

(2)编写的TaskListener监听类

  1. package com.hnu.scw.tasklistener;
  2. import org.activiti.engine.delegate.DelegateTask;
  3. import org.activiti.engine.delegate.TaskListener;
  4. /**
  5. * @author Administrator
  6. * @create 2018-01-16 11:10
  7. * @desc tack任务的监听,主要是为了动态分配执行人
  8. **/
  9. public class MyTaskListener implements TaskListener {
  10. @Override
  11. public void notify(DelegateTask delegateTask) {
  12. /**
  13. * 任务的执行人可以动态的赋值
  14. * 1、流程变量
  15. * 可以通过提取流程变量的方式给任务赋值执行人
  16. * 2、可以操作数据库
  17. * 方法一:(必须在web环境) WebApplicationContext ac = WebApplicationContextUtils
  18. * .getWebApplicationContext(ServletActionContext.getServletContext());
  19. xxxxService xxxxService = (xxxxService) ac.getBean("xxxxService");
  20. 方法二:通过JDBC来进行数据库操作
  21. */
  22. //动态分配(这里是从上一节点中的tack变量的map中获取,只有流程没有结束,所有的变量都是可以获取)
  23. /*String value = (String)delegateTask.getVariable("aaa");
  24. delegateTask.setAssignee(value);*/
  25. //静态分配(用于确定该执行人就只有一种情况,是一种固定的)
  26. delegateTask.setAssignee("我是班主任");
  27. }
  28. }

通过这样的方式的话,当有“请假申请”进行提交之后,“班主任”的这个节点,就会自动进行分配执行人。

情况四:

流程图如下:

具体的测试代码:(注意看写的注释内容,就明白了对应的数据库的什么表)

  1. package com.hnu.scw.test;
  2. import org.activiti.engine.ProcessEngine;
  3. import org.activiti.engine.ProcessEngines;
  4. import org.activiti.engine.task.IdentityLink;
  5. import org.activiti.engine.task.Task;
  6. import org.junit.Test;
  7. import java.util.List;
  8.  
  9. /**
  10. * @author scw
  11. * @create 2018-01-23 15:45
  12. * @desc 关于对于组任务的测试内容
  13. **/
  14. public class GroupTaskTest {
  15. /**
  16. * 主要是对于某些任务流程中,有N个人,但是只需要其中的某一个通过,
  17. * 则该任务就通过了,所以针对这样的业务需求,就有如下的内容
  18. */
  19. @Test
  20. public void deployTashTest(){
  21. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  22. processEngine.getRepositoryService()
  23. .createDeployment()
  24. .addClasspathResource("com/hnu/scw/test/task3.bpmn")
  25. .addClasspathResource("com/hnu/scw/test/task3.png")
  26. .name("组任务的测试")
  27. .deploy();
  28. }
  29. /**
  30. * 当启动完流程实例以后,进入了"电脑维修"节点,该节点是一个组任务
  31. * 这个时候,组任务的候选人就会被插入到两张表中
  32. * act_ru_identitylink 存放的是当前正在执行的组任务的候选人
  33. * act_hi_identitylink
  34. */
  35. @Test
  36. public void processTaskStartTest(){
  37. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  38. processEngine.getRuntimeService()
  39. .startProcessInstanceByKey("task3");
  40. }
  41. /**
  42. * 对于act_hi_identitylink表,根据任务ID,即TASK_ID字段查询候选人
  43. */
  44. @Test
  45. public void testQueryCandidateByTaskId(){
  46. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  47. List<IdentityLink> identityLinks = processEngine.getTaskService()
  48. .getIdentityLinksForTask("2104");
  49. for (IdentityLink identityLink : identityLinks) {
  50. System.out.println(identityLink.getUserId());
  51. }
  52. }
  53.  
  54. /**
  55. * 对于act_hi_identitylink表,根据候选人,即USER_ID_查看组任务
  56. */
  57. @Test
  58. public void testQueryTaskByCandidate(){
  59. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  60. List<Task> tasks = processEngine.getTaskService()
  61. .createTaskQuery()
  62. .taskCandidateUser("工程师1")
  63. .list();
  64. for (Task task : tasks) {
  65. System.out.println(task.getName());
  66. }
  67. }
  68. /**
  69. * 候选人中的其中一个人认领任务
  70. */
  71. @Test
  72. public void testClaimTask(){
  73. ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  74. processEngine.getTaskService()
  75. /**
  76. * 第一个参数为taskId
  77. * 第二个参数为认领人
  78. */
  79. .claim("2104", "工程师2");
  80. }
  81.  
  82. }

九:实际项目中的关于Activiti的工具类方法封装

  1. package com.hnu.scw.activiti.utils;
  2. import org.activiti.engine.ProcessEngine;
  3. import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
  4. import org.activiti.engine.impl.pvm.PvmActivity;
  5. import org.activiti.engine.impl.pvm.PvmTransition;
  6. import org.activiti.engine.impl.pvm.process.ActivityImpl;
  7. import org.activiti.engine.repository.Deployment;
  8. import org.activiti.engine.repository.ProcessDefinition;
  9. import org.activiti.engine.runtime.ProcessInstance;
  10. import org.activiti.engine.task.Task;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Component;
  13.  
  14. import javax.annotation.Resource;
  15. import java.io.File;
  16. import java.io.FileInputStream;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.zip.ZipInputStream;
  23.  
  24. /**
  25. * @author scw
  26. * @create 2018-01-24 9:51
  27. * @desc 针对流程管理的工具类
  28. **/
  29. @Component("activitiUtils")
  30. public class ActivitiUtils {
  31. @Resource(name = "processEngine")
  32. private ProcessEngine processEngine;
  33.  
  34. /**
  35. * 部署流程
  36. * @param file 流程的zip文件
  37. * @param processName 流程的名字
  38. * @throws IOException
  39. */
  40. public void deployeProcess(File file , String processName)throws IOException{
  41. InputStream inputStream = new FileInputStream(file);
  42. ZipInputStream zipInputStream = new ZipInputStream(inputStream);
  43. this.processEngine.getRepositoryService()
  44. .createDeployment()
  45. .name(processName)
  46. .addZipInputStream(zipInputStream)
  47. .deploy();
  48. }
  49.  
  50. /**
  51. * 通过字节流来进行部署流程
  52. * @param io
  53. * @param processName
  54. */
  55. public void deplyoProcessByInputSterm(InputStream io , String processName){
  56. ZipInputStream zipInputStream = new ZipInputStream(io);
  57. this.processEngine.getRepositoryService()
  58. .createDeployment()
  59. .name(processName)
  60. .addZipInputStream(zipInputStream)
  61. .deploy();
  62. }
  63.  
  64.  
  65. /**
  66. * 查询所有的部署流程
  67. * @return
  68. */
  69. public List<Deployment> getAllDeplyoment(){
  70. return this.processEngine.getRepositoryService()
  71. .createDeploymentQuery()
  72. .orderByDeploymenTime()
  73. .desc()
  74. .list();
  75. }
  76. /**
  77. * 查询所有的部署定义信息
  78. * @return
  79. */
  80. public List<ProcessDefinition> getAllProcessInstance(){
  81. return this.processEngine.getRepositoryService()
  82. .createProcessDefinitionQuery()
  83. .orderByProcessDefinitionVersion()
  84. .desc()
  85. .list();
  86. }
  87.  
  88. /**
  89. * 根据部署ID,来删除部署
  90. * @param deplyomenId
  91. */
  92. public void deleteDeplyomentByPID(String deplyomenId){
  93. this.processEngine.getRepositoryService()
  94. .deleteDeployment(deplyomenId , true);
  95. }
  96.  
  97. /**
  98. * 查询某个部署流程的流程图
  99. * @param pid
  100. * @return
  101. */
  102. public InputStream lookProcessPicture(String pid){
  103. return this.processEngine.getRepositoryService()
  104. .getProcessDiagram(pid);
  105. }
  106.  
  107. /**
  108. * 开启请假的流程实例
  109. * @param billId
  110. * @param userId
  111. */
  112. public void startProceesInstance(Long billId , String userId){
  113. Map<String , Object> variables = new HashMap<>();
  114. variables.put("userID" , userId);
  115. this.processEngine.getRuntimeService()
  116. .startProcessInstanceByKey("shenqingtest" , ""+billId , variables); //第一个参数,就是流程中自己定义的名字,这个一定要匹配,否则是找不到的。
  117. }
  118.  
  119. /**
  120. * 查询当前登陆人的所有任务
  121. * @param userId
  122. * @return
  123. */
  124. public List<Task> queryCurretUserTaskByAssignerr(String userId){
  125. return this.processEngine.getTaskService()
  126. .createTaskQuery()
  127. .taskAssignee(userId)
  128. .orderByTaskCreateTime()
  129. .desc()
  130. .list();
  131. }
  132.  
  133. /**
  134. * 根据TaskId,获取到当前的执行节点实例对象
  135. * @param taskId
  136. * @return
  137. */
  138. public ActivityImpl getActivityImplByTaskId(String taskId){
  139. //首先得到任务
  140. Task task = this.getTaskByTaskId(taskId);
  141. //其次,得到流程实例
  142. ProcessInstance processInstance = this.getProcessInstanceByTask(task);
  143. //再次,根据流程实例来获取到流程定义
  144. ProcessDefinitionEntity processDefinitionEntity = this.getProcessDefinitionEntityByTask(task);
  145. //再根据,流程定义,通过流程实例中来获取到activiti的ID,从而得到acitviImp
  146. ActivityImpl activity = processDefinitionEntity.findActivity(processInstance.getActivityId());
  147. return activity;
  148. }
  149.  
  150. /**
  151. * 根据taskId,判断对应的流程实例是否结束
  152. * 如果结束了,那么得到的流程实例就是返回一个null
  153. * 否则就是返回对应的流程实例对象
  154. * 当然也可以选择返回boolean类型的
  155. * @param taskId 任务ID
  156. * @return
  157. */
  158. public ProcessInstance isFinishProcessInstancs(String taskId){
  159. //1,先根据taskid,得到任务
  160. Task task = getTaskByTaskId(taskId);
  161. //2:完成当前任务
  162. finishCurrentTaskByTaskId(taskId);
  163. //3:得到当前任务对应得的流程实例对象
  164. ProcessInstance processInstance = getProcessInstanceByTask(task);
  165. return processInstance;
  166. }
  167.  
  168. /**
  169. * 获取当前执行节点的所有出口
  170. * @param activity
  171. * @return
  172. */
  173. public List<PvmTransition> getCurrentActivitiImplPvm(ActivityImpl activity){
  174. List<PvmTransition> outgoingTransitions = activity.getOutgoingTransitions();
  175. return outgoingTransitions;
  176. }
  177.  
  178. /**
  179. * 根据taskId获取到task
  180. * @param taskId
  181. * @return
  182. */
  183. public Task getTaskByTaskId(String taskId) {
  184. //得到当前的任务
  185. Task task = this.processEngine.getTaskService()
  186. .createTaskQuery()
  187. .taskId(taskId)
  188. .singleResult();
  189. return task;
  190. }
  191.  
  192. /**
  193. * 根据Task中的流程实例的ID,来获取对应的流程实例
  194. * @param task 流程中的任务
  195. * @return
  196. */
  197. public ProcessInstance getProcessInstanceByTask(Task task) {
  198. //得到当前任务的流程
  199. ProcessInstance processInstance = this.processEngine.getRuntimeService()
  200. .createProcessInstanceQuery()
  201. .processInstanceId(task.getProcessInstanceId())
  202. .singleResult();
  203. return processInstance;
  204. }
  205.  
  206. /**
  207. * 根据Task来获取对应的流程定义信息
  208. * @param task
  209. * @return
  210. */
  211. public ProcessDefinitionEntity getProcessDefinitionEntityByTask(Task task){
  212. ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) this.processEngine.getRepositoryService()
  213. .getProcessDefinition(task.getProcessDefinitionId());
  214. return processDefinitionEntity;
  215. }
  216.  
  217. /**
  218. * 根据taskId获取到businesskey,这个值是管理activiti表和自己流程业务表的关键之处
  219. * @param taskId 任务的ID
  220. * @return
  221. */
  222. public String getBusinessKeyByTaskId(String taskId){
  223. Task task = this.getTaskByTaskId(taskId);
  224. ProcessInstance processInstance = this.getProcessInstanceByTask(task);
  225. //返回值
  226. return processInstance.getBusinessKey();
  227. }
  228.  
  229. /**
  230. * 根据taskId,完成任务
  231. * @param taskId
  232. */
  233. public void finishCurrentTaskByTaskId(String taskId){
  234. this.processEngine.getTaskService().complete(taskId);
  235. }
  236.  
  237. /**
  238. * 完成任务的同时,进行下一个节点的审批人员的信息的传递
  239. * @param taskId
  240. * @param object
  241. */
  242. public void finishCurrentTaskByTaskId(String taskId , Object object){
  243. Map<String , Object> map = new HashMap<>();
  244. map.put("assigeUser" , object);
  245. this.processEngine.getTaskService().complete(taskId , map);
  246. }
  247. }

OK,这上面就是关于对于控制流的一些介绍了。对于这个,我们在很多的流程管理项目中都是有很好的借鉴意义的。当然,对于Actitivi这个框架,提供的只是部分的API接口,而且当我们刚开始接触的时候,对于里面内部存在的表的关系还会很模糊,但是,只要慢慢的熟悉了,这个就能够很好的理解了。另外的话,还说一个知识点。

附加知识点:

问题:Activiti里面本身自带有很多的数据表,它里面都是存在着关联关系,那么如何将其本身的表与我们的实际业务中的表进行关联呢?

解惑:其实,这个对于Activiti早已经想到这个问题,就是通过act_ru_exectution这个表中的business_key这个字段来进行关联。

实例分析:比如,针对上面的请假流程,那么,我们肯定在自己的业务中,就需要一张请假的信息表,比如,里面就包含,请假原因,请假人,请假时间等等基本请假信息。然后,我们其他的业务,也会根据这张表的内容,进行不断的扩充,比如,还需要记录对每条请假信息,每个审批节点中每个人的具体描述信息,那么这样就出现了一张“请假审批详细表”,很明显,这两张表就是通过“请假表中的主键ID”来进行关联的,那么就作为“请假详情表”中的外键。。。那么,同理,我们也是一样的,我们就通过对于act_ru_exectution这个数据表的business_key字段来关联着我们的业务主键即可。所以,这样就把我们自身的业务和Activiti进行了关联。如下图:

附加知识点2:Activiti工作流的自带数据表的含义

(1)资源库流程规则表
1)act_re_deployment 部署信息表
2)act_re_model  流程设计模型部署表
3)act_re_procdef  流程定义数据表
(2):运行时数据库表
1)act_ru_execution 运行时流程执行实例表
2)act_ru_identitylink 运行时流程人员表,主要存储任务节点与参与者的相关信息
3)act_ru_task 运行时任务节点表
4)act_ru_variable 运行时流程变量数据表
(3):历史数据库表
1)act_hi_actinst 历史节点表
2)act_hi_attachment 历史附件表
3)act_hi_comment 历史意见表
4)act_hi_identitylink 历史流程人员表
5)act_hi_detail 历史详情表,提供历史变量的查询
6)act_hi_procinst 历史流程实例表
7)act_hi_taskinst 历史任务实例表
8)act_hi_varinst 历史变量表
(4):组织机构表
1)act_id_group 用户组信息表
2)act_id_info 用户扩展信息表
3)act_id_membership 用户与用户组对应信息表
4)act_id_user 用户信息表
这四张表很常见,基本的组织机构管理,关于用户认证方面建议还是自己开发一套,组件自带的功能太简单,使用中有很多需求难以满足 
(5):通用数据表
1)act_ge_bytearray 二进制数据表
2)act_ge_property 属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录,

附加知识点3:完整的一个Activiti工作流的项目代码。

环境:IDEA+ SpringMvc+Spring+Hibernate  +Mysql

获取方式:留言即可。我会随时浏览信息的。。。。

github地址:git@github.com:qq496616246/ActivitiCode.git

或者https://github.com/qq496616246/ActivitiCode.git

手把手教你如何玩转Activiti工作流的更多相关文章

  1. 转:手把手教你如何玩转Solr(包含项目实战)

    原文地址:手把手教你如何玩转Solr(包含项目实战) 参考原文

  2. 手把手教你如何玩转消息中间件(ActiveMQ)

    手把手教你如何玩转消息中间件(ActiveMQ) 2018年07月15日 18:07:39 Cs_hnu_scw 阅读数 12270 标签: 中间件消息中间件ActiveMQ分布式集群 更多 个人分类 ...

  3. 手把手教你如何玩转Solr(包含项目实战)

    一:Solr简介       Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引 ...

  4. 手把手教你如何玩转消息中间件(ActiveMQ) https://blog.csdn.net/cs_hnu_scw/article/details/81040834

    #情景引入小白:起床起床起床起床....快起床~我:怎么了又,大惊小怪,吓到我了.小白:我有事有事想找你,十万火急呢~~我:你能有什么事?反正我不信..那你说说看~~小白:就是我有两个小表弟,叫大白和 ...

  5. 手把手教你如何玩转CLion

    声明:配置是基于CLion的2019.1版本 〇.CLion简介 一.安装 \(JetBrains\)官方下载地址:CLion2019.3 百度网盘:CLion2019.1 个人觉得还是2019.1版 ...

  6. 手把手教你玩转SOCKET模型之重叠I/O篇(下)

    四.     实现重叠模型的步骤 作 了这么多的准备工作,费了这么多的笔墨,我们终于可以开始着手编码了.其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,但是只是学会 如何来使用它,却 ...

  7. 转:变手把手教你玩转SOCKET模型之重叠I/O篇

    手把手教你玩转SOCKET模型之重叠I/O篇 “身为一个初学者,时常能体味到初学者入门的艰辛,所以总是想抽空作点什么来尽我所能的帮助那些需要帮助的人.我也希望大家能把自己的所学和他人一起分享,不要去鄙 ...

  8. 手把手教你玩转 CSS3 3D 技术

    css3的3d起步 要玩转css3的3d,就必须了解几个词汇,便是透视(perspective).旋转(rotate)和移动(translate).透视即是以现实的视角来看屏幕上的2D事物,从而展现3 ...

  9. 手把手教你玩转CSS3 3D技术

    手把手教你玩转 CSS3 3D 技术   要玩转css3的3d,就必须了解几个词汇,便是透视(perspective).旋转(rotate)和移动(translate).透视即是以现实的视角来看屏幕上 ...

随机推荐

  1. MAC终端安装指定版本node

    MAC终端安装指定版本node 安装brew 终端上运行 $ /usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Home ...

  2. Docker运行简单的Demo

    打开cmd.exe 输入docker run hello-world,本机没有这个images实例,将会从官方下载下载 运行一个简单的web实例,例如输入: docker run --name asp ...

  3. Teaching Machines to Understand Us 让机器理解我们 之三 自然语言学习及深度学习的信仰

    Language learning 自然语言学习 Facebook’s New York office is a three-minute stroll up Broadway from LeCun’ ...

  4. 第五章—if语句

    5-1 条件测试 :编写一系列条件测试:将每个测试以及你对其结果的预测和实际结果都打印出来.你编写的代码应类似于下面这样: car = 'subaru' print("Is car == ' ...

  5. We are writing to let you know we have removed your selling privileges

     Hello, We are writing to let you know we have removed your selling privileges, canceled your listin ...

  6. python单元测试之参数化

    paramunittest下载地址:https://pypi.python.org/pypi/ParamUnittest/ 当然我们也可以通过pip install paramunittest方式进行 ...

  7. js 零零散散的总结。

    Array.slice.call(arguments);可以将一个类数组转化为数组. Array.from() ,[...arr];也可以将一个类数组转化为数组(es6). (function() { ...

  8. 提升Android ListView性能的几个技巧

    ListView如何运作的? ListView是设计应用于对可扩展性和高性能要求的地方.实际上,这就意味着ListView有以下2个要求: 尽可能少的创建View: 只是绘制和布局在屏幕上可见的子Vi ...

  9. border、margin、padding三者的区别

    当你写前端的时候,肯定会遇到border,margin和padding这几个单词. 如: padding: 16px 0; margin: 0 10px; 在CSS中,他们是表示距离的东西,很多人刚开 ...

  10. asp.net登录状态验证

    文章:ASP.NET 登录验证 文章:ASP.NET MVC下判断用户登录和授权状态方法 文章:.net学习笔记---HttpHandle与HttpModule 第一篇文章,介绍了 1)早期的Base ...