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