Springboot flowable workflow-2 (code integration)

Time:2022-1-14
Springboot flowable workflow-2 (code integration)

1. Preface

Last blog【Springboot integrates flowable workflow-1 (drawing process definition)】This paper draws a simple flow chart with flowable UI.


This blog will introduce the code integration part, which mainly includes: [publish process definition], [open process task], [get user task], [user approval task], [add approval opinion], [get flow chart], [get my to-do task], [get my initiated process], [my approved process]

2. Add code dependency

<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.4.2</version>
</dependency>

3. Create database

Creating a database can be realized by using SQL provided by flowable, or by automatically creating a database through a program

3.1 SQL provided by flowable

Download the resources related to the file flowable and enterhttps://flowable.com/open-source/downloadsClick [download flowable V6. X.x] to download a compressed package. After decompression, you will see the following directory structure

└ - database # database file
    └─create
        └─all
            └─flowable.mysql.all.create.sql

Find $/ database / create / database / create / flowable mysql. all. create. SQL file and import it into MySQL database

3.2 automatic database creation by application (recommended)

You need to add a parameter value in the JDBC URL, nullcatalogmeanscurrent = true

As follows:

jdbc:mysql://127.0.0.1:3306/flowable?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true

4. Code part

4.1 several common service classes

/**Runtime service (used for runtime process instances, process variables, process nodes)*/
@Autowired
private RuntimeService runtimeService;
/**Resource storage service (for model and process definition)*/
@Autowired
private RepositoryService repositoryService;
/**Process engine service*/
@Qualifier("processEngine")
@Autowired
private ProcessEngine processEngine;
/**Task service (used for user tasks and approval logs at runtime)*/
@Autowired
private TaskService taskService;
@Autowired
protected ManagementService managementService;
/**History service (used for history recording, which can find historical process instances and process nodes)*/
@Autowired
protected HistoryService historyService;

4.2 process definition and related codes

4.2.1 release process definition

Last blog【Springboot integrates flowable workflow-1 (drawing process definition)】After drawing the process, download an XML file of “leave process 1. Bpmn20. XML”. After downloading, you can publish the process to the process definition through code.

The code is as follows

@Override
public boolean importProcessDefinition(MultipartFile file) {
    try {
        Deployment deployment = repositoryService.createDeployment()
                // .key()
                // .name(name)
                // .category(category)
                // .tenantId()
                //Publish multiple rows at a time in the form of compressed packages
                // .addZipInputStream()
                //Publish as InputStream
                .addInputStream(file.getOriginalFilename(), file.getInputStream())
                //Publish through the file stored in the classpath directory
                // .addClasspathResource("p1.bpmn20.xml")
                //In the form of an XML string
                // .addString()
                .deploy();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        if (processDefinition == null) {
            return false;
        }
    } catch (Exception e) {
        Throw new businessexception ("import process definition failed:" + e.getmessage());
    }
    return true;
}

Based on the springboot publishing process definition, another ingenious form is to create a folder processes in the resources directory, and then send the corresponding process file to this folder. When starting the springboot project, it will be found by observing the log, and the process will be published automatically. (not recommended)

workflow-server
  └─src
     └─main
         ├─java
         └─resources
             ├─mapper
             └─processes
                 └ - leave process 1 bpmn20. xml

4.2.2 query process definition

//Create processdefinitionquery 
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//Define ID according to process
// processDefinitionQuery.processDefinitionId(requestDTO.getId());
//Define key according to process
// processDefinitionQuery.processDefinitionKeyLike("%" + requestDTO.getKey().trim() + "%");
//Define name according to process
// processDefinitionQuery.processDefinitionNameLike("%" + requestDTO.getName().trim() + "%");
//
//Get total
long count = processDefinitionQuery.count();
//Get single 
ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
//Get list
List<ProcessDefinition> processDefinitions = processDefinitionQuery.list();
//Get paging
List<ProcessDefinition> processDefinitions = processDefinitionQuery.listPage(0, 10)

4.2.3 get process definition XML

public String getXmlResource(String id) {
     ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
     InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                                                                     processDefinition.getResourceName());
     try {
         return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
     } catch (Exception e) {
         Throw new businessexception ("failed to get resources:" + e.getmessage());
     } finally {
         try {
             IOUtils.close(inputStream, null);
         } catch (IOException ignored) {
         }
     }
}

4.2.4 obtain process definition picture

