Java实现自定义工作流

article/2025/9/30 8:32:30

这篇文章实现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,调用流程方法。


http://chatgpt.dhexx.cn/article/qJK1xvvX.shtml

相关文章

JAVA工作流的优雅实现方式

今天查找线上问题&#xff0c;看到一个让我脑洞大开的工作流实现方式。以前用过责任链模式&#xff0c;也用过模板模式实现类工作流的方式&#xff0c;但是对比这个工具&#xff0c;逊色不少&#xff0c;不卖关子了&#xff0c;就是Apache Commons Chain&#xff0c;它是Comman…

Java开源工作流引擎

http://www.open-open.com/08.htm Willow 由Huihoo Power开发详细可到其中文主页查看。 更多Willow信息 OpenWFE OpenWFE是一个开放源码的Java工作流引擎。它是一个完整的业务处理管理套件&#xff1a;一个引擎&#xff0c;一个工作列表&#xff0c;一个Web界面和一个反应器&…

Java工作流管理系统(activity6.0)

activity6.0工作流系统知识点文章 第一章 activity流程部署&#xff08;自动部署与动态BPMN部署&#xff09; 第二章 activity变量使用 第三章 activity权限控制&#xff08;代办任务查询&#xff09; 第四章 activity审核任务&#xff08;签领、完成任务、跳过节点、新增节点…

JAVA实现一个工作流引擎

介绍 工作流是一种将一系列相关的任务和活动组织起来的技术&#xff0c;以便在企业或组织中自动化或半自动化地管理业务流程。工作流技术可以帮助企业或组织更好地管理和优化业务流程&#xff0c;提高生产效率和质量&#xff0c;降低成本和风险。 JAVA作为一种面向对象编程语…

Java 3个常用工作流引擎

一&#xff1a;Java工作流框架是一种用于设计、执行和管理工作流程的技术。以下是几个常见的Java工作流框架&#xff1a; Activiti&#xff1a;Activiti是一款流行的开源Java工作流引擎&#xff0c;它基于BPMN 2.0标准&#xff0c;支持复杂的工作流程设计和管理。Activiti具有高…

Java 流行的工作流引擎

Java 流行的工作流引擎 JBPM 工作流 JBPM 是一个Java业务流程管理系统&#xff0c;是JBoss中一款开源的工作流引擎&#xff0c;是一个轻量级的&#xff0c;使用BPMN 2规范可扩展的 工作流引擎&#xff0c;也是一个工作流管理系统&#xff0c;它可以运行在任何java环境&#x…

Java工作流有哪些?如何快速掌握Java技术

工作流是什么?工作流是指两个或两个以上的人&#xff0c;为了共同的目标&#xff0c;连续的以串行或并行的方式去完成某一业务。Java工作流就是一个基于Java开发的流程框架&#xff0c;是每一个参加郑州Java软件开发培训的学员需要掌握的入门知识。那么常见的Java工作流有哪些…

JAVA工作流

一、 什么是工作流 以请假为例&#xff0c;现在大多数公司的请假流程是这样的 员工打电话&#xff08;或网聊&#xff09;向上级提出请假申请——上级口头同意——上级将请假记录下来——月底将请假记录上交公司——公司将请假录入电脑 采用工作流技术的公司的请假流程是这样…

java工作流详解

什么是工作流&#xff1f; 工作流&#xff1a;两个或两个以上的人&#xff0c;为了共同的目标&#xff0c;连续的以串行或并行的方式去完成某一业务。 业务&#xff1a;工作流所指业务涵盖了与经营相关的活动。 串行或并行&#xff1a;业务中的步骤也许以一步接着一步的方式…

Java开源工作流框架对比

什么是工作流&#xff1f; 工作流&#xff0c;是指“业务​过程的部分或整体在​计算机应用环境下的自动化”。是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。 在计算机中&#xff0c;工作流属于计算机支持的协同工作&#xff08;CSCW&#xff09;的一部分。 工作流…

