acitivi 的表的文档说明链接:https://www.devdoc.cn/activiti-table-act_ru_identitylink.html
-
activiti的表说明
- 使用25张表
- ACT_RE 流程定义和流程资源
- ACT_RU 运行时,流程实例、任务、变量
- ACT_HI 历史表
- ACT_GE 通用表
- 使用25张表
-
Activiti的架构、类关系图
- 获取流程引擎的工具类
- ProcessEngines.使用默认方式获取配置文件,构造流程引擎。配置文件名字activiti.cfg.xml 放在class path下
- ProcessEngineConfiguration.可以自定义配置文件名
- 使用上面两个工具类都可以获得流程引擎
- ProcessEngine:流程引擎,获取各种服务的接口
- 服务接口:用于流程的部署、执行、管理,使用这些接口就是在操作对应的数据表
- RepositoryService 资源管理类
- RuntimeService 运行时管理类
- TaskService 任务管理类
- HistoryService 历史数据管理类
- ManagementService 流程引擎
-
BPMN插件
- idea安装actiBPM
-
流程符号、画流程图
- 流程符号:事件Event、活动Activity,网关 geteway,流向
- 使用流程设计器画出流程图
- bpmn文件本质上是xml文件,因为安装actibpm插件
- 创建bpmn文件,在流程设计器使用流程符号来表达流程,指定流程的key,指定任务负责人
- 生成png文件,把bpmn文件后缀改为xml,在这个文件上右键选择diagrams-》show BPMN2.0 Designer,打开窗口,点击导出文件
-
部署流程
使用activiti提供的API把流程图的内容写入数据库中
属于资源类操作,使用repositoryService
单文件部署:把BPMN文件和png文件一个一个来处理
压缩包部署:把BPMN文件和png打压缩包来处理
//1、创建processEngine ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2、获取repositoryService RepositoryService service = processEngine.getRepositoryService(); //3、使用service进行流程的部署,定义一个流程的名字,把bpmn和png部署到数据中 Deployment deploy = service.createDeployment() .name("出差申请流程") .addClasspathResource("bpmn/evection.bpmn") .addClasspathResource("bpmn/evection.png") .deploy(); //4、输出部署信息 System.out.println("流程部署id=" + deploy.getId()); System.out.println("流程部署名称=" + deploy.getName());
-
部署操作表:
- act_re_deployment 部署表
- act_re_procdef 流程定义表
- act_ge_bytearray 资源表
-
启动流程实例
使用runtimeService 根据流程定义的key
//1、创建processEngine ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2、获取runtimeService RuntimeService service = processEngine.getRuntimeService(); //3、根据流程定义的ID启动流程 ProcessInstance instance = service.startProcessInstanceByKey("myEvection"); //4、输出内容 System.out.println("流程定义ID:" + instance.getProcessDefinitionId()); System.out.println("流程实例ID:" + instance.getId()); System.out.println("当前活动的ID:" + instance.getActivityId());
-
操作表:
- act_hi_actinst 流程实例执行历史信息
- act_hi_identitylink 流程参与用户的历史信息
- act_hi_procinst 流程实例的历史信息
- act_hi_taskinst 流程任务的历史信息
- act_ru_execution 流程执行信息
- act_ru_identitylink 流程正在参与用户信息
- act_ru_task 流程当前任务信息
-
任务查询
使用Task Service,根据流程定义的key,任务的负责人来进行查询
//1、获取流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2、获取taskService TaskService taskService = processEngine.getTaskService(); //3、根据流程key和任务的负责人查询任务 List<Task> taskList = taskService.createTaskQuery() .processDefinitionKey("myEvection") .taskAssignee("zhangsan") .list(); for (Task task : taskList) { System.out.println("流程实例ID:" + task.getProcessDefinitionId()); System.out.println("任务ID:" + task.getId()); System.out.println("任务负责人:" + task.getAssignee()); System.out.println("任务名称:" + task.getName()); }
-
任务完成
使用TaskService用任务的ID来完成任务
TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService(); taskService.complete("30005");
-
删除流程
/** * 删除流程部署信息 * act_ge_bytearray * act_re_deployment * act_re_procdef * 删除流程不会删除流程历史信息 * 删除注意事项:如果当前的流程并没有完成,想要删除流程的话,需要进行级联删除 */ @Test public void deleteDeployment() { RepositoryService service = ProcessEngines.getDefaultProcessEngine().getRepositoryService(); String deploymentID = "27501"; service.deleteDeployment(deploymentID); //删除注意事项:如果当前的流程并没有完成,想要删除流程的话,需要进行级联删除,true代表级联删除 service.deleteDeployment(deploymentID,true); }
-
添加业务key到Activiti表
/** * 添加业务key到activiti表 */ @Test public void addBusinessKey() { RuntimeService service = ProcessEngines.getDefaultProcessEngine().getRuntimeService(); ProcessInstance instance = service.startProcessInstanceByKey("myEvection", "1001"); System.out.println(instance.getBusinessKey()); }
-
全部流程实例的挂起和激活
/** * 全部流程实例的挂起和激活 */ @Test public void suspendAllPrecessInstance() { RepositoryService service = ProcessEngines.getDefaultProcessEngine().getRepositoryService(); //查询流程定义,获取流程定义的查询对象 ProcessDefinition evection = service.createProcessDefinitionQuery() .processDefinitionKey("myEvection") .singleResult(); //获取当前流程定义的实例是否都是挂起状态 boolean suspended = evection.isSuspended(); //获取流程定义的ID String id = evection.getId(); //如果是挂起状态,改为激活状态 if (suspended) { //如果是挂起,执行激活 //args1 流程定义id args2 是否激活 args3 激活时间 service.activateProcessDefinitionById(id, true, null); System.out.println("流程定义id:" + id + ",已激活"); } else { //如果是激活状态,改为挂起状态 //args1 流程定义id args2 是否挂起 args3 挂起时间 service.suspendProcessDefinitionById(id, true, null); System.out.println("流程定义id:" + id + ",已挂起"); } }
-
挂起激活单个流程实例
/** * 挂起激活单个流程实例 */ @Test public void suspendSingleProcessInstance() { RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService(); ProcessInstance instance = runtimeService.createProcessInstanceQuery() .processInstanceId("") .singleResult(); boolean suspended = instance.isSuspended(); // true 已暂停 false 激活 String instanceId = instance.getId(); if (suspended) { runtimeService.activateProcessInstanceById(instanceId); System.out.println("流程实例ID:" + instanceId + ",已激活"); } else { runtimeService.suspendProcessInstanceById(instanceId); System.out.println("流程实例ID:" + instanceId + ",已挂起"); } }
处理工作流回退MySQL存储过程
CREATE DEFINER=`admin`@`` PROCEDURE `activiti_back`(IN proc_inst_id VARCHAR(64))
BEGIN
DECLARE v_id_ VARCHAR(64);
DECLARE v_proc_def_id_ VARCHAR(64);
DECLARE v_task_def_key_ VARCHAR(255);
DECLARE v_name_ VARCHAR(255);
DECLARE v_assignee_ VARCHAR(255);
DECLARE cur_act_hi_taskinst CURSOR FOR SELECT ID_,PROC_DEF_ID_,TASK_DEF_KEY_,NAME_,ASSIGNEE_
FROM act_hi_taskinst
where PROC_INST_ID_ = proc_inst_id and TASK_DEF_KEY_ is not null
order by START_TIME_ desc ;
OPEN cur_act_hi_taskinst;
set @i = 0;
label: LOOP
SET @i = @i + 1;
FETCH cur_act_hi_taskinst INTO v_id_, v_proc_def_id_, v_task_def_key_, v_name_, v_assignee_;
IF @i = 1 THEN
-- 1、处理历史任务 将当前任务设置为完成状态
UPDATE ACT_HI_TASKINST SET END_TIME_ = now(),duration_ = 1 ,DELETE_REASON_ = 'completed' where id_ = v_id_;
ELSEIF @i = 2 THEN
-- 2、处理历史任务 添加当前新任务
INSERT INTO ACT_HI_TASKINST (id_ ,proc_def_id_ ,task_def_key_,proc_inst_id_,execution_id_
,name_,assignee_,start_time_,priority_)
values (uuid(),v_proc_def_id_,v_task_def_key_,proc_inst_id,proc_inst_id
,v_name_,v_assignee_,now(),50);
LEAVE label;
ELSE
LEAVE label;
END IF;
END LOOP label;
CLOSE cur_act_hi_taskinst;
-- 3、修改当前执行信息
UPDATE act_ru_execution SET rev_ = rev_ + 1 , ACT_ID_ = v_task_def_key_ where PROC_INST_ID_ = proc_inst_id;
-- 4、修改当前任务信息
UPDATE act_ru_task SET name_ = v_name_,TASK_DEF_KEY_ = v_task_def_key_,ASSIGNEE_ = v_assignee_ where PROC_INST_ID_ = proc_inst_id;
END
一、创建Spring boot项目
- pom文件依赖
<!--新增activiti7依赖-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M4</version>
</dependency>
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0.M4</version>
<type>pom</type>
</dependency>
<!--新增activiti7依赖-->
<!--其它依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
File | Settings | Editor | File Encodings 编码改为 UTF-8
-
application.yml依赖
server: port: 8089 servlet: context-path: / spring: datasource: url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/activiti?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai&allowMultiQueries=true username: xxxx password: xxxxxx driver-class-name: com.mysql.cj.jdbc.Driver activiti: #activiti创建历史记录表 db-history-used: true #历史记录为所有记录 history-level: full #不自动部署流程 check-process-definitions: false
resources目录下创建bpmn文件夹,将流程文件放入(bpmn文件和png文件)
-
activiti剔除security
-
新建SpringSecurityConfig
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {}
-
新建SelfUserDetailsServiceImpl
import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Slf4j @Component public class SelfUserDetailsServiceImpl implements UserDetailsService { private final UserService userService; public SelfUserDetailsServiceImpl(UserService userService) { this.userService = userService; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userService.findOneUserByName(username); } }
-
新建UserService
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { private final Logger logger = LoggerFactory.getLogger(UserService.class); public User findOneUserByName(String username) { logger.info("username:"); logger.info(username); List<GrantedAuthority> authorities= AuthorityUtils.commaSeparatedStringToAuthorityList("admin"); // 密码置空 return new User(username,"",authorities); }
-
需要在启动类上面加
@SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
-
-
activiti 7 的M4版本中有字段缺失bug需要手动更新
-- ---------------------------- -- 修复Activiti7的M4版本缺失字段Bug -- ---------------------------- alter table ACT_RE_DEPLOYMENT add column PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL; alter table ACT_RE_DEPLOYMENT add column VERSION_ varchar(255) DEFAULT NULL;
至此activiti7配置完毕
二、部署流程、流程相关
-
流程部署
@SpringBootTest class ActivityDemoApplicationTests { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; /** * 装柜通知单流程部署 * act_re_deployment 流程部署信息表 * ID_ 流程部署ID * NAME_ 流程部署名称 * DEPLOY_TIME_ 流程部署时间 */ @Test public void initPackDeployment() { Deployment deploy = repositoryService .createDeployment() .name("装柜通知流程") .addClasspathResource("bpmn/Container.bpmn") .addClasspathResource("bpmn/Container.png") .deploy(); System.out.println("流程部署成功:id==》" + deploy.getId()); System.out.println("流程部署成功:name==》" + deploy.getName()); } /** * 开启一个装柜通知单流程实例 * act_ru_task 运行时任务节点表 * PROC_INST_ID_ 流程实例ID 开启一个新的流程的实例ID * PROC_DEF_ID_ 流程部署时的ID * NAME_ 节点定义名称 * ASSIGNEE_ 任务执行人 */ @Test public void startProcessInstance() { ProcessInstance instance = runtimeService.startProcessInstanceByKey("Container", "1"); System.out.println("流程实例ID:" + instance.getProcessInstanceId()); //9ab732ff-8002-11ec-8ac6-8cc6819ca54f } /** * 单证查询个人代办的任务 */ @Test public void getTaskByAssignee() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("rxy").list(); for (Task task : list) { System.out.println("单证任务ID:" + task.getId()); //任务TaskID 9abab573-8002-11ec-8ac6-8cc6819ca54f System.out.println("单证任务名称:" + task.getName()); } } /** * 单证执行任务 (转发货代,此处需依据货代标识对应转发相应货代) */ @Test public void completeTask() { taskService.complete("9abab573-8002-11ec-8ac6-8cc6819ca54f"); System.out.println("执行任务成功"); } /** * 货代查询个人代办的任务 */ @Test public void getTaskByAssigneeForHd() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("dn").list(); for (Task task : list) { System.out.println("货代任务ID:" + task.getId()); System.out.println("货代任务名称:" + task.getName()); } } /** * 货代拾取任务 */ @Test public void claimTaskByHd() { taskService.claim("af675955-7fd5-11ec-a892-8cc6819ca54f", "dn"); System.out.println("货代拾取任务成功");//货代拾取任务成功 } /** * 货代执行任务(修改) */ @Test public void hdCompleteTask() { //todo 此处根据装柜通知单ID修改表单 //货代执行任务(修改) taskService.complete("af675955-7fd5-11ec-a892-8cc6819ca54f"); System.out.println("货代填写完信息,交给单证"); } /** * 单证再次查询个人代办的任务 */ @Test public void getTaskByAssigneeByDz() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("rxy").list(); for (Task task : list) { System.out.println("单证任务ID:" + task.getId());//2dbfbfcd-7fd7-11ec-abf5-8cc6819ca54f System.out.println("单证任务名称:" + task.getName());//单证审核货代 } } /** * 单证再次拾取任务 */ @Test public void claimTaskAgain() { taskService.claim("2dbfbfcd-7fd7-11ec-abf5-8cc6819ca54f", "rxy"); System.out.println("再次拾取任务成功"); } /** * 单证执行任务 (转发自提) */ @Test public void completeTaskForZiTi() { taskService.complete("2dbfbfcd-7fd7-11ec-abf5-8cc6819ca54f"); System.out.println("执行任务成功"); } /** * 自提查询个人代办的任务 */ @Test public void getTaskByAssigneeByZT() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("hmf").list(); for (Task task : list) { System.out.println("自提任务ID:" + task.getId());//9b05d6f0-7fd7-11ec-b78d-8cc6819ca54f System.out.println("自提任务名称:" + task.getName());//单证审核货代 } } /** * 自提拾取任务 */ @Test public void claimTaskAgainByZT() { taskService.claim("9b05d6f0-7fd7-11ec-b78d-8cc6819ca54f", "hmf"); System.out.println("再次拾取任务成功"); } /** * 自提执行任务 (转发单证) */ @Test public void completeTaskForZiTiByDanZheng() { taskService.complete("9b05d6f0-7fd7-11ec-b78d-8cc6819ca54f"); System.out.println("执行任务成功"); } /** * 单证再次查询个人代办的任务 (审核自提) */ @Test public void getTaskByAssigneeByDzZT() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("rxy").list(); for (Task task : list) { System.out.println("单证任务ID:" + task.getId());//11749858-7fd8-11ec-aa53-8cc6819ca54f System.out.println("单证任务名称:" + task.getName());//单证审核货代 } } /** * 单证再次拾取任务 (审核自提) */ @Test public void claimTaskAgainzz() { taskService.claim("11749858-7fd8-11ec-aa53-8cc6819ca54f", "rxy"); System.out.println("再次拾取任务成功"); } /** * 单证执行任务 ((审核自提)并且最终转发货代) */ @Test public void completeTaskForZiTizz() { taskService.complete("11749858-7fd8-11ec-aa53-8cc6819ca54f"); System.out.println("执行任务成功"); } /** * 货代查询个人代办的任务 */ @Test public void getTaskByAssigneeForHdhh() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("dn").list(); for (Task task : list) { System.out.println("货代任务ID:" + task.getId());//67b7926d-7fd8-11ec-98b3-8cc6819ca54f System.out.println("货代任务名称:" + task.getName());//货代确认 } } /** * 货代拾取任务 */ @Test public void claimTaskByHdhh() { taskService.claim("67b7926d-7fd8-11ec-98b3-8cc6819ca54f", "dn"); System.out.println("货代拾取任务成功");//货代拾取任务成功 } /** * 货代执行任务(最终) */ @Test public void hdCompleteTaskhh() { //货代执行任务(最终) taskService.complete("67b7926d-7fd8-11ec-98b3-8cc6819ca54f"); System.out.println("货代填写完信息,各自走自己的装鞋柜流程"); } }
说明:
流程回退,存储过程中传入的流程实例ID也就是 act_ru_task 表中的 PROC_INST_ID_ ,其余流程进行中的拾取任务、执行任务等取得都是 act_ru_task 表中的 ID_
重要说明
Bpmn文件 idea 2020 之后不能使用act BPMN插件 需要使用扩展程序camunda-modeler
下载地址:https://github.com/camunda/camunda-modeler
之后
之后 创建的BPMN文件会出现bug assignee为null
解决方案 :camunda-modeler画完图以后, 得到的xml中的标签是camunda:assignee, 需要换成activiti:assignee
修改后标签会报错, 需要改命名空间
<pre mdtype="fences" cid="n805" lang="xml" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:activiti="http://activiti.org/bpmn" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_03io3g1"
targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.9.0"
modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0"></pre>