Liferay内置的工作流是企业版的功能,虽然简单粗糙,但依然不支持社区版。
既然要用更强大的Activiti来替代它,那就非常有必要学习一下内置工作流的一些思想,以便借鉴。
它的特点:
- 实体的工作流操作可以通过service layer进行集成;
- 需要添加4个额外的字段来跟踪流程状态;
- 通过Service Builder来建立字段,然后更新service layer;
- 视图层UI可以显示实体的工作流状态。
实体集成逻辑
比如,发布一个实体类的工作流支持,首先添加注解:
@Component( property = {"model.class.name=com.my.app.package.model.FooEntity"}, service = WorkflowHandler.class )
开发XXXWorkflowHandler,实现Override 三个方法
package com.liferay.docs.foo.workflow; ... public class FooEntityWorkflowHandler extends BaseWorkflowHandler { public static final String CLASS_NAME = FooEntity.class.getName(); @Override public String getClassName() { return CLASS_NAME; } @Override public String getType(Locale locale) { return LanguageUtil.get(locale, "model.resource" + CLASS_NAME); } @Override public Object updateStatus(int status, Map<String, Serializable> workflowContext) throws PortalException, SystemException { long userId = GetterUtil.getLong( (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID)); long fooEntityId = GetterUtil.getLong( (String)workflowContext.get( WorkflowConstants.CONTEXT_ENTRY_CLASS_PK)); ServiceContext serviceContext = (ServiceContext)workflowContext.get( "serviceContext"); return FooEntityLocalServiceUtil.updateStatus( userId, fooEntityId, status, serviceContext); } }
为了记录实体的当前流程状态,必须要有以下4个字段(通过Service Builder)
service.xml中添加:
<column name="status" type="int" /> <column name="statusByUserId" type="long" /> <column name="statusByUserName" type="String" /> <column name="statusDate" type="Date" />
比如在更新实体时,自动通过这4个字段,把实体和流程状态相关联:
fooEntity.setStatus(status);
fooEntity.setStatusByUserId(user.getUserId());
fooEntity.setStatusByUserName(user.getFullName());
fooEntity.setStatusDate(serviceContext.getModifiedDate(now));
fooEntityPersistence.update(fooEntity);
启动一个和实体关联的流程:
WorkflowHandlerRegistryUtil.startWorkflowInstance(fooEntity.getCompanyId(), fooEntity.getGroupId(), fooEntity.getUserId(), FooEntity.class.getName(), fooEntity.getPrimaryKey(), fooEntity, serviceContext);
如果实体属于Assets,那在为实体设置工作流字段后,就可以添加任何附加的逻辑方法。
例如,如果想根据其工作流状态来设置可见性(未审批就不显示):
if (status == WorkflowConstants.STATUS_APPROVED) { assetEntryLocalService.updateEntry( FooEntity.class.getName(), fooEntityId, fooEntity.getDisplayDate(), null, true, true); } else { assetEntryLocalService.updateVisible( fooEntity.class.getName(), entryId, false); }
为了在视图中展示,需要设置finder (service.xml)
<finder name="G_S" return-type="Collection"> <finder-column name="groupId"></finder-column> <finder-column name="status"></finder-column> </finder>
实现一个Getter:
public List<FooEntity> getFooEntities(long groupId, int status) throws SystemException { return fooEntityPersistence.findByG_S(groupId, WorkflowConstants.STATUS_APPROVED); } jsp UI: <liferay-ui:search-container-results results="<%=FooEntityLocalServiceUtil.getFooEntities(scopeGroupId, fooEntityId(), Workflowconstants.STATUS_APPROVED, searchContainer.getStart(), searchContainer.getEnd())%>" ...
流程设计器
这个属于企业版功能,有点弱
这个有点意思:
设置启动任务的签收者,
一共有6种类型:
- 创建者
- 某角色
- 角色ID
- 通过一个代码段来指定
- 特定的用户
- 通过资源
在Kaleo中,只有这6种节点,这实在可怜,恐怕要实现复杂的流程将依靠编写大量的代码来实现。
现在知道为啥Kaleo叫workflow而不是叫BPM了吧,因为其不能实现业务流程集成,但实现仅简单顺序流程,还是没有问题的。
- Condition: 通过脚本来实现条件逻辑(script包含几种语言:Beanshell、Drl、Groovy、JavaScript、Python、Ruby),这有点不太友好和方便;
- Fork: 并行处理
- Join: Fork并行后的汇总,这个可以实现会签,比如说一个节点由领导A、领导B同时审核才能结束,往下走;
- Join XOR: 异或逻辑的汇总,通俗的说就是实现抢签,有点像Activiti的候选组的概念,比如说一个节点由领导A、领导B审核,随便一个领导先审核了,节点就结束,往下走;
- State: 状态节点,比如开始、结束就是属于这个类型;
- Task: 任务节点,比如审批、填写表单....
一个XML的描述文件: