基于jeecg-boot的flowable流程加签功能实现

更多nbcio-boot功能请看演示系统

gitee源代码地址

后端代码: https://gitee.com/nbacheng/nbcio-boot

前端代码:https://gitee.com/nbacheng/nbcio-vue.git

在线演示(包括H5) : http://122.227.135.243:9888

今天我们实现nbcio-boot的flowable的流程加签功能。

一、加签的几个概念

1、向前加签

任务在 A 这里,A 这个时候需要 B 核对一下,等 B 核对之后又回到 A 这里,这时 A 才能继续自己的任务

2、向后加签

任务在 A 这里,A 这个时候需要 B 处理这个事情,处理完毕之后就不用管了,继续后面的审批环节

3、多实例加签

任务只能对多实例任务进行加签,其它无效

二、前端实现

界面代码如下:

javascript 复制代码
<!--加签流程-->
    <a-modal :z-index="100" :title="addSignTitle" @cancel="addSignOpen = false" :visible.sync="addSignOpen" :width="'40%'" append-to-body>
      <el-form ref="addSignForm" :model="addSignForm" label-width="160px">
        <el-form-item label="加签类型" prop="addSignType" :rules="[{ required: true, message: '请选择加签类型', trigger: 'blur' }]">
          <el-radio-group v-model="addSignForm.addSignType" @change="changeAddSignType">
              <el-radio :label="0">前加签</el-radio>
              <el-radio :label="1">后加签</el-radio>
              <el-radio :label="2">多实例加签</el-radio>
            </el-radio-group>
        </el-form-item>
        <el-form-item label="用户选择" prop="addSignUsers" :rules="[{ required: true, message: '请选择用户', trigger: 'blur' }]">
          <j-select-user-by-dep v-model="addSignForm.addSignUsers" />
        </el-form-item>
        <el-form-item label="处理意见" prop="comment" :rules="[{ required: true, message: '请输入处理意见', trigger: 'blur' }]">
          <el-input type="textarea" v-model="addSignForm.comment" placeholder="请输入处理意见" />
        </el-form-item>
        <el-form-item label="附件"  prop="commentFileDto.fileurl">
          <j-upload v-model="addSignForm.commentFileDto.fileurl"   ></j-upload>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addSignOpen = false">取 消</el-button>
        <el-button type="primary" @click="addSignComplete(true)">确 定</el-button>
      </span>
    </a-modal>

加签实现代码如下:

javascript 复制代码
/** 加签 */
      handleAddSign() {
        this.addSignOpen = true;
        this.addSignTitle = "前加签流程";
      },
      changeAddSignType(val) {
        this.addSignForm.addSignType = val;
        if(this.addSignForm.addSignType === 0) {
          this.addSignTitle = "前加签流程";
        }
        if(this.addSignForm.addSignType === 1) {
          this.addSignTitle = "后加签流程";
        }
        if(this.addSignForm.addSignType === 2) {
          this.addSignTitle = "多实例加签流程";
        }
        console.log("changeAddSignType =",val);
        console.log("this.addSignTitle =",this.addSignTitle);
      },
      /** 加签任务 */
      addSignComplete() {
        if (!this.addSignForm.addSignUsers ) {
            this.$message.error("请选择用户");
            return;
        }
        // 流程信息
        this.addSignForm.deployId = this.$route.query && this.$route.query.deployId;
        this.addSignForm.taskId = this.$route.query && this.$route.query.taskId;
        this.addSignForm.procInsId = this.$route.query && this.$route.query.procInsId;
        this.addSignForm.instanceId = this.$route.query && this.$route.query.procInsId;
        // 初始化表单
        this.addSignForm.procDefId = this.$route.query && this.$route.query.procDefId;
        this.addSignForm.businessKey = this.$route.query && this.$route.query.businessKey;
        this.addSignForm.category = this.$route.query && this.$route.query.category;
        this.addSignForm.dataId = this.$route.query && this.$route.query.businessKey;
        //节点类型
        this.addSignForm.nodeType = this.$route.query && this.$route.query.nodeType;
        //online表单id和数据id
        this.addSignForm.onlineId = this.$route.query && this.$route.query.onlineId;
        if (this.addSignForm.category === 'online') {
          this.addSignForm.onlineDataId = this.$route.query && this.$route.query.businessKey;
        }  
        //对formdesigner后续加签审批的时候需要用到
        this.addSignForm.values = this.taskForm.values;
        console.log("this.addSignForm=",this.addSignForm);
        
        if(this.addSignForm.addSignType === 2) {
          multiInstanceAddSignTask(this.addSignForm).then(response => {
          this.$message.success(response.message);
          this.addSignOpen = false;
          this.goBack();
          });
        }
        else {
          addSignTask(this.addSignForm).then(response => {
          this.$message.success(response.message);
          this.addSignOpen = false;
          this.goBack();
          });
        }
      },

