【业务功能篇36】Springboot+activiti7 工作流引擎

这篇具有很好参考价值的文章主要介绍了【业务功能篇36】Springboot+activiti7 工作流引擎。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

业务场景:前段时间总结的有一个告警工单流程,我们都是直接自己建表,状态节点,操作节点,都是自定义设计的,而到后面会有很多的工单流程,比如创建一个遗留问题电子流,指定处理人进行分析闭环,等等多种电子流,后期重复的开发工作以及维护工作会越来越多。那么这里我们可以借助开源的工作流引擎 activiti

  •   Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言(BPMN2.0)进行定义,业务系统按照预先定义的流程进行执行,实现了业务系统的业务流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。 

前置条件

  1. 本地安装jdk8以上
  2. 下载tomcat, 下载过程需要选择jre,所以要先安装好jdk

安装

Activiti6.0官方文档:https://www.activiti.org/userguide/

下载Activiti6.0,解压后有activiti-app.war、activiti-admin.war、activiti-rest.war三个war包。

  • activiti-app:在线流程设计器、表单管理、应用管理、用户管理等;
  • activiti-admin:查看流程定义、发起的流程实例、任务等;
  • activiti-rest:提供Restful风格的服务;

将上面3个war放到tomcat的webapps目录下,运行bin目录下的startup.bat可以启动应用。

启动应用后,浏览器可以访问:
http://localhost:8080/activiti-app 默认账号密码:admin test
http://localhost:8080/activiti-admin 默认账号密码:admin admin
http://127.0.0.1:8080/activiti-rest/docs 默认账号密码:kermit kermit

补充: 这里我们只需要运用这个app包,来进行在线流程设计,也就是用建模语言bpmn2.0定义流程图,结合实际业务场景的流程进行作图设计,然后再导出 xml文件,再放到我们的项目程序运行即可

 访问登录后页面

【业务功能篇36】Springboot+activiti7 工作流引擎,业务场景实例问题,Spring boot,Java,spring boot,后端,java,activiti

 这里我们是利用create process 创建流程,进行我们的业务流程图定义

【业务功能篇36】Springboot+activiti7 工作流引擎,业务场景实例问题,Spring boot,Java,spring boot,后端,java,activiti

总结特点:

1.流程图准备:

把对应的流程图画好导出bpmn2.0文件后,放入到项目中 resources ->  process 目录下 ,文件以xml形式保存

2.引入依赖jar包

<dependency>

<groupId>org.activiti</groupId>

<artifactId>activiti-spring-boot-starter</artifactId>

<version>7.1.0.M6</version>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-actuator-autoconfigure</artifactId>

</exclusion>

</exclusions>

</dependency>



<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-security</artifactId>

<version>2.6.6</version>

</dependency>

3.配置文件中 进行配置

activiti:

database-schema-update:true   

#drop-create   该配置表示每次会删除activiti框架内置的表,然后再重新创建   true值则不会再重新建表

history-level:full

deployment-name:deploy-test

db-history-used:true

#process-definition-location-prefix:classpath*:/processes/

4.接着运行程序项目后,框架会许多事情:

act_ge_bytearray       保存bpmn2.0xml文件记录

act_re_procdef           给bpmn2.0xml,创建对应的流程定义

5.也可以调用service接口  当前前端在创建一个问题电子流时,先生成一个 流程定义,对应的在act_re_procdef 表中会生成一个流程定义记录,对应的key是自定义的一个常量

@Override

publicProcessDefinitiongetKnowIssueProcessDef(){

Return repositoryService.createProcessDefinitionQuery()

.processDefinitionKey(KNOWN_ISSUE_PROCESS_DEF_KEY)

.latestVersion()

.singleResult();

}

 

 

 

 

6.针对该流程定义,创建一个实例,也就是开始一个流程的处理,需要由一个实例类对象来承载完成,与此同时,在对应的act_ru_task表中会生成一个该流程的一个子任务,也就是说,流程实例创建后,会对应这多个task任务,比如责任人处理任务,审批任务等,会把当前节点转到第一个子任务上,而不是在一开始的 开始事件

ProcessInstanceprocessInstance=runtimeService.startProcessInstanceByKey(KNOWN_ISSUE_PROCESS_DEF_KEY);

 

 

 

7.状态开始流转,节点任务走完,调用task的接口方法,传递对应的任务id

 

ProcessInstanceprocessInstance=runtimeService.startProcessInstanceByKey(KNOWN_ISSUE_PROCESS_DEF_KEY);

StringprocessInstanceId=processInstance.getId();

Tasktask=getKnownIssueTaskByProcessInstanceId(processInstanceId);

 

@Override

publicTaskgetKnownIssueTaskByProcessInstanceId(StringprocessInstanceId){

returntaskService.createTaskQuery()

.active()

.processInstanceId(processInstanceId)

.taskDefinitionKey(KNOWN_ISSUE_TASK_DEF_KEY)

.singleResult();

}

 

 

taskService.complete(task.getId());

 

8.如果流程只有一个任务节点,那么完成之后,流程就会结束,在act_ru_task表中的任务记录就会删除,并且在 act_hi_taskinst表中会记录着这个任务的历史记录  ,  act_hi_actinst表中则会记录整个流程过程记录,比如一开始的开始事件--》 到任务节点 --》 到结束事件,分别记录这几条数据,任务节点停留了多长时间

9.流程涉及有审批的话,就会有审批通过与驳回,这时需要增加一个 排他网关节点,通过设定一个全局变量,在审批任务执行完成时,通过内置方设置变量值,我们自定义,值为true则审批通过,流程结束,为false那么就驳回,流程节点回到前一个任务,设置变量记录会在 act_ru_variable表中记录

【业务功能篇36】Springboot+activiti7 工作流引擎,业务场景实例问题,Spring boot,Java,spring boot,后端,java,activiti

 

10.一个流程,调用另外一个子流程

【业务功能篇36】Springboot+activiti7 工作流引擎,业务场景实例问题,Spring boot,Java,spring boot,后端,java,activiti

 

这里直接展开我们业务的遗留问题单电子流的功能设计开发

 controller层

package com.xxx.service;

import com.xxx.delegate.ProcessKnownIssueDelegate;
import org.springframework.web.bind.annotation.*;
import com.xxx.model.KnowIssueCreateParam;
import org.springframework.validation.annotation.Validated;
import com.xxx.model.UpdateProgressParam;
import com.xxx.tools.commonlib.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import com.xxx.model.KnownIssueInfo;
import com.xxx.model.ResponseVo;



@RestController
@RequestMapping(value = "/process/knownIssue", produces = {"application/json;charset=UTF-8"})
@Validated
public class ProcessKnownIssueController {

    @Autowired(required=false) 
    private ProcessKnownIssueDelegate delegate;  
	
    @RequestMapping(
    		value = "/closeKnownIssue", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo closeKnownIssue(@RequestBody KnownIssueInfo knownIssueInfo) 
            throws ServiceException {
    	    
		return delegate.closeKnownIssue(knownIssueInfo);
    }

	
    @RequestMapping(
    		value = "/createKnownIssue", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo createKnownIssue(@RequestBody KnowIssueCreateParam knowIssueCreateParam) 
            throws ServiceException {
    	    
		return delegate.createKnownIssue(knowIssueCreateParam);
    }

	
    @RequestMapping(
    		value = "/getKnownIssueDesc", 
    		produces = { "application/json" }, 
    		method = RequestMethod.GET)
    public ResponseVo getKnownIssueDesc( @RequestParam(value = "knownIssueId", required = true) String knownIssueId) 
            throws ServiceException {
    	    
		return delegate.getKnownIssueDesc(knownIssueId);
    }

	
    @RequestMapping(
    		value = "/getKnownIssueDetails", 
    		produces = { "application/json" }, 
    		method = RequestMethod.GET)
    public ResponseVo getKnownIssueDetails( @RequestParam(value = "knownIssueId", required = true) String knownIssueId) 
            throws ServiceException {
    	    
		return delegate.getKnownIssueDetails(knownIssueId);
    }

	
    @RequestMapping(
    		value = "/getKnownIssueList", 
    		produces = { "application/json" }, 
    		method = RequestMethod.GET)
    public ResponseVo getKnownIssueList( @RequestParam(value = "pageNo", required = true) Integer pageNo,
    		 @RequestParam(value = "pageSize", required = true) Integer pageSize) 
            throws ServiceException {
    	    
		return delegate.getKnownIssueList(pageNo
			, pageSize);
    }

	
    @RequestMapping(
    		value = "/getKnownIssueMoreDetails", 
    		produces = { "application/json" }, 
    		method = RequestMethod.GET)
    public ResponseVo getKnownIssueMoreDetails( @RequestParam(value = "knownIssueId", required = true) String knownIssueId) 
            throws ServiceException {
    	    
		return delegate.getKnownIssueMoreDetails(knownIssueId);
    }

	
    @RequestMapping(
    		value = "/getKnownIssueProgress", 
    		produces = { "application/json" }, 
    		method = RequestMethod.GET)
    public ResponseVo getKnownIssueProgress( @RequestParam(value = "knownIssueId", required = true) String knownIssueId) 
            throws ServiceException {
    	    
		return delegate.getKnownIssueProgress(knownIssueId);
    }

	
    @RequestMapping(
    		value = "/updateProgress", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo updateProgress(@RequestBody UpdateProgressParam updateProgressParam) 
            throws ServiceException {
    	    
		return delegate.updateProgress(updateProgressParam);
    }
}

service层 接口

package com.xxx.delegate;

import com.xxx.model.KnowIssueCreateParam;
import com.xxx.model.KnownIssueInfo;
import com.xxx.model.UpdateProgressParam;
import com.xxx.model.ResponseVo;
import com.xxx.tools.commonlib.exception.ServiceException;



public interface ProcessKnownIssueDelegate {
  
    ResponseVo closeKnownIssue(KnownIssueInfo knownIssueInfo) 
    	throws ServiceException;
  
    ResponseVo createKnownIssue(KnowIssueCreateParam knowIssueCreateParam) 
    	throws ServiceException;
  
    ResponseVo getKnownIssueDesc(String knownIssueId) 
    	throws ServiceException;
  
    ResponseVo getKnownIssueDetails(String knownIssueId) 
    	throws ServiceException;
  
    ResponseVo getKnownIssueList(Integer pageNo,
                     Integer pageSize) 
    	throws ServiceException;
  
    ResponseVo getKnownIssueMoreDetails(String knownIssueId) 
    	throws ServiceException;
  
    ResponseVo getKnownIssueProgress(String knownIssueId) 
    	throws ServiceException;
  
    ResponseVo updateProgress(UpdateProgressParam updateProgressParam) 
    	throws ServiceException;
  
}

service层 实现类 

这里的业务层比较抽象,下层还有定义了更具象的服务接口,注入该实现类进行调用


package com.xxx.impl.process;

import com.xxx.delegate.ProcessKnownIssueDelegate;
import com.xxx.model.KnowIssueCreateParam;
import com.xxx.model.KnownIssueInfo;
import com.xxx.model.ResponseVo;
import com.xxx.model.UpdateProgressParam;
import com.xxx.energytools.domain.entity.VerifyInfo;
import com.xxx.energytools.service.process.ProcessKnownIssueService;
import com.xxx.energytools.utils.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;


@Slf4j
@Service
public class ProcessKnownIssueDelegateImpl implements ProcessKnownIssueDelegate {
    @Resource
    ProcessKnownIssueService processKnownIssueService;


    /**
     * closeKnownIssue
     * 
     * @param knownIssueInfo knownIssueInfo
     * @return ResponseVo
     */
    @Override
    public ResponseVo closeKnownIssue(KnownIssueInfo knownIssueInfo) {
        boolean closed = processKnownIssueService.closeKnownIssue(knownIssueInfo.getKnownIssueId());
        if (closed) {
            return ResponseUtils.successResponse("问题关闭成功", "问题关闭成功");
        }
        return ResponseUtils.errorResponse(null, "问题关闭失败");
    }


    /**
     * createKnownIssue
     * 
     * @param knowIssueCreateParam knowIssueCreateParam
     * @return ResponseVo
     */
    @Override
    public ResponseVo createKnownIssue(KnowIssueCreateParam knowIssueCreateParam) {
        VerifyInfo info = verifyKnowIssueCreateParam(knowIssueCreateParam);
        if (!info.isPass()) {
            return ResponseUtils.errorResponse(info.getMessage(), "创建字段错误");
        }
        VerifyInfo knownIssue = processKnownIssueService.createKnownIssue(knowIssueCreateParam);
        if (knownIssue.isPass()) {
            return ResponseUtils.successResponse(knownIssue.getMessage(), "创建遗留问题成功");
        }
        return ResponseUtils.errorResponse(knownIssue.getMessage(), "创建遗留问题失败");
    }


    /**
     * verifyKnowIssueCreateParam
     * 
     * @param knowIssueCreateParam knowIssueCreateParam
     * @return VerifyInfo
     */
    private VerifyInfo verifyKnowIssueCreateParam(KnowIssueCreateParam knowIssueCreateParam) {
        VerifyInfo result = new VerifyInfo();
        if (knowIssueCreateParam == null) {
            return result.notPassed("创建字段为空");
        }
        if (StringUtils.isEmpty(knowIssueCreateParam.getIssueDesc())) {
            return result.notPassed("问题描述为空");
        }
        List<String> handlers = knowIssueCreateParam.getHandlers();
        if (handlers == null || handlers.isEmpty()) {
            result.notPassed("处理人为空");
        }
        return result;
    }


    /**
     * getKnownIssueDesc
     * 
     * @param knownIssueId knownIssueId
     * @return ResponseVo
     */
    @Override
    public ResponseVo getKnownIssueDesc(String knownIssueId) {
        return ResponseUtils.successResponse(processKnownIssueService.getKnownIssueDesc(knownIssueId), "获取信息成功");
    }


    /**
     * getKnownIssueDetails
     * 
     * @param knownIssueId knownIssueId
     * @return ResponseVo
     */
    @Override
    public ResponseVo getKnownIssueDetails(String knownIssueId) {
        return ResponseUtils.successResponse(processKnownIssueService.getKnownIssueDetails(knownIssueId), "获取信息成功");
    }


    /**
     * getKnownIssueList
     * 
     * @param pageNo pageNo
     * @param pageSize pageSize
     * @return ResponseVo
     */
    @Override
    public ResponseVo getKnownIssueList(Integer pageNo, Integer pageSize) {
        return processKnownIssueService.getKnownIssueList(pageNo, pageSize);
    }


    /**
     * getKnownIssueMoreDetails
     * 
     * @param knownIssueId knownIssueId
     * @return ResponseVo
     */
    @Override
    public ResponseVo getKnownIssueMoreDetails(String knownIssueId) {
        return ResponseUtils.successResponse(processKnownIssueService.getKnownIssueDetails(knownIssueId), "获取信息成功");
    }


    /**
     * getKnownIssueProgress
     * 
     * @param knownIssueId knownIssueId
     * @return ResponseVo
     */
    @Override
    public ResponseVo getKnownIssueProgress(String knownIssueId) {
        return ResponseUtils.successResponse(processKnownIssueService.getProgressDetailsByKnowIssueId(knownIssueId), "获取信息成功");
    }


    /**
     * updateProgress
     * 
     * @param updateProgressParam updateProgressParam
     * @return ResponseVo
     */
    @Override
    public ResponseVo updateProgress(UpdateProgressParam updateProgressParam) {
        String content = updateProgressParam.getContent();
        if (StringUtils.isBlank(content)) {
            return ResponseUtils.errorResponse(null, "更新进展失败: 进展不能为空");
        }
        VerifyInfo verifyInfo = processKnownIssueService.updateProcess(updateProgressParam);
        if (verifyInfo.isPass()) {
            return ResponseUtils.successResponse("更新进展成功", "更新进展成功");
        }
        return ResponseUtils.errorResponse(null, verifyInfo.getMessage());
    }
}

service层 接口


package com.xxx.service.process;

import com.xxx.model.KnowIssueCreateParam;
import com.xxx.model.ResponseVo;
import com.xxx.model.UpdateProgressParam;
import com.xxx.domain.entity.VerifyInfo;
import com.xxx.domain.vo.process.KnownIssueDetailsVo;
import com.xxx.domain.vo.ProgressDetailsVo;

import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Task;

import java.util.List;


public interface ProcessKnownIssueService {

    /**
     * getKnowIssueProcessDef
     * 
     * @return ProcessDefinition
     */
    ProcessDefinition getKnowIssueProcessDef();


    /**
     * createKnownIssue
     * 
     * @param createParam createParam
     * @return VerifyInfo
     */
    VerifyInfo createKnownIssue(KnowIssueCreateParam createParam);


    /**
     * getKnownIssueTaskByProcessInstanceId
     * 
     * @param processInstanceId processInstanceId
     * @return Task
     */
    Task getKnownIssueTaskByProcessInstanceId(String processInstanceId);


    /**
     * closeKnownIssue
     * 
     * @param knownIssueId knownIssueId
     * @return boolean
     */
    boolean closeKnownIssue(String knownIssueId);


    /**
     * getKnownIssueDetails
     * 
     * @param taskId taskId
     * @return KnownIssueDetailsVo
     */
    KnownIssueDetailsVo getKnownIssueDetails(String taskId);


    /**
     * getKnownIssueDesc
     * 
     * @param taskId taskId
     * @return String
     */
    String getKnownIssueDesc(String taskId);


    /**
     * getProgressDetailsByKnowIssueId
     * 
     * @param taskId taskId
     * @return List<ProgressDetailsVo>
     */
    List<ProgressDetailsVo> getProgressDetailsByKnowIssueId(String taskId);


    /**
     * updateProcess
     * 
     * @param updateProgressParam updateProgressParam
     * @return VerifyInfo
     */
    VerifyInfo updateProcess(UpdateProgressParam updateProgressParam);


    /**
     * getKnownIssueList
     * 
     * @param pageNo pageNo
     * @param pageSize pageSize
     * @return ResponseVo
     */
    ResponseVo getKnownIssueList(Integer pageNo, Integer pageSize);
}

service层 实现类



package com.xxx.service.impl.process;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxx.model.KnowIssueCreateParam;
import com.xxx.model.ResponseVo;
import com.xxx.model.UpdateProgressParam;
import com.xxx.adapter.UserAdapter;
import com.xxx.domain.dto.process.ProcessVarHistoryRecordDto;
import com.xxx.domain.entity.VerifyInfo;
import com.xxx.domain.entity.user.UserModel;
import com.xxx.domain.model.DpProcessMessage;
import com.xxx.domain.model.KnownIssueDetailsTab;
import com.xxx.domain.vo.ProgressDetailsVo;
import com.xxx.domain.vo.UserVo;
import com.xxx.domain.vo.process.KnownIssueDetailsVo;
import com.xxx.domain.vo.process.KnownIssueInfoVo;
import com.xxx.enums.State;
import com.xxx.service.UserService;
import com.xxx.service.dao.iservice.process.KnownIssueDetailsService;
import com.xxx.service.dao.mapper.process.ProcessVarHistoryMapper;
import com.xxx.service.process.ProcessKnownIssueService;
import com.xxx.service.process.ProcessMessage;
import com.xxx.utils.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.xxx.constants.process.ProcessConstants.CANCELED_STATE;
import static com.xxx.constants.process.ProcessConstants.CLOSE_STATE;
import static com.xxx.constants.process.ProcessConstants.KNOWN_ISSUE_PROCESS_DEF_KEY;
import static com.xxx.constants.process.ProcessConstants.KNOWN_ISSUE_PROGRESS_CONTENT;
import static com.xxx.constants.process.ProcessConstants.KNOWN_ISSUE_PROGRESS_UPDATER;
import static com.xxx.constants.process.ProcessConstants.KNOWN_ISSUE_PROGRESS_VAR;
import static com.xxx.constants.process.ProcessConstants.KNOWN_ISSUE_TASK_DEF_KEY;
import static com.xxx.constants.process.ProcessConstants.KNOWN_ISSUE_TASK_STATE_VAR;
import static com.xxx.constants.process.ProcessConstants.OPEN_STATE;


@Service
@Slf4j
public class ProcessKnownIssueServiceImpl extends ProcessMessage implements ProcessKnownIssueService {
    @Resource
    RepositoryService repositoryService;

    @Resource
    RuntimeService runtimeService;

    @Resource
    TaskService taskService;

    @Resource
    KnownIssueDetailsService knownIssueDetailsService;

    @Resource
    ProcessVarHistoryMapper processVarHistoryMapper;

    @Resource
    UserService userService;

    @Resource
    UserAdapter userAdapter;


    /**
     * getKnowIssueProcessDef
     * 获取流程定义  key值是在BPMN2.0流程图设计过程中 给变量 process identifier赋值的名称 需要与图中的名称一致才能找到该流程图
     * @return ProcessDefinition
     */
    @Override
    public ProcessDefinition getKnowIssueProcessDef() {
        return repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey(KNOWN_ISSUE_PROCESS_DEF_KEY)
            .latestVersion()
            .singleResult();
    }


    /**
     * createKnownIssue
     * 创建遗留问题电子流
     * @param createParam createParam
     * @return VerifyInfo
     */
    @Override
    public VerifyInfo createKnownIssue(KnowIssueCreateParam createParam) {
        VerifyInfo result = new VerifyInfo();
        ProcessDefinition knowIssueProcessDef = getKnowIssueProcessDef();
        if (knowIssueProcessDef == null) {
            return result.notPassed("system error: 未找到 遗留问题 流程定义");
        }
		
		//开启流程的一个实例对象,对应的key还是流程图的process identifier值  流程key
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(KNOWN_ISSUE_PROCESS_DEF_KEY);
		//获取流程id 
        String processInstanceId = processInstance.getId();
		//通过流程id 获取对应的一个子任务task
        Task task = getKnownIssueTaskByProcessInstanceId(processInstanceId);
        if (task == null) {
            return result.notPassed("system error: 未找到 遗留问题 实例任务");
        }
		
		//这里是创建业务的遗留问题单电子流表单,包括了业务的一些信息,以及对应的任务id,流程id 才能去关联对应的流程信息
        VerifyInfo createInfo = knownIssueDetailsService.createKnownIssue(task.getId(), task.getProcessInstanceId(),
            task.getCreateTime(), createParam);
        if (!createInfo.isPass()) {
			//创建不成功的话 就设置子任务id,对应的一个变量值 knownIssueState ,赋值CANCELED  并将任务提交complete 由于这个电子流比较简单只有一个处理任务 提交后,整个流程就结束了 这里赋值变量会插入到act_ru_variable表中的 TEXT_字段
            log.error("create known issue failed");
            taskService.setVariable(task.getId(), KNOWN_ISSUE_TASK_STATE_VAR, CANCELED_STATE);
            taskService.complete(task.getId());
            return result.notPassed(createInfo.getMessage());
        }
		
		//创建成功,则设置状态变量knownIssueState 赋值 OPEN
        taskService.setVariable(task.getId(), KNOWN_ISSUE_TASK_STATE_VAR, OPEN_STATE);

		//这里是创建一个业务流程电子流表,目的是承载一个简化通用的电子流清单,可能会存在多种业务电子流,用户会有多种业务类型电子流,统一展示在一个表单中,有一个表承载多种业务类型电子流
        DpProcessMessage message = new DpProcessMessage();
        message.setProcessType("遗留问题清单");
        message.setStatus("问题处理中");
        message.setMessage(createParam.getIssueDesc());
        message.setProcessCode(createInfo.getMessage());

		//这里是获取用户信息  电子流可能涉及多个处理人 list封装
        List<UserModel> users = userAdapter.getUserInfo(createParam.getHandlers());
        StringBuffer userStr = new StringBuffer();
		//多个用户进行拼接在一个字符串中,张三  01   是一个用户信息 然后换行放下一个用户信息
        users.forEach(user -> userStr.append(user.getNameCn()).append(String.format("  %s\n", user.getId())));
        message.setCreateUser(userStr.toString());
        message.setDealUser(userStr.toString());

        message.setProcessUrl(String.format("/leftIssueDetail?knownIssueId=%s", message.getProcessCode()));
        message.setCreateTime(LocalDateTime.now());
        sendMessage(message);
        return result.pass(createInfo.getMessage());
    }


    /**
     * getKnownIssueTaskByProcessInstanceId
     * 获取流程的指定子任务task
     * @param processInstanceId processInstanceId
     * @return Task
     */
    @Override
    public Task getKnownIssueTaskByProcessInstanceId(String processInstanceId) {
        return taskService.createTaskQuery()
            .active()
            .processInstanceId(processInstanceId)
            .taskDefinitionKey(KNOWN_ISSUE_TASK_DEF_KEY)
            .singleResult();
    }


    /**
     * closeKnownIssue
     * 关闭电子流
     * @param knownIssueId knownIssueId
     * @return boolean
     */
    @Override
    public boolean closeKnownIssue(String knownIssueId) {
		//获取指定单号的遗留问题电子流
        KnownIssueDetailsTab detailsTab = getDetailTabByKnowIssueId(knownIssueId);
        if (detailsTab == null) {
            return false;
        }
        // SR为生产预警单,定时任务自动关闭,不走当前用户判断逻辑
        if (!"SR".equals(knownIssueId.substring(0, 2)) && !detailsTab.getHandler()
            .contains(userService.getUser().getAccount())) {
            return false;
        }
        Task task = getKnownIssueTaskByProcessInstanceId(detailsTab.getProcessInstanceId());
        if (task == null) {
            return false;
        }
		//存在对应电子流,将其子任务 状态变量赋值 CLOSE   并提交完成  流程结束
        taskService.setVariable(task.getId(), KNOWN_ISSUE_TASK_STATE_VAR, CLOSE_STATE);
        taskService.complete(task.getId());

		//删除通用信息表记录,这样用户前台就没有显示问题清单了
        deleteMessage(knownIssueId);
        return true;
    }


    /**
     * getKnownIssueDetails
     * 获取问题电子流的明细
     * @param knowIssueId knowIssueId
     * @return KnownIssueDetailsVo
     */
    @Override
    public KnownIssueDetailsVo getKnownIssueDetails(String knowIssueId) {
        KnownIssueDetailsTab issueDetailsTab = getDetailTabByKnowIssueId(knowIssueId);
        if (!issueDetailsTab.getHandler().contains(userService.getUser().getAccount())) {
            return new KnownIssueDetailsVo();
        }
        return parse2KnownIssueDetailsVo(issueDetailsTab);
    }


    /**
     * parse2KnownIssueDetailsVo
     * 
     * @param detailsTab detailsTab
     * @return KnownIssueDetailsVo
     */
    private KnownIssueDetailsVo parse2KnownIssueDetailsVo(KnownIssueDetailsTab detailsTab) {
        KnownIssueDetailsVo result = new KnownIssueDetailsVo();
        if (detailsTab == null) {
            return result;
        }
        result.setKnownIssueId(detailsTab.getId());
        result.setLabel(detailsTab.getLabel());
        result.setPduName(detailsTab.getPduName());
        result.setPlanFinishedDate(detailsTab.getPlanFinishedDate());
		
		//这里是获取是流程对应的一个 状态最新情况 是打开 还是关闭 
        ProcessVarHistoryRecordDto stateVar = processVarHistoryMapper.findLatestVarByProcInsId(
            detailsTab.getProcessInstanceId(), KNOWN_ISSUE_TASK_STATE_VAR);
        if (stateVar != null && !StringUtils.isEmpty(stateVar.getVarContent())) {
			//这里状态值的设置   由于后台变量记录的是英文 OPEN , 前台需要展示中文,所以我们用枚举类 State 对每个状态进行转换
            result.setIssueStatus(State.valueOf(stateVar.getVarContent()).getNameCn());
        }
        result.setCurrentHandlers(getUserInfo(detailsTab));
        return result;
    }


    /**
     * getKnownIssueDesc
     * 获取问题的描述信息
     * @param knowIssueId knowIssueId
     * @return String
     */
    @Override
    public String getKnownIssueDesc(String knowIssueId) {
        KnownIssueDetailsTab detailsTab = getDetailTabByKnowIssueId(knowIssueId);
        if (detailsTab == null) {
            return null;
        }
        if (!detailsTab.getHandler().contains(userService.getUser().getAccount())) {
            return null;
        }
        return detailsTab.getIssueDesc();
    }


    /**
     * getProgressDetailsByKnowIssueId
     * 获取遗留问题单的进展细节
     * @param knowIssueId knowIssueId
     * @return List<ProgressDetailsVo>
     */
    @Override
    public List<ProgressDetailsVo> getProgressDetailsByKnowIssueId(String knowIssueId) {
        KnownIssueDetailsTab detailsTab = getDetailTabByKnowIssueId(knowIssueId);
        if (detailsTab == null) {
            return new ArrayList<>();
        }
        if (!detailsTab.getHandler().contains(userService.getUser().getAccount())) {
            return new ArrayList<>();
        }
        String taskId = detailsTab.getTaskId();
		//通过任务id 以及设置变量  PROGRESS_DETAIL问题进展,查询表中的历史提交进展记录 多次提交就有多个记录
        List<ProcessVarHistoryRecordDto> progressRecords = processVarHistoryMapper.findVarHistoryByTaskId(taskId,
            KNOWN_ISSUE_PROGRESS_VAR);
        return progressRecords.stream().map(this::parseHisDetail).collect(Collectors.toList());
    }


    /**
     * parseHisDetail
     * 解析历史进展细节
     * @param one one
     * @return ProgressDetailsVo
     */
    private ProgressDetailsVo parseHisDetail(ProcessVarHistoryRecordDto one) {
        ProgressDetailsVo result = new ProgressDetailsVo();
        result.setUpdateDate(one.getOperateDate());
		//这里的TEXT_值,前台返回的是 包括 更新人,更新内容两个信息 我们转换成json字符串保存{"UPDATER":"用户账号","CONTENT":"<p>进展内容</p>"}
        JSONObject varCont = JSONObject.parseObject(one.getVarContent());
		//解析JSON对象 获取 UPDATER 和 CONTENT 分别赋值
        result.setUser(userAdapter.getUserInfo(varCont.getString(KNOWN_ISSUE_PROGRESS_UPDATER)));
        result.setContent(varCont.getString(KNOWN_ISSUE_PROGRESS_CONTENT));
        return result;
    }


    /**
     * updateProcess
     * 更新遗留问题进展
     * @param updateProgressParam updateProgressParam
     * @return VerifyInfo
     */
    @Override
    public VerifyInfo updateProcess(UpdateProgressParam updateProgressParam) {
        VerifyInfo result = new VerifyInfo();
        Map<String, Object> progressMap = new HashMap<>();
        UserVo user = userService.getUser();
        if (user == null) {
            return result.notPassed("未获取到用户信息");
        }
		
		//传参有更新的问题单号,查询该单号的问题记录
        KnownIssueDetailsTab tab = getDetailTabByKnowIssueId(updateProgressParam.getKnownIssueId());
        if (!tab.getHandler().contains(userService.getUser().getAccount())) {
            return result.notPassed("您无权限修改此流程");
        }
		//获取流程id的流程当前最新状态
        State state = getStateByProcInsId(tab.getProcessInstanceId());
        if (!State.OPEN.equals(state)) {
            return result.notPassed("问题已关闭,无法更新进展");
        }
		
		//信息类map插入 更新人 更新内容
        progressMap.put(KNOWN_ISSUE_PROGRESS_UPDATER, user.getAccount());
        progressMap.put(KNOWN_ISSUE_PROGRESS_CONTENT, updateProgressParam.getContent());

		//给对应流程的这个子任务设置任务局部变量 流程进展 赋值为信息类
        taskService.setVariableLocal(tab.getTaskId(), KNOWN_ISSUE_PROGRESS_VAR, progressMap);
        return result.pass("更新进展成功");
    }


    /**
     * getStateByProcInsId
     * 
     * @param processInstanceId processInstanceId
     * @return State
     */
    private State getStateByProcInsId(String processInstanceId) {
        ProcessVarHistoryRecordDto stateDto = processVarHistoryMapper.findLatestVarByProcInsId(processInstanceId,
            KNOWN_ISSUE_TASK_STATE_VAR);
        if (stateDto == null) {
            return null;
        }
        return State.valueOf(stateDto.getVarContent());
    }


    /**
     * getKnownIssueList
     * 获取问题清单
     * @param pageNo pageNo
     * @param pageSize pageSize
     * @return ResponseVo
     */
    @Override
    public ResponseVo getKnownIssueList(Integer pageNo, Integer pageSize) {
        UserVo user = userService.getUser();
        if (user == null) {
            return ResponseUtils.unAuthResponse(new ArrayList<>(), "请登录系统");
        }
        List<KnownIssueDetailsTab> list = knownIssueDetailsService.list(
            new LambdaQueryWrapper<KnownIssueDetailsTab>().like(KnownIssueDetailsTab::getHandler,
                "%" + user.getAccount() + "%"));
        List<String> taskIds = list.stream()
            .map(KnownIssueDetailsTab::getTaskId)
            .distinct()
            .collect(Collectors.toList());
        List<String> procInsIds = list.stream()
            .map(KnownIssueDetailsTab::getProcessInstanceId)
            .distinct()
            .collect(Collectors.toList());
        Map<String, ProcessVarHistoryRecordDto> progressMap = getLatestProgressMap(taskIds);
        Map<String, ProcessVarHistoryRecordDto> stateMap = getStateMap(procInsIds);

        List<KnownIssueInfoVo> infos = list.stream()
            .filter(item -> item.getHandler().contains(user.getAccount()))
            .map(knownIssueDetailsTab -> parseDetailTab(knownIssueDetailsTab, progressMap, stateMap))
            .collect(Collectors.toList());
        List<KnownIssueInfoVo> result = infos.stream()
            .sorted(Comparator.comparing(KnownIssueInfoVo::getKnownIssueId).reversed())
            .skip((long) (pageNo - 1) * pageSize)
            .limit(pageSize)
            .collect(Collectors.toList());
        return ResponseUtils.successPageResponse(result, infos.size());
    }


    /**
     * getStateMap
     * 
     * @param procInsIds procInsIds
     * @return Map<String,ProcessVarHistoryRecordDto>
     */
    private Map<String, ProcessVarHistoryRecordDto> getStateMap(List<String> procInsIds) {
        List<ProcessVarHistoryRecordDto> stateVars = processVarHistoryMapper.findLatestVarByProcInsIds(procInsIds,
            KNOWN_ISSUE_TASK_STATE_VAR);
        return stateVars.stream().collect(Collectors.toMap(ProcessVarHistoryRecordDto::getProcInsId, item -> item));
    }


    /**
     * getLatestProgressMap
     * 
     * @param taskIds taskIds
     * @return Map<String,ProcessVarHistoryRecordDto>
     */
    private Map<String, ProcessVarHistoryRecordDto> getLatestProgressMap(List<String> taskIds) {
        List<ProcessVarHistoryRecordDto> progressVars = processVarHistoryMapper.findLatestVarByTaskIds(taskIds,
            KNOWN_ISSUE_PROGRESS_VAR);
        return progressVars.stream().collect(Collectors.toMap(ProcessVarHistoryRecordDto::getTaskId, item -> item));
    }


    /**
     * parseDetailTab
     * 
     * @param knownIssueDetailsTab knownIssueDetailsTab
     * @param progressMap progressMap
     * @param stateMap stateMap
     * @return KnownIssueInfoVo
     */
    private KnownIssueInfoVo parseDetailTab(KnownIssueDetailsTab knownIssueDetailsTab,
        Map<String, ProcessVarHistoryRecordDto> progressMap, Map<String, ProcessVarHistoryRecordDto> stateMap) {
        KnownIssueInfoVo result = new KnownIssueInfoVo();
        String knownIssueId = knownIssueDetailsTab.getId();
        String taskId = knownIssueDetailsTab.getTaskId();
        String processInstanceId = knownIssueDetailsTab.getProcessInstanceId();
        result.setKnownIssueId(knownIssueId);
        result.setIssueDesc(knownIssueDetailsTab.getIssueDesc());
        result.setLabel(knownIssueDetailsTab.getLabel());
        result.setPduName(knownIssueDetailsTab.getPduName());
        result.setPlanFinishedDate(knownIssueDetailsTab.getPlanFinishedDate());
        result.setCurrentHandlers(
            getUserInfo(knownIssueDetailsTab).stream().map(UserModel::getNameCn).collect(Collectors.joining(",")));
        if (progressMap.containsKey(taskId)) {
            ProcessVarHistoryRecordDto processVarHistoryRecordDto = progressMap.get(taskId);
            String varContent = processVarHistoryRecordDto.getVarContent();
            JSONObject jsonObject = JSONObject.parseObject(varContent);
            result.setLatestProgress(jsonObject.getString(KNOWN_ISSUE_PROGRESS_CONTENT));
            UserModel userInfo = userAdapter.getUserInfo(jsonObject.getString(KNOWN_ISSUE_PROGRESS_UPDATER));
            if (userInfo != null) {
                result.setLatestUpdater(userInfo.getNameCn());
            }
        }
        if (stateMap.containsKey(processInstanceId)) {
            ProcessVarHistoryRecordDto processVarHistoryRecordDto = stateMap.get(processInstanceId);
            State state = State.valueOf(processVarHistoryRecordDto.getVarContent());
            result.setIssueStatus(state.getNameCn());
            if (State.CLOSE.equals(state)) {
                result.setCloseDate(processVarHistoryRecordDto.getOperateDate());
            }
        }
        return result;
    }


    /**
     * getUserInfo
     * 
     * @param knownIssueDetailsTab knownIssueDetailsTab
     * @return List<UserModel>
     */
    private List<UserModel> getUserInfo(KnownIssueDetailsTab knownIssueDetailsTab) {
        return userAdapter.getUserInfo(JSONArray.parseArray(knownIssueDetailsTab.getHandler(), String.class));
    }


    /**
     * getDetailTabByKnowIssueId
     * 
     * @param knowIssueId knowIssueId
     * @return KnownIssueDetailsTab
     */
    private KnownIssueDetailsTab getDetailTabByKnowIssueId(String knowIssueId) {
        return knownIssueDetailsService.getOne(
            new LambdaQueryWrapper<KnownIssueDetailsTab>().eq(KnownIssueDetailsTab::getId, knowIssueId));
    }
}

dao层 接口 创建遗留问题单



package com..service.dao.iservice.process;

import com.model.KnowIssueCreateParam;
import com.domain.entity.VerifyInfo;
import com.domain.model.KnownIssueDetailsTab;

import com.baomidou.mybatisplus.extension.service.IService;

import java.util.Date;


public interface KnownIssueDetailsService extends IService<KnownIssueDetailsTab> {


    /**
     * createKnownIssue
     * 
     * @param taskId taskId
     * @param procInsId procInsId
     * @param createTime createTime
     * @param param param
     * @return VerifyInfo
     */
    VerifyInfo createKnownIssue(String taskId, String procInsId, Date createTime, KnowIssueCreateParam param);
}

dao层 实现 创建遗留问题单


package com..service.impl.dao.iservice.process;

import com.model.KnowIssueCreateParam;
import com.domain.entity.VerifyInfo;
import com.domain.model.KnownIssueDetailsTab;
import com.service.dao.iservice.process.KnownIssueDetailsService;
import com.service.dao.mapper.process.KnownIssueDetailsMapper;
import com.utils.DateUtils;
import com.it.auth.util.StringUtils;
import com.tools.commonlib.exception.ServiceException;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;


@Service
@Slf4j
public class KnownIssueDetailsServiceImpl extends ServiceImpl<KnownIssueDetailsMapper, KnownIssueDetailsTab>
    implements KnownIssueDetailsService {

	//可重入锁  目的就是如果存在大量的问题单在同一时刻创建,那么就会导致 问题单号的重复,因为单号是根据表中当前的数量累加,可能存在第一次请求还没结束 第二次的也接着请求 导致单号最大数值一样了 加锁目的就是为了请求是一次次的完成 
    private static final ReentrantLock LOCK = new ReentrantLock();


    /**
     * createKnownIssue
     * 
     * @param taskId taskId
     * @param procInsId procInsId
     * @param createTime createTime
     * @param param param
     * @return VerifyInfo
     */
    @Override
    public VerifyInfo createKnownIssue(String taskId, String procInsId, Date createTime, KnowIssueCreateParam param) {
        VerifyInfo result = new VerifyInfo();
        KnownIssueDetailsTab saveOne = new KnownIssueDetailsTab();
        saveOne.setTaskId(taskId);
        saveOne.setProcessInstanceId(procInsId);
        saveOne.setIssueDesc(param.getIssueDesc());
		
		//存储处理人 转换成json格式数组
        saveOne.setHandler(JSONArray.toJSONString(param.getHandlers()));
        saveOne.setLabel(param.getLabel());
        saveOne.setPduName(param.getPduName());
        saveOne.setCreatedDate(createTime);
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            saveOne.setPlanFinishedDate(dateFormat.parse(param.getPlanFinishedDate()));
        } catch (ParseException e) {
            log.error("date format error");
            return result.notPassed("param error: date format error");
        }
        try {
            LOCK.lock();
            String id = "";
            if (StringUtils.isEmpty(param.getProblemNo())) {
                id = String.format(Locale.ROOT, "KI%s%s", DateUtils.getTimeMillisString(new Date()),
                    formatNum2Five(baseMapper.getTodayKnownIssueNum() + 1));
                saveOne.setId(id);
            } else {
                id = param.getProblemNo();
            }
            saveOne.setId(id);
			
			//调用mp的内置方法 插入数据
            save(saveOne);
            return result.pass(id);
        } catch (ServiceException e) {
            log.error("create problem failed");
            return result.notPassed("system error: create KI id, save data error");
        } finally {
            LOCK.unlock();
        }
    }


    /**
     * formatNum2Five
     * 
     * @param num num
     * @return String
     * @throws ServiceException ServiceException
     */
    private String formatNum2Five(int num) throws ServiceException {
        if (num > 99999 || num < 0) {
            throw new ServiceException("num out of range");
        }
        String numStr = String.valueOf(num);
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < (5 - numStr.length()); i++) {
            builder.append("0");
        }
        return builder + numStr;
    }

}

dao层 mapper接口



package com.service.dao.mapper.process;

import com.domain.model.KnownIssueDetailsTab;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;


@Mapper
public interface KnownIssueDetailsMapper extends BaseMapper<KnownIssueDetailsTab> {


    /**
     * getTodayKnownIssueNum
     * 
     * @return int
     */
    @Select("select count(*) from dp_process_known_issue_details where TO_DAYS(created_date) = TO_DAYS(NOW()) ")
    int getTodayKnownIssueNum();
}

dao层 mapper接口  获取流程历史记录的变量信息 

  • 这里我们设定了两种变量  一个是流程状态knownIssueState, 一个是进展细节 PROGRESS_DETAIL ,都是保存在表中的act_hi_detail,这里是历史记录表,会记录这个流程或者任务我们设定的变量的全部过程,比如状态 ,是从 OPEN开始的,闭环了则增加一条 CLOSE的内容,也就是状态变量 记录有2条,进展细节也是同理,一个子任务 提交多少次细节处理,我们就把内容都赋值给变量进行插入记录。对应就有相应条数记录


package com.service.dao.mapper.process;

import com.domain.dto.process.ProcessVarHistoryRecordDto;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;


@Mapper
public interface ProcessVarHistoryMapper {

    /**
     * 根据 任务id taskId 查询 流程 局部变量信息
     *
     * @param taskId taskId
     * @param varName varName
     * @return List<ProcessVarHistoryRecordDto>
     */
    List<ProcessVarHistoryRecordDto> findVarHistoryByTaskId(@Param("taskId") String taskId,
        @Param("varName") String varName);

