废话不多,直接上教程,需要什么概念之类的自己去搜
idea准备工作
安装 actiBPM插件.
部分用户安装可能会出点小插曲,自己百度一下都能轻松解决.
1.pom文件(项目自己建)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.xxdaha</groupId>
<artifactId>approval</artifactId>
<version>1.0.0</version>
<name>approval</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<activiti-dependencies.version>7.1.24</activiti-dependencies.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>${activiti-dependencies.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>${java.version}</release>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<optimize>true</optimize>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>alfresco</id>
<name>Activiti Releases</name>
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
</project>
最新本SpringBoot与最新版Activiti整合报错,版本选择 2.1.13, 其他版本未尝试.
2. application.yml文件
server:
port: 8080
session-timeout: 60
tomcat:
max-threads: 100
uri-encoding: UTF-8
spring:
resources:
static-locations: classpath:/
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.239.129:3306/activiti?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
username: root
password: Aa112233..
hikari:
minimum-idle: 5
maximum-pool-size: 20
connection-test-query: SELECT 1
activiti:
database-schema: ACTIVITI
database-schema-update: true # false:false为默认值,设置为该值后,Activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配时,将在启动时抛出异常。
# true:设置为该值后,Activiti会对数据库中所有的表进行更新,如果表不存在,则Activiti会自动创建。
# create-drop:Activiti启动时,会执行数据库表的创建操作,在Activiti关闭时,执行数据库表的删除操作。
# drop-create:Activiti启动时,执行数据库表的删除操作在Activiti关闭时,会执行数据库表的创建操作。
history-level: full #none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
#activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
#audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
#full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
db-history-used: true #表示使用历史表,如果不配置,则工程启动后可以检查数据库,历史表没有建立,则流程图及运行节点无法展示。
数据库连接那里自己改改.
3.准备Security相关配置:
SpringBoot集成Activiti7时, 默认引入spring-Security,下拉jar包列表可以找到.
凡是要认证,都得有认证过程.官方demo中有 SecurityUtil, 这里直接拿过来用.
/**
* @author xxdaha
* @date 2020-04-20 10:42
* @explain
*/
@Component
public class SecurityComponent {
@Autowired
private UserDetailsService userDetailsService;
public void logInAs(String username) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new IllegalStateException("用户未定义");
}
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public Object getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}
凡是要认证,就得有数据源.官方demo中有DemoApplicationConfiguration,直接拿过来用.
/**
* @author xxdaha
* @date 2020-04-20 10:45
* @explain
*/
@Configuration
@EnableWebSecurity
public class ApplicationConfiguration extends WebSecurityConfigurerAdapter {
private Logger logger = LoggerFactory.getLogger(ApplicationConfiguration.class);
@Override
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService());
}
@Bean
public UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
String[][] usersGroupsAndRoles = {
{"zhangsan", "123", "ROLE_ACTIVITI_USER", "GROUP_emp"},
{"lisi", "123", "ROLE_ACTIVITI_USER", "GROUP_boss"},
{"wangwu", "123", "ROLE_ACTIVITI_USER", "GROUP_money"},
};
for (String[] user : usersGroupsAndRoles) {
List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
}
return inMemoryUserDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
usersGroupsAndRoles数组中就是认证数据源,项目中要从数据库认证的自己改,这里图方便.
在启动类中排除SecurityAutoConfiguration.
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
4. 画流程图
在resources下新建一个processes文件夹,新建一个bpmn文件.
ps:要装actiBPM插件才有这个文件选项.
这里流程图如下:
流程图属性以及说明:
Assignee: 直接指定任务处理人.采用变量,方便启动流程实例的指定申请人.
Candidate Groups: 指定某个组(角色)的人员进行审批(完成任务).开发中可能这个选项用的比较多,毕竟有时候审批人不止一个.
可选: 可以将bpmn文件复制粘贴来同级目录下xml文件, 或者直接用其他记事本工具打开bpmn文件,可以发现里面就是一堆坐标信息.发现bpmn本质上也就是个xml文件.
右键bpmn或者重新生成的xml文件, 生成图片.
可以导出图片保存.
5.开始测试
1.新建数据库activiti(项目启动时, activiti跟根据配置在数据库新建相关表.包含历史记录表共25张)
2.写测试类,我这里直接拿SpringBoot项目生成时自带的.
3.注入需要使用的api
@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessRuntime processRuntime; // activiti7新api
@Autowired
private SecurityComponent securityComponent; //
@Autowired
private TaskRuntime taskRuntime; // activiti7新api
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
4.部署:这里直接跳过部署,因为SpringBoot与Activiti7集成启动时,自动读取Processes目录下的bpmn文件,解析节点信息加载到数据库进行部署.
5.查看部署的流程信息定义
/**
* @param
* @return void
* @author xxDaha
* @date 2020/4/20
* @explain 流程定义信息的查看
*/
@Test
public void testPro() {
securityComponent.logInAs("zhangsan"); // security的认证工作 不加会报错 没有权限 An Authentication object was not found in the SecurityContext
Page processDefinitionPage = processRuntime
.processDefinitions(Pageable.of(0, 10));
//查看已部署流程个数
System.out.println("已部署流程数" + processDefinitionPage.getTotalItems());
//得到每一个部署流程定义
for (Object o : processDefinitionPage.getContent()) {
System.out.println("已部署详细流程定义" + o);
}
}
认证用户名"zhangsan",是我们刚才在demo配置文件里面配置的,可以自行更换.
方法执行完毕,刷新数据库,发现多了25张表,点开act_re_deployment, act_re_procdef,act_ge_bytearray查看,输出信息与刚刚部署的数据一致.
6 启动一个流程实例(可以看做申请对象).
/**
* @author xxdaha
* @date 2020/4/21
* @explain
* @param
* @return void
* act_hi_actinst: 已完成的活动信息
* act_hi_identitylink: 参与者信息
* act_hi_procinst: 流程实例
* act_hi_taskinst: 任务实例
* act_ru_execution: 执行表
* act_ru_identitylink: 参与者信息
* act_ru_task: 任务
*/
@Test
public void testStart() {
String name = "zhangsan";
securityComponent.logInAs(name);
Map map = new HashMap();
map.put("emp", name);
map.put("money", "5000");
map.put("time", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
org.activiti.api.process.model.ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
.start()
.withProcessDefinitionKey("approval") //流程定义的key
.withName("zhangsanApproval") //流程实例的名称
.withVariables(map)
.build());
System.out.println("启动实例流程" + processInstance);
}
这里是直接给流程图中的emp属性赋值了zhangsan.刚才流程图中name的值为变量{emp}.所以任务执行人为zhangsan. 变量数据保存在act_ru_cariable表中.
7.zhangsan查询自己代办的任务.
/**
* @param
* @return void
* @author xxDaha
* @date 2020/4/20
* @explain 查询任务状态与变量
*/
@Test
public void findTask() {
securityComponent.logInAs("zhangsan"); //指定用户认证信息
Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
// 遍历任务列表
System.out.println("任务详情: " + task);
String money = (String) taskService.getVariable(task.getId(), "money");
String time = (String) taskService.getVariable(task.getId(), "time");
System.out.println("启动时设置的变量, money: " + money + ", " + "time: " + time);
}
}
}
也可以修改变量值(taskService.setVariable()).想玩自己试.
8.zhangsan查询并完成自己代办的任务.
/**
* @param
* @return void
* @author xxDaha
* @date 2020/4/20
* @explain 相关表:
* act_hi_actinst
* act_hi_identitylink
* act_hi_taskinst
* act_ru_identitylink
* act_ru_task
*/
@Test
public void completeTask() {
String name = "zhangsan";
securityComponent.logInAs(name); //指定用户认证信息
Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
// 遍历任务列表
System.out.println("任务详情: " + task);
if (name.equals(task.getAssignee())) {
// 获得任务
//完成任务
org.activiti.api.task.model.Task complete = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
System.out.println("完成任务返回值: " + complete);
}
}
}
// 此时再查询一遍任务列表
int count = 0;
tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println("任务详情: " + task);
// 获得任务
if (name.equals(task.getAssignee())) {
count++;
}
}
}
if (count == 0) {
System.out.println("您当前没有任何任务哦");
}
}
之前启动流程的时候是指定了任务处理人为zhangsan,所以这里判断了一下名字一致,避免领到别人的其他没有指定处理人的任务.也可以省略掉.
9.boss角色(组)的人员查看自己的任务, 获取任务并且审批.
/**
* @param
* @return void
* @author xxDaha
* @date 2020/4/20
* @explain boss获取任务进行审批
*/
@Test
public void bossCompleteTask() {
String name = "lisi";
securityComponent.logInAs(name); //指定用户认证信息
Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
// 遍历任务列表
System.out.println("任务详情: " + task);
// 获得任务
org.activiti.api.task.model.Task claim = taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
System.out.println("拾取, 获得任务返回值 : " + claim);
// if (task != null) {
// 如果设置为null,归还组任务,该 任务没有负责人 可以重新被拾取
// taskService.setAssignee(task.getId(), null);
// }
//完成任务
org.activiti.api.task.model.Task complete = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
System.out.println("完成任务返回值: " + complete);
}
}
// 此时再查询一遍任务列表
int count = 0;
tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println("剩余任务详情: " + task);
// 获得任务
if (name.equals(task.getAssignee())) {
count++;
}
}
}
if (count == 0) {
System.out.println("您当前没有任何任务哦");
}
}
boss审批后,可以查看act_ru_task中的数据.其他表可以自己看看.
上例中有放弃任务,归还任务池的,那么也有指定任务给其他同事的,这里组里没有多个角色,就随便举例一下
/**
* @param
* @return void
* @author xxDaha
* @date 2020/4/20
* @explain 将任务交接给其他人处理
*/
@Test
public void setAssigneeToCandidateUser() {
// 当前待办任务
String taskId = "taskId";
// 任务负责人
String userId = "lisi";
// 将此任务交给其它候选人办理该 任务
String candidateuser = "lisi";
// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
taskService.setAssignee(taskId, candidateuser);
}
}
10. money(角色)组的任务获取任务进行完成, 审批.
/**
* @param
* @return void
* @author xxDaha
* @date 2020/4/20
* @explain 财务获取任务进行审批
*/
@Test
public void moneyCompleteTask() {
String name = "wangwu";
securityComponent.logInAs(name); //指定用户认证信息
Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
// 遍历任务列表
System.out.println("任务详情: " + task);
// 获得任务
org.activiti.api.task.model.Task claim = taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
System.out.println("拾取, 获得任务返回值 : " + claim);
//完成任务
org.activiti.api.task.model.Task complete = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
System.out.println("完成任务返回值: " + complete);
}
}
// 此时再查询一遍任务列表
int count = 0;
tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
if (tasks != null && tasks.getTotalItems() > 0) {
//有任务
for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println("剩余任务详情: " + task);
// 获得任务
if (name.equals(task.getAssignee())) {
count++;
}
}
}
if (count == 0) {
System.out.println("您当前没有任何任务哦");
}
}