工作场景: 有一项任务需要完成,不同的部门领导根据实际情况,有的交给一个人完成,有的会交给2、3甚至更多的人完成,这个时候,流程就是由程序进行设置了,而无法在画流程图的时候写死了。
找到了一些资料:
如:
http://yy629.iteye.com/blog/660701 --- 比较全面
http://phoenix-clt.iteye.com/blog/428242
做了一下,可以通过设置subtask来创建会签流程,有问题正在解决:
1. 如果会签中的一员已经signal了,而其他人还没有完成,怎么把这个subtask complete掉,否则只要其他人没有结束,这个人就是signal了,还是看到这个任务;
在signal中,把ActivityExecution转成ExecutionImpl,调用其getExecution().end即可,这样这个人的会签就结束了;
2. 在例子中,加入的subtask,都需要在HistoryTaskImpl中进行addsubtask,现在一到这里就会发生Exception;
3. 还没有做回退的功能
4.
http://yy629.iteye.com/blog/660701 --- 比较全面
一些说明:
创建流程
在execute方法中,创建subTask
1. 首先创建一个task,将这个task存盘(通过DbSession)
//创建会签任务
// DBSession: DbSession dbsession = EnvironmentImpl.getFromCurrent(DbSession.class);
// execution: execute方法的参数
private TaskImpl createCounterSignTask(DbSession dbsession, ExecutionImpl execution) {
// 创建一个新Task
TaskImpl task = dbsession.createTask();
task.setName(execution.getActivityName() + "_会签");
// 设置了type, 便于以后做流程展示时区别 -- 暂时没有清楚作用
execution.getActivity().setType(COUNTER_SIGN_TASK_TYPE);
task.setExecution(execution);
task.setProcessInstance(execution.getProcessInstance());
task.setSignalling(false); // false, 自己手工处理execution的跳转
task.setFormResourceName(form);
task.setDescription(description);
// 存盘,非常重要,获取到task的id
dbsession.save(task);
// 这个应该和历史事件等相关,目前还不清楚
HistoryEvent.fire(new TaskActivityStart(task), execution);
return task;
}
2. 创建这个task的subTask,根据人数,每个人一个subtask
// 创建会签子任务 这里才是真正的会签任务..
// 函数参数中的execution:是execute方法中的execution
private void newCounterSignSubTask(DbSession dbsession, ExecutionImpl execution,
TaskImpl task, String user, HistoryTaskImpl ht) {
// 开始创建sub task
TaskImpl subtask = task.createSubTask();
// 设置一些属性
subtask.setName(execution.getActivityName() + "$" + user);
subtask.setAssignee(user);
subtask.setSignalling(false); // false, 自己手工处理execution的跳转
// 这里用的都是父execution,自己写的时候曾经搞错过,错误查都查不出来
ExecutionImpl exec = execution.createExecution(); // 创建子Execution
ActivityImpl activity = execution.getActivity();
activity.setType(COUNTER_SIGN_SUB_TASK_TYPE); // 设置类型, 便于区别
exec.setActivity(activity);
exec.setState(Execution.STATE_ACTIVE_CONCURRENT); // 再设置原来的type
exec.setHistoryActivityStart(Clock.getCurrentTime());
// sub task对应的是 子execution
subtask.setExecution(exec);
subtask.setFormResourceName(form);
subtask.setDescription(description);
// 也是需要持久化的,存盘
dbsession.save(subtask);
// 下面的这些暂未看,以后再研究
// 修改了jbpm4中的源码, 主要是用来计算任务的url的, 把其中的占位符替:{TASK_ID}换为该task的task id
TaskActivity.replaceTaskResourceFormTaskIdMarco(subtask);
HistoryEvent.fire(new TaskActivityStart(subtask), exec);
ht.addSubTask(dbsession.get(HistoryTaskImpl.class, subtask.getDbid()));
}
Signal的实现
1. signalName,作为了审核答复
2. 把task作为map中的一个参数,传给了Signal方法(在signal方法中,通过execution.getTask获取的task,是个null )
3.
4. 将所有的会签结果,放在ProcessInstance的variable中,这样在整个流程中可见
通过传给signal方法的subtask,获取到父Task,通过这个父task,可以获取到其他所有的subtask,把这些task、execution停止;
List<Task> subTaskList = taskService.getSubTasks(task.getId()); // 这个task是父task
if (subTaskList != null && subTaskList.size() > 1) {
// 处理未完成的会签子任务
String assignee = theSubTask.getAssignee();
for (Task subTask : subTaskList) {
if (!assignee.equals(subTask.getAssignee())) {
TaskImpl subTaskImpl = (TaskImpl) subTask;
subTaskImpl.setSuperTask(null);
taskService.completeTask(subTaskImpl.getId(), COUNTER_SIGN_SUB_TASK_CANEL_STATE);
subTaskImpl.getExecution().end(COUNTER_SIGN_SUB_EXECUTION_END_STATE); // 结束execution
}
}
}
下面是处理转向的流程
// 定义一个转向
Transition transition = null;
// 获得当前的activity
Activity activity = executionImpl.getActivity();
// 找到这个转向
if ((outcome == null)
|| outcome.length() == 0
|| ((Task.STATE_COMPLETED.equals(outcome) || TaskConstants.NO_TASK_OUTCOME_SPECIFIED.equals(outcome))
&& (activity.getOutgoingTransitions() != null) && (activity.getOutgoingTransitions().size() == 1))) {
transition = activity.getOutgoingTransitions().get(0);
} else {
transition = activity.findOutgoingTransition(outcome);
}
String transitionName = transition == null ? null : transition.getName();
variables.put(transtionName, transitionName);
// 完成父任务,进行转向
taskService.completeTask(task.getId(), transitionName, variables);
if (transition != null) {
executionImpl.take(transition);
}