├── src └── main │ ├── resources │ ├── processes │ │ ├── leave │ │ │ ├── leave.png │ │ │ └── leave.bpmn │ │ ├── listener │ │ │ ├── listener.png │ │ │ └── listener.bpmn │ │ ├── shopping │ │ │ ├── shopping.png │ │ │ └── shopping.bpmn │ │ ├── countersign │ │ │ ├── countersign.png │ │ │ └── countersign.bpmn │ │ ├── claim-expense │ │ │ ├── claim-expense.png │ │ │ ├── claim-expense.zip │ │ │ └── claim-expense.bpmn │ │ └── exclusive-gateway │ │ │ ├── exclusive-gateway.png │ │ │ └── exclusive-gateway.bpmn │ └── application.properties │ └── java │ └── com │ └── jellyleo │ └── activiti │ ├── service │ ├── ResumeService.java │ └── impl │ │ └── ResumeServiceImpl.java │ ├── Application.java │ ├── config │ └── SecurityConfig.java │ ├── entity │ └── CommonVariable.java │ ├── controller │ ├── TestController.java │ ├── BaseController.java │ ├── HistoricController.java │ ├── TaskController.java │ └── ProcessController.java │ ├── listener │ ├── JlExecutionListener.java │ ├── JlTaskListener.java │ └── JlSignListener.java │ └── util │ └── BeanUtil.java ├── .gitignore ├── README.md └── pom.xml /src/main/resources/processes/leave/leave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/leave/leave.png -------------------------------------------------------------------------------- /src/main/resources/processes/listener/listener.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/listener/listener.png -------------------------------------------------------------------------------- /src/main/resources/processes/shopping/shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/shopping/shopping.png -------------------------------------------------------------------------------- /src/main/resources/processes/countersign/countersign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/countersign/countersign.png -------------------------------------------------------------------------------- /src/main/resources/processes/claim-expense/claim-expense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/claim-expense/claim-expense.png -------------------------------------------------------------------------------- /src/main/resources/processes/claim-expense/claim-expense.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/claim-expense/claim-expense.zip -------------------------------------------------------------------------------- /src/main/resources/processes/exclusive-gateway/exclusive-gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jellyleo/activiti7/HEAD/src/main/resources/processes/exclusive-gateway/exclusive-gateway.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | .bpmn 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /build/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/service/ResumeService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.service; 7 | 8 | /** 9 | * 功能描述: 10 | * 11 | * @author Jelly 12 | * @created 2019年11月20日 13 | * @version 1.0.0 14 | */ 15 | public interface ResumeService { 16 | 17 | void positiveNumber(); 18 | 19 | void zeroNumber(); 20 | 21 | void negativeNumber(); 22 | 23 | void otherNumber(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/Application.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti; 7 | 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | 11 | /** 12 | * 13 | * 功能描述:应用启动类 14 | * 15 | * @author Jelly 16 | * @created 2019年11月19日 17 | * @version 1.0.0 18 | */ 19 | @SpringBootApplication 20 | public class Application { 21 | public static void main(String[] args) { 22 | SpringApplication.run(Application.class, args); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Activiti7 + SpringBoot2 2 | 3 | ## 介绍 4 | * springboot2集成activiti7实现的简单工作流,修改application.properties数据库配置即可启动。 5 | 6 | --- 7 | 8 | ### 流程图介绍 9 | >claim-expense 10 | * 简单报销审批流程 11 | ![blockchain](src/main/resources/processes/claim-expense/claim-expense.png) 12 | 13 | >shopping 14 | * 购物 15 | ![blockchain](src/main/resources/processes/shopping/shopping.png) 16 | 17 | >exclusive-gateway 18 | * 排他网关学习 19 | ![blockchain](src/main/resources/processes/exclusive-gateway/exclusive-gateway.png) 20 | 21 | >listener 22 | * 执行监听和任务监听 23 | ![blockchain](src/main/resources/processes/listener/listener.png) 24 | 25 | >countersign 26 | * 会签 27 | ![blockchain](src/main/resources/processes/countersign/countersign.png) -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.config; 7 | 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | 12 | /** 13 | * 14 | * 功能描述: Spring Security验证配置 15 | * 16 | * @author Jelly 17 | * @created 2019年11月19日 18 | * @version 1.0.0 19 | */ 20 | @Configuration 21 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 22 | 23 | @Override 24 | protected void configure(HttpSecurity security) throws Exception { 25 | // 禁用CSRF保护 26 | security.csrf().disable(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/entity/CommonVariable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.entity; 7 | 8 | import java.util.List; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.Builder; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | 15 | /** 16 | * 功能描述: 17 | * 18 | * @author Jelly 19 | * @created 2019年11月22日 20 | * @version 1.0.0 21 | */ 22 | @Data 23 | @Builder(toBuilder=true) 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | public class CommonVariable { 27 | 28 | private List signList; 29 | 30 | private Integer day; 31 | 32 | private Integer amount; 33 | 34 | private Integer val; 35 | 36 | private Integer pay; 37 | 38 | private Integer order; 39 | 40 | private Integer sendout; 41 | 42 | private Integer pass; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/controller/TestController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.controller; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | 15 | /** 16 | * 功能描述:测试控制器 17 | * 18 | * @author Jelly 19 | * @created 2019年11月19日 20 | * @version 1.0.0 21 | */ 22 | @Controller 23 | public class TestController extends BaseController { 24 | 25 | /** 26 | * 27 | * 功能描述:测试 28 | * 29 | * @param request 30 | * @param response 31 | * @see [相关类/方法](可选) 32 | * @since [产品/模块版本](可选) 33 | */ 34 | @RequestMapping(value = "/test") 35 | @ResponseBody 36 | public String test(HttpServletRequest request, HttpServletResponse response) { 37 | 38 | return "success"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 2 | # 自建表 3 | spring.activiti.database-schema-update=true 4 | spring.activiti.history-level=full 5 | spring.activiti.db-history-used=true 6 | spring.activiti.check-process-definitions=false 7 | # JDBC配置 8 | spring.datasource.druid.url=jdbc:mysql://192.168.4.2:3306/activiti?useSSL=false 9 | spring.datasource.druid.username=u_activiti 10 | spring.datasource.druid.password=1234RTYU&*() 11 | # 连接池配置(通常来说只需要修改initial-size,min-idle,mac-active) 12 | spring.datasource.druid.initial-size=1 13 | spring.datasource.druid.max-active=20 14 | spring.datasource.druid.min-idle=1 15 | # 获取连接等待的超时时间 16 | spring.datasource.druid.max-wait=60000 17 | #打开PSCache,并且指定每个连接上PSCache的大小 18 | spring.datasource.druid.pool-prepared-statements=true 19 | spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 20 | spring.datasource.druid.validation-query=SELECT 'x' 21 | spring.datasource.druid.test-on-borrow=false 22 | spring.datasource.druid.test-on-return=false 23 | spring.datasource.druid.test-while-idle=true 24 | # 配置间隔多久进行一次检查,检查需要关闭的空闲连接,单位毫秒 25 | spring.datasource.druid.time-between-eviction-runs-millis=60000 26 | # 配置一个连接在连接池中最小的生存时间,单位毫秒 27 | spring.datasource.druid.min-evictable-idle-time-millis=300000 28 | # 配置多个英文逗号分割 29 | spring.datasource.druid.filters=stat 30 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/listener/JlExecutionListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.listener; 7 | 8 | import org.activiti.engine.delegate.DelegateExecution; 9 | import org.activiti.engine.delegate.ExecutionListener; 10 | 11 | /** 12 | * 功能描述: 13 | * 14 | * @author Jelly 15 | * @created 2019年11月21日 16 | * @version 1.0.0 17 | */ 18 | public class JlExecutionListener implements ExecutionListener { 19 | 20 | String EVENTNAME_START = "start"; 21 | String EVENTNAME_END = "end"; 22 | String EVENTNAME_TAKE = "take"; 23 | 24 | /** 25 | */ 26 | private static final long serialVersionUID = 1L; 27 | 28 | /* 29 | * (non-Javadoc) 30 | * 31 | * @see 32 | * org.activiti.engine.delegate.ExecutionListener#notify(org.activiti.engine. 33 | * delegate.DelegateExecution) 34 | */ 35 | @Override 36 | public void notify(DelegateExecution execution) { 37 | String eventName = execution.getEventName(); 38 | if (EVENTNAME_START.equals(eventName)) { 39 | System.out.println("start=========流程启动"); 40 | } else if (EVENTNAME_END.equals(eventName)) { 41 | System.out.println("end=========流程结束"); 42 | } else if (EVENTNAME_TAKE.equals(eventName)) 43 | System.out.println("take ======经过连线"); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/service/impl/ResumeServiceImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.service.impl; 7 | 8 | import org.springframework.stereotype.Service; 9 | 10 | import com.jellyleo.activiti.service.ResumeService; 11 | 12 | /** 13 | * 功能描述: 14 | * 15 | * @author Jelly 16 | * @created 2019年11月20日 17 | * @version 1.0.0 18 | */ 19 | @Service("resumeService") 20 | public class ResumeServiceImpl implements ResumeService { 21 | 22 | /* 23 | * (non-Javadoc) 24 | * 25 | * @see com.jellyleo.activiti.service.ResumeService#positiveNumber() 26 | */ 27 | @Override 28 | public void positiveNumber() { 29 | System.out.println("处理结果为:正数"); 30 | } 31 | 32 | /* 33 | * (non-Javadoc) 34 | * 35 | * @see com.jellyleo.activiti.service.ResumeService#zeroNumber() 36 | */ 37 | @Override 38 | public void zeroNumber() { 39 | System.out.println("处理结果为:零"); 40 | } 41 | 42 | /* 43 | * (non-Javadoc) 44 | * 45 | * @see com.jellyleo.activiti.service.ResumeService#negativeNumber() 46 | */ 47 | @Override 48 | public void negativeNumber() { 49 | System.out.println("处理结果为:负数"); 50 | } 51 | 52 | /* 53 | * (non-Javadoc) 54 | * @see com.jellyleo.activiti.service.ResumeService#otherNumber() 55 | */ 56 | @Override 57 | public void otherNumber() { 58 | System.out.println("处理结果为:非整数"); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/listener/JlTaskListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.listener; 7 | 8 | import org.activiti.engine.delegate.DelegateTask; 9 | import org.activiti.engine.delegate.TaskListener; 10 | 11 | /** 12 | * 功能描述: 13 | * 14 | * @author Jelly 15 | * @created 2019年11月21日 16 | * @version 1.0.0 17 | */ 18 | public class JlTaskListener implements TaskListener { 19 | 20 | String EVENTNAME_CREATE = "create"; 21 | String EVENTNAME_ASSIGNMENT = "assignment"; 22 | String EVENTNAME_COMPLETE = "complete"; 23 | String EVENTNAME_DELETE = "delete"; 24 | 25 | /** 26 | */ 27 | private static final long serialVersionUID = 1L; 28 | 29 | /* 30 | * (non-Javadoc) 31 | * 32 | * @see 33 | * org.activiti.engine.delegate.TaskListener#notify(org.activiti.engine.delegate 34 | * .DelegateTask) 35 | */ 36 | @Override 37 | public void notify(DelegateTask delegateTask) { 38 | String eventName = delegateTask.getEventName(); 39 | if (EVENTNAME_CREATE.endsWith(eventName)) 40 | System.out.println("create===任务创建"); 41 | else if (EVENTNAME_ASSIGNMENT.endsWith(eventName)) 42 | System.out.println("assignment===任务分配"); 43 | else if (EVENTNAME_COMPLETE.endsWith(eventName)) 44 | System.out.println("complete===任务完成"); 45 | else if (EVENTNAME_DELETE.endsWith(eventName)) 46 | System.out.println("delete===任务删除"); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/util/BeanUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.util; 7 | 8 | import java.beans.BeanInfo; 9 | import java.beans.Introspector; 10 | import java.beans.PropertyDescriptor; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Map.Entry; 14 | 15 | /** 16 | * 17 | * 功能描述: 18 | * 19 | * @author Jelly 20 | * @created 2019年11月22日 21 | * @version 1.0.0 22 | */ 23 | public class BeanUtil { 24 | 25 | /** 26 | * 27 | * 功能描述: 28 | * 29 | * @param map 30 | * @param beanType 31 | * @return 32 | * @throws Exception 33 | * @see [相关类/方法](可选) 34 | * @since [产品/模块版本](可选) 35 | */ 36 | public static T mapToBean(Map map, Class beanType) throws Exception { 37 | T t = beanType.newInstance(); 38 | PropertyDescriptor[] pds = Introspector.getBeanInfo(beanType, Object.class).getPropertyDescriptors(); 39 | for (PropertyDescriptor pd : pds) { 40 | for (Entry entry : map.entrySet()) { 41 | if (entry.getKey().equals(pd.getName())) { 42 | pd.getWriteMethod().invoke(t, entry.getValue()); 43 | } 44 | } 45 | } 46 | return t; 47 | } 48 | 49 | /** 50 | * 51 | * 功能描述: 52 | * 53 | * @param bean 54 | * @return 55 | * @throws Exception 56 | * @see [相关类/方法](可选) 57 | * @since [产品/模块版本](可选) 58 | */ 59 | public static Map beanToMap(Object bean) throws Exception { 60 | 61 | Map map = new HashMap<>(); 62 | BeanInfo info = Introspector.getBeanInfo(bean.getClass(), Object.class); 63 | PropertyDescriptor[] pds = info.getPropertyDescriptors(); 64 | for (PropertyDescriptor pd : pds) { 65 | String key = pd.getName(); 66 | Object value = pd.getReadMethod().invoke(bean); 67 | map.put(key, value); 68 | } 69 | return map; 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.controller; 7 | 8 | import java.util.List; 9 | 10 | import org.activiti.api.process.runtime.ProcessAdminRuntime; 11 | import org.activiti.api.process.runtime.ProcessRuntime; 12 | import org.activiti.api.task.runtime.TaskAdminRuntime; 13 | import org.activiti.api.task.runtime.TaskRuntime; 14 | import org.activiti.bpmn.model.FormProperty; 15 | import org.activiti.bpmn.model.UserTask; 16 | import org.activiti.engine.HistoryService; 17 | import org.activiti.engine.RepositoryService; 18 | import org.activiti.engine.RuntimeService; 19 | import org.activiti.engine.TaskService; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.stereotype.Controller; 22 | 23 | /** 24 | * 25 | * 功能描述:activiti基类 26 | * 27 | * @author Jelly 28 | * @created 2019年11月19日 29 | * @version 1.0.0 30 | */ 31 | @Controller 32 | public class BaseController { 33 | @Autowired 34 | TaskService taskService; 35 | @Autowired 36 | RuntimeService runtimeService; 37 | @Autowired 38 | HistoryService historyService; 39 | @Autowired 40 | RepositoryService repositoryService; 41 | 42 | /** 43 | * ProcessRuntime类内部最终调用repositoryService和runtimeService相关API。 需要ACTIVITI_USER权限 44 | */ 45 | @Autowired 46 | ProcessRuntime processRuntime; 47 | 48 | /** 49 | * ProcessRuntime类内部最终调用repositoryService和runtimeService相关API。 50 | * 需要ACTIVITI_ADMIN权限 51 | */ 52 | @Autowired 53 | ProcessAdminRuntime processAdminRuntime; 54 | 55 | /** 56 | * 类内部调用taskService 需要ACTIVITI_USER权限 57 | */ 58 | @Autowired 59 | TaskRuntime taskRuntime; 60 | 61 | /** 62 | * 类内部调用taskService 需要ACTIVITI_ADMIN权限 63 | */ 64 | @Autowired 65 | TaskAdminRuntime taskAdminRuntime; 66 | 67 | /** 68 | * 69 | * 功能描述:Acticiti7 formService替代方法 70 | * 71 | * @see [相关类/方法](可选) 72 | * @since [产品/模块版本](可选) 73 | */ 74 | List getFormPropertiesOfOldFormService(String processDefinitionId, String taskDefinitionKey) { 75 | // 在Acticiti7中,删除了FormService接口,可用以下方法代替 76 | UserTask userTask = (UserTask) repositoryService.getBpmnModel(processDefinitionId) 77 | .getFlowElement(taskDefinitionKey); 78 | return userTask.getFormProperties(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/listener/JlSignListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.listener; 7 | 8 | import org.activiti.engine.ProcessEngine; 9 | import org.activiti.engine.ProcessEngines; 10 | import org.activiti.engine.RuntimeService; 11 | import org.activiti.engine.TaskService; 12 | import org.activiti.engine.delegate.DelegateTask; 13 | import org.activiti.engine.delegate.TaskListener; 14 | import org.activiti.engine.task.Task; 15 | 16 | /** 17 | * 18 | * 功能描述:会签监听 19 | * 20 | * @author Jelly 21 | * @created 2019年11月22日 22 | * @version 1.0.0 23 | */ 24 | public class JlSignListener implements TaskListener { 25 | 26 | /** 27 | * 28 | */ 29 | private static final long serialVersionUID = 1L; 30 | 31 | /* 32 | * (non-Javadoc) 33 | * 34 | * @see 35 | * org.activiti.engine.delegate.TaskListener#notify(org.activiti.engine.delegate 36 | * .DelegateTask) 37 | */ 38 | @Override 39 | public void notify(DelegateTask delegateTask) { 40 | System.out.println("会签监听"); 41 | // 获取流程id 42 | String executionId = delegateTask.getExecutionId(); 43 | 44 | // 创建ProcessEngine 45 | ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); 46 | RuntimeService runtimeService = engine.getRuntimeService(); 47 | TaskService taskService = engine.getTaskService(); 48 | 49 | // 本次审批结果 50 | Integer pass = (Integer) runtimeService.getVariable(executionId, "pass"); 51 | // 审批通过实例数 52 | Integer positive = (Integer) runtimeService.getVariable(executionId, "nrOfPositiveInstances"); 53 | if (pass == 1) { 54 | positive = (positive == null) ? 1 : positive + 1; 55 | } 56 | // 审批完成实例数(不包含此实例,因为监听触发在更新nrOfCompletedInstances之前) 57 | Integer complete = (Integer) runtimeService.getVariable(executionId, "nrOfCompletedInstances"); 58 | // 所有实例数 59 | Integer all = (Integer) runtimeService.getVariable(executionId, "nrOfInstances"); 60 | 61 | // 全部实例审批完成 62 | if ((complete + 1) == all) { 63 | int result = (positive != null && positive == (int) all) ? 1 : 0; 64 | // 会签结束,设置参数result,下个任务为申请 65 | runtimeService.setVariable(executionId, "result", result); 66 | // 下个任务 67 | String processInstanceId = delegateTask.getProcessInstanceId(); 68 | Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult(); 69 | System.out.println("下个任务编码:" + task.getId() + ",下个任务名称:" + task.getName()); 70 | } 71 | 72 | // 更新通过实例数 73 | runtimeService.setVariable(executionId, "nrOfPositiveInstances", positive); 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/main/resources/processes/listener/listener.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.5.RELEASE 10 | 11 | 12 | 13 | 4.0.0 14 | com.jellyleo.activiti 15 | activiti7 16 | 1.0.0 17 | activiti7 18 | 19 | 20 | 21 | 1.8 22 | 7.1.0.M2 23 | 3.1.1 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.activiti.dependencies 31 | activiti-dependencies 32 | ${activiti.version} 33 | import 34 | pom 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-web 48 | 49 | 50 | 51 | mysql 52 | mysql-connector-java 53 | 54 | 55 | 56 | org.mybatis.spring.boot 57 | mybatis-spring-boot-starter 58 | 2.0.1 59 | 60 | 61 | 62 | com.alibaba 63 | druid-spring-boot-starter 64 | 1.1.18 65 | 66 | 67 | 68 | org.apache.xmlgraphics 69 | batik-all 70 | 1.10 71 | 72 | 73 | org.activiti 74 | activiti-spring-boot-starter 75 | 76 | 77 | org.activiti 78 | activiti-json-converter 79 | 80 | 81 | org.activiti 82 | activiti-image-generator 83 | 84 | 85 | 86 | 87 | org.projectlombok 88 | lombok 89 | 90 | 91 | 92 | 93 | com.alibaba 94 | fastjson 95 | 1.2.62 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/main/resources/processes/leave/leave.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 0&&day<=3}]]> 12 | 13 | 14 | 15 | 3}]]> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/main/resources/processes/exclusive-gateway/exclusive-gateway.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 0}]]> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/resources/processes/countersign/countersign.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ${nrOfInstances == nrOfCompletedInstances} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/controller/HistoricController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.controller; 7 | 8 | import java.util.List; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.activiti.engine.history.HistoricActivityInstance; 14 | import org.activiti.engine.history.HistoricProcessInstance; 15 | import org.activiti.engine.history.HistoricTaskInstance; 16 | import org.activiti.engine.history.HistoricVariableInstance; 17 | import org.springframework.stereotype.Controller; 18 | import org.springframework.util.StringUtils; 19 | import org.springframework.web.bind.annotation.RequestMapping; 20 | import org.springframework.web.bind.annotation.ResponseBody; 21 | 22 | /** 23 | * 功能描述:历史控制器 24 | * 25 | * @author Jelly 26 | * @created 2019年11月19日 27 | * @version 1.0.0 28 | */ 29 | @Controller 30 | @RequestMapping("/history") 31 | public class HistoricController extends BaseController { 32 | 33 | /** 34 | * 35 | * 功能描述:查询历史流程实例 36 | * 37 | * @param request 38 | * @param response 39 | * @see [相关类/方法](可选) 40 | * @since [产品/模块版本](可选) 41 | */ 42 | @RequestMapping(value = "/process") 43 | @ResponseBody 44 | public String historicProcess(HttpServletRequest request, HttpServletResponse response) { 45 | 46 | try { 47 | List list = historyService.createHistoricProcessInstanceQuery() 48 | .orderByProcessInstanceStartTime().asc()// 排序 49 | .list(); 50 | if (list != null && list.size() > 0) { 51 | for (HistoricProcessInstance hpi : list) { 52 | System.out.println("流程定义ID:" + hpi.getProcessDefinitionId()); 53 | System.out.println("流程实例ID:" + hpi.getId()); 54 | System.out.println("开始时间:" + hpi.getStartTime()); 55 | System.out.println("结束时间:" + hpi.getEndTime()); 56 | System.out.println("流程持续时间:" + hpi.getDurationInMillis()); 57 | System.out.println("*****************************************************************************"); 58 | } 59 | } 60 | } catch (Exception e) { 61 | return "fail"; 62 | } 63 | return "success"; 64 | } 65 | 66 | /** 67 | * 68 | * 功能描述:查询流程历史步骤 69 | * 70 | * @param request 71 | * @param response 72 | * @see [相关类/方法](可选) 73 | * @since [产品/模块版本](可选) 74 | */ 75 | @RequestMapping(value = "/activity") 76 | @ResponseBody 77 | public String historicActivity(HttpServletRequest request, HttpServletResponse response) { 78 | 79 | String processInstanceId = request.getParameter("processInstanceId"); 80 | 81 | if (StringUtils.isEmpty(processInstanceId)) { 82 | return "param error"; 83 | } 84 | 85 | try { 86 | List list = historyService.createHistoricActivityInstanceQuery() 87 | .processInstanceId(processInstanceId).list(); 88 | if (list != null && list.size() > 0) { 89 | for (HistoricActivityInstance hai : list) { 90 | System.out.println(hai.getId()); 91 | System.out.println("步骤ID:" + hai.getActivityId()); 92 | System.out.println("步骤名称:" + hai.getActivityName()); 93 | System.out.println("执行人:" + hai.getAssignee()); 94 | System.out.println("*****************************************************************************"); 95 | } 96 | } 97 | } catch (Exception e) { 98 | return "fail"; 99 | } 100 | return "success"; 101 | } 102 | 103 | /** 104 | * 105 | * 功能描述:查询流程执行任务记录 106 | * 107 | * @param request 108 | * @param response 109 | * @see [相关类/方法](可选) 110 | * @since [产品/模块版本](可选) 111 | */ 112 | @RequestMapping(value = "/task") 113 | @ResponseBody 114 | public String historicTask(HttpServletRequest request, HttpServletResponse response) { 115 | 116 | String processInstanceId = request.getParameter("processInstanceId"); 117 | 118 | if (StringUtils.isEmpty(processInstanceId)) { 119 | return "param error"; 120 | } 121 | 122 | try { 123 | List list = historyService.createHistoricTaskInstanceQuery() 124 | .processInstanceId(processInstanceId).list(); 125 | if (list != null && list.size() > 0) { 126 | for (HistoricTaskInstance hti : list) { 127 | System.out.println("任务ID:" + hti.getId()); 128 | System.out.println("任务名称:" + hti.getName()); 129 | System.out.println("流程定义ID:" + hti.getProcessDefinitionId()); 130 | System.out.println("办理人:" + hti.getAssignee()); 131 | System.out.println("*****************************************************************************"); 132 | } 133 | } 134 | } catch (Exception e) { 135 | return "fail"; 136 | } 137 | return "success"; 138 | } 139 | 140 | /** 141 | * 142 | * 功能描述:查询流程历史变量 143 | * 144 | * @param request 145 | * @param response 146 | * @see [相关类/方法](可选) 147 | * @since [产品/模块版本](可选) 148 | */ 149 | @RequestMapping(value = "/variable") 150 | @ResponseBody 151 | public String historicVariable(HttpServletRequest request, HttpServletResponse response) { 152 | 153 | String processInstanceId = request.getParameter("processInstanceId"); 154 | 155 | if (StringUtils.isEmpty(processInstanceId)) { 156 | return "param error"; 157 | } 158 | 159 | try { 160 | List list = historyService.createHistoricVariableInstanceQuery() 161 | .processInstanceId(processInstanceId).list(); 162 | if (list != null && list.size() > 0) { 163 | System.out.println("流程实例ID:" + processInstanceId); 164 | for (HistoricVariableInstance hvi : list) { 165 | System.out.println("任务ID:" + hvi.getTaskId()); 166 | System.out.println("变量:[" + hvi.getVariableName() + "=" + hvi.getValue() + "]"); 167 | System.out.println("*****************************************************************************"); 168 | } 169 | } 170 | 171 | } catch (Exception e) { 172 | return "fail"; 173 | } 174 | return "success"; 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/controller/TaskController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.controller; 7 | 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | import org.activiti.engine.runtime.ProcessInstance; 16 | import org.activiti.engine.task.DelegationState; 17 | import org.activiti.engine.task.Task; 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.util.CollectionUtils; 20 | import org.springframework.util.StringUtils; 21 | import org.springframework.web.bind.annotation.RequestMapping; 22 | import org.springframework.web.bind.annotation.ResponseBody; 23 | 24 | import com.alibaba.fastjson.JSON; 25 | import com.jellyleo.activiti.entity.CommonVariable; 26 | import com.jellyleo.activiti.util.BeanUtil; 27 | 28 | /** 29 | * 功能描述:任务控制器 30 | * 31 | * @author Jelly 32 | * @created 2019年11月19日 33 | * @version 1.0.0 34 | */ 35 | @Controller 36 | @RequestMapping("/task") 37 | public class TaskController extends BaseController { 38 | 39 | /** 40 | * 41 | * 功能描述:查询任务 42 | * 43 | * @param request 44 | * @param response 45 | * @see [相关类/方法](可选) 46 | * @since [产品/模块版本](可选) 47 | */ 48 | @RequestMapping(value = "/query") 49 | @ResponseBody 50 | public String taskQuery(HttpServletRequest request, HttpServletResponse response) { 51 | 52 | try { 53 | List list = taskService.createTaskQuery()// 创建任务查询对象 54 | .list(); 55 | if (list != null && list.size() > 0) { 56 | for (Task task : list) { 57 | System.out.println("任务ID:" + task.getId()); 58 | System.out.println("任务名称:" + task.getName()); 59 | System.out.println("任务的创建时间:" + task.getCreateTime()); 60 | System.out.println("任务的办理人:" + task.getAssignee()); 61 | System.out.println("流程实例ID:" + task.getProcessInstanceId()); 62 | System.out.println("执行对象ID:" + task.getExecutionId()); 63 | System.out.println("流程定义ID:" + task.getProcessDefinitionId()); 64 | System.out.println("*****************************************************************************"); 65 | } 66 | } 67 | } catch (Exception e) { 68 | return "fail"; 69 | } 70 | return "success"; 71 | } 72 | 73 | /** 74 | * 75 | * 功能描述:查询当前任务 76 | * 77 | * @param request 78 | * @param response 79 | * @see [相关类/方法](可选) 80 | * @since [产品/模块版本](可选) 81 | */ 82 | @RequestMapping(value = "/get") 83 | @ResponseBody 84 | public String getTask(HttpServletRequest request, HttpServletResponse response) { 85 | 86 | String processInstanceId = request.getParameter("processInstanceId"); 87 | 88 | if (StringUtils.isEmpty(processInstanceId)) { 89 | return "param error"; 90 | } 91 | 92 | try { 93 | Task task = taskService.createTaskQuery()// 创建查询对象 94 | .processInstanceId(processInstanceId)// 通过流程实例id来查询当前任务 95 | .singleResult();// 获取单个查询结果 96 | if (task == null) { 97 | System.out.println("流程已结束"); 98 | System.out.println("流程实例ID:" + processInstanceId); 99 | System.out.println("*****************************************************************************"); 100 | return "success"; 101 | } 102 | System.out.println("任务ID:" + task.getId()); 103 | System.out.println("任务名称:" + task.getName()); 104 | System.out.println("任务的创建时间:" + task.getCreateTime()); 105 | System.out.println("任务的办理人:" + task.getAssignee()); 106 | System.out.println("流程实例ID:" + task.getProcessInstanceId()); 107 | System.out.println("执行对象ID:" + task.getExecutionId()); 108 | System.out.println("流程定义ID:" + task.getProcessDefinitionId()); 109 | System.out.println("*****************************************************************************"); 110 | } catch (Exception e) { 111 | return "fail"; 112 | } 113 | return "success"; 114 | } 115 | 116 | /** 117 | * 118 | * 功能描述:查询当前全部任务 119 | * 120 | * @param request 121 | * @param response 122 | * @see [相关类/方法](可选) 123 | * @since [产品/模块版本](可选) 124 | */ 125 | @RequestMapping(value = "/list") 126 | @ResponseBody 127 | public String listTask(HttpServletRequest request, HttpServletResponse response) { 128 | 129 | String processInstanceId = request.getParameter("processInstanceId"); 130 | 131 | if (StringUtils.isEmpty(processInstanceId)) { 132 | return "param error"; 133 | } 134 | 135 | try { 136 | List taskList = taskService.createTaskQuery()// 创建查询对象 137 | .processInstanceId(processInstanceId)// 通过流程实例id来查询当前任务 138 | .list();// 获取单个查询结果 139 | if (CollectionUtils.isEmpty(taskList)) { 140 | System.out.println("流程已结束"); 141 | System.out.println("流程实例ID:" + processInstanceId); 142 | System.out.println("*****************************************************************************"); 143 | return "success"; 144 | } 145 | 146 | taskList.forEach(task -> { 147 | System.out.println("任务ID:" + task.getId()); 148 | System.out.println("任务名称:" + task.getName()); 149 | System.out.println("任务的创建时间:" + task.getCreateTime()); 150 | System.out.println("任务的办理人:" + task.getAssignee()); 151 | System.out.println("流程实例ID:" + task.getProcessInstanceId()); 152 | System.out.println("执行对象ID:" + task.getExecutionId()); 153 | System.out.println("流程定义ID:" + task.getProcessDefinitionId()); 154 | System.out.println("*****************************************************************************"); 155 | }); 156 | 157 | } catch (Exception e) { 158 | return "fail"; 159 | } 160 | return "success"; 161 | } 162 | 163 | /** 164 | * 165 | * 功能描述:查询进行中任务 166 | * 167 | * @param request 168 | * @param response 169 | * @see [相关类/方法](可选) 170 | * @since [产品/模块版本](可选) 171 | */ 172 | @RequestMapping(value = "/get/run") 173 | @ResponseBody 174 | public String getRun(HttpServletRequest request, HttpServletResponse response) { 175 | 176 | String processInstanceId = request.getParameter("processInstanceId"); 177 | 178 | if (StringUtils.isEmpty(processInstanceId)) { 179 | return "param error"; 180 | } 181 | 182 | try { 183 | ProcessInstance process = runtimeService.createProcessInstanceQuery()// 获取查询对象 184 | .processInstanceId(processInstanceId)// 根据id查询流程实例 185 | .singleResult();// 获取查询结果,如果为空,说明这个流程已经执行完毕,否则,获取任务并执行 186 | if (process == null) { 187 | System.out.println("流程已结束"); 188 | System.out.println("流程实例ID:" + processInstanceId); 189 | System.out.println("*****************************************************************************"); 190 | return "success"; 191 | } 192 | } catch (Exception e) { 193 | return "fail"; 194 | } 195 | return "success"; 196 | } 197 | 198 | /** 199 | * 200 | * 功能描述:完成任务 201 | * 202 | * @param request 203 | * @param response 204 | * @see [相关类/方法](可选) 205 | * @since [产品/模块版本](可选) 206 | */ 207 | @RequestMapping(value = "/complete") 208 | @ResponseBody 209 | public String complete(HttpServletRequest request, HttpServletResponse response) { 210 | 211 | String taskId = request.getParameter("taskid"); 212 | String variable = request.getParameter("variable"); 213 | 214 | if (StringUtils.isEmpty(taskId)) { 215 | return "param error"; 216 | } 217 | 218 | try { 219 | Map variables = new HashMap<>(); 220 | if (!StringUtils.isEmpty(variable)) { 221 | CommonVariable variablesEntity = JSON.parseObject(variable, CommonVariable.class); 222 | variables = BeanUtil.beanToMap(variablesEntity); 223 | } 224 | // // 设置流程参数(单) 225 | // taskService.setVariable(taskId, key, value); 226 | // 设置流程参数(多) 227 | taskService.setVariables(taskId, variables); 228 | 229 | // 若是委托任务,请先解决委托任务 230 | Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); 231 | if (DelegationState.PENDING.equals(task.getDelegationState())) { 232 | return "resolve delegation first"; 233 | } 234 | taskService.complete(taskId); 235 | System.out.println("任务完成"); 236 | System.out.println("任务ID:" + taskId); 237 | System.out.println("任务处理结果:" + variables); 238 | System.out.println("*****************************************************************************"); 239 | } catch (Exception e) { 240 | return "fail"; 241 | } 242 | return "success"; 243 | } 244 | 245 | /** 246 | * 247 | * 功能描述:任务委托 248 | * 249 | * @param request 250 | * @param response 251 | * @see [相关类/方法](可选) 252 | * @since [产品/模块版本](可选) 253 | */ 254 | @RequestMapping(value = "/assignee") 255 | @ResponseBody 256 | public String assignee(HttpServletRequest request, HttpServletResponse response) { 257 | 258 | String taskId = request.getParameter("taskid"); 259 | String assignee = request.getParameter("assignee"); 260 | 261 | if (StringUtils.isEmpty(taskId) || StringUtils.isEmpty(assignee)) { 262 | return "param error"; 263 | } 264 | 265 | try { 266 | taskService.delegateTask(taskId, assignee); 267 | System.out.println("任务已委托给:" + assignee); 268 | System.out.println("*****************************************************************************"); 269 | } catch (Exception e) { 270 | return "fail"; 271 | } 272 | return "success"; 273 | } 274 | 275 | /** 276 | * 277 | * 功能描述:解决委托任务 278 | * 279 | * @param request 280 | * @param response 281 | * @see [相关类/方法](可选) 282 | * @since [产品/模块版本](可选) 283 | */ 284 | @RequestMapping(value = "/resolve") 285 | @ResponseBody 286 | public String resolve(HttpServletRequest request, HttpServletResponse response) { 287 | 288 | String taskId = request.getParameter("taskid"); 289 | String variable = request.getParameter("variable"); 290 | 291 | if (StringUtils.isEmpty(taskId)) { 292 | return "param error"; 293 | } 294 | 295 | try { 296 | Map variables = new HashMap<>(); 297 | if (!StringUtils.isEmpty(variable)) { 298 | CommonVariable variablesEntity = JSON.parseObject(variable, CommonVariable.class); 299 | variables = BeanUtil.beanToMap(variablesEntity); 300 | } 301 | // // 设置流程参数(单) 302 | // taskService.setVariable(taskId, key, value); 303 | // 设置流程参数(多) 304 | taskService.setVariables(taskId, variables); 305 | 306 | // 根据taskId提取任务 307 | Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); 308 | if (task.getOwner() != null && !task.getOwner().equals("null")) { 309 | DelegationState delegationState = task.getDelegationState(); 310 | if (delegationState.equals(DelegationState.RESOLVED)) { 311 | System.out.println("此委托任务已是完结状态"); 312 | } else if (delegationState.equals(DelegationState.PENDING)) { 313 | // 如果是委托任务需要做处理 314 | taskService.resolveTask(taskId, variables); 315 | } else { 316 | System.out.println("此任务不是委托任务"); 317 | } 318 | } 319 | System.out.println("委托任务处理完毕"); 320 | System.out.println("*****************************************************************************"); 321 | } catch (Exception e) { 322 | return "fail"; 323 | } 324 | return "success"; 325 | } 326 | 327 | } 328 | -------------------------------------------------------------------------------- /src/main/resources/processes/shopping/shopping.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /src/main/resources/processes/claim-expense/claim-expense.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | =1000}]]> 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/main/java/com/jellyleo/activiti/controller/ProcessController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jellyleo on 2019年12月16日 3 | * Copyright © 2019 jellyleo.com 4 | * All rights reserved. 5 | */ 6 | package com.jellyleo.activiti.controller; 7 | 8 | import java.io.InputStream; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.zip.ZipInputStream; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | import org.activiti.engine.repository.Deployment; 18 | import org.activiti.engine.repository.ProcessDefinition; 19 | import org.activiti.engine.runtime.ProcessInstance; 20 | import org.springframework.stereotype.Controller; 21 | import org.springframework.util.StringUtils; 22 | import org.springframework.web.bind.annotation.RequestMapping; 23 | import org.springframework.web.bind.annotation.ResponseBody; 24 | 25 | import com.alibaba.fastjson.JSON; 26 | import com.jellyleo.activiti.entity.CommonVariable; 27 | import com.jellyleo.activiti.util.BeanUtil; 28 | 29 | /** 30 | * 功能描述:流程控制器 31 | * 32 | * @author Jelly 33 | * @created 2019年11月19日 34 | * @version 1.0.0 35 | */ 36 | @Controller 37 | @RequestMapping("/process") 38 | public class ProcessController extends BaseController { 39 | 40 | /** 41 | * 42 | * 功能描述:classpath部署流程 43 | * 44 | * @param request 45 | * @param response 46 | * @see [相关类/方法](可选) 47 | * @since [产品/模块版本](可选) 48 | */ 49 | @RequestMapping(value = "/deploy") 50 | @ResponseBody 51 | public String deploy(HttpServletRequest request, HttpServletResponse response) { 52 | 53 | String name = request.getParameter("name"); 54 | String resource = request.getParameter("resource"); 55 | 56 | if (StringUtils.isEmpty(resource) || StringUtils.isEmpty(name)) { 57 | return "param error"; 58 | } 59 | 60 | try { 61 | // 创建一个部署对象 62 | Deployment deploy = repositoryService.createDeployment().name(name) 63 | .addClasspathResource("processes/" + resource).deploy(); 64 | System.out.println("部署成功:" + deploy.getId()); 65 | System.out.println("*****************************************************************************"); 66 | } catch (Exception e) { 67 | return "fail"; 68 | } 69 | return "success"; 70 | } 71 | 72 | /** 73 | * 74 | * 功能描述:zip部署流程 75 | * 76 | * @param request 77 | * @param response 78 | * @see [相关类/方法](可选) 79 | * @since [产品/模块版本](可选) 80 | */ 81 | @RequestMapping(value = "/deploy/zip") 82 | @ResponseBody 83 | public String deployZip(HttpServletRequest request, HttpServletResponse response) { 84 | 85 | String name = request.getParameter("name"); 86 | String zip = request.getParameter("zip"); 87 | 88 | if (StringUtils.isEmpty(zip) || StringUtils.isEmpty(name)) { 89 | return "param error"; 90 | } 91 | 92 | try { 93 | InputStream in = this.getClass().getClassLoader().getResourceAsStream("processes/" + zip); 94 | ZipInputStream zipInputStream = new ZipInputStream(in); 95 | Deployment deployment = repositoryService// 与流程定义和部署对象相关的Service 96 | .createDeployment()// 创建一个部署对象 97 | .name(name)// 添加部署名称 98 | .addZipInputStream(zipInputStream)// 完成zip文件的部署 99 | .deploy();// 完成部署 100 | System.out.println("部署ID:" + deployment.getId()); 101 | System.out.println("部署名称:" + deployment.getName()); 102 | System.out.println("*****************************************************************************"); 103 | } catch (Exception e) { 104 | return "fail"; 105 | } 106 | return "success"; 107 | } 108 | 109 | /** 110 | * 111 | * 功能描述:查询流程定义 112 | * 113 | * @param request 114 | * @param response 115 | * @see [相关类/方法](可选) 116 | * @since [产品/模块版本](可选) 117 | */ 118 | @RequestMapping(value = "/definition/query") 119 | @ResponseBody 120 | public String processDefinition(HttpServletRequest request, HttpServletResponse response) { 121 | 122 | try { 123 | List list = repositoryService// 与流程定义和部署对象相关的Service 124 | .createProcessDefinitionQuery()// 创建一个流程定义查询 125 | /* 指定查询条件,where条件 */ 126 | // .deploymentId(deploymentId)//使用部署对象ID查询 127 | // .processDefinitionId(processDefinitionId)//使用流程定义ID查询 128 | // .processDefinitionKey(processDefinitionKey)//使用流程定义的KEY查询 129 | // .processDefinitionNameLike(processDefinitionNameLike)//使用流程定义的名称模糊查询 130 | 131 | /* 排序 */ 132 | .orderByProcessDefinitionVersion().asc()// 按照版本的升序排列 133 | // .orderByProcessDefinitionName().desc()//按照流程定义的名称降序排列 134 | 135 | .list();// 返回一个集合列表,封装流程定义 136 | // .singleResult();//返回唯一结果集 137 | // .count();//返回结果集数量 138 | // .listPage(firstResult, maxResults)//分页查询 139 | 140 | if (list != null && list.size() > 0) { 141 | for (ProcessDefinition processDefinition : list) { 142 | System.out.println("流程定义ID:" + processDefinition.getId());// 流程定义的key+版本+随机生成数 143 | System.out.println("流程定义名称:" + processDefinition.getName());// 对应HelloWorld.bpmn文件中的name属性值 144 | System.out.println("流程定义的key:" + processDefinition.getKey());// 对应HelloWorld.bpmn文件中的id属性值 145 | System.out.println("流程定义的版本:" + processDefinition.getVersion());// 当流程定义的key值相同的情况下,版本升级,默认从1开始 146 | System.out.println("资源名称bpmn文件:" + processDefinition.getResourceName()); 147 | System.out.println("资源名称png文件:" + processDefinition.getDiagramResourceName()); 148 | System.out.println("部署对象ID:" + processDefinition.getDeploymentId()); 149 | System.out.println("*****************************************************************************"); 150 | } 151 | } 152 | } catch (Exception e) { 153 | return "fail"; 154 | } 155 | return "success"; 156 | } 157 | 158 | /** 159 | * 160 | * 功能描述:删除流程定义 161 | * 162 | * @param request 163 | * @param response 164 | * @see [相关类/方法](可选) 165 | * @since [产品/模块版本](可选) 166 | */ 167 | @RequestMapping(value = "/deployment/delete") 168 | @ResponseBody 169 | public String deleteDeployment(HttpServletRequest request, HttpServletResponse response) { 170 | 171 | String deploymentId = request.getParameter("deployId"); 172 | 173 | if (StringUtils.isEmpty(deploymentId)) { 174 | return "param error"; 175 | } 176 | 177 | try { 178 | // // 不带级联的删除:只能删除没有启动的流程,如果流程启动,则抛出异常 179 | // repositoryService.deleteDeployment(deploymentId); 180 | // 能级联的删除:能删除启动的流程,会删除和当前规则相关的所有信息,正在执行的信息,也包括历史信息 181 | repositoryService.deleteDeployment(deploymentId, true); 182 | System.out.println("删除成功:" + deploymentId); 183 | System.out.println("*****************************************************************************"); 184 | } catch (Exception e) { 185 | return "fail"; 186 | } 187 | return "success"; 188 | } 189 | 190 | /** 191 | * 192 | * 功能描述:启动流程 193 | * 194 | * @param request 195 | * @param response 196 | * @see [相关类/方法](可选) 197 | * @since [产品/模块版本](可选) 198 | */ 199 | @RequestMapping(value = "/start") 200 | @ResponseBody 201 | public String start(HttpServletRequest request, HttpServletResponse response) { 202 | 203 | String processDefinitionKey = request.getParameter("processId"); 204 | String variable = request.getParameter("variable"); 205 | 206 | if (StringUtils.isEmpty(processDefinitionKey)) { 207 | return "param error"; 208 | } 209 | 210 | try { 211 | Map variables = new HashMap<>(); 212 | if (!StringUtils.isEmpty(variable)) { 213 | CommonVariable variablesEntity = JSON.parseObject(variable, CommonVariable.class); 214 | variables = BeanUtil.beanToMap(variablesEntity); 215 | } 216 | 217 | ProcessInstance instance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables); 218 | // // Businesskey:业务标识,通常为业务表的主键,业务标识和流程实例一一对应。业务标识来源于业务系统。存储业务标识就是根据业务标识来关联查询业务系统的数据 219 | // ProcessInstance instance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, 220 | // variables); 221 | 222 | System.out.println("流程实例ID:" + instance.getId()); 223 | System.out.println("流程定义ID:" + instance.getProcessDefinitionId()); 224 | System.out.println("*****************************************************************************"); 225 | } catch (Exception e) { 226 | e.printStackTrace(); 227 | return "fail"; 228 | } 229 | return "success"; 230 | } 231 | 232 | /** 233 | * 234 | * 功能描述:删除流程 235 | * 236 | * @param request 237 | * @param response 238 | * @see [相关类/方法](可选) 239 | * @since [产品/模块版本](可选) 240 | */ 241 | @RequestMapping(value = "/delete") 242 | @ResponseBody 243 | public String deleteProcess(HttpServletRequest request, HttpServletResponse response) { 244 | 245 | String processId = request.getParameter("processId"); 246 | 247 | if (StringUtils.isEmpty(processId)) { 248 | return "param error"; 249 | } 250 | 251 | try { 252 | runtimeService.deleteProcessInstance(processId, "流程已完毕"); 253 | System.out.println("终止流程"); 254 | System.out.println("*****************************************************************************"); 255 | } catch (Exception e) { 256 | return "fail"; 257 | } 258 | return "success"; 259 | } 260 | 261 | /** 262 | * 263 | * 功能描述:流程实例挂起 264 | * 265 | * @param request 266 | * @param response 267 | * @see [相关类/方法](可选) 268 | * @since [产品/模块版本](可选) 269 | */ 270 | @RequestMapping(value = "/instance/suspend") 271 | @ResponseBody 272 | public String suspendProcessInstance(HttpServletRequest request, HttpServletResponse response) { 273 | 274 | String processInstanceId = request.getParameter("processInstanceId"); 275 | 276 | if (StringUtils.isEmpty(processInstanceId)) { 277 | return "param error"; 278 | } 279 | 280 | try { 281 | // 根据一个流程实例的id挂起该流程实例 282 | runtimeService.suspendProcessInstanceById(processInstanceId); 283 | ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() 284 | .processInstanceId(processInstanceId).singleResult(); 285 | System.out.println("流程实例ID:" + processInstance.getId()); 286 | System.out.println("流程定义ID:" + processInstance.getProcessDefinitionId()); 287 | System.out.println("流程实例状态:" + processInstance.isSuspended()); 288 | System.out.println("*****************************************************************************"); 289 | } catch (Exception e) { 290 | return "fail"; 291 | } 292 | return "success"; 293 | } 294 | 295 | /** 296 | * 297 | * 功能描述:流程定义挂起 298 | * 299 | * @param request 300 | * @param response 301 | * @see [相关类/方法](可选) 302 | * @since [产品/模块版本](可选) 303 | */ 304 | @RequestMapping(value = "/definition/suspend") 305 | @ResponseBody 306 | public String suspendProcessDefinition(HttpServletRequest request, HttpServletResponse response) { 307 | 308 | String processDefinitionId = request.getParameter("processDefinitionId"); 309 | 310 | if (StringUtils.isEmpty(processDefinitionId)) { 311 | return "param error"; 312 | } 313 | 314 | try { 315 | // 根据一个流程定义的id挂起该流程实例 316 | repositoryService.suspendProcessDefinitionByKey(processDefinitionId); 317 | ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() 318 | .processDefinitionKey(processDefinitionId).singleResult(); 319 | System.out.println("流程定义ID:" + processDefinition.getId()); 320 | System.out.println("流程定义状态:" + processDefinition.isSuspended()); 321 | System.out.println("*****************************************************************************"); 322 | } catch (Exception e) { 323 | return "fail"; 324 | } 325 | return "success"; 326 | } 327 | 328 | /** 329 | * 330 | * 功能描述:流程实例激活 331 | * 332 | * @param request 333 | * @param response 334 | * @see [相关类/方法](可选) 335 | * @since [产品/模块版本](可选) 336 | */ 337 | @RequestMapping(value = "/instance/activate") 338 | @ResponseBody 339 | public String activateProcessInstance(HttpServletRequest request, HttpServletResponse response) { 340 | 341 | String processInstanceId = request.getParameter("processInstanceId"); 342 | 343 | if (StringUtils.isEmpty(processInstanceId)) { 344 | return "param error"; 345 | } 346 | 347 | try { 348 | // 根据一个流程实例id激活该流程 349 | runtimeService.activateProcessInstanceById(processInstanceId); 350 | ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() 351 | .processInstanceId(processInstanceId).singleResult(); 352 | System.out.println("流程实例ID:" + processInstance.getId()); 353 | System.out.println("流程定义ID:" + processInstance.getProcessDefinitionId()); 354 | System.out.println("流程实例状态:" + processInstance.isSuspended()); 355 | System.out.println("*****************************************************************************"); 356 | } catch (Exception e) { 357 | return "fail"; 358 | } 359 | return "success"; 360 | } 361 | 362 | /** 363 | * 364 | * 功能描述:流程定义激活 365 | * 366 | * @param request 367 | * @param response 368 | * @see [相关类/方法](可选) 369 | * @since [产品/模块版本](可选) 370 | */ 371 | @RequestMapping(value = "/definition/activate") 372 | @ResponseBody 373 | public String activateProcessDefinition(HttpServletRequest request, HttpServletResponse response) { 374 | 375 | String processDefinitionId = request.getParameter("processDefinitionId"); 376 | 377 | if (StringUtils.isEmpty(processDefinitionId)) { 378 | return "param error"; 379 | } 380 | 381 | try { 382 | // 根据一个流程定义的id挂起该流程实例 383 | repositoryService.activateProcessDefinitionById(processDefinitionId); 384 | ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() 385 | .processDefinitionKey(processDefinitionId).singleResult(); 386 | System.out.println("流程定义ID:" + processDefinition.getId()); 387 | System.out.println("流程定义状态:" + processDefinition.isSuspended()); 388 | System.out.println("*****************************************************************************"); 389 | } catch (Exception e) { 390 | return "fail"; 391 | } 392 | return "success"; 393 | } 394 | 395 | } 396 | --------------------------------------------------------------------------------