概念:
流程:整体工作流程。
实例:ProcessInstance。启动一个流程实例表示开始一次业务的运行。
查询待办任务:业务流程都交给activiti管理,通过activiti就可以查询当前流程执行到哪里了,当前用户需要办理什么任务。
用户办理任务:查询到任务 就可以办理任务了
流程结束:当前任务办理完成没有下一个任务节点了。
生成表配置:
依赖版本
<properties>
<activiti.version>5.22.0</activiti.version>
</properties>
<dependencies>
<!-- Activiti 启动器 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Activiti 流程图 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- Activiti 在线设计 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>${activiti.version}</version>
</dependency>
</dependencies>
配置文件:
@Configuration
public class ActivitiDataSourceConfig extends AbstractProcessEngineAutoConfiguration {
@Resource
private ActivitiDataSourceProperties activitiDataSourceProperties;
@Bean
public DataSource activitiDataSource() {
DruidDataSource DruiddataSource = new DruidDataSource();
DruiddataSource.setUrl(activitiDataSourceProperties.getUrl());
DruiddataSource.setDriverClassName(activitiDataSourceProperties.getDriverClassName());
DruiddataSource.setPassword(activitiDataSourceProperties.getPassword());
DruiddataSource.setUsername(activitiDataSourceProperties.getUsername());
return DruiddataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(activitiDataSource());
}
@Bean
public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
configuration.setDataSource(activitiDataSource());
//如果当前数据库 有相关表 就不管 没有就创建
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
configuration.setJobExecutorActivate(false);
configuration.setTransactionManager(transactionManager());
configuration.setActivityFontName("宋体");
configuration.setLabelFontName("宋体");
configuration.setAnnotationFontName("宋体");
//id生成器
//configuration.setIdGenerator(new MyUUIDgenerator());
return configuration;
}
}
数据库连接设置:如果使用mysql-connect 8.0以上版本,必须要加nullCatalogMeansCurrent=true的配置,否则会扫描整个服务器的库。(这里如果不加,如果有多个数据库实例,就有可能报找不到表)
spring:
datasource:
dynamic:
datasource:
master:
url: jdbc:mysql://localhost:3306/activiti?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&useAffectedRows=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
表结构说明
- act_ge(general): 一般数据
- act_hi(history): 流程历史记录
- act_re(repository): 流程定义表
- act_ru(runtime): 运行实例表
- act_id(identity):组织架构信息
表分类 | 表名 | 说明 |
一般数据 | ||
ACT_GE_BYTEARRAY | 通用的流程定义和流程资源 | |
ACT_GE_PROPERTY | 系统相关属性 | |
流程历史记录 | ||
ACT_HI_ACTINST | 历史的流程实例 | |
ACT_HI_ATTACHMENT | 历史的流程附件 | |
ACT_HI_COMMENT | 历史的说明性信息 | |
ACT_HI_DETAIL | 历史的流程运行中的细节信息 | |
ACT_HI_IDENTITYLINK | 历史的流程运行过程中用户信息 | |
ACT_HI_PROCINST | 历史的流程实例 | |
ACT_HI_TASKINST | 历史的任务实例 | |
ACT_HI_VARINST | 历史的流程运行中的变量信息 | |
流程定义表 | ||
ACT_RE_DEPLOYMENT | 部署单元信息 | |
ACT_RE_MODEL | 模型信息 | |
ACT_RE_PROCDEF | 已部署的流程定义 | |
运行实例表 | ||
ACT_RU_EVENT_SUBSCR | 运行时事件 | |
ACT_RU_EXECUTION | 运行时流程执行实例 | |
ACT_RU_IDENTITYLINK | 运行时用户关系信息,存储任务节点与参与者的相关信息 | |
ACT_RU_JOB | 运行时作业 | |
ACT_RU_TASK | 运行时任务 | |
ACT_RU_VARIABLE | 运行时变量表 |
创建流程
- 定义流程 按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来
- 部署流程 把画好的流程定义文件,加载到数据库中, 生成表的数据
- 启动流程 使用java代码来操作数据库表中的内容
流程符号:
事件Event
活动Activity:
一个活动可以是一个任务,还可以是一个当前流程的子处理流程,还可以为活动指定不同的类型
网关Gateway:
网关用来处理决策
- 排他网关
只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流;
如果多条路线计算结果都是true,则会执行第一个值为true的线路,如果所有网关计算结果没有true,则引擎会抛出异常。
排他网关需要和条件顺序流结合使用,default属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
- 并行网关
所有路径会被同事选择
拆分:并行执行所有输出顺序流,为每一条顺序流创建一个并行执行路线
合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
- 包容网关
可以同时执行多条线路,也可以在网关上设置条件
拆分:计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行。
合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
- 事件网关
专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
流向Flow
流是连接两个流程节点的连线。
常见的流向包含以下几种:
顺序流 (Sequence Flow) 消息流(Message Flow) 关联(Association) 数据关联(Data Association)
代码流程:
- 创建模型
//初始化一个空模型
Model model = repositoryService.newModel();
//保存模型
repositoryService.saveModel(model);
//保存设计流程资源
repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
涉及更新/新增的表:act_ge_property、act_re_model、act_ge_bytearray
2. 部署流程
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addString(processName, new String(bpmnBytes, "UTF-8"))
.deploy();
涉及到的表:ACT_RE_PROCDEF、ACT_RE_DEPLOYMENT、ACT_GE_BYTEARRAY
3. 激活流程定义
repositoryService.activateProcessDefinitionById(id, true, new Date());
涉及到的表:ACT_RU_JOB
4. 启用流程实例,并分配任务
//启用实例
ProcessInstance pi = runtimeService.startProcessInstanceById(actBusiness.getProcDefId(), actBusiness.getId(), params);
// 设置流程实例名称
runtimeService.setProcessInstanceName(pi.getId(), actBusiness.getTitle());
//任务分配
taskService.addCandidateUser(task.getId(), user.getUsername());
// 设置任务优先级
taskService.setPriority(task.getId(), actBusiness.getPriority());
5. 查询任务
//构建查询条件
TaskQuery query = taskService.createTaskQuery();
//查询对应人
query.taskCandidateOrAssigned(userId);
//查询对应任务
List<Task> taskList = query.list();
6. 获取下一节点
public ProcessNodeVo getNextNode(String procDefId, String currActId) {
ProcessNodeVo node = new ProcessNodeVo();
// 当前执行节点id
ProcessDefinitionEntity dfe = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(procDefId);
// 获取所有节点
List<ActivityImpl> activitiList = dfe.getActivities();
// 判断出当前流程所处节点,根据路径获得下一个节点实例
for(ActivityImpl activityImpl : activitiList){
if (activityImpl.getId().equals(currActId)) {
// 获取下一个节点
List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();
PvmActivity pvmActivity = pvmTransitions.get(0).getDestination();
String type = pvmActivity.getProperty("type").toString();
if("userTask".equals(type)){
// 用户任务节点
node.setType(ActivitiConstant.NODE_TYPE_TASK);
node.setTitle(pvmActivity.getProperty("name").toString());
// 设置关联用户
List<LoginUser> users = getNodetUsers(pvmActivity.getId());
node.setUsers(removeDuplicate(users));
}else if("exclusiveGateway".equals(type)){
// 排他网关
node.setType(ActivitiConstant.NODE_TYPE_EG);
}else if("parallelGateway".equals(type)){
// 平行网关
node.setType(ActivitiConstant.NODE_TYPE_PG);
}else if("endEvent".equals(type)){
// 结束
node.setType(ActivitiConstant.NODE_TYPE_END);
}else{
throw new JeecgBootException("流程设计错误,包含无法处理的节点");
}
break;
}
}
return node;
}
7. 审核通过
public Result<Object> pass(@ApiParam("任务id") @RequestParam String id,
@ApiParam("流程实例id") @RequestParam String procInstId,
@ApiParam("下个节点审批人") @RequestParam(required = false) String assignees,
@ApiParam("优先级") @RequestParam(required = false) Integer priority,
@ApiParam("意见评论") @RequestParam(required = false) String comment,
@ApiParam("是否发送站内消息") @RequestParam(defaultValue = "false") Boolean sendMessage,
@ApiParam("是否发送短信通知") @RequestParam(defaultValue = "false") Boolean sendSms,
@ApiParam("是否发送邮件通知") @RequestParam(defaultValue = "false") Boolean sendEmail){
if(StrUtil.isBlank(comment)){
comment = "";
}
taskService.addComment(id, procInstId, comment);
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
Task task = taskService.createTaskQuery().taskId(id).singleResult();
if(StrUtil.isNotBlank(task.getOwner())&&!("RESOLVED").equals(task.getDelegationState().toString())){
// 未解决的委托任务 先resolve
String oldAssignee = task.getAssignee();
taskService.resolveTask(id);
taskService.setAssignee(id, oldAssignee);
}
taskService.complete(id);
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
// 判断下一个节点
if(tasks!=null&&tasks.size()>0){
ActBusiness actBusiness = actBusinessService.getById(pi.getBusinessKey());
for(Task t : tasks){
if(StrUtil.isBlank(assignees)){
// 如果下个节点未分配审批人为空 取消结束流程
List<LoginUser> users = actZprocessService.getNode(t.getTaskDefinitionKey()).getUsers();
if(users==null||users.size()==0){
runtimeService.deleteProcessInstance(procInstId, "canceled-审批节点未分配审批人,流程自动中断取消");
break;
}else{
// 避免重复添加
List<String> list = actBusinessService.selectIRunIdentity(t.getId(), "candidate");
if(list==null||list.size()==0) {
// 分配了节点负责人分发给全部
for (LoginUser user : users) {
taskService.addCandidateUser(t.getId(), user.getId());
}
taskService.setPriority(t.getId(), task.getPriority());
}
}
}else{
// 避免重复添加
List<String> list = actBusinessService.selectIRunIdentity(t.getId(), "candidate");
if(list==null||list.size()==0) {
for(String assignee : assignees.split(",")){
taskService.addCandidateUser(t.getId(), assignee);
taskService.setPriority(t.getId(), priority);
}
}
}
}
}
return Result.ok(MessageUtils.message(I18nConstant.operateSuccess));
}