    /**
     * 根据 流程实例id procInsId 查询 流程 变量信息
     *
     * @param procInsId procInsId
     * @param varName varName
     * @return List<ProcessVarHistoryRecordDto>
     */
    List<ProcessVarHistoryRecordDto> findVarHistoryByProcInsId(@Param("procInsId") String procInsId,
        @Param("varName") String varName);

    /**
     * 根据taskIds 批量获取 遗留问题的最新进展详情
     *
     * @param taskIds taskIds
     * @param varName varName
     * @return List<ProcessVarHistoryRecordDto>
     */
    List<ProcessVarHistoryRecordDto> findLatestVarByTaskIds(@Param("taskIds") List<String> taskIds,
        @Param("varName") String varName);

    /**
     * 根据procInsIds 批量获取 遗留问题的进展详情
     *
     * @param procInsIds procInsIds
     * @param varName varName
     * @return List<ProcessVarHistoryRecordDto>
     */
    List<ProcessVarHistoryRecordDto> findLatestVarByProcInsIds(@Param("procInsIds") List<String> procInsIds,
        @Param("varName") String varName);

    /**
     * 根据taskId 获取单个 局部变量 最新的详情
     *
     * @param taskId taskId
     * @param varName varName
     * @return ProcessVarHistoryRecordDto
     */
    ProcessVarHistoryRecordDto findLatestVarByTaskId(@Param("taskId") String taskId, @Param("varName") String varName);

    /**
     * 根据procInsId 获取单个 实例变量 最新的详情
     *
     * @param procInsId procInsId
     * @param varName varName
     * @return ProcessVarHistoryRecordDto
     */
    ProcessVarHistoryRecordDto findLatestVarByProcInsId(@Param("procInsId") String procInsId,
        @Param("varName") String varName);
}

 

dao层 xml映射 获取流程历史记录的变量信息 

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.service.dao.mapper.process.ProcessVarHistoryMapper">
    <select id="findVarHistoryByTaskId" resultType="com.domain.dto.process.ProcessVarHistoryRecordDto">
        select
            TASK_ID_ as taskId,
            PROC_INST_ID_ as procInsId,
            NAME_ as varName,
            VAR_TYPE_ as varType,
            TIME_ as operateDate,
            TEXT_ as varContent
        from act_hi_detail
        where TASK_ID_ = #{taskId} and NAME_ =#{varName}
          and TYPE_ = 'VariableUpdate'
        order by TIME_ asc
    </select>

    <select id="findVarHistoryByProcInsId" resultType="com.domain.dto.process.ProcessVarHistoryRecordDto">
        select
            TASK_ID_ as taskId,
            PROC_INST_ID_ as procInsId,
            NAME_ as varName,
            VAR_TYPE_ as varType,
            TIME_ as operateDate,
            TEXT_ as varContent
        from act_hi_detail
        where PROC_INST_ID_ = #{procInsId} and NAME_ =#{varName}
          and TYPE_ = 'VariableUpdate'
        order by TIME_ asc
    </select>

    <select id="findLatestVarByTaskIds" resultType="com.domain.dto.process.ProcessVarHistoryRecordDto">
        select
            a.TASK_ID_ as taskId,
            a.PROC_INST_ID_ as procInsId,
            a.NAME_ as varName,
            a.VAR_TYPE_ as varType,
            a.TIME_ as operateDate,
            a.TEXT_ as varContent
        from (
                select *,
                       row_number() over(partition by TASK_ID_ order by TIME_ desc) as rank_number
                from act_hi_detail
                <where>
                    <if test="taskIds != null and taskIds.size > 0">
                        and TASK_ID_ in
                        <foreach collection="taskIds" item="taskId" open="(" separator="," close=")">
                            #{taskId}
                        </foreach>
                    </if>
                    and NAME_ =#{varName}
                    and TYPE_ = 'VariableUpdate'
                </where>
        ) a
        where rank_number = 1
    </select>

    <select id="findLatestVarByProcInsIds" resultType="com.domain.dto.process.ProcessVarHistoryRecordDto">
        select
            a.TASK_ID_ as taskId,
            a.PROC_INST_ID_ as procInsId,
            a.NAME_ as varName,
            a.VAR_TYPE_ as varType,
            a.TIME_ as operateDate,
            a.TEXT_ as varContent
        from (
                select *,
                    row_number() over(partition by PROC_INST_ID_ order by TIME_ desc) as rank_number
                from act_hi_detail
                <where>
                    <if test="procInsIds != null and procInsIds.size > 0">
                        and PROC_INST_ID_ in
                        <foreach collection="procInsIds" item="procInstId" open="(" separator="," close=")">
                            #{procInstId}
                        </foreach>
                    </if>
                    and NAME_ =#{varName}
                    and TYPE_ = 'VariableUpdate'
                </where>
        ) a
        where rank_number = 1
    </select>


    <select id="findLatestVarByTaskId" resultType="com.domain.dto.process.ProcessVarHistoryRecordDto">
        select
            a.TASK_ID_ as taskId,
            a.PROC_INST_ID_ as procInsId,
            a.NAME_ as varName,
            a.VAR_TYPE_ as varType,
            a.TIME_ as operateDate,
            a.TEXT_ as varContent
        from (
        select *,
        row_number() over(partition by TASK_ID_ order by TIME_ desc) as rank_number
        from act_hi_detail
        <where>
            and TASK_ID_ = #{taskId}
            and NAME_ =#{varName}
            and TYPE_ = 'VariableUpdate'
        </where>
        ) a
        where rank_number = 1
    </select>

    <select id="findLatestVarByProcInsId" resultType="com.domain.dto.process.ProcessVarHistoryRecordDto">
        select
        a.TASK_ID_ as taskId,
        a.PROC_INST_ID_ as procInsId,
        a.NAME_ as varName,
        a.VAR_TYPE_ as varType,
        a.TIME_ as operateDate,
        a.TEXT_ as varContent
        from (
        select *,
        row_number() over(partition by PROC_INST_ID_ order by TIME_ desc) as rank_number
        from act_hi_detail
        <where>
            and PROC_INST_ID_ = #{procInsId}
            and NAME_ =#{varName}
            and TYPE_ = 'VariableUpdate'
        </where>
        ) a
        where rank_number = 1
    </select>
</mapper>

通用信息设计接口

  • 观察者模式,注入了一个 集合list 目的是后续能得到多个被观察者实现类的内容,之前我们有一篇文章 写导出数据与导出模板用一个接口完成的,就涉及到这个模式设计的运用,利用反射或者每个实现类名去区别不同类方法 【业务功能篇04】Springboot+mybatis-plus+POI模板表头下载以及数据查询下载_studyday1的博客-CSDN博客

 controller层

直接调用mapper层内置的查询接口



package com.controller;

import com.domain.dto.process.ProcessContent;
import com.domain.model.DpProcessMessage;
import com.qualitybigdata.model.ResponseVo;
import com.service.UserService;
import com.service.dao.mapper.DpProcessMessageMapper;
import com.utils.ResponseUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import javax.annotation.Resource;

/**
 * 流程消息接口
 *
 */
@RestController
@RequestMapping("/message")
@Slf4j
public class ProcessMessageController {
    @Autowired
    DpProcessMessageMapper processMessageMapper;

    @Resource
    UserService userService;

