最近的项目有涉及审批流程业务,为了不增加系统的复杂度,不想引入开源的流程引擎(市面上开源流程引擎功能都比较齐全,本项目的流程审批功能没有那么复杂),于是自己决定设计一套简单易用的审批流程来。
审批流程相信大家都有了解。比如请假流程审批,办公物品领取申请流程审批,员工转正流程审批等等。无论是何种场景的审批流程,我们都可以抽象为「提交节点」,「审批节点」。
❝提交节点:流程提交人提交进入审批流程 审核节点:流程审批人审批流程 ❞
流程审核
因此我们需要写两个方法来执行审批流程。
但是在执行审批流程之前,需要配置审批流程。
由于审批结束时,如果审批不通过,还有可能从头开始进行审批。那么需要设置可重复审批次数。所以在审批流程配置时可增加字段可审批轮次times
。
流程配置
如上图,设计了一个流程配置主表,一个流程节点分表。它们是一对多的关系。
流程配置后就是流程执行后的审批记录了。
审核记录
审批记录主要存储审批信息,主要字段见上图。
以上的流程设计比较简单,没有考虑驳回上一环节,指定审批人等情况。有兴趣的童鞋可以思考思考。
理清流程审批的思路,接下来就是创建数据库表了,这里主要创建3张表。
流程配置表:as_config
流程审批人表:as_node
审批记录表:as_process
接下来就是程序开发了。
由于流程的配置就是简单的增删改查,这里就不作详细的介绍了。我们主要来说说「提交功能」和「审批功能」。
在提交审批前需要提交哪些参数呢?如上图,流程编号,业务单据号,审批意见,审批状态,审批的业务模块,这些都是需要提交的参数。
提交审批时要做的第一件事就是参数的校验:
Pair<String, String> pair = validateParamAndReturn(createParam);
这里主要校验参数不能为空,然后我们返回重要的参数流程编号,业务单据号。
接下来需要将审核层级数初始化:
//初始化层级:1
process.initLevel();
设置审核轮次:
//设置审核次数
byte currentTimes = validateConfigAndReturnCurrentTimes(code, sheetNo);
process.setTimes(currentTimes);
validateConfigAndReturnCurrentTimes()
会获取当前审核的轮次,这里的审核轮次计算逻辑为:
❝初次审核:审核轮次为1 二审及以后的审核:上一次审核轮次 + 1 ❞
当然还要校验审核配置是否已配置,审核轮次是否已用完:
接下来设置审批人:
//设置审核人
String currentApproveMan = getCurrentApproveMan(code, 1);
process.setApproveMan(currentApproveMan);
审批人从as_node
表中获取,因为是提交节点,所以这里的层级为1,及获取的第一层级的审批人。
初始化流程状态为:审批中
//进入审核流程之前,创建的流程单状态默认为:审核中
process.setStatus(Byte.valueOf(ApproveStatusEnum.IN.getValue()));
然后创建流程记录
processMapper.insert(process);
到这里提交进入审批流程的功能已经结束,但是有的业务场景提交之后可能做一些额外的操作,比如通知给审批人,修改业务单据状态等。这里我用策略模式做了一个扩展,即提交后的功能。
//策略-提交进入流程的策略操作
ProcessResult result = new ProcessResult();
result.setCode(code);
result.setSheetNo(sheetNo);
result.setNextProcessId(String.valueOf(process.getId()));
result.setApproveMan(getCurrentAccount());
result.setNextApproveMan(currentApproveMan);
result.setModuleCode(createParam.getModuleCode());
processStrategyApplicationService.nextProcessOperation(result);
这里的代码是固定的,如果你是请假模块的流程,可能需要OA内部通知给审批人,这里你只需要写一个请假流程策略类去实现ProcessStrategy
接口。然后实现nextProcessOperation()
和getModuleTypeStr()
方法(如下代码)。这样我们既做到了按需扩展,又不修改流程主代码(开发人员无需查看提交审批代码,只需按照说明文档扩展即可),安全简便。
@Component
@Slf4j
public class HolidayProcessStrategy implements ProcessStrategy {
@Override
public void nextProcessOperation(ProcessResult result) {
//策略模式,比如OA通知下一环节审批人等
}
@Override
public String getModuleTypeStr() {
//请假模块
return HOLIDAY.getValue();
}
}
接下来就是审核功能了,和提交一样,需要校验流程编码和业务单号,然后就要获取最后一次审核记录:
通过获取到的最新已审批节点,我们就可以更新当前待审批的节点了。
//1.更新当前节点
updateLatestProcess(createParam, latestInfo, currentDate);
更新待审批节点是要注意必要的参数校验:
这里需要判断当前登录人是不是待审批环节的审批人,该节点是不是待审批节点。
更新了当前审批节点之后,就要创建下一环节待审批节点了。
在审批方法中同样可以返回节点信息,用于后续的操作。来看ProcessResult
里面的信息:
返回的节点信息如上图。
我们来看看生成下一个待审批节点的方法:
createNextProcess(createParam, latestInfo, currentDate, result);
这里要注意,如果审批节点不通过,或者该审批节点为最后一个节点。则不应该创建节点。
创建节点中,需要增加审核层级
Byte latestLevel = latestInfo.getLevel();
byte nextLevel = (byte) (latestLevel + 1);
createNext.setLevel(nextLevel);
新创建的审核节点审核状态为审核中
createNext.setStatus(Byte.valueOf(ApproveStatusEnum.IN.getValue()));
同样我们在创建节点中,使用策略模式用于创建节点后的扩展。
//策略模式,下个审批人需要做的事情
processStrategyApplicationService.nextProcessOperation(result);
如果需要对下一个审核人发通知,可以写一个类实现ProcessStrategy
,然后实现nextProcessOperation()
方法。
@Override
public void nextProcessOperation(ProcessResult result) {
//策略模式,比如通知下一环节审批人等
...
}
这样我们的审核功能完成了。
整体流程如下图:
审批流程梳理
来看看工程的目录结构:
主要方法在ProcessService
类中。
功能完成,就是紧张刺激的测试环节了,先测试submitProcess()
方法,测试代码下:
提交审核功能中,只需要填入3个参数:流程编号,业务单据号,业务模块。流程编号一般可以通过业务模块查询,具体查询逻辑不在这里体现。
查询数据库发现新增一条待审批的节点。
如上图,可以看到当前环节审批人为:lvshen1,审批状态:0(待审批)。
接下来测试审批功能,测试代码如下:
如上代码,入参需要填入审批编码,业务单据号,审批意见,审批结果。代码中审批结果设置为审批通过。
来查询数据库,发现上一条数据已经审批,审批状态更改为1(审批通过),并填入了审批时间。
同时创建了一条新的待审批节点。
我们多模拟几条审批,数据库记录如下:
当然,如果当前登录人不是审批人,会报出如下异常。
如果当前节点是最后一个节点,则不会创建新的节点了。
这里做了控制,已审批的节点不能重新审批。
测试到这里,我们的功能已经实现,可满足一般业务需求了。
如果你对本文的功能感兴趣,欢迎和我探讨交流