美文网首页SpringBoot专题
SpringBoot入门建站全系列(十九)集成Activiti做

SpringBoot入门建站全系列(十九)集成Activiti做

作者: 逍遥天扬 | 来源:发表于2019-07-25 17:15 被阅读111次

    SpringBoot入门建站全系列(十九)集成Activiti做工作流

    一、概述

    Activiti作为一个流行的开源工作流引擎,正在不断发展,其6.0版本以API形式提供服务,而之前版本基本都是要求我们的应用以JDK方式与其交互,只能将其携带到我们的应用中,而API方式则可以服务器独立运行方式,能够形成一个专网内工作流引擎资源共享的方式。

    本篇activiti工作流基于5.22.0。

    首发地址:
    品茗IT-同步发布

    品茗IT 提供在线支持:

    一键快速构建Spring项目工具

    一键快速构建SpringBoot项目工具

    一键快速构建SpringCloud项目工具

    一站式Springboot项目生成

    Mysql一键生成Mybatis注解Mapper

    代码可以在SpringBoot组件化构建https://www.pomit.cn/java/spring/springboot.html中的Activiti组件中查看,并下载。

    二、配置

    本文假设你已经引入spring-boot-starter-web。已经是个SpringBoot项目了,如果不会搭建,可以打开这篇文章看一看《SpringBoot入门建站全系列(一)项目建立》

    使用activiti前,首先要在数据库中将activiti需要的sql导入到数据库中。

    可以去官网:https://www.activiti.org/下载个activiti,把下载好的文件中的sql导入;

    也可以不管它,启动的时候会自动生成表的。。。

    2.1 Maven依赖

    需要引入activiti-spring-boot-starter-basic,这里要访问数据库对工作流数据进行操作,所以要依赖数据库相关jar包。

    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring-boot-starter-basic</artifactId>
        <version>5.22.0</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
    </dependency>
    

    5.22版本是可以正常运行的一个版本,但是存在一个bug,就是jar包下载之后提示损坏,如果出现这个提示,直接到maven中央仓库下载下来jar包覆盖本地的即可。

    2.2 配置文件

    在application.properties 中需要添加下面的配置:

    spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
    spring.datasource.dbcp2.max-wait-millis=60000
    spring.datasource.dbcp2.min-idle=20
    spring.datasource.dbcp2.initial-size=2
    spring.datasource.dbcp2.validation-query=SELECT 1
    spring.datasource.dbcp2.connection-properties=characterEncoding=utf8
    spring.datasource.dbcp2.validation-query=SELECT 1
    spring.datasource.dbcp2.test-while-idle=true
    spring.datasource.dbcp2.test-on-borrow=true
    spring.datasource.dbcp2.test-on-return=false
    
    spring.datasource.driverClassName = com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/cff?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    spring.datasource.username=cff
    spring.datasource.password=123456
    
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    mybatis.mapper-locations=classpath:mapper/*.xml
    
    spring.autoconfigure.exclude=org.activiti.spring.boot.SecurityAutoConfiguration
    
    

    这里的配置主要就是数据库及数据源、mybatis的配置。

    spring.autoconfigure.exclude,这个配置比较特殊,是activiti的bug导致启动失败,需要将org.activiti.spring.boot.SecurityAutoConfiguration排除掉。

    2.3 Activiti流程配置

    下面是工作流流程配置文件,配置了一个流程,Start--》commit--》CustomerServiceApproval--》ManagerApproval--》End。

    <?xml version="1.0" encoding="UTF-8"?>
    <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
      <process id="productAdvice" name="product Advice" isExecutable="true">
        <startEvent id="startevent1" name="Start"></startEvent>
        <endEvent id="endevent1" name="End"></endEvent>
        <userTask id="usertask1" name="commit"></userTask>
        <userTask id="usertask2" name="CustomerServiceApproval"></userTask>
        <userTask id="usertask3" name="ManagerApproval"></userTask>
        <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1">
          <extensionElements>
            <activiti:executionListener event="create" class="com.cff.springbootwork.activiti.listener.CommitExecutionListener"></activiti:executionListener>
          </extensionElements>
        </sequenceFlow>
        <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
        <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
        <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
      </process>
      <bpmndi:BPMNDiagram id="BPMNDiagram_productAdvice">
        <bpmndi:BPMNPlane bpmnElement="productAdvice" id="BPMNPlane_productAdvice">
          <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
            <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="20.0"></omgdc:Bounds>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
            <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="330.0"></omgdc:Bounds>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
            <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="80.0"></omgdc:Bounds>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
            <omgdc:Bounds height="81.0" width="105.0" x="295.0" y="160.0"></omgdc:Bounds>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
            <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="260.0"></omgdc:Bounds>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
            <omgdi:waypoint x="347.0" y="55.0"></omgdi:waypoint>
            <omgdi:waypoint x="347.0" y="80.0"></omgdi:waypoint>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
            <omgdi:waypoint x="347.0" y="135.0"></omgdi:waypoint>
            <omgdi:waypoint x="347.0" y="160.0"></omgdi:waypoint>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
            <omgdi:waypoint x="347.0" y="241.0"></omgdi:waypoint>
            <omgdi:waypoint x="347.0" y="260.0"></omgdi:waypoint>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
            <omgdi:waypoint x="347.0" y="315.0"></omgdi:waypoint>
            <omgdi:waypoint x="347.0" y="330.0"></omgdi:waypoint>
          </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
      </bpmndi:BPMNDiagram>
    </definitions>
    

    这里,activiti:executionListener 配置了一个监听器,监听流程流转。

    三、Activiti工作流功能

    流程监听器,我这里啥也不监听了,但是还是要写出来。

    CommitExecutionListener:

    package com.cff.springbootwork.activiti.listener;
    
    import org.activiti.engine.delegate.DelegateExecution;
    import org.activiti.engine.delegate.ExecutionListener;
    
    public class CommitExecutionListener implements ExecutionListener{
    
        /**
         * 
         */
        private static final long serialVersionUID = 6482750935517963649L;
    
        @Override
        public void notify(DelegateExecution execution) throws Exception {
            String eventName = execution.getEventName();
            System.out.println("BeforCommitExecutionListener:"+eventName);
            
            
        }
    
    }
    
    
    

    工作流处理过程的service:

    ProductService:

    package com.cff.springbootwork.activiti.service;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.activiti.engine.HistoryService;
    import org.activiti.engine.RepositoryService;
    import org.activiti.engine.RuntimeService;
    import org.activiti.engine.TaskService;
    import org.activiti.engine.runtime.ProcessInstance;
    import org.activiti.engine.task.Task;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    
    import com.cff.springbootwork.activiti.dao.ProductMapper;
    import com.cff.springbootwork.activiti.domain.ProductTask;
    import com.cff.springbootwork.activiti.domain.UserInfo;
    
    @Service
    public class ProductService {
        protected Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private RuntimeService runtimeService;
    
        @Autowired
        private TaskService taskService;
    
        @Autowired
        private RepositoryService repositoryService;
    
        @Autowired
        private HistoryService historyService;
    
        @Autowired
        ProductMapper productMapper;
    
        @Autowired
        UserInfoService appUserService;
    
        /**
         * 产生工作流
         * 
         * @param userTask
         * @param userid
         */
        public void genTask(ProductTask userTask, String userid) {
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("productAdvice");
            Task tmp = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId())
                    .singleResult();
    
            userTask.setInstanceId(processInstance.getProcessInstanceId());
            tmp.setAssignee(userid);
            taskService.complete(tmp.getId());
    
            userTask.setUserName(userid);
            productMapper.save(userTask);
        }
    
        /**
         * 获取提交的工作
         * 
         * @param userid
         * @return
         * @throws Exception
         */
        public List<ProductTask> applyList(String userid) throws Exception {
            List<ProductTask> tasks = productMapper.getUserTask(userid);
            return tasks;
        }
    
        /**
         * 获取待处理的工作
         * 
         * @param userid
         * @return
         * @throws Exception
         */
        public List<ProductTask> waitList(String userid) throws Exception {
            String userType = findTaskType(userid);
            logger.info("准备查询userType为{}的任务", userType);
            List<Task> tasks = taskService.createTaskQuery().taskName(userType).orderByTaskCreateTime().asc().list();
            List<ProductTask> utasks = new ArrayList<ProductTask>();
            for (int i = 0; i < tasks.size(); i++) {
                ProductTask userTaskTmp = productMapper.getUserTaskByInstanceId(tasks.get(i).getProcessInstanceId());
                if (userTaskTmp != null) {
                    userTaskTmp.setTaskId(tasks.get(i).getId());
                    utasks.add(userTaskTmp);
                }
            }
    
            return utasks;
        }
    
        /**
         * 工作流流转
         * 
         * @param taskid
         * @param processid
         * @param userid
         * @return
         * @throws Exception
         */
        public synchronized Boolean processCommit(String taskid, String instanceId, String userid) throws Exception {
            logger.info("审批taskid:{},instanceId:{}", taskid, instanceId);
            try {
                ProductTask userTask = productMapper.getUserTaskByInstanceId(instanceId);
                taskService.complete(taskid);
                userTask.setCurviewer(userid);
                productMapper.updateStatus(userTask);
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    
        /**
         * 根据用户类型获取任务名称
         * 
         * @param userId
         * @return
         */
        public String findTaskType(String userId) {
            UserInfo appUser = appUserService.getUserInfoByUserName(userId);
            String userType = appUser.getUserType();
            if (userType == null)
                return null;
            if (!StringUtils.isEmpty(userType)) {
                if ("2001".equals(userType)) {
                    return "CustomerServiceApproval";
                } else if ("0000".equals(userType)) {
                    return "ManagerApproval";
                } else {
                    return "commit";
                }
            }
            return null;
        }
    
        /**
         * 获取处理过的任务
         * 
         * @param userid
         * @return
         */
        public List<ProductTask> manageList(String userid) {
            List<ProductTask> utasks = productMapper.getUserTaskByCurrentViwer(userid);
    
            return utasks;
        }
    }
    
    

    这个service中:

    • genTask 是工作流的开端,产生任务之后,即流转到下一个节点。

    • applyList,申请的任务列表。

    • waitList:待处理的任务。

    • processCommit: 让工作流流转起来。

    四、测试Activiti工作流

    我们定义一个web接口来做测试。

    ActivitiRest:

    package com.cff.springbootwork.activiti.web;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PathVariable;
    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 com.cff.springbootwork.activiti.domain.ProductTask;
    import com.cff.springbootwork.activiti.service.ProductService;
    
    @RestController
    @RequestMapping("/activiti")
    public class ActivitiRest {
    
        @Autowired
        ProductService productService;
    
        @RequestMapping(value = "/add/{name}")
        public String add(@RequestBody ProductTask productTask, @PathVariable("name") String name) {
            productService.genTask(productTask, name);
            return "Success";
        }
    
        @RequestMapping(value = "/applyList/{name}")
        public List<ProductTask> applyList(@PathVariable("name") String name) throws Exception {
            return productService.applyList(name);
        }
    
        @RequestMapping(value = "/waitList/{name}")
        public List<ProductTask> waitList(@PathVariable("name") String name) throws Exception {
            return productService.waitList(name);
        }
    
        @RequestMapping(value = "/next/{name}")
        public Boolean processCommit(@PathVariable("name") String name, @RequestParam("instanceId") String instanceId,
                @RequestParam("taskId") String taskId) throws Exception {
            return productService.processCommit(taskId, instanceId, name);
        }
    }
    
    

    五、过程中用到的其他数据库相关service、mapper、实体

    UserInfoService:

    package com.cff.springbootwork.activiti.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.cff.springbootwork.activiti.dao.UserInfoMapper;
    import com.cff.springbootwork.activiti.domain.UserInfo;
    
    @Service
    public class UserInfoService {
        @Autowired
        UserInfoMapper userInfoDao;
        public UserInfo getUserInfoByUserName(String userName){
            return userInfoDao.findByUserName(userName);
        }
    }
    
    

    ProductMapper :

    package com.cff.springbootwork.activiti.dao;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Mapper;
    
    import com.cff.springbootwork.activiti.domain.ProductTask;
    
    @Mapper
    public interface ProductMapper {
    
        public void save(ProductTask userTask);
        
        public List<ProductTask> getUserTask(String userid);
        
        public ProductTask getUserTaskByInstanceId(String instanceId);
        
        public void updateStatus(ProductTask userTask);
    
        public List<ProductTask> getUserTaskByCurrentViwer(String userid);
    }
    
    

    ProductMapper 对应的mybatis-productTask.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.cff.springbootwork.activiti.dao.ProductMapper">  
        <resultMap id="BaseResultMap" type="com.cff.springbootwork.activiti.domain.ProductTask">  
            <result column="instance_id" property="instanceId" />  
            <result column="user_name" property="userName" /> 
            <result column="title" property="title" />
            <result column="task_type" property="taskType" />  
            <result column="content" property="content" />  
            <result column="curviewer" property="curviewer" />
        </resultMap>  
        <sql id="Base_Column_List" >  
            instance_id, user_name, title, task_type, content, curviewer
        </sql>  
        <select id="getUserTask" resultMap="BaseResultMap" parameterType="java.lang.String" >  
           select   
           <include refid="Base_Column_List" />  
           from se_product_task  
           where user_name = #{userName,jdbcType=VARCHAR}  
        </select>  
        
        <select id="getUserTaskByInstanceId" resultMap="BaseResultMap" parameterType="java.lang.String" >  
           select   
           <include refid="Base_Column_List" />  
           from se_product_task  
           where instance_id = #{instanceId,jdbcType=VARCHAR}  
        </select>  
        
        <select id="getUserTaskByCurrentViwer" resultMap="BaseResultMap" parameterType="java.lang.String" >  
           select   
           <include refid="Base_Column_List" />  
           from se_product_task  
           where curviewer = #{curviewer,jdbcType=VARCHAR}  
        </select>   
        
        <insert id="save" parameterType="com.cff.springbootwork.activiti.domain.ProductTask">
          insert into se_product_task(instance_id, user_name, title, task_type, content, curviewer) 
                values(#{instanceId}, #{userName}, #{title}, #{taskType}, #{content}, #{curviewer})
        </insert>
        
        <update id="updateStatus" parameterType="com.cff.springbootwork.activiti.domain.ProductTask">
            update se_product_task
            <set>
                <if test="curviewer != null">curviewer=#{curviewer}</if>
            </set>
            where instance_id=#{instanceId}
        </update>
    </mapper>  
    

    ProductTask:

    
    package com.cff.springbootwork.activiti.domain;
    
    import java.io.Serializable;
    
    public class ProductTask implements Serializable {
        /**
         * 
         */
        private static final long serialVersionUID = 3481818865839076537L;
    
        String instanceId;
        String userName;
        String taskType;
        String content;
        String title;
        String curviewer;
        String taskId;
    
        public String getInstanceId() {
            return instanceId;
        }
    
        public void setInstanceId(String instanceId) {
            this.instanceId = instanceId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getTaskType() {
            return taskType;
        }
    
        public void setTaskType(String taskType) {
            this.taskType = taskType;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public String getCurviewer() {
            return curviewer;
        }
    
        public void setCurviewer(String curviewer) {
            this.curviewer = curviewer;
        }
    
        public String getTaskId() {
            return taskId;
        }
    
        public void setTaskId(String taskId) {
            this.taskId = taskId;
        }
    
        public static long getSerialversionuid() {
            return serialVersionUID;
        }
    
    }
    
    

    UserInfoMapper :

    
    
    

    UserInfoMapper 对应的mybatis-userInfo.xml:

    
    

    UserInfo :

    
    
    

    详细完整的实体及测试用例,可以访问品茗IT-博客《SpringBoot入门建站全系列(十九)集成Activiti做工作流》进行查看

    快速构建项目

    Spring组件化构建

    SpringBoot组件化构建

    SpringCloud服务化构建

    喜欢这篇文章么,喜欢就加入我们一起讨论SpringBoot使用吧!


    品茗IT交流群

    相关文章

      网友评论

        本文标题:SpringBoot入门建站全系列(十九)集成Activiti做

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