mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-23 07:41:53 +08:00
【功能重构】工作流:重构流程不通过、取消的处理逻辑,完全转向 flowable 的 moveActivityIdsToSingleActivityId API
This commit is contained in:
parent
521cc3deb4
commit
32804d3e0b
@ -5,13 +5,13 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程实例/任务的删除原因枚举
|
* 流程实例/任务的的处理原因枚举
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum BpmDeleteReasonEnum {
|
public enum BpmReasonEnum {
|
||||||
|
|
||||||
// ========== 流程实例的独有原因 ==========
|
// ========== 流程实例的独有原因 ==========
|
||||||
|
|
||||||
@ -36,10 +36,4 @@ public enum BpmDeleteReasonEnum {
|
|||||||
return StrUtil.format(reason, args);
|
return StrUtil.format(reason, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 逻辑 ==========
|
|
||||||
|
|
||||||
public static boolean isRejectReason(String reason) {
|
|
||||||
return StrUtil.startWith(reason, "审批不通过任务,原因:");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -89,11 +89,6 @@ public interface BpmProcessInstanceConvert {
|
|||||||
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
||||||
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
|
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
|
||||||
|
|
||||||
default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, HistoricProcessInstance instance, Integer status) {
|
|
||||||
return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
|
|
||||||
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {;
|
default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {;
|
||||||
return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
|
return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
|
||||||
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
|
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
|
||||||
|
@ -15,6 +15,14 @@ public class BpmConstants {
|
|||||||
* @see ProcessInstance#getProcessVariables()
|
* @see ProcessInstance#getProcessVariables()
|
||||||
*/
|
*/
|
||||||
public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
|
public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
|
||||||
|
/**
|
||||||
|
* 流程实例的变量 - 理由
|
||||||
|
*
|
||||||
|
* 例如说:审批不通过的理由(目前审核通过暂时不会记录)
|
||||||
|
*
|
||||||
|
* @see ProcessInstance#getProcessVariables()
|
||||||
|
*/
|
||||||
|
public static final String PROCESS_INSTANCE_VARIABLE_REASON = "PROCESS_REASON";
|
||||||
/**
|
/**
|
||||||
* 流程实例的变量 - 发起用户选择的审批人 Map
|
* 流程实例的变量 - 发起用户选择的审批人 Map
|
||||||
*
|
*
|
||||||
|
@ -6,7 +6,6 @@ import jakarta.annotation.Resource;
|
|||||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
|
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
|
||||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
|
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
|
||||||
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
|
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
|
||||||
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
|
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -21,24 +20,18 @@ import java.util.Set;
|
|||||||
@Component
|
@Component
|
||||||
public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener {
|
public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener {
|
||||||
|
|
||||||
@Resource
|
|
||||||
@Lazy
|
|
||||||
private BpmProcessInstanceService processInstanceService;
|
|
||||||
|
|
||||||
public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
|
public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
|
||||||
.add(FlowableEngineEventType.PROCESS_CANCELLED)
|
.add(FlowableEngineEventType.PROCESS_COMPLETED)
|
||||||
.add(FlowableEngineEventType.PROCESS_COMPLETED)
|
.build();
|
||||||
.build();
|
|
||||||
|
@Resource
|
||||||
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
|
private BpmProcessInstanceService processInstanceService;
|
||||||
|
|
||||||
public BpmProcessInstanceEventListener(){
|
public BpmProcessInstanceEventListener(){
|
||||||
super(PROCESS_INSTANCE_EVENTS);
|
super(PROCESS_INSTANCE_EVENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processCancelled(FlowableCancelledEvent event) {
|
|
||||||
processInstanceService.updateProcessInstanceWhenCancel(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void processCompleted(FlowableEngineEntityEvent event) {
|
protected void processCompleted(FlowableEngineEntityEvent event) {
|
||||||
processInstanceService.updateProcessInstanceWhenCompleted((ProcessInstance)event.getEntity());
|
processInstanceService.updateProcessInstanceWhenCompleted((ProcessInstance)event.getEntity());
|
||||||
|
@ -6,11 +6,9 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
|
|||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
|
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -120,32 +118,16 @@ public interface BpmProcessInstanceService {
|
|||||||
*/
|
*/
|
||||||
void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO);
|
void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO);
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新 ProcessInstance 拓展记录为取消
|
|
||||||
*
|
|
||||||
* @param event 流程取消事件
|
|
||||||
*/
|
|
||||||
void updateProcessInstanceWhenCancel(FlowableCancelledEvent event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新 ProcessInstance 为完成
|
|
||||||
*
|
|
||||||
* @param instance 流程任务
|
|
||||||
*/
|
|
||||||
void updateProcessInstanceWhenApprove(ProcessInstance instance);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新 ProcessInstance 为不通过
|
* 更新 ProcessInstance 为不通过
|
||||||
*
|
*
|
||||||
* @param processInstance 流程实例
|
* @param processInstance 流程实例
|
||||||
* @param activityIds 当前未完成活动节点 Id
|
|
||||||
* @param endId 结束节点 Id
|
|
||||||
* @param reason 理由。例如说,审批不通过时,需要传递该值
|
* @param reason 理由。例如说,审批不通过时,需要传递该值
|
||||||
*/
|
*/
|
||||||
void updateProcessInstanceReject(ProcessInstance processInstance, Collection<String> activityIds, String endId, String reason);
|
void updateProcessInstanceReject(ProcessInstance processInstance, String reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当流程结束时候,更新 ProcessInstance 为通过
|
* 处理 ProcessInstance 完成(审批通过、不通过、取消)
|
||||||
*
|
*
|
||||||
* @param instance 流程任务
|
* @param instance 流程任务
|
||||||
*/
|
*/
|
||||||
|
File diff suppressed because one or more lines are too long
@ -90,8 +90,6 @@ public interface BpmTaskService {
|
|||||||
*/
|
*/
|
||||||
void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);
|
void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将流程任务分配给指定用户
|
* 将流程任务分配给指定用户
|
||||||
*
|
*
|
||||||
@ -100,6 +98,13 @@ public interface BpmTaskService {
|
|||||||
*/
|
*/
|
||||||
void transferTask(Long userId, BpmTaskTransferReqVO reqVO);
|
void transferTask(Long userId, BpmTaskTransferReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定流程实例的、进行中的流程任务,移动到结束节点
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程编号
|
||||||
|
*/
|
||||||
|
void moveTaskToEnd(String processInstanceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新 Task 状态,在创建时
|
* 更新 Task 状态,在创建时
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
|||||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
|
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
|
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum;
|
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
|
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
|
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
|
||||||
@ -327,15 +327,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||||||
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
|
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 处理当前任务
|
|
||||||
// 2.1 更新流程任务为不通过
|
// 2.1 更新流程任务为不通过
|
||||||
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason());
|
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason());
|
||||||
// 2.2 添加评论
|
// 2.2 添加流程评论
|
||||||
taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
|
taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
|
||||||
BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
|
BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
|
||||||
|
// 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过
|
||||||
// 3. 处理其他进行中的任务
|
|
||||||
// 3.1 如果当前任务时被加签的,则加它的根任务也标记成未通过
|
|
||||||
// 疑问:为什么要标记未通过呢?
|
// 疑问:为什么要标记未通过呢?
|
||||||
// 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。
|
// 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。
|
||||||
if (task.getParentTaskId() != null) {
|
if (task.getParentTaskId() != null) {
|
||||||
@ -345,41 +342,22 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||||||
taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
|
taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
|
||||||
BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过"));
|
BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过"));
|
||||||
}
|
}
|
||||||
// 3.2 其它未结束的任务,直接取消
|
|
||||||
// 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢?
|
|
||||||
// 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景
|
|
||||||
List<Task> taskList = getRunningTaskListByProcessInstanceId(instance.getProcessInstanceId(), null, null, null);
|
|
||||||
taskList.forEach(otherTask -> {
|
|
||||||
if (!otherTask.getId().equals(task.getId())) { // 不需要处理当前任务
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
|
|
||||||
if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateTaskStatusWhenCanceled(otherTask.getId());
|
|
||||||
});
|
|
||||||
taskList.stream().filter(otherTask -> !otherTask.getId().equals(task.getId())) // 需要排除当前任务
|
|
||||||
.forEach(otherTask -> updateTaskStatusWhenCanceled(otherTask.getId()));
|
|
||||||
|
|
||||||
// 4.1 驳回到指定的任务节点
|
// 3. 根据不同的 RejectHandler 处理策略
|
||||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||||
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||||
|
// 3.1 情况一:驳回到指定的任务节点
|
||||||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
|
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
|
||||||
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
||||||
String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
|
String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
|
||||||
Assert.notNull(returnTaskId, "回退的节点不能为空");
|
Assert.notNull(returnTaskId, "回退的节点不能为空");
|
||||||
BpmTaskReturnReqVO returnReq = new BpmTaskReturnReqVO().setId(task.getId()).setTargetTaskDefinitionKey(returnTaskId)
|
returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
|
||||||
.setReason(reqVO.getReason());
|
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
||||||
returnTask(userId, returnReq);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 3.2 情况二:直接结束,审批不通过
|
||||||
// 4.2 终止流程
|
processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过
|
||||||
Set<String> activityIds = convertSet(taskList, Task::getTaskDefinitionKey);
|
moveTaskToEnd(task.getProcessInstanceId()); // 结束流程
|
||||||
EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
|
|
||||||
Assert.notNull(endEvent, "结束节点不能未空");
|
|
||||||
processInstanceService.updateProcessInstanceReject(instance, activityIds, endEvent.getId(), reqVO.getReason());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -449,7 +427,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||||||
log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status);
|
log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason());
|
updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());
|
||||||
// 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由
|
// 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,6 +643,35 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||||||
taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString());
|
taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveTaskToEnd(String processInstanceId) {
|
||||||
|
List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null, null);
|
||||||
|
if (CollUtil.isEmpty(taskList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 其它未结束的任务,直接取消
|
||||||
|
// 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢?
|
||||||
|
// 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景
|
||||||
|
taskList.forEach(task -> {
|
||||||
|
Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
|
||||||
|
if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateTaskStatusWhenCanceled(task.getId());
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 终止流程
|
||||||
|
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId());
|
||||||
|
List<String> activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey));
|
||||||
|
EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
|
||||||
|
Assert.notNull(endEvent, "结束节点不能未空");
|
||||||
|
runtimeService.createChangeActivityStateBuilder()
|
||||||
|
.processInstanceId(processInstanceId)
|
||||||
|
.moveActivityIdsToSingleActivityId(activityIds, endEvent.getId())
|
||||||
|
.changeState();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) {
|
public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) {
|
||||||
|
Loading…
Reference in New Issue
Block a user