add - 提炼转交/加签的公共逻辑

This commit is contained in:
wangli 2023-12-22 15:34:10 +08:00
parent 7de634e2d1
commit 8dbcb88ce1
8 changed files with 291 additions and 71 deletions

View File

@ -0,0 +1,79 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_ID;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE;
/**
* 自定的业务指定审批人命令实现
*
* @author wangli
* @since 2023/12/22 13:51
*/
public class CustomBizSpecifyAssigneeToTaskCmd implements Command<Boolean>, Serializable {
private final String executionId;
private final List<BpmnTaskDelegateAssigner> addedAssigners;
public CustomBizSpecifyAssigneeToTaskCmd(String executionId, List<BpmnTaskDelegateAssigner> addedAssigners) {
this.executionId = executionId;
this.addedAssigners = addedAssigners;
}
@Override
public Boolean execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
Task task = taskService.createTaskQuery().executionId(executionId)
.taskAssignee(DUMMY_ASSIGNEE_ID)
.singleResult();
validTask(task);
addAssignee(commandContext, taskService, task);
return true;
}
private void addAssignee(CommandContext commandContext, TaskService taskService, Task task) {
if (CollectionUtils.isEmpty(addedAssigners)) {
return;
}
addedAssigners.forEach(i -> {
if (Objects.equals(task.getAssignee(), DUMMY_ASSIGNEE_ID)) {
task.setAssignee(i.buildAssigneeId());
taskService.saveTask(task);
} else {
CustomTaskHelper.addMultiTask(commandContext, (TaskEntity) task, i);
}
});
}
private void validTask(Task task) {
if (Objects.isNull(task)) {
throw new WorkflowEngineException(ACTIVITY_BIZ_SET_ASSIGNEE_ERROR, executionId);
}
if (!Objects.equals(task.getAssignee(), BpmnConstants.DUMMY_ASSIGNEE_ID)) {
throw new WorkflowEngineException(ACTIVITY_CANT_SET_ASSIGNEE);
}
}
}

View File

@ -1,43 +0,0 @@
package cn.axzo.workflow.core.engine.cmd;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.io.Serializable;
/**
* 自定义的扩展命令, 主要用于加签和转交功能, 创建子任务的审批人指派事件
*
* @author wangli
* @since 2023/12/9 16:37
*/
public class CustomEventAssignmentCmd implements Command<Void>, Serializable {
private String processDefinitionId;
private String taskDefinitionKey;
private TaskEntity task;
public CustomEventAssignmentCmd(String processDefinitionId, String taskDefinitionKey, TaskEntity task) {
this.processDefinitionId = processDefinitionId;
this.taskDefinitionKey = taskDefinitionKey;
this.task = task;
}
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
UserTask flowElement = (UserTask) bpmnModel.getFlowElement(taskDefinitionKey);
processEngineConfiguration.getListenerNotificationHelper().executeTaskListeners(flowElement, task,
TaskListener.EVENTNAME_ASSIGNMENT);
return null;
}
}

View File

@ -0,0 +1,132 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.task.Attachment;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_TRANSFER;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ASSIGNEE_HAS_BEEN_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
/**
* 转交任务的命令实现
*
* @author wangli
* @since 2023/12/22 10:22
*/
public class CustomTransferUserTaskCmd implements Command<Void>, Serializable {
private final String originTaskId;
private final BpmnTaskDelegateAssigner originTaskAssignee;
private final String advice;
private final List<AttachmentDTO> attachmentList;
private final BpmnTaskDelegateAssigner targetTaskAssignee;
public CustomTransferUserTaskCmd(String originTaskId, BpmnTaskDelegateAssigner originTaskAssignee, String advice,
List<AttachmentDTO> attachmentList, BpmnTaskDelegateAssigner targetTaskAssignee) {
this.originTaskId = originTaskId;
this.originTaskAssignee = originTaskAssignee;
this.advice = advice;
this.attachmentList = attachmentList;
this.targetTaskAssignee = targetTaskAssignee;
}
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
Task task = taskService.createTaskQuery().taskId(originTaskId).singleResult();
validTask(task);
validAssignee(processEngineConfiguration, task);
resolveOriginTask(taskService, task);
addAttachment(taskService, task);
CustomTaskHelper.addMultiTask(commandContext, (TaskEntity) task, targetTaskAssignee);
return null;
}
private void addAttachment(TaskService taskService, Task task) {
if (CollectionUtils.isEmpty(attachmentList)) {
return;
}
for (AttachmentDTO dto : attachmentList) {
Attachment attachment = taskService.createAttachment(dto.getType().getType(), originTaskId,
task.getProcessInstanceId(), dto.getName(), dto.getDescription(),
dto.getUrl());
taskService.saveAttachment(attachment);
}
}
private void resolveOriginTask(TaskService taskService, Task task) {
task.setAssignee(TASK_ASSIGNEE_SKIP_FLAT);
((TaskEntity) task).setScopeType("TRANSFER");
taskService.saveTask(task);
Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId());
taskService.addComment(task.getId(), task.getProcessInstanceId(), COMMENT_TYPE_OPERATION_TRANSFER,
"转交给 " + targetTaskAssignee.getAssignerName());
if (StringUtils.hasLength(advice)) {
taskService.addComment(task.getId(), task.getProcessInstanceId(), COMMENT_TYPE_ADVICE, advice);
}
Authentication.setAuthenticatedUserId(null);
}
public void validTask(Task task) {
if (Objects.isNull(task)) {
throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS);
}
if (Objects.nonNull(originTaskAssignee) && !Objects.equals(task.getAssignee(),
originTaskAssignee.getAssignee())) {
throw new WorkflowEngineException(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
}
}
public void validAssignee(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) {
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List<BpmnTaskDelegateAssigner> originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(),
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);
Optional<BpmnTaskDelegateAssigner> exists = originAssingeeList.stream()
.filter(i -> Objects.equals(i.buildAssigneeId(), targetTaskAssignee.buildAssigneeId())).findAny();
if (exists.isPresent()) {
throw new WorkflowEngineException(ASSIGNEE_HAS_BEEN_EXISTS);
}
for (BpmnTaskDelegateAssigner assigner : originAssingeeList) {
if (Objects.equals(assigner.buildAssigneeId(), originTaskAssignee.buildAssigneeId())) {
originAssingeeList.remove(assigner);
break;
}
}
originAssingeeList.add(targetTaskAssignee);
runtimeService.setVariable(task.getProcessInstanceId(),
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
originAssingeeList);
}
}

View File

@ -0,0 +1,48 @@
package cn.axzo.workflow.core.engine.cmd.helper;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 用于转交/加签命令中的辅助类
*
* @author wangli
* @since 2023/12/22 10:59
*/
public class CustomTaskHelper {
public static void addMultiTask(CommandContext commandContext, TaskEntity originTask,
BpmnTaskDelegateAssigner newTaskAssignee) {
if (Objects.isNull(originTask)) {
return;
}
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
Map<String, Object> executionVariables = new HashMap<>();
executionVariables.put("assigneeName", newTaskAssignee.buildAssigneeId());
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
Execution subExecution = runtimeService.addMultiInstanceExecution(originTask.getTaskDefinitionKey(),
originTask.getProcessInstanceId(), executionVariables);
// CommandContextUtil.getEntityCache().findInCache(TaskEntity.class).stream().filter(i-> Objects
// .equals(i.getExecutionId(), subExecution.getId())).findAny()
// .ifPresent(i-> {
// i.setParentTaskId(originTask.getId());
// taskService.saveTask(i);
// });
}
}

View File

@ -106,7 +106,7 @@ public class EngineExecutionStartListener implements ExecutionListener {
case bizSpecify:
// 构建一个虚拟的审批人避免因为没有审批人而导致流程触发了"审批人为空时"的处理逻辑, TODO 当业务传入审批时,需要更新这里的变量
BpmnTaskDelegateAssigner dummyApprover = buildBpmnTaskDelegateAssigner(DUMMY_ASSIGNEE,
DUMMY_ASSIGNEE_TYPE, DUMMY_ASSIGNEE_NAME, null, null, NO_TENANT_ID);
DUMMY_ASSIGNEE_TYPE, DUMMY_ASSIGNEE_NAME, "0", "0", NO_TENANT_ID);
List<String> dummyAssigneeIdList = new ArrayList<>();
dummyAssigneeIdList.add(dummyApprover.buildAssigneeId());

View File

@ -1,22 +1,19 @@
package cn.axzo.workflow.core.service.impl;
import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskCmd;
import cn.axzo.workflow.core.service.BpmnProcessActivityService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.Task;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_ID;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_TRIGGER_NOT_EXISTS;
@ -27,7 +24,7 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
@Resource
private RuntimeService runtimeService;
@Resource
private TaskService taskService;
private SpringProcessEngineConfiguration processEngineConfiguration;
@Override
public void trigger(String executionId) {
@ -40,17 +37,7 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
@Override
public void setAssignee(BpmnActivitySetAssigneeDTO dto) {
Task task = taskService.createTaskQuery().executionId(dto.getTriggerId())
.taskAssignee(DUMMY_ASSIGNEE_ID)
.singleResult();
if (Objects.isNull(task)) {
throw new WorkflowEngineException(ACTIVITY_BIZ_SET_ASSIGNEE_ERROR, dto.getTriggerId());
}
task.getTaskDefinitionKey();
if (!Objects.equals(task.getAssignee(), BpmnConstants.DUMMY_ASSIGNEE_ID)) {
throw new WorkflowEngineException(ACTIVITY_CANT_SET_ASSIGNEE);
}
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskCmd(dto.getTriggerId(), dto.getAssigners()));
}
}

View File

