Merge branch 'feature/REQ-7125' into dev

This commit is contained in:
wangli 2026-03-03 16:11:48 +08:00
commit 1ccaeaabeb
19 changed files with 558 additions and 79 deletions

View File

@ -198,9 +198,20 @@ public interface ProcessTaskApi {
* @return
*/
@Operation(summary = "重置节点审批人(提级审批、指定审批人)")
@PostMapping("/api/process/task/approvers/reset")
@PostMapping("/api/process/task/upgrade")
@InvokeMode(ASYNC)
CommonResponse<Boolean> resetTaskApprovers(@Validated @RequestBody BpmnTaskResetApproversDTO dto);
CommonResponse<Boolean> upgradeTask(@Validated @RequestBody BpmnTaskResetApproversDTO dto);
/**
* 重置节点审批管理员指定审批人
*
* @param dto
* @return
*/
@Operation(summary = "重置节点审批(管理员指定审批人)")
@PostMapping("/api/process/task/repoint")
@InvokeMode(ASYNC)
CommonResponse<Boolean> repointTask(@Validated @RequestBody BpmnTaskResetApproversDTO dto);
/**
* 催办

View File

@ -23,6 +23,7 @@ public enum ApprovalMethodEnum {
nobody("nobody", "不设置审批人", "[仅业务节点可能有该值]"),
bizSpecify("bizSpecify", "业务指定审批人", "[仅业务节点可能有该值]"),
transferToAdmin("transferToAdmin", "转办给管理员", "该枚举仅日志处理使用"),
transferToAdminSpecify("transferToAdminSpecify", "管理员指定审批人", "该枚举仅日志处理使用"),
@JsonEnumDefaultValue
unknown("unknown", "未知", "兜底");

View File

@ -17,6 +17,7 @@ public enum BpmnProcessInstanceResultEnum {
TRANSFER("TRANSFER", "已转交"),
COUNTERSIGN("COUNTERSIGN", "已加签"),
UPGRADED("UPGRADED", "已提级"),
REPOINT("REPOINT", "已指定"),
COMMENTED("COMMENTED", "已评论"),
DELETED("DELETED", "已删除"),
HIDDEN("HIDDEN", "已隐藏"),

View File

@ -39,6 +39,6 @@ public abstract class BaseBpmnTaskDelegateAssigner {
/**
* 是否是管理员指定审批人
*/
private Boolean isTransferToAdminSpecify;
private Boolean isTransferToAdminSpecify = false;
}

View File

