Activiti
本质
Activiti最本质最核心的东西就是将流程定义(xml文件)转换成表记录(MySQL数据)
核心Service
ProcessEngine
package org.activiti.engine;
import org.activiti.engine.api.internal.Internal;
@Internal
public interface ProcessEngine {
String VERSION = "7.1.0-M6";
String getName();
void close();
// 流程部署、流程定义、静态数据
RepositoryService getRepositoryService();
// 流程实例、流程变量
RuntimeService getRuntimeService();
// 任务相关
TaskService getTaskService();
// 历史数据
HistoryService getHistoryService();
ManagementService getManagementService();
DynamicBpmnService getDynamicBpmnService();
// 流程定义相关信息
ProcessEngineConfiguration getProcessEngineConfiguration();
}
数据表说明
表名 | 说明 |
---|---|
act_evt_log | 流程引擎通用日志表 |
act_ge_bytearray | 二进制表,存储通用的流程资源 |
act_ge_property | 系统存储表,存储整个流程引擎数据,默认存储三条数据 |
act_hi_actinst | 历史节点表 |
act_hi_attachment | 历史附件表 |
act_hi_comment | 历史意见表 |
act_hi_detail | 历史详情表 |
act_hi_identitylink | 历史用户信息表 |
act_hi_procinst | 历史流程实例表 |
act_hi_taskinst | 历史任务实例表 |
act_hi_varinst | 历史变量表 |
act_procdef_info | 流程定义的动态变更信息 |
act_re_deployment | 部署信息表 |
act_re_model | 流程设计实体表 |
act_re_procdef | 流程定义数据表 |
act_ru_deadletter_job | 作业失败表,失败次数>重试次数 |
act_ru_event_subscr | 运行时事件表 |
act_ru_execution | 运行时流程执行实例表 |
act_ru_identitylink | 运行时用户信息表 |
act_ru_integration | 运行时综合表 |
act_ru_job | 作业表 |
act_ru_suspended_job | 作业暂停表 |
act_ru_task | 运行时任务信息表 |
act_ru_timer_job | 运行时定时器表 |
act_ru_variable | 运行时变量表 |
注:
- ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
- ACT_RU_*: 'RU'表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
- ACT_ID_*: 'ID'表示identity。 这些表包含身份信息,比如用户,组等等。
- ACT_HI_*: 'HI'表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
- ACT_GE_*: 'GE'表示general。通用数据, 用于不同场景下,如存放资源文件。
核心API
流程部署
@RequestMapping(value = "/deploy", method = RequestMethod.POST)
@ResponseBody
public AjaxResult fileupload(@RequestParam MultipartFile uploadfile) {
try {
String filename = uploadfile.getOriginalFilename();
InputStream is = uploadfile.getInputStream();
if (filename.endsWith("zip")) {
repositoryService.createDeployment().name(filename).addZipInputStream(new ZipInputStream(is)).deploy();
} else if (filename.endsWith("bpmn") || filename.endsWith("xml")) {
repositoryService.createDeployment().name(filename).addInputStream(filename, is).deploy();
} else {
return AjaxResult.error("文件格式错误");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("流程部署失败");
}
return AjaxResult.success("流程部署成功");
}
流程发起
@PostMapping("/startProcess")
@ResponseBody
public AjaxResult startProcess(@RequestParam String pdid)
{
// 设置流程发起人
SysUser user = SecurityUtils.getLoginUser().getUser();
// 会自动在表ACT_HI_PROCINST 中的START_USER_ID_中设置用户ID,以便后续获取流程发起人信息
// 设置的认证用户会在当前线程的上下文中生效,直到线程执行完成或者手动清除认证用户
identityService.setAuthenticatedUserId(user.getUserName());
// 动态传递参数
HashMap<String, Object> v = new HashMap<>();
v.put("name", "wsz");
runtimeService.startProcessInstanceById(pdid, v);
return AjaxResult.success();
}
流程待办
@PostMapping("/todoList")
@ResponseBody
public TableDataInfo todoList(TaskInfo param)
{
// 获取当前登录用户
String username = getUsername();
TaskQuery condition = taskService.createTaskQuery().taskAssignee(username);
if (StringUtils.isNotEmpty(param.getTaskName())) {
condition.taskName(param.getTaskName());
}
if (StringUtils.isNotEmpty(param.getProcessName())) {
condition.processDefinitionName(param.getProcessName());
}
// 过滤掉流程挂起的待办任务(激活的任务)
int total = condition.active().orderByTaskCreateTime().desc().list().size();
int start = (param.getPageNum()-1) * param.getPageSize();
// 分页查询
List<Task> taskList = condition.active().orderByTaskCreateTime().desc().listPage(start, param.getPageSize());
List<TaskInfo> tasks = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
taskList.stream().forEach(a->{
// 根据任务获取流程实例,封装任务信息
ProcessInstance process = runtimeService.createProcessInstanceQuery().processInstanceId(a.getProcessInstanceId()).singleResult();
TaskInfo info = new TaskInfo();
info.setAssignee(a.getAssignee());
info.setBusinessKey(process.getBusinessKey());
info.setCreateTime(sdf.format(a.getCreateTime()));
info.setTaskName(a.getName());
info.setExecutionId(a.getExecutionId());
info.setProcessInstanceId(a.getProcessInstanceId());
info.setProcessName(process.getProcessDefinitionName());
info.setStarter(process.getStartUserId());
info.setStartTime(sdf.format(process.getStartTime()));
info.setTaskId(a.getId());
String formKey = formService.getTaskFormData(a.getId()).getFormKey();
info.setFormKey(formKey);
tasks.add(info);
});
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(200);
rspData.setRows(tasks);
rspData.setTotal(total);
return rspData;
}
流程审批
@RequestMapping(value = "/completeTask/{taskId}", method = RequestMethod.POST)
@ResponseBody
public AjaxResult completeTask(@PathVariable("taskId") String taskId, @RequestBody(required=false) Map<String, Object> variables) {
String username = getUsername();
// 设置任务的办理人,记录该操作人为当前登录用户
taskService.setAssignee(taskId, username);
// 查出流程实例id
String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
if (variables == null) {
taskService.complete(taskId);
} else {
// 添加审批意见
if (variables.get("comment") != null) {
taskService.addComment(taskId, processInstanceId, (String) variables.get("comment"));
// 移除审批意见
variables.remove("comment");
}
// 流程变量可以在流程实例级别传播
taskService.complete(taskId, variables);
}
return AjaxResult.success();
}
流程跳转
@GetMapping(value = "/jump/{taskId}/{sid}")
@ResponseBody
public AjaxResult jump(@PathVariable String taskId, @PathVariable String sid) {
Task t = taskService.createTaskQuery().taskId(taskId).singleResult();
String processDefinitionId = runtimeService.createProcessInstanceQuery().processInstanceId(t.getProcessInstanceId()).singleResult().getProcessDefinitionId();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 寻找流程实例当前任务的activeId
Execution execution = runtimeService.createExecutionQuery().executionId(t.getExecutionId()).singleResult();
String activityId = execution.getActivityId();
// 当前节点
FlowNode currentNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(activityId);
// 目标节点
FlowNode targetNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(sid);
// 创建连接线
List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newFlow");
newSequenceFlow.setSourceFlowElement(currentNode);
newSequenceFlow.setTargetFlowElement(targetNode);
newSequenceFlowList.add(newSequenceFlow);
// 备份原有方向
List<SequenceFlow> dataflows = currentNode.getOutgoingFlows();
List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
oriSequenceFlows.addAll(dataflows);
// 清空原有方向
currentNode.getOutgoingFlows().clear();
// 设置新方向
currentNode.setOutgoingFlows(newSequenceFlowList);
// 完成当前任务
taskService.addComment(taskId, t.getProcessInstanceId(), "comment", "跳转节点");
taskService.complete(taskId);
// 恢复原有方向
currentNode.setOutgoingFlows(oriSequenceFlows);
return AjaxResult.success();
}
强制结束流程
@GetMapping(value = "/forceEnd/{taskId}")
@ResponseBody
public AjaxResult forceEnd(@PathVariable String taskId) {
Task t = taskService.createTaskQuery().taskId(taskId).singleResult();
String processDefinitionId = runtimeService.createProcessInstanceQuery().processInstanceId(t.getProcessInstanceId()).singleResult().getProcessDefinitionId();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 寻找流程实例当前任务的activeId
Execution execution = runtimeService.createExecutionQuery().executionId(t.getExecutionId()).singleResult();
String activityId = execution.getActivityId();
FlowNode currentNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(activityId);
// 创建结束节点和连接线
EndEvent end = new EndEvent();
end.setName("强制结束");
end.setId("forceEnd");
List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newFlow");
newSequenceFlow.setSourceFlowElement(currentNode);
newSequenceFlow.setTargetFlowElement(end);
newSequenceFlowList.add(newSequenceFlow);
// 备份原有方向
List<SequenceFlow> dataflows = currentNode.getOutgoingFlows();
List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
oriSequenceFlows.addAll(dataflows);
// 清空原有方向
currentNode.getOutgoingFlows().clear();
// 设置新方向
currentNode.setOutgoingFlows(newSequenceFlowList);
// 完成当前任务
taskService.addComment(taskId, t.getProcessInstanceId(), "comment", "撤销流程");
taskService.complete(taskId);
// 恢复原有方向,避免影响其他流程实例
currentNode.setOutgoingFlows(oriSequenceFlows);
return AjaxResult.success();
}
查看进度
// 查看当前活动任务(可能当前流程实例有多个任务)
List<Task> tasks = taskService.createTaskQuery().processInstanceId(p.getProcessInstanceId()).list();
if (tasks.size() > 0) {
String taskName = "";
String assignee = "";
for (Task t : tasks) {
taskName += t.getName() + ",";
assignee += t.getAssignee() + ",";
}
// 去掉最后一个逗号
taskName = taskName.substring(0, taskName.length() -1);
assignee = assignee.substring(0, assignee.length() -1);
// 设置当前任务
info.setCurrentTask(taskName);
info.setAssignee(assignee);
}
SpringBoot 整合Activity7
引入依赖
注:引入依赖后启动项目会自动生成相对应的表
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
禁用security框架
// 禁用SpringSecurity
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class})
public class ActivitiApplication {
public static void main(String[] args) {
SpringApplication.run(ActivitiApplication.class, args);
}
}
控制台打印SQL语句
logging:
level:
org.activiti.engine.impl.persistence.entity: debug
遇到的坑
流程设计器不兼容
https://www.cnblogs.com/ReturnOfTheKing/p/18198068
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix