springboot 整合 activiti工作流 学习

springboot + activiti工作流 学习

第一步:

         引入依赖:
            <!-- activiti -->
             	<dependency>
	             	<groupId>org.activiti</groupId>
		            <artifactId>activiti-spring-boot-starter-basic</artifactId>
		            <version>6.0.0</version>
	            </dependency>
	        <!--整合activiti的 druid 要求的版本为0.2.16 或 1.0.9,百度上说是官方的bug-->
            	<dependency>
	            	<groupId>com.alibaba</groupId>
		           <artifactId>druid</artifactId>
	            	<version>0.2.16</version>
	            </dependency>

第二步:application.yml文件配置

       # activiti default configuration
       #在activiti的默认配置中,process-definition-location-prefix 是指定activiti流程描述文件的前缀(即路径),
       #启动时,activiti就会去寻找此路径下的流程描述文件,并且自动部署;suffix 是一个String数组,表示描述文件的默    认后缀名,默认以上两种。
       activiti:
         database-schema-update: true #是否每次都更新数据库
         # 自动部署验证设置:true-开启(默认)、false-关闭
         check-process-definitions: true # activti是否自动部署
         process-definition-location-prefix: classpath:/processes/
         #process-definition-location-suffixes: .bpmn
         history-level: full
         #db-history-used: true
         db-identity-used: false

第三步:建processes文件夹

在这里插入图片描述

id要唯一
在这里插入图片描述

activiti工作流6大service服务的简单介绍

https://blog.csdn.net/a8532156/article/details/78427545

创建activiti提供的各种服务 ActivitiConfig

package com.shiro_learning.demo.config.activiti;

import org.activiti.engine.*;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;

     /**
           创建activiti提供的各种服务
      */

 @Configuration
public class ActivitiConfig{
private Logger logger = LoggerFactory.getLogger(ActivitiConfig.class);

@Value("${spring.activiti.database-schema-update}")
private String databaseSchemaUpdate;

@Value("${spring.activiti.db-identity-used}")
private boolean dbIdentityUsed;

@Value("${spring.datasource.url}")
private String dbUrl;

@Bean
public ProcessEngine processEngine(DataSourceTransactionManager transactionManager, DataSource dataSource) throws IOException {
    logger.info("==========activiti=======开始==============");
    SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();

    /*  自动部署已有的流程文件
    作用相当于 (据bpmn文件部署流程repositoryService.createDeployment().addClasspathResource("singleAssignee.bpmn").deploy();)*/

    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceLoader.CLASSPATH_URL_PREFIX + "processes/*.bpmn");
    configuration.setTransactionManager(transactionManager);
    //设置数据源
    configuration.setDataSource(dataSource);
    //是否每次都更新数据库
    //configuration.setDatabaseSchemaUpdate(databaseSchemaUpdate);
    configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
    //configuration.setAsyncExecutorActivate(false);
    configuration.setDeploymentResources(resources);
    //设置是否使用activti自带的用户体系
    configuration.setDbIdentityUsed(dbIdentityUsed);
    return configuration.buildProcessEngine();
}


/**
 * 工作流仓储服务
 * @param processEngine
 * @return
 */

@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
    return processEngine.getRepositoryService();
}


/**
 * 工作流运行服务
 * @param processEngine
 * @return
 */

@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
    return processEngine.getRuntimeService();
}

/**
 * 工作流任务服务
 * @param processEngine
 * @return
 */

@Bean
public TaskService taskService(ProcessEngine processEngine) {
    return processEngine.getTaskService();
}


/**
 * 工作流历史数据服务
 * @param processEngine
 * @return
 */

@Bean
public HistoryService historyService(ProcessEngine processEngine) {
    return processEngine.getHistoryService();
}


/**
 * 工作流管理服务
 * @param processEngine
 * @return
 */

@Bean
public ManagementService managementService(ProcessEngine processEngine) {
    return processEngine.getManagementService();
}


/**
 * 工作流唯一服务
 * @param processEngine
 * @return
 */

@Bean
public IdentityService identityService(ProcessEngine processEngine) {
    return processEngine.getIdentityService();
   }
}

实体类:ActivitiTask,Activiti 业务类:ActivitiServiceImpl 和 业务接口:ActivitiService

package com.shiro_learning.demo.entity;

import java.util.Date;

public class ActivitiTask {

    private String id;
    private String name;
    private Activiti activiti;
    private Date createTime;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Activiti getActiviti() {
        return activiti;
    }

    public void setActiviti(Activiti activiti) {
        this.activiti = activiti;
    }
}

package com.shiro_learning.demo.entity;

import java.io.Serializable;
import java.util.Date;

public class Activiti implements Serializable{

/**
 * 申请人
 */
private String applyUser;
private int days;
private String reason;
private Date applyTime;
private String applyStatus;

/**
 * 审核人
 */
private String auditor;
private String result;
private Date auditTime;

public String getApplyUser() {
    return applyUser;
}

public void setApplyUser(String applyUser) {
    this.applyUser = applyUser;
}

public int getDays() {
    return days;
}

public void setDays(int days) {
    this.days = days;
}

public String getReason() {
    return reason;
}

public void setReason(String reason) {
    this.reason = reason;
}

public Date getApplyTime() {
    return applyTime;
}

public void setApplyTime(Date applyTime) {
    this.applyTime = applyTime;
}

public String getApplyStatus() {
    return applyStatus;
}

public void setApplyStatus(String applyStatus) {
    this.applyStatus = applyStatus;
}

public String getAuditor() {
    return auditor;
}

public void setAuditor(String auditor) {
    this.auditor = auditor;
}

public String getResult() {
    return result;
}

public void setResult(String result) {
    this.result = result;
}

public Date getAuditTime() {
    return auditTime;
}

public void setAuditTime(Date auditTime) {
    this.auditTime = auditTime;
}

}

在这里插入代码片
package com.shiro_learning.demo.service;

import com.shiro_learning.demo.entity.Activiti;
import com.shiro_learning.demo.entity.ActivitiTask;
import java.util.List;