@ -69,7 +69,7 @@ public class BpmnTaskResetApproversDTO implements Serializable {
/**
* 任务提级给谁审批
*/
@ApiModelProperty(value = "任务提级给谁审批")
@ApiModelProperty(value = "任务给谁审批")
@NotEmpty(message = "任务接收人不能为空")
private List<BpmnTaskDelegateAssigner> targetAssignerList;
@ -86,6 +86,7 @@ public class BpmnTaskResetApproversDTO implements Serializable {
*/
@ApiModelProperty(value = "设置节点审批人操作后的角标")
private BpmnProcessInstanceResultEnum statusEnum;
/**
* 是否异步执行
*/

View File

@ -20,8 +20,10 @@ import cn.axzo.workflow.core.engine.job.AsyncCountersignUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncExtTaskInstJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncRejectTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncRemindTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncRepointTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncResetApproversUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncTransferUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncUpgradeTaskJobHandler;
import cn.axzo.workflow.core.engine.job.NextActivityConfigCheckJobHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncJobLogClearTraceExceptionHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncRunnableExceptionExceptionHandler;
@ -121,8 +123,11 @@ public class FlowableConfiguration {
configuration.addCustomJobHandler(new AsyncActivityCallbackJobHandler());
configuration.addCustomJobHandler(new AsyncApproveTaskWithFormJobHandler());
configuration.addCustomJobHandler(new AsyncRemindTaskJobHandler(refreshProperties));
// 这个 jobHandler 后期可以下掉它已经被 AsyncUpgradeTaskJobHandler()代替目前为了保证在途审批不出异常先保留
configuration.addCustomJobHandler(new AsyncResetApproversUserTaskJobHandler(extAxHiTaskInstService));
configuration.addCustomJobHandler(new NextActivityConfigCheckJobHandler(refreshProperties));
configuration.addCustomJobHandler(new AsyncUpgradeTaskJobHandler());
configuration.addCustomJobHandler(new AsyncRepointTaskJobHandler());
configurers.forEach(i -> configuration.addCustomJobHandler(i.getJobHandler()));
// 异步任务异常重试时间间隔
configuration.setDefaultFailedJobWaitTime(30);

View File

@ -0,0 +1,95 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.core.engine.job.AsyncRepointTaskJobHandler;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
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.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.io.Serializable;
import static cn.axzo.workflow.common.code.OtherRespCode.ASSIGNEE_NODE_ID_NOT_EXISTS;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCategoryVersion;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
/**
* 重置节点下的所有审批人
*
* @author wangli
* @since 2025-06-24 17:17
*/
@Slf4j
public class CustomRepointTaskAsyncCmd extends CustomResetTaskApproversAsyncCmd implements Serializable {
private static final long serialVersionUID = 6231755400617981273L;
public CustomRepointTaskAsyncCmd(BpmnTaskResetApproversDTO dto) {
super(dto);
}
@Override
public String paramToJsonString() {
return JSON.toJSONString(dto);
}
@Override
public Void executeInternal(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery =
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService();
TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
validTargetAssigneeNodeId(task.getProcessDefinitionId());
validTask(historicTaskInstance, task, dto.getOriginAssigner(), null);
startAsync(processEngineConfiguration, task);
return null;
}
private void startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, TaskEntity task) {
JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
JobEntity job = jobService.createJob();
// 这里的 executionId 可为 null
job.setExecutionId(task.getExecutionId());
job.setProcessInstanceId(task.getProcessInstanceId());
job.setProcessDefinitionId(task.getProcessDefinitionId());
job.setElementId(task.getTaskDefinitionKey());
job.setElementName(task.getName());
job.setJobHandlerType(AsyncRepointTaskJobHandler.TYPE);
job.setTenantId(task.getTenantId());
// 携带自定义的数据
job.setCustomValues(JSONUtil.toJsonStr(dto));
// 创建异步任务并调度
jobService.createAsyncJob(job, false);
jobService.scheduleAsyncJob(job);
}
private void validTargetAssigneeNodeId(String processDefinitionId) {
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
boolean present = dto.getTargetAssignerList().stream().anyMatch(assigner -> !org.springframework.util.StringUtils.hasText(assigner.getNodeId()));
Integer categoryVersion = getCategoryVersion(bpmnModel.getMainProcess()).orElse(0);
if (categoryVersion > 0 && present) {
throw new WorkflowEngineException(ASSIGNEE_NODE_ID_NOT_EXISTS, "审批人");
}
}
}

View File

@ -0,0 +1,72 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.io.Serializable;
import java.util.List;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.RESET_TASK_ASSIGNER_SHOW_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner.buildDummyAssigner;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
/**
* 重置节点下的所有审批人
*
* @author wangli
* @since 2025-06-24 17:17
*/
@Slf4j
public class CustomRepointTaskCmd extends CustomResetTaskApproversCmd implements Serializable {
public CustomRepointTaskCmd(String originTaskId,
String advice,
List<AttachmentDTO> attachmentList,
BpmnTaskDelegateAssigner originTaskAssignee,
List<BpmnTaskDelegateAssigner> targetTaskAssigneeList,
BpmnProcessInstanceResultEnum statusEnum) {
super(originTaskId, advice, attachmentList, originTaskAssignee, targetTaskAssigneeList, statusEnum);
}
@Override
protected void resolveOriginTask(CommandContext commandContext, TaskService taskService, TaskEntity task) {
BpmnTaskDelegateAssigner assigner = buildDummyAssigner("repoint", TASK_ASSIGNEE_SKIP_FLAT, "dummyApprover");
task.setAssignee(assigner.buildAssigneeId());
task.setScopeType("REPOINT");
Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId());
// 构建评论内容
StringBuilder message = new StringBuilder("指定");
int end = Math.min(targetTaskAssigneeList.size(), RESET_TASK_ASSIGNER_SHOW_NUMBER);
//加签人员数量显示指定个数
for (int i = 0; i < end; i++) {
message.append(targetTaskAssigneeList.get(i).getAssignerName());
if (i < end - 1) {
message.append("");
}
}
if (targetTaskAssigneeList.size() > end) {
message.append("")
.append(targetTaskAssigneeList.size())
.append("");
}
message.append("为审批人(管理员指定)");
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, message.toString());
addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice);
Authentication.setAuthenticatedUserId(null);
taskService.saveTask(task);
}
}

