如何使用流程设计器定义流程请参考三、流程设计器。
部署的相关方法包含:deploy、redeploy、undeploy,分别表示流程定义的部署、
重新部署、卸载。部署时统一使用 InputStream 输入流作为流程定义的数据。可借助
org.snaker.engine.helper.StreamHelper 帮助类完成。方法如下定义:
InputStream 的方法名称 描述
getStreamFromString 根据字符串获取输入流
getStreamFromFile 根据文件对象获取输入流
getStreamFromClasspath 根据类路径下的资源文件名称获取输入流
getStreamFromUrl 根据 Url 远程资源获取输入流
engine.process().deploy(StreamHelper.
getStreamFromClasspath(“test/task/simple/process.snaker”));
部署相同的流程定义,会产生版本号依次加1的新的流程定义数据。但是不会对同名的流程
实例产生影响。
engine.process().redeploy(processId, StreamHelper.
getStreamFromClasspath("test/task/simple/process.snaker"));
重新部署流程会影响当前流程实例的执行
engine.process().undeploy(processId);
卸载流程只会更新状态 state 值,不会物理删除数据。
可根据流程定义的 id 或者名称启动流程实例。如果相同的流程名称存在不同的版本,
并使用名称启动实例时,会按照最新的版本来启动,其它低版本运行中的流程实例不会受到
影响,这样就允许流程的多个版本同时运行。
engine.startInstanceById(processId);
engine.startInstanceById(processId, "admin");
engine.startInstanceById(processId, "admin", args);
由 id 启动实例的参数为:流程定义 id、操作人 operator、参数列表 args
engine.startInstanceByName("simple");
engine.startInstanceByName("simple", 0);
engine.startInstanceByName("simple", 0, "admin");
engine.startInstanceByName("simple", 0, "admin", args);
由 name 启动实例的参数为:流程定义 name、版本号 version、操作人 operator、参数列表 args
流程实例的启动会在以下的表中产生数据:
wf_order、wf_hist_order
执行任务的处理逻辑包括两部分:
engine.executeTask(taskId);
engine.executeTask(taskId, "admin");
engine.executeTask(taskId, "admin", args);
执行任务的参数为:任务号 taskId、操作人 operator、参数列表 args
任务转派的业务逻辑是结束当前任务,并创建新的任务给转派人。其调用的 api 为:
engine.task().createNewTask(task.getId(), 0, "test");
engine.task().complete(task.getId());
createNewTask 方法中的第二个参数“任务类型”表示创建主办、协办任务。
根据历史任务 id,撤回由该历史任务派发的所有活动任务,如果无活动任务,则不允许撤
回。抛出 SnakerException 异常
engine.task().withdrawTask(taskId, "admin");
任务提取一般发生在参与者为部门、角色等组的情况下,该组的某位成员提取任务后,其它
成员无法处理任务。
engine.task().take(taskId, "admin");
任务驳回有多种场景,常见的有:驳回上一步、驳回到任意节点
engine.executeAndJumpTask(String taskId, String operator, Map args,
String nodeName)
方法的参数 nodeName 决定驳回的方式:
为空(null 或空字符),则驳回至上一步(不允许驳回至 fork、join、
suprocess 以及会签任务)
非空,则根据 nodeName 确定跳转的目标节点。该实现原理与其它流程引擎思路一致,通过动态创建连接完成跳转。
Snaker 支持两种自由流:
在 engine.executeAndJumpTask 方法已经支持,参考任务驳回
未定义流转节点,即流程定义没有流程模型的情况,由用户随意创建自定义任务(任务
名称、任务参与者、任务关联的表单等)
TaskModel tm1 = new TaskModel();
tm1.setName("task1");
tm1.setDisplayName("任务1");
List tasks = null;
tasks = engine.createFreeTask(orderId, "1", args, tm1); for(Task task : tasks) {
engine.task().complete(task.getId(), "1", null);}
自由任务需要调用 engine.task().complete 方法结束任务。并且需要手动
结束流程实例。
engine.order().complete(order.getId());
自由流程存在强制终止的情况,此时需要调用
void terminate(String orderId);
void terminate(String orderId, String operator);
强制终止实例先结束该实例所有活动的任务,最后结束流程实例。
添加参与者需要判断所属的任务模型的参与类型(ANY、ALL)
ITaskService 提供两个添加参与者的方法:
该方法根据 taskId 对应的任务模型判断是否属于会签任务,如果属于 ALL 的参与类型,则添加的每个 actor 都会产生新的任务。如果属于 ANY 的参与类型,则只是将 actor 添加到当前的任务中
2. addTaskActor(String taskId, Integer performType, String... actors);
该方法根据 performType 类型确定是否产生新的任务
3. removeTaskActor(String taskId, String... actors);
减少参与者仅仅是将所属任务的参与者删除(参考表:wf_task_actor)
编码设置参与者主要用到 AssignmentHandler 接口,并在定义任务模型时,需要配置
该接口的实现类:
public class TaskAssign implements AssignmentHandler {
public Object assign(Execution execution) {
return "admin";
}
}
此处方便测试,直接返回字符串。实际使用时,应该根据执行对象的 args 参数来判断执行
人。
Snaker 支持对任意类型的节点提供前置、后置拦截器处理。
preInterceptors 为前置拦截器属性配置、postInterceptors 为后置拦截器属性
配置。自定义拦截器需要实现接口:SnakerInterceptor。如下代码片段即实现对产生
的任务拦截并输出日志信息:
public class LocalTaskInterceptor implements SnakerInterceptor {
private static final Logger log = LoggerFactory.getLogger(LocalTaskInterceptor.class);
public void intercept(Execution execution) {
if (log.isInfoEnabled()) {
log.info("LocalTaskInterceptor start...");
for (Task task : execution.getTasks()) {
StringBuffer buffer = new StringBuffer(100);
buffer.append("创建任务[标识=").append(task.getId());
buffer.append(",名称=").append(task.getDisplayName());
buffer.append(",创建时间=").append(task.getCreateTime());
buffer.append(",参与者={");
if (task.getActorIds() != null) {
for (String actor : task.getActorIds()) {
buffer.append(actor).append(";");
}
}
buffer.append("}]");
log.info(buffer.toString());
}
log.info("LocalTaskInterceptor finish...");
}
}
}
public class CustomAccessStrategy extends GeneralAccessStrategy {
protected List ensureGroup(String operator) {
List groups = new ArrayList();
if (operator.equals("test")) {
groups.add("test");
} else {
groups.add("role1");
}
return groups;
}
}
继承 GeneralAccessStrategy 类,实现 ensureGroup 方法,根据操作人获取该操作人对
应的组(部门、角色等)
在 snaker.xml 中增加访问策略类的配置:
获取待办任务时,设置的参与者数组增加组的信息即可
String[] actorIds = new String[]{username, groups};
snakerEngine.query().getWorkItems(page,
new QueryFilter().setOperators(actorIds));
执行任务的方式没有改变,依然是调用
snakerEngine.executeTask(taskId, userName, args);
表名称为 wf_surrogate,其字段定义如下:
在类路径下的 snaker.xml 中增加拦截器配置,如下:
由于在 snaker.xml 中配置的拦截器,属于全局任务拦截器,则当产生新的任务时,会执行该
拦截器负责检查委托代理表,是否存在委托授权情况,如存在,则增加代理人的参与权限。
保存或更新委托代理配置
engine.manager().saveOrUpdate(Surrogate surrogate);
删除委托代理配置
engine.manager().deleteSurrogate(String id);
根据id获取委托代理对象
engine.manager().getSurrogate(String id);
根据当前操作人、流程名称获取该操作人的代理人,支持多级代理
engine.manager().getSurrogate(String operator, String processName)
子流程定义,主要是设置 processName(子流程的名称 name 值)属性
1、 API 方式整合
API 整合方式适用于无业务容器托管的场景。
import javax.sql.DataSource;
import org.snaker.engine.SnakerEngine;
import org.snaker.engine.access.jdbc.JdbcHelper;
import org.snaker.engine.cfg.Configuration;
/**
*
* Snaker引擎帮助类
*
*/
public class SnakerHelper {
private static final SnakerEngine engine;
static {
DataSource dataSource = JdbcHelper.getDataSource();
engine = new Configuration()
.initAccessDBObject(dataSource)
.buildSnakerEngine();
}
public static SnakerEngine getEngine() {
return engine;
}
}
考虑到与现有应用整合,故提供 initAccessDBObject 方法,由应用系统提供具体的数据库
访问对象,目前支持(jdbc 方式 DataSource、hibernate 方式 SessionFactory、mybatis 方
式 SqlSessionFactory)。测试用例的基类中没有调用该方法。代码如下:
类路径:org.snaker.engine.test.TestSnakerBase
public class TestSnakerBase {
protected String processId;
protected SnakerEngine engine = getEngine();
protected IProcessService processService = engine.process();
protected IQueryService queryService = engine.query();
private SnakerEngine getEngine() {
return new Configuration().buildSnakerEngine();
}
}
为了方便测试,Snaker 根据所使用的 DBAccess 实现类,从 snaker.properties 中获取初始
化参数,如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/snaker
jdbc.username=root
jdbc.password=root
SnakerHelper.getEngine().process().deploy(……);
Spring 集成 Snaker 时,需要配置流程引擎、事务管理、数据访问方式.具体可参考 snaker-demo。具体配置如下:
使用 hibernate 时,需要在 sessionFactory 配置中增加 snaker 映射文件的 mappingResources
属性:
hbm/snaker.task.hbm.xml
hbm/snaker.order.hbm.xml
hbm/snaker.ccorder.hbm.xml
hbm/snaker.process.hbm.xml
hbm/snaker.taskactor.hbm.xml
hbm/snaker.workitem.hbm.xml
hbm/snaker.surrogate.hbm.xml
使用mybatis,需要在mybatis.cfg.xml配置文件中增加snaker的类型及映射文件
基于 Spring 的项目在使用 Snaker 时,完全将事务交给 Spring 托管,其事务配置如下:
snaker-core-master/dist/plugins/snaker-designer_*.*.*.jar
复制 snaker-designer_..*.jar 到 eclipse 的 plugins 目录下,重新启动
eclipse 即可(经过测试的版本有 eclipse4.2/4.3)
依次选择 File->New->Other->Snaker,如果安装成功,如下图所示:
选择 Snaker Process File 并输入文件名称,如下图所示:
点击 Finish,则打开流程设计器主界面,其中包括两大部分:流程组件、属性
目前节点组件包括:start、end、task、custom、sub-process、decision、
fork、join,分别对应开始、结束、任务、自定义、子流程、决策、分支、合并组件模
型.
对组件模型设置属性,包括常用的 name、displayName 等
组件模型 属性 描述
通用属性 name 组件名称,模型内名称唯一
displayName 组件中文显示名称,方便阅读
preInterceptors 前置拦截器(节点模型)
postInterceptors 后置拦截器(节点模型)
Process instanceUrl 流程定义列表页面直接启动流程实例的 URL
instanceNoClass 流程实例编号生成类
Transition expr 决策选择 Decision 节点的输出变迁表达式
Task form 用户参与的表单任务对应的 URL
assignee 任务参与者变量
assignmentHandler 任务参与者处理类
taskType 任务类型(Main:主办;Aidant:协办)
performType 任务参与类型(针对多个参与者)
ANY 为其中一个参与者完成即往下流转;
ALL 为所有参与者完成才往下流转
expireTime 期望完成时间,设置表达式变量由参数传递
autoExecute 是否自动执行:Y 自动;N 非自动
callback 执行后的回调类
reminderTime 提醒时间
reminderRepeat 提醒次数
Custom clazz 自定义节点的 Java 类路径,两种方式:
1.实现 IHandler 接口
2.无接口实现的普通 java 类,需要设置下面方
法名称、参数属性
methodName 定义需要执行的 java 类的方法名称
args 定义传递的参数表达式
var 定义返回值变量名称
SubProcess processName 子流程名称(对应 process 的 name 属性)
Decision expr 决策选择表达式
handleClass 决策选择的处理类,实现 DecisionHandler 接口
Snaker 流程设计器中的组件模型是通过 xml 的配置加载的。配置文件请参考:
http://git.oschina.net/yuqs/snaker-designer/blob/master/src/model-process.xml
配置内容包括属性、显示图标、节点名称等。
如果需要增加节点模型,只需要参考 component 的属性定义即可。model-
process.xml 文件注释了 Sql 节点的定义,仅供参考。
自定义配置完成后,需要更新配置文件,步骤如下:
选择 Window->Preferences 打开首选项
//获取process服务
IProcessService process();
//获取查询服务
IQueryService query();
//获取实例服务
IOrderService order();
//获取任务服务
ITaskService task();
//获取管理服务
IManagerService manager();
//根据流程定义ID启动流程实例
Order startInstanceById(String id);
//根据流程定义ID,操作人ID启动流程实例
Order startInstanceById(String id, String operator);
//根据流程定义ID,操作人ID,参数列表启动流程实例
Order startInstanceById(String id, String operator, Map args);
//根据流程名称启动流程实例
Order startInstanceByName(String name);
//根据流程名称、版本号启动流程实例
Order startInstanceByName(String name, Integer version);
//根据流程名称、版本号、操作人启动流程实例
Order startInstanceByName(String name, Integer version, String operator);
//根据流程名称、版本号、操作人、参数列表启动流程实例
Order startInstanceByName(String name, Integer version, String operator, Map args);
//根据父执行对象启动子流程实例
Order startInstanceByExecution(Execution execution);
//根据任务主键ID执行任务
List executeTask(String taskId);
//根据任务主键ID,操作人ID执行任务
List executeTask(String taskId, String operator);
//根据任务主键ID,操作人ID,参数列表执行任务
List executeTask(String taskId, String operator, Map args);
//根据任务主键ID,操作人ID,参数列表执行任务,并且根据nodeName跳转到任意节点
List executeAndJumpTask(String taskId, String operator, Map args, String nodeName);
//根据流程实例ID,操作人ID,参数列表按照节点模型model创建新的自由任务 List createFreeTask(String orderId, String operator, Map args, WorkModel model);
//保存流程定义
void saveProcess(Process process);
//根据主键ID获取流程定义对象
Process getProcessById(String id);
//根据流程name获取流程定义对象
Process getProcessByName(String name);
//根据流程name、version获取流程定义对象
Process getProcessByVersion(String name, Integer version);
//根据给定的参数列表args查询process List getProcesss(QueryFilter filter);
//根据给定的参数列表args分页查询process
List getProcesss(Page page, QueryFilter filter);
//根據InputStream輸入流,部署流程定义
String deploy(InputStream input);
//根據InputStream輸入流,部署流程定义
void redeploy(String id, InputStream input);
//卸载指定的流程定义,只更新状态
void undeploy(String id);
/**
* 流程实例正常完成
* @param orderId 流程实例id */
void complete(String orderId);
/**
* 创建抄送实例
* @param orderId 流程实例id
* @param actorIds 参与者id
* @since 1.5
*/
void createCCOrder(String orderId, String... actorIds); /**
* 流程实例强制终止
* @param orderId 流程实例id */
void terminate(String orderId);
/**
* 流程实例强制终止
* @param orderId 流程实例id
* @param operator 处理人员 */
void terminate(String orderId, String operator); /**
* 更新抄送记录为已阅
* @param orderId 流程实例id
* @param actorIds 参与者id */
void updateCCStatus(String orderId, String... actorIds); /**
* 删除抄送记录
* @param orderId 流程实例id
* @param actorId 参与者id */
void deleteCCOrder(String orderId, String actorId);
- ITaskService:
//完成指定的任务,删除活动任务记录,创建历史任务 Task complete(String taskId);
//完成指定的任务,删除活动任务记录,创建历史任务 Task complete(String taskId, String operator);
//根据任务主键ID,操作人ID完成任务
Task complete(String taskId, String operator, Map args);
//根据任务主键ID,操作人ID提取任务
Task take(String taskId, String operator);
//向指定的任务id添加参与者
void addTaskActor(String taskId, String... actors);
//向指定的任务id添加参与者
void addTaskActor(String taskId, Integer performType, String... actors);
//对指定的任务id删除参与者
void removeTaskActor(String taskId, String... actors);
//根据任务主键id、操作人撤回任务
Task withdrawTask(String taskId, String operator); /**
* 根据已有任务id、任务类型、参与者创建新的任务
*/
List createNewTask(String taskId, int taskType, String... actors);
/**
* 根据流程实例ID获取流程实例对象
* @param orderId 流程实例id
* @return Order 流程实例对象
*/
Order getOrder(String orderId);
/**
* 根据流程实例ID获取历史流程实例对象
* @param orderId 历史流程实例id
* @return HistoryOrder 历史流程实例对象 */
HistoryOrder getHistOrder(String orderId); /**
* 根据任务ID获取任务对象
* @param taskId 任务id
* @return Task 任务对象
*/
Task getTask(String taskId);
/**
* 根据任务ID获取历史任务对象
* @param taskId 历史任务id
* @return HistoryTask 历史任务对象 */
HistoryTask getHistTask(String taskId);
/**
* 根据任务ID获取活动任务参与者数组
* @param taskId 任务id
* @return String[] 参与者id数组 */
String[] getTaskActorsByTaskId(String taskId); /**
* 根据任务ID获取历史任务参与者数组
* @param taskId 历史任务id
* @return String[] 历史参与者id数组 */
String[] getHistoryTaskActorsByTaskId(String taskId);
/**
* 根据filter查询活动任务
* @param filter 查询过滤器
* @return List 活动任务集合 */
List getActiveTasks(QueryFilter filter);
/**
* 根据filter分页查询活动任务
* @param page 分页对象
* @param filter 查询过滤器
* @return List 活动任务集合 */
List getActiveTasks(Page page, QueryFilter filter);
/**
* 根据filter查询流程实例列表
* @param filter 查询过滤器
* @return List 活动实例集合 */
List getActiveOrders(QueryFilter filter);
/**
* 根据filter分页查询流程实例列表
* @param page 分页对象
* @param filter 查询过滤器
* @return List 活动实例集合 */
List getActiveOrders(Page page, QueryFilter filter);
/**
* 根据filter查询历史流程实例
* @param filter 查询过滤器
* @return List 历史实例集合 */
List getHistoryOrders(QueryFilter filter);
/**
* 根据filter分页查询历史流程实例
* @param page 分页对象
* @param filter 查询过滤器
* @return List 历史实例集合 */
List getHistoryOrders(Page page, QueryFilter filter);
/**
* 根据filter查询所有已完成的任务
* @param filter 查询过滤器
* @return List 历史任务集合 */
List getHistoryTasks(QueryFilter filter);
/**
* 根据filter分页查询已完成的历史任务
* @param page 分页对象
* @param filter 查询过滤器
* @return List 历史任务集合 */
List getHistoryTasks(Page page,
QueryFilter filter);
/**
* 根据filter分页查询工作项(包含process、order、task三个实体的字段集
合)
* @param page 分页对象
* @param filter 查询过滤器
* @return List 活动工作项集合 */
List getWorkItems(Page page, QueryFilter filter);
/**
* 根据filter分页查询抄送工作项(包含process、order)
* @param page 分页对象
* @param filter 查询过滤器
* @return List 抄送工作项集合
*/
List getCCWorks(Page page, QueryFilter filter);
/**
* 根据filter分页查询已完成的历史任务项
* @param page 分页对象
* @param filter 查询过滤器
* @return List 历史工作项集合 */
List getHistoryWorkItems(Page page, QueryFilter filter);
/**
* 根据类型T、Sql语句、参数查询单个对象
* @param T 类型
* @param sql sql语句
* @param args 参数列表
* @return
*/
public T nativeQueryObject(Class T, String sql, Object... args);
/**
* 根据类型T、Sql语句、参数查询列表对象
* @param T 类型
* @param sql sql语句
* @param args 参数列表
* @return
*/
public List nativeQueryList(Class T, String sql, Object... args);
/**
* 根据类型T、Sql语句、参数分页查询列表对象
* @param page 分页对象
* @param T 类型
* @param sql sql语句
* @param args 参数列表
* @return
*/
public List nativeQueryList(Page page, Class T, String sql, Object... args);