我的流程(文档借阅流程)设计总结
20XX年X月X日 星期X
为了方便大家进一步熟悉IBM FileNet产品开发,来做的一个简单流程(文档借阅流程),主要的功能实现和流程节点设计,还是参照CNOOC_TJ 项目上的问题提出流程。时间仓促,对PE设计只是介绍了最常用的几点,深层的东西还不曾接触和思考到。CE方面应该涉及到流程结束时的一个存档操作。
1. 流程设计器 ProcessDesigner,一般是在Workplace(XT)中提供。
设计流程的各个节点细节。
2. 流程控制台 Process Configuration Console ,单独使用或集成在WorkplaceXT 中。
设置一些Queue或Roster属性,或进行暴露字段的设置(提供特殊查询)。
3. FileNet Enterprise Management,设计OS中一些文档对象,自定义类型,文件夹等类型相关属性、安全性设置。
基于FileNet产品进行流程设计,其实就是将实际工作中固定化的处理流程信息进行信息化处理,把我们从繁琐的工作流程记忆、协调中解脱出来,我们只需要关注自己负责的处理节点即可。这就是作为企业信息化的一种体现,重复、固定的东西交给计算机去做。
1. 比如,虚拟一个流程环境,(成功)借阅文档的流程:提出者将请求提交--> 第一级处理(同意)-->第二级处理(同意)--> 提出者对 审核结果确认 --> 流程结束 。
2. 只考虑流程设计这个环节,文档借阅流程,针对这整只流程以及其中每一次节点传递之间,需要设计一些数据字段。作为数据信息传递的一个重要环节,一定要把流程相关的数据字段设计完善。
3. 需要注意一点,就是我们要从启动节点开始,就在节点设置里把设计好的数据字段作为参数添加到该节点中,这是很重要的一步操作。
因为我们后面在处理每一步节点(VWWorkObject)的时候,获取到当前节点,我们需要把该流程一步步传递过来的数据信息进行处理,也就是说在需要的节点上添加(暴露)必要的数据字段,是为了保持流程数据始终如一的通过节点,直至流程结束。
关于流程的数据字段还有一部分是系统自带的,也就是说提供整只流程使用的数据字段由系统字段和自定义字段组成。系统字段主要是记录节点名、流程名、流程启动时间等,自定义字段就是我们根据实际需求的来的。
在这里,需要注意,(Flex+Java)编码中需要将部分系统字段提取出来,用做前台信息显示。
需要注意,一个系统字段WobNum,当流程提交的时候该字段为空,系统会自动生成,需要在第一个节点处理时把WobNum字段值添加到流程数据字段中,因为在以后的节点处理中会用到该字段值。
1. 领导审批1 节点
这个节点没有进行复杂的逻辑设计,只是将该节点的处理目标(步骤目标)指定为一个工作队列(WorkQueue,也叫做抢单,经过该节点时会将流程申请提交给该队列内的所有用户,作为公共任务)。当然我们可以单独指定该节点的处理者为XXX(或指定一个组群,也叫会签,必须该组内所有用户处理过才能进入下个节点),就需要在参与者里边设置。比如在该流程中,最后一个(审批通过,待确认)节点,就是指定为当前参与者(流程提交人)去处理,则当流程进行到此时,流程信息会跑到当前参与者的Inbox中(在流程提交者的我的待办 中会出现一条待确认 记录。)
2. 领导审批2 节点
和领导审批1节点设置一样,设计上该节点主要是进一步熟练FileNet 中关于节点的处理实现。
3. 一对多节点之间的路由设置
在流程设计里,这种分支路径的转发,叫做路由设置。如上图,可以选择转发的条件,我们之前添加到节点中的数据字段在此时就发挥了作用,如此,当条件(state=”批准”)成立时,会由领导审批1节点转发到下一级(领导审批2)节点。
4. FileNet 提供的流程设计工具中,有很多节点类型以供它用,尽量去满足我们的需求。我从海油这边的项目上看到的很多都只是用到这几个常用节点(启动、结束、队列、延时等待)。
比如有Compontent节点,我就没找到现实应用,没具体使用过这个节点,只知道,这需要在流程控制台中进行设置,在Compontent Queue(组件队列)中添加这样一个以供流程设计外用的组件,在添加配置过程中,需要把进行复杂业务逻辑处理的jar包导入其中,然后根据(流程控制台会自动检出可供暴露的类方法)方法接口和参数接口。在流程设计的Compontent节点进行处理。
由于使用了IBM封装的P8Helper.jar ,具体的代码不展示,只说下重点。明其意即可。
1. 流程启动(提交流程)
在这里需要将我们流程字段(流程实体类)封装好 dataMap。
VWStepElement stepElement = vwSession.createWorkflow(workflowName); setDataMap(stepElement, dataMap);//对当前节点数据信息进行处理 stepElement.doDispatch();//启动流程
2. 根据userName查询个人任务和公共人物(我的待办)
文档借阅流程中领导审批节点都是WorkQueue节点,在查询是需要从queue中去做。
/** * 获取个人任务队列 * @return */ @SuppressWarnings("unchecked") publicList<DocumentBorrowingVo> getMyWorkList(){ VWSessionvwSession = getVWSession(); List<DocumentBorrowingVo>workItems = null; StringqueueName = "" ; try{ //系统定义,个人任务 Inbox queueName= "Inbox(0)"; IntegeruserId = new Integer((int)(vwSession.getCurrentUserSecId())); //设定筛选条件,指定当前用户Id和当前流程的主题信息。 Stringfilter = "F_BoundUser =:userId AND(F_Subject ='文档借阅1')" ; Object[]substitutionVars = {userId}; //通过QueueHelper 得到检索结果wobs,里边有所有符合条件的结果信息。 List<VWWorkObject>wobs = QueueHelper.getWorkElements(vwSession, queueName, filter,substitutionVars, 1); //将当前查询得到的所有结果处理,返回一个特定实体类型集合。 workItems= getWorkItems(wobs,vwSession); }catch(VWException e){ logger.error(e); e.printStackTrace(); }catch(Exception e){ logger.error(e); e.printStackTrace(); } return workItems; } /** * 获取公共队列中的任务 * @return */ @SuppressWarnings("unchecked") private List<DocumentBorrowingVo>getPublicWorklist(){ VWSessionvwSession = getVWSession(); List<DocumentBorrowingVo>workItems = newArrayList<DocumentBorrowingVo>(); StringqueueName = null; Stringfilter = "F_Subject='文档借阅1' "; try{ //获取当前用户所有的Queue队列信息,一个用户可以在多个Queue队列中。 List<Map>queueNames = UserRoleOperation.getQueueNameByUserId(vwSession.fetchCurrentUserInfo().getName()); List<DocumentBorrowingVo>queueWorkItems; for(int i = 0 ;i<queueNames.size() ; i++){ //在单一Queue队列中进行查询。 queueName= (String)queueNames.get(i).get("queueName"); List<VWWorkObject>wobs = QueueHelper.getWorkElements(vwSession, queueName, filter, null, 1); //进行数据的拆箱 装箱。最后把处理好的流程实体对象集合返回。 queueWorkItems= getWorkItems(wobs,vwSession); if(queueWorkItems != null){ workItems.addAll(queueWorkItems); } } }catch(VWException e){ logger.error(e); e.printStackTrace(); }catch(Exception e){ logger.error(e); e.printStackTrace(); } return workItems; } /** * 将VWWorkObjects和 DocumentBorrowingVo进行转换。 * @param wobs * @param vwSession * @return * @throws VWException */ privateList<DocumentBorrowingVo> getWorkItems(List<VWWorkObject> wobs, VWSessionvwSession) throws VWException { List<DocumentBorrowingVo>workItems = new ArrayList<DocumentBorrowingVo>(); for (Iteratoriter = wobs.iterator(); iter.hasNext();) { VWWorkObjectwo = (VWWorkObject) iter.next(); DocumentBorrowingVo documentBorrowing = newDocumentBorrowingVo(); documentBorrowing.setAttachment(null); documentBorrowing.setDocumentExplain("setDocumentExplain"); documentBorrowing.setBusinessId((String)wo.getFieldValue("businessId")); documentBorrowing.setDocumentName((String)wo.getFieldValue("documentName")); documentBorrowing.setDocumentType(2); documentBorrowing.setState("setState"); documentBorrowing.setUploaded(true); // documentBorrowing.setUser((String)wo.getFieldValue("applicant")); documentBorrowing.setStartTime((Date)wo.getFieldValue(P8BpmConstants.PE_SYSTEM_FIELD_STARTTIME)); documentBorrowing.setStepName((String)wo.getFieldValue("F_StepName")); documentBorrowing.setStartTime((Date)wo.getFieldValue(P8BpmConstants.PE_SYSTEM_FIELD_STARTTIME)); documentBorrowing.setStepName((String)wo.getFieldValue(P8BpmConstants.PE_SYSTEM_FIELD_STEP_NAME)); documentBorrowing.setSubject((String)wo.getFieldValue(P8BpmConstants.PE_SYSTEM_FIELD_SUBJECT)); documentBorrowing.setWobNum((String)wo.getWorkObjectNumber()); workItems.add(documentBorrowing); } return workItems; }
3. 根据userName查询所提交流程的流转状态 (我的在办)
/** * 获取我提交的未完成流程 */ publicList<DocumentBorrowingVo> getMyDoingWorkItems(String application) throws Exception{ StringworkflowName ="文档借阅1"; List<DocumentBorrowingVo>workItems = newArrayList<DocumentBorrowingVo>(); VWSessionadminVWSession = null; try{ adminVWSession = SessionHelper.logon(Constants.cepe_resourceBundle.getString("admin"), Constants.cepe_resourceBundle.getString("pwd")); StringrosterName = Constants.cepe_resourceBundle.getString("rosterName"); VWRosterroster = adminVWSession.getRoster(rosterName); int queryFlag =VWRoster.QUERY_NO_OPTIONS; StringqueryFilter = ""; //组合 queryFilter 。 if(!"".equalsIgnoreCase(workflowName)&& workflowName != null){ if("".equalsIgnoreCase(queryFilter)){ queryFilter+= " F_Subject ='" + workflowName + "' "; }else{ queryFilter+=" AND F_Subject='" + workflowName + "'"; } } if(!"".equalsIgnoreCase(application)&& application != null){ int userNameToId =adminVWSession.convertUserNameToId(application); if("".equalsIgnoreCase(queryFilter)){ queryFilter+= " F_Originator ='"+ String.valueOf(userNameToId) +"'"; }else{ queryFilter+= " AND F_Originator ='" + String.valueOf(userNameToId)+ "'" ; } } //这里是针对数据进行了一个简单的筛选,只显示具有明确节点名称的数据。 queryFilter+= " AND F_StepName !='Tracker Step'"; VWRosterQueryrosterQuery = roster.createQuery(null, null, null, queryFlag, queryFilter, null, VWFetchType.FETCH_TYPE_WORKOBJECT); while(rosterQuery.hasNext()){ VWWorkObjectwo = (VWWorkObject)rosterQuery.next(); DocumentBorrowingVodocumentBorrowingVo = new DocumentBorrowingVo(); documentBorrowingVo.setAttachment(null); documentBorrowingVo.setBusinessId((String)wo.getFieldValue("businessId")); documentBorrowingVo.setDocumentExplain("documentExplain"); documentBorrowingVo.setDocumentName((String)wo.getFieldValue("documentName")); documentBorrowingVo.setDocumentType(1); documentBorrowingVo.setStartTime((Date)wo.getFieldValue(P8BpmConstants.PE_SYSTEM_FIELD_STARTTIME)); documentBorrowingVo.setState((String)wo.getFieldValue("state")); documentBorrowingVo.setUploaded(false); documentBorrowingVo.setUser((String)wo.getFieldValue("applicant")); documentBorrowingVo.setStepName((String)wo.getFieldValue("F_StepName")); workItems.add(documentBorrowingVo); } }catch(VWException e){ logger.error(e); e.printStackTrace(); }catch(Exception e){ logger.error(e); e.printStackTrace(); }finally{ SessionHelper.logoff(adminVWSession); } return workItems; }
4. 每个节点的处理(流转)
public boolean doSubmit(DocumentBorrowingVo documentBorrowingVo){ boolean submit = false; try{ VWSessionvwSession = getVWSession(); if(vwSession == null){ logger.error("vwSession is null "); return false; } BPMWorkflowServicesbpmWorkflowServices = new BPMWorkflowServices(vwSession); Map<String,Object>map = getDataFieldMap(documentBorrowingVo); // 节点处理,把当前节点的WobNum和当前节点对象的数据传参。 submit= bpmWorkflowServices.completeWorkItem(documentBorrowingVo.getWobNum(), map); }catch(Exception e){ e.printStackTrace(); logger.error(e); } return submit; }
这个模拟流程,还有很多不完善的地方,流程逻辑可能不合实际。
代码部分,贴出来,也只是明其意即可,封装好的P8Helper,此处提供下载。