本教程用于记录activiti-app中的模型、实例、任务、自定义表单、决策表的使用体验,以提升对activiti的理解,便于更进一步的学习
activiti简介
一款优秀的老牌开源流程框架,以bpmn作为流程的定义模型,从github上看,最早的一个文件都是十年前,从时间上看是完全经得起考验的,并且本身足够强大,功能也相当丰富,前前后后经历了5-6-7三个版本,目前官方主要维护activiti7。本文主要基于activiti6学习和体验。
流程引擎的主要工作则是:将业务流程图运转起来,如:请假审批中,填写请假单,审批请假条称为任务节点;请假条填写后,负责审批的人,就能看到审批请假的任务,这个称为任务流转;若请假理由或时间不合适,需要将请假单申请驳回给发起申请人重新填写等。这些每个任务节点需要填写的表单内容自动完成任务的流转以及什么事件标志流程结束等都是通过bpmn语言约束好的。好在activiti提供了在线设计器,以画图的方式设计流程,不需要手写bpmn语言。通过设计器绘制的流程称为模型。由于一个流程中会携带各种业务参数,将定义好业务参数的模型运行起来称为流程实例。因此一个模型会对应多个流程示例。activiti为了更好的解释他们流程引擎的应用场景和使用教学,提供了一个官方demo:activti-app。里面提供了模型、实例、任务、自定义表单、决策表等的使用场景
下载与运行
两种方式可以下载:1.去官网下载war包,并在tomcat下部署运行。2.前往github拉取源码,导入本地打包运行
这里为了方便后面的学习,选择方式二,拉取源码到本地运行
1.编译代码mvn install
下载代码后,进入项目跟目录,执行mvn install:
2.进入modules/activiti-ui/
下
activiti-app就在该模块下,可见已经提供运行的脚本:
进入git bash here
使用git提供的linux环境执行start.sh
脚本
运行后,会自动编译项目,并打包运行activiti-app
项目,访问地址为:http://localhost:9999/activiti-app/
,需要账号和密码(admin/test)
3. 将项目导入idea运行
在idea中用maven项目的方式导入activiti-ui
可以看到有7个子项目,这里重点关注前四个子项目
activiti-app 最终运行的war包,该项目webapp下集成了activiti-app的前端文件(基于angularJS,可以独立运行,实际上前后端分离的)
activiti-app-conf 与spring项目集成的各种配置项
activiti-app-logic 提供service以及repository层的逻辑代码
activiti-app-rest 提供前端请求的api接口层的逻辑代码
activiti-app 集成mvn的tomcat7插件,在该模块的pom文件种可以看到其配置的端口以及启动项目名称信息:
因此在idea种只需要以mvn插件方式即可运行,步骤如下:
- 打开右上角启动按钮旁边的
run/debug configurations
- 找到maven组件
- 指定
working Dir
和command line
为:tomcat7:run
配置完毕后即可在idea种run/debug启动
4.修改数据库
activiti-app中提供了多种数据类型支持,默认数据库为h2(不用惊讶,该数据库基于内存,无需安装运行),现修改为mysql:
tips: 创建一个专用数据库即可,第一次启动会自动创建所需的数据表,项目使用liquibase管理数据库
在最下方提供了默认管理员账号和密码:
使用介绍
访问:http://localhost:9999/activiti-app
,使用账号/密码:admin/test
登录到系统:
Kickstart App:是用来设计模型,表单,决策表和部署实例
Task App:实例部署之后,在里面运行,以及各个流程完成任务
Identity management:提供管理用户以及用户组信息
1.进入Identity management创建用户
创建完成后,就能给将任务指定给对应的用户
2.设计流程模型
进入Kickstart App,点击create process
按钮创建一个流程模型
绘制如图一个流程图
tips:为了更加美观的画图,可以使用工具栏的ben-point
工具灵活的添加和删除弯折点
首先点击任务"发起事件"节点,点击下方Referenced form
在弹出框中选择New From
创建一个请假表单:
将请假天数id设置为days
将请假理由id设置为reason
,这里的days
和reason
在当前任务后面的所有节点中都可以获取到,并以流程变量的形式展示,流转控制主要是由流程变量去控制
比如接下来的排他网关(有且仅有一个表达式命中)使用变量days
去控制流程的流传:
点击"大于半个月"这条连线:
在下方的flow condition
中设置表达式为: ${days>15}
同理将连线"小于半个月"的flow condition
中设置表达式为: ${days<=15}
此时任务在流转时候就会根据任务"发起事件"中 请假天数days
去控制流转方向
然后将大毛子审批
和小毛子审批
任务节点添加操作意见的Reference Form
:
将id设置为opt
options中添加同意和不同意两种单选枚举项:
点击任务大毛子审批
,将下方的Assignment添加为User:大毛子
小毛子审批同理
最后设置结束的排他网关的条件表达式:
- 选中连线
同意
设置Flow condition
为:${opt=='同意'}
- 选中连线
不同意
设置Flow condition
为:${opt=='不同意'}
tips: Flow condition中字符串类型必须用单引号包裹起来,字符串和boolean类型可以直接写
最后给每一个任务节点,每个连线填写上不同的id标识
整体流程图导出成bpmn格式内容为:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
<process id="modelKey" name="modelName" isExecutable="true">
<documentation>description</documentation>
<startEvent id="startEvent1"></startEvent>
<userTask id="task1" name="发起事件" default="sid-F8DC7A50-70B4-45E5-99E3-FEA4E13F1028" activiti:formKey="askFor"></userTask>
<sequenceFlow id="sid-02F03600-38E2-44A5-A20A-358B98614F42" sourceRef="startEvent1" targetRef="task1"></sequenceFlow>
<exclusiveGateway id="choose1"></exclusiveGateway>
<userTask id="task2" name="大毛子审批" default="sid-E68E6CD3-672F-4F3C-8080-8D4FD062AE9E" activiti:candidateUsers="damao" activiti:formKey="opt">
<extensionElements>
<modeler:user-info-email-damao xmlns:modeler="http://activiti.com/modeler"><![CDATA[213@qq.com]]></modeler:user-info-email-damao>
<modeler:user-info-firstname-damao xmlns:modeler="http://activiti.com/modeler"><![CDATA[大毛子]]></modeler:user-info-firstname-damao>
<modeler:user-info-lastname-damao xmlns:modeler="http://activiti.com/modeler"><![CDATA[大毛子]]></modeler:user-info-lastname-damao>
<modeler:activiti-idm-candidate-user xmlns:modeler="http://activiti.com/modeler"><![CDATA[true]]></modeler:activiti-idm-candidate-user>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<userTask id="task3" name="小毛子审批" default="sid-F7C32E74-ABE8-4559-87FF-F2595F1D5A04" activiti:candidateUsers="xiaomao" activiti:formKey="opt">
<extensionElements>
<modeler:user-info-email-xiaomao xmlns:modeler="http://activiti.com/modeler"><![CDATA[xiaomao@ll.com]]></modeler:user-info-email-xiaomao>
<modeler:user-info-firstname-xiaomao xmlns:modeler="http://activiti.com/modeler"><![CDATA[小毛子]]></modeler:user-info-firstname-xiaomao>
<modeler:user-info-lastname-xiaomao xmlns:modeler="http://activiti.com/modeler"><![CDATA[小毛子]]></modeler:user-info-lastname-xiaomao>
<modeler:activiti-idm-candidate-user xmlns:modeler="http://activiti.com/modeler"><![CDATA[true]]></modeler:activiti-idm-candidate-user>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<exclusiveGateway id="choose2"></exclusiveGateway>
<endEvent id="sid-5CF50DF5-0074-423E-9425-2DD04BEA7D0F"></endEvent>
<sequenceFlow id="sid-F8DC7A50-70B4-45E5-99E3-FEA4E13F1028" sourceRef="task1" targetRef="choose1"></sequenceFlow>
<sequenceFlow id="sid-BCA9144E-5B52-41C1-9FD5-5BA9C7DCC41E" name="不同意" sourceRef="choose2" targetRef="task1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${opt=='不同意'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-F7C32E74-ABE8-4559-87FF-F2595F1D5A04" sourceRef="task3" targetRef="choose2"></sequenceFlow>
<sequenceFlow id="sid-E68E6CD3-672F-4F3C-8080-8D4FD062AE9E" sourceRef="task2" targetRef="choose2"></sequenceFlow>
<sequenceFlow id="longDays" name="大于半个月" sourceRef="choose1" targetRef="task2">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${days>15}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="shortDays" name="小于半个月" sourceRef="choose1" targetRef="task3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${days<=15}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-E4010791-0102-441F-8230-9C6C4E24D11C" name="同意" sourceRef="choose2" targetRef="sid-5CF50DF5-0074-423E-9425-2DD04BEA7D0F">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${opt=='同意'}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_modelKey">
<bpmndi:BPMNPlane bpmnElement="modelKey" id="BPMNPlane_modelKey">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="45.0" y="163.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="task1" id="BPMNShape_task1">
<omgdc:Bounds height="80.0" width="100.0" x="135.0" y="138.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="choose1" id="BPMNShape_choose1">
<omgdc:Bounds height="40.0" width="40.0" x="320.0" y="158.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="task2" id="BPMNShape_task2">
<omgdc:Bounds height="80.0" width="100.0" x="435.0" y="45.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="task3" id="BPMNShape_task3">
<omgdc:Bounds height="80.0" width="100.0" x="435.0" y="240.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="choose2" id="BPMNShape_choose2">
<omgdc:Bounds height="40.0" width="40.0" x="645.0" y="165.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-5CF50DF5-0074-423E-9425-2DD04BEA7D0F" id="BPMNShape_sid-5CF50DF5-0074-423E-9425-2DD04BEA7D0F">
<omgdc:Bounds height="28.0" width="28.0" x="765.0" y="171.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="longDays" id="BPMNEdge_longDays">
<omgdi:waypoint x="346.993006993007" y="164.993006993007"></omgdi:waypoint>
<omgdi:waypoint x="390.0" y="85.0"></omgdi:waypoint>
<omgdi:waypoint x="435.0" y="85.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-BCA9144E-5B52-41C1-9FD5-5BA9C7DCC41E" id="BPMNEdge_sid-BCA9144E-5B52-41C1-9FD5-5BA9C7DCC41E">
<omgdi:waypoint x="665.5" y="165.5"></omgdi:waypoint>
<omgdi:waypoint x="665.5" y="12.0"></omgdi:waypoint>
<omgdi:waypoint x="185.0" y="12.0"></omgdi:waypoint>
<omgdi:waypoint x="185.0" y="138.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="shortDays" id="BPMNEdge_shortDays">
<omgdi:waypoint x="346.30872483221475" y="191.69127516778525"></omgdi:waypoint>
<omgdi:waypoint x="387.0" y="280.0"></omgdi:waypoint>
<omgdi:waypoint x="435.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-F8DC7A50-70B4-45E5-99E3-FEA4E13F1028" id="BPMNEdge_sid-F8DC7A50-70B4-45E5-99E3-FEA4E13F1028">
<omgdi:waypoint x="235.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="320.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-E68E6CD3-672F-4F3C-8080-8D4FD062AE9E" id="BPMNEdge_sid-E68E6CD3-672F-4F3C-8080-8D4FD062AE9E">
<omgdi:waypoint x="535.0" y="85.0"></omgdi:waypoint>
<omgdi:waypoint x="577.4000244140625" y="85.0"></omgdi:waypoint>
<omgdi:waypoint x="655.6609821976433" y="174.3390178023567"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-F7C32E74-ABE8-4559-87FF-F2595F1D5A04" id="BPMNEdge_sid-F7C32E74-ABE8-4559-87FF-F2595F1D5A04">
<omgdi:waypoint x="535.0" y="280.0"></omgdi:waypoint>
<omgdi:waypoint x="582.4000244140625" y="280.0"></omgdi:waypoint>
<omgdi:waypoint x="655.698199668843" y="195.698199668843"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-02F03600-38E2-44A5-A20A-358B98614F42" id="BPMNEdge_sid-02F03600-38E2-44A5-A20A-358B98614F42">
<omgdi:waypoint x="75.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="135.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-E4010791-0102-441F-8230-9C6C4E24D11C" id="BPMNEdge_sid-E4010791-0102-441F-8230-9C6C4E24D11C">
<omgdi:waypoint x="685.0" y="185.0"></omgdi:waypoint>
<omgdi:waypoint x="765.0" y="185.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
3.部署实例
一个流程模型对应了多个实例,每一个部署的实例,都部署的在部署时刻的流程模型的快照,后续对流程模型的任何修改不会影响到已经部署的实例中。
首先进入Kicstart App/App中,点击按钮create Ap
p创建实例
创建完成后使用App Editor
进入编辑界面:
点击Edit include models
添加此app包含的流程模型,最后点击保存按钮退出编辑器。
app信息页面顶部菜单栏中点击Publish
按钮部署实例,实例完成后就能在task App
中体验流程了
4.发起流程并使用
进入首页task App
,进入菜单processes
中,点击start a process
发起一个流程
在部署实例列表中选择部署的实例,并选择start process
返回到process列表中就能看到启动的流程列表:
点击show diagram就能看到当前流程进行到什么步骤:
蓝色代表已经走过的部分,绿色代表正在进行的部分
在Active tasks
中点击第一个任务发起事件
点击claim
表示认领该任务,认领后就会填写该环节绑定的请假表单内容:
点击complete
按钮,提交任务后,在返回process菜单show Diagram,发现任务已经自动流转到大毛子审批
中
最后切换账号,登录大毛子,在Task App出现了"大毛子审批"任务:
点击compete,整个流程结束。若是选择不同意,则再次流转到发起事件
总结
本文介绍了如何使用源码导入到idea运行activiti-app,并介绍了切换数据源的方式。 通过activiti-app使用,来加深流程引擎中模型、实例、任务、自定义表单、变量等有了更加感性的认识。后续会增加对决策表的使用、设计器提取汉化、springboot的无缝整合相关笔记