工作场景: 有一项任务需要完成,不同的部门领导根据实际情况,有的交给一个人完成,有的会交给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); }