这篇文章实现java自定义工作流程,对工作流不太熟悉的可以先看下工作流相关文章:
工作流
相关表结构、实体创建
流程主表:tbl_workflow_requestbase(这里以项目工地工作流为例)
CREATE TABLE `tbl_workflow_requestbase` (`requestid` bigint(20) NOT NULL AUTO_INCREMENT,`projectno` varchar(255) COLLATE utf8_bin NOT NULL,`workflowid` bigint(20) NOT NULL,`entityid` bigint(20) NOT NULL,`requestname` varchar(255) COLLATE utf8_bin DEFAULT NULL,`currentoperatorid` varchar(255) COLLATE utf8_bin DEFAULT NULL,`status` smallint(6) DEFAULT NULL,`creator` varchar(50) COLLATE utf8_bin DEFAULT NULL,`createtime` datetime DEFAULT NULL,`lastoperatorid` bigint(20) DEFAULT NULL,`lastoperatetime` datetime DEFAULT NULL,PRIMARY KEY (`requestid`)
) ENGINE=InnoDB AUTO_INCREMENT=162561 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
entity
@ApiModelProperty("流程ID")@Id@Column(name = "requestid")private String requestId;@ApiModelProperty("工地编码")@Column(name = "projectno")private String projectNo;@ApiModelProperty("流程类型ID")@Column(name = "workflowid")private String workflowId;@ApiModelProperty("业务ID")@Column(name = "entityid")private String entityId;@ApiModelProperty("流程标题")@Column(name = "requestname")private String requestName;@ApiModelProperty("当前操作人ID")@Column(name = "currentoperatorid")private String currentOperatorId;@ApiModelProperty("状态")@Column(name = "status")private Integer status;@ApiModelProperty("创建人")@Column(name = "creator")private String creator;@ApiModelProperty("创建时间")@Column(name = "createtime")private Date createTime;@ApiModelProperty("最后操作人ID")@Column(name = "lastoperatorid")private String lastOperatorId;@ApiModelProperty("最后操作时间")@Column(name = "lastoperatetime")private Date lastOperateTime;
然后是工作流节点表:tbl_workflow_requestnode
CREATE TABLE `tbl_workflow_requestnode` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`requestid` bigint(20) NOT NULL,`operatorid` varchar(500) COLLATE utf8_bin NOT NULL,`nodetype` smallint(6) DEFAULT NULL,`status` smallint(6) DEFAULT NULL,`sort` smallint(6) DEFAULT NULL,`createtime` datetime DEFAULT NULL,`modifytime` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=54460 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
entity
@ApiModelProperty("ID")@Idprivate Integer id;@ApiModelProperty("流程ID")@Column(name = "requestid")private String requestId;@ApiModelProperty("操作人")@Column(name = "operatorid")private String operatorId;@ApiModelProperty("节点类型")@Column(name = "nodetype")private Integer nodeType;@ApiModelProperty("状态")@Column(name = "status")private Integer status;@ApiModelProperty("顺序")@Column(name = "sort")private Integer sort;@ApiModelProperty("创建时间")@Column(name = "createtime")private String createTime;@ApiModelProperty("更新时间")@Column(name = "modifytime")private String modifyTime;
这里简单介绍下,工作流主表:用户发起的一条涉及多用户操作的流程,记录流程详细信息,通过requestId关联流程节点表信息。
流程节点信息表:记录各个节点用户的操作。
定义流程的方法,创建、执行、退回
/*** 创建工作流*/public ResponseResult create(CreateWorkflowVo createWorkflowVo) {// 同一个业务ID不能重复多线程创建流程synchronized (createWorkflowVo.getEntityId()) {return handleAction("/workflow/create", createWorkflowVo);}}/*** 执行工作流,往下一节点走*/public ResponseResult execute(ExecuteWorkflowVo executeWorkflowVo) {// 同一条流程不能重复操作synchronized (executeWorkflowVo.getRequestId()) {return handleAction("/workflow/execute", executeWorkflowVo);}}/*** 退回工作流*/public ResponseResult reject(RejectWorkflowVo rejectWorkflowVo) {// 同一条流程不能重复操作synchronized (rejectWorkflowVo.getRequestId()) {return handleAction("/workflow/reject", rejectWorkflowVo);}}private ResponseResult handleAction(String action, Object vo) {Asserts.notEmpty(uri, "请求地址");try {String res = restTemplate.postForObject(uri + action, vo, String.class);if (!StringUtils.isEmpty(res)) {ResponseResult responseResult = JSONObject.parseObject(res, ResponseResult.class);return responseResult;}} catch (Exception ex) {log.error("调用工作流服务失败:action=" + action, ex);}throw new GlobalException("12000", "调用工作流服务失败,action=" + action + ":无法获取请求数据");}
执行对应流程创建、执行、退回的方法,会调用执行定义好的一个工作流工程的回调函数,把函数的指针(地址)作为参数传递给另一个函数,返回结果。uri=工程的ip+端口
如下:
/*** 创建工作流** @param createWorkflowVo* @return*/@PostMapping("create")public ResponseResult<String> create(@RequestBody CreateWorkflowVo createWorkflowVo) {return processService.createFlow(createWorkflowVo);}/*** 流程往下走** @param executeWorkflowVo* @return*/@PostMapping("execute")public ResponseResult<String> execute(@RequestBody ExecuteWorkflowVo executeWorkflowVo) {return processService.executeFlow(executeWorkflowVo);}/*** 退回** @param rejectWorkflowVo* @return*/@RequestMapping("reject")public ResponseResult<String> reject(@RequestBody RejectWorkflowVo rejectWorkflowVo) {return processService.rejectFlow(rejectWorkflowVo);}
service代码实现:
//创建工作流@Override@Transactional(propagation = Propagation.REQUIRED)public ResponseResult<String> createFlow(CreateWorkflowVo createWorkflowVo) {log.info("请求参数: {}", createWorkflowVo);assertNotNull(createWorkflowVo);WorkflowBase base = workflowBaseMapper.selectByPrimaryKey(createWorkflowVo.getWorkflowBaseId());if (base == null) {throw new GlobalException("120004", "找不到Workflow base配置表信息");}// 回调地址不能为空Asserts.notNull(base.getActionUrl(), "actionUrl");WorkflowRequestBase srchBase = new WorkflowRequestBase();srchBase.setEntityId(createWorkflowVo.getEntityId());srchBase.setStatus(0);Integer count = workflowRequestBaseMapper.selectCount(srchBase);if (count != null && count > 0) {throw new GlobalException("120011", "同一条业务不能重复创建流程");}WorkflowRequestBase requestBase = setRequestBase(createWorkflowVo);// 插入流程审批表workflowRequestBaseMapper.insertReturnKeys(requestBase);log.info("id = {}", requestBase.getRequestId());if (requestBase.getRequestId() != null && requestBase.getRequestId().length() > 0) {List<String> requestNodes = createWorkflowVo.getRequestNodes();// 插入流程节点表int sort = 0;requestNodes.add("0");// 归档节点List<WorkflowRequestNode> nodesList = new ArrayList<WorkflowRequestNode>();for (String userid : requestNodes) {WorkflowRequestNode node = new WorkflowRequestNode();node.setRequestId(requestBase.getRequestId());node.setOperatorId(userid);node.setNodeType("0".equalsIgnoreCase(userid) ? 1 : 0);node.setStatus(0);node.setSort(sort++);nodesList.add(node);}workflowRequestNodeMapper.batchInsert(nodesList);} else {throw new GlobalException("120005", "无法获取Request ID");}return ResponseResult.success(String.valueOf(requestBase.getRequestId()));}//执行流程/*** 流程往下走** @param executeWorkflowVo* @return*/@Override@Transactional(propagation = Propagation.REQUIRED)public ResponseResult executeFlow(ExecuteWorkflowVo executeWorkflowVo) {WorkflowRequestBase requestBase = getRequestBase(executeWorkflowVo.getRequestId());// 如果工作流的状态不对,不能继续操作if (requestBase.getStatus() != 0) {throw new GlobalException("120010", "该工作流可能已归档,无法继续操作");}// 只有当前操作人才能操作,防重复提交if (!requestBase.getCurrentOperatorId().equalsIgnoreCase(String.valueOf(executeWorkflowVo.getCurrentOperatorId()))) {throw new GlobalException("120007", "当前操作人不合法");}WorkflowRequestNode nextNode = null;// 如果需要插入节点if (executeWorkflowVo.isInsertNode()) {if (CollectionUtils.isEmpty(executeWorkflowVo.getNodes())) {throw new GlobalException("120011", "插入的节点操作人不能为空");}// 获取当前操作人节点及以后的节点List<WorkflowRequestNode> afterNodes = workflowRequestNodeMapper.selectNextOperator(executeWorkflowVo.getRequestId(),executeWorkflowVo.getCurrentOperatorId());Integer initSort = afterNodes.get(0).getSort();List<String> nodes = executeWorkflowVo.getNodes();List<WorkflowRequestNode> insertNodes = Lists.newArrayList();for (int i = 0; i < nodes.size(); i ++) {initSort ++;WorkflowRequestNode eachNode = new WorkflowRequestNode();eachNode.setRequestId(executeWorkflowVo.getRequestId());eachNode.setNodeType(0);// 不是归档节点eachNode.setOperatorId(executeWorkflowVo.getNodes().get(0));eachNode.setStatus(0);eachNode.setSort(initSort);eachNode.setCreateTime(DateUtil.getCurrentDateTime());eachNode.setModifyTime(DateUtil.getCurrentDateTime());if (i == 0) {nextNode = eachNode;}insertNodes.add(eachNode);}// 将当前操作人节点后面的节点sort都往后推移for (WorkflowRequestNode node : afterNodes) {initSort ++;node.setSort(initSort);// 更新workflowRequestNodeMapper.updateByPrimaryKeySelective(node);}// 插入节点workflowRequestNodeMapper.batchInsert(insertNodes);} else {// 查询下一节点操作人List<WorkflowRequestNode> nextNodes = workflowRequestNodeMapper.selectNextOperator(executeWorkflowVo.getRequestId(),executeWorkflowVo.getCurrentOperatorId());if (CollectionUtils.isEmpty(nextNodes)) {throw new GlobalException("120008", "流程下一节点为空!");}nextNode = nextNodes.get(0);}Integer nextNodeType = nextNode.getNodeType();String nextOperatorId = nextNode.getOperatorId();// 修改当前操作人的状态为审核通过WorkflowRequestNode requestNode = new WorkflowRequestNode();requestNode.setStatus(1);requestNode.setModifyTime(DateUtil.getCurrentDateTime());updateRequestNode(requestNode, executeWorkflowVo.getRequestId(),executeWorkflowVo.getCurrentOperatorId());// 修改流程审批表的当前操作人为查询出来的下一节点操作人WorkflowRequestBase updateBase = new WorkflowRequestBase();updateBase.setCurrentOperatorId(nextOperatorId);updateBase.setLastOperatorId(executeWorkflowVo.getCurrentOperatorId());updateBase.setLastOperateTime(DateUtil.currentDate());updateRequestBase(updateBase, executeWorkflowVo.getRequestId(),executeWorkflowVo.getCurrentOperatorId());// 记录一条操作日志recordRequestLog(executeWorkflowVo.getRequestId(), executeWorkflowVo.getCurrentOperatorId(),nextOperatorId, 1, "已审批");// 如果下一节点是归档节点,则直接归档if (nextNodeType == 1) {boolean flag = finish(requestBase.getWorkflowId(), requestBase.getEntityId(),executeWorkflowVo.getRequestId());log.info("归档结果,{}", flag);}return ResponseResult.success();}/*** 流程退回** @param rejectWorkflowVo* @return*/@Override@Transactional(propagation = Propagation.REQUIRED)public ResponseResult rejectFlow(RejectWorkflowVo rejectWorkflowVo) {WorkflowRequestBase requestBase = getRequestBase(rejectWorkflowVo.getRequestId());// 如果工作流的状态不对,不能继续操作if (requestBase.getStatus() != 0) {throw new GlobalException("120010", "该工作流可能已归档,无法继续操作");}// 只有当前操作人才能操作,防重复提交if (!requestBase.getCurrentOperatorId().equalsIgnoreCase(String.valueOf(rejectWorkflowVo.getCurrentOperatorId()))) {throw new GlobalException("120007", "当前操作人不合法");}// 修改当前操作人的状态为审核通过WorkflowRequestNode requestNode = new WorkflowRequestNode();requestNode.setStatus(-1);requestNode.setModifyTime(DateUtil.getCurrentDateTime());updateRequestNode(requestNode, rejectWorkflowVo.getRequestId(),rejectWorkflowVo.getCurrentOperatorId());// 修改流程审批表的当前操作人为查询出来的下一节点操作人WorkflowRequestBase updateBase = new WorkflowRequestBase();updateBase.setStatus(-1);updateBase.setLastOperatorId(rejectWorkflowVo.getCurrentOperatorId());updateBase.setLastOperateTime(DateUtil.currentDate());updateRequestBase(updateBase, rejectWorkflowVo.getRequestId(),rejectWorkflowVo.getCurrentOperatorId());// 记录一条操作日志recordRequestLog(rejectWorkflowVo.getRequestId(), rejectWorkflowVo.getCurrentOperatorId(),null, 1, "已退回:" + rejectWorkflowVo.getRejectNote());boolean flag =callbackAction(requestBase.getWorkflowId(), requestBase.getEntityId(), -1, rejectWorkflowVo.getRejectNote());log.info("退回结果,{}", flag);return ResponseResult.success();}
相关Vo
创建工作流
public class CreateWorkflowVo implements Serializable {@ApiModelProperty("流程标题")private String requestName;@ApiModelProperty("哪种类型的流程")private String workflowBaseId;@ApiModelProperty("实际业务ID")private String entityId;@ApiModelProperty("工地编码")private String projectNo;@ApiModelProperty("提交人ID")private String creator;@ApiModelProperty("流程节点处理人")private List<String> requestNodes;}
执行工作流,走向下一节点
public class ExecuteWorkflowVo implements Serializable {/*** 工作流ID*/private String requestId;/*** 当前操作人ID*/private String currentOperatorId;/*** 是否插入节点*/private boolean insertNode;/*** 节点*/private List<String> nodes;}
退回工作流
public class RejectWorkflowVo implements Serializable {/*** 工作流ID**/private String requestId;/*** 当前操作人ID*/private String currentOperatorId;/*** 退回原因*/private String rejectNote;}
工作流流程用户在创建、往下执行、退回调用对应的自定义工作流方法,举例:一条由工地项目经理发起的流程,设置涉及的流程节点:项目经理、项目主管、片区工程经理、业主。执行创建流程:
CreateWorkflowVo workflow = new CreateWorkflowVo();workflow.setProjectNo(contractMeasure.getClientNo());workflow.setEntityId(contractMeasure.getId());workflow.setCreator(Integer.toString(xmjl));workflow.setWorkflowBaseId("12");workflow.setRequestName("关于××审批");//创建审批节点List<String> requestNodes = new ArrayList<String>();//项目主管节点requestNodes.add(Integer.toString(xmzg));//添加业主节点requestNodes.add(contractMeasure.getClientNo());workflow.setRequestNodes(requestNodes);ResponseResult result = workflowUtil.create(workflow);
查看流程主表,会有一条新的记录
查看requestid关联的流程节点表
后面节点审批就省略了,无非也是封装对应的参数Vo,调用流程方法。