GitHub

Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。

创始人TomBaeyens是JBossjBPM的项目架构师,以及另一位架构师JoramBarrez,一起加入到创建Alfresco这项首次实现Apache开源许可的BPMN2.0引擎开发中来。

Activiti是一个独立运作和经营的开源项目品牌,并将独立于Alfresco开源ECM系统运行。Activiti将是一种轻量级,可嵌入的BPM引擎,而且还设计适用于可扩展的云架构。Activiti将提供宽松的Apache许可2.0,以便这个项目可以广泛被使用,同时促进ActivitiBPM引擎和的BPMN2.0的匹配,该项目现正由OMG通过标准审定。

以请假为例,现在好多中小公司请假流程是这样的

没有系统支撑:

采用工作流技术的公司的请假流程是这样的:

就这样,一个请假流程就结束了。

有人会问,那上级不用向公司提交请假记录?公司不用将记录录入电脑?答案是,用的。但是这一切的工作都会在上级点击允许后自动运行!这个些信息会自动的计入到数据库。随时支持查询,不需要再手动录入。

这就是工作流技术

Georgakopoulos给出的工作流定义是:工作流是将一组任务组织起来以完成某个经营过程:定义了任务的触发顺序和触发条件,每个任务可以由一个或多个软件系统完成,也可以由一个或一组人完成,还可以由一个或多个人与软件系统协作完。**

从上面的例子,很容易看出

Java开发者会为什么要学activiti工作流

在Java领域,JBPM和activiti是两个主流的工作流系统,而activiti的出现无疑将会取代JBPM(activiti的开发者就是从Jbpm开发者出来的)。

应用场景:

1)审批环节[维修审批,服务器审批,贷款审批....]

2)请假环节[学员请假,员工请假....]

"图"转换编程能力:

画图->xml文档-->编程解析

安装:

或者本地安装(Idea2018):

重启:

重启Idea

新建bpmn文件:放入资源文件resources目录。

Idea中:

Idea中注意箭头的画法:

注意:画图的时候需要思考,一个任务分配给谁执行呢?(当前任务)

工作流画的是公司的公共流程,针对所有人。工作流是一套公共的模板流程。

本质:这个请假流程图只需要画一次,就可以给公司的所有员工使用。

这里处理任务给的具体人。后期需要使用变量的方式做成通用流程。

(给属性Assignee赋值)

Activiti的运行支持,必须要有Activiti的25张表,主要是在流程运行过程中,记录存储一些参与流程的用户主体,组,以及流程定义的存储,流程执行时候的一些信息,以及流程的历史信息等.

org.activiti.engine.ProcessEngineConfiguration

引擎类

org.activiti.engine.ProcessEngine

packagecom.qf.act;importorg.activiti.engine.ProcessEngine;importorg.activiti.engine.ProcessEngineConfiguration;publicclassInitActivitiTable{//生成工作流需要的表publicvoidinitTable(){//流程引擎的配置对象,这种方式不需要配置文件ProcessEngineConfigurationengineConfiguration=ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();engineConfiguration.setJdbcDriver("org.gjt.mm.mysql.Driver");engineConfiguration.setJdbcUrl("jdbc:mysql:///qfcharacterEncoding=utf-8");engineConfiguration.setJdbcUsername("root");engineConfiguration.setJdbcPassword("");//设置表的更新engineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);//上面的代码就是加载配置,然后需要得到工作流的核心对象ProcessEngineprocessEngine=engineConfiguration.buildProcessEngine();//后续将使用ProcessEngine来操作工作流的表}publicstaticvoidmain(String[]args){InitActivitiTableactDAO=newInitActivitiTable();actDAO.initTable();}}刷新数据库,即可看到25张表

5种类型数据库表说明:

Activiti的后台是有数据库的支持,所有的表都以ACT_开头。第二部分是表示表的用途的两个字母标识。用途也和服务的API对应。

ACT_RE_*:'RE'表示repository。这个前缀的表包含了流程定义和流程静态资源(图片,规则,等等)。

ACT_RU_*:'RU'表示runtime。这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。Activiti只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这样运行时表可以一直很小速度很快。

ACT_ID_*:'ID'表示identity。这些表包含身份信息,比如用户,组等等。

ACT_HI_*:'HI'表示history。这些表包含历史数据,比如历史流程实例,变量,任务等等。

ACT_GE_*:'GE'表示general。通用数据,用于不同场景下,如存放资源文件。

这些表结构,还有一些辅助表。我们后续会详细解释,这里大家先大体了解即可;

详细表说明如下:日志表:

通用数据表:

默认的配置文件名称就是:activiti.cfg.xml

activiti.cfg.xml配置简化创建表功能

配置代码:

这里的核心就是运用了Spring的能力(IOC+DI)

测试观察结果:

深入理解:

这里没有导入Spring的包,但是为什么可以使用Spring的Bean的管理能力

其实这里已经由Spring整合了工作流。(这里使用了Spring的核心之一bean的管理IOC+DI)

依赖传递:

也就是这个开源项目,在开发的时候就已经加入了spring的包。

**同时注意:**这里它默认加入的spring的版本是4.1.5.未来整合到项目中的时候,可能和项目中的spring的包产生冲突。项目中把工作流引擎的包放到最后,加载顺序是从前到后,这样可以不需要排除可能的冲突。

认识流程图的本质

画流程图

复制一份bpmn文件重命名变为xml:

图和xml文件是一一对应的:

Idea中导出为图片:(了解)Eclipse中可以自动生成。

这样也是正常的,本身不需要生成,会自动生成到数据库中去。

工作流的流程图本质仍然是XML文件

创建Maven普通项目,画流程图

加入依赖和之前一致。

这个工作流的图要想生效,要想任务执行,就需要读取这个图的内容,也就是需要把这个内容添加到工作流引擎的数据库中去。最终工作流的所有信息都会在工作流引擎的数据库中有记录。

部署的位置起始就是工作流引擎的数据库。

流程定义和流程实例

得到引擎默认会加载配置文件activiti.cfg.xml(复制过来)

定义一个部署方法后再开发代码:

注意导包:org.activiti.engine.ProcessEngine;

//得到流程引擎对象privateProcessEngineprocessEngine=ProcessEngines.getDefaultProcessEngine();底层有加载配置文件:也说明这个文件的名称不能修改。

看初始化的方法:

读取配置文件activiti.cfg.xml的内容:

注意导包:org.activiti.engine.repository.Deployment;

//部署流程定义publicvoiddeplory(){//得到部署的serviceRepositoryServicerepositoryService=processEngine.getRepositoryService();//部署Deploymentdeploy=repositoryService.createDeployment().addClasspathResource("world.bpmn")//加载流程文件 .name("worldProcess")//流程名称.deploy();//部署//得到部署的信息System.out.println("部署ID"+deploy.getId());}部署流程名称可自定义

可不用添加图片,会自动生成加入库。

部署流程定义数据库表发生更新

1)部署表:

act_re_deployment流程定义部署表,插入了一条数据;

2)流程定义表:

act_re_prodef流程定义表也会有插入一条数据

这里有流程定义idnamekeyversion等重要信息;

后面可以通过接口来获取这些数据;

这个key对应的流程定义图的ID

后面就可以根据这个唯一的key来启动流程

3)资源信息表:

act_ge_bytearray表用来存资源信息,上传后的原始文件存储的地方。

部署ID20001

从工作角度,这个流程只需要部署一次,后面就可以多次使用。

空白地方单击看Id,需要依靠这个ID启动,对应在数据库存储的是流程定义表中的key_字段。

对应:

数据库流程运行表也会发生相应的变化;注意导包:org.activiti.engine.runtime.ProcessInstance

任务ID:

谁执行任务:

重要:ID_是任务id;PROC_INST_ID_是流程实例ID

这里是用具体的用户去执行

执行主题信息表:

流程实例启动完,接下来就到了具体任务节点:

查看指定用户的任务:

注意导包:org.activiti.engine.task.Task;

查询任务

publicvoidfindTask(){//得到任务serviceTaskServicetaskService=processEngine.getTaskService();//查询Listlist=taskService.createTaskQuery().taskAssignee("攻城狮")//按照指定人的名称查询.list();for(Tasktask:list){System.out.println("任务ID"+task.getId());System.out.println("任务名称"+task.getName());System.out.println("执行者"+task.getAssignee());System.out.println("流程定义ID"+task.getProcessDefinitionId());}}输出如下:(说明用户有任务可执行)

任务ID22504

任务名称工程师请假

执行者攻城狮

流程定义IDworldProcess:1:20004

继续走流程执行completeTask方法

这里的参数任务id是字符串。

//完成任务publicvoidcompletTask(){//得到任务serviceTaskServicetaskService=processEngine.getTaskService();taskService.complete("22504");}执行完后,流程其实就已经走完了。

再运行findTask,没有输出,已经没有任务了。

结束

数据库表变化:

历史表中有记录:

历史流程任务表:

历史流程实例表:

历史流程参与表:

历史活动节点表:

工作流框架自动操作底层数据库表,这个框架已经完成了所有持久层的操作,同时提供了服务层的各种service操作类。我们这里仅仅只需要编写流程业务。

开发流程

第一步:画流程图(通用模板)流程定义只需要定义一次。

第二步:部署流程图,需要把这个图部署到流程框架中,添加到数据库。只需要一次。

第四步:执行任务,完成当前自己的任务。(多次使用)

若实际项目,需要来动态导入流程定义文件,通过把bpmn[+png文件]打包成zip压缩包,然后用户界面直接导入到系统,然后解析,部署流程定义。

先画图。

把bpmn[+png]文件打成zip压缩包

png文件可选

结果:

publicvoiddeployZIP(){//加载zip文件InputStreaminputStream=this.getClass().getClassLoader().getResourceAsStream("world.zip");ZipInputStreamzipInputStream=newZipInputStream(inputStream);//得到部署的serviceRepositoryServicerepositoryService=processEngine.getRepositoryService();Deploymentdeploy=repositoryService.createDeployment().addZipInputStream(zipInputStream).name("worldProcess2").deploy();//得到部署的信息System.out.println("部署ID"+deploy.getId());}部署后表变化和之前一致,后面执行流程和之前一致。

部署ID27501,发现会把zip文件解压,同时生成有png文件。act_ge_bytearray

流动定义表act_re_procdef

从启动流程,到查询任务,到执行完成任务,跟前面一致。(注意任务ID)

注意:act_ge_property属性表

next_dbid是主键策略,就是规定好了下一次生成的id

某个流程定义不需要可删除它,我们可以通过接口,通过流程定义部署ID来删除流程定义。

表act_re_deployment

假如这个流程定义的流程实例在运行活动中未完结,不用级联删除会报错,所以尽量删除已完结的活动。

publicvoiddelByDeployId(){//尽量使用级联删除RepositoryServicerepositoryService=processEngine.getRepositoryService();repositoryService.deleteDeployment("1",true);}就算有关联的没有完成的任务也会删除。

流程定义是不能修改的

本身没有修改。

支持重新发布,版本不一样,就算修改。

使用key启动的流程实例,运行的是最新版本的流程。

流程定义查询:本质的话就是通过Activiti框架提供的API对act_re_procdef表进行查询操作;Activiti给我们提供非常丰富的API,用来模拟SQL查询;

api:

查询的是历史任务实例表act_hi_taskinst

l不管是已经完结的任务还是正在执行的任务,都会记录下这个表里。Activiti给我们提供了一个接口finished;加了之后就是查询已经完结的任务;同理还有一个接口unfinished顾名思义,就是查询未完结的任务;当然这两个都不加,就是把所有任务都查询出来;

可以用流程实例Id去运行时执行表去查,假如能查到数据,说明流程实例还是运行,假如没查到,就说明这个流程实例已经运行结束了;

表act_ru_execution

IDea中:

启动后到了请假申请节点,这时可以添加一些流程变量

packagecom.qf.dto;importjava.io.Serializable;/**ThanksforEverything.*/publicclassStuDTOimplementsSerializable{//序列化传递的对象privateLongsid;privateStringstuName;publicLonggetSid(){returnsid;}publicvoidsetSid(Longsid){this.sid=sid;}publicStringgetStuName(){returnstuName;}publicvoidsetStuName(StringstuName){this.stuName=stuName;}}流动实例开始后,完成下个任务节点,传递参数

设置参数方案一:

请假任务设置参数:

//完成学员的请假任务,添加请假的条件,天数,2,原因:publicvoidstuComplete(){TaskServicetaskService=processEngine.getTaskService();StringtaskId="112505";//请你提交请假原因taskService.setVariable(taskId,"days",3);taskService.setVariable(taskId,"reson","家长逼着回家相亲");//传员工对象StudentDTOdto=newStudentDTO(1,"张三");taskService.setVariable(taskId,"stu",dto);taskService.complete(taskId);}设置参数方案二:

在完成任务的的时候直接携带参数:

publicvoidstuComplete2(){TaskServicetaskService=processEngine.getTaskService();StringtaskId="120005";Mapvariables=newHashMap<>();variables.put("days",3);variables.put("reson","家长逼着回家相亲");StudentDTOdto=newStudentDTO(2,"张三");variables.put("stu",dto);taskService.complete(taskId,variables);}完成任务后,流程变量信息存储到历史变量实例表act_hi_varinst:

节点走到下一个节点,主管审批

首先获取之前任务节点设置的流程变量值:

publicvoidteaCom(){TaskServicetaskService=processEngine.getTaskService();StringtaskId="122507";//得到请假原因Integerdays=(Integer)taskService.getVariable(taskId,"days");System.out.println("请假天数:"+days);Stringreson=(String)taskService.getVariable(taskId,"reson");System.out.println("请假原因:"+reson);StudentDTOstu=(StudentDTO)taskService.getVariable(taskId,"stu");//学员信息System.out.println("学生ID:"+stu.getStuID()+"\t学生姓名:"+stu.getStuName());taskService.complete(taskId);}启动的时候传递变量

1)画图

2)部署

3)启动流程(流程开始工作),一般按照key启动

4)任务节点执行(同时包含任务查询)

5)任务之间传递参数,它可以传递基本数据类型,也可以传递对象,传递的对象必须序列化。

a)方案一:使用map封装。

b)方案二:使用方法setVariable

可能成在分支的情况。

可以在连线上设置条件。

流程实例具体执行的时候我们要通过设置流程变量的值来具体执行某个线路,这个时候还得设置连线的执行表达式.

给连线设置条件:

Condition=${day<=3}

Condition=${day>3}

Xml确认:

任务正常部署

学生请假任务完成时设置变量

传递参数,请假天数

//班主任审批publicvoidcompletTask2(){//得到任务serviceTaskServicetaskService=processEngine.getTaskService();StringtaskId="20003";//得到请假添加Integerdays=(Integer)taskService.getVariable(taskId,"days");System.out.println(days);Mapvariables=newHashMap<>();variables.put("days",days);//班主任设置请假天数,交给连线的执行表达式去判断。taskService.complete(taskId,variables);}由连线的执行表达式获得days的具体值,做判断,走符合条件的分支。

查看审批历史表act_ge_actinst:

//系主任审批publicvoidcompletTask3(){//得到任务serviceTaskServicetaskService=processEngine.getTaskService();taskService.complete("12502");}如果连线的执行表达式获取的值大于3,走系主任审批,查看审批历史表act_ge_actinst如下:

根据请假天数,来具体让谁来审批,用表达式来实现。

部署流程定义,启动流程实例,代码测试:

学生请假审批提交,班长和班主任审批,当他们都审批完才最终让校长审批。都审批不需要执行表达式。

任务的审批人需要指定好:

审批自动提交:

都审批后才提交给校长:

直接在流程图中Assignee中写具体分配的任务执行人

方案二:使用流程变量

设置流程变量Assignee${userame}

程序启动时设置值:

//员工完成请假任务publicvoidygCom(){TaskServicetaskService=processEngine.getTaskService();StringtaskId="57509";//60003taskService.setVariable(taskId,"days",3);taskService.setVariable(taskId,"reason","也要回家...");//提交给谁做审批。根据员工查询自己的主管是谁。Stringleader="兴宇";taskService.setVariable(taskId,"leader",leader);taskService.complete(taskId);}数据库任务表变化(act_ru_task):

开发流程:

POP3邮件接受协议

SMTP邮件发送协议

腾讯升级:

**不再用独立密码:**rzlbfauhfhuxbjeb

必须设置编码UTF-8

POM中加入编码插件:

org.apache.maven.pluginsmaven-resources-pluginUTF-8中文插件若识别失败,乱发发送会失败,用英文测试就可以了。

在activiti.cfg.xml文件中需要配置指定的邮件服务器

部署启动流程并自动发送邮件

画流程图:

方案一:

方案二:(推荐)

在springboot项目启动时自动配置了四个configuration,打开DataSourceProcessEngineAutoConfiguration配置类,在这个配置类里将SpringProcessEngineConfiguration注册成一个bean组件。

@Bean@ConditionalOnMissingBeanpublicSpringProcessEngineConfigurationspringProcessEngineConfiguration(DataSourcedataSource,PlatformTransactionManagertransactionManager,SpringAsyncExecutorspringAsyncExecutor)throwsIOException{returnthis.baseSpringProcessEngineConfiguration(dataSource,transactionManager,springAsyncExecutor);}SpringProcessEngineConfiguration继承了ProcessEngineConfigurationImpl,在这个组件里有一个buildProcessEngine()方法,此方法利用父类ProcessEngineConfigurationImpl的buildProcessEngine方法构建了ProcessEngine(activiti流程引擎的核心类)类,接下来我们来看看父类ProcessEngineConfigurationImpl的buildProcessEngine方法,这个方法有两部操作,一是调用了此类中的init方法,二是返回一个ProcessEngine对象。

每个角色初始化一个用户:

每个用户的密码初始为:admin

启动完成后,查看数据库,25张表创建完成,如下图所示:

选择文件:code\综合项目\流程定义\borrow.zip,点提交,如下图所示:

流程发布成功,显示列表,如下图所示:

选择菜单“我的申请记录”,显示出一条申请记录,状态为“等待经理审核”,如下图所示:

选择”同意“,填写备注,点击”确定“按钮,如下图所示:

选择”确定“按钮后,”我的待办事项“为空,如下图所示:

注意:财务没有审批权,只是在打款后确认汇款单号,填写汇款单号,点击”确定“按钮,如下图所示:

选“业务管理--->我的待办事项--->查看”,如下图所示:

员工点击”确定“按钮:

选择”我的申请记录“,看到刚才申请的纪录状态改为了”已成功“,如下图所示:

经理选择“同意”,录入备注信息后,点“确定”按钮,如下图所示:

总经理选择“拒绝”,点击“确定”按钮,如下图所示:

显示“借款流程”被驳回,点击“确定”按钮,如下图所示:

THE END
1.VS如何运行测试代码VS如何运行测试代码 简介 现在我们来看看,VS如何运行测试代码。工具/原料 联想2020 Win11 VS 2017 方法/步骤 1 我们可以使用最简单的办法直接点击启动按钮。2 或者我们点击菜单栏中的测试按钮。3 然后我们点击运行二级菜单。4 接下来我们点击所以测试按钮。5 也可以使用它的快捷键。6 同理,还可以使用它的调试命令https://jingyan.baidu.com/article/67662997a06a7d54d51b84e7.html
2.Python中如何实现代码测试问答在Python中,通常可以使用以下几种方法来实现代码测试:1. 单元测试(Unit testing):使用Python内置的unittest模块或第三方库如pytest来编写和运行单元测试,以验证https://www.yisu.com/ask/27942242.html
3.Golang中如何编写test测试代码这个输出结果是否是我们期望的!很多是否都要碰都这种情况,特别是在写一些项目的时候,文件很多代码也多,以前在不知道这样方法的时候就很恼火,以前在遇到这种情况的时候处理方法就是用IDE新建一个环境,然后把要测试的函数直接复制过去,但是这样不仅麻烦很多时候还没有办法做。https://www.jianshu.com/p/3e9aa5d20782
4.MSDN—探索如何创建测试和理解代码MicrosoftLearn创建代码 了解如何创建代码,获取工具,并立即开始学习。 测试代码 阅读有关测试代码的基础知识,获取工具,并立即了解更多信息。 理解代码 学习理解代码的过程,获取工具,并深入了解! 所有开发人员中心 展开表 任务 展开表 理解代码 理解代码涉及多个任务。工具可帮助您以直观形式查看和理解代码的组织、关系和行为。 https://msdn.microsoft.com/zh-cn/gg192976
5.如何创建临时查询,测试运行代码临时查询用于在本地测试代码的实际情况与期望值是否相符或排查代码错误。 背景信息 若您仅需在数据开发(DataStudio),即开发环境,查询数据及相关SQL代码、测试代码的实际运行情况与期望值是否相符,或验证代码的正确性,而无需将数据或SQL代码发布至生产环境并操作生产环境引擎,则可通过 新建临时查询文件实现。https://help.aliyun.com/zh/dataworks/user-guide/create-an-ad-hoc-query
6.四种常见的代码覆盖率测试当您添加新的功能和测试时,增加代码覆盖率百分比可以让您更加确信您的应用程序已经经过了彻底的测试。然而,还有更多的问题有待发现。 四种常见的代码覆盖类型 有四种常见的方法来收集和计算代码覆盖率:函数、行、分支和语句覆盖率。要查看每种类型的代码覆盖率如何计算其百分比,请思考以下计算咖啡成分的代码示例: https://blog.csdn.net/2301_81967508/article/details/144409765
7.谷歌指南:编写可测试的代码谷歌的软件工程师为了保持代码的最佳状态, 写了以下提醒. 缺陷: 构造器做了真正的工作 构造器中的工作包括: 创建/初始化协同类, 和其他service通信, 和设置自身状态的逻辑, 失去了测试的必要, 迫使subclass/mocks去继承不必要的行为. 构造器中太多的工作阻止了测试中的实例化或协同类的修改. https://zhuanlan.zhihu.com/p/146951456
8.JavaIDE如何使用代码覆盖率工具?在软件开发中,代码覆盖率是一个重要的质量指标,它用于衡量测试代码对实际代码的覆盖程度。Java 开发者常常使用 IntelliJ IDEA 这样的 IDE 来进行项目开发和代码测试。然而,有时在使用“Run with Coverage”功能时,会遇到一些问题,导致代码覆盖率无法正常工作。本文将介绍如何在 IntelliJ IDEA 中使用代码覆盖率工具,并https://blog.51cto.com/u_16175520/11996918
9.如何在树莓派4B上通过GoogleMediapipe解决方案实现手势检测6. 编写代码测试 接下来就可以尝试编写代码进行测试了,其中可以参考官方提供Python代码示例来进行学习: import cv2 import mediapipe as mp mp_drawing = mp.solutions.drawing_utils mp_drawing_styles = mp.solutions.drawing_styles mp_hands = mp.solutions.hands https://www.eet-china.com/mp/a148964.html
10.如何在Simulink中实现MIL的自动化测试?汽车测试技术自动化测试代码主要包括3个指令:xlsread、sim、xlswrite。 先用xlsread把测试用例的输入和期望输出读取进来;借助for循环,实现每一个测试用例的单独执行,这里会用到sim(‘XX.mdl’),把执行完的结果与期望的结果对比即可获得测试结果;最后把测试结果通过xlswrite写入excel。 https://www.auto-testing.net/news/show-107747.html
11.通过调试器如何在线测试S7SCL源代码?内容预览:描述: 调试器帮助对S7-SCL源代码进行一个简单调试,如果在编译S7-SCL 源代码过程没有错误发生,能够通过利用断点找出程序逻辑错误,并在运行时监视S7-SCL 代码的函数功能。过程如下面表格中描述: https://www.ad.siemens.com.cn/download/DocumentDetail_2719.html
12.静态代码分析测试静态分析如何保障软件质量和软件安全? 提高代码质量并降低缺陷成本 在开发过程的早期防止代码缺陷,以免它们在软件测试的后期阶段造成昂贵的成本。 满足行业功能安全标准 引入支持IS26262、DO-178C、IEC62304、IEC61508、EN50128等功能安全标准的静态分析解决方案。 https://www.parasoftchina.cn/solutions/static-code-analysis/