极简 Java 工作流概念入门

1. 为什么需要工作流 假设我有一个请假需求&#xff0c;流程如下&#xff1a; 请假可以提交给我的上司&#xff0c;上司可以选择批准或者拒绝&#xff0c;无论批准还是拒绝&#xff0c;都会给我一个通知。 这个流程比较简单&#xff0c;我们很容易想到解决方案&#xff0c;不…

Java工作流详解(附6大工作流框架对比)

目录 1.什么是工作流 2.工作流应用场景 3.工作流实现方式 4.有哪些工作流框架? 5.1.Activiti6. 2.Flowable7. 3.Camunda8.4.jBPM9. 5.osworkflow&#xff0c;6.jflow. 10.工作流框架对比 什么是工作流工作流(Worklow) 工作流是对工作流程及其各操作步骤之间业务规则的…

smalldatetime类型

1、MS Sql Server 的smalldatetime 与datetime类型的区别 原来数据库字段为smalldatetime类型&#xff0c; update table set column 11:59:30 发现commit后db中的值为12:00:00 update table set column 11:59:29 发现commit后db中的值为11:59:00 原来smalldatetime是不存储秒…

SQL Server中smalldatetime的日期范围为何是[1900-01-01,2079-06-06]

本文目录列表&#xff1a; 1、SQL Server中的基准日期 2、smalldatetime的日期范围 3、smalldatetime的日期范围和无符号2字节整数的关系 4、总结语 5、参考清单列表 SQL Server中的基准日期 SQL Server 中针对datetime和smalldatetime这两个日期时间数据类型提供一个基准日期&…

smalldatetime mysql_SQL 中 date 与datetime的区别

潇潇雨雨 我们看看这几个数据库中(mysql、oracle和sqlserver)如何表示时间mysql数据库&#xff1a;它们分别是 date、datetime、time、timestamp和year。date &#xff1a;“yyyy-mm-dd”格式表示的日期值 time &#xff1a;“hh:mm:ss”格式表示的时间值 datetime&#xff1a;…

SQL datetime和smalldatetime区别

datetime 存储大小8个字节&#xff0c;精确到分后的3为小数&#xff0c;日期范围从1753 年 1 月 1 日到 9999 年 12 月 31 日&#xff1b;而 smalldatetime存储大小为4个字节&#xff0c;精确到分&#xff0c;日期范围从1900 年 1 月 1 日到 2079 年 6 月 6 日。 参考 http://m…

170406回顾-SQL Server的smalldatetime类型比较

在比较SQL Server的类型为smalldatetime字段时出现下面的错误:将 expression 转换为数据类型 smalldatetime 时出现算术溢出错误 正确的比较方法如下:将long型转换为时间格式的字符串,再与smalldatetime类型的字段比较 SELECT * FROM tablename WHERE EditFlag > 2017/…

【SQL Server】将字符串转换为 smalldatetime 数据类型时失败

如下图&#xff0c;我在执行下列语句时遇到了“将字符串转换为 smalldatetime 数据类型时失败”的报错 然后去搜了一下&#xff0c;原来是存储过程的问题,我看的是其他博主的解答&#xff08;但是还没有按照博主的方法实践过&#xff09;&#xff0c;内容详情有需要的自己去搜一…

android笔记1(activity跳转)

1.利用Android studio创建一个新项目&#xff0c;项目结构如下。 2.创建一个新的activity&#xff0c;用于跳转。 3.修改两个activity的布局&#xff0c;主activity中有一个输入文本框和一个按钮&#xff0c;实现点击按钮发送文本框的内容到第二个activity。第二个activity中有…

Activity 跳转的生命周期变化

1&#xff09;Activity1跳转到Activity2的生命周期流程 1.Activity1启动&#xff1a;Activity1: onCreate()Activity1: onStart()Activity1: onResume()2.点击按钮跳转到Activity2:Activity1: onPause()Activity2: onCreate()Activity2: onStart()Activity2: onResume()Activit…