@ -21,6 +21,7 @@ import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskCmd;
import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder;
import cn.axzo.workflow.core.engine.event.MessagePushEventImpl;
import cn.axzo.workflow.core.engine.event.MessagePushEventType;
@ -90,6 +91,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_AUTO_PASSED;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_COUNTERSIGN;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_TRANSFER;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID;
@ -523,6 +525,11 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
vo.setResult(TRANSFER);
});
// 处理"业务指定审批人" 特殊的日志
if (Objects.equals(DUMMY_ASSIGNEE_ID, vo.getAssignee())) {
vo.setOperationDesc("业务指定审批人");
}
List<Attachment> attachments = attachmentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList());
vo.setAttachments(attachmentConverter.toVos(attachments));
@ -636,6 +643,13 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
return null;
}
@Transactional(rollbackFor = Exception.class)
public void transferTaskV2(BpmnTaskTransferDTO dto) {
processEngineConfiguration.getCommandExecutor().execute(new CustomTransferUserTaskCmd(dto.getTaskId(),
dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssigner()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void transferTask(BpmnTaskTransferDTO dto) {
@ -643,7 +657,9 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
List<BpmnTaskDelegateAssigner> originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(),
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);
Optional<BpmnTaskDelegateAssigner> exists = originAssingeeList.stream().filter(i -> Objects.equals(i.buildAssigneeId(), dto.getTargetAssigner().buildAssigneeId())).findAny();
Optional<BpmnTaskDelegateAssigner> exists =
originAssingeeList.stream().filter(i -> Objects.equals(i.buildAssigneeId(),
dto.getTargetAssigner().buildAssigneeId())).findAny();
if (exists.isPresent()) {
throw new WorkflowEngineException(ASSIGNEE_HAS_BEEN_EXISTS);
}
@ -746,7 +762,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
}
Authentication.setAuthenticatedUserId(null);
//更新父任务待加签人列表
// taskService.setVariable(task.getId(), COUNTERSIGN_ORIGIN_ASSIGNER, originAssigner.buildAssigneeId());
// taskService.setVariable(task.getId(), COUNTERSIGN_ORIGIN_ASSIGNER, originAssigner
// .buildAssigneeId());
//该父任务加签时会创建子任务用于分配给其他人加签
//目前多个子任务时会按照加签人的顺序依次进行加签操作
//例如 加签按顺序依次分配给ABC三人,需A任务进行完成后进行B任务再进行C任务

View File

@ -61,12 +61,12 @@ public class RocketMqBpmActivityEventListener implements BpmnActivityEventListen
@Override
public void onStart(DelegateExecution execution) {
if (log.isDebugEnabled()) {
log.debug("RocketMqBpmActivityEventListener#onStarted...activityId: {}", execution.getCurrentActivityId());
log.debug("RocketMqBpmActivityEventListener#onStart...activityId: {}", execution.getCurrentActivityId());
}
ProcessActivityDTO dto = buildActivityDTO(PROCESS_ACTIVITY_START, execution);
sendMessageQueue(dto, PROCESS_ACTIVITY_START);
if (log.isDebugEnabled()) {
log.debug("RocketMqBpmActivityEventListener#onStarted...end, activityId: {}",
log.debug("RocketMqBpmActivityEventListener#onStart...end, activityId: {}",
execution.getCurrentActivityId());
}
}
@ -94,7 +94,7 @@ public class RocketMqBpmActivityEventListener implements BpmnActivityEventListen
@Override
public void onTake(DelegateExecution execution) {
if (log.isDebugEnabled()) {
log.debug("RocketMqBpmActivityEventListener#onCompleted...activityId: {}",
log.debug("RocketMqBpmActivityEventListener#onTake...activityId: {}",
execution.getCurrentActivityId());
}
ProcessActivityDTO dto = buildActivityDTO(PROCESS_ACTIVITY_TAKE, execution);
@ -103,7 +103,7 @@ public class RocketMqBpmActivityEventListener implements BpmnActivityEventListen
sendMessageQueue(dto, PROCESS_ACTIVITY_TAKE);
if (log.isDebugEnabled()) {
log.debug("RocketMqBpmActivityEventListener#onCompleted...end, activityId: {}",
log.debug("RocketMqBpmActivityEventListener#onTake...end, activityId: {}",
execution.getCurrentActivityId());
}
}
@ -111,7 +111,7 @@ public class RocketMqBpmActivityEventListener implements BpmnActivityEventListen
@Override
public void onEnd(DelegateExecution execution) {
if (log.isDebugEnabled()) {
log.debug("RocketMqMessagePushEventListener#onCancelled...activityId: {}",
log.debug("RocketMqMessagePushEventListener#onEnd...activityId: {}",
execution.getCurrentActivityId());
}
ProcessActivityDTO dto = buildActivityDTO(PROCESS_ACTIVITY_END, execution);
@ -120,7 +120,7 @@ public class RocketMqBpmActivityEventListener implements BpmnActivityEventListen
sendMessageQueue(dto, PROCESS_ACTIVITY_END);
if (log.isDebugEnabled()) {
log.debug("RocketMqBpmActivityEventListener#onCancelled...end, activityId: {}",
log.debug("RocketMqBpmActivityEventListener#onEnd...end, activityId: {}",
execution.getCurrentActivityId());
}
}