View File

@ -31,10 +31,10 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask
* @since 2025-06-24 17:17
*/
@Slf4j
public class CustomResetTaskApproversAsyncCmd extends AbstractCommand<Void> implements Serializable {
public abstract class CustomResetTaskApproversAsyncCmd extends AbstractCommand<Void> implements Serializable {
private static final long serialVersionUID = 6231755400617981273L;
private final BpmnTaskResetApproversDTO dto;
protected final BpmnTaskResetApproversDTO dto;
public CustomResetTaskApproversAsyncCmd(BpmnTaskResetApproversDTO dto) {
this.dto = dto;

View File

@ -5,12 +5,10 @@ import cn.axzo.workflow.common.exception.WorkflowEngineException;
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.engine.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
@ -30,17 +28,11 @@ import java.util.Objects;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.code.OtherRespCode.ASSIGNEE_NODE_ID_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.RESET_TASK_ASSIGNER_SHOW_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.UPGRADED;
import static cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner.buildDummyAssigner;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCategoryVersion;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
@ -51,30 +43,27 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask
* @since 2025-06-24 17:17
*/
@Slf4j
public class CustomResetTaskApproversCmd extends AbstractCommand<Void> implements Serializable {
public abstract class CustomResetTaskApproversCmd extends AbstractCommand<Void> implements Serializable {
private final String originTaskId;
private final String advice;
private final List<AttachmentDTO> attachmentList;
private final BpmnTaskDelegateAssigner originTaskAssignee;
private final List<BpmnTaskDelegateAssigner> targetTaskAssigneeList;
private final BpmnProcessInstanceResultEnum statusEnum;
private final ExtAxHiTaskInstService extAxHiTaskInstService;
protected final String originTaskId;
protected final String advice;
protected final List<AttachmentDTO> attachmentList;
protected final BpmnTaskDelegateAssigner originTaskAssignee;
protected final List<BpmnTaskDelegateAssigner> targetTaskAssigneeList;
protected final BpmnProcessInstanceResultEnum statusEnum;
public CustomResetTaskApproversCmd(String originTaskId,
String advice,
List<AttachmentDTO> attachmentList,
BpmnTaskDelegateAssigner originTaskAssignee,
List<BpmnTaskDelegateAssigner> targetTaskAssigneeList,
BpmnProcessInstanceResultEnum statusEnum,
ExtAxHiTaskInstService extAxHiTaskInstService) {
BpmnProcessInstanceResultEnum statusEnum) {
this.originTaskId = originTaskId;
this.advice = advice;
this.attachmentList = attachmentList;
this.originTaskAssignee = originTaskAssignee;
this.targetTaskAssigneeList = targetTaskAssigneeList;
this.statusEnum = statusEnum;
this.extAxHiTaskInstService = extAxHiTaskInstService;
}
@Override
@ -136,35 +125,7 @@ public class CustomResetTaskApproversCmd extends AbstractCommand<Void> implement
// runtimeService.removeVariables(taskEntity.getProcessInstanceId(), variables.keySet());
}
private void resolveOriginTask(CommandContext commandContext, TaskService taskService, TaskEntity task) {
BpmnTaskDelegateAssigner assigner = buildDummyAssigner("upgrade", TASK_ASSIGNEE_SKIP_FLAT, "dummyApprover");
task.setAssignee(assigner.buildAssigneeId());
task.setScopeType("UPGRADE");
Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId());
// 构建评论内容
StringBuilder message = new StringBuilder("提级给");
int end = Math.min(targetTaskAssigneeList.size(), RESET_TASK_ASSIGNER_SHOW_NUMBER);
//加签人员数量显示指定个数
for (int i = 0; i < end; i++) {
message.append(targetTaskAssigneeList.get(i).getAssignerName());
if (i < end - 1) {
message.append("");
}
}
if (targetTaskAssigneeList.size() > end) {
message.append("")
.append(targetTaskAssigneeList.size())
.append("");
}
message.append("审批");
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, message.toString());
addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice);
Authentication.setAuthenticatedUserId(null);
taskService.saveTask(task);
}
protected abstract void resolveOriginTask(CommandContext commandContext, TaskService taskService, TaskEntity task);
private void validTargetAssigneeNodeId(String processDefinitionId) {
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);

View File

@ -0,0 +1,95 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.core.engine.job.AsyncUpgradeTaskJobHandler;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
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.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.io.Serializable;
import static cn.axzo.workflow.common.code.OtherRespCode.ASSIGNEE_NODE_ID_NOT_EXISTS;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCategoryVersion;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
/**
* 重置节点下的所有审批人
*
* @author wangli
* @since 2025-06-24 17:17
*/
@Slf4j
public class CustomUpgradeTaskAsyncCmd extends CustomResetTaskApproversAsyncCmd implements Serializable {
private static final long serialVersionUID = 6231755400617981273L;
public CustomUpgradeTaskAsyncCmd(BpmnTaskResetApproversDTO dto) {
super(dto);
}
@Override
public String paramToJsonString() {
return JSON.toJSONString(dto);
}
@Override
public Void executeInternal(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery =
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService();
TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
validTargetAssigneeNodeId(task.getProcessDefinitionId());
validTask(historicTaskInstance, task, dto.getOriginAssigner(), null);
startAsync(processEngineConfiguration, task);
return null;
}
private void startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, TaskEntity task) {
JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
JobEntity job = jobService.createJob();
// 这里的 executionId 可为 null
job.setExecutionId(task.getExecutionId());
job.setProcessInstanceId(task.getProcessInstanceId());
job.setProcessDefinitionId(task.getProcessDefinitionId());
job.setElementId(task.getTaskDefinitionKey());
job.setElementName(task.getName());
job.setJobHandlerType(AsyncUpgradeTaskJobHandler.TYPE);
job.setTenantId(task.getTenantId());
// 携带自定义的数据
job.setCustomValues(JSONUtil.toJsonStr(dto));
// 创建异步任务并调度
jobService.createAsyncJob(job, false);
jobService.scheduleAsyncJob(job);
}
private void validTargetAssigneeNodeId(String processDefinitionId) {
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
boolean present = dto.getTargetAssignerList().stream().anyMatch(assigner -> !org.springframework.util.StringUtils.hasText(assigner.getNodeId()));
Integer categoryVersion = getCategoryVersion(bpmnModel.getMainProcess()).orElse(0);
if (categoryVersion > 0 && present) {
throw new WorkflowEngineException(ASSIGNEE_NODE_ID_NOT_EXISTS, "审批人");
}
}
}

View File

@ -0,0 +1,71 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.io.Serializable;
import java.util.List;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.RESET_TASK_ASSIGNER_SHOW_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner.buildDummyAssigner;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
/**
* 重置节点下的所有审批人
*
* @author wangli
* @since 2025-06-24 17:17
*/
@Slf4j
public class CustomUpgradeTaskCmd extends CustomResetTaskApproversCmd implements Serializable {
public CustomUpgradeTaskCmd(String originTaskId,
String advice,
List<AttachmentDTO> attachmentList,
BpmnTaskDelegateAssigner originTaskAssignee,
List<BpmnTaskDelegateAssigner> targetTaskAssigneeList,
BpmnProcessInstanceResultEnum statusEnum) {
super(originTaskId, advice, attachmentList, originTaskAssignee, targetTaskAssigneeList, statusEnum);
}
@Override
protected void resolveOriginTask(CommandContext commandContext, TaskService taskService, TaskEntity task) {
BpmnTaskDelegateAssigner assigner = buildDummyAssigner("upgrade", TASK_ASSIGNEE_SKIP_FLAT, "dummyApprover");
task.setAssignee(assigner.buildAssigneeId());
task.setScopeType("UPGRADE");
Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId());
// 构建评论内容
StringBuilder message = new StringBuilder("提级给");
int end = Math.min(targetTaskAssigneeList.size(), RESET_TASK_ASSIGNER_SHOW_NUMBER);
//加签人员数量显示指定个数
for (int i = 0; i < end; i++) {
message.append(targetTaskAssigneeList.get(i).getAssignerName());
if (i < end - 1) {
message.append("");
}
}
if (targetTaskAssigneeList.size() > end) {
message.append("")
.append(targetTaskAssigneeList.size())
.append("");
}
message.append("审批");
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, message.toString());
addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice);
Authentication.setAuthenticatedUserId(null);
taskService.saveTask(task);
}
}

View File

@ -0,0 +1,42 @@
package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.core.engine.cmd.CustomRepointTaskCmd;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
@Slf4j
public class AsyncRepointTaskJobHandler extends AbstractJobHandler implements JobHandler {
public static final String TYPE = "async-repoint-task";
public AsyncRepointTaskJobHandler() {
}
@Override
public String getType() {
return TYPE;
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("AsyncCountersignUserTaskJobHandler executing...");
log(job);
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
BpmnTaskResetApproversDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnTaskResetApproversDTO.class);
processEngineConfiguration.getCommandExecutor().execute(new CustomRepointTaskCmd(dto.getTaskId(),
dto.getAdvice(),
dto.getAttachmentList(),
dto.getOriginAssigner(),
dto.getTargetAssignerList(),
BpmnProcessInstanceResultEnum.REPOINT));
}
}

View File

@ -2,7 +2,7 @@ package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.core.engine.cmd.CustomResetTaskApproversCmd;
import cn.axzo.workflow.core.engine.cmd.CustomUpgradeTaskCmd;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
@ -13,7 +13,13 @@ import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
/**
* 该功能抽象到底层了上层目前有提级审批指定审批人两种实现
* <p>
* 为了兼容先保留该 handler
*/
@Slf4j
@Deprecated
public class AsyncResetApproversUserTaskJobHandler extends AbstractJobHandler implements JobHandler {
public static final String TYPE = "async-reset-approves-task";
@ -36,12 +42,11 @@ public class AsyncResetApproversUserTaskJobHandler extends AbstractJobHandler im
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
BpmnTaskResetApproversDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnTaskResetApproversDTO.class);
processEngineConfiguration.getCommandExecutor().execute(new CustomResetTaskApproversCmd(dto.getTaskId(),
processEngineConfiguration.getCommandExecutor().execute(new CustomUpgradeTaskCmd(dto.getTaskId(),
dto.getAdvice(),
dto.getAttachmentList(),
dto.getOriginAssigner(),
dto.getTargetAssignerList(),
BpmnProcessInstanceResultEnum.UPGRADED,
extAxHiTaskInstService));
BpmnProcessInstanceResultEnum.UPGRADED));
}
}

View File

@ -0,0 +1,42 @@
package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.core.engine.cmd.CustomUpgradeTaskCmd;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
@Slf4j
public class AsyncUpgradeTaskJobHandler extends AbstractJobHandler implements JobHandler {
public static final String TYPE = "async-upgrade-task";
public AsyncUpgradeTaskJobHandler() {
}
@Override
public String getType() {
return TYPE;
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("AsyncCountersignUserTaskJobHandler executing...");
log(job);
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
BpmnTaskResetApproversDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnTaskResetApproversDTO.class);
processEngineConfiguration.getCommandExecutor().execute(new CustomUpgradeTaskCmd(dto.getTaskId(),
dto.getAdvice(),
dto.getAttachmentList(),
dto.getOriginAssigner(),
dto.getTargetAssignerList(),
BpmnProcessInstanceResultEnum.UPGRADED));
}
}

View File

@ -1,9 +1,26 @@
package cn.axzo.workflow.core.service;
import cn.axzo.workflow.common.model.request.bpmn.task.*;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditWithFormDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.task.*;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskDonePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import org.flowable.form.api.FormInfo;
import java.util.List;
@ -141,7 +158,9 @@ public interface BpmnProcessTaskService {
*/
void countersignTask(BpmnTaskCountersignDTO countersignDTO);
void resetTaskApprovers(BpmnTaskResetApproversDTO dto);
void upgradeTask(BpmnTaskResetApproversDTO dto);
void repointTask(BpmnTaskResetApproversDTO dto);
void remindTask(BpmnTaskRemindDTO dto);
@ -152,5 +171,4 @@ public interface BpmnProcessTaskService {
String findTaskIdByInstanceIdAndPersonId(String processInstanceId, String personId);
Map<String, String> findTaskIdByInstanceIdsAndPersonId(List<String> processInstanceIds, String personId);
}

View File

@ -206,6 +206,7 @@ import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.autoPassed_empty;
import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.autoRejection_empty;
import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.human;
import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.transferToAdmin;
import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.transferToAdminSpecify;
import static cn.axzo.workflow.common.enums.BpmnButtonEnum.BPMN_REPOINT;
import static cn.axzo.workflow.common.enums.BpmnButtonEnum.BPMN_UPGRADE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.AND;
@ -1203,7 +1204,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
.execute(new CustomForecastUserTaskAssigneeCmd(processInstanceId,
i, engineExecutionStartListener, categoryVersion));
node.setForecastAssigners(forecastAssigners);
if (CollectionUtils.isEmpty(forecastAssigners)) {
if (CollectionUtils.isEmpty(forecastAssigners) || forecastAssigners.stream().anyMatch(j -> Objects.equals(Boolean.TRUE, j.getIsTransferToAdminSpecify()))) {
getApproverEmptyHandleType(i).ifPresent(emptyHandleType -> {
switch (emptyHandleType) {
case autoPassed:
@ -1215,6 +1216,9 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
case transferToAdmin:
node.setApprovalMethod(transferToAdmin);
break;
case transferToAdminSpecify:
node.setApprovalMethod(transferToAdminSpecify);
break;
default:
node.setNodeMode(EXCEPTIONAL);
break;
@ -1746,6 +1750,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
.forecastAssignees(e.getForecastAssigners())
.build();
if (Objects.nonNull(e.getApprovalMethod())) {
int countApprovers = e.getForecastAssigners().size();
switch (e.getApprovalMethod()) {
case bizSpecify:
build.setOperationDesc("动态审批人");
@ -1762,17 +1767,23 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
case transferToAdmin:
build.setOperationDesc("找不到审批人且转交管理员失败,系统终止");
break;
case transferToAdminSpecify:
if (countApprovers > 1) {
build.setOperationDesc("审批人缺失,需管理员指定,仅一人指定即可");
} else {
build.setOperationDesc("审批人缺失,需" + e.getForecastAssigners().stream().findFirst().orElse(new BpmnTaskDelegateAssigner()).getAssignerName() + "指定审批人(管理员指定)");
}
break;
case human:
if (Objects.equals(e.getNodeMode(), EXCEPTIONAL)) {
build.setOperationDesc("");
} else {
int countPerson = e.getForecastAssigners().size();
if (Objects.equals(BpmnFlowNodeMode.AND, e.getNodeMode())) {
build.setOperationDesc(countPerson + "人会签,需要全部同意");
build.setOperationDesc(countApprovers + "人会签,需要全部同意");
} else if (Objects.equals(BpmnFlowNodeMode.OR, e.getNodeMode())) {
build.setOperationDesc(countPerson + "人或签,仅一人同意即可");
build.setOperationDesc(countApprovers + "人或签,仅一人同意即可");
}
if (Objects.equals(countPerson, 1)) {
if (Objects.equals(countApprovers, 1)) {
// 如果未来节点是单人则按单人节点展示
build.setAssigneeSnapshot(build.getForecastAssignees().get(0));
build.setOperationDesc(build.getAssigneeSnapshot().getAssignerName());
@ -1823,6 +1834,10 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
// 不修改操作描述
break;
}
// 审批人指定特殊文案定制
if (Objects.nonNull(assigner) && Objects.equals(Boolean.TRUE, assigner.getIsTransferToAdminSpecify())) {
i.setOperationDesc("审批人缺失,待管理员指定,仅一人指定即可");
}
i.setAssigneeSnapshot(null);
i.setForecastAssignees(assigners);
i.setButtonConf(e.getButtonConf());
@ -1833,6 +1848,11 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
}
});
} else {
boolean adminSpecify = false;
BpmnTaskDelegateAssigner adminSpecifyUser = ListUtils.emptyIfNull(e.getAssigneeFull()).stream().filter(i -> Objects.equals(Boolean.TRUE, i.getIsTransferToAdminSpecify())).findFirst().orElse(null);
if (Objects.nonNull(adminSpecifyUser)) {
adminSpecify = true;
}
tasks.add(BpmnTaskInstanceLogVO.builder()
.taskId(e.getTaskId())
.taskDefinitionKey(e.getActivityId())
@ -1843,7 +1863,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
.nodeType(BpmnFlowNodeType.valueOfType(e.getNodeType()))
.nodeMode(BpmnFlowNodeMode.valueOfType(e.getNodeMode()))
.result(BpmnProcessInstanceResultEnum.valueOfStatus(e.getStatus()))
.operationDesc(e.getOperationDesc())
.operationDesc(Objects.equals(e.getStatus(), PROCESSING.getStatus()) && adminSpecify ? "审批人缺失,待" + adminSpecifyUser.getAssignerName() + "指定审批人(管理员指定)" : e.getOperationDesc())
.advice(e.getAdvice())
.commentExt("")
.buttonConf(e.getButtonConf())

View File

@ -47,10 +47,12 @@ import cn.axzo.workflow.core.engine.cmd.CustomRejectionTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomRejectionTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomRemindTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomRemindTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomResetTaskApproversAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomResetTaskApproversCmd;
import cn.axzo.workflow.core.engine.cmd.CustomRepointTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomRepointTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomUpgradeTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomUpgradeTaskCmd;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.BpmnProcessDefinitionService;
import cn.axzo.workflow.core.service.BpmnProcessTaskService;
@ -887,13 +889,25 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
public void resetTaskApprovers(BpmnTaskResetApproversDTO dto) {
public void upgradeTask(BpmnTaskResetApproversDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
if (dto.getAsync() != null && dto.getAsync()) {
commandExecutor.execute(new CustomResetTaskApproversAsyncCmd(dto));
commandExecutor.execute(new CustomUpgradeTaskAsyncCmd(dto));
} else {
commandExecutor.execute(new CustomResetTaskApproversCmd(dto.getTaskId(), dto.getAdvice(),
dto.getAttachmentList(), dto.getOriginAssigner(), dto.getTargetAssignerList(), dto.getStatusEnum(), extAxHiTaskInstService));
commandExecutor.execute(new CustomUpgradeTaskCmd(dto.getTaskId(), dto.getAdvice(),
dto.getAttachmentList(), dto.getOriginAssigner(), dto.getTargetAssignerList(), dto.getStatusEnum()));
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void repointTask(BpmnTaskResetApproversDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
if (dto.getAsync() != null && dto.getAsync()) {
commandExecutor.execute(new CustomRepointTaskAsyncCmd(dto));
} else {
commandExecutor.execute(new CustomRepointTaskCmd(dto.getTaskId(), dto.getAdvice(),
dto.getAttachmentList(), dto.getOriginAssigner(), dto.getTargetAssignerList(), dto.getStatusEnum()));
}
}

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.server.controller.web.bpmn;
import cn.axzo.workflow.client.feign.bpmn.ProcessTaskApi;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.enums.AttachmentTypeEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
@ -59,6 +60,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_OPERATION_PARAM_INVALID;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC;
import static cn.azxo.framework.common.model.CommonResponse.success;
/**
@ -331,16 +333,16 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp
}
/**
* 提级审批
* 提级审批设置审批人
*
* @param dto
* @return
*/
@Operation(summary = "置审批人")
@Operation(summary = "提级审批设置审批人")
@Override
@PostMapping("/approvers/reset")
@PostMapping("/upgrade")
@RepeatSubmit
public CommonResponse<Boolean> resetTaskApprovers(@Validated @RequestBody BpmnTaskResetApproversDTO dto) {
public CommonResponse<Boolean> upgradeTask(@Validated @RequestBody BpmnTaskResetApproversDTO dto) {
if (!StringUtils.hasText(dto.getProcessInstanceId()) && !StringUtils.hasText(dto.getTaskId())) {
throw new WorkflowEngineException(TASK_OPERATION_PARAM_INVALID);
}
@ -349,7 +351,30 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp
}
populateUsersAvatar(dto.getOriginAssigner());
populateUsersAvatar(dto.getTargetAssignerList());
bpmnProcessTaskService.resetTaskApprovers(dto);
bpmnProcessTaskService.upgradeTask(dto);
return success(true);
}
/**
* 重置节点审批管理员指定审批人
*
* @param dto
* @return
*/
@Operation(summary = "重置节点审批(管理员指定审批人)")
@PostMapping("/api/process/task/repoint")
@InvokeMode(ASYNC)
@Override
public CommonResponse<Boolean> repointTask(BpmnTaskResetApproversDTO dto) {
if (!StringUtils.hasText(dto.getProcessInstanceId()) && !StringUtils.hasText(dto.getTaskId())) {
throw new WorkflowEngineException(TASK_OPERATION_PARAM_INVALID);
}
if (!StringUtils.hasText(dto.getTaskId())) {
dto.setTaskId(bpmnProcessTaskService.findTaskIdByInstanceIdAndPersonId(dto.getProcessInstanceId(), dto.getOriginAssigner().getPersonId()));
}
populateUsersAvatar(dto.getOriginAssigner());
populateUsersAvatar(dto.getTargetAssignerList());
bpmnProcessTaskService.repointTask(dto);
return success(true);
}