Activiti原理分析(三)用户管理

Activiti 自带一套用户管理系统,维护了用户和用户组的对应关系,通过 identityService 可以对它们进行增删改查。当你在 UserTask 中指定了用户组时,Activiti 就会使用它维护的这个关系寻找用户了。

但是这也为 Activiti 的使用带来了困难,因为一般应用都有一套自建的用户管理,不可能去使用 Activiti 这一套,就不得不花一些成本来对接,而且对接方法都比较 hack,个人觉得这是 Activiti 设计得不太好的地方。

受理人与候选人

一个 UserTask 可以设置一个受理人(Assignee),或者多个候选人。

受理人就是负责完成该任务的人,在审批场景下就可以理解成审批人,只能有一个,如果要有多个的话则应该使用上一篇文章提到的多实例任务。

<!--kermit就是受理人的 id-->
<userTask id="theTask" name="my task" activiti:assignee="kermit" />

候选人可以有一个或者多个,他们都是受理人的“候选”,他们要“抢”任务,谁先调用了 taskService.claim(taskId, userId);,谁就变成了任务的受理人。

<!--两个候选人 kermit与gonzo-->
<userTask id="theTask" name="my task" activiti:candidateUsers="kermit,gonzo"/>


<!--management用户组的所有用户作为候选人-->
<userTask id="theTask" name="my task" activiti:candidateGroups="management"/>


内置的用户管理

Activiti 中的用户模型非常简单,就是用户和用户组之间的多对多关系,所以只涉及三张表:

  • ACT_ID_USER:用户信息
  • ACT_ID_GROUP:用户组信息
  • ACT_ID_MEMBERSHIP:存储用户和用户组之间的多对多关联


Activiti原理分析(三)用户管理


流程运行时用户信息

在应用中一般会调用下面的方法查询某个用户的 task:


            // 查询所有候选 userId 的任务
            List<Task> tasks = taskService.createTaskQuery()
                    .taskCandidateUser(userId).list();
            // 查询所有 userId 受理的任务
            List<Task> tasks = taskService.createTaskQuery()
                    .taskAssignee(userId).list();
            // 查询所有 userId候选或者受理的任务
            List<Task> tasks = taskService.createTaskQuery()
                    .taskCandidateOrAssigned(userId).list();


受理人查询

ACT_RU_TASK 表中本来就有一个 ASSIGNEE_ 字段表示受理人,所有当你使用 taskAssignee(userId) 查询时,直接就是用的 ASSIGNEE_=userIdACT_RU_TASK 表中去查的。

候选人查询

在流程运行时还会有一张 ACT_RU_IDENTITYLINK 表,用来记录流程运行时相关的人员信息,比如流程发起人,参与人,Task 的候选人等信息:


Activiti原理分析(三)用户管理

其中 TYPE_ 有以下几种取值:

  • starter: 流程发起人
  • participant: 流程参与人,当前的受理人就会被记录在这里
  • candidate: Task 对应的候选人或者候选用户组

因为这是一张运行时表,所以里面数据是会随着流程运转不断增删改的。

当你使用 taskCandidateUser(userId) 条件查询时其实就是去这张表里根据 TYPE_='candidate' AND USER_ID_=userId 找到用户候选的 Task

候选用户组

假如你将 UserTask 配置成 activiti:candidateGroups="management" ,如果 userId 属于 manangement 用户组,那么使用 taskCandidateUser(userId) 条件查询,也是能查出该 UserTask 的。

此时就需要用到上一小节提到的用户管理相关表了。Activiti 如果会去 ACT_ID_MEMBERSHIP 中找到该用户的 groupId,然后去 ACT_RU_IDENTITYLINK 中根据 GROUP_ID_ 查。

Activiti 如何与已有用户管理系统对接?

从上面的分析可以看到,Activiti 只有在你配置了 candidateGroups 才可能去查询内置的用户管理信息,所以个人建议是就不要使用 candidateGroups 配置了,全部使用 candidateUsersassignee,然后在 userId 里加入一些特征区分用户组或者别的业务相关的东西,比如加个前缀 GROUP-groupId,之后就全部用自己业务系统里的 id 去查 Task。不用担心这些ID在 Activiti 内置的用户管理系统中没有,Activiti 根本不会去校验这个。

当然也有人研究过一些更加麻烦的对接方法,列出如下,也可以参考:

    • 往 Activiti 内置的用户表中,同步自己业务的用户数据。个人感觉比较麻烦,而且也容易出各种问题
    • 将 Activiti 用户相关存储类用自己的实现类换掉。听起来很复杂,但是因为 Activiti 6 所采用的可插拔的存储层设计,其实只要替换两类就可以了,后面我有空会再研究一下它的存储层设计。而且如果要搞分库分表的话,存储层本来就是要重新弄的,顺手就可以把用户管理换掉。
    • 用业务用户数据的视图替换掉 Activiti 内置用户管理的这三张表。感觉不一定所有的业务用户数据都可以映射到 Activiti 的用户模型,所有通用性有限

参考


上一篇:Activiti原理分析(一)从一个简单流程开始


下一篇:idea中maven项目(不同package下)分别打包