工作流-202509120150
工作流
1. 什么是工作流
使用编程语言完成一套固定的审批流程:例如请假审批流程、订单配送流程、入职,辞职审
批流程
使用场景:
业务类:合同审批流程、订单处理流程、出入库审批流程等。
行政类:请假流程、出差流程、用车流程、办公用品申请流程等。
财务类:报销流程、支付流程等。
客户服务类:售后跟踪、客户投诉等。
常见软件:
OA:办公自动化(Office Automation)
ERP:企业资源计划即 ERP (Enterprise Resource Planning)
CRM:客户关系管理(Customer Relationship Management)
工作流中的核心三要素:
流程名称
流程绑定业务id
各流程负责人
2. Activiti
官网:https://www.activiti.org/
Activiti 是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,
并通过api进行流程调度
BPM(Business Process Management 即业务流程管理) 的创建者 Tom Baeyens 离开 JBos
s 之后建立的项目
BPMN 2.0其实也是一种类似于xml和html的一种标记语言,就是使用bpmn的规范来定义画
流程图,最终形成一个bpmn文件
3. Springboot集成Activiti
3.1. 导入依赖
注意: activiti7与spring security 强耦合因此两个依赖 必须都得引入
3.2. 项目配置
集成好之后activiti会自动 再项目连接的MySQL数据库下创建25张表
4. Activiti开发流程
4.1. 画流程图模型
遵守BPMN的流程规范,使用BPMN的流程定义工具,通过 流程符号 把整个业务流程定义出来,
将流程定义文件字节流保存到模型数据表中(Model)。每一个流程图都是一个.bpmn文件
Spring 配置
spring:
使用ConcurrentHashMap做本地缓存
cache:
type: simple
activiti7与Spring security 深度耦合,排除Spring security的自动配置
autoconfigure:
exclude:
-
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoCon
figuration
activiti:
#是否让activiti自动创建所有的历史表
history-level: full
#是否需要使用历史表,默认false不使用,而配置true是使用历史表
db-history-used: true
#流程自动部署,关闭,需要手动部署流程 服务启动的时候自动检查resources目录下的
bpmn文件 如果为true自动部署流程
check-process-definitions: false
4.1.1. 流程图模型中的各种符号
4.1.1.1. 事件event符号:
4.1.1.2. 活动activity符号
任务(Task)是最基本的活动类型,表示一个简单的、可执行的工作单元。常见符号:
事件类型
定义
开始事件 (Start Event)
表示流程的起点,触发流程的启动。
中间事件 (Intermediate Event)
流程中的事件,可细分为定时器事件、消息事
件、信号事件等多种类型。
结束事件 (End Event)
表示流程的结束点,触发流程的结束。
任务类型
描述
用户任务(User Task)
需要具体用户执行的任务,流程到达此节点时
生成待办,指定执行人完成。
服务任务(Service Task)
通过调用外部服务或执行业务逻辑完成任务,
可调用Java类、Web服务、REST接口等。
接收任务(Receive Task)
等待外部触发,接收到信号或消息后流程继
续,用于外部系统通知或事件触发。
4.1.1.3. 网关Gateway符号
用于处理流程中的决策
4.2. 部署流程
把xml文件和图片写入到数据库当中
Java类:RepositoryService
涉及表:
ACT_RE_PROCDEF 新增数据: 流程定义数据
ACT_RE_DEPLOYMENT 新增数据: 流程部署数据
ACT_GE_BYTEARRAY 新增数据:将当前流程图绑定到此流程定义部署数据上
ACT_RE_MODEL 更新部署id
4.3. 启动流程
读取部署好到表中的流程数据开启流程,创建流程审批模板
发送任务(Send Task)
发送消息或触发事件,与外部系统交互,如通
知外部系统或启动特定事件。
业务规则任务(Business Rule Task)
执行业务规则完成任务,利用规则引擎根据预
设条件做决策。
网关类型
描述
行为特点
排他网关
又叫 XOR网关、基于数据的
排他网关。创建决策点,流
程中的分支选择
计算所有出口顺序流的条
件,仅选择一个条件为true的
路径继续流程,类似if-else
包容网关
又叫 AND网关、包容性决策
网关。创建平行路径,支持
多路径同时进行
所有出口顺序流同时计算,
至少一个条件为true的路径被
选中,可有多条路径被选
综合网关
模拟复杂同步行为
使用表达式激活条件来精确
控制流程行为
基于事件的网关
流程分支依据事件触发
分支路径的选择基于特定事
件的发生,非流程数据评估
并行网关
同步并行流,创建并行执行
用于同步进入和离开的多个
流,保证所有并行分支同时
开始和结束
Java类:RuntimeService
涉及到的表:
act_hi_actinst 流程实例执行历史
act_hi_identitylink 流程的参与用户历史信息
act_hi_procinst 流程实例历史信息
act_hi_taskinst 流程任务历史信息
act_ru_execution 流程执行信息
act_ru_identitylink 流程的参与用户信息
act_ru_task 任务信息
4.4. 处理任务
完成任务审批,生成审批结果,生成下一节点任务数据。直至满足条件,流程走到结束节
点。Java类:TaskService
4.4.1. 查询待办任务:TaskService
查询单条代办查询:singleResult
查询全部代办任务:list
分页查询代办任务:listPage
条件查询:
根据流程名称查询:processDefinitionKey
根据负责人查询:taskAssignee
根据流程绑定业务id查询:processInstanceBusinessKey
模糊查询:方法中带like
精确查询:方法中带equals
范围查询:方法中带after,before,group,in,between等
4.4.2. 完成任务:TaskSercive
complete(String taskId, Map
可结束当前节点流程,标记为已完成,并且流程会自动开启下一个节点的任务。同时在结束时还
可以把填写的数据,作为流程变量传递到下一个流程中
4.4.3. 拒绝任务:RuntimeService
deleteProcessInstance(String processInstanceId, String deleteReason);
TaskSercive:deleteTask
4.4.4. 查询历史:HistoryService
4.5. 示例
方法名
描述
processInstanceBusinessKey(String)
根据业务流程实例的唯一标识(business ke
y)查询历史任务实例
taskId(String)
通过任务的唯一ID精确查找单个历史任务实例
taskAssignee(String) / taskAssigneeLike(Stri
ng)
查找分配给特定执行者的任务(精确/模糊匹
配,如用户名中包含特定字符串)
finished() / unfinished()
筛选已完成或未完成(进行中/已取消)的任
务实例
orderByHistoricTaskInstanceEndTime().desc
()
按任务结束时间降序排列查询结果
taskName(String) / taskNameLike(String)
根据任务名称查询(精确/类似匹配),适用于
按任务类型或描述分类统计
includeProcessVariables()
在查询结果中包含流程变量,以便分析任务执
行的具体条件或上下文
processVariableValueEquals(String, Object)
筛选流程变量中指定变量名的值完全等于给定
值的任务实例
processVariableValueNotEquals(String, Obje
ct)
查找流程变量中指定变量值不等于给定值的任
务实例
processVariableValueGreaterThan(String, Ob
ject)
筛选出流程变量中指定变量值大于特定值的任
务实例,适用于数值筛选
processVariableValueLessThan(String, Obje
ct)
查找流程变量中指定变量值小于特定值的任务
实例,适用于数值比较
createHistoricTaskInstanceQuery.list()
综合上述所有条件,执行查询并返回符合要求
的历史任务实例列表。此为查询执行步骤,整
合筛选、排序及包含项,以获取最终结果集
@Service
public class WorkFlowServiceImpl implements WorkFlowService {
@Resource
RepositoryService repositoryService;
@Resource
RuntimeService runtimeService;
@Resource
TaskService taskService;
@Resource
HistoryService historyService;
/**
*
*/
@Override
public void deploy(String bpmnResource, String imgResource, String
bpmnName) {
repositoryService.createDeployment()
.addClasspathResource(bpmnResource)
.addClasspathResource(imgResource)
.name(bpmnName)
.deploy();
}
/**
*
*/
@Override
public void deleteDeployment(String deploymentId) {
repositoryService.deleteDeployment(deploymentId, true);
}
/**
*
*/
@Override
public void startProcessInstanceByKey(String processDefinitionKey,
String businessKey, Map
runtimeService.startProcessInstanceByKey(processDefinitionKey,
businessKey, variables);
}
/**
*
*/
@Override
public List
return taskService.createTaskQuery()
.taskAssignee(assignee).list();
}
/**
*
*/
@Override
public void completeTask(String assignee, Map
variables) {
List
tasks.forEach(task -> taskService.complete(task.getId(),
variables,true));
}
@Override
public void completeByTaskId(String taskId, Map
variables) {
// Task task =
taskService.createTaskQuery().taskId(taskId).taskAssignee(assignee).sin
gleResult();
taskService.complete(taskId,variables);
}
/**
*
*/
@Override
public void rejectTask(String taskId, String reason) {
runtimeService.deleteProcessInstance(taskId, reason);
}
/**
*
*/
@Override
public List
pageSize, String assigneeId) {
List
.taskAssignee(assigneeId)
.listPage((pageNum-1)*pageSize, pageSize);
return tasks;
}
/**
*
*/
@Override
public List
queryHistoryTask(Integer pageNum, Integer pageSize,String assigneeId) {
// 查询已完成的任务
List
historyService.createHistoricTaskInstanceQuery()
.finished()
.taskAssignee(assigneeId)
.listPage((pageNum - 1) * pageSize, pageSize);
// 按流程实例 ID 分组,并筛选每个分组中的最新任务
Map
latestTasksByProcessInstanceId = historicTaskInstances.stream()
.collect(Collectors.toMap(
HistoricTaskInstance::getProcessInstanceId,
task -> task,
(task1, task2) ->
task1.getEndTime().after(task2.getEndTime()) ? task1 : task2
本文整理自实践经验,如有问题欢迎交流讨论。