# springboot-flowable
**Repository Path**: wichell/springboot-flowable
## Basic Information
- **Project Name**: springboot-flowable
- **Description**: springboot-flowable 快速开发工作流
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 354
- **Created**: 2019-05-21
- **Last Updated**: 2022-05-29
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# springboot-flowable
#### 介绍
springboot-flowable 快速开发工作流
#### 软件架构
springboot + flowable
#### 使用说明
1. 将flowable的依赖加入到POM中即可,flowable使用需要一个数据库,这里为了方便我选择mysql
~~~xml
org.springframework.boot
spring-boot-starter-web
org.flowable
flowable-spring-boot-starter
6.3.0
mysql
mysql-connector-java
5.1.45
~~~
2.yml 文件配置
~~~yml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/flowable-spring-boot?characterEncoding=UTF-8
username: root
password: root
flowable:
#关闭定时任务JOB
async-executor-activate: false
~~~
这样操作后,flowable与springBoot的整个就完成了!
然后就可以运行了,初次运行时flowable会将自动执行flowable中的初始化脚本完成工作流所需要的数据表的建立,如果指定的数据库中还未创建过flowable的相关数据表的话。
## 定义流程文件
ExpenseProcess.bpmn20.xml
~~~xml
报销流程
500}]]>
~~~
其中的两个代理类为:
~~~java
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
public class ManagerTaskHandler implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("经理");
}
}
~~~
~~~java
public class BossTaskHandler implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("老板");
}
}
~~~
尽管上面的BPMN文件很长,但放心,毕竟那是通过相关的工具生成出来的,对于核心的逻辑部分也很少(主要在process 标签内) ,如需要详细了解的可自行学习下BPMN的标签即可!当然,在flowable的使用文档中也有相关的描述,详见:Creating a ProcessEngine
---------------------

#### 这样当此框架启动的时候它会默认加载resource目录下的processes时就可以将此流程配置加载到数据库进行持久化了
# 测试controller
为了方便这里通过一个controller来完成此DEMO的快速编写
~~~java
@Controller
@RequestMapping(value = "expense")
public class ExpenseController {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessEngine processEngine;
/***************此处为业务代码******************/
}
~~~
~~~java
/**
* 添加报销
*
* @param userId 用户Id
* @param money 报销金额
* @param descption 描述
*/
@RequestMapping(value = "add")
@ResponseBody
public String addExpense(String userId, Integer money, String descption) {
//启动流程
HashMap map = new HashMap<>();
map.put("taskUser", userId);
map.put("money", money);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("Expense", map);
return "提交成功.流程Id为:" + processInstance.getId();
}
~~~
~~~java
/**
* 获取审批管理列表
*/
@RequestMapping(value = "/list")
@ResponseBody
public Object list(String userId) {
List tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
for (Task task : tasks) {
System.out.println(task.toString());
}
return tasks.toArray().toString();
}
~~~
~~~java
/**
* 批准
*
* @param taskId 任务ID
*/
@RequestMapping(value = "apply")
@ResponseBody
public String apply(String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new RuntimeException("流程不存在");
}
//通过审核
HashMap map = new HashMap<>();
map.put("outcome", "通过");
taskService.complete(taskId, map);
return "processed ok!";
}
~~~
~~~java
/**
* 拒绝
*/
@ResponseBody
@RequestMapping(value = "reject")
public String reject(String taskId) {
HashMap map = new HashMap<>();
map.put("outcome", "驳回");
taskService.complete(taskId, map);
return "reject";
}
~~~
~~~java
/**
* 生成流程图
*
* @param processId 任务ID
*/
@RequestMapping(value = "processDiagram")
public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
//流程走完的不显示图
if (pi == null) {
return;
}
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
//使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
String InstanceId = task.getProcessInstanceId();
List executions = runtimeService
.createExecutionQuery()
.processInstanceId(InstanceId)
.list();
//得到正在执行的Activity的Id
List activityIds = new ArrayList<>();
List flows = new ArrayList<>();
for (Execution exe : executions) {
List ids = runtimeService.getActiveActivityIds(exe.getId());
activityIds.addAll(ids);
}
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0);
OutputStream out = null;
byte[] buf = new byte[1024];
int legth = 0;
try {
out = httpServletResponse.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
~~~
通过传入流程ID生成当前流程的流程图给前端,如果流程中使用到中文且生成的图片是乱码的,则需要进配置下字体:
~~~java
/**
* @author haiyangp
* date: 2018/4/7
* desc: flowable配置----为放置生成的流程图中中文乱码
*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
}
}
~~~
#### 测试请求
1.先启动好此项目,然后创建一个流程:
访问:http://localhost:8080/expense/add?userId=123&money=123321
返回:提交成功.流程Id为:2501
2.查询待办列表:
访问:http://localhost:8080/expense/list?userId=123
输出:Task[id=2507, name=出差报销]
3.同意:
==注意 : 带的是taskid==
访问:http://localhost:8080/expense/apply?taskId=2507
返回:processed ok!
4.生成流程图:
访问: http://localhost:8080/expense/processDiagram?processId=2501

#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 码云特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)