public interface ActivitiService {

public Boolean startActiviti(Activiti vac, String userName);

public List<Activiti> myActiviti(String userName);

public List<ActivitiTask> myApproval(String userName);

public Boolean passApproval(String userName, ActivitiTask activitiTask);

public List<Activiti> myActivitiRecord(String userName);

public List<Activiti> myApprovalRecord(String userName);

}


 package com.shiro_learning.demo.service.impl;

 import com.shiro_learning.demo.util.ActivitiUtil;
 import com.shiro_learning.demo.entity.Activiti;
 import com.shiro_learning.demo.entity.ActivitiTask;
 import com.shiro_learning.demo.service.ActivitiService;
 import org.activiti.engine.*;
 import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.history.HistoricVariableInstance;
 import org.activiti.engine.runtime.ProcessInstance;
 import org.activiti.engine.task.Task;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 import javax.annotation.Resource;
 import java.util.*;

 /**
   RepositoryService:  流程仓库Service,用于管理流程仓库,例如:部署,删除,读取流程资源

   IdentityService:身份Service,可以管理,查询用户,组之间的关系

   RuntimeService:运行时Service,可以处理所有正在运行状态的流程实例,任务等

   TaskService:任务Service,用于管理,查询任务,例如:签收,办理,指派等

   HistoryService:历史Service,可以查询所有历史数据,例如:流程实例,任务,活动,变量,附件等

   FormService:表单Service,用于读取和流程,任务相关的表单数据

   ManagementService:引擎管理Service,和具体业务无关,主要是可以查询引擎配置,数据库,作业等

   DynamicBpmnService:一个新增的服务,用于动态修改流程中的一些参数信息等,是引擎中的一个辅助的服务
  */
  @Service
  public class ActivitiServiceImpl implements ActivitiService {
  private Logger logger = LoggerFactory.getLogger(ActivitiServiceImpl.class);
   //所运行工作流的名字
   //private static final String PROCESS_DEFINE_KEY = "test.bpmn";
   private static final String PROCESS_DEFINE_KEY = "demo2";
   private static final String NEXT_ASSIGNEE = "huangxu2";
   @Resource
   private RuntimeService runtimeService;
   @Resource
   private IdentityService identityService;
   @Resource
   private TaskService taskService;
   @Resource
   private HistoryService historyService;
   @Resource
   private RepositoryService repositoryService;

/* //工作流运行服务
@Autowired
private RuntimeService runtimeService;

//工作流唯一服务
@Autowired
private IdentityService identityService;

//工作流任务服务
@Autowired
private TaskService taskService;

//工作流管理服务
@Autowired
private HistoryService historyService;
@Autowired
private RepositoryService repositoryService;
  */
/**
 * 开始流程
 * @param vac
 * @param userName
 * @return
 */
@Override
public Boolean startActiviti(Activiti vac, String userName) {
    logger.info("method startActivityDemo begin....");
    /*认证用户的作用是设置流程发起人:在流程开始之前设置,会自动在表ACT_HI_PROCINST 中的START_USER_ID_中设置用户ID
    用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中*/
    try{
        identityService.setAuthenticatedUserId(userName);
        // 开始流程
        ProcessInstance pi = runtimeService.startProcessInstanceByKey(PROCESS_DEFINE_KEY);
        String processId = pi.getId();
        logger.info("===============processId==================="+processId);

        // 查询当前任务
        Task currentTask = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        String taskId1 = currentTask.getId();
        logger.info("===============taskId1==================="+taskId1);

        // 申明任务人
        //taskService.claim(currentTask.getId(), userName);
        taskService.setAssignee(taskId1, userName);
        Map<String, Object> vars = new HashMap<>(4);
        vars.put("applyUser", userName);
        vars.put("days", vac.getDays());
        vars.put("reason", vac.getReason());
        //在此方法中,Vaction 是申请时的具体信息,在完成“申请请假”任务时,可以将这些信息设置成参数。
        //完成第一步申请
        taskService.complete(currentTask.getId(), vars);

        // 到了下一个任务, 应该在此处指派任务由谁来处理
        // 重新获取当前任务
        Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
        String taskId2 = task.getId();
        logger.info("===============taskId2==================="+taskId2);
        taskService.setAssignee(taskId2, NEXT_ASSIGNEE);
    }catch (Exception e){
        e.printStackTrace();
    }
    return true;
}


@Override
public List<Activiti> myActiviti(String userName) {
    List<ProcessInstance> instanceList = runtimeService.createProcessInstanceQuery().startedBy(userName).list();
    List<Activiti> activitisList = new ArrayList<>();
    for (ProcessInstance instance : instanceList) {
        Activiti activiti = getActiviti(instance);
        activitisList.add(activiti);
    }
    return activitisList;
}


/**
 * 查询需要自己审批
 * @param userName
 * @return
 */
@Override
public List<ActivitiTask> myApproval(String userName) {
    userName = "huangxu2";
    List<Task> taskList = taskService.createTaskQuery().taskAssignee(userName)
            .orderByTaskCreateTime().desc().list();
            // 多此一举 taskList中包含了以下内容(用户的任务中包含了所在用户组的任务)
            //        Group group = identityService.createGroupQuery().groupMember(userName).singleResult();
            //        List<Task> list = taskService.createTaskQuery().taskCandidateGroup(group.getId()).list();
            //        taskList.addAll(list);
    List<ActivitiTask> activitiTaskList = new ArrayList<>();
    for (Task task : taskList) {
        ActivitiTask activitiTask = new ActivitiTask();
        activitiTask.setId(task.getId());
        activitiTask.setName(task.getName());
        activitiTask.setCreateTime(task.getCreateTime());
        String instanceId = task.getProcessInstanceId();
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        Activiti activiti = getActiviti(instance);
        activitiTask.setActiviti(activiti);
        activitiTaskList.add(activitiTask);
    }
    return activitiTaskList;
}


private Activiti getActiviti(ProcessInstance instance) {
    Integer days = runtimeService.getVariable(instance.getId(), "days", Integer.class);
    String reason = runtimeService.getVariable(instance.getId(), "reason", String.class);
    Activiti activiti = new Activiti();
    activiti.setApplyUser(instance.getStartUserId());
    activiti.setDays(days);
    activiti.setReason(reason);
    Date startTime = instance.getStartTime(); // activiti 6 才有
    activiti.setApplyTime(startTime);
    activiti.setApplyStatus(instance.isEnded() ? "申请结束" : "等待审批");
    return activiti;
}


/**
 * Activiti任务认领
 * taskService.setAssignee(String taskId, String userId);
 * taskService.claim(String taskId, String userId);
 * taskService.setOwner(String taskId, String userId);
 * setAssignee和claim两个的区别是在认领任务时,claim会检查该任务是否已经被认领,如果被认领则会抛出ActivitiTaskAlreadyClaimedException ,而setAssignee不会进行这样的检查,其他方面两个方法效果一致。
 * setOwner和setAssignee的区别在于,setOwner是在代理任务时使用,代表着任务的归属者,而这时,setAssignee代表的是代理办理者,
 *
 * 举个例子来说,公司总经理现在有个任务taskA,去核实一下本年度的财务报表,他现在又很忙没时间,于是将该任务委托给其助理进行办理,此时,就应该这么做
 * taskService.setOwner(taskA.getId(), 总经理.getId());
 * taskService.setAssignee/claim(taskA.getId(), 助理.getId());
 */
/**
 * 审批操作
 * @param userName
 * @param activitiTask
 * @return
 */
/**
 * 同理,result是审批的结果,也是在完成审批任务时需要传入的参数;taskId是刚才老板查询到的当前需要自己完成的审批任务ID。
 * (如果流程在这里设置分支,可以通过判断result的值来跳转到不同的任务)
 */
@Override
public Boolean passApproval(String userName, ActivitiTask activitiTask) {
    userName = "huangxu2";
    String taskId = activitiTask.getId();
    String result = activitiTask.getActiviti().getResult();
    Map<String, Object> vars = new HashMap<>();
    vars.put("result", result);
    vars.put("auditor", userName);
    vars.put("auditTime", new Date());
    //taskService.claim(taskId, userName);
    taskService.setAssignee(taskId, userName);
    taskService.complete(taskId, vars);
    return true;
}


/**
 * 查询已完成的请假记录
 * 由于已完成的请假在数据库runtime表中查不到(runtime表只保存正在进行的流程示例信息),所以需要在history表中查询。
 * @param userName
 * @return
 */
@Override
public List<Activiti> myActivitiRecord(String userName) {
    List<HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
            .processDefinitionKey(PROCESS_DEFINE_KEY).startedBy(userName).finished()
            .orderByProcessInstanceEndTime().desc().list();

    List<Activiti> activitiList = new ArrayList<>();
    for (HistoricProcessInstance hisInstance : hisProInstance) {
        Activiti activiti = new Activiti();
        activiti.setApplyUser(hisInstance.getStartUserId());
        activiti.setApplyTime(hisInstance.getStartTime());
        activiti.setApplyStatus("申请结束");
        List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(hisInstance.getId()).list();
        ActivitiUtil.setVars(activiti, varInstanceList);
        activitiList.add(activiti);
    }
    return activitiList;
}


/**
 * 我审批的记录列表
 * @param userName
 * @return
 */
@Override
public List<Activiti> myApprovalRecord(String userName) {
    userName = "huangxu2";
    List<HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
            .processDefinitionKey(PROCESS_DEFINE_KEY).involvedUser(userName).finished()
            .orderByProcessInstanceEndTime().desc().list();

    List<String> auditTaskNameList = new ArrayList<>();
    auditTaskNameList.add("经理审批");
    auditTaskNameList.add("总监审批");
    List<Activiti> activitiList = new ArrayList<>();
    for (HistoricProcessInstance hisInstance : hisProInstance) {
        /*List<HistoricTaskInstance> hisTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(hisInstance.getId()).processFinished()
                .taskAssignee(userName)
                .taskNameIn(auditTaskNameList)
                .orderByHistoricTaskInstanceEndTime().desc().list();
        boolean isMyAudit = false;
        for (HistoricTaskInstance taskInstance : hisTaskInstanceList) {
            if (taskInstance.getAssignee().equals(userName)) {
                isMyAudit = true;
            }
        }
        if (!isMyAudit) {
            continue;
        }*/
        Activiti activiti = new Activiti();
        activiti.setApplyUser(hisInstance.getStartUserId());
        activiti.setApplyStatus("申请结束");
        activiti.setApplyTime(hisInstance.getStartTime());
        List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(hisInstance.getId()).list();
        ActivitiUtil.setVars(activiti, varInstanceList);
        activitiList.add(activiti);
    }
    return activitiList;
  }
}

