一、Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.18</version>
</dependency>
<!--MySql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus 插件-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M5</version>
<exclusions>
<exclusion>
<groupId>org.activiti.core.common</groupId>
<artifactId>activiti-spring-identity</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
二、配置
1. application.properties配置
spring:
activiti:
# 表示启动时检查数据库表,不存在则创建
database-schema-update:true
# 表示哪种情况下使用历史表,这里配置为full表示全部记录历史,方便绘制流程图
history-level:full
# true 表示使用历史表,如果不配置,则工程启动后可以检查数据库,只建立了17张表
db-history-used:true
# 数据库连接
datasource:
url:jdbc:mysql://localhost:3306/xtsz_workflow?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=true
username:root
password:123
driver-class-name:com.mysql.cj.jdbc.Driver
type:com.alibaba.druid.pool.DruidDataSource
参数说明: databaseSchemaUpdate配置项可以设置流程引擎启动和关闭时数据库执行的策略。 databaseSchemaUpdate有以下四个值: false:false为默认值,设置为该值后,Activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配时,将在启动时抛出异常。 true:设置为该值后,Activiti会对数据库中所有的表进行更新,如果表不存在,则Activiti会自动创建。 create-drop:Activiti启动时,会执行数据库表的创建操作,在Activiti关闭时,执行数据库表的删除操作。 drop-create:Activiti启动时,执行数据库表的删除操作在Activiti关闭时,会执行数据库表的创建操作。
- asyncExecutorEnabled属性设置设置true后将代替那些老的Job executor
- spring.activiti.async-executor-enabled=false
- spring.activiti.job-executor-activate=false
- asyncExecutorActivate是指示activiti在流程引擎启动就激活AsyncExecutor,异步
- spring.activiti.async-executor-activate=
- 校验流程文件,默认校验resources下的processes文件夹里的流程文件
- spring.activiti.check-process-definitions=
- 使用自定义mybatis-mapper
- spring.activiti.custom-mybatis-mappers=
- spring.activiti.custom-mybatis-xmlmappers=
- 数据源指定
- spring.activiti.database-schema=
- 建表规则
- flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。
- true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建。
- create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。
- drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
- spring.activiti.database-schema-update=false
- 检测历史表是否存在
- spring.activiti.db-history-used=false
- 检测身份信息表是否存在
- spring.activiti.db-identity-used=false
- 流程部署名称
- spring.activiti.deployment-name=
- 记录历史等级 可配置的历史级别有none, acitivity, audit, all
- spring.activiti.history-level=
- spring jpa使用
- spring.activiti.jpa-enabled=false
- 邮件发送服务配置
- spring.activiti.mail-server-default-from=
- spring.activiti.mail-server-host=
- spring.activiti.mail-server-password=
- spring.activiti.mail-server-port=
- spring.activiti.mail-server-use-ssl=
- spring.activiti.mail-server-use-tls=
- spring.activiti.mail-server-user-name=
- 自定义流程文件位置
- spring.activiti.process-definition-location-prefix=
- spring.activiti.process-definition-location-suffixes=
- activiti rest 配置
- spring.activiti.rest-api-enabled=false
- spring.activiti.rest-api-mapping=
- spring.activiti.rest-api-servlet-name=
2. 关闭activiti默认的安全校验
activiti7内置了Spring security框架,因此只要我们在项目中引入了如下jar包,那么项目中所有接口都会被security拦截到,然后跳转到登录页面。
取消security验证:
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
security认证
这里需要输入用户名和密码,默认的用户名是user用户,密码会在项目启动的时候生成。
密码
3. 配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll().and().logout().permitAll();
}
}
4. 重写用户权限
@Service
public class CustomUserGroupManagerImpl implements UserGroupManager {
public static List<String> roles = new ArrayList<>();
public static List<String> groups = new ArrayList<>();
public static List<String> users = new ArrayList<>();
public static Map<String,String> userRoleMap = new HashMap<>();
static {
roles.add("workCreate");
roles.add("workPermit");
roles.add("workLeader");
groups.add("workGroupA");
users.add("admin");
users.add("laowang");
users.add("xiaofang");
userRoleMap.put("admin", "workCreate");
userRoleMap.put("laowang", "workPermit");
userRoleMap.put("xiaofang", "workLeader");
}
@Override
public List<String> getUserGroups(String s) {
return groups;
}
@Override
public List<String> getUserRoles(String s) {
String role = userRoleMap.get(s);
List<String> list = new ArrayList<>();
list.add(role);
return list;
}
@Override
public List<String> getGroups() {
return groups;
}
@Override
public List<String> getUsers() {
return users;
}
}
5. 配置activiti的数据源和线程池
@Configuration
public class WorkFlowConfiguration {
@Autowired
private UserGroupManager userGroupManager;
@Autowired
private DataSource dataSource;
private int corePoolSize = 10;
private int maxPoolSize = 30;
private int keepAliveSeconds = 300;
private int queueCapacity = 300;
/**
* 处理引擎配置
* @param transactionManager
* @return
*/
@Bean
public SpringProcessEngineConfiguration springProcessEngineConfiguration(
PlatformTransactionManager transactionManager) {
SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
configuration.setDataSource(dataSource);
configuration.setTransactionManager(transactionManager);
SpringAsyncExecutor asyncExecutor = new SpringAsyncExecutor();
asyncExecutor.setTaskExecutor(workFlowAsync());
configuration.setAsyncExecutor(asyncExecutor);
configuration.setDatabaseSchemaUpdate("true");
configuration.setUserGroupManager(userGroupManager);
configuration.setHistoryLevel(HistoryLevel.FULL);
configuration.setDbHistoryUsed(true);
return configuration;
}
/**
* 线程池
*
* @return
*/
@Primary
@Bean("workFlowTaskExecutor")
public ThreadPoolTaskExecutor workFlowAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("workFlowTaskExecutor-");
executor.initialize();
return executor;
}
}
三、Spring Security相关配置
因为activiti7使用了Spring Security,因此需要创建Spring Security需要用到的表和初始化一系列的用户数据。
1. 创建表
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`userName` varchar(20) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`enabled` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`authority` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
2. 程序启用web security
为了能够与Process Runtime API进行交互,我们需要使用具有ROLE_ACTIVITI_USER角色的用户进行身份验证。 如果我们只是直接从Java代码调用Process Runtime API,例如来自带有main方法的类,那么我们可以在进行API调用之前
直接设置用户上下文。
设置我们在与流程引擎API交互时可以使用的一些用户和组。
关于security问题
activiti7最新的类似Runtime API和Task API都集成了security。
如果使用上述的API,那么必须要使用security,不能屏蔽security,否则会报错。
使用引擎服务类的时候,可以排除security,因为这些是最原始的API。但是activiti7官方已经明确说了,随时可能会干掉这些API。不建议开发人员直接使用引擎类以及引擎配置了、服务类等。
四、流程绘制
其实就是设计中的活动图。
activiti7目前使用的Bpmn-js设计器,但是目前还没有发布正式版。activiti7流程设计器可以使用eclipse或者idea插件即可。接下来绘制一个简单的请假流程。流程图如下:
- 在resources中创建目录processes:
- 绘制流程图
**
加-》任务的办理人(Assignee)-> houjianjun
流程非常简单,请假天数大于3天,总经理审批;否则部门经理审批。
拷贝bpmn文件修改扩展名为xml,然后调整布局:
保存为png图片。
五、部署流程
部署流程直接使用RepositoryService即可:
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Test
public void deploy() {
DeploymentBuilder builder = repositoryService.createDeployment();
// bpmn文件的名称
builder.addClasspathResource("processes/leave.bpmn");
builder.addClasspathResource("processes/leave.png");
// 设置key
builder.key("leave");
// 设定名称,也可以在图中定义
builder.name("请假流程");
// 进行布署
Deployment deployment = builder.deploy();
log.info("部署ID:" + deployment.getId());
log.info("部署名称:" + deployment.getName());
}
startProcessInstanceByKey需要的参数,对应ACTRE_PROCDEF表key列的值。
流程文档部署完毕之后,会在以下表中创建记录:
- ACT_RE_DEPLOYMENT(部署表)
- ACT_RE_PROCDEF(流程定义表)
- ACT_GE_BYTEARRAY(资源表)
- ACT_GE_PROPERTY(通用属性表)
- ACT_RU_IDENTITYLINK(运行权限表)
六、核心service
- 服务类:
- 在activiti5/6时代,用户永远可以通过如下几个服务类进行操作。
- RepositoryService:仓库服务类
- RuntimeService :运行服务类
- TaskService :用户任务服务类
- HistoryService :历史服务类
- ManagementService :管理服务类
- DynamicBpmnService :动态服务类
activiti7在salaboy带领下开发的云bpm产品,为了使其能够更好的支持cloud换机,activiti7进行了如下几个点的重构。
**
- 服务类替换类:
- Activiti Cloud Runtime Bundle
- Activiti Cloud Query
- Activiti Cloud Audit
- Activiti Cloud Connectors
- Activiti Cloud Notifications Service (GraphQL)
总结
- ProcessRuntime类内部最终调用repositoryService和runtimeService相关API。
- TaskRuntime类内部调用taskService。
- TaskAdminRuntime类内部调用taskService
- ProcessRuntime类需要ACTIVITI_USER权限。
- TaskRuntime需要ACTIVITI_USER权限。
- TaskAdminRuntime需要ACTIVITI_ADMIN权限。
七、启动流程
/**
* 启动流程
*/
@Test
public void start() {
// 每一个流程有对应的一个key这个是某一个流程内固定的写在bpmn内的
String processDefinitionKey = "leave";
HashMap<String, Object> variables = new HashMap<>();
// 流程实例
ProcessInstance instance = runtimeService
.startProcessInstanceByKey(processDefinitionKey,variables);
log.info("流程实例ID:" + instance.getId());
log.info("流程定义ID:" + instance.getProcessDefinitionId());
}
startProcessInstanceByKey需要的参数,对应ACTRE_PROCDEF表key列的值。
八、查询任务
/**
* 查询当前人的个人任务
* 查询任务表ACT_RU_TASK。启动实例之后,实例直接运转到请假申请节点。
*/
@Test
public void findTask() {
// 创建任务查询对象
List<Task> list = taskService.createTaskQuery()
.list();
if (list != null && list.size() > 0) {
for (Task task : list) {
log.info("任务ID:" + task.getId());
log.info("任务名称:" + task.getName());
log.info("任务的创建时间:" + task.getCreateTime());
log.info("任务的办理人:" + task.getAssignee());
log.info("流程实例ID:" + task.getProcessInstanceId());
log.info("执行对象ID:" + task.getExecutionId());
log.info("流程定义ID:" + task.getProcessDefinitionId());
}
}
}
九、完成用户任务
完成该任务之后,实例直接运转到了总经理审批节点,完成任务的时候如果没有传递day参数,会报错。继续查询待办-完成任务,直至实例结束即可。
/**
* 完成用户任务
* 完成该任务之后,实例直接运转到了总经理审批节点,完成任务的时候如果没有传递day参数,
* 会报错。继续查询待办-完成任务,直至实例结束即可。
*/
@Test
public void completeTask() {
Map map = new HashMap();
map.put("day", 4);
// 按配置的任务id填写
map.put("_7","true");
// 参数为:act_ru_task 表中的ID_
taskService.complete("10005", map);
}
十、常见问题:
- Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘VERSION‘ in ‘field list’
Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘PROJECT_RELEASE_VERSION‘ in ‘field list’
原因:
创建表缺少VERSION_字段
添加两个字段。