大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
Activiti工作流使用之项目实例
文章目录
一、配置文件
1.1 pom依赖引入
<dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-basic</artifactId> <version>6.0.0</version> </dependency>
1.2 application.properties文件
spring.datasource.url= jdbc:mysql://192.168.xx.xx:3306/activiti?useSSL=true spring.datasource.driver-class-name= com.mysql.jdbc.Driver spring.datasource.username= root spring.datasource.password= root123 # mybatis mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl mybatis.configuration.map-underscore-to-camel-case=true mybatis.mapper-locations=classpath:mybatis/*.xml # activiti default configuration true代表自动生成数据库表 spring.activiti.database-schema-update= true # 自动部署验证设置:true-开启(默认)、false-关闭 spring.activiti.check-process-definitions= false spring.activiti.process-definition-location-prefix= classpath:/ # process-definition-location-suffixes: # - **.bpmn # - **.bpmn20.xml spring.activiti.history-level= full spring.activiti.db-history-used= true spring.activiti.db-identity-used= true
二、BPMN文件
这里实例是积分审核流程
<?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/processdef"> <process id="pointsRequestProcesses" name="积分审批流程" isExecutable="true"> <startEvent id="startEventPoint" name="开始" /> <userTask id="userTaskPointsRequest" name="积分申请" activiti:assignee="${applicantUser}" activiti:exclusive="true"> <outgoing>Flow_0z08cw6</outgoing> </userTask> <userTask id="userTaskCompanyApproval" name="省公司审批" activiti:candidateUsers="${reviewerUser}" activiti:exclusive="true"> <incoming>Flow_0z08cw6</incoming> </userTask> <exclusiveGateway id="exclusivegateway02" name="省公司审批exclusivegateway02" /> <endEvent id="endEventPoint" /> <sequenceFlow id="flow1" sourceRef="startEventPoint" targetRef="userTaskPointsRequest" /> <sequenceFlow id="flow4" sourceRef="userTaskCompanyApproval" targetRef="exclusivegateway02" /> <sequenceFlow id="flowrightexclusivegateway2" name="省公司审批通过" sourceRef="exclusivegateway02" targetRef="endEventPoint"> <extensionElements> <activiti:executionListener class="com.awifi.capacity.project.manager.activiti.provider.listener.MyTaskListener" event="take" /> </extensionElements> <conditionExpression xsi:type="tFormalExpression"> ${isOk == true} </conditionExpression> </sequenceFlow> <sequenceFlow id="flowleftexclusivegateway2" name="省公司审批未通过" sourceRef="exclusivegateway02" targetRef="userTaskPointsRequest"> <extensionElements> <activiti:executionListener class="com.awifi.capacity.project.manager.activiti.provider.listener.MyTaskListener" event="take" /> </extensionElements> <conditionExpression xsi:type="tFormalExpression"> ${isOk == false} </conditionExpression> </sequenceFlow> <sequenceFlow id="Flow_0z08cw6" sourceRef="userTaskPointsRequest" targetRef="userTaskCompanyApproval" /> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_pointsRequest"> <bpmndi:BPMNPlane id="BPMNPlane_pointsRequest" bpmnElement="pointsRequestProcesses"> <bpmndi:BPMNEdge id="BPMNEdge_flowleftexclusivegateway2" bpmnElement="flowleftexclusivegateway2"> <omgdi:waypoint x="769.402004454343" y="69.40200445434299" /> <omgdi:waypoint x="383.75" y="162.88046485888214" /> <bpmndi:BPMNLabel> <omgdc:Bounds x="533" y="91" width="88" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="BPMNEdge_flowrightexclusivegateway2" bpmnElement="flowrightexclusivegateway2"> <omgdi:waypoint x="798.9766063630692" y="71.02339363693075" /> <omgdi:waypoint x="1056.795793598862" y="176.69071224016682" /> <bpmndi:BPMNLabel> <omgdc:Bounds x="889" y="99" width="77" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="BPMNEdge_flow4" bpmnElement="flow4"> <omgdi:waypoint x="785" y="135" /> <omgdi:waypoint x="785" y="85" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="BPMNEdge_flow1" bpmnElement="flow1"> <omgdi:waypoint x="189.99732231181528" y="177.7165860350523" /> <omgdi:waypoint x="283.75" y="175.9448818897638" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="Flow_0z08cw6_di" bpmnElement="Flow_0z08cw6"> <omgdi:waypoint x="384" y="175" /> <omgdi:waypoint x="735" y="175" /> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="BPMNShape_startEvent1" bpmnElement="startEventPoint"> <omgdc:Bounds x="160" y="163" width="30" height="30" /> <bpmndi:BPMNLabel> <omgdc:Bounds x="164" y="193" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="BPMNShape_userTask1" bpmnElement="userTaskPointsRequest"> <omgdc:Bounds x="284" y="135" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="BPMNShape_userTask3" bpmnElement="userTaskCompanyApproval"> <omgdc:Bounds x="735" y="135" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="BPMNShape_exclusivegateway02" bpmnElement="exclusivegateway02" isMarkerVisible="true"> <omgdc:Bounds x="765" y="45" width="40" height="40" /> <bpmndi:BPMNLabel> <omgdc:Bounds x="742" y="85" width="86" height="27" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="BPMNShape_sid-5477B73E-9754-4A01-B13D-B6B61CC509E0" bpmnElement="endEventPoint"> <omgdc:Bounds x="1056" y="168" width="28" height="28" /> </bpmndi:BPMNShape> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
三、控制层Controller
初始化流程的DTO
package com.awifi.capacity.project.manager.activiti.provider.bean.dto; import lombok.Data; /** * @ClassName InitiationProcessDTO * @Description TODO * @Author 张鑫 * @Date 2022/5/25 * @Version 1.0 **/ @Data public class InitiationProcessDTO extends TaskApprovalDTO{ String applicantUser; String reviewerUser; String processInstanceId; String processType; String deleteReason; }
任务审核DTO
package com.awifi.capacity.project.manager.activiti.provider.bean.dto; import lombok.Data; import java.util.HashMap; /** * @ClassName TaskApprovalDTO * @Description TODO * @Author 张鑫 * @Date 2022/5/25 * @Version 1.0 **/ @Data public class TaskApprovalDTO { String taskId; String processInstanceId; String userName; String field = "isOk"; String isOk = "true"; String comment = "同意"; HashMap<String, Object> hashMap; }
控制层
package com.awifi.capacity.project.manager.activiti.provider.controller; import com.alibaba.fastjson.JSON; import com.awifi.capacity.project.manager.activiti.provider.bean.dto.InitiationProcessDTO; import com.awifi.capacity.project.manager.activiti.provider.bean.dto.TaskApprovalDTO; import com.awifi.capacity.project.manager.activiti.provider.exception.UserAuthException; import com.awifi.capacity.project.manager.activiti.provider.service.ActivitiService; import com.awifi.capacity.project.manager.common.basic.EnumCode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Optional; /** * @ClassName ActivitiController * @Description activiti * @Author 张鑫 * @Date 2022/5/25 * @Version 1.0 **/ @RestController @RequestMapping("/rest/pm/activiti") public class ActivitiController { @Autowired private ActivitiService activitiService; // 开始流程(新增流程) /** * 开启申请流程 只进行流程启动不进入下一步 * * @Author 张鑫 * @Date 2022/5/25 * @Param [initiationProcessDTO] * @return java.lang.String **/ @PostMapping(value = "/startProcess", produces = "text/html;charset=UTF-8") @ResponseBody public String startProcess(@RequestBody String params) { InitiationProcessDTO initiationProcessDTO = JSON.parseObject(params, InitiationProcessDTO.class); Optional.ofNullable(initiationProcessDTO.getApplicantUser()).orElseThrow(() -> new UserAuthException(EnumCode.ACTIVITI_APPLICANTUSER_ISMISS.getCode(),EnumCode.ACTIVITI_APPLICANTUSER_ISMISS.getText())); Optional.ofNullable(initiationProcessDTO.getProcessType()).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_PROCESSTYPE_ISMISS.getCode(),EnumCode.ACTICITI_PROCESSTYPE_ISMISS.getText())); return activitiService.startProcess(initiationProcessDTO); } /** * 开启申请流程 进行流程启动且进入下一步 * * @Author 张鑫 * @Date 2022/5/25 * @Param [initiationProcessDTO] * @return java.lang.String **/ @PostMapping(value = "/initiationProcess", produces = "text/html;charset=UTF-8") @ResponseBody public String startProcessTo(@RequestBody String params) { InitiationProcessDTO initiationProcessDTO = JSON.parseObject(params, InitiationProcessDTO.class); Optional.ofNullable(initiationProcessDTO.getApplicantUser()).orElseThrow(() -> new UserAuthException(EnumCode.ACTIVITI_APPLICANTUSER_ISMISS.getCode(),EnumCode.ACTIVITI_APPLICANTUSER_ISMISS.getText())); Optional.ofNullable(initiationProcessDTO.getProcessType()).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_PROCESSTYPE_ISMISS.getCode(),EnumCode.ACTICITI_PROCESSTYPE_ISMISS.getText())); Optional.ofNullable(initiationProcessDTO.getReviewerUser()).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_REVIEWERUSER_ISMISS.getCode(),EnumCode.ACTICITI_REVIEWERUSER_ISMISS.getText())); return activitiService.startProcessTo(initiationProcessDTO); } // 流程审批 /** * 任务审批 * * @Author 张鑫 * @Date 2022/5/25 * @Param [taskApprovalDTO] * @return java.lang.String **/ @PutMapping("/approval") @ResponseBody public String approvalTask(@RequestBody String params) { TaskApprovalDTO taskApprovalDTO = JSON.parseObject(params, TaskApprovalDTO.class); Optional.ofNullable(taskApprovalDTO.getUserName()).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_USERNAME_IS_MISS.getCode(),EnumCode.ACTICITI_USERNAME_IS_MISS.getText())); Optional.ofNullable(taskApprovalDTO.getProcessInstanceId()).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_PROCESSINSTANCEID_IS_MISS.getCode(),EnumCode.ACTICITI_PROCESSINSTANCEID_IS_MISS.getText())); return activitiService.approvalTask(taskApprovalDTO); } // 查询流程列表 /** * 查询我流程列表 * * @return java.lang.String * @Author 张鑫 * @Date 2022/5/25 * @Param [user] **/ @GetMapping("/myProcess") @ResponseBody public String myProcess(String userName) { Optional.ofNullable(userName).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_USERNAME_IS_MISS.getCode(),EnumCode.ACTICITI_USERNAME_IS_MISS.getText())); return activitiService.myProcess(userName); } // 查询单个流程的详情 /** * 查询流程详情 * * @return java.lang.String * @Author 张鑫 * @Date 2022/5/25 * @Param [user] **/ @GetMapping("/processInfo") @ResponseBody public String processInfo(String processInstanceId) { Optional.ofNullable(processInstanceId).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_PROCESSINSTANCEID_IS_MISS.getCode(),EnumCode.ACTICITI_PROCESSINSTANCEID_IS_MISS.getText())); return activitiService.processInfo(processInstanceId); } // 删除流程 /** * 删除流程 包含历史记录 * * @Author 张鑫 * @Date 2022/5/26 * @Param [processInstanceId] * @return java.lang.String **/ @DeleteMapping("/deleteProcess") @ResponseBody public String deleteProcess(@RequestBody String params) { InitiationProcessDTO deleteProcessDto = JSON.parseObject(params, InitiationProcessDTO.class); Optional.ofNullable(deleteProcessDto.getProcessInstanceId()).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_PROCESSINSTANCEID_IS_MISS.getCode(),EnumCode.ACTICITI_PROCESSINSTANCEID_IS_MISS.getText())); return activitiService.deleteProcess(deleteProcessDto); } // 查询我待审核的任务 /** * 查询我的待审核的任务列表 * * @return java.lang.String * @Author 张鑫 * @Date 2022/5/25 * @Param [user] **/ @GetMapping("/taskCandidateIsMe") @ResponseBody public String taskCandidateIsMe(String userName) { Optional.ofNullable(userName).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_USERNAME_IS_MISS.getCode(),EnumCode.ACTICITI_USERNAME_IS_MISS.getText())); return activitiService.taskCandidateIsMe(userName); } /** * 查询我是代理人的任务列表(用户重新申请时调用进行查询我的任务列表来获取taskid) * * @Author 张鑫 * @Date 2022/5/25 * @Param [user] * @return java.lang.String **/ @GetMapping("/taskAssigneeIsMe") @ResponseBody public String taskAssigneeIsMe(String userName) { Optional.ofNullable(userName).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_USERNAME_IS_MISS.getCode(),EnumCode.ACTICITI_USERNAME_IS_MISS.getText())); return activitiService.taskAssigneeIsMe(userName); } // 查询我开始的流程下的所有任务 /** * 查看用户发起的所有流程下的任务 * * @Author 张鑫 * @Date 2022/5/25 * @Param [user] * @return java.lang.String **/ @GetMapping("/taskListByInitiator") @ResponseBody public String taskListByInitiator(String userName) { Optional.ofNullable(userName).orElseThrow(() -> new UserAuthException(EnumCode.ACTICITI_USERNAME_IS_MISS.getCode(),EnumCode.ACTICITI_USERNAME_IS_MISS.getText())); return activitiService.taskListByInitiator(userName); } // 查询我未完成的流程 // 查询我完成的流程 }
四、实现类Service
4.1 流程启动(不进入下一环节)
@Override public String startProcess(InitiationProcessDTO initiationProcessDTO) { ResponseJson responseJson = new ResponseJson(); // 根据流程类型获取processFile String processFile = EnumProcess.getEnumText(initiationProcessDTO.getProcessType()); if (processFile == null) { throw new UserAuthException(EnumCode.ACTICITI_PROCESSTYPE_ISWORRY.getCode(), EnumCode.ACTICITI_PROCESSTYPE_ISWORRY.getText()); } // 生成businessKey String businessKey = IDGenerator.getID(); identityService.setAuthenticatedUserId(initiationProcessDTO.getApplicantUser()); // 申请人写入 Map<String, Object> map = new HashMap<>(); map.put("applicantUser", initiationProcessDTO.getApplicantUser()); map.put("reviewerUser", initiationProcessDTO.getReviewerUser()); // 流程启动 ProcessInstance processInstance = ActivitiManager.startProcess(processFile, initiationProcessDTO.getProcessType(), activitiDeployMapper, businessKey, map); responseJson.setData(processInstance.getProcessInstanceId()); return JSON.toJSONString(responseJson); }
4.2 流程启动(直接进入下一环节)
@Override public String startProcessTo(InitiationProcessDTO initiationProcessDTO) { ResponseJson responseJson = new ResponseJson(); // 根据流程类型获取processFile String processFile = EnumProcess.getEnumText(initiationProcessDTO.getProcessType()); if (processFile == null) { throw new UserAuthException(EnumCode.ACTICITI_PROCESSTYPE_ISWORRY.getCode(), EnumCode.ACTICITI_PROCESSTYPE_ISWORRY.getText()); } // 生成businessKey String businessKey = IDGenerator.getID(); // 申请人写入 Map<String, Object> map = new HashMap<>(); map.put("applicantUser", initiationProcessDTO.getApplicantUser()); map.put("reviewerUser", initiationProcessDTO.getReviewerUser()); identityService.setAuthenticatedUserId(initiationProcessDTO.getApplicantUser()); // 流程启动 ProcessInstance processInstance = ActivitiManager.startProcess(processFile, initiationProcessDTO.getProcessType(), activitiDeployMapper, businessKey, map); Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); taskService.complete(task.getId()); // 开启后,环节会走到发起请假请求,要完成这个环节,才能到下一个审核环节 responseJson.setData(processInstance.getProcessInstanceId()); return JSON.toJSONString(responseJson); }
4.3 审批任务
@Override public String approvalTask(TaskApprovalDTO taskApprovalDTO) { String user = taskApprovalDTO.getUserName(); String isOk = taskApprovalDTO.getIsOk(); //先查历史表 判断流程是否存在 HistoricProcessInstance historicProcessInstance =historyService.createHistoricProcessInstanceQuery().processInstanceId(taskApprovalDTO.getProcessInstanceId()).singleResult(); if(Objects.isNull(historicProcessInstance)) { throw new UserAuthException(EnumCode.ACTICITI_PROCESS_ISEXSIT.getCode(), EnumCode.ACTICITI_PROCESS_ISEXSIT.getText()); } Task task = taskService.createTaskQuery().processInstanceId(taskApprovalDTO.getProcessInstanceId()).taskCandidateOrAssigned(user).singleResult(); // 判断任务是否存在 if (Objects.isNull(task)) { throw new UserAuthException(EnumCode.ACTICITI_TASK_IS_NOTFOUND.getCode(), EnumCode.ACTICITI_TASK_IS_NOTFOUND.getText()); } // 进行签收 taskService.claim(task.getId(), user); //增加审批备注 taskService.addComment(task.getId(), task.getProcessInstanceId(), taskApprovalDTO.getComment()); //设置本地参数。在myListener1监听中获取。防止审核通过进行驳回 taskService.setVariableLocal(task.getId(), taskApprovalDTO.getField(), isOk); // 传入变量 Map<String, Object> map = new HashMap<>(); map.put(taskApprovalDTO.getField(), taskApprovalDTO.getIsOk()); taskService.complete(task.getId(), map); TaskVO taskVO = BeanUtil.copyProperties(task, TaskVO.class); taskVO.setTaskId(task.getId()); taskVO.setProcessInstanceId(taskApprovalDTO.getProcessInstanceId()); taskVO.setProcessInstanceName(historicProcessInstance.getProcessDefinitionName()); taskVO.setTaskName(task.getName()); // 查询流程信息(返回流程状态) ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(taskApprovalDTO.getProcessInstanceId()).singleResult(); if (processInstance != null) { taskVO.processInstanceIsEnd(processInstance.isEnded()); } else { taskVO.processInstanceIsEnd(true); taskVO.setEndTime(historicProcessInstance.getEndTime()); } ResponseJson responseJson = new ResponseJson(); responseJson.setData(taskVO); return JSON.toJSONString(responseJson);
4.4 查询我发起的流程
@Override public String myProcess(String userName) { List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().startedBy(userName).list(); ResponseJson responseJson = new ResponseJson(); responseJson.setData(list); return JSON.toJSONString(responseJson); }
4.5 流程下任务详情
@Override public String processInfo(String processInstanceId) { List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list(); ResponseJson responseJson = new ResponseJson(); responseJson.setData(list); return JSON.toJSONString(responseJson); }
4.6 删除流程
@Override public String deleteProcess(InitiationProcessDTO deleteProcessDto) { try { runtimeService.deleteProcessInstance(deleteProcessDto.getProcessInstanceId(),deleteProcessDto.getDeleteReason()); } catch (Exception e) { throw new UserAuthException(EnumCode.ACTICITI_DELETEPROCESS_ISWORRY.getCode(),EnumCode.ACTICITI_DELETEPROCESS_ISWORRY.getText()); } ResponseJson responseJson = new ResponseJson(); responseJson.setData("OK"); return JSON.toJSONString(responseJson); }
4.7 我待审核的任务
@Override public String taskCandidateIsMe(String userName) { ResponseJson responseJson = new ResponseJson(); List<TaskVO> taskVOS = new ArrayList<>(); List<Task> tasks = taskService//与任务相关的Service .createTaskQuery()//创建一个任务查询对象 .taskCandidateOrAssigned(userName) .list(); if(tasks !=null && tasks.size()>0){ for(Task task:tasks){ ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); TaskVO taskVO = BeanUtil.copyProperties(task, TaskVO.class); taskVO.setApplicantUser(processInstance.getStartUserId()); taskVO.setProcessInstanceName(processInstance.getProcessDefinitionName()); taskVO.setTaskId(task.getId()); taskVO.setTaskName(task.getName()); taskVO.processInstanceIsEnd(processInstance.isEnded()); taskVOS.add(taskVO); } } responseJson.setData(taskVOS); return JSON.toJSONString(responseJson); }
五、监听器
package com.awifi.capacity.project.manager.activiti.provider.listener; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.ExecutionListener; import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.impl.persistence.entity.ExecutionEntity; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import java.util.List; /** * @ClassName MyTaskListener * @Description TODO * @Author 张鑫 * @Date 2022/5/24 * @Version 1.0 **/ @Service public class MyTaskListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { ProcessEngine services = ProcessEngines.getDefaultProcessEngine(); ExecutionEntity entity = (ExecutionEntity) execution; String processInstId = entity.getProcessInstanceId(); //流程实例Id //根据审批传入的参数判断是否打回 Object value = execution.getVariableInstance("isOk").getValue(); if (ObjectUtils.isEmpty(value)) { value = execution.getParent().getVariableInstance("isOk").getValue(); } ; if (!ObjectUtils.isEmpty(value) && "true".equals(value)) { return; } List<HistoricTaskInstance> list = services.getHistoryService().createHistoricTaskInstanceQuery().orderByTaskCreateTime().asc() .processInstanceId(processInstId) .list(); if (list != null) { String user = list.get(0).getAssignee(); //获取最新的一个责任人信息回退给他 execution.setVariable("applicantUser", user); } } }
六、流程示意
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/185622.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...