工具类:ActivitiUtil

 package com.shiro_learning.demo.util;

 import org.activiti.engine.history.HistoricVariableInstance;
 import java.lang.reflect.Field;
 import java.util.List;

public class ActivitiUtil {

/**
 * 将历史参数列表设置到实体中去
 * @param entity 实体
 * @param varInstanceList 历史参数列表
 */
public static <T> void setVars(T entity, List<HistoricVariableInstance> varInstanceList) {
    Class<?> tClass = entity.getClass();
    try {
        for (HistoricVariableInstance varInstance : varInstanceList) {
            Field field = tClass.getDeclaredField(varInstance.getVariableName());
            if (field == null) {
                continue;
            }
            field.setAccessible(true);
            field.set(entity, varInstance.getValue());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
}

页面展示: 丑得我都看不下去了

在这里插入图片描述

注:

activiti 插件安装:https://blog.csdn.net/gozhuyinglong/article/details/80336765
activiti 乱码处理: http://www.cnblogs.com/mymelody/p/6049291.html
activiti 任务认领: https://www.cnblogs.com/boulder/p/3658528.html
activiti 报错 SQL语句报com.alibaba.druid.sql.parser.ParserException: TODO IDENTIFIER cross
处理方法:https://www.cnblogs.com/zhuangjixiang/p/4147802.html
https://www.oschina.net/question/253454_107787
整合activiti的 druid 要求的版本为0.2.16 或 1.0.9
activiti工作流6大service服务的简单介绍:https://blog.csdn.net/a8532156/article/details/78427545

发布了33 篇原创文章 · 获赞 42 · 访问量 3181

猜你喜欢

转载自blog.csdn.net/weixin_40991408/article/details/89027783