Based on ruoyi-nbcio’s support for flowable process roles, and at the same time modifying the flow user to username, the process startup will be greatly adjusted (1)

For more ruoyi-nbcio functions, please see the demo system

gitee source code address

Front-end and back-end code: https://gitee.com/nbacheng/ruoyi-nbcio

Demonstration address: RuoYi-Nbcio backend management system

The original process based on it does not support role transfer. At the same time, without any processing of the node logic after startup, many problems will be encountered. At the same time, the ID was originally used as the key user for process transfer. Now the username account is modified as the key user for transfer. All of these are Make unified adjustments, so the originally inherited projects will no longer be supported in the future.

1. Make the following adjustments to the startup process.

/**
     * Start process instance
     */
    private R startProcess(ProcessDefinition procDef, Map<String, Object> variables) {
        if (ObjectUtil.isNotNull(procDef) & amp; & procDef.isSuspended()) {
            throw new ServiceException("The process has been suspended, please activate the process first");
        }
        //Set the process initiator ID to the process, including variables
        String userStr = TaskUtils.getUserName();
        SysUser sysUsr = sysUserService.selectUserByUserName(userStr);
 setFlowVariables(sysUsr,variables);
 \t\t
 Map<String, Object> variablesnew = variables;
 Map<String, Object> usermap = new HashMap<String, Object>();
        List<String> userlist = new ArrayList<String>();
        boolean bparallelGateway = false;
        boolean bapprovedEG = false;
        //Get next node information
        getNextFlowInfo(procDef, variablesnew, usermap, variables, userlist);
        //Take out two special variables
        if(variablesnew.containsKey("bparallelGateway")) {//Parallel Gateway
        bparallelGateway = (boolean) variablesnew.get("bparallelGateway");
        variablesnew.remove("bparallelGateway");
        }
        if(variablesnew.containsKey("bapprovedEG")) {//General refusal to agree to the exclusive gateway
        bapprovedEG = (boolean) variablesnew.get("bapprovedEG");
        variablesnew.remove("bapprovedEG");
        }
        //Initiate process instance
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDef.getId(), variables);
        // If the first user task is the initiator, the task will be completed automatically
        wfTaskService.startFirstTask(processInstance, variables);
        return setNextAssignee(processInstance, usermap, userlist, sysUsr, variables, bparallelGateway, bapprovedEG);
    }
    

2. Set the next node handler code as follows:

 /**
* Set the next node information processing personnel
* add by nbacheng
*
* @param variablesnew, usermap,
* userlist, sysUser, variables, bparallelGateway
*
* @return
*/
private R setNextAssignee(ProcessInstance processInstance, Map<String, Object> usermap,
List<String> userlist, SysUser sysUser, Map<String, Object> variables,
boolean bparallelGateway, boolean bapprovedEG) {
// Set the task executor and opinions for the first step applicant node
if((usermap.containsKey("isSequential")) & amp; & amp; !(boolean)usermap.get("isSequential")) {//More than 2 concurrent signatures will require special handling
List<Task> nexttasklist = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().list();
int i=0;
for (Task nexttask : nexttasklist) {
String assignee = userlist.get(i).toString();
taskService.addComment(nexttask.getId(), processInstance.getProcessInstanceId(),
FlowComment.NORMAL.getType(), sysUser.getNickName() + "Initiate process application");
taskService.setAssignee(nexttask.getId(), assignee);
i + + ;
}
return R.ok("Multi-instance countersign process started successfully.");
 }
else {//Set the task executor and opinions for the first step applicant node
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active()
.singleResult();
if (Objects.nonNull(task)) {
taskService.addComment(task.getId(), processInstance.getProcessInstanceId(),
FlowComment.NORMAL.getType(), sysUser.getNickName() + "Initiate process application");
taskService.setAssignee(task.getId(), sysUser.getUserName());
}

// Get the next node data and setting data

FlowNextDto nextFlowNode = wfTaskService.getNextFlowNode(task.getId(), variables);
if(Objects.nonNull(nextFlowNode)) {
if (Objects.nonNull(task)) {
Map<String, Object> nVariablesMap = taskService.getVariables(task.getId());
if(nVariablesMap.containsKey("SetAssigneeTaskListener")) {//Whether to dynamically set the approver's task listener
taskService.complete(task.getId(), variables);
Task nexttask = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
taskService.setAssignee(nexttask.getId(), nVariablesMap.get("SetAssigneeTaskListener").toString());
return R.ok("The task listener process by dynamically setting the approver is started successfully.");
}
if(nVariablesMap.containsKey("SetDeptHeadTaskListener")) {//Whether to dynamically set the task listener of the initiator department head
taskService.complete(task.getId(), variables);
Task nexttask = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
if(Objects.nonNull(nexttask)) {
if(Objects.nonNull((List<String>) nVariablesMap.get("SetDeptHeadTaskListener"))) {
if(((List<String>) nVariablesMap.get("SetDeptHeadTaskListener")).size() == 1) {//Is it just one person?
taskService.setAssignee(nexttask.getId(), ((List<String>)nVariablesMap.get("SetDeptHeadTaskListener")).get(0).toString());
return R.ok("The process of setting the task listener of the initiator department head was started successfully.");
}
else {
for (String username : ((List<String>) nVariablesMap.get("SetDeptHeadTaskListener"))) {
taskService.addCandidateUser(nexttask.getId(), username);
}
return R.ok("The process of setting up task listeners for multiple initiator department heads was successfully started. Currently, users can complete the approval by signing.");
}
\t\t\t\t\t\t\t\t  
}
\t\t\t\t\t\t\t
}
\t\t\t\t\t\t  
}
}
if(Objects.nonNull(nextFlowNode.getUserList())) {
if( nextFlowNode.getUserList().size() == 1 ) {
if (nextFlowNode.getUserList().get(0) != null) {
if(StringUtils.equalsAnyIgnoreCase(nextFlowNode.getUserList().get(0).getUserName(), "${INITIATOR}")) {//Special treatment for the initiator
taskService.complete(task.getId(), variables);
return R.ok("The process started successfully to the initiator.");
}
else if(nextFlowNode.getUserTask().getCandidateUsers().size()>0 & amp; & amp; StringUtils.equalsAnyIgnoreCase(nextFlowNode.getUserTask().getCandidateUsers().get(0), "${DepManagerHandler.getUsers( execution)}")) {//Special treatment for department managers
//taskService.complete(task.getId(), variables);
return R.ok("The process is successfully started and reported to the department manager. Please go to my to-do to submit and transfer the process.");
}
else {
taskService.complete(task.getId(), variables);
return R.ok("Process started successfully.");
}
}
else {
return R.fail("The approver does not exist, the process failed to start!");
}
\t\t\t\t\t\t
}
else if(nextFlowNode.getType() == ProcessConstants.PROCESS_MULTI_INSTANCE ) {//You can also do special processing for multi-instance countersigning or modify it in the process design later
Map<String, Object> approvalmap = new HashMap<>();
List<String> sysuserlist = nextFlowNode.getUserList().stream().map(obj-> (String) obj.getUserName()).collect(Collectors.toList());
approvalmap.put("approval", sysuserlist);
taskService.complete(task.getId(), approvalmap);
if(!nextFlowNode.isBisSequential()){//Assignee separate assignment for concurrent countersigning
List<Task> nexttasklist = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().list();
int i=0;
for (Task nexttask : nexttasklist) {
String assignee = sysuserlist.get(i).toString();
taskService.setAssignee(nexttask.getId(), assignee);
i + + ;
}
\t\t  \t\t\t\t 
}
return R.ok("Multi-instance countersign process started successfully.");
}
else if(nextFlowNode.getUserList().size() > 1) {
if (bparallelGateway) {//If the latter node is a parallel gateway
taskService.complete(task.getId(), variables);
return R.ok("Process started successfully.");
}
else {
return R.ok("The process started successfully, please go to my to-do to submit and transfer the process.");
}
}
else {
return R.ok("Process startup failed, please check the process setter!");
}
}
else {//Special processing for skipping processes
List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, variables);
if (CollectionUtils.isNotEmpty(nextUserTask)) {
List<FlowableListener> listlistener = nextUserTask.get(0).getTaskListeners();
if(CollectionUtils.isNotEmpty(listlistener)) {
String tasklistener = listlistener.get(0).getImplementation();
if(StringUtils.contains(tasklistener, "AutoSkipTaskListener")) {
taskService.complete(task.getId(), variables);
return R.ok("Process started successfully.");
}else {
return R.ok("Process startup failed, please check the process setter!");
}
}else {
return R.ok("Process startup failed, please check the process setter!");
}
\t\t            \t
}
else {
return R.ok("Process startup failed, please check the process setter!");
}
}
}
else {
if(bapprovedEG) {
return R.ok("The general refusal to agree process has been started successfully, please go to my to-do to submit and transfer the process.");
}
taskService.complete(task.getId(), variables);
return R.ok("Process started successfully.");
}
}
}

3. Set the initiator variable

/**
* Set the initiator variable
* add by nbacheng
*
* @param variables
* Process variables
* @return
*/
private void setFlowVariables(SysUser sysUser,Map<String, Object> variables) {
//Set the process initiator ID to the process
        identityService.setAuthenticatedUserId(sysUser.getUserName());
        variables.put(BpmnXMLConstants.ATTRIBUTE_EVENT_START_INITIATOR, sysUser.getUserName());
        //Set the process status to in progress
        variables.put(ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.RUNNING.getStatus());
     //Set the process status to in progress
        variables.put(ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.RUNNING.getStatus());
}

4. Obtain the next node information and process the parallel and exclusive gateways

/**
* Obtain next node information and process parallel and exclusive gateways
* add by nbacheng
*
* @param processDefinition, variablesnew, usermap,
variables, userlist, bparallelGateway
*
* @return
*/
@SuppressWarnings("unchecked")
private void getNextFlowInfo(ProcessDefinition processDefinition, Map<String, Object> variablesnew, Map<String, Object> usermap,
Map<String, Object> variables, List<String> userlist) {
String definitionld = processDefinition.getId(); //Get bpm (model) object
        BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionld);
        //Pass the node definition key to get the current node
        List<org.flowable.bpmn.model.Process> processes = bpmnModel.getProcesses();
        //Only handle the situation where the initiator is followed by an exclusive gateway and then followed by a countersign. Others are not currently considered.
        //List<UserTask> userTasks = process.findFlowElementsOfType(UserTask.class);
        List<FlowNode> flowNodes = processes.get(0).findFlowElementsOfType(FlowNode.class);
        List<SequenceFlow> outgoingFlows = flowNodes.get(1).getOutgoingFlows();
        //Traverse and return the next node information
        for (SequenceFlow outgoingFlow : outgoingFlows) {
            //Judge the type yourself (get whether the next node is a gateway or a node)
            FlowElement targetFlowElement = outgoingFlow.getTargetFlowElement();
            //Next is the node
           if(targetFlowElement instanceof ExclusiveGateway){// If the next exit is an exclusive gateway, and the latter user task is countersigned, approval assignment processing is required, otherwise an error will be reported
        usermap = GetExclusiveGatewayUser(targetFlowElement,variables);//It is still necessary to return the user and whether it is concurrent, because concurrency requires special processing
        if(usermap != null) {
        userlist = (ArrayList<String>) usermap.get("approval");
        variablesnew.put("approval", userlist);
        }
        if(FindNextNodeUtil.GetExclusiveGatewayExpression(targetFlowElement)) {//The next exit is a universal refusal to agree to the exclusive gateway
        variablesnew.put("bapprovedEG",true);
        }
        break;
            }
           if(targetFlowElement instanceof ParallelGateway){// If the next exit is a parallel gateway, you need to complete it directly, otherwise an error will be reported
        variablesnew.put("bparallelGateway",true);
           }
        }
}