Activiti7进阶

一.1. 流程定义管理

查询流程定义(testQueryProcessDefinition)查询流程定义(testQueryProcessDefinition)

(查看Activiti7中部署了多少个流程,有多少个规则)

repositoryService.createProcessDefinitionQuery().list();

查 act_re_procdef 表,能看到当前系统部署了哪些流程规则。
输出流程定义ID、名称、部署ID。流程定义ID 格式是 流程key:版本号:部署ID。

删除流程定义(deleteProcessDefinition)

repositoryService.deleteDeployment("1", true);

1.1 在刚部署的前提下,可以直接删除
1.2 添加开启流程后,有正在运行的流程实例后,直接删不好使,MySQL由于外键原因,需要添加 true ,会连带删除其下所有流程定义和资源。
第一个参数为:部署id,第二个参数 true:关键。如果该流程定义下还有正在跑的流程实例,不传 true 会抛异常;传 true 就级联删除实例。
生产环境慎用 true。

下载流程资源(testDownloadFile)

ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
    .processDefinitionKey("leaveProcess")
    .latestVersion()       // 只要最新版本
    .singleResult();

InputStream in = repositoryService.getResourceAsStream(deploymentId, resourceName);

image

实现思路

- 首先查询到对应的流程定义对象。
- 根据流程定义对象获取到对应的 -> 部署ID deploymentId + 文件名。
- 我们就可以根据 Activiti7 API 来获取到对应的资源二进制流。
- 我们可以通过输出流保存到本地磁盘中
业务场景:前端要在线看流程图、要下载 bpmn 文件,就从 Activiti 里把二进制流读出来。

补充: 流程定义版本问题

1. 这里就是说若我们上传了相同 id 的流程,会出现什么问题?
2. 例如:
- A 的 bpmn 文件。 id=leaveProcess ==> 先部署。
- B 的 bpmn 文件。 id=leaveProcess ==> 后部署。
3. 会出现什么问题?
- 不会覆盖,不会抛异常。会存在 Key 相同的两条记录。但是会存在不同 Version 版本号。后部署的版本号大。
- 根据 Key 启动时,会默认按照版本号大的启动。
4. 若 version = 1 的流程已经有在执行的流程实例了,该流程实例还没有走完。此时再次部署了 Version = 2 的流程定义。
- 老的没跑完的流程实例按照哪个版本执行? ==> 依然按照老的版本执行。
- 新发起的流程实例按照哪个版本执行? ==> 按照新版本执行。

2. BusinessKey —— 业务关联(重要!)

Activiti 自己不管你的业务表,它怎么知道这次流程对应哪条请假单?靠 businessKey。
image

理解:流程实例 = Activiti 内部的事;businessKey = 一座桥,桥接到你的业务数据。没有它,Activiti 流程就和企业数据割裂了。

3. 挂起与激活(三个层次,别混淆)

Activiti 的挂起分三个粒度,由粗到细:

(1) 只挂起流程定义(testSuspendProcessDefinition)
repositoryService.suspendProcessDefinitionById(pd.getId());
repositoryService.activateProcessDefinitionById(pd.getId());

效果:规则被冻结,不能发起新流程实例,但已经跑起来的老实例继续执行。

(2) 挂起流程定义 + 级联挂起其实例(2) 挂起流程定义 + 级联挂起其实例(testSuspendProcessDefinitionAndProcessInstance)
repositoryService.suspendProcessDefinitionById(pd.getId(), true, null);
//                                                    		 ↑  ↑
//                                            		是否挂起实例  挂起时间(null=立即)

第二个参数传 true,已经发起的实例也一并冻结。

(3) 单独挂起某个流程实例(3) 单独挂起某个流程实例(testSuspendProcessInstance)
runtimeService.suspendProcessInstanceById(processInstanceId);
runtimeService.activateProcessInstanceById(processInstanceId);

粒度最细,只控制"某一次具体的请假",不影响规则本身和其他实例。
判断状态统一用 isSuspended() 返回 boolean。挂起的实例如果再 complete 任务,会抛异常。

4. 候选人机制(多个候选人共享一个任务)

之前用 assignee(固定负责人),但有些任务"谁都可以做",比如客服组。这就用 candidate。

启动时绑定变量(testStartProcessBindAssignee

Map<String,Object> params = new HashMap<>();
params.put("assignee0","小张");
params.put("assignee1","小明");
runtimeService.startProcessInstanceByKey("leaveProcess", params);

这里把负责人作为流程变量传进去,BPMN 里通过 ${assignee0} 这种 EL 表达式取值。
好处:负责人不写死在 BPMN 里,每次启动可以不同。
按候选人查询(testQueryTaskByCandidate)

taskService.createTaskQuery()
    .taskCandidateUser("小明")   // 注意不是 taskAssignee
    .list();

候选人任务此时没有 assignee(assignee 为 null),任何候选人都能查到。
认领(claim)后才能完成(claimTask)

taskService.claim(taskId, "张三");   // 张三把任务认领过来 → assignee 变成"张三"
taskService.complete(taskId);

候选任务的执行流程:候选 → 认领 → 完成,必须先 claim 才能 complete。
认领后这个任务就只属于认领人了,其他候选人看不到了。这就是"抢单"模式。

5. 流程变量 + 网关(连线上的变量)

testStartProcessVariables
params.put("assignee0","张飞");
params.put("assignee1","李逵");
params.put("assignee2","张三丰");
params.put("day",10);                          // ← 关键:连线条件变量
runtimeService.startProcessInstanceByKey("leaveProcess", businessKey, params);

day=10 是给排他网关(Exclusive Gateway)用的。BPMN 里的连线会写 ${day>3} 这种条件,引擎根据 day 的值决定走哪条线。
部署的流程文件是 leaveProcessIGateway.bpmn20.xml(IGateway = Inclusive/Exclusive Gateway)。
理解:流程变量是流程的"参数包",既可决定负责人,又可决定分支走向。它是 Activiti 的"动态能力"所在。

四、整体串起来:一次完整请假的全生命周期

把两个 Demo 串起来,一次请假是这样走的:把两个 Demo 串起来,一次请假是这样走的:

1. 管理员部署规则 testDeploy() → act_re_deployment/procdef
2. (可选)挂起规则 testSuspendProcessDefinition()
3. 员工发起请假 testStartProcess()+businessKey → act_ru_execution/task(我是表)
同时传流程变量 assignee0/1/2 + day
4. 网关根据 day 决定走哪条线 (引擎内部)
5. 候选人查询任务 testQueryTaskByCandidate()
6. 某候选人认领 claimTask()
7. 加批注 + 审批通过 testAddComment()
8. 流转到下一节点 (引擎自动)
9. ... 重复 5~8 ...
10. 流程结束 act_ru_* 数据清空,全部进 act_hi_*
11. 审计查询历史 testQueryHistoryTask() → act_hi_taskinst/comment
**12. (可选)下载流程图 ** testDownloadFile()
13. (可选)删除部署 deleteProcessDefinition()

五、一句话总结每个 Service 能干什么

RepositoryService:管"规则"——部署、查询、删除、下载、挂起流程定义。
RuntimeService:管"运行中的实例"——启动、绑定 businessKey、传流程变量、挂起/激活实例。
TaskService:管"人做的事"——查待办、complete、加批注、claim 认领、查批注。
HistoryService:管"过去的事"——历史任务、历史实例、耗时统计。

六、几个容易踩的坑(你以后会遇到的)

删除部署时第二个参数:没在跑的实例 → 默认 false 即可;有在跑的 → 必须传 true 才能级联删,否则抛异常。
candidate 任务必须先 claim 再 complete:直接 complete 候选任务会失败。
流程变量 key 必须和 BPMN 里的 ${xxx} 一致:写错就报"Unknown property used in expression"。
挂起状态下不能 complete 任务:会抛 ActivitiException,必须先激活。
businessKey 是 String 类型:即使你存的是 Long,引擎也是按字符串存的,取出来要自己转型。
同一流程定义 key 重复部署会产生新版本:版本号自增,老版本默认不会被覆盖。
单独流程定义的挂起和激活。

挂起流程定义则不让发起新的流程实例。未完成的任务依然可以执行。挂起流程定义则不让发起新的流程实例。未完成的任务依然可以执行。

例如公司制度改变过程中的流程, 总经理更换,有100个人的流程审批, 70个人已经完成审批,30个人还未完成。老的总经理需要将剩余的30审批都结束才可以进行交接。

此时我们需要挂起流程定义,不让发起新的流程实例。

// 当前流程定义为激活状态,我们需要对流程定义挂起

repositoryService.suspendProcessDefinitionById(processDefinition.getId());
System.out.println("当前流程定义已被挂起");

等待老的总经理将30个流程都审批结束后。我们激活流程。可以继续发起新的流程实例

// 当前流程定义为挂起状态,我们需要对流程定义激活

repositoryService.activateProcessDefinitionById(processDefinition.getId());
System.out.println("当前流程定义已被激活");

流程定义及对应流程实例的挂起和激活。

挂起流程定义及对应的流程实例。则说明不让发起新的任务,已经在执行的任务也不能在继续执行了。

【开始节点】-->【A节点】-->【B节点】-->【C节点】-->【结束节点】

【C节点】的业务逻辑需要和外部接口交互,刚好外部接口出问题了,如果剩下的流程都走到【C节点】,执行【C节点】的业务逻辑,那都会报错。

此时我们需要挂起流程定义及对应的流程实例。

// 参数1:流程定义的Id 参数2:是否挂起对应的流程实例 参数3:指定时间挂起,null 为立即挂起

repositoryService.suspendProcessDefinitionById(processDefinition.getId(),true,null);
System.out.println("当前流程定义及对应的流程实例已被挂起");

等待 C 节点修复后,在激活流程定义及对应的流程实例。

// 当前流程定义及对应的流程实例为挂起状态,我们需要对流程定义及对应的流程实例激活

repositoryService.activateProcessDefinitionById(processDefinition.getId(),true,null);
System.out.println("当前流程定义及对应的流程实例已被激活");

单独流程实例的挂起和激活。

挂起流程实例。则不让该实例对应的所有任务执行了。但其他实例依然可以继续执行。

评分流程:可设置多级评分,评分流程会按照从上往下的顺序,依次评分;评分人必须在评分截至时间内完成评分,否则不允许继续评分。

此时我们需要挂起流程实例。

// 当前流程实例为激活状态,我们需要挂起对应的流程实例。

System.out.println("当前流程实例已被挂起");

后续在激活流程实例。

// 当前流程实例为挂起状态,我们需要激活对应的流程实例。
runtimeService.activateProcessInstanceById(processInstanceId);
System.out.println("当前流程实例已被激活");

posted @ 2026-09-01 19:07  诺森德的雪原  阅读(16)  评论(0)    收藏  举报