一、前言
近期做了一个简单的财务管理系统,在工作审批模块中运用了工作流引擎。在对比了JBPM与Activiti后,毅然而然决定使用Activiti。本文对Activiti不做过多的介绍,重点在于分析其自身的用户管理模块。刚接触,也只是阐述下自己的个人项目经验。若有不足之处,还望指正。
二、Activiti的用户管理
数据库表:
act_id_user表:
act_id_group表:
act_id_membership表:
从图中可以看出,activiti的用户模块只有用户与组为多对多的关系,仅此而已。
那么现实中呢?我们往往是有分层级关系的。例如小组-->部门-->中心,一层一层的所属关系,这该怎么破?本身我们利用工作流的话,一般都是作为中间件或服务来用。而自己的业务系统已经有一套用户管理模块了,又该如何去整合在一起呢?这些无疑是对于我们干接触的新手来说最为困恼的方面。
接下来我就,以自身所做项目去分析下我是如何处理这些问题的。首先,先介绍下我的项目架构:
1、公司内部有自己的一套管理系统,所有内部系统的用户模块都由其进行管理。
2、工作流引擎将在今后作为一个服务系统,所以运用其REST接口等可以单独独立出来成为一个系统。
3、所以将涉及到业务系统、用户管理系统、工作流引擎三者间的交互。
So……我在此次的项目中摒弃了Activiti的用户管理模块,重新自定义后与Uap系统整合起来。该如何自定义activiti的用户管理模块呢?不急,我会慢慢道来。先给大家介绍篇‘咖啡兔’大神(接触过activiti的伙伴都懂得他是谁)的文章:《同步或者重构Activiti Identify用户数据的多种方案比较》
文章中介绍三种方式:
1、调用IdentifyService接口完成同步。
2、自定义SessionFactory,重写其关于用户与组的增删改查操作。
3、用视图覆盖同名的ACT_ID_系列表。
三种方式各有优劣,而我则用了第二种方式,对其进行重构。
三、如何重构Activiti用户管理模块增删改查
对不同的表的CRUD操作均由一个对应的实体管理器(XxxEntityManager,有接口和实现类),引擎的7个Service接口在需要CRUD实体时会根据接口获取注册的实体管理器实现类(初始化引擎时用Map对象维护两者的映射关系),并且引擎允许我们覆盖内部的实体管理器,查看源码后可以知道有关Identity操作的两个接口分别为:UserIdentityManager和GroupIdentityManager。
查看引擎配置对象ProcessEngineConfigurationImpl类可以找到一个名称为“customSessionFactories”的属性,该属性可以用来自定义SessionFactory(每一个XXxManager类都是一个Session<实现Session接口>,由SessionFactory来管理),为了能替代内部的实体管理器的实现我们可以自定义一个SessionFactory并注册到引擎。
这种自定义SessionFactory的方式适用于公司内部有独立的身份系统或者公共的身份模块的情况,所有和用户、角色、权限的服务均通过一个统一的接口获取,而业务系统则不保存这些数据,此时引擎的身份模块表就没必要存在(ACT_ID_*)。所以,我重写了其增删改查等方法后,就摒弃了Activiti的用户表,让其与公司的uap管理系统相关联。
1、项目activiti项目使用的是spring mvc框架,所以先看配置:
<!-- Activiti begin --> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> ……………… <!-- 自定义用户管理 --> <property name="customSessionFactories"> <list> <bean class="me.kafeitu.demo.activiti.service.activiti.custom.CustomUserEntityManagerFactory" /> <bean class="me.kafeitu.demo.activiti.service.activiti.custom.CustomGroupEntityManagerFactory" /> </list> </property> ……………… </beans>
自定义了:CustomUserEntityManagerFactory与CustomGroupEntityManagerFactory两个类,并注入spring管理容器中。
2、自定义类代码:
/** * 自定义的Activiti用户会话工厂 * @author linhy * */ @Service public class CustomUserEntityManagerFactory implements SessionFactory { @Resource private CustomUserManager customUserManager; @Override public Class<?> getSessionType() { // 返回原始的UserManager类型 return UserIdentityManager.class; } @Override public Session openSession() { // 返回自定义的UserManager实例 return customUserManager; } }
注意点:(1)实现SessionFactory接口;(2)返回原始类型与返回自定义实例,如上注释代码;
/** * 自定义用户管理器 * @author linhy * */ @Service("customUserManager") public class CustomUserManager extends UserEntityManager { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public User findUserById(String userId) { User user = new UserEntity(); //引入Uap系统客户端包,关联进行查询 BaseDto baseDto = RoomyUapUserUtils.getUser(userId); if (baseDto.getRemoteStatus() == RemoteStatus.SUCCESS) { UserDto userDto = (UserDto) baseDto; user.setId(userDto.getUserName()); user.setFirstName(userDto.getName()); user.setLastName(userDto.getName()); } else { user = null; logger.info("用户名:" + userId + ";无法获取用户信息"); } return user; } @Override public List<Group> findGroupsByUser(String userId) { //…… return list; } @Override public Boolean checkPassword(String userId, String password) { //…… return Boolean.valueOf(true); } @Override public List<User> findUserByQueryCriteria(UserQueryImpl query, Page page) { //…… return list; } @Override public long findUserCountByQueryCriteria(UserQueryImpl query) { //…… } }
注意点:继承UserEntityManager,进入去看其源码,你就会恍然大悟。
重写自己需要用到的方法,与自己的用户管理系统相关联。组的自定义方式也一样,这里省略。这样,当Activiti要查询用户的时候,就通过uap客户端去查询公司自己的用户管理系统。这就把已有的用户管理模块与Activiti整合在了一起,相对于调用内部接口同步用户数据,我觉得自定义重写过更适合目前的项目需求。
四、结语
本文只介绍了如何实现自定义用户管理,但针对文章一开头所提出的上下级层级关系却未作出解答,将在下一篇文章向大家介绍。本文若有不足之处,敬请指正。