在涉及到工作流相关的业务场景时,业务数据和流程数据最好是要做到相互隔离,互不影响。因此,数据和逻辑之间,耦合度越低越好。工作流引擎作为一个独立的模块,要方便业务数据调用。
启动一个流程,需要插入业务数据,也需要插入流程数据(包括流程日志、流程任务)。为了方便整合流程,流程引擎提供SDK接口调用。
流程主要有2个接口:启动流程和提交流程,其他的为辅助接口。
所有的接口功能,都封装在FlowUtil类中,提供静态方法调用。
启动流程,只需要调用FlowUtil.startFlowInstance(NoCodeFlowParam flowParam)方法即可,传入NoCodeFlowParam对象参数,返回NoCodeResult对象。方法自动处理流程数据,如果传入了业务数据SQL语句,也会处理业务数据。
NoCodeFlowParam定义如下:
public class NoCodeFlowParam {
private int flowId;
private int dataId;
private int userId;
private int actionType; //操作类型
private int nodeId; //当前操作节点
private int backNodeId; //回退、拒绝、驳回后的节点
private String nextNodeIds; //下一节点
private String tableName; //业务数据库表名称
private boolean executeSQL; //是否执行更新或插入业务数据SQL
private String sqlBusiness; //业务数据SQL
private Object[] sqlParam; //业务数据SQL参数
private NoCodeFlowNode flowNode; //当前流程节点
private NoCodeFlowProcess flowProcess; //处理日志
private List flowTaskList; //要发布的任务列表
private int remarkField; //备注关联字段id
private int attachmentField; //附件关联字段id
private String flowRemark; //备注信息
private String flowAttachment; //相关附件
}
在调用启动流程时,并不需要传入所有参数。参数说明如下:
提交流程,调用接口FlowUtil.submitFlowInstance(NoCodeFlowParam flowParam)方法,参数与返回值,类似于启动流程。
要传递的参数,与启动流程的区别如下。
如果由工作流引擎来处理业务数据,则需要将插入业务数据的SQL语句以及SQL参数,传递到接口方法中。如下代码所示。
//提交发起流程表单。添加业务表单数据,发起流程。
public NoCodeResult submitFlowStart(HttpSession httpSession, Map mapRequestParam, int flowId) {
if (!baseValidate(httpSession, mapRequestParam, flowId)) {
return mResult;
}
//获取发起流程节点字段列表
mListField = FlowService.getFlowStartPageFieldList(flowId);
//将request的数据,存入字段列表
setFieldValueFromRequestForSave();
//验证字段数据
if (!validateFieldValueForSave()) {
return mResult;
}
//从session中获取当前用户id
int userId = StringUtil.convertToInt(httpSession.getAttribute(NoCodeUser.SESSION_USERID));
//保存业务数据的SQL和参数
List
提交流程与启动流程类似,只需要多传递几个参数即可。
//确认流程提交。已经创建了流程,进行业务数据更新,以及流程状态更新。
public NoCodeResult submitFlowAction(HttpSession httpSession, Map mapRequestParam, int flowId, int nodeId, int dataId) {
if (!baseValidate(httpSession, mapRequestParam, flowId, dataId)) {
return mResult;
}
//获取操作类型
int actionType = StringUtil.convertToInt(StringUtil.convertStringArrayToString(mapRequestParam.get("actionType")));
if (actionType <= 0) {
mResult.setFailureInfo("请选择操作类型");
return mResult;
}
int backNodeId = StringUtil.convertToInt(StringUtil.convertStringArrayToString(mapRequestParam.get("backNodeId")));
if (actionType == NoCodeFlowNode.NODE_ACTION_BACK && backNodeId <= 0) {
mResult.setFailureInfo("退回操作未选择回退节点");
return mResult;
}
//TODO 需要对当前节点类型进行判断,如果是驳回修改的,那么需要更新原始表单。
//从session中获取当前用户id
int userId = StringUtil.convertToInt(httpSession.getAttribute(NoCodeUser.SESSION_USERID));
//获取当前节点的字段列表
mListField = FlowService.getFlowActionPageFieldList(flowId, nodeId);
if (mListField == null || mListField.size() == 0) {
mResult.setSuccessInfo("无字段需要保存");
return mResult;
}
//将request的数据,存入字段列表
setFieldValueFromRequestForSave();
//验证字段数据
if (!validateFieldValueForSave()) {
return mResult;
}
//保存业务数据的SQL和参数
List
对节点进行定义,包含节点类型、状态、操作类型。
public class NoCodeFlowNode {
//region 流程节点类型
public final static int NODE_TYPE_START = 1; //开始节点
public final static int NODE_TYPE_END = 2; //结束节点
public final static int NODE_TYPE_SERIAL = 3; //串行节点
public final static int NODE_TYPE_BRANCH = 4; //分支节点
public final static int NODE_TYPE_AND_SINGLE = 5; //并行单审节点
public final static int NODE_TYPE_AND_MULTI = 6; //并行多审节点
public final static int NODE_TYPE_AND_ALL = 7; //会签节点
//endregion
//region 流程节点操作类型
public static final int NODE_ACTION_SAVE = 0; //暂存
public static final int NODE_ACTION_SUBMIT = 1; //提交
public static final int NODE_ACTION_CANCEL = 2; //撤回
public static final int NODE_ACTION_REJECT = 8; //驳回修改
public static final int NODE_ACTION_REFUSE = 9; //审核拒绝
public static final int NODE_ACTION_APPROVE = 10; //审核通过
public static final int NODE_ACTION_READ = 11; //阅读
public static final int NODE_ACTION_FORWARD = 12; //转发
public static final int NODE_ACTION_BACK = 49; //退回
public static final int NODE_ACTION_FREEZE = 50; //冻结
public static final int NODE_ACTION_UNFREEZE = 51; //解冻
public static final int NODE_ACTION_DELETE = 98; //删除
public static final int NODE_ACTION_ABANDON = 99; //作废
public static final int NODE_ACTION_FINISH = 100; //完结
//endregion
//region 流程节点状态
public static final int NODE_STATE_NO_SUBMIT = 0; //未提交
public static final int NODE_STATE_HAS_SUBMIT = 1; //已提交
public static final int NODE_STATE_CANCEL = 2; //已撤回
public static final int NODE_STATE_REJECT = 8; //驳回修改
public static final int NODE_STATE_REFUSE = 9; //审核拒绝
public static final int NODE_STATE_APPROVE = 10; //审核通过
public static final int NODE_STATE_READ = 11; //已阅读
public static final int NODE_STATE_FORWARD = 12; //已转发
public static final int NODE_STATE_BACK = 49; //退回
public static final int NODE_STATE_FREEZE = 50; //已冻结
public static final int NODE_STATE_UNFREEZE = 51; //已解冻
public static final int NODE_STATE_DELETE = 98; //已删除
public static final int NODE_STATE_ABANDON = 99; //已作废
public static final int NODE_STATE_FINISHED = 100; //已完结
//endregion
}
辅助类FlowUtil中还封装了其他方法,以便方便的获取流程相关的数据。
主要就是获取流程节点类型、状态、操作类型的描述说明、显示样式颜色等。
public class FlowUtil {
//region 节点状态、类型、动作,值与名称转换
//获取节点值与名称的map表
public static Map getNodeTypeMap() {
Map mapNodeType = new LinkedHashMap<>();
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_START, "开始节点");
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_END, "结束节点");
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_SERIAL, "串行节点");
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_BRANCH, "分支节点");
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_AND_SINGLE, "并行单审节点");
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_AND_MULTI, "并行多审节点");
mapNodeType.put(NoCodeFlowNode.NODE_TYPE_AND_ALL, "会签节点");
return mapNodeType;
}
//根据节点类型值,获取节点名称
public static String getNodeTypeName(int nodeType) {
return getNodeTypeMap().get(nodeType);
}
//获取流程状态值与名称map表
public static Map getNodeStateMap() {
Map mapNodeState = new LinkedHashMap<>();
mapNodeState.put(NoCodeFlowNode.NODE_STATE_NO_SUBMIT, "未提交");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_HAS_SUBMIT, "已提交");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_APPROVE, "已通过");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_REFUSE, "已拒绝");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_REJECT, "已驳回");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_CANCEL, "已撤回");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_READ, "已阅读");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_FORWARD, "已转发");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_BACK, "已退回");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_FREEZE, "已冻结");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_UNFREEZE, "已解冻");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_DELETE, "已删除");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_ABANDON, "已作废");
mapNodeState.put(NoCodeFlowNode.NODE_STATE_FINISHED, "已完结");
return mapNodeState;
}
//根据节点状态值,获取节点状态名称
public static String getNodeStateName(int nodeState) {
return getNodeStateMap().get(nodeState);
}
//获取操作类型值与名称map表
public static Map getNodeActionMap() {
Map mapNodeState = new LinkedHashMap<>();
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_SAVE, " 暂存");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_SUBMIT, "提交");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_CANCEL, "撤回");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_APPROVE, "审核通过");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_REFUSE, "审核拒绝");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_REJECT, "驳回修改");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_FORWARD, "转发");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_READ, "阅读");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_BACK, "退回");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_FREEZE, "冻结");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_UNFREEZE, "解冻");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_DELETE, "删除");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_ABANDON, "作废");
mapNodeState.put(NoCodeFlowNode.NODE_ACTION_FINISH, "完结");
return mapNodeState;
}
//根据节点操作类型值,获取操作类型名称
public static String getNodeActionName(int nodeAction) {
return getNodeActionMap().get(nodeAction);
}
//根据节点动作,获取节点状态
public static int getNodeStateByAction(int nodeAction) {
switch (nodeAction) {
case NoCodeFlowNode.NODE_ACTION_SAVE: //暂存
return NoCodeFlowNode.NODE_STATE_NO_SUBMIT; //未提交
case NoCodeFlowNode.NODE_ACTION_SUBMIT: //提交
return NoCodeFlowNode.NODE_STATE_HAS_SUBMIT; //已提交
case NoCodeFlowNode.NODE_ACTION_CANCEL: //撤回
return NoCodeFlowNode.NODE_STATE_CANCEL; //已撤回
case NoCodeFlowNode.NODE_ACTION_APPROVE: //审核通过
return NoCodeFlowNode.NODE_STATE_APPROVE; //已审核通过
case NoCodeFlowNode.NODE_ACTION_REFUSE: //审核拒绝
return NoCodeFlowNode.NODE_STATE_REFUSE; //已审核拒绝
case NoCodeFlowNode.NODE_ACTION_REJECT: //驳回修改
return NoCodeFlowNode.NODE_STATE_REJECT; //已驳回修改
case NoCodeFlowNode.NODE_ACTION_FORWARD: //转发
return NoCodeFlowNode.NODE_STATE_FORWARD; //已转发
case NoCodeFlowNode.NODE_ACTION_READ: //阅读
return NoCodeFlowNode.NODE_STATE_READ; //已阅读
case NoCodeFlowNode.NODE_ACTION_BACK: //退回
return NoCodeFlowNode.NODE_STATE_BACK; //已退回
case NoCodeFlowNode.NODE_ACTION_FREEZE: //冻结
return NoCodeFlowNode.NODE_STATE_FREEZE; //已冻结
case NoCodeFlowNode.NODE_ACTION_UNFREEZE: //解冻
return NoCodeFlowNode.NODE_STATE_UNFREEZE; //已解冻
case NoCodeFlowNode.NODE_ACTION_DELETE: //删除
return NoCodeFlowNode.NODE_STATE_DELETE; //已删除
case NoCodeFlowNode.NODE_ACTION_ABANDON: //作废
return NoCodeFlowNode.NODE_STATE_ABANDON; //已作废
case NoCodeFlowNode.NODE_ACTION_FINISH: //完结
return NoCodeFlowNode.NODE_STATE_FINISHED; //已完结
}
return NoCodeFlowNode.NODE_STATE_NO_SUBMIT;
}
//endregion
//region 节点状态、动作、类型,文字颜色样式
//获取节点值与名称的map表
public static Map getNodeTypeCssMap() {
Map mapNodeTypeCss = new LinkedHashMap<>();
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_START, "text-info");
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_END, "text-muted");
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_SERIAL, "text-success");
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_BRANCH, "text-warning");
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_AND_SINGLE, "text-danger");
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_AND_MULTI, "text-danger");
mapNodeTypeCss.put(NoCodeFlowNode.NODE_TYPE_AND_ALL, "text-danger");
return mapNodeTypeCss;
}
//根据节点类型获取文字样式名称
public static String getNodeTypeCss(int nodeType) {
return getNodeTypeCssMap().get(nodeType);
}
//获取流程状态值与名称map表
public static Map getNodeStateCssMap() {
Map mapNodeStateCss = new LinkedHashMap<>();
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_NO_SUBMIT, "");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_HAS_SUBMIT, "text-success");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_APPROVE, "text-info");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_REFUSE, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_REJECT, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_CANCEL, "text-warning");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_READ, "text-success");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_FORWARD, "text-success");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_BACK, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_FREEZE, "text-warning");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_UNFREEZE, "text-info");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_DELETE, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_ABANDON, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_STATE_FINISHED, "text-muted");
return mapNodeStateCss;
}
//根据节点状态获取文字样式名称
public static String getNodeStateCss(int nodeState) {
return getNodeStateCssMap().get(nodeState);
}
//获取操作类型值与名称map表
public static Map getNodeActionCssMap() {
Map mapNodeStateCss = new LinkedHashMap<>();
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_SAVE, "");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_SUBMIT, "text-success");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_CANCEL, "text-warning");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_APPROVE, "text-info");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_REFUSE, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_REJECT, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_FORWARD, "text-success");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_READ, "text-success");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_BACK, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_FREEZE, "text-warning");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_UNFREEZE, "text-info");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_DELETE, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_ABANDON, "text-danger");
mapNodeStateCss.put(NoCodeFlowNode.NODE_ACTION_FINISH, "text-muted");
return mapNodeStateCss;
}
//根据节点类型获取文字样式名称
public static String getNodeActionCss(int nodeAction) {
return getNodeActionCssMap().get(nodeAction);
}
//endregion
}