实现效果图如下:

三、后端主要代码

java 复制代码
@Override
	public void addTasksBefore(String processInstanceId, String assignee, Set<String> assignees, String description) {
		addTask(processInstanceId, assignee, assignees, description, Boolean.FALSE);
		
	}

	@Override
	public void addTasksAfter(String processInstanceId, String assignee, Set<String> assignees, String description) {
		addTask(processInstanceId, assignee, assignees, description, Boolean.TRUE);
		
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void addTask(String processInstanceId, String assignee, Set<String> assignees, String description,
			Boolean flag) {
		TaskEntityImpl task = (TaskEntityImpl) taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(assignee).singleResult();
        Assert.notNull(task, String.format("分配人 [%s] 没有待处理任务", assignee));

        //如果是加签再加签
        String parentTaskId = task.getParentTaskId();
        if (StrUtil.isBlank(parentTaskId)) {
            task.setOwner(assignee);
            task.setAssignee(null);
            task.setCountEnabled(true);
            if (flag) {
                task.setScopeType("after");
            } else {
                task.setScopeType("before");
            }
            // 设置任务为空执行者
            taskService.saveTask(task);
        }
        //添加加签数据
        this.createSignSubTasks(assignee, assignees, task);
        //添加审批意见
        String type = flag ? FlowComment.HJQ.getType() : FlowComment.QJQ.getType();
        taskService.addComment(task.getId(), processInstanceId, type, description);
		
	}
	
	/**
     * 创建加签子任务
     * @param assignees 被加签人
     * @param assignee 加签人
     * @param taskEntity 父任务
     */
    private void createSignSubTasks(String assignee, Set<String> assignees, TaskEntity taskEntity) {
        if (CollectionUtil.isNotEmpty(assignees)) {
        	//1.创建被加签人的任务列表
            assignees.forEach(userId -> {
                if (StrUtil.isNotBlank(userId)) {
                    this.createSubTask(taskEntity, taskEntity.getId(), userId);
                }
            });
        	
            String parentTaskId = taskEntity.getParentTaskId();
            if (StrUtil.isBlank(parentTaskId)) {
                parentTaskId = taskEntity.getId();
            }
            String finalParentTaskId = parentTaskId;
            //2.创建加签人的任务并执行完毕
            String taskId = taskEntity.getId();
            if (StrUtil.isBlank(taskEntity.getParentTaskId())) {
                Task task = this.createSubTask(taskEntity, finalParentTaskId, assignee);
                taskId = task.getId();
            }
            Task taskInfo = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (ObjectUtil.isNotNull(taskInfo)) {
                taskService.complete(taskId);
            }
            //如果是候选人,需要删除运行时候选不中的数据。
            long candidateCount = taskService.createTaskQuery().taskId(parentTaskId).taskCandidateUser(assignee).count();
            if (candidateCount > 0) {
                taskService.deleteCandidateUser(parentTaskId, assignee);
            }
        }
    }

	
	
	public String getMultiInstanceActAssigneeParam(String processDefinitionId, String actId) {
        AtomicReference<String> resultParam = new AtomicReference<>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).singleResult();
        //获取bpmnModel并转为modelNode
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        //获取主流程
        Process mainProcess = bpmnModel.getMainProcess();
        //获取用户任务节点类型,深入子流程
        mainProcess.findFlowElementsOfType(UserTask.class, true).forEach(userTask -> {
            String userTaskId = userTask.getId();
            if (userTaskId.equals(actId)) {
                Object behavior = userTask.getBehavior();
                if (ObjectUtil.isNotNull(behavior)) {
                    //并行多实例节点
                    if (behavior instanceof ParallelMultiInstanceBehavior) {
                        ParallelMultiInstanceBehavior parallelMultiInstanceBehavior =
                                (ParallelMultiInstanceBehavior) behavior;
                        String collectionElementVariable = parallelMultiInstanceBehavior
                                .getCollectionElementVariable();
                        if (ObjectUtil.isNotEmpty(collectionElementVariable)) {
                            resultParam.set(collectionElementVariable);
                        }
                    }
                    //串行多实例节点
                    if (behavior instanceof SequentialMultiInstanceBehavior) {
                        SequentialMultiInstanceBehavior sequentialMultiInstanceBehavior =
                                (SequentialMultiInstanceBehavior) behavior;
                        String collectionElementVariable = sequentialMultiInstanceBehavior
                                .getCollectionElementVariable();
                        if (ObjectUtil.isNotEmpty(collectionElementVariable)) {
                            resultParam.set(collectionElementVariable);
                        }
                    }
                }
            }
        });
        return resultParam.get();
    }

四、实际效果图如下:

相关推荐
卸任11 小时前
Vibe coding工具使用心得
前端·ai编程
brien912 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance12 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M12 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写12 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李12 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易13 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马13 小时前
前沿视界
前端
kyriewen13 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易13 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员