赞
踩
流程表达式菜单
把流程发起人表达式放入申请人 活动节点。
此时设计流程保存后,用户发起流程,点击提交表单,这时后台会先去 获取下一活动节点,如果下一活动节点有流程变量 就弹框 让用户选择变量指代的人员 并且写提交意见;如果没有就写提交意见,发起流程。
由于申请人节点用了 流程变量,所以会弹框让用户选择变量指代的人员,但是这样就有些奇怪。所以需要修改代码:
1.获取下一节点接口:遇到${INITIATOR} 就跳过
2.发起流程接口:给 ${INITIATOR} 设置指代对象,自动帮发起人完成审批流程
这样就可以直接 流转到 申请人的下一活动节点。
修改代码
1.获取下一节点接口:遇到${INITIATOR} 就跳过
@Override public AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo) { ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(flowTaskVo.getDeploymentId()).singleResult(); // Step 1. 获取当前节点并找到下一步节点 FlowNextDto flowNextDto = new FlowNextDto(); // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式) List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasksByStart(repositoryService, processDefinition, flowTaskVo.getVariables()); if (CollectionUtils.isNotEmpty(nextUserTask)) { for (UserTask userTask : nextUserTask) { MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics(); // 会签节点 if (Objects.nonNull(multiInstance)) { flowNextDto.setVars(multiInstance.getInputDataItem()); flowNextDto.setType(ProcessConstants.PROCESS_MULTI_INSTANCE); flowNextDto.setDataType(ProcessConstants.DYNAMIC); } else { //如果遇到发起人流程变量 则跳过:启动时会自动添加 if (("${" + ProcessConstants.PROCESS_INITIATOR + "}").equals(userTask.getAssignee())) { continue; } // 读取自定义节点属性 判断是否是否需要动态指定任务接收人员、组 String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE); String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE); flowNextDto.setVars(ProcessConstants.PROCESS_APPROVAL); flowNextDto.setType(userType); flowNextDto.setDataType(dataType); } } } return AjaxResult.success(flowNextDto); }
2.发起流程接口:给 ${INITIATOR} 设置指代对象,自动帮发起人完成审批流程
/** * 根据流程定义ID启动流程实例 * * @param procDefId 流程模板ID * @param variables 流程变量 * @return */ @Override public AjaxResult startProcessInstanceById(String procDefId, Map<String, Object> variables) { try { ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) .latestVersion().singleResult(); if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { return AjaxResult.error("流程已被挂起,请先激活流程"); } // 设置流程发起人Id到流程中 SysUser sysUser = SecurityUtils.getLoginUser().getUser(); identityService.setAuthenticatedUserId(sysUser.getUserId().toString()); variables.put(ProcessConstants.PROCESS_INITIATOR, sysUser.getUserId()); ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefId, variables); // 如果第一活动节点为当前用户,则默认当前用户==申请人,则自动审批:给第一步申请人节点设置任务执行人和意见 Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).taskAssignee(sysUser.getUserId().toString()).singleResult(); if (Objects.nonNull(task)) { taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); // taskService.setAssignee(task.getId(), sysUser.getUserId().toString()); taskService.complete(task.getId(), variables); } return AjaxResult.success("流程启动成功"); } catch (Exception e) { e.printStackTrace(); return AjaxResult.error("流程启动错误"); } }
1.通过驳回按钮驳回,这种驳回 只能一级一级驳;(如上图)
2.通过设计流程图 排他网关+判断表单字段变量: 可以越级驳回(不需要驳回按钮)
3. 在申请人活动节点添加表单,这样驳回到申请人时 可以重新写表单。
示例:
2.通过设计流程图 排他网关+判断表单字段变量: 可以越级驳回(不许要驳回按钮)
排他网关:驳回:$ {sp == 1} ; 通过:$ {sp == 2};
sp字段在 图中 审批表单里面
修改代码
flowable/task/todo/detail/index.vue
第二种设计流程 用户表单的选项会影响 下一节点是哪个,但是原代码并未把新的表单选项 传给后端,后端还是用的 原先的值。
/** 申请流程表单数据提交 */ submitForm(formData) { // 根据当前任务或者流程设计配置的下一步节点 todo 暂时未涉及到考虑网关、表达式和多节点情况 var params = {}; //该节点 没有新增表单 if (formData == null){ params = {taskId: this.taskForm.taskId}; } else {//todo 该节点有新增表单:考虑表单内有影响 选择 下一活动节点 的表达式值,所有一起传过去 params = {...this.taskForm}; params.formData = formData; // 表单是否禁用 params.formData.formData.disabled = true; // 是否显示按钮 params.formData.formData.formBtns = false; params.variables = Object.assign({}, params.variables, params.formData.valData); params.variables.variables = params.formData.formData; } getNextFlowNode(params).then(res => { const data = res.data; this.taskForm.formData = formData; if (data) { if (data.dataType === 'dynamic') { if (data.type === 'assignee') { // 指定人员 this.checkSendUser = true; this.checkType = "single"; } else if (data.type === 'candidateUsers') { // 候选人员(多个) this.checkSendUser = true; this.checkType = "multiple"; } else if (data.type === 'candidateGroups') { // 指定组(所属角色接收任务) this.checkSendRole = true; } else { // 会签 // 流程设计指定的 elementVariable 作为会签人员列表 this.multiInstanceVars = data.vars; this.checkSendUser = true; this.checkType = "multiple"; } } } this.completeOpen = true; this.completeTitle = "流程审批"; }) },
todo下为新增代码
把新表单 流程变量和之前流程变量 合并
/** * 获取下一节点 * * @param flowTaskVo 任务 * @return */ @Override public AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo) { // Step 1. 获取当前节点并找到下一步节点 Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); FlowNextDto flowNextDto = new FlowNextDto(); if (Objects.nonNull(task)) { // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式) Map<String, Object> variables = taskService.getVariables(task.getId()); //todo 如果当前节点有新增表单,考虑新增表单内有表达式值会影响 选择 下一活动节点,所有把流程变量合并 if (flowTaskVo.getVariables() != null) { flowTaskVo.getVariables().forEach((key, value) -> variables.merge(key, value, (v1, v2) -> v2)); } List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, variables); if (CollectionUtils.isNotEmpty(nextUserTask)) { for (UserTask userTask : nextUserTask) { MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics(); // 会签节点 if (Objects.nonNull(multiInstance)) { flowNextDto.setVars(multiInstance.getInputDataItem()); flowNextDto.setType(ProcessConstants.PROCESS_MULTI_INSTANCE); flowNextDto.setDataType(ProcessConstants.DYNAMIC); } else { // 读取自定义节点属性 判断是否是否需要动态指定任务接收人员、组 String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE); String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE); flowNextDto.setVars(ProcessConstants.PROCESS_APPROVAL); flowNextDto.setType(userType); flowNextDto.setDataType(dataType); } } } else { return AjaxResult.success("流程已完结", null); } } return AjaxResult.success(flowNextDto); }
如果遇到发起人流程变量 则跳过:因为该值固定 不需要用户重新赋值
/** * 查找下一节点 * * @param flowElements * @param flowElement * @param map * @param nextUser */ public static void next(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) { //如果是结束节点 if (flowElement instanceof EndEvent) { //如果是子任务的结束节点 if (getSubProcess(flowElements, flowElement) != null) { flowElement = getSubProcess(flowElements, flowElement); } } //获取Task的出线信息--可以拥有多个 List<SequenceFlow> outGoingFlows = null; if (flowElement instanceof Task) { outGoingFlows = ((Task) flowElement).getOutgoingFlows(); } else if (flowElement instanceof Gateway) { outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); } else if (flowElement instanceof StartEvent) { outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); } else if (flowElement instanceof SubProcess) { outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); } else if (flowElement instanceof CallActivity) { outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); } if (outGoingFlows != null && outGoingFlows.size() > 0) { //遍历所有的出线--找到可以正确执行的那一条 for (SequenceFlow sequenceFlow : outGoingFlows) { //1.有表达式,且为true //2.无表达式 String expression = sequenceFlow.getConditionExpression(); if (expression == null || expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { //出线的下一节点 String nextFlowElementID = sequenceFlow.getTargetRef(); if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { continue; } //查询下一节点的信息 FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); //调用流程 if (nextFlowElement instanceof CallActivity) { CallActivity ca = (CallActivity) nextFlowElement; if (ca.getLoopCharacteristics() != null) { UserTask userTask = new UserTask(); userTask.setId(ca.getId()); userTask.setId(ca.getId()); userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); userTask.setName(ca.getName()); nextUser.add(userTask); } next(flowElements, nextFlowElement, map, nextUser); } //用户任务 if (nextFlowElement instanceof UserTask) { //如果遇到发起人流程变量 则跳过:因为该值固定 不需要用户重新赋值 if (!("${" + ProcessConstants.PROCESS_INITIATOR + "}").equals(((UserTask) nextFlowElement).getAssignee())) { nextUser.add((UserTask) nextFlowElement); } } //排他网关 else if (nextFlowElement instanceof ExclusiveGateway) { next(flowElements, nextFlowElement, map, nextUser); } //并行网关 else if (nextFlowElement instanceof ParallelGateway) { next(flowElements, nextFlowElement, map, nextUser); } //接收任务 else if (nextFlowElement instanceof ReceiveTask) { next(flowElements, nextFlowElement, map, nextUser); } //服务任务 else if (nextFlowElement instanceof ServiceTask) { next(flowElements, nextFlowElement, map, nextUser); } //子任务的起点 else if (nextFlowElement instanceof StartEvent) { next(flowElements, nextFlowElement, map, nextUser); } //结束节点 else if (nextFlowElement instanceof EndEvent) { next(flowElements, nextFlowElement, map, nextUser); } } } } }
1.方法表达式
在该系统测试,会报异常,无法识别 表达式,不知道哪有问题
2.任务监听器
@PostConstruct
/** * 任务监听器 * <p> * create(创建):在任务被创建且所有的任务属性设置完成后才触发 * assignment(指派):在任务被分配给某个办理人之后触发 * complete(完成):在配置了监听器的上一个任务完成时触发 * delete(删除):在任务即将被删除前触发。请注意任务由completeTask正常完成时也会触发 * * @author Tony * @date 2021/4/20 */ @Slf4j @Component public class FlowTaskListener implements TaskListener { @Resource protected TaskService taskService; @Autowired private ISysRelationService relationService; @Autowired private SysRelationMapper relationMapper; @Resource private ISysUserService sysUserService; private static FlowTaskListener flowTaskListener; //!!!!必加 不然 @Autowired和@Resource 自动注入 都为null @PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作 public void init() { flowTaskListener = this; flowTaskListener.taskService= this.taskService; flowTaskListener.relationService= this.relationService; flowTaskListener.relationMapper= this.relationMapper; flowTaskListener.sysUserService= this.sysUserService; // 初使化时将已静态化的testService实例化 } @SneakyThrows @Override public void notify(DelegateTask delegateTask) { log.info("任务监听器:{}", delegateTask); // TODO 获取事件类型 delegateTask.getEventName(),可以通过监听器给任务执行人发送相应的通知消息 //获取当前任务节点 Task task = flowTaskListener.taskService.createTaskQuery().processInstanceId(delegateTask.getProcessInstanceId()).singleResult(); //查询任务节点分配人信息 SysUser sysUser = flowTaskListener.sysUserService.selectUserById(Long.parseLong(task.getAssignee())); //获取父部门下最高职位员工 SysUserDeptPostVo param = new SysUserDeptPostVo(); param.setDeptId(sysUser.getDept().getParentId()); SysUserDeptPostVo sysUserDeptPostVo = flowTaskListener.relationService.getTopFromUsers(flowTaskListener.relationMapper.getUsersByDept(param)); if (sysUserDeptPostVo != null) { //!!! 这么写没有历史记录 // delegateTask.setAssignee(sysUserDeptPostVo.getUserId().toString()); //!!!有历史记录 flowTaskListener.taskService.setAssignee(delegateTask.getId(), sysUserDeptPostVo.getUserId().toString()); } else { throw new Exception("获取上级人员失败,系统异常"); } } }
eg:${flowTaskListener} 第一个字母小写
1.类:com.ruoyi.flowable.listener.MytaskListener1
2.表达式: ${MytaskListener2.test1()}
3.委托表达式: ${MytaskListener3}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。