springboot整合flowable工作流初体验
1.添加两个依赖:
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.7.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.application.yml 文件添加数据库链接配置
3.启动项目
启动完成后会生成工作流相关的表
4.idea安装工作流插件-Flowable BPMN visualizer
5.画流程图
首先在resources资源目录下新建 processes目录,然后新建一个.bpmn20的xml文件
然后在xml文件右键选择 View BPMN(Flowable) Diagram 画流程图。
在 View BPMN(Flowable) Diagram 界面右键选择对应的实践,流程,角色等。我完成的一个请假流程图如下:
流程走向判断的地方需要输入变量判断条件:
注意:
现在启动可能回报如下错误:
[Validation set: 'flowable-executable-process' | Problem: 'flowable-servicetask-missing-implementation'] : One of the attributes 'class', 'delegateExpression', 'type', 'operation', or 'expression' is mandatory on serviceTask. - [Extra info : processDefinitionId = ask_for_leave | processDefinitionName = ask_for_leave | | id = sid-84a802b7-3136-43f3-b076-b2cb83639175 | | activityName = 发送失败提示 | ] ( line: 26, column: 131)
这是因为serviceTask节点需要 'class', 'delegateExpression', 'type', 'operation', or 'expression'其中一个属性,我这里添加了class属性:flowable:class="com.example.demo.listener.FailHandler"。
当审核拒绝或驳回的时候会回调或者说触发这个类。
public class FailHandler implements JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) {
String name = delegateExecution.getVariable("name").toString();
String rejectReason = delegateExecution.getVariable("rejectReason").toString();
System.out.println(name+"你的请假申请被驳回了,原因是:"+rejectReason);
}
}
我这里只是简单的打印一下请假人名称和和驳回的原因。这里面的variable属性在整个流程没有结束期间都可以设置和获取。``
6.开启一个流程
我这里写一个单元测试来启动一个请假流程:
@Resource
private RuntimeService runtimeService;
@Resource
private TaskService taskService;
private String headman = "8888";
private String manager = "9999";
/**
* 开启一个请假流程
* @param userId
* @return
*/
@RequestMapping("startProcess")
public String startProcess(String userId){
Map<String,Object> params = new HashMap<>();
params.put("user",userId);
ProcessInstance instance = runtimeService.startProcessInstanceByKey("askforleave", params);
runtimeService.setVariable(instance.getProcessInstanceId(),"name","张大炮");
runtimeService.setVariable(instance.getProcessInstanceId(),"reason","去医院看病");
runtimeService.setVariable(instance.getProcessInstanceId(),"days","10");
return instance.getProcessInstanceId();
}
创建请假流程 ,返回流程id: 868704fb-0317-11ed-a8eb-4c796e57fe35
ProcessInstance instance = runtimeService.startProcessInstanceByKey("askforleave", params); 这里的流程实例"askforleave" 是生成的流程图xml文件的process 的id
7.查看当前流程进度
@GetMapping("/pic")
public void showPic(HttpServletResponse response,String processId){
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
if(null == instance){
return;
}
List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processId).list();
List<String> activeIds = new ArrayList<>();
List<String> flows = new ArrayList<>();
for (Execution execution : executions) {
List<String> activeActivityIds = runtimeService.getActiveActivityIds(execution.getId());
activeIds.addAll(activeActivityIds);
}
/**
* 生成流程图
*/
BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activeIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false);
OutputStream out = null;
byte[] buf = new byte[1024];
int legth = 0;
try {
out = response.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
接着调用这个接口会看到生成的流程图
可以看到流程当前处于员工请假阶段
可能会出现流程图中文乱码情况,需要在项目中增加如下配置:
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration con) {
//防止生成的流程图中中文乱码
con.setActivityFontName("宋体");
con.setLabelFontName("宋体");
con.setAnnotationFontName("宋体");
}
}
8.提交请假申请到下一步给组长审批
/**
* 提交给组长审批
* @param processId
* @return
*/
@RequestMapping(value = "submit")
public String submit(String userId,String processId) {
Task task = taskService.createTaskQuery().taskAssignee(userId).processInstanceId(processId).singleResult();
if (task == null) {
return "流程不存在";
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
map.put("headman",headman);
taskService.complete(task.getId(), map);
return "processed ok!";
}
map里的headman是在流程图的时候设置的:
意思是提交给下一步谁来审核处理,关于 assignee,Candidate Users和 Candidate Groups可以百度一下。
9.查看代办任务
/**
* 获取审批管理列表
*/
@RequestMapping(value = "/list")
public String list(String assignee) {
List<Task> tasks = taskService.createTaskQuery().taskAssignee(assignee).orderByTaskCreateTime().desc().list();
String result = "";
for (Task task : tasks) {
result += "processId:"+task.getProcessInstanceId()+" taskId: "+task.getId()+" name: "+task.getName()+" assignee: "+task.getAssignee()+" owner:"+task.getOwner()+"\n";
}
return result;
}
组长 8888 有两条代办任务,选择其中一条任务id 进行审核,下一步组长审核,通过或者拒绝。
10.组长审核
/**
* 组长审核通过
* @param taskId
* @return
*/
@RequestMapping(value = "leaderApply")
public String leaderApply(String taskId) {
Task task = taskService.createTaskQuery().taskAssignee(headman).taskId(taskId).singleResult();
if(task == null){
task = taskService.createTaskQuery().taskId(taskId).singleResult();
}
// Task
if (task == null) {
return "流程不存在";
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
map.put("audit", "通过");
map.put("manager",manager);
taskService.complete(task.getId(), map);
return "processed ok!";
}
map里有两个值, audit == 通过,表示审核通过,在步骤5中有过说明。 manager == manager表示下一步提交给manger审核。
这里的表示受理人是谁。
然后可以调用步骤7的查看流程图接口看到当前流程已经走到了经理审核阶段。
11.经理审核
先调用步骤9接口传入上一步设置的manager的值可以查看经理代办任务,可以看到经理的代办任务id(taskId),然后进行审核。
/**
* 经理审核驳回
* @param taskId
* @return
*/
@RequestMapping(value = "managerReject")
public String managerReject(String taskId) {
Task task = taskService.createTaskQuery().taskAssignee(manager).taskId(taskId).singleResult();
// Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
return "流程不存在";
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
map.put("audit", "驳回");
taskService.setVariable(task.getId(),"rejectReason","理由不充分");
taskService.complete(task.getId(), map);
return "processed ok!";
}
拒绝之后会发送系统消息。也就是步骤5最后的说明。最后流程结束。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?