更多nbcio-boot功能请看演示系统
gitee源代码地址
在线演示(包括H5) : http://122.227.135.243:9888
今天我们实现nbcio-boot的flowable的流程加签功能。
一、加签的几个概念
1、向前加签
任务在 A 这里,A 这个时候需要 B 核对一下,等 B 核对之后又回到 A 这里,这时 A 才能继续自己的任务
2、向后加签
任务在 A 这里,A 这个时候需要 B 处理这个事情,处理完毕之后就不用管了,继续后面的审批环节
3、多实例加签
任务只能对多实例任务进行加签,其它无效
二、前端实现
界面代码如下:
<!--加签流程-->
<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>
加签实现代码如下:
/** 加签 */
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();
});
}
},
实现效果图如下:
三、后端主要代码
@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();
}
四、实际效果图如下: