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();//查询List
任务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";Map
节点走到下一个节点,主管审批
首先获取之前任务节点设置的流程变量值:
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);Map
查看审批历史表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中加入编码插件:
在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,点提交,如下图所示:
流程发布成功,显示列表,如下图所示:
选择菜单“我的申请记录”,显示出一条申请记录,状态为“等待经理审核”,如下图所示:
选择”同意“,填写备注,点击”确定“按钮,如下图所示:
选择”确定“按钮后,”我的待办事项“为空,如下图所示:
注意:财务没有审批权,只是在打款后确认汇款单号,填写汇款单号,点击”确定“按钮,如下图所示:
选“业务管理--->我的待办事项--->查看”,如下图所示:
员工点击”确定“按钮:
选择”我的申请记录“,看到刚才申请的纪录状态改为了”已成功“,如下图所示:
经理选择“同意”,录入备注信息后,点“确定”按钮,如下图所示:
总经理选择“拒绝”,点击“确定”按钮,如下图所示:
显示“借款流程”被驳回,点击“确定”按钮,如下图所示: