Warm-Flow国产工作流引擎入门
Warm-Flow,国产的工作流引擎,以其简洁轻量、五脏俱全、灵活扩展性强的特点,成为了众多开发者的首选。它不仅可以通过jar包快速集成设计器,同时原生支持经典和仿钉钉双模式
- 官网 https://www.warm-flow.com/
- 入门开发环境:solon 3.8.4 + Warm-Flow 1.8.4(20260205最新版) + warm-flow-mybatis-flex 1.8.4
下面我演示用简单的方式接入Warm-Flow流程,我用过 Flowable (2020年)【有相关经验】说实话,难用很重。
文章出自:https://lingkang.top/archives/warm-flow-rm
OA 请假流程
需要做以下流程:
- 导入 warm-flow 的表格
- 设计审批权限表,用于配置用户有哪些审批权限,比你直接把固定审批人放到
warm-flow更好 - 设计请假表,记录请假的开始、结束时间等。我们不存储到
warm-flow的变量中,减少耦合
依赖
整合到我的某个模块下
xml
<dependency>
<groupId>org.dromara.warm-flow-mybatis-flex</groupId>
<artifactId>warm-flow-mybatis-flex-solon-plugin</artifactId>
<exclusions>
<exclusion>
<groupId>org.noear</groupId>
<artifactId>mybatis-solon-plugin</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 设计器 -->
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-plugin-ui-solon-web</artifactId>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web</artifactId>
<scope>provided</scope>
</dependency>
<!--入参校验-->
<dependency>
<groupId>top.lingkang</groupId>
<artifactId>final-validator-solon</artifactId>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>mybatis-flex-solon-plugin</artifactId>
<scope>provided</scope>
</dependency>
审批权限表
为什么要设计审批权限表?因为你开始一个流程实例后,需要用户去审批,如果你的系统有OA可以直接拿到上级领导放在审批人字段;当你没有类似OA的组织架构时
审批权限表可以配置你可以审批哪些流程任务。
我们在设计流程图时,将节点定义:待提交(submit)、项目经理审批(PM_A)、部门经理审批(DM_A);当你开始一个请假流程后,节点是待提交(submit),将流程提交到审批时,节点是:项目经理审批(PM_A);这时项目经理审批(PM_A)就是用户拥有的权限:PM_A,用户查询任务节点是 PM_A 的就是他能够审批的任务。
sql
CREATE TABLE `flow_approval_permission` (
`id` bigint NOT NULL COMMENT 'id',
`user_id` bigint DEFAULT NULL COMMENT '用户id',
`permission` json DEFAULT NULL COMMENT '审批权限,数组json',
`status` tinyint(1) DEFAULT NULL COMMENT '状态:1启用,0禁用',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`create_user_id` bigint DEFAULT NULL COMMENT '创建用户id',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建用户',
`update_user_id` bigint DEFAULT NULL COMMENT '更新用户id',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新用户名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户审批权限表';
OA 请假表
sql
CREATE TABLE `flow_leave` (
`id` bigint unsigned NOT NULL COMMENT '主键',
`type` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '请假类型,字典:warm_flow:leaveType',
`reason` varchar(500) COLLATE utf8mb4_general_ci NOT NULL COMMENT '请假原因',
`picture` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '截图,json数组',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`day` decimal(5,2) NOT NULL DEFAULT '0.00' COMMENT '请假天数',
`instance_id` bigint NOT NULL COMMENT '流程实例的id',
`node_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '节点编码',
`node_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '流程节点名称',
`node_type` tinyint(1) NOT NULL COMMENT '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)',
`flow_status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)',
`create_user_id` bigint unsigned DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_user_id` bigint unsigned DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` tinyint(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='OA 请假申请表';
给用户配置权限

编辑

数据库数据如下

编写流程图
编写一个简单的审批流程图

注意节点的名称和编码

条件如下

发起请假
请假时长需要自己算计

发起代码如下:
java
@Transaction
@Override
public Long addData(FlowLeaveSaveParam param) {
boolean hasDefinition = flowDefinitionService.existsPublishByFlowCode(FlowConstants.FLOW_CODE_LEAVE);
Assert.notNull(hasDefinition, "流程不存在:OA请假");
BigDecimal day = param.getTime().divide(new BigDecimal("8.0"));
Map<String, Object> var = new HashMap<>();
var.put("time", param.getTime());
var.put("day", day);
var.put("reason", param.getReason());
var.put("picture", param.getPicture());
var.put("startTime", format.format(param.getStartTime()));
var.put("endTime", format.format(param.getEndTime()));
FlowLeaveEntity entity = BeanUtil.copyProperties(param, FlowLeaveEntity.class);
FlowParams flowParams = new FlowParams()
.flowCode(FlowConstants.FLOW_CODE_LEAVE)// 编码是流程图的编码 leave
.handler(UserUtils.getUserIdElseSystemUserId())// 发起人
.flowStatus(FlowStatus.TOBESUBMIT.getKey()) // 待提交
.message("请假")
.variable(var);
// 开始一个请假流程
Instance instance = FlowEngine.insService().start("level_" + IdUtils.genSnowflake_62(), flowParams);
entity.setDay(day);
entity.setInstanceId(instance.getId());
entity.setNodeType(instance.getNodeType());
entity.setNodeCode(instance.getNodeCode());
entity.setNodeName(instance.getNodeName());
entity.setFlowStatus(instance.getFlowStatus());
save(entity);
return entity.getId();
}
发起完继续提示提交到审批流程

提交到项目经理审批代码:
java
@Override
public void submit(Long id) {
Long insId = getOneAs(
QueryWrapper.create().select(FlowLeaveEntity::getInstanceId)
.eq(FlowLeaveEntity::getId, id),
Long.class
);
FlowTask task = flowTaskService.getLastTaskByInstanceId(insId);
boolean isSubmit = FlowNodeCode.SUBMIT.getCode().equals(task.getNodeCode());
Assert.isTrue(isSubmit, "当前流程非待提交状态");
Map<String, Object> var = flowInstanceService.getVariableMapById(insId);
Instance instance = FlowEngine.taskService().pass(task.getId(), "提交审批", var);
updateStatus(id, instance);// 需要将流程的节点、状态同步更新到 flow_leave 请假表
}
public void updateStatus(Long id, Instance instance) {
updateChain().set(FlowLeaveEntity::getFlowStatus, instance.getFlowStatus())
.set(FlowLeaveEntity::getNodeType, instance.getNodeType())
.set(FlowLeaveEntity::getNodeCode, instance.getNodeCode())
.set(FlowLeaveEntity::getNodeName, instance.getNodeName())
.eq(FlowLeaveEntity::getId, id)
.update();
}
项目经理审批流程
请假流程管理页面可以看到由项目经理审批

有审批权限的用户登录查看OA请假审批即可看到能审批的流程

对应的审批列表查询代码如下:
java
public PageData<FlowLeaveVO> approvalList(FlowLeaveParam param) {
UserVO userVO = UserUtils.getUser();
QueryWrapper query = QueryWrapper.create()
.select(FlowLeaveEntity::getId, FlowLeaveEntity::getType, FlowLeaveEntity::getReason,
FlowLeaveEntity::getPicture, FlowLeaveEntity::getStartTime, FlowLeaveEntity::getEndTime,
FlowLeaveEntity::getDay, FlowLeaveEntity::getInstanceId, FlowLeaveEntity::getNodeCode,
FlowLeaveEntity::getNodeName, FlowLeaveEntity::getNodeType, FlowLeaveEntity::getFlowStatus,
FlowLeaveEntity::getCreateUserId, FlowLeaveEntity::getCreateTime, FlowLeaveEntity::getUpdateUserId,
FlowLeaveEntity::getUpdateTime, FlowLeaveEntity::getDelFlag)
.in(FlowLeaveEntity::getFlowStatus, Arrays.asList(
FlowStatus.APPROVAL.getKey()
))
.in(FlowLeaveEntity::getNodeCode,
flowApprovalPermissionService.getApprovalPermissionList(userVO.getUserId()),
!UserUtils.isAdminByUserType(userVO.getUserType())
)
.eq(FlowLeaveEntity::getType, param.getType(), ObjUtil.isNotEmpty(param.getType()))
.between(
FlowLeaveEntity::getStartTime, param.getStartTimeStart(), param.getStartTimeEnd(),
ObjUtil.isNotEmpty(param.getStartTimeStart()) && ObjUtil.isNotEmpty(param.getStartTimeEnd())
)
.between(
FlowLeaveEntity::getEndTime, param.getEndTimeStart(), param.getEndTimeEnd(),
ObjUtil.isNotEmpty(param.getEndTimeStart()) && ObjUtil.isNotEmpty(param.getEndTimeEnd())
)
.eq(FlowLeaveEntity::getNodeCode, param.getNodeCode(), ObjUtil.isNotEmpty(param.getNodeCode()))
.eq(FlowLeaveEntity::getFlowStatus, param.getFlowStatus(), ObjUtil.isNotEmpty(param.getFlowStatus()))
.orderBy(FlowLeaveEntity::getId, false);
Page<FlowLeaveVO> result = pageAs(param.toQueryPage(), query, FlowLeaveVO.class);
PageData<FlowLeaveVO> pageData = PageData.of(result);
return DictUtils.convert(pageData);
}
其中用节点code来作为权限查询审批数据
// 返回:["PM_A", "DM_A"]
flowApprovalPermissionService.getApprovalPermissionList(userVO.getUserId())
然后就是 项目经理审批同意

后端代码如下:
java
@Transaction
@Override
public void approval(FlowApprovalLeaveParam param) {
FlowLeaveEntity entity = getById(param.getId());
Assert.notNull(entity, "审批的任务不存在");
Assert.isTrue(DelFlagEnum.NOT.getCode().equals(entity.getDelFlag()), "审批的任务不存在");
boolean hasPermission = flowApprovalPermissionService.hasPermission(entity.getNodeCode());
Assert.isTrue(hasPermission, "您没有权限审批流程");
Map<String, Object> variable = flowInstanceService.getVariableMapById(entity.getInstanceId());
Long taskId = flowTaskService.getLastIdByInstanceId(entity.getInstanceId());
Instance instance = null;
if (FlowApprovalEnum.PASS.getType().equals(param.getType())) {
// 同意
instance = FlowEngine.taskService().pass(taskId, param.getMessage(), variable);
} else if (FlowApprovalEnum.REJECT.getType().equals(param.getType())) {
// 拒绝
instance = FlowEngine.taskService().reject(taskId, param.getMessage(), variable);
} else {
throw new BusinessException("审批类型错误:" + param.getType());
}
updateStatus(param.getId(), instance);// 需要将流程的节点、状态同步更新到 flow_leave 请假表
}
查看流程详情

本次用到的warm-flow前端接口
-
查看任务详情
/warm-flow-ui/index.html?id=${instanceId}&type=FlowChart&onlyDesignShow=false

-
编辑流程图
/warm-flow-ui/index.html?id=" + 流程图的id + "&onlyDesignShow=false
-
添加流程图
/warm-flow-ui/index.html