public String getDiagramImageResource(String id) {
    //Theoretically, I can use this form, but there will be garbled code when I get it. I'm also strange, so I changed the way through bpmnmodel
    // ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
    // InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
    //                                                                 processDefinition.getDiagramResourceName());
    //Get bpmnmodel object
    BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
    //Generate picture stream
    ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
    ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
    InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", Collections.emptyList(),
                                                               Collections. Emptylist(), "Tahoma", "Tahoma", "Tahoma",
                                                               this.getClass().getClassLoader(), 1.0, true);
    try {
        return "data:image/png;base64," + Base64.encode(inputStream);
    } catch (Exception e) {
        Throw new businessexception ("failed to get resources:" + e.getmessage());
    } finally {
        try {
            IOUtils.close(inputStream, null);
        } catch (IOException ignored) {
        }
    }
}

4.2.5 delete process definition

public void deleteByIds(List<String> ids) {
     List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().processDefinitionIds(new HashSet<>(ids)).list();
     // repositoryService.deleteDeployment(String deploymentId, boolean cascade)
     //Delete the given deployment and cascade process instances, historical process instances, and jobs.
     processDefinitions.forEach(v -> repositoryService.deleteDeployment(v.getDeploymentId(), true));
 }

4.3 process instance related codes

4.3.1 add process instance approval comments

It plays a role similar to the operation record of the recording process. Here, I encapsulate one layer by myself. I encapsulate my business user ID, user name, execution type, opinion content

@Data
@Apimodel ("process instance - approval opinion request parameter")
public class ProcessInstanceCommentRequestDTO implements Serializable {

    @Apimodelproperty (value = "process definition key")
    private String processInstanceId;
    @Apimodelproperty (value = "task ID (default)")
    private String taskId;
    @Apimodelproperty (value = "type commententity: event / comment")
    private String type;

    @Apimodelproperty (value = "user ID")
    private String userId;
    @Apimodelproperty (value = "user nickname")
    private String nickname;
    @Apimodelproperty (value = "execution type")
    private String executeType;
    @Apimodelproperty (value = "execution type (refer to executetype enum) - submit; yes - agree; no - reject; stop - process termination; delete - process deletion")
    private String executeTypeValue;
    @Apimodelproperty (value = "content")
    private String content;
    @Apimodelproperty (value = "additional content carried")
    private String ext;
}

Add process instance approval comments

//Add process instance approval record
public void addProcessInstanceComment(ProcessInstanceCommentRequestDTO requestDTO) {
    CommentWrapper wrapper = new CommentWrapper();
    wrapper.setUserId(requestDTO.getUserId());
    wrapper.setNickname(requestDTO.getNickname());
    wrapper.setExecuteType(requestDTO.getExecuteType());
    wrapper.setExecuteTypeValue(requestDTO.getExecuteTypeValue());
    wrapper.setContent(requestDTO.getContent());
    wrapper.setExt(requestDTO.getExt());
    String message = JSON.toJSONString(wrapper);
    //Add an approval comment using taskservice
    taskService.addComment(requestDTO.getTaskId(), requestDTO.getProcessInstanceId(), requestDTO.getType(), message);
}

4.3.2 example of startup process

@Data
@Apimodel ("process instance - start request parameter")
public class ProcessInstanceStartRequestDTO implements Serializable {

    @Apimodelproperty (value = "process definition key")
    @Notempty (message = "process definition key cannot be empty")
    private String processDefinitionKey;
    @Apimodelproperty (value = "process instance name")
    @Notempty (message = "process instance name cannot be empty")
    private String name;
    @Apimodelproperty (value = "item ID")
    @Notempty (message = "item ID cannot be empty")
    private String communityId;
    @Apimodelproperty (value = "global variable")
    private Map<String, Object> variables;

}

Start process instance

