使用springboot聚合项目,搭建了一套框架,领导说需要把工作流给集成进来。自己以前没做过工作流相关,网上搜索了一下,activiti现在逐渐被flowable给替代,那怎么说呢,那就使用flwoable呗。花了几天时间,搭建起来,简单总结一下,以后便于翻阅。
1. 集成
先在网上搜索了一些资料,搜索到了一篇采用springboot+flowable快速实现工作流。按照内容把项目搭建起来了,然后运行,没什么问题。唯一的就是展示流程图的时候,出现了乱码,配置了config为宋体,或者直接写死为宋体,依然是乱码,后面在网上继续搜索了,设置响应头为图片,这才终于解决了问题。
// 设置响应的类型格式为图片格式
httpServletResponse.setContentType("image/png");
//禁止图像缓存。
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setHeader("Cache-Control", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
out = httpServletResponse.getOutputStream();
2. 新建
集成好了以后,自己测试着新建一个流程,看着上面的博客,看着好像是需要把创建好的保存为bpmn20.xml然后放入processse,重新启动后,就能使用。 自己感觉这个就有点坑了吧,我发布出去的程序,如果新建了一个流程,居然需要拷贝文件并重新启动才能生效?这个不能忍,明明都存到数据库了,为什么还要使用文件呢,后面慢慢继续摸索,终于了解了,需要部署,部署是有API的,哈,这不就能解决这个问题了吗?以后我不需要processse文件夹了,可以直接部署自己想要的,想要部署哪一个就部署哪一个。
public Map addModle(String modelId){
Map res=new HashMap<>();
Model modelData =modelService.getModel(modelId);
byte[] bytes = modelService.getBpmnXML(modelData);
if(bytes==null){
res.put("error","模型数据为空,请先设计流程并成功保存,再进行发布。");
return res;
}
BpmnModel model = modelService.getBpmnModel(modelData);
if(model.getProcesses().size()==0){
res.put("error","数据模型不符要求,请至少设计一条主线流程。");
return res;
}
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName()+".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addBytes(processName,bpmnBytes)
.deploy();
return res;
}
3. 权限
新增流程,处理流程,都可以了以后,考虑到,这个是一套单独的系统,但是这样以后肯定不行啊,不可能让用户在自己本身的业务系统里操作得好好的,来画一个流程还得再次登录,网上有的人说直接这里不适用权限@SpringBootApplication(exclude = SecurityAutoConfiguration.class),但是我个人觉得这个肯定不行啊,这不就脱离了初衷了嘛,于是自己准备把项目迁移到本身的聚合项目里来,我本身的项目是有一套权限管理的,使用的也是security,那就集成进来呗。首先把项目里涉及到权限的包给屏蔽了
<!--security -->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-core</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-config</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-crypto</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-web</artifactId>-->
<!--</dependency>-->
<!-- Servlet -->
然后继续屏蔽IDM应用,因为flowable IDM应用就主要是用户权限啥的,我们既然有,保留这个也没什么用。
<!--security -->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-core</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-config</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-crypto</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.security</groupId>-->
<!--<artifactId>spring-security-web</artifactId>-->
<!--</dependency>-->
<!-- Servlet -->
然后加入了自己的安全校验的包
<dependency> <groupId>com.tofly</groupId> <artifactId>common-oauth</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
我自身的系统使用的是token校验方式,这里可能还得研究一下,暂时处理方式就是把调用接口的地方都加上我这的token。
加上以后,发现modeler获取用户时都是从org.flowable.ui.common.security.SecurityUtils里去获取的用户信息,而org.flowable.ui.common.security.SecurityUtils里面使用的是org.flowable.ui.common.security.FlowableAppUser对象,我们自身的系统肯定不是使用的这个,那怎么办,替换调呗,于是新建org.flowable.ui.common.security.SecurityUtils直接把已经存在的给替代掉。将自己系统使用的对象转换为FlowableAppUser对象
package org.flowable.ui.common.security;
import com.tofly.common.oauth.auth.ToflyUser;
import org.flowable.idm.api.User;
import org.flowable.ui.common.model.RemoteUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Iterator;
public class SecurityUtils {
private static User assumeUser;
private SecurityUtils() {
}
public static String getCurrentUserId() {
User user = getCurrentUserObject();
return user != null ? user.getId() : null;
}
public static User getCurrentUserObject() {
if (assumeUser != null) {
return assumeUser;
} else {
User user = null;
FlowableAppUser appUser = getCurrentFlowableAppUser();
if (appUser != null) {
user = appUser.getUserObject();
}
return user;
}
}
public static FlowableAppUser getCurrentFlowableAppUser() {
FlowableAppUser user = null;
SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext != null && securityContext.getAuthentication() != null) {
Object principal = securityContext.getAuthentication().getPrincipal();
if (principal instanceof ToflyUser) {
ToflyUser toflyUser=(ToflyUser)principal;
RemoteUser remoteUser=new RemoteUser();
remoteUser.setId(toflyUser.getUsername());
remoteUser.setFullName(toflyUser.getRealName());
remoteUser.setFirstName(toflyUser.getRealName());
remoteUser.setPassword(toflyUser.getPassword());
user=new FlowableAppUser(remoteUser,toflyUser.getUsername(),toflyUser.getAuthorities());
}
}
return user;
}
public static boolean currentUserHasCapability(String capability) {
FlowableAppUser user = getCurrentFlowableAppUser();
Iterator var2 = user.getAuthorities().iterator();
GrantedAuthority grantedAuthority;
do {
if (!var2.hasNext()) {
return false;
}
grantedAuthority = (GrantedAuthority)var2.next();
} while(!capability.equals(grantedAuthority.getAuthority()));
return true;
}
public static void assumeUser(User user) {
assumeUser = user;
}
public static void clearAssumeUser() {
assumeUser = null;
}
}
这里获取就可以了,当然还要进入modeler系统时,顶部还有一个获取,但是这里面调用了idm的一些,而我们不适用idm,肯定获取不到了,所以把这个接口也给替换掉把,
package com.tofly.flowable.controller;
import com.tofly.common.oauth.auth.ToflyUser;
import com.tofly.common.oauth.util.SecurityUtils;
import org.flowable.ui.common.model.UserRepresentation;
import org.flowable.ui.common.security.DefaultPrivileges;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author tuonbed
* @date 2019/11/16 9:42
*/
@RestController
@RequestMapping("/app")
public class UserInfoController {
public UserInfoController() {
}
@RequestMapping(value = "/rest/account", method = RequestMethod.GET, produces = "application/json")
public UserRepresentation getAccount() {
UserRepresentation userRepresentation = new UserRepresentation();
ToflyUser toflyUser= SecurityUtils.getUser();
userRepresentation.setId(toflyUser.getUsername());
userRepresentation.setLastName(toflyUser.getRealName());
userRepresentation.setFirstName(toflyUser.getUsername());
List<String> privileges = new ArrayList<>();
privileges.add(DefaultPrivileges.ACCESS_MODELER);
privileges.add(DefaultPrivileges.ACCESS_IDM);
privileges.add(DefaultPrivileges.ACCESS_ADMIN);
privileges.add(DefaultPrivileges.ACCESS_TASK);
privileges.add(DefaultPrivileges.ACCESS_REST_API);
userRepresentation.setPrivileges(privileges);
return userRepresentation;
}
}
别忘了 移除已经存在的
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = RemoteAccountResource.class)
至此,权限系统已经使用自己的了。
至于用户的同步,用户组的同步问题,这个很好解决,在自己的系统里使用消息队列,消息推送,异步调用等等都可以实现。
4. 使用
这个就得看如何规划的了,查询模板,部署模板。任务启动,任务处理,任务结束。使用org.flowable.engine.RuntimeService,org.flowable.engine.TaskService,org.flowable.engine.RepositoryService 这些API就行了,上面发的博客地址也有个专门的ExpenseController,依据葫芦画瓢就可以了。
第一次写博客,自己记录一下吧,希望自己以后养成写博客的好习惯。源码还未看的,书非借不能读也,希望遇见更多的问题,就能更多的了解相关知识。