一、Activiti 的 25 张表的两种创建方式

/**
     * 生成Activiti需要的25表
     */
    @Test
    public void testCreateTable(){
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); // 获取流程引擎配置
        pec.setJdbcDriver("com.mysql.jdbc.Driver"); // 配置驱动
        pec.setJdbcUrl("jdbc:mysql://localhost:3306/db_activiti"); // 配置连接地址
        pec.setJdbcUsername("root"); // 用户名
        pec.setJdbcPassword("root"); // 密码
        /**
         * 配置模式  true 自动创建和更新表
         */
        pec.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        
        // 获取流程引擎对象
        ProcessEngine pe=pec.buildProcessEngine(); 
    }
    
    /**
     * 生成Activiti需要的25表 使用配置文件
     */

在resources下新建一个activiti.cfg.xml文件  文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_activiti" />
    <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUsername" value="root" />
    <property name="jdbcPassword" value="root" />
    <property name="databaseSchemaUpdate" value="true" />
  </bean>
</beans>
    @Test
    public void testCreateTableWithXml(){
         // 引擎配置
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        // 获取流程引擎对象
        ProcessEngine processEngine=pec.buildProcessEngine();
    }

二、在 Eclipse 上安装 Activiti 插件

1、 Help → Install New Software→Add.

2、Name:Activiti BPMN 2.0 designer

     Location:http://activiti.org/designer/update/

Activiti 工作流

Activiti 工作流

这个要勾选上  否则创建图片不能保存

三、第一个activiti 程序

public class HelloWorldProcess {

    /**
     * 获取默认流程引擎实例,会自动读取activiti.cfg.xml文件
     */
    private ProcessEngine processEngine=ProcessEngines.getDefaultProcessEngine();
    

部署流程定义


    /**
     * 部署流程定义
     */
    @Test
    public void deploy(){
        Deployment deployment=processEngine.getRepositoryService() // 获取部署相关Service
                .createDeployment() // 创建部署
                .addClasspathResource("diagrams/HelloWorld.bpmn") // 加载资源文件
                .addClasspathResource("diagrams/HelloWorld.png") // 加载资源文件
                .name("HelloWorld流程") // 流程名称
                .deploy(); // 部署
        System.out.println("流程部署ID:"+deployment.getId()); 
        System.out.println("流程部署Name:"+deployment.getName());
    }
    

启动流程实例


    /**
     * 启动流程实例
     */
    @Test
    public void start(){
        ProcessInstance pi=processEngine.getRuntimeService() // 运行时Service

//  .startProcessInstanceByKey("myFirstProcess"  "变量是一个map集合")  流程启动的时候就设置个变量         相 当于用 processEngine.getRuntimeService().setVariable(taskId, "date", new Date()) 相当于给这个流程初始化数据
            .startProcessInstanceByKey("myFirstProcess"); // 流程定义 act_re_procdef表的KEY字段值
        System.out.println("流程实例ID:"+pi.getId());
        System.out.println("流程定义ID:"+pi.getProcessDefinitionId()); 
    }
    

查看任务


    /**
     * 查看任务
     */
    @Test
    public void findTask(){
        List<Task> taskList=processEngine.getTaskService() // 任务相关Service
            .createTaskQuery() // 创建任务查询
            .taskAssignee("java1234_小锋") // 指定某个人  act_ru_task这个表里的 assignee_字段
            .list();
        for(Task task:taskList){
            System.out.println("任务ID:"+task.getId()); 
            System.out.println("任务名称:"+task.getName());
            System.out.println("任务创建时间:"+task.getCreateTime());
            System.out.println("任务委派人:"+task.getAssignee());
            System.out.println("流程实例ID:"+task.getProcessInstanceId());
        }
    }
    

完成任务