@Transactional(rollbackFor = Exception.class)
public ProcessInstanceStartResponseDTO startProcessInstance(ProcessInstanceStartRequestDTO requestDTO) {
    ValidatorUtils.validate(requestDTO);
    //
    ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
    processDefinitionQuery.processDefinitionKey(requestDTO.getProcessDefinitionKey());
    ProcessDefinition processDefinition = processDefinitionQuery.latestVersion().singleResult();
    AssertUtils. Notempty (ProcessDefinition, "process definition not found");
    //Start process
    ProcessInstanceBuilder builder = runtimeService.createProcessInstanceBuilder();
    builder.processDefinitionKey(requestDTO.getProcessDefinitionKey());
    builder.name(requestDTO.getName());
    // variables("name", "value") 
    //You can add process variables. For example, I added many business variables here to process business when the process flows
    builder.variables(requestDTO.getVariables());
    builder.variable(VAR_COMMUNITY_ID, VAR_COMMUNITY_ID_EQ + requestDTO.getCommunityId());
    builder.variable(VAR_PROCESS_INSTANCE_NAME, VAR_PROCESS_INSTANCE_NAME_EQ + requestDTO.getName());
    builder.variable(VAR_CREATE_USER_ID, VAR_CREATE_USER_ID_EQ + SecurityUser.getUserId());
    builder.variable(VAR_CREATE_USER_NICKNAME, VAR_CREATE_USER_NICKNAME_EQ + SecurityUser.get().getName());
    builder.variable(VAR_PROCESS_DEFINITION_NAME, VAR_PROCESS_DEFINITION_NAME_EQ + processDefinition.getName());
    builder.transientVariables(requestDTO.getTransientVariables());
    // builder.tenantId("101");
    ProcessInstance processInstance = builder.start();
    //Add approval comments
    ProcessInstanceCommentRequestDTO commentRequestDTO = new ProcessInstanceCommentRequestDTO();
    commentRequestDTO.setProcessInstanceId(processInstance.getProcessInstanceId());
    commentRequestDTO.setTaskId(null);
    commentRequestDTO.setUserId(SecurityUser.getUserId());
    commentRequestDTO.setNickname(SecurityUser.get().getName());
    commentRequestDTO.setExecuteType(ExecuteTypeEnum.SUBMIT.name());
    commentRequestDTO.setExecuteTypeValue(ExecuteTypeEnum.SUBMIT.getValue());
    commentRequestDTO. Setcontent (requestdto. Getname() + "submission process");
    commentRequestDTO.setExt("");
    this.addProcessInstanceComment(commentRequestDTO);
    //Build responsedto
    ProcessInstanceStartResponseDTO responseDTO = new ProcessInstanceStartResponseDTO();
    responseDTO.setProcessInstanceId(processInstance.getProcessInstanceId());
    responseDTO.setProcessDefinitionId(processInstance.getProcessDefinitionId());
    responseDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
    responseDTO.setProcessDefinitionName(processInstance.getProcessDefinitionName());
    responseDTO.setName(processInstance.getName());
    responseDTO.setBusinessKey(processInstance.getBusinessKey());
    responseDTO.setDescription(processInstance.getDescription());
    //
    return responseDTO;
}

4.3.3 obtain process progress picture

public String getProcessImage(String processInstanceId) {
    // 1. Get the current process instance
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    String processDefinitionId;
    List<String> activeActivityIds = new ArrayList<>();
    List<String> highLightedFlows = new ArrayList<>();
    // 2. Gets all historical Trackline objects
    List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
            .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW).list();
    historicActivityInstances.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
    // 3.  Get the process definition ID and the highlighted node ID
    if (processInstance != null) {
        //3.1 running process instances
        processDefinitionId = processInstance.getProcessDefinitionId();
        activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
    } else {
        //3.2 completed process examples
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        processDefinitionId = historicProcessInstance.getProcessDefinitionId();
        //3.3 get end node list
        List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
        List<String> finalActiveActivityIds = activeActivityIds;
        historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
    }
    // 4.  Get bpmnmodel object
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
    // 5.  Generate picture stream
    ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
    ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
    InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds,
                                                               Highlightedflows, "Tahoma", "Tahoma", "Tahoma",
                                                               this.getClass().getClassLoader(), 1.0, true);
    // 6.  Convert to Base64 network transmission
    return "data:image/png;base64," + Base64.encode(inputStream);
}

4.3.4 obtain process approval opinions

@Override
public List<ProcessInstanceCommentResponseDTO> findProcessInstanceCommentList(String processInstanceId) {
    List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
    List<ProcessInstanceCommentResponseDTO> list = processInstanceComments.stream()
            .map(this::convertComment)
            .filter(Objects::nonNull).collect(Collectors.toList());
    return list;
}

