空壳网站清理通知,浅谈京东企业的电子商务网站建设,做网站怎么入账,东营网站制作方案点击上方“好好学java”#xff0c;选择“置顶”公众号重磅资源、干货#xff0c;第一时间送达重磅推荐 ① 纯福利 | 公众号资源大汇总#xff0c;一年才一次#xff01;② 重磅#xff01;#xff01;2018年Java全套入门到进阶学习视频及项目实战③ 2018年java架构师学习… 点击上方“好好学java”选择“置顶”公众号重磅资源、干货第一时间送达重磅推荐 ① 纯福利 | 公众号资源大汇总一年才一次② 重磅2018年Java全套入门到进阶学习视频及项目实战③ 2018年java架构师学习视频教程资源④ 源码系列spring源码深度解析文章源码托管https://github.com/OUYANGSIHAI/Activiti-learninig欢迎 star 本来想着闲来无事前面在项目中刚刚用到了工作流 Activiti 框架写写博客的但是事情总是纷纷杂杂一直拖延到现在这一节原本想要写一下关于 Activiti 的 API 但是想着太多这样的博客了而且显得太生硬难以理解所以这些 API 就在实际的 demo 中来讲解。一、建立流程图 在开始做工作流之前我们首先应该把具体的业务在工作流的部署流程图体现出来并且都测试通过这样就相当于成功了一半后面的具体业务的开发就相对轻松一些了。首先我们先看一看在 idea 中有哪些控件常用的控件进行了标注。下面我们讲一下建立一个流程图的具体过程。首先我们需要拉入一个开始节点到 bpmn 文件中这是图像化的界面只需要拉入即可。然后我们从控件中拉入一个 UserTask 用户任务节点到 bpmn 文件中。这样子就有了两个审批节点了如果还需要其他的一些业务需求我们还可以加入一些网关这里就暂时不加了。最后我们只需要一个结束节点 EndEvent 就完成了这个工作流的部署图的绘制。我们最后看一下完整的例子。看似已经完成了整个流程图的绘制但美中不足的是我们目前并没有设置导师审批和辅导员审批到底由谁来审批所以我们还是需要来瞅一瞅怎么设置审批人员。首先我们需要选中一个审批节点例如选中导师审批这个节点。其次我们就显而易见的可以在 idea 编辑器的左侧看到一个名为 BPMN editor 的属性框里面包括一个用户任务节点的可以设置的所有属性。注意候选用户、候选组、任务监听器这三个属性这里暂时不讲后面再补充。由于这一步我们需要设置审批人所以我们需要在 Assignee 这个属性中设置我们的审批人。如上图这里设置导师审批这个节点的审批人为 sihai 。设置审批人除了直接设置之外还有两种方式设置后面再补充。另外一个审批节点也通过这种方式设置就可以完成审批人的设置了。very good这样就基本完成了一个流程图的创建。接下来我们将通过实例来具体讲解Activiti 的 API 的讲解。二、实例讲解 API 在上面这个流程图的创建中我们还没有生成 png 图片所以如果不知道如何生成的可以参考之前的这篇文章Activiti工作流从入门到入土整合spring。既然是讲解 API 那么还是先看一下主要有哪些 API 吧这样才有一个整体把握。这些 API 具体怎么用接下来一一道来。2.1 流程定义既然是流程定义那肯定少不了如何部署流程定义了。2.1.1 部署流程定义方法1 Autowired private ProcessEngine processEngine; Autowired private TaskService taskService; Autowired private RuntimeService runtimeService; Autowired private HistoryService historyService; /** * 部署流程定义(从classpath) */ Test public void deploymentProcessDefinition_classpath(){ Deployment deployment processEngine.getRepositoryService()//与流程定义和部署对象相关的Service .createDeployment()//创建一个部署对象 .name(流程定义)//添加部署的名称 .addClasspathResource(bpmn/hello.bpmn)//从classpath的资源中加载一次只能加载一个文件 .addClasspathResource(bpmn/hello.png)//从classpath的资源中加载一次只能加载一个文件 .deploy();//完成部署 System.out.println(部署IDdeployment.getId()); System.out.println(部署名称deployment.getName()); }注意这里用的是整合 spring 之后的 junit 测试环境如何整合 spring 请看这篇文章Activiti工作流从入门到入土整合spring。输出结果这样我们就部署了这个流程。那么具体是怎么操作的呢我们再来看看整个过程。获取流程引擎对象这个跟 spring 整合了。通过流程引擎获取了一个 RepositoryService 对象(仓库对象)由仓库的服务对象产生一个部署对象配置对象用来封装部署操作的相关配置。这是一个链式编程在部署配置对象中设置显示名上传流程定义规则文件向数据库表中存放流程定义的规则信息。其实这一步操作用到了 Activiti 数据库中的三张表分别是act_re_deployment(部署对象表)act_re_procdef(流程定义表)act_ge_bytearray(资源文件表)。我们看看这三张表的变化1)act_re_deployment可以看到部署ID和部署名称就存在这张表中。2)act_re_procdef这张表中存放了部署的Deployment_ID部署流程的id、bpmn资源文件名称、png图片名称等信息。3)act_ge_bytearray存储流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录一条是关于 bpmn 规则文件的一条是图片的(如果部署时只指定了 bpmn 一个文件activiti 会在部署时解析 bpmn 文件内容自动生成流程图)。两个文件不是很大都是以二进制形式存储在数据库中。2.1.2 部署流程定义方法2 /** * 部署流程定义(从zip) */ Testpublic void deploymentProcessDefinition_zip(){ InputStream in this.getClass().getClassLoader().getResourceAsStream(bpmn/hello.zip); ZipInputStream zipInputStream new ZipInputStream(in); Deployment deployment processEngine.getRepositoryService()//与流程定义和部署对象相关的Service .createDeployment()//创建一个部署对象 .name(流程定义)//添加部署的名称 .addZipInputStream(zipInputStream)//指定zip格式的文件完成部署 .deploy();//完成部署 System.out.println(部署IDdeployment.getId());// System.out.println(部署名称deployment.getName());// }项目结构如下输出结果如此看来也是没有任何问题的唯一的区别只是压缩成zip格式的文件使用zip的输入流用作部署流程定义其他使用并无区别。部署了流程定义之后我们应该想查看一下流程定义的一些信息。2.1.3 查看流程定义/** * 查询流程定义 */ Testpublic void findProcessDefinition(){ List list processEngine.getRepositoryService()//与流程定义和部署对象相关的Service .createProcessDefinitionQuery()//创建一个流程定义的查询/**指定查询条件,where条件*/// .deploymentId(deploymentId)//使用部署对象ID查询// .processDefinitionId(processDefinitionId)//使用流程定义ID查询// .processDefinitionKey(processDefinitionKey)//使用流程定义的key查询// .processDefinitionNameLike(processDefinitionNameLike)//使用流程定义的名称模糊查询/**排序*/ .orderByProcessDefinitionVersion().asc()//按照版本的升序排列// .orderByProcessDefinitionName().desc()//按照流程定义的名称降序排列/**返回的结果集*/ .list();//返回一个集合列表封装流程定义// .singleResult();//返回惟一结果集// .count();//返回结果集数量// .listPage(firstResult, maxResults);//分页查询if(list!null list.size()0){for(ProcessDefinition pd:list){ System.out.println(流程定义ID:pd.getId());//流程定义的key版本随机生成数 System.out.println(流程定义的名称:pd.getName());//对应hello.bpmn文件中的name属性值 System.out.println(流程定义的key:pd.getKey());//对应hello.bpmn文件中的id属性值 System.out.println(流程定义的版本:pd.getVersion());//当流程定义的key值相同的相同下版本升级默认1 System.out.println(资源名称bpmn文件:pd.getResourceName()); System.out.println(资源名称png文件:pd.getDiagramResourceName()); System.out.println(部署对象IDpd.getDeploymentId()); System.out.println(*********************************************); } } }输出结果查询流程定义小结流程定义和部署对象相关的Service都是 RepositoryService 后面会发现关于流程定义的都是 RepositoryService。通过这个 createProcessDefinitionQuery() 方法来设置一些查询参数比如通过条件、降序升序等。2.1.4 删除流程定义通过删除部署 ID 为2501的信息。/** * 删除流程定义 */ Testpublic void deleteProcessDefinition(){ //使用部署ID完成删除指定部署对象id为2501删除 String deploymentId 2501; /** * 不带级联的删除 * 只能删除没有启动的流程如果流程启动就会抛出异常 */// processEngine.getRepositoryService()//// .deleteDeployment(deploymentId); /** * 级联删除 * 不管流程是否启动都能可以删除 */ processEngine.getRepositoryService()// .deleteDeployment(deploymentId, true); System.out.println(删除成功); }输出结果到数据库查看发现 act_re_deployment 中的数据已经不存在了。这里还是通过 getRepositoryService() 方法获取部署定义对象然后指定 ID 删除信息。2.1.5 获取流程定义文档的资源这里的作用主要是查询图片通过图片可以在后面做流程展示用的。我们看看具体怎么查看。/** * 查看流程图 * * throws IOException */ Test public void viewPic() throws IOException { /**将生成图片放到文件夹下*/ String deploymentId 5001; //获取图片资源名称 ListString list processEngine.getRepositoryService()// .getDeploymentResourceNames(deploymentId); //定义图片资源的名称 String resourceName ; if (list ! null list.size() 0) { for (String name : list) { if (name.indexOf(.png) 0) { resourceName name; } } } //获取图片的输入流 InputStream in processEngine.getRepositoryService()// .getResourceAsStream(deploymentId, resourceName); //将图片生成到F盘的目录下 File file new File(F:/ resourceName); //将输入流的图片写到磁盘 FileUtils.copyInputStreamToFile(in, file); }在F盘下可以找到图片。2.1.6 查询最新版本的流程定义 /** * 查询最新版本的流程定义 */ Test public void findLastVersionProcessDefinition() { List list processEngine.getRepositoryService()// .createProcessDefinitionQuery()// .orderByProcessDefinitionVersion().asc()//使用流程定义的版本升序排列 .list();/** map集合的特点当map集合key值相同的情况下后一次的值将替换前一次的值 */ Map map new LinkedHashMap();if (list ! null list.size() 0) {for (ProcessDefinition pd : list) { map.put(pd.getKey(), pd); } } List pdList new ArrayList(map.values());if (pdList ! null pdList.size() 0) {for (ProcessDefinition pd : pdList) { System.out.println(流程定义ID: pd.getId());//流程定义的key版本随机生成数 System.out.println(流程定义的名称: pd.getName());//对应hello.bpmn文件中的name属性值 System.out.println(流程定义的key: pd.getKey());//对应hello.bpmn文件中的id属性值 System.out.println(流程定义的版本: pd.getVersion());//当流程定义的key值相同的相同下版本升级默认1 System.out.println(资源名称bpmn文件: pd.getResourceName()); System.out.println(资源名称png文件: pd.getDiagramResourceName()); System.out.println(部署对象ID pd.getDeploymentId()); System.out.println(*********************************************************************************); } } }输出结果2.1.7 流程定义总结1、部署流程定义用到了 Activiti 的下面的几张表。act_re_deployment部署对象表act_re_procdef流程定义表act_ge_bytearray资源文件表act_ge_property主键生成策略表2、我们发现部署流程定义的操作都是在 RepositoryService 这个类下进行操作的我们只需要通过 getRepositoryService() 拿到对象通过链式规则就可以进行部署流程定义的所有操作。2.2 工作流完整实例的使用这一节我们通过一个完整的例子来总结一下前面讲过的一些基本的知识这样能够更好的学习前面以及后面的知识点这也算是一个过渡的章节。回到第一节的建立流程图我们已经将基本的 bpmn 图已经建立好了但是需要做一个完整的实例我们还是需要补充一些内容的这样才能够把这样的一个实例做好我们先把第一节的那个 bpmn 图拿过来。首先我们需要明确这个图到目前为止我们只是简简单单的把流程给画出来了比如我们需要审核的时候是需要具体到某一个具体的人员去审核的所以我们需要给每个节点设置审核的具体人员。注意设置节点的审核人员后面还会分一节细讲这里只是做一个简单的实例所以只需要这里能够看懂做好就ok了。设置审核人员步骤首先我们需要选中一个节点例如下图中的“导师审批”节点。接下来在左边的工具栏我们会看到好多选项有一项为 Assignee 我们需要在这个选项中设置我们这个节点需要设置的审批人。Assignee设置格式直接使用英文或者中文都可以例如sihai更复杂的设置后面再讲。下面的节点设置也是跟上面一模一样。辅导员审批的审批人员是欧阳思海。perfect这样流程图的任务就完成了下面我们就可以进行这个实例的测试阶段了。1)部署流程定义部署流程定义在前面的章节已经讲过了有两种方式进行处理一种是加载 bpmn 文件和 png 文件还有一种是将这两个文件压缩成 zip 格式的压缩文件然后加载。这里我们使用第一种方式进行处理。/** * 部署流程定义(从classpath) */ Testpublic void deploymentProcessDefinition_classpath() { Deployment deployment processEngine.getRepositoryService()//与流程定义和部署对象相关的Service .createDeployment()//创建一个部署对象 .name(hello)//添加部署的名称 .addClasspathResource(bpmn/hello.bpmn)//从classpath的资源中加载一次只能加载一个文件 .addClasspathResource(bpmn/hello.png)//从classpath的资源中加载一次只能加载一个文件 .deploy();//完成部署 log.info(部署ID deployment.getId()); log.info(部署名称 deployment.getName()); }现在流程定义已经有了下面我们就需要启动这个流程实例。关于关于这一步做了什么事情可以在前面的章节查看。2)启动流程实例 /** * 启动流程实例 */ Testpublic void startProcessInstance(){ //1、流程定义的key通过这个key来启动流程实例 String processDefinitionKey hello; //2、与正在执行的流程实例和执行对象相关的Service // startProcessInstanceByKey方法还可以设置其他的参数比如流程变量。 ProcessInstance pi processEngine.getRuntimeService() .startProcessInstanceByKey(processDefinitionKey);//使用流程定义的key启动流程实例key对应helloworld.bpmn文件中id的属性值使用key值启动默认是按照最新版本的流程定义启动 log.info(流程实例ID:pi.getId());//流程实例ID log.info(流程定义ID:pi.getProcessDefinitionId());//流程定义ID }注意 processDefinitionKey 是 bpmn 文件的名称。步骤1 获取到 runtimeService 实例。2 通过 bpmn 文件的名称也就是 processDefinitionKey 来启动流程实例。3 启动流程后流程的任务就走到了导师审批节点。下面就是查询个人任务了我们可以查询导师审批节点的任务。3)查询个人任务/** * 查询当前人的个人任务 */ Testpublic void findPersonalTask(){ String assignee sihai; List list processEngine.getTaskService()//与正在执行的任务管理相关的Service .createTaskQuery()//创建任务查询对象/**查询条件(where部分)*/ .taskAssignee(assignee)//指定个人任务查询指定办理人// .taskCandidateUser(candidateUser)//组任务的办理人查询// .processDefinitionId(processDefinitionId)//使用流程定义ID查询// .processInstanceId(processInstanceId)//使用流程实例ID查询// .executionId(executionId)//使用执行对象ID查询/**排序*/ .orderByTaskCreateTime().asc()//使用创建时间的升序排列/**返回结果集*/// .singleResult()//返回惟一结果集// .count()//返回结果集的数量// .listPage(firstResult, maxResults);//分页查询 .list();//返回列表if(list!null list.size()0){for(Task task:list){log.info(任务ID:task.getId());log.info(任务名称:task.getName());log.info(任务的创建时间:task.getCreateTime());log.info(任务的办理人:task.getAssignee());log.info(流程实例IDtask.getProcessInstanceId());log.info(执行对象ID:task.getExecutionId());log.info(流程定义ID:task.getProcessDefinitionId());log.info(********************************************); } } }通过 sihai 这个审批人查询到了下面的信息。分析步骤1 首先通过 getTaskService 方法获取到 TaskService 对象。2 通过 createTaskQuery 方法创建查询对象。3 通过 taskAssignee 方法设置审核人。4 对于结果的返回我们可以通过 orderByTaskCreateTime().asc() 设置排序等其他信息。这里需要注意一点查询到的一个重要的信息是任务 id(taskId)下一步我们需要通过这个任务 id 来完成任务。4)办理个人任务/** * 完成我的任务 */ Testpublic void completePersonalTask() { //任务ID上一步查询得到的。 String taskId 7504; processEngine.getTaskService()//与正在执行的任务管理相关的Service .complete(taskId); log.info(完成任务任务ID taskId); }通过上一步的任务 id 7504完成任务。步骤1 首先通过 getTaskService 方法拿到 TaskService 对象。2 调用 complete 方法给定具体的任务 id 完成任务。5)查询流程状态(判断流程走到哪一个节点)这个接口还是十分需要的当我们在具体的业务中我们需要判断我们的流程的状态是什么状态或者说我们的流程走到了哪一个节点的时候这一个接口就让我们实现业务省了非常多的事情。/** * 查询流程状态(判断流程走到哪一个节点) */ Testpublic void isProcessActive() { String processInstanceId 7501; ProcessInstance pi processEngine.getRuntimeService()//表示正在执行的流程实例和执行对象 .createProcessInstanceQuery()//创建流程实例查询 .processInstanceId(processInstanceId)//使用流程实例ID查询 .singleResult(); if (pi null) { log.info(流程已经结束); } else { log.info(流程没有结束); //获取任务状态 log.info(节点id pi.getActivityId()); } }步骤1 获取到流程实例 ProcessInstance 对象。2 通过 getActivityId 方法获取到实例 Id(节点 id )。那么拿到了节点 Id 有什么作用呢其实有了这个 Id 之后我们就可以判断流程走到哪一步了。例如上面的输出的节点 id 是 _4这个 _4 就是对应 辅导员审批节点的 id所以我们就可以判读流程其实是已经走到这个节点了后期需要在页面显示流程状态的时候就发挥作用了。6)查询流程执行的历史信息通过查看 activiti 5 的官方 API 接口发现查看历史信息有下面的查询接口。下面我们通过上面的实例对下面的方法一一进行测试。历史活动实例查询接口/** * 历史活动查询接口 */ Testpublic void findHistoryActivity() { String processInstanceId 7501; List hais processEngine.getHistoryService()// .createHistoricActivityInstanceQuery() .processInstanceId(processInstanceId) .list();for (HistoricActivityInstance hai : hais) {log.info(活动id hai.getActivityId() 审批人 hai.getAssignee() 任务id hai.getTaskId());log.info(************************************); } }通过这个接口不仅仅查到这些信息还有其他的方法可以获取更多的关于历史活动的其他信息。历史流程实例查询接口/** * 查询历史流程实例 */ Testpublic void findHistoryProcessInstance() { String processInstanceId 7501; HistoricProcessInstance hpi processEngine.getHistoryService()// 与历史数据(历史表)相关的Service .createHistoricProcessInstanceQuery()// 创建历史流程实例查询 .processInstanceId(processInstanceId)// 使用流程实例ID查询 .orderByProcessInstanceStartTime().asc().singleResult(); log.info(hpi.getId() hpi.getProcessDefinitionId() hpi.getStartTime() hpi.getEndTime() hpi.getDurationInMillis()); }这个接口可以查询到关于历史流程实例的所有信息。历史任务实例查询接口 /** * 查询历史任务 */ Testpublic void findHistoryTask() { String processInstanceId 7501; List list processEngine.getHistoryService()// 与历史数据(历史表)相关的Service .createHistoricTaskInstanceQuery()// 创建历史任务实例查询 .processInstanceId(processInstanceId)// .orderByHistoricTaskInstanceStartTime().asc().list();if (list ! null list.size() 0) {for (HistoricTaskInstance hti : list) {log.info(\n 任务Id hti.getId() 任务名称 hti.getName() 流程实例Id hti.getProcessInstanceId() \n 开始时间 hti.getStartTime() 结束时间 hti.getEndTime() 持续时间 hti.getDurationInMillis()); } } }这个查询接口可以查询到历史任务信息。历史流程变量查询接口/** * 查询历史流程变量 */ Testpublic void findHistoryProcessVariables() { String processInstanceId 7501; List list processEngine.getHistoryService()// .createHistoricVariableInstanceQuery()// 创建一个历史的流程变量查询对象 .processInstanceId(processInstanceId)// .list();if (list ! null list.size() 0) {for (HistoricVariableInstance hvi : list) {log.info(\n hvi.getId() hvi.getProcessInstanceId() \n hvi.getVariableName() hvi.getVariableTypeName() hvi.getValue()); } } }在这个实例中没有设置流程变量所以这里是查询不到任何历史信息的。这个接口主要是关于历史流程变量的设置的一些信息。历史本地接口查询接口/** * 通过执行sql来查询历史数据由于activiti底层就是数据库表。 */ Testpublic void findHistoryByNative() { HistoricProcessInstance hpi processEngine.getHistoryService() .createNativeHistoricProcessInstanceQuery() .sql(查询底层数据库表的sql语句) .singleResult(); log.info(\n hpi.getId() hpi.getProcessDefinitionId() hpi.getStartTime() \n hpi.getEndTime() hpi.getDurationInMillis()); }这个接口是提供直接通过 sql 语句来查询历史信息的我们只需要在 sql() 方法中写原生的 sql 语句就可以进行数据查询。写到这里我想应该通过这样的一个完整的实例将 Activiti 工作流的 API 都介绍的差不多了这一节到这里也就要说拜拜了。再回看一下文章开头的 API 接口这也算是这一节的总结。推荐阅读1. 重磅Java基础就业课程IDEA版本2. 物流云商项目源码及教程3. 疯狂学习 SpringCloud 教程4. dubbo 最新学习视频教程你「在看」吗??点击「阅读原文」更多精彩