activity任意节点加签数据持久化

前言

在之前的章节中,我们聊到了activity可以解决动态的任意加签节点的问题,但那时候我们的加签节点是临时存储的,而在现实的业务中,往往需要通过创建自身的业务表与activity的表建立一定的业务关系,从而实现数据持久化,即我们加签的节点不能因为加签完毕后就找不到历史数据了

activity和mybatis实现了较好了的整合,如果采用spring的xml方式进行配置的话,我们只需要导入mybatis的依赖并进行简单的配置即可

下面我们先通过一个简单的例子,体验一下如何将自建的业务表数据存储进去

1、建表语句

CREATE TABLE `act_creation` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `PROCESS_DEFINITION_ID` varchar(255) DEFAULT NULL COMMENT '流程定义id',
  `DOUSERID` varchar(20) DEFAULT NULL COMMENT '操作人id',
  `ACT_ID` varchar(64) DEFAULT NULL,
  `PROCESS_INSTANCE_ID` varchar(255) NOT NULL DEFAULT '0' COMMENT '流程实例id',
  `PROPERTIES_TEXT` varchar(2000) DEFAULT NULL COMMENT '参数',
  `STATE_` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0代表有效 1代表无效',
  `create_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='临时创建活动节点';

我们将该表创建在和activity本身的表在一个库中,该表的作用表示用户自定义加签的节点属性信息,

对应的实体类:

public class ActCreation {

	private String id;
	/**
	 * 流程定义ID
	 */
	private String processDefinitionId;

	/**
	 * 对比assignee字段
	 */
	private String doUserId;

	/**
	 * 流程节点ID
	 */
	private String actId;

	/**
	 * 流程实例ID
	 */
	private String processInstanceId;
	private String processText;

	/**
	 * 状态
	 */
	private Integer state;
	
	public String getProcessText() {
		return processText;
	}
	public void setProcessText(String processText) {
		this.processText = processText;
	}
	public Integer getState() {
		return state;
	}
	public void setState(Integer state) {
		this.state = state;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getProcessDefinitionId() {
		return processDefinitionId;
	}
	public void setProcessDefinitionId(String processDefinitionId) {
		this.processDefinitionId = processDefinitionId;
	}
	public String getDoUserId() {
		return doUserId;
	}
	public void setDoUserId(String doUserId) {
		this.doUserId = doUserId;
	}
	public String getActId() {
		return actId;
	}
	public void setActId(String actId) {
		this.actId = actId;
	}
	public String getProcessInstanceId() {
		return processInstanceId;
	}
	public void setProcessInstanceId(String processInstanceId) {
		this.processInstanceId = processInstanceId;
	}

	@Override
	public String toString() {
		return "ActCreation{" +
				"id='" + id + '\'' +
				", processDefinitionId='" + processDefinitionId + '\'' +
				", doUserId='" + doUserId + '\'' +
				", actId='" + actId + '\'' +
				", processInstanceId='" + processInstanceId + '\'' +
				", processText='" + processText + '\'' +
				", state=" + state +
				'}';
	}
}

2、创建接口

该接口有3个方法,和数据库映射与交互的原理就不再过多讲述了

public interface CreationMapper {

	@Select("select * from act_creation where STATE_=0 ")
	@Results({
		@Result(property="id",column="ID"),
		@Result(property="processDefinitionId",column="PROCESS_DEFINITION_ID"),
		@Result(property="doUserId",column="DOUSERID"),
		@Result(property="actId",column="ACT_ID"),
		@Result(property="processInstanceId",column="PROCESS_INSTANCE_ID"),
		@Result(property="processText",column="PROPERTIES_TEXT"),
		@Result(property="state",column="STATE_"),
	})
	public List find(String processInatanceId);
	
	
	@Insert("insert into act_creation(PROCESS_DEFINITION_ID, PROCESS_INSTANCE_ID,PROPERTIES_TEXT,create_time) values(#{processDefinitionId}, #{processInstanceId}, #{processText},now())")
	public void insert(ActCreation actCreation);

	@Update("update act_creation set STATE_ = 1 where STATE_=0 and  PROCESS_INSTANCE_ID=#{processInstanceId}")
	void updateState(String processInstanceId);
}

3、将该接口配置到activiti.cfg.xml中

事实上,在真正的项目整合中,我们没有必要这样做的,那只需要全局配置即可

	
            
                com.congge.addpersistant.CreationMapper
            
        

4、测试

public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine
                .getProcessEngineConfiguration();
        SqlSessionFactory sqlSessionFactory = processEngineConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List selectList = sqlSession.selectList("com.congge.addpersistant.CreationMapper.find",
                null);
        System.out.println(selectList);
    }

假如在表中我们初始化了一条数据,运行上面的代码,可以看到获取到了数据
在这里插入图片描述

知道了如何通过整合mybatis完成自定义加签节点的数据持久化,下面我们开始思考如何开始编写代码呢?

考虑如下的流程图:

假如我们需要在auth1和auth2之间增加一个审批节点,其添加的时机如何呢?按照流程实例的实际运转情况,有这么几点值得思考,第一点,加签的节点是在auth1审批完成了以后添加还是未审批之前添加呢?第二点,加签成功后,下一步的开始审批的地方从哪里开始呢?第三点,是不是可以增加很多节点呢?
activity任意节点加签数据持久化_第1张图片
假如说可以无无限增加加签节点的话,那么可能还会出现下面的情形
activity任意节点加签数据持久化_第2张图片

在之前的那篇任意加签节点中,其实我们说了加签节点的实现思路,这里再简单总结下,

1、构建加签节点的实例任务
最终加签的节点要运转,必须以任务的方式存在

2、构建任务节点的基本信息
节点的存在,和其他节点通过连线的方式连接在一起的,因此需要构建加签节点的出线、入线信息

3、将新增的加签节点信息保存到process
process中存储了所有节点的信息,任务信息邓,因此需要把构建好的完整的加签节点添加process

4、进行加签后任务的审批
加签成功后,就可以按照新的流程实例进行节点任务的审批了

按照这个思路,我们开始实现动态加签的代码实现过程

动态加签完整步骤

1、构建新增任务节点的userTask

/**
 * 加签节点操作相关
 */
public class AddNode {

    /**
     * @param processDefinitionId       流程定义ID
     * @param processInatanceId         流程实例ID
     * @param processEngine
     * @param taskModelList
     * @param firstNodeId               加签的源节点
     * @param lastNodeId                加签的目标节点
     * @param persistenceDataToDataBase 是否持久化到数据库
     * @param isFire
     * @param taskId                    当前的任务ID
     * @param targetNodeId              加签的节点ID
     */
    public void addUserTask(String processDefinitionId, String processInatanceId, ProcessEngine processEngine,
                            List taskModelList, String firstNodeId, String lastNodeId, boolean persistenceDataToDataBase,
                            boolean isFire, String taskId, String targetNodeId) {
        ManagementService managementService = processEngine.getManagementService();
        managementService.executeCommand(new GetProcessCmd(processDefinitionId));
        //确保这里一定可以从缓存中拿到流程定义相关信息
        ProcessDefinitionCacheEntry processDefinitionCacheEntry = managementService.executeCommand(new GetProcessDefinitionCacheEntryCmd(processDefinitionId));
        Process process = processDefinitionCacheEntry.getProcess();
        List userTasks = new ArrayList<>(taskModelList.size());

        // 批量生成任务,将用户自定义的相关流程信息转为activity的userTask
        for (TaskModel taskModel : taskModelList) {
            UserTask userTask = GenerateActivityUtils.transformation(taskModel, processEngine);
            userTasks.add(userTask);
            process.addFlowElement(userTask);
        }

        for (int i = 0; i < userTasks.size(); i++) {
            UserTask userTask = userTasks.get(i);
            SequenceFlow sequenceFlow = null;
            if (i == userTasks.size() - 1) {
                //是最后一个任务节点,即你要加签的节点数量,可以加多个
                sequenceFlow = GenerateActivityUtils.genarateSequenceFlow(userTask.getId() + "---->>>>" + lastNodeId, userTask.getId() + "---->>>>" + lastNodeId,
                        userTask.getId(), lastNodeId);
                sequenceFlow.setTargetFlowElement(process.getFlowElement(lastNodeId));
                userTask.setOutgoingFlows(Arrays.asList(sequenceFlow));
            } else {
                //不是最后一个节点
                sequenceFlow = GenerateActivityUtils.genarateSequenceFlow(userTask.getId() + "--->>>" + userTasks.get(i + 1).getId(), userTask.getId() + "--->>>" + userTasks.get(i + 1).getId(), userTask.getId(),
                        userTasks.get(i + 1).getId());
                sequenceFlow.setTargetFlowElement(userTasks.get(i + 1));
                userTask.setOutgoingFlows(Arrays.asList(sequenceFlow));
            }
            //将连线信息添加到process
            process.addFlowElement(sequenceFlow);
        }
        //更新缓存,确保缓存中存储的是最新的数据
        processDefinitionCacheEntry.setProcess(process);
        if (isFire) {
            managementService.executeCommand(new JumpNodeCmd(taskId, targetNodeId));
        }
        if (persistenceDataToDataBase) {
            persistenceDataToDataBase(processDefinitionId, processInatanceId, firstNodeId, lastNodeId, taskModelList, processEngine);
        }
    }

    /**
     * 将加签的任务节存储到数据库中
     *
     * @param processDefinitionId
     * @param processInatanceId
     * @param firstNodeId
     * @param lastNodeId
     * @param taskModelList
     * @param processEngine
     */
    private void persistenceDataToDataBase(String processDefinitionId, String processInatanceId, String firstNodeId,
                                           String lastNodeId, List taskModelList, ProcessEngine processEngine) {
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        SqlSessionFactory sqlSessionFactory = processEngineConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        TempActivityModel tempActivityModel = new TempActivityModel();
        tempActivityModel.setFirst(firstNodeId);
        tempActivityModel.setLast(lastNodeId);
        tempActivityModel.setActivity(taskModelList);
        StringBuffer sb = new StringBuffer();
        for (TaskModel taskModel : taskModelList) {
            sb.append(taskModel.getId() + ",");
        }
        tempActivityModel.setActivityIds(sb.toString());
        CreationMapper cm = sqlSession.getMapper(CreationMapper.class);
        ActCreation actCreation = new ActCreation();
        actCreation.setProcessInstanceId(processInatanceId);
        actCreation.setProcessDefinitionId(processDefinitionId);
        actCreation.setProcessText(JSON.toJSONString(tempActivityModel));
        cm.insert(actCreation);
        sqlSession.commit();
        sqlSession.close();
    }

}

上述代码用到的GetProcessDefinitionCacheEntryCmd

public class GetProcessDefinitionCacheEntryCmd  implements Command{
	String processDefinitionId;
	
	public GetProcessDefinitionCacheEntryCmd(String processDefinitionId) {
		this.processDefinitionId = processDefinitionId;
	}

	@Override
	public ProcessDefinitionCacheEntry execute(CommandContext commandContext) {
		DeploymentManager deploymentManager = commandContext.getProcessEngineConfiguration().getDeploymentManager();
		ProcessDefinitionCacheEntry processDefinitionCacheEntry = deploymentManager.getProcessDefinitionCache().get(processDefinitionId);
		return processDefinitionCacheEntry;
	}

}

用到的节点转换工具类:

public class GenerateActivityUtils {
	/**
	 * 生成连线
	 * 
	 * @param id
	 * @param name
	 * @param source
	 * @param target
	 * @return
	 */
	public static SequenceFlow genarateSequenceFlow(String id, String name, String source, String target) {
		SequenceFlow sequenceFlow = new SequenceFlow();
		sequenceFlow.setId(id);
		sequenceFlow.setName(name);
		sequenceFlow.setSourceRef(source);
		sequenceFlow.setTargetRef(target);
		return sequenceFlow;
	}

	/**
	 * 生成任务节点
	 * 
	 * @param id
	 * @param name
	 * @param assignee
	 * @param processEngine
	 * @return
	 */
	public static UserTask generateUserTask(String id, String name, String assignee, ProcessEngine processEngine) {
		UserTask userTask = new UserTask();
		userTask.setId(id);
		userTask.setName(name);
		userTask.setAssignee(assignee);
		userTask.setBehavior(createUserTaskBehvior(userTask, processEngine));
		return userTask;
	}
	
	/**
	 * 生成自定义的任务模型
	 * @param id
	 * @param name
	 * @param assignee
	 * @return
	 */
	public static TaskModel generateTaskModel(String id, String name, String assignee) {
		TaskModel taskModel=new TaskModel();
		taskModel.setId(id);
		taskModel.setName(name);
		taskModel.setDoUserId(assignee);
		return taskModel;
	}
	/**
	 * 将自定义的TaskModel转换为UserTask;
	 * @param taskModel
	 * @param processEngine
	 * @return
	 */
	public static UserTask transformation(TaskModel taskModel,ProcessEngine processEngine) {
		UserTask generateUserTask = generateUserTask(taskModel.getId(), taskModel.getName(), taskModel.getDoUserId(), processEngine);
		return generateUserTask;
	}

	/**
	 * 生成任务行为类
	 * 
	 * @param userTask
	 * @param processEngine
	 * @return
	 */
	private static UserTaskActivityBehavior createUserTaskBehvior(UserTask userTask, ProcessEngine processEngine) {
		ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine
				.getProcessEngineConfiguration();
		ActivityBehaviorFactory activityBehaviorFactory = processEngineConfiguration.getActivityBehaviorFactory();
		UserTaskActivityBehavior userTaskActivityBehavior = activityBehaviorFactory
				.createUserTaskActivityBehavior(userTask);
		return userTaskActivityBehavior;
	}
}

JumpNodeCmd

public class JumpNodeCmd implements Command {

    private String taskId;
    private String targetNodeId;

    public JumpNodeCmd(String taskId, String targetNodeId) {
        this.taskId = taskId;
        this.targetNodeId = targetNodeId;
    }

    public Void execute(CommandContext commandContext) {
        ActivitiEngineAgenda agenda = commandContext.getAgenda();
        TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();
        TaskEntity te = taskEntityManager.findById(taskId);
        //执行实例ID
        String executionId = te.getExecutionId();
        String processDefinitionId = te.getProcessDefinitionId();
        ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
        HistoryManager historyManager = commandContext.getHistoryManager();
        //执行实例实例对象
        ExecutionEntity ee = executionEntityManager.findById(executionId);
        Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
        FlowElement flowElement = process.getFlowElement(targetNodeId);

        if (flowElement == null) {
            throw new RuntimeException("目标节点不存在");
        }
        historyManager.recordActivityEnd(ee, "jump");
        ee.setCurrentFlowElement(flowElement);
        agenda.planContinueProcessInCompensation(ee);
        //删除当前的任务
        taskEntityManager.delete(taskId);
        //更新的是历史活动表
        historyManager.recordTaskEnd(taskId, "jump");
        return null;
    }
}

2、加签代码测试

其实最重要的就是上面的这个类,下面就通过几个测试的方法来模拟一下

3、部署流程文件和启动流程

	 //部署
    public static void deploy(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment =
                repositoryService.createDeployment()
                        .addClasspathResource("process/demo/dynamicAddNode.bpmn").name("dynamicAddNode").deploy();
        System.out.println("流程部署的id:" + deployment.getId());
        System.out.println("流程部署的name:" +deployment.getName());
    }

    //启动流程实例
    public static void runProcess(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.startProcessInstanceByKey("dynamicAdd");
        System.out.println("流程实例启动成功");
    }
    
	public static void main(String[] args) throws Exception{
        deploy();
        runProcess();
    }

我们在main方法中调用一下,可以看到当前任务表中,任务来到auth1节点
在这里插入图片描述
4、加签节点测试

按照前文所说,我们在auth1和auth2之间进行节点加签,最终代码如下

	/**
     * 测试新增一个任务节点
     * @throws Exception
     */
    public static void addUserTasks() throws Exception {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        String taskId = "2505";
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        String firstNodeId = "auth1";
        String lastNodeId = "auth2";
        List taskModelList = new ArrayList<>();
        TaskModel generateTaskModel1 = GenerateActivityUtils.generateTaskModel("authMiddle1", "authMiddle1","authMiddle1");
		TaskModel generateTaskModel2 = GenerateActivityUtils.generateTaskModel("authMiddle2", "authMiddle2","authMiddle2");
        taskModelList.add(generateTaskModel1);
        taskModelList.add(generateTaskModel2);
        AddNode shareniuAddNode = new AddNode();
        String processDefinitionId = taskEntity.getProcessDefinitionId();
        String processInatanceId = taskEntity.getProcessInstanceId();
        shareniuAddNode.addUserTask(processDefinitionId, processInatanceId, processEngine, taskModelList, firstNodeId,
                lastNodeId, true, true, taskId, taskModelList.get(0).getId());
    }

	public static void main(String[] args) throws Exception{
        addUserTasks();
    }

在测试的过程中,为了避免出现空指针异常,我们在这里配置了一个监听器

public class MyProcessEngineLifecycleListener implements ProcessEngineLifecycleListener {

    @Override
    public void onProcessEngineBuilt(ProcessEngine processEngine) {
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine .getProcessEngineConfiguration();
        if(processEngineConfiguration==null){
            System.out.println("null");
        }
        SqlSessionFactory sqlSessionFactory = processEngineConfiguration.getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        if(sqlSession ==null){
            System.out.println("null");
        }
        List selectList = sqlSession.selectList("com.congge.addpersistant.CreationMapper.find");
        for (ActCreation actCreation : selectList) {
            String processDefinitionId = actCreation.getProcessDefinitionId();
            String processInatanceId = actCreation.getProcessInstanceId();
            String processText = actCreation.getProcessText();
            TempActivityModel parseObject = JSON.parseObject(processText, TempActivityModel.class);
            AddNode addNode = new AddNode();
            addNode.addUserTask(processDefinitionId, processInatanceId, processEngine, parseObject.getActivity(), parseObject.getFirst(), parseObject.getLast(), false, false, null, null);
        }
    }

    @Override
    public void onProcessEngineClosed(ProcessEngine processEngine) {

    }

}

然后再在activiti.cfg.xml配置一下即可

		
            
        

运行上述代码,测试一下是否能够成功进行加签,观察数据库的任务表,发现已经成功加签,任务节点来到authMiddle1
在这里插入图片描述

5、完成加签节点的任务审批

	public static void completeTask() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        taskService.complete("5002");
    }
    
	public static void main(String[] args) throws Exception{
        completeTask();
    }

继续运行上面的代码,发现任务来到我们的第二个加签节点authMiddle2
在这里插入图片描述

继续运行,这时我们看到任务走到了我们原本的auth2节点了
activity任意节点加签数据持久化_第3张图片

通过上面的代码测试,可以看到不仅成功完成了加签,同时加签的节点仍然可以按照我们预期的效果进行流转

本篇到此结束,最后感谢观看!

你可能感兴趣的:(activity)