    /**
     * 完成任务
     */
    @Test
    public void completeTask(){
        processEngine.getTaskService() // 任务相关Service
            .complete("30004"); //30004表示正在运行的任务id     task.getId()   表里的act_ru_task id_字段
    }

查询流程定义

  

/**
     * 查询流程定义 返回流程定义集合  对应表 act_re_procdef
     */
    @Test
    public void list(){
        String processDefinitionKey="myFirstProcess";
        List<ProcessDefinition> pdList=processEngine.getRepositoryService() // 获取service
            .createProcessDefinitionQuery() // 创建流程定义查询
            .processDefinitionKey(processDefinitionKey) // 通过key查询  act_re_procdef 表里的key_字段
            .list();  // 返回一个集合
        for(ProcessDefinition pd:pdList){
            System.out.println("ID_"+pd.getId());
            System.out.println("NAME_"+pd.getName());
            System.out.println("KEY_"+pd.getKey());
            System.out.println("VERSION_"+pd.getVersion());
            System.out.println("=========");
        }
    }

通过ID查询某个流程定义

 

/**
     * 通过ID查询某个流程定义
     */
    @Test
    public void getById(){
        String processDefinitionId="myFirstProcess:2:7504";
        ProcessDefinition pd=processEngine.getRepositoryService() // 获取service
            .createProcessDefinitionQuery() // 创建流程定义查询
            .processDefinitionId(processDefinitionId) // 通过id查询  act_re_procdef 中的 id_字段
            .singleResult();
        System.out.println("ID_"+pd.getId());
        System.out.println("NAME_"+pd.getName());
        System.out.println("KEY_"+pd.getKey());
        System.out.println("VERSION_"+pd.getVersion());
            
    }

查询最新版本的流程定义

 

//查询最新版本的流程定义
    @Test
    public void queryPng(){
        RepositoryService service = engine.getRepositoryService();
        List<ProcessDefinition> version = service.createProcessDefinitionQuery().latestVersion().list();
        for (ProcessDefinition processDefinition : version) {
            System.out.println(processDefinition.getName());
        }
    }

/**
     * 根据流程部署id和资源文件名称来查询流程图片
     * @throws Exception
     */
    @Test
    public void getImageById()throws Exception{
        InputStream inputStream=processEngine.getRepositoryService() // 获取sevice
            .getResourceAsStream("10001", "helloWorld/HelloWorld.png");
        FileUtils.copyInputStreamToFile(inputStream,new File("c:/helloWorld.png"));
    }

/**
     * 查询最新版本的流程定义
     * @throws Exception
     */
    @Test
    public void listLastVersion()throws Exception{

        List<ProcessDefinition> listAll=processEngine.getRepositoryService() // 获取service
            .createProcessDefinitionQuery() // 创建流程定义查询
            .orderByProcessDefinitionVersion().asc() // 根据流程定义版本升序
            .list();  // 返回一个集合
        
        // 定义有序Map,相同的Key,假如添加map的值  后者的值会覆盖前面相同的key的值
        Map<String,ProcessDefinition> map=new LinkedHashMap<String,ProcessDefinition>();
        // 遍历集合,根据key来覆盖前面的值,来保证最新的key覆盖前面所有老的key的值
        for(ProcessDefinition pd:listAll){
            map.put(pd.getKey(), pd);
        }
        
        List<ProcessDefinition> pdList=new LinkedList<ProcessDefinition>(map.values());
        for(ProcessDefinition pd:pdList){
            System.out.println("ID_"+pd.getId());
            System.out.println("NAME_"+pd.getName());
            System.out.println("KEY_"+pd.getKey());
            System.out.println("VERSION_"+pd.getVersion());
            System.out.println("=========");
        }
    }

删除所有key相同的流程定义

 

/**
     * 删除所有key相同的流程定义
     * @throws Exception
     */
    @Test
    public void deleteByKey()throws Exception{
        String processDefinitionKey="helloWorld2";
        List<ProcessDefinition> pdList=processEngine.getRepositoryService() // 获取service
                .createProcessDefinitionQuery() // 创建流程定义查询
                .processDefinitionKey(processDefinitionKey) // 根据key查询
                .list();  // 返回一个集合
        for(ProcessDefinition pd:pdList){
            processEngine.getRepositoryService()//按照部署流程id删除   true表示级联删除
                .deleteDeployment(pd.getDeploymentId(),true);   act_re_deployment这个表的 id_  
        }

查询流程状态

/**
     * 查询流程状态(正在执行 or 已经执行结束)
     */
    @Test
    public void processState(){
        ProcessInstance pi=processEngine.getRuntimeService() // 获取运行时Service
            .createProcessInstanceQuery() // 创建流程实例查询
            .processInstanceId("35001") // 用流程实例id查询  act_ru_execution 表中的proc_inst_id_
            .singleResult();
        if(pi!=null){
            System.out.println("流程正在执行!");
        }else{
            System.out.println("流程已经执行结束!");
        }
    }

历史任务查询

    /**
     * 历史任务查询  在act_hi_taskinst表里    只有任务的节点 不包括开始节点和结束节点
     */
    @Test
    public void historyTaskList(){
        List<HistoricTaskInstance> list=processEngine.getHistoryService() // 历史相关Service
            .createHistoricTaskInstanceQuery() // 创建历史任务实例查询
            .processInstanceId("35001") // 用流程实例id查询  act_hi_taskinst 表里的proc_inst_id_ 字段
            .finished() // 查询已经完成的任务
            .list(); 
        for(HistoricTaskInstance hti:list){
            System.out.println("任务ID:"+hti.getId());
            System.out.println("流程实例ID:"+hti.getProcessInstanceId());
            System.out.println("任务名称:"+hti.getName());
            System.out.println("办理人:"+hti.getAssignee());
            System.out.println("开始时间:"+hti.getStartTime());
            System.out.println("结束时间:"+hti.getEndTime());
            System.out.println("=================================");
        }
    }

历史活动查询

 

/**
     * 历史活动查询  在act_hi_actinst表里  包括开始节点和结束节点
     */
    @Test
    public void historyActInstanceList(){
        List<HistoricActivityInstance>  list=processEngine.getHistoryService() // 历史相关Service
            .createHistoricActivityInstanceQuery() // 创建历史活动实例查询
            .processInstanceId("35001") // 执行流程实例id  act_hi_actinst里的proc_inst_id_ 字段
            .finished()  //unfinished表示没完成的
            .list();
        for(HistoricActivityInstance hai:list){
            System.out.println("活动ID:"+hai.getId());
            System.out.println("流程实例ID:"+hai.getProcessInstanceId());
            System.out.println("活动名称:"+hai.getActivityName());
            System.out.println("办理人:"+hai.getAssignee());
            System.out.println("开始时间:"+hai.getStartTime());
            System.out.println("结束时间:"+hai.getEndTime());
            System.out.println("=================================");
        }
    }

/**
     * 设置流程变量数据
     */
    @Test
    public void setVariableValues(){
        TaskService taskService=processEngine.getTaskService(); // 任务Service
        String taskId="72504";
        taskService.setVariable(taskId, "days", 2);
        // taskService.setVariable(taskId, "date", new Date());
        taskService.setVariableLocal(taskId,"date", new Date());
        taskService.setVariable(taskId, "reason", "发烧");
        Student student=new Student();
        student.setId(1);
        student.setName("张三");
        taskService.setVariable(taskId, "student", student); // 存序列化对象
    }
    
    /**
     * 获取流程变量数据
     */
    @Test
    public void getVariableValues(){
        TaskService taskService=processEngine.getTaskService(); // 任务Service
        String taskId="80002";
        Integer days=(Integer) taskService.getVariable(taskId, "days");
        // Date date=(Date) taskService.getVariable(taskId, "date");
        Date date=(Date) taskService.getVariableLocal(taskId, "date");
        String reason=(String) taskService.getVariable(taskId, "reason");
        Student student=(Student) taskService.getVariable(taskId, "student"); 
        System.out.println("请假天数:"+days);
        System.out.println("请假日期:"+date);
        System.out.println("请假原因:"+reason);
        System.out.println("请假对象:"+student.getId()+","+student.getName());
    }


    
    用TaskService 设置流程变量数据


    
    /**
     * 设置流程变量数据
     */
    @Test
    public void setVariableValue(){
        TaskService taskService=processEngine.getTaskService(); // 任务Service
        String taskId="60004";  //正在执行的任务id
        Student student=new Student(); //如果传递的是实体对象 该对象必须要实现序列化
        student.setId(1);
        student.setName("张三");

        Map<String, Object> variables=new HashMap<String,Object>();
        variables.put("days", 2);
        variables.put("date", new Date());
        variables.put("reason", "回家");
        variables.put("student", student);
        taskService.setVariables(taskId, variables);

//        service.setVariable(taskId, "days", 5);     一次可以设置一个变量 也可以把变量放到map集合里  设置变量
//        service.setVariable(taskId, "reason", "回家");
//        service.setVariable(taskId, "student", s);

        // service.setVariableLocal(taskId, "key", "value"); 设置局部变量 用的较少
    }

用TaskService获取流程变量数据


    
    /**
     * 获取流程变量数据
     */
    @Test
    public void getVariableValues(){
        TaskService taskService=processEngine.getTaskService(); // 任务Service
        String taskId="65002";
        Map<String,Object> variables=taskService.getVariables(taskId);
        Integer days=(Integer) variables.get("days");
        Date date=(Date) variables.get("date");
        String reason=(String) variables.get("reason");
        Student student=(Student)variables.get("student"); 

    //service.getVariableLocal(taskId, "key") 根据key获取局部变量 用的较少

//        Integer days = (Integer)service.getVariable(taskId, "days");
//        String reason = (String) service.getVariable(taskId, "reason");
//        Student student = (Student) service.getVariable(taskId, "student");

        System.out.println("请假天数:"+days);
        System.out.println("请假日期:"+date);
        System.out.println("请假原因:"+reason);
        System.out.println("请假对象:"+student.getId()+","+student.getName());
    }

  用RuntimeService 设置流程变量数据 流程启动的时候就可以设置变量和taskservice差不多

 

/**
     * 设置流程变量数据
     */
    @Test
    public void setVariableValues(){
        RuntimeService runtimeService=processEngine.getRuntimeService(); // 任务Service
        String executionId="90001";
        runtimeService.setVariable(executionId, "days", 2);
        runtimeService.setVariable(executionId, "date", new Date());
        runtimeService.setVariable(executionId, "reason", "发烧");
        Student student=new Student();
        student.setId(1);
        student.setName("张三");
        runtimeService.setVariable(executionId, "student", student); // 存序列化对象
    }


     用RuntimeService 获取流程变量数据  


    /**
     * 获取流程变量数据
     */
    @Test
    public void getVariableValues(){
        RuntimeService runtimeService=processEngine.getRuntimeService(); // 任务Service
        String executionId="90001";  //ac_ru_execution 表中的id_
        Integer days=(Integer) runtimeService.getVariable(executionId, "days");
        Date date=(Date) runtimeService.getVariable(executionId, "date");
        String reason=(String) runtimeService.getVariable(executionId, "reason");
        Student student=(Student) runtimeService.getVariable(executionId, "student"); 
        System.out.println("请假天数:"+days);
        System.out.println("请假日期:"+date);
        System.out.println("请假原因:"+reason);
        System.out.println("请假对象:"+student.getId()+","+student.getName());
    }

启动流程实例时设置变量

/**
     * 启动流程实例
     */
    @Test
    public void start(){
        Student student=new Student();
        student.setId(1);
        student.setName("张三");

        Map<String, Object> variables=new HashMap<String,Object>();
        variables.put("days", 2);
        variables.put("date", new Date());
        variables.put("reason", "回家");
        variables.put("student", student);
        
        ProcessInstance pi=processEngine.getRuntimeService() // 运行时Service
            .startProcessInstanceByKey("studentLevaeProcess", variables); // 启动流程的时候,设置流程变量

        System.out.println("流程实例ID:"+pi.getId());
        System.out.println("流程定义ID:"+pi.getProcessDefinitionId()); 
    }

完成任务的时候设置流程变量

/**
     * 完成任务
     */
    @Test
    public void completeTask(){

Student student=new Student();
        student.setId(1);
        student.setName("张三");

        Map<String, Object> variables=new HashMap<String,Object>();
        variables.put("days", 2);
        variables.put("date", new Date());
        variables.put("reason", "发烧");
        variables.put("student", student);
        processEngine.getTaskService() // 任务相关Service
            .complete("112504", variables); //完成任务的时候,设置流程变量
    }

}

四、流程部署加载的zip压缩包的方式

/**
     * 部署流程定义使用zip方式
     */
    public void deployWithZip(){
        InputStream inputStream=this.getClass() // 取得当前class对象
            .getClassLoader() // 获取类加载器
            .getResourceAsStream("diagrams/helloWorld.zip"); // 获取指定文件资源流
        
        ZipInputStream zipInputStream=new ZipInputStream(inputStream); // 实例化zip输入流
        Deployment deployment=processEngine.getRepositoryService() // 获取部署相关Service
                .createDeployment() // 创建部署
                .addZipInputStream(zipInputStream) // 添加zip输入流
                .name("HelloWorld流程") // 流程名称
                .deploy(); // 部署
        System.out.println("流程部署ID:"+deployment.getId()); 
        System.out.println("流程部署Name:"+deployment.getName());
    }

五、数据库表的变化

################################
# 部署流程定义涉及到的表

# 流程部署表
SELECT * FROM `act_re_deployment`

# 流程定义表
SELECT * FROM `act_re_procdef`

# 资源文件表
SELECT * FROM `act_ge_bytearray`

# 系统配置表 
SELECT * FROM `act_ge_property`

################################
# 启动流程实例涉及到的表

# 流程实例运行时 执行对象表
SELECT * FROM `act_ru_execution`

# 流程实例运行时 身份联系表
SELECT * FROM `act_ru_identitylink`

# 流程实例运行时 用户任务表
SELECT * FROM `act_ru_task`

# 活动节点历史表
SELECT * FROM `act_hi_actinst`

# 身份联系表 历史
SELECT * FROM `act_hi_identitylink`

# 流程实例表 历史
SELECT * FROM `act_hi_procinst`

# 历史任务表 
SELECT * FROM `act_hi_taskinst`

################################
# 结束流程实例涉及到的表
# 运行时  表数据全部清空
# 历史表  表数据修改 或者增加了数据


################################
# 设置流程变量涉及到的表

# 运行时流程变量表
SELECT * FROM `act_ru_variable`

# 历史流程变量表
SELECT * FROM `act_hi_varinst`

六、多种线路流程执行情况

连线

流程图如下

Activiti 工作流

当请假天数小于10天班长审批完直接结束

当请假天数大于10天时班长审批完班主任审批完才能结束

流程线表达式如下

Activiti 工作流

//完成任务
    @Test
    public void compelet(){
        TaskService service = engine.getTaskService();
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("days", 18);
        service.complete("105002",map); //在请假完成的时候带参数 或者设置一个全局变量 key的值一定要是days和表达式中的值要一致
    }

排它网关  排它网关和连线的区别  是排它网关如果没有匹配的条件时可以默认执行某条流程

流程图如下

Activiti 工作流

当请假天数小于3天时班长审批

当请假天数大于3天小于7天班主任审批

当请假天数条件不满足前两个(也就是大于7天)的是校长审批

设置默认走校长审批  流程  配置如下   大于7天的那个条线是flow5   默认的flow5 这个一定不要设置大于几天的那个表达式 (condition) 因为前两个如果不满足的情况下回默认走flow5那个流程

Activiti 工作流

做法和上面的连线是一样的  在请假完成的时候带参数 或者设置一个全局变量 key的值一定要是days和表达式中的值要一致

 

并行网关  并行网关 是多个流程一起走  当多个流程都走完时在继续向下执行

流程图如下

Activiti 工作流

班长和班主任都审批通过后才可以在往下执行

七、用户和组的创建删除

创建用户

@Test
    public void testSaveUser(){
        IdentityService indentityService=processEngine.getIdentityService();
        User user=new UserEntity(); // 实例化用户实体  activiti提供的一个用户接口
        user.setId("wangwu");
        user.setPassword("123");
        user.setEmail("[email protected]");
        indentityService.saveUser(user); 
    }

删除用户

@Test
    public void testDeleteUser(){
        IdentityService indentityService=processEngine.getIdentityService();
        indentityService.deleteUser("wangwu"); //根据  act_id_user id_字段删除
    }

创建组

@Test
    public void testSaveGroup(){
        IdentityService indentityService=processEngine.getIdentityService();
        Group group=new GroupEntity();
        group.setId("test");
        indentityService.saveGroup(group); 
    }

删除组



    @Test
    public void testDeleteGroup(){
        IdentityService indentityService=processEngine.getIdentityService();
        indentityService.deleteGroup("test");   //根据  act_id_group id_字段删除
    }

把用户添加到组里


    @Test
    public void testSaveMembership(){
        IdentityService indentityService=processEngine.getIdentityService();
        indentityService.createMembership("wangwu", "test"); //第一个表示act_id_user中的id_字段第二个表示act_id_group中的id_字段
    }

删除用户和组的关联关系

@Test
    public void testDeleteMembership(){
        IdentityService indentityService=processEngine.getIdentityService();
        indentityService.deleteMembership("wangwu", "test");  /第一个表示act_id_user中的id_字段第二个表示act_id_group中的id_字段
    }

八、任务分配

个人任务分配

1、在流程图的Main config -> Assignee写个固定的值    (此方式不常用)

2、在启动或者完成任务的时候设置办理人  在配置表达式  如Main config -> Assignee    ${userId}

这种方式变量的key值一定要和assignee 配置的表达式的值一致

3、TaskListener 监听实现

新建一个类  实现 TaskListener接口  代码如下

public class MyTaskListener implements TaskListener{
    private static final long serialVersionUID = 1L;

    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("赵六"); // 指定办理人
    }

}

下图中的listeners中配置上  上面自定义的监听器类TaskListener   走到此流程的时候会执行这个类中notify方法的内容

