flowable的使用

1、部署流程定义(BPMN文件),获取流程定义文件对象

// resouce:BPMN文件路径,inputStream:该文件的字节流
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().addInputStream(resource, inputStream);
/*根据参数设置流程部署构建器
parameter :部署参数,
一个
Map<String, Object> deploymentBuilder.category(parameter.get("flowType"))
.name(parameter.get("flowName"))
.key(parameter.get("flowKey")).tenantId(parameter.get("flowTenantId"));
*/
// 并获取流程定义部署对象 
Deployment deployment = deploymentBuilder.deploy();
String deploymentId = deployment.getId();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
// 流程定义ID
String processDefinitionId = processDefinition.getId();
// 流程定义Key
String processDefinitionKey = processDefinition.getKey();

2、挂起与恢复流程定义,挂起后发起流程实例就会抛出异常

// 挂起
repositoryService.suspendProcessDefinitionById(processDefinitionId);
repositoryService.suspendProcessDefinitionByKey(processDefinitionKey);
// 恢复
repositoryService.activateProcessDefinitionById(processDefinitionId);
repositoryService.activateProcessDefinitionByKey(processDefinitionKey);

3、启动流程实例,并获取流程实例对象

// variables:流程变量,Map<String, Object>类型
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, variables);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
// 将流程实例与发起人绑定
identityService.setAuthenticatedUserId(userId);

4、对流程实例的操作

processInstance
// 流程实例ID
String processInstanceId = processInstance.getId();
// 判断流程是否结束
processInstance.isEnded();
// 判断流程是否挂起
processInstance.isSuspended();
// 获取流程的发起人ID
String startUserId = processInstance.getStartUserId();
runtimeService
// 该流程的执行对象查询
List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).singleResult();
// 该流程实例下的所有活动实例
List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
// 更改多个活动实例的状态节点为指定节点 activityId ,比如结束节点终止流程    
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, activityId).changeState();
// 挂起流程实例
runtimeService.suspendProcessInstanceById(processInstanceId);
// 恢复挂起的流程实例
runtimeService.activateProcessInstanceById(processInstanceId);
// 删除一个正在流转的流程 deleteReason:删除原因
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId);
// 获取该流程实例下的任务数量
long count = query.count();
// 任务数量大于1,则流程已经启动了,不能撤回
if (count > 1) {
    throw new FlowException(ResultEnum.EX_INSTANCE_BEGIN);
}
runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
// 获取流程实例的查询对象
ProcessInstanceQuery instanceQuery = runtimeService.createProcessInstanceQuery();
// 与某个用户相关的
instanceQuery.involvedUser(userId);
// 某个用户开启的
instanceQuery.startedBy(userId);
// 或者查询条件 .or().endOr() ==> (xx or  xx or ... ... or xx),等于包裹内容的两括号
instanceQuery.or().endOr();
// 挂起的流程
instanceQuery.suspended();
// 在某个时间点之后开始
instanceQuery.startedAfter(Date date);
// 在某个时间点之前开始
instanceQuery.startedBefore(Date date);
// 获取正在流转的一个指定的流程实例
instanceQuery.processInstanceId(processInstanceId);
// 单个的流程实例
ProcessInstance processInstance = instanceQuery.singleResult();
// 多个流程实例 begin : 从第几个开始 ; max : 展示多少个
List<ProcessInstance> processInstances = instanceQuery.list();
List<ProcessInstance> processInstances = instanceQuery.listPage(int begin,int max);
// 流程实例的数量
long count = taskQuery.count();

historyService
// 获取历史流程实例查询对象
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
// 已完成的
historicProcessInstanceQuery.finished();
// 未完成的
historicProcessInstanceQuery.unfinished();
// 删除的
historicProcessInstanceQuery.deleted();
// 没有删除的
historicProcessInstanceQuery.notDeleted();
// 在某个时间点之后结束
historicProcessInstanceQuery.finishedAfter(Date date);
// 在某个时间点之前结束
historicProcessInstanceQuery.finishedBefore(Date date);
// 指定父流程ID的流程实例 historicProcessInstanceQuery.superProcessInstanceId(processInstanceId)
// 历史流程实例
HistoricProcessInstance processInstance = historicProcessInstanceQuery.processInstanceId(processInstanceId).singleResult();
// 删除该流程的历史记录
historyService.deleteHistoricProcessInstance(processInstanceId);

5、任务服务的操作

获取task任务对象
// 任务基础查询对象
TaskQuery taskQuery = taskService.createTaskQuery();
// 某个任务
taskQuery.taskId(taskId);
// 某个经办人的任务
taskQuery.taskAssignee(userId);
// 某个委托人的任务
taskQuery.taskOwner(userId);
// 某个或多个流程实例的任务
taskQuery.processInstanceId(String processInstanceId);
taskQuery.processInstanceIdIn(List<String> processInstanceIds);
// 某个或多个部署实例的任务
taskQuery.deploymentId(String deploymentId);
taskQuery.deploymentIdIn(List<String> deploymentIds);
// 某个活动实例的任务
taskQuery.executionId(String executionId);
// 按照任务创建时间倒序
taskQuery.orderByTaskCreateTime().desc();
// 存活的任务
taskQuery.active();
// 挂起的任务
taskQuery.suspended();
// 没有 删除原因 的任务
taskQuery.taskWithoutDeleteReason();
// 没有签收的任务
taskQuery.taskUnassigned();
// 单个的任务对象
Task task = taskQuery.singleResult();
// 多个任务对象 begin : 从第几个开始 ; max : 展示多少个
List<Task> tasks = taskQuery.list();
List<Task> tasks = taskQuery.listPage(int begin,int max);
// 任务的数量
long count = taskQuery.count();
变量的设值与取值
// 任务ID
String taskId = task.getId();
// 设置全局变量
taskService.setVariable(taskId,"key1","value1");
// 设置局部变量
taskService.setVariableLocal(taskId,"key2","value2");
// 获取全局变量
Map<String,Object> a = taskService.getVariables(taskId);
// 获取局部变量
Map<String,Object> b = taskService.getVariablesLocal(taskId);
// 流程启动后获取变量(全局变量)
Map<String,Object> variables = runtimeService.getVariables(processInstanceId);
// 设置变量(全局变量)
runtimeService.setVariable(processInstanceId,"key","value");
任务的流转
// 任务的执行(委托人)
taskService.resolveTask(taskId);
taskService.complete(taskId);
// 任务的执行(经办人) variables : 下次任务所需要的参数 localScope : 变量的存储范围(true:作用范围为当前任务,false:表示这个变量是全局的)
taskService.complete(taskId);
taskService.complete(String taskId, Map<String, Object> variables);
taskService.complete(String taskId, Map<String, Object> variables, boolean localScope);
// 添加和删除候选人
taskService.addCandidateUser(taskId, userId);
taskService.deleteCandidateUser(taskId, userId);
// 签收
taskService.claim(taskId, userId);
// 委派
taskService.delegateTask(taskId, acceptUserId);
// 转发
taskService.setAssignee(taskId, acceptUserId);
// 驳回 currTaskKeys : 该任务的节点 ; activityId : 上一个节点ID
List<String> currTaskKeys = new ArrayList<>();
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
for (Task task : tasks) {
    currTaskKeys.add(task.getTaskDefinitionKey());
}
runtimeService.createChangeActivityStateBuilder()
    .processInstanceId(processInstanceId)
    .moveActivityIdsToSingleActivityId(currTaskKeys, activityId)
    .changeState();
// 删除任务
taskService.deleteTask(taskId, deleteReason);
taskService.deleteTasks(List<String> taskIds, deleteReason);