private ProcessInstanceCommentResponseDTO convertComment(Comment v) {
    String fullMessage = v.getFullMessage();
    if (StringUtils.startsWith(fullMessage, "{")) {
        CommentWrapper wrapper = JSON.parseObject(fullMessage, CommentWrapper.class);
        ProcessInstanceCommentResponseDTO responseDTO = new ProcessInstanceCommentResponseDTO();
        responseDTO.setProcessInstanceId(v.getProcessInstanceId());
        responseDTO.setType(v.getType());
        responseDTO.setTaskId(v.getTaskId());
        responseDTO.setTime(v.getTime());
        responseDTO.setUserId(wrapper.getUserId());
        responseDTO.setNickname(wrapper.getNickname());
        responseDTO.setExecuteType(wrapper.getExecuteType());
        responseDTO.setExecuteTypeValue(wrapper.getExecuteTypeValue());
        responseDTO.setContent(wrapper.getContent());
        responseDTO.setExt(wrapper.getExt());
        return responseDTO;
    }
    return null;
}

4.3.5 process instance execution next step

@Data
@Apimodel ("process instance - execute next request parameter")
public class ProcessInstanceExecuteNextStepRequestDTO implements Serializable {

    @Apimodelproperty (value = "process instance ID")
    @Notempty (message = "process instance ID cannot be empty")
    private String processInstanceId;
    @Apimodelproperty (value = "task ID")
    private String taskId;
    @Apimodelproperty (value = "executetype enum execution type")
    @Notempty (message = "execution type cannot be empty")
    private String executeType;
    @Apimodelproperty (value = "approval comments")
    @Notempty (message = "approval comments cannot be blank")
    private String commentContent;
    @Apimodelproperty (value = "variable parameter")
    private HashMap<String, Object> variables;

}
@Transactional(rollbackFor = Exception.class)
public void executeNextStep(ProcessInstanceExecuteNextStepRequestDTO requestDTO) {
    ValidatorUtils.validate(requestDTO);
    //
    if (ExecuteTypeEnum.of(requestDTO.getExecuteType()).isNone()) {
        Throw new businessexception ("unknown execution status");
    }
    TaskQuery taskQuery = taskService.createTaskQuery();
    taskQuery.taskId(requestDTO.getTaskId());
    Task task = taskQuery.singleResult();
    AssertUtils. Notempty (task, "task not found");
    //
    //Add approval comments
    ProcessInstanceCommentRequestDTO commentRequestDTO = new ProcessInstanceCommentRequestDTO();
    commentRequestDTO.setProcessInstanceId(task.getProcessInstanceId());
    commentRequestDTO.setTaskId(task.getId());
    // commentRequestDTO.setType("event");
    commentRequestDTO.setUserId(SecurityUser.getUserId());
    commentRequestDTO.setNickname(SecurityUser.get().getName());
    commentRequestDTO.setExecuteType(requestDTO.getExecuteType());
    commentRequestDTO.setExecuteTypeValue(ExecuteTypeEnum.of(requestDTO.getExecuteType()).getValue());
    commentRequestDTO.setContent(task.getName() + ":" + requestDTO.getCommentContent());
    commentRequestDTO.setExt("");
    //
    this.addProcessInstanceComment(commentRequestDTO);
    //Process approval
    HashMap<String, Object> variables = requestDTO.getVariables();
    if (variables == null) {
        variables = new HashMap<>(8);
    }
    //Here, an execution variable executetype will be put, that is, the variable name in the process definition XML, which determines the direction of the process
    variables.put(WorkflowConstants.EXECUTE_TYPE, requestDTO.getExecuteType());
    //Add executor
    variables.put("_execute_user_id=" + SecurityUser.getUserId(), "_execute_user_id=" + SecurityUser.getUserId());
    taskService.complete(task.getId(), variables);
}

4.3.6 termination process example

@Data
@Apimodel ("process instance - paging request parameter")
public class ProcessInstanceStopRequestDTO implements Serializable {

    @Apimodelproperty (value = "process instance ID")
    @Notempty (message = "process instance ID cannot be empty")
    private String processInstanceId;
    @Apimodelproperty (value = "approval comments")
    @Notempty (message = "approval comments cannot be blank")
    private String commentContent;

}
public void stopProcessInstance(ProcessInstanceStopRequestDTO requestDTO) {
    ValidatorUtils.validate(requestDTO);
    //Modify flow execution status
    runtimeService.setVariable(requestDTO.getProcessInstanceId(), WorkflowConstants.EXECUTE_TYPE, ExecuteTypeEnum.STOP.name());
    //
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(requestDTO.getProcessInstanceId()).singleResult();
    //Add an approval record
    ProcessInstanceCommentRequestDTO commentRequestDTO = new ProcessInstanceCommentRequestDTO();
    commentRequestDTO.setProcessInstanceId(processInstance.getProcessInstanceId());
    commentRequestDTO.setTaskId(null);
    commentRequestDTO.setUserId(SecurityUser.getUserId());
    commentRequestDTO.setNickname(SecurityUser.get().getName());
    commentRequestDTO.setExecuteType(ExecuteTypeEnum.STOP.name());
    commentRequestDTO.setExecuteTypeValue(ExecuteTypeEnum.STOP.getValue());
    commentRequestDTO. Setcontent (stringutils. Defaultstring (requestdto. Getcommentcontent(), "terminate process");
    commentRequestDTO.setExt("");
    this.addProcessInstanceComment(commentRequestDTO);
    ///Execution termination
    List<Execution> executions = runtimeService.createExecutionQuery().parentId(requestDTO.getProcessInstanceId()).list();
    List<String> executionIds = executions.stream().map(v -> v.getId()).collect(Collectors.toList());
    //Get process end point
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
    Process process = bpmnModel.getMainProcess();
    List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class);
    String endId = endNodes.get(endNodes.size() - 1).getId();
    //Perform jump
    runtimeService.createChangeActivityStateBuilder()
            .moveExecutionsToSingleActivityId(executionIds, endId)
            .changeState();
}

4.3.7 batch obtain process instance variable list

This method is user-defined because flowable does not have a corresponding method to obtain batch process variables according to the process instance ID list

/**
 *Act of instance mapping flowable_ hi_ Varinst table
 */
@Data
public class HistoryVariable implements Serializable {

    public static final HistoryVariable EMPTY = new HistoryVariable();

    private String id;
    private String rev;
    private String processInstanceId;
    private String executionId;
    private String taskId;
    private String name;
    private String varType;
    private String scopeId;
    private String subScopeId;
    private String scopeType;
    private String bytearrayId;
    private Double doubleValue;
    private Long longValue;
    private String text;
    private String text2;
    private Date createTime;
    private Date lastUpdatedTime;

    /*
    act_ hi_ Varinst field list:
    ID_
    REV_
    PROC_INST_ID_
    EXECUTION_ID_
    TASK_ID_
    NAME_
    VAR_TYPE_
    SCOPE_ID_
    SUB_SCOPE_ID_
    SCOPE_TYPE_
    BYTEARRAY_ID_
    DOUBLE_
    LONG_
    TEXT_
    TEXT2_
    CREATE_TIME_
    LAST_UPDATED_TIME_
     */
}

Service

@Override
public List<HistoryVariable> findHistoryVariableList(Collection<String> processInstanceIds) {
    if (CollectionUtils.isEmpty(processInstanceIds)) {
        return Collections.emptyList();
    }
    QueryWrapper<HistoryVariable> ew = new QueryWrapper<>();
    ew.in("t.PROC_INST_ID_", processInstanceIds);
    return this.baseMapper.findHistoryVariableList(ew);
}

Dao

List<HistoryVariable> findHistoryVariableList(@Param("ew") QueryWrapper<HistoryVariable> ew);

xml

<select resultType="cn.leadersheep.xz.workflow.server.entity.flowable.HistoryVariable">
    SELECT
    t.ID_ AS id,
    t.REV_ AS rev,
    t.PROC_INST_ID_ AS process_instance_id,
    t.EXECUTION_ID_ AS execution_id,
    t.TASK_ID_ AS task_id,
    t.NAME_ AS name,
    t.VAR_TYPE_ AS var_type,
    t.SCOPE_ID_ AS scope_id,
    t.SUB_SCOPE_ID_ AS sub_scope_id,
    t.SCOPE_TYPE_ AS scope_type,
    t.BYTEARRAY_ID_ AS bytearray_id,
    t.DOUBLE_ AS double_value,
    t.LONG_ AS long_value,
    t.TEXT_ AS text,
    t.TEXT2_ AS text2,
    t.CREATE_TIME_ AS create_time,
    t.LAST_UPDATED_TIME_ AS last_update_time

    FROM act_hi_varinst t
    <where>
        ${ew.sqlSegment}
    </where>
</select>

4.3.8 get process instance Pagination

Get process instance paging, because it involvesData permission filtering, andneed to be dealt withAlready doneHistorical recordsI initiateWait, the logic is quite complex, so it may not be a good choice to simply use the flowable API. Therefore, the custom implementation here finds the matching records by looking up the relevant database tables of flowable


1. If the process instance is still in progress, the data is saved in theact_ru_*In these tables, if the process instance ends, the data is saved in theact_hi_*In these tables, you need to query unused tables according to different scenarios;
2. The assigned user groups defined in the process definition diagram are saved in theact_ru_identitylinkIn the table;

Process instance – paging request parameters

@Data
@EqualsAndHashCode(callSuper = true)
@Apimodel ("process instance - paging request parameter")
public class ProcessInstancePageRequestDTO extends PageParamsEntity {

    @Apimodelproperty (value = "item ID")
    private String communityId;
    @Apimodelproperty (value = "search range: my_todo - my to do; my_done - my done; my_scope - my range; my_create - my create;")
    private String searchScope;
    @Apimodelproperty (value = "task name")
    private String processInstanceName;

}

Process definition – paging response results

@Data
@Apimodel ("process definition - paging response result")
public class ProcessInstancePageResponseDTO implements Serializable {

    @Apimodelproperty (value = "item ID")
    private String communityId;
    @Apimodelproperty (value = "project name")
    private String communityName;
    @Apimodelproperty (value = "process instance ID")
    private String processInstanceId;
    @Apimodelproperty (value = "process instance name")
    private String processInstanceName;
    @Apimodelproperty (value = "process definition ID")
    private String processDefinitionId;
    @Apimodelproperty (value = "process definition name")
    private String processDefinitionName;
    @Apimodelproperty (value = "task ID")
    private String taskId;
    @Apimodelproperty (value = "task name")
    private String taskName;
    @Apimodelproperty (value = "start time")
    private Date startTime;
    @Apimodelproperty (value = "end time")
    private Date endTime;
    @Apimodelproperty (value = "duration")
    private String duration;
    @Apimodelproperty (value = "creator ID")
    private String createId;
    @Apimodelproperty (value = "creator name")
    private String createName;

}

Service

public PageDTO<ProcessInstancePageResponseDTO> findPage(ProcessInstancePageRequestDTO requestDTO) {
    //Data filtering is implemented here
    String filterSql = StringUtils.defaultString(processFilterSQL("TEXT_", requestDTO.getCommunityId(), true), "");
    filterSql = filterSql.replace("('", "('" + WorkflowConstants.VAR_COMMUNITY_ID_EQ);
    filterSql = filterSql.replace(", '", ", '" + WorkflowConstants.VAR_COMMUNITY_ID_EQ);
    // TEXT_ IN ('_community_id=1545645315843546', '_community_id=15456453158436521')
    // System.out.println("filterSQL = " + filterSQL);
    //
    PageDTO<ProcessInstancePageResponseDTO> page = this.buildPage(requestDTO);
    if ("MY_TODO".equals(requestDTO.getSearchScope())) {
        //My to-do list
        QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
        ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t4.NAME_", requestDTO.getProcessInstanceName());
        ew.eq("1", 1);
        ew.apply(CoreUtil.isNotEmpty(filterSql), "t3." + filterSql);
        ew.orderByDesc("t.CREATE_TIME_");
        ew.groupBy("t.PROC_INST_ID_");
        //
        if (!AuthHelper.isRoleSuperAdmin(SecurityUser.current().getRoleCodeSet())) {
            //You are not a super administrator and need to filter
            ew.and(and -> and.in("t2.GROUP_ID_", SecurityUser.current().getRoleCodeSet()).or(or -> {
                or.eq("t2.USER_ID_", SecurityUser.getUserId());
            }));
        }
        page = this.baseMapper.findMyTodo(page, ew);
    } else if ("MY_DONE".equals(requestDTO.getSearchScope())) {
        //My work has been done
        QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
        ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t.NAME_", requestDTO.getProcessInstanceName());
        ew.eq("t2.TEXT_", "_execute_user_id=" + SecurityUser.getUserId());
        ew.apply(filterSql.length() > 0, "t3." + filterSql);
        ew.orderByDesc("t.START_TIME_");
        ew.groupBy("t.ID_");
        //
        page = this.baseMapper.findMyDonePage(page, ew);
    } else if ("MY_SCOPE".equals(requestDTO.getSearchScope())) {
        //My scope
        QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
        ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t.NAME_", requestDTO.getProcessInstanceName());
        ew.eq("1", 1);
        ew.apply(filterSql.length() > 0, "t4." + filterSql);
        ew.orderByDesc("t.START_TIME_");
        ew.groupBy("t.ID_");
        //
        if (!AuthHelper.isRoleSuperAdmin(SecurityUser.current().getRoleCodeSet())) {
            //You are not a super administrator and need to filter
            ew.and(and -> and.in("t3.GROUP_ID_", SecurityUser.current().getRoleCodeSet()).or(or -> {
                or.eq("t3.USER_ID_", SecurityUser.getUserId());
            }));
        }
        page = this.baseMapper.findMyScopePage(page, ew);
    } else if ("MY_CREATE".equals(requestDTO.getSearchScope())) {
        //I initiated it
        QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
        ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t.NAME_", requestDTO.getProcessInstanceName());
        ew.eq("t2.TEXT_", "_create_user_id=" + SecurityUser.getUserId());
        ew.apply(filterSql.length() > 0, "t3." + filterSql);
        ew.orderByDesc("t.START_TIME_");
        ew.groupBy("t.ID_");
        //
        page = this.baseMapper.findMyDonePage(page, ew);
    }
    //
    if (CollectionUtils.isNotEmpty(page.getRecords())) {
        //Populate other attributes
        List<String> processInstanceIds = page.getRecords().stream().map(v -> v.getProcessInstanceId()).distinct().collect(Collectors.toList());
        List<HistoryVariable> variableList = this.findHistoryVariableList(processInstanceIds);
        Map<String, HistoryVariable> variableMap = variableList.stream()
                .collect(Collectors.toMap(v -> v.getProcessInstanceId() + "_" + v.getName(), v -> v));
        page.getRecords().forEach(v -> {
            String prefix = v.getProcessInstanceId() + "_";
            //
            HistoryVariable variable = variableMap.getOrDefault(prefix + VAR_COMMUNITY_ID, HistoryVariable.EMPTY);
            String text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_COMMUNITY_ID_EQ, "")).orElse(null);
            v.setCommunityId(text);
            //
            // variable = variableMap.getOrDefault(prefix + VAR_PROCESS_INSTANCE_NAME, HistoryVariable.EMPTY);
            // text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_PROCESS_INSTANCE_NAME_EQ, "")).orElse(null);
            // v.setProcessInstanceName(text);
            //
            variable = variableMap.getOrDefault(prefix + VAR_CREATE_USER_ID, HistoryVariable.EMPTY);
            text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_CREATE_USER_ID_EQ, "")).orElse(null);
            v.setCreateId(text);
            //
            variable = variableMap.getOrDefault(prefix + VAR_CREATE_USER_NICKNAME, HistoryVariable.EMPTY);
            text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_CREATE_USER_NICKNAME_EQ, "")).orElse(null);
            v.setCreateName(text);
            //
            variable = variableMap.getOrDefault(prefix + VAR_PROCESS_DEFINITION_NAME, HistoryVariable.EMPTY);
            text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_PROCESS_DEFINITION_NAME_EQ, "")).orElse(null);
            v.setProcessDefinitionName(text);
        });
        sysOrganizationRemote.fillOrganization(page.getRecords(), v -> v.getCommunityId(), (v, c) -> v.setCommunityName(c.getName()));
    }
    return page;
}

Dao

/**
 *Get my to-do tasks
 *
 * @author houyu [email protected] <br>
 *@ param page paging
 *@ param EW parameter wrapper
 * @return PageDTO<ProcessInstancePageResponseDTO>
 */
PageDTO<ProcessInstancePageResponseDTO> findMyTodo(@Param("page") PageDTO<ProcessInstancePageResponseDTO> page, @Param("ew") QueryWrapper<ProcessInstancePageResponseDTO> ew);

/**
 *Get tasks in my scope
 *
 * @author houyu [email protected] <br>
 *@ param page paging
 *@ param EW parameter wrapper
 * @return PageDTO<ProcessInstancePageResponseDTO>
 */
PageDTO<ProcessInstancePageResponseDTO> findMyScopePage(@Param("page") PageDTO<ProcessInstancePageResponseDTO> page, @Param("ew") QueryWrapper<ProcessInstancePageResponseDTO> ew);

/**
 *Get my done (initiated / approved by me)
 *
 * @author houyu [email protected] <br>
 *@ param page paging
 *@ param EW parameter wrapper
 * @return PageDTO<ProcessInstancePageResponseDTO>
 */
