在pom文件中添加SpringBoot集成Activiti7的依赖
<!--添加activiti和SpringBoot整合的依赖
内置的MyBatis版本与外面的会有冲突,所以需要排除-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.SR1</version>
<exclusions>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<!--activiti可以绘制流程的的依赖-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>7.0.0.SR1</version>
</dependency>
配置application.yml
配置文件**
spring:
activiti:
database-schema-update: true
db-history-used: true
history-level: full
check-process-definitions: false
use-strong-uuids: false
-
database-schema-update属性
1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常 2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建 3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表) 4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
-
db-history-used
检测历史表是否存在 activiti7默认没有开启数据库历史记录,true启动数据库历史记录
-
history-level
#记录历史等级 可配置的历史级别有none, activity, audit, full 1.none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。 2.activity:级别高于none,保存流程实例与流程行为,其他数据不保存。 3.audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。 4.full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
-
check-process-definitions
#校验流程文件,默认校验resources下的processes文件夹里的流程文件
-
use-strong-uuids
是否使用UUID作为主键生成策略
排除Spring Security的自动装配
Activiti7默认和Spring Security集成了,但是如果我们的项目中使用的是Shiro,所以我们需要在项目中排除掉Spring Security的自动装配配置,否则我们的登录页会被覆盖
在启动类注解中排除
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
SecurityAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class
})
流程定义列表
表设计
对应的实体
/**
* 流程定义
*/
@Setter
@Getter
public class BpmnInfo {
private Long id;
private String bpmnName; //流程(图)名称
private String bpmnType; //流程(图)类型
private String bpmnPath; //流程(图)路径
private String deploymentId; //流程部署id
private String actProcessId; //activity流程定义生成的主键
private String actProcessKey; //activity流程定义生成的key
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date deployTime; //部署时间
private String info; //描述信息
}
提供流程文件上传接口
这个版本允许部署zip压缩包形式,可以将流程图文件压缩,或和流程图图片一起压缩上传部署
//上传
@RequiresPermissions("system:bpmnInfo:upload")
@RequestMapping("/upload")
@ResponseBody
public AjaxResult upload(MultipartFile file) {
//文件上传操作:
//1:文件输入流 2>文件输出流
if(file != null && file.getSize() > 0){
//上传的文件名
String filename = file.getOriginalFilename();
//获取文件名后缀
String exp = FilenameUtils.getExtension(filename);
if("zip".equalsIgnoreCase(exp)|| "bpmn".equalsIgnoreCase(exp)){
//一般文件存在硬盘中,然后将路径存在数据库
String upload = null;
try {
//上传成功之后,返回路径为: /upload/日期/随机文件名.bpmn
upload = FileUploadUtils.upload("从spring配置文件中获取一个当前服务器的绝对路径", file);
System.err.println(upload);
} catch (IOException e) {
e.printStackTrace();
return AjaxResult.error("流程文件上传失败");
}
//上传成功之后返回文件路径,用于后面的保存
return AjaxResult.success("上传成功", upload);
}else{
return AjaxResult.error("流程定义文件仅支持 bpmn 和 zip 格式!");
}
}else{
return AjaxResult.error("不允许上传空文件!");
}
}
提供部署流程接口
@RequiresPermissions("system:bpmnInfo:deploy")
@RequestMapping("/deploy")
@ResponseBody
public AjaxResult deploy(String bpmnPath, String bpmnType, String info) throws FileNotFoundException {
bpmnInfoService.deploy(bpmnPath, bpmnType, info);
return AjaxResult.success();
}
服务方法
//集成了spring boot里面的服务对象已经在容器里了,可以直接注入使用
@Autowired
private RepositoryService repositoryService;
@Override
public void deploy(String bpmnPath, String bpmnType, String info) throws FileNotFoundException {
//1:判断参数是否合法
//2:部署流程
//获得文件相对路径
String lowStr = bpmnPath.toLowerCase();
//文件流: 读取之前上传的流程文件
//@Cleanup
FileInputStream stream = new FileInputStream(new File("之前从spring配置文件中获取一个当前服务器的绝对路径", bpmnPath));
//FileInputStream(绝对路径 + bpmnPath);拼成这个文件的绝对路径
Deployment deploy = null;
if(lowStr.endsWith("zip")){
//zip文件
deploy = repositoryService.createDeployment()
.addZipInputStream(new ZipInputStream(stream))
.deploy();
}else if(lowStr.endsWith("bpmn")){
//bpmn文件
deploy = repositoryService.createDeployment()
//参数1:资源名称(使用路径作为名称), 参数2:流程文件流
.addInputStream(bpmnPath, stream)
.deploy();
}
if(deploy == null){
throw new BusinessException("部署失败");
}
//3:创建流程定义对象-自己建表
BpmnInfo bpmnInfo = new BpmnInfo();
bpmnInfo.setInfo(info);
bpmnInfo.setBpmnType(bpmnType);
bpmnInfo.setDeploymentId(deploy.getId());
bpmnInfo.setDeployTime(deploy.getDeploymentTime());
//activiti7生成的流程定义表
//获取activiti中流程定义对象的: ActProcessId ActProcessKey
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploy.getId())
.latestVersion()
.singleResult();
//部署对象无法获取name, 使用定义对象获取name(当初画流程图时指定名称)
bpmnInfo.setBpmnName(processDefinition.getName());
bpmnInfo.setActProcessId(processDefinition.getId());
bpmnInfo.setActProcessKey(processDefinition.getKey());
//保存流程图信息
bpmnInfoMapper.insert(bpmnInfo);
}
流程定义文件删除
@RequiresPermissions("business:bpmnInfo:delete")
@RequestMapping("/delete")
@ResponseBody
public AjaxResult delete(Long id){
bpmnInfoService.delete(id);
return AjaxResult.success();
}
服务类
@Override
public void delete(Long id) {
BpmnInfo bpmnInfo = this.get(id);
bpmnInfoMapper.deleteByPrimaryKey(id);
//删除本地保存的流程文件...省略
//获取该流程的所有流程实例
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery()
.processDefinitionKey(bpmnInfo.getActProcessKey())
.list();
for (ProcessInstance instance : list) {
//删除与流程实例绑定的业务信息
}
}
//删除流程部署对象,连带删除流程定义
repositoryService.deleteDeployment(bpmnInfo.getDeploymentId(), true);
}
查看流程文件
/**
* 查看资源
*/
@RequiresPermissions("business:bpmnInfo:readResource")
@RequestMapping( "/readResource")
@ResponseBody
public void readResource(String deploymentId, String type , HttpServletResponse response) throws IOException {
InputStream inputStream = bpmnInfoService.getResoucceByType(deploymentId, type);
IOUtils.copy(inputStream,response.getOutputStream());
}
服务类
@Override
public InputStream getResoucceByType(String deploymentId, String type) {
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploymentId).latestVersion().singleResult();
if("xml".equalsIgnoreCase(type)){
return repositoryService.getResourceAsStream(deploymentId, definition.getResourceName());
}else if("png".equalsIgnoreCase(type)){
BpmnModel model = repositoryService.getBpmnModel(definition.getId());
//利用上面绘制流程图依赖的api绘制图片
ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
//generateDiagram(流程模型,需要高亮的节点,需要高亮的线条,后面三个参数都表示是字体)
InputStream inputStream = generator.generateDiagram(model, Collections.EMPTY_LIST, Collections.EMPTY_LIST,"宋体","宋体","宋体");
return inputStream;
}
return null;
}
查看某个流程审核到哪个节点图片
//根据流程定义id获取流程模型
BpmnModel model = repositoryService.getBpmnModel(bpmnInfo.getActProcessId());
//根据流程实例id找到正在执行的任务
Task task = taskService.createTaskQuery().processInstanceId(流程实例id).singleResult();
//获取需要高亮的位置
List<String> activeActivityIds = runtimeService.getActiveActivityIds(task.getExecutionId());
//绘制当前节点高亮流程图
inputStream = generator.generateDiagram(model, activeActivityIds,
Collections.EMPTY_LIST,
"宋体","宋体","宋体");
审核撤销
@RequestMapping(value = "/cancelApply")
@ResponseBody
public AjaxResult cancelApply(Long id) throws IOException {
carPackageAuditService.cancelApply(id);
return AjaxResult.success();
}
服务类
@Override
public void cancelApply(Long id) {
//满足撤销状态
//与该流程已经产生的信息删除
//流程实例删除
runtimeService.deleteProcessInstance(流程实例id, "删除理由");
}
审核通过与拒绝
在执行这个节点时才知道用户点的是同意或拒绝,所以我们要给这两个分支线路上的流程变量赋值,才能完成这个节点
//设置审核操作的同意与否参数
taskService.setVariable(task.getId(), "auditStatus",auditStatus);
查询是否还有下一个节点
//获取下一个节点
Task nextTask = taskService.createTaskQuery().processInstanceId(audit.getInstanceId()).singleResult();
if(nextTask != null){
//还有下一个节点任务
}