根据流程实例ID获取流程图(流程图在服务器上的访问地址)
// 1.获取流程实例
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
// 2.根据流程实例ID获取该实例所有的历史轨迹对象
List<HistoricActivityInstance> historyProcess = historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId).finished().list();
// 所有的历史轨迹对象
Map<String, HistoricActivityInstance> hisActivityMap = new HashMap<>(16);
historyProcess.forEach(historicActivityInstance -> {
if (!hisActivityMap.containsKey(historicActivityInstance.getActivityId())) { hisActivityMap.put(historicActivityInstance.getActivityId(),historicActivityInstance);
    }
});
// 流程定义ID
String processDefinitionId;
// 节点ID的集合
List<String> activityIds = new ArrayList<>();
// 3.获取高亮显示的节点ID和流程定义ID
if (pi == null) {
    // 如果流程已经走完了,则从历史记录查询 historyService
    HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
    processDefinitionId = historicProcessInstance.getProcessDefinitionId();
    // 获取该流程的结束节点高亮显示
    String endActivityId = historicProcessInstance.getEndActivityId();
    activityIds.add(endActivityId);
} else {
    // 如果流程没有走完,则从运行记录查询 runtimeService
    processDefinitionId = pi.getProcessDefinitionId();
    activityIds = runtimeService.getActiveActivityIds(instanceId);
}
// 4.获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 5.获取流程定义的所有节点信息
Map<String, FlowElement> activityMap = new HashMap<>(16);
List<Process> processes = bpmnModel.getProcesses();
for (Process process : processes) {
Collection<FlowElement> flowElements = process.getFlowElements();
    for (FlowElement flowElement : flowElements) {
    if (flowElement != null) {
        String flowElementId = flowElement.getId();
        activityMap.put(flowElementId, flowElement);
    }
    }
}
// 6.获取高亮显示的连线集合
List<String> flows = new ArrayList<>();
// 递归获取高亮连线
for (String activityId : activityIds) {
    this.getHighLightedFlows(activityMap, hisActivityMap, activityId, flows,activityId);                
     // 递归方法
     private void getHighLightedFlows(Map<String, FlowElement> flowNodeMap,Map<String, HistoricActivityInstance> hisActivityMap,String activeActivityId,List<String> highLightedFlows,String oldActivityId) {
     // 获取有效节点的节点信息
     FlowElement flowElement = flowNodeMap.get(activeActivityId);
     FlowNode flowNode = (FlowNode) flowElement;
      // 获取连线信息集合
     List<SequenceFlow> incomingFlows = flowNode.getIncomingFlows();
     for (SequenceFlow sequenceFlow : incomingFlows) {
        // 获取连线ID
        String sourceRefId = sequenceFlow.getSourceRef();
        if (hisActivityMap.containsKey(sourceRefId) && !oldActivityId.equals(sourceRefId)) {
            highLightedFlows.add(sequenceFlow.getId());
            this.getHighLightedFlows(flowNodeMap, hisActivityMap, sourceRefId, highLightedFlows, oldActivityId);
        } else {
             if (hisActivityMap.containsKey(sourceRefId)) {
                highLightedFlows.add(sequenceFlow.getId());
              }
              break;
         }
      }
    }

}
// 获取流程引擎配置
ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
// 定义流程画布生成器
ProcessDiagramGenerator processDiagramGenerator = engConf.getProcessDiagramGenerator();
// 获取流程图字节流
InputStream in = processDiagramGenerator.generateDiagram(bpmnModel, FlowableConstants.PNG, activityIds, flows, engConf.getActivityFontName(), engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(), 1.0, true);
// 流程图文件名
String fileName = instanceId.replace("-", "");
// 将流程图保存到本地,并返回流程图在服务器上的访问地址
try {
    // ***该方法为本文4.5.6的方法***,应单独封装为一个工具类中的方法
    return FileUtil.writeFile(in, filePath, fileName, FlowableConstants.PNG);
} catch (IOException e) {
    return null;
}

根据流程定义ID获取流程图(流程图在服务器的访问路径)
// 获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 获取流程引擎配置
ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
// 定义流程画布生成器
ProcessDiagramGenerator processDiagramGenerator = engConf.getProcessDiagramGenerator();
InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel,FlowableConstants.PNG,engConf.getActivityFontName(),engConf.getLabelFontName(),engConf.getAnnotationFontName(),engConf.getClassLoader(),1.0,true);
// 流程图文件名
String fileName = bpmnModel.getTargetNamespace();
if (fileName == null || "".equals(fileName)) {
    fileName = processDefinitionId.substring(0, processDefinitionId.indexOf(":")).replace("-", "");
}
// 将流程图保存到本地
try {
    // ***该方法为本文4.5.6的方法***,应单独封装为一个工具类中的方法
    return FileUtil.writeFile(inputStream, filePath, fileName, FlowableConstants.PNG);
} catch (IOException e) {
    return null;
}

保存文件到服务器,并返回保存文件在服务器的路径
 /**
  * 保存文件并返回保存文件的路径
  *
  * @param inputStream 数据流
  * @param path        保存路径
  * @param fileName    文件名
  * @param fileType    文件类型
  * @return
*/
public static String writeFile(InputStream inputStream, String path, String fileName, String fileType) throws IOException {
    OutputStream out = null;
    // 按照文件类型分目录存放
    String folderPath = path + "/" + fileType + "/";
    File file = new File(folderPath);
    // 判断目录是否存在
    if (!file.exists()) {
        file.mkdirs();
    }
    // 完整文件名
    String fileNames = fileName + "." + fileType;
    // 文件存放路径
    String filePath = folderPath + fileNames;
    File image = new File(filePath);
    image.createNewFile();
    byte[] bytes = new byte[1024];
    int length = 0;
    try {
        out = new FileOutputStream(image);
        while ((length = inputStream.read(bytes)) != -1) {
        out.write(bytes, 0, length);
            }
        return fileType + "/" + fileNames;
    } finally {
        if (inputStream != null) {
            inputStream.close();
        }
        if (out != null) {
            out.close();
        }
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容