当前位置: 首页 > 工具软件 > Snaker > 使用案例 >

Snaker的回退流程源码分析

裴宏壮
2023-12-01

节点数据模型

BaseModel
NodeModel
ProcessModel
WorkModel
TaskModel
CustomModel
DecisionModel

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在撤回过程中会记录原活动的方向
 类似资料: