美文网首页Java
在 Spring Boot 项目中使用 activiti

在 Spring Boot 项目中使用 activiti

作者: 新一993 | 来源:发表于2019-12-21 16:23 被阅读0次

    新建springBoot项目时勾选activiti,或者在已建立的springBoot项目添加以下依赖:

    <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-spring-boot-starter-basic</artifactId>
      <version>6.0.0</version>
    </dependency>
    

    数据源和activiti配置:

    server:
      port: 8081
    
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/act5?useSSL=true
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
    
      # activiti default configuration
      activiti:
        database-schema-update: true
        check-process-definitions: true
        process-definition-location-prefix: classpath:/processes/
    # process-definition-location-suffixes:
    # - **.bpmn
    # - **.bpmn20.xml
        history-level: full
    

    在activiti的默认配置中,process-definition-location-prefix 是指定activiti流程描述文件的前缀(即路径),启动时,activiti就会去寻找此路径下的流程描述文件,并且自动部署;suffix 是一个String数组,表示描述文件的默认后缀名,默认以上两种。

    springMVC配置:

    package com.yawn.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.servlet.config.annotation.*;
    
    /**
     * Created by yawn on 2017/8/5.
     */
    @EnableWebMvc
    @Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
            registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
            super.addResourceHandlers(registry);
        }
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/index");
            registry.addViewController("/user");
            registry.addRedirectViewController("/","/templates/login.html");
    // registry.addStatusController("/403", HttpStatus.FORBIDDEN);
            super.addViewControllers(registry);
        }
    }
    

    这里配置静态资源和直接访问的页面:在本示例项目中,添加了thymeleaf依赖解析视图,主要采用异步方式获取数据,通过angularJS进行前端数据的处理和展示。

    配置了数据源和activiti后,启动项目,activiti 的各个服务组件就已经被加入到spring容器中了,所以就可以直接注入使用了。如果在未自动配置的spring环境中,可以使用通过指定bean的init-method来配置activiti的服务组件。

    以以下请假流程为例:

    image

    1. 开始流程并“申请请假”(员工)

    private static final String PROCESS_DEFINE_KEY = "vacationProcess";
    
        public Object startVac(String userName, Vacation vac) {
    
            identityService.setAuthenticatedUserId(userName);
            // 开始流程
            ProcessInstance vacationInstance = runtimeService.startProcessInstanceByKey(PROCESS_DEFINE_KEY);
            // 查询当前任务
            Task currentTask = taskService.createTaskQuery().processInstanceId(vacationInstance.getId()).singleResult();
            // 申明任务
            taskService.claim(currentTask.getId(), userName);
    
            Map<String, Object> vars = new HashMap<>(4);
            vars.put("applyUser", userName);
            vars.put("days", vac.getDays());
            vars.put("reason", vac.getReason());
            // 完成任务
            taskService.complete(currentTask.getId(), vars);
    
            return true;
        }
    

    在此方法中,Vaction 是申请时的具体信息,在完成“申请请假”任务时,可以将这些信息设置成参数。

    2. 审批请假(老板)

    (1)查询需要自己审批的请假

    public Object myAudit(String userName) {
            List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(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<VacTask> vacTaskList = new ArrayList<>();
            for (Task task : taskList) {
                VacTask vacTask = new VacTask();
                vacTask.setId(task.getId());
                vacTask.setName(task.getName());
                vacTask.setCreateTime(task.getCreateTime());
                String instanceId = task.getProcessInstanceId();
                ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
                Vacation vac = getVac(instance);
                vacTask.setVac(vac);
                vacTaskList.add(vacTask);
            }
            return vacTaskList;
        }
    
        private Vacation getVac(ProcessInstance instance) {
            Integer days = runtimeService.getVariable(instance.getId(), "days", Integer.class);
            String reason = runtimeService.getVariable(instance.getId(), "reason", String.class);
            Vacation vac = new Vacation();
            vac.setApplyUser(instance.getStartUserId());
            vac.setDays(days);
            vac.setReason(reason);
            Date startTime = instance.getStartTime(); // activiti 6 才有
            vac.setApplyTime(startTime);
            vac.setApplyStatus(instance.isEnded() ? "申请结束" : "等待审批");
            return vac;
        }
    
    package com.yawn.entity;
    
    import java.util.Date;
    
    /**
     * @author Created by yawn on 2018-01-09 14:31
     */
    public class VacTask {
    
        private String id;
        private String name;
        private Vacation vac;
        private Date createTime;
    
        // getter setter ...
    }
    

    老板查询自己当前需要审批的任务,并且将任务和参数设置到一个VacTask对象,用于页面的展示。

    (2)审批请假

    public Object passAudit(String userName, VacTask vacTask) {
            String taskId = vacTask.getId();
            String result = vacTask.getVac().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.complete(taskId, vars);
            return true;
        }
    

    同理,result是审批的结果,也是在完成审批任务时需要传入的参数;taskId是刚才老板查询到的当前需要自己完成的审批任务ID。(如果流程在这里设置分支,可以通过判断result的值来跳转到不同的任务)

    3. 查询记录

    由于已完成的请假在数据库runtime表中查不到(runtime表只保存正在进行的流程示例信息),所以需要在history表中查询。

    (1) 查询请假记录

    public Object myVacRecord(String userName) {
            List<HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
                    .processDefinitionKey(PROCESS_DEFINE_KEY).startedBy(userName).finished()
                    .orderByProcessInstanceEndTime().desc().list();
    
            List<Vacation> vacList = new ArrayList<>();
            for (HistoricProcessInstance hisInstance : hisProInstance) {
                Vacation vacation = new Vacation();
                vacation.setApplyUser(hisInstance.getStartUserId());
                vacation.setApplyTime(hisInstance.getStartTime());
                vacation.setApplyStatus("申请结束");
                List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
                        .processInstanceId(hisInstance.getId()).list();
                ActivitiUtil.setVars(vacation, varInstanceList);
                vacList.add(vacation);
            }
            return vacList;
        }
    

    请假记录即查出历史流程实例,再查出关联的历史参数,将历史流程实例和历史参数设置到Vcation对象(VO对象)中去,即可返回,用来展示。

    package com.yawn.util;
    
    import org.activiti.engine.history.HistoricVariableInstance;
    
    import java.lang.reflect.Field;
    import java.util.List;
    
    /**
     * activiti中使用得到的工具方法
     * @author Created by yawn on 2018-01-10 16:32
     */
    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();
            }
        }
    }
    

    此外,以上是查询历史流程实例和历史参数后,设置VO对象的通用方法:可以根据参数列表中的参数,将与VO对象属性同名的参数设置到VO对象中去。

    4. 前端展示和操作

    (1)审批列表和审批操作示例

    image
    <div ng-controller="myAudit">
            <h2 ng-init="myAudit()">待我审核的请假</h2>
            <table border="0">
                <tr>
                    <td>任务名称</td>
                    <td>任务时间</td>
                    <td>申请人</td>
                    <td>申请时间</td>
                    <td>天数</td>
                    <td>事由</td>
                    <td>操作</td>
                </tr>
                <tr ng-repeat="vacTask in vacTaskList">
                    <td>{{vacTask.name}}</td>
                    <td>{{vacTask.createTime | date:'yyyy-MM-dd HH:mm:ss'}}</td>
                    <td>{{vacTask.vac.applyUser}}</td>
                    <td>{{vacTask.vac.applyTime | date:'yyyy-MM-dd HH:mm:ss'}}</td>
                    <td>{{vacTask.vac.days}}</td>
                    <td>{{vacTask.vac.reason}}</td>
                    <td>
                        <button type="button" ng-click="passAudit(vacTask.id, 1)">审核通过</button>
                        <button type="button" ng-click="passAudit(vacTask.id, 0)">审核拒绝</button>
                    </td>
                </tr>
            </table>
        </div>
    
    app.controller("myAudit", function ($scope, $http, $window) {
        $scope.vacTaskList = [];
    
        $scope.myAudit = function () {
            $http.get(
                "/myAudit"
            ).then(function (response) {
                $scope.vacTaskList = response.data;
            })
        };
    
        $scope.passAudit = function (taskId, result) {
            $http.post(
                "/passAudit",
                {
                    "id": taskId,
                    "vac": {
                        "result": result >= 1 ? "审核通过" : "审核拒绝"
                    }
                }
            ).then(function (response) {
                if (response.data === true) {
                    alert("操作成功!");
                    $window.location.reload();
                } else {
                    alert("操作失败!");
                }
            })
        }
    });
    

    本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。
    传送门:https://mp.weixin.qq.com/s/igMojff-bbmQ6irCGO3mqA

    相关文章

      网友评论

        本文标题:在 Spring Boot 项目中使用 activiti

        本文链接:https://www.haomeiwen.com/subject/oeugnctx.html