工作流SDK接口使用说明

1.需求场景 在涉及到工作流相关的业务场景时,业务数据和流程数据最好是要做到相互隔离,互不影响。因此,数据和逻辑之间,耦合度越低越好。工作流引擎作为一个独立的模块,要方便业务数据调用。
2.接口说明 启动一个流程,需要插入业务数据,也需要插入流程数据(包括流程日志、流程任务)。为了方便整合流程,流程引擎提供SDK接口调用。
流程主要有2个接口:启动流程和提交流程,其他的为辅助接口。
所有的接口功能,都封装在FlowUtil类中,提供静态方法调用。
2.1.启动流程 启动流程,只需要调用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; //相关附件 }

在调用启动流程时,并不需要传入所有参数。参数说明如下:
  1. flowId。流程Id,必填。当前启动的是哪个流程。引擎会根据该Id值查找配置项。
  2. dataId。业务数据Id,视情况而定。如果自己单独处理了业务数据(新增业务数据),则需要传递dataId,以便引擎更新该业务数据的流程状态。如果需要引擎处理业务数据,则不需要传值。
  3. userId。当前操作的用户id,必填。一般从session中取值,获取当前用户Id。如果是APP等无状态接口,则通过接口用户验证取到用户Id值。
  4. actionType。操作类型,不用填。启动流程时,不需要传值。内部会固定为NoCodeFlowNode.NODE_TYPE_START,其值为1。提交流程时,则根据实际是什么操作,就传递什么值。各种操作类型,在NoCodeFlowNode中有定义。
  5. nodeId。当前操作的节点Id,不用填。启用流程会固定查找启动节点。
  6. backNodeId。回退节点,不用填。启动流程时,不会回退。
  7. nextNodeIds。下一个节点Id列表,不用填。内部会自动获取。
  8. tableName。业务数据库表名称,必填。引擎需要去更新业务数据的流程启动人、流程节点、流程状态数据。
  9. executeSQL。是否执行业务数据SQL,必填。设置为true后,引擎会执行业务数据的SQL语句(sqlBusiness和sqlParam)。会自动判断是insert还是update。
  10. sqlBusiness。业务SQL语句,视情况而定。如果需要引擎执行业务数据,则传递SQL语句给引擎执行。支持insert和update。如果是insert,则会把新增的数据id值,返回到NoCodeResult的data属性中。
  11. sqlParam。业务SQL语句配套的参数,根据sqlBusiness而定。
  12. flowNode。当前流程节点,不用填。如果是启动流程,引擎会自动查找开启节点;如果是提交流程,引擎会根据nodeId
  13. flowProcess。流程日志对象,不用填。引擎会自动构造。
  14. flowTaskList。流程任务对象列表,不用填。引擎会自动构造。
  15. remarkField。关联节点的备注字段,不用填。自动根据节点设置项来读取。
  16. attachmentField。关联节点的附件字段,不用填。自动根据节点设置项来读取。
  17. flowRemark。备注说明内容,视情况而定。需要存储到流程日志中。如果有,就赋值。
  18. flowAttachment。相关附件内容,视情况而定。需要存储在流程日志中。如果有,就赋值。

2.2.提交流程 提交流程,调用接口FlowUtil.submitFlowInstance(NoCodeFlowParam flowParam)方法,参数与返回值,类似于启动流程。
要传递的参数,与启动流程的区别如下。
  1. nodeId。节点Id,必填。当前提交的是哪个流程节点,必填。
  2. dataId。业务数据Id,必填。提交流程时,必然是有关联的业务数据。
  3. actionType。操作类型,必填。提交流程时,必然是选择某种操作。
  4. backNodeId。如果是回退操作类型,还需要传递选择的回退节点Id。
3.调用样例 3.1.启动流程 如果由工作流引擎来处理业务数据,则需要将插入业务数据的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 sqlParam = genSqlParamForInsert(mListField); String sqlSave = genSqlQueryForDataInsert(); //获取节点信息 NoCodeFlowNode startNode = FlowService.getFlowStartNode(flowId); if (startNode == null) { mResult.setFailureInfo("没有找到启动节点信息"); return mResult; }NoCodeFlowParam flowParam = new NoCodeFlowParam(); flowParam.setTableName(mPage.getTableName()); flowParam.setFlowId(flowId); flowParam.setUserId(userId); flowParam.setExecuteSQL(true); flowParam.setSqlBusiness(sqlSave); flowParam.setSqlParam(sqlParam.toArray()); flowParam.setFlowNode(startNode); //设置备注说明与相关附件字段 setRemarkAndAttachmentField(startNode, flowParam, mapRequestParam); //暂存数据后,启动新流程。启动成功后,会将新的流程实例id赋值到data属性中 return FlowUtil.startFlowInstance(flowParam); }
3.2.提交流程 提交流程与启动流程类似,只需要多传递几个参数即可。
//确认流程提交。已经创建了流程,进行业务数据更新,以及流程状态更新。 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 sqlParam = genSqlParamForUpdate(mListField); String sqlSave = genSqlQueryForDataUpdate(); //获取节点信息 NoCodeFlowNode flowNode = FlowService.getFlowNode(flowId, nodeId); if (flowNode == null) { mResult.setFailureInfo("没有找到节点信息"); return mResult; } NoCodeFlowParam flowParam = new NoCodeFlowParam(); flowParam.setFlowId(flowId); flowParam.setUserId(userId); flowParam.setTableName(mPage.getTableName()); flowParam.setNodeId(nodeId); flowParam.setBackNodeId(backNodeId); flowParam.setExecuteSQL(true); flowParam.setSqlBusiness(sqlSave); flowParam.setSqlParam(sqlParam.toArray()); flowParam.setFlowNode(flowNode); flowParam.setDataId(dataId); flowParam.setActionType(actionType); //备注说明、相关附件信息 //设置备注说明与相关附件字段 setRemarkAndAttachmentField(flowNode, flowParam, mapRequestParam); //调用流程提交 return FlowUtil.submitFlowInstance(flowParam); }

3.3.节点定义 对节点进行定义,包含节点类型、状态、操作类型。
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 }

3.4.获取节点数据 【工作流SDK接口使用说明】辅助类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 }


    推荐阅读