工作流框架是每一个比较复杂的OA或者ERP系统都会用到的一套自动化工作流程的组件,刚开始从官网下载下来的时候看到辣么大的一个包(解压下来完整整150M左右。。。。),然后再稍微建一下数据库,打开表一看,当时我就方了(整整23张表),但是没办法,自己框架要用到,早晚也要看的,只能硬着头皮撸了~
网上的资料大部分都是Activiti与Spring集成的,用的都是Activiti5的版本,但是由于我的基础框架是Spring Boot,所以只能撸一套和Spring Boot集成的框架,而且还得支持RESTful,仔细想一下,要填的坑还是不少的。
用过Spring Boot的同学都知道Spring Boot Stater是个好东西,基于这个东东开发的组件基本上是零配置就能集成进Spring Boot里面,非常的方便,即官方所说的“开箱即用",现在有不少插件都是有这个开箱即用的版本,activiti也不例外。
当找到这个插件以后心里微微一喜,毕竟有了这个可以很简单的使用activiti,然鹅,集成之后稍微一试,控制到就无情的报错了。仔细一查控制台才发现,这个插件的最新版本是基于Activiti 6.0来做的,并不支持Spring Boot 2.0这个版本。内心一下万马奔腾。
然后找了一下Activiti 7,当然这不是Release的版本,只存在于Github里,这一个版本是基于Spring Boot 2.0来开发的,但是它并没有开箱即用的版本,所以还是放弃了,最后选择了一个折中的方法,选择了Activiti6使用与Spring集成的方式来与Spring Boot集成,虽然没有做到开箱即用,但是配置代码还是没有太多的。
Spring Boot的配置都是基于文件的,也就是说零配置文件来实现,想到这里的话我们就可以开始开工了:
com.fasterxml.jackson.core
jackson-core
2.9.5
org.activiti
activiti-spring
6.0.0
先引入activiti的依赖,并且可能会用到jackson的依赖,所以一并导入。
导入依赖之后新建activiti的配置文件ActivitiConfig.java:
package tec.gomoo.oa.config;
import org.activiti.engine.*;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
/**
* @author phw
* @date Created in 05-09-2018
* @description
*/
@Configuration
public class ActivitiConfig {
@Bean
public ProcessEngine processEngine(DataSourceTransactionManager transactionManager, DataSource dataSource) throws IOException {
SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
//自动部署已有的流程文件
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceLoader.CLASSPATH_URL_PREFIX + "processes/*.bpmn");
configuration.setTransactionManager(transactionManager);
configuration.setDataSource(dataSource);
configuration.setDatabaseSchemaUpdate("true");
configuration.setDeploymentResources(resources);
configuration.setDbIdentityUsed(false);
return configuration.buildProcessEngine();
}
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
@Bean
public ManagementService managementService(ProcessEngine processEngine) {
return processEngine.getManagementService();
}
@Bean
public IdentityService identityService(ProcessEngine processEngine) {
return processEngine.getIdentityService();
}
}
在Config文件上打上@Configuration注解表示这是一个自动配置文件。
首先配置一下activiti流程引擎ProcessEngine的配置类SpringProcessEngineConfiguration,这一个类是activiti和Spring集成的关键类,这个类需要我们注入两个东西,一个是数据源DataSource,一个是数据源的事务管理器DataSourceTransactionManager,这两个东西都可以通过Spring自动注入的东西。
然后把已有的流程文件设置到Configuration里面,在服务启第一次启动时就会自动创建那23张数据库的表,并且部署bpmn文件。生成ProcessEngine后就可以生成那常用几大服务了,像RepositoryService/RuntimeService/TaskService等等。
然后我们再来测试一下:
package tec.gomoo.oa.config;
/**
* @author phw
* @date Created in 05-14-2018
* @description
*/
@Slf4j
@Service
@Transactional
public class ActivitiService {
private final RuntimeService runtimeService;
private final TaskService taskService;
private final HistoryService historyService;
private final RepositoryService repositoryService;
@Autowired
public ActivitiService(RuntimeService runtimeService, TaskService taskService, HistoryService historyService, RepositoryService repositoryService) {
this.runtimeService = runtimeService;
this.taskService = taskService;
this.historyService = historyService;
this.repositoryService = repositoryService;
}
public ProcessDefinition deployProcess(MultipartFile bpmn, String path) throws IOException {
if (BaseUtils.isNullOrEmpty(bpmn) || BaseUtils.isNullOrEmpty(path)) {
return null;
}
//上传文件到processes
File file = new File(path + bpmn.getOriginalFilename());
bpmn.transferTo(file);
String resource = ResourceLoader.CLASSPATH_URL_PREFIX + bpmn.getOriginalFilename();
Deployment deployment = repositoryService.createDeployment().addClasspathResource(resource).deploy();
log.info("Process [" + deployment.getName() + "] deployed successful");
return repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
}
public ProcessInstance startProcess(String key) {
if (BaseUtils.isNullOrEmpty(key)) {
return null;
}
return runtimeService.startProcessInstanceByKey(key);
}
public List getTasksByAssignee(String assignee) {
if (BaseUtils.isNullOrEmpty(assignee)) {
return null;
}
List tasks = taskService.createTaskQuery().taskAssignee(assignee).list();
if (BaseUtils.isNullOrEmpty(tasks)) {
return null;
}
List infos = new ArrayList<>();
for (Task task: tasks) {
infos.add(new TaskInfo(task.getId(), task.getName()));
}
return infos;
}
public List getTasksByGroup(String group) {
if (BaseUtils.isNullOrEmpty(group)) {
return null;
}
List tasks = taskService.createTaskQuery().taskCandidateGroup(group).list();
if (BaseUtils.isNullOrEmpty(tasks)) {
return null;
}
List infos = new ArrayList<>();
for (Task task : tasks) {
infos.add(new TaskInfo(task.getId(), task.getName()));
}
return infos;
}
public List getTasks(String assigneeOrGroup) {
if (BaseUtils.isNullOrEmpty(assigneeOrGroup)) {
return null;
}
List tasks = taskService.createTaskQuery().taskCandidateOrAssigned(assigneeOrGroup).list();
if (BaseUtils.isNullOrEmpty(tasks)) {
return null;
}
List infos = new ArrayList<>();
for (Task task : tasks) {
infos.add(new TaskInfo(task.getId(), task.getName()));
}
return infos;
}
public void completeTask(String taskId, Object item) {
Map map = BaseUtils.object2ConditionMap(item);
if (BaseUtils.isNullOrEmpty(taskId) || BaseUtils.isNullOrEmpty(map)) {
log.error("Params cannot be empty");
throw new RuntimeException("Params cannot be empty");
}
taskService.complete(taskId, map);
}
public void completeTask(String taskId) {
if (BaseUtils.isNullOrEmpty(taskId)) {
log.error("Param taskId cannot be empty");
return;
}
taskService.complete(taskId);
}
public static class TaskInfo {
private String id;
private String name;
public TaskInfo(String id, String name) {
this.id = id;
this.name = name;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
在Junit中进行测试,若没有报错就说明我们集成完成了。
下一步就是在Activiti中统一用户管理,不使用它自带的比较简陋的用户管理。
(未完待续...)