直接使用Workflow实现工作流功能
使用com.zving.workflow.plugin开发工作流插件,所必要条件:
1. 实现一个工作流类型扩展项
实现代码如下:
package com.zving.fastworkflow.impl.extend;
import java.util.ArrayList;
import java.util.List;
import com.zving.fastworkflow.impl.method.AuditMethod;
import com.zving.workflow.IWorkflowType;
import com.zving.workflow.WorkflowAdapter;
import com.zving.workflow.methods.ConditionMethod;
import com.zving.workflow.methods.NodeMethod;
/**
* 工作流类型,需实现IWorkflowType接口
* @author XYUU
*
*/
public class FastWorkflowType implements IWorkflowType {
public static final String ID = "FastWorkflow";
/**
* 工作流类型ID,扩展项必须实现
*/
public String getID() {
return ID;
}
/**
* 工作流类型名称,扩展项必须实现
*/
public String getName() {
return "@{Fastworkflow.Name}";
}
/**
* 获取工作流适配器
*/
public WorkflowAdapter getAdapter() {
return new FastWorkflowAdapter();
}
/**
* 添加条件方法,用来执行相应条件判断
*/
public List<ConditionMethod> getConditionMethods() {
return null;
}
/**
* 添加节点方法,用来执行工作流中的前置及后置方法
*/
public List<NodeMethod> getNodeMethods() {
ArrayList<NodeMethod> list = new ArrayList<NodeMethod>();
list.add(new AuditMethod());
return list;
}
}
2. 实现一个工作流适配器
上一步中,我们的工作流类型需要返回一个工作流适配器来完成该类工作流的特定功能;这需要我们自己实现,或直接使用空的工作流EmptyWorkflowAdapter适配器(不推荐);
这里我们自己实现一个简单的FastWorkflowAdapter,代码如下:
package com.zving.fastworkflow.impl.extend;
import com.zving.fastworkflow.IFastWorkflow;
import com.zving.fastworkflow.service.FastWorkflowService;
import com.zving.framework.collection.Mapx;
import com.zving.framework.data.Transaction;
import com.zving.framework.utility.StringUtil;
import com.zving.workflow.WorkflowAdapter;
import com.zving.workflow.core.Context;
import com.zving.workflow.core.WorkflowAction;
/**
* 工作流适配器,需要继承WorkflowAdapter
* @author XYUU
*
*/
public class FastWorkflowAdapter extends WorkflowAdapter {
/**
* 动作执行时
*/
@Override
public void onActionExecute(Context context, WorkflowAction action) {
// 这里可以编写一些通知方法,如短消息告知和单据相关的人员单据已经执行了哪些步骤
}
/**
* 步骤创建时
*/
@Override
public void onStepCreate(Context context) {
// 这里可以编写一些通知方法,如短消息告知和单据相关的人员单据已经进入了哪些步骤
}
/**
* 通知下一步
*/
@Override
public void notifyNextStep(Context context, String[] users) {
// 这里可以编写一些通知方法,如短消息提醒和单据相关的人员来处理这些工作
}
/**
* 流程删除时
*/
public void onWorkflowDelete(Transaction tran, long workflowID) {
// 这里我们需要在流程删除时修改还没有执行完的单据或内容的状态,否则这些单据或内容将无法继续工作
}
/**
* 获取变量
*/
public Mapx<String, Object> getVariables(Context context) {
// 获取内容或单据中的变量值,将这些变量放在一个Mapx中,以在后边判断
String instanceType = context.getInstance().getInstanceType();
IFastWorkflow ct = FastWorkflowService.getInstance().get(instanceType);
Mapx<String, Object> map = new Mapx<String, Object>();
String dataID = context.getInstance().getDataID();
if (ct != null && StringUtil.isNotEmpty(dataID)) {
map.putAll(ct.getDAO(dataID).toMapx());
}
return map;
}
/**
* 保存变量
*/
public boolean saveVariables(Context context) {
// 这里是保存流程处理过程中的一些变量,保存成功返回true
return true;
}
}
3. 创建工作流按钮的UI扩展点
在要加入工作流相应流程按钮的zhtml中创建一个UI扩展点,以便于可以在此界面插入工作流相应按钮;
在相应的zhtml代码中插入扩展点标识:
4. 编写一个实现此扩展点的扩展行为
该行为直接引入工作流脚本,实现刚才添加的扩展点的扩展行为,引入工作流脚本:
扩展行为的代码极其简单,如图示
package com.zving.fastworkflow.extend;
import com.zving.framework.extend.ExtendException;
import com.zving.framework.extend.action.ZhtmlContext;
import com.zving.framework.extend.action.ZhtmlExtendAction;
public class FastWorkflowUIAction extends ZhtmlExtendAction{
/**
* 扩展行为是否可用
*/
public boolean isUsable() {
return true;
}
/**
* 扩展行为引入的脚本文件
*/
public void execute(ZhtmlContext context) throws ExtendException {
context.include("fastworkflow/fastWorkflowCommonrScript.zhtml");
}
}
关于fastWorkflowCommonrScript.zhtml的代码其实是一个极其通用的JS,其代码如下:
var fastType = "${FastType}";// 内容类型
var fastIDControl = "${FastIDControl}";// 保存内容ID的控件
var toolbarID = "${ToolbarID}";// 工具栏控件的ID
Page.onLoad(function(){
// 工作流按钮
if(window.loadButtons) {
var dc = {InstanceID:'${InstanceID}',Status:'${Status}',WorkflowID:'${WorkflowID}',InstanceType:fastType};
if(dc.WorkflowID){
loadButtons(dc);
}
}
},1);
// 往编辑器的工具栏的最前面添加按钮
function workflow_addButton(strText,strIcon,strID,strEvent){
var div = $G(toolbarID);
div = div.childNodes[0].childNodes[0].childNodes[0];
new Button({
text : strText,
icon : "../"+strIcon,
id : "Workflow_"+strID,
cls : "z-btn-flat",
onclick : strEvent
}).render(div,0);
}
// 申请工作流步骤
function workflow_applyStep(workflowID,instanceID,nodeID){
var dc = {WorkflowID:workflowID,InstanceID:instanceID,NodeID:nodeID,InstanceType:fastType};
Server.sendRequest("FastWorkflow.applyStep",dc,function(response){
var dt = response.Data;
workflow_reloadButtons(dt);
Dialog.alert(response.Message);
},null,null,'json');
}
// 申请修改(己发布内容重新修改)
function workflow_restart(workflowID,instanceID){
var dc = {WorkflowID:workflowID,InstanceID:instanceID,FastID:$V(fastIDControl),InstanceType:fastType};
Server.sendRequest("FastWorkflow.restart",dc,function(response){
var dt = response.Data;
workflow_reloadButtons(dt);
Dialog.alert(response.Message);
if(window.flist){
flist(response);
}
},null,null,'json');
}
// 执行工作流动作
function workflow_commitAction(workflowID,instanceID,actionID,allowSelectUser){
var dc = {WorkflowID:workflowID,InstanceID:instanceID,ActionID:actionID,InstanceType:fastType};
if(allowSelectUser){
workflow_selectUser(function(users){
dc.NextStepUser=users;
save(function(response, func){
workflow_commitActionToServer(dc, func);
});
});
}else{
save(function(response, func){
workflow_commitActionToServer(dc, func);
});
}
}
// 提交工作流动作数据到服务器
function workflow_commitActionToServer(dc, func){
dc.FastID = $V(fastIDControl);
Server.sendRequest("FastWorkflow.commitAction",dc,function(response){
if(window.refreshData){
refreshData(response);
}
var dt = response.Data;
workflow_reloadButtons(dt);
if(func) {
func(response);
}
Dialog.alert(response.Message);
},null,null,'json');
}
// 选择下一步用户
function workflow_selectUser(func){
var diag = new Dialog("选择工作流下一步用户","../platform/userSelectDialog.zhtml",700,400);
diag.onOk = function(){
var users = $DW.getSelectedUsers();
func(users);
$D.close();
}
diag.show();
}
// 工作流临时保存
function workflow_tempSave(workflowID,instanceID,actionID){
save();
}
// 工作流发布
function workflow_publish(workflowID,instanceID,actionID){
publish(true);
}
function workflow_reloadButtons(dt){
$('#'+toolbarID+' a').each(function(i){
if(this.id && this.id.startsWith("Workflow_")){
$(this).getComponent().destroy(true);
}
});
if($("#BtnSave").length>0 && Application.hasPriv($("#BtnSave").attr("Priv"))){
}else{
return;
}
if(dt&&dt.Rows){
var rs = dt.Rows;
for(var i=0;i < rs.length;i++){
var dr = rs[i];
workflow_addButton(dr.get("Name"),dr.get("Icon"),dr.get("ID"),dr.get("Event"));
}
}
}
/**
* 加载工作流按钮
*/
function loadButtons(dc) {
Server.sendRequest("FastWorkflow.loadButtons", dc, function(response){
var dt = response.ButtonsDT;
workflow_reloadButtons(dt);
},null,null,'json');
}
5. 改写init方法及Save方法,添加Toolbar工具条
上述代码中,我们可以看到,我们需要一个Toolbar工具条来呈现工作流按钮,所以我们需要改写Save相关的方法:
首先我们在添加/编辑界面中加入
<z:toolbar id="Toolbar" theme="flat">
<z:button onClick="save()" id="BtnSave" theme="flat" Priv="SupportTicket"><img src="../icons/icon003a16.png" /><z:lang id="Common.Save">保存</z:lang></z:button>
<z:button theme="flat" onClick="print()"><img src="../icons/icon403a3.png" />打印</z:button>
<z:button theme="flat" onClick="closeX()"><img src="../icons/icon403a11.png" /><z:lang id="Common.Close">关闭</z:lang></z:button>
</z:toolbar>
这个Toolbar的ID必须记下,一会要用,还有save()方法的控件id="BtnSave"最好不要改动,设置Save保存方法的权限值,如:Priv="SupportTicket",这些最好能满足要求,且不要修改,否则需要修改相应的JS代码;
在JS脚本中加入closeX()函数,用来关闭添加/编辑对话框。
function closeX(){
ownerDialog.close();
}
在JS脚本中加入refreshData()函数,用来刷新上层数据。
function refreshData(){
ownerDialog.opener.DataGrid.loadData('grid');
}
新增一个有参数的save方法,用来执行工作流来供Dialog调用;
function save(func){
var dc = Form.getData("form1");
if(Verify.hasError()){
return;
}
var method = "SupportTicket.save";
if(!$V('ID')||$V('ID')=="null"){
method = "SupportTicket.add";
}
ownerDialog.save(func,dc,method);
}
在UI方法中修改init、add、save方法:
在init方法中加入:WorkflowID、FastType、FastIDControl、ToolbarID相应的值,这些值分别表示:工作流ID(内容对应的流程ID)、工作流类型(步骤1中新增的工作流类型的扩展项的ID)、内容或单据的ID(通常是单据或内容的主键,使我们能在工作流中获取唯一的单据及内容)、工具条ID(刚刚记下的Toolbar的ID),代码示例:
//工作流支持
long siteID = SiteBL.getCurrentSite();
long workflowID = Long.parseLong(SiteUtil.getPropertyValue(siteID, SupportTicketWorkflowID.ID));
Response.put("WorkflowID",workflowID);
Response.put("FastType",SupportTicket.ID);
Response.put("FastIDControl","ID");
Response.put("ToolbarID","Toolbar");
加入代码的位置图示:
在add方法中加入内容或单据ID返回:
// 工作流要使用
$S("ID", supportTicket.getID());
加入代码的位置图示:
在save方法中加入内容或单据ID返回:
// 工作流要使用
$S("ID", supportTicket.getID());
加入代码的位置图示:
修改弹出对话框时的调用脚本:
function save(func,dc,method){
Server.sendRequest(method,dc,function(response){
$DW.$S("ID",response.ID);
if(func){//如果有传递函数参数,则不弹出alert信息
func(response);
DataGrid.loadData('grid');
}else{
Dialog.alert(response.Message,function(){
if(response.Status==1){
DataGrid.loadData('grid');
$D.close();
}
},response.Status);
}
});
}
function add(addFlag){
var diag = new Dialog("新建工单","supportTicketDialog.zhtml?flag="+addFlag,700,340);
diag.save = save;
diag.show();
}
function edit(editFlag){
var arr = DataGrid.getSelectedValue("grid");
if(!arr||arr.length==0){
Dialog.alert("<z:lang id="Common.PleaseSelectRowFirst">请先选择一条记录</z:lang>");
return;
}
var diag = new Dialog("编辑工单","supportTicketDialog.zhtml?ID="+arr[0]+""+"&&flag="+editFlag,800,400);
diag.save = save;
diag.show();
}
注意上述例子中标红的代码,这些地方容易出错或漏改而导致出现各种问题;
6. 编写工作流脚本相应的UI类代码
package com.zving.fastworkflow.ui;
import com.zving.fastworkflow.IFastWorkflow;
import com.zving.fastworkflow.impl.code.AuditStatus;
import com.zving.fastworkflow.impl.extend.FastWorkflowType;
import com.zving.fastworkflow.service.FastWorkflowService;
import com.zving.framework.Current;
import com.zving.framework.UIFacade;
import com.zving.framework.annotation.Alias;
import com.zving.framework.annotation.Priv;
import com.zving.framework.annotation.Verify;
import com.zving.framework.data.DataTable;
import com.zving.framework.data.Transaction;
import com.zving.framework.i18n.Lang;
import com.zving.framework.ui.tag.ListAction;
import com.zving.framework.utility.Errorx;
import com.zving.framework.utility.StringUtil;
import com.zving.schema.ZWInstance;
import com.zving.workflow.core.Context;
import com.zving.workflow.core.WorkflowAction;
import com.zving.workflow.core.WorkflowException;
import com.zving.workflow.core.WorkflowUtil;
import com.zving.workflow.service.WorkflowService;
/**
* @author 王育春
* @email wyuch@zving.com
* @date 2012-1-19
*/
@Alias("FastWorkflow")
public class FastWorkflowUI extends UIFacade {
/**
* 提供流程配置的下拉选项
* @return
*/
@Priv
public DataTable getWorkflows() {
return WorkflowService.getWorkflows(FastWorkflowType.ID);
}
/*
* 本方法在编辑器的Article.init之后执行,所以Response中已经有数据了
*/
@Priv
public void bindButtons(ListAction la) {
long instanceID = 0;
String instance = Response.getString("InstanceID");
if (StringUtil.isNotEmpty(instance)) {
instanceID = Long.parseLong(instance);
}
long workflowID = Response.getLong("WorkflowID");
String instanceType = $V("InstanceType");
IFastWorkflow wf = FastWorkflowService.getInstance().get(instanceType);
boolean enableRestart = wf.getEnableRestart(this);
DataTable dt = wf.getButtons(workflowID, instanceID, enableRestart);
la.bindData(dt);
}
/**
* 载入工作流按钮
*/
@Verify(ignoreAll = true)
@Priv
public void loadButtons() {
long instanceID = 0;
String instance = Request.getString("InstanceID");
if (StringUtil.isNotEmpty(instance)) {
instanceID = Long.parseLong(instance);
}
long workflowID = Request.getLong("WorkflowID");
String instanceType = $V("InstanceType");
IFastWorkflow wf = FastWorkflowService.getInstance().get(instanceType);
boolean enableRestart = wf.getEnableRestart(this);
DataTable dt = wf.getButtons(workflowID, instanceID, enableRestart);
$S("ButtonsDT", dt);
}
/**
* 重启工作流
*/
@Priv
public void restart() {
long workflowID = Request.getLong("WorkflowID");
String instanceType = $V("InstanceType");
String dataID = Request.getString("FastID");
long instanceID = Request.getLong("InstanceID");
Transaction tran = Current.getTransaction();
boolean create = false;// 是否创建新的流程
try {
ZWInstance instance = WorkflowUtil.findInstance(instanceID);
if (instance.getWorkflowID() == workflowID) {
WorkflowUtil.restartInstance(tran, instance, "0");
} else {
create = true;
}
} catch (Exception e) {
create = true;
}
if (create) {
try {
Context context = WorkflowUtil.createInstance(tran, workflowID, instanceType, dataID, "0");
instanceID = context.getInstance().getID();
} catch (Exception e1) {
e1.printStackTrace();
fail(Lang.get("Contentworkflow.WorkflowEditFailure") + ":" + e1.getMessage());
return;
}
}
// 设置单据的值
IFastWorkflow wf = FastWorkflowService.getInstance().get(instanceType);
wf.restart(tran,instanceID,dataID,Request);
tran.commit();
$S("Data", wf.getButtons(workflowID, instanceID, false));
success(Lang.get("Contentworkflow.WorkflowOperationSuccess"));
}
/**
* 提交动作
*/
@Priv
public void commitAction() {
long workflowID = Request.getLong("WorkflowID");
long instanceID = Request.getLong("InstanceID");
int actionID = Request.getInt("ActionID");
String instanceType = $V("InstanceType");
String dataID = $V("FastID");
Transaction tran = Current.getTransaction();
IFastWorkflow wf = FastWorkflowService.getInstance().get(instanceType);
try {
WorkflowAction action = WorkflowUtil.findAction(workflowID, actionID);
try {
WorkflowUtil.findInstance(instanceID);
action.execute(tran, instanceID, $V("NextStepUser"), $V("Memo"));
} catch (WorkflowException e) {// 未找到实例则创建实例
try {
Context context = WorkflowUtil.createInstance(tran, workflowID, instanceType, dataID, "0");
instanceID = context.getInstance().getID();
context.setValue("InstanceID", instanceID);// 需要通过saveVariables()方法写入instanceID
context.setValue("Status", AuditStatus.WORKFLOW);
action.execute(context, $V("NextStepUser"), $V("Memo"));
} catch (Exception e1) {
e1.printStackTrace();
fail(Lang.get("Contentworkflow.WorkflowEditFailure") + ":" + e1.getMessage());
return;
}
}
// 设置单据的值
wf.commitAction(tran,instanceID,action,dataID,Request);
} catch (Exception e) {
e.printStackTrace();
fail(Lang.get("Contentworkflow.WorkflowEditFailure") + ":" + e.getMessage());
return;
}
tran.commit();
$S("Data", wf.getButtons(workflowID, instanceID, false));
success(Lang.get("Contentworkflow.WorkflowOperationSuccess") + Errorx.getAllMessage());
}
/**
* 应用步骤
*/
@Priv
public void applyStep() {
long workflowID = Request.getLong("WorkflowID");
long instanceID = Request.getLong("InstanceID");
int nodeID = Request.getInt("NodeID");
Transaction tran = Current.getTransaction();
try {
WorkflowUtil.applyStep(instanceID, nodeID);
success(Lang.get("Contentworkflow.ApplicationSuccessful"));
} catch (Exception e) {
e.printStackTrace();
fail(Lang.get("Contentworkflow.ApplyFilure") + ":" + e.getMessage());
return;
}
tran.commit();
String instanceType = $V("InstanceType");
IFastWorkflow wf = FastWorkflowService.getInstance().get(instanceType);
$S("Data", wf.getButtons(workflowID, instanceID, false));
success(Lang.get("Contentworkflow.ApplySetpSuccess"));
}
}
7. 实现节点方法
节点方法是用来提供前置及后置方法的,即:当流程到达某一步骤时需要做写什么操作。
package com.zving.fastworkflow.impl.method;
import com.zving.fastworkflow.IFastWorkflow;
import com.zving.fastworkflow.impl.code.AuditStatus;
import com.zving.fastworkflow.service.FastWorkflowService;
import com.zving.framework.collection.Executor;
import com.zving.framework.collection.Mapx;
import com.zving.framework.data.Transaction;
import com.zving.framework.orm.DAO;
import com.zving.workflow.core.Context;
import com.zving.workflow.methods.NodeMethod;
public class AuditMethod extends NodeMethod {
public static final String ID = "FastWorkflow.Audit";
/**
* 执行节点方法
* @param context
* @throws Exception
*/
public void execute(Context context) throws Exception {
Mapx<String, Object> params = new Mapx<String, Object>();
params.put("ID", context.getInstance().getDataID());
params.put("FastType", context.getInstance().getInstanceType());
context.getTransaction().addExecutor(new Executor(params) {
public boolean execute() {
@SuppressWarnings("unchecked")
Mapx<String, Object> params = (Mapx<String, Object>) this.params[0];
String type = params.getString("FastType");
IFastWorkflow ct = FastWorkflowService.getInstance().get(type);
DAO<?> dao = ct.getDAO(params.getString("ID"));
Transaction tran = new Transaction();
dao.setV("Status", AuditStatus.AUDITED);
tran.commit();
return true;
}
});
}
/**
* 节点方法ID
* @return
*/
public String getID() {
return ID;
}
/**
* 节点方法名称
* @return
*/
public String getName() {
return "@{Fastworkflow.Audit}";
}
}
8. 实现工作流相关的WorkflowID配置
需要指定执行相关工作流对应的内容或单据的流程ID及配置,如本例中,我们使用站点扩展配置中实现工作流配置:
<div class="z-legend"><b>快速工作流设置</b></div>
<table width="500" border="1" cellpadding="4" cellspacing="0" bordercolor="#eeeeee" class="formTable" style="margin-bottom:6px">
<tr>
<td width="160">工单审核流程:</td>
<td>
<z:select id="SupportTicketWorkflowID" method="FastWorkflow.getWorkflows" defaultblank="true" style="width:160px" value="${SupportTicketWorkflowID}"/>
</td>
</tr>
</table>
使用FastWorkflow快速接入工作流
如果我们所需要实现的工作流是一个极其简单的流程,比如只需要根据不同的步骤改变单据或内容的状态值,则我们可以直接使用fastworkflow来快速完成这类简单的工作流流程。
1. 实现一个快速工作流扩展项接口
接口代码示例:
package com.zving.zpbranch.extend;
import com.zving.fastworkflow.AbstractWorkflow;
import com.zving.fastworkflow.ui.FastWorkflowUI;
import com.zving.framework.RequestData;
import com.zving.framework.data.Transaction;
import com.zving.schema.ZPSupportTicket;
import com.zving.workflow.core.WorkflowAction;
public class SupportTicket extends AbstractWorkflow {
public static final String ID = "SupportTicket";
@Override
public String getID() {
return ID;
}
@Override
public String getName() {
return "工单审核流程";
}
/**
* 提交动作时
*/
public void commitAction(Transaction tran, long instanceID,
WorkflowAction action, String dataID, RequestData request) {
ZPSupportTicket dao = getDAO(dataID);
dao.setInstanceID(instanceID);
dao.setStatus(AuditStatus.WORKFLOW);
tran.add(dao,Transaction.UPDATE);
}
/**
* 执行重启流程时
*/
public void restart(Transaction tran, long instanceID, String dataID,
RequestData request) {
ZPSupportTicket dao = getDAO(dataID);
dao.setInstanceID(instanceID);
tran.add(dao,Transaction.UPDATE);
}
/**
* 什么时候显示重启流程按钮
*/
public boolean getEnableRestart(FastWorkflowUI fastWorkflowUI) {
return false;
}
/**
* 获取要操作的DAO
*/
public ZPSupportTicket getDAO(String dataID) {
ZPSupportTicket dao = new ZPSupportTicket();
dao.setID(dataID);
dao.fill();
return dao;
}
}
2. 修改表字段,新增InstanceID字段、Status字段
3. 在要加入工作流相应流程按钮的zhtml中创建一个UI扩展点,以便于可以在此界面插入工作流相应按钮;
同步骤3
4. 编写一个实现此扩展点的扩展行为,该行为直接引入工作流脚本;
同步骤4
5. 改写init方法及Save方法,添加Toolbar工具条;
同步骤5
6. 实现工作流相关的WorkflowID配置;
同步骤8
|
所有评论仅代表网友意见