    /**
     * getMessage
     *
     * @param pageNo pageNo
     * @param pageSize pageSize
     * @param keyword keyword
     * @return ResponseVo
     */
    @PostMapping("/getMessage")
    public ResponseVo getMessage(@RequestParam Long pageNo, @RequestParam Integer pageSize,
        @RequestBody ProcessContent keyword) {
        return queryMessage(pageNo, pageSize, keyword.getKeyword(),
            wrapper -> wrapper.like(DpProcessMessage::getDealUser, userService.getUser().getAccount()));
    }

    /**
     * getApplication
     *
     * @param pageNo pageNo
     * @param pageSize pageSize
     * @param keyword keyword
     * @return ResponseVo
     */
    @PostMapping("/getApplication")
    public ResponseVo getApplication(@RequestParam Long pageNo, @RequestParam Integer pageSize,
        @RequestBody ProcessContent keyword) {
        return queryMessage(pageNo, pageSize, keyword.getKeyword(),
            wrapper -> wrapper.like(DpProcessMessage::getCreateUser, userService.getUser().getAccount()));
    }

    /**
     * getApplication
     *
     * @param keyword keyword
     * @param processType processType
     * @return ResponseVo
     */
    @GetMapping("/getComponentCode")
    public ResponseVo getApplication(@RequestParam String keyword, @RequestParam String processType) {
        Page<DpProcessMessage> page = new Page<>(1, 5);
        LambdaQueryWrapper<DpProcessMessage> wrapper = new LambdaQueryWrapper<>();
        if (ObjectUtils.isNotEmpty(keyword)) {
            wrapper.like(DpProcessMessage::getMessage, keyword);
        }
        if ("getMessage".equals(processType)) {
            wrapper.like(DpProcessMessage::getDealUser, userService.getUser().getAccount());
        } else {
            wrapper.like(DpProcessMessage::getCreateUser, userService.getUser().getAccount());
        }
        wrapper.orderByDesc(DpProcessMessage::getProcessCode);
        List<String> list = processMessageMapper.selectPage(page, wrapper)
            .getRecords()
            .stream()
            .map(DpProcessMessage::getMessage)
            .collect(Collectors.toList());
        return ResponseUtils.successResponse(list, "");
    }

    /**
     * getApplicationNum
     *
     * @return ResponseVo
     */
    @GetMapping("/getApplicationNum")
    public ResponseVo getApplicationNum() {
        String Account = userService.getUser().getAccount();
        return ResponseUtils.successResponse(new Long[] {
            countMessage(wrapper -> wrapper.like(DpProcessMessage::getDealUser, Account)),
            countMessage(wrapper -> wrapper.like(DpProcessMessage::getCreateUser, Account))
        }, "");
    }

    /**
     * countMessage
     *
     * @param consumer consumer
     * @return Long
     */
    private Long countMessage(Consumer<LambdaQueryChainWrapper<DpProcessMessage>> consumer) {
        LambdaQueryChainWrapper<DpProcessMessage> wrapper = new LambdaQueryChainWrapper<>(processMessageMapper);
        consumer.accept(wrapper);
        return wrapper.count();
    }

    /**
     * queryMessage
     *
     * @param pageNo pageNo
     * @param pageSize pageSize
     * @param keyWord keyWord
     * @param consumer consumer
     * @return ResponseVo
     */
    private ResponseVo queryMessage(Long pageNo, Integer pageSize, String keyWord,
        Consumer<LambdaQueryWrapper<DpProcessMessage>> consumer) {
        Page<DpProcessMessage> page = new Page<>(pageNo, pageSize);
        LambdaQueryWrapper<DpProcessMessage> wrapper = new LambdaQueryWrapper<>();
        consumer.accept(wrapper);
        if (ObjectUtils.isNotEmpty(keyWord)) {
            wrapper.like(DpProcessMessage::getMessage, keyWord);
        }
        wrapper.orderByDesc(DpProcessMessage::getProcessCode);
        return ResponseUtils.successPageResponse(processMessageMapper.selectPage(page, wrapper).getRecords(), page);
    }
}

通用信息类 



package com.service.process;

import com.domain.model.DpProcessMessage;
import com.observe.ProcessObserve;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.List;


public abstract class ProcessMessage {
    @Value("${Url}")
    private String Url;

	
    @Autowired
    List<ProcessObserve> observeList;


    /**
     * sendMessage
     *
     * @param message message
     */
    protected void sendMessage(DpProcessMessage message) {
        observeList.forEach(observe -> observe.saveMessage(message));
    }


    /**
     * deleteMessage
     *
     * @param processCode processCode
     */
    protected void deleteMessage(String processCode) {
        observeList.forEach(observe -> observe.removeMessage(processCode));
    }
}

观察者接口


package comobserve;

import com.domain.model.DpProcessMessage;


public interface ProcessObserve {

    /**
     * saveMessage
     * 
     * @param message message
     */
    void saveMessage(DpProcessMessage message);


    /**
     * removeMessage
     * 
     * @param processCode processCode
     */
    void removeMessage(String processCode);
}

观察者 实现类



package com.observe;

import com.service.dao.mapper.DpProcessMessageMapper;
import com.domain.model.DpProcessMessage;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;


@Component
public class ProcessMessageImpl implements ProcessObserve {

    @Autowired
    DpProcessMessageMapper processMessageMapper;


    /**
     * saveMessage
     * 
     * @param message message
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveMessage(DpProcessMessage message) {
        deleteByProcessCode(message.getProcessCode());
        processMessageMapper.insert(message);
    }


    /**
     * removeMessage
     * 
     * @param processCode processCode
     */
    @Override
    public void removeMessage(String processCode) {
        deleteByProcessCode(processCode);
    }


    /**
     * deleteByProcessCode
     * 
     * @param processCode processCode
     */
    private void deleteByProcessCode(String processCode) {
        QueryWrapper<DpProcessMessage> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(DpProcessMessage::getProcessCode, processCode);
        processMessageMapper.delete(wrapper);
    }
}

观察者 mapper接口



package com.service.dao.mapper;

import com.domain.model.DpProcessMessage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;


@Mapper
public interface DpProcessMessageMapper extends BaseMapper<DpProcessMessage> {

}

通用信息 实体类



package com.domain.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;


@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@NoArgsConstructor
@TableName("dp_process_message")
public class DpProcessMessage implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 流程类型
     */
    @TableField("process_type")
    private String processType;

    /**
     * 流程单号
     */
    @TableField("process_code")
    private String processCode;

    /**
     * 流程跳转url
     */
    @TableField("process_url")
    private String processUrl;

    /**
     * 流程信息
     */
    @TableField("message")
    private String message;

    /**
     * 流程状态
     */
    @TableField("status")
    private String status;

    /**
     * 创建人
     */
    @TableField("create_user")
    private String createUser;

    /**
     * 处理人
     */
    @TableField("deal_user")
    private String dealUser;

    @TableField(value = "msg_create_user", exist = false)
    private String msgCreateUser;

    /**
     * 创建时间
     */
    @TableField("create_time")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
}

涉及部分的实体类 

流程常量类 

  • 方便直接调用

package com.constants.process;

/**
 * 流程常量
 *
 */
public class ProcessConstants {

    /**
     * 开始
     */
    public static final String START_EVENT_ID = "start";

    /**
     * 结束
     */
    public static final String END_EVENT_ID = "end";


    /**
     * 遗留问题 流程定义 key
     */
    public static final String KNOWN_ISSUE_PROCESS_DEF_KEY = "knownIssue";

    /**
     * 遗留问题 任务定义 key
     */
    public static final String KNOWN_ISSUE_TASK_DEF_KEY = "knownIssueTaskId";

    /**
     * 遗留问题 任务状态 variables
     */
    public static final String KNOWN_ISSUE_TASK_STATE_VAR = "knownIssueState";

    /**
     * 遗留问题 进展 变量名
     */
    public static final String KNOWN_ISSUE_PROGRESS_VAR = "PROGRESS_DETAIL";

    /**
     * 遗留问题 更新人
     */
    public static final String KNOWN_ISSUE_PROGRESS_UPDATER = "UPDATER";

    /**
     * 遗留问题 更新内容
     */
    public static final String KNOWN_ISSUE_PROGRESS_CONTENT = "CONTENT";


    /**
     * 开启
     */
    public static final String OPEN_STATE = "OPEN";

    /**
     * 关闭
     */
    public static final String CLOSE_STATE = "CLOSE";

    /**
     * 完成
     */
    public static final String FINISHED_STATE = "FINISHED";

    /**
     * 取消
     */
    public static final String CANCELED_STATE = "CANCELED";

}

 文章来源地址https://www.toymoban.com/news/detail-522346.html

业务流程表单实体类



package com.domain.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;


@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("dp_process_known_issue_details")
public class KnownIssueDetailsTab {

    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    private String id;

    @TableField("proc_ins_id")
    private String processInstanceId;

    @TableField("task_id")
    private String taskId;

    @TableField("handler")
    private String handler;

    @TableField("issue_desc")
    private String issueDesc;

    @TableField("plan_finished_date")
    private Date planFinishedDate;

    @TableField("`label`")
    private String label;

    @TableField("pdu_name")
    private String pduName;

    @TableField("created_date")
    private Date createdDate;
}

状态变量 枚举类



package com.enums;

import lombok.Getter;


public enum State {

    OPEN(0, "进行中", "open"),
    CLOSE(1, "已关闭", "close"),
    FINISHED(2, "已完成", "finished"),
    CANCELED(3, "已撤销", "canceled"),
    UNDO(4, "未开始", "undo"),

    REJECTED(5, "被驳回", "rejected"),

    DRAFT(6, "草稿", "draft");

    @Getter
    private final Integer code;

    @Getter
    private final String nameCn;

    @Getter
    private final String nameEn;

    State(Integer code, String nameCn, String nameEn) {
        this.code = code;
        this.nameCn = nameCn;
        this.nameEn = nameEn;
    }
}

创建遗留问题单 传参类

package com.model;

import java.util.*;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;




 @JsonIgnoreProperties(ignoreUnknown = true)

public class KnowIssueCreateParam implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @JsonProperty("handlers")
    private List<String> handlers = new ArrayList<String>();
    
    @JsonProperty("issueDesc")
    private String issueDesc = null;
    
    @JsonProperty("planFinishedDate")
    private String planFinishedDate = null;
    
    @JsonProperty("label")
    private String label = null;
    
    @JsonProperty("pduName")
    private String pduName = null;
    
    @JsonProperty("problemNo")
    private String problemNo = null;
    
    @JsonProperty("handler")
    private String handler = null;
    
    public KnowIssueCreateParam() {
        super();
    } 
    
    /**
    **/
    public List<String> getHandlers() {
        return handlers;
    }

    public void setHandlers(List<String> handlers) {
        this.handlers = handlers;
    }
    
    /**
    **/
    public String getIssueDesc() {
        return issueDesc;
    }

    public void setIssueDesc(String issueDesc) {
        this.issueDesc = issueDesc;
    }
    
    /**
    **/
    public String getPlanFinishedDate() {
        return planFinishedDate;
    }

    public void setPlanFinishedDate(String planFinishedDate) {
        this.planFinishedDate = planFinishedDate;
    }
    
    /**
    **/
    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }
    
    /**
    **/
    public String getPduName() {
        return pduName;
    }

    public void setPduName(String pduName) {
        this.pduName = pduName;
    }
    
    /**
    **/
    public String getProblemNo() {
        return problemNo;
    }

    public void setProblemNo(String problemNo) {
        this.problemNo = problemNo;
    }
    
    /**
    **/
    public String getHandler() {
        return handler;
    }

    public void setHandler(String handler) {
        this.handler = handler;
    }
    
      
    
      
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        
        
        KnowIssueCreateParam knowIssueCreateParam = (KnowIssueCreateParam) o;
        return Objects.equals(this.handlers,
                knowIssueCreateParam.handlers)
                && Objects.equals(this.issueDesc,
                knowIssueCreateParam.issueDesc)
                && Objects.equals(this.planFinishedDate,
                knowIssueCreateParam.planFinishedDate)
                && Objects.equals(this.label,
                knowIssueCreateParam.label)
                && Objects.equals(this.pduName,
                knowIssueCreateParam.pduName)
                && Objects.equals(this.problemNo,
                knowIssueCreateParam.problemNo)
                && Objects.equals(this.handler,
                knowIssueCreateParam.handler);
    } 
    
      
    @Override
    public int hashCode() {
        return Objects.hash(handlers
                , issueDesc
                , planFinishedDate
                , label
                , pduName
                , problemNo
                , handler);
    } 
    
       

      
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("class KnowIssueCreateParam { ");
        
        sb.append("  handlers: ").append(handlers).append(", ");
        sb.append("  issueDesc: ").append(issueDesc).append(", ");
        sb.append("  planFinishedDate: ").append(planFinishedDate).append(", ");
        sb.append("  label: ").append(label).append(", ");
        sb.append("  pduName: ").append(pduName).append(", ");
        sb.append("  problemNo: ").append(problemNo).append(", ");
        sb.append("  handler: ").append(handler).append(", ");
        sb.append("} ");
        return sb.toString();
      }
}

流程变量封装类



package com.domain.dto.process;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;


@AllArgsConstructor
@NoArgsConstructor
@Data
public class ProcessVarHistoryRecordDto {
    private String taskId;

    private String procInsId;

    private String varName;

    private String varType;

    private String varContent;

    private Date operateDate;
}

到了这里,关于【业务功能篇36】Springboot+activiti7 工作流引擎的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Activiti7 工作流非原流程终止

    正常工作流,需要经过 node1、node2 才能结束。 现在要求已经开启的流程,目前停留在 node1,可以提前终止。 一般根据实际需要,可以有几种做法: 新绘制流程图,新增 node1 结束的流程分支,替换原流程 SQL 的方式,将该流程的数据,手动修改为终止的状态 代码动态修改流程

    2023年04月16日
    浏览(26)
  • Activiti7工作流引擎:在线流程编辑器Activiti Modoler5.x

    有的时候我们的流程图需要业务人员自己绘制,然后使用自己绘制的流程图,此时就需要一个在线流程图编辑器需要集成到我们的web系统中。Activiti Modoler是Activiti官方推出的在线流程编辑器。 https://github.com/Activiti/Activiti/tree/5.x 将整个项目下载下来。不同版本的目录结构区别

    2024年02月09日
    浏览(40)
  • Springboot集成工作流Activity

    介绍 官网:https://www.activiti.org/ 一 、工作流介绍 1.工作流(workflow) 就是通过计算机对业务流程自动化执行管理,它主要解决的是“使在多个参与这之间按照某种预定义规则自动化进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使目标的实现。”

    2023年04月13日
    浏览(38)
  • springboot项目集成activiti工作流引擎

    一、一种较为简单,只需要使用idea的插件来画流程图。(我所使用的的插件是Activiti BPMN visualizer) 二、另一种就是可以使用(在线流程设计器)或者(页面设计器部署到自己项目中)来画流程图。 本文是将流程设计页面部署到了项目中 一、使用idea的插件来画流程图。  二

    2024年02月10日
    浏览(31)
  • 若依框架SpringBoot+Activiti工作流的使用

    使用简介:本技术点主要是针对类审批的业务流程的建模,可以有:任务发布(即流程开始)到一级一级的审批到最终结束(即流程结束)一整套完备的模型 1、idea下载activiti插件 ider以前版本下载actiBPM,但是新版ider这个插件已经被淘汰,已经被下面这个替代     2、单独起

    2024年02月11日
    浏览(30)
  • SpringBoot整合Activiti实现工作流的低代码系统(附源码+文档)

    activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。 springboot+vue+activiti集成了activiti在线编辑器,流行的前后端分离部署开发模式,快速开发平台,可插拔工作流服务。工作流表单

    2024年04月09日
    浏览(34)
  • SpringBoot整合Activiti实现工作流的低代码系统(附源码和配套文档)

    activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。 springboot+vue+activiti集成了activiti在线编辑器,流行的前后端分离部署开发模式,快速开发平台,可插拔工作流服务。工作流表单

    2024年03月15日
    浏览(56)
  • 模仿Activiti工作流自动建表机制,实现Springboot项目启动后自动创建多表关联的数据库与表的方案

    文/朱季谦 熬夜写完,尚有不足,但仍在努力学习与总结中,而您的点赞与关注,是对我最大的鼓励! 在一些本地化项目开发当中,存在这样一种需求,即开发完成的项目,在第一次部署启动时,需能自行构建系统需要的数据库及其对应的数据库表。 若要解决这类需求,其实

    2024年01月24日
    浏览(45)
  • Activity工作流引擎

    目录 一、了解工作流 1、什么是工作流 2、工作流引擎 3、常见工作流引擎 4、Activiti7概述 4.1、Activiti介绍 4.2、建模语言BPMN 4.3、Activiti使用流程 二、Activiti7 1、Activiti使用 1.1、数据库支持 1.2、Activiti环境 1.3、Activiti常用Service服务接口 1.4、流程设计工具 2、Activiti流程操作 2.1、

    2024年02月13日
    浏览(32)
  • Activiti 工作流简介

    1、什么是工作流         工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。 1.2、工作流系统   

    2024年02月04日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包