PageDTO<ProcessInstancePageResponseDTO> findMyDonePage(@Param("page") PageDTO<ProcessInstancePageResponseDTO> page, @Param("ew") QueryWrapper<ProcessInstancePageResponseDTO> ew);

Xml

<select resultType="cn.leadersheep.xz.workflow.client.dto.flowable.response.ProcessInstancePageResponseDTO">
    SELECT
    t.ID_ AS task_id,
    t.PROC_INST_ID_ AS process_instance_id,
    t4.NAME_ AS process_instance_name,
    t.NAME_ AS task_name,
    t.PROC_DEF_ID_ as process_definition_id,
    t.CREATE_TIME_ as start_time,
    NULL as end_time

    FROM act_ru_task t
    LEFT JOIN act_ru_identitylink t2 ON t2.TASK_ID_ = t.ID_
    LEFT JOIN act_ru_variable t3 ON t3.PROC_INST_ID_ = t.PROC_INST_ID_
    LEFT JOIN act_hi_procinst t4 ON t4.PROC_INST_ID_ = t.PROC_INST_ID_
    <where>
        ${ew.sqlSegment}
    </where>
</select>

<select resultType="cn.leadersheep.xz.workflow.client.dto.flowable.response.ProcessInstancePageResponseDTO">
    SELECT
    t.PROC_INST_ID_ AS process_instance_id,
    t.NAME_ AS process_instance_name,
    t.PROC_DEF_ID_ as process_definition_id,
    t.START_TIME_ as start_time,
    t.END_TIME_ as end_time

    FROM act_hi_procinst t
    LEFT JOIN act_hi_taskinst t2 ON t2.PROC_INST_ID_ = t.ID_
    LEFT JOIN act_hi_identitylink t3 ON t3.TASK_ID_ = t2.ID_
    LEFT JOIN act_hi_varinst t4 ON t4.PROC_INST_ID_ = t.ID_
    <where>
        ${ew.sqlSegment}
    </where>
</select>

<select resultType="cn.leadersheep.xz.workflow.client.dto.flowable.response.ProcessInstancePageResponseDTO">
    SELECT
    t.PROC_INST_ID_ AS process_instance_id,
    t.NAME_ AS process_instance_name,
    t.PROC_DEF_ID_ as process_definition_id,
    t.START_TIME_ as start_time,
    t.END_TIME_ as end_time

    FROM act_hi_procinst t
    LEFT JOIN act_hi_varinst t2 ON t2.PROC_INST_ID_ = t.ID_
    LEFT JOIN act_hi_varinst t3 ON t3.PROC_INST_ID_ = t.ID_
    <where>
        ${ew.sqlSegment}
    </where>
</select>

5. Linux deployment flow chart text garbled

Briefly describe the situation I encountered. I developed it on windows and set the font of the flowchart to Song typeface. There was no garbled code, but when I deployed to the Linux server to view the flowchart, the text was garbled, Then I can probably guess that it is due to the lack of fonts (because I have known that the chaotic code of activiti flowchart is the lack of fonts), so I will add Song Ti fonts to Linux according to the same routine this time.

5.1 copying fonts on Windows

Enter the directory: C: \ windows \ fonts copy “Arial general” to the desktop for standby

Springboot flowable workflow-2 (code integration)

5.2 Linux Java location

Execute command

whereis java

My is in / var / lib / JDK / jdk1 8.0_ 211/bin/java

5.3 copy font file to JRE / lib / fonts

Find out the location of Java, and the location of JRE is in the first few directories of Java

java:
/var/lib/jdk/jdk1.8.0_211/bin/java

JRE font directory (copy here):
/var/lib/jdk/jdk1.8.0_211/jre/lib/fonts

5.4 copy the font file to the system font directory (/ usr / share / fonts)

This directory may not exist. If it does not exist, create it yourself

/usr/share/fonts

5.5 restart the system

reboot

After restarting the system, start the application, that is, there will be no garbled code. Good luck~


Last blog:Springboot integrates flowable workflow-1 (drawing process definition)

The code based on flowable spring boot starter integration is basically completed, but it still feels a little missing. The process is executed step by step. When will it be completed? What’s the point now? It seems that we don’t know how to operate after executing the business. We need to introduce the flowable global event listener. The next blog will introduce the flowable global event listener and realize the business notification business in combination with the listener.