BaseModel是所有节点模型的基类,并实现了序列化。其他所有的模型对象皆继承自BaseModel。
Task是任务实体类,实现了序列化。
Task与TaskModel之间的关系:Task是节点模型的动态处理流程,在Task中会存储相对应的模型对象以及流程处理中的所有信息。
公积金贷款流程图:
撤回流程实现类:
传入指定的taskId(要撤回到的地方),从该节点开始,将所有不为空且未完成的Task存入List<Task> tasks。执行结束后,如果tasks为空,说明后续任务已完成或不存在,无法撤回。否则,删除所有这些活动任务,并初始化一个新task为该撤回节点。
/**
* 撤回指定的任务
*/
public Task withdrawTask(String taskId, String operator) {
HistoryTask hist = access().getHistTask(taskId);
AssertHelper.notNull(hist, "指定的历史任务[id=" + taskId + "]不存在");
List<Task> tasks;
if(hist.isPerformAny()) {
tasks = access().getNextActiveTasks(hist.getId());
} else {
tasks = access().getNextActiveTasks(hist.getOrderId(),
hist.getTaskName(), hist.getParentTaskId());
}
if(tasks == null || tasks.isEmpty()) {
throw new SnakerException("后续活动任务已完成或不存在,无法撤回.");
}
for(Task task : tasks) {
access().deleteTask(task);
}
Task task = hist.undoTask();
task.setId(StringHelper.getPrimaryKey());
task.setCreateTime(DateHelper.getTime());
access().saveTask(task);
assignTask(task.getId(), task.getOperator());
return task;
}
undotask()具体执行撤回任务
/**
* 根据历史任务产生撤回的任务对象
* @return 任务对象
*/
public Task undoTask() {
Task task = new Task();
task.setOrderId(this.getOrderId());;
task.setTaskName(this.getTaskName());
task.setDisplayName(this.getDisplayName());
task.setTaskType(this.getTaskType());
task.setExpireTime(this.getExpireTime());
task.setActionUrl(this.getActionUrl());
task.setParentTaskId(this.getParentTaskId());
task.setVariable(this.getVariable());
task.setPerformType(this.getPerformType());
task.setOperator(this.getOperator());
return task;
}
Snaker作者在源码中分别对撤回和驳回两种流程进行了定义,奇怪的是在SnakerEngineImp中并没有对撤回流程进行任何的调用。猜测,作者觉得撤回流程不够完善,并没有使用
跳转流程参数列表
.
/**
* 根据任务主键ID,操作人ID,参数列表执行任务,并且根据nodeName跳转到任意节点
* 1、nodeName为null时,则跳转至上一步处理
* 2、nodeName不为null时,则任意跳转,即动态创建转移
* @param taskId 任务主键ID
* @param operator 操作人主键ID
* @param args 参数列表
* @param nodeName 跳转的节点名称
* @return List<Task> 任务集合
*/
public List<Task> executeAndJumpTask(String taskId, String operator, Map<String, Object> args, String nodeName);
跳转流程实现类
/**
* 根据任务主键ID,操作人ID,参数列表执行任务,并且根据nodeName跳转到任意节点
* 1、nodeName为null时,则驳回至上一步处理
* 2、nodeName不为null时,则任意跳转,即动态创建转移
*/
public List<Task> executeAndJumpTask(String taskId, String operator, Map<String, Object> args, String nodeName) {
Execution execution = execute(taskId, operator, args);
if(execution == null) return Collections.emptyList();
ProcessModel model = execution.getProcess().getModel();
AssertHelper.notNull(model, "当前任务未找到流程定义模型");
if(StringHelper.isEmpty(nodeName)) {
Task newTask = task().rejectTask(model, execution.getTask());
execution.addTask(newTask);
} else {
NodeModel nodeModel = model.getNode(nodeName);
AssertHelper.notNull(nodeModel, "根据节点名称[" + nodeName + "]无法找到节点模型");
//动态创建转移对象,由转移对象执行execution实例
TransitionModel tm = new TransitionModel();
tm.setTarget(nodeModel);
tm.setEnabled(true);
tm.execute(execution);
}
return execution.getTasks();
}
驳回任务
/**
* 驳回任务
*/
public Task rejectTask(ProcessModel model, Task currentTask) {
String parentTaskId = currentTask.getParentTaskId();
if(StringHelper.isEmpty(parentTaskId) || parentTaskId.equals(START)) {
throw new SnakerException("上一步任务ID为空,无法驳回至上一步处理");
}
NodeModel current = model.getNode(currentTask.getTaskName());
HistoryTask history = access().getHistTask(parentTaskId);
NodeModel parent = model.getNode(history.getTaskName());
if(!NodeModel.canRejected(current, parent)) {
throw new SnakerException("无法驳回至上一步处理,请确认上一步骤并非fork、join、suprocess以及会签任务");
}
Task task = history.undoTask();
task.setId(StringHelper.getPrimaryKey());
task.setCreateTime(DateHelper.getTime());
task.setOperator(history.getOperator());
access().saveTask(task);
assignTask(task.getId(), task.getOperator());
return task;
}
Activiti中的撤回流程:
public void revoke(String objId) throws Exception {
Task task = taskService.createTaskQuery().processInstanceBusinessKey(objId).singleResult();
if(task==null) {
throw new ServiceException("流程未启动或已执行完成,无法撤回");
}
LoginUser loginUser = SessionContext.getLoginUser();
List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
.processInstanceBusinessKey(objId)
.orderByTaskCreateTime()
.asc()
.list();
String myTaskId = null;
HistoricTaskInstance myTask = null;
for(HistoricTaskInstance hti : htiList) {
if(loginUser.getUsername().equals(hti.getAssignee())) {
myTaskId = hti.getId();
myTask = hti;
break;
}
}
if(null==myTaskId) {
throw new ServiceException("该任务非当前用户提交,无法撤回");
}
String processDefinitionId = myTask.getProcessDefinitionId();
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//变量
// Map<String, VariableInstance> variables = runtimeService.getVariableInstances(currentTask.getExecutionId());
String myActivityId = null;
List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
.executionId(myTask.getExecutionId()).finished().list();
for(HistoricActivityInstance hai : haiList) {
if(myTaskId.equals(hai.getTaskId())) {
myActivityId = hai.getActivityId();
break;
}
}
FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
String activityId = execution.getActivityId();
logger.warn("------->> activityId:" + activityId);
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
//记录原活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
//清理活动方向
flowNode.getOutgoingFlows().clear();
//建立新方向
List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(flowNode);
newSequenceFlow.setTargetFlowElement(myFlowNode);
newSequenceFlowList.add(newSequenceFlow);
flowNode.setOutgoingFlows(newSequenceFlowList);
Authentication.setAuthenticatedUserId(loginUser.getUsername());
taskService.addComment(task.getId(), task.getProcessInstanceId(), "撤回");
Map<String,Object> currentVariables = new HashMap<String,Object>();
currentVariables.put("applier", loginUser.getUsername());
//完成任务
taskService.complete(task.getId(),currentVariables);
//恢复原方向
flowNode.setOutgoingFlows(oriSequenceFlows);
}
根据Activiti中的源码我们可以看出,Acticiti在撤回的实现逻辑上与Snaker类似,但更加完善。主要体现在以下几个方面:
Activiti做了用户权限的校验
Activiti在撤回过程中会记录原活动的方向