Integrating the Activiti workflow engine into a Spring Boot application generally requires the following steps:
Add the following dependencies to the pom.xml file
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
Configure data sources. For example:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
Configure Activiti, for example:
activiti:
database-schema-update: true
database-reduce-time-to-live: true
async-executor-activate: true
history-level: full
check-process-definitions: true
process-definitions:
deploy-resources: classpath*:/processes/*.bpmn20.xml
allow-multiple: true
id-generator: org.activiti.engine.impl.persistence.StrongUuidGenerator
jdbc-batch-processing: true
job-execution:
activate: true
max-jobs-per-acquisition: 5
wait-time-in-millis: 5000
mail:
enabled: false
Load a BPMN file and deploy a process definition, for example:
@PostConstruct
private void initProcessDefinitions() {
final Set<String> deploymentResourceNames = resourceResolver.getResourcesAsStream("/processes/*.bpmn20.xml")
.map(resource -> resource.getFilename())
.collect(Collectors.toSet());
deploymentResourceNames.forEach(resourceName -> {
final String processDefinitionId = repositoryService.createDeployment()
.addClasspathResource("processes/" + resourceName)
.name(resourceName)
.deploy()
.getId();
log.info("ProcessDefinition <{}> deployed with id: <{}>", resourceName, processDefinitionId);
});
}
Write Activiti related service code, for example:
@Service
public class MyWorkflowService {
private final ProcessEngine processEngine;
@Autowired
public MyWorkflowService(final ProcessEngine processEngine) {
this.processEngine = processEngine;
}
public Execution startWorkflow() {
return processEngine.getRuntimeService().startProcessInstanceByKey("myProcessKey");
}
public List<Task> getTasksByUser(final String user) {
return processEngine.getTaskService().createTaskQuery()
.taskAssignee(user)
.list();
}
public void completeTask(final String taskId) {
processEngine.getTaskService().complete(taskId);
}
}
Activiti is more suitable for larger, complex business processes.
In the Activiti workflow engine, the specific implementation of rejecting a task may vary, but generally the following two solutions can be used:
Historical task restart: re-import the execution history of the current task into the process instance, thereby restarting the process. It can be achieved by the following code:
TaskEntity currentTask = (TaskEntity) taskService.createTaskQuery()
.taskId(taskId)
.singleResult();
String processInstanceId = currentTask.getProcessInstanceId();
HistoricTaskInstanceEntity historicTask = (HistoricTaskInstanceEntity) historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.taskId(taskId)
.singleResult();
List<HistoricTaskInstanceEntity> toRollbackTasks = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.taskDefinitionKey(historicTask.getTaskDefinitionKey())
.orderByTaskCreateTime()
.asc()
.list();
String activityId = toRollbackTasks.get(0).getTaskDefinitionKey();
runtimeService.createProcessInstanceModification(processInstanceId)
.cancelAllForActivity(activityId)
.startBeforeActivity(activityId)
.processInstanceVariable("taskRejectReason", rejectReason)
.execute(true, true);
Transfer to the previous task node: transfer the current task to the previous task node, and set the approval status of the current task to rejected. It can be achieved by the following code:
Task currentTask = taskService.createTaskQuery()
.taskId(taskId)
.singleResult();
String currentActivityId = currentTask.getTaskDefinitionKey();
List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(currentTask.getProcessInstanceId())
.activityType("userTask")
.finished()
.orderByHistoricActivityInstanceEndTime()
.desc()
.list();
if (historicActivityInstances.size() > 1) {
String previousActivityId = historicActivityInstances.get(0).getActivityId();
taskService.addComment(taskId, processInsId, comment);
taskService.complete(taskId, Map.of("status", "rejected", "destination", previousActivityId));
}
It should be noted that the code provided here is just a sample code, and the specific implementation process and details may be different. Developers need to make corresponding modifications and adjustments according to their actual situation. At the same time, it should be noted that the rejection operation may affect the normal flow of workflow, so it needs to be used with caution in actual operation.