Liferay 6.1开发学习(二十二):在插件工程中使用kaleo工作流

2013年08月01日 Liferay 评论 7 条 阅读 12,377 views 次

kaleo是Liferay默认的工作流,此篇文章介绍kaleo工作流的与二次开发的Portlet的集成,kaleo的设计本篇文章不涉及。

Liferay默认情况下并没有安装工作流的插件,需要我们到他的官方市场上下载安装。安装过程这里不描述,以上内容假定已经安装了工作流插件。

工作流的集成步骤

第一步:请确保ServiceBuilder的xml文件中的相应实体包含以下内容,并执行ServiceBuilder

  1. <!-- workflow fields -->  
  2. <column name="companyId" type="long" />  
  3. <column name="groupId" type="long"/>  
  4. <column name="title" type="String"></column>  
  5. <column name="content" type="String"></column>  
  6.   
  7. <column name="status" type="int"></column>  
  8. <column name="statusByUserId" type="long"></column>  
  9. <column name="statusByUserName" type="String"></column>  
  10. <column name="statusDate" type="Date"></column>  
  11. <reference package-path="com.liferay.portal" entity="WorkflowInstanceLink"></reference>  

第二步:在liferay-portlet.xml中添加如下内容(asset-renderer-factory可以先暂时不添加)。

  1. <asset-renderer-factory>com.huqiwen.asset.NewsAssetRenderFactory</asset-renderer-factory>  
  2. <workflow-handler>com.huqiwen.workflow.NewsWorkflowHandler</workflow-handler>  

第三步:编写NewsWorkflowHandler,这类名由自己定义,这两个类的具体定义如下:

NewsWorkflowHandler是工作流处理的核心类,比如状态的改变等。继承BaseWorkflowHandler类,此类的示例代码如下:

  1. public class NewsWorkflowHandler extends BaseWorkflowHandler {   
  2.   
  3.     public static final String CLASS_NAME = News.class.getName();   
  4.     @Override  
  5.     public String getClassName() {   
  6.         return CLASS_NAME;   
  7.     }   
  8.   
  9.     @Override  
  10.     public String getType(Locale locale) {   
  11.         return ResourceActionsUtil.getModelResource(locale, CLASS_NAME);   
  12.     }   
  13.   
  14.     @Override  
  15.     public Object updateStatus(int status, Map<String, Serializable> workflowContext)   
  16.             throws PortalException, SystemException {   
  17.         long userId = GetterUtil.getLong(   
  18.                 (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));   
  19.             long resourcePrimKey = GetterUtil.getLong(   
  20.                 (String)workflowContext.get(   
  21.                     WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));   
  22.             ServiceContext serviceContext = (ServiceContext)workflowContext.get(   
  23.                     "serviceContext");   
  24.         return NewsLocalServiceUtil.updateStatus(userId, resourcePrimKey, status, serviceContext);   
  25.     }   
  26.   
  27. }  

说明

CLASS_NAME:为我们要添加工作流的实体,比如我这里是新建的一个News的实体。

getType():此方法实际是用来处理国际化的,有些地方可以看到是写的mode.resource+CLASS_NAME,其实这里不重要,这个值的使用就是提供一个key值,用来显示国际化的Key值,对应于国际化资源文件中的key,这里在插件工程的content/Language_zh_CN.properties里面添加此处的说明,比如此示例中我添加的为:model.resource.com.huqiwen.portlet.article.model.News=\u65B0\u95FB\u5BA1\u6838

updateStatus():此方法是用来处理工作流流转的方法,具体的可以照抄上面的内容,return的方法为实体的xxServiceLocalUtil方法,因为我这里的实体名为news,所以如上。

第四步:在我们的实体的处理方法里面添加自定义方法updateStatus,在xxLocalServiceImpl(此处为NewsLocalServiceImpl)里面添加updateStatus方法,方法的定义如下,完成之后再次执行ServiceBuilder:

  1. public News updateStatus(long userId,long resourcePrimKey,int status,ServiceContext serviceContext) throws NoSuchUserException, SystemException{   
  2.     User user = userPersistence.findByPrimaryKey(userId);   
  3.     Date now = new Date();   
  4.     News news = newsPersistence.fetchByPrimaryKey(resourcePrimKey);   
  5.        
  6.     news.setModifiedDate(serviceContext.getModifiedDate(now));   
  7.     news.setStatus(status);   
  8.     news.setStatusByUserId(user.getUserId());   
  9.     news.setStatusByUserName(user.getFullName());   
  10.     news.setStatusDate(serviceContext.getModifiedDate(now));   
  11.     newsPersistence.update(news, false);   
  12.     return news;   
  13. }  

基本上照抄就行,需要注意的是返回的内容为相对应的实体。

到这里时,如果是第二步中没有添加asset-renderer-factory,则我们发布工程,就可以在后台的工作流配置的地方,看到我们添加的工作流了,也可以进行基本的流转的,但是我们仔细测试的话会发则,在我的任务里面,当点击工作流名称的时候不能看到工作流的详情,那要怎么处理呢?接下来我们完善此工作流的处理。

第六步:(第二步里面的asset-renderer-factory确保已经添加),建立NewsAssetRenderFactory类,继承BaseAssetRendererFactory,此类的示例代码如下:

  1. public class NewsAssetRenderFactory extends BaseAssetRendererFactory {   
  2.        
  3.     public static final String CLASS_NAME = News.class.getName();   
  4.     public static final String TYPE = "news";   
  5.        
  6.     @Override  
  7.     public AssetRenderer getAssetRenderer(long classPk, int type)   
  8.             throws PortalException, SystemException {   
  9.         int status = WorkflowConstants.STATUS_ANY;   
  10.         /**
  11.          * 如果需要根据不同的状态获取不同的内容,可以在此进行设置  
  12.          */  
  13.         if (type == TYPE_LATEST_APPROVED) {   
  14.             status = WorkflowConstants.STATUS_APPROVED;   
  15.         }   
  16.            
  17.         News news = NewsLocalServiceUtil.getNews(classPk);   
  18.         return new NewsAssetRender(news);   
  19.     }   
  20.   
  21.     @Override  
  22.     public String getClassName() {   
  23.         return CLASS_NAME;   
  24.     }   
  25.   
  26.     @Override  
  27.     public String getType() {   
  28.         return TYPE;   
  29.     }  

此类也比较简单,基本上是复制下来使用即可,在自己的类里面将news修改为自己对应的实体,这里又有一个新的类NewsAssetRender,同样需要新建,建立NewsAssetRender,继承BaseAssetRenderer,示例代码如下:

  1. public class NewsAssetRender extends BaseAssetRenderer {   
  2.     public static final String CLASS_NAME = News.class.getName();   
  3.        
  4.     public NewsAssetRender(News news){   
  5.         _news = news;   
  6.     }   
  7.     @Override  
  8.     public long getClassPK() {   
  9.         return _news.getNewsId();   
  10.     }   
  11.   
  12.     @Override  
  13.     public long getGroupId() {   
  14.         return _news.getGroupId();   
  15.     }   
  16.   
  17.     @Override  
  18.     public String getSummary(Locale arg0) {   
  19.         return HtmlUtil.stripHtml(_news.getContent());   
  20.     }   
  21.   
  22.     @Override  
  23.     public String getTitle(Locale arg0) {   
  24.         return _news.getTitle();   
  25.     }   
  26.   
  27.     @Override  
  28.     public long getUserId() {   
  29.         return _news.getUserId();   
  30.     }   
  31.   
  32.     @Override  
  33.     public String getUserName() {   
  34.         return _news.getUserName();   
  35.     }   
  36.   
  37.     @Override  
  38.     public String getUuid() {   
  39.         return _news.getUuid();   
  40.     }   
  41.   
  42.     @Override  
  43.     public String render(RenderRequest renderRequest, RenderResponse renderResponse, String template)   
  44.             throws Exception {   
  45.         if (template.equals(TEMPLATE_FULL_CONTENT)) {   
  46.             renderRequest.setAttribute(   
  47.                 "news", _news);   
  48.   
  49.             return "/html/asset/" + template + ".jsp";   
  50.         }   
  51.         else {   
  52.             return null;   
  53.         }   
  54.     }   
  55.   
  56.     private News _news;   
  57. }  

此类需要说明的方法为render方法,此方法是我们点击查看详情的时候显示的,可以看到他是跳转到了/html/asset/xxx.jsp的页面,其实这个路径为/html/asset/full_content.jsp的页面。

现在我们的portlet插件工程的docroot目录下面建立上面的路径的jsp页面,JSP里面的内容如下:

  1. <%@page import="com.huqiwen.portlet.article.model.News"%>   
  2. <%@ include file="/html/init.jsp" %>   
  3.   
  4. <%   
  5. News news = (News)request.getAttribute("news");   
  6. %>   
  7.   
  8. <%= news.getContent() %>  

此处只是示例,所以只是显示了正文,此页面可以根据实际的情况自由编写。

至此,整个工作流的使用介绍完毕,现在deploy插件工程,工作流即集成到了我们的portlet插件工程里面。

工作流的初始化

上面介绍的部分为配置部分,但是工作流什么时候启动呢?一般是我们在添加一个新的内容时启动工作流,启动的代码如下:

WorkflowHandlerRegistryUtil.startWorkflowInstance(companyId,userId, News.class.getName(), news.getNewsId(), news,serviceContext);

此代码在我们添加保存实体后进行。

为了方便对流程的详情进行流利,我们要添加asset的支持,在上面的代码后台,添加如下代码,后面的两个null为分类和标签,如果有使用的话就添加,没有使用的话使用null即可。

AssetEntryLocalServiceUtil.updateEntry(userId, groupId,News.class.getName(), news.getNewsId(), null, null);

上面是当我们添加内容的时候启动了工作流,当我们删除信息时也需要删除工作流,删除的代码如下:

WorkflowInstanceLinkLocalServiceUtil.deleteWorkflowInstanceLinks(news.getCompanyId(), ews.getGroupId(), News.class.getName(),news.getNewsId());

回顾

看到第一步的时候,可能会有疑问,我的实体必须要有title和content字段么?我们仔细分析一下代码其实也不是必须的,title的目标是我们可以看到要审批的内容的标题,content的内容是方便我们对内容进行预览,我们可以标题的地方取name值,正文的地方取summary,或者是整个实体的toString,或者是其他内容的拼接。

用户头像

7 条留言  访客:7 条  博主:0 条

  1. 本人菜苗一枚,请求培育
    这个东西好像在liferay里面有吧?有个图形化操作界面。。。不过好像不是针对某个特定的类。。。

    在liferay control panel里面有workflow->Default Configuraion….有什么区别?

  2. 顺便问一下,我的liferay control panel是叫做portal configuraion.没关系把?因为我读了官网structure & template那块,感觉它的跟我的有点儿区别。。。。6.1CE
    先拜谢了 😆

  3. 恩。。。还有就是,工作流到底是针对外部业务的还是针对内部任务的。。。。有点儿晕 😳

  4. 不错。最近正在研究liferay.

  5. 现在已经下载 kaleo-web 6.1.0 插件要怎么安装

  6. avatar 白喵是也

    你好,胡总,最近项目在使用这个工作流,想问下。比如你这个news类。 怎么通知news类的主键id(classPk),来查到工作流WorkflowTask类的workflowTaskId呢?忘答复,谢谢!

给我留言

您必须 登录 才能发表留言!

Copyright © IT人生录 保留所有权利.   主题设计 知更鸟 滇ICP备16001547号-1

用户登录

分享到: