一、采购计划制定的业务流程
采购计划制定的业务流程如下:(其中开始节点就是启动采购计划录入任务,即打开采购计划的录入界面)
二、建立工作流模型
(一)、工作流模型的基本信息
打开新建模型页面后,点击任意空白处,下方即弹出如下窗口。根据实际业务流程信息来填写 流程名称,名称以及描述。其中流程名称较为关键,后面编程中启动工作流需要用到。
(二)、开始节点
点击开始节点后,将弹出下面信息框。需要填写的关键信息为 Id,名称,发起人,自定义表单。
其中:
1、Id将对应 工作流任务(Task)的taskDefinitionKey
2、名称就是这个任务活动的名称(在流转信息中显示)
3、发起人填写自定义的“发起人”变量名,如applyUserId。(主要的用处就是:如果后面的活动任务需要指定改发起人做,就在对应任务节点的代理中填写此处定义的发起人变量名。如这个例子中的采购计划重新申请 就只能是原制单人做,故在对应活动节点的代理人框中需输入${applyUserId})
4、自定义表单表示 这个任务活动启动后要打开的页面连接;这个开始活动节点的自定义表单就是 工作流启动后的起始页面;在这个采购计划的例子中 自定义表单就应该填 采购计划制定的录入 对应的页面链接。
(三)、一般的活动任务节点(领导审批任务为例)
点击领导审批节点(活动任务节点)后,将弹出下面信息框。需要填写的关键信息为 Id,名称,代理,自定义表单。
其中:
1、Id将对应 工作流任务(Task)的taskDefinitionKey
2、名称就是这个任务活动的名称(在流转信息中显示)
3、点击代理弹出如下窗口:
代理人:任务执行人,设置系统中的“登录名”(loginName)。
候选人:任务执行人,多个用逗号“,”隔开。
候选组:任务执行组,多个用逗号“,”隔开,设置系统中的“角色英文名(enname)”。
注意:代理人和候选人、候选组为互斥关系;即指定了代理人,后面再指定候选人或候选组将不起作用。
代理人和候选人的区别是:代理人不需要签收任务,直接可执行任务;候选人和候选组为竞争方式分配任务,被指定人待办中都有一条任务,谁先签收谁就获得任务的执行权。
参与者可指定流程变量(EL表达式),动态指定参与者,如:${processer}
4、自定义表单 和上述第(二)节所述的开始节点一样,表示 当前活动任务节点使用的页面链接。
(四)、互斥网关
互斥网关(排他网关):条件计算为true的顺序流会被选择继续流程,有且只有一条出口,如果出现多个条件为true,则会默认选择第一条true来执行,如果没有条件输出true,流程走到这一步的时候则会报错。
互斥网关本身不需要设置任何参数,关键在于它输出的流转条件设置。
互斥网关的流转条件:
领导审批任务执行后有三个流转条件:(1)当申请单总额大于等于1万并且审核通过,流转到总经理审批(2)当申请单总额小于1万并且审核通过,流转到下达任务(3)审核不通过,流转回制单人重新申请。
流转条件(1)所填的参数信息如下:
需要填写项为 名称,流转条件;其中流转条件里的EL表达式中的变量为自定义,需要与后面代码开发的对应上,并且值只能为true或者false(布尔型)。
流转条件(2)所填的参数信息如下:
流转条件(3)所填的参数信息如下:
无网关的流传:
该采购计划案例中涉及到无网关流转的有 计划下达 和 结案这两个任务节点。
无条件流转不需要做任何流转条件的设置,活动之间的流转就是单纯的页面切换。
(五)、结束节点
结束节点简单地填写一下Id和名称即可,不用做其他设置。
三、代码开发部分
(一)数据库表的修改和流程类的继承
1.业务主表中必须要增加 proc_ins_id 字段(记录流程实例);
2.对应业务主表的实体需要继承ActEntity (T为自己的业务实体类)
(二)界面部分的代码修改
界面部分的修改主要针对 所有form页面文件。(如采购计划这部分要修改的页面文件包括了purPlanMainCloseForm.jsp;purPlanMainDraftForm.jsp;purPlanMainForm.jsp;purPlanMainOrderForm.jsp;purPlanMainQueryForm.jsp)
其次,需要增加一个审核界面(原来旧的审核页面不能用)
1、form页面的修改
针对上述所述的所有form页面文件中,需要在这些页面隐藏域里加入以下流程变量:
<%--工作流涉及的变量--%>
[/span>form:hidden path="act.taskId"/>
[/span>form:hidden path="act.taskName"/>
[/span>form:hidden path="act.taskDefKey"/>
[/span>form:hidden path="act.procInsId"/>
[/span>form:hidden path="act.procDefId"/>
[/span>form:hidden id="flag" path="act.flag"/>
页面底部(按钮下面)加入以下代码(用来显示详细的流转信息;注意把purPlanMain替换成相应的对象):
[/span>act:flowChart procInsId="${purPlanMain.act.procInsId}"/>
[/span>act:histoicFlow procInsId="${purPlanMain.act.procInsId}"/>
2、增加一个审核页面
这部分可以直接从该模块中任意一个form页面复制过来,然后加入上述1中需要添加的代码,再做如下修改:
1)添加审核人意见填写框(审核人意见保存在act.comment属性里)
[/span>div class="form-group"
[/span>label class="control-label"
[/span>form:textarea path="act.comment" class="form-control required" rows="5" maxlength="20"/>
2)为审核通过和不通过按钮添加事件
if(whichBtn=="审核通过"){
$("#flag").val("yes");
form.submit();
}else if(whichBtn=="审核不通过"){
$("#flag").val("no");
form.submit();
}
(三)后台JAVA代码
1、开始节点的后台处理代码(工作流启动)
工作流启动代码一般写在service中某个函数里(也可直接写在Controller里),在开始节点对应的页面中 点击提交按钮时被调用,以下是涉及工作流启动的代码,其中标红的是工作流启动的关键代码:
//结构{对应第一节所述的工作流模型里的流程名称,业务表名}
public static final String【】 PUR_PLAN_AUDIT = new String【】{"pur_plan", "pur_planmain"};
/
提交操作 保存并且启动工流
@param purPlanMain
*/
@Transactional(readOnly = false)
public void submitSave(PurPlanMain purPlanMain) {
/-------------------------------业务操作----------------------/
save(purPlanMain);
/-------------工作流启动操作------------------/
// 申请发起
if (StringUtils.isBlank(purPlanMain.getId())||"".equals(purPlanMain.getAct().getTaskId())||purPlanMain.getAct().getTaskId()==null){
// 用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中
identityService.setAuthenticatedUserId(purPlanMain.getCurrentUser().getLoginName());
// 启动流程
actTaskService.startProcess(PUR_PLAN_AUDIT【0】, PUR_PLAN_AUDIT【1】, purPlanMain.getId(), purPlanMain.getBillNum()); //后两个参数分别表示业务表的主键ID和在代办任务页面里显示的标题。这里就直接把业务表的单据编号作为代办任务的标题了
}
else{ // 重新编辑申请
purPlanMain.getAct().setComment(("yes".equals(purPlanMain.getAct().getFlag())?"【重申】 ":"【作废】 ")+purPlanMain.getAct().getComment());
// 完成流程任务
Map vars = Maps.newHashMap();
vars.put("reApply", "yes".equals(purPlanMain.getAct().getFlag())? true : false);
actTaskService.complete(purPlanMain.getAct().getTaskId(), purPlanMain.getAct().getProcInsId(), purPlanMain.getAct().getComment(), purPlanMain.getBillNum(), vars);
}
}
2、活动任务节点的后台处理代码
采购计划的工作流模型中各个活动节点
工作流模型中各活动节点的后台处理代码如下,在各活动节点对应的页面中 点击提交按钮时被调用。
/
保存业务表数据和工作流数据
@param purPlanMain
*/
@Transactional(readOnly = false)
public void auditSave(PurPlanMain purPlanMain) {
Map vars = Maps.newHashMap();
// 对不同环节的业务逻辑进行操作
String taskDefKey = purPlanMain.getAct().getTaskDefKey();
// 流程环节
if ("planAudit1".equals(taskDefKey)){ //判断该环节是否为 领导审批环节;此处的taskDefKey即对应着第二节(三)部分所述的活动任务节点ID
vars.put("auditPass1", "yes".equals(purPlanMain.getAct().getFlag())? true : false); //调用getFlag()获取审核人在前端页面中点击了审核通过还是不通过
vars.put("sum",purPlanMain.getPlanPriceSum()); //此处的auditPass1和sum变量对应着第二节(四)部分所述的流转条件里EL表达式中自定义变量
purPlanMain.getAct().setComment(("yes".equals(purPlanMain.getAct().getFlag())?"【同意】 ":"【不同意】 ")+purPlanMain.getAct().getComment()); //调用getComment()获取审核人在前端页面填写的审核意见并加上通过 或 不通过结论
if("yes".equals(purPlanMain.getAct().getFlag())) { //通过且金额小于1万
if(purPlanMain.getPlanPriceSum()<10000){
purPlanMain.setBillStateFlag("E"); //总额少于1万且通过领导审核,就可把业务表状态设为通过审核状态 (依赖于工作流模型,该工作流模型中只有两层审核,只需要通过领导和总经理两层人员的审批)
}
}else{
purPlanMain.setBillStateFlag("B"); //否者把业务表状态设置为不通过
}
}
else if("planAudit2".equals(taskDefKey)){
vars.put("auditPass1", "yes".equals(purPlanMain.getAct().getFlag())? true : false);
vars.put("sum",purPlanMain.getPlanPriceSum());
purPlanMain.getAct().setComment(("yes".equals(purPlanMain.getAct().getFlag())?"【同意】 ":"【不同意】 ")+purPlanMain.getAct().getComment());
if("yes".equals(purPlanMain.getAct().getFlag())) {
purPlanMain.setBillStateFlag("E"); //总经理审批通过,就可把业务表状态设为通过审核状态 (这里依赖于工作流模型,该工作流模型中只有两层审核,只需要通过领导和总经理两层人员的审批)
}else{
purPlanMain.setBillStateFlag("B");
}
}
else if ("reInput".equals(taskDefKey)){
vars.put("reApply", "yes".equals(purPlanMain.getAct().getFlag())? true : false);
//代码效果参考:http://www.jhylw.com.cn/140333671.html
purPlanMain.getAct().setComment(("yes".equals(purPlanMain.getAct().getFlag())?"【重申】 ":"【作废】 ")+purPlanMain.getAct().getComment());if( "yes".equals(purPlanMain.getAct().getFlag())){
purPlanMain.setBillStateFlag("W");//选择了重申就把业务表状态设为录入状态
}else{
purPlanMain.setBillStateFlag("V");//作废业务表
}
}
else if("planClose".equals(taskDefKey)){ //是否为结案环节
vars.put("billFlag",purPlanMain.getBillStateFlag());
}
else if("planOrder".equals(taskDefKey)){ //是否为下达环节
vars.put("billFlag",purPlanMain.getBillStateFlag());
}
else if ("end".equals(taskDefKey)){ //是否为结束环节
}
// 未知环节,直接返回
else{
return;
}
// 提交流程任务(工作流的任务ID,工作流实例ID,审核意见,自定义的条件变量)
actTaskService.complete(purPlanMain.getAct().getTaskId(), purPlanMain.getAct().getProcInsId(), //代码效果参考:http://www.jhylw.com.cn/294437683.html
purPlanMain.getAct().getComment(), vars);//保存业务表状态
save(purPlanMain);
}
Everyone has their own Time Zone. Just keep moving forward.