SpringBoot整合Activiti小例子

tech2022-07-07  209

  最近项目中要用到工作流Activiti,刚好有时间可以学习下。这里记录我的学习成果。 概念啥的不说了,直接上干货。   首先在idea中找到actiBPM插件并安装。   接下来,新建一个SpringBoot项目,在pom中加入依赖(这里只列出来activiti的依赖,其他依赖自行添加)

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

在yml文件中进行配置(自行配置数据库相关)

spring: activiti: check-process-definitions: true #自动检查、部署流程定义文件 database-schema-update: true #自动更新数据库结构 history-level: full #保存历史数据级别设置为full最高级别,便于历史数据的追溯 # process-definition-location-prefix: classpath:/processes/ #流程定义文件存放目录 #process-definition-location-suffixes: #流程文件格式 # - **.bpmn20.xml # - **.bpmn

  接下来编写一个配置类,让项目启动的时候自动扫描流程文件

@Configuration public class ActivitiConfig { private final static Logger log = LoggerFactory.getLogger(ActivitiConfig.class); /* * 配置分为以下几步骤 * 1. 创建ActivitiConfig * 2. 使用ActivitiConfig创建ProcessEngineFactoryBean * 3. 使用ProcessEngineFactoryBean创建ProcessEngine对象 * 4. 使用ProcessEngine对象创建需要的服务对象 * */ private Logger logger = LoggerFactory.getLogger(ActivitiConfig.class); private final DataSource dataSource; private final PlatformTransactionManager platformTransactionManager; @Autowired public ActivitiConfig(DataSource dataSource, PlatformTransactionManager platformTransactionManager) { this.dataSource = dataSource; this.platformTransactionManager = platformTransactionManager; } /* *自动扫描process包下的bpmn(流程定义文件)的设置,这样就可以省去了部署 * */ @Bean public SpringProcessEngineConfiguration springProcessEngineConfiguration() { SpringProcessEngineConfiguration spec = new SpringProcessEngineConfiguration(); spec.setDataSource(dataSource); spec.setTransactionManager(platformTransactionManager); spec.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); Resource[] resources = null; // 启动自动部署流程 try { //注意:下面那个路径是你resource文件夹下存放流程图的路径 resources = new PathMatchingResourcePatternResolver().getResources("classpath*:processes/*.bpmn"); } catch (IOException e) { e.printStackTrace(); } spec.setDeploymentResources(resources); return spec; } @Bean public ProcessEngineFactoryBean processEngine() { ProcessEngineFactoryBean processEngineFactoryBean = new ProcessEngineFactoryBean(); processEngineFactoryBean.setProcessEngineConfiguration(springProcessEngineConfiguration()); return processEngineFactoryBean; } @Bean public RepositoryService repositoryService() throws Exception { return processEngine().getObject().getRepositoryService(); } @Bean public RuntimeService runtimeService() throws Exception { return processEngine().getObject().getRuntimeService(); } @Bean public TaskService taskService() throws Exception { return processEngine().getObject().getTaskService(); } @Bean public HistoryService historyService() throws Exception { return processEngine().getObject().getHistoryService(); } }

  接着启动项目,启动完后,你会发现在数据库里面多了一些表 其中:   “GE”代表“General”(通用),用在各种情况下;   “HI”代表“History”(历史),这些表中保存的都是历史数据,Activit默认提供了4种历史级别: none: 不保存任何历史记录,可以提高系统性能; activity:保存所有的流程实例、任务、活动信息; audit:也是Activiti的默认级别,保存所有的流程实例、任务、活动、表单属性; full:最完整的历史记录,除了包含audit级别的信息之外还能保存详细,例如:流程变量。 之前我们设置的就是full

  “ID”代表“Identity”(身份),这些表中保存的都是身份信息   “RE”代表“Repository”(仓库)   “RU”代表“Runtime”(运行时)

  接着开始画图,这是我的一个小例子 代码如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1539766523202" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1539766523202" name="" targetNamespace="http://www.activiti.org/testm1539766523202" typeLanguage="http://www.w3.org/2001/XMLSchema"> <process id="leave1" isClosed="false" isExecutable="true" processType="None"> <startEvent id="_2" name="start"/> <!--因为后面要用到这个参数,所以写成可变的,下面的代码也是一样的--> <userTask activiti:assignee="${agent}" activiti:exclusive="true" id="_5" name="submit"> <documentation id="_5_D_1"><![CDATA[submit]]></documentation> </userTask> <endEvent id="_7" name="EndEvent"/> <sequenceFlow id="_8" sourceRef="_2" targetRef="_5"/> <userTask activiti:assignee="${agent}" activiti:exclusive="true" id="_17" name="approve"> <documentation id="_17_D_1"><![CDATA[approve]]></documentation> </userTask> <exclusiveGateway gatewayDirection="Unspecified" id="_18" name="judge"/> <endEvent id="_20" name="EndEvent"/> <sequenceFlow id="_21" sourceRef="_5" targetRef="_18"/> <sequenceFlow id="_23" sourceRef="_18" targetRef="_17"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag == 'true'}]]></conditionExpression> </sequenceFlow> <userTask activiti:assignee="${agent}" activiti:exclusive="true" id="_10" name="give up"> <documentation id="_10_D_1"><![CDATA[give up]]></documentation> </userTask> <sequenceFlow id="_11" sourceRef="_18" targetRef="_10"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag == 'false'}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="_12" sourceRef="_10" targetRef="_7"/> <sequenceFlow id="_13" sourceRef="_17" targetRef="_20"/> </process> <bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram"> <bpmndi:BPMNPlane bpmnElement="leave1"> <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2"> <dc:Bounds height="32.0" width="32.0" x="110.0" y="225.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5"> <dc:Bounds height="55.0" width="85.0" x="310.0" y="215.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7"> <dc:Bounds height="32.0" width="32.0" x="1030.0" y="215.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_17" id="Shape-_17"> <dc:Bounds height="55.0" width="85.0" x="530.0" y="435.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_18" id="Shape-_18" isMarkerVisible="false"> <dc:Bounds height="32.0" width="32.0" x="555.0" y="225.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_20" id="Shape-_20"> <dc:Bounds height="32.0" width="32.0" x="560.0" y="725.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_10" id="Shape-_10"> <dc:Bounds height="55.0" width="85.0" x="780.0" y="210.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="_17" targetElement="_20"> <di:waypoint x="576.0" y="490.0"/> <di:waypoint x="576.0" y="725.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="_23" id="BPMNEdge__23" sourceElement="_18" targetElement="_17"> <di:waypoint x="571.0" y="257.0"/> <di:waypoint x="571.0" y="435.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_10" targetElement="_7"> <di:waypoint x="865.0" y="237.5"/> <di:waypoint x="1030.0" y="231.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_2" targetElement="_5"> <di:waypoint x="139.22875655532295" y="250.0"/> <di:waypoint x="225.0" y="250.0"/> <di:waypoint x="310.0" y="250.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_18" targetElement="_10"> <di:waypoint x="587.0" y="241.0"/> <di:waypoint x="780.0" y="237.5"/> <bpmndi:BPMNLabel> <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="_21" id="BPMNEdge__21" sourceElement="_5" targetElement="_18"> <di:waypoint x="395.0" y="242.5"/> <di:waypoint x="555.0" y="241.0"/> <bpmndi:BPMNLabel> <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>

之前写配置类的时候,统一扫描的是.bpmn文件,所以不需要转成xml文件。

  开始编写业务代码,这个例子我就直接写到controller里了 开启流程的方法:

@Postapping("/start") public Map startTask(@RequestBody LeaveForm leaveForm){ Map<String, Object> map = new HashMap<>(); //设置流程是启动人 IdentityService identityService = processEngine.getIdentityService(); identityService.setAuthenticatedUserId(leaveForm.getPerson_code()); leaveFormMapper.insertSelective(leaveForm); //设置流程参数,之后用来查看 历史记录,资源文件。。。 //businessKey 业务关联可以用到,这里暂时没有用到 String formId = leaveForm.getId(); String businessKey = "1:FormKey" + "." + formId; map.put("agent",leaveForm.getPerson_code()); map.put("formId",formId); //下面的leave1是之前你画图完成后的 process id="leave1" //map里面的参数是为了给之前xml文件中的assignee="${agent}"赋值 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave1",businessKey,map); leaveForm.setProcInstanId(processInstance.getId()); leaveFormMapper.updateByPrimaryKeySelective(leaveForm); log.info("任务:{}启动",processInstance.getBusinessKey()); Map<String,String> map1 = new HashMap<>(); map1.put("data","任务启动成功!"); return map1; }

上面代码中的LeaveForm用来接收参数,和展示流程数据

CREATE TABLE `leaveform` ( `id` int NOT NULL AUTO_INCREMENT, `procInstId` varchar(255) DEFAULT '' COMMENT '实例id', `person_code` varchar(255) NOT NULL, `person_name` varchar(120) DEFAULT NULL, `agent_Id` varchar(255) NOT NULL, `title` varchar(255) NOT NULL DEFAULT '', `leaveType` int NOT NULL, `leaveContent` varchar(255) NOT NULL, `leaveTime` timestamp NULL DEFAULT NULL COMMENT '生效时间', `expireTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '请假到期时间', `createTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `endTime` timestamp NULL DEFAULT NULL, `reply` varchar(255) DEFAULT NULL, `status` int NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=46; public class LeaveForm { private String id; /** 发起人Id */ private String person_code; private String person_name; /** 实例ID*/ private String procInstanId; /** 代理人ID */ private String agent_Id; /** 标题*/ private String title; /**请假类型 0:事假:1:病假:2:公假*/ private String leaveType; /** 审批发起时间 */ private Date createTime; /**审批完成结束时间*/ private Date endTime; /** 请假生效时间*/ private Date leaveTime; /**到期时间*/ private Date expireTime; /** 备注 */ private String leaveContent; /**回复内容*/ private String reply; /** 状态 */ private Integer status; private Date currDate; private boolean flag; private String taskId; }

启动之后我们就要开始提交,也可以放弃

@PostMapping("/submit") public Map submit(@RequestBody LeaveForm leaveForm){ String proceId = leaveForm.getProcInstanId(); Map<String,Object> map = new HashMap<>(); Task task = taskService.createTaskQuery().processInstanceId(proceId).singleResult(); //获取到参数 String formId = (String)taskService.getVariable(task.getId(),"formId"); map.put("formId",formId); if(leaveForm.isFlag()){ //设置审批人 String assignee = leaveFormMapper.getById(Integer.parseInt(formId)).getAgent_Id(); map.put("agent",assignee); map.put("flag","true"); taskService.complete(task.getId(),map); } else { String assignee = (String)taskService.getVariable(task.getId(),"agent"); map.put("agent",assignee); map.put("flag","false"); taskService.complete(task.getId(),map); Task task1 = taskService.createTaskQuery().processInstanceId(proceId).singleResult(); taskService.complete(task1.getId()); } Map<String,String> map1 = new HashMap<>(); map1.put("data","成功!"); return map1; }

如果选择提交审批人通过下面的方法获取到未审批信息

@GetMapping("/getNotApprove") public Map getNotApprove(LeaveForm leaveForm){ ActTask actTask = new ActTask(); actTask.setAssignee(leaveForm.getPerson_code()); //这是对act_ru_task leaveform进行操作 List<ActTask> list = actTaskMapper.selectACTTaskList(actTask); for(ActTask actTask1 : list){ String formId = (String)taskService.getVariable(actTask1.getId(),"formId"); actTask1.setFormId(formId); } Map<String,Object> map = new HashMap<>(); map.put("data",list); return map; }

审批人进行审批

@PostMapping("/approve") public Map approve(@RequestBody LeaveForm leaveForm){ Map<String,Object> map = new HashMap<>(); Map<String,Object> map1 = new HashMap<>(); map1.put("agent",leaveForm.getPerson_code()); taskService.complete(leaveForm.getTaskId(),map1); //修改状态 leaveFormMapper.updateByPrimaryKeySelective(leaveForm); map.put("data","success"); return map; }
最新回复(0)