前言
本手册介绍 CUBA 框架业务流程管理扩展组件。
目标读者
该手册是为基于 CUBA 平台构建应用程序的开发者准备的。假设读者熟悉 开发者手册 。
更多阅读材料
该向导,以及其它 CUBA 平台的文档,可以在 https://www.cuba-platform.com/documentation 访问。
CUBA 业务流程管理扩展组件是基于 Activiti 框架开发的,因此,对于该框架的知识都很有帮助。参阅 http://www.activiti.org 学习更多关于 Activiti 的知识。流程是根据 BPMN 2.0 规范定义的,所以这里假设读者熟悉该标注规范。更多关于 BPMN 2.0 标准可以参阅 http://www.bpmn.org 。
反馈
如果有关于该手册的任何建议或者意见,可以在 GitHub 上提交 issue。如果发现了拼写或者文字错误、bug 或者不一致的地方,可以直接 fork 我们的 repo 并进行修复。感谢!
1. 快速开始
在本章中,将创建一个小项目来演示如何使用业务流程扩展组件,将以合同审批业务流程作为示例。
通常,审批流程涉及以下步骤:
-
用户创建
Contract
对象,定义参与者并发起审批流程。 -
具有
Controller
角色的参与者接收任务并审验附加的合同。 -
如果审验通过,则合同将传递给分配了
Manager
角色的用户,否则该流程将以Not valid
状态终止。 -
在相关管理人员批准或拒绝合同之后,分别以
Approved
或Not approved
状态返回。
1.1. 创建项目
-
按照 CUBA Studio 用户向导 中 创建新项目 部分的描述在 CUBA Studio 中创建一个新项目:
-
项目名称:
bpm-demo
-
项目命名空间:
demo
-
根包:
com.company.bpmdemo
-
-
在 CUBA Studio 中打开 Project Properties 编辑器:在主菜单项中点击 CUBA > Project Properties。在 App components 列表中添加 bpm 应用程序组件。当 Studio 建议重新创建 Gradle 脚本时,点击确认。Studio 会下载需要的源码和二进制 工件 。如果项目没有自动同步 Gradle 文件,可以点击 Gradle 工具窗口的
按钮手动同步。
-
在本地 HyperSQL 服务创建数据库:主菜单选择 CUBA > Create database。默认数据库名与项目空间名称相同。
-
运行应用程序:点击主工具栏中选中
CUBA Application
配置旁边的按钮。CUBA 项目树中的 Runs at… 部分会显示应用程序的链接,可以用来直接从 Studio 中打开应用程序。
用户名和密码都是:
admin
/admin
.运行的应用程序包含两个主菜单项(Administration 和 Help),以及 安全 和管理子系统的功能。
1.2. 创建数据模型
首先创建 Contract
实体类。
-
在 CUBA 项目树的 Data Model 部分点击 New > Entity。会显示 New CUBA Entity 对话框。
-
在 Entity name 字段输入实体类名称 –
Contract
并点击 OK 按钮。会在工作区显示 Entity Designer 界面。 -
使用 Entity Designer 添加属性:
-
number
-String
类型 -
date
-Date
类型 -
state
-String
类型
-
切换到 Instance name editor 然后在 Name pattern attributes 添加 number
属性。
Contract
实体的创建就完成了。
1.3. 创建标准界面
现在我们为合同创建界面。
在 CUBA 项目树的 Data Model 部分,右键点击 Contract
实体,然后在右键菜单中选择 New > Screen 来创建查看和编辑 Contract
实例的标准界面。点击之后,会显示模板浏览界面。
在可用模板列表中选择 Entity browser and editor screens 然后点击 Next。
弹窗中的所有字段都已经填了默认值,不需要修改。点击 Finish。
会在 Generic UI 树部分的 Screens 部分显示界面文件:
-
contract-browse.xml
– 浏览界面描述文件; -
ContractBrowse
– 浏览界面控制器; -
contract-edit.xml
– 编辑界面描述文件; -
ContractEdit
– 编辑界面控制器。
1.4. 创建 ApprovalHelper Bean
按照 CUBA Studio 用户向导 中的 创建托管Bean 部分描述创建 ApprovalHelper
bean。
用下面的代码替换其内容:
package com.company.bpmdemo.core;
import org.springframework.stereotype.Component;
import com.company.bpmdemo.entity.Contract;
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.Transaction;
import javax.inject.Inject;
import java.util.UUID;
@Component(ApprovalHelper.NAME)
public class ApprovalHelper {
public static final String NAME = "demo_ApprovalHelper";
@Inject
private Persistence persistence;
public void updateState(UUID entityId, String state) {
try (Transaction tx = persistence.getTransaction()) {
Contract contract = persistence.getEntityManager().find(Contract.class, entityId);
if (contract != null) {
contract.setState(state);
}
tx.commit();
}
}
}
将在合同审批流程中调用 ApprovalHelper
bean 的 updateState()
方法来设置合同状态。
方法参数:
-
entityId
- 合同实体标识符 -
state
- 合同状态
1.5. 创建数据库并运行应用程序
在主菜单点击 CUBA > Generate Database Scripts 创建数据库表 。然后,会打开 Database Scripts 界面。
点击 Save and close 按钮保存生成的数据库脚本。
如果需要运行数据库更新脚本,点击 Debug 工具窗旁边的 按钮先停止运行的应用程序,然后再点击 CUBA > Update database。
现在看看创建的界面在真实的系统中长什么样。在主工具栏点击 按钮。
在浏览器打开应用程序地址 http://localhost:8080/app 。也可以使用 CUBA 项目树的 Runs At… 部分的链接打开项目。
1.6. 创建流程
在本节中,将创建和部署这个合同审批流程。
1.6.1. 创建流程模型
流程模型的最终版本将如下所示:

我们看看创建模型所需要的步骤。
在应用程序的 web 界面中,打开 BPM > Process Models 界面,然后单击 Create。输入 Contract approval
作为模型名称,然后单击 OK。Model Editor 会在新的浏览器标签页中打开。
Tip
|
在创建或复制流程模型时,在应用程序窗口右下角会显示一个弹窗通知,上面有打开流程模型编辑页面的链接。如果点击 Edit 按钮,会在新的标签页中打开流程模型编辑页面。 |
在模型属性面板中选择 Process roles 属性。会打开 Process roles 编辑窗口。

有两种类型的参与者参与此流程:管理人员和操作人员。创建两个角色: Manager
和 Controller
。

将 Start event 节点从 Start Events 组拖动到工作区。
我们需要在流程开始时显示一个表单来选择流程参与者。选择开始事件节点。在其属性面板中选择 Start form - 将出现一个表单选择窗口。在 Form name 字段中选择 Standard form
。然后添加两个表单参数:
-
procActorsVisible
的值为true
表示表单上将显示用于选择流程参与人员的表格。 -
attachmentsVisible
的值为true
表示表单上将显示用于上传附件的表格。

将 Activities 组的 User task 节点添加到模型中,并命名为 Validation
。

选择此节点并将 controller
值分配给属性面板上的 Process role 属性,这样我们就可以定义任务将其被分配给具有 controller
角色的流程参与者。

接下来,选择 Task outcomes 属性。将打开任务输出(outcome)编辑窗口。输出定义用户接收任务时可能进行的操作。创建两个输出:Valid
和 Not valid
。为两种输出定义 标准表单
。为 Not valid
输出添加表单参数 commentRequired=true
,因为我们想让用户在选择合同无效时添加意见。

根据操作人员的决定,我们必须将合同发送给管理人员批准或以 Not valid
状态完成流程。Gateways 组中 Exclusive gateway 节点用于控制流程。将其添加到工作区,然后添加另外两个元素:名称是 Set 'Not valid' state
的 Script task 和名称是 Approval
的 User task。将 Script task 的流转(flow)命名为 Not valid
,将 User task 的流转(flow)命名为 Valid
。

选择 Not valid
流。从属性面板中展开 Flow outcome 下拉列表。列表中显示了网关之前的任务输出。选择 Not valid
值。
-
Not Valid 流输出

现在,如果选择 Not valid
输出,流程将转向这个流。
应将 Valid
流标记为默认流(如果没有其它流条件为 true )。选择 Valid
流并勾选 Default flow 属性。
Warning
|
对于标记为默认的流,Flow outcome 下拉列表中不能选择任何值。 |
接下来,选择 Exclusive gateway 并打开其 Flow order 属性编辑界面。确保 Not valid
流位于列表中第一位,必要时请更改流顺序。

选中 Set 'Not valid' state
节点。在这个任务中需要将 Contract 实体的 state
属性设置为 Not valid
值。选择节点,将 Script format 属性值设置为 groovy
。单击 Script 属性字段 - 将打开脚本编辑界面。复制并粘贴以下代码:
def em = persistence.getEntityManager()
def contract = em.find(com.company.bpmdemo.entity.Contract.class, entityId)
contract.setState('Not valid')
在脚本中可以使用流程变量和 persistence
以及 metadata
平台对象(请参阅 开发人员手册 )。entityId
变量在流程启动时创建并存储关联实体的标识符。
合同状态改变之后,应结束该流程。我们将 End events 组中的 End event 节点添加到工作区并与 Set 'Not valid' state
节点连接。
返回到 Approval
任务。像我们为第一个任务所做的那样为其定义 manager
流程角色。为了将任务同时分配给多个管理人员,请将其 Multi-instance type 属性设置为 Parallel(并行)
。

创建两种任务输出:Approve
和 Reject
(Task outcomes 属性)。对这两种输出设置 Standard form
表单,并将 Reject
输出的 commentRequired
参数设置为 true
。
审批完成后,应根据审批结果为合同分配 Approved
或 Not approved
状态。在 Approval 任务
之后添加一个 Exclusive gateway 节点。在排他网关之后添加两个 Service task :Set 'Approved' state
和 Set 'Not approved' state
。它们将与我们之前添加的 Script task 实现同样的功能,但是以不同的方式来实现:通过调用 Spring bean 方法。将 Set 'Approved' state
流命名为 Approved
,将 Set 'Not approved' state
流命名为 Not approved
。

选择 Not approved
流节点并在 Flow outcome 列表中选择 Reject
值。现在,如果有一个管理人员执行了 Reject
操作,流程将转到此流。选择 Approved
流节点并选中 Default flow 复选框。这意味着如果没有启动其它流,则将使用此流。
设置 Exclusive gateway 的流顺序,就像我们前一个做的那样。选择 Exclusive gateway 并打开 Flow order 属性编辑界面。应首先处理 Not approved
。

我们返回到 Service task。选择 Set 'Approved' state
节点并为其 Expression 属性设置为以下值:
${demo_ApprovalHelper.updateState(entityId, 'Approved')}
将以下脚本应用于 Set 'Not approved' state
节点。
${demo_ApprovalHelper.updateState(entityId, 'Not approved')}
Activiti 引擎已经与 Spring 框架集成,因此我们可以使用名称访问 Spring 托管 bean。entityId
是一个流程变量,用于存储关联到流程的合同的标识符。它的值在流程开始时设置。
使用 End event 连接两种服务类型的任务并单击保存按钮。到此,模型已准备就绪,现在我们可以继续进行模型部署。

1.6.2. 流程模型部署
模型部署过程包括以下步骤:
-
生成 BPMN 2.0 格式的流程模型 XML。
-
将流程部署到 Activiti 引擎内部数据表中。
-
创建与 Activiti 流程相关的
ProcDefinition
对象。 -
为模型中定义的流程角色创建
ProcRole
对象。
在 Process Models 界面上的列表中选择模型。点击 Deploy 按钮。将显示模型部署窗口。由于是首次部署模型,因此选择 Create new process 选项。也可以选择部署模型到现有流程来应用模型的更改。单击 OK,会创建流程定义。

打开界面 BPM > Process Definitions。打开 Contract approval
项进行编辑。Code 字段值是 contractApproval
。记住这个值 - 我们将在本章后面用它来识别流程定义。

1.7. 使界面适应流程
在本章中,我们将在合同编辑界面上添加一个使用合同审批流程的功能。
1.7.1. 合同编辑界面布局
在 Studio 中打开 contract-edit.xml
界面,并使用以下代码完全替换其内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://editorCaption"
focusComponent="form"
messagesPack="com.company.bpmdemo.web.contract">
<data>
<instance id="contractDc"
class="com.company.bpmdemo.entity.Contract"
view="_local">
<loader id="contractDl"/>
</instance>
<collection id="procAttachmentsDc"
class="com.haulmont.bpm.entity.ProcAttachment"
view="procAttachment-browse">
<loader id="procAttachmentsDl">
<query><![CDATA[select e from bpm$ProcAttachment e
where e.procInstance.entity.entityId = :entityId
order by e.createTs]]>
</query>
</loader>
</collection>
</data>
<dialogMode height="600"
width="800"/>
<layout expand="editActions"
spacing="true">
<form id="form"
dataContainer="contractDc">
<column width="250px">
<textField id="numberField"
property="number"/>
<dateField id="dateField"
property="date"/>
<textField id="stateField"
property="state"/>
</column>
</form>
<groupBox id="procActionsBox"
caption="msg://process"
spacing="true"
width="AUTO"
orientation="vertical">
<fragment id="procActionsFragment"
screen="bpm_ProcActionsFragment"/>
</groupBox>
<groupBox caption="msg://attachments"
height="300px"
spacing="true"
width="700px">
<table id="attachmentsTable"
dataContainer="procAttachmentsDc"
height="100%"
width="100%">
<columns>
<column id="file.name"/>
<column id="author"/>
<column id="type"/>
<column id="comment"
maxTextLength="50"/>
</columns>
</table>
</groupBox>
<hbox id="editActions"
spacing="true">
<button action="windowCommitAndClose"/>
<button action="windowClose"/>
</hbox>
</layout>
</window>
该界面包含用于合同编辑的一些字段、用于显示流程操作的 fragment 以及显示流程附件的表格。
1.7.2. 合同编辑界面控制器
打开 ContractEdit
界面控制器,并使用以下代码替换其内容:
package com.company.bpmdemo.web.contract;
import com.haulmont.bpm.entity.ProcAttachment;
import com.haulmont.bpm.gui.procactionsfragment.ProcActionsFragment;
import com.haulmont.cuba.gui.app.core.file.FileDownloadHelper;
import com.haulmont.cuba.gui.components.Table;
import com.haulmont.cuba.gui.model.CollectionLoader;
import com.haulmont.cuba.gui.model.InstanceContainer;
import com.haulmont.cuba.gui.model.InstanceLoader;
import com.haulmont.cuba.gui.screen.*;
import com.company.bpmdemo.entity.Contract;
import javax.inject.Inject;
@UiController("demo_Contract.edit")
@UiDescriptor("contract-edit.xml")
@EditedEntityContainer("contractDc")
public class ContractEdit extends StandardEditor<Contract> {
private static final String PROCESS_CODE = "contractApproval";
@Inject
private CollectionLoader<ProcAttachment> procAttachmentsDl;
@Inject
private InstanceContainer<Contract> contractDc;
@Inject
protected ProcActionsFragment procActionsFragment;
@Inject
private Table<ProcAttachment> attachmentsTable;
@Inject
private InstanceLoader<Contract> contractDl;
@Subscribe
private void onBeforeShow(BeforeShowEvent event) {
contractDl.load();
procAttachmentsDl.setParameter("entityId",contractDc.getItem().getId());
procAttachmentsDl.load();
procActionsFragment.initializer()
.standard()
.init(PROCESS_CODE, getEditedEntity());
FileDownloadHelper.initGeneratedColumn(attachmentsTable, "file");
}
}
我们详细了解一下控制器代码。
ProcessActionsFragment
是显示用户当前可用的流程操作的 fragment。在初始化时,fragment 通过两个参数搜索相关的 ProcInstance
对象:流程代码和实体实例。如果未找到 ProcInstance
对象,则创建一个新实例,该界面显示开始流程按钮。如果找到相关的流程实例,则 fragment 将搜索当前用户的未完成的流程任务,并显示流程任务的操作按钮。有关详细信息,请参阅 ProcActionsFragment。
在合同编辑界面中,流程操作 fragment 的初始化在 onBeforeShow()
方法中执行。该方法的关键部分是调用 init(PROCESS_CODE, getEditedEntity())
。 PROCESS_CODE
保存了一个流程代码(contractApproval
- 我们在模型部署期间看到了这个值,参阅(流程模型部署)。第二个参数 getEditedEntity()
是当前合同实体实例。
standard()
方法返回的初始化器执行以下操作:
-
初始化标准流程操作断言(predicate),这个断言在流程启动前和流程任务完成之前调用。断言提交实体编辑界面。
-
初始化标准监听器,用于显示 "流程开始" 或 "任务完成" 等通知,并在流程启动或流程任务完成后刷新
procActionsFragment
。
1.8. 使用应用程序
默认情况下,Studio 中启用了热部署机制,因此界面中的所有更改都应该已应用于正在运行的应用程序。如果禁用热部署,请使用 CUBA > Restart Application Server 菜单命令重新启动服务。
1.8.1. 创建用户
要演示该流程,请在 Administration > Users 界面中创建三个用户:
-
用户名:
norman
, 名字:Tommy
, 姓氏:Norman
-
用户名:
roberts
, 名字:Casey
, 姓氏:Roberts
-
用户名:
pierce
, 名字:Walter
, 姓氏:Pierce
1.8.2. 创建合同并启动流程
-
打开 Application > Contracts 界面并创建新合同。填写 Number 和 Date 字段,然后单击 Save。
-
单击 Start process 按钮,应出现启动流程表单。在模型创建过程中,我们为 Start event 节点定义了
Standard form
。其属性为procActorsVisible=true
和attachmentsVisible=true
。这就是为什么现在我们看到带有流程参与者和附件表格的表单。 -
输入意见并添加参与者:操作人员是
norman
、两个管理人员是pierce
和roberts
。 -
使用附件表格中的 Upload 按钮添加附件。
Figure 15. 开始流程表单 -
点击 OK ,现在流程开始。
1.8.3. 操作人员验证阶段
以 norman
登录。
当流程到达 User task 节点时,会创建一个 ProcTask
对象。此对象链接到指定的流程参与者。BPM 扩展组件有一个界面,用于显示用户当前未完成的任务:BPM > Process Tasks。

我们看到用户 norman
有一个未完成的任务:Contract approval
流程的 Validation
任务。选择它并单击 Open entity editor 按钮。将出现合同编辑界面:

当前用户(norman
)有一个未完成的任务(ProcTask),因此 procActionsFragment
显示可用的流程操作。在定义 Validation
UserTask 节点时,我们设置了两个可能的输出:Valid
和 Not valid
。这就是为什么两个按钮被添加到 procActionsFragment
。
点击 Valid 按钮并在打开的窗口中输入意见:

点击 OK。
成功验证后,合同应进入管理人员并行审批环节。
1.8.4. 管理人员审批阶段
以 pierce
用户登录。
打开当前用户的任务列表: BPM > Process tasks。可以看到 Approval
任务。

选择任务,这次单击 Open process instance 按钮。将打开用于处理流程实例的系统界面。

在这个界面显示流程的开始时间、发起人、附件列表、参与者和流程实例任务列表等信息。该界面还允许打开关联实体编辑界面并执行流程操作。
注意 Tasks 表格。上一个任务 Validation
已经完成,并且输出为 Valid
,同时已经为管理员 pierce
和 roberts
创建了两个新的 Approval
任务。
点击 Approve 按钮审批合同。
然后以 roberts
登录。从 Application > Contracts 的列表中打开合同。
用户 roberts
有一个未完成任务,所以 procActionsFragment
显示 Approve 和 Reject 操作。点击 Reject 按钮。

在模型设计器中定义 Reject
输出时,将 commentRequired
表单参数设置为 true
,因此会看到任务完成表单中需要输入意见。输入意见并点击 OK。
其中一位管理人员拒绝了合同,因此应将 Not approved
状态分配给合同。我们检查一下,打开合同。

审批流程以 Not approved
状态完成。
2. 数据模型

Tip
|
名称以 |
-
ProcModel
- 流程模型。模型属性:-
name
- 模型名称。 -
description
- 模型描述。 -
actModelId
- Activiti 引擎的模型 ID,存储在ACT_RE_MODEL
表中。
-
-
ProcDefinition
-流程定义。可以从数据模型获取或者直接从 XML 文件加载。实体属性:-
name
- 流程名称。 -
code
- 流程代码。它可用于从应用程序代码中确定实体实例。 -
actId
- Activiti 的流程的 ID。如需访问 BPMN 模型,则必须(它会读取 extensionElements) -
active
- 定义当前流程定义是否允许新的流程实例启动。 -
procRoles
- 定义流程参与者的对象集合。 -
model
- 模型的引用。
-
-
ProcRole
- 流程角色。在流程部署时会自动创建此类型对象。流程角色定义了流程参与者的类型。ProcRole
定义流程角色类型。实体属性:-
name
- 角色名称。 -
code
- 角色代码。它可以被应用程序用于角色识别。 -
order
- 序号。应用程序可以使用它以适当的顺序显示角色。 -
procDefinition
- 流程定义的引用。
-
-
ProcInstance
- 流程实例。可以关联项目实体启动流程实例,也可以不关联项目实体。例如,合同审批流程实例可以与合同实体相关联。实体属性:-
description
- 流程实例的描述。 -
startDate
- 流程实例开始日期。 -
endDate
- 流程实例结束日期。 -
startedBy
- 启动流程的用户。 -
active
- 标识流程实例是否处理已启动但尚未完成的状态。 -
cancelled
- 标识流程是否已经取消。 -
actProcessInstanceId
- Activiti 的相应ProcessInstance
的标识符。 -
startComment
- 流程启动时的意见。 -
cancelComment
- 流程取消时的意见。 -
entityName
- 链接的实体名称。 -
entityId
- 链接的实体 ID。 -
entityEditorName
- 用于编辑关联实体的界面名称。 -
procTasks
- 流程任务集合。 -
procActors
- 流程参与人员集合。 -
procAttachments
- 流程附件集合。
-
-
ProcActor
- 流程参与者。该实体为流程实例定义一个具有的特定角色的执行者。实体属性:-
user
- 用户的引用。 -
procInstance
- 流程实例的引用。 -
procRole
- 流程角色的引用。 -
order
- 序号。该字段用于为多用户的有序任务定义参与人员的顺序。
-
-
ProcTask
- 流程任务。当流程到达用户任务节点时,将自动创建此类对象。实体属性:-
name
- 任务名称。 -
startDate
- 任务开始日期。 -
claimDate
- 要求处理日期。该字段用于任务没有显式的流程参与者的情况。 -
endDate
- 任务结束日期。 -
outcome
- 任务完成输出。 -
comment
- 任务完成意见。 -
procActor
- 执行人。 -
actTaskId
- Activiti 任务 ID。此字段用于报告 Activiti 引擎任务的完成情况。 -
actExecutionId
- Activiti 执行 ID。该字段用于读/写流程变量。 -
actTaskDefinitionKey
- 在流程 XML 中,它是UserTask
元素的id
属性。用于构建存储任务结果的变量名称([task_id]_result
)。请参阅依赖任务输出的流转。 -
cancelled
- 标识任务是否在以取消的方式完成。 -
candidateUsers
- 群组任务的可能的程参与者列表。 -
procInstance
- 流程实例的引用。
-
-
ProcAttachment
- 流程附件。实体属性:-
file
-FileDescriptor
的引用。 -
type
- 附件类型(ProcAttachmentType
)。 -
comment
- 意见。 -
author
- 附件作者的引用。 -
procInstance
- 流程实例的引用。 -
procTask
- 流程任务的可选引用。
-
-
ProcAttachmentType
- 附件类型。实体属性:-
code
- 附件类型代码。 -
name
- 附件类型名称。
-
3. 功能
BPM 扩展组件使用 Activiti 引擎来执行业务流程。Activiti Explorer 中的模型编辑器被用于使用 BPMN 2.0 对流程建模。除了 Activiti 框架功能之外,BPM 扩展组件还提供了本节所描述的其它功能。对 Activiti 框架的说明超出了本手册的范围,可以通过 https://www.activiti.org/userguide/ 查看详细说明。
3.1. BpmActivitiListener
创建模型时,BpmActivitiListener
事件监听器会自动被添加到流程中。BpmActivitiListener
实现了 ActivitiEventListener
接口(参阅 http://www.activiti.org/userguide/#eventDispatcher ) 。当某些流程事件发生时(例如,用户任务开始、流程取消、任务完成等),监听器负责创建和修改 BPM 实体。这个监听器创建 ProcTasks
对象,并且为 ProcInstance
设置 endDate
值。
3.2. 流程角色
流程角色定义流程参与者类型,例如 “操作员”或“管理员”。要打开流程角色编辑界面,请在模型属性面板中选择 Process roles 属性。有关角色的信息将在模型部署期间写入流程 XML(process
元素的 extensionElements
部分)。
流程角色定义:
<process id="testProcess" name="Test process">
<extensionElements>
<cuba:procRoles>
<cuba:procRole name="Manager" code="manager"/>
<cuba:procRole name="Operator" code="operator"/>
</cuba:procRoles>
</extensionElements>
</process>
3.3. 启动流程表单
要定义将在流程开始时显示的表单,请使用 Start event 节点的 Start form 属性。在流程表单章节查阅更多有关表单的内容。
启用流程表单定义:
<startEvent id="startEvent">
<extensionElements>
<cuba:form name="standardProcForm">
<cuba:param name="procActorsVisible" value="true"></cuba:param>
</cuba:form>
</extensionElements>
</startEvent>
3.4. 用户任务
要定义任务受理人,请在 User Task 节点的 Process role 属性中选择一个流程角色。当流程到达用户任务时,将在所有流程操作员中找到给定角色的流程操作员,并将任务分配给它们。
任务中的流程角色:
<userTask id="managerApproval" name="Manager approval">
<extensionElements>
<cuba:procRole>manager</cuba:procRole>
</extensionElements>
</process>
如果要将任务分配给多个用户,请为 User Task 节点的 Multi-instance type 属性设置 Parallel
或 Sequential
。
还可以在 User Task 节点的 assignee 属性中指定任务受理人。属性值可以是包含一个 CUBA 用户标识符的字符串常量:da8159cc-757f-7f59-6579-7f629ac02f72
,也可以是一个包含用户 id 字符串的变量: ${varialbeName}
,或者一个调用服务的表达式,在服务中返回用户标识: ${someService.getSomeUserId()}
。请注意,procRole 属性必须定义。当流程到达此类用户任务时,将搜索指定用户和流程角色的 ProcActor
实例。如果它不存在,则创建新的 ProcActor
对象。要在模型设计界面中指定受理人,请选择 User Task,单击 Show advanced properties 链接,然后点击进入 Assignments 属性编辑界面。将出现一个新对话框,在这个界面填写 Assignee 属性。
如果不希望将任务立即分配给特定用户,而是显示在该组用户的可用任务列表中,请设置 Claim allowed 属性。然后,其中一名候选人将能够领取该任务。任务候选者在具有流程 Process role 属性指定的角色的参与者范围内定义。
没有明确指定执行者的任务:
<userTask id="managerApproval" name="Manager approval">
<extensionElements>
<cuba:claimAllowed>true</cuba:claimAllowed>
</extensionElements>
</process>
3.5. 任务输出
通常,期望用户对任务做出决定,例如,批准或拒绝合同。流程的下一个路线取决于此决定。User task 节点的 Task outcomes 属性用于定义输出列表。可以分别为每个输出定义选择输出时应显示的名称和表单。应该传递给表单的参数也可以进行定义(参阅流程表单)。
任务结果:
<userTask id="managerApproval" name="Manager approval">
<extensionElements>
<cuba:outcomes>
<cuba:outcome name="approve">
<cuba:form name="standardProcessForm">
<cuba:param name="commentRequired">true</cuba:param>
<cuba:param name="attachmentsVisible">true</cuba:param>
</cuba:form>
</cuba:outcome>
<cuba:outcome name="reject">
<cuba:form name="someOtherProcessForm">
</cuba:form>
</cuba:outcome>
</cuba:outcomes>
</extensionElements>
</process>
3.6. 依赖任务输出的流转
BPMN 2.0 表示法没有为用户任务提供定义多个输出的方法。要使流程在需要的方向上继续,请使用 Exclusive gateway(排它网关)。排它网关的条件中可以使用前一任务的结果。当用户完成任务时,其结果将写入名为 [taskId]_result
的流程变量。变量类型是 ProcTaskResult
。
ProcTaskResult
类的方法:
-
count(String outcomeName): int
- 返回使用给定输出完成任务的用户数。 -
exists(String outcomeName): boolean
- 如果有用户使用给定输出完成任务,则返回true
。
任务完成结果对象用于网关输出流的 Flow condition(流条件) 表达式。
Example

假设 approval
任务被并行分配给多个用户。为任务定义了两个输出:approve
和 reject
。当所有用户完成任务后,该流程将转到排他网关。我们想要实现以下行为:如果有人选择 reject
选项,那么转到 Rejected
流程; 如果每个人都批准了这项任务,则转到 Approved
流程。
- 在流程输出字段中定义条件
-
定义流条件的最简单方法是在流节点的 Flow outcome 属性中选择前一任务输出的名称。如果至少有一个任务使用所选输出完成,则该流将被激活。
- 为流节点定义复杂条件
-
如果需要为输出实现更复杂的条件,可以在 Flow condition 字段中定义它。例如,“超过 5 个用户选择拒绝选项”条件如下所示:
${approval_result.count('reject') > 5}
3.7. 执行脚本
Script task 节点被用于执行脚本。系统会分析 Script 属性值的内容,如果该值是有效的文件路径且该文件存在,则该文件中的脚本被执行,否则 Script 字段值作为脚本执行。
请注意,可以在脚本中使用 persistence
和 metadata
对象。
3.8. 中间件 Bean 方法调用
Service task 节点用于调用服务方法。Activiti 引擎与 Spring 框架集成,因此可以按名称访问中间件 bean。要调用托管 bean 的方法,请将下面的表达式用于 Expression 字段:
${beanName.methodName(processVarName, 'someStringParam')}
可以使用流程变量作为方法参数,包括在流程启动时自动创建的变量(entityId
、 bpmProcInstanceId
等,如流程运行时服务中所述)。
3.9. 通过计时器完成任务
要在一定时间间隔后完成任务,应该:
-
将 Boundary timer event 节点添加到任务节点。
-
绘制从计时器节点到另一个所需节点的流。
-
在计时器节点的 Time duration 属性中定义时间间隔的表达式。例如,
PT15M
是 15 分钟间隔的表达式。 -
将 Cancel activity 属性设置为 true。当计时器被触发时,它将取消当前任务。
-
在 Timer outcome 属性中,定义在计时器完成任务时应使用的任务输出。

定义计时器的输出:
<boundaryEvent id="managerApprovalTimer" cancelActivity="true" attachedToRef="managerApproval">
<extensionElements>
<cuba:outcome>approve</cuba:outcome>
</extensionElements>
</boundaryEvent>
Tip
|
默认情况下,处理计时器的 Job 执行器是禁用状态。要启用它,请设置应用程序属性 |
3.10. 本地化
流程可能包含用于在用户界面中显示任务或输出的本地化消息。
要打开本地化消息编辑界面,请在模型属性面板中选择 Localization 属性。
要本地化任务名称,请创建一个以任务 ID 作为键的记录。
要本地化任务输出名称,请使用类似 TASK_ID.OUTCOME_NAME
的表达式作为键创建记录。
要本地化流程角色名称,请使用角色代码作为键创建记录。
本地化消息:
<process id="testProcess" name="Test process">
<extensionElements>
<cuba:localizations>
<cuba:localization lang="en">
<cuba:msg key="key1" value="value1"/>
<cuba:msg key="key2" value="value2"/>
</cuba:localization>
<cuba:localization lang="ru">
<cuba:msg key="key1" value="value1"/>
<cuba:msg key="key2" value="value2"/>
</cuba:localization>
</cuba:localizations>
</extensionElements>
</process>
3.12. 模型设计器中的自定义元素
BPM 扩展组件允许为流程模型设计器创建自定义元素。基本上,自定义元素就是 ServiceTask
,它使开发人员不再需要为方法调用输入很长的表达式,例如 ${app_MyBean.someMethod(argument1, 'argument2')}
。下面是自定义元素创建的示例。
假设有一个名为 app_DiscountManager
的中间件 bean。bean 中有一个 makeDiscount(BigDecimal discountPercent, UUID entityId)
方法。该方法通过减去折扣来更新合同金额。
在此示例中,我们会创建一个将调用该方法的自定义模型元素。折扣百分比将作为模型元素的参数被定义。
使用菜单项 BPM > Model Elements Editor 打开模型元素编辑界面。
单击 Add group 按钮并输入组名称 - Discounts。

选择创建的组,然后单击 Add element 按钮。

为元素属性输入以下值:
-
标题: Contract discount
-
模型 ID: contractDiscount
-
图标:单击 Upload 按钮并选择图标文件(可选)
-
Bean 名称:选择 app_DiscountManager
-
方法名称:选择 makeDiscount
Warning
|
Bean name 仅查找实现了接口的 bean。Method name 仅查找实现的接口方法。 |
Method arguments 表格包含方法参数。可以更改参数显示名称和参数默认值。
单击 Save 按钮保存设置的元素。
打开流程模型编辑界面(BPM > Process Models)。元素列表中有 Discounts 组和 Contract discount 元素。将新元素拖放到模型中并选中它。将看到折扣百分比字段和存储实体标识符的流程变量名称显示在界面上。

Tip
|
|
在流程部署期间,自定义元素将被转换为 serviceTask
:
<serviceTask id="sid-5C184F22-6071-45CD-AEA9-1792512BBDCE" name="Make discount" activiti:expression="${app_DiscountManager.makeDiscount(10,entityId)}"></serviceTask>
元素可以被导出到 ZIP 存档,然后从存档中还原。这有助于在开发人员的计算机上创建新元素然后导入到生产服务器。使用元素编辑界面上的相应按钮执行导入和导出。
Reset 按钮删除所有自定义组和元素,并将元素的设置恢复为初始状态。
4. 主要服务
本节仅包含服务的一般描述。Java 类文档中提供了详细的服务方法描述。
4.1. 流程仓库服务
它旨在用于流程定义。该服务用于:
-
从 XML 加载流程定义;
-
撤销 Activiti 引擎中的流程;
-
将 JSON 模型转换为 BPMN XML。
要访问中间件的服务功能,请使用 ProcessRepositoryManager
bean。
5. UI 组件
本节包含关于 BPM 扩展组件提供的用户界面组件的介绍。
5.1. ProcActionsFragment
流程操作 Fragment
ProcActionsFragment
用于处理流程操作。fragment 被初始化之后,以下组件会自动显示:
-
启动流程按钮,在流程尚未启动的情况下显示
-
处理任务按钮,在流程已启动且当前用户具有活动任务的情况下显示
-
取消流程按钮
-
任务信息面板(显示任务的名称和创建日期等信息)
可以将断言分配给每个流程操作以检查操作是否可以被执行(例如,断言提交编辑器,如果提交失败,则不执行流程操作)。还可以定义 post-action 监听器(例如,监听器将关闭编辑器并显示一条通知)。
ProcActionsFragment
必须与 ProcInstance
关联。在 fragment 初始化期间进行关联。
fragment 初始化的一个示例:
procActionsFragment.initializer()
.setBeforeStartProcessPredicate(() -> {
getScreenData().getDataContext().commit();
return true;
})
.setAfterStartProcessListener(() -> {
notifications.create()
.withCaption(messageBundle.getMessage("processStarted"))
.withType(Notifications.NotificationType.HUMANIZED)
.show();
})
.init(PROCESS_CODE, getEditedEntity());
-
initializer()
方法返回一个用于初始化 fragment 的对象。 -
setBeforeStartProcessPredicate
方法设置将在流程启动之前被执行的断言。如果断言返回false
,则流程启动会中断。 -
setAfterStartProcessListener
方法定义一个在流程启动操作被执行后将被调用的监听器。 -
init
方法有两个参数:流程代码和实体实例。此方法被调用时,将搜索ProcInstance
对象,这个对象与实体实例关联,并且具有对给定代码的ProcDefinition
的引用。如果这个ProcInstance
对象存在,则将 fragment 关联到它,否则创建一个新的ProcInstance
对象。
初始化 ProcActionsFragment
的最简单方法是使用 standard() 初始化程序:
procActionsFragment.initializer()
.standard()
.init(PROCESS_CODE, getEditedEntity());
标准初始化器执行以下操作:
-
创建在启动流程之前提交实体编辑器和完成任务操作的断言
-
创建显示 “流程启动” 或 “任务完成” 等通知并且刷新
ProcActionsFragment
的监听器。
以下是用于自定义 fragment 的方法列表。
- 流程生命周期
-
-
initializer()
- 返回一个 fragment 初始化器的新实例。
-
init()
- 尝试通过指定的流程代码和实体引用来查找流程实例。如果未找到流程实例,则会创建一个新流程实例。然后适用于当前用户和流程实例流程操作 UI 被初始化。
-
- 流程配置
-
-
setStartProcessEnabled()
- 定义是否可以启动该流程。
-
setCancelProcessEnabled()
- 定义是否可以取消该流程。
-
setCompleteTaskEnabled()
- 定义任务是否可以完成。
-
setClaimTaskEnabled()
- 定义是否可以自己将任务分配给用户。
-
setTaskInfoEnabled()
- 定义是否启用具有本地化任务名称及其开始日期的布局。
-
setButtonWidth()
- 设置操作控制按钮的宽度。默认值为 150px。
-
addActionButton()
- 允许在自动生成的按钮旁边给 fragment 添加自定义按钮。
-
- 断言
-
-
setBeforeStartProcessPredicate()
- 设置在流程启动之前将被执行的断言。如果断言返回false
,则流程启动将被中断。
-
setBeforeCompleteTaskPredicate()
- 设置在任务完成之前将被执行的断言。如果断言返回false
,则任务完成将被中断。
-
setBeforeClaimTaskPredicate()
- 设置在向用户分配任务之前将被执行的断言。如果断言返回false
,则任务分配将被中断。
-
setBeforeCancelProcessPredicate()
- 设置在任务取消之前将被执行的断言。如果断言返回false
,则该任务不会被取消。
-
- 流程和任务监听器
-
-
setAfterStartProcessListener()
- 定义将在执行流程启动操作后将被调用的监听器。
-
setAfterCompleteTaskListener()
- 定义将在执行任务完成操作后将被调用的监听器。
-
setAfterClaimTaskListener()
- 定义将在执行任务分配操作后将被调用的监听器。
-
setAfterCancelProcessListener()
- 定义将在执行流程取消操作后将被调用的监听器。
-
- 变量和参数提供者
-
-
setStartProcessActionProcessVariablesSupplier()
- 设置流程变量提供者。流程变量提供者返回流程变量字典,必须在流程启动时将其添加到 Activiti 流程实例。
-
setCompleteTaskActionProcessVariablesSupplier()
- 设置流程变量提供者。流程变量提供者返回流程变量字典,必须在任务完成时将其添加到 Activiti 流程实例。
-
setStartProcessActionScreenParametersSupplier()
- 设置流程表单界面参数提供者。这些界面参数提供者返回一个界面参数字典,该字典将传递给通过StartProcessAction
显示的流程表单。
-
setCompleteTaskActionScreenParametersSupplier()
- 设置流程表单界面参数提供者。这些界面参数提供者返回一个界面参数字典,该字典将传递给通过CompleteTaskAction
显示的流程表单。
-
- ProcActionsFragment API
- 初始化程序 API
-
init - setAfterCancelProcessListener - setAfterClaimTaskListener - setAfterCompleteTaskListener - setAfterStartProcessListener - setBeforeCancelProcessPredicate - setBeforeClaimTaskPredicate - setBeforeCompleteTaskPredicate - setBeforeStartProcessPredicate - setButtonWidth - setCancelProcessEnabled - setClaimTaskEnabled - setCompleteTaskActionProcessVariablesSupplier - setCompleteTaskActionScreenParametersSupplier - setCompleteTaskEnabled - setStartProcessActionProcessVariablesSupplier - setStartProcessActionScreenParametersSupplier - setStartProcessEnabled - setTaskInfoEnabled
5.2. 流程表单
- 流程表单接口
-
在模型编辑界面中声明用户任务输出或启动事件节点时,可以设置将显示给用户的表单。表单类应该实现
ProcForm
接口。ProcForm
接口的方法:-
getComment(): String
- 如果表单在流程启动时显示,则返回要被写入ProcTask
对象comment
字段或procInstance
对象的startComment
字段的值。
-
getFormResult(): Map<String, Object>
- 返回表单提交后将被添加到流程变量的对象列表。
-
- 用于流程模型设计器的表单列表
-
根据
bpm.formsConfig
应用程序属性中定义的配置文件构建在流程模型设计器中可用的表单列表。要添加新的流程表单,请执行以下操作:-
创建并注册表单的界面。界面控制器必须实现
ProcForm
接口。 -
创建一个 XML 文件,例如
app-bpm-forms.xml
,它将包含自定义表单的描述,并将其放在 web 或 gui 模块的src
目录下。例如:<?xml version="1.0" encoding="UTF-8"?> <forms xmlns="http://schemas.haulmont.com/cuba/bpm-forms.xsd"> <form name="myCustomForm" default="true"> <param name="someParam" value="hello"/> <param name="otherParam"/> </form> </forms>
这里的
myCustomForm
是界面 id。上述配置还描述了可用的表单参数及其名称和默认值。
具有
default="true"
属性的表单将在模型中被用作默认表单。 -
覆盖
web-app.properties
文件中的bpm.formsConfig
属性。bpm.formsConfig = bpm-forms.xml app-bpm-forms.xml
-
- API
6. 示例
6.1. 任务执行示例
此示例演示以下内容:
-
如何使用
ProcActionsFragment
以编程方式在流程开始时创建流程参与者 -
如何使用
ProcActionsFragment
将流程变量传递给流程实例 -
如何获取和修改由
ProcActionsFragment
创建的标准流程操作(例如,更改“启动流程”按钮标题) -
如何在没有
ProcActionsFragment
的情况下以编程方式启动流程 -
每次流程进行一步时如何使用
ActivitiEventListener
自动更新processState
字段
此示例使用 Task execution - 1 流程模型:

在此示例中,我们不使用 StandardProcForm
来分配流程参与者。我们通过 ProcActionsFragment
的 流程开始前断言 来完成。请参阅 setBeforeStartProcessPredicate()
方法的使用。
@UiController("bpmsamples$Task.edit")
@UiDescriptor("task-edit.xml")
@EditedEntityContainer("taskDc")
@LoadDataBeforeShow
public class TaskEdit extends StandardEditor<Task> {
public static final String PROCESS_CODE = "taskExecution-1";
@Inject
protected ProcActionsFragment procActionsFragment;
@Inject
protected BpmEntitiesService bpmEntitiesService;
@Inject
protected ProcessRuntimeService processRuntimeService;
@Inject
private MessageBundle messageBundle;
@Inject
private Notifications notifications;
@Inject
private Messages messages;
@Inject
private InstanceLoader<Task> taskDl;
...
/** * Method starts the process without {@link ProcActionsFragment} */
@Subscribe("startProcessProgrammaticallyBtn")
private void onStartProcessProgrammaticallyBtnClick(Button.ClickEvent event) {
commitChanges()
.then(() -> {
/*The ProcInstanceDetails object is used for describing a ProcInstance to be created with its proc actors*/
BpmEntitiesService.ProcInstanceDetails procInstanceDetails = new BpmEntitiesService.ProcInstanceDetails(PROCESS_CODE)
.addProcActor("initiator", getEditedEntity().getInitiator())
.addProcActor("executor", getEditedEntity().getExecutor())
.setEntity(getEditedEntity());
/*The created ProcInstance will have two proc actors. None of the entities is persisted yet.*/
ProcInstance procInstance = bpmEntitiesService.createProcInstance(procInstanceDetails);
/*A map with process variables that must be passed to the Activiti process instance when it is started. This variable is used in the model to make a decision for one of gateways.*/
HashMap<String, Object> processVariables = new HashMap<>();
processVariables.put("acceptanceRequired", getEditedEntity().getAcceptanceRequired());
/*Starts the process. The "startProcess" method automatically persists the passed procInstance with its actors*/
processRuntimeService.startProcess(procInstance, "Process started programmatically", processVariables);
notifications.create()
.withCaption(messageBundle.getMessage("processStarted"))
.withType(Notifications.NotificationType.HUMANIZED)
.show();
/*refresh the procActionsFragment to display complete tasks buttons (if a process task appears for the current user after the process is started)*/
initProcActionsFragment();
});
}
private void initProcActionsFragment() {
procActionsFragment.initializer()
.standard()
.setBeforeStartProcessPredicate(() -> {
/*the predicate creates process actors and sets them to the process instance created by the ProcActionsFragment*/
if (commitChanges().getStatus() == OperationResult.Status.SUCCESS) {
ProcInstance procInstance = procActionsFragment.getProcInstance();
ProcActor initiatorProcActor = createProcActor("initiator", procInstance, getEditedEntity().getInitiator());
ProcActor executorProcActor = createProcActor("executor", procInstance, getEditedEntity().getExecutor());
Set<ProcActor> procActors = new HashSet<>();
procActors.add(initiatorProcActor);
procActors.add(executorProcActor);
procInstance.setProcActors(procActors);
return true;
}
return false;
})
.setStartProcessActionProcessVariablesSupplier(() -> {
/*the supplier returns a map with process variables that will be used by the Activiti process*/
Map<String, Object> processVariables = new HashMap<>();
processVariables.put("acceptanceRequired", getEditedEntity().getAcceptanceRequired());
return processVariables;
})
.setAfterStartProcessListener(() -> {
/*custom listener in addition to the standard behavior refreshes the "taskDs", because the process automatically updates the "processState" field of the "Task" entity.*/
notifications.create()
.withCaption(messages.getMessage(ProcActionsFragment.class,"processStarted"))
.withType(Notifications.NotificationType.HUMANIZED)
.show();
initProcActionsFragment();
taskDl.setEntityId(getEditedEntity().getId());
taskDl.load();
})
.setAfterCompleteTaskListener(() -> {
notifications.create()
.withCaption(messages.getMessage(ProcActionsFragment.class,"taskCompleted"))
.withType(Notifications.NotificationType.HUMANIZED)
.show();
initProcActionsFragment();
taskDl.setEntityId(getEditedEntity().getId());
taskDl.load();
})
.init(PROCESS_CODE, getEditedEntity());
}
/** * Method demonstrates how to get and modify process actions automatically created by the ProcActionsFragment */
private void changeStartProcessBtnCaption() {
StartProcessAction startProcessAction = procActionsFragment.getStartProcessAction();
if (startProcessAction != null) {
startProcessAction.setCaption("Start process using ProcActionsFragment");
}
}
}
请参阅 TaskEdit.java
中的 setStartProcessActionProcessVariablesSupplier()
方法的用法,作为如何在流程启动时使用 ProcActionsFragment
传递流程变量的示例。其中一个流程网关中使用 acceptanceRequired
变量决定是否必须由发起者接受任务,或者流程必须完成。
changeStartProcessBtnCaption()
演示了如何获取和修改 ProcActionsFragment
生成的流程操作。在此方法中,标准按钮标题“启动流程”将由自定义标题替换。
onStartProcessProgrammaticallyBtnClick()
方法演示了如何在没有 ProcActionsFragment
的情况下启动新的流程实例。
UpdateProcessStateListener.java
是 org.activiti.engine.delegate.event.ActivitiEventListener
的一个实现。此监听器作为流程级别监听器被注册。它执行以下操作:每次到达新的流程步骤时,相关的 com.company.bpmsamples.entity.Task
实体的 processState
字段将使用当前流程步骤名称进行更新。
/** * The listener updates the "processState" field of the {@link HasProcessState} with the name of current BPM process * node. This listener is used in the "taskExecution-1" BPM process */
public class UpdateProcessStateListener implements ActivitiEventListener {
private static final Logger log = LoggerFactory.getLogger(UpdateProcessStateListener.class);
private Metadata metadata;
public UpdateProcessStateListener() {
metadata = AppBeans.get(Metadata.class);
}
@Override
public void onEvent(ActivitiEvent event) {
RuntimeService runtimeService = event.getEngineServices().getRuntimeService();
String executionId = event.getExecutionId();
UUID entityId = (UUID) runtimeService.getVariable(executionId, "entityId");
String entityName = (String) runtimeService.getVariable(executionId, "entityName");
if (entityId == null) {
log.error("Cannot update process state. entityId variable is null");
return;
}
if (Strings.isNullOrEmpty(entityName)) {
log.error("Cannot update process state. entityName variable is null");
return;
}
MetaClass metaClass = metadata.getClass(entityName);
if (metaClass == null) {
log.error("Cannot update process state. MetaClass {} not found", entityName);
return;
}
if (!HasProcessState.class.isAssignableFrom(metaClass.getJavaClass())) {
log.error("{} doesn't implement the HasProcessState");
return;
}
switch (event.getType()) {
case ACTIVITY_STARTED:
//activityName is the name of the current element taken from the process model
String activityName = ((ActivitiActivityEvent) event).getActivityName();
if (!Strings.isNullOrEmpty(activityName)) {
updateProcessState(metaClass, entityId, activityName);
}
break;
}
}
/** * Method updates the process state of the entity linked with the process instance */
private void updateProcessState(MetaClass metaClass, UUID entityId, String processState) {
Persistence persistence = AppBeans.get(Persistence.class);
try (Transaction tx = persistence.getTransaction()) {
EntityManager em = persistence.getEntityManager();
Entity entity = em.find(metaClass.getJavaClass(), entityId);
if (entity != null) {
((HasProcessState) entity).setProcessState(processState);
} else {
log.error("Entity {} with id {} not found", metaClass.getName(), entityId);
}
tx.commit();
}
}
@Override
public boolean isFailOnException() {
return false;
}
}
这是流程模型中流程级事件监听器配置界面。

要打开此窗口,请单击建模器中的某个位置,单击 Show advanced properties 链接,然后配置 Event listeners 属性。