Activiti 工作流

多用户任务分配    只要是配置的多个用户其中的一个完成任务即可

1、在流程图的Main config -> Candidate users(comma speparated)写个固定的值    (此方式不常用)

在流程图中配置方式如下

Activiti 工作流
    /**
     * 查看任务
     */
    @Test
    public void findTask(){
        List<Task> taskList=processEngine.getTaskService() // 任务相关Service
            .createTaskQuery() // 创建任务查询
            //.taskAssignee("李四") // 指定某个人
            .taskCandidateUser("赵六") // 指定候选人
            .list();
        for(Task task:taskList){
            System.out.println("任务ID:"+task.getId()); 
            System.out.println("任务名称:"+task.getName());
            System.out.println("任务创建时间:"+task.getCreateTime());
            System.out.println("任务委派人:"+task.getAssignee());
            System.out.println("流程实例ID:"+task.getProcessInstanceId());
        }
    }

2、在启动或者完成任务的时候设置办理人  在配置表达式  如Main config -> Candidate users(comma speparated)    ${userIds}

这种方式变量的key值一定要和Candidate users(comma speparated)配置的表达式的值一致

//完成任务
    @Test
    public void compelet(){
        TaskService service = engine.getTaskService();
        Map<String,Object> map=new HashMap<String, Object>();
        map.put("userIds", "bb,aa,cc"); //设置处理任务的候选人
        service.complete("247504",map);
    }

3、TaskListener 监听实现    和个人任务分配中的监听器一样

public class MyTaskListener implements TaskListener{
    private static final long serialVersionUID = 1L;

    public void notify(DelegateTask delegateTask) {
        // delegateTask.setAssignee("赵六"); // 指定办理人
        delegateTask.addCandidateUser("张三");
        delegateTask.addCandidateUser("李四");
        delegateTask.addCandidateUser("王五");
    }

}

组任务分配

租任务分配和个人 任务分配   多人任务分配是一样的

只要在这个组下的某一个用户都可以查看任务  并且处理任务

1、在流程图的Main config -> Candidate groups(comma speparated)写个固定的值    (此方式不常用)

配置如下

Activiti 工作流

2、使用流程变量 Main config -> Candidate groups(comma speparated)

配置如下   这种方式变量的key值一定要和Candidate groups(comma speparated)配置的表达式的值一致

Activiti 工作流

3、TaskListener 监听实现    和个人任务分配中的监听器一样

public void notify(DelegateTask delegateTask) {
        delegateTask.addCandidateGroup("1");
        delegateTask.addCandidateGroup("3");
    }

九、审批被打回来流程图

Activiti 工作流

如果请假天数超过3天  班主任审批不给通过  这里用的是排他网关

相关文章: